Index: /GPL/branches/alsa-resync1/alsa-kernel/core/control.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/core/control.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/core/control.c	(revision 91)
@@ -0,0 +1,1405 @@
+/*
+ *  Routines for driver control interface
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <sound/driver.h>
+#include <sound/minors.h>
+#include <sound/info.h>
+#include <sound/control.h>
+
+/* max number of user-defined controls */
+#define MAX_USER_CONTROLS       32
+
+extern int control_id_changed;
+extern int card_id_changed;
+
+typedef struct _snd_kctl_ioctl {
+    struct list_head list;		/* list of all ioctls */
+    snd_kctl_ioctl_func_t fioctl;
+} snd_kctl_ioctl_t;
+
+#define snd_kctl_ioctl(n) list_entry(n, snd_kctl_ioctl_t, list)
+
+static DECLARE_RWSEM(snd_ioctl_rwsem);
+static LIST_HEAD(snd_control_ioctls);
+
+static int snd_ctl_open(struct inode *inode, struct file *file)
+{
+    int cardnum = SNDRV_MINOR_CARD(MINOR(inode->i_rdev));
+    unsigned long flags;
+    struct snd_card *card;
+    struct snd_ctl_file *ctl;
+    int err;
+
+    card = snd_cards[cardnum];
+    if (!card) {
+        err = -ENODEV;
+        goto __error1;
+    }
+    err = snd_card_file_add(card, file);
+    if (err < 0) {
+        err = -ENODEV;
+        goto __error1;
+    }
+    if (!try_module_get(card->module)) {
+        err = -EFAULT;
+        goto __error2;
+    }
+    ctl = kcalloc(1, sizeof(*ctl), GFP_KERNEL);
+    if (ctl == NULL) {
+        err = -ENOMEM;
+        goto __error;
+    }
+    INIT_LIST_HEAD(&ctl->events);
+    init_waitqueue_head(&ctl->change_sleep);
+    spin_lock_init(&ctl->read_lock);
+    ctl->card = card;
+    ctl->pid = current->pid;
+    file->private_data = ctl;
+    write_lock_irqsave(&card->ctl_files_rwlock, flags);
+    list_add_tail(&ctl->list, &card->ctl_files);
+    write_unlock_irqrestore(&card->ctl_files_rwlock, flags);
+    return 0;
+
+__error:
+    module_put(card->module);
+__error2:
+    snd_card_file_remove(card, file);
+__error1:
+    return err;
+}
+
+static void snd_ctl_empty_read_queue(struct snd_ctl_file * ctl)
+{
+    struct snd_kctl_event *cread;
+
+    spin_lock(&ctl->read_lock);
+    while (!list_empty(&ctl->events)) {
+        cread = snd_kctl_event(ctl->events.next);
+        list_del(&cread->list);
+        kfree(cread);
+    }
+    spin_unlock(&ctl->read_lock);
+}
+
+static int snd_ctl_release(struct inode *inode, struct file *file)
+{
+    unsigned long flags;
+    struct list_head *list;
+    struct snd_card *card;
+    struct snd_ctl_file *ctl;
+    struct snd_kcontrol *control;
+    unsigned int idx;
+
+    ctl = file->private_data;
+    fasync_helper(-1, file, 0, &ctl->fasync);
+    file->private_data = NULL;
+    card = ctl->card;
+    write_lock_irqsave(&card->ctl_files_rwlock, flags);
+    list_del(&ctl->list);
+    write_unlock_irqrestore(&card->ctl_files_rwlock, flags);
+    down_write(&card->controls_rwsem);
+    list_for_each(list, &card->controls) {
+        control = snd_kcontrol(list);
+        for (idx = 0; idx < control->count; idx++)
+            if (control->vd[idx].owner == ctl)
+                control->vd[idx].owner = NULL;
+    }
+    up_write(&card->controls_rwsem);
+    snd_ctl_empty_read_queue(ctl);
+    kfree(ctl);
+    module_put(card->module);
+    snd_card_file_remove(card, file);
+    return 0;
+}
+
+void snd_ctl_notify(struct snd_card *card, unsigned int mask, struct snd_ctl_elem_id *id)
+{
+    unsigned long flags;
+    struct list_head *flist;
+    struct snd_ctl_file *ctl;
+    struct snd_kctl_event *ev;
+
+    snd_runtime_check(card != NULL && id != NULL, return);
+
+    control_id_changed = id->numid;
+    card_id_changed = card->number;
+    //        printk("ctl id %i changed\n", id->numid);
+    read_lock(&card->ctl_files_rwlock);
+#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
+    card->mixer_oss_change_count++;
+#endif
+    list_for_each(flist, &card->ctl_files) {
+        struct list_head *elist;
+        ctl = snd_ctl_file(flist);
+        if (!ctl->subscribed)
+            continue;
+        spin_lock_irqsave(&ctl->read_lock, flags);
+        list_for_each(elist, &ctl->events) {
+            ev = snd_kctl_event(elist);
+            if (ev->id.numid == id->numid) {
+                ev->mask |= mask;
+                goto _found;
+            }
+        }
+        ev = kcalloc(1, sizeof(*ev), GFP_ATOMIC);
+        if (ev) {
+            ev->id = *id;
+            ev->mask = mask;
+            list_add_tail(&ev->list, &ctl->events);
+        } else {
+            snd_printk("No memory available to allocate event\n");
+        }
+        _found:
+        wake_up(&ctl->change_sleep);
+        spin_unlock_irqrestore(&ctl->read_lock, flags);
+        kill_fasync(ctl->fasync, SIGIO, POLL_IN);
+    }
+    read_unlock(&card->ctl_files_rwlock);
+}
+
+/**
+ * snd_ctl_new - create a control instance from the template
+ * @control: the control template
+ * @access: the default control access
+ *
+ * Allocates a new struct snd_kcontrol instance and copies the given template
+ * to the new instance. It does not copy volatile data (access).
+ *
+ * Returns the pointer of the new instance, or NULL on failure.
+ */
+struct snd_kcontrol *snd_ctl_new(struct snd_kcontrol * control, unsigned int access)
+{
+    struct snd_kcontrol *kctl;
+    unsigned int idx;
+
+    snd_runtime_check(control != NULL, return NULL);
+    snd_runtime_check(control->count > 0, return NULL);
+    kctl = kcalloc(1, sizeof(*kctl) + sizeof(struct snd_kcontrol_volatile) * control->count, GFP_KERNEL);
+    if (kctl == NULL)
+        return NULL;
+    *kctl = *control;
+    for (idx = 0; idx < kctl->count; idx++)
+        kctl->vd[idx].access = access;
+    return kctl;
+}
+
+/**
+ * snd_ctl_new1 - create a control instance from the template
+ * @ncontrol: the initialization record
+ * @private_data: the private data to set
+ *
+ * Allocates a new struct snd_kcontrol instance and initialize from the given
+ * template.  When the access field of ncontrol is 0, it's assumed as
+ * READWRITE access. When the count field is 0, it's assumes as one.
+ *
+ * Returns the pointer of the newly generated instance, or NULL on failure.
+ */
+struct snd_kcontrol *snd_ctl_new1(struct snd_kcontrol_new const * ncontrol,
+                                  void *private_data)
+{
+    struct snd_kcontrol kctl;
+    unsigned int access;
+
+    snd_runtime_check(ncontrol != NULL, return NULL);
+    snd_assert(ncontrol->info != NULL, return NULL);
+    memset(&kctl, 0, sizeof(kctl));
+    kctl.id.iface = ncontrol->iface;
+    kctl.id.device = ncontrol->device;
+    kctl.id.subdevice = ncontrol->subdevice;
+    if (ncontrol->name)
+        strlcpy(kctl.id.name, ncontrol->name, sizeof(kctl.id.name));
+    kctl.id.index = ncontrol->index;
+    kctl.count = ncontrol->count ? ncontrol->count : 1;
+    access = ncontrol->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE :
+        (ncontrol->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE|SNDRV_CTL_ELEM_ACCESS_INACTIVE|
+                             SNDRV_CTL_ELEM_ACCESS_DINDIRECT|SNDRV_CTL_ELEM_ACCESS_INDIRECT));
+    kctl.info = ncontrol->info;
+    kctl.get = ncontrol->get;
+    kctl.put = ncontrol->put;
+    kctl.tlv.p =
+        ncontrol->tlv.p;
+    kctl.private_value = ncontrol->private_value;
+    kctl.private_data = private_data;
+    return snd_ctl_new(&kctl, access);
+}
+
+/**
+ * snd_ctl_free_one - release the control instance
+ * @kcontrol: the control instance
+ *
+ * Releases the control instance created via snd_ctl_new()
+ * or snd_ctl_new1().
+ * Don't call this after the control was added to the card.
+ */
+void snd_ctl_free_one(struct snd_kcontrol * kcontrol)
+{
+    if (kcontrol) {
+        if (kcontrol->private_free)
+            kcontrol->private_free(kcontrol);
+        kfree(kcontrol);
+    }
+}
+
+static unsigned int snd_ctl_hole_check(struct snd_card * card,
+                                       unsigned int count)
+{
+    struct list_head *list;
+    struct snd_kcontrol *kctl;
+
+    list_for_each(list, &card->controls) {
+        kctl = snd_kcontrol(list);
+        if ((kctl->id.numid <= card->last_numid &&
+             kctl->id.numid + kctl->count > card->last_numid) ||
+            (kctl->id.numid <= card->last_numid + count - 1 &&
+             kctl->id.numid + kctl->count > card->last_numid + count - 1))
+            return card->last_numid = kctl->id.numid + kctl->count - 1;
+    }
+    return card->last_numid;
+}
+
+static int snd_ctl_find_hole(struct snd_card * card, unsigned int count)
+{
+    unsigned int last_numid, iter = 100000;
+
+    last_numid = card->last_numid;
+    while (last_numid != snd_ctl_hole_check(card, count)) {
+        if (--iter == 0) {
+            /* this situation is very unlikely */
+            snd_printk(KERN_ERR "unable to allocate new control numid\n");
+            return -ENOMEM;
+        }
+        last_numid = card->last_numid;
+    }
+    return 0;
+}
+
+/**
+ * snd_ctl_add - add the control instance to the card
+ * @card: the card instance
+ * @kcontrol: the control instance to add
+ *
+ * Adds the control instance created via snd_ctl_new() or
+ * snd_ctl_new1() to the given card. Assigns also an unique
+ * numid used for fast search.
+ *
+ * Returns zero if successful, or a negative error code on failure.
+ *
+ * It frees automatically the control which cannot be added.
+ */
+int snd_ctl_add(struct snd_card * card, struct snd_kcontrol * kcontrol)
+{
+    struct snd_ctl_elem_id id;
+    unsigned int idx;
+    int err = -EINVAL;
+
+    if (! kcontrol)
+        return err;
+
+    snd_assert(card != NULL, goto error);
+    snd_assert(kcontrol->info != NULL, goto error);
+
+    id = kcontrol->id;
+    down_write(&card->controls_rwsem);
+#if 1 // doesnt works, traps on some systems
+    if (snd_ctl_find_id(card, &id)) {
+        up_write(&card->controls_rwsem);
+        snd_ctl_free_one(kcontrol);
+        snd_printd(KERN_ERR "control %i:%i:%i:%s:%i is already present\n",
+                   id.iface,
+                   id.device,
+                   id.subdevice,
+                   id.name,
+                   id.index);
+        err = -EBUSY;
+        goto error;
+    }
+    if (snd_ctl_find_hole(card, kcontrol->count) < 0) {
+        up_write(&card->controls_rwsem);
+        err = -ENOMEM;
+        goto error;
+    }
+#endif
+    list_add_tail(&kcontrol->list, &card->controls);
+    card->controls_count += kcontrol->count;
+    kcontrol->id.numid = card->last_numid + 1;
+    card->last_numid += kcontrol->count;
+    up_write(&card->controls_rwsem);
+    for (idx = 0; idx < kcontrol->count; idx++, id.index++, id.numid++)
+        snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_ADD, &id);
+    return 0;
+
+error:
+    snd_ctl_free_one(kcontrol);
+    return err;
+}
+
+/**
+ * snd_ctl_remove - remove the control from the card and release it
+ * @card: the card instance
+ * @kcontrol: the control instance to remove
+ *
+ * Removes the control from the card and then releases the instance.
+ * You don't need to call snd_ctl_free_one(). You must be in
+ * the write lock - down_write(&card->controls_rwsem).
+ *
+ * Returns 0 if successful, or a negative error code on failure.
+ */
+int snd_ctl_remove(struct snd_card * card, struct snd_kcontrol * kcontrol)
+{
+    struct snd_ctl_elem_id id;
+    unsigned int idx;
+
+    snd_runtime_check(card != NULL && kcontrol != NULL, return -EINVAL);
+    list_del(&kcontrol->list);
+    card->controls_count -= kcontrol->count;
+    id = kcontrol->id;
+    for (idx = 0; idx < kcontrol->count; idx++, id.index++, id.numid++)
+        snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_REMOVE, &id);
+    snd_ctl_free_one(kcontrol);
+    return 0;
+}
+
+/**
+ * snd_ctl_remove_id - remove the control of the given id and release it
+ * @card: the card instance
+ * @id: the control id to remove
+ *
+ * Finds the control instance with the given id, removes it from the
+ * card list and releases it.
+ *
+ * Returns 0 if successful, or a negative error code on failure.
+ */
+int snd_ctl_remove_id(struct snd_card * card, struct snd_ctl_elem_id *id)
+{
+    struct snd_kcontrol *kctl;
+
+    kctl = snd_ctl_find_id(card, id);
+    if (kctl == NULL)
+        return -ENOENT;
+    return snd_ctl_remove(card, kctl);
+}
+
+/**
+ * snd_ctl_remove_unlocked_id - remove the unlocked control of the given id and release it
+ * @file: active control handle
+ * @id: the control id to remove
+ *
+ * Finds the control instance with the given id, removes it from the
+ * card list and releases it.
+ *
+ * Returns 0 if successful, or a negative error code on failure.
+ */
+static int snd_ctl_remove_unlocked_id(struct snd_ctl_file * file, struct snd_ctl_elem_id *id)
+{
+    struct snd_card *card = file->card;
+    struct snd_kcontrol *kctl;
+    int idx, ret;
+
+    down_write(&card->controls_rwsem);
+    kctl = snd_ctl_find_id(card, id);
+    if (kctl == NULL) {
+        up_write(&card->controls_rwsem);
+        return -ENOENT;
+    }
+    for (idx = 0; idx < kctl->count; idx++)
+        if (kctl->vd[idx].owner != NULL && kctl->vd[idx].owner != file) {
+            up_write(&card->controls_rwsem);
+            return -EBUSY;
+        }
+    ret = snd_ctl_remove(card, kctl);
+    up_write(&card->controls_rwsem);
+    return ret;
+}
+
+/**
+ * snd_ctl_rename_id - replace the id of a control on the card
+ * @card: the card instance
+ * @src_id: the old id
+ * @dst_id: the new id
+ *
+ * Finds the control with the old id from the card, and replaces the
+ * id with the new one.
+ *
+ * Returns zero if successful, or a negative error code on failure.
+ */
+int snd_ctl_rename_id(struct snd_card *card, struct snd_ctl_elem_id *src_id,
+		      struct snd_ctl_elem_id *dst_id)
+{
+	struct snd_kcontrol *kctl;
+
+	down_write(&card->controls_rwsem);
+	kctl = snd_ctl_find_id(card, src_id);
+	if (kctl == NULL) {
+		up_write(&card->controls_rwsem);
+		return -ENOENT;
+	}
+	kctl->id = *dst_id;
+	kctl->id.numid = card->last_numid + 1;
+	card->last_numid += kctl->count;
+	up_write(&card->controls_rwsem);
+	return 0;
+}
+
+/**
+ * snd_ctl_find_numid - find the control instance with the given number-id
+ * @card: the card instance
+ * @numid: the number-id to search
+ *
+ * Finds the control instance with the given number-id from the card.
+ *
+ * Returns the pointer of the instance if found, or NULL if not.
+ *
+ * The caller must down card->controls_rwsem before calling this function
+ * (if the race condition can happen).
+ */
+struct snd_kcontrol *snd_ctl_find_numid(struct snd_card *card, unsigned int numid)
+{
+    struct list_head *list;
+    struct snd_kcontrol *kctl;
+
+    snd_assert(card != NULL && numid != 0, return NULL);
+    list_for_each(list, &card->controls) {
+        kctl = snd_kcontrol(list);
+        if (kctl->id.numid <= numid && kctl->id.numid + kctl->count > numid)
+            return kctl;
+    }
+    return NULL;
+}
+
+/**
+* snd_ctl_find_id - find the control instance with the given id
+* @card: the card instance
+* @id: the id to search
+*
+    * Finds the control instance with the given id from the card.
+    *
+    * Returns the pointer of the instance if found, or NULL if not.
+        *
+            * The caller must down card->controls_rwsem before calling this function
+            * (if the race condition can happen).
+            */
+            struct snd_kcontrol *snd_ctl_find_id(struct snd_card *card,
+                                                 struct snd_ctl_elem_id *id)
+    {
+        struct list_head *list;
+        struct snd_kcontrol *kctl;
+
+        snd_assert(card != NULL && id != NULL, return NULL);
+        if (id->numid != 0)
+            return snd_ctl_find_numid(card, id->numid);
+        list_for_each(list, &card->controls) {
+            kctl = snd_kcontrol(list);
+            if (kctl->id.iface != id->iface)
+                continue;
+            if (kctl->id.device != id->device)
+                continue;
+            if (kctl->id.subdevice != id->subdevice)
+                continue;
+            if (strncmp(kctl->id.name, id->name, sizeof(kctl->id.name)))
+                continue;
+            if (kctl->id.index > id->index)
+                continue;
+            if (kctl->id.index + kctl->count <= id->index)
+                continue;
+            return kctl;
+        }
+        return NULL;
+    }
+
+static int snd_ctl_card_info(struct snd_card * card, struct snd_ctl_file * ctl,
+                             unsigned int cmd, unsigned long arg)
+{
+    struct snd_ctl_card_info info;
+
+    memset(&info, 0, sizeof(info));
+    down_read(&snd_ioctl_rwsem);
+    info.card = card->number;
+    strlcpy(info.id, card->id, sizeof(info.id));
+    strlcpy(info.driver, card->driver, sizeof(info.driver));
+    strlcpy(info.name, card->shortname, sizeof(info.name));
+    strlcpy(info.longname, card->longname, sizeof(info.longname));
+    strlcpy(info.mixername, card->mixername, sizeof(info.mixername));
+    strlcpy(info.components, card->components, sizeof(info.components));
+    up_read(&snd_ioctl_rwsem);
+    if (copy_to_user((void *) arg, &info, sizeof(struct snd_ctl_card_info)))
+        return -EFAULT;
+    return 0;
+}
+
+static int snd_ctl_elem_list(struct snd_card *card, struct snd_ctl_elem_list *_list)
+{
+    struct list_head *plist;
+    struct snd_ctl_elem_list list;
+    struct snd_kcontrol *kctl;
+    struct snd_ctl_elem_id *dst, *id;
+    unsigned int offset, space, first, jidx;
+
+    if (copy_from_user(&list, _list, sizeof(list)))
+        return -EFAULT;
+    offset = list.offset;
+    space = list.space;
+    first = 0;
+    /* try limit maximum space */
+    if (space > 16384)
+        return -ENOMEM;
+    if (space > 0) {
+        /* allocate temporary buffer for atomic operation */
+        dst = vmalloc(space * sizeof(struct snd_ctl_elem_id));
+        if (dst == NULL)
+            return -ENOMEM;
+        down_read(&card->controls_rwsem);
+        list.count = card->controls_count;
+        plist = card->controls.next;
+        while (plist != &card->controls) {
+            if (offset == 0)
+                break;
+            kctl = snd_kcontrol(plist);
+            if (offset < kctl->count)
+                break;
+            offset -= kctl->count;
+            plist = plist->next;
+        }
+        list.used = 0;
+        id = dst;
+        while (space > 0 && plist != &card->controls) {
+            kctl = snd_kcontrol(plist);
+            for (jidx = offset; space > 0 && jidx < kctl->count; jidx++) {
+                snd_ctl_build_ioff(id, kctl, jidx);
+                id++;
+                space--;
+                list.used++;
+            }
+            plist = plist->next;
+            offset = 0;
+        }
+        up_read(&card->controls_rwsem);
+        if (list.used > 0 && copy_to_user(list.pids, dst, list.used * sizeof(struct snd_ctl_elem_id))) {
+            vfree(dst);
+            return -EFAULT;
+        }
+        vfree(dst);
+    } else {
+        down_read(&card->controls_rwsem);
+        list.count = card->controls_count;
+        up_read(&card->controls_rwsem);
+    }
+    if (copy_to_user(_list, &list, sizeof(list)))
+        return -EFAULT;
+    return 0;
+}
+
+static int snd_ctl_elem_info(struct snd_ctl_file *ctl,
+                             struct snd_ctl_elem_info *info)
+{
+    struct snd_card *card = ctl->card;
+    struct snd_kcontrol *kctl;
+    struct snd_kcontrol_volatile *vd;
+    unsigned int index_offset;
+    int result;
+
+    down_read(&card->controls_rwsem);
+    kctl = snd_ctl_find_id(card, &info->id);
+    if (kctl == NULL) {
+        up_read(&card->controls_rwsem);
+        return -ENOENT;
+    }
+#ifdef CONFIG_SND_DEBUG
+    info->access = 0;
+#endif
+    result = kctl->info(kctl, info);
+    if (result >= 0) {
+        snd_assert(info->access == 0, );
+        index_offset = snd_ctl_get_ioff(kctl, &info->id);
+        vd = &kctl->vd[index_offset];
+        snd_ctl_build_ioff(&info->id, kctl, index_offset);
+        info->access = vd->access;
+        if (vd->owner) {
+            info->access |= SNDRV_CTL_ELEM_ACCESS_LOCK;
+            if (vd->owner == ctl)
+                info->access |= SNDRV_CTL_ELEM_ACCESS_OWNER;
+            info->owner = vd->owner_pid;
+        } else {
+            info->owner = -1;
+        }
+    }
+    up_read(&card->controls_rwsem);
+    return result;
+}
+
+int snd_ctl_elem_read(struct snd_card *card, struct snd_ctl_elem_value *control)
+{
+	struct snd_kcontrol *kctl;
+	struct snd_kcontrol_volatile *vd;
+	unsigned int index_offset;
+	int result, indirect;
+
+	down_read(&card->controls_rwsem);
+	kctl = snd_ctl_find_id(card, &control->id);
+	if (kctl == NULL) {
+		result = -ENOENT;
+	} else {
+		index_offset = snd_ctl_get_ioff(kctl, &control->id);
+		vd = &kctl->vd[index_offset];
+		indirect = vd->access & SNDRV_CTL_ELEM_ACCESS_INDIRECT ? 1 : 0;
+		if (control->indirect != indirect) {
+			result = -EACCES;
+		} else {
+			if ((vd->access & SNDRV_CTL_ELEM_ACCESS_READ) && kctl->get != NULL) {
+				snd_ctl_build_ioff(&control->id, kctl, index_offset);
+				result = kctl->get(kctl, control);
+			} else {
+				result = -EPERM;
+			}
+		}
+	}
+	up_read(&card->controls_rwsem);
+	return result;
+}
+
+static int snd_ctl_elem_read_user(struct snd_card *card, struct snd_ctl_elem_value __user *_control)
+{
+	struct snd_ctl_elem_value *control;
+	int result;
+
+	control = kmalloc(sizeof(*control), GFP_KERNEL);
+	if (control == NULL)
+		return -ENOMEM;
+	if (copy_from_user(control, _control, sizeof(*control))) {
+		kfree(control);
+		return -EFAULT;
+	}
+	result = snd_ctl_elem_read(card, control);
+	if (result >= 0)
+		if (copy_to_user(_control, control, sizeof(*control)))
+			result = -EFAULT;
+	kfree(control);
+	return result;
+}
+
+int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file, struct snd_ctl_elem_value *control)
+{
+	struct snd_kcontrol *kctl;
+	struct snd_kcontrol_volatile *vd;
+	unsigned int index_offset;
+	int result, indirect;
+
+	down_read(&card->controls_rwsem);
+	kctl = snd_ctl_find_id(card, &control->id);
+	if (kctl == NULL) {
+		result = -ENOENT;
+	} else {
+		index_offset = snd_ctl_get_ioff(kctl, &control->id);
+		vd = &kctl->vd[index_offset];
+		indirect = vd->access & SNDRV_CTL_ELEM_ACCESS_INDIRECT ? 1 : 0;
+		if (control->indirect != indirect) {
+			result = -EACCES;
+		} else {
+			if (!(vd->access & SNDRV_CTL_ELEM_ACCESS_WRITE) ||
+			    kctl->put == NULL ||
+			    (file && vd->owner != NULL && vd->owner != file)) {
+				result = -EPERM;
+			} else {
+				snd_ctl_build_ioff(&control->id, kctl, index_offset);
+				result = kctl->put(kctl, control);
+			}
+			if (result > 0) {
+				up_read(&card->controls_rwsem);
+				snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &control->id);
+				return 0;
+			}
+		}
+	}
+	up_read(&card->controls_rwsem);
+	return result;
+}
+
+static int snd_ctl_elem_write_user(struct snd_ctl_file *file, struct snd_ctl_elem_value __user *_control)
+{
+	struct snd_ctl_elem_value *control;
+	int result;
+
+	control = kmalloc(sizeof(*control), GFP_KERNEL);
+	if (control == NULL)
+		return -ENOMEM;
+	if (copy_from_user(control, _control, sizeof(*control))) {
+		kfree(control);
+		return -EFAULT;
+	}
+	result = snd_ctl_elem_write(file->card, file, control);
+	if (result >= 0)
+		if (copy_to_user(_control, control, sizeof(*control)))
+			result = -EFAULT;
+	kfree(control);
+	return result;
+}
+
+
+static int snd_ctl_elem_lock(struct snd_ctl_file *file,
+                             struct snd_ctl_elem_id __user *_id)
+{
+    struct snd_card *card = file->card;
+    struct snd_ctl_elem_id id;
+    struct snd_kcontrol *kctl;
+    struct snd_kcontrol_volatile *vd;
+    int result;
+
+    if (copy_from_user(&id, _id, sizeof(id)))
+        return -EFAULT;
+    down_write(&card->controls_rwsem);
+    kctl = snd_ctl_find_id(card, &id);
+    if (kctl == NULL) {
+        result = -ENOENT;
+    } else {
+        vd = &kctl->vd[snd_ctl_get_ioff(kctl, &id)];
+        if (vd->owner != NULL)
+            result = -EBUSY;
+        else {
+            vd->owner = file;
+            vd->owner_pid = current->pid;
+            result = 0;
+        }
+    }
+    up_write(&card->controls_rwsem);
+    return result;
+}
+
+static int snd_ctl_elem_unlock(struct snd_ctl_file *file,
+                               struct snd_ctl_elem_id __user *_id)
+{
+    struct snd_card *card = file->card;
+    struct snd_ctl_elem_id id;
+    struct snd_kcontrol *kctl;
+    struct snd_kcontrol_volatile *vd;
+    int result;
+
+    if (copy_from_user(&id, _id, sizeof(id)))
+        return -EFAULT;
+    down_write(&card->controls_rwsem);
+    kctl = snd_ctl_find_id(card, &id);
+    if (kctl == NULL) {
+        result = -ENOENT;
+    } else {
+        vd = &kctl->vd[snd_ctl_get_ioff(kctl, &id)];
+        if (vd->owner == NULL)
+            result = -EINVAL;
+        else if (vd->owner != file)
+            result = -EPERM;
+        else {
+            vd->owner = NULL;
+            vd->owner_pid = 0;
+            result = 0;
+        }
+    }
+    up_write(&card->controls_rwsem);
+    return result;
+}
+
+struct user_element {
+	struct snd_ctl_elem_info info;
+	void *elem_data;		/* element data */
+	unsigned long elem_data_size;	/* size of element data in bytes */
+	void *priv_data;		/* private data (like strings for enumerated type) */
+	unsigned long priv_data_size;	/* size of private data in bytes */
+};
+
+static int snd_ctl_elem_user_info(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_info *uinfo)
+{
+	struct user_element *ue = kcontrol->private_data;
+
+	*uinfo = ue->info;
+	return 0;
+}
+
+static int snd_ctl_elem_user_get(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol)
+{
+	struct user_element *ue = kcontrol->private_data;
+
+	memcpy(&ucontrol->value, ue->elem_data, ue->elem_data_size);
+	return 0;
+}
+
+static int snd_ctl_elem_user_put(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol)
+{
+	int change;
+	struct user_element *ue = kcontrol->private_data;
+
+	change = memcmp(&ucontrol->value, ue->elem_data, ue->elem_data_size) != 0;
+	if (change)
+		memcpy(ue->elem_data, &ucontrol->value, ue->elem_data_size);
+	return change;
+}
+
+
+static void snd_ctl_elem_user_free(struct snd_kcontrol * kcontrol)
+{
+    kfree(kcontrol->private_data);
+}
+
+static int snd_ctl_elem_add(struct snd_ctl_file *file,
+			    struct snd_ctl_elem_info *info, int replace)
+{
+	struct snd_card *card = file->card;
+	struct snd_kcontrol kctl, *_kctl;
+	unsigned int access;
+	long private_size;
+	struct user_element *ue;
+	int idx, err;
+
+	if (card->user_ctl_count >= MAX_USER_CONTROLS)
+		return -ENOMEM;
+	if (info->count > 1024)
+		return -EINVAL;
+	access = info->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE :
+		(info->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE|
+				 SNDRV_CTL_ELEM_ACCESS_INACTIVE));
+	info->id.numid = 0;
+	memset(&kctl, 0, sizeof(kctl));
+	down_write(&card->controls_rwsem);
+	_kctl = snd_ctl_find_id(card, &info->id);
+	err = 0;
+	if (_kctl) {
+		if (replace)
+			err = snd_ctl_remove(card, _kctl);
+		else
+			err = -EBUSY;
+	} else {
+		if (replace)
+			err = -ENOENT;
+	}
+	up_write(&card->controls_rwsem);
+	if (err < 0)
+		return err;
+	memcpy(&kctl.id, &info->id, sizeof(info->id));
+	kctl.count = info->owner ? info->owner : 1;
+	access |= SNDRV_CTL_ELEM_ACCESS_USER;
+	kctl.info = snd_ctl_elem_user_info;
+	if (access & SNDRV_CTL_ELEM_ACCESS_READ)
+		kctl.get = snd_ctl_elem_user_get;
+	if (access & SNDRV_CTL_ELEM_ACCESS_WRITE)
+		kctl.put = snd_ctl_elem_user_put;
+	switch (info->type) {
+	case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
+		private_size = sizeof(char);
+		if (info->count > 128)
+			return -EINVAL;
+		break;
+	case SNDRV_CTL_ELEM_TYPE_INTEGER:
+		private_size = sizeof(long);
+		if (info->count > 128)
+			return -EINVAL;
+		break;
+	case SNDRV_CTL_ELEM_TYPE_INTEGER64:
+		private_size = sizeof(long long);
+		if (info->count > 64)
+			return -EINVAL;
+		break;
+	case SNDRV_CTL_ELEM_TYPE_BYTES:
+		private_size = sizeof(unsigned char);
+		if (info->count > 512)
+			return -EINVAL;
+		break;
+	case SNDRV_CTL_ELEM_TYPE_IEC958:
+		private_size = sizeof(struct snd_aes_iec958);
+		if (info->count != 1)
+			return -EINVAL;
+		break;
+	default:
+		return -EINVAL;
+	}
+	private_size *= info->count;
+	ue = (struct user_element *)kzalloc(sizeof(struct user_element) + private_size, GFP_KERNEL);
+	if (ue == NULL)
+		return -ENOMEM;
+	ue->info = *info;
+	ue->elem_data = (char *)ue + sizeof(*ue);
+	ue->elem_data_size = private_size;
+	kctl.private_free = snd_ctl_elem_user_free;
+	_kctl = snd_ctl_new(&kctl, access);
+	if (_kctl == NULL) {
+		kfree(_kctl->private_data);
+		return -ENOMEM;
+	}
+	_kctl->private_data = ue;
+	for (idx = 0; idx < _kctl->count; idx++)
+		_kctl->vd[idx].owner = file;
+	err = snd_ctl_add(card, _kctl);
+	if (err < 0) {
+		snd_ctl_free_one(_kctl);
+		return err;
+	}
+
+	down_write(&card->controls_rwsem);
+	card->user_ctl_count++;
+	up_write(&card->controls_rwsem);
+
+	return 0;
+}
+
+static int snd_ctl_elem_add_user(struct snd_ctl_file *file,
+				 struct snd_ctl_elem_info __user *_info, int replace)
+{
+	struct snd_ctl_elem_info info;
+	if (copy_from_user(&info, _info, sizeof(info)))
+		return -EFAULT;
+	return snd_ctl_elem_add(file, &info, replace);
+}
+
+static int snd_ctl_elem_remove(struct snd_ctl_file *file, struct snd_ctl_elem_id *_id)
+{
+    struct snd_ctl_elem_id id;
+
+    if (copy_from_user(&id, _id, sizeof(id)))
+        return -EFAULT;
+    return snd_ctl_remove_unlocked_id(file, &id);
+}
+
+static int snd_ctl_subscribe_events(struct snd_ctl_file *file, int *ptr)
+{
+    int subscribe;
+    if (get_user(subscribe, ptr))
+        return -EFAULT;
+    if (subscribe < 0) {
+        subscribe = file->subscribed;
+        if (put_user(subscribe, ptr))
+            return -EFAULT;
+        return 0;
+    }
+    if (subscribe) {
+        file->subscribed = 1;
+        return 0;
+    } else if (file->subscribed) {
+        snd_ctl_empty_read_queue(file);
+        file->subscribed = 0;
+    }
+    return 0;
+}
+
+#ifdef CONFIG_PM
+/*
+ * change the power state
+ */
+static int snd_ctl_set_power_state(struct snd_card *card, unsigned int power_state)
+{
+    switch (power_state) {
+    case SNDRV_CTL_POWER_D0:
+        if (card->power_state != power_state) {
+            /* FIXME: pass the correct state value */
+            card->pm_resume(card, 0);
+            snd_power_change_state(card, power_state);
+        }
+        break;
+    case SNDRV_CTL_POWER_D3hot:
+        if (card->power_state != power_state) {
+            /* FIXME: pass the correct state value */
+            card->pm_suspend(card, 0);
+            snd_power_change_state(card, power_state);
+        }
+        break;
+    case SNDRV_CTL_POWER_D1:
+    case SNDRV_CTL_POWER_D2:
+    case SNDRV_CTL_POWER_D3cold:
+        /* not supported yet */
+    default:
+        return -EINVAL;
+    }
+    return 0;
+}
+
+#endif
+
+static int snd_ctl_tlv_ioctl(struct snd_ctl_file *file,
+                             struct snd_ctl_tlv __user *_tlv,
+                             int op_flag)
+{
+	struct snd_card *card = file->card;
+	struct snd_ctl_tlv tlv;
+	struct snd_kcontrol *kctl;
+	struct snd_kcontrol_volatile *vd;
+	unsigned int len;
+	int err = 0;
+
+	if (copy_from_user(&tlv, _tlv, sizeof(tlv)))
+		return -EFAULT;
+	if (tlv.length < sizeof(unsigned int) * 3)
+		return -EINVAL;
+	down_read(&card->controls_rwsem);
+	kctl = snd_ctl_find_numid(card, tlv.numid);
+	if (kctl == NULL) {
+		err = -ENOENT;
+		goto __kctl_end;
+	}
+	if (kctl->tlv.p == NULL) {
+		err = -ENXIO;
+		goto __kctl_end;
+	}
+	vd = &kctl->vd[tlv.numid - kctl->id.numid];
+	if ((op_flag == 0 && (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_READ) == 0) ||
+	    (op_flag > 0 && (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_WRITE) == 0) ||
+	    (op_flag < 0 && (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND) == 0)) {
+	    	err = -ENXIO;
+	    	goto __kctl_end;
+	}
+	if (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) {
+		if (file && vd->owner != NULL && vd->owner != file) {
+			err = -EPERM;
+			goto __kctl_end;
+		}
+		err = kctl->tlv.c(kctl, op_flag, tlv.length, _tlv->tlv); 
+		if (err > 0) {
+			up_read(&card->controls_rwsem);
+			snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_TLV, &kctl->id);
+			return 0;
+		}
+	} else {
+		if (op_flag) {
+			err = -ENXIO;
+			goto __kctl_end;
+		}
+		len = kctl->tlv.p[1] + 2 * sizeof(unsigned int);
+		if (tlv.length < len) {
+			err = -ENOMEM;
+			goto __kctl_end;
+		}
+		if (copy_to_user(_tlv->tlv, kctl->tlv.p, len))
+			err = -EFAULT;
+	}
+      __kctl_end:
+	up_read(&card->controls_rwsem);
+	return err;
+}
+
+
+static int snd_ctl_ioctl(struct inode *inode, struct file *file,
+                         unsigned int cmd, unsigned long arg)
+{
+    struct snd_ctl_file *ctl;
+    struct snd_card *card;
+    struct list_head *list;
+    snd_kctl_ioctl_t *p;
+    void __user *argp = (void __user *)arg;
+    int err;
+
+    ctl = file->private_data;
+    card = ctl->card;
+    snd_assert(card != NULL, return -ENXIO);
+    switch (cmd) {
+    case SNDRV_CTL_IOCTL_PVERSION:
+        return put_user(SNDRV_CTL_VERSION, (int *)arg) ? -EFAULT : 0;
+    case SNDRV_CTL_IOCTL_CARD_INFO:
+        return snd_ctl_card_info(card, ctl, cmd, arg);
+    case SNDRV_CTL_IOCTL_ELEM_LIST:
+        return snd_ctl_elem_list(ctl->card, (struct snd_ctl_elem_list *) arg);
+    case SNDRV_CTL_IOCTL_ELEM_INFO:
+        return snd_ctl_elem_info(ctl, (struct snd_ctl_elem_info *) arg);
+    case SNDRV_CTL_IOCTL_ELEM_READ:
+        return snd_ctl_elem_read_user(ctl->card, argp);
+    case SNDRV_CTL_IOCTL_ELEM_WRITE:
+        return snd_ctl_elem_write_user(ctl, argp);
+    case SNDRV_CTL_IOCTL_ELEM_LOCK:
+        return snd_ctl_elem_lock(ctl, (struct snd_ctl_elem_id *) arg);
+    case SNDRV_CTL_IOCTL_ELEM_UNLOCK:
+        return snd_ctl_elem_unlock(ctl, (struct snd_ctl_elem_id *) arg);
+    case SNDRV_CTL_IOCTL_ELEM_ADD:
+        return snd_ctl_elem_add(ctl, (struct snd_ctl_elem_info *) arg, 0);
+    case SNDRV_CTL_IOCTL_ELEM_REPLACE:
+        return snd_ctl_elem_add(ctl, (struct snd_ctl_elem_info *) arg, 1);
+    case SNDRV_CTL_IOCTL_ELEM_REMOVE:
+        return snd_ctl_elem_remove(ctl, (struct snd_ctl_elem_id *) arg);
+    case SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS:
+        return snd_ctl_subscribe_events(ctl, (int *) arg);
+    case SNDRV_CTL_IOCTL_TLV_READ:
+        return snd_ctl_tlv_ioctl(ctl, argp, 0);
+    case SNDRV_CTL_IOCTL_TLV_WRITE:
+        return snd_ctl_tlv_ioctl(ctl, argp, 1);
+    case SNDRV_CTL_IOCTL_TLV_COMMAND:
+        return snd_ctl_tlv_ioctl(ctl, argp, -1);
+    case SNDRV_CTL_IOCTL_POWER:
+        if (get_user(err, (int *)arg))
+            return -EFAULT;
+        if (!capable(CAP_SYS_ADMIN))
+            return -EPERM;
+#ifdef CONFIG_PM
+        if (card->pm_suspend && card->pm_resume) {
+            snd_power_lock(card);
+            err = snd_ctl_set_power_state(card, err);
+            snd_power_unlock(card);
+        } else
+#endif
+            err = -ENOPROTOOPT;
+        return err;
+    case SNDRV_CTL_IOCTL_POWER_STATE:
+#ifdef CONFIG_PM
+        return put_user(card->power_state, (int *)arg) ? -EFAULT : 0;
+#else
+        return put_user(SNDRV_CTL_POWER_D0, (int *)arg) ? -EFAULT : 0;
+#endif
+    }
+    down_read(&snd_ioctl_rwsem);
+    list_for_each(list, &snd_control_ioctls) {
+        p = list_entry(list, snd_kctl_ioctl_t, list);
+        err = p->fioctl(card, ctl, cmd, arg);
+        if (err != -ENOIOCTLCMD) {
+            up_read(&snd_ioctl_rwsem);
+            return err;
+        }
+    }
+    up_read(&snd_ioctl_rwsem);
+    snd_printd("unknown ioctl = 0x%x\n", cmd);
+    return -ENOTTY;
+}
+
+static ssize_t snd_ctl_read(struct file *file, char *buffer, size_t count, loff_t * offset)
+{
+    struct snd_ctl_file *ctl;
+    int err = 0;
+    ssize_t result = 0;
+
+    ctl = file->private_data;
+    snd_assert(ctl != NULL && ctl->card != NULL, return -ENXIO);
+    if (!ctl->subscribed)
+        return -EBADFD;
+    if (count < sizeof(struct snd_ctl_event))
+        return -EINVAL;
+    spin_lock_irq(&ctl->read_lock);
+    while (count >= sizeof(struct snd_ctl_event)) {
+        struct snd_ctl_event ev;
+        struct snd_kctl_event *kev;
+        while (list_empty(&ctl->events)) {
+            wait_queue_t wait;
+            if ((file->f_flags & O_NONBLOCK) != 0 || result > 0) {
+                err = -EAGAIN;
+                goto __end_lock;
+            }
+            init_waitqueue_entry(&wait, current);
+            add_wait_queue(&ctl->change_sleep, &wait);
+            set_current_state(TASK_INTERRUPTIBLE);
+            spin_unlock_irq(&ctl->read_lock);
+            schedule();
+            remove_wait_queue(&ctl->change_sleep, &wait);
+            if (signal_pending(current))
+                return result > 0 ? result : -ERESTARTSYS;
+            spin_lock_irq(&ctl->read_lock);
+        }
+        kev = snd_kctl_event(ctl->events.next);
+        ev.type = SNDRV_CTL_EVENT_ELEM;
+        ev.data.elem.mask = kev->mask;
+        ev.data.elem.id = kev->id;
+        list_del(&kev->list);
+        spin_unlock_irq(&ctl->read_lock);
+        kfree(kev);
+        if (copy_to_user(buffer, &ev, sizeof(struct snd_ctl_event))) {
+            err = -EFAULT;
+            goto __end;
+        }
+        spin_lock_irq(&ctl->read_lock);
+        buffer += sizeof(struct snd_ctl_event);
+        count -= sizeof(struct snd_ctl_event);
+        result += sizeof(struct snd_ctl_event);
+    }
+__end_lock:
+    spin_unlock_irq(&ctl->read_lock);
+__end:
+    return result > 0 ? result : err;
+}
+
+static unsigned int snd_ctl_poll(struct file *file, poll_table * wait)
+{
+    unsigned int mask;
+    struct snd_ctl_file *ctl;
+
+    ctl = file->private_data;
+    if (!ctl->subscribed)
+        return 0;
+    poll_wait(file, &ctl->change_sleep, wait);
+
+    mask = 0;
+    if (!list_empty(&ctl->events))
+        mask |= POLLIN | POLLRDNORM;
+
+    return mask;
+}
+
+/*
+ * register the device-specific control-ioctls.
+ * called from each device manager like pcm.c, hwdep.c, etc.
+ */
+int snd_ctl_register_ioctl(snd_kctl_ioctl_func_t fcn)
+{
+    snd_kctl_ioctl_t *pn;
+
+    pn = kcalloc(1, sizeof(snd_kctl_ioctl_t), GFP_KERNEL);
+    if (pn == NULL)
+        return -ENOMEM;
+    pn->fioctl = fcn;
+    down_write(&snd_ioctl_rwsem);
+    list_add_tail(&pn->list, &snd_control_ioctls);
+    up_write(&snd_ioctl_rwsem);
+    return 0;
+}
+
+/*
+ * de-register the device-specific control-ioctls.
+ */
+int snd_ctl_unregister_ioctl(snd_kctl_ioctl_func_t fcn)
+{
+    struct list_head *list;
+    snd_kctl_ioctl_t *p;
+
+    snd_runtime_check(fcn != NULL, return -EINVAL);
+    down_write(&snd_ioctl_rwsem);
+    list_for_each(list, &snd_control_ioctls) {
+        p = list_entry(list, snd_kctl_ioctl_t, list);
+        if (p->fioctl == fcn) {
+            list_del(&p->list);
+            up_write(&snd_ioctl_rwsem);
+            kfree(p);
+            return 0;
+        }
+    }
+    up_write(&snd_ioctl_rwsem);
+    snd_BUG();
+    return -EINVAL;
+}
+
+static int snd_ctl_fasync(int fd, struct file * file, int on)
+{
+    struct snd_ctl_file *ctl;
+    int err;
+    ctl = file->private_data;
+    err = fasync_helper(fd, file, on, &ctl->fasync);
+    if (err < 0)
+        return err;
+    return 0;
+}
+
+/*
+ *  INIT PART
+ */
+
+#ifdef TARGET_OS2
+static struct file_operations snd_ctl_f_ops =
+{
+#ifdef LINUX_2_3
+    THIS_MODULE,
+#endif
+    0,
+    snd_ctl_read,
+    0,0,
+    snd_ctl_poll,
+    snd_ctl_ioctl,
+    0,
+    snd_ctl_open,0,
+    snd_ctl_release,
+    0,
+    snd_ctl_fasync,
+    0,0,0
+};
+
+static struct snd_minor snd_ctl_reg =
+{
+	.comment =	"ctl",
+	.f_ops =	&snd_ctl_f_ops,
+};
+
+#else
+static struct file_operations snd_ctl_f_ops =
+{
+#ifdef LINUX_2_3
+owner:		THIS_MODULE,
+#endif
+    read:		snd_ctl_read,
+    open:		snd_ctl_open,
+    release:	snd_ctl_release,
+    poll:		snd_ctl_poll,
+    ioctl:		snd_ctl_ioctl,
+    fasync:		snd_ctl_fasync,
+};
+
+static snd_minor_t snd_ctl_reg =
+{
+comment:	"ctl",
+    f_ops:		&snd_ctl_f_ops,
+};
+#endif
+
+/*
+ * registration of the control device:
+ * called from init.c
+ */
+int snd_ctl_register(struct snd_card *card)
+{
+    int err, cardnum;
+    char name[16];
+
+    snd_assert(card != NULL, return -ENXIO);
+    cardnum = card->number;
+    snd_assert(cardnum >= 0 && cardnum < SNDRV_CARDS, return -ENXIO);
+    sprintf(name, "controlC%i", cardnum);
+    if ((err = snd_register_device(SNDRV_DEVICE_TYPE_CONTROL,
+                                   card, 0, &snd_ctl_reg, name)) < 0)
+        return err;
+    return 0;
+}
+
+/*
+ * disconnection of the control device:
+ * called from init.c
+ */
+int snd_ctl_disconnect(struct snd_card *card)
+{
+    struct list_head *flist;
+    struct snd_ctl_file *ctl;
+
+    down_read(&card->controls_rwsem);
+    list_for_each(flist, &card->ctl_files) {
+        ctl = snd_ctl_file(flist);
+        wake_up(&ctl->change_sleep);
+        kill_fasync(ctl->fasync, SIGIO, POLL_ERR);
+    }
+    up_read(&card->controls_rwsem);
+    return 0;
+}
+
+/*
+ * de-registration of the control device:
+ * called from init.c
+ */
+int snd_ctl_unregister(struct snd_card *card)
+{
+    int err, cardnum;
+    struct snd_kcontrol *control;
+
+    snd_assert(card != NULL, return -ENXIO);
+    cardnum = card->number;
+    snd_assert(cardnum >= 0 && cardnum < SNDRV_CARDS, return -ENXIO);
+    if ((err = snd_unregister_device(SNDRV_DEVICE_TYPE_CONTROL, card, 0)) < 0)
+        return err;
+    down_write(&card->controls_rwsem);
+    while (!list_empty(&card->controls)) {
+        control = snd_kcontrol(card->controls.next);
+        snd_ctl_remove(card, control);
+    }
+    up_write(&card->controls_rwsem);
+    return 0;
+}
Index: /GPL/branches/alsa-resync1/alsa-kernel/core/device.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/core/device.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/core/device.c	(revision 91)
@@ -0,0 +1,240 @@
+/*
+ *  Device management routines
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <sound/driver.h>
+
+/**
+ * snd_device_new - create an ALSA device component
+ * @card: the card instance
+ * @type: the device type, SNDRV_DEV_TYPE_XXX
+ * @device_data: the data pointer of this device
+ * @ops: the operator table
+ *
+ * Creates a new device component for the given data pointer.
+ * The device will be assigned to the card and managed together
+ * by the card.
+ *
+ * The data pointer plays a role as the identifier, too, so the
+ * pointer address must be unique and unchanged.
+ *
+ * Returns zero if successful, or a negative error code on failure.
+ */
+int snd_device_new(struct snd_card *card, snd_device_type_t type,
+		   void *device_data, struct snd_device_ops *ops)
+{
+	struct snd_device *dev;
+
+        snd_assert(card != NULL && device_data != NULL && ops != NULL, return -ENXIO);
+        dev = kcalloc(1, sizeof(*dev), GFP_KERNEL);
+	if (dev == NULL)
+		return -ENOMEM;
+	dev->card = card;
+	dev->type = type;
+	dev->state = SNDRV_DEV_BUILD;
+	dev->device_data = device_data;
+	dev->ops = ops;
+	list_add(&dev->list, &card->devices);	/* add to the head of list */
+	return 0;
+}
+
+/**
+ * snd_device_free - release the device from the card
+ * @card: the card instance
+ * @device_data: the data pointer to release
+ *
+ * Removes the device from the list on the card and invokes the
+ * callback, dev_unregister or dev_free, corresponding to the state.
+ * Then release the device.
+ *
+ * Returns zero if successful, or a negative error code on failure or if the
+ * device not found.
+ */
+int snd_device_free(struct snd_card *card, void *device_data)
+{
+	struct list_head *list;
+	struct snd_device *dev;
+	
+	snd_assert(card != NULL, return -ENXIO);
+	snd_assert(device_data != NULL, return -ENXIO);
+	list_for_each(list, &card->devices) {
+		dev = snd_device(list);
+		if (dev->device_data != device_data)
+			continue;
+		/* unlink */
+		list_del(&dev->list);
+		if ((dev->state == SNDRV_DEV_REGISTERED || dev->state == SNDRV_DEV_DISCONNECTED) &&
+		    dev->ops->dev_unregister) {
+			if (dev->ops->dev_unregister(dev))
+				snd_printk("device unregister failure\n");
+		} else {
+			if (dev->ops->dev_free) {
+				if (dev->ops->dev_free(dev))
+					snd_printk("device free failure\n");
+			}
+                }
+                kfree(dev);
+		return 0;
+	}
+	snd_printd("device free %p (from %p), not found\n", device_data, __builtin_return_address(0));
+	return -ENXIO;
+}
+
+/**
+ * snd_device_free - disconnect the device
+ * @card: the card instance
+ * @device_data: the data pointer to disconnect
+ *
+ * Turns the device into the disconnection state, invoking
+ * dev_disconnect callback, if the device was already registered.
+ *
+ * Usually called from snd_card_disconnect().
+ *
+ * Returns zero if successful, or a negative error code on failure or if the
+ * device not found.
+ */
+int snd_device_disconnect(struct snd_card *card, void *device_data)
+{
+	struct list_head *list;
+	struct snd_device *dev;
+	
+	snd_assert(card != NULL, return -ENXIO);
+	snd_assert(device_data != NULL, return -ENXIO);
+	list_for_each(list, &card->devices) {
+		dev = snd_device(list);
+		if (dev->device_data != device_data)
+			continue;
+		if (dev->state == SNDRV_DEV_REGISTERED && dev->ops->dev_disconnect) {
+			if (dev->ops->dev_disconnect(dev))
+				snd_printk(KERN_ERR "device disconnect failure\n");
+			dev->state = SNDRV_DEV_DISCONNECTED;
+		}
+		return 0;
+	}
+	snd_printd("device disconnect %p (from %p), not found\n", device_data, __builtin_return_address(0));
+	return -ENXIO;
+}
+
+/**
+ * snd_device_register - register the device
+ * @card: the card instance
+ * @device_data: the data pointer to register
+ *
+ * Registers the device which was already created via
+ * snd_device_new().  Usually this is called from snd_card_register(),
+ * but it can be called later if any new devices are created after
+ * invocation of snd_card_register().
+ *
+ * Returns zero if successful, or a negative error code on failure or if the
+ * device not found.
+ */
+int snd_device_register(struct snd_card *card, void *device_data)
+{
+	struct list_head *list;
+	struct snd_device *dev;
+	int err;
+	
+	snd_assert(card != NULL && device_data != NULL, return -ENXIO);
+	list_for_each(list, &card->devices) {
+		dev = snd_device(list);
+		if (dev->device_data != device_data)
+			continue;
+		if (dev->state == SNDRV_DEV_BUILD && dev->ops->dev_register) {
+			if ((err = dev->ops->dev_register(dev)) < 0)
+				return err;
+			dev->state = SNDRV_DEV_REGISTERED;
+			return 0;
+		}
+		return -EBUSY;
+	}
+	snd_BUG();
+	return -ENXIO;
+}
+
+/*
+ * register all the devices on the card.
+ * called from init.c
+ */
+int snd_device_register_all(struct snd_card *card)
+{
+	struct list_head *list;
+	struct snd_device *dev;
+	int err;
+
+        printk("snd_device_register_all %x\n", card);
+	snd_assert(card != NULL, return -ENXIO);
+	list_for_each(list, &card->devices) {
+		dev = snd_device(list);
+		if (dev->state == SNDRV_DEV_BUILD && dev->ops->dev_register) {
+                    if ((err = dev->ops->dev_register(dev)) < 0)
+                    {
+                        printk("err %i et device_register\n", err);
+                        return err;
+                    }
+			dev->state = SNDRV_DEV_REGISTERED;
+		}
+	}
+	return 0;
+}
+
+/*
+ * disconnect all the devices on the card.
+ * called from init.c
+ */
+int snd_device_disconnect_all(struct snd_card *card)
+{
+	struct snd_device *dev;
+	struct list_head *list;
+	int err = 0;
+
+	snd_assert(card != NULL, return -ENXIO);
+	list_for_each(list, &card->devices) {
+		dev = snd_device(list);
+		if (snd_device_disconnect(card, dev->device_data) < 0)
+			err = -ENXIO;
+	}
+	return err;
+}
+
+/*
+ * release all the devices on the card.
+ * called from init.c
+ */
+int snd_device_free_all(struct snd_card *card, snd_device_cmd_t cmd)
+{
+	struct snd_device *dev;
+	struct list_head *list;
+	int err;
+	unsigned int range_low, range_high;
+
+	snd_assert(card != NULL, return -ENXIO);
+	range_low = cmd * SNDRV_DEV_TYPE_RANGE_SIZE;
+	range_high = range_low + SNDRV_DEV_TYPE_RANGE_SIZE - 1;
+      __again:
+	list_for_each(list, &card->devices) {
+		dev = snd_device(list);		
+		if (dev->type >= range_low && dev->type <= range_high) {
+			if ((err = snd_device_free(card, dev->device_data)) < 0)
+				return err;
+			goto __again;
+		}
+	}
+	return 0;
+}
Index: /GPL/branches/alsa-resync1/alsa-kernel/core/diff1.cmd
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/core/diff1.cmd	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/core/diff1.cmd	(revision 91)
@@ -0,0 +1,1 @@
+diff.exe -Nuwr z:\ua2\GPL\alsa-kernel\core\%1 E:\DEV\alsa\alsa-kernel\core\%1 >%1.diff
Index: /GPL/branches/alsa-resync1/alsa-kernel/core/hwdep.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/core/hwdep.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/core/hwdep.c	(revision 91)
@@ -0,0 +1,525 @@
+/*
+ *  Hardware dependent layer
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <sound/control.h>
+#include <sound/minors.h>
+#include <sound/hwdep.h>
+#include <sound/info.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Hardware dependent layer");
+MODULE_LICENSE("GPL");
+
+snd_hwdep_t *snd_hwdep_devices[SNDRV_CARDS * SNDRV_MINOR_HWDEPS];
+
+static DECLARE_MUTEX(register_mutex);
+
+static int snd_hwdep_free(snd_hwdep_t *hwdep);
+static int snd_hwdep_dev_free(snd_device_t *device);
+static int snd_hwdep_dev_register(snd_device_t *device);
+static int snd_hwdep_dev_unregister(snd_device_t *device);
+
+/*
+
+*/
+
+static loff_t snd_hwdep_llseek(struct file * file, loff_t offset, int orig)
+{
+    snd_hwdep_t *hw = file->private_data;
+    if (hw->ops.llseek)
+        return hw->ops.llseek(hw, file, offset, orig);
+    return -ENXIO;
+}
+
+static ssize_t snd_hwdep_read(struct file * file, char *buf, size_t count, loff_t *offset)
+{
+    snd_hwdep_t *hw = file->private_data;
+    if (hw->ops.read)
+        return hw->ops.read(hw, buf, count, offset);
+    return -ENXIO;
+}
+
+static ssize_t snd_hwdep_write(struct file * file, const char *buf, size_t count, loff_t *offset)
+{
+    snd_hwdep_t *hw = file->private_data;
+    if (hw->ops.write)
+        return hw->ops.write(hw, buf, count, offset);
+    return -ENXIO;
+}
+
+static int snd_hwdep_open(struct inode *inode, struct file * file)
+{
+    int major = MAJOR(inode->i_rdev);
+    int cardnum;
+    int device;
+    snd_hwdep_t *hw;
+    int err;
+    wait_queue_t wait;
+
+    switch (major) {
+    case CONFIG_SND_MAJOR:
+        cardnum = SNDRV_MINOR_CARD(MINOR(inode->i_rdev));
+        device = SNDRV_MINOR_DEVICE(MINOR(inode->i_rdev)) - SNDRV_MINOR_HWDEP;
+        break;
+#ifdef CONFIG_SND_OSSEMUL
+    case SOUND_MAJOR:
+        cardnum = SNDRV_MINOR_OSS_CARD(MINOR(inode->i_rdev));
+        device = 0;
+        break;
+#endif
+    default:
+        return -ENXIO;
+    }
+    cardnum %= SNDRV_CARDS;
+    device %= SNDRV_MINOR_HWDEPS;
+    hw = snd_hwdep_devices[(cardnum * SNDRV_MINOR_HWDEPS) + device];
+    if (hw == NULL)
+        return -ENODEV;
+
+    if (!hw->ops.open)
+        return -ENXIO;
+#ifdef CONFIG_SND_OSSEMUL
+    if (major == SOUND_MAJOR && hw->oss_type < 0)
+        return -ENXIO;
+#endif
+
+    if (!try_module_get(hw->card->module))
+        return -EFAULT;
+
+    init_waitqueue_entry(&wait, current);
+    add_wait_queue(&hw->open_wait, &wait);
+    down(&hw->open_mutex);
+    while (1) {
+        if (hw->exclusive && hw->used > 0) {
+            err = -EBUSY;
+            break;
+        }
+        err = hw->ops.open(hw, file);
+        if (err >= 0)
+            break;
+        if (err == -EAGAIN) {
+            if (file->f_flags & O_NONBLOCK) {
+                err = -EBUSY;
+                break;
+            }
+        } else
+            break;
+        set_current_state(TASK_INTERRUPTIBLE);
+        up(&hw->open_mutex);
+        schedule();
+        down(&hw->open_mutex);
+        if (signal_pending(current)) {
+            err = -ERESTARTSYS;
+            break;
+        }
+    }
+    remove_wait_queue(&hw->open_wait, &wait);
+    if (err >= 0) {
+        err = snd_card_file_add(hw->card, file);
+        if (err >= 0) {
+            file->private_data = hw;
+            hw->used++;
+        } else {
+            if (hw->ops.release)
+                hw->ops.release(hw, file);
+        }
+    }
+    up(&hw->open_mutex);
+    if (err < 0)
+        module_put(hw->card->module);
+    return err;
+}
+
+static int snd_hwdep_release(struct inode *inode, struct file * file)
+{
+    int err = -ENXIO;
+    snd_hwdep_t *hw = file->private_data;
+    down(&hw->open_mutex);
+    if (hw->ops.release) {
+        err = hw->ops.release(hw, file);
+        wake_up(&hw->open_wait);
+    }
+    if (hw->used > 0)
+        hw->used--;
+    snd_card_file_remove(hw->card, file);
+    up(&hw->open_mutex);
+    module_put(hw->card->module);
+    return err;
+}
+
+static unsigned int snd_hwdep_poll(struct file * file, poll_table * wait)
+{
+    snd_hwdep_t *hw = file->private_data;
+    if (hw->ops.poll)
+        return hw->ops.poll(hw, file, wait);
+    return 0;
+}
+
+static int snd_hwdep_info(snd_hwdep_t *hw, snd_hwdep_info_t *_info)
+{
+    snd_hwdep_info_t info;
+
+    memset(&info, 0, sizeof(info));
+    info.card = hw->card->number;
+    strlcpy(info.id, hw->id, sizeof(info.id));
+    strlcpy(info.name, hw->name, sizeof(info.name));
+    info.iface = hw->iface;
+    if (copy_to_user(_info, &info, sizeof(info)))
+        return -EFAULT;
+    return 0;
+}
+
+static int snd_hwdep_dsp_status(snd_hwdep_t *hw, snd_hwdep_dsp_status_t *_info)
+{
+    snd_hwdep_dsp_status_t info;
+    int err;
+
+    if (! hw->ops.dsp_status)
+        return -ENXIO;
+    memset(&info, 0, sizeof(info));
+    info.dsp_loaded = hw->dsp_loaded;
+    if ((err = hw->ops.dsp_status(hw, &info)) < 0)
+        return err;
+    if (copy_to_user(_info, &info, sizeof(info)))
+        return -EFAULT;
+    return 0;
+}
+
+static int snd_hwdep_dsp_load(snd_hwdep_t *hw, snd_hwdep_dsp_image_t *_info)
+{
+    snd_hwdep_dsp_image_t info;
+    int err;
+
+    if (! hw->ops.dsp_load)
+        return -ENXIO;
+    memset(&info, 0, sizeof(info));
+    if (copy_from_user(&info, _info, sizeof(info)))
+        return -EFAULT;
+    /* check whether the dsp was already loaded */
+    if (hw->dsp_loaded & (1 << info.index))
+        return -EBUSY;
+    if (verify_area(VERIFY_READ, info.image, info.length))
+        return -EFAULT;
+    err = hw->ops.dsp_load(hw, &info);
+    if (err < 0)
+        return err;
+    hw->dsp_loaded |= (1 << info.index);
+    return 0;
+}
+
+static int snd_hwdep_ioctl(struct inode *inode, struct file * file,
+                           unsigned int cmd, unsigned long arg)
+{
+    snd_hwdep_t *hw = file->private_data;
+    switch (cmd) {
+    case SNDRV_HWDEP_IOCTL_PVERSION:
+        return put_user(SNDRV_HWDEP_VERSION, (int *)arg);
+    case SNDRV_HWDEP_IOCTL_INFO:
+        return snd_hwdep_info(hw, (snd_hwdep_info_t *)arg);
+    case SNDRV_HWDEP_IOCTL_DSP_STATUS:
+        return snd_hwdep_dsp_status(hw, (snd_hwdep_dsp_status_t *)arg);
+    case SNDRV_HWDEP_IOCTL_DSP_LOAD:
+        return snd_hwdep_dsp_load(hw, (snd_hwdep_dsp_image_t *)arg);
+    }
+    if (hw->ops.ioctl)
+        return hw->ops.ioctl(hw, file, cmd, arg);
+    return -ENOTTY;
+}
+
+#ifndef TARGET_OS2
+static int snd_hwdep_mmap(struct file * file, struct vm_area_struct * vma)
+{
+    snd_hwdep_t *hw = file->private_data;
+    if (hw->ops.mmap)
+        return hw->ops.mmap(hw, file, vma);
+    return -ENXIO;
+}
+#endif
+
+static int snd_hwdep_control_ioctl(snd_card_t * card, snd_ctl_file_t * control,
+                                   unsigned int cmd, unsigned long arg)
+{
+    unsigned int tmp;
+
+    tmp = card->number * SNDRV_MINOR_HWDEPS;
+    switch (cmd) {
+    case SNDRV_CTL_IOCTL_HWDEP_NEXT_DEVICE:
+        {
+            int device;
+
+            if (get_user(device, (int *)arg))
+                return -EFAULT;
+            device = device < 0 ? 0 : device + 1;
+            while (device < SNDRV_MINOR_HWDEPS) {
+                if (snd_hwdep_devices[tmp + device])
+                    break;
+                device++;
+            }
+            if (device >= SNDRV_MINOR_HWDEPS)
+                device = -1;
+            if (put_user(device, (int *)arg))
+                return -EFAULT;
+            return 0;
+        }
+    case SNDRV_CTL_IOCTL_HWDEP_INFO:
+        {
+            snd_hwdep_info_t *info = (snd_hwdep_info_t *)arg;
+            int device;
+            snd_hwdep_t *hwdep;
+
+            if (get_user(device, &info->device))
+                return -EFAULT;
+            if (device < 0 || device >= SNDRV_MINOR_HWDEPS)
+                return -ENXIO;
+            hwdep = snd_hwdep_devices[tmp + device];
+            if (hwdep == NULL)
+                return -ENXIO;
+            return snd_hwdep_info(hwdep, info);
+        }
+    }
+    return -ENOIOCTLCMD;
+}
+
+/*
+
+*/
+
+static struct file_operations snd_hwdep_f_ops =
+{
+#ifndef TARGET_OS2
+    .owner = 	THIS_MODULE,
+#endif
+    .llseek =	snd_hwdep_llseek,
+    .read = 	snd_hwdep_read,
+    .write =	snd_hwdep_write,
+    .open =		snd_hwdep_open,
+    .release =	snd_hwdep_release,
+    .poll =		snd_hwdep_poll,
+    .ioctl =	snd_hwdep_ioctl,
+#ifndef TARGET_OS2
+    .compat_ioctl =	snd_hwdep_ioctl_compat,
+    .mmap =		snd_hwdep_mmap,
+#endif
+};
+
+static struct snd_minor snd_hwdep_reg =
+{
+    .comment =	"hardware dependent",
+    .f_ops =	&snd_hwdep_f_ops,
+};
+
+/**
+ * snd_hwdep_new - create a new hwdep instance
+ * @card: the card instance
+ * @id: the id string
+ * @device: the device index (zero-based)
+ * @rhwdep: the pointer to store the new hwdep instance
+ *
+ * Creates a new hwdep instance with the given index on the card.
+ * The callbacks (hwdep->ops) must be set on the returned instance
+ * after this call manually by the caller.
+ *
+ * Returns zero if successful, or a negative error code on failure.
+ */
+int snd_hwdep_new(snd_card_t * card, char *id, int device, snd_hwdep_t ** rhwdep)
+{
+    snd_hwdep_t *hwdep;
+    int err;
+#ifdef TARGET_OS2
+    static snd_device_ops_t ops = {
+        snd_hwdep_dev_free,
+        snd_hwdep_dev_register,
+        0,
+        snd_hwdep_dev_unregister
+    };
+#else
+    static snd_device_ops_t ops = {
+    dev_free:	snd_hwdep_dev_free,
+        dev_register:	snd_hwdep_dev_register,
+        dev_unregister:	snd_hwdep_dev_unregister
+    };
+#endif
+
+    snd_assert(rhwdep != NULL, return -EINVAL);
+    *rhwdep = NULL;
+    snd_assert(card != NULL, return -ENXIO);
+    hwdep = kcalloc(1, sizeof(*hwdep), GFP_KERNEL);
+    if (hwdep == NULL)
+        return -ENOMEM;
+    hwdep->card = card;
+    hwdep->device = device;
+    if (id) {
+        strlcpy(hwdep->id, id, sizeof(hwdep->id));
+    }
+#ifdef CONFIG_SND_OSSEMUL
+    hwdep->oss_type = -1;
+#endif
+    if ((err = snd_device_new(card, SNDRV_DEV_HWDEP, hwdep, &ops)) < 0) {
+        snd_hwdep_free(hwdep);
+        return err;
+    }
+    init_waitqueue_head(&hwdep->open_wait);
+    init_MUTEX(&hwdep->open_mutex);
+    *rhwdep = hwdep;
+    return 0;
+}
+
+static int snd_hwdep_free(snd_hwdep_t *hwdep)
+{
+    snd_assert(hwdep != NULL, return -ENXIO);
+    if (hwdep->private_free)
+        hwdep->private_free(hwdep);
+    kfree(hwdep);
+    return 0;
+}
+
+static int snd_hwdep_dev_free(snd_device_t *device)
+{
+    snd_hwdep_t *hwdep = device->device_data;
+    return snd_hwdep_free(hwdep);
+}
+
+static int snd_hwdep_dev_register(snd_device_t *device)
+{
+    snd_hwdep_t *hwdep = device->device_data;
+    int idx, err;
+    char name[32];
+
+    down(&register_mutex);
+    idx = (hwdep->card->number * SNDRV_MINOR_HWDEPS) + hwdep->device;
+    if (snd_hwdep_devices[idx]) {
+        up(&register_mutex);
+        return -EBUSY;
+    }
+    snd_hwdep_devices[idx] = hwdep;
+    sprintf(name, "hwC%iD%i", hwdep->card->number, hwdep->device);
+    if ((err = snd_register_device(SNDRV_DEVICE_TYPE_HWDEP,
+                                   hwdep->card, hwdep->device,
+                                   &snd_hwdep_reg, name)) < 0) {
+        snd_printk("unable to register hardware dependant device %i:%i\n",
+                   hwdep->card->number, hwdep->device);
+        snd_hwdep_devices[idx] = NULL;
+        up(&register_mutex);
+        return err;
+    }
+#ifdef CONFIG_SND_OSSEMUL
+    hwdep->ossreg = 0;
+    if (hwdep->oss_type >= 0) {
+        if ((hwdep->oss_type == SNDRV_OSS_DEVICE_TYPE_DMFM) && (hwdep->device != 0)) {
+            snd_printk ("only hwdep device 0 can be registered as OSS direct FM device!\n");
+        } else {
+            if (snd_register_oss_device(hwdep->oss_type,
+                                        hwdep->card, hwdep->device,
+                                        &snd_hwdep_reg, hwdep->oss_dev) < 0) {
+                snd_printk("unable to register OSS compatibility device %i:%i\n",
+                           hwdep->card->number, hwdep->device);
+            } else
+                hwdep->ossreg = 1;
+        }
+    }
+#endif
+    up(&register_mutex);
+    return 0;
+}
+
+static int snd_hwdep_dev_unregister(snd_device_t *device)
+{
+    snd_hwdep_t *hwdep = device->device_data;
+    int idx;
+
+    snd_assert(hwdep != NULL, return -ENXIO);
+    down(&register_mutex);
+    idx = (hwdep->card->number * SNDRV_MINOR_HWDEPS) + hwdep->device;
+    if (snd_hwdep_devices[idx] != hwdep) {
+        up(&register_mutex);
+        return -EINVAL;
+    }
+#ifdef CONFIG_SND_OSSEMUL
+    if (hwdep->ossreg)
+        snd_unregister_oss_device(hwdep->oss_type, hwdep->card, hwdep->device);
+#endif
+    snd_unregister_device(SNDRV_DEVICE_TYPE_HWDEP, hwdep->card, hwdep->device);
+    snd_hwdep_devices[idx] = NULL;
+    up(&register_mutex);
+    return snd_hwdep_free(hwdep);
+}
+
+/*
+ *  Info interface
+ */
+
+static void snd_hwdep_proc_read(snd_info_entry_t *entry,
+                                snd_info_buffer_t * buffer)
+{
+    int idx;
+    snd_hwdep_t *hwdep;
+
+    down(&register_mutex);
+    for (idx = 0; idx < SNDRV_CARDS * SNDRV_MINOR_HWDEPS; idx++) {
+        hwdep = snd_hwdep_devices[idx];
+        if (hwdep == NULL)
+            continue;
+        snd_iprintf(buffer, "%02i-%02i: %s\n",
+                    idx / SNDRV_MINOR_HWDEPS,
+                    idx % SNDRV_MINOR_HWDEPS,
+                    hwdep->name);
+    }
+    up(&register_mutex);
+}
+
+/*
+ *  ENTRY functions
+ */
+
+static snd_info_entry_t *snd_hwdep_proc_entry = NULL;
+
+static int __init alsa_hwdep_init(void)
+{
+    snd_info_entry_t *entry;
+
+    memset(snd_hwdep_devices, 0, sizeof(snd_hwdep_devices));
+    if ((entry = snd_info_create_module_entry(THIS_MODULE, "hwdep", NULL)) != NULL) {
+        entry->c.text.read_size = 512;
+        entry->c.text.read = snd_hwdep_proc_read;
+        if (snd_info_register(entry) < 0) {
+            snd_info_free_entry(entry);
+            entry = NULL;
+        }
+    }
+    snd_hwdep_proc_entry = entry;
+    snd_ctl_register_ioctl(snd_hwdep_control_ioctl);
+    return 0;
+}
+
+static void __exit alsa_hwdep_exit(void)
+{
+    snd_ctl_unregister_ioctl(snd_hwdep_control_ioctl);
+    if (snd_hwdep_proc_entry) {
+        snd_info_unregister(snd_hwdep_proc_entry);
+        snd_hwdep_proc_entry = NULL;
+    }
+}
+
+module_init(alsa_hwdep_init)
+module_exit(alsa_hwdep_exit)
+
+EXPORT_SYMBOL(snd_hwdep_new);
Index: /GPL/branches/alsa-resync1/alsa-kernel/core/info.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/core/info.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/core/info.c	(revision 91)
@@ -0,0 +1,1109 @@
+/*
+ *  Information interface for ALSA driver
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <sound/minors.h>
+#include <sound/info.h>
+#include <sound/version.h>
+#include <stdarg.h>
+#ifdef CONFIG_DEVFS_FS
+#include <linux/devfs_fs_kernel.h>
+#endif
+
+#define min(x,y) (x < y ? x : y )
+
+/*
+ *
+ */
+
+int snd_info_check_reserved_words(const char *str)
+{
+    static char *reserved[] =
+    {
+        "version",
+        "meminfo",
+        "memdebug",
+        "detect",
+        "devices",
+        "oss",
+        "cards",
+        "timers",
+        "synth",
+        "pcm",
+        "seq",
+        NULL
+    };
+    char **xstr = reserved;
+
+    while (*xstr) {
+        if (!strcmp(*xstr, str))
+            return 0;
+        xstr++;
+    }
+    if (!strncmp(str, "card", 4))
+        return 0;
+    return 1;
+}
+
+#ifdef CONFIG_PROC_FS
+
+static DECLARE_MUTEX(info_mutex);
+
+typedef struct _snd_info_private_data {
+    snd_info_buffer_t *rbuffer;
+    snd_info_buffer_t *wbuffer;
+    snd_info_entry_t *entry;
+    void *file_private_data;
+} snd_info_private_data_t;
+
+static int snd_info_version_init(void);
+static int snd_info_version_done(void);
+
+
+#ifndef TARGET_OS2
+/**
+ * snd_iprintf - printf on the procfs buffer
+ * @buffer: the procfs buffer
+ * @fmt: the printf format
+ *
+ * Outputs the string on the procfs buffer just like printf().
+ *
+ * Returns the size of output string.
+ */
+int snd_iprintf(snd_info_buffer_t * buffer, char *fmt,...)
+{
+    va_list args;
+    int res;
+    char sbuffer[512];
+
+    if (buffer->stop || buffer->error)
+        return 0;
+    va_start(args, fmt);
+    res = vsprintf(sbuffer, fmt, args);
+    va_end(args);
+    if (buffer->size + res >= buffer->len) {
+        buffer->stop = 1;
+        return 0;
+    }
+    strcpy(buffer->curr, sbuffer);
+    buffer->curr += res;
+    buffer->size += res;
+    return res;
+}
+#endif
+
+/*
+
+*/
+
+static struct proc_dir_entry *snd_proc_root = NULL;
+snd_info_entry_t *snd_seq_root = NULL;
+#ifdef CONFIG_SND_OSSEMUL
+snd_info_entry_t *snd_oss_root = NULL;
+#endif
+
+static inline void snd_info_entry_prepare(struct proc_dir_entry *de)
+{
+    de->owner = THIS_MODULE;
+}
+
+void snd_remove_proc_entry(struct proc_dir_entry *parent,
+                           struct proc_dir_entry *de)
+{
+    if (de)
+        remove_proc_entry(de->name, parent);
+}
+
+static loff_t snd_info_entry_llseek(struct file *file, loff_t offset, int orig)
+{
+    snd_info_private_data_t *data;
+    struct snd_info_entry *entry;
+    loff_t ret;
+
+    data = file->private_data;
+    entry = data->entry;
+#ifndef TARGET_OS2
+    lock_kernel();
+#endif
+    switch (entry->content) {
+    case SNDRV_INFO_CONTENT_TEXT:
+        switch (orig) {
+        case 0:	/* SEEK_SET */
+            file->f_pos = offset;
+            ret = file->f_pos;
+            goto out;
+        case 1:	/* SEEK_CUR */
+            file->f_pos += offset;
+            ret = file->f_pos;
+            goto out;
+        case 2:	/* SEEK_END */
+        default:
+            ret = -EINVAL;
+            goto out;
+        }
+        break;
+    case SNDRV_INFO_CONTENT_DATA:
+        if (entry->c.ops->llseek) {
+            ret = entry->c.ops->llseek(entry,
+                                       data->file_private_data,
+                                       file, offset, orig);
+            goto out;
+        }
+        break;
+    }
+    ret = -ENXIO;
+out:
+#ifndef TARGET_OS2
+    unlock_kernel();
+#endif
+    return ret;
+}
+
+static ssize_t snd_info_entry_read(struct file *file, char *buffer,
+                                   size_t count, loff_t * offset)
+{
+    snd_info_private_data_t *data;
+    struct snd_info_entry *entry;
+    snd_info_buffer_t *buf;
+    long size = 0;
+    loff_t pos;
+
+    data = file->private_data;
+    snd_assert(data != NULL, return -ENXIO);
+    pos = *offset;
+    if (pos < 0 || (long) pos != pos || (ssize_t) count < 0)
+        return -EIO;
+    if ((unsigned long) pos + (unsigned long) count < (unsigned long) pos)
+        return -EIO;
+    entry = data->entry;
+    switch (entry->content) {
+    case SNDRV_INFO_CONTENT_TEXT:
+        buf = data->rbuffer;
+        if (buf == NULL)
+            return -EIO;
+        if (pos >= buf->size)
+            return 0;
+        size = buf->size - pos;
+        size = min(count, size);
+        if (copy_to_user(buffer, buf->buffer + pos, size))
+            return -EFAULT;
+        break;
+    case SNDRV_INFO_CONTENT_DATA:
+        if (entry->c.ops->read)
+            size = entry->c.ops->read(entry,
+                                      data->file_private_data,
+                                      file, buffer, count, pos);
+        break;
+    }
+    if ((ssize_t) size > 0)
+        *offset = pos + size;
+    return size;
+}
+
+static ssize_t snd_info_entry_write(struct file *file, const char *buffer,
+                                    size_t count, loff_t * offset)
+{
+    snd_info_private_data_t *data;
+    struct snd_info_entry *entry;
+    snd_info_buffer_t *buf;
+    long size = 0;
+    loff_t pos;
+
+    data = file->private_data;
+    snd_assert(data != NULL, return -ENXIO);
+    entry = data->entry;
+    pos = *offset;
+    if (pos < 0 || (long) pos != pos || (ssize_t) count < 0)
+        return -EIO;
+    if ((unsigned long) pos + (unsigned long) count < (unsigned long) pos)
+        return -EIO;
+    switch (entry->content) {
+    case SNDRV_INFO_CONTENT_TEXT:
+        buf = data->wbuffer;
+        if (buf == NULL)
+            return -EIO;
+        if (pos >= buf->len)
+            return -ENOMEM;
+        size = buf->len - pos;
+        size = min(count, size);
+        if (copy_from_user(buf->buffer + pos, buffer, size))
+            return -EFAULT;
+        if ((long)buf->size < pos + size)
+            buf->size = pos + size;
+        break;
+    case SNDRV_INFO_CONTENT_DATA:
+        if (entry->c.ops->write)
+            size = entry->c.ops->write(entry,
+                                       data->file_private_data,
+                                       file, buffer, count, pos);
+        break;
+    }
+    if ((ssize_t) size > 0)
+        *offset = pos + size;
+    return size;
+}
+
+#ifndef TARGET_OS2
+static int snd_info_entry_open(struct inode *inode, struct file *file)
+{
+    snd_info_entry_t *entry;
+    snd_info_private_data_t *data;
+    snd_info_buffer_t *buffer;
+    struct proc_dir_entry *p;
+    int mode, err;
+
+    down(&info_mutex);
+    p = (struct proc_dir_entry *) inode->u.generic_ip;
+    entry = p == NULL ? NULL : (snd_info_entry_t *)p->data;
+    if (entry == NULL) {
+        up(&info_mutex);
+        return -ENODEV;
+    }
+#ifndef LINUX_2_3
+    MOD_INC_USE_COUNT;
+#endif
+    if (entry->module && !try_inc_mod_count(entry->module)) {
+        err = -EFAULT;
+        goto __error1;
+    }
+    mode = file->f_flags & O_ACCMODE;
+    if (mode == O_RDONLY || mode == O_RDWR) {
+        if ((entry->content == SNDRV_INFO_CONTENT_TEXT &&
+             !entry->c.text.read_size) ||
+            (entry->content == SNDRV_INFO_CONTENT_DATA &&
+             entry->c.ops->read == NULL) ||
+            entry->content == SNDRV_INFO_CONTENT_DEVICE) {
+            err = -ENODEV;
+            goto __error;
+        }
+    }
+    if (mode == O_WRONLY || mode == O_RDWR) {
+        if ((entry->content == SNDRV_INFO_CONTENT_TEXT &&
+             !entry->c.text.write_size) ||
+            (entry->content == SNDRV_INFO_CONTENT_DATA &&
+             entry->c.ops->write == NULL) ||
+            entry->content == SNDRV_INFO_CONTENT_DEVICE) {
+            err = -ENODEV;
+            goto __error;
+        }
+    }
+    data = kcalloc(1, sizeof(*data), GFP_KERNEL);
+    if (data == NULL) {
+        err = -ENOMEM;
+        goto __error;
+    }
+    data->entry = entry;
+    switch (entry->content) {
+    case SNDRV_INFO_CONTENT_TEXT:
+        if (mode == O_RDONLY || mode == O_RDWR) {
+            buffer = (snd_info_buffer_t *)
+                snd_kcalloc(sizeof(snd_info_buffer_t), GFP_KERNEL);
+            if (buffer == NULL) {
+                kfree(data);
+                err = -ENOMEM;
+                goto __error;
+            }
+            buffer->len = (entry->c.text.read_size +
+                           (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
+            buffer->buffer = vmalloc(buffer->len);
+            if (buffer->buffer == NULL) {
+                kfree(buffer);
+                kfree(data);
+                err = -ENOMEM;
+                goto __error;
+            }
+            buffer->curr = buffer->buffer;
+            data->rbuffer = buffer;
+        }
+        if (mode == O_WRONLY || mode == O_RDWR) {
+            buffer = (snd_info_buffer_t *)
+                snd_kcalloc(sizeof(snd_info_buffer_t), GFP_KERNEL);
+            if (buffer == NULL) {
+                if (mode == O_RDWR) {
+                    vfree(data->rbuffer->buffer);
+                    kfree(data->rbuffer);
+                }
+                kfree(data);
+                err = -ENOMEM;
+                goto __error;
+            }
+            buffer->len = (entry->c.text.write_size +
+                           (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
+            buffer->buffer = vmalloc(buffer->len);
+            if (buffer->buffer == NULL) {
+                if (mode == O_RDWR) {
+                    vfree(data->rbuffer->buffer);
+                    kfree(data->rbuffer);
+                }
+                kfree(buffer);
+                kfree(data);
+                err = -ENOMEM;
+                goto __error;
+            }
+            buffer->curr = buffer->buffer;
+            data->wbuffer = buffer;
+        }
+        break;
+    case SNDRV_INFO_CONTENT_DATA:	/* data */
+        if (entry->c.ops->open) {
+            if ((err = entry->c.ops->open(entry, mode,
+                                          &data->file_private_data)) < 0) {
+                kfree(data);
+                goto __error;
+            }
+        }
+        break;
+    }
+    file->private_data = data;
+    up(&info_mutex);
+    if (entry->content == SNDRV_INFO_CONTENT_TEXT &&
+        (mode == O_RDONLY || mode == O_RDWR)) {
+        if (entry->c.text.read) {
+            down(&entry->access);
+            entry->c.text.read(entry, data->rbuffer);
+            up(&entry->access);
+        }
+    }
+    return 0;
+
+__error:
+    dec_mod_count(entry->module);
+__error1:
+#ifndef LINUX_2_3
+    MOD_DEC_USE_COUNT;
+#endif
+    up(&info_mutex);
+    return err;
+}
+#endif
+
+static int snd_info_entry_release(struct inode *inode, struct file *file)
+{
+    snd_info_entry_t *entry;
+    snd_info_private_data_t *data;
+    int mode;
+
+    mode = file->f_flags & O_ACCMODE;
+    data = file->private_data;
+    entry = data->entry;
+    switch (entry->content) {
+    case SNDRV_INFO_CONTENT_TEXT:
+        if (mode == O_RDONLY || mode == O_RDWR) {
+            vfree(data->rbuffer->buffer);
+            kfree(data->rbuffer);
+        }
+        if (mode == O_WRONLY || mode == O_RDWR) {
+            if (entry->c.text.write) {
+                entry->c.text.write(entry, data->wbuffer);
+                if (data->wbuffer->error) {
+                    snd_printk("data write error to %s (%i)\n",
+                               entry->name,
+                               data->wbuffer->error);
+                }
+            }
+            vfree(data->wbuffer->buffer);
+            kfree(data->wbuffer);
+        }
+        break;
+    case SNDRV_INFO_CONTENT_DATA:
+        if (entry->c.ops->release)
+            entry->c.ops->release(entry, mode,
+                                  data->file_private_data);
+        break;
+    }
+    module_put(entry->module);
+    kfree(data);
+    return 0;
+}
+
+static unsigned int snd_info_entry_poll(struct file *file, poll_table * wait)
+{
+    snd_info_private_data_t *data;
+    struct snd_info_entry *entry;
+    unsigned int mask;
+
+    data = file->private_data;
+    if (data == NULL)
+        return 0;
+    entry = data->entry;
+    mask = 0;
+    switch (entry->content) {
+    case SNDRV_INFO_CONTENT_DATA:
+        if (entry->c.ops->poll)
+            return entry->c.ops->poll(entry,
+                                      data->file_private_data,
+                                      file, wait);
+        if (entry->c.ops->read)
+            mask |= POLLIN | POLLRDNORM;
+        if (entry->c.ops->write)
+            mask |= POLLOUT | POLLWRNORM;
+        break;
+    }
+    return mask;
+}
+
+static int snd_info_entry_ioctl(struct inode *inode, struct file *file,
+                                unsigned int cmd, unsigned long arg)
+{
+    snd_info_private_data_t *data;
+    struct snd_info_entry *entry;
+
+    data = file->private_data;
+    if (data == NULL)
+        return 0;
+    entry = data->entry;
+    switch (entry->content) {
+    case SNDRV_INFO_CONTENT_DATA:
+        if (entry->c.ops->ioctl)
+            return entry->c.ops->ioctl(entry,
+                                       data->file_private_data,
+                                       file, cmd, arg);
+        break;
+    }
+    return -ENOTTY;
+}
+
+static int snd_info_entry_mmap(struct file *file, struct vm_area_struct *vma)
+{
+    struct inode *inode = file->f_dentry->d_inode;
+    snd_info_private_data_t *data;
+    struct snd_info_entry *entry;
+
+    data = file->private_data;
+    if (data == NULL)
+        return 0;
+    entry = data->entry;
+    switch (entry->content) {
+    case SNDRV_INFO_CONTENT_DATA:
+        if (entry->c.ops->mmap)
+            return entry->c.ops->mmap(entry,
+                                      data->file_private_data,
+                                      inode, file, vma);
+        break;
+    }
+    return -ENXIO;
+}
+
+#ifdef TARGET_OS2
+static struct file_operations snd_info_entry_operations =
+{
+#ifdef LINUX_2_3
+    THIS_MODULE,
+#endif
+    snd_info_entry_llseek,
+    snd_info_entry_read,
+    snd_info_entry_write,
+    0,
+    snd_info_entry_poll,
+    snd_info_entry_ioctl,
+    snd_info_entry_mmap,
+    0,
+    //	snd_info_entry_open,
+    0,
+    snd_info_entry_release,
+    0,0,0,0,0
+};
+#else
+static struct file_operations snd_info_entry_operations =
+{
+#ifdef LINUX_2_3
+owner:		THIS_MODULE,
+#endif
+    llseek:		snd_info_entry_llseek,
+    read:		snd_info_entry_read,
+    write:		snd_info_entry_write,
+    poll:		snd_info_entry_poll,
+    ioctl:		snd_info_entry_ioctl,
+    mmap:		snd_info_entry_mmap,
+    open:		snd_info_entry_open,
+    release:	snd_info_entry_release,
+};
+
+#ifndef LINUX_2_3
+static struct inode_operations snd_info_entry_inode_operations =
+{
+    &snd_info_entry_operations,	/* default sound info directory file-ops */
+};
+
+static struct inode_operations snd_info_device_inode_operations =
+{
+    &snd_fops,		/* default sound info directory file-ops */
+};
+#endif	/* LINUX_2_3 */
+
+static int snd_info_card_readlink(struct dentry *dentry,
+                                  char *buffer, int buflen)
+{
+    char *s = ((struct proc_dir_entry *) dentry->d_inode->u.generic_ip)->data;
+#ifdef LINUX_2_3
+    return vfs_readlink(dentry, buffer, buflen, s);
+#else
+    int len;
+
+    if (s == NULL)
+        return -EIO;
+    len = strlen(s);
+    if (len > buflen)
+        len = buflen;
+    if (copy_to_user(buffer, s, len))
+        return -EFAULT;
+    return len;
+#endif
+}
+
+#ifdef LINUX_2_3
+static int snd_info_card_followlink(struct dentry *dentry,
+                                    struct nameidata *nd)
+{
+    char *s = ((struct proc_dir_entry *) dentry->d_inode->u.generic_ip)->data;
+    return vfs_follow_link(nd, s);
+}
+#else
+static struct dentry *snd_info_card_followlink(struct dentry *dentry,
+                                               struct dentry *base,
+                                               unsigned int follow)
+{
+    char *s = ((struct proc_dir_entry *) dentry->d_inode->u.generic_ip)->data;
+    return lookup_dentry(s, base, follow);
+}
+#endif
+
+#ifndef LINUX_2_3
+static struct file_operations snd_info_card_link_operations =
+{
+    NULL
+};
+#endif
+
+struct inode_operations snd_info_card_link_inode_operations =
+{
+#ifndef LINUX_2_3
+default_file_ops:	&snd_info_card_link_operations,
+#endif
+    readlink:		snd_info_card_readlink,
+    follow_link:		snd_info_card_followlink,
+};
+#endif //TARGET_OS2
+
+/**
+ * snd_create_proc_entry - create a procfs entry
+ * @name: the name of the proc file
+ * @mode: the file permission bits, S_Ixxx
+ * @parent: the parent proc-directory entry
+ *
+ * Creates a new proc file entry with the given name and permission
+ * on the given directory.
+ *
+ * Returns the pointer of new instance or NULL on failure.
+ */
+struct proc_dir_entry *snd_create_proc_entry(const char *name, mode_t mode,
+                                             struct proc_dir_entry *parent)
+{
+    struct proc_dir_entry *p;
+    p = create_proc_entry(name, mode, parent);
+    if (p)
+        snd_info_entry_prepare(p);
+    return p;
+}
+
+int __init snd_info_init(void)
+{
+    struct proc_dir_entry *p;
+
+    p = snd_create_proc_entry("asound", S_IFDIR | S_IRUGO | S_IXUGO, &proc_root);
+    if (p == NULL)
+        return -ENOMEM;
+    snd_proc_root = p;
+#ifdef CONFIG_SND_OSSEMUL
+    {
+        snd_info_entry_t *entry;
+        if ((entry = snd_info_create_module_entry(THIS_MODULE, "oss", NULL)) == NULL)
+            return -ENOMEM;
+        entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
+        if (snd_info_register(entry) < 0) {
+            snd_info_free_entry(entry);
+            return -ENOMEM;
+        }
+        snd_oss_root = entry;
+    }
+#endif
+#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
+    {
+        snd_info_entry_t *entry;
+        if ((entry = snd_info_create_module_entry(THIS_MODULE, "seq", NULL)) == NULL)
+            return -ENOMEM;
+        entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
+        if (snd_info_register(entry) < 0) {
+            snd_info_free_entry(entry);
+            return -ENOMEM;
+        }
+        snd_seq_root = entry;
+    }
+#endif
+    snd_info_version_init();
+#ifdef CONFIG_SND_DEBUG_MEMORY
+    snd_memory_info_init();
+#endif
+    snd_minor_info_init();
+#ifdef CONFIG_SND_OSSEMUL
+    snd_minor_info_oss_init();
+#endif
+    snd_card_info_init();
+    return 0;
+}
+
+int __exit snd_info_done(void)
+{
+    snd_card_info_done();
+#ifdef CONFIG_SND_OSSEMUL
+    snd_minor_info_oss_done();
+#endif
+    snd_minor_info_done();
+#ifdef CONFIG_SND_DEBUG_MEMORY
+    snd_memory_info_done();
+#endif
+    snd_info_version_done();
+    if (snd_proc_root) {
+#ifdef CONFIG_SND_SEQUENCER
+        if (snd_seq_root)
+            snd_info_unregister(snd_seq_root);
+#endif
+        snd_remove_proc_entry(&proc_root, snd_proc_root);
+    }
+    return 0;
+}
+
+/*
+
+*/
+
+
+/*
+ * create a card proc file
+ * called from init.c
+ */
+int snd_info_card_create(snd_card_t * card)
+{
+    char str[8];
+    snd_info_entry_t *entry;
+
+    snd_assert(card != NULL, return -ENXIO);
+
+    sprintf(str, "card%i", card->number);
+    if ((entry = snd_info_create_module_entry(card->module, str, NULL)) == NULL)
+        return -ENOMEM;
+    entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
+    if (snd_info_register(entry) < 0) {
+        snd_info_free_entry(entry);
+        return -ENOMEM;
+    }
+    card->proc_root = entry;
+    return 0;
+}
+
+/*
+ * register the card proc file
+ * called from init.c
+ */
+int snd_info_card_register(snd_card_t * card)
+{
+    struct proc_dir_entry *p = NULL;
+
+    snd_assert(card != NULL, return -ENXIO);
+
+    if (!strcmp(card->id, card->proc_root->name))
+        return 0;
+
+#ifndef TARGET_OS2
+    p = proc_symlink(card->id, snd_proc_root, card->proc_root->name);
+    if (p == NULL)
+        return -ENOMEM;
+#endif
+    card->proc_root_link = p;
+    return 0;
+}
+
+/*
+ * de-register the card proc file
+ * called from init.c
+ */
+int snd_info_card_free(snd_card_t * card)
+{
+    snd_assert(card != NULL, return -ENXIO);
+    if (card->proc_root_link) {
+        snd_remove_proc_entry(snd_proc_root, card->proc_root_link);
+        card->proc_root_link = NULL;
+    }
+    if (card->proc_root) {
+        snd_info_unregister(card->proc_root);
+        card->proc_root = NULL;
+    }
+    return 0;
+}
+
+/**
+ * snd_info_get_line - read one line from the procfs buffer
+ * @buffer: the procfs buffer
+ * @line: the buffer to store
+ * @len: the max. buffer size - 1
+ *
+ * Reads one line from the buffer and stores the string.
+ *
+ * Returns zero if successful, or 1 if error or EOF.
+ */
+int snd_info_get_line(snd_info_buffer_t * buffer, char *line, int len)
+{
+    int c = -1;
+
+    if (len <= 0 || buffer->stop || buffer->error)
+        return 1;
+    while (--len > 0) {
+        c = *buffer->curr++;
+        if (c == '\n') {
+            if ((buffer->curr - buffer->buffer) >= (long)buffer->size) {
+                buffer->stop = 1;
+            }
+            break;
+        }
+        *line++ = c;
+        if ((buffer->curr - buffer->buffer) >= (long)buffer->size) {
+            buffer->stop = 1;
+            break;
+        }
+    }
+    while (c != '\n' && !buffer->stop) {
+        c = *buffer->curr++;
+        if ((buffer->curr - buffer->buffer) >= (long)buffer->size) {
+            buffer->stop = 1;
+        }
+    }
+    *line = '\0';
+    return 0;
+}
+
+/**
+ * snd_info_get_line - parse a string token
+ * @dest: the buffer to store the string token
+ * @src: the original string
+ * @len: the max. length of token - 1
+ *
+ * Parses the original string and copy a token to the given
+ * string buffer.
+ *
+ * Returns the updated pointer of the original string so that
+ * it can be used for the next call.
+ */
+char *snd_info_get_str(char *dest, char *src, int len)
+{
+    int c;
+
+    while (*src == ' ' || *src == '\t')
+        src++;
+    if (*src == '"' || *src == '\'') {
+        c = *src++;
+        while (--len > 0 && *src && *src != c) {
+            *dest++ = *src++;
+        }
+        if (*src == c)
+            src++;
+    } else {
+        while (--len > 0 && *src && *src != ' ' && *src != '\t') {
+            *dest++ = *src++;
+        }
+    }
+    *dest = 0;
+    while (*src == ' ' || *src == '\t')
+        src++;
+    return src;
+}
+
+/**
+ * snd_info_create_entry - create an info entry
+ * @name: the proc file name
+ *
+ * Creates an info entry with the given file name and initializes as
+ * the default state.
+ *
+ * Usually called from other functions such as
+ * snd_info_create_card_entry().
+ *
+ * Returns the pointer of the new instance, or NULL on failure.
+ */
+static snd_info_entry_t *snd_info_create_entry(const char *name)
+{
+    snd_info_entry_t *entry;
+    entry = kcalloc(1, sizeof(*entry), GFP_KERNEL);
+    if (entry == NULL)
+        return NULL;
+    entry->name = snd_kmalloc_strdup(name, GFP_KERNEL);
+    if (entry->name == NULL) {
+        kfree(entry);
+        return NULL;
+    }
+    entry->mode = S_IFREG | S_IRUGO;
+    entry->content = SNDRV_INFO_CONTENT_TEXT;
+    init_MUTEX(&entry->access);
+    return entry;
+}
+
+/**
+ * snd_info_create_module_entry - create an info entry for the given module
+ * @module: the module pointer
+ * @name: the file name
+ * @parent: the parent directory
+ *
+ * Creates a new info entry and assigns it to the given module.
+ *
+ * Returns the pointer of the new instance, or NULL on failure.
+ */
+snd_info_entry_t *snd_info_create_module_entry(struct module * module,
+                                               const char *name,
+                                               snd_info_entry_t *parent)
+{
+    snd_info_entry_t *entry = snd_info_create_entry(name);
+    if (entry) {
+        entry->module = module;
+        entry->parent = parent;
+    }
+    return entry;
+}
+
+/**
+ * snd_info_create_card_entry - create an info entry for the given card
+ * @card: the card instance
+ * @name: the file name
+ * @parent: the parent directory
+ *
+ * Creates a new info entry and assigns it to the given card.
+ *
+ * Returns the pointer of the new instance, or NULL on failure.
+ */
+snd_info_entry_t *snd_info_create_card_entry(snd_card_t * card,
+                                             const char *name,
+                                             snd_info_entry_t * parent)
+{
+    snd_info_entry_t *entry = snd_info_create_entry(name);
+    if (entry) {
+        entry->module = card->module;
+        entry->card = card;
+        entry->parent = parent;
+    }
+    return entry;
+}
+
+static int snd_info_dev_free_entry(snd_device_t *device)
+{
+    snd_info_entry_t *entry = device->device_data;
+    snd_info_free_entry(entry);
+    return 0;
+}
+
+static int snd_info_dev_register_entry(snd_device_t *device)
+{
+    snd_info_entry_t *entry = device->device_data;
+    return snd_info_register(entry);
+}
+
+static int snd_info_dev_disconnect_entry(snd_device_t *device)
+{
+    snd_info_entry_t *entry = device->device_data;
+    entry->disconnected = 1;
+    return 0;
+}
+
+static int snd_info_dev_unregister_entry(snd_device_t *device)
+{
+    snd_info_entry_t *entry = device->device_data;
+    return snd_info_unregister(entry);
+}
+
+/**
+ * snd_card_proc_new - create an info entry for the given card
+ * @card: the card instance
+ * @name: the file name
+ * @entryp: the pointer to store the new info entry
+ *
+ * Creates a new info entry and assigns it to the given card.
+ * Unlike snd_info_create_card_entry(), this function registers the
+ * info entry as an ALSA device component, so that it can be
+ * unregistered/released without explicit call.
+ * Also, you don't have to register this entry via snd_info_register(),
+ * since this will be registered by snd_card_register() automatically.
+ *
+ * The parent is assumed as card->proc_root.
+ *
+ * For releasing this entry, use snd_device_free() instead of
+ * snd_info_free_entry().
+ *
+ * Returns zero if successful, or a negative error code on failure.
+ */
+//#ifndef TARGET_OS2
+int snd_card_proc_new(snd_card_t *card, const char *name,
+                      snd_info_entry_t **entryp)
+{
+    static snd_device_ops_t ops = {
+        /*.dev_free = */snd_info_dev_free_entry,
+        /*.dev_register =*/ snd_info_dev_register_entry,
+        /*.dev_disconnect = */snd_info_dev_disconnect_entry,
+        /*.dev_unregister = */snd_info_dev_unregister_entry
+    };
+    snd_info_entry_t *entry;
+    int err;
+
+    entry = snd_info_create_card_entry(card, name, card->proc_root);
+    if (! entry)
+        return -ENOMEM;
+    if ((err = snd_device_new(card, SNDRV_DEV_INFO, entry, &ops)) < 0) {
+        snd_info_free_entry(entry);
+        return err;
+    }
+    if (entryp)
+        *entryp = entry;
+    return 0;
+}
+//#endif
+
+/**
+ * snd_info_free_entry - release the info entry
+ * @entry: the info entry
+ *
+ * Releases the info entry.  Don't call this after registered.
+ */
+void snd_info_free_entry(snd_info_entry_t * entry)
+{
+    if (entry == NULL)
+        return;
+    if (entry->name)
+        kfree((char *)entry->name);
+    if (entry->private_free)
+        entry->private_free(entry);
+    kfree(entry);
+}
+
+/**
+ * snd_info_register - register the info entry
+ * @entry: the info entry
+ *
+ * Registers the proc info entry.
+ *
+ * Returns zero if successful, or a negative error code on failure.
+ */
+int snd_info_register(snd_info_entry_t * entry)
+{
+    struct proc_dir_entry *root, *p = NULL;
+
+    snd_assert(entry != NULL, return -ENXIO);
+    root = entry->parent == NULL ? snd_proc_root : entry->parent->p;
+    down(&info_mutex);
+    p = snd_create_proc_entry(entry->name, entry->mode, root);
+    if (!p) {
+        up(&info_mutex);
+        return -ENOMEM;
+    }
+#ifdef LINUX_2_3
+    p->owner = entry->module;
+#endif
+
+#ifndef TARGET_OS2
+    if (!S_ISDIR(entry->mode)) {
+#ifdef LINUX_2_3
+        p->proc_fops = &snd_info_entry_operations;
+#else
+        p->ops = &snd_info_entry_inode_operations;
+#endif
+    }
+#endif
+    p->size = entry->size;
+    p->data = entry;
+    entry->p = p;
+    up(&info_mutex);
+    return 0;
+}
+
+/**
+ * snd_info_unregister - de-register the info entry
+ * @entry: the info entry
+ *
+ * De-registers the info entry and releases the instance.
+ *
+ * Returns zero if successful, or a negative error code on failure.
+ */
+int snd_info_unregister(snd_info_entry_t * entry)
+{
+    struct proc_dir_entry *root;
+
+    snd_assert(entry != NULL && entry->p != NULL, return -ENXIO);
+    root = entry->parent == NULL ? snd_proc_root : entry->parent->p;
+    snd_assert(root, return -ENXIO);
+    down(&info_mutex);
+    snd_remove_proc_entry(root, entry->p);
+    up(&info_mutex);
+    snd_info_free_entry(entry);
+    return 0;
+}
+
+/*
+
+*/
+
+static snd_info_entry_t *snd_info_version_entry = NULL;
+
+static void snd_info_version_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer)
+{
+    static char *kernel_version = UTS_RELEASE;
+
+    snd_iprintf(buffer,
+                "Advanced Linux Sound Architecture Driver Version " CONFIG_SND_VERSION ".\n"
+                "Compiled on " __DATE__ " for kernel %s"
+#ifdef __SMP__
+                " (SMP)"
+#endif
+#ifdef MODVERSIONS
+                " with versioned symbols"
+#endif
+                ".\n", kernel_version);
+}
+
+static int __init snd_info_version_init(void)
+{
+    snd_info_entry_t *entry;
+
+    entry = snd_info_create_module_entry(THIS_MODULE, "version", NULL);
+    if (entry == NULL)
+        return -ENOMEM;
+    entry->c.text.read_size = 256;
+    entry->c.text.read = snd_info_version_read;
+    if (snd_info_register(entry) < 0) {
+        snd_info_free_entry(entry);
+        return -ENOMEM;
+    }
+    snd_info_version_entry = entry;
+    return 0;
+}
+
+static int __exit snd_info_version_done(void)
+{
+    if (snd_info_version_entry)
+        snd_info_unregister(snd_info_version_entry);
+    return 0;
+}
+
+#endif /* CONFIG_PROC_FS */
Index: /GPL/branches/alsa-resync1/alsa-kernel/core/init.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/core/init.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/core/init.c	(revision 91)
@@ -0,0 +1,831 @@
+/*
+ *  Initialization routines
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <sound/control.h>
+#include <sound/info.h>
+
+struct snd_shutdown_f_ops {
+	struct file_operations f_ops;
+	struct snd_shutdown_f_ops *next;
+};
+
+int snd_cards_count = 0;
+#ifdef TARGET_OS2
+struct snd_card *snd_cards[SNDRV_CARDS] = {0};
+#else
+struct snd_card *snd_cards[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = NULL};
+#endif
+unsigned int snd_cards_lock = 0;	/* locked for registering/using */
+rwlock_t snd_card_rwlock = RW_LOCK_UNLOCKED;
+
+static unsigned int pci_saved_config[SNDRV_CARDS][16];
+
+#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
+int (*snd_mixer_oss_notify_callback)(struct snd_card *card, int free_flag);
+#endif
+
+static void snd_card_id_read(snd_info_entry_t *entry, struct snd_info_buffer * buffer)
+{
+	snd_iprintf(buffer, "%s\n", entry->card->id);
+}
+
+static void snd_card_free_thread(void * __card);
+
+/**
+ *  snd_card_new - create and initialize a soundcard structure
+ *  @idx: card index (address) [0 ... (SNDRV_CARDS-1)]
+ *  @xid: card identification (ASCII string)
+ *  @module: top level module for locking
+ *  @extra_size: allocate this extra size after the main soundcard structure
+ *
+ *  Creates and initializes a soundcard structure.
+ *
+ *  Returns kmallocated struct snd_card structure. Creates the ALSA control interface
+ *  (which is blocked until snd_card_register function is called).
+ */
+struct snd_card *snd_card_new(int idx, const char *xid,
+			 struct module *module, int extra_size)
+{
+	struct snd_card *card;
+	int err;
+
+	if (extra_size < 0)
+            extra_size = 0;
+        card = kcalloc(1, sizeof(*card) + extra_size, GFP_KERNEL);
+	if (card == NULL)
+		return NULL;
+	if (xid) {
+		if (!snd_info_check_reserved_words(xid))
+			goto __error;
+		strlcpy(card->id, xid, sizeof(card->id));
+	}
+	write_lock(&snd_card_rwlock);
+	if (idx < 0) {
+		int idx2;
+		for (idx2 = 0; idx2 < snd_ecards_limit; idx2++)
+			if (!(snd_cards_lock & (1 << idx2))) {
+				idx = idx2;
+				break;
+			}
+		if (idx < 0 && snd_ecards_limit < SNDRV_CARDS)
+			/* for dynamically additional devices like hotplug:
+			 * increment the limit if still free slot exists.
+			 */
+			idx = snd_ecards_limit++;
+	} else if (idx < snd_ecards_limit) {
+		if (snd_cards_lock & (1 << idx))
+			idx = -1;	/* invalid */
+	} else if (idx < SNDRV_CARDS)
+		snd_ecards_limit = idx + 1; /* increase the limit */
+	else
+		idx = -1;
+	if (idx < 0) {
+		write_unlock(&snd_card_rwlock);
+		if (idx >= snd_ecards_limit)
+			snd_printk("card %i is out of range (0-%i)\n", idx, snd_ecards_limit-1);
+		goto __error;
+	}
+	snd_cards_lock |= 1 << idx;		/* lock it */
+	write_unlock(&snd_card_rwlock);
+	card->number = idx;
+	card->module = module;
+	INIT_LIST_HEAD(&card->devices);
+	init_rwsem(&card->controls_rwsem);
+	rwlock_init(&card->ctl_files_rwlock);
+	INIT_LIST_HEAD(&card->controls);
+	INIT_LIST_HEAD(&card->ctl_files);
+	spin_lock_init(&card->files_lock);
+	init_waitqueue_head(&card->shutdown_sleep);
+	INIT_WORK(&card->free_workq, snd_card_free_thread, card);
+#ifdef CONFIG_PM
+	init_MUTEX(&card->power_lock);
+	init_waitqueue_head(&card->power_sleep);
+#endif
+	/* the control interface cannot be accessed from the user space until */
+	/* snd_cards_bitmask and snd_cards are set with snd_card_register */
+	if ((err = snd_ctl_register(card)) < 0) {
+		snd_printd("unable to register control minors\n");
+		goto __error;
+	}
+	if ((err = snd_info_card_create(card)) < 0) {
+		snd_printd("unable to create card info\n");
+		goto __error_ctl;
+	}
+	if (extra_size > 0)
+		card->private_data = (char *)card + sizeof(struct snd_card);
+	return card;
+
+      __error_ctl:
+	snd_ctl_unregister(card);
+      __error:
+	kfree(card);
+      	return NULL;
+}
+
+static unsigned int snd_disconnect_poll(struct file * file, poll_table * wait)
+{
+	return POLLERR | POLLNVAL;
+}
+
+/**
+ *  snd_card_disconnect - disconnect all APIs from the file-operations (user space)
+ *  @card: soundcard structure
+ *
+ *  Disconnects all APIs from the file-operations (user space).
+ *
+ *  Returns zero, otherwise a negative error code.
+ *
+ *  Note: The current implementation replaces all active file->f_op with special
+ *        dummy file operations (they do nothing except release).
+ */
+int snd_card_disconnect(struct snd_card * card)
+{
+	struct snd_monitor_file *mfile;
+	struct file *file;
+	struct snd_shutdown_f_ops *s_f_ops;
+	struct file_operations *f_ops, *old_f_ops;
+	int err;
+
+	spin_lock(&card->files_lock);
+	if (card->shutdown) {
+		spin_unlock(&card->files_lock);
+		return 0;
+	}
+	card->shutdown = 1;
+	spin_unlock(&card->files_lock);
+
+	/* phase 1: disable fops (user space) operations for ALSA API */
+	write_lock(&snd_card_rwlock);
+	snd_cards[card->number] = NULL;
+	write_unlock(&snd_card_rwlock);
+
+	/* phase 2: replace file->f_op with special dummy operations */
+
+	spin_lock(&card->files_lock);
+	mfile = card->files;
+	while (mfile) {
+		file = mfile->file;
+
+		/* it's critical part, use endless loop */
+		/* we have no room to fail */
+		s_f_ops = kmalloc(sizeof(struct snd_shutdown_f_ops), GFP_ATOMIC);
+                if (s_f_ops == NULL)
+                {
+                    snd_printk("Atomic allocation failed for snd_shutdown_f_ops!");
+                    return -1;
+                }
+
+		f_ops = &s_f_ops->f_ops;
+
+		memset(f_ops, 0, sizeof(*f_ops));
+#ifdef LINUX_2_3
+                f_ops->owner = file->f_op->owner;
+#endif
+		f_ops->release = file->f_op->release;
+		f_ops->poll = snd_disconnect_poll;
+
+		s_f_ops->next = card->s_f_ops;
+		card->s_f_ops = s_f_ops;
+
+		f_ops = fops_get(f_ops);
+
+		old_f_ops = file->f_op;
+		file->f_op = f_ops;	/* must be atomic */
+		fops_put(old_f_ops);
+
+		mfile = mfile->next;
+	}
+	spin_unlock(&card->files_lock);
+
+	/* phase 3: notify all connected devices about disconnection */
+	/* at this point, they cannot respond to any calls except release() */
+
+	snd_ctl_disconnect(card);
+
+#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
+	if (snd_mixer_oss_notify_callback)
+		snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_DISCONNECT);
+#endif
+
+	/* notify all devices that we are disconnected */
+	err = snd_device_disconnect_all(card);
+	if (err < 0)
+		snd_printk(KERN_ERR "not all devices for card %i can be disconnected\n", card->number);
+
+	return 0;
+}
+
+/**
+ *  snd_card_free - frees given soundcard structure
+ *  @card: soundcard structure
+ *
+ *  This function releases the soundcard structure and the all assigned
+ *  devices automatically.  That is, you don't have to release the devices
+ *  by yourself.
+ *
+ *  Returns zero. Frees all associated devices and frees the control
+ *  interface associated to given soundcard.
+ */
+int snd_card_free(struct snd_card * card)
+{
+	struct snd_shutdown_f_ops *s_f_ops;
+
+	if (card == NULL)
+		return -EINVAL;
+	write_lock(&snd_card_rwlock);
+	snd_cards[card->number] = NULL;
+	snd_cards_count--;
+	write_unlock(&snd_card_rwlock);
+
+#ifdef CONFIG_PM
+        wake_up(&card->power_sleep);
+#ifdef CONFIG_ISA
+        if (card->pm_dev) {
+            pm_unregister(card->pm_dev);
+            card->pm_dev = NULL;
+        }
+#endif
+#endif
+
+#ifndef TARGET_OS2
+	/* wait, until all devices are ready for the free operation */
+	wait_event(card->shutdown_sleep, card->files == NULL);
+#endif
+
+#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
+	if (snd_mixer_oss_notify_callback)
+		snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_FREE);
+#endif
+	if (snd_device_free_all(card, SNDRV_DEV_CMD_PRE) < 0) {
+		snd_printk("unable to free all devices (pre)\n");
+		/* Fatal, but this situation should never occur */
+	}
+	if (snd_device_free_all(card, SNDRV_DEV_CMD_NORMAL) < 0) {
+		snd_printk("unable to free all devices (normal)\n");
+		/* Fatal, but this situation should never occur */
+	}
+	if (snd_ctl_unregister(card) < 0) {
+		snd_printk("unable to unregister control minors\n");
+		/* Not fatal error */
+	}
+	if (snd_device_free_all(card, SNDRV_DEV_CMD_POST) < 0) {
+		snd_printk("unable to free all devices (post)\n");
+		/* Fatal, but this situation should never occur */
+	}
+	if (card->private_free)
+            card->private_free(card);
+        if (card->proc_id)
+            snd_info_unregister(card->proc_id);
+	if (snd_info_card_free(card) < 0) {
+		snd_printk(KERN_WARNING "unable to free card info\n");
+		/* Not fatal error */
+	}
+	while (card->s_f_ops) {
+		s_f_ops = card->s_f_ops;
+		card->s_f_ops = s_f_ops->next;
+		kfree(s_f_ops);
+	}
+	write_lock(&snd_card_rwlock);
+	snd_cards_lock &= ~(1 << card->number);
+	write_unlock(&snd_card_rwlock);
+	kfree(card);
+	return 0;
+}
+
+static void snd_card_free_thread(void * __card)
+{
+	struct snd_card *card = __card;
+	struct module * module = card->module;
+
+	if (!try_module_get(module)) {
+		snd_printk(KERN_ERR "unable to lock toplevel module for card %i in free thread\n", card->number);
+		module = NULL;
+	}
+
+	snd_card_free(card);
+
+	module_put(module);
+}
+
+/**
+ *  snd_card_free_in_thread - call snd_card_free() in thread
+ *  @card: soundcard structure
+ *
+ *  This function schedules the call of snd_card_free() function in a
+ *  work queue.  When all devices are released (non-busy), the work
+ *  is woken up and calls snd_card_free().
+ *
+ *  When a card can be disconnected at any time by hotplug service,
+ *  this function should be used in disconnect (or detach) callback
+ *  instead of calling snd_card_free() directly.
+ *
+ *  Returns - zero otherwise a negative error code if the start of thread failed.
+ */
+int snd_card_free_in_thread(struct snd_card * card)
+{
+	if (card->files == NULL) {
+		snd_card_free(card);
+		return 0;
+	}
+
+#ifndef TARGET_OS2
+	if (schedule_work(&card->free_workq))
+		return 0;
+#endif
+
+	snd_printk(KERN_ERR "schedule_work() failed in snd_card_free_in_thread for card %i\n", card->number);
+	/* try to free the structure immediately */
+	snd_card_free(card);
+	return -EFAULT;
+}
+
+static void choose_default_id(struct snd_card * card)
+{
+	int i, len, idx_flag = 0, loops = 8;
+	char *id, *spos;
+
+	id = spos = card->shortname;
+	while (*id != '\0') {
+		if (*id == ' ')
+			spos = id + 1;
+		id++;
+	}
+	id = card->id;
+	while (*spos != '\0' && !isalnum1(*spos))
+		spos++;
+	if (isdigit1(*spos))
+		*id++ = isalpha1(card->shortname[0]) ? card->shortname[0] : 'D';
+	while (*spos != '\0' && (size_t)(id - card->id) < sizeof(card->id) - 1) {
+		if (isalnum1(*spos))
+			*id++ = *spos;
+		spos++;
+	}
+	*id = '\0';
+
+	id = card->id;
+
+	if (*id == '\0')
+		strcpy(id, "default");
+
+	while (1) {
+	      	if (loops-- == 0) {
+      			snd_printk(KERN_ERR "unable to choose default card id (%s)", id);
+      			strcpy(card->id, card->proc_root->name);
+      			return;
+      		}
+	      	if (!snd_info_check_reserved_words(id))
+      			goto __change;
+		for (i = 0; i < snd_ecards_limit; i++) {
+			if (snd_cards[i] && !strcmp(snd_cards[i]->id, id))
+				goto __change;
+		}
+		break;
+
+	      __change:
+		len = strlen(id);
+		if (idx_flag)
+			id[len-1]++;
+		else if ((size_t)len <= sizeof(card->id) - 3) {
+			strcat(id, "_1");
+			idx_flag++;
+		} else {
+			spos = id + len - 2;
+			if ((size_t)len <= sizeof(card->id) - 2)
+				spos++;
+			*spos++ = '_';
+			*spos++ = '1';
+			*spos++ = '\0';
+			idx_flag++;
+		}
+	}
+}
+
+/**
+ *  snd_card_register - register the soundcard
+ *  @card: soundcard structure
+ *
+ *  This function registers all the devices assigned to the soundcard.
+ *  Until calling this, the ALSA control interface is blocked from the
+ *  external accesses.  Thus, you should call this function at the end
+ *  of the initialization of the card.
+ *
+ *  Returns zero otherwise a negative error code if the registrain failed.
+ */
+int snd_card_register(struct snd_card * card)
+{
+	int err;
+	snd_info_entry_t *entry;
+
+#ifdef DEBUG
+        dprintf(("snd_card_register"));
+#endif
+        snd_runtime_check(card != NULL, return -EINVAL);
+        if ((err = snd_device_register_all(card)) < 0)
+        {
+            printk("err %i at snd_device_register_all\n", err);
+            return err;
+        }
+	write_lock(&snd_card_rwlock);
+	if (snd_cards[card->number]) {
+		/* already registered */
+		write_unlock(&snd_card_rwlock);
+		return 0;
+        }
+
+	if (card->id[0] == '\0')
+		choose_default_id(card);
+	snd_cards[card->number] = card;
+	snd_cards_count++;
+	write_unlock(&snd_card_rwlock);
+	if ((err = snd_info_card_register(card)) < 0) {
+		snd_printd("unable to create card info\n");
+		goto __skip_info;
+	}
+	if ((entry = snd_info_create_card_entry(card, "id", card->proc_root)) == NULL) {
+		snd_printd("unable to create card entry\n");
+		goto __skip_info;
+	}
+	entry->c.text.read_size = PAGE_SIZE;
+	entry->c.text.read = snd_card_id_read;
+	if (snd_info_register(entry) < 0) {
+		snd_info_free_entry(entry);
+		entry = NULL;
+	}
+	card->proc_id = entry;
+      __skip_info:
+#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
+	if (snd_mixer_oss_notify_callback)
+		snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_REGISTER);
+#endif
+	return 0;
+}
+
+static snd_info_entry_t *snd_card_info_entry = NULL;
+
+static void snd_card_info_read(snd_info_entry_t *entry, struct snd_info_buffer * buffer)
+{
+	int idx, count;
+	struct snd_card *card;
+
+	for (idx = count = 0; idx < SNDRV_CARDS; idx++) {
+		read_lock(&snd_card_rwlock);
+		if ((card = snd_cards[idx]) != NULL) {
+			count++;
+			snd_iprintf(buffer, "%i [%-15s]: %s - %s\n",
+					idx,
+					card->id,
+					card->driver,
+					card->shortname);
+			snd_iprintf(buffer, "                     %s\n",
+					card->longname);
+		}
+		read_unlock(&snd_card_rwlock);
+	}
+	if (!count)
+		snd_iprintf(buffer, "--- no soundcards ---\n");
+}
+
+#if defined(CONFIG_SND_OSSEMUL) && defined(CONFIG_PROC_FS)
+
+void snd_card_info_read_oss(snd_info_buffer_t * buffer)
+{
+	int idx, count;
+	struct snd_card *card;
+
+	for (idx = count = 0; idx < SNDRV_CARDS; idx++) {
+		read_lock(&snd_card_rwlock);
+		if ((card = snd_cards[idx]) != NULL) {
+			count++;
+			snd_iprintf(buffer, "%s\n", card->longname);
+		}
+		read_unlock(&snd_card_rwlock);
+	}
+	if (!count) {
+		snd_iprintf(buffer, "--- no soundcards ---\n");
+	}
+}
+
+#endif
+
+#ifdef MODULE
+static snd_info_entry_t *snd_card_module_info_entry;
+static void snd_card_module_info_read(snd_info_entry_t *entry, struct snd_info_buffer * buffer)
+{
+	int idx;
+	struct snd_card *card;
+
+	for (idx = 0; idx < SNDRV_CARDS; idx++) {
+		read_lock(&snd_card_rwlock);
+//		if ((card = snd_cards[idx]) != NULL) //???
+//			snd_iprintf(buffer, "%i %s\n", idx, card->module->name);
+		read_unlock(&snd_card_rwlock);
+	}
+}
+#endif
+
+int __init snd_card_info_init(void)
+{
+	snd_info_entry_t *entry;
+
+	entry = snd_info_create_module_entry(THIS_MODULE, "cards", NULL);
+	snd_runtime_check(entry != NULL, return -ENOMEM);
+	entry->c.text.read_size = PAGE_SIZE;
+	entry->c.text.read = snd_card_info_read;
+	if (snd_info_register(entry) < 0) {
+		snd_info_free_entry(entry);
+		return -ENOMEM;
+	}
+	snd_card_info_entry = entry;
+
+#ifdef MODULE
+	entry = snd_info_create_module_entry(THIS_MODULE, "modules", NULL);
+	if (entry) {
+		entry->content = SNDRV_INFO_CONTENT_TEXT;
+		entry->c.text.read_size = PAGE_SIZE;
+		entry->c.text.read = snd_card_module_info_read;
+		if (snd_info_register(entry) < 0)
+			snd_info_free_entry(entry);
+		else
+			snd_card_module_info_entry = entry;
+	}
+#endif
+
+	return 0;
+}
+
+int __exit snd_card_info_done(void)
+{
+	if (snd_card_info_entry)
+		snd_info_unregister(snd_card_info_entry);
+#ifdef MODULE
+	if (snd_card_module_info_entry)
+		snd_info_unregister(snd_card_module_info_entry);
+#endif
+	return 0;
+}
+
+/**
+ *  snd_component_add - add a component string
+ *  @card: soundcard structure
+ *  @component: the component id string
+ *
+ *  This function adds the component id string to the supported list.
+ *  The component can be referred from the alsa-lib.
+ *
+ *  Returns zero otherwise a negative error code.
+ */
+
+int snd_component_add(struct snd_card *card, const char *component)
+{
+	char *ptr;
+	int len = strlen(component);
+
+	ptr = strstr1(card->components, component);
+	if (ptr != NULL) {
+		if (ptr[len] == '\0' || ptr[len] == ' ')	/* already there */
+			return 1;
+	}
+	if (strlen(card->components) + 1 + len + 1 > sizeof(card->components)) {
+		snd_BUG();
+		return -ENOMEM;
+	}
+	if (card->components[0] != '\0')
+		strcat(card->components, " ");
+	strcat(card->components, component);
+	return 0;
+}
+/**
+ *  snd_card_file_add - add the file to the file list of the card
+ *  @card: soundcard structure
+ *  @file: file pointer
+ *
+ *  This function adds the file to the file linked-list of the card.
+ *  This linked-list is used to keep tracking the connection state,
+ *  and to avoid the release of busy resources by hotplug.
+ *
+ *  Returns zero or a negative error code.
+ */
+int snd_card_file_add(struct snd_card *card, struct file *file)
+{
+	struct snd_monitor_file *mfile;
+
+	mfile = kmalloc(sizeof(*mfile), GFP_KERNEL);
+	if (mfile == NULL)
+		return -ENOMEM;
+	mfile->file = file;
+	mfile->next = NULL;
+	spin_lock(&card->files_lock);
+	if (card->shutdown) {
+		spin_unlock(&card->files_lock);
+		kfree(mfile);
+		return -ENODEV;
+	}
+	mfile->next = card->files;
+	card->files = mfile;
+	spin_unlock(&card->files_lock);
+	return 0;
+}
+
+/**
+ *  snd_card_file_remove - remove the file from the file list
+ *  @card: soundcard structure
+ *  @file: file pointer
+ *
+ *  This function removes the file formerly added to the card via
+ *  snd_card_file_add() function.
+ *  If all files are removed and the release of the card is
+ *  scheduled, it will wake up the the thread to call snd_card_free()
+ *  (see snd_card_free_in_thread() function).
+ *
+ *  Returns zero or a negative error code.
+ */
+int snd_card_file_remove(struct snd_card *card, struct file *file)
+{
+	struct snd_monitor_file *mfile, *pfile = NULL;
+
+	spin_lock(&card->files_lock);
+	mfile = card->files;
+	while (mfile) {
+		if (mfile->file == file) {
+			if (pfile)
+				pfile->next = mfile->next;
+			else
+				card->files = mfile->next;
+			break;
+		}
+		pfile = mfile;
+		mfile = mfile->next;
+	}
+	spin_unlock(&card->files_lock);
+	if (card->files == NULL)
+		wake_up(&card->shutdown_sleep);
+	if (mfile) {
+		kfree(mfile);
+	} else {
+		snd_printk(KERN_ERR "ALSA card file remove problem (%p)\n", file);
+		return -ENOENT;
+	}
+	return 0;
+}
+
+#ifdef CONFIG_PM
+/**
+ *  snd_power_wait - wait until the power-state is changed.
+ *  @card: soundcard structure
+ *  @power_state: expected power state
+ *  @file: file structure for the O_NONBLOCK check (optional)
+ *
+ *  Waits until the power-state is changed.
+ *
+ *  Note: the power lock must be active before call.
+ */
+int snd_power_wait(struct snd_card *card, unsigned int power_state, struct file *file)
+{
+	wait_queue_t wait;
+	int result = 0;
+
+	/* fastpath */
+	if (snd_power_get_state(card) == power_state)
+		return 0;
+	init_waitqueue_entry(&wait, current);
+	add_wait_queue(&card->power_sleep, &wait);
+	while (1) {
+		if (card->shutdown) {
+			result = -ENODEV;
+			break;
+		}
+		if (snd_power_get_state(card) == power_state)
+			break;
+#if 0 /* block all devices */
+		if (file && (file->f_flags & O_NONBLOCK)) {
+			result = -EAGAIN;
+			break;
+		}
+#endif
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		snd_power_unlock(card);
+		schedule_timeout(30 * HZ);
+		snd_power_lock(card);
+	}
+	remove_wait_queue(&card->power_sleep, &wait);
+	return result;
+}
+
+/**
+ * snd_card_set_pm_callback - set the PCI power-management callbacks
+ * @card: soundcard structure
+ * @suspend: suspend callback function
+ * @resume: resume callback function
+ * @private_data: private data to pass to the callback functions
+ *
+ * Sets the power-management callback functions of the card.
+ * These callbacks are called from ALSA's common PCI suspend/resume
+ * handler and from the control API.
+ */
+int snd_card_set_pm_callback(struct snd_card *card,
+                             int (*suspend)(struct snd_card *, unsigned int),
+                             int (*resume)(struct snd_card *, unsigned int),
+                             void *private_data)
+{
+    card->pm_suspend = suspend;
+    card->pm_resume = resume;
+    card->pm_private_data = private_data;
+    return 0;
+}
+
+#ifdef CONFIG_ISA
+static int snd_isa_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data)
+{
+    struct snd_card *card = dev->data;
+
+    switch (rqst) {
+    case PM_SUSPEND:
+        /* FIXME: the correct state value? */
+        card->pm_suspend(card, 0);
+        break;
+    case PM_RESUME:
+        /* FIXME: the correct state value? */
+        card->pm_resume(card, 0);
+        break;
+    }
+    return 0;
+}
+
+/**
+ * snd_card_set_isa_pm_callback - set the ISA power-management callbacks
+ * @card: soundcard structure
+ * @suspend: suspend callback function
+ * @resume: resume callback function
+ * @private_data: private data to pass to the callback functions
+ *
+ * Registers the power-management and sets the lowlevel callbacks for
+ * the given ISA card.  These callbacks are called from the ALSA's
+ * common PM handler and from the control API.
+ */
+int snd_card_set_isa_pm_callback(struct snd_card *card,
+                                 int (*suspend)(struct snd_card *, unsigned int),
+                                 int (*resume)(struct snd_card *, unsigned int),
+                                 void *private_data)
+{
+    card->pm_dev = pm_register(PM_ISA_DEV, 0, snd_isa_pm_callback);
+    if (! card->pm_dev)
+        return -ENOMEM;
+    card->pm_dev->data = card;
+    return snd_card_set_pm_callback(card, suspend, resume, private_data);
+}
+#endif
+
+#ifdef CONFIG_PCI
+int snd_card_pci_suspend(struct pci_dev *dev, u32 state)
+{
+    int err;
+    struct snd_card *card = pci_get_drvdata(dev);
+    if (! card || ! card->pm_suspend)
+        return 0;
+    if (card->power_state == SNDRV_CTL_POWER_D3hot)
+        return 0;
+    /* FIXME: correct state value? */
+    err = card->pm_suspend(card, 0);
+    pci_save_state(dev);
+    snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+    return err;
+}
+
+int snd_card_pci_resume(struct pci_dev *dev)
+{
+    struct snd_card *card = pci_get_drvdata(dev);
+    if (! card || ! card->pm_resume)
+        return 0;
+    printk("pci resume from %i state\n", card->power_state);
+    if (card->power_state == SNDRV_CTL_POWER_D0)
+        return 0;
+    /* restore the PCI config space */
+    pci_restore_state(dev);
+    /* FIXME: correct state value? */
+    card->pm_resume(card, 0);
+    snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+    return 0;
+}
+#endif
+
+#endif /* CONFIG_PM */
+
Index: /GPL/branches/alsa-resync1/alsa-kernel/core/isadma.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/core/isadma.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/core/isadma.c	(revision 91)
@@ -0,0 +1,80 @@
+/*
+ *  ISA DMA support functions
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/*
+ * Defining following add some delay. Maybe this helps for some broken
+ * ISA DMA controllers.
+ */
+
+#undef HAVE_REALLY_SLOW_DMA_CONTROLLER
+
+#include <sound/driver.h>
+
+#ifdef CONFIG_SND_ISA
+
+/*
+ *
+ */
+ 
+void snd_dma_program(unsigned long dma,
+		     const void *buf, unsigned int size,
+                     unsigned short mode)
+{
+	unsigned long flags;
+
+	flags = claim_dma_lock();
+	disable_dma(dma);
+	clear_dma_ff(dma);
+	set_dma_mode(dma, mode);
+	set_dma_addr(dma, virt_to_bus((void *) buf));
+	set_dma_count(dma, size);
+	if (!(mode & DMA_MODE_NO_ENABLE))
+		enable_dma(dma);
+	release_dma_lock(flags);
+}
+
+void snd_dma_disable(unsigned long dma)
+{
+	unsigned long flags;
+
+	flags = claim_dma_lock();
+	clear_dma_ff(dma);
+	disable_dma(dma);
+	release_dma_lock(flags);
+}
+
+unsigned int snd_dma_residue(unsigned long dma)
+{
+	unsigned long flags;
+	unsigned int result;
+
+	flags = claim_dma_lock();
+	clear_dma_ff(dma);
+	if (!isa_dma_bridge_buggy)
+		disable_dma(dma);
+	result = get_dma_residue(dma);
+	if (!isa_dma_bridge_buggy)
+		enable_dma(dma);
+	release_dma_lock(flags);
+	return result;
+}
+
+#endif /* CONFIG_SND_ISA */
Index: /GPL/branches/alsa-resync1/alsa-kernel/core/makefile.os2
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/core/makefile.os2	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/core/makefile.os2	(revision 91)
@@ -0,0 +1,81 @@
+CINCLUDES = ..\..\include;..\include;$(%WATCOM)\H
+AINCLUDES = $(DDK_BASE)\H
+
+CDEFINES = -D__KERNEL__ -DMODULE -dTARGET_OS2 -dALSA_BUILD -D__ISAPNP__ -DCONFIG_ISAPNP -D__i386_
+
+32BIT=1
+!include ..\..\..\makefile.inc
+
+NMAKEOPTS=-f makefile.os2 $(LOGO)
+
+!if "$(DEBUG)" == "1"
+NMAKEOPTS += DEBUG=1
+!endif
+
+!if "$(KEE)" == "0"
+NMAKEOPTS += KEE=0
+KEE=0
+!else
+NMAKEOPTS += KEE=1
+KEE=1
+!endif
+
+!if "$(DEBUG)" == "1"
+!Message BUILDING DEBUG VERSION
+!else
+!Message BUILDING RELEASE VERSION
+!endif
+
+#===================================================================
+#
+#   List of source files
+#
+#===================================================================
+FILE1    = sound.obj init.obj memory.obj memalloc.obj sgbuf.obj
+FILE2    = control.obj device.obj misc.obj wrappers.obj 
+#FILE3    = sound_oss.obj info_oss.obj isadma.obj
+FILE4    = pcm.obj pcm_native.obj pcm_lib.obj pcm_misc.obj pcm_timer.obj pcm_memory.obj
+FILE5    = rawmidi.obj
+FILE6    = timer.obj
+FILE7    = rtctimer.obj
+FILE8    = hwdep.obj info.obj
+FILELAST =
+FILES    = $(FILE0) $(FILE1) $(FILE2) $(FILE3) $(FILE4) $(FILE5) $(FILE6) $(FILE7) $(FILE8) $(FILE9) $(FILE10) $(FILE11) $(FILE12)
+
+TARGET   = core
+
+LNKFILE  = $(OBJDIR)\$(TARGET).lnk
+
+
+#===================================================================
+#
+#   Specific dependencies
+#
+#===================================================================
+all: $(OBJDIR)\$(TARGET).lib subdirs
+
+
+$(LNKFILE): makefile.os2
+    @%create $(LNKFILE)
+    @for %f in ($(FILES)) do @%append $(LNKFILE) -+$(OBJDIR)\%f
+
+$(OBJDIR)\$(TARGET).lib: $(LNKFILE) $(FILES)
+    $(LIB) $(OBJDIR)\$(TARGET).lib @$(LNKFILE)
+    copy $(OBJDIR)\$(TARGET).lib $(ALSA_LIB)
+
+
+subdirs: .SYMBOLIC
+    @echo BUILDING seq
+    @cd seq
+    @wmake $(NMAKEOPTS)
+    @cd ..
+
+clean: .SYMBOLIC
+    @echo CLEARING seq
+    @cd seq
+    @wmake $(NMAKEOPTS) clean
+    @cd ..
+    -@rm *.err
+    @cd $(OBJDIR)
+    -rm *.obj *.lnk *.sys *.sym *.lst *.map *.wmap *.lib 2>nul
+    @cd ..\..
Index: /GPL/branches/alsa-resync1/alsa-kernel/core/memalloc.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/core/memalloc.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/core/memalloc.c	(revision 91)
@@ -0,0 +1,884 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *                   Takashi Iwai <tiwai@suse.de>
+ *
+ *  Generic memory allocators
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <sound/info.h>
+#include <sound/memalloc.h>
+
+
+MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>, Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Memory allocator for ALSA system.");
+MODULE_LICENSE("GPL");
+
+
+#ifndef SNDRV_CARDS
+#define SNDRV_CARDS	8
+#endif
+static int enable[SNDRV_CARDS] = {1,1,1,1,1,1,1,1};
+MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(enable, "Enable cards to allocate buffers.");
+
+
+void *snd_malloc_sgbuf_pages(struct device *device,
+                             size_t size, struct snd_dma_buffer *dmab,
+			     size_t *res_size);
+int snd_free_sgbuf_pages(struct snd_dma_buffer *dmab);
+
+/*
+ */
+
+static DECLARE_MUTEX(list_mutex);
+static LIST_HEAD(mem_list_head);
+
+/* buffer preservation list */
+struct snd_mem_list {
+    struct snd_dma_buffer buffer;
+    unsigned int id;
+    struct list_head list;
+};
+
+#include <linux/proc_fs.h>
+static inline struct proc_dir_entry *create_proc_read_entry(const char *name,
+        mode_t mode, struct proc_dir_entry *base,
+        read_proc_t *read_proc, void * data)
+{
+        struct proc_dir_entry *res=create_proc_entry(name,mode,base);
+        if (res) {
+                res->read_proc=read_proc;
+                res->data=data;
+        }
+        return res;
+}
+
+/* id for pre-allocated buffers */
+#define SNDRV_DMA_DEVICE_UNUSED (unsigned int)-1
+
+#ifdef CONFIG_PCI
+#if defined(__i386__) || defined(__ppc__) || defined(__x86_64__)
+#define HACK_PCI_ALLOC_CONSISTENT
+
+/*
+ * A hack to allocate large buffers via pci_alloc_consistent()
+ *
+ * since pci_alloc_consistent always tries GFP_DMA when the requested
+ * pci memory region is below 32bit, it happens quite often that even
+ * 2 order of pages cannot be allocated.
+ *
+ * so in the following, we allocate at first without dma_mask, so that
+ * allocation will be done without GFP_DMA.  if the area doesn't match
+ * with the requested region, then realloate with the original dma_mask
+ * again.
+ */
+
+static void *snd_pci_hack_alloc_consistent(struct pci_dev *hwdev, size_t size,
+				    dma_addr_t *dma_handle)
+{
+	void *ret;
+	u64 dma_mask, cdma_mask;
+	unsigned long mask;
+
+	if (hwdev == NULL)
+		return pci_alloc_consistent(hwdev, size, dma_handle);
+	dma_mask = hwdev->dma_mask;
+	cdma_mask = hwdev->consistent_dma_mask;
+	mask = (unsigned long)dma_mask && (unsigned long)cdma_mask;
+	hwdev->dma_mask = 0xffffffff; /* do without masking */
+	hwdev->consistent_dma_mask = 0xffffffff; /* do without masking */
+	ret = pci_alloc_consistent(hwdev, size, dma_handle);
+	hwdev->dma_mask = dma_mask; /* restore */
+	hwdev->consistent_dma_mask = cdma_mask; /* restore */
+	if (ret) {
+		/* obtained address is out of range? */
+		if (((unsigned long)*dma_handle + size - 1) & ~mask) {
+			/* reallocate with the proper mask */
+			pci_free_consistent(hwdev, size, ret, *dma_handle);
+			ret = pci_alloc_consistent(hwdev, size, dma_handle);
+		}
+	} else {
+		/* wish to success now with the proper mask... */
+		if (mask != 0xffffffffUL)
+			ret = pci_alloc_consistent(hwdev, size, dma_handle);
+	}
+	return ret;
+}
+
+/* redefine pci_alloc_consistent for some architectures */
+#undef pci_alloc_consistent
+#define pci_alloc_consistent snd_pci_hack_alloc_consistent
+
+#endif /* arch */
+/* for 2.2/2.4 kernels */
+#define dma_alloc_coherent(dev,size,addr,flags) pci_alloc_consistent((struct pci_dev *)(dev),size,addr)
+#define dma_free_coherent(dev,size,ptr,addr) pci_free_consistent((struct pci_dev *)(dev),size,ptr,addr)
+
+#endif /* CONFIG_PCI */
+
+/* allocate the coherent DMA pages */
+static void *snd_malloc_dev_pages(struct device *dev, size_t size, dma_addr_t *dma)
+{
+	int pg;
+	void *res;
+	unsigned int gfp_flags;
+
+	snd_assert(size > 0, return NULL);
+	snd_assert(dma != NULL, return NULL);
+	pg = get_order(size);
+	gfp_flags = GFP_KERNEL;
+	if (pg > 0)
+		gfp_flags |= __GFP_NOWARN;
+	res = dma_alloc_coherent(dev, PAGE_SIZE << pg, dma, gfp_flags);
+	if (res != NULL) {
+		inc_snd_pages(pg);
+	}
+
+	return res;
+}
+
+/* free the coherent DMA pages */
+static void snd_free_dev_pages(struct device *dev, size_t size, void *ptr,
+			       dma_addr_t dma)
+{
+	int pg;
+
+	if (ptr == NULL)
+		return;
+	pg = get_order(size);
+	dec_snd_pages(pg);
+	dma_free_coherent(dev, PAGE_SIZE << pg, ptr, dma);
+}
+
+#if ! defined(__arm__)
+#define NEED_RESERVE_PAGES
+#endif
+/**
+ * snd_dma_alloc_pages - allocate the buffer area according to the given type
+ * @dev: the buffer device info
+ * @size: the buffer size to allocate
+ * @dmab: buffer allocation record to store the allocated data
+ *
+ * Calls the memory-allocator function for the corresponding
+ * buffer type.
+ *
+ * Returns zero if the buffer with the given size is allocated successfuly,
+ * other a negative value at error.
+ */
+int snd_dma_alloc_pages(int type, struct device *device, size_t size,
+			struct snd_dma_buffer *dmab)
+{
+#ifdef DEBUG
+    dprintf(("snd_dma_alloc_pages with size = %d",size));
+#endif
+    snd_assert(size > 0, return -ENXIO);
+    snd_assert(dmab != NULL, return -ENXIO);
+
+    dmab->dev.type = type;
+    dmab->dev.dev = device;
+    dmab->bytes = 0;
+    switch (type) {
+    case SNDRV_DMA_TYPE_CONTINUOUS:
+        dmab->area = snd_malloc_pages(size, (unsigned long)device);
+        dmab->addr = 0;
+        break;
+#ifdef CONFIG_SBUS
+    case SNDRV_DMA_TYPE_SBUS:
+        dmab->area = snd_malloc_sbus_pages(device, size, &dmab->addr);
+		break;
+#endif
+    case SNDRV_DMA_TYPE_DEV:
+        dmab->area = snd_malloc_dev_pages(device, size, &dmab->addr);
+        break;
+    case SNDRV_DMA_TYPE_DEV_SG:
+        snd_malloc_sgbuf_pages(device, size, dmab, NULL);
+        break;
+    default:
+        printk(KERN_ERR "snd-malloc: invalid device type %d\n", type);
+        dmab->area = NULL;
+        dmab->addr = 0;
+        return -ENXIO;
+    }
+    if (! dmab->area)
+        return -ENOMEM;
+    dmab->bytes = size;
+    return 0;
+}
+
+/**
+ * snd_dma_alloc_pages_fallback - allocate the buffer area according to the given type with fallback
+ * @type: the DMA buffer type
+ * @device: the device pointer
+ * @size: the buffer size to allocate
+ * @dmab: buffer allocation record to store the allocated data
+ *
+ * Calls the memory-allocator function for the corresponding
+ * buffer type.  When no space is left, this function reduces the size and
+ * tries to allocate again.  The size actually allocated is stored in
+ * res_size argument.
+ * 
+ * Returns zero if the buffer with the given size is allocated successfuly,
+ * other a negative value at error.
+ */
+int snd_dma_alloc_pages_fallback(int type, struct device *device, size_t size,
+				 struct snd_dma_buffer *dmab)
+{
+	int err;
+
+	snd_assert(size > 0, return -ENXIO);
+	snd_assert(dmab != NULL, return -ENXIO);
+
+	while ((err = snd_dma_alloc_pages(type, device, size, dmab)) < 0) {
+		if (err != -ENOMEM)
+			return err;
+		size >>= 1;
+		if (size <= PAGE_SIZE)
+			return -ENOMEM;
+	}
+	if (! dmab->area)
+		return -ENOMEM;
+	return 0;
+}
+
+/**
+ * snd_dma_free_pages - release the allocated buffer
+ * @dmab: the buffer allocation record to release
+ *
+ * Releases the allocated buffer via snd_dma_alloc_pages().
+ */
+void snd_dma_free_pages(struct snd_dma_buffer *dmab)
+{
+	switch (dmab->dev.type) {
+	case SNDRV_DMA_TYPE_CONTINUOUS:
+		snd_free_pages(dmab->area, dmab->bytes);
+		break;
+#ifdef CONFIG_SBUS
+	case SNDRV_DMA_TYPE_SBUS:
+		snd_free_sbus_pages(dmab->dev.dev, dmab->bytes, dmab->area, dmab->addr);
+		break;
+#endif
+	case SNDRV_DMA_TYPE_DEV:
+		snd_free_dev_pages(dmab->dev.dev, dmab->bytes, dmab->area, dmab->addr);
+		break;
+	case SNDRV_DMA_TYPE_DEV_SG:
+		snd_free_sgbuf_pages(dmab);
+		break;
+	default:
+		printk(KERN_ERR "snd-malloc: invalid device type %d\n", dmab->dev.type);
+	}
+}
+
+/**
+ * snd_dma_get_reserved - get the reserved buffer for the given device
+ * @dmab: the buffer allocation record to store
+ * @id: the buffer id
+ *
+ * Looks for the reserved-buffer list and re-uses if the same buffer
+ * is found in the list.  When the buffer is found, it's removed from the free list.
+ *
+ * Returns the size of buffer if the buffer is found, or zero if not found.
+ */
+size_t snd_dma_get_reserved_buf(struct snd_dma_buffer *dmab, unsigned int id)
+{
+	struct list_head *p;
+	struct snd_mem_list *mem;
+
+	snd_assert(dmab, return 0);
+
+	down(&list_mutex);
+	list_for_each(p, &mem_list_head) {
+		mem = list_entry(p, struct snd_mem_list, list);
+		if (mem->id == id &&
+		    ! memcmp(&mem->buffer.dev, &dmab->dev, sizeof(dmab->dev))) {
+			list_del(p);
+			*dmab = mem->buffer;
+			kfree(mem);
+			up(&list_mutex);
+			return dmab->bytes;
+		}
+	}
+	up(&list_mutex);
+	return 0;
+}
+
+/**
+ * snd_dma_reserve_buf - reserve the buffer
+ * @dmab: the buffer to reserve
+ * @id: the buffer id
+ *
+ * Reserves the given buffer as a reserved buffer.
+ * 
+ * Returns zero if successful, or a negative code at error.
+ */
+int snd_dma_reserve_buf(struct snd_dma_buffer *dmab, unsigned int id)
+{
+	struct snd_mem_list *mem;
+
+	snd_assert(dmab, return -EINVAL);
+	mem = kmalloc(sizeof(*mem), GFP_KERNEL);
+	if (! mem)
+		return -ENOMEM;
+	down(&list_mutex);
+	mem->buffer = *dmab;
+	mem->id = id;
+	list_add_tail(&mem->list, &mem_list_head);
+	up(&list_mutex);
+	return 0;
+}
+
+/*
+ * purge all reserved buffers
+ */
+static void free_all_reserved_pages(void)
+{
+	struct list_head *p;
+	struct snd_mem_list *mem;
+
+	down(&list_mutex);
+	while (! list_empty(&mem_list_head)) {
+		p = mem_list_head.next;
+		mem = list_entry(p, struct snd_mem_list, list);
+		list_del(p);
+		snd_dma_free_pages(&mem->buffer);
+		kfree(mem);
+	}
+	up(&list_mutex);
+}
+
+
+/*
+ *
+ *  Generic memory allocators
+ *
+ */
+
+/* Pure 2^n version of get_order */
+static inline int get_order(unsigned int size)
+{
+        int order;
+
+        size = (size-1) >> (PAGE_SHIFT-1);
+        order = -1;
+        do {
+                size >>= 1;
+                order++;
+        } while (size);
+        return order;
+}
+
+static long snd_allocated_pages; /* holding the number of allocated pages */
+
+static inline void inc_snd_pages(int order)
+{
+        snd_allocated_pages += 1 << order;
+}
+
+static inline void dec_snd_pages(int order)
+{
+        snd_allocated_pages -= 1 << order;
+}
+
+
+/**
+ * snd_malloc_pages - allocate pages with the given size
+ * @size: the size to allocate in bytes
+ * @gfp_flags: the allocation conditions, GFP_XXX
+ *
+ * Allocates the physically contiguous pages with the given size.
+ *
+ * Returns the pointer of the buffer, or NULL if no enoguh memory.
+ */
+void *snd_malloc_pages(unsigned long size, unsigned int dma_flags)
+{
+    int pg;
+    void *res;
+
+    snd_assert(size > 0, return NULL);
+    snd_assert(dma_flags != 0, return NULL);
+    pg = get_order(size);
+    if ((res = (void *) __get_free_pages(dma_flags, pg)) != NULL) {
+        inc_snd_pages(pg);
+    }
+    return res;
+}
+
+/**
+ * snd_free_pages - release the pages
+ * @ptr: the buffer pointer to release
+ * @size: the allocated buffer size
+ *
+ * Releases the buffer allocated via snd_malloc_pages().
+ */
+void snd_free_pages(void *ptr, unsigned long size)
+{
+    int pg;
+    struct page *page, *last_page;
+
+    if (ptr == NULL)
+        return;
+    pg = get_order(size);
+    dec_snd_pages(pg);
+    free_pages((unsigned long) ptr, pg);
+}
+
+#if defined(CONFIG_ISA) && ! defined(CONFIG_PCI)
+
+/**
+ * snd_malloc_isa_pages - allocate pages for ISA bus with the given size
+ * @size: the size to allocate in bytes
+ * @dma_addr: the pointer to store the physical address of the buffer
+ *
+ * Allocates the physically contiguous pages with the given size for
+ * ISA bus.
+ *
+ * Returns the pointer of the buffer, or NULL if no enoguh memory.
+ */
+void *snd_malloc_isa_pages(size_t size, dma_addr_t *dma_addr)
+{
+	void *dma_area;
+	dma_area = snd_malloc_pages(size, GFP_ATOMIC|GFP_DMA);
+	*dma_addr = dma_area ? isa_virt_to_bus(dma_area) : 0UL;
+	return dma_area;
+}
+
+/**
+ * snd_malloc_isa_pages_fallback - allocate pages with the given size with fallback for ISA bus
+ * @size: the requested size to allocate in bytes
+ * @dma_addr: the pointer to store the physical address of the buffer
+ * @res_size: the pointer to store the size of buffer actually allocated
+ *
+ * Allocates the physically contiguous pages with the given request
+ * size for PCI bus.  When no space is left, this function reduces the size and
+ * tries to allocate again.  The size actually allocated is stored in
+ * res_size argument.
+ *
+ * Returns the pointer of the buffer, or NULL if no enoguh memory.
+ */
+void *snd_malloc_isa_pages_fallback(size_t size,
+				    dma_addr_t *dma_addr,
+				    size_t *res_size)
+{
+	void *dma_area;
+	dma_area = snd_malloc_pages_fallback(size, GFP_ATOMIC|GFP_DMA, res_size);
+	*dma_addr = dma_area ? isa_virt_to_bus(dma_area) : 0UL;
+	return dma_area;
+}
+
+#endif /* CONFIG_ISA && !CONFIG_PCI */
+
+#ifdef CONFIG_PCI
+
+/**
+ * snd_malloc_pci_pages - allocate pages for PCI bus with the given size
+ * @pci: the pci device pointer
+ * @size: the size to allocate in bytes
+ * @dma_addr: the pointer to store the physical address of the buffer
+ *
+ * Allocates the physically contiguous pages with the given size for
+ * PCI bus.
+ *
+ * Returns the pointer of the buffer, or NULL if no enoguh memory.
+ */
+void *snd_malloc_pci_pages(struct pci_dev *pci,
+			   size_t size,
+			   dma_addr_t *dmaaddr)
+{
+	int pg;
+	void *res;
+#ifdef DEBUG
+        dprintf(("snd_malloc_pci_pages. size = %d",size));
+#endif
+	snd_assert(size > 0, return NULL);
+	snd_assert(dmaaddr != NULL, return NULL);
+	for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++);
+	res = pci_alloc_consistent(pci, PAGE_SIZE * (1 << pg), dmaaddr);
+        if (res != NULL) {
+            inc_snd_pages(pg);
+	}
+	return res;
+}
+
+/**
+ * snd_malloc_pci_pages_fallback - allocate pages with the given size with fallback for PCI bus
+ * @pci: pci device pointer
+ * @size: the requested size to allocate in bytes
+ * @dma_addr: the pointer to store the physical address of the buffer
+ * @res_size: the pointer to store the size of buffer actually allocated
+ *
+ * Allocates the physically contiguous pages with the given request
+ * size for PCI bus.  When no space is left, this function reduces the size and
+ * tries to allocate again.  The size actually allocated is stored in
+ * res_size argument.
+ *
+ * Returns the pointer of the buffer, or NULL if no enoguh memory.
+ */
+void *snd_malloc_pci_pages_fallback(struct pci_dev *pci, size_t size,
+				    dma_addr_t *dmaaddr,
+				    size_t *res_size)
+{
+	void *res;
+
+	snd_assert(res_size != NULL, return NULL);
+	do {
+		if ((res = snd_malloc_pci_pages(pci, size, dmaaddr)) != NULL) {
+			*res_size = size;
+			return res;
+		}
+		size >>= 1;
+	} while (size >= PAGE_SIZE);
+	return NULL;
+}
+
+/**
+ * snd_free_pci_pages - release the pages
+ * @pci: pci device pointer
+ * @size: the allocated buffer size
+ * @ptr: the buffer pointer to release
+ * @dma_addr: the physical address of the buffer
+ *
+ * Releases the buffer allocated via snd_malloc_pci_pages().
+ */
+void snd_free_pci_pages(struct pci_dev *pci, size_t size, void *ptr, dma_addr_t dmaaddr)
+{
+	int pg;
+	mem_map_t *page, *last_page;
+
+	if (ptr == NULL)
+		return;
+        for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++);
+        dec_snd_pages(pg);
+	pci_free_consistent(pci, PAGE_SIZE * (1 << pg), ptr, dmaaddr);
+}
+
+#if defined(__i386__)
+/*
+ * on ix86, we allocate a page with GFP_KERNEL to assure the
+ * allocation.  the code is almost same with kernel/i386/pci-dma.c but
+ * it allocates only a single page and checks the validity of the
+ * page address with the given pci dma mask.
+ */
+
+/**
+ * snd_malloc_pci_page - allocate a page in the valid pci dma mask
+ * @pci: pci device pointer
+ * @addrp: the pointer to store the physical address of the buffer
+ *
+ * Allocates a single page for the given PCI device and returns
+ * the virtual address and stores the physical address on addrp.
+ *
+ * This function cannot be called from interrupt handlers or
+ * within spinlocks.
+ */
+void *snd_malloc_pci_page(struct pci_dev *pci, dma_addr_t *addrp)
+{
+	void *ptr;
+	dma_addr_t addr;
+	unsigned long mask;
+
+	mask = pci ? (unsigned long)pci->consistent_dma_mask : 0x00ffffffUL;
+	ptr = (void *)__get_free_page(GFP_KERNEL);
+	if (ptr) {
+		addr = virt_to_phys(ptr);
+		if (((unsigned long)addr + PAGE_SIZE - 1) & ~mask) {
+			/* try to reallocate with the GFP_DMA */
+			free_page((unsigned long)ptr);
+			/* use GFP_ATOMIC for the DMA zone to avoid stall */
+			ptr = (void *)__get_free_page(GFP_ATOMIC | GFP_DMA);
+			if (ptr) /* ok, the address must be within lower 16MB... */
+				addr = virt_to_phys(ptr);
+			else
+				addr = 0;
+		}
+	} else
+		addr = 0;
+	if (ptr) {
+		memset(ptr, 0, PAGE_SIZE);
+	}
+	*addrp = addr;
+	return ptr;
+}
+#else
+
+/* on other architectures, call snd_malloc_pci_pages() helper function
+ * which uses pci_alloc_consistent().
+ */
+void *snd_malloc_pci_page(struct pci_dev *pci, dma_addr_t *addrp)
+{
+        return snd_malloc_pci_pages(pci, PAGE_SIZE, addrp);
+}
+
+#endif
+
+#endif /* CONFIG_PCI */
+
+#ifdef CONFIG_SBUS
+
+/**
+ * snd_malloc_sbus_pages - allocate pages for SBUS with the given size
+ * @sdev: sbus device pointer
+ * @size: the size to allocate in bytes
+ * @dma_addr: the pointer to store the physical address of the buffer
+ *
+ * Allocates the physically contiguous pages with the given size for
+ * SBUS.
+ *
+ * Returns the pointer of the buffer, or NULL if no enoguh memory.
+ */
+void *snd_malloc_sbus_pages(struct sbus_dev *sdev,
+			    size_t size,
+			    dma_addr_t *dma_addr)
+{
+	int pg;
+	void *res;
+
+	snd_assert(size > 0, return NULL);
+        snd_assert(dma_addr != NULL, return NULL);
+        pg = get_order(size);
+	res = sbus_alloc_consistent(sdev, PAGE_SIZE * (1 << pg), dma_addr);
+        if (res != NULL)
+            inc_snd_pages(pg);
+	return res;
+}
+
+/**
+ * snd_malloc_pci_pages_fallback - allocate pages with the given size with fallback for SBUS
+ * @sdev: sbus device pointer
+ * @size: the requested size to allocate in bytes
+ * @dma_addr: the pointer to store the physical address of the buffer
+ * @res_size: the pointer to store the size of buffer actually allocated
+ *
+ * Allocates the physically contiguous pages with the given request
+ * size for SBUS.  When no space is left, this function reduces the size and
+ * tries to allocate again.  The size actually allocated is stored in
+ * res_size argument.
+ *
+ * Returns the pointer of the buffer, or NULL if no enoguh memory.
+ */
+/**
+ * snd_free_sbus_pages - release the pages
+ * @sdev: sbus device pointer
+ * @size: the allocated buffer size
+ * @ptr: the buffer pointer to release
+ * @dma_addr: the physical address of the buffer
+ *
+ * Releases the buffer allocated via snd_malloc_pci_pages().
+ */
+void snd_free_sbus_pages(struct sbus_dev *sdev,
+			 size_t size,
+			 void *ptr,
+			 dma_addr_t dma_addr)
+{
+	int pg;
+
+	if (ptr == NULL)
+            return;
+        pg = get_order(size);
+        dec_snd_pages(pg);
+	sbus_free_consistent(sdev, PAGE_SIZE * (1 << pg), ptr, dma_addr);
+}
+
+#endif /* CONFIG_SBUS */
+
+/*
+ * allocation of buffers for pre-defined devices
+ */
+
+#ifdef CONFIG_PCI
+/* FIXME: for pci only - other bus? */
+struct prealloc_dev {
+	unsigned short vendor;
+	unsigned short device;
+	unsigned long dma_mask;
+	unsigned int size;
+	unsigned int buffers;
+};
+
+#define HAMMERFALL_BUFFER_SIZE    (16*1024*4*(26+1))
+
+static struct prealloc_dev prealloc_devices[] __initdata = {
+	{
+		/* hammerfall */
+		/*.vendor = */0x10ee,
+		/*.device = */0x3fc4,
+		/*.dma_mask = */0xffffffff,
+		/*.size = */HAMMERFALL_BUFFER_SIZE,
+		/*.buffers = */2
+	},
+	{
+		/* HDSP */
+		/*.vendor = */0x10ee,
+		/*.device = */0x3fc5,
+		/*.dma_mask = */0xffffffff,
+		/*.size = */HAMMERFALL_BUFFER_SIZE,
+		/*.buffers = */2
+	},
+	{0 }, /* terminator */
+};
+
+static void __init preallocate_cards(void)
+{
+	struct pci_dev *pci = NULL;
+	int card;
+
+	card = 0;
+
+	while ((pci = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pci)) != NULL) {
+		struct prealloc_dev *dev;
+		unsigned int i;
+		if (card >= SNDRV_CARDS)
+			break;
+		for (dev = prealloc_devices; dev->vendor; dev++) {
+			if (dev->vendor == pci->vendor && dev->device == pci->device)
+				break;
+		}
+		if (! dev->vendor)
+			continue;
+		if (! enable[card++]) {
+			printk(KERN_DEBUG "snd-page-alloc: skipping card %d, device %04x:%04x\n", card, pci->vendor, pci->device);
+			continue;
+		}
+			
+		pci_set_dma_mask(pci, dev->dma_mask);
+		for (i = 0; i < dev->buffers; i++) {
+			struct snd_dma_buffer dmab;
+			memset(&dmab, 0, sizeof(dmab));
+			if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
+						dev->size, &dmab) < 0)
+				printk(KERN_WARNING "snd-page-alloc: cannot allocate buffer pages (size = %d)\n", dev->size);
+			else
+				snd_dma_reserve_buf(&dmab, snd_dma_pci_buf_id(pci));
+		}
+	}
+}
+#else
+#define preallocate_cards()	/* NOP */
+#endif
+
+#ifdef CONFIG_PROC_FS
+/*
+ * proc file interface
+ */
+static int snd_mem_proc_read(char *page, char **start, off_t off,
+			     int count, int *eof, void *data)
+{
+	int len = 0;
+	long pages = snd_allocated_pages >> (PAGE_SHIFT-12);
+	struct list_head *p;
+	struct snd_mem_list *mem;
+	int devno;
+	static char *types[] = { "UNKNOWN", "CONT", "DEV", "DEV-SG", "SBUS" };
+#if 0
+	down(&list_mutex);
+	len += snprintf(page + len, count - len,
+			"pages  : %li bytes (%li pages per %likB)\n",
+			pages * PAGE_SIZE, pages, PAGE_SIZE / 1024);
+	devno = 0;
+	list_for_each(p, &mem_list_head) {
+		mem = list_entry(p, struct snd_mem_list, list);
+                devno++;
+
+		len += snprintf(page + len, count - len,
+				"buffer %d : ID %08x : type %s\n",
+				devno, mem->id, types[mem->buffer.dev.type]);
+		len += snprintf(page + len, count - len,
+				"  addr = 0x%lx, size = %d bytes\n",
+                                (unsigned long)mem->buffer.addr, (int)mem->buffer.bytes);
+
+	}
+        up(&list_mutex);
+#endif
+	return len;
+}
+#endif /* CONFIG_PROC_FS */
+
+/*
+ * module entry
+ */
+
+static int __init snd_mem_init(void)
+{
+#ifdef CONFIG_PROC_FS
+	create_proc_read_entry("driver/snd-page-alloc", 0, NULL, snd_mem_proc_read, NULL);
+#endif
+	preallocate_cards();
+	return 0;
+}
+
+static void __exit snd_mem_exit(void)
+{
+	remove_proc_entry("driver/snd-page-alloc", NULL);
+	free_all_reserved_pages();
+	if (snd_allocated_pages > 0)
+		printk(KERN_ERR "snd-malloc: Memory leak?  pages not freed = %li\n", snd_allocated_pages);
+}
+
+module_init(snd_mem_init)
+module_exit(snd_mem_exit)
+
+
+#ifndef MODULE
+
+/* format is: snd-page-alloc=enable */
+
+static int __init snd_mem_setup(char *str)
+{
+	static unsigned __initdata nr_dev = 0;
+
+	if (nr_dev >= SNDRV_CARDS)
+		return 0;
+	(void)(get_option(&str,&enable[nr_dev]) == 2);
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-page-alloc=", snd_mem_setup);
+
+#endif
+
+/*
+ * exports
+ */
+EXPORT_SYMBOL(snd_dma_alloc_pages);
+EXPORT_SYMBOL(snd_dma_free_pages);
+EXPORT_SYMBOL(snd_dma_get_reserved);
+EXPORT_SYMBOL(snd_dma_free_reserved);
+EXPORT_SYMBOL(snd_dma_set_reserved);
+
+EXPORT_SYMBOL(snd_malloc_pages);
+EXPORT_SYMBOL(snd_malloc_pages_fallback);
+EXPORT_SYMBOL(snd_free_pages);
+#if defined(CONFIG_ISA) && ! defined(CONFIG_PCI)
+EXPORT_SYMBOL(snd_malloc_isa_pages);
+EXPORT_SYMBOL(snd_malloc_isa_pages_fallback);
+#endif
+#ifdef CONFIG_PCI
+EXPORT_SYMBOL(snd_malloc_pci_pages);
+EXPORT_SYMBOL(snd_malloc_pci_pages_fallback);
+EXPORT_SYMBOL(snd_malloc_pci_page);
+EXPORT_SYMBOL(snd_free_pci_pages);
+EXPORT_SYMBOL(snd_malloc_sgbuf_pages);
+EXPORT_SYMBOL(snd_free_sgbuf_pages);
+#endif
+#ifdef CONFIG_SBUS
+EXPORT_SYMBOL(snd_malloc_sbus_pages);
+EXPORT_SYMBOL(snd_malloc_sbus_pages_fallback);
+EXPORT_SYMBOL(snd_free_sbus_pages);
+#endif
+
Index: /GPL/branches/alsa-resync1/alsa-kernel/core/memalloc.c.alsa
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/core/memalloc.c.alsa	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/core/memalloc.c.alsa	(revision 91)
@@ -0,0 +1,1011 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *                   Takashi Iwai <tiwai@suse.de>
+ * 
+ *  Generic memory allocators
+ *
+ *
+ *   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 <linux/config.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/proc_fs.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <asm/semaphore.h>
+#include <sound/memalloc.h>
+
+
+MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>, Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Memory allocator for ALSA system.");
+MODULE_LICENSE("GPL");
+
+
+#ifndef SNDRV_CARDS
+#define SNDRV_CARDS	8
+#endif
+static int enable[8] = {[0 ... (SNDRV_CARDS-1)] = 1};
+MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(enable, "Enable cards to allocate buffers.");
+
+
+/*
+ */
+
+static DECLARE_MUTEX(list_mutex);
+static LIST_HEAD(mem_list_head);
+
+/* buffer preservation list */
+struct snd_mem_list {
+	struct snd_dma_device dev;
+	struct snd_dma_buffer buffer;
+	int used;
+	struct list_head list;
+};
+
+/* id for pre-allocated buffers */
+#define SNDRV_DMA_DEVICE_UNUSED (unsigned int)-1
+
+#ifdef CONFIG_SND_DEBUG
+#define __ASTRING__(x) #x
+#define snd_assert(expr, args...) do {\
+	if (!(expr)) {\
+		printk(KERN_ERR "snd-malloc: BUG? (%s) (called from %p)\n", __ASTRING__(expr), __builtin_return_address(0));\
+		args;\
+	}\
+} while (0)
+#else
+#define snd_assert(expr, args...) /**/
+#endif
+
+#ifdef CONFIG_PCI
+#if defined(__i386__) || defined(__ppc__) || defined(__x86_64__)
+#define HACK_PCI_ALLOC_CONSISTENT
+
+/*
+ * A hack to allocate large buffers via pci_alloc_consistent()
+ *
+ * since pci_alloc_consistent always tries GFP_DMA when the requested
+ * pci memory region is below 32bit, it happens quite often that even
+ * 2 order of pages cannot be allocated.
+ *
+ * so in the following, we allocate at first without dma_mask, so that
+ * allocation will be done without GFP_DMA.  if the area doesn't match
+ * with the requested region, then realloate with the original dma_mask
+ * again.
+ */
+
+static void *snd_pci_hack_alloc_consistent(struct pci_dev *hwdev, size_t size,
+				    dma_addr_t *dma_handle)
+{
+	void *ret;
+	u64 dma_mask;
+	unsigned long mask;
+
+	if (hwdev == NULL)
+		return pci_alloc_consistent(hwdev, size, dma_handle);
+	dma_mask = hwdev->consistent_dma_mask;
+	mask = (unsigned long)dma_mask;
+	hwdev->consistent_dma_mask = 0xffffffff; /* do without masking */
+	ret = pci_alloc_consistent(hwdev, size, dma_handle);
+	hwdev->consistent_dma_mask = dma_mask; /* restore */
+	if (ret) {
+		/* obtained address is out of range? */
+		if (((unsigned long)*dma_handle + size - 1) & ~mask) {
+			/* reallocate with the proper mask */
+			pci_free_consistent(hwdev, size, ret, *dma_handle);
+			ret = pci_alloc_consistent(hwdev, size, dma_handle);
+		}
+	} else {
+		/* wish to success now with the proper mask... */
+		if (mask != 0xffffffffUL)
+			ret = pci_alloc_consistent(hwdev, size, dma_handle);
+	}
+	return ret;
+}
+
+/* redefine pci_alloc_consistent for some architectures */
+#undef pci_alloc_consistent
+#define pci_alloc_consistent snd_pci_hack_alloc_consistent
+
+#endif /* arch */
+#endif /* CONFIG_PCI */
+
+
+/*
+ * compare the two devices
+ * returns non-zero if matched.
+ */
+static int compare_device(const struct snd_dma_device *a, const struct snd_dma_device *b, int allow_unused)
+{
+	if (a->type != b->type)
+		return 0;
+	if (a->id != b->id) {
+		if (! allow_unused || (a->id != SNDRV_DMA_DEVICE_UNUSED && b->id != SNDRV_DMA_DEVICE_UNUSED))
+			return 0;
+	}
+	switch (a->type) {
+	case SNDRV_DMA_TYPE_CONTINUOUS:
+#ifdef CONFIG_ISA
+	case SNDRV_DMA_TYPE_ISA:
+#endif
+		return a->dev.flags == b->dev.flags;
+#ifdef CONFIG_PCI
+	case SNDRV_DMA_TYPE_PCI:
+	case SNDRV_DMA_TYPE_PCI_SG:
+		return a->dev.pci == b->dev.pci;
+#endif
+#ifdef CONFIG_SBUS
+	case SNDRV_DMA_TYPE_SBUS:
+		return a->dev.sbus == b->dev.sbus;
+#endif
+	}
+	return 0;
+}
+
+/**
+ * snd_dma_alloc_pages - allocate the buffer area according to the given type
+ * @dev: the buffer device info
+ * @size: the buffer size to allocate
+ * @dmab: buffer allocation record to store the allocated data
+ *
+ * Calls the memory-allocator function for the corresponding
+ * buffer type.
+ * 
+ * Returns zero if the buffer with the given size is allocated successfuly,
+ * other a negative value at error.
+ */
+int snd_dma_alloc_pages(const struct snd_dma_device *dev, size_t size,
+			struct snd_dma_buffer *dmab)
+{
+	snd_assert(dev != NULL, return -ENXIO);
+	snd_assert(size > 0, return -ENXIO);
+	snd_assert(dmab != NULL, return -ENXIO);
+
+	dmab->bytes = 0;
+	switch (dev->type) {
+	case SNDRV_DMA_TYPE_CONTINUOUS:
+		dmab->area = snd_malloc_pages(size, dev->dev.flags);
+		dmab->addr = 0;
+		break;
+#ifdef CONFIG_ISA
+	case SNDRV_DMA_TYPE_ISA:
+		dmab->area = snd_malloc_isa_pages(size, &dmab->addr);
+		break;
+#endif
+#ifdef CONFIG_PCI
+	case SNDRV_DMA_TYPE_PCI:
+		dmab->area = snd_malloc_pci_pages(dev->dev.pci, size, &dmab->addr);
+		break;
+	case SNDRV_DMA_TYPE_PCI_SG:
+		snd_malloc_sgbuf_pages(dev->dev.pci, size, dmab);
+		break;
+#endif
+#ifdef CONFIG_SBUS
+	case SNDRV_DMA_TYPE_SBUS:
+		dmab->area = snd_malloc_sbus_pages(dev->dev.sbus, size, &dmab->addr);
+		break;
+#endif
+	default:
+		printk(KERN_ERR "snd-malloc: invalid device type %d\n", dev->type);
+		dmab->area = NULL;
+		dmab->addr = 0;
+		return -ENXIO;
+	}
+	if (! dmab->area)
+            return -ENOMEM;
+        dmab->bytes = size;
+	return 0;
+}
+
+
+/**
+ * snd_dma_free_pages - release the allocated buffer
+ * @dev: the buffer device info
+ * @dmbab: the buffer allocation record to release
+ *
+ * Releases the allocated buffer via snd_dma_alloc_pages().
+ */
+void snd_dma_free_pages(const struct snd_dma_device *dev, struct snd_dma_buffer *dmab)
+{
+	switch (dev->type) {
+	case SNDRV_DMA_TYPE_CONTINUOUS:
+		snd_free_pages(dmab->area, dmab->bytes);
+		break;
+#ifdef CONFIG_ISA
+	case SNDRV_DMA_TYPE_ISA:
+		snd_free_isa_pages(dmab->bytes, dmab->area, dmab->addr);
+		break;
+#endif
+#ifdef CONFIG_PCI
+	case SNDRV_DMA_TYPE_PCI:
+		snd_free_pci_pages(dev->dev.pci, dmab->bytes, dmab->area, dmab->addr);
+		break;
+	case SNDRV_DMA_TYPE_PCI_SG:
+		snd_free_sgbuf_pages(dmab);
+		break;
+#endif
+#ifdef CONFIG_SBUS
+	case SNDRV_DMA_TYPE_SBUS:
+		snd_free_sbus_pages(dev->dev.sbus, dmab->bytes, dmab->area, dmab->addr);
+		break;
+#endif
+	default:
+		printk(KERN_ERR "snd-malloc: invalid device type %d\n", dev->type);
+	}
+}
+
+
+/*
+ * search for the device
+ */
+static struct snd_mem_list *mem_list_find(const struct snd_dma_device *dev, int search_empty)
+{
+	struct list_head *p;
+	struct snd_mem_list *mem;
+
+	list_for_each(p, &mem_list_head) {
+		mem = list_entry(p, struct snd_mem_list, list);
+		if (mem->used && search_empty)
+			continue;
+		if (compare_device(&mem->dev, dev, search_empty))
+			return mem;
+	}
+	return NULL;
+}
+
+/**
+ * snd_dma_get_reserved - get the reserved buffer for the given device
+ * @dev: the buffer device info
+ * @dmab: the buffer allocation record to store
+ *
+ * Looks for the reserved-buffer list and re-uses if the same buffer
+ * is found in the list.  When the buffer is found, it's marked as used.
+ * For unmarking the buffer, call snd_dma_free_reserved().
+ *
+ * Returns the size of buffer if the buffer is found, or zero if not found.
+ */
+size_t snd_dma_get_reserved(const struct snd_dma_device *dev, struct snd_dma_buffer *dmab)
+{
+	struct snd_mem_list *mem;
+
+	snd_assert(dev && dmab, return 0);
+
+	down(&list_mutex);
+	mem = mem_list_find(dev, 1);
+	if (mem) {
+		mem->used = 1;
+		mem->dev = *dev;
+		*dmab = mem->buffer;
+		up(&list_mutex);
+		return dmab->bytes;
+	}
+	up(&list_mutex);
+	return 0;
+}
+
+/**
+ * snd_dma_free_reserved - unmark the reserved buffer
+ * @dev: the buffer device info
+ *
+ * Looks for the matching reserved buffer and erases the mark on it
+ * if found.
+ *
+ * Returns zero.
+ */
+int snd_dma_free_reserved(const struct snd_dma_device *dev)
+{
+	struct snd_mem_list *mem;
+
+	snd_assert(dev, return -EINVAL);
+	down(&list_mutex);
+	mem = mem_list_find(dev, 0);
+	if (mem)
+		mem->used = 0;
+	up(&list_mutex);
+	return 0;
+}
+
+/**
+ * snd_dma_set_reserved - reserve the buffer
+ * @dev: the buffer device info
+ * @dmab: the buffer to reserve
+ *
+ * Reserves the given buffer as a reserved buffer.
+ * When an old reserved buffer already exists, the old one is released
+ * and replaced with the new one.
+ *
+ * When NULL buffer pointer or zero buffer size is given, the existing
+ * buffer is released and the entry is removed.
+ * 
+ * Returns zero if successful, or a negative code at error.
+ */
+int snd_dma_set_reserved(const struct snd_dma_device *dev, struct snd_dma_buffer *dmab)
+{
+	struct snd_mem_list *mem;
+
+	snd_assert(dev, return -EINVAL);
+	down(&list_mutex);
+	mem = mem_list_find(dev, 0);
+        if (mem) {
+            if (mem->used)
+                printk(KERN_WARNING "snd-page-alloc: releasing the used block (type=%d, id=0x%x\n", mem->dev.type, mem->dev.id);
+		snd_dma_free_pages(dev, &mem->buffer);
+		if (! dmab || ! dmab->bytes) {
+			/* remove the entry */
+			list_del(&mem->list);
+			kfree(mem);
+			up(&list_mutex);
+			return 0;
+		}
+	} else {
+		if (! dmab || ! dmab->bytes) {
+			up(&list_mutex);
+			return 0;
+		}
+		mem = kmalloc(sizeof(*mem), GFP_KERNEL);
+		if (! mem) {
+			up(&list_mutex);
+			return -ENOMEM;
+		}
+		mem->dev = *dev;
+		list_add_tail(&mem->list, &mem_list_head);
+	}
+	/* store the entry */
+	mem->used = 1;
+	mem->buffer = *dmab;
+	up(&list_mutex);
+	return 0;
+}
+
+/*
+ * purge all reserved buffers
+ */
+static void free_all_reserved_pages(void)
+{
+	struct list_head *p;
+	struct snd_mem_list *mem;
+
+	down(&list_mutex);
+	while (! list_empty(&mem_list_head)) {
+		p = mem_list_head.next;
+		mem = list_entry(p, struct snd_mem_list, list);
+		list_del(p);
+		snd_dma_free_pages(&mem->dev, &mem->buffer);
+		kfree(mem);
+	}
+	up(&list_mutex);
+}
+
+
+/*
+ *
+ *  Generic memory allocators
+ *
+ */
+
+static long snd_allocated_pages; /* holding the number of allocated pages */
+
+static void mark_pages(void *res, int order)
+{
+	struct page *page = virt_to_page(res);
+	struct page *last_page = page + (1 << order);
+	while (page < last_page)
+		SetPageReserved(page++);
+	snd_allocated_pages += 1 << order;
+}
+
+static void unmark_pages(void *res, int order)
+{
+	struct page *page = virt_to_page(res);
+	struct page *last_page = page + (1 << order);
+	while (page < last_page)
+		ClearPageReserved(page++);
+	snd_allocated_pages -= 1 << order;
+}
+
+/**
+ * snd_malloc_pages - allocate pages with the given size
+ * @size: the size to allocate in bytes
+ * @gfp_flags: the allocation conditions, GFP_XXX
+ *
+ * Allocates the physically contiguous pages with the given size.
+ *
+ * Returns the pointer of the buffer, or NULL if no enoguh memory.
+ */
+void *snd_malloc_pages(size_t size, unsigned int gfp_flags)
+{
+	int pg;
+	void *res;
+
+	snd_assert(size > 0, return NULL);
+	snd_assert(gfp_flags != 0, return NULL);
+	for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++);
+	if ((res = (void *) __get_free_pages(gfp_flags, pg)) != NULL) {
+		mark_pages(res, pg);
+	}
+	return res;
+}
+
+/**
+ * snd_malloc_pages_fallback - allocate pages with the given size with fallback
+ * @size: the requested size to allocate in bytes
+ * @gfp_flags: the allocation conditions, GFP_XXX
+ * @res_size: the pointer to store the size of buffer actually allocated
+ *
+ * Allocates the physically contiguous pages with the given request
+ * size.  When no space is left, this function reduces the size and
+ * tries to allocate again.  The size actually allocated is stored in
+ * res_size argument.
+ *
+ * Returns the pointer of the buffer, or NULL if no enoguh memory.
+ */
+void *snd_malloc_pages_fallback(size_t size, unsigned int gfp_flags, size_t *res_size)
+{
+	void *res;
+
+	snd_assert(size > 0, return NULL);
+	snd_assert(res_size != NULL, return NULL);
+	do {
+		if ((res = snd_malloc_pages(size, gfp_flags)) != NULL) {
+			*res_size = size;
+			return res;
+		}
+		size >>= 1;
+	} while (size >= PAGE_SIZE);
+	return NULL;
+}
+
+/**
+ * snd_free_pages - release the pages
+ * @ptr: the buffer pointer to release
+ * @size: the allocated buffer size
+ *
+ * Releases the buffer allocated via snd_malloc_pages().
+ */
+void snd_free_pages(void *ptr, size_t size)
+{
+	int pg;
+
+	if (ptr == NULL)
+		return;
+	for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++);
+	unmark_pages(ptr, pg);
+	free_pages((unsigned long) ptr, pg);
+}
+
+#if defined(CONFIG_ISA) && ! defined(CONFIG_PCI)
+
+/**
+ * snd_malloc_isa_pages - allocate pages for ISA bus with the given size
+ * @size: the size to allocate in bytes
+ * @dma_addr: the pointer to store the physical address of the buffer
+ *
+ * Allocates the physically contiguous pages with the given size for
+ * ISA bus.
+ *
+ * Returns the pointer of the buffer, or NULL if no enoguh memory.
+ */
+void *snd_malloc_isa_pages(size_t size, dma_addr_t *dma_addr)
+{
+	void *dma_area;
+	dma_area = snd_malloc_pages(size, GFP_ATOMIC|GFP_DMA);
+	*dma_addr = dma_area ? isa_virt_to_bus(dma_area) : 0UL;
+	return dma_area;
+}
+
+/**
+ * snd_malloc_isa_pages_fallback - allocate pages with the given size with fallback for ISA bus
+ * @size: the requested size to allocate in bytes
+ * @dma_addr: the pointer to store the physical address of the buffer
+ * @res_size: the pointer to store the size of buffer actually allocated
+ *
+ * Allocates the physically contiguous pages with the given request
+ * size for PCI bus.  When no space is left, this function reduces the size and
+ * tries to allocate again.  The size actually allocated is stored in
+ * res_size argument.
+ *
+ * Returns the pointer of the buffer, or NULL if no enoguh memory.
+ */
+void *snd_malloc_isa_pages_fallback(size_t size,
+				    dma_addr_t *dma_addr,
+				    size_t *res_size)
+{
+	void *dma_area;
+	dma_area = snd_malloc_pages_fallback(size, GFP_ATOMIC|GFP_DMA, res_size);
+	*dma_addr = dma_area ? isa_virt_to_bus(dma_area) : 0UL;
+	return dma_area;
+}
+
+#endif /* CONFIG_ISA && !CONFIG_PCI */
+
+#ifdef CONFIG_PCI
+
+/**
+ * snd_malloc_pci_pages - allocate pages for PCI bus with the given size
+ * @pci: the pci device pointer
+ * @size: the size to allocate in bytes
+ * @dma_addr: the pointer to store the physical address of the buffer
+ *
+ * Allocates the physically contiguous pages with the given size for
+ * PCI bus.
+ *
+ * Returns the pointer of the buffer, or NULL if no enoguh memory.
+ */
+void *snd_malloc_pci_pages(struct pci_dev *pci,
+			   size_t size,
+			   dma_addr_t *dma_addr)
+{
+	int pg;
+	void *res;
+
+	snd_assert(size > 0, return NULL);
+	snd_assert(dma_addr != NULL, return NULL);
+	for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++);
+	res = pci_alloc_consistent(pci, PAGE_SIZE * (1 << pg), dma_addr);
+	if (res != NULL) {
+		mark_pages(res, pg);
+	}
+	return res;
+}
+
+/**
+ * snd_malloc_pci_pages_fallback - allocate pages with the given size with fallback for PCI bus
+ * @pci: pci device pointer
+ * @size: the requested size to allocate in bytes
+ * @dma_addr: the pointer to store the physical address of the buffer
+ * @res_size: the pointer to store the size of buffer actually allocated
+ *
+ * Allocates the physically contiguous pages with the given request
+ * size for PCI bus.  When no space is left, this function reduces the size and
+ * tries to allocate again.  The size actually allocated is stored in
+ * res_size argument.
+ *
+ * Returns the pointer of the buffer, or NULL if no enoguh memory.
+ */
+void *snd_malloc_pci_pages_fallback(struct pci_dev *pci,
+				    size_t size,
+				    dma_addr_t *dma_addr,
+				    size_t *res_size)
+{
+	void *res;
+
+	snd_assert(res_size != NULL, return NULL);
+	do {
+		if ((res = snd_malloc_pci_pages(pci, size, dma_addr)) != NULL) {
+			*res_size = size;
+			return res;
+		}
+		size >>= 1;
+	} while (size >= PAGE_SIZE);
+	return NULL;
+}
+
+/**
+ * snd_free_pci_pages - release the pages
+ * @pci: pci device pointer
+ * @size: the allocated buffer size
+ * @ptr: the buffer pointer to release
+ * @dma_addr: the physical address of the buffer
+ *
+ * Releases the buffer allocated via snd_malloc_pci_pages().
+ */
+void snd_free_pci_pages(struct pci_dev *pci,
+			size_t size,
+			void *ptr,
+			dma_addr_t dma_addr)
+{
+	int pg;
+
+	if (ptr == NULL)
+		return;
+	for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++);
+	unmark_pages(ptr, pg);
+	pci_free_consistent(pci, PAGE_SIZE * (1 << pg), ptr, dma_addr);
+}
+
+
+#if defined(__i386__)
+/*
+ * on ix86, we allocate a page with GFP_KERNEL to assure the
+ * allocation.  the code is almost same with kernel/i386/pci-dma.c but
+ * it allocates only a single page and checks the validity of the
+ * page address with the given pci dma mask.
+ */
+
+/**
+ * snd_malloc_pci_page - allocate a page in the valid pci dma mask
+ * @pci: pci device pointer
+ * @addrp: the pointer to store the physical address of the buffer
+ *
+ * Allocates a single page for the given PCI device and returns
+ * the virtual address and stores the physical address on addrp.
+ * 
+ * This function cannot be called from interrupt handlers or
+ * within spinlocks.
+ */
+void *snd_malloc_pci_page(struct pci_dev *pci, dma_addr_t *addrp)
+{
+	void *ptr;
+	dma_addr_t addr;
+	unsigned long mask;
+
+	mask = pci ? (unsigned long)pci->consistent_dma_mask : 0x00ffffffUL;
+	ptr = (void *)__get_free_page(GFP_KERNEL);
+	if (ptr) {
+		addr = virt_to_phys(ptr);
+		if (((unsigned long)addr + PAGE_SIZE - 1) & ~mask) {
+			/* try to reallocate with the GFP_DMA */
+			free_page((unsigned long)ptr);
+			/* use GFP_ATOMIC for the DMA zone to avoid stall */
+			ptr = (void *)__get_free_page(GFP_ATOMIC | GFP_DMA);
+			if (ptr) /* ok, the address must be within lower 16MB... */
+				addr = virt_to_phys(ptr);
+			else
+				addr = 0;
+		}
+	} else
+		addr = 0;
+	if (ptr) {
+		memset(ptr, 0, PAGE_SIZE);
+		mark_pages(ptr, 0);
+	}
+	*addrp = addr;
+	return ptr;
+}
+#else
+
+/* on other architectures, call snd_malloc_pci_pages() helper function
+ * which uses pci_alloc_consistent().
+ */
+void *snd_malloc_pci_page(struct pci_dev *pci, dma_addr_t *addrp)
+{
+	return snd_malloc_pci_pages(pci, PAGE_SIZE, addrp);
+}
+
+#endif
+
+#if 0 /* for kernel-doc */
+/**
+ * snd_free_pci_page - release a page
+ * @pci: pci device pointer
+ * @ptr: the buffer pointer to release
+ * @dma_addr: the physical address of the buffer
+ *
+ * Releases the buffer allocated via snd_malloc_pci_page().
+ */
+void snd_free_pci_page(struct pci_dev *pci, void *ptr, dma_addr_t dma_addr);
+#endif /* for kernel-doc */
+
+#endif /* CONFIG_PCI */
+
+#ifdef CONFIG_SBUS
+
+/**
+ * snd_malloc_sbus_pages - allocate pages for SBUS with the given size
+ * @sdev: sbus device pointer
+ * @size: the size to allocate in bytes
+ * @dma_addr: the pointer to store the physical address of the buffer
+ *
+ * Allocates the physically contiguous pages with the given size for
+ * SBUS.
+ *
+ * Returns the pointer of the buffer, or NULL if no enoguh memory.
+ */
+void *snd_malloc_sbus_pages(struct sbus_dev *sdev,
+			    size_t size,
+			    dma_addr_t *dma_addr)
+{
+	int pg;
+	void *res;
+
+	snd_assert(size > 0, return NULL);
+	snd_assert(dma_addr != NULL, return NULL);
+	for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++);
+	res = sbus_alloc_consistent(sdev, PAGE_SIZE * (1 << pg), dma_addr);
+	if (res != NULL) {
+		mark_pages(res, pg);
+	}
+	return res;
+}
+
+/**
+ * snd_malloc_pci_pages_fallback - allocate pages with the given size with fallback for SBUS
+ * @sdev: sbus device pointer
+ * @size: the requested size to allocate in bytes
+ * @dma_addr: the pointer to store the physical address of the buffer
+ * @res_size: the pointer to store the size of buffer actually allocated
+ *
+ * Allocates the physically contiguous pages with the given request
+ * size for SBUS.  When no space is left, this function reduces the size and
+ * tries to allocate again.  The size actually allocated is stored in
+ * res_size argument.
+ *
+ * Returns the pointer of the buffer, or NULL if no enoguh memory.
+ */
+void *snd_malloc_sbus_pages_fallback(struct sbus_dev *sdev,
+				     size_t size,
+				     dma_addr_t *dma_addr,
+				     size_t *res_size)
+{
+	void *res;
+
+	snd_assert(res_size != NULL, return NULL);
+	do {
+		if ((res = snd_malloc_sbus_pages(sdev, size, dma_addr)) != NULL) {
+			*res_size = size;
+			return res;
+		}
+		size >>= 1;
+	} while (size >= PAGE_SIZE);
+	return NULL;
+}
+
+/**
+ * snd_free_sbus_pages - release the pages
+ * @sdev: sbus device pointer
+ * @size: the allocated buffer size
+ * @ptr: the buffer pointer to release
+ * @dma_addr: the physical address of the buffer
+ *
+ * Releases the buffer allocated via snd_malloc_pci_pages().
+ */
+void snd_free_sbus_pages(struct sbus_dev *sdev,
+			 size_t size,
+			 void *ptr,
+			 dma_addr_t dma_addr)
+{
+	int pg;
+
+	if (ptr == NULL)
+		return;
+	for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++);
+	unmark_pages(ptr, pg);
+	sbus_free_consistent(sdev, PAGE_SIZE * (1 << pg), ptr, dma_addr);
+}
+
+#endif /* CONFIG_SBUS */
+
+
+/*
+ * allocation of buffers for pre-defined devices
+ */
+
+#ifdef CONFIG_PCI
+/* FIXME: for pci only - other bus? */
+struct prealloc_dev {
+	unsigned short vendor;
+	unsigned short device;
+	unsigned long dma_mask;
+	unsigned int size;
+	unsigned int buffers;
+};
+
+#define HAMMERFALL_BUFFER_SIZE    (16*1024*4*(26+1))
+
+static struct prealloc_dev prealloc_devices[] __initdata = {
+	{
+		/* hammerfall */
+		.vendor = 0x10ee,
+		.device = 0x3fc4,
+		.dma_mask = 0xffffffff,
+		.size = HAMMERFALL_BUFFER_SIZE,
+		.buffers = 2
+	},
+	{
+		/* HDSP */
+		.vendor = 0x10ee,
+		.device = 0x3fc5,
+		.dma_mask = 0xffffffff,
+		.size = HAMMERFALL_BUFFER_SIZE,
+		.buffers = 2
+	},
+	{ }, /* terminator */
+};
+
+static void __init preallocate_cards(void)
+{
+	struct pci_dev *pci = NULL;
+	int card;
+
+	card = 0;
+
+	while ((pci = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pci)) != NULL) {
+		struct prealloc_dev *dev;
+		unsigned int i;
+		if (card >= SNDRV_CARDS)
+			break;
+		for (dev = prealloc_devices; dev->vendor; dev++) {
+			if (dev->vendor == pci->vendor && dev->device == pci->device)
+				break;
+		}
+		if (! dev->vendor)
+				continue;
+		if (! enable[card++]) {
+			printk(KERN_DEBUG "snd-page-alloc: skipping card %d, device %04x:%04x\n", card, pci->vendor, pci->device);
+				continue;
+		}
+			
+			if (pci_set_consistent_dma_mask(pci, dev->dma_mask) < 0) {
+				printk(KERN_ERR "snd-page-alloc: cannot set DMA mask %lx for pci %04x:%04x\n", dev->dma_mask, dev->vendor, dev->device);
+				continue;
+			}
+                for (i = 0; i < dev->buffers; i++) {
+                    struct snd_mem_list *mem;
+                    mem = kmalloc(sizeof(*mem), GFP_KERNEL);
+                    if (! mem) {
+                        printk(KERN_WARNING "snd-page-alloc: can't malloc memlist\n");
+                        break;
+                    }
+                    memset(mem, 0, sizeof(*mem));
+                    snd_dma_device_pci(&mem->dev, pci, SNDRV_DMA_DEVICE_UNUSED);
+                    if (snd_dma_alloc_pages(&mem->dev, dev->size, &mem->buffer) < 0) {
+                        printk(KERN_WARNING "snd-page-alloc: cannot allocate buffer pages (size = %d)\n", dev->size);
+                        kfree(mem);
+                    } else {
+                        down(&list_mutex);
+                        list_add_tail(&mem->list, &mem_list_head);
+                        up(&list_mutex);
+                    }
+                }
+        }
+}
+#else
+#define preallocate_cards()	/* NOP */
+#endif
+
+
+#ifdef CONFIG_PROC_FS
+/*
+ * proc file interface
+ */
+static int snd_mem_proc_read(char *page, char **start, off_t off,
+			     int count, int *eof, void *data)
+{
+	int len = 0;
+	long pages = snd_allocated_pages >> (PAGE_SHIFT-12);
+	struct list_head *p;
+	struct snd_mem_list *mem;
+	int devno;
+
+	down(&list_mutex);
+	len += sprintf(page + len, "pages  : %li bytes (%li pages per %likB)\n",
+		       pages * PAGE_SIZE, pages, PAGE_SIZE / 1024);
+	devno = 0;
+	list_for_each(p, &mem_list_head) {
+		mem = list_entry(p, struct snd_mem_list, list);
+		devno++;
+		len += sprintf(page + len, "buffer %d : ", devno);
+		if (mem->dev.id == SNDRV_DMA_DEVICE_UNUSED)
+			len += sprintf(page + len, "UNUSED");
+		else
+			len += sprintf(page + len, "ID %08x", mem->dev.id);
+		len += sprintf(page + len, " : type ");
+		switch (mem->dev.type) {
+		case SNDRV_DMA_TYPE_CONTINUOUS:
+			len += sprintf(page + len, "CONT [%x]", mem->dev.dev.flags);
+			break;
+#ifdef CONFIG_PCI
+		case SNDRV_DMA_TYPE_PCI:
+		case SNDRV_DMA_TYPE_PCI_SG:
+			if (mem->dev.dev.pci) {
+				len += sprintf(page + len, "%s [%04x:%04x]",
+					       mem->dev.type == SNDRV_DMA_TYPE_PCI ? "PCI" : "PCI-SG",
+					       mem->dev.dev.pci->vendor,
+					       mem->dev.dev.pci->device);
+			}
+			break;
+#endif
+#ifdef CONFIG_ISA
+		case SNDRV_DMA_TYPE_ISA:
+			len += sprintf(page + len, "ISA [%x]", mem->dev.dev.flags);
+			break;
+#endif
+#ifdef CONFIG_SBUS
+		case SNDRV_DMA_TYPE_SBUS:
+			len += sprintf(page + len, "SBUS [%x]", mem->dev.dev.sbus->slot);
+			break;
+#endif
+		default:
+			len += sprintf(page + len, "UNKNOWN");
+			break;
+		}
+		len += sprintf(page + len, "\n  addr = 0x%lx, size = %d bytes, used = %s\n",
+			       (unsigned long)mem->buffer.addr, (int)mem->buffer.bytes,
+			       mem->used ? "yes" : "no");
+	}
+	up(&list_mutex);
+	return len;
+}
+#endif /* CONFIG_PROC_FS */
+
+/*
+ * module entry
+ */
+
+static int __init snd_mem_init(void)
+{
+#ifdef CONFIG_PROC_FS
+	create_proc_read_entry("driver/snd-page-alloc", 0, 0, snd_mem_proc_read, NULL);
+#endif
+	preallocate_cards();
+	return 0;
+}
+
+static void __exit snd_mem_exit(void)
+{
+	remove_proc_entry("driver/snd-page-alloc", NULL);
+	free_all_reserved_pages();
+	if (snd_allocated_pages > 0)
+		printk(KERN_ERR "snd-malloc: Memory leak?  pages not freed = %li\n", snd_allocated_pages);
+}
+
+
+module_init(snd_mem_init)
+module_exit(snd_mem_exit)
+
+
+#ifndef MODULE
+
+/* format is: snd-page-alloc=enable */
+
+static int __init snd_mem_setup(char *str)
+{
+	static unsigned __initdata nr_dev = 0;
+
+	if (nr_dev >= SNDRV_CARDS)
+		return 0;
+	(void)(get_option(&str,&enable[nr_dev]) == 2);
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-page-alloc=", snd_mem_setup);
+
+#endif
+
+/*
+ * exports
+ */
+EXPORT_SYMBOL(snd_dma_alloc_pages);
+EXPORT_SYMBOL(snd_dma_free_pages);
+EXPORT_SYMBOL(snd_dma_get_reserved);
+EXPORT_SYMBOL(snd_dma_free_reserved);
+EXPORT_SYMBOL(snd_dma_set_reserved);
+
+EXPORT_SYMBOL(snd_malloc_pages);
+EXPORT_SYMBOL(snd_malloc_pages_fallback);
+EXPORT_SYMBOL(snd_free_pages);
+#if defined(CONFIG_ISA) && ! defined(CONFIG_PCI)
+EXPORT_SYMBOL(snd_malloc_isa_pages);
+EXPORT_SYMBOL(snd_malloc_isa_pages_fallback);
+#endif
+#ifdef CONFIG_PCI
+EXPORT_SYMBOL(snd_malloc_pci_pages);
+EXPORT_SYMBOL(snd_malloc_pci_pages_fallback);
+EXPORT_SYMBOL(snd_malloc_pci_page);
+EXPORT_SYMBOL(snd_free_pci_pages);
+EXPORT_SYMBOL(snd_malloc_sgbuf_pages);
+EXPORT_SYMBOL(snd_free_sgbuf_pages);
+#endif
+#ifdef CONFIG_SBUS
+EXPORT_SYMBOL(snd_malloc_sbus_pages);
+EXPORT_SYMBOL(snd_malloc_sbus_pages_fallback);
+EXPORT_SYMBOL(snd_free_sbus_pages);
+#endif
Index: /GPL/branches/alsa-resync1/alsa-kernel/core/memory.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/core/memory.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/core/memory.c	(revision 91)
@@ -0,0 +1,330 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *  Memory allocation helpers.
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <sound/info.h>
+#include <sound/memalloc.h>
+
+/*
+ *  memory allocation helpers and debug routines
+ */
+
+#ifdef CONFIG_SND_DEBUG_MEMORY
+
+struct snd_alloc_track {
+    unsigned long magic;
+    void *caller;
+    size_t size;
+    struct list_head list;
+    long data[0];
+};
+
+#define snd_alloc_track_entry(obj) (struct snd_alloc_track *)((char*)obj - (unsigned long)((struct snd_alloc_track *)0)->data)
+
+static long snd_alloc_pages;
+static long snd_alloc_kmalloc;
+static long snd_alloc_vmalloc;
+static LIST_HEAD(snd_alloc_kmalloc_list);
+static LIST_HEAD(snd_alloc_vmalloc_list);
+static spinlock_t snd_alloc_kmalloc_lock = SPIN_LOCK_UNLOCKED;
+static spinlock_t snd_alloc_vmalloc_lock = SPIN_LOCK_UNLOCKED;
+#define KMALLOC_MAGIC 0x87654321
+#define VMALLOC_MAGIC 0x87654320
+static snd_info_entry_t *snd_memory_info_entry;
+
+void snd_memory_init(void)
+{
+    snd_alloc_pages = 0;
+    snd_alloc_kmalloc = 0;
+    snd_alloc_vmalloc = 0;
+}
+
+void snd_memory_done(void)
+{
+    struct list_head *head;
+    struct snd_alloc_track *t;
+    if (snd_alloc_pages > 0)
+        snd_printk("Not freed snd_alloc_pages = %li\n", snd_alloc_pages);
+    if (snd_alloc_kmalloc > 0)
+        snd_printk("Not freed snd_alloc_kmalloc = %li\n", snd_alloc_kmalloc);
+    if (snd_alloc_vmalloc > 0)
+        snd_printk("Not freed snd_alloc_vmalloc = %li\n", snd_alloc_vmalloc);
+    list_for_each_prev(head, &snd_alloc_kmalloc_list) {
+        t = list_entry(head, struct snd_alloc_track, list);
+        if (t->magic != KMALLOC_MAGIC) {
+            snd_printk("Corrupted kmalloc\n");
+            break;
+        }
+        snd_printk("kmalloc(%ld) from %p not freed\n", (long) t->size, t->caller);
+    }
+    list_for_each_prev(head, &snd_alloc_vmalloc_list) {
+        t = list_entry(head, struct snd_alloc_track, list);
+        if (t->magic != VMALLOC_MAGIC) {
+            snd_printk("Corrupted vmalloc\n");
+            break;
+        }
+        snd_printk("vmalloc(%ld) from %p not freed\n", (long) t->size, t->caller);
+    }
+}
+
+void *__snd_kmalloc(size_t size, int flags, void *caller)
+{
+    unsigned long cpu_flags;
+    struct snd_alloc_track *t;
+    void *ptr;
+
+    ptr = snd_wrapper_kmalloc(size + sizeof(struct snd_alloc_track), flags);
+    if (ptr != NULL) {
+        t = (struct snd_alloc_track *)ptr;
+        t->magic = KMALLOC_MAGIC;
+        t->caller = caller;
+        spin_lock_irqsave(&snd_alloc_kmalloc_lock, cpu_flags);
+        list_add_tail(&t->list, &snd_alloc_kmalloc_list);
+        spin_unlock_irqrestore(&snd_alloc_kmalloc_lock, cpu_flags);
+        t->size = size;
+        snd_alloc_kmalloc += size;
+        ptr = t->data;
+    }
+    return ptr;
+}
+
+#define _snd_kmalloc(size, flags) __snd_kmalloc((size), (flags), __builtin_return_address(0));
+void *snd_hidden_kmalloc(size_t size, int flags)
+{
+    return _snd_kmalloc(size, flags);
+}
+
+void *snd_hidden_kcalloc(size_t n, size_t size, int flags)
+{
+    void *ret = NULL;
+    if (n != 0 && size > INT_MAX / n)
+        return ret;
+    ret = _snd_kmalloc(n * size, flags);
+    if (ret)
+        memset(ret, 0, n * size);
+    return ret;
+}
+
+void snd_hidden_kfree(const void *obj)
+{
+    unsigned long flags;
+    struct snd_alloc_track *t;
+    if (obj == NULL) {
+        snd_printk("null kfree (called from %p)\n", __builtin_return_address(0));
+        return;
+    }
+    t = snd_alloc_track_entry(obj);
+    if (t->magic != KMALLOC_MAGIC) {
+        snd_printk("bad kfree (called from %p)\n", __builtin_return_address(0));
+        return;
+    }
+    spin_lock_irqsave(&snd_alloc_kmalloc_lock, flags);
+    list_del(&t->list);
+    spin_unlock_irqrestore(&snd_alloc_kmalloc_lock, flags);
+    t->magic = 0;
+    snd_alloc_kmalloc -= t->size;
+    obj = t;
+    snd_wrapper_kfree(obj);
+}
+
+void *snd_hidden_vmalloc(unsigned long size)
+{
+    void *ptr;
+    ptr = snd_wrapper_vmalloc(size + sizeof(struct snd_alloc_track));
+    if (ptr) {
+        struct snd_alloc_track *t = (struct snd_alloc_track *)ptr;
+        t->magic = VMALLOC_MAGIC;
+        t->caller = __builtin_return_address(0);
+        spin_lock(&snd_alloc_vmalloc_lock);
+        list_add_tail(&t->list, &snd_alloc_vmalloc_list);
+        spin_unlock(&snd_alloc_vmalloc_lock);
+        t->size = size;
+        snd_alloc_vmalloc += size;
+        ptr = t->data;
+    }
+    return ptr;
+}
+
+void snd_hidden_vfree(void *obj)
+{
+    struct snd_alloc_track *t;
+    if (obj == NULL) {
+        snd_printk("null vfree (called from %p)\n", __builtin_return_address(0));
+        return;
+    }
+    t = snd_alloc_track_entry(obj);
+    if (t->magic != VMALLOC_MAGIC) {
+        snd_printk("bad vfree (called from %p)\n", __builtin_return_address(0));
+        return;
+    }
+    spin_lock(&snd_alloc_vmalloc_lock);
+    list_del(&t->list);
+    spin_unlock(&snd_alloc_vmalloc_lock);
+    t->magic = 0;
+    snd_alloc_vmalloc -= t->size;
+    obj = t;
+    snd_wrapper_vfree(obj);
+}
+
+static void snd_memory_info_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer)
+{
+    long pages = snd_alloc_pages >> (PAGE_SHIFT-12);
+    snd_iprintf(buffer, "pages  : %li bytes (%li pages per %likB)\n", pages * PAGE_SIZE, pages, PAGE_SIZE / 1024);
+    snd_iprintf(buffer, "kmalloc: %li bytes\n", snd_alloc_kmalloc);
+    snd_iprintf(buffer, "vmalloc: %li bytes\n", snd_alloc_vmalloc);
+}
+
+int __init snd_memory_info_init(void)
+{
+    snd_info_entry_t *entry;
+
+    entry = snd_info_create_module_entry(THIS_MODULE, "meminfo", NULL);
+    if (entry) {
+        entry->content = SNDRV_INFO_CONTENT_TEXT;
+        entry->c.text.read_size = 256;
+        entry->c.text.read = snd_memory_info_read;
+        if (snd_info_register(entry) < 0) {
+            snd_info_free_entry(entry);
+            entry = NULL;
+        }
+    }
+    snd_memory_info_entry = entry;
+    return 0;
+}
+
+int __exit snd_memory_info_done(void)
+{
+    if (snd_memory_info_entry)
+        snd_info_unregister(snd_memory_info_entry);
+    return 0;
+}
+#else
+#define _snd_kmalloc kmalloc
+#endif /* CONFIG_SND_DEBUG_MEMORY */
+
+/**
+ * snd_kmalloc_strdup - copy the string
+ * @string: the original string
+ * @flags: allocation conditions, GFP_XXX
+ *
+ * Allocates a memory chunk via kmalloc() and copies the string to it.
+ *
+ * Returns the pointer, or NULL if no enoguh memory.
+ */
+char *snd_kmalloc_strdup(const char *string, int flags)
+{
+    size_t len;
+    char *ptr;
+
+    if (!string)
+        return NULL;
+    len = strlen(string) + 1;
+    ptr = _snd_kmalloc(len, flags);
+    if (ptr)
+        memcpy(ptr, string, len);
+    return ptr;
+}
+
+/**
+ * copy_to_user_fromio - copy data from mmio-space to user-space
+ * @dst: the destination pointer on user-space
+ * @src: the source pointer on mmio
+ * @count: the data size to copy in bytes
+ *
+ * Copies the data from mmio-space to user-space.
+ *
+ * Returns zero if successful, or non-zero on failure.
+ */
+int copy_to_user_fromio(void __user *dst, const volatile void __iomem *src, size_t count)
+{
+#if defined(__i386__) || defined(CONFIG_SPARC32)
+    return copy_to_user(dst, (const void*)src, count) ? -EFAULT : 0;
+#else
+    char buf[256];
+    while (count) {
+        size_t c = count;
+        if (c > sizeof(buf))
+            c = sizeof(buf);
+        memcpy_fromio(buf, (void*)src, c);
+        if (copy_to_user(dst, buf, c))
+            return -EFAULT;
+        count -= c;
+        (char*)dst += c;
+        (char*)src += c;
+    }
+    return 0;
+#endif
+}
+
+/**
+ * copy_from_user_toio - copy data from user-space to mmio-space
+ * @dst: the destination pointer on mmio-space
+ * @src: the source pointer on user-space
+ * @count: the data size to copy in bytes
+ *
+ * Copies the data from user-space to mmio-space.
+ *
+ * Returns zero if successful, or non-zero on failure.
+ */
+int copy_from_user_toio(volatile void __iomem *dst, const void __user *src, size_t count)
+{
+#if defined(__i386__) || defined(CONFIG_SPARC32)
+    return copy_from_user((void*)dst, src, count) ? -EFAULT : 0;
+#else
+    char buf[256];
+    while (count) {
+        size_t c = count;
+        if (c > sizeof(buf))
+            c = sizeof(buf);
+        if (copy_from_user(buf, src, c))
+            return -EFAULT;
+        memcpy_toio((void*)dst, buf, c);
+        count -= c;
+        (char*)dst += c;
+        (char*)src += c;
+    }
+    return 0;
+#endif
+}
+
+char *snd_hidden_kstrdup(const char *s, int flags)
+{
+    int len;
+    char *buf;
+
+    if (!s) return NULL;
+
+    len = strlen(s) + 1;
+    buf = _snd_kmalloc(len, flags);
+    if (buf)
+        memcpy(buf, s, len);
+    return buf;
+}
+
+void *kzalloc(size_t size, unsigned int flags)
+{
+    void *ret = _snd_kmalloc(size, flags);
+    if (ret)
+        memset(ret, 0, size);
+    return ret;
+}
+
Index: /GPL/branches/alsa-resync1/alsa-kernel/core/misc.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/core/misc.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/core/misc.c	(revision 91)
@@ -0,0 +1,736 @@
+/*
+ *  Misc and compatibility things
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <sound/driver.h>
+#include <sound/firmware.h>
+int snd_task_name(struct task_struct *task, char *name, size_t size)
+{
+	unsigned int idx;
+
+	snd_assert(task != NULL && name != NULL && size >= 2, return -EINVAL);
+	for (idx = 0; idx < sizeof(task->comm) && idx + 1 < size; idx++)
+		name[idx] = task->comm[idx];
+	name[idx] = '\0';
+	return 0;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
+
+int try_inc_mod_count(struct module *module)
+{
+	__MOD_INC_USE_COUNT(module);
+	return 1;
+}
+
+struct resource snd_compat_mem_region;
+
+struct resource *snd_compat_request_region(unsigned long start, unsigned long size, const char *name)
+{
+	struct resource *resource;
+
+	resource = snd_kcalloc(sizeof(struct resource), GFP_KERNEL);
+	if (resource == NULL)
+		return NULL;
+	if (check_region(start, size)) {
+		kfree(resource);
+		return NULL;
+	}
+	snd_wrapper_request_region(start, size, name);
+	resource->name = name;
+	resource->start = start;
+	resource->end = start + size - 1;
+	resource->flags = IORESOURCE_IO;
+	return resource;
+}
+
+int snd_compat_release_resource(struct resource *resource)
+{
+	snd_runtime_check(resource != NULL, return -EINVAL);
+	if (resource == &snd_compat_mem_region)
+		return 0;
+	release_region(resource->start, (resource->end - resource->start) + 1);
+	kfree(resource);
+	return 0;
+}
+
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) && defined(CONFIG_PCI)
+
+/*
+ *  Registration of PCI drivers and handling of hot-pluggable devices.
+ */
+
+static LIST_HEAD(pci_drivers);
+
+struct pci_driver_mapping {
+	struct pci_dev *dev;
+	struct pci_driver *drv;
+	unsigned long dma_mask;
+	void *driver_data;
+};
+
+#define PCI_MAX_MAPPINGS 64
+static struct pci_driver_mapping drvmap [PCI_MAX_MAPPINGS] = { { NULL, } , };
+
+
+static struct pci_driver_mapping *get_pci_driver_mapping(struct pci_dev *dev)
+{
+	int i;
+	
+	for (i = 0; i < PCI_MAX_MAPPINGS; i++)
+		if (drvmap[i].dev == dev)
+			return &drvmap[i];
+	return NULL;
+}
+
+void * snd_pci_compat_get_driver_data (struct pci_dev *dev)
+{
+	struct pci_driver_mapping *map = get_pci_driver_mapping(dev);
+	if (map)
+		return map->driver_data;
+	return NULL;
+}
+
+
+void snd_pci_compat_set_driver_data (struct pci_dev *dev, void *driver_data)
+{
+	struct pci_driver_mapping *map = get_pci_driver_mapping(dev);
+	if (map)
+		map->driver_data = driver_data;
+}
+
+
+unsigned long snd_pci_compat_get_dma_mask (struct pci_dev *dev)
+{
+	struct pci_driver_mapping *map = get_pci_driver_mapping(dev);
+	if (map)
+		return map->dma_mask;
+	return 0;
+}
+
+
+void snd_pci_compat_set_dma_mask (struct pci_dev *dev, unsigned long mask)
+{
+	struct pci_driver_mapping *map = get_pci_driver_mapping(dev);
+	if (map)
+		map->dma_mask = mask;
+}
+
+
+const struct pci_device_id * snd_pci_compat_match_device(const struct pci_device_id *ids, struct pci_dev *dev)
+{
+	u16 subsystem_vendor, subsystem_device;
+
+	pci_read_config_word(dev, PCI_SUBSYSTEM_VENDOR_ID, &subsystem_vendor);
+	pci_read_config_word(dev, PCI_SUBSYSTEM_ID, &subsystem_device);
+
+	while (ids->vendor || ids->subvendor || ids->class_mask) {
+		if ((ids->vendor == PCI_ANY_ID || ids->vendor == dev->vendor) &&
+		    (ids->device == PCI_ANY_ID || ids->device == dev->device) &&
+		    (ids->subvendor == PCI_ANY_ID || ids->subvendor == subsystem_vendor) &&
+		    (ids->subdevice == PCI_ANY_ID || ids->subdevice == subsystem_device) &&
+		    !((ids->class ^ dev->class) & ids->class_mask))
+			return ids;
+		ids++;
+	}
+	return NULL;
+}
+
+static int snd_pci_announce_device(struct pci_driver *drv, struct pci_dev *dev)
+{
+        int i;
+	const struct pci_device_id *id;
+
+	if (drv->id_table) {
+		id = snd_pci_compat_match_device(drv->id_table, dev);
+		if (!id)
+			return 0;
+	} else {
+		id = NULL;
+	}
+        for (i = 0; i < PCI_MAX_MAPPINGS; i++) {
+	        if (drvmap[i].dev == NULL) {
+	                drvmap[i].dev = dev;
+	                drvmap[i].drv = drv;
+			drvmap[i].dma_mask = ~0UL;
+	                break;
+	        }
+        }
+        if (i >= PCI_MAX_MAPPINGS)
+		return 0;
+	if (drv->probe(dev, id) < 0) {
+                 drvmap[i].dev = NULL;
+	         return 0;
+	}
+	return 1;
+}
+
+int snd_pci_compat_register_driver(struct pci_driver *drv)
+{
+	struct pci_dev *dev;
+	int count = 0;
+
+	list_add_tail(&drv->node, &pci_drivers);
+	pci_for_each_dev(dev) {
+		struct pci_driver_mapping *map = get_pci_driver_mapping(dev);
+		if (! map)
+			count += snd_pci_announce_device(drv, dev);
+	}
+	return count;
+}
+
+void snd_pci_compat_unregister_driver(struct pci_driver *drv)
+{
+	struct pci_dev *dev;
+
+	list_del(&drv->node);
+	pci_for_each_dev(dev) {
+		struct pci_driver_mapping *map = get_pci_driver_mapping(dev);
+		if (map && map->drv == drv) {
+			if (drv->remove)
+				drv->remove(dev);
+			map->dev = NULL;
+			map->drv = NULL;
+	        }
+	}
+}
+
+unsigned long snd_pci_compat_get_size (struct pci_dev *dev, int n_base)
+{
+	u32 l, sz;
+	int reg = PCI_BASE_ADDRESS_0 + (n_base << 2);
+
+	pci_read_config_dword (dev, reg, &l);
+	if (l == 0xffffffff)
+		return 0;
+
+	pci_write_config_dword (dev, reg, ~0);
+	pci_read_config_dword (dev, reg, &sz);
+	pci_write_config_dword (dev, reg, l);
+
+	if (!sz || sz == 0xffffffff)
+		return 0;
+	if ((l & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_MEMORY) {
+		sz = ~(sz & PCI_BASE_ADDRESS_MEM_MASK);
+	} else {
+		sz = ~(sz & PCI_BASE_ADDRESS_IO_MASK) & 0xffff;
+	}
+	
+	return sz;
+}
+
+int snd_pci_compat_get_flags (struct pci_dev *dev, int n_base)
+{
+	unsigned long foo = dev->base_address[n_base] & PCI_BASE_ADDRESS_SPACE;
+	int flags = 0;
+	
+	if (foo == 0)
+		flags |= IORESOURCE_MEM;
+	if (foo == 1)
+		flags |= IORESOURCE_IO;
+	
+	return flags;
+}
+
+/*
+ *  Set power management state of a device.  For transitions from state D3
+ *  it isn't as straightforward as one could assume since many devices forget
+ *  their configuration space during wakeup.  Returns old power state.
+ */
+int snd_pci_compat_set_power_state(struct pci_dev *dev, int new_state)
+{
+	u32 base[5], romaddr;
+	u16 pci_command, pwr_command;
+	u8  pci_latency, pci_cacheline;
+	int i, old_state;
+	int pm = snd_pci_compat_find_capability(dev, PCI_CAP_ID_PM);
+
+	if (!pm)
+		return 0;
+	pci_read_config_word(dev, pm + PCI_PM_CTRL, &pwr_command);
+	old_state = pwr_command & PCI_PM_CTRL_STATE_MASK;
+	if (old_state == new_state)
+		return old_state;
+	if (old_state == 3) {
+		pci_read_config_word(dev, PCI_COMMAND, &pci_command);
+		pci_write_config_word(dev, PCI_COMMAND, pci_command & ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY));
+		for (i = 0; i < 5; i++)
+			pci_read_config_dword(dev, PCI_BASE_ADDRESS_0 + i*4, &base[i]);
+		pci_read_config_dword(dev, PCI_ROM_ADDRESS, &romaddr);
+		pci_read_config_byte(dev, PCI_LATENCY_TIMER, &pci_latency);
+		pci_read_config_byte(dev, PCI_CACHE_LINE_SIZE, &pci_cacheline);
+		pci_write_config_word(dev, pm + PCI_PM_CTRL, new_state);
+		for (i = 0; i < 5; i++)
+			pci_write_config_dword(dev, PCI_BASE_ADDRESS_0 + i*4, base[i]);
+		pci_write_config_dword(dev, PCI_ROM_ADDRESS, romaddr);
+		pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq);
+		pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, pci_cacheline);
+		pci_write_config_byte(dev, PCI_LATENCY_TIMER, pci_latency);
+		pci_write_config_word(dev, PCI_COMMAND, pci_command);
+	} else
+		pci_write_config_word(dev, pm + PCI_PM_CTRL, (pwr_command & ~PCI_PM_CTRL_STATE_MASK) | new_state);
+	return old_state;
+}
+
+/*
+ *  Initialize device before it's used by a driver. Ask low-level code
+ *  to enable I/O and memory. Wake up the device if it was suspended.
+ *  Beware, this function can fail.
+ */
+int snd_pci_compat_enable_device(struct pci_dev *dev)
+{
+	u16 pci_command;
+
+	pci_read_config_word(dev, PCI_COMMAND, &pci_command);
+	pci_write_config_word(dev, PCI_COMMAND, pci_command | (PCI_COMMAND_IO | PCI_COMMAND_MEMORY));
+	snd_pci_compat_set_power_state(dev, 0);
+	return 0;
+}
+
+int snd_pci_compat_find_capability(struct pci_dev *dev, int cap)
+{
+	u16 status;
+	u8 pos, id;
+	int ttl = 48;
+
+	pci_read_config_word(dev, PCI_STATUS, &status);
+	if (!(status & PCI_STATUS_CAP_LIST))
+		return 0;
+	pci_read_config_byte(dev, PCI_CAPABILITY_LIST, &pos);
+	while (ttl-- && pos >= 0x40) {
+		pos &= ~3;
+		pci_read_config_byte(dev, pos + PCI_CAP_LIST_ID, &id);
+		if (id == 0xff)
+			break;
+		if (id == cap)
+			return pos;
+		pci_read_config_byte(dev, pos + PCI_CAP_LIST_NEXT, &pos);
+	}
+	return 0;
+}
+
+static void *snd_pci_compat_alloc_consistent1(unsigned long dma_mask,
+					      unsigned long size,
+					      int hop)
+{
+	void *res;
+
+	if (++hop > 10)
+		return NULL;
+	res = snd_malloc_pages(size, GFP_KERNEL | (dma_mask <= 0x00ffffff ? GFP_DMA : 0));
+	if (res == NULL)
+		return NULL;
+	if ((virt_to_bus(res) & ~dma_mask) ||
+	    ((virt_to_bus(res) + size - 1) & ~dma_mask)) {
+		void *res1 = snd_pci_compat_alloc_consistent1(dma_mask, size, hop);
+		snd_free_pages(res, size);
+		return res1;
+	}
+	return res;
+}
+
+void *snd_pci_compat_alloc_consistent(struct pci_dev *dev,
+				      long size,
+				      dma_addr_t *dmaaddr)
+{
+	unsigned long dma_mask = snd_pci_compat_get_dma_mask(dev);
+	void *res = snd_pci_compat_alloc_consistent1(dma_mask, size, 0);
+	if (res != NULL)
+		*dmaaddr = (dma_addr_t)virt_to_bus(res);
+	return res;
+}
+
+void snd_pci_compat_free_consistent(struct pci_dev *dev, long size, void *ptr, dma_addr_t dmaaddr)
+{
+	snd_runtime_check(bus_to_virt(dmaaddr) == ptr, return);
+	snd_free_pages(ptr, size);
+}
+
+int snd_pci_compat_dma_supported(struct pci_dev *dev, dma_addr_t mask)
+{
+	return 1;
+}
+
+#endif /* kernel version < 2.3.0 && CONFIG_PCI */
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) && defined(CONFIG_APM)
+
+#include <linux/apm_bios.h>
+
+static spinlock_t pm_devs_lock = SPIN_LOCK_UNLOCKED;
+static LIST_HEAD(pm_devs);
+
+#ifdef CONFIG_PCI
+static struct pm_dev *pci_compat_pm_dev;
+static int pci_compat_pm_callback(struct pm_dev *pdev, pm_request_t rqst, void *data)
+{
+	struct pci_dev *dev;
+	switch (rqst) {
+	case PM_SUSPEND:
+		pci_for_each_dev(dev) {
+			struct pci_driver_mapping *map = get_pci_driver_mapping(dev);
+			if (map->drv && map->drv->suspend)
+				map->drv->suspend(dev);
+		}
+		break;
+	case PM_RESUME:
+		pci_for_each_dev(dev) {
+			struct pci_driver_mapping *map = get_pci_driver_mapping(dev);
+			if (map->drv && map->drv->resume)
+				map->drv->resume(dev);
+		}
+		break;
+	}	
+	return 0;
+}
+#endif
+
+static int snd_apm_callback(apm_event_t ev)
+{
+	struct list_head *entry;
+	pm_request_t rqst;
+	void *data;
+	int status;
+	
+	switch (ev) {
+	case APM_SYS_SUSPEND:
+	case APM_USER_SUSPEND:
+	case APM_CRITICAL_SUSPEND:
+		rqst = PM_SUSPEND;
+		data = (void *)3;
+		break;
+	case APM_NORMAL_RESUME:
+	case APM_CRITICAL_RESUME:
+	case APM_STANDBY_RESUME:		/* ??? */
+		rqst = PM_RESUME;
+		data = (void *)0;
+		break;
+	default:
+		return 0;
+	}
+	for (entry = pm_devs.next; entry != &pm_devs; entry = entry->next) {
+		struct pm_dev *dev = list_entry(entry, struct pm_dev, entry);
+		if ((status = pm_send(dev, rqst, data)))
+			return status;
+	}
+	return 0;
+}
+
+int __init pm_init(void)
+{
+	if (apm_register_callback(snd_apm_callback))
+		snd_printk("apm_register_callback failure!\n");
+#ifdef CONFIG_PCI
+	pci_compat_pm_dev = pm_register(PM_PCI_DEV, 0, pci_compat_pm_callback);
+#endif
+	return 0;
+}
+
+void __exit pm_done(void)
+{
+#ifdef CONFIG_PCI
+	if (pci_compat_pm_dev)
+		pm_unregister(pci_compat_pm_dev);
+#endif
+	apm_unregister_callback(snd_apm_callback);
+}
+
+struct pm_dev *pm_register(pm_dev_t type,
+			   unsigned long id,
+			   pm_callback callback)
+{
+	struct pm_dev *dev = kmalloc(sizeof(struct pm_dev), GFP_KERNEL);
+
+	if (dev) {
+		unsigned long flags;
+		
+		memset(dev, 0, sizeof(*dev));
+		dev->type = type;
+		dev->id = id;
+		dev->callback = callback;
+		
+		spin_lock_irqsave(&pm_devs_lock, flags);
+		list_add(&dev->entry, &pm_devs);
+		spin_unlock_irqrestore(&pm_devs_lock, flags);
+	}
+	return dev;
+}
+
+void pm_unregister(struct pm_dev *dev)
+{
+	if (dev) {
+		unsigned long flags;
+		
+		spin_lock_irqsave(&pm_devs_lock, flags);
+		list_del(&dev->entry);
+		spin_unlock_irqrestore(&pm_devs_lock, flags);
+
+		kfree(dev);
+	}
+}
+
+int pm_send(struct pm_dev *dev, pm_request_t rqst, void *data)
+{
+	int status = 0;
+	int prev_state, next_state;
+	
+	switch (rqst) {
+	case PM_SUSPEND:
+	case PM_RESUME:
+		prev_state = dev->state;
+		next_state = (int) data;
+		if (prev_state != next_state) {
+			if (dev->callback)
+				status = (*dev->callback)(dev, rqst, data);
+			if (!status) {
+				dev->state = next_state;
+				dev->prev_state = prev_state;
+			}
+		} else {
+			dev->prev_state = prev_state;
+		}
+		break;
+	default:
+		if (dev->callback)
+			status = (*dev->callback)(dev, rqst, data);
+		break;
+	}
+	return status;
+}
+
+#endif /* kernel version < 2.3.0 && CONFIG_APM */
+
+static void run_workqueue(struct workqueue_struct *wq)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&wq->lock, flags);
+	while (!list_empty(&wq->worklist)) {
+		struct work_struct *work = list_entry(wq->worklist.next,
+						      struct work_struct, entry);
+		void (*f) (void *) = work->func;
+		void *data = work->data;
+
+		list_del_init(wq->worklist.next);
+		spin_unlock_irqrestore(&wq->lock, flags);
+		clear_bit(0, &work->pending);
+		f(data);
+		spin_lock_irqsave(&wq->lock, flags);
+		wake_up(&wq->work_done);
+	}
+	spin_unlock_irqrestore(&wq->lock, flags);
+}
+
+void flush_workqueue(struct workqueue_struct *wq)
+{
+	if (0 /* wq->task == current */) {
+		run_workqueue(wq);
+	} else {
+		wait_queue_t wait;
+
+		init_waitqueue_entry(&wait, current);
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		spin_lock_irq(&wq->lock);
+		add_wait_queue(&wq->work_done, &wait);
+		while (!list_empty(&wq->worklist)) {
+			spin_unlock_irq(&wq->lock);
+			schedule();
+			spin_lock_irq(&wq->lock);
+		}
+		remove_wait_queue(&wq->work_done, &wait);
+		spin_unlock_irq(&wq->lock);
+	}
+}
+
+void destroy_workqueue(struct workqueue_struct *wq)
+{
+    flush_workqueue(wq);
+#if 0
+	kill_proc(wq->task_pid, SIGKILL, 1);
+	if (wq->task_pid >= 0)
+            wait_for_completion(&wq->thread_exited);
+#endif
+	kfree(wq);
+}
+
+static int xworker_thread(void *data)
+{
+	struct workqueue_struct *wq = data;
+
+	strcpy(current->comm, wq->name); /* FIXME: different names? */
+
+	do {
+            run_workqueue(wq);
+#if 0
+            wait_event_interruptible(wq->more_work, !list_empty(&wq->worklist));
+#endif
+	} while (!signal_pending(current));
+#if 0
+        complete_and_exit(&wq->thread_exited, 0);
+#endif
+}
+
+struct workqueue_struct *create_workqueue(const char *name)
+{
+	struct workqueue_struct *wq;
+	
+	//BUG_ON(strlen(name) > 10);
+	
+	wq = kmalloc(sizeof(*wq), GFP_KERNEL);
+	if (!wq)
+		return NULL;
+	memset(wq, 0, sizeof(*wq));
+	
+	spin_lock_init(&wq->lock);
+	INIT_LIST_HEAD(&wq->worklist);
+	init_waitqueue_head(&wq->more_work);
+        init_waitqueue_head(&wq->work_done);
+        wq->name = name;
+#if 0
+	init_completion(&wq->thread_exited);
+	wq->task_pid = kernel_thread(xworker_thread, wq, 0);
+	if (wq->task_pid < 0) {
+		printk(KERN_ERR "snd: failed to start thread %s\n", name);
+		destroy_workqueue(wq);
+		wq = NULL;
+        }
+#endif
+	return wq;
+}
+
+static void __x_queue_work(struct workqueue_struct *wq, struct work_struct *work)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&wq->lock, flags);
+	work->wq_data = wq;
+	list_add_tail(&work->entry, &wq->worklist);
+	wake_up(&wq->more_work);
+	spin_unlock_irqrestore(&wq->lock, flags);
+}
+
+int queue_work(struct workqueue_struct *wq, struct work_struct *work)
+{
+	if (!test_and_set_bit(0, &work->pending)) {
+		__x_queue_work(wq, work);
+		return 1;
+	}
+	return 0;
+}
+
+static void delayed_work_timer_fn(unsigned long __data)
+{
+	struct work_struct *work = (struct work_struct *)__data;
+	struct workqueue_struct *wq = work->wq_data;
+	
+	__x_queue_work(wq, work);
+}
+
+int queue_delayed_work(struct workqueue_struct *wq, struct work_struct *work, unsigned long delay)
+{
+	struct timer_list *timer = &work->timer;
+
+	if (!test_and_set_bit(0, &work->pending)) {
+		work->wq_data = work;
+		timer->expires = jiffies + delay;
+		timer->data = (unsigned long)work;
+		timer->function = delayed_work_timer_fn;
+		add_timer(timer);
+		return 1;
+	}
+	return 0;
+}
+
+void release_and_free_resource(struct resource *res)
+{
+        if (res) {
+                release_resource(res);
+                kfree(res);
+        }
+}
+
+int mod_firmware_load(const char *fn, char **fp)
+{
+    return 0;
+}
+
+static int snd_try_load_firmware(const char *path, const char *name,
+				 struct firmware *firmware)
+{
+	char filename[30 + FIRMWARE_NAME_MAX];
+
+	sprintf(filename, "%s/%s", path, name);
+	firmware->size = mod_firmware_load(filename, (char **)&firmware->data);
+	if (firmware->size)
+		printk(KERN_INFO "Loaded '%s'.", filename);
+	return firmware->size;
+}
+
+int request_firmware(const struct firmware **fw, const char *name)
+{
+	struct firmware *firmware;
+
+	*fw = NULL;
+	firmware = kmalloc(sizeof *firmware, GFP_KERNEL);
+	if (!firmware)
+		return -ENOMEM;
+	if (!snd_try_load_firmware("/lib/firmware", name, firmware) &&
+	    !snd_try_load_firmware("/lib/hotplug/firmware", name, firmware) &&
+	    !snd_try_load_firmware("/usr/lib/hotplug/firmware", name, firmware)) {
+		kfree(firmware);
+		return -EIO;
+	}
+	*fw = firmware;
+	return 0;
+}
+
+void release_firmware(const struct firmware *fw)
+{
+	if (fw) {
+		vfree(fw->data);
+		kfree(fw);
+	}
+}
+
+/**
+ * snd_pci_quirk_lookup - look up a PCI SSID quirk list
+ * @pci: pci_dev handle
+ * @list: quirk list, terminated by a null entry
+ *
+ * Look through the given quirk list and finds a matching entry
+ * with the same PCI SSID.  When subdevice is 0, all subdevice
+ * values may match.
+ *
+ * Returns the matched entry pointer, or NULL if nothing matched.
+ */
+const struct snd_pci_quirk *
+snd_pci_quirk_lookup(struct pci_dev *pci, const struct snd_pci_quirk *list)
+{
+	const struct snd_pci_quirk *q;
+
+	for (q = list; q->subvendor; q++)
+		if (q->subvendor == pci->subsystem_vendor &&
+		    (!q->subdevice || q->subdevice == pci->subsystem_device))
+			return q;
+	return NULL;
+}
+
Index: /GPL/branches/alsa-resync1/alsa-kernel/core/pcm.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/core/pcm.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/core/pcm.c	(revision 91)
@@ -0,0 +1,1107 @@
+/*
+ *  Digital Audio (PCM) abstract layer
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <sound/minors.h>
+#include <sound/pcm.h>
+#include <sound/control.h>
+#include <sound/info.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>, Abramo Bagnara <abramo@alsa-project.org>");
+MODULE_DESCRIPTION("Midlevel PCM code for ALSA.");
+MODULE_LICENSE("GPL");
+
+snd_pcm_t *snd_pcm_devices[SNDRV_CARDS * SNDRV_PCM_DEVICES];
+static LIST_HEAD(snd_pcm_notify_list);
+static DECLARE_MUTEX(register_mutex);
+static int snd_pcm_free(snd_pcm_t *pcm);
+static int snd_pcm_dev_free(struct snd_device *device);
+static int snd_pcm_dev_register(struct snd_device *device);
+static int snd_pcm_dev_disconnect(struct snd_device *device);
+static int snd_pcm_dev_unregister(struct snd_device *device);
+
+static int snd_pcm_control_ioctl(snd_card_t * card,
+                                 snd_ctl_file_t * control,
+                                 unsigned int cmd, unsigned long arg)
+{
+    unsigned int tmp;
+
+    tmp = card->number * SNDRV_PCM_DEVICES;
+    switch (cmd) {
+    case SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE:
+        {
+            int device;
+
+            if (get_user(device, (int *)arg))
+                return -EFAULT;
+            device = device < 0 ? 0 : device + 1;
+            while (device < SNDRV_PCM_DEVICES) {
+                if (snd_pcm_devices[tmp + device])
+                    break;
+                device++;
+            }
+            if (device == SNDRV_PCM_DEVICES)
+                device = -1;
+            if (put_user(device, (int *)arg))
+                return -EFAULT;
+            return 0;
+        }
+    case SNDRV_CTL_IOCTL_PCM_INFO:
+        {
+            snd_pcm_info_t *info = (snd_pcm_info_t *)arg;
+            unsigned int device, subdevice;
+            int stream;
+            snd_pcm_t *pcm;
+            snd_pcm_str_t *pstr;
+            snd_pcm_substream_t *substream;
+            if (get_user(device, &info->device))
+                return -EFAULT;
+            if (device >= SNDRV_PCM_DEVICES)
+                return -ENXIO;
+            pcm = snd_pcm_devices[tmp + device];
+            if (pcm == NULL)
+                return -ENXIO;
+            if (get_user(stream, &info->stream))
+                return -EFAULT;
+            if (stream < 0 || stream > 1)
+                return -EINVAL;
+            pstr = &pcm->streams[stream];
+            if (pstr->substream_count == 0)
+                return -ENOENT;
+            if (get_user(subdevice, &info->subdevice))
+                return -EFAULT;
+            if (subdevice >= pstr->substream_count)
+                return -ENXIO;
+            for (substream = pstr->substream; substream; substream = substream->next)
+                if (substream->number == (int)subdevice)
+                    break;
+            if (substream == NULL)
+                return -ENXIO;
+            return snd_pcm_info_user(substream, info);
+        }
+    case SNDRV_CTL_IOCTL_PCM_PREFER_SUBDEVICE:
+        {
+            int val;
+
+            if (get_user(val, (int *)arg))
+                return -EFAULT;
+            control->prefer_pcm_subdevice = val;
+            return 0;
+        }
+    }
+    return -ENOIOCTLCMD;
+}
+#define STATE(v) [SNDRV_PCM_STATE_##v] = #v
+#define STREAM(v) [SNDRV_PCM_STREAM_##v] = #v
+#define READY(v) [SNDRV_PCM_READY_##v] = #v
+#define XRUN(v) [SNDRV_PCM_XRUN_##v] = #v
+#define SILENCE(v) [SNDRV_PCM_SILENCE_##v] = #v
+#define TSTAMP(v) [SNDRV_PCM_TSTAMP_##v] = #v
+#define ACCESS(v) [SNDRV_PCM_ACCESS_##v] = #v
+#define START(v) [SNDRV_PCM_START_##v] = #v
+#define FORMAT(v) [SNDRV_PCM_FORMAT_##v] = #v
+#define SUBFORMAT(v) [SNDRV_PCM_SUBFORMAT_##v] = #v
+
+char *snd_pcm_stream_names[] = {
+    "PLAYBACK",
+    "CAPTURE",
+};
+
+char *snd_pcm_state_names[] = {
+    "OPEN",
+    "SETUP",
+    "PREPARED",
+    "RUNNING",
+    "XRUN",
+    "DRAINING",
+    "PAUSED",
+    "SUSPENDED",
+};
+
+char *snd_pcm_access_names[] = {
+    "MMAP_INTERLEAVED",
+    "MMAP_NONINTERLEAVED",
+    "MMAP_COMPLEX",
+    "RW_INTERLEAVED",
+    "RW_NONINTERLEAVED",
+};
+
+char *snd_pcm_format_names[] = {
+    "S8",
+    "U8",
+    "S16_LE",
+    "S16_BE",
+    "U16_LE",
+    "U16_BE",
+    "S24_LE",
+    "S24_BE",
+    "U24_LE",
+    "U24_BE",
+    "S32_LE",
+    "S32_BE",
+    "U32_LE",
+    "U32_BE",
+    "FLOAT_LE",
+    "FLOAT_BE",
+    "FLOAT64_LE",
+    "FLOAT64_BE",
+    "IEC958_SUBFRAME_LE",
+    "IEC958_SUBFRAME_BE",
+    "MU_LAW",
+    "A_LAW",
+    "IMA_ADPCM",
+    "MPEG",
+    "GSM",
+    "SPECIAL",
+    "S24_3LE",
+    "S24_3BE",
+    "U24_3LE",
+    "U24_3BE",
+    "S20_3LE",
+    "S20_3BE",
+    "U20_3LE",
+    "U20_3BE",
+    "S18_3LE",
+    "S18_3BE",
+    "U18_3LE",
+    "U18_3BE",
+};
+
+char *snd_pcm_subformat_names[] = {
+    "STD",
+};
+
+char *snd_pcm_tstamp_mode_names[] = {
+    "NONE",
+    "MMAP",
+};
+
+const char *snd_pcm_stream_name(int stream)
+{
+    snd_assert(stream <= SNDRV_PCM_STREAM_LAST, return 0);
+    return snd_pcm_stream_names[stream];
+}
+
+const char *snd_pcm_access_name(snd_pcm_access_t access)
+{
+    snd_assert(access <= SNDRV_PCM_ACCESS_LAST, return 0);
+    return snd_pcm_access_names[access];
+}
+
+const char *snd_pcm_format_name(snd_pcm_format_t format)
+{
+    snd_assert(format <= SNDRV_PCM_FORMAT_LAST, return 0);
+    return snd_pcm_format_names[format];
+}
+
+const char *snd_pcm_subformat_name(snd_pcm_subformat_t subformat)
+{
+    snd_assert(subformat <= SNDRV_PCM_SUBFORMAT_LAST, return 0);
+    return snd_pcm_subformat_names[subformat];
+}
+
+const char *snd_pcm_tstamp_mode_name(int mode)
+{
+    snd_assert(mode <= SNDRV_PCM_TSTAMP_LAST, return 0);
+    return snd_pcm_tstamp_mode_names[mode];
+}
+
+const char *snd_pcm_state_name(snd_pcm_state_t state)
+{
+    snd_assert(state <= SNDRV_PCM_STATE_LAST, return 0);
+    return snd_pcm_state_names[state];
+}
+
+#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
+#include <linux/soundcard.h>
+const char *snd_pcm_oss_format_name(int format)
+{
+    switch (format) {
+    case AFMT_MU_LAW:
+        return "MU_LAW";
+    case AFMT_A_LAW:
+        return "A_LAW";
+    case AFMT_IMA_ADPCM:
+        return "IMA_ADPCM";
+    case AFMT_U8:
+        return "U8";
+    case AFMT_S16_LE:
+        return "S16_LE";
+    case AFMT_S16_BE:
+        return "S16_BE";
+    case AFMT_S8:
+        return "S8";
+    case AFMT_U16_LE:
+        return "U16_LE";
+    case AFMT_U16_BE:
+        return "U16_BE";
+    case AFMT_MPEG:
+        return "MPEG";
+    default:
+        return "unknown";
+    }
+}
+#endif
+
+#ifdef CONFIG_PROC_FS
+static void snd_pcm_proc_info_read(snd_pcm_substream_t *substream, snd_info_buffer_t *buffer)
+{
+    snd_pcm_info_t info;
+    int err;
+    snd_runtime_check(substream, return);
+    err = snd_pcm_info(substream, &info);
+    if (err < 0) {
+        snd_iprintf(buffer, "error %d\n", err);
+        return;
+    }
+    snd_iprintf(buffer, "card: %d\n", info.card);
+    snd_iprintf(buffer, "device: %d\n", info.device);
+    snd_iprintf(buffer, "subdevice: %d\n", info.subdevice);
+    snd_iprintf(buffer, "stream: %s\n", snd_pcm_stream_name(info.stream));
+    snd_iprintf(buffer, "id: %s\n", info.id);
+    snd_iprintf(buffer, "name: %s\n", info.name);
+    snd_iprintf(buffer, "subname: %s\n", info.subname);
+    snd_iprintf(buffer, "class: %d\n", info.dev_class);
+    snd_iprintf(buffer, "subclass: %d\n", info.dev_subclass);
+    snd_iprintf(buffer, "subdevices_count: %d\n", info.subdevices_count);
+    snd_iprintf(buffer, "subdevices_avail: %d\n", info.subdevices_avail);
+}
+
+static void snd_pcm_stream_proc_info_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer)
+{
+    snd_pcm_proc_info_read(((snd_pcm_str_t *)entry->private_data)->substream, buffer);
+}
+
+static void snd_pcm_substream_proc_info_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer)
+{
+    snd_pcm_proc_info_read((snd_pcm_substream_t *)entry->private_data, buffer);
+}
+
+static void snd_pcm_substream_proc_hw_params_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer)
+{
+    snd_pcm_substream_t *substream = (snd_pcm_substream_t *)entry->private_data;
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    if (!runtime) {
+        snd_iprintf(buffer, "closed\n");
+        return;
+    }
+    snd_pcm_stream_lock_irq(substream);
+    if (runtime->status->state == SNDRV_PCM_STATE_OPEN) {
+        snd_iprintf(buffer, "no setup\n");
+        snd_pcm_stream_unlock_irq(substream);
+        return;
+    }
+    snd_iprintf(buffer, "access: %s\n", snd_pcm_access_name(runtime->access));
+    snd_iprintf(buffer, "format: %s\n", snd_pcm_format_name(runtime->format));
+    snd_iprintf(buffer, "subformat: %s\n", snd_pcm_subformat_name(runtime->subformat));
+    snd_iprintf(buffer, "channels: %u\n", runtime->channels);
+    snd_iprintf(buffer, "rate: %u (%u/%u)\n", runtime->rate, runtime->rate_num, runtime->rate_den);
+    snd_iprintf(buffer, "period_size: %lu\n", runtime->period_size);
+    snd_iprintf(buffer, "buffer_size: %lu\n", runtime->buffer_size);
+    snd_iprintf(buffer, "tick_time: %u\n", runtime->tick_time);
+#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
+    if (substream->oss.oss) {
+        snd_iprintf(buffer, "OSS format: %s\n", snd_pcm_oss_format_name(runtime->oss.format));
+        snd_iprintf(buffer, "OSS channels: %u\n", runtime->oss.channels);
+        snd_iprintf(buffer, "OSS rate: %u\n", runtime->oss.rate);
+        snd_iprintf(buffer, "OSS period bytes: %lu\n", (unsigned long)runtime->oss.period_bytes);
+        snd_iprintf(buffer, "OSS periods: %u\n", runtime->oss.periods);
+    }
+#endif
+    snd_pcm_stream_unlock_irq(substream);
+}
+
+static void snd_pcm_substream_proc_sw_params_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer)
+{
+    snd_pcm_substream_t *substream = (snd_pcm_substream_t *)entry->private_data;
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    if (!runtime) {
+        snd_iprintf(buffer, "closed\n");
+        return;
+    }
+    snd_pcm_stream_lock_irq(substream);
+    if (runtime->status->state == SNDRV_PCM_STATE_OPEN) {
+        snd_iprintf(buffer, "no setup\n");
+        snd_pcm_stream_unlock_irq(substream);
+        return;
+    }
+    snd_iprintf(buffer, "tstamp_mode: %s\n", snd_pcm_tstamp_mode_name(runtime->tstamp_mode));
+    snd_iprintf(buffer, "period_step: %u\n", runtime->period_step);
+    snd_iprintf(buffer, "sleep_min: %u\n", runtime->sleep_min);
+    snd_iprintf(buffer, "avail_min: %lu\n", runtime->control->avail_min);
+    snd_iprintf(buffer, "xfer_align: %lu\n", runtime->xfer_align);
+    snd_iprintf(buffer, "start_threshold: %lu\n", runtime->start_threshold);
+    snd_iprintf(buffer, "stop_threshold: %lu\n", runtime->stop_threshold);
+    snd_iprintf(buffer, "silence_threshold: %lu\n", runtime->silence_threshold);
+    snd_iprintf(buffer, "silence_size: %lu\n", runtime->silence_size);
+    snd_iprintf(buffer, "boundary: %lu\n", runtime->boundary);
+    snd_pcm_stream_unlock_irq(substream);
+}
+
+static void snd_pcm_substream_proc_status_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer)
+{
+    snd_pcm_substream_t *substream = (snd_pcm_substream_t *)entry->private_data;
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    snd_pcm_status_t status;
+    int err;
+    if (!runtime) {
+        snd_iprintf(buffer, "closed\n");
+        return;
+    }
+    memset(&status, 0, sizeof(status));
+    err = snd_pcm_status(substream, &status);
+    if (err < 0) {
+        snd_iprintf(buffer, "error %d\n", err);
+        return;
+    }
+    snd_iprintf(buffer, "state: %s\n", snd_pcm_state_name(status.state));
+#ifndef TARGET_OS2
+    snd_iprintf(buffer, "trigger_time: %ld.%09ld\n",
+                status.trigger_tstamp.tv_sec, status.trigger_tstamp.tv_nsec);
+    snd_iprintf(buffer, "tstamp      : %ld.%09ld\n",
+                status.tstamp.tv_sec, status.tstamp.tv_nsec);
+#endif
+    snd_iprintf(buffer, "delay       : %ld\n", status.delay);
+    snd_iprintf(buffer, "avail       : %ld\n", status.avail);
+    snd_iprintf(buffer, "avail_max   : %ld\n", status.avail_max);
+    snd_iprintf(buffer, "-----\n");
+    snd_iprintf(buffer, "hw_ptr      : %ld\n", runtime->status->hw_ptr);
+    snd_iprintf(buffer, "appl_ptr    : %ld\n", runtime->control->appl_ptr);
+}
+#endif
+
+#ifdef CONFIG_SND_DEBUG
+static void snd_pcm_xrun_debug_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer)
+{
+    snd_pcm_str_t *pstr = (snd_pcm_str_t *)entry->private_data;
+    snd_iprintf(buffer, "%d\n", pstr->xrun_debug);
+}
+
+static void snd_pcm_xrun_debug_write(snd_info_entry_t *entry, snd_info_buffer_t *buffer)
+{
+    snd_pcm_str_t *pstr = (snd_pcm_str_t *)entry->private_data;
+    char line[64];
+    if (!snd_info_get_line(buffer, line, sizeof(line)))
+        pstr->xrun_debug = !!simple_strtoul(line, NULL, 10);
+}
+#endif
+
+static int snd_pcm_stream_proc_init(snd_pcm_str_t *pstr)
+{
+    snd_pcm_t *pcm = pstr->pcm;
+    snd_info_entry_t *entry;
+    char name[16];
+
+    sprintf(name, "pcm%i%c", pcm->device,
+            pstr->stream == SNDRV_PCM_STREAM_PLAYBACK ? 'p' : 'c');
+    if ((entry = snd_info_create_card_entry(pcm->card, name, pcm->card->proc_root)) == NULL)
+        return -ENOMEM;
+    entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
+    if (snd_info_register(entry) < 0) {
+        snd_info_free_entry(entry);
+        return -ENOMEM;
+    }
+    pstr->proc_root = entry;
+
+    if ((entry = snd_info_create_card_entry(pcm->card, "info", pstr->proc_root)) != NULL) {
+        snd_info_set_text_ops(entry, pstr, 256, snd_pcm_stream_proc_info_read);
+        if (snd_info_register(entry) < 0) {
+            snd_info_free_entry(entry);
+            entry = NULL;
+        }
+    }
+    pstr->proc_info_entry = entry;
+
+#ifdef CONFIG_SND_DEBUG
+    if ((entry = snd_info_create_card_entry(pcm->card, "xrun_debug", pstr->proc_root)) != NULL) {
+        entry->c.text.read_size = 64;
+        entry->c.text.read = snd_pcm_xrun_debug_read;
+        entry->c.text.write_size = 64;
+        entry->c.text.write = snd_pcm_xrun_debug_write;
+        entry->private_data = pstr;
+        if (snd_info_register(entry) < 0) {
+            snd_info_free_entry(entry);
+            entry = NULL;
+        }
+    }
+    pstr->proc_xrun_debug_entry = entry;
+#endif
+    return 0;
+}
+
+static int snd_pcm_stream_proc_done(snd_pcm_str_t *pstr)
+{
+#ifdef CONFIG_SND_DEBUG
+    if (pstr->proc_xrun_debug_entry) {
+        snd_info_unregister(pstr->proc_xrun_debug_entry);
+        pstr->proc_xrun_debug_entry = NULL;
+    }
+#endif
+    if (pstr->proc_info_entry) {
+        snd_info_unregister(pstr->proc_info_entry);
+        pstr->proc_info_entry = NULL;
+    }
+    if (pstr->proc_root) {
+        snd_info_unregister(pstr->proc_root);
+        pstr->proc_root = NULL;
+    }
+    return 0;
+}
+
+static int snd_pcm_substream_proc_init(snd_pcm_substream_t *substream)
+{
+    snd_info_entry_t *entry;
+    snd_card_t *card;
+    char name[16];
+
+    card = substream->pcm->card;
+
+    sprintf(name, "sub%i", substream->number);
+    if ((entry = snd_info_create_card_entry(card, name, substream->pstr->proc_root)) == NULL)
+        return -ENOMEM;
+    entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
+    if (snd_info_register(entry) < 0) {
+        snd_info_free_entry(entry);
+        return -ENOMEM;
+    }
+    substream->proc_root = entry;
+
+    if ((entry = snd_info_create_card_entry(card, "info", substream->proc_root)) != NULL) {
+        snd_info_set_text_ops(entry, substream, 256, snd_pcm_substream_proc_info_read);
+        if (snd_info_register(entry) < 0) {
+            snd_info_free_entry(entry);
+            entry = NULL;
+        }
+    }
+    substream->proc_info_entry = entry;
+
+    if ((entry = snd_info_create_card_entry(card, "hw_params", substream->proc_root)) != NULL) {
+        snd_info_set_text_ops(entry, substream, 256, snd_pcm_substream_proc_hw_params_read);
+        if (snd_info_register(entry) < 0) {
+            snd_info_free_entry(entry);
+            entry = NULL;
+        }
+    }
+    substream->proc_hw_params_entry = entry;
+
+    if ((entry = snd_info_create_card_entry(card, "sw_params", substream->proc_root)) != NULL) {
+        snd_info_set_text_ops(entry, substream, 256, snd_pcm_substream_proc_sw_params_read);
+        if (snd_info_register(entry) < 0) {
+            snd_info_free_entry(entry);
+            entry = NULL;
+        }
+    }
+    substream->proc_sw_params_entry = entry;
+
+    if ((entry = snd_info_create_card_entry(card, "status", substream->proc_root)) != NULL) {
+        snd_info_set_text_ops(entry, substream, 256, snd_pcm_substream_proc_status_read);
+        if (snd_info_register(entry) < 0) {
+            snd_info_free_entry(entry);
+            entry = NULL;
+        }
+    }
+    substream->proc_status_entry = entry;
+
+    return 0;
+}
+
+static int snd_pcm_substream_proc_done(snd_pcm_substream_t *substream)
+{
+    if (substream->proc_info_entry) {
+        snd_info_unregister(substream->proc_info_entry);
+        substream->proc_info_entry = NULL;
+    }
+    if (substream->proc_hw_params_entry) {
+        snd_info_unregister(substream->proc_hw_params_entry);
+        substream->proc_hw_params_entry = NULL;
+    }
+    if (substream->proc_sw_params_entry) {
+        snd_info_unregister(substream->proc_sw_params_entry);
+        substream->proc_sw_params_entry = NULL;
+    }
+    if (substream->proc_status_entry) {
+        snd_info_unregister(substream->proc_status_entry);
+        substream->proc_status_entry = NULL;
+    }
+    if (substream->proc_root) {
+        snd_info_unregister(substream->proc_root);
+        substream->proc_root = NULL;
+    }
+    return 0;
+}
+
+/**
+ * snd_pcm_new_stream - create a new PCM stream
+ * @pcm: the pcm instance
+ * @stream: the stream direction, SNDRV_PCM_STREAM_XXX
+ * @substream_count: the number of substreams
+ *
+ * Creates a new stream for the pcm.
+ * The corresponding stream on the pcm must have been empty before
+ * calling this, i.e. zero must be given to the argument of
+ * snd_pcm_new().
+ *
+ * Returns zero if successful, or a negative error code on failure.
+ */
+int snd_pcm_new_stream(snd_pcm_t *pcm, int stream, int substream_count)
+{
+    int idx, err;
+    snd_pcm_str_t *pstr = &pcm->streams[stream];
+    struct snd_pcm_substream *substream, *prev;
+
+#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
+    init_MUTEX(&pstr->oss.setup_mutex);
+#endif
+    pstr->stream = stream;
+    pstr->pcm = pcm;
+    pstr->substream_count = substream_count;
+    pstr->reg = &snd_pcm_reg[stream];
+    if (substream_count > 0) {
+        err = snd_pcm_stream_proc_init(pstr);
+        if (err < 0)
+            return err;
+    }
+    prev = NULL;
+    for (idx = 0, prev = NULL; idx < substream_count; idx++) {
+        substream = (struct snd_pcm_substream *)kzalloc(sizeof(*substream), GFP_KERNEL);
+        if (substream == NULL)
+            return -ENOMEM;
+        substream->pcm = pcm;
+        substream->pstr = pstr;
+        substream->number = idx;
+        substream->stream = stream;
+        sprintf(substream->name, "subdevice #%i", idx);
+        substream->buffer_bytes_max = UINT_MAX;
+        if (prev == NULL)
+            pstr->substream = substream;
+        else
+            prev->next = substream;
+        err = snd_pcm_substream_proc_init(substream);
+        if (err < 0) {
+            kfree(substream);
+            return err;
+        }
+        substream->group = &substream->self_group;
+        spin_lock_init(&substream->self_group.lock);
+        INIT_LIST_HEAD(&substream->self_group.substreams);
+        list_add_tail(&substream->link_list, &substream->self_group.substreams);
+        spin_lock_init(&substream->timer_lock);
+        prev = substream;
+    }
+    return 0;
+}
+
+/**
+ * snd_pcm_new - create a new PCM instance
+ * @card: the card instance
+ * @id: the id string
+ * @device: the device index (zero based)
+ * @playback_count: the number of substreams for playback
+ * @capture_count: the number of substreams for capture
+ * @rpcm: the pointer to store the new pcm instance
+ *
+ * Creates a new PCM instance.
+ *
+ * The pcm operators have to be set afterwards to the new instance
+ * via snd_pcm_set_ops().
+ *
+ * Returns zero if successful, or a negative error code on failure.
+ */
+int snd_pcm_new(snd_card_t * card, char *id, int device,
+                int playback_count, int capture_count,
+                snd_pcm_t ** rpcm)
+{
+    snd_pcm_t *pcm;
+    int err;
+    static snd_device_ops_t ops = {
+        snd_pcm_dev_free,
+        snd_pcm_dev_register,
+        snd_pcm_dev_disconnect,
+        snd_pcm_dev_unregister
+    };
+
+    snd_assert(rpcm != NULL, return -EINVAL);
+    *rpcm = NULL;
+    snd_assert(card != NULL, return -ENXIO);
+    pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);
+    if (pcm == NULL)
+        return -ENOMEM;
+    pcm->card = card;
+    pcm->device = device;
+    if (id) {
+        strlcpy(pcm->id, id, sizeof(pcm->id));
+    }
+    if ((err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK, playback_count)) < 0) {
+        snd_pcm_free(pcm);
+        return err;
+    }
+    if ((err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_CAPTURE, capture_count)) < 0) {
+        snd_pcm_free(pcm);
+        return err;
+    }
+    init_MUTEX(&pcm->open_mutex);
+    init_waitqueue_head(&pcm->open_wait);
+    if ((err = snd_device_new(card, SNDRV_DEV_PCM, pcm, &ops)) < 0) {
+        snd_pcm_free(pcm);
+        return err;
+    }
+    *rpcm = pcm;
+    return 0;
+}
+
+static void snd_pcm_free_stream(snd_pcm_str_t * pstr)
+{
+    snd_pcm_substream_t *substream, *substream_next;
+#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
+    snd_pcm_oss_setup_t *setup, *setupn;
+#endif
+    substream = pstr->substream;
+    while (substream) {
+        substream_next = substream->next;
+        snd_pcm_substream_proc_done(substream);
+        kfree(substream);
+        substream = substream_next;
+    }
+    snd_pcm_stream_proc_done(pstr);
+#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
+    for (setup = pstr->oss.setup_list; setup; setup = setupn) {
+        setupn = setup->next;
+        kfree(setup->task_name);
+        kfree(setup);
+    }
+#endif
+}
+
+static int snd_pcm_free(snd_pcm_t *pcm)
+{
+    snd_assert(pcm != NULL, return -ENXIO);
+    if (pcm->private_free)
+        pcm->private_free(pcm);
+    snd_pcm_lib_preallocate_free_for_all(pcm);
+    snd_pcm_free_stream(&pcm->streams[SNDRV_PCM_STREAM_PLAYBACK]);
+    snd_pcm_free_stream(&pcm->streams[SNDRV_PCM_STREAM_CAPTURE]);
+    kfree(pcm);
+    return 0;
+}
+
+static int snd_pcm_dev_free(snd_device_t *device)
+{
+    snd_pcm_t *pcm = device->device_data;
+    return snd_pcm_free(pcm);
+}
+
+static void snd_pcm_tick_timer_func(unsigned long data)
+{
+    snd_pcm_substream_t *substream = (snd_pcm_substream_t*) data;
+    snd_pcm_tick_elapsed(substream);
+}
+
+int snd_pcm_open_substream(snd_pcm_t *pcm, int stream,
+                           snd_pcm_substream_t **rsubstream)
+{
+    snd_pcm_str_t * pstr;
+    snd_pcm_substream_t * substream;
+    snd_pcm_runtime_t * runtime;
+    snd_ctl_file_t *kctl;
+    snd_card_t *card;
+    struct list_head *list;
+    int prefer_subdevice = -1;
+    size_t size;
+
+    snd_assert(rsubstream != NULL, return -EINVAL);
+    *rsubstream = NULL;
+    snd_assert(pcm != NULL, return -ENXIO);
+    pstr = &pcm->streams[stream];
+    if (pstr->substream == NULL)
+        return -ENODEV;
+
+    card = pcm->card;
+    down_read(&card->controls_rwsem);
+    list_for_each(list, &card->ctl_files) {
+        kctl = snd_ctl_file(list);
+        if (kctl->pid == current->pid) {
+            prefer_subdevice = kctl->prefer_pcm_subdevice;
+            break;
+        }
+    }
+    up_read(&card->controls_rwsem);
+
+    if (pstr->substream_count == 0)
+        return -ENODEV;
+    switch (stream) {
+    case SNDRV_PCM_STREAM_PLAYBACK:
+        if (pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX) {
+            for (substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; substream; substream = substream->next) {
+                if (SUBSTREAM_BUSY(substream))
+                {
+                    printk("substream %x bysy\n", substream);
+                    return -EAGAIN;
+                }
+            }
+        }
+        break;
+    case SNDRV_PCM_STREAM_CAPTURE:
+        if (pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX) {
+            for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next) {
+                if (SUBSTREAM_BUSY(substream))
+                {
+                    return -EAGAIN;
+                }
+            }
+        }
+        break;
+    default:
+        return -EINVAL;
+    }
+
+    if (prefer_subdevice >= 0) {
+        for (substream = pstr->substream; substream; substream = substream->next)
+            if (!SUBSTREAM_BUSY(substream) && substream->number == prefer_subdevice)
+                goto __ok;
+    }
+    for (substream = pstr->substream; substream; substream = substream->next)
+        if (!SUBSTREAM_BUSY(substream))
+            break;
+__ok:
+    if (substream == NULL)
+    {
+        return -EAGAIN;
+    }
+
+    runtime = kzalloc(sizeof(*runtime), GFP_KERNEL);
+    if (runtime == NULL)
+        return -ENOMEM;
+
+    size = PAGE_ALIGN(sizeof(snd_pcm_mmap_status_t));
+    runtime->status = snd_malloc_pages(size, GFP_KERNEL);
+    if (runtime->status == NULL) {
+        kfree(runtime);
+        return -ENOMEM;
+    }
+    memset((void*)runtime->status, 0, size);
+
+    size = PAGE_ALIGN(sizeof(snd_pcm_mmap_control_t));
+    runtime->control = snd_malloc_pages(size, GFP_KERNEL);
+    if (runtime->control == NULL) {
+        snd_free_pages((void*)runtime->status, PAGE_ALIGN(sizeof(snd_pcm_mmap_status_t)));
+        kfree(runtime);
+        return -ENOMEM;
+    }
+    memset((void*)runtime->control, 0, size);
+
+    init_waitqueue_head(&runtime->sleep);
+    atomic_set(&runtime->mmap_count, 0);
+    init_timer(&runtime->tick_timer);
+    runtime->tick_timer.function = snd_pcm_tick_timer_func;
+    runtime->tick_timer.data = (unsigned long) substream;
+
+    runtime->status->state = SNDRV_PCM_STATE_OPEN;
+
+    substream->runtime = runtime;
+    substream->private_data = pcm->private_data;
+    pstr->substream_opened++;
+    *rsubstream = substream;
+    return 0;
+}
+
+void snd_pcm_release_substream(snd_pcm_substream_t *substream)
+{
+    snd_pcm_runtime_t * runtime;
+    substream->file = NULL;
+    runtime = substream->runtime;
+    snd_assert(runtime != NULL, return);
+    if (runtime->private_free != NULL)
+        runtime->private_free(runtime);
+    snd_free_pages((void*)runtime->status, PAGE_ALIGN(sizeof(snd_pcm_mmap_status_t)));
+    snd_free_pages((void*)runtime->control, PAGE_ALIGN(sizeof(snd_pcm_mmap_control_t)));
+    if (runtime->hw_constraints.rules)
+        kfree(runtime->hw_constraints.rules);
+    kfree(runtime);
+    substream->runtime = NULL;
+    substream->pstr->substream_opened--;
+}
+
+static int snd_pcm_dev_register(snd_device_t *device)
+{
+    int idx, cidx, err;
+    unsigned short minor;
+    snd_pcm_substream_t *substream;
+    struct list_head *list;
+    char str[16];
+    snd_pcm_t *pcm = device->device_data;
+
+    snd_assert(pcm != NULL && device != NULL, return -ENXIO);
+    down(&register_mutex);
+    idx = (pcm->card->number * SNDRV_PCM_DEVICES) + pcm->device;
+    if (snd_pcm_devices[idx]) {
+        up(&register_mutex);
+        return -EBUSY;
+    }
+    snd_pcm_devices[idx] = pcm;
+    for (cidx = 0; cidx < 2; cidx++) {
+        int devtype = -1;
+        if (pcm->streams[cidx].substream == NULL)
+            continue;
+        switch (cidx) {
+        case SNDRV_PCM_STREAM_PLAYBACK:
+            sprintf(str, "pcmC%iD%ip", pcm->card->number, pcm->device);
+            minor = SNDRV_MINOR_PCM_PLAYBACK + idx;
+            devtype = SNDRV_DEVICE_TYPE_PCM_PLAYBACK;
+            break;
+        case SNDRV_PCM_STREAM_CAPTURE:
+            sprintf(str, "pcmC%iD%ic", pcm->card->number, pcm->device);
+            minor = SNDRV_MINOR_PCM_CAPTURE + idx;
+            devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE;
+            break;
+        }
+        if ((err = snd_register_device(devtype, pcm->card, pcm->device, pcm->streams[cidx].reg, str)) < 0) {
+            snd_pcm_devices[idx] = NULL;
+            up(&register_mutex);
+            return err;
+        }
+        for (substream = pcm->streams[cidx].substream; substream; substream = substream->next)
+            snd_pcm_timer_init(substream);
+    }
+    list_for_each(list, &snd_pcm_notify_list) {
+        snd_pcm_notify_t *notify;
+        notify = list_entry(list, snd_pcm_notify_t, list);
+        notify->n_register(pcm);
+    }
+    up(&register_mutex);
+    return 0;
+}
+
+static int snd_pcm_dev_disconnect(snd_device_t *device)
+{
+    snd_pcm_t *pcm = device->device_data;
+    struct list_head *list;
+    snd_pcm_substream_t *substream;
+    int idx, cidx;
+
+    down(&register_mutex);
+    idx = (pcm->card->number * SNDRV_PCM_DEVICES) + pcm->device;
+    snd_pcm_devices[idx] = NULL;
+    for (cidx = 0; cidx < 2; cidx++)
+        for (substream = pcm->streams[cidx].substream; substream; substream = substream->next)
+            if (substream->runtime)
+                substream->runtime->status->state = SNDRV_PCM_STATE_DISCONNECTED;
+    list_for_each(list, &snd_pcm_notify_list) {
+        snd_pcm_notify_t *notify;
+        notify = list_entry(list, snd_pcm_notify_t, list);
+        notify->n_disconnect(pcm);
+    }
+    up(&register_mutex);
+    return 0;
+}
+
+static int snd_pcm_dev_unregister(snd_device_t *device)
+{
+    int idx, cidx, devtype;
+    snd_pcm_substream_t *substream;
+    struct list_head *list;
+    snd_pcm_t *pcm = device->device_data;
+
+    snd_assert(pcm != NULL, return -ENXIO);
+    down(&register_mutex);
+    idx = (pcm->card->number * SNDRV_PCM_DEVICES) + pcm->device;
+    snd_pcm_devices[idx] = NULL;
+    for (cidx = 0; cidx < 2; cidx++) {
+        devtype = -1;
+        switch (cidx) {
+        case SNDRV_PCM_STREAM_PLAYBACK:
+            devtype = SNDRV_DEVICE_TYPE_PCM_PLAYBACK;
+            break;
+        case SNDRV_PCM_STREAM_CAPTURE:
+            devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE;
+            break;
+        }
+        snd_unregister_device(devtype, pcm->card, pcm->device);
+        for (substream = pcm->streams[cidx].substream; substream; substream = substream->next)
+            snd_pcm_timer_done(substream);
+    }
+    list_for_each(list, &snd_pcm_notify_list) {
+        snd_pcm_notify_t *notify;
+        notify = list_entry(list, snd_pcm_notify_t, list);
+        notify->n_unregister(pcm);
+    }
+    up(&register_mutex);
+    return snd_pcm_free(pcm);
+}
+
+int snd_pcm_notify(snd_pcm_notify_t *notify, int nfree)
+{
+    int idx;
+
+    snd_assert(notify != NULL && notify->n_register != NULL && notify->n_unregister != NULL, return -EINVAL);
+    down(&register_mutex);
+    if (nfree) {
+        list_del(&notify->list);
+        for (idx = 0; idx < SNDRV_CARDS * SNDRV_PCM_DEVICES; idx++) {
+            if (snd_pcm_devices[idx] == NULL)
+                continue;
+            notify->n_unregister(snd_pcm_devices[idx]);
+        }
+    } else {
+        list_add_tail(&notify->list, &snd_pcm_notify_list);
+        for (idx = 0; idx < SNDRV_CARDS * SNDRV_PCM_DEVICES; idx++) {
+            if (snd_pcm_devices[idx] == NULL)
+                continue;
+            notify->n_register(snd_pcm_devices[idx]);
+        }
+    }
+    up(&register_mutex);
+    return 0;
+}
+
+/*
+ *  Info interface
+ */
+
+static void snd_pcm_proc_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer)
+{
+    int idx;
+    snd_pcm_t *pcm;
+
+    down(&register_mutex);
+    for (idx = 0; idx < SNDRV_CARDS * SNDRV_PCM_DEVICES; idx++) {
+        pcm = snd_pcm_devices[idx];
+        if (pcm == NULL)
+            continue;
+        snd_iprintf(buffer, "%02i-%02i: %s : %s", idx / SNDRV_PCM_DEVICES,
+                    idx % SNDRV_PCM_DEVICES, pcm->id, pcm->name);
+        if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream)
+            snd_iprintf(buffer, " : playback %i", pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream_count);
+        if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream)
+            snd_iprintf(buffer, " : capture %i", pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream_count);
+        snd_iprintf(buffer, "\n");
+
+    }
+    up(&register_mutex);
+}
+
+void pcm_info(void)
+{
+    int idx;
+    snd_pcm_t *pcm;
+
+    down(&register_mutex);
+    for (idx = 0; idx < SNDRV_CARDS * SNDRV_PCM_DEVICES; idx++) {
+        pcm = snd_pcm_devices[idx];
+        if (pcm == NULL)
+            continue;
+        printk("%02i-%02i: %s : %s", idx / SNDRV_PCM_DEVICES,
+               idx % SNDRV_PCM_DEVICES, pcm->id, pcm->name);
+        if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream)
+            printk(" : playback %i", pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream_count);
+        if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream)
+            printk(" : capture %i", pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream_count);
+        printk("\n");
+    }
+    up(&register_mutex);
+}
+/* get number of pcm instance s for given card */
+int pcm_instances(int card_id)
+{
+    int idx;
+    int pcms = 0;
+    snd_pcm_t *pcm;
+
+    down(&register_mutex);
+    for (idx = 0; idx < SNDRV_PCM_DEVICES; idx++) {
+        pcm = snd_pcm_devices[(card_id*SNDRV_PCM_DEVICES)+idx];
+        if (pcm == NULL)
+            continue;
+        pcms++;
+    }
+    up(&register_mutex);
+    return pcms;
+}
+
+/*
+ *  ENTRY functions
+ */
+
+static snd_info_entry_t *snd_pcm_proc_entry = NULL;
+
+static int __init alsa_pcm_init(void)
+{
+    snd_info_entry_t *entry;
+
+    snd_ctl_register_ioctl(snd_pcm_control_ioctl);
+    if ((entry = snd_info_create_module_entry(THIS_MODULE, "pcm", NULL)) != NULL) {
+        snd_info_set_text_ops(entry, NULL, SNDRV_CARDS * SNDRV_PCM_DEVICES * 128, snd_pcm_proc_read);
+        if (snd_info_register(entry) < 0) {
+            snd_info_free_entry(entry);
+            entry = NULL;
+        }
+    }
+    snd_pcm_proc_entry = entry;
+    return 0;
+}
+
+static void __exit alsa_pcm_exit(void)
+{
+    snd_ctl_unregister_ioctl(snd_pcm_control_ioctl);
+    if (snd_pcm_proc_entry) {
+        snd_info_unregister(snd_pcm_proc_entry);
+        snd_pcm_proc_entry = NULL;
+    }
+}
+
+module_init(alsa_pcm_init)
+module_exit(alsa_pcm_exit)
+
+EXPORT_SYMBOL(snd_pcm_devices);
+EXPORT_SYMBOL(snd_pcm_new);
+EXPORT_SYMBOL(snd_pcm_new_stream);
+EXPORT_SYMBOL(snd_pcm_notify);
+EXPORT_SYMBOL(snd_pcm_open_substream);
+EXPORT_SYMBOL(snd_pcm_release_substream);
+EXPORT_SYMBOL(snd_pcm_format_name);
+EXPORT_SYMBOL(snd_pcm_subformat_name);
+/* pcm_native.c */
+EXPORT_SYMBOL(snd_pcm_link_rwlock);
+EXPORT_SYMBOL(snd_pcm_start);
+#ifdef CONFIG_PM
+EXPORT_SYMBOL(snd_pcm_suspend);
+EXPORT_SYMBOL(snd_pcm_suspend_all);
+#endif
+EXPORT_SYMBOL(snd_pcm_kernel_playback_ioctl);
+EXPORT_SYMBOL(snd_pcm_kernel_capture_ioctl);
+EXPORT_SYMBOL(snd_pcm_kernel_ioctl);
+EXPORT_SYMBOL(snd_pcm_open);
+EXPORT_SYMBOL(snd_pcm_release);
+EXPORT_SYMBOL(snd_pcm_playback_poll);
+EXPORT_SYMBOL(snd_pcm_capture_poll);
+EXPORT_SYMBOL(snd_pcm_mmap_data);
+/* pcm_misc.c */
+EXPORT_SYMBOL(snd_pcm_format_signed);
+EXPORT_SYMBOL(snd_pcm_format_unsigned);
+EXPORT_SYMBOL(snd_pcm_format_linear);
+EXPORT_SYMBOL(snd_pcm_format_little_endian);
+EXPORT_SYMBOL(snd_pcm_format_big_endian);
+EXPORT_SYMBOL(snd_pcm_format_width);
+EXPORT_SYMBOL(snd_pcm_format_physical_width);
+EXPORT_SYMBOL(snd_pcm_format_size);
+EXPORT_SYMBOL(snd_pcm_format_silence_64);
+EXPORT_SYMBOL(snd_pcm_format_set_silence);
+EXPORT_SYMBOL(snd_pcm_build_linear_format);
+EXPORT_SYMBOL(snd_pcm_limit_hw_rates);
Index: /GPL/branches/alsa-resync1/alsa-kernel/core/pcm_lib.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/core/pcm_lib.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/core/pcm_lib.c	(revision 91)
@@ -0,0 +1,2691 @@
+/*
+ *  Digital Audio (PCM) abstract layer
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *                   Abramo Bagnara <abramo@alsa-project.org>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <sound/control.h>
+#include <sound/info.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/timer.h>
+
+//#define DEBUG_PK // debug printk
+
+extern snd_pcm_substream_t *substream_int[8*256]; // interrupted substream
+
+/*
+ * fill ring buffer with silence
+ * runtime->silence_start: starting pointer to silence area
+ * runtime->silence_filled: size filled with silence
+ * runtime->silence_threshold: threshold from application
+ * runtime->silence_size: maximal size from application
+ *
+ * when runtime->silence_size >= runtime->boundary - fill processed area with silence immediately
+ */
+void snd_pcm_playback_silence(snd_pcm_substream_t *substream, snd_pcm_uframes_t new_hw_ptr)
+{
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    snd_pcm_uframes_t frames, ofs, transfer;
+
+    if (runtime->silence_size < runtime->boundary) {
+        snd_pcm_sframes_t noise_dist, n;
+        if (runtime->silence_start != runtime->control->appl_ptr) {
+            n = runtime->control->appl_ptr - runtime->silence_start;
+            if (n < 0)
+                n += runtime->boundary;
+            if ((snd_pcm_uframes_t)n < runtime->silence_filled)
+                runtime->silence_filled -= n;
+            else
+                runtime->silence_filled = 0;
+            runtime->silence_start = runtime->control->appl_ptr;
+        }
+        if (runtime->silence_filled == runtime->buffer_size)
+            return;
+        snd_assert(runtime->silence_filled <= runtime->buffer_size, return);
+        noise_dist = snd_pcm_playback_hw_avail(runtime) + runtime->silence_filled;
+        if (noise_dist >= (snd_pcm_sframes_t) runtime->silence_threshold)
+            return;
+        frames = runtime->silence_threshold - noise_dist;
+        if (frames > runtime->silence_size)
+            frames = runtime->silence_size;
+    } else {
+        if (new_hw_ptr == ULONG_MAX) {	/* initialization */
+            snd_pcm_sframes_t avail = snd_pcm_playback_hw_avail(runtime);
+            runtime->silence_filled = avail > 0 ? avail : 0;
+            runtime->silence_start = (runtime->status->hw_ptr +
+                                      runtime->silence_filled) %
+                runtime->boundary;
+        } else {
+            ofs = runtime->status->hw_ptr;
+            frames = new_hw_ptr - ofs;
+            if ((snd_pcm_sframes_t)frames < 0)
+                frames += runtime->boundary;
+            runtime->silence_filled -= frames;
+            if ((snd_pcm_sframes_t)runtime->silence_filled < 0) {
+                runtime->silence_filled = 0;
+                runtime->silence_start = (ofs + frames) - runtime->buffer_size;
+            } else {
+                runtime->silence_start = ofs - runtime->silence_filled;
+            }
+            if ((snd_pcm_sframes_t)runtime->silence_start < 0)
+                runtime->silence_start += runtime->boundary;
+        }
+        frames = runtime->buffer_size - runtime->silence_filled;
+    }
+    snd_assert(frames <= runtime->buffer_size, return);
+    if (frames == 0)
+        return;
+    ofs = (runtime->silence_start + runtime->silence_filled) % runtime->buffer_size;
+    while (frames > 0) {
+        transfer = ofs + frames > runtime->buffer_size ? runtime->buffer_size - ofs : frames;
+        if (runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED ||
+            runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) {
+            if (substream->ops->silence) {
+                int err;
+                err = substream->ops->silence(substream, -1, ofs, transfer);
+                snd_assert(err >= 0, );
+            } else {
+                char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, ofs);
+                snd_pcm_format_set_silence(runtime->format, hwbuf, transfer * runtime->channels);
+            }
+        } else {
+            unsigned int c;
+            unsigned int channels = runtime->channels;
+            if (substream->ops->silence) {
+                for (c = 0; c < channels; ++c) {
+                    int err;
+                    err = substream->ops->silence(substream, c, ofs, transfer);
+                    snd_assert(err >= 0, );
+                }
+            } else {
+                size_t dma_csize = runtime->dma_bytes / channels;
+                for (c = 0; c < channels; ++c) {
+                    char *hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, ofs);
+                    snd_pcm_format_set_silence(runtime->format, hwbuf, transfer);
+                }
+            }
+        }
+        runtime->silence_filled += transfer;
+        frames -= transfer;
+        ofs = 0;
+    }
+}
+
+static void xrun(snd_pcm_substream_t *substream)
+{
+    snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
+#ifdef CONFIG_SND_DEBUG
+    if (substream->pstr->xrun_debug) {
+        snd_printd(KERN_DEBUG "XRUN: pcmC%dD%d%c\n",
+                   substream->pcm->card->number,
+                   substream->pcm->device,
+                   substream->stream ? 'c' : 'p');
+        if (substream->pstr->xrun_debug > 1)
+            dump_stack();
+    }
+#endif
+}
+
+static inline snd_pcm_uframes_t snd_pcm_update_hw_ptr_pos(struct snd_pcm_substream *substream,
+                                                          struct snd_pcm_runtime *runtime)
+{
+    snd_pcm_uframes_t pos;
+
+    pos = substream->ops->pointer(substream);
+    if (pos == SNDRV_PCM_POS_XRUN)
+        return pos; /* XRUN */
+    if (runtime->tstamp_mode & SNDRV_PCM_TSTAMP_MMAP)
+        getnstimeofday((struct timespec *)&runtime->status->tstamp);
+#ifdef CONFIG_SND_DEBUG
+    if (pos >= runtime->buffer_size) {
+        snd_printk(KERN_ERR  "BUG: stream = %i, pos = 0x%lx, buffer size = 0x%lx, period size = 0x%lx\n", substream->stream, pos, runtime->buffer_size, runtime->period_size);
+    }
+#endif
+    pos -= pos % runtime->min_align;
+    return pos;
+}
+
+static inline int snd_pcm_update_hw_ptr_post(snd_pcm_substream_t *substream,
+                                             snd_pcm_runtime_t *runtime)
+{
+    snd_pcm_uframes_t avail;
+    if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+        avail = snd_pcm_playback_avail(runtime);
+    else
+        avail = snd_pcm_capture_avail(runtime);
+    if (avail > runtime->avail_max)
+        runtime->avail_max = avail;
+    if (avail >= runtime->stop_threshold) {
+        if (substream->runtime->status->state == SNDRV_PCM_STATE_DRAINING)
+            snd_pcm_drain_done(substream);
+        else
+            xrun(substream);
+        return -EPIPE;
+    }
+    if (avail >= runtime->control->avail_min)
+        wake_up(&runtime->sleep);
+    return 0;
+}
+
+static inline int snd_pcm_update_hw_ptr_interrupt(snd_pcm_substream_t *substream)
+{
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    snd_pcm_uframes_t pos;
+    snd_pcm_uframes_t new_hw_ptr, hw_ptr_interrupt;
+    snd_pcm_sframes_t delta;
+
+    pos = snd_pcm_update_hw_ptr_pos(substream, runtime);
+    if (pos == SNDRV_PCM_POS_XRUN) {
+        xrun(substream);
+        return -EPIPE;
+    }
+    if (runtime->period_size == runtime->buffer_size)
+        goto __next_buf;
+    new_hw_ptr = runtime->hw_ptr_base + pos;
+    hw_ptr_interrupt = runtime->hw_ptr_interrupt + runtime->period_size;
+
+    delta = hw_ptr_interrupt - new_hw_ptr;
+    if (delta > 0) {
+        if ((snd_pcm_uframes_t)delta < runtime->buffer_size / 2) {
+#ifdef CONFIG_SND_DEBUG
+            if (runtime->periods > 1)
+                snd_printd(KERN_ERR "Unexpected hw_pointer value [1] (stream = %i, delta: -%ld, max jitter = %ld): wrong interrupt acknowledge?\n", substream->stream, (long) delta, runtime->buffer_size / 2);
+#endif
+            return 0;
+        }
+    __next_buf:
+        runtime->hw_ptr_base += runtime->buffer_size;
+        if (runtime->hw_ptr_base == runtime->boundary)
+            runtime->hw_ptr_base = 0;
+        new_hw_ptr = runtime->hw_ptr_base + pos;
+    }
+
+    if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
+        runtime->silence_size > 0)
+        snd_pcm_playback_silence(substream, new_hw_ptr);
+
+    runtime->status->hw_ptr = new_hw_ptr;
+    runtime->hw_ptr_interrupt = new_hw_ptr - new_hw_ptr % runtime->period_size;
+
+    return snd_pcm_update_hw_ptr_post(substream, runtime);
+}
+
+/* CAUTION: call it with irq disabled */
+int snd_pcm_update_hw_ptr(snd_pcm_substream_t *substream)
+{
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    snd_pcm_uframes_t pos;
+    snd_pcm_uframes_t old_hw_ptr, new_hw_ptr;
+    snd_pcm_sframes_t delta;
+
+    old_hw_ptr = runtime->status->hw_ptr;
+    pos = snd_pcm_update_hw_ptr_pos(substream, runtime);
+    if (pos == SNDRV_PCM_POS_XRUN) {
+        xrun(substream);
+        return -EPIPE;
+    }
+    new_hw_ptr = runtime->hw_ptr_base + pos;
+
+    delta = old_hw_ptr - new_hw_ptr;
+    if (delta > 0) {
+        if ((snd_pcm_uframes_t)delta < runtime->buffer_size / 2) {
+#ifdef CONFIG_SND_DEBUG
+            if (runtime->periods > 2)
+                snd_printd(KERN_ERR "Unexpected hw_pointer value [2] (stream = %i, delta: -%ld, max jitter = %ld): wrong interrupt acknowledge?\n", substream->stream, (long) delta, runtime->buffer_size / 2);
+#endif
+            return 0;
+        }
+        runtime->hw_ptr_base += runtime->buffer_size;
+        if (runtime->hw_ptr_base == runtime->boundary)
+            runtime->hw_ptr_base = 0;
+        new_hw_ptr = runtime->hw_ptr_base + pos;
+    }
+    if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
+        runtime->silence_size > 0)
+        snd_pcm_playback_silence(substream, new_hw_ptr);
+    runtime->status->hw_ptr = new_hw_ptr;
+
+    return snd_pcm_update_hw_ptr_post(substream, runtime);
+}
+
+/**
+ * snd_pcm_set_ops - set the PCM operators
+ * @pcm: the pcm instance
+ * @direction: stream direction, SNDRV_PCM_STREAM_XXX
+ * @ops: the operator table
+ *
+ * Sets the given PCM operators to the pcm instance.
+ */
+void snd_pcm_set_ops(snd_pcm_t *pcm, int direction, snd_pcm_ops_t *ops)
+{
+    snd_pcm_str_t *stream = &pcm->streams[direction];
+    snd_pcm_substream_t *substream;
+
+    for (substream = stream->substream; substream != NULL; substream = substream->next)
+        substream->ops = ops;
+}
+
+/**
+ * snd_pcm_sync - set the PCM sync id
+ * @substream: the pcm substream
+ *
+ * Sets the PCM sync identifier for the card.
+ */
+void snd_pcm_set_sync(snd_pcm_substream_t * substream)
+{
+    snd_pcm_runtime_t *runtime = substream->runtime;
+
+    runtime->sync.id32[0] = substream->pcm->card->number;
+    runtime->sync.id32[1] = -1;
+    runtime->sync.id32[2] = -1;
+    runtime->sync.id32[3] = -1;
+}
+
+/*
+ *  Standard ioctl routine
+ */
+
+/* Code taken from alsa-lib */
+#define assert(a) snd_assert((a), return -EINVAL)
+
+static inline unsigned int div32(unsigned int a, unsigned int b,
+                                 unsigned int *r)
+{
+    if (b == 0) {
+        *r = 0;
+        return UINT_MAX;
+    }
+    *r = a % b;
+    return a / b;
+}
+
+static inline unsigned int div_down(unsigned int a, unsigned int b)
+{
+    if (b == 0)
+        return UINT_MAX;
+    return a / b;
+}
+
+static inline unsigned int div_up(unsigned int a, unsigned int b)
+{
+    unsigned int r;
+    unsigned int q;
+    if (b == 0)
+        return UINT_MAX;
+    q = div32(a, b, &r);
+    if (r)
+        ++q;
+    return q;
+}
+
+static inline unsigned int mul(unsigned int a, unsigned int b)
+{
+    if (a == 0)
+        return 0;
+    if (div_down(UINT_MAX, a) < b)
+        return UINT_MAX;
+    return a * b;
+}
+
+static inline unsigned int muldiv32(unsigned int a, unsigned int b,
+                                    unsigned int c, unsigned int *r)
+{
+    u_int64_t n = (u_int64_t) a * b;
+    if (c == 0) {
+        snd_assert(n > 0, );
+        *r = 0;
+        return UINT_MAX;
+    }
+    div64_32(&n, c, r);
+    if (n >= UINT_MAX) {
+        *r = 0;
+        return UINT_MAX;
+    }
+    return n;
+}
+
+int snd_interval_refine_min(snd_interval_t *i, unsigned int min, int openmin)
+{
+    int changed = 0;
+    assert(!snd_interval_empty(i));
+    if (i->min < min) {
+        i->min = min;
+        i->openmin = openmin;
+        changed = 1;
+    } else if (i->min == min && !i->openmin && openmin) {
+        i->openmin = 1;
+        changed = 1;
+    }
+    if (i->integer) {
+        if (i->openmin) {
+            i->min++;
+            i->openmin = 0;
+        }
+    }
+    if (snd_interval_checkempty(i)) {
+        snd_interval_none(i);
+        return -EINVAL;
+    }
+    return changed;
+}
+
+int snd_interval_refine_max(snd_interval_t *i, unsigned int max, int openmax)
+{
+    int changed = 0;
+    assert(!snd_interval_empty(i));
+    if (i->max > max) {
+        i->max = max;
+        i->openmax = openmax;
+        changed = 1;
+    } else if (i->max == max && !i->openmax && openmax) {
+        i->openmax = 1;
+        changed = 1;
+    }
+    if (i->integer) {
+        if (i->openmax) {
+            i->max--;
+            i->openmax = 0;
+        }
+    }
+    if (snd_interval_checkempty(i)) {
+        snd_interval_none(i);
+        return -EINVAL;
+    }
+    return changed;
+}
+
+/**
+ * snd_interval_refine - refine the interval value of configurator
+ * @i: the interval value to refine
+ * @v: the interval value to refer to
+ *
+ * Refines the interval value with the reference value.
+ * The interval is changed to the range satisfying both intervals.
+ * The interval status (min, max, integer, etc.) are evaluated.
+ *
+ * Returns non-zero if the value is changed, zero if not changed.
+ */
+int snd_interval_refine(snd_interval_t *i, const snd_interval_t *v)
+{
+    int changed = 0;
+    assert(!snd_interval_empty(i));
+    if (i->min < v->min) {
+        i->min = v->min;
+        i->openmin = v->openmin;
+        changed = 1;
+    } else if (i->min == v->min && !i->openmin && v->openmin) {
+        i->openmin = 1;
+        changed = 1;
+    }
+    if (i->max > v->max) {
+        i->max = v->max;
+        i->openmax = v->openmax;
+        changed = 1;
+    } else if (i->max == v->max && !i->openmax && v->openmax) {
+        i->openmax = 1;
+        changed = 1;
+    }
+    if (!i->integer && v->integer) {
+        i->integer = 1;
+        changed = 1;
+    }
+    if (i->integer) {
+        if (i->openmin) {
+            i->min++;
+            i->openmin = 0;
+        }
+        if (i->openmax) {
+            i->max--;
+            i->openmax = 0;
+        }
+    } else if (!i->openmin && !i->openmax && i->min == i->max)
+        i->integer = 1;
+    if (snd_interval_checkempty(i)) {
+        snd_interval_none(i);
+        return -EINVAL;
+    }
+    return changed;
+}
+
+int snd_interval_refine_first(snd_interval_t *i)
+{
+    assert(!snd_interval_empty(i));
+    if (snd_interval_single(i))
+        return 0;
+    i->max = i->min;
+    i->openmax = i->openmin;
+    if (i->openmax)
+        i->max++;
+    return 1;
+}
+
+int snd_interval_refine_last(snd_interval_t *i)
+{
+    assert(!snd_interval_empty(i));
+    if (snd_interval_single(i))
+        return 0;
+    i->min = i->max;
+    i->openmin = i->openmax;
+    if (i->openmin)
+        i->min--;
+    return 1;
+}
+
+int snd_interval_refine_set(snd_interval_t *i, unsigned int val)
+{
+    snd_interval_t t;
+    t.empty = 0;
+    t.min = t.max = val;
+    t.openmin = t.openmax = 0;
+    t.integer = 1;
+    return snd_interval_refine(i, &t);
+}
+
+void snd_interval_mul(const snd_interval_t *a, const snd_interval_t *b, snd_interval_t *c)
+{
+    if (a->empty || b->empty) {
+        snd_interval_none(c);
+        return;
+    }
+    c->empty = 0;
+    c->min = mul(a->min, b->min);
+    c->openmin = (a->openmin || b->openmin);
+    c->max = mul(a->max,  b->max);
+    c->openmax = (a->openmax || b->openmax);
+    c->integer = (a->integer && b->integer);
+}
+
+/**
+ * snd_interval_div - refine the interval value with division
+ *
+ * c = a / b
+ *
+ * Returns non-zero if the value is changed, zero if not changed.
+ */
+void snd_interval_div(const snd_interval_t *a, const snd_interval_t *b, snd_interval_t *c)
+{
+    unsigned int r;
+    if (a->empty || b->empty) {
+        snd_interval_none(c);
+        return;
+    }
+    c->empty = 0;
+    c->min = div32(a->min, b->max, &r);
+    c->openmin = (r || a->openmin || b->openmax);
+    if (b->min > 0) {
+        c->max = div32(a->max, b->min, &r);
+        if (r) {
+            c->max++;
+            c->openmax = 1;
+        } else
+            c->openmax = (a->openmax || b->openmin);
+    } else {
+        c->max = UINT_MAX;
+        c->openmax = 0;
+    }
+    c->integer = 0;
+}
+
+/**
+ * snd_interval_muldivk - refine the interval value
+ *
+ * c = a * b / k
+ *
+ * Returns non-zero if the value is changed, zero if not changed.
+ */
+void snd_interval_muldivk(const snd_interval_t *a, const snd_interval_t *b,
+                          unsigned int k, snd_interval_t *c)
+{
+    unsigned int r;
+    if (a->empty || b->empty) {
+        snd_interval_none(c);
+        return;
+    }
+    c->empty = 0;
+    c->min = muldiv32(a->min, b->min, k, &r);
+    c->openmin = (r || a->openmin || b->openmin);
+    c->max = muldiv32(a->max, b->max, k, &r);
+    if (r) {
+        c->max++;
+        c->openmax = 1;
+    } else
+        c->openmax = (a->openmax || b->openmax);
+    c->integer = 0;
+}
+
+/**
+ * snd_interval_mulkdiv - refine the interval value
+ *
+ * c = a * k / b
+ *
+ * Returns non-zero if the value is changed, zero if not changed.
+ */
+void snd_interval_mulkdiv(const snd_interval_t *a, unsigned int k,
+                          const snd_interval_t *b, snd_interval_t *c)
+{
+    unsigned int r;
+    if (a->empty || b->empty) {
+        snd_interval_none(c);
+        return;
+    }
+    c->empty = 0;
+    c->min = muldiv32(a->min, k, b->max, &r);
+    c->openmin = (r || a->openmin || b->openmax);
+    if (b->min > 0) {
+        c->max = muldiv32(a->max, k, b->min, &r);
+        if (r) {
+            c->max++;
+            c->openmax = 1;
+        } else
+            c->openmax = (a->openmax || b->openmin);
+    } else {
+        c->max = UINT_MAX;
+        c->openmax = 0;
+    }
+    c->integer = 0;
+}
+
+#undef assert
+/* ---- */
+
+/**
+ * snd_interval_ratnum - refine the interval value
+ *
+ * Returns non-zero if the value is changed, zero if not changed.
+ */
+int snd_interval_ratnum(snd_interval_t *i,
+                        unsigned int rats_count, ratnum_t *rats,
+                        unsigned int *nump, unsigned int *denp)
+{
+    unsigned int best_num, best_diff, best_den;
+    unsigned int k;
+    snd_interval_t t;
+    int err;
+
+    best_num = best_den = best_diff = 0;
+    for (k = 0; k < rats_count; ++k) {
+        unsigned int num = rats[k].num;
+        unsigned int den;
+        unsigned int q = i->min;
+        int diff;
+        if (q == 0)
+            q = 1;
+        den = div_down(num, q);
+        if (den < rats[k].den_min)
+            continue;
+        if (den > rats[k].den_max)
+            den = rats[k].den_max;
+        else {
+            unsigned int r;
+            r = (den - rats[k].den_min) % rats[k].den_step;
+            if (r != 0)
+                den -= r;
+        }
+        diff = num - q * den;
+        if (best_num == 0 ||
+            diff * best_den < best_diff * den) {
+            best_diff = diff;
+            best_den = den;
+            best_num = num;
+        }
+    }
+    if (best_den == 0) {
+        i->empty = 1;
+        return -EINVAL;
+    }
+    t.min = div_down(best_num, best_den);
+    t.openmin = !!(best_num % best_den);
+
+    best_num = best_den = best_diff = 0;
+    for (k = 0; k < rats_count; ++k) {
+        unsigned int num = rats[k].num;
+        unsigned int den;
+        unsigned int q = i->max;
+        int diff;
+        if (q == 0) {
+            i->empty = 1;
+            return -EINVAL;
+        }
+        den = div_up(num, q);
+        if (den > rats[k].den_max)
+            continue;
+        if (den < rats[k].den_min)
+            den = rats[k].den_min;
+        else {
+            unsigned int r;
+            r = (den - rats[k].den_min) % rats[k].den_step;
+            if (r != 0)
+                den += rats[k].den_step - r;
+        }
+        diff = q * den - num;
+        if (best_num == 0 ||
+            diff * best_den < best_diff * den) {
+            best_diff = diff;
+            best_den = den;
+            best_num = num;
+        }
+    }
+    if (best_den == 0) {
+        i->empty = 1;
+        return -EINVAL;
+    }
+    t.max = div_up(best_num, best_den);
+    t.openmax = !!(best_num % best_den);
+    t.integer = 0;
+    err = snd_interval_refine(i, &t);
+    if (err < 0)
+        return err;
+
+    if (snd_interval_single(i)) {
+        if (nump)
+            *nump = best_num;
+        if (denp)
+            *denp = best_den;
+    }
+    return err;
+}
+
+/**
+ * snd_interval_ratden - refine the interval value
+ *
+ * Returns non-zero if the value is changed, zero if not changed.
+ */
+int snd_interval_ratden(snd_interval_t *i,
+                        unsigned int rats_count, ratden_t *rats,
+                        unsigned int *nump, unsigned int *denp)
+{
+    unsigned int best_num, best_diff, best_den;
+    unsigned int k;
+    snd_interval_t t;
+    int err;
+
+    best_num = best_den = best_diff = 0;
+    for (k = 0; k < rats_count; ++k) {
+        unsigned int num;
+        unsigned int den = rats[k].den;
+        unsigned int q = i->min;
+        int diff;
+        num = mul(q, den);
+        if (num > rats[k].num_max)
+            continue;
+        if (num < rats[k].num_min)
+            num = rats[k].num_max;
+        else {
+            unsigned int r;
+            r = (num - rats[k].num_min) % rats[k].num_step;
+            if (r != 0)
+                num += rats[k].num_step - r;
+        }
+        diff = num - q * den;
+        if (best_num == 0 ||
+            diff * best_den < best_diff * den) {
+            best_diff = diff;
+            best_den = den;
+            best_num = num;
+        }
+    }
+    if (best_den == 0) {
+        i->empty = 1;
+        return -EINVAL;
+    }
+    t.min = div_down(best_num, best_den);
+    t.openmin = !!(best_num % best_den);
+
+    best_num = best_den = best_diff = 0;
+    for (k = 0; k < rats_count; ++k) {
+        unsigned int num;
+        unsigned int den = rats[k].den;
+        unsigned int q = i->max;
+        int diff;
+        num = mul(q, den);
+        if (num < rats[k].num_min)
+            continue;
+        if (num > rats[k].num_max)
+            num = rats[k].num_max;
+        else {
+            unsigned int r;
+            r = (num - rats[k].num_min) % rats[k].num_step;
+            if (r != 0)
+                num -= r;
+        }
+        diff = q * den - num;
+        if (best_num == 0 ||
+            diff * best_den < best_diff * den) {
+            best_diff = diff;
+            best_den = den;
+            best_num = num;
+        }
+    }
+    if (best_den == 0) {
+        i->empty = 1;
+        return -EINVAL;
+    }
+    t.max = div_up(best_num, best_den);
+    t.openmax = !!(best_num % best_den);
+    t.integer = 0;
+    err = snd_interval_refine(i, &t);
+    if (err < 0)
+        return err;
+
+    if (snd_interval_single(i)) {
+        if (nump)
+            *nump = best_num;
+        if (denp)
+            *denp = best_den;
+    }
+    return err;
+}
+
+/**
+ * snd_interval_list - refine the interval value from the list
+ * @i: the interval value to refine
+ * @count: the number of elements in the list
+ * @list: the value list
+ * @mask: the bit-mask to evaluate
+ *
+ * Refines the interval value from the list.
+ * When mask is non-zero, only the elements corresponding to bit 1 are
+ * evaluated.
+ *
+ * Returns non-zero if the value is changed, zero if not changed.
+ */
+int snd_interval_list(snd_interval_t *i, unsigned int count, unsigned int *list, unsigned int mask)
+{
+    unsigned int k;
+
+    int changed = 0;
+
+    for (k = 0; k < count; k++) {
+        if (mask && !(mask & (1 << k)))
+            continue;
+        if (i->min == list[k] && !i->openmin)
+            goto _l1;
+        if (i->min < list[k]) {
+            i->min = list[k];
+            i->openmin = 0;
+            changed = 1;
+            goto _l1;
+        }
+    }
+    i->empty = 1;
+    return -EINVAL;
+    _l1:
+        for (k = count; k-- > 0;) {
+            if (mask && !(mask & (1 << k)))
+                continue;
+            if (i->max == list[k] && !i->openmax)
+                goto _l2;
+            if (i->max > list[k]) {
+                i->max = list[k];
+                i->openmax = 0;
+                changed = 1;
+                goto _l2;
+            }
+        }
+        i->empty = 1;
+        return -EINVAL;
+        _l2:
+            if (snd_interval_checkempty(i)) {
+                i->empty = 1;
+                return -EINVAL;
+            }
+            return changed;
+}
+
+int snd_interval_step(snd_interval_t *i, unsigned int min, unsigned int step)
+{
+    unsigned int n;
+    int changed = 0;
+    n = (i->min - min) % step;
+    if (n != 0 || i->openmin) {
+        i->min += step - n;
+        changed = 1;
+    }
+    n = (i->max - min) % step;
+    if (n != 0 || i->openmax) {
+        i->max -= n;
+        changed = 1;
+    }
+    if (snd_interval_checkempty(i)) {
+        i->empty = 1;
+        return -EINVAL;
+    }
+    return changed;
+}
+
+/* Info constraints helpers */
+
+/**
+ * snd_pcm_hw_rule_add - add the hw-constraint rule
+ * @runtime: the pcm runtime instance
+ * @cond: condition bits
+ * @var: the variable to evaluate
+ * @func: the evaluation function
+ * @private: the private data pointer passed to function
+ * @dep: the dependent variables
+ *
+ * Returns zero if successful, or a negative error code on failure.
+ */
+int snd_pcm_hw_rule_add(snd_pcm_runtime_t *runtime, unsigned int cond,
+                        int var,
+                        snd_pcm_hw_rule_func_t func, void *private,
+                        int dep, ...)
+{
+    snd_pcm_hw_constraints_t *constrs = &runtime->hw_constraints;
+    snd_pcm_hw_rule_t *c;
+    unsigned int k;
+    va_list args;
+    va_start(args, dep);
+
+#ifdef DEBUG
+    //        dprintf(("snd_pcm_hw_rule_add. func() = %x, var = %d",func, var));
+#endif
+    if (constrs->rules_num >= constrs->rules_all) {
+        snd_pcm_hw_rule_t *new;
+        unsigned int new_rules = constrs->rules_all + 16;
+        new = kcalloc(new_rules, sizeof(*c), GFP_KERNEL);
+        if (!new)
+            return -ENOMEM;
+        if (constrs->rules) {
+            memcpy(new, constrs->rules,
+                   constrs->rules_num * sizeof(*c));
+            kfree(constrs->rules);
+        }
+        constrs->rules = new;
+        constrs->rules_all = new_rules;
+    }
+    c = &constrs->rules[constrs->rules_num];
+    c->cond = cond;
+    c->func = func;
+    c->var = var;
+    c->private = private;
+    k = 0;
+    while (1) {
+        snd_assert(k < sizeof(c->deps) / sizeof(c->deps[0]), return -EINVAL);
+        c->deps[k++] = dep;
+        if (dep < 0)
+            break;
+        dep = va_arg(args, int);
+    }
+    constrs->rules_num++;
+    va_end(args);
+    return 0;
+}
+
+/**
+ * snd_pcm_hw_constraint_mask
+ */
+int snd_pcm_hw_constraint_mask(snd_pcm_runtime_t *runtime, snd_pcm_hw_param_t var,
+                               u_int32_t mask)
+{
+    snd_pcm_hw_constraints_t *constrs = &runtime->hw_constraints;
+    snd_mask_t *maskp = constrs_mask(constrs, var);
+    *maskp->bits &= mask;
+    memset(maskp->bits + 1, 0, (SNDRV_MASK_MAX-32) / 8); /* clear rest */
+    if (*maskp->bits == 0)
+        return -EINVAL;
+    return 0;
+}
+
+/**
+ * snd_pcm_hw_constraint_mask64
+ */
+int snd_pcm_hw_constraint_mask64(snd_pcm_runtime_t *runtime, snd_pcm_hw_param_t var,
+                                 u_int64_t mask)
+{
+    snd_pcm_hw_constraints_t *constrs = &runtime->hw_constraints;
+    snd_mask_t *maskp = constrs_mask(constrs, var);
+    maskp->bits[0] &= (u_int32_t)mask;
+    maskp->bits[1] &= (u_int32_t)(mask >> 32);
+    memset(maskp->bits + 2, 0, (SNDRV_MASK_MAX-64) / 8); /* clear rest */
+    if (! maskp->bits[0] && ! maskp->bits[1])
+        return -EINVAL;
+    return 0;
+}
+
+/**
+ * snd_pcm_hw_constraint_integer
+ */
+int snd_pcm_hw_constraint_integer(snd_pcm_runtime_t *runtime, snd_pcm_hw_param_t var)
+{
+    snd_pcm_hw_constraints_t *constrs = &runtime->hw_constraints;
+    return snd_interval_setinteger(constrs_interval(constrs, var));
+}
+
+/**
+ * snd_pcm_hw_constraint_minmax
+ */
+int snd_pcm_hw_constraint_minmax(snd_pcm_runtime_t *runtime, snd_pcm_hw_param_t var,
+                                 unsigned int min, unsigned int max)
+{
+    snd_pcm_hw_constraints_t *constrs = &runtime->hw_constraints;
+    snd_interval_t t;
+    t.min = min;
+    t.max = max;
+    t.openmin = t.openmax = 0;
+    t.integer = 0;
+    return snd_interval_refine(constrs_interval(constrs, var), &t);
+}
+
+static int snd_pcm_hw_rule_list(snd_pcm_hw_params_t *params,
+                                snd_pcm_hw_rule_t *rule)
+{
+    snd_pcm_hw_constraint_list_t *list = rule->private;
+    return snd_interval_list(hw_param_interval(params, rule->var), list->count, list->list, list->mask);
+}
+
+/**
+ * snd_pcm_hw_constraint_list
+ */
+int snd_pcm_hw_constraint_list(snd_pcm_runtime_t *runtime,
+                               unsigned int cond,
+                               snd_pcm_hw_param_t var,
+                               snd_pcm_hw_constraint_list_t *l)
+{
+    return snd_pcm_hw_rule_add(runtime, cond, var,
+                               snd_pcm_hw_rule_list, l,
+                               var, -1);
+}
+
+static int snd_pcm_hw_rule_ratnums(snd_pcm_hw_params_t *params,
+                                   snd_pcm_hw_rule_t *rule)
+{
+    snd_pcm_hw_constraint_ratnums_t *r = rule->private;
+    unsigned int num = 0, den = 0;
+    int err;
+    err = snd_interval_ratnum(hw_param_interval(params, rule->var),
+                              r->nrats, r->rats, &num, &den);
+    if (err >= 0 && den && rule->var == SNDRV_PCM_HW_PARAM_RATE) {
+        params->rate_num = num;
+        params->rate_den = den;
+    }
+    return err;
+}
+
+/**
+ * snd_pcm_hw_constraint_ratnums
+ */
+int snd_pcm_hw_constraint_ratnums(snd_pcm_runtime_t *runtime,
+                                  unsigned int cond,
+                                  snd_pcm_hw_param_t var,
+                                  snd_pcm_hw_constraint_ratnums_t *r)
+{
+    return snd_pcm_hw_rule_add(runtime, cond, var,
+                               snd_pcm_hw_rule_ratnums, r,
+                               var, -1);
+}
+
+static int snd_pcm_hw_rule_ratdens(snd_pcm_hw_params_t *params,
+                                   snd_pcm_hw_rule_t *rule)
+{
+    snd_pcm_hw_constraint_ratdens_t *r = rule->private;
+    unsigned int num = 0, den = 0;
+    int err = snd_interval_ratden(hw_param_interval(params, rule->var),
+                                  r->nrats, r->rats, &num, &den);
+    if (err >= 0 && den && rule->var == SNDRV_PCM_HW_PARAM_RATE) {
+        params->rate_num = num;
+        params->rate_den = den;
+    }
+    return err;
+}
+
+/**
+ * snd_pcm_hw_constraint_ratdens
+ */
+int snd_pcm_hw_constraint_ratdens(snd_pcm_runtime_t *runtime,
+                                  unsigned int cond,
+                                  snd_pcm_hw_param_t var,
+                                  snd_pcm_hw_constraint_ratdens_t *r)
+{
+    return snd_pcm_hw_rule_add(runtime, cond, var,
+                               snd_pcm_hw_rule_ratdens, r,
+                               var, -1);
+}
+
+static int snd_pcm_hw_rule_msbits(snd_pcm_hw_params_t *params,
+                                  snd_pcm_hw_rule_t *rule)
+{
+    unsigned int l = (unsigned long) rule->private;
+    int width = l & 0xffff;
+    unsigned int msbits = l >> 16;
+    snd_interval_t *i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS);
+    if (snd_interval_single(i) && snd_interval_value(i) == width)
+        params->msbits = msbits;
+    return 0;
+}
+
+/**
+ * snd_pcm_hw_constraint_msbits
+ */
+int snd_pcm_hw_constraint_msbits(snd_pcm_runtime_t *runtime,
+                                 unsigned int cond,
+                                 unsigned int width,
+                                 unsigned int msbits)
+{
+    unsigned long l = (msbits << 16) | width;
+    return snd_pcm_hw_rule_add(runtime, cond, -1,
+                               snd_pcm_hw_rule_msbits,
+                               (void*) l,
+                               SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1);
+}
+
+static int snd_pcm_hw_rule_step(snd_pcm_hw_params_t *params,
+                                snd_pcm_hw_rule_t *rule)
+{
+    unsigned long step = (unsigned long) rule->private;
+    return snd_interval_step(hw_param_interval(params, rule->var), 0, step);
+}
+
+/**
+ * snd_pcm_hw_constraint_step
+ */
+int snd_pcm_hw_constraint_step(snd_pcm_runtime_t *runtime,
+                               unsigned int cond,
+                               snd_pcm_hw_param_t var,
+                               unsigned long step)
+{
+    return snd_pcm_hw_rule_add(runtime, cond, var,
+                               snd_pcm_hw_rule_step, (void *) step,
+                               var, -1);
+}
+
+static int snd_pcm_hw_rule_pow2(snd_pcm_hw_params_t *params, snd_pcm_hw_rule_t *rule)
+{
+    static unsigned int pow2_sizes[] = {
+        1<<0, 1<<1, 1<<2, 1<<3, 1<<4, 1<<5, 1<<6, 1<<7,
+        1<<8, 1<<9, 1<<10, 1<<11, 1<<12, 1<<13, 1<<14, 1<<15,
+        1<<16, 1<<17, 1<<18, 1<<19, 1<<20, 1<<21, 1<<22, 1<<23,
+        1<<24, 1<<25, 1<<26, 1<<27, 1<<28, 1<<29, 1<<30
+    };
+    return snd_interval_list(hw_param_interval(params, rule->var),
+                             sizeof(pow2_sizes)/sizeof(int), pow2_sizes, 0);
+}
+
+/**
+ * snd_pcm_hw_constraint_pow2
+ */
+int snd_pcm_hw_constraint_pow2(snd_pcm_runtime_t *runtime,
+                               unsigned int cond,
+                               snd_pcm_hw_param_t var)
+{
+    return snd_pcm_hw_rule_add(runtime, cond, var,
+                               snd_pcm_hw_rule_pow2, NULL,
+                               var, -1);
+}
+
+/* To use the same code we have in alsa-lib */
+#define snd_pcm_t snd_pcm_substream_t
+#define assert(i) snd_assert((i), return -EINVAL)
+#ifndef INT_MIN
+#define INT_MIN ((int)((unsigned int)INT_MAX+1))
+#endif
+
+void _snd_pcm_hw_param_any(snd_pcm_hw_params_t *params, snd_pcm_hw_param_t var)
+{
+    if (hw_is_mask(var)) {
+        snd_mask_any(hw_param_mask(params, var));
+        params->cmask |= 1 << var;
+        params->rmask |= 1 << var;
+        return;
+    }
+    if (hw_is_interval(var)) {
+        snd_interval_any(hw_param_interval(params, var));
+        params->cmask |= 1 << var;
+        params->rmask |= 1 << var;
+        return;
+    }
+    snd_BUG();
+}
+
+/**
+ * snd_pcm_hw_param_any
+ */
+int snd_pcm_hw_param_any(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
+                         snd_pcm_hw_param_t var)
+{
+    _snd_pcm_hw_param_any(params, var);
+    return snd_pcm_hw_refine(pcm, params);
+}
+
+void _snd_pcm_hw_params_any(snd_pcm_hw_params_t *params)
+{
+    unsigned int k;
+    memset(params, 0, sizeof(*params));
+    for (k = SNDRV_PCM_HW_PARAM_FIRST_MASK; k <= SNDRV_PCM_HW_PARAM_LAST_MASK; k++)
+        _snd_pcm_hw_param_any(params, k);
+    for (k = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++)
+        _snd_pcm_hw_param_any(params, k);
+    params->info = ~0U;
+}
+
+/**
+ * snd_pcm_hw_params_any
+ *
+ * Fill PARAMS with full configuration space boundaries
+ */
+int snd_pcm_hw_params_any(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
+{
+    _snd_pcm_hw_params_any(params);
+    return snd_pcm_hw_refine(pcm, params);
+}
+
+/**
+ * snd_pcm_hw_param_value
+ *
+ * Return the value for field PAR if it's fixed in configuration space
+ *  defined by PARAMS. Return -EINVAL otherwise
+ */
+int snd_pcm_hw_param_value(const snd_pcm_hw_params_t *params,
+                           snd_pcm_hw_param_t var, int *dir)
+{
+    if (hw_is_mask(var)) {
+        const snd_mask_t *mask = hw_param_mask_c(params, var);
+        if (!snd_mask_single(mask))
+            return -EINVAL;
+        if (dir)
+            *dir = 0;
+        return snd_mask_value(mask);
+    }
+    if (hw_is_interval(var)) {
+        const snd_interval_t *i = hw_param_interval_c(params, var);
+        if (!snd_interval_single(i))
+            return -EINVAL;
+        if (dir)
+            *dir = i->openmin;
+        return snd_interval_value(i);
+    }
+    assert(0);
+    return -EINVAL;
+}
+
+/**
+ * snd_pcm_hw_param_value_min
+ *
+ * Return the minimum value for field PAR.
+ */
+unsigned int snd_pcm_hw_param_value_min(const snd_pcm_hw_params_t *params,
+                                        snd_pcm_hw_param_t var, int *dir)
+{
+    if (hw_is_mask(var)) {
+        if (dir)
+            *dir = 0;
+        return snd_mask_min(hw_param_mask_c(params, var));
+    }
+    if (hw_is_interval(var)) {
+        const snd_interval_t *i = hw_param_interval_c(params, var);
+        if (dir)
+            *dir = i->openmin;
+        return snd_interval_min(i);
+    }
+    assert(0);
+    return -EINVAL;
+}
+
+/**
+ * snd_pcm_hw_param_value_max
+ *
+ * Return the maximum value for field PAR.
+ */
+unsigned int snd_pcm_hw_param_value_max(const snd_pcm_hw_params_t *params,
+                                        snd_pcm_hw_param_t var, int *dir)
+{
+    if (hw_is_mask(var)) {
+        if (dir)
+            *dir = 0;
+        return snd_mask_max(hw_param_mask_c(params, var));
+    }
+    if (hw_is_interval(var)) {
+        const snd_interval_t *i = hw_param_interval_c(params, var);
+        if (dir)
+            *dir = - (int) i->openmax;
+        return snd_interval_max(i);
+    }
+    assert(0);
+    return -EINVAL;
+}
+
+void _snd_pcm_hw_param_setempty(snd_pcm_hw_params_t *params,
+                                snd_pcm_hw_param_t var)
+{
+    if (hw_is_mask(var)) {
+        snd_mask_none(hw_param_mask(params, var));
+        params->cmask |= 1 << var;
+        params->rmask |= 1 << var;
+    } else if (hw_is_interval(var)) {
+        snd_interval_none(hw_param_interval(params, var));
+        params->cmask |= 1 << var;
+        params->rmask |= 1 << var;
+    } else {
+        snd_BUG();
+    }
+}
+
+int _snd_pcm_hw_param_setinteger(snd_pcm_hw_params_t *params,
+                                 snd_pcm_hw_param_t var)
+{
+    int changed;
+    assert(hw_is_interval(var));
+    changed = snd_interval_setinteger(hw_param_interval(params, var));
+    if (changed) {
+        params->cmask |= 1 << var;
+        params->rmask |= 1 << var;
+    }
+    return changed;
+}
+
+/**
+ * snd_pcm_hw_param_setinteger
+ *
+ * Inside configuration space defined by PARAMS remove from PAR all
+ * non integer values. Reduce configuration space accordingly.
+ * Return -EINVAL if the configuration space is empty
+ */
+int snd_pcm_hw_param_setinteger(snd_pcm_t *pcm,
+                                snd_pcm_hw_params_t *params,
+                                snd_pcm_hw_param_t var)
+{
+    int changed = _snd_pcm_hw_param_setinteger(params, var);
+    if (changed < 0)
+        return changed;
+    if (params->rmask) {
+        int err = snd_pcm_hw_refine(pcm, params);
+        if (err < 0)
+            return err;
+    }
+    return 0;
+}
+
+int _snd_pcm_hw_param_first(snd_pcm_hw_params_t *params,
+                            snd_pcm_hw_param_t var)
+{
+    int changed;
+    if (hw_is_mask(var))
+        changed = snd_mask_refine_first(hw_param_mask(params, var));
+    else if (hw_is_interval(var))
+        changed = snd_interval_refine_first(hw_param_interval(params, var));
+    else {
+        assert(0);
+        return -EINVAL;
+    }
+    if (changed) {
+        params->cmask |= 1 << var;
+        params->rmask |= 1 << var;
+    }
+    return changed;
+}
+
+
+/**
+ * snd_pcm_hw_param_first
+ *
+ * Inside configuration space defined by PARAMS remove from PAR all
+ * values > minimum. Reduce configuration space accordingly.
+ * Return the minimum.
+ */
+int snd_pcm_hw_param_first(snd_pcm_t *pcm,
+                           snd_pcm_hw_params_t *params,
+                           snd_pcm_hw_param_t var, int *dir)
+{
+    int changed = _snd_pcm_hw_param_first(params, var);
+    if (changed < 0)
+        return changed;
+    if (params->rmask) {
+        int err = snd_pcm_hw_refine(pcm, params);
+        assert(err >= 0);
+    }
+    return snd_pcm_hw_param_value(params, var, dir);
+}
+
+int _snd_pcm_hw_param_last(snd_pcm_hw_params_t *params,
+                           snd_pcm_hw_param_t var)
+{
+    int changed;
+    if (hw_is_mask(var))
+        changed = snd_mask_refine_last(hw_param_mask(params, var));
+    else if (hw_is_interval(var))
+        changed = snd_interval_refine_last(hw_param_interval(params, var));
+    else {
+        assert(0);
+        return -EINVAL;
+    }
+    if (changed) {
+        params->cmask |= 1 << var;
+        params->rmask |= 1 << var;
+    }
+    return changed;
+}
+
+
+/**
+ * snd_pcm_hw_param_last
+ *
+ * Inside configuration space defined by PARAMS remove from PAR all
+ * values < maximum. Reduce configuration space accordingly.
+ * Return the maximum.
+ */
+int snd_pcm_hw_param_last(snd_pcm_t *pcm,
+                          snd_pcm_hw_params_t *params,
+                          snd_pcm_hw_param_t var, int *dir)
+{
+    int changed = _snd_pcm_hw_param_last(params, var);
+    if (changed < 0)
+        return changed;
+    if (params->rmask) {
+        int err = snd_pcm_hw_refine(pcm, params);
+        assert(err >= 0);
+    }
+    return snd_pcm_hw_param_value(params, var, dir);
+}
+
+int _snd_pcm_hw_param_min(snd_pcm_hw_params_t *params,
+                          snd_pcm_hw_param_t var, unsigned int val, int dir)
+{
+    int changed;
+    int open = 0;
+    if (dir) {
+        if (dir > 0) {
+            open = 1;
+        } else if (dir < 0) {
+            if (val > 0) {
+                open = 1;
+                val--;
+            }
+        }
+    }
+    if (hw_is_mask(var))
+        changed = snd_mask_refine_min(hw_param_mask(params, var), val + !!open);
+    else if (hw_is_interval(var))
+        changed = snd_interval_refine_min(hw_param_interval(params, var), val, open);
+    else {
+        assert(0);
+        return -EINVAL;
+    }
+    if (changed) {
+        params->cmask |= 1 << var;
+        params->rmask |= 1 << var;
+    }
+    return changed;
+}
+
+/**
+ * snd_pcm_hw_param_min
+ *
+ * Inside configuration space defined by PARAMS remove from PAR all
+ * values < VAL. Reduce configuration space accordingly.
+ * Return new minimum or -EINVAL if the configuration space is empty
+ */
+int snd_pcm_hw_param_min(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
+                         snd_pcm_hw_param_t var, unsigned int val, int *dir)
+{
+    int changed = _snd_pcm_hw_param_min(params, var, val, dir ? *dir : 0);
+    if (changed < 0)
+        return changed;
+    if (params->rmask) {
+        int err = snd_pcm_hw_refine(pcm, params);
+        if (err < 0)
+            return err;
+    }
+    return snd_pcm_hw_param_value_min(params, var, dir);
+}
+
+int _snd_pcm_hw_param_max(snd_pcm_hw_params_t *params,
+                          snd_pcm_hw_param_t var, unsigned int val, int dir)
+{
+    int changed;
+    int open = 0;
+    if (dir) {
+        if (dir < 0) {
+            open = 1;
+        } else if (dir > 0) {
+            open = 1;
+            val++;
+        }
+    }
+    if (hw_is_mask(var)) {
+        if (val == 0 && open) {
+            snd_mask_none(hw_param_mask(params, var));
+            changed = -EINVAL;
+        } else
+            changed = snd_mask_refine_max(hw_param_mask(params, var), val - !!open);
+    } else if (hw_is_interval(var))
+        changed = snd_interval_refine_max(hw_param_interval(params, var), val, open);
+    else {
+        assert(0);
+        return -EINVAL;
+    }
+    if (changed) {
+        params->cmask |= 1 << var;
+        params->rmask |= 1 << var;
+    }
+    return changed;
+}
+
+/**
+ * snd_pcm_hw_param_max
+ *
+ * Inside configuration space defined by PARAMS remove from PAR all
+ *  values >= VAL + 1. Reduce configuration space accordingly.
+ *  Return new maximum or -EINVAL if the configuration space is empty
+ */
+int snd_pcm_hw_param_max(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
+                         snd_pcm_hw_param_t var, unsigned int val, int *dir)
+{
+    int changed = _snd_pcm_hw_param_max(params, var, val, dir ? *dir : 0);
+    if (changed < 0)
+        return changed;
+    if (params->rmask) {
+        int err = snd_pcm_hw_refine(pcm, params);
+        if (err < 0)
+            return err;
+    }
+    return snd_pcm_hw_param_value_max(params, var, dir);
+}
+
+int _snd_pcm_hw_param_set(snd_pcm_hw_params_t *params,
+                          snd_pcm_hw_param_t var, unsigned int val, int dir)
+{
+    int changed;
+    if (hw_is_mask(var)) {
+        snd_mask_t *m = hw_param_mask(params, var);
+        if (val == 0 && dir < 0) {
+            changed = -EINVAL;
+            snd_mask_none(m);
+        } else {
+            if (dir > 0)
+                val++;
+            else if (dir < 0)
+                val--;
+            changed = snd_mask_refine_set(hw_param_mask(params, var), val);
+        }
+    } else if (hw_is_interval(var)) {
+        snd_interval_t *i = hw_param_interval(params, var);
+        if (val == 0 && dir < 0) {
+            changed = -EINVAL;
+            snd_interval_none(i);
+        } else if (dir == 0)
+            changed = snd_interval_refine_set(i, val);
+        else {
+            snd_interval_t t;
+            t.openmin = 1;
+            t.openmax = 1;
+            t.empty = 0;
+            t.integer = 0;
+            if (dir < 0) {
+                t.min = val - 1;
+                t.max = val;
+            } else {
+                t.min = val;
+                t.max = val+1;
+            }
+            changed = snd_interval_refine(i, &t);
+        }
+    } else {
+        assert(0);
+        return -EINVAL;
+    }
+    if (changed) {
+        params->cmask |= 1 << var;
+        params->rmask |= 1 << var;
+    }
+    return changed;
+}
+
+/**
+ * snd_pcm_hw_param_set
+ *
+ * Inside configuration space defined by PARAMS remove from PAR all
+ * values != VAL. Reduce configuration space accordingly.
+ *  Return VAL or -EINVAL if the configuration space is empty
+ */
+int snd_pcm_hw_param_set(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
+                         snd_pcm_hw_param_t var, unsigned int val, int dir)
+{
+    int changed = _snd_pcm_hw_param_set(params, var, val, dir);
+    if (changed < 0)
+        return changed;
+    if (params->rmask) {
+        int err = snd_pcm_hw_refine(pcm, params);
+        if (err < 0)
+            return err;
+    }
+    return snd_pcm_hw_param_value(params, var, 0);
+}
+
+static int _snd_pcm_hw_param_mask(snd_pcm_hw_params_t *params,
+                           snd_pcm_hw_param_t var, const snd_mask_t *val)
+{
+    int changed;
+    assert(hw_is_mask(var));
+    changed = snd_mask_refine(hw_param_mask(params, var), val);
+    if (changed) {
+        params->cmask |= 1 << var;
+        params->rmask |= 1 << var;
+    }
+    return changed;
+}
+
+/**
+ * snd_pcm_hw_param_mask
+ *
+ * Inside configuration space defined by PARAMS remove from PAR all values
+ * not contained in MASK. Reduce configuration space accordingly.
+ * This function can be called only for SNDRV_PCM_HW_PARAM_ACCESS,
+ * SNDRV_PCM_HW_PARAM_FORMAT, SNDRV_PCM_HW_PARAM_SUBFORMAT.
+ * Return 0 on success or -EINVAL
+ * if the configuration space is empty
+ */
+int snd_pcm_hw_param_mask(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
+                          snd_pcm_hw_param_t var, const snd_mask_t *val)
+{
+    int changed = _snd_pcm_hw_param_mask(params, var, val);
+    if (changed < 0)
+        return changed;
+    if (params->rmask) {
+        int err = snd_pcm_hw_refine(pcm, params);
+        if (err < 0)
+            return err;
+    }
+    return 0;
+}
+
+static int boundary_sub(int a, int adir,
+                        int b, int bdir,
+                        int *c, int *cdir)
+{
+    adir = adir < 0 ? -1 : (adir > 0 ? 1 : 0);
+    bdir = bdir < 0 ? -1 : (bdir > 0 ? 1 : 0);
+    *c = a - b;
+    *cdir = adir - bdir;
+    if (*cdir == -2) {
+        assert(*c > INT_MIN);
+        (*c)--;
+    } else if (*cdir == 2) {
+        assert(*c < INT_MAX);
+        (*c)++;
+    }
+    return 0;
+}
+
+static int boundary_lt(unsigned int a, int adir,
+                       unsigned int b, int bdir)
+{
+    assert(a > 0 || adir >= 0);
+    assert(b > 0 || bdir >= 0);
+    if (adir < 0) {
+        a--;
+        adir = 1;
+    } else if (adir > 0)
+        adir = 1;
+    if (bdir < 0) {
+        b--;
+        bdir = 1;
+    } else if (bdir > 0)
+        bdir = 1;
+    return a < b || (a == b && adir < bdir);
+}
+
+/* Return 1 if min is nearer to best than max */
+static int boundary_nearer(int min, int mindir,
+                           int best, int bestdir,
+                           int max, int maxdir)
+{
+    int dmin, dmindir;
+    int dmax, dmaxdir;
+    boundary_sub(best, bestdir, min, mindir, &dmin, &dmindir);
+    boundary_sub(max, maxdir, best, bestdir, &dmax, &dmaxdir);
+    return boundary_lt(dmin, dmindir, dmax, dmaxdir);
+}
+
+/**
+ * snd_pcm_hw_param_near
+ *
+ * Inside configuration space defined by PARAMS set PAR to the available value
+ * nearest to VAL. Reduce configuration space accordingly.
+ * This function cannot be called for SNDRV_PCM_HW_PARAM_ACCESS,
+ * SNDRV_PCM_HW_PARAM_FORMAT, SNDRV_PCM_HW_PARAM_SUBFORMAT.
+ * Return the value found.
+ */
+int snd_pcm_hw_param_near(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
+                          snd_pcm_hw_param_t var, unsigned int best, int *dir)
+{
+    snd_pcm_hw_params_t *save = NULL;
+    int v;
+    unsigned int saved_min;
+    int last = 0;
+    int min, max;
+    int mindir, maxdir;
+    int valdir = dir ? *dir : 0;
+    /* FIXME */
+    if (best > INT_MAX)
+        best = INT_MAX;
+    min = max = best;
+    mindir = maxdir = valdir;
+    if (maxdir > 0)
+        maxdir = 0;
+    else if (maxdir == 0)
+        maxdir = -1;
+    else {
+        maxdir = 1;
+        max--;
+    }
+    save = kmalloc(sizeof(*save), GFP_KERNEL);
+    if (save == NULL)
+        return -ENOMEM;
+    *save = *params;
+    saved_min = min;
+    min = snd_pcm_hw_param_min(pcm, params, var, min, &mindir);
+    if (min >= 0) {
+        snd_pcm_hw_params_t *params1;
+        if (max < 0)
+            goto _end;
+        if ((unsigned int)min == saved_min && mindir == valdir)
+            goto _end;
+        params1 = kmalloc(sizeof(*params1), GFP_KERNEL);
+        if (params1 == NULL) {
+            kfree(save);
+            return -ENOMEM;
+        }
+        *params1 = *save;
+        max = snd_pcm_hw_param_max(pcm, params1, var, max, &maxdir);
+        if (max < 0) {
+            kfree(params1);
+            goto _end;
+        }
+        if (boundary_nearer(max, maxdir, best, valdir, min, mindir)) {
+            *params = *params1;
+            last = 1;
+        }
+        kfree(params1);
+    } else {
+        *params = *save;
+        max = snd_pcm_hw_param_max(pcm, params, var, max, &maxdir);
+        assert(max >= 0);
+        last = 1;
+    }
+    _end:
+    kfree(save);
+    if (last)
+        v = snd_pcm_hw_param_last(pcm, params, var, dir);
+    else
+        v = snd_pcm_hw_param_first(pcm, params, var, dir);
+    assert(v >= 0);
+    return v;
+}
+
+/**
+ * snd_pcm_hw_param_choose
+ *
+ * Choose one configuration from configuration space defined by PARAMS
+ * The configuration chosen is that obtained fixing in this order:
+ * first access, first format, first subformat, min channels,
+ * min rate, min period time, max buffer size, min tick time
+ */
+int snd_pcm_hw_params_choose(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
+{
+    int err;
+
+    err = snd_pcm_hw_param_first(pcm, params, SNDRV_PCM_HW_PARAM_ACCESS, 0);
+    assert(err >= 0);
+
+    err = snd_pcm_hw_param_first(pcm, params, SNDRV_PCM_HW_PARAM_FORMAT, 0);
+    assert(err >= 0);
+
+    err = snd_pcm_hw_param_first(pcm, params, SNDRV_PCM_HW_PARAM_SUBFORMAT, 0);
+    assert(err >= 0);
+
+    err = snd_pcm_hw_param_first(pcm, params, SNDRV_PCM_HW_PARAM_CHANNELS, 0);
+    assert(err >= 0);
+
+    err = snd_pcm_hw_param_first(pcm, params, SNDRV_PCM_HW_PARAM_RATE, 0);
+    assert(err >= 0);
+
+    //#ifndef TARGET_OS2
+    err = snd_pcm_hw_param_first(pcm, params, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 0);
+    assert(err >= 0);
+    //#endif
+
+    err = snd_pcm_hw_param_last(pcm, params, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0);
+    assert(err >= 0);
+
+    err = snd_pcm_hw_param_first(pcm, params, SNDRV_PCM_HW_PARAM_TICK_TIME, 0);
+    assert(err >= 0);
+
+    return 0;
+}
+
+#undef snd_pcm_t
+#undef assert
+
+static int snd_pcm_lib_ioctl_reset(snd_pcm_substream_t *substream,
+                                   void *arg)
+{
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    unsigned long flags;
+    snd_pcm_stream_lock_irqsave(substream, flags);
+    if (snd_pcm_running(substream) &&
+        snd_pcm_update_hw_ptr(substream) >= 0)
+        runtime->status->hw_ptr %= runtime->buffer_size;
+    else
+        runtime->status->hw_ptr = 0;
+    snd_pcm_stream_unlock_irqrestore(substream, flags);
+    return 0;
+}
+
+static int snd_pcm_lib_ioctl_channel_info(snd_pcm_substream_t *substream,
+                                          void *arg)
+{
+    snd_pcm_channel_info_t *info = arg;
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    int width;
+    if (!(runtime->info & SNDRV_PCM_INFO_MMAP)) {
+        info->offset = -1;
+        return 0;
+    }
+    width = snd_pcm_format_physical_width(runtime->format);
+    if (width < 0)
+        return width;
+    info->offset = 0;
+    switch (runtime->access) {
+    case SNDRV_PCM_ACCESS_MMAP_INTERLEAVED:
+    case SNDRV_PCM_ACCESS_RW_INTERLEAVED:
+        info->first = info->channel * width;
+        info->step = runtime->channels * width;
+        break;
+    case SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED:
+    case SNDRV_PCM_ACCESS_RW_NONINTERLEAVED:
+        {
+            size_t size = runtime->dma_bytes / runtime->channels;
+            info->first = info->channel * size * 8;
+            info->step = width;
+            break;
+        }
+    default:
+        snd_BUG();
+        break;
+    }
+    return 0;
+}
+
+/**
+ * snd_pcm_lib_ioctl - a generic PCM ioctl callback
+ * @substream: the pcm substream instance
+ * @cmd: ioctl command
+ * @arg: ioctl argument
+ *
+ * Processes the generic ioctl commands for PCM.
+ * Can be passed as the ioctl callback for PCM ops.
+ *
+ * Returns zero if successful, or a negative error code on failure.
+ */
+int snd_pcm_lib_ioctl(snd_pcm_substream_t *substream,
+                      unsigned int cmd, void *arg)
+{
+    switch (cmd) {
+    case SNDRV_PCM_IOCTL1_INFO:
+        return 0;
+    case SNDRV_PCM_IOCTL1_RESET:
+        return snd_pcm_lib_ioctl_reset(substream, arg);
+    case SNDRV_PCM_IOCTL1_CHANNEL_INFO:
+        return snd_pcm_lib_ioctl_channel_info(substream, arg);
+    }
+    return -ENXIO;
+}
+
+static void snd_pcm_system_tick_set(snd_pcm_substream_t *substream,
+                                    unsigned long ticks)
+{
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    if (ticks == 0)
+        del_timer(&runtime->tick_timer);
+    else {
+        ticks += (1000000 / HZ) - 1;
+        ticks /= (1000000 / HZ);
+        mod_timer(&runtime->tick_timer, jiffies + ticks);
+    }
+}
+
+/* Temporary alias */
+void snd_pcm_tick_set(snd_pcm_substream_t *substream, unsigned long ticks)
+{
+    snd_pcm_system_tick_set(substream, ticks);
+}
+
+void snd_pcm_tick_prepare(snd_pcm_substream_t *substream)
+{
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    snd_pcm_uframes_t frames = ULONG_MAX;
+    snd_pcm_uframes_t avail, dist;
+    unsigned int ticks;
+    u_int64_t n;
+    u_int32_t r;
+    if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+        if (runtime->silence_size >= runtime->boundary) {
+            frames = 1;
+        } else if (runtime->silence_size > 0 &&
+                   runtime->silence_filled < runtime->buffer_size) {
+            snd_pcm_sframes_t noise_dist;
+            noise_dist = snd_pcm_playback_hw_avail(runtime) + runtime->silence_filled;
+            snd_assert(noise_dist <= (snd_pcm_sframes_t)runtime->silence_threshold, );
+            frames = noise_dist - runtime->silence_threshold;
+        }
+        avail = snd_pcm_playback_avail(runtime);
+    } else {
+        avail = snd_pcm_capture_avail(runtime);
+    }
+    if (avail < runtime->control->avail_min) {
+        snd_pcm_sframes_t n = runtime->control->avail_min - avail;
+        if (n > 0 && frames > (snd_pcm_uframes_t)n)
+            frames = n;
+    }
+    if (avail < runtime->buffer_size) {
+        snd_pcm_sframes_t n = runtime->buffer_size - avail;
+        if (n > 0 && frames > (snd_pcm_uframes_t)n)
+            frames = n;
+    }
+    if (frames == ULONG_MAX) {
+        snd_pcm_tick_set(substream, 0);
+        return;
+    }
+    dist = runtime->status->hw_ptr - runtime->hw_ptr_base;
+    /* Distance to next interrupt */
+    dist = runtime->period_size - dist % runtime->period_size;
+    if (dist <= frames) {
+        snd_pcm_tick_set(substream, 0);
+        return;
+    }
+    /* the base time is us */
+    n = frames;
+    n *= 1000000;
+    div64_32(&n, runtime->tick_time * runtime->rate, &r);
+    ticks = n + (r > 0 ? 1 : 0);
+    if (ticks < runtime->sleep_min)
+        ticks = runtime->sleep_min;
+    snd_pcm_tick_set(substream, (unsigned long) ticks);
+}
+
+void snd_pcm_tick_elapsed(snd_pcm_substream_t *substream)
+{
+    snd_pcm_runtime_t *runtime;
+    unsigned long flags;
+
+    snd_assert(substream != NULL, return);
+    runtime = substream->runtime;
+    snd_assert(runtime != NULL, return);
+
+    snd_pcm_stream_lock_irqsave(substream, flags);
+    if (!snd_pcm_running(substream) ||
+        snd_pcm_update_hw_ptr(substream) < 0)
+        goto _end;
+    if (runtime->sleep_min)
+        snd_pcm_tick_prepare(substream);
+    _end:
+        snd_pcm_stream_unlock_irqrestore(substream, flags);
+}
+
+/**
+ * snd_pcm_period_elapsed - update the pcm status for the next period
+ * @substream: the pcm substream instance
+ *
+ * This function is called from the interrupt handler when the
+ * PCM has processed the period size.  It will update the current
+ * pointer, set up the tick, wake up sleepers, etc.
+ *
+ * Even if more than one periods have elapsed since the last call, you
+ * have to call this only once.
+ */
+void snd_pcm_period_elapsed(snd_pcm_substream_t *substream)
+{
+    snd_pcm_runtime_t *runtime;
+    unsigned long flags;
+    int i;
+
+    snd_assert(substream != NULL, return);
+    runtime = substream->runtime;
+    snd_assert(runtime != NULL, return);
+
+    if (runtime->transfer_ack_begin)
+        runtime->transfer_ack_begin(substream);
+
+    snd_pcm_stream_lock_irqsave(substream, flags);
+    if (!snd_pcm_running(substream) ||
+        snd_pcm_update_hw_ptr_interrupt(substream) < 0)
+        goto _end;
+
+    for (i=0; i < 8*256; i++)
+    {
+        if (substream_int[i] == 0)
+        {
+            substream_int[i] = substream; // interrupted substream
+            //                printk("added %x to %i\n", substream, i);
+            break;
+        }
+        if (substream_int[i] == substream)
+        {
+            //                printk("already exist %x at %i\n", substream, i);
+            break;
+        }
+    }
+
+    if (substream->timer_running)
+        snd_timer_interrupt(substream->timer, 1);
+    if (runtime->sleep_min)
+        snd_pcm_tick_prepare(substream);
+    _end:
+        snd_pcm_stream_unlock_irqrestore(substream, flags);
+        if (runtime->transfer_ack_end)
+            runtime->transfer_ack_end(substream);
+        kill_fasync(runtime->fasync, SIGIO, POLL_IN);
+}
+
+static int snd_pcm_lib_write_transfer(snd_pcm_substream_t *substream,
+                                      unsigned int hwoff,
+                                      void *data, unsigned int off,
+                                      snd_pcm_uframes_t frames)
+{
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    int err;
+    char *buf = (char *) data + frames_to_bytes(runtime, off);
+#ifdef DEBUG_PK
+    printk("snd_pcm_lib_write_transfer hwoff %i, off %i, frames %i\n", hwoff, off, frames);
+#endif
+    if (substream->ops->copy) {
+        if ((err = substream->ops->copy(substream, -1, hwoff, buf, frames)) < 0)
+            return err;
+    } else {
+        char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff);
+        snd_assert(runtime->dma_area, return -EFAULT);
+        if (copy_from_user(hwbuf, buf, frames_to_bytes(runtime, frames)))
+            return -EFAULT;
+    }
+#ifdef DEBUG_PK
+    printk("snd_pcm_lib_write_transfer end\n");
+#endif
+    return 0;
+}
+
+typedef int (*transfer_f)(snd_pcm_substream_t *substream, unsigned int hwoff,
+                          void *data, unsigned int off, snd_pcm_uframes_t size);
+
+static snd_pcm_sframes_t snd_pcm_lib_write1(snd_pcm_substream_t *substream,
+                                            const void *data, snd_pcm_uframes_t size,
+                                            int nonblock,
+                                            transfer_f transfer)
+{
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    snd_pcm_uframes_t xfer = 0;
+    snd_pcm_uframes_t offset = 0;
+    int err = 0;
+
+#ifdef DEBUG_PK
+    printk("snd_pcm_lib_write1. align: %i, size %i\n",runtime->xfer_align, size);
+#endif
+    if (size == 0)
+        return 0;
+    if (size > runtime->xfer_align)
+        size -= size % runtime->xfer_align;
+
+    snd_pcm_stream_lock_irq(substream);
+    switch (runtime->status->state) {
+    case SNDRV_PCM_STATE_PREPARED:
+    case SNDRV_PCM_STATE_RUNNING:
+    case SNDRV_PCM_STATE_PAUSED:
+        break;
+    case SNDRV_PCM_STATE_XRUN:
+        err = -EPIPE;
+        goto _end_unlock;
+    case SNDRV_PCM_STATE_SUSPENDED:
+        err = -ESTRPIPE;
+        goto _end_unlock;
+    default:
+        err = -EBADFD;
+        goto _end_unlock;
+    }
+
+    while (size > 0) {
+        snd_pcm_uframes_t frames, appl_ptr, appl_ofs;
+        snd_pcm_uframes_t avail;
+        snd_pcm_uframes_t cont;
+        if (runtime->sleep_min == 0 && runtime->status->state == SNDRV_PCM_STATE_RUNNING)
+            snd_pcm_update_hw_ptr(substream);
+        avail = snd_pcm_playback_avail(runtime);
+        if (((avail < runtime->control->avail_min && size > avail) ||
+             (size >= runtime->xfer_align && avail < runtime->xfer_align))) {
+            wait_queue_t wait;
+            enum { READY, SIGNALED, ERROR, SUSPENDED, EXPIRED, DROPPED } state;
+            long tout;
+
+            if (nonblock) {
+                err = -EAGAIN;
+                //                            printk("av: %i, sz: %i, avmin: %i, xfer al: %i\n",
+                //                                   avail, size, runtime->control->avail_min, runtime->xfer_align);
+                goto _end_unlock;
+            }
+
+            init_waitqueue_entry(&wait, current);
+            add_wait_queue(&runtime->sleep, &wait);
+            while (1) {
+                if (signal_pending(current)) {
+                    state = SIGNALED;
+                    break;
+                }
+                set_current_state(TASK_INTERRUPTIBLE);
+                snd_pcm_stream_unlock_irq(substream);
+                tout = schedule_timeout(10 * HZ);
+                snd_pcm_stream_lock_irq(substream);
+                if (tout == 0) {
+                    if (runtime->status->state != SNDRV_PCM_STATE_PREPARED &&
+                        runtime->status->state != SNDRV_PCM_STATE_PAUSED) {
+                        state = runtime->status->state == SNDRV_PCM_STATE_SUSPENDED ? SUSPENDED : EXPIRED;
+                        break;
+                    }
+                }
+                switch (runtime->status->state) {
+                case SNDRV_PCM_STATE_XRUN:
+                case SNDRV_PCM_STATE_DRAINING:
+                    state = ERROR;
+                    goto _end_loop;
+                case SNDRV_PCM_STATE_SUSPENDED:
+                    state = SUSPENDED;
+                    goto _end_loop;
+                case SNDRV_PCM_STATE_SETUP:
+                    state = DROPPED;
+                    goto _end_loop;
+                default:
+                    break;
+                }
+                avail = snd_pcm_playback_avail(runtime);
+                if (avail >= runtime->control->avail_min) {
+                    state = READY;
+                    break;
+                }
+            }
+        _end_loop:
+            remove_wait_queue(&runtime->sleep, &wait);
+
+            switch (state) {
+            case ERROR:
+                err = -EPIPE;
+                goto _end_unlock;
+            case SUSPENDED:
+                err = -ESTRPIPE;
+                goto _end_unlock;
+            case SIGNALED:
+                err = -ERESTARTSYS;
+                goto _end_unlock;
+            case EXPIRED:
+                snd_printd("playback write error (DMA or IRQ trouble?)\n");
+                err = -EIO;
+                goto _end_unlock;
+            case DROPPED:
+                err = -EBADFD;
+                goto _end_unlock;
+            default:
+                break;
+            }
+        }
+        if (avail > runtime->xfer_align)
+            avail -= avail % runtime->xfer_align;
+        frames = size > avail ? avail : size;
+        cont = runtime->buffer_size - runtime->control->appl_ptr % runtime->buffer_size;
+        if (frames > cont)
+            frames = cont;
+        snd_assert(frames != 0, snd_pcm_stream_unlock_irq(substream); return -EINVAL);
+        appl_ptr = runtime->control->appl_ptr;
+        appl_ofs = appl_ptr % runtime->buffer_size;
+        snd_pcm_stream_unlock_irq(substream);
+        if ((err = transfer(substream, appl_ofs, (void *)data, offset, frames)) < 0)
+        {
+            printk("err: %i, appl ofs %i, off: %i, frames: %i\n", err, appl_ofs, offset, frames);
+            goto _end;
+        }
+        snd_pcm_stream_lock_irq(substream);
+        switch (runtime->status->state) {
+        case SNDRV_PCM_STATE_XRUN:
+            err = -EPIPE;
+            goto _end_unlock;
+        case SNDRV_PCM_STATE_SUSPENDED:
+            err = -ESTRPIPE;
+            goto _end_unlock;
+        default:
+            break;
+        }
+        appl_ptr += frames;
+        if (appl_ptr >= runtime->boundary)
+            appl_ptr -= runtime->boundary;
+        runtime->control->appl_ptr = appl_ptr;
+        if (substream->ops->ack)
+            substream->ops->ack(substream);
+
+        offset += frames;
+        size -= frames;
+        xfer += frames;
+        if (runtime->status->state == SNDRV_PCM_STATE_PREPARED &&
+            snd_pcm_playback_hw_avail(runtime) >= (snd_pcm_sframes_t)runtime->start_threshold) {
+            err = snd_pcm_start(substream);
+            if (err < 0)
+            {
+                printk("snd_pcm_start err: %i\n", err);
+                goto _end_unlock;
+            }
+        }
+        if (runtime->sleep_min &&
+            runtime->status->state == SNDRV_PCM_STATE_RUNNING)
+            snd_pcm_tick_prepare(substream);
+    }
+    _end_unlock:
+    snd_pcm_stream_unlock_irq(substream);
+    _end:
+#ifdef DEBUG_PK
+        printk("snd_pcm_lib_write1 end\n");
+#endif
+        return xfer > 0 ? (snd_pcm_sframes_t)xfer : err;
+}
+
+snd_pcm_sframes_t snd_pcm_lib_write(snd_pcm_substream_t *substream, const void *buf, snd_pcm_uframes_t size)
+{
+    snd_pcm_runtime_t *runtime;
+    int nonblock;
+
+#ifdef DEBUG_PK
+    printk("snd_pcm_lib_write substream: %x, buf: %x, size: %d\n",substream, buf, size);
+#endif
+    snd_assert(substream != NULL, return -ENXIO);
+    runtime = substream->runtime;
+    snd_assert(runtime != NULL, return -ENXIO);
+    snd_assert(substream->ops->copy != NULL || runtime->dma_area != NULL, return -EINVAL);
+    if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+        return -EBADFD;
+
+    snd_assert(substream->ffile != NULL, return -ENXIO);
+    nonblock = !!(substream->ffile->f_flags & O_NONBLOCK);
+#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
+    if (substream->oss.oss) {
+        snd_pcm_oss_setup_t *setup = substream->oss.setup;
+        if (setup != NULL) {
+            if (setup->nonblock)
+                nonblock = 1;
+            else if (setup->block)
+                nonblock = 0;
+        }
+    }
+#endif
+
+    if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED &&
+        runtime->channels > 1)
+    {
+        printk("acc: %i, ch: %i\n",runtime->access,runtime->channels);
+        return -EINVAL;
+    }
+#ifdef DEBUG_PK
+    printk("snd_pcm_lib_write end \n");
+#endif
+    return snd_pcm_lib_write1(substream, buf, size, nonblock,
+                              snd_pcm_lib_write_transfer);
+}
+
+static int snd_pcm_lib_writev_transfer(snd_pcm_substream_t *substream,
+                                       unsigned int hwoff,
+                                       void *data, unsigned int off,
+                                       snd_pcm_uframes_t frames)
+{
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    int err;
+    void **bufs = data;
+    int channels = runtime->channels;
+    int c;
+    if (substream->ops->copy) {
+        snd_assert(substream->ops->silence != NULL, return -EINVAL);
+        for (c = 0; c < channels; ++c, ++bufs) {
+            if (*bufs == NULL) {
+                if ((err = substream->ops->silence(substream, c, hwoff, frames)) < 0)
+                    return err;
+            } else {
+#ifdef TARGET_OS2
+                char *buf = (char *)*bufs + samples_to_bytes(runtime, off);
+#else
+                char *buf = *bufs + samples_to_bytes(runtime, off);
+#endif
+                if ((err = substream->ops->copy(substream, c, hwoff, buf, frames)) < 0)
+                    return err;
+            }
+        }
+    } else {
+        /* default transfer behaviour */
+        size_t dma_csize = runtime->dma_bytes / channels;
+        snd_assert(runtime->dma_area, return -EFAULT);
+        for (c = 0; c < channels; ++c, ++bufs) {
+            char *hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, hwoff);
+            if (*bufs == NULL) {
+                snd_pcm_format_set_silence(runtime->format, hwbuf, frames);
+            } else {
+#ifdef TARGET_OS2
+                char *buf = (char *)*bufs + samples_to_bytes(runtime, off);
+#else
+                char *buf = *bufs + samples_to_bytes(runtime, off);
+#endif
+                if (copy_from_user(hwbuf, buf, samples_to_bytes(runtime, frames)))
+                    return -EFAULT;
+            }
+        }
+    }
+    return 0;
+}
+
+snd_pcm_sframes_t snd_pcm_lib_writev(snd_pcm_substream_t *substream, void **bufs,
+                                     snd_pcm_uframes_t frames)
+{
+    snd_pcm_runtime_t *runtime;
+    int nonblock;
+
+#ifdef DEBUG_PK
+    printk("snd_pcm_lib_writev substream: %x, buf: %x, size: %d\n",substream, bufs, frames);
+#endif
+    snd_assert(substream != NULL, return -ENXIO);
+    runtime = substream->runtime;
+    snd_assert(runtime != NULL, return -ENXIO);
+    snd_assert(substream->ops->copy != NULL || runtime->dma_area != NULL, return -EINVAL);
+    if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+        return -EBADFD;
+
+    snd_assert(substream->ffile != NULL, return -ENXIO);
+    nonblock = !!(substream->ffile->f_flags & O_NONBLOCK);
+#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
+    if (substream->oss.oss) {
+        snd_pcm_oss_setup_t *setup = substream->oss.setup;
+        if (setup != NULL) {
+            if (setup->nonblock)
+                nonblock = 1;
+            else if (setup->block)
+                nonblock = 0;
+        }
+    }
+#endif
+
+    if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED)
+        return -EINVAL;
+#ifdef DEBUG_PK
+    printk("snd_pcm_lib_writev end\n");
+#endif
+    return snd_pcm_lib_write1(substream, bufs, frames, nonblock,
+                              snd_pcm_lib_writev_transfer);
+}
+
+static int snd_pcm_lib_read_transfer(snd_pcm_substream_t *substream,
+                                     unsigned int hwoff,
+                                     void *data, unsigned int off,
+                                     snd_pcm_uframes_t frames)
+{
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    int err;
+    char *buf = (char *) data + frames_to_bytes(runtime, off);
+    if (substream->ops->copy) {
+        if ((err = substream->ops->copy(substream, -1, hwoff, buf, frames)) < 0)
+            return err;
+    } else {
+        char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff);
+        snd_assert(runtime->dma_area, return -EFAULT);
+        if (copy_to_user(buf, hwbuf, frames_to_bytes(runtime, frames)))
+            return -EFAULT;
+    }
+    return 0;
+}
+
+static snd_pcm_sframes_t snd_pcm_lib_read1(snd_pcm_substream_t *substream, void *data, snd_pcm_uframes_t size, int nonblock,
+                                           transfer_f transfer)
+{
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    snd_pcm_uframes_t xfer = 0;
+    snd_pcm_uframes_t offset = 0;
+    int err = 0;
+
+    if (size == 0)
+        return 0;
+    if (size > runtime->xfer_align)
+        size -= size % runtime->xfer_align;
+
+    snd_pcm_stream_lock_irq(substream);
+    switch (runtime->status->state) {
+    case SNDRV_PCM_STATE_PREPARED:
+        if (size >= runtime->start_threshold) {
+            err = snd_pcm_start(substream);
+            if (err < 0)
+                goto _end_unlock;
+        }
+        break;
+    case SNDRV_PCM_STATE_DRAINING:
+    case SNDRV_PCM_STATE_RUNNING:
+    case SNDRV_PCM_STATE_PAUSED:
+        break;
+    case SNDRV_PCM_STATE_XRUN:
+        err = -EPIPE;
+        goto _end_unlock;
+    case SNDRV_PCM_STATE_SUSPENDED:
+        err = -ESTRPIPE;
+        goto _end_unlock;
+    default:
+        err = -EBADFD;
+        goto _end_unlock;
+    }
+
+    while (size > 0) {
+        snd_pcm_uframes_t frames, appl_ptr, appl_ofs;
+        snd_pcm_uframes_t avail;
+        snd_pcm_uframes_t cont;
+        if (runtime->sleep_min == 0 && runtime->status->state == SNDRV_PCM_STATE_RUNNING)
+            snd_pcm_update_hw_ptr(substream);
+    __draining:
+        avail = snd_pcm_capture_avail(runtime);
+        if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) {
+            if (avail < runtime->xfer_align) {
+                err = -EPIPE;
+                goto _end_unlock;
+            }
+        } else if ((avail < runtime->control->avail_min && size > avail) ||
+                   (size >= runtime->xfer_align && avail < runtime->xfer_align)) {
+            wait_queue_t wait;
+            enum { READY, SIGNALED, ERROR, SUSPENDED, EXPIRED, DROPPED } state;
+            long tout;
+
+            if (nonblock) {
+                err = -EAGAIN;
+                goto _end_unlock;
+            }
+
+            init_waitqueue_entry(&wait, current);
+            add_wait_queue(&runtime->sleep, &wait);
+            while (1) {
+                if (signal_pending(current)) {
+                    state = SIGNALED;
+                    break;
+                }
+                set_current_state(TASK_INTERRUPTIBLE);
+                snd_pcm_stream_unlock_irq(substream);
+                tout = schedule_timeout(10 * HZ);
+                snd_pcm_stream_lock_irq(substream);
+                if (tout == 0) {
+                    if (runtime->status->state != SNDRV_PCM_STATE_PREPARED &&
+                        runtime->status->state != SNDRV_PCM_STATE_PAUSED) {
+                        state = runtime->status->state == SNDRV_PCM_STATE_SUSPENDED ? SUSPENDED : EXPIRED;
+                        break;
+                    }
+                }
+                switch (runtime->status->state) {
+                case SNDRV_PCM_STATE_XRUN:
+                    state = ERROR;
+                    goto _end_loop;
+                case SNDRV_PCM_STATE_SUSPENDED:
+                    state = SUSPENDED;
+                    goto _end_loop;
+                case SNDRV_PCM_STATE_DRAINING:
+                    goto __draining;
+                case SNDRV_PCM_STATE_SETUP:
+                    state = DROPPED;
+                    goto _end_loop;
+                default:
+                    break;
+                }
+                avail = snd_pcm_capture_avail(runtime);
+                if (avail >= runtime->control->avail_min) {
+                    state = READY;
+                    break;
+                }
+            }
+        _end_loop:
+            remove_wait_queue(&runtime->sleep, &wait);
+
+            switch (state) {
+            case ERROR:
+                err = -EPIPE;
+                goto _end_unlock;
+            case SUSPENDED:
+                err = -ESTRPIPE;
+                goto _end_unlock;
+            case SIGNALED:
+                err = -ERESTARTSYS;
+                goto _end_unlock;
+            case EXPIRED:
+                snd_printd("capture read error (DMA or IRQ trouble?)\n");
+                err = -EIO;
+                goto _end_unlock;
+            case DROPPED:
+                err = -EBADFD;
+                goto _end_unlock;
+            default:
+                break;
+            }
+        }
+        if (avail > runtime->xfer_align)
+            avail -= avail % runtime->xfer_align;
+        frames = size > avail ? avail : size;
+        cont = runtime->buffer_size - runtime->control->appl_ptr % runtime->buffer_size;
+        if (frames > cont)
+            frames = cont;
+        snd_assert(frames != 0, snd_pcm_stream_unlock_irq(substream); return -EINVAL);
+        appl_ptr = runtime->control->appl_ptr;
+        appl_ofs = appl_ptr % runtime->buffer_size;
+        snd_pcm_stream_unlock_irq(substream);
+        if ((err = transfer(substream, appl_ofs, (void *)data, offset, frames)) < 0)
+            goto _end;
+        snd_pcm_stream_lock_irq(substream);
+        switch (runtime->status->state) {
+        case SNDRV_PCM_STATE_XRUN:
+            err = -EPIPE;
+            goto _end_unlock;
+        case SNDRV_PCM_STATE_SUSPENDED:
+            err = -ESTRPIPE;
+            goto _end_unlock;
+        default:
+            break;
+        }
+        appl_ptr += frames;
+        if (appl_ptr >= runtime->boundary)
+            appl_ptr -= runtime->boundary;
+        runtime->control->appl_ptr = appl_ptr;
+        if (substream->ops->ack)
+            substream->ops->ack(substream);
+
+        offset += frames;
+        size -= frames;
+        xfer += frames;
+        if (runtime->sleep_min &&
+            runtime->status->state == SNDRV_PCM_STATE_RUNNING)
+            snd_pcm_tick_prepare(substream);
+    }
+    _end_unlock:
+    snd_pcm_stream_unlock_irq(substream);
+    _end:
+        return xfer > 0 ? (snd_pcm_sframes_t)xfer : err;
+}
+
+snd_pcm_sframes_t snd_pcm_lib_read(snd_pcm_substream_t *substream, void *buf, snd_pcm_uframes_t size)
+{
+    snd_pcm_runtime_t *runtime;
+    int nonblock;
+
+    snd_assert(substream != NULL, return -ENXIO);
+    runtime = substream->runtime;
+    snd_assert(runtime != NULL, return -ENXIO);
+    snd_assert(substream->ops->copy != NULL || runtime->dma_area != NULL, return -EINVAL);
+    if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+        return -EBADFD;
+
+    snd_assert(substream->ffile != NULL, return -ENXIO);
+    nonblock = !!(substream->ffile->f_flags & O_NONBLOCK);
+#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
+    if (substream->oss.oss) {
+        snd_pcm_oss_setup_t *setup = substream->oss.setup;
+        if (setup != NULL) {
+            if (setup->nonblock)
+                nonblock = 1;
+            else if (setup->block)
+                nonblock = 0;
+        }
+    }
+#endif
+    if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED)
+        return -EINVAL;
+    return snd_pcm_lib_read1(substream, buf, size, nonblock, snd_pcm_lib_read_transfer);
+}
+
+static int snd_pcm_lib_readv_transfer(snd_pcm_substream_t *substream,
+                                      unsigned int hwoff,
+                                      void *data, unsigned int off,
+                                      snd_pcm_uframes_t frames)
+{
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    int err;
+    void **bufs = data;
+    int channels = runtime->channels;
+    int c;
+    if (substream->ops->copy) {
+        for (c = 0; c < channels; ++c, ++bufs) {
+            char *buf;
+            if (*bufs == NULL)
+                continue;
+#ifdef TARGET_OS2
+            buf = (char *)*bufs + samples_to_bytes(runtime, off);
+#else
+            buf = *bufs + samples_to_bytes(runtime, off);
+#endif
+            if ((err = substream->ops->copy(substream, c, hwoff, buf, frames)) < 0)
+                return err;
+        }
+    } else {
+        snd_pcm_uframes_t dma_csize = runtime->dma_bytes / channels;
+        snd_assert(runtime->dma_area, return -EFAULT);
+        for (c = 0; c < channels; ++c, ++bufs) {
+            char *hwbuf, *buf;
+            if (*bufs == NULL)
+                continue;
+
+            hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, hwoff);
+#ifdef TARGET_OS2
+            buf = (char *)*bufs + samples_to_bytes(runtime, off);
+#else
+            buf = *bufs + samples_to_bytes(runtime, off);
+#endif
+            if (copy_to_user(buf, hwbuf, samples_to_bytes(runtime, frames)))
+                return -EFAULT;
+        }
+    }
+    return 0;
+}
+
+snd_pcm_sframes_t snd_pcm_lib_readv(snd_pcm_substream_t *substream, void **bufs,
+                                    snd_pcm_uframes_t frames)
+{
+    snd_pcm_runtime_t *runtime;
+    int nonblock;
+
+    snd_assert(substream != NULL, return -ENXIO);
+    runtime = substream->runtime;
+    snd_assert(runtime != NULL, return -ENXIO);
+    snd_assert(substream->ops->copy != NULL || runtime->dma_area != NULL, return -EINVAL);
+    if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+        return -EBADFD;
+
+    snd_assert(substream->ffile != NULL, return -ENXIO);
+    nonblock = !!(substream->ffile->f_flags & O_NONBLOCK);
+#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
+    if (substream->oss.oss) {
+        snd_pcm_oss_setup_t *setup = substream->oss.setup;
+        if (setup != NULL) {
+            if (setup->nonblock)
+                nonblock = 1;
+            else if (setup->block)
+                nonblock = 0;
+        }
+    }
+#endif
+
+    if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED)
+        return -EINVAL;
+    return snd_pcm_lib_read1(substream, bufs, frames, nonblock, snd_pcm_lib_readv_transfer);
+}
+
+/*
+ *  Exported symbols
+ */
+
+EXPORT_SYMBOL(snd_interval_refine);
+EXPORT_SYMBOL(snd_interval_list);
+EXPORT_SYMBOL(snd_interval_ratnum);
+EXPORT_SYMBOL(snd_interval_ratden);
+EXPORT_SYMBOL(snd_interval_muldivk);
+EXPORT_SYMBOL(snd_interval_mulkdiv);
+EXPORT_SYMBOL(snd_interval_div);
+EXPORT_SYMBOL(_snd_pcm_hw_params_any);
+EXPORT_SYMBOL(_snd_pcm_hw_param_min);
+EXPORT_SYMBOL(_snd_pcm_hw_param_set);
+EXPORT_SYMBOL(_snd_pcm_hw_param_setempty);
+EXPORT_SYMBOL(_snd_pcm_hw_param_setinteger);
+EXPORT_SYMBOL(snd_pcm_hw_param_value_min);
+EXPORT_SYMBOL(snd_pcm_hw_param_value_max);
+EXPORT_SYMBOL(snd_pcm_hw_param_mask);
+EXPORT_SYMBOL(snd_pcm_hw_param_first);
+EXPORT_SYMBOL(snd_pcm_hw_param_last);
+EXPORT_SYMBOL(snd_pcm_hw_param_near);
+EXPORT_SYMBOL(snd_pcm_hw_param_set);
+EXPORT_SYMBOL(snd_pcm_hw_refine);
+EXPORT_SYMBOL(snd_pcm_hw_constraints_init);
+EXPORT_SYMBOL(snd_pcm_hw_constraints_complete);
+EXPORT_SYMBOL(snd_pcm_hw_constraint_list);
+EXPORT_SYMBOL(snd_pcm_hw_constraint_step);
+EXPORT_SYMBOL(snd_pcm_hw_constraint_ratnums);
+EXPORT_SYMBOL(snd_pcm_hw_constraint_ratdens);
+EXPORT_SYMBOL(snd_pcm_hw_constraint_msbits);
+EXPORT_SYMBOL(snd_pcm_hw_constraint_minmax);
+EXPORT_SYMBOL(snd_pcm_hw_constraint_integer);
+EXPORT_SYMBOL(snd_pcm_hw_constraint_pow2);
+EXPORT_SYMBOL(snd_pcm_hw_rule_add);
+EXPORT_SYMBOL(snd_pcm_set_ops);
+EXPORT_SYMBOL(snd_pcm_set_sync);
+EXPORT_SYMBOL(snd_pcm_lib_ioctl);
+EXPORT_SYMBOL(snd_pcm_playback_ready);
+EXPORT_SYMBOL(snd_pcm_capture_ready);
+EXPORT_SYMBOL(snd_pcm_playback_data);
+EXPORT_SYMBOL(snd_pcm_capture_empty);
+EXPORT_SYMBOL(snd_pcm_stop);
+EXPORT_SYMBOL(snd_pcm_period_elapsed);
+EXPORT_SYMBOL(snd_pcm_lib_write);
+EXPORT_SYMBOL(snd_pcm_lib_read);
+EXPORT_SYMBOL(snd_pcm_lib_writev);
+EXPORT_SYMBOL(snd_pcm_lib_readv);
+EXPORT_SYMBOL(snd_pcm_lib_buffer_bytes);
+EXPORT_SYMBOL(snd_pcm_lib_period_bytes);
+/* pcm_memory.c */
+EXPORT_SYMBOL(snd_pcm_lib_preallocate_free);
+EXPORT_SYMBOL(snd_pcm_lib_preallocate_free_for_all);
+EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages);
+EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages_for_all);
+EXPORT_SYMBOL(snd_pcm_lib_malloc_pages);
+EXPORT_SYMBOL(snd_pcm_lib_free_pages);
+#ifdef CONFIG_ISA
+EXPORT_SYMBOL(snd_pcm_lib_preallocate_isa_pages);
+EXPORT_SYMBOL(snd_pcm_lib_preallocate_isa_pages_for_all);
+#endif
+#ifdef CONFIG_PCI
+EXPORT_SYMBOL(snd_pcm_lib_preallocate_pci_pages);
+EXPORT_SYMBOL(snd_pcm_lib_preallocate_pci_pages_for_all);
+EXPORT_SYMBOL(snd_pcm_lib_preallocate_sg_pages);
+EXPORT_SYMBOL(snd_pcm_lib_preallocate_sg_pages_for_all);
+EXPORT_SYMBOL(snd_pcm_sgbuf_ops_page);
+#endif
Index: /GPL/branches/alsa-resync1/alsa-kernel/core/pcm_memory.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/core/pcm_memory.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/core/pcm_memory.c	(revision 91)
@@ -0,0 +1,362 @@
+/*
+ *  Digital Audio (PCM) abstract layer
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <sound/pcm.h>
+#include <sound/info.h>
+#include <sound/initval.h>
+
+static int preallocate_dma = 1;
+MODULE_PARM(preallocate_dma, "i");
+MODULE_PARM_DESC(preallocate_dma, "Preallocate DMA memory when the PCM devices are initialized.");
+MODULE_PARM_SYNTAX(preallocate_dma, SNDRV_BOOLEAN_TRUE_DESC);
+
+static int maximum_substreams = 4;
+MODULE_PARM(maximum_substreams, "i");
+MODULE_PARM_DESC(maximum_substreams, "Maximum substreams with preallocated DMA memory.");
+MODULE_PARM_SYNTAX(maximum_substreams, SNDRV_BOOLEAN_TRUE_DESC);
+
+const static size_t snd_minimum_buffer = 16384;
+
+
+/*
+ * try to allocate as the large pages as possible.
+ * stores the resultant memory size in *res_size.
+ *
+ * the minimum size is snd_minimum_buffer.  it should be power of 2.
+ */
+static int preallocate_pcm_pages(snd_pcm_substream_t *substream, size_t size)
+{
+    struct snd_dma_buffer *dmab = &substream->dma_buffer;
+    int err;
+
+    snd_assert(size > 0, return -EINVAL);
+
+    /* already reserved? */
+    if (snd_dma_get_reserved_buf(dmab, substream->dma_buf_id) > 0) {
+        if (dmab->bytes >= size)
+            return 0; /* yes */
+        /* no, free the reserved block */
+        snd_dma_free_pages(dmab);
+        dmab->bytes = 0;
+    }
+
+    do {
+        if ((err = snd_dma_alloc_pages(dmab->dev.type, dmab->dev.dev,
+  	                                                size, dmab)) < 0) {
+            if (err != -ENOMEM)
+                return err; /* fatal error */
+        } else
+            return 0;
+        size >>= 1;
+    } while (size >= snd_minimum_buffer);
+    dmab->bytes = 0; /* tell error */
+    return 0;
+}
+
+/*
+ * release the preallocated buffer if not yet done.
+ */
+static void snd_pcm_lib_preallocate_dma_free(snd_pcm_substream_t *substream)
+{
+    if (substream->dma_buffer.area == NULL)
+        return;
+    if (substream->dma_buf_id)
+        snd_dma_reserve_buf(&substream->dma_buffer, substream->dma_buf_id);
+    else
+        snd_dma_free_pages(&substream->dma_buffer);
+    substream->dma_buffer.area = NULL;
+}
+
+/**
+ * snd_pcm_lib_preallocate_free - release the preallocated buffer of the specified substream.
+ * @substream: the pcm substream instance
+ *
+ * Releases the pre-allocated buffer of the given substream.
+ *
+ * Returns zero if successful, or a negative error code on failure.
+ */
+int snd_pcm_lib_preallocate_free(snd_pcm_substream_t *substream)
+{
+    snd_pcm_lib_preallocate_dma_free(substream);
+    if (substream->proc_prealloc_entry) {
+        snd_info_unregister(substream->proc_prealloc_entry);
+        substream->proc_prealloc_entry = NULL;
+    }
+    return 0;
+}
+
+/**
+ * snd_pcm_lib_preallocate_free_for_all - release all pre-allocated buffers on the pcm
+ * @pcm: the pcm instance
+ *
+ * Releases all the pre-allocated buffers on the given pcm.
+ *
+ * Returns zero if successful, or a negative error code on failure.
+ */
+int snd_pcm_lib_preallocate_free_for_all(snd_pcm_t *pcm)
+{
+    snd_pcm_substream_t *substream;
+    int stream;
+
+    for (stream = 0; stream < 2; stream++)
+        for (substream = pcm->streams[stream].substream; substream; substream = substream->next)
+            snd_pcm_lib_preallocate_free(substream);
+    return 0;
+}
+
+/*
+ * read callback for prealloc proc file
+ *
+ * prints the current allocated size in kB.
+ */
+static void snd_pcm_lib_preallocate_proc_read(snd_info_entry_t *entry,
+                                              snd_info_buffer_t *buffer)
+{
+    snd_pcm_substream_t *substream = (snd_pcm_substream_t *)entry->private_data;
+    snd_iprintf(buffer, "%lu\n", (unsigned long) substream->dma_buffer.bytes / 1024);
+}
+
+/*
+ * write callback for prealloc proc file
+ *
+ * accepts the preallocation size in kB.
+ */
+static void snd_pcm_lib_preallocate_proc_write(snd_info_entry_t *entry,
+                                               snd_info_buffer_t *buffer)
+{
+    snd_pcm_substream_t *substream = (snd_pcm_substream_t *)entry->private_data;
+    char line[64], str[64];
+    size_t size;
+    struct snd_dma_buffer new_dmab;
+
+    if (substream->runtime) {
+        buffer->error = -EBUSY;
+        return;
+    }
+    if (!snd_info_get_line(buffer, line, sizeof(line))) {
+        snd_info_get_str(str, line, sizeof(str));
+        size = simple_strtoul(str, NULL, 10) * 1024;
+        if ((size != 0 && size < 8192) || size > substream->dma_max) {
+            buffer->error = -EINVAL;
+            return;
+        }
+        if (substream->dma_buffer.bytes == size)
+            return;
+        memset(&new_dmab, 0, sizeof(new_dmab));
+        new_dmab.dev = substream->dma_buffer.dev;
+        if (size > 0) {
+            if (snd_dma_alloc_pages(substream->dma_buffer.dev.type,
+                                    substream->dma_buffer.dev.dev,
+                                    size, &new_dmab) < 0) {
+                buffer->error = -ENOMEM;
+                return;
+            }
+            substream->buffer_bytes_max = size;
+        } else {
+            substream->buffer_bytes_max = UINT_MAX;
+        }
+        if (substream->dma_buffer.area)
+            snd_dma_free_pages(&substream->dma_buffer);
+        substream->dma_buffer = new_dmab;
+    } else {
+        buffer->error = -EINVAL;
+    }
+}
+
+/*
+ * pre-allocate the buffer and create a proc file for the substream
+ */
+static int snd_pcm_lib_preallocate_pages1(snd_pcm_substream_t *substream,
+                                          size_t size, size_t max)
+{
+    snd_info_entry_t *entry;
+
+    if (size > 0 && preallocate_dma && substream->number < maximum_substreams)
+        preallocate_pcm_pages(substream, size);
+
+    if (substream->dma_buffer.bytes > 0)
+        substream->buffer_bytes_max = substream->dma_buffer.bytes;
+    substream->dma_max = max;
+    if ((entry = snd_info_create_card_entry(substream->pcm->card, "prealloc", substream->proc_root)) != NULL) {
+        entry->c.text.read_size = 64;
+        entry->c.text.read = snd_pcm_lib_preallocate_proc_read;
+        entry->c.text.write_size = 64;
+        entry->c.text.write = snd_pcm_lib_preallocate_proc_write;
+        entry->private_data = substream;
+        if (snd_info_register(entry) < 0) {
+            snd_info_free_entry(entry);
+            entry = NULL;
+        }
+    }
+    substream->proc_prealloc_entry = entry;
+    return 0;
+}
+
+/**
+ * snd_pcm_lib_preallocate_pages - pre-allocation for the given DMA type
+ * @substream: the pcm substream instance
+ * @type: DMA type (SNDRV_DMA_TYPE_*)
+ * @data: DMA type dependant data
+ * @size: the requested pre-allocation size in bytes
+ * @max: the max. allowed pre-allocation size
+ *
+ * Do pre-allocation for the given DMA buffer type.
+ *
+ * When substream->dma_buf_id is set, the function tries to look for
+ * the reserved buffer, and the buffer is not freed but reserved at
+ * destruction time.  The dma_buf_id must be unique for all systems
+ * (in the same DMA buffer type) e.g. using snd_dma_pci_buf_id().
+ *
+ * Returns zero if successful, or a negative error code on failure.
+ */
+int snd_pcm_lib_preallocate_pages(snd_pcm_substream_t *substream,
+				  int type, struct device *data,
+				  size_t size, size_t max)
+{
+	substream->dma_buffer.dev.type = type;
+	substream->dma_buffer.dev.dev = data;
+	return snd_pcm_lib_preallocate_pages1(substream, size, max);
+}
+
+/**
+ * snd_pcm_lib_preallocate_pages_for_all - pre-allocation for continous memory type (all substreams)
+ * @substream: the pcm substream instance
+ * @type: DMA type (SNDRV_DMA_TYPE_*)
+ * @data: DMA type dependant data
+ * @size: the requested pre-allocation size in bytes
+ * @max: the max. allowed pre-allocation size
+ *
+ * Do pre-allocation to all substreams of the given pcm for the
+ * specified DMA type.
+ *
+ * Returns zero if successful, or a negative error code on failure.
+ */
+int snd_pcm_lib_preallocate_pages_for_all(snd_pcm_t *pcm,
+					  int type, void *data,
+					  size_t size, size_t max)
+{
+	snd_pcm_substream_t *substream;
+	int stream, err;
+
+	for (stream = 0; stream < 2; stream++)
+		for (substream = pcm->streams[stream].substream; substream; substream = substream->next)
+			if ((err = snd_pcm_lib_preallocate_pages(substream, type, data, size, max)) < 0)
+				return err;
+	return 0;
+}
+
+
+/**
+ * snd_pcm_sgbuf_ops_page - get the page struct at the given offset
+ * @substream: the pcm substream instance
+ * @offset: the buffer offset
+ *
+ * Returns the page struct at the given buffer offset.
+ * Used as the page callback of PCM ops.
+ */
+struct page *snd_pcm_sgbuf_ops_page(snd_pcm_substream_t *substream, unsigned long offset)
+{
+	struct snd_sg_buf *sgbuf = snd_pcm_substream_sgbuf(substream);
+
+	unsigned int idx = offset >> PAGE_SHIFT;
+	if (idx >= (unsigned int)sgbuf->pages)
+		return NULL;
+	return sgbuf->page_table[idx];
+}
+
+/**
+ * snd_pcm_lib_malloc_pages - allocate the DMA buffer
+ * @substream: the substream to allocate the DMA buffer to
+ * @size: the requested buffer size in bytes
+ *
+ * Allocates the DMA buffer on the BUS type given by
+ * snd_pcm_lib_preallocate_xxx_pages().
+ *
+ * Returns 1 if the buffer is changed, 0 if not changed, or a negative
+ * code on failure.
+ */
+int snd_pcm_lib_malloc_pages(snd_pcm_substream_t *substream, size_t size)
+{
+	snd_pcm_runtime_t *runtime;
+	struct snd_dma_buffer *dmab = NULL;
+
+	snd_assert(substream->dma_buffer.dev.type != SNDRV_DMA_TYPE_UNKNOWN, return -EINVAL);
+	snd_assert(substream != NULL, return -EINVAL);
+	runtime = substream->runtime;
+	snd_assert(runtime != NULL, return -EINVAL);
+
+	if (runtime->dma_buffer_p) {
+		/* perphaps, we might free the large DMA memory region
+		   to save some space here, but the actual solution
+		   costs us less time */
+		if (runtime->dma_buffer_p->bytes >= size) {
+			runtime->dma_bytes = size;
+			return 0;	/* ok, do not change */
+		}
+		snd_pcm_lib_free_pages(substream);
+	}
+	if (substream->dma_buffer.area != NULL && substream->dma_buffer.bytes >= size) {
+		dmab = &substream->dma_buffer; /* use the pre-allocated buffer */
+        } else {
+            dmab = (struct snd_dma_buffer *)kzalloc(sizeof(*dmab), GFP_KERNEL);
+            if (! dmab)
+                return -ENOMEM;
+		dmab->dev = substream->dma_buffer.dev;
+		if (snd_dma_alloc_pages(substream->dma_buffer.dev.type,
+					substream->dma_buffer.dev.dev,
+					size, dmab) < 0) {
+			kfree(dmab);
+			return -ENOMEM;
+		}
+	}
+	snd_pcm_set_runtime_buffer(substream, dmab);
+	runtime->dma_bytes = size;
+	return 1;			/* area was changed */
+}
+
+/**
+ * snd_pcm_lib_free_pages - release the allocated DMA buffer.
+ * @substream: the substream to release the DMA buffer
+ *
+ * Releases the DMA buffer allocated via snd_pcm_lib_malloc_pages().
+ *
+ * Returns zero if successful, or a negative error code on failure.
+ */
+int snd_pcm_lib_free_pages(snd_pcm_substream_t *substream)
+{
+	snd_pcm_runtime_t *runtime;
+
+	snd_assert(substream != NULL, return -EINVAL);
+	runtime = substream->runtime;
+	snd_assert(runtime != NULL, return -EINVAL);
+	if (runtime->dma_area == NULL)
+		return 0;
+	if (runtime->dma_buffer_p != &substream->dma_buffer) {
+		/* it's a newly allocated buffer.  release it now. */
+		snd_dma_free_pages(runtime->dma_buffer_p);
+		kfree(runtime->dma_buffer_p);
+	}
+	snd_pcm_set_runtime_buffer(substream, NULL);
+	return 0;
+}
+
+
Index: /GPL/branches/alsa-resync1/alsa-kernel/core/pcm_misc.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/core/pcm_misc.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/core/pcm_misc.c	(revision 91)
@@ -0,0 +1,465 @@
+/*
+ *  PCM Interface - misc routines
+ *  Copyright (c) 1998 by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This library is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU Library 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 Library General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Library General Public
+ *   License along with this library; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+  
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#define SND_PCM_FORMAT_UNKNOWN (-1)
+
+/* NOTE: "signed" prefix must be given below since the default char is
+ *       unsigned on some architectures!
+ */
+struct pcm_format_data {
+	unsigned char width;	/* bit width */
+	unsigned char phys;	/* physical bit width */
+	signed char le;	/* 0 = big-endian, 1 = little-endian, -1 = others */
+	signed char signd;	/* 0 = unsigned, 1 = signed, -1 = others */
+	unsigned char silence[8];	/* silence data to fill */
+};
+
+static struct pcm_format_data pcm_formats[SNDRV_PCM_FORMAT_LAST+1] = {
+	[SNDRV_PCM_FORMAT_S8] = {
+		.width = 8, .phys = 8, .le = -1, .signd = 1,
+		.silence = {0},
+	},
+	[SNDRV_PCM_FORMAT_U8] = {
+		.width = 8, .phys = 8, .le = -1, .signd = 0,
+		.silence = { 0x80 },
+	},
+	[SNDRV_PCM_FORMAT_S16_LE] = {
+		.width = 16, .phys = 16, .le = 1, .signd = 1,
+		.silence = {0},
+	},
+	[SNDRV_PCM_FORMAT_S16_BE] = {
+		.width = 16, .phys = 16, .le = 0, .signd = 1,
+		.silence = {0},
+	},
+	[SNDRV_PCM_FORMAT_U16_LE] = {
+		.width = 16, .phys = 16, .le = 1, .signd = 0,
+		.silence = { 0x00, 0x80 },
+	},
+	[SNDRV_PCM_FORMAT_U16_BE] = {
+		.width = 16, .phys = 16, .le = 0, .signd = 0,
+		.silence = { 0x80, 0x00 },
+	},
+	[SNDRV_PCM_FORMAT_S24_LE] = {
+		.width = 24, .phys = 32, .le = 1, .signd = 1,
+		.silence = {0},
+	},
+	[SNDRV_PCM_FORMAT_S24_BE] = {
+		.width = 24, .phys = 32, .le = 0, .signd = 1,
+		.silence = {0},
+	},
+	[SNDRV_PCM_FORMAT_U24_LE] = {
+		.width = 24, .phys = 32, .le = 1, .signd = 0,
+		.silence = { 0x00, 0x00, 0x80 },
+	},
+	[SNDRV_PCM_FORMAT_U24_BE] = {
+		.width = 24, .phys = 32, .le = 0, .signd = 0,
+		.silence = { 0x80, 0x00, 0x00 },
+	},
+	[SNDRV_PCM_FORMAT_S32_LE] = {
+		.width = 32, .phys = 32, .le = 1, .signd = 1,
+		.silence = {0},
+	},
+	[SNDRV_PCM_FORMAT_S32_BE] = {
+		.width = 32, .phys = 32, .le = 0, .signd = 1,
+		.silence = {0},
+	},
+	[SNDRV_PCM_FORMAT_U32_LE] = {
+		.width = 32, .phys = 32, .le = 1, .signd = 0,
+		.silence = { 0x00, 0x00, 0x00, 0x80 },
+	},
+	[SNDRV_PCM_FORMAT_U32_BE] = {
+		.width = 32, .phys = 32, .le = 0, .signd = 0,
+		.silence = { 0x80, 0x00, 0x00, 0x00 },
+	},
+	[SNDRV_PCM_FORMAT_FLOAT_LE] = {
+		.width = 32, .phys = 32, .le = 1, .signd = -1,
+		.silence = {0},
+	},
+	[SNDRV_PCM_FORMAT_FLOAT_BE] = {
+		.width = 32, .phys = 32, .le = 0, .signd = -1,
+		.silence = {0},
+	},
+	[SNDRV_PCM_FORMAT_FLOAT64_LE] = {
+		.width = 64, .phys = 64, .le = 1, .signd = -1,
+		.silence = {0},
+	},
+	[SNDRV_PCM_FORMAT_FLOAT64_BE] = {
+		.width = 64, .phys = 64, .le = 0, .signd = -1,
+		.silence = {0},
+	},
+	[SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE] = {
+		.width = 32, .phys = 32, .le = 1, .signd = -1,
+		.silence = {0},
+	},
+	[SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE] = {
+		.width = 32, .phys = 32, .le = 0, .signd = -1,
+		.silence = {0},
+	},
+	[SNDRV_PCM_FORMAT_MU_LAW] = {
+		.width = 8, .phys = 8, .le = -1, .signd = -1,
+		.silence = { 0x7f },
+	},
+	[SNDRV_PCM_FORMAT_A_LAW] = {
+		.width = 8, .phys = 8, .le = -1, .signd = -1,
+		.silence = { 0x55 },
+	},
+	[SNDRV_PCM_FORMAT_IMA_ADPCM] = {
+		.width = 4, .phys = 4, .le = -1, .signd = -1,
+		.silence = {0},
+	},
+	/* FIXME: the following three formats are not defined properly yet */
+	[SNDRV_PCM_FORMAT_MPEG] = {
+		.le = -1, .signd = -1,
+	},
+	[SNDRV_PCM_FORMAT_GSM] = {
+		.le = -1, .signd = -1,
+	},
+	[SNDRV_PCM_FORMAT_SPECIAL] = {
+		.le = -1, .signd = -1,
+	},
+	[SNDRV_PCM_FORMAT_S24_3LE] = {
+		.width = 24, .phys = 24, .le = 1, .signd = 1,
+		.silence = {0},
+	},
+	[SNDRV_PCM_FORMAT_S24_3BE] = {
+		.width = 24, .phys = 24, .le = 0, .signd = 1,
+		.silence = {0},
+	},
+	[SNDRV_PCM_FORMAT_U24_3LE] = {
+		.width = 24, .phys = 24, .le = 1, .signd = 0,
+		.silence = { 0x00, 0x00, 0x80 },
+	},
+	[SNDRV_PCM_FORMAT_U24_3BE] = {
+		.width = 24, .phys = 24, .le = 0, .signd = 0,
+		.silence = { 0x80, 0x00, 0x00 },
+	},
+	[SNDRV_PCM_FORMAT_S20_3LE] = {
+		.width = 20, .phys = 24, .le = 1, .signd = 1,
+		.silence = {0},
+	},
+	[SNDRV_PCM_FORMAT_S20_3BE] = {
+		.width = 20, .phys = 24, .le = 0, .signd = 1,
+		.silence = {0},
+	},
+	[SNDRV_PCM_FORMAT_U20_3LE] = {
+		.width = 20, .phys = 24, .le = 1, .signd = 0,
+		.silence = { 0x00, 0x00, 0x08 },
+	},
+	[SNDRV_PCM_FORMAT_U20_3BE] = {
+		.width = 20, .phys = 24, .le = 0, .signd = 0,
+		.silence = { 0x08, 0x00, 0x00 },
+	},
+	[SNDRV_PCM_FORMAT_S18_3LE] = {
+		.width = 18, .phys = 24, .le = 1, .signd = 1,
+		.silence = {0},
+	},
+	[SNDRV_PCM_FORMAT_S18_3BE] = {
+		.width = 18, .phys = 24, .le = 0, .signd = 1,
+		.silence = {0},
+	},
+	[SNDRV_PCM_FORMAT_U18_3LE] = {
+		.width = 18, .phys = 24, .le = 1, .signd = 0,
+		.silence = { 0x00, 0x00, 0x02 },
+	},
+	[SNDRV_PCM_FORMAT_U18_3BE] = {
+		.width = 18, .phys = 24, .le = 0, .signd = 0,
+		.silence = { 0x02, 0x00, 0x00 },
+	},
+};
+
+
+/**
+ * snd_pcm_format_signed - Check the PCM format is signed linear
+ * @format: the format to check
+ *
+ * Returns 1 if the given PCM format is signed linear, 0 if unsigned
+ * linear, and a negative error code for non-linear formats.
+ */
+int snd_pcm_format_signed(snd_pcm_format_t format)
+{
+	int val;
+	if (format < 0 || format > SNDRV_PCM_FORMAT_LAST)
+		return -EINVAL;
+	if ((val = pcm_formats[format].signd) < 0)
+		return -EINVAL;
+	return val;
+}
+
+/**
+ * snd_pcm_format_unsigned - Check the PCM format is unsigned linear
+ * @format: the format to check
+ *
+ * Returns 1 if the given PCM format is unsigned linear, 0 if signed
+ * linear, and a negative error code for non-linear formats.
+ */
+int snd_pcm_format_unsigned(snd_pcm_format_t format)
+{
+	int val;
+
+	val = snd_pcm_format_signed(format);
+	if (val < 0)
+		return val;
+	return !val;
+}
+
+/**
+ * snd_pcm_format_linear - Check the PCM format is linear
+ * @format: the format to check
+ *
+ * Returns 1 if the given PCM format is linear, 0 if not.
+ */
+int snd_pcm_format_linear(snd_pcm_format_t format)
+{
+	return snd_pcm_format_signed(format) >= 0;
+}
+
+/**
+ * snd_pcm_format_little_endian - Check the PCM format is little-endian
+ * @format: the format to check
+ *
+ * Returns 1 if the given PCM format is little-endian, 0 if
+ * big-endian, or a negative error code if endian not specified.
+ */
+int snd_pcm_format_little_endian(snd_pcm_format_t format)
+{
+	int val;
+	if (format < 0 || format > SNDRV_PCM_FORMAT_LAST)
+		return -EINVAL;
+	if ((val = pcm_formats[format].le) < 0)
+		return -EINVAL;
+	return val;
+}
+
+/**
+ * snd_pcm_format_big_endian - Check the PCM format is big-endian
+ * @format: the format to check
+ *
+ * Returns 1 if the given PCM format is big-endian, 0 if
+ * little-endian, or a negative error code if endian not specified.
+ */
+int snd_pcm_format_big_endian(snd_pcm_format_t format)
+{
+	int val;
+
+	val = snd_pcm_format_little_endian(format);
+	if (val < 0)
+		return val;
+	return !val;
+}
+
+/**
+ * snd_pcm_format_width - return the bit-width of the format
+ * @format: the format to check
+ *
+ * Returns the bit-width of the format, or a negative error code
+ * if unknown format.
+ */
+int snd_pcm_format_width(snd_pcm_format_t format)
+{
+	int val;
+	if (format < 0 || format > SNDRV_PCM_FORMAT_LAST)
+		return -EINVAL;
+	if ((val = pcm_formats[format].width) == 0)
+		return -EINVAL;
+	return val;
+}
+
+/**
+ * snd_pcm_format_physical_width - return the physical bit-width of the format
+ * @format: the format to check
+ *
+ * Returns the physical bit-width of the format, or a negative error code
+ * if unknown format.
+ */
+int snd_pcm_format_physical_width(snd_pcm_format_t format)
+{
+	int val;
+	if (format < 0 || format > SNDRV_PCM_FORMAT_LAST)
+		return -EINVAL;
+	if ((val = pcm_formats[format].phys) == 0)
+		return -EINVAL;
+	return val;
+}
+
+/**
+ * snd_pcm_format_size - return the byte size of samples on the given format
+ * @format: the format to check
+ *
+ * Returns the byte size of the given samples for the format, or a
+ * negative error code if unknown format.
+ */
+ssize_t snd_pcm_format_size(snd_pcm_format_t format, size_t samples)
+{
+	int phys_width = snd_pcm_format_physical_width(format);
+	if (phys_width < 0)
+		return -EINVAL;
+	return samples * phys_width / 8;
+}
+
+/**
+ * snd_pcm_format_silence_64 - return the silent data in 8 bytes array
+ * @format: the format to check
+ *
+ * Returns the format pattern to fill or NULL if error.
+ */
+const unsigned char *snd_pcm_format_silence_64(snd_pcm_format_t format)
+{
+	if (format < 0 || format > SNDRV_PCM_FORMAT_LAST)
+		return NULL;
+	if (! pcm_formats[format].phys)
+		return NULL;
+	return pcm_formats[format].silence;
+}
+
+/**
+ * snd_pcm_format_set_silence - set the silence data on the buffer
+ * @format: the PCM format
+ * @data: the buffer pointer
+ * @samples: the number of samples to set silence
+ *
+ * Sets the silence data on the buffer for the given samples.
+ *
+ * Returns zero if successful, or a negative error code on failure.
+ */
+int snd_pcm_format_set_silence(snd_pcm_format_t format, void *data, unsigned int samples)
+{
+	int width;
+	unsigned char *dst, *pat;
+
+	if (format < 0 || format > SNDRV_PCM_FORMAT_LAST)
+		return -EINVAL;
+	if (samples == 0)
+		return 0;
+	width = pcm_formats[format].phys; /* physical width */
+	pat = pcm_formats[format].silence;
+	if (! width)
+		return -EINVAL;
+	/* signed or 1 byte data */
+	if (pcm_formats[format].signd == 1 || width <= 8) {
+		unsigned int bytes = samples * width / 8;
+		memset(data, *pat, bytes);
+		return 0;
+	}
+	/* non-zero samples, fill using a loop */
+	width /= 8;
+	dst = data;
+#if 0
+	while (samples--) {
+		memcpy(dst, pat, width);
+		dst += width;
+	}
+#else
+	/* a bit optimization for constant width */
+	switch (width) {
+	case 2:
+		while (samples--) {
+			memcpy(dst, pat, 2);
+			dst += 2;
+		}
+		break;
+	case 3:
+		while (samples--) {
+			memcpy(dst, pat, 3);
+			dst += 3;
+		}
+		break;
+	case 4:
+		while (samples--) {
+			memcpy(dst, pat, 4);
+			dst += 4;
+		}
+		break;
+	case 8:
+		while (samples--) {
+			memcpy(dst, pat, 8);
+			dst += 8;
+		}
+		break;
+	}
+#endif
+	return 0;
+}
+
+/* [width][unsigned][bigendian] */
+static int linear_formats[4][2][2] = {
+	{{ SNDRV_PCM_FORMAT_S8, SNDRV_PCM_FORMAT_S8},
+	 { SNDRV_PCM_FORMAT_U8, SNDRV_PCM_FORMAT_U8}},
+	{{SNDRV_PCM_FORMAT_S16_LE, SNDRV_PCM_FORMAT_S16_BE},
+	 {SNDRV_PCM_FORMAT_U16_LE, SNDRV_PCM_FORMAT_U16_BE}},
+	{{SNDRV_PCM_FORMAT_S24_LE, SNDRV_PCM_FORMAT_S24_BE},
+	 {SNDRV_PCM_FORMAT_U24_LE, SNDRV_PCM_FORMAT_U24_BE}},
+	{{SNDRV_PCM_FORMAT_S32_LE, SNDRV_PCM_FORMAT_S32_BE},
+	 {SNDRV_PCM_FORMAT_U32_LE, SNDRV_PCM_FORMAT_U32_BE}}
+};
+
+/**
+ * snd_pcm_build_linear_format - return the suitable linear format for the given condition
+ * @width: the bit-width
+ * @unsignd: 1 if unsigned, 0 if signed.
+ * @big_endian: 1 if big-endian, 0 if little-endian
+ *
+ * Returns the suitable linear format for the given condition.
+ */
+snd_pcm_format_t snd_pcm_build_linear_format(int width, int unsignd, int big_endian)
+{
+	if (width & 7)
+		return SND_PCM_FORMAT_UNKNOWN;
+	width = (width / 8) - 1;
+	if (width < 0 || width >= 4)
+		return SND_PCM_FORMAT_UNKNOWN;
+	return linear_formats[width][!!unsignd][!!big_endian];
+}
+
+/**
+ * snd_pcm_limit_hw_rates - determine rate_min/rate_max fields
+ * @runtime: the runtime instance
+ *
+ * Determines the rate_min and rate_max fields from the rates bits of
+ * the given runtime->hw.
+ *
+ * Returns zero if successful.
+ */
+int snd_pcm_limit_hw_rates(struct snd_pcm_runtime *runtime)
+{
+	static unsigned rates[] = {
+		/* ATTENTION: these values depend on the definition in pcm.h! */
+		5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000,
+		64000, 88200, 96000, 176400, 192000
+	};
+	int i;
+	for (i = 0; i < (int)ARRAY_SIZE(rates); i++) {
+		if (runtime->hw.rates & (1 << i)) {
+			runtime->hw.rate_min = rates[i];
+			break;
+		}
+	}
+	for (i = (int)ARRAY_SIZE(rates) - 1; i >= 0; i--) {
+		if (runtime->hw.rates & (1 << i)) {
+			runtime->hw.rate_max = rates[i];
+			break;
+		}
+	}
+	return 0;
+}
Index: /GPL/branches/alsa-resync1/alsa-kernel/core/pcm_native.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/core/pcm_native.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/core/pcm_native.c	(revision 91)
@@ -0,0 +1,3338 @@
+/*
+ *  Digital Audio (PCM) abstract layer
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <sound/control.h>
+#include <sound/info.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/minors.h>
+#include <linux/file.h>
+
+/*
+ *  Compatibility
+ */
+//#define RULES_DEBUG
+//#define DEBUG_PK
+//#define static
+struct snd_pcm_hw_params_old {
+    unsigned int flags;
+    unsigned int masks[SNDRV_PCM_HW_PARAM_SUBFORMAT -
+                       SNDRV_PCM_HW_PARAM_ACCESS + 1];
+    struct snd_interval intervals[SNDRV_PCM_HW_PARAM_TICK_TIME -
+                                    SNDRV_PCM_HW_PARAM_SAMPLE_BITS + 1];
+    unsigned int rmask;
+    unsigned int cmask;
+    unsigned int info;
+    unsigned int msbits;
+    unsigned int rate_num;
+    unsigned int rate_den;
+    snd_pcm_uframes_t fifo_size;
+    unsigned char reserved[64];
+};
+
+#define SNDRV_PCM_IOCTL_HW_REFINE_OLD _IOWR('A', 0x10, struct snd_pcm_hw_params_old)
+#define SNDRV_PCM_IOCTL_HW_PARAMS_OLD _IOWR('A', 0x11, struct snd_pcm_hw_params_old)
+
+static int snd_pcm_hw_refine_old_user(snd_pcm_substream_t * substream, struct snd_pcm_hw_params_old * _oparams);
+static int snd_pcm_hw_params_old_user(snd_pcm_substream_t * substream, struct snd_pcm_hw_params_old * _oparams);
+
+/*
+ *
+ */
+
+rwlock_t snd_pcm_link_rwlock = RW_LOCK_UNLOCKED;
+static DECLARE_RWSEM(snd_pcm_link_rwsem);
+
+#ifndef TARGET_OS2
+static inline mm_segment_t snd_enter_user(void)
+{
+    mm_segment_t fs = get_fs();
+    set_fs(get_ds());
+    return fs;
+}
+
+static inline void snd_leave_user(mm_segment_t fs)
+{
+    set_fs(fs);
+}
+#endif
+
+int snd_pcm_info(snd_pcm_substream_t * substream, snd_pcm_info_t *info)
+{
+    snd_pcm_runtime_t * runtime;
+    snd_pcm_t *pcm = substream->pcm;
+    snd_pcm_str_t *pstr = substream->pstr;
+
+    snd_assert(substream != NULL, return -ENXIO);
+    memset(info, 0, sizeof(*info));
+    info->card = pcm->card->number;
+    info->device = pcm->device;
+    info->stream = substream->stream;
+    info->subdevice = substream->number;
+    strlcpy(info->id, pcm->id, sizeof(info->id));
+    strlcpy(info->name, pcm->name, sizeof(info->name));
+    info->dev_class = pcm->dev_class;
+    info->dev_subclass = pcm->dev_subclass;
+    info->subdevices_count = pstr->substream_count;
+    info->subdevices_avail = pstr->substream_count - pstr->substream_opened;
+    strlcpy(info->subname, substream->name, sizeof(info->subname));
+    runtime = substream->runtime;
+    /* AB: FIXME!!! This is definitely nonsense */
+    if (runtime) {
+        info->sync = runtime->sync;
+        substream->ops->ioctl(substream, SNDRV_PCM_IOCTL1_INFO, info);
+    }
+    return 0;
+}
+
+int snd_pcm_info_user(snd_pcm_substream_t * substream, snd_pcm_info_t * _info)
+{
+    snd_pcm_info_t info;
+    int err = snd_pcm_info(substream, &info);
+    if (copy_to_user(_info, &info, sizeof(info)))
+        return -EFAULT;
+    return err;
+}
+
+
+#define RULES_DEBUG
+#undef RULES_DEBUG
+
+#ifdef RULES_DEBUG
+#ifdef TARGET_OS2
+#define HW_PARAM(v) #v
+#else
+#define HW_PARAM(v) [SNDRV_PCM_HW_PARAM_##v] = #v
+#endif
+char *snd_pcm_hw_param_names[] = {
+    HW_PARAM(ACCESS),
+    HW_PARAM(FORMAT),
+#ifdef TARGET_OS2
+    HW_PARAM(RATE_MASK),
+#endif
+    HW_PARAM(SUBFORMAT),
+    HW_PARAM(SAMPLE_BITS),
+    HW_PARAM(FRAME_BITS),
+    HW_PARAM(CHANNELS),
+    HW_PARAM(RATE),
+    HW_PARAM(PERIOD_TIME),
+    HW_PARAM(PERIOD_SIZE),
+    HW_PARAM(PERIOD_BYTES),
+    HW_PARAM(PERIODS),
+    HW_PARAM(BUFFER_TIME),
+    HW_PARAM(BUFFER_SIZE),
+    HW_PARAM(BUFFER_BYTES),
+    HW_PARAM(TICK_TIME),
+};
+#endif
+
+int snd_pcm_hw_refine(snd_pcm_substream_t *substream,
+                      snd_pcm_hw_params_t *params)
+{
+    unsigned int k;
+    snd_pcm_hardware_t *hw;
+    snd_interval_t *i = NULL;
+    snd_mask_t *m = NULL;
+    snd_pcm_hw_constraints_t *constrs = &substream->runtime->hw_constraints;
+#ifdef TARGET_OS2
+    unsigned int rstamps[32];
+#else
+    unsigned int rstamps[constrs->rules_num];
+#endif
+    unsigned int vstamps[SNDRV_PCM_HW_PARAM_LAST_INTERVAL + 1];
+    unsigned int stamp = 2;
+    int changed, again;
+
+#ifdef TARGET_OS2
+    if(constrs->rules_num > 32) {
+#ifdef DEBUG
+        dprintf(("snd_pcm_hw_refine error. rules > 32"));
+#endif
+
+        DebugInt3();
+        return EFAULT;
+    }
+#endif
+
+    params->info = 0;
+    params->fifo_size = 0;
+    if (params->rmask & (1 << SNDRV_PCM_HW_PARAM_SAMPLE_BITS))
+        params->msbits = 0;
+    if (params->rmask & (1 << SNDRV_PCM_HW_PARAM_RATE)) {
+        params->rate_num = 0;
+        params->rate_den = 0;
+    }
+
+    for (k = SNDRV_PCM_HW_PARAM_FIRST_MASK; k <= SNDRV_PCM_HW_PARAM_LAST_MASK; k++) {
+        m = hw_param_mask(params, k);
+        if (snd_mask_empty(m))
+        {
+#ifdef DEBUG
+            dprintf(("snd_mask_empty() error"));
+#endif
+            return -EINVAL;
+        }
+        if (!(params->rmask & (1 << k)))
+            continue;
+#ifdef RULES_DEBUG
+        printk("%s = ", snd_pcm_hw_param_names[k]);
+        printk("%04x%04x%04x%04x -> ", m->bits[3], m->bits[2], m->bits[1], m->bits[0]);
+#endif
+        changed = snd_mask_refine(m, constrs_mask(constrs, k));
+#ifdef RULES_DEBUG
+        printk("%04x%04x%04x%04x\n", m->bits[3], m->bits[2], m->bits[1], m->bits[0]);
+#endif
+        if (changed)
+            params->cmask |= 1 << k;
+        if (changed < 0)
+            return changed;
+    }
+
+    for (k = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) {
+        i = hw_param_interval(params, k);
+        if (snd_interval_empty(i))
+        {
+#ifdef DEBUG
+            dprintf(("snd_interval_empty() error"));
+#endif
+            return -EINVAL;
+        }
+        if (!(params->rmask & (1 << k)))
+            continue;
+#ifdef RULES_DEBUG
+        printk("%s = ", snd_pcm_hw_param_names[k]);
+        if (i->empty)
+            printk("empty");
+        else
+            printk("%c%u %u%c",
+                   i->openmin ? '(' : '[', i->min,
+                   i->max, i->openmax ? ')' : ']');
+        printk(" -> ");
+#endif
+        changed = snd_interval_refine(i, constrs_interval(constrs, k));
+#ifdef RULES_DEBUG
+        if (i->empty)
+            printk("empty\n");
+        else
+            printk("%c%u %u%c\n",
+                   i->openmin ? '(' : '[', i->min,
+                   i->max, i->openmax ? ')' : ']');
+#endif
+        if (changed)
+            params->cmask |= 1 << k;
+        if (changed < 0)
+            return changed;
+    }
+
+    for (k = 0; k < constrs->rules_num; k++)
+        rstamps[k] = 0;
+    for (k = 0; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++)
+        vstamps[k] = (params->rmask & (1 << k)) ? 1 : 0;
+
+    //        DebugInt3();
+
+    do {
+        again = 0;
+        for (k = 0; k < constrs->rules_num; k++) {
+            snd_pcm_hw_rule_t *r = &constrs->rules[k];
+            unsigned int d;
+            int doit = 0;
+            if (r->cond && !(r->cond & params->flags))
+                continue;
+            for (d = 0; r->deps[d] >= 0; d++) {
+                if (vstamps[r->deps[d]] > rstamps[k]) {
+                    doit = 1;
+                    break;
+                }
+            }
+            if (!doit)
+                continue;
+#ifdef RULES_DEBUG
+            printk("Rule %d (r->var=%d) [%p]: ", k, r->var, r->func);
+            if (r->var >= 0) {
+                printk("%s = ", snd_pcm_hw_param_names[r->var]);
+                if (hw_is_mask(r->var)) {
+                    m = hw_param_mask(params, r->var);
+                    printk("%x", *m->bits);
+                } else {
+                    i = hw_param_interval(params, r->var);
+                    if (i->empty)
+                        printk("empty");
+                    else
+                        printk("%c%u %u%c",
+                               i->openmin ? '(' : '[', i->min,
+                               i->max, i->openmax ? ')' : ']');
+                }
+            }
+#endif
+            if (r && r->func)
+                changed = r->func(params, r);
+#ifdef RULES_DEBUG
+            if (r->var >= 0) {
+                printk(" -> ");
+                if (hw_is_mask(r->var))
+                    printk("%x", *m->bits);
+                else {
+                    if (i->empty)
+                        printk("empty");
+                    else
+                        printk("%c%u %u%c",
+                               i->openmin ? '(' : '[', i->min,
+                               i->max, i->openmax ? ')' : ']');
+                }
+            }
+            printk("\n");
+#endif
+            rstamps[k] = stamp;
+            if (changed && r->var >= 0) {
+                params->cmask |= (1 << r->var);
+                vstamps[r->var] = stamp;
+                again = 1;
+            }
+            if (changed < 0)
+            {
+#ifdef DEBUG
+                dprintf(("invalid changed = %d rule addr: %x, k = %d",changed,r->func,k));
+#endif
+                return changed;
+            }
+            stamp++;
+        }
+    } while (again);
+
+    if (!params->msbits) {
+        i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS);
+        if (snd_interval_single(i))
+            params->msbits = snd_interval_value(i);
+    }
+
+    if (!params->rate_den) {
+        i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+        if (snd_interval_single(i)) {
+            params->rate_num = snd_interval_value(i);
+            params->rate_den = 1;
+        }
+    }
+
+    hw = &substream->runtime->hw;
+    if (!params->info)
+        params->info = hw->info;
+    if (!params->fifo_size)
+        params->fifo_size = hw->fifo_size;
+    params->rmask = 0;
+    return 0;
+}
+
+static int snd_pcm_hw_refine_user(snd_pcm_substream_t * substream, snd_pcm_hw_params_t * _params)
+{
+    snd_pcm_hw_params_t *params;
+    int err;
+#ifdef DEBUG
+    //dprintf(("snd_pcm_hw_refine_user()"));
+#endif
+    params = kmalloc(sizeof(*params), GFP_KERNEL);
+    if (!params) {
+        err = -ENOMEM;
+        goto out;
+    }
+    if (copy_from_user(params, _params, sizeof(*params))) {
+        err = -EFAULT;
+        goto out;
+    }
+    err = snd_pcm_hw_refine(substream, params);
+    if (copy_to_user(_params, params, sizeof(*params))) {
+        if (!err)
+            err = -EFAULT;
+    }
+out:
+    kfree(params);
+#ifdef DEBUG
+    if (err)
+        dprintf(("snd_pcm_hw_refine_user() rc = %d",err));
+#endif
+
+    return err;
+}
+
+static int snd_pcm_hw_params(snd_pcm_substream_t *substream,
+                             snd_pcm_hw_params_t *params)
+{
+    snd_pcm_runtime_t *runtime;
+    int err;
+    unsigned int bits;
+    snd_pcm_uframes_t frames;
+
+    snd_assert(substream != NULL, return -ENXIO);
+    runtime = substream->runtime;
+    snd_assert(runtime != NULL, return -ENXIO);
+    switch (runtime->status->state) {
+    case SNDRV_PCM_STATE_OPEN:
+    case SNDRV_PCM_STATE_SETUP:
+    case SNDRV_PCM_STATE_PREPARED:
+        break;
+    default:
+        return -EBADFD;
+    }
+#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
+    if (!substream->oss.oss)
+#endif
+        if (atomic_read(&runtime->mmap_count))
+            return -EBADFD;
+
+    params->rmask = ~0U;
+    err = snd_pcm_hw_refine(substream, params);
+    if (err < 0)
+    {
+        printk("hw params refine error = %i\n",err);
+        goto _error;
+    }
+
+    err = snd_pcm_hw_params_choose(substream, params);
+    if (err < 0)
+    {
+        printk("hw params choose error = %i\n",err);
+        goto _error;
+    }
+
+    if (substream->ops->hw_params != NULL) {
+        err = substream->ops->hw_params(substream, params);
+        if (err < 0)
+        {
+            printk("hw params error = %i\n",err);
+            goto _error;
+        }
+    }
+
+    runtime->access = params_access(params);
+    runtime->format = params_format(params);
+    runtime->subformat = params_subformat(params);
+    runtime->channels = params_channels(params);
+    runtime->rate = params_rate(params);
+    runtime->period_size = params_period_size(params);
+    runtime->periods = params_periods(params);
+    runtime->buffer_size = params_buffer_size(params);
+    runtime->tick_time = params_tick_time(params);
+    runtime->info = params->info;
+    runtime->rate_num = params->rate_num;
+    runtime->rate_den = params->rate_den;
+
+    bits = snd_pcm_format_physical_width(runtime->format);
+    runtime->sample_bits = bits;
+    bits *= runtime->channels;
+    runtime->frame_bits = bits;
+    frames = 1;
+    while (bits % 8 != 0) {
+        bits *= 2;
+        frames *= 2;
+    }
+    runtime->byte_align = bits / 8;
+    runtime->min_align = frames;
+
+    /* Default sw params */
+    runtime->tstamp_mode = SNDRV_PCM_TSTAMP_NONE;
+    runtime->period_step = 1;
+    runtime->sleep_min = 0;
+    runtime->control->avail_min = runtime->period_size;
+    runtime->xfer_align = runtime->period_size;
+    runtime->start_threshold = 1;
+    runtime->stop_threshold = runtime->buffer_size;
+    runtime->silence_threshold = 0;
+    runtime->silence_size = 0;
+    runtime->boundary = runtime->buffer_size;
+    while (runtime->boundary * 2 <= LONG_MAX - runtime->buffer_size)
+        runtime->boundary *= 2;
+
+    snd_pcm_timer_resolution_change(substream);
+    runtime->status->state = SNDRV_PCM_STATE_SETUP;
+    return 0;
+    _error:
+        /* hardware might be unuseable from this time,
+         so we force application to retry to set
+         the correct hardware parameter settings */
+        runtime->status->state = SNDRV_PCM_STATE_OPEN;
+        if (substream->ops->hw_free != NULL)
+            substream->ops->hw_free(substream);
+        return err;
+}
+
+static int snd_pcm_hw_params_user(snd_pcm_substream_t * substream, snd_pcm_hw_params_t * _params)
+{
+    snd_pcm_hw_params_t *params;
+    int err;
+    params = kmalloc(sizeof(*params), GFP_KERNEL);
+    if (!params) {
+        err = -ENOMEM;
+        goto out;
+    }
+    if (copy_from_user(params, _params, sizeof(*params))) {
+        err = -EFAULT;
+        goto out;
+    }
+    err = snd_pcm_hw_params(substream, params);
+    if (copy_to_user(_params, params, sizeof(*params))) {
+        if (!err)
+            err = -EFAULT;
+    }
+out:
+    kfree(params);
+    return err;
+}
+
+static int snd_pcm_hw_free(snd_pcm_substream_t * substream)
+{
+    snd_pcm_runtime_t *runtime;
+    int result = 0;
+
+    snd_assert(substream != NULL, return -ENXIO);
+    runtime = substream->runtime;
+    snd_assert(runtime != NULL, return -ENXIO);
+    switch (runtime->status->state) {
+    case SNDRV_PCM_STATE_SETUP:
+    case SNDRV_PCM_STATE_PREPARED:
+        break;
+    default:
+        return -EBADFD;
+    }
+    if (atomic_read(&runtime->mmap_count))
+        return -EBADFD;
+    if (substream->ops->hw_free)
+        result = substream->ops->hw_free(substream);
+    runtime->status->state = SNDRV_PCM_STATE_OPEN;
+    return result;
+}
+
+static int snd_pcm_sw_params(snd_pcm_substream_t * substream, snd_pcm_sw_params_t *params)
+{
+    snd_pcm_runtime_t *runtime;
+    snd_assert(substream != NULL, return -ENXIO);
+    runtime = substream->runtime;
+    snd_assert(runtime != NULL, return -ENXIO);
+    if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+        return -EBADFD;
+
+    if (params->tstamp_mode > SNDRV_PCM_TSTAMP_LAST)
+        return -EINVAL;
+    if (params->avail_min == 0)
+        return -EINVAL;
+    if (params->xfer_align == 0 ||
+        params->xfer_align % runtime->min_align != 0)
+        return -EINVAL;
+    if (params->silence_size >= runtime->boundary) {
+        if (params->silence_threshold != 0)
+            return -EINVAL;
+    } else {
+        if (params->silence_size > params->silence_threshold)
+            return -EINVAL;
+        if (params->silence_threshold > runtime->buffer_size)
+            return -EINVAL;
+    }
+    snd_pcm_stream_lock_irq(substream);
+    runtime->tstamp_mode = params->tstamp_mode;
+    runtime->sleep_min = params->sleep_min;
+    runtime->period_step = params->period_step;
+    runtime->control->avail_min = params->avail_min;
+    runtime->start_threshold = params->start_threshold;
+    runtime->stop_threshold = params->stop_threshold;
+    runtime->silence_threshold = params->silence_threshold;
+    runtime->silence_size = params->silence_size;
+    runtime->xfer_align = params->xfer_align;
+    params->boundary = runtime->boundary;
+    if (snd_pcm_running(substream)) {
+        if (runtime->sleep_min)
+            snd_pcm_tick_prepare(substream);
+        else
+            snd_pcm_tick_set(substream, 0);
+        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
+            runtime->silence_size > 0)
+            snd_pcm_playback_silence(substream, ULONG_MAX);
+        wake_up(&runtime->sleep);
+    }
+    snd_pcm_stream_unlock_irq(substream);
+    return 0;
+}
+
+static int snd_pcm_sw_params_user(snd_pcm_substream_t * substream, snd_pcm_sw_params_t * _params)
+{
+    snd_pcm_sw_params_t params;
+    int err;
+    if (copy_from_user(&params, _params, sizeof(params)))
+        return -EFAULT;
+    err = snd_pcm_sw_params(substream, &params);
+    if (copy_to_user(_params, &params, sizeof(params)))
+        return -EFAULT;
+    return err;
+}
+
+int snd_pcm_status(struct snd_pcm_substream *substream,
+                   struct snd_pcm_status *status)
+{
+    struct snd_pcm_runtime *runtime = substream->runtime;
+
+    snd_pcm_stream_lock_irq(substream);
+    status->state = runtime->status->state;
+    status->suspended_state = runtime->status->suspended_state;
+    if (status->state == SNDRV_PCM_STATE_OPEN)
+        goto _end;
+    status->trigger_tstamp = runtime->trigger_tstamp;
+    if (snd_pcm_running(substream)) {
+        snd_pcm_update_hw_ptr(substream);
+        if (runtime->tstamp_mode & SNDRV_PCM_TSTAMP_MMAP)
+            status->tstamp = runtime->status->tstamp;
+        else
+            getnstimeofday(&status->tstamp);
+    } else
+        getnstimeofday(&status->tstamp);
+    status->appl_ptr = runtime->control->appl_ptr;
+    status->hw_ptr = runtime->status->hw_ptr;
+    if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+        status->avail = snd_pcm_playback_avail(runtime);
+        if (runtime->status->state == SNDRV_PCM_STATE_RUNNING ||
+            runtime->status->state == SNDRV_PCM_STATE_DRAINING)
+            status->delay = runtime->buffer_size - status->avail;
+        else
+            status->delay = 0;
+    } else {
+        status->avail = snd_pcm_capture_avail(runtime);
+        if (runtime->status->state == SNDRV_PCM_STATE_RUNNING)
+            status->delay = status->avail;
+        else
+            status->delay = 0;
+    }
+    status->avail_max = runtime->avail_max;
+    status->overrange = runtime->overrange;
+    runtime->avail_max = 0;
+    runtime->overrange = 0;
+    _end:
+        snd_pcm_stream_unlock_irq(substream);
+        return 0;
+}
+
+static int snd_pcm_status_user(snd_pcm_substream_t * substream, snd_pcm_status_t * _status)
+{
+    snd_pcm_status_t status;
+    snd_pcm_runtime_t *runtime;
+    int res;
+
+    snd_assert(substream != NULL, return -ENXIO);
+    runtime = substream->runtime;
+    memset(&status, 0, sizeof(status));
+    res = snd_pcm_status(substream, &status);
+    if (res < 0)
+        return res;
+    if (copy_to_user(_status, &status, sizeof(status)))
+        return -EFAULT;
+    return 0;
+}
+
+static int snd_pcm_channel_info(snd_pcm_substream_t * substream, snd_pcm_channel_info_t * _info)
+{
+    snd_pcm_channel_info_t info;
+    snd_pcm_runtime_t *runtime;
+    int res;
+    unsigned int channel;
+
+    snd_assert(substream != NULL, return -ENXIO);
+    if (copy_from_user(&info, _info, sizeof(info)))
+        return -EFAULT;
+    channel = info.channel;
+    runtime = substream->runtime;
+    if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+        return -EBADFD;
+    if (channel >= runtime->channels)
+        return -EINVAL;
+    memset(&info, 0, sizeof(info));
+    info.channel = channel;
+    res = substream->ops->ioctl(substream, SNDRV_PCM_IOCTL1_CHANNEL_INFO, &info);
+    if (res < 0)
+        return res;
+    if (copy_to_user(_info, &info, sizeof(info)))
+        return -EFAULT;
+    return 0;
+}
+
+static void snd_pcm_trigger_tstamp(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	if (runtime->trigger_master == NULL)
+		return;
+	if (runtime->trigger_master == substream) {
+		getnstimeofday(&runtime->trigger_tstamp);
+	} else {
+		snd_pcm_trigger_tstamp(runtime->trigger_master);
+		runtime->trigger_tstamp = runtime->trigger_master->runtime->trigger_tstamp;
+	}
+	runtime->trigger_master = NULL;
+}
+
+struct action_ops {
+    int (*pre_action)(snd_pcm_substream_t *substream, int state);
+    int (*do_action)(snd_pcm_substream_t *substream, int state);
+    void (*undo_action)(snd_pcm_substream_t *substream, int state);
+    void (*post_action)(snd_pcm_substream_t *substream, int state);
+};
+
+/*
+ *  this functions is core for handling of linked stream
+ *  Note: the stream state might be changed also on failure
+ *  Note2: call with calling stream lock + link lock
+ */
+static int snd_pcm_action_group(struct action_ops *ops,
+                                snd_pcm_substream_t *substream,
+                                int state, int do_lock)
+{
+    struct list_head *pos;
+    snd_pcm_substream_t *s = NULL;
+    snd_pcm_substream_t *s1;
+    int res = 0;
+
+    snd_pcm_group_for_each(pos, substream) {
+        s = snd_pcm_group_substream_entry(pos);
+        if (do_lock && s != substream)
+            spin_lock(&s->self_group.lock);
+        res = ops->pre_action(s, state);
+        if (res < 0)
+            goto _unlock;
+    }
+    snd_pcm_group_for_each(pos, substream) {
+        s = snd_pcm_group_substream_entry(pos);
+        res = ops->do_action(s, state);
+        if (res < 0) {
+            if (ops->undo_action) {
+                snd_pcm_group_for_each(pos, substream) {
+                    s1 = snd_pcm_group_substream_entry(pos);
+                    if (s1 == s) /* failed stream */
+                        break;
+                    ops->undo_action(s1, state);
+                }
+            }
+            s = NULL; /* unlock all */
+            goto _unlock;
+        }
+    }
+    snd_pcm_group_for_each(pos, substream) {
+        s = snd_pcm_group_substream_entry(pos);
+        ops->post_action(s, state);
+    }
+_unlock:
+    if (do_lock) {
+        /* unlock streams */
+        snd_pcm_group_for_each(pos, substream) {
+            s1 = snd_pcm_group_substream_entry(pos);
+            if (s1 != substream)
+                spin_unlock(&s1->self_group.lock);
+            if (s1 == s)	/* end */
+                break;
+        }
+    }
+    return res;
+}
+
+/*
+ *  Note: call with stream lock
+ */
+static int snd_pcm_action_single(struct action_ops *ops,
+                                 snd_pcm_substream_t *substream,
+                                 int state)
+{
+    int res;
+    res = ops->pre_action(substream, state);
+    if (res < 0)
+        return res;
+    res = ops->do_action(substream, state);
+    if (res == 0)
+        ops->post_action(substream, state);
+    else if (ops->undo_action)
+        ops->undo_action(substream, state);
+
+    return res;
+}
+
+/*
+ *  Note: call with stream lock
+ *
+ * NB2: this won't handle the non-atomic callbacks
+ */
+static int snd_pcm_action(struct action_ops *ops,
+                          snd_pcm_substream_t *substream,
+                          int state)
+{
+    int res;
+
+    if (snd_pcm_stream_linked(substream)) {
+        if (!spin_trylock(&substream->group->lock)) {
+            spin_unlock(&substream->self_group.lock);
+            spin_lock(&substream->group->lock);
+            spin_lock(&substream->self_group.lock);
+        }
+        res = snd_pcm_action_group(ops, substream, state, 1);
+        spin_unlock(&substream->group->lock);
+    } else {
+        res = snd_pcm_action_single(ops, substream, state);
+    }
+    return res;
+}
+
+/*
+ *  Note: don't use any locks before
+ */
+static int snd_pcm_action_lock_irq(struct action_ops *ops,
+                                   snd_pcm_substream_t *substream,
+                                   int state)
+{
+    int res;
+
+    read_lock_irq(&snd_pcm_link_rwlock);
+    if (snd_pcm_stream_linked(substream)) {
+        spin_lock(&substream->group->lock);
+        spin_lock(&substream->self_group.lock);
+        res = snd_pcm_action_group(ops, substream, state, 1);
+        spin_unlock(&substream->self_group.lock);
+        spin_unlock(&substream->group->lock);
+    } else {
+        spin_lock(&substream->self_group.lock);
+        res = snd_pcm_action_single(ops, substream, state);
+        spin_unlock(&substream->self_group.lock);
+    }
+    read_unlock_irq(&snd_pcm_link_rwlock);
+    return res;
+}
+
+/*
+ */
+static int snd_pcm_action_nonatomic(struct action_ops *ops,
+                                    snd_pcm_substream_t *substream,
+                                    int state)
+{
+    int res;
+
+    down_read(&snd_pcm_link_rwsem);
+    if (snd_pcm_stream_linked(substream))
+        res = snd_pcm_action_group(ops, substream, state, 0);
+    else
+        res = snd_pcm_action_single(ops, substream, state);
+    up_read(&snd_pcm_link_rwsem);
+    return res;
+}
+
+/*
+ * start callbacks
+ */
+static int snd_pcm_pre_start(snd_pcm_substream_t *substream, int state)
+{
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    if (runtime->status->state != SNDRV_PCM_STATE_PREPARED)
+        return -EBADFD;
+    if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
+        !snd_pcm_playback_data(substream))
+        return -EPIPE;
+    runtime->trigger_master = substream;
+    return 0;
+}
+
+static int snd_pcm_do_start(snd_pcm_substream_t *substream, int state)
+{
+    if (substream->runtime->trigger_master != substream)
+        return 0;
+    return substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_START);
+}
+
+static void snd_pcm_undo_start(snd_pcm_substream_t *substream, int state)
+{
+    if (substream->runtime->trigger_master == substream)
+        substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_STOP);
+}
+
+static void snd_pcm_post_start(snd_pcm_substream_t *substream, int state)
+{
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    snd_pcm_trigger_tstamp(substream);
+    runtime->status->state = state;
+    if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
+        runtime->silence_size > 0)
+        snd_pcm_playback_silence(substream, ULONG_MAX);
+    if (runtime->sleep_min)
+        snd_pcm_tick_prepare(substream);
+    if (substream->timer)
+        snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MSTART, &runtime->trigger_tstamp);
+}
+
+static struct action_ops snd_pcm_action_start = {
+    /*.pre_action = */  snd_pcm_pre_start,
+    /*.do_action = */   snd_pcm_do_start,
+    snd_pcm_undo_start,
+    /*.post_action = */ snd_pcm_post_start
+};
+
+/**
+ * snd_pcm_start
+ *
+ * Start all linked streams.
+ */
+int snd_pcm_start(snd_pcm_substream_t *substream)
+{
+    return snd_pcm_action(&snd_pcm_action_start, substream, SNDRV_PCM_STATE_RUNNING);
+}
+
+/*
+ * stop callbacks
+ */
+static int snd_pcm_pre_stop(snd_pcm_substream_t *substream, int state)
+{
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+        return -EBADFD;
+    runtime->trigger_master = substream;
+    return 0;
+}
+
+static int snd_pcm_do_stop(snd_pcm_substream_t *substream, int state)
+{
+    if (substream->runtime->trigger_master == substream &&
+        snd_pcm_running(substream))
+        substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_STOP);
+    return 0; /* unconditonally stop all substreams */
+}
+
+static void snd_pcm_post_stop(snd_pcm_substream_t *substream, int state)
+{
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    if (runtime->status->state != state) {
+        snd_pcm_trigger_tstamp(substream);
+        if (substream->timer)
+            snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MSTOP, &runtime->trigger_tstamp);
+        runtime->status->state = state;
+        snd_pcm_tick_set(substream, 0);
+    }
+    wake_up(&runtime->sleep);
+}
+
+static struct action_ops snd_pcm_action_stop = {
+    /*.pre_action = */ snd_pcm_pre_stop,
+    /*.do_action =  */ snd_pcm_do_stop,0,
+    /*.post_action =*/ snd_pcm_post_stop
+};
+
+/**
+ * snd_pcm_stop
+ *
+ * Try to stop all running streams in the substream group.
+ * The state of each stream is changed to the given value after that unconditionally.
+ */
+int snd_pcm_stop(snd_pcm_substream_t *substream, int state)
+{
+    return snd_pcm_action(&snd_pcm_action_stop, substream, state);
+}
+
+/**
+ * snd_pcm_drain_done
+ *
+ * Stop the DMA only when the given stream is playback.
+ * The state is changed to SETUP.
+ * Unlike snd_pcm_stop(), this affects only the given stream.
+ */
+int snd_pcm_drain_done(snd_pcm_substream_t *substream)
+{
+    return snd_pcm_action_single(&snd_pcm_action_stop, substream, SNDRV_PCM_STATE_SETUP);
+}
+
+/*
+ * pause callbacks
+ */
+static int snd_pcm_pre_pause(snd_pcm_substream_t *substream, int push)
+{
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    if (!(runtime->info & SNDRV_PCM_INFO_PAUSE))
+        return -ENOSYS;
+    if (push) {
+        if (runtime->status->state != SNDRV_PCM_STATE_RUNNING)
+            return -EBADFD;
+    } else if (runtime->status->state != SNDRV_PCM_STATE_PAUSED)
+        return -EBADFD;
+    runtime->trigger_master = substream;
+    return 0;
+}
+
+static int snd_pcm_do_pause(snd_pcm_substream_t *substream, int push)
+{
+    if (substream->runtime->trigger_master != substream)
+        return 0;
+    return substream->ops->trigger(substream,
+                                   push ? SNDRV_PCM_TRIGGER_PAUSE_PUSH :
+                                   SNDRV_PCM_TRIGGER_PAUSE_RELEASE);
+}
+
+static void snd_pcm_undo_pause(snd_pcm_substream_t *substream, int push)
+{
+    if (substream->runtime->trigger_master == substream)
+        substream->ops->trigger(substream,
+                                push ? SNDRV_PCM_TRIGGER_PAUSE_RELEASE :
+                                SNDRV_PCM_TRIGGER_PAUSE_PUSH);
+}
+
+static void snd_pcm_post_pause(snd_pcm_substream_t *substream, int push)
+{
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    snd_pcm_trigger_tstamp(substream);
+    if (push) {
+        runtime->status->state = SNDRV_PCM_STATE_PAUSED;
+        if (substream->timer)
+            snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MPAUSE, &runtime->trigger_tstamp);
+        snd_pcm_tick_set(substream, 0);
+        wake_up(&runtime->sleep);
+    } else {
+        runtime->status->state = SNDRV_PCM_STATE_RUNNING;
+        if (runtime->sleep_min)
+            snd_pcm_tick_prepare(substream);
+        if (substream->timer)
+            snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MCONTINUE, &runtime->trigger_tstamp);
+    }
+}
+
+static struct action_ops snd_pcm_action_pause = {
+    /*.pre_action = */ snd_pcm_pre_pause,
+    /*.do_action =  */ snd_pcm_do_pause,
+    /*.undo_action =*/ snd_pcm_undo_pause,
+    /*.post_action =*/ snd_pcm_post_pause
+};
+
+/*
+ * Push/release the pause for all linked streams.
+ */
+static int snd_pcm_pause(snd_pcm_substream_t *substream, int push)
+{
+    return snd_pcm_action(&snd_pcm_action_pause, substream, push);
+}
+
+#ifdef CONFIG_PM
+/* suspend */
+
+static int snd_pcm_pre_suspend(snd_pcm_substream_t *substream, int state)
+{
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    if (runtime->status->state == SNDRV_PCM_STATE_SUSPENDED)
+        return -EBUSY;
+    runtime->trigger_master = substream;
+    return 0;
+}
+
+static int snd_pcm_do_suspend(snd_pcm_substream_t *substream, int state)
+{
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    if (runtime->trigger_master != substream)
+        return 0;
+    if (! snd_pcm_running(substream))
+        return 0;
+    substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_SUSPEND);
+    return 0; /* suspend unconditionally */
+}
+
+static void snd_pcm_post_suspend(snd_pcm_substream_t *substream, int state)
+{
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    snd_pcm_trigger_tstamp(substream);
+    if (substream->timer)
+        snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MSUSPEND, &runtime->trigger_tstamp);
+    runtime->status->suspended_state = runtime->status->state;
+    runtime->status->state = SNDRV_PCM_STATE_SUSPENDED;
+    snd_pcm_tick_set(substream, 0);
+    wake_up(&runtime->sleep);
+}
+
+static struct action_ops snd_pcm_action_suspend = {
+    /*.pre_action = */ snd_pcm_pre_suspend,
+    /*.do_action =  */ snd_pcm_do_suspend, 0,
+    /*.post_action =*/ snd_pcm_post_suspend
+};
+
+/**
+ * snd_pcm_suspend
+ *
+ * Trigger SUSPEND to all linked streams.
+ * After this call, all streams are changed to SUSPENDED state.
+ */
+int snd_pcm_suspend(snd_pcm_substream_t *substream)
+{
+    return snd_pcm_action(&snd_pcm_action_suspend, substream, 0);
+}
+
+/**
+ * snd_pcm_suspend_all
+ *
+ * Trigger SUSPEND to all substreams in the given pcm.
+ * After this call, all streams are changed to SUSPENDED state.
+ */
+int snd_pcm_suspend_all(snd_pcm_t *pcm)
+{
+    snd_pcm_substream_t *substream;
+    int stream, err = 0;
+
+    for (stream = 0; stream < 2; stream++) {
+        for (substream = pcm->streams[stream].substream; substream; substream = substream->next) {
+            /* FIXME: the open/close code should lock this as well */
+            if (substream->runtime == NULL)
+                continue;
+            snd_pcm_stream_lock(substream);
+            if (substream->runtime->status->state != SNDRV_PCM_STATE_SUSPENDED)
+                err = snd_pcm_suspend(substream);
+            snd_pcm_stream_unlock(substream);
+            if (err < 0)
+                return err;
+        }
+    }
+    return 0;
+}
+
+/* resume */
+
+static int snd_pcm_pre_resume(snd_pcm_substream_t *substream, int state)
+{
+    snd_pcm_runtime_t *runtime;
+    if (substream) runtime = substream->runtime;
+    if (!runtime) return 0;
+    if (!(runtime->info & SNDRV_PCM_INFO_RESUME))
+        return -ENOSYS;
+    runtime->trigger_master = substream;
+    return 0;
+}
+
+static int snd_pcm_do_resume(snd_pcm_substream_t *substream, int state)
+{
+    snd_pcm_runtime_t *runtime;
+    if (substream) runtime = substream->runtime;
+    if (!runtime) return 0;
+    if (runtime->trigger_master != substream)
+        return 0;
+    /* DMA not running previously? */
+    if (runtime->status->suspended_state != SNDRV_PCM_STATE_RUNNING &&
+        (runtime->status->suspended_state != SNDRV_PCM_STATE_DRAINING ||
+         substream->stream != SNDRV_PCM_STREAM_PLAYBACK))
+        return 0;
+    if (substream->ops->trigger)
+        return substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_RESUME);
+    else return 0;
+}
+
+static void snd_pcm_undo_resume(snd_pcm_substream_t *substream, int state)
+{
+    if (substream->runtime->trigger_master == substream &&
+        snd_pcm_running(substream))
+        substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_SUSPEND);
+}
+
+static void snd_pcm_post_resume(snd_pcm_substream_t *substream, int state)
+{
+    snd_pcm_runtime_t *runtime;
+    if (substream) runtime = substream->runtime;
+    if (!runtime) return;
+    snd_pcm_trigger_tstamp(substream);
+    if (substream->timer)
+        snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MRESUME, &runtime->trigger_tstamp);
+    runtime->status->state = runtime->status->suspended_state;
+    if (runtime->sleep_min)
+        snd_pcm_tick_prepare(substream);
+}
+
+static struct action_ops snd_pcm_action_resume = {
+    /*.pre_action =  */snd_pcm_pre_resume,
+    /*.do_action =   */snd_pcm_do_resume,
+    /*.undo_action = */snd_pcm_undo_resume,
+    /*.post_action = */snd_pcm_post_resume
+};
+
+static int snd_pcm_resume(snd_pcm_substream_t *substream)
+{
+    snd_card_t *card;
+    int res;
+
+    if(substream && substream->pcm)
+    {
+        card = substream->pcm->card;
+        snd_power_lock(card);
+        if ((res = snd_power_wait(card, SNDRV_CTL_POWER_D0, substream->ffile)) >= 0)
+            res = snd_pcm_action_lock_irq(&snd_pcm_action_resume, substream, 0);
+        snd_power_unlock(card);
+    }
+    return res;
+}
+
+#else
+
+static int snd_pcm_resume(snd_pcm_substream_t *substream)
+{
+    return -ENOSYS;
+}
+
+#endif /* CONFIG_PM */
+
+/*
+ * xrun ioctl
+ *
+ * Change the RUNNING stream(s) to XRUN state.
+ */
+
+static int snd_pcm_xrun(snd_pcm_substream_t *substream)
+{
+    snd_card_t *card = substream->pcm->card;
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    int result;
+
+    snd_power_lock(card);
+    if (runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {
+        result = snd_power_wait(card, SNDRV_CTL_POWER_D0, substream->ffile);
+        if (result < 0)
+            goto _unlock;
+    }
+    snd_pcm_stream_lock_irq(substream);
+    switch (runtime->status->state) {
+    case SNDRV_PCM_STATE_XRUN:
+        result = 0;	/* already there */
+        break;
+    case SNDRV_PCM_STATE_RUNNING:
+        result = snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
+        break;
+    default:
+        result = -EBADFD;
+    }
+    snd_pcm_stream_unlock_irq(substream);
+ _unlock:
+    snd_power_unlock(card);
+    return result;
+}
+
+/*
+ * reset ioctl
+ */
+static int snd_pcm_pre_reset(snd_pcm_substream_t * substream, int state)
+{
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    switch (runtime->status->state) {
+    case SNDRV_PCM_STATE_RUNNING:
+    case SNDRV_PCM_STATE_PREPARED:
+    case SNDRV_PCM_STATE_PAUSED:
+    case SNDRV_PCM_STATE_SUSPENDED:
+        return 0;
+    default:
+        return -EBADFD;
+    }
+}
+
+static int snd_pcm_do_reset(snd_pcm_substream_t * substream, int state)
+{
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    int err = substream->ops->ioctl(substream, SNDRV_PCM_IOCTL1_RESET, 0);
+    if (err < 0)
+        return err;
+    // snd_assert(runtime->status->hw_ptr < runtime->buffer_size, );
+    runtime->hw_ptr_base = 0;
+    runtime->hw_ptr_interrupt = runtime->status->hw_ptr - runtime->status->hw_ptr % runtime->period_size;
+    runtime->silence_start = runtime->status->hw_ptr;
+    runtime->silence_filled = 0;
+    return 0;
+}
+
+static void snd_pcm_post_reset(snd_pcm_substream_t * substream, int state)
+{
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    runtime->control->appl_ptr = runtime->status->hw_ptr;
+    if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
+        runtime->silence_size > 0)
+        snd_pcm_playback_silence(substream, ULONG_MAX);
+}
+
+static struct action_ops snd_pcm_action_reset = {
+    /*.pre_action =  */snd_pcm_pre_reset,
+    /*.do_action =   */snd_pcm_do_reset, 0,
+    /*.post_action = */snd_pcm_post_reset
+};
+
+
+static int snd_pcm_reset(snd_pcm_substream_t *substream)
+{
+    return snd_pcm_action_nonatomic(&snd_pcm_action_reset, substream, 0);
+}
+
+/*
+ * prepare ioctl
+ */
+static int snd_pcm_pre_prepare(snd_pcm_substream_t * substream, int state)
+{
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+        return -EBADFD;
+    if (snd_pcm_running(substream))
+        return -EBUSY;
+
+    return 0;
+}
+
+static int snd_pcm_do_prepare(snd_pcm_substream_t * substream, int state)
+{
+    int err;
+    err = substream->ops->prepare(substream);
+    if (err < 0)
+        return err;
+    return snd_pcm_do_reset(substream, 0);
+}
+
+static void snd_pcm_post_prepare(snd_pcm_substream_t * substream, int state)
+{
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    runtime->control->appl_ptr = runtime->status->hw_ptr;
+    runtime->status->state = SNDRV_PCM_STATE_PREPARED;
+}
+
+static struct action_ops snd_pcm_action_prepare = {
+    /*.pre_action =  */snd_pcm_pre_prepare,
+    /*.do_action =   */snd_pcm_do_prepare, 0,
+    /*.post_action = */snd_pcm_post_prepare
+};
+
+/**
+ * snd_pcm_prepare
+ */
+int snd_pcm_prepare(snd_pcm_substream_t *substream)
+{
+    int res;
+    snd_card_t *card = substream->pcm->card;
+
+    snd_power_lock(card);
+    if ((res = snd_power_wait(card, SNDRV_CTL_POWER_D0, substream->ffile)) >= 0)
+        res = snd_pcm_action_nonatomic(&snd_pcm_action_prepare, substream, 0);
+    snd_power_unlock(card);
+    return res;
+}
+
+#define static
+/*
+ * drain ioctl
+ */
+
+static int snd_pcm_pre_drain_init(snd_pcm_substream_t * substream, int state)
+{
+    if (substream->ffile->f_flags & O_NONBLOCK)
+        return -EAGAIN;
+    substream->runtime->trigger_master = substream;
+    return 0;
+}
+static int snd_pcm_do_drain_init(snd_pcm_substream_t * substream, int state)
+{
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+        switch (runtime->status->state) {
+        case SNDRV_PCM_STATE_PREPARED:
+            /* start playback stream if possible */
+            if (! snd_pcm_playback_empty(substream)) {
+                snd_pcm_do_start(substream, SNDRV_PCM_STATE_DRAINING);
+                snd_pcm_post_start(substream, SNDRV_PCM_STATE_DRAINING);
+            }
+            break;
+        case SNDRV_PCM_STATE_RUNNING:
+            runtime->status->state = SNDRV_PCM_STATE_DRAINING;
+            break;
+        default:
+            break;
+        }
+    } else {
+        /* stop running stream */
+        if (runtime->status->state == SNDRV_PCM_STATE_RUNNING) {
+            int state = snd_pcm_capture_avail(runtime) > 0 ?
+                SNDRV_PCM_STATE_DRAINING : SNDRV_PCM_STATE_SETUP;
+            snd_pcm_do_stop(substream, state);
+            snd_pcm_post_stop(substream, state);
+        }
+    }
+    return 0;
+}
+
+static void snd_pcm_post_drain_init(snd_pcm_substream_t * substream, int state)
+{
+}
+
+static struct action_ops snd_pcm_action_drain_init = {
+    /*.pre_action =*/ snd_pcm_pre_drain_init,
+    /*.do_action = */ snd_pcm_do_drain_init, 0,
+    /*.post_action =*/snd_pcm_post_drain_init
+};
+
+struct drain_rec {
+    snd_pcm_substream_t *substream;
+    wait_queue_t wait;
+    snd_pcm_uframes_t stop_threshold;
+};
+
+static int snd_pcm_drop(snd_pcm_substream_t *substream);
+
+/*
+ * Drain the stream(s).
+ * When the substream is linked, sync until the draining of all playback streams
+ * is finished.
+ * After this call, all streams are supposed to be either SETUP or DRAINING
+ * (capture only) state.
+ */
+static int snd_pcm_drain(snd_pcm_substream_t *substream)
+{
+    snd_card_t *card;
+    snd_pcm_runtime_t *runtime;
+    struct list_head *pos;
+    int result = 0;
+
+    int i, num_drecs;
+
+    struct drain_rec *drec, drec_tmp, *d;
+
+    snd_assert(substream != NULL, return -ENXIO);
+    card = substream->pcm->card;
+    runtime = substream->runtime;
+
+    if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+        return -EBADFD;
+
+    snd_power_lock(card);
+    if (runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {
+        result = snd_power_wait(card, SNDRV_CTL_POWER_D0, substream->ffile);
+        if (result < 0) {
+            snd_power_unlock(card);
+            return result;
+        }
+    }
+    /* allocate temporary record for drain sync */
+    down_read(&snd_pcm_link_rwsem);
+    if (snd_pcm_stream_linked(substream)) {
+        drec = kmalloc(substream->group->count * sizeof(*drec), GFP_KERNEL);
+        if (! drec) {
+            up_read(&snd_pcm_link_rwsem);
+            snd_power_unlock(card);
+            return -ENOMEM;
+        }
+    } else
+        drec = &drec_tmp;
+
+    //DevBeep(1000, 100);
+    /* count only playback streams */
+    num_drecs = 0;
+    snd_pcm_group_for_each(pos, substream) {
+        snd_pcm_substream_t *s = snd_pcm_group_substream_entry(pos);
+        runtime = s->runtime;
+        if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+            d = &drec[num_drecs++];
+            d->substream = s;
+            init_waitqueue_entry(&d->wait, current);
+            add_wait_queue(&runtime->sleep, &d->wait);
+            /* stop_threshold fixup to avoid endless loop when
+             * stop_threshold > buffer_size
+             */
+            d->stop_threshold = runtime->stop_threshold;
+            if (runtime->stop_threshold > runtime->buffer_size)
+                runtime->stop_threshold = runtime->buffer_size;
+        }
+    }
+    up_read(&snd_pcm_link_rwsem);
+
+    snd_pcm_stream_lock_irq(substream);
+    /* resume pause */
+    if (runtime->status->state == SNDRV_PCM_STATE_PAUSED)
+        snd_pcm_pause(substream, 0);
+
+    /* pre-start/stop - all running streams are changed to DRAINING state */
+    result = snd_pcm_action(&snd_pcm_action_drain_init, substream, 0);
+    if (result < 0) {
+        snd_pcm_stream_unlock_irq(substream);
+        goto _error;
+    }
+
+    for (;;) {
+        long tout;
+        if (signal_pending(current)) {
+            result = -ERESTARTSYS;
+            break;
+        }
+        /* all finished? */
+        for (i = 0; i < num_drecs; i++) {
+            runtime = drec[i].substream->runtime;
+            if (runtime->status->state == SNDRV_PCM_STATE_DRAINING)
+                break;
+        }
+        if (i == num_drecs)
+            break; /* yes, all drained */
+
+        set_current_state(TASK_INTERRUPTIBLE);
+        snd_pcm_stream_unlock_irq(substream);
+        snd_power_unlock(card);
+        tout = schedule_timeout(10 * HZ);
+        snd_power_lock(card);
+        snd_pcm_stream_lock_irq(substream);
+        if (tout == 0) {
+            if (substream->runtime->status->state == SNDRV_PCM_STATE_SUSPENDED)
+                result = -ESTRPIPE;
+            else {
+                snd_printd("playback drain error (DMA or IRQ trouble?)\n");
+                snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);
+                result = -EIO;
+            }
+            break;
+        }
+    }
+    snd_pcm_stream_unlock_irq(substream);
+
+_error:
+    for (i = 0; i < num_drecs; i++) {
+        d = &drec[i];
+        runtime = d->substream->runtime;
+        remove_wait_queue(&runtime->sleep, &d->wait);
+        runtime->stop_threshold = d->stop_threshold;
+    }
+    if (drec && drec != &drec_tmp)
+        kfree(drec);
+    snd_power_unlock(card);
+
+    return result;
+}
+
+/*
+ * drop ioctl
+ *
+ * Immediately put all linked substreams into SETUP state.
+ */
+static int snd_pcm_drop(snd_pcm_substream_t *substream)
+{
+    snd_pcm_runtime_t *runtime;
+    snd_card_t *card;
+    int result = 0;
+
+    snd_assert(substream != NULL, return -ENXIO);
+    runtime = substream->runtime;
+    snd_assert(runtime != NULL, return -ENXIO);
+    if (substream->pcm)
+        card = substream->pcm->card;
+
+    if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+        return -EBADFD;
+
+    snd_power_lock(card);
+    if (runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {
+        result = snd_power_wait(card, SNDRV_CTL_POWER_D0, substream->ffile);
+        if (result < 0)
+            goto _unlock;
+    }
+    snd_pcm_stream_lock_irq(substream);
+    /* resume pause */
+    if (runtime->status->state == SNDRV_PCM_STATE_PAUSED)
+        snd_pcm_pause(substream, 0);
+    snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);
+    /* runtime->control->appl_ptr = runtime->status->hw_ptr; */
+    snd_pcm_stream_unlock_irq(substream);
+_unlock:
+    snd_power_unlock(card);
+    return result;
+}
+
+#ifndef TARGET_OS2
+/* WARNING: Don't forget to fput back the file */
+extern int snd_major;
+static struct file *snd_pcm_file_fd(int fd)
+{
+    struct file *file;
+    struct inode *inode;
+    unsigned short minor;
+    file = fget(fd);
+    if (!file)
+        return 0;
+    inode = file->f_dentry->d_inode;
+    if (!S_ISCHR(inode->i_mode) ||
+        MAJOR(inode->i_rdev) != CONFIG_SND_MAJOR) {
+        fput(file);
+        return 0;
+    }
+    minor = MINOR(inode->i_rdev);
+    if (minor >= 256 ||
+        minor % SNDRV_MINOR_DEVICES < SNDRV_MINOR_PCM_PLAYBACK) {
+        fput(file);
+        return 0;
+    }
+    return file;
+}
+#endif
+
+/*
+ * PCM link handling
+ */
+static int snd_pcm_link(snd_pcm_substream_t *substream, int fd)
+{
+    int res = 0;
+    struct file *file;
+    snd_pcm_file_t *pcm_file;
+    snd_pcm_substream_t *substream1;
+
+#ifdef TARGET_OS2
+    file = (struct file *)substream->file;
+#else
+    file = snd_pcm_file_fd(fd);
+#endif
+    if (!file)
+        return -EBADFD;
+    pcm_file = file->private_data;
+    substream1 = pcm_file->substream;
+    down_write(&snd_pcm_link_rwsem);
+    write_lock_irq(&snd_pcm_link_rwlock);
+    if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN ||
+        substream->runtime->status->state != substream1->runtime->status->state) {
+        res = -EBADFD;
+        goto _end;
+    }
+    if (snd_pcm_stream_linked(substream1)) {
+        res = -EALREADY;
+        goto _end;
+    }
+    if (!snd_pcm_stream_linked(substream)) {
+        substream->group = kmalloc(sizeof(snd_pcm_group_t), GFP_ATOMIC);
+        if (substream->group == NULL) {
+            res = -ENOMEM;
+            goto _end;
+        }
+        spin_lock_init(&substream->group->lock);
+        INIT_LIST_HEAD(&substream->group->substreams);
+        list_add_tail(&substream->link_list, &substream->group->substreams);
+        substream->group->count = 1;
+    }
+    list_add_tail(&substream1->link_list, &substream->group->substreams);
+    substream->group->count++;
+    substream1->group = substream->group;
+    _end:
+        write_unlock_irq(&snd_pcm_link_rwlock);
+        up_write(&snd_pcm_link_rwsem);
+#ifndef TARGET_OS2
+        fput(file);
+#endif
+        return res;
+}
+
+static void relink_to_local(snd_pcm_substream_t *substream)
+{
+    substream->group = &substream->self_group;
+    INIT_LIST_HEAD(&substream->self_group.substreams);
+    list_add_tail(&substream->link_list, &substream->self_group.substreams);
+}
+
+static int snd_pcm_unlink(snd_pcm_substream_t *substream)
+{
+    struct list_head *pos;
+    int res = 0;
+
+    down_write(&snd_pcm_link_rwsem);
+    write_lock_irq(&snd_pcm_link_rwlock);
+    if (!snd_pcm_stream_linked(substream)) {
+        res = -EALREADY;
+        goto _end;
+    }
+    list_del(&substream->link_list);
+    substream->group->count--;
+    if (substream->group->count == 1) {     /* detach the last stream, too */
+        snd_pcm_group_for_each(pos, substream) {
+            relink_to_local(snd_pcm_group_substream_entry(pos));
+            break;
+        }
+        kfree(substream->group);
+    }
+    relink_to_local(substream);
+_end:
+    write_unlock_irq(&snd_pcm_link_rwlock);
+    up_write(&snd_pcm_link_rwsem);
+    return res;
+}
+
+/*
+ * hw configurator
+ */
+static int snd_pcm_hw_rule_mul(snd_pcm_hw_params_t *params,
+                               snd_pcm_hw_rule_t *rule)
+{
+    snd_interval_t t;
+    snd_interval_mul(hw_param_interval_c(params, rule->deps[0]),
+                     hw_param_interval_c(params, rule->deps[1]), &t);
+    return snd_interval_refine(hw_param_interval(params, rule->var), &t);
+}
+
+static int snd_pcm_hw_rule_div(snd_pcm_hw_params_t *params,
+                               snd_pcm_hw_rule_t *rule)
+{
+    snd_interval_t t;
+    snd_interval_div(hw_param_interval_c(params, rule->deps[0]),
+                     hw_param_interval_c(params, rule->deps[1]), &t);
+    return snd_interval_refine(hw_param_interval(params, rule->var), &t);
+}
+
+static int snd_pcm_hw_rule_muldivk(snd_pcm_hw_params_t *params,
+                                   snd_pcm_hw_rule_t *rule)
+{
+    snd_interval_t t;
+    snd_interval_muldivk(hw_param_interval_c(params, rule->deps[0]),
+                         hw_param_interval_c(params, rule->deps[1]),
+                         (unsigned long) rule->private, &t);
+    return snd_interval_refine(hw_param_interval(params, rule->var), &t);
+}
+
+static int snd_pcm_hw_rule_mulkdiv(snd_pcm_hw_params_t *params,
+                                   snd_pcm_hw_rule_t *rule)
+{
+    snd_interval_t t;
+    snd_interval_mulkdiv(hw_param_interval_c(params, rule->deps[0]),
+                         (unsigned long) rule->private,
+                         hw_param_interval_c(params, rule->deps[1]), &t);
+    return snd_interval_refine(hw_param_interval(params, rule->var), &t);
+}
+
+static int snd_pcm_hw_rule_format(snd_pcm_hw_params_t *params,
+                                  snd_pcm_hw_rule_t *rule)
+{
+    unsigned int k;
+    snd_interval_t *i = hw_param_interval(params, rule->deps[0]);
+    snd_mask_t m;
+    snd_mask_t *mask = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+    snd_mask_any(&m);
+    for (k = 0; k <= SNDRV_PCM_FORMAT_LAST; ++k) {
+        int bits;
+        if (! snd_mask_test(mask, k))
+            continue;
+        bits = snd_pcm_format_physical_width(k);
+        if (bits <= 0)
+            continue; /* ignore invalid formats */
+        if ((unsigned)bits < i->min || (unsigned)bits > i->max)
+            snd_mask_reset(&m, k);
+    }
+    return snd_mask_refine(mask, &m);
+}
+
+static int snd_pcm_hw_rule_sample_bits(snd_pcm_hw_params_t *params,
+                                       snd_pcm_hw_rule_t *rule)
+{
+    snd_interval_t t;
+    unsigned int k;
+    t.min = UINT_MAX;
+    t.max = 0;
+    t.openmin = 0;
+    t.openmax = 0;
+    for (k = 0; k <= SNDRV_PCM_FORMAT_LAST; ++k) {
+        int bits;
+        if (! snd_mask_test(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), k))
+            continue;
+        bits = snd_pcm_format_physical_width(k);
+        if (bits <= 0)
+            continue; /* ignore invalid formats */
+        if (t.min > (unsigned)bits)
+            t.min = bits;
+        if (t.max < (unsigned)bits)
+            t.max = bits;
+    }
+    t.integer = 1;
+    return snd_interval_refine(hw_param_interval(params, rule->var), &t);
+}
+
+#if SNDRV_PCM_RATE_5512 != 1 << 0 || SNDRV_PCM_RATE_192000 != 1 << 12
+#error "Change this table"
+#endif
+
+static unsigned int rates[] = { 5512, 8000, 11025, 16000, 22050, 32000, 44100,
+48000, 64000, 88200, 96000, 176400, 192000 };
+
+#define RATES (sizeof(rates) / sizeof(rates[0]))
+
+static int snd_pcm_hw_rule_rate(snd_pcm_hw_params_t *params,
+                                snd_pcm_hw_rule_t *rule)
+{
+    snd_pcm_hardware_t *hw = rule->private;
+    return snd_interval_list(hw_param_interval(params, rule->var), RATES, rates, hw->rates);
+}
+
+static int snd_pcm_hw_rule_buffer_bytes_max(snd_pcm_hw_params_t *params,
+                                            snd_pcm_hw_rule_t *rule)
+{
+    snd_interval_t t;
+    snd_pcm_substream_t *substream = rule->private;
+    t.min = 0;
+    t.max = substream->buffer_bytes_max;
+    t.openmin = 0;
+    t.openmax = 0;
+    t.integer = 1;
+    return snd_interval_refine(hw_param_interval(params, rule->var), &t);
+}
+
+int snd_pcm_hw_constraints_init(snd_pcm_substream_t *substream)
+{
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    snd_pcm_hw_constraints_t *constrs = &runtime->hw_constraints;
+    int k, err;
+
+    for (k = SNDRV_PCM_HW_PARAM_FIRST_MASK; k <= SNDRV_PCM_HW_PARAM_LAST_MASK; k++) {
+        snd_mask_any(constrs_mask(constrs, k));
+    }
+
+    for (k = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) {
+        snd_interval_any(constrs_interval(constrs, k));
+    }
+
+    snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_CHANNELS));
+    snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_BUFFER_SIZE));
+    snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_BUFFER_BYTES));
+    snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_SAMPLE_BITS));
+    snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_FRAME_BITS));
+
+    err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT,
+                              snd_pcm_hw_rule_format, 0,
+                              SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1);
+    if (err < 0)
+        return err;
+    err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
+                              snd_pcm_hw_rule_sample_bits, 0,
+                              SNDRV_PCM_HW_PARAM_FORMAT,
+                              SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1);
+    if (err < 0)
+        return err;
+    err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
+                              snd_pcm_hw_rule_div, 0,
+                              SNDRV_PCM_HW_PARAM_FRAME_BITS, SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+    if (err < 0)
+        return err;
+    err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FRAME_BITS,
+                              snd_pcm_hw_rule_mul, 0,
+                              SNDRV_PCM_HW_PARAM_SAMPLE_BITS, SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+    if (err < 0)
+        return err;
+    err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FRAME_BITS,
+                              snd_pcm_hw_rule_mulkdiv, (void*) 8,
+                              SNDRV_PCM_HW_PARAM_PERIOD_BYTES, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, -1);
+    if (err < 0)
+        return err;
+    err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FRAME_BITS,
+                              snd_pcm_hw_rule_mulkdiv, (void*) 8,
+                              SNDRV_PCM_HW_PARAM_BUFFER_BYTES, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, -1);
+    if (err < 0)
+        return err;
+    err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+                              snd_pcm_hw_rule_div, 0,
+                              SNDRV_PCM_HW_PARAM_FRAME_BITS, SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1);
+    if (err < 0)
+        return err;
+#ifdef TARGET_OS2
+    err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+                              snd_pcm_hw_rule_mulkdiv, (void*) 1000000,
+                              SNDRV_PCM_HW_PARAM_PERIOD_SIZE, SNDRV_PCM_HW_PARAM_PERIOD_TIME, -1);
+    if (err < 0)
+        return err;
+    err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+                              snd_pcm_hw_rule_mulkdiv, (void*) 1000000,
+                              SNDRV_PCM_HW_PARAM_BUFFER_SIZE, SNDRV_PCM_HW_PARAM_BUFFER_TIME, -1);
+    if (err < 0)
+        return err;
+#endif
+    err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIODS,
+                              snd_pcm_hw_rule_div, 0,
+                              SNDRV_PCM_HW_PARAM_BUFFER_SIZE, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, -1);
+    if (err < 0)
+        return err;
+    err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+                              snd_pcm_hw_rule_div, 0,
+                              SNDRV_PCM_HW_PARAM_BUFFER_SIZE, SNDRV_PCM_HW_PARAM_PERIODS, -1);
+    if (err < 0)
+        return err;
+    err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+                              snd_pcm_hw_rule_mulkdiv, (void*) 8,
+                              SNDRV_PCM_HW_PARAM_PERIOD_BYTES, SNDRV_PCM_HW_PARAM_FRAME_BITS, -1);
+    if (err < 0)
+        return err;
+#ifdef TARGET_OS2
+    err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+                              snd_pcm_hw_rule_muldivk, (void*) 1000000,
+                              SNDRV_PCM_HW_PARAM_PERIOD_TIME, SNDRV_PCM_HW_PARAM_RATE, -1);
+    if (err < 0)
+        return err;
+#endif
+    err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
+                              snd_pcm_hw_rule_mul, 0,
+                              SNDRV_PCM_HW_PARAM_PERIOD_SIZE, SNDRV_PCM_HW_PARAM_PERIODS, -1);
+    if (err < 0)
+        return err;
+    err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
+                              snd_pcm_hw_rule_mulkdiv, (void*) 8,
+                              SNDRV_PCM_HW_PARAM_BUFFER_BYTES, SNDRV_PCM_HW_PARAM_FRAME_BITS, -1);
+    if (err < 0)
+        return err;
+#ifdef TARGET_OS2
+    err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
+                              snd_pcm_hw_rule_muldivk, (void*) 1000000,
+                              SNDRV_PCM_HW_PARAM_BUFFER_TIME, SNDRV_PCM_HW_PARAM_RATE, -1);
+    if (err < 0)
+        return err;
+#endif
+    err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
+                              snd_pcm_hw_rule_muldivk, (void*) 8,
+                              SNDRV_PCM_HW_PARAM_PERIOD_SIZE, SNDRV_PCM_HW_PARAM_FRAME_BITS, -1);
+    if (err < 0)
+        return err;
+    err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+                              snd_pcm_hw_rule_muldivk, (void*) 8,
+                              SNDRV_PCM_HW_PARAM_BUFFER_SIZE, SNDRV_PCM_HW_PARAM_FRAME_BITS, -1);
+    if (err < 0)
+        return err;
+#ifdef TARGET_OS2
+    err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_TIME,
+                              snd_pcm_hw_rule_mulkdiv, (void*) 1000000,
+                              SNDRV_PCM_HW_PARAM_PERIOD_SIZE, SNDRV_PCM_HW_PARAM_RATE, -1);
+    if (err < 0)
+        return err;
+    err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_TIME,
+                              snd_pcm_hw_rule_mulkdiv, (void*) 1000000,
+                              SNDRV_PCM_HW_PARAM_BUFFER_SIZE, SNDRV_PCM_HW_PARAM_RATE, -1);
+    if (err < 0)
+        return err;
+#endif
+    return 0;
+}
+
+int snd_pcm_hw_constraints_complete(snd_pcm_substream_t *substream)
+{
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    snd_pcm_hardware_t *hw = &runtime->hw;
+    int err;
+    unsigned int mask = 0;
+
+    if (hw->info & SNDRV_PCM_INFO_INTERLEAVED)
+        mask |= 1 << SNDRV_PCM_ACCESS_RW_INTERLEAVED;
+    if (hw->info & SNDRV_PCM_INFO_NONINTERLEAVED)
+        mask |= 1 << SNDRV_PCM_ACCESS_RW_NONINTERLEAVED;
+    if (hw->info & SNDRV_PCM_INFO_MMAP) {
+        if (hw->info & SNDRV_PCM_INFO_INTERLEAVED)
+            mask |= 1 << SNDRV_PCM_ACCESS_MMAP_INTERLEAVED;
+        if (hw->info & SNDRV_PCM_INFO_NONINTERLEAVED)
+            mask |= 1 << SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED;
+        if (hw->info & SNDRV_PCM_INFO_COMPLEX)
+            mask |= 1 << SNDRV_PCM_ACCESS_MMAP_COMPLEX;
+    }
+    err = snd_pcm_hw_constraint_mask(runtime, SNDRV_PCM_HW_PARAM_ACCESS, mask);
+    snd_assert(err >= 0, return -EINVAL);
+
+    err = snd_pcm_hw_constraint_mask64(runtime, SNDRV_PCM_HW_PARAM_FORMAT, hw->formats);
+    snd_assert(err >= 0, return -EINVAL);
+
+    err = snd_pcm_hw_constraint_mask(runtime, SNDRV_PCM_HW_PARAM_SUBFORMAT, 1 << SNDRV_PCM_SUBFORMAT_STD);
+    snd_assert(err >= 0, return -EINVAL);
+
+#ifdef TARGET_OS2
+    err = snd_pcm_hw_constraint_mask(runtime, SNDRV_PCM_HW_PARAM_RATE_MASK, hw->rates);
+    snd_assert(err >= 0, return -EINVAL);
+#endif
+
+    err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_CHANNELS,
+                                       hw->channels_min, hw->channels_max);
+    snd_assert(err >= 0, return -EINVAL);
+
+    err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_RATE,
+                                       hw->rate_min, hw->rate_max);
+    snd_assert(err >= 0, return -EINVAL);
+
+    err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
+                                       hw->period_bytes_min, hw->period_bytes_max);
+    snd_assert(err >= 0, return -EINVAL);
+
+    err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIODS,
+                                       hw->periods_min, hw->periods_max);
+    snd_assert(err >= 0, return -EINVAL);
+
+    err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+                                       hw->period_bytes_min, hw->buffer_bytes_max);
+    snd_assert(err >= 0, return -EINVAL);
+
+    err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+                              snd_pcm_hw_rule_buffer_bytes_max, substream,
+                              SNDRV_PCM_HW_PARAM_BUFFER_BYTES, -1);
+    if (err < 0)
+        return err;
+
+    /* FIXME: remove */
+    if (runtime->dma_bytes) {
+        err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 0, runtime->dma_bytes);
+        snd_assert(err >= 0, return -EINVAL);
+    }
+
+    if (!(hw->rates & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))) {
+        err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+                                  snd_pcm_hw_rule_rate, hw,
+                                  SNDRV_PCM_HW_PARAM_RATE, -1);
+        if (err < 0)
+            return err;
+    }
+
+    /* FIXME: this belong to lowlevel */
+    snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_TICK_TIME,
+                                 1000000 / HZ, 1000000 / HZ);
+    snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
+
+    return 0;
+}
+
+static void snd_pcm_add_file(snd_pcm_str_t *str,
+                             snd_pcm_file_t *pcm_file)
+{
+    pcm_file->next = str->files;
+    str->files = pcm_file;
+}
+
+static void snd_pcm_remove_file(snd_pcm_str_t *str,
+                                snd_pcm_file_t *pcm_file)
+{
+    snd_pcm_file_t * pcm_file1;
+    if (str->files == pcm_file) {
+        str->files = pcm_file->next;
+    } else {
+        pcm_file1 = str->files;
+        while (pcm_file1 && pcm_file1->next != pcm_file)
+            pcm_file1 = pcm_file1->next;
+        if (pcm_file1 != NULL)
+            pcm_file1->next = pcm_file->next;
+    }
+}
+
+static int snd_pcm_release_file(snd_pcm_file_t * pcm_file)
+{
+    snd_pcm_substream_t *substream;
+    snd_pcm_runtime_t *runtime;
+    snd_pcm_str_t * str;
+
+    snd_assert(pcm_file != NULL, return -ENXIO);
+    substream = pcm_file->substream;
+    snd_assert(substream != NULL, return -ENXIO);
+    runtime = substream->runtime;
+    str = substream->pstr;
+    snd_pcm_unlink(substream);
+    if (substream->ffile != NULL) {
+        if (substream->ops->hw_free != NULL)
+            substream->ops->hw_free(substream);
+        substream->ops->close(substream);
+        substream->ffile = NULL;
+    }
+    snd_pcm_remove_file(str, pcm_file);
+    snd_pcm_release_substream(substream);
+    kfree(pcm_file);
+    return 0;
+}
+
+static int snd_pcm_open_file(struct file *file,
+                             snd_pcm_t *pcm,
+                             int stream,
+                             snd_pcm_file_t **rpcm_file)
+{
+    int err = 0;
+    snd_pcm_file_t *pcm_file;
+    snd_pcm_substream_t *substream;
+    snd_pcm_str_t *str;
+
+    printk("snd_pcm_open_file %X\n",rpcm_file);
+
+    snd_assert(rpcm_file != NULL, return -EINVAL);
+    *rpcm_file = NULL;
+
+    pcm_file = (snd_pcm_file_t *)kzalloc(sizeof(*pcm_file), GFP_KERNEL);
+    if (pcm_file == NULL) {
+        return -ENOMEM;
+    }
+
+    if ((err = snd_pcm_open_substream(pcm, stream, &substream)) < 0) {
+        printk("snd_pcm_open_file: error open substream %i\n",err);
+        kfree(pcm_file);
+        return err;
+    }
+
+    str = substream->pstr;
+    substream->file = pcm_file;
+
+    pcm_file->substream = substream;
+
+    snd_pcm_add_file(str, pcm_file);
+
+    err = snd_pcm_hw_constraints_init(substream);
+    if (err < 0) {
+        snd_printd("snd_pcm_hw_constraints_init failed: %i\n", err);
+        snd_pcm_release_file(pcm_file);
+        return err;
+    }
+
+    if ((err = substream->ops->open(substream)) < 0) {
+        snd_pcm_release_file(pcm_file);
+        snd_printd("supbstream open failed: %i\n", err);
+        return err;
+    }
+    substream->ffile = file;
+
+    err = snd_pcm_hw_constraints_complete(substream);
+    if (err < 0) {
+        snd_printd("snd_pcm_hw_constraints_complete failed: %i\n", err);
+        snd_pcm_release_file(pcm_file);
+        return err;
+    }
+
+    file->private_data = pcm_file;
+    *rpcm_file = pcm_file;
+    return 0;
+}
+
+int snd_pcm_open(struct inode *inode, struct file *file)
+{
+    int cardnum = SNDRV_MINOR_CARD(MINOR(inode->i_rdev));
+    int device = SNDRV_MINOR_DEVICE(MINOR(inode->i_rdev));
+    int err;
+    snd_pcm_t *pcm;
+    snd_pcm_file_t *pcm_file;
+    wait_queue_t wait;
+    //#ifdef DEBUG_PK
+    printk("snd_pcm_open card: %i, dev: %i\n", cardnum, device);
+    //#endif
+    snd_runtime_check(device >= SNDRV_MINOR_PCM_PLAYBACK && device < SNDRV_MINOR_DEVICES, return -ENXIO);
+    pcm = snd_pcm_devices[(cardnum * SNDRV_PCM_DEVICES) + (device % SNDRV_MINOR_PCMS)];
+    if (pcm == NULL) {
+        err = -ENODEV;
+        goto __error1;
+    }
+    err = snd_card_file_add(pcm->card, file);
+    //        printk("snd_pcm_open: snd_card_file_add %i\n",err);
+    if (err < 0)
+        goto __error1;
+    if (!try_module_get(pcm->card->module)) {
+        err = -EFAULT;
+        goto __error2;
+    }
+    init_waitqueue_entry(&wait, current);
+    add_wait_queue(&pcm->open_wait, &wait);
+    down(&pcm->open_mutex);
+    while (1) {
+        err = snd_pcm_open_file(file, pcm, device >= SNDRV_MINOR_PCM_CAPTURE ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK, &pcm_file);
+        if (err >= 0)
+            break;
+        if (err == -EAGAIN) {
+            if (file->f_flags & O_NONBLOCK) {
+                err = -EBUSY;
+                break;
+            }
+        } else
+            break;
+        set_current_state(TASK_INTERRUPTIBLE);
+        up(&pcm->open_mutex);
+        schedule();
+        down(&pcm->open_mutex);
+        if (signal_pending(current)) {
+            err = -ERESTARTSYS;
+            break;
+        }
+    }
+    remove_wait_queue(&pcm->open_wait, &wait);
+    up(&pcm->open_mutex);
+    if (err < 0)
+        goto __error;
+    return err;
+
+__error:
+    module_put(pcm->card->module);
+__error2:
+    snd_card_file_remove(pcm->card, file);
+__error1:
+    return err;
+}
+
+int snd_pcm_release(struct inode *inode, struct file *file)
+{
+    snd_pcm_t *pcm;
+    snd_pcm_substream_t *substream;
+    snd_pcm_file_t *pcm_file;
+
+    pcm_file = file->private_data;
+    substream = pcm_file->substream;
+    snd_assert(substream != NULL, return -ENXIO);
+    snd_assert(!atomic_read(&substream->runtime->mmap_count), );
+    pcm = substream->pcm;
+    snd_pcm_drop(substream);
+    fasync_helper(-1, file, 0, &substream->runtime->fasync);
+    down(&pcm->open_mutex);
+    snd_pcm_release_file(pcm_file);
+    up(&pcm->open_mutex);
+    wake_up(&pcm->open_wait);
+    module_put(pcm->card->module);
+    snd_card_file_remove(pcm->card, file);
+    return 0;
+}
+
+snd_pcm_sframes_t snd_pcm_playback_rewind(snd_pcm_substream_t *substream, snd_pcm_uframes_t frames)
+{
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    snd_pcm_sframes_t appl_ptr;
+    snd_pcm_sframes_t ret;
+    snd_pcm_sframes_t hw_avail;
+
+    if (frames == 0)
+        return 0;
+
+    snd_pcm_stream_lock_irq(substream);
+    switch (runtime->status->state) {
+    case SNDRV_PCM_STATE_PREPARED:
+        break;
+    case SNDRV_PCM_STATE_DRAINING:
+    case SNDRV_PCM_STATE_RUNNING:
+        if (snd_pcm_update_hw_ptr(substream) >= 0)
+            break;
+        /* Fall through */
+    case SNDRV_PCM_STATE_XRUN:
+        ret = -EPIPE;
+        goto __end;
+    default:
+        ret = -EBADFD;
+        goto __end;
+    }
+
+    hw_avail = snd_pcm_playback_hw_avail(runtime);
+    if (hw_avail <= 0) {
+        ret = 0;
+        goto __end;
+    }
+    if (frames > (snd_pcm_uframes_t)hw_avail)
+        frames = hw_avail;
+    else
+        frames -= frames % runtime->xfer_align;
+    appl_ptr = runtime->control->appl_ptr - frames;
+    if (appl_ptr < 0)
+        appl_ptr += runtime->boundary;
+    runtime->control->appl_ptr = appl_ptr;
+    if (runtime->status->state == SNDRV_PCM_STATE_RUNNING &&
+        runtime->sleep_min)
+        snd_pcm_tick_prepare(substream);
+    ret = frames;
+    __end:
+        snd_pcm_stream_unlock_irq(substream);
+        return ret;
+}
+
+snd_pcm_sframes_t snd_pcm_capture_rewind(snd_pcm_substream_t *substream, snd_pcm_uframes_t frames)
+{
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    snd_pcm_sframes_t appl_ptr;
+    snd_pcm_sframes_t ret;
+    snd_pcm_sframes_t hw_avail;
+
+    if (frames == 0)
+        return 0;
+
+    snd_pcm_stream_lock_irq(substream);
+    switch (runtime->status->state) {
+    case SNDRV_PCM_STATE_PREPARED:
+    case SNDRV_PCM_STATE_DRAINING:
+        break;
+    case SNDRV_PCM_STATE_RUNNING:
+        if (snd_pcm_update_hw_ptr(substream) >= 0)
+            break;
+        /* Fall through */
+    case SNDRV_PCM_STATE_XRUN:
+        ret = -EPIPE;
+        goto __end;
+    default:
+        ret = -EBADFD;
+        goto __end;
+    }
+
+    hw_avail = snd_pcm_capture_hw_avail(runtime);
+    if (hw_avail <= 0) {
+        ret = 0;
+        goto __end;
+    }
+    if (frames > (snd_pcm_uframes_t)hw_avail)
+        frames = hw_avail;
+    else
+        frames -= frames % runtime->xfer_align;
+    appl_ptr = runtime->control->appl_ptr - frames;
+    if (appl_ptr < 0)
+        appl_ptr += runtime->boundary;
+    runtime->control->appl_ptr = appl_ptr;
+    if (runtime->status->state == SNDRV_PCM_STATE_RUNNING &&
+        runtime->sleep_min)
+        snd_pcm_tick_prepare(substream);
+    ret = frames;
+    __end:
+        snd_pcm_stream_unlock_irq(substream);
+        return ret;
+}
+
+snd_pcm_sframes_t snd_pcm_playback_forward(snd_pcm_substream_t *substream, snd_pcm_uframes_t frames)
+{
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    snd_pcm_sframes_t appl_ptr;
+    snd_pcm_sframes_t ret;
+    snd_pcm_sframes_t avail;
+
+    if (frames == 0)
+        return 0;
+
+    snd_pcm_stream_lock_irq(substream);
+    switch (runtime->status->state) {
+    case SNDRV_PCM_STATE_PREPARED:
+    case SNDRV_PCM_STATE_PAUSED:
+        break;
+    case SNDRV_PCM_STATE_DRAINING:
+    case SNDRV_PCM_STATE_RUNNING:
+        if (snd_pcm_update_hw_ptr(substream) >= 0)
+            break;
+        /* Fall through */
+    case SNDRV_PCM_STATE_XRUN:
+        ret = -EPIPE;
+        goto __end;
+    default:
+        ret = -EBADFD;
+        goto __end;
+    }
+
+    avail = snd_pcm_playback_avail(runtime);
+    if (avail <= 0) {
+        ret = 0;
+        goto __end;
+    }
+    if (frames > (snd_pcm_uframes_t)avail)
+        frames = avail;
+    else
+        frames -= frames % runtime->xfer_align;
+    appl_ptr = runtime->control->appl_ptr + frames;
+    if (appl_ptr >= (snd_pcm_sframes_t)runtime->boundary)
+        appl_ptr -= runtime->boundary;
+    runtime->control->appl_ptr = appl_ptr;
+    if (runtime->status->state == SNDRV_PCM_STATE_RUNNING &&
+        runtime->sleep_min)
+        snd_pcm_tick_prepare(substream);
+    ret = frames;
+    __end:
+        snd_pcm_stream_unlock_irq(substream);
+        return ret;
+}
+
+snd_pcm_sframes_t snd_pcm_capture_forward(snd_pcm_substream_t *substream, snd_pcm_uframes_t frames)
+{
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    snd_pcm_sframes_t appl_ptr;
+    snd_pcm_sframes_t ret;
+    snd_pcm_sframes_t avail;
+
+    if (frames == 0)
+        return 0;
+
+    snd_pcm_stream_lock_irq(substream);
+    switch (runtime->status->state) {
+    case SNDRV_PCM_STATE_PREPARED:
+    case SNDRV_PCM_STATE_DRAINING:
+    case SNDRV_PCM_STATE_PAUSED:
+        break;
+    case SNDRV_PCM_STATE_RUNNING:
+        if (snd_pcm_update_hw_ptr(substream) >= 0)
+            break;
+        /* Fall through */
+    case SNDRV_PCM_STATE_XRUN:
+        ret = -EPIPE;
+        goto __end;
+    default:
+        ret = -EBADFD;
+        goto __end;
+    }
+
+    avail = snd_pcm_capture_avail(runtime);
+    if (avail <= 0) {
+        ret = 0;
+        goto __end;
+    }
+    if (frames > (snd_pcm_uframes_t)avail)
+        frames = avail;
+    else
+        frames -= frames % runtime->xfer_align;
+    appl_ptr = runtime->control->appl_ptr + frames;
+    if (appl_ptr >= (snd_pcm_sframes_t)runtime->boundary)
+        appl_ptr -= runtime->boundary;
+    runtime->control->appl_ptr = appl_ptr;
+    if (runtime->status->state == SNDRV_PCM_STATE_RUNNING &&
+        runtime->sleep_min)
+        snd_pcm_tick_prepare(substream);
+    ret = frames;
+    __end:
+        snd_pcm_stream_unlock_irq(substream);
+        return ret;
+}
+
+static int snd_pcm_hwsync(snd_pcm_substream_t *substream)
+{
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    int err;
+
+    snd_pcm_stream_lock_irq(substream);
+    switch (runtime->status->state) {
+    case SNDRV_PCM_STATE_DRAINING:
+        if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+            goto __badfd;
+    case SNDRV_PCM_STATE_RUNNING:
+        if ((err = snd_pcm_update_hw_ptr(substream)) < 0)
+            break;
+        /* Fall through */
+    case SNDRV_PCM_STATE_PREPARED:
+    case SNDRV_PCM_STATE_SUSPENDED:
+        err = 0;
+        break;
+    case SNDRV_PCM_STATE_XRUN:
+        err = -EPIPE;
+        break;
+    default:
+    __badfd:
+        err = -EBADFD;
+        break;
+    }
+    snd_pcm_stream_unlock_irq(substream);
+    return err;
+}
+
+static int snd_pcm_delay(snd_pcm_substream_t *substream, snd_pcm_sframes_t *res)
+{
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    int err;
+    snd_pcm_sframes_t n = 0;
+
+    snd_pcm_stream_lock_irq(substream);
+    switch (runtime->status->state) {
+    case SNDRV_PCM_STATE_DRAINING:
+        if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+            goto __badfd;
+    case SNDRV_PCM_STATE_RUNNING:
+        if ((err = snd_pcm_update_hw_ptr(substream)) < 0)
+            break;
+        /* Fall through */
+    case SNDRV_PCM_STATE_PREPARED:
+    case SNDRV_PCM_STATE_SUSPENDED:
+        err = 0;
+        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+            n = snd_pcm_playback_hw_avail(runtime);
+        else
+            n = snd_pcm_capture_avail(runtime);
+        break;
+    case SNDRV_PCM_STATE_XRUN:
+        err = -EPIPE;
+        break;
+    default:
+    __badfd:
+        err = -EBADFD;
+        break;
+    }
+    snd_pcm_stream_unlock_irq(substream);
+    if (!err)
+        if (put_user(n, res))
+            err = -EFAULT;
+    return err;
+}
+
+static int snd_pcm_sync_ptr(snd_pcm_substream_t *substream, struct snd_pcm_sync_ptr *_sync_ptr)
+{
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    struct snd_pcm_sync_ptr sync_ptr;
+    volatile struct snd_pcm_mmap_status *status;
+    volatile struct snd_pcm_mmap_control *control;
+    int err;
+
+    memset(&sync_ptr, 0, sizeof(sync_ptr));
+    if (get_user(sync_ptr.flags, (unsigned int *) &(_sync_ptr->flags)))
+        return -EFAULT;
+    if (copy_from_user(&sync_ptr.c.control, &(_sync_ptr->c.control), sizeof(struct snd_pcm_mmap_control)))
+        return -EFAULT;
+    status = runtime->status;
+    control = runtime->control;
+    if (sync_ptr.flags & SNDRV_PCM_SYNC_PTR_HWSYNC) {
+        err = snd_pcm_hwsync(substream);
+        if (err < 0)
+            return err;
+    }
+    snd_pcm_stream_lock_irq(substream);
+    if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_APPL))
+        control->appl_ptr = sync_ptr.c.control.appl_ptr;
+    else
+        sync_ptr.c.control.appl_ptr = control->appl_ptr;
+    if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_AVAIL_MIN))
+        control->avail_min = sync_ptr.c.control.avail_min;
+    else
+        sync_ptr.c.control.avail_min = control->avail_min;
+    sync_ptr.s.status.state = status->state;
+    sync_ptr.s.status.hw_ptr = status->hw_ptr;
+    sync_ptr.s.status.tstamp = status->tstamp;
+    sync_ptr.s.status.suspended_state = status->suspended_state;
+    snd_pcm_stream_unlock_irq(substream);
+    if (copy_to_user(_sync_ptr, &sync_ptr, sizeof(sync_ptr)))
+        return -EFAULT;
+    return 0;
+}
+
+static int snd_pcm_playback_ioctl1(snd_pcm_substream_t *substream,
+                                   unsigned int cmd, void *arg);
+static int snd_pcm_capture_ioctl1(snd_pcm_substream_t *substream,
+                                  unsigned int cmd, void *arg);
+
+static int snd_pcm_common_ioctl1(snd_pcm_substream_t *substream,
+                                 unsigned int cmd, void *arg)
+{
+    snd_assert(substream != NULL, return -ENXIO);
+#ifdef DEBUG
+    //        dprintf(("vladest: snd_pcm_common_ioctl1. cmd = %d",cmd));
+#endif
+
+    switch (cmd) {
+    case SNDRV_PCM_IOCTL_PVERSION:
+        return put_user(SNDRV_PCM_VERSION, (int *)arg) ? -EFAULT : 0;
+    case SNDRV_PCM_IOCTL_INFO:
+        return snd_pcm_info_user(substream, (snd_pcm_info_t *) arg);
+    case SNDRV_PCM_IOCTL_TSTAMP: /* just for compatibility */
+        return 0;
+    case SNDRV_PCM_IOCTL_HW_REFINE:
+        return snd_pcm_hw_refine_user(substream, (snd_pcm_hw_params_t *) arg);
+    case SNDRV_PCM_IOCTL_HW_PARAMS:
+        return snd_pcm_hw_params_user(substream, (snd_pcm_hw_params_t *) arg);
+    case SNDRV_PCM_IOCTL_HW_FREE:
+        return snd_pcm_hw_free(substream);
+    case SNDRV_PCM_IOCTL_SW_PARAMS:
+        return snd_pcm_sw_params_user(substream, (snd_pcm_sw_params_t *) arg);
+    case SNDRV_PCM_IOCTL_STATUS:
+        return snd_pcm_status_user(substream, (snd_pcm_status_t *) arg);
+    case SNDRV_PCM_IOCTL_CHANNEL_INFO:
+        return snd_pcm_channel_info(substream, (snd_pcm_channel_info_t *) arg);
+    case SNDRV_PCM_IOCTL_PREPARE:
+        return snd_pcm_prepare(substream);
+    case SNDRV_PCM_IOCTL_RESET:
+        return snd_pcm_reset(substream);
+    case SNDRV_PCM_IOCTL_START:
+        return snd_pcm_action_lock_irq(&snd_pcm_action_start, substream, SNDRV_PCM_STATE_RUNNING);
+    case SNDRV_PCM_IOCTL_LINK:
+        return snd_pcm_link(substream, (long) arg);
+    case SNDRV_PCM_IOCTL_UNLINK:
+        return snd_pcm_unlink(substream);
+    case SNDRV_PCM_IOCTL_RESUME:
+        return snd_pcm_resume(substream);
+    case SNDRV_PCM_IOCTL_XRUN:
+        return snd_pcm_xrun(substream);
+    case SNDRV_PCM_IOCTL_HWSYNC:
+        return snd_pcm_hwsync(substream);
+    case SNDRV_PCM_IOCTL_DELAY:
+        return snd_pcm_delay(substream, (snd_pcm_sframes_t *) arg);
+    case SNDRV_PCM_IOCTL_SYNC_PTR:
+        return snd_pcm_sync_ptr(substream, (struct snd_pcm_sync_ptr *) arg);
+    case SNDRV_PCM_IOCTL_HW_REFINE_OLD:
+        return snd_pcm_hw_refine_old_user(substream, (struct snd_pcm_hw_params_old *) arg);
+    case SNDRV_PCM_IOCTL_HW_PARAMS_OLD:
+        return snd_pcm_hw_params_old_user(substream, (struct snd_pcm_hw_params_old *) arg);
+    case SNDRV_PCM_IOCTL_DRAIN:
+        return snd_pcm_drain(substream);
+    case SNDRV_PCM_IOCTL_DROP:
+        return snd_pcm_drop(substream);
+    case SNDRV_PCM_IOCTL_PAUSE:
+        {
+            int res;
+            snd_pcm_stream_lock_irq(substream);
+            res = snd_pcm_pause(substream, (int)(unsigned long)arg);
+            snd_pcm_stream_unlock_irq(substream);
+            return res;
+        }
+    }
+    snd_printd("unknown ioctl = 0x%x\n", cmd);
+    return -ENOTTY;
+}
+
+static int snd_pcm_playback_ioctl1(snd_pcm_substream_t *substream,
+                                   unsigned int cmd, void *arg)
+{
+    snd_assert(substream != NULL, return -ENXIO);
+    snd_assert(substream->stream == SNDRV_PCM_STREAM_PLAYBACK, return -EINVAL);
+    //#ifdef DEBUG
+    //        dprintf2(("vladest: snd_pcm_playback_ioctl1. cmd = %d",cmd));
+    //#endif
+    switch (cmd) {
+    case SNDRV_PCM_IOCTL_WRITEI_FRAMES:
+        {
+            snd_xferi_t xferi, *_xferi = arg;
+            snd_pcm_runtime_t *runtime = substream->runtime;
+            snd_pcm_sframes_t result;
+            if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+                return -EBADFD;
+            if (put_user(0, &_xferi->result))
+                return -EFAULT;
+            if (copy_from_user(&xferi, _xferi, sizeof(xferi)))
+                return -EFAULT;
+            result = snd_pcm_lib_write(substream, xferi.buf, xferi.frames);
+            __put_user(result, &_xferi->result);
+            return result < 0 ? result : 0;
+        }
+    case SNDRV_PCM_IOCTL_WRITEN_FRAMES:
+        {
+            snd_xfern_t xfern, *_xfern = arg;
+            snd_pcm_runtime_t *runtime = substream->runtime;
+            void *bufs;
+            snd_pcm_sframes_t result;
+            if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+                return -EBADFD;
+            if (runtime->channels > 128)
+                return -EINVAL;
+            if (put_user(0, &_xfern->result))
+                return -EFAULT;
+            if (copy_from_user(&xfern, _xfern, sizeof(xfern)))
+                return -EFAULT;
+            bufs = kmalloc(sizeof(void *) * runtime->channels, GFP_KERNEL);
+            if (bufs == NULL)
+                return -ENOMEM;
+            if (copy_from_user(bufs, xfern.bufs, sizeof(void *) * runtime->channels)) {
+                kfree(bufs);
+                return -EFAULT;
+            }
+            result = snd_pcm_lib_writev(substream, bufs, xfern.frames);
+            kfree(bufs);
+            __put_user(result, &_xfern->result);
+            return result < 0 ? result : 0;
+        }
+    case SNDRV_PCM_IOCTL_REWIND:
+        {
+            snd_pcm_uframes_t frames, *_frames = arg;
+            snd_pcm_sframes_t result;
+            if (get_user(frames, _frames))
+                return -EFAULT;
+            if (put_user(0, _frames))
+                return -EFAULT;
+            result = snd_pcm_playback_rewind(substream, frames);
+            __put_user(result, _frames);
+            return result < 0 ? result : 0;
+        }
+    case SNDRV_PCM_IOCTL_FORWARD:
+        {
+            snd_pcm_uframes_t frames, *_frames = arg;
+            snd_pcm_sframes_t result;
+            if (get_user(frames, _frames))
+                return -EFAULT;
+            if (put_user(0, _frames))
+                return -EFAULT;
+            result = snd_pcm_playback_forward(substream, frames);
+            __put_user(result, _frames);
+            return result < 0 ? result : 0;
+        }
+    }
+    return snd_pcm_common_ioctl1(substream, cmd, arg);
+}
+
+static int snd_pcm_capture_ioctl1(snd_pcm_substream_t *substream,
+                                  unsigned int cmd, void *arg)
+{
+    snd_assert(substream != NULL, return -ENXIO);
+    snd_assert(substream->stream == SNDRV_PCM_STREAM_CAPTURE, return -EINVAL);
+#ifdef DEBUG
+    //        dprintf2(("vladest: snd_pcm_capture_ioctl1. cmd = %d",cmd));
+#endif
+    switch (cmd) {
+    case SNDRV_PCM_IOCTL_READI_FRAMES:
+        {
+            snd_xferi_t xferi, *_xferi = arg;
+            snd_pcm_runtime_t *runtime = substream->runtime;
+            snd_pcm_sframes_t result;
+            if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+                return -EBADFD;
+            if (put_user(0, &_xferi->result))
+                return -EFAULT;
+            if (copy_from_user(&xferi, _xferi, sizeof(xferi)))
+                return -EFAULT;
+            result = snd_pcm_lib_read(substream, xferi.buf, xferi.frames);
+            __put_user(result, &_xferi->result);
+            return result < 0 ? result : 0;
+        }
+    case SNDRV_PCM_IOCTL_READN_FRAMES:
+        {
+            snd_xfern_t xfern, *_xfern = arg;
+            snd_pcm_runtime_t *runtime = substream->runtime;
+            void *bufs;
+            snd_pcm_sframes_t result;
+            if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+                return -EBADFD;
+            if (runtime->channels > 128)
+                return -EINVAL;
+            if (put_user(0, &_xfern->result))
+                return -EFAULT;
+            if (copy_from_user(&xfern, _xfern, sizeof(xfern)))
+                return -EFAULT;
+            bufs = kmalloc(sizeof(void *) * runtime->channels, GFP_KERNEL);
+            if (bufs == NULL)
+                return -ENOMEM;
+            if (copy_from_user(bufs, xfern.bufs, sizeof(void *) * runtime->channels)) {
+                kfree(bufs);
+                return -EFAULT;
+            }
+            result = snd_pcm_lib_readv(substream, bufs, xfern.frames);
+            kfree(bufs);
+            __put_user(result, &_xfern->result);
+            return result < 0 ? result : 0;
+        }
+    case SNDRV_PCM_IOCTL_REWIND:
+        {
+            snd_pcm_uframes_t frames, *_frames = arg;
+            snd_pcm_sframes_t result;
+            if (get_user(frames, _frames))
+                return -EFAULT;
+            if (put_user(0, _frames))
+                return -EFAULT;
+            result = snd_pcm_capture_rewind(substream, frames);
+            __put_user(result, _frames);
+            return result < 0 ? result : 0;
+        }
+    case SNDRV_PCM_IOCTL_FORWARD:
+        {
+            snd_pcm_uframes_t frames, *_frames = arg;
+            snd_pcm_sframes_t result;
+            if (get_user(frames, _frames))
+                return -EFAULT;
+            if (put_user(0, _frames))
+                return -EFAULT;
+            result = snd_pcm_capture_forward(substream, frames);
+            __put_user(result, _frames);
+            return result < 0 ? result : 0;
+        }
+    }
+    return snd_pcm_common_ioctl1(substream, cmd, arg);
+}
+
+static int snd_pcm_playback_ioctl(struct inode *inode, struct file *file,
+                                  unsigned int cmd, unsigned long arg)
+{
+    snd_pcm_file_t *pcm_file;
+
+    pcm_file = file->private_data;
+
+    if (((cmd >> 8) & 0xff) != 'A')
+        return -ENOTTY;
+
+    return snd_pcm_playback_ioctl1(pcm_file->substream, cmd, (void *) arg);
+}
+
+static int snd_pcm_capture_ioctl(struct inode *inode, struct file *file,
+                                 unsigned int cmd, unsigned long arg)
+{
+    snd_pcm_file_t *pcm_file;
+
+    pcm_file = file->private_data;
+
+    if (((cmd >> 8) & 0xff) != 'A')
+        return -ENOTTY;
+
+    return snd_pcm_capture_ioctl1(pcm_file->substream, cmd, (void *) arg);
+}
+
+int snd_pcm_kernel_playback_ioctl(snd_pcm_substream_t *substream,
+                                  unsigned int cmd, void *arg)
+{
+    mm_segment_t fs;
+    int result;
+
+    fs = snd_enter_user();
+    result = snd_pcm_playback_ioctl1(substream, cmd, arg);
+    snd_leave_user(fs);
+    return result;
+}
+
+int snd_pcm_kernel_capture_ioctl(snd_pcm_substream_t *substream,
+                                 unsigned int cmd, void *arg)
+{
+    mm_segment_t fs;
+    int result;
+
+    fs = snd_enter_user();
+    result = snd_pcm_capture_ioctl1(substream, cmd, arg);
+    snd_leave_user(fs);
+    return result;
+}
+
+int snd_pcm_kernel_ioctl(snd_pcm_substream_t *substream,
+                         unsigned int cmd, void *arg)
+{
+    switch (substream->stream) {
+    case SNDRV_PCM_STREAM_PLAYBACK:
+        return snd_pcm_kernel_playback_ioctl(substream, cmd, arg);
+    case SNDRV_PCM_STREAM_CAPTURE:
+        return snd_pcm_kernel_capture_ioctl(substream, cmd, arg);
+    default:
+        return -EINVAL;
+    }
+}
+
+static ssize_t snd_pcm_read(struct file *file, char *buf, size_t count, loff_t * offset)
+{
+    snd_pcm_file_t *pcm_file;
+    snd_pcm_substream_t *substream;
+    snd_pcm_runtime_t *runtime;
+    snd_pcm_sframes_t result;
+
+    pcm_file = file->private_data;
+    substream = pcm_file->substream;
+    snd_assert(substream != NULL, return -ENXIO);
+    runtime = substream->runtime;
+    if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+        return -EBADFD;
+    if (!frame_aligned(runtime, count))
+        return -EINVAL;
+    count = bytes_to_frames(runtime, count);
+    result = snd_pcm_lib_read(substream, buf, count);
+    if (result > 0)
+        result = frames_to_bytes(runtime, result);
+    return result;
+}
+
+static ssize_t snd_pcm_write(struct file *file, const char *buf, size_t count, loff_t * offset)
+{
+    snd_pcm_file_t *pcm_file;
+    snd_pcm_substream_t *substream;
+    snd_pcm_runtime_t *runtime;
+    snd_pcm_sframes_t result;
+
+#ifdef DEBUG_PK
+    printk("snd_pcm_write. count = %i, offset = %i\n",count, offset);
+#endif
+    pcm_file = file->private_data;
+    substream = pcm_file->substream;
+    snd_assert(substream != NULL, result = -ENXIO; goto end);
+    runtime = substream->runtime;
+    if (runtime->status->state == SNDRV_PCM_STATE_OPEN) {
+        result = -EBADFD;
+        goto end;
+    }
+    if (!frame_aligned(runtime, count)) {
+#ifdef DEBUG_PK
+        printk("snd_pcm_write. frame_aligned error. byte align = %i\n",runtime->byte_align);
+#endif
+        result = -EINVAL;
+        goto end;
+    }
+    count = bytes_to_frames(runtime, count);
+    result = snd_pcm_lib_write(substream, buf, count);
+    if (result > 0)
+        result = frames_to_bytes(runtime, result);
+    end:
+#ifdef DEBUG_PK
+        printk("snd_pcm_write res = %i\n",result);
+#endif
+        return result;
+}
+
+static ssize_t snd_pcm_readv(struct file *file, const struct iovec *_vector,
+                             unsigned long count, loff_t * offset)
+
+{
+    snd_pcm_file_t *pcm_file;
+    snd_pcm_substream_t *substream;
+    snd_pcm_runtime_t *runtime;
+    snd_pcm_sframes_t result;
+    unsigned long i;
+    void **bufs;
+    snd_pcm_uframes_t frames;
+
+    pcm_file = file->private_data;
+    substream = pcm_file->substream;
+    snd_assert(substream != NULL, return -ENXIO);
+    runtime = substream->runtime;
+    if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+        return -EBADFD;
+    if (count > 1024 || count != runtime->channels)
+        return -EINVAL;
+    if (!frame_aligned(runtime, _vector->iov_len))
+        return -EINVAL;
+    frames = bytes_to_samples(runtime, _vector->iov_len);
+    bufs = kmalloc(sizeof(void *) * count, GFP_KERNEL);
+    if (bufs == NULL)
+        return -ENOMEM;
+    for (i = 0; i < count; ++i)
+        bufs[i] = _vector[i].iov_base;
+    result = snd_pcm_lib_readv(substream, bufs, frames);
+    if (result > 0)
+        result = frames_to_bytes(runtime, result);
+    kfree(bufs);
+    return result;
+}
+
+static ssize_t snd_pcm_writev(struct file *file, const struct iovec *_vector,
+                              unsigned long count, loff_t * offset)
+{
+    snd_pcm_file_t *pcm_file;
+    snd_pcm_substream_t *substream;
+    snd_pcm_runtime_t *runtime;
+    snd_pcm_sframes_t result;
+    unsigned long i;
+    void **bufs;
+    snd_pcm_uframes_t frames;
+
+#ifdef DEBUG_PK
+    printk("snd_pcm_writev. count = %i, offset = %i\n",count, offset);
+#endif
+    pcm_file = file->private_data;
+    substream = pcm_file->substream;
+    snd_assert(substream != NULL, result = -ENXIO; goto end);
+    runtime = substream->runtime;
+    if (runtime->status->state == SNDRV_PCM_STATE_OPEN) {
+        result = -EBADFD;
+        goto end;
+    }
+    if (count > 128 || count != runtime->channels ||
+        !frame_aligned(runtime, _vector->iov_len)) {
+        result = -EINVAL;
+        goto end;
+    }
+    frames = bytes_to_samples(runtime, _vector->iov_len);
+    bufs = kmalloc(sizeof(void *) * count, GFP_KERNEL);
+    if (bufs == NULL)
+        return -ENOMEM;
+    for (i = 0; i < count; ++i)
+        bufs[i] = _vector[i].iov_base;
+    result = snd_pcm_lib_writev(substream, bufs, frames);
+    if (result > 0)
+        result = frames_to_bytes(runtime, result);
+    kfree(bufs);
+    end:
+#ifdef DEBUG_PK
+        printk("snd_pcm_writev. res = %i\n",result);
+#endif
+        return result;
+}
+
+unsigned int snd_pcm_playback_poll(struct file *file, poll_table * wait)
+{
+    snd_pcm_file_t *pcm_file;
+    snd_pcm_substream_t *substream;
+    snd_pcm_runtime_t *runtime;
+    unsigned int mask;
+    snd_pcm_uframes_t avail;
+
+    pcm_file = file->private_data;
+
+    substream = pcm_file->substream;
+    snd_assert(substream != NULL, return -ENXIO);
+    runtime = substream->runtime;
+
+    poll_wait(file, &runtime->sleep, wait);
+
+    snd_pcm_stream_lock_irq(substream);
+    avail = snd_pcm_playback_avail(runtime);
+    switch (runtime->status->state) {
+    case SNDRV_PCM_STATE_RUNNING:
+    case SNDRV_PCM_STATE_PREPARED:
+    case SNDRV_PCM_STATE_PAUSED:
+        if (avail >= runtime->control->avail_min) {
+            mask = POLLOUT | POLLWRNORM;
+            break;
+        }
+        /* Fall through */
+    case SNDRV_PCM_STATE_DRAINING:
+        mask = 0;
+        break;
+    default:
+        mask = POLLOUT | POLLWRNORM | POLLERR;
+        break;
+    }
+    snd_pcm_stream_unlock_irq(substream);
+    return mask;
+}
+
+unsigned int snd_pcm_capture_poll(struct file *file, poll_table * wait)
+{
+    snd_pcm_file_t *pcm_file;
+    snd_pcm_substream_t *substream;
+    snd_pcm_runtime_t *runtime;
+    unsigned int mask;
+    snd_pcm_uframes_t avail;
+
+    pcm_file = file->private_data;
+
+    substream = pcm_file->substream;
+    snd_assert(substream != NULL, return -ENXIO);
+    runtime = substream->runtime;
+
+    poll_wait(file, &runtime->sleep, wait);
+
+    snd_pcm_stream_lock_irq(substream);
+    avail = snd_pcm_capture_avail(runtime);
+    switch (runtime->status->state) {
+    case SNDRV_PCM_STATE_RUNNING:
+    case SNDRV_PCM_STATE_PREPARED:
+    case SNDRV_PCM_STATE_PAUSED:
+        if (avail >= runtime->control->avail_min) {
+            mask = POLLIN | POLLRDNORM;
+            break;
+        }
+        mask = 0;
+        break;
+    case SNDRV_PCM_STATE_DRAINING:
+        if (avail > 0) {
+            mask = POLLIN | POLLRDNORM;
+            break;
+        }
+        /* Fall through */
+    default:
+        mask = POLLIN | POLLRDNORM | POLLERR;
+        break;
+    }
+    snd_pcm_stream_unlock_irq(substream);
+    return mask;
+}
+
+#ifndef TARGET_OS2
+static struct page * snd_pcm_mmap_status_nopage(struct vm_area_struct *area, unsigned long address, int *type)
+{
+    snd_pcm_substream_t *substream = (snd_pcm_substream_t *)area->vm_private_data;
+    snd_pcm_runtime_t *runtime;
+    struct page * page;
+
+    if (substream == NULL)
+        return NOPAGE_OOM;
+    runtime = substream->runtime;
+    page = virt_to_page(runtime->status);
+    if (!PageReserved(page))
+        get_page(page);
+    if (type)
+        *type = VM_FAULT_MINOR;
+    return page;
+}
+
+static struct vm_operations_struct snd_pcm_vm_ops_status =
+{
+    .nopage =	snd_pcm_mmap_status_nopage,
+};
+
+int snd_pcm_mmap_status(snd_pcm_substream_t *substream, struct file *file,
+                        struct vm_area_struct *area)
+{
+    snd_pcm_runtime_t *runtime;
+    long size;
+    if (!(area->vm_flags & VM_READ))
+        return -EINVAL;
+    runtime = substream->runtime;
+    snd_assert(runtime != NULL, return -EAGAIN);
+    size = area->vm_end - area->vm_start;
+    if (size != PAGE_ALIGN(sizeof(snd_pcm_mmap_status_t)))
+        return -EINVAL;
+    area->vm_ops = &snd_pcm_vm_ops_status;
+    area->vm_private_data = substream;
+    area->vm_flags |= VM_RESERVED;
+    return 0;
+}
+
+static struct page * snd_pcm_mmap_control_nopage(struct vm_area_struct *area, unsigned long address, int *type)
+{
+    snd_pcm_substream_t *substream = (snd_pcm_substream_t *)area->vm_private_data;
+    snd_pcm_runtime_t *runtime;
+    struct page * page;
+
+    if (substream == NULL)
+        return NOPAGE_OOM;
+    runtime = substream->runtime;
+    page = virt_to_page(runtime->control);
+    if (!PageReserved(page))
+        get_page(page);
+    if (type)
+        *type = VM_FAULT_MINOR;
+    return page;
+}
+
+static struct vm_operations_struct snd_pcm_vm_ops_control =
+{
+    .nopage =	snd_pcm_mmap_control_nopage,
+};
+
+static int snd_pcm_mmap_control(snd_pcm_substream_t *substream, struct file *file,
+                                struct vm_area_struct *area)
+{
+    snd_pcm_runtime_t *runtime;
+    long size;
+    if (!(area->vm_flags & VM_READ))
+        return -EINVAL;
+    runtime = substream->runtime;
+    snd_assert(runtime != NULL, return -EAGAIN);
+    size = area->vm_end - area->vm_start;
+    if (size != PAGE_ALIGN(sizeof(snd_pcm_mmap_control_t)))
+        return -EINVAL;
+    area->vm_ops = &snd_pcm_vm_ops_control;
+    area->vm_private_data = substream;
+    area->vm_flags |= VM_RESERVED;
+    return 0;
+}
+
+static void snd_pcm_mmap_data_open(struct vm_area_struct *area)
+{
+    snd_pcm_substream_t *substream = (snd_pcm_substream_t *)area->vm_private_data;
+    atomic_inc(&substream->runtime->mmap_count);
+}
+
+static void snd_pcm_mmap_data_close(struct vm_area_struct *area)
+{
+    snd_pcm_substream_t *substream = (snd_pcm_substream_t *)area->vm_private_data;
+    atomic_dec(&substream->runtime->mmap_count);
+}
+
+static struct page * snd_pcm_mmap_data_nopage(struct vm_area_struct *area, unsigned long address, int *type)
+{
+    snd_pcm_substream_t *substream = (snd_pcm_substream_t *)area->vm_private_data;
+    snd_pcm_runtime_t *runtime;
+    unsigned long offset;
+    struct page * page;
+    void *vaddr;
+    size_t dma_bytes;
+
+    if (substream == NULL)
+        return NOPAGE_OOM;
+    runtime = substream->runtime;
+    offset = area->vm_pgoff << PAGE_SHIFT;
+    offset += address - area->vm_start;
+    snd_assert((offset % PAGE_SIZE) == 0, return NOPAGE_OOM);
+    dma_bytes = PAGE_ALIGN(runtime->dma_bytes);
+    if (offset > dma_bytes - PAGE_SIZE)
+        return NOPAGE_SIGBUS;
+    if (substream->ops->page) {
+        page = substream->ops->page(substream, offset);
+        if (! page)
+            return NOPAGE_OOM;
+    } else {
+        vaddr = runtime->dma_area + offset;
+        page = virt_to_page(vaddr);
+    }
+    if (!PageReserved(page))
+        get_page(page);
+    if (type)
+        *type = VM_FAULT_MINOR;
+    return page;
+}
+
+static struct vm_operations_struct snd_pcm_vm_ops_data =
+{
+    .open =		snd_pcm_mmap_data_open,
+    .close =	snd_pcm_mmap_data_close,
+    .nopage =	snd_pcm_mmap_data_nopage,
+};
+
+int snd_pcm_mmap_data(snd_pcm_substream_t *substream, struct file *file,
+                      struct vm_area_struct *area)
+{
+    snd_pcm_runtime_t *runtime;
+    long size;
+    unsigned long offset;
+    size_t dma_bytes;
+
+    if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+        if (!(area->vm_flags & (VM_WRITE|VM_READ)))
+            return -EINVAL;
+    } else {
+        if (!(area->vm_flags & VM_READ))
+            return -EINVAL;
+    }
+    runtime = substream->runtime;
+    snd_assert(runtime != NULL, return -EAGAIN);
+    if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+        return -EBADFD;
+    if (!(runtime->info & SNDRV_PCM_INFO_MMAP))
+        return -ENXIO;
+    if (runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED ||
+        runtime->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED)
+        return -EINVAL;
+    size = area->vm_end - area->vm_start;
+    offset = area->vm_pgoff << PAGE_SHIFT;
+    dma_bytes = PAGE_ALIGN(runtime->dma_bytes);
+    if ((size_t)size > dma_bytes)
+        return -EINVAL;
+    if (offset > dma_bytes - size)
+        return -EINVAL;
+
+    area->vm_ops = &snd_pcm_vm_ops_data;
+    area->vm_private_data = substream;
+    area->vm_flags |= VM_RESERVED;
+    atomic_inc(&runtime->mmap_count);
+    return 0;
+}
+
+static int snd_pcm_mmap(struct file *file, struct vm_area_struct *area)
+{
+    snd_pcm_file_t * pcm_file;
+    snd_pcm_substream_t *substream;
+    unsigned long offset;
+
+    pcm_file = file->private_data;
+    substream = pcm_file->substream;
+    snd_assert(substream != NULL, return -ENXIO);
+
+    offset = area->vm_pgoff << PAGE_SHIFT;
+    switch (offset) {
+    case SNDRV_PCM_MMAP_OFFSET_STATUS:
+        return snd_pcm_mmap_status(substream, file, area);
+    case SNDRV_PCM_MMAP_OFFSET_CONTROL:
+        return snd_pcm_mmap_control(substream, file, area);
+    default:
+        return snd_pcm_mmap_data(substream, file, area);
+    }
+    return 0;
+}
+
+static int snd_pcm_fasync(int fd, struct file * file, int on)
+{
+    snd_pcm_file_t * pcm_file;
+    snd_pcm_substream_t *substream;
+    snd_pcm_runtime_t *runtime;
+    int err;
+
+    pcm_file = file->private_data;
+    substream = pcm_file->substream;
+    snd_assert(substream != NULL, return -ENXIO);
+    runtime = substream->runtime;
+
+    err = fasync_helper(fd, file, on, &runtime->fasync);
+    if (err < 0)
+        return err;
+    return 0;
+}
+#endif //!TARGET_OS2
+
+/*
+ *  To be removed helpers to keep binary compatibility
+ */
+
+#define __OLD_TO_NEW_MASK(x) ((x&7)|((x&0x07fffff8)<<5))
+#define __NEW_TO_OLD_MASK(x) ((x&7)|((x&0xffffff00)>>5))
+
+static void snd_pcm_hw_convert_from_old_params(snd_pcm_hw_params_t *params, struct snd_pcm_hw_params_old *oparams)
+{
+    unsigned int i;
+
+    memset(params, 0, sizeof(*params));
+    params->flags = oparams->flags;
+    for (i = 0; i < sizeof(oparams->masks) / sizeof(unsigned int); i++)
+        params->masks[i].bits[0] = oparams->masks[i];
+    memcpy(params->intervals, oparams->intervals, sizeof(oparams->intervals));
+    params->rmask = __OLD_TO_NEW_MASK(oparams->rmask);
+    params->cmask = __OLD_TO_NEW_MASK(oparams->cmask);
+    params->info = oparams->info;
+    params->msbits = oparams->msbits;
+    params->rate_num = oparams->rate_num;
+    params->rate_den = oparams->rate_den;
+    params->fifo_size = oparams->fifo_size;
+}
+
+static void snd_pcm_hw_convert_to_old_params(struct snd_pcm_hw_params_old *oparams, snd_pcm_hw_params_t *params)
+{
+    unsigned int i;
+
+    memset(oparams, 0, sizeof(*oparams));
+    oparams->flags = params->flags;
+    for (i = 0; i < sizeof(oparams->masks) / sizeof(unsigned int); i++)
+        oparams->masks[i] = params->masks[i].bits[0];
+    memcpy(oparams->intervals, params->intervals, sizeof(oparams->intervals));
+    oparams->rmask = __NEW_TO_OLD_MASK(params->rmask);
+    oparams->cmask = __NEW_TO_OLD_MASK(params->cmask);
+    oparams->info = params->info;
+    oparams->msbits = params->msbits;
+    oparams->rate_num = params->rate_num;
+    oparams->rate_den = params->rate_den;
+    oparams->fifo_size = params->fifo_size;
+}
+
+static int snd_pcm_hw_refine_old_user(snd_pcm_substream_t * substream, struct snd_pcm_hw_params_old * _oparams)
+{
+    snd_pcm_hw_params_t params;
+    struct snd_pcm_hw_params_old oparams;
+    int err;
+    if (copy_from_user(&oparams, _oparams, sizeof(oparams)))
+        return -EFAULT;
+    snd_pcm_hw_convert_from_old_params(&params, &oparams);
+    err = snd_pcm_hw_refine(substream, &params);
+    snd_pcm_hw_convert_to_old_params(&oparams, &params);
+    if (copy_to_user(_oparams, &oparams, sizeof(oparams)))
+        return -EFAULT;
+    return err;
+}
+
+static int snd_pcm_hw_params_old_user(snd_pcm_substream_t * substream, struct snd_pcm_hw_params_old * _oparams)
+{
+    snd_pcm_hw_params_t params;
+    struct snd_pcm_hw_params_old oparams;
+    int err;
+    if (copy_from_user(&oparams, _oparams, sizeof(oparams)))
+        return -EFAULT;
+    snd_pcm_hw_convert_from_old_params(&params, &oparams);
+    err = snd_pcm_hw_params(substream, &params);
+    snd_pcm_hw_convert_to_old_params(&oparams, &params);
+    if (copy_to_user(_oparams, &oparams, sizeof(oparams)))
+        return -EFAULT;
+    return err;
+}
+
+/*
+ *  Register section
+ */
+
+static struct file_operations snd_pcm_f_ops_playback = {
+#ifdef LINUX_2_3
+    .owner =	THIS_MODULE,
+#endif
+    .write =	snd_pcm_write,
+#ifdef LINUX_2_3
+    .writev =	snd_pcm_writev,
+#endif
+    .open =	snd_pcm_open,
+    .release =	snd_pcm_release,
+    .poll =	snd_pcm_playback_poll,
+    .ioctl =	snd_pcm_playback_ioctl,
+#ifdef LINUX_2_3
+    .compat_ioctl = snd_pcm_ioctl_compat,
+    .mmap =		snd_pcm_mmap,
+#endif
+#ifndef TARGET_OS2
+    .fasync =	snd_pcm_fasync,
+#endif
+};
+
+static struct file_operations snd_pcm_f_ops_capture = {
+#ifdef LINUX_2_3
+    .owner =	THIS_MODULE,
+#endif
+    .read =		snd_pcm_read,
+#ifdef LINUX_2_3
+    .readv =	snd_pcm_readv,
+#endif
+    .open =		snd_pcm_open,
+    .release =	snd_pcm_release,
+    .poll =		snd_pcm_capture_poll,
+    .ioctl =	snd_pcm_capture_ioctl,
+#ifdef LINUX_2_3
+    .ioctl = snd_pcm_ioctl_compat,
+    .mmap =		snd_pcm_mmap,
+#endif
+#ifndef TARGET_OS2
+    .fasync =	snd_pcm_fasync,
+#endif
+};
+
+struct snd_minor snd_pcm_reg[2] =
+{
+    {
+        .comment =	"digital audio playback",
+        .f_ops =	&snd_pcm_f_ops_playback,
+    },
+    {
+        .comment =	"digital audio capture",
+        .f_ops =	&snd_pcm_f_ops_capture,
+    }
+};
+
+
Index: /GPL/branches/alsa-resync1/alsa-kernel/core/pcm_os2.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/core/pcm_os2.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/core/pcm_os2.c	(revision 91)
@@ -0,0 +1,39 @@
+#ifdef TARGET_OS2
+static int snd_pcm_setvolume(snd_pcm_substream_t * substream, snd_pcm_volume_t * _volume)
+{
+	snd_pcm_volume_t volume;
+	snd_pcm_runtime_t *runtime;
+	int res;
+	
+	snd_assert(substream != NULL, return -ENXIO);
+	if (copy_from_user(&volume, _volume, sizeof(volume)))
+		return -EFAULT;
+	runtime = substream->runtime;
+	if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+		return -EBADFD;
+	res = substream->ops->ioctl(substream, SNDRV_PCM_IOCTL1_SETVOLUME, &volume);
+	if (res < 0)
+		return res;
+	return 0;
+}
+
+static int snd_pcm_getvolume(snd_pcm_substream_t * substream, snd_pcm_volume_t * _volume)
+{
+	snd_pcm_volume_t volume;
+	snd_pcm_runtime_t *runtime;
+	int res;
+	
+	snd_assert(substream != NULL, return -ENXIO);
+    memset(&volume, 0, sizeof(volume));
+	
+    runtime = substream->runtime;
+	if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+		return -EBADFD;
+	res = substream->ops->ioctl(substream, SNDRV_PCM_IOCTL1_GETVOLUME, &volume);
+	if (res < 0)
+		return res;
+	if (copy_to_user(_volume, &volume, sizeof(volume)))
+		return -EFAULT;
+	return 0;
+}
+#endif
Index: /GPL/branches/alsa-resync1/alsa-kernel/core/pcm_sgbuf.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/core/pcm_sgbuf.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/core/pcm_sgbuf.c	(revision 91)
@@ -0,0 +1,323 @@
+/*
+ * Scatter-Gather PCM access
+ *
+ *  Copyright (c) by 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 <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_sgbuf.h>
+
+
+/* table entries are align to 32 */
+#define SGBUF_TBL_ALIGN		32
+#define sgbuf_align_table(tbl)	((((tbl) + SGBUF_TBL_ALIGN - 1) / SGBUF_TBL_ALIGN) * SGBUF_TBL_ALIGN)
+
+/*
+ * shrink to the given pages.
+ * free the unused pages
+ */
+static void sgbuf_shrink(struct snd_sg_buf *sgbuf, int pages)
+{
+	snd_assert(sgbuf, return);
+	if (! sgbuf->table)
+		return;
+	while (sgbuf->pages > pages) {
+		sgbuf->pages--;
+		snd_free_pci_pages(sgbuf->pci, PAGE_SIZE,
+				   sgbuf->table[sgbuf->pages].buf,
+				   sgbuf->table[sgbuf->pages].addr);
+	}
+}
+
+/*
+ * initialize the sg buffer
+ * assigned to substream->dma_private.
+ * initialize the table with the given size.
+ */
+int snd_pcm_sgbuf_init(snd_pcm_substream_t *substream, struct pci_dev *pci, int tblsize)
+{
+	struct snd_sg_buf *sgbuf;
+
+	tblsize = sgbuf_align_table(tblsize);
+	sgbuf = snd_magic_kcalloc(snd_pcm_sgbuf_t, 0, GFP_KERNEL);
+	if (! sgbuf)
+		return -ENOMEM;
+	substream->dma_private = sgbuf;
+	sgbuf->pci = pci;
+	sgbuf->pages = 0;
+	sgbuf->tblsize = tblsize;
+	sgbuf->table = kmalloc(sizeof(struct snd_sg_page) * tblsize, GFP_KERNEL);
+	if (! sgbuf->table) {
+		snd_pcm_sgbuf_free(substream);
+		return -ENOMEM;
+	}
+	memset(sgbuf->table, 0, sizeof(struct snd_sg_page) * tblsize);
+	return 0;
+}
+
+/*
+ * release all pages and free the sgbuf instance
+ */
+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)
+		kfree(sgbuf->table);
+	snd_magic_kfree(sgbuf);
+	substream->dma_private = NULL;
+	return 0;
+}
+
+/*
+ * allocate sg buffer table with the given byte size.
+ * if the buffer table already exists, try to resize it.
+ * call this from hw_params callback.
+ */
+int snd_pcm_sgbuf_alloc(snd_pcm_substream_t *substream, size_t size)
+{
+	struct snd_sg_buf *sgbuf;
+	unsigned int pages;
+	unsigned int tblsize;
+	int changed = 0;
+
+	sgbuf = snd_magic_cast(snd_pcm_sgbuf_t, substream->dma_private, return -EINVAL);
+	pages = snd_pcm_sgbuf_pages(size);
+	tblsize = sgbuf_align_table(pages);
+	if (pages < sgbuf->pages) {
+		/* release unsed pages */
+		sgbuf_shrink(sgbuf, pages);
+		substream->runtime->dma_bytes = size;
+		return 1; /* changed */
+	} else if (pages > sgbuf->tblsize) {
+		/* bigger than existing one.  reallocate the table. */
+		struct snd_sg_page *table;
+		table = kmalloc(sizeof(*table) * tblsize, GFP_KERNEL);
+		if (! table)
+			return -ENOMEM;
+		memcpy(table, sgbuf->table, sizeof(*table) * sgbuf->tblsize);
+		kfree(sgbuf->table);
+		sgbuf->table = table;
+		sgbuf->tblsize = tblsize;
+	}
+	/* allocate each page */
+	while (sgbuf->pages < pages) {
+		void *ptr;
+		dma_addr_t addr;
+		ptr = snd_malloc_pci_pages(sgbuf->pci, PAGE_SIZE, &addr);
+		if (! ptr)
+			return -ENOMEM;
+		sgbuf->table[sgbuf->pages].buf = ptr;
+		sgbuf->table[sgbuf->pages].addr = addr;
+		sgbuf->pages++;
+		changed = 1;
+	}
+	sgbuf->size = size;
+	substream->runtime->dma_bytes = size;
+	return changed;
+}
+
+/*
+ * free the sg buffer
+ * the table is kept.
+ * call this from hw_free callback.
+ */
+int snd_pcm_sgbuf_free(snd_pcm_substream_t *substream)
+{
+	struct snd_sg_buf *sgbuf;
+
+	sgbuf = snd_magic_cast(snd_pcm_sgbuf_t, substream->dma_private, return -EINVAL);
+	sgbuf_shrink(sgbuf, 0);
+	return 0;
+}
+#if 1
+/*
+ * get the page pointer on the given offset
+ * used as the page callback of pcm ops
+ */
+void *snd_pcm_sgbuf_ops_page(snd_pcm_substream_t *substream, unsigned long offset)
+{
+	struct snd_sg_buf *sgbuf;
+	unsigned int idx;
+
+	sgbuf = snd_magic_cast(snd_pcm_sgbuf_t, substream->dma_private, return NULL);
+	idx = offset >> PAGE_SHIFT;
+	if (idx >= sgbuf->pages)
+		return 0;
+	return sgbuf->table[idx].buf;
+}
+#endif
+/*
+ * do copy_from_user to the sg buffer
+ */
+static int copy_from_user_sg_buf(snd_pcm_substream_t *substream,
+				 char *buf, size_t hwoff, ssize_t bytes)
+{
+	int len;
+	char *addr;
+	size_t p = (hwoff >> PAGE_SHIFT) << PAGE_SHIFT;
+	hwoff -= p;
+	len = PAGE_SIZE - hwoff;
+	for (;;) {
+		addr = snd_pcm_sgbuf_ops_page(substream, p);
+		if (! addr)
+			return -EFAULT;
+		if (len > bytes)
+			len = bytes;
+		if (copy_from_user(addr + hwoff, buf, len))
+			return -EFAULT;
+		bytes -= len;
+		if (bytes <= 0)
+			break;
+		buf += len;
+		p += PAGE_SIZE;
+		len = PAGE_SIZE;
+		hwoff = 0;
+	}
+	return 0;
+}
+
+/*
+ * do copy_to_user from the sg buffer
+ */
+static int copy_to_user_sg_buf(snd_pcm_substream_t *substream,
+			       char *buf, size_t hwoff, ssize_t bytes)
+{
+	int len;
+	char *addr;
+	size_t p = (hwoff >> PAGE_SHIFT) << PAGE_SHIFT;
+	hwoff -= p;
+	len = PAGE_SIZE - hwoff;
+	for (;;) {
+		addr = snd_pcm_sgbuf_ops_page(substream, p);
+		if (! addr)
+			return -EFAULT;
+		if (len > bytes)
+			len = bytes;
+		if (copy_to_user(buf, addr + hwoff, len))
+			return -EFAULT;
+		bytes -= len;
+		if (bytes <= 0)
+			break;
+		buf += len;
+		p += PAGE_SIZE;
+		len = PAGE_SIZE;
+		hwoff = 0;
+	}
+	return 0;
+}
+
+/*
+ * set silence on the sg buffer
+ */
+static int set_silence_sg_buf(snd_pcm_substream_t *substream,
+			      size_t hwoff, ssize_t samples)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int len, page_len;
+	char *addr;
+	size_t p = (hwoff >> PAGE_SHIFT) << PAGE_SHIFT;
+	hwoff -= p;
+	len = bytes_to_samples(substream->runtime, PAGE_SIZE - hwoff);
+	page_len = bytes_to_samples(substream->runtime, PAGE_SIZE);
+	for (;;) {
+		addr = snd_pcm_sgbuf_ops_page(substream, p);
+		if (! addr)
+			return -EFAULT;
+		if (len > samples)
+			len = samples;
+		snd_pcm_format_set_silence(runtime->format, addr + hwoff, len);
+		samples -= len;
+		if (samples <= 0)
+			break;
+		p += PAGE_SIZE;
+		len = page_len;
+		hwoff = 0;
+	}
+	return 0;
+}
+
+/*
+ * copy callback for playback pcm ops
+ */
+int snd_pcm_sgbuf_ops_copy_playback(snd_pcm_substream_t *substream, int channel,
+				    snd_pcm_uframes_t hwoff, void *buf, snd_pcm_uframes_t count)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	if (channel < 0) {
+		return copy_from_user_sg_buf(substream, buf, frames_to_bytes(runtime, hwoff), frames_to_bytes(runtime, count));
+	} else {
+		size_t dma_csize = runtime->dma_bytes / runtime->channels;
+		size_t c_ofs = (channel * dma_csize) + samples_to_bytes(runtime, hwoff);
+		return copy_from_user_sg_buf(substream, buf, c_ofs, samples_to_bytes(runtime, count));
+	}
+}
+
+/*
+ * copy callback for capture pcm ops
+ */
+int snd_pcm_sgbuf_ops_copy_capture(snd_pcm_substream_t *substream, int channel,
+				   snd_pcm_uframes_t hwoff, void *buf, snd_pcm_uframes_t count)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	if (channel < 0) {
+		return copy_to_user_sg_buf(substream, buf, frames_to_bytes(runtime, hwoff), frames_to_bytes(runtime, count));
+	} else {
+		size_t dma_csize = runtime->dma_bytes / runtime->channels;
+		size_t c_ofs = (channel * dma_csize) + samples_to_bytes(runtime, hwoff);
+		return copy_to_user_sg_buf(substream, buf, c_ofs, samples_to_bytes(runtime, count));
+	}
+}
+
+/*
+ * silence callback for pcm ops
+ */
+int snd_pcm_sgbuf_ops_silence(snd_pcm_substream_t *substream, int channel,
+			      snd_pcm_uframes_t hwoff, snd_pcm_uframes_t count)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	if (channel < 0) {
+		return set_silence_sg_buf(substream, frames_to_bytes(runtime, hwoff),
+					  frames_to_bytes(runtime, count));
+	} else {
+		size_t dma_csize = runtime->dma_bytes / runtime->channels;
+		size_t c_ofs = (channel * dma_csize) + samples_to_bytes(runtime, hwoff);
+		return set_silence_sg_buf(substream, c_ofs, samples_to_bytes(runtime, count));
+	}
+}
+
+
+/*
+ *  Exported symbols
+ */
+EXPORT_SYMBOL(snd_pcm_sgbuf_init);
+EXPORT_SYMBOL(snd_pcm_sgbuf_delete);
+EXPORT_SYMBOL(snd_pcm_sgbuf_alloc);
+EXPORT_SYMBOL(snd_pcm_sgbuf_free);
+EXPORT_SYMBOL(snd_pcm_sgbuf_ops_copy_playback);
+EXPORT_SYMBOL(snd_pcm_sgbuf_ops_copy_capture);
+EXPORT_SYMBOL(snd_pcm_sgbuf_ops_silence);
+EXPORT_SYMBOL(snd_pcm_sgbuf_ops_page);
Index: /GPL/branches/alsa-resync1/alsa-kernel/core/pcm_timer.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/core/pcm_timer.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/core/pcm_timer.c	(revision 91)
@@ -0,0 +1,159 @@
+/*
+ *  Digital Audio (PCM) abstract layer
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <sound/driver.h>
+#include <sound/pcm.h>
+#include <sound/timer.h>
+
+/*
+ *  Timer functions
+ */
+
+/* Greatest common divisor */
+static unsigned long gcd(unsigned long a, unsigned long b)
+{
+    unsigned long r;
+    if (a < b) {
+        r = a;
+        a = b;
+        b = r;
+    }
+    while ((r = a % b) != 0) {
+        a = b;
+        b = r;
+    }
+    return b;
+}
+
+void snd_pcm_timer_resolution_change(snd_pcm_substream_t *substream)
+{
+    unsigned long rate, mult, fsize, l, post;
+    snd_pcm_runtime_t *runtime = substream->runtime;
+
+    mult = 1000000000;
+    rate = runtime->rate;
+    snd_assert(rate != 0, return);
+    l = gcd(mult, rate);
+    mult /= l;
+    rate /= l;
+    fsize = runtime->period_size;
+    snd_assert(fsize != 0, return);
+    l = gcd(rate, fsize);
+    rate /= l;
+    fsize /= l;
+    post = 1;
+    while ((mult * fsize) / fsize != mult) {
+        mult /= 2;
+        post *= 2;
+    }
+    if (rate == 0) {
+        snd_printk(KERN_ERR "pcm timer resolution out of range (rate = %u, period_size = %lu)\n", runtime->rate, runtime->period_size);
+        runtime->timer_resolution = -1;
+        return;
+    }
+    runtime->timer_resolution = (mult * fsize / rate) * post;
+}
+
+static unsigned long snd_pcm_timer_resolution(snd_timer_t * timer)
+{
+    snd_pcm_substream_t * substream;
+
+    substream = timer->private_data;
+    return substream->runtime ? substream->runtime->timer_resolution : 0;
+}
+
+static int snd_pcm_timer_start(snd_timer_t * timer)
+{
+    unsigned long flags;
+    snd_pcm_substream_t * substream;
+
+    substream = snd_timer_chip(timer);
+    spin_lock_irqsave(&substream->timer_lock, flags);
+    substream->timer_running = 1;
+    spin_unlock_irqrestore(&substream->timer_lock, flags);
+    return 0;
+}
+
+static int snd_pcm_timer_stop(snd_timer_t * timer)
+{
+    unsigned long flags;
+    snd_pcm_substream_t * substream;
+
+    substream = snd_timer_chip(timer);
+    spin_lock_irqsave(&substream->timer_lock, flags);
+    substream->timer_running = 0;
+    spin_unlock_irqrestore(&substream->timer_lock, flags);
+    return 0;
+}
+
+static struct snd_timer_hardware snd_pcm_timer =
+{
+	.flags =	SNDRV_TIMER_HW_AUTO | SNDRV_TIMER_HW_SLAVE,
+	.resolution =	0,
+	.ticks =	1,
+	.c_resolution =	snd_pcm_timer_resolution,
+	.start =	snd_pcm_timer_start,
+	.stop =		snd_pcm_timer_stop,
+};
+
+/*
+ *  Init functions
+ */
+
+static void snd_pcm_timer_free(snd_timer_t *timer)
+{
+    snd_pcm_substream_t *substream = timer->private_data;
+    substream->timer = NULL;
+}
+
+void snd_pcm_timer_init(snd_pcm_substream_t *substream)
+{
+    snd_timer_id_t tid;
+    snd_timer_t *timer;
+
+    tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE;
+    tid.dev_class = SNDRV_TIMER_CLASS_PCM;
+    tid.card = substream->pcm->card->number;
+    tid.device = substream->pcm->device;
+    tid.subdevice = (substream->number << 1) | (substream->stream & 1);
+    if (snd_timer_new(substream->pcm->card, "PCM", &tid, &timer) < 0)
+        return;
+    sprintf(timer->name, "PCM %s %i-%i-%i",
+            substream->stream == SNDRV_PCM_STREAM_CAPTURE ?
+            "capture" : "playback",
+            tid.card, tid.device, tid.subdevice);
+    timer->hw = snd_pcm_timer;
+    if (snd_device_register(timer->card, timer) < 0) {
+        snd_device_free(timer->card, timer);
+        return;
+    }
+    timer->private_data = substream;
+    timer->private_free = snd_pcm_timer_free;
+    substream->timer = timer;
+}
+
+void snd_pcm_timer_done(snd_pcm_substream_t *substream)
+{
+    if (substream->timer) {
+        snd_device_free(substream->pcm->card, substream->timer);
+        substream->timer = NULL;
+    }
+}
Index: /GPL/branches/alsa-resync1/alsa-kernel/core/rawmidi.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/core/rawmidi.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/core/rawmidi.c	(revision 91)
@@ -0,0 +1,1685 @@
+/*
+ *  Abstract layer for MIDI v1.0 stream
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#define SNDRV_MAIN_OBJECT_FILE
+#include <sound/driver.h>
+#include <sound/rawmidi.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/minors.h>
+#include <sound/initval.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Midlevel RawMidi code for ALSA.");
+MODULE_LICENSE("GPL");
+
+#ifdef CONFIG_SND_OSSEMUL
+#ifdef TARGET_OS2
+static int midi_map[SNDRV_CARDS] = {REPEAT_SNDRV(0)};
+static int amidi_map[SNDRV_CARDS] = {REPEAT_SNDRV(1)};
+#else
+static int midi_map[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 0};
+static int amidi_map[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 1};
+#endif
+MODULE_PARM(midi_map, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(midi_map, "Raw MIDI device number assigned to 1st OSS device.");
+MODULE_PARM_SYNTAX(midi_map, "default:0,skill:advanced");
+MODULE_PARM(amidi_map, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(amidi_map, "Raw MIDI device number assigned to 2nd OSS device.");
+MODULE_PARM_SYNTAX(amidi_map, "default:1,skill:advanced");
+#endif /* CONFIG_SND_OSSEMUL */
+
+static int snd_rawmidi_free(snd_rawmidi_t *rawmidi);
+static int snd_rawmidi_dev_free(snd_device_t *device);
+static int snd_rawmidi_dev_register(snd_device_t *device);
+static int snd_rawmidi_dev_disconnect(snd_device_t *device);
+static int snd_rawmidi_dev_unregister(snd_device_t *device);
+
+snd_rawmidi_t *snd_rawmidi_devices[SNDRV_CARDS * SNDRV_RAWMIDI_DEVICES];
+
+static DECLARE_MUTEX(register_mutex);
+
+static inline unsigned short snd_rawmidi_file_flags(struct file *file)
+{
+    switch (file->f_mode & (FMODE_READ | FMODE_WRITE)) {
+    case FMODE_WRITE:
+        return SNDRV_RAWMIDI_LFLG_OUTPUT;
+    case FMODE_READ:
+        return SNDRV_RAWMIDI_LFLG_INPUT;
+    default:
+        return SNDRV_RAWMIDI_LFLG_OPEN;
+    }
+}
+
+static inline int snd_rawmidi_ready(snd_rawmidi_substream_t * substream)
+{
+    snd_rawmidi_runtime_t *runtime = substream->runtime;
+    return runtime->avail >= runtime->avail_min;
+}
+
+static inline int snd_rawmidi_ready_append(snd_rawmidi_substream_t * substream, size_t count)
+{
+    snd_rawmidi_runtime_t *runtime = substream->runtime;
+    return runtime->avail >= runtime->avail_min &&
+        (!substream->append || runtime->avail >= count);
+}
+
+static int snd_rawmidi_init(snd_rawmidi_substream_t *substream)
+{
+    snd_rawmidi_runtime_t *runtime = substream->runtime;
+    spin_lock_init(&runtime->lock);
+    init_waitqueue_head(&runtime->sleep);
+    runtime->event = NULL;
+    runtime->buffer_size = PAGE_SIZE;
+    runtime->avail_min = 1;
+    if (substream->stream == SNDRV_RAWMIDI_STREAM_INPUT)
+        runtime->avail = 0;
+    else
+        runtime->avail = runtime->buffer_size;
+    if ((runtime->buffer = kmalloc(runtime->buffer_size, GFP_KERNEL)) == NULL)
+        return -ENOMEM;
+    runtime->appl_ptr = runtime->hw_ptr = 0;
+    return 0;
+}
+
+static int snd_rawmidi_done_buffer(snd_rawmidi_runtime_t *runtime)
+{
+    if (runtime->buffer) {
+        kfree(runtime->buffer);
+        runtime->buffer = NULL;
+    }
+    return 0;
+}
+
+int snd_rawmidi_drop_output(snd_rawmidi_substream_t * substream)
+{
+    struct snd_rawmidi_runtime *runtime = substream->runtime;
+
+    if (substream->ops->trigger) substream->ops->trigger(substream, 0);
+    runtime->drain = 0;
+    /* interrupts are not enabled at this moment,
+     so spinlock is not required */
+    runtime->appl_ptr = runtime->hw_ptr = 0;
+    runtime->avail = runtime->buffer_size;
+    return 0;
+}
+
+int snd_rawmidi_drain_output(snd_rawmidi_substream_t * substream)
+{
+    int err;
+    long timeout;
+    snd_rawmidi_runtime_t *runtime = substream->runtime;
+
+    err = 0;
+    runtime->drain = 1;
+    while (runtime->avail < runtime->buffer_size) {
+        timeout = interruptible_sleep_on_timeout(&runtime->sleep, 10 * HZ);
+        if (signal_pending(current)) {
+            err = -ERESTARTSYS;
+            break;
+        }
+        if (runtime->avail < runtime->buffer_size && !timeout) {
+            snd_printk(KERN_WARNING "rawmidi drain error (avail = %li, buffer_size = %li)\n", (long)runtime->avail, (long)runtime->buffer_size);
+            err = -EIO;
+            break;
+        }
+    }
+    runtime->drain = 0;
+    if (err != -ERESTARTSYS) {
+        /* we need wait a while to make sure that Tx FIFOs are empty */
+        if (substream->ops->drain)
+            substream->ops->drain(substream);
+        else {
+            set_current_state(TASK_UNINTERRUPTIBLE);
+            schedule_timeout(HZ / 20);
+        }
+        snd_rawmidi_drop_output(substream);
+    }
+    return err;
+}
+
+int snd_rawmidi_drain_input(snd_rawmidi_substream_t * substream)
+{
+    snd_rawmidi_runtime_t *runtime = substream->runtime;
+
+    if (substream->ops->trigger) substream->ops->trigger(substream, 0);
+    runtime->drain = 0;
+    /* interrupts aren't enabled at this moment, so spinlock isn't needed */
+    runtime->appl_ptr = runtime->hw_ptr = 0;
+    runtime->avail = 0;
+    return 0;
+}
+
+int snd_rawmidi_kernel_open(int cardnum, int device, int subdevice,
+                            int mode, snd_rawmidi_file_t * rfile)
+{
+    snd_rawmidi_t *rmidi;
+    struct list_head *list1, *list2;
+    snd_rawmidi_substream_t *sinput = NULL, *soutput = NULL;
+    snd_rawmidi_runtime_t *input = NULL, *output = NULL;
+    int err;
+
+    printk("snd_rawmidi_kernel_open. mode: %i\n", mode);
+    if (rfile)
+        rfile->input = rfile->output = NULL;
+    rmidi = snd_rawmidi_devices[(cardnum * SNDRV_RAWMIDI_DEVICES) + device];
+    if (rmidi == NULL) {
+        err = -ENODEV;
+        goto __error1;
+    }
+    if (!try_module_get(rmidi->card->module)) {
+        err = -EFAULT;
+        goto __error1;
+    }
+    if (!(mode & SNDRV_RAWMIDI_LFLG_NOOPENLOCK))
+        down(&rmidi->open_mutex);
+    if (mode & SNDRV_RAWMIDI_LFLG_INPUT) {
+        if (!(rmidi->info_flags & SNDRV_RAWMIDI_INFO_INPUT)) {
+            err = -ENXIO;
+            goto __error;
+        }
+        if (subdevice >= 0 && (unsigned int)subdevice >= rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substream_count) {
+            err = -ENODEV;
+            goto __error;
+        }
+        if (rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substream_opened >=
+            rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substream_count) {
+            err = -EAGAIN;
+            goto __error;
+        }
+    }
+    if (mode & SNDRV_RAWMIDI_LFLG_OUTPUT) {
+        if (!(rmidi->info_flags & SNDRV_RAWMIDI_INFO_OUTPUT)) {
+            err = -ENXIO;
+            goto __error;
+        }
+        if (subdevice >= 0 && (unsigned int)subdevice >= rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substream_count) {
+            err = -ENODEV;
+            goto __error;
+        }
+        if (rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substream_opened >=
+            rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substream_count) {
+            err = -EAGAIN;
+            goto __error;
+        }
+    }
+    list1 = rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substreams.next;
+    while (1) {
+        if (list1 == &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substreams) {
+            sinput = NULL;
+            if (mode & SNDRV_RAWMIDI_LFLG_INPUT) {
+                err = -EAGAIN;
+                goto __error;
+            }
+            break;
+        }
+        sinput = list_entry(list1, snd_rawmidi_substream_t, list);
+        if ((mode & SNDRV_RAWMIDI_LFLG_INPUT) && sinput->opened)
+            goto __nexti;
+        if (subdevice < 0 || (subdevice >= 0 && subdevice == sinput->number))
+            break;
+    __nexti:
+        list1 = list1->next;
+    }
+    list2 = rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams.next;
+    while (1) {
+        if (list2 == &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams) {
+            soutput = NULL;
+            if (mode & SNDRV_RAWMIDI_LFLG_OUTPUT) {
+                err = -EAGAIN;
+                goto __error;
+            }
+            break;
+        }
+        soutput = list_entry(list2, snd_rawmidi_substream_t, list);
+        if (mode & SNDRV_RAWMIDI_LFLG_OUTPUT) {
+            if (mode & SNDRV_RAWMIDI_LFLG_APPEND) {
+                if (soutput->opened && !soutput->append)
+                    goto __nexto;
+            } else {
+                if (soutput->opened)
+                    goto __nexto;
+            }
+        }
+        if (subdevice < 0 || (subdevice >= 0 && subdevice == soutput->number))
+            break;
+    __nexto:
+        list2 = list2->next;
+    }
+    if (mode & SNDRV_RAWMIDI_LFLG_INPUT) {
+        input = kcalloc(1, sizeof(*input), GFP_KERNEL);
+        if (input == NULL) {
+            err = -ENOMEM;
+            goto __error;
+        }
+        sinput->runtime = input;
+        if (snd_rawmidi_init(sinput) < 0) {
+            err = -ENOMEM;
+            goto __error;
+        }
+        if ((err = sinput->ops->open(sinput)) < 0) {
+            sinput->runtime = NULL;
+            goto __error;
+        }
+        sinput->opened = 1;
+        rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substream_opened++;
+    } else {
+        sinput = NULL;
+    }
+    if (mode & SNDRV_RAWMIDI_LFLG_OUTPUT) {
+        if (soutput->opened)
+            goto __skip_output;
+        output = kcalloc(1, sizeof(*output), GFP_KERNEL);
+        if (output == NULL) {
+            err = -ENOMEM;
+            goto __error;
+        }
+        soutput->runtime = output;
+        if (snd_rawmidi_init(soutput) < 0) {
+            if (mode & SNDRV_RAWMIDI_LFLG_INPUT) {
+                sinput->ops->close(sinput);
+                sinput->runtime = NULL;
+            }
+            err = -ENOMEM;
+            goto __error;
+        }
+        if ((err = soutput->ops->open(soutput)) < 0) {
+            if (mode & SNDRV_RAWMIDI_LFLG_INPUT) {
+                sinput->ops->close(sinput);
+                sinput->runtime = NULL;
+            }
+            soutput->runtime = NULL;
+            goto __error;
+        }
+    __skip_output:
+        soutput->opened = 1;
+        if (mode & SNDRV_RAWMIDI_LFLG_APPEND)
+            soutput->append = 1;
+        if (soutput->use_count++ == 0)
+            soutput->active_sensing = 1;
+        rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substream_opened++;
+    } else {
+        soutput = NULL;
+    }
+    if (!(mode & SNDRV_RAWMIDI_LFLG_NOOPENLOCK))
+        up(&rmidi->open_mutex);
+    if (rfile) {
+        rfile->rmidi = rmidi;
+        rfile->input = sinput;
+        rfile->output = soutput;
+    }
+    return 0;
+__error:
+    if (input != NULL) {
+        snd_rawmidi_done_buffer(input);
+        kfree(input);
+    }
+    if (output != NULL) {
+        snd_rawmidi_done_buffer(output);
+        kfree(output);
+    }
+    module_put(rmidi->card->module);
+    if (!(mode & SNDRV_RAWMIDI_LFLG_NOOPENLOCK))
+        up(&rmidi->open_mutex);
+__error1:
+    return err;
+}
+
+static int snd_rawmidi_open(struct inode *inode, struct file *file)
+{
+    int major = MAJOR(inode->i_rdev);
+    int cardnum;
+    snd_card_t *card;
+    int device, subdevice;
+    unsigned short fflags;
+    int err;
+    snd_rawmidi_t *rmidi;
+    snd_rawmidi_file_t *rawmidi_file;
+    wait_queue_t wait;
+    struct list_head *list;
+    snd_ctl_file_t *kctl;
+
+    printk("rawmidi open\n");
+    switch (major) {
+    case CONFIG_SND_MAJOR:
+        cardnum = SNDRV_MINOR_CARD(MINOR(inode->i_rdev));
+        cardnum %= SNDRV_CARDS;
+        device = SNDRV_MINOR_DEVICE(MINOR(inode->i_rdev)) - SNDRV_MINOR_RAWMIDI;
+        device %= SNDRV_MINOR_RAWMIDIS;
+        break;
+#ifdef CONFIG_SND_OSSEMUL
+    case SOUND_MAJOR:
+        cardnum = SNDRV_MINOR_OSS_CARD(MINOR(inode->i_rdev));
+        cardnum %= SNDRV_CARDS;
+        device = SNDRV_MINOR_OSS_DEVICE(inode->i_rdev) == SNDRV_MINOR_OSS_MIDI ?
+            midi_map[cardnum] : amidi_map[cardnum];
+        break;
+#endif
+    default:
+        return -ENXIO;
+    }
+
+    rmidi = snd_rawmidi_devices[(cardnum * SNDRV_RAWMIDI_DEVICES) + device];
+    if (rmidi == NULL)
+        return -ENODEV;
+#ifdef CONFIG_SND_OSSEMUL
+    if (major == SOUND_MAJOR && !rmidi->ossreg)
+        return -ENXIO;
+#endif
+    if ((file->f_flags & O_APPEND) && !(file->f_flags & O_NONBLOCK))
+        return -EINVAL;		/* invalid combination */
+    card = rmidi->card;
+    err = snd_card_file_add(card, file);
+    if (err < 0)
+        return -ENODEV;
+    fflags = snd_rawmidi_file_flags(file);
+    if ((file->f_flags & O_APPEND) || major != CONFIG_SND_MAJOR) /* OSS emul? */
+        fflags |= SNDRV_RAWMIDI_LFLG_APPEND;
+    fflags |= SNDRV_RAWMIDI_LFLG_NOOPENLOCK;
+    rawmidi_file = kmalloc(sizeof(*rawmidi_file), GFP_KERNEL);
+    if (rawmidi_file == NULL) {
+        snd_card_file_remove(card, file);
+        return -ENOMEM;
+    }
+    init_waitqueue_entry(&wait, current);
+    add_wait_queue(&rmidi->open_wait, &wait);
+    down(&rmidi->open_mutex);
+    while (1) {
+        subdevice = -1;
+        down_read(&card->controls_rwsem);
+        list_for_each(list, &card->ctl_files) {
+            kctl = snd_ctl_file(list);
+            if (kctl->pid == current->pid) {
+                subdevice = kctl->prefer_rawmidi_subdevice;
+                break;
+            }
+        }
+        up_read(&card->controls_rwsem);
+        err = snd_rawmidi_kernel_open(cardnum, device, subdevice, fflags, rawmidi_file);
+        if (err >= 0)
+            break;
+        if (err == -EAGAIN) {
+            if (file->f_flags & O_NONBLOCK) {
+                err = -EBUSY;
+                break;
+            }
+        } else
+            break;
+        set_current_state(TASK_INTERRUPTIBLE);
+        up(&rmidi->open_mutex);
+        schedule();
+        down(&rmidi->open_mutex);
+        if (signal_pending(current)) {
+            err = -ERESTARTSYS;
+            break;
+        }
+    }
+#ifdef CONFIG_SND_OSSEMUL
+    if (rawmidi_file->input && rawmidi_file->input->runtime)
+        rawmidi_file->input->runtime->oss = (major == SOUND_MAJOR);
+    if (rawmidi_file->output && rawmidi_file->output->runtime)
+        rawmidi_file->output->runtime->oss = (major == SOUND_MAJOR);
+#endif
+    remove_wait_queue(&rmidi->open_wait, &wait);
+    if (err >= 0) {
+        file->private_data = rawmidi_file;
+    } else {
+        snd_card_file_remove(card, file);
+        kfree(rawmidi_file);
+    }
+    up(&rmidi->open_mutex);
+    return err;
+}
+
+int snd_rawmidi_kernel_release(snd_rawmidi_file_t * rfile)
+{
+    snd_rawmidi_t *rmidi;
+    snd_rawmidi_substream_t *substream;
+    snd_rawmidi_runtime_t *runtime;
+
+    snd_assert(rfile != NULL, return -ENXIO);
+    snd_assert(rfile->input != NULL || rfile->output != NULL, return -ENXIO);
+    rmidi = rfile->rmidi;
+    down(&rmidi->open_mutex);
+    if (rfile->input != NULL) {
+        substream = rfile->input;
+        rfile->input = NULL;
+        runtime = substream->runtime;
+        if (substream->ops->trigger)
+            substream->ops->trigger(substream, 0);
+        if (substream->ops->close)
+            substream->ops->close(substream);
+        snd_rawmidi_done_buffer(runtime);
+        if (runtime->private_free != NULL)
+            runtime->private_free(substream);
+        kfree(runtime);
+        substream->runtime = NULL;
+        substream->opened = 0;
+        rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substream_opened--;
+    }
+    if (rfile->output != NULL) {
+        substream = rfile->output;
+        rfile->output = NULL;
+        if (--substream->use_count == 0) {
+            runtime = substream->runtime;
+#if 0
+            if (substream->active_sensing) {
+                unsigned char buf = 0xfe;
+                /* sending single active sensing message to shut the device up */
+                snd_rawmidi_kernel_write(substream, &buf, 1);
+            }
+#endif
+            if (snd_rawmidi_drain_output(substream) == -ERESTARTSYS)
+                if (substream->ops->trigger)
+                    substream->ops->trigger(substream, 0);
+            if (substream->ops->close)
+                substream->ops->close(substream);
+            snd_rawmidi_done_buffer(runtime);
+            if (runtime->private_free != NULL)
+                runtime->private_free(substream);
+            kfree(runtime);
+            substream->runtime = NULL;
+            substream->opened = 0;
+            substream->append = 0;
+        }
+        rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substream_opened--;
+    }
+    up(&rmidi->open_mutex);
+    module_put(rmidi->card->module);
+    return 0;
+}
+
+static int snd_rawmidi_release(struct inode *inode, struct file *file)
+{
+    snd_rawmidi_file_t *rfile;
+    snd_rawmidi_t *rmidi;
+    int err = 0;
+
+    rfile = file->private_data;
+    err = snd_rawmidi_kernel_release(rfile);
+    rmidi = rfile->rmidi;
+    wake_up(&rmidi->open_wait);
+    kfree(rfile);
+    snd_card_file_remove(rmidi->card, file);
+    return err;
+}
+
+int snd_rawmidi_info(snd_rawmidi_substream_t *substream, snd_rawmidi_info_t *info)
+{
+    snd_rawmidi_t *rmidi;
+
+    if (substream == NULL)
+        return -ENODEV;
+    rmidi = substream->rmidi;
+    memset(info, 0, sizeof(*info));
+    info->card = rmidi->card->number;
+    info->device = rmidi->device;
+    info->subdevice = substream->number;
+    info->stream = substream->stream;
+    info->flags = rmidi->info_flags;
+    strcpy(info->id, rmidi->id);
+    strcpy(info->name, rmidi->name);
+    strcpy(info->subname, substream->name);
+    info->subdevices_count = substream->pstr->substream_count;
+    info->subdevices_avail = (substream->pstr->substream_count -
+                              substream->pstr->substream_opened);
+    return 0;
+}
+
+static int snd_rawmidi_info_user(snd_rawmidi_substream_t *substream, snd_rawmidi_info_t * _info)
+{
+    snd_rawmidi_info_t info;
+    int err;
+    if ((err = snd_rawmidi_info(substream, &info)) < 0)
+        return err;
+    if (copy_to_user(_info, &info, sizeof(snd_rawmidi_info_t)))
+        return -EFAULT;
+    return 0;
+}
+
+int snd_rawmidi_info_select(snd_card_t *card, snd_rawmidi_info_t *info)
+{
+    snd_rawmidi_t *rmidi;
+    snd_rawmidi_str_t *pstr;
+    snd_rawmidi_substream_t *substream;
+    struct list_head *list;
+    if (info->device >= SNDRV_RAWMIDI_DEVICES)
+        return -ENXIO;
+    rmidi = snd_rawmidi_devices[card->number * SNDRV_RAWMIDI_DEVICES + info->device];
+    if (info->stream < 0 || info->stream > 1)
+        return -EINVAL;
+    pstr = &rmidi->streams[info->stream];
+    if (pstr->substream_count == 0)
+        return -ENOENT;
+    if (info->subdevice >= pstr->substream_count)
+        return -ENXIO;
+    list_for_each(list, &pstr->substreams) {
+        substream = list_entry(list, snd_rawmidi_substream_t, list);
+        if ((unsigned int)substream->number == info->subdevice)
+            return snd_rawmidi_info(substream, info);
+    }
+    return -ENXIO;
+}
+
+static int snd_rawmidi_info_select_user(snd_card_t *card,
+                                        snd_rawmidi_info_t *_info)
+{
+    int err;
+    snd_rawmidi_info_t info;
+    if (get_user(info.device, &_info->device))
+        return -EFAULT;
+    if (get_user(info.stream, &_info->stream))
+        return -EFAULT;
+    if (get_user(info.subdevice, &_info->subdevice))
+        return -EFAULT;
+    if ((err = snd_rawmidi_info_select(card, &info)) < 0)
+        return err;
+    if (copy_to_user(_info, &info, sizeof(snd_rawmidi_info_t)))
+        return -EFAULT;
+    return 0;
+}
+
+int snd_rawmidi_output_params(snd_rawmidi_substream_t * substream,
+                              snd_rawmidi_params_t * params)
+{
+    char *newbuf;
+    snd_rawmidi_runtime_t *runtime = substream->runtime;
+
+    if (substream->append && substream->use_count > 1)
+        return -EBUSY;
+    snd_rawmidi_drain_output(substream);
+    if (params->buffer_size < 32 || params->buffer_size > 1024L * 1024L) {
+        return -EINVAL;
+    }
+    if (params->avail_min < 1 || params->avail_min > params->buffer_size) {
+        return -EINVAL;
+    }
+    if (params->buffer_size != runtime->buffer_size) {
+        if ((newbuf = (char *) kmalloc(params->buffer_size, GFP_KERNEL)) == NULL)
+            return -ENOMEM;
+        kfree(runtime->buffer);
+        runtime->buffer = newbuf;
+        runtime->buffer_size = params->buffer_size;
+    }
+    runtime->avail_min = params->avail_min;
+    substream->active_sensing = !params->no_active_sensing;
+    return 0;
+}
+
+int snd_rawmidi_input_params(snd_rawmidi_substream_t * substream,
+                             snd_rawmidi_params_t * params)
+{
+    char *newbuf;
+    snd_rawmidi_runtime_t *runtime = substream->runtime;
+
+    snd_rawmidi_drain_input(substream);
+    if (params->buffer_size < 32 || params->buffer_size > 1024L * 1024L) {
+        return -EINVAL;
+    }
+    if (params->avail_min < 1 || params->avail_min > params->buffer_size) {
+        return -EINVAL;
+    }
+    if (params->buffer_size != runtime->buffer_size) {
+        if ((newbuf = (char *) kmalloc(params->buffer_size, GFP_KERNEL)) == NULL)
+            return -ENOMEM;
+        kfree(runtime->buffer);
+        runtime->buffer = newbuf;
+        runtime->buffer_size = params->buffer_size;
+    }
+    runtime->avail_min = params->avail_min;
+    return 0;
+}
+
+static int snd_rawmidi_output_status(snd_rawmidi_substream_t * substream,
+                                     snd_rawmidi_status_t * status)
+{
+    snd_rawmidi_runtime_t *runtime = substream->runtime;
+
+    memset(status, 0, sizeof(*status));
+    status->stream = SNDRV_RAWMIDI_STREAM_OUTPUT;
+    spin_lock_irq(&runtime->lock);
+    status->avail = runtime->avail;
+    spin_unlock_irq(&runtime->lock);
+    return 0;
+}
+
+static int snd_rawmidi_input_status(snd_rawmidi_substream_t * substream,
+                                    snd_rawmidi_status_t * status)
+{
+    snd_rawmidi_runtime_t *runtime = substream->runtime;
+
+    memset(status, 0, sizeof(*status));
+    status->stream = SNDRV_RAWMIDI_STREAM_INPUT;
+    spin_lock_irq(&runtime->lock);
+    status->avail = runtime->avail;
+    status->xruns = runtime->xruns;
+    runtime->xruns = 0;
+    spin_unlock_irq(&runtime->lock);
+    return 0;
+}
+
+static int snd_rawmidi_ioctl(struct inode *inode, struct file *file,
+                             unsigned int cmd, unsigned long arg)
+{
+    snd_rawmidi_file_t *rfile;
+
+    rfile = file->private_data;
+    if (((cmd >> 8) & 0xff) != 'W')
+        return -ENOTTY;
+    switch (cmd) {
+    case SNDRV_RAWMIDI_IOCTL_PVERSION:
+        return put_user(SNDRV_RAWMIDI_VERSION, (int *)arg) ? -EFAULT : 0;
+    case SNDRV_RAWMIDI_IOCTL_INFO:
+        {
+            int stream;
+            snd_rawmidi_info_t *info = (snd_rawmidi_info_t *) arg;
+            if (get_user(stream, &info->stream))
+                return -EFAULT;
+            switch (stream) {
+            case SNDRV_RAWMIDI_STREAM_INPUT:
+                return snd_rawmidi_info_user(rfile->input, info);
+            case SNDRV_RAWMIDI_STREAM_OUTPUT:
+                return snd_rawmidi_info_user(rfile->output, info);
+            default:
+                return -EINVAL;
+            }
+        }
+    case SNDRV_RAWMIDI_IOCTL_PARAMS:
+        {
+            snd_rawmidi_params_t params;
+            if (copy_from_user(&params, (snd_rawmidi_params_t *) arg, sizeof(snd_rawmidi_params_t)))
+                return -EFAULT;
+            switch (params.stream) {
+            case SNDRV_RAWMIDI_STREAM_OUTPUT:
+                if (rfile->output == NULL)
+                    return -EINVAL;
+                return snd_rawmidi_output_params(rfile->output, &params);
+            case SNDRV_RAWMIDI_STREAM_INPUT:
+                if (rfile->input == NULL)
+                    return -EINVAL;
+                return snd_rawmidi_input_params(rfile->input, &params);
+            default:
+                return -EINVAL;
+            }
+        }
+    case SNDRV_RAWMIDI_IOCTL_STATUS:
+        {
+            int err = 0;
+            snd_rawmidi_status_t status;
+            if (copy_from_user(&status, (snd_rawmidi_status_t *) arg, sizeof(snd_rawmidi_status_t)))
+                return -EFAULT;
+            switch (status.stream) {
+            case SNDRV_RAWMIDI_STREAM_OUTPUT:
+                if (rfile->output == NULL)
+                    return -EINVAL;
+                err = snd_rawmidi_output_status(rfile->output, &status);
+                break;
+            case SNDRV_RAWMIDI_STREAM_INPUT:
+                if (rfile->input == NULL)
+                    return -EINVAL;
+                err = snd_rawmidi_input_status(rfile->input, &status);
+                break;
+            default:
+                return -EINVAL;
+            }
+            if (err < 0)
+                return err;
+            if (copy_to_user((snd_rawmidi_status_t *) arg, &status, sizeof(snd_rawmidi_status_t)))
+                return -EFAULT;
+            return 0;
+        }
+    case SNDRV_RAWMIDI_IOCTL_DROP:
+        {
+            int val;
+            if (get_user(val, (long *) arg))
+                return -EFAULT;
+            switch (val) {
+            case SNDRV_RAWMIDI_STREAM_OUTPUT:
+                if (rfile->output == NULL)
+                    return -EINVAL;
+                return snd_rawmidi_drop_output(rfile->output);
+            default:
+                return -EINVAL;
+            }
+        }
+    case SNDRV_RAWMIDI_IOCTL_DRAIN:
+        {
+            int val;
+            if (get_user(val, (long *) arg))
+                return -EFAULT;
+            switch (val) {
+            case SNDRV_RAWMIDI_STREAM_OUTPUT:
+                if (rfile->output == NULL)
+                    return -EINVAL;
+                return snd_rawmidi_drain_output(rfile->output);
+            case SNDRV_RAWMIDI_STREAM_INPUT:
+                if (rfile->input == NULL)
+                    return -EINVAL;
+                return snd_rawmidi_drain_input(rfile->input);
+            default:
+                return -EINVAL;
+            }
+        }
+#ifdef CONFIG_SND_DEBUG
+    default:
+        snd_printk("rawmidi: unknown command = 0x%x\n", cmd);
+#endif
+    }
+    return -ENOTTY;
+}
+
+int snd_rawmidi_control_ioctl(snd_card_t * card, snd_ctl_file_t * control,
+                              unsigned int cmd, unsigned long arg)
+{
+    unsigned int tmp;
+
+    tmp = card->number * SNDRV_RAWMIDI_DEVICES;
+    switch (cmd) {
+    case SNDRV_CTL_IOCTL_RAWMIDI_NEXT_DEVICE:
+        {
+            int device;
+
+            if (get_user(device, (int *)arg))
+                return -EFAULT;
+            device = device < 0 ? 0 : device + 1;
+            while (device < SNDRV_RAWMIDI_DEVICES) {
+                if (snd_rawmidi_devices[tmp + device])
+                    break;
+                device++;
+            }
+            if (device == SNDRV_RAWMIDI_DEVICES)
+                device = -1;
+            if (put_user(device, (int *)arg))
+                return -EFAULT;
+            return 0;
+        }
+    case SNDRV_CTL_IOCTL_RAWMIDI_PREFER_SUBDEVICE:
+        {
+            int val;
+
+            if (get_user(val, (int *)arg))
+                return -EFAULT;
+            control->prefer_rawmidi_subdevice = val;
+            return 0;
+        }
+    case SNDRV_CTL_IOCTL_RAWMIDI_INFO:
+        return snd_rawmidi_info_select_user(card, (snd_rawmidi_info_t *)arg);
+    }
+    return -ENOIOCTLCMD;
+}
+
+/**
+ * snd_rawmidi_receive - receive the input data from the device
+ * @substream: the rawmidi substream
+ * @buffer: the buffer pointer
+ * @count: the data size to read
+ *
+ * Reads the data from the internal buffer.
+ *
+ * Returns the size of read data, or a negative error code on failure.
+ */
+int snd_rawmidi_receive(snd_rawmidi_substream_t * substream, const unsigned char *buffer, int count)
+{
+    unsigned long flags;
+    int result = 0, count1;
+    snd_rawmidi_runtime_t *runtime = substream->runtime;
+
+    if (runtime->buffer == NULL) {
+        snd_printd("snd_rawmidi_receive: input is not active!!!\n");
+        return -EINVAL;
+    }
+    //        printk("got %i bytes from mpu\n", count);
+    spin_lock_irqsave(&runtime->lock, flags);
+    if (count == 1) {	/* special case, faster code */
+        substream->bytes++;
+        if (runtime->avail < runtime->buffer_size) {
+            runtime->buffer[runtime->hw_ptr++] = buffer[0];
+            runtime->hw_ptr %= runtime->buffer_size;
+            runtime->avail++;
+            result++;
+        } else {
+            runtime->xruns++;
+        }
+    } else {
+        substream->bytes += count;
+        count1 = runtime->buffer_size - runtime->hw_ptr;
+        if (count1 > count)
+            count1 = count;
+        if (count1 > (int)(runtime->buffer_size - runtime->avail))
+            count1 = runtime->buffer_size - runtime->avail;
+        memcpy(runtime->buffer + runtime->hw_ptr, buffer, count1);
+        runtime->hw_ptr += count1;
+        runtime->hw_ptr %= runtime->buffer_size;
+        runtime->avail += count1;
+        count -= count1;
+        result += count1;
+        if (count > 0) {
+            buffer += count1;
+            count1 = count;
+            if (count1 > (int)(runtime->buffer_size - runtime->avail)) {
+                count1 = runtime->buffer_size - runtime->avail;
+                runtime->xruns += count - count1;
+            }
+            if (count1 > 0) {
+                memcpy(runtime->buffer, buffer, count1);
+                runtime->hw_ptr = count1;
+                runtime->avail += count1;
+                result += count1;
+            }
+        }
+    }
+    if (result > 0 && runtime->event == NULL) {
+        if (snd_rawmidi_ready(substream))
+            wake_up(&runtime->sleep);
+    }
+    spin_unlock_irqrestore(&runtime->lock, flags);
+    if (result > 0 && runtime->event)
+        runtime->event(substream);
+    OSS32_ProcessMIDIIRQ(result);
+    return result;
+}
+
+static long snd_rawmidi_kernel_read1(snd_rawmidi_substream_t *substream,
+                                     unsigned char *buf, long count, int kernel)
+{
+    unsigned long flags;
+    long result = 0, count1;
+    snd_rawmidi_runtime_t *runtime = substream->runtime;
+
+    while (count > 0 && runtime->avail) {
+        count1 = runtime->buffer_size - runtime->appl_ptr;
+        if (count1 > count)
+            count1 = count;
+        spin_lock_irqsave(&runtime->lock, flags);
+        if (count1 > (int)runtime->avail)
+            count1 = runtime->avail;
+        if (kernel) {
+            memcpy(buf + result, runtime->buffer + runtime->appl_ptr, count1);
+        } else {
+            if (copy_to_user(buf + result, runtime->buffer + runtime->appl_ptr, count1)) {
+                spin_unlock_irqrestore(&runtime->lock, flags);
+                return result > 0 ? result : -EFAULT;
+            }
+        }
+        runtime->appl_ptr += count1;
+        runtime->appl_ptr %= runtime->buffer_size;
+        runtime->avail -= count1;
+        spin_unlock_irqrestore(&runtime->lock, flags);
+        result += count1;
+        count -= count1;
+    }
+    return result;
+}
+
+long snd_rawmidi_kernel_read(snd_rawmidi_substream_t *substream, unsigned char *buf, long count)
+{
+    if (substream->ops->trigger)
+        substream->ops->trigger(substream, 1);
+    return snd_rawmidi_kernel_read1(substream, buf, count, 1);
+}
+
+static ssize_t snd_rawmidi_read(struct file *file, char *buf, size_t count, loff_t *offset)
+{
+    long result;
+    int count1;
+    snd_rawmidi_file_t *rfile;
+    snd_rawmidi_substream_t *substream;
+    snd_rawmidi_runtime_t *runtime;
+
+    rfile = file->private_data;
+    substream = rfile->input;
+    if (substream == NULL)
+        return -EIO;
+    runtime = substream->runtime;
+    if (substream->ops->trigger)
+        substream->ops->trigger(substream, 1);
+    result = 0;
+    while (count > 0) {
+        spin_lock_irq(&runtime->lock);
+        while (!snd_rawmidi_ready(substream)) {
+            wait_queue_t wait;
+            if ((file->f_flags & O_NONBLOCK) != 0 || result > 0) {
+                spin_unlock_irq(&runtime->lock);
+                return result > 0 ? result : -EAGAIN;
+            }
+            init_waitqueue_entry(&wait, current);
+            add_wait_queue(&runtime->sleep, &wait);
+            set_current_state(TASK_INTERRUPTIBLE);
+            spin_unlock_irq(&runtime->lock);
+            schedule();
+            remove_wait_queue(&runtime->sleep, &wait);
+            if (signal_pending(current))
+                return result > 0 ? result : -ERESTARTSYS;
+            if (!runtime->avail)
+                return result > 0 ? result : -EIO;
+            spin_lock_irq(&runtime->lock);
+        }
+        spin_unlock_irq(&runtime->lock);
+        count1 = snd_rawmidi_kernel_read1(substream, buf, count, 0);
+        if (count1 < 0)
+            return result > 0 ? result : count1;
+        result += count1;
+        buf += count1;
+        count -= count1;
+    }
+    return result;
+}
+
+/**
+ * snd_rawmidi_transmit_empty - check whether the output buffer is empty
+ * @substream: the rawmidi substream
+ *
+ * Returns 1 if the internal output buffer is empty, 0 if not.
+ */
+int snd_rawmidi_transmit_empty(snd_rawmidi_substream_t * substream)
+{
+    snd_rawmidi_runtime_t *runtime = substream->runtime;
+    int result;
+    unsigned long flags;
+
+    if (runtime->buffer == NULL) {
+        snd_printd("snd_rawmidi_transmit_empty: output is not active!!!\n");
+        return 1;
+    }
+    spin_lock_irqsave(&runtime->lock, flags);
+    result = runtime->avail >= runtime->buffer_size;
+    spin_unlock_irqrestore(&runtime->lock, flags);
+    return result;
+}
+
+/**
+ * snd_rawmidi_transmit_peek - copy data from the internal buffer
+ * @substream: the rawmidi substream
+ * @buffer: the buffer pointer
+ * @count: data size to transfer
+ *
+ * Copies data from the internal output buffer to the given buffer.
+ *
+ * Call this in the interrupt handler when the midi output is ready,
+ * and call snd_rawmidi_transmit_ack() after the transmission is
+ * finished.
+ *
+ * Returns the size of copied data, or a negative error code on failure.
+ */
+int snd_rawmidi_transmit_peek(snd_rawmidi_substream_t * substream, unsigned char *buffer, int count)
+{
+    unsigned long flags;
+    int result, count1;
+    snd_rawmidi_runtime_t *runtime = substream->runtime;
+
+    if (runtime->buffer == NULL) {
+        snd_printd("snd_rawmidi_transmit_peek: output is not active!!!\n");
+        return -EINVAL;
+    }
+    result = 0;
+    spin_lock_irqsave(&runtime->lock, flags);
+    if (runtime->avail >= runtime->buffer_size) {
+        /* warning: lowlevel layer MUST trigger down the hardware */
+        goto __skip;
+    }
+    if (count == 1) {	/* special case, faster code */
+        *buffer = runtime->buffer[runtime->hw_ptr];
+        result++;
+    } else {
+        count1 = runtime->buffer_size - runtime->hw_ptr;
+        if (count1 > count)
+            count1 = count;
+        if (count1 > (int)(runtime->buffer_size - runtime->avail))
+            count1 = runtime->buffer_size - runtime->avail;
+        memcpy(buffer, runtime->buffer + runtime->hw_ptr, count1);
+        count -= count1;
+        result += count1;
+        if (count > 0) {
+            if (count > (int)(runtime->buffer_size - runtime->avail - count1))
+                count = runtime->buffer_size - runtime->avail - count1;
+            memcpy(buffer + count1, runtime->buffer, count);
+            result += count;
+        }
+    }
+__skip:
+    spin_unlock_irqrestore(&runtime->lock, flags);
+    return result;
+}
+
+/**
+ * snd_rawmidi_transmit_ack - acknowledge the transmission
+ * @substream: the rawmidi substream
+ * @count: the tranferred count
+ *
+ * Advances the hardware pointer for the internal output buffer with
+ * the given size and updates the condition.
+ * Call after the transmission is finished.
+ *
+ * Returns the advanced size if successful, or a negative error code on failure.
+ */
+int snd_rawmidi_transmit_ack(snd_rawmidi_substream_t * substream, int count)
+{
+    unsigned long flags;
+    snd_rawmidi_runtime_t *runtime = substream->runtime;
+
+    if (runtime->buffer == NULL) {
+        snd_printd("snd_rawmidi_transmit_ack: output is not active!!!\n");
+        return -EINVAL;
+    }
+    spin_lock_irqsave(&runtime->lock, flags);
+    snd_assert(runtime->avail + count <= runtime->buffer_size, );
+    runtime->hw_ptr += count;
+    runtime->hw_ptr %= runtime->buffer_size;
+    runtime->avail += count;
+    substream->bytes += count;
+    if (runtime->drain)
+        wake_up(&runtime->sleep);
+    else
+        if (count > 0 && runtime->event == NULL)
+            if (snd_rawmidi_ready(substream))
+                wake_up(&runtime->sleep);
+    spin_unlock_irqrestore(&runtime->lock, flags);
+    if (count > 0 && runtime->event)
+        runtime->event(substream);
+    return count;
+}
+
+/**
+ * snd_rawmidi_transmit - copy from the buffer to the device
+ * @substream: the rawmidi substream
+ * @buf: the buffer pointer
+ * @count: the data size to transfer
+ *
+ * Copies data from the buffer to the device and advances the pointer.
+ *
+ * Returns the copied size if successful, or a negative error code on failure.
+ */
+int snd_rawmidi_transmit(snd_rawmidi_substream_t * substream, unsigned char *buffer, int count)
+{
+    count = snd_rawmidi_transmit_peek(substream, buffer, count);
+    if (count < 0)
+        return count;
+    return snd_rawmidi_transmit_ack(substream, count);
+}
+
+static long snd_rawmidi_kernel_write1(snd_rawmidi_substream_t * substream, const unsigned char *buf, long count, int kernel)
+{
+    unsigned long flags;
+    long count1, result;
+    snd_rawmidi_runtime_t *runtime = substream->runtime;
+
+    snd_assert(buf != NULL, return -EINVAL);
+    snd_assert(runtime->buffer != NULL, return -EINVAL);
+
+    result = 0;
+    spin_lock_irqsave(&runtime->lock, flags);
+    if (substream->append) {
+        if ((long)runtime->avail < count) {
+            spin_unlock_irqrestore(&runtime->lock, flags);
+            return -EAGAIN;
+        }
+    }
+    while (count > 0 && runtime->avail > 0) {
+        count1 = runtime->buffer_size - runtime->appl_ptr;
+        if (count1 > count)
+            count1 = count;
+        if (count1 > (long)runtime->avail)
+            count1 = runtime->avail;
+        if (kernel) {
+            memcpy(runtime->buffer + runtime->appl_ptr, buf, count1);
+        } else {
+            if (copy_from_user(runtime->buffer + runtime->appl_ptr, buf, count1)) {
+                result = result > 0 ? result : -EFAULT;
+                goto __end;
+            }
+        }
+        runtime->appl_ptr += count1;
+        runtime->appl_ptr %= runtime->buffer_size;
+        runtime->avail -= count1;
+        result += count1;
+        buf += count1;
+        count -= count1;
+    }
+__end:
+    count1 = runtime->avail < runtime->buffer_size;
+    spin_unlock_irqrestore(&runtime->lock, flags);
+    if (count1)
+        if (substream->ops->trigger)
+            substream->ops->trigger(substream, 1);
+    return result;
+}
+
+long snd_rawmidi_kernel_write(snd_rawmidi_substream_t * substream, const unsigned char *buf, long count)
+{
+    return snd_rawmidi_kernel_write1(substream, buf, count, 1);
+}
+
+static ssize_t snd_rawmidi_write(struct file *file, const char *buf, size_t count, loff_t *offset)
+{
+    long result, timeout;
+    int count1;
+    snd_rawmidi_file_t *rfile;
+    snd_rawmidi_runtime_t *runtime;
+    snd_rawmidi_substream_t *substream;
+
+    rfile = file->private_data;
+    substream = rfile->output;
+    runtime = substream->runtime;
+    /* we cannot put an atomic message to our buffer */
+    if (substream->append && count > runtime->buffer_size)
+        return -EIO;
+    result = 0;
+    while (count > 0) {
+        spin_lock_irq(&runtime->lock);
+        while (!snd_rawmidi_ready_append(substream, count)) {
+            wait_queue_t wait;
+            if (file->f_flags & O_NONBLOCK) {
+                spin_unlock_irq(&runtime->lock);
+                return result > 0 ? result : -EAGAIN;
+            }
+            init_waitqueue_entry(&wait, current);
+            add_wait_queue(&runtime->sleep, &wait);
+            set_current_state(TASK_INTERRUPTIBLE);
+            spin_unlock_irq(&runtime->lock);
+            timeout = schedule_timeout(30 * HZ);
+            remove_wait_queue(&runtime->sleep, &wait);
+            if (signal_pending(current))
+                return result > 0 ? result : -ERESTARTSYS;
+            if (!runtime->avail && !timeout)
+                return result > 0 ? result : -EIO;
+            spin_lock_irq(&runtime->lock);
+        }
+        spin_unlock_irq(&runtime->lock);
+        count1 = snd_rawmidi_kernel_write1(substream, buf, count, 0);
+        if (count1 < 0)
+            return result > 0 ? result : count1;
+        result += count1;
+        buf += count1;
+        if ((size_t)count1 < count && (file->f_flags & O_NONBLOCK))
+            break;
+        count -= count1;
+    }
+    if (file->f_flags & O_SYNC) {
+        spin_lock_irq(&runtime->lock);
+        while (runtime->avail != runtime->buffer_size) {
+            wait_queue_t wait;
+            unsigned int last_avail = runtime->avail;
+            init_waitqueue_entry(&wait, current);
+            add_wait_queue(&runtime->sleep, &wait);
+            set_current_state(TASK_INTERRUPTIBLE);
+            spin_unlock_irq(&runtime->lock);
+            timeout = schedule_timeout(30 * HZ);
+            remove_wait_queue(&runtime->sleep, &wait);
+            if (signal_pending(current))
+                return result > 0 ? result : -ERESTARTSYS;
+            if (runtime->avail == last_avail && !timeout)
+                return result > 0 ? result : -EIO;
+            spin_lock_irq(&runtime->lock);
+        }
+        spin_unlock_irq(&runtime->lock);
+    }
+    return result;
+}
+
+static unsigned int snd_rawmidi_poll(struct file *file, poll_table * wait)
+{
+    snd_rawmidi_file_t *rfile;
+    snd_rawmidi_runtime_t *runtime;
+    unsigned int mask;
+
+    rfile = file->private_data;
+    if (rfile->input != NULL) {
+        runtime = rfile->input->runtime;
+        rfile->input->ops->trigger(rfile->input, 1);
+        poll_wait(file, &runtime->sleep, wait);
+    }
+    if (rfile->output != NULL) {
+        runtime = rfile->output->runtime;
+        poll_wait(file, &runtime->sleep, wait);
+    }
+    mask = 0;
+    if (rfile->input != NULL) {
+        if (snd_rawmidi_ready(rfile->input))
+            mask |= POLLIN | POLLRDNORM;
+    }
+    if (rfile->output != NULL) {
+        if (snd_rawmidi_ready(rfile->output))
+            mask |= POLLOUT | POLLWRNORM;
+    }
+    return mask;
+}
+
+/*
+
+*/
+
+static void snd_rawmidi_proc_info_read(snd_info_entry_t *entry,
+                                       snd_info_buffer_t * buffer)
+{
+    snd_rawmidi_t *rmidi;
+    snd_rawmidi_substream_t *substream;
+    snd_rawmidi_runtime_t *runtime;
+    struct list_head *list;
+
+    rmidi = entry->private_data;
+    snd_iprintf(buffer, "%s\n\n", rmidi->name);
+    down(&rmidi->open_mutex);
+    if (rmidi->info_flags & SNDRV_RAWMIDI_INFO_OUTPUT) {
+        list_for_each(list, &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams) {
+            substream = list_entry(list, snd_rawmidi_substream_t, list);
+            snd_iprintf(buffer,
+                        "Output %d\n"
+                        "  Tx bytes     : %lu\n",
+                        substream->number,
+                        (unsigned long) substream->bytes);
+            if (substream->opened) {
+                runtime = substream->runtime;
+                snd_iprintf(buffer,
+                            "  Mode         : %s\n"
+                            "  Buffer size  : %lu\n"
+                            "  Avail        : %lu\n",
+                            runtime->oss ? "OSS compatible" : "native",
+                            (unsigned long) runtime->buffer_size,
+                            (unsigned long) runtime->avail);
+            }
+        }
+    }
+    if (rmidi->info_flags & SNDRV_RAWMIDI_INFO_INPUT) {
+        list_for_each(list, &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substreams) {
+            substream = list_entry(list, snd_rawmidi_substream_t, list);
+            snd_iprintf(buffer,
+                        "Input %d\n"
+                        "  Rx bytes     : %lu\n",
+                        substream->number,
+                        (unsigned long) substream->bytes);
+            if (substream->opened) {
+                runtime = substream->runtime;
+                snd_iprintf(buffer,
+                            "  Buffer size  : %lu\n"
+                            "  Avail        : %lu\n"
+                            "  Overruns     : %lu\n",
+                            (unsigned long) runtime->buffer_size,
+                            (unsigned long) runtime->avail,
+                            (unsigned long) runtime->xruns);
+            }
+        }
+    }
+    up(&rmidi->open_mutex);
+}
+
+/*
+ *  Register functions
+ */
+
+static struct file_operations snd_rawmidi_f_ops =
+{
+#ifndef TARGET_OS2
+    .owner =	THIS_MODULE,
+#endif
+    .read =		snd_rawmidi_read,
+    .write =	snd_rawmidi_write,
+    .open =		snd_rawmidi_open,
+    .release =	snd_rawmidi_release,
+    .poll =		snd_rawmidi_poll,
+    .ioctl =	snd_rawmidi_ioctl,
+#ifndef TARGET_OS2
+    .compat_ioctl =	snd_rawmidi_ioctl_compat,
+#endif
+};
+
+static struct snd_minor snd_rawmidi_reg =
+{
+    .comment =	"raw midi",
+    .f_ops =	&snd_rawmidi_f_ops,
+};
+
+
+static int snd_rawmidi_alloc_substreams(snd_rawmidi_t *rmidi,
+                                        snd_rawmidi_str_t *stream,
+                                        int direction,
+                                        int count)
+{
+    snd_rawmidi_substream_t *substream;
+    int idx;
+
+    INIT_LIST_HEAD(&stream->substreams);
+    for (idx = 0; idx < count; idx++) {
+        substream = kcalloc(1, sizeof(*substream), GFP_KERNEL);
+        if (substream == NULL)
+            return -ENOMEM;
+        substream->stream = direction;
+        substream->number = idx;
+        substream->rmidi = rmidi;
+        substream->pstr = stream;
+        list_add_tail(&substream->list, &stream->substreams);
+        stream->substream_count++;
+    }
+    return 0;
+}
+
+/**
+ * snd_rawmidi_new - create a rawmidi instance
+ * @card: the card instance
+ * @id: the id string
+ * @device: the device index
+ * @output_count: the number of output streams
+ * @input_count: the number of input streams
+ * @rrawmidi: the pointer to store the new rawmidi instance
+ *
+ * Creates a new rawmidi instance.
+ * Use snd_rawmidi_set_ops() to set the operators to the new instance.
+ *
+ * Returns zero if successful, or a negative error code on failure.
+ */
+int snd_rawmidi_new(snd_card_t * card, char *id, int device,
+                    int output_count, int input_count,
+                    snd_rawmidi_t ** rrawmidi)
+{
+    snd_rawmidi_t *rmidi;
+    int err;
+    static snd_device_ops_t ops = {
+        snd_rawmidi_dev_free,
+        snd_rawmidi_dev_register,
+        snd_rawmidi_dev_disconnect,
+        snd_rawmidi_dev_unregister
+    };
+
+    snd_assert(rrawmidi != NULL, return -EINVAL);
+    *rrawmidi = NULL;
+    snd_assert(card != NULL, return -ENXIO);
+    rmidi = kcalloc(1, sizeof(*rmidi), GFP_KERNEL);
+    if (rmidi == NULL)
+        return -ENOMEM;
+    rmidi->card = card;
+    rmidi->device = device;
+    init_MUTEX(&rmidi->open_mutex);
+    init_waitqueue_head(&rmidi->open_wait);
+    if (id != NULL)
+        strlcpy(rmidi->id, id, sizeof(rmidi->id));
+    if ((err = snd_rawmidi_alloc_substreams(rmidi, &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT], SNDRV_RAWMIDI_STREAM_INPUT, input_count)) < 0) {
+        snd_rawmidi_free(rmidi);
+        return err;
+    }
+    if ((err = snd_rawmidi_alloc_substreams(rmidi, &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT], SNDRV_RAWMIDI_STREAM_OUTPUT, output_count)) < 0) {
+        snd_rawmidi_free(rmidi);
+        return err;
+    }
+    if ((err = snd_device_new(card, SNDRV_DEV_RAWMIDI, rmidi, &ops)) < 0) {
+        snd_rawmidi_free(rmidi);
+        return err;
+    }
+    *rrawmidi = rmidi;
+    return 0;
+}
+
+static void snd_rawmidi_free_substreams(snd_rawmidi_str_t *stream)
+{
+    snd_rawmidi_substream_t *substream;
+
+    while (!list_empty(&stream->substreams)) {
+        substream = list_entry(stream->substreams.next, snd_rawmidi_substream_t, list);
+        list_del(&substream->list);
+        kfree(substream);
+    }
+}
+
+static int snd_rawmidi_free(snd_rawmidi_t *rmidi)
+{
+    snd_assert(rmidi != NULL, return -ENXIO);
+    snd_rawmidi_free_substreams(&rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT]);
+    snd_rawmidi_free_substreams(&rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT]);
+    if (rmidi->private_free)
+        rmidi->private_free(rmidi);
+    kfree(rmidi);
+    return 0;
+}
+
+static int snd_rawmidi_dev_free(snd_device_t *device)
+{
+    snd_rawmidi_t *rmidi = device->device_data;
+    return snd_rawmidi_free(rmidi);
+}
+
+#ifdef CONFIG_SND_SEQUENCER
+static void snd_rawmidi_dev_seq_free(snd_seq_device_t *device)
+{
+    snd_rawmidi_t *rmidi = device->private_data;
+    rmidi->seq_dev = NULL;
+}
+#endif
+
+static int snd_rawmidi_dev_register(snd_device_t *device)
+{
+    int idx, err;
+    snd_info_entry_t *entry;
+    char name[16];
+    snd_rawmidi_t *rmidi = device->device_data;
+
+    if (rmidi->device >= SNDRV_RAWMIDI_DEVICES)
+        return -ENOMEM;
+    down(&register_mutex);
+    idx = (rmidi->card->number * SNDRV_RAWMIDI_DEVICES) + rmidi->device;
+    if (snd_rawmidi_devices[idx] != NULL) {
+        up(&register_mutex);
+        return -EBUSY;
+    }
+    snd_rawmidi_devices[idx] = rmidi;
+    sprintf(name, "midiC%iD%i", rmidi->card->number, rmidi->device);
+    if ((err = snd_register_device(SNDRV_DEVICE_TYPE_RAWMIDI,
+                                   rmidi->card, rmidi->device,
+                                   &snd_rawmidi_reg, name)) < 0) {
+        snd_printk("unable to register rawmidi device %i:%i\n", rmidi->card->number, rmidi->device);
+        snd_rawmidi_devices[idx] = NULL;
+        up(&register_mutex);
+        return err;
+    }
+    if (rmidi->ops && rmidi->ops->dev_register &&
+        (err = rmidi->ops->dev_register(rmidi)) < 0) {
+        snd_unregister_device(SNDRV_DEVICE_TYPE_RAWMIDI, rmidi->card, rmidi->device);
+        snd_rawmidi_devices[idx] = NULL;
+        up(&register_mutex);
+        return err;
+    }
+#ifdef CONFIG_SND_OSSEMUL
+    rmidi->ossreg = 0;
+    if ((int)rmidi->device == midi_map[rmidi->card->number]) {
+        if (snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MIDI,
+                                    rmidi->card, 0, &snd_rawmidi_reg, name) < 0) {
+            snd_printk("unable to register OSS rawmidi device %i:%i\n", rmidi->card->number, 0);
+        } else {
+            rmidi->ossreg++;
+#ifdef SNDRV_OSS_INFO_DEV_MIDI
+            snd_oss_info_register(SNDRV_OSS_INFO_DEV_MIDI, rmidi->card->number, rmidi->name);
+#endif
+        }
+    }
+    if ((int)rmidi->device == amidi_map[rmidi->card->number]) {
+        if (snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MIDI,
+                                    rmidi->card, 1, &snd_rawmidi_reg, name) < 0) {
+            snd_printk("unable to register OSS rawmidi device %i:%i\n", rmidi->card->number, 1);
+        } else {
+            rmidi->ossreg++;
+        }
+    }
+#endif /* CONFIG_SND_OSSEMUL */
+    up(&register_mutex);
+    sprintf(name, "midi%d", rmidi->device);
+    entry = snd_info_create_card_entry(rmidi->card, name, rmidi->card->proc_root);
+    if (entry) {
+        entry->private_data = rmidi;
+        entry->c.text.read_size = 1024;
+        entry->c.text.read = snd_rawmidi_proc_info_read;
+        if (snd_info_register(entry) < 0) {
+            snd_info_free_entry(entry);
+            entry = NULL;
+        }
+    }
+    rmidi->proc_entry = entry;
+#ifdef CONFIG_SND_SEQUENCER
+    if (!rmidi->ops || !rmidi->ops->dev_register) { /* own registration mechanism */
+        if (snd_seq_device_new(rmidi->card, rmidi->device, SNDRV_SEQ_DEV_ID_MIDISYNTH, 0, &rmidi->seq_dev) >= 0) {
+            rmidi->seq_dev->private_data = rmidi;
+            rmidi->seq_dev->private_free = snd_rawmidi_dev_seq_free;
+            sprintf(rmidi->seq_dev->name, "MIDI %d-%d", rmidi->card->number, rmidi->device);
+            snd_device_register(rmidi->card, rmidi->seq_dev);
+        }
+    }
+#endif
+    return 0;
+}
+
+static int snd_rawmidi_dev_disconnect(snd_device_t *device)
+{
+    snd_rawmidi_t *rmidi = device->device_data;
+    int idx;
+
+    down(&register_mutex);
+    idx = (rmidi->card->number * SNDRV_RAWMIDI_DEVICES) + rmidi->device;
+    snd_rawmidi_devices[idx] = NULL;
+    up(&register_mutex);
+    return 0;
+}
+
+static int snd_rawmidi_dev_unregister(snd_device_t *device)
+{
+    int idx;
+    snd_rawmidi_t *rmidi = device->device_data;
+
+    snd_assert(rmidi != NULL, return -ENXIO);
+    down(&register_mutex);
+    idx = (rmidi->card->number * SNDRV_RAWMIDI_DEVICES) + rmidi->device;
+    snd_rawmidi_devices[idx] = NULL;
+    if (rmidi->proc_entry) {
+        snd_info_unregister(rmidi->proc_entry);
+        rmidi->proc_entry = NULL;
+    }
+#ifdef CONFIG_SND_OSSEMUL
+    if (rmidi->ossreg) {
+        if ((int)rmidi->device == midi_map[rmidi->card->number]) {
+            snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_MIDI, rmidi->card, 0);
+#ifdef SNDRV_OSS_INFO_DEV_MIDI
+            snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_MIDI, rmidi->card->number);
+#endif
+        }
+        if ((int)rmidi->device == amidi_map[rmidi->card->number])
+            snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_MIDI, rmidi->card, 1);
+        rmidi->ossreg = 0;
+    }
+#endif /* CONFIG_SND_OSSEMUL */
+    if (rmidi->ops && rmidi->ops->dev_unregister)
+        rmidi->ops->dev_unregister(rmidi);
+    snd_unregister_device(SNDRV_DEVICE_TYPE_RAWMIDI, rmidi->card, rmidi->device);
+    up(&register_mutex);
+#ifdef CONFIG_SND_SEQUENCER
+    if (rmidi->seq_dev) {
+        snd_device_free(rmidi->card, rmidi->seq_dev);
+        rmidi->seq_dev = NULL;
+    }
+#endif
+    return snd_rawmidi_free(rmidi);
+}
+
+/**
+ * snd_rawmidi_set_ops - set the rawmidi operators
+ * @rmidi: the rawmidi instance
+ * @stream: the stream direction, SNDRV_RAWMIDI_STREAM_XXX
+ * @ops: the operator table
+ *
+ * Sets the rawmidi operators for the given stream direction.
+ */
+void snd_rawmidi_set_ops(snd_rawmidi_t *rmidi, int stream, snd_rawmidi_ops_t *ops)
+{
+    struct list_head *list;
+    snd_rawmidi_substream_t *substream;
+
+    list_for_each(list, &rmidi->streams[stream].substreams) {
+        substream = list_entry(list, snd_rawmidi_substream_t, list);
+        substream->ops = ops;
+    }
+}
+
+/*
+ *  ENTRY functions
+ */
+
+static int __init alsa_rawmidi_init(void)
+{
+
+    snd_ctl_register_ioctl(snd_rawmidi_control_ioctl);
+#ifdef CONFIG_SND_OSSEMUL
+    { int i;
+        /* check device map table */
+        for (i = 0; i < SNDRV_CARDS; i++) {
+            if (midi_map[i] < 0 || midi_map[i] >= SNDRV_RAWMIDI_DEVICES) {
+                snd_printk(KERN_ERR "invalid midi_map[%d] = %d\n", i, midi_map[i]);
+                midi_map[i] = 0;
+            }
+            if (amidi_map[i] < 0 || amidi_map[i] >= SNDRV_RAWMIDI_DEVICES) {
+                snd_printk(KERN_ERR "invalid amidi_map[%d] = %d\n", i, amidi_map[i]);
+                amidi_map[i] = 1;
+            }
+        }
+    }
+#endif /* CONFIG_SND_OSSEMUL */
+    return 0;
+}
+
+static void __exit alsa_rawmidi_exit(void)
+{
+    snd_ctl_unregister_ioctl(snd_rawmidi_control_ioctl);
+}
+
+module_init(alsa_rawmidi_init)
+module_exit(alsa_rawmidi_exit)
+
+#ifndef MODULE
+#ifdef CONFIG_SND_OSSEMUL
+/* format is: snd-rawmidi=midi_map,amidi_map */
+
+static int __init alsa_rawmidi_setup(char *str)
+{
+    static unsigned __initdata nr_dev = 0;
+
+    if (nr_dev >= SNDRV_CARDS)
+        return 0;
+    (void)(get_option(&str,&midi_map[nr_dev]) == 2 &&
+           get_option(&str,&amidi_map[nr_dev]) == 2);
+    nr_dev++;
+    return 1;
+}
+
+__setup("snd-rawmidi=", alsa_rawmidi_setup);
+#endif /* CONFIG_SND_OSSEMUL */
+#endif /* ifndef MODULE */
+
+EXPORT_SYMBOL(snd_rawmidi_output_params);
+EXPORT_SYMBOL(snd_rawmidi_input_params);
+EXPORT_SYMBOL(snd_rawmidi_drop_output);
+EXPORT_SYMBOL(snd_rawmidi_drain_output);
+EXPORT_SYMBOL(snd_rawmidi_drain_input);
+EXPORT_SYMBOL(snd_rawmidi_receive);
+EXPORT_SYMBOL(snd_rawmidi_transmit_empty);
+EXPORT_SYMBOL(snd_rawmidi_transmit_peek);
+EXPORT_SYMBOL(snd_rawmidi_transmit_ack);
+EXPORT_SYMBOL(snd_rawmidi_transmit);
+EXPORT_SYMBOL(snd_rawmidi_new);
+EXPORT_SYMBOL(snd_rawmidi_set_ops);
+EXPORT_SYMBOL(snd_rawmidi_info);
+EXPORT_SYMBOL(snd_rawmidi_info_select);
+EXPORT_SYMBOL(snd_rawmidi_kernel_open);
+EXPORT_SYMBOL(snd_rawmidi_kernel_release);
+EXPORT_SYMBOL(snd_rawmidi_kernel_read);
+EXPORT_SYMBOL(snd_rawmidi_kernel_write);
Index: /GPL/branches/alsa-resync1/alsa-kernel/core/rtctimer.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/core/rtctimer.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/core/rtctimer.c	(revision 91)
@@ -0,0 +1,215 @@
+/*
+ *  RTC based high-frequency timer
+ *
+ *  Copyright (C) 2000 Takashi Iwai
+ *	based on rtctimer.c by Steve Ratcliffe
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * 
+ *================================================================
+ * For enabling this timer, apply the patch file to your kernel.
+ * The configure script checks the patch automatically.
+ * The patches, rtc-xxx.dif, are found under utils/patches, where
+ * xxx is the kernel version.
+ *================================================================
+ *
+ */
+
+#define SNDRV_MAIN_OBJECT_FILE
+#include <sound/driver.h>
+#include <sound/timer.h>
+#include <sound/info.h>
+
+#if defined(CONFIG_RTC) || defined(CONFIG_RTC_MODULE)
+
+#include <linux/mc146818rtc.h>
+
+/* use tasklet for interrupt handling */
+#define USE_TASKLET
+
+#define RTC_FREQ	1024		/* default frequency */
+#define NANO_SEC	1000000000L	/* 10^9 in sec */
+
+/*
+ * prototypes
+ */
+static int rtctimer_open(snd_timer_t *t);
+static int rtctimer_close(snd_timer_t *t);
+static int rtctimer_start(snd_timer_t *t);
+static int rtctimer_stop(snd_timer_t *t);
+
+
+/*
+ * The harware depenant description for this timer.
+ */
+static struct _snd_timer_hardware rtc_hw = {
+	flags:		SNDRV_TIMER_HW_FIRST|SNDRV_TIMER_HW_AUTO,
+	ticks:		100000000L,		/* FIXME: XXX */
+	open:		rtctimer_open,
+	close:		rtctimer_close,
+	start:		rtctimer_start,
+	stop:		rtctimer_stop,
+};
+
+int rtctimer_freq = RTC_FREQ;		/* frequency */
+static snd_timer_t *rtctimer;
+static volatile int rtc_inc = 0;
+static rtc_task_t rtc_task;
+
+/* tasklet */
+#ifdef USE_TASKLET
+static struct tasklet_struct rtc_tq;
+#endif
+
+static int
+rtctimer_open(snd_timer_t *t)
+{
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static int
+rtctimer_close(snd_timer_t *t)
+{
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+static int
+rtctimer_start(snd_timer_t *timer)
+{
+	rtc_task_t *rtc = timer->private_data;
+	snd_assert(rtc != NULL, return -EINVAL);
+	rtc_control(rtc, RTC_IRQP_SET, rtctimer_freq);
+	rtc_control(rtc, RTC_PIE_ON, 0);
+	rtc_inc = 0;
+	return 0;
+}
+
+static int
+rtctimer_stop(snd_timer_t *timer)
+{
+	rtc_task_t *rtc = timer->private_data;
+	snd_assert(rtc != NULL, return -EINVAL);
+	rtc_control(rtc, RTC_PIE_OFF, 0);
+	return 0;
+}
+
+/*
+ * interrupt
+ */
+static void rtctimer_interrupt(void *private_data)
+{
+	rtc_inc++;
+#ifdef USE_TASKLET
+	tasklet_hi_schedule(&rtc_tq);
+#else
+	snd_timer_interrupt((snd_timer_t*)private_data, rtc_inc);
+	rtc_inc = 0;
+#endif /* USE_TASKLET */
+}
+
+#ifdef USE_TASKLET
+static void rtctimer_interrupt2(void *private_data)
+{
+	snd_timer_t *timer = private_data;
+	snd_assert(timer != NULL, return);
+	do {
+		snd_timer_interrupt(timer, 1);
+	} while (--rtc_inc > 0);
+}
+#endif /* USE_TASKLET */
+
+static void rtctimer_private_free(snd_timer_t *timer)
+{
+	rtc_task_t *rtc = timer->private_data;
+	if (rtc)
+		rtc_unregister(rtc);
+}
+
+
+/*
+ *  ENTRY functions
+ */
+static int __init rtctimer_init(void)
+{
+	int order, err;
+	snd_timer_t *timer;
+
+	if (rtctimer_freq < 2 || rtctimer_freq > 8192) {
+		snd_printk("rtctimer: invalid frequency %d\n", rtctimer_freq);
+		return -EINVAL;
+	}
+	for (order = 1; rtctimer_freq > order; order <<= 1)
+		;
+	if (rtctimer_freq != order) {
+		snd_printk("rtctimer: invalid frequency %d\n", rtctimer_freq);
+		return -EINVAL;
+	}
+
+	/* Create a new timer and set up the fields */
+	err = snd_timer_global_new("rtc", SNDRV_TIMER_GLOBAL_RTC, &timer);
+	if (err < 0)
+		return err;
+
+#ifdef USE_TASKLET
+	tasklet_init(&rtc_tq, rtctimer_interrupt2, timer);
+#endif /* USE_TASKLET */
+
+	strcpy(timer->name, "RTC timer");
+	timer->hw = rtc_hw;
+	timer->hw.resolution = NANO_SEC / rtctimer_freq;
+
+	/* register RTC callback */
+	rtc_task.func = rtctimer_interrupt;
+	rtc_task.private_data = timer;
+	err = rtc_register(&rtc_task);
+	if (err < 0) {
+		snd_timer_global_free(timer);
+		return err;
+	}
+	timer->private_data = &rtc_task;
+	timer->private_free = rtctimer_private_free;
+
+	err = snd_timer_global_register(timer);
+	if (err < 0) {
+		snd_timer_global_free(timer);
+		return err;
+	}
+	rtctimer = timer;
+
+	return 0;
+}
+
+static void __exit rtctimer_exit(void)
+{
+	if (rtctimer) {
+		snd_timer_global_unregister(rtctimer);
+		rtctimer = NULL;
+	}
+}
+
+
+/*
+ * exported stuffs
+ */
+module_init(rtctimer_init)
+module_exit(rtctimer_exit)
+
+MODULE_PARM(rtctimer_freq, "i");
+MODULE_PARM_DESC(rtctimer_freq, "timer frequency in Hz");
+
+#endif /* CONFIG_RTC || CONFIG_RTC_MODULE */
Index: /GPL/branches/alsa-resync1/alsa-kernel/core/seq/instr/ainstr_fm.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/core/seq/instr/ainstr_fm.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/core/seq/instr/ainstr_fm.c	(revision 91)
@@ -0,0 +1,156 @@
+/*
+ *  FM (OPL2/3) Instrument routines
+ *  Copyright (c) 2000 Uros Bizjak <uros@kss-loka.si>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+ 
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <sound/core.h>
+#include <sound/ainstr_fm.h>
+#include <sound/initval.h>
+#include <asm/uaccess.h>
+
+MODULE_AUTHOR("Uros Bizjak <uros@kss-loka.si>");
+MODULE_DESCRIPTION("Advanced Linux Sound Architecture FM Instrument support.");
+MODULE_LICENSE("GPL");
+
+static int snd_seq_fm_put(void *private_data, struct snd_seq_kinstr *instr,
+			  char __user *instr_data, long len, int atomic, int cmd)
+{
+	struct fm_instrument *ip;
+	struct fm_xinstrument ix;
+	int idx;
+
+	if (cmd != SNDRV_SEQ_INSTR_PUT_CMD_CREATE)
+		return -EINVAL;
+	/* copy instrument data */
+	if (len < (long)sizeof(ix))
+		return -EINVAL;
+	if (copy_from_user(&ix, instr_data, sizeof(ix)))
+		return -EFAULT;
+	if (ix.stype != FM_STRU_INSTR)
+		return -EINVAL;
+	ip = (struct fm_instrument *)KINSTR_DATA(instr);
+	ip->share_id[0] = le32_to_cpu(ix.share_id[0]);
+	ip->share_id[1] = le32_to_cpu(ix.share_id[1]);
+	ip->share_id[2] = le32_to_cpu(ix.share_id[2]);
+	ip->share_id[3] = le32_to_cpu(ix.share_id[3]);
+	ip->type = ix.type;
+	for (idx = 0; idx < 4; idx++) {
+		ip->op[idx].am_vib = ix.op[idx].am_vib;
+		ip->op[idx].ksl_level = ix.op[idx].ksl_level;
+		ip->op[idx].attack_decay = ix.op[idx].attack_decay;
+		ip->op[idx].sustain_release = ix.op[idx].sustain_release;
+		ip->op[idx].wave_select = ix.op[idx].wave_select;
+	}
+	for (idx = 0; idx < 2; idx++) {
+		ip->feedback_connection[idx] = ix.feedback_connection[idx];
+	}
+	ip->echo_delay = ix.echo_delay;
+	ip->echo_atten = ix.echo_atten;
+	ip->chorus_spread = ix.chorus_spread;
+	ip->trnsps = ix.trnsps;
+	ip->fix_dur = ix.fix_dur;
+	ip->modes = ix.modes;
+	ip->fix_key = ix.fix_key;
+	return 0;
+}
+
+static int snd_seq_fm_get(void *private_data, struct snd_seq_kinstr *instr,
+			  char __user *instr_data, long len, int atomic,
+			  int cmd)
+{
+	struct fm_instrument *ip;
+	struct fm_xinstrument ix;
+	int idx;
+	
+	if (cmd != SNDRV_SEQ_INSTR_GET_CMD_FULL)
+		return -EINVAL;
+	if (len < (long)sizeof(ix))
+		return -ENOMEM;
+	memset(&ix, 0, sizeof(ix));
+	ip = (struct fm_instrument *)KINSTR_DATA(instr);
+	ix.stype = FM_STRU_INSTR;
+	ix.share_id[0] = cpu_to_le32(ip->share_id[0]);
+	ix.share_id[1] = cpu_to_le32(ip->share_id[1]);
+	ix.share_id[2] = cpu_to_le32(ip->share_id[2]);
+	ix.share_id[3] = cpu_to_le32(ip->share_id[3]);
+	ix.type = ip->type;
+	for (idx = 0; idx < 4; idx++) {
+		ix.op[idx].am_vib = ip->op[idx].am_vib;
+		ix.op[idx].ksl_level = ip->op[idx].ksl_level;
+		ix.op[idx].attack_decay = ip->op[idx].attack_decay;
+		ix.op[idx].sustain_release = ip->op[idx].sustain_release;
+		ix.op[idx].wave_select = ip->op[idx].wave_select;
+	}
+	for (idx = 0; idx < 2; idx++) {
+		ix.feedback_connection[idx] = ip->feedback_connection[idx];
+	}
+	if (copy_to_user(instr_data, &ix, sizeof(ix)))
+		return -EFAULT;
+	ix.echo_delay = ip->echo_delay;
+	ix.echo_atten = ip->echo_atten;
+	ix.chorus_spread = ip->chorus_spread;
+	ix.trnsps = ip->trnsps;
+	ix.fix_dur = ip->fix_dur;
+	ix.modes = ip->modes;
+	ix.fix_key = ip->fix_key;
+	return 0;
+}
+
+static int snd_seq_fm_get_size(void *private_data, struct snd_seq_kinstr *instr,
+			       long *size)
+{
+	*size = sizeof(struct fm_xinstrument);
+	return 0;
+}
+
+int snd_seq_fm_init(struct snd_seq_kinstr_ops *ops,
+		    struct snd_seq_kinstr_ops *next)
+{
+	memset(ops, 0, sizeof(*ops));
+	// ops->private_data = private_data;
+	ops->add_len = sizeof(struct fm_instrument);
+	ops->instr_type = SNDRV_SEQ_INSTR_ID_OPL2_3;
+	ops->put = snd_seq_fm_put;
+	ops->get = snd_seq_fm_get;
+	ops->get_size = snd_seq_fm_get_size;
+	// ops->remove = snd_seq_fm_remove;
+	// ops->notify = snd_seq_fm_notify;
+	ops->next = next;
+	return 0;
+}
+
+/*
+ *  Init part
+ */
+
+static int __init alsa_ainstr_fm_init(void)
+{
+	return 0;
+}
+
+static void __exit alsa_ainstr_fm_exit(void)
+{
+}
+
+module_init(alsa_ainstr_fm_init)
+module_exit(alsa_ainstr_fm_exit)
+
+EXPORT_SYMBOL(snd_seq_fm_init);
Index: /GPL/branches/alsa-resync1/alsa-kernel/core/seq/instr/ainstr_gf1.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/core/seq/instr/ainstr_gf1.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/core/seq/instr/ainstr_gf1.c	(revision 91)
@@ -0,0 +1,360 @@
+/*
+ *   GF1 (GUS) Patch - Instrument routines
+ *   Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+ 
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/ainstr_gf1.h>
+#include <sound/initval.h>
+#include <asm/uaccess.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Advanced Linux Sound Architecture GF1 (GUS) Patch support.");
+MODULE_LICENSE("GPL");
+
+static unsigned int snd_seq_gf1_size(unsigned int size, unsigned int format)
+{
+	unsigned int result = size;
+	
+	if (format & GF1_WAVE_16BIT)
+		result <<= 1;
+	if (format & GF1_WAVE_STEREO)
+		result <<= 1;
+	return format;
+}
+
+static int snd_seq_gf1_copy_wave_from_stream(struct snd_gf1_ops *ops,
+					     struct gf1_instrument *ip,
+					     char __user **data,
+					     long *len,
+					     int atomic)
+{
+	struct gf1_wave *wp, *prev;
+	struct gf1_xwave xp;
+	int err;
+	gfp_t gfp_mask;
+	unsigned int real_size;
+	
+	gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL;
+	if (*len < (long)sizeof(xp))
+		return -EINVAL;
+	if (copy_from_user(&xp, *data, sizeof(xp)))
+		return -EFAULT;
+	*data += sizeof(xp);
+	*len -= sizeof(xp);
+	wp = kzalloc(sizeof(*wp), gfp_mask);
+	if (wp == NULL)
+		return -ENOMEM;
+	wp->share_id[0] = le32_to_cpu(xp.share_id[0]);
+	wp->share_id[1] = le32_to_cpu(xp.share_id[1]);
+	wp->share_id[2] = le32_to_cpu(xp.share_id[2]);
+	wp->share_id[3] = le32_to_cpu(xp.share_id[3]);
+	wp->format = le32_to_cpu(xp.format);
+	wp->size = le32_to_cpu(xp.size);
+	wp->start = le32_to_cpu(xp.start);
+	wp->loop_start = le32_to_cpu(xp.loop_start);
+	wp->loop_end = le32_to_cpu(xp.loop_end);
+	wp->loop_repeat = le16_to_cpu(xp.loop_repeat);
+	wp->flags = xp.flags;
+	wp->sample_rate = le32_to_cpu(xp.sample_rate);
+	wp->low_frequency = le32_to_cpu(xp.low_frequency);
+	wp->high_frequency = le32_to_cpu(xp.high_frequency);
+	wp->root_frequency = le32_to_cpu(xp.root_frequency);
+	wp->tune = le16_to_cpu(xp.tune);
+	wp->balance = xp.balance;
+	memcpy(wp->envelope_rate, xp.envelope_rate, 6);
+	memcpy(wp->envelope_offset, xp.envelope_offset, 6);
+	wp->tremolo_sweep = xp.tremolo_sweep;
+	wp->tremolo_rate = xp.tremolo_rate;
+	wp->tremolo_depth = xp.tremolo_depth;
+	wp->vibrato_sweep = xp.vibrato_sweep;
+	wp->vibrato_rate = xp.vibrato_rate;
+	wp->vibrato_depth = xp.vibrato_depth;
+	wp->scale_frequency = le16_to_cpu(xp.scale_frequency);
+	wp->scale_factor = le16_to_cpu(xp.scale_factor);
+	real_size = snd_seq_gf1_size(wp->size, wp->format);
+	if ((long)real_size > *len) {
+		kfree(wp);
+		return -ENOMEM;
+	}
+	if (ops->put_sample) {
+		err = ops->put_sample(ops->private_data, wp,
+				      *data, real_size, atomic);
+		if (err < 0) {
+			kfree(wp);
+			return err;
+		}
+	}
+	*data += real_size;
+	*len -= real_size;
+	prev = ip->wave;
+	if (prev) {
+		while (prev->next) prev = prev->next;
+		prev->next = wp;
+	} else {
+		ip->wave = wp;
+	}
+	return 0;
+}
+
+static void snd_seq_gf1_wave_free(struct snd_gf1_ops *ops,
+				  struct gf1_wave *wave,
+				  int atomic)
+{
+	if (ops->remove_sample)
+		ops->remove_sample(ops->private_data, wave, atomic);
+	kfree(wave);
+}
+
+static void snd_seq_gf1_instr_free(struct snd_gf1_ops *ops,
+				   struct gf1_instrument *ip,
+				   int atomic)
+{
+	struct gf1_wave *wave;
+	
+	while ((wave = ip->wave) != NULL) {
+		ip->wave = wave->next;
+		snd_seq_gf1_wave_free(ops, wave, atomic);
+	}
+}
+
+static int snd_seq_gf1_put(void *private_data, struct snd_seq_kinstr *instr,
+			   char __user *instr_data, long len, int atomic,
+			   int cmd)
+{
+	struct snd_gf1_ops *ops = private_data;
+	struct gf1_instrument *ip;
+	struct gf1_xinstrument ix;
+	int err;
+	gfp_t gfp_mask;
+
+	if (cmd != SNDRV_SEQ_INSTR_PUT_CMD_CREATE)
+		return -EINVAL;
+	gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL;
+	/* copy instrument data */
+	if (len < (long)sizeof(ix))
+		return -EINVAL;
+	if (copy_from_user(&ix, instr_data, sizeof(ix)))
+		return -EFAULT;
+	if (ix.stype != GF1_STRU_INSTR)
+		return -EINVAL;
+	instr_data += sizeof(ix);
+	len -= sizeof(ix);
+	ip = (struct gf1_instrument *)KINSTR_DATA(instr);
+	ip->exclusion = le16_to_cpu(ix.exclusion);
+	ip->exclusion_group = le16_to_cpu(ix.exclusion_group);
+	ip->effect1 = ix.effect1;
+	ip->effect1_depth = ix.effect1_depth;
+	ip->effect2 = ix.effect2;
+	ip->effect2_depth = ix.effect2_depth;
+	/* copy layers */
+	while (len > (long)sizeof(__u32)) {
+		__u32 stype;
+
+		if (copy_from_user(&stype, instr_data, sizeof(stype)))
+			return -EFAULT;
+		if (stype != GF1_STRU_WAVE) {
+			snd_seq_gf1_instr_free(ops, ip, atomic);
+			return -EINVAL;
+		}
+		err = snd_seq_gf1_copy_wave_from_stream(ops,
+							ip,
+							&instr_data,
+							&len,
+							atomic);
+		if (err < 0) {
+			snd_seq_gf1_instr_free(ops, ip, atomic);
+			return err;
+		}
+	}
+	return 0;
+}
+
+static int snd_seq_gf1_copy_wave_to_stream(struct snd_gf1_ops *ops,
+					   struct gf1_instrument *ip,
+					   char __user **data,
+					   long *len,
+					   int atomic)
+{
+	struct gf1_wave *wp;
+	struct gf1_xwave xp;
+	int err;
+	unsigned int real_size;
+	
+	for (wp = ip->wave; wp; wp = wp->next) {
+		if (*len < (long)sizeof(xp))
+			return -ENOMEM;
+		memset(&xp, 0, sizeof(xp));
+		xp.stype = GF1_STRU_WAVE;
+		xp.share_id[0] = cpu_to_le32(wp->share_id[0]);
+		xp.share_id[1] = cpu_to_le32(wp->share_id[1]);
+		xp.share_id[2] = cpu_to_le32(wp->share_id[2]);
+		xp.share_id[3] = cpu_to_le32(wp->share_id[3]);
+		xp.format = cpu_to_le32(wp->format);
+		xp.size = cpu_to_le32(wp->size);
+		xp.start = cpu_to_le32(wp->start);
+		xp.loop_start = cpu_to_le32(wp->loop_start);
+		xp.loop_end = cpu_to_le32(wp->loop_end);
+		xp.loop_repeat = cpu_to_le32(wp->loop_repeat);
+		xp.flags = wp->flags;
+		xp.sample_rate = cpu_to_le32(wp->sample_rate);
+		xp.low_frequency = cpu_to_le32(wp->low_frequency);
+		xp.high_frequency = cpu_to_le32(wp->high_frequency);
+		xp.root_frequency = cpu_to_le32(wp->root_frequency);
+		xp.tune = cpu_to_le16(wp->tune);
+		xp.balance = wp->balance;
+		memcpy(xp.envelope_rate, wp->envelope_rate, 6);
+		memcpy(xp.envelope_offset, wp->envelope_offset, 6);
+		xp.tremolo_sweep = wp->tremolo_sweep;
+		xp.tremolo_rate = wp->tremolo_rate;
+		xp.tremolo_depth = wp->tremolo_depth;
+		xp.vibrato_sweep = wp->vibrato_sweep;
+		xp.vibrato_rate = wp->vibrato_rate;
+		xp.vibrato_depth = wp->vibrato_depth;
+		xp.scale_frequency = cpu_to_le16(wp->scale_frequency);
+		xp.scale_factor = cpu_to_le16(wp->scale_factor);
+		if (copy_to_user(*data, &xp, sizeof(xp)))
+			return -EFAULT;
+		*data += sizeof(xp);
+		*len -= sizeof(xp);
+		real_size = snd_seq_gf1_size(wp->size, wp->format);
+		if (*len < (long)real_size)
+			return -ENOMEM;
+		if (ops->get_sample) {
+			err = ops->get_sample(ops->private_data, wp,
+					      *data, real_size, atomic);
+			if (err < 0)
+				return err;
+		}
+		*data += wp->size;
+		*len -= wp->size;
+	}
+	return 0;
+}
+
+static int snd_seq_gf1_get(void *private_data, struct snd_seq_kinstr *instr,
+			   char __user *instr_data, long len, int atomic,
+			   int cmd)
+{
+	struct snd_gf1_ops *ops = private_data;
+	struct gf1_instrument *ip;
+	struct gf1_xinstrument ix;
+	
+	if (cmd != SNDRV_SEQ_INSTR_GET_CMD_FULL)
+		return -EINVAL;
+	if (len < (long)sizeof(ix))
+		return -ENOMEM;
+	memset(&ix, 0, sizeof(ix));
+	ip = (struct gf1_instrument *)KINSTR_DATA(instr);
+	ix.stype = GF1_STRU_INSTR;
+	ix.exclusion = cpu_to_le16(ip->exclusion);
+	ix.exclusion_group = cpu_to_le16(ip->exclusion_group);
+	ix.effect1 = cpu_to_le16(ip->effect1);
+	ix.effect1_depth = cpu_to_le16(ip->effect1_depth);
+	ix.effect2 = ip->effect2;
+	ix.effect2_depth = ip->effect2_depth;
+	if (copy_to_user(instr_data, &ix, sizeof(ix)))
+		return -EFAULT;
+	instr_data += sizeof(ix);
+	len -= sizeof(ix);
+	return snd_seq_gf1_copy_wave_to_stream(ops,
+					       ip,
+					       &instr_data,
+					       &len,
+					       atomic);
+}
+
+static int snd_seq_gf1_get_size(void *private_data, struct snd_seq_kinstr *instr,
+				long *size)
+{
+	long result;
+	struct gf1_instrument *ip;
+	struct gf1_wave *wp;
+
+	*size = 0;
+	ip = (struct gf1_instrument *)KINSTR_DATA(instr);
+	result = sizeof(struct gf1_xinstrument);
+	for (wp = ip->wave; wp; wp = wp->next) {
+		result += sizeof(struct gf1_xwave);
+		result += wp->size;
+	}
+	*size = result;
+	return 0;
+}
+
+static int snd_seq_gf1_remove(void *private_data,
+			      struct snd_seq_kinstr *instr,
+                              int atomic)
+{
+	struct snd_gf1_ops *ops = private_data;
+	struct gf1_instrument *ip;
+
+	ip = (struct gf1_instrument *)KINSTR_DATA(instr);
+	snd_seq_gf1_instr_free(ops, ip, atomic);
+	return 0;
+}
+
+static void snd_seq_gf1_notify(void *private_data,
+			       struct snd_seq_kinstr *instr,
+			       int what)
+{
+	struct snd_gf1_ops *ops = private_data;
+
+	if (ops->notify)
+		ops->notify(ops->private_data, instr, what);
+}
+
+int snd_seq_gf1_init(struct snd_gf1_ops *ops,
+		     void *private_data,
+		     struct snd_seq_kinstr_ops *next)
+{
+	memset(ops, 0, sizeof(*ops));
+	ops->private_data = private_data;
+	ops->kops.private_data = ops;
+	ops->kops.add_len = sizeof(struct gf1_instrument);
+	ops->kops.instr_type = SNDRV_SEQ_INSTR_ID_GUS_PATCH;
+	ops->kops.put = snd_seq_gf1_put;
+	ops->kops.get = snd_seq_gf1_get;
+	ops->kops.get_size = snd_seq_gf1_get_size;
+	ops->kops.remove = snd_seq_gf1_remove;
+	ops->kops.notify = snd_seq_gf1_notify;
+	ops->kops.next = next;
+	return 0;
+}
+
+/*
+ *  Init part
+ */
+
+static int __init alsa_ainstr_gf1_init(void)
+{
+	return 0;
+}
+
+static void __exit alsa_ainstr_gf1_exit(void)
+{
+}
+
+module_init(alsa_ainstr_gf1_init)
+module_exit(alsa_ainstr_gf1_exit)
+
+EXPORT_SYMBOL(snd_seq_gf1_init);
Index: /GPL/branches/alsa-resync1/alsa-kernel/core/seq/instr/ainstr_iw.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/core/seq/instr/ainstr_iw.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/core/seq/instr/ainstr_iw.c	(revision 91)
@@ -0,0 +1,624 @@
+/*
+ *   IWFFFF - AMD InterWave (tm) - Instrument routines
+ *   Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+ 
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/ainstr_iw.h>
+#include <sound/initval.h>
+#include <asm/uaccess.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Advanced Linux Sound Architecture IWFFFF support.");
+MODULE_LICENSE("GPL");
+
+static unsigned int snd_seq_iwffff_size(unsigned int size, unsigned int format)
+{
+	unsigned int result = size;
+	
+	if (format & IWFFFF_WAVE_16BIT)
+		result <<= 1;
+	if (format & IWFFFF_WAVE_STEREO)
+		result <<= 1;
+	return result;
+}
+
+static void snd_seq_iwffff_copy_lfo_from_stream(struct iwffff_lfo *fp,
+						struct iwffff_xlfo *fx)
+{
+	fp->freq = le16_to_cpu(fx->freq);
+	fp->depth = le16_to_cpu(fx->depth);
+	fp->sweep = le16_to_cpu(fx->sweep);
+	fp->shape = fx->shape;
+	fp->delay = fx->delay;
+}
+
+static int snd_seq_iwffff_copy_env_from_stream(__u32 req_stype,
+					       struct iwffff_layer *lp,
+					       struct iwffff_env *ep,
+					       struct iwffff_xenv *ex,
+					       char __user **data,
+					       long *len,
+					       gfp_t gfp_mask)
+{
+	__u32 stype;
+	struct iwffff_env_record *rp, *rp_last;
+	struct iwffff_xenv_record rx;
+	struct iwffff_env_point *pp;
+	struct iwffff_xenv_point px;
+	int points_size, idx;
+
+	ep->flags = ex->flags;
+	ep->mode = ex->mode;
+	ep->index = ex->index;
+	rp_last = NULL;
+	while (1) {
+		if (*len < (long)sizeof(__u32))
+			return -EINVAL;
+		if (copy_from_user(&stype, *data, sizeof(stype)))
+			return -EFAULT;
+		if (stype == IWFFFF_STRU_WAVE)
+			return 0;
+		if (req_stype != stype) {
+			if (stype == IWFFFF_STRU_ENV_RECP ||
+			    stype == IWFFFF_STRU_ENV_RECV)
+				return 0;
+		}
+		if (*len < (long)sizeof(rx))
+			return -EINVAL;
+		if (copy_from_user(&rx, *data, sizeof(rx)))
+			return -EFAULT;
+		*data += sizeof(rx);
+		*len -= sizeof(rx);
+		points_size = (le16_to_cpu(rx.nattack) + le16_to_cpu(rx.nrelease)) * 2 * sizeof(__u16);
+		if (points_size > *len)
+			return -EINVAL;
+		rp = kzalloc(sizeof(*rp) + points_size, gfp_mask);
+		if (rp == NULL)
+			return -ENOMEM;
+		rp->nattack = le16_to_cpu(rx.nattack);
+		rp->nrelease = le16_to_cpu(rx.nrelease);
+		rp->sustain_offset = le16_to_cpu(rx.sustain_offset);
+		rp->sustain_rate = le16_to_cpu(rx.sustain_rate);
+		rp->release_rate = le16_to_cpu(rx.release_rate);
+		rp->hirange = rx.hirange;
+		pp = (struct iwffff_env_point *)(rp + 1);
+		for (idx = 0; idx < rp->nattack + rp->nrelease; idx++) {
+			if (copy_from_user(&px, *data, sizeof(px)))
+				return -EFAULT;
+			*data += sizeof(px);
+			*len -= sizeof(px);
+			pp->offset = le16_to_cpu(px.offset);
+			pp->rate = le16_to_cpu(px.rate);
+		}
+		if (ep->record == NULL) {
+			ep->record = rp;
+		} else {
+			rp_last = rp;
+		}
+		rp_last = rp;
+	}
+	return 0;
+}
+
+static int snd_seq_iwffff_copy_wave_from_stream(struct snd_iwffff_ops *ops,
+						struct iwffff_layer *lp,
+					        char __user **data,
+					        long *len,
+					        int atomic)
+{
+	struct iwffff_wave *wp, *prev;
+	struct iwffff_xwave xp;
+	int err;
+	gfp_t gfp_mask;
+	unsigned int real_size;
+	
+	gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL;
+	if (*len < (long)sizeof(xp))
+		return -EINVAL;
+	if (copy_from_user(&xp, *data, sizeof(xp)))
+		return -EFAULT;
+	*data += sizeof(xp);
+	*len -= sizeof(xp);
+	wp = kzalloc(sizeof(*wp), gfp_mask);
+	if (wp == NULL)
+		return -ENOMEM;
+	wp->share_id[0] = le32_to_cpu(xp.share_id[0]);
+	wp->share_id[1] = le32_to_cpu(xp.share_id[1]);
+	wp->share_id[2] = le32_to_cpu(xp.share_id[2]);
+	wp->share_id[3] = le32_to_cpu(xp.share_id[3]);
+	wp->format = le32_to_cpu(xp.format);
+	wp->address.memory = le32_to_cpu(xp.offset);
+	wp->size = le32_to_cpu(xp.size);
+	wp->start = le32_to_cpu(xp.start);
+	wp->loop_start = le32_to_cpu(xp.loop_start);
+	wp->loop_end = le32_to_cpu(xp.loop_end);
+	wp->loop_repeat = le16_to_cpu(xp.loop_repeat);
+	wp->sample_ratio = le32_to_cpu(xp.sample_ratio);
+	wp->attenuation = xp.attenuation;
+	wp->low_note = xp.low_note;
+	wp->high_note = xp.high_note;
+	real_size = snd_seq_iwffff_size(wp->size, wp->format);
+	if (!(wp->format & IWFFFF_WAVE_ROM)) {
+		if ((long)real_size > *len) {
+			kfree(wp);
+			return -ENOMEM;
+		}
+	}
+	if (ops->put_sample) {
+		err = ops->put_sample(ops->private_data, wp,
+				      *data, real_size, atomic);
+		if (err < 0) {
+			kfree(wp);
+			return err;
+		}
+	}
+	if (!(wp->format & IWFFFF_WAVE_ROM)) {
+		*data += real_size;
+		*len -= real_size;
+	}
+	prev = lp->wave;
+	if (prev) {
+		while (prev->next) prev = prev->next;
+		prev->next = wp;
+	} else {
+		lp->wave = wp;
+	}
+	return 0;
+}
+
+static void snd_seq_iwffff_env_free(struct snd_iwffff_ops *ops,
+				    struct iwffff_env *env,
+				    int atomic)
+{
+	struct iwffff_env_record *rec;
+	
+	while ((rec = env->record) != NULL) {
+		env->record = rec->next;
+		kfree(rec);
+	}
+}
+				    
+static void snd_seq_iwffff_wave_free(struct snd_iwffff_ops *ops,
+				     struct iwffff_wave *wave,
+				     int atomic)
+{
+	if (ops->remove_sample)
+		ops->remove_sample(ops->private_data, wave, atomic);
+	kfree(wave);
+}
+
+static void snd_seq_iwffff_instr_free(struct snd_iwffff_ops *ops,
+                                      struct iwffff_instrument *ip,
+                                      int atomic)
+{
+	struct iwffff_layer *layer;
+	struct iwffff_wave *wave;
+	
+	while ((layer = ip->layer) != NULL) {
+		ip->layer = layer->next;
+		snd_seq_iwffff_env_free(ops, &layer->penv, atomic);
+		snd_seq_iwffff_env_free(ops, &layer->venv, atomic);
+		while ((wave = layer->wave) != NULL) {
+			layer->wave = wave->next;
+			snd_seq_iwffff_wave_free(ops, wave, atomic);
+		}
+		kfree(layer);
+	}
+}
+
+static int snd_seq_iwffff_put(void *private_data, struct snd_seq_kinstr *instr,
+			      char __user *instr_data, long len, int atomic,
+			      int cmd)
+{
+	struct snd_iwffff_ops *ops = private_data;
+	struct iwffff_instrument *ip;
+	struct iwffff_xinstrument ix;
+	struct iwffff_layer *lp, *prev_lp;
+	struct iwffff_xlayer lx;
+	int err;
+	gfp_t gfp_mask;
+
+	if (cmd != SNDRV_SEQ_INSTR_PUT_CMD_CREATE)
+		return -EINVAL;
+	gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL;
+	/* copy instrument data */
+	if (len < (long)sizeof(ix))
+		return -EINVAL;
+	if (copy_from_user(&ix, instr_data, sizeof(ix)))
+		return -EFAULT;
+	if (ix.stype != IWFFFF_STRU_INSTR)
+		return -EINVAL;
+	instr_data += sizeof(ix);
+	len -= sizeof(ix);
+	ip = (struct iwffff_instrument *)KINSTR_DATA(instr);
+	ip->exclusion = le16_to_cpu(ix.exclusion);
+	ip->layer_type = le16_to_cpu(ix.layer_type);
+	ip->exclusion_group = le16_to_cpu(ix.exclusion_group);
+	ip->effect1 = ix.effect1;
+	ip->effect1_depth = ix.effect1_depth;
+	ip->effect2 = ix.effect2;
+	ip->effect2_depth = ix.effect2_depth;
+	/* copy layers */
+	prev_lp = NULL;
+	while (len > 0) {
+		if (len < (long)sizeof(struct iwffff_xlayer)) {
+			snd_seq_iwffff_instr_free(ops, ip, atomic);
+			return -EINVAL;
+		}
+		if (copy_from_user(&lx, instr_data, sizeof(lx)))
+			return -EFAULT;
+		instr_data += sizeof(lx);
+		len -= sizeof(lx);
+		if (lx.stype != IWFFFF_STRU_LAYER) {
+			snd_seq_iwffff_instr_free(ops, ip, atomic);
+			return -EINVAL;
+		}
+		lp = kzalloc(sizeof(*lp), gfp_mask);
+		if (lp == NULL) {
+			snd_seq_iwffff_instr_free(ops, ip, atomic);
+			return -ENOMEM;
+		}
+		if (prev_lp) {
+			prev_lp->next = lp;
+		} else {
+			ip->layer = lp;
+		}
+		prev_lp = lp;
+		lp->flags = lx.flags;
+		lp->velocity_mode = lx.velocity_mode;
+		lp->layer_event = lx.layer_event;
+		lp->low_range = lx.low_range;
+		lp->high_range = lx.high_range;
+		lp->pan = lx.pan;
+		lp->pan_freq_scale = lx.pan_freq_scale;
+		lp->attenuation = lx.attenuation;
+		snd_seq_iwffff_copy_lfo_from_stream(&lp->tremolo, &lx.tremolo);
+		snd_seq_iwffff_copy_lfo_from_stream(&lp->vibrato, &lx.vibrato);
+		lp->freq_scale = le16_to_cpu(lx.freq_scale);
+		lp->freq_center = lx.freq_center;
+		err = snd_seq_iwffff_copy_env_from_stream(IWFFFF_STRU_ENV_RECP,
+							  lp,
+							  &lp->penv, &lx.penv,
+						          &instr_data, &len,
+						          gfp_mask);
+		if (err < 0) {
+			snd_seq_iwffff_instr_free(ops, ip, atomic);
+			return err;
+		}
+		err = snd_seq_iwffff_copy_env_from_stream(IWFFFF_STRU_ENV_RECV,
+							  lp,
+							  &lp->venv, &lx.venv,
+						          &instr_data, &len,
+						          gfp_mask);
+		if (err < 0) {
+			snd_seq_iwffff_instr_free(ops, ip, atomic);
+			return err;
+		}
+		while (len > (long)sizeof(__u32)) {
+			__u32 stype;
+
+			if (copy_from_user(&stype, instr_data, sizeof(stype)))
+				return -EFAULT;
+			if (stype != IWFFFF_STRU_WAVE)
+				break;
+			err = snd_seq_iwffff_copy_wave_from_stream(ops,
+								   lp,
+							    	   &instr_data,
+								   &len,
+								   atomic);
+			if (err < 0) {
+				snd_seq_iwffff_instr_free(ops, ip, atomic);
+				return err;
+			}
+		}
+	}
+	return 0;
+}
+
+static void snd_seq_iwffff_copy_lfo_to_stream(struct iwffff_xlfo *fx,
+					      struct iwffff_lfo *fp)
+{
+	fx->freq = cpu_to_le16(fp->freq);
+	fx->depth = cpu_to_le16(fp->depth);
+	fx->sweep = cpu_to_le16(fp->sweep);
+	fp->shape = fx->shape;
+	fp->delay = fx->delay;
+}
+
+static int snd_seq_iwffff_copy_env_to_stream(__u32 req_stype,
+					     struct iwffff_layer *lp,
+					     struct iwffff_xenv *ex,
+					     struct iwffff_env *ep,
+					     char __user **data,
+					     long *len)
+{
+	struct iwffff_env_record *rp;
+	struct iwffff_xenv_record rx;
+	struct iwffff_env_point *pp;
+	struct iwffff_xenv_point px;
+	int points_size, idx;
+
+	ex->flags = ep->flags;
+	ex->mode = ep->mode;
+	ex->index = ep->index;
+	for (rp = ep->record; rp; rp = rp->next) {
+		if (*len < (long)sizeof(rx))
+			return -ENOMEM;
+		memset(&rx, 0, sizeof(rx));
+		rx.stype = req_stype;
+		rx.nattack = cpu_to_le16(rp->nattack);
+		rx.nrelease = cpu_to_le16(rp->nrelease);
+		rx.sustain_offset = cpu_to_le16(rp->sustain_offset);
+		rx.sustain_rate = cpu_to_le16(rp->sustain_rate);
+		rx.release_rate = cpu_to_le16(rp->release_rate);
+		rx.hirange = cpu_to_le16(rp->hirange);
+		if (copy_to_user(*data, &rx, sizeof(rx)))
+			return -EFAULT;
+		*data += sizeof(rx);
+		*len -= sizeof(rx);
+		points_size = (rp->nattack + rp->nrelease) * 2 * sizeof(__u16);
+		if (*len < points_size)
+			return -ENOMEM;
+		pp = (struct iwffff_env_point *)(rp + 1);
+		for (idx = 0; idx < rp->nattack + rp->nrelease; idx++) {
+			px.offset = cpu_to_le16(pp->offset);
+			px.rate = cpu_to_le16(pp->rate);
+			if (copy_to_user(*data, &px, sizeof(px)))
+				return -EFAULT;
+			*data += sizeof(px);
+			*len -= sizeof(px);
+		}
+	}
+	return 0;
+}
+
+static int snd_seq_iwffff_copy_wave_to_stream(struct snd_iwffff_ops *ops,
+					      struct iwffff_layer *lp,
+					      char __user **data,
+					      long *len,
+					      int atomic)
+{
+	struct iwffff_wave *wp;
+	struct iwffff_xwave xp;
+	int err;
+	unsigned int real_size;
+	
+	for (wp = lp->wave; wp; wp = wp->next) {
+		if (*len < (long)sizeof(xp))
+			return -ENOMEM;
+		memset(&xp, 0, sizeof(xp));
+		xp.stype = IWFFFF_STRU_WAVE;
+		xp.share_id[0] = cpu_to_le32(wp->share_id[0]);
+		xp.share_id[1] = cpu_to_le32(wp->share_id[1]);
+		xp.share_id[2] = cpu_to_le32(wp->share_id[2]);
+		xp.share_id[3] = cpu_to_le32(wp->share_id[3]);
+		xp.format = cpu_to_le32(wp->format);
+		if (wp->format & IWFFFF_WAVE_ROM)
+			xp.offset = cpu_to_le32(wp->address.memory);
+		xp.size = cpu_to_le32(wp->size);
+		xp.start = cpu_to_le32(wp->start);
+		xp.loop_start = cpu_to_le32(wp->loop_start);
+		xp.loop_end = cpu_to_le32(wp->loop_end);
+		xp.loop_repeat = cpu_to_le32(wp->loop_repeat);
+		xp.sample_ratio = cpu_to_le32(wp->sample_ratio);
+		xp.attenuation = wp->attenuation;
+		xp.low_note = wp->low_note;
+		xp.high_note = wp->high_note;
+		if (copy_to_user(*data, &xp, sizeof(xp)))
+			return -EFAULT;
+		*data += sizeof(xp);
+		*len -= sizeof(xp);
+		real_size = snd_seq_iwffff_size(wp->size, wp->format);
+		if (!(wp->format & IWFFFF_WAVE_ROM)) {
+			if (*len < (long)real_size)
+				return -ENOMEM;
+		}
+		if (ops->get_sample) {
+			err = ops->get_sample(ops->private_data, wp,
+					      *data, real_size, atomic);
+			if (err < 0)
+				return err;
+		}
+		if (!(wp->format & IWFFFF_WAVE_ROM)) {
+			*data += real_size;
+			*len -= real_size;
+		}
+	}
+	return 0;
+}
+
+static int snd_seq_iwffff_get(void *private_data, struct snd_seq_kinstr *instr,
+			      char __user *instr_data, long len, int atomic, int cmd)
+{
+	struct snd_iwffff_ops *ops = private_data;
+	struct iwffff_instrument *ip;
+	struct iwffff_xinstrument ix;
+	struct iwffff_layer *lp;
+	struct iwffff_xlayer lx;
+	char __user *layer_instr_data;
+	int err;
+	
+	if (cmd != SNDRV_SEQ_INSTR_GET_CMD_FULL)
+		return -EINVAL;
+	if (len < (long)sizeof(ix))
+		return -ENOMEM;
+	memset(&ix, 0, sizeof(ix));
+	ip = (struct iwffff_instrument *)KINSTR_DATA(instr);
+	ix.stype = IWFFFF_STRU_INSTR;
+	ix.exclusion = cpu_to_le16(ip->exclusion);
+	ix.layer_type = cpu_to_le16(ip->layer_type);
+	ix.exclusion_group = cpu_to_le16(ip->exclusion_group);
+	ix.effect1 = cpu_to_le16(ip->effect1);
+	ix.effect1_depth = cpu_to_le16(ip->effect1_depth);
+	ix.effect2 = ip->effect2;
+	ix.effect2_depth = ip->effect2_depth;
+	if (copy_to_user(instr_data, &ix, sizeof(ix)))
+		return -EFAULT;
+	instr_data += sizeof(ix);
+	len -= sizeof(ix);
+	for (lp = ip->layer; lp; lp = lp->next) {
+		if (len < (long)sizeof(lx))
+			return -ENOMEM;
+		memset(&lx, 0, sizeof(lx));
+		lx.stype = IWFFFF_STRU_LAYER;
+		lx.flags = lp->flags;
+		lx.velocity_mode = lp->velocity_mode;
+		lx.layer_event = lp->layer_event;
+		lx.low_range = lp->low_range;
+		lx.high_range = lp->high_range;
+		lx.pan = lp->pan;
+		lx.pan_freq_scale = lp->pan_freq_scale;
+		lx.attenuation = lp->attenuation;
+		snd_seq_iwffff_copy_lfo_to_stream(&lx.tremolo, &lp->tremolo);
+		snd_seq_iwffff_copy_lfo_to_stream(&lx.vibrato, &lp->vibrato);
+		layer_instr_data = instr_data;
+		instr_data += sizeof(lx);
+		len -= sizeof(lx);
+		err = snd_seq_iwffff_copy_env_to_stream(IWFFFF_STRU_ENV_RECP,
+							lp,
+							&lx.penv, &lp->penv,
+						        &instr_data, &len);
+		if (err < 0)
+			return err;
+		err = snd_seq_iwffff_copy_env_to_stream(IWFFFF_STRU_ENV_RECV,
+							lp,
+							&lx.venv, &lp->venv,
+						        &instr_data, &len);
+		if (err < 0)
+			return err;
+		/* layer structure updating is now finished */
+		if (copy_to_user(layer_instr_data, &lx, sizeof(lx)))
+			return -EFAULT;
+		err = snd_seq_iwffff_copy_wave_to_stream(ops,
+							 lp,
+							 &instr_data,
+							 &len,
+							 atomic);
+		if (err < 0)
+			return err;
+	}
+	return 0;
+}
+
+static long snd_seq_iwffff_env_size_in_stream(struct iwffff_env *ep)
+{
+	long result = 0;
+	struct iwffff_env_record *rp;
+
+	for (rp = ep->record; rp; rp = rp->next) {
+		result += sizeof(struct iwffff_xenv_record);
+		result += (rp->nattack + rp->nrelease) * 2 * sizeof(__u16);
+	}
+	return 0;
+}
+
+static long snd_seq_iwffff_wave_size_in_stream(struct iwffff_layer *lp)
+{
+	long result = 0;
+	struct iwffff_wave *wp;
+	
+	for (wp = lp->wave; wp; wp = wp->next) {
+		result += sizeof(struct iwffff_xwave);
+		if (!(wp->format & IWFFFF_WAVE_ROM))
+			result += wp->size;
+	}
+	return result;
+}
+
+static int snd_seq_iwffff_get_size(void *private_data, struct snd_seq_kinstr *instr,
+				   long *size)
+{
+	long result;
+	struct iwffff_instrument *ip;
+	struct iwffff_layer *lp;
+
+	*size = 0;
+	ip = (struct iwffff_instrument *)KINSTR_DATA(instr);
+	result = sizeof(struct iwffff_xinstrument);
+	for (lp = ip->layer; lp; lp = lp->next) {
+		result += sizeof(struct iwffff_xlayer);
+		result += snd_seq_iwffff_env_size_in_stream(&lp->penv);
+		result += snd_seq_iwffff_env_size_in_stream(&lp->venv);
+		result += snd_seq_iwffff_wave_size_in_stream(lp);
+	}
+	*size = result;
+	return 0;
+}
+
+static int snd_seq_iwffff_remove(void *private_data,
+				 struct snd_seq_kinstr *instr,
+                                 int atomic)
+{
+	struct snd_iwffff_ops *ops = private_data;
+	struct iwffff_instrument *ip;
+
+	ip = (struct iwffff_instrument *)KINSTR_DATA(instr);
+	snd_seq_iwffff_instr_free(ops, ip, atomic);
+	return 0;
+}
+
+static void snd_seq_iwffff_notify(void *private_data,
+				  struct snd_seq_kinstr *instr,
+                                  int what)
+{
+	struct snd_iwffff_ops *ops = private_data;
+
+	if (ops->notify)
+		ops->notify(ops->private_data, instr, what);
+}
+
+int snd_seq_iwffff_init(struct snd_iwffff_ops *ops,
+			void *private_data,
+			struct snd_seq_kinstr_ops *next)
+{
+	memset(ops, 0, sizeof(*ops));
+	ops->private_data = private_data;
+	ops->kops.private_data = ops;
+	ops->kops.add_len = sizeof(struct iwffff_instrument);
+	ops->kops.instr_type = SNDRV_SEQ_INSTR_ID_INTERWAVE;
+	ops->kops.put = snd_seq_iwffff_put;
+	ops->kops.get = snd_seq_iwffff_get;
+	ops->kops.get_size = snd_seq_iwffff_get_size;
+	ops->kops.remove = snd_seq_iwffff_remove;
+	ops->kops.notify = snd_seq_iwffff_notify;
+	ops->kops.next = next;
+	return 0;
+}
+
+/*
+ *  Init part
+ */
+
+static int __init alsa_ainstr_iw_init(void)
+{
+	return 0;
+}
+
+static void __exit alsa_ainstr_iw_exit(void)
+{
+}
+
+module_init(alsa_ainstr_iw_init)
+module_exit(alsa_ainstr_iw_exit)
+
+EXPORT_SYMBOL(snd_seq_iwffff_init);
Index: /GPL/branches/alsa-resync1/alsa-kernel/core/seq/instr/ainstr_simple.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/core/seq/instr/ainstr_simple.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/core/seq/instr/ainstr_simple.c	(revision 91)
@@ -0,0 +1,216 @@
+/*
+ *   Simple (MOD player) - Instrument routines
+ *   Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+ 
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/ainstr_simple.h>
+#include <sound/initval.h>
+#include <asm/uaccess.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Advanced Linux Sound Architecture Simple Instrument support.");
+MODULE_LICENSE("GPL");
+
+static unsigned int snd_seq_simple_size(unsigned int size, unsigned int format)
+{
+	unsigned int result = size;
+	
+	if (format & SIMPLE_WAVE_16BIT)
+		result <<= 1;
+	if (format & SIMPLE_WAVE_STEREO)
+		result <<= 1;
+	return result;
+}
+
+static void snd_seq_simple_instr_free(struct snd_simple_ops *ops,
+				      struct simple_instrument *ip,
+				      int atomic)
+{
+	if (ops->remove_sample)
+		ops->remove_sample(ops->private_data, ip, atomic);
+}
+
+static int snd_seq_simple_put(void *private_data, struct snd_seq_kinstr *instr,
+			      char __user *instr_data, long len,
+			      int atomic, int cmd)
+{
+	struct snd_simple_ops *ops = private_data;
+	struct simple_instrument *ip;
+	struct simple_xinstrument ix;
+	int err;
+	gfp_t gfp_mask;
+	unsigned int real_size;
+
+	if (cmd != SNDRV_SEQ_INSTR_PUT_CMD_CREATE)
+		return -EINVAL;
+	gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL;
+	/* copy instrument data */
+	if (len < (long)sizeof(ix))
+		return -EINVAL;
+	if (copy_from_user(&ix, instr_data, sizeof(ix)))
+		return -EFAULT;
+	if (ix.stype != SIMPLE_STRU_INSTR)
+		return -EINVAL;
+	instr_data += sizeof(ix);
+	len -= sizeof(ix);
+	ip = (struct simple_instrument *)KINSTR_DATA(instr);
+	ip->share_id[0] = le32_to_cpu(ix.share_id[0]);
+	ip->share_id[1] = le32_to_cpu(ix.share_id[1]);
+	ip->share_id[2] = le32_to_cpu(ix.share_id[2]);
+	ip->share_id[3] = le32_to_cpu(ix.share_id[3]);
+	ip->format = le32_to_cpu(ix.format);
+	ip->size = le32_to_cpu(ix.size);
+	ip->start = le32_to_cpu(ix.start);
+	ip->loop_start = le32_to_cpu(ix.loop_start);
+	ip->loop_end = le32_to_cpu(ix.loop_end);
+	ip->loop_repeat = le16_to_cpu(ix.loop_repeat);
+	ip->effect1 = ix.effect1;
+	ip->effect1_depth = ix.effect1_depth;
+	ip->effect2 = ix.effect2;
+	ip->effect2_depth = ix.effect2_depth;
+	real_size = snd_seq_simple_size(ip->size, ip->format);
+	if (len < (long)real_size)
+		return -EINVAL;
+	if (ops->put_sample) {
+		err = ops->put_sample(ops->private_data, ip,
+				      instr_data, real_size, atomic);
+		if (err < 0)
+			return err;
+	}
+	return 0;
+}
+
+static int snd_seq_simple_get(void *private_data, struct snd_seq_kinstr *instr,
+			      char __user *instr_data, long len,
+			      int atomic, int cmd)
+{
+	struct snd_simple_ops *ops = private_data;
+	struct simple_instrument *ip;
+	struct simple_xinstrument ix;
+	int err;
+	unsigned int real_size;
+	
+	if (cmd != SNDRV_SEQ_INSTR_GET_CMD_FULL)
+		return -EINVAL;
+	if (len < (long)sizeof(ix))
+		return -ENOMEM;
+	memset(&ix, 0, sizeof(ix));
+	ip = (struct simple_instrument *)KINSTR_DATA(instr);
+	ix.stype = SIMPLE_STRU_INSTR;
+	ix.share_id[0] = cpu_to_le32(ip->share_id[0]);
+	ix.share_id[1] = cpu_to_le32(ip->share_id[1]);
+	ix.share_id[2] = cpu_to_le32(ip->share_id[2]);
+	ix.share_id[3] = cpu_to_le32(ip->share_id[3]);
+	ix.format = cpu_to_le32(ip->format);
+	ix.size = cpu_to_le32(ip->size);
+	ix.start = cpu_to_le32(ip->start);
+	ix.loop_start = cpu_to_le32(ip->loop_start);
+	ix.loop_end = cpu_to_le32(ip->loop_end);
+	ix.loop_repeat = cpu_to_le32(ip->loop_repeat);
+	ix.effect1 = cpu_to_le16(ip->effect1);
+	ix.effect1_depth = cpu_to_le16(ip->effect1_depth);
+	ix.effect2 = ip->effect2;
+	ix.effect2_depth = ip->effect2_depth;
+	if (copy_to_user(instr_data, &ix, sizeof(ix)))
+		return -EFAULT;
+	instr_data += sizeof(ix);
+	len -= sizeof(ix);
+	real_size = snd_seq_simple_size(ip->size, ip->format);
+	if (len < (long)real_size)
+		return -ENOMEM;
+	if (ops->get_sample) {
+		err = ops->get_sample(ops->private_data, ip,
+				      instr_data, real_size, atomic);
+		if (err < 0)
+			return err;
+	}
+	return 0;
+}
+
+static int snd_seq_simple_get_size(void *private_data, struct snd_seq_kinstr *instr,
+				   long *size)
+{
+	struct simple_instrument *ip;
+
+	ip = (struct simple_instrument *)KINSTR_DATA(instr);
+	*size = sizeof(struct simple_xinstrument) + snd_seq_simple_size(ip->size, ip->format);
+	return 0;
+}
+
+static int snd_seq_simple_remove(void *private_data,
+			         struct snd_seq_kinstr *instr,
+                                 int atomic)
+{
+	struct snd_simple_ops *ops = private_data;
+	struct simple_instrument *ip;
+
+	ip = (struct simple_instrument *)KINSTR_DATA(instr);
+	snd_seq_simple_instr_free(ops, ip, atomic);
+	return 0;
+}
+
+static void snd_seq_simple_notify(void *private_data,
+			          struct snd_seq_kinstr *instr,
+                                  int what)
+{
+	struct snd_simple_ops *ops = private_data;
+
+	if (ops->notify)
+		ops->notify(ops->private_data, instr, what);
+}
+
+int snd_seq_simple_init(struct snd_simple_ops *ops,
+		        void *private_data,
+		        struct snd_seq_kinstr_ops *next)
+{
+	memset(ops, 0, sizeof(*ops));
+	ops->private_data = private_data;
+	ops->kops.private_data = ops;
+	ops->kops.add_len = sizeof(struct simple_instrument);
+	ops->kops.instr_type = SNDRV_SEQ_INSTR_ID_SIMPLE;
+	ops->kops.put = snd_seq_simple_put;
+	ops->kops.get = snd_seq_simple_get;
+	ops->kops.get_size = snd_seq_simple_get_size;
+	ops->kops.remove = snd_seq_simple_remove;
+	ops->kops.notify = snd_seq_simple_notify;
+	ops->kops.next = next;
+	return 0;
+}
+
+/*
+ *  Init part
+ */
+
+static int __init alsa_ainstr_simple_init(void)
+{
+	return 0;
+}
+
+static void __exit alsa_ainstr_simple_exit(void)
+{
+}
+
+module_init(alsa_ainstr_simple_init)
+module_exit(alsa_ainstr_simple_exit)
+
+EXPORT_SYMBOL(snd_seq_simple_init);
Index: /GPL/branches/alsa-resync1/alsa-kernel/core/seq/instr/makefile.os2
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/core/seq/instr/makefile.os2	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/core/seq/instr/makefile.os2	(revision 91)
@@ -0,0 +1,48 @@
+CINCLUDES = ..\..\..\..\include;..\..\..\include;$(%WATCOM)\H
+AINCLUDES = $(DDK_BASE)\H
+
+CDEFINES = -D__KERNEL__ -DMODULE -dTARGET_OS2 -dALSA_BUILD -D__ISAPNP__ -DCONFIG_ISAPNP -D__i386_
+
+32BIT=1
+!include ..\..\..\..\..\makefile.inc
+
+
+#===================================================================
+#
+#   List of source files
+#
+#===================================================================
+FILE1    = ainstr_fm.obj
+FILE2    = 
+FILE3    = 
+FILE4    = 
+FILE5    = 
+FILELAST =
+FILES    = $(FILE0) $(FILE1) $(FILE2) $(FILE3) $(FILE4) $(FILE5) $(FILE6) $(FILE7) $(FILE8) $(FILE9) $(FILE10) $(FILE11) $(FILE12)
+
+TARGET   = instr
+
+LNKFILE  = $(OBJDIR)\$(TARGET).lnk
+
+
+#===================================================================
+#
+#   Specific dependencies
+#
+#===================================================================
+all: $(OBJDIR)\$(TARGET).lib
+
+
+$(LNKFILE): makefile.os2
+    @%create $(LNKFILE)
+    @for %f in ($(FILES)) do @%append $(LNKFILE) -+$(OBJDIR)\%f
+
+$(OBJDIR)\$(TARGET).lib: $(LNKFILE) $(FILES)
+    $(LIB) $(OBJDIR)\$(TARGET).lib @$(LNKFILE)
+    copy $(OBJDIR)\$(TARGET).lib $(ALSA_LIB)
+
+
+clean: .SYMBOLIC
+     @cd $(OBJDIR)
+     -rm *.obj *.err *.lnk *.sys *.sym *.lst *.map *.wmap *.lib 2>nul
+     @cd ../..
Index: /GPL/branches/alsa-resync1/alsa-kernel/core/seq/makefile.os2
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/core/seq/makefile.os2	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/core/seq/makefile.os2	(revision 91)
@@ -0,0 +1,78 @@
+CINCLUDES = ..\..\..\include;..\..\include;$(%WATCOM)\H
+AINCLUDES = $(DDK_BASE)\H
+
+CDEFINES = -D__KERNEL__ -DMODULE -dTARGET_OS2 -dALSA_BUILD -D__ISAPNP__ -DCONFIG_ISAPNP -D__i386_
+
+32BIT=1
+!include ..\..\..\..\makefile.inc
+
+NMAKEOPTS=-f makefile.os2 $(LOGO)
+
+!if "$(DEBUG)" == "1"
+NMAKEOPTS += DEBUG=1
+!endif
+
+!if "$(KEE)" == "0"
+NMAKEOPTS += KEE=0
+KEE=0
+!else
+NMAKEOPTS += KEE=1
+KEE=1
+!endif
+
+!if "$(DEBUG)" == "1"
+!Message BUILDING DEBUG VERSION
+!else
+!Message BUILDING RELEASE VERSION
+!endif
+
+#===================================================================
+#
+#   List of source files
+#
+#===================================================================
+FILE1    = seq_device.obj seq.obj seq_instr.obj seq_midi_emul.obj seq_midi_event.obj 
+#seq_virmidi.obj
+FILE2    = seq.obj seq_lock.obj seq_clientmgr.obj seq_memory.obj seq_queue.obj 
+FILE3    = seq_fifo.obj seq_prioq.obj seq_timer.obj 
+FILE4    = seq_system.obj seq_ports.obj seq_info.obj seq_sync.obj
+FILE5    = seq_midi_clock.obj seq_mtc.obj seq_dtl.obj
+FILELAST =
+FILES    = $(FILE0) $(FILE1) $(FILE2) $(FILE3) $(FILE4) $(FILE5) $(FILE6) $(FILE7) $(FILE8) $(FILE9) $(FILE10) $(FILE11) $(FILE12)
+
+TARGET   = sequencer
+
+LNKFILE  = $(OBJDIR)\$(TARGET).lnk
+
+
+#===================================================================
+#
+#   Specific dependencies
+#
+#===================================================================
+all: $(OBJDIR)\$(TARGET).lib subdirs
+
+
+$(LNKFILE): makefile.os2
+    @%create $(LNKFILE)
+    @for %f in ($(FILES)) do @%append $(LNKFILE) -+$(OBJDIR)\%f
+
+$(OBJDIR)\$(TARGET).lib: $(LNKFILE) $(FILES)
+    $(LIB) $(OBJDIR)\$(TARGET).lib @$(LNKFILE)
+    copy $(OBJDIR)\$(TARGET).lib $(ALSA_LIB)
+
+subdirs: .SYMBOLIC
+    @echo BUILDING instr
+    @cd instr
+    @wmake $(NMAKEOPTS)
+    @cd ..
+
+clean: .SYMBOLIC
+    @echo BUILDING instr
+    @cd instr
+    @wmake $(NMAKEOPTS) clean
+    @cd ..
+    -@rm *.err 2>nul
+    @cd $(OBJDIR)
+    -rm *.obj *.lnk *.sys *.sym *.lst *.map *.wmap *.lib 2>nul
+    @cd ../..
Index: /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq.c	(revision 91)
@@ -0,0 +1,153 @@
+/*
+ *  ALSA sequencer main module
+ *  Copyright (c) 1998-1999 by Frank van de Pol <fvdpol@coil.demon.nl>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/moduleparam.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+
+#include <sound/seq_kernel.h>
+#include "seq_clientmgr.h"
+#include "seq_memory.h"
+#include "seq_queue.h"
+#include "seq_lock.h"
+#include "seq_timer.h"
+#include "seq_system.h"
+#include "seq_info.h"
+#include <sound/seq_device.h>
+
+#if defined(CONFIG_SND_SEQ_DUMMY_MODULE)
+int seq_client_load[64] = {[0] = SNDRV_SEQ_CLIENT_DUMMY, [1 ... 63] = -1};
+#else
+int seq_client_load[64] = {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1};
+#endif
+int seq_default_timer_class = SNDRV_TIMER_CLASS_GLOBAL;
+int seq_default_timer_sclass = SNDRV_TIMER_SCLASS_NONE;
+int seq_default_timer_card = -1;
+int seq_default_timer_device =
+#ifdef CONFIG_SND_SEQ_RTCTIMER_DEFAULT
+	SNDRV_TIMER_GLOBAL_RTC
+#else
+	SNDRV_TIMER_GLOBAL_SYSTEM
+#endif
+	;
+int seq_default_timer_subdevice = 0;
+int seq_default_timer_resolution = 0;	/* Hz */
+
+MODULE_AUTHOR("Frank van de Pol <fvdpol@coil.demon.nl>, Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Advanced Linux Sound Architecture sequencer.");
+MODULE_LICENSE("GPL");
+
+//module_param_array(seq_client_load, int, NULL, 0444);
+MODULE_PARM_DESC(seq_client_load, "The numbers of global (system) clients to load through kmod.");
+//module_param(seq_default_timer_class, int, 0644);
+MODULE_PARM_DESC(seq_default_timer_class, "The default timer class.");
+//module_param(seq_default_timer_sclass, int, 0644);
+MODULE_PARM_DESC(seq_default_timer_sclass, "The default timer slave class.");
+//module_param(seq_default_timer_card, int, 0644);
+MODULE_PARM_DESC(seq_default_timer_card, "The default timer card number.");
+//module_param(seq_default_timer_device, int, 0644);
+MODULE_PARM_DESC(seq_default_timer_device, "The default timer device number.");
+//module_param(seq_default_timer_subdevice, int, 0644);
+MODULE_PARM_DESC(seq_default_timer_subdevice, "The default timer subdevice number.");
+//module_param(seq_default_timer_resolution, int, 0644);
+MODULE_PARM_DESC(seq_default_timer_resolution, "The default timer resolution in Hz.");
+
+/*
+ *  INIT PART
+ */
+
+static int __init alsa_seq_init(void)
+{
+	int err;
+
+	snd_seq_autoload_lock();
+	if ((err = client_init_data()) < 0)
+		goto error;
+
+	/* init memory, room for selected events */
+	if ((err = snd_sequencer_memory_init()) < 0)
+		goto error;
+
+	/* init event queues */
+	if ((err = snd_seq_queues_init()) < 0)
+		goto error;
+
+	/* register sequencer device */
+	if ((err = snd_sequencer_device_init()) < 0)
+		goto error;
+
+	/* register proc interface */
+	if ((err = snd_seq_info_init()) < 0)
+		goto error;
+
+	/* register our internal client */
+	if ((err = snd_seq_system_client_init()) < 0)
+		goto error;
+
+ error:
+	snd_seq_autoload_unlock();
+	return err;
+}
+
+static void __exit alsa_seq_exit(void)
+{
+	/* unregister our internal client */
+	snd_seq_system_client_done();
+
+	/* unregister proc interface */
+	snd_seq_info_done();
+	
+	/* delete timing queues */
+	snd_seq_queues_delete();
+
+	/* unregister sequencer device */
+	snd_sequencer_device_done();
+
+	/* release event memory */
+	snd_sequencer_memory_done();
+}
+
+module_init(alsa_seq_init)
+module_exit(alsa_seq_exit)
+
+  /* seq_clientmgr.c */
+EXPORT_SYMBOL(snd_seq_create_kernel_client);
+EXPORT_SYMBOL(snd_seq_delete_kernel_client);
+EXPORT_SYMBOL(snd_seq_kernel_client_enqueue);
+EXPORT_SYMBOL(snd_seq_kernel_client_enqueue_blocking);
+EXPORT_SYMBOL(snd_seq_kernel_client_dispatch);
+EXPORT_SYMBOL(snd_seq_kernel_client_ctl);
+EXPORT_SYMBOL(snd_seq_kernel_client_write_poll);
+EXPORT_SYMBOL(snd_seq_set_queue_tempo);
+  /* seq_memory.c */
+EXPORT_SYMBOL(snd_seq_expand_var_event);
+EXPORT_SYMBOL(snd_seq_dump_var_event);
+  /* seq_ports.c */
+EXPORT_SYMBOL(snd_seq_event_port_attach);
+EXPORT_SYMBOL(snd_seq_event_port_detach);
+  /* seq_lock.c */
+#if defined(CONFIG_SMP) || defined(CONFIG_SND_DEBUG)
+/*EXPORT_SYMBOL(snd_seq_sleep_in_lock);*/
+/*EXPORT_SYMBOL(snd_seq_sleep_timeout_in_lock);*/
+EXPORT_SYMBOL(snd_use_lock_sync_helper);
+#endif
Index: /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_clientmgr.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_clientmgr.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_clientmgr.c	(revision 91)
@@ -0,0 +1,2562 @@
+/*
+ *  ALSA sequencer Client Manager
+ *  Copyright (c) 1998-2001 by Frank van de Pol <fvdpol@coil.demon.nl>
+ *                             Jaroslav Kysela <perex@suse.cz>
+ *                             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 <linux/init.h>
+//#include <linux/smp_lock.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/minors.h>
+#include <linux/kmod.h>
+
+#include <sound/seq_kernel.h>
+#include "seq_clientmgr.h"
+#include "seq_memory.h"
+#include "seq_queue.h"
+#include "seq_timer.h"
+#include "seq_info.h"
+#include "seq_system.h"
+#include <sound/seq_device.h>
+#ifdef CONFIG_COMPAT
+#include <linux/compat.h>
+#endif
+
+/* Client Manager
+
+ * this module handles the connections of userland and kernel clients
+ * 
+ */
+
+#define SNDRV_SEQ_LFLG_INPUT	0x0001
+#define SNDRV_SEQ_LFLG_OUTPUT	0x0002
+#define SNDRV_SEQ_LFLG_OPEN	(SNDRV_SEQ_LFLG_INPUT|SNDRV_SEQ_LFLG_OUTPUT)
+
+static spinlock_t clients_lock = SPIN_LOCK_UNLOCKED;
+static DECLARE_MUTEX(register_mutex);
+
+/*
+ * client table
+ */
+static char clienttablock[SNDRV_SEQ_MAX_CLIENTS];
+static struct snd_seq_client *clienttab[SNDRV_SEQ_MAX_CLIENTS];
+static struct snd_seq_usage client_usage;
+
+/*
+ * prototypes
+ */
+static int bounce_error_event(struct snd_seq_client *client,
+			      struct snd_seq_event *event,
+			      int err, int atomic, int hop);
+static int snd_seq_deliver_single_event(struct snd_seq_client *client,
+					struct snd_seq_event *event,
+					int filter, int atomic, int hop);
+
+/*
+ */
+#ifndef TARGET_OS2
+static inline mm_segment_t snd_enter_user(void)
+{
+	mm_segment_t fs = get_fs();
+	set_fs(get_ds());
+	return fs;
+}
+
+static inline void snd_leave_user(mm_segment_t fs)
+{
+	set_fs(fs);
+}
+#endif
+/*
+ */
+static inline unsigned short snd_seq_file_flags(struct file *file)
+{
+        switch (file->f_mode & (FMODE_READ | FMODE_WRITE)) {
+        case FMODE_WRITE:
+                return SNDRV_SEQ_LFLG_OUTPUT;
+        case FMODE_READ:
+                return SNDRV_SEQ_LFLG_INPUT;
+        default:
+                return SNDRV_SEQ_LFLG_OPEN;
+        }
+}
+
+static inline int snd_seq_write_pool_allocated(struct snd_seq_client *client)
+{
+	return snd_seq_total_cells(client->pool) > 0;
+}
+
+/* return pointer to client structure for specified id */
+static struct snd_seq_client *clientptr(int clientid)
+{
+	if (clientid < 0 || clientid >= SNDRV_SEQ_MAX_CLIENTS) {
+		snd_printd("Seq: oops. Trying to get pointer to client %d\n",
+			   clientid);
+		return NULL;
+	}
+	return clienttab[clientid];
+}
+
+extern int seq_client_load[];
+
+struct snd_seq_client *snd_seq_client_use_ptr(int clientid)
+{
+	unsigned long flags;
+	struct snd_seq_client *client;
+
+	if (clientid < 0 || clientid >= SNDRV_SEQ_MAX_CLIENTS) {
+		snd_printd("Seq: oops. Trying to get pointer to client %d\n",
+			   clientid);
+		return NULL;
+	}
+	spin_lock_irqsave(&clients_lock, flags);
+	client = clientptr(clientid);
+	if (client)
+		goto __lock;
+	if (clienttablock[clientid]) {
+		spin_unlock_irqrestore(&clients_lock, flags);
+		return NULL;
+	}
+	spin_unlock_irqrestore(&clients_lock, flags);
+#ifdef CONFIG_KMOD
+	if (!in_interrupt() && current->fs->root) {
+		static char client_requested[64];
+		static char card_requested[SNDRV_CARDS];
+		if (clientid < 64) {
+			int idx;
+			
+			if (! client_requested[clientid] && current->fs->root) {
+				client_requested[clientid] = 1;
+				for (idx = 0; idx < 64; idx++) {
+					if (seq_client_load[idx] < 0)
+						break;
+					if (seq_client_load[idx] == clientid) {
+						request_module("snd-seq-client-%i",
+							       clientid);
+						break;
+					}
+				}
+			}
+		} else if (clientid >= 64 && clientid < 128) {
+			int card = (clientid - 64) / 8;
+			if (card < snd_ecards_limit) {
+				if (! card_requested[card]) {
+					card_requested[card] = 1;
+					snd_request_card(card);
+				}
+				snd_seq_device_load_drivers();
+			}
+		}
+		spin_lock_irqsave(&clients_lock, flags);
+		client = clientptr(clientid);
+		if (client)
+			goto __lock;
+		spin_unlock_irqrestore(&clients_lock, flags);
+	}
+#endif
+	return NULL;
+
+      __lock:
+	snd_use_lock_use(&client->use_lock);
+	spin_unlock_irqrestore(&clients_lock, flags);
+	return client;
+}
+
+static void usage_alloc(struct snd_seq_usage *res, int num)
+{
+	res->cur += num;
+	if (res->cur > res->peak)
+		res->peak = res->cur;
+}
+
+static void usage_free(struct snd_seq_usage *res, int num)
+{
+	res->cur -= num;
+}
+
+/* initialise data structures */
+int __init client_init_data(void)
+{
+	/* zap out the client table */
+	memset(&clienttablock, 0, sizeof(clienttablock));
+	memset(&clienttab, 0, sizeof(clienttab));
+	return 0;
+}
+
+
+static struct snd_seq_client *seq_create_client1(int client_index, int poolsize)
+{
+	unsigned long flags;
+	int c;
+	struct snd_seq_client *client;
+
+	/* init client data */
+	client = (struct snd_seq_client *)kzalloc(sizeof(*client), GFP_KERNEL);
+	if (client == NULL)
+		return NULL;
+	client->pool = snd_seq_pool_new(poolsize);
+	if (client->pool == NULL) {
+		kfree(client);
+		return NULL;
+	}
+	client->type = NO_CLIENT;
+	snd_use_lock_init(&client->use_lock);
+	rwlock_init(&client->ports_lock);
+	init_MUTEX(&client->ports_mutex);
+	INIT_LIST_HEAD(&client->ports_list_head);
+
+	/* find free slot in the client table */
+	spin_lock_irqsave(&clients_lock, flags);
+	if (client_index < 0) {
+		for (c = 128; c < SNDRV_SEQ_MAX_CLIENTS; c++) {
+			if (clienttab[c] || clienttablock[c])
+				continue;
+			clienttab[client->number = c] = client;
+			spin_unlock_irqrestore(&clients_lock, flags);
+			return client;
+		}
+	} else {
+		if (clienttab[client_index] == NULL && !clienttablock[client_index]) {
+			clienttab[client->number = client_index] = client;
+			spin_unlock_irqrestore(&clients_lock, flags);
+			return client;
+		}
+	}
+	spin_unlock_irqrestore(&clients_lock, flags);
+	snd_seq_pool_delete(&client->pool);
+	kfree(client);
+	return NULL;	/* no free slot found or busy, return failure code */
+}
+
+
+static int seq_free_client1(struct snd_seq_client *client)
+{
+	unsigned long flags;
+
+	snd_assert(client != NULL, return -EINVAL);
+	snd_seq_delete_all_ports(client);
+	snd_seq_queue_client_leave(client->number);
+	spin_lock_irqsave(&clients_lock, flags);
+	clienttablock[client->number] = 1;
+	clienttab[client->number] = NULL;
+	spin_unlock_irqrestore(&clients_lock, flags);
+	snd_use_lock_sync(&client->use_lock);
+	snd_seq_queue_client_termination(client->number);
+	if (client->pool)
+		snd_seq_pool_delete(&client->pool);
+	spin_lock_irqsave(&clients_lock, flags);
+	clienttablock[client->number] = 0;
+	spin_unlock_irqrestore(&clients_lock, flags);
+	return 0;
+}
+
+
+static void seq_free_client(struct snd_seq_client * client)
+{
+	down(&register_mutex);
+	switch (client->type) {
+	case NO_CLIENT:
+		snd_printk(KERN_WARNING "Seq: Trying to free unused client %d\n",
+			   client->number);
+		break;
+	case USER_CLIENT:
+	case KERNEL_CLIENT:
+		seq_free_client1(client);
+		usage_free(&client_usage, 1);
+		break;
+
+	default:
+		snd_printk(KERN_ERR "Seq: Trying to free client %d with undefined type = %d\n",
+			   client->number, client->type);
+	}
+	up(&register_mutex);
+
+	snd_seq_system_client_ev_client_exit(client->number);
+}
+
+
+
+/* -------------------------------------------------------- */
+
+/* create a user client */
+static int snd_seq_open(struct inode *inode, struct file *file)
+{
+	int c, mode;			/* client id */
+	struct snd_seq_client *client;
+	struct snd_seq_user_client *user;
+
+	if (down_interruptible(&register_mutex))
+		return -ERESTARTSYS;
+	client = seq_create_client1(-1, SNDRV_SEQ_DEFAULT_EVENTS);
+	if (client == NULL) {
+		up(&register_mutex);
+		return -ENOMEM;	/* failure code */
+	}
+
+	mode = snd_seq_file_flags(file);
+	if (mode & SNDRV_SEQ_LFLG_INPUT)
+		client->accept_input = 1;
+	if (mode & SNDRV_SEQ_LFLG_OUTPUT)
+		client->accept_output = 1;
+
+	user = &client->data.user;
+	user->fifo = NULL;
+	user->fifo_pool_size = 0;
+
+	if (mode & SNDRV_SEQ_LFLG_INPUT) {
+		user->fifo_pool_size = SNDRV_SEQ_DEFAULT_CLIENT_EVENTS;
+		user->fifo = snd_seq_fifo_new(user->fifo_pool_size);
+		if (user->fifo == NULL) {
+			seq_free_client1(client);
+			kfree(client);
+			up(&register_mutex);
+			return -ENOMEM;
+		}
+	}
+
+	usage_alloc(&client_usage, 1);
+	client->type = USER_CLIENT;
+	up(&register_mutex);
+
+	c = client->number;
+	file->private_data = client;
+
+	/* fill client data */
+	user->file = file;
+	sprintf(client->name, "Client-%d", c);
+
+	/* make others aware this new client */
+	snd_seq_system_client_ev_client_start(c);
+
+	return 0;
+}
+
+/* delete a user client */
+static int snd_seq_release(struct inode *inode, struct file *file)
+{
+	struct snd_seq_client *client = file->private_data;
+
+	if (client) {
+		seq_free_client(client);
+		if (client->data.user.fifo)
+			snd_seq_fifo_delete(&client->data.user.fifo);
+		kfree(client);
+	}
+
+	return 0;
+}
+
+
+/* handle client read() */
+/* possible error values:
+ *	-ENXIO	invalid client or file open mode
+ *	-ENOSPC	FIFO overflow (the flag is cleared after this error report)
+ *	-EINVAL	no enough user-space buffer to write the whole event
+ *	-EFAULT	seg. fault during copy to user space
+ */
+static ssize_t snd_seq_read(struct file *file, char __user *buf, size_t count,
+			    loff_t *offset)
+{
+	struct snd_seq_client *client = file->private_data;
+	struct snd_seq_fifo *fifo;
+	int err;
+	long result = 0;
+	struct snd_seq_event_cell *cell;
+
+	if (!(snd_seq_file_flags(file) & SNDRV_SEQ_LFLG_INPUT))
+		return -ENXIO;
+
+	if (!access_ok(VERIFY_WRITE, buf, count))
+		return -EFAULT;
+
+	/* check client structures are in place */
+	snd_assert(client != NULL, return -ENXIO);
+
+	if (!client->accept_input || (fifo = client->data.user.fifo) == NULL)
+		return -ENXIO;
+
+	if (atomic_read(&fifo->overflow) > 0) {
+		/* buffer overflow is detected */
+		snd_seq_fifo_clear(fifo);
+		/* return error code */
+		return -ENOSPC;
+	}
+
+	cell = NULL;
+	err = 0;
+	snd_seq_fifo_lock(fifo);
+
+	/* while data available in queue */
+	while (count >= sizeof(struct snd_seq_event)) {
+		int nonblock;
+
+		nonblock = (file->f_flags & O_NONBLOCK) || result > 0;
+		if ((err = snd_seq_fifo_cell_out(fifo, &cell, nonblock)) < 0) {
+			break;
+		}
+		if (snd_seq_ev_is_variable(&cell->event)) {
+			struct snd_seq_event tmpev;
+			tmpev = cell->event;
+			tmpev.data.ext.len &= ~SNDRV_SEQ_EXT_MASK;
+			if (copy_to_user(buf, &tmpev, sizeof(struct snd_seq_event))) {
+				err = -EFAULT;
+				break;
+			}
+			count -= sizeof(struct snd_seq_event);
+			buf += sizeof(struct snd_seq_event);
+			err = snd_seq_expand_var_event(&cell->event, count,
+						       (char __force *)buf, 0,
+						       sizeof(struct snd_seq_event));
+			if (err < 0)
+				break;
+			result += err;
+			count -= err;
+			buf += err;
+		} else {
+			if (copy_to_user(buf, &cell->event, sizeof(struct snd_seq_event))) {
+				err = -EFAULT;
+				break;
+			}
+			count -= sizeof(struct snd_seq_event);
+			buf += sizeof(struct snd_seq_event);
+		}
+		snd_seq_cell_free(cell);
+		cell = NULL; /* to be sure */
+		result += sizeof(struct snd_seq_event);
+	}
+
+	if (err < 0) {
+		if (cell)
+			snd_seq_fifo_cell_putback(fifo, cell);
+		if (err == -EAGAIN && result > 0)
+			err = 0;
+	}
+	snd_seq_fifo_unlock(fifo);
+
+	return (err < 0) ? err : result;
+}
+
+
+/*
+ * check access permission to the port
+ */
+static int check_port_perm(struct snd_seq_client_port *port, unsigned int flags)
+{
+	if ((port->capability & flags) != flags)
+		return 0;
+	return flags;
+}
+
+/*
+ * check if the destination client is available, and return the pointer
+ * if filter is non-zero, client filter bitmap is tested.
+ */
+static struct snd_seq_client *get_event_dest_client(struct snd_seq_event *event,
+						    int filter)
+{
+	struct snd_seq_client *dest;
+
+	dest = snd_seq_client_use_ptr(event->dest.client);
+	if (dest == NULL)
+		return NULL;
+	if (! dest->accept_input)
+		goto __not_avail;
+	if ((dest->filter & SNDRV_SEQ_FILTER_USE_EVENT) &&
+	    ! test_bit(event->type, dest->event_filter))
+		goto __not_avail;
+	if (filter && !(dest->filter & filter))
+		goto __not_avail;
+
+	return dest; /* ok - accessible */
+__not_avail:
+	snd_seq_client_unlock(dest);
+	return NULL;
+}
+
+
+/*
+ * Return the error event.
+ *
+ * If the receiver client is a user client, the original event is
+ * encapsulated in SNDRV_SEQ_EVENT_BOUNCE as variable length event.  If
+ * the original event is also variable length, the external data is
+ * copied after the event record. 
+ * If the receiver client is a kernel client, the original event is
+ * quoted in SNDRV_SEQ_EVENT_KERNEL_ERROR, since this requires no extra
+ * kmalloc.
+ */
+static int bounce_error_event(struct snd_seq_client *client,
+			      struct snd_seq_event *event,
+			      int err, int atomic, int hop)
+{
+	struct snd_seq_event bounce_ev;
+	int result;
+
+	if (client == NULL ||
+	    ! (client->filter & SNDRV_SEQ_FILTER_BOUNCE) ||
+	    ! client->accept_input)
+		return 0; /* ignored */
+
+	/* set up quoted error */
+	memset(&bounce_ev, 0, sizeof(bounce_ev));
+	bounce_ev.type = SNDRV_SEQ_EVENT_KERNEL_ERROR;
+	bounce_ev.flags = SNDRV_SEQ_EVENT_LENGTH_FIXED;
+	bounce_ev.queue = SNDRV_SEQ_QUEUE_DIRECT;
+	bounce_ev.source.client = SNDRV_SEQ_CLIENT_SYSTEM;
+	bounce_ev.source.port = SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE;
+	bounce_ev.dest.client = client->number;
+	bounce_ev.dest.port = event->source.port;
+	bounce_ev.data.quote.origin = event->dest;
+	bounce_ev.data.quote.event = event;
+	bounce_ev.data.quote.value = -err; /* use positive value */
+	result = snd_seq_deliver_single_event(NULL, &bounce_ev, 0, atomic, hop + 1);
+	if (result < 0) {
+		client->event_lost++;
+		return result;
+	}
+
+	return result;
+}
+
+
+/*
+ * rewrite the time-stamp of the event record with the curren time
+ * of the given queue.
+ * return non-zero if updated.
+ */
+static int update_timestamp_of_queue(struct snd_seq_event *event,
+				     int queue, int real_time)
+{
+	struct snd_seq_queue *q;
+
+	q = queueptr(queue);
+	if (! q)
+		return 0;
+	event->queue = queue;
+	event->flags &= ~SNDRV_SEQ_TIME_STAMP_MASK;
+	if (real_time) {
+		event->time.time = snd_seq_timer_get_cur_time(q->timer);
+		event->flags |= SNDRV_SEQ_TIME_STAMP_REAL;
+	} else {
+		event->time.tick = snd_seq_timer_get_cur_tick(q->timer);
+		event->flags |= SNDRV_SEQ_TIME_STAMP_TICK;
+	}
+	queuefree(q);
+	return 1;
+}
+
+
+/*
+ * deliver an event to the specified destination.
+ * if filter is non-zero, client filter bitmap is tested.
+ *
+ *  RETURN VALUE: 0 : if succeeded
+ *		 <0 : error
+ */
+static int snd_seq_deliver_single_event(struct snd_seq_client *client,
+					struct snd_seq_event *event,
+					int filter, int atomic, int hop)
+{
+	struct snd_seq_client *dest = NULL;
+	struct snd_seq_client_port *dest_port = NULL;
+	int result = -ENOENT;
+	int direct;
+
+	direct = snd_seq_ev_is_direct(event);
+
+	dest = get_event_dest_client(event, filter);
+	if (dest == NULL)
+		goto __skip;
+	dest_port = snd_seq_port_use_ptr(dest, event->dest.port);
+	if (dest_port == NULL)
+		goto __skip;
+
+	/* check permission */
+	if (! check_port_perm(dest_port, SNDRV_SEQ_PORT_CAP_WRITE)) {
+		result = -EPERM;
+		goto __skip;
+	}
+		
+	if (dest_port->timestamping)
+		update_timestamp_of_queue(event, dest_port->time_queue,
+					  dest_port->time_real);
+
+	switch (dest->type) {
+	case USER_CLIENT:
+		if (dest->data.user.fifo)
+			result = snd_seq_fifo_event_in(dest->data.user.fifo, event);
+		break;
+
+	case KERNEL_CLIENT:
+		if (dest_port->event_input == NULL)
+			break;
+		result = dest_port->event_input(event, direct,
+						dest_port->private_data,
+						atomic, hop);
+		break;
+	default:
+		break;
+	}
+
+  __skip:
+	if (dest_port)
+		snd_seq_port_unlock(dest_port);
+	if (dest)
+		snd_seq_client_unlock(dest);
+
+	if (result < 0 && !direct) {
+		result = bounce_error_event(client, event, result, atomic, hop);
+	}
+	return result;
+}
+
+
+/*
+ * send the event to all subscribers:
+ */
+static int deliver_to_subscribers(struct snd_seq_client *client,
+				  struct snd_seq_event *event,
+				  int atomic, int hop)
+{
+	struct snd_seq_subscribers *subs;
+	int err = 0, num_ev = 0;
+	struct snd_seq_event event_saved;
+	struct snd_seq_client_port *src_port;
+	struct list_head *p;
+	struct snd_seq_port_subs_info *grp;
+
+	src_port = snd_seq_port_use_ptr(client, event->source.port);
+	if (src_port == NULL)
+		return -EINVAL; /* invalid source port */
+	/* save original event record */
+	event_saved = *event;
+	grp = &src_port->c_src;
+	
+	/* lock list */
+	if (atomic)
+		read_lock(&grp->list_lock);
+	else
+		down_read(&grp->list_mutex);
+	list_for_each(p, &grp->list_head) {
+		subs = list_entry(p, struct snd_seq_subscribers, src_list);
+		event->dest = subs->info.dest;
+		if (subs->info.flags & SNDRV_SEQ_PORT_SUBS_TIMESTAMP)
+			/* convert time according to flag with subscription */
+			update_timestamp_of_queue(event, subs->info.queue,
+						  subs->info.flags & SNDRV_SEQ_PORT_SUBS_TIME_REAL);
+		err = snd_seq_deliver_single_event(client, event,
+						   0, atomic, hop);
+		if (err < 0)
+			break;
+		num_ev++;
+		/* restore original event record */
+		*event = event_saved;
+	}
+	if (atomic)
+		read_unlock(&grp->list_lock);
+	else
+		up_read(&grp->list_mutex);
+	*event = event_saved; /* restore */
+	snd_seq_port_unlock(src_port);
+	return (err < 0) ? err : num_ev;
+}
+
+
+#ifdef SUPPORT_BROADCAST 
+/*
+ * broadcast to all ports:
+ */
+static int port_broadcast_event(struct snd_seq_client *client,
+				struct snd_seq_event *event,
+				int atomic, int hop)
+{
+	int num_ev = 0, err = 0;
+	struct snd_seq_client *dest_client;
+	struct list_head *p;
+
+	dest_client = get_event_dest_client(event, SNDRV_SEQ_FILTER_BROADCAST);
+	if (dest_client == NULL)
+		return 0; /* no matching destination */
+
+	read_lock(&dest_client->ports_lock);
+	list_for_each(p, &dest_client->ports_list_head) {
+		struct snd_seq_client_port *port = list_entry(p, struct snd_seq_client_port, list);
+		event->dest.port = port->addr.port;
+		/* pass NULL as source client to avoid error bounce */
+		err = snd_seq_deliver_single_event(NULL, event,
+						   SNDRV_SEQ_FILTER_BROADCAST,
+						   atomic, hop);
+		if (err < 0)
+			break;
+		num_ev++;
+	}
+	read_unlock(&dest_client->ports_lock);
+	snd_seq_client_unlock(dest_client);
+	event->dest.port = SNDRV_SEQ_ADDRESS_BROADCAST; /* restore */
+	return (err < 0) ? err : num_ev;
+}
+
+/*
+ * send the event to all clients:
+ * if destination port is also ADDRESS_BROADCAST, deliver to all ports.
+ */
+static int broadcast_event(struct snd_seq_client *client,
+			   struct snd_seq_event *event, int atomic, int hop)
+{
+	int err = 0, num_ev = 0;
+	int dest;
+	struct snd_seq_addr addr;
+
+	addr = event->dest; /* save */
+
+	for (dest = 0; dest < SNDRV_SEQ_MAX_CLIENTS; dest++) {
+		/* don't send to itself */
+		if (dest == client->number)
+			continue;
+		event->dest.client = dest;
+		event->dest.port = addr.port;
+		if (addr.port == SNDRV_SEQ_ADDRESS_BROADCAST)
+			err = port_broadcast_event(client, event, atomic, hop);
+		else
+			/* pass NULL as source client to avoid error bounce */
+			err = snd_seq_deliver_single_event(NULL, event,
+							   SNDRV_SEQ_FILTER_BROADCAST,
+							   atomic, hop);
+		if (err < 0)
+			break;
+		num_ev += err;
+	}
+	event->dest = addr; /* restore */
+	return (err < 0) ? err : num_ev;
+}
+
+
+/* multicast - not supported yet */
+static int multicast_event(struct snd_seq_client *client, struct snd_seq_event *event,
+			   int atomic, int hop)
+{
+	snd_printd("seq: multicast not supported yet.\n");
+	return 0; /* ignored */
+}
+#endif /* SUPPORT_BROADCAST */
+
+
+/* deliver an event to the destination port(s).
+ * if the event is to subscribers or broadcast, the event is dispatched
+ * to multiple targets.
+ *
+ * RETURN VALUE: n > 0  : the number of delivered events.
+ *               n == 0 : the event was not passed to any client.
+ *               n < 0  : error - event was not processed.
+ */
+static int snd_seq_deliver_event(struct snd_seq_client *client, struct snd_seq_event *event,
+				 int atomic, int hop)
+{
+	int result;
+
+	hop++;
+	if (hop >= SNDRV_SEQ_MAX_HOPS) {
+		snd_printd("too long delivery path (%d:%d->%d:%d)\n",
+			   event->source.client, event->source.port,
+			   event->dest.client, event->dest.port);
+		return -EMLINK;
+	}
+
+	if (event->queue == SNDRV_SEQ_ADDRESS_SUBSCRIBERS ||
+	    event->dest.client == SNDRV_SEQ_ADDRESS_SUBSCRIBERS)
+		result = deliver_to_subscribers(client, event, atomic, hop);
+#ifdef SUPPORT_BROADCAST
+	else if (event->queue == SNDRV_SEQ_ADDRESS_BROADCAST ||
+		 event->dest.client == SNDRV_SEQ_ADDRESS_BROADCAST)
+		result = broadcast_event(client, event, atomic, hop);
+	else if (event->dest.client >= SNDRV_SEQ_MAX_CLIENTS)
+		result = multicast_event(client, event, atomic, hop);
+	else if (event->dest.port == SNDRV_SEQ_ADDRESS_BROADCAST)
+		result = port_broadcast_event(client, event, atomic, hop);
+#endif
+	else
+		result = snd_seq_deliver_single_event(client, event, 0, atomic, hop);
+
+	return result;
+}
+
+/*
+ * dispatch an event cell:
+ * This function is called only from queue check routines in timer
+ * interrupts or after enqueued.
+ * The event cell shall be released or re-queued in this function.
+ *
+ * RETURN VALUE: n > 0  : the number of delivered events.
+ *		 n == 0 : the event was not passed to any client.
+ *		 n < 0  : error - event was not processed.
+ */
+int snd_seq_dispatch_event(struct snd_seq_event_cell *cell, int atomic, int hop)
+{
+	struct snd_seq_client *client;
+	int result;
+
+	snd_assert(cell != NULL, return -EINVAL);
+
+	client = snd_seq_client_use_ptr(cell->event.source.client);
+	if (client == NULL) {
+		snd_seq_cell_free(cell); /* release this cell */
+		return -EINVAL;
+	}
+
+	if (cell->event.type == SNDRV_SEQ_EVENT_NOTE) {
+		/* NOTE event:
+		 * the event cell is re-used as a NOTE-OFF event and
+		 * enqueued again.
+		 */
+		struct snd_seq_event tmpev, *ev;
+
+		/* reserve this event to enqueue note-off later */
+		tmpev = cell->event;
+		tmpev.type = SNDRV_SEQ_EVENT_NOTEON;
+		result = snd_seq_deliver_event(client, &tmpev, atomic, hop);
+
+		/*
+		 * This was originally a note event.  We now re-use the
+		 * cell for the note-off event.
+		 */
+
+		ev = &cell->event;
+		ev->type = SNDRV_SEQ_EVENT_NOTEOFF;
+		ev->flags |= SNDRV_SEQ_PRIORITY_HIGH;
+
+		/* add the duration time */
+		switch (ev->flags & SNDRV_SEQ_TIME_STAMP_MASK) {
+		case SNDRV_SEQ_TIME_STAMP_TICK:
+			ev->time.tick += ev->data.note.duration;
+			break;
+		case SNDRV_SEQ_TIME_STAMP_REAL:
+			/* unit for duration is ms */
+			ev->time.time.tv_nsec += 1000000 * (ev->data.note.duration % 1000);
+			ev->time.time.tv_sec += ev->data.note.duration / 1000 +
+						ev->time.time.tv_nsec / 1000000000;
+			ev->time.time.tv_nsec %= 1000000000;
+			break;
+		}
+		ev->data.note.velocity = ev->data.note.off_velocity;
+
+		/* Now queue this cell as the note off event */
+		if (snd_seq_enqueue_event(cell, atomic, hop) < 0)
+			snd_seq_cell_free(cell); /* release this cell */
+
+	} else {
+		/* Normal events:
+		 * event cell is freed after processing the event
+		 */
+
+		result = snd_seq_deliver_event(client, &cell->event, atomic, hop);
+		snd_seq_cell_free(cell);
+	}
+
+	snd_seq_client_unlock(client);
+	return result;
+}
+
+
+/* Allocate a cell from client pool and enqueue it to queue:
+ * if pool is empty and blocking is TRUE, sleep until a new cell is
+ * available.
+ */
+static int snd_seq_client_enqueue_event(struct snd_seq_client *client,
+					struct snd_seq_event *event,
+					struct file *file, int blocking,
+					int atomic, int hop)
+{
+	struct snd_seq_event_cell *cell;
+	int err;
+
+	/* special queue values - force direct passing */
+	if (event->queue == SNDRV_SEQ_ADDRESS_SUBSCRIBERS) {
+		event->dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS;
+		event->queue = SNDRV_SEQ_QUEUE_DIRECT;
+	} else
+#ifdef SUPPORT_BROADCAST
+		if (event->queue == SNDRV_SEQ_ADDRESS_BROADCAST) {
+			event->dest.client = SNDRV_SEQ_ADDRESS_BROADCAST;
+			event->queue = SNDRV_SEQ_QUEUE_DIRECT;
+		}
+#endif
+	if (event->dest.client == SNDRV_SEQ_ADDRESS_SUBSCRIBERS) {
+		/* check presence of source port */
+		struct snd_seq_client_port *src_port = snd_seq_port_use_ptr(client, event->source.port);
+		if (src_port == NULL)
+			return -EINVAL;
+		snd_seq_port_unlock(src_port);
+	}
+
+	/* direct event processing without enqueued */
+	if (snd_seq_ev_is_direct(event)) {
+		if (event->type == SNDRV_SEQ_EVENT_NOTE)
+			return -EINVAL; /* this event must be enqueued! */
+		return snd_seq_deliver_event(client, event, atomic, hop);
+	}
+
+	/* Not direct, normal queuing */
+	if (snd_seq_queue_is_used(event->queue, client->number) <= 0)
+		return -EINVAL;  /* invalid queue */
+	if (! snd_seq_write_pool_allocated(client))
+		return -ENXIO; /* queue is not allocated */
+
+	/* allocate an event cell */
+	err = snd_seq_event_dup(client->pool, event, &cell, !blocking || atomic, file);
+	if (err < 0)
+		return err;
+
+	/* we got a cell. enqueue it. */
+	if ((err = snd_seq_enqueue_event(cell, atomic, hop)) < 0) {
+		snd_seq_cell_free(cell);
+		return err;
+	}
+
+	return 0;
+}
+
+
+/*
+ * check validity of event type and data length.
+ * return non-zero if invalid.
+ */
+static int check_event_type_and_length(struct snd_seq_event *ev)
+{
+	switch (snd_seq_ev_length_type(ev)) {
+	case SNDRV_SEQ_EVENT_LENGTH_FIXED:
+		if (snd_seq_ev_is_variable_type(ev))
+			return -EINVAL;
+		break;
+	case SNDRV_SEQ_EVENT_LENGTH_VARIABLE:
+		if (! snd_seq_ev_is_variable_type(ev) ||
+		    (ev->data.ext.len & ~SNDRV_SEQ_EXT_MASK) >= SNDRV_SEQ_MAX_EVENT_LEN)
+			return -EINVAL;
+		break;
+	case SNDRV_SEQ_EVENT_LENGTH_VARUSR:
+		if (! snd_seq_ev_is_instr_type(ev) ||
+		    ! snd_seq_ev_is_direct(ev))
+			return -EINVAL;
+		break;
+	}
+	return 0;
+}
+
+
+/* handle write() */
+/* possible error values:
+ *	-ENXIO	invalid client or file open mode
+ *	-ENOMEM	malloc failed
+ *	-EFAULT	seg. fault during copy from user space
+ *	-EINVAL	invalid event
+ *	-EAGAIN	no space in output pool
+ *	-EINTR	interrupts while sleep
+ *	-EMLINK	too many hops
+ *	others	depends on return value from driver callback
+ */
+static ssize_t snd_seq_write(struct file *file, const char __user *buf,
+			     size_t count, loff_t *offset)
+{
+	struct snd_seq_client *client = file->private_data;
+	int written = 0, len;
+	int err = -EINVAL;
+	struct snd_seq_event event;
+
+	if (!(snd_seq_file_flags(file) & SNDRV_SEQ_LFLG_OUTPUT))
+		return -ENXIO;
+
+	/* check client structures are in place */
+	snd_assert(client != NULL, return -ENXIO);
+		
+	if (!client->accept_output || client->pool == NULL)
+		return -ENXIO;
+
+	/* allocate the pool now if the pool is not allocated yet */ 
+	if (client->pool->size > 0 && !snd_seq_write_pool_allocated(client)) {
+		if (snd_seq_pool_init(client->pool) < 0)
+			return -ENOMEM;
+	}
+
+	/* only process whole events */
+	while (count >= sizeof(struct snd_seq_event)) {
+		/* Read in the event header from the user */
+		len = sizeof(event);
+		if (copy_from_user(&event, buf, len)) {
+			err = -EFAULT;
+			break;
+		}
+		event.source.client = client->number;	/* fill in client number */
+		/* Check for extension data length */
+		if (check_event_type_and_length(&event)) {
+			err = -EINVAL;
+			break;
+		}
+
+		/* check for special events */
+		if (event.type == SNDRV_SEQ_EVENT_NONE)
+			goto __skip_event;
+		else if (snd_seq_ev_is_reserved(&event)) {
+			err = -EINVAL;
+			break;
+		}
+
+		if (snd_seq_ev_is_variable(&event)) {
+			int extlen = event.data.ext.len & ~SNDRV_SEQ_EXT_MASK;
+			if ((size_t)(extlen + len) > count) {
+				/* back out, will get an error this time or next */
+				err = -EINVAL;
+				break;
+			}
+			/* set user space pointer */
+			event.data.ext.len = extlen | SNDRV_SEQ_EXT_USRPTR;
+			event.data.ext.ptr = (char __force *)buf
+						+ sizeof(struct snd_seq_event);
+			len += extlen; /* increment data length */
+		} else {
+#ifdef CONFIG_COMPAT
+			if (client->convert32 && snd_seq_ev_is_varusr(&event)) {
+				void *ptr = compat_ptr(event.data.raw32.d[1]);
+				event.data.ext.ptr = ptr;
+			}
+#endif
+		}
+
+		/* ok, enqueue it */
+		err = snd_seq_client_enqueue_event(client, &event, file,
+						   !(file->f_flags & O_NONBLOCK),
+						   0, 0);
+		if (err < 0)
+			break;
+
+	__skip_event:
+		/* Update pointers and counts */
+		count -= len;
+		buf += len;
+		written += len;
+	}
+
+	return written ? written : err;
+}
+
+
+/*
+ * handle polling
+ */
+static unsigned int snd_seq_poll(struct file *file, poll_table * wait)
+{
+	struct snd_seq_client *client = file->private_data;
+	unsigned int mask = 0;
+
+	/* check client structures are in place */
+	snd_assert(client != NULL, return -ENXIO);
+
+	if ((snd_seq_file_flags(file) & SNDRV_SEQ_LFLG_INPUT) &&
+	    client->data.user.fifo) {
+
+		/* check if data is available in the outqueue */
+		if (snd_seq_fifo_poll_wait(client->data.user.fifo, file, wait))
+			mask |= POLLIN | POLLRDNORM;
+	}
+
+	if (snd_seq_file_flags(file) & SNDRV_SEQ_LFLG_OUTPUT) {
+
+		/* check if data is available in the pool */
+		if (!snd_seq_write_pool_allocated(client) ||
+		    snd_seq_pool_poll_wait(client->pool, file, wait))
+			mask |= POLLOUT | POLLWRNORM;
+	}
+
+	return mask;
+}
+
+
+/*-----------------------------------------------------*/
+
+
+/* SYSTEM_INFO ioctl() */
+static int snd_seq_ioctl_system_info(struct snd_seq_client *client, void __user *arg)
+{
+	struct snd_seq_system_info info;
+
+	memset(&info, 0, sizeof(info));
+	/* fill the info fields */
+	info.queues = SNDRV_SEQ_MAX_QUEUES;
+	info.clients = SNDRV_SEQ_MAX_CLIENTS;
+	info.ports = 256;	/* fixed limit */
+	info.channels = 256;	/* fixed limit */
+	info.cur_clients = client_usage.cur;
+	info.cur_queues = snd_seq_queue_get_cur_queues();
+
+	if (copy_to_user(arg, &info, sizeof(info)))
+		return -EFAULT;
+	return 0;
+}
+
+
+/* RUNNING_MODE ioctl() */
+static int snd_seq_ioctl_running_mode(struct snd_seq_client *client, void __user *arg)
+{
+	struct snd_seq_running_info info;
+	struct snd_seq_client *cptr;
+	int err = 0;
+
+	if (copy_from_user(&info, arg, sizeof(info)))
+		return -EFAULT;
+
+	/* requested client number */
+	cptr = snd_seq_client_use_ptr(info.client);
+	if (cptr == NULL)
+		return -ENOENT;		/* don't change !!! */
+
+#ifdef SNDRV_BIG_ENDIAN
+	if (! info.big_endian) {
+		err = -EINVAL;
+		goto __err;
+	}
+#else
+	if (info.big_endian) {
+		err = -EINVAL;
+		goto __err;
+	}
+
+#endif
+	if (info.cpu_mode > sizeof(long)) {
+		err = -EINVAL;
+		goto __err;
+	}
+	cptr->convert32 = (info.cpu_mode < sizeof(long));
+ __err:
+	snd_seq_client_unlock(cptr);
+	return err;
+}
+
+/* CLIENT_INFO ioctl() */
+static void get_client_info(struct snd_seq_client *cptr,
+			    struct snd_seq_client_info *info)
+{
+	info->client = cptr->number;
+
+	/* fill the info fields */
+	info->type = cptr->type;
+	strcpy(info->name, cptr->name);
+	info->filter = cptr->filter;
+	info->event_lost = cptr->event_lost;
+	memcpy(info->event_filter, cptr->event_filter, 32);
+	info->num_ports = cptr->num_ports;
+	memset(info->reserved, 0, sizeof(info->reserved));
+}
+
+static int snd_seq_ioctl_get_client_info(struct snd_seq_client *client,
+					 void __user *arg)
+{
+	struct snd_seq_client *cptr;
+	struct snd_seq_client_info client_info;
+
+	if (copy_from_user(&client_info, arg, sizeof(client_info)))
+		return -EFAULT;
+
+	/* requested client number */
+	cptr = snd_seq_client_use_ptr(client_info.client);
+	if (cptr == NULL)
+		return -ENOENT;		/* don't change !!! */
+
+	get_client_info(cptr, &client_info);
+	snd_seq_client_unlock(cptr);
+
+	if (copy_to_user(arg, &client_info, sizeof(client_info)))
+		return -EFAULT;
+	return 0;
+}
+
+
+/* CLIENT_INFO ioctl() */
+static int snd_seq_ioctl_set_client_info(struct snd_seq_client *client,
+					 void __user *arg)
+{
+	struct snd_seq_client_info client_info;
+
+	if (copy_from_user(&client_info, arg, sizeof(client_info)))
+		return -EFAULT;
+
+	/* it is not allowed to set the info fields for an another client */
+	if (client->number != client_info.client)
+		return -EPERM;
+	/* also client type must be set now */
+	if (client->type != client_info.type)
+		return -EINVAL;
+
+	/* fill the info fields */
+	if (client_info.name[0])
+		strlcpy(client->name, client_info.name, sizeof(client->name));
+
+	client->filter = client_info.filter;
+	client->event_lost = client_info.event_lost;
+	memcpy(client->event_filter, client_info.event_filter, 32);
+
+	return 0;
+}
+
+
+/* 
+ * CREATE PORT ioctl() 
+ */
+static int snd_seq_ioctl_create_port(struct snd_seq_client *client,
+				     void __user *arg)
+{
+	struct snd_seq_client_port *port;
+	struct snd_seq_port_info info;
+	struct snd_seq_port_callback *callback;
+
+	if (copy_from_user(&info, arg, sizeof(info)))
+		return -EFAULT;
+
+	/* it is not allowed to create the port for an another client */
+	if (info.addr.client != client->number)
+		return -EPERM;
+
+	port = snd_seq_create_port(client, (info.flags & SNDRV_SEQ_PORT_FLG_GIVEN_PORT) ? info.addr.port : -1);
+	if (port == NULL)
+		return -ENOMEM;
+
+	if (client->type == USER_CLIENT && info.kernel) {
+		snd_seq_delete_port(client, port->addr.port);
+		return -EINVAL;
+	}
+	if (client->type == KERNEL_CLIENT) {
+		if ((callback = info.kernel) != NULL) {
+			if (callback->owner)
+				port->owner = callback->owner;
+			port->private_data = callback->private_data;
+			port->private_free = callback->private_free;
+			port->callback_all = callback->callback_all;
+			port->event_input = callback->event_input;
+			port->c_src.open = callback->subscribe;
+			port->c_src.close = callback->unsubscribe;
+			port->c_dest.open = callback->use;
+			port->c_dest.close = callback->unuse;
+		}
+	}
+
+	info.addr = port->addr;
+
+	snd_seq_set_port_info(port, &info);
+	snd_seq_system_client_ev_port_start(port->addr.client, port->addr.port);
+
+	if (copy_to_user(arg, &info, sizeof(info)))
+		return -EFAULT;
+
+	return 0;
+}
+
+/* 
+ * DELETE PORT ioctl() 
+ */
+static int snd_seq_ioctl_delete_port(struct snd_seq_client *client,
+				     void __user *arg)
+{
+	struct snd_seq_port_info info;
+	int err;
+
+	/* set passed parameters */
+	if (copy_from_user(&info, arg, sizeof(info)))
+		return -EFAULT;
+	
+	/* it is not allowed to remove the port for an another client */
+	if (info.addr.client != client->number)
+		return -EPERM;
+
+	err = snd_seq_delete_port(client, info.addr.port);
+	if (err >= 0)
+		snd_seq_system_client_ev_port_exit(client->number, info.addr.port);
+	return err;
+}
+
+
+/* 
+ * GET_PORT_INFO ioctl() (on any client) 
+ */
+static int snd_seq_ioctl_get_port_info(struct snd_seq_client *client,
+				       void __user *arg)
+{
+	struct snd_seq_client *cptr;
+	struct snd_seq_client_port *port;
+	struct snd_seq_port_info info;
+
+	if (copy_from_user(&info, arg, sizeof(info)))
+		return -EFAULT;
+	cptr = snd_seq_client_use_ptr(info.addr.client);
+	if (cptr == NULL)
+		return -ENXIO;
+
+	port = snd_seq_port_use_ptr(cptr, info.addr.port);
+	if (port == NULL) {
+		snd_seq_client_unlock(cptr);
+		return -ENOENT;			/* don't change */
+	}
+
+	/* get port info */
+	snd_seq_get_port_info(port, &info);
+	snd_seq_port_unlock(port);
+	snd_seq_client_unlock(cptr);
+
+	if (copy_to_user(arg, &info, sizeof(info)))
+		return -EFAULT;
+	return 0;
+}
+
+
+/* 
+ * SET_PORT_INFO ioctl() (only ports on this/own client) 
+ */
+static int snd_seq_ioctl_set_port_info(struct snd_seq_client *client,
+				       void __user *arg)
+{
+	struct snd_seq_client_port *port;
+	struct snd_seq_port_info info;
+
+	if (copy_from_user(&info, arg, sizeof(info)))
+		return -EFAULT;
+
+	if (info.addr.client != client->number) /* only set our own ports ! */
+		return -EPERM;
+	port = snd_seq_port_use_ptr(client, info.addr.port);
+	if (port) {
+		snd_seq_set_port_info(port, &info);
+		snd_seq_port_unlock(port);
+	}
+	return 0;
+}
+
+
+/*
+ * port subscription (connection)
+ */
+#define PERM_RD		(SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_SUBS_READ)
+#define PERM_WR		(SNDRV_SEQ_PORT_CAP_WRITE|SNDRV_SEQ_PORT_CAP_SUBS_WRITE)
+
+static int check_subscription_permission(struct snd_seq_client *client,
+					 struct snd_seq_client_port *sport,
+					 struct snd_seq_client_port *dport,
+					 struct snd_seq_port_subscribe *subs)
+{
+	if (client->number != subs->sender.client &&
+	    client->number != subs->dest.client) {
+		/* connection by third client - check export permission */
+		if (check_port_perm(sport, SNDRV_SEQ_PORT_CAP_NO_EXPORT))
+			return -EPERM;
+		if (check_port_perm(dport, SNDRV_SEQ_PORT_CAP_NO_EXPORT))
+			return -EPERM;
+	}
+
+	/* check read permission */
+	/* if sender or receiver is the subscribing client itself,
+	 * no permission check is necessary
+	 */
+	if (client->number != subs->sender.client) {
+		if (! check_port_perm(sport, PERM_RD))
+			return -EPERM;
+	}
+	/* check write permission */
+	if (client->number != subs->dest.client) {
+		if (! check_port_perm(dport, PERM_WR))
+			return -EPERM;
+	}
+	return 0;
+}
+
+/*
+ * send an subscription notify event to user client:
+ * client must be user client.
+ */
+int snd_seq_client_notify_subscription(int client, int port,
+				       struct snd_seq_port_subscribe *info,
+				       int evtype)
+{
+	struct snd_seq_event event;
+
+	memset(&event, 0, sizeof(event));
+	event.type = evtype;
+	event.data.connect.dest = info->dest;
+	event.data.connect.sender = info->sender;
+
+	return snd_seq_system_notify(client, port, &event);  /* non-atomic */
+}
+
+
+/* 
+ * add to port's subscription list IOCTL interface 
+ */
+static int snd_seq_ioctl_subscribe_port(struct snd_seq_client *client,
+					void __user *arg)
+{
+	int result = -EINVAL;
+	struct snd_seq_client *receiver = NULL, *sender = NULL;
+	struct snd_seq_client_port *sport = NULL, *dport = NULL;
+	struct snd_seq_port_subscribe subs;
+
+	if (copy_from_user(&subs, arg, sizeof(subs)))
+		return -EFAULT;
+
+	if ((receiver = snd_seq_client_use_ptr(subs.dest.client)) == NULL)
+		goto __end;
+	if ((sender = snd_seq_client_use_ptr(subs.sender.client)) == NULL)
+		goto __end;
+	if ((sport = snd_seq_port_use_ptr(sender, subs.sender.port)) == NULL)
+		goto __end;
+	if ((dport = snd_seq_port_use_ptr(receiver, subs.dest.port)) == NULL)
+		goto __end;
+
+	result = check_subscription_permission(client, sport, dport, &subs);
+	if (result < 0)
+		goto __end;
+
+	/* connect them */
+	result = snd_seq_port_connect(client, sender, sport, receiver, dport, &subs);
+	if (! result) /* broadcast announce */
+		snd_seq_client_notify_subscription(SNDRV_SEQ_ADDRESS_SUBSCRIBERS, 0,
+						   &subs, SNDRV_SEQ_EVENT_PORT_SUBSCRIBED);
+      __end:
+      	if (sport)
+		snd_seq_port_unlock(sport);
+	if (dport)
+		snd_seq_port_unlock(dport);
+	if (sender)
+		snd_seq_client_unlock(sender);
+	if (receiver)
+		snd_seq_client_unlock(receiver);
+	return result;
+}
+
+
+/* 
+ * remove from port's subscription list 
+ */
+static int snd_seq_ioctl_unsubscribe_port(struct snd_seq_client *client,
+					  void __user *arg)
+{
+	int result = -ENXIO;
+	struct snd_seq_client *receiver = NULL, *sender = NULL;
+	struct snd_seq_client_port *sport = NULL, *dport = NULL;
+	struct snd_seq_port_subscribe subs;
+
+	if (copy_from_user(&subs, arg, sizeof(subs)))
+		return -EFAULT;
+
+	if ((receiver = snd_seq_client_use_ptr(subs.dest.client)) == NULL)
+		goto __end;
+	if ((sender = snd_seq_client_use_ptr(subs.sender.client)) == NULL)
+		goto __end;
+	if ((sport = snd_seq_port_use_ptr(sender, subs.sender.port)) == NULL)
+		goto __end;
+	if ((dport = snd_seq_port_use_ptr(receiver, subs.dest.port)) == NULL)
+		goto __end;
+
+	result = check_subscription_permission(client, sport, dport, &subs);
+	if (result < 0)
+		goto __end;
+
+	result = snd_seq_port_disconnect(client, sender, sport, receiver, dport, &subs);
+	if (! result) /* broadcast announce */
+		snd_seq_client_notify_subscription(SNDRV_SEQ_ADDRESS_SUBSCRIBERS, 0,
+						   &subs, SNDRV_SEQ_EVENT_PORT_UNSUBSCRIBED);
+      __end:
+      	if (sport)
+		snd_seq_port_unlock(sport);
+	if (dport)
+		snd_seq_port_unlock(dport);
+	if (sender)
+		snd_seq_client_unlock(sender);
+	if (receiver)
+		snd_seq_client_unlock(receiver);
+	return result;
+}
+
+
+/* CREATE_QUEUE ioctl() */
+static int snd_seq_ioctl_create_queue(struct snd_seq_client *client,
+				      void __user *arg)
+{
+	struct snd_seq_queue_info info;
+	int result;
+	struct snd_seq_queue *q;
+
+	if (copy_from_user(&info, arg, sizeof(info)))
+		return -EFAULT;
+
+	result = snd_seq_queue_alloc(client->number, info.locked, info.flags);
+	if (result < 0)
+		return result;
+
+	q = queueptr(result);
+	if (q == NULL)
+		return -EINVAL;
+
+	info.queue = q->queue;
+	info.locked = q->locked;
+	info.owner = q->owner;
+
+	/* set queue name */
+	if (! info.name[0])
+		sprintf(info.name, "Queue-%d", q->queue);
+	strlcpy(q->name, info.name, sizeof(q->name));
+	queuefree(q);
+
+	if (copy_to_user(arg, &info, sizeof(info)))
+		return -EFAULT;
+
+	return 0;
+}
+
+/* DELETE_QUEUE ioctl() */
+static int snd_seq_ioctl_delete_queue(struct snd_seq_client *client,
+				      void __user *arg)
+{
+	struct snd_seq_queue_info info;
+
+	if (copy_from_user(&info, arg, sizeof(info)))
+		return -EFAULT;
+
+	return snd_seq_queue_delete(client->number, info.queue);
+}
+
+/* GET_QUEUE_INFO ioctl() */
+static int snd_seq_ioctl_get_queue_info(struct snd_seq_client *client,
+					void __user *arg)
+{
+	struct snd_seq_queue_info info;
+	struct snd_seq_queue *q;
+
+	if (copy_from_user(&info, arg, sizeof(info)))
+		return -EFAULT;
+
+	q = queueptr(info.queue);
+	if (q == NULL)
+		return -EINVAL;
+
+	memset(&info, 0, sizeof(info));
+	info.queue = q->queue;
+	info.owner = q->owner;
+	info.locked = q->locked;
+	strlcpy(info.name, q->name, sizeof(info.name));
+	queuefree(q);
+
+	if (copy_to_user(arg, &info, sizeof(info)))
+		return -EFAULT;
+
+	return 0;
+}
+
+/* SET_QUEUE_INFO ioctl() */
+static int snd_seq_ioctl_set_queue_info(struct snd_seq_client *client,
+					void __user *arg)
+{
+	struct snd_seq_queue_info info;
+	struct snd_seq_queue *q;
+
+	if (copy_from_user(&info, arg, sizeof(info)))
+		return -EFAULT;
+
+	if (info.owner != client->number)
+		return -EINVAL;
+
+	/* change owner/locked permission */
+	if (snd_seq_queue_check_access(info.queue, client->number)) {
+		if (snd_seq_queue_set_owner(info.queue, client->number, info.locked) < 0)
+			return -EPERM;
+		if (info.locked)
+			snd_seq_queue_use(info.queue, client->number, 1);
+	} else {
+		return -EPERM;
+	}	
+
+	q = queueptr(info.queue);
+	if (! q)
+		return -EINVAL;
+	if (q->owner != client->number) {
+		queuefree(q);
+		return -EPERM;
+	}
+	strlcpy(q->name, info.name, sizeof(q->name));
+	queuefree(q);
+
+	return 0;
+}
+
+/* GET_NAMED_QUEUE ioctl() */
+static int snd_seq_ioctl_get_named_queue(struct snd_seq_client *client, void __user *arg)
+{
+	struct snd_seq_queue_info info;
+	struct snd_seq_queue *q;
+
+	if (copy_from_user(&info, arg, sizeof(info)))
+		return -EFAULT;
+
+	q = snd_seq_queue_find_name(info.name);
+	if (q == NULL)
+		return -EINVAL;
+	info.queue = q->queue;
+	info.owner = q->owner;
+	info.locked = q->locked;
+	queuefree(q);
+
+	if (copy_to_user(arg, &info, sizeof(info)))
+		return -EFAULT;
+
+	return 0;
+}
+
+/* GET_QUEUE_STATUS ioctl() */
+static int snd_seq_ioctl_get_queue_status(struct snd_seq_client *client,
+					  void __user *arg)
+{
+	struct snd_seq_queue_status status;
+	struct snd_seq_queue *queue;
+	struct snd_seq_timer *tmr;
+
+	if (copy_from_user(&status, arg, sizeof(status)))
+		return -EFAULT;
+
+	queue = queueptr(status.queue);
+	if (queue == NULL)
+		return -EINVAL;
+	memset(&status, 0, sizeof(status));
+	status.queue = queue->queue;
+	
+	tmr = queue->timer;
+	status.events = queue->tickq->cells + queue->timeq->cells;
+
+	status.time = snd_seq_timer_get_cur_time(tmr);
+	status.tick = snd_seq_timer_get_cur_tick(tmr);
+
+	status.running = tmr->running;
+
+	status.flags = queue->flags;
+	queuefree(queue);
+
+	if (copy_to_user(arg, &status, sizeof(status)))
+		return -EFAULT;
+	return 0;
+}
+
+
+/* GET_QUEUE_TEMPO ioctl() */
+static int snd_seq_ioctl_get_queue_tempo(struct snd_seq_client *client,
+					 void __user *arg)
+{
+	struct snd_seq_queue_tempo tempo;
+	struct snd_seq_queue *queue;
+	struct snd_seq_timer *tmr;
+
+	if (copy_from_user(&tempo, arg, sizeof(tempo)))
+		return -EFAULT;
+
+	queue = queueptr(tempo.queue);
+	if (queue == NULL)
+		return -EINVAL;
+	memset(&tempo, 0, sizeof(tempo));
+	tempo.queue = queue->queue;
+	
+	tmr = queue->timer;
+
+	tempo.tempo = tmr->tempo;
+	tempo.ppq = tmr->ppq;
+	tempo.skew_value = tmr->skew;
+	tempo.skew_base = tmr->skew_base;
+	queuefree(queue);
+
+	if (copy_to_user(arg, &tempo, sizeof(tempo)))
+		return -EFAULT;
+	return 0;
+}
+
+
+/* SET_QUEUE_TEMPO ioctl() */
+int snd_seq_set_queue_tempo(int client, struct snd_seq_queue_tempo *tempo)
+{
+	if (!snd_seq_queue_check_access(tempo->queue, client))
+		return -EPERM;
+	return snd_seq_queue_timer_set_tempo(tempo->queue, client, tempo);
+}
+
+static int snd_seq_ioctl_set_queue_tempo(struct snd_seq_client *client,
+					 void __user *arg)
+{
+	int result;
+	struct snd_seq_queue_tempo tempo;
+
+	if (copy_from_user(&tempo, arg, sizeof(tempo)))
+		return -EFAULT;
+
+	result = snd_seq_set_queue_tempo(client->number, &tempo);
+	return result < 0 ? result : 0;
+}
+
+
+/* GET_QUEUE_TIMER ioctl() */
+static int snd_seq_ioctl_get_queue_timer(struct snd_seq_client *client,
+					 void __user *arg)
+{
+	struct snd_seq_queue_timer timer;
+	struct snd_seq_queue *queue;
+	struct snd_seq_timer *tmr;
+
+	if (copy_from_user(&timer, arg, sizeof(timer)))
+		return -EFAULT;
+
+	queue = queueptr(timer.queue);
+	if (queue == NULL)
+		return -EINVAL;
+
+	if (down_interruptible(&queue->timer_mutex)) {
+		queuefree(queue);
+		return -ERESTARTSYS;
+	}
+	tmr = queue->timer;
+	memset(&timer, 0, sizeof(timer));
+	timer.queue = queue->queue;
+
+	timer.type = tmr->type;
+	if (tmr->type == SNDRV_SEQ_TIMER_ALSA) {
+		timer.u.alsa.id = tmr->alsa_id;
+		timer.u.alsa.resolution = tmr->preferred_resolution;
+	}
+	up(&queue->timer_mutex);
+	queuefree(queue);
+	
+	if (copy_to_user(arg, &timer, sizeof(timer)))
+		return -EFAULT;
+	return 0;
+}
+
+
+/* SET_QUEUE_TIMER ioctl() */
+static int snd_seq_ioctl_set_queue_timer(struct snd_seq_client *client,
+					 void __user *arg)
+{
+	int result = 0;
+	struct snd_seq_queue_timer timer;
+
+	if (copy_from_user(&timer, arg, sizeof(timer)))
+		return -EFAULT;
+
+	if (timer.type != SNDRV_SEQ_TIMER_ALSA)
+		return -EINVAL;
+
+	if (snd_seq_queue_check_access(timer.queue, client->number)) {
+		struct snd_seq_queue *q;
+		struct snd_seq_timer *tmr;
+
+		q = queueptr(timer.queue);
+		if (q == NULL)
+			return -ENXIO;
+		if (down_interruptible(&q->timer_mutex)) {
+			queuefree(q);
+			return -ERESTARTSYS;
+		}
+		tmr = q->timer;
+		snd_seq_queue_timer_close(timer.queue);
+		tmr->type = timer.type;
+		if (tmr->type == SNDRV_SEQ_TIMER_ALSA) {
+			tmr->alsa_id = timer.u.alsa.id;
+			tmr->preferred_resolution = timer.u.alsa.resolution;
+		}
+		result = snd_seq_queue_timer_open(timer.queue);
+		up(&q->timer_mutex);
+		queuefree(q);
+	} else {
+		return -EPERM;
+	}	
+
+	return result;
+}
+
+
+/* GET_QUEUE_CLIENT ioctl() */
+static int snd_seq_ioctl_get_queue_client(struct snd_seq_client *client,
+					  void __user *arg)
+{
+	struct snd_seq_queue_client info;
+	int used;
+
+	if (copy_from_user(&info, arg, sizeof(info)))
+		return -EFAULT;
+
+	used = snd_seq_queue_is_used(info.queue, client->number);
+	if (used < 0)
+		return -EINVAL;
+	info.used = used;
+	info.client = client->number;
+
+	if (copy_to_user(arg, &info, sizeof(info)))
+		return -EFAULT;
+	return 0;
+}
+
+
+/* SET_QUEUE_CLIENT ioctl() */
+static int snd_seq_ioctl_set_queue_client(struct snd_seq_client *client,
+					  void __user *arg)
+{
+	int err;
+	struct snd_seq_queue_client info;
+
+	if (copy_from_user(&info, arg, sizeof(info)))
+		return -EFAULT;
+
+	if (info.used >= 0) {
+		err = snd_seq_queue_use(info.queue, client->number, info.used);
+		if (err < 0)
+			return err;
+	}
+
+	return snd_seq_ioctl_get_queue_client(client, arg);
+}
+
+
+/* GET_CLIENT_POOL ioctl() */
+static int snd_seq_ioctl_get_client_pool(struct snd_seq_client *client,
+					 void __user *arg)
+{
+	struct snd_seq_client_pool info;
+	struct snd_seq_client *cptr;
+
+	if (copy_from_user(&info, arg, sizeof(info)))
+		return -EFAULT;
+
+	cptr = snd_seq_client_use_ptr(info.client);
+	if (cptr == NULL)
+		return -ENOENT;
+	memset(&info, 0, sizeof(info));
+	info.output_pool = cptr->pool->size;
+	info.output_room = cptr->pool->room;
+	info.output_free = info.output_pool;
+	if (cptr->pool)
+		info.output_free = snd_seq_unused_cells(cptr->pool);
+	if (cptr->type == USER_CLIENT) {
+		info.input_pool = cptr->data.user.fifo_pool_size;
+		info.input_free = info.input_pool;
+		if (cptr->data.user.fifo)
+			info.input_free = snd_seq_unused_cells(cptr->data.user.fifo->pool);
+	} else {
+		info.input_pool = 0;
+		info.input_free = 0;
+	}
+	snd_seq_client_unlock(cptr);
+	
+	if (copy_to_user(arg, &info, sizeof(info)))
+		return -EFAULT;
+	return 0;
+}
+
+/* SET_CLIENT_POOL ioctl() */
+static int snd_seq_ioctl_set_client_pool(struct snd_seq_client *client,
+					 void __user *arg)
+{
+	struct snd_seq_client_pool info;
+	int rc;
+
+	if (copy_from_user(&info, arg, sizeof(info)))
+		return -EFAULT;
+
+	if (client->number != info.client)
+		return -EINVAL; /* can't change other clients */
+
+	if (info.output_pool >= 1 && info.output_pool <= SNDRV_SEQ_MAX_EVENTS &&
+	    (! snd_seq_write_pool_allocated(client) ||
+	     info.output_pool != client->pool->size)) {
+		if (snd_seq_write_pool_allocated(client)) {
+			/* remove all existing cells */
+			snd_seq_queue_client_leave_cells(client->number);
+			snd_seq_pool_done(client->pool);
+		}
+		client->pool->size = info.output_pool;
+		rc = snd_seq_pool_init(client->pool);
+		if (rc < 0)
+			return rc;
+	}
+	if (client->type == USER_CLIENT && client->data.user.fifo != NULL &&
+	    info.input_pool >= 1 &&
+	    info.input_pool <= SNDRV_SEQ_MAX_CLIENT_EVENTS &&
+	    info.input_pool != client->data.user.fifo_pool_size) {
+		/* change pool size */
+		rc = snd_seq_fifo_resize(client->data.user.fifo, info.input_pool);
+		if (rc < 0)
+			return rc;
+		client->data.user.fifo_pool_size = info.input_pool;
+	}
+	if (info.output_room >= 1 &&
+	    info.output_room <= client->pool->size) {
+		client->pool->room  = info.output_room;
+	}
+
+	return snd_seq_ioctl_get_client_pool(client, arg);
+}
+
+
+/* REMOVE_EVENTS ioctl() */
+static int snd_seq_ioctl_remove_events(struct snd_seq_client *client,
+				       void __user *arg)
+{
+	struct snd_seq_remove_events info;
+
+	if (copy_from_user(&info, arg, sizeof(info)))
+		return -EFAULT;
+
+	/*
+	 * Input mostly not implemented XXX.
+	 */
+	if (info.remove_mode & SNDRV_SEQ_REMOVE_INPUT) {
+		/*
+		 * No restrictions so for a user client we can clear
+		 * the whole fifo
+		 */
+		if (client->type == USER_CLIENT)
+			snd_seq_fifo_clear(client->data.user.fifo);
+	}
+
+	if (info.remove_mode & SNDRV_SEQ_REMOVE_OUTPUT)
+		snd_seq_queue_remove_cells(client->number, &info);
+
+	return 0;
+}
+
+
+/*
+ * get subscription info
+ */
+static int snd_seq_ioctl_get_subscription(struct snd_seq_client *client,
+					  void __user *arg)
+{
+	int result;
+	struct snd_seq_client *sender = NULL;
+	struct snd_seq_client_port *sport = NULL;
+	struct snd_seq_port_subscribe subs;
+	struct snd_seq_subscribers *p;
+
+	if (copy_from_user(&subs, arg, sizeof(subs)))
+		return -EFAULT;
+
+	result = -EINVAL;
+	if ((sender = snd_seq_client_use_ptr(subs.sender.client)) == NULL)
+		goto __end;
+	if ((sport = snd_seq_port_use_ptr(sender, subs.sender.port)) == NULL)
+		goto __end;
+	p = snd_seq_port_get_subscription(&sport->c_src, &subs.dest);
+	if (p) {
+		result = 0;
+		subs = p->info;
+	} else
+		result = -ENOENT;
+
+      __end:
+      	if (sport)
+		snd_seq_port_unlock(sport);
+	if (sender)
+		snd_seq_client_unlock(sender);
+	if (result >= 0) {
+		if (copy_to_user(arg, &subs, sizeof(subs)))
+			return -EFAULT;
+	}
+	return result;
+}
+
+
+/*
+ * get subscription info - check only its presence
+ */
+static int snd_seq_ioctl_query_subs(struct snd_seq_client *client,
+				    void __user *arg)
+{
+	int result = -ENXIO;
+	struct snd_seq_client *cptr = NULL;
+	struct snd_seq_client_port *port = NULL;
+	struct snd_seq_query_subs subs;
+	struct snd_seq_port_subs_info *group;
+	struct list_head *p;
+	int i;
+
+	if (copy_from_user(&subs, arg, sizeof(subs)))
+		return -EFAULT;
+
+	if ((cptr = snd_seq_client_use_ptr(subs.root.client)) == NULL)
+		goto __end;
+	if ((port = snd_seq_port_use_ptr(cptr, subs.root.port)) == NULL)
+		goto __end;
+
+	switch (subs.type) {
+	case SNDRV_SEQ_QUERY_SUBS_READ:
+		group = &port->c_src;
+		break;
+	case SNDRV_SEQ_QUERY_SUBS_WRITE:
+		group = &port->c_dest;
+		break;
+	default:
+		goto __end;
+	}
+
+	down_read(&group->list_mutex);
+	/* search for the subscriber */
+	subs.num_subs = group->count;
+	i = 0;
+	result = -ENOENT;
+	list_for_each(p, &group->list_head) {
+		if (i++ == subs.index) {
+			/* found! */
+			struct snd_seq_subscribers *s;
+			if (subs.type == SNDRV_SEQ_QUERY_SUBS_READ) {
+				s = list_entry(p, struct snd_seq_subscribers, src_list);
+				subs.addr = s->info.dest;
+			} else {
+				s = list_entry(p, struct snd_seq_subscribers, dest_list);
+				subs.addr = s->info.sender;
+			}
+			subs.flags = s->info.flags;
+			subs.queue = s->info.queue;
+			result = 0;
+			break;
+		}
+	}
+	up_read(&group->list_mutex);
+
+      __end:
+   	if (port)
+		snd_seq_port_unlock(port);
+	if (cptr)
+		snd_seq_client_unlock(cptr);
+	if (result >= 0) {
+		if (copy_to_user(arg, &subs, sizeof(subs)))
+			return -EFAULT;
+	}
+	return result;
+}
+
+
+/*
+ * query next client
+ */
+static int snd_seq_ioctl_query_next_client(struct snd_seq_client *client,
+					   void __user *arg)
+{
+	struct snd_seq_client *cptr = NULL;
+	struct snd_seq_client_info info;
+
+	if (copy_from_user(&info, arg, sizeof(info)))
+		return -EFAULT;
+
+	/* search for next client */
+	info.client++;
+	if (info.client < 0)
+		info.client = 0;
+	for (; info.client < SNDRV_SEQ_MAX_CLIENTS; info.client++) {
+		cptr = snd_seq_client_use_ptr(info.client);
+		if (cptr)
+			break; /* found */
+	}
+	if (cptr == NULL)
+		return -ENOENT;
+
+	get_client_info(cptr, &info);
+	snd_seq_client_unlock(cptr);
+
+	if (copy_to_user(arg, &info, sizeof(info)))
+		return -EFAULT;
+	return 0;
+}
+
+/* 
+ * query next port
+ */
+static int snd_seq_ioctl_query_next_port(struct snd_seq_client *client,
+					 void __user *arg)
+{
+	struct snd_seq_client *cptr;
+	struct snd_seq_client_port *port = NULL;
+	struct snd_seq_port_info info;
+
+	if (copy_from_user(&info, arg, sizeof(info)))
+		return -EFAULT;
+	cptr = snd_seq_client_use_ptr(info.addr.client);
+	if (cptr == NULL)
+		return -ENXIO;
+
+	/* search for next port */
+	info.addr.port++;
+	port = snd_seq_port_query_nearest(cptr, &info);
+	if (port == NULL) {
+		snd_seq_client_unlock(cptr);
+		return -ENOENT;
+	}
+
+	/* get port info */
+	info.addr = port->addr;
+	snd_seq_get_port_info(port, &info);
+	snd_seq_port_unlock(port);
+	snd_seq_client_unlock(cptr);
+
+	if (copy_to_user(arg, &info, sizeof(info)))
+		return -EFAULT;
+	return 0;
+}
+
+/* -------------------------------------------------------- */
+
+static struct seq_ioctl_table {
+	unsigned int cmd;
+	int (*func)(struct snd_seq_client *client, void __user * arg);
+} ioctl_tables[] = {
+	{ SNDRV_SEQ_IOCTL_SYSTEM_INFO, snd_seq_ioctl_system_info },
+	{ SNDRV_SEQ_IOCTL_RUNNING_MODE, snd_seq_ioctl_running_mode },
+	{ SNDRV_SEQ_IOCTL_GET_CLIENT_INFO, snd_seq_ioctl_get_client_info },
+	{ SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, snd_seq_ioctl_set_client_info },
+	{ SNDRV_SEQ_IOCTL_CREATE_PORT, snd_seq_ioctl_create_port },
+	{ SNDRV_SEQ_IOCTL_DELETE_PORT, snd_seq_ioctl_delete_port },
+	{ SNDRV_SEQ_IOCTL_GET_PORT_INFO, snd_seq_ioctl_get_port_info },
+	{ SNDRV_SEQ_IOCTL_SET_PORT_INFO, snd_seq_ioctl_set_port_info },
+	{ SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, snd_seq_ioctl_subscribe_port },
+	{ SNDRV_SEQ_IOCTL_UNSUBSCRIBE_PORT, snd_seq_ioctl_unsubscribe_port },
+	{ SNDRV_SEQ_IOCTL_CREATE_QUEUE, snd_seq_ioctl_create_queue },
+	{ SNDRV_SEQ_IOCTL_DELETE_QUEUE, snd_seq_ioctl_delete_queue },
+	{ SNDRV_SEQ_IOCTL_GET_QUEUE_INFO, snd_seq_ioctl_get_queue_info },
+	{ SNDRV_SEQ_IOCTL_SET_QUEUE_INFO, snd_seq_ioctl_set_queue_info },
+	{ SNDRV_SEQ_IOCTL_GET_NAMED_QUEUE, snd_seq_ioctl_get_named_queue },
+	{ SNDRV_SEQ_IOCTL_GET_QUEUE_STATUS, snd_seq_ioctl_get_queue_status },
+	{ SNDRV_SEQ_IOCTL_GET_QUEUE_TEMPO, snd_seq_ioctl_get_queue_tempo },
+	{ SNDRV_SEQ_IOCTL_SET_QUEUE_TEMPO, snd_seq_ioctl_set_queue_tempo },
+	{ SNDRV_SEQ_IOCTL_GET_QUEUE_TIMER, snd_seq_ioctl_get_queue_timer },
+	{ SNDRV_SEQ_IOCTL_SET_QUEUE_TIMER, snd_seq_ioctl_set_queue_timer },
+	{ SNDRV_SEQ_IOCTL_GET_QUEUE_CLIENT, snd_seq_ioctl_get_queue_client },
+	{ SNDRV_SEQ_IOCTL_SET_QUEUE_CLIENT, snd_seq_ioctl_set_queue_client },
+	{ SNDRV_SEQ_IOCTL_GET_CLIENT_POOL, snd_seq_ioctl_get_client_pool },
+	{ SNDRV_SEQ_IOCTL_SET_CLIENT_POOL, snd_seq_ioctl_set_client_pool },
+	{ SNDRV_SEQ_IOCTL_GET_SUBSCRIPTION, snd_seq_ioctl_get_subscription },
+	{ SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT, snd_seq_ioctl_query_next_client },
+	{ SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT, snd_seq_ioctl_query_next_port },
+	{ SNDRV_SEQ_IOCTL_REMOVE_EVENTS, snd_seq_ioctl_remove_events },
+	{ SNDRV_SEQ_IOCTL_QUERY_SUBS, snd_seq_ioctl_query_subs },
+	{ 0, NULL },
+};
+
+static int snd_seq_do_ioctl(struct snd_seq_client *client, unsigned int cmd,
+			    void __user *arg)
+{
+	struct seq_ioctl_table *p;
+
+	switch (cmd) {
+	case SNDRV_SEQ_IOCTL_PVERSION:
+		/* return sequencer version number */
+		return put_user(SNDRV_SEQ_VERSION, (int __user *)arg) ? -EFAULT : 0;
+	case SNDRV_SEQ_IOCTL_CLIENT_ID:
+		/* return the id of this client */
+		return put_user(client->number, (int __user *)arg) ? -EFAULT : 0;
+	}
+
+	if (! arg)
+		return -EFAULT;
+	for (p = ioctl_tables; p->cmd; p++) {
+		if (p->cmd == cmd)
+			return p->func(client, arg);
+	}
+	snd_printd("seq unknown ioctl() 0x%x (type='%c', number=0x%2x)\n",
+		   cmd, _IOC_TYPE(cmd), _IOC_NR(cmd));
+	return -ENOTTY;
+}
+
+
+static long snd_seq_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct snd_seq_client *client = file->private_data;
+
+	snd_assert(client != NULL, return -ENXIO);
+		
+	return snd_seq_do_ioctl(client, cmd, (void __user *) arg);
+}
+
+#ifdef CONFIG_COMPAT
+#include "seq_compat.c"
+#else
+#define snd_seq_ioctl_compat	NULL
+#endif
+
+/* -------------------------------------------------------- */
+
+
+/* exported to kernel modules */
+int snd_seq_create_kernel_client(struct snd_card *card, int client_index,
+				 struct snd_seq_client_callback *callback)
+{
+	struct snd_seq_client *client;
+
+	snd_assert(! in_interrupt(), return -EBUSY);
+
+	if (callback == NULL)
+		return -EINVAL;
+	if (card && client_index > 7)
+		return -EINVAL;
+	if (card == NULL && client_index > 63)
+		return -EINVAL;
+	if (card)
+		client_index += 64 + (card->number << 3);
+
+	if (down_interruptible(&register_mutex))
+		return -ERESTARTSYS;
+	/* empty write queue as default */
+	client = seq_create_client1(client_index, 0);
+	if (client == NULL) {
+		up(&register_mutex);
+		return -EBUSY;	/* failure code */
+	}
+	usage_alloc(&client_usage, 1);
+
+	client->accept_input = callback->allow_output;
+	client->accept_output = callback->allow_input;
+		
+	/* fill client data */
+	client->data.kernel.card = card;
+	client->data.kernel.private_data = callback->private_data;
+	sprintf(client->name, "Client-%d", client->number);
+
+	client->type = KERNEL_CLIENT;
+	up(&register_mutex);
+
+	/* make others aware this new client */
+	snd_seq_system_client_ev_client_start(client->number);
+	
+	/* return client number to caller */
+	return client->number;
+}
+
+/* exported to kernel modules */
+int snd_seq_delete_kernel_client(int client)
+{
+	struct snd_seq_client *ptr;
+
+	snd_assert(! in_interrupt(), return -EBUSY);
+
+	ptr = clientptr(client);
+	if (ptr == NULL)
+		return -EINVAL;
+
+	seq_free_client(ptr);
+	kfree(ptr);
+	return 0;
+}
+
+
+/* skeleton to enqueue event, called from snd_seq_kernel_client_enqueue
+ * and snd_seq_kernel_client_enqueue_blocking
+ */
+static int kernel_client_enqueue(int client, struct snd_seq_event *ev,
+				 struct file *file, int blocking,
+				 int atomic, int hop)
+{
+	struct snd_seq_client *cptr;
+	int result;
+
+	snd_assert(ev != NULL, return -EINVAL);
+
+	if (ev->type == SNDRV_SEQ_EVENT_NONE)
+		return 0; /* ignore this */
+	if (ev->type == SNDRV_SEQ_EVENT_KERNEL_ERROR)
+		return -EINVAL; /* quoted events can't be enqueued */
+
+	/* fill in client number */
+	ev->source.client = client;
+
+	if (check_event_type_and_length(ev))
+		return -EINVAL;
+
+	cptr = snd_seq_client_use_ptr(client);
+	if (cptr == NULL)
+		return -EINVAL;
+	
+	if (! cptr->accept_output)
+		result = -EPERM;
+	else /* send it */
+		result = snd_seq_client_enqueue_event(cptr, ev, file, blocking, atomic, hop);
+
+	snd_seq_client_unlock(cptr);
+	return result;
+}
+
+/*
+ * exported, called by kernel clients to enqueue events (w/o blocking)
+ *
+ * RETURN VALUE: zero if succeed, negative if error
+ */
+int snd_seq_kernel_client_enqueue(int client, struct snd_seq_event * ev,
+				  int atomic, int hop)
+{
+	return kernel_client_enqueue(client, ev, NULL, 0, atomic, hop);
+}
+
+/*
+ * exported, called by kernel clients to enqueue events (with blocking)
+ *
+ * RETURN VALUE: zero if succeed, negative if error
+ */
+int snd_seq_kernel_client_enqueue_blocking(int client, struct snd_seq_event * ev,
+					   struct file *file,
+					   int atomic, int hop)
+{
+	return kernel_client_enqueue(client, ev, file, 1, atomic, hop);
+}
+
+
+/* 
+ * exported, called by kernel clients to dispatch events directly to other
+ * clients, bypassing the queues.  Event time-stamp will be updated.
+ *
+ * RETURN VALUE: negative = delivery failed,
+ *		 zero, or positive: the number of delivered events
+ */
+int snd_seq_kernel_client_dispatch(int client, struct snd_seq_event * ev,
+				   int atomic, int hop)
+{
+	struct snd_seq_client *cptr;
+	int result;
+
+	snd_assert(ev != NULL, return -EINVAL);
+
+	/* fill in client number */
+	ev->queue = SNDRV_SEQ_QUEUE_DIRECT;
+	ev->source.client = client;
+
+	if (check_event_type_and_length(ev))
+		return -EINVAL;
+
+	cptr = snd_seq_client_use_ptr(client);
+	if (cptr == NULL)
+		return -EINVAL;
+
+	if (!cptr->accept_output)
+		result = -EPERM;
+	else
+		result = snd_seq_deliver_event(cptr, ev, atomic, hop);
+
+	snd_seq_client_unlock(cptr);
+	return result;
+}
+
+
+/*
+ * exported, called by kernel clients to perform same functions as with
+ * userland ioctl() 
+ */
+int snd_seq_kernel_client_ctl(int clientid, unsigned int cmd, void *arg)
+{
+	struct snd_seq_client *client;
+	mm_segment_t fs;
+	int result;
+
+	client = clientptr(clientid);
+	if (client == NULL)
+		return -ENXIO;
+	fs = snd_enter_user();
+	result = snd_seq_do_ioctl(client, cmd, (void __user *)arg);
+	snd_leave_user(fs);
+	return result;
+}
+
+
+/* exported (for OSS emulator) */
+int snd_seq_kernel_client_write_poll(int clientid, struct file *file, poll_table *wait)
+{
+	struct snd_seq_client *client;
+
+	client = clientptr(clientid);
+	if (client == NULL)
+		return -ENXIO;
+
+	if (! snd_seq_write_pool_allocated(client))
+		return 1;
+	if (snd_seq_pool_poll_wait(client->pool, file, wait))
+		return 1;
+	return 0;
+}
+
+/*---------------------------------------------------------------------------*/
+
+/*
+ *  /proc interface
+ */
+static void snd_seq_info_dump_subscribers(struct snd_info_buffer *buffer,
+					  struct snd_seq_port_subs_info *group,
+					  int is_src, char *msg)
+{
+	struct list_head *p;
+	struct snd_seq_subscribers *s;
+	int count = 0;
+
+	down_read(&group->list_mutex);
+	if (list_empty(&group->list_head)) {
+		up_read(&group->list_mutex);
+		return;
+	}
+	snd_iprintf(buffer, msg);
+	list_for_each(p, &group->list_head) {
+		if (is_src)
+			s = list_entry(p, struct snd_seq_subscribers, src_list);
+		else
+			s = list_entry(p, struct snd_seq_subscribers, dest_list);
+		if (count++)
+			snd_iprintf(buffer, ", ");
+		snd_iprintf(buffer, "%d:%d",
+			    is_src ? s->info.dest.client : s->info.sender.client,
+			    is_src ? s->info.dest.port : s->info.sender.port);
+		if (s->info.flags & SNDRV_SEQ_PORT_SUBS_TIMESTAMP)
+			snd_iprintf(buffer, "[%c:%d]", ((s->info.flags & SNDRV_SEQ_PORT_SUBS_TIME_REAL) ? 'r' : 't'), s->info.queue);
+		if (group->exclusive)
+			snd_iprintf(buffer, "[ex]");
+	}
+	up_read(&group->list_mutex);
+	snd_iprintf(buffer, "\n");
+}
+
+#define FLAG_PERM_RD(perm) ((perm) & SNDRV_SEQ_PORT_CAP_READ ? ((perm) & SNDRV_SEQ_PORT_CAP_SUBS_READ ? 'R' : 'r') : '-')
+#define FLAG_PERM_WR(perm) ((perm) & SNDRV_SEQ_PORT_CAP_WRITE ? ((perm) & SNDRV_SEQ_PORT_CAP_SUBS_WRITE ? 'W' : 'w') : '-')
+#define FLAG_PERM_EX(perm) ((perm) & SNDRV_SEQ_PORT_CAP_NO_EXPORT ? '-' : 'e')
+
+#define FLAG_PERM_DUPLEX(perm) ((perm) & SNDRV_SEQ_PORT_CAP_DUPLEX ? 'X' : '-')
+
+static void snd_seq_info_dump_ports(struct snd_info_buffer *buffer,
+				    struct snd_seq_client *client)
+{
+	struct list_head *l;
+
+	down(&client->ports_mutex);
+	list_for_each(l, &client->ports_list_head) {
+		struct snd_seq_client_port *p = list_entry(l, struct snd_seq_client_port, list);
+		snd_iprintf(buffer, "  Port %3d : \"%s\" (%c%c%c%c)\n",
+			    p->addr.port, p->name,
+			    FLAG_PERM_RD(p->capability),
+			    FLAG_PERM_WR(p->capability),
+			    FLAG_PERM_EX(p->capability),
+			    FLAG_PERM_DUPLEX(p->capability));
+		snd_seq_info_dump_subscribers(buffer, &p->c_src, 1, "    Connecting To: ");
+		snd_seq_info_dump_subscribers(buffer, &p->c_dest, 0, "    Connected From: ");
+	}
+	up(&client->ports_mutex);
+}
+
+
+void snd_seq_info_pool(struct snd_info_buffer *buffer,
+		       struct snd_seq_pool *pool, char *space);
+
+/* exported to seq_info.c */
+void snd_seq_info_clients_read(struct snd_info_entry *entry, 
+			       struct snd_info_buffer *buffer)
+{
+	int c;
+	struct snd_seq_client *client;
+
+	snd_iprintf(buffer, "Client info\n");
+	snd_iprintf(buffer, "  cur  clients : %d\n", client_usage.cur);
+	snd_iprintf(buffer, "  peak clients : %d\n", client_usage.peak);
+	snd_iprintf(buffer, "  max  clients : %d\n", SNDRV_SEQ_MAX_CLIENTS);
+	snd_iprintf(buffer, "\n");
+
+	/* list the client table */
+	for (c = 0; c < SNDRV_SEQ_MAX_CLIENTS; c++) {
+		client = snd_seq_client_use_ptr(c);
+		if (client == NULL)
+			continue;
+		if (client->type == NO_CLIENT) {
+			snd_seq_client_unlock(client);
+			continue;
+		}
+
+		snd_iprintf(buffer, "Client %3d : \"%s\" [%s]\n",
+			    c, client->name,
+			    client->type == USER_CLIENT ? "User" : "Kernel");
+		snd_seq_info_dump_ports(buffer, client);
+		if (snd_seq_write_pool_allocated(client)) {
+			snd_iprintf(buffer, "  Output pool :\n");
+			snd_seq_info_pool(buffer, client->pool, "    ");
+		}
+		if (client->type == USER_CLIENT && client->data.user.fifo &&
+		    client->data.user.fifo->pool) {
+			snd_iprintf(buffer, "  Input pool :\n");
+			snd_seq_info_pool(buffer, client->data.user.fifo->pool, "    ");
+		}
+		snd_seq_client_unlock(client);
+	}
+}
+
+
+/*---------------------------------------------------------------------------*/
+
+
+/*
+ *  REGISTRATION PART
+ */
+
+static struct file_operations snd_seq_f_ops =
+{
+#ifndef TARGET_OS2
+    .owner =	THIS_MODULE,
+#endif
+	.read =		snd_seq_read,
+	.write =	snd_seq_write,
+	.open =		snd_seq_open,
+	.release =	snd_seq_release,
+	.poll =		snd_seq_poll,
+        .ioctl =	snd_seq_ioctl,
+#ifndef TARGET_OS2
+        .compat_ioctl =	snd_seq_ioctl_compat,
+#endif
+};
+
+static struct snd_minor snd_seq_reg =
+{
+	.comment =	"sequencer",
+	.f_ops =	&snd_seq_f_ops,
+};
+
+
+/* 
+ * register sequencer device 
+ */
+int __init snd_sequencer_device_init(void)
+{
+	int err;
+
+	if (down_interruptible(&register_mutex))
+		return -ERESTARTSYS;
+
+	if ((err = snd_register_device(SNDRV_DEVICE_TYPE_SEQUENCER, NULL, 0, &snd_seq_reg, "seq")) < 0) {
+		up(&register_mutex);
+		return err;
+	}
+	
+	up(&register_mutex);
+
+	return 0;
+}
+
+
+
+/* 
+ * unregister sequencer device 
+ */
+void __exit snd_sequencer_device_done(void)
+{
+	snd_unregister_device(SNDRV_DEVICE_TYPE_SEQUENCER, NULL, 0);
+}
Index: /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_clientmgr.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_clientmgr.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_clientmgr.h	(revision 91)
@@ -0,0 +1,106 @@
+/*
+ *   ALSA sequencer Client Manager
+ *   Copyright (c) 1998-1999 by Frank van de Pol <fvdpol@coil.demon.nl>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+#ifndef __SND_SEQ_CLIENTMGR_H
+#define __SND_SEQ_CLIENTMGR_H
+
+#include <sound/seq_kernel.h>
+#include <linux/bitops.h>
+#include "seq_fifo.h"
+#include "seq_ports.h"
+#include "seq_lock.h"
+
+
+/* client manager */
+
+struct snd_seq_user_client {
+	struct file *file;	/* file struct of client */
+	/* ... */
+	
+	/* fifo */
+	struct snd_seq_fifo *fifo;	/* queue for incoming events */
+	int fifo_pool_size;
+};
+
+struct snd_seq_kernel_client {
+	struct snd_card *card;
+	/* pointer to client functions */
+	void *private_data;			/* private data for client */
+	/* ... */
+};
+
+
+struct snd_seq_client {
+	snd_seq_client_type_t type;
+	unsigned int accept_input: 1,
+		accept_output: 1;
+	char name[64];		/* client name */
+	int number;		/* client number */
+	unsigned int filter;	/* filter flags */
+	DECLARE_BITMAP(event_filter, 256);
+	snd_use_lock_t use_lock;
+	int event_lost;
+	/* ports */
+	int num_ports;		/* number of ports */
+	struct list_head ports_list_head;
+	rwlock_t ports_lock;
+	struct semaphore ports_mutex;
+	int convert32;		/* convert 32->64bit */
+
+	/* output pool */
+	struct snd_seq_pool *pool;		/* memory pool for this client */
+
+	union {
+		struct snd_seq_user_client user;
+		struct snd_seq_kernel_client kernel;
+	} data;
+};
+
+/* usage statistics */
+struct snd_seq_usage {
+	int cur;
+	int peak;
+};
+
+
+int client_init_data(void);
+int snd_sequencer_device_init(void);
+void snd_sequencer_device_done(void);
+
+/* get locked pointer to client */
+struct snd_seq_client *snd_seq_client_use_ptr(int clientid);
+
+/* unlock pointer to client */
+#define snd_seq_client_unlock(client) snd_use_lock_free(&(client)->use_lock)
+
+/* dispatch event to client(s) */
+int snd_seq_dispatch_event(struct snd_seq_event_cell *cell, int atomic, int hop);
+
+/* exported to other modules */
+int snd_seq_register_kernel_client(struct snd_seq_client_callback *callback, void *private_data);
+int snd_seq_unregister_kernel_client(int client);
+int snd_seq_kernel_client_enqueue(int client, struct snd_seq_event *ev, int atomic, int hop);
+int snd_seq_kernel_client_enqueue_blocking(int client, struct snd_seq_event * ev,
+					   struct file *file, int atomic, int hop);
+int snd_seq_kernel_client_write_poll(int clientid, struct file *file, poll_table *wait);
+int snd_seq_client_notify_subscription(int client, int port,
+				       struct snd_seq_port_subscribe *info, int evtype);
+
+#endif
Index: /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_device.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_device.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_device.c	(revision 91)
@@ -0,0 +1,579 @@
+/*
+ *  ALSA sequencer device management
+ *  Copyright (c) 1999 by 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
+ *
+ *
+ *----------------------------------------------------------------
+ *
+ * This device handler separates the card driver module from sequencer
+ * stuff (sequencer core, synth drivers, etc), so that user can avoid
+ * to spend unnecessary resources e.g. if he needs only listening to
+ * MP3s.
+ *
+ * The card (or lowlevel) driver creates a sequencer device entry
+ * via snd_seq_device_new().  This is an entry pointer to communicate
+ * with the sequencer device "driver", which is involved with the
+ * actual part to communicate with the sequencer core.
+ * Each sequencer device entry has an id string and the corresponding
+ * driver with the same id is loaded when required.  For example,
+ * lowlevel codes to access emu8000 chip on sbawe card are included in
+ * emu8000-synth module.  To activate this module, the hardware
+ * resources like i/o port are passed via snd_seq_device argument.
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/seq_device.h>
+#include <sound/seq_kernel.h>
+#include <sound/initval.h>
+#include <linux/kmod.h>
+#include <linux/slab.h>
+
+MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
+MODULE_DESCRIPTION("ALSA sequencer device management");
+MODULE_LICENSE("GPL");
+
+/* driver state */
+#define DRIVER_EMPTY		0
+#define DRIVER_LOADED		(1<<0)
+#define DRIVER_REQUESTED	(1<<1)
+#define DRIVER_LOCKED		(1<<2)
+
+struct ops_list {
+	char id[ID_LEN];	/* driver id */
+	int driver;		/* driver state */
+	int used;		/* reference counter */
+	int argsize;		/* argument size */
+
+	/* operators */
+	struct snd_seq_dev_ops ops;
+
+	/* registred devices */
+	struct list_head dev_list;	/* list of devices */
+	int num_devices;	/* number of associated devices */
+	int num_init_devices;	/* number of initialized devices */
+	struct semaphore reg_mutex;
+
+	struct list_head list;	/* next driver */
+};
+
+
+static LIST_HEAD(opslist);
+static int num_ops;
+static DECLARE_MUTEX(ops_mutex);
+static struct snd_info_entry *info_entry = NULL;
+
+/*
+ * prototypes
+ */
+static int snd_seq_device_free(struct snd_seq_device *dev);
+static int snd_seq_device_dev_free(struct snd_device *device);
+static int snd_seq_device_dev_register(struct snd_device *device);
+static int snd_seq_device_dev_disconnect(struct snd_device *device);
+static int snd_seq_device_dev_unregister(struct snd_device *device);
+
+static int init_device(struct snd_seq_device *dev, struct ops_list *ops);
+static int free_device(struct snd_seq_device *dev, struct ops_list *ops);
+static struct ops_list *find_driver(char *id, int create_if_empty);
+static struct ops_list *create_driver(char *id);
+static void unlock_driver(struct ops_list *ops);
+static void remove_drivers(void);
+
+/*
+ * show all drivers and their status
+ */
+
+static void snd_seq_device_info(struct snd_info_entry *entry,
+				struct snd_info_buffer *buffer)
+{
+	struct list_head *head;
+
+	down(&ops_mutex);
+	list_for_each(head, &opslist) {
+		struct ops_list *ops = list_entry(head, struct ops_list, list);
+		snd_iprintf(buffer, "snd-%s%s%s%s,%d\n",
+				ops->id,
+				ops->driver & DRIVER_LOADED ? ",loaded" : (ops->driver == DRIVER_EMPTY ? ",empty" : ""),
+				ops->driver & DRIVER_REQUESTED ? ",requested" : "",
+				ops->driver & DRIVER_LOCKED ? ",locked" : "",
+				ops->num_devices);
+	}
+	up(&ops_mutex);	
+}
+ 
+/*
+ * load all registered drivers (called from seq_clientmgr.c)
+ */
+
+#ifdef CONFIG_KMOD
+/* avoid auto-loading during module_init() */
+static int snd_seq_in_init;
+void snd_seq_autoload_lock(void)
+{
+	snd_seq_in_init++;
+}
+
+void snd_seq_autoload_unlock(void)
+{
+	snd_seq_in_init--;
+}
+#endif
+
+void snd_seq_device_load_drivers(void)
+{
+#ifdef CONFIG_KMOD
+	struct list_head *head;
+
+	/* Calling request_module during module_init()
+	 * may cause blocking.
+	 */
+	if (snd_seq_in_init)
+		return;
+
+	if (! current->fs->root)
+		return;
+
+	down(&ops_mutex);
+	list_for_each(head, &opslist) {
+		struct ops_list *ops = list_entry(head, struct ops_list, list);
+		if (! (ops->driver & DRIVER_LOADED) &&
+		    ! (ops->driver & DRIVER_REQUESTED)) {
+			ops->used++;
+			up(&ops_mutex);
+			ops->driver |= DRIVER_REQUESTED;
+			request_module("snd-%s", ops->id);
+			down(&ops_mutex);
+			ops->used--;
+		}
+	}
+	up(&ops_mutex);
+#endif
+}
+
+/*
+ * register a sequencer device
+ * card = card info (NULL allowed)
+ * device = device number (if any)
+ * id = id of driver
+ * result = return pointer (NULL allowed if unnecessary)
+ */
+int snd_seq_device_new(struct snd_card *card, int device, char *id, int argsize,
+		       struct snd_seq_device **result)
+{
+	struct snd_seq_device *dev;
+	struct ops_list *ops;
+	int err;
+	static struct snd_device_ops dops = {
+		.dev_free = snd_seq_device_dev_free,
+		.dev_register = snd_seq_device_dev_register,
+		.dev_disconnect = snd_seq_device_dev_disconnect,
+		.dev_unregister = snd_seq_device_dev_unregister
+	};
+
+	if (result)
+		*result = NULL;
+
+	snd_assert(id != NULL, return -EINVAL);
+
+	ops = find_driver(id, 1);
+	if (ops == NULL)
+		return -ENOMEM;
+
+	dev = (struct snd_seq_device *)kzalloc(sizeof(*dev)*2 + argsize, GFP_KERNEL);
+	if (dev == NULL) {
+		unlock_driver(ops);
+		return -ENOMEM;
+	}
+
+	/* set up device info */
+	dev->card = card;
+	dev->device = device;
+	strlcpy(dev->id, id, sizeof(dev->id));
+	dev->argsize = argsize;
+	dev->status = SNDRV_SEQ_DEVICE_FREE;
+
+	/* add this device to the list */
+	down(&ops->reg_mutex);
+	list_add_tail(&dev->list, &ops->dev_list);
+	ops->num_devices++;
+	up(&ops->reg_mutex);
+
+	unlock_driver(ops);
+	
+	if ((err = snd_device_new(card, SNDRV_DEV_SEQUENCER, dev, &dops)) < 0) {
+		snd_seq_device_free(dev);
+		return err;
+	}
+	
+	if (result)
+		*result = dev;
+
+	return 0;
+}
+
+/*
+ * free the existing device
+ */
+static int snd_seq_device_free(struct snd_seq_device *dev)
+{
+	struct ops_list *ops;
+
+	snd_assert(dev != NULL, return -EINVAL);
+
+	ops = find_driver(dev->id, 0);
+	if (ops == NULL)
+		return -ENXIO;
+
+	/* remove the device from the list */
+	down(&ops->reg_mutex);
+	list_del(&dev->list);
+	ops->num_devices--;
+	up(&ops->reg_mutex);
+
+	free_device(dev, ops);
+	if (dev->private_free)
+		dev->private_free(dev);
+	kfree(dev);
+
+	unlock_driver(ops);
+
+	return 0;
+}
+
+static int snd_seq_device_dev_free(struct snd_device *device)
+{
+	struct snd_seq_device *dev = device->device_data;
+	return snd_seq_device_free(dev);
+}
+
+/*
+ * register the device
+ */
+static int snd_seq_device_dev_register(struct snd_device *device)
+{
+	struct snd_seq_device *dev = device->device_data;
+	struct ops_list *ops;
+
+	ops = find_driver(dev->id, 0);
+	if (ops == NULL)
+		return -ENOENT;
+
+	/* initialize this device if the corresponding driver was
+	 * already loaded
+	 */
+	if (ops->driver & DRIVER_LOADED)
+		init_device(dev, ops);
+
+	unlock_driver(ops);
+	return 0;
+}
+
+/*
+ * disconnect the device
+ */
+static int snd_seq_device_dev_disconnect(struct snd_device *device)
+{
+	struct snd_seq_device *dev = device->device_data;
+	struct ops_list *ops;
+
+	ops = find_driver(dev->id, 0);
+	if (ops == NULL)
+		return -ENOENT;
+
+	free_device(dev, ops);
+
+	unlock_driver(ops);
+	return 0;
+}
+
+/*
+ * unregister the existing device
+ */
+static int snd_seq_device_dev_unregister(struct snd_device *device)
+{
+	struct snd_seq_device *dev = device->device_data;
+	return snd_seq_device_free(dev);
+}
+
+/*
+ * register device driver
+ * id = driver id
+ * entry = driver operators - duplicated to each instance
+ */
+int snd_seq_device_register_driver(char *id, struct snd_seq_dev_ops *entry,
+				   int argsize)
+{
+	struct list_head *head;
+	struct ops_list *ops;
+
+	if (id == NULL || entry == NULL ||
+	    entry->init_device == NULL || entry->free_device == NULL)
+		return -EINVAL;
+
+	snd_seq_autoload_lock();
+	ops = find_driver(id, 1);
+	if (ops == NULL) {
+		snd_seq_autoload_unlock();
+		return -ENOMEM;
+	}
+	if (ops->driver & DRIVER_LOADED) {
+		snd_printk(KERN_WARNING "driver_register: driver '%s' already exists\n", id);
+		unlock_driver(ops);
+		snd_seq_autoload_unlock();
+		return -EBUSY;
+	}
+
+	down(&ops->reg_mutex);
+	/* copy driver operators */
+	ops->ops = *entry;
+	ops->driver |= DRIVER_LOADED;
+	ops->argsize = argsize;
+
+	/* initialize existing devices if necessary */
+	list_for_each(head, &ops->dev_list) {
+		struct snd_seq_device *dev = list_entry(head, struct snd_seq_device, list);
+		init_device(dev, ops);
+	}
+	up(&ops->reg_mutex);
+
+	unlock_driver(ops);
+	snd_seq_autoload_unlock();
+
+	return 0;
+}
+
+
+/*
+ * create driver record
+ */
+static struct ops_list * create_driver(char *id)
+{
+	struct ops_list *ops;
+
+	ops = kmalloc(sizeof(*ops), GFP_KERNEL);
+	if (ops == NULL)
+		return ops;
+	memset(ops, 0, sizeof(*ops));
+
+	/* set up driver entry */
+	strlcpy(ops->id, id, sizeof(ops->id));
+	init_MUTEX(&ops->reg_mutex);
+	ops->driver = DRIVER_EMPTY;
+	INIT_LIST_HEAD(&ops->dev_list);
+	/* lock this instance */
+	ops->used = 1;
+
+	/* register driver entry */
+	down(&ops_mutex);
+	list_add_tail(&ops->list, &opslist);
+	num_ops++;
+	up(&ops_mutex);
+
+	return ops;
+}
+
+
+/*
+ * unregister the specified driver
+ */
+int snd_seq_device_unregister_driver(char *id)
+{
+	struct list_head *head;
+	struct ops_list *ops;
+
+	ops = find_driver(id, 0);
+	if (ops == NULL)
+		return -ENXIO;
+	if (! (ops->driver & DRIVER_LOADED) ||
+	    (ops->driver & DRIVER_LOCKED)) {
+		snd_printk(KERN_ERR "driver_unregister: cannot unload driver '%s': status=%x\n",
+			   id, ops->driver);
+		unlock_driver(ops);
+		return -EBUSY;
+	}
+
+	/* close and release all devices associated with this driver */
+	down(&ops->reg_mutex);
+	ops->driver |= DRIVER_LOCKED; /* do not remove this driver recursively */
+	list_for_each(head, &ops->dev_list) {
+		struct snd_seq_device *dev = list_entry(head, struct snd_seq_device, list);
+		free_device(dev, ops);
+	}
+
+	ops->driver = 0;
+	if (ops->num_init_devices > 0)
+		snd_printk(KERN_ERR "free_driver: init_devices > 0!! (%d)\n",
+			   ops->num_init_devices);
+	up(&ops->reg_mutex);
+
+	unlock_driver(ops);
+
+	/* remove empty driver entries */
+	remove_drivers();
+
+	return 0;
+}
+
+
+/*
+ * remove empty driver entries
+ */
+static void remove_drivers(void)
+{
+	struct list_head *head;
+
+	down(&ops_mutex);
+	head = opslist.next;
+	while (head != &opslist) {
+		struct ops_list *ops = list_entry(head, struct ops_list, list);
+		if (! (ops->driver & DRIVER_LOADED) &&
+		    ops->used == 0 && ops->num_devices == 0) {
+			head = head->next;
+			list_del(&ops->list);
+			kfree(ops);
+			num_ops--;
+		} else
+			head = head->next;
+	}
+	up(&ops_mutex);
+}
+
+/*
+ * initialize the device - call init_device operator
+ */
+static int init_device(struct snd_seq_device *dev, struct ops_list *ops)
+{
+	if (! (ops->driver & DRIVER_LOADED))
+		return 0; /* driver is not loaded yet */
+	if (dev->status != SNDRV_SEQ_DEVICE_FREE)
+		return 0; /* already initialized */
+	if (ops->argsize != dev->argsize) {
+		snd_printk(KERN_ERR "incompatible device '%s' for plug-in '%s' (%d %d)\n",
+			   dev->name, ops->id, ops->argsize, dev->argsize);
+		return -EINVAL;
+	}
+	if (ops->ops.init_device(dev) >= 0) {
+		dev->status = SNDRV_SEQ_DEVICE_REGISTERED;
+		ops->num_init_devices++;
+	} else {
+		snd_printk(KERN_ERR "init_device failed: %s: %s\n",
+			   dev->name, dev->id);
+	}
+
+	return 0;
+}
+
+/*
+ * release the device - call free_device operator
+ */
+static int free_device(struct snd_seq_device *dev, struct ops_list *ops)
+{
+	int result;
+
+	if (! (ops->driver & DRIVER_LOADED))
+		return 0; /* driver is not loaded yet */
+	if (dev->status != SNDRV_SEQ_DEVICE_REGISTERED)
+		return 0; /* not registered */
+	if (ops->argsize != dev->argsize) {
+		snd_printk(KERN_ERR "incompatible device '%s' for plug-in '%s' (%d %d)\n",
+			   dev->name, ops->id, ops->argsize, dev->argsize);
+		return -EINVAL;
+	}
+	if ((result = ops->ops.free_device(dev)) >= 0 || result == -ENXIO) {
+		dev->status = SNDRV_SEQ_DEVICE_FREE;
+		dev->driver_data = NULL;
+		ops->num_init_devices--;
+	} else {
+		snd_printk(KERN_ERR "free_device failed: %s: %s\n",
+			   dev->name, dev->id);
+	}
+
+	return 0;
+}
+
+/*
+ * find the matching driver with given id
+ */
+static struct ops_list * find_driver(char *id, int create_if_empty)
+{
+	struct list_head *head;
+
+	down(&ops_mutex);
+	list_for_each(head, &opslist) {
+		struct ops_list *ops = list_entry(head, struct ops_list, list);
+		if (strcmp(ops->id, id) == 0) {
+			ops->used++;
+			up(&ops_mutex);
+			return ops;
+		}
+	}
+	up(&ops_mutex);
+	if (create_if_empty)
+		return create_driver(id);
+	return NULL;
+}
+
+static void unlock_driver(struct ops_list *ops)
+{
+	down(&ops_mutex);
+	ops->used--;
+	up(&ops_mutex);
+}
+
+
+/*
+ * module part
+ */
+
+static int __init alsa_seq_device_init(void)
+{
+	info_entry = snd_info_create_module_entry(THIS_MODULE, "drivers",
+						  snd_seq_root);
+	if (info_entry == NULL)
+		return -ENOMEM;
+	info_entry->content = SNDRV_INFO_CONTENT_TEXT;
+	info_entry->c.text.read_size = 2048;
+	info_entry->c.text.read = snd_seq_device_info;
+	if (snd_info_register(info_entry) < 0) {
+		snd_info_free_entry(info_entry);
+		return -ENOMEM;
+	}
+	return 0;
+}
+
+static void __exit alsa_seq_device_exit(void)
+{
+	remove_drivers();
+	snd_info_unregister(info_entry);
+	if (num_ops)
+		snd_printk(KERN_ERR "drivers not released (%d)\n", num_ops);
+}
+
+module_init(alsa_seq_device_init)
+module_exit(alsa_seq_device_exit)
+
+EXPORT_SYMBOL(snd_seq_device_load_drivers);
+EXPORT_SYMBOL(snd_seq_device_new);
+EXPORT_SYMBOL(snd_seq_device_register_driver);
+EXPORT_SYMBOL(snd_seq_device_unregister_driver);
+#ifdef CONFIG_KMOD
+EXPORT_SYMBOL(snd_seq_autoload_lock);
+EXPORT_SYMBOL(snd_seq_autoload_unlock);
+#endif
Index: /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_dtl.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_dtl.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_dtl.c	(revision 91)
@@ -0,0 +1,191 @@
+/*
+ * DTL(e) event converter
+ *
+ *  Copyright (c) 2000 by 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <sound/driver.h>
+#include "seq_queue.h"
+
+#ifdef SNDRV_SEQ_SYNC_SUPPORT
+
+typedef struct dtl_out {
+	int out_mtp_network;
+	unsigned int time_format;
+	unsigned int full_frame_count;
+	unsigned char sysex[11];
+} dtl_out_t;
+
+typedef struct dtl_in {
+	unsigned int time_format;
+	unsigned int cur_pos;
+} dtl_in_t;
+
+
+static int dtl_open_out(queue_sync_t *sync_info, seq_sync_arg_t *retp)
+{
+	dtl_out_t *arg;
+
+	if (sync_info->time_format >= 4)
+		return -EINVAL;
+	if ((arg = kmalloc(sizeof(*arg), GFP_KERNEL)) == NULL)
+		return -ENOMEM;
+	arg->out_mtp_network = sync_info->opt_info[0];
+	arg->full_frame_count = sync_info->opt_info[1];
+	arg->time_format = sync_info->time_format;
+	sync_info->param.time.subframes = 1; /* MTC uses quarter frame */
+	sync_info->param.time.resolution = snd_seq_get_smpte_resolution(arg->time_format);
+	*retp = arg;
+	return 0;
+}
+
+static int dtl_open_in(queue_sync_t *sync_info, seq_sync_arg_t *retp)
+{
+	dtl_in_t *arg;
+
+	if (sync_info->time_format >= 4)
+		return -EINVAL;
+	if ((arg = kmalloc(sizeof(*arg), GFP_KERNEL)) == NULL)
+		return -ENOMEM;
+	arg->time_format = sync_info->time_format;
+	arg->cur_pos = 0;
+	sync_info->param.time.subframes = 1; /* MTC uses quarter frame */
+	sync_info->param.time.resolution = snd_seq_get_smpte_resolution(arg->time_format);
+	*retp = arg;
+	return 0;
+}
+
+
+/* decode sync position */
+static int sync_pos_out(dtl_out_t *arg, const snd_seq_event_t *src, snd_seq_event_t *ev)
+{
+	sndrv_seq_time_frame_t cur_out;
+	unsigned char *buf = arg->sysex;
+
+	if (arg->time_format != src->data.queue.sync_time_format)
+		return -EINVAL;
+
+	cur_out = snd_seq_position_to_time_frame(arg->time_format, 1, src->data.queue.param.position);
+	buf[0] = 0xf0; /* SYSEX */
+	buf[1] = 0x00; /* MOTU */
+	buf[2] = 0x33; /* MOTU */
+	buf[3] = 0x7f;
+	buf[4] = 0x0c; /* DTL full frame */
+	buf[5] = arg->out_mtp_network; /* 0x00 or 0x08 */
+	buf[6] = cur_out.hour | (arg->time_format << 5);
+	buf[7] = cur_out.min;
+	buf[8] = cur_out.sec;
+	buf[9] = cur_out.frame;
+	buf[10] = 0xf7;
+
+	ev->type = SNDRV_SEQ_EVENT_SYSEX;
+	ev->flags &= ~SNDRV_SEQ_EVENT_LENGTH_MASK;
+	ev->flags |= SNDRV_SEQ_EVENT_LENGTH_VARIABLE;
+	ev->data.ext.len = 11;
+	ev->data.ext.ptr = buf;
+
+	return 1;
+}
+
+/* decode sync signal */
+static int sync_out(dtl_out_t *arg, const snd_seq_event_t *src, snd_seq_event_t *ev)
+{
+	unsigned int pos;
+
+	if (arg->time_format != src->data.queue.sync_time_format)
+		return -EINVAL;
+	pos = src->data.queue.param.position;
+	if (arg->full_frame_count &&
+	    (pos % arg->full_frame_count) == 0)
+		/* send full frame */
+		return sync_pos_out(arg, src, ev);
+	ev->type = SNDRV_SEQ_EVENT_CLOCK;
+	return 1;
+}
+
+static int dtl_sync_out(seq_sync_arg_t _arg, const snd_seq_event_t *src, snd_seq_event_t *ev)
+{
+	dtl_out_t *arg = _arg;
+	switch (src->type) {
+	case SNDRV_SEQ_EVENT_SYNC:
+		return sync_out(arg, src, ev);
+	case SNDRV_SEQ_EVENT_SYNC_POS:
+		return sync_pos_out(arg, src, ev);
+	}
+	return 0;
+}
+
+/* decode sync position */
+static int sync_pos_in(dtl_in_t *arg, const snd_seq_event_t *src, snd_seq_event_t *ev)
+{
+	unsigned time_format;
+	static unsigned char id[] = {
+		0xf0, 0x00, 0x33, 0x7f, 0x0c,
+	};
+	sndrv_seq_time_frame_t cur_in;
+	char buf[11];
+
+	if (snd_seq_expand_var_event(src, 11, buf, 1, 0) != 11)
+		return 0;
+	if (memcmp(buf, id, sizeof(id)) != 0)
+		return 0;
+	time_format = (buf[6] >> 5) & 3;
+	if (time_format != arg->time_format)
+		return -EINVAL;
+	cur_in.hour = buf[6] & 0x1f;
+	cur_in.min = buf[7];
+	cur_in.sec = buf[8];
+	cur_in.frame = buf[9];
+	arg->cur_pos = snd_seq_time_frame_to_position(time_format, 1, &cur_in);
+
+	ev->type = SNDRV_SEQ_EVENT_SYNC_POS;
+	ev->data.queue.sync_time_format = time_format;
+	ev->data.queue.param.position = arg->cur_pos;
+
+	return 1;
+}
+
+static int dtl_sync_in(seq_sync_arg_t _arg, const snd_seq_event_t *src, snd_seq_event_t *ev)
+{
+	dtl_in_t *arg = _arg;
+	switch (src->type) {
+	case SNDRV_SEQ_EVENT_CLOCK:
+		arg->cur_pos++;
+		ev->type = SNDRV_SEQ_EVENT_SYNC;
+		ev->data.queue.param.position = arg->cur_pos;
+		return 1;
+	case SNDRV_SEQ_EVENT_SYSEX:
+		return sync_pos_in(arg, src, ev);
+	}
+	return 0;
+}
+
+/* exported */
+seq_sync_parser_t snd_seq_dtl_parser = {
+	format: SNDRV_SEQ_SYNC_FMT_DTL,
+	in: {
+		open: dtl_open_in,
+		sync: dtl_sync_in,
+	},
+	out: {
+		open: dtl_open_out,
+		sync: dtl_sync_out,
+	},
+};
+
+#endif /* SNDRV_SEQ_SYNC_SUPPORT */
Index: /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_dummy.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_dummy.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_dummy.c	(revision 91)
@@ -0,0 +1,271 @@
+/*
+ * ALSA sequencer MIDI-through client
+ * Copyright (c) 1999-2000 by 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 <linux/init.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <sound/core.h>
+#include "seq_clientmgr.h"
+#include <sound/initval.h>
+#include <sound/asoundef.h>
+
+/*
+
+  Sequencer MIDI-through client
+
+  This gives a simple midi-through client.  All the normal input events
+  are redirected to output port immediately.
+  The routing can be done via aconnect program in alsa-utils.
+
+  Each client has a static client number 62 (= SNDRV_SEQ_CLIENT_DUMMY).
+  If you want to auto-load this module, you may add the following alias
+  in your /etc/conf.modules file.
+
+	alias snd-seq-client-62  snd-seq-dummy
+
+  The module is loaded on demand for client 62, or /proc/asound/seq/
+  is accessed.  If you don't need this module to be loaded, alias
+  snd-seq-client-62 as "off".  This will help modprobe.
+
+  The number of ports to be created can be specified via the module
+  parameter "ports".  For example, to create four ports, add the
+  following option in /etc/modprobe.conf:
+
+	option snd-seq-dummy ports=4
+
+  The modle option "duplex=1" enables duplex operation to the port.
+  In duplex mode, a pair of ports are created instead of single port,
+  and events are tunneled between pair-ports.  For example, input to
+  port A is sent to output port of another port B and vice versa.
+  In duplex mode, each port has DUPLEX capability.
+
+ */
+
+
+MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
+MODULE_DESCRIPTION("ALSA sequencer MIDI-through client");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("snd-seq-client-" __stringify(SNDRV_SEQ_CLIENT_DUMMY));
+
+static int ports = 1;
+static int duplex = 0;
+
+module_param(ports, int, 0444);
+MODULE_PARM_DESC(ports, "number of ports to be created");
+module_param(duplex, bool, 0444);
+MODULE_PARM_DESC(duplex, "create DUPLEX ports");
+
+struct snd_seq_dummy_port {
+	int client;
+	int port;
+	int duplex;
+	int connect;
+};
+
+static int my_client = -1;
+
+/*
+ * unuse callback - send ALL_SOUNDS_OFF and RESET_CONTROLLERS events
+ * to subscribers.
+ * Note: this callback is called only after all subscribers are removed.
+ */
+static int
+dummy_unuse(void *private_data, struct snd_seq_port_subscribe *info)
+{
+	struct snd_seq_dummy_port *p;
+	int i;
+	struct snd_seq_event ev;
+
+	p = private_data;
+	memset(&ev, 0, sizeof(ev));
+	if (p->duplex)
+		ev.source.port = p->connect;
+	else
+		ev.source.port = p->port;
+	ev.dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS;
+	ev.type = SNDRV_SEQ_EVENT_CONTROLLER;
+	for (i = 0; i < 16; i++) {
+		ev.data.control.channel = i;
+		ev.data.control.param = MIDI_CTL_ALL_SOUNDS_OFF;
+		snd_seq_kernel_client_dispatch(p->client, &ev, 0, 0);
+		ev.data.control.param = MIDI_CTL_RESET_CONTROLLERS;
+		snd_seq_kernel_client_dispatch(p->client, &ev, 0, 0);
+	}
+	return 0;
+}
+
+/*
+ * event input callback - just redirect events to subscribers
+ */
+static int
+dummy_input(struct snd_seq_event *ev, int direct, void *private_data,
+	    int atomic, int hop)
+{
+	struct snd_seq_dummy_port *p;
+	struct snd_seq_event tmpev;
+
+	p = private_data;
+	if (ev->source.client == SNDRV_SEQ_CLIENT_SYSTEM ||
+	    ev->type == SNDRV_SEQ_EVENT_KERNEL_ERROR)
+		return 0; /* ignore system messages */
+	tmpev = *ev;
+	if (p->duplex)
+		tmpev.source.port = p->connect;
+	else
+		tmpev.source.port = p->port;
+	tmpev.dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS;
+	return snd_seq_kernel_client_dispatch(p->client, &tmpev, atomic, hop);
+}
+
+/*
+ * free_private callback
+ */
+static void
+dummy_free(void *private_data)
+{
+	kfree(private_data);
+}
+
+/*
+ * create a port
+ */
+static struct snd_seq_dummy_port __init *
+create_port(int idx, int type)
+{
+	struct snd_seq_port_info pinfo;
+	struct snd_seq_port_callback pcb;
+	struct snd_seq_dummy_port *rec;
+
+	if ((rec = kzalloc(sizeof(*rec), GFP_KERNEL)) == NULL)
+		return NULL;
+
+	rec->client = my_client;
+	rec->duplex = duplex;
+	rec->connect = 0;
+	memset(&pinfo, 0, sizeof(pinfo));
+	pinfo.addr.client = my_client;
+	if (duplex)
+		sprintf(pinfo.name, "Midi Through Port-%d:%c", idx,
+			(type ? 'B' : 'A'));
+	else
+		sprintf(pinfo.name, "Midi Through Port-%d", idx);
+	pinfo.capability = SNDRV_SEQ_PORT_CAP_READ | SNDRV_SEQ_PORT_CAP_SUBS_READ;
+	pinfo.capability |= SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE;
+	if (duplex)
+		pinfo.capability |= SNDRV_SEQ_PORT_CAP_DUPLEX;
+	pinfo.type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC;
+	memset(&pcb, 0, sizeof(pcb));
+	pcb.owner = THIS_MODULE;
+	pcb.unuse = dummy_unuse;
+	pcb.event_input = dummy_input;
+	pcb.private_free = dummy_free;
+	pcb.private_data = rec;
+	pinfo.kernel = &pcb;
+	if (snd_seq_kernel_client_ctl(my_client, SNDRV_SEQ_IOCTL_CREATE_PORT, &pinfo) < 0) {
+		kfree(rec);
+		return NULL;
+	}
+	rec->port = pinfo.addr.port;
+	return rec;
+}
+
+/*
+ * register client and create ports
+ */
+static int __init
+register_client(void)
+{
+	struct snd_seq_client_callback cb;
+	struct snd_seq_client_info cinfo;
+	struct snd_seq_dummy_port *rec1, *rec2;
+	int i;
+
+	if (ports < 1) {
+		snd_printk(KERN_ERR "invalid number of ports %d\n", ports);
+		return -EINVAL;
+	}
+
+	/* create client */
+	memset(&cb, 0, sizeof(cb));
+	cb.allow_input = 1;
+	cb.allow_output = 1;
+	my_client = snd_seq_create_kernel_client(NULL, SNDRV_SEQ_CLIENT_DUMMY, &cb);
+	if (my_client < 0)
+		return my_client;
+
+	/* set client name */
+	memset(&cinfo, 0, sizeof(cinfo));
+	cinfo.client = my_client;
+	cinfo.type = KERNEL_CLIENT;
+	strcpy(cinfo.name, "Midi Through");
+	snd_seq_kernel_client_ctl(my_client, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, &cinfo);
+
+	/* create ports */
+	for (i = 0; i < ports; i++) {
+		rec1 = create_port(i, 0);
+		if (rec1 == NULL) {
+			snd_seq_delete_kernel_client(my_client);
+			return -ENOMEM;
+		}
+		if (duplex) {
+			rec2 = create_port(i, 1);
+			if (rec2 == NULL) {
+				snd_seq_delete_kernel_client(my_client);
+				return -ENOMEM;
+			}
+			rec1->connect = rec2->port;
+			rec2->connect = rec1->port;
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * delete client if exists
+ */
+static void __exit
+delete_client(void)
+{
+	if (my_client >= 0)
+		snd_seq_delete_kernel_client(my_client);
+}
+
+/*
+ *  Init part
+ */
+
+static int __init alsa_seq_dummy_init(void)
+{
+	int err;
+	snd_seq_autoload_lock();
+	err = register_client();
+	snd_seq_autoload_unlock();
+	return err;
+}
+
+static void __exit alsa_seq_dummy_exit(void)
+{
+	delete_client();
+}
+
+module_init(alsa_seq_dummy_init)
+module_exit(alsa_seq_dummy_exit)
Index: /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_fifo.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_fifo.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_fifo.c	(revision 91)
@@ -0,0 +1,268 @@
+/*
+ *   ALSA sequencer FIFO
+ *   Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <linux/slab.h>
+#include "seq_fifo.h"
+#include "seq_lock.h"
+
+
+/* FIFO */
+
+/* create new fifo */
+struct snd_seq_fifo *snd_seq_fifo_new(int poolsize)
+{
+	struct snd_seq_fifo *f;
+
+	f = (struct snd_seq_fifo *)kzalloc(sizeof(*f), GFP_KERNEL);
+	if (f == NULL) {
+		snd_printd("malloc failed for snd_seq_fifo_new() \n");
+		return NULL;
+	}
+
+	f->pool = snd_seq_pool_new(poolsize);
+	if (f->pool == NULL) {
+		kfree(f);
+		return NULL;
+	}
+	if (snd_seq_pool_init(f->pool) < 0) {
+		snd_seq_pool_delete(&f->pool);
+		kfree(f);
+		return NULL;
+	}
+
+	spin_lock_init(&f->lock);
+	snd_use_lock_init(&f->use_lock);
+	init_waitqueue_head(&f->input_sleep);
+	atomic_set(&f->overflow, 0);
+
+	f->head = NULL;
+	f->tail = NULL;
+	f->cells = 0;
+	
+	return f;
+}
+
+void snd_seq_fifo_delete(struct snd_seq_fifo **fifo)
+{
+	struct snd_seq_fifo *f;
+
+	snd_assert(fifo != NULL, return);
+	f = *fifo;
+	snd_assert(f != NULL, return);
+	*fifo = NULL;
+
+	snd_seq_fifo_clear(f);
+
+	/* wake up clients if any */
+	if (waitqueue_active(&f->input_sleep))
+		wake_up(&f->input_sleep);
+
+	/* release resources...*/
+	/*....................*/
+
+	if (f->pool) {
+		snd_seq_pool_done(f->pool);
+		snd_seq_pool_delete(&f->pool);
+	}
+	
+	kfree(f);
+}
+
+static struct snd_seq_event_cell *fifo_cell_out(struct snd_seq_fifo *f);
+
+/* clear queue */
+void snd_seq_fifo_clear(struct snd_seq_fifo *f)
+{
+	struct snd_seq_event_cell *cell;
+	unsigned long flags;
+
+	/* clear overflow flag */
+	atomic_set(&f->overflow, 0);
+
+	snd_use_lock_sync(&f->use_lock);
+	spin_lock_irqsave(&f->lock, flags);
+	/* drain the fifo */
+	while ((cell = fifo_cell_out(f)) != NULL) {
+		snd_seq_cell_free(cell);
+	}
+	spin_unlock_irqrestore(&f->lock, flags);
+}
+
+
+/* enqueue event to fifo */
+int snd_seq_fifo_event_in(struct snd_seq_fifo *f,
+			  struct snd_seq_event *event)
+{
+	struct snd_seq_event_cell *cell;
+	unsigned long flags;
+	int err;
+
+	snd_assert(f != NULL, return -EINVAL);
+
+	snd_use_lock_use(&f->use_lock);
+	err = snd_seq_event_dup(f->pool, event, &cell, 1, NULL); /* always non-blocking */
+	if (err < 0) {
+		if (err == -ENOMEM)
+			atomic_inc(&f->overflow);
+		snd_use_lock_free(&f->use_lock);
+		return err;
+	}
+		
+	/* append new cells to fifo */
+	spin_lock_irqsave(&f->lock, flags);
+	if (f->tail != NULL)
+		f->tail->next = cell;
+	f->tail = cell;
+	if (f->head == NULL)
+		f->head = cell;
+	f->cells++;
+	spin_unlock_irqrestore(&f->lock, flags);
+
+	/* wakeup client */
+	if (waitqueue_active(&f->input_sleep))
+		wake_up(&f->input_sleep);
+
+	snd_use_lock_free(&f->use_lock);
+
+	return 0; /* success */
+
+}
+
+/* dequeue cell from fifo */
+static struct snd_seq_event_cell *fifo_cell_out(struct snd_seq_fifo *f)
+{
+	struct snd_seq_event_cell *cell;
+
+	if ((cell = f->head) != NULL) {
+		f->head = cell->next;
+
+		/* reset tail if this was the last element */
+		if (f->tail == cell)
+			f->tail = NULL;
+
+		cell->next = NULL;
+		f->cells--;
+	}
+
+	return cell;
+}
+
+/* dequeue cell from fifo and copy on user space */
+int snd_seq_fifo_cell_out(struct snd_seq_fifo *f,
+			  struct snd_seq_event_cell **cellp, int nonblock)
+{
+	struct snd_seq_event_cell *cell;
+	unsigned long flags;
+	wait_queue_t wait;
+
+	snd_assert(f != NULL, return -EINVAL);
+
+	*cellp = NULL;
+	init_waitqueue_entry(&wait, current);
+	spin_lock_irqsave(&f->lock, flags);
+	while ((cell = fifo_cell_out(f)) == NULL) {
+		if (nonblock) {
+			/* non-blocking - return immediately */
+			spin_unlock_irqrestore(&f->lock, flags);
+			return -EAGAIN;
+		}
+		set_current_state(TASK_INTERRUPTIBLE);
+		add_wait_queue(&f->input_sleep, &wait);
+		spin_unlock_irq(&f->lock);
+		schedule();
+		spin_lock_irq(&f->lock);
+		remove_wait_queue(&f->input_sleep, &wait);
+		if (signal_pending(current)) {
+			spin_unlock_irqrestore(&f->lock, flags);
+			return -ERESTARTSYS;
+		}
+	}
+	spin_unlock_irqrestore(&f->lock, flags);
+	*cellp = cell;
+
+	return 0;
+}
+
+
+void snd_seq_fifo_cell_putback(struct snd_seq_fifo *f,
+			       struct snd_seq_event_cell *cell)
+{
+	unsigned long flags;
+
+	if (cell) {
+		spin_lock_irqsave(&f->lock, flags);
+		cell->next = f->head;
+		f->head = cell;
+		f->cells++;
+		spin_unlock_irqrestore(&f->lock, flags);
+	}
+}
+
+
+/* polling; return non-zero if queue is available */
+int snd_seq_fifo_poll_wait(struct snd_seq_fifo *f, struct file *file,
+			   poll_table *wait)
+{
+	poll_wait(file, &f->input_sleep, wait);
+	return (f->cells > 0);
+}
+
+/* change the size of pool; all old events are removed */
+int snd_seq_fifo_resize(struct snd_seq_fifo *f, int poolsize)
+{
+	unsigned long flags;
+	struct snd_seq_pool *newpool, *oldpool;
+	struct snd_seq_event_cell *cell, *next, *oldhead;
+
+	snd_assert(f != NULL && f->pool != NULL, return -EINVAL);
+
+	/* allocate new pool */
+	newpool = snd_seq_pool_new(poolsize);
+	if (newpool == NULL)
+		return -ENOMEM;
+	if (snd_seq_pool_init(newpool) < 0) {
+		snd_seq_pool_delete(&newpool);
+		return -ENOMEM;
+	}
+
+	spin_lock_irqsave(&f->lock, flags);
+	/* remember old pool */
+	oldpool = f->pool;
+	oldhead = f->head;
+	/* exchange pools */
+	f->pool = newpool;
+	f->head = NULL;
+	f->tail = NULL;
+	f->cells = 0;
+	/* NOTE: overflow flag is not cleared */
+	spin_unlock_irqrestore(&f->lock, flags);
+
+	/* release cells in old pool */
+	for (cell = oldhead; cell; cell = next) {
+		next = cell->next;
+		snd_seq_cell_free(cell);
+	}
+	snd_seq_pool_delete(&oldpool);
+
+	return 0;
+}
Index: /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_fifo.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_fifo.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_fifo.h	(revision 91)
@@ -0,0 +1,72 @@
+/*
+ *   ALSA sequencer FIFO
+ *   Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+#ifndef __SND_SEQ_FIFO_H
+#define __SND_SEQ_FIFO_H
+
+#include "seq_memory.h"
+#include "seq_lock.h"
+
+
+/* === FIFO === */
+
+struct snd_seq_fifo {
+	struct snd_seq_pool *pool;		/* FIFO pool */
+	struct snd_seq_event_cell *head;    	/* pointer to head of fifo */
+	struct snd_seq_event_cell *tail;    	/* pointer to tail of fifo */
+	int cells;
+	spinlock_t lock;
+	snd_use_lock_t use_lock;
+	wait_queue_head_t input_sleep;
+	atomic_t overflow;
+
+};
+
+/* create new fifo (constructor) */
+struct snd_seq_fifo *snd_seq_fifo_new(int poolsize);
+
+/* delete fifo (destructor) */
+void snd_seq_fifo_delete(struct snd_seq_fifo **f);
+
+
+/* enqueue event to fifo */
+int snd_seq_fifo_event_in(struct snd_seq_fifo *f, struct snd_seq_event *event);
+
+/* lock fifo from release */
+#define snd_seq_fifo_lock(fifo)		snd_use_lock_use(&(fifo)->use_lock)
+#define snd_seq_fifo_unlock(fifo)	snd_use_lock_free(&(fifo)->use_lock)
+
+/* get a cell from fifo - fifo should be locked */
+int snd_seq_fifo_cell_out(struct snd_seq_fifo *f, struct snd_seq_event_cell **cellp, int nonblock);
+
+/* free dequeued cell - fifo should be locked */
+void snd_seq_fifo_cell_putback(struct snd_seq_fifo *f, struct snd_seq_event_cell *cell);
+
+/* clean up queue */
+void snd_seq_fifo_clear(struct snd_seq_fifo *f);
+
+/* polling */
+int snd_seq_fifo_poll_wait(struct snd_seq_fifo *f, struct file *file, poll_table *wait);
+
+/* resize pool in fifo */
+int snd_seq_fifo_resize(struct snd_seq_fifo *f, int poolsize);
+
+
+#endif
Index: /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_info.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_info.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_info.c	(revision 91)
@@ -0,0 +1,76 @@
+/*
+ *   ALSA sequencer /proc interface
+ *   Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <sound/core.h>
+
+#include "seq_info.h"
+#include "seq_clientmgr.h"
+#include "seq_timer.h"
+
+
+static struct snd_info_entry *queues_entry;
+static struct snd_info_entry *clients_entry;
+static struct snd_info_entry *timer_entry;
+
+
+static struct snd_info_entry * __init
+create_info_entry(char *name, int size, void (*read)(struct snd_info_entry *,
+						     struct snd_info_buffer *))
+{
+	struct snd_info_entry *entry;
+
+	entry = snd_info_create_module_entry(THIS_MODULE, name, snd_seq_root);
+	if (entry == NULL)
+		return NULL;
+	entry->content = SNDRV_INFO_CONTENT_TEXT;
+	entry->c.text.read_size = size;
+	entry->c.text.read = read;
+	if (snd_info_register(entry) < 0) {
+		snd_info_free_entry(entry);
+		return NULL;
+	}
+	return entry;
+}
+
+
+/* create all our /proc entries */
+int __init snd_seq_info_init(void)
+{
+	queues_entry = create_info_entry("queues", 512 + (256 * SNDRV_SEQ_MAX_QUEUES),
+					 snd_seq_info_queues_read);
+	clients_entry = create_info_entry("clients", 512 + (256 * SNDRV_SEQ_MAX_CLIENTS),
+					  snd_seq_info_clients_read);
+	timer_entry = create_info_entry("timer", 1024, snd_seq_info_timer_read);
+	return 0;
+}
+
+int __exit snd_seq_info_done(void)
+{
+	if (queues_entry)
+		snd_info_unregister(queues_entry);
+	if (clients_entry)
+		snd_info_unregister(clients_entry);
+	if (timer_entry)
+		snd_info_unregister(timer_entry);
+	return 0;
+}
Index: /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_info.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_info.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_info.h	(revision 91)
@@ -0,0 +1,36 @@
+/*
+ *   ALSA sequencer /proc info
+ *   Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+#ifndef __SND_SEQ_INFO_H
+#define __SND_SEQ_INFO_H
+
+#include <sound/info.h>
+#include <sound/seq_kernel.h>
+
+void snd_seq_info_clients_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer);
+void snd_seq_info_timer_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer);
+void snd_seq_info_queues_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer);
+
+
+int snd_seq_info_init( void );
+int snd_seq_info_done( void );
+
+
+#endif
Index: /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_instr.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_instr.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_instr.c	(revision 91)
@@ -0,0 +1,653 @@
+/*
+ *   Generic Instrument routines for ALSA sequencer
+ *   Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+ 
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include "seq_clientmgr.h"
+#include <sound/seq_instr.h>
+#include <sound/initval.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Advanced Linux Sound Architecture sequencer instrument library.");
+MODULE_LICENSE("GPL");
+
+
+static void snd_instr_lock_ops(struct snd_seq_kinstr_list *list)
+{
+	if (!(list->flags & SNDRV_SEQ_INSTR_FLG_DIRECT)) {
+		spin_lock_irqsave(&list->ops_lock, list->ops_flags);
+	} else {
+		down(&list->ops_mutex);
+	}
+}
+
+static void snd_instr_unlock_ops(struct snd_seq_kinstr_list *list)
+{
+	if (!(list->flags & SNDRV_SEQ_INSTR_FLG_DIRECT)) {
+		spin_unlock_irqrestore(&list->ops_lock, list->ops_flags);
+	} else {
+		up(&list->ops_mutex);
+	}
+}
+
+static struct snd_seq_kinstr *snd_seq_instr_new(int add_len, int atomic)
+{
+	struct snd_seq_kinstr *instr;
+	
+	instr = (struct snd_seq_kinstr *)kzalloc(sizeof(struct snd_seq_kinstr) + add_len, atomic ? GFP_ATOMIC : GFP_KERNEL);
+	if (instr == NULL)
+		return NULL;
+	instr->add_len = add_len;
+	return instr;
+}
+
+static int snd_seq_instr_free(struct snd_seq_kinstr *instr, int atomic)
+{
+	int result = 0;
+
+	if (instr == NULL)
+		return -EINVAL;
+	if (instr->ops && instr->ops->remove)
+		result = instr->ops->remove(instr->ops->private_data, instr, 1);
+	if (!result)
+		kfree(instr);
+	return result;
+}
+
+struct snd_seq_kinstr_list *snd_seq_instr_list_new(void)
+{
+	struct snd_seq_kinstr_list *list;
+
+	list = (struct snd_seq_kinstr_list *)kzalloc(sizeof(struct snd_seq_kinstr_list), GFP_KERNEL);
+	if (list == NULL)
+		return NULL;
+	spin_lock_init(&list->lock);
+	spin_lock_init(&list->ops_lock);
+	init_MUTEX(&list->ops_mutex);
+	list->owner = -1;
+	return list;
+}
+
+void snd_seq_instr_list_free(struct snd_seq_kinstr_list **list_ptr)
+{
+	struct snd_seq_kinstr_list *list;
+	struct snd_seq_kinstr *instr;
+	struct snd_seq_kcluster *cluster;
+	int idx;
+	unsigned long flags;
+
+	if (list_ptr == NULL)
+		return;
+	list = *list_ptr;
+	*list_ptr = NULL;
+	if (list == NULL)
+		return;
+	
+	for (idx = 0; idx < SNDRV_SEQ_INSTR_HASH_SIZE; idx++) {		
+		while ((instr = list->hash[idx]) != NULL) {
+			list->hash[idx] = instr->next;
+			list->count--;
+			spin_lock_irqsave(&list->lock, flags);
+			while (instr->use) {
+				spin_unlock_irqrestore(&list->lock, flags);
+				schedule_timeout_interruptible(1);
+				spin_lock_irqsave(&list->lock, flags);
+			}				
+			spin_unlock_irqrestore(&list->lock, flags);
+			if (snd_seq_instr_free(instr, 0)<0)
+				snd_printk(KERN_WARNING "instrument free problem\n");
+		}
+		while ((cluster = list->chash[idx]) != NULL) {
+			list->chash[idx] = cluster->next;
+			list->ccount--;
+			kfree(cluster);
+		}
+	}
+	kfree(list);
+}
+
+static int instr_free_compare(struct snd_seq_kinstr *instr,
+			      struct snd_seq_instr_header *ifree,
+			      unsigned int client)
+{
+	switch (ifree->cmd) {
+	case SNDRV_SEQ_INSTR_FREE_CMD_ALL:
+		/* all, except private for other clients */
+		if ((instr->instr.std & 0xff000000) == 0)
+			return 0;
+		if (((instr->instr.std >> 24) & 0xff) == client)
+			return 0;
+		return 1;
+	case SNDRV_SEQ_INSTR_FREE_CMD_PRIVATE:
+		/* all my private instruments */
+		if ((instr->instr.std & 0xff000000) == 0)
+			return 1;
+		if (((instr->instr.std >> 24) & 0xff) == client)
+			return 0;
+		return 1;
+	case SNDRV_SEQ_INSTR_FREE_CMD_CLUSTER:
+		/* all my private instruments */
+		if ((instr->instr.std & 0xff000000) == 0) {
+			if (instr->instr.cluster == ifree->id.cluster)
+				return 0;
+			return 1;
+		}
+		if (((instr->instr.std >> 24) & 0xff) == client) {
+			if (instr->instr.cluster == ifree->id.cluster)
+				return 0;
+		}
+		return 1;
+	}
+	return 1;
+}
+
+int snd_seq_instr_list_free_cond(struct snd_seq_kinstr_list *list,
+			         struct snd_seq_instr_header *ifree,
+			         int client,
+			         int atomic)
+{
+	struct snd_seq_kinstr *instr, *prev, *next, *flist;
+	int idx;
+	unsigned long flags;
+
+	snd_instr_lock_ops(list);
+	for (idx = 0; idx < SNDRV_SEQ_INSTR_HASH_SIZE; idx++) {
+		spin_lock_irqsave(&list->lock, flags);
+		instr = list->hash[idx];
+		prev = flist = NULL;
+		while (instr) {
+			while (instr && instr_free_compare(instr, ifree, (unsigned int)client)) {
+				prev = instr;
+				instr = instr->next;
+			}
+			if (instr == NULL)
+				continue;
+			if (instr->ops && instr->ops->notify)
+				instr->ops->notify(instr->ops->private_data, instr, SNDRV_SEQ_INSTR_NOTIFY_REMOVE);
+			next = instr->next;
+			if (prev == NULL) {
+				list->hash[idx] = next;
+			} else {
+				prev->next = next;
+			}
+			list->count--;
+			instr->next = flist;
+			flist = instr;
+			instr = next;
+		}
+		spin_unlock_irqrestore(&list->lock, flags);
+		while (flist) {
+			instr = flist;
+			flist = instr->next;
+			while (instr->use)
+				schedule_timeout_interruptible(1);
+			if (snd_seq_instr_free(instr, atomic)<0)
+				snd_printk(KERN_WARNING "instrument free problem\n");
+			instr = next;
+		}
+	}
+	snd_instr_unlock_ops(list);
+	return 0;	
+}
+
+static int compute_hash_instr_key(struct snd_seq_instr *instr)
+{
+	int result;
+	
+	result = instr->bank | (instr->prg << 16);
+	result += result >> 24;
+	result += result >> 16;
+	result += result >> 8;
+	return result & (SNDRV_SEQ_INSTR_HASH_SIZE-1);
+}
+
+#if 0
+static int compute_hash_cluster_key(snd_seq_instr_cluster_t cluster)
+{
+	int result;
+	
+	result = cluster;
+	result += result >> 24;
+	result += result >> 16;
+	result += result >> 8;
+	return result & (SNDRV_SEQ_INSTR_HASH_SIZE-1);
+}
+#endif
+
+static int compare_instr(struct snd_seq_instr *i1, struct snd_seq_instr *i2, int exact)
+{
+	if (exact) {
+		if (i1->cluster != i2->cluster ||
+		    i1->bank != i2->bank ||
+		    i1->prg != i2->prg)
+			return 1;
+		if ((i1->std & 0xff000000) != (i2->std & 0xff000000))
+			return 1;
+		if (!(i1->std & i2->std))
+			return 1;
+		return 0;
+	} else {
+		unsigned int client_check;
+		
+		if (i2->cluster && i1->cluster != i2->cluster)
+			return 1;
+		client_check = i2->std & 0xff000000;
+		if (client_check) {
+			if ((i1->std & 0xff000000) != client_check)
+				return 1;
+		} else {
+			if ((i1->std & i2->std) != i2->std)
+				return 1;
+		}
+		return i1->bank != i2->bank || i1->prg != i2->prg;
+	}
+}
+
+struct snd_seq_kinstr *snd_seq_instr_find(struct snd_seq_kinstr_list *list,
+					  struct snd_seq_instr *instr,
+					  int exact,
+					  int follow_alias)
+{
+	unsigned long flags;
+	int depth = 0;
+	struct snd_seq_kinstr *result;
+
+	if (list == NULL || instr == NULL)
+		return NULL;
+	spin_lock_irqsave(&list->lock, flags);
+      __again:
+	result = list->hash[compute_hash_instr_key(instr)];
+	while (result) {
+		if (!compare_instr(&result->instr, instr, exact)) {
+			if (follow_alias && (result->type == SNDRV_SEQ_INSTR_ATYPE_ALIAS)) {
+				instr = (struct snd_seq_instr *)KINSTR_DATA(result);
+				if (++depth > 10)
+					goto __not_found;
+				goto __again;
+			}
+			result->use++;
+			spin_unlock_irqrestore(&list->lock, flags);
+			return result;
+		}
+		result = result->next;
+	}
+      __not_found:
+	spin_unlock_irqrestore(&list->lock, flags);
+	return NULL;
+}
+
+void snd_seq_instr_free_use(struct snd_seq_kinstr_list *list,
+			    struct snd_seq_kinstr *instr)
+{
+	unsigned long flags;
+
+	if (list == NULL || instr == NULL)
+		return;
+	spin_lock_irqsave(&list->lock, flags);
+	if (instr->use <= 0) {
+		snd_printk(KERN_ERR "free_use: fatal!!! use = %i, name = '%s'\n", instr->use, instr->name);
+	} else {
+		instr->use--;
+	}
+	spin_unlock_irqrestore(&list->lock, flags);
+}
+
+static struct snd_seq_kinstr_ops *instr_ops(struct snd_seq_kinstr_ops *ops,
+					    char *instr_type)
+{
+	while (ops) {
+		if (!strcmp(ops->instr_type, instr_type))
+			return ops;
+		ops = ops->next;
+	}
+	return NULL;
+}
+
+static int instr_result(struct snd_seq_event *ev,
+			int type, int result,
+			int atomic)
+{
+	struct snd_seq_event sev;
+	
+	memset(&sev, 0, sizeof(sev));
+	sev.type = SNDRV_SEQ_EVENT_RESULT;
+	sev.flags = SNDRV_SEQ_TIME_STAMP_REAL | SNDRV_SEQ_EVENT_LENGTH_FIXED |
+	            SNDRV_SEQ_PRIORITY_NORMAL;
+	sev.source = ev->dest;
+	sev.dest = ev->source;
+	sev.data.result.event = type;
+	sev.data.result.result = result;
+#if 0
+	printk("instr result - type = %i, result = %i, queue = %i, source.client:port = %i:%i, dest.client:port = %i:%i\n",
+				type, result,
+				sev.queue,
+				sev.source.client, sev.source.port,
+				sev.dest.client, sev.dest.port);
+#endif
+	return snd_seq_kernel_client_dispatch(sev.source.client, &sev, atomic, 0);
+}
+
+static int instr_begin(struct snd_seq_kinstr_ops *ops,
+		       struct snd_seq_kinstr_list *list,
+		       struct snd_seq_event *ev,
+		       int atomic, int hop)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&list->lock, flags);
+	if (list->owner >= 0 && list->owner != ev->source.client) {
+		spin_unlock_irqrestore(&list->lock, flags);
+		return instr_result(ev, SNDRV_SEQ_EVENT_INSTR_BEGIN, -EBUSY, atomic);
+	}
+	list->owner = ev->source.client;
+	spin_unlock_irqrestore(&list->lock, flags);
+	return instr_result(ev, SNDRV_SEQ_EVENT_INSTR_BEGIN, 0, atomic);
+}
+
+static int instr_end(struct snd_seq_kinstr_ops *ops,
+		     struct snd_seq_kinstr_list *list,
+		     struct snd_seq_event *ev,
+		     int atomic, int hop)
+{
+	unsigned long flags;
+
+	/* TODO: timeout handling */
+	spin_lock_irqsave(&list->lock, flags);
+	if (list->owner == ev->source.client) {
+		list->owner = -1;
+		spin_unlock_irqrestore(&list->lock, flags);
+		return instr_result(ev, SNDRV_SEQ_EVENT_INSTR_END, 0, atomic);
+	}
+	spin_unlock_irqrestore(&list->lock, flags);
+	return instr_result(ev, SNDRV_SEQ_EVENT_INSTR_END, -EINVAL, atomic);
+}
+
+static int instr_info(struct snd_seq_kinstr_ops *ops,
+		      struct snd_seq_kinstr_list *list,
+		      struct snd_seq_event *ev,
+		      int atomic, int hop)
+{
+	return -ENXIO;
+}
+
+static int instr_format_info(struct snd_seq_kinstr_ops *ops,
+			     struct snd_seq_kinstr_list *list,
+			     struct snd_seq_event *ev,
+			     int atomic, int hop)
+{
+	return -ENXIO;
+}
+
+static int instr_reset(struct snd_seq_kinstr_ops *ops,
+		       struct snd_seq_kinstr_list *list,
+		       struct snd_seq_event *ev,
+		       int atomic, int hop)
+{
+	return -ENXIO;
+}
+
+static int instr_status(struct snd_seq_kinstr_ops *ops,
+			struct snd_seq_kinstr_list *list,
+			struct snd_seq_event *ev,
+			int atomic, int hop)
+{
+	return -ENXIO;
+}
+
+static int instr_put(struct snd_seq_kinstr_ops *ops,
+		     struct snd_seq_kinstr_list *list,
+		     struct snd_seq_event *ev,
+		     int atomic, int hop)
+{
+	unsigned long flags;
+	struct snd_seq_instr_header put;
+	struct snd_seq_kinstr *instr;
+	int result = -EINVAL, len, key;
+
+	if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARUSR)
+		goto __return;
+
+	if (ev->data.ext.len < sizeof(struct snd_seq_instr_header))
+		goto __return;
+	if (copy_from_user(&put, (void __user *)ev->data.ext.ptr,
+			   sizeof(struct snd_seq_instr_header))) {
+		result = -EFAULT;
+		goto __return;
+	}
+	snd_instr_lock_ops(list);
+	if (put.id.instr.std & 0xff000000) {	/* private instrument */
+		put.id.instr.std &= 0x00ffffff;
+		put.id.instr.std |= (unsigned int)ev->source.client << 24;
+	}
+	if ((instr = snd_seq_instr_find(list, &put.id.instr, 1, 0))) {
+		snd_seq_instr_free_use(list, instr);
+		snd_instr_unlock_ops(list);
+		result = -EBUSY;
+		goto __return;
+	}
+	ops = instr_ops(ops, put.data.data.format);
+	if (ops == NULL) {
+		snd_instr_unlock_ops(list);
+		goto __return;
+	}
+	len = ops->add_len;
+	if (put.data.type == SNDRV_SEQ_INSTR_ATYPE_ALIAS)
+		len = sizeof(struct snd_seq_instr);
+	instr = snd_seq_instr_new(len, atomic);
+	if (instr == NULL) {
+		snd_instr_unlock_ops(list);
+		result = -ENOMEM;
+		goto __return;
+	}
+	instr->ops = ops;
+	instr->instr = put.id.instr;
+	strlcpy(instr->name, put.data.name, sizeof(instr->name));
+	instr->type = put.data.type;
+	if (instr->type == SNDRV_SEQ_INSTR_ATYPE_DATA) {
+		result = ops->put(ops->private_data,
+				  instr,
+				  (char __user *)ev->data.ext.ptr + sizeof(struct snd_seq_instr_header),
+				  ev->data.ext.len - sizeof(struct snd_seq_instr_header),
+				  atomic,
+				  put.cmd);
+		if (result < 0) {
+			snd_seq_instr_free(instr, atomic);
+			snd_instr_unlock_ops(list);
+			goto __return;
+		}
+	}
+	key = compute_hash_instr_key(&instr->instr);
+	spin_lock_irqsave(&list->lock, flags);
+	instr->next = list->hash[key];
+	list->hash[key] = instr;
+	list->count++;
+	spin_unlock_irqrestore(&list->lock, flags);
+	snd_instr_unlock_ops(list);
+	result = 0;
+      __return:
+	instr_result(ev, SNDRV_SEQ_EVENT_INSTR_PUT, result, atomic);
+	return result;
+}
+
+static int instr_get(struct snd_seq_kinstr_ops *ops,
+		     struct snd_seq_kinstr_list *list,
+		     struct snd_seq_event *ev,
+		     int atomic, int hop)
+{
+	return -ENXIO;
+}
+
+static int instr_free(struct snd_seq_kinstr_ops *ops,
+		      struct snd_seq_kinstr_list *list,
+		      struct snd_seq_event *ev,
+		      int atomic, int hop)
+{
+	struct snd_seq_instr_header ifree;
+	struct snd_seq_kinstr *instr, *prev;
+	int result = -EINVAL;
+	unsigned long flags;
+	unsigned int hash;
+
+	if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARUSR)
+		goto __return;
+
+	if (ev->data.ext.len < sizeof(struct snd_seq_instr_header))
+		goto __return;
+	if (copy_from_user(&ifree, (void __user *)ev->data.ext.ptr,
+			   sizeof(struct snd_seq_instr_header))) {
+		result = -EFAULT;
+		goto __return;
+	}
+	if (ifree.cmd == SNDRV_SEQ_INSTR_FREE_CMD_ALL ||
+	    ifree.cmd == SNDRV_SEQ_INSTR_FREE_CMD_PRIVATE ||
+	    ifree.cmd == SNDRV_SEQ_INSTR_FREE_CMD_CLUSTER) {
+	    	result = snd_seq_instr_list_free_cond(list, &ifree, ev->dest.client, atomic);
+	    	goto __return;
+	}
+	if (ifree.cmd == SNDRV_SEQ_INSTR_FREE_CMD_SINGLE) {
+		if (ifree.id.instr.std & 0xff000000) {
+			ifree.id.instr.std &= 0x00ffffff;
+			ifree.id.instr.std |= (unsigned int)ev->source.client << 24;
+		}
+		hash = compute_hash_instr_key(&ifree.id.instr);
+		snd_instr_lock_ops(list);
+		spin_lock_irqsave(&list->lock, flags);
+		instr = list->hash[hash];
+		prev = NULL;
+		while (instr) {
+			if (!compare_instr(&instr->instr, &ifree.id.instr, 1))
+				goto __free_single;
+			prev = instr;
+			instr = instr->next;
+		}
+		result = -ENOENT;
+		spin_unlock_irqrestore(&list->lock, flags);
+		snd_instr_unlock_ops(list);
+		goto __return;
+		
+	      __free_single:
+		if (prev) {
+			prev->next = instr->next;
+		} else {
+			list->hash[hash] = instr->next;
+		}
+		if (instr->ops && instr->ops->notify)
+			instr->ops->notify(instr->ops->private_data, instr,
+					   SNDRV_SEQ_INSTR_NOTIFY_REMOVE);
+		while (instr->use) {
+			spin_unlock_irqrestore(&list->lock, flags);
+			schedule_timeout_interruptible(1);
+			spin_lock_irqsave(&list->lock, flags);
+		}				
+		spin_unlock_irqrestore(&list->lock, flags);
+		result = snd_seq_instr_free(instr, atomic);
+		snd_instr_unlock_ops(list);
+		goto __return;
+	}
+
+      __return:
+	instr_result(ev, SNDRV_SEQ_EVENT_INSTR_FREE, result, atomic);
+	return result;
+}
+
+static int instr_list(struct snd_seq_kinstr_ops *ops,
+		      struct snd_seq_kinstr_list *list,
+		      struct snd_seq_event *ev,
+		      int atomic, int hop)
+{
+	return -ENXIO;
+}
+
+static int instr_cluster(struct snd_seq_kinstr_ops *ops,
+			 struct snd_seq_kinstr_list *list,
+			 struct snd_seq_event *ev,
+			 int atomic, int hop)
+{
+	return -ENXIO;
+}
+
+int snd_seq_instr_event(struct snd_seq_kinstr_ops *ops,
+			struct snd_seq_kinstr_list *list,
+			struct snd_seq_event *ev,
+			int client,
+			int atomic,
+			int hop)
+{
+	int direct = 0;
+
+	snd_assert(ops != NULL && list != NULL && ev != NULL, return -EINVAL);
+	if (snd_seq_ev_is_direct(ev)) {
+		direct = 1;
+		switch (ev->type) {
+		case SNDRV_SEQ_EVENT_INSTR_BEGIN:
+			return instr_begin(ops, list, ev, atomic, hop);
+		case SNDRV_SEQ_EVENT_INSTR_END:
+			return instr_end(ops, list, ev, atomic, hop);
+		}
+	}
+	if ((list->flags & SNDRV_SEQ_INSTR_FLG_DIRECT) && !direct)
+		return -EINVAL;
+	switch (ev->type) {
+	case SNDRV_SEQ_EVENT_INSTR_INFO:
+		return instr_info(ops, list, ev, atomic, hop);
+	case SNDRV_SEQ_EVENT_INSTR_FINFO:
+		return instr_format_info(ops, list, ev, atomic, hop);
+	case SNDRV_SEQ_EVENT_INSTR_RESET:
+		return instr_reset(ops, list, ev, atomic, hop);
+	case SNDRV_SEQ_EVENT_INSTR_STATUS:
+		return instr_status(ops, list, ev, atomic, hop);
+	case SNDRV_SEQ_EVENT_INSTR_PUT:
+		return instr_put(ops, list, ev, atomic, hop);
+	case SNDRV_SEQ_EVENT_INSTR_GET:
+		return instr_get(ops, list, ev, atomic, hop);
+	case SNDRV_SEQ_EVENT_INSTR_FREE:
+		return instr_free(ops, list, ev, atomic, hop);
+	case SNDRV_SEQ_EVENT_INSTR_LIST:
+		return instr_list(ops, list, ev, atomic, hop);
+	case SNDRV_SEQ_EVENT_INSTR_CLUSTER:
+		return instr_cluster(ops, list, ev, atomic, hop);
+	}
+	return -EINVAL;
+}
+			
+/*
+ *  Init part
+ */
+
+static int __init alsa_seq_instr_init(void)
+{
+	return 0;
+}
+
+static void __exit alsa_seq_instr_exit(void)
+{
+}
+
+module_init(alsa_seq_instr_init)
+module_exit(alsa_seq_instr_exit)
+
+EXPORT_SYMBOL(snd_seq_instr_list_new);
+EXPORT_SYMBOL(snd_seq_instr_list_free);
+EXPORT_SYMBOL(snd_seq_instr_list_free_cond);
+EXPORT_SYMBOL(snd_seq_instr_find);
+EXPORT_SYMBOL(snd_seq_instr_free_use);
+EXPORT_SYMBOL(snd_seq_instr_event);
Index: /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_lock.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_lock.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_lock.c	(revision 91)
@@ -0,0 +1,47 @@
+/*
+ *  Do sleep inside a spin-lock
+ *  Copyright (c) 1999 by 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 <sound/core.h>
+#include "seq_lock.h"
+
+#if defined(CONFIG_SMP) || defined(CONFIG_SND_DEBUG)
+
+/* wait until all locks are released */
+void snd_use_lock_sync_helper(snd_use_lock_t *lockp, const char *file, int line)
+{
+	int max_count = 5 * HZ;
+
+	if (atomic_read(lockp) < 0) {
+		printk(KERN_WARNING "seq_lock: lock trouble [counter = %d] in %s:%d\n", atomic_read(lockp), file, line);
+		return;
+	}
+	while (atomic_read(lockp) > 0) {
+		if (max_count == 0) {
+			snd_printk(KERN_WARNING "seq_lock: timeout [%d left] in %s:%d\n", atomic_read(lockp), file, line);
+			break;
+		}
+		schedule_timeout_uninterruptible(1);
+		max_count--;
+	}
+}
+
+#endif
Index: /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_lock.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_lock.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_lock.h	(revision 91)
@@ -0,0 +1,33 @@
+#ifndef __SND_SEQ_LOCK_H
+#define __SND_SEQ_LOCK_H
+
+#include <linux/sched.h>
+
+#if defined(CONFIG_SMP) || defined(CONFIG_SND_DEBUG)
+
+typedef atomic_t snd_use_lock_t;
+
+/* initialize lock */
+#define snd_use_lock_init(lockp) atomic_set(lockp, 0)
+
+/* increment lock */
+#define snd_use_lock_use(lockp) atomic_inc(lockp)
+
+/* release lock */
+#define snd_use_lock_free(lockp) atomic_dec(lockp)
+
+/* wait until all locks are released */
+void snd_use_lock_sync_helper(snd_use_lock_t *lock, const char *file, int line);
+#define snd_use_lock_sync(lockp) snd_use_lock_sync_helper(lockp, __BASE_FILE__, __LINE__)
+
+#else /* SMP || CONFIG_SND_DEBUG */
+
+typedef spinlock_t snd_use_lock_t;	/* dummy */
+#define snd_use_lock_init(lockp) /**/
+#define snd_use_lock_use(lockp) /**/
+#define snd_use_lock_free(lockp) /**/
+#define snd_use_lock_sync(lockp) /**/
+
+#endif /* SMP || CONFIG_SND_DEBUG */
+
+#endif /* __SND_SEQ_LOCK_H */
Index: /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_memory.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_memory.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_memory.c	(revision 91)
@@ -0,0 +1,518 @@
+/*
+ *  ALSA sequencer Memory Manager
+ *  Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
+ *                        Jaroslav Kysela <perex@suse.cz>
+ *                2000 by 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 <linux/init.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <sound/core.h>
+
+#include <sound/seq_kernel.h>
+#include "seq_memory.h"
+#include "seq_queue.h"
+#include "seq_info.h"
+#include "seq_lock.h"
+
+/* semaphore in struct file record */
+#define semaphore_of(fp)	((fp)->f_dentry->d_inode->i_sem)
+
+
+static inline int snd_seq_pool_available(struct snd_seq_pool *pool)
+{
+	return pool->total_elements - atomic_read(&pool->counter);
+}
+
+static inline int snd_seq_output_ok(struct snd_seq_pool *pool)
+{
+	return snd_seq_pool_available(pool) >= pool->room;
+}
+
+/*
+ * Variable length event:
+ * The event like sysex uses variable length type.
+ * The external data may be stored in three different formats.
+ * 1) kernel space
+ *    This is the normal case.
+ *      ext.data.len = length
+ *      ext.data.ptr = buffer pointer
+ * 2) user space
+ *    When an event is generated via read(), the external data is
+ *    kept in user space until expanded.
+ *      ext.data.len = length | SNDRV_SEQ_EXT_USRPTR
+ *      ext.data.ptr = userspace pointer
+ * 3) chained cells
+ *    When the variable length event is enqueued (in prioq or fifo),
+ *    the external data is decomposed to several cells.
+ *      ext.data.len = length | SNDRV_SEQ_EXT_CHAINED
+ *      ext.data.ptr = the additiona cell head
+ *         -> cell.next -> cell.next -> ..
+ */
+
+/*
+ * exported:
+ * call dump function to expand external data.
+ */
+
+static int get_var_len(const struct snd_seq_event *event)
+{
+	if ((event->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARIABLE)
+		return -EINVAL;
+
+	return event->data.ext.len & ~SNDRV_SEQ_EXT_MASK;
+}
+
+int snd_seq_dump_var_event(const struct snd_seq_event *event,
+			   snd_seq_dump_func_t func, void *private_data)
+{
+	int len, err;
+	struct snd_seq_event_cell *cell;
+
+	if ((len = get_var_len(event)) <= 0)
+		return len;
+
+	if (event->data.ext.len & SNDRV_SEQ_EXT_USRPTR) {
+		char buf[32];
+		char __user *curptr = (char __user *)event->data.ext.ptr;
+		while (len > 0) {
+			int size = sizeof(buf);
+			if (len < size)
+				size = len;
+			if (copy_from_user(buf, curptr, size))
+				return -EFAULT;
+			err = func(private_data, buf, size);
+			if (err < 0)
+				return err;
+			curptr += size;
+			len -= size;
+		}
+		return 0;
+	} if (! (event->data.ext.len & SNDRV_SEQ_EXT_CHAINED)) {
+		return func(private_data, event->data.ext.ptr, len);
+	}
+
+	cell = (struct snd_seq_event_cell *)event->data.ext.ptr;
+	for (; len > 0 && cell; cell = cell->next) {
+		int size = sizeof(struct snd_seq_event);
+		if (len < size)
+			size = len;
+		err = func(private_data, &cell->event, size);
+		if (err < 0)
+			return err;
+		len -= size;
+	}
+	return 0;
+}
+
+
+/*
+ * exported:
+ * expand the variable length event to linear buffer space.
+ */
+
+static int seq_copy_in_kernel(char **bufptr, const void *src, int size)
+{
+	memcpy(*bufptr, src, size);
+	*bufptr += size;
+	return 0;
+}
+
+static int seq_copy_in_user(char __user **bufptr, const void *src, int size)
+{
+	if (copy_to_user(*bufptr, src, size))
+		return -EFAULT;
+	*bufptr += size;
+	return 0;
+}
+
+int snd_seq_expand_var_event(const struct snd_seq_event *event, int count, char *buf,
+			     int in_kernel, int size_aligned)
+{
+	int len, newlen;
+	int err;
+
+	if ((len = get_var_len(event)) < 0)
+		return len;
+	newlen = len;
+	if (size_aligned > 0)
+		newlen = ((len + size_aligned - 1) / size_aligned) * size_aligned;
+	if (count < newlen)
+		return -EAGAIN;
+
+	if (event->data.ext.len & SNDRV_SEQ_EXT_USRPTR) {
+		if (! in_kernel)
+			return -EINVAL;
+		if (copy_from_user(buf, (void __user *)event->data.ext.ptr, len))
+			return -EFAULT;
+		return newlen;
+	}
+	err = snd_seq_dump_var_event(event,
+				     in_kernel ? (snd_seq_dump_func_t)seq_copy_in_kernel :
+				     (snd_seq_dump_func_t)seq_copy_in_user,
+				     &buf);
+	return err < 0 ? err : newlen;
+}
+
+
+/*
+ * release this cell, free extended data if available
+ */
+
+static inline void free_cell(struct snd_seq_pool *pool,
+			     struct snd_seq_event_cell *cell)
+{
+	cell->next = pool->free;
+	pool->free = cell;
+	atomic_dec(&pool->counter);
+}
+
+void snd_seq_cell_free(struct snd_seq_event_cell * cell)
+{
+	unsigned long flags;
+	struct snd_seq_pool *pool;
+
+	snd_assert(cell != NULL, return);
+	pool = cell->pool;
+	snd_assert(pool != NULL, return);
+
+	spin_lock_irqsave(&pool->lock, flags);
+	free_cell(pool, cell);
+	if (snd_seq_ev_is_variable(&cell->event)) {
+		if (cell->event.data.ext.len & SNDRV_SEQ_EXT_CHAINED) {
+			struct snd_seq_event_cell *curp, *nextptr;
+			curp = cell->event.data.ext.ptr;
+			for (; curp; curp = nextptr) {
+				nextptr = curp->next;
+				curp->next = pool->free;
+				free_cell(pool, curp);
+			}
+		}
+	}
+	if (waitqueue_active(&pool->output_sleep)) {
+		/* has enough space now? */
+		if (snd_seq_output_ok(pool))
+			wake_up(&pool->output_sleep);
+	}
+	spin_unlock_irqrestore(&pool->lock, flags);
+}
+
+
+/*
+ * allocate an event cell.
+ */
+static int snd_seq_cell_alloc(struct snd_seq_pool *pool,
+			      struct snd_seq_event_cell **cellp,
+			      int nonblock, struct file *file)
+{
+	struct snd_seq_event_cell *cell;
+	unsigned long flags;
+	int err = -EAGAIN;
+	wait_queue_t wait;
+
+	if (pool == NULL)
+		return -EINVAL;
+
+	*cellp = NULL;
+
+	init_waitqueue_entry(&wait, current);
+	spin_lock_irqsave(&pool->lock, flags);
+	if (pool->ptr == NULL) {	/* not initialized */
+		snd_printd("seq: pool is not initialized\n");
+		err = -EINVAL;
+		goto __error;
+	}
+	while (pool->free == NULL && ! nonblock && ! pool->closing) {
+
+		set_current_state(TASK_INTERRUPTIBLE);
+		add_wait_queue(&pool->output_sleep, &wait);
+		spin_unlock_irq(&pool->lock);
+		schedule();
+		spin_lock_irq(&pool->lock);
+		remove_wait_queue(&pool->output_sleep, &wait);
+		/* interrupted? */
+		if (signal_pending(current)) {
+			err = -ERESTARTSYS;
+			goto __error;
+		}
+	}
+	if (pool->closing) { /* closing.. */
+		err = -ENOMEM;
+		goto __error;
+	}
+
+	cell = pool->free;
+	if (cell) {
+		int used;
+		pool->free = cell->next;
+		atomic_inc(&pool->counter);
+		used = atomic_read(&pool->counter);
+		if (pool->max_used < used)
+			pool->max_used = used;
+		pool->event_alloc_success++;
+		/* clear cell pointers */
+		cell->next = NULL;
+		err = 0;
+	} else
+		pool->event_alloc_failures++;
+	*cellp = cell;
+
+__error:
+	spin_unlock_irqrestore(&pool->lock, flags);
+	return err;
+}
+
+
+/*
+ * duplicate the event to a cell.
+ * if the event has external data, the data is decomposed to additional
+ * cells.
+ */
+int snd_seq_event_dup(struct snd_seq_pool *pool, struct snd_seq_event *event,
+		      struct snd_seq_event_cell **cellp, int nonblock,
+		      struct file *file)
+{
+	int ncells, err;
+	unsigned int extlen;
+	struct snd_seq_event_cell *cell;
+
+	*cellp = NULL;
+
+	ncells = 0;
+	extlen = 0;
+	if (snd_seq_ev_is_variable(event)) {
+		extlen = event->data.ext.len & ~SNDRV_SEQ_EXT_MASK;
+		ncells = (extlen + sizeof(struct snd_seq_event) - 1) / sizeof(struct snd_seq_event);
+	}
+	if (ncells >= pool->total_elements)
+		return -ENOMEM;
+
+	err = snd_seq_cell_alloc(pool, &cell, nonblock, file);
+	if (err < 0)
+		return err;
+
+	/* copy the event */
+	cell->event = *event;
+
+	/* decompose */
+	if (snd_seq_ev_is_variable(event)) {
+		int len = extlen;
+		int is_chained = event->data.ext.len & SNDRV_SEQ_EXT_CHAINED;
+		int is_usrptr = event->data.ext.len & SNDRV_SEQ_EXT_USRPTR;
+		struct snd_seq_event_cell *src, *tmp, *tail;
+		char *buf;
+
+		cell->event.data.ext.len = extlen | SNDRV_SEQ_EXT_CHAINED;
+		cell->event.data.ext.ptr = NULL;
+
+		src = (struct snd_seq_event_cell *)event->data.ext.ptr;
+		buf = (char *)event->data.ext.ptr;
+		tail = NULL;
+
+		while (ncells-- > 0) {
+			int size = sizeof(struct snd_seq_event);
+			if (len < size)
+				size = len;
+			err = snd_seq_cell_alloc(pool, &tmp, nonblock, file);
+			if (err < 0)
+				goto __error;
+			if (cell->event.data.ext.ptr == NULL)
+				cell->event.data.ext.ptr = tmp;
+			if (tail)
+				tail->next = tmp;
+			tail = tmp;
+			/* copy chunk */
+			if (is_chained && src) {
+				tmp->event = src->event;
+				src = src->next;
+			} else if (is_usrptr) {
+				if (copy_from_user(&tmp->event, (char __user *)buf, size)) {
+					err = -EFAULT;
+					goto __error;
+				}
+			} else {
+				memcpy(&tmp->event, buf, size);
+			}
+			buf += size;
+			len -= size;
+		}
+	}
+
+	*cellp = cell;
+	return 0;
+
+__error:
+	snd_seq_cell_free(cell);
+	return err;
+}
+  
+
+/* poll wait */
+int snd_seq_pool_poll_wait(struct snd_seq_pool *pool, struct file *file,
+			   poll_table *wait)
+{
+	poll_wait(file, &pool->output_sleep, wait);
+	return snd_seq_output_ok(pool);
+}
+
+
+/* allocate room specified number of events */
+int snd_seq_pool_init(struct snd_seq_pool *pool)
+{
+	int cell;
+	struct snd_seq_event_cell *cellptr;
+	unsigned long flags;
+
+	snd_assert(pool != NULL, return -EINVAL);
+	if (pool->ptr)			/* should be atomic? */
+		return 0;
+
+	pool->ptr = vmalloc(sizeof(struct snd_seq_event_cell) * pool->size);
+	if (pool->ptr == NULL) {
+		snd_printd("seq: malloc for sequencer events failed\n");
+		return -ENOMEM;
+	}
+
+	/* add new cells to the free cell list */
+	spin_lock_irqsave(&pool->lock, flags);
+	pool->free = NULL;
+
+	for (cell = 0; cell < pool->size; cell++) {
+		cellptr = pool->ptr + cell;
+		cellptr->pool = pool;
+		cellptr->next = pool->free;
+		pool->free = cellptr;
+	}
+	pool->room = (pool->size + 1) / 2;
+
+	/* init statistics */
+	pool->max_used = 0;
+	pool->total_elements = pool->size;
+	spin_unlock_irqrestore(&pool->lock, flags);
+	return 0;
+}
+
+/* remove events */
+int snd_seq_pool_done(struct snd_seq_pool *pool)
+{
+	unsigned long flags;
+	struct snd_seq_event_cell *ptr;
+	int max_count = 5 * HZ;
+
+	snd_assert(pool != NULL, return -EINVAL);
+
+	/* wait for closing all threads */
+	spin_lock_irqsave(&pool->lock, flags);
+	pool->closing = 1;
+	spin_unlock_irqrestore(&pool->lock, flags);
+
+	if (waitqueue_active(&pool->output_sleep))
+		wake_up(&pool->output_sleep);
+
+	while (atomic_read(&pool->counter) > 0) {
+		if (max_count == 0) {
+			snd_printk(KERN_WARNING "snd_seq_pool_done timeout: %d cells remain\n", atomic_read(&pool->counter));
+			break;
+		}
+		schedule_timeout_uninterruptible(1);
+		max_count--;
+	}
+	
+	/* release all resources */
+	spin_lock_irqsave(&pool->lock, flags);
+	ptr = pool->ptr;
+	pool->ptr = NULL;
+	pool->free = NULL;
+	pool->total_elements = 0;
+	spin_unlock_irqrestore(&pool->lock, flags);
+
+	vfree(ptr);
+
+	spin_lock_irqsave(&pool->lock, flags);
+	pool->closing = 0;
+	spin_unlock_irqrestore(&pool->lock, flags);
+
+	return 0;
+}
+
+
+/* init new memory pool */
+struct snd_seq_pool *snd_seq_pool_new(int poolsize)
+{
+	struct snd_seq_pool *pool;
+
+	/* create pool block */
+	pool = (struct snd_seq_pool *)kzalloc(sizeof(*pool), GFP_KERNEL);
+	if (pool == NULL) {
+		snd_printd("seq: malloc failed for pool\n");
+		return NULL;
+	}
+	spin_lock_init(&pool->lock);
+	pool->ptr = NULL;
+	pool->free = NULL;
+	pool->total_elements = 0;
+	atomic_set(&pool->counter, 0);
+	pool->closing = 0;
+	init_waitqueue_head(&pool->output_sleep);
+	
+	pool->size = poolsize;
+
+	/* init statistics */
+	pool->max_used = 0;
+	return pool;
+}
+
+/* remove memory pool */
+int snd_seq_pool_delete(struct snd_seq_pool **ppool)
+{
+	struct snd_seq_pool *pool = *ppool;
+
+	*ppool = NULL;
+	if (pool == NULL)
+		return 0;
+	snd_seq_pool_done(pool);
+	kfree(pool);
+	return 0;
+}
+
+/* initialize sequencer memory */
+int __init snd_sequencer_memory_init(void)
+{
+	return 0;
+}
+
+/* release sequencer memory */
+void __exit snd_sequencer_memory_done(void)
+{
+}
+
+
+/* exported to seq_clientmgr.c */
+void snd_seq_info_pool(struct snd_info_buffer *buffer,
+		       struct snd_seq_pool *pool, char *space)
+{
+	if (pool == NULL)
+		return;
+	snd_iprintf(buffer, "%sPool size          : %d\n", space, pool->total_elements);
+	snd_iprintf(buffer, "%sCells in use       : %d\n", space, atomic_read(&pool->counter));
+	snd_iprintf(buffer, "%sPeak cells in use  : %d\n", space, pool->max_used);
+	snd_iprintf(buffer, "%sAlloc success      : %d\n", space, pool->event_alloc_success);
+	snd_iprintf(buffer, "%sAlloc failures     : %d\n", space, pool->event_alloc_failures);
+}
Index: /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_memory.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_memory.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_memory.h	(revision 91)
@@ -0,0 +1,103 @@
+/*
+ *  ALSA sequencer Memory Manager
+ *  Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+#ifndef __SND_SEQ_MEMORYMGR_H
+#define __SND_SEQ_MEMORYMGR_H
+
+#include <sound/seq_kernel.h>
+#include <linux/poll.h>
+
+/* container for sequencer event (internal use) */
+struct snd_seq_event_cell {
+	struct snd_seq_event event;
+	struct snd_seq_pool *pool;				/* used pool */
+	struct snd_seq_event_cell *next;	/* next cell */
+};
+
+/* design note: the pool is a contigious block of memory, if we dynamicly
+   want to add additional cells to the pool be better store this in another
+   pool as we need to know the base address of the pool when releasing
+   memory. */
+
+struct snd_seq_pool {
+	struct snd_seq_event_cell *ptr;	/* pointer to first event chunk */
+	struct snd_seq_event_cell *free;	/* pointer to the head of the free list */
+
+	int total_elements;	/* pool size actually allocated */
+	atomic_t counter;	/* cells free */
+
+	int size;		/* pool size to be allocated */
+	int room;		/* watermark for sleep/wakeup */
+
+	int closing;
+
+	/* statistics */
+	int max_used;
+	int event_alloc_nopool;
+	int event_alloc_failures;
+	int event_alloc_success;
+
+	/* Write locking */
+	wait_queue_head_t output_sleep;
+
+	/* Pool lock */
+	spinlock_t lock;
+};
+
+void snd_seq_cell_free(struct snd_seq_event_cell *cell);
+
+int snd_seq_event_dup(struct snd_seq_pool *pool, struct snd_seq_event *event,
+		      struct snd_seq_event_cell **cellp, int nonblock, struct file *file);
+
+/* return number of unused (free) cells */
+static inline int snd_seq_unused_cells(struct snd_seq_pool *pool)
+{
+	return pool ? pool->total_elements - atomic_read(&pool->counter) : 0;
+}
+
+/* return total number of allocated cells */
+static inline int snd_seq_total_cells(struct snd_seq_pool *pool)
+{
+	return pool ? pool->total_elements : 0;
+}
+
+/* init pool - allocate events */
+int snd_seq_pool_init(struct snd_seq_pool *pool);
+
+/* done pool - free events */
+int snd_seq_pool_done(struct snd_seq_pool *pool);
+
+/* create pool */
+struct snd_seq_pool *snd_seq_pool_new(int poolsize);
+
+/* remove pool */
+int snd_seq_pool_delete(struct snd_seq_pool **pool);
+
+/* init memory */
+int snd_sequencer_memory_init(void);
+            
+/* release event memory */
+void snd_sequencer_memory_done(void);
+
+/* polling */
+int snd_seq_pool_poll_wait(struct snd_seq_pool *pool, struct file *file, poll_table *wait);
+
+
+#endif
Index: /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_midi.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_midi.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_midi.c	(revision 91)
@@ -0,0 +1,485 @@
+/*
+ *   Generic MIDI synth driver for ALSA sequencer
+ *   Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
+ *                         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
+ *
+ */
+ 
+/* 
+Possible options for midisynth module:
+	- automatic opening of midi ports on first received event or subscription
+	  (close will be performed when client leaves)
+*/
+
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/moduleparam.h>
+#include <asm/semaphore.h>
+#include <sound/core.h>
+#include <sound/rawmidi.h>
+#include <sound/seq_kernel.h>
+#include <sound/seq_device.h>
+#include <sound/seq_midi_event.h>
+#include <sound/initval.h>
+
+MODULE_AUTHOR("Frank van de Pol <fvdpol@coil.demon.nl>, Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Advanced Linux Sound Architecture sequencer MIDI synth.");
+MODULE_LICENSE("GPL");
+static int output_buffer_size = PAGE_SIZE;
+module_param(output_buffer_size, int, 0644);
+MODULE_PARM_DESC(output_buffer_size, "Output buffer size in bytes.");
+static int input_buffer_size = PAGE_SIZE;
+module_param(input_buffer_size, int, 0644);
+MODULE_PARM_DESC(input_buffer_size, "Input buffer size in bytes.");
+
+/* data for this midi synth driver */
+struct seq_midisynth {
+	struct snd_card *card;
+	int device;
+	int subdevice;
+	struct snd_rawmidi_file input_rfile;
+	struct snd_rawmidi_file output_rfile;
+	int seq_client;
+	int seq_port;
+	struct snd_midi_event *parser;
+};
+
+struct seq_midisynth_client {
+	int seq_client;
+	int num_ports;
+	int ports_per_device[SNDRV_RAWMIDI_DEVICES];
+ 	struct seq_midisynth *ports[SNDRV_RAWMIDI_DEVICES];
+};
+
+static struct seq_midisynth_client *synths[SNDRV_CARDS];
+static DECLARE_MUTEX(register_mutex);
+
+/* handle rawmidi input event (MIDI v1.0 stream) */
+static void snd_midi_input_event(struct snd_rawmidi_substream *substream)
+{
+	struct snd_rawmidi_runtime *runtime;
+	struct seq_midisynth *msynth;
+	struct snd_seq_event ev;
+	char buf[16], *pbuf;
+	long res, count;
+
+	if (substream == NULL)
+		return;
+	runtime = substream->runtime;
+	msynth = runtime->private_data;
+	if (msynth == NULL)
+		return;
+	memset(&ev, 0, sizeof(ev));
+	while (runtime->avail > 0) {
+		res = snd_rawmidi_kernel_read(substream, buf, sizeof(buf));
+		if (res <= 0)
+			continue;
+		if (msynth->parser == NULL)
+			continue;
+		pbuf = buf;
+		while (res > 0) {
+			count = snd_midi_event_encode(msynth->parser, pbuf, res, &ev);
+			if (count < 0)
+				break;
+			pbuf += count;
+			res -= count;
+			if (ev.type != SNDRV_SEQ_EVENT_NONE) {
+				ev.source.port = msynth->seq_port;
+				ev.dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS;
+				snd_seq_kernel_client_dispatch(msynth->seq_client, &ev, 1, 0);
+				/* clear event and reset header */
+				memset(&ev, 0, sizeof(ev));
+			}
+		}
+	}
+}
+
+static int dump_midi(struct snd_rawmidi_substream *substream, const char *buf, int count)
+{
+	struct snd_rawmidi_runtime *runtime;
+	int tmp;
+
+	snd_assert(substream != NULL || buf != NULL, return -EINVAL);
+	runtime = substream->runtime;
+	if ((tmp = runtime->avail) < count) {
+		snd_printd("warning, output event was lost (count = %i, available = %i)\n", count, tmp);
+		return -ENOMEM;
+	}
+	if (snd_rawmidi_kernel_write(substream, buf, count) < count)
+		return -EINVAL;
+	return 0;
+}
+
+static int event_process_midi(struct snd_seq_event *ev, int direct,
+			      void *private_data, int atomic, int hop)
+{
+	struct seq_midisynth *msynth = private_data;
+	unsigned char msg[10];	/* buffer for constructing midi messages */
+	struct snd_rawmidi_substream *substream;
+	int len;
+
+	snd_assert(msynth != NULL, return -EINVAL);
+	substream = msynth->output_rfile.output;
+	if (substream == NULL)
+		return -ENODEV;
+	if (ev->type == SNDRV_SEQ_EVENT_SYSEX) {	/* special case, to save space */
+		if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARIABLE) {
+			/* invalid event */
+			snd_printd("seq_midi: invalid sysex event flags = 0x%x\n", ev->flags);
+			return 0;
+		}
+		snd_seq_dump_var_event(ev, (snd_seq_dump_func_t)dump_midi, substream);
+		snd_midi_event_reset_decode(msynth->parser);
+	} else {
+		if (msynth->parser == NULL)
+			return -EIO;
+		len = snd_midi_event_decode(msynth->parser, msg, sizeof(msg), ev);
+		if (len < 0)
+			return 0;
+		if (dump_midi(substream, msg, len) < 0)
+			snd_midi_event_reset_decode(msynth->parser);
+	}
+	return 0;
+}
+
+
+static int snd_seq_midisynth_new(struct seq_midisynth *msynth,
+				 struct snd_card *card,
+				 int device,
+				 int subdevice)
+{
+	if (snd_midi_event_new(MAX_MIDI_EVENT_BUF, &msynth->parser) < 0)
+		return -ENOMEM;
+	msynth->card = card;
+	msynth->device = device;
+	msynth->subdevice = subdevice;
+	return 0;
+}
+
+/* open associated midi device for input */
+static int midisynth_subscribe(void *private_data, struct snd_seq_port_subscribe *info)
+{
+	int err;
+	struct seq_midisynth *msynth = private_data;
+	struct snd_rawmidi_runtime *runtime;
+	struct snd_rawmidi_params params;
+
+	/* open midi port */
+	if ((err = snd_rawmidi_kernel_open(msynth->card->number, msynth->device, msynth->subdevice, SNDRV_RAWMIDI_LFLG_INPUT, &msynth->input_rfile)) < 0) {
+		snd_printd("midi input open failed!!!\n");
+		return err;
+	}
+	runtime = msynth->input_rfile.input->runtime;
+	memset(&params, 0, sizeof(params));
+	params.avail_min = 1;
+	params.buffer_size = input_buffer_size;
+	if ((err = snd_rawmidi_input_params(msynth->input_rfile.input, &params)) < 0) {
+		snd_rawmidi_kernel_release(&msynth->input_rfile);
+		return err;
+	}
+	snd_midi_event_reset_encode(msynth->parser);
+	runtime->event = snd_midi_input_event;
+	runtime->private_data = msynth;
+	snd_rawmidi_kernel_read(msynth->input_rfile.input, NULL, 0);
+	return 0;
+}
+
+/* close associated midi device for input */
+static int midisynth_unsubscribe(void *private_data, struct snd_seq_port_subscribe *info)
+{
+	int err;
+	struct seq_midisynth *msynth = private_data;
+
+	snd_assert(msynth->input_rfile.input != NULL, return -EINVAL);
+	err = snd_rawmidi_kernel_release(&msynth->input_rfile);
+	return err;
+}
+
+/* open associated midi device for output */
+static int midisynth_use(void *private_data, struct snd_seq_port_subscribe *info)
+{
+	int err;
+	struct seq_midisynth *msynth = private_data;
+	struct snd_rawmidi_params params;
+
+	/* open midi port */
+	if ((err = snd_rawmidi_kernel_open(msynth->card->number, msynth->device, msynth->subdevice, SNDRV_RAWMIDI_LFLG_OUTPUT, &msynth->output_rfile)) < 0) {
+		snd_printd("midi output open failed!!!\n");
+		return err;
+	}
+	memset(&params, 0, sizeof(params));
+	params.avail_min = 1;
+	params.buffer_size = output_buffer_size;
+	if ((err = snd_rawmidi_output_params(msynth->output_rfile.output, &params)) < 0) {
+		snd_rawmidi_kernel_release(&msynth->output_rfile);
+		return err;
+	}
+	snd_midi_event_reset_decode(msynth->parser);
+	return 0;
+}
+
+/* close associated midi device for output */
+static int midisynth_unuse(void *private_data, struct snd_seq_port_subscribe *info)
+{
+	struct seq_midisynth *msynth = private_data;
+	unsigned char buf = 0xff; /* MIDI reset */
+
+	snd_assert(msynth->output_rfile.output != NULL, return -EINVAL);
+	/* sending single MIDI reset message to shut the device up */
+	snd_rawmidi_kernel_write(msynth->output_rfile.output, &buf, 1);
+	snd_rawmidi_drain_output(msynth->output_rfile.output);
+	return snd_rawmidi_kernel_release(&msynth->output_rfile);
+}
+
+/* delete given midi synth port */
+static void snd_seq_midisynth_delete(struct seq_midisynth *msynth)
+{
+	if (msynth == NULL)
+		return;
+
+	if (msynth->seq_client > 0) {
+		/* delete port */
+		snd_seq_event_port_detach(msynth->seq_client, msynth->seq_port);
+	}
+
+	if (msynth->parser)
+		snd_midi_event_free(msynth->parser);
+}
+
+/* set our client name */
+static int set_client_name(struct seq_midisynth_client *client, struct snd_card *card,
+			   struct snd_rawmidi_info *rmidi)
+{
+	struct snd_seq_client_info cinfo;
+	const char *name;
+
+	memset(&cinfo, 0, sizeof(cinfo));
+	cinfo.client = client->seq_client;
+	cinfo.type = KERNEL_CLIENT;
+	name = rmidi->name[0] ? (const char *)rmidi->name : "External MIDI";
+	strlcpy(cinfo.name, name, sizeof(cinfo.name));
+	return snd_seq_kernel_client_ctl(client->seq_client, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, &cinfo);
+}
+
+/* register new midi synth port */
+static int
+snd_seq_midisynth_register_port(struct snd_seq_device *dev)
+{
+	struct seq_midisynth_client *client;
+	struct seq_midisynth *msynth, *ms;
+	struct snd_seq_port_info *port;
+	struct snd_rawmidi_info *info;
+	int newclient = 0;
+	unsigned int p, ports;
+	struct snd_seq_client_callback callbacks;
+	struct snd_seq_port_callback pcallbacks;
+	struct snd_card *card = dev->card;
+	int device = dev->device;
+	unsigned int input_count = 0, output_count = 0;
+
+	snd_assert(card != NULL && device >= 0 && device < SNDRV_RAWMIDI_DEVICES, return -EINVAL);
+	info = kmalloc(sizeof(*info), GFP_KERNEL);
+	if (! info)
+		return -ENOMEM;
+	info->device = device;
+	info->stream = SNDRV_RAWMIDI_STREAM_OUTPUT;
+	info->subdevice = 0;
+	if (snd_rawmidi_info_select(card, info) >= 0)
+		output_count = info->subdevices_count;
+	info->stream = SNDRV_RAWMIDI_STREAM_INPUT;
+	if (snd_rawmidi_info_select(card, info) >= 0) {
+		input_count = info->subdevices_count;
+	}
+	ports = output_count;
+	if (ports < input_count)
+		ports = input_count;
+	if (ports == 0) {
+		kfree(info);
+		return -ENODEV;
+	}
+	if (ports > (256 / SNDRV_RAWMIDI_DEVICES))
+		ports = 256 / SNDRV_RAWMIDI_DEVICES;
+
+	down(&register_mutex);
+	client = synths[card->number];
+	if (client == NULL) {
+		newclient = 1;
+		client = kzalloc(sizeof(*client), GFP_KERNEL);
+		if (client == NULL) {
+			up(&register_mutex);
+			kfree(info);
+			return -ENOMEM;
+		}
+		memset(&callbacks, 0, sizeof(callbacks));
+		callbacks.private_data = client;
+		callbacks.allow_input = callbacks.allow_output = 1;
+		client->seq_client = snd_seq_create_kernel_client(card, 0, &callbacks);
+		if (client->seq_client < 0) {
+			kfree(client);
+			up(&register_mutex);
+			kfree(info);
+			return -ENOMEM;
+		}
+		set_client_name(client, card, info);
+	} else if (device == 0)
+		set_client_name(client, card, info); /* use the first device's name */
+
+	msynth = kcalloc(ports, sizeof(struct seq_midisynth), GFP_KERNEL);
+	port = kmalloc(sizeof(*port), GFP_KERNEL);
+	if (msynth == NULL || port == NULL)
+		goto __nomem;
+
+	for (p = 0; p < ports; p++) {
+		ms = &msynth[p];
+
+		if (snd_seq_midisynth_new(ms, card, device, p) < 0)
+			goto __nomem;
+
+		/* declare port */
+		memset(port, 0, sizeof(*port));
+		port->addr.client = client->seq_client;
+		port->addr.port = device * (256 / SNDRV_RAWMIDI_DEVICES) + p;
+		port->flags = SNDRV_SEQ_PORT_FLG_GIVEN_PORT;
+		memset(info, 0, sizeof(*info));
+		info->device = device;
+		if (p < output_count)
+			info->stream = SNDRV_RAWMIDI_STREAM_OUTPUT;
+		else
+			info->stream = SNDRV_RAWMIDI_STREAM_INPUT;
+		info->subdevice = p;
+		if (snd_rawmidi_info_select(card, info) >= 0)
+			strcpy(port->name, info->subname);
+		if (! port->name[0]) {
+			if (info->name[0]) {
+				if (ports > 1)
+					sprintf(port->name, "%s-%d", info->name, p);
+				else
+					sprintf(port->name, "%s", info->name);
+			} else {
+				/* last resort */
+				if (ports > 1)
+					sprintf(port->name, "MIDI %d-%d-%d", card->number, device, p);
+				else
+					sprintf(port->name, "MIDI %d-%d", card->number, device);
+			}
+		}
+		if ((info->flags & SNDRV_RAWMIDI_INFO_OUTPUT) && p < output_count)
+			port->capability |= SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SYNC_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE;
+		if ((info->flags & SNDRV_RAWMIDI_INFO_INPUT) && p < input_count)
+			port->capability |= SNDRV_SEQ_PORT_CAP_READ | SNDRV_SEQ_PORT_CAP_SYNC_READ | SNDRV_SEQ_PORT_CAP_SUBS_READ;
+		if ((port->capability & (SNDRV_SEQ_PORT_CAP_WRITE|SNDRV_SEQ_PORT_CAP_READ)) == (SNDRV_SEQ_PORT_CAP_WRITE|SNDRV_SEQ_PORT_CAP_READ) &&
+		    info->flags & SNDRV_RAWMIDI_INFO_DUPLEX)
+			port->capability |= SNDRV_SEQ_PORT_CAP_DUPLEX;
+		port->type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC;
+		port->midi_channels = 16;
+		memset(&pcallbacks, 0, sizeof(pcallbacks));
+		pcallbacks.owner = THIS_MODULE;
+		pcallbacks.private_data = ms;
+		pcallbacks.subscribe = midisynth_subscribe;
+		pcallbacks.unsubscribe = midisynth_unsubscribe;
+		pcallbacks.use = midisynth_use;
+		pcallbacks.unuse = midisynth_unuse;
+		pcallbacks.event_input = event_process_midi;
+		port->kernel = &pcallbacks;
+		if (snd_seq_kernel_client_ctl(client->seq_client, SNDRV_SEQ_IOCTL_CREATE_PORT, port)<0)
+			goto __nomem;
+		ms->seq_client = client->seq_client;
+		ms->seq_port = port->addr.port;
+	}
+	client->ports_per_device[device] = ports;
+	client->ports[device] = msynth;
+	client->num_ports++;
+	if (newclient)
+		synths[card->number] = client;
+	up(&register_mutex);
+	kfree(info);
+	kfree(port);
+	return 0;	/* success */
+
+      __nomem:
+	if (msynth != NULL) {
+	      	for (p = 0; p < ports; p++)
+	      		snd_seq_midisynth_delete(&msynth[p]);
+		kfree(msynth);
+	}
+	if (newclient) {
+		snd_seq_delete_kernel_client(client->seq_client);
+		kfree(client);
+	}
+	kfree(info);
+	kfree(port);
+	up(&register_mutex);
+	return -ENOMEM;
+}
+
+/* release midi synth port */
+static int
+snd_seq_midisynth_unregister_port(struct snd_seq_device *dev)
+{
+	struct seq_midisynth_client *client;
+	struct seq_midisynth *msynth;
+	struct snd_card *card = dev->card;
+	int device = dev->device, p, ports;
+	
+	down(&register_mutex);
+	client = synths[card->number];
+	if (client == NULL || client->ports[device] == NULL) {
+		up(&register_mutex);
+		return -ENODEV;
+	}
+	ports = client->ports_per_device[device];
+	client->ports_per_device[device] = 0;
+	msynth = client->ports[device];
+	client->ports[device] = NULL;
+	for (p = 0; p < ports; p++)
+		snd_seq_midisynth_delete(&msynth[p]);
+	kfree(msynth);
+	client->num_ports--;
+	if (client->num_ports <= 0) {
+		snd_seq_delete_kernel_client(client->seq_client);
+		synths[card->number] = NULL;
+		kfree(client);
+	}
+	up(&register_mutex);
+	return 0;
+}
+
+
+static int __init alsa_seq_midi_init(void)
+{
+	static struct snd_seq_dev_ops ops = {
+		snd_seq_midisynth_register_port,
+		snd_seq_midisynth_unregister_port,
+	};
+	memset(&synths, 0, sizeof(synths));
+	snd_seq_autoload_lock();
+	snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_MIDISYNTH, &ops, 0);
+	snd_seq_autoload_unlock();
+	return 0;
+}
+
+static void __exit alsa_seq_midi_exit(void)
+{
+	snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_MIDISYNTH);
+}
+
+module_init(alsa_seq_midi_init)
+module_exit(alsa_seq_midi_exit)
Index: /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_midi_clock.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_midi_clock.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_midi_clock.c	(revision 91)
@@ -0,0 +1,89 @@
+/*
+ *  MIDI clock event converter
+ *
+ *  Copyright (c) 2000 by 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <sound/driver.h>
+#include "seq_queue.h"
+
+#ifdef SNDRV_SEQ_SYNC_SUPPORT
+
+typedef struct midi_clock {
+	unsigned int cur_pos;
+} midi_clock_t;
+
+static int midi_open(queue_sync_t *sync_info, seq_sync_arg_t *retp)
+{
+	midi_clock_t *arg;
+
+	if ((arg = kmalloc(sizeof(*arg), GFP_KERNEL)) == NULL)
+		return -ENOMEM;
+	sync_info->param.tick.ppq = 24;
+	sync_info->param.tick.ticks = 1;
+	arg->cur_pos = 0;
+	*retp = arg;
+	return 0;
+}
+
+static int midi_sync_out(seq_sync_arg_t _arg, const snd_seq_event_t *src, snd_seq_event_t *ev)
+{
+	switch (src->type) {
+	case SNDRV_SEQ_EVENT_SYNC:
+		ev->type = SNDRV_SEQ_EVENT_CLOCK;
+		return 1;
+	case SNDRV_SEQ_EVENT_SYNC_POS:
+		ev->type = SNDRV_SEQ_EVENT_SONGPOS;
+		ev->data.control.value = src->data.queue.param.position / 6;
+		return 1;
+	}
+	return 0;
+}
+
+static int midi_sync_in(seq_sync_arg_t _arg, const snd_seq_event_t *src, snd_seq_event_t *ev)
+{
+	midi_clock_t *arg = _arg;
+	switch (src->type) {
+	case SNDRV_SEQ_EVENT_CLOCK:
+		ev->type = SNDRV_SEQ_EVENT_SYNC;
+		ev->data.queue.param.position = arg->cur_pos;
+		arg->cur_pos++;
+		return 1;
+	case SNDRV_SEQ_EVENT_SONGPOS:
+		ev->type = SNDRV_SEQ_EVENT_SYNC_POS;
+		arg->cur_pos = src->data.control.value * 6;
+		ev->data.queue.param.position = arg->cur_pos;
+		return 1;
+	}
+	return 0;
+}
+
+/* exported */
+seq_sync_parser_t snd_seq_midi_clock_parser = {
+	format: SNDRV_SEQ_SYNC_FMT_MIDI_CLOCK,
+	in: {
+		open: midi_open,
+		sync: midi_sync_in,
+	},
+	out: {
+		open: midi_open,
+		sync: midi_sync_out,
+	},
+};
+
+#endif /* SNDRV_SEQ_SYNC_SUPPORT */
Index: /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_midi_emul.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_midi_emul.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_midi_emul.c	(revision 91)
@@ -0,0 +1,747 @@
+/*
+ *  GM/GS/XG midi module.
+ *
+ *  Copyright (C) 1999 Steve Ratcliffe
+ *
+ *  Based on awe_wave.c by Takashi Iwai
+ *
+ *   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
+ *
+ */
+/*
+ * This module is used to keep track of the current midi state.
+ * It can be used for drivers that are required to emulate midi when
+ * the hardware doesn't.
+ *
+ * It was written for a AWE64 driver, but there should be no AWE specific
+ * code in here.  If there is it should be reported as a bug.
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <sound/core.h>
+#include <sound/seq_kernel.h>
+#include <sound/seq_midi_emul.h>
+#include <sound/initval.h>
+#include <sound/asoundef.h>
+
+MODULE_AUTHOR("Takashi Iwai / Steve Ratcliffe");
+MODULE_DESCRIPTION("Advanced Linux Sound Architecture sequencer MIDI emulation.");
+MODULE_LICENSE("GPL");
+
+/* Prototypes for static functions */
+static void note_off(struct snd_midi_op *ops, void *drv,
+		     struct snd_midi_channel *chan,
+		     int note, int vel);
+static void do_control(struct snd_midi_op *ops, void *private,
+		       struct snd_midi_channel_set *chset,
+		       struct snd_midi_channel *chan,
+		       int control, int value);
+static void rpn(struct snd_midi_op *ops, void *drv, struct snd_midi_channel *chan,
+		struct snd_midi_channel_set *chset);
+static void nrpn(struct snd_midi_op *ops, void *drv, struct snd_midi_channel *chan,
+		 struct snd_midi_channel_set *chset);
+static void sysex(struct snd_midi_op *ops, void *private, unsigned char *sysex,
+		  int len, struct snd_midi_channel_set *chset);
+static void all_sounds_off(struct snd_midi_op *ops, void *private,
+			   struct snd_midi_channel *chan);
+static void all_notes_off(struct snd_midi_op *ops, void *private,
+			  struct snd_midi_channel *chan);
+static void snd_midi_reset_controllers(struct snd_midi_channel *chan);
+static void reset_all_channels(struct snd_midi_channel_set *chset);
+
+
+/*
+ * Process an event in a driver independent way.  This means dealing
+ * with RPN, NRPN, SysEx etc that are defined for common midi applications
+ * such as GM, GS and XG.
+ * There modes that this module will run in are:
+ *   Generic MIDI - no interpretation at all, it will just save current values
+ *                  of controlers etc.
+ *   GM - You can use all gm_ prefixed elements of chan.  Controls, RPN, NRPN,
+ *        SysEx will be interpreded as defined in General Midi.
+ *   GS - You can use all gs_ prefixed elements of chan. Codes for GS will be
+ *        interpreted.
+ *   XG - You can use all xg_ prefixed elements of chan.  Codes for XG will
+ *        be interpreted.
+ */
+void
+snd_midi_process_event(struct snd_midi_op *ops,
+		       struct snd_seq_event *ev,
+		       struct snd_midi_channel_set *chanset)
+{
+	struct snd_midi_channel *chan;
+	void *drv;
+	int dest_channel = 0;
+
+	if (ev == NULL || chanset == NULL) {
+		snd_printd("ev or chanbase NULL (snd_midi_process_event)\n");
+		return;
+	}
+	if (chanset->channels == NULL)
+		return;
+
+	if (snd_seq_ev_is_channel_type(ev)) {
+		dest_channel = ev->data.note.channel;
+		if (dest_channel >= chanset->max_channels) {
+			snd_printd("dest channel is %d, max is %d\n",
+				   dest_channel, chanset->max_channels);
+			return;
+		}
+	}
+
+	chan = chanset->channels + dest_channel;
+	drv  = chanset->private_data;
+
+	/* EVENT_NOTE should be processed before queued */
+	if (ev->type == SNDRV_SEQ_EVENT_NOTE)
+		return;
+
+	/* Make sure that we don't have a note on that should really be
+	 * a note off */
+	if (ev->type == SNDRV_SEQ_EVENT_NOTEON && ev->data.note.velocity == 0)
+		ev->type = SNDRV_SEQ_EVENT_NOTEOFF;
+
+	/* Make sure the note is within array range */
+	if (ev->type == SNDRV_SEQ_EVENT_NOTEON ||
+	    ev->type == SNDRV_SEQ_EVENT_NOTEOFF ||
+	    ev->type == SNDRV_SEQ_EVENT_KEYPRESS) {
+		if (ev->data.note.note >= 128)
+			return;
+	}
+
+	switch (ev->type) {
+	case SNDRV_SEQ_EVENT_NOTEON:
+		if (chan->note[ev->data.note.note] & SNDRV_MIDI_NOTE_ON) {
+			if (ops->note_off)
+				ops->note_off(drv, ev->data.note.note, 0, chan);
+		}
+		chan->note[ev->data.note.note] = SNDRV_MIDI_NOTE_ON;
+		if (ops->note_on)
+			ops->note_on(drv, ev->data.note.note, ev->data.note.velocity, chan);
+		break;
+	case SNDRV_SEQ_EVENT_NOTEOFF:
+		if (! (chan->note[ev->data.note.note] & SNDRV_MIDI_NOTE_ON))
+			break;
+		if (ops->note_off)
+			note_off(ops, drv, chan, ev->data.note.note, ev->data.note.velocity);
+		break;
+	case SNDRV_SEQ_EVENT_KEYPRESS:
+		if (ops->key_press)
+			ops->key_press(drv, ev->data.note.note, ev->data.note.velocity, chan);
+		break;
+	case SNDRV_SEQ_EVENT_CONTROLLER:
+		do_control(ops, drv, chanset, chan,
+			   ev->data.control.param, ev->data.control.value);
+		break;
+	case SNDRV_SEQ_EVENT_PGMCHANGE:
+		chan->midi_program = ev->data.control.value;
+		break;
+	case SNDRV_SEQ_EVENT_PITCHBEND:
+		chan->midi_pitchbend = ev->data.control.value;
+		if (ops->control)
+			ops->control(drv, MIDI_CTL_PITCHBEND, chan);
+		break;
+	case SNDRV_SEQ_EVENT_CHANPRESS:
+		chan->midi_pressure = ev->data.control.value;
+		if (ops->control)
+			ops->control(drv, MIDI_CTL_CHAN_PRESSURE, chan);
+		break;
+	case SNDRV_SEQ_EVENT_CONTROL14:
+		/* Best guess is that this is any of the 14 bit controller values */
+		if (ev->data.control.param < 32) {
+			/* set low part first */
+			chan->control[ev->data.control.param + 32] =
+				ev->data.control.value & 0x7f;
+			do_control(ops, drv, chanset, chan,
+				   ev->data.control.param,
+				   ((ev->data.control.value>>7) & 0x7f));
+		} else
+			do_control(ops, drv, chanset, chan,
+				   ev->data.control.param,
+				   ev->data.control.value);
+		break;
+	case SNDRV_SEQ_EVENT_NONREGPARAM:
+		/* Break it back into its controler values */
+		chan->param_type = SNDRV_MIDI_PARAM_TYPE_NONREGISTERED;
+		chan->control[MIDI_CTL_MSB_DATA_ENTRY]
+			= (ev->data.control.value >> 7) & 0x7f;
+		chan->control[MIDI_CTL_LSB_DATA_ENTRY]
+			= ev->data.control.value & 0x7f;
+		chan->control[MIDI_CTL_NONREG_PARM_NUM_MSB]
+			= (ev->data.control.param >> 7) & 0x7f;
+		chan->control[MIDI_CTL_NONREG_PARM_NUM_LSB]
+			= ev->data.control.param & 0x7f;
+		nrpn(ops, drv, chan, chanset);
+		break;
+	case SNDRV_SEQ_EVENT_REGPARAM:
+		/* Break it back into its controler values */
+		chan->param_type = SNDRV_MIDI_PARAM_TYPE_REGISTERED;
+		chan->control[MIDI_CTL_MSB_DATA_ENTRY]
+			= (ev->data.control.value >> 7) & 0x7f;
+		chan->control[MIDI_CTL_LSB_DATA_ENTRY]
+			= ev->data.control.value & 0x7f;
+		chan->control[MIDI_CTL_REGIST_PARM_NUM_MSB]
+			= (ev->data.control.param >> 7) & 0x7f;
+		chan->control[MIDI_CTL_REGIST_PARM_NUM_LSB]
+			= ev->data.control.param & 0x7f;
+		rpn(ops, drv, chan, chanset);
+		break;
+	case SNDRV_SEQ_EVENT_SYSEX:
+		if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) == SNDRV_SEQ_EVENT_LENGTH_VARIABLE) {
+			unsigned char sysexbuf[64];
+			int len;
+			len = snd_seq_expand_var_event(ev, sizeof(sysexbuf), sysexbuf, 1, 0);
+			if (len > 0)
+				sysex(ops, drv, sysexbuf, len, chanset);
+		}
+		break;
+	case SNDRV_SEQ_EVENT_SONGPOS:
+	case SNDRV_SEQ_EVENT_SONGSEL:
+	case SNDRV_SEQ_EVENT_CLOCK:
+	case SNDRV_SEQ_EVENT_START:
+	case SNDRV_SEQ_EVENT_CONTINUE:
+	case SNDRV_SEQ_EVENT_STOP:
+	case SNDRV_SEQ_EVENT_QFRAME:
+	case SNDRV_SEQ_EVENT_TEMPO:
+	case SNDRV_SEQ_EVENT_TIMESIGN:
+	case SNDRV_SEQ_EVENT_KEYSIGN:
+		goto not_yet;
+	case SNDRV_SEQ_EVENT_SENSING:
+		break;
+	case SNDRV_SEQ_EVENT_CLIENT_START:
+	case SNDRV_SEQ_EVENT_CLIENT_EXIT:
+	case SNDRV_SEQ_EVENT_CLIENT_CHANGE:
+	case SNDRV_SEQ_EVENT_PORT_START:
+	case SNDRV_SEQ_EVENT_PORT_EXIT:
+	case SNDRV_SEQ_EVENT_PORT_CHANGE:
+	case SNDRV_SEQ_EVENT_SAMPLE:
+	case SNDRV_SEQ_EVENT_SAMPLE_START:
+	case SNDRV_SEQ_EVENT_SAMPLE_STOP:
+	case SNDRV_SEQ_EVENT_SAMPLE_FREQ:
+	case SNDRV_SEQ_EVENT_SAMPLE_VOLUME:
+	case SNDRV_SEQ_EVENT_SAMPLE_LOOP:
+	case SNDRV_SEQ_EVENT_SAMPLE_POSITION:
+	case SNDRV_SEQ_EVENT_ECHO:
+	not_yet:
+	default:
+		/*snd_printd("Unimplemented event %d\n", ev->type);*/
+		break;
+	}
+}
+
+
+/*
+ * release note
+ */
+static void
+note_off(struct snd_midi_op *ops, void *drv, struct snd_midi_channel *chan,
+	 int note, int vel)
+{
+	if (chan->gm_hold) {
+		/* Hold this note until pedal is turned off */
+		chan->note[note] |= SNDRV_MIDI_NOTE_RELEASED;
+	} else if (chan->note[note] & SNDRV_MIDI_NOTE_SOSTENUTO) {
+		/* Mark this note as release; it will be turned off when sostenuto
+		 * is turned off */
+		chan->note[note] |= SNDRV_MIDI_NOTE_RELEASED;
+	} else {
+		chan->note[note] = 0;
+		if (ops->note_off)
+			ops->note_off(drv, note, vel, chan);
+	}
+}
+
+/*
+ * Do all driver independent operations for this controler and pass
+ * events that need to take place immediately to the driver.
+ */
+static void
+do_control(struct snd_midi_op *ops, void *drv, struct snd_midi_channel_set *chset,
+	   struct snd_midi_channel *chan, int control, int value)
+{
+	int  i;
+
+	/* Switches */
+	if ((control >=64 && control <=69) || (control >= 80 && control <= 83)) {
+		/* These are all switches; either off or on so set to 0 or 127 */
+		value = (value >= 64)? 127: 0;
+	}
+	chan->control[control] = value;
+
+	switch (control) {
+	case MIDI_CTL_SUSTAIN:
+		if (value == 0) {
+			/* Sustain has been released, turn off held notes */
+			for (i = 0; i < 128; i++) {
+				if (chan->note[i] & SNDRV_MIDI_NOTE_RELEASED) {
+					chan->note[i] = SNDRV_MIDI_NOTE_OFF;
+					if (ops->note_off)
+						ops->note_off(drv, i, 0, chan);
+				}
+			}
+		}
+		break;
+	case MIDI_CTL_PORTAMENTO:
+		break;
+	case MIDI_CTL_SOSTENUTO:
+		if (value) {
+			/* Mark each note that is currently held down */
+			for (i = 0; i < 128; i++) {
+				if (chan->note[i] & SNDRV_MIDI_NOTE_ON)
+					chan->note[i] |= SNDRV_MIDI_NOTE_SOSTENUTO;
+			}
+		} else {
+			/* release all notes that were held */
+			for (i = 0; i < 128; i++) {
+				if (chan->note[i] & SNDRV_MIDI_NOTE_SOSTENUTO) {
+					chan->note[i] &= ~SNDRV_MIDI_NOTE_SOSTENUTO;
+					if (chan->note[i] & SNDRV_MIDI_NOTE_RELEASED) {
+						chan->note[i] = SNDRV_MIDI_NOTE_OFF;
+						if (ops->note_off)
+							ops->note_off(drv, i, 0, chan);
+					}
+				}
+			}
+		}
+		break;
+	case MIDI_CTL_MSB_DATA_ENTRY:
+		chan->control[MIDI_CTL_LSB_DATA_ENTRY] = 0;
+		/* go through here */
+	case MIDI_CTL_LSB_DATA_ENTRY:
+		if (chan->param_type == SNDRV_MIDI_PARAM_TYPE_REGISTERED)
+			rpn(ops, drv, chan, chset);
+		else
+			nrpn(ops, drv, chan, chset);
+		break;
+	case MIDI_CTL_REGIST_PARM_NUM_LSB:
+	case MIDI_CTL_REGIST_PARM_NUM_MSB:
+		chan->param_type = SNDRV_MIDI_PARAM_TYPE_REGISTERED;
+		break;
+	case MIDI_CTL_NONREG_PARM_NUM_LSB:
+	case MIDI_CTL_NONREG_PARM_NUM_MSB:
+		chan->param_type = SNDRV_MIDI_PARAM_TYPE_NONREGISTERED;
+		break;
+
+	case MIDI_CTL_ALL_SOUNDS_OFF:
+		all_sounds_off(ops, drv, chan);
+		break;
+
+	case MIDI_CTL_ALL_NOTES_OFF:
+		all_notes_off(ops, drv, chan);
+		break;
+
+	case MIDI_CTL_MSB_BANK:
+		if (chset->midi_mode == SNDRV_MIDI_MODE_XG) {
+			if (value == 127)
+				chan->drum_channel = 1;
+			else
+				chan->drum_channel = 0;
+		}
+		break;
+	case MIDI_CTL_LSB_BANK:
+		break;
+
+	case MIDI_CTL_RESET_CONTROLLERS:
+		snd_midi_reset_controllers(chan);
+		break;
+
+	case MIDI_CTL_SOFT_PEDAL:
+	case MIDI_CTL_LEGATO_FOOTSWITCH:
+	case MIDI_CTL_HOLD2:
+	case MIDI_CTL_SC1_SOUND_VARIATION:
+	case MIDI_CTL_SC2_TIMBRE:
+	case MIDI_CTL_SC3_RELEASE_TIME:
+	case MIDI_CTL_SC4_ATTACK_TIME:
+	case MIDI_CTL_SC5_BRIGHTNESS:
+	case MIDI_CTL_E1_REVERB_DEPTH:
+	case MIDI_CTL_E2_TREMOLO_DEPTH:
+	case MIDI_CTL_E3_CHORUS_DEPTH:
+	case MIDI_CTL_E4_DETUNE_DEPTH:
+	case MIDI_CTL_E5_PHASER_DEPTH:
+		goto notyet;
+	notyet:
+	default:
+		if (ops->control)
+			ops->control(drv, control, chan);
+		break;
+	}
+}
+
+
+/*
+ * initialize the MIDI status
+ */
+void
+snd_midi_channel_set_clear(struct snd_midi_channel_set *chset)
+{
+	int i;
+
+	chset->midi_mode = SNDRV_MIDI_MODE_GM;
+	chset->gs_master_volume = 127;
+
+	for (i = 0; i < chset->max_channels; i++) {
+		struct snd_midi_channel *chan = chset->channels + i;
+		memset(chan->note, 0, sizeof(chan->note));
+
+		chan->midi_aftertouch = 0;
+		chan->midi_pressure = 0;
+		chan->midi_program = 0;
+		chan->midi_pitchbend = 0;
+		snd_midi_reset_controllers(chan);
+		chan->gm_rpn_pitch_bend_range = 256; /* 2 semitones */
+		chan->gm_rpn_fine_tuning = 0;
+		chan->gm_rpn_coarse_tuning = 0;
+
+		if (i == 9)
+			chan->drum_channel = 1;
+		else
+			chan->drum_channel = 0;
+	}
+}
+
+/*
+ * Process a rpn message.
+ */
+static void
+rpn(struct snd_midi_op *ops, void *drv, struct snd_midi_channel *chan,
+    struct snd_midi_channel_set *chset)
+{
+	int type;
+	int val;
+
+	if (chset->midi_mode != SNDRV_MIDI_MODE_NONE) {
+		type = (chan->control[MIDI_CTL_REGIST_PARM_NUM_MSB] << 8) |
+			chan->control[MIDI_CTL_REGIST_PARM_NUM_LSB];
+		val = (chan->control[MIDI_CTL_MSB_DATA_ENTRY] << 7) |
+			chan->control[MIDI_CTL_LSB_DATA_ENTRY];
+
+		switch (type) {
+		case 0x0000: /* Pitch bend sensitivity */
+			/* MSB only / 1 semitone per 128 */
+			chan->gm_rpn_pitch_bend_range = val;
+			break;
+					
+		case 0x0001: /* fine tuning: */
+			/* MSB/LSB, 8192=center, 100/8192 cent step */
+			chan->gm_rpn_fine_tuning = val - 8192;
+			break;
+
+		case 0x0002: /* coarse tuning */
+			/* MSB only / 8192=center, 1 semitone per 128 */
+			chan->gm_rpn_coarse_tuning = val - 8192;
+			break;
+
+		case 0x7F7F: /* "lock-in" RPN */
+			/* ignored */
+			break;
+		}
+	}
+	/* should call nrpn or rpn callback here.. */
+}
+
+/*
+ * Process an nrpn message.
+ */
+static void
+nrpn(struct snd_midi_op *ops, void *drv, struct snd_midi_channel *chan,
+     struct snd_midi_channel_set *chset)
+{
+	/* parse XG NRPNs here if possible */
+	if (ops->nrpn)
+		ops->nrpn(drv, chan, chset);
+}
+
+
+/*
+ * convert channel parameter in GS sysex
+ */
+static int
+get_channel(unsigned char cmd)
+{
+	int p = cmd & 0x0f;
+	if (p == 0)
+		p = 9;
+	else if (p < 10)
+		p--;
+	return p;
+}
+
+
+/*
+ * Process a sysex message.
+ */
+static void
+sysex(struct snd_midi_op *ops, void *private, unsigned char *buf, int len,
+      struct snd_midi_channel_set *chset)
+{
+	/* GM on */
+	static unsigned char gm_on_macro[] = {
+		0x7e,0x7f,0x09,0x01,
+	};
+	/* XG on */
+	static unsigned char xg_on_macro[] = {
+		0x43,0x10,0x4c,0x00,0x00,0x7e,0x00,
+	};
+	/* GS prefix
+	 * drum channel: XX=0x1?(channel), YY=0x15, ZZ=on/off
+	 * reverb mode: XX=0x01, YY=0x30, ZZ=0-7
+	 * chorus mode: XX=0x01, YY=0x38, ZZ=0-7
+	 * master vol:  XX=0x00, YY=0x04, ZZ=0-127
+	 */
+	static unsigned char gs_pfx_macro[] = {
+		0x41,0x10,0x42,0x12,0x40,/*XX,YY,ZZ*/
+	};
+
+	int parsed = SNDRV_MIDI_SYSEX_NOT_PARSED;
+
+	if (len <= 0 || buf[0] != 0xf0)
+		return;
+	/* skip first byte */
+	buf++;
+	len--;
+
+	/* GM on */
+	if (len >= (int)sizeof(gm_on_macro) &&
+	    memcmp(buf, gm_on_macro, sizeof(gm_on_macro)) == 0) {
+		if (chset->midi_mode != SNDRV_MIDI_MODE_GS &&
+		    chset->midi_mode != SNDRV_MIDI_MODE_XG) {
+			chset->midi_mode = SNDRV_MIDI_MODE_GM;
+			reset_all_channels(chset);
+			parsed = SNDRV_MIDI_SYSEX_GM_ON;
+		}
+	}
+
+	/* GS macros */
+	else if (len >= 8 &&
+		 memcmp(buf, gs_pfx_macro, sizeof(gs_pfx_macro)) == 0) {
+		if (chset->midi_mode != SNDRV_MIDI_MODE_GS &&
+		    chset->midi_mode != SNDRV_MIDI_MODE_XG)
+			chset->midi_mode = SNDRV_MIDI_MODE_GS;
+
+		if (buf[5] == 0x00 && buf[6] == 0x7f && buf[7] == 0x00) {
+			/* GS reset */
+			parsed = SNDRV_MIDI_SYSEX_GS_RESET;
+			reset_all_channels(chset);
+		}
+
+		else if ((buf[5] & 0xf0) == 0x10 && buf[6] == 0x15) {
+			/* drum pattern */
+			int p = get_channel(buf[5]);
+			if (p < chset->max_channels) {
+				parsed = SNDRV_MIDI_SYSEX_GS_DRUM_CHANNEL;
+				if (buf[7])
+					chset->channels[p].drum_channel = 1;
+				else
+					chset->channels[p].drum_channel = 0;
+			}
+
+		} else if ((buf[5] & 0xf0) == 0x10 && buf[6] == 0x21) {
+			/* program */
+			int p = get_channel(buf[5]);
+			if (p < chset->max_channels &&
+			    ! chset->channels[p].drum_channel) {
+				parsed = SNDRV_MIDI_SYSEX_GS_DRUM_CHANNEL;
+				chset->channels[p].midi_program = buf[7];
+			}
+
+		} else if (buf[5] == 0x01 && buf[6] == 0x30) {
+			/* reverb mode */
+			parsed = SNDRV_MIDI_SYSEX_GS_REVERB_MODE;
+			chset->gs_reverb_mode = buf[7];
+
+		} else if (buf[5] == 0x01 && buf[6] == 0x38) {
+			/* chorus mode */
+			parsed = SNDRV_MIDI_SYSEX_GS_CHORUS_MODE;
+			chset->gs_chorus_mode = buf[7];
+
+		} else if (buf[5] == 0x00 && buf[6] == 0x04) {
+			/* master volume */
+			parsed = SNDRV_MIDI_SYSEX_GS_MASTER_VOLUME;
+			chset->gs_master_volume = buf[7];
+
+		}
+	}
+
+	/* XG on */
+	else if (len >= (int)sizeof(xg_on_macro) &&
+		 memcmp(buf, xg_on_macro, sizeof(xg_on_macro)) == 0) {
+		int i;
+		chset->midi_mode = SNDRV_MIDI_MODE_XG;
+		parsed = SNDRV_MIDI_SYSEX_XG_ON;
+		/* reset CC#0 for drums */
+		for (i = 0; i < chset->max_channels; i++) {
+			if (chset->channels[i].drum_channel)
+				chset->channels[i].control[MIDI_CTL_MSB_BANK] = 127;
+			else
+				chset->channels[i].control[MIDI_CTL_MSB_BANK] = 0;
+		}
+	}
+
+	if (ops->sysex)
+		ops->sysex(private, buf - 1, len + 1, parsed, chset);
+}
+
+/*
+ * all sound off
+ */
+static void
+all_sounds_off(struct snd_midi_op *ops, void *drv, struct snd_midi_channel *chan)
+{
+	int n;
+
+	if (! ops->note_terminate)
+		return;
+	for (n = 0; n < 128; n++) {
+		if (chan->note[n]) {
+			ops->note_terminate(drv, n, chan);
+			chan->note[n] = 0;
+		}
+	}
+}
+
+/*
+ * all notes off
+ */
+static void
+all_notes_off(struct snd_midi_op *ops, void *drv, struct snd_midi_channel *chan)
+{
+	int n;
+
+	if (! ops->note_off)
+		return;
+	for (n = 0; n < 128; n++) {
+		if (chan->note[n] == SNDRV_MIDI_NOTE_ON)
+			note_off(ops, drv, chan, n, 0);
+	}
+}
+
+/*
+ * Initialise a single midi channel control block.
+ */
+static void snd_midi_channel_init(struct snd_midi_channel *p, int n)
+{
+	if (p == NULL)
+		return;
+
+	memset(p, 0, sizeof(struct snd_midi_channel));
+	p->private = NULL;
+	p->number = n;
+
+	snd_midi_reset_controllers(p);
+	p->gm_rpn_pitch_bend_range = 256; /* 2 semitones */
+	p->gm_rpn_fine_tuning = 0;
+	p->gm_rpn_coarse_tuning = 0;
+
+	if (n == 9)
+		p->drum_channel = 1;	/* Default ch 10 as drums */
+}
+
+/*
+ * Allocate and initialise a set of midi channel control blocks.
+ */
+static struct snd_midi_channel *snd_midi_channel_init_set(int n)
+{
+	struct snd_midi_channel *chan;
+	int  i;
+
+	chan = kmalloc(n * sizeof(struct snd_midi_channel), GFP_KERNEL);
+	if (chan) {
+		for (i = 0; i < n; i++)
+			snd_midi_channel_init(chan+i, i);
+	}
+
+	return chan;
+}
+
+/*
+ * reset all midi channels
+ */
+static void
+reset_all_channels(struct snd_midi_channel_set *chset)
+{
+	int ch;
+	for (ch = 0; ch < chset->max_channels; ch++) {
+		struct snd_midi_channel *chan = chset->channels + ch;
+		snd_midi_reset_controllers(chan);
+		chan->gm_rpn_pitch_bend_range = 256; /* 2 semitones */
+		chan->gm_rpn_fine_tuning = 0;
+		chan->gm_rpn_coarse_tuning = 0;
+
+		if (ch == 9)
+			chan->drum_channel = 1;
+		else
+			chan->drum_channel = 0;
+	}
+}
+
+
+/*
+ * Allocate and initialise a midi channel set.
+ */
+struct snd_midi_channel_set *snd_midi_channel_alloc_set(int n)
+{
+	struct snd_midi_channel_set *chset;
+
+	chset = kmalloc(sizeof(*chset), GFP_KERNEL);
+	if (chset) {
+		chset->channels = snd_midi_channel_init_set(n);
+		chset->private_data = NULL;
+		chset->max_channels = n;
+	}
+	return chset;
+}
+
+/*
+ * Reset the midi controllers on a particular channel to default values.
+ */
+static void snd_midi_reset_controllers(struct snd_midi_channel *chan)
+{
+	memset(chan->control, 0, sizeof(chan->control));
+	chan->gm_volume = 127;
+	chan->gm_expression = 127;
+	chan->gm_pan = 64;
+}
+
+
+/*
+ * Free a midi channel set.
+ */
+void snd_midi_channel_free_set(struct snd_midi_channel_set *chset)
+{
+	if (chset == NULL)
+		return;
+	kfree(chset->channels);
+	kfree(chset);
+}
+
+static int __init alsa_seq_midi_emul_init(void)
+{
+	return 0;
+}
+
+static void __exit alsa_seq_midi_emul_exit(void)
+{
+}
+
+module_init(alsa_seq_midi_emul_init)
+module_exit(alsa_seq_midi_emul_exit)
+
+EXPORT_SYMBOL(snd_midi_process_event);
+EXPORT_SYMBOL(snd_midi_channel_set_clear);
+EXPORT_SYMBOL(snd_midi_channel_alloc_set);
+EXPORT_SYMBOL(snd_midi_channel_free_set);
Index: /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_midi_event.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_midi_event.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_midi_event.c	(revision 91)
@@ -0,0 +1,545 @@
+/*
+ *  MIDI byte <-> sequencer event coder
+ *
+ *  Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>,
+ *                        Jaroslav Kysela <perex@suse.cz>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include <sound/driver.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <sound/core.h>
+#include <sound/seq_kernel.h>
+#include <sound/seq_midi_event.h>
+#include <sound/asoundef.h>
+
+MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>, Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("MIDI byte <-> sequencer event coder");
+MODULE_LICENSE("GPL");
+
+/* queue type */
+/* from 0 to 7 are normal commands (note off, on, etc.) */
+#define ST_NOTEOFF	0
+#define ST_NOTEON	1
+#define ST_SPECIAL	8
+#define ST_SYSEX	ST_SPECIAL
+/* from 8 to 15 are events for 0xf0-0xf7 */
+
+
+/*
+ * prototypes
+ */
+static void note_event(struct snd_midi_event *dev, struct snd_seq_event *ev);
+static void one_param_ctrl_event(struct snd_midi_event *dev, struct snd_seq_event *ev);
+static void pitchbend_ctrl_event(struct snd_midi_event *dev, struct snd_seq_event *ev);
+static void two_param_ctrl_event(struct snd_midi_event *dev, struct snd_seq_event *ev);
+static void one_param_event(struct snd_midi_event *dev, struct snd_seq_event *ev);
+static void songpos_event(struct snd_midi_event *dev, struct snd_seq_event *ev);
+static void note_decode(struct snd_seq_event *ev, unsigned char *buf);
+static void one_param_decode(struct snd_seq_event *ev, unsigned char *buf);
+static void pitchbend_decode(struct snd_seq_event *ev, unsigned char *buf);
+static void two_param_decode(struct snd_seq_event *ev, unsigned char *buf);
+static void songpos_decode(struct snd_seq_event *ev, unsigned char *buf);
+
+/*
+ * event list
+ */
+static struct status_event_list {
+	int event;
+	int qlen;
+	void (*encode)(struct snd_midi_event *dev, struct snd_seq_event *ev);
+	void (*decode)(struct snd_seq_event *ev, unsigned char *buf);
+} status_event[] = {
+	/* 0x80 - 0xf0 */
+	{SNDRV_SEQ_EVENT_NOTEOFF,	2, note_event, note_decode},
+	{SNDRV_SEQ_EVENT_NOTEON,	2, note_event, note_decode},
+	{SNDRV_SEQ_EVENT_KEYPRESS,	2, note_event, note_decode},
+	{SNDRV_SEQ_EVENT_CONTROLLER,	2, two_param_ctrl_event, two_param_decode},
+	{SNDRV_SEQ_EVENT_PGMCHANGE,	1, one_param_ctrl_event, one_param_decode},
+	{SNDRV_SEQ_EVENT_CHANPRESS,	1, one_param_ctrl_event, one_param_decode},
+	{SNDRV_SEQ_EVENT_PITCHBEND,	2, pitchbend_ctrl_event, pitchbend_decode},
+	{SNDRV_SEQ_EVENT_NONE,		0, NULL, NULL}, /* 0xf0 */
+	/* 0xf0 - 0xff */
+	{SNDRV_SEQ_EVENT_SYSEX,		1, NULL, NULL}, /* sysex: 0xf0 */
+	{SNDRV_SEQ_EVENT_QFRAME,	1, one_param_event, one_param_decode}, /* 0xf1 */
+	{SNDRV_SEQ_EVENT_SONGPOS,	2, songpos_event, songpos_decode}, /* 0xf2 */
+	{SNDRV_SEQ_EVENT_SONGSEL,	1, one_param_event, one_param_decode}, /* 0xf3 */
+	{SNDRV_SEQ_EVENT_NONE,		0, NULL, NULL}, /* 0xf4 */
+	{SNDRV_SEQ_EVENT_NONE,		0, NULL, NULL}, /* 0xf5 */
+	{SNDRV_SEQ_EVENT_TUNE_REQUEST,	0, NULL, NULL},	/* 0xf6 */
+	{SNDRV_SEQ_EVENT_NONE,		0, NULL, NULL}, /* 0xf7 */
+	{SNDRV_SEQ_EVENT_CLOCK,		0, NULL, NULL}, /* 0xf8 */
+	{SNDRV_SEQ_EVENT_NONE,		0, NULL, NULL}, /* 0xf9 */
+	{SNDRV_SEQ_EVENT_START,		0, NULL, NULL}, /* 0xfa */
+	{SNDRV_SEQ_EVENT_CONTINUE,	0, NULL, NULL}, /* 0xfb */
+	{SNDRV_SEQ_EVENT_STOP, 		0, NULL, NULL}, /* 0xfc */
+	{SNDRV_SEQ_EVENT_NONE, 		0, NULL, NULL}, /* 0xfd */
+	{SNDRV_SEQ_EVENT_SENSING, 	0, NULL, NULL}, /* 0xfe */
+	{SNDRV_SEQ_EVENT_RESET, 	0, NULL, NULL}, /* 0xff */
+};
+
+static int extra_decode_ctrl14(struct snd_midi_event *dev, unsigned char *buf, int len,
+			       struct snd_seq_event *ev);
+static int extra_decode_xrpn(struct snd_midi_event *dev, unsigned char *buf, int count,
+			     struct snd_seq_event *ev);
+
+static struct extra_event_list {
+	int event;
+	int (*decode)(struct snd_midi_event *dev, unsigned char *buf, int len,
+		      struct snd_seq_event *ev);
+} extra_event[] = {
+	{SNDRV_SEQ_EVENT_CONTROL14, extra_decode_ctrl14},
+	{SNDRV_SEQ_EVENT_NONREGPARAM, extra_decode_xrpn},
+	{SNDRV_SEQ_EVENT_REGPARAM, extra_decode_xrpn},
+};
+
+/*
+ *  new/delete record
+ */
+
+int snd_midi_event_new(int bufsize, struct snd_midi_event **rdev)
+{
+	struct snd_midi_event *dev;
+
+	*rdev = NULL;
+	dev = (struct snd_midi_event *)kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (dev == NULL)
+		return -ENOMEM;
+	if (bufsize > 0) {
+		dev->buf = kmalloc(bufsize, GFP_KERNEL);
+		if (dev->buf == NULL) {
+			kfree(dev);
+			return -ENOMEM;
+		}
+	}
+	dev->bufsize = bufsize;
+	dev->lastcmd = 0xff;
+	spin_lock_init(&dev->lock);
+	*rdev = dev;
+	return 0;
+}
+
+void snd_midi_event_free(struct snd_midi_event *dev)
+{
+	if (dev != NULL) {
+		kfree(dev->buf);
+		kfree(dev);
+	}
+}
+
+/*
+ * initialize record
+ */
+static inline void reset_encode(struct snd_midi_event *dev)
+{
+	dev->read = 0;
+	dev->qlen = 0;
+	dev->type = 0;
+}
+
+void snd_midi_event_reset_encode(struct snd_midi_event *dev)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	reset_encode(dev);
+	spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+void snd_midi_event_reset_decode(struct snd_midi_event *dev)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	dev->lastcmd = 0xff;
+	spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+#if 0
+void snd_midi_event_init(struct snd_midi_event *dev)
+{
+	snd_midi_event_reset_encode(dev);
+	snd_midi_event_reset_decode(dev);
+}
+#endif  /*  0  */
+
+void snd_midi_event_no_status(struct snd_midi_event *dev, int on)
+{
+	dev->nostat = on ? 1 : 0;
+}
+
+/*
+ * resize buffer
+ */
+#if 0
+int snd_midi_event_resize_buffer(struct snd_midi_event *dev, int bufsize)
+{
+	unsigned char *new_buf, *old_buf;
+	unsigned long flags;
+
+	if (bufsize == dev->bufsize)
+		return 0;
+	new_buf = kmalloc(bufsize, GFP_KERNEL);
+	if (new_buf == NULL)
+		return -ENOMEM;
+	spin_lock_irqsave(&dev->lock, flags);
+	old_buf = dev->buf;
+	dev->buf = new_buf;
+	dev->bufsize = bufsize;
+	reset_encode(dev);
+	spin_unlock_irqrestore(&dev->lock, flags);
+	kfree(old_buf);
+	return 0;
+}
+#endif  /*  0  */
+
+/*
+ *  read bytes and encode to sequencer event if finished
+ *  return the size of encoded bytes
+ */
+long snd_midi_event_encode(struct snd_midi_event *dev, unsigned char *buf, long count,
+			   struct snd_seq_event *ev)
+{
+	long result = 0;
+	int rc;
+
+	ev->type = SNDRV_SEQ_EVENT_NONE;
+
+	while (count-- > 0) {
+		rc = snd_midi_event_encode_byte(dev, *buf++, ev);
+		result++;
+		if (rc < 0)
+			return rc;
+		else if (rc > 0)
+			return result;
+	}
+
+	return result;
+}
+
+/*
+ *  read one byte and encode to sequencer event:
+ *  return 1 if MIDI bytes are encoded to an event
+ *         0 data is not finished
+ *         negative for error
+ */
+int snd_midi_event_encode_byte(struct snd_midi_event *dev, int c,
+			       struct snd_seq_event *ev)
+{
+	int rc = 0;
+	unsigned long flags;
+
+	c &= 0xff;
+
+	if (c >= MIDI_CMD_COMMON_CLOCK) {
+		/* real-time event */
+		ev->type = status_event[ST_SPECIAL + c - 0xf0].event;
+		ev->flags &= ~SNDRV_SEQ_EVENT_LENGTH_MASK;
+		ev->flags |= SNDRV_SEQ_EVENT_LENGTH_FIXED;
+		return 1;
+	}
+
+	spin_lock_irqsave(&dev->lock, flags);
+	if (dev->qlen > 0) {
+		/* rest of command */
+		dev->buf[dev->read++] = c;
+		if (dev->type != ST_SYSEX)
+			dev->qlen--;
+	} else {
+		/* new command */
+		dev->read = 1;
+		if (c & 0x80) {
+			dev->buf[0] = c;
+			if ((c & 0xf0) == 0xf0) /* special events */
+				dev->type = (c & 0x0f) + ST_SPECIAL;
+			else
+				dev->type = (c >> 4) & 0x07;
+			dev->qlen = status_event[dev->type].qlen;
+		} else {
+			/* process this byte as argument */
+			dev->buf[dev->read++] = c;
+			dev->qlen = status_event[dev->type].qlen - 1;
+		}
+	}
+	if (dev->qlen == 0) {
+		ev->type = status_event[dev->type].event;
+		ev->flags &= ~SNDRV_SEQ_EVENT_LENGTH_MASK;
+		ev->flags |= SNDRV_SEQ_EVENT_LENGTH_FIXED;
+		if (status_event[dev->type].encode) /* set data values */
+			status_event[dev->type].encode(dev, ev);
+		rc = 1;
+	} else 	if (dev->type == ST_SYSEX) {
+		if (c == MIDI_CMD_COMMON_SYSEX_END ||
+		    dev->read >= dev->bufsize) {
+			ev->flags &= ~SNDRV_SEQ_EVENT_LENGTH_MASK;
+			ev->flags |= SNDRV_SEQ_EVENT_LENGTH_VARIABLE;
+			ev->type = SNDRV_SEQ_EVENT_SYSEX;
+			ev->data.ext.len = dev->read;
+			ev->data.ext.ptr = dev->buf;
+			if (c != MIDI_CMD_COMMON_SYSEX_END)
+				dev->read = 0; /* continue to parse */
+			else
+				reset_encode(dev); /* all parsed */
+			rc = 1;
+		}
+	}
+
+	spin_unlock_irqrestore(&dev->lock, flags);
+	return rc;
+}
+
+/* encode note event */
+static void note_event(struct snd_midi_event *dev, struct snd_seq_event *ev)
+{
+	ev->data.note.channel = dev->buf[0] & 0x0f;
+	ev->data.note.note = dev->buf[1];
+	ev->data.note.velocity = dev->buf[2];
+}
+
+/* encode one parameter controls */
+static void one_param_ctrl_event(struct snd_midi_event *dev, struct snd_seq_event *ev)
+{
+	ev->data.control.channel = dev->buf[0] & 0x0f;
+	ev->data.control.value = dev->buf[1];
+}
+
+/* encode pitch wheel change */
+static void pitchbend_ctrl_event(struct snd_midi_event *dev, struct snd_seq_event *ev)
+{
+	ev->data.control.channel = dev->buf[0] & 0x0f;
+	ev->data.control.value = (int)dev->buf[2] * 128 + (int)dev->buf[1] - 8192;
+}
+
+/* encode midi control change */
+static void two_param_ctrl_event(struct snd_midi_event *dev, struct snd_seq_event *ev)
+{
+	ev->data.control.channel = dev->buf[0] & 0x0f;
+	ev->data.control.param = dev->buf[1];
+	ev->data.control.value = dev->buf[2];
+}
+
+/* encode one parameter value*/
+static void one_param_event(struct snd_midi_event *dev, struct snd_seq_event *ev)
+{
+	ev->data.control.value = dev->buf[1];
+}
+
+/* encode song position */
+static void songpos_event(struct snd_midi_event *dev, struct snd_seq_event *ev)
+{
+	ev->data.control.value = (int)dev->buf[2] * 128 + (int)dev->buf[1];
+}
+
+/*
+ * decode from a sequencer event to midi bytes
+ * return the size of decoded midi events
+ */
+long snd_midi_event_decode(struct snd_midi_event *dev, unsigned char *buf, long count,
+			   struct snd_seq_event *ev)
+{
+	unsigned int cmd, type;
+
+	if (ev->type == SNDRV_SEQ_EVENT_NONE)
+		return -ENOENT;
+
+	for (type = 0; type < ARRAY_SIZE(status_event); type++) {
+		if (ev->type == status_event[type].event)
+			goto __found;
+	}
+	for (type = 0; type < ARRAY_SIZE(extra_event); type++) {
+		if (ev->type == extra_event[type].event)
+			return extra_event[type].decode(dev, buf, count, ev);
+	}
+	return -ENOENT;
+
+      __found:
+	if (type >= ST_SPECIAL)
+		cmd = 0xf0 + (type - ST_SPECIAL);
+	else
+		/* data.note.channel and data.control.channel is identical */
+		cmd = 0x80 | (type << 4) | (ev->data.note.channel & 0x0f);
+
+
+	if (cmd == MIDI_CMD_COMMON_SYSEX) {
+		snd_midi_event_reset_decode(dev);
+		return snd_seq_expand_var_event(ev, count, buf, 1, 0);
+	} else {
+		int qlen;
+		unsigned char xbuf[4];
+		unsigned long flags;
+
+		spin_lock_irqsave(&dev->lock, flags);
+		if ((cmd & 0xf0) == 0xf0 || dev->lastcmd != cmd || dev->nostat) {
+			dev->lastcmd = cmd;
+			spin_unlock_irqrestore(&dev->lock, flags);
+			xbuf[0] = cmd;
+			if (status_event[type].decode)
+				status_event[type].decode(ev, xbuf + 1);
+			qlen = status_event[type].qlen + 1;
+		} else {
+			spin_unlock_irqrestore(&dev->lock, flags);
+			if (status_event[type].decode)
+				status_event[type].decode(ev, xbuf + 0);
+			qlen = status_event[type].qlen;
+		}
+		if (count < qlen)
+			return -ENOMEM;
+		memcpy(buf, xbuf, qlen);
+		return qlen;
+	}
+}
+
+
+/* decode note event */
+static void note_decode(struct snd_seq_event *ev, unsigned char *buf)
+{
+	buf[0] = ev->data.note.note & 0x7f;
+	buf[1] = ev->data.note.velocity & 0x7f;
+}
+
+/* decode one parameter controls */
+static void one_param_decode(struct snd_seq_event *ev, unsigned char *buf)
+{
+	buf[0] = ev->data.control.value & 0x7f;
+}
+
+/* decode pitch wheel change */
+static void pitchbend_decode(struct snd_seq_event *ev, unsigned char *buf)
+{
+	int value = ev->data.control.value + 8192;
+	buf[0] = value & 0x7f;
+	buf[1] = (value >> 7) & 0x7f;
+}
+
+/* decode midi control change */
+static void two_param_decode(struct snd_seq_event *ev, unsigned char *buf)
+{
+	buf[0] = ev->data.control.param & 0x7f;
+	buf[1] = ev->data.control.value & 0x7f;
+}
+
+/* decode song position */
+static void songpos_decode(struct snd_seq_event *ev, unsigned char *buf)
+{
+	buf[0] = ev->data.control.value & 0x7f;
+	buf[1] = (ev->data.control.value >> 7) & 0x7f;
+}
+
+/* decode 14bit control */
+static int extra_decode_ctrl14(struct snd_midi_event *dev, unsigned char *buf,
+			       int count, struct snd_seq_event *ev)
+{
+	unsigned char cmd;
+	int idx = 0;
+
+	cmd = MIDI_CMD_CONTROL|(ev->data.control.channel & 0x0f);
+	if (ev->data.control.param < 0x20) {
+		if (count < 4)
+			return -ENOMEM;
+		if (dev->nostat && count < 6)
+			return -ENOMEM;
+		if (cmd != dev->lastcmd || dev->nostat) {
+			if (count < 5)
+				return -ENOMEM;
+			buf[idx++] = dev->lastcmd = cmd;
+		}
+		buf[idx++] = ev->data.control.param;
+		buf[idx++] = (ev->data.control.value >> 7) & 0x7f;
+		if (dev->nostat)
+			buf[idx++] = cmd;
+		buf[idx++] = ev->data.control.param + 0x20;
+		buf[idx++] = ev->data.control.value & 0x7f;
+	} else {
+		if (count < 2)
+			return -ENOMEM;
+		if (cmd != dev->lastcmd || dev->nostat) {
+			if (count < 3)
+				return -ENOMEM;
+			buf[idx++] = dev->lastcmd = cmd;
+		}
+		buf[idx++] = ev->data.control.param & 0x7f;
+		buf[idx++] = ev->data.control.value & 0x7f;
+	}
+	return idx;
+}
+
+/* decode reg/nonreg param */
+static int extra_decode_xrpn(struct snd_midi_event *dev, unsigned char *buf,
+			     int count, struct snd_seq_event *ev)
+{
+	unsigned char cmd;
+	char *cbytes;
+	static char cbytes_nrpn[4] = { MIDI_CTL_NONREG_PARM_NUM_MSB,
+				       MIDI_CTL_NONREG_PARM_NUM_LSB,
+				       MIDI_CTL_MSB_DATA_ENTRY,
+				       MIDI_CTL_LSB_DATA_ENTRY };
+	static char cbytes_rpn[4] =  { MIDI_CTL_REGIST_PARM_NUM_MSB,
+				       MIDI_CTL_REGIST_PARM_NUM_LSB,
+				       MIDI_CTL_MSB_DATA_ENTRY,
+				       MIDI_CTL_LSB_DATA_ENTRY };
+	unsigned char bytes[4];
+	int idx = 0, i;
+
+	if (count < 8)
+		return -ENOMEM;
+	if (dev->nostat && count < 12)
+		return -ENOMEM;
+	cmd = MIDI_CMD_CONTROL|(ev->data.control.channel & 0x0f);
+	bytes[0] = ev->data.control.param & 0x007f;
+	bytes[1] = (ev->data.control.param & 0x3f80) >> 7;
+	bytes[2] = ev->data.control.value & 0x007f;
+	bytes[3] = (ev->data.control.value & 0x3f80) >> 7;
+	if (cmd != dev->lastcmd && !dev->nostat) {
+		if (count < 9)
+			return -ENOMEM;
+		buf[idx++] = dev->lastcmd = cmd;
+	}
+	cbytes = ev->type == SNDRV_SEQ_EVENT_NONREGPARAM ? cbytes_nrpn : cbytes_rpn;
+	for (i = 0; i < 4; i++) {
+		if (dev->nostat)
+			buf[idx++] = dev->lastcmd = cmd;
+		buf[idx++] = cbytes[i];
+		buf[idx++] = bytes[i];
+	}
+	return idx;
+}
+
+/*
+ *  exports
+ */
+ 
+EXPORT_SYMBOL(snd_midi_event_new);
+EXPORT_SYMBOL(snd_midi_event_free);
+EXPORT_SYMBOL(snd_midi_event_reset_encode);
+EXPORT_SYMBOL(snd_midi_event_reset_decode);
+EXPORT_SYMBOL(snd_midi_event_no_status);
+EXPORT_SYMBOL(snd_midi_event_encode);
+EXPORT_SYMBOL(snd_midi_event_encode_byte);
+EXPORT_SYMBOL(snd_midi_event_decode);
+
+static int __init alsa_seq_midi_event_init(void)
+{
+	return 0;
+}
+
+static void __exit alsa_seq_midi_event_exit(void)
+{
+}
+
+module_init(alsa_seq_midi_event_init)
+module_exit(alsa_seq_midi_event_exit)
Index: /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_mtc.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_mtc.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_mtc.c	(revision 91)
@@ -0,0 +1,250 @@
+/*
+ * MTC event converter
+ *
+ *  Copyright (c) 2000 by 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <sound/driver.h>
+#include "seq_queue.h"
+
+#ifdef SNDRV_SEQ_SYNC_SUPPORT
+
+typedef struct mtc_out {
+	int out_channel;
+	unsigned int time_format;
+	sndrv_seq_time_frame_t cur_time;
+	unsigned int decode_offset;
+	unsigned char sysex[10];
+} mtc_out_t;
+
+typedef struct mtc_in {
+	unsigned int time_format;
+	sndrv_seq_time_frame_t cur_time;
+	unsigned int cur_pos;
+	int prev_in_offset;
+} mtc_in_t;
+
+
+static int mtc_open_out(queue_sync_t *sync_info, seq_sync_arg_t *retp)
+{
+	mtc_out_t *arg;
+
+	if (sync_info->time_format >= 4)
+		return -EINVAL;
+	if ((arg = kmalloc(sizeof(*arg), GFP_KERNEL)) == NULL)
+		return -ENOMEM;
+	arg->out_channel = sync_info->opt_info[0];
+	if (arg->out_channel == 0)
+		arg->out_channel = 127;
+	arg->time_format = sync_info->time_format;
+	sync_info->param.time.subframes = 4; /* MTC uses quarter frame */
+	sync_info->param.time.resolution = snd_seq_get_smpte_resolution(arg->time_format);
+	memset(&arg->cur_time, 0, sizeof(arg->cur_time));
+	*retp = arg;
+	return 0;
+}
+
+static int mtc_open_in(queue_sync_t *sync_info, seq_sync_arg_t *retp)
+{
+	mtc_in_t *arg;
+
+	if (sync_info->time_format >= 4)
+		return -EINVAL;
+	if ((arg = kmalloc(sizeof(*arg), GFP_KERNEL)) == NULL)
+		return -ENOMEM;
+	arg->time_format = sync_info->time_format;
+	memset(&arg->cur_time, 0, sizeof(arg->cur_time));
+	arg->cur_pos = 0;
+	arg->prev_in_offset = -1;
+	sync_info->param.time.subframes = 4; /* MTC uses quarter frame */
+	sync_info->param.time.resolution = snd_seq_get_smpte_resolution(arg->time_format);
+	*retp = arg;
+	return 0;
+}
+
+
+/* decode sync signal */
+static int sync_out(mtc_out_t *arg, const snd_seq_event_t *src, snd_seq_event_t *ev)
+{
+	int val, offset;
+
+	if (arg->time_format != src->data.queue.sync_time_format)
+		return -EINVAL;
+	offset = (src->data.queue.param.position + arg->decode_offset) % 8;
+	if (offset == 0) {
+		/* convert and remember the current time
+		   for the following 7 MTC quarter frames */
+		arg->cur_time = snd_seq_position_to_time_frame(arg->time_format, 4, src->data.queue.param.position);
+	}
+	switch (offset) {
+	case 0: val = arg->cur_time.frame & 0x0f; break;
+	case 1: val = (arg->cur_time.frame >> 4) & 0x0f; break;
+	case 2: val = arg->cur_time.sec & 0x0f; break;
+	case 3: val = (arg->cur_time.sec >> 4) & 0x0f; break;
+	case 4: val = arg->cur_time.min & 0x0f; break;
+	case 5: val = (arg->cur_time.min >> 4) & 0x0f; break;
+	case 6: val = arg->cur_time.hour & 0x0f; break;
+	case 7:
+	default:
+		val = ((arg->cur_time.hour >> 4) & 0x01) | (arg->time_format << 1);
+		break;
+	}
+	val |= (offset << 4);
+	ev->type = SNDRV_SEQ_EVENT_QFRAME;
+	ev->data.control.value = val;
+	return 1;
+}
+
+/* decode sync position */
+static int sync_pos_out(mtc_out_t *arg, const snd_seq_event_t *src, snd_seq_event_t *ev)
+{
+	unsigned int pos;
+	unsigned char *buf = arg->sysex;
+
+	if (arg->time_format != src->data.queue.sync_time_format)
+		return -EINVAL;
+
+	pos = src->data.queue.param.position; /* quarter frames */
+	arg->decode_offset = pos & 4;
+	pos /= 4;
+	arg->cur_time = snd_seq_position_to_time_frame(arg->time_format, 4, pos);
+
+	buf[0] = 0xf0; /* SYSEX */
+	buf[1] = 0x7f;
+	buf[2] = arg->out_channel;
+	buf[3] = 0x01;
+	buf[4] = 0x01;
+	buf[5] = arg->cur_time.hour | (arg->time_format << 5);
+	buf[6] = arg->cur_time.min;
+	buf[7] = arg->cur_time.sec;
+	buf[8] = arg->cur_time.frame;
+	buf[9] = 0xf7;
+
+	ev->type = SNDRV_SEQ_EVENT_SYSEX;
+	ev->flags &= ~SNDRV_SEQ_EVENT_LENGTH_MASK;
+	ev->flags |= SNDRV_SEQ_EVENT_LENGTH_VARIABLE;
+	ev->data.ext.len = 10;
+	ev->data.ext.ptr = buf;
+
+	return 1;
+}
+
+static int mtc_sync_out(seq_sync_arg_t _arg, const snd_seq_event_t *src, snd_seq_event_t *ev)
+{
+	mtc_out_t *arg = _arg;
+	switch (src->type) {
+	case SNDRV_SEQ_EVENT_SYNC:
+		return sync_out(arg, src, ev);
+	case SNDRV_SEQ_EVENT_SYNC_POS:
+		return sync_pos_out(arg, src, ev);
+	}
+	return 0;
+}
+
+/* decode sync signal */
+static int sync_in(mtc_in_t *arg, const snd_seq_event_t *src, snd_seq_event_t *ev)
+{
+	int val, offset;
+	unsigned int time_format;
+
+	offset = (src->data.control.value & 0x70) >> 4;
+	val = src->data.control.value & 0x0f;
+	if (offset > 0 && offset != arg->prev_in_offset + 1) {
+		/* bad quarter frame message - something missing.. */
+		arg->prev_in_offset = -1; /* wait for next 0 */
+		return -EINVAL;
+	}
+	switch (offset) {
+	case 0: arg->cur_time.frame =  val; break;
+	case 1: arg->cur_time.frame |= (val << 4); break;
+	case 2: arg->cur_time.sec = val; break;
+	case 3: arg->cur_time.sec |= (val << 4); break;
+	case 4: arg->cur_time.min = val; break;
+	case 5: arg->cur_time.min |= (val << 4); break;
+	case 6: arg->cur_time.hour = val; break;
+	case 7:
+	default:
+		arg->cur_time.hour |= (val & 1) << 4;
+		time_format = (val >> 1) & 3;
+		if (time_format != arg->time_format)
+			return -EINVAL;
+		arg->cur_pos = snd_seq_time_frame_to_position(time_format, 4, &arg->cur_time);
+		arg->cur_pos += 7; /* correct the receive time */
+		break;
+	}
+
+	ev->type = SNDRV_SEQ_EVENT_SYNC;
+	ev->data.queue.sync_time_format = arg->time_format;
+	ev->data.queue.param.position = arg->cur_pos;
+	arg->cur_pos++;
+
+	return 1; /* composed */
+}
+
+/* decode sync position */
+static int sync_pos_in(mtc_in_t *arg, const snd_seq_event_t *src, snd_seq_event_t *ev)
+{
+	unsigned time_format;
+	char buf[10];
+
+	if (snd_seq_expand_var_event(src, 10, buf, 1, 0) != 10)
+		return 0;
+	if (buf[1] != 0x7f || buf[3] != 0x01 || buf[4] != 0x01)
+		return 0;
+	time_format = (buf[5] >> 5) & 3;
+	if (time_format != arg->time_format)
+		return -EINVAL;
+	arg->cur_time.hour = buf[5] & 0x1f;
+	arg->cur_time.min = buf[6];
+	arg->cur_time.sec = buf[7];
+	arg->cur_time.frame = buf[8];
+	arg->cur_pos = snd_seq_time_frame_to_position(time_format, 4, &arg->cur_time);
+
+	ev->type = SNDRV_SEQ_EVENT_SYNC_POS;
+	ev->data.queue.sync_time_format = time_format;
+	ev->data.queue.param.position = arg->cur_pos;
+
+	return 1; /* composed */
+}
+
+static int mtc_sync_in(seq_sync_arg_t _arg, const snd_seq_event_t *src, snd_seq_event_t *ev)
+{
+	mtc_in_t *arg = _arg;
+	switch (src->type) {
+	case SNDRV_SEQ_EVENT_QFRAME:
+		return sync_in(arg, src, ev);
+	case SNDRV_SEQ_EVENT_SYSEX:
+		return sync_pos_in(arg, src, ev);
+	}
+	return 0;
+}
+
+/* exported */
+seq_sync_parser_t snd_seq_mtc_parser = {
+	format: SNDRV_SEQ_SYNC_FMT_MTC,
+	in: {
+		open: mtc_open_in,
+		sync: mtc_sync_in,
+	},
+	out: {
+		open: mtc_open_out,
+		sync: mtc_sync_out,
+	},
+};
+
+#endif /* SNDRV_SEQ_SYNC_SUPPORT */
Index: /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_ports.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_ports.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_ports.c	(revision 91)
@@ -0,0 +1,696 @@
+/*
+ *   ALSA sequencer Ports
+ *   Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
+ *                         Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <linux/slab.h>
+#include "seq_system.h"
+#include "seq_ports.h"
+#include "seq_clientmgr.h"
+
+/*
+
+   registration of client ports
+
+ */
+
+
+/* 
+
+NOTE: the current implementation of the port structure as a linked list is
+not optimal for clients that have many ports. For sending messages to all
+subscribers of a port we first need to find the address of the port
+structure, which means we have to traverse the list. A direct access table
+(array) would be better, but big preallocated arrays waste memory.
+
+Possible actions:
+
+1) leave it this way, a client does normaly does not have more than a few
+ports
+
+2) replace the linked list of ports by a array of pointers which is
+dynamicly kmalloced. When a port is added or deleted we can simply allocate
+a new array, copy the corresponding pointers, and delete the old one. We
+then only need a pointer to this array, and an integer that tells us how
+much elements are in array.
+
+*/
+
+/* return pointer to port structure - port is locked if found */
+struct snd_seq_client_port *snd_seq_port_use_ptr(struct snd_seq_client *client,
+						 int num)
+{
+	struct list_head *p;
+	struct snd_seq_client_port *port;
+
+	if (client == NULL)
+		return NULL;
+	read_lock(&client->ports_lock);
+	list_for_each(p, &client->ports_list_head) {
+		port = list_entry(p, struct snd_seq_client_port, list);
+		if (port->addr.port == num) {
+			if (port->closing)
+				break; /* deleting now */
+			snd_use_lock_use(&port->use_lock);
+			read_unlock(&client->ports_lock);
+			return port;
+		}
+	}
+	read_unlock(&client->ports_lock);
+	return NULL;		/* not found */
+}
+
+
+/* search for the next port - port is locked if found */
+struct snd_seq_client_port *snd_seq_port_query_nearest(struct snd_seq_client *client,
+						       struct snd_seq_port_info *pinfo)
+{
+	int num;
+	struct list_head *p;
+	struct snd_seq_client_port *port, *found;
+
+	num = pinfo->addr.port;
+	found = NULL;
+	read_lock(&client->ports_lock);
+	list_for_each(p, &client->ports_list_head) {
+		port = list_entry(p, struct snd_seq_client_port, list);
+		if (port->addr.port < num)
+			continue;
+		if (port->addr.port == num) {
+			found = port;
+			break;
+		}
+		if (found == NULL || port->addr.port < found->addr.port)
+			found = port;
+	}
+	if (found) {
+		if (found->closing)
+			found = NULL;
+		else
+			snd_use_lock_use(&found->use_lock);
+	}
+	read_unlock(&client->ports_lock);
+	return found;
+}
+
+
+/* initialize snd_seq_port_subs_info */
+static void port_subs_info_init(struct snd_seq_port_subs_info *grp)
+{
+	INIT_LIST_HEAD(&grp->list_head);
+	grp->count = 0;
+	grp->exclusive = 0;
+	rwlock_init(&grp->list_lock);
+	init_rwsem(&grp->list_mutex);
+	grp->open = NULL;
+	grp->close = NULL;
+}
+
+
+/* create a port, port number is returned (-1 on failure) */
+struct snd_seq_client_port *snd_seq_create_port(struct snd_seq_client *client,
+						int port)
+{
+	unsigned long flags;
+	struct snd_seq_client_port *new_port;
+	struct list_head *l;
+	int num = -1;
+	
+	/* sanity check */
+	snd_assert(client, return NULL);
+
+	if (client->num_ports >= SNDRV_SEQ_MAX_PORTS - 1) {
+		snd_printk(KERN_WARNING "too many ports for client %d\n", client->number);
+		return NULL;
+	}
+
+	/* create a new port */
+	new_port = (struct snd_seq_client_port *)kzalloc(sizeof(*new_port), GFP_KERNEL);
+	if (! new_port) {
+		snd_printd("malloc failed for registering client port\n");
+		return NULL;	/* failure, out of memory */
+	}
+	/* init port data */
+	new_port->addr.client = client->number;
+	new_port->addr.port = -1;
+	new_port->owner = THIS_MODULE;
+	sprintf(new_port->name, "port-%d", num);
+	snd_use_lock_init(&new_port->use_lock);
+	port_subs_info_init(&new_port->c_src);
+	port_subs_info_init(&new_port->c_dest);
+
+	num = port >= 0 ? port : 0;
+	down(&client->ports_mutex);
+	write_lock_irqsave(&client->ports_lock, flags);
+	list_for_each(l, &client->ports_list_head) {
+		struct snd_seq_client_port *p = list_entry(l, struct snd_seq_client_port, list);
+		if (p->addr.port > num)
+			break;
+		if (port < 0) /* auto-probe mode */
+			num = p->addr.port + 1;
+	}
+	/* insert the new port */
+	list_add_tail(&new_port->list, l);
+	client->num_ports++;
+	new_port->addr.port = num;	/* store the port number in the port */
+	write_unlock_irqrestore(&client->ports_lock, flags);
+	up(&client->ports_mutex);
+	sprintf(new_port->name, "port-%d", num);
+
+	return new_port;
+}
+
+/* */
+enum group_type {
+	SRC_LIST, DEST_LIST
+};
+
+static int subscribe_port(struct snd_seq_client *client,
+			  struct snd_seq_client_port *port,
+			  struct snd_seq_port_subs_info *grp,
+			  struct snd_seq_port_subscribe *info, int send_ack);
+static int unsubscribe_port(struct snd_seq_client *client,
+			    struct snd_seq_client_port *port,
+			    struct snd_seq_port_subs_info *grp,
+			    struct snd_seq_port_subscribe *info, int send_ack);
+
+
+static struct snd_seq_client_port *get_client_port(struct snd_seq_addr *addr,
+						   struct snd_seq_client **cp)
+{
+	struct snd_seq_client_port *p;
+	*cp = snd_seq_client_use_ptr(addr->client);
+	if (*cp) {
+		p = snd_seq_port_use_ptr(*cp, addr->port);
+		if (! p) {
+			snd_seq_client_unlock(*cp);
+			*cp = NULL;
+		}
+		return p;
+	}
+	return NULL;
+}
+
+/*
+ * remove all subscribers on the list
+ * this is called from port_delete, for each src and dest list.
+ */
+static void clear_subscriber_list(struct snd_seq_client *client,
+				  struct snd_seq_client_port *port,
+				  struct snd_seq_port_subs_info *grp,
+				  int grptype)
+{
+	struct list_head *p, *n;
+
+	list_for_each_safe(p, n, &grp->list_head) {
+		struct snd_seq_subscribers *subs;
+		struct snd_seq_client *c;
+		struct snd_seq_client_port *aport;
+
+		if (grptype == SRC_LIST) {
+			subs = list_entry(p, struct snd_seq_subscribers, src_list);
+			aport = get_client_port(&subs->info.dest, &c);
+		} else {
+			subs = list_entry(p, struct snd_seq_subscribers, dest_list);
+			aport = get_client_port(&subs->info.sender, &c);
+		}
+		list_del(p);
+		unsubscribe_port(client, port, grp, &subs->info, 0);
+		if (!aport) {
+			/* looks like the connected port is being deleted.
+			 * we decrease the counter, and when both ports are deleted
+			 * remove the subscriber info
+			 */
+			if (atomic_dec_and_test(&subs->ref_count))
+				kfree(subs);
+		} else {
+			/* ok we got the connected port */
+			struct snd_seq_port_subs_info *agrp;
+			agrp = (grptype == SRC_LIST) ? &aport->c_dest : &aport->c_src;
+			down_write(&agrp->list_mutex);
+			if (grptype == SRC_LIST)
+				list_del(&subs->dest_list);
+			else
+				list_del(&subs->src_list);
+			unsubscribe_port(c, aport, agrp, &subs->info, 1);
+			kfree(subs);
+			up_write(&agrp->list_mutex);
+			snd_seq_port_unlock(aport);
+			snd_seq_client_unlock(c);
+		}
+	}
+}
+
+/* delete port data */
+static int port_delete(struct snd_seq_client *client,
+		       struct snd_seq_client_port *port)
+{
+	/* set closing flag and wait for all port access are gone */
+	port->closing = 1;
+	snd_use_lock_sync(&port->use_lock); 
+
+	/* clear subscribers info */
+	clear_subscriber_list(client, port, &port->c_src, SRC_LIST);
+	clear_subscriber_list(client, port, &port->c_dest, DEST_LIST);
+
+	if (port->private_free)
+		port->private_free(port->private_data);
+
+	snd_assert(port->c_src.count == 0,);
+	snd_assert(port->c_dest.count == 0,);
+
+	kfree(port);
+	return 0;
+}
+
+
+/* delete a port with the given port id */
+int snd_seq_delete_port(struct snd_seq_client *client, int port)
+{
+	unsigned long flags;
+	struct list_head *l;
+	struct snd_seq_client_port *found = NULL;
+
+	down(&client->ports_mutex);
+	write_lock_irqsave(&client->ports_lock, flags);
+	list_for_each(l, &client->ports_list_head) {
+		struct snd_seq_client_port *p = list_entry(l, struct snd_seq_client_port, list);
+		if (p->addr.port == port) {
+			/* ok found.  delete from the list at first */
+			list_del(l);
+			client->num_ports--;
+			found = p;
+			break;
+		}
+	}
+	write_unlock_irqrestore(&client->ports_lock, flags);
+	up(&client->ports_mutex);
+	if (found)
+		return port_delete(client, found);
+	else
+		return -ENOENT;
+}
+
+/* delete the all ports belonging to the given client */
+int snd_seq_delete_all_ports(struct snd_seq_client *client)
+{
+	unsigned long flags;
+	struct list_head deleted_list, *p, *n;
+	
+	/* move the port list to deleted_list, and
+	 * clear the port list in the client data.
+	 */
+	down(&client->ports_mutex);
+	write_lock_irqsave(&client->ports_lock, flags);
+	if (! list_empty(&client->ports_list_head)) {
+		__list_add(&deleted_list,
+			   client->ports_list_head.prev,
+			   client->ports_list_head.next);
+		INIT_LIST_HEAD(&client->ports_list_head);
+	} else {
+		INIT_LIST_HEAD(&deleted_list);
+	}
+	client->num_ports = 0;
+	write_unlock_irqrestore(&client->ports_lock, flags);
+
+	/* remove each port in deleted_list */
+	list_for_each_safe(p, n, &deleted_list) {
+		struct snd_seq_client_port *port = list_entry(p, struct snd_seq_client_port, list);
+		list_del(p);
+		snd_seq_system_client_ev_port_exit(port->addr.client, port->addr.port);
+		port_delete(client, port);
+	}
+	up(&client->ports_mutex);
+	return 0;
+}
+
+/* set port info fields */
+int snd_seq_set_port_info(struct snd_seq_client_port * port,
+			  struct snd_seq_port_info * info)
+{
+	snd_assert(port && info, return -EINVAL);
+
+	/* set port name */
+	if (info->name[0])
+		strlcpy(port->name, info->name, sizeof(port->name));
+	
+	/* set capabilities */
+	port->capability = info->capability;
+	
+	/* get port type */
+	port->type = info->type;
+
+	/* information about supported channels/voices */
+	port->midi_channels = info->midi_channels;
+	port->midi_voices = info->midi_voices;
+	port->synth_voices = info->synth_voices;
+
+	/* timestamping */
+	port->timestamping = (info->flags & SNDRV_SEQ_PORT_FLG_TIMESTAMP) ? 1 : 0;
+	port->time_real = (info->flags & SNDRV_SEQ_PORT_FLG_TIME_REAL) ? 1 : 0;
+	port->time_queue = info->time_queue;
+
+	return 0;
+}
+
+/* get port info fields */
+int snd_seq_get_port_info(struct snd_seq_client_port * port,
+			  struct snd_seq_port_info * info)
+{
+	snd_assert(port && info, return -EINVAL);
+
+	/* get port name */
+	strlcpy(info->name, port->name, sizeof(info->name));
+	
+	/* get capabilities */
+	info->capability = port->capability;
+
+	/* get port type */
+	info->type = port->type;
+
+	/* information about supported channels/voices */
+	info->midi_channels = port->midi_channels;
+	info->midi_voices = port->midi_voices;
+	info->synth_voices = port->synth_voices;
+
+	/* get subscriber counts */
+	info->read_use = port->c_src.count;
+	info->write_use = port->c_dest.count;
+	
+	/* timestamping */
+	info->flags = 0;
+	if (port->timestamping) {
+		info->flags |= SNDRV_SEQ_PORT_FLG_TIMESTAMP;
+		if (port->time_real)
+			info->flags |= SNDRV_SEQ_PORT_FLG_TIME_REAL;
+		info->time_queue = port->time_queue;
+	}
+
+	return 0;
+}
+
+
+
+/*
+ * call callback functions (if any):
+ * the callbacks are invoked only when the first (for connection) or
+ * the last subscription (for disconnection) is done.  Second or later
+ * subscription results in increment of counter, but no callback is
+ * invoked.
+ * This feature is useful if these callbacks are associated with
+ * initialization or termination of devices (see seq_midi.c).
+ *
+ * If callback_all option is set, the callback function is invoked
+ * at each connnection/disconnection. 
+ */
+
+static int subscribe_port(struct snd_seq_client *client,
+			  struct snd_seq_client_port *port,
+			  struct snd_seq_port_subs_info *grp,
+			  struct snd_seq_port_subscribe *info,
+			  int send_ack)
+{
+	int err = 0;
+
+	if (!try_module_get(port->owner))
+		return -EFAULT;
+	grp->count++;
+	if (grp->open && (port->callback_all || grp->count == 1)) {
+		err = grp->open(port->private_data, info);
+		if (err < 0) {
+			module_put(port->owner);
+			grp->count--;
+		}
+	}
+	if (err >= 0 && send_ack && client->type == USER_CLIENT)
+		snd_seq_client_notify_subscription(port->addr.client, port->addr.port,
+						   info, SNDRV_SEQ_EVENT_PORT_SUBSCRIBED);
+
+	return err;
+}
+
+static int unsubscribe_port(struct snd_seq_client *client,
+			    struct snd_seq_client_port *port,
+			    struct snd_seq_port_subs_info *grp,
+			    struct snd_seq_port_subscribe *info,
+			    int send_ack)
+{
+	int err = 0;
+
+	if (! grp->count)
+		return -EINVAL;
+	grp->count--;
+	if (grp->close && (port->callback_all || grp->count == 0))
+		err = grp->close(port->private_data, info);
+	if (send_ack && client->type == USER_CLIENT)
+		snd_seq_client_notify_subscription(port->addr.client, port->addr.port,
+						   info, SNDRV_SEQ_EVENT_PORT_UNSUBSCRIBED);
+	module_put(port->owner);
+	return err;
+}
+
+
+
+/* check if both addresses are identical */
+static inline int addr_match(struct snd_seq_addr *r, struct snd_seq_addr *s)
+{
+	return (r->client == s->client) && (r->port == s->port);
+}
+
+/* check the two subscribe info match */
+/* if flags is zero, checks only sender and destination addresses */
+static int match_subs_info(struct snd_seq_port_subscribe *r,
+			   struct snd_seq_port_subscribe *s)
+{
+	if (addr_match(&r->sender, &s->sender) &&
+	    addr_match(&r->dest, &s->dest)) {
+		if (r->flags && r->flags == s->flags)
+			return r->queue == s->queue;
+		else if (! r->flags)
+			return 1;
+	}
+	return 0;
+}
+
+
+/* connect two ports */
+int snd_seq_port_connect(struct snd_seq_client *connector,
+			 struct snd_seq_client *src_client,
+			 struct snd_seq_client_port *src_port,
+			 struct snd_seq_client *dest_client,
+			 struct snd_seq_client_port *dest_port,
+			 struct snd_seq_port_subscribe *info)
+{
+	struct snd_seq_port_subs_info *src = &src_port->c_src;
+	struct snd_seq_port_subs_info *dest = &dest_port->c_dest;
+	struct snd_seq_subscribers *subs;
+	struct list_head *p;
+	int err, src_called = 0;
+	unsigned long flags;
+	int exclusive;
+
+	subs = (struct snd_seq_subscribers *)kzalloc(sizeof(*subs), GFP_KERNEL);
+	if (! subs)
+		return -ENOMEM;
+
+	subs->info = *info;
+	atomic_set(&subs->ref_count, 2);
+
+	down_write(&src->list_mutex);
+	down_write(&dest->list_mutex);
+
+	exclusive = info->flags & SNDRV_SEQ_PORT_SUBS_EXCLUSIVE ? 1 : 0;
+	err = -EBUSY;
+	if (exclusive) {
+		if (! list_empty(&src->list_head) || ! list_empty(&dest->list_head))
+			goto __error;
+	} else {
+		if (src->exclusive || dest->exclusive)
+			goto __error;
+		/* check whether already exists */
+		list_for_each(p, &src->list_head) {
+			struct snd_seq_subscribers *s = list_entry(p, struct snd_seq_subscribers, src_list);
+			if (match_subs_info(info, &s->info))
+				goto __error;
+		}
+		list_for_each(p, &dest->list_head) {
+			struct snd_seq_subscribers *s = list_entry(p, struct snd_seq_subscribers, dest_list);
+			if (match_subs_info(info, &s->info))
+				goto __error;
+		}
+	}
+
+	if ((err = subscribe_port(src_client, src_port, src, info,
+				  connector->number != src_client->number)) < 0)
+		goto __error;
+	src_called = 1;
+
+	if ((err = subscribe_port(dest_client, dest_port, dest, info,
+				  connector->number != dest_client->number)) < 0)
+		goto __error;
+
+	/* add to list */
+	write_lock_irqsave(&src->list_lock, flags);
+	// write_lock(&dest->list_lock); // no other lock yet
+	list_add_tail(&subs->src_list, &src->list_head);
+	list_add_tail(&subs->dest_list, &dest->list_head);
+	// write_unlock(&dest->list_lock); // no other lock yet
+	write_unlock_irqrestore(&src->list_lock, flags);
+
+	src->exclusive = dest->exclusive = exclusive;
+
+	up_write(&dest->list_mutex);
+	up_write(&src->list_mutex);
+	return 0;
+
+ __error:
+	if (src_called)
+		unsubscribe_port(src_client, src_port, src, info,
+				 connector->number != src_client->number);
+	kfree(subs);
+	up_write(&dest->list_mutex);
+	up_write(&src->list_mutex);
+	return err;
+}
+
+
+/* remove the connection */
+int snd_seq_port_disconnect(struct snd_seq_client *connector,
+			    struct snd_seq_client *src_client,
+			    struct snd_seq_client_port *src_port,
+			    struct snd_seq_client *dest_client,
+			    struct snd_seq_client_port *dest_port,
+			    struct snd_seq_port_subscribe *info)
+{
+	struct snd_seq_port_subs_info *src = &src_port->c_src;
+	struct snd_seq_port_subs_info *dest = &dest_port->c_dest;
+	struct snd_seq_subscribers *subs;
+	struct list_head *p;
+	int err = -ENOENT;
+	unsigned long flags;
+
+	down_write(&src->list_mutex);
+	down_write(&dest->list_mutex);
+
+	/* look for the connection */
+	list_for_each(p, &src->list_head) {
+		subs = list_entry(p, struct snd_seq_subscribers, src_list);
+		if (match_subs_info(info, &subs->info)) {
+			write_lock_irqsave(&src->list_lock, flags);
+			// write_lock(&dest->list_lock);  // no lock yet
+			list_del(&subs->src_list);
+			list_del(&subs->dest_list);
+			// write_unlock(&dest->list_lock);
+			write_unlock_irqrestore(&src->list_lock, flags);
+			src->exclusive = dest->exclusive = 0;
+			unsubscribe_port(src_client, src_port, src, info,
+					 connector->number != src_client->number);
+			unsubscribe_port(dest_client, dest_port, dest, info,
+					 connector->number != dest_client->number);
+			kfree(subs);
+			err = 0;
+			break;
+		}
+	}
+
+	up_write(&dest->list_mutex);
+	up_write(&src->list_mutex);
+	return err;
+}
+
+
+/* get matched subscriber */
+struct snd_seq_subscribers *snd_seq_port_get_subscription(struct snd_seq_port_subs_info *src_grp,
+							  struct snd_seq_addr *dest_addr)
+{
+	struct list_head *p;
+	struct snd_seq_subscribers *s, *found = NULL;
+
+	down_read(&src_grp->list_mutex);
+	list_for_each(p, &src_grp->list_head) {
+		s = list_entry(p, struct snd_seq_subscribers, src_list);
+		if (addr_match(dest_addr, &s->info.dest)) {
+			found = s;
+			break;
+		}
+	}
+	up_read(&src_grp->list_mutex);
+	return found;
+}
+
+/*
+ * Attach a device driver that wants to receive events from the
+ * sequencer.  Returns the new port number on success.
+ * A driver that wants to receive the events converted to midi, will
+ * use snd_seq_midisynth_register_port().
+ */
+/* exported */
+int snd_seq_event_port_attach(int client,
+			      struct snd_seq_port_callback *pcbp,
+			      int cap, int type, int midi_channels,
+			      int midi_voices, char *portname)
+{
+	struct snd_seq_port_info portinfo;
+	int  ret;
+
+	/* Set up the port */
+	memset(&portinfo, 0, sizeof(portinfo));
+	portinfo.addr.client = client;
+	strlcpy(portinfo.name, portname ? portname : "Unamed port",
+		sizeof(portinfo.name));
+
+	portinfo.capability = cap;
+	portinfo.type = type;
+	portinfo.kernel = pcbp;
+	portinfo.midi_channels = midi_channels;
+	portinfo.midi_voices = midi_voices;
+
+	/* Create it */
+	ret = snd_seq_kernel_client_ctl(client,
+					SNDRV_SEQ_IOCTL_CREATE_PORT,
+					&portinfo);
+
+	if (ret >= 0)
+		ret = portinfo.addr.port;
+
+	return ret;
+}
+
+
+/*
+ * Detach the driver from a port.
+ */
+/* exported */
+int snd_seq_event_port_detach(int client, int port)
+{
+	struct snd_seq_port_info portinfo;
+	int  err;
+
+	memset(&portinfo, 0, sizeof(portinfo));
+	portinfo.addr.client = client;
+	portinfo.addr.port   = port;
+	err = snd_seq_kernel_client_ctl(client,
+					SNDRV_SEQ_IOCTL_DELETE_PORT,
+					&portinfo);
+
+	return err;
+}
Index: /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_ports.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_ports.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_ports.h	(revision 91)
@@ -0,0 +1,142 @@
+/*
+ *   ALSA sequencer Ports 
+ *   Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+#ifndef __SND_SEQ_PORTS_H
+#define __SND_SEQ_PORTS_H
+
+#include <sound/seq_kernel.h>
+#include "seq_lock.h"
+
+/* list of 'exported' ports */
+
+/* Client ports that are not exported are still accessible, but are
+ anonymous ports. 
+ 
+ If a port supports SUBSCRIPTION, that port can send events to all
+ subscribersto a special address, with address
+ (queue==SNDRV_SEQ_ADDRESS_SUBSCRIBERS). The message is then send to all
+ recipients that are registered in the subscription list. A typical
+ application for these SUBSCRIPTION events is handling of incoming MIDI
+ data. The port doesn't 'know' what other clients are interested in this
+ message. If for instance a MIDI recording application would like to receive
+ the events from that port, it will first have to subscribe with that port.
+ 
+*/
+
+struct snd_seq_subscribers {
+	struct snd_seq_port_subscribe info;	/* additional info */
+	struct list_head src_list;	/* link of sources */
+	struct list_head dest_list;	/* link of destinations */
+	atomic_t ref_count;
+};
+
+struct snd_seq_port_subs_info {
+	struct list_head list_head;	/* list of subscribed ports */
+	unsigned int count;		/* count of subscribers */
+	unsigned int exclusive: 1;	/* exclusive mode */
+	struct rw_semaphore list_mutex;
+	rwlock_t list_lock;
+	int (*open)(void *private_data, struct snd_seq_port_subscribe *info);
+	int (*close)(void *private_data, struct snd_seq_port_subscribe *info);
+};
+
+struct snd_seq_client_port {
+
+	struct snd_seq_addr addr;	/* client/port number */
+	struct module *owner;		/* owner of this port */
+	char name[64];			/* port name */	
+	struct list_head list;		/* port list */
+	snd_use_lock_t use_lock;
+
+	/* subscribers */
+	struct snd_seq_port_subs_info c_src;	/* read (sender) list */
+	struct snd_seq_port_subs_info c_dest;	/* write (dest) list */
+
+	int (*event_input)(struct snd_seq_event *ev, int direct, void *private_data,
+			   int atomic, int hop);
+	void (*private_free)(void *private_data);
+	void *private_data;
+	unsigned int callback_all : 1;
+	unsigned int closing : 1;
+	unsigned int timestamping: 1;
+	unsigned int time_real: 1;
+	int time_queue;
+	
+	/* capability, inport, output, sync */
+	unsigned int capability;	/* port capability bits */
+	unsigned int type;		/* port type bits */
+
+	/* supported channels */
+	int midi_channels;
+	int midi_voices;
+	int synth_voices;
+		
+};
+
+struct snd_seq_client;
+
+/* return pointer to port structure and lock port */
+struct snd_seq_client_port *snd_seq_port_use_ptr(struct snd_seq_client *client, int num);
+
+/* search for next port - port is locked if found */
+struct snd_seq_client_port *snd_seq_port_query_nearest(struct snd_seq_client *client,
+						       struct snd_seq_port_info *pinfo);
+
+/* unlock the port */
+#define snd_seq_port_unlock(port) snd_use_lock_free(&(port)->use_lock)
+
+/* create a port, port number is returned (-1 on failure) */
+struct snd_seq_client_port *snd_seq_create_port(struct snd_seq_client *client, int port_index);
+
+/* delete a port */
+int snd_seq_delete_port(struct snd_seq_client *client, int port);
+
+/* delete all ports */
+int snd_seq_delete_all_ports(struct snd_seq_client *client);
+
+/* set port info fields */
+int snd_seq_set_port_info(struct snd_seq_client_port *port,
+			  struct snd_seq_port_info *info);
+
+/* get port info fields */
+int snd_seq_get_port_info(struct snd_seq_client_port *port,
+			  struct snd_seq_port_info *info);
+
+/* add subscriber to subscription list */
+int snd_seq_port_connect(struct snd_seq_client *caller,
+			 struct snd_seq_client *s, struct snd_seq_client_port *sp,
+			 struct snd_seq_client *d, struct snd_seq_client_port *dp,
+			 struct snd_seq_port_subscribe *info);
+
+/* remove subscriber from subscription list */ 
+int snd_seq_port_disconnect(struct snd_seq_client *caller,
+			    struct snd_seq_client *s, struct snd_seq_client_port *sp,
+			    struct snd_seq_client *d, struct snd_seq_client_port *dp,
+			    struct snd_seq_port_subscribe *info);
+
+/* subscribe port */
+int snd_seq_port_subscribe(struct snd_seq_client_port *port,
+			   struct snd_seq_port_subscribe *info);
+
+/* get matched subscriber */
+struct snd_seq_subscribers *snd_seq_port_get_subscription(struct snd_seq_port_subs_info *src_grp,
+							  struct snd_seq_addr *dest_addr);
+
+#endif
Index: /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_prioq.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_prioq.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_prioq.c	(revision 91)
@@ -0,0 +1,453 @@
+/*
+ *   ALSA sequencer Priority Queue
+ *   Copyright (c) 1998-1999 by Frank van de Pol <fvdpol@coil.demon.nl>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include "seq_timer.h"
+#include "seq_prioq.h"
+
+
+/* Implementation is a simple linked list for now...
+
+   This priority queue orders the events on timestamp. For events with an
+   equeal timestamp the queue behaves as a FIFO. 
+
+   *
+   *           +-------+
+   *  Head --> | first |
+   *           +-------+
+   *                 |next
+   *           +-----v-+
+   *           |       |
+   *           +-------+
+   *                 |
+   *           +-----v-+
+   *           |       |
+   *           +-------+
+   *                 |
+   *           +-----v-+
+   *  Tail --> | last  |
+   *           +-------+
+   *
+
+ */
+
+
+
+/* create new prioq (constructor) */
+struct snd_seq_prioq *snd_seq_prioq_new(void)
+{
+	struct snd_seq_prioq *f;
+
+	f = (struct snd_seq_prioq *)kzalloc(sizeof(*f), GFP_KERNEL);
+	if (f == NULL) {
+		snd_printd("oops: malloc failed for snd_seq_prioq_new()\n");
+		return NULL;
+	}
+	
+	spin_lock_init(&f->lock);
+	f->head = NULL;
+	f->tail = NULL;
+	f->cells = 0;
+	
+	return f;
+}
+
+/* delete prioq (destructor) */
+void snd_seq_prioq_delete(struct snd_seq_prioq **fifo)
+{
+	struct snd_seq_prioq *f = *fifo;
+	*fifo = NULL;
+
+	if (f == NULL) {
+		snd_printd("oops: snd_seq_prioq_delete() called with NULL prioq\n");
+		return;
+	}
+
+	/* release resources...*/
+	/*....................*/
+	
+	if (f->cells > 0) {
+		/* drain prioQ */
+		while (f->cells > 0)
+			snd_seq_cell_free(snd_seq_prioq_cell_out(f));
+	}
+	
+	kfree(f);
+}
+
+
+
+
+/* compare timestamp between events */
+/* return 1 if a >= b; 0 */
+static inline int compare_timestamp(struct snd_seq_event *a,
+				    struct snd_seq_event *b)
+{
+	if ((a->flags & SNDRV_SEQ_TIME_STAMP_MASK) == SNDRV_SEQ_TIME_STAMP_TICK) {
+		/* compare ticks */
+		return (snd_seq_compare_tick_time(&a->time.tick, &b->time.tick));
+	} else {
+		/* compare real time */
+		return (snd_seq_compare_real_time(&a->time.time, &b->time.time));
+	}
+}
+
+/* compare timestamp between events */
+/* return negative if a < b;
+ *        zero     if a = b;
+ *        positive if a > b;
+ */
+static inline int compare_timestamp_rel(struct snd_seq_event *a,
+					struct snd_seq_event *b)
+{
+	if ((a->flags & SNDRV_SEQ_TIME_STAMP_MASK) == SNDRV_SEQ_TIME_STAMP_TICK) {
+		/* compare ticks */
+		if (a->time.tick > b->time.tick)
+			return 1;
+		else if (a->time.tick == b->time.tick)
+			return 0;
+		else
+			return -1;
+	} else {
+		/* compare real time */
+		if (a->time.time.tv_sec > b->time.time.tv_sec)
+			return 1;
+		else if (a->time.time.tv_sec == b->time.time.tv_sec) {
+			if (a->time.time.tv_nsec > b->time.time.tv_nsec)
+				return 1;
+			else if (a->time.time.tv_nsec == b->time.time.tv_nsec)
+				return 0;
+			else
+				return -1;
+		} else
+			return -1;
+	}
+}
+
+/* enqueue cell to prioq */
+int snd_seq_prioq_cell_in(struct snd_seq_prioq * f,
+			  struct snd_seq_event_cell * cell)
+{
+	struct snd_seq_event_cell *cur, *prev;
+	unsigned long flags;
+	int count;
+	int prior;
+
+	snd_assert(f, return -EINVAL);
+	snd_assert(cell, return -EINVAL);
+	
+	/* check flags */
+	prior = (cell->event.flags & SNDRV_SEQ_PRIORITY_MASK);
+
+	spin_lock_irqsave(&f->lock, flags);
+
+	/* check if this element needs to inserted at the end (ie. ordered 
+	   data is inserted) This will be very likeley if a sequencer 
+	   application or midi file player is feeding us (sequential) data */
+	if (f->tail && !prior) {
+		if (compare_timestamp(&cell->event, &f->tail->event)) {
+			/* add new cell to tail of the fifo */
+			f->tail->next = cell;
+			f->tail = cell;
+			cell->next = NULL;
+			f->cells++;
+			spin_unlock_irqrestore(&f->lock, flags);
+			return 0;
+		}
+	}
+	/* traverse list of elements to find the place where the new cell is
+	   to be inserted... Note that this is a order n process ! */
+
+	prev = NULL;		/* previous cell */
+	cur = f->head;		/* cursor */
+
+	count = 10000; /* FIXME: enough big, isn't it? */
+	while (cur != NULL) {
+		/* compare timestamps */
+		int rel = compare_timestamp_rel(&cell->event, &cur->event);
+		if (rel < 0)
+			/* new cell has earlier schedule time, */
+			break;
+		else if (rel == 0 && prior)
+			/* equal schedule time and prior to others */
+			break;
+		/* new cell has equal or larger schedule time, */
+		/* move cursor to next cell */
+		prev = cur;
+		cur = cur->next;
+		if (! --count) {
+			spin_unlock_irqrestore(&f->lock, flags);
+			snd_printk(KERN_ERR "cannot find a pointer.. infinite loop?\n");
+			return -EINVAL;
+		}
+	}
+
+	/* insert it before cursor */
+	if (prev != NULL)
+		prev->next = cell;
+	cell->next = cur;
+
+	if (f->head == cur) /* this is the first cell, set head to it */
+		f->head = cell;
+	if (cur == NULL) /* reached end of the list */
+		f->tail = cell;
+	f->cells++;
+	spin_unlock_irqrestore(&f->lock, flags);
+	return 0;
+}
+
+/* dequeue cell from prioq */
+struct snd_seq_event_cell *snd_seq_prioq_cell_out(struct snd_seq_prioq *f)
+{
+	struct snd_seq_event_cell *cell;
+	unsigned long flags;
+
+	if (f == NULL) {
+		snd_printd("oops: snd_seq_prioq_cell_in() called with NULL prioq\n");
+		return NULL;
+	}
+	spin_lock_irqsave(&f->lock, flags);
+
+	cell = f->head;
+	if (cell) {
+		f->head = cell->next;
+
+		/* reset tail if this was the last element */
+		if (f->tail == cell)
+			f->tail = NULL;
+
+		cell->next = NULL;
+		f->cells--;
+	}
+
+	spin_unlock_irqrestore(&f->lock, flags);
+	return cell;
+}
+
+/* return number of events available in prioq */
+int snd_seq_prioq_avail(struct snd_seq_prioq * f)
+{
+	if (f == NULL) {
+		snd_printd("oops: snd_seq_prioq_cell_in() called with NULL prioq\n");
+		return 0;
+	}
+	return f->cells;
+}
+
+
+/* peek at cell at the head of the prioq */
+struct snd_seq_event_cell *snd_seq_prioq_cell_peek(struct snd_seq_prioq * f)
+{
+	if (f == NULL) {
+		snd_printd("oops: snd_seq_prioq_cell_in() called with NULL prioq\n");
+		return NULL;
+	}
+	return f->head;
+}
+
+
+static inline int prioq_match(struct snd_seq_event_cell *cell,
+			      int client, int timestamp)
+{
+	if (cell->event.source.client == client ||
+	    cell->event.dest.client == client)
+		return 1;
+	if (!timestamp)
+		return 0;
+	switch (cell->event.flags & SNDRV_SEQ_TIME_STAMP_MASK) {
+	case SNDRV_SEQ_TIME_STAMP_TICK:
+		if (cell->event.time.tick)
+			return 1;
+		break;
+	case SNDRV_SEQ_TIME_STAMP_REAL:
+		if (cell->event.time.time.tv_sec ||
+		    cell->event.time.time.tv_nsec)
+			return 1;
+		break;
+	}
+	return 0;
+}
+
+/* remove cells for left client */
+void snd_seq_prioq_leave(struct snd_seq_prioq * f, int client, int timestamp)
+{
+	register struct snd_seq_event_cell *cell, *next;
+	unsigned long flags;
+	struct snd_seq_event_cell *prev = NULL;
+	struct snd_seq_event_cell *freefirst = NULL, *freeprev = NULL, *freenext;
+
+	/* collect all removed cells */
+	spin_lock_irqsave(&f->lock, flags);
+	cell = f->head;
+	while (cell) {
+		next = cell->next;
+		if (prioq_match(cell, client, timestamp)) {
+			/* remove cell from prioq */
+			if (cell == f->head) {
+				f->head = cell->next;
+			} else {
+				prev->next = cell->next;
+			}
+			if (cell == f->tail)
+				f->tail = cell->next;
+			f->cells--;
+			/* add cell to free list */
+			cell->next = NULL;
+			if (freefirst == NULL) {
+				freefirst = cell;
+			} else {
+				freeprev->next = cell;
+			}
+			freeprev = cell;
+		} else {
+#if 0
+			printk("type = %i, source = %i, dest = %i, client = %i\n",
+				cell->event.type,
+				cell->event.source.client,
+				cell->event.dest.client,
+				client);
+#endif
+			prev = cell;
+		}
+		cell = next;		
+	}
+	spin_unlock_irqrestore(&f->lock, flags);	
+
+	/* remove selected cells */
+	while (freefirst) {
+		freenext = freefirst->next;
+		snd_seq_cell_free(freefirst);
+		freefirst = freenext;
+	}
+}
+
+static int prioq_remove_match(struct snd_seq_remove_events *info,
+			      struct snd_seq_event *ev)
+{
+	int res;
+
+	if (info->remove_mode & SNDRV_SEQ_REMOVE_DEST) {
+		if (ev->dest.client != info->dest.client ||
+				ev->dest.port != info->dest.port)
+			return 0;
+	}
+	if (info->remove_mode & SNDRV_SEQ_REMOVE_DEST_CHANNEL) {
+		if (! snd_seq_ev_is_channel_type(ev))
+			return 0;
+		/* data.note.channel and data.control.channel are identical */
+		if (ev->data.note.channel != info->channel)
+			return 0;
+	}
+	if (info->remove_mode & SNDRV_SEQ_REMOVE_TIME_AFTER) {
+		if (info->remove_mode & SNDRV_SEQ_REMOVE_TIME_TICK)
+			res = snd_seq_compare_tick_time(&ev->time.tick, &info->time.tick);
+		else
+			res = snd_seq_compare_real_time(&ev->time.time, &info->time.time);
+		if (!res)
+			return 0;
+	}
+	if (info->remove_mode & SNDRV_SEQ_REMOVE_TIME_BEFORE) {
+		if (info->remove_mode & SNDRV_SEQ_REMOVE_TIME_TICK)
+			res = snd_seq_compare_tick_time(&ev->time.tick, &info->time.tick);
+		else
+			res = snd_seq_compare_real_time(&ev->time.time, &info->time.time);
+		if (res)
+			return 0;
+	}
+	if (info->remove_mode & SNDRV_SEQ_REMOVE_EVENT_TYPE) {
+		if (ev->type != info->type)
+			return 0;
+	}
+	if (info->remove_mode & SNDRV_SEQ_REMOVE_IGNORE_OFF) {
+		/* Do not remove off events */
+		switch (ev->type) {
+		case SNDRV_SEQ_EVENT_NOTEOFF:
+		/* case SNDRV_SEQ_EVENT_SAMPLE_STOP: */
+			return 0;
+		default:
+			break;
+		}
+	}
+	if (info->remove_mode & SNDRV_SEQ_REMOVE_TAG_MATCH) {
+		if (info->tag != ev->tag)
+			return 0;
+	}
+
+	return 1;
+}
+
+/* remove cells matching remove criteria */
+void snd_seq_prioq_remove_events(struct snd_seq_prioq * f, int client,
+				 struct snd_seq_remove_events *info)
+{
+	struct snd_seq_event_cell *cell, *next;
+	unsigned long flags;
+	struct snd_seq_event_cell *prev = NULL;
+	struct snd_seq_event_cell *freefirst = NULL, *freeprev = NULL, *freenext;
+
+	/* collect all removed cells */
+	spin_lock_irqsave(&f->lock, flags);
+	cell = f->head;
+
+	while (cell) {
+		next = cell->next;
+		if (cell->event.source.client == client &&
+			prioq_remove_match(info, &cell->event)) {
+
+			/* remove cell from prioq */
+			if (cell == f->head) {
+				f->head = cell->next;
+			} else {
+				prev->next = cell->next;
+			}
+
+			if (cell == f->tail)
+				f->tail = cell->next;
+			f->cells--;
+
+			/* add cell to free list */
+			cell->next = NULL;
+			if (freefirst == NULL) {
+				freefirst = cell;
+			} else {
+				freeprev->next = cell;
+			}
+
+			freeprev = cell;
+		} else {
+			prev = cell;
+		}
+		cell = next;		
+	}
+	spin_unlock_irqrestore(&f->lock, flags);	
+
+	/* remove selected cells */
+	while (freefirst) {
+		freenext = freefirst->next;
+		snd_seq_cell_free(freefirst);
+		freefirst = freenext;
+	}
+}
+
+
Index: /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_prioq.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_prioq.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_prioq.h	(revision 91)
@@ -0,0 +1,62 @@
+/*
+ *   ALSA sequencer Priority Queue
+ *   Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+#ifndef __SND_SEQ_PRIOQ_H
+#define __SND_SEQ_PRIOQ_H
+
+#include "seq_memory.h"
+
+
+/* === PRIOQ === */
+
+struct snd_seq_prioq {
+	struct snd_seq_event_cell *head;      /* pointer to head of prioq */
+	struct snd_seq_event_cell *tail;      /* pointer to tail of prioq */
+	int cells;
+	spinlock_t lock;
+};
+
+
+/* create new prioq (constructor) */
+struct snd_seq_prioq *snd_seq_prioq_new(void);
+
+/* delete prioq (destructor) */
+void snd_seq_prioq_delete(struct snd_seq_prioq **fifo);
+
+/* enqueue cell to prioq */
+int snd_seq_prioq_cell_in(struct snd_seq_prioq *f, struct snd_seq_event_cell *cell);
+
+/* dequeue cell from prioq */ 
+struct snd_seq_event_cell *snd_seq_prioq_cell_out(struct snd_seq_prioq *f);
+
+/* return number of events available in prioq */
+int snd_seq_prioq_avail(struct snd_seq_prioq *f);
+
+/* peek at cell at the head of the prioq */
+struct snd_seq_event_cell *snd_seq_prioq_cell_peek(struct snd_seq_prioq *f);
+
+/* client left queue */
+void snd_seq_prioq_leave(struct snd_seq_prioq *f, int client, int timestamp);        
+
+/* Remove events */
+void snd_seq_prioq_remove_events(struct snd_seq_prioq *f, int client,
+				 struct snd_seq_remove_events *info);
+
+#endif
Index: /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_queue.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_queue.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_queue.c	(revision 91)
@@ -0,0 +1,791 @@
+/*
+ *   ALSA sequencer Timing queue handling
+ *   Copyright (c) 1998-1999 by Frank van de Pol <fvdpol@coil.demon.nl>
+ *
+ *   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
+ *
+ * MAJOR CHANGES
+ *   Nov. 13, 1999	Takashi Iwai <iwai@ww.uni-erlangen.de>
+ *     - Queues are allocated dynamically via ioctl.
+ *     - When owner client is deleted, all owned queues are deleted, too.
+ *     - Owner of unlocked queue is kept unmodified even if it is
+ *	 manipulated by other clients.
+ *     - Owner field in SET_QUEUE_OWNER ioctl must be identical with the
+ *       caller client.  i.e. Changing owner to a third client is not
+ *       allowed.
+ *
+ *  Aug. 30, 2000	Takashi Iwai
+ *     - Queues are managed in static array again, but with better way.
+ *       The API itself is identical.
+ *     - The queue is locked when struct snd_seq_queue pointer is returned via
+ *       queueptr().  This pointer *MUST* be released afterward by
+ *       queuefree(ptr).
+ *     - Addition of experimental sync support.
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+
+#include "seq_memory.h"
+#include "seq_queue.h"
+#include "seq_clientmgr.h"
+#include "seq_fifo.h"
+#include "seq_timer.h"
+#include "seq_info.h"
+
+/* list of allocated queues */
+static struct snd_seq_queue *queue_list[SNDRV_SEQ_MAX_QUEUES];
+static spinlock_t queue_list_lock = SPIN_LOCK_UNLOCKED;
+/* number of queues allocated */
+static int num_queues;
+
+int snd_seq_queue_get_cur_queues(void)
+{
+	return num_queues;
+}
+
+/*----------------------------------------------------------------*/
+
+/* assign queue id and insert to list */
+static int queue_list_add(struct snd_seq_queue *q)
+{
+	int i;
+	unsigned long flags;
+
+	spin_lock_irqsave(&queue_list_lock, flags);
+	for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
+		if (! queue_list[i]) {
+			queue_list[i] = q;
+			q->queue = i;
+			num_queues++;
+			spin_unlock_irqrestore(&queue_list_lock, flags);
+			return i;
+		}
+	}
+	spin_unlock_irqrestore(&queue_list_lock, flags);
+	return -1;
+}
+
+static struct snd_seq_queue *queue_list_remove(int id, int client)
+{
+	struct snd_seq_queue *q;
+	unsigned long flags;
+
+	spin_lock_irqsave(&queue_list_lock, flags);
+	q = queue_list[id];
+	if (q) {
+		spin_lock(&q->owner_lock);
+		if (q->owner == client) {
+			/* found */
+			q->klocked = 1;
+			spin_unlock(&q->owner_lock);
+			queue_list[id] = NULL;
+			num_queues--;
+			spin_unlock_irqrestore(&queue_list_lock, flags);
+			return q;
+		}
+		spin_unlock(&q->owner_lock);
+	}
+	spin_unlock_irqrestore(&queue_list_lock, flags);
+	return NULL;
+}
+
+/*----------------------------------------------------------------*/
+
+/* create new queue (constructor) */
+static struct snd_seq_queue *queue_new(int owner, int locked)
+{
+	struct snd_seq_queue *q;
+
+	q = (struct snd_seq_queue *)kzalloc(sizeof(*q), GFP_KERNEL);
+	if (q == NULL) {
+		snd_printd("malloc failed for snd_seq_queue_new()\n");
+		return NULL;
+	}
+
+	spin_lock_init(&q->owner_lock);
+	spin_lock_init(&q->check_lock);
+	init_MUTEX(&q->timer_mutex);
+	snd_use_lock_init(&q->use_lock);
+	q->queue = -1;
+
+	q->tickq = snd_seq_prioq_new();
+	q->timeq = snd_seq_prioq_new();
+	q->timer = snd_seq_timer_new();
+	if (q->tickq == NULL || q->timeq == NULL || q->timer == NULL) {
+		snd_seq_prioq_delete(&q->tickq);
+		snd_seq_prioq_delete(&q->timeq);
+		snd_seq_timer_delete(&q->timer);
+		kfree(q);
+		return NULL;
+	}
+
+	q->owner = owner;
+	q->locked = locked;
+	q->klocked = 0;
+
+	return q;
+}
+
+/* delete queue (destructor) */
+static void queue_delete(struct snd_seq_queue *q)
+{
+	/* stop and release the timer */
+	snd_seq_timer_stop(q->timer);
+	snd_seq_timer_close(q);
+	/* wait until access free */
+	snd_use_lock_sync(&q->use_lock);
+	/* release resources... */
+	snd_seq_prioq_delete(&q->tickq);
+	snd_seq_prioq_delete(&q->timeq);
+	snd_seq_timer_delete(&q->timer);
+
+	kfree(q);
+}
+
+
+/*----------------------------------------------------------------*/
+
+/* setup queues */
+int __init snd_seq_queues_init(void)
+{
+	/*
+	memset(queue_list, 0, sizeof(queue_list));
+	num_queues = 0;
+	*/
+	return 0;
+}
+
+/* delete all existing queues */
+void __exit snd_seq_queues_delete(void)
+{
+	int i;
+
+	/* clear list */
+	for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
+		if (queue_list[i])
+			queue_delete(queue_list[i]);
+	}
+}
+
+/* allocate a new queue -
+ * return queue index value or negative value for error
+ */
+int snd_seq_queue_alloc(int client, int locked, unsigned int info_flags)
+{
+	struct snd_seq_queue *q;
+
+	q = queue_new(client, locked);
+	if (q == NULL)
+		return -ENOMEM;
+	q->info_flags = info_flags;
+	if (queue_list_add(q) < 0) {
+		queue_delete(q);
+		return -ENOMEM;
+	}
+	snd_seq_queue_use(q->queue, client, 1); /* use this queue */
+	return q->queue;
+}
+
+/* delete a queue - queue must be owned by the client */
+int snd_seq_queue_delete(int client, int queueid)
+{
+	struct snd_seq_queue *q;
+
+	if (queueid < 0 || queueid >= SNDRV_SEQ_MAX_QUEUES)
+		return -EINVAL;
+	q = queue_list_remove(queueid, client);
+	if (q == NULL)
+		return -EINVAL;
+	queue_delete(q);
+
+	return 0;
+}
+
+
+/* return pointer to queue structure for specified id */
+struct snd_seq_queue *queueptr(int queueid)
+{
+	struct snd_seq_queue *q;
+	unsigned long flags;
+
+	if (queueid < 0 || queueid >= SNDRV_SEQ_MAX_QUEUES)
+		return NULL;
+	spin_lock_irqsave(&queue_list_lock, flags);
+	q = queue_list[queueid];
+	if (q)
+		snd_use_lock_use(&q->use_lock);
+	spin_unlock_irqrestore(&queue_list_lock, flags);
+	return q;
+}
+
+/* return the (first) queue matching with the specified name */
+struct snd_seq_queue *snd_seq_queue_find_name(char *name)
+{
+	int i;
+	struct snd_seq_queue *q;
+
+	for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
+		if ((q = queueptr(i)) != NULL) {
+			if (strncmp(q->name, name, sizeof(q->name)) == 0)
+				return q;
+			queuefree(q);
+		}
+	}
+	return NULL;
+}
+
+
+/* -------------------------------------------------------- */
+
+void snd_seq_check_queue(struct snd_seq_queue *q, int atomic, int hop)
+{
+	unsigned long flags;
+	struct snd_seq_event_cell *cell;
+
+	if (q == NULL)
+		return;
+
+	/* make this function non-reentrant */
+	spin_lock_irqsave(&q->check_lock, flags);
+	if (q->check_blocked) {
+		q->check_again = 1;
+		spin_unlock_irqrestore(&q->check_lock, flags);
+		return;		/* other thread is already checking queues */
+	}
+	q->check_blocked = 1;
+	spin_unlock_irqrestore(&q->check_lock, flags);
+
+      __again:
+	/* Process tick queue... */
+	while ((cell = snd_seq_prioq_cell_peek(q->tickq)) != NULL) {
+		if (snd_seq_compare_tick_time(&q->timer->tick.cur_tick,
+					      &cell->event.time.tick)) {
+			cell = snd_seq_prioq_cell_out(q->tickq);
+			if (cell)
+				snd_seq_dispatch_event(cell, atomic, hop);
+		} else {
+			/* event remains in the queue */
+			break;
+		}
+	}
+
+
+	/* Process time queue... */
+	while ((cell = snd_seq_prioq_cell_peek(q->timeq)) != NULL) {
+		if (snd_seq_compare_real_time(&q->timer->cur_time,
+					      &cell->event.time.time)) {
+			cell = snd_seq_prioq_cell_out(q->timeq);
+			if (cell)
+				snd_seq_dispatch_event(cell, atomic, hop);
+		} else {
+			/* event remains in the queue */
+			break;
+		}
+	}
+
+	/* free lock */
+	spin_lock_irqsave(&q->check_lock, flags);
+	if (q->check_again) {
+		q->check_again = 0;
+		spin_unlock_irqrestore(&q->check_lock, flags);
+		goto __again;
+	}
+	q->check_blocked = 0;
+	spin_unlock_irqrestore(&q->check_lock, flags);
+}
+
+
+/* enqueue a event to singe queue */
+int snd_seq_enqueue_event(struct snd_seq_event_cell *cell, int atomic, int hop)
+{
+	int dest, err;
+	struct snd_seq_queue *q;
+
+	snd_assert(cell != NULL, return -EINVAL);
+	dest = cell->event.queue;	/* destination queue */
+	q = queueptr(dest);
+	if (q == NULL)
+		return -EINVAL;
+	/* handle relative time stamps, convert them into absolute */
+	if ((cell->event.flags & SNDRV_SEQ_TIME_MODE_MASK) == SNDRV_SEQ_TIME_MODE_REL) {
+		switch (cell->event.flags & SNDRV_SEQ_TIME_STAMP_MASK) {
+		case SNDRV_SEQ_TIME_STAMP_TICK:
+			cell->event.time.tick += q->timer->tick.cur_tick;
+			break;
+
+		case SNDRV_SEQ_TIME_STAMP_REAL:
+			snd_seq_inc_real_time(&cell->event.time.time,
+					      &q->timer->cur_time);
+			break;
+		}
+		cell->event.flags &= ~SNDRV_SEQ_TIME_MODE_MASK;
+		cell->event.flags |= SNDRV_SEQ_TIME_MODE_ABS;
+	}
+	/* enqueue event in the real-time or midi queue */
+	switch (cell->event.flags & SNDRV_SEQ_TIME_STAMP_MASK) {
+	case SNDRV_SEQ_TIME_STAMP_TICK:
+		err = snd_seq_prioq_cell_in(q->tickq, cell);
+		break;
+
+	case SNDRV_SEQ_TIME_STAMP_REAL:
+	default:
+		err = snd_seq_prioq_cell_in(q->timeq, cell);
+		break;
+	}
+
+	if (err < 0) {
+		queuefree(q); /* unlock */
+		return err;
+	}
+
+	/* trigger dispatching */
+	snd_seq_check_queue(q, atomic, hop);
+
+	queuefree(q); /* unlock */
+
+	return 0;
+}
+
+
+/*----------------------------------------------------------------*/
+
+static inline int check_access(struct snd_seq_queue *q, int client)
+{
+	return (q->owner == client) || (!q->locked && !q->klocked);
+}
+
+/* check if the client has permission to modify queue parameters.
+ * if it does, lock the queue
+ */
+static int queue_access_lock(struct snd_seq_queue *q, int client)
+{
+	unsigned long flags;
+	int access_ok;
+	
+	spin_lock_irqsave(&q->owner_lock, flags);
+	access_ok = check_access(q, client);
+	if (access_ok)
+		q->klocked = 1;
+	spin_unlock_irqrestore(&q->owner_lock, flags);
+	return access_ok;
+}
+
+/* unlock the queue */
+static inline void queue_access_unlock(struct snd_seq_queue *q)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&q->owner_lock, flags);
+	q->klocked = 0;
+	spin_unlock_irqrestore(&q->owner_lock, flags);
+}
+
+/* exported - only checking permission */
+int snd_seq_queue_check_access(int queueid, int client)
+{
+	struct snd_seq_queue *q = queueptr(queueid);
+	int access_ok;
+	unsigned long flags;
+
+	if (! q)
+		return 0;
+	spin_lock_irqsave(&q->owner_lock, flags);
+	access_ok = check_access(q, client);
+	spin_unlock_irqrestore(&q->owner_lock, flags);
+	queuefree(q);
+	return access_ok;
+}
+
+/*----------------------------------------------------------------*/
+
+/*
+ * change queue's owner and permission
+ */
+int snd_seq_queue_set_owner(int queueid, int client, int locked)
+{
+	struct snd_seq_queue *q = queueptr(queueid);
+
+	if (q == NULL)
+		return -EINVAL;
+
+	if (! queue_access_lock(q, client)) {
+		queuefree(q);
+		return -EPERM;
+	}
+
+	q->locked = locked ? 1 : 0;
+	q->owner = client;
+	queue_access_unlock(q);
+	queuefree(q);
+
+	return 0;
+}
+
+
+/*----------------------------------------------------------------*/
+
+/* open timer -
+ * q->use mutex should be down before calling this function to avoid
+ * confliction with snd_seq_queue_use()
+ */
+int snd_seq_queue_timer_open(int queueid)
+{
+	int result = 0;
+	struct snd_seq_queue *queue;
+	struct snd_seq_timer *tmr;
+
+	queue = queueptr(queueid);
+	if (queue == NULL)
+		return -EINVAL;
+	tmr = queue->timer;
+	if ((result = snd_seq_timer_open(queue)) < 0) {
+		snd_seq_timer_defaults(tmr);
+		result = snd_seq_timer_open(queue);
+	}
+	queuefree(queue);
+	return result;
+}
+
+/* close timer -
+ * q->use mutex should be down before calling this function
+ */
+int snd_seq_queue_timer_close(int queueid)
+{
+	struct snd_seq_queue *queue;
+	struct snd_seq_timer *tmr;
+	int result = 0;
+
+	queue = queueptr(queueid);
+	if (queue == NULL)
+		return -EINVAL;
+	tmr = queue->timer;
+	snd_seq_timer_close(queue);
+	queuefree(queue);
+	return result;
+}
+
+/* change queue tempo and ppq */
+int snd_seq_queue_timer_set_tempo(int queueid, int client,
+				  struct snd_seq_queue_tempo *info)
+{
+	struct snd_seq_queue *q = queueptr(queueid);
+	int result;
+
+	if (q == NULL)
+		return -EINVAL;
+	if (! queue_access_lock(q, client)) {
+		queuefree(q);
+		return -EPERM;
+	}
+
+	result = snd_seq_timer_set_tempo(q->timer, info->tempo);
+	if (result >= 0)
+		result = snd_seq_timer_set_ppq(q->timer, info->ppq);
+	if (result >= 0 && info->skew_base > 0)
+		result = snd_seq_timer_set_skew(q->timer, info->skew_value,
+						info->skew_base);
+	queue_access_unlock(q);
+	queuefree(q);
+	return result;
+}
+
+
+/* use or unuse this queue -
+ * if it is the first client, starts the timer.
+ * if it is not longer used by any clients, stop the timer.
+ */
+int snd_seq_queue_use(int queueid, int client, int use)
+{
+	struct snd_seq_queue *queue;
+
+	queue = queueptr(queueid);
+	if (queue == NULL)
+		return -EINVAL;
+	down(&queue->timer_mutex);
+	if (use) {
+		if (!test_and_set_bit(client, queue->clients_bitmap))
+			queue->clients++;
+	} else {
+		if (test_and_clear_bit(client, queue->clients_bitmap))
+			queue->clients--;
+	}
+	if (queue->clients) {
+		if (use && queue->clients == 1)
+			snd_seq_timer_defaults(queue->timer);
+		snd_seq_timer_open(queue);
+	} else {
+		snd_seq_timer_close(queue);
+	}
+	up(&queue->timer_mutex);
+	queuefree(queue);
+	return 0;
+}
+
+/*
+ * check if queue is used by the client
+ * return negative value if the queue is invalid.
+ * return 0 if not used, 1 if used.
+ */
+int snd_seq_queue_is_used(int queueid, int client)
+{
+	struct snd_seq_queue *q;
+	int result;
+
+	q = queueptr(queueid);
+	if (q == NULL)
+		return -EINVAL; /* invalid queue */
+	result = test_bit(client, q->clients_bitmap) ? 1 : 0;
+	queuefree(q);
+	return result;
+}
+
+
+/*----------------------------------------------------------------*/
+
+/* notification that client has left the system -
+ * stop the timer on all queues owned by this client
+ */
+void snd_seq_queue_client_termination(int client)
+{
+	unsigned long flags;
+	int i;
+	struct snd_seq_queue *q;
+
+	for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
+		if ((q = queueptr(i)) == NULL)
+			continue;
+		spin_lock_irqsave(&q->owner_lock, flags);
+		if (q->owner == client)
+			q->klocked = 1;
+		spin_unlock_irqrestore(&q->owner_lock, flags);
+		if (q->owner == client) {
+			if (q->timer->running)
+				snd_seq_timer_stop(q->timer);
+			snd_seq_timer_reset(q->timer);
+		}
+		queuefree(q);
+	}
+}
+
+/* final stage notification -
+ * remove cells for no longer exist client (for non-owned queue)
+ * or delete this queue (for owned queue)
+ */
+void snd_seq_queue_client_leave(int client)
+{
+	int i;
+	struct snd_seq_queue *q;
+
+	/* delete own queues from queue list */
+	for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
+		if ((q = queue_list_remove(i, client)) != NULL)
+			queue_delete(q);
+	}
+
+	/* remove cells from existing queues -
+	 * they are not owned by this client
+	 */
+	for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
+		if ((q = queueptr(i)) == NULL)
+			continue;
+		if (test_bit(client, q->clients_bitmap)) {
+			snd_seq_prioq_leave(q->tickq, client, 0);
+			snd_seq_prioq_leave(q->timeq, client, 0);
+			snd_seq_queue_use(q->queue, client, 0);
+		}
+		queuefree(q);
+	}
+}
+
+
+
+/*----------------------------------------------------------------*/
+
+/* remove cells from all queues */
+void snd_seq_queue_client_leave_cells(int client)
+{
+	int i;
+	struct snd_seq_queue *q;
+
+	for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
+		if ((q = queueptr(i)) == NULL)
+			continue;
+		snd_seq_prioq_leave(q->tickq, client, 0);
+		snd_seq_prioq_leave(q->timeq, client, 0);
+		queuefree(q);
+	}
+}
+
+/* remove cells based on flush criteria */
+void snd_seq_queue_remove_cells(int client, struct snd_seq_remove_events *info)
+{
+	int i;
+	struct snd_seq_queue *q;
+
+	for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
+		if ((q = queueptr(i)) == NULL)
+			continue;
+		if (test_bit(client, q->clients_bitmap) &&
+		    (! (info->remove_mode & SNDRV_SEQ_REMOVE_DEST) ||
+		     q->queue == info->queue)) {
+			snd_seq_prioq_remove_events(q->tickq, client, info);
+			snd_seq_prioq_remove_events(q->timeq, client, info);
+		}
+		queuefree(q);
+	}
+}
+
+/*----------------------------------------------------------------*/
+
+/*
+ * send events to all subscribed ports
+ */
+static void queue_broadcast_event(struct snd_seq_queue *q, struct snd_seq_event *ev,
+				  int atomic, int hop)
+{
+	struct snd_seq_event sev;
+
+	sev = *ev;
+	
+	sev.flags = SNDRV_SEQ_TIME_STAMP_TICK|SNDRV_SEQ_TIME_MODE_ABS;
+	sev.time.tick = q->timer->tick.cur_tick;
+	sev.queue = q->queue;
+	sev.data.queue.queue = q->queue;
+
+	/* broadcast events from Timer port */
+	sev.source.client = SNDRV_SEQ_CLIENT_SYSTEM;
+	sev.source.port = SNDRV_SEQ_PORT_SYSTEM_TIMER;
+	sev.dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS;
+	snd_seq_kernel_client_dispatch(SNDRV_SEQ_CLIENT_SYSTEM, &sev, atomic, hop);
+}
+
+/*
+ * process a received queue-control event.
+ * this function is exported for seq_sync.c.
+ */
+static void snd_seq_queue_process_event(struct snd_seq_queue *q,
+					struct snd_seq_event *ev,
+					int atomic, int hop)
+{
+	switch (ev->type) {
+	case SNDRV_SEQ_EVENT_START:
+		snd_seq_prioq_leave(q->tickq, ev->source.client, 1);
+		snd_seq_prioq_leave(q->timeq, ev->source.client, 1);
+		if (! snd_seq_timer_start(q->timer))
+			queue_broadcast_event(q, ev, atomic, hop);
+		break;
+
+	case SNDRV_SEQ_EVENT_CONTINUE:
+		if (! snd_seq_timer_continue(q->timer))
+			queue_broadcast_event(q, ev, atomic, hop);
+		break;
+
+	case SNDRV_SEQ_EVENT_STOP:
+		snd_seq_timer_stop(q->timer);
+		queue_broadcast_event(q, ev, atomic, hop);
+		break;
+
+	case SNDRV_SEQ_EVENT_TEMPO:
+		snd_seq_timer_set_tempo(q->timer, ev->data.queue.param.value);
+		queue_broadcast_event(q, ev, atomic, hop);
+		break;
+
+	case SNDRV_SEQ_EVENT_SETPOS_TICK:
+		if (snd_seq_timer_set_position_tick(q->timer, ev->data.queue.param.time.tick) == 0) {
+			queue_broadcast_event(q, ev, atomic, hop);
+		}
+		break;
+
+	case SNDRV_SEQ_EVENT_SETPOS_TIME:
+		if (snd_seq_timer_set_position_time(q->timer, ev->data.queue.param.time.time) == 0) {
+			queue_broadcast_event(q, ev, atomic, hop);
+		}
+		break;
+	case SNDRV_SEQ_EVENT_QUEUE_SKEW:
+		if (snd_seq_timer_set_skew(q->timer,
+					   ev->data.queue.param.skew.value,
+					   ev->data.queue.param.skew.base) == 0) {
+			queue_broadcast_event(q, ev, atomic, hop);
+		}
+		break;
+	}
+}
+
+
+/*
+ * Queue control via timer control port:
+ * this function is exported as a callback of timer port.
+ */
+int snd_seq_control_queue(struct snd_seq_event *ev, int atomic, int hop)
+{
+	struct snd_seq_queue *q;
+
+	snd_assert(ev != NULL, return -EINVAL);
+	q = queueptr(ev->data.queue.queue);
+
+	if (q == NULL)
+		return -EINVAL;
+
+	if (! queue_access_lock(q, ev->source.client)) {
+		queuefree(q);
+		return -EPERM;
+	}
+
+	snd_seq_queue_process_event(q, ev, atomic, hop);
+
+	queue_access_unlock(q);
+	queuefree(q);
+	return 0;
+}
+
+
+/*----------------------------------------------------------------*/
+
+/* exported to seq_info.c */
+void snd_seq_info_queues_read(struct snd_info_entry *entry, 
+			      struct snd_info_buffer *buffer)
+{
+	int i, bpm;
+	struct snd_seq_queue *q;
+	struct snd_seq_timer *tmr;
+
+	for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
+		if ((q = queueptr(i)) == NULL)
+			continue;
+
+		tmr = q->timer;
+		if (tmr->tempo)
+			bpm = 60000000 / tmr->tempo;
+		else
+			bpm = 0;
+
+		snd_iprintf(buffer, "queue %d: [%s]\n", q->queue, q->name);
+		snd_iprintf(buffer, "owned by client    : %d\n", q->owner);
+		snd_iprintf(buffer, "lock status        : %s\n", q->locked ? "Locked" : "Free");
+		snd_iprintf(buffer, "queued time events : %d\n", snd_seq_prioq_avail(q->timeq));
+		snd_iprintf(buffer, "queued tick events : %d\n", snd_seq_prioq_avail(q->tickq));
+		snd_iprintf(buffer, "timer state        : %s\n", tmr->running ? "Running" : "Stopped");
+		snd_iprintf(buffer, "timer PPQ          : %d\n", tmr->ppq);
+		snd_iprintf(buffer, "current tempo      : %d\n", tmr->tempo);
+		snd_iprintf(buffer, "current BPM        : %d\n", bpm);
+		snd_iprintf(buffer, "current time       : %d.%09d s\n", tmr->cur_time.tv_sec, tmr->cur_time.tv_nsec);
+		snd_iprintf(buffer, "current tick       : %d\n", tmr->tick.cur_tick);
+		snd_iprintf(buffer, "\n");
+		queuefree(q);
+	}
+}
Index: /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_queue.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_queue.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_queue.h	(revision 91)
@@ -0,0 +1,139 @@
+/*
+ *   ALSA sequencer Queue handling
+ *   Copyright (c) 1998-1999 by Frank van de Pol <fvdpol@coil.demon.nl>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+#ifndef __SND_SEQ_QUEUE_H
+#define __SND_SEQ_QUEUE_H
+
+#include "seq_memory.h"
+#include "seq_prioq.h"
+#include "seq_timer.h"
+#include "seq_lock.h"
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/bitops.h>
+
+#define SEQ_QUEUE_NO_OWNER (-1)
+
+struct snd_seq_queue {
+	int queue;		/* queue number */
+
+	char name[64];		/* name of this queue */
+
+	struct snd_seq_prioq	*tickq;		/* midi tick event queue */
+	struct snd_seq_prioq	*timeq;		/* real-time event queue */	
+	
+	struct snd_seq_timer *timer;	/* time keeper for this queue */
+	int	owner;		/* client that 'owns' the timer */
+	unsigned int	locked:1,	/* timer is only accesibble by owner if set */
+		klocked:1,	/* kernel lock (after START) */	
+		check_again:1,
+		check_blocked:1;
+
+	unsigned int flags;		/* status flags */
+	unsigned int info_flags;	/* info for sync */
+
+	spinlock_t owner_lock;
+	spinlock_t check_lock;
+
+	/* clients which uses this queue (bitmap) */
+	DECLARE_BITMAP(clients_bitmap, SNDRV_SEQ_MAX_CLIENTS);
+	unsigned int clients;	/* users of this queue */
+	struct semaphore timer_mutex;
+
+	snd_use_lock_t use_lock;
+};
+
+
+/* get the number of current queues */
+int snd_seq_queue_get_cur_queues(void);
+
+/* init queues structure */
+int snd_seq_queues_init(void);
+
+/* delete queues */ 
+void snd_seq_queues_delete(void);
+
+
+/* create new queue (constructor) */
+int snd_seq_queue_alloc(int client, int locked, unsigned int flags);
+
+/* delete queue (destructor) */
+int snd_seq_queue_delete(int client, int queueid);
+
+/* notification that client has left the system */
+void snd_seq_queue_client_termination(int client);
+
+/* final stage */
+void snd_seq_queue_client_leave(int client);
+
+/* enqueue a event received from one the clients */
+int snd_seq_enqueue_event(struct snd_seq_event_cell *cell, int atomic, int hop);
+
+/* Remove events */
+void snd_seq_queue_client_leave_cells(int client);
+void snd_seq_queue_remove_cells(int client, struct snd_seq_remove_events *info);
+
+/* return pointer to queue structure for specified id */
+struct snd_seq_queue *queueptr(int queueid);
+/* unlock */
+#define queuefree(q) snd_use_lock_free(&(q)->use_lock)
+
+/* return the (first) queue matching with the specified name */
+struct snd_seq_queue *snd_seq_queue_find_name(char *name);
+
+/* check single queue and dispatch events */
+void snd_seq_check_queue(struct snd_seq_queue *q, int atomic, int hop);
+
+/* access to queue's parameters */
+int snd_seq_queue_check_access(int queueid, int client);
+int snd_seq_queue_timer_set_tempo(int queueid, int client, struct snd_seq_queue_tempo *info);
+int snd_seq_queue_set_owner(int queueid, int client, int locked);
+int snd_seq_queue_set_locked(int queueid, int client, int locked);
+int snd_seq_queue_timer_open(int queueid);
+int snd_seq_queue_timer_close(int queueid);
+int snd_seq_queue_use(int queueid, int client, int use);
+int snd_seq_queue_is_used(int queueid, int client);
+
+int snd_seq_control_queue(struct snd_seq_event *ev, int atomic, int hop);
+
+/*
+ * 64bit division - for sync stuff..
+ */
+#if defined(i386) || defined(i486)
+
+#define udiv_qrnnd(q, r, n1, n0, d) \
+  __asm__ ("divl %4"		\
+	   : "=a" ((u32)(q)),	\
+	     "=d" ((u32)(r))	\
+	   : "0" ((u32)(n0)),	\
+	     "1" ((u32)(n1)),	\
+	     "rm" ((u32)(d)))
+
+#define u64_div(x,y,q) do {u32 __tmp; udiv_qrnnd(q, __tmp, (x)>>32, x, y);} while (0)
+#define u64_mod(x,y,r) do {u32 __tmp; udiv_qrnnd(__tmp, q, (x)>>32, x, y);} while (0)
+#define u64_divmod(x,y,q,r) udiv_qrnnd(q, r, (x)>>32, x, y)
+
+#else
+#define u64_div(x,y,q)	((q) = (u32)((u64)(x) / (u64)(y)))
+#define u64_mod(x,y,r)	((r) = (u32)((u64)(x) % (u64)(y)))
+#define u64_divmod(x,y,q,r) (u64_div(x,y,q), u64_mod(x,y,r))
+#endif
+
+
+#endif
Index: /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_sync.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_sync.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_sync.c	(revision 91)
@@ -0,0 +1,1004 @@
+/*
+ *   ALSA sequencer queue synchronization routine
+ *
+ *   Copyright (c) 2000 by 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <sound/driver.h>
+
+#include "seq_memory.h"
+#include "seq_queue.h"
+#include "seq_clientmgr.h"
+#include "seq_fifo.h"
+#include "seq_timer.h"
+#include "seq_info.h"
+
+#ifdef SNDRV_SEQ_SYNC_SUPPORT
+
+#define FOR_EACH_LIST(var,list) \
+for (var = (list)->next; var != list; var = var->next)
+
+/*
+ * callbacks
+ */
+static int event_input_sync(snd_seq_event_t * ev, int direct, void *private_data, int atomic, int hop);
+static int queue_master_add(void *private_data, snd_seq_port_subscribe_t *subs);
+static int queue_master_remove(void *private_data, snd_seq_port_subscribe_t *subs);
+static void queue_delete_all_masters(queue_t *q);
+static int queue_slave_set(void *private_data, snd_seq_port_subscribe_t *subs);
+static int queue_slave_reset(void *private_data, snd_seq_port_subscribe_t *subs);
+static void queue_sync_close_parser(queue_sync_t *sync, int slave);
+
+/*
+ * pre-defined event parsers
+ */
+
+extern seq_sync_parser_t snd_seq_midi_clock_parser; /* seq_midi_clock.c */
+extern seq_sync_parser_t snd_seq_mtc_parser; /* seq_mtc.c */
+extern seq_sync_parser_t snd_seq_dtl_parser; /* seq_dtl.c */
+
+static seq_sync_parser_t *event_parsers[] = {
+	&snd_seq_midi_clock_parser,
+	&snd_seq_mtc_parser,
+	&snd_seq_dtl_parser,
+	NULL
+};
+
+/*
+ * create a sync port corresponding to the specified queue
+ */
+int snd_seq_sync_create_port(queue_t *queue)
+{
+	snd_seq_port_info_t port;
+	snd_seq_port_callback_t pcallbacks;
+
+	memset(&pcallbacks, 0, sizeof(pcallbacks));
+	memset(&port, 0, sizeof(port));
+	pcallbacks.owner = THIS_MODULE;
+	pcallbacks.event_input = event_input_sync;
+	pcallbacks.subscribe = queue_master_add;
+	pcallbacks.unsubscribe = queue_master_remove;
+	pcallbacks.use = queue_slave_set;
+	pcallbacks.unuse = queue_slave_reset;
+	pcallbacks.private_data = queue;
+	pcallbacks.callback_all = 1; /* call callbacks at each subscription */
+	port.capability = SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_SUBS_READ|
+		SNDRV_SEQ_PORT_CAP_WRITE|SNDRV_SEQ_PORT_CAP_SUBS_WRITE|
+		SNDRV_SEQ_PORT_CAP_DUPLEX|
+		SNDRV_SEQ_PORT_CAP_SYNC_READ|SNDRV_SEQ_PORT_CAP_SYNC_WRITE;
+	port.type = 0;
+	sprintf(port.name, "Sync Queue %d", queue->queue);
+	port.kernel = &pcallbacks;
+	port.flags = SNDRV_SEQ_PORT_FLG_GIVEN_PORT;
+	port.port = snd_seq_queue_sync_port(queue->queue);
+	if (snd_seq_kernel_client_ctl(SNDRV_SEQ_CLIENT_SYSTEM, SNDRV_SEQ_IOCTL_CREATE_PORT, &port) < 0)
+		return -ENOMEM;
+	queue->sync_port.client = SNDRV_SEQ_CLIENT_SYSTEM;
+	queue->sync_port.port = port.port;
+	return 0;
+}
+
+/*
+ * delete attached sync port to the queue
+ */
+int snd_seq_sync_delete_port(queue_t *queue)
+{
+	snd_seq_port_info_t port;
+
+	memset(&port, 0, sizeof(port));
+	port.client = queue->sync_port.client;
+	port.port = queue->sync_port.port;
+	if (snd_seq_kernel_client_ctl(SNDRV_SEQ_CLIENT_SYSTEM, SNDRV_SEQ_IOCTL_DELETE_PORT, &port) < 0)
+		return -ENOMEM;
+	queue_delete_all_masters(queue);
+	queue_sync_close_parser(&queue->slave, 1);
+	return 0;
+}
+
+
+/*
+ * send a sync signal to the sync slave client
+ */
+static void queue_send_sync_event(queue_t *q, queue_sync_t *master, int type, int atomic, int hop)
+{
+	snd_seq_event_t event;
+
+	memset(&event, 0, sizeof(event));
+	
+	event.flags = SNDRV_SEQ_TIME_MODE_ABS;
+	/* since we use direct delivery, we have to convert time stamp here.. */
+	switch (master->format & SNDRV_SEQ_SYNC_MODE) {
+	case SNDRV_SEQ_SYNC_TICK:
+		event.flags |= SNDRV_SEQ_TIME_STAMP_TICK;
+		event.time.tick = q->timer->tick.cur_tick;
+		break;
+	case SNDRV_SEQ_SYNC_TIME:
+		event.flags |= SNDRV_SEQ_TIME_STAMP_REAL;
+		event.time.time = q->timer->cur_time;
+		break;
+	}
+	event.type = type;
+	event.data.queue.queue = q->queue;
+	event.data.queue.sync_format = master->format;
+	event.data.queue.sync_time_format = master->time_format;
+	event.data.queue.param.position = master->counter;
+	event.source = q->sync_port;
+	event.dest = master->addr;
+	if (master->parser) {
+		snd_seq_event_t newev;
+		newev = event;
+		if (master->parser->out.sync(master->parser_arg, &event, &newev) > 0) {
+			snd_seq_kernel_client_dispatch(SNDRV_SEQ_CLIENT_SYSTEM, &newev, atomic, hop);
+			return;
+		}
+	}
+	snd_seq_kernel_client_dispatch(SNDRV_SEQ_CLIENT_SYSTEM, &event, atomic, hop);
+}
+
+/*
+ * initialize the sync position
+ */
+static void queue_sync_clear(queue_sync_t *sync)
+{
+	memset(&sync->cur_time, 0, sizeof(sync->cur_time));
+	sync->counter = 0;
+	sync->sync_tick.cur_tick = 0;
+	sync->sync_tick.fraction = 0;
+}
+
+/*
+ * initialize all sync positions
+ */
+void snd_seq_sync_clear(queue_t *q)
+{
+	struct list_head *head;
+
+	/* clear master positions */
+	read_lock(&q->master_lock);
+	FOR_EACH_LIST(head, &q->master_head) {
+		queue_sync_t *master = list_entry(head, queue_sync_t, list);
+		queue_sync_clear(master);
+	}
+	read_unlock(&q->master_lock);
+	read_lock(&q->slave_lock);
+	queue_sync_clear(&q->slave);
+	read_unlock(&q->slave_lock);
+}
+
+
+/*
+ * change tick resolution of sync master/slave
+ */
+static void queue_sync_set_tick_resolution(queue_t *q, queue_sync_t *sync)
+{
+	unsigned int tempo, ppq;
+	tempo = q->timer->tempo;
+	if (sync->param.tick.ppq == 0)
+		ppq = q->timer->ppq;
+	else
+		ppq = sync->param.tick.ppq;
+	snd_seq_timer_set_tick_resolution(&sync->sync_tick, tempo, ppq, sync->param.tick.ticks);
+}
+
+
+/*
+ * update sync-master resolutions
+ */
+void snd_seq_sync_update_tempo(queue_t *q)
+{
+	struct list_head *head;
+
+	read_lock(&q->master_lock);
+	FOR_EACH_LIST(head, &q->master_head) {
+		queue_sync_t *master = list_entry(head, queue_sync_t, list);
+		if (master->format & SNDRV_SEQ_SYNC_TICK)
+			queue_sync_set_tick_resolution(q, master);
+	}
+	read_unlock(&q->master_lock);
+	read_lock(&q->slave_lock);
+	if (q->slave.format & SNDRV_SEQ_SYNC_TICK)
+		queue_sync_set_tick_resolution(q, &q->slave);
+	read_unlock(&q->slave_lock);
+}
+
+
+/*
+ * change the tick position from the current tick of the queue
+ */
+static void queue_sync_change_tick(queue_t *q, queue_sync_t *sync)
+{
+	if (sync->param.tick.ppq == 0)
+		sync->counter = q->timer->tick.cur_tick;
+	else
+		sync->counter = (q->timer->tick.cur_tick * sync->param.tick.ppq) / q->timer->ppq;
+	sync->counter /= sync->param.tick.ticks;
+	sync->sync_tick.cur_tick = sync->counter;
+	sync->sync_tick.fraction = 0;
+}
+
+/*
+ * change the time position from the current time of the queue
+ */
+static void queue_sync_change_time(queue_t *q, queue_sync_t *sync)
+{
+	/* we need 64bit calculation here.. */	
+	u64 nsec;
+
+	nsec = q->timer->cur_time.tv_sec;
+	nsec *= 1000000000UL;
+	nsec += q->timer->cur_time.tv_nsec;
+	u64_div(nsec, sync->param.time.resolution, sync->counter);
+	sync->counter *= sync->param.time.subframes;
+	sync->cur_time = q->timer->cur_time;
+}
+
+
+/*
+ * update the tick position of all sync
+ */
+void snd_seq_sync_update_tick(queue_t *q, int master_only, int atomic, int hop)
+{
+	struct list_head *head;
+
+	read_lock(&q->master_lock);
+	FOR_EACH_LIST(head, &q->master_head) {
+		queue_sync_t *master = list_entry(head, queue_sync_t, list);
+		if (master->format & SNDRV_SEQ_SYNC_TICK) {
+			queue_sync_change_tick(q, master);
+			queue_send_sync_event(q, master, SNDRV_SEQ_EVENT_SYNC_POS, atomic, hop); /* broadcast to client */
+		}
+	}
+	read_unlock(&q->master_lock);
+	if (master_only)
+		return;
+	read_lock(&q->slave_lock);
+	if (q->slave.format & SNDRV_SEQ_SYNC_TICK)
+		queue_sync_change_tick(q, &q->slave);
+	read_unlock(&q->slave_lock);
+}
+
+/*
+ * update the time position of all sync
+ */
+void snd_seq_sync_update_time(queue_t *q, int master_only, int atomic, int hop)
+{
+	struct list_head *head;
+
+	read_lock(&q->master_lock);
+	FOR_EACH_LIST(head, &q->master_head) {
+		queue_sync_t *master = list_entry(head, queue_sync_t, list);
+		if (master->format & SNDRV_SEQ_SYNC_TIME) {
+			queue_sync_change_time(q, master);
+			queue_send_sync_event(q, master, SNDRV_SEQ_EVENT_SYNC_POS, atomic, hop);
+		}
+	}
+	read_unlock(&q->master_lock);
+	if (master_only)
+		return;
+	read_lock(&q->slave_lock);
+	if (q->slave.format & SNDRV_SEQ_SYNC_TIME)
+		queue_sync_change_time(q, &q->slave);
+	read_unlock(&q->slave_lock);
+}
+
+
+/*
+ * check the current timer value and send sync messages if the sync
+ * time is elapsed
+ */
+static void queue_master_check(queue_t *q, unsigned long ticks, int atomic, int hop)
+{
+	struct list_head *head;
+
+	read_lock(&q->master_lock);
+	FOR_EACH_LIST(head, &q->master_head) {
+		queue_sync_t *master = list_entry(head, queue_sync_t, list);
+		switch (master->format & SNDRV_SEQ_SYNC_MODE) {
+		case SNDRV_SEQ_SYNC_TICK:
+			snd_seq_timer_update_tick(&master->sync_tick, ticks);
+			while (master->sync_tick.cur_tick >= master->counter) {
+				queue_send_sync_event(q, master, SNDRV_SEQ_EVENT_SYNC, atomic, hop);
+				master->counter++;
+			}
+			break;
+		case SNDRV_SEQ_SYNC_TIME:
+			while (snd_seq_compare_real_time(&q->timer->cur_time, &master->cur_time)) {
+				queue_send_sync_event(q, master, SNDRV_SEQ_EVENT_SYNC, atomic, hop);
+				snd_seq_inc_time_nsec(&master->cur_time, master->resolution);
+				master->counter++;
+			}
+			break;
+		}
+	}
+	read_unlock(&q->master_lock);
+}
+
+
+/*
+ * slave stuff
+ */
+
+/*
+ * update tick
+ */
+static void queue_slave_check(queue_t *q, unsigned long ticks)
+{
+	switch (q->slave.format & SNDRV_SEQ_SYNC_MODE) {
+	case SNDRV_SEQ_SYNC_TICK:
+		snd_seq_timer_update_tick(&q->slave.sync_tick, ticks);
+		break;
+	case SNDRV_SEQ_SYNC_TIME:
+		/* nothing */
+		break;
+	}
+}
+
+/*
+ * slave synchronization in real-time unit
+ */
+static int queue_slave_sync_time(queue_t *q, unsigned int position)
+{
+	struct timeval tm;
+	long diff_time, new_period;
+	queue_sync_t *sync = &q->slave;
+	sndrv_seq_queue_time_sync_t *p = &sync->param.time;
+	seq_timer_t *tmr = q->timer;
+	u64 external_counter, tmp;
+
+	do_gettimeofday(&tm);
+	if (tmr->sync_start) {
+		/* XXX: we should use 64bit for diff_time, too. */
+		diff_time = (tm.tv_sec - tmr->sync_last_tm.tv_sec) * 1000000 +
+			((long)tm.tv_usec - (long)tmr->sync_last_tm.tv_usec);
+		diff_time = (p->x0 * tmr->sync_time_diff + p->x1 * (diff_time * 1000)) / (p->x0 + p->x1);
+#define MIN_DIFF_TIME	1000	/* 1ms minimum */
+		if (diff_time < MIN_DIFF_TIME)
+			diff_time = MIN_DIFF_TIME;
+		tmr->sync_time_diff = diff_time;
+		tmp = (u64)tmr->base_period * (u64)sync->resolution;
+		u64_div(tmp, diff_time, new_period);
+
+		/* phase adjustment */
+		external_counter = position;
+		external_counter *= sync->resolution;
+
+		/* calculate current time */
+		tmp = snd_seq_timer_get_cur_nsec(tmr, &tm);
+
+		if (external_counter > tmp) {
+			tmp = external_counter - tmp;
+			if (tmp < p->max_time_diff) {
+				/* locked */
+				int hz = p->phase_correct_time / tmr->base_period;
+				diff_time = (u32)tmp;
+				diff_time /= hz;
+				new_period += diff_time;
+				q->flags &= ~SNDRV_SEQ_QUEUE_FLG_SYNC_LOST;
+			}
+		} else {
+			tmp = tmp - external_counter;
+			if (tmp == 0)
+				q->flags &= ~SNDRV_SEQ_QUEUE_FLG_SYNC_LOST;
+			else if (tmp < p->max_time_diff) {
+				/* locked */
+				int hz = p->phase_correct_time / tmr->base_period;
+				diff_time = (u32)tmp;
+				diff_time /= hz;
+				if (new_period - diff_time > MIN_DIFF_TIME) {
+					new_period -= diff_time;
+					q->flags &= ~SNDRV_SEQ_QUEUE_FLG_SYNC_LOST;
+				} else
+					q->flags |= SNDRV_SEQ_QUEUE_FLG_SYNC_LOST;
+			}
+		}
+		tmr->period = new_period;
+	} else {
+		tmr->sync_start = 1;
+		tmr->sync_time_diff = sync->resolution;
+	}
+	tmr->sync_last_tm = tm;
+	sync->counter = position;
+
+	return 0;
+}
+
+/*
+ * slave synchronization in tick unit
+ */
+static int queue_slave_sync_tick(queue_t *q, unsigned int position)
+{
+	struct timeval tm;
+	long diff_time, tick_diff;
+	unsigned int tick_time;
+	queue_sync_t *sync = &q->slave;
+	seq_timer_t *tmr = q->timer;
+	sndrv_seq_queue_tick_sync_t *p = &sync->param.tick;
+
+	do_gettimeofday(&tm);
+	if (tmr->sync_start) {
+		/* XXX: diff_time should be 64bit for enough long sync period.. */
+		diff_time = (tm.tv_sec - tmr->sync_last_tm.tv_sec) * 1000000 +
+			((long)tm.tv_usec - (long)tmr->sync_last_tm.tv_usec);
+		diff_time *= 1000; /* in nsec */
+		tick_time = (p->x0 * sync->sync_tick.resolution +
+			     p->x1 * diff_time) / (p->x0 + p->x1);
+		/* phase adjustment */
+		tick_diff = (long)position - (long)sync->sync_tick.cur_tick;
+		if (tick_diff != 0) {
+			if (tick_diff >= -p->max_tick_diff &&
+			    tick_diff <= p->max_tick_diff) {
+				/* locked */
+				q->flags &= ~SNDRV_SEQ_QUEUE_FLG_SYNC_LOST;
+				tick_time = (tick_time * p->max_tick_diff2) /
+					(p->max_tick_diff2 + tick_diff);
+			} else {
+				/* sync lost.. freewheeling */
+				q->flags |= SNDRV_SEQ_QUEUE_FLG_SYNC_LOST;
+			}
+		} else
+			q->flags &= ~SNDRV_SEQ_QUEUE_FLG_SYNC_LOST;
+
+#define MIN_TICK_TIME	1000	/* 1ms */
+		if (tick_time < MIN_TICK_TIME)
+			tick_time = MIN_TICK_TIME;
+
+		sync->sync_tick.resolution = tick_time;
+		snd_seq_timer_update_tick(&sync->sync_tick, 0);
+		if (p->ppq)
+			tmr->tick.resolution = (tick_time * p->ppq) / tmr->ppq;
+		else
+			tmr->tick.resolution = tick_time;
+		snd_seq_timer_update_tick(&tmr->tick, 0);
+		tmr->tempo = (tmr->tick.resolution * tmr->ppq) / 1000;
+
+	} else 
+		tmr->sync_start = 1;
+	tmr->sync_last_tm = tm;
+
+	sync->counter = position;
+
+	return 0;
+}
+
+
+/*
+ */
+static void queue_slave_jump_to_time(queue_t *q, unsigned int position, int atomic, int hop)
+{
+	u64 nsec;
+	queue_sync_t *sync = &q->slave;
+
+	q->slave.counter = position;
+	nsec = sync->counter;
+	nsec *= sync->resolution;
+	u64_divmod(nsec, 1000000000, sync->cur_time.tv_sec, sync->cur_time.tv_nsec);
+	q->timer->cur_time = sync->cur_time;
+
+	/* update master */
+	snd_seq_sync_update_time(q, 1, atomic, hop);
+}
+
+static void queue_slave_jump_to_tick(queue_t *q, unsigned int position, int atomic, int hop)
+{
+	unsigned int tick;
+	queue_sync_t *sync = &q->slave;
+
+	sync->counter = position;
+	sync->sync_tick.cur_tick = sync->counter;
+	sync->sync_tick.fraction = 0;
+
+	/* update queue timer */
+	if (sync->param.tick.ppq == 0)
+		tick = sync->counter;
+	else
+		tick = sync->counter * q->timer->ppq / sync->param.tick.ppq;
+	q->timer->tick.cur_tick = tick * sync->param.tick.ticks;
+	q->timer->tick.fraction = 0;
+
+	/* update master */
+	snd_seq_sync_update_tick(q, 1, atomic, hop);
+}
+
+
+/*
+ * event input callback
+ */
+static int event_input_sync(snd_seq_event_t * ev, int direct, void *private_data, int atomic, int hop)
+{
+	queue_t *q = private_data;
+	unsigned long flags;
+	snd_seq_event_t newev;
+
+	snd_assert(q != NULL, return -ENXIO);
+
+	/* lock the queue owner access.. */
+	spin_lock_irqsave(&q->owner_lock, flags);
+	q->klocked = 1;
+	spin_unlock_irqrestore(&q->owner_lock, flags);
+
+	read_lock(&q->slave_lock);
+	if (q->slave.format) {
+		if (q->slave.parser) {
+			memset(&newev, 0, sizeof(newev));
+			if (q->slave.parser->in.sync(q->slave.parser_arg, ev, &newev) > 0)
+				ev = &newev;
+		}
+	}
+	if (ev->type == SNDRV_SEQ_EVENT_SYNC) {
+		/* slave signal received */
+		switch (q->slave.format & SNDRV_SEQ_SYNC_MODE) {
+		case SNDRV_SEQ_SYNC_TICK:
+			queue_slave_sync_tick(q, ev->data.queue.param.position);
+			break;
+		case SNDRV_SEQ_SYNC_TIME:
+			queue_slave_sync_time(q, ev->data.queue.param.position);
+			break;
+		}
+	} else if (ev->type == SNDRV_SEQ_EVENT_SYNC_POS) {
+		/* jump to position */
+		switch (q->slave.format & SNDRV_SEQ_SYNC_MODE) {
+		case SNDRV_SEQ_SYNC_TICK:
+			if (q->timer->running)
+				queue_slave_sync_tick(q, ev->data.queue.param.position);
+			else
+				queue_slave_jump_to_tick(q, ev->data.queue.param.position, atomic, hop);
+			break;
+		case SNDRV_SEQ_SYNC_TIME:
+			if (q->timer->running)
+				queue_slave_sync_time(q, ev->data.queue.param.position);
+			else
+				queue_slave_jump_to_time(q, ev->data.queue.param.position, atomic, hop);
+			break;
+		}
+	} else {
+		/* control queue */
+		snd_seq_queue_process_event(q, ev, 0, atomic, hop);
+	}
+	read_unlock(&q->slave_lock);
+
+	/* unlock */
+	spin_lock_irqsave(&q->owner_lock, flags);
+	q->klocked = 0;
+	spin_unlock_irqrestore(&q->owner_lock, flags);
+
+	return 0;
+}
+
+
+/*
+ * initialize sync parameters
+ */
+static int queue_param_init(queue_t *q, queue_sync_t *sync,
+			    snd_seq_addr_t *addr, sndrv_seq_queue_sync_t *info,
+			    int slave)
+{
+	seq_sync_parser_t *parser, **list;
+
+	sync->format = info->format;
+	sync->time_format = info->time_format;
+	*sync->opt_info = *info->info;
+	sync->addr = *addr;
+	/* copy params */
+	if (info->format&SNDRV_SEQ_SYNC_TICK)
+	    sync->param.tick=info->param.tick;
+	else
+	    sync->param.time=info->param.time;
+
+	sync->parser = NULL;
+	sync->parser_arg = NULL;
+	for (list = event_parsers; (parser = *list) != NULL; list++) {
+		if (parser->format == sync->format) {
+			int err;
+			if (slave)
+				err = parser->in.open(sync, &sync->parser_arg);
+			else
+				err = parser->out.open(sync, &sync->parser_arg);
+			if (err < 0)
+				return err;
+			sync->parser = parser;
+			break;
+		}
+	}
+
+	switch (sync->format & SNDRV_SEQ_SYNC_MODE) {
+	case SNDRV_SEQ_SYNC_TICK:
+		if (sync->param.tick.ppq > 200)
+			goto __error;
+		if (sync->param.tick.ticks == 0)
+			sync->param.tick.ticks = 1;
+		queue_sync_set_tick_resolution(q, sync);
+		/* sync slave parameters -- will be configurable */
+		sync->param.tick.x0 = 4;
+		sync->param.tick.x1 = 1;
+		sync->param.tick.max_tick_diff = 50;
+		sync->param.tick.max_tick_diff2 = sync->param.tick.max_tick_diff * 2;
+		break;
+	case SNDRV_SEQ_SYNC_TIME:
+		sync->resolution = sync->param.time.resolution;
+		if (sync->param.time.subframes == 0)
+			goto __error;
+		sync->resolution /= sync->param.time.subframes;
+		if (sync->resolution < 1000000) /* minimum = 1ms */
+			goto __error;
+		/* sync slave parameters -- will be configurable */
+		sync->param.time.x0 = 2;
+		sync->param.time.x1 = 1;
+		sync->param.time.max_time_diff = 1000000000UL; /* 1s */
+		sync->param.time.phase_correct_time = 100000000UL; /* 0.1s */
+		break;
+	default:
+		snd_printd("seq_sync: invalid format %x\n", sync->format);
+		goto __error;
+	}
+	return 0;
+
+__error:
+	queue_sync_close_parser(sync, slave);
+	return -EINVAL;
+}
+
+
+/*
+ * close event parser if exists
+ */
+static void queue_sync_close_parser(queue_sync_t *sync, int slave)
+{
+	if (sync->parser == NULL)
+		return;
+	if (slave) {
+		if (sync->parser->in.close)
+			sync->parser->in.close(sync->parser_arg);
+		else if (sync->parser_arg)
+			kfree(sync->parser_arg);
+	} else {
+		if (sync->parser->out.close)
+			sync->parser->out.close(sync->parser_arg);
+		else if (sync->parser_arg)
+			kfree(sync->parser_arg);
+	}
+	sync->parser = NULL;
+	sync->parser_arg = NULL;
+}
+
+
+/*
+ * add to master list
+ */
+static int queue_master_add(void *private_data, snd_seq_port_subscribe_t *subs)
+{
+	queue_t *q = private_data;
+	queue_sync_t *master;
+	unsigned long flags;
+	int err;
+
+	snd_assert(q != NULL, return -EINVAL);
+	if (! subs->sync)
+		return -EINVAL;
+	master = snd_kcalloc(sizeof(*master), GFP_KERNEL);
+	if (master == NULL)
+		return -ENOMEM;
+	err = queue_param_init(q, master, &subs->dest, &subs->opt.sync_info, 0);
+	if (err < 0) {
+		kfree(master);
+		return err;
+	}
+	write_lock_irqsave(&q->master_lock, flags);
+	list_add(&master->list, &q->master_head);
+	write_unlock_irqrestore(&q->master_lock, flags);
+
+	return 0;
+}
+
+/*
+ * remove master
+ */
+static int queue_master_remove(void *private_data, snd_seq_port_subscribe_t *subs)
+{
+	queue_t *q = private_data;
+	sndrv_seq_queue_sync_t *info;
+	snd_seq_addr_t *addr;
+	struct list_head *head;
+	unsigned long flags;
+
+	snd_assert(q != NULL, return -EINVAL);
+	if (! subs->sync)
+		return -EINVAL;
+	info = &subs->opt.sync_info;
+	addr = &subs->dest;
+
+	write_lock_irqsave(&q->master_lock, flags);
+	FOR_EACH_LIST(head, &q->master_head) {
+		queue_sync_t *master = list_entry(head, queue_sync_t, list);
+		if (master->format == info->format &&
+		    master->addr.client == addr->client &&
+		    master->addr.port == addr->port) {
+			list_del(&master->list);
+			write_unlock_irqrestore(&q->master_lock, flags);
+			queue_sync_close_parser(master, 0);
+			kfree(master);
+			return 0;
+		}
+	}
+	write_unlock_irqrestore(&q->master_lock, flags);
+	snd_printd("seq_queue: can't find master from %d.%d format %0x\n", addr->client, addr->port, info->format);
+	return -ENXIO;
+}
+
+/* remove all master connections if any exist */
+static void queue_delete_all_masters(queue_t *q)
+{
+	struct list_head *head;
+	unsigned long flags;
+
+	write_lock_irqsave(&q->master_lock, flags);
+	FOR_EACH_LIST(head, &q->master_head) {
+		queue_sync_t *master = list_entry(head, queue_sync_t, list);
+		list_del(&master->list);
+		queue_sync_close_parser(master, 0);
+		kfree(master);
+	}
+	write_unlock_irqrestore(&q->master_lock, flags);
+}
+
+/*
+ * set slave mode
+ */
+static int queue_slave_set(void *private_data, snd_seq_port_subscribe_t *subs)
+{
+	queue_t *q = private_data;
+	unsigned long flags;
+	int err;
+
+	snd_assert(q != NULL, return -EINVAL);
+	if (! subs->sync)
+		return -EINVAL;
+	write_lock_irqsave(&q->slave_lock, flags);
+	if (q->slave.format) {
+		write_unlock_irqrestore(&q->slave_lock, flags);
+		return -EBUSY;
+	}
+	err = queue_param_init(q, &q->slave, &subs->sender,
+			       &subs->opt.sync_info, 1);
+	if (err < 0) {
+		q->slave.format = 0;
+		write_unlock_irqrestore(&q->slave_lock, flags);
+		return err;
+	}
+	write_unlock_irqrestore(&q->slave_lock, flags);
+	return 0;
+}
+
+/*
+ * remove slave mode
+ */
+static int queue_slave_reset(void *private_data, snd_seq_port_subscribe_t *subs)
+{
+	queue_t *q = private_data;
+	unsigned long flags;
+
+	snd_assert(q != NULL, return -EINVAL);
+	if (! subs->sync)
+		return -EINVAL;
+	write_lock_irqsave(&q->slave_lock, flags);
+	if (q->slave.addr.client == subs->sender.client &&
+	    q->slave.addr.port == subs->sender.port) {
+		q->slave.format = 0;
+		queue_sync_close_parser(&q->slave, 1);
+		write_unlock_irqrestore(&q->slave_lock, flags);
+		return 0;
+	}
+	write_unlock_irqrestore(&q->slave_lock, flags);
+	snd_printd("seq_queue: can't match slave condition\n");
+	return -ENXIO;
+}
+
+
+/*
+ * sync check
+ * this function is called at each timer interrupt.
+ */
+
+void snd_seq_sync_check(queue_t *q, unsigned long resolution, int atomic, int hop)
+{
+	queue_master_check(q, resolution, atomic, hop);
+	queue_slave_check(q, resolution);
+}
+
+
+/*
+ * support functions for SMPTE time frame
+ */
+static unsigned int linear_time_to_position(sndrv_seq_time_frame_t time,
+					    int nframes, int nsubs)
+{
+	unsigned int count;
+	count = time.hour * 60 + time.min;
+	count = count * 60 + time.sec;
+	count = count * nframes + time.frame;
+	count = count * nsubs + time.subframe;
+	return count;
+}
+
+static sndrv_seq_time_frame_t linear_position_to_time(unsigned int count,
+						      int nframes, int nsubs)
+{
+	sndrv_seq_time_frame_t time;
+	time.subframe = count % nsubs;
+	count /= nsubs;
+	time.hour = count / (3600 * nframes);
+	count %= 3600 * nframes;
+	time.min = count / (60 * nframes);
+	count %= 60 * nframes;
+	time.sec = count / nframes;
+	time.frame = count % nframes;
+	return time;
+}
+
+/* drop frame - only 30fps */
+#define NFRAMES		30
+#define FRAMES_PER_MIN		(NFRAMES * 60 - 2)
+#define FRAMES_PER_10MIN	(FRAMES_PER_MIN * 10 + 2)
+#define FRAMES_PER_HOUR		(FRAMES_PER_10MIN * 6)
+
+static unsigned int drop_time_to_position(sndrv_seq_time_frame_t time, int nsubs)
+{
+	unsigned int count, min;
+
+	min = time.min % 10;
+	count = time.frame;
+	if (min > 0) {
+		if (time.sec == 0 && time.frame < 2)
+			count = 2;
+	}
+	count += time.sec * NFRAMES;
+	count += min * FRAMES_PER_MIN;
+	count += (time.min / 10) * FRAMES_PER_10MIN;
+	count += time.hour * (FRAMES_PER_HOUR);
+	count *= nsubs;
+	count += time.subframe;
+
+	return count;
+}
+
+static sndrv_seq_time_frame_t drop_position_to_time(int count, int nsubs)
+{
+	unsigned int min10;
+	sndrv_seq_time_frame_t time;
+
+	time.subframe = count % nsubs;
+	count /= nsubs;
+	min10 = count / FRAMES_PER_10MIN;
+	time.hour = min10 / 6;
+	min10 %= 6;
+	count %= FRAMES_PER_10MIN;
+	if (count < 2) {
+		time.min = min10 * 10;
+		time.sec = 0;
+	} else {
+		count -= 2;
+		time.min = count / FRAMES_PER_MIN;
+		time.min += min10 * 10;
+		count %= FRAMES_PER_MIN;
+		count += 2;
+		time.sec = count / NFRAMES;
+		count %= NFRAMES;
+	}
+	time.frame = count;
+
+	return time;
+}
+
+/* convert from position counter to time frame */
+sndrv_seq_time_frame_t snd_seq_position_to_time_frame(int format, unsigned int nsubs, unsigned int pos)
+{
+	switch (format) {
+	case SNDRV_SEQ_SYNC_FPS_24:
+		return linear_position_to_time(pos, 24, nsubs);
+	case SNDRV_SEQ_SYNC_FPS_25:
+		return linear_position_to_time(pos, 25, nsubs);
+	case SNDRV_SEQ_SYNC_FPS_30_NDP:
+		return linear_position_to_time(pos, 30, nsubs);
+	case SNDRV_SEQ_SYNC_FPS_30_DP:
+	default:
+		return drop_position_to_time(pos, nsubs);
+	}
+}
+
+/* convert from position counter to time frame */
+unsigned int snd_seq_time_frame_to_position(int format, unsigned int nsubs, sndrv_seq_time_frame_t *rtime)
+{
+	switch (format) {
+	case SNDRV_SEQ_SYNC_FPS_24:
+		return linear_time_to_position(*rtime, 24, nsubs);
+	case SNDRV_SEQ_SYNC_FPS_25:
+		return linear_time_to_position(*rtime, 25, nsubs);
+	case SNDRV_SEQ_SYNC_FPS_30_NDP:
+		return linear_time_to_position(*rtime, 30, nsubs);
+	case SNDRV_SEQ_SYNC_FPS_30_DP:
+	default:
+		return drop_time_to_position(*rtime, nsubs);
+	}
+}
+
+/* resolution in nsec */
+unsigned long snd_seq_get_smpte_resolution(int time_format)
+{
+	switch (time_format) {
+	case SNDRV_SEQ_SYNC_FPS_24:
+		return 1000000000UL / 24;
+	case SNDRV_SEQ_SYNC_FPS_25:
+		return 1000000000UL / 25;
+	case SNDRV_SEQ_SYNC_FPS_30_DP:
+	case SNDRV_SEQ_SYNC_FPS_30_NDP:
+		return (unsigned long)(1000000000.0/29.97);
+	}
+	return 0;
+}
+
+
+/*
+ * proc interface
+ */
+
+static void print_sync_info(snd_info_buffer_t *buffer, queue_sync_t *sync)
+{
+	snd_iprintf(buffer, " [%s] ==> %d:%d\n",
+		    (sync->format & SNDRV_SEQ_SYNC_TICK ? "tick" : "time"),
+		    sync->addr.client, sync->addr.port);
+	snd_iprintf(buffer, "    format 0x%0x / time_format %d\n",
+		    sync->format, sync->time_format);
+	switch (sync->format & SNDRV_SEQ_SYNC_MODE) {
+	case SNDRV_SEQ_SYNC_TICK:
+		snd_iprintf(buffer, "   ppq: %d, ticks: %d\n",
+			    sync->param.tick.ppq, sync->param.tick.ticks);
+		snd_iprintf(buffer, "   resolution: %ld ns, position: %d\n",
+			    sync->sync_tick.resolution,
+			    sync->counter);
+		break;
+	case SNDRV_SEQ_SYNC_TIME:
+		snd_iprintf(buffer, "   subframes %d, resolution: %ld ns, position: %d\n",
+			    sync->param.time.subframes,
+			    sync->resolution,
+			    sync->counter);
+		break;
+	}
+}
+
+void snd_seq_sync_info_read(queue_t *q, snd_info_buffer_t *buffer)
+{
+	struct list_head *head;
+	int count = 0;
+
+	read_lock(&q->master_lock);
+	FOR_EACH_LIST(head, &q->master_head) {
+		queue_sync_t *master = list_entry(head, queue_sync_t, list);
+		snd_iprintf(buffer, "master %d", count);
+		print_sync_info(buffer, master);
+		count++;
+	}
+	read_unlock(&q->master_lock);
+	if (q->slave.format) {
+		snd_iprintf(buffer, "slave");
+		print_sync_info(buffer, &q->slave);
+		count++;
+	}
+	if (count)
+		snd_iprintf(buffer, "\n");
+}
+
+#endif /* SNDRV_SEQ_SYNC_SUPPORT */
Index: /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_sync.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_sync.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_sync.h	(revision 91)
@@ -0,0 +1,79 @@
+/*
+ *  Synchronization of ALSA sequencer queues
+ *
+ *   Copyright (c) 2000 by 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+#ifndef __SND_SEQ_SYNC_H
+#define __SND_SEQ_SYNC_H
+
+typedef struct snd_queue_sync queue_sync_t;
+typedef struct snd_seq_sync_parser seq_sync_parser_t;
+typedef void *seq_sync_arg_t;
+
+struct snd_queue_sync {
+	unsigned char format;
+	unsigned char time_format;
+	unsigned char opt_info[6];	/* optional info */
+	snd_seq_addr_t addr;		/* master/slave address */
+
+	unsigned int counter;		/* current position */
+	unsigned long resolution;	/* resolution for time */
+	snd_seq_real_time_t cur_time;	/* current time */
+	seq_timer_tick_t sync_tick;		/* tick info */
+
+	union {
+		struct sndrv_seq_queue_tick_sync tick;
+		struct sndrv_seq_queue_time_sync time;
+	} param;
+
+	seq_sync_parser_t *parser;
+	seq_sync_arg_t parser_arg;
+
+	struct list_head list;
+};
+
+
+struct seq_sync_parser_ops {
+	int (*open)(queue_sync_t *sync_info, seq_sync_arg_t *retp);
+	int (*sync)(seq_sync_arg_t arg, const snd_seq_event_t *src, snd_seq_event_t *ev);
+	int (*close)(seq_sync_arg_t arg);
+};
+
+struct snd_seq_sync_parser {
+	unsigned int format;	/* supported format */
+	struct seq_sync_parser_ops in;	/* sync-in (slave) */
+	struct seq_sync_parser_ops out;	/* sync-out (mastering) */
+};
+
+/*
+ * prototypes
+ */
+int snd_seq_sync_create_port(queue_t *queue);
+int snd_seq_sync_delete_port(queue_t *queue);
+void snd_seq_sync_clear(queue_t *q);
+void snd_seq_sync_update_tempo(queue_t *q);
+void snd_seq_sync_update_tick(queue_t *q, int master_only, int atomic, int hop);
+void snd_seq_sync_update_time(queue_t *q, int master_only, int atomic, int hop);
+void snd_seq_sync_check(queue_t *q, unsigned long resolution, int atomic, int hop);
+
+sndrv_seq_time_frame_t snd_seq_position_to_time_frame(int format, unsigned int nsubs, unsigned int pos);
+unsigned int snd_seq_time_frame_to_position(int format, unsigned int nsubs, sndrv_seq_time_frame_t *rtime);
+unsigned long snd_seq_get_smpte_resolution(int time_format);
+
+
+#endif
Index: /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_system.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_system.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_system.c	(revision 91)
@@ -0,0 +1,190 @@
+/*
+ *   ALSA sequencer System services Client
+ *   Copyright (c) 1998-1999 by Frank van de Pol <fvdpol@coil.demon.nl>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <sound/core.h>
+#include "seq_system.h"
+#include "seq_timer.h"
+#include "seq_queue.h"
+
+/* internal client that provide system services, access to timer etc. */
+
+/*
+ * Port "Timer"
+ *      - send tempo /start/stop etc. events to this port to manipulate the 
+ *        queue's timer. The queue address is specified in
+ *	  data.queue.queue.
+ *      - this port supports subscription. The received timer events are 
+ *        broadcasted to all subscribed clients. The modified tempo
+ *	  value is stored on data.queue.value.
+ *	  The modifier client/port is not send.
+ *
+ * Port "Announce"
+ *      - does not receive message
+ *      - supports supscription. For each client or port attaching to or 
+ *        detaching from the system an announcement is send to the subscribed
+ *        clients.
+ *
+ * Idea: the subscription mechanism might also work handy for distributing 
+ * synchronisation and timing information. In this case we would ideally have
+ * a list of subscribers for each type of sync (time, tick), for each timing
+ * queue.
+ *
+ * NOTE: the queue to be started, stopped, etc. must be specified
+ *	 in data.queue.addr.queue field.  queue is used only for
+ *	 scheduling, and no longer referred as affected queue.
+ *	 They are used only for timer broadcast (see above).
+ *							-- iwai
+ */
+
+
+/* client id of our system client */
+static int sysclient = -1;
+
+/* port id numbers for this client */
+static int announce_port = -1;
+
+
+
+/* fill standard header data, source port & channel are filled in */
+static int setheader(struct snd_seq_event * ev, int client, int port)
+{
+	if (announce_port < 0)
+		return -ENODEV;
+
+	memset(ev, 0, sizeof(struct snd_seq_event));
+
+	ev->flags &= ~SNDRV_SEQ_EVENT_LENGTH_MASK;
+	ev->flags |= SNDRV_SEQ_EVENT_LENGTH_FIXED;
+
+	ev->source.client = sysclient;
+	ev->source.port = announce_port;
+	ev->dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS;
+
+	/* fill data */
+	/*ev->data.addr.queue = SNDRV_SEQ_ADDRESS_UNKNOWN;*/
+	ev->data.addr.client = client;
+	ev->data.addr.port = port;
+
+	return 0;
+}
+
+
+/* entry points for broadcasting system events */
+void snd_seq_system_broadcast(int client, int port, int type)
+{
+	struct snd_seq_event ev;
+	
+	if (setheader(&ev, client, port) < 0)
+		return;
+	ev.type = type;
+	snd_seq_kernel_client_dispatch(sysclient, &ev, 0, 0);
+}
+
+/* entry points for broadcasting system events */
+int snd_seq_system_notify(int client, int port, struct snd_seq_event *ev)
+{
+	ev->flags = SNDRV_SEQ_EVENT_LENGTH_FIXED;
+	ev->source.client = sysclient;
+	ev->source.port = announce_port;
+	ev->dest.client = client;
+	ev->dest.port = port;
+	return snd_seq_kernel_client_dispatch(sysclient, ev, 0, 0);
+}
+
+/* call-back handler for timer events */
+static int event_input_timer(struct snd_seq_event * ev, int direct, void *private_data, int atomic, int hop)
+{
+	return snd_seq_control_queue(ev, atomic, hop);
+}
+
+/* register our internal client */
+int __init snd_seq_system_client_init(void)
+{
+
+	struct snd_seq_client_callback callbacks;
+	struct snd_seq_port_callback pcallbacks;
+	struct snd_seq_client_info *inf;
+	struct snd_seq_port_info *port;
+
+	inf = (struct snd_seq_client_info *)kzalloc(sizeof(*inf), GFP_KERNEL);
+	port = (struct snd_seq_port_info *)kzalloc(sizeof(*port), GFP_KERNEL);
+	if (! inf || ! port) {
+		kfree(inf);
+		kfree(port);
+		return -ENOMEM;
+	}
+
+	memset(&callbacks, 0, sizeof(callbacks));
+	memset(&pcallbacks, 0, sizeof(pcallbacks));
+	pcallbacks.owner = THIS_MODULE;
+	pcallbacks.event_input = event_input_timer;
+
+	/* register client */
+	callbacks.allow_input = callbacks.allow_output = 1;
+	sysclient = snd_seq_create_kernel_client(NULL, 0, &callbacks);
+
+	/* set our name */
+	inf->client = 0;
+	inf->type = KERNEL_CLIENT;
+	strcpy(inf->name, "System");
+	snd_seq_kernel_client_ctl(sysclient, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, inf);
+
+	/* register timer */
+	strcpy(port->name, "Timer");
+	port->capability = SNDRV_SEQ_PORT_CAP_WRITE; /* accept queue control */
+	port->capability |= SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_SUBS_READ; /* for broadcast */
+	port->kernel = &pcallbacks;
+	port->type = 0;
+	port->flags = SNDRV_SEQ_PORT_FLG_GIVEN_PORT;
+	port->addr.client = sysclient;
+	port->addr.port = SNDRV_SEQ_PORT_SYSTEM_TIMER;
+	snd_seq_kernel_client_ctl(sysclient, SNDRV_SEQ_IOCTL_CREATE_PORT, port);
+
+	/* register announcement port */
+	strcpy(port->name, "Announce");
+	port->capability = SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_SUBS_READ; /* for broadcast only */
+	port->kernel = NULL;
+	port->type = 0;
+	port->flags = SNDRV_SEQ_PORT_FLG_GIVEN_PORT;
+	port->addr.client = sysclient;
+	port->addr.port = SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE;
+	snd_seq_kernel_client_ctl(sysclient, SNDRV_SEQ_IOCTL_CREATE_PORT, port);
+	announce_port = port->addr.port;
+
+	kfree(inf);
+	kfree(port);
+	return 0;
+}
+
+
+/* unregister our internal client */
+void __exit snd_seq_system_client_done(void)
+{
+	int oldsysclient = sysclient;
+
+	if (oldsysclient >= 0) {
+		sysclient = -1;
+		announce_port = -1;
+		snd_seq_delete_kernel_client(oldsysclient);
+	}
+}
Index: /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_system.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_system.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_system.h	(revision 91)
@@ -0,0 +1,46 @@
+/*
+ *  ALSA sequencer System Client
+ *  Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+#ifndef __SND_SEQ_SYSTEM_H
+#define __SND_SEQ_SYSTEM_H
+
+#include <sound/seq_kernel.h>
+
+
+/* entry points for broadcasting system events */
+void snd_seq_system_broadcast(int client, int port, int type);
+
+#define snd_seq_system_client_ev_client_start(client) snd_seq_system_broadcast(client, 0, SNDRV_SEQ_EVENT_CLIENT_START)
+#define snd_seq_system_client_ev_client_exit(client) snd_seq_system_broadcast(client, 0, SNDRV_SEQ_EVENT_CLIENT_EXIT)
+#define snd_seq_system_client_ev_client_change(client) snd_seq_system_broadcast(client, 0, SNDRV_SEQ_EVENT_CLIENT_CHANGE)
+#define snd_seq_system_client_ev_port_start(client, port) snd_seq_system_broadcast(client, port, SNDRV_SEQ_EVENT_PORT_START)
+#define snd_seq_system_client_ev_port_exit(client, port) snd_seq_system_broadcast(client, port, SNDRV_SEQ_EVENT_PORT_EXIT)
+#define snd_seq_system_client_ev_port_change(client, port) snd_seq_system_broadcast(client, port, SNDRV_SEQ_EVENT_PORT_CHANGE)
+
+int snd_seq_system_notify(int client, int port, struct snd_seq_event *ev);
+
+/* register our internal client */
+int snd_seq_system_client_init(void);
+
+/* unregister our internal client */
+void snd_seq_system_client_done(void);
+
+
+#endif
Index: /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_timer.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_timer.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_timer.c	(revision 91)
@@ -0,0 +1,453 @@
+/*
+ *   ALSA sequencer Timer
+ *   Copyright (c) 1998-1999 by Frank van de Pol <fvdpol@coil.demon.nl>
+ *                              Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <linux/slab.h>
+#include "seq_timer.h"
+#include "seq_queue.h"
+#include "seq_info.h"
+
+extern int seq_default_timer_class;
+extern int seq_default_timer_sclass;
+extern int seq_default_timer_card;
+extern int seq_default_timer_device;
+extern int seq_default_timer_subdevice;
+extern int seq_default_timer_resolution;
+
+/* allowed sequencer timer frequencies, in Hz */
+#define MIN_FREQUENCY		10
+#define MAX_FREQUENCY		6250
+#define DEFAULT_FREQUENCY	1000
+
+#define SKEW_BASE	0x10000	/* 16bit shift */
+
+static void snd_seq_timer_set_tick_resolution(struct snd_seq_timer_tick *tick,
+					      int tempo, int ppq)
+{
+	if (tempo < 1000000)
+		tick->resolution = (tempo * 1000) / ppq;
+	else {
+		/* might overflow.. */
+		unsigned int s;
+		s = tempo % ppq;
+		s = (s * 1000) / ppq;
+		tick->resolution = (tempo / ppq) * 1000;
+		tick->resolution += s;
+	}
+	if (tick->resolution <= 0)
+		tick->resolution = 1;
+	snd_seq_timer_update_tick(tick, 0);
+}
+
+/* create new timer (constructor) */
+struct snd_seq_timer *snd_seq_timer_new(void)
+{
+	struct snd_seq_timer *tmr;
+	
+	tmr = (struct snd_seq_timer *)kzalloc(sizeof(*tmr), GFP_KERNEL);
+	if (tmr == NULL) {
+		snd_printd("malloc failed for snd_seq_timer_new() \n");
+		return NULL;
+	}
+	spin_lock_init(&tmr->lock);
+
+	/* reset setup to defaults */
+	snd_seq_timer_defaults(tmr);
+	
+	/* reset time */
+	snd_seq_timer_reset(tmr);
+	
+	return tmr;
+}
+
+/* delete timer (destructor) */
+void snd_seq_timer_delete(struct snd_seq_timer **tmr)
+{
+	struct snd_seq_timer *t = *tmr;
+	*tmr = NULL;
+
+	if (t == NULL) {
+		snd_printd("oops: snd_seq_timer_delete() called with NULL timer\n");
+		return;
+	}
+	t->running = 0;
+
+	/* reset time */
+	snd_seq_timer_stop(t);
+	snd_seq_timer_reset(t);
+
+	kfree(t);
+}
+
+void snd_seq_timer_defaults(struct snd_seq_timer * tmr)
+{
+	/* setup defaults */
+	tmr->ppq = 96;		/* 96 PPQ */
+	tmr->tempo = 500000;	/* 120 BPM */
+	snd_seq_timer_set_tick_resolution(&tmr->tick, tmr->tempo, tmr->ppq);
+	tmr->running = 0;
+
+	tmr->type = SNDRV_SEQ_TIMER_ALSA;
+	tmr->alsa_id.dev_class = seq_default_timer_class;
+	tmr->alsa_id.dev_sclass = seq_default_timer_sclass;
+	tmr->alsa_id.card = seq_default_timer_card;
+	tmr->alsa_id.device = seq_default_timer_device;
+	tmr->alsa_id.subdevice = seq_default_timer_subdevice;
+	tmr->preferred_resolution = seq_default_timer_resolution;
+
+	tmr->skew = tmr->skew_base = SKEW_BASE;
+}
+
+void snd_seq_timer_reset(struct snd_seq_timer * tmr)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&tmr->lock, flags);
+
+	/* reset time & songposition */
+	tmr->cur_time.tv_sec = 0;
+	tmr->cur_time.tv_nsec = 0;
+
+	tmr->tick.cur_tick = 0;
+	tmr->tick.fraction = 0;
+
+	spin_unlock_irqrestore(&tmr->lock, flags);
+}
+
+
+/* called by timer interrupt routine. the period time since previous invocation is passed */
+static void snd_seq_timer_interrupt(struct snd_timer_instance *timeri,
+				    unsigned long resolution,
+				    unsigned long ticks)
+{
+	unsigned long flags;
+	struct snd_seq_queue *q = timeri->callback_data;
+	struct snd_seq_timer *tmr;
+
+	if (q == NULL)
+		return;
+	tmr = q->timer;
+	if (tmr == NULL)
+		return;
+	if (!tmr->running)
+		return;
+
+	resolution *= ticks;
+	if (tmr->skew != tmr->skew_base) {
+		/* FIXME: assuming skew_base = 0x10000 */
+		resolution = (resolution >> 16) * tmr->skew +
+			(((resolution & 0xffff) * tmr->skew) >> 16);
+	}
+
+	spin_lock_irqsave(&tmr->lock, flags);
+
+	/* update timer */
+	snd_seq_inc_time_nsec(&tmr->cur_time, resolution);
+
+	/* calculate current tick */
+	snd_seq_timer_update_tick(&tmr->tick, resolution);
+
+	/* register actual time of this timer update */
+	do_gettimeofday(&tmr->last_update);
+
+	spin_unlock_irqrestore(&tmr->lock, flags);
+
+	/* check queues and dispatch events */
+	snd_seq_check_queue(q, 1, 0);
+}
+
+/* set current tempo */
+int snd_seq_timer_set_tempo(struct snd_seq_timer * tmr, int tempo)
+{
+	unsigned long flags;
+
+	snd_assert(tmr, return -EINVAL);
+	if (tempo <= 0)
+		return -EINVAL;
+	spin_lock_irqsave(&tmr->lock, flags);
+	if ((unsigned int)tempo != tmr->tempo) {
+		tmr->tempo = tempo;
+		snd_seq_timer_set_tick_resolution(&tmr->tick, tmr->tempo, tmr->ppq);
+	}
+	spin_unlock_irqrestore(&tmr->lock, flags);
+	return 0;
+}
+
+/* set current ppq */
+int snd_seq_timer_set_ppq(struct snd_seq_timer * tmr, int ppq)
+{
+	unsigned long flags;
+
+	snd_assert(tmr, return -EINVAL);
+	if (ppq <= 0)
+		return -EINVAL;
+	spin_lock_irqsave(&tmr->lock, flags);
+	if (tmr->running && (ppq != tmr->ppq)) {
+		/* refuse to change ppq on running timers */
+		/* because it will upset the song position (ticks) */
+		spin_unlock_irqrestore(&tmr->lock, flags);
+		snd_printd("seq: cannot change ppq of a running timer\n");
+		return -EBUSY;
+	}
+
+	tmr->ppq = ppq;
+	snd_seq_timer_set_tick_resolution(&tmr->tick, tmr->tempo, tmr->ppq);
+	spin_unlock_irqrestore(&tmr->lock, flags);
+	return 0;
+}
+
+/* set current tick position */
+int snd_seq_timer_set_position_tick(struct snd_seq_timer *tmr,
+				    snd_seq_tick_time_t position)
+{
+	unsigned long flags;
+
+	snd_assert(tmr, return -EINVAL);
+
+	spin_lock_irqsave(&tmr->lock, flags);
+	tmr->tick.cur_tick = position;
+	tmr->tick.fraction = 0;
+	spin_unlock_irqrestore(&tmr->lock, flags);
+	return 0;
+}
+
+/* set current real-time position */
+int snd_seq_timer_set_position_time(struct snd_seq_timer *tmr,
+				    snd_seq_real_time_t position)
+{
+	unsigned long flags;
+
+	snd_assert(tmr, return -EINVAL);
+
+	snd_seq_sanity_real_time(&position);
+	spin_lock_irqsave(&tmr->lock, flags);
+	tmr->cur_time = position;
+	spin_unlock_irqrestore(&tmr->lock, flags);
+	return 0;
+}
+
+/* set timer skew */
+int snd_seq_timer_set_skew(struct snd_seq_timer *tmr, unsigned int skew,
+			   unsigned int base)
+{
+	unsigned long flags;
+
+	snd_assert(tmr, return -EINVAL);
+
+	/* FIXME */
+	if (base != SKEW_BASE) {
+		snd_printd("invalid skew base 0x%x\n", base);
+		return -EINVAL;
+	}
+	spin_lock_irqsave(&tmr->lock, flags);
+	tmr->skew = skew;
+	spin_unlock_irqrestore(&tmr->lock, flags);
+	return 0;
+}
+
+int snd_seq_timer_open(struct snd_seq_queue *q)
+{
+	struct snd_timer_instance *t;
+	struct snd_seq_timer *tmr;
+	char str[32];
+	int err;
+
+	tmr = q->timer;
+	snd_assert(tmr != NULL, return -EINVAL);
+	if (tmr->timeri)
+		return -EBUSY;
+	sprintf(str, "sequencer queue %i", q->queue);
+	if (tmr->type != SNDRV_SEQ_TIMER_ALSA)	/* standard ALSA timer */
+		return -EINVAL;
+	if (tmr->alsa_id.dev_class != SNDRV_TIMER_CLASS_SLAVE)
+		tmr->alsa_id.dev_sclass = SNDRV_TIMER_SCLASS_SEQUENCER;
+	err = snd_timer_open(&t, str, &tmr->alsa_id, q->queue);
+	if (err < 0 && tmr->alsa_id.dev_class != SNDRV_TIMER_CLASS_SLAVE) {
+		if (tmr->alsa_id.dev_class != SNDRV_TIMER_CLASS_GLOBAL ||
+		    tmr->alsa_id.device != SNDRV_TIMER_GLOBAL_SYSTEM) {
+			struct snd_timer_id tid;
+			memset(&tid, 0, sizeof(tid));
+			tid.dev_class = SNDRV_TIMER_CLASS_GLOBAL;
+			tid.dev_sclass = SNDRV_TIMER_SCLASS_SEQUENCER;
+			tid.card = -1;
+			tid.device = SNDRV_TIMER_GLOBAL_SYSTEM;
+			err = snd_timer_open(&t, str, &tid, q->queue);
+		}
+		if (err < 0) {
+			snd_printk(KERN_ERR "seq fatal error: cannot create timer (%i)\n", err);
+			return err;
+		}
+	}
+	t->callback = snd_seq_timer_interrupt;
+	t->callback_data = q;
+	t->flags |= SNDRV_TIMER_IFLG_AUTO;
+	tmr->timeri = t;
+	return 0;
+}
+
+int snd_seq_timer_close(struct snd_seq_queue *q)
+{
+	struct snd_seq_timer *tmr;
+	
+	tmr = q->timer;
+	snd_assert(tmr != NULL, return -EINVAL);
+	if (tmr->timeri) {
+		snd_timer_stop(tmr->timeri);
+		snd_timer_close(tmr->timeri);
+		tmr->timeri = NULL;
+	}
+	return 0;
+}
+
+int snd_seq_timer_stop(struct snd_seq_timer * tmr)
+{
+	if (! tmr->timeri)
+		return -EINVAL;
+	if (!tmr->running)
+		return 0;
+	tmr->running = 0;
+	snd_timer_pause(tmr->timeri);
+	return 0;
+}
+
+static int initialize_timer(struct snd_seq_timer *tmr)
+{
+	struct snd_timer *t;
+	unsigned long freq;
+
+	t = tmr->timeri->timer;
+	snd_assert(t, return -EINVAL);
+
+	freq = tmr->preferred_resolution;
+	if (!freq)
+		freq = DEFAULT_FREQUENCY;
+	else if (freq < MIN_FREQUENCY)
+		freq = MIN_FREQUENCY;
+	else if (freq > MAX_FREQUENCY)
+		freq = MAX_FREQUENCY;
+
+	tmr->ticks = 1;
+	if (!(t->hw.flags & SNDRV_TIMER_HW_SLAVE)) {
+		unsigned long r = t->hw.resolution;
+		if (! r && t->hw.c_resolution)
+			r = t->hw.c_resolution(t);
+		if (r) {
+			tmr->ticks = (unsigned int)(1000000000uL / (r * freq));
+			if (! tmr->ticks)
+				tmr->ticks = 1;
+		}
+	}
+	tmr->initialized = 1;
+	return 0;
+}
+
+int snd_seq_timer_start(struct snd_seq_timer * tmr)
+{
+	if (! tmr->timeri)
+		return -EINVAL;
+	if (tmr->running)
+		snd_seq_timer_stop(tmr);
+	snd_seq_timer_reset(tmr);
+	if (initialize_timer(tmr) < 0)
+		return -EINVAL;
+	snd_timer_start(tmr->timeri, tmr->ticks);
+	tmr->running = 1;
+	do_gettimeofday(&tmr->last_update);
+	return 0;
+}
+
+int snd_seq_timer_continue(struct snd_seq_timer * tmr)
+{
+	if (! tmr->timeri)
+		return -EINVAL;
+	if (tmr->running)
+		return -EBUSY;
+	if (! tmr->initialized) {
+		snd_seq_timer_reset(tmr);
+		if (initialize_timer(tmr) < 0)
+			return -EINVAL;
+	}
+	snd_timer_start(tmr->timeri, tmr->ticks);
+	tmr->running = 1;
+	do_gettimeofday(&tmr->last_update);
+	return 0;
+}
+
+/* return current 'real' time. use timeofday() to get better granularity. */
+snd_seq_real_time_t snd_seq_timer_get_cur_time(struct snd_seq_timer *tmr)
+{
+	snd_seq_real_time_t cur_time;
+
+	cur_time = tmr->cur_time;
+	if (tmr->running) { 
+		struct timeval tm;
+		int usec;
+		do_gettimeofday(&tm);
+		usec = (int)(tm.tv_usec - tmr->last_update.tv_usec);
+		if (usec < 0) {
+			cur_time.tv_nsec += (1000000 + usec) * 1000;
+			cur_time.tv_sec += tm.tv_sec - tmr->last_update.tv_sec - 1;
+		} else {
+			cur_time.tv_nsec += usec * 1000;
+			cur_time.tv_sec += tm.tv_sec - tmr->last_update.tv_sec;
+		}
+		snd_seq_sanity_real_time(&cur_time);
+	}
+                
+	return cur_time;	
+}
+
+/* TODO: use interpolation on tick queue (will only be useful for very
+ high PPQ values) */
+snd_seq_tick_time_t snd_seq_timer_get_cur_tick(struct snd_seq_timer *tmr)
+{
+	return tmr->tick.cur_tick;
+}
+
+
+/* exported to seq_info.c */
+void snd_seq_info_timer_read(struct snd_info_entry *entry,
+			     struct snd_info_buffer *buffer)
+{
+	int idx;
+	struct snd_seq_queue *q;
+	struct snd_seq_timer *tmr;
+	struct snd_timer_instance *ti;
+	unsigned long resolution;
+	
+	for (idx = 0; idx < SNDRV_SEQ_MAX_QUEUES; idx++) {
+		q = queueptr(idx);
+		if (q == NULL)
+			continue;
+		if ((tmr = q->timer) == NULL ||
+		    (ti = tmr->timeri) == NULL) {
+			queuefree(q);
+			continue;
+		}
+		snd_iprintf(buffer, "Timer for queue %i : %s\n", q->queue, ti->timer->name);
+		resolution = snd_timer_resolution(ti) * tmr->ticks;
+		snd_iprintf(buffer, "  Period time : %lu.%09lu\n", resolution / 1000000000, resolution % 1000000000);
+		snd_iprintf(buffer, "  Skew : %u / %u\n", tmr->skew, tmr->skew_base);
+		queuefree(q);
+ 	}
+}
Index: /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_timer.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_timer.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_timer.h	(revision 91)
@@ -0,0 +1,141 @@
+/*
+ *  ALSA sequencer Timer
+ *  Copyright (c) 1998-1999 by Frank van de Pol <fvdpol@coil.demon.nl>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+#ifndef __SND_SEQ_TIMER_H
+#define __SND_SEQ_TIMER_H
+
+#include <sound/timer.h>
+#include <sound/seq_kernel.h>
+
+struct snd_seq_timer_tick {
+	snd_seq_tick_time_t	cur_tick;	/* current tick */
+	unsigned long		resolution;	/* time per tick in nsec */
+	unsigned long		fraction;	/* current time per tick in nsec */
+};
+
+struct snd_seq_timer {
+	/* ... tempo / offset / running state */
+
+	unsigned int		running:1,	/* running state of queue */	
+				initialized:1;	/* timer is initialized */
+
+	unsigned int		tempo;		/* current tempo, us/tick */
+	int			ppq;		/* time resolution, ticks/quarter */
+
+	snd_seq_real_time_t	cur_time;	/* current time */
+	struct snd_seq_timer_tick	tick;	/* current tick */
+	int tick_updated;
+	
+	int			type;		/* timer type */
+	struct snd_timer_id	alsa_id;	/* ALSA's timer ID */
+	struct snd_timer_instance	*timeri;	/* timer instance */
+	unsigned int		ticks;
+	unsigned long		preferred_resolution; /* timer resolution, ticks/sec */
+
+	unsigned int skew;
+	unsigned int skew_base;
+
+	struct timeval 		last_update;	 /* time of last clock update, used for interpolation */
+
+	spinlock_t lock;
+};
+
+
+/* create new timer (constructor) */
+struct snd_seq_timer *snd_seq_timer_new(void);
+
+/* delete timer (destructor) */
+void snd_seq_timer_delete(struct snd_seq_timer **tmr);
+
+/* */
+static inline void snd_seq_timer_update_tick(struct snd_seq_timer_tick *tick,
+					     unsigned long resolution)
+{
+	if (tick->resolution > 0) {
+		tick->fraction += resolution;
+		tick->cur_tick += (unsigned int)(tick->fraction / tick->resolution);
+		tick->fraction %= tick->resolution;
+	}
+}
+
+
+/* compare timestamp between events */
+/* return 1 if a >= b; otherwise return 0 */
+static inline int snd_seq_compare_tick_time(snd_seq_tick_time_t *a, snd_seq_tick_time_t *b)
+{
+	/* compare ticks */
+	return (*a >= *b);
+}
+
+static inline int snd_seq_compare_real_time(snd_seq_real_time_t *a, snd_seq_real_time_t *b)
+{
+	/* compare real time */
+	if (a->tv_sec > b->tv_sec)
+		return 1;
+	if ((a->tv_sec == b->tv_sec) && (a->tv_nsec >= b->tv_nsec))
+		return 1;
+	return 0;
+}
+
+
+static inline void snd_seq_sanity_real_time(snd_seq_real_time_t *tm)
+{
+	while (tm->tv_nsec >= 1000000000) {
+		/* roll-over */
+		tm->tv_nsec -= 1000000000;
+                tm->tv_sec++;
+        }
+}
+
+
+/* increment timestamp */
+static inline void snd_seq_inc_real_time(snd_seq_real_time_t *tm, snd_seq_real_time_t *inc)
+{
+	tm->tv_sec  += inc->tv_sec;
+	tm->tv_nsec += inc->tv_nsec;
+	snd_seq_sanity_real_time(tm);
+}
+
+static inline void snd_seq_inc_time_nsec(snd_seq_real_time_t *tm, unsigned long nsec)
+{
+	tm->tv_nsec  += nsec;
+	snd_seq_sanity_real_time(tm);
+}
+
+/* called by timer isr */
+struct snd_seq_queue;
+int snd_seq_timer_open(struct snd_seq_queue *q);
+int snd_seq_timer_close(struct snd_seq_queue *q);
+int snd_seq_timer_midi_open(struct snd_seq_queue *q);
+int snd_seq_timer_midi_close(struct snd_seq_queue *q);
+void snd_seq_timer_defaults(struct snd_seq_timer *tmr);
+void snd_seq_timer_reset(struct snd_seq_timer *tmr);
+int snd_seq_timer_stop(struct snd_seq_timer *tmr);
+int snd_seq_timer_start(struct snd_seq_timer *tmr);
+int snd_seq_timer_continue(struct snd_seq_timer *tmr);
+int snd_seq_timer_set_tempo(struct snd_seq_timer *tmr, int tempo);
+int snd_seq_timer_set_ppq(struct snd_seq_timer *tmr, int ppq);
+int snd_seq_timer_set_position_tick(struct snd_seq_timer *tmr, snd_seq_tick_time_t position);
+int snd_seq_timer_set_position_time(struct snd_seq_timer *tmr, snd_seq_real_time_t position);
+int snd_seq_timer_set_skew(struct snd_seq_timer *tmr, unsigned int skew, unsigned int base);
+snd_seq_real_time_t snd_seq_timer_get_cur_time(struct snd_seq_timer *tmr);
+snd_seq_tick_time_t snd_seq_timer_get_cur_tick(struct snd_seq_timer *tmr);
+
+#endif
Index: /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_virmidi.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_virmidi.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/core/seq/seq_virmidi.c	(revision 91)
@@ -0,0 +1,557 @@
+/*
+ *  Virtual Raw MIDI client on Sequencer
+ *
+ *  Copyright (c) 2000 by Takashi Iwai <tiwai@suse.de>,
+ *                        Jaroslav Kysela <perex@perex.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
+ *
+ */
+
+/*
+ * Virtual Raw MIDI client
+ *
+ * The virtual rawmidi client is a sequencer client which associate
+ * a rawmidi device file.  The created rawmidi device file can be
+ * accessed as a normal raw midi, but its MIDI source and destination
+ * are arbitrary.  For example, a user-client software synth connected
+ * to this port can be used as a normal midi device as well.
+ *
+ * The virtual rawmidi device accepts also multiple opens.  Each file
+ * has its own input buffer, so that no conflict would occur.  The drain
+ * of input/output buffer acts only to the local buffer.
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/rawmidi.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/minors.h>
+#include <sound/seq_kernel.h>
+#include <sound/seq_midi_event.h>
+#include <sound/seq_virmidi.h>
+
+MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
+MODULE_DESCRIPTION("Virtual Raw MIDI client on Sequencer");
+MODULE_LICENSE("GPL");
+
+/*
+ * initialize an event record
+ */
+static void snd_virmidi_init_event(struct snd_virmidi *vmidi,
+				   struct snd_seq_event *ev)
+{
+	memset(ev, 0, sizeof(*ev));
+	ev->source.port = vmidi->port;
+	switch (vmidi->seq_mode) {
+	case SNDRV_VIRMIDI_SEQ_DISPATCH:
+		ev->dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS;
+		break;
+	case SNDRV_VIRMIDI_SEQ_ATTACH:
+		/* FIXME: source and destination are same - not good.. */
+		ev->dest.client = vmidi->client;
+		ev->dest.port = vmidi->port;
+		break;
+	}
+	ev->type = SNDRV_SEQ_EVENT_NONE;
+}
+
+/*
+ * decode input event and put to read buffer of each opened file
+ */
+static int snd_virmidi_dev_receive_event(struct snd_virmidi_dev *rdev,
+					 struct snd_seq_event *ev)
+{
+	struct snd_virmidi *vmidi;
+	struct list_head *list;
+	unsigned char msg[4];
+	int len;
+
+	read_lock(&rdev->filelist_lock);
+	list_for_each(list, &rdev->filelist) {
+		vmidi = list_entry(list, struct snd_virmidi, list);
+		if (!vmidi->trigger)
+			continue;
+		if (ev->type == SNDRV_SEQ_EVENT_SYSEX) {
+			if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARIABLE)
+				continue;
+			snd_seq_dump_var_event(ev, (snd_seq_dump_func_t)snd_rawmidi_receive, vmidi->substream);
+		} else {
+			len = snd_midi_event_decode(vmidi->parser, msg, sizeof(msg), ev);
+			if (len > 0)
+				snd_rawmidi_receive(vmidi->substream, msg, len);
+		}
+	}
+	read_unlock(&rdev->filelist_lock);
+
+	return 0;
+}
+
+/*
+ * receive an event from the remote virmidi port
+ *
+ * for rawmidi inputs, you can call this function from the event
+ * handler of a remote port which is attached to the virmidi via
+ * SNDRV_VIRMIDI_SEQ_ATTACH.
+ */
+#if 0
+int snd_virmidi_receive(struct snd_rawmidi *rmidi, struct snd_seq_event *ev)
+{
+	struct snd_virmidi_dev *rdev;
+
+	rdev = rmidi->private_data;
+	return snd_virmidi_dev_receive_event(rdev, ev);
+}
+#endif  /*  0  */
+
+/*
+ * event handler of virmidi port
+ */
+static int snd_virmidi_event_input(struct snd_seq_event *ev, int direct,
+				   void *private_data, int atomic, int hop)
+{
+	struct snd_virmidi_dev *rdev;
+
+	rdev = private_data;
+	if (!(rdev->flags & SNDRV_VIRMIDI_USE))
+		return 0; /* ignored */
+	return snd_virmidi_dev_receive_event(rdev, ev);
+}
+
+/*
+ * trigger rawmidi stream for input
+ */
+static void snd_virmidi_input_trigger(struct snd_rawmidi_substream *substream, int up)
+{
+	struct snd_virmidi *vmidi = substream->runtime->private_data;
+
+	if (up) {
+		vmidi->trigger = 1;
+	} else {
+		vmidi->trigger = 0;
+	}
+}
+
+/*
+ * trigger rawmidi stream for output
+ */
+static void snd_virmidi_output_trigger(struct snd_rawmidi_substream *substream, int up)
+{
+	struct snd_virmidi *vmidi = substream->runtime->private_data;
+	int count, res;
+	unsigned char buf[32], *pbuf;
+
+	if (up) {
+		vmidi->trigger = 1;
+		if (vmidi->seq_mode == SNDRV_VIRMIDI_SEQ_DISPATCH &&
+		    !(vmidi->rdev->flags & SNDRV_VIRMIDI_SUBSCRIBE)) {
+			snd_rawmidi_transmit_ack(substream, substream->runtime->buffer_size - substream->runtime->avail);
+			return;		/* ignored */
+		}
+		if (vmidi->event.type != SNDRV_SEQ_EVENT_NONE) {
+			if (snd_seq_kernel_client_dispatch(vmidi->client, &vmidi->event, 0, 0) < 0)
+				return;
+			vmidi->event.type = SNDRV_SEQ_EVENT_NONE;
+		}
+		while (1) {
+			count = snd_rawmidi_transmit_peek(substream, buf, sizeof(buf));
+			if (count <= 0)
+				break;
+			pbuf = buf;
+			while (count > 0) {
+				res = snd_midi_event_encode(vmidi->parser, pbuf, count, &vmidi->event);
+				if (res < 0) {
+					snd_midi_event_reset_encode(vmidi->parser);
+					continue;
+				}
+				snd_rawmidi_transmit_ack(substream, res);
+				pbuf += res;
+				count -= res;
+				if (vmidi->event.type != SNDRV_SEQ_EVENT_NONE) {
+					if (snd_seq_kernel_client_dispatch(vmidi->client, &vmidi->event, 0, 0) < 0)
+						return;
+					vmidi->event.type = SNDRV_SEQ_EVENT_NONE;
+				}
+			}
+		}
+	} else {
+		vmidi->trigger = 0;
+	}
+}
+
+/*
+ * open rawmidi handle for input
+ */
+static int snd_virmidi_input_open(struct snd_rawmidi_substream *substream)
+{
+	struct snd_virmidi_dev *rdev = substream->rmidi->private_data;
+	struct snd_rawmidi_runtime *runtime = substream->runtime;
+	struct snd_virmidi *vmidi;
+	unsigned long flags;
+
+	vmidi = kzalloc(sizeof(*vmidi), GFP_KERNEL);
+	if (vmidi == NULL)
+		return -ENOMEM;
+	vmidi->substream = substream;
+	if (snd_midi_event_new(0, &vmidi->parser) < 0) {
+		kfree(vmidi);
+		return -ENOMEM;
+	}
+	vmidi->seq_mode = rdev->seq_mode;
+	vmidi->client = rdev->client;
+	vmidi->port = rdev->port;	
+	runtime->private_data = vmidi;
+	write_lock_irqsave(&rdev->filelist_lock, flags);
+	list_add_tail(&vmidi->list, &rdev->filelist);
+	write_unlock_irqrestore(&rdev->filelist_lock, flags);
+	vmidi->rdev = rdev;
+	return 0;
+}
+
+/*
+ * open rawmidi handle for output
+ */
+static int snd_virmidi_output_open(struct snd_rawmidi_substream *substream)
+{
+	struct snd_virmidi_dev *rdev = substream->rmidi->private_data;
+	struct snd_rawmidi_runtime *runtime = substream->runtime;
+	struct snd_virmidi *vmidi;
+
+	vmidi = kzalloc(sizeof(*vmidi), GFP_KERNEL);
+	if (vmidi == NULL)
+		return -ENOMEM;
+	vmidi->substream = substream;
+	if (snd_midi_event_new(MAX_MIDI_EVENT_BUF, &vmidi->parser) < 0) {
+		kfree(vmidi);
+		return -ENOMEM;
+	}
+	vmidi->seq_mode = rdev->seq_mode;
+	vmidi->client = rdev->client;
+	vmidi->port = rdev->port;
+	snd_virmidi_init_event(vmidi, &vmidi->event);
+	vmidi->rdev = rdev;
+	runtime->private_data = vmidi;
+	return 0;
+}
+
+/*
+ * close rawmidi handle for input
+ */
+static int snd_virmidi_input_close(struct snd_rawmidi_substream *substream)
+{
+	struct snd_virmidi *vmidi = substream->runtime->private_data;
+	snd_midi_event_free(vmidi->parser);
+	list_del(&vmidi->list);
+	substream->runtime->private_data = NULL;
+	kfree(vmidi);
+	return 0;
+}
+
+/*
+ * close rawmidi handle for output
+ */
+static int snd_virmidi_output_close(struct snd_rawmidi_substream *substream)
+{
+	struct snd_virmidi *vmidi = substream->runtime->private_data;
+	snd_midi_event_free(vmidi->parser);
+	substream->runtime->private_data = NULL;
+	kfree(vmidi);
+	return 0;
+}
+
+/*
+ * subscribe callback - allow output to rawmidi device
+ */
+static int snd_virmidi_subscribe(void *private_data,
+				 struct snd_seq_port_subscribe *info)
+{
+	struct snd_virmidi_dev *rdev;
+
+	rdev = private_data;
+	if (!try_module_get(rdev->card->module))
+		return -EFAULT;
+	rdev->flags |= SNDRV_VIRMIDI_SUBSCRIBE;
+	return 0;
+}
+
+/*
+ * unsubscribe callback - disallow output to rawmidi device
+ */
+static int snd_virmidi_unsubscribe(void *private_data,
+				   struct snd_seq_port_subscribe *info)
+{
+	struct snd_virmidi_dev *rdev;
+
+	rdev = private_data;
+	rdev->flags &= ~SNDRV_VIRMIDI_SUBSCRIBE;
+	module_put(rdev->card->module);
+	return 0;
+}
+
+
+/*
+ * use callback - allow input to rawmidi device
+ */
+static int snd_virmidi_use(void *private_data,
+			   struct snd_seq_port_subscribe *info)
+{
+	struct snd_virmidi_dev *rdev;
+
+	rdev = private_data;
+	if (!try_module_get(rdev->card->module))
+		return -EFAULT;
+	rdev->flags |= SNDRV_VIRMIDI_USE;
+	return 0;
+}
+
+/*
+ * unuse callback - disallow input to rawmidi device
+ */
+static int snd_virmidi_unuse(void *private_data,
+			     struct snd_seq_port_subscribe *info)
+{
+	struct snd_virmidi_dev *rdev;
+
+	rdev = private_data;
+	rdev->flags &= ~SNDRV_VIRMIDI_USE;
+	module_put(rdev->card->module);
+	return 0;
+}
+
+
+/*
+ *  Register functions
+ */
+
+static struct snd_rawmidi_ops snd_virmidi_input_ops = {
+	.open = snd_virmidi_input_open,
+	.close = snd_virmidi_input_close,
+	.trigger = snd_virmidi_input_trigger,
+};
+
+static struct snd_rawmidi_ops snd_virmidi_output_ops = {
+	.open = snd_virmidi_output_open,
+	.close = snd_virmidi_output_close,
+	.trigger = snd_virmidi_output_trigger,
+};
+
+/*
+ * create a sequencer client and a port
+ */
+static int snd_virmidi_dev_attach_seq(struct snd_virmidi_dev *rdev)
+{
+	int client;
+	struct snd_seq_client_callback callbacks;
+	struct snd_seq_port_callback pcallbacks;
+	struct snd_seq_client_info *info;
+	struct snd_seq_port_info *pinfo;
+	int err;
+
+	if (rdev->client >= 0)
+		return 0;
+
+	info = kmalloc(sizeof(*info), GFP_KERNEL);
+	pinfo = kmalloc(sizeof(*pinfo), GFP_KERNEL);
+	if (! info || ! pinfo) {
+		err = -ENOMEM;
+		goto __error;
+	}
+
+	memset(&callbacks, 0, sizeof(callbacks));
+	callbacks.private_data = rdev;
+	callbacks.allow_input = 1;
+	callbacks.allow_output = 1;
+	client = snd_seq_create_kernel_client(rdev->card, rdev->device, &callbacks);
+	if (client < 0) {
+		err = client;
+		goto __error;
+	}
+	rdev->client = client;
+
+	/* set client name */
+	memset(info, 0, sizeof(*info));
+	info->client = client;
+	info->type = KERNEL_CLIENT;
+	sprintf(info->name, "%s %d-%d", rdev->rmidi->name, rdev->card->number, rdev->device);
+	snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, info);
+
+	/* create a port */
+	memset(pinfo, 0, sizeof(*pinfo));
+	pinfo->addr.client = client;
+	sprintf(pinfo->name, "VirMIDI %d-%d", rdev->card->number, rdev->device);
+	/* set all capabilities */
+	pinfo->capability |= SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SYNC_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE;
+	pinfo->capability |= SNDRV_SEQ_PORT_CAP_READ | SNDRV_SEQ_PORT_CAP_SYNC_READ | SNDRV_SEQ_PORT_CAP_SUBS_READ;
+	pinfo->capability |= SNDRV_SEQ_PORT_CAP_DUPLEX;
+	pinfo->type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC;
+	pinfo->midi_channels = 16;
+	memset(&pcallbacks, 0, sizeof(pcallbacks));
+	pcallbacks.owner = THIS_MODULE;
+	pcallbacks.private_data = rdev;
+	pcallbacks.subscribe = snd_virmidi_subscribe;
+	pcallbacks.unsubscribe = snd_virmidi_unsubscribe;
+	pcallbacks.use = snd_virmidi_use;
+	pcallbacks.unuse = snd_virmidi_unuse;
+	pcallbacks.event_input = snd_virmidi_event_input;
+	pinfo->kernel = &pcallbacks;
+	err = snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_CREATE_PORT, pinfo);
+	if (err < 0) {
+		snd_seq_delete_kernel_client(client);
+		rdev->client = -1;
+		goto __error;
+	}
+
+	rdev->port = pinfo->addr.port;
+	err = 0; /* success */
+
+ __error:
+	kfree(info);
+	kfree(pinfo);
+	return err;
+}
+
+
+/*
+ * release the sequencer client
+ */
+static void snd_virmidi_dev_detach_seq(struct snd_virmidi_dev *rdev)
+{
+	if (rdev->client >= 0) {
+		snd_seq_delete_kernel_client(rdev->client);
+		rdev->client = -1;
+	}
+}
+
+/*
+ * register the device
+ */
+static int snd_virmidi_dev_register(struct snd_rawmidi *rmidi)
+{
+	struct snd_virmidi_dev *rdev = rmidi->private_data;
+	int err;
+
+	switch (rdev->seq_mode) {
+	case SNDRV_VIRMIDI_SEQ_DISPATCH:
+		err = snd_virmidi_dev_attach_seq(rdev);
+		if (err < 0)
+			return err;
+		break;
+	case SNDRV_VIRMIDI_SEQ_ATTACH:
+		if (rdev->client == 0)
+			return -EINVAL;
+		/* should check presence of port more strictly.. */
+		break;
+	default:
+		snd_printk(KERN_ERR "seq_mode is not set: %d\n", rdev->seq_mode);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+
+/*
+ * unregister the device
+ */
+static int snd_virmidi_dev_unregister(struct snd_rawmidi *rmidi)
+{
+	struct snd_virmidi_dev *rdev = rmidi->private_data;
+
+	if (rdev->seq_mode == SNDRV_VIRMIDI_SEQ_DISPATCH)
+		snd_virmidi_dev_detach_seq(rdev);
+	return 0;
+}
+
+/*
+ *
+ */
+static struct snd_rawmidi_global_ops snd_virmidi_global_ops = {
+	.dev_register = snd_virmidi_dev_register,
+	.dev_unregister = snd_virmidi_dev_unregister,
+};
+
+/*
+ * free device
+ */
+static void snd_virmidi_free(struct snd_rawmidi *rmidi)
+{
+	struct snd_virmidi_dev *rdev = rmidi->private_data;
+	kfree(rdev);
+}
+
+/*
+ * create a new device
+ *
+ */
+/* exported */
+int snd_virmidi_new(struct snd_card *card, int device, struct snd_rawmidi **rrmidi)
+{
+	struct snd_rawmidi *rmidi;
+	struct snd_virmidi_dev *rdev;
+	int err;
+	
+	*rrmidi = NULL;
+	if ((err = snd_rawmidi_new(card, "VirMidi", device,
+				   16,	/* may be configurable */
+				   16,	/* may be configurable */
+				   &rmidi)) < 0)
+		return err;
+	strcpy(rmidi->name, rmidi->id);
+	rdev = kzalloc(sizeof(*rdev), GFP_KERNEL);
+	if (rdev == NULL) {
+		snd_device_free(card, rmidi);
+		return -ENOMEM;
+	}
+	rdev->card = card;
+	rdev->rmidi = rmidi;
+	rdev->device = device;
+	rdev->client = -1;
+	rwlock_init(&rdev->filelist_lock);
+	INIT_LIST_HEAD(&rdev->filelist);
+	rdev->seq_mode = SNDRV_VIRMIDI_SEQ_DISPATCH;
+	rmidi->private_data = rdev;
+	rmidi->private_free = snd_virmidi_free;
+	rmidi->ops = &snd_virmidi_global_ops;
+	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_virmidi_input_ops);
+	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_virmidi_output_ops);
+	rmidi->info_flags = SNDRV_RAWMIDI_INFO_INPUT |
+			    SNDRV_RAWMIDI_INFO_OUTPUT |
+			    SNDRV_RAWMIDI_INFO_DUPLEX;
+	*rrmidi = rmidi;
+	return 0;
+}
+
+/*
+ *  ENTRY functions
+ */
+
+static int __init alsa_virmidi_init(void)
+{
+	return 0;
+}
+
+static void __exit alsa_virmidi_exit(void)
+{
+}
+
+module_init(alsa_virmidi_init)
+module_exit(alsa_virmidi_exit)
+
+EXPORT_SYMBOL(snd_virmidi_new);
Index: /GPL/branches/alsa-resync1/alsa-kernel/core/sgbuf.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/core/sgbuf.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/core/sgbuf.c	(revision 91)
@@ -0,0 +1,201 @@
+/*
+ * Scatter-Gather buffer
+ *
+ *  Copyright (c) by 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 <sound/info.h>
+#include <sound/memalloc.h>
+
+/* table entries are align to 32 */
+#define SGBUF_TBL_ALIGN		32
+#define sgbuf_align_table(tbl)	((((tbl) + SGBUF_TBL_ALIGN - 1) / SGBUF_TBL_ALIGN) * SGBUF_TBL_ALIGN)
+
+/* set up the page table from the given vmalloc'ed buffer pointer.
+ * return a negative error if the page is out of the pci address mask.
+ */
+static int store_page_tables(struct snd_sg_buf *sgbuf, void *vmaddr, unsigned int pages)
+{
+	unsigned int i;
+
+	sgbuf->pages = 0;
+        DebugInt3();
+	for (i = 0; i < pages; i++) {
+		struct page *page;
+		void *ptr;
+		dma_addr_t addr;
+                ptr = (void*)virt_to_phys((void*)((char*)vmaddr + (i << PAGE_SHIFT)));
+//		ptr = get_vmalloc_addr(vmaddr + (i << PAGE_SHIFT));
+		addr = virt_to_bus(ptr);
+		page = virt_to_page((int)ptr);
+#ifdef DEBUG
+                dprintf(("virt mem: %x, phys: %x, addr: %x, page: %x",(char*)vmaddr, (char*)ptr, addr, page));
+#endif
+		sgbuf->table[i].buf = ptr;
+		sgbuf->table[i].addr = addr;
+		sgbuf->page_table[i] = page;
+		SetPageReserved(page);
+		sgbuf->pages++;
+	}
+	return 0;
+}
+
+/*
+ * shrink to the given pages.
+ * free the unused pages
+ */
+static void sgbuf_shrink(struct snd_sg_buf *sgbuf, int pages)
+{
+        snd_assert(sgbuf, return);
+        if (! sgbuf->table)
+                return;
+        while (sgbuf->pages > pages) {
+                sgbuf->pages--;
+                snd_free_pci_pages(sgbuf->pci, PAGE_SIZE,
+                                   sgbuf->table[sgbuf->pages].buf,
+                                   sgbuf->table[sgbuf->pages].addr);
+        }
+}
+
+/**
+ * snd_malloc_sgbuf_pages - allocate the pages for the PCI SG buffer
+ * @pci: the pci device pointer
+ * @size: the requested buffer size in bytes
+ * @dmab: the buffer record to store
+ *
+ * Initializes the SG-buffer table and allocates the buffer pages
+ * for the given size.
+ * The pages are mapped to the virtually continuous memory.
+ *
+ * This function is usually called from the middle-level functions such as
+ * snd_pcm_lib_malloc_pages().
+ *
+ * Returns the mapped virtual address of the buffer if allocation was
+ * successful, or NULL at error.
+ */
+void *snd_malloc_sgbuf_pages(struct pci_dev *pci,
+                             size_t size,
+                             struct snd_dma_buffer *dmab,
+                             size_t *res_size)
+{
+	struct snd_sg_buf *sgbuf;
+        unsigned int pages;
+        void *ptr;
+        dma_addr_t addr;
+
+#ifdef DEBUG
+        dprintf(("snd_malloc_sgbuf_pages. size %x",size));
+#endif
+
+	dmab->area = NULL;
+	dmab->addr = 0;
+	dmab->private_data = sgbuf = kmalloc(sizeof(*sgbuf), GFP_KERNEL);
+        if (! sgbuf)
+        {
+#ifdef DEBUG
+            dprintf(("snd_malloc_sgbuf_pages failed: error allocating sgbuf"));
+#endif
+            return NULL;
+        }
+	memset(sgbuf, 0, sizeof(*sgbuf));
+	sgbuf->pci = pci;
+	pages = snd_sgbuf_aligned_pages(size);
+	sgbuf->tblsize = sgbuf_align_table(pages);
+	sgbuf->table = kmalloc(sizeof(*sgbuf->table) * sgbuf->tblsize, GFP_KERNEL);
+        if (! sgbuf->table)
+        {
+#ifdef DEBUG
+            dprintf(("snd_malloc_sgbuf_pages failed: error allocating sgbuf->table"));
+#endif
+            return NULL;
+        }
+	memset(sgbuf->table, 0, sizeof(*sgbuf->table) * sgbuf->tblsize);
+	sgbuf->page_table = kmalloc(sizeof(*sgbuf->page_table) * sgbuf->tblsize, GFP_KERNEL);
+	if (! sgbuf->page_table)
+        {
+#ifdef DEBUG
+            dprintf(("snd_malloc_sgbuf_pages failed: error allocating sgbuf->page_table"));
+#endif
+            return NULL;
+        }
+        memset(sgbuf->page_table, 0, sizeof(*sgbuf->page_table) * sgbuf->tblsize);
+
+#ifdef DEBUG
+            dprintf(("allocating %d pages",pages));
+#endif
+            ptr = snd_malloc_pci_pages(sgbuf->pci, size, &addr);
+            if (! ptr)
+                goto _failed;
+
+            /* allocate each page */
+            while (sgbuf->pages < pages) {
+                mem_map_t *page;
+
+                sgbuf->table[sgbuf->pages].buf = (char*)ptr + PAGE_SIZE*sgbuf->pages;
+                sgbuf->table[sgbuf->pages].addr = addr + PAGE_SIZE*sgbuf->pages;
+                page = (mem_map_t *)virt_to_page((int)sgbuf->table[sgbuf->pages].buf);
+                sgbuf->page_table[sgbuf->pages] = page;
+                SetPageReserved(page);
+                sgbuf->pages++;
+            }
+
+            memset(ptr,0,size);
+
+            dmab->area = ptr;
+            sgbuf->size = size;
+            return dmab->area;
+
+        _failed:
+#ifdef DEBUG
+            dprintf(("snd_malloc_sgbuf_pages failed"));
+#endif
+
+	snd_free_sgbuf_pages(dmab); /* free the table */
+	return NULL;
+}
+
+
+/**
+ * snd_free_sgbuf_pages - free the sg buffer
+ * @dmab: buffer record
+ *
+ * Releases the pages and the SG-buffer table.
+ *
+ * This function is called usually from the middle-level function
+ * such as snd_pcm_lib_free_pages().
+ *
+ * Returns zero if successful, or a negative error code on failure.
+ */
+int snd_free_sgbuf_pages(struct snd_dma_buffer *dmab)
+{
+	struct snd_sg_buf *sgbuf = dmab->private_data;
+
+        sgbuf_shrink(sgbuf, 0);
+	if (sgbuf->table)
+		kfree(sgbuf->table);
+	sgbuf->table = NULL;
+	if (sgbuf->page_table)
+		kfree(sgbuf->page_table);
+	kfree(sgbuf);
+	dmab->private_data = NULL;
+	
+	return 0;
+}
+
+
Index: /GPL/branches/alsa-resync1/alsa-kernel/core/sound.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/core/sound.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/core/sound.c	(revision 91)
@@ -0,0 +1,557 @@
+/*
+ *  Advanced Linux Sound Architecture
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#define SNDRV_MAIN_OBJECT_FILE
+#include <sound/driver.h>
+#include <sound/minors.h>
+#include <sound/info.h>
+#include <sound/version.h>
+#include <sound/control.h>
+#include <sound/initval.h>
+#ifdef CONFIG_KMOD
+#include <linux/kmod.h>
+#endif
+#ifdef CONFIG_DEVFS_FS
+#include <linux/devfs_fs_kernel.h>
+#endif
+
+#define SNDRV_OS_MINORS 256
+
+static int major = CONFIG_SND_MAJOR;
+int snd_major;
+static int cards_limit = 1;
+#ifdef CONFIG_DEVFS_FS
+static int device_mode = S_IFCHR | S_IRUGO | S_IWUGO;
+#endif
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Advanced Linux Sound Architecture driver for soundcards.");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_SUPPORTED_DEVICE("sound");
+MODULE_PARM(major, "i");
+MODULE_PARM_DESC(major, "Major # for sound driver.");
+MODULE_PARM_SYNTAX(major, "default:116,skill:devel");
+MODULE_PARM(cards_limit, "i");
+MODULE_PARM_DESC(cards_limit, "Count of auto-loadable soundcards.");
+MODULE_PARM_SYNTAX(cards_limit, "default:8,skill:advanced");
+#ifdef CONFIG_DEVFS_FS
+MODULE_PARM(device_mode, "i");
+MODULE_PARM_DESC(device_mode, "Device file permission mask for devfs.");
+MODULE_PARM_SYNTAX(device_mode, "default:0666,base:8");
+#endif
+
+/* this one holds the actual max. card number currently available.
+ * as default, it's identical with cards_limit option.  when more
+ * modules are loaded manually, this limit number increases, too.
+ */
+int snd_ecards_limit;
+
+static struct list_head snd_minors_hash[SNDRV_CARDS];
+
+static DECLARE_MUTEX(sound_mutex);
+
+#ifdef CONFIG_DEVFS_FS
+extern struct class_simple *sound_class;
+#endif
+
+#ifdef CONFIG_KMOD
+
+/**
+ * snd_request_card - try to load the card module
+ * @card: the card number
+ *
+ * Tries to load the module "snd-card-X" for the given card number
+ * via KMOD.  Returns immediately if already loaded.
+ */
+void snd_request_card(int card)
+{
+    int locked;
+
+    read_lock(&snd_card_rwlock);
+    locked = snd_cards_lock & (1 << card);
+    read_unlock(&snd_card_rwlock);
+    if (locked)
+        return;
+    if (card < 0 || card >= cards_limit)
+        return;
+    request_module("snd-card-%i", card);
+}
+
+static void snd_request_other(int minor)
+{
+    char *str;
+
+    switch (minor) {
+    case SNDRV_MINOR_SEQUENCER:	str = "snd-seq";	break;
+    case SNDRV_MINOR_TIMER:		str = "snd-timer";	break;
+    default:			return;
+    }
+    request_module(str);
+}
+
+#endif				/* request_module support */
+
+static struct snd_minor *snd_minor_search(int minor)
+{
+    struct list_head *list;
+    snd_minor_t *mptr;
+
+    list_for_each(list, &snd_minors_hash[SNDRV_MINOR_CARD(minor)]) {
+        mptr = list_entry(list, struct snd_minor, list);
+        if (mptr->number == minor)
+            return mptr;
+    }
+    return NULL;
+}
+
+static int snd_open(struct inode *inode, struct file *file)
+{
+    int minor = MINOR(inode->i_rdev);
+    int card = SNDRV_MINOR_CARD(minor);
+    int dev = SNDRV_MINOR_DEVICE(minor);
+    snd_minor_t *mptr = NULL;
+    struct file_operations *old_fops;
+    int err = 0;
+
+    if (dev != SNDRV_MINOR_SEQUENCER && dev != SNDRV_MINOR_TIMER) {
+        if (snd_cards[card] == NULL) {
+#ifdef CONFIG_KMOD
+            snd_request_card(card);
+            if (snd_cards[card] == NULL)
+#endif
+                return -ENODEV;
+        }
+    } else {
+#ifdef CONFIG_KMOD
+        if ((mptr = snd_minor_search(minor)) == NULL)
+            snd_request_other(minor);
+#endif
+    }
+    if (mptr == NULL && (mptr = snd_minor_search(minor)) == NULL)
+        return -ENODEV;
+    old_fops = file->f_op;
+    //        printk("snd_open: mptr: %x, card: %i, dev: %i, minor: %i\n", mptr, card, dev, minor);
+    file->f_op = fops_get(mptr->f_ops);
+    if (file->f_op->open)
+        err = file->f_op->open(inode, file);
+    if (err) {
+        fops_put(file->f_op);
+        file->f_op = fops_get(old_fops);
+    }
+    fops_put(old_fops);
+    return err;
+}
+
+#ifdef TARGET_OS2
+struct file_operations snd_fops =
+{
+    0,0,0,0,0,0,0,
+    snd_open,0,0,0,0,0,0,0
+};
+#else
+struct file_operations snd_fops =
+{
+#ifdef LINUX_2_3
+owner:		THIS_MODULE,
+#endif
+    open:		snd_open
+};
+#endif
+
+static int snd_kernel_minor(int type, struct snd_card * card, int dev)
+{
+    int minor;
+
+    switch (type) {
+    case SNDRV_DEVICE_TYPE_SEQUENCER:
+    case SNDRV_DEVICE_TYPE_TIMER:
+        minor = type;
+        break;
+    case SNDRV_DEVICE_TYPE_CONTROL:
+        snd_assert(card != NULL, return -EINVAL);
+        minor = SNDRV_MINOR(card->number, type);
+        break;
+    case SNDRV_DEVICE_TYPE_HWDEP:
+    case SNDRV_DEVICE_TYPE_RAWMIDI:
+    case SNDRV_DEVICE_TYPE_PCM_PLAYBACK:
+    case SNDRV_DEVICE_TYPE_PCM_CAPTURE:
+        snd_assert(card != NULL, return -EINVAL);
+        minor = SNDRV_MINOR(card->number, type + dev);
+        break;
+    default:
+        return -EINVAL;
+    }
+    snd_assert(minor >= 0 && minor < SNDRV_OS_MINORS, return -EINVAL);
+    return minor;
+}
+
+/**
+ * snd_register_device - Register the ALSA device file for the card
+ * @type: the device type, SNDRV_DEVICE_TYPE_XXX
+ * @card: the card instance
+ * @dev: the device index
+ * @reg: the snd_minor_t record
+ * @name: the device file name
+ *
+ * Registers an ALSA device file for the given card.
+ * The operators have to be set in reg parameter.
+ *
+ * Retrurns zero if successful, or a negative error code on failure.
+ */
+int snd_register_device(int type, struct snd_card * card, int dev, snd_minor_t * reg, const char *name)
+{
+    int minor = snd_kernel_minor(type, card, dev);
+    snd_minor_t *preg;
+    struct device *device = NULL;
+
+    if (minor < 0)
+        return minor;
+    snd_assert(name, return -EINVAL);
+    preg = (snd_minor_t *)kmalloc(sizeof(snd_minor_t) + strlen(name) + 1, GFP_KERNEL);
+    if (preg == NULL)
+        return -ENOMEM;
+    *preg = *reg;
+    preg->number = minor;
+    preg->device = dev;
+    strcpy(preg->name, name);
+    down(&sound_mutex);
+    if (snd_minor_search(minor)) {
+        up(&sound_mutex);
+        kfree(preg);
+        return -EBUSY;
+    }
+    list_add_tail(&preg->list, &snd_minors_hash[SNDRV_MINOR_CARD(minor)]);
+#ifdef CONFIG_DEVFS_FS
+    if (strncmp(name, "controlC", 8) || card->number >= cards_limit) {
+        devfs_mk_cdev(MKDEV(major, minor), S_IFCHR | device_mode, "snd/%s", name);
+        if (card)
+            device = card->dev;
+        class_simple_device_add(sound_class, MKDEV(major, minor), device, name);
+    }
+
+#endif
+    up(&sound_mutex);
+    return 0;
+}
+
+/**
+ * snd_unregister_device - unregister the device on the given card
+ * @type: the device type, SNDRV_DEVICE_TYPE_XXX
+ * @card: the card instance
+ * @dev: the device index
+ *
+ * Unregisters the device file already registered via
+ * snd_register_device().
+ *
+ * Returns zero if sucecessful, or a negative error code on failure
+ */
+int snd_unregister_device(int type, struct snd_card * card, int dev)
+{
+    int minor = snd_kernel_minor(type, card, dev);
+    snd_minor_t *mptr;
+
+    if (minor < 0)
+        return minor;
+    down(&sound_mutex);
+    if ((mptr = snd_minor_search(minor)) == NULL) {
+        up(&sound_mutex);
+        return -EINVAL;
+    }
+#ifdef CONFIG_DEVFS_FS
+    if (strncmp(name, "controlC", 8) || card->number >= cards_limit) {
+        devfs_remove("snd/%s", mptr->name);
+        class_simple_device_remove(MKDEV(major, minor));
+    }
+#endif
+    list_del(&mptr->list);
+    up(&sound_mutex);
+    kfree(mptr);
+    return 0;
+}
+
+/*
+ *  INFO PART
+ */
+
+static snd_info_entry_t *snd_minor_info_entry = NULL;
+
+static void snd_minor_info_read(snd_info_entry_t *entry, struct snd_info_buffer * buffer)
+{
+    int card, device;
+    struct list_head *list;
+    snd_minor_t *mptr;
+
+    down(&sound_mutex);
+    for (card = 0; card < SNDRV_CARDS; card++) {
+        list_for_each(list, &snd_minors_hash[card]) {
+            mptr = list_entry(list, snd_minor_t, list);
+            if (SNDRV_MINOR_DEVICE(mptr->number) != SNDRV_MINOR_SEQUENCER) {
+                if ((device = mptr->device) >= 0)
+                    snd_iprintf(buffer, "%3i: [%i-%2i]: %s\n", mptr->number, card, device, mptr->comment);
+                else
+                    snd_iprintf(buffer, "%3i: [%i]   : %s\n", mptr->number, card, mptr->comment);
+            } else {
+                snd_iprintf(buffer, "%3i:       : %s\n", mptr->number, mptr->comment);
+            }
+        }
+    }
+    up(&sound_mutex);
+}
+
+int __init snd_minor_info_init(void)
+{
+    snd_info_entry_t *entry;
+
+    entry = snd_info_create_module_entry(THIS_MODULE, "devices", NULL);
+    if (entry) {
+        entry->c.text.read_size = PAGE_SIZE;
+        entry->c.text.read = snd_minor_info_read;
+        if (snd_info_register(entry) < 0) {
+            snd_info_free_entry(entry);
+            entry = NULL;
+        }
+    }
+    snd_minor_info_entry = entry;
+    return 0;
+}
+
+int __exit snd_minor_info_done(void)
+{
+    if (snd_minor_info_entry)
+        snd_info_unregister(snd_minor_info_entry);
+    return 0;
+}
+
+/*
+ *  INIT PART
+ */
+
+static int __init alsa_sound_init(void)
+{
+#ifdef CONFIG_DEVFS_FS
+    short controlnum;
+#endif
+#ifdef CONFIG_SND_OSSEMUL
+    int err;
+#endif
+    int card;
+
+    snd_major = major;
+    snd_ecards_limit = cards_limit;
+    for (card = 0; card < SNDRV_CARDS; card++)
+        INIT_LIST_HEAD(&snd_minors_hash[card]);
+#ifdef CONFIG_SND_OSSEMUL
+    if ((err = snd_oss_init_module()) < 0)
+        return err;
+#endif
+
+#ifdef CONFIG_DEVFS_FS
+    devfs_mk_dir("snd");
+#endif
+    if (register_chrdev(major, "alsa", &snd_fops)) {
+        snd_printk(KERN_ERR "unable to register native major device number %d\n", major);
+#ifdef CONFIG_DEVFS_FS
+        devfs_remove("snd");
+#endif
+        return -EIO;
+    }
+#ifdef CONFIG_SND_DEBUG_MEMORY
+    snd_memory_init();
+#endif
+    if (snd_info_init() < 0) {
+        snd_printk(KERN_ERR "unable to init info\n");
+#ifdef CONFIG_SND_DEBUG_MEMORY
+        snd_memory_done();
+#endif
+        unregister_chrdev(major, "alsa");
+#ifdef CONFIG_DEVFS_FS
+        devfs_remove("snd");
+#endif
+        return -ENOMEM;
+    }
+#ifdef CONFIG_SND_OSSEMUL
+    snd_info_minor_register();
+#endif
+#ifdef CONFIG_DEVFS_FS
+    for (controlnum = 0; controlnum < cards_limit; controlnum++) {
+        devfs_mk_cdev(MKDEV(major, controlnum<<5), S_IFCHR | device_mode, "snd/controlC%d", controlnum);
+        class_simple_device_add(sound_class, MKDEV(major, controlnum<<5), NULL, "controlC%d", controlnum);
+    }
+#endif
+#ifndef MODULE
+    printk(KERN_INFO "Advanced Linux Sound Architecture Driver Version " CONFIG_SND_VERSION CONFIG_SND_DATE ".\n");
+#endif
+    return 0;
+}
+
+static void __exit alsa_sound_exit(void)
+{
+#ifdef CONFIG_DEVFS_FS
+    short controlnum;
+
+    for (controlnum = 0; controlnum < cards_limit; controlnum++) {
+        devfs_remove("snd/controlC%d", controlnum);
+        class_simple_device_remove(MKDEV(major, controlnum<<5));
+    }
+#endif
+
+#ifdef CONFIG_SND_OSSEMUL
+    snd_info_minor_unregister();
+#endif
+    snd_info_done();
+#ifdef CONFIG_SND_DEBUG_MEMORY
+    snd_memory_done();
+#endif
+    if (unregister_chrdev(major, "alsa") != 0)
+        snd_printk(KERN_ERR "unable to unregister major device number %d\n", major);
+#ifdef CONFIG_DEVFS_FS
+    devfs_remove("snd");
+#endif
+}
+
+module_init(alsa_sound_init)
+module_exit(alsa_sound_exit)
+
+#ifndef MODULE
+
+/* format is: snd=major,cards_limit[,device_mode] */
+
+static int __init alsa_sound_setup(char *str)
+{
+    (void)(get_option(&str,&major) == 2 &&
+           get_option(&str,&cards_limit) == 2);
+#ifdef CONFIG_DEVFS_FS
+    (void)(get_option(&str,&device_mode) == 2);
+#endif
+    return 1;
+}
+
+__setup("snd=", alsa_sound_setup);
+
+#endif /* ifndef MODULE */
+
+/* sound.c */
+EXPORT_SYMBOL(snd_major);
+EXPORT_SYMBOL(snd_ecards_limit);
+#if defined(CONFIG_KMOD)
+EXPORT_SYMBOL(snd_request_card);
+#endif
+EXPORT_SYMBOL(snd_register_device);
+EXPORT_SYMBOL(snd_unregister_device);
+#if defined(CONFIG_SND_OSSEMUL)
+EXPORT_SYMBOL(snd_register_oss_device);
+EXPORT_SYMBOL(snd_unregister_oss_device);
+#endif
+/* memory.c */
+#ifdef CONFIG_SND_DEBUG_MEMORY
+EXPORT_SYMBOL(snd_hidden_kmalloc);
+EXPORT_SYMBOL(snd_hidden_kcalloc);
+EXPORT_SYMBOL(snd_hidden_kfree);
+EXPORT_SYMBOL(snd_hidden_vmalloc);
+EXPORT_SYMBOL(snd_hidden_vfree);
+#endif
+EXPORT_SYMBOL(snd_kmalloc_strdup);
+EXPORT_SYMBOL(copy_to_user_fromio);
+EXPORT_SYMBOL(copy_from_user_toio);
+/* init.c */
+EXPORT_SYMBOL(snd_cards_count);
+EXPORT_SYMBOL(snd_cards);
+#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
+EXPORT_SYMBOL(snd_mixer_oss_notify_callback);
+#endif
+EXPORT_SYMBOL(snd_card_new);
+EXPORT_SYMBOL(snd_card_disconnect);
+EXPORT_SYMBOL(snd_card_free);
+EXPORT_SYMBOL(snd_card_free_in_thread);
+EXPORT_SYMBOL(snd_card_register);
+EXPORT_SYMBOL(snd_component_add);
+EXPORT_SYMBOL(snd_card_file_add);
+EXPORT_SYMBOL(snd_card_file_remove);
+#ifdef CONFIG_PM
+EXPORT_SYMBOL(snd_power_wait);
+EXPORT_SYMBOL(snd_card_set_pm_callback);
+#ifdef CONFIG_PCI
+EXPORT_SYMBOL(snd_card_pci_suspend);
+EXPORT_SYMBOL(snd_card_pci_resume);
+#endif
+#ifdef CONFIG_ISA
+EXPORT_SYMBOL(snd_card_set_isa_pm_callback);
+#endif
+#endif
+/* device.c */
+EXPORT_SYMBOL(snd_device_new);
+EXPORT_SYMBOL(snd_device_register);
+EXPORT_SYMBOL(snd_device_free);
+EXPORT_SYMBOL(snd_device_free_all);
+/* isadma.c */
+#ifdef CONFIG_ISA
+EXPORT_SYMBOL(snd_dma_program);
+EXPORT_SYMBOL(snd_dma_disable);
+EXPORT_SYMBOL(snd_dma_pointer);
+#endif
+/* info.c */
+#ifdef CONFIG_PROC_FS
+EXPORT_SYMBOL(snd_seq_root);
+EXPORT_SYMBOL(snd_create_proc_entry);
+EXPORT_SYMBOL(snd_remove_proc_entry);
+EXPORT_SYMBOL(snd_iprintf);
+EXPORT_SYMBOL(snd_info_get_line);
+EXPORT_SYMBOL(snd_info_get_str);
+EXPORT_SYMBOL(snd_info_create_module_entry);
+EXPORT_SYMBOL(snd_info_create_card_entry);
+EXPORT_SYMBOL(snd_info_free_entry);
+EXPORT_SYMBOL(snd_info_register);
+EXPORT_SYMBOL(snd_info_unregister);
+EXPORT_SYMBOL(snd_card_proc_new);
+#endif
+/* info_oss.c */
+#if defined(CONFIG_SND_OSSEMUL) && defined(CONFIG_PROC_FS)
+EXPORT_SYMBOL(snd_oss_info_register);
+#endif
+/* control.c */
+EXPORT_SYMBOL(snd_ctl_new);
+EXPORT_SYMBOL(snd_ctl_new1);
+EXPORT_SYMBOL(snd_ctl_free_one);
+EXPORT_SYMBOL(snd_ctl_add);
+EXPORT_SYMBOL(snd_ctl_remove);
+EXPORT_SYMBOL(snd_ctl_remove_id);
+EXPORT_SYMBOL(snd_ctl_rename_id);
+EXPORT_SYMBOL(snd_ctl_find_numid);
+EXPORT_SYMBOL(snd_ctl_find_id);
+EXPORT_SYMBOL(snd_ctl_notify);
+EXPORT_SYMBOL(snd_ctl_register_ioctl);
+EXPORT_SYMBOL(snd_ctl_unregister_ioctl);
+/* misc.c */
+EXPORT_SYMBOL(snd_task_name);
+#ifdef CONFIG_SND_VERBOSE_PRINTK
+EXPORT_SYMBOL(snd_verbose_printk);
+#endif
+#if defined(CONFIG_SND_DEBUG) && defined(CONFIG_SND_VERBOSE_PRINTK)
+EXPORT_SYMBOL(snd_verbose_printd);
+#endif
+/* wrappers */
+#ifdef CONFIG_SND_DEBUG_MEMORY
+EXPORT_SYMBOL(snd_wrapper_kmalloc);
+EXPORT_SYMBOL(snd_wrapper_kfree);
+EXPORT_SYMBOL(snd_wrapper_vmalloc);
+EXPORT_SYMBOL(snd_wrapper_vfree);
+#endif
Index: /GPL/branches/alsa-resync1/alsa-kernel/core/timer.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/core/timer.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/core/timer.c	(revision 91)
@@ -0,0 +1,1930 @@
+/*
+ *  Timers abstract layer
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#define SNDRV_MAIN_OBJECT_FILE
+#include <sound/driver.h>
+#include <sound/timer.h>
+#include <sound/control.h>
+#include <sound/info.h>
+#include <sound/minors.h>
+#include <sound/initval.h>
+#ifdef CONFIG_KMOD
+#include <linux/kmod.h>
+#endif
+#ifdef CONFIG_KERNELD
+#include <linux/kerneld.h>
+#endif
+
+#if !defined(CONFIG_SND_RTCTIMER) && !defined(CONFIG_SND_RTCTIMER_MODULE)
+#define DEFAULT_TIMER_LIMIT 1
+#else
+#define DEFAULT_TIMER_LIMIT 2
+#endif
+
+static int timer_limit = DEFAULT_TIMER_LIMIT;
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>, Takashi Iwai <tiwai@suse.de>");
+MODULE_DESCRIPTION("ALSA timer interface");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_PARM(timer_limit, "i");
+MODULE_PARM_DESC(timer_limit, "Maximum global timers in system.");
+
+typedef struct {
+    snd_timer_instance_t *timeri;
+    int tread;			/* enhanced read with timestamps and events */
+    unsigned long ticks;
+    unsigned long overrun;
+    int qhead;
+    int qtail;
+    int qused;
+    int queue_size;
+    snd_timer_read_t *queue;
+    snd_timer_tread_t *tqueue;
+    spinlock_t qlock;
+    unsigned long last_resolution;
+    unsigned int filter;
+    struct timespec tstamp;		/* trigger tstamp */
+    wait_queue_head_t qchange_sleep;
+    struct fasync_struct *fasync;
+} snd_timer_user_t;
+
+/* list of timers */
+static LIST_HEAD(snd_timer_list);
+
+/* list of slave instances */
+static LIST_HEAD(snd_timer_slave_list);
+
+/* lock for slave active lists */
+static spinlock_t slave_active_lock = SPIN_LOCK_UNLOCKED;
+
+static DECLARE_MUTEX(register_mutex);
+
+static int snd_timer_free(snd_timer_t *timer);
+static int snd_timer_dev_free(snd_device_t *device);
+static int snd_timer_dev_register(snd_device_t *device);
+static int snd_timer_dev_unregister(snd_device_t *device);
+
+static void snd_timer_reschedule(snd_timer_t * timer, unsigned long ticks_left);
+
+/*
+ * create a timer instance with the given owner string.
+ * when timer is not NULL, increments the module counter
+ */
+static snd_timer_instance_t *snd_timer_instance_new(char *owner, snd_timer_t *timer)
+{
+    snd_timer_instance_t *timeri;
+    timeri = kcalloc(1, sizeof(*timeri), GFP_KERNEL);
+    if (timeri == NULL)
+        return NULL;
+    timeri->owner = snd_kmalloc_strdup(owner, GFP_KERNEL);
+    if (! timeri->owner) {
+        kfree(timeri);
+        return NULL;
+    }
+    INIT_LIST_HEAD(&timeri->open_list);
+    INIT_LIST_HEAD(&timeri->active_list);
+    INIT_LIST_HEAD(&timeri->ack_list);
+    INIT_LIST_HEAD(&timeri->slave_list_head);
+    INIT_LIST_HEAD(&timeri->slave_active_head);
+
+    timeri->timer = timer;
+    if (timer && timer->card && !try_module_get(timer->card->module)) {
+        kfree(timeri->owner);
+        kfree(timeri);
+        return NULL;
+    }
+
+    return timeri;
+}
+
+/*
+ * find a timer instance from the given timer id
+ */
+static snd_timer_t *snd_timer_find(snd_timer_id_t *tid)
+{
+    snd_timer_t *timer = NULL;
+    struct list_head *p;
+
+    list_for_each(p, &snd_timer_list) {
+        timer = (snd_timer_t *)list_entry(p, snd_timer_t, device_list);
+
+        if (timer->tmr_class != tid->dev_class)
+            continue;
+        if ((timer->tmr_class == SNDRV_TIMER_CLASS_CARD ||
+             timer->tmr_class == SNDRV_TIMER_CLASS_PCM) &&
+            (timer->card == NULL ||
+             timer->card->number != tid->card))
+            continue;
+        if (timer->tmr_device != tid->device)
+            continue;
+        if (timer->tmr_subdevice != tid->subdevice)
+            continue;
+        return timer;
+    }
+    return NULL;
+}
+
+#ifdef CONFIG_KMOD
+
+static void snd_timer_request(snd_timer_id_t *tid)
+{
+    switch (tid->dev_class) {
+    case SNDRV_TIMER_CLASS_GLOBAL:
+        if (tid->device < timer_limit)
+            request_module("snd-timer-%i", tid->device);
+        break;
+    case SNDRV_TIMER_CLASS_CARD:
+    case SNDRV_TIMER_CLASS_PCM:
+        if (tid->card < snd_ecards_limit)
+            request_module("snd-card-%i", tid->card);
+        break;
+    default:
+        break;
+    }
+}
+
+#endif
+
+/*
+ * look for a master instance matching with the slave id of the given slave.
+ * when found, relink the open_link of the slave.
+ *
+ * call this with register_mutex down.
+ */
+static void snd_timer_check_slave(snd_timer_instance_t *slave)
+{
+    snd_timer_t *timer;
+    snd_timer_instance_t *master;
+    struct list_head *p, *q;
+
+    /* FIXME: it's really dumb to look up all entries.. */
+    list_for_each(p, &snd_timer_list) {
+        timer = (snd_timer_t *)list_entry(p, snd_timer_t, device_list);
+        list_for_each(q, &timer->open_list_head) {
+            master = (snd_timer_instance_t *)list_entry(q, snd_timer_instance_t, open_list);
+            if (slave->slave_class == master->slave_class &&
+                slave->slave_id == master->slave_id) {
+                list_del(&slave->open_list);
+                list_add_tail(&slave->open_list, &master->slave_list_head);
+                spin_lock_irq(&slave_active_lock);
+                slave->master = master;
+                slave->timer = master->timer;
+                spin_unlock_irq(&slave_active_lock);
+                return;
+            }
+        }
+    }
+}
+
+/*
+ * look for slave instances matching with the slave id of the given master.
+ * when found, relink the open_link of slaves.
+ *
+ * call this with register_mutex down.
+ */
+static void snd_timer_check_master(snd_timer_instance_t *master)
+{
+    snd_timer_instance_t *slave;
+    struct list_head *p, *n;
+
+    /* check all pending slaves */
+    list_for_each_safe(p, n, &snd_timer_slave_list) {
+        slave = (snd_timer_instance_t *)list_entry(p, snd_timer_instance_t, open_list);
+        if (slave->slave_class == master->slave_class &&
+            slave->slave_id == master->slave_id) {
+            list_del(p);
+            list_add_tail(p, &master->slave_list_head);
+            spin_lock_irq(&slave_active_lock);
+            slave->master = master;
+            slave->timer = master->timer;
+            if (slave->flags & SNDRV_TIMER_IFLG_RUNNING)
+                list_add_tail(&slave->active_list, &master->slave_active_head);
+            spin_unlock_irq(&slave_active_lock);
+        }
+    }
+}
+
+/*
+ * open a timer instance
+ * when opening a master, the slave id must be here given.
+ */
+int snd_timer_open(snd_timer_instance_t **ti,
+                   char *owner, snd_timer_id_t *tid,
+                   unsigned int slave_id)
+{
+    snd_timer_t *timer;
+    snd_timer_instance_t *timeri = NULL;
+
+    if (tid->dev_class == SNDRV_TIMER_CLASS_SLAVE) {
+        /* open a slave instance */
+        if (tid->dev_sclass <= SNDRV_TIMER_SCLASS_NONE ||
+            tid->dev_sclass > SNDRV_TIMER_SCLASS_OSS_SEQUENCER) {
+            snd_printd("invalid slave class %i\n", tid->dev_sclass);
+            return -EINVAL;
+        }
+        down(&register_mutex);
+        timeri = snd_timer_instance_new(owner, NULL);
+        if (!timeri) {
+            up(&register_mutex);
+            return -ENOMEM;
+        }
+        timeri->slave_class = tid->dev_sclass;
+        timeri->slave_id = tid->device;
+        timeri->flags |= SNDRV_TIMER_IFLG_SLAVE;
+        list_add_tail(&timeri->open_list, &snd_timer_slave_list);
+        snd_timer_check_slave(timeri);
+        up(&register_mutex);
+        *ti = timeri;
+        return 0;
+    }
+
+    /* open a master instance */
+    down(&register_mutex);
+    timer = snd_timer_find(tid);
+#ifdef CONFIG_KMOD
+    if (timer == NULL) {
+        up(&register_mutex);
+        snd_timer_request(tid);
+        down(&register_mutex);
+        timer = snd_timer_find(tid);
+    }
+#endif
+    if (!timer) {
+        up(&register_mutex);
+        return -ENODEV;
+    }
+    if (!list_empty(&timer->open_list_head)) {
+        timeri = list_entry(timer->open_list_head.next,
+                            snd_timer_instance_t, open_list);
+            if (timeri->flags & SNDRV_TIMER_IFLG_EXCLUSIVE) {
+                up(&register_mutex);
+                return -EBUSY;
+            }
+        }
+    timeri = snd_timer_instance_new(owner, timer);
+    if (!timeri) {
+        up(&register_mutex);
+        return -ENOMEM;
+    }
+            timeri->slave_class = tid->dev_sclass;
+            timeri->slave_id = slave_id;
+            if (list_empty(&timer->open_list_head) && timer->hw.open)
+                timer->hw.open(timer);
+            list_add_tail(&timeri->open_list, &timer->open_list_head);
+            snd_timer_check_master(timeri);
+    up(&register_mutex);
+    *ti = timeri;
+    return 0;
+}
+
+static int _snd_timer_stop(struct snd_timer_instance *timeri,
+			   int keep_flag, int event);
+
+/*
+ * close a timer instance
+ */
+int snd_timer_close(snd_timer_instance_t * timeri)
+{
+    snd_timer_t *timer = NULL;
+    struct list_head *p, *n;
+    snd_timer_instance_t *slave;
+
+    snd_assert(timeri != NULL, return -ENXIO);
+
+    /* force to stop the timer */
+    snd_timer_stop(timeri);
+
+    if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) {
+        /* wait, until the active callback is finished */
+        spin_lock_irq(&slave_active_lock);
+        while (timeri->flags & SNDRV_TIMER_IFLG_CALLBACK) {
+            spin_unlock_irq(&slave_active_lock);
+            udelay(10);
+            spin_lock_irq(&slave_active_lock);
+        }
+        spin_unlock_irq(&slave_active_lock);
+        down(&register_mutex);
+        list_del(&timeri->open_list);
+        up(&register_mutex);
+    } else {
+        timer = timeri->timer;
+        /* wait, until the active callback is finished */
+        spin_lock_irq(&timer->lock);
+        while (timeri->flags & SNDRV_TIMER_IFLG_CALLBACK) {
+            spin_unlock_irq(&timer->lock);
+            udelay(10);
+            spin_lock_irq(&timer->lock);
+        }
+        spin_unlock_irq(&timer->lock);
+        down(&register_mutex);
+        list_del(&timeri->open_list);
+        if (timer && list_empty(&timer->open_list_head) && timer->hw.close)
+            timer->hw.close(timer);
+        /* remove slave links */
+        list_for_each_safe(p, n, &timeri->slave_list_head) {
+            slave = (snd_timer_instance_t *)list_entry(p, snd_timer_instance_t, open_list);
+            spin_lock_irq(&slave_active_lock);
+            _snd_timer_stop(slave, 1, SNDRV_TIMER_EVENT_RESOLUTION);
+            list_del(p);
+            list_add_tail(p, &snd_timer_slave_list);
+            slave->master = NULL;
+            slave->timer = NULL;
+            spin_unlock_irq(&slave_active_lock);
+        }
+        up(&register_mutex);
+    }
+    if (timeri->private_free)
+        timeri->private_free(timeri);
+    if (timeri->owner)
+        kfree(timeri->owner);
+    kfree(timeri);
+    if (timer && timer->card)
+        module_put(timer->card->module);
+    return 0;
+}
+
+unsigned long snd_timer_resolution(snd_timer_instance_t * timeri)
+{
+    snd_timer_t * timer;
+
+    if (timeri == NULL)
+        return 0;
+    if ((timer = timeri->timer) != NULL) {
+        if (timer->hw.c_resolution)
+            return timer->hw.c_resolution(timer);
+        return timer->hw.resolution;
+    }
+    return 0;
+}
+
+static void snd_timer_notify1(struct snd_timer_instance *ti, int event)
+{
+    snd_timer_t *timer;
+    unsigned long flags;
+    unsigned long resolution = 0;
+    snd_timer_instance_t *ts;
+    struct list_head *n;
+    struct timespec tstamp;
+
+    snd_timestamp_now(&tstamp, 1);
+    snd_assert(event >= SNDRV_TIMER_EVENT_START && event <= SNDRV_TIMER_EVENT_PAUSE, return);
+    if (event == SNDRV_TIMER_EVENT_START || event == SNDRV_TIMER_EVENT_CONTINUE)
+        resolution = snd_timer_resolution(ti);
+    if (ti->ccallback)
+        ti->ccallback(ti, SNDRV_TIMER_EVENT_START, &tstamp, resolution);
+    if (ti->flags & SNDRV_TIMER_IFLG_SLAVE)
+        return;
+    timer = ti->timer;
+    if (timer == NULL)
+        return;
+    if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE)
+        return;
+    spin_lock_irqsave(&timer->lock, flags);
+    list_for_each(n, &ti->slave_active_head) {
+        ts = (snd_timer_instance_t *)list_entry(n, snd_timer_instance_t, active_list);
+        if (ts->ccallback)
+            ts->ccallback(ti, event + 100, &tstamp, resolution);
+    }
+    spin_unlock_irqrestore(&timer->lock, flags);
+}
+
+static int snd_timer_start1(snd_timer_t *timer, snd_timer_instance_t *timeri, unsigned long sticks)
+{
+    list_del(&timeri->active_list);
+    list_add_tail(&timeri->active_list, &timer->active_list_head);
+    if (timer->running) {
+        if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE)
+            goto __start_now;
+        timer->flags |= SNDRV_TIMER_FLG_RESCHED;
+        timeri->flags |= SNDRV_TIMER_IFLG_START;
+        return 1;	/* delayed start */
+    } else {
+        timer->sticks = sticks;
+        timer->hw.start(timer);
+    __start_now:
+        timer->running++;
+        timeri->flags |= SNDRV_TIMER_IFLG_RUNNING;
+        return 0;
+    }
+}
+
+static int snd_timer_start_slave(snd_timer_instance_t *timeri)
+{
+    unsigned long flags;
+
+    spin_lock_irqsave(&slave_active_lock, flags);
+    timeri->flags |= SNDRV_TIMER_IFLG_RUNNING;
+    if (timeri->master)
+        list_add_tail(&timeri->active_list, &timeri->master->slave_active_head);
+    spin_unlock_irqrestore(&slave_active_lock, flags);
+    return 1; /* delayed start */
+}
+
+/*
+ *  start the timer instance
+ */
+int snd_timer_start(snd_timer_instance_t * timeri, unsigned int ticks)
+{
+    snd_timer_t *timer;
+    int result = -EINVAL;
+    unsigned long flags;
+
+    if (timeri == NULL || ticks < 1)
+        return -EINVAL;
+    if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) {
+        result = snd_timer_start_slave(timeri);
+        snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_START);
+        return result;
+    }
+    timer = timeri->timer;
+    if (timer == NULL)
+        return -EINVAL;
+    spin_lock_irqsave(&timer->lock, flags);
+    timeri->ticks = timeri->cticks = ticks;
+    timeri->pticks = 0;
+    result = snd_timer_start1(timer, timeri, ticks);
+    spin_unlock_irqrestore(&timer->lock, flags);
+    snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_START);
+    return result;
+}
+
+static int _snd_timer_stop(struct snd_timer_instance * timeri,
+			   int keep_flag, int event)
+{
+    snd_timer_t *timer;
+    unsigned long flags;
+
+    snd_assert(timeri != NULL, return -ENXIO);
+
+    if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) {
+        if (!keep_flag) {
+            spin_lock_irqsave(&slave_active_lock, flags);
+            timeri->flags &= ~SNDRV_TIMER_IFLG_RUNNING;
+            spin_unlock_irqrestore(&slave_active_lock, flags);
+        }
+        goto __end;
+    }
+    timer = timeri->timer;
+    if (! timer)
+        return -EINVAL;
+    spin_lock_irqsave(&timer->lock, flags);
+    list_del_init(&timeri->ack_list);
+    list_del_init(&timeri->active_list);
+    if ((timeri->flags & SNDRV_TIMER_IFLG_RUNNING) &&
+        !(--timer->running)) {
+        timer->hw.stop(timer);
+        if (timer->flags & SNDRV_TIMER_FLG_RESCHED) {
+            timer->flags &= ~SNDRV_TIMER_FLG_RESCHED;
+            snd_timer_reschedule(timer, 0);
+            if (timer->flags & SNDRV_TIMER_FLG_CHANGE) {
+                timer->flags &= ~SNDRV_TIMER_FLG_CHANGE;
+                timer->hw.start(timer);
+            }
+        }
+    }
+    if (!keep_flag)
+        timeri->flags &= ~(SNDRV_TIMER_IFLG_RUNNING|SNDRV_TIMER_IFLG_START);
+    spin_unlock_irqrestore(&timer->lock, flags);
+__end:
+    if (event != SNDRV_TIMER_EVENT_RESOLUTION)
+        snd_timer_notify1(timeri, event);
+    return 0;
+}
+
+/*
+ * stop the timer instance.
+ *
+ * do not call this from the timer callback!
+ */
+int snd_timer_stop(snd_timer_instance_t * timeri)
+{
+    snd_timer_t *timer;
+    unsigned long flags;
+    int err;
+
+    err = _snd_timer_stop(timeri, 0, SNDRV_TIMER_EVENT_STOP);
+    if (err < 0)
+        return err;
+    timer = timeri->timer;
+    spin_lock_irqsave(&timer->lock, flags);
+    timeri->cticks = timeri->ticks;
+    timeri->pticks = 0;
+    spin_unlock_irqrestore(&timer->lock, flags);
+    return 0;
+}
+
+/*
+ * start again..  the tick is kept.
+ */
+int snd_timer_continue(snd_timer_instance_t * timeri)
+{
+    snd_timer_t * timer;
+    int result = -EINVAL;
+    unsigned long flags;
+
+    if (timeri == NULL)
+        return result;
+    if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
+        return snd_timer_start_slave(timeri);
+    timer = timeri->timer;
+    if (! timer)
+        return -EINVAL;
+    spin_lock_irqsave(&timer->lock, flags);
+    if (!timeri->cticks)
+        timeri->cticks = 1;
+    timeri->pticks = 0;
+    result = snd_timer_start1(timer, timeri, timer->sticks);
+    spin_unlock_irqrestore(&timer->lock, flags);
+    snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_CONTINUE);
+    return result;
+}
+
+/*
+ * pause.. remember the ticks left
+ */
+int snd_timer_pause(snd_timer_instance_t * timeri)
+{
+    return _snd_timer_stop(timeri, 0, SNDRV_TIMER_EVENT_PAUSE);
+}
+
+/*
+ * reschedule the timer
+ *
+ * start pending instances and check the scheduling ticks.
+ * when the scheduling ticks is changed set CHANGE flag to reprogram the timer.
+ */
+static void snd_timer_reschedule(snd_timer_t * timer, unsigned long ticks_left)
+{
+    snd_timer_instance_t *ti;
+    unsigned long ticks = ~0UL;
+    struct list_head *p;
+
+    list_for_each(p, &timer->active_list_head) {
+        ti = (snd_timer_instance_t *)list_entry(p, snd_timer_instance_t, active_list);
+        if (ti->flags & SNDRV_TIMER_IFLG_START) {
+            ti->flags &= ~SNDRV_TIMER_IFLG_START;
+            ti->flags |= SNDRV_TIMER_IFLG_RUNNING;
+            timer->running++;
+        }
+        if (ti->flags & SNDRV_TIMER_IFLG_RUNNING) {
+            if (ticks > ti->cticks)
+                ticks = ti->cticks;
+        }
+    }
+    if (ticks == ~0UL) {
+        timer->flags &= ~SNDRV_TIMER_FLG_RESCHED;
+        return;
+    }
+    if (ticks > timer->hw.ticks)
+        ticks = timer->hw.ticks;
+    if (ticks_left != ticks)
+        timer->flags |= SNDRV_TIMER_FLG_CHANGE;
+    timer->sticks = ticks;
+}
+
+/*
+ * timer tasklet
+ *
+ */
+static void snd_timer_tasklet(unsigned long arg)
+{
+    snd_timer_t *timer = (snd_timer_t *) arg;
+    snd_timer_instance_t *ti;
+    struct list_head *p;
+    unsigned long resolution, ticks;
+
+    spin_lock(&timer->lock);
+    /* now process all callbacks */
+    while (!list_empty(&timer->sack_list_head)) {
+        p = timer->sack_list_head.next;		/* get first item */
+        ti = (snd_timer_instance_t *)list_entry(p, snd_timer_instance_t, ack_list);
+
+        /* remove from ack_list and make empty */
+        list_del_init(p);
+
+        ticks = ti->pticks;
+        ti->pticks = 0;
+        resolution = ti->resolution;
+
+        ti->flags |= SNDRV_TIMER_IFLG_CALLBACK;
+        spin_unlock(&timer->lock);
+        if (ti->callback)
+            ti->callback(ti, resolution, ticks);
+        spin_lock(&timer->lock);
+        ti->flags &= ~SNDRV_TIMER_IFLG_CALLBACK;
+    }
+    spin_unlock(&timer->lock);
+}
+
+/*
+ * timer interrupt
+ *
+ * ticks_left is usually equal to timer->sticks.
+ *
+ */
+void snd_timer_interrupt(snd_timer_t * timer, unsigned long ticks_left)
+{
+    snd_timer_instance_t *ti, *ts;
+    unsigned long resolution, ticks;
+    struct list_head *p, *q, *n;
+    int use_tasklet = 0;
+
+    if (timer == NULL)
+        return;
+
+    spin_lock(&timer->lock);
+
+    /* remember the current resolution */
+    if (timer->hw.c_resolution)
+        resolution = timer->hw.c_resolution(timer);
+    else
+        resolution = timer->hw.resolution;
+
+    /* loop for all active instances
+     * here we cannot use list_for_each because the active_list of a processed
+     * instance is relinked to done_list_head before callback is called.
+     */
+    list_for_each_safe(p, n, &timer->active_list_head) {
+        ti = (snd_timer_instance_t *)list_entry(p, snd_timer_instance_t, active_list);
+        if (!(ti->flags & SNDRV_TIMER_IFLG_RUNNING))
+            continue;
+        ti->pticks += ticks_left;
+        ti->resolution = resolution;
+        if (ti->cticks < ticks_left)
+            ti->cticks = 0;
+        else
+            ti->cticks -= ticks_left;
+        if (ti->cticks) /* not expired */
+            continue;
+        if (ti->flags & SNDRV_TIMER_IFLG_AUTO) {
+            ti->cticks = ti->ticks;
+        } else {
+            ti->flags &= ~SNDRV_TIMER_IFLG_RUNNING;
+            if (--timer->running)
+                list_del(p);
+        }
+        if (list_empty(&ti->ack_list)) {
+            if ((timer->hw.flags & SNDRV_TIMER_HW_TASKLET) ||
+                (ti->flags & SNDRV_TIMER_IFLG_FAST)) {
+                list_add_tail(&ti->ack_list, &timer->ack_list_head);
+            } else {
+                list_add_tail(&ti->ack_list, &timer->sack_list_head);
+            }
+        }
+        list_for_each(q, &ti->slave_active_head) {
+            ts = (snd_timer_instance_t *)list_entry(q, snd_timer_instance_t, active_list);
+            ts->pticks = ti->pticks;
+            ts->resolution = resolution;
+            if (list_empty(&ts->ack_list)) {
+                if ((timer->hw.flags & SNDRV_TIMER_HW_TASKLET) ||
+                    (ti->flags & SNDRV_TIMER_IFLG_FAST)) {
+                    list_add_tail(&ts->ack_list, &timer->ack_list_head);
+                } else {
+                    list_add_tail(&ts->ack_list, &timer->sack_list_head);
+                }
+            }
+        }
+    }
+    if (timer->flags & SNDRV_TIMER_FLG_RESCHED)
+        snd_timer_reschedule(timer, ticks_left);
+    if (timer->running) {
+        if (timer->hw.flags & SNDRV_TIMER_HW_STOP) {
+            timer->hw.stop(timer);
+            timer->flags |= SNDRV_TIMER_FLG_CHANGE;
+        }
+        if (!(timer->hw.flags & SNDRV_TIMER_HW_AUTO) ||
+            (timer->flags & SNDRV_TIMER_FLG_CHANGE)) {
+            /* restart timer */
+            timer->flags &= ~SNDRV_TIMER_FLG_CHANGE;
+            timer->hw.start(timer);
+        }
+    } else {
+        timer->hw.stop(timer);
+    }
+
+    /* now process all fast callbacks */
+    while (!list_empty(&timer->ack_list_head)) {
+        p = timer->ack_list_head.next;		/* get first item */
+        ti = (snd_timer_instance_t *)list_entry(p, snd_timer_instance_t, ack_list);
+
+        /* remove from ack_list and make empty */
+        list_del_init(p);
+
+        ticks = ti->pticks;
+        ti->pticks = 0;
+
+        ti->flags |= SNDRV_TIMER_IFLG_CALLBACK;
+        spin_unlock(&timer->lock);
+        if (ti->callback)
+            ti->callback(ti, resolution, ticks);
+        spin_lock(&timer->lock);
+        ti->flags &= ~SNDRV_TIMER_IFLG_CALLBACK;
+    }
+
+    /* do we have any slow callbacks? */
+    use_tasklet = !list_empty(&timer->sack_list_head);
+    spin_unlock(&timer->lock);
+
+    if (use_tasklet)
+        tasklet_hi_schedule(&timer->task_queue);
+}
+
+/*
+
+*/
+
+int snd_timer_new(snd_card_t *card, char *id, snd_timer_id_t *tid, snd_timer_t ** rtimer)
+{
+    snd_timer_t *timer;
+    int err;
+#ifdef TARGET_OS2
+    static snd_device_ops_t ops = {
+        snd_timer_dev_free,
+        snd_timer_dev_register,
+        0,
+        snd_timer_dev_unregister
+    };
+#else
+    static snd_device_ops_t ops = {
+    dev_free:	snd_timer_dev_free,
+        dev_register:	snd_timer_dev_register,
+        dev_unregister:	snd_timer_dev_unregister
+    };
+#endif
+
+    snd_assert(tid != NULL, return -EINVAL);
+    snd_assert(rtimer != NULL, return -EINVAL);
+    *rtimer = NULL;
+    timer = kcalloc(1, sizeof(*timer), GFP_KERNEL);
+    if (timer == NULL)
+        return -ENOMEM;
+    timer->tmr_class = tid->dev_class;
+    timer->card = card;
+    timer->tmr_device = tid->device;
+    timer->tmr_subdevice = tid->subdevice;
+    if (id)
+        strlcpy(timer->id, id, sizeof(timer->id));
+    INIT_LIST_HEAD(&timer->device_list);
+    INIT_LIST_HEAD(&timer->open_list_head);
+    INIT_LIST_HEAD(&timer->active_list_head);
+    INIT_LIST_HEAD(&timer->ack_list_head);
+    INIT_LIST_HEAD(&timer->sack_list_head);
+    spin_lock_init(&timer->lock);
+    tasklet_init(&timer->task_queue, snd_timer_tasklet, (unsigned long)timer);
+    if (card != NULL) {
+        if ((err = snd_device_new(card, SNDRV_DEV_TIMER, timer, &ops)) < 0) {
+            snd_timer_free(timer);
+            return err;
+        }
+    }
+    *rtimer = timer;
+    return 0;
+}
+
+static int snd_timer_free(snd_timer_t *timer)
+{
+    snd_assert(timer != NULL, return -ENXIO);
+    if (timer->private_free)
+        timer->private_free(timer);
+    kfree(timer);
+    return 0;
+}
+
+static int snd_timer_dev_free(snd_device_t *device)
+{
+    snd_timer_t *timer = device->device_data;
+    return snd_timer_free(timer);
+}
+
+static int snd_timer_dev_register(snd_device_t *dev)
+{
+    snd_timer_t *timer = dev->device_data;
+    snd_timer_t *timer1;
+    struct list_head *p;
+
+    snd_assert(timer != NULL && timer->hw.start != NULL && timer->hw.stop != NULL, return -ENXIO);
+    if (!(timer->hw.flags & SNDRV_TIMER_HW_SLAVE) &&
+        !timer->hw.resolution && timer->hw.c_resolution == NULL)
+        return -EINVAL;
+
+    down(&register_mutex);
+    list_for_each(p, &snd_timer_list) {
+        timer1 = (snd_timer_t *)list_entry(p, snd_timer_t, device_list);
+        if (timer1->tmr_class > timer->tmr_class)
+            break;
+        if (timer1->tmr_class < timer->tmr_class)
+            continue;
+        if (timer1->card && timer->card) {
+            if (timer1->card->number > timer->card->number)
+                break;
+            if (timer1->card->number < timer->card->number)
+                continue;
+        }
+        if (timer1->tmr_device > timer->tmr_device)
+            break;
+        if (timer1->tmr_device < timer->tmr_device)
+            continue;
+        if (timer1->tmr_subdevice > timer->tmr_subdevice)
+            break;
+        if (timer1->tmr_subdevice < timer->tmr_subdevice)
+            continue;
+        /* conflicts.. */
+        up(&register_mutex);
+        return -EBUSY;
+    }
+    list_add_tail(&timer->device_list, p);
+    up(&register_mutex);
+    return 0;
+}
+
+int snd_timer_unregister(snd_timer_t *timer)
+{
+    struct list_head *p, *n;
+    snd_timer_instance_t *ti;
+
+    snd_assert(timer != NULL, return -ENXIO);
+    down(&register_mutex);
+    if (! list_empty(&timer->open_list_head)) {
+        snd_printk(KERN_WARNING "timer 0x%lx is busy?\n", (long)timer);
+        list_for_each_safe(p, n, &timer->open_list_head) {
+            list_del_init(p);
+            ti = (snd_timer_instance_t *)list_entry(p, snd_timer_instance_t, open_list);
+            ti->timer = NULL;
+        }
+    }
+    list_del(&timer->device_list);
+    up(&register_mutex);
+    return snd_timer_free(timer);
+}
+
+static int snd_timer_dev_unregister(snd_device_t *device)
+{
+    snd_timer_t *timer = device->device_data;
+    return snd_timer_unregister(timer);
+}
+
+void snd_timer_notify(struct snd_timer *timer, int event, struct timespec *tstamp)
+{
+    unsigned long flags;
+    unsigned long resolution = 0;
+    snd_timer_instance_t *ti, *ts;
+    struct list_head *p, *n;
+
+    snd_runtime_check(timer->hw.flags & SNDRV_TIMER_HW_SLAVE, return);
+    snd_assert(event >= SNDRV_TIMER_EVENT_MSTART && event <= SNDRV_TIMER_EVENT_MRESUME, return);
+    spin_lock_irqsave(&timer->lock, flags);
+    if (event == SNDRV_TIMER_EVENT_MSTART ||
+        event == SNDRV_TIMER_EVENT_MCONTINUE ||
+        event == SNDRV_TIMER_EVENT_MRESUME) {
+        if (timer->hw.c_resolution)
+            resolution = timer->hw.c_resolution(timer);
+        else
+            resolution = timer->hw.resolution;
+    }
+    list_for_each(p, &timer->active_list_head) {
+        ti = (snd_timer_instance_t *)list_entry(p, snd_timer_instance_t, active_list);
+        if (ti->ccallback)
+            ti->ccallback(ti, event, tstamp, resolution);
+        list_for_each(n, &ti->slave_active_head) {
+            ts = (snd_timer_instance_t *)list_entry(n, snd_timer_instance_t, active_list);
+            if (ts->ccallback)
+                ts->ccallback(ts, event, tstamp, resolution);
+        }
+    }
+    spin_unlock_irqrestore(&timer->lock, flags);
+}
+
+/*
+ * exported functions for global timers
+ */
+int snd_timer_global_new(char *id, int device, snd_timer_t **rtimer)
+{
+    snd_timer_id_t tid;
+
+    tid.dev_class = SNDRV_TIMER_CLASS_GLOBAL;
+    tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE;
+    tid.card = -1;
+    tid.device = device;
+    tid.subdevice = 0;
+    return snd_timer_new(NULL, id, &tid, rtimer);
+}
+
+int snd_timer_global_free(snd_timer_t *timer)
+{
+    return snd_timer_free(timer);
+}
+
+int snd_timer_global_register(snd_timer_t *timer)
+{
+    snd_device_t dev;
+
+    memset(&dev, 0, sizeof(dev));
+    dev.device_data = timer;
+    return snd_timer_dev_register(&dev);
+}
+
+int snd_timer_global_unregister(snd_timer_t *timer)
+{
+    return snd_timer_unregister(timer);
+}
+
+/*
+ *  System timer
+ */
+
+struct snd_timer_system_private {
+    struct timer_list tlist;
+    struct timer * timer;
+    unsigned long last_expires;
+    unsigned long last_jiffies;
+    unsigned long correction;
+};
+
+unsigned int snd_timer_system_resolution(void)
+{
+    return 1000000000L / HZ;
+}
+
+static void snd_timer_s_function(unsigned long data)
+{
+    snd_timer_t *timer = (snd_timer_t *)data;
+    struct snd_timer_system_private *priv = timer->private_data;
+    unsigned long jiff = jiffies;
+    if (time_after(jiff, priv->last_expires))
+        priv->correction = (long)jiff - (long)priv->last_expires;
+    snd_timer_interrupt(timer, (long)jiff - (long)priv->last_jiffies);
+}
+
+static int snd_timer_s_start(snd_timer_t * timer)
+{
+    struct snd_timer_system_private *priv;
+    unsigned long njiff;
+
+    priv = (struct snd_timer_system_private *) timer->private_data;
+    njiff = (priv->last_jiffies = jiffies);
+    if (priv->correction > timer->sticks - 1) {
+        priv->correction -= timer->sticks - 1;
+        njiff++;
+    } else {
+        njiff += timer->sticks - priv->correction;
+        priv->correction -= timer->sticks;
+    }
+    priv->last_expires = priv->tlist.expires = njiff;
+    add_timer(&priv->tlist);
+    return 0;
+}
+
+static int snd_timer_s_stop(snd_timer_t * timer)
+{
+    struct snd_timer_system_private *priv;
+    unsigned long jiff;
+
+    priv = (struct snd_timer_system_private *) timer->private_data;
+    del_timer(&priv->tlist);
+    jiff = jiffies;
+    if (time_before(jiff, priv->last_expires))
+        timer->sticks = priv->last_expires - jiff;
+    else
+        timer->sticks = 1;
+    return 0;
+}
+
+static struct snd_timer_hardware snd_timer_system =
+{
+	.flags =	SNDRV_TIMER_HW_FIRST | SNDRV_TIMER_HW_TASKLET,
+	.resolution =	1000000000L / HZ,
+	.ticks =	10000000L,
+	.start =	snd_timer_s_start,
+	.stop =		snd_timer_s_stop
+};
+
+static void snd_timer_free_system(snd_timer_t *timer)
+{
+    if (timer->private_data)
+        kfree(timer->private_data);
+}
+
+static int snd_timer_register_system(void)
+{
+    snd_timer_t *timer;
+    struct snd_timer_system_private *priv;
+    int err;
+
+    if ((err = snd_timer_global_new("system", SNDRV_TIMER_GLOBAL_SYSTEM, &timer)) < 0)
+        return err;
+    strcpy(timer->name, "system timer");
+    timer->hw = snd_timer_system;
+    priv = kcalloc(1, sizeof(*priv), GFP_KERNEL);
+    if (priv == NULL) {
+        snd_timer_free(timer);
+        return -ENOMEM;
+    }
+    init_timer(&priv->tlist);
+    priv->tlist.function = snd_timer_s_function;
+    priv->tlist.data = (unsigned long) timer;
+    timer->private_data = priv;
+    timer->private_free = snd_timer_free_system;
+    return snd_timer_global_register(timer);
+}
+
+/*
+ *  Info interface
+ */
+
+static void snd_timer_proc_read(snd_info_entry_t *entry,
+                                snd_info_buffer_t * buffer)
+{
+    unsigned long flags;
+    snd_timer_t *timer;
+    snd_timer_instance_t *ti;
+    struct list_head *p, *q;
+
+    down(&register_mutex);
+    list_for_each(p, &snd_timer_list) {
+        timer = (snd_timer_t *)list_entry(p, snd_timer_t, device_list);
+        switch (timer->tmr_class) {
+        case SNDRV_TIMER_CLASS_GLOBAL:
+            snd_iprintf(buffer, "G%i: ", timer->tmr_device);
+            break;
+        case SNDRV_TIMER_CLASS_CARD:
+            snd_iprintf(buffer, "C%i-%i: ", timer->card->number, timer->tmr_device);
+            break;
+        case SNDRV_TIMER_CLASS_PCM:
+            snd_iprintf(buffer, "P%i-%i-%i: ", timer->card->number, timer->tmr_device, timer->tmr_subdevice);
+            break;
+        default:
+            snd_iprintf(buffer, "?%i-%i-%i-%i: ", timer->tmr_class, timer->card ? timer->card->number : -1, timer->tmr_device, timer->tmr_subdevice);
+        }
+        snd_iprintf(buffer, "%s :", timer->name);
+        if (timer->hw.resolution)
+            snd_iprintf(buffer, " %lu.%03luus (%lu ticks)", timer->hw.resolution / 1000, timer->hw.resolution % 1000, timer->hw.ticks);
+        if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE)
+            snd_iprintf(buffer, " SLAVE");
+        snd_iprintf(buffer, "\n");
+        spin_lock_irqsave(&timer->lock, flags);
+        list_for_each(q, &timer->open_list_head) {
+            ti = (snd_timer_instance_t *)list_entry(q, snd_timer_instance_t, open_list);
+            snd_iprintf(buffer, "  Client %s : %s : lost interrupts %li\n",
+                        ti->owner ? ti->owner : "unknown",
+                        ti->flags & (SNDRV_TIMER_IFLG_START|SNDRV_TIMER_IFLG_RUNNING) ? "running" : "stopped",
+                        ti->lost);
+        }
+        spin_unlock_irqrestore(&timer->lock, flags);
+    }
+    up(&register_mutex);
+}
+
+/*
+ *  USER SPACE interface
+ */
+
+static void snd_timer_user_interrupt(snd_timer_instance_t *timeri,
+                                     unsigned long resolution,
+                                     unsigned long ticks)
+{
+    snd_timer_user_t *tu = timeri->callback_data;
+    snd_timer_read_t *r;
+    int prev;
+
+    spin_lock(&tu->qlock);
+    if (tu->qused > 0) {
+        prev = tu->qtail == 0 ? tu->queue_size - 1 : tu->qtail - 1;
+        r = &tu->queue[prev];
+        if (r->resolution == resolution) {
+            r->ticks += ticks;
+            goto __wake;
+        }
+    }
+    if (tu->qused >= tu->queue_size) {
+        tu->overrun++;
+    } else {
+        r = &tu->queue[tu->qtail++];
+        tu->qtail %= tu->queue_size;
+        r->resolution = resolution;
+        r->ticks = ticks;
+        tu->qused++;
+    }
+__wake:
+    spin_unlock(&tu->qlock);
+    kill_fasync(tu->fasync, SIGIO, POLL_IN);
+    wake_up(&tu->qchange_sleep);
+}
+
+static void snd_timer_user_append_to_tqueue(snd_timer_user_t *tu, snd_timer_tread_t *tread)
+{
+    if (tu->qused >= tu->queue_size) {
+        tu->overrun++;
+    } else {
+        memcpy(&tu->tqueue[tu->qtail++], tread, sizeof(*tread));
+        tu->qtail %= tu->queue_size;
+        tu->qused++;
+    }
+}
+
+static void snd_timer_user_ccallback(struct snd_timer_instance *timeri,
+				     int event,
+				     struct timespec *tstamp,
+				     unsigned long resolution)
+{
+    snd_timer_user_t *tu = timeri->callback_data;
+    snd_timer_tread_t r1;
+
+    if (event >= SNDRV_TIMER_EVENT_START && event <= SNDRV_TIMER_EVENT_PAUSE)
+        tu->tstamp = *tstamp;
+    if ((tu->filter & (1 << event)) == 0 || !tu->tread)
+        return;
+    r1.event = event;
+    r1.tstamp = *tstamp;
+    r1.val = resolution;
+    spin_lock(&tu->qlock);
+    snd_timer_user_append_to_tqueue(tu, &r1);
+    spin_unlock(&tu->qlock);
+    //kill_fasync(&tu->fasync, SIGIO, POLL_IN);
+    wake_up(&tu->qchange_sleep);
+}
+
+static void snd_timer_user_tinterrupt(snd_timer_instance_t *timeri,
+                                      unsigned long resolution,
+                                      unsigned long ticks)
+{
+    snd_timer_user_t *tu = timeri->callback_data;
+    snd_timer_tread_t *r, r1;
+    struct timespec tstamp;
+    int prev, append = 0;
+
+    memset(&tstamp, 0, sizeof(tstamp));
+    spin_lock(&tu->qlock);
+    if ((tu->filter & ((1 << SNDRV_TIMER_EVENT_RESOLUTION)|(1 << SNDRV_TIMER_EVENT_TICK))) == 0) {
+        spin_unlock(&tu->qlock);
+        return;
+    }
+    if (tu->last_resolution != resolution || ticks > 0)
+        snd_timestamp_now(&tstamp, 1);
+    if ((tu->filter & (1 << SNDRV_TIMER_EVENT_RESOLUTION)) && tu->last_resolution != resolution) {
+        r1.event = SNDRV_TIMER_EVENT_RESOLUTION;
+        r1.tstamp = tstamp;
+        r1.val = resolution;
+        snd_timer_user_append_to_tqueue(tu, &r1);
+        tu->last_resolution = resolution;
+        append++;
+    }
+    if ((tu->filter & (1 << SNDRV_TIMER_EVENT_TICK)) == 0)
+        goto __wake;
+    if (ticks == 0)
+        goto __wake;
+    if (tu->qused > 0) {
+        prev = tu->qtail == 0 ? tu->queue_size - 1 : tu->qtail - 1;
+        r = &tu->tqueue[prev];
+        if (r->event == SNDRV_TIMER_EVENT_TICK) {
+            r->tstamp = tstamp;
+            r->val += ticks;
+            append++;
+            goto __wake;
+        }
+    }
+    r1.event = SNDRV_TIMER_EVENT_TICK;
+    r1.tstamp = tstamp;
+    r1.val = ticks;
+    snd_timer_user_append_to_tqueue(tu, &r1);
+    append++;
+__wake:
+    spin_unlock(&tu->qlock);
+    if (append == 0)
+        return;
+    kill_fasync(tu->fasync, SIGIO, POLL_IN);
+    wake_up(&tu->qchange_sleep);
+}
+
+static int snd_timer_user_open(struct inode *inode, struct file *file)
+{
+    snd_timer_user_t *tu;
+
+    tu = kcalloc(1, sizeof(*tu), GFP_KERNEL);
+    if (tu == NULL)
+        return -ENOMEM;
+    spin_lock_init(&tu->qlock);
+    init_waitqueue_head(&tu->qchange_sleep);
+    tu->ticks = 1;
+    tu->queue_size = 128;
+    tu->queue = (snd_timer_read_t *)kmalloc(tu->queue_size * sizeof(snd_timer_read_t), GFP_KERNEL);
+    if (tu->queue == NULL) {
+        kfree(tu);
+        return -ENOMEM;
+    }
+    file->private_data = tu;
+    return 0;
+}
+
+static int snd_timer_user_release(struct inode *inode, struct file *file)
+{
+    snd_timer_user_t *tu;
+
+    if (file->private_data) {
+        tu = file->private_data;
+        file->private_data = NULL;
+        fasync_helper(-1, file, 0, &tu->fasync);
+        if (tu->timeri)
+            snd_timer_close(tu->timeri);
+        if (tu->queue)
+            kfree(tu->queue);
+        if (tu->tqueue)
+            kfree(tu->tqueue);
+        kfree(tu);
+    }
+    return 0;
+}
+
+static void snd_timer_user_zero_id(snd_timer_id_t *id)
+{
+    id->dev_class = SNDRV_TIMER_CLASS_NONE;
+    id->dev_sclass = SNDRV_TIMER_SCLASS_NONE;
+    id->card = -1;
+    id->device = -1;
+    id->subdevice = -1;
+}
+
+static void snd_timer_user_copy_id(snd_timer_id_t *id, snd_timer_t *timer)
+{
+    id->dev_class = timer->tmr_class;
+    id->dev_sclass = SNDRV_TIMER_SCLASS_NONE;
+    id->card = timer->card ? timer->card->number : -1;
+    id->device = timer->tmr_device;
+    id->subdevice = timer->tmr_subdevice;
+}
+
+static int snd_timer_user_next_device(snd_timer_id_t *_tid)
+{
+    snd_timer_id_t id;
+    snd_timer_t *timer;
+    struct list_head *p;
+
+    if (copy_from_user(&id, _tid, sizeof(id)))
+        return -EFAULT;
+    down(&register_mutex);
+    if (id.dev_class < 0) {		/* first item */
+        if (list_empty(&snd_timer_list))
+            snd_timer_user_zero_id(&id);
+        else {
+            timer = (snd_timer_t *)list_entry(snd_timer_list.next, snd_timer_t, device_list);
+            snd_timer_user_copy_id(&id, timer);
+        }
+    } else {
+        switch (id.dev_class) {
+        case SNDRV_TIMER_CLASS_GLOBAL:
+            id.device = id.device < 0 ? 0 : id.device + 1;
+            list_for_each(p, &snd_timer_list) {
+                timer = (snd_timer_t *)list_entry(p, snd_timer_t, device_list);
+                if (timer->tmr_class > SNDRV_TIMER_CLASS_GLOBAL) {
+                    snd_timer_user_copy_id(&id, timer);
+                    break;
+                }
+                if (timer->tmr_device >= id.device) {
+                    snd_timer_user_copy_id(&id, timer);
+                    break;
+                }
+            }
+            if (p == &snd_timer_list)
+                snd_timer_user_zero_id(&id);
+            break;
+        case SNDRV_TIMER_CLASS_CARD:
+        case SNDRV_TIMER_CLASS_PCM:
+            if (id.card < 0) {
+                id.card = 0;
+            } else {
+                if (id.card < 0) {
+                    id.card = 0;
+                } else {
+                    if (id.device < 0) {
+                        id.device = 0;
+                    } else {
+                        id.subdevice = id.subdevice < 0 ? 0 : id.subdevice + 1;
+                    }
+                }
+            }
+            list_for_each(p, &snd_timer_list) {
+                timer = (snd_timer_t *)list_entry(p, snd_timer_t, device_list);
+                if (timer->tmr_class > id.dev_class) {
+                    snd_timer_user_copy_id(&id, timer);
+                    break;
+                }
+                if (timer->tmr_class < id.dev_class)
+                    continue;
+                if (timer->card->number > id.card) {
+                    snd_timer_user_copy_id(&id, timer);
+                    break;
+                }
+                if (timer->card->number < id.card)
+                    continue;
+                if (timer->tmr_device > id.device) {
+                    snd_timer_user_copy_id(&id, timer);
+                    break;
+                }
+                if (timer->tmr_device < id.device)
+                    continue;
+                if (timer->tmr_subdevice > id.subdevice) {
+                    snd_timer_user_copy_id(&id, timer);
+                    break;
+                }
+                if (timer->tmr_subdevice < id.subdevice)
+                    continue;
+                snd_timer_user_copy_id(&id, timer);
+                break;
+            }
+            if (p == &snd_timer_list)
+                snd_timer_user_zero_id(&id);
+            break;
+        default:
+            snd_timer_user_zero_id(&id);
+        }
+    }
+    up(&register_mutex);
+    if (copy_to_user(_tid, &id, sizeof(*_tid)))
+        return -EFAULT;
+    return 0;
+}
+
+static int snd_timer_user_ginfo(struct file *file, snd_timer_ginfo_t *_ginfo)
+{
+    snd_timer_ginfo_t ginfo;
+    snd_timer_id_t tid;
+    snd_timer_t *t;
+    struct list_head *p;
+    int err = 0;
+
+    if (copy_from_user(&ginfo, _ginfo, sizeof(ginfo)))
+        return -EFAULT;
+    tid = ginfo.tid;
+    memset(&ginfo, 0, sizeof(ginfo));
+    ginfo.tid = tid;
+    down(&register_mutex);
+    t = snd_timer_find(&tid);
+    if (t != NULL) {
+        ginfo.card = t->card ? t->card->number : -1;
+        if (t->hw.flags & SNDRV_TIMER_HW_SLAVE)
+            ginfo.flags |= SNDRV_TIMER_FLG_SLAVE;
+        strlcpy(ginfo.id, t->id, sizeof(ginfo.id));
+        strlcpy(ginfo.name, t->name, sizeof(ginfo.name));
+        ginfo.resolution = t->hw.resolution;
+        if (t->hw.resolution_min > 0) {
+            ginfo.resolution_min = t->hw.resolution_min;
+            ginfo.resolution_max = t->hw.resolution_max;
+        }
+        list_for_each(p, &t->open_list_head) {
+            ginfo.clients++;
+        }
+    } else {
+        err = -ENODEV;
+    }
+    up(&register_mutex);
+    if (err >= 0 && copy_to_user(_ginfo, &ginfo, sizeof(ginfo)))
+        err = -EFAULT;
+    return err;
+}
+
+static int snd_timer_user_gparams(struct file *file, snd_timer_gparams_t *_gparams)
+{
+    snd_timer_gparams_t gparams;
+    snd_timer_t *t;
+    int err;
+
+    if (copy_from_user(&gparams, _gparams, sizeof(gparams)))
+        return -EFAULT;
+    down(&register_mutex);
+    t = snd_timer_find(&gparams.tid);
+    if (t != NULL) {
+        if (list_empty(&t->open_list_head)) {
+            if (t->hw.set_period)
+                err = t->hw.set_period(t, gparams.period_num, gparams.period_den);
+            else
+                err = -ENOSYS;
+        } else {
+            err = -EBUSY;
+        }
+    } else {
+        err = -ENODEV;
+    }
+    up(&register_mutex);
+    return err;
+}
+
+static int snd_timer_user_gstatus(struct file *file, snd_timer_gstatus_t *_gstatus)
+{
+    snd_timer_gstatus_t gstatus;
+    snd_timer_id_t tid;
+    snd_timer_t *t;
+    int err = 0;
+
+    if (copy_from_user(&gstatus, _gstatus, sizeof(gstatus)))
+        return -EFAULT;
+    tid = gstatus.tid;
+    memset(&gstatus, 0, sizeof(gstatus));
+    gstatus.tid = tid;
+    down(&register_mutex);
+    t = snd_timer_find(&tid);
+    if (t != NULL) {
+        if (t->hw.c_resolution)
+            gstatus.resolution = t->hw.c_resolution(t);
+        else
+            gstatus.resolution = t->hw.resolution;
+        if (t->hw.precise_resolution) {
+            t->hw.precise_resolution(t, &gstatus.resolution_num, &gstatus.resolution_den);
+        } else {
+            gstatus.resolution_num = gstatus.resolution;
+            gstatus.resolution_den = 1000000000uL;
+        }
+    } else {
+        err = -ENODEV;
+    }
+    up(&register_mutex);
+    if (err >= 0 && copy_from_user(_gstatus, &gstatus, sizeof(gstatus)))
+        err = -EFAULT;
+    return err;
+}
+
+static int snd_timer_user_tselect(struct file *file, snd_timer_select_t *_tselect)
+{
+    snd_timer_user_t *tu;
+    snd_timer_select_t tselect;
+    char str[32];
+    int err;
+
+    tu = file->private_data;
+    if (tu->timeri)
+        snd_timer_close(tu->timeri);
+    if (copy_from_user(&tselect, _tselect, sizeof(tselect)))
+        return -EFAULT;
+    sprintf(str, "application %i", current->pid);
+    if (tselect.id.dev_class != SNDRV_TIMER_CLASS_SLAVE)
+        tselect.id.dev_sclass = SNDRV_TIMER_SCLASS_APPLICATION;
+    if ((err = snd_timer_open(&tu->timeri, str, &tselect.id, current->pid)) < 0)
+        return err;
+
+    if (tu->queue) {
+        kfree(tu->queue);
+        tu->queue = NULL;
+    }
+    if (tu->tqueue) {
+        kfree(tu->tqueue);
+        tu->tqueue = NULL;
+    }
+    if (tu->tread) {
+        tu->tqueue = (snd_timer_tread_t *)kmalloc(tu->queue_size * sizeof(snd_timer_tread_t), GFP_KERNEL);
+        if (tu->tqueue == NULL) {
+            snd_timer_close(tu->timeri);
+            return -ENOMEM;
+        }
+    } else {
+        tu->queue = (snd_timer_read_t *)kmalloc(tu->queue_size * sizeof(snd_timer_read_t), GFP_KERNEL);
+        if (tu->queue == NULL) {
+            snd_timer_close(tu->timeri);
+            return -ENOMEM;
+        }
+    }
+
+    tu->timeri->flags |= SNDRV_TIMER_IFLG_FAST;
+    tu->timeri->callback = tu->tread ? snd_timer_user_tinterrupt : snd_timer_user_interrupt;
+    tu->timeri->ccallback = snd_timer_user_ccallback;
+    tu->timeri->callback_data = (void *)tu;
+    return 0;
+}
+
+static int snd_timer_user_info(struct file *file, snd_timer_info_t *_info)
+{
+    snd_timer_user_t *tu;
+    snd_timer_info_t info;
+    snd_timer_t *t;
+
+    tu = file->private_data;
+    snd_assert(tu->timeri != NULL, return -ENXIO);
+    t = tu->timeri->timer;
+    snd_assert(t != NULL, return -ENXIO);
+    memset(&info, 0, sizeof(info));
+    info.card = t->card ? t->card->number : -1;
+    if (t->hw.flags & SNDRV_TIMER_HW_SLAVE)
+        info.flags |= SNDRV_TIMER_FLG_SLAVE;
+    strlcpy(info.id, t->id, sizeof(info.id));
+    strlcpy(info.name, t->name, sizeof(info.name));
+    info.resolution = t->hw.resolution;
+    if (copy_to_user(_info, &info, sizeof(*_info)))
+        return -EFAULT;
+    return 0;
+}
+
+static int snd_timer_user_params(struct file *file, snd_timer_params_t *_params)
+{
+    snd_timer_user_t *tu;
+    snd_timer_params_t params;
+    snd_timer_t *t;
+    snd_timer_read_t *tr;
+    snd_timer_tread_t *ttr;
+    int err;
+
+    tu = file->private_data;
+    snd_assert(tu->timeri != NULL, return -ENXIO);
+    t = tu->timeri->timer;
+    snd_assert(t != NULL, return -ENXIO);
+    if (copy_from_user(&params, _params, sizeof(params)))
+        return -EFAULT;
+    if (!(t->hw.flags & SNDRV_TIMER_HW_SLAVE) && params.ticks < 1) {
+        err = -EINVAL;
+        goto _end;
+    }
+    if (params.queue_size > 0 && (params.queue_size < 32 || params.queue_size > 1024)) {
+        err = -EINVAL;
+        goto _end;
+    }
+    if (params.filter & ~((1<<SNDRV_TIMER_EVENT_RESOLUTION)|
+                          (1<<SNDRV_TIMER_EVENT_TICK)|
+                          (1<<SNDRV_TIMER_EVENT_START)|
+                          (1<<SNDRV_TIMER_EVENT_STOP)|
+                          (1<<SNDRV_TIMER_EVENT_CONTINUE)|
+                          (1<<SNDRV_TIMER_EVENT_PAUSE)|
+                          (1<<SNDRV_TIMER_EVENT_SUSPEND)|
+                          (1<<SNDRV_TIMER_EVENT_RESUME)|
+                          (1<<SNDRV_TIMER_EVENT_MSTART)|
+                          (1<<SNDRV_TIMER_EVENT_MSTOP)|
+                          (1<<SNDRV_TIMER_EVENT_MCONTINUE)|
+                          (1<<SNDRV_TIMER_EVENT_MPAUSE)|
+                          (1<<SNDRV_TIMER_EVENT_MSUSPEND)|
+                          (1<<SNDRV_TIMER_EVENT_MRESUME))) {
+        err = -EINVAL;
+        goto _end;
+    }
+    snd_timer_stop(tu->timeri);
+    spin_lock_irq(&t->lock);
+    tu->timeri->flags &= ~(SNDRV_TIMER_IFLG_AUTO|
+                           SNDRV_TIMER_IFLG_EXCLUSIVE|
+                           SNDRV_TIMER_IFLG_EARLY_EVENT);
+    if (params.flags & SNDRV_TIMER_PSFLG_AUTO)
+        tu->timeri->flags |= SNDRV_TIMER_IFLG_AUTO;
+    if (params.flags & SNDRV_TIMER_PSFLG_EXCLUSIVE)
+        tu->timeri->flags |= SNDRV_TIMER_IFLG_EXCLUSIVE;
+    if (params.flags & SNDRV_TIMER_PSFLG_EARLY_EVENT)
+        tu->timeri->flags |= SNDRV_TIMER_IFLG_EARLY_EVENT;
+    spin_unlock_irq(&t->lock);
+    if (params.queue_size > 0 && (unsigned int)tu->queue_size != params.queue_size) {
+        if (tu->tread) {
+            ttr = (snd_timer_tread_t *)kmalloc(params.queue_size * sizeof(snd_timer_tread_t), GFP_KERNEL);
+            if (ttr) {
+                kfree(tu->tqueue);
+                tu->queue_size = params.queue_size;
+                tu->tqueue = ttr;
+            }
+        } else {
+            tr = (snd_timer_read_t *)kmalloc(params.queue_size * sizeof(snd_timer_read_t), GFP_KERNEL);
+            if (tr) {
+                kfree(tu->queue);
+                tu->queue_size = params.queue_size;
+                tu->queue = tr;
+            }
+        }
+    }
+    tu->qhead = tu->qtail = tu->qused = 0;
+    if (tu->timeri->flags & SNDRV_TIMER_IFLG_EARLY_EVENT) {
+        if (tu->tread) {
+            snd_timer_tread_t tread;
+            tread.event = SNDRV_TIMER_EVENT_EARLY;
+            tread.tstamp.tv_sec = 0;
+            tread.tstamp.tv_nsec = 0;
+            tread.val = 0;
+            snd_timer_user_append_to_tqueue(tu, &tread);
+        } else {
+            snd_timer_read_t *r = &tu->queue[0];
+            r->resolution = 0;
+            r->ticks = 0;
+            tu->qused++;
+            tu->qtail++;
+        }
+    }
+    tu->filter = params.filter;
+    tu->ticks = params.ticks;
+    err = 0;
+    _end:
+        if (copy_to_user(_params, &params, sizeof(params)))
+            return -EFAULT;
+        return err;
+}
+
+static int snd_timer_user_status(struct file *file, snd_timer_status_t *_status)
+{
+    snd_timer_user_t *tu;
+    snd_timer_status_t status;
+
+    tu = file->private_data;
+    snd_assert(tu->timeri != NULL, return -ENXIO);
+    memset(&status, 0, sizeof(status));
+    status.tstamp = tu->tstamp;
+    status.resolution = snd_timer_resolution(tu->timeri);
+    status.lost = tu->timeri->lost;
+    status.overrun = tu->overrun;
+    spin_lock_irq(&tu->qlock);
+    status.queue = tu->qused;
+    spin_unlock_irq(&tu->qlock);
+    if (copy_to_user(_status, &status, sizeof(status)))
+        return -EFAULT;
+    return 0;
+}
+
+static int snd_timer_user_start(struct file *file)
+{
+    int err;
+    snd_timer_user_t *tu;
+
+    tu = file->private_data;
+    snd_assert(tu->timeri != NULL, return -ENXIO);
+    snd_timer_stop(tu->timeri);
+    tu->timeri->lost = 0;
+    tu->last_resolution = 0;
+    return (err = snd_timer_start(tu->timeri, tu->ticks)) < 0 ? err : 0;
+}
+
+static int snd_timer_user_stop(struct file *file)
+{
+    int err;
+    snd_timer_user_t *tu;
+
+    tu = file->private_data;
+    snd_assert(tu->timeri != NULL, return -ENXIO);
+    return (err = snd_timer_stop(tu->timeri)) < 0 ? err : 0;
+}
+
+static int snd_timer_user_continue(struct file *file)
+{
+    int err;
+    snd_timer_user_t *tu;
+
+    tu = file->private_data;
+    snd_assert(tu->timeri != NULL, return -ENXIO);
+    tu->timeri->lost = 0;
+    return (err = snd_timer_continue(tu->timeri)) < 0 ? err : 0;
+}
+
+static int snd_timer_user_pause(struct file *file)
+{
+    int err;
+    snd_timer_user_t *tu;
+
+    tu = file->private_data;
+    snd_assert(tu->timeri != NULL, return -ENXIO);
+    return (err = snd_timer_pause(tu->timeri)) < 0 ? err : 0;
+}
+
+static int snd_timer_user_ioctl(struct inode *inode, struct file *file,
+                                unsigned int cmd, unsigned long arg)
+{
+    snd_timer_user_t *tu;
+
+    tu = file->private_data;
+    switch (cmd) {
+    case SNDRV_TIMER_IOCTL_PVERSION:
+        return put_user(SNDRV_TIMER_VERSION, (int *)arg) ? -EFAULT : 0;
+    case SNDRV_TIMER_IOCTL_NEXT_DEVICE:
+        return snd_timer_user_next_device((snd_timer_id_t *)arg);
+    case SNDRV_TIMER_IOCTL_TREAD:
+        {
+            int xarg;
+
+            if (tu->timeri)		/* too late */
+                return -EBUSY;
+            if (get_user(xarg, (int *) arg))
+                return -EFAULT;
+            tu->tread = xarg ? 1 : 0;
+            return 0;
+        }
+    case SNDRV_TIMER_IOCTL_GINFO:
+        return snd_timer_user_ginfo(file, (snd_timer_ginfo_t *)arg);
+    case SNDRV_TIMER_IOCTL_GPARAMS:
+        return snd_timer_user_gparams(file, (snd_timer_gparams_t *)arg);
+    case SNDRV_TIMER_IOCTL_GSTATUS:
+        return snd_timer_user_gstatus(file, (snd_timer_gstatus_t *)arg);
+    case SNDRV_TIMER_IOCTL_SELECT:
+        return snd_timer_user_tselect(file, (snd_timer_select_t *)arg);
+    case SNDRV_TIMER_IOCTL_INFO:
+        return snd_timer_user_info(file, (snd_timer_info_t *)arg);
+    case SNDRV_TIMER_IOCTL_PARAMS:
+        return snd_timer_user_params(file, (snd_timer_params_t *)arg);
+    case SNDRV_TIMER_IOCTL_STATUS:
+        return snd_timer_user_status(file, (snd_timer_status_t *)arg);
+    case SNDRV_TIMER_IOCTL_START:
+        return snd_timer_user_start(file);
+    case SNDRV_TIMER_IOCTL_STOP:
+        return snd_timer_user_stop(file);
+    case SNDRV_TIMER_IOCTL_CONTINUE:
+        return snd_timer_user_continue(file);
+    case SNDRV_TIMER_IOCTL_PAUSE:
+        return snd_timer_user_pause(file);
+    }
+    return -ENOTTY;
+}
+
+static int snd_timer_user_fasync(int fd, struct file * file, int on)
+{
+    snd_timer_user_t *tu;
+    int err;
+
+    tu = file->private_data;
+    err = fasync_helper(fd, file, on, &tu->fasync);
+    if (err < 0)
+        return err;
+    return 0;
+}
+
+static ssize_t snd_timer_user_read(struct file *file, char *buffer, size_t count, loff_t *offset)
+{
+    snd_timer_user_t *tu;
+    long result = 0, unit;
+    int  err = 0;
+
+    tu = file->private_data;
+    unit = tu->tread ? sizeof(snd_timer_tread_t) : sizeof(snd_timer_read_t);
+    spin_lock_irq(&tu->qlock);
+    while ((long)count - result >= unit) {
+        while (!tu->qused) {
+            wait_queue_t wait;
+
+            if ((file->f_flags & O_NONBLOCK) != 0 || result > 0) {
+                err = -EAGAIN;
+                break;
+            }
+
+            set_current_state(TASK_INTERRUPTIBLE);
+            init_waitqueue_entry(&wait, current);
+            add_wait_queue(&tu->qchange_sleep, &wait);
+
+            spin_unlock_irq(&tu->qlock);
+            schedule();
+            spin_lock_irq(&tu->qlock);
+
+            remove_wait_queue(&tu->qchange_sleep, &wait);
+
+            if (signal_pending(current)) {
+                err = -ERESTARTSYS;
+                break;
+            }
+        }
+
+        spin_unlock_irq(&tu->qlock);
+        if (err < 0)
+            goto _error;
+
+        if (tu->tread) {
+            if (copy_to_user(buffer, &tu->tqueue[tu->qhead++], sizeof(snd_timer_tread_t))) {
+                err = -EFAULT;
+                goto _error;
+            }
+        } else {
+            if (copy_to_user(buffer, &tu->queue[tu->qhead++], sizeof(snd_timer_read_t))) {
+                err = -EFAULT;
+                goto _error;
+            }
+        }
+
+        tu->qhead %= tu->queue_size;
+
+        result += unit;
+        buffer += unit;
+
+        spin_lock_irq(&tu->qlock);
+        tu->qused--;
+    }
+    spin_unlock_irq(&tu->qlock);
+    _error:
+        return result > 0 ? result : err;
+}
+
+static unsigned int snd_timer_user_poll(struct file *file, poll_table * wait)
+{
+    unsigned int mask;
+    snd_timer_user_t *tu;
+
+    tu = file->private_data;
+
+    poll_wait(file, &tu->qchange_sleep, wait);
+
+    mask = 0;
+    if (tu->qused)
+        mask |= POLLIN | POLLRDNORM;
+
+    return mask;
+}
+
+static struct file_operations snd_timer_f_ops =
+{
+#ifndef TARGET_OS2
+    .owner =	THIS_MODULE,
+#endif
+    .read =		snd_timer_user_read,
+    .open =		snd_timer_user_open,
+    .release =	snd_timer_user_release,
+    .poll =		snd_timer_user_poll,
+    .ioctl =	snd_timer_user_ioctl,
+#ifndef TARGET_OS2
+    .compat_ioctl =	snd_timer_user_ioctl_compat,
+#endif
+    .fasync = 	snd_timer_user_fasync,
+};
+
+static struct snd_minor snd_timer_reg =
+{
+    .comment =	"timer",
+    .f_ops =	&snd_timer_f_ops,
+};
+
+/*
+ *  ENTRY functions
+ */
+
+static snd_info_entry_t *snd_timer_proc_entry = NULL;
+
+static int __init alsa_timer_init(void)
+{
+    int err;
+    snd_info_entry_t *entry;
+
+#ifdef SNDRV_OSS_INFO_DEV_TIMERS
+    snd_oss_info_register(SNDRV_OSS_INFO_DEV_TIMERS, SNDRV_CARDS - 1, "system timer");
+#endif
+    if ((entry = snd_info_create_module_entry(THIS_MODULE, "timers", NULL)) != NULL) {
+        entry->c.text.read_size = SNDRV_TIMER_DEVICES * 128;
+        entry->c.text.read = snd_timer_proc_read;
+        if (snd_info_register(entry) < 0) {
+            snd_info_free_entry(entry);
+            entry = NULL;
+        }
+    }
+    snd_timer_proc_entry = entry;
+    if ((err = snd_timer_register_system()) < 0)
+        snd_printk("unable to register system timer (%i)\n", err);
+    if ((err = snd_register_device(SNDRV_DEVICE_TYPE_TIMER,
+                                   NULL, 0, &snd_timer_reg, "timer"))<0)
+        snd_printk("unable to register timer device (%i)\n", err);
+    return 0;
+}
+
+static void __exit alsa_timer_exit(void)
+{
+    struct list_head *p, *n;
+
+    snd_unregister_device(SNDRV_DEVICE_TYPE_TIMER, NULL, 0);
+    /* unregister the system timer */
+    list_for_each_safe(p, n, &snd_timer_list) {
+        snd_timer_t *timer = (snd_timer_t *)list_entry(p, snd_timer_t, device_list);
+        snd_timer_unregister(timer);
+    }
+    if (snd_timer_proc_entry) {
+        snd_info_unregister(snd_timer_proc_entry);
+        snd_timer_proc_entry = NULL;
+    }
+#ifdef SNDRV_OSS_INFO_DEV_TIMERS
+    snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_TIMERS, SNDRV_CARDS - 1);
+#endif
+}
+
+module_init(alsa_timer_init)
+module_exit(alsa_timer_exit)
+
+#ifndef MODULE
+/* format is: snd-timer=timer_limit */
+
+static int __init alsa_timer_setup(char *str)
+{
+    (void)(get_option(&str,&timer_limit) == 2);
+    return 1;
+}
+
+__setup("snd-timer=", alsa_timer_setup);
+#endif /* ifndef MODULE */
+
+EXPORT_SYMBOL(snd_timer_open);
+EXPORT_SYMBOL(snd_timer_close);
+EXPORT_SYMBOL(snd_timer_resolution);
+EXPORT_SYMBOL(snd_timer_start);
+EXPORT_SYMBOL(snd_timer_stop);
+EXPORT_SYMBOL(snd_timer_continue);
+EXPORT_SYMBOL(snd_timer_pause);
+EXPORT_SYMBOL(snd_timer_new);
+EXPORT_SYMBOL(snd_timer_notify);
+EXPORT_SYMBOL(snd_timer_global_new);
+EXPORT_SYMBOL(snd_timer_global_free);
+EXPORT_SYMBOL(snd_timer_global_register);
+EXPORT_SYMBOL(snd_timer_global_unregister);
+EXPORT_SYMBOL(snd_timer_interrupt);
+EXPORT_SYMBOL(snd_timer_system_resolution);
Index: /GPL/branches/alsa-resync1/alsa-kernel/core/wrappers.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/core/wrappers.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/core/wrappers.c	(revision 91)
@@ -0,0 +1,67 @@
+/*
+ *  Various wrappers
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifdef ALSA_BUILD
+#include <sound/config.h>
+#endif
+
+#include <linux/version.h>
+#include <linux/config.h>
+#ifdef ALSA_BUILD
+#if defined(CONFIG_MODVERSIONS) && !defined(__GENKSYMS__) && !defined(__DEPEND__)
+#define MODVERSIONS
+#include <linux/modversions.h>
+#include <sound/sndversions.h>
+#endif
+#endif
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <linux/vmalloc.h>
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 3, 0)
+void snd_wrapper_request_region(unsigned long from, unsigned long extent, const char *name)
+{
+	return request_region(from, extent, name);
+}
+#endif
+
+#ifdef CONFIG_SND_DEBUG_MEMORY
+void *snd_wrapper_kmalloc(size_t size, int flags)
+{
+	return kmalloc(size, flags);
+}
+
+void snd_wrapper_kfree(const void *obj)
+{
+	kfree(obj);
+}
+
+void *snd_wrapper_vmalloc(unsigned long size)
+{
+	return vmalloc(size);
+}
+
+void snd_wrapper_vfree(void *obj)
+{
+	vfree(obj);
+}
+#endif
Index: /GPL/branches/alsa-resync1/alsa-kernel/drivers/dummy.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/drivers/dummy.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/drivers/dummy.c	(revision 91)
@@ -0,0 +1,659 @@
+/*
+ *  Dummy soundcard
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#define SNDRV_MAIN_OBJECT_FILE
+#include <sound/driver.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/rawmidi.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Dummy soundcard (/dev/null)");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{ALSA,Dummy soundcard}}");
+
+#define MAX_PCM_DEVICES		4
+#define MAX_PCM_SUBSTREAMS	16
+#define MAX_MIDI_DEVICES	2
+
+
+#if 0 /* RME9652 emulation */
+#define MAX_BUFFER_SIZE		(26 * 64 * 1024)
+#define USE_FORMATS		SNDRV_PCM_FMTBIT_S32_LE
+#define USE_CHANNELS_MIN	26
+#define USE_CHANNELS_MAX	26
+#define USE_PERIODS_MIN		2
+#define USE_PERIODS_MAX		2
+#endif
+
+#if 0 /* ICE1712 emulation */
+#define MAX_BUFFER_SIZE		(256 * 1024)
+#define USE_FORMATS		SNDRV_PCM_FMTBIT_S32_LE
+#define USE_CHANNELS_MIN	10
+#define USE_CHANNELS_MAX	10
+#define USE_PERIODS_MIN		1
+#define USE_PERIODS_MAX		1024
+#endif
+
+#if 0 /* UDA1341 emulation */
+#define MAX_BUFFER_SIZE		(16380)
+#define USE_FORMATS		SNDRV_PCM_FMTBIT_S16_LE
+#define USE_CHANNELS_MIN	2
+#define USE_CHANNELS_MAX	2
+#define USE_PERIODS_MIN		2
+#define USE_PERIODS_MAX		255
+#endif
+
+#if 0 /* simple AC97 bridge (intel8x0) with 48kHz AC97 only codec */
+#define USE_FORMATS		SNDRV_PCM_FMTBIT_S16_LE
+#define USE_CHANNELS_MIN	2
+#define USE_CHANNELS_MAX	2
+#define USE_RATE		SNDRV_PCM_RATE_48000
+#define USE_RATE_MIN		48000
+#define USE_RATE_MAX		48000
+#endif
+
+
+/* defaults */
+#ifndef MAX_BUFFER_SIZE
+#define MAX_BUFFER_SIZE		(64*1024)
+#endif
+#ifndef USE_FORMATS
+#define USE_FORMATS 		(SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE)
+#endif
+#ifndef USE_RATE
+#define USE_RATE		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000
+#define USE_RATE_MIN		5500
+#define USE_RATE_MAX		48000
+#endif
+#ifndef USE_CHANNELS_MIN
+#define USE_CHANNELS_MIN 	1
+#endif
+#ifndef USE_CHANNELS_MAX
+#define USE_CHANNELS_MAX 	2
+#endif
+#ifndef USE_PERIODS_MIN
+#define USE_PERIODS_MIN 	1
+#endif
+#ifndef USE_PERIODS_MAX
+#define USE_PERIODS_MAX 	1024
+#endif
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+#if 0
+static int enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 0};
+static int pcm_devs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
+static int pcm_substreams[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 8};
+#else
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE;
+static int pcm_devs[SNDRV_CARDS] = SNDDRV_DEFAULT_PCM_DEVS;
+static int pcm_substreams[SNDRV_CARDS] = SNDDRV_DEFAULT_PCM_SUBSTREAMS;
+#endif
+//static int midi_devs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2};
+
+MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(index, "Index value for dummy soundcard.");
+MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC);
+MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+MODULE_PARM_DESC(id, "ID string for dummy soundcard.");
+MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC);
+MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(enable, "Enable this dummy soundcard.");
+MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(pcm_devs, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(pcm_devs, "PCM devices # (0-4) for dummy driver.");
+MODULE_PARM_SYNTAX(pcm_devs, SNDRV_ENABLED ",allows:{{0,4}},default:1,dialog:list");
+MODULE_PARM(pcm_substreams, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(pcm_substreams, "PCM substreams # (1-16) for dummy driver.");
+MODULE_PARM_SYNTAX(pcm_substreams, SNDRV_ENABLED ",allows:{{1,16}},default:8,dialog:list");
+//MODULE_PARM(midi_devs, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+//MODULE_PARM_DESC(midi_devs, "MIDI devices # (0-2) for dummy driver.");
+//MODULE_PARM_SYNTAX(midi_devs, SNDRV_ENABLED ",allows:{{0,2}},default:8,dialog:list");
+#define MIXER_ADDR_MASTER	0
+#define MIXER_ADDR_LINE		1
+#define MIXER_ADDR_MIC		2
+#define MIXER_ADDR_SYNTH	3
+#define MIXER_ADDR_CD		4
+#define MIXER_ADDR_LAST		4
+
+typedef struct snd_card_dummy {
+    snd_card_t *card;
+    spinlock_t mixer_lock;
+    int mixer_volume[MIXER_ADDR_LAST+1][2];
+    int capture_source[MIXER_ADDR_LAST+1][2];
+} snd_card_dummy_t;
+
+typedef struct snd_card_dummy_pcm {
+    snd_card_dummy_t *dummy;
+    spinlock_t lock;
+    struct timer_list timer;
+    unsigned int pcm_size;
+    unsigned int pcm_count;
+    unsigned int pcm_bps;		/* bytes per second */
+    unsigned int pcm_jiffie;	/* bytes per one jiffie */
+    unsigned int pcm_irq_pos;	/* IRQ position */
+    unsigned int pcm_buf_pos;	/* position in buffer */
+    snd_pcm_substream_t *substream;
+} snd_card_dummy_pcm_t;
+
+static snd_card_t *snd_dummy_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+
+
+static int snd_card_dummy_playback_ioctl(snd_pcm_substream_t * substream,
+                                         unsigned int cmd,
+                                         void *arg)
+{
+    return snd_pcm_lib_ioctl(substream, cmd, arg);
+}
+
+static int snd_card_dummy_capture_ioctl(snd_pcm_substream_t * substream,
+                                        unsigned int cmd,
+                                        void *arg)
+{
+    return snd_pcm_lib_ioctl(substream, cmd, arg);
+}
+
+static void snd_card_dummy_pcm_timer_start(snd_pcm_substream_t * substream)
+{
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    snd_card_dummy_pcm_t *dpcm = runtime->private_data;
+
+    dpcm->timer.expires = 1 + jiffies;
+    add_timer(&dpcm->timer);
+}
+
+static void snd_card_dummy_pcm_timer_stop(snd_pcm_substream_t * substream)
+{
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    snd_card_dummy_pcm_t *dpcm = runtime->private_data;
+
+    del_timer(&dpcm->timer);
+}
+
+static int snd_card_dummy_playback_trigger(snd_pcm_substream_t * substream,
+                                           int cmd)
+{
+    if (cmd == SNDRV_PCM_TRIGGER_START) {
+        snd_card_dummy_pcm_timer_start(substream);
+    } else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+        snd_card_dummy_pcm_timer_stop(substream);
+    } else {
+        return -EINVAL;
+    }
+    return 0;
+}
+
+static int snd_card_dummy_capture_trigger(snd_pcm_substream_t * substream,
+                                          int cmd)
+{
+    if (cmd == SNDRV_PCM_TRIGGER_START) {
+        snd_card_dummy_pcm_timer_start(substream);
+    } else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+        snd_card_dummy_pcm_timer_stop(substream);
+    } else {
+        return -EINVAL;
+    }
+    return 0;
+}
+
+static int snd_card_dummy_pcm_prepare(snd_pcm_substream_t * substream)
+{
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    snd_card_dummy_pcm_t *dpcm = runtime->private_data;
+    unsigned int bps;
+
+    bps = runtime->rate * runtime->channels;
+    bps *= snd_pcm_format_width(runtime->format);
+    bps /= 8;
+    if (bps <= 0)
+        return -EINVAL;
+    dpcm->pcm_bps = bps;
+    dpcm->pcm_jiffie = bps / HZ;
+    dpcm->pcm_size = snd_pcm_lib_buffer_bytes(substream);
+    dpcm->pcm_count = snd_pcm_lib_period_bytes(substream);
+    dpcm->pcm_irq_pos = 0;
+    dpcm->pcm_buf_pos = 0;
+    return 0;
+}
+
+static int snd_card_dummy_playback_prepare(snd_pcm_substream_t * substream)
+{
+    return snd_card_dummy_pcm_prepare(substream);
+}
+
+static int snd_card_dummy_capture_prepare(snd_pcm_substream_t * substream)
+{
+    return snd_card_dummy_pcm_prepare(substream);
+}
+
+static void snd_card_dummy_pcm_timer_function(unsigned long data)
+{
+    snd_card_dummy_pcm_t *dpcm = (snd_card_dummy_pcm_t *)data;
+
+    dpcm->timer.expires = 1 + jiffies;
+    add_timer(&dpcm->timer);
+    spin_lock_irq(&dpcm->lock);
+    dpcm->pcm_irq_pos += dpcm->pcm_jiffie;
+    dpcm->pcm_buf_pos += dpcm->pcm_jiffie;
+    dpcm->pcm_buf_pos %= dpcm->pcm_size;
+    if (dpcm->pcm_irq_pos >= dpcm->pcm_count) {
+        dpcm->pcm_irq_pos %= dpcm->pcm_count;
+        snd_pcm_period_elapsed(dpcm->substream);
+    }
+    spin_unlock_irq(&dpcm->lock);
+}
+
+static snd_pcm_uframes_t snd_card_dummy_playback_pointer(snd_pcm_substream_t * substream)
+{
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    snd_card_dummy_pcm_t *dpcm = runtime->private_data;
+
+    return bytes_to_frames(runtime, dpcm->pcm_buf_pos);
+}
+
+static snd_pcm_uframes_t snd_card_dummy_capture_pointer(snd_pcm_substream_t * substream)
+{
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    snd_card_dummy_pcm_t *dpcm = runtime->private_data;
+
+    return bytes_to_frames(runtime, dpcm->pcm_buf_pos);
+}
+
+static snd_pcm_hardware_t snd_card_dummy_playback =
+{
+    /*	info:		  */   (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+                                SNDRV_PCM_INFO_MMAP_VALID),
+                                /*	formats:	  */    USE_FORMATS,
+                                /*	rates:		  */	USE_RATE,
+                                /*	rate_min:	  */	USE_RATE_MIN,
+                                /*	rate_max:	  */	USE_RATE_MAX,
+                                /*	channels_min:	  */	USE_CHANNELS_MIN,
+                                /*	channels_max:	  */	USE_CHANNELS_MAX,
+                                /*	buffer_bytes_max: */	MAX_BUFFER_SIZE,
+                                /*	period_bytes_min: */	64,
+                                /*	period_bytes_max: */	MAX_BUFFER_SIZE,
+                                /*	periods_min:	  */	USE_PERIODS_MIN,
+                                /*	periods_max:	  */	USE_PERIODS_MAX,
+                                /*	fifo_size:	  */	0,
+};
+
+static snd_pcm_hardware_t snd_card_dummy_capture =
+{
+    /*	info:		  */	(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+                                 SNDRV_PCM_INFO_MMAP_VALID),
+                                 /*	formats:	  */    USE_FORMATS,
+                                 /*	rates:		  */	USE_RATE,
+                                 /*	rate_min:	  */	USE_RATE_MIN,
+                                 /*	rate_max:	  */	USE_RATE_MAX,
+                                 /*	channels_min:	  */	USE_CHANNELS_MIN,
+                                 /*	channels_max:	  */	USE_CHANNELS_MAX,
+                                 /*	buffer_bytes_max: */	MAX_BUFFER_SIZE,
+                                 /*	period_bytes_min: */	64,
+                                 /*	period_bytes_max: */	MAX_BUFFER_SIZE,
+                                 /*	periods_min:	  */	USE_PERIODS_MIN,
+                                 /*	periods_max:	  */	USE_PERIODS_MAX,
+                                 /*	fifo_size:	  */	0,
+};
+
+static void snd_card_dummy_runtime_free(snd_pcm_runtime_t *runtime)
+{
+    snd_card_dummy_pcm_t *dpcm = runtime->private_data;
+    kfree(dpcm);
+}
+
+static int snd_card_dummy_playback_open(snd_pcm_substream_t * substream)
+{
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    snd_card_dummy_pcm_t *dpcm;
+
+    dpcm = kcalloc(1, sizeof(*dpcm), GFP_KERNEL);
+    if (dpcm == NULL)
+        return -ENOMEM;
+    if ((runtime->dma_area = snd_malloc_pages_fallback(MAX_BUFFER_SIZE, GFP_KERNEL, &runtime->dma_bytes)) == NULL) {
+        kfree(dpcm);
+        return -ENOMEM;
+    }
+    init_timer(&dpcm->timer);
+    dpcm->timer.data = (unsigned long) dpcm;
+    dpcm->timer.function = snd_card_dummy_pcm_timer_function;
+    spin_lock_init(&dpcm->lock);
+    dpcm->substream = substream;
+    runtime->private_data = dpcm;
+    runtime->private_free = snd_card_dummy_runtime_free;
+    runtime->hw = snd_card_dummy_playback;
+    if (substream->pcm->device & 1) {
+        runtime->hw.info &= ~SNDRV_PCM_INFO_INTERLEAVED;
+        runtime->hw.info |= SNDRV_PCM_INFO_NONINTERLEAVED;
+    }
+    if (substream->pcm->device & 2)
+        runtime->hw.info &= ~(SNDRV_PCM_INFO_MMAP|SNDRV_PCM_INFO_MMAP_VALID);
+    return 0;
+}
+
+static int snd_card_dummy_capture_open(snd_pcm_substream_t * substream)
+{
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    snd_card_dummy_pcm_t *dpcm;
+
+    dpcm = kcalloc(1, sizeof(*dpcm), GFP_KERNEL);
+    if (dpcm == NULL)
+        return -ENOMEM;
+    if ((runtime->dma_area = snd_malloc_pages_fallback(MAX_BUFFER_SIZE, GFP_KERNEL, &runtime->dma_bytes)) == NULL) {
+        kfree(dpcm);
+        return -ENOMEM;
+    }
+    memset(runtime->dma_area, 0, runtime->dma_bytes);
+    init_timer(&dpcm->timer);
+    dpcm->timer.data = (unsigned long) dpcm;
+    dpcm->timer.function = snd_card_dummy_pcm_timer_function;
+    spin_lock_init(&dpcm->lock);
+    dpcm->substream = substream;
+    runtime->private_data = dpcm;
+    runtime->private_free = snd_card_dummy_runtime_free;
+    runtime->hw = snd_card_dummy_capture;
+    if (substream->pcm->device == 1) {
+        runtime->hw.info &= ~SNDRV_PCM_INFO_INTERLEAVED;
+        runtime->hw.info |= SNDRV_PCM_INFO_NONINTERLEAVED;
+    }
+    if (substream->pcm->device & 2)
+        runtime->hw.info &= ~(SNDRV_PCM_INFO_MMAP|SNDRV_PCM_INFO_MMAP_VALID);
+    return 0;
+}
+
+static int snd_card_dummy_playback_close(snd_pcm_substream_t * substream)
+{
+    snd_pcm_runtime_t *runtime = substream->runtime;
+
+    snd_free_pages(runtime->dma_area, runtime->dma_bytes);
+    return 0;
+}
+
+static int snd_card_dummy_capture_close(snd_pcm_substream_t * substream)
+{
+    snd_pcm_runtime_t *runtime = substream->runtime;
+
+    snd_free_pages(runtime->dma_area, runtime->dma_bytes);
+    return 0;
+}
+
+static snd_pcm_ops_t snd_card_dummy_playback_ops = {
+    /*	open:		*/	snd_card_dummy_playback_open,
+    /*	close:		*/	snd_card_dummy_playback_close,
+    /*	ioctl:		*/	snd_card_dummy_playback_ioctl,
+    NULL, NULL,
+    /*	prepare:	*/	snd_card_dummy_playback_prepare,
+    /*	trigger:	*/	snd_card_dummy_playback_trigger,
+    /*	pointer:	*/	snd_card_dummy_playback_pointer,
+    NULL, NULL
+};
+
+static snd_pcm_ops_t snd_card_dummy_capture_ops = {
+    /*	open:		*/	snd_card_dummy_capture_open,
+    /*	close:		*/	snd_card_dummy_capture_close,
+    /*	ioctl:		*/	snd_card_dummy_capture_ioctl,
+    NULL, NULL,
+    /*	prepare:	*/	snd_card_dummy_capture_prepare,
+    /*	trigger:	*/	snd_card_dummy_capture_trigger,
+    /*	pointer:	*/	snd_card_dummy_capture_pointer,
+    NULL, NULL
+};
+
+static int __init snd_card_dummy_pcm(snd_card_dummy_t *dummy, int device, int substreams)
+{
+    snd_pcm_t *pcm;
+    int err;
+
+    if ((err = snd_pcm_new(dummy->card, "Dummy PCM", device, substreams, substreams, &pcm)) < 0)
+        return err;
+    snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_card_dummy_playback_ops);
+    snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_card_dummy_capture_ops);
+    pcm->private_data = dummy;
+    pcm->info_flags = 0;
+    strcpy(pcm->name, "Dummy PCM");
+    return 0;
+}
+#define DUMMY_VOLUME(xname, xindex, addr) \
+    { SNDRV_CTL_ELEM_IFACE_MIXER, 0, 0, xname, xindex, \
+    0, 0, snd_dummy_volume_info, \
+    snd_dummy_volume_get, snd_dummy_volume_put, \
+    addr }
+
+static int snd_dummy_volume_info(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+    uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+    uinfo->count = 2;
+    uinfo->value.integer.min = -50;
+    uinfo->value.integer.max = 100;
+    return 0;
+}
+
+static int snd_dummy_volume_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+    snd_card_dummy_t *dummy = snd_kcontrol_chip(kcontrol);
+    unsigned long flags;
+    int addr = kcontrol->private_value;
+
+    spin_lock_irqsave(&dummy->mixer_lock, flags);
+    ucontrol->value.integer.value[0] = dummy->mixer_volume[addr][0];
+    ucontrol->value.integer.value[1] = dummy->mixer_volume[addr][1];
+    spin_unlock_irqrestore(&dummy->mixer_lock, flags);
+    return 0;
+}
+
+static int snd_dummy_volume_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+    snd_card_dummy_t *dummy = snd_kcontrol_chip(kcontrol);
+    unsigned long flags;
+    int change, addr = kcontrol->private_value;
+    int left, right;
+
+    left = ucontrol->value.integer.value[0];
+    if (left < -50)
+        left = -50;
+    if (left > 100)
+        left = 100;
+    right = ucontrol->value.integer.value[1];
+    if (right < -50)
+        right = -50;
+    if (right > 100)
+        right = 100;
+    spin_lock_irqsave(&dummy->mixer_lock, flags);
+    change = dummy->mixer_volume[addr][0] != left ||
+        dummy->mixer_volume[addr][1] != right;
+    dummy->mixer_volume[addr][0] = left;
+    dummy->mixer_volume[addr][1] = right;
+    spin_unlock_irqrestore(&dummy->mixer_lock, flags);
+    return change;
+}
+
+#define DUMMY_CAPSRC(xname, xindex, addr) \
+    { SNDRV_CTL_ELEM_IFACE_MIXER, 0, 0, xname, xindex, \
+    0, 0, snd_dummy_capsrc_info, \
+    snd_dummy_capsrc_get, snd_dummy_capsrc_put, \
+    addr }
+
+static int snd_dummy_capsrc_info(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+    uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+    uinfo->count = 2;
+    uinfo->value.integer.min = 0;
+    uinfo->value.integer.max = 1;
+    return 0;
+}
+
+static int snd_dummy_capsrc_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+    snd_card_dummy_t *dummy = snd_kcontrol_chip(kcontrol);
+    unsigned long flags;
+    int addr = kcontrol->private_value;
+
+    spin_lock_irqsave(&dummy->mixer_lock, flags);
+    ucontrol->value.integer.value[0] = dummy->capture_source[addr][0];
+    ucontrol->value.integer.value[1] = dummy->capture_source[addr][1];
+    spin_unlock_irqrestore(&dummy->mixer_lock, flags);
+    return 0;
+}
+
+static int snd_dummy_capsrc_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+    snd_card_dummy_t *dummy = snd_kcontrol_chip(kcontrol);
+    unsigned long flags;
+    int change, addr = kcontrol->private_value;
+    int left, right;
+
+    left = ucontrol->value.integer.value[0] & 1;
+    right = ucontrol->value.integer.value[1] & 1;
+    spin_lock_irqsave(&dummy->mixer_lock, flags);
+    change = dummy->capture_source[addr][0] != left &&
+        dummy->capture_source[addr][1] != right;
+    dummy->capture_source[addr][0] = left;
+    dummy->capture_source[addr][1] = right;
+    spin_unlock_irqrestore(&dummy->mixer_lock, flags);
+    return change;
+}
+
+#define DUMMY_CONTROLS (sizeof(snd_dummy_controls)/sizeof(snd_kcontrol_new_t))
+
+static snd_kcontrol_new_t snd_dummy_controls[] = {
+    DUMMY_VOLUME("Master Volume", 0, MIXER_ADDR_MASTER),
+    DUMMY_CAPSRC("Master Capture Switch", 0, MIXER_ADDR_MASTER),
+    DUMMY_VOLUME("Synth Volume", 0, MIXER_ADDR_SYNTH),
+    DUMMY_CAPSRC("Synth Capture Switch", 0, MIXER_ADDR_MASTER),
+    DUMMY_VOLUME("Line Volume", 0, MIXER_ADDR_LINE),
+    DUMMY_CAPSRC("Line Capture Switch", 0, MIXER_ADDR_MASTER),
+    DUMMY_VOLUME("Mic Volume", 0, MIXER_ADDR_MIC),
+    DUMMY_CAPSRC("Mic Capture Switch", 0, MIXER_ADDR_MASTER),
+    DUMMY_VOLUME("CD Volume", 0, MIXER_ADDR_CD),
+    DUMMY_CAPSRC("CD Capture Switch", 0, MIXER_ADDR_MASTER)
+};
+
+int __init snd_card_dummy_new_mixer(snd_card_dummy_t * dummy)
+{
+    snd_card_t *card = dummy->card;
+    unsigned int idx;
+    int err;
+
+    snd_assert(dummy != NULL, return -EINVAL);
+    spin_lock_init(&dummy->mixer_lock);
+    strcpy(card->mixername, "Dummy Mixer");
+
+    for (idx = 0; idx < DUMMY_CONTROLS; idx++) {
+        if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_dummy_controls[idx], dummy))) < 0)
+            return err;
+    }
+    return 0;
+}
+
+static int __init snd_card_dummy_probe(int dev)
+{
+    snd_card_t *card;
+    struct snd_card_dummy *dummy;
+    int idx, err;
+
+    if (!enable[dev])
+        return -ENODEV;
+    card = snd_card_new(index[dev], id[dev], THIS_MODULE,
+                        sizeof(struct snd_card_dummy));
+    if (card == NULL)
+        return -ENOMEM;
+    dummy = (struct snd_card_dummy *)card->private_data;
+    dummy->card = card;
+    for (idx = 0; idx < MAX_PCM_DEVICES && idx < pcm_devs[dev]; idx++) {
+        if (pcm_substreams[dev] < 1)
+            pcm_substreams[dev] = 1;
+        if (pcm_substreams[dev] > MAX_PCM_SUBSTREAMS)
+            pcm_substreams[dev] = MAX_PCM_SUBSTREAMS;
+        if ((err = snd_card_dummy_pcm(dummy, idx, pcm_substreams[dev])) < 0)
+            goto __nodev;
+    }
+    if ((err = snd_card_dummy_new_mixer(dummy)) < 0)
+        goto __nodev;
+    strcpy(card->driver, "Dummy");
+    strcpy(card->shortname, "Dummy");
+    sprintf(card->longname, "Dummy %i", dev + 1);
+    if ((err = snd_card_register(card)) == 0) {
+        snd_dummy_cards[dev] = card;
+        return 0;
+    }
+__nodev:
+    snd_card_free(card);
+    return err;
+}
+
+static int __init alsa_card_dummy_init(void)
+{
+    int dev, cards;
+
+    for (dev = cards = 0; dev < SNDRV_CARDS && enable[dev]; dev++) {
+        if (snd_card_dummy_probe(dev) < 0) {
+#ifdef MODULE
+            snd_printk("Dummy soundcard #%i not found or device busy\n", dev + 1);
+#endif
+            break;
+        }
+        cards++;
+    }
+    if (!cards) {
+#ifdef MODULE
+        snd_printk("Dummy soundcard not found or device busy\n");
+#endif
+        return -ENODEV;
+    }
+    return 0;
+}
+
+static void __exit alsa_card_dummy_exit(void)
+{
+    int idx;
+
+    for (idx = 0; idx < SNDRV_CARDS; idx++)
+        snd_card_free(snd_dummy_cards[idx]);
+}
+
+module_init(alsa_card_dummy_init)
+module_exit(alsa_card_dummy_exit)
+
+#ifndef MODULE
+
+/* format is: snd-card-dummy=snd_enable,snd_index,snd_id,
+ snd_pcm_devs,snd_pcm_substreams */
+
+static int __init alsa_card_dummy_setup(char *str)
+{
+    static unsigned __initdata nr_dev = 0;
+
+    if (nr_dev >= SNDRV_CARDS)
+        return 0;
+    (void)(get_option(&str,&enable[nr_dev]) == 2 &&
+           get_option(&str,&index[nr_dev]) == 2 &&
+           get_id(&str,&id[nr_dev]) == 2 &&
+           get_option(&str,&pcm_devs[nr_dev]) == 2 &&
+           get_option(&str,&pcm_substreams[nr_dev]) == 2);
+    nr_dev++;
+    return 1;
+}
+
+__setup("snd-dummy=", alsa_card_dummy_setup);
+
+#endif /* ifndef MODULE */
Index: /GPL/branches/alsa-resync1/alsa-kernel/drivers/makefile.os2
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/drivers/makefile.os2	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/drivers/makefile.os2	(revision 91)
@@ -0,0 +1,87 @@
+CINCLUDES = ..\..\include;..\include;$(%WATCOM)\H
+AINCLUDES = $(DDK_BASE)\H
+
+CDEFINES = -D__KERNEL__ -DMODULE -dTARGET_OS2 -dALSA_BUILD -D__ISAPNP__ -DCONFIG_ISAPNP -D__i386_
+
+32BIT=1
+!include ..\..\..\makefile.inc
+
+NMAKEOPTS=-f makefile.os2 $(LOGO)
+
+!if "$(DEBUG)" == "1"
+NMAKEOPTS += DEBUG=1
+!endif
+
+!if "$(KEE)" == "0"
+NMAKEOPTS += KEE=0
+KEE=0
+!else
+NMAKEOPTS += KEE=1
+KEE=1
+!endif
+
+!if "$(DEBUG)" == "1"
+!Message BUILDING DEBUG VERSION
+!else
+!Message BUILDING RELEASE VERSION
+!endif
+
+#===================================================================
+#
+#   List of source files
+#
+#===================================================================
+FILE1    = dummy.obj virmidi.obj serial-u16550.obj
+FILE2    = 
+FILE3    = 
+FILE4    = 
+FILE5    = 
+FILE6    = 
+FILE7    = 
+FILE8    = 
+FILELAST =
+FILES    = $(FILE0) $(FILE1) $(FILE2) $(FILE3) $(FILE4) $(FILE5) $(FILE6) $(FILE7) $(FILE8) $(FILE9) $(FILE10) $(FILE11) $(FILE12)
+
+TARGET   = drivers
+
+LNKFILE  = $(OBJDIR)\$(TARGET).lnk
+
+
+#===================================================================
+#
+#   Specific dependencies
+#
+#===================================================================
+all: $(OBJDIR)\$(TARGET).lib subdirs
+
+$(LNKFILE): makefile.os2
+    @%create $(LNKFILE)
+    @for %f in ($(FILES)) do @%append $(LNKFILE) -+$(OBJDIR)\%f
+
+$(OBJDIR)\$(TARGET).lib: $(LNKFILE) $(FILES) 
+    $(LIB) $(OBJDIR)\$(TARGET).lib @$(LNKFILE)
+    copy $(OBJDIR)\$(TARGET).lib $(ALSA_LIB)
+
+subdirs: .SYMBOLIC
+    @echo BUILDING opl3
+    @cd opl3
+    @wmake $(NMAKEOPTS)
+    @cd ..
+    @echo BUILDING mpu401
+    @cd mpu401
+    @wmake $(NMAKEOPTS)
+    @cd ..
+
+clean: .SYMBOLIC
+    @echo CLEANING opl3
+    @cd opl3
+    @wmake $(NMAKEOPTS) clean
+    @cd ..
+    @echo CLEANING mpu401
+    @cd mpu401
+    @wmake $(NMAKEOPTS) clean
+    @cd ..
+    -@rm  *.err 2>nul
+    @cd $(OBJDIR)
+    -rm *.obj *.lnk *.sys *.sym *.lst *.map *.wmap *.lib 2>nul
+    @cd ../..
Index: /GPL/branches/alsa-resync1/alsa-kernel/drivers/mpu401/makefile.os2
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/drivers/mpu401/makefile.os2	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/drivers/mpu401/makefile.os2	(revision 91)
@@ -0,0 +1,52 @@
+CINCLUDES = ..\..\..\include;..\..\include;$(%WATCOM)\H
+AINCLUDES = $(DDK_BASE)\H
+
+CDEFINES = -D__KERNEL__ -DMODULE -dTARGET_OS2 -dALSA_BUILD -D__ISAPNP__ -DCONFIG_ISAPNP -D__i386_
+
+32BIT=1
+!include ..\..\..\..\makefile.inc
+
+
+#===================================================================
+#
+#   List of source files
+#
+#===================================================================
+FILE1    = mpu401.obj mpu401_uart.obj
+FILE2    = 
+FILE3    = 
+FILE4    = 
+FILE5    = 
+FILE6    = 
+FILE7    = 
+FILE8    = 
+FILELAST =
+FILES    = $(FILE0) $(FILE1) $(FILE2) $(FILE3) $(FILE4) $(FILE5) $(FILE6) $(FILE7) $(FILE8) $(FILE9) $(FILE10) $(FILE11) $(FILE12)
+
+TARGET   = mpu401
+
+LNKFILE  = $(OBJDIR)\$(TARGET).lnk
+
+
+#===================================================================
+#
+#   Specific dependencies
+#
+#===================================================================
+all: $(OBJDIR)\$(TARGET).lib
+
+
+$(LNKFILE): makefile.os2
+    @%create $(LNKFILE)
+    @for %f in ($(FILES)) do @%append $(LNKFILE) -+$(OBJDIR)\%f
+
+$(OBJDIR)\$(TARGET).lib: $(LNKFILE) $(FILES)
+    $(LIB) $(OBJDIR)\$(TARGET).lib @$(LNKFILE)
+    copy $(OBJDIR)\$(TARGET).lib $(ALSA_LIB)
+
+
+clean: .SYMBOLIC
+    -@rm  *.err 2>nul
+    @cd $(OBJDIR)
+    -rm *.obj *.lnk *.sys *.sym *.lst *.map *.wmap *.lib 2>nul
+    @cd ../..
Index: /GPL/branches/alsa-resync1/alsa-kernel/drivers/mpu401/mpu401.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/drivers/mpu401/mpu401.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/drivers/mpu401/mpu401.c	(revision 91)
@@ -0,0 +1,163 @@
+/*
+ *  Driver for generic MPU-401 boards (UART mode only)
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#define SNDRV_MAIN_OBJECT_FILE
+#include <sound/driver.h>
+#include <sound/mpu401.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("MPU-401 UART");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE;	/* Enable this card */
+static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* MPU-401 port number */
+static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* MPU-401 IRQ */
+#ifdef CONFIG_X86_PC9800
+static int pc98ii[SNDRV_CARDS];				/* PC98-II dauther board */
+#endif
+
+MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(index, "Index value for MPU-401 device.");
+MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC);
+MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+MODULE_PARM_DESC(id, "ID string for MPU-401 device.");
+MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC);
+MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(enable, "Enable MPU-401 device.");
+MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(port, "Port # for MPU-401 device.");
+MODULE_PARM_SYNTAX(port, SNDRV_PORT12_DESC);
+MODULE_PARM(irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(irq, "IRQ # for MPU-401 device.");
+MODULE_PARM_SYNTAX(irq, SNDRV_IRQ_DESC);
+#ifdef CONFIG_X86_PC9800
+MODULE_PARM(pc98ii, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(pc98ii, "Roland MPU-PC98II support.");
+MODULE_PARM_SYNTAX(pc98ii, SNDRV_BOOLEAN_FALSE_DESC);
+#endif
+
+static snd_card_t *snd_mpu401_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+
+static int __init snd_card_mpu401_probe(int dev)
+{
+	snd_card_t *card;
+	int err;
+
+	if (port[dev] == SNDRV_AUTO_PORT) {
+		snd_printk("specify port\n");
+		return -EINVAL;
+	}
+	if (irq[dev] == SNDRV_AUTO_IRQ) {
+		snd_printk("specify or disable IRQ port\n");
+		return -EINVAL;
+	}
+
+	card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+	if (card == NULL)
+		return -ENOMEM;
+	if (snd_mpu401_uart_new(card, 0,
+#ifdef CONFIG_X86_PC9800
+				pc98ii[dev] ? MPU401_HW_PC98II :
+#endif
+				MPU401_HW_MPU401,
+				port[dev], 0,
+				irq[dev], irq[dev] >= 0 ? SA_INTERRUPT : 0, NULL) < 0) {
+		printk(KERN_ERR "MPU401 not detected at 0x%lx\n", port[dev]);
+		snd_card_free(card);
+		return -ENODEV;
+	}
+	strcpy(card->driver, "MPU-401 UART");
+	strcpy(card->shortname, card->driver);
+	sprintf(card->longname, "%s at 0x%lx, ", card->shortname, port[dev]);
+	if (irq[dev] >= 0) {
+		sprintf(card->longname + strlen(card->longname), "IRQ %d", irq[dev]);
+	} else {
+		strcat(card->longname, "polled");
+	}
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	snd_mpu401_cards[dev] = card;
+	return 0;
+}
+
+static int __init alsa_card_mpu401_init(void)
+{
+	int dev, cards = 0;
+
+	for (dev = 0; dev < SNDRV_CARDS; dev++) {
+		if (!enable[dev])
+			continue;
+		if (snd_card_mpu401_probe(dev) >= 0)
+			cards++;
+	}
+	if (!cards) {
+#ifdef MODULE
+		snd_printk("MPU-401 device not found or device busy\n");
+#endif
+		return -ENODEV;
+	}
+	return 0;
+}
+
+static void __exit alsa_card_mpu401_exit(void)
+{
+	int idx;
+
+	for (idx = 0; idx < SNDRV_CARDS; idx++)
+		snd_card_free(snd_mpu401_cards[idx]);
+}
+
+module_init(alsa_card_mpu401_init)
+module_exit(alsa_card_mpu401_exit)
+
+#ifndef MODULE
+
+/* format is: snd-mpu401=enable,index,id,port,irq */
+
+static int __init alsa_card_mpu401_setup(char *str)
+{
+	static unsigned __initdata nr_dev = 0;
+
+	if (nr_dev >= SNDRV_CARDS)
+		return 0;
+	(void)(get_option(&str,&enable[nr_dev]) == 2 &&
+	       get_option(&str,&index[nr_dev]) == 2 &&
+	       get_id(&str,&id[nr_dev]) == 2 &&
+#ifdef CONFIG_X86_PC9800
+	       get_option(&str,&pc98ii[nr_dev]) == 2 &&
+#endif
+	       get_option_long(&str,&port[nr_dev]) == 2 &&
+	       get_option(&str,&irq[nr_dev]) == 2);
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-mpu401=", alsa_card_mpu401_setup);
+
+#endif /* ifndef MODULE */
Index: /GPL/branches/alsa-resync1/alsa-kernel/drivers/mpu401/mpu401_uart.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/drivers/mpu401/mpu401_uart.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/drivers/mpu401/mpu401_uart.c	(revision 91)
@@ -0,0 +1,582 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  Routines for control of MPU-401 in UART mode
+ *
+ *  MPU-401 supports UART mode which is not capable generate transmit
+ *  interrupts thus output is done via polling. Also, if irq < 0, then
+ *  input is done also via polling. Do not expect good performance.
+ *
+ *
+ *   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
+ *
+ *   13-03-2003:
+ *      Added support for different kind of hardware I/O. Build in choices
+ *      are port and mmio. For other kind of I/O, set mpu->read and
+ *      mpu->write to your own I/O functions.
+ *
+ */
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/errno.h>
+#include <sound/core.h>
+#include <sound/mpu401.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Routines for control of MPU-401 in UART mode");
+MODULE_LICENSE("GPL");
+
+static void snd_mpu401_uart_input_read(struct snd_mpu401 * mpu);
+static void snd_mpu401_uart_output_write(struct snd_mpu401 * mpu);
+
+/*
+
+ */
+
+#define snd_mpu401_input_avail(mpu)	(!(mpu->read(mpu, MPU401C(mpu)) & 0x80))
+#define snd_mpu401_output_ready(mpu)	(!(mpu->read(mpu, MPU401C(mpu)) & 0x40))
+
+#define MPU401_RESET		0xff
+#define MPU401_ENTER_UART	0x3f
+#define MPU401_ACK		0xfe
+
+/* Build in lowlevel io */
+static void mpu401_write_port(struct snd_mpu401 *mpu, unsigned char data, unsigned long addr)
+{
+	outb(data, addr);
+}
+
+static unsigned char mpu401_read_port(struct snd_mpu401 *mpu, unsigned long addr)
+{
+	return inb(addr);
+}
+
+static void mpu401_write_mmio(struct snd_mpu401 *mpu, unsigned char data, unsigned long addr)
+{
+	writeb(data, (void __iomem *)addr);
+}
+
+static unsigned char mpu401_read_mmio(struct snd_mpu401 *mpu, unsigned long addr)
+{
+	return readb((void __iomem *)addr);
+}
+/*  */
+
+static void snd_mpu401_uart_clear_rx(struct snd_mpu401 *mpu)
+{
+	int timeout = 100000;
+	for (; timeout > 0 && snd_mpu401_input_avail(mpu); timeout--)
+		mpu->read(mpu, MPU401D(mpu));
+#ifdef CONFIG_SND_DEBUG
+	if (timeout <= 0)
+		snd_printk("cmd: clear rx timeout (status = 0x%x)\n", mpu->read(mpu, MPU401C(mpu)));
+#endif
+}
+
+static void uart_interrupt_tx(struct snd_mpu401 *mpu)
+{
+    if (test_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode) &&
+        test_bit(MPU401_MODE_BIT_OUTPUT_TRIGGER, &mpu->mode)) {
+        spin_lock(&mpu->output_lock);
+        snd_mpu401_uart_output_write(mpu);
+        spin_unlock(&mpu->output_lock);
+    }
+}
+
+static void _snd_mpu401_uart_interrupt(struct snd_mpu401 *mpu)
+{
+    if (mpu->info_flags & MPU401_INFO_INPUT) {
+        spin_lock(&mpu->input_lock);
+        if (test_bit(MPU401_MODE_BIT_INPUT, &mpu->mode))
+            snd_mpu401_uart_input_read(mpu);
+        else
+            snd_mpu401_uart_clear_rx(mpu);
+        spin_unlock(&mpu->input_lock);
+    }
+    if (! (mpu->info_flags & MPU401_INFO_TX_IRQ))
+        /* ok. for better Tx performance try do some output
+         when input is done */
+        uart_interrupt_tx(mpu);
+}
+
+/**
+ * snd_mpu401_uart_interrupt - generic MPU401-UART interrupt handler
+ * @irq: the irq number
+ * @dev_id: mpu401 instance
+ * @regs: the reigster
+ *
+ * Processes the interrupt for MPU401-UART i/o.
+ */
+irqreturn_t snd_mpu401_uart_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct snd_mpu401 *mpu = dev_id;
+	
+	if (mpu == NULL)
+		return IRQ_NONE;
+	_snd_mpu401_uart_interrupt(mpu);
+	return IRQ_HANDLED;
+}
+
+/**
+ * snd_mpu401_uart_interrupt_tx - generic MPU401-UART transmit irq handler
+ * @irq: the irq number
+ * @dev_id: mpu401 instance
+ * @regs: the reigster
+ *
+ * Processes the interrupt for MPU401-UART output.
+ */
+irqreturn_t snd_mpu401_uart_interrupt_tx(int irq, void *dev_id,
+					 struct pt_regs *regs)
+{
+    struct snd_mpu401 *mpu = dev_id;
+
+    if (mpu == NULL)
+        return IRQ_NONE;
+    uart_interrupt_tx(mpu);
+    return IRQ_HANDLED;
+}
+
+EXPORT_SYMBOL(snd_mpu401_uart_interrupt_tx);
+
+/*
+ * timer callback
+ * reprogram the timer and call the interrupt job
+ */
+static void snd_mpu401_uart_timer(unsigned long data)
+{
+	struct snd_mpu401 *mpu = (struct snd_mpu401 *)data;
+
+	spin_lock(&mpu->timer_lock);
+	/*mpu->mode |= MPU401_MODE_TIMER;*/
+	mpu->timer.expires = 1 + jiffies;
+	add_timer(&mpu->timer);
+	spin_unlock(&mpu->timer_lock);
+	if (mpu->rmidi)
+		_snd_mpu401_uart_interrupt(mpu);
+}
+
+/*
+ * initialize the timer callback if not programmed yet
+ */
+static void snd_mpu401_uart_add_timer (struct snd_mpu401 *mpu, int input)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave (&mpu->timer_lock, flags);
+	if (mpu->timer_invoked == 0) {
+		init_timer(&mpu->timer);
+		mpu->timer.data = (unsigned long)mpu;
+		mpu->timer.function = snd_mpu401_uart_timer;
+		mpu->timer.expires = 1 + jiffies;
+		add_timer(&mpu->timer);
+	} 
+	mpu->timer_invoked |= input ? MPU401_MODE_INPUT_TIMER : MPU401_MODE_OUTPUT_TIMER;
+	spin_unlock_irqrestore (&mpu->timer_lock, flags);
+}
+
+/*
+ * remove the timer callback if still active
+ */
+static void snd_mpu401_uart_remove_timer (struct snd_mpu401 *mpu, int input)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave (&mpu->timer_lock, flags);
+	if (mpu->timer_invoked) {
+		mpu->timer_invoked &= input ? ~MPU401_MODE_INPUT_TIMER : ~MPU401_MODE_OUTPUT_TIMER;
+		if (! mpu->timer_invoked)
+			del_timer(&mpu->timer);
+	}
+	spin_unlock_irqrestore (&mpu->timer_lock, flags);
+}
+
+/*
+
+ */
+
+static void snd_mpu401_uart_cmd(struct snd_mpu401 * mpu, unsigned char cmd, int ack)
+{
+	unsigned long flags;
+	int timeout, ok;
+
+	spin_lock_irqsave(&mpu->input_lock, flags);
+	if (mpu->hardware != MPU401_HW_TRID4DWAVE) {
+		mpu->write(mpu, 0x00, MPU401D(mpu));
+		/*snd_mpu401_uart_clear_rx(mpu);*/
+	}
+	/* ok. standard MPU-401 initialization */
+	if (mpu->hardware != MPU401_HW_SB) {
+		for (timeout = 1000; timeout > 0 && !snd_mpu401_output_ready(mpu); timeout--)
+			udelay(10);
+#ifdef CONFIG_SND_DEBUG
+		if (!timeout)
+			snd_printk("cmd: tx timeout (status = 0x%x)\n", mpu->read(mpu, MPU401C(mpu)));
+#endif
+	}
+	mpu->write(mpu, cmd, MPU401C(mpu));
+	if (ack) {
+		ok = 0;
+		timeout = 10000;
+		while (!ok && timeout-- > 0) {
+			if (snd_mpu401_input_avail(mpu)) {
+				if (mpu->read(mpu, MPU401D(mpu)) == MPU401_ACK)
+					ok = 1;
+			}
+		}
+		if (!ok && mpu->read(mpu, MPU401D(mpu)) == MPU401_ACK)
+			ok = 1;
+	} else {
+		ok = 1;
+	}
+	spin_unlock_irqrestore(&mpu->input_lock, flags);
+	if (! ok)
+		snd_printk("cmd: 0x%x failed at 0x%lx (status = 0x%x, data = 0x%x)\n", cmd, mpu->port, mpu->read(mpu, MPU401C(mpu)), mpu->read(mpu, MPU401D(mpu)));
+	// snd_printk("cmd: 0x%x at 0x%lx (status = 0x%x, data = 0x%x)\n", cmd, mpu->port, mpu->read(mpu, MPU401C(mpu)), mpu->read(mpu, MPU401D(mpu)));
+}
+
+/*
+ * input/output open/close - protected by open_mutex in rawmidi.c
+ */
+static int snd_mpu401_uart_input_open(struct snd_rawmidi_substream *substream)
+{
+	struct snd_mpu401 *mpu;
+	int err;
+
+	mpu = substream->rmidi->private_data;
+	if (mpu->open_input && (err = mpu->open_input(mpu)) < 0)
+		return err;
+	if (! test_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode)) {
+		snd_mpu401_uart_cmd(mpu, MPU401_RESET, 1);
+		snd_mpu401_uart_cmd(mpu, MPU401_ENTER_UART, 1);
+	}
+	mpu->substream_input = substream;
+	set_bit(MPU401_MODE_BIT_INPUT, &mpu->mode);
+	return 0;
+}
+
+static int snd_mpu401_uart_output_open(struct snd_rawmidi_substream *substream)
+{
+	struct snd_mpu401 *mpu;
+	int err;
+
+	mpu = substream->rmidi->private_data;
+	if (mpu->open_output && (err = mpu->open_output(mpu)) < 0)
+		return err;
+	if (! test_bit(MPU401_MODE_BIT_INPUT, &mpu->mode)) {
+		snd_mpu401_uart_cmd(mpu, MPU401_RESET, 1);
+		snd_mpu401_uart_cmd(mpu, MPU401_ENTER_UART, 1);
+	}
+	mpu->substream_output = substream;
+	set_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode);
+	return 0;
+}
+
+static int snd_mpu401_uart_input_close(struct snd_rawmidi_substream *substream)
+{
+	struct snd_mpu401 *mpu;
+
+	mpu = substream->rmidi->private_data;
+	clear_bit(MPU401_MODE_BIT_INPUT, &mpu->mode);
+	mpu->substream_input = NULL;
+	if (! test_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode))
+		snd_mpu401_uart_cmd(mpu, MPU401_RESET, 0);
+	if (mpu->close_input)
+		mpu->close_input(mpu);
+	return 0;
+}
+
+static int snd_mpu401_uart_output_close(struct snd_rawmidi_substream *substream)
+{
+	struct snd_mpu401 *mpu;
+
+	mpu = substream->rmidi->private_data;
+	clear_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode);
+	mpu->substream_output = NULL;
+	if (! test_bit(MPU401_MODE_BIT_INPUT, &mpu->mode))
+		snd_mpu401_uart_cmd(mpu, MPU401_RESET, 0);
+	if (mpu->close_output)
+		mpu->close_output(mpu);
+	return 0;
+}
+
+/*
+ * trigger input callback
+ */
+static void snd_mpu401_uart_input_trigger(struct snd_rawmidi_substream *substream, int up)
+{
+	unsigned long flags;
+	struct snd_mpu401 *mpu;
+	int max = 64;
+
+	mpu = substream->rmidi->private_data;
+	if (up) {
+		if (! test_and_set_bit(MPU401_MODE_BIT_INPUT_TRIGGER, &mpu->mode)) {
+			/* first time - flush FIFO */
+			while (max-- > 0)
+				mpu->read(mpu, MPU401D(mpu));
+			if (mpu->irq < 0)
+				snd_mpu401_uart_add_timer(mpu, 1);
+		}
+		
+		/* read data in advance */
+		spin_lock_irqsave(&mpu->input_lock, flags);
+		snd_mpu401_uart_input_read(mpu);
+		spin_unlock_irqrestore(&mpu->input_lock, flags);
+	} else {
+		if (mpu->irq < 0)
+			snd_mpu401_uart_remove_timer(mpu, 1);
+		clear_bit(MPU401_MODE_BIT_INPUT_TRIGGER, &mpu->mode);
+	}
+}
+
+/*
+ * transfer input pending data
+ * call with input_lock spinlock held
+ */
+static void snd_mpu401_uart_input_read(struct snd_mpu401 * mpu)
+{
+	int max = 128;
+	unsigned char byte;
+
+        while (max-- > 0) {
+            if (! snd_mpu401_input_avail(mpu))
+                break; /* input not available */
+            byte = mpu->read(mpu, MPU401D(mpu));
+            if (test_bit(MPU401_MODE_BIT_INPUT_TRIGGER, &mpu->mode))
+                snd_rawmidi_receive(mpu->substream_input, &byte, 1);
+	}
+}
+
+/*
+ *  Tx FIFO sizes:
+ *    CS4237B			- 16 bytes
+ *    AudioDrive ES1688         - 12 bytes
+ *    S3 SonicVibes             -  8 bytes
+ *    SoundBlaster AWE 64       -  2 bytes (ugly hardware)
+ */
+
+/*
+ * write output pending bytes
+ * call with output_lock spinlock held
+ */
+static void snd_mpu401_uart_output_write(struct snd_mpu401 * mpu)
+{
+	unsigned char byte;
+	int max = 256, timeout;
+
+        do {
+            if (snd_rawmidi_transmit_peek(mpu->substream_output,
+                                          &byte, 1) == 1) {
+                for (timeout = 100; timeout > 0; timeout--) {
+                    if (snd_mpu401_output_ready(mpu))
+                        break;
+                }
+                if (timeout == 0)
+                    break;	/* Tx FIFO full - try again later */
+                mpu->write(mpu, byte, MPU401D(mpu));
+                snd_rawmidi_transmit_ack(mpu->substream_output, 1);
+            } else {
+                snd_mpu401_uart_remove_timer (mpu, 0);
+                break;	/* no other data - leave the tx loop */
+            }
+	} while (--max > 0);
+}
+
+/*
+ * output trigger callback
+ */
+static void snd_mpu401_uart_output_trigger(struct snd_rawmidi_substream *substream, int up)
+{
+	unsigned long flags;
+	struct snd_mpu401 *mpu;
+
+	mpu = substream->rmidi->private_data;
+	if (up) {
+		set_bit(MPU401_MODE_BIT_OUTPUT_TRIGGER, &mpu->mode);
+
+		/* try to add the timer at each output trigger,
+		 * since the output timer might have been removed in
+		 * snd_mpu401_uart_output_write().
+                 */
+                if (! (mpu->info_flags & MPU401_INFO_TX_IRQ))
+                    snd_mpu401_uart_add_timer(mpu, 0);
+		/* output pending data */
+		spin_lock_irqsave(&mpu->output_lock, flags);
+		snd_mpu401_uart_output_write(mpu);
+		spin_unlock_irqrestore(&mpu->output_lock, flags);
+        } else {
+            if (! (mpu->info_flags & MPU401_INFO_TX_IRQ))
+                snd_mpu401_uart_remove_timer(mpu, 0);
+            clear_bit(MPU401_MODE_BIT_OUTPUT_TRIGGER, &mpu->mode);
+	}
+}
+
+/*
+
+ */
+
+static struct snd_rawmidi_ops snd_mpu401_uart_output =
+{
+	.open =		snd_mpu401_uart_output_open,
+	.close =	snd_mpu401_uart_output_close,
+	.trigger =	snd_mpu401_uart_output_trigger,
+};
+
+static struct snd_rawmidi_ops snd_mpu401_uart_input =
+{
+	.open =		snd_mpu401_uart_input_open,
+	.close =	snd_mpu401_uart_input_close,
+	.trigger =	snd_mpu401_uart_input_trigger,
+};
+
+static void snd_mpu401_uart_free(struct snd_rawmidi *rmidi)
+{
+	struct snd_mpu401 *mpu = rmidi->private_data;
+	if (mpu->irq_flags && mpu->irq >= 0)
+            free_irq(mpu->irq, (void *) mpu);
+        if (mpu->res) {
+            release_resource(mpu->res);
+            kfree_nocheck(mpu->res);
+        }
+	kfree(mpu);
+}
+
+/**
+ * snd_mpu401_uart_new - create an MPU401-UART instance
+ * @card: the card instance
+ * @device: the device index, zero-based
+ * @hardware: the hardware type, MPU401_HW_XXXX
+ * @port: the base address of MPU401 port
+ * @info_flags: bitflags MPU401_INFO_XXX
+ * @irq: the irq number, -1 if no interrupt for mpu
+ * @irq_flags: the irq request flags (SA_XXX), 0 if irq was already reserved.
+ * @rrawmidi: the pointer to store the new rawmidi instance
+ *
+ * Creates a new MPU-401 instance.
+ *
+ * Note that the rawmidi instance is returned on the rrawmidi argument,
+ * not the mpu401 instance itself.  To access to the mpu401 instance,
+ * cast from rawmidi->private_data (with struct snd_mpu401 magic-cast).
+ *
+ * Returns zero if successful, or a negative error code.
+ */
+int snd_mpu401_uart_new(struct snd_card *card, int device,
+                        unsigned short hardware,
+                        unsigned long port,
+			unsigned int info_flags,
+			int irq, int irq_flags,
+			struct snd_rawmidi ** rrawmidi)
+{
+	struct snd_mpu401 *mpu;
+        struct snd_rawmidi *rmidi;
+        int in_enable, out_enable;
+	int err;
+
+	if (rrawmidi)
+            *rrawmidi = NULL;
+        if (! (info_flags & (MPU401_INFO_INPUT | MPU401_INFO_OUTPUT)))
+		info_flags |= MPU401_INFO_INPUT | MPU401_INFO_OUTPUT;
+	in_enable = (info_flags & MPU401_INFO_INPUT) ? 1 : 0;
+	out_enable = (info_flags & MPU401_INFO_OUTPUT) ? 1 : 0;
+	if ((err = snd_rawmidi_new(card, "MPU-401U", device,
+                                   out_enable, in_enable, &rmidi)) < 0)
+            return err;
+	mpu = (struct snd_mpu401 *)kzalloc(sizeof(*mpu), GFP_KERNEL);
+	if (mpu == NULL) {
+		snd_printk(KERN_ERR "mpu401_uart: cannot allocate\n");
+		snd_device_free(card, rmidi);
+		return -ENOMEM;
+	}
+	rmidi->private_data = mpu;
+	rmidi->private_free = snd_mpu401_uart_free;
+	spin_lock_init(&mpu->input_lock);
+	spin_lock_init(&mpu->output_lock);
+	spin_lock_init(&mpu->timer_lock);
+        mpu->hardware = hardware;
+        if (! (info_flags & MPU401_INFO_INTEGRATED)) {
+		int res_size = hardware == MPU401_HW_PC98II ? 4 : 2;
+		if ((mpu->res = request_region(port, res_size, "MPU401 UART")) == NULL) {
+			snd_printk(KERN_ERR "mpu401_uart: unable to grab port 0x%lx size %d\n", port, res_size);
+			snd_device_free(card, rmidi);
+			return -EBUSY;
+		}
+        }
+        if (info_flags & MPU401_INFO_MMIO) {
+		mpu->write = mpu401_write_mmio;
+                mpu->read = mpu401_read_mmio;
+        } else {
+		mpu->write = mpu401_write_port;
+		mpu->read = mpu401_read_port;
+	}
+	mpu->port = port;
+	if (hardware == MPU401_HW_PC98II)
+		mpu->cport = port + 2;
+	else
+		mpu->cport = port + 1;
+	if (irq >= 0 && irq_flags) {
+		if (request_irq(irq, snd_mpu401_uart_interrupt, irq_flags, "MPU401 UART", (void *) mpu)) {
+			snd_printk(KERN_ERR "mpu401_uart: unable to grab IRQ %d\n", irq);
+			snd_device_free(card, rmidi);
+			return -EBUSY;
+		}
+        }
+        mpu->info_flags = info_flags;
+	mpu->irq = irq;
+	mpu->irq_flags = irq_flags;
+	if (card->shortname[0])
+		sprintf(rmidi->name, "%s MIDI", card->shortname);
+	else
+            sprintf(rmidi->name, "MPU-401 MIDI %d-%d", card->number, device);
+        if (out_enable) {
+            snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
+                                &snd_mpu401_uart_output);
+            rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT;
+        }
+        if (in_enable) {
+            snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
+                                &snd_mpu401_uart_input);
+            rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
+            if (out_enable)
+                rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
+	}
+        mpu->rmidi = rmidi;
+	if (rrawmidi)
+		*rrawmidi = rmidi;
+	return 0;
+}
+
+EXPORT_SYMBOL(snd_mpu401_uart_interrupt);
+EXPORT_SYMBOL(snd_mpu401_uart_new);
+
+/*
+ *  INIT part
+ */
+
+static int __init alsa_mpu401_uart_init(void)
+{
+	return 0;
+}
+
+static void __exit alsa_mpu401_uart_exit(void)
+{
+}
+
+module_init(alsa_mpu401_uart_init)
+module_exit(alsa_mpu401_uart_exit)
Index: /GPL/branches/alsa-resync1/alsa-kernel/drivers/mtpav.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/drivers/mtpav.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/drivers/mtpav.c	(revision 91)
@@ -0,0 +1,819 @@
+/*
+ *      MOTU Midi Timepiece ALSA Main routines
+ *      Copyright by Michael T. Mayers (c) Jan 09, 2000
+ *      mail: tweakoz@pacbell.net
+ *      Thanks to John Galbraith
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ *      This driver is for the 'Mark Of The Unicorn' (MOTU)
+ *      MidiTimePiece AV multiport MIDI interface 
+ *
+ *      IOPORTS
+ *      -------
+ *      8 MIDI Ins and 8 MIDI outs
+ *      Video Sync In (BNC), Word Sync Out (BNC), 
+ *      ADAT Sync Out (DB9)
+ *      SMPTE in/out (1/4")
+ *      2 programmable pedal/footswitch inputs and 4 programmable MIDI controller knobs.
+ *      Macintosh RS422 serial port
+ *      RS422 "network" port for ganging multiple MTP's
+ *      PC Parallel Port ( which this driver currently uses )
+ *
+ *      MISC FEATURES
+ *      -------------
+ *      Hardware MIDI routing, merging, and filtering   
+ *      MIDI Synchronization to Video, ADAT, SMPTE and other Clock sources
+ *      128 'scene' memories, recallable from MIDI program change
+ *
+ *
+ * ChangeLog
+ * Jun 11 2001	Takashi Iwai <tiwai@suse.de>
+ *      - Recoded & debugged
+ *      - Added timer interrupt for midi outputs
+ *      - snd_hwports is between 1 and 8, which specifies the number of hardware ports.
+ *        The three global ports, computer, adat and broadcast ports, are created
+ *        always after h/w and remote ports.
+ *
+ */
+
+#define SNDRV_MAIN_OBJECT_FILE
+#include <sound/driver.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+#include <sound/rawmidi.h>
+
+/*
+ *      globals
+ */
+EXPORT_NO_SYMBOLS;
+MODULE_DESCRIPTION("MOTU MidiTimePiece AV multiport MIDI");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{MOTU,MidiTimePiece AV multiport MIDI}}");
+
+// io resources
+#define MTPAV_IOBASE		0x378
+#define MTPAV_IRQ		7
+#define MTPAV_MAX_PORTS		8
+
+static int snd_index = SNDRV_DEFAULT_IDX1;
+static char *snd_id = SNDRV_DEFAULT_STR1;
+static long snd_port = MTPAV_IOBASE;	/* 0x378, 0x278 */
+static int snd_irq = MTPAV_IRQ;		/* 7, 5 */
+static int snd_hwports = MTPAV_MAX_PORTS;	/* use hardware ports 1-8 */
+
+MODULE_PARM(snd_index, "i");
+MODULE_PARM_DESC(snd_index, "Index value for MotuMTPAV MIDI.");
+MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC);
+MODULE_PARM(snd_id, "s");
+MODULE_PARM_DESC(snd_id, "ID string for MotuMTPAV MIDI.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_port, "l");
+MODULE_PARM_DESC(snd_port, "Parallel port # for MotuMTPAV MIDI.");
+MODULE_PARM_SYNTAX(snd_port, "allows:{{0x378},{0x278}},dialog:list");
+MODULE_PARM(snd_irq, "i");
+MODULE_PARM_DESC(snd_irq, "Parallel IRQ # for MotuMTPAV MIDI.");
+MODULE_PARM_SYNTAX(snd_irq,  "allows:{{7},{5}},dialog:list");
+MODULE_PARM(snd_hwports, "i");
+MODULE_PARM_DESC(snd_hwports, "Hardware ports # for MotuMTPAV MIDI.");
+MODULE_PARM_SYNTAX(snd_hwports, "allows:{{1,8}},dialog:list");
+
+/*
+ *      defines
+ */
+//#define USE_FAKE_MTP //       dont actually read/write to MTP device (for debugging without an actual unit) (does not work yet)
+
+// parallel port usage masks
+#define SIGS_BYTE 0x08
+#define SIGS_RFD 0x80
+#define SIGS_IRQ 0x40
+#define SIGS_IN0 0x10
+#define SIGS_IN1 0x20
+
+#define SIGC_WRITE 0x04
+#define SIGC_READ 0x08
+#define SIGC_INTEN 0x10
+
+#define DREG 0
+#define SREG 1
+#define CREG 2
+
+//
+#define MTPAV_MODE_INPUT_OPENED		0x01
+#define MTPAV_MODE_OUTPUT_OPENED	0x02
+#define MTPAV_MODE_INPUT_TRIGGERED	0x04
+#define MTPAV_MODE_OUTPUT_TRIGGERED	0x08
+
+#define NUMPORTS (0x12+1)
+
+
+/*
+ */
+
+typedef struct mtpav_port {
+	u8 number;
+	u8 hwport;
+	u8 mode;
+	snd_rawmidi_substream_t *input;
+	snd_rawmidi_substream_t *output;
+} mtpav_port_t;
+
+typedef struct mtpav {
+	snd_card_t *card;
+	unsigned long port;
+	struct resource *res_port;
+	int irq;			/* interrupt (for inputs) */
+	spinlock_t spinlock;
+	int share_irq;			/* number of accesses to input interrupts */
+	int istimer;			/* number of accesses to timer interrupts */
+	struct timer_list timer;	/* timer interrupts for outputs */
+	snd_rawmidi_t *rmidi;
+	int num_ports;		/* number of hw ports (1-8) */
+	mtpav_port_t ports[NUMPORTS];	/* all ports including computer, adat and bc */
+
+	u32 inmidiport;		/* selected input midi port */
+	u32 inmidistate;	/* during midi command 0xf5 */
+
+	u32 outmidihwport;	/* selected output midi hw port */
+} mtpav_t;
+
+
+/*
+ * global instance
+ * hey, we handle at most only one card..
+ */
+static mtpav_t *mtp_card;
+
+/*
+ * possible hardware ports (selected by 0xf5 port message)
+ *      0x00		all ports
+ *      0x01 .. 0x08    this MTP's ports 1..8
+ *      0x09 .. 0x10    networked MTP's ports (9..16)
+ *      0x11            networked MTP's computer port
+ *      0x63            to ADAT
+ *
+ * mappig:
+ *  subdevice 0 - (X-1)    ports
+ *            X - (2*X-1)  networked ports
+ *            X            computer
+ *            X+1          ADAT
+ *            X+2          all ports
+ *
+ *  where X = chip->num_ports
+ */
+
+#define MTPAV_PIDX_COMPUTER	0
+#define MTPAV_PIDX_ADAT		1
+#define MTPAV_PIDX_BROADCAST	2
+
+
+static int translate_subdevice_to_hwport(mtpav_t *chip, int subdev)
+{
+	if (subdev < 0)
+		return 0x01; /* invalid - use port 0 as default */
+	else if (subdev < chip->num_ports)
+		return subdev + 1; /* single mtp port */
+	else if (subdev < chip->num_ports * 2)
+		return subdev - chip->num_ports + 0x09; /* remote port */
+	else if (subdev == chip->num_ports * 2 + MTPAV_PIDX_COMPUTER)
+		return 0x11; /* computer port */
+	else if (subdev == chip->num_ports + MTPAV_PIDX_ADAT)
+		return 0x63;		/* ADAT */
+	return 0; /* all ports */
+}
+
+static int translate_hwport_to_subdevice(mtpav_t *chip, int hwport)
+{
+	int port;
+	if (hwport <= 0x00) /* all ports */
+		return chip->num_ports + MTPAV_PIDX_BROADCAST;
+	else if (hwport <= 0x08) { /* single port */
+		port = hwport - 1;
+		if (port >= chip->num_ports)
+			port = 0;
+		return port;
+	} else if (hwport <= 0x10) { /* remote port */
+		port = hwport - 0x09 + chip->num_ports;
+		if (port >= chip->num_ports * 2)
+			port = chip->num_ports;
+		return port;
+	} else if (hwport == 0x11)  /* computer port */
+		return chip->num_ports + MTPAV_PIDX_COMPUTER;
+	else  /* ADAT */
+		return chip->num_ports + MTPAV_PIDX_ADAT;
+}
+
+
+/*
+ */
+
+static u8 snd_mtpav_getreg(mtpav_t *chip, u16 reg)
+{
+	u8 rval = 0;
+
+	if (reg == SREG) {
+		rval = inb(chip->port + SREG);
+		rval = (rval & 0xf8);
+	} else if (reg == CREG) {
+		rval = inb(chip->port + CREG);
+		rval = (rval & 0x1c);
+	}
+
+	return rval;
+}
+
+/*
+ */
+
+static void snd_mtpav_mputreg(mtpav_t *chip, u16 reg, u8 val)
+{
+	if (reg == DREG) {
+		outb(val, chip->port + DREG);
+	} else if (reg == CREG) {
+		outb(val, chip->port + CREG);
+	}
+}
+
+/*
+ */
+
+static void snd_mtpav_wait_rfdhi(mtpav_t *chip)
+{
+	int counts = 10000;
+	u8 sbyte;
+
+	sbyte = snd_mtpav_getreg(chip, SREG);
+	while (!(sbyte & SIGS_RFD) && counts--) {
+		sbyte = snd_mtpav_getreg(chip, SREG);
+		udelay(10);
+	}
+}
+
+static void snd_mtpav_send_byte(mtpav_t *chip, u8 byte)
+{
+	u8 tcbyt;
+	u8 clrwrite;
+	u8 setwrite;
+
+	snd_mtpav_wait_rfdhi(chip);
+
+	/////////////////
+
+	tcbyt = snd_mtpav_getreg(chip, CREG);
+	clrwrite = tcbyt & (SIGC_WRITE ^ 0xff);
+	setwrite = tcbyt | SIGC_WRITE;
+
+	snd_mtpav_mputreg(chip, DREG, byte);
+	snd_mtpav_mputreg(chip, CREG, clrwrite);	// clear write bit
+
+	snd_mtpav_mputreg(chip, CREG, setwrite);	// set write bit
+
+}
+
+
+/*
+ */
+
+/* call this with spin lock held */
+static void snd_mtpav_output_port_write(mtpav_port_t *port,
+					snd_rawmidi_substream_t *substream)
+{
+	u8 outbyte;
+
+	// send port change command if necessary
+
+	if (port->hwport != mtp_card->outmidihwport) {
+		mtp_card->outmidihwport = port->hwport;
+
+		snd_mtpav_send_byte(mtp_card, 0xf5);
+		snd_mtpav_send_byte(mtp_card, port->hwport);
+		//snd_printk("new outport: 0x%x\n", (unsigned int) port->hwport);
+
+	}
+
+	// send data
+
+	while (snd_rawmidi_transmit(substream, &outbyte, 1) == 1)
+		snd_mtpav_send_byte(mtp_card, outbyte);
+}
+
+static void snd_mtpav_output_write(snd_rawmidi_substream_t * substream)
+{
+	mtpav_port_t *port = &mtp_card->ports[substream->number];
+	unsigned long flags;
+
+	spin_lock_irqsave(&mtp_card->spinlock, flags);
+	snd_mtpav_output_port_write(port, substream);
+	spin_unlock_irqrestore(&mtp_card->spinlock, flags);
+}
+
+
+/*
+ *      mtpav control
+ */
+
+static void snd_mtpav_portscan(mtpav_t *chip)	// put mtp into smart routing mode
+{
+	u8 port;
+
+	for (port = 0; port < 8; port++) {
+		snd_mtpav_send_byte(chip, 0xf5);
+		snd_mtpav_send_byte(chip, port);
+		snd_mtpav_send_byte(chip, 0xfe);
+	}
+}
+
+/*
+ */
+
+static int snd_mtpav_input_open(snd_rawmidi_substream_t * substream)
+{
+	unsigned long flags;
+	mtpav_port_t *port = &mtp_card->ports[substream->number];
+
+	//printk("mtpav port: %d opened\n", (int) substream->number);
+	spin_lock_irqsave(&mtp_card->spinlock, flags);
+	port->mode |= MTPAV_MODE_INPUT_OPENED;
+	port->input = substream;
+	if (mtp_card->share_irq++ == 0)
+		snd_mtpav_mputreg(mtp_card, CREG, (SIGC_INTEN | SIGC_WRITE));	// enable pport interrupts
+	spin_unlock_irqrestore(&mtp_card->spinlock, flags);
+	return 0;
+}
+
+/*
+ */
+
+static int snd_mtpav_input_close(snd_rawmidi_substream_t *substream)
+{
+	unsigned long flags;
+	mtpav_port_t *port = &mtp_card->ports[substream->number];
+
+	//printk("mtpav port: %d closed\n", (int) port);
+
+	spin_lock_irqsave(&mtp_card->spinlock, flags);
+
+	port->mode &= (~MTPAV_MODE_INPUT_OPENED);
+	port->input = NULL;
+	if (--mtp_card->share_irq == 0)
+		snd_mtpav_mputreg(mtp_card, CREG, 0);	// disable pport interrupts
+
+	spin_unlock_irqrestore(&mtp_card->spinlock, flags);
+	return 0;
+}
+
+/*
+ */
+
+static void snd_mtpav_input_trigger(snd_rawmidi_substream_t * substream, int up)
+{
+	unsigned long flags;
+	mtpav_port_t *port = &mtp_card->ports[substream->number];
+
+	spin_lock_irqsave(&mtp_card->spinlock, flags);
+	if (up)
+		port->mode |= MTPAV_MODE_INPUT_TRIGGERED;
+	else
+		port->mode &= ~MTPAV_MODE_INPUT_TRIGGERED;
+	spin_unlock_irqrestore(&mtp_card->spinlock, flags);
+
+}
+
+
+/*
+ * timer interrupt for outputs
+ */
+
+static void snd_mtpav_output_timer(unsigned long data)
+{
+	mtpav_t *chip = snd_magic_cast(mtpav_t, (void *)data, return);
+	int p;
+
+	spin_lock(&chip->spinlock);
+	/* reprogram timer */
+	chip->timer.expires = 1 + jiffies;
+	add_timer(&chip->timer);
+	/* process each port */
+	for (p = 0; p <= chip->num_ports * 2 + MTPAV_PIDX_BROADCAST; p++) {
+		mtpav_port_t *port = &mtp_card->ports[p];
+		if ((port->mode & MTPAV_MODE_OUTPUT_TRIGGERED) && port->output)
+			snd_mtpav_output_port_write(port, port->output);
+	}
+	spin_unlock(&chip->spinlock);
+}
+
+static void snd_mtpav_add_output_timer(mtpav_t *chip)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->spinlock, flags);
+	chip->timer.function = snd_mtpav_output_timer;
+	chip->timer.data = (unsigned long) mtp_card;
+	chip->timer.expires = 1 + jiffies;
+	add_timer(&chip->timer);
+	spin_unlock_irqrestore(&chip->spinlock, flags);
+}
+
+static void snd_mtpav_remove_output_timer(mtpav_t *chip)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->spinlock, flags);
+	del_timer(&chip->timer);
+	spin_unlock_irqrestore(&chip->spinlock, flags);
+}
+
+/*
+ */
+
+static int snd_mtpav_output_open(snd_rawmidi_substream_t * substream)
+{
+	unsigned long flags;
+	mtpav_port_t *port = &mtp_card->ports[substream->number];
+
+	spin_lock_irqsave(&mtp_card->spinlock, flags);
+	port->mode |= MTPAV_MODE_OUTPUT_OPENED;
+	port->output = substream;
+	spin_unlock_irqrestore(&mtp_card->spinlock, flags);
+	return 0;
+};
+
+/*
+ */
+
+static int snd_mtpav_output_close(snd_rawmidi_substream_t * substream)
+{
+	unsigned long flags;
+	mtpav_port_t *port = &mtp_card->ports[substream->number];
+
+	spin_lock_irqsave(&mtp_card->spinlock, flags);
+	port->mode &= (~MTPAV_MODE_OUTPUT_OPENED);
+	port->output = NULL;
+	spin_unlock_irqrestore(&mtp_card->spinlock, flags);
+	return 0;
+};
+
+/*
+ */
+
+static void snd_mtpav_output_trigger(snd_rawmidi_substream_t * substream, int up)
+{
+	unsigned long flags;
+	mtpav_port_t *port = &mtp_card->ports[substream->number];
+
+	spin_lock_irqsave(&mtp_card->spinlock, flags);
+	if (up) {
+		if (! (port->mode  & MTPAV_MODE_OUTPUT_TRIGGERED)) {
+			if (mtp_card->istimer++ == 0)
+				snd_mtpav_add_output_timer(mtp_card);
+			port->mode |= MTPAV_MODE_OUTPUT_TRIGGERED;
+		}
+	} else {
+		port->mode &= ~MTPAV_MODE_OUTPUT_TRIGGERED;
+		if (--mtp_card->istimer == 0)
+			snd_mtpav_remove_output_timer(mtp_card);
+	}
+	spin_unlock_irqrestore(&mtp_card->spinlock, flags);
+
+	if (up)
+		snd_mtpav_output_write(substream);
+}
+
+/*
+ * midi interrupt for inputs
+ */
+
+static void snd_mtpav_inmidi_process(mtpav_t *mcrd, u8 inbyte)
+{
+	mtpav_port_t *port;
+
+	if (mcrd->inmidiport > mcrd->num_ports * 2 + MTPAV_PIDX_BROADCAST)
+		return;
+
+	port = &mcrd->ports[mcrd->inmidiport];
+	if (port->mode & MTPAV_MODE_INPUT_TRIGGERED)
+		snd_rawmidi_receive(port->input, &inbyte, 1);
+}
+
+static void snd_mtpav_inmidi_h(mtpav_t * mcrd, u8 inbyte)
+{
+#ifndef TARGET_OS2
+	snd_assert(mcrd, return);
+#endif
+	if (inbyte >= 0xf8) {
+		/* real-time midi code */
+		snd_mtpav_inmidi_process(mcrd, inbyte);
+		return;
+	}
+
+	if (mcrd->inmidistate == 0) {	// awaiting command
+		if (inbyte == 0xf5)	// MTP port #
+			mcrd->inmidistate = 1;
+		else
+			snd_mtpav_inmidi_process(mcrd, inbyte);
+	} else if (mcrd->inmidistate) {
+		mcrd->inmidiport = translate_hwport_to_subdevice(mcrd, inbyte);
+		mcrd->inmidistate = 0;
+	}
+}
+
+static void snd_mtpav_read_bytes(mtpav_t * mcrd)
+{
+	u8 clrread, setread;
+	u8 mtp_read_byte;
+	u8 sr, cbyt;
+	int i;
+
+	u8 sbyt = snd_mtpav_getreg(mcrd, SREG);
+
+	//printk("snd_mtpav_read_bytes() sbyt: 0x%x\n", sbyt);
+
+	if (!(sbyt & SIGS_BYTE))
+		return;
+
+	cbyt = snd_mtpav_getreg(mcrd, CREG);
+	clrread = cbyt & (SIGC_READ ^ 0xff);
+	setread = cbyt | SIGC_READ;
+
+	do {
+
+		mtp_read_byte = 0;
+		for (i = 0; i < 4; i++) {
+			snd_mtpav_mputreg(mcrd, CREG, setread);
+			sr = snd_mtpav_getreg(mcrd, SREG);
+			snd_mtpav_mputreg(mcrd, CREG, clrread);
+
+			sr &= SIGS_IN0 | SIGS_IN1;
+			sr >>= 4;
+			mtp_read_byte |= sr << (i * 2);
+		}
+
+		snd_mtpav_inmidi_h(mcrd, mtp_read_byte);
+
+		sbyt = snd_mtpav_getreg(mcrd, SREG);
+
+	} while (sbyt & SIGS_BYTE);
+}
+
+static void snd_mtpav_irqh(int irq, void *dev_id, struct pt_regs *regs)
+{
+	mtpav_t *mcard = snd_magic_cast(mtpav_t, dev_id, return);
+
+	//printk("irqh()\n");
+	spin_lock(&mcard->spinlock);
+	snd_mtpav_read_bytes(mcard);
+	spin_unlock(&mcard->spinlock);
+}
+
+/*
+ * get ISA resources
+ */
+static int snd_mtpav_get_ISA(mtpav_t * mcard)
+{
+	if ((mcard->res_port = request_region(snd_port, 3, "MotuMTPAV MIDI")) == NULL) {
+		snd_printk("MTVAP port 0x%lx is busy\n", snd_port);
+		return -EBUSY;
+	}
+	mcard->port = snd_port;
+	if (request_irq(snd_irq, snd_mtpav_irqh, SA_INTERRUPT, "MOTU MTPAV", (void *)mcard)) {
+		snd_printk("MTVAP IRQ %d busy\n", snd_irq);
+		return -EBUSY;
+	}
+	mcard->irq = snd_irq;
+	return 0;
+}
+
+
+/*
+ */
+
+#ifdef TARGET_OS2
+static snd_rawmidi_ops_t snd_mtpav_output = {
+	snd_mtpav_output_open,
+	snd_mtpav_output_close,
+	snd_mtpav_output_trigger,
+        0
+};
+
+static snd_rawmidi_ops_t snd_mtpav_input = {
+	snd_mtpav_input_open,
+	snd_mtpav_input_close,
+	snd_mtpav_input_trigger,
+        0
+};
+#else
+static snd_rawmidi_ops_t snd_mtpav_output = {
+	open:		snd_mtpav_output_open,
+	close:		snd_mtpav_output_close,
+	trigger:	snd_mtpav_output_trigger,
+};
+
+static snd_rawmidi_ops_t snd_mtpav_input = {
+	open:		snd_mtpav_input_open,
+	close:		snd_mtpav_input_close,
+	trigger:	snd_mtpav_input_trigger,
+};
+#endif
+
+/*
+ * get RAWMIDI resources
+ */
+
+static void snd_mtpav_set_name(mtpav_t *chip, snd_rawmidi_substream_t *substream)
+{
+	if (substream->number >= 0 && substream->number < chip->num_ports)
+		sprintf(substream->name, "MTP direct %d", (substream->number % chip->num_ports) + 1);
+	else if (substream->number >= 8 && substream->number < chip->num_ports * 2)
+		sprintf(substream->name, "MTP remote %d", (substream->number % chip->num_ports) + 1);
+	else if (substream->number == chip->num_ports * 2)
+		strcpy(substream->name, "MTP computer");
+	else if (substream->number == chip->num_ports * 2 + 1)
+		strcpy(substream->name, "MTP ADAT");
+	else
+		strcpy(substream->name, "MTP broadcast");
+}
+
+static int snd_mtpav_get_RAWMIDI(mtpav_t * mcard)
+{
+	int rval = 0;
+	snd_rawmidi_t *rawmidi;
+	snd_rawmidi_substream_t *substream;
+	struct list_head *list;
+
+	//printk("entering snd_mtpav_get_RAWMIDI\n");
+
+	if (snd_hwports < 1)
+		mcard->num_ports = 1;
+	else if (snd_hwports > 8)
+		mcard->num_ports = 8;
+	else
+		mcard->num_ports = snd_hwports;
+
+	if ((rval = snd_rawmidi_new(mcard->card, "MotuMIDI", 0,
+				    mcard->num_ports * 2 + MTPAV_PIDX_BROADCAST + 1,
+				    mcard->num_ports * 2 + MTPAV_PIDX_BROADCAST + 1,
+				    &mcard->rmidi)) < 0)
+		return rval;
+	rawmidi = mcard->rmidi;
+
+	list_for_each(list, &rawmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substreams) {
+		substream = list_entry(list, snd_rawmidi_substream_t, list);
+		snd_mtpav_set_name(mcard, substream);
+		substream->ops = &snd_mtpav_input;
+	}
+	list_for_each(list, &rawmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams) {
+		substream = list_entry(list, snd_rawmidi_substream_t, list);
+		snd_mtpav_set_name(mcard, substream);
+		substream->ops = &snd_mtpav_output;
+		mcard->ports[substream->number].hwport = translate_subdevice_to_hwport(mcard, substream->number);
+	}
+	rawmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT |
+			       SNDRV_RAWMIDI_INFO_DUPLEX;
+	sprintf(rawmidi->name, "MTP AV MIDI");
+	//printk("exiting snd_mtpav_get_RAWMIDI() \n");
+	return 0;
+}
+
+/*
+ */
+
+static mtpav_t *new_mtpav(void)
+{
+	mtpav_t *ncrd = (mtpav_t *) snd_kcalloc(sizeof(mtpav_t), GFP_KERNEL);
+	if (ncrd != NULL) {
+		spin_lock_init(&ncrd->spinlock);
+
+		ncrd->card = NULL;
+		ncrd->irq = -1;
+		ncrd->share_irq = 0;
+
+		ncrd->inmidiport = 0xffffffff;
+		ncrd->inmidistate = 0;
+		ncrd->outmidihwport = 0xffffffff;
+	}
+	return ncrd;
+}
+
+/*
+ */
+
+static void free_mtpav(mtpav_t * crd)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&crd->spinlock, flags);
+	if (crd->istimer > 0)
+		snd_mtpav_remove_output_timer(crd);
+	spin_unlock_irqrestore(&crd->spinlock, flags);
+	if (crd->irq >= 0)
+		free_irq(crd->irq, (void *)crd);
+	if (crd->res_port)
+		release_resource(crd->res_port);
+	if (crd != NULL)
+		kfree(crd);
+}
+
+/*
+ */
+
+static int __init alsa_card_mtpav_init(void)
+{
+	int err = 0;
+	char longname_buffer[80];
+
+	mtp_card = new_mtpav();
+	if (mtp_card == NULL)
+		return -ENOMEM;
+
+	mtp_card->card = snd_card_new(snd_index, snd_id, THIS_MODULE, 0);
+	if (mtp_card->card == NULL) {
+		free_mtpav(mtp_card);
+		return -ENOMEM;
+	}
+
+	err = snd_mtpav_get_ISA(mtp_card);
+	//printk("snd_mtpav_get_ISA returned: %d\n", err);
+	if (err < 0)
+		goto __error;
+
+	strcpy(mtp_card->card->driver, "MTPAV");
+	strcpy(mtp_card->card->shortname, "MTPAV on parallel port");
+	memset(longname_buffer, 0, sizeof(longname_buffer));
+	sprintf(longname_buffer, "MTPAV on parallel port at");
+
+	err = snd_mtpav_get_RAWMIDI(mtp_card);
+	//snd_printk("snd_mtapv_get_RAWMIDI returned: %d\n", err);
+	if (err < 0)
+		goto __error;
+
+	err = snd_card_register(mtp_card->card);	// dont snd_card_register until AFTER all cards reources done!
+
+	//printk("snd_card_register returned %d\n", err);
+	if (err < 0)
+		goto __error;
+
+
+	snd_mtpav_portscan(mtp_card);
+
+	snd_printk("Motu MidiTimePiece on parallel port irq: %d ioport: 0x%lx\n", snd_irq, snd_port);
+
+	return 0;
+
+      __error:
+	snd_card_free(mtp_card->card);
+	free_mtpav(mtp_card);
+	return err;
+}
+
+/*
+ */
+
+static void __exit alsa_card_mtpav_exit(void)
+{
+	if (mtp_card == NULL)
+		return;
+	if (mtp_card->card)
+		snd_card_free(mtp_card->card);
+	free_mtpav(mtp_card);
+}
+
+/*
+ */
+
+module_init(alsa_card_mtpav_init)
+module_exit(alsa_card_mtpav_exit)
+
+#ifndef MODULE
+
+/* format is: snd-card-mtpav=snd_enable,snd_index,snd_id,
+			     snd_port,snd_irq,snd_hwports */
+
+static int __init alsa_card_mtpav_setup(char *str)
+{
+        int __attribute__ ((__unused__)) enable = 1;
+
+	(void)(get_option(&str,&enable) == 2 &&
+	       get_option(&str,&snd_index) == 2 &&
+	       get_id(&str,&snd_id) == 2 &&
+	       get_option(&str,(int *)&snd_port) == 2 &&
+	       get_option(&str,&snd_irq) == 2 &&
+	       get_option(&str,&snd_hwports) == 2);
+	return 1;
+}
+
+__setup("snd-card-mtpav=", alsa_card_mtpav_setup);
+
+#endif /* ifndef MODULE */
Index: /GPL/branches/alsa-resync1/alsa-kernel/drivers/opl3/makefile.os2
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/drivers/opl3/makefile.os2	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/drivers/opl3/makefile.os2	(revision 91)
@@ -0,0 +1,52 @@
+CINCLUDES = ..\..\..\include;..\..\include;$(%WATCOM)\H
+AINCLUDES = $(DDK_BASE)\H
+
+CDEFINES = -D__KERNEL__ -DMODULE -dTARGET_OS2 -dALSA_BUILD -D__ISAPNP__ -DCONFIG_ISAPNP -D__i386_
+
+32BIT=1
+!include ..\..\..\..\makefile.inc
+
+
+#===================================================================
+#
+#   List of source files
+#
+#===================================================================
+FILE1    = 
+FILE2    = opl3_lib.obj opl3_synth.obj
+FILE3    = opl3_seq.obj opl3_midi.obj opl3_drums.obj
+FILE4    = 
+FILE5    = 
+FILE6    = 
+FILE7    = 
+FILE8    = 
+FILELAST =
+FILES    = $(FILE0) $(FILE1) $(FILE2) $(FILE3) $(FILE4) $(FILE5) $(FILE6) $(FILE7) $(FILE8) $(FILE9) $(FILE10) $(FILE11) $(FILE12)
+
+TARGET   = opl3
+
+LNKFILE  = $(OBJDIR)\$(TARGET).lnk
+
+
+#===================================================================
+#
+#   Specific dependencies
+#
+#===================================================================
+all: $(OBJDIR)\$(TARGET).lib
+
+
+$(LNKFILE): makefile.os2
+    @%create $(LNKFILE)
+    @for %f in ($(FILES)) do @%append $(LNKFILE) -+$(OBJDIR)\%f
+
+$(OBJDIR)\$(TARGET).lib: $(LNKFILE) $(FILES)
+    $(LIB) $(OBJDIR)\$(TARGET).lib @$(LNKFILE)
+    copy $(OBJDIR)\$(TARGET).lib $(ALSA_LIB)
+
+
+clean: .SYMBOLIC
+    -@rm  *.err 2>nul
+     @cd $(OBJDIR)
+     -rm *.obj *.lnk *.sys *.sym *.lst *.map *.wmap *.lib 2>nul
+     @cd ../..
Index: /GPL/branches/alsa-resync1/alsa-kernel/drivers/opl3/opl3_drums.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/drivers/opl3/opl3_drums.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/drivers/opl3/opl3_drums.c	(revision 91)
@@ -0,0 +1,223 @@
+/*
+ *  Copyright (c) by Uros Bizjak <uros@kss-loka.si>
+ *
+ *   OPL2/OPL3/OPL4 FM routines for internal percussion channels
+ *
+ *   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 "opl3_voice.h"
+
+extern char snd_opl3_regmap[MAX_OPL2_VOICES][4];
+
+static char snd_opl3_drum_table[47] =
+{
+	OPL3_BASSDRUM_ON,  OPL3_BASSDRUM_ON,  OPL3_HIHAT_ON,	/* 35 - 37 */
+	OPL3_SNAREDRUM_ON, OPL3_HIHAT_ON,     OPL3_SNAREDRUM_ON, /* 38 - 40 */
+	OPL3_BASSDRUM_ON,  OPL3_HIHAT_ON,     OPL3_BASSDRUM_ON,	/* 41 - 43 */
+	OPL3_HIHAT_ON,     OPL3_TOMTOM_ON,    OPL3_HIHAT_ON,	/* 44 - 46 */
+	OPL3_TOMTOM_ON,    OPL3_TOMTOM_ON,    OPL3_CYMBAL_ON,	/* 47 - 49 */
+
+	OPL3_TOMTOM_ON,    OPL3_CYMBAL_ON,    OPL3_CYMBAL_ON,	/* 50 - 52 */
+	OPL3_CYMBAL_ON,    OPL3_CYMBAL_ON,    OPL3_CYMBAL_ON,	/* 53 - 55 */
+	OPL3_HIHAT_ON,     OPL3_CYMBAL_ON,    OPL3_TOMTOM_ON,	/* 56 - 58 */
+	OPL3_CYMBAL_ON,    OPL3_TOMTOM_ON,    OPL3_TOMTOM_ON,	/* 59 - 61 */
+	OPL3_HIHAT_ON,     OPL3_TOMTOM_ON,    OPL3_TOMTOM_ON,	/* 62 - 64 */
+
+	OPL3_TOMTOM_ON,    OPL3_TOMTOM_ON,    OPL3_TOMTOM_ON,	/* 65 - 67 */
+	OPL3_TOMTOM_ON,    OPL3_HIHAT_ON,     OPL3_HIHAT_ON,	/* 68 - 70 */
+	OPL3_HIHAT_ON,     OPL3_HIHAT_ON,     OPL3_TOMTOM_ON,	/* 71 - 73 */
+	OPL3_TOMTOM_ON,    OPL3_TOMTOM_ON,    OPL3_TOMTOM_ON,	/* 74 - 76 */
+	OPL3_TOMTOM_ON,    OPL3_TOMTOM_ON,    OPL3_TOMTOM_ON,	/* 77 - 79 */
+	OPL3_CYMBAL_ON,    OPL3_CYMBAL_ON			/* 80 - 81 */
+};
+
+typedef struct snd_opl3_drum_voice {
+	int voice;
+	int op;
+	unsigned char am_vib;
+	unsigned char ksl_level;
+	unsigned char attack_decay;
+	unsigned char sustain_release;
+	unsigned char feedback_connection;
+	unsigned char wave_select;
+} snd_opl3_drum_voice_t;
+
+typedef struct snd_opl3_drum_note {
+	int voice;
+	unsigned char fnum;
+	unsigned char octave_f;
+	unsigned char feedback_connection;
+} snd_opl3_drum_note_t;
+
+static snd_opl3_drum_voice_t bass_op0 = {6, 0, 0x00, 0x32, 0xf8, 0x66, 0x30, 0x00};
+static snd_opl3_drum_voice_t bass_op1 = {6, 1, 0x00, 0x03, 0xf6, 0x57, 0x30, 0x00};
+static snd_opl3_drum_note_t bass_note = {6, 0x90, 0x09};
+
+static snd_opl3_drum_voice_t hihat = {7, 0, 0x00, 0x03, 0xf0, 0x06, 0x20, 0x00};
+
+static snd_opl3_drum_voice_t snare = {7, 1, 0x00, 0x03, 0xf0, 0x07, 0x20, 0x02};
+static snd_opl3_drum_note_t snare_note = {7, 0xf4, 0x0d};
+
+static snd_opl3_drum_voice_t tomtom = {8, 0, 0x02, 0x03, 0xf0, 0x06, 0x10, 0x00};
+static snd_opl3_drum_note_t tomtom_note = {8, 0xf4, 0x09};
+
+static snd_opl3_drum_voice_t cymbal = {8, 1, 0x04, 0x03, 0xf0, 0x06, 0x10, 0x00};
+
+/*
+ * set drum voice characteristics
+ */
+void snd_opl3_drum_voice_set(opl3_t *opl3, snd_opl3_drum_voice_t *data)
+{
+	unsigned char op_offset = snd_opl3_regmap[data->voice][data->op];
+	unsigned char voice_offset = data->voice;
+	unsigned short opl3_reg;
+	
+	/* Set OPL3 AM_VIB register */ 
+	opl3_reg = OPL3_LEFT | (OPL3_REG_AM_VIB + op_offset);
+	opl3->command(opl3, opl3_reg, data->am_vib);
+
+	/* Set OPL3 KSL_LEVEL register */ 
+	opl3_reg = OPL3_LEFT | (OPL3_REG_KSL_LEVEL + op_offset);
+	opl3->command(opl3, opl3_reg, data->ksl_level);
+
+	/* Set OPL3 ATTACK_DECAY register */ 
+	opl3_reg = OPL3_LEFT | (OPL3_REG_ATTACK_DECAY + op_offset);
+	opl3->command(opl3, opl3_reg, data->attack_decay);
+
+	/* Set OPL3 SUSTAIN_RELEASE register */ 
+	opl3_reg = OPL3_LEFT | (OPL3_REG_SUSTAIN_RELEASE + op_offset);
+	opl3->command(opl3, opl3_reg, data->sustain_release);
+
+	/* Set OPL3 FEEDBACK_CONNECTION register */ 
+	opl3_reg = OPL3_LEFT | (OPL3_REG_FEEDBACK_CONNECTION + voice_offset);
+	opl3->command(opl3, opl3_reg, data->feedback_connection);
+
+	/* Select waveform */
+	opl3_reg = OPL3_LEFT | (OPL3_REG_WAVE_SELECT + op_offset);
+	opl3->command(opl3, opl3_reg, data->wave_select);
+}
+
+/*
+ * Set drum voice pitch
+ */
+void snd_opl3_drum_note_set(opl3_t *opl3, snd_opl3_drum_note_t *data)
+{
+	unsigned char voice_offset = data->voice;
+	unsigned short opl3_reg;
+
+	/* Set OPL3 FNUM_LOW register */ 
+	opl3_reg = OPL3_LEFT | (OPL3_REG_FNUM_LOW + voice_offset);
+	opl3->command(opl3, opl3_reg, data->fnum);
+
+	/* Set OPL3 KEYON_BLOCK register */ 
+	opl3_reg = OPL3_LEFT | (OPL3_REG_KEYON_BLOCK + voice_offset);
+	opl3->command(opl3, opl3_reg, data->octave_f);
+}
+
+/*
+ * Set drum voice volume and position
+ */
+void snd_opl3_drum_vol_set(opl3_t *opl3, snd_opl3_drum_voice_t *data, int vel,
+			   snd_midi_channel_t *chan)
+{
+	unsigned char op_offset = snd_opl3_regmap[data->voice][data->op];
+	unsigned char voice_offset = data->voice;
+	unsigned char reg_val;
+	unsigned short opl3_reg;
+
+	/* Set OPL3 KSL_LEVEL register */ 
+	reg_val = data->ksl_level;
+	snd_opl3_calc_volume(&reg_val, vel, chan);
+	opl3_reg = OPL3_LEFT | (OPL3_REG_KSL_LEVEL + op_offset);
+	opl3->command(opl3, opl3_reg, reg_val);
+
+	/* Set OPL3 FEEDBACK_CONNECTION register */ 
+	/* Set output voice connection */
+	reg_val = data->feedback_connection | OPL3_STEREO_BITS;
+	if (chan->gm_pan < 43)
+		reg_val &= ~OPL3_VOICE_TO_RIGHT;
+	if (chan->gm_pan > 85)
+		reg_val &= ~OPL3_VOICE_TO_LEFT;
+	opl3_reg = OPL3_LEFT | (OPL3_REG_FEEDBACK_CONNECTION + voice_offset);
+	opl3->command(opl3, opl3_reg, reg_val);
+}
+
+/*
+ * Loads drum voices at init time
+ */
+void snd_opl3_load_drums(opl3_t *opl3)
+{
+	snd_opl3_drum_voice_set(opl3, &bass_op0);
+	snd_opl3_drum_voice_set(opl3, &bass_op1);
+	snd_opl3_drum_note_set(opl3, &bass_note);
+
+	snd_opl3_drum_voice_set(opl3, &hihat);
+
+	snd_opl3_drum_voice_set(opl3, &snare);
+	snd_opl3_drum_note_set(opl3, &snare_note);
+
+	snd_opl3_drum_voice_set(opl3, &tomtom);
+	snd_opl3_drum_note_set(opl3, &tomtom_note);
+
+	snd_opl3_drum_voice_set(opl3, &cymbal);
+}
+
+/*
+ * Switch drum voice on or off
+ */
+void snd_opl3_drum_switch(opl3_t *opl3, int note, int vel, int on_off,
+			  snd_midi_channel_t *chan)
+{
+	unsigned char drum_mask;
+	snd_opl3_drum_voice_t *drum_voice;
+
+	if (!(opl3->drum_reg & OPL3_PERCUSSION_ENABLE))
+		return;
+
+	if ((note < 35) || (note > 81))
+		return;
+	drum_mask = snd_opl3_drum_table[note - 35];
+
+	if (on_off) {
+		switch (drum_mask) {
+		case OPL3_BASSDRUM_ON:
+			drum_voice = &bass_op1;
+			break;
+		case OPL3_HIHAT_ON:
+			drum_voice = &hihat;
+			break;
+		case OPL3_SNAREDRUM_ON:
+			drum_voice = &snare;
+			break;
+		case OPL3_TOMTOM_ON:
+			drum_voice = &tomtom;
+			break;
+		case OPL3_CYMBAL_ON:
+			drum_voice = &cymbal;
+			break;
+		default:
+			drum_voice = &tomtom;
+		}
+
+		snd_opl3_drum_vol_set(opl3, drum_voice, vel, chan);
+		opl3->drum_reg |= drum_mask;
+	} else {
+		opl3->drum_reg &= ~drum_mask;
+	}
+	opl3->command(opl3, OPL3_LEFT | OPL3_REG_PERCUSSION,
+			 opl3->drum_reg);
+}
Index: /GPL/branches/alsa-resync1/alsa-kernel/drivers/opl3/opl3_lib.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/drivers/opl3/opl3_lib.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/drivers/opl3/opl3_lib.c	(revision 91)
@@ -0,0 +1,554 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>,
+ *                   Hannu Savolainen 1993-1996,
+ *                   Rob Hooft
+ *                   
+ *  Routines for control of AdLib FM cards (OPL2/OPL3/OPL4 chips)
+ *
+ *  Most if code is ported from OSS/Lite.
+ *
+ *   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/opl3.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <sound/minors.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>, Hannu Savolainen 1993-1996, Rob Hooft");
+MODULE_DESCRIPTION("Routines for control of AdLib FM cards (OPL2/OPL3/OPL4 chips)");
+MODULE_LICENSE("GPL");
+
+extern char snd_opl3_regmap[MAX_OPL2_VOICES][4];
+
+static void snd_opl2_command(struct snd_opl3 * opl3, unsigned short cmd, unsigned char val)
+{
+	unsigned long flags;
+	unsigned long port;
+
+	/*
+	 * The original 2-OP synth requires a quite long delay
+	 * after writing to a register.
+	 */
+
+	port = (cmd & OPL3_RIGHT) ? opl3->r_port : opl3->l_port;
+
+	spin_lock_irqsave(&opl3->reg_lock, flags);
+
+	outb((unsigned char) cmd, port);
+	udelay(10);
+
+	outb((unsigned char) val, port + 1);
+	udelay(30);
+
+	spin_unlock_irqrestore(&opl3->reg_lock, flags);
+}
+
+static void snd_opl3_command(struct snd_opl3 * opl3, unsigned short cmd, unsigned char val)
+{
+	unsigned long flags;
+	unsigned long port;
+
+	/*
+	 * The OPL-3 survives with just two INBs
+	 * after writing to a register.
+	 */
+
+	port = (cmd & OPL3_RIGHT) ? opl3->r_port : opl3->l_port;
+
+	spin_lock_irqsave(&opl3->reg_lock, flags);
+
+	outb((unsigned char) cmd, port);
+	inb(opl3->l_port);
+	inb(opl3->l_port);
+
+	outb((unsigned char) val, port + 1);
+	inb(opl3->l_port);
+	inb(opl3->l_port);
+
+	spin_unlock_irqrestore(&opl3->reg_lock, flags);
+}
+
+static int snd_opl3_detect(struct snd_opl3 * opl3)
+{
+	/*
+	 * This function returns 1 if the FM chip is present at the given I/O port
+	 * The detection algorithm plays with the timer built in the FM chip and
+	 * looks for a change in the status register.
+	 *
+	 * Note! The timers of the FM chip are not connected to AdLib (and compatible)
+	 * boards.
+	 *
+	 * Note2! The chip is initialized if detected.
+	 */
+
+	unsigned char stat1, stat2, signature;
+
+	/* Reset timers 1 and 2 */
+	opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, OPL3_TIMER1_MASK | OPL3_TIMER2_MASK);
+	/* Reset the IRQ of the FM chip */
+	opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, OPL3_IRQ_RESET);
+	signature = stat1 = inb(opl3->l_port);	/* Status register */
+	if ((stat1 & 0xe0) != 0x00) {	/* Should be 0x00 */
+		snd_printd("OPL3: stat1 = 0x%x\n", stat1);
+		return -ENODEV;
+	}
+	/* Set timer1 to 0xff */
+	opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER1, 0xff);
+	/* Unmask and start timer 1 */
+	opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, OPL3_TIMER2_MASK | OPL3_TIMER1_START);
+	/* Now we have to delay at least 80us */
+	udelay(200);
+	/* Read status after timers have expired */
+	stat2 = inb(opl3->l_port);
+	/* Stop the timers */
+	opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, OPL3_TIMER1_MASK | OPL3_TIMER2_MASK);
+	/* Reset the IRQ of the FM chip */
+	opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, OPL3_IRQ_RESET);
+	if ((stat2 & 0xe0) != 0xc0) {	/* There is no YM3812 */
+		snd_printd("OPL3: stat2 = 0x%x\n", stat2);
+		return -ENODEV;
+	}
+
+	/* If the toplevel code knows exactly the type of chip, don't try
+	   to detect it. */
+	if (opl3->hardware != OPL3_HW_AUTO)
+		return 0;
+
+	/* There is a FM chip on this address. Detect the type (OPL2 to OPL4) */
+	if (signature == 0x06) {	/* OPL2 */
+		opl3->hardware = OPL3_HW_OPL2;
+	} else {
+		/*
+		 * If we had an OPL4 chip, opl3->hardware would have been set
+		 * by the OPL4 driver; so we can assume OPL3 here.
+		 */
+		snd_assert(opl3->r_port != 0, return -ENODEV);
+		opl3->hardware = OPL3_HW_OPL3;
+	}
+	return 0;
+}
+
+/*
+ *  AdLib timers
+ */
+
+/*
+ *  Timer 1 - 80us
+ */
+
+static int snd_opl3_timer1_start(struct snd_timer * timer)
+{
+	unsigned long flags;
+	unsigned char tmp;
+	unsigned int ticks;
+	struct snd_opl3 *opl3;
+
+	opl3 = snd_timer_chip(timer);
+	spin_lock_irqsave(&opl3->timer_lock, flags);
+	ticks = timer->sticks;
+	tmp = (opl3->timer_enable | OPL3_TIMER1_START) & ~OPL3_TIMER1_MASK;
+	opl3->timer_enable = tmp;
+	opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER1, 256 - ticks);	/* timer 1 count */
+	opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, tmp);	/* enable timer 1 IRQ */
+	spin_unlock_irqrestore(&opl3->timer_lock, flags);
+	return 0;
+}
+
+static int snd_opl3_timer1_stop(struct snd_timer * timer)
+{
+	unsigned long flags;
+	unsigned char tmp;
+	struct snd_opl3 *opl3;
+
+	opl3 = snd_timer_chip(timer);
+	spin_lock_irqsave(&opl3->timer_lock, flags);
+	tmp = (opl3->timer_enable | OPL3_TIMER1_MASK) & ~OPL3_TIMER1_START;
+	opl3->timer_enable = tmp;
+	opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, tmp);	/* disable timer #1 */
+	spin_unlock_irqrestore(&opl3->timer_lock, flags);
+	return 0;
+}
+
+/*
+ *  Timer 2 - 320us
+ */
+
+static int snd_opl3_timer2_start(struct snd_timer * timer)
+{
+	unsigned long flags;
+	unsigned char tmp;
+	unsigned int ticks;
+	struct snd_opl3 *opl3;
+
+	opl3 = snd_timer_chip(timer);
+	spin_lock_irqsave(&opl3->timer_lock, flags);
+	ticks = timer->sticks;
+	tmp = (opl3->timer_enable | OPL3_TIMER2_START) & ~OPL3_TIMER2_MASK;
+	opl3->timer_enable = tmp;
+	opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER2, 256 - ticks);	/* timer 1 count */
+	opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, tmp);	/* enable timer 1 IRQ */
+	spin_unlock_irqrestore(&opl3->timer_lock, flags);
+	return 0;
+}
+
+static int snd_opl3_timer2_stop(struct snd_timer * timer)
+{
+	unsigned long flags;
+	unsigned char tmp;
+	struct snd_opl3 *opl3;
+
+	opl3 = snd_timer_chip(timer);
+	spin_lock_irqsave(&opl3->timer_lock, flags);
+	tmp = (opl3->timer_enable | OPL3_TIMER2_MASK) & ~OPL3_TIMER2_START;
+	opl3->timer_enable = tmp;
+	opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, tmp);	/* disable timer #1 */
+	spin_unlock_irqrestore(&opl3->timer_lock, flags);
+	return 0;
+}
+
+/*
+
+ */
+
+static struct snd_timer_hardware snd_opl3_timer1 =
+{
+	.flags =	SNDRV_TIMER_HW_STOP,
+	.resolution =	80000,
+	.ticks =	256,
+	.start =	snd_opl3_timer1_start,
+	.stop =		snd_opl3_timer1_stop,
+};
+
+static struct snd_timer_hardware snd_opl3_timer2 =
+{
+	.flags =	SNDRV_TIMER_HW_STOP,
+	.resolution =	320000,
+	.ticks =	256,
+	.start =	snd_opl3_timer2_start,
+	.stop =		snd_opl3_timer2_stop,
+};
+
+static int snd_opl3_timer1_init(struct snd_opl3 * opl3, int timer_no)
+{
+	struct snd_timer *timer = NULL;
+	struct snd_timer_id tid;
+	int err;
+
+	tid.dev_class = SNDRV_TIMER_CLASS_CARD;
+	tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE;
+	tid.card = opl3->card->number;
+	tid.device = timer_no;
+	tid.subdevice = 0;
+	if ((err = snd_timer_new(opl3->card, "AdLib timer #1", &tid, &timer)) >= 0) {
+		strcpy(timer->name, "AdLib timer #1");
+		timer->private_data = opl3;
+		timer->hw = snd_opl3_timer1;
+	}
+	opl3->timer1 = timer;
+	return err;
+}
+
+static int snd_opl3_timer2_init(struct snd_opl3 * opl3, int timer_no)
+{
+	struct snd_timer *timer = NULL;
+	struct snd_timer_id tid;
+	int err;
+
+	tid.dev_class = SNDRV_TIMER_CLASS_CARD;
+	tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE;
+	tid.card = opl3->card->number;
+	tid.device = timer_no;
+	tid.subdevice = 0;
+	if ((err = snd_timer_new(opl3->card, "AdLib timer #2", &tid, &timer)) >= 0) {
+		strcpy(timer->name, "AdLib timer #2");
+		timer->private_data = opl3;
+		timer->hw = snd_opl3_timer2;
+	}
+	opl3->timer2 = timer;
+	return err;
+}
+
+/*
+
+ */
+
+void snd_opl3_interrupt(struct snd_hwdep * hw)
+{
+	unsigned char status;
+	struct snd_opl3 *opl3;
+	struct snd_timer *timer;
+
+	if (hw == NULL)
+		return;
+
+	opl3 = hw->private_data;
+	status = inb(opl3->l_port);
+#if 0
+	snd_printk("AdLib IRQ status = 0x%x\n", status);
+#endif
+	if (!(status & 0x80))
+		return;
+
+	if (status & 0x40) {
+		timer = opl3->timer1;
+		snd_timer_interrupt(timer, timer->sticks);
+	}
+	if (status & 0x20) {
+		timer = opl3->timer2;
+		snd_timer_interrupt(timer, timer->sticks);
+	}
+}
+
+/*
+
+ */
+
+static int snd_opl3_free(struct snd_opl3 *opl3)
+{
+	snd_assert(opl3 != NULL, return -ENXIO);
+	if (opl3->private_free)
+		opl3->private_free(opl3);
+	release_and_free_resource(opl3->res_l_port);
+	release_and_free_resource(opl3->res_r_port);
+	kfree(opl3);
+	return 0;
+}
+
+static int snd_opl3_dev_free(struct snd_device *device)
+{
+	struct snd_opl3 *opl3 = device->device_data;
+	return snd_opl3_free(opl3);
+}
+
+int snd_opl3_new(struct snd_card *card,
+		 unsigned short hardware,
+		 struct snd_opl3 **ropl3)
+{
+	static struct snd_device_ops ops = {
+		.dev_free = snd_opl3_dev_free,
+	};
+	struct snd_opl3 *opl3;
+	int err;
+
+	*ropl3 = NULL;
+	opl3 = (struct snd_opl3 *)kzalloc(sizeof(*opl3), GFP_KERNEL);
+	if (opl3 == NULL) {
+		snd_printk(KERN_ERR "opl3: cannot allocate\n");
+		return -ENOMEM;
+	}
+
+	opl3->card = card;
+	opl3->hardware = hardware;
+	spin_lock_init(&opl3->reg_lock);
+	spin_lock_init(&opl3->timer_lock);
+	init_MUTEX(&opl3->access_mutex);
+
+	if ((err = snd_device_new(card, SNDRV_DEV_CODEC, opl3, &ops)) < 0) {
+		snd_opl3_free(opl3);
+		return err;
+	}
+
+	*ropl3 = opl3;
+	return 0;
+}
+
+int snd_opl3_init(struct snd_opl3 *opl3)
+{
+	if (! opl3->command) {
+		printk(KERN_ERR "snd_opl3_init: command not defined!\n");
+		return -EINVAL;
+	}
+
+	opl3->command(opl3, OPL3_LEFT | OPL3_REG_TEST, OPL3_ENABLE_WAVE_SELECT);
+	/* Melodic mode */
+	opl3->command(opl3, OPL3_LEFT | OPL3_REG_PERCUSSION, 0x00);
+
+	switch (opl3->hardware & OPL3_HW_MASK) {
+	case OPL3_HW_OPL2:
+		opl3->max_voices = MAX_OPL2_VOICES;
+		break;
+	case OPL3_HW_OPL3:
+	case OPL3_HW_OPL4:
+		opl3->max_voices = MAX_OPL3_VOICES;
+		/* Enter OPL3 mode */
+		opl3->command(opl3, OPL3_RIGHT | OPL3_REG_MODE, OPL3_OPL3_ENABLE);
+	}
+	return 0;
+}
+
+int snd_opl3_create(struct snd_card *card,
+		    unsigned long l_port,
+		    unsigned long r_port,
+		    unsigned short hardware,
+		    int integrated,
+		    struct snd_opl3 ** ropl3)
+{
+	struct snd_opl3 *opl3;
+	int err;
+
+	*ropl3 = NULL;
+	if ((err = snd_opl3_new(card, hardware, &opl3)) < 0)
+		return err;
+	if (! integrated) {
+		if ((opl3->res_l_port = request_region(l_port, 2, "OPL2/3 (left)")) == NULL) {
+			snd_printk(KERN_ERR "opl3: can't grab left port 0x%lx\n", l_port);
+			snd_opl3_free(opl3);
+			return -EBUSY;
+		}
+		if (r_port != 0 &&
+		    (opl3->res_r_port = request_region(r_port, 2, "OPL2/3 (right)")) == NULL) {
+			snd_printk(KERN_ERR "opl3: can't grab right port 0x%lx\n", r_port);
+			snd_opl3_free(opl3);
+			return -EBUSY;
+		}
+	}
+	opl3->l_port = l_port;
+	opl3->r_port = r_port;
+
+	switch (opl3->hardware) {
+	/* some hardware doesn't support timers */
+	case OPL3_HW_OPL3_SV:
+	case OPL3_HW_OPL3_CS:
+	case OPL3_HW_OPL3_FM801:
+		opl3->command = &snd_opl3_command;
+		break;
+	default:
+		opl3->command = &snd_opl2_command;
+		if ((err = snd_opl3_detect(opl3)) < 0) {
+			snd_printd("OPL2/3 chip not detected at 0x%lx/0x%lx\n",
+				   opl3->l_port, opl3->r_port);
+			snd_opl3_free(opl3);
+			return err;
+		}
+		/* detect routine returns correct hardware type */
+		switch (opl3->hardware & OPL3_HW_MASK) {
+		case OPL3_HW_OPL3:
+		case OPL3_HW_OPL4:
+			opl3->command = &snd_opl3_command;
+		}
+	}
+
+	snd_opl3_init(opl3);
+
+	*ropl3 = opl3;
+	return 0;
+}
+
+int snd_opl3_timer_new(struct snd_opl3 * opl3, int timer1_dev, int timer2_dev)
+{
+	int err;
+
+	if (timer1_dev >= 0)
+		if ((err = snd_opl3_timer1_init(opl3, timer1_dev)) < 0)
+			return err;
+	if (timer2_dev >= 0) {
+		if ((err = snd_opl3_timer2_init(opl3, timer2_dev)) < 0) {
+			snd_device_free(opl3->card, opl3->timer1);
+			opl3->timer1 = NULL;
+			return err;
+		}
+	}
+	return 0;
+}
+
+int snd_opl3_hwdep_new(struct snd_opl3 * opl3,
+		       int device, int seq_device,
+		       struct snd_hwdep ** rhwdep)
+{
+	struct snd_hwdep *hw;
+	struct snd_card *card = opl3->card;
+	int err;
+
+	if (rhwdep)
+		*rhwdep = NULL;
+
+	/* create hardware dependent device (direct FM) */
+
+	if ((err = snd_hwdep_new(card, "OPL2/OPL3", device, &hw)) < 0) {
+		snd_device_free(card, opl3);
+		return err;
+	}
+	hw->private_data = opl3;
+#ifdef CONFIG_SND_OSSEMUL
+	if (device == 0) {
+		hw->oss_type = SNDRV_OSS_DEVICE_TYPE_DMFM;
+		sprintf(hw->oss_dev, "dmfm%i", card->number);
+	}
+#endif
+	strcpy(hw->name, hw->id);
+	switch (opl3->hardware & OPL3_HW_MASK) {
+	case OPL3_HW_OPL2:
+		strcpy(hw->name, "OPL2 FM");
+		hw->iface = SNDRV_HWDEP_IFACE_OPL2;
+		break;
+	case OPL3_HW_OPL3:
+		strcpy(hw->name, "OPL3 FM");
+		hw->iface = SNDRV_HWDEP_IFACE_OPL3;
+		break;
+	case OPL3_HW_OPL4:
+		strcpy(hw->name, "OPL4 FM");
+		hw->iface = SNDRV_HWDEP_IFACE_OPL4;
+		break;
+	}
+
+	/* operators - only ioctl */
+	hw->ops.open = snd_opl3_open;
+	hw->ops.ioctl = snd_opl3_ioctl;
+	hw->ops.release = snd_opl3_release;
+
+	opl3->seq_dev_num = seq_device;
+#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE))
+	if (snd_seq_device_new(card, seq_device, SNDRV_SEQ_DEV_ID_OPL3,
+			       sizeof(struct snd_opl3 *), &opl3->seq_dev) >= 0) {
+		strcpy(opl3->seq_dev->name, hw->name);
+		*(struct snd_opl3 **)SNDRV_SEQ_DEVICE_ARGPTR(opl3->seq_dev) = opl3;
+	}
+#endif
+	if (rhwdep)
+		*rhwdep = hw;
+	return 0;
+}
+
+EXPORT_SYMBOL(snd_opl3_interrupt);
+EXPORT_SYMBOL(snd_opl3_new);
+EXPORT_SYMBOL(snd_opl3_init);
+EXPORT_SYMBOL(snd_opl3_create);
+EXPORT_SYMBOL(snd_opl3_timer_new);
+EXPORT_SYMBOL(snd_opl3_hwdep_new);
+
+/* opl3_synth.c */
+EXPORT_SYMBOL(snd_opl3_regmap);
+EXPORT_SYMBOL(snd_opl3_reset);
+
+/*
+ *  INIT part
+ */
+
+static int __init alsa_opl3_init(void)
+{
+	return 0;
+}
+
+static void __exit alsa_opl3_exit(void)
+{
+}
+
+module_init(alsa_opl3_init)
+module_exit(alsa_opl3_exit)
Index: /GPL/branches/alsa-resync1/alsa-kernel/drivers/opl3/opl3_midi.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/drivers/opl3/opl3_midi.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/drivers/opl3/opl3_midi.c	(revision 91)
@@ -0,0 +1,873 @@
+/*
+ *  Copyright (c) by Uros Bizjak <uros@kss-loka.si>
+ *
+ *  Midi synth routines for OPL2/OPL3/OPL4 FM
+ *
+ *   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
+ *
+ */
+
+#undef DEBUG_ALLOC
+#undef DEBUG_MIDI
+
+#include "opl3_voice.h"
+#include <sound/asoundef.h>
+
+extern char snd_opl3_regmap[MAX_OPL2_VOICES][4];
+
+extern int use_internal_drums;
+
+/*
+ * The next table looks magical, but it certainly is not. Its values have
+ * been calculated as table[i]=8*log(i/64)/log(2) with an obvious exception
+ * for i=0. This log-table converts a linear volume-scaling (0..127) to a
+ * logarithmic scaling as present in the FM-synthesizer chips. so :    Volume
+ * 64 =  0 db = relative volume  0 and:    Volume 32 = -6 db = relative
+ * volume -8 it was implemented as a table because it is only 128 bytes and
+ * it saves a lot of log() calculations. (Rob Hooft <hooft@chem.ruu.nl>)
+ */
+
+static char opl3_volume_table[128] =
+{
+	-63, -48, -40, -35, -32, -29, -27, -26,
+	-24, -23, -21, -20, -19, -18, -18, -17,
+	-16, -15, -15, -14, -13, -13, -12, -12,
+	-11, -11, -10, -10, -10, -9, -9, -8,
+	-8, -8, -7, -7, -7, -6, -6, -6,
+	-5, -5, -5, -5, -4, -4, -4, -4,
+	-3, -3, -3, -3, -2, -2, -2, -2,
+	-2, -1, -1, -1, -1, 0, 0, 0,
+	0, 0, 0, 1, 1, 1, 1, 1,
+	1, 2, 2, 2, 2, 2, 2, 2,
+	3, 3, 3, 3, 3, 3, 3, 4,
+	4, 4, 4, 4, 4, 4, 4, 5,
+	5, 5, 5, 5, 5, 5, 5, 5,
+	6, 6, 6, 6, 6, 6, 6, 6,
+	6, 7, 7, 7, 7, 7, 7, 7,
+	7, 7, 7, 8, 8, 8, 8, 8
+};
+
+void snd_opl3_calc_volume(unsigned char *volbyte, int vel,
+			  struct snd_midi_channel *chan)
+{
+	int oldvol, newvol, n;
+	int volume;
+
+	volume = (vel * chan->gm_volume * chan->gm_expression) / (127*127);
+	if (volume > 127)
+		volume = 127;
+
+	oldvol = OPL3_TOTAL_LEVEL_MASK - (*volbyte & OPL3_TOTAL_LEVEL_MASK);
+
+	newvol = opl3_volume_table[volume] + oldvol;
+	if (newvol > OPL3_TOTAL_LEVEL_MASK)
+		newvol = OPL3_TOTAL_LEVEL_MASK;
+	else if (newvol < 0)
+		newvol = 0;
+
+	n = OPL3_TOTAL_LEVEL_MASK - (newvol & OPL3_TOTAL_LEVEL_MASK);
+
+	*volbyte = (*volbyte & OPL3_KSL_MASK) | (n & OPL3_TOTAL_LEVEL_MASK);
+}
+
+/*
+ * Converts the note frequency to block and fnum values for the FM chip
+ */
+static short opl3_note_table[16] =
+{
+	305, 323,	/* for pitch bending, -2 semitones */
+	343, 363, 385, 408, 432, 458, 485, 514, 544, 577, 611, 647,
+	686, 726	/* for pitch bending, +2 semitones */
+};
+
+static void snd_opl3_calc_pitch(unsigned char *fnum, unsigned char *blocknum,
+				int note, struct snd_midi_channel *chan)
+{
+	int block = ((note / 12) & 0x07) - 1;
+	int idx = (note % 12) + 2;
+	int freq;
+
+	if (chan->midi_pitchbend) {
+		int pitchbend = chan->midi_pitchbend;
+		int segment;
+
+		if (pitchbend > 0x1FFF)
+			pitchbend = 0x1FFF;
+
+		segment = pitchbend / 0x1000;
+		freq = opl3_note_table[idx+segment];
+		freq += ((opl3_note_table[idx+segment+1] - freq) *
+			 (pitchbend % 0x1000)) / 0x1000;
+	} else {
+		freq = opl3_note_table[idx];
+	}
+
+	*fnum = (unsigned char) freq;
+	*blocknum = ((freq >> 8) & OPL3_FNUM_HIGH_MASK) |
+		((block << 2) & OPL3_BLOCKNUM_MASK);
+}
+
+
+#ifdef DEBUG_ALLOC
+static void debug_alloc(struct snd_opl3 *opl3, char *s, int voice) {
+	int i;
+	char *str = "x.24";
+
+	printk("time %.5i: %s [%.2i]: ", opl3->use_time, s, voice);
+	for (i = 0; i < opl3->max_voices; i++)
+		printk("%c", *(str + opl3->voices[i].state + 1));
+	printk("\n");
+}
+#endif
+
+/*
+ * Get a FM voice (channel) to play a note on.
+ */
+static int opl3_get_voice(struct snd_opl3 *opl3, int instr_4op,
+			  struct snd_midi_channel *chan) {
+	int chan_4op_1;		/* first voice for 4op instrument */
+	int chan_4op_2;		/* second voice for 4op instrument */
+
+	struct snd_opl3_voice *vp, *vp2;
+	unsigned int voice_time;
+	int i;
+
+#ifdef DEBUG_ALLOC
+	char *alloc_type[3] = { "FREE     ", "CHEAP    ", "EXPENSIVE" };
+#endif
+
+	/* This is our "allocation cost" table */
+	enum {
+		FREE = 0, CHEAP, EXPENSIVE, END
+	};
+
+	/* Keeps track of what we are finding */
+	struct best {
+		unsigned int time;
+		int voice;
+	} best[END];
+	struct best *bp;
+
+	for (i = 0; i < END; i++) {
+		best[i].time = (unsigned int)(-1); /* XXX MAX_?INT really */;
+		best[i].voice = -1;
+	}
+
+	/* Look through all the channels for the most suitable. */
+	for (i = 0; i < opl3->max_voices; i++) {
+		vp = &opl3->voices[i];
+
+		if (vp->state == SNDRV_OPL3_ST_NOT_AVAIL)
+		  /* skip unavailable channels, allocated by
+		     drum voices or by bounded 4op voices) */
+			continue;
+
+		voice_time = vp->time;
+		bp = best;
+
+		chan_4op_1 = ((i < 3) || (i > 8 && i < 12));
+		chan_4op_2 = ((i > 2 && i < 6) || (i > 11 && i < 15));
+		if (instr_4op) {
+			/* allocate 4op voice */
+			/* skip channels unavailable to 4op instrument */
+			if (!chan_4op_1)
+				continue;
+
+			if (vp->state)
+				/* kill one voice, CHEAP */
+				bp++;
+			/* get state of bounded 2op channel
+			   to be allocated for 4op instrument */
+			vp2 = &opl3->voices[i + 3];
+			if (vp2->state == SNDRV_OPL3_ST_ON_2OP) {
+				/* kill two voices, EXPENSIVE */
+				bp++;
+				voice_time = (voice_time > vp->time) ?
+					voice_time : vp->time;
+			}
+		} else {
+			/* allocate 2op voice */
+			if ((chan_4op_1) || (chan_4op_2))
+				/* use bounded channels for 2op, CHEAP */
+				bp++;
+			else if (vp->state)
+				/* kill one voice on 2op channel, CHEAP */
+				bp++;
+			/* raise kill cost to EXPENSIVE for all channels */
+			if (vp->state)
+				bp++;
+		}
+		if (voice_time < bp->time) {
+			bp->time = voice_time;
+			bp->voice = i;
+		}
+	}
+
+	for (i = 0; i < END; i++) {
+		if (best[i].voice >= 0) {
+#ifdef DEBUG_ALLOC
+			printk("%s %iop allocation on voice %i\n",
+			       alloc_type[i], instr_4op ? 4 : 2,
+			       best[i].voice);
+#endif
+			return best[i].voice;
+		}
+	}
+	/* not found */
+	return -1;
+}
+
+/* ------------------------------ */
+
+/*
+ * System timer interrupt function
+ */
+void snd_opl3_timer_func(unsigned long data)
+{
+
+	struct snd_opl3 *opl3 = (struct snd_opl3 *)data;
+	int again = 0;
+	int i;
+
+	spin_lock(&opl3->sys_timer_lock);
+	for (i = 0; i < opl3->max_voices; i++) {
+		struct snd_opl3_voice *vp = &opl3->voices[i];
+		if (vp->state > 0 && vp->note_off_check) {
+			if (vp->note_off == jiffies)
+				snd_opl3_note_off(opl3, vp->note, 0, vp->chan);
+			else
+				again++;
+		}
+	}
+	if (again) {
+		opl3->tlist.expires = jiffies + 1;	/* invoke again */
+		add_timer(&opl3->tlist);
+	} else {
+		opl3->sys_timer_status = 0;
+	}
+	spin_unlock(&opl3->sys_timer_lock);
+}
+
+/*
+ * Start system timer
+ */
+static void snd_opl3_start_timer(struct snd_opl3 *opl3)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&opl3->sys_timer_lock, flags);
+	if (! opl3->sys_timer_status) {
+		opl3->tlist.expires = jiffies + 1;
+		add_timer(&opl3->tlist);
+		opl3->sys_timer_status = 1;
+	}
+	spin_unlock_irqrestore(&opl3->sys_timer_lock, flags);
+}
+
+/* ------------------------------ */
+
+
+static int snd_opl3_oss_map[MAX_OPL3_VOICES] = {
+	0, 1, 2, 9, 10, 11, 6, 7, 8, 15, 16, 17, 3, 4 ,5, 12, 13, 14
+};
+
+/*
+ * Start a note.
+ */
+void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan)
+{
+	struct snd_opl3 *opl3;
+	struct snd_seq_instr wanted;
+	struct snd_seq_kinstr *kinstr;
+	int instr_4op;
+
+	int voice;
+	struct snd_opl3_voice *vp, *vp2;
+	unsigned short connect_mask;
+	unsigned char connection;
+	unsigned char vol_op[4];
+
+	int extra_prg = 0;
+
+	unsigned short reg_side;
+	unsigned char op_offset;
+	unsigned char voice_offset;
+	unsigned short opl3_reg;
+	unsigned char reg_val;
+
+	int key = note;
+	unsigned char fnum, blocknum;
+	int i;
+
+	struct fm_instrument *fm;
+	unsigned long flags;
+
+	opl3 = p;
+
+#ifdef DEBUG_MIDI
+	snd_printk("Note on, ch %i, inst %i, note %i, vel %i\n",
+		   chan->number, chan->midi_program, note, vel);
+#endif
+	wanted.cluster = 0;
+	wanted.std = SNDRV_SEQ_INSTR_TYPE2_OPL2_3;
+
+	/* in SYNTH mode, application takes care of voices */
+	/* in SEQ mode, drum voice numbers are notes on drum channel */
+	if (opl3->synth_mode == SNDRV_OPL3_MODE_SEQ) {
+		if (chan->drum_channel) {
+			/* percussion instruments are located in bank 128 */
+			wanted.bank = 128;
+			wanted.prg = note;
+		} else {
+			wanted.bank = chan->gm_bank_select;
+			wanted.prg = chan->midi_program;
+		}
+	} else {
+		/* Prepare for OSS mode */
+		if (chan->number >= MAX_OPL3_VOICES)
+			return;
+
+		/* OSS instruments are located in bank 127 */
+		wanted.bank = 127;
+		wanted.prg = chan->midi_program;
+	}
+
+	spin_lock_irqsave(&opl3->voice_lock, flags);
+
+	if (use_internal_drums) {
+		snd_opl3_drum_switch(opl3, note, vel, 1, chan);
+		spin_unlock_irqrestore(&opl3->voice_lock, flags);
+		return;
+	}
+
+ __extra_prg:
+	kinstr = snd_seq_instr_find(opl3->ilist, &wanted, 1, 0);
+	if (kinstr == NULL) {
+		spin_unlock_irqrestore(&opl3->voice_lock, flags);
+		return;
+	}
+
+	fm = KINSTR_DATA(kinstr);
+
+	switch (fm->type) {
+	case FM_PATCH_OPL2:
+		instr_4op = 0;
+		break;
+	case FM_PATCH_OPL3:
+		if (opl3->hardware >= OPL3_HW_OPL3) {
+			instr_4op = 1;
+			break;
+		}
+	default:
+		snd_seq_instr_free_use(opl3->ilist, kinstr);
+		spin_unlock_irqrestore(&opl3->voice_lock, flags);
+		return;
+	}
+
+#ifdef DEBUG_MIDI
+	snd_printk("  --> OPL%i instrument: %s\n",
+		   instr_4op ? 3 : 2, kinstr->name);
+#endif
+	/* in SYNTH mode, application takes care of voices */
+	/* in SEQ mode, allocate voice on free OPL3 channel */
+	if (opl3->synth_mode == SNDRV_OPL3_MODE_SEQ) {
+		voice = opl3_get_voice(opl3, instr_4op, chan);
+	} else {
+		/* remap OSS voice */
+		voice = snd_opl3_oss_map[chan->number];		
+	}
+
+	if (voice < MAX_OPL2_VOICES) {
+		/* Left register block for voices 0 .. 8 */
+		reg_side = OPL3_LEFT;
+		voice_offset = voice;
+		connect_mask = (OPL3_LEFT_4OP_0 << voice_offset) & 0x07;
+	} else {
+		/* Right register block for voices 9 .. 17 */
+		reg_side = OPL3_RIGHT;
+		voice_offset = voice - MAX_OPL2_VOICES;
+		connect_mask = (OPL3_RIGHT_4OP_0 << voice_offset) & 0x38;
+	}
+
+	/* kill voice on channel */
+	vp = &opl3->voices[voice];
+	if (vp->state > 0) {
+		opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK + voice_offset);
+		reg_val = vp->keyon_reg & ~OPL3_KEYON_BIT;
+		opl3->command(opl3, opl3_reg, reg_val);
+	}
+	if (instr_4op) {
+		vp2 = &opl3->voices[voice + 3];
+		if (vp->state > 0) {
+			opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK +
+					       voice_offset + 3);
+			reg_val = vp->keyon_reg & ~OPL3_KEYON_BIT;
+			opl3->command(opl3, opl3_reg, reg_val);
+		}
+	}
+
+	/* set connection register */
+	if (instr_4op) {
+		if ((opl3->connection_reg ^ connect_mask) & connect_mask) {
+			opl3->connection_reg |= connect_mask;
+			/* set connection bit */
+			opl3_reg = OPL3_RIGHT | OPL3_REG_CONNECTION_SELECT;
+			opl3->command(opl3, opl3_reg, opl3->connection_reg);
+		}
+	} else {
+		if ((opl3->connection_reg ^ ~connect_mask) & connect_mask) {
+			opl3->connection_reg &= ~connect_mask;
+			/* clear connection bit */
+			opl3_reg = OPL3_RIGHT | OPL3_REG_CONNECTION_SELECT;
+			opl3->command(opl3, opl3_reg, opl3->connection_reg);
+		}
+	}
+
+#ifdef DEBUG_MIDI
+	snd_printk("  --> setting OPL3 connection: 0x%x\n",
+		   opl3->connection_reg);
+#endif
+	/*
+	 * calculate volume depending on connection
+	 * between FM operators (see include/opl3.h)
+	 */
+	for (i = 0; i < (instr_4op ? 4 : 2); i++)
+		vol_op[i] = fm->op[i].ksl_level;
+
+	connection = fm->feedback_connection[0] & 0x01;
+	if (instr_4op) {
+		connection <<= 1;
+		connection |= fm->feedback_connection[1] & 0x01;
+
+		snd_opl3_calc_volume(&vol_op[3], vel, chan);
+		switch (connection) {
+		case 0x03:
+			snd_opl3_calc_volume(&vol_op[2], vel, chan);
+			/* fallthru */
+		case 0x02:
+			snd_opl3_calc_volume(&vol_op[0], vel, chan);
+			break;
+		case 0x01:
+			snd_opl3_calc_volume(&vol_op[1], vel, chan);
+		}
+	} else {
+		snd_opl3_calc_volume(&vol_op[1], vel, chan);
+		if (connection)
+			snd_opl3_calc_volume(&vol_op[0], vel, chan);
+	}
+
+	/* Program the FM voice characteristics */
+	for (i = 0; i < (instr_4op ? 4 : 2); i++) {
+#ifdef DEBUG_MIDI
+		snd_printk("  --> programming operator %i\n", i);
+#endif
+		op_offset = snd_opl3_regmap[voice_offset][i];
+
+		/* Set OPL3 AM_VIB register of requested voice/operator */ 
+		reg_val = fm->op[i].am_vib;
+		opl3_reg = reg_side | (OPL3_REG_AM_VIB + op_offset);
+		opl3->command(opl3, opl3_reg, reg_val);
+
+		/* Set OPL3 KSL_LEVEL register of requested voice/operator */ 
+		reg_val = vol_op[i];
+		opl3_reg = reg_side | (OPL3_REG_KSL_LEVEL + op_offset);
+		opl3->command(opl3, opl3_reg, reg_val);
+
+		/* Set OPL3 ATTACK_DECAY register of requested voice/operator */ 
+		reg_val = fm->op[i].attack_decay;
+		opl3_reg = reg_side | (OPL3_REG_ATTACK_DECAY + op_offset);
+		opl3->command(opl3, opl3_reg, reg_val);
+
+		/* Set OPL3 SUSTAIN_RELEASE register of requested voice/operator */ 
+		reg_val = fm->op[i].sustain_release;
+		opl3_reg = reg_side | (OPL3_REG_SUSTAIN_RELEASE + op_offset);
+		opl3->command(opl3, opl3_reg, reg_val);
+
+		/* Select waveform */
+		reg_val = fm->op[i].wave_select;
+		opl3_reg = reg_side | (OPL3_REG_WAVE_SELECT + op_offset);
+		opl3->command(opl3, opl3_reg, reg_val);
+	}
+
+	/* Set operator feedback and 2op inter-operator connection */
+	reg_val = fm->feedback_connection[0];
+	/* Set output voice connection */
+	reg_val |= OPL3_STEREO_BITS;
+	if (chan->gm_pan < 43)
+		reg_val &= ~OPL3_VOICE_TO_RIGHT;
+	if (chan->gm_pan > 85)
+		reg_val &= ~OPL3_VOICE_TO_LEFT;
+	opl3_reg = reg_side | (OPL3_REG_FEEDBACK_CONNECTION + voice_offset);
+	opl3->command(opl3, opl3_reg, reg_val);
+
+	if (instr_4op) {
+		/* Set 4op inter-operator connection */
+		reg_val = fm->feedback_connection[1] & OPL3_CONNECTION_BIT;
+		/* Set output voice connection */
+		reg_val |= OPL3_STEREO_BITS;
+		if (chan->gm_pan < 43)
+			reg_val &= ~OPL3_VOICE_TO_RIGHT;
+		if (chan->gm_pan > 85)
+			reg_val &= ~OPL3_VOICE_TO_LEFT;
+		opl3_reg = reg_side | (OPL3_REG_FEEDBACK_CONNECTION +
+				       voice_offset + 3);
+		opl3->command(opl3, opl3_reg, reg_val);
+	}
+
+	/*
+	 * Special treatment of percussion notes for fm:
+	 * Requested pitch is really program, and pitch for
+	 * device is whatever was specified in the patch library.
+	 */
+	if (fm->fix_key)
+		note = fm->fix_key;
+	/*
+	 * use transpose if defined in patch library
+	 */
+	if (fm->trnsps)
+		note += (fm->trnsps - 64);
+
+	snd_opl3_calc_pitch(&fnum, &blocknum, note, chan);
+
+	/* Set OPL3 FNUM_LOW register of requested voice */
+	opl3_reg = reg_side | (OPL3_REG_FNUM_LOW + voice_offset);
+	opl3->command(opl3, opl3_reg, fnum);
+
+	opl3->voices[voice].keyon_reg = blocknum;
+
+	/* Set output sound flag */
+	blocknum |= OPL3_KEYON_BIT;
+
+#ifdef DEBUG_MIDI
+	snd_printk("  --> trigger voice %i\n", voice);
+#endif
+	/* Set OPL3 KEYON_BLOCK register of requested voice */ 
+	opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK + voice_offset);
+	opl3->command(opl3, opl3_reg, blocknum);
+
+	/* kill note after fixed duration (in centiseconds) */
+	if (fm->fix_dur) {
+		opl3->voices[voice].note_off = jiffies +
+			(fm->fix_dur * HZ) / 100;
+		snd_opl3_start_timer(opl3);
+		opl3->voices[voice].note_off_check = 1;
+	} else
+		opl3->voices[voice].note_off_check = 0;
+
+	/* get extra pgm, but avoid possible loops */
+	extra_prg = (extra_prg) ? 0 : fm->modes;
+
+	snd_seq_instr_free_use(opl3->ilist, kinstr);
+
+	/* do the bookkeeping */
+	vp->time = opl3->use_time++;
+	vp->note = key;
+	vp->chan = chan;
+
+	if (instr_4op) {
+		vp->state = SNDRV_OPL3_ST_ON_4OP;
+
+		vp2 = &opl3->voices[voice + 3];
+		vp2->time = opl3->use_time++;
+		vp2->note = key;
+		vp2->chan = chan;
+		vp2->state = SNDRV_OPL3_ST_NOT_AVAIL;
+	} else {
+		if (vp->state == SNDRV_OPL3_ST_ON_4OP) {
+			/* 4op killed by 2op, release bounded voice */
+			vp2 = &opl3->voices[voice + 3];
+			vp2->time = opl3->use_time++;
+			vp2->state = SNDRV_OPL3_ST_OFF;
+		}
+		vp->state = SNDRV_OPL3_ST_ON_2OP;
+	}
+
+#ifdef DEBUG_ALLOC
+	debug_alloc(opl3, "note on ", voice);
+#endif
+
+	/* allocate extra program if specified in patch library */
+	if (extra_prg) {
+		if (extra_prg > 128) {
+			wanted.bank = 128;
+			/* percussions start at 35 */
+			wanted.prg = extra_prg - 128 + 35 - 1;
+		} else {
+			wanted.bank = 0;
+			wanted.prg = extra_prg - 1;
+		}
+#ifdef DEBUG_MIDI
+		snd_printk(" *** allocating extra program\n");
+#endif
+		goto __extra_prg;
+	}
+	spin_unlock_irqrestore(&opl3->voice_lock, flags);
+}
+
+static void snd_opl3_kill_voice(struct snd_opl3 *opl3, int voice)
+{
+	unsigned short reg_side;
+	unsigned char voice_offset;
+	unsigned short opl3_reg;
+
+	struct snd_opl3_voice *vp, *vp2;
+
+	snd_assert(voice < MAX_OPL3_VOICES, return);
+
+	vp = &opl3->voices[voice];
+	if (voice < MAX_OPL2_VOICES) {
+		/* Left register block for voices 0 .. 8 */
+		reg_side = OPL3_LEFT;
+		voice_offset = voice;
+	} else {
+		/* Right register block for voices 9 .. 17 */
+		reg_side = OPL3_RIGHT;
+		voice_offset = voice - MAX_OPL2_VOICES;
+	}
+
+	/* kill voice */
+#ifdef DEBUG_MIDI
+	snd_printk("  --> kill voice %i\n", voice);
+#endif
+	opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK + voice_offset);
+	/* clear Key ON bit */
+	opl3->command(opl3, opl3_reg, vp->keyon_reg);
+
+	/* do the bookkeeping */
+	vp->time = opl3->use_time++;
+
+	if (vp->state == SNDRV_OPL3_ST_ON_4OP) {
+		vp2 = &opl3->voices[voice + 3];
+
+		vp2->time = opl3->use_time++;
+		vp2->state = SNDRV_OPL3_ST_OFF;
+	}
+	vp->state = SNDRV_OPL3_ST_OFF;
+#ifdef DEBUG_ALLOC
+	debug_alloc(opl3, "note off", voice);
+#endif
+
+}
+
+/*
+ * Release a note in response to a midi note off.
+ */
+void snd_opl3_note_off(void *p, int note, int vel, struct snd_midi_channel *chan)
+{
+  	struct snd_opl3 *opl3;
+
+	int voice;
+	struct snd_opl3_voice *vp;
+
+	unsigned long flags;
+
+	opl3 = p;
+
+#ifdef DEBUG_MIDI
+	snd_printk("Note off, ch %i, inst %i, note %i\n",
+		   chan->number, chan->midi_program, note);
+#endif
+
+	spin_lock_irqsave(&opl3->voice_lock, flags);
+
+	if (opl3->synth_mode == SNDRV_OPL3_MODE_SEQ) {
+		if (chan->drum_channel && use_internal_drums) {
+			snd_opl3_drum_switch(opl3, note, vel, 0, chan);
+			spin_unlock_irqrestore(&opl3->voice_lock, flags);
+			return;
+		}
+		/* this loop will hopefully kill all extra voices, because
+		   they are grouped by the same channel and note values */
+		for (voice = 0; voice < opl3->max_voices; voice++) {
+			vp = &opl3->voices[voice];
+			if (vp->state > 0 && vp->chan == chan && vp->note == note) {
+				snd_opl3_kill_voice(opl3, voice);
+			}
+		}
+	} else {
+		/* remap OSS voices */
+		if (chan->number < MAX_OPL3_VOICES) {
+			voice = snd_opl3_oss_map[chan->number];		
+			snd_opl3_kill_voice(opl3, voice);
+		}
+	}
+	spin_unlock_irqrestore(&opl3->voice_lock, flags);
+}
+
+/*
+ * key pressure change
+ */
+void snd_opl3_key_press(void *p, int note, int vel, struct snd_midi_channel *chan)
+{
+  	struct snd_opl3 *opl3;
+
+	opl3 = p;
+#ifdef DEBUG_MIDI
+	snd_printk("Key pressure, ch#: %i, inst#: %i\n",
+		   chan->number, chan->midi_program);
+#endif
+}
+
+/*
+ * terminate note
+ */
+void snd_opl3_terminate_note(void *p, int note, struct snd_midi_channel *chan)
+{
+  	struct snd_opl3 *opl3;
+
+	opl3 = p;
+#ifdef DEBUG_MIDI
+	snd_printk("Terminate note, ch#: %i, inst#: %i\n",
+		   chan->number, chan->midi_program);
+#endif
+}
+
+static void snd_opl3_update_pitch(struct snd_opl3 *opl3, int voice)
+{
+	unsigned short reg_side;
+	unsigned char voice_offset;
+	unsigned short opl3_reg;
+
+	unsigned char fnum, blocknum;
+
+	struct snd_opl3_voice *vp;
+
+	snd_assert(voice < MAX_OPL3_VOICES, return);
+
+	vp = &opl3->voices[voice];
+	if (vp->chan == NULL)
+		return; /* not allocated? */
+
+	if (voice < MAX_OPL2_VOICES) {
+		/* Left register block for voices 0 .. 8 */
+		reg_side = OPL3_LEFT;
+		voice_offset = voice;
+	} else {
+		/* Right register block for voices 9 .. 17 */
+		reg_side = OPL3_RIGHT;
+		voice_offset = voice - MAX_OPL2_VOICES;
+	}
+
+	snd_opl3_calc_pitch(&fnum, &blocknum, vp->note, vp->chan);
+
+	/* Set OPL3 FNUM_LOW register of requested voice */
+	opl3_reg = reg_side | (OPL3_REG_FNUM_LOW + voice_offset);
+	opl3->command(opl3, opl3_reg, fnum);
+
+	vp->keyon_reg = blocknum;
+
+	/* Set output sound flag */
+	blocknum |= OPL3_KEYON_BIT;
+
+	/* Set OPL3 KEYON_BLOCK register of requested voice */ 
+	opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK + voice_offset);
+	opl3->command(opl3, opl3_reg, blocknum);
+
+	vp->time = opl3->use_time++;
+}
+
+/*
+ * Update voice pitch controller
+ */
+static void snd_opl3_pitch_ctrl(struct snd_opl3 *opl3, struct snd_midi_channel *chan)
+{
+	int voice;
+	struct snd_opl3_voice *vp;
+
+	unsigned long flags;
+
+	spin_lock_irqsave(&opl3->voice_lock, flags);
+
+	if (opl3->synth_mode == SNDRV_OPL3_MODE_SEQ) {
+		for (voice = 0; voice < opl3->max_voices; voice++) {
+			vp = &opl3->voices[voice];
+			if (vp->state > 0 && vp->chan == chan) {
+				snd_opl3_update_pitch(opl3, voice);
+			}
+		}
+	} else {
+		/* remap OSS voices */
+		if (chan->number < MAX_OPL3_VOICES) {
+			voice = snd_opl3_oss_map[chan->number];		
+			snd_opl3_update_pitch(opl3, voice);
+		}
+	}
+	spin_unlock_irqrestore(&opl3->voice_lock, flags);
+}
+
+/*
+ * Deal with a controler type event.  This includes all types of
+ * control events, not just the midi controllers
+ */
+void snd_opl3_control(void *p, int type, struct snd_midi_channel *chan)
+{
+  	struct snd_opl3 *opl3;
+
+	opl3 = p;
+#ifdef DEBUG_MIDI
+	snd_printk("Controller, TYPE = %i, ch#: %i, inst#: %i\n",
+		   type, chan->number, chan->midi_program);
+#endif
+
+	switch (type) {
+	case MIDI_CTL_MSB_MODWHEEL:
+		if (chan->control[MIDI_CTL_MSB_MODWHEEL] > 63)
+			opl3->drum_reg |= OPL3_VIBRATO_DEPTH;
+		else 
+			opl3->drum_reg &= ~OPL3_VIBRATO_DEPTH;
+		opl3->command(opl3, OPL3_LEFT | OPL3_REG_PERCUSSION,
+				 opl3->drum_reg);
+		break;
+	case MIDI_CTL_E2_TREMOLO_DEPTH:
+		if (chan->control[MIDI_CTL_E2_TREMOLO_DEPTH] > 63)
+			opl3->drum_reg |= OPL3_TREMOLO_DEPTH;
+		else 
+			opl3->drum_reg &= ~OPL3_TREMOLO_DEPTH;
+		opl3->command(opl3, OPL3_LEFT | OPL3_REG_PERCUSSION,
+				 opl3->drum_reg);
+		break;
+	case MIDI_CTL_PITCHBEND:
+		snd_opl3_pitch_ctrl(opl3, chan);
+		break;
+	}
+}
+
+/*
+ * NRPN events
+ */
+void snd_opl3_nrpn(void *p, struct snd_midi_channel *chan,
+		   struct snd_midi_channel_set *chset)
+{
+  	struct snd_opl3 *opl3;
+
+	opl3 = p;
+#ifdef DEBUG_MIDI
+	snd_printk("NRPN, ch#: %i, inst#: %i\n",
+		   chan->number, chan->midi_program);
+#endif
+}
+
+/*
+ * receive sysex
+ */
+void snd_opl3_sysex(void *p, unsigned char *buf, int len,
+		    int parsed, struct snd_midi_channel_set *chset)
+{
+  	struct snd_opl3 *opl3;
+
+	opl3 = p;
+#ifdef DEBUG_MIDI
+	snd_printk("SYSEX\n");
+#endif
+}
Index: /GPL/branches/alsa-resync1/alsa-kernel/drivers/opl3/opl3_seq.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/drivers/opl3/opl3_seq.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/drivers/opl3/opl3_seq.c	(revision 91)
@@ -0,0 +1,314 @@
+/*
+ *  Copyright (c) by Uros Bizjak <uros@kss-loka.si>
+ *
+ *  Midi Sequencer interface routines for OPL2/OPL3/OPL4 FM
+ *
+ *  OPL2/3 FM instrument loader:
+ *   alsa-tools/seq/sbiload/
+ *
+ *   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 "opl3_voice.h"
+#include <linux/init.h>
+#include <linux/moduleparam.h>
+#include <sound/initval.h>
+
+MODULE_AUTHOR("Uros Bizjak <uros@kss-loka.si>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("ALSA driver for OPL3 FM synth");
+
+int use_internal_drums = 0;
+//module_param(use_internal_drums, bool, 0444);
+MODULE_PARM_DESC(use_internal_drums, "Enable internal OPL2/3 drums.");
+
+int snd_opl3_synth_use_inc(struct snd_opl3 * opl3)
+{
+	if (!try_module_get(opl3->card->module))
+		return -EFAULT;
+	return 0;
+
+}
+
+void snd_opl3_synth_use_dec(struct snd_opl3 * opl3)
+{
+	module_put(opl3->card->module);
+}
+
+int snd_opl3_synth_setup(struct snd_opl3 * opl3)
+{
+	int idx;
+
+	down(&opl3->access_mutex);
+	if (opl3->used) {
+		up(&opl3->access_mutex);
+		return -EBUSY;
+	}
+	opl3->used++;
+	up(&opl3->access_mutex);
+
+	snd_opl3_reset(opl3);
+
+	for (idx = 0; idx < MAX_OPL3_VOICES; idx++) {
+		opl3->voices[idx].state = SNDRV_OPL3_ST_OFF;
+		opl3->voices[idx].time = 0;
+		opl3->voices[idx].keyon_reg = 0x00;
+	}
+	opl3->use_time = 0;
+	opl3->connection_reg = 0x00;
+	if (opl3->hardware >= OPL3_HW_OPL3) {
+		/* Clear 4-op connections */
+		opl3->command(opl3, OPL3_RIGHT | OPL3_REG_CONNECTION_SELECT,
+				 opl3->connection_reg);
+		opl3->max_voices = MAX_OPL3_VOICES;
+	}
+	return 0;
+}
+
+void snd_opl3_synth_cleanup(struct snd_opl3 * opl3)
+{
+	unsigned long flags;
+
+	/* Stop system timer */
+	spin_lock_irqsave(&opl3->sys_timer_lock, flags);
+	if (opl3->sys_timer_status) {
+		del_timer(&opl3->tlist);
+		opl3->sys_timer_status = 0;
+	}
+	spin_unlock_irqrestore(&opl3->sys_timer_lock, flags);
+
+	snd_opl3_reset(opl3);
+	down(&opl3->access_mutex);
+	opl3->used--;
+	up(&opl3->access_mutex);
+}
+
+static int snd_opl3_synth_use(void *private_data, struct snd_seq_port_subscribe * info)
+{
+	struct snd_opl3 *opl3 = private_data;
+	int err;
+
+	if ((err = snd_opl3_synth_setup(opl3)) < 0)
+		return err;
+
+	if (use_internal_drums) {
+		/* Percussion mode */
+		opl3->voices[6].state = opl3->voices[7].state = 
+			opl3->voices[8].state = SNDRV_OPL3_ST_NOT_AVAIL;
+		snd_opl3_load_drums(opl3);
+		opl3->drum_reg = OPL3_PERCUSSION_ENABLE;
+		opl3->command(opl3, OPL3_LEFT | OPL3_REG_PERCUSSION, opl3->drum_reg);
+	} else {
+		opl3->drum_reg = 0x00;
+	}
+
+	if (info->sender.client != SNDRV_SEQ_CLIENT_SYSTEM) {
+		if ((err = snd_opl3_synth_use_inc(opl3)) < 0)
+			return err;
+	}
+	opl3->synth_mode = SNDRV_OPL3_MODE_SEQ;
+	return 0;
+}
+
+static int snd_opl3_synth_unuse(void *private_data, struct snd_seq_port_subscribe * info)
+{
+	struct snd_opl3 *opl3 = private_data;
+
+	snd_opl3_synth_cleanup(opl3);
+
+	if (info->sender.client != SNDRV_SEQ_CLIENT_SYSTEM)
+		snd_opl3_synth_use_dec(opl3);
+	return 0;
+}
+
+/*
+ * MIDI emulation operators
+ */
+struct snd_midi_op opl3_ops = {
+	.note_on =		snd_opl3_note_on,
+	.note_off =		snd_opl3_note_off,
+	.key_press =		snd_opl3_key_press,
+	.note_terminate =	snd_opl3_terminate_note,
+	.control =		snd_opl3_control,
+	.nrpn =			snd_opl3_nrpn,
+	.sysex =		snd_opl3_sysex,
+};
+
+static int snd_opl3_synth_event_input(struct snd_seq_event * ev, int direct,
+				      void *private_data, int atomic, int hop)
+{
+	struct snd_opl3 *opl3 = private_data;
+
+	if (ev->type >= SNDRV_SEQ_EVENT_INSTR_BEGIN &&
+	    ev->type <= SNDRV_SEQ_EVENT_INSTR_CHANGE) {
+		if (direct) {
+			snd_seq_instr_event(&opl3->fm_ops, opl3->ilist, ev,
+					    opl3->seq_client, atomic, hop);
+		}
+	} else {
+		snd_midi_process_event(&opl3_ops, ev, opl3->chset);
+	}
+	return 0;
+}
+
+/* ------------------------------ */
+
+static void snd_opl3_synth_free_port(void *private_data)
+{
+	struct snd_opl3 *opl3 = private_data;
+
+	snd_midi_channel_free_set(opl3->chset);
+}
+
+static int snd_opl3_synth_create_port(struct snd_opl3 * opl3)
+{
+	struct snd_seq_port_callback callbacks;
+	char name[32];
+	int voices, opl_ver;
+
+	voices = (opl3->hardware < OPL3_HW_OPL3) ?
+		MAX_OPL2_VOICES : MAX_OPL3_VOICES;
+	opl3->chset = snd_midi_channel_alloc_set(16);
+	if (opl3->chset == NULL)
+		return -ENOMEM;
+	opl3->chset->private_data = opl3;
+
+	memset(&callbacks, 0, sizeof(callbacks));
+	callbacks.owner = THIS_MODULE;
+	callbacks.use = snd_opl3_synth_use;
+	callbacks.unuse = snd_opl3_synth_unuse;
+	callbacks.event_input = snd_opl3_synth_event_input;
+	callbacks.private_free = snd_opl3_synth_free_port;
+	callbacks.private_data = opl3;
+
+	opl_ver = (opl3->hardware & OPL3_HW_MASK) >> 8;
+	sprintf(name, "OPL%i FM Port", opl_ver);
+
+	opl3->chset->client = opl3->seq_client;
+	opl3->chset->port = snd_seq_event_port_attach(opl3->seq_client, &callbacks,
+						      SNDRV_SEQ_PORT_CAP_WRITE |
+						      SNDRV_SEQ_PORT_CAP_SUBS_WRITE,
+						      SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC |
+						      SNDRV_SEQ_PORT_TYPE_MIDI_GM |
+						      SNDRV_SEQ_PORT_TYPE_SYNTH,
+						      16, voices,
+						      name);
+	if (opl3->chset->port < 0) {
+		snd_midi_channel_free_set(opl3->chset);
+		return opl3->chset->port;
+	}
+	return 0;
+}
+
+/* ------------------------------ */
+
+static int snd_opl3_seq_new_device(struct snd_seq_device *dev)
+{
+	struct snd_opl3 *opl3;
+	int client;
+	struct snd_seq_client_callback callbacks;
+	struct snd_seq_client_info cinfo;
+	int opl_ver;
+
+	opl3 = *(struct snd_opl3 **)SNDRV_SEQ_DEVICE_ARGPTR(dev);
+	if (opl3 == NULL)
+		return -EINVAL;
+
+	spin_lock_init(&opl3->voice_lock);
+
+	opl3->seq_client = -1;
+
+	/* allocate new client */
+	memset(&callbacks, 0, sizeof(callbacks));
+	callbacks.private_data = opl3;
+	callbacks.allow_output = callbacks.allow_input = 1;
+	client = opl3->seq_client =
+	    snd_seq_create_kernel_client(opl3->card, opl3->seq_dev_num, &callbacks);
+	if (client < 0)
+		return client;
+
+	/* change name of client */
+	memset(&cinfo, 0, sizeof(cinfo));
+	cinfo.client = client;
+	cinfo.type = KERNEL_CLIENT;
+	opl_ver = (opl3->hardware & OPL3_HW_MASK) >> 8;
+	sprintf(cinfo.name, "OPL%i FM synth", opl_ver);
+	snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, &cinfo);
+
+	snd_opl3_synth_create_port(opl3);
+
+	/* initialize instrument list */
+	opl3->ilist = snd_seq_instr_list_new();
+	if (opl3->ilist == NULL) {
+		snd_seq_delete_kernel_client(client);
+		opl3->seq_client = -1;
+		return -ENOMEM;
+	}
+	opl3->ilist->flags = SNDRV_SEQ_INSTR_FLG_DIRECT;
+	snd_seq_fm_init(&opl3->fm_ops, NULL);
+
+	/* setup system timer */
+	init_timer(&opl3->tlist);
+	opl3->tlist.function = snd_opl3_timer_func;
+	opl3->tlist.data = (unsigned long) opl3;
+	spin_lock_init(&opl3->sys_timer_lock);
+	opl3->sys_timer_status = 0;
+
+#ifdef CONFIG_SND_SEQUENCER_OSS
+	snd_opl3_init_seq_oss(opl3, cinfo.name);
+#endif
+	return 0;
+}
+
+static int snd_opl3_seq_delete_device(struct snd_seq_device *dev)
+{
+	struct snd_opl3 *opl3;
+
+	opl3 = *(struct snd_opl3 **)SNDRV_SEQ_DEVICE_ARGPTR(dev);
+	if (opl3 == NULL)
+		return -EINVAL;
+
+#ifdef CONFIG_SND_SEQUENCER_OSS
+	snd_opl3_free_seq_oss(opl3);
+#endif
+	if (opl3->seq_client >= 0) {
+		snd_seq_delete_kernel_client(opl3->seq_client);
+		opl3->seq_client = -1;
+	}
+	if (opl3->ilist)
+		snd_seq_instr_list_free(&opl3->ilist);
+	return 0;
+}
+
+static int __init alsa_opl3_seq_init(void)
+{
+	static struct snd_seq_dev_ops ops =
+	{
+		snd_opl3_seq_new_device,
+		snd_opl3_seq_delete_device
+	};
+
+	return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_OPL3, &ops,
+					      sizeof(struct snd_opl3 *));
+}
+
+static void __exit alsa_opl3_seq_exit(void)
+{
+	snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_OPL3);
+}
+
+module_init(alsa_opl3_seq_init)
+module_exit(alsa_opl3_seq_exit)
Index: /GPL/branches/alsa-resync1/alsa-kernel/drivers/opl3/opl3_synth.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/drivers/opl3/opl3_synth.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/drivers/opl3/opl3_synth.c	(revision 91)
@@ -0,0 +1,447 @@
+/*
+ *  Copyright (c) by Uros Bizjak <uros@kss-loka.si>
+ *
+ *  Routines for OPL2/OPL3/OPL4 control
+ *
+ *   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/opl3.h>
+#define __SND_OSS_COMPAT__
+#include <sound/asound_fm.h>
+
+/*
+ *    There is 18 possible 2 OP voices
+ *      (9 in the left and 9 in the right).
+ *      The first OP is the modulator and 2nd is the carrier.
+ *
+ *      The first three voices in the both sides may be connected
+ *      with another voice to a 4 OP voice. For example voice 0
+ *      can be connected with voice 3. The operators of voice 3 are
+ *      used as operators 3 and 4 of the new 4 OP voice.
+ *      In this case the 2 OP voice number 0 is the 'first half' and
+ *      voice 3 is the second.
+ */
+
+
+/*
+ *    Register offset table for OPL2/3 voices,
+ *    OPL2 / one OPL3 register array side only
+ */
+
+char snd_opl3_regmap[MAX_OPL2_VOICES][4] =
+{
+    /*	  OP1   OP2   OP3   OP4		*/
+    /*	 ------------------------	*/
+    { 0x00, 0x03, 0x08, 0x0b },
+    { 0x01, 0x04, 0x09, 0x0c },
+    { 0x02, 0x05, 0x0a, 0x0d },
+
+    { 0x08, 0x0b, 0x00, 0x00 },
+    { 0x09, 0x0c, 0x00, 0x00 },
+    { 0x0a, 0x0d, 0x00, 0x00 },
+
+    { 0x10, 0x13, 0x00, 0x00 },	/* used by percussive voices */
+    { 0x11, 0x14, 0x00, 0x00 },	/* if the percussive mode */
+    { 0x12, 0x15, 0x00, 0x00 }	/* is selected (only left reg block) */
+};
+
+/*
+ * prototypes
+ */
+static int snd_opl3_play_note(opl3_t * opl3, snd_dm_fm_note_t * note);
+static int snd_opl3_set_voice(opl3_t * opl3, snd_dm_fm_voice_t * voice);
+static int snd_opl3_set_params(opl3_t * opl3, snd_dm_fm_params_t * params);
+static int snd_opl3_set_mode(opl3_t * opl3, int mode);
+static int snd_opl3_set_connection(opl3_t * opl3, int connection);
+
+/* ------------------------------ */
+
+/*
+ * open the device exclusively
+ */
+int snd_opl3_open(snd_hwdep_t * hw, struct file *file)
+{
+    opl3_t *opl3 = hw->private_data;
+
+    down(&opl3->access_mutex);
+    if (opl3->used) {
+        up(&opl3->access_mutex);
+        return -EAGAIN;
+    }
+    opl3->used++;
+    up(&opl3->access_mutex);
+
+    return 0;
+}
+
+/*
+ * ioctl for hwdep device:
+ */
+int snd_opl3_ioctl(snd_hwdep_t * hw, struct file *file,
+                   unsigned int cmd, unsigned long arg)
+{
+    opl3_t *opl3 = hw->private_data;
+
+    snd_assert(opl3 != NULL, return -EINVAL);
+
+    switch (cmd) {
+        /* get information */
+    case SNDRV_DM_FM_IOCTL_INFO:
+        {
+            snd_dm_fm_info_t info;
+
+            info.fm_mode = opl3->fm_mode;
+            info.rhythm = opl3->rhythm;
+            if (copy_to_user((snd_dm_fm_info_t *) arg, &info, sizeof(snd_dm_fm_info_t)))
+                return -EFAULT;
+            return 0;
+        }
+
+    case SNDRV_DM_FM_IOCTL_RESET:
+#ifdef CONFIG_SND_OSSEMUL
+    case SNDRV_DM_FM_OSS_IOCTL_RESET:
+#endif
+        snd_opl3_reset(opl3);
+        return 0;
+
+    case SNDRV_DM_FM_IOCTL_PLAY_NOTE:
+#ifdef CONFIG_SND_OSSEMUL
+    case SNDRV_DM_FM_OSS_IOCTL_PLAY_NOTE:
+#endif
+        {
+            snd_dm_fm_note_t note;
+            if (copy_from_user(&note, (snd_dm_fm_note_t *) arg, sizeof(snd_dm_fm_note_t)))
+                return -EFAULT;
+            return snd_opl3_play_note(opl3, &note);
+        }
+
+    case SNDRV_DM_FM_IOCTL_SET_VOICE:
+#ifdef CONFIG_SND_OSSEMUL
+    case SNDRV_DM_FM_OSS_IOCTL_SET_VOICE:
+#endif
+        {
+            snd_dm_fm_voice_t voice;
+            if (copy_from_user(&voice, (snd_dm_fm_voice_t *) arg, sizeof(snd_dm_fm_voice_t)))
+                return -EFAULT;
+            return snd_opl3_set_voice(opl3, &voice);
+        }
+
+    case SNDRV_DM_FM_IOCTL_SET_PARAMS:
+#ifdef CONFIG_SND_OSSEMUL
+    case SNDRV_DM_FM_OSS_IOCTL_SET_PARAMS:
+#endif
+        {
+            snd_dm_fm_params_t params;
+            if (copy_from_user(&params, (snd_dm_fm_params_t *) arg, sizeof(snd_dm_fm_params_t)))
+                return -EFAULT;
+            return snd_opl3_set_params(opl3, &params);
+        }
+
+    case SNDRV_DM_FM_IOCTL_SET_MODE:
+#ifdef CONFIG_SND_OSSEMUL
+    case SNDRV_DM_FM_OSS_IOCTL_SET_MODE:
+#endif
+        return snd_opl3_set_mode(opl3, (int) arg);
+
+    case SNDRV_DM_FM_IOCTL_SET_CONNECTION:
+#ifdef CONFIG_SND_OSSEMUL
+    case SNDRV_DM_FM_OSS_IOCTL_SET_OPL:
+#endif
+        return snd_opl3_set_connection(opl3, (int) arg);
+
+#ifdef CONFIG_SND_DEBUG
+    default:
+        snd_printk("unknown IOCTL: 0x%x\n", cmd);
+#endif
+    }
+    return -ENOTTY;
+}
+
+/*
+ * close the device
+ */
+int snd_opl3_release(snd_hwdep_t * hw, struct file *file)
+{
+    opl3_t *opl3 = hw->private_data;
+
+    snd_opl3_reset(opl3);
+    down(&opl3->access_mutex);
+    opl3->used--;
+    up(&opl3->access_mutex);
+
+    return 0;
+}
+
+/* ------------------------------ */
+
+void snd_opl3_reset(opl3_t * opl3)
+{
+    unsigned short opl3_reg;
+
+    unsigned short reg_side;
+    unsigned char voice_offset;
+
+    int max_voices, i;
+
+    max_voices = (opl3->hardware < OPL3_HW_OPL3) ?
+        MAX_OPL2_VOICES : MAX_OPL3_VOICES;
+
+    for (i = 0; i < max_voices; i++) {
+        /* Get register array side and offset of voice */
+        if (i < MAX_OPL2_VOICES) {
+            /* Left register block for voices 0 .. 8 */
+            reg_side = OPL3_LEFT;
+            voice_offset = i;
+        } else {
+            /* Right register block for voices 9 .. 17 */
+            reg_side = OPL3_RIGHT;
+            voice_offset = i - MAX_OPL2_VOICES;
+        }
+        opl3_reg = reg_side | (OPL3_REG_KSL_LEVEL + snd_opl3_regmap[voice_offset][0]);
+        opl3->command(opl3, opl3_reg, OPL3_TOTAL_LEVEL_MASK); /* Operator 1 volume */
+        opl3_reg = reg_side | (OPL3_REG_KSL_LEVEL + snd_opl3_regmap[voice_offset][1]);
+        opl3->command(opl3, opl3_reg, OPL3_TOTAL_LEVEL_MASK); /* Operator 2 volume */
+
+        opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK + voice_offset);
+        opl3->command(opl3, opl3_reg, 0x00);	/* Note off */
+    }
+
+    opl3->max_voices = MAX_OPL2_VOICES;
+    opl3->fm_mode = SNDRV_DM_FM_MODE_OPL2;
+
+    opl3->command(opl3, OPL3_LEFT | OPL3_REG_TEST, OPL3_ENABLE_WAVE_SELECT);
+    opl3->command(opl3, OPL3_LEFT | OPL3_REG_PERCUSSION, 0x00);	/* Melodic mode */
+    opl3->rhythm = 0;
+}
+
+
+static int snd_opl3_play_note(opl3_t * opl3, snd_dm_fm_note_t * note)
+{
+    unsigned short reg_side;
+    unsigned char voice_offset;
+
+    unsigned short opl3_reg;
+    unsigned char reg_val;
+
+    /* Voices 0 -  8 in OPL2 mode */
+    /* Voices 0 - 17 in OPL3 mode */
+    if (note->voice >= ((opl3->fm_mode == SNDRV_DM_FM_MODE_OPL3) ?
+                        MAX_OPL3_VOICES : MAX_OPL2_VOICES))
+        return -EINVAL;
+
+    /* Get register array side and offset of voice */
+    if (note->voice < MAX_OPL2_VOICES) {
+        /* Left register block for voices 0 .. 8 */
+        reg_side = OPL3_LEFT;
+        voice_offset = note->voice;
+    } else {
+        /* Right register block for voices 9 .. 17 */
+        reg_side = OPL3_RIGHT;
+        voice_offset = note->voice - MAX_OPL2_VOICES;
+    }
+
+    /* Set lower 8 bits of note frequency */
+    reg_val = (unsigned char) note->fnum;
+    opl3_reg = reg_side | (OPL3_REG_FNUM_LOW + voice_offset);
+    opl3->command(opl3, opl3_reg, reg_val);
+
+    reg_val = 0x00;
+    /* Set output sound flag */
+    if (note->key_on)
+        reg_val |= OPL3_KEYON_BIT;
+    /* Set octave */
+    reg_val |= (note->octave << 2) & OPL3_BLOCKNUM_MASK;
+    /* Set higher 2 bits of note frequency */
+    reg_val |= (unsigned char) (note->fnum >> 8) & OPL3_FNUM_HIGH_MASK;
+
+    /* Set OPL3 KEYON_BLOCK register of requested voice */
+    opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK + voice_offset);
+    opl3->command(opl3, opl3_reg, reg_val);
+
+    return 0;
+}
+
+
+static int snd_opl3_set_voice(opl3_t * opl3, snd_dm_fm_voice_t * voice)
+{
+    unsigned short reg_side;
+    unsigned char op_offset;
+    unsigned char voice_offset;
+
+    unsigned short opl3_reg;
+    unsigned char reg_val;
+
+    /* Only operators 1 and 2 */
+    if (voice->op > 1)
+        return -EINVAL;
+    /* Voices 0 -  8 in OPL2 mode */
+    /* Voices 0 - 17 in OPL3 mode */
+    if (voice->voice >= ((opl3->fm_mode == SNDRV_DM_FM_MODE_OPL3) ?
+                         MAX_OPL3_VOICES : MAX_OPL2_VOICES))
+        return -EINVAL;
+
+    /* Get register array side and offset of voice */
+    if (voice->voice < MAX_OPL2_VOICES) {
+        /* Left register block for voices 0 .. 8 */
+        reg_side = OPL3_LEFT;
+        voice_offset = voice->voice;
+    } else {
+        /* Right register block for voices 9 .. 17 */
+        reg_side = OPL3_RIGHT;
+        voice_offset = voice->voice - MAX_OPL2_VOICES;
+    }
+    /* Get register offset of operator */
+    op_offset = snd_opl3_regmap[voice_offset][voice->op];
+
+    reg_val = 0x00;
+    /* Set amplitude modulation (tremolo) effect */
+    if (voice->am)
+        reg_val |= OPL3_TREMOLO_ON;
+    /* Set vibrato effect */
+    if (voice->vibrato)
+        reg_val |= OPL3_VIBRATO_ON;
+    /* Set sustaining sound phase */
+    if (voice->do_sustain)
+        reg_val |= OPL3_SUSTAIN_ON;
+    /* Set keyboard scaling bit */
+    if (voice->kbd_scale)
+        reg_val |= OPL3_KSR;
+    /* Set harmonic or frequency multiplier */
+    reg_val |= voice->harmonic & OPL3_MULTIPLE_MASK;
+
+    /* Set OPL3 AM_VIB register of requested voice/operator */
+    opl3_reg = reg_side | (OPL3_REG_AM_VIB + op_offset);
+    opl3->command(opl3, opl3_reg, reg_val);
+
+    /* Set decreasing volume of higher notes */
+    reg_val = (voice->scale_level << 6) & OPL3_KSL_MASK;
+    /* Set output volume */
+    reg_val |= ~voice->volume & OPL3_TOTAL_LEVEL_MASK;
+
+    /* Set OPL3 KSL_LEVEL register of requested voice/operator */
+    opl3_reg = reg_side | (OPL3_REG_KSL_LEVEL + op_offset);
+    opl3->command(opl3, opl3_reg, reg_val);
+
+    /* Set attack phase level */
+    reg_val = (voice->attack << 4) & OPL3_ATTACK_MASK;
+    /* Set decay phase level */
+    reg_val |= voice->decay & OPL3_DECAY_MASK;
+
+    /* Set OPL3 ATTACK_DECAY register of requested voice/operator */
+    opl3_reg = reg_side | (OPL3_REG_ATTACK_DECAY + op_offset);
+    opl3->command(opl3, opl3_reg, reg_val);
+
+    /* Set sustain phase level */
+    reg_val = (voice->sustain << 4) & OPL3_SUSTAIN_MASK;
+    /* Set release phase level */
+    reg_val |= voice->release & OPL3_RELEASE_MASK;
+
+    /* Set OPL3 SUSTAIN_RELEASE register of requested voice/operator */
+    opl3_reg = reg_side | (OPL3_REG_SUSTAIN_RELEASE + op_offset);
+    opl3->command(opl3, opl3_reg, reg_val);
+
+    /* Set inter-operator feedback */
+    reg_val = (voice->feedback << 1) & OPL3_FEEDBACK_MASK;
+    /* Set inter-operator connection */
+    if (voice->connection)
+        reg_val |= OPL3_CONNECTION_BIT;
+    /* OPL-3 only */
+    if (opl3->fm_mode == SNDRV_DM_FM_MODE_OPL3) {
+        if (voice->left)
+            reg_val |= OPL3_VOICE_TO_LEFT;
+        if (voice->right)
+            reg_val |= OPL3_VOICE_TO_RIGHT;
+    }
+    /* Feedback/connection bits are applicable to voice */
+    opl3_reg = reg_side | (OPL3_REG_FEEDBACK_CONNECTION + voice_offset);
+    opl3->command(opl3, opl3_reg, reg_val);
+
+    /* Select waveform */
+    reg_val = voice->waveform & OPL3_WAVE_SELECT_MASK;
+    opl3_reg = reg_side | (OPL3_REG_WAVE_SELECT + op_offset);
+    opl3->command(opl3, opl3_reg, reg_val);
+
+    return 0;
+}
+
+static int snd_opl3_set_params(opl3_t * opl3, snd_dm_fm_params_t * params)
+{
+    unsigned char reg_val;
+
+    reg_val = 0x00;
+    /* Set keyboard split method */
+    if (params->kbd_split)
+        reg_val |= OPL3_KEYBOARD_SPLIT;
+    opl3->command(opl3, OPL3_LEFT | OPL3_REG_KBD_SPLIT, reg_val);
+
+    reg_val = 0x00;
+    /* Set amplitude modulation (tremolo) depth */
+    if (params->am_depth)
+        reg_val |= OPL3_TREMOLO_DEPTH;
+    /* Set vibrato depth */
+    if (params->vib_depth)
+        reg_val |= OPL3_VIBRATO_DEPTH;
+    /* Set percussion mode */
+    if (params->rhythm) {
+        reg_val |= OPL3_PERCUSSION_ENABLE;
+        opl3->rhythm = 1;
+    } else {
+        opl3->rhythm = 0;
+    }
+    /* Play percussion instruments */
+    if (params->bass)
+        reg_val |= OPL3_BASSDRUM_ON;
+    if (params->snare)
+        reg_val |= OPL3_SNAREDRUM_ON;
+    if (params->tomtom)
+        reg_val |= OPL3_TOMTOM_ON;
+    if (params->cymbal)
+        reg_val |= OPL3_CYMBAL_ON;
+    if (params->hihat)
+        reg_val |= OPL3_HIHAT_ON;
+
+    opl3->command(opl3, OPL3_LEFT | OPL3_REG_PERCUSSION, reg_val);
+    return 0;
+}
+
+static int snd_opl3_set_mode(opl3_t * opl3, int mode)
+{
+    if ((mode == SNDRV_DM_FM_MODE_OPL3) && (opl3->hardware < OPL3_HW_OPL3))
+        return -EINVAL;
+
+    opl3->fm_mode = mode;
+    if (opl3->hardware >= OPL3_HW_OPL3)
+        opl3->command(opl3, OPL3_RIGHT | OPL3_REG_CONNECTION_SELECT, 0x00);	/* Clear 4-op connections */
+
+    return 0;
+}
+
+static int snd_opl3_set_connection(opl3_t * opl3, int connection)
+{
+    unsigned char reg_val;
+
+    /* OPL-3 only */
+    if (opl3->fm_mode != SNDRV_DM_FM_MODE_OPL3)
+        return -EINVAL;
+
+    reg_val = connection & (OPL3_RIGHT_4OP_0 | OPL3_RIGHT_4OP_1 | OPL3_RIGHT_4OP_2 |
+                            OPL3_LEFT_4OP_0 | OPL3_LEFT_4OP_1 | OPL3_LEFT_4OP_2);
+    /* Set 4-op connections */
+    opl3->command(opl3, OPL3_RIGHT | OPL3_REG_CONNECTION_SELECT, reg_val);
+
+    return 0;
+}
Index: /GPL/branches/alsa-resync1/alsa-kernel/drivers/opl3/opl3_voice.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/drivers/opl3/opl3_voice.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/drivers/opl3/opl3_voice.h	(revision 91)
@@ -0,0 +1,52 @@
+#ifndef __OPL3_VOICE_H
+#define __OPL3_VOICE_H
+
+/*
+ *  Copyright (c) 2000 Uros Bizjak <uros@kss-loka.si>
+ *
+ *   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/opl3.h>
+
+/* Prototypes for opl3_seq.c */
+int snd_opl3_synth_use_inc(opl3_t * opl3);
+void snd_opl3_synth_use_dec(opl3_t * opl3);
+int snd_opl3_synth_setup(opl3_t * opl3);
+void snd_opl3_synth_cleanup(opl3_t * opl3);
+
+/* Prototypes for opl3_midi.c */
+void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan);
+void snd_opl3_note_off(void *p, int note, int vel, struct snd_midi_channel *chan);
+void snd_opl3_key_press(void *p, int note, int vel, struct snd_midi_channel *chan);
+void snd_opl3_terminate_note(void *p, int note, snd_midi_channel_t *chan);
+void snd_opl3_control(void *p, int type, struct snd_midi_channel *chan);
+void snd_opl3_nrpn(void *p, snd_midi_channel_t *chan, snd_midi_channel_set_t *chset);
+void snd_opl3_sysex(void *p, unsigned char *buf, int len, int parsed, snd_midi_channel_set_t *chset);
+
+void snd_opl3_calc_volume(unsigned char *reg, int vel, snd_midi_channel_t *chan);
+void snd_opl3_timer_func(unsigned long data);
+
+/* Prototypes for opl3_drums.c */
+void snd_opl3_load_drums(opl3_t *opl3);
+void snd_opl3_drum_switch(opl3_t *opl3, int note, int on_off, int vel, snd_midi_channel_t *chan);
+
+/* Prototypes for opl3_oss.c */
+#ifdef CONFIG_SND_OSSEMUL
+void snd_opl3_init_seq_oss(opl3_t *opl3, char *name);
+void snd_opl3_free_seq_oss(opl3_t *opl3);
+#endif
+
+#endif
Index: /GPL/branches/alsa-resync1/alsa-kernel/drivers/serial-u16550.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/drivers/serial-u16550.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/drivers/serial-u16550.c	(revision 91)
@@ -0,0 +1,1015 @@
+/*
+ *   serial.c
+ *   Copyright (c) by Jaroslav Kysela <perex@suse.cz>,
+ *                    Isaku Yamahata <yamahata@private.email.ne.jp>,
+ *		      George Hansper <ghansper@apana.org.au>,
+ *		      Hannu Savolainen
+ *
+ *   This code is based on the code from ALSA 0.5.9, but heavily rewritten.
+ *
+ * Sat Mar 31 17:27:57 PST 2001 tim.mann@compaq.com
+ *      Added support for the Midiator MS-124T and for the MS-124W in
+ *      Single Addressed (S/A) or Multiple Burst (M/B) mode, with
+ *      power derived either parasitically from the serial port or
+ *      from a separate power supply.
+ *
+ *      The new snd_adaptor module parameter allows you to select
+ *      either the default Roland Soundcanvas support (0), which was
+ *      previously included in this driver but was not documented,
+ *      Midiator MS-124T support (1), Midiator MS-124W S/A mode
+ *      support (2), or MS-124W M/B mode support (3).  For the
+ *      Midiator MS-124W, you must set the physical M-S and A-B
+ *      switches on the Midiator to match the driver mode you select.
+ *
+ *      - In Roland Soundcanvas mode, multiple ALSA raw MIDI
+ *      substreams are supported (midiCnD0-midiCnD15).  Whenever you
+ *      write to a different substream, the driver sends the
+ *      nonstandard MIDI command sequence F5 NN, where NN is the
+ *      substream number plus 1.  Roland modules use this command to
+ *      switch between different "parts", so this feature lets you
+ *      treat each part as a distinct raw MIDI substream.  The driver
+ *      provides no way to send F5 00 (no selection) or to not send
+ *      the F5 NN command sequence at all; perhaps it ought to.
+ *
+ *      - In MS-124T mode, one raw MIDI substream is supported
+ *      (midiCnD0); the snd_outs module parameter is automatically set
+ *      to 1.  The driver sends the same data to all four MIDI Out
+ *      connectors.  Set the A-B switch and the snd_speed module
+ *      parameter to match (A=19200, B=9600).
+ *
+ *      Usage example for MS-124T, with A-B switch in A position:
+ *        setserial /dev/ttyS0 uart none
+ *        /sbin/modprobe snd-card-serial snd_port=0x3f8 snd_irq=4 \
+ *            snd_adaptor=1 snd_speed=19200
+ *
+ *      - In MS-124W S/A mode, one raw MIDI substream is supported
+ *      (midiCnD0); the snd_outs module parameter is automatically set
+ *      to 1.  The driver sends the same data to all four MIDI Out
+ *      connectors at full MIDI speed.
+ *
+ *      Usage example for S/A mode:
+ *        setserial /dev/ttyS0 uart none
+ *        /sbin/modprobe snd-card-serial snd_port=0x3f8 snd_irq=4 \
+ *            snd_adaptor=2
+ *
+ *      - In MS-124W M/B mode, the driver supports 16 ALSA raw MIDI
+ *      substreams; the snd_outs module parameter is automatically set
+ *      to 16.  The substream number gives a bitmask of which MIDI Out
+ *      connectors the data should be sent to, with midiCnD1 sending
+ *      to Out 1, midiCnD2 to Out 2, midiCnD4 to Out 3, and midiCnD8
+ *      to Out 4.  Thus midiCnD15 sends the data to all 4 ports.  As a
+ *      special case, midiCnD0 also sends to all ports, since it is
+ *      not useful to send the data to no ports.  M/B mode has extra
+ *      overhead to select the MIDI Out for each byte, so the
+ *      aggregate data rate across all four MIDI Outs is at most one
+ *      byte every 520 us, as compared with the full MIDI data rate of
+ *      one byte every 320 us per port.
+ *
+ *      Usage example for M/B mode:
+ *        setserial /dev/ttyS0 uart none
+ *        /sbin/insmod snd-card-serial snd_port=0x3f8 snd_irq=4 \
+ *            snd_adaptor=3
+ *
+ *      - The MS-124W hardware's M/A mode is currently not supported.
+ *      This mode allows the MIDI Outs to act independently at double
+ *      the aggregate throughput of M/B, but does not allow sending
+ *      the same byte simultaneously to multiple MIDI Outs.  The M/A
+ *      protocol requires the driver to twiddle the modem control
+ *      lines under timing constraints, so it would be a bit more
+ *      complicated to implement than the other modes.
+ *
+ *      - Midiator models other than MS-124W and MS-124T are currently
+ *      not supported.  Note that the suffix letter is significant;
+ *      the MS-124 and MS-124B are not compatible, nor are the other
+ *      known models MS-101, MS-101B, MS-103, and MS-114.  I do have
+ *      documentation that partially covers these models, but no units
+ *      to experiment with.  The MS-124W support is tested with a real
+ *      unit.  The MS-124T support is untested, but should work.
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+
+#define SNDRV_MAIN_OBJECT_FILE
+
+#include <sound/driver.h>
+#include <sound/rawmidi.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+#include <linux/serial_reg.h>
+
+EXPORT_NO_SYMBOLS;
+MODULE_DESCRIPTION("MIDI serial");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{ALSA, MIDI serial}}");
+
+#define SNDRV_SERIAL_SOUNDCANVAS 0 /* Roland Soundcanvas; F5 NN selects part */
+#define SNDRV_SERIAL_MS124T 1      /* Midiator MS-124T */
+#define SNDRV_SERIAL_MS124W_SA 2   /* Midiator MS-124W in S/A mode */
+#define SNDRV_SERIAL_MS124W_MB 3   /* Midiator MS-124W in M/B mode */
+#define SNDRV_SERIAL_MAX_ADAPTOR SNDRV_SERIAL_MS124W_MB
+static char *adaptor_names[] = {
+    "Soundcanvas",
+    "MS-124T",
+    "MS-124W S/A",
+    "MS-124W M/B"
+};
+
+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;	/* Enable this card */
+static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;     /* 0x3f8,0x2f8,0x3e8,0x2e8 */
+static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* 3,4,5,7,9,10,11,14,15 */
+#ifdef TARGET_OS2
+static int snd_speed[SNDRV_CARDS] = {REPEAT_SNDRV(38400)}; /* 9600,19200,38400,57600,115200 */
+static int snd_base[SNDRV_CARDS] = {REPEAT_SNDRV(115200)}; /* baud base */
+static int snd_outs[SNDRV_CARDS] = {REPEAT_SNDRV(1)};	/* 1 to 16 */
+static int snd_adaptor[SNDRV_CARDS] = {REPEAT_SNDRV(SNDRV_SERIAL_SOUNDCANVAS)};
+#else
+static int snd_speed[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 38400}; /* 9600,19200,38400,57600,115200 */
+static int snd_base[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 115200}; /* baud base */
+static int snd_outs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};	/* 1 to 16 */
+static int snd_adaptor[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = SNDRV_SERIAL_SOUNDCANVAS};
+#endif
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for Serial MIDI.");
+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 Serial MIDI.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_enable, "Enable UART16550A chip.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_port, "Port # for UART16550A chip.");
+MODULE_PARM_SYNTAX(snd_port, SNDRV_PORT12_DESC);
+MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_irq, "IRQ # for UART16550A chip.");
+MODULE_PARM_SYNTAX(snd_irq, SNDRV_IRQ_DESC);
+MODULE_PARM(snd_speed, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_speed, "Speed in bauds.");
+MODULE_PARM_SYNTAX(snd_speed, SNDRV_ENABLED ",allows:{9600,19200,38400,57600,115200},dialog:list");
+MODULE_PARM(snd_base, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_base, "Base for divisor in bauds.");
+MODULE_PARM_SYNTAX(snd_base, SNDRV_ENABLED ",allows:{57600,115200,230400,460800},dialog:list");
+MODULE_PARM(snd_outs, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_outs, "Number of MIDI outputs.");
+MODULE_PARM_SYNTAX(snd_outs, SNDRV_ENABLED ",allows:{{1,16}},dialog:list");
+MODULE_PARM(snd_adaptor, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_adaptor, "Type of adaptor.");
+MODULE_PARM_SYNTAX(snd_adaptor, SNDRV_ENABLED ",allows:{{0=Soundcanvas,1=MS-124T,2=MS-124W S/A,3=MS-124W M/B}},dialog:list");
+
+/*#define SNDRV_SERIAL_MS124W_MB_NOCOMBO 1*/  /* Address outs as 0-3 instead of bitmap */
+
+#define SNDRV_SERIAL_MAX_OUTS	16		/* max 64, min 16 */
+
+#define TX_BUFF_SIZE		(1<<9)		/* Must be 2^n */
+#define TX_BUFF_MASK		(TX_BUFF_SIZE - 1)
+
+#define SERIAL_MODE_NOT_OPENED 		(0)
+#define SERIAL_MODE_INPUT_OPEN		(1 << 0)
+#define SERIAL_MODE_OUTPUT_OPEN		(1 << 1)
+#define SERIAL_MODE_INPUT_TRIGGERED	(1 << 2)
+#define SERIAL_MODE_OUTPUT_TRIGGERED	(1 << 3)
+
+typedef struct _snd_uart16550 {
+    snd_card_t *card;
+    snd_rawmidi_t *rmidi;
+    snd_rawmidi_substream_t *midi_output[SNDRV_SERIAL_MAX_OUTS];
+    snd_rawmidi_substream_t *midi_input;
+
+    int filemode;		//open status of file
+
+    spinlock_t open_lock;
+
+    int irq;
+
+    unsigned long base;
+    struct resource *res_base;
+
+    unsigned int speed;
+    unsigned int speed_base;
+    unsigned char divisor;
+
+    unsigned char old_divisor_lsb;
+    unsigned char old_divisor_msb;
+    unsigned char old_line_ctrl_reg;
+
+    // parameter for using of write loop
+    short int fifo_limit;	//used in uart16550
+    short int fifo_count;	//used in uart16550
+
+    // type of adaptor
+    int adaptor;
+
+    // outputs
+    int prev_out;
+    unsigned char prev_status[SNDRV_SERIAL_MAX_OUTS];
+
+    // write buffer and its writing/reading position
+    unsigned char tx_buff[TX_BUFF_SIZE];
+    int buff_in_count;
+    int buff_in;
+    int buff_out;
+
+    // wait timer
+    unsigned int timer_running:1;
+    struct timer_list buffer_timer;
+
+} snd_uart16550_t;
+
+static snd_card_t *snd_serial_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+
+inline static void snd_uart16550_add_timer(snd_uart16550_t *uart)
+{
+    if (! uart->timer_running) {
+        /* timer 38600bps * 10bit * 16byte */
+        uart->buffer_timer.expires = jiffies + (HZ+255)/256;
+        uart->timer_running = 1;
+        add_timer(&uart->buffer_timer);
+    }
+}
+
+inline static void snd_uart16550_del_timer(snd_uart16550_t *uart)
+{
+    if (uart->timer_running) {
+        del_timer(&uart->buffer_timer);
+        uart->timer_running = 0;
+    }
+}
+
+/* This macro is only used in snd_uart16550_io_loop */
+inline static void snd_uart16550_buffer_output(snd_uart16550_t *uart)
+{
+    unsigned short buff_out = uart->buff_out;
+    outb(uart->tx_buff[buff_out], uart->base + UART_TX);
+    uart->fifo_count++;
+    buff_out++;
+    buff_out &= TX_BUFF_MASK;
+    uart->buff_out = buff_out;
+    uart->buff_in_count--;
+}
+
+/* This loop should be called with interrupts disabled
+ * We don't want to interrupt this,
+ * as we're already handling an interupt
+ */
+static void snd_uart16550_io_loop(snd_uart16550_t * uart)
+{
+    unsigned char c, status;
+
+    /* Read Loop */
+    while ((status = inb(uart->base + UART_LSR)) & UART_LSR_DR) {
+        /* while receive data ready */
+        c = inb(uart->base + UART_RX);
+        if (uart->filemode & SERIAL_MODE_INPUT_OPEN) {
+            snd_rawmidi_receive(uart->midi_input, &c, 1);
+        }
+        if (status & UART_LSR_OE)
+            snd_printk("%s: Overrun on device at 0x%lx\n",
+                       uart->rmidi->name, uart->base);
+    }
+
+    /* no need of check SERIAL_MODE_OUTPUT_OPEN because if not,
+     buffer is never filled. */
+    /* Check write status */
+    if (status & UART_LSR_THRE) {
+        uart->fifo_count = 0;
+    }
+    if (uart->adaptor == SNDRV_SERIAL_MS124W_SA) {
+        /* Can't use FIFO, must send only when CTS is true */
+        status = inb(uart->base + UART_MSR);
+        if (uart->fifo_count == 0 && (status & UART_MSR_CTS)
+            && uart->buff_in_count > 0)
+            snd_uart16550_buffer_output(uart);
+    } else {
+        /* Write loop */
+        while (uart->fifo_count < uart->fifo_limit	/* Can we write ? */
+               && uart->buff_in_count > 0)	/* Do we want to? */
+            snd_uart16550_buffer_output(uart);
+    }
+    if (uart->irq < 0 && uart->buff_in_count > 0)
+        snd_uart16550_add_timer(uart);
+}
+
+/* NOTES ON SERVICING INTERUPTS
+ * ---------------------------
+ * After receiving a interrupt, it is important to indicate to the UART that
+ * this has been done.
+ * For a Rx interupt, this is done by reading the received byte.
+ * For a Tx interupt this is done by either:
+ * a) Writing a byte
+ * b) Reading the IIR
+ * It is particularly important to read the IIR if a Tx interupt is received
+ * when there is no data in tx_buff[], as in this case there no other
+ * indication that the interupt has been serviced, and it remains outstanding
+ * indefinitely. This has the curious side effect that and no further interupts
+ * will be generated from this device AT ALL!!.
+ * It is also desirable to clear outstanding interupts when the device is
+ * opened/closed.
+ *
+ *
+ * Note that some devices need OUT2 to be set before they will generate
+ * interrupts at all. (Possibly tied to an internal pull-up on CTS?)
+ */
+static irqreturn_t snd_uart16550_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+    snd_uart16550_t *uart;
+
+    uart = (snd_uart16550_t *) dev_id;
+    spin_lock(&uart->open_lock);
+    if (uart->filemode == SERIAL_MODE_NOT_OPENED) {
+        spin_unlock(&uart->open_lock);
+        return IRQ_HANDLED;
+    }
+    inb(uart->base + UART_IIR);		/* indicate to the UART that the interupt has been serviced */
+    snd_uart16550_io_loop(uart);
+    spin_unlock(&uart->open_lock);
+    return IRQ_HANDLED;
+}
+
+/* When the polling mode, this function calls snd_uart16550_io_loop. */
+static void snd_uart16550_buffer_timer(unsigned long data)
+{
+    snd_uart16550_t *uart;
+
+    uart = (snd_uart16550_t *)data;
+    spin_lock(&uart->open_lock);
+    snd_uart16550_del_timer(uart);
+    snd_uart16550_io_loop(uart);
+    spin_unlock(&uart->open_lock);
+}
+
+/*
+ *  this method probes, if an uart sits on given port
+ *  return 0 if found
+ *  return negative error if not found
+ */
+static int __init snd_uart16550_detect(unsigned int io_base)
+{
+    int ok;
+    unsigned char c;
+
+    if (check_region(io_base, 8))
+        return -EBUSY;
+
+    /* Do some vague tests for the presence of the uart */
+    if (io_base == 0)
+        return -ENODEV;	/* Not configured */
+
+    ok = 1;			/* uart detected unless one of the following tests should fail */
+    /* 8 data-bits, 1 stop-bit, parity off, DLAB = 0 */
+    outb(UART_LCR_WLEN8, io_base + UART_LCR); /* Line Control Register */
+    c = inb(io_base + UART_IER);
+    /* The top four bits of the IER should always == 0 */
+    if ((c & 0xf0) != 0)
+        ok = 0;		/* failed */
+
+    outb(0xaa, io_base + UART_SCR);
+    /* Write arbitrary data into the scratch reg */
+    c = inb(io_base + UART_SCR);
+    /* If it comes back, it's OK */
+    if (c != 0xaa)
+        ok = 0;		/* failed */
+
+    outb(0x55, io_base + UART_SCR);
+    /* Write arbitrary data into the scratch reg */
+    c = inb(io_base + UART_SCR);
+    /* If it comes back, it's OK */
+    if (c != 0x55)
+        ok = 0;		/* failed */
+
+    return ok;
+}
+
+static void snd_uart16550_do_open(snd_uart16550_t * uart)
+{
+    char byte;
+
+    /* Initialize basic variables */
+    uart->buff_in_count = 0;
+    uart->buff_in = 0;
+    uart->buff_out = 0;
+    uart->fifo_limit = 1;
+    uart->fifo_count = 0;
+    uart->timer_running = 0;
+
+    outb(UART_FCR_ENABLE_FIFO	/* Enable FIFO's (if available) */
+         | UART_FCR_CLEAR_RCVR	/* Clear receiver FIFO */
+         | UART_FCR_CLEAR_XMIT	/* Clear transmitter FIFO */
+         | UART_FCR_TRIGGER_4	/* Set FIFO trigger at 4-bytes */
+         /* NOTE: interupt generated after T=(time)4-bytes
+          * if less than UART_FCR_TRIGGER bytes received
+          */
+         ,uart->base + UART_FCR);	/* FIFO Control Register */
+
+    if ((inb(uart->base + UART_IIR) & 0xf0) == 0xc0)
+        uart->fifo_limit = 16;
+    if (uart->divisor != 0) {
+        uart->old_line_ctrl_reg = inb(uart->base + UART_LCR);
+        outb(UART_LCR_DLAB	/* Divisor latch access bit */
+             ,uart->base + UART_LCR);	/* Line Control Register */
+        uart->old_divisor_lsb = inb(uart->base + UART_DLL);
+        uart->old_divisor_msb = inb(uart->base + UART_DLM);
+
+        outb(uart->divisor
+             ,uart->base + UART_DLL);	/* Divisor Latch Low */
+        outb(0
+             ,uart->base + UART_DLM);	/* Divisor Latch High */
+        /* DLAB is reset to 0 in next outb() */
+    }
+    /* Set serial parameters (parity off, etc) */
+    outb(UART_LCR_WLEN8	/* 8 data-bits */
+         | 0		/* 1 stop-bit */
+         | 0		/* parity off */
+         | 0		/* DLAB = 0 */
+         ,uart->base + UART_LCR);	/* Line Control Register */
+
+    switch (uart->adaptor) {
+    default:
+        outb(UART_MCR_RTS	/* Set Request-To-Send line active */
+             | UART_MCR_DTR	/* Set Data-Terminal-Ready line active */
+             | UART_MCR_OUT2	/* Set OUT2 - not always required, but when
+             * it is, it is ESSENTIAL for enabling interrupts
+             */
+             ,uart->base + UART_MCR);	/* Modem Control Register */
+        break;
+    case SNDRV_SERIAL_MS124W_SA:
+    case SNDRV_SERIAL_MS124W_MB:
+        /* MS-124W can draw power from RTS and DTR if they
+         are in opposite states. */
+        outb(UART_MCR_RTS | (0&UART_MCR_DTR) | UART_MCR_OUT2,
+             uart->base + UART_MCR);
+        break;
+    case SNDRV_SERIAL_MS124T:
+        /* MS-124T can draw power from RTS and/or DTR (preferably
+         both) if they are both asserted. */
+        outb(UART_MCR_RTS | UART_MCR_DTR | UART_MCR_OUT2,
+             uart->base + UART_MCR);
+        break;
+    }
+
+    if (uart->irq < 0) {
+        byte = (0 & UART_IER_RDI)	/* Disable Receiver data interupt */
+            |(0 & UART_IER_THRI)	/* Disable Transmitter holding register empty interupt */
+            ;
+    } else if (uart->adaptor == SNDRV_SERIAL_MS124W_SA) {
+        byte = UART_IER_RDI	/* Enable Receiver data interrupt */
+            | UART_IER_MSI	/* Enable Modem status interrupt */
+            ;
+    } else {
+        byte = UART_IER_RDI	/* Enable Receiver data interupt */
+            | UART_IER_THRI	/* Enable Transmitter holding register empty interupt */
+            ;
+    }
+    outb(byte, uart->base + UART_IER);	/* Interupt enable Register */
+
+    inb(uart->base + UART_LSR);	/* Clear any pre-existing overrun indication */
+    inb(uart->base + UART_IIR);	/* Clear any pre-existing transmit interrupt */
+    inb(uart->base + UART_RX);	/* Clear any pre-existing receive interrupt */
+}
+
+static void snd_uart16550_do_close(snd_uart16550_t * uart)
+{
+    if (uart->irq < 0)
+        snd_uart16550_del_timer(uart);
+
+    /* NOTE: may need to disable interrupts before de-registering out handler.
+     * For now, the consequences are harmless.
+     */
+
+    outb((0 & UART_IER_RDI)		/* Disable Receiver data interupt */
+         |(0 & UART_IER_THRI)	/* Disable Transmitter holding register empty interupt */
+         ,uart->base + UART_IER);	/* Interupt enable Register */
+
+    switch (uart->adaptor) {
+    default:
+        outb((0 & UART_MCR_RTS)		/* Deactivate Request-To-Send line  */
+             |(0 & UART_MCR_DTR)	/* Deactivate Data-Terminal-Ready line */
+             |(0 & UART_MCR_OUT2)	/* Deactivate OUT2 */
+             ,uart->base + UART_MCR);	/* Modem Control Register */
+        break;
+    case SNDRV_SERIAL_MS124W_SA:
+    case SNDRV_SERIAL_MS124W_MB:
+        /* MS-124W can draw power from RTS and DTR if they
+         are in opposite states; leave it powered. */
+        outb(UART_MCR_RTS | (0&UART_MCR_DTR) | (0&UART_MCR_OUT2),
+             uart->base + UART_MCR);
+        break;
+    case SNDRV_SERIAL_MS124T:
+        /* MS-124T can draw power from RTS and/or DTR (preferably
+         both) if they are both asserted; leave it powered. */
+        outb(UART_MCR_RTS | UART_MCR_DTR | (0&UART_MCR_OUT2),
+             uart->base + UART_MCR);
+        break;
+    }
+
+    inb(uart->base + UART_IIR);	/* Clear any outstanding interupts */
+
+    /* Restore old divisor */
+    if (uart->divisor != 0) {
+        outb(UART_LCR_DLAB		/* Divisor latch access bit */
+             ,uart->base + UART_LCR);	/* Line Control Register */
+        outb(uart->old_divisor_lsb
+             ,uart->base + UART_DLL);	/* Divisor Latch Low */
+        outb(uart->old_divisor_msb
+             ,uart->base + UART_DLM);	/* Divisor Latch High */
+        /* Restore old LCR (data bits, stop bits, parity, DLAB) */
+        outb(uart->old_line_ctrl_reg
+             ,uart->base + UART_LCR);	/* Line Control Register */
+    }
+}
+
+static int snd_uart16550_input_open(snd_rawmidi_substream_t * substream)
+{
+    unsigned long flags;
+    snd_uart16550_t *uart = substream->rmidi->private_data;
+
+    spin_lock_irqsave(&uart->open_lock, flags);
+    if (uart->filemode == SERIAL_MODE_NOT_OPENED)
+        snd_uart16550_do_open(uart);
+    uart->filemode |= SERIAL_MODE_INPUT_OPEN;
+    uart->midi_input = substream;
+    spin_unlock_irqrestore(&uart->open_lock, flags);
+    return 0;
+}
+
+static int snd_uart16550_input_close(snd_rawmidi_substream_t * substream)
+{
+    unsigned long flags;
+    snd_uart16550_t *uart = substream->rmidi->private_data;
+
+    spin_lock_irqsave(&uart->open_lock, flags);
+    uart->filemode &= ~SERIAL_MODE_INPUT_OPEN;
+    uart->midi_input = NULL;
+    if (uart->filemode == SERIAL_MODE_NOT_OPENED)
+        snd_uart16550_do_close(uart);
+    spin_unlock_irqrestore(&uart->open_lock, flags);
+    return 0;
+}
+
+static void snd_uart16550_input_trigger(snd_rawmidi_substream_t * substream, int up)
+{
+    unsigned long flags;
+    snd_uart16550_t *uart = substream->rmidi->private_data;
+
+    spin_lock_irqsave(&uart->open_lock, flags);
+    if (up) {
+        uart->filemode |= SERIAL_MODE_INPUT_TRIGGERED;
+    } else {
+        uart->filemode &= ~SERIAL_MODE_INPUT_TRIGGERED;
+    }
+    spin_unlock_irqrestore(&uart->open_lock, flags);
+}
+
+static int snd_uart16550_output_open(snd_rawmidi_substream_t * substream)
+{
+    unsigned long flags;
+    snd_uart16550_t *uart = substream->rmidi->private_data;
+
+    spin_lock_irqsave(&uart->open_lock, flags);
+    if (uart->filemode == SERIAL_MODE_NOT_OPENED)
+        snd_uart16550_do_open(uart);
+    uart->filemode |= SERIAL_MODE_OUTPUT_OPEN;
+    uart->midi_output[substream->number] = substream;
+    spin_unlock_irqrestore(&uart->open_lock, flags);
+    return 0;
+};
+
+static int snd_uart16550_output_close(snd_rawmidi_substream_t * substream)
+{
+    unsigned long flags;
+    snd_uart16550_t *uart = substream->rmidi->private_data;
+
+    spin_lock_irqsave(&uart->open_lock, flags);
+    uart->filemode &= ~SERIAL_MODE_OUTPUT_OPEN;
+    uart->midi_output[substream->number] = NULL;
+    if (uart->filemode == SERIAL_MODE_NOT_OPENED)
+        snd_uart16550_do_close(uart);
+    spin_unlock_irqrestore(&uart->open_lock, flags);
+    return 0;
+};
+
+inline static void snd_uart16550_write_buffer(snd_uart16550_t *uart, unsigned char byte)
+{
+    unsigned short buff_in = uart->buff_in;
+    uart->tx_buff[buff_in] = byte;
+    buff_in++;
+    buff_in &= TX_BUFF_MASK;
+    uart->buff_in = buff_in;
+    uart->buff_in_count++;
+    if (uart->irq < 0) /* polling mode */
+        snd_uart16550_add_timer(uart);
+}
+
+static void snd_uart16550_output_byte(snd_uart16550_t *uart, snd_rawmidi_substream_t * substream, unsigned char midi_byte)
+{
+    if (uart->buff_in_count == 0                            /* Buffer empty? */
+        && (uart->adaptor != SNDRV_SERIAL_MS124W_SA ||
+            (uart->fifo_count == 0                               /* FIFO empty? */
+             && (inb(uart->base + UART_MSR) & UART_MSR_CTS)))) { /* CTS? */
+
+        /* Tx Buffer Empty - try to write immediately */
+        if ((inb(uart->base + UART_LSR) & UART_LSR_THRE) != 0) {
+            /* Transmitter holding register (and Tx FIFO) empty */
+            uart->fifo_count = 1;
+            outb(midi_byte, uart->base + UART_TX);
+        } else {
+            if (uart->fifo_count < uart->fifo_limit) {
+                uart->fifo_count++;
+                outb(midi_byte, uart->base + UART_TX);
+            } else {
+                /* Cannot write (buffer empty) - put char in buffer */
+                snd_uart16550_write_buffer(uart, midi_byte);
+            }
+        }
+    } else {
+        if (uart->buff_in_count >= TX_BUFF_SIZE) {
+            snd_printk("%s: Buffer overrun on device at 0x%lx\n",
+                       uart->rmidi->name, uart->base);
+            return;
+        }
+        snd_uart16550_write_buffer(uart, midi_byte);
+    }
+}
+
+static void snd_uart16550_output_write(snd_rawmidi_substream_t * substream)
+{
+    unsigned long flags;
+    unsigned char midi_byte, addr_byte;
+    snd_uart16550_t *uart = substream->rmidi->private_data;
+    char first;
+
+    /* Interupts are disabled during the updating of the tx_buff,
+     * since it is 'bad' to have two processes updating the same
+     * variables (ie buff_in & buff_out)
+     */
+
+    spin_lock_irqsave(&uart->open_lock, flags);
+
+    if (uart->irq < 0)	//polling
+        snd_uart16550_io_loop(uart);
+
+    if (uart->adaptor == SNDRV_SERIAL_MS124W_MB) {
+        while (1) {
+            /* buffer full? */
+            /* in this mode we need two bytes of space */
+            if (uart->buff_in_count > TX_BUFF_SIZE - 2)
+                break;
+            if (snd_rawmidi_transmit(substream, &midi_byte, 1) != 1)
+                break;
+#if SNDRV_SERIAL_MS124W_MB_NOCOMBO
+            /* select exactly one of the four ports */
+            addr_byte = (1 << (substream->number + 4)) | 0x08;
+#else
+            /* select any combination of the four ports */
+            addr_byte = (substream->number << 4) | 0x08;
+            /* ...except none */
+            if (addr_byte == 0x08) addr_byte = 0xf8;
+#endif
+            snd_uart16550_output_byte(uart, substream, addr_byte);
+            /* send midi byte */
+            snd_uart16550_output_byte(uart, substream, midi_byte);
+        }
+    } else {
+        first = 0;
+        while (1) {
+            /* buffer full? */
+            if (uart->buff_in_count >= TX_BUFF_SIZE)
+                break;
+            if (snd_rawmidi_transmit(substream, &midi_byte, 1) != 1)
+                break;
+            if (first == 0 && uart->adaptor == SNDRV_SERIAL_SOUNDCANVAS &&
+                uart->prev_out != substream->number) {
+                /* Roland Soundcanvas part selection */
+                /* If this substream of the data is different previous
+                 substream in this uart, send the change part event */
+                uart->prev_out = substream->number;
+                /* change part */
+                snd_uart16550_output_byte(uart, substream, 0xf5);
+                /* data */
+                snd_uart16550_output_byte(uart, substream, uart->prev_out + 1);
+                /* If midi_byte is a data byte, send the previous status byte */
+                if (midi_byte < 0x80)
+                    snd_uart16550_output_byte(uart, substream, uart->prev_status[uart->prev_out]);
+            }
+            /* send midi byte */
+            snd_uart16550_output_byte(uart, substream, midi_byte);
+            if (midi_byte >= 0x80 && midi_byte < 0xf0)
+                uart->prev_status[uart->prev_out] = midi_byte;
+            first = 1;
+        }
+    }
+    spin_unlock_irqrestore(&uart->open_lock, flags);
+}
+
+static void snd_uart16550_output_trigger(snd_rawmidi_substream_t * substream, int up)
+{
+    unsigned long flags;
+    snd_uart16550_t *uart = substream->rmidi->private_data;
+
+    spin_lock_irqsave(&uart->open_lock, flags);
+    if (up) {
+        uart->filemode |= SERIAL_MODE_OUTPUT_TRIGGERED;
+    } else {
+        uart->filemode &= ~SERIAL_MODE_OUTPUT_TRIGGERED;
+    }
+    spin_unlock_irqrestore(&uart->open_lock, flags);
+    if (up)
+        snd_uart16550_output_write(substream);
+}
+
+#ifdef TARGET_OS2
+static snd_rawmidi_ops_t snd_uart16550_output =
+{
+    snd_uart16550_output_open,
+    snd_uart16550_output_close,
+    snd_uart16550_output_trigger,0
+};
+
+static snd_rawmidi_ops_t snd_uart16550_input =
+{
+    snd_uart16550_input_open,
+    snd_uart16550_input_close,
+    snd_uart16550_input_trigger,0
+};
+#else
+static snd_rawmidi_ops_t snd_uart16550_output =
+{
+open:		snd_uart16550_output_open,
+    close:		snd_uart16550_output_close,
+    trigger:	snd_uart16550_output_trigger,
+};
+
+static snd_rawmidi_ops_t snd_uart16550_input =
+{
+open:		snd_uart16550_input_open,
+    close:		snd_uart16550_input_close,
+    trigger:	snd_uart16550_input_trigger,
+};
+#endif
+
+static int snd_uart16550_free(snd_uart16550_t *uart)
+{
+    if (uart->irq >= 0)
+        free_irq(uart->irq, (void *)uart);
+    if (uart->res_base)
+        release_resource(uart->res_base);
+    kfree(uart);
+    return 0;
+};
+
+static int snd_uart16550_dev_free(snd_device_t *device)
+{
+    snd_uart16550_t *uart = device->device_data;
+    return snd_uart16550_free(uart);
+}
+
+static int __init snd_uart16550_create(snd_card_t * card,
+                                       unsigned long iobase,
+                                       int irq,
+                                       unsigned int speed,
+                                       unsigned int base,
+                                       int adaptor,
+                                       snd_uart16550_t **ruart)
+{
+#ifdef TARGET_OS2
+    static snd_device_ops_t ops = {
+        snd_uart16550_dev_free,0,0,0
+    };
+#else
+    static snd_device_ops_t ops = {
+    dev_free:       snd_uart16550_dev_free,
+    };
+#endif
+    snd_uart16550_t *uart;
+    int err;
+
+
+    if ((uart = kcalloc(1, sizeof(*uart), GFP_KERNEL)) == NULL)
+        return -ENOMEM;
+    uart->adaptor = adaptor;
+    uart->card = card;
+    spin_lock_init(&uart->open_lock);
+    uart->irq = -1;
+    if ((uart->res_base = request_region(iobase, 8, "Serial MIDI")) == NULL) {
+        snd_printk("unable to grab ports 0x%lx-0x%lx\n", iobase, iobase + 8 - 1);
+        return -EBUSY;
+    }
+    uart->base = iobase;
+    if (irq >= 0) {
+        if (request_irq(irq, snd_uart16550_interrupt,
+                        SA_INTERRUPT, "Serial MIDI", (void *) uart)) {
+            uart->irq = -1;
+            snd_printk("irq %d busy. Using Polling.\n", irq);
+        } else {
+            uart->irq = irq;
+        }
+    }
+    uart->divisor = base / speed;
+    uart->speed = base / (unsigned int)uart->divisor;
+    uart->speed_base = base;
+    uart->prev_out = -1;
+    memset(uart->prev_status, 0x80, sizeof(unsigned char) * SNDRV_SERIAL_MAX_OUTS);
+    uart->buffer_timer.function = snd_uart16550_buffer_timer;
+    uart->buffer_timer.data = (unsigned long)uart;
+    uart->timer_running = 0;
+
+    /* Register device */
+    if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, uart, &ops)) < 0) {
+        snd_uart16550_free(uart);
+        return err;
+    }
+
+    switch (uart->adaptor) {
+    case SNDRV_SERIAL_MS124W_SA:
+    case SNDRV_SERIAL_MS124W_MB:
+        /* MS-124W can draw power from RTS and DTR if they
+         are in opposite states. */
+        outb(UART_MCR_RTS | (0&UART_MCR_DTR), uart->base + UART_MCR);
+        break;
+    case SNDRV_SERIAL_MS124T:
+        /* MS-124T can draw power from RTS and/or DTR (preferably
+         both) if they are asserted. */
+        outb(UART_MCR_RTS | UART_MCR_DTR, uart->base + UART_MCR);
+        break;
+    default:
+        break;
+    }
+
+    if (ruart)
+        *ruart = uart;
+
+    return 0;
+}
+
+static int __init snd_uart16550_rmidi(snd_uart16550_t *uart, int device, int outs, snd_rawmidi_t **rmidi)
+{
+    snd_rawmidi_t *rrawmidi;
+    int err;
+
+    if ((err = snd_rawmidi_new(uart->card, "UART Serial MIDI", device, outs, 1, &rrawmidi)) < 0)
+        return err;
+    snd_rawmidi_set_ops(rrawmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_uart16550_input);
+    snd_rawmidi_set_ops(rrawmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_uart16550_output);
+    sprintf(rrawmidi->name, "uart16550 MIDI #%d", device);
+    rrawmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT |
+        SNDRV_RAWMIDI_INFO_INPUT |
+        SNDRV_RAWMIDI_INFO_DUPLEX;
+    rrawmidi->private_data = uart;
+    if (rmidi)
+        *rmidi = rrawmidi;
+    return 0;
+}
+
+static int __init snd_serial_probe(int dev)
+{
+    snd_card_t *card;
+    snd_uart16550_t *uart;
+    int err;
+
+    if (!snd_enable[dev])
+        return -ENOENT;
+
+    switch (snd_adaptor[dev]) {
+    case SNDRV_SERIAL_SOUNDCANVAS:
+        break;
+    case SNDRV_SERIAL_MS124T:
+    case SNDRV_SERIAL_MS124W_SA:
+        snd_outs[dev] = 1;
+        break;
+    case SNDRV_SERIAL_MS124W_MB:
+        snd_outs[dev] = 16;
+        break;
+    default:
+        snd_printk("Adaptor type is out of range 0-%d (%d)\n",
+                   SNDRV_SERIAL_MAX_ADAPTOR, snd_adaptor[dev]);
+        return -ENODEV;
+    }
+
+    if (snd_outs[dev] < 1 || snd_outs[dev] > SNDRV_SERIAL_MAX_OUTS) {
+        snd_printk("Count of outputs is out of range 1-%d (%d)\n",
+                   SNDRV_SERIAL_MAX_OUTS, snd_outs[dev]);
+        return -ENODEV;
+    }
+
+    card  = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0);
+    if (card == NULL)
+        return -ENOMEM;
+
+    strcpy(card->driver, "Serial");
+    strcpy(card->shortname, "Serial midi (uart16550A)");
+
+    if ((err = snd_uart16550_detect(snd_port[dev])) <= 0) {
+        snd_card_free(card);
+        snd_printk("no UART detected at 0x%lx\n", (long)snd_port[dev]);
+        return err;
+    }
+
+    if ((err = snd_uart16550_create(card,
+                                    snd_port[dev],
+                                    snd_irq[dev],
+                                    snd_speed[dev],
+                                    snd_base[dev],
+                                    snd_adaptor[dev],
+                                    &uart)) < 0) {
+        snd_card_free(card);
+        return err;
+    }
+
+    if ((err = snd_uart16550_rmidi(uart, 0, snd_outs[dev], &uart->rmidi)) < 0) {
+        snd_card_free(card);
+        return err;
+    }
+
+    sprintf(card->longname, "%s at 0x%lx, irq %d speed %d div %d outs %d adaptor %s",
+            card->shortname,
+            uart->base,
+            uart->irq,
+            uart->speed,
+            (int)uart->divisor,
+            snd_outs[dev],
+            adaptor_names[uart->adaptor]);
+
+    if ((err = snd_card_register(card)) < 0) {
+        snd_card_free(card);
+        return err;
+    }
+    snd_serial_cards[dev] = card;
+    return 0;
+}
+
+static int __init alsa_card_serial_init(void)
+{
+    int dev = 0;
+    int cards = 0;
+
+    for (dev = 0; dev < SNDRV_CARDS; dev++) {
+        if (snd_serial_probe(dev) == 0)
+            cards++;
+    }
+
+    if (cards == 0) {
+#ifdef MODULE
+        snd_printk("serial midi soundcard not found or device busy\n");
+#endif
+        return -ENODEV;
+    }
+    return 0;
+}
+
+static void __exit alsa_card_serial_exit(void)
+{
+    int dev;
+
+    for (dev = 0; dev < SNDRV_CARDS; dev++) {
+        if (snd_serial_cards[dev] != NULL)
+            snd_card_free(snd_serial_cards[dev]);
+    }
+}
+
+module_init(alsa_card_serial_init)
+module_exit(alsa_card_serial_exit)
+
+#ifndef MODULE
+
+/* format is: snd-card-serial=snd_enable,snd_index,snd_id,
+ snd_port,snd_irq,snd_speed,snd_base,snd_outs */
+
+static int __init alsa_card_serial_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_port[nr_dev]) == 2 &&
+           get_option(&str,&snd_irq[nr_dev]) == 2 &&
+           get_option(&str,&snd_speed[nr_dev]) == 2 &&
+           get_option(&str,&snd_base[nr_dev]) == 2 &&
+           get_option(&str,&snd_outs[nr_dev]) == 2 &&
+           get_option(&str,&snd_adaptor[nr_dev]) == 2);
+    nr_dev++;
+    return 1;
+}
+
+__setup("snd-card-serial=", alsa_card_serial_setup);
+
+#endif /* ifndef MODULE */
Index: /GPL/branches/alsa-resync1/alsa-kernel/drivers/virmidi.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/drivers/virmidi.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/drivers/virmidi.c	(revision 91)
@@ -0,0 +1,184 @@
+/*
+ *  Dummy soundcard for virtual rawmidi devices
+ *
+ *  Copyright (c) 2000 by 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/*
+ * VIRTUAL RAW MIDI DEVICE CARDS
+ *
+ * This dummy card contains up to 4 virtual rawmidi devices.
+ * They are not real rawmidi devices but just associated with sequencer
+ * clients, so that any input/output sources can be connected as a raw
+ * MIDI device arbitrary.
+ * Also, multiple access is allowed to a single rawmidi device.
+ *
+ * Typical usage is like following:
+ * - Load snd-card-virmidi module.
+ *	# modprobe snd-card-virmidi snd_index=2
+ *   Then, sequencer clients 72:0 to 75:0 will be created, which are
+ *   mapped from /dev/snd/midiC1D0 to /dev/snd/midiC1D3, respectively.
+ *
+ * - Connect input/output via aconnect.
+ *	% aconnect 64:0 72:0	# keyboard input redirection 64:0 -> 72:0
+ *	% aconnect 72:0 65:0	# output device redirection 72:0 -> 65:0
+ *
+ * - Run application using a midi device (eg. /dev/snd/midiC1D0)
+ */
+
+#define SNDRV_MAIN_OBJECT_FILE
+#include <sound/driver.h>
+#include <sound/seq_kernel.h>
+#include <sound/seq_virmidi.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+EXPORT_NO_SYMBOLS;
+MODULE_DESCRIPTION("Dummy soundcard for virtual rawmidi devices");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{ALSA,Virtual rawmidi device}}");
+
+#define MAX_MIDI_DEVICES	8
+
+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 */
+#ifdef TARGET_OS2
+static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE;
+static int snd_midi_devs[SNDRV_CARDS] = SNDDRV_DEFAULT_MIDI_DEVS
+#else
+    static int snd_enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 0};
+static int snd_midi_devs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 4};
+#endif
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for virmidi soundcard.");
+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 virmidi soundcard.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable this soundcard.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(snd_midi_devs, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_midi_devs, "MIDI devices # (1-8)");
+MODULE_PARM_SYNTAX(snd_midi_devs, SNDRV_ENABLED ",allows:{{1,8}}");
+
+typedef struct snd_card_virmidi {
+    snd_card_t *card;
+    snd_rawmidi_t *midi[MAX_MIDI_DEVICES];
+} snd_card_virmidi_t;
+
+static snd_card_t *snd_virmidi_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+
+
+static int __init snd_card_virmidi_probe(int dev)
+{
+    snd_card_t *card;
+    struct snd_card_virmidi *vmidi;
+    int idx, err;
+
+    if (!snd_enable[dev])
+        return -ENODEV;
+    card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE,
+                        sizeof(struct snd_card_virmidi));
+    if (card == NULL)
+        return -ENOMEM;
+    vmidi = (struct snd_card_virmidi *)card->private_data;
+    vmidi->card = card;
+
+    if (snd_midi_devs[dev] > MAX_MIDI_DEVICES) {
+        snd_printk("too much midi devices for virmidi %d: force to use %d\n", dev, MAX_MIDI_DEVICES);
+        snd_midi_devs[dev] = MAX_MIDI_DEVICES;
+    }
+    for (idx = 0; idx < snd_midi_devs[dev]; idx++) {
+        snd_rawmidi_t *rmidi;
+        snd_virmidi_dev_t *rdev;
+        if ((err = snd_virmidi_new(card, idx, &rmidi)) < 0)
+            goto __nodev;
+        rdev = rmidi->private_data;
+        vmidi->midi[idx] = rmidi;
+        strcpy(rmidi->name, "Virtual Raw MIDI");
+        rdev->seq_mode = SNDRV_VIRMIDI_SEQ_DISPATCH;
+    }
+
+    strcpy(card->driver, "VirMIDI");
+    strcpy(card->shortname, "VirMIDI");
+    sprintf(card->longname, "Virtual MIDI Card %i", dev + 1);
+    if ((err = snd_card_register(card)) == 0) {
+        snd_virmidi_cards[dev] = card;
+        return 0;
+    }
+__nodev:
+    snd_card_free(card);
+    return err;
+}
+
+static int __init alsa_card_virmidi_init(void)
+{
+    int dev, cards;
+
+    for (dev = cards = 0; dev < SNDRV_CARDS && snd_enable[dev]; dev++) {
+        if (snd_card_virmidi_probe(dev) < 0) {
+#ifdef MODULE
+            snd_printk("Card-VirMIDI #%i not found or device busy\n", dev + 1);
+#endif
+            break;
+        }
+        cards++;
+    }
+    if (!cards) {
+#ifdef MODULE
+        snd_printk("Card-VirMIDI soundcard not found or device busy\n");
+#endif
+        return -ENODEV;
+    }
+    return 0;
+}
+
+static void __exit alsa_card_virmidi_exit(void)
+{
+    int dev;
+
+    for (dev = 0; dev < SNDRV_CARDS; dev++)
+        snd_card_free(snd_virmidi_cards[dev]);
+}
+
+module_init(alsa_card_virmidi_init)
+module_exit(alsa_card_virmidi_exit)
+
+#ifndef MODULE
+
+/* format is: snd-card-virmidi=snd_enable,snd_index,snd_id,snd_midi_devs */
+
+static int __init alsa_card_virmidi_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_midi_devs[nr_dev]) == 2);
+    nr_dev++;
+    return 1;
+}
+
+__setup("snd-card-virmidi=", alsa_card_virmidi_setup);
+
+#endif /* ifndef MODULE */
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/Makefile
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/Makefile	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/Makefile	(revision 91)
@@ -0,0 +1,13 @@
+#
+# Makefile for ALSA driver
+# Copyright (c) 1994-98 by Jaroslav Kysela <perex@suse.cz>
+#
+
+include ../Makefile.conf
+
+all: $(TARGETS)
+	@echo "What's?" 
+
+clean:
+	rm -f core .depend *.o *.orig *~ modules/*.ver
+
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/ac97_codec.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/ac97_codec.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/ac97_codec.h	(revision 91)
@@ -0,0 +1,648 @@
+#ifndef __SOUND_AC97_CODEC_H
+#define __SOUND_AC97_CODEC_H
+
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  Universal interface for Audio Codec '97
+ *
+ *  For more details look to AC '97 component specification revision 2.1
+ *  by Intel Corporation (http://developer.intel.com).
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <linux/bitops.h>
+//#include <linux/device.h>
+#include "pcm.h"
+#include "control.h"
+#include "info.h"
+
+/*
+ *  AC'97 codec registers
+ */
+
+#define AC97_RESET		0x00	/* Reset */
+#define AC97_MASTER		0x02	/* Master Volume */
+#define AC97_HEADPHONE		0x04	/* Headphone Volume (optional) */
+#define AC97_MASTER_MONO	0x06	/* Master Volume Mono (optional) */
+#define AC97_MASTER_TONE	0x08	/* Master Tone (Bass & Treble) (optional) */
+#define AC97_PC_BEEP		0x0a	/* PC Beep Volume (optinal) */
+#define AC97_PHONE		0x0c	/* Phone Volume (optional) */
+#define AC97_MIC		0x0e	/* MIC Volume */
+#define AC97_LINE		0x10	/* Line In Volume */
+#define AC97_CD			0x12	/* CD Volume */
+#define AC97_VIDEO		0x14	/* Video Volume (optional) */
+#define AC97_AUX		0x16	/* AUX Volume (optional) */
+#define AC97_PCM		0x18	/* PCM Volume */
+#define AC97_REC_SEL		0x1a	/* Record Select */
+#define AC97_REC_GAIN		0x1c	/* Record Gain */
+#define AC97_REC_GAIN_MIC	0x1e	/* Record Gain MIC (optional) */
+#define AC97_GENERAL_PURPOSE	0x20	/* General Purpose (optional) */
+#define AC97_3D_CONTROL		0x22	/* 3D Control (optional) */
+#define AC97_INT_PAGING		0x24	/* Audio Interrupt & Paging (AC'97 2.3) */
+#define AC97_POWERDOWN		0x26	/* Powerdown control / status */
+/* range 0x28-0x3a - AUDIO AC'97 2.0 extensions */
+#define AC97_EXTENDED_ID	0x28	/* Extended Audio ID */
+#define AC97_EXTENDED_STATUS	0x2a	/* Extended Audio Status and Control */
+#define AC97_PCM_FRONT_DAC_RATE 0x2c	/* PCM Front DAC Rate */
+#define AC97_PCM_SURR_DAC_RATE	0x2e	/* PCM Surround DAC Rate */
+#define AC97_PCM_LFE_DAC_RATE	0x30	/* PCM LFE DAC Rate */
+#define AC97_PCM_LR_ADC_RATE	0x32	/* PCM LR ADC Rate */
+#define AC97_PCM_MIC_ADC_RATE	0x34	/* PCM MIC ADC Rate */
+#define AC97_CENTER_LFE_MASTER	0x36	/* Center + LFE Master Volume */
+#define AC97_SURROUND_MASTER	0x38	/* Surround (Rear) Master Volume */
+#define AC97_SPDIF		0x3a	/* S/PDIF control */
+/* range 0x3c-0x58 - MODEM */
+#define AC97_EXTENDED_MID	0x3c	/* Extended Modem ID */
+#define AC97_EXTENDED_MSTATUS	0x3e	/* Extended Modem Status and Control */
+#define AC97_LINE1_RATE		0x40	/* Line1 DAC/ADC Rate */
+#define AC97_LINE2_RATE		0x42	/* Line2 DAC/ADC Rate */
+#define AC97_HANDSET_RATE	0x44	/* Handset DAC/ADC Rate */
+#define AC97_LINE1_LEVEL	0x46	/* Line1 DAC/ADC Level */
+#define AC97_LINE2_LEVEL	0x48	/* Line2 DAC/ADC Level */
+#define AC97_HANDSET_LEVEL	0x4a	/* Handset DAC/ADC Level */
+#define AC97_GPIO_CFG		0x4c	/* GPIO Configuration */
+#define AC97_GPIO_POLARITY	0x4e	/* GPIO Pin Polarity/Type, 0=low, 1=high active */
+#define AC97_GPIO_STICKY	0x50	/* GPIO Pin Sticky, 0=not, 1=sticky */
+#define AC97_GPIO_WAKEUP	0x52	/* GPIO Pin Wakeup, 0=no int, 1=yes int */
+#define AC97_GPIO_STATUS	0x54	/* GPIO Pin Status, slot 12 */
+#define AC97_MISC_AFE		0x56	/* Miscellaneous Modem AFE Status and Control */
+/* range 0x5a-0x7b - Vendor Specific */
+#define AC97_VENDOR_ID1		0x7c	/* Vendor ID1 */
+#define AC97_VENDOR_ID2		0x7e	/* Vendor ID2 / revision */
+/* range 0x60-0x6f (page 1) - extended codec registers */
+#define AC97_CODEC_CLASS_REV	0x60	/* Codec Class/Revision */
+#define AC97_PCI_SVID		0x62	/* PCI Subsystem Vendor ID */
+#define AC97_PCI_SID		0x64	/* PCI Subsystem ID */
+#define AC97_FUNC_SELECT	0x66	/* Function Select */
+#define AC97_FUNC_INFO		0x68	/* Function Information */
+#define AC97_SENSE_INFO		0x6a	/* Sense Details */
+
+/* slot allocation */
+#define AC97_SLOT_TAG		0
+#define AC97_SLOT_CMD_ADDR	1
+#define AC97_SLOT_CMD_DATA	2
+#define AC97_SLOT_PCM_LEFT	3
+#define AC97_SLOT_PCM_RIGHT	4
+#define AC97_SLOT_MODEM_LINE1	5
+#define AC97_SLOT_PCM_CENTER	6
+#define AC97_SLOT_MIC		6	/* input */
+#define AC97_SLOT_SPDIF_LEFT1	6
+#define AC97_SLOT_PCM_SLEFT	7	/* surround left */
+#define AC97_SLOT_PCM_LEFT_0	7	/* double rate operation */
+#define AC97_SLOT_SPDIF_LEFT	7
+#define AC97_SLOT_PCM_SRIGHT	8	/* surround right */
+#define AC97_SLOT_PCM_RIGHT_0	8	/* double rate operation */
+#define AC97_SLOT_SPDIF_RIGHT	8
+#define AC97_SLOT_LFE		9
+#define AC97_SLOT_SPDIF_RIGHT1	9
+#define AC97_SLOT_MODEM_LINE2	10
+#define AC97_SLOT_PCM_LEFT_1	10	/* double rate operation */
+#define AC97_SLOT_SPDIF_LEFT2	10
+#define AC97_SLOT_HANDSET	11	/* output */
+#define AC97_SLOT_PCM_RIGHT_1	11	/* double rate operation */
+#define AC97_SLOT_SPDIF_RIGHT2	11
+#define AC97_SLOT_MODEM_GPIO	12	/* modem GPIO */
+#define AC97_SLOT_PCM_CENTER_1	12	/* double rate operation */
+
+/* basic capabilities (reset register) */
+#define AC97_BC_DEDICATED_MIC	0x0001	/* Dedicated Mic PCM In Channel */
+#define AC97_BC_RESERVED1	0x0002	/* Reserved (was Modem Line Codec support) */
+#define AC97_BC_BASS_TREBLE	0x0004	/* Bass & Treble Control */
+#define AC97_BC_SIM_STEREO	0x0008	/* Simulated stereo */
+#define AC97_BC_HEADPHONE	0x0010	/* Headphone Out Support */
+#define AC97_BC_LOUDNESS	0x0020	/* Loudness (bass boost) Support */
+#define AC97_BC_16BIT_DAC	0x0000	/* 16-bit DAC resolution */
+#define AC97_BC_18BIT_DAC	0x0040	/* 18-bit DAC resolution */
+#define AC97_BC_20BIT_DAC	0x0080	/* 20-bit DAC resolution */
+#define AC97_BC_DAC_MASK	0x00c0
+#define AC97_BC_16BIT_ADC	0x0000	/* 16-bit ADC resolution */
+#define AC97_BC_18BIT_ADC	0x0100	/* 18-bit ADC resolution */
+#define AC97_BC_20BIT_ADC	0x0200	/* 20-bit ADC resolution */
+#define AC97_BC_ADC_MASK	0x0300
+
+/* general purpose */
+#define AC97_GP_DRSS_MASK	0x0c00	/* double rate slot select */
+#define AC97_GP_DRSS_1011	0x0000	/* LR(C) 10+11(+12) */
+#define AC97_GP_DRSS_78		0x0400	/* LR 7+8 */
+
+/* powerdown bits */
+#define AC97_PD_ADC_STATUS	0x0001	/* ADC status (RO) */
+#define AC97_PD_DAC_STATUS	0x0002	/* DAC status (RO) */
+#define AC97_PD_MIXER_STATUS	0x0004	/* Analog mixer status (RO) */
+#define AC97_PD_VREF_STATUS	0x0008	/* Vref status (RO) */
+#define AC97_PD_PR0		0x0100	/* Power down PCM ADCs and input MUX */
+#define AC97_PD_PR1		0x0200	/* Power down PCM front DAC */
+#define AC97_PD_PR2		0x0400	/* Power down Mixer (Vref still on) */
+#define AC97_PD_PR3		0x0800	/* Power down Mixer (Vref off) */
+#define AC97_PD_PR4		0x1000	/* Power down AC-Link */
+#define AC97_PD_PR5		0x2000	/* Disable internal clock usage */
+#define AC97_PD_PR6		0x4000	/* Headphone amplifier */
+#define AC97_PD_EAPD		0x8000	/* External Amplifer Power Down (EAPD) */
+
+/* extended audio ID bit defines */
+#define AC97_EI_VRA		0x0001	/* Variable bit rate supported */
+#define AC97_EI_DRA		0x0002	/* Double rate supported */
+#define AC97_EI_SPDIF		0x0004	/* S/PDIF out supported */
+#define AC97_EI_VRM		0x0008	/* Variable bit rate supported for MIC */
+#define AC97_EI_DACS_SLOT_MASK	0x0030	/* DACs slot assignment */
+#define AC97_EI_DACS_SLOT_SHIFT	4
+#define AC97_EI_CDAC		0x0040	/* PCM Center DAC available */
+#define AC97_EI_SDAC		0x0080	/* PCM Surround DACs available */
+#define AC97_EI_LDAC		0x0100	/* PCM LFE DAC available */
+#define AC97_EI_AMAP		0x0200	/* indicates optional slot/DAC mapping based on codec ID */
+#define AC97_EI_REV_MASK	0x0c00	/* AC'97 revision mask */
+#define AC97_EI_REV_22		0x0400	/* AC'97 revision 2.2 */
+#define AC97_EI_REV_23		0x0800	/* AC'97 revision 2.3 */
+#define AC97_EI_REV_SHIFT	10
+#define AC97_EI_ADDR_MASK	0xc000	/* physical codec ID (address) */
+#define AC97_EI_ADDR_SHIFT	14
+
+/* extended audio status and control bit defines */
+#define AC97_EA_VRA		0x0001	/* Variable bit rate enable bit */
+#define AC97_EA_DRA		0x0002	/* Double-rate audio enable bit */
+#define AC97_EA_SPDIF		0x0004	/* S/PDIF out enable bit */
+#define AC97_EA_VRM		0x0008	/* Variable bit rate for MIC enable bit */
+#define AC97_EA_SPSA_SLOT_MASK	0x0030	/* Mask for slot assignment bits */
+#define AC97_EA_SPSA_SLOT_SHIFT 4
+#define AC97_EA_SPSA_3_4	0x0000	/* Slot assigned to 3 & 4 */
+#define AC97_EA_SPSA_7_8	0x0010	/* Slot assigned to 7 & 8 */
+#define AC97_EA_SPSA_6_9	0x0020	/* Slot assigned to 6 & 9 */
+#define AC97_EA_SPSA_10_11	0x0030	/* Slot assigned to 10 & 11 */
+#define AC97_EA_CDAC		0x0040	/* PCM Center DAC is ready (Read only) */
+#define AC97_EA_SDAC		0x0080	/* PCM Surround DACs are ready (Read only) */
+#define AC97_EA_LDAC		0x0100	/* PCM LFE DAC is ready (Read only) */
+#define AC97_EA_MDAC		0x0200	/* MIC ADC is ready (Read only) */
+#define AC97_EA_SPCV		0x0400	/* S/PDIF configuration valid (Read only) */
+#define AC97_EA_PRI		0x0800	/* Turns the PCM Center DAC off */
+#define AC97_EA_PRJ		0x1000	/* Turns the PCM Surround DACs off */
+#define AC97_EA_PRK		0x2000	/* Turns the PCM LFE DAC off */
+#define AC97_EA_PRL		0x4000	/* Turns the MIC ADC off */
+
+/* S/PDIF control bit defines */
+#define AC97_SC_PRO		0x0001	/* Professional status */
+#define AC97_SC_NAUDIO		0x0002	/* Non audio stream */
+#define AC97_SC_COPY		0x0004	/* Copyright status */
+#define AC97_SC_PRE		0x0008	/* Preemphasis status */
+#define AC97_SC_CC_MASK		0x07f0	/* Category Code mask */
+#define AC97_SC_CC_SHIFT	4
+#define AC97_SC_L		0x0800	/* Generation Level status */
+#define AC97_SC_SPSR_MASK	0x3000	/* S/PDIF Sample Rate bits */
+#define AC97_SC_SPSR_SHIFT	12
+#define AC97_SC_SPSR_44K	0x0000	/* Use 44.1kHz Sample rate */
+#define AC97_SC_SPSR_48K	0x2000	/* Use 48kHz Sample rate */
+#define AC97_SC_SPSR_32K	0x3000	/* Use 32kHz Sample rate */
+#define AC97_SC_DRS		0x4000	/* Double Rate S/PDIF */
+#define AC97_SC_V		0x8000	/* Validity status */
+
+/* Interrupt and Paging bit defines (AC'97 2.3) */
+#define AC97_PAGE_MASK		0x000f	/* Page Selector */
+#define AC97_PAGE_VENDOR	0	/* Vendor-specific registers */
+#define AC97_PAGE_1		1	/* Extended Codec Registers page 1 */
+#define AC97_INT_ENABLE		0x0800	/* Interrupt Enable */
+#define AC97_INT_SENSE		0x1000	/* Sense Cycle */
+#define AC97_INT_CAUSE_SENSE	0x2000	/* Sense Cycle Completed (RO) */
+#define AC97_INT_CAUSE_GPIO	0x4000	/* GPIO bits changed (RO) */
+#define AC97_INT_STATUS		0x8000	/* Interrupt Status */
+
+/* extended modem ID bit defines */
+#define AC97_MEI_LINE1		0x0001	/* Line1 present */
+#define AC97_MEI_LINE2		0x0002	/* Line2 present */
+#define AC97_MEI_HANDSET	0x0004	/* Handset present */
+#define AC97_MEI_CID1		0x0008	/* caller ID decode for Line1 is supported */
+#define AC97_MEI_CID2		0x0010	/* caller ID decode for Line2 is supported */
+#define AC97_MEI_ADDR_MASK	0xc000	/* physical codec ID (address) */
+#define AC97_MEI_ADDR_SHIFT	14
+
+/* extended modem status and control bit defines */
+#define AC97_MEA_GPIO		0x0001	/* GPIO is ready (ro) */
+#define AC97_MEA_MREF		0x0002	/* Vref is up to nominal level (ro) */
+#define AC97_MEA_ADC1		0x0004	/* ADC1 operational (ro) */
+#define AC97_MEA_DAC1		0x0008	/* DAC1 operational (ro) */
+#define AC97_MEA_ADC2		0x0010	/* ADC2 operational (ro) */
+#define AC97_MEA_DAC2		0x0020	/* DAC2 operational (ro) */
+#define AC97_MEA_HADC		0x0040	/* HADC operational (ro) */
+#define AC97_MEA_HDAC		0x0080	/* HDAC operational (ro) */
+#define AC97_MEA_PRA		0x0100	/* GPIO power down (high) */
+#define AC97_MEA_PRB		0x0200	/* reserved */
+#define AC97_MEA_PRC		0x0400	/* ADC1 power down (high) */
+#define AC97_MEA_PRD		0x0800	/* DAC1 power down (high) */
+#define AC97_MEA_PRE		0x1000	/* ADC2 power down (high) */
+#define AC97_MEA_PRF		0x2000	/* DAC2 power down (high) */
+#define AC97_MEA_PRG		0x4000	/* HADC power down (high) */
+#define AC97_MEA_PRH		0x8000	/* HDAC power down (high) */
+
+/* modem gpio status defines */
+#define AC97_GPIO_LINE1_OH      0x0001  /* Off Hook Line1 */
+#define AC97_GPIO_LINE1_RI      0x0002  /* Ring Detect Line1 */
+#define AC97_GPIO_LINE1_CID     0x0004  /* Caller ID path enable Line1 */
+#define AC97_GPIO_LINE1_LCS     0x0008  /* Loop Current Sense Line1 */
+#define AC97_GPIO_LINE1_PULSE   0x0010  /* Opt./ Pulse Dial Line1 (out) */
+#define AC97_GPIO_LINE1_HL1R    0x0020  /* Opt./ Handset to Line1 relay control (out) */
+#define AC97_GPIO_LINE1_HOHD    0x0040  /* Opt./ Handset off hook detect Line1 (in) */
+#define AC97_GPIO_LINE12_AC     0x0080  /* Opt./ Int.bit 1 / Line1/2 AC (out) */
+#define AC97_GPIO_LINE12_DC     0x0100  /* Opt./ Int.bit 2 / Line1/2 DC (out) */
+#define AC97_GPIO_LINE12_RS     0x0200  /* Opt./ Int.bit 3 / Line1/2 RS (out) */
+#define AC97_GPIO_LINE2_OH      0x0400  /* Off Hook Line2 */
+#define AC97_GPIO_LINE2_RI      0x0800  /* Ring Detect Line2 */
+#define AC97_GPIO_LINE2_CID     0x1000  /* Caller ID path enable Line2 */
+#define AC97_GPIO_LINE2_LCS     0x2000  /* Loop Current Sense Line2 */
+#define AC97_GPIO_LINE2_PULSE   0x4000  /* Opt./ Pulse Dial Line2 (out) */
+#define AC97_GPIO_LINE2_HL1R    0x8000  /* Opt./ Handset to Line2 relay control (out) */
+
+/* specific - SigmaTel */
+#define AC97_SIGMATEL_OUTSEL	0x64	/* Output Select, STAC9758 */
+#define AC97_SIGMATEL_INSEL	0x66	/* Input Select, STAC9758 */
+#define AC97_SIGMATEL_IOMISC	0x68	/* STAC9758 */
+#define AC97_SIGMATEL_ANALOG	0x6c	/* Analog Special */
+#define AC97_SIGMATEL_DAC2INVERT 0x6e
+#define AC97_SIGMATEL_BIAS1	0x70
+#define AC97_SIGMATEL_BIAS2	0x72
+#define AC97_SIGMATEL_VARIOUS	0x72	/* STAC9758 */
+#define AC97_SIGMATEL_MULTICHN	0x74	/* Multi-Channel programming */
+#define AC97_SIGMATEL_CIC1	0x76
+#define AC97_SIGMATEL_CIC2	0x78
+
+/* specific - Analog Devices */
+#define AC97_AD_TEST		0x5a	/* test register */
+#define AC97_AD_TEST2		0x5c	/* undocumented test register 2 */
+#define AC97_AD_CODEC_CFG	0x70	/* codec configuration */
+#define AC97_AD_JACK_SPDIF	0x72	/* Jack Sense & S/PDIF */
+#define AC97_AD_SERIAL_CFG	0x74	/* Serial Configuration */
+#define AC97_AD_MISC		0x76	/* Misc Control Bits */
+
+/* specific - Cirrus Logic */
+#define AC97_CSR_ACMODE		0x5e	/* AC Mode Register */
+#define AC97_CSR_MISC_CRYSTAL	0x60	/* Misc Crystal Control */
+#define AC97_CSR_SPDIF		0x68	/* S/PDIF Register */
+#define AC97_CSR_SERIAL		0x6a	/* Serial Port Control */
+#define AC97_CSR_SPECF_ADDR	0x6c	/* Special Feature Address */
+#define AC97_CSR_SPECF_DATA	0x6e	/* Special Feature Data */
+#define AC97_CSR_BDI_STATUS	0x7a	/* BDI Status */
+
+/* specific - Conexant */
+#define AC97_CXR_AUDIO_MISC	0x5c
+#define AC97_CXR_SPDIFEN	(1<<3)
+#define AC97_CXR_COPYRGT	(1<<2)
+#define AC97_CXR_SPDIF_MASK	(3<<0)
+#define AC97_CXR_SPDIF_PCM	0x0
+#define AC97_CXR_SPDIF_AC3	0x2
+
+/* specific - ALC */
+#define AC97_ALC650_SPDIF_INPUT_STATUS1	0x60
+/* S/PDIF input status 1 bit defines */
+#define AC97_ALC650_PRO             0x0001  /* Professional status */
+#define AC97_ALC650_NAUDIO          0x0002  /* Non audio stream */
+#define AC97_ALC650_COPY            0x0004  /* Copyright status */
+#define AC97_ALC650_PRE             0x0038  /* Preemphasis status */
+#define AC97_ALC650_PRE_SHIFT       3
+#define AC97_ALC650_MODE            0x00C0  /* Preemphasis status */
+#define AC97_ALC650_MODE_SHIFT      6
+#define AC97_ALC650_CC_MASK         0x7f00  /* Category Code mask */
+#define AC97_ALC650_CC_SHIFT        8
+#define AC97_ALC650_L               0x8000  /* Generation Level status */
+
+#define AC97_ALC650_SPDIF_INPUT_STATUS2	0x62
+/* S/PDIF input status 2 bit defines */
+#define AC97_ALC650_SOUCE_MASK      0x000f  /* Source number */
+#define AC97_ALC650_CHANNEL_MASK    0x00f0  /* Channel number */
+#define AC97_ALC650_CHANNEL_SHIFT   4
+#define AC97_ALC650_SPSR_MASK       0x0f00  /* S/PDIF Sample Rate bits */
+#define AC97_ALC650_SPSR_SHIFT      8
+#define AC97_ALC650_SPSR_44K        0x0000  /* Use 44.1kHz Sample rate */
+#define AC97_ALC650_SPSR_48K        0x0200  /* Use 48kHz Sample rate */
+#define AC97_ALC650_SPSR_32K        0x0300  /* Use 32kHz Sample rate */
+#define AC97_ALC650_CLOCK_ACCURACY  0x3000  /* Clock accuracy */
+#define AC97_ALC650_CLOCK_SHIFT     12
+#define AC97_ALC650_CLOCK_LOCK      0x4000  /* Clock locked status */
+#define AC97_ALC650_V               0x8000  /* Validity status */
+
+#define AC97_ALC650_SURR_DAC_VOL	0x64
+#define AC97_ALC650_LFE_DAC_VOL		0x66
+#define AC97_ALC650_UNKNOWN1		0x68
+#define AC97_ALC650_MULTICH		0x6a
+#define AC97_ALC650_UNKNOWN2		0x6c
+#define AC97_ALC650_REVISION		0x6e
+#define AC97_ALC650_UNKNOWN3		0x70
+#define AC97_ALC650_UNKNOWN4		0x72
+#define AC97_ALC650_MISC		0x74
+#define AC97_ALC650_GPIO_SETUP		0x76
+#define AC97_ALC650_GPIO_STATUS		0x78
+#define AC97_ALC650_CLOCK		0x7a
+
+/* specific - Yamaha YMF753 */
+#define AC97_YMF753_DIT_CTRL2	0x66	/* DIT Control 2 */
+#define AC97_YMF753_3D_MODE_SEL	0x68	/* 3D Mode Select */
+
+/* specific - C-Media */
+#define AC97_CM9738_VENDOR_CTRL	0x5a
+#define AC97_CM9739_MULTI_CHAN	0x64
+#define AC97_CM9739_SPDIF_IN_STATUS	0x68 /* 32bit */
+#define AC97_CM9739_SPDIF_CTRL	0x6c
+
+/* specific - wolfson */
+#define AC97_WM97XX_FMIXER_VOL  0x72
+#define AC97_WM9704_RMIXER_VOL  0x74
+#define AC97_WM9704_TEST        0x5a
+#define AC97_WM9704_RPCM_VOL    0x70
+#define AC97_WM9711_OUT3VOL     0x16
+
+
+/* ac97->scaps */
+#define AC97_SCAP_AUDIO		(1<<0)	/* audio codec 97 */
+#define AC97_SCAP_MODEM		(1<<1)	/* modem codec 97 */
+#define AC97_SCAP_SURROUND_DAC	(1<<2)	/* surround L&R DACs are present */
+#define AC97_SCAP_CENTER_LFE_DAC (1<<3)	/* center and LFE DACs are present */
+#define AC97_SCAP_SKIP_AUDIO	(1<<4)	/* skip audio part of codec */
+#define AC97_SCAP_SKIP_MODEM	(1<<5)	/* skip modem part of codec */
+#define AC97_SCAP_INDEP_SDIN	(1<<6)	/* independent SDIN */
+#define AC97_SCAP_INV_EAPD	(1<<7)	/* inverted EAPD */
+#define AC97_SCAP_DETECT_BY_VENDOR (1<<8) /* use vendor registers for read tests */
+#define AC97_SCAP_NO_SPDIF	(1<<9)	/* don't build SPDIF controls */
+#define AC97_SCAP_EAPD_LED	(1<<10)	/* EAPD as mute LED */
+#define AC97_SCAP_POWER_SAVE	(1<<11)	/* capable for aggresive power-saving */
+
+/* ac97->flags */
+#define AC97_HAS_PC_BEEP	(1<<0)	/* force PC Speaker usage */
+#define AC97_AD_MULTI		(1<<1)	/* Analog Devices - multi codecs */
+#define AC97_CS_SPDIF		(1<<2)	/* Cirrus Logic uses funky SPDIF */
+#define AC97_CX_SPDIF		(1<<3)	/* Conexant's spdif interface */
+#define AC97_STEREO_MUTES	(1<<4)	/* has stereo mute bits */
+#define AC97_DOUBLE_RATE	(1<<5)	/* supports double rate playback */
+#define AC97_HAS_NO_MASTER_VOL	(1<<6)	/* no Master volume */
+#define AC97_HAS_NO_PCM_VOL	(1<<7)	/* no PCM volume */
+#define AC97_DEFAULT_POWER_OFF	(1<<8)	/* no RESET write */
+#define AC97_MODEM_PATCH	(1<<9)	/* modem patch */
+#define AC97_HAS_NO_REC_GAIN	(1<<10) /* no Record gain */
+#define AC97_HAS_NO_PHONE	(1<<11) /* no PHONE volume */
+#define AC97_HAS_NO_PC_BEEP	(1<<12) /* no PC Beep volume */
+#define AC97_HAS_NO_VIDEO	(1<<13) /* no Video volume */
+#define AC97_HAS_NO_CD		(1<<14) /* no CD volume */
+#define AC97_HAS_NO_MIC	(1<<15) /* no MIC volume */
+#define AC97_HAS_NO_TONE	(1<<16) /* no Tone volume */
+#define AC97_HAS_NO_STD_PCM	(1<<17)	/* no standard AC97 PCM volume and mute */
+#define AC97_HAS_NO_AUX		(1<<18) /* no standard AC97 AUX volume and mute */
+
+/* rates indexes */
+#define AC97_RATES_FRONT_DAC	0
+#define AC97_RATES_SURR_DAC	1
+#define AC97_RATES_LFE_DAC	2
+#define AC97_RATES_ADC		3
+#define AC97_RATES_MIC_ADC	4
+#define AC97_RATES_SPDIF	5
+
+/*
+ *
+ */
+
+struct snd_ac97;
+
+struct snd_ac97_build_ops {
+	int (*build_3d) (struct snd_ac97 *ac97);
+	int (*build_specific) (struct snd_ac97 *ac97);
+	int (*build_spdif) (struct snd_ac97 *ac97);
+	int (*build_post_spdif) (struct snd_ac97 *ac97);
+#ifdef CONFIG_PM
+	void (*suspend) (struct snd_ac97 *ac97);
+	void (*resume) (struct snd_ac97 *ac97);
+#endif
+	void (*update_jacks) (struct snd_ac97 *ac97);	/* for jack-sharing */
+};
+
+struct snd_ac97_bus_ops {
+    void (*reset) (struct snd_ac97 *ac97);
+    void (*warm_reset)(struct snd_ac97 *ac97);
+	void (*write) (struct snd_ac97 *ac97, unsigned short reg, unsigned short val);
+	unsigned short (*read) (struct snd_ac97 *ac97, unsigned short reg);
+	void (*wait) (struct snd_ac97 *ac97);
+	void (*init) (struct snd_ac97 *ac97);
+};
+
+struct snd_ac97_bus {
+	/* -- lowlevel (hardware) driver specific -- */
+	struct snd_ac97_bus_ops *ops;
+	void *private_data;
+	void (*private_free) (struct snd_ac97_bus *bus);
+	/* --- */
+	struct snd_card *card;
+	unsigned short num;	/* bus number */
+	unsigned short no_vra: 1, /* bridge doesn't support VRA */
+		       dra: 1,	/* bridge supports double rate */
+		       isdin: 1;/* independent SDIN */
+	unsigned int clock;	/* AC'97 base clock (usually 48000Hz) */
+	spinlock_t bus_lock;	/* used mainly for slot allocation */
+	unsigned short used_slots[2][4]; /* actually used PCM slots */
+	unsigned short pcms_count; /* count of PCMs */
+	struct ac97_pcm *pcms;
+	struct snd_ac97 *codec[4];
+	struct snd_info_entry *proc;
+};
+
+/* static resolution table */
+struct snd_ac97_res_table {
+    unsigned short reg;     /* register */
+    unsigned short bits;    /* resolution bitmask */
+};
+
+struct snd_ac97_template {
+	void *private_data;
+	void (*private_free) (struct snd_ac97 *ac97);
+	struct pci_dev *pci;	/* assigned PCI device - used for quirks */
+	unsigned short num;	/* number of codec: 0 = primary, 1 = secondary */
+	unsigned short addr;	/* physical address of codec [0-3] */
+	unsigned int scaps;	/* driver capabilities */
+        const struct snd_ac97_res_table *res_table;     /* static resolution */
+};
+
+struct snd_ac97 {
+	/* -- lowlevel (hardware) driver specific -- */
+	struct snd_ac97_build_ops * build_ops;
+	void *private_data;
+	void (*private_free) (struct snd_ac97 *ac97);
+	/* --- */
+	struct snd_ac97_bus *bus;
+	struct pci_dev *pci;	/* assigned PCI device - used for quirks */
+	struct snd_info_entry *proc;
+	struct snd_info_entry *proc_regs;
+	unsigned short subsystem_vendor;
+	unsigned short subsystem_device;
+	struct semaphore reg_mutex;
+	struct semaphore page_mutex;	/* mutex for AD18xx multi-codecs and paging (2.3) */
+	unsigned short num;	/* number of codec: 0 = primary, 1 = secondary */
+	unsigned short addr;	/* physical address of codec [0-3] */
+	unsigned int id;	/* identification of codec */
+	unsigned short caps;	/* capabilities (register 0) */
+	unsigned short ext_id;	/* extended feature identification (register 28) */
+        unsigned short ext_mid;	/* extended modem ID (register 3C) */
+        const struct snd_ac97_res_table *res_table;     /* static resolution */
+	unsigned int scaps;	/* driver capabilities */
+	unsigned int flags;	/* specific code */
+	unsigned int rates[6];	/* see AC97_RATES_* defines */
+	unsigned int spdif_status;
+	unsigned short regs[0x80]; /* register cache */
+	DECLARE_BITMAP(reg_accessed, 0x80); /* bit flags */
+	union {			/* vendor specific code */
+		struct {
+			unsigned short unchained[3];	// 0 = C34, 1 = C79, 2 = C69
+			unsigned short chained[3];	// 0 = C34, 1 = C79, 2 = C69
+			unsigned short id[3];		// codec IDs (lower 16-bit word)
+			unsigned short pcmreg[3];	// PCM registers
+                        unsigned short codec_cfg[3];	// CODEC_CFG bits
+                        unsigned char swap_mic_linein;	// AD1986/AD1986A only
+		} ad18xx;
+		unsigned int dev_flags;		/* device specific */
+	} spec;
+	/* jack-sharing info */
+	unsigned char indep_surround;
+        unsigned char channel_mode;
+
+#ifdef CONFIG_SND_AC97_POWER_SAVE
+	unsigned int power_up;	/* power states */
+	struct workqueue_struct *power_workq;
+	struct work_struct power_work;
+#endif
+	struct device dev;
+};
+
+#define to_ac97_t(d) container_of(d, struct snd_ac97, dev)
+
+/* conditions */
+static inline int ac97_is_audio(struct snd_ac97 * ac97)
+{
+	return (ac97->scaps & AC97_SCAP_AUDIO);
+}
+static inline int ac97_is_modem(struct snd_ac97 * ac97)
+{
+	return (ac97->scaps & AC97_SCAP_MODEM);
+}
+static inline int ac97_is_rev22(struct snd_ac97 * ac97)
+{
+	return (ac97->ext_id & AC97_EI_REV_MASK) >= AC97_EI_REV_22;
+}
+static inline int ac97_can_amap(struct snd_ac97 * ac97)
+{
+	return (ac97->ext_id & AC97_EI_AMAP) != 0;
+}
+static inline int ac97_can_spdif(struct snd_ac97 * ac97)
+{
+	return (ac97->ext_id & AC97_EI_SPDIF) != 0;
+}
+
+/* functions */
+/* create new AC97 bus */
+int snd_ac97_bus(struct snd_card *card, int num, struct snd_ac97_bus_ops *ops,
+		 void *private_data, struct snd_ac97_bus **rbus);
+/* create mixer controls */
+int snd_ac97_mixer(struct snd_ac97_bus *bus, struct snd_ac97_template *template,
+		   struct snd_ac97 **rac97);
+const char *snd_ac97_get_short_name(struct snd_ac97 *ac97);
+
+void snd_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short value);
+unsigned short snd_ac97_read(struct snd_ac97 *ac97, unsigned short reg);
+void snd_ac97_write_cache(struct snd_ac97 *ac97, unsigned short reg, unsigned short value);
+int snd_ac97_update(struct snd_ac97 *ac97, unsigned short reg, unsigned short value);
+int snd_ac97_update_bits(struct snd_ac97 *ac97, unsigned short reg, unsigned short mask, unsigned short value);
+#ifdef CONFIG_SND_AC97_POWER_SAVE
+int snd_ac97_update_power(struct snd_ac97 *ac97, int reg, int powerup);
+#else
+static inline int snd_ac97_update_power(struct snd_ac97 *ac97, int reg,
+					int powerup)
+{
+	return 0;
+}
+#endif
+#ifdef CONFIG_PM
+void snd_ac97_suspend(struct snd_ac97 *ac97);
+void snd_ac97_resume(struct snd_ac97 *ac97);
+#endif
+
+/* quirk types */
+enum {
+	AC97_TUNE_DEFAULT = -1,	/* use default from quirk list (not valid in list) */
+	AC97_TUNE_NONE = 0,	/* nothing extra to do */
+	AC97_TUNE_HP_ONLY,	/* headphone (true line-out) control as master only */
+	AC97_TUNE_SWAP_HP,	/* swap headphone and master controls */
+	AC97_TUNE_SWAP_SURROUND, /* swap master and surround controls */
+	AC97_TUNE_AD_SHARING,	/* for AD1985, turn on OMS bit and use headphone */
+	AC97_TUNE_ALC_JACK,	/* for Realtek, enable JACK detection */
+	AC97_TUNE_INV_EAPD,	/* inverted EAPD implementation */
+	AC97_TUNE_MUTE_LED,	/* EAPD bit works as mute LED */
+	AC97_TUNE_HP_MUTE_LED,  /* EAPD bit works as mute LED, use headphone control as master */
+};
+
+struct ac97_quirk {
+	unsigned short subvendor; /* PCI subsystem vendor id */
+	unsigned short subdevice; /* PCI sybsystem device id */
+	unsigned short mask;	/* device id bit mask, 0 = accept all */
+	unsigned int codec_id;	/* codec id (if any), 0 = accept all */
+	const char *name;	/* name shown as info */
+	int type;		/* quirk type above */
+};
+
+int snd_ac97_tune_hardware(struct snd_ac97 *ac97, struct ac97_quirk *quirk, const char *override);
+int snd_ac97_set_rate(struct snd_ac97 *ac97, int reg, unsigned int rate);
+
+/*
+ * PCM allocation
+ */
+
+enum ac97_pcm_cfg {
+	AC97_PCM_CFG_FRONT = 2,
+	AC97_PCM_CFG_REAR = 10,		/* alias surround */
+	AC97_PCM_CFG_LFE = 11,		/* center + lfe */
+	AC97_PCM_CFG_40 = 4,		/* front + rear */
+	AC97_PCM_CFG_51 = 6,		/* front + rear + center/lfe */
+	AC97_PCM_CFG_SPDIF = 20
+};
+
+struct ac97_pcm {
+    struct snd_ac97_bus *bus;
+#if 0
+	unsigned int stream: 1,	   	   /* stream type: 1 = capture */
+		     exclusive: 1,	   /* exclusive mode, don't override with other pcms */
+		     copy_flag: 1,	   /* lowlevel driver must fill all entries */
+    spdif: 1;		   /* spdif pcm */
+#else
+    unsigned int stream;	   	   /* stream type: 1 = capture */
+    unsigned int exclusive;	   /* exclusive mode, don't override with other pcms */
+    unsigned int copy_flag;	   /* lowlevel driver must fill all entries */
+    unsigned int spdif;		   /* spdif pcm */
+#endif
+    unsigned short aslots;		   /* active slots */
+    unsigned short cur_dbl;		   /* current double-rate state */
+    unsigned int rates;		   /* available rates */
+	struct {
+		unsigned short slots;	   /* driver input: requested AC97 slot numbers */
+		unsigned short rslots[4];  /* allocated slots per codecs */
+		unsigned char rate_table[4];
+		struct snd_ac97 *codec[4];	   /* allocated codecs */
+	} r[2];				   /* 0 = standard rates, 1 = double rates */
+	unsigned long private_value;	   /* used by the hardware driver */
+};
+
+int snd_ac97_pcm_assign(struct snd_ac97_bus *ac97,
+			unsigned short pcms_count,
+			const struct ac97_pcm *pcms);
+int snd_ac97_pcm_open(struct ac97_pcm *pcm, unsigned int rate,
+		      enum ac97_pcm_cfg cfg, unsigned short slots);
+int snd_ac97_pcm_close(struct ac97_pcm *pcm);
+int snd_ac97_pcm_double_rate_rules(struct snd_pcm_runtime *runtime);
+
+/* ad hoc AC97 device driver access */
+extern struct bus_type ac97_bus_type;
+
+#endif /* __SOUND_AC97_CODEC_H */
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/ad1816a.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/ad1816a.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/ad1816a.h	(revision 91)
@@ -0,0 +1,176 @@
+
+#ifndef __AD1816A_H
+#define __AD1816A_H
+
+/*
+    ad1816a.h - definitions for ADI SoundPort AD1816A chip.
+    Copyright (C) 1999-2000 by Massimo Piccioni <dafastidio@libero.it>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "control.h"
+#include "pcm.h"
+#include "timer.h"
+
+#define AD1816A_REG(r)			(chip->port + r)
+
+#define AD1816A_CHIP_STATUS		0x00
+#define AD1816A_INDIR_ADDR		0x00
+#define AD1816A_INTERRUPT_STATUS	0x01
+#define AD1816A_INDIR_DATA_LOW		0x02
+#define AD1816A_INDIR_DATA_HIGH		0x03
+#define AD1816A_PIO_DEBUG		0x04
+#define AD1816A_PIO_STATUS		0x05
+#define AD1816A_PIO_DATA		0x06
+#define AD1816A_RESERVED_7		0x07
+#define AD1816A_PLAYBACK_CONFIG		0x08
+#define AD1816A_CAPTURE_CONFIG		0x09
+#define AD1816A_RESERVED_10		0x0a
+#define AD1816A_RESERVED_11		0x0b
+#define AD1816A_JOYSTICK_RAW_DATA	0x0c
+#define AD1816A_JOYSTICK_CTRL		0x0d
+#define AD1816A_JOY_POS_DATA_LOW	0x0e
+#define AD1816A_JOY_POS_DATA_HIGH	0x0f
+
+#define AD1816A_LOW_BYTE_TMP		0x00
+#define AD1816A_INTERRUPT_ENABLE	0x01
+#define AD1816A_EXTERNAL_CTRL		0x01
+#define AD1816A_PLAYBACK_SAMPLE_RATE	0x02
+#define AD1816A_CAPTURE_SAMPLE_RATE	0x03
+#define AD1816A_VOICE_ATT		0x04
+#define AD1816A_FM_ATT			0x05
+#define AD1816A_I2S_1_ATT		0x06
+#define AD1816A_I2S_0_ATT		0x07
+#define AD1816A_PLAYBACK_BASE_COUNT	0x08
+#define AD1816A_PLAYBACK_CURR_COUNT	0x09
+#define AD1816A_CAPTURE_BASE_COUNT	0x0a
+#define AD1816A_CAPTURE_CURR_COUNT	0x0b
+#define AD1816A_TIMER_BASE_COUNT	0x0c
+#define AD1816A_TIMER_CURR_COUNT	0x0d
+#define AD1816A_MASTER_ATT		0x0e
+#define AD1816A_CD_GAIN_ATT		0x0f
+#define AD1816A_SYNTH_GAIN_ATT		0x10
+#define AD1816A_VID_GAIN_ATT		0x11
+#define AD1816A_LINE_GAIN_ATT		0x12
+#define AD1816A_MIC_GAIN_ATT		0x13
+#define AD1816A_PHONE_IN_GAIN_ATT	0x13
+#define AD1816A_ADC_SOURCE_SEL		0x14
+#define AD1816A_ADC_PGA			0x14
+#define AD1816A_CHIP_CONFIG		0x20
+#define AD1816A_DSP_CONFIG		0x21
+#define AD1816A_FM_SAMPLE_RATE		0x22
+#define AD1816A_I2S_1_SAMPLE_RATE	0x23
+#define AD1816A_I2S_0_SAMPLE_RATE	0x24
+#define AD1816A_RESERVED_37		0x25
+#define AD1816A_PROGRAM_CLOCK_RATE	0x26
+#define AD1816A_3D_PHAT_CTRL		0x27
+#define AD1816A_PHONE_OUT_ATT		0x27
+#define AD1816A_RESERVED_40		0x28
+#define AD1816A_HW_VOL_BUT		0x29
+#define AD1816A_DSP_MAILBOX_0		0x2a
+#define AD1816A_DSP_MAILBOX_1		0x2b
+#define AD1816A_POWERDOWN_CTRL		0x2c
+#define AD1816A_TIMER_CTRL		0x2c
+#define AD1816A_VERSION_ID		0x2d
+#define AD1816A_RESERVED_46		0x2e
+
+#define AD1816A_READY			0x80
+
+#define AD1816A_PLAYBACK_IRQ_PENDING	0x80
+#define AD1816A_CAPTURE_IRQ_PENDING	0x40
+#define AD1816A_TIMER_IRQ_PENDING	0x20
+
+#define AD1816A_PLAYBACK_ENABLE		0x01
+#define AD1816A_PLAYBACK_PIO		0x02
+#define AD1816A_CAPTURE_ENABLE		0x01
+#define AD1816A_CAPTURE_PIO		0x02
+
+#define AD1816A_FMT_LINEAR_8		0x00
+#define AD1816A_FMT_ULAW_8		0x08
+#define AD1816A_FMT_LINEAR_16_LIT	0x10
+#define AD1816A_FMT_ALAW_8		0x18
+#define AD1816A_FMT_LINEAR_16_BIG	0x30
+#define AD1816A_FMT_ALL			0x38
+#define AD1816A_FMT_STEREO		0x04
+
+#define AD1816A_PLAYBACK_IRQ_ENABLE	0x8000
+#define AD1816A_CAPTURE_IRQ_ENABLE	0x4000
+#define AD1816A_TIMER_IRQ_ENABLE	0x2000
+#define AD1816A_TIMER_ENABLE		0x0080
+
+#define AD1816A_SRC_LINE		0x00
+#define AD1816A_SRC_OUT			0x10
+#define AD1816A_SRC_CD			0x20
+#define AD1816A_SRC_SYNTH		0x30
+#define AD1816A_SRC_VIDEO		0x40
+#define AD1816A_SRC_MIC			0x50
+#define AD1816A_SRC_MONO		0x50
+#define AD1816A_SRC_PHONE_IN		0x60
+#define AD1816A_SRC_MASK		0x70
+
+#define AD1816A_CAPTURE_NOT_EQUAL	0x1000
+#define AD1816A_WSS_ENABLE		0x8000
+
+typedef struct _snd_ad1816a ad1816a_t;
+
+struct _snd_ad1816a {
+	unsigned long port;
+	struct resource *res_port;
+	int irq;
+	int dma1;
+	int dma2;
+
+	unsigned short hardware;
+	unsigned short version;
+
+	spinlock_t lock;
+
+	unsigned short mode;
+
+	snd_card_t *card;
+	snd_pcm_t *pcm;
+
+	snd_pcm_substream_t *playback_substream;
+	snd_pcm_substream_t *capture_substream;
+	unsigned int p_dma_size;
+	unsigned int c_dma_size;
+
+	snd_timer_t *timer;
+};
+
+
+#define AD1816A_HW_AUTO		0
+#define AD1816A_HW_AD1816A	1
+#define AD1816A_HW_AD1815	2
+#define AD1816A_HW_AD18MAX10	3
+
+#define AD1816A_MODE_PLAYBACK	0x01
+#define AD1816A_MODE_CAPTURE	0x02
+#define AD1816A_MODE_TIMER	0x04
+#define AD1816A_MODE_OPEN	(AD1816A_MODE_PLAYBACK |	\
+				AD1816A_MODE_CAPTURE |		\
+				AD1816A_MODE_TIMER)
+
+
+extern int snd_ad1816a_create(snd_card_t *card, unsigned long port,
+			      int irq, int dma1, int dma2,
+			      ad1816a_t **chip);
+
+extern int snd_ad1816a_pcm(ad1816a_t *chip, int device, snd_pcm_t **rpcm);
+extern int snd_ad1816a_mixer(ad1816a_t *chip);
+
+#endif	/* __AD1816A_H */
+
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/ad1848.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/ad1848.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/ad1848.h	(revision 91)
@@ -0,0 +1,204 @@
+#ifndef __AD1848_H
+#define __AD1848_H
+
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  Definitions for AD1847/AD1848/CS4248 chips
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "control.h"
+#include "pcm.h"
+
+/* IO ports */
+
+#define AD1848P( codec, x ) ( (chip) -> port + c_d_c_AD1848##x )
+
+#define c_d_c_AD1848REGSEL	0
+#define c_d_c_AD1848REG		1
+#define c_d_c_AD1848STATUS	2
+#define c_d_c_AD1848PIO		3
+
+/* codec registers */
+
+#define AD1848_LEFT_INPUT	0x00	/* left input control */
+#define AD1848_RIGHT_INPUT	0x01	/* right input control */
+#define AD1848_AUX1_LEFT_INPUT	0x02	/* left AUX1 input control */
+#define AD1848_AUX1_RIGHT_INPUT	0x03	/* right AUX1 input control */
+#define AD1848_AUX2_LEFT_INPUT	0x04	/* left AUX2 input control */
+#define AD1848_AUX2_RIGHT_INPUT	0x05	/* right AUX2 input control */
+#define AD1848_LEFT_OUTPUT	0x06	/* left output control register */
+#define AD1848_RIGHT_OUTPUT	0x07	/* right output control register */
+#define AD1848_DATA_FORMAT	0x08	/* clock and data format - playback/capture - bits 7-0 MCE */
+#define AD1848_IFACE_CTRL	0x09	/* interface control - bits 7-2 MCE */
+#define AD1848_PIN_CTRL		0x0a	/* pin control */
+#define AD1848_TEST_INIT	0x0b	/* test and initialization */
+#define AD1848_MISC_INFO	0x0c	/* miscellaneaous information */
+#define AD1848_LOOPBACK		0x0d	/* loopback control */
+#define AD1848_DATA_UPR_CNT	0x0e	/* playback/capture upper base count */
+#define AD1848_DATA_LWR_CNT	0x0f	/* playback/capture lower base count */
+
+/* definitions for codec register select port - CODECP( REGSEL ) */
+
+#define AD1848_INIT		0x80	/* CODEC is initializing */
+#define AD1848_MCE		0x40	/* mode change enable */
+#define AD1848_TRD		0x20	/* transfer request disable */
+
+/* definitions for codec status register - CODECP( STATUS ) */
+
+#define AD1848_GLOBALIRQ	0x01	/* IRQ is active */
+
+/* definitions for AD1848_LEFT_INPUT and AD1848_RIGHT_INPUT registers */
+
+#define AD1848_ENABLE_MIC_GAIN	0x20
+
+#define AD1848_MIXS_LINE1	0x00
+#define AD1848_MIXS_AUX1	0x40
+#define AD1848_MIXS_LINE2	0x80
+#define AD1848_MIXS_ALL		0xc0
+
+/* definitions for clock and data format register - AD1848_PLAYBK_FORMAT */
+
+#define AD1848_LINEAR_8		0x00	/* 8-bit unsigned data */
+#define AD1848_ALAW_8		0x60	/* 8-bit A-law companded */
+#define AD1848_ULAW_8		0x20	/* 8-bit U-law companded */
+#define AD1848_LINEAR_16	0x40	/* 16-bit twos complement data - little endian */
+#define AD1848_STEREO		0x10	/* stereo mode */
+/* bits 3-1 define frequency divisor */
+#define AD1848_XTAL1		0x00	/* 24.576 crystal */
+#define AD1848_XTAL2		0x01	/* 16.9344 crystal */
+
+/* definitions for interface control register - AD1848_IFACE_CTRL */
+
+#define AD1848_CAPTURE_PIO	0x80	/* capture PIO enable */
+#define AD1848_PLAYBACK_PIO	0x40	/* playback PIO enable */
+#define AD1848_CALIB_MODE	0x18	/* calibration mode bits */
+#define AD1848_AUTOCALIB	0x08	/* auto calibrate */
+#define AD1848_SINGLE_DMA	0x04	/* use single DMA channel */
+#define AD1848_CAPTURE_ENABLE	0x02	/* capture enable */
+#define AD1848_PLAYBACK_ENABLE	0x01	/* playback enable */
+
+/* definitions for pin control register - AD1848_PIN_CTRL */
+
+#define AD1848_IRQ_ENABLE	0x02	/* enable IRQ */
+#define AD1848_XCTL1		0x40	/* external control #1 */
+#define AD1848_XCTL0		0x80	/* external control #0 */
+
+/* definitions for test and init register - AD1848_TEST_INIT */
+
+#define AD1848_CALIB_IN_PROGRESS 0x20	/* auto calibrate in progress */
+#define AD1848_DMA_REQUEST	0x10	/* DMA request in progress */
+
+/* defines for codec.mode */
+
+#define AD1848_MODE_NONE	0x0000
+#define AD1848_MODE_PLAY	0x0001
+#define AD1848_MODE_CAPTURE	0x0002
+#define AD1848_MODE_TIMER	0x0004
+#define AD1848_MODE_OPEN	(AD1848_MODE_PLAY|AD1848_MODE_CAPTURE|AD1848_MODE_TIMER)
+
+/* defines for codec.hardware */
+
+#define AD1848_HW_DETECT	0x0000	/* let AD1848 driver detect chip */
+#define AD1848_HW_AD1847	0x0001	/* AD1847 chip */
+#define AD1848_HW_AD1848	0x0002	/* AD1848 chip */
+#define AD1848_HW_CS4248	0x0003	/* CS4248 chip */
+#define AD1848_HW_CMI8330	0x0004	/* CMI8330 chip */
+
+struct _snd_ad1848 {
+	unsigned long port;		/* i/o port */
+	struct resource *res_port;
+	int irq;			/* IRQ line */
+	int dma;			/* data DMA */
+	unsigned short version;		/* version of CODEC chip */
+	unsigned short mode;		/* see to AD1848_MODE_XXXX */
+	unsigned short hardware;	/* see to AD1848_HW_XXXX */
+	unsigned short single_dma:1;	/* forced single DMA mode (GUS 16-bit daughter board) or dma1 == dma2 */
+
+	snd_pcm_t *pcm;
+	snd_pcm_substream_t *playback_substream;
+	snd_pcm_substream_t *capture_substream;
+	snd_card_t *card;
+
+	unsigned char image[32];	/* SGalaxy needs an access to extended registers */
+	int mce_bit;
+	int calibrate_mute;
+        int dma_size;
+        int thinkpad_flag;              /* Thinkpad CS4248 needs some extra help */
+
+	spinlock_t reg_lock;
+	struct semaphore open_mutex;
+};
+
+typedef struct _snd_ad1848 ad1848_t;
+
+/* exported functions */
+
+void snd_ad1848_out(ad1848_t *chip, unsigned char reg, unsigned char value);
+
+int snd_ad1848_create(snd_card_t * card,
+		      unsigned long port,
+		      int irq, int dma,
+		      unsigned short hardware,
+		      ad1848_t ** chip);
+
+int snd_ad1848_pcm(ad1848_t * chip, int device, snd_pcm_t **rpcm);
+int snd_ad1848_mixer(ad1848_t * chip);
+void snd_ad1848_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+
+#ifdef TARGET_OS2
+#define AD1848_SINGLE(xname, xindex, reg, shift, mask, invert) \
+{ SNDRV_CTL_ELEM_IFACE_MIXER, 0, 0, xname, xindex, \
+  0, 0, snd_ad1848_info_single, \
+  snd_ad1848_get_single, snd_ad1848_put_single, \
+  reg | (shift << 8) | (mask << 16) | (invert << 24) }
+#else
+#define AD1848_SINGLE(xname, xindex, reg, shift, mask, invert) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
+  info: snd_ad1848_info_single, \
+  get: snd_ad1848_get_single, put: snd_ad1848_put_single, \
+  private_value: reg | (shift << 8) | (mask << 16) | (invert << 24) }
+#endif
+
+int snd_ad1848_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo);
+int snd_ad1848_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol);
+int snd_ad1848_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol);
+
+#ifdef TARGET_OS2
+#define AD1848_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \
+{ SNDRV_CTL_ELEM_IFACE_MIXER, 0, 0, xname, xindex, \
+  0, 0, snd_ad1848_info_double, \
+  snd_ad1848_get_double, snd_ad1848_put_double, \
+  left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
+#else
+#define AD1848_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
+  info: snd_ad1848_info_double, \
+  get: snd_ad1848_get_double, put: snd_ad1848_put_double, \
+  private_value: left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
+#endif
+
+int snd_ad1848_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo);
+int snd_ad1848_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol);
+int snd_ad1848_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol);
+
+#ifdef CONFIG_SND_DEBUG
+void snd_ad1848_debug(ad1848_t *chip);
+#endif
+
+#endif				/* __AD1848_H */
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/ainstr_fm.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/ainstr_fm.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/ainstr_fm.h	(revision 91)
@@ -0,0 +1,134 @@
+/*
+ *  Advanced Linux Sound Architecture
+ *
+ *  FM (OPL2/3) Instrument Format
+ *  Copyright (c) 2000 Uros Bizjak <uros@kss-loka.si>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#ifndef __SOUND_AINSTR_FM_H
+#define __SOUND_AINSTR_FM_H
+
+#ifndef __KERNEL__
+#include <asm/types.h>
+#include <asm/byteorder.h>
+#endif
+
+/*
+ *  share types (share ID 1)
+ */
+
+#define FM_SHARE_FILE		0
+
+/*
+ * FM operator
+ */
+
+struct fm_operator {
+	unsigned char am_vib;
+	unsigned char ksl_level;
+	unsigned char attack_decay;
+	unsigned char sustain_release;
+	unsigned char wave_select;
+};
+
+/*
+ *  Instrument
+ */
+
+#define FM_PATCH_OPL2	0x01		/* OPL2 2 operators FM instrument */
+#define FM_PATCH_OPL3	0x02		/* OPL3 4 operators FM instrument */
+
+struct fm_instrument {
+	unsigned int share_id[4];	/* share id - zero = no sharing */
+	unsigned char type;		/* instrument type */
+
+	struct fm_operator op[4];
+	unsigned char feedback_connection[2];
+
+	unsigned char echo_delay;
+	unsigned char echo_atten;
+	unsigned char chorus_spread;
+	unsigned char trnsps;
+	unsigned char fix_dur;
+	unsigned char modes;
+	unsigned char fix_key;
+};
+
+/*
+ *
+ *    Kernel <-> user space
+ *    Hardware (CPU) independent section
+ *
+ *    * = zero or more
+ *    + = one or more
+ *
+ *    fm_xinstrument	FM_STRU_INSTR
+ *
+ */
+
+#define FM_STRU_INSTR	__cpu_to_be32(('I'<<24)|('N'<<16)|('S'<<8)|'T')
+
+/*
+ * FM operator
+ */
+
+struct fm_xoperator {
+	__u8 am_vib;
+	__u8 ksl_level;
+	__u8 attack_decay;
+	__u8 sustain_release;
+	__u8 wave_select;
+};
+
+/*
+ *  Instrument
+ */
+
+struct fm_xinstrument {
+	__u32 stype;			/* structure type */
+
+	__u32 share_id[4];		/* share id - zero = no sharing */
+	__u8 type;			/* instrument type */
+
+	struct fm_xoperator op[4];		/* fm operators */
+	__u8 feedback_connection[2];
+
+	__u8 echo_delay;
+	__u8 echo_atten;
+	__u8 chorus_spread;
+	__u8 trnsps;
+	__u8 fix_dur;
+	__u8 modes;
+	__u8 fix_key;
+};
+
+#ifdef __KERNEL__
+
+#include "seq_instr.h"
+
+int snd_seq_fm_init(struct snd_seq_kinstr_ops * ops,
+		    struct snd_seq_kinstr_ops * next);
+
+#endif
+
+/* typedefs for compatibility to user-space */
+typedef struct fm_xoperator fm_xoperator_t;
+typedef struct fm_xinstrument fm_xinstrument_t;
+
+#endif	/* __SOUND_AINSTR_FM_H */
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/ainstr_gf1.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/ainstr_gf1.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/ainstr_gf1.h	(revision 91)
@@ -0,0 +1,227 @@
+/*
+ *  Advanced Linux Sound Architecture
+ *
+ *  GF1 (GUS) Patch Instrument Format
+ *  Copyright (c) 1994-99 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef __AINSTR_GF1_H
+#define __AINSTR_GF1_H
+
+#ifndef __KERNEL__
+#include <asm/types.h>
+#include <asm/byteorder.h>
+#endif
+
+/*
+ *  share types (share ID 1)
+ */
+
+#define GF1_SHARE_FILE			0
+
+/*
+ *  wave formats
+ */
+
+#define GF1_WAVE_16BIT			0x0001  /* 16-bit wave */
+#define GF1_WAVE_UNSIGNED		0x0002  /* unsigned wave */
+#define GF1_WAVE_INVERT			0x0002  /* same as unsigned wave */
+#define GF1_WAVE_BACKWARD		0x0004  /* backward mode (maybe used for reverb or ping-ping loop) */
+#define GF1_WAVE_LOOP			0x0008  /* loop mode */
+#define GF1_WAVE_BIDIR			0x0010  /* bidirectional mode */
+#define GF1_WAVE_STEREO			0x0100	/* stereo mode */
+#define GF1_WAVE_ULAW			0x0200	/* uLaw compression mode */
+
+/*
+ *  Wavetable definitions
+ */
+
+typedef struct gf1_wave {
+	unsigned int share_id[4];	/* share id - zero = no sharing */
+	unsigned int format;		/* wave format */
+
+	struct {
+		unsigned int number;	/* some other ID for this instrument */
+		unsigned int memory;	/* begin of waveform in onboard memory */
+		unsigned char *ptr;	/* pointer to waveform in system memory */
+	} address;
+
+	unsigned int size;		/* size of waveform in samples */
+	unsigned int start;		/* start offset in samples * 16 (lowest 4 bits - fraction) */
+	unsigned int loop_start;	/* bits loop start offset in samples * 16 (lowest 4 bits - fraction) */
+	unsigned int loop_end;		/* loop start offset in samples * 16 (lowest 4 bits - fraction) */
+	unsigned short loop_repeat;	/* loop repeat - 0 = forever */
+
+	unsigned char flags;		/* GF1 patch flags */
+	unsigned char pad;
+	unsigned int sample_rate;	/* sample rate in Hz */
+	unsigned int low_frequency;	/* low frequency range */
+	unsigned int high_frequency;	/* high frequency range */
+	unsigned int root_frequency;	/* root frequency range */
+	signed short tune;
+	unsigned char balance;
+	unsigned char envelope_rate[6];
+	unsigned char envelope_offset[6];
+	unsigned char tremolo_sweep;
+	unsigned char tremolo_rate;
+	unsigned char tremolo_depth;
+	unsigned char vibrato_sweep;
+	unsigned char vibrato_rate;
+	unsigned char vibrato_depth;
+	unsigned short scale_frequency;
+	unsigned short scale_factor;	/* 0-2048 or 0-2 */
+  
+	struct gf1_wave *next;
+} gf1_wave_t;
+
+/*
+ *  Instrument
+ */
+
+#define IWFFFF_EXCLUDE_NONE		0x0000	/* exclusion mode - none */
+#define IWFFFF_EXCLUDE_SINGLE		0x0001	/* exclude single - single note from the instrument group */
+#define IWFFFF_EXCLUDE_MULTIPLE		0x0002	/* exclude multiple - stop only same note from this instrument */
+
+#define IWFFFF_EFFECT_NONE		0
+#define IWFFFF_EFFECT_REVERB		1
+#define IWFFFF_EFFECT_CHORUS		2
+#define IWFFFF_EFFECT_ECHO		3
+
+typedef struct {
+	unsigned short exclusion;
+	unsigned short exclusion_group;	/* 0 - none, 1-65535 */
+
+	unsigned char effect1;		/* effect 1 */
+	unsigned char effect1_depth;	/* 0-127 */
+	unsigned char effect2;		/* effect 2 */
+	unsigned char effect2_depth;	/* 0-127 */
+
+	gf1_wave_t *wave;		/* first waveform */
+} gf1_instrument_t;
+
+/*
+ *
+ *    Kernel <-> user space
+ *    Hardware (CPU) independent section
+ *
+ *    * = zero or more
+ *    + = one or more
+ *
+ *    gf1_xinstrument		IWFFFF_STRU_INSTR
+ *      +gf1_xwave		IWFFFF_STRU_WAVE
+ *
+ */
+
+#define GF1_STRU_WAVE		__cpu_to_be32(('W'<<24)|('A'<<16)|('V'<<8)|'E')
+#define GF1_STRU_INSTR		__cpu_to_be32(('I'<<24)|('N'<<16)|('S'<<8)|'T')
+
+/*
+ *  Wavetable definitions
+ */
+
+typedef struct gf1_xwave {
+	__u32 stype;			/* structure type */
+
+	__u32 share_id[4];		/* share id - zero = no sharing */
+	__u32 format;			/* wave format */
+
+	__u32 size;			/* size of waveform in samples */
+	__u32 start;			/* start offset in samples * 16 (lowest 4 bits - fraction) */
+	__u32 loop_start;		/* bits loop start offset in samples * 16 (lowest 4 bits - fraction) */
+	__u32 loop_end;			/* loop start offset in samples * 16 (lowest 4 bits - fraction) */
+	__u16 loop_repeat;		/* loop repeat - 0 = forever */
+
+	__u8 flags;			/* GF1 patch flags */
+	__u8 pad;
+	__u32 sample_rate;		/* sample rate in Hz */
+	__u32 low_frequency;		/* low frequency range */
+	__u32 high_frequency;		/* high frequency range */
+	__u32 root_frequency;		/* root frequency range */
+	__s16 tune;
+	__u8 balance;
+	__u8 envelope_rate[6];
+	__u8 envelope_offset[6];
+	__u8 tremolo_sweep;
+	__u8 tremolo_rate;
+	__u8 tremolo_depth;
+	__u8 vibrato_sweep;
+	__u8 vibrato_rate;
+	__u8 vibrato_depth;
+	__u16 scale_frequency;
+	__u16 scale_factor;		/* 0-2048 or 0-2 */  
+} gf1_xwave_t;
+
+/*
+ *  Instrument
+ */
+
+typedef struct gf1_xinstrument {
+	__u32 stype;
+	
+	__u16 exclusion;
+	__u16 exclusion_group;		/* 0 - none, 1-65535 */
+
+	__u8 effect1;			/* effect 1 */
+	__u8 effect1_depth;		/* 0-127 */
+	__u8 effect2;			/* effect 2 */
+	__u8 effect2_depth;		/* 0-127 */
+} gf1_xinstrument_t;
+
+/*
+ *  Instrument info
+ */
+
+#define GF1_INFO_ENVELOPE		(1<<0)
+#define GF1_INFO_TREMOLO		(1<<1)
+#define GF1_INFO_VIBRATO		(1<<2)
+
+typedef struct gf1_info {
+	unsigned char flags;		/* supported wave flags */
+	unsigned char pad[3];
+	unsigned int features;		/* supported features */
+	unsigned int max8_len;		/* maximum 8-bit wave length */
+	unsigned int max16_len;		/* maximum 16-bit wave length */
+} gf1_info_t;
+
+#ifdef __KERNEL__
+
+#include "seq_instr.h"
+
+extern char *snd_seq_gf1_id;
+
+typedef struct {
+	void *private_data;
+	int (*info)(void *private_data, gf1_info_t *info);
+	int (*put_sample)(void *private_data, gf1_wave_t *wave,
+	                  char *data, long len, int atomic);
+	int (*get_sample)(void *private_data, gf1_wave_t *wave,
+			  char *data, long len, int atomic);
+	int (*remove_sample)(void *private_data, gf1_wave_t *wave,
+			     int atomic);
+	void (*notify)(void *private_data, snd_seq_kinstr_t *instr, int what);
+	snd_seq_kinstr_ops_t kops;
+} snd_gf1_ops_t;
+
+int snd_seq_gf1_init(snd_gf1_ops_t *ops,
+		     void *private_data,
+		     snd_seq_kinstr_ops_t *next);
+
+#endif
+
+#endif /* __SEQ_INSTR_GF1_H */
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/ainstr_iw.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/ainstr_iw.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/ainstr_iw.h	(revision 91)
@@ -0,0 +1,375 @@
+/*
+ *  Advanced Linux Sound Architecture
+ *
+ *  InterWave FFFF Instrument Format
+ *  Copyright (c) 1994-99 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef __AINSTR_IW_H
+#define __AINSTR_IW_H
+
+#ifndef __KERNEL__
+#include <asm/types.h>
+#include <asm/byteorder.h>
+#endif
+
+/*
+ *  share types (share ID 1)
+ */
+
+#define IWFFFF_SHARE_FILE		0
+
+/*
+ *  wave formats
+ */
+
+#define IWFFFF_WAVE_16BIT		0x0001  /* 16-bit wave */
+#define IWFFFF_WAVE_UNSIGNED		0x0002  /* unsigned wave */
+#define IWFFFF_WAVE_INVERT		0x0002  /* same as unsigned wave */
+#define IWFFFF_WAVE_BACKWARD		0x0004  /* backward mode (maybe used for reverb or ping-ping loop) */
+#define IWFFFF_WAVE_LOOP		0x0008  /* loop mode */
+#define IWFFFF_WAVE_BIDIR		0x0010  /* bidirectional mode */
+#define IWFFFF_WAVE_ULAW		0x0020  /* uLaw compressed wave */
+#define IWFFFF_WAVE_RAM			0x0040  /* wave is _preloaded_ in RAM (it is used for ROM simulation) */
+#define IWFFFF_WAVE_ROM			0x0080  /* wave is in ROM */
+#define IWFFFF_WAVE_STEREO		0x0100	/* wave is stereo */
+
+/*
+ *  Wavetable definitions
+ */
+
+typedef struct iwffff_wave {
+	unsigned int share_id[4];	/* share id - zero = no sharing */
+	unsigned int format;		/* wave format */
+
+	struct {
+		unsigned int number;	/* some other ID for this wave */
+		unsigned int memory;	/* begin of waveform in onboard memory */
+		unsigned char *ptr;	/* pointer to waveform in system memory */
+	} address;
+
+	unsigned int size;		/* size of waveform in samples */
+	unsigned int start;		/* start offset in samples * 16 (lowest 4 bits - fraction) */
+	unsigned int loop_start;	/* bits loop start offset in samples * 16 (lowest 4 bits - fraction) */
+	unsigned int loop_end;		/* loop start offset in samples * 16 (lowest 4 bits - fraction) */
+	unsigned short loop_repeat;	/* loop repeat - 0 = forever */
+	unsigned int sample_ratio;	/* sample ratio (44100 * 1024 / rate) */
+	unsigned char attenuation;	/* 0 - 127 (no corresponding midi controller) */
+	unsigned char low_note;		/* lower frequency range for this waveform */
+	unsigned char high_note;	/* higher frequency range for this waveform */
+	unsigned char pad;
+  
+	struct iwffff_wave *next;
+} iwffff_wave_t;
+
+/*
+ *  Layer
+ */
+
+#define IWFFFF_LFO_SHAPE_TRIANGLE	0
+#define IWFFFF_LFO_SHAPE_POSTRIANGLE	1
+
+typedef struct iwffff_lfo {
+	unsigned short freq;		/* (0-2047) 0.01Hz - 21.5Hz */
+	signed short depth;		/* volume +- (0-255) 0.48675dB/step */
+	signed short sweep;		/* 0 - 950 deciseconds */
+	unsigned char shape;		/* see to IWFFFF_LFO_SHAPE_XXXX */
+	unsigned char delay;		/* 0 - 255 deciseconds */
+} iwffff_lfo_t;
+
+#define IWFFFF_ENV_FLAG_RETRIGGER	0x0001	/* flag - retrigger */
+
+#define IWFFFF_ENV_MODE_ONE_SHOT	0x0001	/* mode - one shot */
+#define IWFFFF_ENV_MODE_SUSTAIN		0x0002	/* mode - sustain */
+#define IWFFFF_ENV_MODE_NO_SUSTAIN	0x0003	/* mode - no sustain */
+
+#define IWFFFF_ENV_INDEX_VELOCITY	0x0001	/* index - velocity */
+#define IWFFFF_ENV_INDEX_FREQUENCY	0x0002	/* index - frequency */
+
+typedef struct iwffff_env_point {
+	unsigned short offset;
+	unsigned short rate;
+} iwffff_env_point_t;
+
+typedef struct iwffff_env_record {
+	unsigned short nattack;
+	unsigned short nrelease;
+	unsigned short sustain_offset;
+	unsigned short sustain_rate;
+	unsigned short release_rate;
+	unsigned char hirange;
+	unsigned char pad;
+	struct iwffff_env_record *next;
+	/* points are stored here */
+	/* count of points = nattack + nrelease */
+} iwffff_env_record_t;
+
+typedef struct iwffff_env {
+	unsigned char flags;
+  	unsigned char mode;
+  	unsigned char index;
+	unsigned char pad;
+	struct iwffff_env_record *record;
+} iwffff_env_t;
+
+#define IWFFFF_LAYER_FLAG_RETRIGGER	0x0001	/* retrigger */
+
+#define IWFFFF_LAYER_VELOCITY_TIME	0x0000	/* velocity mode = time */
+#define IWFFFF_LAYER_VELOCITY_RATE	0x0001	/* velocity mode = rate */
+
+#define IWFFFF_LAYER_EVENT_KUP		0x0000	/* layer event - key up */
+#define IWFFFF_LAYER_EVENT_KDOWN	0x0001	/* layer event - key down */
+#define IWFFFF_LAYER_EVENT_RETRIG	0x0002	/* layer event - retrigger */
+#define IWFFFF_LAYER_EVENT_LEGATO	0x0003	/* layer event - legato */
+
+typedef struct iwffff_layer {
+	unsigned char flags;
+	unsigned char velocity_mode;
+      	unsigned char layer_event;
+	unsigned char low_range;	/* range for layer based */
+	unsigned char high_range;	/* on either velocity or frequency */
+	unsigned char pan;		/* pan offset from CC1 (0 left - 127 right) */
+	unsigned char pan_freq_scale;	/* position based on frequency (0-127) */
+	unsigned char attenuation;	/* 0-127 (no corresponding midi controller) */
+	iwffff_lfo_t tremolo;		/* tremolo effect */
+	iwffff_lfo_t vibrato;		/* vibrato effect */
+	unsigned short freq_scale;	/* 0-2048, 1024 is equal to semitone scaling */
+	unsigned char freq_center;	/* center for keyboard frequency scaling */
+	unsigned char pad;
+	iwffff_env_t penv;		/* pitch envelope */
+	iwffff_env_t venv;		/* volume envelope */
+
+	iwffff_wave_t *wave;
+	struct iwffff_layer *next;
+} iwffff_layer_t;
+
+/*
+ *  Instrument
+ */
+
+#define IWFFFF_EXCLUDE_NONE		0x0000	/* exclusion mode - none */
+#define IWFFFF_EXCLUDE_SINGLE		0x0001	/* exclude single - single note from the instrument group */
+#define IWFFFF_EXCLUDE_MULTIPLE		0x0002	/* exclude multiple - stop only same note from this instrument */
+
+#define IWFFFF_LAYER_NONE		0x0000	/* not layered */
+#define IWFFFF_LAYER_ON			0x0001	/* layered */
+#define IWFFFF_LAYER_VELOCITY		0x0002	/* layered by velocity */
+#define IWFFFF_LAYER_FREQUENCY		0x0003	/* layered by frequency */
+
+#define IWFFFF_EFFECT_NONE		0
+#define IWFFFF_EFFECT_REVERB		1
+#define IWFFFF_EFFECT_CHORUS		2
+#define IWFFFF_EFFECT_ECHO		3
+
+typedef struct {
+	unsigned short exclusion;
+	unsigned short layer_type;
+	unsigned short exclusion_group;	/* 0 - none, 1-65535 */
+
+	unsigned char effect1;		/* effect 1 */
+	unsigned char effect1_depth;	/* 0-127 */
+	unsigned char effect2;		/* effect 2 */
+	unsigned char effect2_depth;	/* 0-127 */
+
+	iwffff_layer_t *layer;		/* first layer */
+} iwffff_instrument_t;
+
+/*
+ *
+ *    Kernel <-> user space
+ *    Hardware (CPU) independent section
+ *
+ *    * = zero or more
+ *    + = one or more
+ *
+ *    iwffff_xinstrument		IWFFFF_STRU_INSTR
+ *      +iwffff_xlayer			IWFFFF_STRU_LAYER
+ *        *iwffff_xenv_record		IWFFFF_STRU_ENV_RECT (tremolo)
+ *        *iwffff_xenv_record		IWFFFF_STRU_EVN_RECT (vibrato)
+ *          +iwffff_xwave		IWFFFF_STRU_WAVE
+ *
+ */
+
+#define IWFFFF_STRU_WAVE	__cpu_to_be32(('W'<<24)|('A'<<16)|('V'<<8)|'E')
+#define IWFFFF_STRU_ENV_RECP	__cpu_to_be32(('E'<<24)|('N'<<16)|('R'<<8)|'P')
+#define IWFFFF_STRU_ENV_RECV	__cpu_to_be32(('E'<<24)|('N'<<16)|('R'<<8)|'V')
+#define IWFFFF_STRU_LAYER 	__cpu_to_be32(('L'<<24)|('A'<<16)|('Y'<<8)|'R')
+#define IWFFFF_STRU_INSTR 	__cpu_to_be32(('I'<<24)|('N'<<16)|('S'<<8)|'T')
+
+/*
+ *  Wavetable definitions
+ */
+
+typedef struct iwffff_xwave {
+	__u32 stype;			/* structure type */
+
+	__u32 share_id[4];		/* share id - zero = no sharing */
+
+	__u32 format;			/* wave format */
+	__u32 offset;			/* offset to ROM (address) */
+
+	__u32 size;			/* size of waveform in samples */
+	__u32 start;			/* start offset in samples * 16 (lowest 4 bits - fraction) */
+	__u32 loop_start;		/* bits loop start offset in samples * 16 (lowest 4 bits - fraction) */
+	__u32 loop_end;			/* loop start offset in samples * 16 (lowest 4 bits - fraction) */
+	__u16 loop_repeat;		/* loop repeat - 0 = forever */
+	__u32 sample_ratio;		/* sample ratio (44100 * 1024 / rate) */
+	__u8 attenuation;		/* 0 - 127 (no corresponding midi controller) */
+	__u8 low_note;			/* lower frequency range for this waveform */
+	__u8 high_note;			/* higher frequency range for this waveform */
+	__u8 pad;
+} iwffff_xwave_t;
+
+/*
+ *  Layer
+ */
+
+typedef struct iwffff_xlfo {
+	__u16 freq;			/* (0-2047) 0.01Hz - 21.5Hz */
+	__s16 depth;			/* volume +- (0-255) 0.48675dB/step */
+	__s16 sweep;			/* 0 - 950 deciseconds */
+	__u8 shape;			/* see to ULTRA_IW_LFO_SHAPE_XXXX */
+	__u8 delay;			/* 0 - 255 deciseconds */
+} iwffff_xlfo_t;
+
+typedef struct iwffff_xenv_point {
+	__u16 offset;
+	__u16 rate;
+} iwffff_xenv_point_t;
+
+typedef struct iwffff_xenv_record {
+	__u32 stype;
+	__u16 nattack;
+	__u16 nrelease;
+	__u16 sustain_offset;
+	__u16 sustain_rate;
+	__u16 release_rate;
+	__u8 hirange;
+	__u8 pad;
+	/* points are stored here.. */
+	/* count of points = nattack + nrelease */
+} iwffff_xenv_record_t;
+
+typedef struct iwffff_xenv {
+	__u8 flags;
+  	__u8 mode;
+  	__u8 index;
+	__u8 pad;
+} iwffff_xenv_t;
+
+typedef struct iwffff_xlayer {
+	__u32 stype;
+	__u8 flags;
+	__u8 velocity_mode;
+      	__u8 layer_event;
+	__u8 low_range;			/* range for layer based */
+	__u8 high_range;		/* on either velocity or frequency */
+	__u8 pan;			/* pan offset from CC1 (0 left - 127 right) */
+	__u8 pan_freq_scale;		/* position based on frequency (0-127) */
+	__u8 attenuation;		/* 0-127 (no corresponding midi controller) */
+	iwffff_xlfo_t tremolo;		/* tremolo effect */
+	iwffff_xlfo_t vibrato;		/* vibrato effect */
+	__u16 freq_scale;		/* 0-2048, 1024 is equal to semitone scaling */
+	__u8 freq_center;		/* center for keyboard frequency scaling */
+	__u8 pad;
+	iwffff_xenv_t penv;		/* pitch envelope */
+	iwffff_xenv_t venv;		/* volume envelope */
+} iwffff_xlayer_t;
+
+/*
+ *  Instrument
+ */
+
+typedef struct iwffff_xinstrument {
+	__u32 stype;
+	
+	__u16 exclusion;
+	__u16 layer_type;
+	__u16 exclusion_group;		/* 0 - none, 1-65535 */
+
+	__u8 effect1;			/* effect 1 */
+	__u8 effect1_depth;		/* 0-127 */
+	__u8 effect2;			/* effect 2 */
+	__u8 effect2_depth;		/* 0-127 */
+} iwffff_xinstrument_t;
+
+/*
+ *  ROM support
+ *    InterWave ROMs are Little-Endian (x86)
+ */
+
+#define IWFFFF_ROM_HDR_SIZE	512
+
+typedef struct {
+	__u8 iwave[8];
+	__u8 revision;
+	__u8 series_number;
+	__u8 series_name[16];
+	__u8 date[10];
+	__u16 vendor_revision_major;
+	__u16 vendor_revision_minor;
+	__u32 rom_size;
+	__u8 copyright[128];
+	__u8 vendor_name[64];
+	__u8 description[128];
+} iwffff_rom_header_t;
+
+/*
+ *  Instrument info
+ */
+
+#define IWFFFF_INFO_LFO_VIBRATO		(1<<0)
+#define IWFFFF_INFO_LFO_VIBRATO_SHAPE	(1<<1)
+#define IWFFFF_INFO_LFO_TREMOLO		(1<<2)
+#define IWFFFF_INFO_LFO_TREMOLO_SHAPE	(1<<3)
+
+typedef struct iwffff_info {
+	unsigned int format;		/* supported format bits */
+	unsigned int effects;		/* supported effects (1 << IWFFFF_EFFECT*) */
+	unsigned int lfos;		/* LFO effects */
+	unsigned int max8_len;		/* maximum 8-bit wave length */
+	unsigned int max16_len;		/* maximum 16-bit wave length */
+} iwffff_info_t;
+
+#ifdef __KERNEL__
+
+#include "seq_instr.h"
+
+extern char *snd_seq_iwffff_id;
+
+typedef struct {
+	void *private_data;
+	int (*info)(void *private_data, iwffff_info_t *info);
+	int (*put_sample)(void *private_data, iwffff_wave_t *wave,
+	                  char *data, long len, int atomic);
+	int (*get_sample)(void *private_data, iwffff_wave_t *wave,
+			  char *data, long len, int atomic);
+	int (*remove_sample)(void *private_data, iwffff_wave_t *wave,
+			     int atomic);
+	void (*notify)(void *private_data, snd_seq_kinstr_t *instr, int what);
+	snd_seq_kinstr_ops_t kops;
+} snd_iwffff_ops_t;
+
+int snd_seq_iwffff_init(snd_iwffff_ops_t *ops,
+			void *private_data,
+                        snd_seq_kinstr_ops_t *next);
+
+#endif
+
+#endif /* __SEQ_INSTR_IW_H */
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/ainstr_simple.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/ainstr_simple.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/ainstr_simple.h	(revision 91)
@@ -0,0 +1,158 @@
+/*
+ *  Advanced Linux Sound Architecture
+ *
+ *  Simple (MOD player) Instrument Format
+ *  Copyright (c) 1994-99 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef __AINSTR_SIMPLE_H
+#define __AINSTR_SIMPLE_H
+
+#ifndef __KERNEL__
+#include <asm/types.h>
+#include <asm/byteorder.h>
+#endif
+
+/*
+ *  share types (share ID 1)
+ */
+
+#define SIMPLE_SHARE_FILE		0
+
+/*
+ *  wave formats
+ */
+
+#define SIMPLE_WAVE_16BIT		0x0001  /* 16-bit wave */
+#define SIMPLE_WAVE_UNSIGNED		0x0002  /* unsigned wave */
+#define SIMPLE_WAVE_INVERT		0x0002  /* same as unsigned wave */
+#define SIMPLE_WAVE_BACKWARD		0x0004  /* backward mode (maybe used for reverb or ping-ping loop) */
+#define SIMPLE_WAVE_LOOP		0x0008  /* loop mode */
+#define SIMPLE_WAVE_BIDIR		0x0010  /* bidirectional mode */
+#define SIMPLE_WAVE_STEREO		0x0100	/* stereo wave */
+#define SIMPLE_WAVE_ULAW		0x0200	/* uLaw compression mode */
+
+/*
+ *  instrument effects
+ */
+
+#define SIMPLE_EFFECT_NONE		0
+#define SIMPLE_EFFECT_REVERB		1
+#define SIMPLE_EFFECT_CHORUS		2
+#define SIMPLE_EFFECT_ECHO		3
+
+/*
+ *  instrument info
+ */
+
+typedef struct simple_instrument_info {
+	unsigned int format;		/* supported format bits */
+	unsigned int effects;		/* supported effects (1 << SIMPLE_EFFECT_*) */
+	unsigned int max8_len;		/* maximum 8-bit wave length */
+	unsigned int max16_len;		/* maximum 16-bit wave length */
+} simple_instrument_info_t;
+
+/*
+ *  Instrument
+ */
+
+typedef struct {
+	unsigned int share_id[4];	/* share id - zero = no sharing */
+	unsigned int format;		/* wave format */
+
+	struct {
+		unsigned int number;	/* some other ID for this instrument */
+		unsigned int memory;	/* begin of waveform in onboard memory */
+		unsigned char *ptr;	/* pointer to waveform in system memory */
+	} address;
+
+	unsigned int size;		/* size of waveform in samples */
+	unsigned int start;		/* start offset in samples * 16 (lowest 4 bits - fraction) */
+	unsigned int loop_start;	/* loop start offset in samples * 16 (lowest 4 bits - fraction) */
+	unsigned int loop_end;		/* loop end offset in samples * 16 (lowest 4 bits - fraction) */
+	unsigned short loop_repeat;	/* loop repeat - 0 = forever */
+
+	unsigned char effect1;		/* effect 1 */
+	unsigned char effect1_depth;	/* 0-127 */
+	unsigned char effect2;		/* effect 2 */
+	unsigned char effect2_depth;	/* 0-127 */
+} simple_instrument_t;
+
+/*
+ *
+ *    Kernel <-> user space
+ *    Hardware (CPU) independent section
+ *
+ *    * = zero or more
+ *    + = one or more
+ *
+ *    simple_xinstrument	SIMPLE_STRU_INSTR
+ *
+ */
+
+#define SIMPLE_STRU_INSTR	__cpu_to_be32(('I'<<24)|('N'<<16)|('S'<<8)|'T')
+
+/*
+ *  Instrument
+ */
+
+typedef struct simple_xinstrument {
+	__u32 stype;
+
+	__u32 share_id[4];		/* share id - zero = no sharing */
+	__u32 format;			/* wave format */
+
+	__u32 size;			/* size of waveform in samples */
+	__u32 start;			/* start offset in samples * 16 (lowest 4 bits - fraction) */
+	__u32 loop_start;		/* bits loop start offset in samples * 16 (lowest 4 bits - fraction) */
+	__u32 loop_end;			/* loop start offset in samples * 16 (lowest 4 bits - fraction) */
+	__u16 loop_repeat;		/* loop repeat - 0 = forever */
+	
+	__u8 effect1;			/* effect 1 */
+	__u8 effect1_depth;		/* 0-127 */
+	__u8 effect2;			/* effect 2 */
+	__u8 effect2_depth;		/* 0-127 */
+} simple_xinstrument_t;
+
+#ifdef __KERNEL__
+
+#include "seq_instr.h"
+
+extern char *snd_seq_simple_id;
+
+typedef struct snd_simple_ops {
+	void *private_data;
+	int (*info)(void *private_data, simple_instrument_info_t *info);
+	int (*put_sample)(void *private_data, simple_instrument_t *instr,
+	                  char *data, long len, int atomic);
+	int (*get_sample)(void *private_data, simple_instrument_t *instr,
+			  char *data, long len, int atomic);
+	int (*remove_sample)(void *private_data, simple_instrument_t *instr,
+			     int atomic);
+	void (*notify)(void *private_data, snd_seq_kinstr_t *instr, int what);
+	snd_seq_kinstr_ops_t kops;
+} snd_simple_ops_t;
+
+int snd_seq_simple_init(snd_simple_ops_t *ops,
+			void *private_data,
+			snd_seq_kinstr_ops_t *next);
+
+#endif
+
+#endif /* __SEQ_INSTR_SIMPLE_H */
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/ak4531_codec.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/ak4531_codec.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/ak4531_codec.h	(revision 91)
@@ -0,0 +1,85 @@
+#ifndef __SOUND_AK4531_CODEC_H
+#define __SOUND_AK4531_CODEC_H
+
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  Universal interface for Audio Codec '97
+ *
+ *  For more details look to AC '97 component specification revision 2.1
+ *  by Intel Corporation (http://developer.intel.com).
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include "info.h"
+#include "control.h"
+
+/*
+ *  ASAHI KASEI - AK4531 codec
+ *  - not really AC'97 codec, but it uses very similar interface as AC'97
+ */
+
+/*
+ *  AK4531 codec registers
+ */
+
+#define AK4531_LMASTER  0x00	/* master volume left */
+#define AK4531_RMASTER  0x01	/* master volume right */
+#define AK4531_LVOICE   0x02	/* channel volume left */
+#define AK4531_RVOICE   0x03	/* channel volume right */
+#define AK4531_LFM      0x04	/* FM volume left */
+#define AK4531_RFM      0x05	/* FM volume right */
+#define AK4531_LCD      0x06	/* CD volume left */
+#define AK4531_RCD      0x07	/* CD volume right */
+#define AK4531_LLINE    0x08	/* LINE volume left */
+#define AK4531_RLINE    0x09	/* LINE volume right */
+#define AK4531_LAUXA    0x0a	/* AUXA volume left */
+#define AK4531_RAUXA    0x0b	/* AUXA volume right */
+#define AK4531_MONO1    0x0c	/* MONO1 volume left */
+#define AK4531_MONO2    0x0d	/* MONO1 volume right */
+#define AK4531_MIC      0x0e	/* MIC volume */
+#define AK4531_MONO_OUT 0x0f	/* Mono-out volume */
+#define AK4531_OUT_SW1  0x10	/* Output mixer switch 1 */
+#define AK4531_OUT_SW2  0x11	/* Output mixer switch 2 */
+#define AK4531_LIN_SW1  0x12	/* Input left mixer switch 1 */
+#define AK4531_RIN_SW1  0x13	/* Input right mixer switch 1 */
+#define AK4531_LIN_SW2  0x14	/* Input left mixer switch 2 */
+#define AK4531_RIN_SW2  0x15	/* Input right mixer switch 2 */
+#define AK4531_RESET    0x16	/* Reset & power down */
+#define AK4531_CLOCK    0x17	/* Clock select */
+#define AK4531_AD_IN    0x18	/* AD input select */
+#define AK4531_MIC_GAIN 0x19	/* MIC amplified gain */
+
+struct snd_ak4531 {
+	void (*write) (struct snd_ak4531 *ak4531, unsigned short reg,
+		       unsigned short val);
+	void *private_data;
+	void (*private_free) (struct snd_ak4531 *ak4531);
+	/* --- */
+	unsigned char regs[0x20];
+	struct semaphore reg_mutex;
+};
+
+int snd_ak4531_mixer(struct snd_card *card, struct snd_ak4531 *_ak4531,
+		     struct snd_ak4531 **rak4531);
+
+#ifdef CONFIG_PM
+void snd_ak4531_suspend(struct snd_ak4531 *ak4531);
+void snd_ak4531_resume(struct snd_ak4531 *ak4531);
+#endif
+
+#endif /* __SOUND_AK4531_CODEC_H */
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/asequencer.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/asequencer.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/asequencer.h	(revision 91)
@@ -0,0 +1,914 @@
+/*
+ *  Main header file for the ALSA sequencer
+ *  Copyright (c) 1998-1999 by Frank van de Pol <fvdpol@coil.demon.nl>
+ *            (c) 1998-1999 by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+#ifndef __SOUND_ASEQUENCER_H
+#define __SOUND_ASEQUENCER_H
+
+#ifdef __KERNEL__
+#include <linux/ioctl.h>
+#include <sound/asound.h>
+#endif
+
+/** version of the sequencer */
+#define SNDRV_SEQ_VERSION SNDRV_PROTOCOL_VERSION (1, 0, 1)
+
+/**
+ * definition of sequencer event types
+ */
+
+/** system messages
+ * event data type = #snd_seq_result
+ */
+#define SNDRV_SEQ_EVENT_SYSTEM		0
+#define SNDRV_SEQ_EVENT_RESULT		1
+
+/** note messages (channel specific)
+ * event data type = #snd_seq_ev_note
+ */
+#define SNDRV_SEQ_EVENT_NOTE		5
+#define SNDRV_SEQ_EVENT_NOTEON		6
+#define SNDRV_SEQ_EVENT_NOTEOFF		7
+#define SNDRV_SEQ_EVENT_KEYPRESS	8
+	
+/** control messages (channel specific)
+ * event data type = #snd_seq_ev_ctrl
+ */
+#define SNDRV_SEQ_EVENT_CONTROLLER	10
+#define SNDRV_SEQ_EVENT_PGMCHANGE	11
+#define SNDRV_SEQ_EVENT_CHANPRESS	12
+#define SNDRV_SEQ_EVENT_PITCHBEND	13	/**< from -8192 to 8191 */
+#define SNDRV_SEQ_EVENT_CONTROL14	14	/**< 14 bit controller value */
+#define SNDRV_SEQ_EVENT_NONREGPARAM	15	/**< 14 bit NRPN address + 14 bit unsigned value */
+#define SNDRV_SEQ_EVENT_REGPARAM	16	/**< 14 bit RPN address + 14 bit unsigned value */
+
+/** synchronisation messages
+ * event data type = #snd_seq_ev_ctrl
+ */
+#define SNDRV_SEQ_EVENT_SONGPOS		20	/* Song Position Pointer with LSB and MSB values */
+#define SNDRV_SEQ_EVENT_SONGSEL		21	/* Song Select with song ID number */
+#define SNDRV_SEQ_EVENT_QFRAME		22	/* midi time code quarter frame */
+#define SNDRV_SEQ_EVENT_TIMESIGN	23	/* SMF Time Signature event */
+#define SNDRV_SEQ_EVENT_KEYSIGN		24	/* SMF Key Signature event */
+	        
+/** timer messages
+ * event data type = snd_seq_ev_queue_control
+ */
+#define SNDRV_SEQ_EVENT_START		30	/* midi Real Time Start message */
+#define SNDRV_SEQ_EVENT_CONTINUE	31	/* midi Real Time Continue message */
+#define SNDRV_SEQ_EVENT_STOP		32	/* midi Real Time Stop message */	
+#define	SNDRV_SEQ_EVENT_SETPOS_TICK	33	/* set tick queue position */
+#define SNDRV_SEQ_EVENT_SETPOS_TIME	34	/* set realtime queue position */
+#define SNDRV_SEQ_EVENT_TEMPO		35	/* (SMF) Tempo event */
+#define SNDRV_SEQ_EVENT_CLOCK		36	/* midi Real Time Clock message */
+#define SNDRV_SEQ_EVENT_TICK		37	/* midi Real Time Tick message */
+#define SNDRV_SEQ_EVENT_QUEUE_SKEW	38	/* skew queue tempo */
+
+/** others
+ * event data type = none
+ */
+#define SNDRV_SEQ_EVENT_TUNE_REQUEST	40	/* tune request */
+#define SNDRV_SEQ_EVENT_RESET		41	/* reset to power-on state */
+#define SNDRV_SEQ_EVENT_SENSING		42	/* "active sensing" event */
+
+/** echo back, kernel private messages
+ * event data type = any type
+ */
+#define SNDRV_SEQ_EVENT_ECHO		50	/* echo event */
+#define SNDRV_SEQ_EVENT_OSS		51	/* OSS raw event */
+
+/** system status messages (broadcast for subscribers)
+ * event data type = snd_seq_addr
+ */
+#define SNDRV_SEQ_EVENT_CLIENT_START	60	/* new client has connected */
+#define SNDRV_SEQ_EVENT_CLIENT_EXIT	61	/* client has left the system */
+#define SNDRV_SEQ_EVENT_CLIENT_CHANGE	62	/* client status/info has changed */
+#define SNDRV_SEQ_EVENT_PORT_START	63	/* new port was created */
+#define SNDRV_SEQ_EVENT_PORT_EXIT	64	/* port was deleted from system */
+#define SNDRV_SEQ_EVENT_PORT_CHANGE	65	/* port status/info has changed */
+
+/** port connection changes
+ * event data type = snd_seq_connect
+ */
+#define SNDRV_SEQ_EVENT_PORT_SUBSCRIBED	66	/* ports connected */
+#define SNDRV_SEQ_EVENT_PORT_UNSUBSCRIBED 67	/* ports disconnected */
+
+/** synthesizer events
+ * event data type = snd_seq_eve_sample_control
+ */
+#define SNDRV_SEQ_EVENT_SAMPLE		70	/* sample select */
+#define SNDRV_SEQ_EVENT_SAMPLE_CLUSTER	71	/* sample cluster select */
+#define SNDRV_SEQ_EVENT_SAMPLE_START	72	/* voice start */
+#define SNDRV_SEQ_EVENT_SAMPLE_STOP	73	/* voice stop */
+#define SNDRV_SEQ_EVENT_SAMPLE_FREQ	74	/* playback frequency */
+#define SNDRV_SEQ_EVENT_SAMPLE_VOLUME	75	/* volume and balance */
+#define SNDRV_SEQ_EVENT_SAMPLE_LOOP	76	/* sample loop */
+#define SNDRV_SEQ_EVENT_SAMPLE_POSITION	77	/* sample position */
+#define SNDRV_SEQ_EVENT_SAMPLE_PRIVATE1	78	/* private (hardware dependent) event */
+
+/** user-defined events with fixed length
+ * event data type = any
+ */
+#define SNDRV_SEQ_EVENT_USR0		90
+#define SNDRV_SEQ_EVENT_USR1		91
+#define SNDRV_SEQ_EVENT_USR2		92
+#define SNDRV_SEQ_EVENT_USR3		93
+#define SNDRV_SEQ_EVENT_USR4		94
+#define SNDRV_SEQ_EVENT_USR5		95
+#define SNDRV_SEQ_EVENT_USR6		96
+#define SNDRV_SEQ_EVENT_USR7		97
+#define SNDRV_SEQ_EVENT_USR8		98
+#define SNDRV_SEQ_EVENT_USR9		99
+
+/** instrument layer
+ * variable length data can be passed directly to the driver
+ */
+#define SNDRV_SEQ_EVENT_INSTR_BEGIN	100	/* begin of instrument management */
+#define SNDRV_SEQ_EVENT_INSTR_END	101	/* end of instrument management */
+#define SNDRV_SEQ_EVENT_INSTR_INFO	102	/* instrument interface info */
+#define SNDRV_SEQ_EVENT_INSTR_INFO_RESULT 103	/* result */
+#define SNDRV_SEQ_EVENT_INSTR_FINFO	104	/* get format info */
+#define SNDRV_SEQ_EVENT_INSTR_FINFO_RESULT 105	/* get format info */
+#define SNDRV_SEQ_EVENT_INSTR_RESET	106	/* reset instrument memory */
+#define SNDRV_SEQ_EVENT_INSTR_STATUS	107	/* instrument interface status */
+#define SNDRV_SEQ_EVENT_INSTR_STATUS_RESULT 108	/* result */
+#define SNDRV_SEQ_EVENT_INSTR_PUT	109	/* put instrument to port */
+#define SNDRV_SEQ_EVENT_INSTR_GET	110	/* get instrument from port */
+#define SNDRV_SEQ_EVENT_INSTR_GET_RESULT 111	/* result */
+#define SNDRV_SEQ_EVENT_INSTR_FREE	112	/* free instrument(s) */
+#define SNDRV_SEQ_EVENT_INSTR_LIST	113	/* instrument list */
+#define SNDRV_SEQ_EVENT_INSTR_LIST_RESULT 114	/* result */
+#define SNDRV_SEQ_EVENT_INSTR_CLUSTER	115	/* cluster parameters */
+#define SNDRV_SEQ_EVENT_INSTR_CLUSTER_GET 116	/* get cluster parameters */
+#define SNDRV_SEQ_EVENT_INSTR_CLUSTER_RESULT 117 /* result */
+#define SNDRV_SEQ_EVENT_INSTR_CHANGE	118	/* instrument change */
+/* 119-129: reserved */
+
+/* 130-139: variable length events
+ * event data type = snd_seq_ev_ext
+ * (SNDRV_SEQ_EVENT_LENGTH_VARIABLE must be set)
+ */
+#define SNDRV_SEQ_EVENT_SYSEX		130	/* system exclusive data (variable length) */
+#define SNDRV_SEQ_EVENT_BOUNCE		131	/* error event */
+/* 132-134: reserved */
+#define SNDRV_SEQ_EVENT_USR_VAR0	135
+#define SNDRV_SEQ_EVENT_USR_VAR1	136
+#define SNDRV_SEQ_EVENT_USR_VAR2	137
+#define SNDRV_SEQ_EVENT_USR_VAR3	138
+#define SNDRV_SEQ_EVENT_USR_VAR4	139
+
+/* 150-151: kernel events with quote - DO NOT use in user clients */
+#define SNDRV_SEQ_EVENT_KERNEL_ERROR	150
+#define SNDRV_SEQ_EVENT_KERNEL_QUOTE	151	/* obsolete */
+
+/* 152-191: reserved */
+
+/* 192-254: hardware specific events */
+
+/* 255: special event */
+#define SNDRV_SEQ_EVENT_NONE		255
+
+
+typedef unsigned char snd_seq_event_type_t;
+
+/** event address */
+struct snd_seq_addr {
+	unsigned char client;	/**< Client number:         0..255, 255 = broadcast to all clients */
+	unsigned char port;	/**< Port within client:    0..255, 255 = broadcast to all ports */
+};
+
+/** port connection */
+struct snd_seq_connect {
+	struct snd_seq_addr sender;
+	struct snd_seq_addr dest;
+};
+
+
+#define SNDRV_SEQ_ADDRESS_UNKNOWN	253	/* unknown source */
+#define SNDRV_SEQ_ADDRESS_SUBSCRIBERS	254	/* send event to all subscribed ports */
+#define SNDRV_SEQ_ADDRESS_BROADCAST	255	/* send event to all queues/clients/ports/channels */
+#define SNDRV_SEQ_QUEUE_DIRECT		253	/* direct dispatch */
+
+	/* event mode flag - NOTE: only 8 bits available! */
+#define SNDRV_SEQ_TIME_STAMP_TICK	(0<<0) /* timestamp in clock ticks */
+#define SNDRV_SEQ_TIME_STAMP_REAL	(1<<0) /* timestamp in real time */
+#define SNDRV_SEQ_TIME_STAMP_MASK	(1<<0)
+
+#define SNDRV_SEQ_TIME_MODE_ABS		(0<<1)	/* absolute timestamp */
+#define SNDRV_SEQ_TIME_MODE_REL		(1<<1)	/* relative to current time */
+#define SNDRV_SEQ_TIME_MODE_MASK	(1<<1)
+
+#define SNDRV_SEQ_EVENT_LENGTH_FIXED	(0<<2)	/* fixed event size */
+#define SNDRV_SEQ_EVENT_LENGTH_VARIABLE	(1<<2)	/* variable event size */
+#define SNDRV_SEQ_EVENT_LENGTH_VARUSR	(2<<2)	/* variable event size - user memory space */
+#define SNDRV_SEQ_EVENT_LENGTH_MASK	(3<<2)
+
+#define SNDRV_SEQ_PRIORITY_NORMAL	(0<<4)	/* normal priority */
+#define SNDRV_SEQ_PRIORITY_HIGH		(1<<4)	/* event should be processed before others */
+#define SNDRV_SEQ_PRIORITY_MASK		(1<<4)
+
+
+	/* note event */
+struct snd_seq_ev_note {
+	unsigned char channel;
+	unsigned char note;
+	unsigned char velocity;
+	unsigned char off_velocity;	/* only for SNDRV_SEQ_EVENT_NOTE */
+	unsigned int duration;		/* only for SNDRV_SEQ_EVENT_NOTE */
+};
+
+	/* controller event */
+struct snd_seq_ev_ctrl {
+	unsigned char channel;
+	unsigned char unused1, unused2, unused3;	/* pad */
+	unsigned int param;
+	signed int value;
+};
+
+	/* generic set of bytes (12x8 bit) */
+struct snd_seq_ev_raw8 {
+	unsigned char d[12];	/* 8 bit value */
+};
+
+	/* generic set of integers (3x32 bit) */
+struct snd_seq_ev_raw32 {
+	unsigned int d[3];	/* 32 bit value */
+};
+
+/* external stored data */
+#pragma pack(1)
+struct snd_seq_ev_ext {
+	unsigned int len;	/* length of data */
+	void *ptr;		/* pointer to data (note: maybe 64-bit) */
+};
+#pragma pack()
+
+/* Instrument cluster type */
+typedef unsigned int snd_seq_instr_cluster_t;
+
+/* Instrument type */
+struct snd_seq_instr {
+	snd_seq_instr_cluster_t cluster;
+	unsigned int std;		/* the upper byte means a private instrument (owner - client #) */
+	unsigned short bank;
+	unsigned short prg;
+};
+
+	/* sample number */
+struct snd_seq_ev_sample {
+	unsigned int std;
+	unsigned short bank;
+	unsigned short prg;
+};
+
+	/* sample cluster */
+struct snd_seq_ev_cluster {
+	snd_seq_instr_cluster_t cluster;
+};
+
+	/* sample position */
+typedef unsigned int snd_seq_position_t; /* playback position (in samples) * 16 */
+
+	/* sample stop mode */
+enum {
+	SAMPLE_STOP_IMMEDIATELY = 0,	/* terminate playing immediately */
+	SAMPLE_STOP_VENVELOPE = 1,	/* finish volume envelope */
+	SAMPLE_STOP_LOOP = 2		/* terminate loop and finish wave */
+};
+
+	/* sample frequency */
+typedef int snd_seq_frequency_t; /* playback frequency in HZ * 16 */
+
+	/* sample volume control; if any value is set to -1 == do not change */
+struct snd_seq_ev_volume {
+	signed short volume;	/* range: 0-16383 */
+	signed short lr;	/* left-right balance; range: 0-16383 */
+	signed short fr;	/* front-rear balance; range: 0-16383 */
+	signed short du;	/* down-up balance; range: 0-16383 */
+};
+
+	/* simple loop redefinition */
+struct snd_seq_ev_loop {
+	unsigned int start;	/* loop start (in samples) * 16 */
+	unsigned int end;	/* loop end (in samples) * 16 */
+};
+
+struct snd_seq_ev_sample_control {
+	unsigned char channel;
+	unsigned char unused1, unused2, unused3;	/* pad */
+	union {
+		struct snd_seq_ev_sample sample;
+		struct snd_seq_ev_cluster cluster;
+		snd_seq_position_t position;
+		int stop_mode;
+		snd_seq_frequency_t frequency;
+		struct snd_seq_ev_volume volume;
+		struct snd_seq_ev_loop loop;
+		unsigned char raw8[8];
+	} param;
+};
+
+
+
+/* INSTR_BEGIN event */
+struct snd_seq_ev_instr_begin {
+	int timeout;		/* zero = forever, otherwise timeout in ms */
+};
+
+struct snd_seq_result {
+	int event;		/* processed event type */
+	int result;
+};
+
+
+struct snd_seq_real_time {
+	unsigned int tv_sec;	/* seconds */
+	unsigned int tv_nsec;	/* nanoseconds */
+};
+
+typedef unsigned int snd_seq_tick_time_t;	/* midi ticks */
+
+union snd_seq_timestamp {
+	snd_seq_tick_time_t tick;
+	struct snd_seq_real_time time;
+};
+
+struct snd_seq_queue_skew {
+	unsigned int value;
+	unsigned int base;
+};
+
+	/* queue timer control */
+struct snd_seq_ev_queue_control {
+	unsigned char queue;			/* affected queue */
+	unsigned char pad[3];			/* reserved */
+	union {
+		signed int value;		/* affected value (e.g. tempo) */
+		union snd_seq_timestamp time;	/* time */
+		unsigned int position;		/* sync position */
+		struct snd_seq_queue_skew skew;
+		unsigned int d32[2];
+		unsigned char d8[8];
+	} param;
+};
+
+/* quoted event - inside the kernel only */
+#pragma pack(1)
+struct snd_seq_ev_quote {
+	struct snd_seq_addr origin;		/* original sender */
+	unsigned short value;		/* optional data */
+	struct snd_seq_event *event;		/* quoted event */
+};
+#pragma pack()
+
+	/* sequencer event */
+struct snd_seq_event {
+	snd_seq_event_type_t type;	/* event type */
+	unsigned char flags;		/* event flags */
+	char tag;
+	
+	unsigned char queue;		/* schedule queue */
+	union snd_seq_timestamp time;	/* schedule time */
+
+
+	struct snd_seq_addr source;	/* source address */
+	struct snd_seq_addr dest;	/* destination address */
+
+	union {				/* event data... */
+		struct snd_seq_ev_note note;
+		struct snd_seq_ev_ctrl control;
+		struct snd_seq_ev_raw8 raw8;
+		struct snd_seq_ev_raw32 raw32;
+		struct snd_seq_ev_ext ext;
+		struct snd_seq_ev_queue_control queue;
+		union snd_seq_timestamp time;
+		struct snd_seq_addr addr;
+		struct snd_seq_connect connect;
+		struct snd_seq_result result;
+		struct snd_seq_ev_instr_begin instr_begin;
+		struct snd_seq_ev_sample_control sample;
+		struct snd_seq_ev_quote quote;
+	} data;
+};
+
+
+/*
+ * bounce event - stored as variable size data
+ */
+struct snd_seq_event_bounce {
+	int err;
+	struct snd_seq_event event;
+	/* external data follows here. */
+};
+
+#ifdef __KERNEL__
+
+/* helper macro */
+#define snd_seq_event_bounce_ext_data(ev) ((void*)((char *)(ev)->data.ext.ptr + sizeof(struct snd_seq_event_bounce)))
+
+/*
+ * type check macros
+ */
+/* result events: 0-4 */
+#define snd_seq_ev_is_result_type(ev)	((ev)->type < 5)
+/* channel specific events: 5-19 */
+#define snd_seq_ev_is_channel_type(ev)	((ev)->type >= 5 && (ev)->type < 20)
+/* note events: 5-9 */
+#define snd_seq_ev_is_note_type(ev)	((ev)->type >= 5 && (ev)->type < 10)
+/* control events: 10-19 */
+#define snd_seq_ev_is_control_type(ev)	((ev)->type >= 10 && (ev)->type < 20)
+/* queue control events: 30-39 */
+#define snd_seq_ev_is_queue_type(ev)	((ev)->type >= 30 && (ev)->type < 40)
+/* system status messages */
+#define snd_seq_ev_is_message_type(ev)	((ev)->type >= 60 && (ev)->type < 69)
+/* sample messages */
+#define snd_seq_ev_is_sample_type(ev)	((ev)->type >= 70 && (ev)->type < 79)
+/* user-defined messages */
+#define snd_seq_ev_is_user_type(ev)	((ev)->type >= 90 && (ev)->type < 99)
+/* fixed length events: 0-99 */
+#define snd_seq_ev_is_fixed_type(ev)	((ev)->type < 100)
+/* instrument layer events: 100-129 */
+#define snd_seq_ev_is_instr_type(ev)	((ev)->type >= 100 && (ev)->type < 130)
+/* variable length events: 130-139 */
+#define snd_seq_ev_is_variable_type(ev)	((ev)->type >= 130 && (ev)->type < 140)
+/* reserved for kernel */
+#define snd_seq_ev_is_reserved(ev)	((ev)->type >= 150)
+
+/* direct dispatched events */
+#define snd_seq_ev_is_direct(ev)	((ev)->queue == SNDRV_SEQ_QUEUE_DIRECT)
+
+/*
+ * macros to check event flags
+ */
+/* prior events */
+#define snd_seq_ev_is_prior(ev)		(((ev)->flags & SNDRV_SEQ_PRIORITY_MASK) == SNDRV_SEQ_PRIORITY_HIGH)
+
+/* event length type */
+#define snd_seq_ev_length_type(ev)	((ev)->flags & SNDRV_SEQ_EVENT_LENGTH_MASK)
+#define snd_seq_ev_is_fixed(ev)		(snd_seq_ev_length_type(ev) == SNDRV_SEQ_EVENT_LENGTH_FIXED)
+#define snd_seq_ev_is_variable(ev)	(snd_seq_ev_length_type(ev) == SNDRV_SEQ_EVENT_LENGTH_VARIABLE)
+#define snd_seq_ev_is_varusr(ev)	(snd_seq_ev_length_type(ev) == SNDRV_SEQ_EVENT_LENGTH_VARUSR)
+
+/* time-stamp type */
+#define snd_seq_ev_timestamp_type(ev)	((ev)->flags & SNDRV_SEQ_TIME_STAMP_MASK)
+#define snd_seq_ev_is_tick(ev)		(snd_seq_ev_timestamp_type(ev) == SNDRV_SEQ_TIME_STAMP_TICK)
+#define snd_seq_ev_is_real(ev)		(snd_seq_ev_timestamp_type(ev) == SNDRV_SEQ_TIME_STAMP_REAL)
+
+/* time-mode type */
+#define snd_seq_ev_timemode_type(ev)	((ev)->flags & SNDRV_SEQ_TIME_MODE_MASK)
+#define snd_seq_ev_is_abstime(ev)	(snd_seq_ev_timemode_type(ev) == SNDRV_SEQ_TIME_MODE_ABS)
+#define snd_seq_ev_is_reltime(ev)	(snd_seq_ev_timemode_type(ev) == SNDRV_SEQ_TIME_MODE_REL)
+
+/* queue sync port */
+#define snd_seq_queue_sync_port(q)	((q) + 16)
+
+#endif /* __KERNEL__ */
+
+	/* system information */
+struct snd_seq_system_info {
+	int queues;			/* maximum queues count */
+	int clients;			/* maximum clients count */
+	int ports;			/* maximum ports per client */
+	int channels;			/* maximum channels per port */
+	int cur_clients;		/* current clients */
+	int cur_queues;			/* current queues */
+	char reserved[24];
+};
+
+
+	/* system running information */
+struct snd_seq_running_info {
+	unsigned char client;		/* client id */
+	unsigned char big_endian;	/* 1 = big-endian */
+	unsigned char cpu_mode;		/* 4 = 32bit, 8 = 64bit */
+	unsigned char pad;		/* reserved */
+	unsigned char reserved[12];
+};
+
+
+	/* known client numbers */
+#define SNDRV_SEQ_CLIENT_SYSTEM		0
+#define SNDRV_SEQ_CLIENT_DUMMY		62	/* dummy ports */
+#define SNDRV_SEQ_CLIENT_OSS		63	/* oss sequencer emulator */
+
+
+	/* client types */
+typedef int __bitwise snd_seq_client_type_t;
+#define	NO_CLIENT	((__force snd_seq_client_type_t) 0)
+#define	USER_CLIENT	((__force snd_seq_client_type_t) 1)
+#define	KERNEL_CLIENT	((__force snd_seq_client_type_t) 2)
+                        
+	/* event filter flags */
+#define SNDRV_SEQ_FILTER_BROADCAST	(1<<0)	/* accept broadcast messages */
+#define SNDRV_SEQ_FILTER_MULTICAST	(1<<1)	/* accept multicast messages */
+#define SNDRV_SEQ_FILTER_BOUNCE		(1<<2)	/* accept bounce event in error */
+#define SNDRV_SEQ_FILTER_USE_EVENT	(1<<31)	/* use event filter */
+
+struct snd_seq_client_info {
+	int client;			/* client number to inquire */
+	snd_seq_client_type_t type;	/* client type */
+	char name[64];			/* client name */
+	unsigned int filter;		/* filter flags */
+	unsigned char multicast_filter[8]; /* multicast filter bitmap */
+	unsigned char event_filter[32];	/* event filter bitmap */
+	int num_ports;			/* RO: number of ports */
+	int event_lost;			/* number of lost events */
+	char reserved[64];		/* for future use */
+};
+
+
+/* client pool size */
+struct snd_seq_client_pool {
+	int client;			/* client number to inquire */
+	int output_pool;		/* outgoing (write) pool size */
+	int input_pool;			/* incoming (read) pool size */
+	int output_room;		/* minimum free pool size for select/blocking mode */
+	int output_free;		/* unused size */
+	int input_free;			/* unused size */
+	char reserved[64];
+};
+
+
+/* Remove events by specified criteria */
+
+#define SNDRV_SEQ_REMOVE_INPUT		(1<<0)	/* Flush input queues */
+#define SNDRV_SEQ_REMOVE_OUTPUT		(1<<1)	/* Flush output queues */
+#define SNDRV_SEQ_REMOVE_DEST		(1<<2)	/* Restrict by destination q:client:port */
+#define SNDRV_SEQ_REMOVE_DEST_CHANNEL	(1<<3)	/* Restrict by channel */
+#define SNDRV_SEQ_REMOVE_TIME_BEFORE	(1<<4)	/* Restrict to before time */
+#define SNDRV_SEQ_REMOVE_TIME_AFTER	(1<<5)	/* Restrict to time or after */
+#define SNDRV_SEQ_REMOVE_TIME_TICK	(1<<6)	/* Time is in ticks */
+#define SNDRV_SEQ_REMOVE_EVENT_TYPE	(1<<7)	/* Restrict to event type */
+#define SNDRV_SEQ_REMOVE_IGNORE_OFF 	(1<<8)	/* Do not flush off events */
+#define SNDRV_SEQ_REMOVE_TAG_MATCH 	(1<<9)	/* Restrict to events with given tag */
+
+struct snd_seq_remove_events {
+	unsigned int  remove_mode;	/* Flags that determine what gets removed */
+
+	union snd_seq_timestamp time;
+
+	unsigned char queue;	/* Queue for REMOVE_DEST */
+	struct snd_seq_addr dest;	/* Address for REMOVE_DEST */
+	unsigned char channel;	/* Channel for REMOVE_DEST */
+
+	int  type;	/* For REMOVE_EVENT_TYPE */
+	char  tag;	/* Tag for REMOVE_TAG */
+
+	int  reserved[10];	/* To allow for future binary compatibility */
+
+};
+
+
+	/* known port numbers */
+#define SNDRV_SEQ_PORT_SYSTEM_TIMER	0
+#define SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE	1
+
+	/* port capabilities (32 bits) */
+#define SNDRV_SEQ_PORT_CAP_READ		(1<<0)	/* readable from this port */
+#define SNDRV_SEQ_PORT_CAP_WRITE	(1<<1)	/* writable to this port */
+
+#define SNDRV_SEQ_PORT_CAP_SYNC_READ	(1<<2)
+#define SNDRV_SEQ_PORT_CAP_SYNC_WRITE	(1<<3)
+
+#define SNDRV_SEQ_PORT_CAP_DUPLEX	(1<<4)
+
+#define SNDRV_SEQ_PORT_CAP_SUBS_READ	(1<<5)	/* allow read subscription */
+#define SNDRV_SEQ_PORT_CAP_SUBS_WRITE	(1<<6)	/* allow write subscription */
+#define SNDRV_SEQ_PORT_CAP_NO_EXPORT	(1<<7)	/* routing not allowed */
+
+	/* port type */
+#define SNDRV_SEQ_PORT_TYPE_SPECIFIC	(1<<0)	/* hardware specific */
+#define SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC (1<<1)	/* generic MIDI device */
+#define SNDRV_SEQ_PORT_TYPE_MIDI_GM	(1<<2)	/* General MIDI compatible device */
+#define SNDRV_SEQ_PORT_TYPE_MIDI_GS	(1<<3)	/* GS compatible device */
+#define SNDRV_SEQ_PORT_TYPE_MIDI_XG	(1<<4)	/* XG compatible device */
+#define SNDRV_SEQ_PORT_TYPE_MIDI_MT32	(1<<5)	/* MT-32 compatible device */
+#define SNDRV_SEQ_PORT_TYPE_MIDI_GM2	(1<<6)	/* General MIDI 2 compatible device */
+
+/* other standards...*/
+#define SNDRV_SEQ_PORT_TYPE_SYNTH	(1<<10)	/* Synth device (no MIDI compatible - direct wavetable) */
+#define SNDRV_SEQ_PORT_TYPE_DIRECT_SAMPLE (1<<11)	/* Sampling device (support sample download) */
+#define SNDRV_SEQ_PORT_TYPE_SAMPLE	(1<<12)	/* Sampling device (sample can be downloaded at any time) */
+/*...*/
+#define SNDRV_SEQ_PORT_TYPE_APPLICATION	(1<<20)	/* application (sequencer/editor) */
+
+/* misc. conditioning flags */
+#define SNDRV_SEQ_PORT_FLG_GIVEN_PORT	(1<<0)
+#define SNDRV_SEQ_PORT_FLG_TIMESTAMP	(1<<1)
+#define SNDRV_SEQ_PORT_FLG_TIME_REAL	(1<<2)
+
+struct snd_seq_port_info {
+	struct snd_seq_addr addr;	/* client/port numbers */
+	char name[64];			/* port name */
+
+	unsigned int capability;	/* port capability bits */
+	unsigned int type;		/* port type bits */
+	int midi_channels;		/* channels per MIDI port */
+	int midi_voices;		/* voices per MIDI port */
+	int synth_voices;		/* voices per SYNTH port */
+
+	int read_use;			/* R/O: subscribers for output (from this port) */
+	int write_use;			/* R/O: subscribers for input (to this port) */
+
+	void *kernel;			/* reserved for kernel use (must be NULL) */
+	unsigned int flags;		/* misc. conditioning */
+	unsigned char time_queue;	/* queue # for timestamping */
+	char reserved[59];		/* for future use */
+};
+
+
+/* queue flags */
+#define SNDRV_SEQ_QUEUE_FLG_SYNC	(1<<0)	/* sync enabled */
+
+/* queue information */
+struct snd_seq_queue_info {
+	int queue;		/* queue id */
+
+	/*
+	 *  security settings, only owner of this queue can start/stop timer
+	 *  etc. if the queue is locked for other clients
+	 */
+	int owner;		/* client id for owner of the queue */
+	unsigned locked:1;	/* timing queue locked for other queues */
+	char name[64];		/* name of this queue */
+	unsigned int flags;	/* flags */
+	char reserved[60];	/* for future use */
+
+};
+
+/* queue info/status */
+struct snd_seq_queue_status {
+	int queue;			/* queue id */
+	int events;			/* read-only - queue size */
+	snd_seq_tick_time_t tick;	/* current tick */
+	struct snd_seq_real_time time;	/* current time */
+	int running;			/* running state of queue */
+	int flags;			/* various flags */
+	char reserved[64];		/* for the future */
+};
+
+
+/* queue tempo */
+struct snd_seq_queue_tempo {
+	int queue;			/* sequencer queue */
+	unsigned int tempo;		/* current tempo, us/tick */
+	int ppq;			/* time resolution, ticks/quarter */
+	unsigned int skew_value;	/* queue skew */
+	unsigned int skew_base;		/* queue skew base */
+	char reserved[24];		/* for the future */
+};
+
+
+/* sequencer timer sources */
+#define SNDRV_SEQ_TIMER_ALSA		0	/* ALSA timer */
+#define SNDRV_SEQ_TIMER_MIDI_CLOCK	1	/* Midi Clock (CLOCK event) */
+#define SNDRV_SEQ_TIMER_MIDI_TICK	2	/* Midi Timer Tick (TICK event) */
+
+/* queue timer info */
+struct snd_seq_queue_timer {
+	int queue;			/* sequencer queue */
+	int type;			/* source timer type */
+	union {
+		struct {
+			struct snd_timer_id id;	/* ALSA's timer ID */
+			unsigned int resolution;	/* resolution in Hz */
+		} alsa;
+	} u;
+	char reserved[64];		/* for the future use */
+};
+
+
+struct snd_seq_queue_client {
+	int queue;		/* sequencer queue */
+	int client;		/* sequencer client */
+	int used;		/* queue is used with this client
+				   (must be set for accepting events) */
+	/* per client watermarks */
+	char reserved[64];	/* for future use */
+};
+
+
+#define SNDRV_SEQ_PORT_SUBS_EXCLUSIVE	(1<<0)	/* exclusive connection */
+#define SNDRV_SEQ_PORT_SUBS_TIMESTAMP	(1<<1)
+#define SNDRV_SEQ_PORT_SUBS_TIME_REAL	(1<<2)
+
+struct snd_seq_port_subscribe {
+	struct snd_seq_addr sender;	/* sender address */
+	struct snd_seq_addr dest;	/* destination address */
+	unsigned int voices;		/* number of voices to be allocated (0 = don't care) */
+	unsigned int flags;		/* modes */
+	unsigned char queue;		/* input time-stamp queue (optional) */
+	unsigned char pad[3];		/* reserved */
+	char reserved[64];
+};
+
+/* type of query subscription */
+#define SNDRV_SEQ_QUERY_SUBS_READ	0
+#define SNDRV_SEQ_QUERY_SUBS_WRITE	1
+
+struct snd_seq_query_subs {
+	struct snd_seq_addr root;	/* client/port id to be searched */
+	int type;		/* READ or WRITE */
+	int index;		/* 0..N-1 */
+	int num_subs;		/* R/O: number of subscriptions on this port */
+	struct snd_seq_addr addr;	/* R/O: result */
+	unsigned char queue;	/* R/O: result */
+	unsigned int flags;	/* R/O: result */
+	char reserved[64];	/* for future use */
+};
+
+
+/*
+ *  Instrument abstraction layer
+ *     - based on events
+ */
+
+/* instrument types */
+#define SNDRV_SEQ_INSTR_ATYPE_DATA	0	/* instrument data */
+#define SNDRV_SEQ_INSTR_ATYPE_ALIAS	1	/* instrument alias */
+
+/* instrument ASCII identifiers */
+#define SNDRV_SEQ_INSTR_ID_DLS1		"DLS1"
+#define SNDRV_SEQ_INSTR_ID_DLS2		"DLS2"
+#define SNDRV_SEQ_INSTR_ID_SIMPLE	"Simple Wave"
+#define SNDRV_SEQ_INSTR_ID_SOUNDFONT	"SoundFont"
+#define SNDRV_SEQ_INSTR_ID_GUS_PATCH	"GUS Patch"
+#define SNDRV_SEQ_INSTR_ID_INTERWAVE	"InterWave FFFF"
+#define SNDRV_SEQ_INSTR_ID_OPL2_3	"OPL2/3 FM"
+#define SNDRV_SEQ_INSTR_ID_OPL4		"OPL4"
+
+/* instrument types */
+#define SNDRV_SEQ_INSTR_TYPE0_DLS1	(1<<0)	/* MIDI DLS v1 */
+#define SNDRV_SEQ_INSTR_TYPE0_DLS2	(1<<1)	/* MIDI DLS v2 */
+#define SNDRV_SEQ_INSTR_TYPE1_SIMPLE	(1<<0)	/* Simple Wave */
+#define SNDRV_SEQ_INSTR_TYPE1_SOUNDFONT	(1<<1)	/* EMU SoundFont */
+#define SNDRV_SEQ_INSTR_TYPE1_GUS_PATCH	(1<<2)	/* Gravis UltraSound Patch */
+#define SNDRV_SEQ_INSTR_TYPE1_INTERWAVE	(1<<3)	/* InterWave FFFF */
+#define SNDRV_SEQ_INSTR_TYPE2_OPL2_3	(1<<0)	/* Yamaha OPL2/3 FM */
+#define SNDRV_SEQ_INSTR_TYPE2_OPL4	(1<<1)	/* Yamaha OPL4 */
+
+/* put commands */
+#define SNDRV_SEQ_INSTR_PUT_CMD_CREATE	0
+#define SNDRV_SEQ_INSTR_PUT_CMD_REPLACE	1
+#define SNDRV_SEQ_INSTR_PUT_CMD_MODIFY	2
+#define SNDRV_SEQ_INSTR_PUT_CMD_ADD	3
+#define SNDRV_SEQ_INSTR_PUT_CMD_REMOVE	4
+
+/* get commands */
+#define SNDRV_SEQ_INSTR_GET_CMD_FULL	0
+#define SNDRV_SEQ_INSTR_GET_CMD_PARTIAL	1
+
+/* query flags */
+#define SNDRV_SEQ_INSTR_QUERY_FOLLOW_ALIAS (1<<0)
+
+/* free commands */
+#define SNDRV_SEQ_INSTR_FREE_CMD_ALL		0
+#define SNDRV_SEQ_INSTR_FREE_CMD_PRIVATE	1
+#define SNDRV_SEQ_INSTR_FREE_CMD_CLUSTER	2
+#define SNDRV_SEQ_INSTR_FREE_CMD_SINGLE		3
+
+/* size of ROM/RAM */
+typedef unsigned int snd_seq_instr_size_t;
+
+/* INSTR_INFO */
+
+struct snd_seq_instr_info {
+	int result;			/* operation result */
+	unsigned int formats[8];	/* bitmap of supported formats */
+	int ram_count;			/* count of RAM banks */
+	snd_seq_instr_size_t ram_sizes[16]; /* size of RAM banks */
+	int rom_count;			/* count of ROM banks */
+	snd_seq_instr_size_t rom_sizes[8]; /* size of ROM banks */
+	char reserved[128];
+};
+
+/* INSTR_STATUS */
+
+struct snd_seq_instr_status {
+	int result;			/* operation result */
+	snd_seq_instr_size_t free_ram[16]; /* free RAM in banks */
+	int instrument_count;		/* count of downloaded instruments */
+	char reserved[128];
+};
+
+/* INSTR_FORMAT_INFO */
+
+struct snd_seq_instr_format_info {
+	char format[16];		/* format identifier - SNDRV_SEQ_INSTR_ID_* */	
+	unsigned int len;		/* max data length (without this structure) */
+};
+
+struct snd_seq_instr_format_info_result {
+	int result;			/* operation result */
+	char format[16];		/* format identifier */
+	unsigned int len;		/* filled data length (without this structure) */
+};
+
+/* instrument data */
+struct snd_seq_instr_data {
+	char name[32];			/* instrument name */
+	char reserved[16];		/* for the future use */
+	int type;			/* instrument type */
+	union {
+		char format[16];	/* format identifier */
+		struct snd_seq_instr alias;
+	} data;
+};
+
+/* INSTR_PUT/GET, data are stored in one block (extended), header + data */
+
+struct snd_seq_instr_header {
+	union {
+		struct snd_seq_instr instr;
+		snd_seq_instr_cluster_t cluster;
+	} id;				/* instrument identifier */
+	unsigned int cmd;		/* get/put/free command */
+	unsigned int flags;		/* query flags (only for get) */
+	unsigned int len;		/* real instrument data length (without header) */
+	int result;			/* operation result */
+	char reserved[16];		/* for the future */
+	struct snd_seq_instr_data data; /* instrument data (for put/get result) */
+};
+
+/* INSTR_CLUSTER_SET */
+
+struct snd_seq_instr_cluster_set {
+	snd_seq_instr_cluster_t cluster; /* cluster identifier */
+	char name[32];			/* cluster name */
+	int priority;			/* cluster priority */
+	char reserved[64];		/* for the future use */
+};
+
+/* INSTR_CLUSTER_GET */
+
+struct snd_seq_instr_cluster_get {
+	snd_seq_instr_cluster_t cluster; /* cluster identifier */
+	char name[32];			/* cluster name */
+	int priority;			/* cluster priority */
+	char reserved[64];		/* for the future use */
+};
+
+/*
+ *  IOCTL commands
+ */
+
+#define SNDRV_SEQ_IOCTL_PVERSION	_IOR ('S', 0x00, int)
+#define SNDRV_SEQ_IOCTL_CLIENT_ID	_IOR ('S', 0x01, int)
+#define SNDRV_SEQ_IOCTL_SYSTEM_INFO	_IOWR('S', 0x02, struct snd_seq_system_info)
+#define SNDRV_SEQ_IOCTL_RUNNING_MODE	_IOWR('S', 0x03, struct snd_seq_running_info)
+
+#define SNDRV_SEQ_IOCTL_GET_CLIENT_INFO	_IOWR('S', 0x10, struct snd_seq_client_info)
+#define SNDRV_SEQ_IOCTL_SET_CLIENT_INFO	_IOW ('S', 0x11, struct snd_seq_client_info)
+
+#define SNDRV_SEQ_IOCTL_CREATE_PORT	_IOWR('S', 0x20, struct snd_seq_port_info)
+#define SNDRV_SEQ_IOCTL_DELETE_PORT	_IOW ('S', 0x21, struct snd_seq_port_info)
+#define SNDRV_SEQ_IOCTL_GET_PORT_INFO	_IOWR('S', 0x22, struct snd_seq_port_info)
+#define SNDRV_SEQ_IOCTL_SET_PORT_INFO	_IOW ('S', 0x23, struct snd_seq_port_info)
+
+#define SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT	_IOW ('S', 0x30, struct snd_seq_port_subscribe)
+#define SNDRV_SEQ_IOCTL_UNSUBSCRIBE_PORT _IOW ('S', 0x31, struct snd_seq_port_subscribe)
+
+#define SNDRV_SEQ_IOCTL_CREATE_QUEUE	_IOWR('S', 0x32, struct snd_seq_queue_info)
+#define SNDRV_SEQ_IOCTL_DELETE_QUEUE	_IOW ('S', 0x33, struct snd_seq_queue_info)
+#define SNDRV_SEQ_IOCTL_GET_QUEUE_INFO	_IOWR('S', 0x34, struct snd_seq_queue_info)
+#define SNDRV_SEQ_IOCTL_SET_QUEUE_INFO	_IOWR('S', 0x35, struct snd_seq_queue_info)
+#define SNDRV_SEQ_IOCTL_GET_NAMED_QUEUE	_IOWR('S', 0x36, struct snd_seq_queue_info)
+#define SNDRV_SEQ_IOCTL_GET_QUEUE_STATUS _IOWR('S', 0x40, struct snd_seq_queue_status)
+#define SNDRV_SEQ_IOCTL_GET_QUEUE_TEMPO	_IOWR('S', 0x41, struct snd_seq_queue_tempo)
+#define SNDRV_SEQ_IOCTL_SET_QUEUE_TEMPO	_IOW ('S', 0x42, struct snd_seq_queue_tempo)
+#define SNDRV_SEQ_IOCTL_GET_QUEUE_OWNER	_IOWR('S', 0x43, struct snd_seq_queue_owner)
+#define SNDRV_SEQ_IOCTL_SET_QUEUE_OWNER	_IOW ('S', 0x44, struct snd_seq_queue_owner)
+#define SNDRV_SEQ_IOCTL_GET_QUEUE_TIMER	_IOWR('S', 0x45, struct snd_seq_queue_timer)
+#define SNDRV_SEQ_IOCTL_SET_QUEUE_TIMER	_IOW ('S', 0x46, struct snd_seq_queue_timer)
+/* XXX
+#define SNDRV_SEQ_IOCTL_GET_QUEUE_SYNC	_IOWR('S', 0x53, struct snd_seq_queue_sync)
+#define SNDRV_SEQ_IOCTL_SET_QUEUE_SYNC	_IOW ('S', 0x54, struct snd_seq_queue_sync)
+*/
+#define SNDRV_SEQ_IOCTL_GET_QUEUE_CLIENT	_IOWR('S', 0x49, struct snd_seq_queue_client)
+#define SNDRV_SEQ_IOCTL_SET_QUEUE_CLIENT	_IOW ('S', 0x4a, struct snd_seq_queue_client)
+#define SNDRV_SEQ_IOCTL_GET_CLIENT_POOL	_IOWR('S', 0x4b, struct snd_seq_client_pool)
+#define SNDRV_SEQ_IOCTL_SET_CLIENT_POOL	_IOW ('S', 0x4c, struct snd_seq_client_pool)
+#define SNDRV_SEQ_IOCTL_REMOVE_EVENTS	_IOW ('S', 0x4e, struct snd_seq_remove_events)
+#define SNDRV_SEQ_IOCTL_QUERY_SUBS	_IOWR('S', 0x4f, struct snd_seq_query_subs)
+#define SNDRV_SEQ_IOCTL_GET_SUBSCRIPTION	_IOWR('S', 0x50, struct snd_seq_port_subscribe)
+#define SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT	_IOWR('S', 0x51, struct snd_seq_client_info)
+#define SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT	_IOWR('S', 0x52, struct snd_seq_port_info)
+
+#endif /* __SOUND_ASEQUENCER_H */
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/asound.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/asound.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/asound.h	(revision 91)
@@ -0,0 +1,958 @@
+/*
+ *  Advanced Linux Sound Architecture - ALSA - Driver
+ *  Copyright (c) 1994-2003 by Jaroslav Kysela <perex@suse.cz>,
+ *                             Abramo Bagnara <abramo@alsa-project.org>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#ifndef __SOUND_ASOUND_H
+#define __SOUND_ASOUND_H
+
+#define SNDRV_LITTLE_ENDIAN
+
+#ifndef __KERNEL__
+#include <sys/time.h>
+#include <sys/types.h>
+#endif
+
+struct iovec {
+        char    *iov_base;      /* Base address. */
+#ifdef __32BIT__
+        size_t   iov_len;       /* Length. */
+#else
+        long     iov_len;       /* Length. */
+#endif
+};
+
+#ifndef __force
+#define __force
+#endif
+
+#ifndef __bitwise
+#define __bitwise
+#endif
+
+#ifdef TARGET_OS2
+#define SNDRV_PCM_VOL_FRONT_LEFT        0
+#define SNDRV_PCM_VOL_FRONT_RIGHT       1
+#define SNDRV_PCM_VOL_REAR_LEFT         2
+#define SNDRV_PCM_VOL_REAR_RIGHT        3
+#define SNDRV_PCM_VOL_CENTER            4
+#define SNDRV_PCM_VOL_LFE               5
+
+#define SNDRV_PCM_VOL_MAX               100
+
+struct snd_pcm_volume {
+        int nrchannels;
+        int volume[6];
+};
+#endif
+
+/*
+ *  protocol version
+ */
+
+#define SNDRV_PROTOCOL_VERSION(major, minor, subminor) (((major)<<16)|((minor)<<8)|(subminor))
+#define SNDRV_PROTOCOL_MAJOR(version) (((version)>>16)&0xffff)
+#define SNDRV_PROTOCOL_MINOR(version) (((version)>>8)&0xff)
+#define SNDRV_PROTOCOL_MICRO(version) ((version)&0xff)
+#define SNDRV_PROTOCOL_INCOMPATIBLE(kversion, uversion) \
+	(SNDRV_PROTOCOL_MAJOR(kversion) != SNDRV_PROTOCOL_MAJOR(uversion) || \
+	 (SNDRV_PROTOCOL_MAJOR(kversion) == SNDRV_PROTOCOL_MAJOR(uversion) && \
+	   SNDRV_PROTOCOL_MINOR(kversion) != SNDRV_PROTOCOL_MINOR(uversion)))
+
+/****************************************************************************
+ *                                                                          *
+ *        Digital audio interface					    *
+ *                                                                          *
+ ****************************************************************************/
+
+struct snd_aes_iec958 {
+	unsigned char status[24];	/* AES/IEC958 channel status bits */
+	unsigned char subcode[147];	/* AES/IEC958 subcode bits */
+	unsigned char pad;		/* nothing */
+	unsigned char dig_subframe[4];	/* AES/IEC958 subframe bits */
+};
+
+/****************************************************************************
+ *                                                                          *
+ *      Section for driver hardware dependent interface - /dev/snd/hw?      *
+ *                                                                          *
+ ****************************************************************************/
+
+#define SNDRV_HWDEP_VERSION		SNDRV_PROTOCOL_VERSION(1, 0, 1)
+
+enum {
+	SNDRV_HWDEP_IFACE_OPL2 = 0,
+	SNDRV_HWDEP_IFACE_OPL3,
+	SNDRV_HWDEP_IFACE_OPL4,
+	SNDRV_HWDEP_IFACE_SB16CSP,	/* Creative Signal Processor */
+	SNDRV_HWDEP_IFACE_EMU10K1,	/* FX8010 processor in EMU10K1 chip */
+	SNDRV_HWDEP_IFACE_YSS225,	/* Yamaha FX processor */
+	SNDRV_HWDEP_IFACE_ICS2115,	/* Wavetable synth */
+	SNDRV_HWDEP_IFACE_SSCAPE,	/* Ensoniq SoundScape ISA card (MC68EC000) */
+	SNDRV_HWDEP_IFACE_VX,		/* Digigram VX cards */
+	SNDRV_HWDEP_IFACE_MIXART,	/* Digigram miXart cards */
+	SNDRV_HWDEP_IFACE_USX2Y,	/* Tascam US122, US224 & US428 usb */
+	SNDRV_HWDEP_IFACE_EMUX_WAVETABLE, /* EmuX wavetable */	
+	SNDRV_HWDEP_IFACE_BLUETOOTH,	/* Bluetooth audio */
+	SNDRV_HWDEP_IFACE_USX2Y_PCM,	/* Tascam US122, US224 & US428 rawusb pcm */
+	SNDRV_HWDEP_IFACE_PCXHR,	/* Digigram PCXHR */
+	SNDRV_HWDEP_IFACE_SB_RC,	/* SB Extigy/Audigy2NX remote control */
+
+	/* Don't forget to change the following: */
+	SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_SB_RC
+};
+
+struct snd_hwdep_info {
+	unsigned int device;		/* WR: device number */
+	int card;			/* R: card number */
+	unsigned char id[64];		/* ID (user selectable) */
+	unsigned char name[80];		/* hwdep name */
+	int iface;			/* hwdep interface */
+	unsigned char reserved[64];	/* reserved for future */
+};
+
+/* generic DSP loader */
+struct snd_hwdep_dsp_status {
+	unsigned int version;		/* R: driver-specific version */
+	unsigned char id[32];		/* R: driver-specific ID string */
+	unsigned int num_dsps;		/* R: number of DSP images to transfer */
+	unsigned int dsp_loaded;	/* R: bit flags indicating the loaded DSPs */
+	unsigned int chip_ready;	/* R: 1 = initialization finished */
+	unsigned char reserved[16];	/* reserved for future use */
+};
+
+struct snd_hwdep_dsp_image {
+	unsigned int index;		/* W: DSP index */
+	unsigned char name[64];		/* W: ID (e.g. file name) */
+	unsigned char __user *image;	/* W: binary image */
+	size_t length;			/* W: size of image in bytes */
+	unsigned long driver_data;	/* W: driver-specific data */
+};
+
+enum {
+	SNDRV_HWDEP_IOCTL_PVERSION = _IOR ('H', 0x00, int),
+	SNDRV_HWDEP_IOCTL_INFO = _IOR ('H', 0x01, struct snd_hwdep_info),
+	SNDRV_HWDEP_IOCTL_DSP_STATUS = _IOR('H', 0x02, struct snd_hwdep_dsp_status),
+	SNDRV_HWDEP_IOCTL_DSP_LOAD   = _IOW('H', 0x03, struct snd_hwdep_dsp_image)
+};
+
+/*****************************************************************************
+ *                                                                           *
+ *             Digital Audio (PCM) interface - /dev/snd/pcm??                *
+ *                                                                           *
+ *****************************************************************************/
+
+#define SNDRV_PCM_VERSION		SNDRV_PROTOCOL_VERSION(2, 0, 7)
+
+typedef unsigned long snd_pcm_uframes_t;
+typedef signed long snd_pcm_sframes_t;
+
+enum {
+	SNDRV_PCM_CLASS_GENERIC = 0,	/* standard mono or stereo device */
+	SNDRV_PCM_CLASS_MULTI,		/* multichannel device */
+	SNDRV_PCM_CLASS_MODEM,		/* software modem class */
+	SNDRV_PCM_CLASS_DIGITIZER,	/* digitizer class */
+	/* Don't forget to change the following: */
+	SNDRV_PCM_CLASS_LAST = SNDRV_PCM_CLASS_DIGITIZER,
+};
+
+enum {
+	SNDRV_PCM_SUBCLASS_GENERIC_MIX = 0, /* mono or stereo subdevices are mixed together */
+	SNDRV_PCM_SUBCLASS_MULTI_MIX,	/* multichannel subdevices are mixed together */
+	/* Don't forget to change the following: */
+	SNDRV_PCM_SUBCLASS_LAST = SNDRV_PCM_SUBCLASS_MULTI_MIX,
+};
+
+enum {
+	SNDRV_PCM_STREAM_PLAYBACK = 0,
+	SNDRV_PCM_STREAM_CAPTURE,
+	SNDRV_PCM_STREAM_LAST = SNDRV_PCM_STREAM_CAPTURE,
+};
+
+typedef int __bitwise snd_pcm_access_t;
+#define	SNDRV_PCM_ACCESS_MMAP_INTERLEAVED	((__force snd_pcm_access_t) 0) /* interleaved mmap */
+#define	SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED	((__force snd_pcm_access_t) 1) /* noninterleaved mmap */
+#define	SNDRV_PCM_ACCESS_MMAP_COMPLEX		((__force snd_pcm_access_t) 2) /* complex mmap */
+#define	SNDRV_PCM_ACCESS_RW_INTERLEAVED		((__force snd_pcm_access_t) 3) /* readi/writei */
+#define	SNDRV_PCM_ACCESS_RW_NONINTERLEAVED	((__force snd_pcm_access_t) 4) /* readn/writen */
+#define	SNDRV_PCM_ACCESS_LAST		SNDRV_PCM_ACCESS_RW_NONINTERLEAVED
+
+typedef int __bitwise snd_pcm_format_t;
+#define	SNDRV_PCM_FORMAT_S8	((__force snd_pcm_format_t) 0)
+#define	SNDRV_PCM_FORMAT_U8	((__force snd_pcm_format_t) 1)
+#define	SNDRV_PCM_FORMAT_S16_LE	((__force snd_pcm_format_t) 2)
+#define	SNDRV_PCM_FORMAT_S16_BE	((__force snd_pcm_format_t) 3)
+#define	SNDRV_PCM_FORMAT_U16_LE	((__force snd_pcm_format_t) 4)
+#define	SNDRV_PCM_FORMAT_U16_BE	((__force snd_pcm_format_t) 5)
+#define	SNDRV_PCM_FORMAT_S24_LE	((__force snd_pcm_format_t) 6) /* low three bytes */
+#define	SNDRV_PCM_FORMAT_S24_BE	((__force snd_pcm_format_t) 7) /* low three bytes */
+#define	SNDRV_PCM_FORMAT_U24_LE	((__force snd_pcm_format_t) 8) /* low three bytes */
+#define	SNDRV_PCM_FORMAT_U24_BE	((__force snd_pcm_format_t) 9) /* low three bytes */
+#define	SNDRV_PCM_FORMAT_S32_LE	((__force snd_pcm_format_t) 10)
+#define	SNDRV_PCM_FORMAT_S32_BE	((__force snd_pcm_format_t) 11)
+#define	SNDRV_PCM_FORMAT_U32_LE	((__force snd_pcm_format_t) 12)
+#define	SNDRV_PCM_FORMAT_U32_BE	((__force snd_pcm_format_t) 13)
+#define	SNDRV_PCM_FORMAT_FLOAT_LE	((__force snd_pcm_format_t) 14) /* 4-byte float, IEEE-754 32-bit, range -1.0 to 1.0 */
+#define	SNDRV_PCM_FORMAT_FLOAT_BE	((__force snd_pcm_format_t) 15) /* 4-byte float, IEEE-754 32-bit, range -1.0 to 1.0 */
+#define	SNDRV_PCM_FORMAT_FLOAT64_LE	((__force snd_pcm_format_t) 16) /* 8-byte float, IEEE-754 64-bit, range -1.0 to 1.0 */
+#define	SNDRV_PCM_FORMAT_FLOAT64_BE	((__force snd_pcm_format_t) 17) /* 8-byte float, IEEE-754 64-bit, range -1.0 to 1.0 */
+#define	SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE ((__force snd_pcm_format_t) 18) /* IEC-958 subframe, Little Endian */
+#define	SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE ((__force snd_pcm_format_t) 19) /* IEC-958 subframe, Big Endian */
+#define	SNDRV_PCM_FORMAT_MU_LAW		((__force snd_pcm_format_t) 20)
+#define	SNDRV_PCM_FORMAT_A_LAW		((__force snd_pcm_format_t) 21)
+#define	SNDRV_PCM_FORMAT_IMA_ADPCM	((__force snd_pcm_format_t) 22)
+#define	SNDRV_PCM_FORMAT_MPEG		((__force snd_pcm_format_t) 23)
+#define	SNDRV_PCM_FORMAT_GSM		((__force snd_pcm_format_t) 24)
+#define	SNDRV_PCM_FORMAT_SPECIAL	((__force snd_pcm_format_t) 31)
+#define	SNDRV_PCM_FORMAT_S24_3LE	((__force snd_pcm_format_t) 32)	/* in three bytes */
+#define	SNDRV_PCM_FORMAT_S24_3BE	((__force snd_pcm_format_t) 33)	/* in three bytes */
+#define	SNDRV_PCM_FORMAT_U24_3LE	((__force snd_pcm_format_t) 34)	/* in three bytes */
+#define	SNDRV_PCM_FORMAT_U24_3BE	((__force snd_pcm_format_t) 35)	/* in three bytes */
+#define	SNDRV_PCM_FORMAT_S20_3LE	((__force snd_pcm_format_t) 36)	/* in three bytes */
+#define	SNDRV_PCM_FORMAT_S20_3BE	((__force snd_pcm_format_t) 37)	/* in three bytes */
+#define	SNDRV_PCM_FORMAT_U20_3LE	((__force snd_pcm_format_t) 38)	/* in three bytes */
+#define	SNDRV_PCM_FORMAT_U20_3BE	((__force snd_pcm_format_t) 39)	/* in three bytes */
+#define	SNDRV_PCM_FORMAT_S18_3LE	((__force snd_pcm_format_t) 40)	/* in three bytes */
+#define	SNDRV_PCM_FORMAT_S18_3BE	((__force snd_pcm_format_t) 41)	/* in three bytes */
+#define	SNDRV_PCM_FORMAT_U18_3LE	((__force snd_pcm_format_t) 42)	/* in three bytes */
+#define	SNDRV_PCM_FORMAT_U18_3BE	((__force snd_pcm_format_t) 43)	/* in three bytes */
+#define	SNDRV_PCM_FORMAT_LAST		SNDRV_PCM_FORMAT_U18_3BE
+
+#ifdef SNDRV_LITTLE_ENDIAN
+#define	SNDRV_PCM_FORMAT_S16		SNDRV_PCM_FORMAT_S16_LE
+#define	SNDRV_PCM_FORMAT_U16		SNDRV_PCM_FORMAT_U16_LE
+#define	SNDRV_PCM_FORMAT_S24		SNDRV_PCM_FORMAT_S24_LE
+#define	SNDRV_PCM_FORMAT_U24		SNDRV_PCM_FORMAT_U24_LE
+#define	SNDRV_PCM_FORMAT_S32		SNDRV_PCM_FORMAT_S32_LE
+#define	SNDRV_PCM_FORMAT_U32		SNDRV_PCM_FORMAT_U32_LE
+#define	SNDRV_PCM_FORMAT_FLOAT		SNDRV_PCM_FORMAT_FLOAT_LE
+#define	SNDRV_PCM_FORMAT_FLOAT64	SNDRV_PCM_FORMAT_FLOAT64_LE
+#define	SNDRV_PCM_FORMAT_IEC958_SUBFRAME SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE
+#endif
+#ifdef SNDRV_BIG_ENDIAN
+#define	SNDRV_PCM_FORMAT_S16		SNDRV_PCM_FORMAT_S16_BE
+#define	SNDRV_PCM_FORMAT_U16		SNDRV_PCM_FORMAT_U16_BE
+#define	SNDRV_PCM_FORMAT_S24		SNDRV_PCM_FORMAT_S24_BE
+#define	SNDRV_PCM_FORMAT_U24		SNDRV_PCM_FORMAT_U24_BE
+#define	SNDRV_PCM_FORMAT_S32		SNDRV_PCM_FORMAT_S32_BE
+#define	SNDRV_PCM_FORMAT_U32		SNDRV_PCM_FORMAT_U32_BE
+#define	SNDRV_PCM_FORMAT_FLOAT		SNDRV_PCM_FORMAT_FLOAT_BE
+#define	SNDRV_PCM_FORMAT_FLOAT64	SNDRV_PCM_FORMAT_FLOAT64_BE
+#define	SNDRV_PCM_FORMAT_IEC958_SUBFRAME SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE
+#endif
+
+typedef int __bitwise snd_pcm_subformat_t;
+#define	SNDRV_PCM_SUBFORMAT_STD		((__force snd_pcm_subformat_t) 0)
+#define	SNDRV_PCM_SUBFORMAT_LAST	SNDRV_PCM_SUBFORMAT_STD
+
+#define SNDRV_PCM_INFO_MMAP		0x00000001	/* hardware supports mmap */
+#define SNDRV_PCM_INFO_MMAP_VALID	0x00000002	/* period data are valid during transfer */
+#define SNDRV_PCM_INFO_DOUBLE		0x00000004	/* Double buffering needed for PCM start/stop */
+#define SNDRV_PCM_INFO_BATCH		0x00000010	/* double buffering */
+#define SNDRV_PCM_INFO_INTERLEAVED	0x00000100	/* channels are interleaved */
+#define SNDRV_PCM_INFO_NONINTERLEAVED	0x00000200	/* channels are not interleaved */
+#define SNDRV_PCM_INFO_COMPLEX		0x00000400	/* complex frame organization (mmap only) */
+#define SNDRV_PCM_INFO_BLOCK_TRANSFER	0x00010000	/* hardware transfer block of samples */
+#define SNDRV_PCM_INFO_OVERRANGE	0x00020000	/* hardware supports ADC (capture) overrange detection */
+#define SNDRV_PCM_INFO_RESUME		0x00040000	/* hardware supports stream resume after suspend */
+#define SNDRV_PCM_INFO_PAUSE		0x00080000	/* pause ioctl is supported */
+#define SNDRV_PCM_INFO_HALF_DUPLEX	0x00100000	/* only half duplex */
+#define SNDRV_PCM_INFO_JOINT_DUPLEX	0x00200000	/* playback and capture stream are somewhat correlated */
+#define SNDRV_PCM_INFO_SYNC_START	0x00400000	/* pcm support some kind of sync go */
+
+typedef int __bitwise snd_pcm_state_t;
+#define	SNDRV_PCM_STATE_OPEN		((__force snd_pcm_state_t) 0) /* stream is open */
+#define	SNDRV_PCM_STATE_SETUP		((__force snd_pcm_state_t) 1) /* stream has a setup */
+#define	SNDRV_PCM_STATE_PREPARED	((__force snd_pcm_state_t) 2) /* stream is ready to start */
+#define	SNDRV_PCM_STATE_RUNNING		((__force snd_pcm_state_t) 3) /* stream is running */
+#define	SNDRV_PCM_STATE_XRUN		((__force snd_pcm_state_t) 4) /* stream reached an xrun */
+#define	SNDRV_PCM_STATE_DRAINING	((__force snd_pcm_state_t) 5) /* stream is draining */
+#define	SNDRV_PCM_STATE_PAUSED		((__force snd_pcm_state_t) 6) /* stream is paused */
+#define	SNDRV_PCM_STATE_SUSPENDED	((__force snd_pcm_state_t) 7) /* hardware is suspended */
+#define	SNDRV_PCM_STATE_DISCONNECTED	((__force snd_pcm_state_t) 8) /* hardware is disconnected */
+#define	SNDRV_PCM_STATE_LAST		SNDRV_PCM_STATE_DISCONNECTED
+
+enum {
+	SNDRV_PCM_MMAP_OFFSET_DATA = 0x00000000,
+	SNDRV_PCM_MMAP_OFFSET_STATUS = 0x80000000,
+	SNDRV_PCM_MMAP_OFFSET_CONTROL = 0x81000000,
+};
+
+union snd_pcm_sync_id {
+	unsigned char id[16];
+	unsigned short id16[8];
+	unsigned int id32[4];
+};
+
+struct snd_pcm_info {
+	unsigned int device;		/* RO/WR (control): device number */
+	unsigned int subdevice;		/* RO/WR (control): subdevice number */
+	int stream;			/* RO/WR (control): stream direction */
+	int card;			/* R: card number */
+	unsigned char id[64];		/* ID (user selectable) */
+	unsigned char name[80];		/* name of this device */
+	unsigned char subname[32];	/* subdevice name */
+	int dev_class;			/* SNDRV_PCM_CLASS_* */
+	int dev_subclass;		/* SNDRV_PCM_SUBCLASS_* */
+	unsigned int subdevices_count;
+	unsigned int subdevices_avail;
+	union snd_pcm_sync_id sync;	/* hardware synchronization ID */
+	unsigned char reserved[64];	/* reserved for future... */
+};
+
+typedef int __bitwise snd_pcm_hw_param_t;
+#define	SNDRV_PCM_HW_PARAM_ACCESS	((__force snd_pcm_hw_param_t) 0) /* Access type */
+#ifdef TARGET_OS2
+#define	SNDRV_PCM_HW_PARAM_RATE_MASK    ((__force snd_pcm_hw_param_t) 1) /* Format */
+#endif
+#define	SNDRV_PCM_HW_PARAM_FORMAT	((__force snd_pcm_hw_param_t) 2) /* Format */
+#define	SNDRV_PCM_HW_PARAM_SUBFORMAT	((__force snd_pcm_hw_param_t) 3) /* Subformat */
+#define	SNDRV_PCM_HW_PARAM_FIRST_MASK	SNDRV_PCM_HW_PARAM_ACCESS
+#define	SNDRV_PCM_HW_PARAM_LAST_MASK	SNDRV_PCM_HW_PARAM_SUBFORMAT
+
+#define	SNDRV_PCM_HW_PARAM_SAMPLE_BITS	((__force snd_pcm_hw_param_t) 8) /* Bits per sample */
+#define	SNDRV_PCM_HW_PARAM_FRAME_BITS	((__force snd_pcm_hw_param_t) 9) /* Bits per frame */
+#define	SNDRV_PCM_HW_PARAM_CHANNELS	((__force snd_pcm_hw_param_t) 10) /* Channels */
+#define	SNDRV_PCM_HW_PARAM_RATE		((__force snd_pcm_hw_param_t) 11) /* Approx rate */
+#define	SNDRV_PCM_HW_PARAM_PERIOD_TIME	((__force snd_pcm_hw_param_t) 12) /* Approx distance between interrupts in us */
+#define	SNDRV_PCM_HW_PARAM_PERIOD_SIZE	((__force snd_pcm_hw_param_t) 13) /* Approx frames between interrupts */
+#define	SNDRV_PCM_HW_PARAM_PERIOD_BYTES	((__force snd_pcm_hw_param_t) 14) /* Approx bytes between interrupts */
+#define	SNDRV_PCM_HW_PARAM_PERIODS	((__force snd_pcm_hw_param_t) 15) /* Approx interrupts per buffer */
+#define	SNDRV_PCM_HW_PARAM_BUFFER_TIME	((__force snd_pcm_hw_param_t) 16) /* Approx duration of buffer in us */
+#define	SNDRV_PCM_HW_PARAM_BUFFER_SIZE	((__force snd_pcm_hw_param_t) 17) /* Size of buffer in frames */
+#define	SNDRV_PCM_HW_PARAM_BUFFER_BYTES	((__force snd_pcm_hw_param_t) 18) /* Size of buffer in bytes */
+#define	SNDRV_PCM_HW_PARAM_TICK_TIME	((__force snd_pcm_hw_param_t) 19) /* Approx tick duration in us */
+#define	SNDRV_PCM_HW_PARAM_FIRST_INTERVAL	SNDRV_PCM_HW_PARAM_SAMPLE_BITS
+#define	SNDRV_PCM_HW_PARAM_LAST_INTERVAL	SNDRV_PCM_HW_PARAM_TICK_TIME
+
+#define SNDRV_PCM_HW_PARAMS_NORESAMPLE		(1<<0)	/* avoid rate resampling */
+
+struct snd_interval {
+	unsigned int min, max;
+	unsigned int openmin:1,
+		     openmax:1,
+		     integer:1,
+		     empty:1;
+};
+
+#define SNDRV_MASK_MAX	256
+
+struct snd_mask {
+	u_int32_t bits[(SNDRV_MASK_MAX+31)/32];
+};
+
+struct snd_pcm_hw_params {
+	unsigned int flags;
+	struct snd_mask masks[SNDRV_PCM_HW_PARAM_LAST_MASK - 
+			       SNDRV_PCM_HW_PARAM_FIRST_MASK + 1];
+	struct snd_mask mres[5];	/* reserved masks */
+	struct snd_interval intervals[SNDRV_PCM_HW_PARAM_LAST_INTERVAL -
+				        SNDRV_PCM_HW_PARAM_FIRST_INTERVAL + 1];
+	struct snd_interval ires[9];	/* reserved intervals */
+	unsigned int rmask;		/* W: requested masks */
+	unsigned int cmask;		/* R: changed masks */
+	unsigned int info;		/* R: Info flags for returned setup */
+	unsigned int msbits;		/* R: used most significant bits */
+	unsigned int rate_num;		/* R: rate numerator */
+	unsigned int rate_den;		/* R: rate denominator */
+	snd_pcm_uframes_t fifo_size;	/* R: chip FIFO size in frames */
+	unsigned char reserved[64];	/* reserved for future */
+};
+
+enum {
+	SNDRV_PCM_TSTAMP_NONE = 0,
+	SNDRV_PCM_TSTAMP_MMAP,
+	SNDRV_PCM_TSTAMP_LAST = SNDRV_PCM_TSTAMP_MMAP,
+};
+
+struct snd_pcm_sw_params {
+	int tstamp_mode;			/* timestamp mode */
+	unsigned int period_step;
+	unsigned int sleep_min;			/* min ticks to sleep */
+	snd_pcm_uframes_t avail_min;		/* min avail frames for wakeup */
+	snd_pcm_uframes_t xfer_align;		/* xfer size need to be a multiple */
+	snd_pcm_uframes_t start_threshold;	/* min hw_avail frames for automatic start */
+	snd_pcm_uframes_t stop_threshold;	/* min avail frames for automatic stop */
+	snd_pcm_uframes_t silence_threshold;	/* min distance from noise for silence filling */
+	snd_pcm_uframes_t silence_size;		/* silence block size */
+	snd_pcm_uframes_t boundary;		/* pointers wrap point */
+	unsigned char reserved[64];		/* reserved for future */
+};
+
+struct snd_pcm_channel_info {
+	unsigned int channel;
+	off_t offset;			/* mmap offset */
+	unsigned int first;		/* offset to first sample in bits */
+	unsigned int step;		/* samples distance in bits */
+};
+
+struct snd_pcm_status {
+	snd_pcm_state_t state;		/* stream state */
+	struct timespec trigger_tstamp;	/* time when stream was started/stopped/paused */
+	struct timespec tstamp;		/* reference timestamp */
+	snd_pcm_uframes_t appl_ptr;	/* appl ptr */
+	snd_pcm_uframes_t hw_ptr;	/* hw ptr */
+	snd_pcm_sframes_t delay;	/* current delay in frames */
+	snd_pcm_uframes_t avail;	/* number of frames available */
+	snd_pcm_uframes_t avail_max;	/* max frames available on hw since last status */
+	snd_pcm_uframes_t overrange;	/* count of ADC (capture) overrange detections from last status */
+	snd_pcm_state_t suspended_state; /* suspended stream state */
+	unsigned char reserved[60];	/* must be filled with zero */
+};
+
+struct snd_pcm_mmap_status {
+	snd_pcm_state_t state;		/* RO: state - SNDRV_PCM_STATE_XXXX */
+	int pad1;			/* Needed for 64 bit alignment */
+	snd_pcm_uframes_t hw_ptr;	/* RO: hw ptr (0...boundary-1) */
+	struct timespec tstamp;		/* Timestamp */
+	snd_pcm_state_t suspended_state; /* RO: suspended stream state */
+};
+
+struct snd_pcm_mmap_control {
+	snd_pcm_uframes_t appl_ptr;	/* RW: appl ptr (0...boundary-1) */
+	snd_pcm_uframes_t avail_min;	/* RW: min available frames for wakeup */
+};
+
+#define SNDRV_PCM_SYNC_PTR_HWSYNC	(1<<0)	/* execute hwsync */
+#define SNDRV_PCM_SYNC_PTR_APPL		(1<<1)	/* get appl_ptr from driver (r/w op) */
+#define SNDRV_PCM_SYNC_PTR_AVAIL_MIN	(1<<2)	/* get avail_min from driver */
+
+struct snd_pcm_sync_ptr {
+	unsigned int flags;
+	union {
+		struct snd_pcm_mmap_status status;
+		unsigned char reserved[64];
+	} s;
+	union {
+		struct snd_pcm_mmap_control control;
+		unsigned char reserved[64];
+	} c;
+};
+
+struct snd_xferi {
+	snd_pcm_sframes_t result;
+	void __user *buf;
+	snd_pcm_uframes_t frames;
+};
+
+struct snd_xfern {
+	snd_pcm_sframes_t result;
+	void __user * __user *bufs;
+	snd_pcm_uframes_t frames;
+};
+
+enum {
+	SNDRV_PCM_IOCTL_PVERSION = _IOR('A', 0x00, int),
+	SNDRV_PCM_IOCTL_INFO = _IOR('A', 0x01, struct snd_pcm_info),
+	SNDRV_PCM_IOCTL_TSTAMP = _IOW('A', 0x02, int),
+	SNDRV_PCM_IOCTL_HW_REFINE = _IOWR('A', 0x10, struct snd_pcm_hw_params),
+	SNDRV_PCM_IOCTL_HW_PARAMS = _IOWR('A', 0x11, struct snd_pcm_hw_params),
+	SNDRV_PCM_IOCTL_HW_FREE = _IO('A', 0x12),
+	SNDRV_PCM_IOCTL_SW_PARAMS = _IOWR('A', 0x13, struct snd_pcm_sw_params),
+	SNDRV_PCM_IOCTL_STATUS = _IOR('A', 0x20, struct snd_pcm_status),
+	SNDRV_PCM_IOCTL_DELAY = _IOR('A', 0x21, snd_pcm_sframes_t),
+	SNDRV_PCM_IOCTL_HWSYNC = _IO('A', 0x22),
+	SNDRV_PCM_IOCTL_SYNC_PTR = _IOWR('A', 0x23, struct snd_pcm_sync_ptr),
+	SNDRV_PCM_IOCTL_CHANNEL_INFO = _IOR('A', 0x32, struct snd_pcm_channel_info),
+	SNDRV_PCM_IOCTL_PREPARE = _IO('A', 0x40),
+	SNDRV_PCM_IOCTL_RESET = _IO('A', 0x41),
+	SNDRV_PCM_IOCTL_START = _IO('A', 0x42),
+	SNDRV_PCM_IOCTL_DROP = _IO('A', 0x43),
+	SNDRV_PCM_IOCTL_DRAIN = _IO('A', 0x44),
+	SNDRV_PCM_IOCTL_PAUSE = _IOW('A', 0x45, int),
+	SNDRV_PCM_IOCTL_REWIND = _IOW('A', 0x46, snd_pcm_uframes_t),
+	SNDRV_PCM_IOCTL_RESUME = _IO('A', 0x47),
+	SNDRV_PCM_IOCTL_XRUN = _IO('A', 0x48),
+	SNDRV_PCM_IOCTL_FORWARD = _IOW('A', 0x49, snd_pcm_uframes_t),
+	SNDRV_PCM_IOCTL_WRITEI_FRAMES = _IOW('A', 0x50, struct snd_xferi),
+	SNDRV_PCM_IOCTL_READI_FRAMES = _IOR('A', 0x51, struct snd_xferi),
+	SNDRV_PCM_IOCTL_WRITEN_FRAMES = _IOW('A', 0x52, struct snd_xfern),
+	SNDRV_PCM_IOCTL_READN_FRAMES = _IOR('A', 0x53, struct snd_xfern),
+	SNDRV_PCM_IOCTL_LINK = _IOW('A', 0x60, int),
+	SNDRV_PCM_IOCTL_UNLINK = _IO('A', 0x61),
+};
+
+/* Trick to make alsa-lib/acinclude.m4 happy */
+#define SNDRV_PCM_IOCTL_REWIND SNDRV_PCM_IOCTL_REWIND
+
+/*****************************************************************************
+ *                                                                           *
+ *                            MIDI v1.0 interface                            *
+ *                                                                           *
+ *****************************************************************************/
+
+/*
+ *  Raw MIDI section - /dev/snd/midi??
+ */
+
+#define SNDRV_RAWMIDI_VERSION		SNDRV_PROTOCOL_VERSION(2, 0, 0)
+
+enum {
+	SNDRV_RAWMIDI_STREAM_OUTPUT = 0,
+	SNDRV_RAWMIDI_STREAM_INPUT,
+	SNDRV_RAWMIDI_STREAM_LAST = SNDRV_RAWMIDI_STREAM_INPUT,
+};
+
+#define SNDRV_RAWMIDI_INFO_OUTPUT		0x00000001
+#define SNDRV_RAWMIDI_INFO_INPUT		0x00000002
+#define SNDRV_RAWMIDI_INFO_DUPLEX		0x00000004
+
+struct snd_rawmidi_info {
+	unsigned int device;		/* RO/WR (control): device number */
+	unsigned int subdevice;		/* RO/WR (control): subdevice number */
+	int stream;			/* WR: stream */
+	int card;			/* R: card number */
+	unsigned int flags;		/* SNDRV_RAWMIDI_INFO_XXXX */
+	unsigned char id[64];		/* ID (user selectable) */
+	unsigned char name[80];		/* name of device */
+	unsigned char subname[32];	/* name of active or selected subdevice */
+	unsigned int subdevices_count;
+	unsigned int subdevices_avail;
+	unsigned char reserved[64];	/* reserved for future use */
+};
+
+struct snd_rawmidi_params {
+	int stream;
+	size_t buffer_size;		/* queue size in bytes */
+	size_t avail_min;		/* minimum avail bytes for wakeup */
+	unsigned int no_active_sensing: 1; /* do not send active sensing byte in close() */
+	unsigned char reserved[16];	/* reserved for future use */
+};
+
+struct snd_rawmidi_status {
+	int stream;
+	struct timespec tstamp;		/* Timestamp */
+	size_t avail;			/* available bytes */
+	size_t xruns;			/* count of overruns since last status (in bytes) */
+	unsigned char reserved[16];	/* reserved for future use */
+};
+
+enum {
+	SNDRV_RAWMIDI_IOCTL_PVERSION = _IOR('W', 0x00, int),
+	SNDRV_RAWMIDI_IOCTL_INFO = _IOR('W', 0x01, struct snd_rawmidi_info),
+	SNDRV_RAWMIDI_IOCTL_PARAMS = _IOWR('W', 0x10, struct snd_rawmidi_params),
+	SNDRV_RAWMIDI_IOCTL_STATUS = _IOWR('W', 0x20, struct snd_rawmidi_status),
+	SNDRV_RAWMIDI_IOCTL_DROP = _IOW('W', 0x30, int),
+	SNDRV_RAWMIDI_IOCTL_DRAIN = _IOW('W', 0x31, int),
+};
+
+/*
+ *  Timer section - /dev/snd/timer
+ */
+
+#define SNDRV_TIMER_VERSION		SNDRV_PROTOCOL_VERSION(2, 0, 5)
+
+enum {
+	SNDRV_TIMER_CLASS_NONE = -1,
+	SNDRV_TIMER_CLASS_SLAVE = 0,
+	SNDRV_TIMER_CLASS_GLOBAL,
+	SNDRV_TIMER_CLASS_CARD,
+	SNDRV_TIMER_CLASS_PCM,
+	SNDRV_TIMER_CLASS_LAST = SNDRV_TIMER_CLASS_PCM,
+};
+
+/* slave timer classes */
+enum {
+	SNDRV_TIMER_SCLASS_NONE = 0,
+	SNDRV_TIMER_SCLASS_APPLICATION,
+	SNDRV_TIMER_SCLASS_SEQUENCER,		/* alias */
+	SNDRV_TIMER_SCLASS_OSS_SEQUENCER,	/* alias */
+	SNDRV_TIMER_SCLASS_LAST = SNDRV_TIMER_SCLASS_OSS_SEQUENCER,
+};
+
+/* global timers (device member) */
+#define SNDRV_TIMER_GLOBAL_SYSTEM	0
+#define SNDRV_TIMER_GLOBAL_RTC		1
+#define SNDRV_TIMER_GLOBAL_HPET		2
+
+/* info flags */
+#define SNDRV_TIMER_FLG_SLAVE		(1<<0)	/* cannot be controlled */
+
+struct snd_timer_id {
+	int dev_class;	
+	int dev_sclass;
+	int card;
+	int device;
+	int subdevice;
+};
+
+struct snd_timer_ginfo {
+	struct snd_timer_id tid;	/* requested timer ID */
+	unsigned int flags;		/* timer flags - SNDRV_TIMER_FLG_* */
+	int card;			/* card number */
+	unsigned char id[64];		/* timer identification */
+	unsigned char name[80];		/* timer name */
+	unsigned long reserved0;	/* reserved for future use */
+	unsigned long resolution;	/* average period resolution in ns */
+	unsigned long resolution_min;	/* minimal period resolution in ns */
+	unsigned long resolution_max;	/* maximal period resolution in ns */
+	unsigned int clients;		/* active timer clients */
+	unsigned char reserved[32];
+};
+
+struct snd_timer_gparams {
+	struct snd_timer_id tid;	/* requested timer ID */
+	unsigned long period_num;	/* requested precise period duration (in seconds) - numerator */
+	unsigned long period_den;	/* requested precise period duration (in seconds) - denominator */
+	unsigned char reserved[32];
+};
+
+struct snd_timer_gstatus {
+	struct snd_timer_id tid;	/* requested timer ID */
+	unsigned long resolution;	/* current period resolution in ns */
+	unsigned long resolution_num;	/* precise current period resolution (in seconds) - numerator */
+	unsigned long resolution_den;	/* precise current period resolution (in seconds) - denominator */
+	unsigned char reserved[32];
+};
+
+struct snd_timer_select {
+	struct snd_timer_id id;	/* bind to timer ID */
+	unsigned char reserved[32];	/* reserved */
+};
+
+struct snd_timer_info {
+	unsigned int flags;		/* timer flags - SNDRV_TIMER_FLG_* */
+	int card;			/* card number */
+	unsigned char id[64];		/* timer identificator */
+	unsigned char name[80];		/* timer name */
+	unsigned long reserved0;	/* reserved for future use */
+	unsigned long resolution;	/* average period resolution in ns */
+	unsigned char reserved[64];	/* reserved */
+};
+
+#define SNDRV_TIMER_PSFLG_AUTO		(1<<0)	/* auto start, otherwise one-shot */
+#define SNDRV_TIMER_PSFLG_EXCLUSIVE	(1<<1)	/* exclusive use, precise start/stop/pause/continue */
+#define SNDRV_TIMER_PSFLG_EARLY_EVENT	(1<<2)	/* write early event to the poll queue */
+
+struct snd_timer_params {
+	unsigned int flags;		/* flags - SNDRV_MIXER_PSFLG_* */
+	unsigned int ticks;		/* requested resolution in ticks */
+	unsigned int queue_size;	/* total size of queue (32-1024) */
+	unsigned int reserved0;		/* reserved, was: failure locations */
+	unsigned int filter;		/* event filter (bitmask of SNDRV_TIMER_EVENT_*) */
+	unsigned char reserved[60];	/* reserved */
+};
+
+struct snd_timer_status {
+	struct timespec tstamp;		/* Timestamp - last update */
+	unsigned int resolution;	/* current period resolution in ns */
+	unsigned int lost;		/* counter of master tick lost */
+	unsigned int overrun;		/* count of read queue overruns */
+	unsigned int queue;		/* used queue size */
+	unsigned char reserved[64];	/* reserved */
+};
+
+enum {
+	SNDRV_TIMER_IOCTL_PVERSION = _IOR('T', 0x00, int),
+	SNDRV_TIMER_IOCTL_NEXT_DEVICE = _IOWR('T', 0x01, struct snd_timer_id),
+	SNDRV_TIMER_IOCTL_TREAD = _IOW('T', 0x02, int),
+	SNDRV_TIMER_IOCTL_GINFO = _IOWR('T', 0x03, struct snd_timer_ginfo),
+	SNDRV_TIMER_IOCTL_GPARAMS = _IOW('T', 0x04, struct snd_timer_gparams),
+	SNDRV_TIMER_IOCTL_GSTATUS = _IOWR('T', 0x05, struct snd_timer_gstatus),
+	SNDRV_TIMER_IOCTL_SELECT = _IOW('T', 0x10, struct snd_timer_select),
+	SNDRV_TIMER_IOCTL_INFO = _IOR('T', 0x11, struct snd_timer_info),
+	SNDRV_TIMER_IOCTL_PARAMS = _IOW('T', 0x12, struct snd_timer_params),
+	SNDRV_TIMER_IOCTL_STATUS = _IOR('T', 0x14, struct snd_timer_status),
+	/* The following four ioctls are changed since 1.0.9 due to confliction */
+	SNDRV_TIMER_IOCTL_START = _IO('T', 0xa0),
+	SNDRV_TIMER_IOCTL_STOP = _IO('T', 0xa1),
+	SNDRV_TIMER_IOCTL_CONTINUE = _IO('T', 0xa2),
+	SNDRV_TIMER_IOCTL_PAUSE = _IO('T', 0xa3),
+};
+
+struct snd_timer_read {
+	unsigned int resolution;
+	unsigned int ticks;
+};
+
+enum {
+	SNDRV_TIMER_EVENT_RESOLUTION = 0,	/* val = resolution in ns */
+	SNDRV_TIMER_EVENT_TICK,			/* val = ticks */
+	SNDRV_TIMER_EVENT_START,		/* val = resolution in ns */
+	SNDRV_TIMER_EVENT_STOP,			/* val = 0 */
+	SNDRV_TIMER_EVENT_CONTINUE,		/* val = resolution in ns */
+	SNDRV_TIMER_EVENT_PAUSE,		/* val = 0 */
+	SNDRV_TIMER_EVENT_EARLY,		/* val = 0, early event */
+	SNDRV_TIMER_EVENT_SUSPEND,		/* val = 0 */
+	SNDRV_TIMER_EVENT_RESUME,		/* val = resolution in ns */
+	/* master timer events for slave timer instances */
+	SNDRV_TIMER_EVENT_MSTART = SNDRV_TIMER_EVENT_START + 10,
+	SNDRV_TIMER_EVENT_MSTOP = SNDRV_TIMER_EVENT_STOP + 10,
+	SNDRV_TIMER_EVENT_MCONTINUE = SNDRV_TIMER_EVENT_CONTINUE + 10,
+	SNDRV_TIMER_EVENT_MPAUSE = SNDRV_TIMER_EVENT_PAUSE + 10,
+	SNDRV_TIMER_EVENT_MSUSPEND = SNDRV_TIMER_EVENT_SUSPEND + 10,
+	SNDRV_TIMER_EVENT_MRESUME = SNDRV_TIMER_EVENT_RESUME + 10,
+};
+
+struct snd_timer_tread {
+	int event;
+	struct timespec tstamp;
+	unsigned int val;
+};
+
+/****************************************************************************
+ *                                                                          *
+ *        Section for driver control interface - /dev/snd/control?          *
+ *                                                                          *
+ ****************************************************************************/
+
+#define SNDRV_CTL_VERSION		SNDRV_PROTOCOL_VERSION(2, 0, 4)
+
+struct snd_ctl_card_info {
+	int card;			/* card number */
+	int pad;			/* reserved for future (was type) */
+	unsigned char id[16];		/* ID of card (user selectable) */
+	unsigned char driver[16];	/* Driver name */
+	unsigned char name[32];		/* Short name of soundcard */
+	unsigned char longname[80];	/* name + info text about soundcard */
+	unsigned char reserved_[16];	/* reserved for future (was ID of mixer) */
+	unsigned char mixername[80];	/* visual mixer identification */
+	unsigned char components[80];	/* card components / fine identification, delimited with one space (AC97 etc..) */
+	unsigned char reserved[48];	/* reserved for future */
+};
+
+typedef int __bitwise snd_ctl_elem_type_t;
+#define	SNDRV_CTL_ELEM_TYPE_NONE	((__force snd_ctl_elem_type_t) 0) /* invalid */
+#define	SNDRV_CTL_ELEM_TYPE_BOOLEAN	((__force snd_ctl_elem_type_t) 1) /* boolean type */
+#define	SNDRV_CTL_ELEM_TYPE_INTEGER	((__force snd_ctl_elem_type_t) 2) /* integer type */
+#define	SNDRV_CTL_ELEM_TYPE_ENUMERATED	((__force snd_ctl_elem_type_t) 3) /* enumerated type */
+#define	SNDRV_CTL_ELEM_TYPE_BYTES	((__force snd_ctl_elem_type_t) 4) /* byte array */
+#define	SNDRV_CTL_ELEM_TYPE_IEC958	((__force snd_ctl_elem_type_t) 5) /* IEC958 (S/PDIF) setup */
+#define	SNDRV_CTL_ELEM_TYPE_INTEGER64	((__force snd_ctl_elem_type_t) 6) /* 64-bit integer type */
+#define	SNDRV_CTL_ELEM_TYPE_LAST	SNDRV_CTL_ELEM_TYPE_INTEGER64
+
+typedef int __bitwise snd_ctl_elem_iface_t;
+#define	SNDRV_CTL_ELEM_IFACE_CARD	((__force snd_ctl_elem_iface_t) 0) /* global control */
+#define	SNDRV_CTL_ELEM_IFACE_HWDEP	((__force snd_ctl_elem_iface_t) 1) /* hardware dependent device */
+#define	SNDRV_CTL_ELEM_IFACE_MIXER	((__force snd_ctl_elem_iface_t) 2) /* virtual mixer device */
+#define	SNDRV_CTL_ELEM_IFACE_PCM	((__force snd_ctl_elem_iface_t) 3) /* PCM device */
+#define	SNDRV_CTL_ELEM_IFACE_RAWMIDI	((__force snd_ctl_elem_iface_t) 4) /* RawMidi device */
+#define	SNDRV_CTL_ELEM_IFACE_TIMER	((__force snd_ctl_elem_iface_t) 5) /* timer device */
+#define	SNDRV_CTL_ELEM_IFACE_SEQUENCER	((__force snd_ctl_elem_iface_t) 6) /* sequencer client */
+#define	SNDRV_CTL_ELEM_IFACE_LAST	SNDRV_CTL_ELEM_IFACE_SEQUENCER
+
+#define SNDRV_CTL_ELEM_ACCESS_READ		(1<<0)
+#define SNDRV_CTL_ELEM_ACCESS_WRITE		(1<<1)
+#define SNDRV_CTL_ELEM_ACCESS_READWRITE		(SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE)
+#define SNDRV_CTL_ELEM_ACCESS_VOLATILE		(1<<2)	/* control value may be changed without a notification */
+#define SNDRV_CTL_ELEM_ACCESS_TIMESTAMP		(1<<2)	/* when was control changed */
+#define SNDRV_CTL_ELEM_ACCESS_TLV_READ		(1<<4)	/* TLV read is possible */
+#define SNDRV_CTL_ELEM_ACCESS_TLV_WRITE		(1<<5)	/* TLV write is possible */
+#define SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE	(SNDRV_CTL_ELEM_ACCESS_TLV_READ|SNDRV_CTL_ELEM_ACCESS_TLV_WRITE)
+#define SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND	(1<<6)	/* TLV command is possible */
+#define SNDRV_CTL_ELEM_ACCESS_INACTIVE		(1<<8)	/* control does actually nothing, but may be updated */
+#define SNDRV_CTL_ELEM_ACCESS_LOCK		(1<<9)	/* write lock */
+#define SNDRV_CTL_ELEM_ACCESS_OWNER		(1<<10)	/* write lock owner */
+#define SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK	(1<<28)	/* kernel use a TLV callback */ 
+#define SNDRV_CTL_ELEM_ACCESS_USER		(1<<29) /* user space element */
+#define SNDRV_CTL_ELEM_ACCESS_DINDIRECT		(1<<30)	/* indirect access for matrix dimensions in the info structure */
+#define SNDRV_CTL_ELEM_ACCESS_INDIRECT		(1<<31)	/* indirect access for element value in the value structure */
+
+/* for further details see the ACPI and PCI power management specification */
+#define SNDRV_CTL_POWER_D0		0x0000	/* full On */
+#define SNDRV_CTL_POWER_D1		0x0100	/* partial On */
+#define SNDRV_CTL_POWER_D2		0x0200	/* partial On */
+#define SNDRV_CTL_POWER_D3		0x0300	/* Off */
+#define SNDRV_CTL_POWER_D3hot		(SNDRV_CTL_POWER_D3|0x0000)	/* Off, with power */
+#define SNDRV_CTL_POWER_D3cold		(SNDRV_CTL_POWER_D3|0x0001)	/* Off, without power */
+
+struct snd_ctl_elem_id {
+	unsigned int numid;		/* numeric identifier, zero = invalid */
+	snd_ctl_elem_iface_t iface;	/* interface identifier */
+	unsigned int device;		/* device/client number */
+	unsigned int subdevice;		/* subdevice (substream) number */
+        unsigned char name[44];		/* ASCII name of item */
+	unsigned int index;		/* index of item */
+};
+
+struct snd_ctl_elem_list {
+	unsigned int offset;		/* W: first element ID to get */
+	unsigned int space;		/* W: count of element IDs to get */
+	unsigned int used;		/* R: count of element IDs set */
+	unsigned int count;		/* R: count of all elements */
+	struct snd_ctl_elem_id __user *pids; /* R: IDs */
+	unsigned char reserved[50];
+};
+
+struct snd_ctl_elem_info {
+	struct snd_ctl_elem_id id;	/* W: element ID */
+	snd_ctl_elem_type_t type;	/* R: value type - SNDRV_CTL_ELEM_TYPE_* */
+	unsigned int access;		/* R: value access (bitmask) - SNDRV_CTL_ELEM_ACCESS_* */
+	unsigned int count;		/* count of values */
+	pid_t owner;			/* owner's PID of this control */
+	union {
+		struct {
+			long min;		/* R: minimum value */
+			long max;		/* R: maximum value */
+			long step;		/* R: step (0 variable) */
+		} integer;
+		struct {
+			long long min;		/* R: minimum value */
+			long long max;		/* R: maximum value */
+			long long step;		/* R: step (0 variable) */
+		} integer64;
+		struct {
+			unsigned int items;	/* R: number of items */
+			unsigned int item;	/* W: item number */
+			char name[64];		/* R: value name */
+		} enumerated;
+		unsigned char reserved[128];
+	} value;
+	union {
+		unsigned short d[4];		/* dimensions */
+		unsigned short *d_ptr;		/* indirect */
+	} dimen;
+	unsigned char reserved[64-4*sizeof(unsigned short)];
+};
+
+struct snd_ctl_elem_value {
+	struct snd_ctl_elem_id id;	/* W: element ID */
+	unsigned int indirect: 1;	/* W: use indirect pointer (xxx_ptr member) */
+        union {
+		union {
+			long value[128];
+			long *value_ptr;
+		} integer;
+		union {
+			long long value[64];
+			long long *value_ptr;
+		} integer64;
+		union {
+			unsigned int item[128];
+			unsigned int *item_ptr;
+		} enumerated;
+		union {
+			unsigned char data[512];
+			unsigned char *data_ptr;
+		} bytes;
+		struct snd_aes_iec958 iec958;
+        } value;                /* RO */
+	struct timespec tstamp;
+        unsigned char reserved[128-sizeof(struct timespec)];
+};
+
+struct snd_ctl_tlv {
+    unsigned int numid;	/* control element numeric identification */
+    unsigned int length;	/* in bytes aligned to 4 */
+    unsigned int tlv[1];	/* first TLV */
+};
+
+enum {
+	SNDRV_CTL_IOCTL_PVERSION = _IOR('U', 0x00, int),
+	SNDRV_CTL_IOCTL_CARD_INFO = _IOR('U', 0x01, struct snd_ctl_card_info),
+	SNDRV_CTL_IOCTL_ELEM_LIST = _IOWR('U', 0x10, struct snd_ctl_elem_list),
+	SNDRV_CTL_IOCTL_ELEM_INFO = _IOWR('U', 0x11, struct snd_ctl_elem_info),
+	SNDRV_CTL_IOCTL_ELEM_READ = _IOWR('U', 0x12, struct snd_ctl_elem_value),
+	SNDRV_CTL_IOCTL_ELEM_WRITE = _IOWR('U', 0x13, struct snd_ctl_elem_value),
+	SNDRV_CTL_IOCTL_ELEM_LOCK = _IOW('U', 0x14, struct snd_ctl_elem_id),
+	SNDRV_CTL_IOCTL_ELEM_UNLOCK = _IOW('U', 0x15, struct snd_ctl_elem_id),
+	SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS = _IOWR('U', 0x16, int),
+	SNDRV_CTL_IOCTL_ELEM_ADD = _IOWR('U', 0x17, struct snd_ctl_elem_info),
+	SNDRV_CTL_IOCTL_ELEM_REPLACE = _IOWR('U', 0x18, struct snd_ctl_elem_info),
+        SNDRV_CTL_IOCTL_ELEM_REMOVE = _IOWR('U', 0x19, struct snd_ctl_elem_id),
+        SNDRV_CTL_IOCTL_TLV_READ = _IOWR('U', 0x1a, struct snd_ctl_tlv),
+        SNDRV_CTL_IOCTL_TLV_WRITE = _IOWR('U', 0x1b, struct snd_ctl_tlv),
+        SNDRV_CTL_IOCTL_TLV_COMMAND = _IOWR('U', 0x1c, struct snd_ctl_tlv),
+	SNDRV_CTL_IOCTL_HWDEP_NEXT_DEVICE = _IOWR('U', 0x20, int),
+	SNDRV_CTL_IOCTL_HWDEP_INFO = _IOR('U', 0x21, struct snd_hwdep_info),
+	SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE = _IOR('U', 0x30, int),
+	SNDRV_CTL_IOCTL_PCM_INFO = _IOWR('U', 0x31, struct snd_pcm_info),
+	SNDRV_CTL_IOCTL_PCM_PREFER_SUBDEVICE = _IOW('U', 0x32, int),
+	SNDRV_CTL_IOCTL_RAWMIDI_NEXT_DEVICE = _IOWR('U', 0x40, int),
+	SNDRV_CTL_IOCTL_RAWMIDI_INFO = _IOWR('U', 0x41, struct snd_rawmidi_info),
+	SNDRV_CTL_IOCTL_RAWMIDI_PREFER_SUBDEVICE = _IOW('U', 0x42, int),
+	SNDRV_CTL_IOCTL_POWER = _IOWR('U', 0xd0, int),
+        SNDRV_CTL_IOCTL_POWER_STATE = _IOR('U', 0xd1, int),
+#ifdef TARGET_OS2
+        SNDRV_PCM_IOCTL_SETVOLUME = _IOW('A', 0x62, struct snd_pcm_volume),
+        SNDRV_PCM_IOCTL_GETVOLUME = _IOR('A', 0x63, struct snd_pcm_volume),
+#endif
+
+};
+
+/*
+ *  Read interface.
+ */
+
+enum sndrv_ctl_event_type {
+	SNDRV_CTL_EVENT_ELEM = 0,
+	SNDRV_CTL_EVENT_LAST = SNDRV_CTL_EVENT_ELEM,
+};
+
+#define SNDRV_CTL_EVENT_MASK_VALUE	(1<<0)	/* element value was changed */
+#define SNDRV_CTL_EVENT_MASK_INFO	(1<<1)	/* element info was changed */
+#define SNDRV_CTL_EVENT_MASK_ADD	(1<<2)	/* element was added */
+#define SNDRV_CTL_EVENT_MASK_TLV	(1<<3)	/* element TLV tree was changed */
+#define SNDRV_CTL_EVENT_MASK_REMOVE	(~0U)	/* element was removed */
+
+struct snd_ctl_event {
+	int type;	/* event type - SNDRV_CTL_EVENT_* */
+	union {
+		struct {
+			unsigned int mask;
+			struct snd_ctl_elem_id id;
+		} elem;
+                unsigned char data8[60];
+        } data;
+};
+
+/*
+ *  Control names
+ */
+
+#define SNDRV_CTL_NAME_NONE				""
+#define SNDRV_CTL_NAME_PLAYBACK				"Playback "
+#define SNDRV_CTL_NAME_CAPTURE				"Capture "
+
+#define SNDRV_CTL_NAME_IEC958_NONE			""
+#define SNDRV_CTL_NAME_IEC958_SWITCH			"Switch"
+#define SNDRV_CTL_NAME_IEC958_VOLUME			"Volume"
+#define SNDRV_CTL_NAME_IEC958_DEFAULT			"Default"
+#define SNDRV_CTL_NAME_IEC958_MASK			"Mask"
+#define SNDRV_CTL_NAME_IEC958_CON_MASK			"Con Mask"
+#define SNDRV_CTL_NAME_IEC958_PRO_MASK			"Pro Mask"
+#define SNDRV_CTL_NAME_IEC958_PCM_STREAM		"PCM Stream"
+#define SNDRV_CTL_NAME_IEC958(expl,direction,what)	"IEC958 " expl SNDRV_CTL_NAME_##direction SNDRV_CTL_NAME_IEC958_##what
+
+/*
+ *
+ */
+
+struct snd_xferv {
+	const struct iovec *vector;
+	unsigned long count;
+};
+
+enum {
+	SNDRV_IOCTL_READV = _IOW('K', 0x00, struct snd_xferv),
+	SNDRV_IOCTL_WRITEV = _IOW('K', 0x01, struct snd_xferv),
+};
+
+#define msleep(msecs) \
+        do { \
+                set_current_state(TASK_UNINTERRUPTIBLE); \
+                schedule_timeout(((msecs) * HZ + 999) / 1000);  \
+        } while (0)
+
+#define schedule_timeout_interruptible(x) \
+ {set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(x);}
+#define schedule_timeout_uninterruptible(x) {set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(x);}
+
+#endif /* __SOUND_ASOUND_H */
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/asound_fm.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/asound_fm.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/asound_fm.h	(revision 91)
@@ -0,0 +1,115 @@
+#ifndef __SOUND_ASOUND_FM_H
+#define __SOUND_ASOUND_FM_H
+
+/*
+ *  Advanced Linux Sound Architecture - ALSA
+ *
+ *  Interface file between ALSA driver & user space
+ *  Copyright (c) 1994-98 by Jaroslav Kysela <perex@suse.cz>,
+ *                           4Front Technologies
+ *
+ *  Direct FM control
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#define SNDRV_DM_FM_MODE_OPL2	0x00
+#define SNDRV_DM_FM_MODE_OPL3	0x01
+
+typedef struct snd_dm_fm_info {
+	unsigned char fm_mode;		/* OPL mode, see SNDRV_DM_FM_MODE_XXX */
+	unsigned char rhythm;		/* percussion mode flag */
+} snd_dm_fm_info_t;
+
+/*
+ *  Data structure composing an FM "note" or sound event.
+ */
+
+typedef struct snd_dm_fm_voice {
+	unsigned char op;		/* operator cell (0 or 1) */
+	unsigned char voice;		/* FM voice (0 to 17) */
+
+	unsigned char am;		/* amplitude modulation */
+	unsigned char vibrato;		/* vibrato effect */
+	unsigned char do_sustain;	/* sustain phase */
+	unsigned char kbd_scale;	/* keyboard scaling */
+	unsigned char harmonic;		/* 4 bits: harmonic and multiplier */
+	unsigned char scale_level;	/* 2 bits: decrease output freq rises */
+	unsigned char volume;		/* 6 bits: volume */
+
+	unsigned char attack;		/* 4 bits: attack rate */
+	unsigned char decay;		/* 4 bits: decay rate */
+	unsigned char sustain;		/* 4 bits: sustain level */
+	unsigned char release;		/* 4 bits: release rate */
+
+	unsigned char feedback;		/* 3 bits: feedback for op0 */
+	unsigned char connection;	/* 0 for serial, 1 for parallel */
+	unsigned char left;		/* stereo left */
+	unsigned char right;		/* stereo right */
+	unsigned char waveform;		/* 3 bits: waveform shape */
+} snd_dm_fm_voice_t;
+
+/*
+ *  This describes an FM note by its voice, octave, frequency number (10bit)
+ *  and key on/off.
+ */
+
+typedef struct snd_dm_fm_note {
+	unsigned char voice;	/* 0-17 voice channel */
+	unsigned char octave;	/* 3 bits: what octave to play */
+	unsigned int fnum;	/* 10 bits: frequency number */
+	unsigned char key_on;	/* set for active, clear for silent */
+} snd_dm_fm_note_t;
+
+/*
+ *  FM parameters that apply globally to all voices, and thus are not "notes"
+ */
+
+typedef struct snd_dm_fm_params {
+	unsigned char am_depth;		/* amplitude modulation depth (1=hi) */
+	unsigned char vib_depth;	/* vibrato depth (1=hi) */
+	unsigned char kbd_split;	/* keyboard split */
+	unsigned char rhythm;		/* percussion mode select */
+
+	/* This block is the percussion instrument data */
+	unsigned char bass;
+	unsigned char snare;
+	unsigned char tomtom;
+	unsigned char cymbal;
+	unsigned char hihat;
+} snd_dm_fm_params_t;
+
+/*
+ *  FM mode ioctl settings
+ */
+
+#define SNDRV_DM_FM_IOCTL_INFO		_IOR('H', 0x20, snd_dm_fm_info_t)
+#define SNDRV_DM_FM_IOCTL_RESET		_IO ('H', 0x21)
+#define SNDRV_DM_FM_IOCTL_PLAY_NOTE	_IOW('H', 0x22, snd_dm_fm_note_t)
+#define SNDRV_DM_FM_IOCTL_SET_VOICE	_IOW('H', 0x23, snd_dm_fm_voice_t)
+#define SNDRV_DM_FM_IOCTL_SET_PARAMS	_IOW('H', 0x24, snd_dm_fm_params_t)
+#define SNDRV_DM_FM_IOCTL_SET_MODE	_IOW('H', 0x25, int)
+/* for OPL3 only */
+#define SNDRV_DM_FM_IOCTL_SET_CONNECTION	_IOW('H', 0x26, int)
+
+#define SNDRV_DM_FM_OSS_IOCTL_RESET		0x20
+#define SNDRV_DM_FM_OSS_IOCTL_PLAY_NOTE		0x21
+#define SNDRV_DM_FM_OSS_IOCTL_SET_VOICE		0x22
+#define SNDRV_DM_FM_OSS_IOCTL_SET_PARAMS	0x23
+#define SNDRV_DM_FM_OSS_IOCTL_SET_MODE		0x24
+#define SNDRV_DM_FM_OSS_IOCTL_SET_OPL		0x25
+
+#endif /* __SOUND_ASOUND_FM_H */
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/asoundef.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/asoundef.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/asoundef.h	(revision 91)
@@ -0,0 +1,227 @@
+/*
+ *  Advanced Linux Sound Architecture - ALSA - Driver
+ *  Copyright (c) 1994-2000 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef __ASOUNDEF_H
+#define __ASOUNDEF_H
+
+/****************************************************************************
+ *                                                                          *
+ *        Digital audio interface					    *
+ *                                                                          *
+ ****************************************************************************/
+
+/* AES/IEC958 channel status bits */
+#define IEC958_AES0_PROFESSIONAL	(1<<0)	/* 0 = consumer, 1 = professional */
+#define IEC958_AES0_NONAUDIO		(1<<1)	/* 0 = audio, 1 = non-audio */
+#define IEC958_AES0_PRO_EMPHASIS	(7<<2)	/* mask - emphasis */
+#define IEC958_AES0_PRO_EMPHASIS_NOTID	(0<<2)	/* emphasis not indicated */
+#define IEC958_AES0_PRO_EMPHASIS_NONE	(1<<2)	/* none emphasis */
+#define IEC958_AES0_PRO_EMPHASIS_5015	(3<<2)	/* 50/15us emphasis */
+#define IEC958_AES0_PRO_EMPHASIS_CCITT	(7<<2)	/* CCITT J.17 emphasis */
+#define IEC958_AES0_PRO_FREQ_UNLOCKED	(1<<5)	/* source sample frequency: 0 = locked, 1 = unlocked */
+#define IEC958_AES0_PRO_FS		(3<<6)	/* mask - sample frequency */
+#define IEC958_AES0_PRO_FS_NOTID	(0<<6)	/* fs not indicated */
+#define IEC958_AES0_PRO_FS_44100	(1<<6)	/* 44.1kHz */
+#define IEC958_AES0_PRO_FS_48000	(2<<6)	/* 48kHz */
+#define IEC958_AES0_PRO_FS_32000	(3<<6)	/* 32kHz */
+#define IEC958_AES0_CON_NOT_COPYRIGHT	(1<<2)	/* 0 = copyright, 1 = not copyright */
+#define IEC958_AES0_CON_EMPHASIS	(7<<3)	/* mask - emphasis */
+#define IEC958_AES0_CON_EMPHASIS_NONE	(0<<3)	/* none emphasis */
+#define IEC958_AES0_CON_EMPHASIS_5015	(1<<3)	/* 50/15us emphasis */
+#define IEC958_AES0_CON_MODE		(3<<6)	/* mask - mode */
+#define IEC958_AES1_PRO_MODE		(15<<0)	/* mask - channel mode */
+#define IEC958_AES1_PRO_MODE_NOTID	(0<<0)	/* not indicated */
+#define IEC958_AES1_PRO_MODE_STEREOPHONIC (2<<0) /* stereophonic - ch A is left */
+#define IEC958_AES1_PRO_MODE_SINGLE	(4<<0)	/* single channel */
+#define IEC958_AES1_PRO_MODE_TWO	(8<<0)	/* two channels */
+#define IEC958_AES1_PRO_MODE_PRIMARY	(12<<0)	/* primary/secondary */
+#define IEC958_AES1_PRO_MODE_BYTE3	(15<<0)	/* vector to byte 3 */
+#define IEC958_AES1_PRO_USERBITS	(15<<4)	/* mask - user bits */
+#define IEC958_AES1_PRO_USERBITS_NOTID	(0<<4)	/* not indicated */
+#define IEC958_AES1_PRO_USERBITS_192	(8<<4)	/* 192-bit structure */
+#define IEC958_AES1_PRO_USERBITS_UDEF	(12<<4)	/* user defined application */
+#define IEC958_AES1_CON_CATEGORY	0x7f
+#define IEC958_AES1_CON_GENERAL		0x00
+#define IEC958_AES1_CON_EXPERIMENTAL	0x40
+#define IEC958_AES1_CON_SOLIDMEM_MASK	0x0f
+#define IEC958_AES1_CON_SOLIDMEM_ID	0x08
+#define IEC958_AES1_CON_BROADCAST1_MASK 0x07
+#define IEC958_AES1_CON_BROADCAST1_ID	0x04
+#define IEC958_AES1_CON_DIGDIGCONV_MASK 0x07
+#define IEC958_AES1_CON_DIGDIGCONV_ID	0x02
+#define IEC958_AES1_CON_ADC_COPYRIGHT_MASK 0x1f
+#define IEC958_AES1_CON_ADC_COPYRIGHT_ID 0x06
+#define IEC958_AES1_CON_ADC_MASK	0x1f
+#define IEC958_AES1_CON_ADC_ID		0x16
+#define IEC958_AES1_CON_BROADCAST2_MASK 0x0f
+#define IEC958_AES1_CON_BROADCAST2_ID	0x0e
+#define IEC958_AES1_CON_LASEROPT_MASK	0x07
+#define IEC958_AES1_CON_LASEROPT_ID	0x01
+#define IEC958_AES1_CON_MUSICAL_MASK	0x07
+#define IEC958_AES1_CON_MUSICAL_ID	0x05
+#define IEC958_AES1_CON_MAGNETIC_MASK	0x07
+#define IEC958_AES1_CON_MAGNETIC_ID	0x03
+#define IEC958_AES1_CON_IEC908_CD	(IEC958_AES1_CON_LASEROPT_ID|0x00)
+#define IEC958_AES1_CON_NON_IEC908_CD	(IEC958_AES1_CON_LASEROPT_ID|0x08)
+#define IEC958_AES1_CON_PCM_CODER	(IEC958_AES1_CON_DIGDIGCONV_ID|0x00)
+#define IEC958_AES1_CON_SAMPLER		(IEC958_AES1_CON_DIGDIGCONV_ID|0x20)
+#define IEC958_AES1_CON_MIXER		(IEC958_AES1_CON_DIGDIGCONV_ID|0x10)
+#define IEC958_AES1_CON_RATE_CONVERTER	(IEC958_AES1_CON_DIGDIGCONV_ID|0x18)
+#define IEC958_AES1_CON_SYNTHESIZER	(IEC958_AES1_CON_MUSICAL_ID|0x00)
+#define IEC958_AES1_CON_MICROPHONE	(IEC958_AES1_CON_MUSICAL_ID|0x08)
+#define IEC958_AES1_CON_DAT		(IEC958_AES1_CON_MAGNETIC_ID|0x00)
+#define IEC958_AES1_CON_VCR		(IEC958_AES1_CON_MAGNETIC_ID|0x08)
+#define IEC958_AES1_CON_ORIGINAL	(1<<7)	/* this bits depends on the category code */
+#define IEC958_AES2_PRO_SBITS		(7<<0)	/* mask - sample bits */
+#define IEC958_AES2_PRO_SBITS_20	(2<<0)	/* 20-bit - coordination */
+#define IEC958_AES2_PRO_SBITS_24	(4<<0)	/* 24-bit - main audio */
+#define IEC958_AES2_PRO_SBITS_UDEF	(6<<0)	/* user defined application */
+#define IEC958_AES2_PRO_WORDLEN		(7<<3)	/* mask - source word length */
+#define IEC958_AES2_PRO_WORDLEN_NOTID	(0<<3)	/* not indicated */
+#define IEC958_AES2_PRO_WORDLEN_22_18	(2<<3)	/* 22-bit or 18-bit */
+#define IEC958_AES2_PRO_WORDLEN_23_19	(4<<3)	/* 23-bit or 19-bit */
+#define IEC958_AES2_PRO_WORDLEN_24_20	(5<<3)	/* 24-bit or 20-bit */
+#define IEC958_AES2_PRO_WORDLEN_20_16	(6<<3)	/* 20-bit or 16-bit */
+#define IEC958_AES2_CON_SOURCE		(15<<0)	/* mask - source number */
+#define IEC958_AES2_CON_SOURCE_UNSPEC	(0<<0)	/* unspecified */
+#define IEC958_AES2_CON_CHANNEL		(15<<4)	/* mask - channel number */
+#define IEC958_AES2_CON_CHANNEL_UNSPEC	(0<<4)	/* unspecified */
+#define IEC958_AES3_CON_FS		(15<<0)	/* mask - sample frequency */
+#define IEC958_AES3_CON_FS_44100	(0<<0)	/* 44.1kHz */
+#define IEC958_AES3_CON_FS_48000	(2<<0)	/* 48kHz */
+#define IEC958_AES3_CON_FS_32000	(3<<0)	/* 32kHz */
+#define IEC958_AES3_CON_CLOCK		(3<<4)	/* mask - clock accuracy */
+#define IEC958_AES3_CON_CLOCK_1000PPM	(0<<4)	/* 1000 ppm */
+#define IEC958_AES3_CON_CLOCK_50PPM	(1<<4)	/* 50 ppm */
+#define IEC958_AES3_CON_CLOCK_VARIABLE	(2<<4)	/* variable pitch */
+
+/*****************************************************************************
+ *                                                                           *
+ *                            MIDI v1.0 interface                            *
+ *                                                                           *
+ *****************************************************************************/
+
+#define MIDI_CHANNELS			16
+#define MIDI_GM_DRUM_CHANNEL		(10-1)
+
+/*
+ *  MIDI commands
+ */
+
+#define MIDI_CMD_NOTE_OFF		0x80
+#define MIDI_CMD_NOTE_ON		0x90
+#define MIDI_CMD_NOTE_PRESSURE		0xa0
+#define MIDI_CMD_CONTROL		0xb0
+#define MIDI_CMD_PGM_CHANGE		0xc0
+#define MIDI_CMD_CHANNEL_PRESSURE	0xd0
+#define MIDI_CMD_BENDER			0xe0
+
+#define MIDI_CMD_COMMON_SYSEX		0xf0
+#define MIDI_CMD_COMMON_MTC_QUARTER	0xf1
+#define MIDI_CMD_COMMON_SONG_POS	0xf2
+#define MIDI_CMD_COMMON_SONG_SELECT	0xf3
+#define MIDI_CMD_COMMON_TUNE_REQUEST	0xf6
+#define MIDI_CMD_COMMON_SYSEX_END	0xf7
+#define MIDI_CMD_COMMON_CLOCK		0xf8
+#define MIDI_CMD_COMMON_START		0xfa
+#define MIDI_CMD_COMMON_CONTINUE	0xfb
+#define MIDI_CMD_COMMON_STOP		0xfc
+#define MIDI_CMD_COMMON_SENSING		0xfe
+#define MIDI_CMD_COMMON_RESET		0xff
+
+/*
+ *  MIDI controllers
+ */
+
+#define MIDI_CTL_MSB_BANK		0x00
+#define MIDI_CTL_MSB_MODWHEEL         	0x01
+#define MIDI_CTL_MSB_BREATH           	0x02
+#define MIDI_CTL_MSB_FOOT             	0x04
+#define MIDI_CTL_MSB_PORTNAMENTO_TIME 	0x05
+#define MIDI_CTL_MSB_DATA_ENTRY		0x06
+#define MIDI_CTL_MSB_MAIN_VOLUME      	0x07
+#define MIDI_CTL_MSB_BALANCE          	0x08
+#define MIDI_CTL_MSB_PAN              	0x0a
+#define MIDI_CTL_MSB_EXPRESSION       	0x0b
+#define MIDI_CTL_MSB_EFFECT1		0x0c
+#define MIDI_CTL_MSB_EFFECT2		0x0d
+#define MIDI_CTL_MSB_GENERAL_PURPOSE1 	0x10
+#define MIDI_CTL_MSB_GENERAL_PURPOSE2 	0x11
+#define MIDI_CTL_MSB_GENERAL_PURPOSE3 	0x12
+#define MIDI_CTL_MSB_GENERAL_PURPOSE4 	0x13
+#define MIDI_CTL_LSB_BANK		0x20
+#define MIDI_CTL_LSB_MODWHEEL        	0x21
+#define MIDI_CTL_LSB_BREATH           	0x22
+#define MIDI_CTL_LSB_FOOT             	0x24
+#define MIDI_CTL_LSB_PORTNAMENTO_TIME 	0x25
+#define MIDI_CTL_LSB_DATA_ENTRY		0x26
+#define MIDI_CTL_LSB_MAIN_VOLUME      	0x27
+#define MIDI_CTL_LSB_BALANCE          	0x28
+#define MIDI_CTL_LSB_PAN              	0x2a
+#define MIDI_CTL_LSB_EXPRESSION       	0x2b
+#define MIDI_CTL_LSB_EFFECT1		0x2c
+#define MIDI_CTL_LSB_EFFECT2		0x2d
+#define MIDI_CTL_LSB_GENERAL_PURPOSE1 	0x30
+#define MIDI_CTL_LSB_GENERAL_PURPOSE2 	0x31
+#define MIDI_CTL_LSB_GENERAL_PURPOSE3 	0x32
+#define MIDI_CTL_LSB_GENERAL_PURPOSE4 	0x33
+#define MIDI_CTL_SUSTAIN              	0x40
+#define MIDI_CTL_PORTAMENTO           	0x41
+#define MIDI_CTL_SOSTENUTO            	0x42
+#define MIDI_CTL_SOFT_PEDAL           	0x43
+#define MIDI_CTL_LEGATO_FOOTSWITCH	0x44
+#define MIDI_CTL_HOLD2                	0x45
+#define MIDI_CTL_SC1_SOUND_VARIATION	0x46
+#define MIDI_CTL_SC2_TIMBRE		0x47
+#define MIDI_CTL_SC3_RELEASE_TIME	0x48
+#define MIDI_CTL_SC4_ATTACK_TIME	0x49
+#define MIDI_CTL_SC5_BRIGHTNESS		0x4a
+#define MIDI_CTL_SC6			0x4b
+#define MIDI_CTL_SC7			0x4c
+#define MIDI_CTL_SC8			0x4d
+#define MIDI_CTL_SC9			0x4e
+#define MIDI_CTL_SC10			0x4f
+#define MIDI_CTL_GENERAL_PURPOSE5     	0x50
+#define MIDI_CTL_GENERAL_PURPOSE6     	0x51
+#define MIDI_CTL_GENERAL_PURPOSE7     	0x52
+#define MIDI_CTL_GENERAL_PURPOSE8     	0x53
+#define MIDI_CTL_PORNAMENTO_CONTROL	0x54
+#define MIDI_CTL_E1_REVERB_DEPTH	0x5b
+#define MIDI_CTL_E2_TREMOLO_DEPTH	0x5c
+#define MIDI_CTL_E3_CHORUS_DEPTH	0x5d
+#define MIDI_CTL_E4_DETUNE_DEPTH	0x5e
+#define MIDI_CTL_E5_PHASER_DEPTH	0x5f
+#define MIDI_CTL_DATA_INCREMENT       	0x60
+#define MIDI_CTL_DATA_DECREMENT       	0x61
+#define MIDI_CTL_NONREG_PARM_NUM_LSB  	0x62
+#define MIDI_CTL_NONREG_PARM_NUM_MSB  	0x63
+#define MIDI_CTL_REGIST_PARM_NUM_LSB  	0x64
+#define MIDI_CTL_REGIST_PARM_NUM_MSB	0x65
+#define MIDI_CTL_ALL_SOUNDS_OFF		0x78
+#define MIDI_CTL_RESET_CONTROLLERS	0x79
+#define MIDI_CTL_LOCAL_CONTROL_SWITCH	0x7a
+#define MIDI_CTL_ALL_NOTES_OFF		0x7b
+#define MIDI_CTL_OMNI_OFF		0x7c
+#define MIDI_CTL_OMNI_ON		0x7d
+#define MIDI_CTL_MONO1			0x7e
+#define MIDI_CTL_MONO2			0x7f
+
+#endif				/* __ASOUNDEF_H */
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/awacs.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/awacs.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/awacs.h	(revision 91)
@@ -0,0 +1,182 @@
+/*
+ * Driver for PowerMac AWACS onboard soundchips
+ * Copyright (c) 2001 by Takashi Iwai <tiwai@suse.de>
+ *   based on dmasound.c.
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#ifndef __AWACS_H
+#define __AWACS_H
+
+/*******************************/
+/* AWACs Audio Register Layout */
+/*******************************/
+
+struct awacs_regs {
+    unsigned	control;	/* Audio control register */
+    unsigned	pad0[3];
+    unsigned	codec_ctrl;	/* Codec control register */
+    unsigned	pad1[3];
+    unsigned	codec_stat;	/* Codec status register */
+    unsigned	pad2[3];
+    unsigned	clip_count;	/* Clipping count register */
+    unsigned	pad3[3];
+    unsigned	byteswap;	/* Data is little-endian if 1 */
+};
+
+/*******************/
+/* Audio Bit Masks */
+/*******************/
+
+/* Audio Control Reg Bit Masks */
+/* ----- ------- --- --- ----- */
+#define MASK_ISFSEL	(0xf)		/* Input SubFrame Select */
+#define MASK_OSFSEL	(0xf << 4)	/* Output SubFrame Select */
+#define MASK_RATE	(0x7 << 8)	/* Sound Rate */
+#define MASK_CNTLERR	(0x1 << 11)	/* Error */
+#define MASK_PORTCHG	(0x1 << 12)	/* Port Change */
+#define MASK_IEE	(0x1 << 13)	/* Enable Interrupt on Error */
+#define MASK_IEPC	(0x1 << 14)	/* Enable Interrupt on Port Change */
+#define MASK_SSFSEL	(0x3 << 15)	/* Status SubFrame Select */
+
+/* Audio Codec Control Reg Bit Masks */
+/* ----- ----- ------- --- --- ----- */
+#define MASK_NEWECMD	(0x1 << 24)	/* Lock: don't write to reg when 1 */
+#define MASK_EMODESEL	(0x3 << 22)	/* Send info out on which frame? */
+#define MASK_EXMODEADDR	(0x3ff << 12)	/* Extended Mode Address -- 10 bits */
+#define MASK_EXMODEDATA	(0xfff)		/* Extended Mode Data -- 12 bits */
+
+/* Audio Codec Control Address Values / Masks */
+/* ----- ----- ------- ------- ------ - ----- */
+#define MASK_ADDR0	(0x0 << 12)	/* Expanded Data Mode Address 0 */
+#define MASK_ADDR_MUX	MASK_ADDR0	/* Mux Control */
+#define MASK_ADDR_GAIN	MASK_ADDR0
+
+#define MASK_ADDR1	(0x1 << 12)	/* Expanded Data Mode Address 1 */
+#define MASK_ADDR_MUTE	MASK_ADDR1
+#define MASK_ADDR_RATE	MASK_ADDR1
+
+#define MASK_ADDR2	(0x2 << 12)	/* Expanded Data Mode Address 2 */
+#define MASK_ADDR_VOLA	MASK_ADDR2	/* Volume Control A -- Headphones */
+#define MASK_ADDR_VOLHD MASK_ADDR2
+
+#define MASK_ADDR4	(0x4 << 12)	/* Expanded Data Mode Address 4 */
+#define MASK_ADDR_VOLC	MASK_ADDR4	/* Volume Control C -- Speaker */
+#define MASK_ADDR_VOLSPK MASK_ADDR4
+
+/* additional registers of screamer */
+#define MASK_ADDR5	(0x5 << 12)	/* Expanded Data Mode Address 5 */
+#define MASK_ADDR6	(0x6 << 12)	/* Expanded Data Mode Address 6 */
+#define MASK_ADDR7	(0x7 << 12)	/* Expanded Data Mode Address 7 */
+
+/* Address 0 Bit Masks & Macros */
+/* ------- - --- ----- - ------ */
+#define MASK_GAINRIGHT	(0xf)		/* Gain Right Mask */
+#define MASK_GAINLEFT	(0xf << 4)	/* Gain Left Mask */
+#define MASK_GAINLINE	(0x1 << 8)	/* Disable Mic preamp */
+#define MASK_GAINMIC	(0x0 << 8)	/* Enable Mic preamp */
+
+#define MASK_MUX_CD	(0x1 << 9)	/* Select CD in MUX */
+#define MASK_MUX_MIC	(0x1 << 10)	/* Select Mic in MUX */
+#define MASK_MUX_AUDIN	(0x1 << 11)	/* Select Audio In in MUX */
+#define MASK_MUX_LINE	MASK_MUX_AUDIN
+
+#define GAINRIGHT(x)	((x) & MASK_GAINRIGHT)
+#define GAINLEFT(x)	(((x) << 4) & MASK_GAINLEFT)
+
+/* Address 1 Bit Masks */
+/* ------- - --- ----- */
+#define MASK_ADDR1RES1	(0x3)		/* Reserved */
+#define MASK_RECALIBRATE (0x1 << 2)	/* Recalibrate */
+#define MASK_SAMPLERATE	(0x7 << 3)	/* Sample Rate: */
+#define MASK_LOOPTHRU	(0x1 << 6)	/* Loopthrough Enable */
+#define MASK_CMUTE	(0x1 << 7)	/* Output C (Speaker) Mute when 1 */
+#define MASK_SPKMUTE	MASK_CMUTE
+#define MASK_ADDR1RES2	(0x1 << 8)	/* Reserved */
+#define MASK_AMUTE	(0x1 << 9)	/* Output A (Headphone) Mute when 1 */
+#define MASK_HDMUTE	MASK_AMUTE
+#define MASK_PAROUT	(0x3 << 10)	/* Parallel Out (???) */
+
+#define SAMPLERATE_48000	(0x0 << 3)	/* 48 or 44.1 kHz */
+#define SAMPLERATE_32000	(0x1 << 3)	/* 32 or 29.4 kHz */
+#define SAMPLERATE_24000	(0x2 << 3)	/* 24 or 22.05 kHz */
+#define SAMPLERATE_19200	(0x3 << 3)	/* 19.2 or 17.64 kHz */
+#define SAMPLERATE_16000	(0x4 << 3)	/* 16 or 14.7 kHz */
+#define SAMPLERATE_12000	(0x5 << 3)	/* 12 or 11.025 kHz */
+#define SAMPLERATE_9600		(0x6 << 3)	/* 9.6 or 8.82 kHz */
+#define SAMPLERATE_8000		(0x7 << 3)	/* 8 or 7.35 kHz */
+
+/* Address 2 & 4 Bit Masks & Macros */
+/* ------- - - - --- ----- - ------ */
+#define MASK_OUTVOLRIGHT (0xf)		/* Output Right Volume */
+#define MASK_ADDR2RES1	(0x2 << 4)	/* Reserved */
+#define MASK_ADDR4RES1	MASK_ADDR2RES1
+#define MASK_OUTVOLLEFT	(0xf << 6)	/* Output Left Volume */
+#define MASK_ADDR2RES2	(0x2 << 10)	/* Reserved */
+#define MASK_ADDR4RES2	MASK_ADDR2RES2
+
+#define VOLRIGHT(x)	(((~(x)) & MASK_OUTVOLRIGHT))
+#define VOLLEFT(x)	(((~(x)) << 6) & MASK_OUTVOLLEFT)
+
+/* Audio Codec Status Reg Bit Masks */
+/* ----- ----- ------ --- --- ----- */
+#define MASK_EXTEND	(0x1 << 23)	/* Extend */
+#define MASK_VALID	(0x1 << 22)	/* Valid Data? */
+#define MASK_OFLEFT	(0x1 << 21)	/* Overflow Left */
+#define MASK_OFRIGHT	(0x1 << 20)	/* Overflow Right */
+#define MASK_ERRCODE	(0xf << 16)	/* Error Code */
+#define MASK_REVISION	(0xf << 12)	/* Revision Number */
+#define MASK_MFGID	(0xf << 8)	/* Mfg. ID */
+#define MASK_CODSTATRES	(0xf << 4)	/* bits 4 - 7 reserved */
+#define MASK_INPPORT	(0xf)		/* Input Port */
+#define MASK_HDPCONN	8		/* headphone plugged in */
+
+/* Clipping Count Reg Bit Masks */
+/* -------- ----- --- --- ----- */
+#define MASK_CLIPLEFT	(0xff << 7)	/* Clipping Count, Left Channel */
+#define MASK_CLIPRIGHT	(0xff)		/* Clipping Count, Right Channel */
+
+/* DBDMA ChannelStatus Bit Masks */
+/* ----- ------------- --- ----- */
+#define MASK_CSERR	(0x1 << 7)	/* Error */
+#define MASK_EOI	(0x1 << 6)	/* End of Input -- only for Input Channel */
+#define MASK_CSUNUSED	(0x1f << 1)	/* bits 1-5 not used */
+#define MASK_WAIT	(0x1)		/* Wait */
+
+/* Various Rates */
+/* ------- ----- */
+#define RATE_48000	(0x0 << 8)	/* 48 kHz */
+#define RATE_44100	(0x0 << 8)	/* 44.1 kHz */
+#define RATE_32000	(0x1 << 8)	/* 32 kHz */
+#define RATE_29400	(0x1 << 8)	/* 29.4 kHz */
+#define RATE_24000	(0x2 << 8)	/* 24 kHz */
+#define RATE_22050	(0x2 << 8)	/* 22.05 kHz */
+#define RATE_19200	(0x3 << 8)	/* 19.2 kHz */
+#define RATE_17640	(0x3 << 8)	/* 17.64 kHz */
+#define RATE_16000	(0x4 << 8)	/* 16 kHz */
+#define RATE_14700	(0x4 << 8)	/* 14.7 kHz */
+#define RATE_12000	(0x5 << 8)	/* 12 kHz */
+#define RATE_11025	(0x5 << 8)	/* 11.025 kHz */
+#define RATE_9600	(0x6 << 8)	/* 9.6 kHz */
+#define RATE_8820	(0x6 << 8)	/* 8.82 kHz */
+#define RATE_8000	(0x7 << 8)	/* 8 kHz */
+#define RATE_7350	(0x7 << 8)	/* 7.35 kHz */
+
+#define RATE_LOW	1	/* HIGH = 48kHz, etc;  LOW = 44.1kHz, etc. */
+
+
+#endif /* __AWACS_H */
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/burgundy.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/burgundy.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/burgundy.h	(revision 91)
@@ -0,0 +1,95 @@
+/*
+ * Driver for PowerMac Burgundy onboard soundchips
+ * Copyright (c) 2001 by Takashi Iwai <tiwai@suse.de>
+ *   based on dmasound.c.
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#ifndef __BURGUNDY_H
+#define __BURGUNDY_H
+
+#define MASK_ADDR_BURGUNDY_INPSEL21 (0x11 << 12)
+#define MASK_ADDR_BURGUNDY_INPSEL3 (0x12 << 12)
+
+#define MASK_ADDR_BURGUNDY_GAINCH1 (0x13 << 12)
+#define MASK_ADDR_BURGUNDY_GAINCH2 (0x14 << 12)
+#define MASK_ADDR_BURGUNDY_GAINCH3 (0x15 << 12)
+#define MASK_ADDR_BURGUNDY_GAINCH4 (0x16 << 12)
+
+#define MASK_ADDR_BURGUNDY_VOLCH1 (0x20 << 12)
+#define MASK_ADDR_BURGUNDY_VOLCH2 (0x21 << 12)
+#define MASK_ADDR_BURGUNDY_VOLCH3 (0x22 << 12)
+#define MASK_ADDR_BURGUNDY_VOLCH4 (0x23 << 12)
+
+#define MASK_ADDR_BURGUNDY_OUTPUTSELECTS (0x2B << 12)
+#define MASK_ADDR_BURGUNDY_OUTPUTENABLES (0x2F << 12)
+
+#define MASK_ADDR_BURGUNDY_MASTER_VOLUME (0x30 << 12)
+
+#define MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES (0x60 << 12)
+
+#define MASK_ADDR_BURGUNDY_ATTENSPEAKER (0x62 << 12)
+#define MASK_ADDR_BURGUNDY_ATTENLINEOUT (0x63 << 12)
+#define MASK_ADDR_BURGUNDY_ATTENHP (0x64 << 12)
+
+#define MASK_ADDR_BURGUNDY_VOLCD (MASK_ADDR_BURGUNDY_VOLCH1)
+#define MASK_ADDR_BURGUNDY_VOLLINE (MASK_ADDR_BURGUNDY_VOLCH2)
+#define MASK_ADDR_BURGUNDY_VOLMIC (MASK_ADDR_BURGUNDY_VOLCH3)
+#define MASK_ADDR_BURGUNDY_VOLMODEM (MASK_ADDR_BURGUNDY_VOLCH4)
+
+#define MASK_ADDR_BURGUNDY_GAINCD (MASK_ADDR_BURGUNDY_GAINCH1)
+#define MASK_ADDR_BURGUNDY_GAINLINE (MASK_ADDR_BURGUNDY_GAINCH2)
+#define MASK_ADDR_BURGUNDY_GAINMIC (MASK_ADDR_BURGUNDY_GAINCH3)
+#define MASK_ADDR_BURGUNDY_GAINMODEM (MASK_ADDR_BURGUNDY_VOLCH4)
+
+
+/* These are all default values for the burgundy */
+#define DEF_BURGUNDY_INPSEL21 (0xAA)
+#define DEF_BURGUNDY_INPSEL3 (0x0A)
+
+#define DEF_BURGUNDY_GAINCD (0x33)
+#define DEF_BURGUNDY_GAINLINE (0x44)
+#define DEF_BURGUNDY_GAINMIC (0x44)
+#define DEF_BURGUNDY_GAINMODEM (0x06)
+
+/* Remember: lowest volume here is 0x9b */
+#define DEF_BURGUNDY_VOLCD (0xCCCCCCCC)
+#define DEF_BURGUNDY_VOLLINE (0x00000000)
+#define DEF_BURGUNDY_VOLMIC (0x00000000)
+#define DEF_BURGUNDY_VOLMODEM (0xCCCCCCCC)
+
+#define DEF_BURGUNDY_OUTPUTSELECTS (0x010f010f)
+#define DEF_BURGUNDY_OUTPUTENABLES (0x0A)
+
+/* #define DEF_BURGUNDY_MASTER_VOLUME (0xFFFFFFFF) */ /* too loud */
+#define DEF_BURGUNDY_MASTER_VOLUME (0xDDDDDDDD)
+
+#define DEF_BURGUNDY_MORE_OUTPUTENABLES (0x7E)
+
+#define DEF_BURGUNDY_ATTENSPEAKER (0x44)
+#define DEF_BURGUNDY_ATTENLINEOUT (0xCC)
+#define DEF_BURGUNDY_ATTENHP (0xCC)
+
+/* OUTPUTENABLES bits */
+#define BURGUNDY_OUTPUT_LEFT	0x02
+#define BURGUNDY_OUTPUT_RIGHT	0x04
+#define BURGUNDY_OUTPUT_INTERN	0x80
+
+/* volume offset */
+#define BURGUNDY_VOLUME_OFFSET	155
+
+#endif /* __BURGUNDY_H */
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/compat_22.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/compat_22.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/compat_22.h	(revision 91)
@@ -0,0 +1,476 @@
+#ifndef __COMPAT_22__
+#define __COMPAT_22__
+
+/*
+ *  Missing Linux 2.2.x features
+ */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
+
+#ifndef CONFIG_HAVE_DMA_ADDR_T
+typedef unsigned long dma_addr_t;
+#endif
+
+/*
+ */
+
+#ifndef CONFIG_HAVE_MUTEX_MACROS
+#define init_MUTEX(x) *(x) = MUTEX
+#define DECLARE_MUTEX(x) struct semaphore x = MUTEX
+typedef struct wait_queue wait_queue_t;
+typedef struct wait_queue * wait_queue_head_t;
+#define init_waitqueue_head(x) *(x) = NULL
+#define init_waitqueue_entry(q,p) ((q)->task = (p))
+#define set_current_state(xstate) do { current->state = xstate; } while (0)
+
+/*
+ * Insert a new entry before the specified head..
+ */
+static __inline__ void list_add_tail(struct list_head *new, struct list_head *head)
+{
+	__list_add(new, head->prev, head);
+}
+
+#endif /* !CONFIG_HAVE_MUTEX_MACROS */
+
+
+#define virt_to_page(x) (&mem_map[MAP_NR(x)])
+#define get_page(p) atomic_inc(&(p)->count)
+#define SetPageReserved(p) set_bit(PG_reserved, &(p)->flags)
+#define ClearPageReserved(p) clear_bit(PG_reserved, &(p)->flags)
+#define vm_private_data vm_pte
+#define NOPAGE_OOM 0
+#define NOPAGE_SIGBUS (-1)
+#define fops_get(x) (x)
+#define fops_put(x) do { ; } while (0)
+
+#define local_irq_save(flags) \
+	do { __save_flags(flags); __cli(); } while (0)
+#define local_irq_restore(flags) \
+	do { __restore_flags(flags); } while (0)
+
+/* Some distributions use modified kill_fasync */
+#ifdef CONFIG_OLD_KILL_FASYNC
+#define snd_kill_fasync(fp, sig, band) kill_fasync(*(fp), sig)
+#else
+#define snd_kill_fasync(fp, sig, band) kill_fasync(*(fp), sig, band)
+#endif
+
+#define tasklet_hi_schedule(t)	queue_task((t), &tq_immediate); \
+				mark_bh(IMMEDIATE_BH)
+
+#define tasklet_init(t,f,d)	(t)->next = NULL; \
+				(t)->sync = 0; \
+				(t)->routine = (void (*)(void *))(f); \
+				(t)->data = (void *)(d)
+
+#define tasklet_struct		tq_struct 
+
+#define tasklet_unlock_wait(t)	while (test_bit(0, &(t)->sync)) { }
+
+#define rwlock_init(x) do { *(x) = RW_LOCK_UNLOCKED; } while(0)
+
+#define __init
+#define __initdata
+#define __exit
+#define __exitdata
+#define __devinit
+#define __devinitdata
+#define __devexit
+#define __devexitdata
+
+#ifdef MODULE
+  #ifndef module_init
+  #define module_init(x)      int init_module(void) { return x(); }
+  #define module_exit(x)      void cleanup_module(void) { x(); }
+  #endif
+  #ifndef THIS_MODULE
+  #define THIS_MODULE	      (&__this_module)
+  #endif
+  int try_inc_mod_count(struct module *mod);
+#else
+  #define module_init(x)
+  #define module_exit(x)
+  #define THIS_MODULE NULL
+  #define try_inc_mod_count(x) do { ; } while (0)
+#endif
+
+#define try_module_get(x) try_inc_mod_count(x)
+
+static inline void module_put(struct module *module)
+{
+    if (module)
+        do {} while(0);
+}
+
+#define MODULE_GENERIC_TABLE(gtype,name)        
+#define MODULE_DEVICE_TABLE(type,name)         
+
+/**
+ * list_for_each        -       iterate over a list
+ * @pos:        the &struct list_head to use as a loop counter.
+ * @head:       the head for your list.
+ */
+#define list_for_each(pos, head) \
+    for (pos = (head)->next; pos != (head); pos = pos->next)
+
+/**
+ * list_for_each_safe	-	iterate over a list safely (actual pointer can be invalidated)
+ * @pos:	the &struct list_head to use as a loop counter.
+ * @next:	the &struct list_head to use to save next.
+ * @head:	the head for your list.
+ */
+#define list_for_each_safe(pos, npos, head) \
+	for (pos = (head)->next, npos = pos->next ; pos != (head); pos = npos, npos = pos->next)
+
+/**
+ * list_for_each_entry	-	iterate over list of given type
+ * @pos:	the type * to use as a loop counter.
+ * @head:	the head for your list.
+ * @member:	the name of the list_struct within the struct.
+ */
+#define list_for_each_entry(pos, head, member)				\
+	for (pos = list_entry((head)->next, typeof(*pos), member);	\
+	     pos->member.next, &pos->member != (head); 	\
+	     pos = list_entry(pos->member.next, typeof(*pos), member))
+
+#ifndef IORESOURCE_IO
+struct resource {
+	const char *name;
+	unsigned long start, end;
+	unsigned long flags;
+	struct resource *parent, *sibling, *child;
+};
+
+#define IORESOURCE_IO           0x00000100      /* Resource type */
+#define IORESOURCE_MEM		0x00000200
+#endif
+
+void snd_wrapper_request_region(unsigned long from, unsigned long extent, const char *name);
+#undef request_region
+#define request_region snd_compat_request_region
+#define release_resource snd_compat_release_resource
+#define request_mem_region(from, size, name) (&snd_compat_mem_region)
+
+extern struct resource snd_compat_mem_region;
+
+struct resource *snd_compat_request_region(unsigned long start, unsigned long size, const char *name);
+int snd_compat_release_resource(struct resource *resource);
+
+#if 0
+//#ifdef CONFIG_PCI
+
+/* New-style probing supporting hot-pluggable devices */
+
+#define PCI_PM_CTRL		4	/* PM control and status register */
+#define PCI_PM_CTRL_STATE_MASK	0x0003	/* Current power state (D0 to D3) */
+
+#define PCI_ANY_ID (~0)
+
+#define PCI_GET_DRIVER_DATA snd_pci_compat_get_driver_data
+#define PCI_SET_DRIVER_DATA snd_pci_compat_set_driver_data
+
+#define pci_set_dma_mask snd_pci_compat_set_dma_mask
+
+#define pci_enable_device snd_pci_compat_enable_device
+#define pci_register_driver snd_pci_compat_register_driver
+#define pci_unregister_driver snd_pci_compat_unregister_driver
+//#define pci_set_power_state snd_pci_compat_set_power_state
+
+#define pci_alloc_consistent snd_pci_compat_alloc_consistent
+#define pci_free_consistent snd_pci_compat_free_consistent
+#define pci_dma_supported snd_pci_compat_dma_supported
+
+#define pci_dev_g(n) list_entry(n, struct pci_dev, global_list)
+#define pci_dev_b(n) list_entry(n, struct pci_dev, bus_list)
+
+#define pci_for_each_dev(dev) \
+	for(dev = pci_devices; dev; dev = dev->next)
+
+#define pci_resource_start(dev,bar) \
+	(((dev)->base_address[(bar)] & PCI_BASE_ADDRESS_SPACE) ? \
+	 ((dev)->base_address[(bar)] & PCI_BASE_ADDRESS_IO_MASK) : \
+	 ((dev)->base_address[(bar)] & PCI_BASE_ADDRESS_MEM_MASK))
+#define pci_resource_end(dev,bar) \
+	(pci_resource_start(dev,bar) + snd_pci_compat_get_size((dev),(bar)))
+#define pci_resource_flags(dev,bar) (snd_pci_compat_get_flags((dev),(bar)))
+
+struct pci_device_id {
+	unsigned int vendor, device;		/* Vendor and device ID or PCI_ANY_ID */
+	unsigned int subvendor, subdevice;	/* Subsystem ID's or PCI_ANY_ID */
+	unsigned int class, class_mask;		/* (class,subclass,prog-if) triplet */
+	unsigned long driver_data;		/* Data private to the driver */
+};
+
+struct pci_driver {
+	struct list_head node;
+	struct pci_dev *dev;
+	char *name;
+	const struct pci_device_id *id_table;	/* NULL if wants all devices */
+	int (*probe)(struct pci_dev *dev, const struct pci_device_id *id); /* New device inserted */
+	void (*remove)(struct pci_dev *dev);	/* Device removed (NULL if not a hot-plug capable driver) */
+	int (*suspend)(struct pci_dev *dev, u32 stgate);	/* Device suspended */
+	int (*resume)(struct pci_dev *dev);	/* Device woken up */
+};
+
+
+/*
+ *
+ */
+const struct pci_device_id * snd_pci_compat_match_device(const struct pci_device_id *ids, struct pci_dev *dev);
+int snd_pci_compat_register_driver(struct pci_driver *drv);
+void snd_pci_compat_unregister_driver(struct pci_driver *drv);
+unsigned long snd_pci_compat_get_size (struct pci_dev *dev, int n_base);
+int snd_pci_compat_get_flags (struct pci_dev *dev, int n_base);
+int snd_pci_compat_set_power_state(struct pci_dev *dev, int new_state);
+int snd_pci_compat_enable_device(struct pci_dev *dev);
+int snd_pci_compat_find_capability(struct pci_dev *dev, int cap);
+void *snd_pci_compat_alloc_consistent(struct pci_dev *, long, dma_addr_t *);
+void snd_pci_compat_free_consistent(struct pci_dev *, long, void *, dma_addr_t);
+int snd_pci_compat_dma_supported(struct pci_dev *, dma_addr_t mask);
+unsigned long snd_pci_compat_get_dma_mask(struct pci_dev *);
+void snd_pci_compat_set_dma_mask(struct pci_dev *, unsigned long mask);
+void * snd_pci_compat_get_driver_data (struct pci_dev *dev);
+void snd_pci_compat_set_driver_data (struct pci_dev *dev, void *driver_data);
+static inline int pci_module_init(struct pci_driver *drv)
+{
+	int res = snd_pci_compat_register_driver(drv);
+	if (res < 0)
+		return res;
+	if (res == 0)
+		return -ENODEV;
+	return 0;
+}
+#endif /* CONFIG_PCI */
+
+/*
+ * Power management requests
+ */
+enum
+{
+	PM_SUSPEND, /* enter D1-D3 */
+	PM_RESUME,  /* enter D0 */
+
+	/* enable wake-on */
+	PM_SET_WAKEUP,
+
+	/* bus resource management */
+	PM_GET_RESOURCES,
+	PM_SET_RESOURCES,
+
+	/* base station management */
+	PM_EJECT,
+	PM_LOCK,
+};
+
+typedef int pm_request_t;
+
+/*
+ * Device types
+ */
+enum
+{
+	PM_UNKNOWN_DEV = 0, /* generic */
+	PM_SYS_DEV,	    /* system device (fan, KB controller, ...) */
+	PM_PCI_DEV,	    /* PCI device */
+	PM_USB_DEV,	    /* USB device */
+	PM_SCSI_DEV,	    /* SCSI device */
+	PM_ISA_DEV,	    /* ISA device */
+};
+
+typedef int pm_dev_t;
+
+/*
+ * System device hardware ID (PnP) values
+ */
+enum
+{
+	PM_SYS_UNKNOWN = 0x00000000, /* generic */
+	PM_SYS_KBC =	 0x41d00303, /* keyboard controller */
+	PM_SYS_COM =	 0x41d00500, /* serial port */
+	PM_SYS_IRDA =	 0x41d00510, /* IRDA controller */
+	PM_SYS_FDC =	 0x41d00700, /* floppy controller */
+	PM_SYS_VGA =	 0x41d00900, /* VGA controller */
+	PM_SYS_PCMCIA =	 0x41d00e00, /* PCMCIA controller */
+};
+
+/*
+ * Device identifier
+ */
+#define PM_PCI_ID(dev) ((dev)->bus->number << 16 | (dev)->devfn)
+
+/*
+ * Request handler callback
+ */
+struct pm_dev;
+
+typedef int (*pm_callback)(struct pm_dev *dev, pm_request_t rqst, void *data);
+
+/*
+ * Dynamic device information
+ */
+struct pm_dev
+{
+	pm_dev_t	 type;
+	unsigned long	 id;
+	pm_callback	 callback;
+	void		*data;
+
+	unsigned long	 flags;
+	int		 state;
+	int		 prev_state;
+
+	struct list_head entry;
+};
+
+#ifdef CONFIG_APM
+
+int pm_init(void);
+void pm_done(void);
+
+#define CONFIG_PM
+
+#define PM_IS_ACTIVE() 1
+
+/*
+ * Register a device with power management
+ */
+struct pm_dev *pm_register(pm_dev_t type,
+			   unsigned long id,
+			   pm_callback callback);
+
+/*
+ * Unregister a device with power management
+ */
+void pm_unregister(struct pm_dev *dev);
+
+/*
+ * Send a request to a single device
+ */
+int pm_send(struct pm_dev *dev, pm_request_t rqst, void *data);
+
+#else /* CONFIG_APM */
+
+#define PM_IS_ACTIVE() 0
+
+extern inline struct pm_dev *pm_register(pm_dev_t type,
+					 unsigned long id,
+					 pm_callback callback)
+{
+	return 0;
+}
+
+extern inline void pm_unregister(struct pm_dev *dev) {}
+
+extern inline int pm_send(struct pm_dev *dev, pm_request_t rqst, void *data)
+{
+	return 0;
+}
+
+#endif /* CONFIG_APM */
+
+#endif /* <2.3.0 */
+
+/* rw_semaphore - replaced with mutex */
+#define rw_semaphore semaphore
+#define init_rwsem(x) init_MUTEX(x)
+#define DECLARE_RWSEM(x) DECLARE_MUTEX(x)
+#define down_read(x) down(x)
+#define down_write(x) down(x)
+#define up_read(x) up(x)
+#define up_write(x) up(x)
+
+/* this is identical with tq_struct but the "routine" field is renamed to "func" */
+struct tasklet_struct {
+	struct tasklet_struct *next;	/* linked list of active bh's */
+	unsigned long sync;		/* must be initialized to zero */
+	void (*func)(void *);		/* function to call */
+	void *data;			/* argument to function */
+};
+
+#define tasklet_init(t,f,d)	do { \
+	(t)->next = NULL; \
+	(t)->sync = 0; \
+	(t)->func = (void (*)(void *))(f); \
+	(t)->data = (void *)(d); \
+} while (0)
+
+#define tasklet_unlock_wait(t)	while (test_bit(0, &(t)->sync)) { }
+#define tasklet_kill(t)		tasklet_unlock_wait(t) /* FIXME: world is not perfect... */
+
+#ifndef DECLARE_BITMAP
+#define DECLARE_BITMAP(name,bits) \
+        unsigned long name[((bits)+BITS_PER_LONG-1)/BITS_PER_LONG]
+#endif
+#ifndef __user
+#define __user
+#endif
+
+#define ATOMIC_INIT(i)  { (i) }
+
+/**
+ * atomic_dec_and_test - decrement and test
+ * @v: pointer of type atomic_t
+ *
+ * Atomically decrements @v by 1 and
+ * returns true if the result is 0, or false for all other
+ * cases.  Note that the guaranteed
+ * useful range of an atomic_t is only 24 bits.
+ */
+static inline int atomic_dec_and_test(volatile atomic_t *v)
+{
+    atomic_dec(v);
+    if (v->counter == 0)
+        return 1;
+    return 0;
+}
+
+/**
+ * list_for_each_safe	-	iterate over a list safe against removal of list entry
+ * @pos:	the &struct list_head to use as a loop counter.
+ * @n:		another &struct list_head to use as temporary storage
+ * @head:	the head for your list.
+ */
+#define list_for_each_safe(pos, n, head) \
+	for (pos = (head)->next, n = pos->next; pos != (head); \
+		pos = n, n = pos->next)
+
+/**
+ * list_del_init - deletes entry from list and reinitialize it.
+ * @entry: the element to delete from the list.
+ */
+static __inline__ void list_del_init(struct list_head *entry)
+{
+	__list_del(entry->prev, entry->next);
+	INIT_LIST_HEAD(entry); 
+}
+
+#define local_irq_save(flags) \
+    { save_flags(flags); cli(); }
+#define local_irq_restore(flags) \
+	{ restore_flags(flags); }
+
+#ifndef CONFIG_HAVE_PCI_CONSISTENT_DMA_MASK
+#define pci_set_consistent_dma_mask(p,x) pci_set_dma_mask(p,x)
+#endif
+
+struct completion {
+        unsigned int done;
+        wait_queue_head_t wait;
+};
+
+struct workqueue_struct {
+	spinlock_t lock;
+	const char *name;
+	struct list_head worklist;
+	int task_pid;
+	wait_queue_head_t more_work;
+	wait_queue_head_t work_done;
+	struct completion thread_exited;
+};
+
+struct workqueue_struct *create_workqueue(const char *name);
+int queue_work(struct workqueue_struct *wq, struct work_struct *work);
+
+void destroy_workqueue(struct workqueue_struct *wq);
+
+#endif //__COMPAT_22__
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/config.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/config.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/config.h	(revision 91)
@@ -0,0 +1,236 @@
+/*
+ *  Configuration header file for compilation of the ALSA driver
+ */
+
+#ifndef __ALSA_CONFIG_H__
+#define __ALSA_CONFIG_H__
+
+#define inline __inline
+#define __attribute__
+
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/signal.h>
+#include <linux/kdev_t.h>
+#include <linux/wait.h>
+#include <linux/list.h>
+#include <linux/init.h>
+#include <linux/dcache.h>
+#include <linux/vmalloc.h>
+#include <linux/tqueue.h>
+#include <linux/time.h>
+#include <linux/timer.h>
+#include <linux/stat.h>
+#include <linux/major.h>
+#include <linux/byteorder/little_endian.h>
+#include <asm/ioctl.h>
+#include <asm/hardirq.h>
+#include <asm/processor.h>
+#include <asm/siginfo.h>
+#include <dbgos2.h>
+
+extern int this_module[64];
+#define THIS_MODULE (void *)&this_module[0]
+#define MODULE_GENERIC_TABLE(gtype,name)
+#define MODULE_DEVICE_TABLE(type,name)
+#define CONFIG_PROC_FS
+#define CONFIG_PM
+#define PCI_NEW_SUSPEND
+#define CONFIG_PCI
+#define CONFIG_SND_SEQUENCER
+//#define CONFIG_SND_OSSEMUL
+#define SNDRV_LITTLE_ENDIAN
+#define EXPORT_SYMBOL(a)
+#define CONFIG_SOUND
+#define CONFIG_SND_VERSION	"0.0.1"
+#define ATTRIBUTE_UNUSED
+
+#undef interrupt
+
+/*
+ * Power management requests
+ */
+enum
+{
+	PM_SUSPEND, /* enter D1-D3 */
+	PM_RESUME,  /* enter D0 */
+
+	/* enable wake-on */
+	PM_SET_WAKEUP,
+
+	/* bus resource management */
+	PM_GET_RESOURCES,
+	PM_SET_RESOURCES,
+
+	/* base station management */
+	PM_EJECT,
+	PM_LOCK,
+};
+
+typedef int pm_request_t;
+
+/*
+ * Device types
+ */
+enum
+{
+	PM_UNKNOWN_DEV = 0, /* generic */
+	PM_SYS_DEV,	    /* system device (fan, KB controller, ...) */
+	PM_PCI_DEV,	    /* PCI device */
+	PM_USB_DEV,	    /* USB device */
+	PM_SCSI_DEV,	    /* SCSI device */
+	PM_ISA_DEV,	    /* ISA device */
+};
+
+typedef int pm_dev_t;
+
+/*
+ * System device hardware ID (PnP) values
+ */
+enum
+{
+	PM_SYS_UNKNOWN = 0x00000000, /* generic */
+	PM_SYS_KBC =	 0x41d00303, /* keyboard controller */
+	PM_SYS_COM =	 0x41d00500, /* serial port */
+	PM_SYS_IRDA =	 0x41d00510, /* IRDA controller */
+	PM_SYS_FDC =	 0x41d00700, /* floppy controller */
+	PM_SYS_VGA =	 0x41d00900, /* VGA controller */
+	PM_SYS_PCMCIA =	 0x41d00e00, /* PCMCIA controller */
+};
+
+/*
+ * Request handler callback
+ */
+struct pm_dev;
+
+typedef int (*pm_callback)(struct pm_dev *dev, pm_request_t rqst, void *data);
+
+/*
+ * Dynamic device information
+ */
+struct pm_dev
+{
+	pm_dev_t	 type;
+	unsigned long	 id;
+	pm_callback	 callback;
+	void		*data;
+
+	unsigned long	 flags;
+	int		 state;
+	int		 prev_state;
+
+	struct list_head entry;
+};
+
+int pm_init(void);
+void pm_done(void);
+
+#define CONFIG_PM
+
+#define PM_IS_ACTIVE() 1
+
+/*
+ * Register a device with power management
+ */
+struct pm_dev *pm_register(pm_dev_t type,
+			   unsigned long id,
+			   pm_callback callback);
+
+/*
+ * Unregister a device with power management
+ */
+void pm_unregister(struct pm_dev *dev);
+
+/*
+ * Send a request to a single device
+ */
+int pm_send(struct pm_dev *dev, pm_request_t rqst, void *data);
+
+#define fops_get(x) (x)
+#define fops_put(x) do { ; } while (0)
+
+#define __builtin_return_address(a)	0
+#define SetPageReserved(a)		a
+#define ClearPageReserved(a)		a
+#define set_current_state(a)
+#define try_inc_mod_count(x) 	        ++(*(unsigned long *)x)
+#define try_module_get(x) try_inc_mod_count(x)
+static inline void module_put(struct module *module)
+{
+    if (module)
+        do {} while(0);
+}
+
+#define rwlock_init(x) *(x) = RW_LOCK_UNLOCKED;
+
+#define suser()	1
+
+#define snd_kill_fasync(fp, sig, band) kill_fasync(*(fp), sig, band)
+
+
+//what's this??
+#define capable(a)	1
+#define CAP_SYS_ADMIN   0
+
+#define BITS_PER_LONG	32
+
+#ifndef bitmap_member
+#define bitmap_member(name,bits) \
+	unsigned long name[((bits)+BITS_PER_LONG-1)/BITS_PER_LONG]
+#endif
+
+#ifndef snd_card_set_dev
+#define snd_card_set_dev(card,devptr) ((card)->dev = (devptr))
+#endif
+
+/* for easier backward-porting */
+#if defined(CONFIG_GAMEPORT) || defined(CONFIG_GAMEPORT_MODULE)
+#ifndef gameport_set_dev_parent
+#define gameport_set_dev_parent(gp,xdev) ((gp)->dev.parent = (xdev))
+#define gameport_set_port_data(gp,r) ((gp)->port_data = (r))
+#define gameport_get_port_data(gp) (gp)->port_data
+#endif
+#endif
+
+struct snd_minor {
+	struct list_head list;		/* list of all minors per card */
+	int number;			/* minor number */
+	int device;			/* device number */
+	const char *comment;		/* for /proc/asound/devices */
+	struct file_operations *f_ops;	/* file operations */
+	char name[1];			/* device name (keep at the end of
+								structure) */
+};
+
+/* PCI quirk list helper */
+struct snd_pci_quirk {
+        unsigned short subvendor;       /* PCI subvendor ID */
+        unsigned short subdevice;       /* PCI subdevice ID */
+        int value;                      /* value */
+#ifdef CONFIG_SND_DEBUG_DETECT
+        const char *name;               /* name of the device (optional) */
+#endif
+};
+
+#define _SND_PCI_QUIRK_ID(vend,dev) \
+        .subvendor = (vend), .subdevice = (dev)
+#define SND_PCI_QUIRK_ID(vend,dev) {_SND_PCI_QUIRK_ID(vend, dev)}
+#ifdef CONFIG_SND_DEBUG_DETECT
+#define SND_PCI_QUIRK(vend,dev,xname,val) \
+        {_SND_PCI_QUIRK_ID(vend, dev), .value = (val), .name = (xname)}
+#else
+#define SND_PCI_QUIRK(vend,dev,xname,val) \
+        {_SND_PCI_QUIRK_ID(vend, dev), .value = (val)}
+#endif
+
+const struct snd_pci_quirk *
+snd_pci_quirk_lookup(struct pci_dev *pci, const struct snd_pci_quirk *list);
+
+/* misc.c */
+struct resource;
+void release_and_free_resource(struct resource *res);
+
+#include "typedefs.h"
+
+#endif //__ALSA_CONFIG_H__
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/config.h.in
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/config.h.in	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/config.h.in	(revision 91)
@@ -0,0 +1,4 @@
+/*
+ *  Configuration header file for compilation of the ALSA driver
+ */
+
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/control.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/control.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/control.h	(revision 91)
@@ -0,0 +1,162 @@
+#ifndef __CONTROL_H
+#define __CONTROL_H
+
+/*
+ *  Header file for control interface
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#define snd_kcontrol_chip(kcontrol) ((kcontrol)->private_data)
+struct snd_kcontrol;
+typedef int (snd_kcontrol_info_t) (struct snd_kcontrol * kcontrol, struct snd_ctl_elem_info * uinfo);
+typedef int (snd_kcontrol_get_t) (struct snd_kcontrol * kcontrol, struct snd_ctl_elem_value * ucontrol);
+typedef int (snd_kcontrol_put_t) (struct snd_kcontrol * kcontrol, struct snd_ctl_elem_value * ucontrol);
+typedef int (snd_kcontrol_tlv_rw_t)(struct snd_kcontrol *kcontrol,
+				    int op_flag, /* 0=read,1=write,-1=command */
+				    unsigned int size,
+				    unsigned int __user *tlv);
+
+struct snd_kcontrol_new {
+    snd_ctl_elem_iface_t iface;	/* interface identifier */
+    unsigned int device;		/* device/client number */
+    unsigned int subdevice;		/* subdevice (substream) number */
+    unsigned char *name;		/* ASCII name of item */
+    unsigned int index;		/* index of item */
+    unsigned int access;		/* access rights */
+    unsigned int count;             /* count of same elements */
+    snd_kcontrol_info_t *info;
+    snd_kcontrol_get_t *get;
+    snd_kcontrol_put_t *put;
+    union {
+        snd_kcontrol_tlv_rw_t *c;
+        unsigned int *p;
+    } tlv;
+    unsigned long private_value;
+};
+
+struct snd_kcontrol_volatile {
+    struct snd_ctl_file *owner;     /* locked */
+    pid_t owner_pid;
+    unsigned int access;    /* access rights */
+};
+
+struct snd_kcontrol {
+    struct list_head list;		/* list of controls */
+    struct snd_ctl_elem_id id;
+	unsigned int count;		/* count of same elements */
+	snd_kcontrol_info_t *info;
+	snd_kcontrol_get_t *get;
+        snd_kcontrol_put_t *put;
+	union {
+		snd_kcontrol_tlv_rw_t *c;
+		unsigned int *p;
+	} tlv;
+	unsigned long private_value;
+#ifdef TARGET_OS2
+        void *private_ptr;
+#endif
+        void *private_data;
+        void (*private_free)(struct snd_kcontrol *kcontrol);
+        struct snd_kcontrol_volatile vd[1];     /* volatile data */
+        //snd_kcontrol_volatile_t vd[1];  /* volatile data */
+};
+
+#define snd_kcontrol(n) list_entry(n, struct snd_kcontrol, list)
+
+struct snd_kctl_event {
+    struct list_head list;	/* list of events */
+    struct snd_ctl_elem_id id;
+    unsigned int mask;
+};
+
+#define snd_kctl_event(n) list_entry(n, struct snd_kctl_event, list)
+
+struct snd_ctl_file {
+    struct list_head list;		/* list of all control files */
+    struct snd_card *card;
+	pid_t pid;
+	int prefer_pcm_subdevice;
+	int prefer_rawmidi_subdevice;
+	wait_queue_head_t change_sleep;
+	spinlock_t read_lock;
+	struct fasync_struct *fasync;
+	int subscribed;			/* read interface is activated */
+	struct list_head events;	/* waiting events for read */
+};
+
+#define snd_ctl_file(n) list_entry(n, struct snd_ctl_file, list)
+
+typedef int (*snd_kctl_ioctl_func_t) (struct snd_card * card,
+                                      struct snd_ctl_file * control,
+                                      unsigned int cmd, unsigned long arg);
+
+void snd_ctl_notify(struct snd_card * card, unsigned int mask, struct snd_ctl_elem_id * id);
+
+struct snd_kcontrol *snd_ctl_new(struct snd_kcontrol * kcontrol, unsigned int access);
+struct snd_kcontrol *snd_ctl_new1(const struct snd_kcontrol_new * kcontrolnew, void * private_data);
+void snd_ctl_free_one(struct snd_kcontrol * kcontrol);
+int snd_ctl_add(struct snd_card * card, struct snd_kcontrol * kcontrol);
+int snd_ctl_remove(struct snd_card * card, struct snd_kcontrol * kcontrol);
+int snd_ctl_remove_id(struct snd_card * card, struct snd_ctl_elem_id *id);
+int snd_ctl_rename_id(struct snd_card * card, struct snd_ctl_elem_id *src_id, struct snd_ctl_elem_id *dst_id);
+struct snd_kcontrol *snd_ctl_find_numid(struct snd_card * card, unsigned int numid);
+struct snd_kcontrol *snd_ctl_find_id(struct snd_card * card, struct snd_ctl_elem_id *id);
+
+int snd_ctl_create(struct snd_card *card);
+
+int snd_ctl_register(struct snd_card *card);
+int snd_ctl_disconnect(struct snd_card *card);
+int snd_ctl_can_unregister(struct snd_card *card);
+int snd_ctl_unregister(struct snd_card *card);
+int snd_ctl_register_ioctl(snd_kctl_ioctl_func_t fcn);
+int snd_ctl_unregister_ioctl(snd_kctl_ioctl_func_t fcn);
+
+int snd_ctl_elem_read(struct snd_card *card, struct snd_ctl_elem_value *control);
+int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file, struct snd_ctl_elem_value *control);
+
+static inline unsigned int snd_ctl_get_ioffnum(struct snd_kcontrol *kctl, struct snd_ctl_elem_id *id)
+{
+	return id->numid - kctl->id.numid;
+}
+
+static inline unsigned int snd_ctl_get_ioffidx(struct snd_kcontrol *kctl, struct snd_ctl_elem_id *id)
+{
+	return id->index - kctl->id.index;
+}
+
+static inline unsigned int snd_ctl_get_ioff(struct snd_kcontrol *kctl, struct snd_ctl_elem_id *id)
+{
+	if (id->numid) {
+		return snd_ctl_get_ioffnum(kctl, id);
+	} else {
+		return snd_ctl_get_ioffidx(kctl, id);
+	}
+}
+
+static inline struct snd_ctl_elem_id *snd_ctl_build_ioff(struct snd_ctl_elem_id *dst_id,
+						    struct snd_kcontrol *src_kctl,
+						    unsigned int offset)
+{
+	*dst_id = src_kctl->id;
+	dst_id->index += offset;
+	dst_id->numid += offset;
+	return dst_id;
+}
+
+#endif				/* __CONTROL_H */
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/core.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/core.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/core.h	(revision 91)
@@ -0,0 +1,10 @@
+/*
+ *  Configuration header file for compilation of the ALSA driver
+ */
+
+#ifndef __ALSA_CORE_H__
+#define __ALSA_CORE_H__
+
+#include <sound\config.h>
+
+#endif //__ALSA_CORE_H__
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/cs4231.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/cs4231.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/cs4231.h	(revision 91)
@@ -0,0 +1,339 @@
+#ifndef __CS4231_H
+#define __CS4231_H
+
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  Definitions for CS4231 & InterWave chips & compatible chips
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "control.h"
+#include "pcm.h"
+#include "timer.h"
+
+/* IO ports */
+
+#define CS4231P(chip, x)	((chip)->port + c_d_c_CS4231##x)
+
+#define c_d_c_CS4231REGSEL	0
+#define c_d_c_CS4231REG		1
+#define c_d_c_CS4231STATUS	2
+#define c_d_c_CS4231PIO		3
+
+/* codec registers */
+
+#define CS4231_LEFT_INPUT	0x00	/* left input control */
+#define CS4231_RIGHT_INPUT	0x01	/* right input control */
+#define CS4231_AUX1_LEFT_INPUT	0x02	/* left AUX1 input control */
+#define CS4231_AUX1_RIGHT_INPUT	0x03	/* right AUX1 input control */
+#define CS4231_AUX2_LEFT_INPUT	0x04	/* left AUX2 input control */
+#define CS4231_AUX2_RIGHT_INPUT	0x05	/* right AUX2 input control */
+#define CS4231_LEFT_OUTPUT	0x06	/* left output control register */
+#define CS4231_RIGHT_OUTPUT	0x07	/* right output control register */
+#define CS4231_PLAYBK_FORMAT	0x08	/* clock and data format - playback - bits 7-0 MCE */
+#define CS4231_IFACE_CTRL	0x09	/* interface control - bits 7-2 MCE */
+#define CS4231_PIN_CTRL		0x0a	/* pin control */
+#define CS4231_TEST_INIT	0x0b	/* test and initialization */
+#define CS4231_MISC_INFO	0x0c	/* miscellaneaous information */
+#define CS4231_LOOPBACK		0x0d	/* loopback control */
+#define CS4231_PLY_UPR_CNT	0x0e	/* playback upper base count */
+#define CS4231_PLY_LWR_CNT	0x0f	/* playback lower base count */
+#define CS4231_ALT_FEATURE_1	0x10	/* alternate #1 feature enable */
+#define CS4231_ALT_FEATURE_2	0x11	/* alternate #2 feature enable */
+#define CS4231_LEFT_LINE_IN	0x12	/* left line input control */
+#define CS4231_RIGHT_LINE_IN	0x13	/* right line input control */
+#define CS4231_TIMER_LOW	0x14	/* timer low byte */
+#define CS4231_TIMER_HIGH	0x15	/* timer high byte */
+#define CS4231_LEFT_MIC_INPUT	0x16	/* left MIC input control register (InterWave only) */
+#define CS4231_RIGHT_MIC_INPUT	0x17	/* right MIC input control register (InterWave only) */
+#define CS4236_EXT_REG		0x17	/* extended register access */
+#define CS4231_IRQ_STATUS	0x18	/* irq status register */
+#define CS4231_LINE_LEFT_OUTPUT	0x19	/* left line output control register (InterWave only) */
+#define CS4231_VERSION		0x19	/* CS4231(A) - version values */
+#define CS4231_MONO_CTRL	0x1a	/* mono input/output control */
+#define CS4231_LINE_RIGHT_OUTPUT 0x1b	/* right line output control register (InterWave only) */
+#define CS4235_LEFT_MASTER	0x1b	/* left master output control */
+#define CS4231_REC_FORMAT	0x1c	/* clock and data format - record - bits 7-0 MCE */
+#define CS4231_PLY_VAR_FREQ	0x1d	/* playback variable frequency */
+#define CS4235_RIGHT_MASTER	0x1d	/* right master output control */
+#define CS4231_REC_UPR_CNT	0x1e	/* record upper count */
+#define CS4231_REC_LWR_CNT	0x1f	/* record lower count */
+
+/* definitions for codec register select port - CODECP( REGSEL ) */
+
+#define CS4231_INIT		0x80	/* CODEC is initializing */
+#define CS4231_MCE		0x40	/* mode change enable */
+#define CS4231_TRD		0x20	/* transfer request disable */
+
+/* definitions for codec status register - CODECP( STATUS ) */
+
+#define CS4231_GLOBALIRQ	0x01	/* IRQ is active */
+
+/* definitions for codec irq status */
+
+#define CS4231_PLAYBACK_IRQ	0x10
+#define CS4231_RECORD_IRQ	0x20
+#define CS4231_TIMER_IRQ	0x40
+#define CS4231_ALL_IRQS		0x70
+#define CS4231_REC_UNDERRUN	0x08
+#define CS4231_REC_OVERRUN	0x04
+#define CS4231_PLY_OVERRUN	0x02
+#define CS4231_PLY_UNDERRUN	0x01
+
+/* definitions for CS4231_LEFT_INPUT and CS4231_RIGHT_INPUT registers */
+
+#define CS4231_ENABLE_MIC_GAIN	0x20
+
+#define CS4231_MIXS_LINE	0x00
+#define CS4231_MIXS_AUX1	0x40
+#define CS4231_MIXS_MIC		0x80
+#define CS4231_MIXS_ALL		0xc0
+
+/* definitions for clock and data format register - CS4231_PLAYBK_FORMAT */
+
+#define CS4231_LINEAR_8		0x00	/* 8-bit unsigned data */
+#define CS4231_ALAW_8		0x60	/* 8-bit A-law companded */
+#define CS4231_ULAW_8		0x20	/* 8-bit U-law companded */
+#define CS4231_LINEAR_16	0x40	/* 16-bit twos complement data - little endian */
+#define CS4231_LINEAR_16_BIG	0xc0	/* 16-bit twos complement data - big endian */
+#define CS4231_ADPCM_16		0xa0	/* 16-bit ADPCM */
+#define CS4231_STEREO		0x10	/* stereo mode */
+/* bits 3-1 define frequency divisor */
+#define CS4231_XTAL1		0x00	/* 24.576 crystal */
+#define CS4231_XTAL2		0x01	/* 16.9344 crystal */
+
+/* definitions for interface control register - CS4231_IFACE_CTRL */
+
+#define CS4231_RECORD_PIO	0x80	/* record PIO enable */
+#define CS4231_PLAYBACK_PIO	0x40	/* playback PIO enable */
+#define CS4231_CALIB_MODE	0x18	/* calibration mode bits */
+#define CS4231_AUTOCALIB	0x08	/* auto calibrate */
+#define CS4231_SINGLE_DMA	0x04	/* use single DMA channel */
+#define CS4231_RECORD_ENABLE	0x02	/* record enable */
+#define CS4231_PLAYBACK_ENABLE	0x01	/* playback enable */
+
+/* definitions for pin control register - CS4231_PIN_CTRL */
+
+#define CS4231_IRQ_ENABLE	0x02	/* enable IRQ */
+#define CS4231_XCTL1		0x40	/* external control #1 */
+#define CS4231_XCTL0		0x80	/* external control #0 */
+
+/* definitions for test and init register - CS4231_TEST_INIT */
+
+#define CS4231_CALIB_IN_PROGRESS 0x20	/* auto calibrate in progress */
+#define CS4231_DMA_REQUEST	0x10	/* DMA request in progress */
+
+/* definitions for misc control register - CS4231_MISC_INFO */
+
+#define CS4231_MODE2		0x40	/* MODE 2 */
+#define CS4231_IW_MODE3		0x6c	/* MODE 3 - InterWave enhanced mode */
+#define CS4231_4236_MODE3	0xe0	/* MODE 3 - CS4236+ enhanced mode */
+
+/* definitions for alternate feature 1 register - CS4231_ALT_FEATURE_1 */
+
+#define	CS4231_DACZ		0x01	/* zero DAC when underrun */
+#define CS4231_TIMER_ENABLE	0x40	/* codec timer enable */
+#define CS4231_OLB		0x80	/* output level bit */
+
+/* definitions for Extended Registers - CS4236+ */
+
+#define CS4236_REG(i23val)	(((i23val << 2) & 0x10) | ((i23val >> 4) & 0x0f))
+#define CS4236_I23VAL(reg)	((((reg)&0xf) << 4) | (((reg)&0x10) >> 2) | 0x8)
+
+#define CS4236_LEFT_LINE	0x08	/* left LINE alternate volume */
+#define CS4236_RIGHT_LINE	0x18	/* right LINE alternate volume */
+#define CS4236_LEFT_MIC		0x28	/* left MIC volume */
+#define CS4236_RIGHT_MIC	0x38	/* right MIC volume */
+#define CS4236_LEFT_MIX_CTRL	0x48	/* synthesis and left input mixer control */
+#define CS4236_RIGHT_MIX_CTRL	0x58	/* right input mixer control */
+#define CS4236_LEFT_FM		0x68	/* left FM volume */
+#define CS4236_RIGHT_FM		0x78	/* right FM volume */
+#define CS4236_LEFT_DSP		0x88	/* left DSP serial port volume */
+#define CS4236_RIGHT_DSP	0x98	/* right DSP serial port volume */
+#define CS4236_RIGHT_LOOPBACK	0xa8	/* right loopback monitor volume */
+#define CS4236_DAC_MUTE		0xb8	/* DAC mute and IFSE enable */
+#define CS4236_ADC_RATE		0xc8	/* indenpendent ADC sample frequency */
+#define CS4236_DAC_RATE		0xd8	/* indenpendent DAC sample frequency */
+#define CS4236_LEFT_MASTER	0xe8	/* left master digital audio volume */
+#define CS4236_RIGHT_MASTER	0xf8	/* right master digital audio volume */
+#define CS4236_LEFT_WAVE	0x0c	/* left wavetable serial port volume */
+#define CS4236_RIGHT_WAVE	0x1c	/* right wavetable serial port volume */
+#define CS4236_VERSION		0x9c	/* chip version and ID */
+
+/* defines for codec.mode */
+
+#define CS4231_MODE_NONE	0x0000
+#define CS4231_MODE_PLAY	0x0001
+#define CS4231_MODE_RECORD	0x0002
+#define CS4231_MODE_TIMER	0x0004
+#define CS4231_MODE_OPEN	(CS4231_MODE_PLAY|CS4231_MODE_RECORD|CS4231_MODE_TIMER)
+
+/* defines for codec.hardware */
+
+#define CS4231_HW_DETECT        0x0000	/* let CS4231 driver detect chip */
+#define CS4231_HW_DETECT3	0x0001	/* allow mode 3 */
+#define CS4231_HW_TYPE_MASK	0xff00	/* type mask */
+#define CS4231_HW_CS4231_MASK   0x0100	/* CS4231 serie */
+#define CS4231_HW_CS4231        0x0100	/* CS4231 chip */
+#define CS4231_HW_CS4231A       0x0101	/* CS4231A chip */
+#define CS4231_HW_CS4232_MASK   0x0200	/* CS4232 serie (has control ports) */
+#define CS4231_HW_CS4232        0x0200	/* CS4232 */
+#define CS4231_HW_CS4232A       0x0201	/* CS4232A */
+#define CS4231_HW_CS4236	0x0202	/* CS4236 */
+#define CS4231_HW_CS4236B_MASK	0x0400	/* CS4236B serie (has extended control regs) */
+#define CS4231_HW_CS4235	0x0400	/* CS4235 - Crystal Clear (tm) stereo enhancement */
+#define CS4231_HW_CS4236B       0x0401	/* CS4236B */
+#define CS4231_HW_CS4237B       0x0402	/* CS4237B - SRS 3D */
+#define CS4231_HW_CS4238B	0x0403	/* CS4238B - QSOUND 3D */
+#define CS4231_HW_CS4239	0x0404	/* CS4239 - Crystal Clear (tm) stereo enhancement */
+/* compatible, but clones */
+#define CS4231_HW_INTERWAVE     0x1000	/* InterWave chip */
+#define CS4231_HW_OPL3SA2       0x1001	/* OPL3-SA2 chip */
+
+/* defines for codec.hwshare */
+#define CS4231_HWSHARE_IRQ	(1<<0)
+#define CS4231_HWSHARE_DMA1	(1<<1)
+#define CS4231_HWSHARE_DMA2	(1<<2)
+
+typedef struct _snd_cs4231 cs4231_t;
+
+struct _snd_cs4231 {
+	unsigned long port;		/* base i/o port */
+	struct resource *res_port;
+	unsigned long cport;		/* control base i/o port (CS4236) */
+	struct resource *res_cport;
+	int irq;			/* IRQ line */
+	int dma1;			/* playback DMA */
+	int dma2;			/* record DMA */
+	unsigned short version;		/* version of CODEC chip */
+	unsigned short mode;		/* see to CS4231_MODE_XXXX */
+	unsigned short hardware;	/* see to CS4231_HW_XXXX */
+	unsigned short hwshare;		/* shared resources */
+	unsigned short single_dma:1;	/* forced single DMA mode (GUS 16-bit daughter board) or dma1 == dma2 */
+
+	snd_card_t *card;
+	snd_pcm_t *pcm;
+	snd_pcm_substream_t *playback_substream;
+	snd_pcm_substream_t *capture_substream;
+	snd_timer_t *timer;
+
+	unsigned char image[32];	/* registers image */
+	unsigned char eimage[32];	/* extended registers image */
+	unsigned char cimage[16];	/* control registers image */
+	int mce_bit;
+	int calibrate_mute;
+	int sw_3d_bit;
+	unsigned int p_dma_size;
+	unsigned int c_dma_size;
+
+	spinlock_t reg_lock;
+	struct semaphore mce_mutex;
+	struct semaphore open_mutex;
+
+	int (*rate_constraint) (snd_pcm_runtime_t *runtime);
+	void (*set_playback_format) (cs4231_t *chip, snd_pcm_hw_params_t *hw_params, unsigned char pdfr);
+	void (*set_capture_format) (cs4231_t *chip, snd_pcm_hw_params_t *hw_params, unsigned char cdfr);
+#ifdef CONFIG_PM
+	void (*suspend) (cs4231_t *chip);
+	void (*resume) (cs4231_t *chip);
+#endif
+	void *dma_private_data;
+	int (*claim_dma) (cs4231_t *chip, void *dma_private_data, int dma);
+	int (*release_dma) (cs4231_t *chip, void *dma_private_data, int dma);
+};
+
+/* exported functions */
+
+void snd_cs4231_out(cs4231_t *chip, unsigned char reg, unsigned char val);
+unsigned char snd_cs4231_in(cs4231_t *chip, unsigned char reg);
+void snd_cs4231_outm(cs4231_t *chip, unsigned char reg, unsigned char mask, unsigned char val);
+void snd_cs4236_ext_out(cs4231_t *chip, unsigned char reg, unsigned char val);
+unsigned char snd_cs4236_ext_in(cs4231_t *chip, unsigned char reg);
+void snd_cs4231_mce_up(cs4231_t *chip);
+void snd_cs4231_mce_down(cs4231_t *chip);
+
+void snd_cs4231_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+
+const char *snd_cs4231_chip_id(cs4231_t *chip);
+
+int snd_cs4231_create(snd_card_t * card,
+		      unsigned long port,
+		      unsigned long cport,
+		      int irq, int dma1, int dma2,
+		      unsigned short hardware,
+		      unsigned short hwshare,
+		      cs4231_t ** rchip);
+int snd_cs4231_pcm(cs4231_t * chip, int device, snd_pcm_t **rpcm);
+int snd_cs4231_timer(cs4231_t * chip, int device, snd_timer_t **rtimer);
+int snd_cs4231_mixer(cs4231_t * chip);
+
+int snd_cs4236_create(snd_card_t * card,
+		      unsigned long port,
+		      unsigned long cport,
+		      int irq, int dma1, int dma2,
+		      unsigned short hardware,
+		      unsigned short hwshare,
+		      cs4231_t ** rchip);
+int snd_cs4236_pcm(cs4231_t * chip, int device, snd_pcm_t **rpcm);
+int snd_cs4236_mixer(cs4231_t * chip);
+
+/*
+ *  mixer library
+ */
+
+#ifdef TARGET_OS2
+#define CS4231_SINGLE(xname, xindex, reg, shift, mask, invert) \
+{ SNDRV_CTL_ELEM_IFACE_MIXER, 0,0, xname, xindex, \
+  0, 0, snd_cs4231_info_single, \
+  snd_cs4231_get_single, snd_cs4231_put_single, \
+  reg | (shift << 8) | (mask << 16) | (invert << 24) }
+#else
+#define CS4231_SINGLE(xname, xindex, reg, shift, mask, invert) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
+  info: snd_cs4231_info_single, \
+  get: snd_cs4231_get_single, put: snd_cs4231_put_single, \
+  private_value: reg | (shift << 8) | (mask << 16) | (invert << 24) }
+#endif
+
+int snd_cs4231_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo);
+int snd_cs4231_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol);
+int snd_cs4231_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol);
+
+#ifdef TARGET_OS2
+#define CS4231_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \
+{ SNDRV_CTL_ELEM_IFACE_MIXER, 0, 0, xname, xindex, \
+  0, 0, snd_cs4231_info_double, \
+  snd_cs4231_get_double, snd_cs4231_put_double, \
+  left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
+#else
+#define CS4231_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
+  info: snd_cs4231_info_double, \
+  get: snd_cs4231_get_double, put: snd_cs4231_put_double, \
+  private_value: left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
+#endif
+
+int snd_cs4231_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo);
+int snd_cs4231_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol);
+int snd_cs4231_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol);
+
+#ifdef CONFIG_SND_DEBUG
+void snd_cs4231_debug(cs4231_t *chip);
+#endif
+
+#endif				/* __CS4231_H */
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/cs46xx.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/cs46xx.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/cs46xx.h	(revision 91)
@@ -0,0 +1,1743 @@
+#ifndef __SOUND_CS46XX_H
+#define __SOUND_CS46XX_H
+
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>,
+ *		     Cirrus Logic, Inc.
+ *  Definitions for Cirrus Logic CS46xx chips
+ *
+ *
+ *   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 "pcm.h"
+#include "pcm-indirect.h"
+#include "rawmidi.h"
+#include "ac97_codec.h"
+#include "cs46xx_dsp_spos.h"
+
+/*
+ *  Direct registers
+ */
+
+/*
+ *  The following define the offsets of the registers accessed via base address
+ *  register zero on the CS46xx part.
+ */
+#define BA0_HISR				0x00000000
+#define BA0_HSR0                                0x00000004
+#define BA0_HICR                                0x00000008
+#define BA0_DMSR                                0x00000100
+#define BA0_HSAR                                0x00000110
+#define BA0_HDAR                                0x00000114
+#define BA0_HDMR                                0x00000118
+#define BA0_HDCR                                0x0000011C
+#define BA0_PFMC                                0x00000200
+#define BA0_PFCV1                               0x00000204
+#define BA0_PFCV2                               0x00000208
+#define BA0_PCICFG00                            0x00000300
+#define BA0_PCICFG04                            0x00000304
+#define BA0_PCICFG08                            0x00000308
+#define BA0_PCICFG0C                            0x0000030C
+#define BA0_PCICFG10                            0x00000310
+#define BA0_PCICFG14                            0x00000314
+#define BA0_PCICFG18                            0x00000318
+#define BA0_PCICFG1C                            0x0000031C
+#define BA0_PCICFG20                            0x00000320
+#define BA0_PCICFG24                            0x00000324
+#define BA0_PCICFG28                            0x00000328
+#define BA0_PCICFG2C                            0x0000032C
+#define BA0_PCICFG30                            0x00000330
+#define BA0_PCICFG34                            0x00000334
+#define BA0_PCICFG38                            0x00000338
+#define BA0_PCICFG3C                            0x0000033C
+#define BA0_CLKCR1                              0x00000400
+#define BA0_CLKCR2                              0x00000404
+#define BA0_PLLM                                0x00000408
+#define BA0_PLLCC                               0x0000040C
+#define BA0_FRR                                 0x00000410 
+#define BA0_CFL1                                0x00000414
+#define BA0_CFL2                                0x00000418
+#define BA0_SERMC1                              0x00000420
+#define BA0_SERMC2                              0x00000424
+#define BA0_SERC1                               0x00000428
+#define BA0_SERC2                               0x0000042C
+#define BA0_SERC3                               0x00000430
+#define BA0_SERC4                               0x00000434
+#define BA0_SERC5                               0x00000438
+#define BA0_SERBSP                              0x0000043C
+#define BA0_SERBST                              0x00000440
+#define BA0_SERBCM                              0x00000444
+#define BA0_SERBAD                              0x00000448
+#define BA0_SERBCF                              0x0000044C
+#define BA0_SERBWP                              0x00000450
+#define BA0_SERBRP                              0x00000454
+#ifndef NO_CS4612
+#define BA0_ASER_FADDR                          0x00000458
+#endif
+#define BA0_ACCTL                               0x00000460
+#define BA0_ACSTS                               0x00000464
+#define BA0_ACOSV                               0x00000468
+#define BA0_ACCAD                               0x0000046C
+#define BA0_ACCDA                               0x00000470
+#define BA0_ACISV                               0x00000474
+#define BA0_ACSAD                               0x00000478
+#define BA0_ACSDA                               0x0000047C
+#define BA0_JSPT                                0x00000480
+#define BA0_JSCTL                               0x00000484
+#define BA0_JSC1                                0x00000488
+#define BA0_JSC2                                0x0000048C
+#define BA0_MIDCR                               0x00000490
+#define BA0_MIDSR                               0x00000494
+#define BA0_MIDWP                               0x00000498
+#define BA0_MIDRP                               0x0000049C
+#define BA0_JSIO                                0x000004A0
+#ifndef NO_CS4612
+#define BA0_ASER_MASTER                         0x000004A4
+#endif
+#define BA0_CFGI                                0x000004B0
+#define BA0_SSVID                               0x000004B4
+#define BA0_GPIOR                               0x000004B8
+#ifndef NO_CS4612
+#define BA0_EGPIODR                             0x000004BC
+#define BA0_EGPIOPTR                            0x000004C0
+#define BA0_EGPIOTR                             0x000004C4
+#define BA0_EGPIOWR                             0x000004C8
+#define BA0_EGPIOSR                             0x000004CC
+#define BA0_SERC6                               0x000004D0
+#define BA0_SERC7                               0x000004D4
+#define BA0_SERACC                              0x000004D8
+#define BA0_ACCTL2                              0x000004E0
+#define BA0_ACSTS2                              0x000004E4
+#define BA0_ACOSV2                              0x000004E8
+#define BA0_ACCAD2                              0x000004EC
+#define BA0_ACCDA2                              0x000004F0
+#define BA0_ACISV2                              0x000004F4
+#define BA0_ACSAD2                              0x000004F8
+#define BA0_ACSDA2                              0x000004FC
+#define BA0_IOTAC0                              0x00000500
+#define BA0_IOTAC1                              0x00000504
+#define BA0_IOTAC2                              0x00000508
+#define BA0_IOTAC3                              0x0000050C
+#define BA0_IOTAC4                              0x00000510
+#define BA0_IOTAC5                              0x00000514
+#define BA0_IOTAC6                              0x00000518
+#define BA0_IOTAC7                              0x0000051C
+#define BA0_IOTAC8                              0x00000520
+#define BA0_IOTAC9                              0x00000524
+#define BA0_IOTAC10                             0x00000528
+#define BA0_IOTAC11                             0x0000052C
+#define BA0_IOTFR0                              0x00000540
+#define BA0_IOTFR1                              0x00000544
+#define BA0_IOTFR2                              0x00000548
+#define BA0_IOTFR3                              0x0000054C
+#define BA0_IOTFR4                              0x00000550
+#define BA0_IOTFR5                              0x00000554
+#define BA0_IOTFR6                              0x00000558
+#define BA0_IOTFR7                              0x0000055C
+#define BA0_IOTFIFO                             0x00000580
+#define BA0_IOTRRD                              0x00000584
+#define BA0_IOTFP                               0x00000588
+#define BA0_IOTCR                               0x0000058C
+#define BA0_DPCID                               0x00000590
+#define BA0_DPCIA                               0x00000594
+#define BA0_DPCIC                               0x00000598
+#define BA0_PCPCIR                              0x00000600
+#define BA0_PCPCIG                              0x00000604
+#define BA0_PCPCIEN                             0x00000608
+#define BA0_EPCIPMC                             0x00000610
+#endif
+
+/*
+ *  The following define the offsets of the registers and memories accessed via
+ *  base address register one on the CS46xx part.
+ */
+#define BA1_SP_DMEM0                            0x00000000
+#define BA1_SP_DMEM1                            0x00010000
+#define BA1_SP_PMEM                             0x00020000
+#define BA1_SP_REG				0x00030000
+#define BA1_SPCR                                0x00030000
+#define BA1_DREG                                0x00030004
+#define BA1_DSRWP                               0x00030008
+#define BA1_TWPR                                0x0003000C
+#define BA1_SPWR                                0x00030010
+#define BA1_SPIR                                0x00030014
+#define BA1_FGR1                                0x00030020
+#define BA1_SPCS                                0x00030028
+#define BA1_SDSR                                0x0003002C
+#define BA1_FRMT                                0x00030030
+#define BA1_FRCC                                0x00030034
+#define BA1_FRSC                                0x00030038
+#define BA1_OMNI_MEM                            0x000E0000
+
+
+/*
+ *  The following defines are for the flags in the host interrupt status
+ *  register.
+ */
+#define HISR_VC_MASK                            0x0000FFFF
+#define HISR_VC0                                0x00000001
+#define HISR_VC1                                0x00000002
+#define HISR_VC2                                0x00000004
+#define HISR_VC3                                0x00000008
+#define HISR_VC4                                0x00000010
+#define HISR_VC5                                0x00000020
+#define HISR_VC6                                0x00000040
+#define HISR_VC7                                0x00000080
+#define HISR_VC8                                0x00000100
+#define HISR_VC9                                0x00000200
+#define HISR_VC10                               0x00000400
+#define HISR_VC11                               0x00000800
+#define HISR_VC12                               0x00001000
+#define HISR_VC13                               0x00002000
+#define HISR_VC14                               0x00004000
+#define HISR_VC15                               0x00008000
+#define HISR_INT0                               0x00010000
+#define HISR_INT1                               0x00020000
+#define HISR_DMAI                               0x00040000
+#define HISR_FROVR                              0x00080000
+#define HISR_MIDI                               0x00100000
+#ifdef NO_CS4612
+#define HISR_RESERVED                           0x0FE00000
+#else
+#define HISR_SBINT                              0x00200000
+#define HISR_RESERVED                           0x0FC00000
+#endif
+#define HISR_H0P                                0x40000000
+#define HISR_INTENA                             0x80000000
+
+/*
+ *  The following defines are for the flags in the host signal register 0.
+ */
+#define HSR0_VC_MASK                            0xFFFFFFFF
+#define HSR0_VC16                               0x00000001
+#define HSR0_VC17                               0x00000002
+#define HSR0_VC18                               0x00000004
+#define HSR0_VC19                               0x00000008
+#define HSR0_VC20                               0x00000010
+#define HSR0_VC21                               0x00000020
+#define HSR0_VC22                               0x00000040
+#define HSR0_VC23                               0x00000080
+#define HSR0_VC24                               0x00000100
+#define HSR0_VC25                               0x00000200
+#define HSR0_VC26                               0x00000400
+#define HSR0_VC27                               0x00000800
+#define HSR0_VC28                               0x00001000
+#define HSR0_VC29                               0x00002000
+#define HSR0_VC30                               0x00004000
+#define HSR0_VC31                               0x00008000
+#define HSR0_VC32                               0x00010000
+#define HSR0_VC33                               0x00020000
+#define HSR0_VC34                               0x00040000
+#define HSR0_VC35                               0x00080000
+#define HSR0_VC36                               0x00100000
+#define HSR0_VC37                               0x00200000
+#define HSR0_VC38                               0x00400000
+#define HSR0_VC39                               0x00800000
+#define HSR0_VC40                               0x01000000
+#define HSR0_VC41                               0x02000000
+#define HSR0_VC42                               0x04000000
+#define HSR0_VC43                               0x08000000
+#define HSR0_VC44                               0x10000000
+#define HSR0_VC45                               0x20000000
+#define HSR0_VC46                               0x40000000
+#define HSR0_VC47                               0x80000000
+
+/*
+ *  The following defines are for the flags in the host interrupt control
+ *  register.
+ */
+#define HICR_IEV                                0x00000001
+#define HICR_CHGM                               0x00000002
+
+/*
+ *  The following defines are for the flags in the DMA status register.
+ */
+#define DMSR_HP                                 0x00000001
+#define DMSR_HR                                 0x00000002
+#define DMSR_SP                                 0x00000004
+#define DMSR_SR                                 0x00000008
+
+/*
+ *  The following defines are for the flags in the host DMA source address
+ *  register.
+ */
+#define HSAR_HOST_ADDR_MASK                     0xFFFFFFFF
+#define HSAR_DSP_ADDR_MASK                      0x0000FFFF
+#define HSAR_MEMID_MASK                         0x000F0000
+#define HSAR_MEMID_SP_DMEM0                     0x00000000
+#define HSAR_MEMID_SP_DMEM1                     0x00010000
+#define HSAR_MEMID_SP_PMEM                      0x00020000
+#define HSAR_MEMID_SP_DEBUG                     0x00030000
+#define HSAR_MEMID_OMNI_MEM                     0x000E0000
+#define HSAR_END                                0x40000000
+#define HSAR_ERR                                0x80000000
+
+/*
+ *  The following defines are for the flags in the host DMA destination address
+ *  register.
+ */
+#define HDAR_HOST_ADDR_MASK                     0xFFFFFFFF
+#define HDAR_DSP_ADDR_MASK                      0x0000FFFF
+#define HDAR_MEMID_MASK                         0x000F0000
+#define HDAR_MEMID_SP_DMEM0                     0x00000000
+#define HDAR_MEMID_SP_DMEM1                     0x00010000
+#define HDAR_MEMID_SP_PMEM                      0x00020000
+#define HDAR_MEMID_SP_DEBUG                     0x00030000
+#define HDAR_MEMID_OMNI_MEM                     0x000E0000
+#define HDAR_END                                0x40000000
+#define HDAR_ERR                                0x80000000
+
+/*
+ *  The following defines are for the flags in the host DMA control register.
+ */
+#define HDMR_AC_MASK                            0x0000F000
+#define HDMR_AC_8_16                            0x00001000
+#define HDMR_AC_M_S                             0x00002000
+#define HDMR_AC_B_L                             0x00004000
+#define HDMR_AC_S_U                             0x00008000
+
+/*
+ *  The following defines are for the flags in the host DMA control register.
+ */
+#define HDCR_COUNT_MASK                         0x000003FF
+#define HDCR_DONE                               0x00004000
+#define HDCR_OPT                                0x00008000
+#define HDCR_WBD                                0x00400000
+#define HDCR_WBS                                0x00800000
+#define HDCR_DMS_MASK                           0x07000000
+#define HDCR_DMS_LINEAR                         0x00000000
+#define HDCR_DMS_16_DWORDS                      0x01000000
+#define HDCR_DMS_32_DWORDS                      0x02000000
+#define HDCR_DMS_64_DWORDS                      0x03000000
+#define HDCR_DMS_128_DWORDS                     0x04000000
+#define HDCR_DMS_256_DWORDS                     0x05000000
+#define HDCR_DMS_512_DWORDS                     0x06000000
+#define HDCR_DMS_1024_DWORDS                    0x07000000
+#define HDCR_DH                                 0x08000000
+#define HDCR_SMS_MASK                           0x70000000
+#define HDCR_SMS_LINEAR                         0x00000000
+#define HDCR_SMS_16_DWORDS                      0x10000000
+#define HDCR_SMS_32_DWORDS                      0x20000000
+#define HDCR_SMS_64_DWORDS                      0x30000000
+#define HDCR_SMS_128_DWORDS                     0x40000000
+#define HDCR_SMS_256_DWORDS                     0x50000000
+#define HDCR_SMS_512_DWORDS                     0x60000000
+#define HDCR_SMS_1024_DWORDS                    0x70000000
+#define HDCR_SH                                 0x80000000
+#define HDCR_COUNT_SHIFT                        0
+
+/*
+ *  The following defines are for the flags in the performance monitor control
+ *  register.
+ */
+#define PFMC_C1SS_MASK                          0x0000001F
+#define PFMC_C1EV                               0x00000020
+#define PFMC_C1RS                               0x00008000
+#define PFMC_C2SS_MASK                          0x001F0000
+#define PFMC_C2EV                               0x00200000
+#define PFMC_C2RS                               0x80000000
+#define PFMC_C1SS_SHIFT                         0
+#define PFMC_C2SS_SHIFT                         16
+#define PFMC_BUS_GRANT                          0
+#define PFMC_GRANT_AFTER_REQ                    1
+#define PFMC_TRANSACTION                        2
+#define PFMC_DWORD_TRANSFER                     3
+#define PFMC_SLAVE_READ                         4
+#define PFMC_SLAVE_WRITE                        5
+#define PFMC_PREEMPTION                         6
+#define PFMC_DISCONNECT_RETRY                   7
+#define PFMC_INTERRUPT                          8
+#define PFMC_BUS_OWNERSHIP                      9
+#define PFMC_TRANSACTION_LAG                    10
+#define PFMC_PCI_CLOCK                          11
+#define PFMC_SERIAL_CLOCK                       12
+#define PFMC_SP_CLOCK                           13
+
+/*
+ *  The following defines are for the flags in the performance counter value 1
+ *  register.
+ */
+#define PFCV1_PC1V_MASK                         0xFFFFFFFF
+#define PFCV1_PC1V_SHIFT                        0
+
+/*
+ *  The following defines are for the flags in the performance counter value 2
+ *  register.
+ */
+#define PFCV2_PC2V_MASK                         0xFFFFFFFF
+#define PFCV2_PC2V_SHIFT                        0
+
+/*
+ *  The following defines are for the flags in the clock control register 1.
+ */
+#define CLKCR1_OSCS                             0x00000001
+#define CLKCR1_OSCP                             0x00000002
+#define CLKCR1_PLLSS_MASK                       0x0000000C
+#define CLKCR1_PLLSS_SERIAL                     0x00000000
+#define CLKCR1_PLLSS_CRYSTAL                    0x00000004
+#define CLKCR1_PLLSS_PCI                        0x00000008
+#define CLKCR1_PLLSS_RESERVED                   0x0000000C
+#define CLKCR1_PLLP                             0x00000010
+#define CLKCR1_SWCE                             0x00000020
+#define CLKCR1_PLLOS                            0x00000040
+
+/*
+ *  The following defines are for the flags in the clock control register 2.
+ */
+#define CLKCR2_PDIVS_MASK                       0x0000000F
+#define CLKCR2_PDIVS_1                          0x00000001
+#define CLKCR2_PDIVS_2                          0x00000002
+#define CLKCR2_PDIVS_4                          0x00000004
+#define CLKCR2_PDIVS_7                          0x00000007
+#define CLKCR2_PDIVS_8                          0x00000008
+#define CLKCR2_PDIVS_16                         0x00000000
+
+/*
+ *  The following defines are for the flags in the PLL multiplier register.
+ */
+#define PLLM_MASK                               0x000000FF
+#define PLLM_SHIFT                              0
+
+/*
+ *  The following defines are for the flags in the PLL capacitor coefficient
+ *  register.
+ */
+#define PLLCC_CDR_MASK                          0x00000007
+#ifndef NO_CS4610
+#define PLLCC_CDR_240_350_MHZ                   0x00000000
+#define PLLCC_CDR_184_265_MHZ                   0x00000001
+#define PLLCC_CDR_144_205_MHZ                   0x00000002
+#define PLLCC_CDR_111_160_MHZ                   0x00000003
+#define PLLCC_CDR_87_123_MHZ                    0x00000004
+#define PLLCC_CDR_67_96_MHZ                     0x00000005
+#define PLLCC_CDR_52_74_MHZ                     0x00000006
+#define PLLCC_CDR_45_58_MHZ                     0x00000007
+#endif
+#ifndef NO_CS4612
+#define PLLCC_CDR_271_398_MHZ                   0x00000000
+#define PLLCC_CDR_227_330_MHZ                   0x00000001
+#define PLLCC_CDR_167_239_MHZ                   0x00000002
+#define PLLCC_CDR_150_215_MHZ                   0x00000003
+#define PLLCC_CDR_107_154_MHZ                   0x00000004
+#define PLLCC_CDR_98_140_MHZ                    0x00000005
+#define PLLCC_CDR_73_104_MHZ                    0x00000006
+#define PLLCC_CDR_63_90_MHZ                     0x00000007
+#endif
+#define PLLCC_LPF_MASK                          0x000000F8
+#ifndef NO_CS4610
+#define PLLCC_LPF_23850_60000_KHZ               0x00000000
+#define PLLCC_LPF_7960_26290_KHZ                0x00000008
+#define PLLCC_LPF_4160_10980_KHZ                0x00000018
+#define PLLCC_LPF_1740_4580_KHZ                 0x00000038
+#define PLLCC_LPF_724_1910_KHZ                  0x00000078
+#define PLLCC_LPF_317_798_KHZ                   0x000000F8
+#endif
+#ifndef NO_CS4612
+#define PLLCC_LPF_25580_64530_KHZ               0x00000000
+#define PLLCC_LPF_14360_37270_KHZ               0x00000008
+#define PLLCC_LPF_6100_16020_KHZ                0x00000018
+#define PLLCC_LPF_2540_6690_KHZ                 0x00000038
+#define PLLCC_LPF_1050_2780_KHZ                 0x00000078
+#define PLLCC_LPF_450_1160_KHZ                  0x000000F8
+#endif
+
+/*
+ *  The following defines are for the flags in the feature reporting register.
+ */
+#define FRR_FAB_MASK                            0x00000003
+#define FRR_MASK_MASK                           0x0000001C
+#ifdef NO_CS4612
+#define FRR_CFOP_MASK                           0x000000E0
+#else
+#define FRR_CFOP_MASK                           0x00000FE0
+#endif
+#define FRR_CFOP_NOT_DVD                        0x00000020
+#define FRR_CFOP_A3D                            0x00000040
+#define FRR_CFOP_128_PIN                        0x00000080
+#ifndef NO_CS4612
+#define FRR_CFOP_CS4280                         0x00000800
+#endif
+#define FRR_FAB_SHIFT                           0
+#define FRR_MASK_SHIFT                          2
+#define FRR_CFOP_SHIFT                          5
+
+/*
+ *  The following defines are for the flags in the configuration load 1
+ *  register.
+ */
+#define CFL1_CLOCK_SOURCE_MASK                  0x00000003
+#define CFL1_CLOCK_SOURCE_CS423X                0x00000000
+#define CFL1_CLOCK_SOURCE_AC97                  0x00000001
+#define CFL1_CLOCK_SOURCE_CRYSTAL               0x00000002
+#define CFL1_CLOCK_SOURCE_DUAL_AC97             0x00000003
+#define CFL1_VALID_DATA_MASK                    0x000000FF
+
+/*
+ *  The following defines are for the flags in the configuration load 2
+ *  register.
+ */
+#define CFL2_VALID_DATA_MASK                    0x000000FF
+
+/*
+ *  The following defines are for the flags in the serial port master control
+ *  register 1.
+ */
+#define SERMC1_MSPE                             0x00000001
+#define SERMC1_PTC_MASK                         0x0000000E
+#define SERMC1_PTC_CS423X                       0x00000000
+#define SERMC1_PTC_AC97                         0x00000002
+#define SERMC1_PTC_DAC                          0x00000004
+#define SERMC1_PLB                              0x00000010
+#define SERMC1_XLB                              0x00000020
+
+/*
+ *  The following defines are for the flags in the serial port master control
+ *  register 2.
+ */
+#define SERMC2_LROE                             0x00000001
+#define SERMC2_MCOE                             0x00000002
+#define SERMC2_MCDIV                            0x00000004
+
+/*
+ *  The following defines are for the flags in the serial port 1 configuration
+ *  register.
+ */
+#define SERC1_SO1EN                             0x00000001
+#define SERC1_SO1F_MASK                         0x0000000E
+#define SERC1_SO1F_CS423X                       0x00000000
+#define SERC1_SO1F_AC97                         0x00000002
+#define SERC1_SO1F_DAC                          0x00000004
+#define SERC1_SO1F_SPDIF                        0x00000006
+
+/*
+ *  The following defines are for the flags in the serial port 2 configuration
+ *  register.
+ */
+#define SERC2_SI1EN                             0x00000001
+#define SERC2_SI1F_MASK                         0x0000000E
+#define SERC2_SI1F_CS423X                       0x00000000
+#define SERC2_SI1F_AC97                         0x00000002
+#define SERC2_SI1F_ADC                          0x00000004
+#define SERC2_SI1F_SPDIF                        0x00000006
+
+/*
+ *  The following defines are for the flags in the serial port 3 configuration
+ *  register.
+ */
+#define SERC3_SO2EN                             0x00000001
+#define SERC3_SO2F_MASK                         0x00000006
+#define SERC3_SO2F_DAC                          0x00000000
+#define SERC3_SO2F_SPDIF                        0x00000002
+
+/*
+ *  The following defines are for the flags in the serial port 4 configuration
+ *  register.
+ */
+#define SERC4_SO3EN                             0x00000001
+#define SERC4_SO3F_MASK                         0x00000006
+#define SERC4_SO3F_DAC                          0x00000000
+#define SERC4_SO3F_SPDIF                        0x00000002
+
+/*
+ *  The following defines are for the flags in the serial port 5 configuration
+ *  register.
+ */
+#define SERC5_SI2EN                             0x00000001
+#define SERC5_SI2F_MASK                         0x00000006
+#define SERC5_SI2F_ADC                          0x00000000
+#define SERC5_SI2F_SPDIF                        0x00000002
+
+/*
+ *  The following defines are for the flags in the serial port backdoor sample
+ *  pointer register.
+ */
+#define SERBSP_FSP_MASK                         0x0000000F
+#define SERBSP_FSP_SHIFT                        0
+
+/*
+ *  The following defines are for the flags in the serial port backdoor status
+ *  register.
+ */
+#define SERBST_RRDY                             0x00000001
+#define SERBST_WBSY                             0x00000002
+
+/*
+ *  The following defines are for the flags in the serial port backdoor command
+ *  register.
+ */
+#define SERBCM_RDC                              0x00000001
+#define SERBCM_WRC                              0x00000002
+
+/*
+ *  The following defines are for the flags in the serial port backdoor address
+ *  register.
+ */
+#ifdef NO_CS4612
+#define SERBAD_FAD_MASK                         0x000000FF
+#else
+#define SERBAD_FAD_MASK                         0x000001FF
+#endif
+#define SERBAD_FAD_SHIFT                        0
+
+/*
+ *  The following defines are for the flags in the serial port backdoor
+ *  configuration register.
+ */
+#define SERBCF_HBP                              0x00000001
+
+/*
+ *  The following defines are for the flags in the serial port backdoor write
+ *  port register.
+ */
+#define SERBWP_FWD_MASK                         0x000FFFFF
+#define SERBWP_FWD_SHIFT                        0
+
+/*
+ *  The following defines are for the flags in the serial port backdoor read
+ *  port register.
+ */
+#define SERBRP_FRD_MASK                         0x000FFFFF
+#define SERBRP_FRD_SHIFT                        0
+
+/*
+ *  The following defines are for the flags in the async FIFO address register.
+ */
+#ifndef NO_CS4612
+#define ASER_FADDR_A1_MASK                      0x000001FF
+#define ASER_FADDR_EN1                          0x00008000
+#define ASER_FADDR_A2_MASK                      0x01FF0000
+#define ASER_FADDR_EN2                          0x80000000
+#define ASER_FADDR_A1_SHIFT                     0
+#define ASER_FADDR_A2_SHIFT                     16
+#endif
+
+/*
+ *  The following defines are for the flags in the AC97 control register.
+ */
+#define ACCTL_RSTN                              0x00000001
+#define ACCTL_ESYN                              0x00000002
+#define ACCTL_VFRM                              0x00000004
+#define ACCTL_DCV                               0x00000008
+#define ACCTL_CRW                               0x00000010
+#define ACCTL_ASYN                              0x00000020
+#ifndef NO_CS4612
+#define ACCTL_TC                                0x00000040
+#endif
+
+/*
+ *  The following defines are for the flags in the AC97 status register.
+ */
+#define ACSTS_CRDY                              0x00000001
+#define ACSTS_VSTS                              0x00000002
+#ifndef NO_CS4612
+#define ACSTS_WKUP                              0x00000004
+#endif
+
+/*
+ *  The following defines are for the flags in the AC97 output slot valid
+ *  register.
+ */
+#define ACOSV_SLV3                              0x00000001
+#define ACOSV_SLV4                              0x00000002
+#define ACOSV_SLV5                              0x00000004
+#define ACOSV_SLV6                              0x00000008
+#define ACOSV_SLV7                              0x00000010
+#define ACOSV_SLV8                              0x00000020
+#define ACOSV_SLV9                              0x00000040
+#define ACOSV_SLV10                             0x00000080
+#define ACOSV_SLV11                             0x00000100
+#define ACOSV_SLV12                             0x00000200
+
+/*
+ *  The following defines are for the flags in the AC97 command address
+ *  register.
+ */
+#define ACCAD_CI_MASK                           0x0000007F
+#define ACCAD_CI_SHIFT                          0
+
+/*
+ *  The following defines are for the flags in the AC97 command data register.
+ */
+#define ACCDA_CD_MASK                           0x0000FFFF
+#define ACCDA_CD_SHIFT                          0
+
+/*
+ *  The following defines are for the flags in the AC97 input slot valid
+ *  register.
+ */
+#define ACISV_ISV3                              0x00000001
+#define ACISV_ISV4                              0x00000002
+#define ACISV_ISV5                              0x00000004
+#define ACISV_ISV6                              0x00000008
+#define ACISV_ISV7                              0x00000010
+#define ACISV_ISV8                              0x00000020
+#define ACISV_ISV9                              0x00000040
+#define ACISV_ISV10                             0x00000080
+#define ACISV_ISV11                             0x00000100
+#define ACISV_ISV12                             0x00000200
+
+/*
+ *  The following defines are for the flags in the AC97 status address
+ *  register.
+ */
+#define ACSAD_SI_MASK                           0x0000007F
+#define ACSAD_SI_SHIFT                          0
+
+/*
+ *  The following defines are for the flags in the AC97 status data register.
+ */
+#define ACSDA_SD_MASK                           0x0000FFFF
+#define ACSDA_SD_SHIFT                          0
+
+/*
+ *  The following defines are for the flags in the joystick poll/trigger
+ *  register.
+ */
+#define JSPT_CAX                                0x00000001
+#define JSPT_CAY                                0x00000002
+#define JSPT_CBX                                0x00000004
+#define JSPT_CBY                                0x00000008
+#define JSPT_BA1                                0x00000010
+#define JSPT_BA2                                0x00000020
+#define JSPT_BB1                                0x00000040
+#define JSPT_BB2                                0x00000080
+
+/*
+ *  The following defines are for the flags in the joystick control register.
+ */
+#define JSCTL_SP_MASK                           0x00000003
+#define JSCTL_SP_SLOW                           0x00000000
+#define JSCTL_SP_MEDIUM_SLOW                    0x00000001
+#define JSCTL_SP_MEDIUM_FAST                    0x00000002
+#define JSCTL_SP_FAST                           0x00000003
+#define JSCTL_ARE                               0x00000004
+
+/*
+ *  The following defines are for the flags in the joystick coordinate pair 1
+ *  readback register.
+ */
+#define JSC1_Y1V_MASK                           0x0000FFFF
+#define JSC1_X1V_MASK                           0xFFFF0000
+#define JSC1_Y1V_SHIFT                          0
+#define JSC1_X1V_SHIFT                          16
+
+/*
+ *  The following defines are for the flags in the joystick coordinate pair 2
+ *  readback register.
+ */
+#define JSC2_Y2V_MASK                           0x0000FFFF
+#define JSC2_X2V_MASK                           0xFFFF0000
+#define JSC2_Y2V_SHIFT                          0
+#define JSC2_X2V_SHIFT                          16
+
+/*
+ *  The following defines are for the flags in the MIDI control register.
+ */
+#define MIDCR_TXE                               0x00000001	/* Enable transmitting. */
+#define MIDCR_RXE                               0x00000002	/* Enable receiving. */
+#define MIDCR_RIE                               0x00000004	/* Interrupt upon tx ready. */
+#define MIDCR_TIE                               0x00000008	/* Interrupt upon rx ready. */
+#define MIDCR_MLB                               0x00000010	/* Enable midi loopback. */
+#define MIDCR_MRST                              0x00000020	/* Reset interface. */
+
+/*
+ *  The following defines are for the flags in the MIDI status register.
+ */
+#define MIDSR_TBF                               0x00000001	/* Tx FIFO is full. */
+#define MIDSR_RBE                               0x00000002	/* Rx FIFO is empty. */
+
+/*
+ *  The following defines are for the flags in the MIDI write port register.
+ */
+#define MIDWP_MWD_MASK                          0x000000FF
+#define MIDWP_MWD_SHIFT                         0
+
+/*
+ *  The following defines are for the flags in the MIDI read port register.
+ */
+#define MIDRP_MRD_MASK                          0x000000FF
+#define MIDRP_MRD_SHIFT                         0
+
+/*
+ *  The following defines are for the flags in the joystick GPIO register.
+ */
+#define JSIO_DAX                                0x00000001
+#define JSIO_DAY                                0x00000002
+#define JSIO_DBX                                0x00000004
+#define JSIO_DBY                                0x00000008
+#define JSIO_AXOE                               0x00000010
+#define JSIO_AYOE                               0x00000020
+#define JSIO_BXOE                               0x00000040
+#define JSIO_BYOE                               0x00000080
+
+/*
+ *  The following defines are for the flags in the master async/sync serial
+ *  port enable register.
+ */
+#ifndef NO_CS4612
+#define ASER_MASTER_ME                          0x00000001
+#endif
+
+/*
+ *  The following defines are for the flags in the configuration interface
+ *  register.
+ */
+#define CFGI_CLK                                0x00000001
+#define CFGI_DOUT                               0x00000002
+#define CFGI_DIN_EEN                            0x00000004
+#define CFGI_EELD                               0x00000008
+
+/*
+ *  The following defines are for the flags in the subsystem ID and vendor ID
+ *  register.
+ */
+#define SSVID_VID_MASK                          0x0000FFFF
+#define SSVID_SID_MASK                          0xFFFF0000
+#define SSVID_VID_SHIFT                         0
+#define SSVID_SID_SHIFT                         16
+
+/*
+ *  The following defines are for the flags in the GPIO pin interface register.
+ */
+#define GPIOR_VOLDN                             0x00000001
+#define GPIOR_VOLUP                             0x00000002
+#define GPIOR_SI2D                              0x00000004
+#define GPIOR_SI2OE                             0x00000008
+
+/*
+ *  The following defines are for the flags in the extended GPIO pin direction
+ *  register.
+ */
+#ifndef NO_CS4612
+#define EGPIODR_GPOE0                           0x00000001
+#define EGPIODR_GPOE1                           0x00000002
+#define EGPIODR_GPOE2                           0x00000004
+#define EGPIODR_GPOE3                           0x00000008
+#define EGPIODR_GPOE4                           0x00000010
+#define EGPIODR_GPOE5                           0x00000020
+#define EGPIODR_GPOE6                           0x00000040
+#define EGPIODR_GPOE7                           0x00000080
+#define EGPIODR_GPOE8                           0x00000100
+#endif
+
+/*
+ *  The following defines are for the flags in the extended GPIO pin polarity/
+ *  type register.
+ */
+#ifndef NO_CS4612
+#define EGPIOPTR_GPPT0                          0x00000001
+#define EGPIOPTR_GPPT1                          0x00000002
+#define EGPIOPTR_GPPT2                          0x00000004
+#define EGPIOPTR_GPPT3                          0x00000008
+#define EGPIOPTR_GPPT4                          0x00000010
+#define EGPIOPTR_GPPT5                          0x00000020
+#define EGPIOPTR_GPPT6                          0x00000040
+#define EGPIOPTR_GPPT7                          0x00000080
+#define EGPIOPTR_GPPT8                          0x00000100
+#endif
+
+/*
+ *  The following defines are for the flags in the extended GPIO pin sticky
+ *  register.
+ */
+#ifndef NO_CS4612
+#define EGPIOTR_GPS0                            0x00000001
+#define EGPIOTR_GPS1                            0x00000002
+#define EGPIOTR_GPS2                            0x00000004
+#define EGPIOTR_GPS3                            0x00000008
+#define EGPIOTR_GPS4                            0x00000010
+#define EGPIOTR_GPS5                            0x00000020
+#define EGPIOTR_GPS6                            0x00000040
+#define EGPIOTR_GPS7                            0x00000080
+#define EGPIOTR_GPS8                            0x00000100
+#endif
+
+/*
+ *  The following defines are for the flags in the extended GPIO ping wakeup
+ *  register.
+ */
+#ifndef NO_CS4612
+#define EGPIOWR_GPW0                            0x00000001
+#define EGPIOWR_GPW1                            0x00000002
+#define EGPIOWR_GPW2                            0x00000004
+#define EGPIOWR_GPW3                            0x00000008
+#define EGPIOWR_GPW4                            0x00000010
+#define EGPIOWR_GPW5                            0x00000020
+#define EGPIOWR_GPW6                            0x00000040
+#define EGPIOWR_GPW7                            0x00000080
+#define EGPIOWR_GPW8                            0x00000100
+#endif
+
+/*
+ *  The following defines are for the flags in the extended GPIO pin status
+ *  register.
+ */
+#ifndef NO_CS4612
+#define EGPIOSR_GPS0                            0x00000001
+#define EGPIOSR_GPS1                            0x00000002
+#define EGPIOSR_GPS2                            0x00000004
+#define EGPIOSR_GPS3                            0x00000008
+#define EGPIOSR_GPS4                            0x00000010
+#define EGPIOSR_GPS5                            0x00000020
+#define EGPIOSR_GPS6                            0x00000040
+#define EGPIOSR_GPS7                            0x00000080
+#define EGPIOSR_GPS8                            0x00000100
+#endif
+
+/*
+ *  The following defines are for the flags in the serial port 6 configuration
+ *  register.
+ */
+#ifndef NO_CS4612
+#define SERC6_ASDO2EN                           0x00000001
+#endif
+
+/*
+ *  The following defines are for the flags in the serial port 7 configuration
+ *  register.
+ */
+#ifndef NO_CS4612
+#define SERC7_ASDI2EN                           0x00000001
+#define SERC7_POSILB                            0x00000002
+#define SERC7_SIPOLB                            0x00000004
+#define SERC7_SOSILB                            0x00000008
+#define SERC7_SISOLB                            0x00000010
+#endif
+
+/*
+ *  The following defines are for the flags in the serial port AC link
+ *  configuration register.
+ */
+#ifndef NO_CS4612
+#define SERACC_CHIP_TYPE_MASK                  0x00000001
+#define SERACC_CHIP_TYPE_1_03                  0x00000000
+#define SERACC_CHIP_TYPE_2_0                   0x00000001
+#define SERACC_TWO_CODECS                      0x00000002
+#define SERACC_MDM                             0x00000004
+#define SERACC_HSP                             0x00000008
+#define SERACC_ODT                             0x00000010 /* only CS4630 */
+#endif
+
+/*
+ *  The following defines are for the flags in the AC97 control register 2.
+ */
+#ifndef NO_CS4612
+#define ACCTL2_RSTN                             0x00000001
+#define ACCTL2_ESYN                             0x00000002
+#define ACCTL2_VFRM                             0x00000004
+#define ACCTL2_DCV                              0x00000008
+#define ACCTL2_CRW                              0x00000010
+#define ACCTL2_ASYN                             0x00000020
+#endif
+
+/*
+ *  The following defines are for the flags in the AC97 status register 2.
+ */
+#ifndef NO_CS4612
+#define ACSTS2_CRDY                             0x00000001
+#define ACSTS2_VSTS                             0x00000002
+#endif
+
+/*
+ *  The following defines are for the flags in the AC97 output slot valid
+ *  register 2.
+ */
+#ifndef NO_CS4612
+#define ACOSV2_SLV3                             0x00000001
+#define ACOSV2_SLV4                             0x00000002
+#define ACOSV2_SLV5                             0x00000004
+#define ACOSV2_SLV6                             0x00000008
+#define ACOSV2_SLV7                             0x00000010
+#define ACOSV2_SLV8                             0x00000020
+#define ACOSV2_SLV9                             0x00000040
+#define ACOSV2_SLV10                            0x00000080
+#define ACOSV2_SLV11                            0x00000100
+#define ACOSV2_SLV12                            0x00000200
+#endif
+
+/*
+ *  The following defines are for the flags in the AC97 command address
+ *  register 2.
+ */
+#ifndef NO_CS4612
+#define ACCAD2_CI_MASK                          0x0000007F
+#define ACCAD2_CI_SHIFT                         0
+#endif
+
+/*
+ *  The following defines are for the flags in the AC97 command data register
+ *  2.
+ */
+#ifndef NO_CS4612
+#define ACCDA2_CD_MASK                          0x0000FFFF
+#define ACCDA2_CD_SHIFT                         0  
+#endif
+
+/*
+ *  The following defines are for the flags in the AC97 input slot valid
+ *  register 2.
+ */
+#ifndef NO_CS4612
+#define ACISV2_ISV3                             0x00000001
+#define ACISV2_ISV4                             0x00000002
+#define ACISV2_ISV5                             0x00000004
+#define ACISV2_ISV6                             0x00000008
+#define ACISV2_ISV7                             0x00000010
+#define ACISV2_ISV8                             0x00000020
+#define ACISV2_ISV9                             0x00000040
+#define ACISV2_ISV10                            0x00000080
+#define ACISV2_ISV11                            0x00000100
+#define ACISV2_ISV12                            0x00000200
+#endif
+
+/*
+ *  The following defines are for the flags in the AC97 status address
+ *  register 2.
+ */
+#ifndef NO_CS4612
+#define ACSAD2_SI_MASK                          0x0000007F
+#define ACSAD2_SI_SHIFT                         0
+#endif
+
+/*
+ *  The following defines are for the flags in the AC97 status data register 2.
+ */
+#ifndef NO_CS4612
+#define ACSDA2_SD_MASK                          0x0000FFFF
+#define ACSDA2_SD_SHIFT                         0
+#endif
+
+/*
+ *  The following defines are for the flags in the I/O trap address and control
+ *  registers (all 12).
+ */
+#ifndef NO_CS4612
+#define IOTAC_SA_MASK                           0x0000FFFF
+#define IOTAC_MSK_MASK                          0x000F0000
+#define IOTAC_IODC_MASK                         0x06000000
+#define IOTAC_IODC_16_BIT                       0x00000000
+#define IOTAC_IODC_10_BIT                       0x02000000
+#define IOTAC_IODC_12_BIT                       0x04000000
+#define IOTAC_WSPI                              0x08000000
+#define IOTAC_RSPI                              0x10000000
+#define IOTAC_WSE                               0x20000000
+#define IOTAC_WE                                0x40000000
+#define IOTAC_RE                                0x80000000
+#define IOTAC_SA_SHIFT                          0
+#define IOTAC_MSK_SHIFT                         16
+#endif
+
+/*
+ *  The following defines are for the flags in the I/O trap fast read registers
+ *  (all 8).
+ */
+#ifndef NO_CS4612
+#define IOTFR_D_MASK                            0x0000FFFF
+#define IOTFR_A_MASK                            0x000F0000
+#define IOTFR_R_MASK                            0x0F000000
+#define IOTFR_ALL                               0x40000000
+#define IOTFR_VL                                0x80000000
+#define IOTFR_D_SHIFT                           0
+#define IOTFR_A_SHIFT                           16
+#define IOTFR_R_SHIFT                           24
+#endif
+
+/*
+ *  The following defines are for the flags in the I/O trap FIFO register.
+ */
+#ifndef NO_CS4612
+#define IOTFIFO_BA_MASK                         0x00003FFF
+#define IOTFIFO_S_MASK                          0x00FF0000
+#define IOTFIFO_OF                              0x40000000
+#define IOTFIFO_SPIOF                           0x80000000
+#define IOTFIFO_BA_SHIFT                        0
+#define IOTFIFO_S_SHIFT                         16
+#endif
+
+/*
+ *  The following defines are for the flags in the I/O trap retry read data
+ *  register.
+ */
+#ifndef NO_CS4612
+#define IOTRRD_D_MASK                           0x0000FFFF
+#define IOTRRD_RDV                              0x80000000
+#define IOTRRD_D_SHIFT                          0
+#endif
+
+/*
+ *  The following defines are for the flags in the I/O trap FIFO pointer
+ *  register.
+ */
+#ifndef NO_CS4612
+#define IOTFP_CA_MASK                           0x00003FFF
+#define IOTFP_PA_MASK                           0x3FFF0000
+#define IOTFP_CA_SHIFT                          0
+#define IOTFP_PA_SHIFT                          16
+#endif
+
+/*
+ *  The following defines are for the flags in the I/O trap control register.
+ */
+#ifndef NO_CS4612
+#define IOTCR_ITD                               0x00000001
+#define IOTCR_HRV                               0x00000002
+#define IOTCR_SRV                               0x00000004
+#define IOTCR_DTI                               0x00000008
+#define IOTCR_DFI                               0x00000010
+#define IOTCR_DDP                               0x00000020
+#define IOTCR_JTE                               0x00000040
+#define IOTCR_PPE                               0x00000080
+#endif
+
+/*
+ *  The following defines are for the flags in the direct PCI data register.
+ */
+#ifndef NO_CS4612
+#define DPCID_D_MASK                            0xFFFFFFFF
+#define DPCID_D_SHIFT                           0
+#endif
+
+/*
+ *  The following defines are for the flags in the direct PCI address register.
+ */
+#ifndef NO_CS4612
+#define DPCIA_A_MASK                            0xFFFFFFFF
+#define DPCIA_A_SHIFT                           0
+#endif
+
+/*
+ *  The following defines are for the flags in the direct PCI command register.
+ */
+#ifndef NO_CS4612
+#define DPCIC_C_MASK                            0x0000000F
+#define DPCIC_C_IOREAD                          0x00000002
+#define DPCIC_C_IOWRITE                         0x00000003
+#define DPCIC_BE_MASK                           0x000000F0
+#endif
+
+/*
+ *  The following defines are for the flags in the PC/PCI request register.
+ */
+#ifndef NO_CS4612
+#define PCPCIR_RDC_MASK                         0x00000007
+#define PCPCIR_C_MASK                           0x00007000
+#define PCPCIR_REQ                              0x00008000
+#define PCPCIR_RDC_SHIFT                        0
+#define PCPCIR_C_SHIFT                          12
+#endif
+
+/*
+ *  The following defines are for the flags in the PC/PCI grant register.
+ */ 
+#ifndef NO_CS4612
+#define PCPCIG_GDC_MASK                         0x00000007
+#define PCPCIG_VL                               0x00008000
+#define PCPCIG_GDC_SHIFT                        0
+#endif
+
+/*
+ *  The following defines are for the flags in the PC/PCI master enable
+ *  register.
+ */
+#ifndef NO_CS4612
+#define PCPCIEN_EN                              0x00000001
+#endif
+
+/*
+ *  The following defines are for the flags in the extended PCI power
+ *  management control register.
+ */
+#ifndef NO_CS4612
+#define EPCIPMC_GWU                             0x00000001
+#define EPCIPMC_FSPC                            0x00000002
+#endif 
+
+/*
+ *  The following defines are for the flags in the SP control register.
+ */
+#define SPCR_RUN                                0x00000001
+#define SPCR_STPFR                              0x00000002
+#define SPCR_RUNFR                              0x00000004
+#define SPCR_TICK                               0x00000008
+#define SPCR_DRQEN                              0x00000020
+#define SPCR_RSTSP                              0x00000040
+#define SPCR_OREN                               0x00000080
+#ifndef NO_CS4612
+#define SPCR_PCIINT                             0x00000100
+#define SPCR_OINTD                              0x00000200
+#define SPCR_CRE                                0x00008000
+#endif
+
+/*
+ *  The following defines are for the flags in the debug index register.
+ */
+#define DREG_REGID_MASK                         0x0000007F
+#define DREG_DEBUG                              0x00000080
+#define DREG_RGBK_MASK                          0x00000700
+#define DREG_TRAP                               0x00000800
+#if !defined(NO_CS4612)
+#if !defined(NO_CS4615)
+#define DREG_TRAPX                              0x00001000
+#endif
+#endif
+#define DREG_REGID_SHIFT                        0
+#define DREG_RGBK_SHIFT                         8
+#define DREG_RGBK_REGID_MASK                    0x0000077F
+#define DREG_REGID_R0                           0x00000010
+#define DREG_REGID_R1                           0x00000011
+#define DREG_REGID_R2                           0x00000012
+#define DREG_REGID_R3                           0x00000013
+#define DREG_REGID_R4                           0x00000014
+#define DREG_REGID_R5                           0x00000015
+#define DREG_REGID_R6                           0x00000016
+#define DREG_REGID_R7                           0x00000017
+#define DREG_REGID_R8                           0x00000018
+#define DREG_REGID_R9                           0x00000019
+#define DREG_REGID_RA                           0x0000001A
+#define DREG_REGID_RB                           0x0000001B
+#define DREG_REGID_RC                           0x0000001C
+#define DREG_REGID_RD                           0x0000001D
+#define DREG_REGID_RE                           0x0000001E
+#define DREG_REGID_RF                           0x0000001F
+#define DREG_REGID_RA_BUS_LOW                   0x00000020
+#define DREG_REGID_RA_BUS_HIGH                  0x00000038
+#define DREG_REGID_YBUS_LOW                     0x00000050
+#define DREG_REGID_YBUS_HIGH                    0x00000058
+#define DREG_REGID_TRAP_0                       0x00000100
+#define DREG_REGID_TRAP_1                       0x00000101
+#define DREG_REGID_TRAP_2                       0x00000102
+#define DREG_REGID_TRAP_3                       0x00000103
+#define DREG_REGID_TRAP_4                       0x00000104
+#define DREG_REGID_TRAP_5                       0x00000105
+#define DREG_REGID_TRAP_6                       0x00000106
+#define DREG_REGID_TRAP_7                       0x00000107
+#define DREG_REGID_INDIRECT_ADDRESS             0x0000010E
+#define DREG_REGID_TOP_OF_STACK                 0x0000010F
+#if !defined(NO_CS4612)
+#if !defined(NO_CS4615)
+#define DREG_REGID_TRAP_8                       0x00000110
+#define DREG_REGID_TRAP_9                       0x00000111
+#define DREG_REGID_TRAP_10                      0x00000112
+#define DREG_REGID_TRAP_11                      0x00000113
+#define DREG_REGID_TRAP_12                      0x00000114
+#define DREG_REGID_TRAP_13                      0x00000115
+#define DREG_REGID_TRAP_14                      0x00000116
+#define DREG_REGID_TRAP_15                      0x00000117
+#define DREG_REGID_TRAP_16                      0x00000118
+#define DREG_REGID_TRAP_17                      0x00000119
+#define DREG_REGID_TRAP_18                      0x0000011A
+#define DREG_REGID_TRAP_19                      0x0000011B
+#define DREG_REGID_TRAP_20                      0x0000011C
+#define DREG_REGID_TRAP_21                      0x0000011D
+#define DREG_REGID_TRAP_22                      0x0000011E
+#define DREG_REGID_TRAP_23                      0x0000011F
+#endif
+#endif
+#define DREG_REGID_RSA0_LOW                     0x00000200
+#define DREG_REGID_RSA0_HIGH                    0x00000201
+#define DREG_REGID_RSA1_LOW                     0x00000202
+#define DREG_REGID_RSA1_HIGH                    0x00000203
+#define DREG_REGID_RSA2                         0x00000204
+#define DREG_REGID_RSA3                         0x00000205
+#define DREG_REGID_RSI0_LOW                     0x00000206
+#define DREG_REGID_RSI0_HIGH                    0x00000207
+#define DREG_REGID_RSI1                         0x00000208
+#define DREG_REGID_RSI2                         0x00000209
+#define DREG_REGID_SAGUSTATUS                   0x0000020A
+#define DREG_REGID_RSCONFIG01_LOW               0x0000020B
+#define DREG_REGID_RSCONFIG01_HIGH              0x0000020C
+#define DREG_REGID_RSCONFIG23_LOW               0x0000020D
+#define DREG_REGID_RSCONFIG23_HIGH              0x0000020E
+#define DREG_REGID_RSDMA01E                     0x0000020F
+#define DREG_REGID_RSDMA23E                     0x00000210
+#define DREG_REGID_RSD0_LOW                     0x00000211
+#define DREG_REGID_RSD0_HIGH                    0x00000212
+#define DREG_REGID_RSD1_LOW                     0x00000213
+#define DREG_REGID_RSD1_HIGH                    0x00000214
+#define DREG_REGID_RSD2_LOW                     0x00000215
+#define DREG_REGID_RSD2_HIGH                    0x00000216
+#define DREG_REGID_RSD3_LOW                     0x00000217
+#define DREG_REGID_RSD3_HIGH                    0x00000218
+#define DREG_REGID_SRAR_HIGH                    0x0000021A
+#define DREG_REGID_SRAR_LOW                     0x0000021B
+#define DREG_REGID_DMA_STATE                    0x0000021C
+#define DREG_REGID_CURRENT_DMA_STREAM           0x0000021D
+#define DREG_REGID_NEXT_DMA_STREAM              0x0000021E
+#define DREG_REGID_CPU_STATUS                   0x00000300
+#define DREG_REGID_MAC_MODE                     0x00000301
+#define DREG_REGID_STACK_AND_REPEAT             0x00000302
+#define DREG_REGID_INDEX0                       0x00000304
+#define DREG_REGID_INDEX1                       0x00000305
+#define DREG_REGID_DMA_STATE_0_3                0x00000400
+#define DREG_REGID_DMA_STATE_4_7                0x00000404
+#define DREG_REGID_DMA_STATE_8_11               0x00000408
+#define DREG_REGID_DMA_STATE_12_15              0x0000040C
+#define DREG_REGID_DMA_STATE_16_19              0x00000410
+#define DREG_REGID_DMA_STATE_20_23              0x00000414
+#define DREG_REGID_DMA_STATE_24_27              0x00000418
+#define DREG_REGID_DMA_STATE_28_31              0x0000041C
+#define DREG_REGID_DMA_STATE_32_35              0x00000420
+#define DREG_REGID_DMA_STATE_36_39              0x00000424
+#define DREG_REGID_DMA_STATE_40_43              0x00000428
+#define DREG_REGID_DMA_STATE_44_47              0x0000042C
+#define DREG_REGID_DMA_STATE_48_51              0x00000430
+#define DREG_REGID_DMA_STATE_52_55              0x00000434
+#define DREG_REGID_DMA_STATE_56_59              0x00000438
+#define DREG_REGID_DMA_STATE_60_63              0x0000043C
+#define DREG_REGID_DMA_STATE_64_67              0x00000440
+#define DREG_REGID_DMA_STATE_68_71              0x00000444
+#define DREG_REGID_DMA_STATE_72_75              0x00000448
+#define DREG_REGID_DMA_STATE_76_79              0x0000044C
+#define DREG_REGID_DMA_STATE_80_83              0x00000450
+#define DREG_REGID_DMA_STATE_84_87              0x00000454
+#define DREG_REGID_DMA_STATE_88_91              0x00000458
+#define DREG_REGID_DMA_STATE_92_95              0x0000045C
+#define DREG_REGID_TRAP_SELECT                  0x00000500
+#define DREG_REGID_TRAP_WRITE_0                 0x00000500
+#define DREG_REGID_TRAP_WRITE_1                 0x00000501
+#define DREG_REGID_TRAP_WRITE_2                 0x00000502
+#define DREG_REGID_TRAP_WRITE_3                 0x00000503
+#define DREG_REGID_TRAP_WRITE_4                 0x00000504
+#define DREG_REGID_TRAP_WRITE_5                 0x00000505
+#define DREG_REGID_TRAP_WRITE_6                 0x00000506
+#define DREG_REGID_TRAP_WRITE_7                 0x00000507
+#if !defined(NO_CS4612)
+#if !defined(NO_CS4615)
+#define DREG_REGID_TRAP_WRITE_8                 0x00000510
+#define DREG_REGID_TRAP_WRITE_9                 0x00000511
+#define DREG_REGID_TRAP_WRITE_10                0x00000512
+#define DREG_REGID_TRAP_WRITE_11                0x00000513
+#define DREG_REGID_TRAP_WRITE_12                0x00000514
+#define DREG_REGID_TRAP_WRITE_13                0x00000515
+#define DREG_REGID_TRAP_WRITE_14                0x00000516
+#define DREG_REGID_TRAP_WRITE_15                0x00000517
+#define DREG_REGID_TRAP_WRITE_16                0x00000518
+#define DREG_REGID_TRAP_WRITE_17                0x00000519
+#define DREG_REGID_TRAP_WRITE_18                0x0000051A
+#define DREG_REGID_TRAP_WRITE_19                0x0000051B
+#define DREG_REGID_TRAP_WRITE_20                0x0000051C
+#define DREG_REGID_TRAP_WRITE_21                0x0000051D
+#define DREG_REGID_TRAP_WRITE_22                0x0000051E
+#define DREG_REGID_TRAP_WRITE_23                0x0000051F
+#endif
+#endif
+#define DREG_REGID_MAC0_ACC0_LOW                0x00000600
+#define DREG_REGID_MAC0_ACC1_LOW                0x00000601
+#define DREG_REGID_MAC0_ACC2_LOW                0x00000602
+#define DREG_REGID_MAC0_ACC3_LOW                0x00000603
+#define DREG_REGID_MAC1_ACC0_LOW                0x00000604
+#define DREG_REGID_MAC1_ACC1_LOW                0x00000605
+#define DREG_REGID_MAC1_ACC2_LOW                0x00000606
+#define DREG_REGID_MAC1_ACC3_LOW                0x00000607
+#define DREG_REGID_MAC0_ACC0_MID                0x00000608
+#define DREG_REGID_MAC0_ACC1_MID                0x00000609
+#define DREG_REGID_MAC0_ACC2_MID                0x0000060A
+#define DREG_REGID_MAC0_ACC3_MID                0x0000060B
+#define DREG_REGID_MAC1_ACC0_MID                0x0000060C
+#define DREG_REGID_MAC1_ACC1_MID                0x0000060D
+#define DREG_REGID_MAC1_ACC2_MID                0x0000060E
+#define DREG_REGID_MAC1_ACC3_MID                0x0000060F
+#define DREG_REGID_MAC0_ACC0_HIGH               0x00000610
+#define DREG_REGID_MAC0_ACC1_HIGH               0x00000611
+#define DREG_REGID_MAC0_ACC2_HIGH               0x00000612
+#define DREG_REGID_MAC0_ACC3_HIGH               0x00000613
+#define DREG_REGID_MAC1_ACC0_HIGH               0x00000614
+#define DREG_REGID_MAC1_ACC1_HIGH               0x00000615
+#define DREG_REGID_MAC1_ACC2_HIGH               0x00000616
+#define DREG_REGID_MAC1_ACC3_HIGH               0x00000617
+#define DREG_REGID_RSHOUT_LOW                   0x00000620
+#define DREG_REGID_RSHOUT_MID                   0x00000628
+#define DREG_REGID_RSHOUT_HIGH                  0x00000630
+
+/*
+ *  The following defines are for the flags in the DMA stream requestor write
+ */
+#define DSRWP_DSR_MASK                          0x0000000F
+#define DSRWP_DSR_BG_RQ                         0x00000001
+#define DSRWP_DSR_PRIORITY_MASK                 0x00000006
+#define DSRWP_DSR_PRIORITY_0                    0x00000000
+#define DSRWP_DSR_PRIORITY_1                    0x00000002
+#define DSRWP_DSR_PRIORITY_2                    0x00000004
+#define DSRWP_DSR_PRIORITY_3                    0x00000006
+#define DSRWP_DSR_RQ_PENDING                    0x00000008
+
+/*
+ *  The following defines are for the flags in the trap write port register.
+ */
+#define TWPR_TW_MASK                            0x0000FFFF
+#define TWPR_TW_SHIFT                           0
+
+/*
+ *  The following defines are for the flags in the stack pointer write
+ *  register.
+ */
+#define SPWR_STKP_MASK                          0x0000000F
+#define SPWR_STKP_SHIFT                         0
+
+/*
+ *  The following defines are for the flags in the SP interrupt register.
+ */
+#define SPIR_FRI                                0x00000001
+#define SPIR_DOI                                0x00000002
+#define SPIR_GPI2                               0x00000004
+#define SPIR_GPI3                               0x00000008
+#define SPIR_IP0                                0x00000010
+#define SPIR_IP1                                0x00000020
+#define SPIR_IP2                                0x00000040
+#define SPIR_IP3                                0x00000080
+
+/*
+ *  The following defines are for the flags in the functional group 1 register.
+ */
+#define FGR1_F1S_MASK                           0x0000FFFF
+#define FGR1_F1S_SHIFT                          0
+
+/*
+ *  The following defines are for the flags in the SP clock status register.
+ */
+#define SPCS_FRI                                0x00000001
+#define SPCS_DOI                                0x00000002
+#define SPCS_GPI2                               0x00000004
+#define SPCS_GPI3                               0x00000008
+#define SPCS_IP0                                0x00000010
+#define SPCS_IP1                                0x00000020
+#define SPCS_IP2                                0x00000040
+#define SPCS_IP3                                0x00000080
+#define SPCS_SPRUN                              0x00000100
+#define SPCS_SLEEP                              0x00000200
+#define SPCS_FG                                 0x00000400
+#define SPCS_ORUN                               0x00000800
+#define SPCS_IRQ                                0x00001000
+#define SPCS_FGN_MASK                           0x0000E000
+#define SPCS_FGN_SHIFT                          13
+
+/*
+ *  The following defines are for the flags in the SP DMA requestor status
+ *  register.
+ */
+#define SDSR_DCS_MASK                           0x000000FF
+#define SDSR_DCS_SHIFT                          0
+#define SDSR_DCS_NONE                           0x00000007
+
+/*
+ *  The following defines are for the flags in the frame timer register.
+ */
+#define FRMT_FTV_MASK                           0x0000FFFF
+#define FRMT_FTV_SHIFT                          0
+
+/*
+ *  The following defines are for the flags in the frame timer current count
+ *  register.
+ */
+#define FRCC_FCC_MASK                           0x0000FFFF
+#define FRCC_FCC_SHIFT                          0
+
+/*
+ *  The following defines are for the flags in the frame timer save count
+ *  register.
+ */
+#define FRSC_FCS_MASK                           0x0000FFFF
+#define FRSC_FCS_SHIFT                          0
+
+/*
+ *  The following define the various flags stored in the scatter/gather
+ *  descriptors.
+ */
+#define DMA_SG_NEXT_ENTRY_MASK                  0x00000FF8
+#define DMA_SG_SAMPLE_END_MASK                  0x0FFF0000
+#define DMA_SG_SAMPLE_END_FLAG                  0x10000000
+#define DMA_SG_LOOP_END_FLAG                    0x20000000
+#define DMA_SG_SIGNAL_END_FLAG                  0x40000000
+#define DMA_SG_SIGNAL_PAGE_FLAG                 0x80000000
+#define DMA_SG_NEXT_ENTRY_SHIFT                 3
+#define DMA_SG_SAMPLE_END_SHIFT                 16
+
+/*
+ *  The following define the offsets of the fields within the on-chip generic
+ *  DMA requestor.
+ */
+#define DMA_RQ_CONTROL1                         0x00000000
+#define DMA_RQ_CONTROL2                         0x00000004
+#define DMA_RQ_SOURCE_ADDR                      0x00000008
+#define DMA_RQ_DESTINATION_ADDR                 0x0000000C
+#define DMA_RQ_NEXT_PAGE_ADDR                   0x00000010
+#define DMA_RQ_NEXT_PAGE_SGDESC                 0x00000014
+#define DMA_RQ_LOOP_START_ADDR                  0x00000018
+#define DMA_RQ_POST_LOOP_ADDR                   0x0000001C
+#define DMA_RQ_PAGE_MAP_ADDR                    0x00000020
+
+/*
+ *  The following defines are for the flags in the first control word of the
+ *  on-chip generic DMA requestor.
+ */
+#define DMA_RQ_C1_COUNT_MASK                    0x000003FF
+#define DMA_RQ_C1_DESTINATION_SCATTER           0x00001000
+#define DMA_RQ_C1_SOURCE_GATHER                 0x00002000
+#define DMA_RQ_C1_DONE_FLAG                     0x00004000
+#define DMA_RQ_C1_OPTIMIZE_STATE                0x00008000
+#define DMA_RQ_C1_SAMPLE_END_STATE_MASK         0x00030000
+#define DMA_RQ_C1_FULL_PAGE                     0x00000000
+#define DMA_RQ_C1_BEFORE_SAMPLE_END             0x00010000
+#define DMA_RQ_C1_PAGE_MAP_ERROR                0x00020000
+#define DMA_RQ_C1_AT_SAMPLE_END                 0x00030000
+#define DMA_RQ_C1_LOOP_END_STATE_MASK           0x000C0000
+#define DMA_RQ_C1_NOT_LOOP_END                  0x00000000
+#define DMA_RQ_C1_BEFORE_LOOP_END               0x00040000
+#define DMA_RQ_C1_2PAGE_LOOP_BEGIN              0x00080000
+#define DMA_RQ_C1_LOOP_BEGIN                    0x000C0000
+#define DMA_RQ_C1_PAGE_MAP_MASK                 0x00300000
+#define DMA_RQ_C1_PM_NONE_PENDING               0x00000000
+#define DMA_RQ_C1_PM_NEXT_PENDING               0x00100000
+#define DMA_RQ_C1_PM_RESERVED                   0x00200000
+#define DMA_RQ_C1_PM_LOOP_NEXT_PENDING          0x00300000
+#define DMA_RQ_C1_WRITEBACK_DEST_FLAG           0x00400000
+#define DMA_RQ_C1_WRITEBACK_SRC_FLAG            0x00800000
+#define DMA_RQ_C1_DEST_SIZE_MASK                0x07000000
+#define DMA_RQ_C1_DEST_LINEAR                   0x00000000
+#define DMA_RQ_C1_DEST_MOD16                    0x01000000
+#define DMA_RQ_C1_DEST_MOD32                    0x02000000
+#define DMA_RQ_C1_DEST_MOD64                    0x03000000
+#define DMA_RQ_C1_DEST_MOD128                   0x04000000
+#define DMA_RQ_C1_DEST_MOD256                   0x05000000
+#define DMA_RQ_C1_DEST_MOD512                   0x06000000
+#define DMA_RQ_C1_DEST_MOD1024                  0x07000000
+#define DMA_RQ_C1_DEST_ON_HOST                  0x08000000
+#define DMA_RQ_C1_SOURCE_SIZE_MASK              0x70000000
+#define DMA_RQ_C1_SOURCE_LINEAR                 0x00000000
+#define DMA_RQ_C1_SOURCE_MOD16                  0x10000000
+#define DMA_RQ_C1_SOURCE_MOD32                  0x20000000
+#define DMA_RQ_C1_SOURCE_MOD64                  0x30000000
+#define DMA_RQ_C1_SOURCE_MOD128                 0x40000000
+#define DMA_RQ_C1_SOURCE_MOD256                 0x50000000
+#define DMA_RQ_C1_SOURCE_MOD512                 0x60000000
+#define DMA_RQ_C1_SOURCE_MOD1024                0x70000000
+#define DMA_RQ_C1_SOURCE_ON_HOST                0x80000000
+#define DMA_RQ_C1_COUNT_SHIFT                   0
+
+/*
+ *  The following defines are for the flags in the second control word of the
+ *  on-chip generic DMA requestor.
+ */
+#define DMA_RQ_C2_VIRTUAL_CHANNEL_MASK          0x0000003F
+#define DMA_RQ_C2_VIRTUAL_SIGNAL_MASK           0x00000300
+#define DMA_RQ_C2_NO_VIRTUAL_SIGNAL             0x00000000
+#define DMA_RQ_C2_SIGNAL_EVERY_DMA              0x00000100
+#define DMA_RQ_C2_SIGNAL_SOURCE_PINGPONG        0x00000200
+#define DMA_RQ_C2_SIGNAL_DEST_PINGPONG          0x00000300
+#define DMA_RQ_C2_AUDIO_CONVERT_MASK            0x0000F000
+#define DMA_RQ_C2_AC_NONE                       0x00000000
+#define DMA_RQ_C2_AC_8_TO_16_BIT                0x00001000
+#define DMA_RQ_C2_AC_MONO_TO_STEREO             0x00002000
+#define DMA_RQ_C2_AC_ENDIAN_CONVERT             0x00004000
+#define DMA_RQ_C2_AC_SIGNED_CONVERT             0x00008000
+#define DMA_RQ_C2_LOOP_END_MASK                 0x0FFF0000
+#define DMA_RQ_C2_LOOP_MASK                     0x30000000
+#define DMA_RQ_C2_NO_LOOP                       0x00000000
+#define DMA_RQ_C2_ONE_PAGE_LOOP                 0x10000000
+#define DMA_RQ_C2_TWO_PAGE_LOOP                 0x20000000
+#define DMA_RQ_C2_MULTI_PAGE_LOOP               0x30000000
+#define DMA_RQ_C2_SIGNAL_LOOP_BACK              0x40000000
+#define DMA_RQ_C2_SIGNAL_POST_BEGIN_PAGE        0x80000000
+#define DMA_RQ_C2_VIRTUAL_CHANNEL_SHIFT         0
+#define DMA_RQ_C2_LOOP_END_SHIFT                16
+
+/*
+ *  The following defines are for the flags in the source and destination words
+ *  of the on-chip generic DMA requestor.
+ */
+#define DMA_RQ_SD_ADDRESS_MASK                  0x0000FFFF
+#define DMA_RQ_SD_MEMORY_ID_MASK                0x000F0000
+#define DMA_RQ_SD_SP_PARAM_ADDR                 0x00000000
+#define DMA_RQ_SD_SP_SAMPLE_ADDR                0x00010000
+#define DMA_RQ_SD_SP_PROGRAM_ADDR               0x00020000
+#define DMA_RQ_SD_SP_DEBUG_ADDR                 0x00030000
+#define DMA_RQ_SD_OMNIMEM_ADDR                  0x000E0000
+#define DMA_RQ_SD_END_FLAG                      0x40000000
+#define DMA_RQ_SD_ERROR_FLAG                    0x80000000
+#define DMA_RQ_SD_ADDRESS_SHIFT                 0
+
+/*
+ *  The following defines are for the flags in the page map address word of the
+ *  on-chip generic DMA requestor.
+ */
+#define DMA_RQ_PMA_LOOP_THIRD_PAGE_ENTRY_MASK   0x00000FF8
+#define DMA_RQ_PMA_PAGE_TABLE_MASK              0xFFFFF000
+#define DMA_RQ_PMA_LOOP_THIRD_PAGE_ENTRY_SHIFT  3
+#define DMA_RQ_PMA_PAGE_TABLE_SHIFT             12
+
+#define BA1_VARIDEC_BUF_1       0x000
+
+#define BA1_PDTC                0x0c0    /* BA1_PLAY_DMA_TRANSACTION_COUNT_REG */
+#define BA1_PFIE                0x0c4    /* BA1_PLAY_FORMAT_&_INTERRUPT_ENABLE_REG */
+#define BA1_PBA                 0x0c8    /* BA1_PLAY_BUFFER_ADDRESS */
+#define BA1_PVOL                0x0f8    /* BA1_PLAY_VOLUME_REG */
+#define BA1_PSRC                0x288    /* BA1_PLAY_SAMPLE_RATE_CORRECTION_REG */
+#define BA1_PCTL                0x2a4    /* BA1_PLAY_CONTROL_REG */
+#define BA1_PPI                 0x2b4    /* BA1_PLAY_PHASE_INCREMENT_REG */
+
+#define BA1_CCTL                0x064    /* BA1_CAPTURE_CONTROL_REG */
+#define BA1_CIE                 0x104    /* BA1_CAPTURE_INTERRUPT_ENABLE_REG */
+#define BA1_CBA                 0x10c    /* BA1_CAPTURE_BUFFER_ADDRESS */
+#define BA1_CSRC                0x2c8    /* BA1_CAPTURE_SAMPLE_RATE_CORRECTION_REG */
+#define BA1_CCI                 0x2d8    /* BA1_CAPTURE_COEFFICIENT_INCREMENT_REG */
+#define BA1_CD                  0x2e0    /* BA1_CAPTURE_DELAY_REG */
+#define BA1_CPI                 0x2f4    /* BA1_CAPTURE_PHASE_INCREMENT_REG */
+#define BA1_CVOL                0x2f8    /* BA1_CAPTURE_VOLUME_REG */
+
+#define BA1_CFG1                0x134    /* BA1_CAPTURE_FRAME_GROUP_1_REG */
+#define BA1_CFG2                0x138    /* BA1_CAPTURE_FRAME_GROUP_2_REG */
+#define BA1_CCST                0x13c    /* BA1_CAPTURE_CONSTANT_REG */
+#define BA1_CSPB                0x340    /* BA1_CAPTURE_SPB_ADDRESS */
+
+/*
+ *
+ */
+
+#define CS46XX_MODE_OUTPUT	(1<<0)	 /* MIDI UART - output */ 
+#define CS46XX_MODE_INPUT	(1<<1)	 /* MIDI UART - input */
+
+/*
+ *
+ */
+
+#define SAVE_REG_MAX             0x10
+#define POWER_DOWN_ALL         0x7f0f
+
+/* maxinum number of AC97 codecs connected, AC97 2.0 defined 4 */
+#define MAX_NR_AC97				            4
+#define CS46XX_PRIMARY_CODEC_INDEX          0
+#define CS46XX_SECONDARY_CODEC_INDEX		1
+#define CS46XX_SECONDARY_CODEC_OFFSET		0x80
+#define CS46XX_DSP_CAPTURE_CHANNEL          1
+
+/* capture */
+#define CS46XX_DSP_CAPTURE_CHANNEL          1
+
+/* mixer */
+#define CS46XX_MIXER_SPDIF_INPUT_ELEMENT    1
+#define CS46XX_MIXER_SPDIF_OUTPUT_ELEMENT   2
+
+
+struct snd_cs46xx_pcm {
+	struct snd_dma_buffer hw_buf;
+  
+	unsigned int ctl;
+	unsigned int shift;	/* Shift count to trasform frames in bytes */
+	struct snd_pcm_indirect pcm_rec;
+	struct snd_pcm_substream *substream;
+
+	struct dsp_pcm_channel_descriptor * pcm_channel;
+
+	int pcm_channel_id;    /* Fron Rear, Center Lfe  ... */
+};
+
+struct snd_cs46xx_region {
+	char name[24];
+	unsigned long base;
+	void __iomem *remap_addr;
+	unsigned long size;
+	struct resource *resource;
+};
+
+struct snd_cs46xx {
+	int irq;
+	unsigned long ba0_addr;
+	unsigned long ba1_addr;
+	union {
+		struct {
+			struct snd_cs46xx_region ba0;
+			struct snd_cs46xx_region data0;
+			struct snd_cs46xx_region data1;
+			struct snd_cs46xx_region pmem;
+			struct snd_cs46xx_region reg;
+		} name;
+		struct snd_cs46xx_region idx[5];
+	} region;
+
+	unsigned int mode;
+	
+	struct {
+		struct snd_dma_buffer hw_buf;
+
+		unsigned int ctl;
+		unsigned int shift;	/* Shift count to trasform frames in bytes */
+		struct snd_pcm_indirect pcm_rec;
+		struct snd_pcm_substream *substream;
+	} capt;
+
+
+	int nr_ac97_codecs;
+	struct snd_ac97_bus *ac97_bus;
+	struct snd_ac97 *ac97[MAX_NR_AC97];
+
+	struct pci_dev *pci;
+	struct snd_card *card;
+	struct snd_pcm *pcm;
+
+	struct snd_rawmidi *rmidi;
+	struct snd_rawmidi_substream *midi_input;
+	struct snd_rawmidi_substream *midi_output;
+
+	spinlock_t reg_lock;
+	unsigned int midcr;
+	unsigned int uartm;
+
+	int amplifier;
+	void (*amplifier_ctrl)(struct snd_cs46xx *, int);
+	void (*active_ctrl)(struct snd_cs46xx *, int);
+  	void (*mixer_init)(struct snd_cs46xx *);
+
+	int acpi_port;
+	struct snd_kcontrol *eapd_switch; /* for amplifier hack */
+	int accept_valid;	/* accept mmap valid (for OSS) */
+
+	struct gameport *gameport;
+
+#ifdef CONFIG_SND_CS46XX_DEBUG_GPIO
+	int current_gpio;
+#endif
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+	struct semaphore spos_mutex;
+
+	struct dsp_spos_instance * dsp_spos_instance;
+
+	struct snd_pcm *pcm_rear;
+	struct snd_pcm *pcm_center_lfe;
+	struct snd_pcm *pcm_iec958;
+#else /* for compatibility */
+	struct snd_cs46xx_pcm *playback_pcm;
+	unsigned int play_ctl;
+#endif
+};
+
+int snd_cs46xx_create(struct snd_card *card,
+		      struct pci_dev *pci,
+		      int external_amp, int thinkpad,
+		      struct snd_cs46xx **rcodec);
+int snd_cs46xx_suspend(struct pci_dev *pci, pm_message_t state);
+int snd_cs46xx_resume(struct pci_dev *pci);
+
+int snd_cs46xx_pcm(struct snd_cs46xx *chip, int device, struct snd_pcm **rpcm);
+int snd_cs46xx_pcm_rear(struct snd_cs46xx *chip, int device, struct snd_pcm **rpcm);
+int snd_cs46xx_pcm_iec958(struct snd_cs46xx *chip, int device, struct snd_pcm **rpcm);
+int snd_cs46xx_pcm_center_lfe(struct snd_cs46xx *chip, int device, struct snd_pcm **rpcm);
+int snd_cs46xx_mixer(struct snd_cs46xx *chip, int spdif_device);
+int snd_cs46xx_midi(struct snd_cs46xx *chip, int device, struct snd_rawmidi **rmidi);
+int snd_cs46xx_start_dsp(struct snd_cs46xx *chip);
+int snd_cs46xx_gameport(struct snd_cs46xx *chip);
+
+#endif /* __SOUND_CS46XX_H */
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/cs46xx_dsp_scb_types.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/cs46xx_dsp_scb_types.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/cs46xx_dsp_scb_types.h	(revision 91)
@@ -0,0 +1,1213 @@
+/*
+ *  The driver for the Cirrus Logic's Sound Fusion CS46XX based soundcards
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ *
+ * NOTE: comments are copy/paste from cwcemb80.lst 
+ * provided by Tom Woller at Cirrus (my only
+ * documentation about the SP OS running inside
+ * the DSP) 
+ */
+
+#ifndef __CS46XX_DSP_SCB_TYPES_H__
+#define __CS46XX_DSP_SCB_TYPES_H__
+
+//#include <asm/byteorder.h>
+
+#ifndef ___DSP_DUAL_16BIT_ALLOC
+#if   defined(__LITTLE_ENDIAN)
+#define ___DSP_DUAL_16BIT_ALLOC(a,b) u16 a; u16 b;
+#elif defined(__BIG_ENDIAN)
+#define ___DSP_DUAL_16BIT_ALLOC(a,b) u16 b; u16 a;
+#else
+#error Not __LITTLE_ENDIAN and not __BIG_ENDIAN, then what ???
+#endif
+#endif
+
+/* This structs are used internally by the SP */
+
+struct dsp_basic_dma_req {
+	/* DMA Requestor Word 0 (DCW)  fields:
+
+	   31 [30-28]27  [26:24] 23 22 21 20 [19:18] [17:16] 15 14 13  12  11 10 9 8 7 6  [5:0]
+	   _______________________________________________________________________________________	
+	   |S| SBT  |D|  DBT    |wb|wb|  |  |  LS  |  SS   |Opt|Do|SSG|DSG|  |  | | | | | Dword   |
+	   |H|_____ |H|_________|S_|D |__|__|______|_______|___|ne|__ |__ |__|__|_|_|_|_|_Count -1|
+	*/
+	u32 dcw;                 /* DMA Control Word */
+	u32 dmw;                 /* DMA Mode Word */
+	u32 saw;                 /* Source Address Word */
+	u32 daw;                 /* Destination Address Word  */
+};
+
+struct dsp_scatter_gather_ext {
+	u32 npaw;                /* Next-Page Address Word */
+
+	/* DMA Requestor Word 5 (NPCW)  fields:
+     
+	   31-30 29 28          [27:16]              [15:12]             [11:3]                [2:0] 				
+	   _________________________________________________________________________________________	
+	   |SV  |LE|SE|   Sample-end byte offset   |         | Page-map entry offset for next  |    | 
+	   |page|__|__| ___________________________|_________|__page, if !sample-end___________|____|
+	*/
+	u32 npcw;                /* Next-Page Control Word */
+	u32 lbaw;                /* Loop-Begin Address Word */
+	u32 nplbaw;              /* Next-Page after Loop-Begin Address Word */
+	u32 sgaw;                /* Scatter/Gather Address Word */
+};
+
+struct dsp_volume_control {
+	___DSP_DUAL_16BIT_ALLOC(
+	   rightTarg,  /* Target volume for left & right channels */
+	   leftTarg
+	)
+	___DSP_DUAL_16BIT_ALLOC(
+	   rightVol,   /* Current left & right channel volumes */
+	   leftVol
+	)
+};
+
+/* Generic stream control block (SCB) structure definition */
+struct dsp_generic_scb {
+	/* For streaming I/O, the DSP should never alter any words in the DMA
+	   requestor or the scatter/gather extension.  Only ad hoc DMA request
+	   streams are free to alter the requestor (currently only occur in the
+	   DOS-based MIDI controller and in debugger-inserted code).
+    
+	   If an SCB does not have any associated DMA requestor, these 9 ints
+	   may be freed for use by other tasks, but the pointer to the SCB must
+	   still be such that the insOrd:nextSCB appear at offset 9 from the
+	   SCB pointer.
+     
+	   Basic (non scatter/gather) DMA requestor (4 ints)
+	*/
+  
+	/* Initialized by the host, only modified by DMA 
+	   R/O for the DSP task */
+	struct dsp_basic_dma_req  basic_req;  /* Optional */
+
+	/* Scatter/gather DMA requestor extension   (5 ints) 
+	   Initialized by the host, only modified by DMA
+	   DSP task never needs to even read these.
+	*/
+	struct dsp_scatter_gather_ext sg_ext;  /* Optional */
+
+	/* Sublist pointer & next stream control block (SCB) link.
+	   Initialized & modified by the host R/O for the DSP task
+	*/
+	___DSP_DUAL_16BIT_ALLOC(
+	    next_scb,     /* REQUIRED */
+	    sub_list_ptr  /* REQUIRED */
+	)
+  
+	/* Pointer to this tasks parameter block & stream function pointer 
+	   Initialized by the host  R/O for the DSP task */
+	___DSP_DUAL_16BIT_ALLOC(
+	    entry_point,  /* REQUIRED */
+	    this_spb      /* REQUIRED */
+	)
+
+	/* rsConfig register for stream buffer (rsDMA reg. 
+	   is loaded from basicReq.daw for incoming streams, or 
+	   basicReq.saw, for outgoing streams) 
+
+	   31 30 29  [28:24]     [23:16] 15 14 13 12 11 10 9 8 7 6  5      4      [3:0]
+	   ______________________________________________________________________________
+	   |DMA  |D|maxDMAsize| streamNum|dir|p|  |  |  |  | | |ds |shr 1|rev Cy | mod   |
+	   |prio |_|__________|__________|___|_|__|__|__|__|_|_|___|_____|_______|_______|
+	   31 30 29  [28:24]     [23:16] 15 14 13 12 11 10 9 8 7 6  5      4      [3:0]
+
+
+	   Initialized by the host R/O for the DSP task
+	*/
+	u32  strm_rs_config; /* REQUIRED */
+               // 
+	/* On mixer input streams: indicates mixer input stream configuration
+	   On Tees, this is copied from the stream being snooped
+
+	   Stream sample pointer & MAC-unit mode for this stream 
+     
+	   Initialized by the host Updated by the DSP task
+	*/
+	u32  strm_buf_ptr; /* REQUIRED  */
+
+	/* On mixer input streams: points to next mixer input and is updated by the
+                                   mixer subroutine in the "parent" DSP task
+				   (least-significant 16 bits are preserved, unused)
+    
+           On Tees, the pointer is copied from the stream being snooped on
+	   initialization, and, subsequently, it is copied into the
+	   stream being snooped.
+
+	   On wavetable/3D voices: the strmBufPtr will use all 32 bits to allow for
+                                   fractional phase accumulation
+
+	   Fractional increment per output sample in the input sample buffer
+
+	   (Not used on mixer input streams & redefined on Tees)
+	   On wavetable/3D voices: this 32-bit word specifies the integer.fractional 
+	   increment per output sample.
+	*/
+	u32  strmPhiIncr;
+
+
+	/* Standard stereo volume control
+	   Initialized by the host (host updates target volumes) 
+
+	   Current volumes update by the DSP task
+	   On mixer input streams: required & updated by the mixer subroutine in the
+                                   "parent" DSP task
+
+	   On Tees, both current & target volumes are copied up on initialization,
+	   and, subsequently, the target volume is copied up while the current
+	   volume is copied down.
+     
+	   These two 32-bit words are redefined for wavetable & 3-D voices.    
+	*/
+	struct dsp_volume_control vol_ctrl_t;   /* Optional */
+};
+
+
+struct dsp_spos_control_block {
+	/* WARNING: Certain items in this structure are modified by the host
+	            Any dword that can be modified by the host, must not be
+		    modified by the SP as the host can only do atomic dword
+		    writes, and to do otherwise, even a read modify write, 
+		    may lead to corrupted data on the SP.
+  
+		    This rule does not apply to one off boot time initialisation prior to starting the SP
+	*/
+
+
+	___DSP_DUAL_16BIT_ALLOC( 
+	/* First element on the Hyper forground task tree */
+	    hfg_tree_root_ptr,  /* HOST */			    
+	/* First 3 dwords are written by the host and read-only on the DSP */
+	    hfg_stack_base      /* HOST */
+	)
+
+	___DSP_DUAL_16BIT_ALLOC(
+	/* Point to this data structure to enable easy access */
+	    spos_cb_ptr,	 /* SP */
+	    prev_task_tree_ptr   /* SP && HOST */
+	)
+
+	___DSP_DUAL_16BIT_ALLOC(
+	/* Currently Unused */
+	    xxinterval_timer_period,
+	/* Enable extension of SPOS data structure */
+	    HFGSPB_ptr
+	)
+
+
+	___DSP_DUAL_16BIT_ALLOC(
+	    xxnum_HFG_ticks_thisInterval,
+	/* Modified by the DSP */
+	    xxnum_tntervals
+	)
+
+
+	/* Set by DSP upon encountering a trap (breakpoint) or a spurious
+	   interrupt.  The host must clear this dword after reading it
+	   upon receiving spInt1. */
+	___DSP_DUAL_16BIT_ALLOC(
+	    spurious_int_flag,	 /* (Host & SP) Nature of the spurious interrupt */
+	    trap_flag            /* (Host & SP) Nature of detected Trap */
+	)
+
+	___DSP_DUAL_16BIT_ALLOC(
+	    unused2,					
+	    invalid_IP_flag        /* (Host & SP ) Indicate detection of invalid instruction pointer */
+	)
+
+	___DSP_DUAL_16BIT_ALLOC(
+	/* pointer to forground task tree header for use in next task search */
+	    fg_task_tree_hdr_ptr,	  /* HOST */		
+	/* Data structure for controlling synchronous link update */
+	    hfg_sync_update_ptr           /* HOST */
+	)
+  
+	___DSP_DUAL_16BIT_ALLOC(
+	     begin_foreground_FCNT,  /* SP */
+	/* Place holder for holding sleep timing */
+	     last_FCNT_before_sleep  /* SP */
+	)
+
+	___DSP_DUAL_16BIT_ALLOC(
+	    unused7,           /* SP */
+	    next_task_treePtr  /* SP */
+	)
+
+	u32 unused5;        
+
+	___DSP_DUAL_16BIT_ALLOC(
+	    active_flags,   /* SP */
+	/* State flags, used to assist control of execution of Hyper Forground */
+	    HFG_flags       /* SP */
+	)
+
+	___DSP_DUAL_16BIT_ALLOC(
+	    unused9,
+	    unused8
+	)
+                              
+	/* Space for saving enough context so that we can set up enough 
+	   to save some more context.
+	*/
+	u32 rFE_save_for_invalid_IP;
+	u32 r32_save_for_spurious_int;
+	u32 r32_save_for_trap;
+	u32 r32_save_for_HFG;
+};
+
+/* SPB for MIX_TO_OSTREAM algorithm family */
+struct dsp_mix2_ostream_spb
+{
+	/* 16b.16b integer.frac approximation to the
+	   number of 3 sample triplets to output each
+	   frame. (approximation must be floor, to
+	   insure that the fractional error is always
+	   positive)
+	*/
+	u32 outTripletsPerFrame;
+
+	/* 16b.16b integer.frac accumulated number of
+	   output triplets since the start of group 
+	*/
+	u32 accumOutTriplets;  
+};
+
+/* SCB for Timing master algorithm */
+struct dsp_timing_master_scb {
+	/* First 12 dwords from generic_scb_t */
+	struct dsp_basic_dma_req  basic_req;  /* Optional */
+	struct dsp_scatter_gather_ext sg_ext;  /* Optional */
+	___DSP_DUAL_16BIT_ALLOC(
+	    next_scb,     /* REQUIRED */
+	    sub_list_ptr  /* REQUIRED */
+	)
+
+	___DSP_DUAL_16BIT_ALLOC(
+	    entry_point,  /* REQUIRED */
+	    this_spb      /* REQUIRED */
+	)
+
+	___DSP_DUAL_16BIT_ALLOC(
+	/* Initial values are 0000:xxxx */
+ 	    reserved,
+	    extra_sample_accum
+	)
+
+  
+	/* Initial values are xxxx:0000
+	   hi: Current CODEC output FIFO pointer
+	       (0 to 0x0f)
+           lo: Flag indicating that the CODEC
+	       FIFO is sync'd (host clears to
+	       resynchronize the FIFO pointer
+	       upon start/restart) 
+	*/
+	___DSP_DUAL_16BIT_ALLOC(
+	    codec_FIFO_syncd, 
+	    codec_FIFO_ptr
+	)
+  
+	/* Init. 8000:0005 for 44.1k
+                 8000:0001 for 48k
+	   hi: Fractional sample accumulator 0.16b
+	   lo: Number of frames remaining to be
+	       processed in the current group of
+	       frames
+	*/
+	___DSP_DUAL_16BIT_ALLOC(
+	    frac_samp_accum_qm1,
+	    TM_frms_left_in_group
+	) 
+
+	/* Init. 0001:0005 for 44.1k
+                 0000:0001 for 48k
+	   hi: Fractional sample correction factor 0.16b
+	       to be added every frameGroupLength frames
+	       to correct for truncation error in
+	       nsamp_per_frm_q15
+	   lo: Number of frames in the group
+	*/
+	___DSP_DUAL_16BIT_ALLOC(
+	    frac_samp_correction_qm1,
+	    TM_frm_group_length  
+	)
+
+	/* Init. 44.1k*65536/8k = 0x00058333 for 44.1k
+                 48k*65536/8k = 0x00060000 for 48k
+	   16b.16b integer.frac approximation to the
+	   number of samples to output each frame.
+	   (approximation must be floor, to insure */
+	u32 nsamp_per_frm_q15;
+};
+
+/* SCB for CODEC output algorithm */
+struct dsp_codec_output_scb {
+	/* First 13 dwords from generic_scb_t */
+	struct dsp_basic_dma_req  basic_req;  /* Optional */
+	struct dsp_scatter_gather_ext sg_ext;  /* Optional */
+	___DSP_DUAL_16BIT_ALLOC(
+	    next_scb,       /* REQUIRED */
+	    sub_list_ptr    /* REQUIRED */
+	)
+
+	___DSP_DUAL_16BIT_ALLOC(
+	    entry_point,    /* REQUIRED */
+	    this_spb        /* REQUIRED */
+	)
+
+	u32 strm_rs_config; /* REQUIRED */
+
+	u32 strm_buf_ptr;   /* REQUIRED */
+
+	/* NOTE: The CODEC output task reads samples from the first task on its
+                 sublist at the stream buffer pointer (init. to lag DMA destination
+		 address word).  After the required number of samples is transferred,
+		 the CODEC output task advances sub_list_ptr->strm_buf_ptr past the samples
+		 consumed.
+	*/
+
+	/* Init. 0000:0010 for SDout
+                 0060:0010 for SDout2
+		 0080:0010 for SDout3
+	   hi: Base IO address of FIFO to which
+	       the left-channel samples are to
+	       be written.
+	   lo: Displacement for the base IO
+	       address for left-channel to obtain
+	       the base IO address for the FIFO
+	       to which the right-channel samples
+	       are to be written.
+	*/
+	___DSP_DUAL_16BIT_ALLOC(
+	    left_chan_base_IO_addr,
+	    right_chan_IO_disp
+	)
+
+
+	/* Init: 0x0080:0004 for non-AC-97
+	   Init: 0x0080:0000 for AC-97
+	   hi: Exponential volume change rate
+	       for input stream
+	   lo: Positive shift count to shift the
+	       16-bit input sample to obtain the
+	       32-bit output word
+	*/
+	___DSP_DUAL_16BIT_ALLOC(
+	    CO_scale_shift_count, 
+	    CO_exp_vol_change_rate
+	)
+
+	/* Pointer to SCB at end of input chain */
+	___DSP_DUAL_16BIT_ALLOC(
+	    reserved,
+	    last_sub_ptr
+	)
+};
+
+/* SCB for CODEC input algorithm */
+struct dsp_codec_input_scb {
+	/* First 13 dwords from generic_scb_t */
+	struct dsp_basic_dma_req  basic_req;  /* Optional */
+	struct dsp_scatter_gather_ext sg_ext;  /* Optional */
+	___DSP_DUAL_16BIT_ALLOC(
+	    next_scb,       /* REQUIRED */
+	    sub_list_ptr    /* REQUIRED */
+	)
+
+	___DSP_DUAL_16BIT_ALLOC(
+	    entry_point,    /* REQUIRED */
+	    this_spb        /* REQUIRED */
+	)
+
+	u32 strm_rs_config; /* REQUIRED */
+	u32 strm_buf_ptr;   /* REQUIRED */
+
+	/* NOTE: The CODEC input task reads samples from the hardware FIFO 
+                 sublist at the DMA source address word (sub_list_ptr->basic_req.saw).
+                 After the required number of samples is transferred, the CODEC
+                 output task advances sub_list_ptr->basic_req.saw past the samples
+                 consumed.  SPuD must initialize the sub_list_ptr->basic_req.saw
+                 to point half-way around from the initial sub_list_ptr->strm_nuf_ptr
+                 to allow for lag/lead.
+	*/
+
+	/* Init. 0000:0010 for SDout
+                 0060:0010 for SDout2
+		 0080:0010 for SDout3
+	   hi: Base IO address of FIFO to which
+	       the left-channel samples are to
+	       be written.
+	   lo: Displacement for the base IO
+	       address for left-channel to obtain
+	       the base IO address for the FIFO
+	       to which the right-channel samples
+	       are to be written.
+	*/
+	___DSP_DUAL_16BIT_ALLOC(
+	    rightChanINdisp, 
+	    left_chan_base_IN_addr
+	)
+	/* Init. ?:fffc
+	   lo: Negative shift count to shift the
+	       32-bit input dword to obtain the
+	       16-bit sample msb-aligned (count
+	       is negative to shift left)
+	*/
+	___DSP_DUAL_16BIT_ALLOC(
+	    scaleShiftCount, 
+	    reserver1
+	)
+
+	u32  reserved2;
+};
+
+
+struct dsp_pcm_serial_input_scb {
+	/* First 13 dwords from generic_scb_t */
+	struct dsp_basic_dma_req  basic_req;  /* Optional */
+	struct dsp_scatter_gather_ext sg_ext;  /* Optional */
+	___DSP_DUAL_16BIT_ALLOC(
+	    next_scb,       /* REQUIRED */
+	    sub_list_ptr    /* REQUIRED */
+	)
+
+	___DSP_DUAL_16BIT_ALLOC(
+	    entry_point,    /* REQUIRED */
+	    this_spb        /* REQUIRED */
+	)
+
+	u32 strm_buf_ptr;   /* REQUIRED */
+	u32 strm_rs_config; /* REQUIRED */
+  
+	/* Init. Ptr to CODEC input SCB
+	   hi: Pointer to the SCB containing the
+	       input buffer to which CODEC input
+	       samples are written
+	   lo: Flag indicating the link to the CODEC
+	       input task is to be initialized
+	*/
+	___DSP_DUAL_16BIT_ALLOC(
+	    init_codec_input_link,
+	    codec_input_buf_scb
+	)
+
+	/* Initialized by the host (host updates target volumes) */
+	struct dsp_volume_control psi_vol_ctrl;   
+  
+};
+
+struct dsp_src_task_scb {
+	___DSP_DUAL_16BIT_ALLOC(
+	    frames_left_in_gof,
+	    gofs_left_in_sec
+	)
+
+	___DSP_DUAL_16BIT_ALLOC(
+	    const2_thirds,
+	    num_extra_tnput_samples
+	)
+
+	___DSP_DUAL_16BIT_ALLOC(
+	    cor_per_gof,
+	    correction_per_sec 
+	)
+
+	___DSP_DUAL_16BIT_ALLOC(
+	    output_buf_producer_ptr,  
+	    junk_DMA_MID
+	)
+
+	___DSP_DUAL_16BIT_ALLOC(
+	    gof_length,  
+	    gofs_per_sec
+	)
+
+	u32 input_buf_strm_config;
+
+	___DSP_DUAL_16BIT_ALLOC(
+	    reserved_for_SRC_use,
+	    input_buf_consumer_ptr
+	)
+
+	u32 accum_phi;
+
+	___DSP_DUAL_16BIT_ALLOC(
+	    exp_src_vol_change_rate,
+	    input_buf_producer_ptr
+	)
+
+	___DSP_DUAL_16BIT_ALLOC(
+	    src_next_scb,
+	    src_sub_list_ptr
+	)
+
+	___DSP_DUAL_16BIT_ALLOC(
+	    src_entry_point,
+	    src_this_sbp
+	)
+
+	u32  src_strm_rs_config;
+	u32  src_strm_buf_ptr;
+  
+	u32   phiIncr6int_26frac;
+  
+	struct dsp_volume_control src_vol_ctrl;
+};
+
+struct dsp_decimate_by_pow2_scb {
+	/* decimationFactor = 2, 4, or 8 (larger factors waste too much memory
+	                                  when compared to cascading decimators)
+	*/
+	___DSP_DUAL_16BIT_ALLOC(
+	    dec2_coef_base_ptr,
+	    dec2_coef_increment
+	)
+
+	/* coefIncrement = 128 / decimationFactor (for our ROM filter)
+	   coefBasePtr = 0x8000 (for our ROM filter)
+	*/
+	___DSP_DUAL_16BIT_ALLOC(
+	    dec2_in_samples_per_out_triplet,
+	    dec2_extra_in_samples
+	)
+	/* extraInSamples: # of accumulated, unused input samples (init. to 0)
+	   inSamplesPerOutTriplet = 3 * decimationFactor
+	*/
+
+	___DSP_DUAL_16BIT_ALLOC(
+	    dec2_const2_thirds,
+	    dec2_half_num_taps_mp5
+	)
+	/* halfNumTapsM5: (1/2 number of taps in decimation filter) minus 5
+	   const2thirds: constant 2/3 in 16Q0 format (sign.15)
+	*/
+
+	___DSP_DUAL_16BIT_ALLOC(
+	    dec2_output_buf_producer_ptr,
+	    dec2_junkdma_mid
+	)
+
+	u32  dec2_reserved2;
+
+	u32  dec2_input_nuf_strm_config;
+	/* inputBufStrmConfig: rsConfig for the input buffer to the decimator
+	   (buffer size = decimationFactor * 32 dwords)
+	*/
+
+	___DSP_DUAL_16BIT_ALLOC(
+	    dec2_phi_incr,
+	    dec2_input_buf_consumer_ptr
+	)
+	/* inputBufConsumerPtr: Input buffer read pointer (into SRC filter)
+	   phiIncr = decimationFactor * 4
+	*/
+
+	u32 dec2_reserved3;
+
+	___DSP_DUAL_16BIT_ALLOC(
+	    dec2_exp_vol_change_rate,
+	    dec2_input_buf_producer_ptr
+	)
+	/* inputBufProducerPtr: Input buffer write pointer
+	   expVolChangeRate: Exponential volume change rate for possible
+	                     future mixer on input streams
+	*/
+
+	___DSP_DUAL_16BIT_ALLOC(
+	    dec2_next_scb,
+	    dec2_sub_list_ptr
+	)
+
+	___DSP_DUAL_16BIT_ALLOC(
+	    dec2_entry_point,
+	    dec2_this_spb
+	)
+
+	u32  dec2_strm_rs_config;
+	u32  dec2_strm_buf_ptr;
+
+	u32  dec2_reserved4;
+
+	struct dsp_volume_control dec2_vol_ctrl; /* Not used! */
+};
+
+struct dsp_vari_decimate_scb {
+	___DSP_DUAL_16BIT_ALLOC(
+	    vdec_frames_left_in_gof,
+	    vdec_gofs_left_in_sec
+	)
+
+	___DSP_DUAL_16BIT_ALLOC(
+	    vdec_const2_thirds,
+	    vdec_extra_in_samples
+	)
+	/* extraInSamples: # of accumulated, unused input samples (init. to 0)
+	   const2thirds: constant 2/3 in 16Q0 format (sign.15) */
+
+	___DSP_DUAL_16BIT_ALLOC(
+	    vdec_cor_per_gof,
+	    vdec_correction_per_sec
+	)
+
+	___DSP_DUAL_16BIT_ALLOC(
+	    vdec_output_buf_producer_ptr,
+	    vdec_input_buf_consumer_ptr
+	)
+	/* inputBufConsumerPtr: Input buffer read pointer (into SRC filter) */
+	___DSP_DUAL_16BIT_ALLOC(
+	    vdec_gof_length,
+	    vdec_gofs_per_sec
+	)
+
+	u32  vdec_input_buf_strm_config;
+	/* inputBufStrmConfig: rsConfig for the input buffer to the decimator
+	   (buffer size = 64 dwords) */
+	u32  vdec_coef_increment;
+	/* coefIncrement = - 128.0 / decimationFactor (as a 32Q15 number) */
+
+	u32  vdec_accumphi;
+	/* accumPhi: accumulated fractional phase increment (6.26) */
+
+	___DSP_DUAL_16BIT_ALLOC(
+ 	    vdec_exp_vol_change_rate,
+	    vdec_input_buf_producer_ptr
+	)
+	/* inputBufProducerPtr: Input buffer write pointer
+	   expVolChangeRate: Exponential volume change rate for possible
+	   future mixer on input streams */
+
+	___DSP_DUAL_16BIT_ALLOC(
+	    vdec_next_scb,
+	    vdec_sub_list_ptr
+	)
+
+	___DSP_DUAL_16BIT_ALLOC(
+	    vdec_entry_point,
+	    vdec_this_spb
+	)
+
+	u32 vdec_strm_rs_config;
+	u32 vdec_strm_buf_ptr;
+
+	u32 vdec_phi_incr_6int_26frac;
+
+	struct dsp_volume_control vdec_vol_ctrl;
+};
+
+
+/* SCB for MIX_TO_OSTREAM algorithm family */
+struct dsp_mix2_ostream_scb {
+	/* First 13 dwords from generic_scb_t */
+	struct dsp_basic_dma_req  basic_req;  /* Optional */
+	struct dsp_scatter_gather_ext sg_ext;  /* Optional */
+	___DSP_DUAL_16BIT_ALLOC(
+	    next_scb,       /* REQUIRED */
+	    sub_list_ptr    /* REQUIRED */
+	)
+
+	___DSP_DUAL_16BIT_ALLOC(
+	    entry_point,    /* REQUIRED */
+	    this_spb        /* REQUIRED */
+	)
+
+	u32 strm_rs_config; /* REQUIRED */
+	u32 strm_buf_ptr;   /* REQUIRED */
+
+
+	/* hi: Number of mixed-down input triplets
+	       computed since start of group
+	   lo: Number of frames remaining to be
+	       processed in the current group of
+	       frames
+	*/
+	___DSP_DUAL_16BIT_ALLOC(
+	    frames_left_in_group,
+	    accum_input_triplets
+	)
+
+	/* hi: Exponential volume change rate
+	       for mixer on input streams
+	   lo: Number of frames in the group
+	*/
+	___DSP_DUAL_16BIT_ALLOC(
+	    frame_group_length,
+	    exp_vol_change_rate
+	)
+  
+	___DSP_DUAL_16BIT_ALLOC(
+	    const_FFFF,
+	    const_zero
+	)
+};
+
+
+/* SCB for S16_MIX algorithm */
+struct dsp_mix_only_scb {
+	/* First 13 dwords from generic_scb_t */
+	struct dsp_basic_dma_req  basic_req;  /* Optional */
+	struct dsp_scatter_gather_ext sg_ext;  /* Optional */
+	___DSP_DUAL_16BIT_ALLOC(
+	    next_scb,       /* REQUIRED */
+	    sub_list_ptr    /* REQUIRED */
+	)
+
+	___DSP_DUAL_16BIT_ALLOC(
+	    entry_point,    /* REQUIRED */
+	    this_spb        /* REQUIRED */
+	)
+
+	u32 strm_rs_config; /* REQUIRED */
+	u32 strm_buf_ptr;   /* REQUIRED */
+
+	u32 reserved;
+	struct dsp_volume_control vol_ctrl;
+};
+
+/* SCB for the async. CODEC input algorithm */
+struct dsp_async_codec_input_scb {
+	u32 io_free2;     
+  
+	u32 io_current_total;
+	u32 io_previous_total;
+  
+	u16 io_count;
+	u16 io_count_limit;
+  
+	u16 o_fifo_base_addr;            
+	u16 ost_mo_format;
+	/* 1 = stereo; 0 = mono 
+	   xxx for ASER 1 (not allowed); 118 for ASER2 */
+
+	u32  ostrm_rs_config;
+	u32  ostrm_buf_ptr;
+  
+	___DSP_DUAL_16BIT_ALLOC(
+	    io_sclks_per_lr_clk,
+	    io_io_enable
+	)
+
+	u32  io_free4;
+
+	___DSP_DUAL_16BIT_ALLOC(  
+	    io_next_scb,
+	    io_sub_list_ptr
+	)
+
+	___DSP_DUAL_16BIT_ALLOC(
+	    io_entry_point,
+	    io_this_spb
+	)
+
+	u32 istrm_rs_config;
+	u32 istrm_buf_ptr;
+
+	/* Init. 0000:8042: for ASER1
+                 0000:8044: for ASER2  */
+	___DSP_DUAL_16BIT_ALLOC(
+	    io_stat_reg_addr,
+	    iofifo_pointer
+	)
+
+	/* Init 1 stero:100 ASER1
+	   Init 0 mono:110 ASER2 
+	*/
+	___DSP_DUAL_16BIT_ALLOC(
+	    ififo_base_addr,            
+	    ist_mo_format
+	)
+
+	u32 i_free;
+};
+
+
+/* SCB for the SP/DIF CODEC input and output */
+struct dsp_spdifiscb {
+	___DSP_DUAL_16BIT_ALLOC(
+	    status_ptr,     
+	    status_start_ptr
+	)
+
+	u32 current_total;
+	u32 previous_total;
+
+	___DSP_DUAL_16BIT_ALLOC(
+	    count,
+	    count_limit
+	)
+
+	u32 status_data;
+
+	___DSP_DUAL_16BIT_ALLOC(  
+	    status,
+	    free4
+	)
+
+	u32 free3;
+
+	___DSP_DUAL_16BIT_ALLOC(  
+	    free2,
+	    bit_count
+	)
+
+	u32  temp_status;
+  
+	___DSP_DUAL_16BIT_ALLOC(
+	    next_SCB,
+	    sub_list_ptr
+	)
+
+	___DSP_DUAL_16BIT_ALLOC(
+	    entry_point,
+	    this_spb
+	)
+
+	u32  strm_rs_config;
+	u32  strm_buf_ptr;
+  
+	___DSP_DUAL_16BIT_ALLOC(
+	    stat_reg_addr, 
+	    fifo_pointer
+	)
+
+	___DSP_DUAL_16BIT_ALLOC(
+	    fifo_base_addr, 
+	    st_mo_format
+	)
+
+	u32  free1;
+};
+
+
+/* SCB for the SP/DIF CODEC input and output  */
+struct dsp_spdifoscb {		 
+
+	u32 free2;     
+
+	u32 free3[4];             
+
+	/* Need to be here for compatibility with AsynchFGTxCode */
+	u32 strm_rs_config;
+                               
+	u32 strm_buf_ptr;
+
+	___DSP_DUAL_16BIT_ALLOC(  
+	    status,
+	    free5
+	)
+
+	u32 free4;
+
+	___DSP_DUAL_16BIT_ALLOC(  
+	    next_scb,
+	    sub_list_ptr
+	)
+
+	___DSP_DUAL_16BIT_ALLOC(
+	    entry_point,
+	    this_spb
+	)
+
+	u32 free6[2];
+  
+	___DSP_DUAL_16BIT_ALLOC(
+	    stat_reg_addr, 
+	    fifo_pointer
+	)
+
+	___DSP_DUAL_16BIT_ALLOC(
+	    fifo_base_addr,
+	    st_mo_format
+	)
+
+	u32  free1;                                         
+};
+
+
+struct dsp_asynch_fg_rx_scb {
+	___DSP_DUAL_16BIT_ALLOC(
+	    bot_buf_mask,
+	    buf_Mask
+	)
+
+	___DSP_DUAL_16BIT_ALLOC(
+	    max,
+	    min
+	)
+
+	___DSP_DUAL_16BIT_ALLOC(
+	    old_producer_pointer,
+	    hfg_scb_ptr
+	)
+
+	___DSP_DUAL_16BIT_ALLOC(
+	    delta,
+	    adjust_count
+	)
+
+	u32 unused2[5];  
+
+	___DSP_DUAL_16BIT_ALLOC(  
+	    sibling_ptr,  
+	    child_ptr
+	)
+
+	___DSP_DUAL_16BIT_ALLOC(
+	    code_ptr,
+	    this_ptr
+	)
+
+	u32 strm_rs_config; 
+
+	u32 strm_buf_ptr;
+  
+	u32 unused_phi_incr;
+  
+	___DSP_DUAL_16BIT_ALLOC(
+	    right_targ,   
+	    left_targ
+	)
+
+	___DSP_DUAL_16BIT_ALLOC(
+	    right_vol,
+	    left_vol
+	)
+};
+
+
+struct dsp_asynch_fg_tx_scb {
+	___DSP_DUAL_16BIT_ALLOC(
+	    not_buf_mask,
+	    buf_mask
+	)
+
+	___DSP_DUAL_16BIT_ALLOC(
+	    max,
+	    min
+	)
+
+	___DSP_DUAL_16BIT_ALLOC(
+	    unused1,
+	    hfg_scb_ptr
+	)
+
+	___DSP_DUAL_16BIT_ALLOC(
+	    delta,
+	    adjust_count
+	)
+
+	u32 accum_phi;
+
+	___DSP_DUAL_16BIT_ALLOC(
+	    unused2,
+	    const_one_third
+	)
+
+	u32 unused3[3];
+
+	___DSP_DUAL_16BIT_ALLOC(
+	    sibling_ptr,
+	    child_ptr
+	)
+
+	___DSP_DUAL_16BIT_ALLOC(
+	    codePtr,
+	    this_ptr
+	)
+
+	u32 strm_rs_config;
+
+	u32 strm_buf_ptr;
+
+	u32 phi_incr;
+
+	___DSP_DUAL_16BIT_ALLOC(
+	    unused_right_targ,
+	    unused_left_targ
+	)
+
+	___DSP_DUAL_16BIT_ALLOC(
+	    unused_right_vol,
+	    unused_left_vol
+	)
+};
+
+
+struct dsp_output_snoop_scb {
+	/* First 13 dwords from generic_scb_t */
+	struct dsp_basic_dma_req  basic_req;  /* Optional */
+	struct dsp_scatter_gather_ext sg_ext;  /* Optional */
+	___DSP_DUAL_16BIT_ALLOC(
+	    next_scb,       /* REQUIRED */
+	    sub_list_ptr    /* REQUIRED */
+	)
+
+	___DSP_DUAL_16BIT_ALLOC(
+	    entry_point,    /* REQUIRED */
+	    this_spb        /* REQUIRED */
+	)
+
+	u32 strm_rs_config; /* REQUIRED */
+	u32 strm_buf_ptr;   /* REQUIRED */
+
+	___DSP_DUAL_16BIT_ALLOC(
+	    init_snoop_input_link,
+	    snoop_child_input_scb
+	)
+
+	u32 snoop_input_buf_ptr;
+
+	___DSP_DUAL_16BIT_ALLOC(
+	    reserved,
+	    input_scb
+	)
+};
+
+struct dsp_spio_write_scb {
+	___DSP_DUAL_16BIT_ALLOC(
+	    address1,
+	    address2
+	)
+
+	u32 data1;
+
+	u32 data2;
+
+	___DSP_DUAL_16BIT_ALLOC(
+	    address3,
+	    address4
+	)
+
+	u32 data3;
+
+	u32 data4;
+
+	___DSP_DUAL_16BIT_ALLOC(
+	    unused1,
+	    data_ptr
+	)
+
+	u32 unused2[2];
+
+	___DSP_DUAL_16BIT_ALLOC(
+	    sibling_ptr,
+	    child_ptr
+	)
+
+	___DSP_DUAL_16BIT_ALLOC(
+	    entry_point,
+	    this_ptr
+	)
+
+	u32 unused3[5];
+};
+
+struct dsp_magic_snoop_task {
+	u32 i0;
+	u32 i1;
+
+	u32 strm_buf_ptr1;
+  
+	u16 i2;
+	u16 snoop_scb;
+
+	u32 i3;
+	u32 i4;
+	u32 i5;
+	u32 i6;
+
+	u32 i7;
+
+	___DSP_DUAL_16BIT_ALLOC(
+	    next_scb,
+	    sub_list_ptr
+	)
+
+	___DSP_DUAL_16BIT_ALLOC(
+	    entry_point,
+	    this_ptr
+	)
+
+	u32 strm_buf_config;
+	u32 strm_buf_ptr2;
+
+	u32 i8;
+
+	struct dsp_volume_control vdec_vol_ctrl;
+};
+
+
+struct dsp_filter_scb {
+	___DSP_DUAL_16BIT_ALLOC(
+	      a0_right,          /* 0x00 */
+	      a0_left
+	)
+	___DSP_DUAL_16BIT_ALLOC(
+	      a1_right,          /* 0x01 */
+	      a1_left
+	)
+	___DSP_DUAL_16BIT_ALLOC(
+	      a2_right,          /* 0x02 */
+	      a2_left
+	)
+	___DSP_DUAL_16BIT_ALLOC(
+	      output_buf_ptr,    /* 0x03 */
+	      init
+	)
+
+	___DSP_DUAL_16BIT_ALLOC(
+	      filter_unused3,    /* 0x04 */
+	      filter_unused2
+	)
+
+	u32 prev_sample_output1; /* 0x05 */
+	u32 prev_sample_output2; /* 0x06 */
+	u32 prev_sample_input1;  /* 0x07 */
+	u32 prev_sample_input2;  /* 0x08 */
+
+	___DSP_DUAL_16BIT_ALLOC(
+	      next_scb_ptr,      /* 0x09 */
+	      sub_list_ptr
+	)
+
+	___DSP_DUAL_16BIT_ALLOC(
+	      entry_point,       /* 0x0A */
+	      spb_ptr
+	)
+
+	u32  strm_rs_config;     /* 0x0B */
+	u32  strm_buf_ptr;       /* 0x0C */
+
+	___DSP_DUAL_16BIT_ALLOC(
+              b0_right,          /* 0x0D */
+	      b0_left
+	)
+	___DSP_DUAL_16BIT_ALLOC(
+              b1_right,          /* 0x0E */
+	      b1_left
+	)
+	___DSP_DUAL_16BIT_ALLOC(
+              b2_right,          /* 0x0F */
+	      b2_left
+	)
+};
+#endif /* __DSP_SCB_TYPES_H__ */
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/cs46xx_dsp_spos.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/cs46xx_dsp_spos.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/cs46xx_dsp_spos.h	(revision 91)
@@ -0,0 +1,230 @@
+/*
+ *  The driver for the Cirrus Logic's Sound Fusion CS46XX based soundcards
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#ifndef __CS46XX_DSP_SPOS_H__
+#define __CS46XX_DSP_SPOS_H__
+
+#include "cs46xx_dsp_scb_types.h"
+#include "cs46xx_dsp_task_types.h"
+
+#define SYMBOL_CONSTANT  0x0
+#define SYMBOL_SAMPLE    0x1
+#define SYMBOL_PARAMETER 0x2
+#define SYMBOL_CODE      0x3
+
+#define SEGTYPE_SP_PROGRAM              0x00000001
+#define SEGTYPE_SP_PARAMETER            0x00000002
+#define SEGTYPE_SP_SAMPLE               0x00000003
+#define SEGTYPE_SP_COEFFICIENT          0x00000004
+
+#define DSP_SPOS_UU      0x0deadul     /* unused */
+#define DSP_SPOS_DC      0x0badul      /* don't care */
+#define DSP_SPOS_DC_DC   0x0bad0badul  /* don't care */
+#define DSP_SPOS_UUUU    0xdeadc0edul  /* unused */
+#define DSP_SPOS_UUHI    0xdeadul
+#define DSP_SPOS_UULO    0xc0edul
+#define DSP_SPOS_DCDC    0x0badf1d0ul  /* don't care */
+#define DSP_SPOS_DCDCHI  0x0badul
+#define DSP_SPOS_DCDCLO  0xf1d0ul
+
+#define DSP_MAX_TASK_NAME   60
+#define DSP_MAX_SYMBOL_NAME 100
+#define DSP_MAX_SCB_NAME    60
+#define DSP_MAX_SCB_DESC    200
+#define DSP_MAX_TASK_DESC   50
+
+#define DSP_MAX_PCM_CHANNELS 32
+#define DSP_MAX_SRC_NR       14
+
+#define DSP_PCM_MAIN_CHANNEL        1
+#define DSP_PCM_REAR_CHANNEL        2
+#define DSP_PCM_CENTER_LFE_CHANNEL  3
+#define DSP_PCM_S71_CHANNEL         4 /* surround 7.1 */
+#define DSP_IEC958_CHANNEL          5
+
+#define DSP_SPDIF_STATUS_OUTPUT_ENABLED       1
+#define DSP_SPDIF_STATUS_PLAYBACK_OPEN        2
+#define DSP_SPDIF_STATUS_HW_ENABLED           4
+#define DSP_SPDIF_STATUS_INPUT_CTRL_ENABLED   8
+
+struct dsp_symbol_entry {
+	u32 address;
+	char symbol_name[DSP_MAX_SYMBOL_NAME];
+	int symbol_type;
+
+	/* initialized by driver */
+	struct dsp_module_desc * module;
+	int deleted;
+};
+
+struct dsp_symbol_desc {
+	int nsymbols;
+
+	struct dsp_symbol_entry *symbols;
+
+	/* initialized by driver */
+	int highest_frag_index;
+};
+
+struct dsp_segment_desc {
+	int segment_type;
+	u32 offset;
+	u32 size;
+	u32 * data;
+};
+
+struct dsp_module_desc {
+	char * module_name;
+	struct dsp_symbol_desc symbol_table;
+	int nsegments;
+	struct dsp_segment_desc * segments;
+
+	/* initialized by driver */
+	u32 overlay_begin_address;
+	u32 load_address;
+	int nfixups;
+};
+
+struct dsp_scb_descriptor {
+	char scb_name[DSP_MAX_SCB_NAME];
+	u32 address;
+	int index;
+
+	struct dsp_scb_descriptor * sub_list_ptr;
+	struct dsp_scb_descriptor * next_scb_ptr;
+	struct dsp_scb_descriptor * parent_scb_ptr;
+
+	struct dsp_symbol_entry * task_entry;
+	struct dsp_symbol_entry * scb_symbol;
+
+	struct snd_info_entry *proc_info;
+	int ref_count;
+	spinlock_t lock;
+
+	int deleted;
+};
+
+struct dsp_task_descriptor {
+	char task_name[DSP_MAX_TASK_NAME];
+	int size;
+	u32 address;
+	int index;
+};
+
+struct dsp_pcm_channel_descriptor {
+	int active;
+	int src_slot;
+	int pcm_slot;
+	u32 sample_rate;
+	u32 unlinked;
+	struct dsp_scb_descriptor * pcm_reader_scb;
+	struct dsp_scb_descriptor * src_scb;
+	struct dsp_scb_descriptor * mixer_scb;
+
+	void * private_data;
+};
+
+struct dsp_spos_instance {
+	struct dsp_symbol_desc symbol_table; /* currently availble loaded symbols in SP */
+
+	int nmodules;
+	struct dsp_module_desc * modules; /* modules loaded into SP */
+
+	struct dsp_segment_desc code;
+
+	/* Main PCM playback mixer */
+	struct dsp_scb_descriptor * master_mix_scb;
+	u16 dac_volume_right;
+	u16 dac_volume_left;
+
+	/* Rear/surround PCM playback mixer */
+	struct dsp_scb_descriptor * rear_mix_scb;
+
+	/* Center/LFE mixer */
+	struct dsp_scb_descriptor * center_lfe_mix_scb;
+
+	int npcm_channels;
+	int nsrc_scb;
+	struct dsp_pcm_channel_descriptor pcm_channels[DSP_MAX_PCM_CHANNELS];
+	int src_scb_slots[DSP_MAX_SRC_NR];
+
+	/* cache this symbols */
+	struct dsp_symbol_entry * null_algorithm; /* used by PCMreaderSCB's */
+	struct dsp_symbol_entry * s16_up;         /* used by SRCtaskSCB's */
+
+	/* proc fs */  
+	struct snd_card *snd_card;
+	struct snd_info_entry * proc_dsp_dir;
+	struct snd_info_entry * proc_sym_info_entry;
+	struct snd_info_entry * proc_modules_info_entry;
+	struct snd_info_entry * proc_parameter_dump_info_entry;
+	struct snd_info_entry * proc_sample_dump_info_entry;
+
+	/* SCB's descriptors */
+	int nscb;
+	int scb_highest_frag_index;
+	struct dsp_scb_descriptor scbs[DSP_MAX_SCB_DESC];
+	struct snd_info_entry * proc_scb_info_entry;
+	struct dsp_scb_descriptor * the_null_scb;
+
+	/* Task's descriptors */
+	int ntask;
+	struct dsp_task_descriptor tasks[DSP_MAX_TASK_DESC];
+	struct snd_info_entry * proc_task_info_entry;
+
+	/* SPDIF status */
+	int spdif_status_out;
+	int spdif_status_in;
+	u16 spdif_input_volume_right;
+	u16 spdif_input_volume_left;
+	/* spdif channel status,
+	   left right and user validity bits */
+	unsigned int spdif_csuv_default;
+	unsigned int spdif_csuv_stream;
+
+	/* SPDIF input sample rate converter */
+	struct dsp_scb_descriptor * spdif_in_src;
+	/* SPDIF input asynch. receiver */
+	struct dsp_scb_descriptor * asynch_rx_scb;
+
+	/* Capture record mixer SCB */
+	struct dsp_scb_descriptor * record_mixer_scb;
+    
+	/* CODEC input SCB */
+	struct dsp_scb_descriptor * codec_in_scb;
+
+	/* reference snooper */
+	struct dsp_scb_descriptor * ref_snoop_scb;
+
+	/* SPDIF output  PCM reference  */
+	struct dsp_scb_descriptor * spdif_pcm_input_scb;
+
+	/* asynch TX task */
+	struct dsp_scb_descriptor * asynch_tx_scb;
+
+	/* record sources */
+	struct dsp_scb_descriptor * pcm_input;
+	struct dsp_scb_descriptor * adc_input;
+
+	int spdif_in_sample_rate;
+};
+
+#endif /* __DSP_SPOS_H__ */
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/cs46xx_dsp_task_types.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/cs46xx_dsp_task_types.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/cs46xx_dsp_task_types.h	(revision 91)
@@ -0,0 +1,252 @@
+/*
+ *  The driver for the Cirrus Logic's Sound Fusion CS46XX based soundcards
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ *
+ * NOTE: comments are copy/paste from cwcemb80.lst 
+ * provided by Tom Woller at Cirrus (my only
+ * documentation about the SP OS running inside
+ * the DSP) 
+ */
+
+#ifndef __CS46XX_DSP_TASK_TYPES_H__
+#define __CS46XX_DSP_TASK_TYPES_H__
+
+#include "cs46xx_dsp_scb_types.h"
+
+/*********************************************************************************************
+Example hierarchy of stream control blocks in the SP
+
+hfgTree
+Ptr____Call (c)
+       \
+ -------+------         -------------      -------------      -------------      -----
+| SBlaster IF  |______\| Foreground  |___\| Middlegr'nd |___\| Background  |___\| Nul |
+|              |Goto  /| tree header |g  /| tree header |g  /| tree header |g  /| SCB |r
+ -------------- (g)     -------------      -------------      -------------      -----
+       |c                     |c                 |c                 |c
+       |                      |                  |                  |
+      \/                  -------------      -------------      -------------   
+                       | Foreground  |_\  | Middlegr'nd |_\  | Background  |_\
+                       |     tree    |g/  |    tree     |g/  |     tree    |g/
+                        -------------      -------------      -------------   
+                              |c                 |c                 |c
+                              |                  |                  | 
+                             \/                 \/                 \/ 
+
+*********************************************************************************************/
+
+#define		HFG_FIRST_EXECUTE_MODE			0x0001
+#define		HFG_FIRST_EXECUTE_MODE_BIT		0
+#define		HFG_CONTEXT_SWITCH_MODE			0x0002
+#define		HFG_CONTEXT_SWITCH_MODE_BIT		1
+
+#define MAX_FG_STACK_SIZE 	32			/* THESE NEED TO BE COMPUTED PROPERLY */
+#define MAX_MG_STACK_SIZE 	16
+#define MAX_BG_STACK_SIZE 	9
+#define MAX_HFG_STACK_SIZE	4
+
+#define SLEEP_ACTIVE_INCREMENT		0		/* Enable task tree thread to go to sleep
+											   This should only ever be used on the Background thread */
+#define STANDARD_ACTIVE_INCREMENT	1		/* Task tree thread normal operation */
+#define SUSPEND_ACTIVE_INCREMENT	2		/* Cause execution to suspend in the task tree thread
+                                               This should only ever be used on the Background thread */
+
+#define HOSTFLAGS_DISABLE_BG_SLEEP  0       /* Host-controlled flag that determines whether we go to sleep
+                                               at the end of BG */
+
+/* Minimal context save area for Hyper Forground */
+struct dsp_hf_save_area {
+	u32	r10_save;
+	u32	r54_save;
+	u32	r98_save;
+
+	___DSP_DUAL_16BIT_ALLOC(
+	    status_save,
+	    ind_save
+	)
+
+	___DSP_DUAL_16BIT_ALLOC(
+	    rci1_save,
+	    rci0_save
+	)
+
+	u32	r32_save;
+	u32	r76_save;
+	u32	rsd2_save;
+
+       	___DSP_DUAL_16BIT_ALLOC(
+	      rsi2_save,	  /* See TaskTreeParameterBlock for 
+				     remainder of registers  */
+	      rsa2Save
+	)
+	/* saved as part of HFG context  */
+};
+
+
+/* Task link data structure */
+struct dsp_tree_link {
+	___DSP_DUAL_16BIT_ALLOC(
+	/* Pointer to sibling task control block */
+	    next_scb,
+	/* Pointer to child task control block */
+	    sub_ptr
+	)
+  
+	___DSP_DUAL_16BIT_ALLOC(
+	/* Pointer to code entry point */
+	    entry_point, 
+	/* Pointer to local data */
+	    this_spb
+	)
+};
+
+
+struct dsp_task_tree_data {
+	___DSP_DUAL_16BIT_ALLOC(
+	/* Initial tock count; controls task tree execution rate */
+	    tock_count_limit,
+	/* Tock down counter */
+	    tock_count
+	)
+
+	/* Add to ActiveCount when TockCountLimit reached: 
+	   Subtract on task tree termination */
+	___DSP_DUAL_16BIT_ALLOC(
+	    active_tncrement,		
+	/* Number of pending activations for task tree */
+	    active_count
+	)
+
+        ___DSP_DUAL_16BIT_ALLOC(
+	/* BitNumber to enable modification of correct bit in ActiveTaskFlags */
+	    active_bit,	    
+	/* Pointer to OS location for indicating current activity on task level */
+	    active_task_flags_ptr
+	)
+
+	/* Data structure for controlling movement of memory blocks:- 
+	   currently unused */
+	___DSP_DUAL_16BIT_ALLOC(
+	    mem_upd_ptr,
+	/* Data structure for controlling synchronous link update */
+	    link_upd_ptr
+	)
+  
+	___DSP_DUAL_16BIT_ALLOC(
+	/* Save area for remainder of full context. */
+	    save_area,
+	/* Address of start of local stack for data storage */
+	    data_stack_base_ptr
+	)
+
+};
+
+
+struct dsp_interval_timer_data
+{
+	/* These data items have the same relative locations to those */
+	___DSP_DUAL_16BIT_ALLOC(
+	     interval_timer_period,
+	     itd_unused
+	)
+
+	/* used for this data in the SPOS control block for SPOS 1.0 */
+	___DSP_DUAL_16BIT_ALLOC(
+	     num_FG_ticks_this_interval,        
+	     num_intervals
+	)
+};
+
+
+/* This structure contains extra storage for the task tree
+   Currently, this additional data is related only to a full context save */
+struct dsp_task_tree_context_block {
+	/* Up to 10 values are saved onto the stack.  8 for the task tree, 1 for
+	   The access to the context switch (call or interrupt), and 1 spare that
+	   users should never use.  This last may be required by the system */
+	___DSP_DUAL_16BIT_ALLOC(
+	     stack1,
+	     stack0
+	)
+	___DSP_DUAL_16BIT_ALLOC(
+	     stack3,
+	     stack2
+	)
+	___DSP_DUAL_16BIT_ALLOC(
+	     stack5,
+	     stack4
+	)
+	___DSP_DUAL_16BIT_ALLOC(
+	     stack7,
+	     stack6
+	)
+	___DSP_DUAL_16BIT_ALLOC(
+	     stack9,
+	     stack8
+	)
+
+	u32	  saverfe;					
+
+	/* Value may be overwriten by stack save algorithm.
+	   Retain the size of the stack data saved here if used */
+	___DSP_DUAL_16BIT_ALLOC(
+             reserved1,	
+  	     stack_size
+	)
+	u32		saverba;	  /* (HFG) */
+	u32		saverdc;
+	u32		savers_config_23; /* (HFG) */
+	u32		savers_DMA23;	  /* (HFG) */
+	u32		saversa0;
+	u32		saversi0;
+	u32		saversa1;
+	u32		saversi1;
+	u32		saversa3;
+	u32		saversd0;
+	u32		saversd1;
+	u32		saversd3;
+	u32		savers_config01;
+	u32		savers_DMA01;
+	u32		saveacc0hl;
+	u32		saveacc1hl;
+	u32		saveacc0xacc1x;
+	u32		saveacc2hl;
+	u32		saveacc3hl;
+	u32		saveacc2xacc3x;
+	u32		saveaux0hl;
+	u32		saveaux1hl;
+	u32		saveaux0xaux1x;
+	u32		saveaux2hl;
+	u32		saveaux3hl;
+	u32		saveaux2xaux3x;
+	u32		savershouthl;
+	u32		savershoutxmacmode;
+};
+                
+
+struct dsp_task_tree_control_block {
+	struct dsp_hf_save_area			context;
+	struct dsp_tree_link			links;
+	struct dsp_task_tree_data		data;
+	struct dsp_task_tree_context_block	context_blk;
+	struct dsp_interval_timer_data		int_timer;
+};
+
+
+#endif /* __DSP_TASK_TYPES_H__ */
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/diff.cmd
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/diff.cmd	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/diff.cmd	(revision 91)
@@ -0,0 +1,1 @@
+diff.exe -Nuwr E:\DEV\USS\from_z\ua2\GPL\alsa-kernel\include\sound\%1 E:\DEV\alsa\alsa-kernel\include\%1 >%1.diff
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/driver.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/driver.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/driver.h	(revision 91)
@@ -0,0 +1,765 @@
+#ifndef __DRIVER_H
+#define __DRIVER_H
+
+/*
+ *  Main header file for the ALSA driver
+ *  Copyright (c) 1994-2000 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95)
+#define NEW_MACRO_VARARGS
+#endif
+
+#ifdef ALSA_BUILD
+#include "config.h"
+#endif
+
+#define SNDRV_CARDS		8	/* number of supported soundcards - don't change - minor numbers */
+
+#ifndef CONFIG_SND_MAJOR	/* standard configuration */
+#define CONFIG_SND_MAJOR	116
+#endif
+
+#ifndef CONFIG_SND_DEBUG
+#undef CONFIG_SND_DEBUG_MEMORY
+#endif
+
+#ifndef __iomem
+#define __iomem
+#endif
+
+#ifndef cond_resched
+#define cond_resched() \
+        do { \
+                if (1) { \
+                        set_current_state(TASK_RUNNING); \
+                        schedule(); \
+                } \
+        } while (0)
+#endif
+
+/*
+ *  ==========================================================================
+ */
+
+#ifdef ALSA_BUILD
+#undef MODULE
+#define MODULE
+#endif
+
+#include <linux/config.h>
+#include <linux/version.h>
+
+#define IRQ_NONE      (0)  /*void*/
+#define IRQ_HANDLED   (1)  /*void*/
+#define IRQ_RETVAL(x) ((x) != 0)  /*void*/
+typedef int irqreturn_t;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 2, 3)
+#error "This driver requires Linux 2.2.3 and higher."
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 2, 0)
+#define LINUX_2_2
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 3, 1)
+#define LINUX_2_3
+#endif
+#if defined(LINUX_2_3) && LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 0)
+#error "This code requires Linux 2.4.0-test1 and higher."
+#endif
+
+#ifdef ALSA_BUILD
+#if defined(CONFIG_MODVERSIONS) && !defined(__GENKSYMS__) && !defined(__DEPEND__)
+#define MODVERSIONS
+#include <linux/modversions.h>
+#include "sndversions.h"
+#endif
+#ifdef SNDRV_NO_MODVERS
+#undef MODVERSIONS
+#undef _set_ver
+#endif
+#endif /* ALSA_BUILD */
+
+#ifndef SNDRV_MAIN_OBJECT_FILE
+#define __NO_VERSION__
+#endif
+#include <linux/module.h>
+
+#include <linux/utsname.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/malloc.h>
+#include <linux/delay.h>
+
+#include <linux/ioport.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/dma.h>
+#include <asm/segment.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/string.h>
+
+#ifdef CONFIG_PCI
+#include <linux/pci.h>
+#endif
+#include <linux/interrupt.h>
+#include <linux/pagemap.h>
+#include <linux/fs.h>
+#include <linux/fcntl.h>
+#include <linux/vmalloc.h>
+#include <linux/proc_fs.h>
+#include <linux/poll.h>
+#include <linux/reboot.h>
+
+#ifdef LINUX_2_2
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 2, 18)
+#include <linux/init.h>
+#endif
+#include "compat_22.h"
+#endif
+#ifdef LINUX_2_3
+#include <linux/init.h>
+#include <linux/pm.h>
+#define PCI_GET_DRIVER_DATA(pci) pci->driver_data
+#define PCI_SET_DRIVER_DATA(pci, data) pci->driver_data = data
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 3)
+#define pci_set_dma_mask(pci, mask) pci->dma_mask = mask
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 7)
+#define PCI_NEW_SUSPEND
+#endif
+#ifndef virt_to_page
+#define virt_to_page(x) (&mem_map[MAP_NR(x)])
+#endif
+#define snd_request_region request_region
+#ifndef rwlock_init
+#define rwlock_init(x) do { *(x) = RW_LOCK_UNLOCKED; } while(0)
+#endif
+#define snd_kill_fasync(fp, sig, band) kill_fasync(fp, sig, band)
+#if defined(__i386__) || defined(__ppc__)
+/*
+ * Here a dirty hack for 2.4 kernels.. See kernel/memory.c.
+ */
+#define HACK_PCI_ALLOC_CONSISTENT
+void *snd_pci_hack_alloc_consistent(struct pci_dev *hwdev, size_t size,
+				    dma_addr_t *dma_handle);
+#undef pci_alloc_consistent
+#define pci_alloc_consistent snd_pci_hack_alloc_consistent
+#endif /* i386 or ppc */
+#endif
+
+#if defined(CONFIG_ISAPNP) || (defined(CONFIG_ISAPNP_MODULE) && defined(MODULE))
+#if (defined(CONFIG_ISAPNP_KERNEL) && defined(ALSA_BUILD)) || (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 3, 30) && !defined(ALSA_BUILD))
+#include <linux/isapnp.h>
+#define isapnp_dev pci_dev
+#define isapnp_card pci_bus
+#else
+#include "isapnp.h"
+#endif
+#undef __ISAPNP__
+#define __ISAPNP__
+#endif
+
+#ifndef PCI_D0
+#define PCI_D0     0
+#define PCI_D1     1
+#define PCI_D2     2
+#define PCI_D3hot  3
+#define PCI_D3cold 4
+#define pci_choose_state(pci,state)     ((state) ? PCI_D3hot : PCI_D0)
+#endif
+
+
+#include <sound/asound.h>
+#include <sound/asoundef.h>
+
+/* wrapper for getnstimeofday()
+ * it's needed for recent 2.6 kernels, too, due to lack of EXPORT_SYMBOL
+ */
+#define getnstimeofday(x) do { \
+	struct timeval __x; \
+	do_gettimeofday(&__x); \
+	(x)->tv_sec = __x.tv_sec;	\
+	(x)->tv_nsec = __x.tv_usec * 1000; \
+} while (0)
+
+struct work_struct {
+        unsigned long pending;
+        struct list_head entry;
+        void (*func)(void *);
+        void *data;
+        void *wq_data;
+        struct timer_list timer;
+};
+
+#define INIT_WORK(_work, _func, _data)                  \
+        do {                                            \
+                (_work)->func = _func;                  \
+                (_work)->data = _data;                  \
+                init_timer(&(_work)->timer);            \
+        } while (0)
+#define __WORK_INITIALIZER(n, f, d) {                   \
+        .func = (f),                                    \
+        .data = (d),                                    \
+        }
+#define DECLARE_WORK(n, f, d)                           \
+        struct work_struct n = __WORK_INITIALIZER(n, f, d)
+int snd_compat_schedule_work(struct work_struct *work);
+#define schedule_work(w) snd_compat_schedule_work(w)
+
+/* Name change */
+typedef struct timeval snd_timestamp_t;
+#ifndef TARGET_OS2
+typedef enum sndrv_card_type snd_card_type;
+#endif
+typedef union sndrv_pcm_sync_id snd_pcm_sync_id_t;
+#ifndef TARGET_OS2
+typedef enum sndrv_pcm_start snd_pcm_start_t;
+typedef enum sndrv_pcm_xrun snd_pcm_xrun_t;
+#endif
+#ifdef TARGET_OS2
+typedef struct snd_pcm_volume snd_pcm_volume_t;
+#else
+typedef enum sndrv_timer_global snd_timer_global_t;
+#endif
+
+#ifdef CONFIG_SND_DEBUG_MEMORY
+void *snd_wrapper_kmalloc(size_t, int);
+#undef kmalloc
+void snd_wrapper_kfree(const void *);
+#undef kfree
+void *snd_wrapper_vmalloc(size_t);
+#undef vmalloc
+void snd_wrapper_vfree(void *);
+#undef vfree
+#endif
+
+#ifdef TARGET_OS2
+#define snd_enter_user() 0
+#define snd_leave_user(a)
+#define dec_mod_count(a)	(*(unsigned long *)a)--
+
+#else
+static inline mm_segment_t snd_enter_user(void)
+{
+	mm_segment_t fs = get_fs();
+	set_fs(get_ds());
+	return fs;
+}
+static inline void snd_leave_user(mm_segment_t fs)
+{
+	set_fs(fs);
+}
+static inline void dec_mod_count(struct module *module)
+{
+	if (module)
+		__MOD_DEC_USE_COUNT(module);
+}
+#endif
+
+#if defined(__alpha__) && LINUX_VERSION_CODE < KERNEL_VERSION(2, 3, 14)
+#undef writeb
+#define writeb(v, a) do { __writeb((v),(a)); mb(); } while(0)
+#undef writew
+#define writew(v, a) do {__writew((v),(a)); mb(); } while(0)
+#undef writel
+#define writel(v, a) do {__writel((v),(a)); mb(); } while(0)
+#undef writeq
+#define writeq(v, a) do {__writeq((v),(a)); mb(); } while(0)
+#endif
+
+/* do we have virt_to_bus? */
+#if defined(CONFIG_SPARC64)
+#undef HAVE_VIRT_TO_BUS
+#else
+#define HAVE_VIRT_TO_BUS  1
+#endif
+
+/*
+ *  ==========================================================================
+ */
+
+/* device allocation stuff */
+
+#define SNDRV_DEV_TYPE_RANGE_SIZE		0x1000
+
+typedef enum {
+    SNDRV_DEV_TOPLEVEL =		(0*SNDRV_DEV_TYPE_RANGE_SIZE),
+    SNDRV_DEV_CONTROL,
+	SNDRV_DEV_LOWLEVEL_PRE,
+	SNDRV_DEV_LOWLEVEL_NORMAL =	(1*SNDRV_DEV_TYPE_RANGE_SIZE),
+	SNDRV_DEV_PCM,
+	SNDRV_DEV_RAWMIDI,
+	SNDRV_DEV_TIMER,
+	SNDRV_DEV_SEQUENCER,
+        SNDRV_DEV_HWDEP,
+        SNDRV_DEV_INFO,
+        SNDRV_DEV_BUS,
+        SNDRV_DEV_CODEC,
+	SNDRV_DEV_LOWLEVEL =		(2*SNDRV_DEV_TYPE_RANGE_SIZE)
+} snd_device_type_t;
+
+typedef enum {
+	SNDRV_DEV_BUILD,
+        SNDRV_DEV_REGISTERED,
+        SNDRV_DEV_DISCONNECTED
+} snd_device_state_t;
+
+typedef enum {
+	SNDRV_DEV_CMD_PRE = 0,
+	SNDRV_DEV_CMD_NORMAL = 1,
+	SNDRV_DEV_CMD_POST = 2
+} snd_device_cmd_t;
+
+
+struct snd_device;
+
+struct snd_device_ops {
+        int (*dev_free)(struct snd_device *dev);
+        int (*dev_register)(struct snd_device *dev);
+        int (*dev_disconnect)(struct snd_device *dev);
+        int (*dev_unregister)(struct snd_device *dev);
+};
+
+struct snd_device {
+	struct list_head list;		/* list of registered devices */
+	struct snd_card *card;		/* card which holds this device */
+	snd_device_state_t state;	/* state of the device */
+	snd_device_type_t type;		/* device type */
+	void *device_data;		/* device structure */
+	struct snd_device_ops *ops;		/* operations */
+};
+
+#define snd_device(n) list_entry(n, struct snd_device, list)
+
+/* various typedefs */
+
+typedef struct snd_info_entry snd_info_entry_t;
+#ifdef CONFIG_SND_OSSEMUL
+typedef struct _snd_oss_mixer snd_mixer_oss_t;
+#endif
+
+/* main structure for soundcard */
+
+/* monitor files for graceful shutdown (hotplug) */
+
+struct snd_monitor_file {
+        struct file *file;
+        struct snd_monitor_file *next;
+};
+
+struct snd_card {
+	int number;			/* number of soundcard (index to snd_cards) */
+
+	char id[16];			/* id string of this card */
+	char driver[16];		/* driver name */
+	char shortname[32];		/* short name of this soundcard */
+	char longname[80];		/* name of this soundcard */
+	char mixername[80];		/* mixer name */
+	char components[80];		/* card components delimited with space */
+
+	struct module *module;		/* top-level module */
+
+	void *private_data;		/* private data for soundcard */
+	void (*private_free) (struct snd_card *card); /* callback for freeing of private data */
+
+	struct list_head devices;	/* devices */
+
+        unsigned int last_numid;	/* last used numeric ID */
+        struct rw_semaphore controls_rwsem;     /* controls list lock */	rwlock_t control_rwlock;	/* control list lock */
+	rwlock_t ctl_files_rwlock;	/* ctl_files list lock */
+        int controls_count;		/* count of all controls */
+        int user_ctl_count;             /* count of all user controls */
+	struct list_head controls;	/* all controls for this card */
+	struct list_head ctl_files;	/* active control files */
+
+	snd_info_entry_t *proc_root;	/* root for soundcard specific files */
+	snd_info_entry_t *proc_id;	/* the card id */
+        struct proc_dir_entry *proc_root_link;	/* number link to real id */
+
+        struct snd_monitor_file *files; /* all files associated to this card */
+        struct snd_shutdown_f_ops *s_f_ops; /* file operations in the shutdown state */
+        spinlock_t files_lock;          /* lock the files for this card */
+        int shutdown;                   /* this card is going down */
+        wait_queue_head_t shutdown_sleep;
+        struct work_struct free_workq;	/* for free in workqueue */
+        struct device *dev;
+#ifdef CONFIG_PM
+        int (*pm_suspend)(struct snd_card *card, unsigned int state);
+        int (*pm_resume)(struct snd_card *card, unsigned int state);
+        struct pm_dev *pm_dev;          /* for ISA */
+        void *pm_private_data;
+	unsigned int power_state;	/* power state */
+	struct semaphore power_lock;	/* power lock */
+	wait_queue_head_t power_sleep;
+#endif
+
+#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
+	snd_mixer_oss_t *mixer_oss;
+	int mixer_oss_change_count;
+#endif
+};
+
+#ifdef CONFIG_PM
+static inline void snd_power_lock(struct snd_card *card)
+{
+	down(&card->power_lock);
+}
+
+static inline void snd_power_unlock(struct snd_card *card)
+{
+	up(&card->power_lock);
+}
+
+int snd_power_wait(struct snd_card *card, unsigned int power_state, struct file *file);
+
+static inline unsigned int snd_power_get_state(struct snd_card *card)
+{
+	return card->power_state;
+}
+
+static inline void snd_power_change_state(struct snd_card *card, unsigned int state)
+{
+	card->power_state = state;
+	wake_up(&card->power_sleep);
+}
+int snd_card_set_pm_callback(struct snd_card *card,
+                             int (*suspend)(struct snd_card *, unsigned int),
+                             int (*resume)(struct snd_card *, unsigned int),
+                             void *private_data);
+int snd_card_set_isa_pm_callback(struct snd_card *card,
+                                 int (*suspend)(struct snd_card *, unsigned int),
+                                 int (*resume)(struct snd_card *, unsigned int),
+                                 void *private_data);
+#ifndef SND_PCI_PM_CALLBACKS
+int snd_card_pci_suspend(struct pci_dev *dev, u32 state);
+int snd_card_pci_resume(struct pci_dev *dev);
+#define SND_PCI_PM_CALLBACKS \
+    snd_card_pci_suspend, \
+    snd_card_pci_resume
+#endif
+#else
+#define snd_power_lock(card) do { ; } while (0)
+#define snd_power_unlock(card) do { ; } while (0)
+#define snd_power_wait(card) do { ; } while (0)
+#define snd_power_get_state(card) SNDRV_CTL_POWER_D0
+#define snd_power_change_state(card, state) do { ; } while (0)
+#define snd_card_set_pm_callback(card,suspend,resume,data) -EINVAL
+#define snd_card_set_isa_pm_callback(card,suspend,resume,data) -EINVAL
+#define SND_PCI_PM_CALLBACKS
+#endif
+
+/* device.c */
+
+struct _snd_minor {
+	struct list_head list;		/* list of all minors per card */
+	int number;			/* minor number */
+	int device;			/* device number */
+	const char *comment;		/* for /proc/asound/devices */
+	snd_info_entry_t *dev;		/* for /proc/asound/dev */
+        struct file_operations *f_ops;	/* file operations */
+        char name[1];
+};
+
+/* sound.c */
+
+extern int snd_ecards_limit;
+extern int snd_device_mode;
+extern int snd_device_gid;
+extern int snd_device_uid;
+
+void snd_request_card(int card);
+
+int snd_register_device(int type, struct snd_card *card, int dev, snd_minor_t *reg, const char *name);
+int snd_unregister_device(int type, struct snd_card *card, int dev);
+
+#ifdef CONFIG_SND_OSSEMUL
+int snd_register_oss_device(int type, struct snd_card *card, int dev, snd_minor_t *reg, const char *name);
+int snd_unregister_oss_device(int type, struct snd_card *card, int dev);
+#endif
+
+int snd_minor_info_init(void);
+int snd_minor_info_done(void);
+
+/* sound_oss.c */
+
+#ifdef CONFIG_SND_OSSEMUL
+
+int snd_minor_info_oss_init(void);
+int snd_minor_info_oss_done(void);
+
+int snd_oss_init_module(void);
+void snd_oss_cleanup_module(void);
+
+#endif
+
+/* memory.c */
+
+#ifdef CONFIG_SND_DEBUG_MEMORY
+void snd_memory_init(void);
+void snd_memory_done(void);
+int snd_memory_info_init(void);
+int snd_memory_info_done(void);
+void *snd_hidden_kmalloc(size_t size, int flags);
+void *snd_hidden_kcalloc(size_t n, size_t size, int flags);
+void snd_hidden_kfree(const void *obj);
+void *snd_hidden_vmalloc(unsigned long size);
+void snd_hidden_vfree(void *obj);
+char *snd_hidden_kstrdup(const char *s, int flags);
+#define kmalloc(size, flags) snd_hidden_kmalloc(size, flags)
+#define kcalloc(n, size, flags) snd_hidden_kcalloc(n, size, flags)
+#define kfree(obj) snd_hidden_kfree(obj)
+#define vmalloc(size) snd_hidden_vmalloc(size)
+#define vfree(obj) snd_hidden_vfree(obj)
+#define kstrdup(s, flags)  snd_hidden_kstrdup(s, flags)
+#endif
+
+char *snd_hidden_kstrdup(const char *s, int flags);
+#define kstrdup(s, flags)  snd_hidden_kstrdup(s, flags)
+
+char *snd_kmalloc_strdup(const char *string, int flags);
+int copy_to_user_fromio(void __user *dst, const volatile void __iomem *src, size_t count);
+int copy_from_user_toio(volatile void __iomem *dst, const void __user *src, size_t count);
+
+void *kzalloc(size_t size, unsigned int flags);
+
+/* init.c */
+
+extern int snd_cards_count;
+extern struct snd_card *snd_cards[SNDRV_CARDS];
+extern rwlock_t snd_card_rwlock;
+#ifdef CONFIG_SND_OSSEMUL
+extern int (*snd_mixer_oss_notify_callback)(struct snd_card *card, int free_flag);
+#endif
+
+struct snd_card *snd_card_new(int idx, const char *id,
+			 struct module *module, int extra_size);
+int snd_card_free(struct snd_card *card);
+int snd_card_register(struct snd_card *card);
+int snd_card_info_init(void);
+int snd_card_info_done(void);
+int snd_component_add(struct snd_card *card, const char *component);
+int snd_card_file_add(struct snd_card *card, struct file *file);
+int snd_card_file_remove(struct snd_card *card, struct file *file);
+
+/* device.c */
+
+int snd_device_new(struct snd_card *card, snd_device_type_t type,
+		   void *device_data, struct snd_device_ops *ops);
+int snd_device_register(struct snd_card *card, void *device_data);
+int snd_device_register_all(struct snd_card *card);
+int snd_device_free(struct snd_card *card, void *device_data);
+int snd_device_free_all(struct snd_card *card, snd_device_cmd_t cmd);
+
+/* isadma.c */
+
+#define DMA_MODE_NO_ENABLE	0x0100
+
+void snd_dma_program(unsigned long dma, const void *buf, unsigned int size, unsigned short mode);
+void snd_dma_disable(unsigned long dma);
+unsigned int snd_dma_residue(unsigned long dma);
+
+/* misc.c */
+
+int snd_task_name(struct task_struct *task, char *name, size_t size);
+
+/* --- */
+
+#ifdef NEW_MACRO_VARARGS
+
+/*
+ *  VARARGS section
+ */
+
+#define snd_printk(...) do {\
+	printk("ALSA %s:%d: ", __FILE__, __LINE__); \
+ 	printk(__VA_ARGS__); \
+} while (0)
+
+#ifdef CONFIG_SND_DEBUG
+
+#define snd_printd(...) snd_printk(__VA_ARGS__)
+#define snd_assert(expr, ...) do {\
+	if (!(expr)) {\
+		snd_printk("BUG? (%s) (called from %p)\n", __STRING(expr), __builtin_return_address(0));\
+		__VA_ARGS__;\
+	}\
+} while (0)
+#define snd_runtime_check(expr, ...) do {\
+	if (!(expr)) {\
+		snd_printk("ERROR (%s) (called from %p)\n", __STRING(expr), __builtin_return_address(0));\
+		__VA_ARGS__;\
+	}\
+} while (0)
+
+#else /* !CONFIG_SND_DEBUG */
+
+#define snd_printd(...)	/* nothing */
+#define snd_assert(expr, ...)	/* nothing */
+#define snd_runtime_check(expr, ...) do { if (!(expr)) {__VA_ARGS__;} } while (0)
+
+#endif /* CONFIG_SND_DEBUG */
+
+#ifdef CONFIG_SND_DEBUG_DETECT
+#define snd_printdd(...) snd_printk(__VA_ARGS__)
+#else
+#define snd_printdd(...) /* nothing */
+#endif
+
+#else /* !NEW_MACRO_VARARGS */
+
+/*
+ *  Old args section...
+ */
+
+#ifdef TARGET_OS2
+
+#define snd_printk printk
+
+#ifdef CONFIG_SND_DEBUG
+
+#define snd_printd snd_printk
+#define snd_assert(expr, retval) \
+	if (!(expr)) {\
+		snd_printk("BUG? (%s)\n", __STRING(expr));\
+		##retval;\
+	}
+
+#define snd_runtime_check snd_assert
+#define snd_runtime_check_continue snd_assert_continue
+
+
+#else /* !CONFIG_SND_DEBUG */
+
+#define snd_printd  1 ? (void)0 : (void)((int (*)(char *, ...)) NULL)
+#define snd_assert(expr, retval) \
+	if (!(expr)) {\
+		##retval;\
+	}
+#define snd_runtime_check snd_assert
+
+#endif /* CONFIG_SND_DEBUG */
+
+#ifdef CONFIG_SND_DEBUG_DETECT
+#define snd_printdd snd_printk
+#else
+#define snd_printdd 1 ? (void)0 : (void)((int (*)(char *, ...)) NULL)
+#endif
+
+#else
+#define snd_printk(args...) do {\
+	printk("ALSA %s:%d: ", __FILE__, __LINE__); \
+ 	printk(##args); \
+} while (0)
+
+#ifdef CONFIG_SND_DEBUG
+
+#define snd_printd(args...) snd_printk(##args)
+#define snd_assert(expr, args...) do {\
+	if (!(expr)) {\
+		snd_printk("BUG? (%s)\n", __STRING(expr));\
+		##args;\
+	}\
+} while (0)
+#define snd_runtime_check(expr, args...) do {\
+	if (!(expr)) {\
+		snd_printk("ERROR (%s)\n", __STRING(expr));\
+		##args;\
+	}\
+} while (0)
+
+#else /* !CONFIG_SND_DEBUG */
+
+#define snd_printd(args...) /* nothing */
+#define snd_assert(expr, args...)	/* nothing */
+#define snd_runtime_check(expr, args...) do { if (!(expr)) {##args;} } while (0)
+
+#endif /* CONFIG_SND_DEBUG */
+
+#ifdef CONFIG_SND_DEBUG_DETECT
+#define snd_printdd(args...) snd_printk(##args)
+#else
+#define snd_printdd(args...) /* nothing */
+#endif
+#endif
+
+#endif /* NEW_MACRO_VARARGS */
+
+#ifdef DEBUG
+#define snd_BUG() _asm int 3;
+#else
+#define snd_BUG()
+#endif
+
+static inline void snd_timestamp_now(struct timespec *tstamp, int timespec)
+{
+        struct timeval val;
+        /* FIXME: use a linear time source */
+        do_gettimeofday(&val);
+        tstamp->tv_sec = val.tv_sec;
+        tstamp->tv_nsec = val.tv_usec;
+        if (timespec)
+                tstamp->tv_nsec *= 1000L;
+}
+
+#define snd_timestamp_zero(tstamp) do { (tstamp)->tv_sec = 0; (tstamp)->tv_usec = 0; } while (0)
+#define snd_timestamp_null(tstamp) ((tstamp)->tv_sec == 0 && (tstamp)->tv_usec ==0)
+
+#define SNDRV_OSS_VERSION         ((3<<16)|(8<<8)|(1<<4)|(0))	/* 3.8.1a */
+
+/* kcalloc */
+#ifndef CONFIG_HAVE_KCALLOC
+#ifndef CONFIG_SND_DEBUG_MEMORY
+void *snd_compat_kcalloc(size_t n, size_t size, int gfp_flags);
+#define kcalloc(n,s,f) snd_compat_kcalloc(n,s,f)
+#endif
+#endif
+
+#define __builtin_expect(x, expected_value) (x)
+#define likely(x)       __builtin_expect((x),1)
+#define unlikely(x)     __builtin_expect((x),0)
+
+typedef u32 /*__bitwise*/ pm_message_t;
+
+#ifndef __devexit_p
+#define __devexit_p(x) x
+#endif
+
+#define printk_ratelimit()      1
+
+typedef unsigned gfp_t;
+
+#ifndef cpu_relax
+#define cpu_relax()
+#endif
+
+static inline int snd_pci_enable_msi(struct pci_dev *dev) { return -1; }
+#undef pci_enable_msi
+#define pci_enable_msi(dev) snd_pci_enable_msi(dev)
+#undef pci_disable_msi
+#define pci_disable_msi(dev)
+
+#undef pci_intx
+#define pci_intx(pci,x)
+
+
+#endif				/* __DRIVER_H */
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/emu10k1.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/emu10k1.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/emu10k1.h	(revision 91)
@@ -0,0 +1,1931 @@
+#ifndef __SOUND_EMU10K1_H
+#define __SOUND_EMU10K1_H
+
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>,
+ *		     Creative Labs, Inc.
+ *  Definitions for EMU10K1 (SB Live!) chips
+ *
+ *
+ *   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
+ *
+ */
+
+#ifdef __KERNEL__
+
+#include <sound/pcm.h>
+#include <sound/rawmidi.h>
+#include <sound/hwdep.h>
+#include <sound/ac97_codec.h>
+#include <sound/util_mem.h>
+#include <sound/pcm-indirect.h>
+#include <sound/timer.h>
+#include <linux/interrupt.h>
+#include <asm/io.h>
+
+/* ------------------- DEFINES -------------------- */
+
+#define EMUPAGESIZE     4096
+#define MAXREQVOICES    8
+#define MAXPAGES        8192
+#define RESERVED        0
+#define NUM_MIDI        16
+#define NUM_G           64              /* use all channels */
+#define NUM_FXSENDS     4
+#define NUM_EFX_PLAYBACK    16
+
+/* FIXME? - according to the OSS driver the EMU10K1 needs a 29 bit DMA mask */
+#define EMU10K1_DMA_MASK	0x7fffffffUL	/* 31bit */
+#define AUDIGY_DMA_MASK		0x7fffffffUL	/* 31bit FIXME - 32 should work? */
+						/* See ALSA bug #1276 - rlrevell */
+
+#define TMEMSIZE        256*1024
+#define TMEMSIZEREG     4
+
+#define IP_TO_CP(ip) ((ip == 0) ? 0 : (((0x00001000uL | (ip & 0x00000FFFL)) << (((ip >> 12) & 0x000FL) + 4)) & 0xFFFF0000uL))
+
+// Audigy specify registers are prefixed with 'A_'
+
+/************************************************************************************************/
+/* PCI function 0 registers, address = <val> + PCIBASE0						*/
+/************************************************************************************************/
+
+#define PTR			0x00		/* Indexed register set pointer register	*/
+						/* NOTE: The CHANNELNUM and ADDRESS words can	*/
+						/* be modified independently of each other.	*/
+#define PTR_CHANNELNUM_MASK	0x0000003f	/* For each per-channel register, indicates the	*/
+						/* channel number of the register to be		*/
+						/* accessed.  For non per-channel registers the	*/
+						/* value should be set to zero.			*/
+#define PTR_ADDRESS_MASK	0x07ff0000	/* Register index				*/
+#define A_PTR_ADDRESS_MASK	0x0fff0000
+
+#define DATA			0x04		/* Indexed register set data register		*/
+
+#define IPR			0x08		/* Global interrupt pending register		*/
+						/* Clear pending interrupts by writing a 1 to	*/
+						/* the relevant bits and zero to the other bits	*/
+#define IPR_P16V		0x80000000	/* Bit set when the CA0151 P16V chip wishes
+						   to interrupt */
+#define IPR_GPIOMSG		0x20000000	/* GPIO message interrupt (RE'd, still not sure 
+						   which INTE bits enable it)			*/
+
+/* The next two interrupts are for the midi port on the Audigy Drive (A_MPU1)			*/
+#define IPR_A_MIDITRANSBUFEMPTY2 0x10000000	/* MIDI UART transmit buffer empty		*/
+#define IPR_A_MIDIRECVBUFEMPTY2	0x08000000	/* MIDI UART receive buffer empty		*/
+
+#define IPR_SPDIFBUFFULL	0x04000000	/* SPDIF capture related, 10k2 only? (RE)	*/
+#define IPR_SPDIFBUFHALFFULL	0x02000000	/* SPDIF capture related? (RE)			*/
+
+#define IPR_SAMPLERATETRACKER	0x01000000	/* Sample rate tracker lock status change	*/
+#define IPR_FXDSP		0x00800000	/* Enable FX DSP interrupts			*/
+#define IPR_FORCEINT		0x00400000	/* Force Sound Blaster interrupt		*/
+#define IPR_PCIERROR		0x00200000	/* PCI bus error				*/
+#define IPR_VOLINCR		0x00100000	/* Volume increment button pressed		*/
+#define IPR_VOLDECR		0x00080000	/* Volume decrement button pressed		*/
+#define IPR_MUTE		0x00040000	/* Mute button pressed				*/
+#define IPR_MICBUFFULL		0x00020000	/* Microphone buffer full			*/
+#define IPR_MICBUFHALFFULL	0x00010000	/* Microphone buffer half full			*/
+#define IPR_ADCBUFFULL		0x00008000	/* ADC buffer full				*/
+#define IPR_ADCBUFHALFFULL	0x00004000	/* ADC buffer half full				*/
+#define IPR_EFXBUFFULL		0x00002000	/* Effects buffer full				*/
+#define IPR_EFXBUFHALFFULL	0x00001000	/* Effects buffer half full			*/
+#define IPR_GPSPDIFSTATUSCHANGE	0x00000800	/* GPSPDIF channel status change		*/
+#define IPR_CDROMSTATUSCHANGE	0x00000400	/* CD-ROM channel status change			*/
+#define IPR_INTERVALTIMER	0x00000200	/* Interval timer terminal count		*/
+#define IPR_MIDITRANSBUFEMPTY	0x00000100	/* MIDI UART transmit buffer empty		*/
+#define IPR_MIDIRECVBUFEMPTY	0x00000080	/* MIDI UART receive buffer empty		*/
+#define IPR_CHANNELLOOP		0x00000040	/* Channel (half) loop interrupt(s) pending	*/
+#define IPR_CHANNELNUMBERMASK	0x0000003f	/* When IPR_CHANNELLOOP is set, indicates the	*/
+						/* highest set channel in CLIPL, CLIPH, HLIPL,  */
+						/* or HLIPH.  When IP is written with CL set,	*/
+						/* the bit in H/CLIPL or H/CLIPH corresponding	*/
+						/* to the CIN value written will be cleared.	*/
+
+#define INTE			0x0c		/* Interrupt enable register			*/
+#define INTE_VIRTUALSB_MASK	0xc0000000	/* Virtual Soundblaster I/O port capture	*/
+#define INTE_VIRTUALSB_220	0x00000000	/* Capture at I/O base address 0x220-0x22f	*/
+#define INTE_VIRTUALSB_240	0x40000000	/* Capture at I/O base address 0x240		*/
+#define INTE_VIRTUALSB_260	0x80000000	/* Capture at I/O base address 0x260		*/
+#define INTE_VIRTUALSB_280	0xc0000000	/* Capture at I/O base address 0x280		*/
+#define INTE_VIRTUALMPU_MASK	0x30000000	/* Virtual MPU I/O port capture			*/
+#define INTE_VIRTUALMPU_300	0x00000000	/* Capture at I/O base address 0x300-0x301	*/
+#define INTE_VIRTUALMPU_310	0x10000000	/* Capture at I/O base address 0x310		*/
+#define INTE_VIRTUALMPU_320	0x20000000	/* Capture at I/O base address 0x320		*/
+#define INTE_VIRTUALMPU_330	0x30000000	/* Capture at I/O base address 0x330		*/
+#define INTE_MASTERDMAENABLE	0x08000000	/* Master DMA emulation at 0x000-0x00f		*/
+#define INTE_SLAVEDMAENABLE	0x04000000	/* Slave DMA emulation at 0x0c0-0x0df		*/
+#define INTE_MASTERPICENABLE	0x02000000	/* Master PIC emulation at 0x020-0x021		*/
+#define INTE_SLAVEPICENABLE	0x01000000	/* Slave PIC emulation at 0x0a0-0x0a1		*/
+#define INTE_VSBENABLE		0x00800000	/* Enable virtual Soundblaster			*/
+#define INTE_ADLIBENABLE	0x00400000	/* Enable AdLib emulation at 0x388-0x38b	*/
+#define INTE_MPUENABLE		0x00200000	/* Enable virtual MPU				*/
+#define INTE_FORCEINT		0x00100000	/* Continuously assert INTAN			*/
+
+#define INTE_MRHANDENABLE	0x00080000	/* Enable the "Mr. Hand" logic			*/
+						/* NOTE: There is no reason to use this under	*/
+						/* Linux, and it will cause odd hardware 	*/
+						/* behavior and possibly random segfaults and	*/
+						/* lockups if enabled.				*/
+
+/* The next two interrupts are for the midi port on the Audigy Drive (A_MPU1)			*/
+#define INTE_A_MIDITXENABLE2	0x00020000	/* Enable MIDI transmit-buffer-empty interrupts	*/
+#define INTE_A_MIDIRXENABLE2	0x00010000	/* Enable MIDI receive-buffer-empty interrupts	*/
+
+
+#define INTE_SAMPLERATETRACKER	0x00002000	/* Enable sample rate tracker interrupts	*/
+						/* NOTE: This bit must always be enabled       	*/
+#define INTE_FXDSPENABLE	0x00001000	/* Enable FX DSP interrupts			*/
+#define INTE_PCIERRORENABLE	0x00000800	/* Enable PCI bus error interrupts		*/
+#define INTE_VOLINCRENABLE	0x00000400	/* Enable volume increment button interrupts	*/
+#define INTE_VOLDECRENABLE	0x00000200	/* Enable volume decrement button interrupts	*/
+#define INTE_MUTEENABLE		0x00000100	/* Enable mute button interrupts		*/
+#define INTE_MICBUFENABLE	0x00000080	/* Enable microphone buffer interrupts		*/
+#define INTE_ADCBUFENABLE	0x00000040	/* Enable ADC buffer interrupts			*/
+#define INTE_EFXBUFENABLE	0x00000020	/* Enable Effects buffer interrupts		*/
+#define INTE_GPSPDIFENABLE	0x00000010	/* Enable GPSPDIF status interrupts		*/
+#define INTE_CDSPDIFENABLE	0x00000008	/* Enable CDSPDIF status interrupts		*/
+#define INTE_INTERVALTIMERENB	0x00000004	/* Enable interval timer interrupts		*/
+#define INTE_MIDITXENABLE	0x00000002	/* Enable MIDI transmit-buffer-empty interrupts	*/
+#define INTE_MIDIRXENABLE	0x00000001	/* Enable MIDI receive-buffer-empty interrupts	*/
+
+#define WC			0x10		/* Wall Clock register				*/
+#define WC_SAMPLECOUNTER_MASK	0x03FFFFC0	/* Sample periods elapsed since reset		*/
+#define WC_SAMPLECOUNTER	0x14060010
+#define WC_CURRENTCHANNEL	0x0000003F	/* Channel [0..63] currently being serviced	*/
+						/* NOTE: Each channel takes 1/64th of a sample	*/
+						/* period to be serviced.			*/
+
+#define HCFG			0x14		/* Hardware config register			*/
+						/* NOTE: There is no reason to use the legacy	*/
+						/* SoundBlaster emulation stuff described below	*/
+						/* under Linux, and all kinds of weird hardware	*/
+						/* behavior can result if you try.  Don't.	*/
+#define HCFG_LEGACYFUNC_MASK	0xe0000000	/* Legacy function number 			*/
+#define HCFG_LEGACYFUNC_MPU	0x00000000	/* Legacy MPU	 				*/
+#define HCFG_LEGACYFUNC_SB	0x40000000	/* Legacy SB					*/
+#define HCFG_LEGACYFUNC_AD	0x60000000	/* Legacy AD					*/
+#define HCFG_LEGACYFUNC_MPIC	0x80000000	/* Legacy MPIC					*/
+#define HCFG_LEGACYFUNC_MDMA	0xa0000000	/* Legacy MDMA					*/
+#define HCFG_LEGACYFUNC_SPCI	0xc0000000	/* Legacy SPCI					*/
+#define HCFG_LEGACYFUNC_SDMA	0xe0000000	/* Legacy SDMA					*/
+#define HCFG_IOCAPTUREADDR	0x1f000000	/* The 4 LSBs of the captured I/O address.	*/
+#define HCFG_LEGACYWRITE	0x00800000	/* 1 = write, 0 = read 				*/
+#define HCFG_LEGACYWORD		0x00400000	/* 1 = word, 0 = byte 				*/
+#define HCFG_LEGACYINT		0x00200000	/* 1 = legacy event captured. Write 1 to clear.	*/
+						/* NOTE: The rest of the bits in this register	*/
+						/* _are_ relevant under Linux.			*/
+#define HCFG_PUSH_BUTTON_ENABLE 0x00100000	/* Enables Volume Inc/Dec and Mute functions    */
+#define HCFG_BAUD_RATE		0x00080000	/* 0 = 48kHz, 1 = 44.1kHz			*/
+#define HCFG_EXPANDED_MEM	0x00040000	/* 1 = any 16M of 4G addr, 0 = 32M of 2G addr	*/
+#define HCFG_CODECFORMAT_MASK	0x00030000	/* CODEC format					*/
+
+/* Specific to Alice2, CA0102 */
+#define HCFG_CODECFORMAT_AC97_1	0x00000000	/* AC97 CODEC format -- Ver 1.03		*/
+#define HCFG_CODECFORMAT_AC97_2	0x00010000	/* AC97 CODEC format -- Ver 2.1			*/
+#define HCFG_AUTOMUTE_ASYNC	0x00008000	/* When set, the async sample rate convertors	*/
+						/* will automatically mute their output when	*/
+						/* they are not rate-locked to the external	*/
+						/* async audio source  				*/
+#define HCFG_AUTOMUTE_SPDIF	0x00004000	/* When set, the async sample rate convertors	*/
+						/* will automatically mute their output when	*/
+						/* the SPDIF V-bit indicates invalid audio	*/
+#define HCFG_EMU32_SLAVE	0x00002000	/* 0 = Master, 1 = Slave. Slave for EMU1010	*/
+#define HCFG_SLOW_RAMP		0x00001000	/* Increases Send Smoothing time constant	*/
+/* 0x00000800 not used on Alice2 */
+#define HCFG_PHASE_TRACK_MASK	0x00000700	/* When set, forces corresponding input to	*/
+						/* phase track the previous input.		*/
+						/* I2S0 can phase track the last S/PDIF input	*/
+#define HCFG_I2S_ASRC_ENABLE	0x00000070	/* When set, enables asynchronous sample rate   */
+						/* conversion for the corresponding		*/
+ 						/* I2S format input				*/
+/* Rest of HCFG 0x0000000f same as below. LOCKSOUNDCACHE etc.  */
+
+
+
+/* Older chips */
+#define HCFG_CODECFORMAT_AC97	0x00000000	/* AC97 CODEC format -- Primary Output		*/
+#define HCFG_CODECFORMAT_I2S	0x00010000	/* I2S CODEC format -- Secondary (Rear) Output	*/
+#define HCFG_GPINPUT0		0x00004000	/* External pin112				*/
+#define HCFG_GPINPUT1		0x00002000	/* External pin110				*/
+#define HCFG_GPOUTPUT_MASK	0x00001c00	/* External pins which may be controlled	*/
+#define HCFG_GPOUT0		0x00001000	/* External pin? (spdif enable on 5.1)		*/
+#define HCFG_GPOUT1		0x00000800	/* External pin? (IR)				*/
+#define HCFG_GPOUT2		0x00000400	/* External pin? (IR)				*/
+#define HCFG_JOYENABLE      	0x00000200	/* Internal joystick enable    			*/
+#define HCFG_PHASETRACKENABLE	0x00000100	/* Phase tracking enable			*/
+						/* 1 = Force all 3 async digital inputs to use	*/
+						/* the same async sample rate tracker (ZVIDEO)	*/
+#define HCFG_AC3ENABLE_MASK	0x000000e0	/* AC3 async input control - Not implemented	*/
+#define HCFG_AC3ENABLE_ZVIDEO	0x00000080	/* Channels 0 and 1 replace ZVIDEO		*/
+#define HCFG_AC3ENABLE_CDSPDIF	0x00000040	/* Channels 0 and 1 replace CDSPDIF		*/
+#define HCFG_AC3ENABLE_GPSPDIF  0x00000020      /* Channels 0 and 1 replace GPSPDIF             */
+#define HCFG_AUTOMUTE		0x00000010	/* When set, the async sample rate convertors	*/
+						/* will automatically mute their output when	*/
+						/* they are not rate-locked to the external	*/
+						/* async audio source  				*/
+#define HCFG_LOCKSOUNDCACHE	0x00000008	/* 1 = Cancel bustmaster accesses to soundcache */
+						/* NOTE: This should generally never be used.  	*/
+#define HCFG_LOCKTANKCACHE_MASK	0x00000004	/* 1 = Cancel bustmaster accesses to tankcache	*/
+						/* NOTE: This should generally never be used.  	*/
+#define HCFG_LOCKTANKCACHE	0x01020014
+#define HCFG_MUTEBUTTONENABLE	0x00000002	/* 1 = Master mute button sets AUDIOENABLE = 0.	*/
+						/* NOTE: This is a 'cheap' way to implement a	*/
+						/* master mute function on the mute button, and	*/
+						/* in general should not be used unless a more	*/
+						/* sophisticated master mute function has not	*/
+						/* been written.       				*/
+#define HCFG_AUDIOENABLE	0x00000001	/* 0 = CODECs transmit zero-valued samples	*/
+						/* Should be set to 1 when the EMU10K1 is	*/
+						/* completely initialized.			*/
+
+//For Audigy, MPU port move to 0x70-0x74 ptr register
+
+#define MUDATA			0x18		/* MPU401 data register (8 bits)       		*/
+
+#define MUCMD			0x19		/* MPU401 command register (8 bits)    		*/
+#define MUCMD_RESET		0xff		/* RESET command				*/
+#define MUCMD_ENTERUARTMODE	0x3f		/* Enter_UART_mode command			*/
+						/* NOTE: All other commands are ignored		*/
+
+#define MUSTAT			MUCMD		/* MPU401 status register (8 bits)     		*/
+#define MUSTAT_IRDYN		0x80		/* 0 = MIDI data or command ACK			*/
+#define MUSTAT_ORDYN		0x40		/* 0 = MUDATA can accept a command or data	*/
+
+#define A_IOCFG			0x18		/* GPIO on Audigy card (16bits)			*/
+#define A_GPINPUT_MASK		0xff00
+#define A_GPOUTPUT_MASK		0x00ff
+
+// Audigy output/GPIO stuff taken from the kX drivers
+#define A_IOCFG_GPOUT0		0x0044		/* analog/digital				*/
+#define A_IOCFG_DISABLE_ANALOG	0x0040		/* = 'enable' for Audigy2 (chiprev=4)		*/
+#define A_IOCFG_ENABLE_DIGITAL	0x0004
+#define A_IOCFG_ENABLE_DIGITAL_AUDIGY4	0x0080
+#define A_IOCFG_UNKNOWN_20      0x0020
+#define A_IOCFG_DISABLE_AC97_FRONT      0x0080  /* turn off ac97 front -> front (10k2.1)	*/
+#define A_IOCFG_GPOUT1		0x0002		/* IR? drive's internal bypass (?)		*/
+#define A_IOCFG_GPOUT2		0x0001		/* IR */
+#define A_IOCFG_MULTIPURPOSE_JACK	0x2000  /* center+lfe+rear_center (a2/a2ex)		*/
+                                                /* + digital for generic 10k2			*/
+#define A_IOCFG_DIGITAL_JACK    0x1000          /* digital for a2 platinum			*/
+#define A_IOCFG_FRONT_JACK      0x4000
+#define A_IOCFG_REAR_JACK       0x8000
+#define A_IOCFG_PHONES_JACK     0x0100          /* LiveDrive					*/
+
+/* outputs:
+ *	for audigy2 platinum:	0xa00
+ *	for a2 platinum ex:	0x1c00
+ *	for a1 platinum:	0x0
+ */
+
+#define TIMER			0x1a		/* Timer terminal count register		*/
+						/* NOTE: After the rate is changed, a maximum	*/
+						/* of 1024 sample periods should be allowed	*/
+						/* before the new rate is guaranteed accurate.	*/
+#define TIMER_RATE_MASK		0x000003ff	/* Timer interrupt rate in sample periods	*/
+						/* 0 == 1024 periods, [1..4] are not useful	*/
+#define TIMER_RATE		0x0a00001a
+
+#define AC97DATA		0x1c		/* AC97 register set data register (16 bit)	*/
+
+#define AC97ADDRESS		0x1e		/* AC97 register set address register (8 bit)	*/
+#define AC97ADDRESS_READY	0x80		/* Read-only bit, reflects CODEC READY signal	*/
+#define AC97ADDRESS_ADDRESS	0x7f		/* Address of indexed AC97 register		*/
+
+/* Available on the Audigy 2 and Audigy 4 only. This is the P16V chip. */
+#define PTR2			0x20		/* Indexed register set pointer register	*/
+#define DATA2			0x24		/* Indexed register set data register		*/
+#define IPR2			0x28		/* P16V interrupt pending register		*/
+#define IPR2_PLAYBACK_CH_0_LOOP      0x00001000 /* Playback Channel 0 loop                               */
+#define IPR2_PLAYBACK_CH_0_HALF_LOOP 0x00000100 /* Playback Channel 0 half loop                          */
+#define IPR2_CAPTURE_CH_0_LOOP       0x00100000 /* Capture Channel 0 loop                               */
+#define IPR2_CAPTURE_CH_0_HALF_LOOP  0x00010000 /* Capture Channel 0 half loop                          */
+						/* 0x00000100 Playback. Only in once per period.
+						 * 0x00110000 Capture. Int on half buffer.
+						 */
+#define INTE2			0x2c		/* P16V Interrupt enable register. 	*/
+#define INTE2_PLAYBACK_CH_0_LOOP      0x00001000 /* Playback Channel 0 loop                               */
+#define INTE2_PLAYBACK_CH_0_HALF_LOOP 0x00000100 /* Playback Channel 0 half loop                          */
+#define INTE2_PLAYBACK_CH_1_LOOP      0x00002000 /* Playback Channel 1 loop                               */
+#define INTE2_PLAYBACK_CH_1_HALF_LOOP 0x00000200 /* Playback Channel 1 half loop                          */
+#define INTE2_PLAYBACK_CH_2_LOOP      0x00004000 /* Playback Channel 2 loop                               */
+#define INTE2_PLAYBACK_CH_2_HALF_LOOP 0x00000400 /* Playback Channel 2 half loop                          */
+#define INTE2_PLAYBACK_CH_3_LOOP      0x00008000 /* Playback Channel 3 loop                               */
+#define INTE2_PLAYBACK_CH_3_HALF_LOOP 0x00000800 /* Playback Channel 3 half loop                          */
+#define INTE2_CAPTURE_CH_0_LOOP       0x00100000 /* Capture Channel 0 loop                               */
+#define INTE2_CAPTURE_CH_0_HALF_LOOP  0x00010000 /* Caputre Channel 0 half loop                          */
+#define HCFG2			0x34		/* Defaults: 0, win2000 sets it to 00004201 */
+						/* 0x00000000 2-channel output. */
+						/* 0x00000200 8-channel output. */
+						/* 0x00000004 pauses stream/irq fail. */
+						/* Rest of bits no nothing to sound output */
+						/* bit 0: Enable P16V audio.
+						 * bit 1: Lock P16V record memory cache.
+						 * bit 2: Lock P16V playback memory cache.
+						 * bit 3: Dummy record insert zero samples.
+						 * bit 8: Record 8-channel in phase.
+						 * bit 9: Playback 8-channel in phase.
+						 * bit 11-12: Playback mixer attenuation: 0=0dB, 1=-6dB, 2=-12dB, 3=Mute.
+						 * bit 13: Playback mixer enable.
+						 * bit 14: Route SRC48 mixer output to fx engine.
+						 * bit 15: Enable IEEE 1394 chip.
+						 */
+#define IPR3			0x38		/* Cdif interrupt pending register		*/
+#define INTE3			0x3c		/* Cdif interrupt enable register. 	*/
+/************************************************************************************************/
+/* PCI function 1 registers, address = <val> + PCIBASE1						*/
+/************************************************************************************************/
+
+#define JOYSTICK1		0x00		/* Analog joystick port register		*/
+#define JOYSTICK2		0x01		/* Analog joystick port register		*/
+#define JOYSTICK3		0x02		/* Analog joystick port register		*/
+#define JOYSTICK4		0x03		/* Analog joystick port register		*/
+#define JOYSTICK5		0x04		/* Analog joystick port register		*/
+#define JOYSTICK6		0x05		/* Analog joystick port register		*/
+#define JOYSTICK7		0x06		/* Analog joystick port register		*/
+#define JOYSTICK8		0x07		/* Analog joystick port register		*/
+
+/* When writing, any write causes JOYSTICK_COMPARATOR output enable to be pulsed on write.	*/
+/* When reading, use these bitfields: */
+#define JOYSTICK_BUTTONS	0x0f		/* Joystick button data				*/
+#define JOYSTICK_COMPARATOR	0xf0		/* Joystick comparator data			*/
+
+
+/********************************************************************************************************/
+/* Emu10k1 pointer-offset register set, accessed through the PTR and DATA registers			*/
+/********************************************************************************************************/
+
+#define CPF			0x00		/* Current pitch and fraction register			*/
+#define CPF_CURRENTPITCH_MASK	0xffff0000	/* Current pitch (linear, 0x4000 == unity pitch shift) 	*/
+#define CPF_CURRENTPITCH	0x10100000
+#define CPF_STEREO_MASK		0x00008000	/* 1 = Even channel interleave, odd channel locked	*/
+#define CPF_STOP_MASK		0x00004000	/* 1 = Current pitch forced to 0			*/
+#define CPF_FRACADDRESS_MASK	0x00003fff	/* Linear fractional address of the current channel	*/
+
+#define PTRX			0x01		/* Pitch target and send A/B amounts register		*/
+#define PTRX_PITCHTARGET_MASK	0xffff0000	/* Pitch target of specified channel			*/
+#define PTRX_PITCHTARGET	0x10100001
+#define PTRX_FXSENDAMOUNT_A_MASK 0x0000ff00	/* Linear level of channel output sent to FX send bus A	*/
+#define PTRX_FXSENDAMOUNT_A	0x08080001
+#define PTRX_FXSENDAMOUNT_B_MASK 0x000000ff	/* Linear level of channel output sent to FX send bus B	*/
+#define PTRX_FXSENDAMOUNT_B	0x08000001
+
+#define CVCF			0x02		/* Current volume and filter cutoff register		*/
+#define CVCF_CURRENTVOL_MASK	0xffff0000	/* Current linear volume of specified channel		*/
+#define CVCF_CURRENTVOL		0x10100002
+#define CVCF_CURRENTFILTER_MASK	0x0000ffff	/* Current filter cutoff frequency of specified channel	*/
+#define CVCF_CURRENTFILTER	0x10000002
+
+#define VTFT			0x03		/* Volume target and filter cutoff target register	*/
+#define VTFT_VOLUMETARGET_MASK	0xffff0000	/* Volume target of specified channel			*/
+#define VTFT_VOLUMETARGET	0x10100003
+#define VTFT_FILTERTARGET_MASK	0x0000ffff	/* Filter cutoff target of specified channel		*/
+#define VTFT_FILTERTARGET	0x10000003
+
+#define Z1			0x05		/* Filter delay memory 1 register			*/
+
+#define Z2			0x04		/* Filter delay memory 2 register			*/
+
+#define PSST			0x06		/* Send C amount and loop start address register	*/
+#define PSST_FXSENDAMOUNT_C_MASK 0xff000000	/* Linear level of channel output sent to FX send bus C	*/
+
+#define PSST_FXSENDAMOUNT_C	0x08180006
+
+#define PSST_LOOPSTARTADDR_MASK	0x00ffffff	/* Loop start address of the specified channel		*/
+#define PSST_LOOPSTARTADDR	0x18000006
+
+#define DSL			0x07		/* Send D amount and loop start address register	*/
+#define DSL_FXSENDAMOUNT_D_MASK	0xff000000	/* Linear level of channel output sent to FX send bus D	*/
+
+#define DSL_FXSENDAMOUNT_D	0x08180007
+
+#define DSL_LOOPENDADDR_MASK	0x00ffffff	/* Loop end address of the specified channel		*/
+#define DSL_LOOPENDADDR		0x18000007
+
+#define CCCA			0x08		/* Filter Q, interp. ROM, byte size, cur. addr register */
+#define CCCA_RESONANCE		0xf0000000	/* Lowpass filter resonance (Q) height			*/
+#define CCCA_INTERPROMMASK	0x0e000000	/* Selects passband of interpolation ROM		*/
+						/* 1 == full band, 7 == lowpass				*/
+						/* ROM 0 is used when pitch shifting downward or less	*/
+						/* then 3 semitones upward.  Increasingly higher ROM	*/
+						/* numbers are used, typically in steps of 3 semitones,	*/
+						/* as upward pitch shifting is performed.		*/
+#define CCCA_INTERPROM_0	0x00000000	/* Select interpolation ROM 0				*/
+#define CCCA_INTERPROM_1	0x02000000	/* Select interpolation ROM 1				*/
+#define CCCA_INTERPROM_2	0x04000000	/* Select interpolation ROM 2				*/
+#define CCCA_INTERPROM_3	0x06000000	/* Select interpolation ROM 3				*/
+#define CCCA_INTERPROM_4	0x08000000	/* Select interpolation ROM 4				*/
+#define CCCA_INTERPROM_5	0x0a000000	/* Select interpolation ROM 5				*/
+#define CCCA_INTERPROM_6	0x0c000000	/* Select interpolation ROM 6				*/
+#define CCCA_INTERPROM_7	0x0e000000	/* Select interpolation ROM 7				*/
+#define CCCA_8BITSELECT		0x01000000	/* 1 = Sound memory for this channel uses 8-bit samples	*/
+#define CCCA_CURRADDR_MASK	0x00ffffff	/* Current address of the selected channel		*/
+#define CCCA_CURRADDR		0x18000008
+
+#define CCR			0x09		/* Cache control register				*/
+#define CCR_CACHEINVALIDSIZE	0x07190009
+#define CCR_CACHEINVALIDSIZE_MASK	0xfe000000	/* Number of invalid samples cache for this channel    	*/
+#define CCR_CACHELOOPFLAG	0x01000000	/* 1 = Cache has a loop service pending			*/
+#define CCR_INTERLEAVEDSAMPLES	0x00800000	/* 1 = A cache service will fetch interleaved samples	*/
+#define CCR_WORDSIZEDSAMPLES	0x00400000	/* 1 = A cache service will fetch word sized samples	*/
+#define CCR_READADDRESS		0x06100009
+#define CCR_READADDRESS_MASK	0x003f0000	/* Location of cache just beyond current cache service	*/
+#define CCR_LOOPINVALSIZE	0x0000fe00	/* Number of invalid samples in cache prior to loop	*/
+						/* NOTE: This is valid only if CACHELOOPFLAG is set	*/
+#define CCR_LOOPFLAG		0x00000100	/* Set for a single sample period when a loop occurs	*/
+#define CCR_CACHELOOPADDRHI	0x000000ff	/* DSL_LOOPSTARTADDR's hi byte if CACHELOOPFLAG is set	*/
+
+#define CLP			0x0a		/* Cache loop register (valid if CCR_CACHELOOPFLAG = 1) */
+						/* NOTE: This register is normally not used		*/
+#define CLP_CACHELOOPADDR	0x0000ffff	/* Cache loop address (DSL_LOOPSTARTADDR [0..15])	*/
+
+#define FXRT			0x0b		/* Effects send routing register			*/
+						/* NOTE: It is illegal to assign the same routing to	*/
+						/* two effects sends.					*/
+#define FXRT_CHANNELA		0x000f0000	/* Effects send bus number for channel's effects send A	*/
+#define FXRT_CHANNELB		0x00f00000	/* Effects send bus number for channel's effects send B	*/
+#define FXRT_CHANNELC		0x0f000000	/* Effects send bus number for channel's effects send C	*/
+#define FXRT_CHANNELD		0xf0000000	/* Effects send bus number for channel's effects send D	*/
+
+#define MAPA			0x0c		/* Cache map A						*/
+
+#define MAPB			0x0d		/* Cache map B						*/
+
+#define MAP_PTE_MASK		0xffffe000	/* The 19 MSBs of the PTE indexed by the PTI		*/
+#define MAP_PTI_MASK		0x00001fff	/* The 13 bit index to one of the 8192 PTE dwords      	*/
+
+#define ENVVOL			0x10		/* Volume envelope register				*/
+#define ENVVOL_MASK		0x0000ffff	/* Current value of volume envelope state variable	*/  
+						/* 0x8000-n == 666*n usec delay	       			*/
+
+#define ATKHLDV 		0x11		/* Volume envelope hold and attack register		*/
+#define ATKHLDV_PHASE0		0x00008000	/* 0 = Begin attack phase				*/
+#define ATKHLDV_HOLDTIME_MASK	0x00007f00	/* Envelope hold time (127-n == n*88.2msec)		*/
+#define ATKHLDV_ATTACKTIME_MASK	0x0000007f	/* Envelope attack time, log encoded			*/
+						/* 0 = infinite, 1 = 10.9msec, ... 0x7f = 5.5msec	*/
+
+#define DCYSUSV 		0x12		/* Volume envelope sustain and decay register		*/
+#define DCYSUSV_PHASE1_MASK	0x00008000	/* 0 = Begin attack phase, 1 = begin release phase	*/
+#define DCYSUSV_SUSTAINLEVEL_MASK 0x00007f00	/* 127 = full, 0 = off, 0.75dB increments		*/
+#define DCYSUSV_CHANNELENABLE_MASK 0x00000080	/* 1 = Inhibit envelope engine from writing values in	*/
+						/* this channel and from writing to pitch, filter and	*/
+						/* volume targets.					*/
+#define DCYSUSV_DECAYTIME_MASK	0x0000007f	/* Volume envelope decay time, log encoded     		*/
+						/* 0 = 43.7msec, 1 = 21.8msec, 0x7f = 22msec		*/
+
+#define LFOVAL1 		0x13		/* Modulation LFO value					*/
+#define LFOVAL_MASK		0x0000ffff	/* Current value of modulation LFO state variable	*/
+						/* 0x8000-n == 666*n usec delay				*/
+
+#define ENVVAL			0x14		/* Modulation envelope register				*/
+#define ENVVAL_MASK		0x0000ffff	/* Current value of modulation envelope state variable 	*/
+						/* 0x8000-n == 666*n usec delay				*/
+
+#define ATKHLDM			0x15		/* Modulation envelope hold and attack register		*/
+#define ATKHLDM_PHASE0		0x00008000	/* 0 = Begin attack phase				*/
+#define ATKHLDM_HOLDTIME	0x00007f00	/* Envelope hold time (127-n == n*42msec)		*/
+#define ATKHLDM_ATTACKTIME	0x0000007f	/* Envelope attack time, log encoded			*/
+						/* 0 = infinite, 1 = 11msec, ... 0x7f = 5.5msec		*/
+
+#define DCYSUSM			0x16		/* Modulation envelope decay and sustain register	*/
+#define DCYSUSM_PHASE1_MASK	0x00008000	/* 0 = Begin attack phase, 1 = begin release phase	*/
+#define DCYSUSM_SUSTAINLEVEL_MASK 0x00007f00	/* 127 = full, 0 = off, 0.75dB increments		*/
+#define DCYSUSM_DECAYTIME_MASK	0x0000007f	/* Envelope decay time, log encoded			*/
+						/* 0 = 43.7msec, 1 = 21.8msec, 0x7f = 22msec		*/
+
+#define LFOVAL2 		0x17		/* Vibrato LFO register					*/
+#define LFOVAL2_MASK		0x0000ffff	/* Current value of vibrato LFO state variable 		*/
+						/* 0x8000-n == 666*n usec delay				*/
+
+#define IP			0x18		/* Initial pitch register				*/
+#define IP_MASK			0x0000ffff	/* Exponential initial pitch shift			*/
+						/* 4 bits of octave, 12 bits of fractional octave	*/
+#define IP_UNITY		0x0000e000	/* Unity pitch shift					*/
+
+#define IFATN			0x19		/* Initial filter cutoff and attenuation register	*/
+#define IFATN_FILTERCUTOFF_MASK	0x0000ff00	/* Initial filter cutoff frequency in exponential units	*/
+						/* 6 most significant bits are semitones		*/
+						/* 2 least significant bits are fractions		*/
+#define IFATN_FILTERCUTOFF	0x08080019
+#define IFATN_ATTENUATION_MASK	0x000000ff	/* Initial attenuation in 0.375dB steps			*/
+#define IFATN_ATTENUATION	0x08000019
+
+
+#define PEFE			0x1a		/* Pitch envelope and filter envelope amount register	*/
+#define PEFE_PITCHAMOUNT_MASK	0x0000ff00	/* Pitch envlope amount					*/
+						/* Signed 2's complement, +/- one octave peak extremes	*/
+#define PEFE_PITCHAMOUNT	0x0808001a
+#define PEFE_FILTERAMOUNT_MASK	0x000000ff	/* Filter envlope amount				*/
+						/* Signed 2's complement, +/- six octaves peak extremes */
+#define PEFE_FILTERAMOUNT	0x0800001a
+#define FMMOD			0x1b		/* Vibrato/filter modulation from LFO register		*/
+#define FMMOD_MODVIBRATO	0x0000ff00	/* Vibrato LFO modulation depth				*/
+						/* Signed 2's complement, +/- one octave extremes	*/
+#define FMMOD_MOFILTER		0x000000ff	/* Filter LFO modulation depth				*/
+						/* Signed 2's complement, +/- three octave extremes	*/
+
+
+#define TREMFRQ 		0x1c		/* Tremolo amount and modulation LFO frequency register	*/
+#define TREMFRQ_DEPTH		0x0000ff00	/* Tremolo depth					*/
+						/* Signed 2's complement, with +/- 12dB extremes	*/
+
+#define TREMFRQ_FREQUENCY	0x000000ff	/* Tremolo LFO frequency				*/
+						/* ??Hz steps, maximum of ?? Hz.			*/
+#define FM2FRQ2 		0x1d		/* Vibrato amount and vibrato LFO frequency register	*/
+#define FM2FRQ2_DEPTH		0x0000ff00	/* Vibrato LFO vibrato depth				*/
+						/* Signed 2's complement, +/- one octave extremes	*/
+#define FM2FRQ2_FREQUENCY	0x000000ff	/* Vibrato LFO frequency				*/
+						/* 0.039Hz steps, maximum of 9.85 Hz.			*/
+
+#define TEMPENV 		0x1e		/* Tempory envelope register				*/
+#define TEMPENV_MASK		0x0000ffff	/* 16-bit value						*/
+						/* NOTE: All channels contain internal variables; do	*/
+						/* not write to these locations.			*/
+
+/* 1f something */
+
+#define CD0			0x20		/* Cache data 0 register				*/
+#define CD1			0x21		/* Cache data 1 register				*/
+#define CD2			0x22		/* Cache data 2 register				*/
+#define CD3			0x23		/* Cache data 3 register				*/
+#define CD4			0x24		/* Cache data 4 register				*/
+#define CD5			0x25		/* Cache data 5 register				*/
+#define CD6			0x26		/* Cache data 6 register				*/
+#define CD7			0x27		/* Cache data 7 register				*/
+#define CD8			0x28		/* Cache data 8 register				*/
+#define CD9			0x29		/* Cache data 9 register				*/
+#define CDA			0x2a		/* Cache data A register				*/
+#define CDB			0x2b		/* Cache data B register				*/
+#define CDC			0x2c		/* Cache data C register				*/
+#define CDD			0x2d		/* Cache data D register				*/
+#define CDE			0x2e		/* Cache data E register				*/
+#define CDF			0x2f		/* Cache data F register				*/
+
+/* 0x30-3f seem to be the same as 0x20-2f */
+
+#define PTB			0x40		/* Page table base register				*/
+#define PTB_MASK		0xfffff000	/* Physical address of the page table in host memory	*/
+
+#define TCB			0x41		/* Tank cache base register    				*/
+#define TCB_MASK		0xfffff000	/* Physical address of the bottom of host based TRAM	*/
+
+#define ADCCR			0x42		/* ADC sample rate/stereo control register		*/
+#define ADCCR_RCHANENABLE	0x00000010	/* Enables right channel for writing to the host       	*/
+#define ADCCR_LCHANENABLE	0x00000008	/* Enables left channel for writing to the host		*/
+						/* NOTE: To guarantee phase coherency, both channels	*/
+						/* must be disabled prior to enabling both channels.	*/
+#define A_ADCCR_RCHANENABLE	0x00000020
+#define A_ADCCR_LCHANENABLE	0x00000010
+
+#define A_ADCCR_SAMPLERATE_MASK 0x0000000F      /* Audigy sample rate convertor output rate		*/
+#define ADCCR_SAMPLERATE_MASK	0x00000007	/* Sample rate convertor output rate			*/
+#define ADCCR_SAMPLERATE_48	0x00000000	/* 48kHz sample rate					*/
+#define ADCCR_SAMPLERATE_44	0x00000001	/* 44.1kHz sample rate					*/
+#define ADCCR_SAMPLERATE_32	0x00000002	/* 32kHz sample rate					*/
+#define ADCCR_SAMPLERATE_24	0x00000003	/* 24kHz sample rate					*/
+#define ADCCR_SAMPLERATE_22	0x00000004	/* 22.05kHz sample rate					*/
+#define ADCCR_SAMPLERATE_16	0x00000005	/* 16kHz sample rate					*/
+#define ADCCR_SAMPLERATE_11	0x00000006	/* 11.025kHz sample rate				*/
+#define ADCCR_SAMPLERATE_8	0x00000007	/* 8kHz sample rate					*/
+#define A_ADCCR_SAMPLERATE_12	0x00000006	/* 12kHz sample rate					*/
+#define A_ADCCR_SAMPLERATE_11	0x00000007	/* 11.025kHz sample rate				*/
+#define A_ADCCR_SAMPLERATE_8	0x00000008	/* 8kHz sample rate					*/
+
+#define FXWC			0x43		/* FX output write channels register			*/
+						/* When set, each bit enables the writing of the	*/
+						/* corresponding FX output channel (internal registers  */
+						/* 0x20-0x3f) to host memory.  This mode of recording   */
+						/* is 16bit, 48KHz only. All 32 channels can be enabled */
+						/* simultaneously.					*/
+
+#define FXWC_DEFAULTROUTE_C     (1<<0)		/* left emu out? */
+#define FXWC_DEFAULTROUTE_B     (1<<1)		/* right emu out? */
+#define FXWC_DEFAULTROUTE_A     (1<<12)
+#define FXWC_DEFAULTROUTE_D     (1<<13)
+#define FXWC_ADCLEFT            (1<<18)
+#define FXWC_CDROMSPDIFLEFT     (1<<18)
+#define FXWC_ADCRIGHT           (1<<19)
+#define FXWC_CDROMSPDIFRIGHT    (1<<19)
+#define FXWC_MIC                (1<<20)
+#define FXWC_ZOOMLEFT           (1<<20)
+#define FXWC_ZOOMRIGHT          (1<<21)
+#define FXWC_SPDIFLEFT          (1<<22)		/* 0x00400000 */
+#define FXWC_SPDIFRIGHT         (1<<23)		/* 0x00800000 */
+
+#define TCBS			0x44		/* Tank cache buffer size register			*/
+#define TCBS_MASK		0x00000007	/* Tank cache buffer size field				*/
+#define TCBS_BUFFSIZE_16K	0x00000000
+#define TCBS_BUFFSIZE_32K	0x00000001
+#define TCBS_BUFFSIZE_64K	0x00000002
+#define TCBS_BUFFSIZE_128K	0x00000003
+#define TCBS_BUFFSIZE_256K	0x00000004
+#define TCBS_BUFFSIZE_512K	0x00000005
+#define TCBS_BUFFSIZE_1024K	0x00000006
+#define TCBS_BUFFSIZE_2048K	0x00000007
+
+#define MICBA			0x45		/* AC97 microphone buffer address register		*/
+#define MICBA_MASK		0xfffff000	/* 20 bit base address					*/
+
+#define ADCBA			0x46		/* ADC buffer address register				*/
+#define ADCBA_MASK		0xfffff000	/* 20 bit base address					*/
+
+#define FXBA			0x47		/* FX Buffer Address */
+#define FXBA_MASK		0xfffff000	/* 20 bit base address					*/
+
+/* 0x48 something - word access, defaults to 3f */
+
+#define MICBS			0x49		/* Microphone buffer size register			*/
+
+#define ADCBS			0x4a		/* ADC buffer size register				*/
+
+#define FXBS			0x4b		/* FX buffer size register				*/
+
+/* register: 0x4c..4f: ffff-ffff current amounts, per-channel */
+
+/* The following mask values define the size of the ADC, MIX and FX buffers in bytes */
+#define ADCBS_BUFSIZE_NONE	0x00000000
+#define ADCBS_BUFSIZE_384	0x00000001
+#define ADCBS_BUFSIZE_448	0x00000002
+#define ADCBS_BUFSIZE_512	0x00000003
+#define ADCBS_BUFSIZE_640	0x00000004
+#define ADCBS_BUFSIZE_768	0x00000005
+#define ADCBS_BUFSIZE_896	0x00000006
+#define ADCBS_BUFSIZE_1024	0x00000007
+#define ADCBS_BUFSIZE_1280	0x00000008
+#define ADCBS_BUFSIZE_1536	0x00000009
+#define ADCBS_BUFSIZE_1792	0x0000000a
+#define ADCBS_BUFSIZE_2048	0x0000000b
+#define ADCBS_BUFSIZE_2560	0x0000000c
+#define ADCBS_BUFSIZE_3072	0x0000000d
+#define ADCBS_BUFSIZE_3584	0x0000000e
+#define ADCBS_BUFSIZE_4096	0x0000000f
+#define ADCBS_BUFSIZE_5120	0x00000010
+#define ADCBS_BUFSIZE_6144	0x00000011
+#define ADCBS_BUFSIZE_7168	0x00000012
+#define ADCBS_BUFSIZE_8192	0x00000013
+#define ADCBS_BUFSIZE_10240	0x00000014
+#define ADCBS_BUFSIZE_12288	0x00000015
+#define ADCBS_BUFSIZE_14366	0x00000016
+#define ADCBS_BUFSIZE_16384	0x00000017
+#define ADCBS_BUFSIZE_20480	0x00000018
+#define ADCBS_BUFSIZE_24576	0x00000019
+#define ADCBS_BUFSIZE_28672	0x0000001a
+#define ADCBS_BUFSIZE_32768	0x0000001b
+#define ADCBS_BUFSIZE_40960	0x0000001c
+#define ADCBS_BUFSIZE_49152	0x0000001d
+#define ADCBS_BUFSIZE_57344	0x0000001e
+#define ADCBS_BUFSIZE_65536	0x0000001f
+
+
+#define CDCS			0x50		/* CD-ROM digital channel status register	*/
+
+#define GPSCS			0x51		/* General Purpose SPDIF channel status register*/
+
+#define DBG			0x52		/* DO NOT PROGRAM THIS REGISTER!!! MAY DESTROY CHIP */
+
+#define REG53			0x53		/* DO NOT PROGRAM THIS REGISTER!!! MAY DESTROY CHIP */
+
+#define A_DBG			 0x53
+#define A_DBG_SINGLE_STEP	 0x00020000	/* Set to zero to start dsp */
+#define A_DBG_ZC		 0x40000000	/* zero tram counter */
+#define A_DBG_STEP_ADDR		 0x000003ff
+#define A_DBG_SATURATION_OCCURED 0x20000000
+#define A_DBG_SATURATION_ADDR	 0x0ffc0000
+
+// NOTE: 0x54,55,56: 64-bit
+#define SPCS0			0x54		/* SPDIF output Channel Status 0 register	*/
+
+#define SPCS1			0x55		/* SPDIF output Channel Status 1 register	*/
+
+#define SPCS2			0x56		/* SPDIF output Channel Status 2 register	*/
+
+#define SPCS_CLKACCYMASK	0x30000000	/* Clock accuracy				*/
+#define SPCS_CLKACCY_1000PPM	0x00000000	/* 1000 parts per million			*/
+#define SPCS_CLKACCY_50PPM	0x10000000	/* 50 parts per million				*/
+#define SPCS_CLKACCY_VARIABLE	0x20000000	/* Variable accuracy				*/
+#define SPCS_SAMPLERATEMASK	0x0f000000	/* Sample rate					*/
+#define SPCS_SAMPLERATE_44	0x00000000	/* 44.1kHz sample rate				*/
+#define SPCS_SAMPLERATE_48	0x02000000	/* 48kHz sample rate				*/
+#define SPCS_SAMPLERATE_32	0x03000000	/* 32kHz sample rate				*/
+#define SPCS_CHANNELNUMMASK	0x00f00000	/* Channel number				*/
+#define SPCS_CHANNELNUM_UNSPEC	0x00000000	/* Unspecified channel number			*/
+#define SPCS_CHANNELNUM_LEFT	0x00100000	/* Left channel					*/
+#define SPCS_CHANNELNUM_RIGHT	0x00200000	/* Right channel				*/
+#define SPCS_SOURCENUMMASK	0x000f0000	/* Source number				*/
+#define SPCS_SOURCENUM_UNSPEC	0x00000000	/* Unspecified source number			*/
+#define SPCS_GENERATIONSTATUS	0x00008000	/* Originality flag (see IEC-958 spec)		*/
+#define SPCS_CATEGORYCODEMASK	0x00007f00	/* Category code (see IEC-958 spec)		*/
+#define SPCS_MODEMASK		0x000000c0	/* Mode (see IEC-958 spec)			*/
+#define SPCS_EMPHASISMASK	0x00000038	/* Emphasis					*/
+#define SPCS_EMPHASIS_NONE	0x00000000	/* No emphasis					*/
+#define SPCS_EMPHASIS_50_15	0x00000008	/* 50/15 usec 2 channel				*/
+#define SPCS_COPYRIGHT		0x00000004	/* Copyright asserted flag -- do not modify	*/
+#define SPCS_NOTAUDIODATA	0x00000002	/* 0 = Digital audio, 1 = not audio		*/
+#define SPCS_PROFESSIONAL	0x00000001	/* 0 = Consumer (IEC-958), 1 = pro (AES3-1992)	*/
+
+/* The 32-bit CLIx and SOLx registers all have one bit per channel control/status      		*/
+#define CLIEL			0x58		/* Channel loop interrupt enable low register	*/
+
+#define CLIEH			0x59		/* Channel loop interrupt enable high register	*/
+
+#define CLIPL			0x5a		/* Channel loop interrupt pending low register	*/
+
+#define CLIPH			0x5b		/* Channel loop interrupt pending high register	*/
+
+#define SOLEL			0x5c		/* Stop on loop enable low register		*/
+
+#define SOLEH			0x5d		/* Stop on loop enable high register		*/
+
+#define SPBYPASS		0x5e		/* SPDIF BYPASS mode register			*/
+#define SPBYPASS_SPDIF0_MASK	0x00000003	/* SPDIF 0 bypass mode				*/
+#define SPBYPASS_SPDIF1_MASK	0x0000000c	/* SPDIF 1 bypass mode				*/
+/* bypass mode: 0 - DSP; 1 - SPDIF A, 2 - SPDIF B, 3 - SPDIF C					*/
+#define SPBYPASS_FORMAT		0x00000f00      /* If 1, SPDIF XX uses 24 bit, if 0 - 20 bit	*/
+
+#define AC97SLOT		0x5f            /* additional AC97 slots enable bits		*/
+#define AC97SLOT_REAR_RIGHT	0x01		/* Rear left */
+#define AC97SLOT_REAR_LEFT	0x02		/* Rear right */
+#define AC97SLOT_CNTR		0x10            /* Center enable */
+#define AC97SLOT_LFE		0x20            /* LFE enable */
+
+// NOTE: 0x60,61,62: 64-bit
+#define CDSRCS			0x60		/* CD-ROM Sample Rate Converter status register	*/
+
+#define GPSRCS			0x61		/* General Purpose SPDIF sample rate cvt status */
+
+#define ZVSRCS			0x62		/* ZVideo sample rate converter status		*/
+						/* NOTE: This one has no SPDIFLOCKED field	*/
+						/* Assumes sample lock				*/
+
+/* These three bitfields apply to CDSRCS, GPSRCS, and (except as noted) ZVSRCS.			*/
+#define SRCS_SPDIFVALID		0x04000000	/* SPDIF stream valid				*/
+#define SRCS_SPDIFLOCKED	0x02000000	/* SPDIF stream locked				*/
+#define SRCS_RATELOCKED		0x01000000	/* Sample rate locked				*/
+#define SRCS_ESTSAMPLERATE	0x0007ffff	/* Do not modify this field.			*/
+
+/* Note that these values can vary +/- by a small amount                                        */
+#define SRCS_SPDIFRATE_44	0x0003acd9
+#define SRCS_SPDIFRATE_48	0x00040000
+#define SRCS_SPDIFRATE_96	0x00080000
+
+#define MICIDX                  0x63            /* Microphone recording buffer index register   */
+#define MICIDX_MASK             0x0000ffff      /* 16-bit value                                 */
+#define MICIDX_IDX		0x10000063
+
+#define ADCIDX			0x64		/* ADC recording buffer index register		*/
+#define ADCIDX_MASK		0x0000ffff	/* 16 bit index field				*/
+#define ADCIDX_IDX		0x10000064
+
+#define A_ADCIDX		0x63
+#define A_ADCIDX_IDX		0x10000063
+
+#define A_MICIDX		0x64
+#define A_MICIDX_IDX		0x10000064
+
+#define FXIDX			0x65		/* FX recording buffer index register		*/
+#define FXIDX_MASK		0x0000ffff	/* 16-bit value					*/
+#define FXIDX_IDX		0x10000065
+
+/* The 32-bit HLIx and HLIPx registers all have one bit per channel control/status      		*/
+#define HLIEL			0x66		/* Channel half loop interrupt enable low register	*/
+
+#define HLIEH			0x67		/* Channel half loop interrupt enable high register	*/
+
+#define HLIPL			0x68		/* Channel half loop interrupt pending low register	*/
+
+#define HLIPH			0x69		/* Channel half loop interrupt pending high register	*/
+
+// 0x6a,6b,6c used for some recording
+// 0x6d unused
+// 0x6e,6f - tanktable base / offset
+
+/* This is the MPU port on the card (via the game port)						*/
+#define A_MUDATA1		0x70
+#define A_MUCMD1		0x71
+#define A_MUSTAT1		A_MUCMD1
+
+/* This is the MPU port on the Audigy Drive 							*/
+#define A_MUDATA2		0x72
+#define A_MUCMD2		0x73
+#define A_MUSTAT2		A_MUCMD2	
+
+/* The next two are the Audigy equivalent of FXWC						*/
+/* the Audigy can record any output (16bit, 48kHz, up to 64 channel simultaneously) 		*/
+/* Each bit selects a channel for recording */
+#define A_FXWC1			0x74            /* Selects 0x7f-0x60 for FX recording           */
+#define A_FXWC2			0x75		/* Selects 0x9f-0x80 for FX recording           */
+
+#define A_SPDIF_SAMPLERATE	0x76		/* Set the sample rate of SPDIF output		*/
+#define A_SAMPLE_RATE		0x76		/* Various sample rate settings. */
+#define A_SAMPLE_RATE_NOT_USED  0x0ffc111e	/* Bits that are not used and cannot be set. 	*/
+#define A_SAMPLE_RATE_UNKNOWN	0xf0030001	/* Bits that can be set, but have unknown use. 	*/
+#define A_SPDIF_RATE_MASK	0x000000e0	/* Any other values for rates, just use 48000	*/
+#define A_SPDIF_48000		0x00000000
+#define A_SPDIF_192000		0x00000020
+#define A_SPDIF_96000		0x00000040
+#define A_SPDIF_44100		0x00000080
+
+#define A_I2S_CAPTURE_RATE_MASK	0x00000e00	/* This sets the capture PCM rate, but it is    */
+#define A_I2S_CAPTURE_48000	0x00000000	/* unclear if this sets the ADC rate as well.	*/
+#define A_I2S_CAPTURE_192000	0x00000200
+#define A_I2S_CAPTURE_96000	0x00000400
+#define A_I2S_CAPTURE_44100	0x00000800
+
+#define A_PCM_RATE_MASK		0x0000e000	/* This sets the playback PCM rate on the P16V	*/
+#define A_PCM_48000		0x00000000
+#define A_PCM_192000		0x00002000
+#define A_PCM_96000		0x00004000
+#define A_PCM_44100		0x00008000
+
+/* 0x77,0x78,0x79 "something i2s-related" - default to 0x01080000 on my audigy 2 ZS --rlrevell	*/
+/* 0x7a, 0x7b - lookup tables */
+
+#define A_FXRT2			0x7c
+#define A_FXRT_CHANNELE		0x0000003f	/* Effects send bus number for channel's effects send E	*/
+#define A_FXRT_CHANNELF		0x00003f00	/* Effects send bus number for channel's effects send F	*/
+#define A_FXRT_CHANNELG		0x003f0000	/* Effects send bus number for channel's effects send G	*/
+#define A_FXRT_CHANNELH		0x3f000000	/* Effects send bus number for channel's effects send H	*/
+
+#define A_SENDAMOUNTS		0x7d
+#define A_FXSENDAMOUNT_E_MASK	0xFF000000
+#define A_FXSENDAMOUNT_F_MASK	0x00FF0000
+#define A_FXSENDAMOUNT_G_MASK	0x0000FF00
+#define A_FXSENDAMOUNT_H_MASK	0x000000FF
+/* 0x7c, 0x7e "high bit is used for filtering" */
+ 
+/* The send amounts for this one are the same as used with the emu10k1 */
+#define A_FXRT1			0x7e
+#define A_FXRT_CHANNELA		0x0000003f
+#define A_FXRT_CHANNELB		0x00003f00
+#define A_FXRT_CHANNELC		0x003f0000
+#define A_FXRT_CHANNELD		0x3f000000
+
+
+/* Each FX general purpose register is 32 bits in length, all bits are used			*/
+#define FXGPREGBASE		0x100		/* FX general purpose registers base       	*/
+#define A_FXGPREGBASE		0x400		/* Audigy GPRs, 0x400 to 0x5ff			*/
+
+#define A_TANKMEMCTLREGBASE	0x100		/* Tank memory control registers base - only for Audigy */
+#define A_TANKMEMCTLREG_MASK	0x1f		/* only 5 bits used - only for Audigy */
+
+/* Tank audio data is logarithmically compressed down to 16 bits before writing to TRAM and is	*/
+/* decompressed back to 20 bits on a read.  There are a total of 160 locations, the last 32	*/
+/* locations are for external TRAM. 								*/
+#define TANKMEMDATAREGBASE	0x200		/* Tank memory data registers base     		*/
+#define TANKMEMDATAREG_MASK	0x000fffff	/* 20 bit tank audio data field			*/
+
+/* Combined address field and memory opcode or flag field.  160 locations, last 32 are external	*/
+#define TANKMEMADDRREGBASE	0x300		/* Tank memory address registers base		*/
+#define TANKMEMADDRREG_ADDR_MASK 0x000fffff	/* 20 bit tank address field			*/
+#define TANKMEMADDRREG_CLEAR	0x00800000	/* Clear tank memory				*/
+#define TANKMEMADDRREG_ALIGN	0x00400000	/* Align read or write relative to tank access	*/
+#define TANKMEMADDRREG_WRITE	0x00200000	/* Write to tank memory				*/
+#define TANKMEMADDRREG_READ	0x00100000	/* Read from tank memory			*/
+
+#define MICROCODEBASE		0x400		/* Microcode data base address			*/
+
+/* Each DSP microcode instruction is mapped into 2 doublewords 					*/
+/* NOTE: When writing, always write the LO doubleword first.  Reads can be in either order.	*/
+#define LOWORD_OPX_MASK		0x000ffc00	/* Instruction operand X			*/
+#define LOWORD_OPY_MASK		0x000003ff	/* Instruction operand Y			*/
+#define HIWORD_OPCODE_MASK	0x00f00000	/* Instruction opcode				*/
+#define HIWORD_RESULT_MASK	0x000ffc00	/* Instruction result				*/
+#define HIWORD_OPA_MASK		0x000003ff	/* Instruction operand A			*/
+
+
+/* Audigy Soundcard have a different instruction format */
+#define A_MICROCODEBASE		0x600
+#define A_LOWORD_OPY_MASK	0x000007ff
+#define A_LOWORD_OPX_MASK	0x007ff000
+#define A_HIWORD_OPCODE_MASK	0x0f000000
+#define A_HIWORD_RESULT_MASK	0x007ff000
+#define A_HIWORD_OPA_MASK	0x000007ff
+
+/************************************************************************************************/
+/* EMU1010m HANA FPGA registers									*/
+/************************************************************************************************/
+#define EMU_HANA_DESTHI		0x00	/* 0000xxx  3 bits Link Destination */
+#define EMU_HANA_DESTLO		0x01	/* 00xxxxx  5 bits */
+#define EMU_HANA_SRCHI		0x02	/* 0000xxx  3 bits Link Source */
+#define EMU_HANA_SRCLO		0x03	/* 00xxxxx  5 bits */
+#define EMU_HANA_DOCK_PWR	0x04	/* 000000x  1 bits Audio Dock power */
+#define EMU_HANA_DOCK_PWR_ON		0x01 /* Audio Dock power on */
+#define EMU_HANA_WCLOCK		0x05	/* 0000xxx  3 bits Word Clock source select  */
+					/* Must be written after power on to reset DLL */
+					/* One is unable to detect the Audio dock without this */
+#define EMU_HANA_WCLOCK_SRC_MASK	0x07
+#define EMU_HANA_WCLOCK_INT_48K		0x00
+#define EMU_HANA_WCLOCK_INT_44_1K	0x01
+#define EMU_HANA_WCLOCK_HANA_SPDIF_IN	0x02
+#define EMU_HANA_WCLOCK_HANA_ADAT_IN	0x03
+#define EMU_HANA_WCLOCK_SYNC_BNCN	0x04
+#define EMU_HANA_WCLOCK_2ND_HANA	0x05
+#define EMU_HANA_WCLOCK_SRC_RESERVED	0x06
+#define EMU_HANA_WCLOCK_OFF		0x07 /* For testing, forces fallback to DEFCLOCK */
+#define EMU_HANA_WCLOCK_MULT_MASK	0x18
+#define EMU_HANA_WCLOCK_1X		0x00
+#define EMU_HANA_WCLOCK_2X		0x08
+#define EMU_HANA_WCLOCK_4X		0x10
+#define EMU_HANA_WCLOCK_MULT_RESERVED	0x18
+
+#define EMU_HANA_DEFCLOCK	0x06	/* 000000x  1 bits Default Word Clock  */
+#define EMU_HANA_DEFCLOCK_48K		0x00
+#define EMU_HANA_DEFCLOCK_44_1K		0x01
+
+#define EMU_HANA_UNMUTE		0x07	/* 000000x  1 bits Mute all audio outputs  */
+#define EMU_MUTE			0x00
+#define EMU_UNMUTE			0x01
+
+#define EMU_HANA_FPGA_CONFIG	0x08	/* 00000xx  2 bits Config control of FPGAs  */
+#define EMU_HANA_FPGA_CONFIG_AUDIODOCK	0x01 /* Set in order to program FPGA on Audio Dock */
+#define EMU_HANA_FPGA_CONFIG_HANA	0x02 /* Set in order to program FPGA on Hana */
+
+#define EMU_HANA_IRQ_ENABLE	0x09	/* 000xxxx  4 bits IRQ Enable  */
+#define EMU_HANA_IRQ_WCLK_CHANGED	0x01
+#define EMU_HANA_IRQ_ADAT		0x02
+#define EMU_HANA_IRQ_DOCK		0x04
+#define EMU_HANA_IRQ_DOCK_LOST		0x08
+
+#define EMU_HANA_SPDIF_MODE	0x0a	/* 00xxxxx  5 bits SPDIF MODE  */
+#define EMU_HANA_SPDIF_MODE_TX_COMSUMER	0x00
+#define EMU_HANA_SPDIF_MODE_TX_PRO	0x01
+#define EMU_HANA_SPDIF_MODE_TX_NOCOPY	0x02
+#define EMU_HANA_SPDIF_MODE_RX_COMSUMER	0x00
+#define EMU_HANA_SPDIF_MODE_RX_PRO	0x04
+#define EMU_HANA_SPDIF_MODE_RX_NOCOPY	0x08
+#define EMU_HANA_SPDIF_MODE_RX_INVALID	0x10
+
+#define EMU_HANA_OPTICAL_TYPE	0x0b	/* 00000xx  2 bits ADAT or SPDIF in/out  */
+#define EMU_HANA_OPTICAL_IN_SPDIF	0x00
+#define EMU_HANA_OPTICAL_IN_ADAT	0x01
+#define EMU_HANA_OPTICAL_OUT_SPDIF	0x00
+#define EMU_HANA_OPTICAL_OUT_ADAT	0x02
+
+#define EMU_HANA_MIDI_IN		0x0c	/* 000000x  1 bit  Control MIDI  */
+#define EMU_HANA_MIDI_IN_FROM_HAMOA	0x00 /* HAMOA MIDI in to Alice 2 MIDI B */
+#define EMU_HANA_MIDI_IN_FROM_DOCK	0x01 /* Audio Dock MIDI in to Alice 2 MIDI B */
+
+#define EMU_HANA_DOCK_LEDS_1	0x0d	/* 000xxxx  4 bit  Audio Dock LEDs  */
+#define EMU_HANA_DOCK_LEDS_1_MIDI1	0x01	/* MIDI 1 LED on */
+#define EMU_HANA_DOCK_LEDS_1_MIDI2	0x02	/* MIDI 2 LED on */
+#define EMU_HANA_DOCK_LEDS_1_SMPTE_IN	0x04	/* SMPTE IN LED on */
+#define EMU_HANA_DOCK_LEDS_1_SMPTE_OUT	0x08	/* SMPTE OUT LED on */
+
+#define EMU_HANA_DOCK_LEDS_2	0x0e	/* 0xxxxxx  6 bit  Audio Dock LEDs  */
+#define EMU_HANA_DOCK_LEDS_2_44K	0x01	/* 44.1 kHz LED on */
+#define EMU_HANA_DOCK_LEDS_2_48K	0x02	/* 48 kHz LED on */
+#define EMU_HANA_DOCK_LEDS_2_96K	0x04	/* 96 kHz LED on */
+#define EMU_HANA_DOCK_LEDS_2_192K	0x08	/* 192 kHz LED on */
+#define EMU_HANA_DOCK_LEDS_2_LOCK	0x10	/* LOCK LED on */
+#define EMU_HANA_DOCK_LEDS_2_EXT	0x20	/* EXT LED on */
+
+#define EMU_HANA_DOCK_LEDS_3	0x0f	/* 0xxxxxx  6 bit  Audio Dock LEDs  */
+#define EMU_HANA_DOCK_LEDS_3_CLIP_A	0x01	/* Mic A Clip LED on */
+#define EMU_HANA_DOCK_LEDS_3_CLIP_B	0x02	/* Mic B Clip LED on */
+#define EMU_HANA_DOCK_LEDS_3_SIGNAL_A	0x04	/* Signal A Clip LED on */
+#define EMU_HANA_DOCK_LEDS_3_SIGNAL_B	0x08	/* Signal B Clip LED on */
+#define EMU_HANA_DOCK_LEDS_3_MANUAL_CLIP	0x10	/* Manual Clip detection */
+#define EMU_HANA_DOCK_LEDS_3_MANUAL_SIGNAL	0x20	/* Manual Signal detection */
+
+#define EMU_HANA_ADC_PADS	0x10	/* 0000xxx  3 bit  Audio Dock ADC 14dB pads */
+#define EMU_HANA_DOCK_ADC_PAD1	0x01	/* 14dB Attenuation on Audio Dock ADC 1 */
+#define EMU_HANA_DOCK_ADC_PAD2	0x02	/* 14dB Attenuation on Audio Dock ADC 2 */
+#define EMU_HANA_DOCK_ADC_PAD3	0x04	/* 14dB Attenuation on Audio Dock ADC 3 */
+#define EMU_HANA_0202_ADC_PAD1	0x08	/* 14dB Attenuation on 0202 ADC 1 */
+
+#define EMU_HANA_DOCK_MISC	0x11	/* 0xxxxxx  6 bit  Audio Dock misc bits */
+#define EMU_HANA_DOCK_DAC1_MUTE	0x01	/* DAC 1 Mute */
+#define EMU_HANA_DOCK_DAC2_MUTE	0x02	/* DAC 2 Mute */
+#define EMU_HANA_DOCK_DAC3_MUTE	0x04	/* DAC 3 Mute */
+#define EMU_HANA_DOCK_DAC4_MUTE	0x08	/* DAC 4 Mute */
+#define EMU_HANA_DOCK_PHONES_192_DAC1	0x00	/* DAC 1 Headphones source at 192kHz */
+#define EMU_HANA_DOCK_PHONES_192_DAC2	0x10	/* DAC 2 Headphones source at 192kHz */
+#define EMU_HANA_DOCK_PHONES_192_DAC3	0x20	/* DAC 3 Headphones source at 192kHz */
+#define EMU_HANA_DOCK_PHONES_192_DAC4	0x30	/* DAC 4 Headphones source at 192kHz */
+
+#define EMU_HANA_MIDI_OUT	0x12	/* 00xxxxx  5 bit  Source for each MIDI out port */
+#define EMU_HANA_MIDI_OUT_0202	0x01 /* 0202 MIDI from Alice 2. 0 = A, 1 = B */
+#define EMU_HANA_MIDI_OUT_DOCK1	0x02 /* Audio Dock MIDI1 front, from Alice 2. 0 = A, 1 = B */
+#define EMU_HANA_MIDI_OUT_DOCK2	0x04 /* Audio Dock MIDI2 rear, from Alice 2. 0 = A, 1 = B */
+#define EMU_HANA_MIDI_OUT_SYNC2	0x08 /* Sync card. Not the actual MIDI out jack. 0 = A, 1 = B */
+#define EMU_HANA_MIDI_OUT_LOOP	0x10 /* 0 = bits (3:0) normal. 1 = MIDI loopback enabled. */
+
+#define EMU_HANA_DAC_PADS	0x13	/* 00xxxxx  5 bit  DAC 14dB attenuation pads */
+#define EMU_HANA_DOCK_DAC_PAD1	0x01	/* 14dB Attenuation on AudioDock DAC 1. Left and Right */
+#define EMU_HANA_DOCK_DAC_PAD2	0x02	/* 14dB Attenuation on AudioDock DAC 2. Left and Right */
+#define EMU_HANA_DOCK_DAC_PAD3	0x04	/* 14dB Attenuation on AudioDock DAC 3. Left and Right */
+#define EMU_HANA_DOCK_DAC_PAD4	0x08	/* 14dB Attenuation on AudioDock DAC 4. Left and Right */
+#define EMU_HANA_0202_DAC_PAD1	0x10	/* 14dB Attenuation on 0202 DAC 1. Left and Right */
+
+/* 0x14 - 0x1f Unused R/W registers */
+#define EMU_HANA_IRQ_STATUS	0x20	/* 000xxxx  4 bits IRQ Status  */
+#if 0  /* Already defined for reg 0x09 IRQ_ENABLE */
+#define EMU_HANA_IRQ_WCLK_CHANGED	0x01
+#define EMU_HANA_IRQ_ADAT		0x02
+#define EMU_HANA_IRQ_DOCK		0x04
+#define EMU_HANA_IRQ_DOCK_LOST		0x08
+#endif
+
+#define EMU_HANA_OPTION_CARDS	0x21	/* 000xxxx  4 bits Presence of option cards */
+#define EMU_HANA_OPTION_HAMOA	0x01	/* HAMOA card present */
+#define EMU_HANA_OPTION_SYNC	0x02	/* Sync card present */
+#define EMU_HANA_OPTION_DOCK_ONLINE	0x04	/* Audio Dock online and FPGA configured */
+#define EMU_HANA_OPTION_DOCK_OFFLINE	0x08	/* Audio Dock online and FPGA not configured */
+
+#define EMU_HANA_ID		0x22	/* 1010101  7 bits ID byte & 0x7f = 0x55 */
+
+#define EMU_HANA_MAJOR_REV	0x23	/* 0000xxx  3 bit  Hana FPGA Major rev */
+#define EMU_HANA_MINOR_REV	0x24	/* 0000xxx  3 bit  Hana FPGA Minor rev */
+
+#define EMU_DOCK_MAJOR_REV	0x25	/* 0000xxx  3 bit  Audio Dock FPGA Major rev */
+#define EMU_DOCK_MINOR_REV	0x26	/* 0000xxx  3 bit  Audio Dock FPGA Minor rev */
+
+#define EMU_DOCK_BOARD_ID	0x27	/* 00000xx  2 bits Audio Dock ID pins */
+#define EMU_DOCK_BOARD_ID0	0x00	/* ID bit 0 */
+#define EMU_DOCK_BOARD_ID1	0x03	/* ID bit 1 */
+
+#define EMU_HANA_WC_SPDIF_HI	0x28	/* 0xxxxxx  6 bit  SPDIF IN Word clock, upper 6 bits */
+#define EMU_HANA_WC_SPDIF_LO	0x29	/* 0xxxxxx  6 bit  SPDIF IN Word clock, lower 6 bits */
+
+#define EMU_HANA_WC_ADAT_HI	0x2a	/* 0xxxxxx  6 bit  ADAT IN Word clock, upper 6 bits */
+#define EMU_HANA_WC_ADAT_LO	0x2b	/* 0xxxxxx  6 bit  ADAT IN Word clock, lower 6 bits */
+
+#define EMU_HANA_WC_BNC_LO	0x2c	/* 0xxxxxx  6 bit  BNC IN Word clock, lower 6 bits */
+#define EMU_HANA_WC_BNC_HI	0x2d	/* 0xxxxxx  6 bit  BNC IN Word clock, upper 6 bits */
+
+#define EMU_HANA2_WC_SPDIF_HI	0x2e	/* 0xxxxxx  6 bit  HANA2 SPDIF IN Word clock, upper 6 bits */
+#define EMU_HANA2_WC_SPDIF_LO	0x2f	/* 0xxxxxx  6 bit  HANA2 SPDIF IN Word clock, lower 6 bits */
+/* 0x30 - 0x3f Unused Read only registers */
+
+/************************************************************************************************/
+/* EMU1010m HANA Destinations									*/
+/************************************************************************************************/
+#define EMU_DST_ALICE2_EMU32_0	0x000f	/* 16 EMU32 channels to Alice2 +0 to +0xf */
+#define EMU_DST_ALICE2_EMU32_1	0x0000	/* 16 EMU32 channels to Alice2 +0 to +0xf */
+#define EMU_DST_ALICE2_EMU32_2	0x0001	/* 16 EMU32 channels to Alice2 +0 to +0xf */
+#define EMU_DST_ALICE2_EMU32_3	0x0002	/* 16 EMU32 channels to Alice2 +0 to +0xf */
+#define EMU_DST_ALICE2_EMU32_4	0x0003	/* 16 EMU32 channels to Alice2 +0 to +0xf */
+#define EMU_DST_ALICE2_EMU32_5	0x0004	/* 16 EMU32 channels to Alice2 +0 to +0xf */
+#define EMU_DST_ALICE2_EMU32_6	0x0005	/* 16 EMU32 channels to Alice2 +0 to +0xf */
+#define EMU_DST_ALICE2_EMU32_7	0x0006	/* 16 EMU32 channels to Alice2 +0 to +0xf */
+#define EMU_DST_ALICE2_EMU32_8	0x0007	/* 16 EMU32 channels to Alice2 +0 to +0xf */
+#define EMU_DST_ALICE2_EMU32_9	0x0008	/* 16 EMU32 channels to Alice2 +0 to +0xf */
+#define EMU_DST_ALICE2_EMU32_A	0x0009	/* 16 EMU32 channels to Alice2 +0 to +0xf */
+#define EMU_DST_ALICE2_EMU32_B	0x000a	/* 16 EMU32 channels to Alice2 +0 to +0xf */
+#define EMU_DST_ALICE2_EMU32_C	0x000b	/* 16 EMU32 channels to Alice2 +0 to +0xf */
+#define EMU_DST_ALICE2_EMU32_D	0x000c	/* 16 EMU32 channels to Alice2 +0 to +0xf */
+#define EMU_DST_ALICE2_EMU32_E	0x000d	/* 16 EMU32 channels to Alice2 +0 to +0xf */
+#define EMU_DST_ALICE2_EMU32_F	0x000e	/* 16 EMU32 channels to Alice2 +0 to +0xf */
+#define EMU_DST_DOCK_DAC1_LEFT1	0x0100	/* Audio Dock DAC1 Left, 1st or 48kHz only */
+#define EMU_DST_DOCK_DAC1_LEFT2	0x0101	/* Audio Dock DAC1 Left, 2nd or 96kHz */
+#define EMU_DST_DOCK_DAC1_LEFT3	0x0102	/* Audio Dock DAC1 Left, 3rd or 192kHz */
+#define EMU_DST_DOCK_DAC1_LEFT4	0x0103	/* Audio Dock DAC1 Left, 4th or 192kHz */
+#define EMU_DST_DOCK_DAC1_RIGHT1	0x0104	/* Audio Dock DAC1 Right, 1st or 48kHz only */
+#define EMU_DST_DOCK_DAC1_RIGHT2	0x0105	/* Audio Dock DAC1 Right, 2nd or 96kHz */
+#define EMU_DST_DOCK_DAC1_RIGHT3	0x0106	/* Audio Dock DAC1 Right, 3rd or 192kHz */
+#define EMU_DST_DOCK_DAC1_RIGHT4	0x0107	/* Audio Dock DAC1 Right, 4th or 192kHz */
+#define EMU_DST_DOCK_DAC2_LEFT1	0x0108	/* Audio Dock DAC2 Left, 1st or 48kHz only */
+#define EMU_DST_DOCK_DAC2_LEFT2	0x0109	/* Audio Dock DAC2 Left, 2nd or 96kHz */
+#define EMU_DST_DOCK_DAC2_LEFT3	0x010a	/* Audio Dock DAC2 Left, 3rd or 192kHz */
+#define EMU_DST_DOCK_DAC2_LEFT4	0x010b	/* Audio Dock DAC2 Left, 4th or 192kHz */
+#define EMU_DST_DOCK_DAC2_RIGHT1	0x010c	/* Audio Dock DAC2 Right, 1st or 48kHz only */
+#define EMU_DST_DOCK_DAC2_RIGHT2	0x010d	/* Audio Dock DAC2 Right, 2nd or 96kHz */
+#define EMU_DST_DOCK_DAC2_RIGHT3	0x010e	/* Audio Dock DAC2 Right, 3rd or 192kHz */
+#define EMU_DST_DOCK_DAC2_RIGHT4	0x010f	/* Audio Dock DAC2 Right, 4th or 192kHz */
+#define EMU_DST_DOCK_DAC3_LEFT1	0x0110	/* Audio Dock DAC1 Left, 1st or 48kHz only */
+#define EMU_DST_DOCK_DAC3_LEFT2	0x0111	/* Audio Dock DAC1 Left, 2nd or 96kHz */
+#define EMU_DST_DOCK_DAC3_LEFT3	0x0112	/* Audio Dock DAC1 Left, 3rd or 192kHz */
+#define EMU_DST_DOCK_DAC3_LEFT4	0x0113	/* Audio Dock DAC1 Left, 4th or 192kHz */
+#define EMU_DST_DOCK_PHONES_LEFT1	0x0112	/* Audio Dock PHONES Left, 1st or 48kHz only */
+#define EMU_DST_DOCK_PHONES_LEFT2	0x0113	/* Audio Dock PHONES Left, 2nd or 96kHz */
+#define EMU_DST_DOCK_DAC3_RIGHT1	0x0114	/* Audio Dock DAC1 Right, 1st or 48kHz only */
+#define EMU_DST_DOCK_DAC3_RIGHT2	0x0115	/* Audio Dock DAC1 Right, 2nd or 96kHz */
+#define EMU_DST_DOCK_DAC3_RIGHT3	0x0116	/* Audio Dock DAC1 Right, 3rd or 192kHz */
+#define EMU_DST_DOCK_DAC3_RIGHT4	0x0117	/* Audio Dock DAC1 Right, 4th or 192kHz */
+#define EMU_DST_DOCK_PHONES_RIGHT1	0x0116	/* Audio Dock PHONES Right, 1st or 48kHz only */
+#define EMU_DST_DOCK_PHONES_RIGHT2	0x0117	/* Audio Dock PHONES Right, 2nd or 96kHz */
+#define EMU_DST_DOCK_DAC4_LEFT1	0x0118	/* Audio Dock DAC2 Left, 1st or 48kHz only */
+#define EMU_DST_DOCK_DAC4_LEFT2	0x0119	/* Audio Dock DAC2 Left, 2nd or 96kHz */
+#define EMU_DST_DOCK_DAC4_LEFT3	0x011a	/* Audio Dock DAC2 Left, 3rd or 192kHz */
+#define EMU_DST_DOCK_DAC4_LEFT4	0x011b	/* Audio Dock DAC2 Left, 4th or 192kHz */
+#define EMU_DST_DOCK_SPDIF_LEFT1	0x011a	/* Audio Dock SPDIF Left, 1st or 48kHz only */
+#define EMU_DST_DOCK_SPDIF_LEFT2	0x011b	/* Audio Dock SPDIF Left, 2nd or 96kHz */
+#define EMU_DST_DOCK_DAC4_RIGHT1	0x011c	/* Audio Dock DAC2 Right, 1st or 48kHz only */
+#define EMU_DST_DOCK_DAC4_RIGHT2	0x011d	/* Audio Dock DAC2 Right, 2nd or 96kHz */
+#define EMU_DST_DOCK_DAC4_RIGHT3	0x011e	/* Audio Dock DAC2 Right, 3rd or 192kHz */
+#define EMU_DST_DOCK_DAC4_RIGHT4	0x011f	/* Audio Dock DAC2 Right, 4th or 192kHz */
+#define EMU_DST_DOCK_SPDIF_RIGHT1	0x011e	/* Audio Dock SPDIF Right, 1st or 48kHz only */
+#define EMU_DST_DOCK_SPDIF_RIGHT2	0x011f	/* Audio Dock SPDIF Right, 2nd or 96kHz */
+#define EMU_DST_HANA_SPDIF_LEFT1	0x0200	/* Hana SPDIF Left, 1st or 48kHz only */
+#define EMU_DST_HANA_SPDIF_LEFT2	0x0202	/* Hana SPDIF Left, 2nd or 96kHz */
+#define EMU_DST_HANA_SPDIF_RIGHT1	0x0201	/* Hana SPDIF Right, 1st or 48kHz only */
+#define EMU_DST_HANA_SPDIF_RIGHT2	0x0203	/* Hana SPDIF Right, 2nd or 96kHz */
+#define EMU_DST_HAMOA_DAC_LEFT1	0x0300	/* Hamoa DAC Left, 1st or 48kHz only */
+#define EMU_DST_HAMOA_DAC_LEFT2	0x0302	/* Hamoa DAC Left, 2nd or 96kHz */
+#define EMU_DST_HAMOA_DAC_LEFT3	0x0304	/* Hamoa DAC Left, 3rd or 192kHz */
+#define EMU_DST_HAMOA_DAC_LEFT4	0x0306	/* Hamoa DAC Left, 4th or 192kHz */
+#define EMU_DST_HAMOA_DAC_RIGHT1	0x0301	/* Hamoa DAC Right, 1st or 48kHz only */
+#define EMU_DST_HAMOA_DAC_RIGHT2	0x0303	/* Hamoa DAC Right, 2nd or 96kHz */
+#define EMU_DST_HAMOA_DAC_RIGHT3	0x0305	/* Hamoa DAC Right, 3rd or 192kHz */
+#define EMU_DST_HAMOA_DAC_RIGHT4	0x0307	/* Hamoa DAC Right, 4th or 192kHz */
+#define EMU_DST_HANA_ADAT	0x0400	/* Hana ADAT 8 channel out +0 to +7 */
+#define EMU_DST_ALICE_I2S0_LEFT		0x0500	/* Alice2 I2S0 Left */
+#define EMU_DST_ALICE_I2S0_RIGHT	0x0501	/* Alice2 I2S0 Right */
+#define EMU_DST_ALICE_I2S1_LEFT		0x0600	/* Alice2 I2S1 Left */
+#define EMU_DST_ALICE_I2S1_RIGHT	0x0601	/* Alice2 I2S1 Right */
+#define EMU_DST_ALICE_I2S2_LEFT		0x0700	/* Alice2 I2S2 Left */
+#define EMU_DST_ALICE_I2S2_RIGHT	0x0701	/* Alice2 I2S2 Right */
+
+/************************************************************************************************/
+/* EMU1010m HANA Sources									*/
+/************************************************************************************************/
+#define EMU_SRC_SILENCE		0x0000	/* Silence */
+#define EMU_SRC_DOCK_MIC_A1	0x0100	/* Audio Dock Mic A, 1st or 48kHz only */
+#define EMU_SRC_DOCK_MIC_A2	0x0101	/* Audio Dock Mic A, 2nd or 96kHz */
+#define EMU_SRC_DOCK_MIC_A3	0x0102	/* Audio Dock Mic A, 3rd or 192kHz */
+#define EMU_SRC_DOCK_MIC_A4	0x0103	/* Audio Dock Mic A, 4th or 192kHz */
+#define EMU_SRC_DOCK_MIC_B1	0x0104	/* Audio Dock Mic B, 1st or 48kHz only */
+#define EMU_SRC_DOCK_MIC_B2	0x0105	/* Audio Dock Mic B, 2nd or 96kHz */
+#define EMU_SRC_DOCK_MIC_B3	0x0106	/* Audio Dock Mic B, 3rd or 192kHz */
+#define EMU_SRC_DOCK_MIC_B4	0x0107	/* Audio Dock Mic B, 4th or 192kHz */
+#define EMU_SRC_DOCK_ADC1_LEFT1	0x0108	/* Audio Dock ADC1 Left, 1st or 48kHz only */
+#define EMU_SRC_DOCK_ADC1_LEFT2	0x0109	/* Audio Dock ADC1 Left, 2nd or 96kHz */
+#define EMU_SRC_DOCK_ADC1_LEFT3	0x010a	/* Audio Dock ADC1 Left, 3rd or 192kHz */
+#define EMU_SRC_DOCK_ADC1_LEFT4	0x010b	/* Audio Dock ADC1 Left, 4th or 192kHz */
+#define EMU_SRC_DOCK_ADC1_RIGHT1	0x010c	/* Audio Dock ADC1 Right, 1st or 48kHz only */
+#define EMU_SRC_DOCK_ADC1_RIGHT2	0x010d	/* Audio Dock ADC1 Right, 2nd or 96kHz */
+#define EMU_SRC_DOCK_ADC1_RIGHT3	0x010e	/* Audio Dock ADC1 Right, 3rd or 192kHz */
+#define EMU_SRC_DOCK_ADC1_RIGHT4	0x010f	/* Audio Dock ADC1 Right, 4th or 192kHz */
+#define EMU_SRC_DOCK_ADC2_LEFT1	0x0110	/* Audio Dock ADC2 Left, 1st or 48kHz only */
+#define EMU_SRC_DOCK_ADC2_LEFT2	0x0111	/* Audio Dock ADC2 Left, 2nd or 96kHz */
+#define EMU_SRC_DOCK_ADC2_LEFT3	0x0112	/* Audio Dock ADC2 Left, 3rd or 192kHz */
+#define EMU_SRC_DOCK_ADC2_LEFT4	0x0113	/* Audio Dock ADC2 Left, 4th or 192kHz */
+#define EMU_SRC_DOCK_ADC2_RIGHT1	0x0114	/* Audio Dock ADC2 Right, 1st or 48kHz only */
+#define EMU_SRC_DOCK_ADC2_RIGHT2	0x0115	/* Audio Dock ADC2 Right, 2nd or 96kHz */
+#define EMU_SRC_DOCK_ADC2_RIGHT3	0x0116	/* Audio Dock ADC2 Right, 3rd or 192kHz */
+#define EMU_SRC_DOCK_ADC2_RIGHT4	0x0117	/* Audio Dock ADC2 Right, 4th or 192kHz */
+#define EMU_SRC_DOCK_ADC3_LEFT1	0x0118	/* Audio Dock ADC3 Left, 1st or 48kHz only */
+#define EMU_SRC_DOCK_ADC3_LEFT2	0x0119	/* Audio Dock ADC3 Left, 2nd or 96kHz */
+#define EMU_SRC_DOCK_ADC3_LEFT3	0x011a	/* Audio Dock ADC3 Left, 3rd or 192kHz */
+#define EMU_SRC_DOCK_ADC3_LEFT4	0x011b	/* Audio Dock ADC3 Left, 4th or 192kHz */
+#define EMU_SRC_DOCK_ADC3_RIGHT1	0x011c	/* Audio Dock ADC3 Right, 1st or 48kHz only */
+#define EMU_SRC_DOCK_ADC3_RIGHT2	0x011d	/* Audio Dock ADC3 Right, 2nd or 96kHz */
+#define EMU_SRC_DOCK_ADC3_RIGHT3	0x011e	/* Audio Dock ADC3 Right, 3rd or 192kHz */
+#define EMU_SRC_DOCK_ADC3_RIGHT4	0x011f	/* Audio Dock ADC3 Right, 4th or 192kHz */
+#define EMU_SRC_HAMOA_ADC_LEFT1	0x0200	/* Hamoa ADC Left, 1st or 48kHz only */
+#define EMU_SRC_HAMOA_ADC_LEFT2	0x0202	/* Hamoa ADC Left, 2nd or 96kHz */
+#define EMU_SRC_HAMOA_ADC_LEFT3	0x0204	/* Hamoa ADC Left, 3rd or 192kHz */
+#define EMU_SRC_HAMOA_ADC_LEFT4	0x0206	/* Hamoa ADC Left, 4th or 192kHz */
+#define EMU_SRC_HAMOA_ADC_RIGHT1	0x0201	/* Hamoa ADC Right, 1st or 48kHz only */
+#define EMU_SRC_HAMOA_ADC_RIGHT2	0x0203	/* Hamoa ADC Right, 2nd or 96kHz */
+#define EMU_SRC_HAMOA_ADC_RIGHT3	0x0205	/* Hamoa ADC Right, 3rd or 192kHz */
+#define EMU_SRC_HAMOA_ADC_RIGHT4	0x0207	/* Hamoa ADC Right, 4th or 192kHz */
+#define EMU_SRC_ALICE_EMU32A		0x0300	/* Alice2 EMU32a 16 outputs. +0 to +0xf */
+#define EMU_SRC_ALICE_EMU32B		0x0310	/* Alice2 EMU32b 16 outputs. +0 to +0xf */
+#define EMU_SRC_HANA_ADAT	0x0400	/* Hana ADAT 8 channel in +0 to +7 */
+#define EMU_SRC_HANA_SPDIF_LEFT1	0x0500	/* Hana SPDIF Left, 1st or 48kHz only */
+#define EMU_SRC_HANA_SPDIF_LEFT2	0x0502	/* Hana SPDIF Left, 2nd or 96kHz */
+#define EMU_SRC_HANA_SPDIF_RIGHT1	0x0501	/* Hana SPDIF Right, 1st or 48kHz only */
+#define EMU_SRC_HANA_SPDIF_RIGHT2	0x0503	/* Hana SPDIF Right, 2nd or 96kHz */
+/* 0x600 and 0x700 no used */
+
+/* ------------------- STRUCTURES -------------------- */
+
+enum {
+	EMU10K1_EFX,
+	EMU10K1_PCM,
+	EMU10K1_SYNTH,
+	EMU10K1_MIDI
+};
+
+struct snd_emu10k1;
+
+struct snd_emu10k1_voice {
+	struct snd_emu10k1 *emu;
+	int number;
+	unsigned int use: 1,
+	    pcm: 1,
+	    efx: 1,
+	    synth: 1,
+	    midi: 1;
+	void (*interrupt)(struct snd_emu10k1 *emu, struct snd_emu10k1_voice *pvoice);
+
+	struct snd_emu10k1_pcm *epcm;
+};
+
+enum {
+	PLAYBACK_EMUVOICE,
+	PLAYBACK_EFX,
+	CAPTURE_AC97ADC,
+	CAPTURE_AC97MIC,
+	CAPTURE_EFX
+};
+
+struct snd_emu10k1_pcm {
+	struct snd_emu10k1 *emu;
+	int type;
+	struct snd_pcm_substream *substream;
+	struct snd_emu10k1_voice *voices[NUM_EFX_PLAYBACK];
+	struct snd_emu10k1_voice *extra;
+	unsigned short running;
+	unsigned short first_ptr;
+	struct snd_util_memblk *memblk;
+	unsigned int start_addr;
+	unsigned int ccca_start_addr;
+	unsigned int capture_ipr;	/* interrupt acknowledge mask */
+	unsigned int capture_inte;	/* interrupt enable mask */
+	unsigned int capture_ba_reg;	/* buffer address register */
+	unsigned int capture_bs_reg;	/* buffer size register */
+	unsigned int capture_idx_reg;	/* buffer index register */
+	unsigned int capture_cr_val;	/* control value */
+	unsigned int capture_cr_val2;	/* control value2 (for audigy) */
+	unsigned int capture_bs_val;	/* buffer size value */
+	unsigned int capture_bufsize;	/* buffer size in bytes */
+};
+
+struct snd_emu10k1_pcm_mixer {
+	/* mono, left, right x 8 sends (4 on emu10k1) */
+	unsigned char send_routing[3][8];
+	unsigned char send_volume[3][8];
+	unsigned short attn[3];
+	struct snd_emu10k1_pcm *epcm;
+};
+
+#define snd_emu10k1_compose_send_routing(route) \
+((route[0] | (route[1] << 4) | (route[2] << 8) | (route[3] << 12)) << 16)
+
+#define snd_emu10k1_compose_audigy_fxrt1(route) \
+((unsigned int)route[0] | ((unsigned int)route[1] << 8) | ((unsigned int)route[2] << 16) | ((unsigned int)route[3] << 24))
+
+#define snd_emu10k1_compose_audigy_fxrt2(route) \
+((unsigned int)route[4] | ((unsigned int)route[5] << 8) | ((unsigned int)route[6] << 16) | ((unsigned int)route[7] << 24))
+
+struct snd_emu10k1_memblk {
+	struct snd_util_memblk mem;
+	/* private part */
+	int first_page, last_page, pages, mapped_page;
+	unsigned int map_locked;
+	struct list_head mapped_link;
+	struct list_head mapped_order_link;
+};
+
+#define snd_emu10k1_memblk_offset(blk)	(((blk)->mapped_page << PAGE_SHIFT) | ((blk)->mem.offset & (PAGE_SIZE - 1)))
+
+#define EMU10K1_MAX_TRAM_BLOCKS_PER_CODE	16
+
+struct snd_emu10k1_fx8010_ctl {
+	struct list_head list;		/* list link container */
+	unsigned int vcount;
+	unsigned int count;		/* count of GPR (1..16) */
+	unsigned short gpr[32];		/* GPR number(s) */
+	unsigned int value[32];
+	unsigned int min;		/* minimum range */
+	unsigned int max;		/* maximum range */
+	unsigned int translation;	/* translation type (EMU10K1_GPR_TRANSLATION*) */
+	struct snd_kcontrol *kcontrol;
+};
+
+typedef void (snd_fx8010_irq_handler_t)(struct snd_emu10k1 *emu, void *private_data);
+
+struct snd_emu10k1_fx8010_irq {
+	struct snd_emu10k1_fx8010_irq *next;
+	snd_fx8010_irq_handler_t *handler;
+	unsigned short gpr_running;
+	void *private_data;
+};
+
+struct snd_emu10k1_fx8010_pcm {
+	unsigned int valid: 1,
+		     opened: 1,
+		     active: 1;
+	unsigned int channels;		/* 16-bit channels count */
+	unsigned int tram_start;	/* initial ring buffer position in TRAM (in samples) */
+	unsigned int buffer_size;	/* count of buffered samples */
+	unsigned short gpr_size;		/* GPR containing size of ring buffer in samples (host) */
+	unsigned short gpr_ptr;		/* GPR containing current pointer in the ring buffer (host = reset, FX8010) */
+	unsigned short gpr_count;	/* GPR containing count of samples between two interrupts (host) */
+	unsigned short gpr_tmpcount;	/* GPR containing current count of samples to interrupt (host = set, FX8010) */
+	unsigned short gpr_trigger;	/* GPR containing trigger (activate) information (host) */
+	unsigned short gpr_running;	/* GPR containing info if PCM is running (FX8010) */
+	unsigned char etram[32];	/* external TRAM address & data */
+	struct snd_pcm_indirect pcm_rec;
+	unsigned int tram_pos;
+	unsigned int tram_shift;
+	struct snd_emu10k1_fx8010_irq *irq;
+};
+
+struct snd_emu10k1_fx8010 {
+	unsigned short fxbus_mask;	/* used FX buses (bitmask) */
+	unsigned short extin_mask;	/* used external inputs (bitmask) */
+	unsigned short extout_mask;	/* used external outputs (bitmask) */
+	unsigned short pad1;
+	unsigned int itram_size;	/* internal TRAM size in samples */
+	struct snd_dma_buffer etram_pages; /* external TRAM pages and size */
+	unsigned int dbg;		/* FX debugger register */
+	unsigned char name[128];
+	int gpr_size;			/* size of allocated GPR controls */
+	int gpr_count;			/* count of used kcontrols */
+	struct list_head gpr_ctl;	/* GPR controls */
+	struct semaphore lock;
+	struct snd_emu10k1_fx8010_pcm pcm[8];
+	spinlock_t irq_lock;
+	struct snd_emu10k1_fx8010_irq *irq_handlers;
+};
+
+#define emu10k1_gpr_ctl(n) list_entry(n, struct snd_emu10k1_fx8010_ctl, list)
+
+struct snd_emu10k1_midi {
+	struct snd_emu10k1 *emu;
+	struct snd_rawmidi *rmidi;
+	struct snd_rawmidi_substream *substream_input;
+	struct snd_rawmidi_substream *substream_output;
+	unsigned int midi_mode;
+	spinlock_t input_lock;
+	spinlock_t output_lock;
+	spinlock_t open_lock;
+	int tx_enable, rx_enable;
+	int port;
+	int ipr_tx, ipr_rx;
+	void (*interrupt)(struct snd_emu10k1 *emu, unsigned int status);
+};
+
+struct snd_emu_chip_details {
+	u32 vendor;
+	u32 device;
+	u32 subsystem;
+	unsigned char revision;
+	unsigned char emu10k1_chip; /* Original SB Live. Not SB Live 24bit. */
+	unsigned char emu10k2_chip; /* Audigy 1 or Audigy 2. */
+	unsigned char ca0102_chip;  /* Audigy 1 or Audigy 2. Not SB Audigy 2 Value. */
+	unsigned char ca0108_chip;  /* Audigy 2 Value */
+	unsigned char ca_cardbus_chip; /* Audigy 2 ZS Notebook */
+	unsigned char ca0151_chip;  /* P16V */
+	unsigned char spk71;        /* Has 7.1 speakers */
+	unsigned char sblive51;	    /* SBLive! 5.1 - extout 0x11 -> center, 0x12 -> lfe */
+	unsigned char spdif_bug;    /* Has Spdif phasing bug */
+	unsigned char ac97_chip;    /* Has an AC97 chip: 1 = mandatory, 2 = optional */
+        unsigned char ecard;        /* APS EEPROM */
+        unsigned char emu1010;     /* EMU 1010m card */
+	unsigned char spi_dac;      /* SPI interface for DAC */
+	unsigned char i2c_adc;      /* I2C interface for ADC */
+	unsigned char adc_1361t;    /* Use Philips 1361T ADC */
+	const char *driver;
+	const char *name;
+	const char *id;		/* for backward compatibility - can be NULL if not needed */
+};
+
+struct snd_emu1010 {
+	unsigned int output_source[64];
+	unsigned int input_source[64];
+	unsigned int adc_pads; /* bit mask */
+	unsigned int dac_pads; /* bit mask */
+	unsigned int internal_clock; /* 44100 or 48000 */
+};
+
+
+struct snd_emu10k1 {
+	int irq;
+
+	unsigned long port;			/* I/O port number */
+	unsigned int tos_link: 1,		/* tos link detected */
+		rear_ac97: 1,			/* rear channels are on AC'97 */
+		enable_ir: 1;
+	/* Contains profile of card capabilities */
+	const struct snd_emu_chip_details *card_capabilities;
+	unsigned int audigy;			/* is Audigy? */
+	unsigned int revision;			/* chip revision */
+	unsigned int serial;			/* serial number */
+	unsigned short model;			/* subsystem id */
+	unsigned int card_type;			/* EMU10K1_CARD_* */
+	unsigned int ecard_ctrl;		/* ecard control bits */
+	unsigned long dma_mask;			/* PCI DMA mask */
+	int max_cache_pages;			/* max memory size / PAGE_SIZE */
+	struct snd_dma_buffer silent_page;	/* silent page */
+	struct snd_dma_buffer ptb_pages;	/* page table pages */
+	struct snd_dma_device p16v_dma_dev;
+	struct snd_dma_buffer p16v_buffer;
+
+	struct snd_util_memhdr *memhdr;		/* page allocation list */
+	struct snd_emu10k1_memblk *reserved_page;	/* reserved page */
+
+	struct list_head mapped_link_head;
+	struct list_head mapped_order_link_head;
+	void **page_ptr_table;
+	unsigned long *page_addr_table;
+	spinlock_t memblk_lock;
+
+	unsigned int spdif_bits[3];		/* s/pdif out setup */
+
+	struct snd_emu10k1_fx8010 fx8010;		/* FX8010 info */
+	int gpr_base;
+	
+	struct snd_ac97 *ac97;
+
+	struct pci_dev *pci;
+	struct snd_card *card;
+	struct snd_pcm *pcm;
+	struct snd_pcm *pcm_mic;
+	struct snd_pcm *pcm_efx;
+	struct snd_pcm *pcm_multi;
+	struct snd_pcm *pcm_p16v;
+
+	spinlock_t synth_lock;
+	void *synth;
+	int (*get_synth_voice)(struct snd_emu10k1 *emu);
+
+	spinlock_t reg_lock;
+	spinlock_t emu_lock;
+	spinlock_t voice_lock;
+	//struct semaphore ptb_lock;
+
+	struct snd_emu10k1_voice voices[NUM_G];
+	struct snd_emu10k1_voice p16v_voices[4];
+	struct snd_emu10k1_voice p16v_capture_voice;
+	int p16v_device_offset;
+        u32 p16v_capture_source;
+        struct snd_emu1010 emu1010;
+	u32 p16v_capture_channel;
+	struct snd_emu10k1_pcm_mixer pcm_mixer[32];
+	struct snd_emu10k1_pcm_mixer efx_pcm_mixer[NUM_EFX_PLAYBACK];
+	struct snd_kcontrol *ctl_send_routing;
+	struct snd_kcontrol *ctl_send_volume;
+	struct snd_kcontrol *ctl_attn;
+	struct snd_kcontrol *ctl_efx_send_routing;
+	struct snd_kcontrol *ctl_efx_send_volume;
+	struct snd_kcontrol *ctl_efx_attn;
+
+	void (*hwvol_interrupt)(struct snd_emu10k1 *emu, unsigned int status);
+	void (*capture_interrupt)(struct snd_emu10k1 *emu, unsigned int status);
+	void (*capture_mic_interrupt)(struct snd_emu10k1 *emu, unsigned int status);
+	void (*capture_efx_interrupt)(struct snd_emu10k1 *emu, unsigned int status);
+	void (*spdif_interrupt)(struct snd_emu10k1 *emu, unsigned int status);
+	void (*dsp_interrupt)(struct snd_emu10k1 *emu);
+
+	struct snd_pcm_substream *pcm_capture_substream;
+	struct snd_pcm_substream *pcm_capture_mic_substream;
+	struct snd_pcm_substream *pcm_capture_efx_substream;
+	struct snd_pcm_substream *pcm_playback_efx_substream;
+
+	struct snd_timer *timer;
+
+	struct snd_emu10k1_midi midi;
+	struct snd_emu10k1_midi midi2; /* for audigy */
+
+	unsigned int efx_voices_mask[2];
+	unsigned int next_free_voice;
+
+#ifdef CONFIG_PM
+	unsigned int *saved_ptr;
+	unsigned int *saved_gpr;
+	unsigned int *tram_val_saved;
+	unsigned int *tram_addr_saved;
+	unsigned int *saved_icode;
+	unsigned int *p16v_saved;
+	unsigned int saved_a_iocfg, saved_hcfg;
+#endif
+
+};
+
+int snd_emu10k1_create(struct snd_card *card,
+		       struct pci_dev *pci,
+		       unsigned short extin_mask,
+		       unsigned short extout_mask,
+		       long max_cache_bytes,
+		       int enable_ir,
+		       uint subsystem,
+		       struct snd_emu10k1 ** remu);
+
+int snd_emu10k1_pcm(struct snd_emu10k1 * emu, int device, struct snd_pcm ** rpcm);
+int snd_emu10k1_pcm_mic(struct snd_emu10k1 * emu, int device, struct snd_pcm ** rpcm);
+int snd_emu10k1_pcm_efx(struct snd_emu10k1 * emu, int device, struct snd_pcm ** rpcm);
+int snd_p16v_pcm(struct snd_emu10k1 * emu, int device, struct snd_pcm ** rpcm);
+int snd_p16v_free(struct snd_emu10k1 * emu);
+int snd_p16v_mixer(struct snd_emu10k1 * emu);
+int snd_emu10k1_pcm_multi(struct snd_emu10k1 * emu, int device, struct snd_pcm ** rpcm);
+int snd_emu10k1_fx8010_pcm(struct snd_emu10k1 * emu, int device, struct snd_pcm ** rpcm);
+int snd_emu10k1_mixer(struct snd_emu10k1 * emu, int pcm_device, int multi_device);
+int snd_emu10k1_timer(struct snd_emu10k1 * emu, int device);
+int snd_emu10k1_fx8010_new(struct snd_emu10k1 *emu, int device, struct snd_hwdep ** rhwdep);
+
+irqreturn_t snd_emu10k1_interrupt(int irq, void *dev_id);
+
+void snd_emu10k1_voice_init(struct snd_emu10k1 * emu, int voice);
+int snd_emu10k1_init_efx(struct snd_emu10k1 *emu);
+void snd_emu10k1_free_efx(struct snd_emu10k1 *emu);
+int snd_emu10k1_fx8010_tram_setup(struct snd_emu10k1 *emu, u32 size);
+int snd_emu10k1_done(struct snd_emu10k1 * emu);
+
+/* I/O functions */
+unsigned int snd_emu10k1_ptr_read(struct snd_emu10k1 * emu, unsigned int reg, unsigned int chn);
+void snd_emu10k1_ptr_write(struct snd_emu10k1 *emu, unsigned int reg, unsigned int chn, unsigned int data);
+unsigned int snd_emu10k1_ptr20_read(struct snd_emu10k1 * emu, unsigned int reg, unsigned int chn);
+void snd_emu10k1_ptr20_write(struct snd_emu10k1 *emu, unsigned int reg, unsigned int chn, unsigned int data);
+int snd_emu10k1_spi_write(struct snd_emu10k1 * emu, unsigned int data);
+int snd_emu1010_fpga_write(struct snd_emu10k1 * emu, int reg, int value);
+int snd_emu1010_fpga_read(struct snd_emu10k1 * emu, int reg, int *value);
+int snd_emu1010_fpga_link_dst_src_write(struct snd_emu10k1 * emu, int dst, int src);
+unsigned int snd_emu10k1_efx_read(struct snd_emu10k1 *emu, unsigned int pc);
+void snd_emu10k1_intr_enable(struct snd_emu10k1 *emu, unsigned int intrenb);
+void snd_emu10k1_intr_disable(struct snd_emu10k1 *emu, unsigned int intrenb);
+void snd_emu10k1_voice_intr_enable(struct snd_emu10k1 *emu, unsigned int voicenum);
+void snd_emu10k1_voice_intr_disable(struct snd_emu10k1 *emu, unsigned int voicenum);
+void snd_emu10k1_voice_intr_ack(struct snd_emu10k1 *emu, unsigned int voicenum);
+void snd_emu10k1_voice_half_loop_intr_enable(struct snd_emu10k1 *emu, unsigned int voicenum);
+void snd_emu10k1_voice_half_loop_intr_disable(struct snd_emu10k1 *emu, unsigned int voicenum);
+void snd_emu10k1_voice_half_loop_intr_ack(struct snd_emu10k1 *emu, unsigned int voicenum);
+void snd_emu10k1_voice_set_loop_stop(struct snd_emu10k1 *emu, unsigned int voicenum);
+void snd_emu10k1_voice_clear_loop_stop(struct snd_emu10k1 *emu, unsigned int voicenum);
+void snd_emu10k1_wait(struct snd_emu10k1 *emu, unsigned int wait);
+static inline unsigned int snd_emu10k1_wc(struct snd_emu10k1 *emu) { return (inl(emu->port + WC) >> 6) & 0xfffff; }
+unsigned short snd_emu10k1_ac97_read(struct snd_ac97 *ac97, unsigned short reg);
+void snd_emu10k1_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short data);
+unsigned int snd_emu10k1_rate_to_pitch(unsigned int rate);
+
+#ifdef CONFIG_PM
+void snd_emu10k1_suspend_regs(struct snd_emu10k1 *emu);
+void snd_emu10k1_resume_init(struct snd_emu10k1 *emu);
+void snd_emu10k1_resume_regs(struct snd_emu10k1 *emu);
+int snd_emu10k1_efx_alloc_pm_buffer(struct snd_emu10k1 *emu);
+void snd_emu10k1_efx_free_pm_buffer(struct snd_emu10k1 *emu);
+void snd_emu10k1_efx_suspend(struct snd_emu10k1 *emu);
+void snd_emu10k1_efx_resume(struct snd_emu10k1 *emu);
+int snd_p16v_alloc_pm_buffer(struct snd_emu10k1 *emu);
+void snd_p16v_free_pm_buffer(struct snd_emu10k1 *emu);
+void snd_p16v_suspend(struct snd_emu10k1 *emu);
+void snd_p16v_resume(struct snd_emu10k1 *emu);
+#endif
+
+/* memory allocation */
+struct snd_util_memblk *snd_emu10k1_alloc_pages(struct snd_emu10k1 *emu, struct snd_pcm_substream *substream);
+int snd_emu10k1_free_pages(struct snd_emu10k1 *emu, struct snd_util_memblk *blk);
+struct snd_util_memblk *snd_emu10k1_synth_alloc(struct snd_emu10k1 *emu, unsigned int size);
+int snd_emu10k1_synth_free(struct snd_emu10k1 *emu, struct snd_util_memblk *blk);
+int snd_emu10k1_synth_bzero(struct snd_emu10k1 *emu, struct snd_util_memblk *blk, int offset, int size);
+int snd_emu10k1_synth_copy_from_user(struct snd_emu10k1 *emu, struct snd_util_memblk *blk, int offset, const char __user *data, int size);
+int snd_emu10k1_memblk_map(struct snd_emu10k1 *emu, struct snd_emu10k1_memblk *blk);
+
+/* voice allocation */
+int snd_emu10k1_voice_alloc(struct snd_emu10k1 *emu, int type, int pair, struct snd_emu10k1_voice **rvoice);
+int snd_emu10k1_voice_free(struct snd_emu10k1 *emu, struct snd_emu10k1_voice *pvoice);
+
+/* MIDI uart */
+int snd_emu10k1_midi(struct snd_emu10k1 * emu);
+int snd_emu10k1_audigy_midi(struct snd_emu10k1 * emu);
+
+/* proc interface */
+int snd_emu10k1_proc_init(struct snd_emu10k1 * emu);
+
+/* fx8010 irq handler */
+int snd_emu10k1_fx8010_register_irq_handler(struct snd_emu10k1 *emu,
+					    snd_fx8010_irq_handler_t *handler,
+					    unsigned char gpr_running,
+					    void *private_data,
+					    struct snd_emu10k1_fx8010_irq **r_irq);
+int snd_emu10k1_fx8010_unregister_irq_handler(struct snd_emu10k1 *emu,
+					      struct snd_emu10k1_fx8010_irq *irq);
+
+#endif /* __KERNEL__ */
+
+/*
+ * ---- FX8010 ----
+ */
+
+#define EMU10K1_CARD_CREATIVE			0x00000000
+#define EMU10K1_CARD_EMUAPS			0x00000001
+
+#define EMU10K1_FX8010_PCM_COUNT		8
+
+/* instruction set */
+#define iMAC0	 0x00	/* R = A + (X * Y >> 31)   ; saturation */
+#define iMAC1	 0x01	/* R = A + (-X * Y >> 31)  ; saturation */
+#define iMAC2	 0x02	/* R = A + (X * Y >> 31)   ; wraparound */
+#define iMAC3	 0x03	/* R = A + (-X * Y >> 31)  ; wraparound */
+#define iMACINT0 0x04	/* R = A + X * Y	   ; saturation */
+#define iMACINT1 0x05	/* R = A + X * Y	   ; wraparound (31-bit) */
+#define iACC3	 0x06	/* R = A + X + Y	   ; saturation */
+#define iMACMV   0x07	/* R = A, acc += X * Y >> 31 */
+#define iANDXOR  0x08	/* R = (A & X) ^ Y */
+#define iTSTNEG  0x09	/* R = (A >= Y) ? X : ~X */
+#define iLIMITGE 0x0a	/* R = (A >= Y) ? X : Y */
+#define iLIMITLT 0x0b	/* R = (A < Y) ? X : Y */
+#define iLOG	 0x0c	/* R = linear_data, A (log_data), X (max_exp), Y (format_word) */
+#define iEXP	 0x0d	/* R = log_data, A (linear_data), X (max_exp), Y (format_word) */
+#define iINTERP  0x0e	/* R = A + (X * (Y - A) >> 31)  ; saturation */
+#define iSKIP    0x0f	/* R = A (cc_reg), X (count), Y (cc_test) */
+
+/* GPRs */
+#define FXBUS(x)	(0x00 + (x))	/* x = 0x00 - 0x0f */
+#define EXTIN(x)	(0x10 + (x))	/* x = 0x00 - 0x0f */
+#define EXTOUT(x)	(0x20 + (x))	/* x = 0x00 - 0x0f physical outs -> FXWC low 16 bits */
+#define FXBUS2(x)	(0x30 + (x))	/* x = 0x00 - 0x0f copies of fx buses for capture -> FXWC high 16 bits */
+					/* NB: 0x31 and 0x32 are shared with Center/LFE on SB live 5.1 */
+
+#define C_00000000	0x40
+#define C_00000001	0x41
+#define C_00000002	0x42
+#define C_00000003	0x43
+#define C_00000004	0x44
+#define C_00000008	0x45
+#define C_00000010	0x46
+#define C_00000020	0x47
+#define C_00000100	0x48
+#define C_00010000	0x49
+#define C_00080000	0x4a
+#define C_10000000	0x4b
+#define C_20000000	0x4c
+#define C_40000000	0x4d
+#define C_80000000	0x4e
+#define C_7fffffff	0x4f
+#define C_ffffffff	0x50
+#define C_fffffffe	0x51
+#define C_c0000000	0x52
+#define C_4f1bbcdc	0x53
+#define C_5a7ef9db	0x54
+#define C_00100000	0x55		/* ?? */
+#define GPR_ACCU	0x56		/* ACCUM, accumulator */
+#define GPR_COND	0x57		/* CCR, condition register */
+#define GPR_NOISE0	0x58		/* noise source */
+#define GPR_NOISE1	0x59		/* noise source */
+#define GPR_IRQ		0x5a		/* IRQ register */
+#define GPR_DBAC	0x5b		/* TRAM Delay Base Address Counter */
+#define GPR(x)		(FXGPREGBASE + (x)) /* free GPRs: x = 0x00 - 0xff */
+#define ITRAM_DATA(x)	(TANKMEMDATAREGBASE + 0x00 + (x)) /* x = 0x00 - 0x7f */
+#define ETRAM_DATA(x)	(TANKMEMDATAREGBASE + 0x80 + (x)) /* x = 0x00 - 0x1f */
+#define ITRAM_ADDR(x)	(TANKMEMADDRREGBASE + 0x00 + (x)) /* x = 0x00 - 0x7f */
+#define ETRAM_ADDR(x)	(TANKMEMADDRREGBASE + 0x80 + (x)) /* x = 0x00 - 0x1f */
+
+#define A_ITRAM_DATA(x)	(TANKMEMDATAREGBASE + 0x00 + (x)) /* x = 0x00 - 0xbf */
+#define A_ETRAM_DATA(x)	(TANKMEMDATAREGBASE + 0xc0 + (x)) /* x = 0x00 - 0x3f */
+#define A_ITRAM_ADDR(x)	(TANKMEMADDRREGBASE + 0x00 + (x)) /* x = 0x00 - 0xbf */
+#define A_ETRAM_ADDR(x)	(TANKMEMADDRREGBASE + 0xc0 + (x)) /* x = 0x00 - 0x3f */
+#define A_ITRAM_CTL(x)	(A_TANKMEMCTLREGBASE + 0x00 + (x)) /* x = 0x00 - 0xbf */
+#define A_ETRAM_CTL(x)	(A_TANKMEMCTLREGBASE + 0xc0 + (x)) /* x = 0x00 - 0x3f */
+
+#define A_FXBUS(x)	(0x00 + (x))	/* x = 0x00 - 0x3f FX buses */
+#define A_EXTIN(x)	(0x40 + (x))	/* x = 0x00 - 0x0f physical ins */
+#define A_P16VIN(x)	(0x50 + (x))	/* x = 0x00 - 0x0f p16v ins (A2 only) "EMU32 inputs" */
+#define A_EXTOUT(x)	(0x60 + (x))	/* x = 0x00 - 0x1f physical outs -> A_FXWC1 0x79-7f unknown   */
+#define A_FXBUS2(x)	(0x80 + (x))	/* x = 0x00 - 0x1f extra outs used for EFX capture -> A_FXWC2 */
+#define A_EMU32OUTH(x)	(0xa0 + (x))	/* x = 0x00 - 0x0f "EMU32_OUT_10 - _1F" - ??? */
+#define A_EMU32OUTL(x)	(0xb0 + (x))	/* x = 0x00 - 0x0f "EMU32_OUT_1 - _F" - ??? */
+#define A_GPR(x)	(A_FXGPREGBASE + (x))
+
+/* cc_reg constants */
+#define CC_REG_NORMALIZED C_00000001
+#define CC_REG_BORROW	C_00000002
+#define CC_REG_MINUS	C_00000004
+#define CC_REG_ZERO	C_00000008
+#define CC_REG_SATURATE	C_00000010
+#define CC_REG_NONZERO	C_00000100
+
+/* FX buses */
+#define FXBUS_PCM_LEFT		0x00
+#define FXBUS_PCM_RIGHT		0x01
+#define FXBUS_PCM_LEFT_REAR	0x02
+#define FXBUS_PCM_RIGHT_REAR	0x03
+#define FXBUS_MIDI_LEFT		0x04
+#define FXBUS_MIDI_RIGHT	0x05
+#define FXBUS_PCM_CENTER	0x06
+#define FXBUS_PCM_LFE		0x07
+#define FXBUS_PCM_LEFT_FRONT	0x08
+#define FXBUS_PCM_RIGHT_FRONT	0x09
+#define FXBUS_MIDI_REVERB	0x0c
+#define FXBUS_MIDI_CHORUS	0x0d
+#define FXBUS_PCM_LEFT_SIDE	0x0e
+#define FXBUS_PCM_RIGHT_SIDE	0x0f
+#define FXBUS_PT_LEFT		0x14
+#define FXBUS_PT_RIGHT		0x15
+
+/* Inputs */
+#define EXTIN_AC97_L	   0x00	/* AC'97 capture channel - left */
+#define EXTIN_AC97_R	   0x01	/* AC'97 capture channel - right */
+#define EXTIN_SPDIF_CD_L   0x02	/* internal S/PDIF CD - onboard - left */
+#define EXTIN_SPDIF_CD_R   0x03	/* internal S/PDIF CD - onboard - right */
+#define EXTIN_ZOOM_L	   0x04	/* Zoom Video I2S - left */
+#define EXTIN_ZOOM_R	   0x05	/* Zoom Video I2S - right */
+#define EXTIN_TOSLINK_L	   0x06	/* LiveDrive - TOSLink Optical - left */
+#define EXTIN_TOSLINK_R    0x07	/* LiveDrive - TOSLink Optical - right */
+#define EXTIN_LINE1_L	   0x08	/* LiveDrive - Line/Mic 1 - left */
+#define EXTIN_LINE1_R	   0x09	/* LiveDrive - Line/Mic 1 - right */
+#define EXTIN_COAX_SPDIF_L 0x0a	/* LiveDrive - Coaxial S/PDIF - left */
+#define EXTIN_COAX_SPDIF_R 0x0b /* LiveDrive - Coaxial S/PDIF - right */
+#define EXTIN_LINE2_L	   0x0c	/* LiveDrive - Line/Mic 2 - left */
+#define EXTIN_LINE2_R	   0x0d	/* LiveDrive - Line/Mic 2 - right */
+
+/* Outputs */
+#define EXTOUT_AC97_L	   0x00	/* AC'97 playback channel - left */
+#define EXTOUT_AC97_R	   0x01	/* AC'97 playback channel - right */
+#define EXTOUT_TOSLINK_L   0x02	/* LiveDrive - TOSLink Optical - left */
+#define EXTOUT_TOSLINK_R   0x03	/* LiveDrive - TOSLink Optical - right */
+#define EXTOUT_AC97_CENTER 0x04	/* SB Live 5.1 - center */
+#define EXTOUT_AC97_LFE	   0x05 /* SB Live 5.1 - LFE */
+#define EXTOUT_HEADPHONE_L 0x06	/* LiveDrive - Headphone - left */
+#define EXTOUT_HEADPHONE_R 0x07	/* LiveDrive - Headphone - right */
+#define EXTOUT_REAR_L	   0x08	/* Rear channel - left */
+#define EXTOUT_REAR_R	   0x09	/* Rear channel - right */
+#define EXTOUT_ADC_CAP_L   0x0a	/* ADC Capture buffer - left */
+#define EXTOUT_ADC_CAP_R   0x0b	/* ADC Capture buffer - right */
+#define EXTOUT_MIC_CAP	   0x0c	/* MIC Capture buffer */
+#define EXTOUT_AC97_REAR_L 0x0d	/* SB Live 5.1 (c) 2003 - Rear Left */
+#define EXTOUT_AC97_REAR_R 0x0e	/* SB Live 5.1 (c) 2003 - Rear Right */
+#define EXTOUT_ACENTER	   0x11 /* Analog Center */
+#define EXTOUT_ALFE	   0x12 /* Analog LFE */
+
+/* Audigy Inputs */
+#define A_EXTIN_AC97_L		0x00	/* AC'97 capture channel - left */
+#define A_EXTIN_AC97_R		0x01	/* AC'97 capture channel - right */
+#define A_EXTIN_SPDIF_CD_L	0x02	/* digital CD left */
+#define A_EXTIN_SPDIF_CD_R	0x03	/* digital CD left */
+#define A_EXTIN_OPT_SPDIF_L     0x04    /* audigy drive Optical SPDIF - left */
+#define A_EXTIN_OPT_SPDIF_R     0x05    /*                              right */ 
+#define A_EXTIN_LINE2_L		0x08	/* audigy drive line2/mic2 - left */
+#define A_EXTIN_LINE2_R		0x09	/*                           right */
+#define A_EXTIN_ADC_L		0x0a    /* Philips ADC - left */
+#define A_EXTIN_ADC_R		0x0b    /*               right */
+#define A_EXTIN_AUX2_L		0x0c	/* audigy drive aux2 - left */
+#define A_EXTIN_AUX2_R		0x0d	/*                   - right */
+
+/* Audigiy Outputs */
+#define A_EXTOUT_FRONT_L	0x00	/* digital front left */
+#define A_EXTOUT_FRONT_R	0x01	/*               right */
+#define A_EXTOUT_CENTER		0x02	/* digital front center */
+#define A_EXTOUT_LFE		0x03	/* digital front lfe */
+#define A_EXTOUT_HEADPHONE_L	0x04	/* headphone audigy drive left */
+#define A_EXTOUT_HEADPHONE_R	0x05	/*                        right */
+#define A_EXTOUT_REAR_L		0x06	/* digital rear left */
+#define A_EXTOUT_REAR_R		0x07	/*              right */
+#define A_EXTOUT_AFRONT_L	0x08	/* analog front left */
+#define A_EXTOUT_AFRONT_R	0x09	/*              right */
+#define A_EXTOUT_ACENTER	0x0a	/* analog center */
+#define A_EXTOUT_ALFE		0x0b	/* analog LFE */
+#define A_EXTOUT_ASIDE_L	0x0c	/* analog side left  - Audigy 2 ZS */
+#define A_EXTOUT_ASIDE_R	0x0d	/*             right - Audigy 2 ZS */
+#define A_EXTOUT_AREAR_L	0x0e	/* analog rear left */
+#define A_EXTOUT_AREAR_R	0x0f	/*             right */
+#define A_EXTOUT_AC97_L		0x10	/* AC97 left (front) */
+#define A_EXTOUT_AC97_R		0x11	/*      right */
+#define A_EXTOUT_ADC_CAP_L	0x16	/* ADC capture buffer left */
+#define A_EXTOUT_ADC_CAP_R	0x17	/*                    right */
+#define A_EXTOUT_MIC_CAP	0x18	/* Mic capture buffer */
+
+/* Audigy constants */
+#define A_C_00000000	0xc0
+#define A_C_00000001	0xc1
+#define A_C_00000002	0xc2
+#define A_C_00000003	0xc3
+#define A_C_00000004	0xc4
+#define A_C_00000008	0xc5
+#define A_C_00000010	0xc6
+#define A_C_00000020	0xc7
+#define A_C_00000100	0xc8
+#define A_C_00010000	0xc9
+#define A_C_00000800	0xca
+#define A_C_10000000	0xcb
+#define A_C_20000000	0xcc
+#define A_C_40000000	0xcd
+#define A_C_80000000	0xce
+#define A_C_7fffffff	0xcf
+#define A_C_ffffffff	0xd0
+#define A_C_fffffffe	0xd1
+#define A_C_c0000000	0xd2
+#define A_C_4f1bbcdc	0xd3
+#define A_C_5a7ef9db	0xd4
+#define A_C_00100000	0xd5
+#define A_GPR_ACCU	0xd6		/* ACCUM, accumulator */
+#define A_GPR_COND	0xd7		/* CCR, condition register */
+#define A_GPR_NOISE0	0xd8		/* noise source */
+#define A_GPR_NOISE1	0xd9		/* noise source */
+#define A_GPR_IRQ	0xda		/* IRQ register */
+#define A_GPR_DBAC	0xdb		/* TRAM Delay Base Address Counter - internal */
+#define A_GPR_DBACE	0xde		/* TRAM Delay Base Address Counter - external */
+
+/* definitions for debug register */
+#define EMU10K1_DBG_ZC			0x80000000	/* zero tram counter */
+#define EMU10K1_DBG_SATURATION_OCCURED	0x02000000	/* saturation control */
+#define EMU10K1_DBG_SATURATION_ADDR	0x01ff0000	/* saturation address */
+#define EMU10K1_DBG_SINGLE_STEP		0x00008000	/* single step mode */
+#define EMU10K1_DBG_STEP		0x00004000	/* start single step */
+#define EMU10K1_DBG_CONDITION_CODE	0x00003e00	/* condition code */
+#define EMU10K1_DBG_SINGLE_STEP_ADDR	0x000001ff	/* single step address */
+
+/* tank memory address line */
+#ifndef __KERNEL__
+#define TANKMEMADDRREG_ADDR_MASK 0x000fffff	/* 20 bit tank address field			*/
+#define TANKMEMADDRREG_CLEAR	 0x00800000	/* Clear tank memory				*/
+#define TANKMEMADDRREG_ALIGN	 0x00400000	/* Align read or write relative to tank access	*/
+#define TANKMEMADDRREG_WRITE	 0x00200000	/* Write to tank memory				*/
+#define TANKMEMADDRREG_READ	 0x00100000	/* Read from tank memory			*/
+#endif
+
+struct snd_emu10k1_fx8010_info {
+	unsigned int internal_tram_size;	/* in samples */
+	unsigned int external_tram_size;	/* in samples */
+	char fxbus_names[16][32];		/* names of FXBUSes */
+	char extin_names[16][32];		/* names of external inputs */
+	char extout_names[32][32];		/* names of external outputs */
+	unsigned int gpr_controls;		/* count of GPR controls */
+};
+
+#define EMU10K1_GPR_TRANSLATION_NONE		0
+#define EMU10K1_GPR_TRANSLATION_TABLE100	1
+#define EMU10K1_GPR_TRANSLATION_BASS		2
+#define EMU10K1_GPR_TRANSLATION_TREBLE		3
+#define EMU10K1_GPR_TRANSLATION_ONOFF		4
+
+struct snd_emu10k1_fx8010_control_gpr {
+	struct snd_ctl_elem_id id;		/* full control ID definition */
+	unsigned int vcount;		/* visible count */
+	unsigned int count;		/* count of GPR (1..16) */
+	unsigned short gpr[32];		/* GPR number(s) */
+	unsigned int value[32];		/* initial values */
+	unsigned int min;		/* minimum range */
+	unsigned int max;		/* maximum range */
+	union {
+		snd_kcontrol_tlv_rw_t *c;
+		unsigned int *p;
+	} tlv;
+	unsigned int translation;	/* translation type (EMU10K1_GPR_TRANSLATION*) */
+};
+
+struct snd_emu10k1_fx8010_code {
+	char name[128];
+
+	DECLARE_BITMAP(gpr_valid, 0x200); /* bitmask of valid initializers */
+	u_int32_t __user *gpr_map;	  /* initializers */
+
+	unsigned int gpr_add_control_count; /* count of GPR controls to add/replace */
+	struct snd_emu10k1_fx8010_control_gpr __user *gpr_add_controls; /* GPR controls to add/replace */
+
+	unsigned int gpr_del_control_count; /* count of GPR controls to remove */
+	struct snd_ctl_elem_id __user *gpr_del_controls; /* IDs of GPR controls to remove */
+
+	unsigned int gpr_list_control_count; /* count of GPR controls to list */
+	unsigned int gpr_list_control_total; /* total count of GPR controls */
+	struct snd_emu10k1_fx8010_control_gpr __user *gpr_list_controls; /* listed GPR controls */
+
+	DECLARE_BITMAP(tram_valid, 0x100); /* bitmask of valid initializers */
+	u_int32_t __user *tram_data_map;  /* data initializers */
+	u_int32_t __user *tram_addr_map;  /* map initializers */
+
+	DECLARE_BITMAP(code_valid, 1024); /* bitmask of valid instructions */
+	u_int32_t __user *code;		  /* one instruction - 64 bits */
+};
+
+struct snd_emu10k1_fx8010_tram {
+	unsigned int address;		/* 31.bit == 1 -> external TRAM */
+	unsigned int size;		/* size in samples (4 bytes) */
+	unsigned int *samples;		/* pointer to samples (20-bit) */
+					/* NULL->clear memory */
+};
+
+struct snd_emu10k1_fx8010_pcm_rec {
+	unsigned int substream;		/* substream number */
+	unsigned int res1;		/* reserved */
+	unsigned int channels;		/* 16-bit channels count, zero = remove this substream */
+	unsigned int tram_start;	/* ring buffer position in TRAM (in samples) */
+	unsigned int buffer_size;	/* count of buffered samples */
+	unsigned short gpr_size;		/* GPR containing size of ringbuffer in samples (host) */
+	unsigned short gpr_ptr;		/* GPR containing current pointer in the ring buffer (host = reset, FX8010) */
+	unsigned short gpr_count;	/* GPR containing count of samples between two interrupts (host) */
+	unsigned short gpr_tmpcount;	/* GPR containing current count of samples to interrupt (host = set, FX8010) */
+	unsigned short gpr_trigger;	/* GPR containing trigger (activate) information (host) */
+	unsigned short gpr_running;	/* GPR containing info if PCM is running (FX8010) */
+	unsigned char pad;		/* reserved */
+	unsigned char etram[32];	/* external TRAM address & data (one per channel) */
+	unsigned int res2;		/* reserved */
+};
+
+#define SNDRV_EMU10K1_IOCTL_INFO	_IOR ('H', 0x10, struct snd_emu10k1_fx8010_info)
+#define SNDRV_EMU10K1_IOCTL_CODE_POKE	_IOW ('H', 0x11, struct snd_emu10k1_fx8010_code)
+#define SNDRV_EMU10K1_IOCTL_CODE_PEEK	_IOWR('H', 0x12, struct snd_emu10k1_fx8010_code)
+#define SNDRV_EMU10K1_IOCTL_TRAM_SETUP	_IOW ('H', 0x20, int)
+#define SNDRV_EMU10K1_IOCTL_TRAM_POKE	_IOW ('H', 0x21, struct snd_emu10k1_fx8010_tram)
+#define SNDRV_EMU10K1_IOCTL_TRAM_PEEK	_IOWR('H', 0x22, struct snd_emu10k1_fx8010_tram)
+#define SNDRV_EMU10K1_IOCTL_PCM_POKE	_IOW ('H', 0x30, struct snd_emu10k1_fx8010_pcm_rec)
+#define SNDRV_EMU10K1_IOCTL_PCM_PEEK	_IOWR('H', 0x31, struct snd_emu10k1_fx8010_pcm_rec)
+#define SNDRV_EMU10K1_IOCTL_STOP	_IO  ('H', 0x80)
+#define SNDRV_EMU10K1_IOCTL_CONTINUE	_IO  ('H', 0x81)
+#define SNDRV_EMU10K1_IOCTL_ZERO_TRAM_COUNTER _IO ('H', 0x82)
+#define SNDRV_EMU10K1_IOCTL_SINGLE_STEP	_IOW ('H', 0x83, int)
+#define SNDRV_EMU10K1_IOCTL_DBG_READ	_IOR ('H', 0x84, int)
+
+/* typedefs for compatibility to user-space */
+typedef struct snd_emu10k1_fx8010_info emu10k1_fx8010_info_t;
+typedef struct snd_emu10k1_fx8010_control_gpr emu10k1_fx8010_control_gpr_t;
+typedef struct snd_emu10k1_fx8010_code emu10k1_fx8010_code_t;
+typedef struct snd_emu10k1_fx8010_tram emu10k1_fx8010_tram_t;
+typedef struct snd_emu10k1_fx8010_pcm_rec emu10k1_fx8010_pcm_t;
+
+#endif	/* __SOUND_EMU10K1_H */
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/emu10k1_synth.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/emu10k1_synth.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/emu10k1_synth.h	(revision 91)
@@ -0,0 +1,39 @@
+#ifndef __EMU10K1_SYNTH_H
+#define __EMU10K1_SYNTH_H
+/*
+ *  Defines for the Emu10k1 WaveTable synth
+ *
+ *  Copyright (C) 2000 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 "emu10k1.h"
+#include "emux_synth.h"
+
+/* sequencer device id */
+#define SNDRV_SEQ_DEV_ID_EMU10K1_SYNTH	"emu10k1-synth"
+
+/* argument for snd_seq_device_new */
+struct snd_emu10k1_synth_arg {
+	struct snd_emu10k1 *hwptr;	/* chip */
+	int index;		/* sequencer client index */
+	int seq_ports;		/* number of sequencer ports to be created */
+	int max_voices;		/* maximum number of voices for wavetable */
+};
+
+#define EMU10K1_MAX_MEMSIZE	(32 * 1024 * 1024) /* 32MB */
+
+#endif
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/emu8000.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/emu8000.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/emu8000.h	(revision 91)
@@ -0,0 +1,121 @@
+#ifndef __EMU8000_H
+#define __EMU8000_H
+/*
+ *  Defines for the emu8000 (AWE32/64)
+ *
+ *  Copyright (C) 1999 Steve Ratcliffe
+ *  Copyright (C) 1999-2000 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "driver.h"
+#include "emux_synth.h"
+#include "seq_kernel.h"
+
+/*
+ * Hardware parameters.
+ */
+#define EMU8000_MAX_DRAM (28 * 1024 * 1024) /* Max on-board mem is 28Mb ???*/
+#define EMU8000_DRAM_OFFSET 0x200000	/* Beginning of on board ram */
+#define EMU8000_CHANNELS   32	/* Number of hardware channels */
+#define EMU8000_DRAM_VOICES	30	/* number of normal voices */
+
+/* Flags to set a dma channel to read or write */
+#define EMU8000_RAM_READ   0
+#define EMU8000_RAM_WRITE  1
+#define EMU8000_RAM_CLOSE  2
+#define EMU8000_RAM_MODE_MASK	0x03
+#define EMU8000_RAM_RIGHT	0x10	/* use 'right' DMA channel */
+
+enum {
+	EMU8000_CONTROL_BASS = 0,
+	EMU8000_CONTROL_TREBLE,
+	EMU8000_CONTROL_CHORUS_MODE,
+	EMU8000_CONTROL_REVERB_MODE,
+	EMU8000_CONTROL_FM_CHORUS_DEPTH,
+	EMU8000_CONTROL_FM_REVERB_DEPTH,
+	EMU8000_NUM_CONTROLS,
+};
+
+/*
+ * Structure to hold all state information for the emu8000 driver.
+ *
+ * Note 1: The chip supports 32 channels in hardware this is max_channels
+ * some of the channels may be used for other things so max_channels is
+ * the number in use for wave voices.
+ */
+typedef struct snd_emu8000 {
+
+	snd_emux_t *emu;
+
+	int index;		/* sequencer client index */
+	int seq_ports;		/* number of sequencer ports */
+	int fm_chorus_depth;	/* FM OPL3 chorus depth */
+	int fm_reverb_depth;	/* FM OPL3 reverb depth */
+
+	int mem_size;		/* memory size */
+	unsigned long port1;	/* Port usually base+0 */
+	unsigned long port2;	/* Port usually at base+0x400 */
+	unsigned long port3;	/* Port usually at base+0x800 */
+	struct resource *res_port1;
+	struct resource *res_port2;
+	struct resource *res_port3;
+	unsigned short last_reg;/* Last register command */
+	spinlock_t reg_lock;
+
+	int dram_checked;
+
+	snd_card_t *card;		/* The card that this belongs to */
+
+	int chorus_mode;
+	int reverb_mode;
+	int bass_level;
+	int treble_level;
+
+	snd_util_memhdr_t *memhdr;
+
+	spinlock_t control_lock;
+	snd_kcontrol_t *controls[EMU8000_NUM_CONTROLS];
+	snd_pcm_t *pcm; /* pcm on emu8000 wavetable */
+
+} emu8000_t;
+
+/* sequencer device id */
+#define SNDRV_SEQ_DEV_ID_EMU8000	"emu8000-synth"
+
+
+/* exported functions */
+int snd_emu8000_new(snd_card_t *card, int device, long port, int seq_ports, snd_seq_device_t **ret);
+void snd_emu8000_poke(emu8000_t *emu, unsigned int port, unsigned int reg,
+		      unsigned int val);
+unsigned short snd_emu8000_peek(emu8000_t *emu, unsigned int port,
+				unsigned int reg);
+void snd_emu8000_poke_dw(emu8000_t *emu, unsigned int port, unsigned int reg,
+			 unsigned int val);
+unsigned int snd_emu8000_peek_dw(emu8000_t *emu, unsigned int port,
+				 unsigned int reg);
+void snd_emu8000_dma_chan(emu8000_t *emu, int ch, int mode);
+
+void snd_emu8000_init_fm(emu8000_t *emu);
+
+void snd_emu8000_update_chorus_mode(emu8000_t *emu);
+void snd_emu8000_update_reverb_mode(emu8000_t *emu);
+void snd_emu8000_update_equalizer(emu8000_t *emu);
+int snd_emu8000_load_chorus_fx(emu8000_t *emu, int mode, const void *buf, long len);
+int snd_emu8000_load_reverb_fx(emu8000_t *emu, int mode, const void *buf, long len);
+
+
+#endif
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/emu8000_reg.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/emu8000_reg.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/emu8000_reg.h	(revision 91)
@@ -0,0 +1,207 @@
+#ifndef __EMU8000_REG_H
+#define __EMU8000_REG_H
+/*
+ *  Register operations for the EMU8000
+ *
+ *  Copyright (C) 1999 Steve Ratcliffe
+ *
+ *  Based on awe_wave.c by Takashi Iwai
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/*
+ * Data port addresses relative to the EMU base.
+ */
+#define EMU8000_DATA0(e)    ((e)->port1)
+#define EMU8000_DATA1(e)    ((e)->port2)
+#define EMU8000_DATA2(e)    ((e)->port2+2)
+#define EMU8000_DATA3(e)    ((e)->port3)
+#define EMU8000_PTR(e)      ((e)->port3+2)
+
+/*
+ * Make a command from a register and channel.
+ */
+#define EMU8000_CMD(reg, chan) ((reg)<<5 | (chan))
+
+/*
+ * Commands to read and write the EMU8000 registers.
+ * These macros should be used for all register accesses.
+ */
+#define EMU8000_CPF_READ(emu, chan) \
+	snd_emu8000_peek_dw((emu), EMU8000_DATA0(emu), EMU8000_CMD(0, (chan)))
+#define EMU8000_PTRX_READ(emu, chan) \
+	snd_emu8000_peek_dw((emu), EMU8000_DATA0(emu), EMU8000_CMD(1, (chan)))
+#define EMU8000_CVCF_READ(emu, chan) \
+	snd_emu8000_peek_dw((emu), EMU8000_DATA0(emu), EMU8000_CMD(2, (chan)))
+#define EMU8000_VTFT_READ(emu, chan) \
+	snd_emu8000_peek_dw((emu), EMU8000_DATA0(emu), EMU8000_CMD(3, (chan)))
+#define EMU8000_PSST_READ(emu, chan) \
+	snd_emu8000_peek_dw((emu), EMU8000_DATA0(emu), EMU8000_CMD(6, (chan)))
+#define EMU8000_CSL_READ(emu, chan) \
+	snd_emu8000_peek_dw((emu), EMU8000_DATA0(emu), EMU8000_CMD(7, (chan)))
+#define EMU8000_CCCA_READ(emu, chan) \
+	snd_emu8000_peek_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(0, (chan)))
+#define EMU8000_HWCF4_READ(emu) \
+	snd_emu8000_peek_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 9))
+#define EMU8000_HWCF5_READ(emu) \
+	snd_emu8000_peek_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 10))
+#define EMU8000_HWCF6_READ(emu) \
+	snd_emu8000_peek_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 13))
+#define EMU8000_SMALR_READ(emu) \
+	snd_emu8000_peek_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 20))
+#define EMU8000_SMARR_READ(emu) \
+	snd_emu8000_peek_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 21))
+#define EMU8000_SMALW_READ(emu) \
+	snd_emu8000_peek_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 22))
+#define EMU8000_SMARW_READ(emu) \
+	snd_emu8000_peek_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 23))
+#define EMU8000_SMLD_READ(emu) \
+	snd_emu8000_peek((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 26))
+#define EMU8000_SMRD_READ(emu) \
+	snd_emu8000_peek((emu), EMU8000_DATA2(emu), EMU8000_CMD(1, 26))
+#define EMU8000_WC_READ(emu) \
+	snd_emu8000_peek((emu), EMU8000_DATA2(emu), EMU8000_CMD(1, 27))
+#define EMU8000_HWCF1_READ(emu) \
+	snd_emu8000_peek((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 29))
+#define EMU8000_HWCF2_READ(emu) \
+	snd_emu8000_peek((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 30))
+#define EMU8000_HWCF3_READ(emu) \
+	snd_emu8000_peek((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 31))
+#define EMU8000_INIT1_READ(emu, chan) \
+	snd_emu8000_peek((emu), EMU8000_DATA1(emu), EMU8000_CMD(2, (chan)))
+#define EMU8000_INIT2_READ(emu, chan) \
+	snd_emu8000_peek((emu), EMU8000_DATA2(emu), EMU8000_CMD(2, (chan)))
+#define EMU8000_INIT3_READ(emu, chan) \
+	snd_emu8000_peek((emu), EMU8000_DATA1(emu), EMU8000_CMD(3, (chan)))
+#define EMU8000_INIT4_READ(emu, chan) \
+	snd_emu8000_peek((emu), EMU8000_DATA2(emu), EMU8000_CMD(3, (chan)))
+#define EMU8000_ENVVOL_READ(emu, chan) \
+	snd_emu8000_peek((emu), EMU8000_DATA1(emu), EMU8000_CMD(4, (chan)))
+#define EMU8000_DCYSUSV_READ(emu, chan) \
+	snd_emu8000_peek((emu), EMU8000_DATA1(emu), EMU8000_CMD(5, (chan)))
+#define EMU8000_ENVVAL_READ(emu, chan) \
+	snd_emu8000_peek((emu), EMU8000_DATA1(emu), EMU8000_CMD(6, (chan)))
+#define EMU8000_DCYSUS_READ(emu, chan) \
+	snd_emu8000_peek((emu), EMU8000_DATA1(emu), EMU8000_CMD(7, (chan)))
+#define EMU8000_ATKHLDV_READ(emu, chan) \
+	snd_emu8000_peek((emu), EMU8000_DATA2(emu), EMU8000_CMD(4, (chan)))
+#define EMU8000_LFO1VAL_READ(emu, chan) \
+	snd_emu8000_peek((emu), EMU8000_DATA2(emu), EMU8000_CMD(5, (chan)))
+#define EMU8000_ATKHLD_READ(emu, chan) \
+	snd_emu8000_peek((emu), EMU8000_DATA2(emu), EMU8000_CMD(6, (chan)))
+#define EMU8000_LFO2VAL_READ(emu, chan) \
+	snd_emu8000_peek((emu), EMU8000_DATA2(emu), EMU8000_CMD(7, (chan)))
+#define EMU8000_IP_READ(emu, chan) \
+	snd_emu8000_peek((emu), EMU8000_DATA3(emu), EMU8000_CMD(0, (chan)))
+#define EMU8000_IFATN_READ(emu, chan) \
+	snd_emu8000_peek((emu), EMU8000_DATA3(emu), EMU8000_CMD(1, (chan)))
+#define EMU8000_PEFE_READ(emu, chan) \
+	snd_emu8000_peek((emu), EMU8000_DATA3(emu), EMU8000_CMD(2, (chan)))
+#define EMU8000_FMMOD_READ(emu, chan) \
+	snd_emu8000_peek((emu), EMU8000_DATA3(emu), EMU8000_CMD(3, (chan)))
+#define EMU8000_TREMFRQ_READ(emu, chan) \
+	snd_emu8000_peek((emu), EMU8000_DATA3(emu), EMU8000_CMD(4, (chan)))
+#define EMU8000_FM2FRQ2_READ(emu, chan) \
+	snd_emu8000_peek((emu), EMU8000_DATA3(emu), EMU8000_CMD(5, (chan)))
+
+
+#define EMU8000_CPF_WRITE(emu, chan, val) \
+	snd_emu8000_poke_dw((emu), EMU8000_DATA0(emu), EMU8000_CMD(0, (chan)), (val))
+#define EMU8000_PTRX_WRITE(emu, chan, val) \
+	snd_emu8000_poke_dw((emu), EMU8000_DATA0(emu), EMU8000_CMD(1, (chan)), (val))
+#define EMU8000_CVCF_WRITE(emu, chan, val) \
+	snd_emu8000_poke_dw((emu), EMU8000_DATA0(emu), EMU8000_CMD(2, (chan)), (val))
+#define EMU8000_VTFT_WRITE(emu, chan, val) \
+	snd_emu8000_poke_dw((emu), EMU8000_DATA0(emu), EMU8000_CMD(3, (chan)), (val))
+#define EMU8000_PSST_WRITE(emu, chan, val) \
+	snd_emu8000_poke_dw((emu), EMU8000_DATA0(emu), EMU8000_CMD(6, (chan)), (val))
+#define EMU8000_CSL_WRITE(emu, chan, val) \
+	snd_emu8000_poke_dw((emu), EMU8000_DATA0(emu), EMU8000_CMD(7, (chan)), (val))
+#define EMU8000_CCCA_WRITE(emu, chan, val) \
+	snd_emu8000_poke_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(0, (chan)), (val))
+#define EMU8000_HWCF4_WRITE(emu, val) \
+	snd_emu8000_poke_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 9), (val))
+#define EMU8000_HWCF5_WRITE(emu, val) \
+	snd_emu8000_poke_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 10), (val))
+#define EMU8000_HWCF6_WRITE(emu, val) \
+	snd_emu8000_poke_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 13), (val))
+/* this register is not documented */
+#define EMU8000_HWCF7_WRITE(emu, val) \
+	snd_emu8000_poke_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 14), (val))
+#define EMU8000_SMALR_WRITE(emu, val) \
+	snd_emu8000_poke_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 20), (val))
+#define EMU8000_SMARR_WRITE(emu, val) \
+	snd_emu8000_poke_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 21), (val))
+#define EMU8000_SMALW_WRITE(emu, val) \
+	snd_emu8000_poke_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 22), (val))
+#define EMU8000_SMARW_WRITE(emu, val) \
+	snd_emu8000_poke_dw((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 23), (val))
+#define EMU8000_SMLD_WRITE(emu, val) \
+	snd_emu8000_poke((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 26), (val))
+#define EMU8000_SMRD_WRITE(emu, val) \
+	snd_emu8000_poke((emu), EMU8000_DATA2(emu), EMU8000_CMD(1, 26), (val))
+#define EMU8000_WC_WRITE(emu, val) \
+	snd_emu8000_poke((emu), EMU8000_DATA2(emu), EMU8000_CMD(1, 27), (val))
+#define EMU8000_HWCF1_WRITE(emu, val) \
+	snd_emu8000_poke((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 29), (val))
+#define EMU8000_HWCF2_WRITE(emu, val) \
+	snd_emu8000_poke((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 30), (val))
+#define EMU8000_HWCF3_WRITE(emu, val) \
+	snd_emu8000_poke((emu), EMU8000_DATA1(emu), EMU8000_CMD(1, 31), (val))
+#define EMU8000_INIT1_WRITE(emu, chan, val) \
+	snd_emu8000_poke((emu), EMU8000_DATA1(emu), EMU8000_CMD(2, (chan)), (val))
+#define EMU8000_INIT2_WRITE(emu, chan, val) \
+	snd_emu8000_poke((emu), EMU8000_DATA2(emu), EMU8000_CMD(2, (chan)), (val))
+#define EMU8000_INIT3_WRITE(emu, chan, val) \
+	snd_emu8000_poke((emu), EMU8000_DATA1(emu), EMU8000_CMD(3, (chan)), (val))
+#define EMU8000_INIT4_WRITE(emu, chan, val) \
+	snd_emu8000_poke((emu), EMU8000_DATA2(emu), EMU8000_CMD(3, (chan)), (val))
+#define EMU8000_ENVVOL_WRITE(emu, chan, val) \
+	snd_emu8000_poke((emu), EMU8000_DATA1(emu), EMU8000_CMD(4, (chan)), (val))
+#define EMU8000_DCYSUSV_WRITE(emu, chan, val) \
+	snd_emu8000_poke((emu), EMU8000_DATA1(emu), EMU8000_CMD(5, (chan)), (val))
+#define EMU8000_ENVVAL_WRITE(emu, chan, val) \
+	snd_emu8000_poke((emu), EMU8000_DATA1(emu), EMU8000_CMD(6, (chan)), (val))
+#define EMU8000_DCYSUS_WRITE(emu, chan, val) \
+	snd_emu8000_poke((emu), EMU8000_DATA1(emu), EMU8000_CMD(7, (chan)), (val))
+#define EMU8000_ATKHLDV_WRITE(emu, chan, val) \
+	snd_emu8000_poke((emu), EMU8000_DATA2(emu), EMU8000_CMD(4, (chan)), (val))
+#define EMU8000_LFO1VAL_WRITE(emu, chan, val) \
+	snd_emu8000_poke((emu), EMU8000_DATA2(emu), EMU8000_CMD(5, (chan)), (val))
+#define EMU8000_ATKHLD_WRITE(emu, chan, val) \
+	snd_emu8000_poke((emu), EMU8000_DATA2(emu), EMU8000_CMD(6, (chan)), (val))
+#define EMU8000_LFO2VAL_WRITE(emu, chan, val) \
+	snd_emu8000_poke((emu), EMU8000_DATA2(emu), EMU8000_CMD(7, (chan)), (val))
+#define EMU8000_IP_WRITE(emu, chan, val) \
+	snd_emu8000_poke((emu), EMU8000_DATA3(emu), EMU8000_CMD(0, (chan)), (val))
+#define EMU8000_IFATN_WRITE(emu, chan, val) \
+	snd_emu8000_poke((emu), EMU8000_DATA3(emu), EMU8000_CMD(1, (chan)), (val))
+#define EMU8000_PEFE_WRITE(emu, chan, val) \
+	snd_emu8000_poke((emu), EMU8000_DATA3(emu), EMU8000_CMD(2, (chan)), (val))
+#define EMU8000_FMMOD_WRITE(emu, chan, val) \
+	snd_emu8000_poke((emu), EMU8000_DATA3(emu), EMU8000_CMD(3, (chan)), (val))
+#define EMU8000_TREMFRQ_WRITE(emu, chan, val) \
+	snd_emu8000_poke((emu), EMU8000_DATA3(emu), EMU8000_CMD(4, (chan)), (val))
+#define EMU8000_FM2FRQ2_WRITE(emu, chan, val) \
+	snd_emu8000_poke((emu), EMU8000_DATA3(emu), EMU8000_CMD(5, (chan)), (val))
+
+#define EMU8000_0080_WRITE(emu, chan, val) \
+	snd_emu8000_poke_dw((emu), EMU8000_DATA0(emu), EMU8000_CMD(4, (chan)), (val))
+#define EMU8000_00A0_WRITE(emu, chan, val) \
+	snd_emu8000_poke_dw((emu), EMU8000_DATA0(emu), EMU8000_CMD(5, (chan)), (val))
+
+#endif
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/emux_legacy.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/emux_legacy.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/emux_legacy.h	(revision 91)
@@ -0,0 +1,146 @@
+/*
+ *  Copyright (c) 1999-2000 Takashi Iwai <tiwai@suse.de>
+ *
+ *  Definitions of OSS compatible headers for Emu8000 device informations
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef __EMUX_LEGACY_H
+#define __EMUX_LEGACY_H
+
+#include "seq_oss_legacy.h"
+
+/*
+ * awe hardware controls
+ */
+
+#define _EMUX_OSS_DEBUG_MODE		0x00
+#define _EMUX_OSS_REVERB_MODE		0x01
+#define _EMUX_OSS_CHORUS_MODE		0x02
+#define _EMUX_OSS_REMOVE_LAST_SAMPLES	0x03
+#define _EMUX_OSS_INITIALIZE_CHIP	0x04
+#define _EMUX_OSS_SEND_EFFECT		0x05
+#define _EMUX_OSS_TERMINATE_CHANNEL	0x06
+#define _EMUX_OSS_TERMINATE_ALL		0x07
+#define _EMUX_OSS_INITIAL_VOLUME	0x08
+#define _EMUX_OSS_INITIAL_ATTEN	_EMUX_OSS_INITIAL_VOLUME
+#define _EMUX_OSS_RESET_CHANNEL		0x09
+#define _EMUX_OSS_CHANNEL_MODE		0x0a
+#define _EMUX_OSS_DRUM_CHANNELS		0x0b
+#define _EMUX_OSS_MISC_MODE		0x0c
+#define _EMUX_OSS_RELEASE_ALL		0x0d
+#define _EMUX_OSS_NOTEOFF_ALL		0x0e
+#define _EMUX_OSS_CHN_PRESSURE		0x0f
+#define _EMUX_OSS_EQUALIZER		0x11
+
+#define _EMUX_OSS_MODE_FLAG		0x80
+#define _EMUX_OSS_COOKED_FLAG		0x40	/* not supported */
+#define _EMUX_OSS_MODE_VALUE_MASK	0x3F
+
+
+/*
+ * mode type definitions
+ */
+enum {
+/* 0*/	EMUX_MD_EXCLUSIVE_OFF,	/* obsolete */
+/* 1*/	EMUX_MD_EXCLUSIVE_ON,	/* obsolete */
+/* 2*/	EMUX_MD_VERSION,		/* read only */
+/* 3*/	EMUX_MD_EXCLUSIVE_SOUND,	/* 0/1: exclusive note on (default=1) */
+/* 4*/	EMUX_MD_REALTIME_PAN,	/* 0/1: do realtime pan change (default=1) */
+/* 5*/	EMUX_MD_GUS_BANK,	/* bank number for GUS patches (default=0) */
+/* 6*/	EMUX_MD_KEEP_EFFECT,	/* 0/1: keep effect values, (default=0) */
+/* 7*/	EMUX_MD_ZERO_ATTEN,	/* attenuation of max volume (default=32) */
+/* 8*/	EMUX_MD_CHN_PRIOR,	/* 0/1: set MIDI channel priority mode (default=1) */
+/* 9*/	EMUX_MD_MOD_SENSE,	/* integer: modwheel sensitivity (def=18) */
+/*10*/	EMUX_MD_DEF_PRESET,	/* integer: default preset number (def=0) */
+/*11*/	EMUX_MD_DEF_BANK,	/* integer: default bank number (def=0) */
+/*12*/	EMUX_MD_DEF_DRUM,	/* integer: default drumset number (def=0) */
+/*13*/	EMUX_MD_TOGGLE_DRUM_BANK, /* 0/1: toggle drum flag with bank# (def=0) */
+/*14*/	EMUX_MD_NEW_VOLUME_CALC,	/* 0/1: volume calculation mode (def=1) */
+/*15*/	EMUX_MD_CHORUS_MODE,	/* integer: chorus mode (def=2) */
+/*16*/	EMUX_MD_REVERB_MODE,	/* integer: chorus mode (def=4) */
+/*17*/	EMUX_MD_BASS_LEVEL,	/* integer: bass level (def=5) */
+/*18*/	EMUX_MD_TREBLE_LEVEL,	/* integer: treble level (def=9) */
+/*19*/	EMUX_MD_DEBUG_MODE,	/* integer: debug level (def=0) */
+/*20*/	EMUX_MD_PAN_EXCHANGE,	/* 0/1: exchange panning direction (def=0) */
+	EMUX_MD_END,
+};
+
+
+/*
+ * effect parameters
+ */
+enum {
+
+/* modulation envelope parameters */
+/* 0*/	EMUX_FX_ENV1_DELAY,	/* WORD: ENVVAL */
+/* 1*/	EMUX_FX_ENV1_ATTACK,	/* BYTE: up ATKHLD */
+/* 2*/	EMUX_FX_ENV1_HOLD,	/* BYTE: lw ATKHLD */
+/* 3*/	EMUX_FX_ENV1_DECAY,	/* BYTE: lw DCYSUS */
+/* 4*/	EMUX_FX_ENV1_RELEASE,	/* BYTE: lw DCYSUS */
+/* 5*/	EMUX_FX_ENV1_SUSTAIN,	/* BYTE: up DCYSUS */
+/* 6*/	EMUX_FX_ENV1_PITCH,	/* BYTE: up PEFE */
+/* 7*/	EMUX_FX_ENV1_CUTOFF,	/* BYTE: lw PEFE */
+
+/* volume envelope parameters */
+/* 8*/	EMUX_FX_ENV2_DELAY,	/* WORD: ENVVOL */
+/* 9*/	EMUX_FX_ENV2_ATTACK,	/* BYTE: up ATKHLDV */
+/*10*/	EMUX_FX_ENV2_HOLD,	/* BYTE: lw ATKHLDV */
+/*11*/	EMUX_FX_ENV2_DECAY,	/* BYTE: lw DCYSUSV */
+/*12*/	EMUX_FX_ENV2_RELEASE,	/* BYTE: lw DCYSUSV */
+/*13*/	EMUX_FX_ENV2_SUSTAIN,	/* BYTE: up DCYSUSV */
+	
+/* LFO1 (tremolo & vibrato) parameters */
+/*14*/	EMUX_FX_LFO1_DELAY,	/* WORD: LFO1VAL */
+/*15*/	EMUX_FX_LFO1_FREQ,	/* BYTE: lo TREMFRQ */
+/*16*/	EMUX_FX_LFO1_VOLUME,	/* BYTE: up TREMFRQ */
+/*17*/	EMUX_FX_LFO1_PITCH,	/* BYTE: up FMMOD */
+/*18*/	EMUX_FX_LFO1_CUTOFF,	/* BYTE: lo FMMOD */
+
+/* LFO2 (vibrato) parameters */
+/*19*/	EMUX_FX_LFO2_DELAY,	/* WORD: LFO2VAL */
+/*20*/	EMUX_FX_LFO2_FREQ,	/* BYTE: lo FM2FRQ2 */
+/*21*/	EMUX_FX_LFO2_PITCH,	/* BYTE: up FM2FRQ2 */
+
+/* Other overall effect parameters */
+/*22*/	EMUX_FX_INIT_PITCH,	/* SHORT: pitch offset */
+/*23*/	EMUX_FX_CHORUS,		/* BYTE: chorus effects send (0-255) */
+/*24*/	EMUX_FX_REVERB,		/* BYTE: reverb effects send (0-255) */
+/*25*/	EMUX_FX_CUTOFF,		/* BYTE: up IFATN */
+/*26*/	EMUX_FX_FILTERQ,		/* BYTE: up CCCA */
+
+/* Sample / loop offset changes */
+/*27*/	EMUX_FX_SAMPLE_START,	/* SHORT: offset */
+/*28*/	EMUX_FX_LOOP_START,	/* SHORT: offset */
+/*29*/	EMUX_FX_LOOP_END,	/* SHORT: offset */
+/*30*/	EMUX_FX_COARSE_SAMPLE_START,	/* SHORT: upper word offset */
+/*31*/	EMUX_FX_COARSE_LOOP_START,	/* SHORT: upper word offset */
+/*32*/	EMUX_FX_COARSE_LOOP_END,		/* SHORT: upper word offset */
+/*33*/	EMUX_FX_ATTEN,		/* BYTE: lo IFATN */
+
+	EMUX_FX_END,
+};
+/* number of effects */
+#define EMUX_NUM_EFFECTS  EMUX_FX_END
+
+/* effect flag values */
+#define EMUX_FX_FLAG_OFF	0
+#define EMUX_FX_FLAG_SET	1
+#define EMUX_FX_FLAG_ADD	2
+
+
+#endif
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/emux_synth.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/emux_synth.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/emux_synth.h	(revision 91)
@@ -0,0 +1,244 @@
+#ifndef __SOUND_EMUX_SYNTH_H
+#define __SOUND_EMUX_SYNTH_H
+
+/*
+ *  Defines for the Emu-series WaveTable chip
+ *
+ *  Copyright (C) 2000 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 "seq_kernel.h"
+#include "seq_device.h"
+#include "soundfont.h"
+#include "seq_midi_emul.h"
+#ifdef CONFIG_SND_SEQUENCER_OSS
+#include "seq_oss.h"
+#endif
+#include "emux_legacy.h"
+#include "seq_virmidi.h"
+
+/*
+ * compile flags
+ */
+#define SNDRV_EMUX_USE_RAW_EFFECT
+
+struct snd_emux;
+struct snd_emux_port;
+struct snd_emux_voice;
+struct snd_emux_effect_table;
+
+/*
+ * operators
+ */
+struct snd_emux_operators {
+	struct module *owner;
+	struct snd_emux_voice *(*get_voice)(struct snd_emux *emu,
+					    struct snd_emux_port *port);
+	int (*prepare)(struct snd_emux_voice *vp);
+	void (*trigger)(struct snd_emux_voice *vp);
+	void (*release)(struct snd_emux_voice *vp);
+	void (*update)(struct snd_emux_voice *vp, int update);
+	void (*terminate)(struct snd_emux_voice *vp);
+	void (*free_voice)(struct snd_emux_voice *vp);
+	void (*reset)(struct snd_emux *emu, int ch);
+	/* the first parameters are struct snd_emux */
+	int (*sample_new)(struct snd_emux *emu, struct snd_sf_sample *sp,
+			  struct snd_util_memhdr *hdr,
+			  const void __user *data, long count);
+	int (*sample_free)(struct snd_emux *emu, struct snd_sf_sample *sp,
+			   struct snd_util_memhdr *hdr);
+	void (*sample_reset)(struct snd_emux *emu);
+	int (*load_fx)(struct snd_emux *emu, int type, int arg,
+		       const void __user *data, long count);
+	void (*sysex)(struct snd_emux *emu, char *buf, int len, int parsed,
+		      struct snd_midi_channel_set *chset);
+#ifdef CONFIG_SND_SEQUENCER_OSS
+	int (*oss_ioctl)(struct snd_emux *emu, int cmd, int p1, int p2);
+#endif
+};
+
+
+/*
+ * constant values
+ */
+#define SNDRV_EMUX_MAX_PORTS		32	/* max # of sequencer ports */
+#define SNDRV_EMUX_MAX_VOICES		64	/* max # of voices */
+#define SNDRV_EMUX_MAX_MULTI_VOICES	16	/* max # of playable voices
+						 * simultineously
+						 */
+
+/*
+ * flags
+ */
+#define SNDRV_EMUX_ACCEPT_ROM		(1<<0)
+
+/*
+ * emuX wavetable
+ */
+struct snd_emux {
+
+	struct snd_card *card;	/* assigned card */
+
+	/* following should be initialized before registration */
+	int max_voices;		/* Number of voices */
+	int mem_size;		/* memory size (in byte) */
+	int num_ports;		/* number of ports to be created */
+	int pitch_shift;	/* pitch shift value (for Emu10k1) */
+	struct snd_emux_operators ops;	/* operators */
+	void *hw;		/* hardware */
+	unsigned long flags;	/* other conditions */
+	int midi_ports;		/* number of virtual midi devices */
+	int midi_devidx;	/* device offset of virtual midi */
+	unsigned int linear_panning: 1; /* panning is linear (sbawe = 1, emu10k1 = 0) */
+	int hwdep_idx;		/* hwdep device index */
+	struct snd_hwdep *hwdep;	/* hwdep device */
+
+	/* private */
+	int num_voices;		/* current number of voices */
+	struct snd_sf_list *sflist;	/* root of SoundFont list */
+	struct snd_emux_voice *voices;	/* Voices (EMU 'channel') */
+	int use_time;	/* allocation counter */
+	spinlock_t voice_lock;	/* Lock for voice access */
+	struct semaphore register_mutex;
+	int client;		/* For the sequencer client */
+	int ports[SNDRV_EMUX_MAX_PORTS];	/* The ports for this device */
+	struct snd_emux_port *portptrs[SNDRV_EMUX_MAX_PORTS];
+	int used;	/* use counter */
+	char *name;	/* name of the device (internal) */
+	struct snd_rawmidi **vmidi;
+	struct timer_list tlist;	/* for pending note-offs */
+	int timer_active;
+
+	struct snd_util_memhdr *memhdr;	/* memory chunk information */
+
+#ifdef CONFIG_PROC_FS
+	struct snd_info_entry *proc;
+#endif
+
+#ifdef CONFIG_SND_SEQUENCER_OSS
+	struct snd_seq_device *oss_synth;
+#endif
+};
+
+
+/*
+ * sequencer port information
+ */
+struct snd_emux_port {
+
+	struct snd_midi_channel_set chset;
+	struct snd_emux *emu;
+
+	char port_mode;			/* operation mode */
+	int volume_atten;		/* emuX raw attenuation */
+	unsigned long drum_flags;	/* drum bitmaps */
+	int ctrls[EMUX_MD_END];		/* control parameters */
+#ifdef SNDRV_EMUX_USE_RAW_EFFECT
+	struct snd_emux_effect_table *effect;
+#endif
+#ifdef CONFIG_SND_SEQUENCER_OSS
+	struct snd_seq_oss_arg *oss_arg;
+#endif
+};
+
+/* port_mode */
+#define SNDRV_EMUX_PORT_MODE_MIDI		0	/* normal MIDI port */
+#define SNDRV_EMUX_PORT_MODE_OSS_SYNTH	1	/* OSS synth port */
+#define SNDRV_EMUX_PORT_MODE_OSS_MIDI	2	/* OSS multi channel synth port */
+
+/*
+ * A structure to keep track of each hardware voice
+ */
+struct snd_emux_voice {
+	int  ch;		/* Hardware channel number */
+
+	int  state;		/* status */
+#define SNDRV_EMUX_ST_OFF		0x00	/* Not playing, and inactive */
+#define SNDRV_EMUX_ST_ON		0x01	/* Note on */
+#define SNDRV_EMUX_ST_RELEASED 	(0x02|SNDRV_EMUX_ST_ON)	/* Note released */
+#define SNDRV_EMUX_ST_SUSTAINED	(0x04|SNDRV_EMUX_ST_ON)	/* Note sustained */
+#define SNDRV_EMUX_ST_STANDBY	(0x08|SNDRV_EMUX_ST_ON)	/* Waiting to be triggered */
+#define SNDRV_EMUX_ST_PENDING 	(0x10|SNDRV_EMUX_ST_ON)	/* Note will be released */
+#define SNDRV_EMUX_ST_LOCKED		0x100	/* Not accessible */
+
+	unsigned int  time;	/* An allocation time */
+	unsigned char note;	/* Note currently assigned to this voice */
+	unsigned char key;
+	unsigned char velocity;	/* Velocity of current note */
+
+	struct snd_sf_zone *zone;	/* Zone assigned to this note */
+	void *block;		/* sample block pointer (optional) */
+	struct snd_midi_channel *chan;	/* Midi channel for this note */
+	struct snd_emux_port *port;	/* associated port */
+	struct snd_emux *emu;	/* assigned root info */
+	void *hw;		/* hardware pointer (emu8000 or emu10k1) */
+	unsigned long ontime;	/* jiffies at note triggered */
+	
+	/* Emu8k/Emu10k1 registers */
+	struct soundfont_voice_info reg;
+
+	/* additional registers */
+	int avol;		/* volume attenuation */
+	int acutoff;		/* cutoff target */
+	int apitch;		/* pitch offset */
+	int apan;		/* pan/aux pair */
+	int aaux;
+	int ptarget;		/* pitch target */
+	int vtarget;		/* volume target */
+	int ftarget;		/* filter target */
+
+};
+
+/*
+ * update flags (can be combined)
+ */
+#define SNDRV_EMUX_UPDATE_VOLUME		(1<<0)
+#define SNDRV_EMUX_UPDATE_PITCH		(1<<1)
+#define SNDRV_EMUX_UPDATE_PAN		(1<<2)
+#define SNDRV_EMUX_UPDATE_FMMOD		(1<<3)
+#define SNDRV_EMUX_UPDATE_TREMFREQ	(1<<4)
+#define SNDRV_EMUX_UPDATE_FM2FRQ2		(1<<5)
+#define SNDRV_EMUX_UPDATE_Q		(1<<6)
+
+
+#ifdef SNDRV_EMUX_USE_RAW_EFFECT
+/*
+ * effect table
+ */
+struct snd_emux_effect_table {
+	/* Emu8000 specific effects */
+	short val[EMUX_NUM_EFFECTS];
+	unsigned char flag[EMUX_NUM_EFFECTS];
+};
+#endif /* SNDRV_EMUX_USE_RAW_EFFECT */
+
+
+/*
+ * prototypes - interface to Emu10k1 and Emu8k routines
+ */
+int snd_emux_new(struct snd_emux **remu);
+int snd_emux_register(struct snd_emux *emu, struct snd_card *card, int index, char *name);
+int snd_emux_free(struct snd_emux *emu);
+
+/*
+ * exported functions
+ */
+void snd_emux_terminate_all(struct snd_emux *emu);
+void snd_emux_lock_voice(struct snd_emux *emu, int voice);
+void snd_emux_unlock_voice(struct snd_emux *emu, int voice);
+
+#endif /* __SOUND_EMUX_SYNTH_H */
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/es1688.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/es1688.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/es1688.h	(revision 91)
@@ -0,0 +1,125 @@
+#ifndef __ES1688_H
+#define __ES1688_H
+
+/*
+ *  Header file for ES488/ES1688
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "control.h"
+#include "pcm.h"
+
+#define ES1688_HW_AUTO		0x0000
+#define ES1688_HW_688		0x0001
+#define ES1688_HW_1688		0x0002
+
+struct _snd_es1688 {
+	unsigned long port;		/* port of ESS chip */
+	struct resource *res_port;
+	unsigned long mpu_port;		/* MPU-401 port of ESS chip */
+	int irq;			/* IRQ number of ESS chip */
+	int mpu_irq;			/* MPU IRQ */
+	int dma8;			/* 8-bit DMA */
+	unsigned short version;		/* version of ESS chip */
+	unsigned short hardware;	/* see to ES1688_HW_XXXX */
+
+	unsigned short trigger_value;
+	unsigned char pad;
+	unsigned int dma_size;
+
+	snd_card_t *card;
+	snd_pcm_t *pcm;
+	snd_pcm_substream_t *playback_substream;
+	snd_pcm_substream_t *capture_substream;
+
+	spinlock_t reg_lock;
+	spinlock_t mixer_lock;
+};
+
+typedef struct _snd_es1688 es1688_t;
+
+/* I/O ports */
+
+#define ES1688P(codec, x) ((codec)->port + e_s_s_ESS1688##x)
+
+#define e_s_s_ESS1688RESET	0x6
+#define e_s_s_ESS1688READ	0xa
+#define e_s_s_ESS1688WRITE	0xc
+#define e_s_s_ESS1688COMMAND	0xc
+#define e_s_s_ESS1688STATUS	0xc
+#define e_s_s_ESS1688DATA_AVAIL	0xe
+#define e_s_s_ESS1688DATA_AVAIL_16 0xf
+#define e_s_s_ESS1688MIXER_ADDR	0x4
+#define e_s_s_ESS1688MIXER_DATA	0x5
+#define e_s_s_ESS1688OPL3_LEFT	0x0
+#define e_s_s_ESS1688OPL3_RIGHT	0x2
+#define e_s_s_ESS1688OPL3_BOTH	0x8
+#define e_s_s_ESS1688ENABLE0	0x0
+#define e_s_s_ESS1688ENABLE1	0x9
+#define e_s_s_ESS1688ENABLE2	0xb
+#define e_s_s_ESS1688INIT1	0x7
+
+#define ES1688_DSP_CMD_DMAOFF	0xd0
+#define ES1688_DSP_CMD_SPKON	0xd1
+#define ES1688_DSP_CMD_SPKOFF	0xd3
+#define ES1688_DSP_CMD_DMAON	0xd4
+
+#define ES1688_PCM_DEV		0x14
+#define ES1688_MIC_DEV		0x1a
+#define ES1688_REC_DEV		0x1c
+#define ES1688_MASTER_DEV	0x32
+#define ES1688_FM_DEV		0x36
+#define ES1688_CD_DEV		0x38
+#define ES1688_AUX_DEV		0x3a
+#define ES1688_SPEAKER_DEV	0x3c
+#define ES1688_LINE_DEV		0x3e
+#define ES1688_RECLEV_DEV	0xb4
+
+#define ES1688_MIXS_MASK	0x17
+#define ES1688_MIXS_MIC		0x00
+#define ES1688_MIXS_MIC_MASTER	0x01
+#define ES1688_MIXS_CD		0x02
+#define ES1688_MIXS_AOUT	0x03
+#define ES1688_MIXS_MIC1	0x04
+#define ES1688_MIXS_REC_MIX	0x05
+#define ES1688_MIXS_LINE	0x06
+#define ES1688_MIXS_MASTER	0x07
+#define ES1688_MIXS_MUTE	0x10
+
+/*
+
+ */
+
+void snd_es1688_mixer_write(es1688_t *chip, unsigned char reg, unsigned char data);
+unsigned char snd_es1688_mixer_read(es1688_t *chip, unsigned char reg);
+
+void snd_es1688_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+
+int snd_es1688_create(snd_card_t * card,
+		      unsigned long port,
+		      unsigned long mpu_port,
+		      int irq,
+		      int mpu_irq,
+		      int dma8,
+		      unsigned short hardware,
+		      es1688_t ** rchip);
+int snd_es1688_pcm(es1688_t *chip, int device, snd_pcm_t ** rpcm);
+int snd_es1688_mixer(es1688_t *chip);
+
+#endif				/* __ES1688_H */
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/gus.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/gus.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/gus.h	(revision 91)
@@ -0,0 +1,728 @@
+#ifndef __GUS_H
+#define __GUS_H
+
+/*
+ *  Global structures used for GUS part of ALSA driver
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "pcm.h"
+#include "rawmidi.h"
+#include "timer.h"
+#include "seq_midi_emul.h"
+#include "seq_device.h"
+#include "ainstr_iw.h"
+#include "ainstr_gf1.h"
+#include "ainstr_simple.h"
+
+#define SNDRV_SEQ_DEV_ID_GUS			"synth-gus"
+
+/* IO ports */
+
+#define GUSP(gus, x)			((gus)->gf1.port + SNDRV_g_u_s_##x)
+
+#define SNDRV_g_u_s_MIDICTRL		(0x320-0x220)
+#define SNDRV_g_u_s_MIDISTAT		(0x320-0x220)
+#define SNDRV_g_u_s_MIDIDATA		(0x321-0x220)
+
+#define SNDRV_g_u_s_GF1PAGE		(0x322-0x220)
+#define SNDRV_g_u_s_GF1REGSEL		(0x323-0x220)
+#define SNDRV_g_u_s_GF1DATALOW		(0x324-0x220)
+#define SNDRV_g_u_s_GF1DATAHIGH		(0x325-0x220)
+#define SNDRV_g_u_s_IRQSTAT		(0x226-0x220)
+#define SNDRV_g_u_s_TIMERCNTRL		(0x228-0x220)
+#define SNDRV_g_u_s_TIMERDATA		(0x229-0x220)
+#define SNDRV_g_u_s_DRAM			(0x327-0x220)
+#define SNDRV_g_u_s_MIXCNTRLREG		(0x220-0x220)
+#define SNDRV_g_u_s_IRQDMACNTRLREG	(0x22b-0x220)
+#define SNDRV_g_u_s_REGCNTRLS		(0x22f-0x220)
+#define SNDRV_g_u_s_BOARDVERSION		(0x726-0x220)
+#define SNDRV_g_u_s_MIXCNTRLPORT		(0x726-0x220)
+#define SNDRV_g_u_s_IVER			(0x325-0x220)
+#define SNDRV_g_u_s_MIXDATAPORT		(0x326-0x220)
+#define SNDRV_g_u_s_MAXCNTRLPORT		(0x326-0x220)
+
+/* GF1 registers */
+
+/* global registers */
+#define SNDRV_GF1_GB_ACTIVE_VOICES		0x0e
+#define SNDRV_GF1_GB_VOICES_IRQ			0x0f
+#define SNDRV_GF1_GB_GLOBAL_MODE			0x19
+#define SNDRV_GF1_GW_LFO_BASE			0x1a
+#define SNDRV_GF1_GB_VOICES_IRQ_READ		0x1f
+#define SNDRV_GF1_GB_DRAM_DMA_CONTROL		0x41
+#define SNDRV_GF1_GW_DRAM_DMA_LOW			0x42
+#define SNDRV_GF1_GW_DRAM_IO_LOW			0x43
+#define SNDRV_GF1_GB_DRAM_IO_HIGH			0x44
+#define SNDRV_GF1_GB_SOUND_BLASTER_CONTROL	0x45
+#define SNDRV_GF1_GB_ADLIB_TIMER_1		0x46
+#define SNDRV_GF1_GB_ADLIB_TIMER_2		0x47
+#define SNDRV_GF1_GB_RECORD_RATE			0x48
+#define SNDRV_GF1_GB_REC_DMA_CONTROL		0x49
+#define SNDRV_GF1_GB_JOYSTICK_DAC_LEVEL		0x4b
+#define SNDRV_GF1_GB_RESET			0x4c
+#define SNDRV_GF1_GB_DRAM_DMA_HIGH		0x50
+#define SNDRV_GF1_GW_DRAM_IO16			0x51
+#define SNDRV_GF1_GW_MEMORY_CONFIG		0x52
+#define SNDRV_GF1_GB_MEMORY_CONTROL		0x53
+#define SNDRV_GF1_GW_FIFO_RECORD_BASE_ADDR	0x54
+#define SNDRV_GF1_GW_FIFO_PLAY_BASE_ADDR		0x55
+#define SNDRV_GF1_GW_FIFO_SIZE			0x56
+#define SNDRV_GF1_GW_INTERLEAVE			0x57
+#define SNDRV_GF1_GB_COMPATIBILITY		0x59
+#define SNDRV_GF1_GB_DECODE_CONTROL		0x5a
+#define SNDRV_GF1_GB_VERSION_NUMBER		0x5b
+#define SNDRV_GF1_GB_MPU401_CONTROL_A		0x5c
+#define SNDRV_GF1_GB_MPU401_CONTROL_B		0x5d
+#define SNDRV_GF1_GB_EMULATION_IRQ		0x60
+/* voice specific registers */
+#define SNDRV_GF1_VB_ADDRESS_CONTROL		0x00
+#define SNDRV_GF1_VW_FREQUENCY			0x01
+#define SNDRV_GF1_VW_START_HIGH			0x02
+#define SNDRV_GF1_VW_START_LOW			0x03
+#define SNDRV_GF1_VA_START			SNDRV_GF1_VW_START_HIGH
+#define SNDRV_GF1_VW_END_HIGH			0x04
+#define SNDRV_GF1_VW_END_LOW			0x05
+#define SNDRV_GF1_VA_END				SNDRV_GF1_VW_END_HIGH
+#define SNDRV_GF1_VB_VOLUME_RATE			0x06
+#define SNDRV_GF1_VB_VOLUME_START			0x07
+#define SNDRV_GF1_VB_VOLUME_END			0x08
+#define SNDRV_GF1_VW_VOLUME			0x09
+#define SNDRV_GF1_VW_CURRENT_HIGH			0x0a
+#define SNDRV_GF1_VW_CURRENT_LOW			0x0b
+#define SNDRV_GF1_VA_CURRENT			SNDRV_GF1_VW_CURRENT_HIGH
+#define SNDRV_GF1_VB_PAN				0x0c
+#define SNDRV_GF1_VW_OFFSET_RIGHT			0x0c
+#define SNDRV_GF1_VB_VOLUME_CONTROL		0x0d
+#define SNDRV_GF1_VB_UPPER_ADDRESS		0x10
+#define SNDRV_GF1_VW_EFFECT_HIGH			0x11
+#define SNDRV_GF1_VW_EFFECT_LOW			0x12
+#define SNDRV_GF1_VA_EFFECT			SNDRV_GF1_VW_EFFECT_HIGH
+#define SNDRV_GF1_VW_OFFSET_LEFT			0x13
+#define SNDRV_GF1_VB_ACCUMULATOR			0x14
+#define SNDRV_GF1_VB_MODE				0x15
+#define SNDRV_GF1_VW_EFFECT_VOLUME		0x16
+#define SNDRV_GF1_VB_FREQUENCY_LFO		0x17
+#define SNDRV_GF1_VB_VOLUME_LFO			0x18
+#define SNDRV_GF1_VW_OFFSET_RIGHT_FINAL		0x1b
+#define SNDRV_GF1_VW_OFFSET_LEFT_FINAL		0x1c
+#define SNDRV_GF1_VW_EFFECT_VOLUME_FINAL		0x1d
+
+/* ICS registers */
+
+#define SNDRV_ICS_MIC_DEV		0
+#define SNDRV_ICS_LINE_DEV	1
+#define SNDRV_ICS_CD_DEV		2
+#define SNDRV_ICS_GF1_DEV		3
+#define SNDRV_ICS_NONE_DEV	4
+#define SNDRV_ICS_MASTER_DEV	5
+
+/* LFO */
+
+#define SNDRV_LFO_TREMOLO		0
+#define SNDRV_LFO_VIBRATO		1
+
+/* misc */
+
+#define SNDRV_GF1_DMA_UNSIGNED	0x80
+#define SNDRV_GF1_DMA_16BIT	0x40
+#define SNDRV_GF1_DMA_IRQ		0x20
+#define SNDRV_GF1_DMA_WIDTH16	0x04
+#define SNDRV_GF1_DMA_READ	0x02	/* read from GUS's DRAM */
+#define SNDRV_GF1_DMA_ENABLE	0x01
+
+/* ramp ranges */
+
+#define SNDRV_GF1_ATTEN(x)	(snd_gf1_atten_table[x])
+#define SNDRV_GF1_MIN_VOLUME	1800
+#define SNDRV_GF1_MAX_VOLUME	4095
+#define SNDRV_GF1_MIN_OFFSET	(SNDRV_GF1_MIN_VOLUME>>4)
+#define SNDRV_GF1_MAX_OFFSET	255
+#define SNDRV_GF1_MAX_TDEPTH	90
+
+/* defines for memory manager */
+
+#define SNDRV_GF1_MEM_BLOCK_16BIT		0x0001
+
+#define SNDRV_GF1_MEM_OWNER_DRIVER	0x0001
+#define SNDRV_GF1_MEM_OWNER_WAVE_SIMPLE	0x0002
+#define SNDRV_GF1_MEM_OWNER_WAVE_GF1	0x0003
+#define SNDRV_GF1_MEM_OWNER_WAVE_IWFFFF	0x0004
+
+/* constants for interrupt handlers */
+
+#define SNDRV_GF1_HANDLER_MIDI_OUT	0x00010000
+#define SNDRV_GF1_HANDLER_MIDI_IN		0x00020000
+#define SNDRV_GF1_HANDLER_TIMER1		0x00040000
+#define SNDRV_GF1_HANDLER_TIMER2		0x00080000
+#define SNDRV_GF1_HANDLER_VOICE		0x00100000
+#define SNDRV_GF1_HANDLER_DMA_WRITE	0x00200000
+#define SNDRV_GF1_HANDLER_DMA_READ	0x00400000
+#define SNDRV_GF1_HANDLER_ALL		(0xffff0000&~SNDRV_GF1_HANDLER_VOICE)
+
+/* constants for DMA flags */
+
+#define SNDRV_GF1_DMA_TRIGGER		1
+
+/* --- */
+
+struct _snd_gus_card;
+typedef struct _snd_gus_card snd_gus_card_t;
+
+/* GF1 specific structure */
+
+typedef struct _snd_gf1_bank_info {
+	unsigned int address;
+	unsigned int size;
+} snd_gf1_bank_info_t;
+
+typedef struct _snd_gf1_mem_block {
+	unsigned short flags;	/* flags - SNDRV_GF1_MEM_BLOCK_XXXX */
+	unsigned short owner;	/* owner - SNDRV_GF1_MEM_OWNER_XXXX */
+	unsigned int share;	/* share count */
+	unsigned int share_id[4]; /* share ID */
+	unsigned int ptr;
+	unsigned int size;
+	char *name;
+	struct _snd_gf1_mem_block *next;
+	struct _snd_gf1_mem_block *prev;
+} snd_gf1_mem_block_t;
+
+typedef struct _snd_gf1_mem {
+	snd_gf1_bank_info_t banks_8[4];
+	snd_gf1_bank_info_t banks_16[4];
+	snd_gf1_mem_block_t *first;
+	snd_gf1_mem_block_t *last;
+	snd_info_entry_t *info_entry;
+	struct semaphore memory_mutex;
+} snd_gf1_mem_t;
+
+typedef struct snd_gf1_dma_block {
+	void *buffer;		/* buffer in computer's RAM */
+	unsigned int addr;	/* address in onboard memory */
+	unsigned int count;	/* count in bytes */
+	unsigned int cmd;	/* DMA command (format) */
+	void (*ack)(snd_gus_card_t * gus, void *private_data);
+	void *private_data;
+	struct snd_gf1_dma_block *next;
+} snd_gf1_dma_block_t;
+
+typedef struct {
+	snd_midi_channel_set_t * chset;
+	snd_gus_card_t * gus;
+	int mode;		/* operation mode */
+	int client;		/* sequencer client number */
+	int port;		/* sequencer port number */
+	int midi_has_voices: 1;
+} snd_gus_port_t;
+
+typedef struct _snd_gus_voice snd_gus_voice_t;
+
+typedef struct {
+	void (*sample_start)(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_position_t position);
+	void (*sample_stop)(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_stop_mode_t mode);
+	void (*sample_freq)(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_frequency_t freq);
+	void (*sample_volume)(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_ev_volume_t *volume);
+	void (*sample_loop)(snd_gus_card_t *card, snd_gus_voice_t *voice, snd_seq_ev_loop_t *loop);
+	void (*sample_pos)(snd_gus_card_t *card, snd_gus_voice_t *voice, snd_seq_position_t position);
+	void (*sample_private1)(snd_gus_card_t *card, snd_gus_voice_t *voice, unsigned char *data);
+} snd_gus_sample_ops_t;
+
+#define SNDRV_GF1_VOICE_TYPE_PCM		0
+#define SNDRV_GF1_VOICE_TYPE_SYNTH 	1
+#define SNDRV_GF1_VOICE_TYPE_MIDI		2
+
+#define SNDRV_GF1_VFLG_RUNNING		(1<<0)
+#define SNDRV_GF1_VFLG_EFFECT_TIMER1	(1<<1)
+#define SNDRV_GF1_VFLG_PAN		(1<<2)
+
+typedef enum {
+	VENV_BEFORE,
+	VENV_ATTACK,
+	VENV_SUSTAIN,
+	VENV_RELEASE,
+	VENV_DONE,
+	VENV_VOLUME
+} snd_gus_volume_state_t;
+
+struct _snd_gus_voice {
+	int number;
+	int use: 1,
+	    pcm: 1,
+	    synth:1,
+	    midi: 1;
+	unsigned int flags;
+	unsigned char client;
+	unsigned char port;
+	unsigned char index;
+	unsigned char pad;
+	
+#ifdef CONFIG_SND_DEBUG
+	unsigned int interrupt_stat_wave;
+	unsigned int interrupt_stat_volume;
+#endif
+	void (*handler_wave) (snd_gus_card_t * gus, snd_gus_voice_t * voice);
+	void (*handler_volume) (snd_gus_card_t * gus, snd_gus_voice_t * voice);
+	void (*handler_effect) (snd_gus_card_t * gus, snd_gus_voice_t * voice);
+	void (*volume_change) (snd_gus_card_t * gus);
+
+	snd_gus_sample_ops_t *sample_ops;
+
+	snd_seq_instr_t instr;
+
+	/* running status / registers */
+
+	snd_seq_ev_volume_t sample_volume;
+
+	unsigned short fc_register;
+	unsigned short fc_lfo;
+	unsigned short gf1_volume;
+	unsigned char control;
+	unsigned char mode;
+	unsigned char gf1_pan;
+	unsigned char effect_accumulator;
+	unsigned char volume_control;
+	unsigned char venv_value_next;
+	snd_gus_volume_state_t venv_state;
+	snd_gus_volume_state_t venv_state_prev;
+	unsigned short vlo;
+	unsigned short vro;
+	unsigned short gf1_effect_volume;
+	
+	/* --- */
+
+	void *private_data;
+	void (*private_free)(snd_gus_voice_t *voice);
+};
+
+struct _snd_gf1 {
+
+	unsigned int enh_mode:1,	/* enhanced mode (GFA1) */
+		     hw_lfo:1,		/* use hardware LFO */
+		     sw_lfo:1,		/* use software LFO */
+		     effect:1;		/* use effect voices */
+
+	unsigned long port;		/* port of GF1 chip */
+	struct resource *res_port1;
+	struct resource *res_port2;
+	int irq;			/* IRQ number */
+	int dma1;			/* DMA1 number */
+	int dma2;			/* DMA2 number */
+	unsigned int memory;		/* GUS's DRAM size in bytes */
+	unsigned int rom_memory;	/* GUS's ROM size in bytes */
+	unsigned int rom_present;	/* bitmask */
+	unsigned int rom_banks;		/* GUS's ROM banks */
+
+	snd_gf1_mem_t mem_alloc;
+	snd_info_entry_t *ram_entries[4];
+	snd_info_entry_t *rom_entries[4];
+
+	/* registers */
+	unsigned short reg_page;
+	unsigned short reg_regsel;
+	unsigned short reg_data8;
+	unsigned short reg_data16;
+	unsigned short reg_irqstat;
+	unsigned short reg_dram;
+	unsigned short reg_timerctrl;
+	unsigned short reg_timerdata;
+	unsigned char ics_regs[6][2];
+	/* --------- */
+
+	unsigned char active_voices;	/* active voices */
+	unsigned char active_voice;	/* selected voice (GF1PAGE register) */
+
+	snd_gus_voice_t voices[32];	/* GF1 voices */
+
+	unsigned int default_voice_address;
+
+	unsigned short playback_freq;	/* GF1 playback (mixing) frequency */
+	unsigned short mode;		/* see to SNDRV_GF1_MODE_XXXX */
+	unsigned char volume_ramp;
+	unsigned char smooth_pan;
+	unsigned char full_range_pan;
+	unsigned char pad0;
+
+	unsigned char *lfos;
+
+	/* interrupt handlers */
+
+	void (*interrupt_handler_midi_out) (snd_gus_card_t * gus);
+	void (*interrupt_handler_midi_in) (snd_gus_card_t * gus);
+	void (*interrupt_handler_timer1) (snd_gus_card_t * gus);
+	void (*interrupt_handler_timer2) (snd_gus_card_t * gus);
+	void (*interrupt_handler_dma_write) (snd_gus_card_t * gus);
+	void (*interrupt_handler_dma_read) (snd_gus_card_t * gus);
+
+#ifdef CONFIG_SND_DEBUG
+	unsigned int interrupt_stat_midi_out;
+	unsigned int interrupt_stat_midi_in;
+	unsigned int interrupt_stat_timer1;
+	unsigned int interrupt_stat_timer2;
+	unsigned int interrupt_stat_dma_write;
+	unsigned int interrupt_stat_dma_read;
+	unsigned int interrupt_stat_voice_lost;
+#endif
+
+	/* synthesizer */
+
+	int seq_client;
+	snd_gus_port_t seq_ports[4];
+	snd_seq_kinstr_list_t *ilist;
+	snd_iwffff_ops_t iwffff_ops;
+	snd_gf1_ops_t gf1_ops;
+	snd_simple_ops_t simple_ops;
+
+	/* timer */
+
+	unsigned short timer_enabled;
+	snd_timer_t *timer1;
+	snd_timer_t *timer2;
+
+	/* midi */
+
+	unsigned short uart_cmd;
+	unsigned int uart_framing;
+	unsigned int uart_overrun;
+
+	/* dma operations */
+
+	unsigned int dma_flags;
+	unsigned int dma_shared;
+	snd_gf1_dma_block_t *dma_data_pcm;
+	snd_gf1_dma_block_t *dma_data_pcm_last;
+	snd_gf1_dma_block_t *dma_data_synth;
+	snd_gf1_dma_block_t *dma_data_synth_last;
+	void (*dma_ack)(snd_gus_card_t * gus, void *private_data);
+	void *dma_private_data;
+
+	/* pcm */
+	int pcm_channels;
+	int pcm_alloc_voices;
+        unsigned short pcm_volume_level_left;
+	unsigned short pcm_volume_level_right;
+	unsigned short pcm_volume_level_left1;
+	unsigned short pcm_volume_level_right1;
+                                
+	unsigned char pcm_rcntrl_reg;
+	unsigned char pad_end;
+};
+
+/* main structure for GUS card */
+
+struct _snd_gus_card {
+	snd_card_t *card;
+
+	unsigned int
+	 initialized: 1,		/* resources were initialized */
+	 equal_irq:1,			/* GF1 and CODEC shares IRQ (GUS MAX only) */
+	 equal_dma:1,			/* if dma channels are equal (not valid for daughter board) */
+	 ics_flag:1,			/* have we ICS mixer chip */
+	 ics_flipped:1,			/* ICS mixer have flipped some channels? */
+	 codec_flag:1,			/* have we CODEC chip? */
+	 max_flag:1,			/* have we GUS MAX card? */
+	 max_ctrl_flag:1,		/* have we original GUS MAX card? */
+	 daughter_flag:1,		/* have we daughter board? */
+	 interwave:1,			/* hey - we have InterWave card */
+	 ess_flag:1,			/* ESS chip found... GUS Extreme */
+	 ace_flag:1,			/* GUS ACE detected */
+	 uart_enable:1;			/* enable MIDI UART */
+	unsigned short revision;	/* revision of chip */
+	unsigned short max_cntrl_val;	/* GUS MAX control value */
+	unsigned short mix_cntrl_reg;	/* mixer control register */
+	unsigned short joystick_dac;	/* joystick DAC level */
+	int timer_dev;			/* timer device */
+
+	struct _snd_gf1 gf1;	/* gf1 specific variables */
+#ifdef CONFIG_SND_DEBUG
+	snd_info_entry_t *irq_entry;
+#endif
+	snd_pcm_t *pcm;
+	snd_pcm_substream_t *pcm_cap_substream;
+	unsigned int c_dma_size;
+	unsigned int c_period_size;
+	unsigned int c_pos;
+
+	snd_rawmidi_t *midi_uart;
+	snd_rawmidi_substream_t *midi_substream_output;
+	snd_rawmidi_substream_t *midi_substream_input;
+
+	snd_seq_device_t *seq_dev;
+
+	spinlock_t reg_lock;
+	spinlock_t voice_alloc;
+	spinlock_t active_voice_lock;
+	spinlock_t event_lock;
+	spinlock_t dma_lock;
+	spinlock_t pcm_volume_level_lock;
+	spinlock_t uart_cmd_lock;
+	struct semaphore dma_mutex;
+	struct semaphore register_mutex;
+};
+
+/* I/O functions for GF1/InterWave chip - gus_io.c */
+
+inline void snd_gf1_select_voice(snd_gus_card_t * gus, int voice)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&gus->active_voice_lock, flags);
+	if (voice != gus->gf1.active_voice) {
+		gus->gf1.active_voice = voice;
+		outb(voice, GUSP(gus, GF1PAGE));
+	}
+	spin_unlock_irqrestore(&gus->active_voice_lock, flags);
+}
+
+inline void snd_gf1_uart_cmd(snd_gus_card_t * gus, unsigned char b)
+{
+	outb(gus->gf1.uart_cmd = b, GUSP(gus, MIDICTRL));
+}
+
+inline unsigned char snd_gf1_uart_stat(snd_gus_card_t * gus)
+{
+	return inb(GUSP(gus, MIDISTAT));
+}
+
+inline void snd_gf1_uart_put(snd_gus_card_t * gus, unsigned char b)
+{
+	outb(b, GUSP(gus, MIDIDATA));
+}
+
+inline unsigned char snd_gf1_uart_get(snd_gus_card_t * gus)
+{
+	return inb(GUSP(gus, MIDIDATA));
+}
+
+extern void snd_gf1_delay(snd_gus_card_t * gus);
+
+extern void snd_gf1_ctrl_stop(snd_gus_card_t * gus, unsigned char reg);
+
+extern void snd_gf1_write8(snd_gus_card_t * gus, unsigned char reg, unsigned char data);
+extern unsigned char snd_gf1_look8(snd_gus_card_t * gus, unsigned char reg);
+extern inline unsigned char snd_gf1_read8(snd_gus_card_t * gus, unsigned char reg)
+{
+	return snd_gf1_look8(gus, reg | 0x80);
+}
+extern void snd_gf1_write16(snd_gus_card_t * gus, unsigned char reg, unsigned int data);
+extern unsigned short snd_gf1_look16(snd_gus_card_t * gus, unsigned char reg);
+extern inline unsigned short snd_gf1_read16(snd_gus_card_t * gus, unsigned char reg)
+{
+	return snd_gf1_look16(gus, reg | 0x80);
+}
+extern void snd_gf1_adlib_write(snd_gus_card_t * gus, unsigned char reg, unsigned char data);
+extern void snd_gf1_dram_addr(snd_gus_card_t * gus, unsigned int addr);
+extern void snd_gf1_poke(snd_gus_card_t * gus, unsigned int addr, unsigned char data);
+extern unsigned char snd_gf1_peek(snd_gus_card_t * gus, unsigned int addr);
+extern void snd_gf1_pokew(snd_gus_card_t * gus, unsigned int addr, unsigned short data);
+extern unsigned short snd_gf1_peekw(snd_gus_card_t * gus, unsigned int addr);
+extern void snd_gf1_dram_setmem(snd_gus_card_t * gus, unsigned int addr, unsigned short value, unsigned int count);
+extern void snd_gf1_write_addr(snd_gus_card_t * gus, unsigned char reg, unsigned int addr, short w_16bit);
+extern unsigned int snd_gf1_read_addr(snd_gus_card_t * gus, unsigned char reg, short w_16bit);
+extern void snd_gf1_i_ctrl_stop(snd_gus_card_t * gus, unsigned char reg);
+extern void snd_gf1_i_write8(snd_gus_card_t * gus, unsigned char reg, unsigned char data);
+extern unsigned char snd_gf1_i_look8(snd_gus_card_t * gus, unsigned char reg);
+extern void snd_gf1_i_write16(snd_gus_card_t * gus, unsigned char reg, unsigned int data);
+extern inline unsigned char snd_gf1_i_read8(snd_gus_card_t * gus, unsigned char reg)
+{
+	return snd_gf1_i_look8(gus, reg | 0x80);
+}
+extern unsigned short snd_gf1_i_look16(snd_gus_card_t * gus, unsigned char reg);
+extern inline unsigned short snd_gf1_i_read16(snd_gus_card_t * gus, unsigned char reg)
+{
+	return snd_gf1_i_look16(gus, reg | 0x80);
+}
+extern void snd_gf1_i_adlib_write(snd_gus_card_t * gus, unsigned char reg, unsigned char data);
+extern void snd_gf1_i_write_addr(snd_gus_card_t * gus, unsigned char reg, unsigned int addr, short w_16bit);
+extern unsigned int snd_gf1_i_read_addr(snd_gus_card_t * gus, unsigned char reg, short w_16bit);
+
+extern void snd_gf1_select_active_voices(snd_gus_card_t * gus);
+
+/* gus_lfo.c */
+
+struct _SND_IW_LFO_PROGRAM {
+	unsigned short freq_and_control;
+	unsigned char depth_final;
+	unsigned char depth_inc;
+	unsigned short twave;
+	unsigned short depth;
+};
+
+#if 0
+extern void snd_gf1_lfo_effect_interrupt(snd_gus_card_t * gus, snd_gf1_voice_t * voice);
+#endif
+extern void snd_gf1_lfo_init(snd_gus_card_t * gus);
+extern void snd_gf1_lfo_done(snd_gus_card_t * gus);
+extern void snd_gf1_lfo_program(snd_gus_card_t * gus, int voice, int lfo_type, struct _SND_IW_LFO_PROGRAM *program);
+extern void snd_gf1_lfo_enable(snd_gus_card_t * gus, int voice, int lfo_type);
+extern void snd_gf1_lfo_disable(snd_gus_card_t * gus, int voice, int lfo_type);
+extern void snd_gf1_lfo_change_freq(snd_gus_card_t * gus, int voice, int lfo_type, int freq);
+extern void snd_gf1_lfo_change_depth(snd_gus_card_t * gus, int voice, int lfo_type, int depth);
+extern void snd_gf1_lfo_setup(snd_gus_card_t * gus, int voice, int lfo_type, int freq, int current_depth, int depth, int sweep, int shape);
+extern void snd_gf1_lfo_shutdown(snd_gus_card_t * gus, int voice, int lfo_type);
+#if 0
+extern void snd_gf1_lfo_command(snd_gus_card_t * gus, int voice, unsigned char *command);
+#endif
+
+/* gus_mem.c */
+
+void snd_gf1_mem_lock(snd_gf1_mem_t * alloc, int xup);
+int snd_gf1_mem_xfree(snd_gf1_mem_t * alloc, snd_gf1_mem_block_t * block);
+snd_gf1_mem_block_t *snd_gf1_mem_look(snd_gf1_mem_t * alloc,
+				      unsigned int address);
+snd_gf1_mem_block_t *snd_gf1_mem_share(snd_gf1_mem_t * alloc,
+				       unsigned int *share_id);
+snd_gf1_mem_block_t *snd_gf1_mem_alloc(snd_gf1_mem_t * alloc, int owner,
+				       char *name, int size, int w_16,
+				       int align, unsigned int *share_id);
+int snd_gf1_mem_free(snd_gf1_mem_t * alloc, unsigned int address);
+int snd_gf1_mem_free_owner(snd_gf1_mem_t * alloc, int owner);
+int snd_gf1_mem_init(snd_gus_card_t * gus);
+int snd_gf1_mem_done(snd_gus_card_t * gus);
+
+/* gus_mem_proc.c */
+
+int snd_gf1_mem_proc_init(snd_gus_card_t * gus);
+int snd_gf1_mem_proc_done(snd_gus_card_t * gus);
+
+/* gus_dma.c */
+
+void snd_gf1_dma_program(snd_gus_card_t * gus, unsigned int addr,
+			 const void *buf, unsigned int count,
+			 unsigned int cmd);
+void snd_gf1_dma_ack(snd_gus_card_t * gus);
+int snd_gf1_dma_init(snd_gus_card_t * gus);
+int snd_gf1_dma_done(snd_gus_card_t * gus);
+int snd_gf1_dma_transfer_block(snd_gus_card_t * gus,
+			       snd_gf1_dma_block_t * block,
+			       int atomic,
+			       int synth);
+
+/* gus_volume.c */
+
+unsigned short snd_gf1_lvol_to_gvol_raw(unsigned int vol);
+unsigned int snd_gf1_gvol_to_lvol_raw(unsigned short gf1_vol);
+unsigned int snd_gf1_calc_ramp_rate(snd_gus_card_t * gus,
+				    unsigned short start,
+				    unsigned short end,
+				    unsigned int us);
+unsigned short snd_gf1_translate_freq(snd_gus_card_t * gus, unsigned int freq2);
+unsigned short snd_gf1_compute_pitchbend(unsigned short pitchbend, unsigned short sens);
+unsigned short snd_gf1_compute_freq(unsigned int freq,
+				    unsigned int rate,
+				    unsigned short mix_rate);
+
+/* gus_reset.c */
+
+void snd_gf1_set_default_handlers(snd_gus_card_t * gus, unsigned int what);
+void snd_gf1_smart_stop_voice(snd_gus_card_t * gus, unsigned short voice);
+void snd_gf1_stop_voice(snd_gus_card_t * gus, unsigned short voice);
+void snd_gf1_clear_voices(snd_gus_card_t * gus, unsigned short v_min, unsigned short v_max);
+void snd_gf1_stop_voices(snd_gus_card_t * gus, unsigned short v_min, unsigned short v_max);
+snd_gus_voice_t *snd_gf1_alloc_voice(snd_gus_card_t * gus, int type, int client, int port);
+void snd_gf1_free_voice(snd_gus_card_t * gus, snd_gus_voice_t *voice);
+int snd_gf1_start(snd_gus_card_t * gus);
+int snd_gf1_stop(snd_gus_card_t * gus);
+
+/* gus_mixer.c */
+
+int snd_gf1_new_mixer(snd_gus_card_t * gus);
+
+/* gus_pcm.c */
+
+int snd_gf1_pcm_new(snd_gus_card_t * gus, int pcm_dev, int control_index, snd_pcm_t ** rpcm);
+
+#ifdef CONFIG_SND_DEBUG
+extern void snd_gf1_print_voice_registers(snd_gus_card_t * gus);
+extern void snd_gf1_print_global_registers(snd_gus_card_t * gus);
+extern void snd_gf1_print_setup_registers(snd_gus_card_t * gus);
+extern void snd_gf1_peek_print_block(snd_gus_card_t * gus, unsigned int addr, int count, int w_16bit);
+#endif
+
+/* gus.c */
+
+int snd_gus_use_inc(snd_gus_card_t * gus);
+void snd_gus_use_dec(snd_gus_card_t * gus);
+int snd_gus_create(snd_card_t * card,
+		   unsigned long port,
+		   int irq, int dma1, int dma2,
+		   int timer_dev,
+		   int voices,
+		   int pcm_channels,
+		   int effect,
+		   snd_gus_card_t ** rgus);
+int snd_gus_initialize(snd_gus_card_t * gus);
+
+/* gus_irq.c */
+
+void snd_gus_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+#ifdef CONFIG_SND_DEBUG
+void snd_gus_irq_profile_init(snd_gus_card_t *gus);
+void snd_gus_irq_profile_done(snd_gus_card_t *gus);
+#endif
+
+/* gus_uart.c */
+
+int snd_gf1_rawmidi_new(snd_gus_card_t * gus, int device, snd_rawmidi_t **rrawmidi);
+
+#if 0
+extern void snd_engine_instrument_register(unsigned short mode,
+		struct _SND_INSTRUMENT_VOICE_COMMANDS *voice_cmds,
+		struct _SND_INSTRUMENT_NOTE_COMMANDS *note_cmds,
+	      	struct _SND_INSTRUMENT_CHANNEL_COMMANDS *channel_cmds);
+extern int snd_engine_instrument_register_ask(unsigned short mode);
+#endif
+
+/* gus_dram.c */
+int snd_gus_dram_write(snd_gus_card_t *gus, char *ptr,
+		       unsigned int addr, unsigned int size);
+int snd_gus_dram_read(snd_gus_card_t *gus, char *ptr,
+		      unsigned int addr, unsigned int size, int rom);
+
+#ifdef CONFIG_SND_SEQUENCER
+
+/* gus_sample.c */
+void snd_gus_sample_event(snd_seq_event_t *ev, snd_gus_port_t *p);
+
+/* gus_simple.c */
+void snd_gf1_simple_init(snd_gus_voice_t *voice);
+
+/* gus_instr.c */
+int snd_gus_iwffff_put_sample(void *private_data, iwffff_wave_t *wave,
+			      char *data, long len, int atomic);
+int snd_gus_iwffff_get_sample(void *private_data, iwffff_wave_t *wave,
+			      char *data, long len, int atomic);
+int snd_gus_iwffff_remove_sample(void *private_data, iwffff_wave_t *wave,
+				 int atomic);
+int snd_gus_gf1_put_sample(void *private_data, gf1_wave_t *wave,
+			   char *data, long len, int atomic);
+int snd_gus_gf1_get_sample(void *private_data, gf1_wave_t *wave,
+			   char *data, long len, int atomic);
+int snd_gus_gf1_remove_sample(void *private_data, gf1_wave_t *wave,
+			      int atomic);
+int snd_gus_simple_put_sample(void *private_data, simple_instrument_t *instr,
+			      char *data, long len, int atomic);
+int snd_gus_simple_get_sample(void *private_data, simple_instrument_t *instr,
+			      char *data, long len, int atomic);
+int snd_gus_simple_remove_sample(void *private_data, simple_instrument_t *instr,
+				 int atomic);
+
+#endif				/* CONFIG_SND_SEQUENCER */
+
+#endif				/* __GUS_H */
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/gus.ho
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/gus.ho	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/gus.ho	(revision 91)
@@ -0,0 +1,728 @@
+#ifndef __GUS_H
+#define __GUS_H
+
+/*
+ *  Global structures used for GUS part of ALSA driver
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "pcm.h"
+#include "rawmidi.h"
+#include "timer.h"
+#include "seq_midi_emul.h"
+#include "seq_device.h"
+#include "ainstr_iw.h"
+#include "ainstr_gf1.h"
+#include "ainstr_simple.h"
+
+#define SNDRV_SEQ_DEV_ID_GUS			"synth-gus"
+
+/* IO ports */
+
+#define GUSP(gus, x)			((gus)->gf1.port + SNDRV_g_u_s_##x)
+
+#define SNDRV_g_u_s_MIDICTRL		(0x320-0x220)
+#define SNDRV_g_u_s_MIDISTAT		(0x320-0x220)
+#define SNDRV_g_u_s_MIDIDATA		(0x321-0x220)
+
+#define SNDRV_g_u_s_GF1PAGE		(0x322-0x220)
+#define SNDRV_g_u_s_GF1REGSEL		(0x323-0x220)
+#define SNDRV_g_u_s_GF1DATALOW		(0x324-0x220)
+#define SNDRV_g_u_s_GF1DATAHIGH		(0x325-0x220)
+#define SNDRV_g_u_s_IRQSTAT		(0x226-0x220)
+#define SNDRV_g_u_s_TIMERCNTRL		(0x228-0x220)
+#define SNDRV_g_u_s_TIMERDATA		(0x229-0x220)
+#define SNDRV_g_u_s_DRAM			(0x327-0x220)
+#define SNDRV_g_u_s_MIXCNTRLREG		(0x220-0x220)
+#define SNDRV_g_u_s_IRQDMACNTRLREG	(0x22b-0x220)
+#define SNDRV_g_u_s_REGCNTRLS		(0x22f-0x220)
+#define SNDRV_g_u_s_BOARDVERSION		(0x726-0x220)
+#define SNDRV_g_u_s_MIXCNTRLPORT		(0x726-0x220)
+#define SNDRV_g_u_s_IVER			(0x325-0x220)
+#define SNDRV_g_u_s_MIXDATAPORT		(0x326-0x220)
+#define SNDRV_g_u_s_MAXCNTRLPORT		(0x326-0x220)
+
+/* GF1 registers */
+
+/* global registers */
+#define SNDRV_GF1_GB_ACTIVE_VOICES		0x0e
+#define SNDRV_GF1_GB_VOICES_IRQ			0x0f
+#define SNDRV_GF1_GB_GLOBAL_MODE			0x19
+#define SNDRV_GF1_GW_LFO_BASE			0x1a
+#define SNDRV_GF1_GB_VOICES_IRQ_READ		0x1f
+#define SNDRV_GF1_GB_DRAM_DMA_CONTROL		0x41
+#define SNDRV_GF1_GW_DRAM_DMA_LOW			0x42
+#define SNDRV_GF1_GW_DRAM_IO_LOW			0x43
+#define SNDRV_GF1_GB_DRAM_IO_HIGH			0x44
+#define SNDRV_GF1_GB_SOUND_BLASTER_CONTROL	0x45
+#define SNDRV_GF1_GB_ADLIB_TIMER_1		0x46
+#define SNDRV_GF1_GB_ADLIB_TIMER_2		0x47
+#define SNDRV_GF1_GB_RECORD_RATE			0x48
+#define SNDRV_GF1_GB_REC_DMA_CONTROL		0x49
+#define SNDRV_GF1_GB_JOYSTICK_DAC_LEVEL		0x4b
+#define SNDRV_GF1_GB_RESET			0x4c
+#define SNDRV_GF1_GB_DRAM_DMA_HIGH		0x50
+#define SNDRV_GF1_GW_DRAM_IO16			0x51
+#define SNDRV_GF1_GW_MEMORY_CONFIG		0x52
+#define SNDRV_GF1_GB_MEMORY_CONTROL		0x53
+#define SNDRV_GF1_GW_FIFO_RECORD_BASE_ADDR	0x54
+#define SNDRV_GF1_GW_FIFO_PLAY_BASE_ADDR		0x55
+#define SNDRV_GF1_GW_FIFO_SIZE			0x56
+#define SNDRV_GF1_GW_INTERLEAVE			0x57
+#define SNDRV_GF1_GB_COMPATIBILITY		0x59
+#define SNDRV_GF1_GB_DECODE_CONTROL		0x5a
+#define SNDRV_GF1_GB_VERSION_NUMBER		0x5b
+#define SNDRV_GF1_GB_MPU401_CONTROL_A		0x5c
+#define SNDRV_GF1_GB_MPU401_CONTROL_B		0x5d
+#define SNDRV_GF1_GB_EMULATION_IRQ		0x60
+/* voice specific registers */
+#define SNDRV_GF1_VB_ADDRESS_CONTROL		0x00
+#define SNDRV_GF1_VW_FREQUENCY			0x01
+#define SNDRV_GF1_VW_START_HIGH			0x02
+#define SNDRV_GF1_VW_START_LOW			0x03
+#define SNDRV_GF1_VA_START			SNDRV_GF1_VW_START_HIGH
+#define SNDRV_GF1_VW_END_HIGH			0x04
+#define SNDRV_GF1_VW_END_LOW			0x05
+#define SNDRV_GF1_VA_END				SNDRV_GF1_VW_END_HIGH
+#define SNDRV_GF1_VB_VOLUME_RATE			0x06
+#define SNDRV_GF1_VB_VOLUME_START			0x07
+#define SNDRV_GF1_VB_VOLUME_END			0x08
+#define SNDRV_GF1_VW_VOLUME			0x09
+#define SNDRV_GF1_VW_CURRENT_HIGH			0x0a
+#define SNDRV_GF1_VW_CURRENT_LOW			0x0b
+#define SNDRV_GF1_VA_CURRENT			SNDRV_GF1_VW_CURRENT_HIGH
+#define SNDRV_GF1_VB_PAN				0x0c
+#define SNDRV_GF1_VW_OFFSET_RIGHT			0x0c
+#define SNDRV_GF1_VB_VOLUME_CONTROL		0x0d
+#define SNDRV_GF1_VB_UPPER_ADDRESS		0x10
+#define SNDRV_GF1_VW_EFFECT_HIGH			0x11
+#define SNDRV_GF1_VW_EFFECT_LOW			0x12
+#define SNDRV_GF1_VA_EFFECT			SNDRV_GF1_VW_EFFECT_HIGH
+#define SNDRV_GF1_VW_OFFSET_LEFT			0x13
+#define SNDRV_GF1_VB_ACCUMULATOR			0x14
+#define SNDRV_GF1_VB_MODE				0x15
+#define SNDRV_GF1_VW_EFFECT_VOLUME		0x16
+#define SNDRV_GF1_VB_FREQUENCY_LFO		0x17
+#define SNDRV_GF1_VB_VOLUME_LFO			0x18
+#define SNDRV_GF1_VW_OFFSET_RIGHT_FINAL		0x1b
+#define SNDRV_GF1_VW_OFFSET_LEFT_FINAL		0x1c
+#define SNDRV_GF1_VW_EFFECT_VOLUME_FINAL		0x1d
+
+/* ICS registers */
+
+#define SNDRV_ICS_MIC_DEV		0
+#define SNDRV_ICS_LINE_DEV	1
+#define SNDRV_ICS_CD_DEV		2
+#define SNDRV_ICS_GF1_DEV		3
+#define SNDRV_ICS_NONE_DEV	4
+#define SNDRV_ICS_MASTER_DEV	5
+
+/* LFO */
+
+#define SNDRV_LFO_TREMOLO		0
+#define SNDRV_LFO_VIBRATO		1
+
+/* misc */
+
+#define SNDRV_GF1_DMA_UNSIGNED	0x80
+#define SNDRV_GF1_DMA_16BIT	0x40
+#define SNDRV_GF1_DMA_IRQ		0x20
+#define SNDRV_GF1_DMA_WIDTH16	0x04
+#define SNDRV_GF1_DMA_READ	0x02	/* read from GUS's DRAM */
+#define SNDRV_GF1_DMA_ENABLE	0x01
+
+/* ramp ranges */
+
+#define SNDRV_GF1_ATTEN(x)	(snd_gf1_atten_table[x])
+#define SNDRV_GF1_MIN_VOLUME	1800
+#define SNDRV_GF1_MAX_VOLUME	4095
+#define SNDRV_GF1_MIN_OFFSET	(SNDRV_GF1_MIN_VOLUME>>4)
+#define SNDRV_GF1_MAX_OFFSET	255
+#define SNDRV_GF1_MAX_TDEPTH	90
+
+/* defines for memory manager */
+
+#define SNDRV_GF1_MEM_BLOCK_16BIT		0x0001
+
+#define SNDRV_GF1_MEM_OWNER_DRIVER	0x0001
+#define SNDRV_GF1_MEM_OWNER_WAVE_SIMPLE	0x0002
+#define SNDRV_GF1_MEM_OWNER_WAVE_GF1	0x0003
+#define SNDRV_GF1_MEM_OWNER_WAVE_IWFFFF	0x0004
+
+/* constants for interrupt handlers */
+
+#define SNDRV_GF1_HANDLER_MIDI_OUT	0x00010000
+#define SNDRV_GF1_HANDLER_MIDI_IN		0x00020000
+#define SNDRV_GF1_HANDLER_TIMER1		0x00040000
+#define SNDRV_GF1_HANDLER_TIMER2		0x00080000
+#define SNDRV_GF1_HANDLER_VOICE		0x00100000
+#define SNDRV_GF1_HANDLER_DMA_WRITE	0x00200000
+#define SNDRV_GF1_HANDLER_DMA_READ	0x00400000
+#define SNDRV_GF1_HANDLER_ALL		(0xffff0000&~SNDRV_GF1_HANDLER_VOICE)
+
+/* constants for DMA flags */
+
+#define SNDRV_GF1_DMA_TRIGGER		1
+
+/* --- */
+
+struct _snd_gus_card;
+typedef struct _snd_gus_card snd_gus_card_t;
+
+/* GF1 specific structure */
+
+typedef struct _snd_gf1_bank_info {
+	unsigned int address;
+	unsigned int size;
+} snd_gf1_bank_info_t;
+
+typedef struct _snd_gf1_mem_block {
+	unsigned short flags;	/* flags - SNDRV_GF1_MEM_BLOCK_XXXX */
+	unsigned short owner;	/* owner - SNDRV_GF1_MEM_OWNER_XXXX */
+	unsigned int share;	/* share count */
+	unsigned int share_id[4]; /* share ID */
+	unsigned int ptr;
+	unsigned int size;
+	char *name;
+	struct _snd_gf1_mem_block *next;
+	struct _snd_gf1_mem_block *prev;
+} snd_gf1_mem_block_t;
+
+typedef struct _snd_gf1_mem {
+	snd_gf1_bank_info_t banks_8[4];
+	snd_gf1_bank_info_t banks_16[4];
+	snd_gf1_mem_block_t *first;
+	snd_gf1_mem_block_t *last;
+	snd_info_entry_t *info_entry;
+	struct semaphore memory_mutex;
+} snd_gf1_mem_t;
+
+typedef struct snd_gf1_dma_block {
+	void *buffer;		/* buffer in computer's RAM */
+	unsigned int addr;	/* address in onboard memory */
+	unsigned int count;	/* count in bytes */
+	unsigned int cmd;	/* DMA command (format) */
+	void (*ack)(snd_gus_card_t * gus, void *private_data);
+	void *private_data;
+	struct snd_gf1_dma_block *next;
+} snd_gf1_dma_block_t;
+
+typedef struct {
+	snd_midi_channel_set_t * chset;
+	snd_gus_card_t * gus;
+	int mode;		/* operation mode */
+	int client;		/* sequencer client number */
+	int port;		/* sequencer port number */
+	int midi_has_voices: 1;
+} snd_gus_port_t;
+
+typedef struct _snd_gus_voice snd_gus_voice_t;
+
+typedef struct {
+	void (*sample_start)(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_position_t position);
+	void (*sample_stop)(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_stop_mode_t mode);
+	void (*sample_freq)(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_frequency_t freq);
+	void (*sample_volume)(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_ev_volume_t *volume);
+	void (*sample_loop)(snd_gus_card_t *card, snd_gus_voice_t *voice, snd_seq_ev_loop_t *loop);
+	void (*sample_pos)(snd_gus_card_t *card, snd_gus_voice_t *voice, snd_seq_position_t position);
+	void (*sample_private1)(snd_gus_card_t *card, snd_gus_voice_t *voice, unsigned char *data);
+} snd_gus_sample_ops_t;
+
+#define SNDRV_GF1_VOICE_TYPE_PCM		0
+#define SNDRV_GF1_VOICE_TYPE_SYNTH 	1
+#define SNDRV_GF1_VOICE_TYPE_MIDI		2
+
+#define SNDRV_GF1_VFLG_RUNNING		(1<<0)
+#define SNDRV_GF1_VFLG_EFFECT_TIMER1	(1<<1)
+#define SNDRV_GF1_VFLG_PAN		(1<<2)
+
+typedef enum {
+	VENV_BEFORE,
+	VENV_ATTACK,
+	VENV_SUSTAIN,
+	VENV_RELEASE,
+	VENV_DONE,
+	VENV_VOLUME
+} snd_gus_volume_state_t;
+
+struct _snd_gus_voice {
+	int number;
+	int use: 1,
+	    pcm: 1,
+	    synth:1,
+	    midi: 1;
+	unsigned int flags;
+	unsigned char client;
+	unsigned char port;
+	unsigned char index;
+	unsigned char pad;
+	
+#ifdef CONFIG_SND_DEBUG
+	unsigned int interrupt_stat_wave;
+	unsigned int interrupt_stat_volume;
+#endif
+	void (*handler_wave) (snd_gus_card_t * gus, snd_gus_voice_t * voice);
+	void (*handler_volume) (snd_gus_card_t * gus, snd_gus_voice_t * voice);
+	void (*handler_effect) (snd_gus_card_t * gus, snd_gus_voice_t * voice);
+	void (*volume_change) (snd_gus_card_t * gus);
+
+	snd_gus_sample_ops_t *sample_ops;
+
+	snd_seq_instr_t instr;
+
+	/* running status / registers */
+
+	snd_seq_ev_volume_t sample_volume;
+
+	unsigned short fc_register;
+	unsigned short fc_lfo;
+	unsigned short gf1_volume;
+	unsigned char control;
+	unsigned char mode;
+	unsigned char gf1_pan;
+	unsigned char effect_accumulator;
+	unsigned char volume_control;
+	unsigned char venv_value_next;
+	snd_gus_volume_state_t venv_state;
+	snd_gus_volume_state_t venv_state_prev;
+	unsigned short vlo;
+	unsigned short vro;
+	unsigned short gf1_effect_volume;
+	
+	/* --- */
+
+	void *private_data;
+	void (*private_free)(snd_gus_voice_t *voice);
+};
+
+struct _snd_gf1 {
+
+	unsigned int enh_mode:1,	/* enhanced mode (GFA1) */
+		     hw_lfo:1,		/* use hardware LFO */
+		     sw_lfo:1,		/* use software LFO */
+		     effect:1;		/* use effect voices */
+
+	unsigned long port;		/* port of GF1 chip */
+	struct resource *res_port1;
+	struct resource *res_port2;
+	int irq;			/* IRQ number */
+	int dma1;			/* DMA1 number */
+	int dma2;			/* DMA2 number */
+	unsigned int memory;		/* GUS's DRAM size in bytes */
+	unsigned int rom_memory;	/* GUS's ROM size in bytes */
+	unsigned int rom_present;	/* bitmask */
+	unsigned int rom_banks;		/* GUS's ROM banks */
+
+	snd_gf1_mem_t mem_alloc;
+	snd_info_entry_t *ram_entries[4];
+	snd_info_entry_t *rom_entries[4];
+
+	/* registers */
+	unsigned short reg_page;
+	unsigned short reg_regsel;
+	unsigned short reg_data8;
+	unsigned short reg_data16;
+	unsigned short reg_irqstat;
+	unsigned short reg_dram;
+	unsigned short reg_timerctrl;
+	unsigned short reg_timerdata;
+	unsigned char ics_regs[6][2];
+	/* --------- */
+
+	unsigned char active_voices;	/* active voices */
+	unsigned char active_voice;	/* selected voice (GF1PAGE register) */
+
+	snd_gus_voice_t voices[32];	/* GF1 voices */
+
+	unsigned int default_voice_address;
+
+	unsigned short playback_freq;	/* GF1 playback (mixing) frequency */
+	unsigned short mode;		/* see to SNDRV_GF1_MODE_XXXX */
+	unsigned char volume_ramp;
+	unsigned char smooth_pan;
+	unsigned char full_range_pan;
+	unsigned char pad0;
+
+	unsigned char *lfos;
+
+	/* interrupt handlers */
+
+	void (*interrupt_handler_midi_out) (snd_gus_card_t * gus);
+	void (*interrupt_handler_midi_in) (snd_gus_card_t * gus);
+	void (*interrupt_handler_timer1) (snd_gus_card_t * gus);
+	void (*interrupt_handler_timer2) (snd_gus_card_t * gus);
+	void (*interrupt_handler_dma_write) (snd_gus_card_t * gus);
+	void (*interrupt_handler_dma_read) (snd_gus_card_t * gus);
+
+#ifdef CONFIG_SND_DEBUG
+	unsigned int interrupt_stat_midi_out;
+	unsigned int interrupt_stat_midi_in;
+	unsigned int interrupt_stat_timer1;
+	unsigned int interrupt_stat_timer2;
+	unsigned int interrupt_stat_dma_write;
+	unsigned int interrupt_stat_dma_read;
+	unsigned int interrupt_stat_voice_lost;
+#endif
+
+	/* synthesizer */
+
+	int seq_client;
+	snd_gus_port_t seq_ports[4];
+	snd_seq_kinstr_list_t *ilist;
+	snd_iwffff_ops_t iwffff_ops;
+	snd_gf1_ops_t gf1_ops;
+	snd_simple_ops_t simple_ops;
+
+	/* timer */
+
+	unsigned short timer_enabled;
+	snd_timer_t *timer1;
+	snd_timer_t *timer2;
+
+	/* midi */
+
+	unsigned short uart_cmd;
+	unsigned int uart_framing;
+	unsigned int uart_overrun;
+
+	/* dma operations */
+
+	unsigned int dma_flags;
+	unsigned int dma_shared;
+	snd_gf1_dma_block_t *dma_data_pcm;
+	snd_gf1_dma_block_t *dma_data_pcm_last;
+	snd_gf1_dma_block_t *dma_data_synth;
+	snd_gf1_dma_block_t *dma_data_synth_last;
+	void (*dma_ack)(snd_gus_card_t * gus, void *private_data);
+	void *dma_private_data;
+
+	/* pcm */
+	int pcm_channels;
+	int pcm_alloc_voices;
+        unsigned short pcm_volume_level_left;
+	unsigned short pcm_volume_level_right;
+	unsigned short pcm_volume_level_left1;
+	unsigned short pcm_volume_level_right1;
+                                
+	unsigned char pcm_rcntrl_reg;
+	unsigned char pad_end;
+};
+
+/* main structure for GUS card */
+
+struct _snd_gus_card {
+	snd_card_t *card;
+
+	unsigned int
+	 initialized: 1,		/* resources were initialized */
+	 equal_irq:1,			/* GF1 and CODEC shares IRQ (GUS MAX only) */
+	 equal_dma:1,			/* if dma channels are equal (not valid for daughter board) */
+	 ics_flag:1,			/* have we ICS mixer chip */
+	 ics_flipped:1,			/* ICS mixer have flipped some channels? */
+	 codec_flag:1,			/* have we CODEC chip? */
+	 max_flag:1,			/* have we GUS MAX card? */
+	 max_ctrl_flag:1,		/* have we original GUS MAX card? */
+	 daughter_flag:1,		/* have we daughter board? */
+	 interwave:1,			/* hey - we have InterWave card */
+	 ess_flag:1,			/* ESS chip found... GUS Extreme */
+	 ace_flag:1,			/* GUS ACE detected */
+	 uart_enable:1;			/* enable MIDI UART */
+	unsigned short revision;	/* revision of chip */
+	unsigned short max_cntrl_val;	/* GUS MAX control value */
+	unsigned short mix_cntrl_reg;	/* mixer control register */
+	unsigned short joystick_dac;	/* joystick DAC level */
+	int timer_dev;			/* timer device */
+
+	struct _snd_gf1 gf1;	/* gf1 specific variables */
+#ifdef CONFIG_SND_DEBUG
+	snd_info_entry_t *irq_entry;
+#endif
+	snd_pcm_t *pcm;
+	snd_pcm_substream_t *pcm_cap_substream;
+	unsigned int c_dma_size;
+	unsigned int c_period_size;
+	unsigned int c_pos;
+
+	snd_rawmidi_t *midi_uart;
+	snd_rawmidi_substream_t *midi_substream_output;
+	snd_rawmidi_substream_t *midi_substream_input;
+
+	snd_seq_device_t *seq_dev;
+
+	spinlock_t reg_lock;
+	spinlock_t voice_alloc;
+	spinlock_t active_voice_lock;
+	spinlock_t event_lock;
+	spinlock_t dma_lock;
+	spinlock_t pcm_volume_level_lock;
+	spinlock_t uart_cmd_lock;
+	struct semaphore dma_mutex;
+	struct semaphore register_mutex;
+};
+
+/* I/O functions for GF1/InterWave chip - gus_io.c */
+
+static inline void snd_gf1_select_voice(snd_gus_card_t * gus, int voice)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&gus->active_voice_lock, flags);
+	if (voice != gus->gf1.active_voice) {
+		gus->gf1.active_voice = voice;
+		outb(voice, GUSP(gus, GF1PAGE));
+	}
+	spin_unlock_irqrestore(&gus->active_voice_lock, flags);
+}
+
+static inline void snd_gf1_uart_cmd(snd_gus_card_t * gus, unsigned char b)
+{
+	outb(gus->gf1.uart_cmd = b, GUSP(gus, MIDICTRL));
+}
+
+static inline unsigned char snd_gf1_uart_stat(snd_gus_card_t * gus)
+{
+	return inb(GUSP(gus, MIDISTAT));
+}
+
+static inline void snd_gf1_uart_put(snd_gus_card_t * gus, unsigned char b)
+{
+	outb(b, GUSP(gus, MIDIDATA));
+}
+
+static inline unsigned char snd_gf1_uart_get(snd_gus_card_t * gus)
+{
+	return inb(GUSP(gus, MIDIDATA));
+}
+
+extern void snd_gf1_delay(snd_gus_card_t * gus);
+
+extern void snd_gf1_ctrl_stop(snd_gus_card_t * gus, unsigned char reg);
+
+extern void snd_gf1_write8(snd_gus_card_t * gus, unsigned char reg, unsigned char data);
+extern unsigned char snd_gf1_look8(snd_gus_card_t * gus, unsigned char reg);
+extern inline unsigned char snd_gf1_read8(snd_gus_card_t * gus, unsigned char reg)
+{
+	return snd_gf1_look8(gus, reg | 0x80);
+}
+extern void snd_gf1_write16(snd_gus_card_t * gus, unsigned char reg, unsigned int data);
+extern unsigned short snd_gf1_look16(snd_gus_card_t * gus, unsigned char reg);
+extern inline unsigned short snd_gf1_read16(snd_gus_card_t * gus, unsigned char reg)
+{
+	return snd_gf1_look16(gus, reg | 0x80);
+}
+extern void snd_gf1_adlib_write(snd_gus_card_t * gus, unsigned char reg, unsigned char data);
+extern void snd_gf1_dram_addr(snd_gus_card_t * gus, unsigned int addr);
+extern void snd_gf1_poke(snd_gus_card_t * gus, unsigned int addr, unsigned char data);
+extern unsigned char snd_gf1_peek(snd_gus_card_t * gus, unsigned int addr);
+extern void snd_gf1_pokew(snd_gus_card_t * gus, unsigned int addr, unsigned short data);
+extern unsigned short snd_gf1_peekw(snd_gus_card_t * gus, unsigned int addr);
+extern void snd_gf1_dram_setmem(snd_gus_card_t * gus, unsigned int addr, unsigned short value, unsigned int count);
+extern void snd_gf1_write_addr(snd_gus_card_t * gus, unsigned char reg, unsigned int addr, short w_16bit);
+extern unsigned int snd_gf1_read_addr(snd_gus_card_t * gus, unsigned char reg, short w_16bit);
+extern void snd_gf1_i_ctrl_stop(snd_gus_card_t * gus, unsigned char reg);
+extern void snd_gf1_i_write8(snd_gus_card_t * gus, unsigned char reg, unsigned char data);
+extern unsigned char snd_gf1_i_look8(snd_gus_card_t * gus, unsigned char reg);
+extern void snd_gf1_i_write16(snd_gus_card_t * gus, unsigned char reg, unsigned int data);
+extern inline unsigned char snd_gf1_i_read8(snd_gus_card_t * gus, unsigned char reg)
+{
+	return snd_gf1_i_look8(gus, reg | 0x80);
+}
+extern unsigned short snd_gf1_i_look16(snd_gus_card_t * gus, unsigned char reg);
+extern inline unsigned short snd_gf1_i_read16(snd_gus_card_t * gus, unsigned char reg)
+{
+	return snd_gf1_i_look16(gus, reg | 0x80);
+}
+extern void snd_gf1_i_adlib_write(snd_gus_card_t * gus, unsigned char reg, unsigned char data);
+extern void snd_gf1_i_write_addr(snd_gus_card_t * gus, unsigned char reg, unsigned int addr, short w_16bit);
+extern unsigned int snd_gf1_i_read_addr(snd_gus_card_t * gus, unsigned char reg, short w_16bit);
+
+extern void snd_gf1_select_active_voices(snd_gus_card_t * gus);
+
+/* gus_lfo.c */
+
+struct _SND_IW_LFO_PROGRAM {
+	unsigned short freq_and_control;
+	unsigned char depth_final;
+	unsigned char depth_inc;
+	unsigned short twave;
+	unsigned short depth;
+};
+
+#if 0
+extern void snd_gf1_lfo_effect_interrupt(snd_gus_card_t * gus, snd_gf1_voice_t * voice);
+#endif
+extern void snd_gf1_lfo_init(snd_gus_card_t * gus);
+extern void snd_gf1_lfo_done(snd_gus_card_t * gus);
+extern void snd_gf1_lfo_program(snd_gus_card_t * gus, int voice, int lfo_type, struct _SND_IW_LFO_PROGRAM *program);
+extern void snd_gf1_lfo_enable(snd_gus_card_t * gus, int voice, int lfo_type);
+extern void snd_gf1_lfo_disable(snd_gus_card_t * gus, int voice, int lfo_type);
+extern void snd_gf1_lfo_change_freq(snd_gus_card_t * gus, int voice, int lfo_type, int freq);
+extern void snd_gf1_lfo_change_depth(snd_gus_card_t * gus, int voice, int lfo_type, int depth);
+extern void snd_gf1_lfo_setup(snd_gus_card_t * gus, int voice, int lfo_type, int freq, int current_depth, int depth, int sweep, int shape);
+extern void snd_gf1_lfo_shutdown(snd_gus_card_t * gus, int voice, int lfo_type);
+#if 0
+extern void snd_gf1_lfo_command(snd_gus_card_t * gus, int voice, unsigned char *command);
+#endif
+
+/* gus_mem.c */
+
+void snd_gf1_mem_lock(snd_gf1_mem_t * alloc, int xup);
+int snd_gf1_mem_xfree(snd_gf1_mem_t * alloc, snd_gf1_mem_block_t * block);
+snd_gf1_mem_block_t *snd_gf1_mem_look(snd_gf1_mem_t * alloc,
+				      unsigned int address);
+snd_gf1_mem_block_t *snd_gf1_mem_share(snd_gf1_mem_t * alloc,
+				       unsigned int *share_id);
+snd_gf1_mem_block_t *snd_gf1_mem_alloc(snd_gf1_mem_t * alloc, int owner,
+				       char *name, int size, int w_16,
+				       int align, unsigned int *share_id);
+int snd_gf1_mem_free(snd_gf1_mem_t * alloc, unsigned int address);
+int snd_gf1_mem_free_owner(snd_gf1_mem_t * alloc, int owner);
+int snd_gf1_mem_init(snd_gus_card_t * gus);
+int snd_gf1_mem_done(snd_gus_card_t * gus);
+
+/* gus_mem_proc.c */
+
+int snd_gf1_mem_proc_init(snd_gus_card_t * gus);
+int snd_gf1_mem_proc_done(snd_gus_card_t * gus);
+
+/* gus_dma.c */
+
+void snd_gf1_dma_program(snd_gus_card_t * gus, unsigned int addr,
+			 const void *buf, unsigned int count,
+			 unsigned int cmd);
+void snd_gf1_dma_ack(snd_gus_card_t * gus);
+int snd_gf1_dma_init(snd_gus_card_t * gus);
+int snd_gf1_dma_done(snd_gus_card_t * gus);
+int snd_gf1_dma_transfer_block(snd_gus_card_t * gus,
+			       snd_gf1_dma_block_t * block,
+			       int atomic,
+			       int synth);
+
+/* gus_volume.c */
+
+unsigned short snd_gf1_lvol_to_gvol_raw(unsigned int vol);
+unsigned int snd_gf1_gvol_to_lvol_raw(unsigned short gf1_vol);
+unsigned int snd_gf1_calc_ramp_rate(snd_gus_card_t * gus,
+				    unsigned short start,
+				    unsigned short end,
+				    unsigned int us);
+unsigned short snd_gf1_translate_freq(snd_gus_card_t * gus, unsigned int freq2);
+unsigned short snd_gf1_compute_pitchbend(unsigned short pitchbend, unsigned short sens);
+unsigned short snd_gf1_compute_freq(unsigned int freq,
+				    unsigned int rate,
+				    unsigned short mix_rate);
+
+/* gus_reset.c */
+
+void snd_gf1_set_default_handlers(snd_gus_card_t * gus, unsigned int what);
+void snd_gf1_smart_stop_voice(snd_gus_card_t * gus, unsigned short voice);
+void snd_gf1_stop_voice(snd_gus_card_t * gus, unsigned short voice);
+void snd_gf1_clear_voices(snd_gus_card_t * gus, unsigned short v_min, unsigned short v_max);
+void snd_gf1_stop_voices(snd_gus_card_t * gus, unsigned short v_min, unsigned short v_max);
+snd_gus_voice_t *snd_gf1_alloc_voice(snd_gus_card_t * gus, int type, int client, int port);
+void snd_gf1_free_voice(snd_gus_card_t * gus, snd_gus_voice_t *voice);
+int snd_gf1_start(snd_gus_card_t * gus);
+int snd_gf1_stop(snd_gus_card_t * gus);
+
+/* gus_mixer.c */
+
+int snd_gf1_new_mixer(snd_gus_card_t * gus);
+
+/* gus_pcm.c */
+
+int snd_gf1_pcm_new(snd_gus_card_t * gus, int pcm_dev, int control_index, snd_pcm_t ** rpcm);
+
+#ifdef CONFIG_SND_DEBUG
+extern void snd_gf1_print_voice_registers(snd_gus_card_t * gus);
+extern void snd_gf1_print_global_registers(snd_gus_card_t * gus);
+extern void snd_gf1_print_setup_registers(snd_gus_card_t * gus);
+extern void snd_gf1_peek_print_block(snd_gus_card_t * gus, unsigned int addr, int count, int w_16bit);
+#endif
+
+/* gus.c */
+
+int snd_gus_use_inc(snd_gus_card_t * gus);
+void snd_gus_use_dec(snd_gus_card_t * gus);
+int snd_gus_create(snd_card_t * card,
+		   unsigned long port,
+		   int irq, int dma1, int dma2,
+		   int timer_dev,
+		   int voices,
+		   int pcm_channels,
+		   int effect,
+		   snd_gus_card_t ** rgus);
+int snd_gus_initialize(snd_gus_card_t * gus);
+
+/* gus_irq.c */
+
+void snd_gus_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+#ifdef CONFIG_SND_DEBUG
+void snd_gus_irq_profile_init(snd_gus_card_t *gus);
+void snd_gus_irq_profile_done(snd_gus_card_t *gus);
+#endif
+
+/* gus_uart.c */
+
+int snd_gf1_rawmidi_new(snd_gus_card_t * gus, int device, snd_rawmidi_t **rrawmidi);
+
+#if 0
+extern void snd_engine_instrument_register(unsigned short mode,
+		struct _SND_INSTRUMENT_VOICE_COMMANDS *voice_cmds,
+		struct _SND_INSTRUMENT_NOTE_COMMANDS *note_cmds,
+	      	struct _SND_INSTRUMENT_CHANNEL_COMMANDS *channel_cmds);
+extern int snd_engine_instrument_register_ask(unsigned short mode);
+#endif
+
+/* gus_dram.c */
+int snd_gus_dram_write(snd_gus_card_t *gus, char *ptr,
+		       unsigned int addr, unsigned int size);
+int snd_gus_dram_read(snd_gus_card_t *gus, char *ptr,
+		      unsigned int addr, unsigned int size, int rom);
+
+#ifdef CONFIG_SND_SEQUENCER
+
+/* gus_sample.c */
+void snd_gus_sample_event(snd_seq_event_t *ev, snd_gus_port_t *p);
+
+/* gus_simple.c */
+void snd_gf1_simple_init(snd_gus_voice_t *voice);
+
+/* gus_instr.c */
+int snd_gus_iwffff_put_sample(void *private_data, iwffff_wave_t *wave,
+			      char *data, long len, int atomic);
+int snd_gus_iwffff_get_sample(void *private_data, iwffff_wave_t *wave,
+			      char *data, long len, int atomic);
+int snd_gus_iwffff_remove_sample(void *private_data, iwffff_wave_t *wave,
+				 int atomic);
+int snd_gus_gf1_put_sample(void *private_data, gf1_wave_t *wave,
+			   char *data, long len, int atomic);
+int snd_gus_gf1_get_sample(void *private_data, gf1_wave_t *wave,
+			   char *data, long len, int atomic);
+int snd_gus_gf1_remove_sample(void *private_data, gf1_wave_t *wave,
+			      int atomic);
+int snd_gus_simple_put_sample(void *private_data, simple_instrument_t *instr,
+			      char *data, long len, int atomic);
+int snd_gus_simple_get_sample(void *private_data, simple_instrument_t *instr,
+			      char *data, long len, int atomic);
+int snd_gus_simple_remove_sample(void *private_data, simple_instrument_t *instr,
+				 int atomic);
+
+#endif				/* CONFIG_SND_SEQUENCER */
+
+#endif				/* __GUS_H */
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/hal2.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/hal2.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/hal2.h	(revision 91)
@@ -0,0 +1,405 @@
+#ifndef __HAL2_H
+#define __HAL2_H
+
+/*
+ *  Global structures used for HAL2 part of ALSA driver
+ *  Copyright (c) by Ulf Carlsson <ulfc@bun.falkenberg.se>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "pcm.h"
+#include "rawmidi.h"
+#include "timer.h"
+
+#include <asm/addrspace.h>
+#include <asm/sgi/sgihpc.h>
+
+/* We can possibly use the addresses in hpc_regs instead of giving the adress in
+ * an absolute way. This will at least save us if someone breaks the hpc_regs
+ * structure again :-)
+ */
+
+#define H2_HAL2_BASE		(HPC3_CHIP0_PBASE + 0x58000)
+#define H2_CTL_PIO		(H2_HAL2_BASE + 0 * 0x400)
+#define H2_AES_PIO		(H2_HAL2_BASE + 1 * 0x400)
+#define H2_VOL_PIO		(H2_HAL2_BASE + 2 * 0x400)
+#define H2_SYN_PIO		(H2_HAL2_BASE + 3 * 0x400)
+
+/* Indirect status register */
+
+#define H2_ISR_TSTATUS		0x01	/* RO: transaction status 1=busy */
+#define H2_ISR_USTATUS		0x02	/* RO: utime status bit 1=armed */
+#define H2_ISR_QUAD_MODE	0x04	/* codec mode 0=indigo 1=quad */
+#define H2_ISR_GLOBAL_RESET_N	0x08	/* chip global reset 0=reset */
+#define H2_ISR_CODEC_RESET_N	0x10	/* codec/synth reset 0=reset  */
+
+	/* Revision register */
+
+#define H2_REV_AUDIO_PRESENT	0x8000	/* RO: audio present 0=present */
+#define H2_REV_BOARD_M		0x7000	/* RO: bits 14:12, board revision */
+#define H2_REV_MAJOR_CHIP_M	0x00F0	/* RO: bits 7:4, major chip revision */
+#define H2_REV_MINOR_CHIP_M	0x000F	/* RO: bits 3:0, minor chip revision */
+
+/* Indirect address register */
+
+/*
+ * Address of indirect internal register to be accessed. A write to this
+ * register initiates read or write access to the indirect registers in the
+ * HAL2. Note that there af four indirect data registers for write access to
+ * registers larger than 16 byte.
+ */
+
+#define H2_IAR_TYPE_M		0xF000	/* bits 15:12, type of functional */
+					/* block the register resides in */
+					/* 1=DMA Port */
+					/* 9=Global DMA Control */
+					/* 2=Bresenham */
+					/* 3=Unix Timer */
+#define H2_IAR_NUM_M		0x0F00	/* bits 11:8 instance of the */
+					/* blockin which the indirect */
+					/* register resides */
+					/* If IAR_TYPE_M=DMA Port: */
+					/* 1=Synth In */
+					/* 2=AES In */
+					/* 3=AES Out */
+					/* 4=DAC Out */
+					/* 5=ADC Out */
+					/* 6=Synth Control */
+					/* If IAR_TYPE_M=Global DMA Control: */
+					/* 1=Control */
+					/* If IAR_TYPE_M=Bresenham: */
+					/* 1=Bresenham Clock Gen 1 */
+					/* 2=Bresenham Clock Gen 2 */
+					/* 3=Bresenham Clock Gen 3 */
+					/* If IAR_TYPE_M=Unix Timer: */
+					/* 1=Unix Timer */
+#define H2_IAR_ACCESS_SELECT	0x0080	/* 1=read 0=write */
+#define H2_IAR_PARAM		0x000C	/* Parameter Select */
+#define H2_IAR_RB_INDEX_M	0x0003	/* Read Back Index */
+					/* 00:word0 */
+					/* 01:word1 */
+					/* 10:word2 */
+					/* 11:word3 */
+/*
+ * HAL2 internal addressing
+ *
+ * The HAL2 has "indirect registers" (idr) which are accessed by writing to the
+ * Indirect Data registers. Write the address to the Indirect Address register
+ * to transfer the data.
+ *
+ * We define the H2IR_* to the read address and H2IW_* to the write address and
+ * H2I_* to be fields in whatever register is referred to.
+ *
+ * When we write to indirect registers which are larger than one word (16 bit)
+ * we have to fill more than one indirect register before writing. When we read
+ * back however we have to read several times, each time with different Read
+ * Back Indexes (there are defs for doing this easily).
+ */
+
+/*
+ * Relay Control
+ */
+#define H2I_RELAY_C		0x9100
+#define H2I_RELAY_C_STATE	0x01		/* state of RELAY pin signal */
+
+/* DMA port enable */
+
+#define H2I_DMA_PORT_EN		0x9104
+#define H2I_DMA_PORT_EN_SY_IN	0x01		/* Synth_in DMA port */
+#define H2I_DMA_PORT_EN_AESRX	0x02		/* AES receiver DMA port */
+#define H2I_DMA_PORT_EN_AESTX	0x04		/* AES transmitter DMA port */
+#define H2I_DMA_PORT_EN_CODECTX	0x08		/* CODEC transmit DMA port */
+#define H2I_DMA_PORT_EN_CODECR	0x10		/* CODEC receive DMA port */
+
+#define H2I_DMA_END		0x9108 		/* global dma endian select */
+#define H2I_DMA_END_SY_IN	0x01		/* Synth_in DMA port */
+#define H2I_DMA_END_AESRX	0x02		/* AES receiver DMA port */
+#define H2I_DMA_END_AESTX	0x04		/* AES transmitter DMA port */
+#define H2I_DMA_END_CODECTX	0x08		/* CODEC transmit DMA port */
+#define H2I_DMA_END_CODECR	0x10		/* CODEC receive DMA port */
+						/* 0=b_end 1=l_end */
+
+#define H2I_DMA_DRV		0x910C  	/* global PBUS DMA enable */
+
+#define H2I_SYNTH_C		0x1104		/* Synth DMA control */
+
+#define H2I_AESRX_C		0x1204	 	/* AES RX dma control */
+#define H2I_AESRX_C_TS_EN	0x20		/* timestamp enable */
+#define H2I_AESRX_C_TS_FMT	0x40		/* timestamp format */
+#define H2I_AESRX_C_NAUDIO	0x80		/* PBUS DMA data format */
+
+/* AESRX CTL, 16 bit */
+
+#define H2I_AESTX_C		0x1304		/* AES TX DMA control */
+#define H2I_AESTX_C_CLKID_SHIFT	3		/* Bresenham Clock Gen 1-3 */
+#define H2I_AESTX_C_CLKID_M	0x18
+#define H2I_AESTX_C_DATAT_SHIFT	8		/* 1=mono 2=stereo (3=quad) */
+#define H2I_AESTX_C_DATAT_M	0x300
+
+/* DAC CTL1, 16 bit */
+
+#define H2I_DAC_C1		0x1404 		/* DAC dma control */
+#define H2I_DAC_C1_DMA_SHIFT	0		/* DMA channel */
+#define H2I_DAC_C1_DMA_M	0x7
+#define H2I_DAC_C1_CLKID_SHIFT	3		/* Bresenham Clock Gen 1-3 */
+#define H2I_DAC_C1_CLKID_M	0x18
+#define H2I_DAC_C1_DATAT_SHIFT	8		/* 1=mono 2=stereo (3=quad) */
+#define H2I_DAC_C1_DATAT_M	0x300
+
+/* DAC CTL2, 32 bit */
+
+#define H2I_DAC_C2		0x1408
+#define H2I_DAC_C2_R_GAIN_SHIFT	0		/* right a/d input gain */	
+#define H2I_DAC_C2_R_GAIN_M	0xf	
+#define H2I_DAC_C2_L_GAIN_SHIFT	4		/* left a/d input gain */
+#define H2I_DAC_C2_L_GAIN_M	0xf0
+#define H2I_DAC_C2_R_SEL	0x100		/* right input select */
+#define H2I_DAC_C2_L_SEL	0x200		/* left input select */
+#define H2I_DAC_C2_MUTE		0x400		/* mute */
+#define H2I_DAC_C2_DO1		0x10000		/* digital output port bit 0 */
+#define H2I_DAC_C2_DO2		0x20000		/* digital output port bit 1 */
+#define H2I_DAC_C2_R_ATT_SHIFT	18		/* right a/d output - */
+#define H2I_DAC_C2_R_ATT_M	0x7c0000	/* attenuation */
+#define H2I_DAC_C2_L_ATT_SHIFT	23		/* left a/d output - */
+#define H2I_DAC_C2_L_ATT_M	0x0000f80	/* attenuation */
+
+/* ADC CTL1, 16 bit */
+
+#define H2I_ADC_C1		0x1504 		/* DAC dma control */
+#define H2I_ADC_C1_DMA_SHIFT	0		/* DMA channel */
+#define H2I_ADC_C1_DMA_M	0x7
+#define H2I_ADC_C1_CLKID_SHIFT	3		/* Bresenham Clock Gen 1-3 */
+#define H2I_ADC_C1_CLKID_M	0x18
+#define H2I_ADC_C1_DATAT_SHIFT	8		/* 1=mono 2=stereo (3=quad) */
+#define H2I_ADC_C1_DATAT_M	0x300
+
+/* ADC CTL2, 32 bit */
+
+#define H2I_ADC_C2		0x1508
+#define H2I_ADC_C2_RGAIN_SHIFT	0		/* right a/d input gain */	
+#define H2I_ADC_C2_R_GAIN_M	0xf	
+#define H2I_ADC_C2_L_GAIN_SHIFT	4		/* left a/d input gain */
+#define H2I_ADC_C2_L_GAIN_M	0xf0
+#define H2I_ADC_C2_R_SEL	0x100		/* right input select */
+#define H2I_ADC_C2_L_SEL	0x200		/* left input select */
+#define H2I_ADC_C2_MUTE		0x400		/* mute */
+#define H2I_ADC_C2_DO1		0x10000		/* digital output port bit 0 */
+#define H2I_ADC_C2_DO2		0x20000		/* digital output port bit 1 */
+#define H2I_ADC_C2_R_ATT_SHIFT	18		/* right a/d output - */
+#define H2I_ADC_C2_R_ATT_M	0x7c0000	/* attenuation */
+#define H2I_ADC_C2_L_ATT_SHIFT	23		/* left a/d output - */
+#define H2I_ADC_C2_L_ATT_M	0x0000f80	/* attenuation */
+
+
+#define H2I_SYNTH_MAP_C		0x1104		/* synth dma handshake ctrl */
+
+/* Clock generator 1 CTL 1, 16 bit */
+
+#define H2I_BRES1_C1		0x2104 
+#define H2I_BRES1_C1_SHIFT	0		/* 0=48.0 1=44.1 2=aes_rx */
+#define H2I_BRES1_C1_M		0x03
+				
+/* Clock generator 1 CTL 2, 32 bit */
+
+#define H2I_BRES1_C2		0x2108
+#define H2I_BRES1_C2_INC_SHIFT	0		/* increment value */
+#define H2I_BRES1_C2_INC_M	0xffff
+#define H2I_BRES1_C2_MOD_SHIFT	16		/* modcontrol value */
+#define H2I_BRES1_C2_MOD_M	0xffff0000	/* modctrl=0xffff&(modinc-1) */
+
+/* Clock generator 2 CTL 1, 16 bit */
+
+#define H2I_BRES2_C1		0x2204 
+#define H2I_BRES2_C1_SHIFT	0		/* 0=48.0 1=44.1 2=aes_rx */
+#define H2I_BRES2_C1_M		0x03
+				
+/* Clock generator 2 CTL 2, 32 bit */
+
+#define H2I_BRES2_C2		0x2208
+#define H2I_BRES2_C2_INC_SHIFT	0		/* increment value */
+#define H2I_BRES2_C2_INC_M	0xffff
+#define H2I_BRES2_C2_MOD_SHIFT	16		/* modcontrol value */
+#define H2I_BRES2_C2_MOD_M	0xffff0000	/* modctrl=0xffff&(modinc-1) */
+
+/* Clock generator 3 CTL 1, 16 bit */
+
+#define H2I_BRES3_C1		0x2304 
+#define H2I_BRES3_C1_SHIFT	0		/* 0=48.0 1=44.1 2=aes_rx */
+#define H2I_BRES3_C1_M		0x03
+				
+/* Clock generator 3 CTL 2, 32 bit */
+
+#define H2I_BRES3_C2		0x2308
+#define H2I_BRES3_C2_INC_SHIFT	0		/* increment value */
+#define H2I_BRES3_C2_INC_M	0xffff
+#define H2I_BRES3_C2_MOD_SHIFT	16		/* modcontrol value */
+#define H2I_BRES3_C2_MOD_M	0xffff0000	/* modctrl=0xffff&(modinc-1) */
+
+/* Unix timer, 64 bit */
+
+#define H2I_UTIME		0x3104
+#define H2I_UTIME_0_LD		0xffff		/* microseconds, LSB's */
+#define H2I_UTIME_1_LD0		0x0f		/* microseconds, MSB's */
+#define H2I_UTIME_1_LD1		0xf0		/* tenths of microseconds */
+#define H2I_UTIME_2_LD		0xffff		/* seconds, LSB's */
+#define H2I_UTIME_3_LD		0xffff		/* seconds, MSB's */
+
+typedef volatile unsigned long snd_hal2_reg_t;
+
+typedef struct _snd_hal2_ctl_regs snd_hal2_ctl_regs_t;
+
+struct _snd_hal2_ctl_regs {
+	snd_hal2_reg_t _unused0[4];
+	snd_hal2_reg_t isr;		/* 0x10 Status Register */
+	snd_hal2_reg_t _unused1[3];
+	snd_hal2_reg_t rev;		/* 0x20 Revision Register */
+	snd_hal2_reg_t _unused2[3];
+	snd_hal2_reg_t iar;		/* 0x30 Indirect Address Register */
+	snd_hal2_reg_t _unused3[3];
+	snd_hal2_reg_t idr0;		/* 0x40 Indirect Data Register 0 */
+	snd_hal2_reg_t _unused4[3];
+	snd_hal2_reg_t idr1;		/* 0x50 Indirect Data Register 1 */
+	snd_hal2_reg_t _unused5[3];
+	snd_hal2_reg_t idr2;		/* 0x60 Indirect Data Register 2 */
+	snd_hal2_reg_t _unused6[3];
+	snd_hal2_reg_t idr3;		/* 0x70 Indirect Data Register 3 */
+};
+
+typedef struct _snd_hal2_aes_regs snd_hal2_aes_regs_t;
+
+struct _snd_hal2_aes_regs {
+	snd_hal2_reg_t rx_stat[2];	/* Status registers */
+	snd_hal2_reg_t rx_cr[2];	/* Control registers */
+	snd_hal2_reg_t rx_ud[4];	/* User data window */
+	snd_hal2_reg_t rx_st[24];	/* Channel status data */
+	
+	snd_hal2_reg_t tx_stat[1];	/* Status register */
+	snd_hal2_reg_t tx_cr[3];	/* Control registers */
+	snd_hal2_reg_t tx_ud[4];	/* User data window */
+	snd_hal2_reg_t tx_st[24];	/* Channel status data */
+};
+
+typedef struct _snd_hal2_vol_regs snd_hal2_vol_regs_t;
+
+struct _snd_hal2_vol_regs {
+	snd_hal2_reg_t right;		/* 0x00 Right volume */
+	snd_hal2_reg_t left;		/* 0x04 Left volume */
+};
+
+typedef struct _snd_hal2_syn_regs snd_hal2_syn_regs_t;
+
+struct _snd_hal2_syn_regs {
+	snd_hal2_reg_t _unused0[2];
+	snd_hal2_reg_t page;		/* DOC Page register */
+	snd_hal2_reg_t regsel;		/* DOC Register selection */
+	snd_hal2_reg_t dlow;		/* DOC Data low */
+	snd_hal2_reg_t dhigh;		/* DOC Data high */
+	snd_hal2_reg_t irq;		/* IRQ Status */
+	snd_hal2_reg_t dram;		/* DRAM Access */
+};
+
+struct _snd_hal2_card;
+typedef struct _snd_hal2_card snd_hal2_card_t;
+
+/* HAL2 specific structure */
+
+typedef struct _snd_hal2_ring snd_hal2_ring_t;
+
+struct _snd_hal2_ring {
+	volatile struct hpc_dma_desc desc;
+	unsigned long _padding;		/* 8 byte aligned */
+};
+
+typedef struct _snd_hal2_pbus snd_hal2_pbus_t;
+
+struct _snd_hal2_pbus {
+	struct hpc3_pbus_dmacregs *pbus;
+	int pbusnr;
+	unsigned long fifobeg;
+	unsigned long fifoend;
+	unsigned long highwater;
+
+	unsigned long ctrl;		/* Current state of pbus->pbdma_ctrl */
+};
+
+typedef struct _snd_hal2_codec snd_hal2_codec_t;
+
+struct _snd_hal2_codec {
+	snd_dma_t *dmaptr;		/* DMA1 pointer */
+	snd_hal2_ring_t *ringbuf;	/* DMA descirptors */
+	snd_hal2_pbus_t pbus;
+	unsigned short blocks;
+};
+
+struct _snd_hal2 {
+	snd_irq_t *irqptr;		/* IRQ pointer */
+	snd_hal2_ctl_regs_t *ctl_regs;	/* HAL2 ctl registers */
+	snd_hal2_aes_regs_t *aes_regs;	/* HAL2 vol registers */
+	snd_hal2_vol_regs_t *vol_regs;	/* HAL2 aes registers */
+	snd_hal2_syn_regs_t *syn_regs;	/* HAL2 syn registers */
+
+	unsigned int master;		/* Master frequency */
+	unsigned short mod;		/* MOD value */
+	unsigned short inc;		/* INC value */
+
+	snd_hal2_codec_t dac;
+	snd_hal2_codec_t adc;
+};
+
+/* main structure for HAL2 card */
+
+struct _snd_hal2_card {
+	snd_card_t *card;
+	snd_pcm_t *pcm;
+	snd_rawmidi_t *midi_uart;
+
+	snd_pcm_substream_t *playback_substream;
+	snd_pcm_substream_t *capture_substream;
+	snd_pcm1_substream_t *playback_substream1;
+	snd_pcm1_substream_t *capture_substream1;
+
+	struct _snd_hal2 hal2;	/* HAL2 specific variables */
+
+	int usecount;
+};
+
+extern void snd_hal2_isr_write(snd_hal2_card_t *hal2, unsigned short val);
+extern unsigned short snd_hal2_isr_look(snd_hal2_card_t *hal2);
+extern unsigned short snd_hal2_rev_look(snd_hal2_card_t *hal2);
+
+extern void snd_hal2_i_write16(snd_hal2_card_t *hal2, unsigned short addr, unsigned short val);
+extern void snd_hal2_i_write32(snd_hal2_card_t *hal2, unsigned short addr, unsigned long val);
+
+extern unsigned short snd_hal2_i_look16(snd_hal2_card_t *hal2, unsigned short addr);
+extern unsigned long snd_hal2_i_look32(snd_hal2_card_t *hal2, unsigned short addr);
+
+extern snd_pcm_t *snd_hal2_pcm_new_device(snd_hal2_card_t *hal2);
+extern void snd_hal2_dump_regs(snd_hal2_card_t *hal2);
+extern snd_pcm_t *snd_hal2_new_device(snd_card_t *card,
+				      snd_irq_t *irqnum,
+				      snd_dma_t *dma1ptr,
+				      snd_dma_t *dma2ptr,
+				      snd_hal2_ctl_regs_t *ctl_regs,
+				      snd_hal2_aes_regs_t *aes_regs,
+				      snd_hal2_vol_regs_t *vol_regs,
+				      snd_hal2_syn_regs_t *syn_regs);
+extern void snd_hal2_interrupt(snd_hal2_card_t * hal2);
+
+extern snd_pcm_t *snd_hal2_pcm(snd_hal2_card_t *hal2);
+
+#endif				/* __HAL2_H */
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/hwdep.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/hwdep.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/hwdep.h	(revision 91)
@@ -0,0 +1,71 @@
+#ifndef __SOUND_HWDEP_H
+#define __SOUND_HWDEP_H
+
+/*
+ *  Hardware dependent layer 
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <sound/asound.h>
+#include <linux/poll.h>
+
+struct snd_hwdep;
+
+struct snd_hwdep_ops {
+	long long (*llseek) (struct snd_hwdep *hw, struct file * file, long long offset, int orig);
+	long (*read) (struct snd_hwdep *hw, char __user *buf, long count, loff_t *offset);
+	long (*write) (struct snd_hwdep *hw, const char __user *buf, long count, loff_t *offset);
+	int (*open) (struct snd_hwdep * hw, struct file * file);
+	int (*release) (struct snd_hwdep *hw, struct file * file);
+	unsigned int (*poll) (struct snd_hwdep *hw, struct file * file, poll_table * wait);
+	int (*ioctl) (struct snd_hwdep *hw, struct file * file, unsigned int cmd, unsigned long arg);
+	int (*ioctl_compat) (struct snd_hwdep *hw, struct file * file, unsigned int cmd, unsigned long arg);
+	int (*mmap) (struct snd_hwdep *hw, struct file * file, struct vm_area_struct * vma);
+	int (*dsp_status) (struct snd_hwdep *hw, struct snd_hwdep_dsp_status *status);
+	int (*dsp_load) (struct snd_hwdep *hw, struct snd_hwdep_dsp_image *image);
+};
+
+struct snd_hwdep {
+	struct snd_card *card;
+	int device;
+	char id[32];
+	char name[80];
+	int iface;
+
+#ifdef CONFIG_SND_OSSEMUL
+	char oss_dev[32];
+	int oss_type;
+	int ossreg;
+#endif
+
+	struct snd_hwdep_ops ops;
+	wait_queue_head_t open_wait;
+	void *private_data;
+	void (*private_free) (struct snd_hwdep *hwdep);
+
+	struct semaphore open_mutex;
+	int used;
+	unsigned int dsp_loaded;
+	unsigned int exclusive: 1;
+};
+
+extern int snd_hwdep_new(struct snd_card *card, char *id, int device,
+			 struct snd_hwdep **rhwdep);
+
+#endif /* __SOUND_HWDEP_H */
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/i2c.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/i2c.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/i2c.h	(revision 91)
@@ -0,0 +1,85 @@
+#ifndef __SND_I2C_H
+#define __SND_I2C_H
+
+/*
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ */
+
+#define SNDRV_LOCK_I2C_BUS(bus) spin_lock_irqsave(&bus->lock, flags)
+#define SNDRV_UNLOCK_I2C_BUS(bus) spin_unlock_irqrestore(&bus->lock, flags)
+
+typedef struct _snd_i2c_device snd_i2c_device_t;
+typedef struct _snd_i2c_bus snd_i2c_bus_t;
+
+struct _snd_i2c_device {
+	struct list_head list;
+	snd_i2c_bus_t *bus;	/* I2C bus */
+	char name[32];		/* some useful device name */
+	unsigned char addr;	/* device address */
+	unsigned long private_value;
+	void *private_data;
+	void (*private_free)(snd_i2c_device_t *device);
+};
+
+#define snd_i2c_device(n) list_entry(n, snd_i2c_device_t, list)
+
+struct _snd_i2c_bus {
+	snd_card_t *card;	/* card which I2C belongs to */
+	char name[32];		/* some useful label */
+
+	spinlock_t lock;
+
+	struct list_head devices; /* attached devices */
+
+	/* Software I2C */
+	void (*i2c_setlines) (snd_i2c_bus_t * bus, int ctrl, int data);
+	int (*i2c_getdataline) (snd_i2c_bus_t * bus);
+
+	/* Hardware I2C */
+	int (*i2c_read) (snd_i2c_bus_t * bus, unsigned char addr);
+	int (*i2c_write) (snd_i2c_bus_t * bus, unsigned char addr, unsigned char b1, unsigned char b2, int both);
+
+	unsigned long private_value;
+	void *private_data;
+	void (*private_free)(snd_i2c_bus_t *bus);
+};
+
+
+int snd_i2c_bus_create(snd_card_t *card, const char *name, snd_i2c_bus_t **ri2c);
+int snd_i2c_device_create(snd_i2c_bus_t *bus, const char *name, unsigned char addr, snd_i2c_device_t **rdevice);
+int snd_i2c_device_free(snd_i2c_device_t *device);
+
+/* i2c bus access functions */
+void snd_i2c_reset(snd_i2c_bus_t *bus);
+void snd_i2c_start(snd_i2c_bus_t *bus);
+void snd_i2c_stop(snd_i2c_bus_t *bus);
+void snd_i2c_one(snd_i2c_bus_t *bus);
+void snd_i2c_zero(snd_i2c_bus_t *bus);
+int snd_i2c_ack(snd_i2c_bus_t *bus);
+
+int snd_i2c_sendbyte(snd_i2c_bus_t *bus, unsigned char data, int wait_for_ack);
+unsigned char snd_i2c_readbyte(snd_i2c_bus_t *bus, int last);
+
+/* i2c (maybe) hardware functions */
+int snd_i2c_read(snd_i2c_bus_t *bus, unsigned char addr);
+int snd_i2c_write(snd_i2c_bus_t *bus, unsigned char addr, unsigned char b1, unsigned char b2, int both);
+int snd_i2c_dev_read(snd_i2c_device_t *device);
+int snd_i2c_dev_write(snd_i2c_device_t *device, unsigned char b1, unsigned char b2, int both);
+
+#endif				/* __SND_I2C_H */
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/info.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/info.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/info.h	(revision 91)
@@ -0,0 +1,191 @@
+#ifndef __SOUND_INFO_H
+#define __SOUND_INFO_H
+
+/*
+ *  Header file for info interface
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <linux/poll.h>
+
+/* buffer for information */
+struct snd_info_buffer {
+	char *buffer;		/* pointer to begin of buffer */
+	char *curr;		/* current position in buffer */
+	unsigned long size;	/* current size */
+	unsigned long len;	/* total length of buffer */
+	int stop;		/* stop flag */
+	int error;		/* error code */
+};
+
+#define SNDRV_INFO_CONTENT_TEXT		0
+#define SNDRV_INFO_CONTENT_DATA		1
+
+struct snd_info_entry;
+
+struct snd_info_entry_text {
+	unsigned long read_size;
+	unsigned long write_size;
+	void (*read) (struct snd_info_entry *entry, struct snd_info_buffer *buffer);
+	void (*write) (struct snd_info_entry *entry, struct snd_info_buffer *buffer);
+};
+
+struct snd_info_entry_ops {
+	int (*open) (struct snd_info_entry *entry,
+		     unsigned short mode, void **file_private_data);
+	int (*release) (struct snd_info_entry * entry,
+			unsigned short mode, void *file_private_data);
+	long (*read) (struct snd_info_entry *entry, void *file_private_data,
+		      struct file * file, char __user *buf,
+		      unsigned long count, unsigned long pos);
+	long (*write) (struct snd_info_entry *entry, void *file_private_data,
+		       struct file * file, const char __user *buf,
+		       unsigned long count, unsigned long pos);
+	long long (*llseek) (struct snd_info_entry *entry, void *file_private_data,
+			    struct file * file, long long offset, int orig);
+	unsigned int (*poll) (struct snd_info_entry *entry, void *file_private_data,
+			      struct file * file, poll_table * wait);
+	int (*ioctl) (struct snd_info_entry *entry, void *file_private_data,
+		      struct file * file, unsigned int cmd, unsigned long arg);
+	int (*mmap) (struct snd_info_entry *entry, void *file_private_data,
+		     struct inode * inode, struct file * file,
+		     struct vm_area_struct * vma);
+};
+
+struct snd_info_entry {
+	const char *name;
+	mode_t mode;
+	long size;
+	unsigned short content;
+	unsigned short disconnected: 1;
+	union {
+		struct snd_info_entry_text text;
+		struct snd_info_entry_ops *ops;
+	} c;
+	struct snd_info_entry *parent;
+	struct snd_card *card;
+	struct module *module;
+	void *private_data;
+	void (*private_free)(struct snd_info_entry *entry);
+	struct proc_dir_entry *p;
+	struct semaphore access;
+};
+
+int snd_info_check_reserved_words(const char *str);
+
+#if defined(CONFIG_SND_OSSEMUL) && defined(CONFIG_PROC_FS)
+int snd_info_minor_register(void);
+int snd_info_minor_unregister(void);
+#else
+#define snd_info_minor_register() /* NOP */
+#define snd_info_minor_unregister() /* NOP */
+#endif
+
+
+#ifdef CONFIG_PROC_FS
+
+extern struct snd_info_entry *snd_seq_root;
+#ifdef CONFIG_SND_OSSEMUL
+extern struct snd_info_entry *snd_oss_root;
+#else
+#define snd_oss_root NULL
+#endif
+
+int snd_iprintf(struct snd_info_buffer * buffer, char *fmt,...);
+int snd_info_init(void);
+int snd_info_done(void);
+
+int snd_info_get_line(struct snd_info_buffer * buffer, char *line, int len);
+char *snd_info_get_str(char *dest, char *src, int len);
+struct snd_info_entry *snd_info_create_module_entry(struct module * module,
+					       const char *name,
+					       struct snd_info_entry * parent);
+struct snd_info_entry *snd_info_create_card_entry(struct snd_card * card,
+					     const char *name,
+					     struct snd_info_entry * parent);
+void snd_info_free_entry(struct snd_info_entry * entry);
+int snd_info_store_text(struct snd_info_entry * entry);
+int snd_info_restore_text(struct snd_info_entry * entry);
+
+int snd_info_card_create(struct snd_card * card);
+int snd_info_card_register(struct snd_card * card);
+int snd_info_card_free(struct snd_card * card);
+int snd_info_register(struct snd_info_entry * entry);
+int snd_info_unregister(struct snd_info_entry * entry);
+
+/* for card drivers */
+int snd_card_proc_new(struct snd_card *card, const char *name, struct snd_info_entry **entryp);
+
+static inline void snd_info_set_text_ops(struct snd_info_entry *entry, 
+					 void *private_data,
+					 long read_size,
+					 void (*read)(struct snd_info_entry *, struct snd_info_buffer *))
+{
+	entry->private_data = private_data;
+	entry->c.text.read_size = read_size;
+	entry->c.text.read = read;
+}
+
+
+#else
+
+#define snd_seq_root NULL
+#define snd_oss_root NULL
+
+static inline int snd_iprintf(struct snd_info_buffer * buffer, char *fmt,...) { return 0; }
+static inline int snd_info_init(void) { return 0; }
+static inline int snd_info_done(void) { return 0; }
+
+static inline int snd_info_get_line(struct snd_info_buffer * buffer, char *line, int len) { return 0; }
+static inline char *snd_info_get_str(char *dest, char *src, int len) { return NULL; }
+static inline struct snd_info_entry *snd_info_create_module_entry(struct module * module, const char *name, struct snd_info_entry * parent) { return NULL; }
+static inline struct snd_info_entry *snd_info_create_card_entry(struct snd_card * card, const char *name, struct snd_info_entry * parent) { return NULL; }
+static inline void snd_info_free_entry(struct snd_info_entry * entry) { ; }
+
+static inline int snd_info_card_create(struct snd_card * card) { return 0; }
+static inline int snd_info_card_register(struct snd_card * card) { return 0; }
+static inline int snd_info_card_free(struct snd_card * card) { return 0; }
+static inline int snd_info_register(struct snd_info_entry * entry) { return 0; }
+static inline int snd_info_unregister(struct snd_info_entry * entry) { return 0; }
+
+#define snd_card_proc_new(card,name,entryp)  0 /* always success */
+#define snd_info_set_text_ops(entry,private_data,read_size,read) /*NOP*/
+
+#endif
+
+/*
+ * OSS info part
+ */
+
+#if defined(CONFIG_SND_OSSEMUL) && defined(CONFIG_PROC_FS)
+
+#define SNDRV_OSS_INFO_DEV_AUDIO	0
+#define SNDRV_OSS_INFO_DEV_SYNTH	1
+#define SNDRV_OSS_INFO_DEV_MIDI		2
+#define SNDRV_OSS_INFO_DEV_TIMERS	4
+#define SNDRV_OSS_INFO_DEV_MIXERS	5
+
+#define SNDRV_OSS_INFO_DEV_COUNT	6
+
+int snd_oss_info_register(int dev, int num, char *string);
+#define snd_oss_info_unregister(dev, num) snd_oss_info_register(dev, num, NULL)
+
+#endif /* CONFIG_SND_OSSEMUL && CONFIG_PROC_FS */
+
+#endif /* __SOUND_INFO_H */
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/initval.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/initval.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/initval.h	(revision 91)
@@ -0,0 +1,198 @@
+#ifndef __INITVAL_H
+#define __INITVAL_H
+
+/*
+ *  Init values for soundcard modules
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef MODULE_GENERIC_STRING
+#ifdef MODULE
+#define MODULE_GENERIC_STRING(name, string) \
+static const char __module_generic_string_##name [] \
+  __attribute__ ((section(".modstring"))) = #name "=" string;
+#else
+#define MODULE_GENERIC_STRING(name, string)
+#endif
+#endif
+
+#ifdef TARGET_OS2
+#define MODULE_CLASSES(val) 
+#define MODULE_DEVICES(val) 
+#define MODULE_PARM_SYNTAX(id, val) 
+#else
+#define MODULE_CLASSES(val) MODULE_GENERIC_STRING(info_classes, val)
+#define MODULE_DEVICES(val) MODULE_GENERIC_STRING(info_devices, val)
+#define MODULE_PARM_SYNTAX(id, val) MODULE_GENERIC_STRING(info_parm_##id, val)
+
+#define SNDRV_MODULE_TYPE_int   "i"
+#define SNDRV_MODULE_TYPE_bool  "i"
+#define SNDRV_MODULE_TYPE_uint  "i"
+#define SNDRV_MODULE_TYPE_charp "s"
+#define SNDRV_MODULE_TYPE_long  "l"
+#define module_param_array(name, type, nump, perm) \
+        MODULE_PARM(name, "1-" __MODULE_STRING(SNDRV_CARDS) SNDRV_MODULE_TYPE_##type)
+#define module_param(name, type, perm) \
+        MODULE_PARM(name, SNDRV_MODULE_TYPE_##type)
+
+#endif
+
+#define SNDRV_AUTO_PORT		0xffff
+#define SNDRV_AUTO_IRQ		0xffff
+#define SNDRV_AUTO_DMA		0xffff
+#define SNDRV_AUTO_DMA_SIZE	(0x7fffffff)
+
+#define SNDRV_DEFAULT_IDX1	(-1)
+#define SNDRV_DEFAULT_STR1	NULL
+#define SNDRV_DEFAULT_ENABLE1	1
+#define SNDRV_DEFAULT_PORT1	SNDRV_AUTO_PORT
+#define SNDRV_DEFAULT_IRQ1	SNDRV_AUTO_IRQ
+#define SNDRV_DEFAULT_DMA1	SNDRV_AUTO_DMA
+#define SNDRV_DEFAULT_DMA_SIZE1	SNDRV_AUTO_DMA_SIZE
+#define SNDRV_DEFAULT_PTR1	SNDRV_DEFAULT_STR1
+
+#ifdef TARGET_OS2
+#define REPEAT_SNDRV(a)         a,a,a,a,a,a,a,a
+#define SNDRV_DEFAULT_IDX	{ REPEAT_SNDRV(-1) }
+#define SNDRV_DEFAULT_STR	{ REPEAT_SNDRV(NULL) }
+#define SNDRV_DEFAULT_ENABLE	{ 1,1,1,1,1,1,1,1 }
+#define SNDRV_DEFAULT_ENABLE_PNP	SNDRV_DEFAULT_ENABLE
+#define SNDRV_DEFAULT_PORT	{ REPEAT_SNDRV(-1) }
+#define SNDRV_DEFAULT_IRQ	{ REPEAT_SNDRV(SNDRV_AUTO_IRQ) }
+#define SNDRV_DEFAULT_DMA	{ REPEAT_SNDRV(SNDRV_AUTO_DMA) }
+#define SNDRV_DEFAULT_DMA_SIZE	{ REPEAT_SNDRV(SNDRV_AUTO_DMA_SIZE) }
+#define SNDRV_DEFAULT_PTR	SNDRV_DEFAULT_STR
+#define SNDDRV_DEFAULT_PCM_DEVS       {REPEAT_SNDRV(1)};
+#define SNDDRV_DEFAULT_PCM_SUBSTREAMS {REPEAT_SNDRV(8)};
+#define SNDDRV_DEFAULT_MIDI_DEVS      {REPEAT_SNDRV(4)};
+#else
+#define SNDRV_DEFAULT_IDX	{ [0 ... (SNDRV_CARDS-1)] = -1 }
+#define SNDRV_DEFAULT_STR	{ [0 ... (SNDRV_CARDS-1)] = NULL }
+#define SNDRV_DEFAULT_ENABLE	{ 1, [1 ... (SNDRV_CARDS-1)] = 0 }
+#define SNDRV_DEFAULT_PORT	{ SNDRV_AUTO_PORT, [1 ... (SNDRV_CARDS-1)] = -1 }
+#define SNDRV_DEFAULT_IRQ	{ [0 ... (SNDRV_CARDS-1)] = SNDRV_AUTO_IRQ }
+#define SNDRV_DEFAULT_DMA	{ [0 ... (SNDRV_CARDS-1)] = SNDRV_AUTO_DMA }
+#define SNDRV_DEFAULT_DMA_SIZE	{ [0 ... (SNDRV_CARDS-1)] = SNDRV_AUTO_DMA_SIZE }
+#define SNDRV_DEFAULT_PTR	SNDRV_DEFAULT_STR
+#endif
+
+#define SNDRV_BOOLEAN_TRUE_DESC	"allows:{{0,Disabled},{1,Enabled}},default:1,dialog:check"
+#define SNDRV_BOOLEAN_FALSE_DESC "allows:{{0,Disabled},{1,Enabled}},default:0,dialog:check"
+
+#define SNDRV_ENABLED		"enable:(snd_enable)"
+
+#define SNDRV_INDEX_DESC	SNDRV_ENABLED ",allows:{{0,7}},unique,skill:required,dialog:list"
+#define SNDRV_ID_DESC		SNDRV_ENABLED ",unique"
+#define SNDRV_ENABLE_DESC	SNDRV_BOOLEAN_FALSE_DESC
+#define SNDRV_ISAPNP_DESC	SNDRV_ENABLED "," SNDRV_BOOLEAN_TRUE_DESC
+#define SNDRV_DMA8_DESC		SNDRV_ENABLED ",allows:{{0,1},{3}},dialog:list"
+#define SNDRV_DMA16_DESC	SNDRV_ENABLED ",allows:{{5,7}},dialog:list"
+#define SNDRV_DMA_DESC		SNDRV_ENABLED ",allows:{{0,1},{3},{5,7}},dialog:list"
+#define SNDRV_IRQ_DESC		SNDRV_ENABLED ",allows:{{5},{7},{9},{10,12},{14,15}},dialog:list"
+#define SNDRV_DMA_SIZE_DESC	SNDRV_ENABLED ",allows:{{4,128}},default:64,skill:advanced"
+#define SNDRV_DMA8_SIZE_DESC	SNDRV_ENABLED ",allows:{{4, 64}},default:64,skill:advanced"
+#define SNDRV_DMA16_SIZE_DESC	SNDRV_ENABLED ",allows:{{4,128}},default:64,skill:advanced"
+#define SNDRV_PORT12_DESC	SNDRV_ENABLED ",allows:{{0,0x3fff}},base:16"
+#define SNDRV_PORT_DESC		SNDRV_ENABLED ",allows:{{0,0xffff}},base:16"
+
+#ifdef SNDRV_LEGACY_AUTO_PROBE
+static int snd_legacy_auto_probe(unsigned long *ports, int (*probe)(unsigned long port))
+{
+	int result = 0;	/* number of detected cards */
+
+	while ((signed long)*ports != -1) {
+		if (probe(*ports) >= 0)
+			result++;
+		ports++;
+	}
+	return result;
+}
+#endif
+
+#ifdef SNDRV_LEGACY_FIND_FREE_IOPORT
+static long snd_legacy_find_free_ioport(long *port_table, long size)
+{
+	while (*port_table != -1) {
+		if (!check_region(*port_table, size))
+			return *port_table;
+		port_table++;
+	}
+	return -1;
+}
+#endif
+
+#ifdef SNDRV_LEGACY_FIND_FREE_IRQ
+static void snd_legacy_empty_irq_handler(int irq, void *dev_id, struct pt_regs *regs)
+{
+}
+
+static int snd_legacy_find_free_irq(int *irq_table)
+{
+	while (*irq_table != -1) {
+		if (!request_irq(*irq_table, snd_legacy_empty_irq_handler,
+				 SA_INTERRUPT, "ALSA Test IRQ", (void *) irq_table)) {
+			free_irq(*irq_table, (void *) irq_table);
+			return *irq_table;
+		}
+		irq_table++;
+	}
+	return -1;
+}
+#endif
+
+#ifdef SNDRV_LEGACY_FIND_FREE_DMA
+static int snd_legacy_find_free_dma(int *dma_table)
+{
+	while (*dma_table != -1) {
+		if (!request_dma(*dma_table, "ALSA Test DMA")) {
+			free_dma(*dma_table);
+			return *dma_table;
+		}
+		dma_table++;
+	}
+	return -1;
+}
+#endif
+
+#if defined(SNDRV_GET_ID) && !defined(MODULE)
+#include <linux/ctype.h>
+static int __init get_id(char **str, char **dst)
+{
+	char *s, *d;
+
+	if (!(*str) || !(**str))
+		return 0;
+	for (s = *str; isalpha(*s) || isdigit(*s) || *s == '_'; s++);
+	if (s != *str) {
+		*dst = (char *)kmalloc(s - *str, GFP_KERNEL);
+		if ((d = *dst) != NULL) {
+			s = *str;
+			while (isalpha(*s) || isdigit(*s) || *s == '_')
+				*d++ = *s++;
+		}
+	}
+	*str = s;
+	if (*s == ',') {
+		(*str)++;
+		return 2;
+	}
+	return 1;
+}
+#endif
+
+#endif				/* __INITVAL_H */
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/instr.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/instr.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/instr.h	(revision 91)
@@ -0,0 +1,177 @@
+/**
+ * \file <file/instr.h>
+ * \brief Application interface library for the ALSA driver
+ * \author Jaroslav Kysela <perex@suse.cz>
+ * \author Abramo Bagnara <abramo@alsa-project.org>
+ * \author Takashi Iwai <tiwai@suse.de>
+ * \date 1998-2001
+ *
+ * Application interface library for the ALSA driver
+ *
+ *
+ *   This library is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU Lesser General Public License as
+ *   published by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this library; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#ifndef __ALSA_INSTR_H
+#define __ALSA_INSTR_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ *  \defgroup Instrument Instrument Interface
+ *  The Instrument Interface.
+ *  \{
+ */
+
+/* instrument get/put */
+
+/** container for sequencer instrument header */
+typedef struct snd_seq_instr_header snd_instr_header_t;
+
+size_t snd_instr_header_sizeof(void);
+#define snd_instr_header_alloca(ptr) \
+do {\
+	assert(ptr);\
+	*ptr = (snd_instr_header_t *)alloca(snd_instr_header_sizeof());\
+	memset(*ptr, 0, snd_instr_header_sizeof());\
+} while (0) /**< allocate instrument header on stack */
+int snd_instr_header_malloc(snd_instr_header_t **ptr, size_t len);
+void snd_instr_header_free(snd_instr_header_t *ptr);
+void snd_instr_header_copy(snd_instr_header_t *dst, const snd_instr_header_t *src);
+
+const snd_seq_instr_t *snd_instr_header_get_id(const snd_instr_header_t *info);
+snd_seq_instr_cluster_t snd_instr_header_get_cluster(const snd_instr_header_t *info);
+unsigned int snd_instr_header_get_cmd(const snd_instr_header_t *info);
+size_t snd_instr_header_get_len(const snd_instr_header_t *info);
+const char *snd_instr_header_get_name(const snd_instr_header_t *info);
+int snd_instr_header_get_type(const snd_instr_header_t *info);
+const char *snd_instr_header_get_format(const snd_instr_header_t *info);
+const snd_seq_instr_t *snd_instr_header_get_alias(const snd_instr_header_t *info);
+void *snd_instr_header_get_data(const snd_instr_header_t *info);
+int snd_instr_header_get_follow_alias(const snd_instr_header_t *info);
+
+void snd_instr_header_set_id(snd_instr_header_t *info, const snd_seq_instr_t *id);
+void snd_instr_header_set_cluster(snd_instr_header_t *info, snd_seq_instr_cluster_t cluster);
+void snd_instr_header_set_cmd(snd_instr_header_t *info, unsigned int cmd);
+void snd_instr_header_set_len(snd_instr_header_t *info, size_t len);
+void snd_instr_header_set_name(snd_instr_header_t *info, const char *name);
+void snd_instr_header_set_type(snd_instr_header_t *info, int type);
+void snd_instr_header_set_format(snd_instr_header_t *info, const char *format);
+void snd_instr_header_set_alias(snd_instr_header_t *info, const snd_seq_instr_t *instr);
+void snd_instr_header_set_follow_alias(snd_instr_header_t *info, int val);
+
+/**
+ *  Instrument abstraction layer
+ *     - based on events
+ */
+
+/** instrument types */
+#define SND_SEQ_INSTR_ATYPE_DATA	0	/**< instrument data */
+#define SND_SEQ_INSTR_ATYPE_ALIAS	1	/**< instrument alias */
+
+/** instrument ASCII identifiers */
+#define SND_SEQ_INSTR_ID_DLS1		"DLS1"		/**< DLS1 */
+#define SND_SEQ_INSTR_ID_DLS2		"DLS2"		/**< DLS2 */
+#define SND_SEQ_INSTR_ID_SIMPLE		"Simple Wave"	/**< Simple Wave */
+#define SND_SEQ_INSTR_ID_SOUNDFONT	"SoundFont"	/**< SoundFont */
+#define SND_SEQ_INSTR_ID_GUS_PATCH	"GUS Patch"	/**< Gravis Patch */
+#define SND_SEQ_INSTR_ID_INTERWAVE	"Interwave FFFF" /**< InterWave FFFF */
+#define SND_SEQ_INSTR_ID_OPL2_3		"OPL2/3 FM"	/**< OPL2/3 FM */
+#define SND_SEQ_INSTR_ID_OPL4		"OPL4"		/**< OPL4 */
+
+/** instrument types */
+#define SND_SEQ_INSTR_TYPE0_DLS1	(1<<0)		/**< MIDI DLS v1 */
+#define SND_SEQ_INSTR_TYPE0_DLS2	(1<<1)		/**< MIDI DLS v2 */
+#define SND_SEQ_INSTR_TYPE1_SIMPLE	(1<<0)		/**< Simple Wave */
+#define SND_SEQ_INSTR_TYPE1_SOUNDFONT	(1<<1)		/**< EMU SoundFont */
+#define SND_SEQ_INSTR_TYPE1_GUS_PATCH	(1<<2)		/**< Gravis UltraSound Patch */
+#define SND_SEQ_INSTR_TYPE1_INTERWAVE	(1<<3)		/**< InterWave FFFF */
+#define SND_SEQ_INSTR_TYPE2_OPL2_3	(1<<0)		/**< Yamaha OPL2/3 FM */
+#define SND_SEQ_INSTR_TYPE2_OPL4	(1<<1)		/**< Yamaha OPL4 */
+
+/** put commands */
+#define SND_SEQ_INSTR_PUT_CMD_CREATE	0	/**< create a new layer */
+#define SND_SEQ_INSTR_PUT_CMD_REPLACE	1	/**< replace the old layer with new one */
+#define SND_SEQ_INSTR_PUT_CMD_MODIFY	2	/**< modify the existing layer */
+#define SND_SEQ_INSTR_PUT_CMD_ADD	3	/**< add one to the existing layer */
+#define SND_SEQ_INSTR_PUT_CMD_REMOVE	4	/**< remove the layer */
+
+/** get commands */
+#define SND_SEQ_INSTR_GET_CMD_FULL	0	/**< get the full data stream */
+#define SND_SEQ_INSTR_GET_CMD_PARTIAL	1	/**< get the partial data stream */
+
+/* query flags */
+#define SND_SEQ_INSTR_QUERY_FOLLOW_ALIAS (1<<0)	/**< follow alias to get the instrument data */
+
+/** free commands */
+#define SND_SEQ_INSTR_FREE_CMD_ALL	0	/**< remove all matching instruments */
+#define SND_SEQ_INSTR_FREE_CMD_PRIVATE	1	/**< remove only private instruments */
+#define SND_SEQ_INSTR_FREE_CMD_CLUSTER	2	/**< remove only cluster instruments */
+#define SND_SEQ_INSTR_FREE_CMD_SINGLE	3	/**< remove single instrument */
+
+
+/**
+ * FM instrument support
+ */
+
+/** FM instrument data structure */
+typedef void snd_instr_fm_t;
+
+int snd_instr_fm_convert_to_stream(snd_instr_fm_t *fm, const char *name, snd_instr_header_t **put, size_t *size);
+int snd_instr_fm_convert_from_stream(snd_instr_header_t *data, size_t size, snd_instr_fm_t **fm);
+int snd_instr_fm_free(snd_instr_fm_t *fm);
+
+
+/**
+ * Simple Wave support
+ */
+
+/** simple instrument data structure */
+typedef void snd_instr_simple_t;
+
+int snd_instr_simple_convert_to_stream(snd_instr_simple_t *simple, const char *name, snd_instr_header_t **put, size_t *size);
+int snd_instr_simple_convert_from_stream(snd_instr_header_t *data, size_t size, snd_instr_simple_t **simple);
+int snd_instr_simple_free(snd_instr_simple_t *simple);
+
+
+/**
+ * InterWave FFFF support
+ */
+
+/** IW FFFF instrument data structure */
+typedef void snd_instr_iwffff_t;
+/** IW FFFF handler */
+typedef struct _snd_iwffff_handle snd_iwffff_handle_t;
+
+int snd_instr_iwffff_open(snd_iwffff_handle_t **handle, const char *name_fff, const char *name_dta);
+int snd_instr_iwffff_open_rom(snd_iwffff_handle_t **handle, int card, int bank, int file);
+int snd_instr_iwffff_open_rom_file(snd_iwffff_handle_t **handle, const char *name, int bank, int file);
+int snd_instr_iwffff_close(snd_iwffff_handle_t *handle);
+int snd_instr_iwffff_load(snd_iwffff_handle_t *handle, int bank, int prg, snd_instr_iwffff_t **iwffff);
+int snd_instr_iwffff_convert_to_stream(snd_instr_iwffff_t *iwffff, const char *name, snd_instr_header_t **data, size_t *size);
+int snd_instr_iwffff_convert_from_stream(snd_instr_header_t *data, size_t size, snd_instr_iwffff_t **iwffff);
+int snd_instr_iwffff_free(snd_instr_iwffff_t *iwffff);
+
+/** \} */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __ALSA_INSTR_H */
+
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/isapnp.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/isapnp.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/isapnp.h	(revision 91)
@@ -0,0 +1,391 @@
+/*
+ *  ISA Plug & Play support
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef LINUX_ISAPNP_H
+#define LINUX_ISAPNP_H
+
+/*
+ *  Configuration registers (TODO: change by specification)
+ */ 
+
+#define ISAPNP_CFG_ACTIVATE		0x30	/* byte */
+#define ISAPNP_CFG_MEM			0x40	/* 4 * dword */
+#define ISAPNP_CFG_PORT			0x60	/* 8 * word */
+#define ISAPNP_CFG_IRQ			0x70	/* 2 * word */
+#define ISAPNP_CFG_DMA			0x74	/* 2 * byte */
+
+/*
+ *
+ */
+
+#define ISAPNP_VENDOR(a,b,c)	(((((a)-'A'+1)&0x3f)<<2)|\
+				((((b)-'A'+1)&0x18)>>3)|((((b)-'A'+1)&7)<<13)|\
+				((((c)-'A'+1)&0x1f)<<8))
+#define ISAPNP_DEVICE(x)	((((x)&0xf000)>>8)|\
+				 (((x)&0x0f00)>>8)|\
+				 (((x)&0x00f0)<<8)|\
+				 (((x)&0x000f)<<8))
+#define ISAPNP_FUNCTION(x)	ISAPNP_DEVICE(x)
+
+/*
+ *
+ */
+
+#ifdef __KERNEL__
+
+#define ISAPNP_PORT_FLAG_16BITADDR	(1<<0)
+#define ISAPNP_PORT_FLAG_FIXED		(1<<1)
+
+struct isapnp_port {
+	unsigned short min;		/* min base number */
+	unsigned short max;		/* max base number */
+	unsigned char align;		/* align boundary */
+	unsigned char size;		/* size of range */
+	unsigned char flags;		/* port flags */
+	unsigned char pad;		/* pad */
+	struct isapnp_resources *res;	/* parent */
+	struct isapnp_port *next;	/* next port */
+};
+
+struct isapnp_irq {
+	unsigned short map;		/* bitmaks for IRQ lines */
+	unsigned char flags;		/* IRQ flags */
+	unsigned char pad;		/* pad */
+	struct isapnp_resources *res;	/* parent */
+	struct isapnp_irq *next;	/* next IRQ */
+};
+
+struct isapnp_dma {
+	unsigned char map;		/* bitmask for DMA channels */
+	unsigned char flags;		/* DMA flags */
+	struct isapnp_resources *res;	/* parent */
+	struct isapnp_dma *next;	/* next port */
+};
+
+struct isapnp_mem {
+	unsigned int min;		/* min base number */
+	unsigned int max;		/* max base number */
+	unsigned int align;		/* align boundary */
+	unsigned int size;		/* size of range */
+	unsigned char flags;		/* memory flags */
+	unsigned char pad;		/* pad */
+	struct isapnp_resources *res;	/* parent */
+	struct isapnp_mem *next;	/* next memory resource */
+};
+
+struct isapnp_mem32 {
+	/* TODO */
+	unsigned char data[17];
+	struct isapnp_resources *res;	/* parent */
+	struct isapnp_mem32 *next;	/* next 32-bit memory resource */
+};
+
+#define ISAPNP_RES_PRIORITY_PREFERRED	0
+#define ISAPNP_RES_PRIORITY_ACCEPTABLE	1
+#define ISAPNP_RES_PRIORITY_FUNCTIONAL	2
+#define ISAPNP_RES_PRIORITY_INVALID	65535
+
+struct isapnp_resources {
+	unsigned short priority;	/* priority */
+	unsigned short dependent;	/* dependent resources */
+	struct isapnp_port *port;	/* first port */
+	struct isapnp_irq *irq;		/* first IRQ */
+	struct isapnp_dma *dma;		/* first DMA */
+	struct isapnp_mem *mem;		/* first memory resource */
+	struct isapnp_mem32 *mem32;	/* first 32-bit memory */
+	struct isapnp_dev *dev;		/* parent */
+	struct isapnp_resources *alt;	/* alternative resource (aka dependent resources) */
+	struct isapnp_resources *next;	/* next resource */
+};
+
+struct isapnp_fixup {
+	unsigned short vendor;		/* matching vendor */
+	unsigned short device;		/* matching device */
+	void (*quirk_function)(struct isapnp_dev *dev);	/* fixup function */
+};
+                        
+
+#ifndef LINUX_VERSION_CODE
+#include <linux/version.h>
+#endif
+
+#include <linux/pci.h>
+
+#if LINUX_VERSION_CODE == KERNEL_VERSION(2, 3, 14)
+#error "Please, upgrade to 2.3.15 kernel."
+#endif
+
+#ifndef IORESOURCE_IO
+struct resource {
+	const char *name;
+	unsigned long start, end;
+	unsigned long flags;
+	struct resource *parent, *sibling, *child;
+};
+#endif
+
+/*
+ * IO resources have these defined flags.
+ */
+#define IORESOURCE_BITS		0x000000ff	/* Bus-specific bits */
+
+#define IORESOURCE_IO		0x00000100	/* Resource type */
+#define IORESOURCE_MEM		0x00000200
+#define IORESOURCE_IRQ		0x00000400
+#define IORESOURCE_DMA		0x00000800
+
+#define IORESOURCE_PREFETCH	0x00001000	/* No side effects */
+#define IORESOURCE_READONLY	0x00002000
+#define IORESOURCE_CACHEABLE	0x00004000
+#define IORESOURCE_RANGELENGTH	0x00008000
+#define IORESOURCE_SHADOWABLE	0x00010000
+
+#define IORESOURCE_UNSET	0x20000000
+#define IORESOURCE_AUTO		0x40000000
+#define IORESOURCE_BUSY		0x80000000	/* Driver has marked this resource busy */
+
+/* ISA PnP IRQ specific bits (IORESOURCE_BITS) */
+#define IORESOURCE_IRQ_HIGHEDGE		(1<<0)
+#define IORESOURCE_IRQ_LOWEDGE		(1<<1)
+#define IORESOURCE_IRQ_HIGHLEVEL	(1<<2)
+#define IORESOURCE_IRQ_LOWLEVEL		(1<<3)
+
+/* ISA PnP DMA specific bits (IORESOURCE_BITS) */
+#define IORESOURCE_DMA_TYPE_MASK	(3<<0)
+#define IORESOURCE_DMA_8BIT		(0<<0)
+#define IORESOURCE_DMA_8AND16BIT	(1<<0)
+#define IORESOURCE_DMA_16BIT		(2<<0)
+
+#define IORESOURCE_DMA_MASTER		(1<<2)
+#define IORESOURCE_DMA_BYTE		(1<<3)
+#define IORESOURCE_DMA_WORD		(1<<4)
+
+#define IORESOURCE_DMA_SPEED_MASK	(3<<6)
+#define IORESOURCE_DMA_COMPATIBLE	(0<<6)
+#define IORESOURCE_DMA_TYPEA		(1<<6)
+#define IORESOURCE_DMA_TYPEB		(2<<6)
+#define IORESOURCE_DMA_TYPEF		(3<<6)
+
+/* ISA PnP memory I/O specific bits (IORESOURCE_BITS) */
+#define IORESOURCE_MEM_WRITEABLE	(1<<0)	/* dup: IORESOURCE_READONLY */
+#define IORESOURCE_MEM_CACHEABLE	(1<<1)	/* dup: IORESOURCE_CACHEABLE */
+#define IORESOURCE_MEM_RANGELENGTH	(1<<2)	/* dup: IORESOURCE_RANGELENGTH */
+#define IORESOURCE_MEM_TYPE_MASK	(3<<3)
+#define IORESOURCE_MEM_8BIT		(0<<3)
+#define IORESOURCE_MEM_16BIT		(1<<3)
+#define IORESOURCE_MEM_8AND16BIT	(2<<3)
+#define IORESOURCE_MEM_SHADOWABLE	(1<<5)	/* dup: IORESOURCE_SHADOWABLE */
+#define IORESOURCE_MEM_EXPANSIONROM	(1<<6)
+
+
+#define DEVICE_COUNT_COMPATIBLE	4
+#define DEVICE_COUNT_IRQ	2
+#define DEVICE_COUNT_DMA	2
+#define DEVICE_COUNT_RESOURCE	12
+
+/*
+ * The pci_dev structure is used to describe both PCI and ISAPnP devices.
+ */
+struct isapnp_dev {
+	int active;			/* device is active */
+	int ro;				/* Read/Only */
+
+	struct isapnp_card *bus;	/* bus this device is on */
+	struct isapnp_dev *sibling;	/* next device on this bus */
+	struct isapnp_dev *next;	/* chain of all devices */
+
+	void		*sysdata;	/* hook for sys-specific extension */
+	struct proc_dir_entry *procent;	/* device entry in /proc/bus/pci */
+
+	unsigned int	devfn;		/* encoded device & function index */
+	unsigned short	vendor;
+	unsigned short	device;
+	unsigned short	subsystem_vendor;
+	unsigned short	subsystem_device;
+	unsigned int	class;		/* 3 bytes: (base,sub,prog-if) */
+	unsigned int	hdr_type;	/* PCI header type */
+	unsigned int	master : 1;	/* set if device is master capable */
+
+	unsigned short	regs;
+
+	/* device is compatible with these IDs */
+	unsigned short vendor_compatible[DEVICE_COUNT_COMPATIBLE];
+	unsigned short device_compatible[DEVICE_COUNT_COMPATIBLE];
+
+	/*
+	 * Instead of touching interrupt line and base address registers
+	 * directly, use the values stored here. They might be different!
+	 */
+	unsigned int	irq;
+	struct resource resource[DEVICE_COUNT_RESOURCE]; /* I/O and memory regions + expansion ROMs */
+	struct resource dma_resource[DEVICE_COUNT_DMA];
+	struct resource irq_resource[DEVICE_COUNT_IRQ];
+
+	char		name[48];	/* Device name */
+
+	int (*prepare)(struct isapnp_dev *dev);
+	int (*activate)(struct isapnp_dev *dev);
+	int (*deactivate)(struct isapnp_dev *dev);
+};
+
+struct isapnp_card {
+	struct isapnp_card *parent;	/* parent bus this bridge is on */
+	struct isapnp_card *children;	/* chain of P2P bridges on this bus */
+	struct isapnp_card *next;	/* chain of all PCI buses */
+	struct pci_ops	*ops;		/* configuration access functions */
+
+	struct isapnp_dev *self;	/* bridge device as seen by parent */
+	struct isapnp_dev *devices;	/* devices behind this bridge */
+
+	void		*sysdata;	/* hook for sys-specific extension */
+	struct proc_dir_entry *procdir;	/* directory entry in /proc/bus/pci */
+
+	unsigned char	number;		/* bus number */
+	unsigned char	primary;	/* number of primary bridge */
+	unsigned char	secondary;	/* number of secondary bridge */
+	unsigned char	subordinate;	/* max number of subordinate buses */
+
+	char		name[48];
+	unsigned short	vendor;
+	unsigned short	device;
+	unsigned int	serial;		/* serial number */
+	unsigned char	pnpver;		/* Plug & Play version */
+	unsigned char	productver;	/* product version */
+	unsigned char	checksum;	/* if zero - checksum passed */
+	unsigned char	pad1;
+};
+
+#define ISAPNP_ANY_ID           0xffff
+#define ISAPNP_CARD_DEVS        8
+
+#ifdef TARGET_OS2
+#define ISAPNP_CARD_ID(_va, _vb, _vc, _device) \
+                ISAPNP_VENDOR(_va, _vb, _vc), ISAPNP_DEVICE(_device)
+#define ISAPNP_CARD_END \
+                0, 0
+#define ISAPNP_DEVICE_ID(_va, _vb, _vc, _function) \
+                { ISAPNP_VENDOR(_va, _vb, _vc), ISAPNP_FUNCTION(_function) } 
+
+#define ISAPNP_CARD_TABLE(name) 
+
+struct isapnp_card_id {
+	unsigned long driver_data;      /* data private to the driver */
+	unsigned short card_vendor, card_device;
+	struct {
+		unsigned short vendor, function;
+	} devs[ISAPNP_CARD_DEVS];       /* logical devices */
+};
+
+#else
+#define ISAPNP_CARD_ID(_va, _vb, _vc, _device) \
+                card_vendor: ISAPNP_VENDOR(_va, _vb, _vc), card_device: ISAPNP_DEVICE(_device)
+#define ISAPNP_CARD_END \
+                card_vendor: 0, card_device: 0
+#define ISAPNP_DEVICE_ID(_va, _vb, _vc, _function) \
+                { vendor: ISAPNP_VENDOR(_va, _vb, _vc), function: ISAPNP_FUNCTION(_function) }
+
+#define ISAPNP_CARD_TABLE(name) \
+		MODULE_GENERIC_TABLE(isapnp_card, name)
+
+struct isapnp_card_id {
+	unsigned long driver_data;      /* data private to the driver */
+	unsigned short card_vendor, card_device;
+	struct {
+		unsigned short vendor, function;
+	} devs[ISAPNP_CARD_DEVS];       /* logical devices */
+};
+
+#endif
+
+#ifdef CONFIG_ISAPNP
+
+#ifndef __ISAPNP__
+#define __ISAPNP__
+#endif
+
+/* lowlevel configuration */
+int isapnp_present(void);
+int isapnp_cfg_begin(int csn, int device);
+int isapnp_cfg_end(void);
+unsigned char isapnp_read_byte(unsigned char idx);
+unsigned short isapnp_read_word(unsigned char idx);
+unsigned int isapnp_read_dword(unsigned char idx);
+void isapnp_write_byte(unsigned char idx, unsigned char val);
+void isapnp_write_word(unsigned char idx, unsigned short val);
+void isapnp_write_dword(unsigned char idx, unsigned int val);
+void isapnp_wake(unsigned char csn);
+void isapnp_device(unsigned char device);
+void isapnp_activate(unsigned char device);
+void isapnp_deactivate(unsigned char device);
+/* manager */
+struct isapnp_card *isapnp_find_card(unsigned short vendor,
+				     unsigned short device,
+				     struct isapnp_card *from);
+struct isapnp_dev *isapnp_find_dev(struct isapnp_card *card,
+				   unsigned short vendor,
+				   unsigned short function,
+				   struct isapnp_dev *from);
+int isapnp_probe_cards(const struct isapnp_card_id *ids,
+		       int (*probe)(struct isapnp_card *card,
+				const struct isapnp_card_id *id));
+/* misc */
+void isapnp_resource_change(struct resource *resource,
+			    unsigned long start,
+			    unsigned long size);
+/* init/main.c */
+int isapnp_init(void);
+
+#else /* !CONFIG_ISAPNP */
+
+/* lowlevel configuration */
+extern inline int isapnp_present(void) { return 0; }
+extern inline int isapnp_cfg_begin(int csn, int device) { return -ENODEV; }
+extern inline int isapnp_cfg_end(void) { return -ENODEV; }
+extern inline unsigned char isapnp_read_byte(unsigned char idx) { return 0xff; }
+extern inline unsigned short isapnp_read_word(unsigned char idx) { return 0xffff; }
+extern inline unsigned int isapnp_read_dword(unsigned char idx) { return 0xffffffff; }
+extern inline void isapnp_write_byte(unsigned char idx, unsigned char val) { ; }
+extern inline void isapnp_write_word(unsigned char idx, unsigned short val) { ; }
+extern inline void isapnp_write_dword(unsigned char idx, unsigned int val) { ; }
+extern void isapnp_wake(unsigned char csn) { ; }
+extern void isapnp_device(unsigned char device) { ; }
+extern void isapnp_activate(unsigned char device) { ; }
+extern void isapnp_deactivate(unsigned char device) { ; }
+/* manager */
+extern struct isapnp_card *isapnp_find_card(unsigned short vendor,
+				            unsigned short device,
+				            struct isapnp_card *from) { return NULL; }
+extern struct isapnp_dev *isapnp_find_dev(struct isapnp_card *card,
+					  unsigned short vendor,
+					  unsigned short function,
+					  struct isapnp_dev *from) { return NULL; }
+int isapnp_probe_cards(const struct isapnp_card_id *ids,
+		       int (*probe)(struct isapnp_card *card,
+				const struct isapnp_card_id *id)) { return 0; }
+/* misc */
+extern void isapnp_resource_change(struct resource *resource,
+				   unsigned long start,
+				   unsigned long size) { ; }
+
+#endif /* CONFIG_ISAPNP */
+
+#endif /* __KERNEL__ */
+#endif /* LINUX_ISAPNP_H */
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/memalloc.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/memalloc.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/memalloc.h	(revision 91)
@@ -0,0 +1,173 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *                   Takashi Iwai <tiwai@suse.de>
+ * 
+ *  Generic memory allocators
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#ifndef __SOUND_MEMALLOC_H
+#define __SOUND_MEMALLOC_H
+
+#include <linux/pci.h>
+#ifdef CONFIG_SBUS
+#include <asm/sbus.h>
+#endif
+
+#if 0
+typedef struct device {
+    struct pci_dev *pci;	/* for PCI and PCI-SG types */
+    unsigned int flags;	/* GFP_XXX for continous and ISA types */
+#ifdef CONFIG_SBUS
+    struct sbus_dev *sbus;	/* for SBUS type */
+#endif
+} device;
+#endif
+/*
+ * buffer device info
+ */
+struct snd_dma_device {
+    int type;                       /* SNDRV_DMA_TYPE_XXX */
+    struct device *dev;
+};
+
+#ifndef snd_dma_pci_data
+#define snd_dma_pci_data(pci)   (&(pci)->dev)
+#define snd_dma_isa_data()      NULL
+#define snd_dma_sbus_data(sbus) ((struct device *)(sbus))
+#define snd_dma_continuous_data(x)      ((struct device *)(unsigned long)(x))
+#endif
+
+/*
+ * buffer types
+ */
+#define SNDRV_DMA_TYPE_UNKNOWN		0	/* not defined */
+#define SNDRV_DMA_TYPE_CONTINUOUS	1	/* continuous no-DMA memory */
+#define SNDRV_DMA_TYPE_DEV		2	/* generic device continuous */
+#define SNDRV_DMA_TYPE_DEV_SG		3	/* generic device SG-buffer */
+#define SNDRV_DMA_TYPE_SBUS		4	/* SBUS continuous */
+#define SNDRV_DMA_TYPE_PCI_SG		5	/* PCI SG-buffer */
+
+#define __GFP_NOWARN    0
+
+/*
+ * info for buffer allocation
+ */
+struct snd_dma_buffer {
+    struct snd_dma_device dev;      /* device type */
+    unsigned char *area;	/* virtual pointer */
+    dma_addr_t addr;	/* physical address */
+    size_t bytes;		/* buffer size in bytes */
+    void *private_data;	/* private for allocator; don't touch */
+};
+
+/* allocate/release a buffer */
+int snd_dma_alloc_pages(int type, struct device *dev, size_t size,
+                        struct snd_dma_buffer *dmab);
+int snd_dma_alloc_pages_fallback(int type, struct device *dev, size_t size,
+                                 struct snd_dma_buffer *dmab);
+void snd_dma_free_pages(struct snd_dma_buffer *dmab);
+
+/* buffer-preservation managements */
+#define snd_dma_pci_buf_id(pci) (((unsigned int)(pci)->vendor << 16) | (pci)->device)
+size_t snd_dma_get_reserved_buf(struct snd_dma_buffer *dmab, unsigned int id);
+int snd_dma_reserve_buf(struct snd_dma_buffer *dmab, unsigned int id);
+
+/*
+ * Generic memory allocators
+ */
+
+/*
+ * continuous pages
+ */
+void *snd_malloc_pages(unsigned long size, unsigned int gfp_flags);
+void snd_free_pages(void *ptr, unsigned long  size);
+
+#ifdef CONFIG_PCI
+/*
+ * PCI continuous pages
+ */
+void *snd_malloc_pci_pages(struct pci_dev *pci, size_t size, dma_addr_t *dma_addr);
+void *snd_malloc_pci_pages_fallback(struct pci_dev *pci, size_t size, dma_addr_t *dma_addr, size_t *res_size);
+void snd_free_pci_pages(struct pci_dev *pci, size_t size, void *ptr, dma_addr_t dma_addr);
+/* one page allocation */
+void *snd_malloc_pci_page(struct pci_dev *pci, dma_addr_t *dma_addr);
+#define snd_free_pci_page(pci,ptr,addr) snd_free_pci_pages(pci,PAGE_SIZE,ptr,addr)
+#endif
+
+#ifdef CONFIG_SBUS
+/*
+ * SBUS continuous pages
+ */
+void *snd_malloc_sbus_pages(struct sbus_dev *sdev, size_t size, dma_addr_t *dma_addr);
+void *snd_malloc_sbus_pages_fallback(struct sbus_dev *sdev, size_t size, dma_addr_t *dma_addr, size_t *res_size);
+void snd_free_sbus_pages(struct sbus_dev *sdev, size_t size, void *ptr, dma_addr_t dma_addr);
+#endif
+
+#ifdef CONFIG_ISA
+/*
+ * ISA continuous pages
+ */
+void *snd_malloc_isa_pages(size_t size, dma_addr_t *dma_addr);
+void *snd_malloc_isa_pages_fallback(size_t size, dma_addr_t *dma_addr, size_t *res_size);
+void snd_free_isa_pages(size_t size, void *ptr, dma_addr_t addr);
+#ifdef CONFIG_PCI
+#define snd_malloc_isa_pages(size, dma_addr) snd_malloc_pci_pages(NULL, size, dma_addr)
+#define snd_malloc_isa_pages_fallback(size, dma_addr, res_size) snd_malloc_pci_pages_fallback(NULL, size, dma_addr, res_size)
+#define snd_free_isa_pages(size, ptr, dma_addr) snd_free_pci_pages(NULL, size, ptr, dma_addr)
+#else /* !CONFIG_PCI */
+#define snd_free_isa_pages(size, ptr, dma_addr) snd_free_pages(ptr, size)
+#endif /* CONFIG_PCI */
+#endif /* CONFIG_ISA */
+
+#ifdef CONFIG_PCI
+/*
+ * Scatter-Gather PCI pages
+ */
+struct snd_sg_page {
+	void *buf;
+	dma_addr_t addr;
+};
+
+struct snd_sg_buf {
+	int size;	/* allocated byte size */
+	int pages;	/* allocated pages */
+	int tblsize;	/* allocated table size */
+	struct snd_sg_page *table;	/* address table */
+	struct page **page_table;	/* page table (for vmap/vunmap) */
+	struct pci_dev *pci;
+};
+
+/*
+ * return the pages matching with the given byte size
+ */
+static inline unsigned int snd_sgbuf_aligned_pages(size_t size)
+{
+	return (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
+}
+
+/*
+ * return the physical address at the corresponding offset
+ */
+static inline dma_addr_t snd_sgbuf_get_addr(struct snd_sg_buf *sgbuf, size_t offset)
+{
+	return sgbuf->table[offset >> PAGE_SHIFT].addr + offset % PAGE_SIZE;
+}
+#endif /* CONFIG_PCI */
+
+#endif /* __SOUND_MEMALLOC_H */
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/minors.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/minors.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/minors.h	(revision 91)
@@ -0,0 +1,86 @@
+#ifndef __MINORS_H
+#define __MINORS_H
+
+/*
+ *  MINOR numbers
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#define SNDRV_MINOR_DEVICES		32
+#define SNDRV_MINOR_CARD(minor)		((minor) >> 5)
+#define SNDRV_MINOR_DEVICE(minor)	((minor) & 0x001f)
+#define SNDRV_MINOR(card, dev)		(((card) << 5) | (dev))
+
+#define SNDRV_MINOR_CONTROL		0	/* 0 - 0 */
+#define SNDRV_MINOR_SEQUENCER		1
+#define SNDRV_MINOR_TIMER		(1+32)
+#define SNDRV_MINOR_HWDEP		4	/* 4 - 7 */
+#define SNDRV_MINOR_HWDEPS		4
+#define SNDRV_MINOR_RAWMIDI		8	/* 8 - 11 */
+#define SNDRV_MINOR_RAWMIDIS		4
+#define SNDRV_MINOR_PCM_PLAYBACK	16	/* 16 - 23 */
+#define SNDRV_MINOR_PCM_CAPTURE		24	/* 24 - 31 */
+#define SNDRV_MINOR_PCMS		8
+
+#define SNDRV_DEVICE_TYPE_CONTROL	SNDRV_MINOR_CONTROL
+#define SNDRV_DEVICE_TYPE_HWDEP		SNDRV_MINOR_HWDEP
+#define SNDRV_DEVICE_TYPE_MIXER		SNDRV_MINOR_MIXER
+#define SNDRV_DEVICE_TYPE_RAWMIDI	SNDRV_MINOR_RAWMIDI
+#define SNDRV_DEVICE_TYPE_PCM_PLAYBACK	SNDRV_MINOR_PCM_PLAYBACK
+#define SNDRV_DEVICE_TYPE_PCM_PLOOP	SNDRV_MINOR_PCM_PLOOP
+#define SNDRV_DEVICE_TYPE_PCM_CAPTURE	SNDRV_MINOR_PCM_CAPTURE
+#define SNDRV_DEVICE_TYPE_PCM_CLOOP	SNDRV_MINOR_PCM_CLOOP
+#define SNDRV_DEVICE_TYPE_SEQUENCER	SNDRV_MINOR_SEQUENCER
+#define SNDRV_DEVICE_TYPE_TIMER		SNDRV_MINOR_TIMER
+
+#ifdef CONFIG_SND_OSSEMUL
+
+#define SNDRV_MINOR_OSS_DEVICES		16
+#define SNDRV_MINOR_OSS_CARD(minor)	((minor) >> 4)
+#define SNDRV_MINOR_OSS_DEVICE(minor)	((minor) & 0x000f)
+#define SNDRV_MINOR_OSS(card, dev)	(((card) << 4) | (dev))
+
+#define SNDRV_MINOR_OSS_MIXER		0	/* /dev/mixer - OSS 3.XX compatible */
+#define SNDRV_MINOR_OSS_SEQUENCER	1	/* /dev/sequencer - OSS 3.XX compatible */
+#define	SNDRV_MINOR_OSS_MIDI		2	/* /dev/midi - native midi interface - OSS 3.XX compatible - UART */
+#define SNDRV_MINOR_OSS_PCM		3	/* alias */
+#define SNDRV_MINOR_OSS_PCM_8		3	/* /dev/dsp - 8bit PCM - OSS 3.XX compatible */
+#define SNDRV_MINOR_OSS_AUDIO		4	/* /dev/audio - SunSparc compatible */
+#define SNDRV_MINOR_OSS_PCM_16		5	/* /dev/dsp16 - 16bit PCM - OSS 3.XX compatible */
+#define SNDRV_MINOR_OSS_SNDSTAT		6	/* /dev/sndstat - for compatibility with OSS */
+#define SNDRV_MINOR_OSS_RESERVED7	7	/* reserved for future use */
+#define SNDRV_MINOR_OSS_MUSIC		8	/* /dev/music - OSS 3.XX compatible */
+#define SNDRV_MINOR_OSS_DMMIDI		9	/* /dev/dmmidi0 - this device can have another minor # with OSS */
+#define SNDRV_MINOR_OSS_DMFM		10	/* /dev/dmfm0 - this device can have another minor # with OSS */
+#define SNDRV_MINOR_OSS_MIXER1		11	/* alternate mixer */
+#define SNDRV_MINOR_OSS_PCM1		12	/* alternate PCM (GF-A-1) */
+#define SNDRV_MINOR_OSS_MIDI1		13	/* alternate midi - SYNTH */
+#define SNDRV_MINOR_OSS_DMMIDI1		14	/* alternate dmmidi - SYNTH */
+#define SNDRV_MINOR_OSS_RESERVED15	15	/* reserved for future use */
+
+#define SNDRV_OSS_DEVICE_TYPE_MIXER	0
+#define SNDRV_OSS_DEVICE_TYPE_SEQUENCER	1
+#define SNDRV_OSS_DEVICE_TYPE_PCM	2
+#define SNDRV_OSS_DEVICE_TYPE_MIDI	3
+#define SNDRV_OSS_DEVICE_TYPE_DMFM	4
+#define SNDRV_OSS_DEVICE_TYPE_SNDSTAT	5
+#define SNDRV_OSS_DEVICE_TYPE_MUSIC	6
+
+#endif
+
+#endif				/* __MINORS_H */
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/mixer_oss.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/mixer_oss.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/mixer_oss.h	(revision 91)
@@ -0,0 +1,71 @@
+#ifndef __MIXER_OSS_H
+#define __MIXER_OSS_H
+
+/*
+ *  OSS MIXER API
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifdef CONFIG_SND_OSSEMUL
+
+typedef struct _snd_oss_mixer_slot snd_mixer_oss_slot_t;
+typedef struct _snd_oss_file snd_mixer_oss_file_t;
+
+typedef int (*snd_mixer_oss_get_volume_t)(snd_mixer_oss_file_t *fmixer, snd_mixer_oss_slot_t *chn, int *left, int *right);
+typedef int (*snd_mixer_oss_put_volume_t)(snd_mixer_oss_file_t *fmixer, snd_mixer_oss_slot_t *chn, int left, int right);
+typedef int (*snd_mixer_oss_get_recsrc_t)(snd_mixer_oss_file_t *fmixer, snd_mixer_oss_slot_t *chn, int *active);
+typedef int (*snd_mixer_oss_put_recsrc_t)(snd_mixer_oss_file_t *fmixer, snd_mixer_oss_slot_t *chn, int active);
+typedef int (*snd_mixer_oss_get_recsrce_t)(snd_mixer_oss_file_t *fmixer, int *active_index);
+typedef int (*snd_mixer_oss_put_recsrce_t)(snd_mixer_oss_file_t *fmixer, int active_index);
+
+struct _snd_oss_mixer_slot {
+	int number;
+	int stereo: 1;
+	snd_mixer_oss_get_volume_t get_volume;
+	snd_mixer_oss_put_volume_t put_volume;
+	snd_mixer_oss_get_recsrc_t get_recsrc;
+	snd_mixer_oss_put_recsrc_t put_recsrc;
+	unsigned long private_value;
+	void *private_data;
+	void (*private_free)(snd_mixer_oss_slot_t *slot);
+};
+
+struct _snd_oss_mixer {
+	snd_card_t *card;
+	char id[16];
+	char name[32];
+	snd_mixer_oss_slot_t slots[32];		/* OSS mixer slots */
+	unsigned int mask_recsrc;		/* exclusive recsrc mask */
+	snd_mixer_oss_get_recsrce_t get_recsrc;
+	snd_mixer_oss_put_recsrce_t put_recsrc;
+	void *private_data_recsrc;
+	void (*private_free_recsrc)(snd_mixer_oss_t *mixer);
+	/* --- */
+	int oss_recsrc;
+};
+
+struct _snd_oss_file {
+	int volume[32][2];
+	snd_card_t *card;
+	snd_mixer_oss_t *mixer;
+};
+
+#endif				/* CONFIG_SND_OSSEMUL */
+
+#endif				/* __MIXER_OSS_H */
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/mpu401.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/mpu401.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/mpu401.h	(revision 91)
@@ -0,0 +1,123 @@
+#ifndef __SOUND_MPU401_H
+#define __SOUND_MPU401_H
+
+/*
+ *  Header file for MPU-401 and compatible cards
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include "rawmidi.h"
+#include <linux/interrupt.h>
+
+#define MPU401_HW_MPU401		1	/* native MPU401 */
+#define MPU401_HW_SB			2	/* SoundBlaster MPU-401 UART */
+#define MPU401_HW_ES1688		3	/* AudioDrive ES1688 MPU-401 UART */
+#define MPU401_HW_OPL3SA2		4	/* Yamaha OPL3-SA2 */
+#define MPU401_HW_SONICVIBES		5	/* S3 SonicVibes */
+#define MPU401_HW_CS4232		6	/* CS4232 */
+#define MPU401_HW_ES18XX		7	/* AudioDrive ES18XX MPU-401 UART */
+#define MPU401_HW_FM801			8	/* ForteMedia FM801 */
+#define MPU401_HW_TRID4DWAVE		9	/* Trident 4DWave */
+#define MPU401_HW_AZT2320		10	/* Aztech AZT2320 */
+#define MPU401_HW_ALS100		11	/* Avance Logic ALS100 */
+#define MPU401_HW_ICE1712		12	/* Envy24 */
+#define MPU401_HW_VIA686A		13	/* VIA 82C686A */
+#define MPU401_HW_YMFPCI		14	/* YMF DS-XG PCI */
+#define MPU401_HW_CMIPCI		15	/* CMIPCI MPU-401 UART */
+#define MPU401_HW_ALS4000		16	/* Avance Logic ALS4000 */
+#define MPU401_HW_INTEL8X0		17	/* Intel8x0 driver */
+#define MPU401_HW_PC98II		18	/* Roland PC98II */
+#define MPU401_HW_AUREAL		19	/* Aureal Vortex */
+
+#define MPU401_INFO_INPUT	(1 << 0)	/* input stream */
+#define MPU401_INFO_OUTPUT	(1 << 1)	/* output stream */
+#define MPU401_INFO_INTEGRATED	(1 << 2)	/* integrated h/w port */
+#define MPU401_INFO_MMIO	(1 << 3)	/* MMIO access */
+#define MPU401_INFO_TX_IRQ	(1 << 4)	/* independent TX irq */
+
+#define MPU401_MODE_BIT_INPUT		0
+#define MPU401_MODE_BIT_OUTPUT		1
+#define MPU401_MODE_BIT_INPUT_TRIGGER	2
+#define MPU401_MODE_BIT_OUTPUT_TRIGGER	3
+
+#define MPU401_MODE_INPUT		(1<<MPU401_MODE_BIT_INPUT)
+#define MPU401_MODE_OUTPUT		(1<<MPU401_MODE_BIT_OUTPUT)
+#define MPU401_MODE_INPUT_TRIGGER	(1<<MPU401_MODE_BIT_INPUT_TRIGGER)
+#define MPU401_MODE_OUTPUT_TRIGGER	(1<<MPU401_MODE_BIT_OUTPUT_TRIGGER)
+
+#define MPU401_MODE_INPUT_TIMER		(1<<0)
+#define MPU401_MODE_OUTPUT_TIMER	(1<<1)
+
+struct snd_mpu401 {
+	struct snd_rawmidi *rmidi;
+
+        unsigned short hardware;	/* MPU401_HW_XXXX */
+        unsigned int info_flags;	/* MPU401_INFO_XXX */
+	unsigned long port;		/* base port of MPU-401 chip */
+	unsigned long cport;		/* port + 1 (usually) */
+	struct resource *res;		/* port resource */
+	int irq;			/* IRQ number of MPU-401 chip (-1 = poll) */
+	int irq_flags;
+
+	unsigned long mode;		/* MPU401_MODE_XXXX */
+	int timer_invoked;
+
+	int (*open_input) (struct snd_mpu401 * mpu);
+	void (*close_input) (struct snd_mpu401 * mpu);
+	int (*open_output) (struct snd_mpu401 * mpu);
+	void (*close_output) (struct snd_mpu401 * mpu);
+	void *private_data;
+
+	struct snd_rawmidi_substream *substream_input;
+	struct snd_rawmidi_substream *substream_output;
+
+	spinlock_t input_lock;
+	spinlock_t output_lock;
+	spinlock_t timer_lock;
+	
+	struct timer_list timer;
+
+	void (*write) (struct snd_mpu401 * mpu, unsigned char data, unsigned long addr);
+	unsigned char (*read) (struct snd_mpu401 *mpu, unsigned long addr);
+};
+
+/* I/O ports */
+
+#define MPU401C(mpu) (mpu)->cport
+#define MPU401D(mpu) (mpu)->port
+
+/*
+
+ */
+
+irqreturn_t snd_mpu401_uart_interrupt(int irq, void *dev_id,
+                                      struct pt_regs *regs);
+irqreturn_t snd_mpu401_uart_interrupt_tx(int irq, void *dev_id,
+                                         struct pt_regs *regs);
+
+int snd_mpu401_uart_new(struct snd_card *card,
+			int device,
+			unsigned short hardware,
+                        unsigned long port,
+                        unsigned int info_flags,
+			int irq,
+			int irq_flags,
+			struct snd_rawmidi ** rrawmidi);
+
+#endif /* __SOUND_MPU401_H */
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/opl3.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/opl3.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/opl3.h	(revision 91)
@@ -0,0 +1,340 @@
+#ifndef __SOUND_OPL3_H
+#define __SOUND_OPL3_H
+
+/*
+ * Definitions of the OPL-3 registers.
+ *
+ * Copyright (c) by Jaroslav Kysela <perex@suse.cz>,
+ *                  Hannu Savolainen 1993-1996
+ *
+ *
+ *      The OPL-3 mode is switched on by writing 0x01, to the offset 5
+ *      of the right side.
+ *
+ *      Another special register at the right side is at offset 4. It contains
+ *      a bit mask defining which voices are used as 4 OP voices.
+ *
+ *      The percussive mode is implemented in the left side only.
+ *
+ *      With the above exceptions the both sides can be operated independently.
+ *      
+ *      A 4 OP voice can be created by setting the corresponding
+ *      bit at offset 4 of the right side.
+ *
+ *      For example setting the rightmost bit (0x01) changes the
+ *      first voice on the right side to the 4 OP mode. The fourth
+ *      voice is made inaccessible.
+ *
+ *      If a voice is set to the 2 OP mode, it works like 2 OP modes
+ *      of the original YM3812 (AdLib). In addition the voice can 
+ *      be connected the left, right or both stereo channels. It can
+ *      even be left unconnected. This works with 4 OP voices also.
+ *
+ *      The stereo connection bits are located in the FEEDBACK_CONNECTION
+ *      register of the voice (0xC0-0xC8). In 4 OP voices these bits are
+ *      in the second half of the voice.
+ *
+ *
+ *   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 "driver.h"
+#include <linux/time.h>
+#include "core.h"
+#include "hwdep.h"
+#include "timer.h"
+#include "seq_midi_emul.h"
+#ifdef CONFIG_SND_SEQUENCER_OSS
+#include "seq_oss.h"
+#include "seq_oss_legacy.h"
+#endif
+#include "seq_device.h"
+#include "ainstr_fm.h"
+
+/*
+ *    Register numbers for the global registers
+ */
+
+#define OPL3_REG_TEST			0x01
+#define   OPL3_ENABLE_WAVE_SELECT	0x20
+
+#define OPL3_REG_TIMER1			0x02
+#define OPL3_REG_TIMER2			0x03
+#define OPL3_REG_TIMER_CONTROL		0x04	/* Left side */
+#define   OPL3_IRQ_RESET		0x80
+#define   OPL3_TIMER1_MASK		0x40
+#define   OPL3_TIMER2_MASK		0x20
+#define   OPL3_TIMER1_START		0x01
+#define   OPL3_TIMER2_START		0x02
+
+#define OPL3_REG_CONNECTION_SELECT	0x04	/* Right side */
+#define   OPL3_LEFT_4OP_0		0x01
+#define   OPL3_LEFT_4OP_1		0x02
+#define   OPL3_LEFT_4OP_2		0x04
+#define   OPL3_RIGHT_4OP_0		0x08
+#define   OPL3_RIGHT_4OP_1		0x10
+#define   OPL3_RIGHT_4OP_2		0x20
+
+#define OPL3_REG_MODE			0x05	/* Right side */
+#define   OPL3_OPL3_ENABLE		0x01	/* OPL3 mode */
+#define   OPL3_OPL4_ENABLE		0x02	/* OPL4 mode */
+
+#define OPL3_REG_KBD_SPLIT		0x08	/* Left side */
+#define   OPL3_COMPOSITE_SINE_WAVE_MODE	0x80	/* Don't use with OPL-3? */
+#define   OPL3_KEYBOARD_SPLIT		0x40
+
+#define OPL3_REG_PERCUSSION		0xbd	/* Left side only */
+#define   OPL3_TREMOLO_DEPTH		0x80
+#define   OPL3_VIBRATO_DEPTH		0x40
+#define	  OPL3_PERCUSSION_ENABLE	0x20
+#define   OPL3_BASSDRUM_ON		0x10
+#define   OPL3_SNAREDRUM_ON		0x08
+#define   OPL3_TOMTOM_ON		0x04
+#define   OPL3_CYMBAL_ON		0x02
+#define   OPL3_HIHAT_ON			0x01
+
+/*
+ *    Offsets to the register banks for operators. To get the
+ *      register number just add the operator offset to the bank offset
+ *
+ *      AM/VIB/EG/KSR/Multiple (0x20 to 0x35)
+ */
+#define OPL3_REG_AM_VIB			0x20
+#define   OPL3_TREMOLO_ON		0x80
+#define   OPL3_VIBRATO_ON		0x40
+#define   OPL3_SUSTAIN_ON		0x20
+#define   OPL3_KSR			0x10	/* Key scaling rate */
+#define   OPL3_MULTIPLE_MASK		0x0f	/* Frequency multiplier */
+
+ /*
+  *   KSL/Total level (0x40 to 0x55)
+  */
+#define OPL3_REG_KSL_LEVEL		0x40
+#define   OPL3_KSL_MASK			0xc0	/* Envelope scaling bits */
+#define   OPL3_TOTAL_LEVEL_MASK		0x3f	/* Strength (volume) of OP */
+
+/*
+ *    Attack / Decay rate (0x60 to 0x75)
+ */
+#define OPL3_REG_ATTACK_DECAY		0x60
+#define   OPL3_ATTACK_MASK		0xf0
+#define   OPL3_DECAY_MASK		0x0f
+
+/*
+ * Sustain level / Release rate (0x80 to 0x95)
+ */
+#define OPL3_REG_SUSTAIN_RELEASE	0x80
+#define   OPL3_SUSTAIN_MASK		0xf0
+#define   OPL3_RELEASE_MASK		0x0f
+
+/*
+ * Wave select (0xE0 to 0xF5)
+ */
+#define OPL3_REG_WAVE_SELECT		0xe0
+#define   OPL3_WAVE_SELECT_MASK		0x07
+
+/*
+ *    Offsets to the register banks for voices. Just add to the
+ *      voice number to get the register number.
+ *
+ *      F-Number low bits (0xA0 to 0xA8).
+ */
+#define OPL3_REG_FNUM_LOW		0xa0
+
+/*
+ *    F-number high bits / Key on / Block (octave) (0xB0 to 0xB8)
+ */
+#define OPL3_REG_KEYON_BLOCK		0xb0
+#define	  OPL3_KEYON_BIT		0x20
+#define	  OPL3_BLOCKNUM_MASK		0x1c
+#define   OPL3_FNUM_HIGH_MASK		0x03
+
+/*
+ *    Feedback / Connection (0xc0 to 0xc8)
+ *
+ *      These registers have two new bits when the OPL-3 mode
+ *      is selected. These bits controls connecting the voice
+ *      to the stereo channels. For 4 OP voices this bit is
+ *      defined in the second half of the voice (add 3 to the
+ *      register offset).
+ *
+ *      For 4 OP voices the connection bit is used in the
+ *      both halves (gives 4 ways to connect the operators).
+ */
+#define OPL3_REG_FEEDBACK_CONNECTION	0xc0
+#define   OPL3_FEEDBACK_MASK		0x0e	/* Valid just for 1st OP of a voice */
+#define   OPL3_CONNECTION_BIT		0x01
+/*
+ *    In the 4 OP mode there is four possible configurations how the
+ *      operators can be connected together (in 2 OP modes there is just
+ *      AM or FM). The 4 OP connection mode is defined by the rightmost
+ *      bit of the FEEDBACK_CONNECTION (0xC0-0xC8) on the both halves.
+ *
+ *      First half      Second half     Mode
+ *
+ *                                       +---+
+ *                                       v   |
+ *      0               0               >+-1-+--2--3--4-->
+ *
+ *
+ *                                      
+ *                                       +---+
+ *                                       |   |
+ *      0               1               >+-1-+--2-+
+ *                                                |->
+ *                                      >--3----4-+
+ *                                      
+ *                                       +---+
+ *                                       |   |
+ *      1               0               >+-1-+-----+
+ *                                                 |->
+ *                                      >--2--3--4-+
+ *
+ *                                       +---+
+ *                                       |   |
+ *      1               1               >+-1-+--+
+ *                                              |
+ *                                      >--2--3-+->
+ *                                              |
+ *                                      >--4----+
+ */
+#define   OPL3_STEREO_BITS		0x30	/* OPL-3 only */
+#define     OPL3_VOICE_TO_LEFT		0x10
+#define     OPL3_VOICE_TO_RIGHT		0x20
+
+/*
+
+ */
+
+#define OPL3_LEFT		0x0000
+#define OPL3_RIGHT		0x0100
+
+#define OPL3_HW_AUTO		0x0000
+#define OPL3_HW_OPL2		0x0200
+#define OPL3_HW_OPL3		0x0300
+#define OPL3_HW_OPL3_SV		0x0301	/* S3 SonicVibes */
+#define OPL3_HW_OPL3_CS		0x0302	/* CS4232/CS4236+ */
+#define OPL3_HW_OPL3_FM801	0x0303	/* FM801 */
+#define OPL3_HW_OPL3_CS4281	0x0304	/* CS4281 */
+#define OPL3_HW_OPL3_PC98	0x0305	/* PC9800 */
+#define OPL3_HW_OPL4		0x0400	/* YMF278B/YMF295 */
+#define OPL3_HW_OPL4_ML		0x0401	/* YMF704/YMF721 */
+#define OPL3_HW_MASK		0xff00
+
+#define MAX_OPL2_VOICES		9
+#define MAX_OPL3_VOICES		18
+
+struct snd_opl3;
+
+/*
+ * A structure to keep track of each hardware voice
+ */
+struct snd_opl3_voice {
+	int  state;		/* status */
+#define SNDRV_OPL3_ST_OFF		0	/* Not playing */
+#define SNDRV_OPL3_ST_ON_2OP	1	/* 2op voice is allocated */
+#define SNDRV_OPL3_ST_ON_4OP	2	/* 4op voice is allocated */
+#define SNDRV_OPL3_ST_NOT_AVAIL	-1	/* voice is not available */
+
+	unsigned int time;	/* An allocation time */
+	unsigned char note;	/* Note currently assigned to this voice */
+
+	unsigned long note_off;	/* note-off time */
+	int note_off_check;	/* check note-off time */
+
+	unsigned char keyon_reg;	/* KON register shadow */
+
+	struct snd_midi_channel *chan;	/* Midi channel for this note */
+};
+
+struct snd_opl3 {
+	unsigned long l_port;
+	unsigned long r_port;
+	struct resource *res_l_port;
+	struct resource *res_r_port;
+	unsigned short hardware;
+	/* hardware access */
+	void (*command) (struct snd_opl3 * opl3, unsigned short cmd, unsigned char val);
+	unsigned short timer_enable;
+	int seq_dev_num;	/* sequencer device number */
+	struct snd_timer *timer1;
+	struct snd_timer *timer2;
+	spinlock_t timer_lock;
+
+	void *private_data;
+	void (*private_free)(struct snd_opl3 *);
+
+	spinlock_t reg_lock;
+	struct snd_card *card;		/* The card that this belongs to */
+	int used;			/* usage flag - exclusive */
+	unsigned char fm_mode;		/* OPL mode, see SNDRV_DM_FM_MODE_XXX */
+	unsigned char rhythm;		/* percussion mode flag */
+	unsigned char max_voices;	/* max number of voices */
+#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
+#define SNDRV_OPL3_MODE_SYNTH 0		/* OSS - voices allocated by application */
+#define SNDRV_OPL3_MODE_SEQ 1		/* ALSA - driver handles voice allocation */
+	int synth_mode;			/* synth mode */
+	int seq_client;
+
+	struct snd_seq_device *seq_dev;	/* sequencer device */
+	struct snd_midi_channel_set * chset;
+
+#ifdef CONFIG_SND_SEQUENCER_OSS
+	struct snd_seq_device *oss_seq_dev;	/* OSS sequencer device */
+	struct snd_midi_channel_set * oss_chset;
+#endif
+ 
+	struct snd_seq_kinstr_ops fm_ops;
+	struct snd_seq_kinstr_list *ilist;
+
+	struct snd_opl3_voice voices[MAX_OPL3_VOICES]; /* Voices (OPL3 'channel') */
+	int use_time;			/* allocation counter */
+
+	unsigned short connection_reg;	/* connection reg shadow */
+	unsigned char drum_reg;		/* percussion reg shadow */
+
+	spinlock_t voice_lock;		/* Lock for voice access */
+
+	struct timer_list tlist;	/* timer for note-offs and effects */
+	int sys_timer_status;		/* system timer run status */
+	spinlock_t sys_timer_lock;	/* Lock for system timer access */
+#endif
+	struct semaphore access_mutex;	/* locking */
+};
+
+/* opl3.c */
+void snd_opl3_interrupt(struct snd_hwdep * hw);
+int snd_opl3_new(struct snd_card *card, unsigned short hardware,
+		 struct snd_opl3 **ropl3);
+int snd_opl3_init(struct snd_opl3 *opl3);
+int snd_opl3_create(struct snd_card *card,
+		    unsigned long l_port, unsigned long r_port,
+		    unsigned short hardware,
+		    int integrated,
+		    struct snd_opl3 ** opl3);
+int snd_opl3_timer_new(struct snd_opl3 * opl3, int timer1_dev, int timer2_dev);
+int snd_opl3_hwdep_new(struct snd_opl3 * opl3, int device, int seq_device,
+		       struct snd_hwdep ** rhwdep);
+
+/* opl3_synth */
+int snd_opl3_open(struct snd_hwdep * hw, struct file *file);
+int snd_opl3_ioctl(struct snd_hwdep * hw, struct file *file,
+		   unsigned int cmd, unsigned long arg);
+int snd_opl3_release(struct snd_hwdep * hw, struct file *file);
+
+void snd_opl3_reset(struct snd_opl3 * opl3);
+
+#endif /* __SOUND_OPL3_H */
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/pcm-indirect.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/pcm-indirect.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/pcm-indirect.h	(revision 91)
@@ -0,0 +1,177 @@
+/*
+ * Helper functions for indirect PCM data transfer
+ *
+ *  Copyright (c) by Takashi Iwai <tiwai@suse.de>
+ *                   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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __SOUND_PCM_INDIRECT_H
+#define __SOUND_PCM_INDIRECT_H
+
+#include <sound/pcm.h>
+
+struct snd_pcm_indirect {
+	unsigned int hw_buffer_size;	/* Byte size of hardware buffer */
+	unsigned int hw_queue_size;	/* Max queue size of hw buffer (0 = buffer size) */
+	unsigned int hw_data;	/* Offset to next dst (or src) in hw ring buffer */
+	unsigned int hw_io;	/* Ring buffer hw pointer */
+	int hw_ready;		/* Bytes ready for play (or captured) in hw ring buffer */
+	unsigned int sw_buffer_size;	/* Byte size of software buffer */
+	unsigned int sw_data;	/* Offset to next dst (or src) in sw ring buffer */
+	unsigned int sw_io;	/* Current software pointer in bytes */
+	int sw_ready;		/* Bytes ready to be transferred to/from hw */
+	snd_pcm_uframes_t appl_ptr;	/* Last seen appl_ptr */
+};
+
+typedef void (*snd_pcm_indirect_copy_t)(struct snd_pcm_substream *substream,
+					struct snd_pcm_indirect *rec, size_t bytes);
+
+/*
+ * helper function for playback ack callback
+ */
+static inline void
+snd_pcm_indirect_playback_transfer(struct snd_pcm_substream *substream,
+				   struct snd_pcm_indirect *rec,
+				   snd_pcm_indirect_copy_t copy)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	snd_pcm_uframes_t appl_ptr = runtime->control->appl_ptr;
+	snd_pcm_sframes_t diff = appl_ptr - rec->appl_ptr;
+	int qsize;
+
+	if (diff) {
+		if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2))
+			diff += runtime->boundary;
+		rec->sw_ready += (int)frames_to_bytes(runtime, diff);
+		rec->appl_ptr = appl_ptr;
+	}
+	qsize = rec->hw_queue_size ? rec->hw_queue_size : rec->hw_buffer_size;
+	while (rec->hw_ready < qsize && rec->sw_ready > 0) {
+		unsigned int hw_to_end = rec->hw_buffer_size - rec->hw_data;
+		unsigned int sw_to_end = rec->sw_buffer_size - rec->sw_data;
+		unsigned int bytes = qsize - rec->hw_ready;
+		if (rec->sw_ready < (int)bytes)
+			bytes = rec->sw_ready;
+		if (hw_to_end < bytes)
+			bytes = hw_to_end;
+		if (sw_to_end < bytes)
+			bytes = sw_to_end;
+		if (! bytes)
+			break;
+		copy(substream, rec, bytes);
+		rec->hw_data += bytes;
+		if (rec->hw_data == rec->hw_buffer_size)
+			rec->hw_data = 0;
+		rec->sw_data += bytes;
+		if (rec->sw_data == rec->sw_buffer_size)
+			rec->sw_data = 0;
+		rec->hw_ready += bytes;
+		rec->sw_ready -= bytes;
+	}
+}
+
+/*
+ * helper function for playback pointer callback
+ * ptr = current byte pointer
+ */
+static inline snd_pcm_uframes_t
+snd_pcm_indirect_playback_pointer(struct snd_pcm_substream *substream,
+				  struct snd_pcm_indirect *rec, unsigned int ptr)
+{
+	int bytes = ptr - rec->hw_io;
+	if (bytes < 0)
+		bytes += rec->hw_buffer_size;
+	rec->hw_io = ptr;
+	rec->hw_ready -= bytes;
+	rec->sw_io += bytes;
+	if (rec->sw_io >= rec->sw_buffer_size)
+		rec->sw_io -= rec->sw_buffer_size;
+	if (substream->ops->ack)
+		substream->ops->ack(substream);
+	return bytes_to_frames(substream->runtime, rec->sw_io);
+}
+
+
+/*
+ * helper function for capture ack callback
+ */
+static inline void
+snd_pcm_indirect_capture_transfer(struct snd_pcm_substream *substream,
+				  struct snd_pcm_indirect *rec,
+				  snd_pcm_indirect_copy_t copy)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	snd_pcm_uframes_t appl_ptr = runtime->control->appl_ptr;
+	snd_pcm_sframes_t diff = appl_ptr - rec->appl_ptr;
+
+	if (diff) {
+		if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2))
+			diff += runtime->boundary;
+		rec->sw_ready -= frames_to_bytes(runtime, diff);
+		rec->appl_ptr = appl_ptr;
+	}
+	while (rec->hw_ready > 0 && 
+	       rec->sw_ready < (int)rec->sw_buffer_size) {
+		size_t hw_to_end = rec->hw_buffer_size - rec->hw_data;
+		size_t sw_to_end = rec->sw_buffer_size - rec->sw_data;
+		size_t bytes = rec->sw_buffer_size - rec->sw_ready;
+		if (rec->hw_ready < (int)bytes)
+			bytes = rec->hw_ready;
+		if (hw_to_end < bytes)
+			bytes = hw_to_end;
+		if (sw_to_end < bytes)
+			bytes = sw_to_end;
+		if (! bytes)
+			break;
+		copy(substream, rec, bytes);
+		rec->hw_data += bytes;
+		if ((int)rec->hw_data == rec->hw_buffer_size)
+			rec->hw_data = 0;
+		rec->sw_data += bytes;
+		if (rec->sw_data == rec->sw_buffer_size)
+			rec->sw_data = 0;
+		rec->hw_ready -= bytes;
+		rec->sw_ready += bytes;
+	}
+}
+
+/*
+ * helper function for capture pointer callback,
+ * ptr = current byte pointer
+ */
+static inline snd_pcm_uframes_t
+snd_pcm_indirect_capture_pointer(struct snd_pcm_substream *substream,
+				 struct snd_pcm_indirect *rec, unsigned int ptr)
+{
+	int qsize;
+	int bytes = ptr - rec->hw_io;
+	if (bytes < 0)
+		bytes += rec->hw_buffer_size;
+	rec->hw_io = ptr;
+	rec->hw_ready += bytes;
+	qsize = rec->hw_queue_size ? rec->hw_queue_size : rec->hw_buffer_size;
+	if (rec->hw_ready > qsize)
+		return SNDRV_PCM_POS_XRUN;
+	rec->sw_io += bytes;
+	if (rec->sw_io >= rec->sw_buffer_size)
+		rec->sw_io -= rec->sw_buffer_size;
+	if (substream->ops->ack)
+		substream->ops->ack(substream);
+	return bytes_to_frames(substream->runtime, rec->sw_io);
+}
+
+#endif /* __SOUND_PCM_INDIRECT_H */
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/pcm.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/pcm.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/pcm.h	(revision 91)
@@ -0,0 +1,1014 @@
+#ifndef __SOUND_PCM_H
+#define __SOUND_PCM_H
+
+/*
+ *  Digital Audio (PCM) abstract layer
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *                   Abramo Bagnara <abramo@alsa-project.org>
+ *
+ *
+ *   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 <limits.h>
+#include <sound/asound.h>
+#include <sound/memalloc.h>
+#include <linux/poll.h>
+#include <linux/bitops.h>
+
+#define snd_pcm_substream_chip(substream) ((substream)->private_data)
+#define snd_pcm_chip(pcm) ((pcm)->private_data)
+
+#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
+#include "pcm_oss.h"
+#endif
+
+/*
+ *  Hardware (lowlevel) section
+ */
+
+struct snd_pcm_hardware {
+	unsigned int info;		/* SNDRV_PCM_INFO_* */
+	u64 formats;			/* SNDRV_PCM_FMTBIT_* */
+	unsigned int rates;		/* SNDRV_PCM_RATE_* */
+	unsigned int rate_min;		/* min rate */
+	unsigned int rate_max;		/* max rate */
+	unsigned int channels_min;	/* min channels */
+	unsigned int channels_max;	/* max channels */
+	size_t buffer_bytes_max;	/* max buffer size */
+	size_t period_bytes_min;	/* min period size */
+	size_t period_bytes_max;	/* max period size */
+	unsigned int periods_min;	/* min # of periods */
+	unsigned int periods_max;	/* max # of periods */
+	size_t fifo_size;		/* fifo size in bytes */
+};
+
+struct snd_pcm_ops {
+	int (*open)(struct snd_pcm_substream *substream);
+	int (*close)(struct snd_pcm_substream *substream);
+	int (*ioctl)(struct snd_pcm_substream * substream,
+		     unsigned int cmd, void *arg);
+	int (*hw_params)(struct snd_pcm_substream *substream,
+			 struct snd_pcm_hw_params *params);
+	int (*hw_free)(struct snd_pcm_substream *substream);
+	int (*prepare)(struct snd_pcm_substream *substream);
+	int (*trigger)(struct snd_pcm_substream *substream, int cmd);
+	snd_pcm_uframes_t (*pointer)(struct snd_pcm_substream *substream);
+	int (*copy)(struct snd_pcm_substream *substream, int channel,
+		    snd_pcm_uframes_t pos,
+		    void __user *buf, snd_pcm_uframes_t count);
+	int (*silence)(struct snd_pcm_substream *substream, int channel, 
+		       snd_pcm_uframes_t pos, snd_pcm_uframes_t count);
+	struct page *(*page)(struct snd_pcm_substream *substream,
+			     unsigned long offset);
+	int (*mmap)(struct snd_pcm_substream *substream, struct vm_area_struct *vma);
+	int (*ack)(struct snd_pcm_substream *substream);
+};
+
+/*
+ *
+ */
+
+#define SNDRV_PCM_DEVICES		8
+
+#define SNDRV_PCM_IOCTL1_FALSE		((void *)0)
+#define SNDRV_PCM_IOCTL1_TRUE		((void *)1)
+
+#define SNDRV_PCM_IOCTL1_RESET		0
+#define SNDRV_PCM_IOCTL1_INFO		1
+#define SNDRV_PCM_IOCTL1_CHANNEL_INFO	2
+#define SNDRV_PCM_IOCTL1_GSTATE		3
+
+#define SNDRV_PCM_TRIGGER_STOP		0
+#define SNDRV_PCM_TRIGGER_START		1
+#define SNDRV_PCM_TRIGGER_PAUSE_PUSH	3
+#define SNDRV_PCM_TRIGGER_PAUSE_RELEASE	4
+#define SNDRV_PCM_TRIGGER_SUSPEND	5
+#define SNDRV_PCM_TRIGGER_RESUME	6
+
+#define SNDRV_PCM_POS_XRUN		((snd_pcm_uframes_t)-1)
+
+/* If you change this don't forget to change rates[] table in pcm_native.c */
+#define SNDRV_PCM_RATE_5512		(1<<0)		/* 5512Hz */
+#define SNDRV_PCM_RATE_8000		(1<<1)		/* 8000Hz */
+#define SNDRV_PCM_RATE_11025		(1<<2)		/* 11025Hz */
+#define SNDRV_PCM_RATE_16000		(1<<3)		/* 16000Hz */
+#define SNDRV_PCM_RATE_22050		(1<<4)		/* 22050Hz */
+#define SNDRV_PCM_RATE_32000		(1<<5)		/* 32000Hz */
+#define SNDRV_PCM_RATE_44100		(1<<6)		/* 44100Hz */
+#define SNDRV_PCM_RATE_48000		(1<<7)		/* 48000Hz */
+#define SNDRV_PCM_RATE_64000		(1<<8)		/* 64000Hz */
+#define SNDRV_PCM_RATE_88200		(1<<9)		/* 88200Hz */
+#define SNDRV_PCM_RATE_96000		(1<<10)		/* 96000Hz */
+#define SNDRV_PCM_RATE_176400		(1<<11)		/* 176400Hz */
+#define SNDRV_PCM_RATE_192000		(1<<12)		/* 192000Hz */
+
+#define SNDRV_PCM_RATE_CONTINUOUS	(1<<30)		/* continuous range */
+#define SNDRV_PCM_RATE_KNOT		(1<<31)		/* supports more non-continuos rates */
+
+#define SNDRV_PCM_RATE_8000_44100	(SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_11025|\
+					 SNDRV_PCM_RATE_16000|SNDRV_PCM_RATE_22050|\
+					 SNDRV_PCM_RATE_32000|SNDRV_PCM_RATE_44100)
+#define SNDRV_PCM_RATE_8000_48000	(SNDRV_PCM_RATE_8000_44100|SNDRV_PCM_RATE_48000)
+#define SNDRV_PCM_RATE_8000_96000	(SNDRV_PCM_RATE_8000_48000|SNDRV_PCM_RATE_64000|\
+					 SNDRV_PCM_RATE_88200|SNDRV_PCM_RATE_96000)
+#define SNDRV_PCM_RATE_8000_192000	(SNDRV_PCM_RATE_8000_96000|SNDRV_PCM_RATE_176400|\
+					 SNDRV_PCM_RATE_192000)
+#define SNDRV_PCM_FMTBIT_S8		(1ULL << SNDRV_PCM_FORMAT_S8)
+#define SNDRV_PCM_FMTBIT_U8		(1ULL << SNDRV_PCM_FORMAT_U8)
+#define SNDRV_PCM_FMTBIT_S16_LE		(1ULL << SNDRV_PCM_FORMAT_S16_LE)
+#define SNDRV_PCM_FMTBIT_S16_BE		(1ULL << SNDRV_PCM_FORMAT_S16_BE)
+#define SNDRV_PCM_FMTBIT_U16_LE		(1ULL << SNDRV_PCM_FORMAT_U16_LE)
+#define SNDRV_PCM_FMTBIT_U16_BE		(1ULL << SNDRV_PCM_FORMAT_U16_BE)
+#define SNDRV_PCM_FMTBIT_S24_LE		(1ULL << SNDRV_PCM_FORMAT_S24_LE)
+#define SNDRV_PCM_FMTBIT_S24_BE		(1ULL << SNDRV_PCM_FORMAT_S24_BE)
+#define SNDRV_PCM_FMTBIT_U24_LE		(1ULL << SNDRV_PCM_FORMAT_U24_LE)
+#define SNDRV_PCM_FMTBIT_U24_BE		(1ULL << SNDRV_PCM_FORMAT_U24_BE)
+#define SNDRV_PCM_FMTBIT_S32_LE		(1ULL << SNDRV_PCM_FORMAT_S32_LE)
+#define SNDRV_PCM_FMTBIT_S32_BE		(1ULL << SNDRV_PCM_FORMAT_S32_BE)
+#define SNDRV_PCM_FMTBIT_U32_LE		(1ULL << SNDRV_PCM_FORMAT_U32_LE)
+#define SNDRV_PCM_FMTBIT_U32_BE		(1ULL << SNDRV_PCM_FORMAT_U32_BE)
+#define SNDRV_PCM_FMTBIT_FLOAT_LE	(1ULL << SNDRV_PCM_FORMAT_FLOAT_LE)
+#define SNDRV_PCM_FMTBIT_FLOAT_BE	(1ULL << SNDRV_PCM_FORMAT_FLOAT_BE)
+#define SNDRV_PCM_FMTBIT_FLOAT64_LE	(1ULL << SNDRV_PCM_FORMAT_FLOAT64_LE)
+#define SNDRV_PCM_FMTBIT_FLOAT64_BE	(1ULL << SNDRV_PCM_FORMAT_FLOAT64_BE)
+#define SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE (1ULL << SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE)
+#define SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_BE (1ULL << SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE)
+#define SNDRV_PCM_FMTBIT_MU_LAW		(1ULL << SNDRV_PCM_FORMAT_MU_LAW)
+#define SNDRV_PCM_FMTBIT_A_LAW		(1ULL << SNDRV_PCM_FORMAT_A_LAW)
+#define SNDRV_PCM_FMTBIT_IMA_ADPCM	(1ULL << SNDRV_PCM_FORMAT_IMA_ADPCM)
+#define SNDRV_PCM_FMTBIT_MPEG		(1ULL << SNDRV_PCM_FORMAT_MPEG)
+#define SNDRV_PCM_FMTBIT_GSM		(1ULL << SNDRV_PCM_FORMAT_GSM)
+#define SNDRV_PCM_FMTBIT_SPECIAL	(1ULL << SNDRV_PCM_FORMAT_SPECIAL)
+#define SNDRV_PCM_FMTBIT_S24_3LE	(1ULL << SNDRV_PCM_FORMAT_S24_3LE)
+#define SNDRV_PCM_FMTBIT_U24_3LE	(1ULL << SNDRV_PCM_FORMAT_U24_3LE)
+#define SNDRV_PCM_FMTBIT_S24_3BE	(1ULL << SNDRV_PCM_FORMAT_S24_3BE)
+#define SNDRV_PCM_FMTBIT_U24_3BE	(1ULL << SNDRV_PCM_FORMAT_U24_3BE)
+#define SNDRV_PCM_FMTBIT_S20_3LE	(1ULL << SNDRV_PCM_FORMAT_S20_3LE)
+#define SNDRV_PCM_FMTBIT_U20_3LE	(1ULL << SNDRV_PCM_FORMAT_U20_3LE)
+#define SNDRV_PCM_FMTBIT_S20_3BE	(1ULL << SNDRV_PCM_FORMAT_S20_3BE)
+#define SNDRV_PCM_FMTBIT_U20_3BE	(1ULL << SNDRV_PCM_FORMAT_U20_3BE)
+#define SNDRV_PCM_FMTBIT_S18_3LE	(1ULL << SNDRV_PCM_FORMAT_S18_3LE)
+#define SNDRV_PCM_FMTBIT_U18_3LE	(1ULL << SNDRV_PCM_FORMAT_U18_3LE)
+#define SNDRV_PCM_FMTBIT_S18_3BE	(1ULL << SNDRV_PCM_FORMAT_S18_3BE)
+#define SNDRV_PCM_FMTBIT_U18_3BE	(1ULL << SNDRV_PCM_FORMAT_U18_3BE)
+
+#ifdef SNDRV_LITTLE_ENDIAN
+#define SNDRV_PCM_FMTBIT_S16		SNDRV_PCM_FMTBIT_S16_LE
+#define SNDRV_PCM_FMTBIT_U16		SNDRV_PCM_FMTBIT_U16_LE
+#define SNDRV_PCM_FMTBIT_S24		SNDRV_PCM_FMTBIT_S24_LE
+#define SNDRV_PCM_FMTBIT_U24		SNDRV_PCM_FMTBIT_U24_LE
+#define SNDRV_PCM_FMTBIT_S32		SNDRV_PCM_FMTBIT_S32_LE
+#define SNDRV_PCM_FMTBIT_U32		SNDRV_PCM_FMTBIT_U32_LE
+#define SNDRV_PCM_FMTBIT_FLOAT		SNDRV_PCM_FMTBIT_FLOAT_LE
+#define SNDRV_PCM_FMTBIT_FLOAT64	SNDRV_PCM_FMTBIT_FLOAT64_LE
+#define SNDRV_PCM_FMTBIT_IEC958_SUBFRAME SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE
+#endif
+#ifdef SNDRV_BIG_ENDIAN
+#define SNDRV_PCM_FMTBIT_S16		SNDRV_PCM_FMTBIT_S16_BE
+#define SNDRV_PCM_FMTBIT_U16		SNDRV_PCM_FMTBIT_U16_BE
+#define SNDRV_PCM_FMTBIT_S24		SNDRV_PCM_FMTBIT_S24_BE
+#define SNDRV_PCM_FMTBIT_U24		SNDRV_PCM_FMTBIT_U24_BE
+#define SNDRV_PCM_FMTBIT_S32		SNDRV_PCM_FMTBIT_S32_BE
+#define SNDRV_PCM_FMTBIT_U32		SNDRV_PCM_FMTBIT_U32_BE
+#define SNDRV_PCM_FMTBIT_FLOAT		SNDRV_PCM_FMTBIT_FLOAT_BE
+#define SNDRV_PCM_FMTBIT_FLOAT64	SNDRV_PCM_FMTBIT_FLOAT64_BE
+#define SNDRV_PCM_FMTBIT_IEC958_SUBFRAME SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_BE
+#endif
+
+struct snd_pcm_file {
+	struct snd_pcm_substream *substream;
+	struct snd_pcm_file *next;
+};
+
+struct snd_pcm_hw_rule;
+typedef int (*snd_pcm_hw_rule_func_t)(struct snd_pcm_hw_params *params,
+				      struct snd_pcm_hw_rule *rule);
+
+struct snd_pcm_hw_rule {
+	unsigned int cond;
+	snd_pcm_hw_rule_func_t func;
+	int var;
+	int deps[4];
+	void *private;
+};
+
+struct snd_pcm_hw_constraints {
+	struct snd_mask masks[SNDRV_PCM_HW_PARAM_LAST_MASK - 
+			 SNDRV_PCM_HW_PARAM_FIRST_MASK + 1];
+	struct snd_interval intervals[SNDRV_PCM_HW_PARAM_LAST_INTERVAL -
+			     SNDRV_PCM_HW_PARAM_FIRST_INTERVAL + 1];
+	unsigned int rules_num;
+	unsigned int rules_all;
+	struct snd_pcm_hw_rule *rules;
+};
+
+static inline struct snd_mask *constrs_mask(struct snd_pcm_hw_constraints *constrs,
+					    snd_pcm_hw_param_t var)
+{
+	return &constrs->masks[var - SNDRV_PCM_HW_PARAM_FIRST_MASK];
+}
+
+static inline struct snd_interval *constrs_interval(struct snd_pcm_hw_constraints *constrs,
+						    snd_pcm_hw_param_t var)
+{
+	return &constrs->intervals[var - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL];
+}
+
+struct snd_ratnum {
+	unsigned int num;
+	unsigned int den_min, den_max, den_step;
+};
+
+struct snd_ratden {
+	unsigned int num_min, num_max, num_step;
+	unsigned int den;
+};
+
+struct snd_pcm_hw_constraint_ratnums {
+	int nrats;
+	struct snd_ratnum *rats;
+};
+
+struct snd_pcm_hw_constraint_ratdens {
+	int nrats;
+	struct snd_ratden *rats;
+};
+
+struct snd_pcm_hw_constraint_list {
+	unsigned int count;
+	unsigned int *list;
+	unsigned int mask;
+};
+
+struct snd_pcm_runtime {
+	/* -- Status -- */
+	struct snd_pcm_substream *trigger_master;
+	struct timespec trigger_tstamp;	/* trigger timestamp */
+	int overrange;
+	snd_pcm_uframes_t avail_max;
+	snd_pcm_uframes_t hw_ptr_base;	/* Position at buffer restart */
+	snd_pcm_uframes_t hw_ptr_interrupt; /* Position at interrupt time*/
+
+	/* -- HW params -- */
+	snd_pcm_access_t access;	/* access mode */
+	snd_pcm_format_t format;	/* SNDRV_PCM_FORMAT_* */
+	snd_pcm_subformat_t subformat;	/* subformat */
+	unsigned int rate;		/* rate in Hz */
+	unsigned int channels;		/* channels */
+	snd_pcm_uframes_t period_size;	/* period size */
+	unsigned int periods;		/* periods */
+	snd_pcm_uframes_t buffer_size;	/* buffer size */
+	unsigned int tick_time;		/* tick time */
+	snd_pcm_uframes_t min_align;	/* Min alignment for the format */
+	size_t byte_align;
+	unsigned int frame_bits;
+	unsigned int sample_bits;
+	unsigned int info;
+	unsigned int rate_num;
+	unsigned int rate_den;
+
+	/* -- SW params -- */
+	int tstamp_mode;		/* mmap timestamp is updated */
+  	unsigned int period_step;
+	unsigned int sleep_min;		/* min ticks to sleep */
+	snd_pcm_uframes_t xfer_align;	/* xfer size need to be a multiple */
+	snd_pcm_uframes_t start_threshold;
+	snd_pcm_uframes_t stop_threshold;
+	snd_pcm_uframes_t silence_threshold; /* Silence filling happens when
+						noise is nearest than this */
+	snd_pcm_uframes_t silence_size;	/* Silence filling size */
+	snd_pcm_uframes_t boundary;	/* pointers wrap point */
+
+	snd_pcm_uframes_t silence_start; /* starting pointer to silence area */
+	snd_pcm_uframes_t silence_filled; /* size filled with silence */
+
+	union snd_pcm_sync_id sync;	/* hardware synchronization ID */
+
+	/* -- mmap -- */
+	volatile struct snd_pcm_mmap_status *status;
+	volatile struct snd_pcm_mmap_control *control;
+	atomic_t mmap_count;
+
+	/* -- locking / scheduling -- */
+	wait_queue_head_t sleep;
+	struct timer_list tick_timer;
+	struct fasync_struct *fasync;
+
+	/* -- private section -- */
+	void *private_data;
+	void (*private_free)(struct snd_pcm_runtime *runtime);
+
+	/* -- hardware description -- */
+	struct snd_pcm_hardware hw;
+	struct snd_pcm_hw_constraints hw_constraints;
+
+	/* -- interrupt callbacks -- */
+	void (*transfer_ack_begin)(struct snd_pcm_substream *substream);
+	void (*transfer_ack_end)(struct snd_pcm_substream *substream);
+
+	/* -- timer -- */
+	unsigned int timer_resolution;	/* timer resolution */
+
+	/* -- DMA -- */           
+	unsigned char *dma_area;	/* DMA area */
+	dma_addr_t dma_addr;		/* physical bus address (not accessible from main CPU) */
+	size_t dma_bytes;		/* size of DMA area */
+
+	struct snd_dma_buffer *dma_buffer_p;	/* allocated buffer */
+
+#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
+	/* -- OSS things -- */
+	struct snd_pcm_oss_runtime oss;
+#endif
+};
+
+struct snd_pcm_group {		/* keep linked substreams */
+	spinlock_t lock;
+	struct list_head substreams;
+	int count;
+};
+
+struct snd_pcm_substream {
+	struct snd_pcm *pcm;
+	struct snd_pcm_str *pstr;
+	void *private_data;		/* copied from pcm->private_data */
+	int number;
+	char name[32];			/* substream name */
+	int stream;			/* stream (direction) */
+	size_t buffer_bytes_max;	/* limit ring buffer size */
+	struct snd_dma_buffer dma_buffer;
+	unsigned int dma_buf_id;
+	size_t dma_max;
+	/* -- hardware operations -- */
+	struct snd_pcm_ops *ops;
+	/* -- runtime information -- */
+	struct snd_pcm_runtime *runtime;
+        /* -- timer section -- */
+	struct snd_timer *timer;		/* timer */
+	unsigned timer_running: 1;	/* time is running */
+	spinlock_t timer_lock;
+	/* -- next substream -- */
+	struct snd_pcm_substream *next;
+	/* -- linked substreams -- */
+	struct list_head link_list;	/* linked list member */
+	struct snd_pcm_group self_group;	/* fake group for non linked substream (with substream lock inside) */
+	struct snd_pcm_group *group;		/* pointer to current group */
+	/* -- assigned files -- */
+	struct snd_pcm_file *file;
+	struct file *ffile;
+#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
+	/* -- OSS things -- */
+	struct snd_pcm_oss_substream oss;
+#endif
+	struct snd_info_entry *proc_root;
+	struct snd_info_entry *proc_info_entry;
+	struct snd_info_entry *proc_hw_params_entry;
+	struct snd_info_entry *proc_sw_params_entry;
+	struct snd_info_entry *proc_status_entry;
+	struct snd_info_entry *proc_prealloc_entry;
+	/* misc flags */
+	unsigned int no_mmap_ctrl: 1;
+};
+
+#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
+#define SUBSTREAM_BUSY(substream) ((substream)->file != NULL || ((substream)->oss.file != NULL))
+#else
+#define SUBSTREAM_BUSY(substream) ((substream)->file != NULL)
+#endif
+
+
+struct snd_pcm_str {
+	int stream;				/* stream (direction) */
+	struct snd_pcm *pcm;
+	/* -- substreams -- */
+	unsigned int substream_count;
+	unsigned int substream_opened;
+	struct snd_pcm_substream *substream;
+#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
+	/* -- OSS things -- */
+	struct snd_pcm_oss_stream oss;
+#endif
+	struct snd_pcm_file *files;
+	struct snd_minor *reg;
+	struct snd_info_entry *proc_root;
+	struct snd_info_entry *proc_info_entry;
+#ifdef CONFIG_SND_DEBUG
+	unsigned int xrun_debug;	/* 0 = disabled, 1 = verbose, 2 = stacktrace */
+	struct snd_info_entry *proc_xrun_debug_entry;
+#endif
+};
+
+struct snd_pcm {
+	struct snd_card *card;
+	unsigned int device;	/* device number */
+	unsigned int info_flags;
+	unsigned short dev_class;
+	unsigned short dev_subclass;
+	char id[64];
+	char name[80];
+	struct snd_pcm_str streams[2];
+	struct semaphore open_mutex;
+	wait_queue_head_t open_wait;
+	void *private_data;
+	void (*private_free) (struct snd_pcm *pcm);
+#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
+	struct snd_pcm_oss oss;
+#endif
+};
+
+struct snd_pcm_notify {
+	int (*n_register) (struct snd_pcm * pcm);
+	int (*n_disconnect) (struct snd_pcm * pcm);
+	int (*n_unregister) (struct snd_pcm * pcm);
+	struct list_head list;
+};
+
+/*
+ *  Registering
+ */
+
+extern struct snd_pcm *snd_pcm_devices[];
+extern struct snd_minor snd_pcm_reg[2];
+
+int snd_pcm_new(struct snd_card *card, char *id, int device,
+		int playback_count, int capture_count,
+		struct snd_pcm **rpcm);
+int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count);
+
+int snd_pcm_notify(struct snd_pcm_notify *notify, int nfree);
+
+/*
+ *  Native I/O
+ */
+
+extern rwlock_t snd_pcm_link_rwlock;
+
+int snd_pcm_info(struct snd_pcm_substream *substream, struct snd_pcm_info *info);
+int snd_pcm_info_user(struct snd_pcm_substream *substream,
+		      struct snd_pcm_info __user *info);
+int snd_pcm_status(struct snd_pcm_substream *substream,
+		   struct snd_pcm_status *status);
+int snd_pcm_prepare(struct snd_pcm_substream *substream);
+int snd_pcm_start(struct snd_pcm_substream *substream);
+int snd_pcm_stop(struct snd_pcm_substream *substream, int status);
+int snd_pcm_drain_done(struct snd_pcm_substream *substream);
+#ifdef CONFIG_PM
+int snd_pcm_suspend(struct snd_pcm_substream *substream);
+int snd_pcm_suspend_all(struct snd_pcm *pcm);
+#endif
+int snd_pcm_kernel_playback_ioctl(struct snd_pcm_substream *substream, unsigned int cmd, void *arg);
+int snd_pcm_kernel_capture_ioctl(struct snd_pcm_substream *substream, unsigned int cmd, void *arg);
+int snd_pcm_kernel_ioctl(struct snd_pcm_substream *substream, unsigned int cmd, void *arg);
+int snd_pcm_open_substream(struct snd_pcm *pcm, int stream, struct snd_pcm_substream **rsubstream);
+void snd_pcm_release_substream(struct snd_pcm_substream *substream);
+void snd_pcm_vma_notify_data(void *client, void *data);
+int snd_pcm_mmap_data(struct snd_pcm_substream *substream, struct file *file, struct vm_area_struct *area);
+
+#if BITS_PER_LONG >= 64
+
+static inline void div64_32(u_int64_t *n, u_int32_t div, u_int32_t *rem)
+{
+	*rem = *n % div;
+	*n /= div;
+}
+
+#elif defined(i386)
+
+static inline void div64_32(u_int64_t *n, u_int32_t div, u_int32_t *rem)
+{
+	u_int32_t low, high;
+	low = *n & 0xffffffff;
+	high = *n >> 32;
+	if (high) {
+		u_int32_t high1 = high % div;
+		high /= div;
+		asm("divl %2":"=a" (low), "=d" (*rem):"rm" (div), "a" (low), "d" (high1));
+		*n = (u_int64_t)high << 32 | low;
+	} else {
+		*n = low / div;
+		*rem = low % div;
+	}
+}
+#else
+
+static inline void divl(u_int32_t high, u_int32_t low,
+			u_int32_t div,
+			u_int32_t *q, u_int32_t *r)
+{
+	u_int64_t n = (u_int64_t)high << 32 | low;
+	u_int64_t d = (u_int64_t)div << 31;
+	u_int32_t q1 = 0;
+	int c = 32;
+	while (n > 0xffffffffU) {
+		q1 <<= 1;
+		if (n >= d) {
+			n -= d;
+			q1 |= 1;
+		}
+		d >>= 1;
+		c--;
+	}
+	q1 <<= c;
+	if (n) {
+		low = n;
+		*q = q1 | (low / div);
+		*r = low % div;
+	} else {
+		*r = 0;
+		*q = q1;
+	}
+	return;
+}
+
+static inline void div64_32(u_int64_t *n, u_int32_t div, u_int32_t *rem)
+{
+	u_int32_t low, high;
+	low = *n & 0xffffffff;
+	high = *n >> 32;
+	if (high) {
+		u_int32_t high1 = high % div;
+		u_int32_t low1 = low;
+		high /= div;
+		divl(high1, low1, div, &low, rem);
+		*n = (u_int64_t)high << 32 | low;
+	} else {
+		*n = low / div;
+		*rem = low % div;
+	}
+}
+#endif
+
+/*
+ *  PCM library
+ */
+
+static inline int snd_pcm_stream_linked(struct snd_pcm_substream *substream)
+{
+	return substream->group != &substream->self_group;
+}
+
+static inline void snd_pcm_stream_lock(struct snd_pcm_substream *substream)
+{
+	read_lock(&snd_pcm_link_rwlock);
+	spin_lock(&substream->self_group.lock);
+}
+
+static inline void snd_pcm_stream_unlock(struct snd_pcm_substream *substream)
+{
+	spin_unlock(&substream->self_group.lock);
+	read_unlock(&snd_pcm_link_rwlock);
+}
+
+static inline void snd_pcm_stream_lock_irq(struct snd_pcm_substream *substream)
+{
+	read_lock_irq(&snd_pcm_link_rwlock);
+	spin_lock(&substream->self_group.lock);
+}
+
+static inline void snd_pcm_stream_unlock_irq(struct snd_pcm_substream *substream)
+{
+	spin_unlock(&substream->self_group.lock);
+	read_unlock_irq(&snd_pcm_link_rwlock);
+}
+
+#define snd_pcm_stream_lock_irqsave(substream, flags) \
+do { \
+	read_lock_irqsave(&snd_pcm_link_rwlock, (flags)); \
+	spin_lock(&substream->self_group.lock); \
+} while (0)
+
+#define snd_pcm_stream_unlock_irqrestore(substream, flags) \
+do { \
+	spin_unlock(&substream->self_group.lock); \
+	read_unlock_irqrestore(&snd_pcm_link_rwlock, (flags)); \
+} while (0)
+
+#define snd_pcm_group_for_each(pos, substream) \
+	list_for_each(pos, &substream->group->substreams)
+
+#define snd_pcm_group_substream_entry(pos) \
+	list_entry(pos, struct snd_pcm_substream, link_list)
+
+static inline int snd_pcm_running(struct snd_pcm_substream *substream)
+{
+	return (substream->runtime->status->state == SNDRV_PCM_STATE_RUNNING ||
+		(substream->runtime->status->state == SNDRV_PCM_STATE_DRAINING &&
+		 substream->stream == SNDRV_PCM_STREAM_PLAYBACK));
+}
+
+static inline ssize_t bytes_to_samples(struct snd_pcm_runtime *runtime, ssize_t size)
+{
+	return size * 8 / runtime->sample_bits;
+}
+
+static inline snd_pcm_sframes_t bytes_to_frames(struct snd_pcm_runtime *runtime, ssize_t size)
+{
+	return size * 8 / runtime->frame_bits;
+}
+
+static inline ssize_t samples_to_bytes(struct snd_pcm_runtime *runtime, ssize_t size)
+{
+	return size * runtime->sample_bits / 8;
+}
+
+static inline ssize_t frames_to_bytes(struct snd_pcm_runtime *runtime, snd_pcm_sframes_t size)
+{
+	return size * runtime->frame_bits / 8;
+}
+
+static inline int frame_aligned(struct snd_pcm_runtime *runtime, ssize_t bytes)
+{
+	return bytes % runtime->byte_align == 0;
+}
+
+static inline size_t snd_pcm_lib_buffer_bytes(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	return frames_to_bytes(runtime, runtime->buffer_size);
+}
+
+static inline size_t snd_pcm_lib_period_bytes(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	return frames_to_bytes(runtime, runtime->period_size);
+}
+
+/*
+ *  result is: 0 ... (boundary - 1)
+ */
+static inline snd_pcm_uframes_t snd_pcm_playback_avail(struct snd_pcm_runtime *runtime)
+{
+	snd_pcm_sframes_t avail = runtime->status->hw_ptr + runtime->buffer_size - runtime->control->appl_ptr;
+	if (avail < 0)
+		avail += runtime->boundary;
+	else if ((snd_pcm_uframes_t) avail >= runtime->boundary)
+		avail -= runtime->boundary;
+	return avail;
+}
+
+/*
+ *  result is: 0 ... (boundary - 1)
+ */
+static inline snd_pcm_uframes_t snd_pcm_capture_avail(struct snd_pcm_runtime *runtime)
+{
+	snd_pcm_sframes_t avail = runtime->status->hw_ptr - runtime->control->appl_ptr;
+	if (avail < 0)
+		avail += runtime->boundary;
+	return avail;
+}
+
+static inline snd_pcm_sframes_t snd_pcm_playback_hw_avail(struct snd_pcm_runtime *runtime)
+{
+	return runtime->buffer_size - snd_pcm_playback_avail(runtime);
+}
+
+static inline snd_pcm_sframes_t snd_pcm_capture_hw_avail(struct snd_pcm_runtime *runtime)
+{
+	return runtime->buffer_size - snd_pcm_capture_avail(runtime);
+}
+
+/**
+ * snd_pcm_playback_ready - check whether the playback buffer is available
+ * @substream: the pcm substream instance
+ *
+ * Checks whether enough free space is available on the playback buffer.
+ *
+ * Returns non-zero if available, or zero if not.
+ */
+static inline int snd_pcm_playback_ready(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	return snd_pcm_playback_avail(runtime) >= runtime->control->avail_min;
+}
+
+/**
+ * snd_pcm_capture_ready - check whether the capture buffer is available
+ * @substream: the pcm substream instance
+ *
+ * Checks whether enough capture data is available on the capture buffer.
+ *
+ * Returns non-zero if available, or zero if not.
+ */
+static inline int snd_pcm_capture_ready(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	return snd_pcm_capture_avail(runtime) >= runtime->control->avail_min;
+}
+
+/**
+ * snd_pcm_playback_data - check whether any data exists on the playback buffer
+ * @substream: the pcm substream instance
+ *
+ * Checks whether any data exists on the playback buffer. If stop_threshold
+ * is bigger or equal to boundary, then this function returns always non-zero.
+ *
+ * Returns non-zero if exists, or zero if not.
+ */
+static inline int snd_pcm_playback_data(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	
+	if (runtime->stop_threshold >= runtime->boundary)
+		return 1;
+	return snd_pcm_playback_avail(runtime) < runtime->buffer_size;
+}
+
+/**
+ * snd_pcm_playback_empty - check whether the playback buffer is empty
+ * @substream: the pcm substream instance
+ *
+ * Checks whether the playback buffer is empty.
+ *
+ * Returns non-zero if empty, or zero if not.
+ */
+static inline int snd_pcm_playback_empty(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	return snd_pcm_playback_avail(runtime) >= runtime->buffer_size;
+}
+
+/**
+ * snd_pcm_capture_empty - check whether the capture buffer is empty
+ * @substream: the pcm substream instance
+ *
+ * Checks whether the capture buffer is empty.
+ *
+ * Returns non-zero if empty, or zero if not.
+ */
+static inline int snd_pcm_capture_empty(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	return snd_pcm_capture_avail(runtime) == 0;
+}
+
+static inline void snd_pcm_trigger_done(struct snd_pcm_substream *substream, 
+					struct snd_pcm_substream *master)
+{
+	substream->runtime->trigger_master = master;
+}
+
+static inline int hw_is_mask(int var)
+{
+	return var >= SNDRV_PCM_HW_PARAM_FIRST_MASK &&
+		var <= SNDRV_PCM_HW_PARAM_LAST_MASK;
+}
+
+static inline int hw_is_interval(int var)
+{
+	return var >= SNDRV_PCM_HW_PARAM_FIRST_INTERVAL &&
+		var <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL;
+}
+
+static inline struct snd_mask *hw_param_mask(struct snd_pcm_hw_params *params,
+				     snd_pcm_hw_param_t var)
+{
+	return &params->masks[var - SNDRV_PCM_HW_PARAM_FIRST_MASK];
+}
+
+static inline struct snd_interval *hw_param_interval(struct snd_pcm_hw_params *params,
+					     snd_pcm_hw_param_t var)
+{
+	return &params->intervals[var - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL];
+}
+
+static inline const struct snd_mask *hw_param_mask_c(const struct snd_pcm_hw_params *params,
+					     snd_pcm_hw_param_t var)
+{
+	return (const struct snd_mask *)hw_param_mask((struct snd_pcm_hw_params*) params, var);
+}
+
+static inline const struct snd_interval *hw_param_interval_c(const struct snd_pcm_hw_params *params,
+						     snd_pcm_hw_param_t var)
+{
+	return (const struct snd_interval *)hw_param_interval((struct snd_pcm_hw_params*) params, var);
+}
+
+#define params_access(p) snd_mask_min(hw_param_mask((p), SNDRV_PCM_HW_PARAM_ACCESS))
+#define params_format(p) snd_mask_min(hw_param_mask((p), SNDRV_PCM_HW_PARAM_FORMAT))
+#define params_subformat(p) snd_mask_min(hw_param_mask((p), SNDRV_PCM_HW_PARAM_SUBFORMAT))
+#define params_channels(p) hw_param_interval((p), SNDRV_PCM_HW_PARAM_CHANNELS)->min
+#define params_rate(p) hw_param_interval((p), SNDRV_PCM_HW_PARAM_RATE)->min
+#define params_period_size(p) hw_param_interval((p), SNDRV_PCM_HW_PARAM_PERIOD_SIZE)->min
+#define params_period_bytes(p) ((params_period_size(p)*snd_pcm_format_physical_width(params_format(p))*params_channels(p))/8)
+#define params_periods(p) hw_param_interval((p), SNDRV_PCM_HW_PARAM_PERIODS)->min
+#define params_buffer_size(p) hw_param_interval((p), SNDRV_PCM_HW_PARAM_BUFFER_SIZE)->min
+#define params_buffer_bytes(p) hw_param_interval((p), SNDRV_PCM_HW_PARAM_BUFFER_BYTES)->min
+#define params_tick_time(p) hw_param_interval((p), SNDRV_PCM_HW_PARAM_TICK_TIME)->min
+
+
+int snd_interval_refine(struct snd_interval *i, const struct snd_interval *v);
+void snd_interval_mul(const struct snd_interval *a, const struct snd_interval *b, struct snd_interval *c);
+void snd_interval_div(const struct snd_interval *a, const struct snd_interval *b, struct snd_interval *c);
+void snd_interval_muldivk(const struct snd_interval *a, const struct snd_interval *b, 
+			  unsigned int k, struct snd_interval *c);
+void snd_interval_mulkdiv(const struct snd_interval *a, unsigned int k,
+			  const struct snd_interval *b, struct snd_interval *c);
+int snd_interval_list(struct snd_interval *i, unsigned int count, unsigned int *list, unsigned int mask);
+int snd_interval_ratnum(struct snd_interval *i,
+			unsigned int rats_count, struct snd_ratnum *rats,
+			unsigned int *nump, unsigned int *denp);
+
+void _snd_pcm_hw_params_any(struct snd_pcm_hw_params *params);
+void _snd_pcm_hw_param_setempty(struct snd_pcm_hw_params *params, snd_pcm_hw_param_t var);
+int snd_pcm_hw_param_near(struct snd_pcm_substream *substream, 
+			  struct snd_pcm_hw_params *params,
+			  snd_pcm_hw_param_t var, 
+			  unsigned int val, int *dir);
+int snd_pcm_hw_param_set(struct snd_pcm_substream *pcm,
+			 struct snd_pcm_hw_params *params,
+			 snd_pcm_hw_param_t var,
+			 unsigned int val, int dir);
+int snd_pcm_hw_params_choose(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params);
+
+int snd_pcm_hw_refine(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params);
+
+int snd_pcm_hw_constraints_init(struct snd_pcm_substream *substream);
+int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream);
+
+int snd_pcm_hw_constraint_mask(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var,
+			       u_int32_t mask);
+int snd_pcm_hw_constraint_mask64(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var,
+				 u_int64_t mask);
+int snd_pcm_hw_constraint_minmax(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var,
+				 unsigned int min, unsigned int max);
+int snd_pcm_hw_constraint_integer(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var);
+int snd_pcm_hw_constraint_list(struct snd_pcm_runtime *runtime, 
+			       unsigned int cond,
+			       snd_pcm_hw_param_t var,
+			       struct snd_pcm_hw_constraint_list *l);
+int snd_pcm_hw_constraint_ratnums(struct snd_pcm_runtime *runtime, 
+				  unsigned int cond,
+				  snd_pcm_hw_param_t var,
+				  struct snd_pcm_hw_constraint_ratnums *r);
+int snd_pcm_hw_constraint_ratdens(struct snd_pcm_runtime *runtime, 
+				  unsigned int cond,
+				  snd_pcm_hw_param_t var,
+				  struct snd_pcm_hw_constraint_ratdens *r);
+int snd_pcm_hw_constraint_msbits(struct snd_pcm_runtime *runtime, 
+				 unsigned int cond,
+				 unsigned int width,
+				 unsigned int msbits);
+int snd_pcm_hw_constraint_step(struct snd_pcm_runtime *runtime,
+			       unsigned int cond,
+			       snd_pcm_hw_param_t var,
+			       unsigned long step);
+int snd_pcm_hw_constraint_pow2(struct snd_pcm_runtime *runtime,
+			       unsigned int cond,
+			       snd_pcm_hw_param_t var);
+int snd_pcm_hw_rule_add(struct snd_pcm_runtime *runtime,
+			unsigned int cond,
+			int var,
+			snd_pcm_hw_rule_func_t func, void *private,
+			int dep, ...);
+
+int snd_pcm_format_signed(snd_pcm_format_t format);
+int snd_pcm_format_unsigned(snd_pcm_format_t format);
+int snd_pcm_format_linear(snd_pcm_format_t format);
+int snd_pcm_format_little_endian(snd_pcm_format_t format);
+int snd_pcm_format_big_endian(snd_pcm_format_t format);
+#if 0 /* just for DocBook */
+/**
+ * snd_pcm_format_cpu_endian - Check the PCM format is CPU-endian
+ * @format: the format to check
+ *
+ * Returns 1 if the given PCM format is CPU-endian, 0 if
+ * opposite, or a negative error code if endian not specified.
+ */
+int snd_pcm_format_cpu_endian(snd_pcm_format_t format);
+#endif /* DocBook */
+#ifdef SNDRV_LITTLE_ENDIAN
+#define snd_pcm_format_cpu_endian(format) snd_pcm_format_little_endian(format)
+#else
+#define snd_pcm_format_cpu_endian(format) snd_pcm_format_big_endian(format)
+#endif
+int snd_pcm_format_width(snd_pcm_format_t format);			/* in bits */
+int snd_pcm_format_physical_width(snd_pcm_format_t format);		/* in bits */
+ssize_t snd_pcm_format_size(snd_pcm_format_t format, size_t samples);
+const unsigned char *snd_pcm_format_silence_64(snd_pcm_format_t format);
+int snd_pcm_format_set_silence(snd_pcm_format_t format, void *buf, unsigned int frames);
+snd_pcm_format_t snd_pcm_build_linear_format(int width, int unsignd, int big_endian);
+const char *snd_pcm_format_name(snd_pcm_format_t format);
+
+void snd_pcm_set_ops(struct snd_pcm * pcm, int direction, struct snd_pcm_ops *ops);
+void snd_pcm_set_sync(struct snd_pcm_substream *substream);
+int snd_pcm_lib_interleave_len(struct snd_pcm_substream *substream);
+int snd_pcm_lib_ioctl(struct snd_pcm_substream *substream,
+		      unsigned int cmd, void *arg);                      
+int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream);
+int snd_pcm_playback_xrun_check(struct snd_pcm_substream *substream);
+int snd_pcm_capture_xrun_check(struct snd_pcm_substream *substream);
+int snd_pcm_playback_xrun_asap(struct snd_pcm_substream *substream);
+int snd_pcm_capture_xrun_asap(struct snd_pcm_substream *substream);
+void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_uframes_t new_hw_ptr);
+void snd_pcm_tick_prepare(struct snd_pcm_substream *substream);
+void snd_pcm_tick_set(struct snd_pcm_substream *substream, unsigned long ticks);
+void snd_pcm_tick_elapsed(struct snd_pcm_substream *substream);
+void snd_pcm_period_elapsed(struct snd_pcm_substream *substream);
+snd_pcm_sframes_t snd_pcm_lib_write(struct snd_pcm_substream *substream,
+				    const void __user *buf,
+				    snd_pcm_uframes_t frames);
+snd_pcm_sframes_t snd_pcm_lib_read(struct snd_pcm_substream *substream,
+				   void __user *buf, snd_pcm_uframes_t frames);
+snd_pcm_sframes_t snd_pcm_lib_writev(struct snd_pcm_substream *substream,
+				     void __user **bufs, snd_pcm_uframes_t frames);
+snd_pcm_sframes_t snd_pcm_lib_readv(struct snd_pcm_substream *substream,
+				    void __user **bufs, snd_pcm_uframes_t frames);
+
+int snd_pcm_limit_hw_rates(struct snd_pcm_runtime *runtime);
+
+static inline void snd_pcm_set_runtime_buffer(struct snd_pcm_substream *substream,
+					      struct snd_dma_buffer *bufp)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	if (bufp) {
+		runtime->dma_buffer_p = bufp;
+		runtime->dma_area = bufp->area;
+		runtime->dma_addr = bufp->addr;
+		runtime->dma_bytes = bufp->bytes;
+	} else {
+		runtime->dma_buffer_p = NULL;
+		runtime->dma_area = NULL;
+		runtime->dma_addr = 0;
+		runtime->dma_bytes = 0;
+	}
+}
+
+/*
+ *  Timer interface
+ */
+
+void snd_pcm_timer_resolution_change(struct snd_pcm_substream *substream);
+void snd_pcm_timer_init(struct snd_pcm_substream *substream);
+void snd_pcm_timer_done(struct snd_pcm_substream *substream);
+
+/*
+ *  Memory
+ */
+
+int snd_pcm_lib_preallocate_free(struct snd_pcm_substream *substream);
+int snd_pcm_lib_preallocate_free_for_all(struct snd_pcm *pcm);
+int snd_pcm_lib_preallocate_pages(struct snd_pcm_substream *substream,
+				  int type, struct device *data,
+				  size_t size, size_t max);
+int snd_pcm_lib_preallocate_pages_for_all(struct snd_pcm *pcm,
+					  int type, void *data,
+					  size_t size, size_t max);
+int snd_pcm_lib_malloc_pages(struct snd_pcm_substream *substream, size_t size);
+int snd_pcm_lib_free_pages(struct snd_pcm_substream *substream);
+
+#define snd_pcm_substream_sgbuf(substream) ((substream)->runtime->dma_buffer_p->private_data)
+#define snd_pcm_sgbuf_pages(size) snd_sgbuf_aligned_pages(size)
+#define snd_pcm_sgbuf_get_addr(sgbuf,ofs) snd_sgbuf_get_addr(sgbuf,ofs)
+struct page *snd_pcm_sgbuf_ops_page(struct snd_pcm_substream *substream, unsigned long offset);
+
+/* handle mmap counter - PCM mmap callback should handle this counter properly */
+static inline void snd_pcm_mmap_data_open(struct vm_area_struct *area)
+{
+	struct snd_pcm_substream *substream = (struct snd_pcm_substream *)area->vm_private_data;
+	atomic_inc(&substream->runtime->mmap_count);
+}
+
+static inline void snd_pcm_mmap_data_close(struct vm_area_struct *area)
+{
+	struct snd_pcm_substream *substream = (struct snd_pcm_substream *)area->vm_private_data;
+	atomic_dec(&substream->runtime->mmap_count);
+}
+
+/* mmap for io-memory area */
+#if defined(CONFIG_X86) || defined(CONFIG_PPC) || defined(CONFIG_ALPHA)
+#define SNDRV_PCM_INFO_MMAP_IOMEM	SNDRV_PCM_INFO_MMAP
+int snd_pcm_lib_mmap_iomem(struct snd_pcm_substream *substream, struct vm_area_struct *area);
+#else
+#define SNDRV_PCM_INFO_MMAP_IOMEM	0
+#define snd_pcm_lib_mmap_iomem	NULL
+#endif
+
+static inline void snd_pcm_limit_isa_dma_size(int dma, size_t *max)
+{
+	*max = dma < 4 ? 64 * 1024 : 128 * 1024;
+}
+
+/*
+ *  Misc
+ */
+
+#define SNDRV_PCM_DEFAULT_CON_SPDIF	(IEC958_AES0_CON_EMPHASIS_NONE|\
+					 (IEC958_AES1_CON_ORIGINAL<<8)|\
+					 (IEC958_AES1_CON_PCM_CODER<<8)|\
+					 (IEC958_AES3_CON_FS_48000<<24))
+
+#endif /* __SOUND_PCM_H */
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/pcm_oss.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/pcm_oss.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/pcm_oss.h	(revision 91)
@@ -0,0 +1,83 @@
+#ifndef __PCM_OSS_H
+#define __PCM_OSS_H
+
+/*
+ *  Digital Audio (PCM) - OSS compatibility abstract layer
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "pcm_plugin.h"
+
+typedef struct _snd_pcm_oss_setup snd_pcm_oss_setup_t;
+
+struct _snd_pcm_oss_setup {
+	char *task_name;
+	unsigned int disable:1,
+		     direct:1,
+		     block:1,
+		     nonblock:1;
+	unsigned int periods;
+	unsigned int period_size;
+	snd_pcm_oss_setup_t *next;
+};
+
+typedef struct _snd_pcm_oss_runtime {
+	int params: 1,				/* format/parameter change */
+            prepare: 1,				/* need to prepare the operation */
+            trigger: 1,				/* trigger flag */
+            sync_trigger: 1;			/* sync trigger flag */
+	int rate;				/* requested rate */
+	int format;				/* requested OSS format */
+	unsigned int channels;			/* requested channels */
+	unsigned int fragshift;
+	unsigned int maxfrags;
+	unsigned int subdivision;		/* requested subdivision */
+	size_t period_bytes;			/* requested period size */
+	unsigned int periods;
+	size_t buffer_bytes;			/* requested period size */
+	size_t bytes;				/* total # bytes processed */
+	size_t mmap_bytes;
+	char *buffer;				/* vmallocated period */
+	size_t buffer_used;			/* used length from buffer */
+	snd_pcm_plugin_t *plugin_first;
+	snd_pcm_plugin_t *plugin_last;
+	unsigned int prev_hw_ptr_interrupt;
+} snd_pcm_oss_runtime_t;
+
+typedef struct _snd_pcm_oss_file {
+	snd_pcm_substream_t *streams[2];
+} snd_pcm_oss_file_t;
+
+typedef struct _snd_pcm_oss_substream {
+	int oss: 1;				/* oss mode */
+	snd_pcm_oss_setup_t *setup;		/* active setup */
+	snd_pcm_oss_file_t *file;
+} snd_pcm_oss_substream_t;
+
+typedef struct _snd_pcm_oss_stream {
+	snd_pcm_oss_setup_t *setup_list;	/* setup list */
+        struct semaphore setup_mutex;
+	snd_info_entry_t *proc_entry;
+} snd_pcm_oss_stream_t;
+
+typedef struct _snd_pcm_oss {
+	int reg;
+} snd_pcm_oss_t;
+
+#endif /* __PCM_OSS_H */
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/pcm_params.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/pcm_params.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/pcm_params.h	(revision 91)
@@ -0,0 +1,365 @@
+#ifndef __PCM_PARAMS_H
+#define __PCM_PARAMS_H
+
+/*
+ *  PCM params helpers
+ *  Copyright (c) by Abramo Bagnara <abramo@alsa-project.org>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+extern int snd_pcm_hw_param_mask(snd_pcm_substream_t *pcm, snd_pcm_hw_params_t *params,
+				 snd_pcm_hw_param_t var, const snd_mask_t *val);
+extern unsigned int snd_pcm_hw_param_value_min(const snd_pcm_hw_params_t *params,
+					       snd_pcm_hw_param_t var, int *dir);
+extern unsigned int snd_pcm_hw_param_value_max(const snd_pcm_hw_params_t *params,
+					       snd_pcm_hw_param_t var, int *dir);
+extern int _snd_pcm_hw_param_min(snd_pcm_hw_params_t *params,
+				 snd_pcm_hw_param_t var, unsigned int val, int dir);
+extern int _snd_pcm_hw_param_setinteger(snd_pcm_hw_params_t *params,
+					snd_pcm_hw_param_t var);
+extern int _snd_pcm_hw_param_set(snd_pcm_hw_params_t *params,
+				 snd_pcm_hw_param_t var, unsigned int val, int dir);
+
+#define INLINE static inline
+#define SNDRV_MASK_BITS 64      /* we use so far 64bits only */
+#define SNDRV_MASK_SIZE (SNDRV_MASK_BITS / 32)
+#define MASK_OFS(i)     ((i) >> 5)
+#define MASK_BIT(i)     (1U << ((i) & 31))
+
+#define assert(a)
+
+INLINE unsigned int ld2(u_int32_t v)
+{
+        unsigned r = 0;
+
+        if (v >= 0x10000) {
+                v >>= 16;
+                r += 16;
+        }
+        if (v >= 0x100) {
+                v >>= 8;
+                r += 8;
+        }
+        if (v >= 0x10) {
+                v >>= 4;
+                r += 4;
+        }
+        if (v >= 4) {
+                v >>= 2;
+                r += 2;
+        }
+        if (v >= 2)
+                r++;
+        return r;
+}
+
+INLINE size_t snd_mask_sizeof(void)
+{
+	return sizeof(snd_mask_t);
+}
+
+INLINE void snd_mask_none(snd_mask_t *mask)
+{
+	memset(mask, 0, sizeof(*mask));
+}
+
+INLINE void snd_mask_any(snd_mask_t *mask)
+{
+	memset(mask, 0xff, SNDRV_MASK_SIZE * sizeof(u_int32_t));
+}
+
+INLINE int snd_mask_empty(const snd_mask_t *mask)
+{
+	int i;
+	for (i = 0; i < SNDRV_MASK_SIZE; i++)
+		if (mask->bits[i])
+			return 0;
+	return 1;
+}
+
+INLINE unsigned int snd_mask_min(const snd_mask_t *mask)
+{
+	int i;
+	assert(!snd_mask_empty(mask));
+	for (i = 0; i < SNDRV_MASK_SIZE; i++) {
+		if (mask->bits[i])
+			return ffs(mask->bits[i]) - 1 + (i << 5);
+	}
+	return 0;
+}
+
+INLINE unsigned int snd_mask_max(const snd_mask_t *mask)
+{
+	int i;
+	assert(!snd_mask_empty(mask));
+	for (i = SNDRV_MASK_SIZE - 1; i >= 0; i--) {
+		if (mask->bits[i])
+			return ld2(mask->bits[i]) + (i << 5);
+	}
+	return 0;
+}
+
+INLINE void snd_mask_set(snd_mask_t *mask, unsigned int val)
+{
+	assert(val <= SNDRV_MASK_BITS);
+	mask->bits[MASK_OFS(val)] |= MASK_BIT(val);
+}
+
+INLINE void snd_mask_reset(snd_mask_t *mask, unsigned int val)
+{
+	assert(val <= SNDRV_MASK_BITS);
+	mask->bits[MASK_OFS(val)] &= ~MASK_BIT(val);
+}
+
+INLINE void snd_mask_set_range(snd_mask_t *mask, unsigned int from, unsigned int to)
+{
+	unsigned int i;
+	assert(to <= SNDRV_MASK_BITS && from <= to);
+	for (i = from; i <= to; i++)
+		mask->bits[MASK_OFS(i)] |= MASK_BIT(i);
+}
+
+INLINE void snd_mask_reset_range(snd_mask_t *mask, unsigned int from, unsigned int to)
+{
+	unsigned int i;
+	assert(to <= SNDRV_MASK_BITS && from <= to);
+	for (i = from; i <= to; i++)
+		mask->bits[MASK_OFS(i)] &= ~MASK_BIT(i);
+}
+
+INLINE void snd_mask_leave(snd_mask_t *mask, unsigned int val)
+{
+	unsigned int v;
+	assert(val <= SNDRV_MASK_BITS);
+	v = mask->bits[MASK_OFS(val)] & MASK_BIT(val);
+	snd_mask_none(mask);
+	mask->bits[MASK_OFS(val)] = v;
+}
+
+INLINE void snd_mask_intersect(snd_mask_t *mask, const snd_mask_t *v)
+{
+	int i;
+	for (i = 0; i < SNDRV_MASK_SIZE; i++)
+		mask->bits[i] &= v->bits[i];
+}
+
+INLINE int snd_mask_eq(const snd_mask_t *mask, const snd_mask_t *v)
+{
+	return ! memcmp(mask, v, SNDRV_MASK_SIZE * sizeof(u_int32_t));
+}
+
+INLINE void snd_mask_copy(snd_mask_t *mask, const snd_mask_t *v)
+{
+	*mask = *v;
+}
+
+INLINE int snd_mask_test(const snd_mask_t *mask, unsigned int val)
+{
+	assert(val <= SNDRV_MASK_BITS);
+	return mask->bits[MASK_OFS(val)] & MASK_BIT(val);
+}
+
+INLINE int snd_mask_single(const snd_mask_t *mask)
+{
+	int i, c = 0;
+	assert(!snd_mask_empty(mask));
+	for (i = 0; i < SNDRV_MASK_SIZE; i++) {
+		if (! mask->bits[i])
+			continue;
+		if (mask->bits[i] & (mask->bits[i] - 1))
+			return 0;
+		if (c)
+			return 0;
+		c++;
+	}
+	return 1;
+}
+
+INLINE int snd_mask_refine(snd_mask_t *mask, const snd_mask_t *v)
+{
+	snd_mask_t old;
+	assert(!snd_mask_empty(mask));
+	snd_mask_copy(&old, mask);
+	snd_mask_intersect(mask, v);
+	if (snd_mask_empty(mask))
+		return -EINVAL;
+	return !snd_mask_eq(mask, &old);
+}
+
+INLINE int snd_mask_refine_first(snd_mask_t *mask)
+{
+	assert(!snd_mask_empty(mask));
+	if (snd_mask_single(mask))
+		return 0;
+	snd_mask_leave(mask, snd_mask_min(mask));
+	return 1;
+}
+
+INLINE int snd_mask_refine_last(snd_mask_t *mask)
+{
+	assert(!snd_mask_empty(mask));
+	if (snd_mask_single(mask))
+		return 0;
+	snd_mask_leave(mask, snd_mask_max(mask));
+	return 1;
+}
+
+INLINE int snd_mask_refine_min(snd_mask_t *mask, unsigned int val)
+{
+	assert(!snd_mask_empty(mask));
+	if (snd_mask_min(mask) >= val)
+		return 0;
+	snd_mask_reset_range(mask, 0, val - 1);
+	if (snd_mask_empty(mask))
+		return -EINVAL;
+	return 1;
+}
+
+INLINE int snd_mask_refine_max(snd_mask_t *mask, unsigned int val)
+{
+	assert(!snd_mask_empty(mask));
+	if (snd_mask_max(mask) <= val)
+		return 0;
+	snd_mask_reset_range(mask, val + 1, SNDRV_MASK_BITS);
+	if (snd_mask_empty(mask))
+		return -EINVAL;
+	return 1;
+}
+
+INLINE int snd_mask_refine_set(snd_mask_t *mask, unsigned int val)
+{
+	int changed;
+	assert(!snd_mask_empty(mask));
+	changed = !snd_mask_single(mask);
+	snd_mask_leave(mask, val);
+	if (snd_mask_empty(mask))
+		return -EINVAL;
+	return changed;
+}
+
+INLINE int snd_mask_value(const snd_mask_t *mask)
+{
+	assert(!snd_mask_empty(mask));
+	return snd_mask_min(mask);
+}
+
+
+INLINE void snd_interval_any(snd_interval_t *i)
+{
+	i->min = 0;
+	i->openmin = 0;
+	i->max = UINT_MAX;
+	i->openmax = 0;
+	i->integer = 0;
+	i->empty = 0;
+}
+
+INLINE void snd_interval_none(snd_interval_t *i)
+{
+	i->empty = 1;
+}
+
+INLINE int snd_interval_checkempty(const snd_interval_t *i)
+{
+	return (i->min > i->max ||
+		(i->min == i->max && (i->openmin || i->openmax)));
+}
+
+INLINE int snd_interval_empty(const snd_interval_t *i)
+{
+	return i->empty;
+}
+
+INLINE int snd_interval_single(const snd_interval_t *i)
+{
+	assert(!snd_interval_empty(i));
+	return (i->min == i->max || 
+		(i->min + 1 == i->max && i->openmax));
+}
+
+INLINE int snd_interval_value(const snd_interval_t *i)
+{
+	assert(snd_interval_single(i));
+	return i->min;
+}
+
+INLINE int snd_interval_min(const snd_interval_t *i)
+{
+	assert(!snd_interval_empty(i));
+	return i->min;
+}
+
+INLINE int snd_interval_max(const snd_interval_t *i)
+{
+	unsigned int v;
+	assert(!snd_interval_empty(i));
+	v = i->max;
+	if (i->openmax)
+		v--;
+	return v;
+}
+
+INLINE int snd_interval_test(const snd_interval_t *i, unsigned int val)
+{
+	return !((i->min > val || (i->min == val && i->openmin) ||
+		  i->max < val || (i->max == val && i->openmax)));
+}
+
+INLINE void snd_interval_copy(snd_interval_t *d, const snd_interval_t *s)
+{
+	*d = *s;
+}
+
+INLINE int snd_interval_setinteger(snd_interval_t *i)
+{
+	if (i->integer)
+		return 0;
+	if (i->openmin && i->openmax && i->min == i->max)
+		return -EINVAL;
+	i->integer = 1;
+	return 1;
+}
+
+INLINE int snd_interval_eq(const snd_interval_t *i1, const snd_interval_t *i2)
+{
+	if (i1->empty)
+		return i2->empty;
+	if (i2->empty)
+		return i1->empty;
+	return i1->min == i2->min && i1->openmin == i2->openmin &&
+		i1->max == i2->max && i1->openmax == i2->openmax;
+}
+
+INLINE unsigned int add(unsigned int a, unsigned int b)
+{
+	if (a >= UINT_MAX - b)
+		return UINT_MAX;
+	return a + b;
+}
+
+INLINE unsigned int sub(unsigned int a, unsigned int b)
+{
+	if (a > b)
+		return a - b;
+	return 0;
+}
+
+#undef INLINE
+#undef assert
+
+#endif
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/pcm_plugin.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/pcm_plugin.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/pcm_plugin.h	(revision 91)
@@ -0,0 +1,313 @@
+#ifndef __PCM_PLUGIN_H
+#define __PCM_PLUGIN_H
+
+/*
+ *  Digital Audio (Plugin interface) abstract layer
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef ATTRIBUTE_UNUSED
+#define ATTRIBUTE_UNUSED __attribute__ ((__unused__))
+#endif
+
+typedef unsigned int bitset_t;
+
+#ifdef TARGET_OS2
+#define bitset_size(nbits) ((nbits + sizeof(bitset_t) * 8 - 1) / (sizeof(bitset_t) * 8))
+
+#define bitset_alloc(nbits) snd_kcalloc(bitset_size(nbits) * sizeof(bitset_t), GFP_KERNEL)
+	
+#define bitset_set(bitmap, pos) bitmap[pos / sizeof(*bitmap) * 8] |= 1 << (pos % sizeof(*bitmap) * 8)
+ 
+
+#define bitset_reset(bitmap, pos) bitmap[pos / sizeof(*bitmap) * 8] &= ~(1 << (pos % sizeof(*bitmap) * 8))
+
+#define bitset_get(bitmap, pos)	(!!(bitmap[pos / (sizeof(*bitmap) * 8)] & (1 << (pos % (sizeof(*bitmap) * 8)))))
+
+#define bitset_copy(dst, src, nbits) memcpy(dst, src, bitset_size(nbits) * sizeof(bitset_t));
+
+#define bitset_and(dst, bs, nbits) \
+{ \
+	bitset_t *end = dst + bitset_size(nbits); \
+	while (dst < end) \
+		*dst++ &= *bs++; \
+}
+
+#define bitset_or(dst, bs, nbits) \
+{ \
+	bitset_t *end = dst + bitset_size(nbits); \
+	while (dst < end) \
+		*dst++ |= *bs++; \
+}
+
+#define bitset_zero(dst, nbits) \
+{ \
+	bitset_t *end = dst + bitset_size(nbits); \
+	while (dst < end) \
+		*dst++ = 0; \
+}
+
+#define bitset_one(dst, nbits) \ 
+{ \
+	bitset_t *end = dst + bitset_size(nbits); \
+	while (dst < end) \
+		*dst++ = ~(bitset_t)0; \
+} 
+#else
+inline size_t bitset_size(int nbits)
+{
+	return (nbits + sizeof(bitset_t) * 8 - 1) / (sizeof(bitset_t) * 8);
+}
+
+inline bitset_t *bitset_alloc(int nbits)
+{
+	return snd_kcalloc(bitset_size(nbits) * sizeof(bitset_t), GFP_KERNEL);
+}
+	
+inline void bitset_set(bitset_t *bitmap, unsigned int pos)
+{
+	size_t bits = sizeof(*bitmap) * 8;
+	bitmap[pos / bits] |= 1 << (pos % bits);
+}
+
+inline void bitset_reset(bitset_t *bitmap, unsigned int pos)
+{
+	size_t bits = sizeof(*bitmap) * 8;
+	bitmap[pos / bits] &= ~(1 << (pos % bits));
+}
+
+inline int bitset_get(bitset_t *bitmap, unsigned int pos)
+{
+	size_t bits = sizeof(*bitmap) * 8;
+	return !!(bitmap[pos / bits] & (1 << (pos % bits)));
+}
+
+inline void bitset_copy(bitset_t *dst, bitset_t *src, unsigned int nbits)
+{
+	memcpy(dst, src, bitset_size(nbits) * sizeof(bitset_t));
+}
+
+inline void bitset_and(bitset_t *dst, bitset_t *bs, unsigned int nbits)
+{
+	bitset_t *end = dst + bitset_size(nbits);
+	while (dst < end)
+		*dst++ &= *bs++;
+}
+
+inline void bitset_or(bitset_t *dst, bitset_t *bs, unsigned int nbits)
+{
+	bitset_t *end = dst + bitset_size(nbits);
+	while (dst < end)
+		*dst++ |= *bs++;
+}
+
+inline void bitset_zero(bitset_t *dst, unsigned int nbits)
+{
+	bitset_t *end = dst + bitset_size(nbits);
+	while (dst < end)
+		*dst++ = 0;
+}
+
+inline void bitset_one(bitset_t *dst, unsigned int nbits)
+{
+	bitset_t *end = dst + bitset_size(nbits);
+	while (dst < end)
+		*dst++ = ~(bitset_t)0;
+}
+#endif
+
+typedef struct _snd_pcm_plugin snd_pcm_plugin_t;
+#define snd_pcm_plug_t snd_pcm_substream_t
+#define snd_pcm_plug_stream(plug) ((plug)->stream)
+
+typedef enum {
+	INIT = 0,
+	PREPARE = 1,
+} snd_pcm_plugin_action_t;
+
+typedef struct _snd_pcm_channel_area {
+#ifdef TARGET_OS2
+	char *addr;			/* base address of channel samples */
+#else
+	void *addr;			/* base address of channel samples */
+#endif
+	unsigned int first;		/* offset to first sample in bits */
+	unsigned int step;		/* samples distance in bits */
+} snd_pcm_channel_area_t;
+
+typedef struct _snd_pcm_plugin_channel {
+	void *aptr;			/* pointer to the allocated area */
+	snd_pcm_channel_area_t area;
+	unsigned int enabled:1;		/* channel need to be processed */
+	unsigned int wanted:1;		/* channel is wanted */
+} snd_pcm_plugin_channel_t;
+
+typedef struct _snd_pcm_plugin_format {
+	int format;
+	unsigned int rate;
+	unsigned int channels;
+} snd_pcm_plugin_format_t;
+
+struct _snd_pcm_plugin {
+	const char *name;		/* plug-in name */
+	int stream;
+	snd_pcm_plugin_format_t src_format;	/* source format */
+	snd_pcm_plugin_format_t dst_format;	/* destination format */
+	int src_width;			/* sample width in bits */
+	int dst_width;			/* sample width in bits */
+	int access;
+	snd_pcm_sframes_t (*src_frames)(snd_pcm_plugin_t *plugin, snd_pcm_uframes_t dst_frames);
+	snd_pcm_sframes_t (*dst_frames)(snd_pcm_plugin_t *plugin, snd_pcm_uframes_t src_frames);
+	snd_pcm_sframes_t (*client_channels)(snd_pcm_plugin_t *plugin,
+				 snd_pcm_uframes_t frames,
+				 snd_pcm_plugin_channel_t **channels);
+	int (*src_channels_mask)(snd_pcm_plugin_t *plugin,
+			       bitset_t *dst_vmask,
+			       bitset_t **src_vmask);
+	int (*dst_channels_mask)(snd_pcm_plugin_t *plugin,
+			       bitset_t *src_vmask,
+			       bitset_t **dst_vmask);
+	snd_pcm_sframes_t (*transfer)(snd_pcm_plugin_t *plugin,
+			    const snd_pcm_plugin_channel_t *src_channels,
+			    snd_pcm_plugin_channel_t *dst_channels,
+			    snd_pcm_uframes_t frames);
+	int (*action)(snd_pcm_plugin_t *plugin,
+		      snd_pcm_plugin_action_t action,
+		      unsigned long data);
+	snd_pcm_plugin_t *prev;
+	snd_pcm_plugin_t *next;
+	snd_pcm_plug_t *plug;
+	void *private_data;
+	void (*private_free)(snd_pcm_plugin_t *plugin);
+	char *buf;
+	snd_pcm_uframes_t buf_frames;
+	snd_pcm_plugin_channel_t *buf_channels;
+	bitset_t *src_vmask;
+	bitset_t *dst_vmask;
+#ifdef TARGET_OS2
+	char extra_data[1];
+#else
+	char extra_data[0];
+#endif
+};
+
+int snd_pcm_plugin_build(snd_pcm_plug_t *handle,
+                         const char *name,
+                         snd_pcm_plugin_format_t *src_format,
+                         snd_pcm_plugin_format_t *dst_format,
+                         size_t extra,
+                         snd_pcm_plugin_t **ret);
+int snd_pcm_plugin_free(snd_pcm_plugin_t *plugin);
+int snd_pcm_plugin_clear(snd_pcm_plugin_t **first);
+int snd_pcm_plug_alloc(snd_pcm_plug_t *plug, snd_pcm_uframes_t frames);
+snd_pcm_sframes_t snd_pcm_plug_client_size(snd_pcm_plug_t *handle, snd_pcm_uframes_t drv_size);
+snd_pcm_sframes_t snd_pcm_plug_slave_size(snd_pcm_plug_t *handle, snd_pcm_uframes_t clt_size);
+
+#define ROUTE_PLUGIN_USE_FLOAT 0
+#define FULL ROUTE_PLUGIN_RESOLUTION
+#define HALF ROUTE_PLUGIN_RESOLUTION / 2
+typedef int route_ttable_entry_t;
+
+int snd_pcm_plugin_build_io(snd_pcm_plug_t *handle,
+			    snd_pcm_hw_params_t *params,
+			    snd_pcm_plugin_t **r_plugin);
+int snd_pcm_plugin_build_linear(snd_pcm_plug_t *handle,
+				snd_pcm_plugin_format_t *src_format,
+				snd_pcm_plugin_format_t *dst_format,
+				snd_pcm_plugin_t **r_plugin);
+int snd_pcm_plugin_build_mulaw(snd_pcm_plug_t *handle,
+			       snd_pcm_plugin_format_t *src_format,
+			       snd_pcm_plugin_format_t *dst_format,
+			       snd_pcm_plugin_t **r_plugin);
+int snd_pcm_plugin_build_rate(snd_pcm_plug_t *handle,
+			      snd_pcm_plugin_format_t *src_format,
+			      snd_pcm_plugin_format_t *dst_format,
+			      snd_pcm_plugin_t **r_plugin);
+int snd_pcm_plugin_build_route(snd_pcm_plug_t *handle,
+			       snd_pcm_plugin_format_t *src_format,
+			       snd_pcm_plugin_format_t *dst_format,
+			       route_ttable_entry_t *ttable,
+		               snd_pcm_plugin_t **r_plugin);
+int snd_pcm_plugin_build_copy(snd_pcm_plug_t *handle,
+			      snd_pcm_plugin_format_t *src_format,
+			      snd_pcm_plugin_format_t *dst_format,
+			      snd_pcm_plugin_t **r_plugin);
+
+unsigned int snd_pcm_plug_formats(unsigned int formats);
+
+int snd_pcm_plug_format_plugins(snd_pcm_plug_t *substream,
+				snd_pcm_hw_params_t *params,
+				snd_pcm_hw_params_t *slave_params);
+
+int snd_pcm_plug_slave_format(int format, unsigned int format_mask);
+
+int snd_pcm_plugin_append(snd_pcm_plugin_t *plugin);
+
+snd_pcm_sframes_t snd_pcm_plug_write_transfer(snd_pcm_plug_t *handle, snd_pcm_plugin_channel_t *src_channels, snd_pcm_uframes_t size);
+snd_pcm_sframes_t snd_pcm_plug_read_transfer(snd_pcm_plug_t *handle, snd_pcm_plugin_channel_t *dst_channels_final, snd_pcm_uframes_t size);
+
+snd_pcm_sframes_t snd_pcm_plug_client_channels_buf(snd_pcm_plug_t *handle,
+					 char *buf, snd_pcm_uframes_t count,
+					 snd_pcm_plugin_channel_t **channels);
+
+snd_pcm_sframes_t snd_pcm_plugin_client_channels(snd_pcm_plugin_t *plugin,
+				       snd_pcm_uframes_t frames,
+				       snd_pcm_plugin_channel_t **channels);
+
+int snd_pcm_area_silence(const snd_pcm_channel_area_t *dst_channel, size_t dst_offset,
+			 size_t samples, int format);
+int snd_pcm_areas_silence(const snd_pcm_channel_area_t *dst_channels, snd_pcm_uframes_t dst_offset,
+			  unsigned int channels, snd_pcm_uframes_t frames, int format);
+int snd_pcm_area_copy(const snd_pcm_channel_area_t *src_channel, size_t src_offset,
+		      const snd_pcm_channel_area_t *dst_channel, size_t dst_offset,
+		      size_t samples, int format);
+int snd_pcm_areas_copy(const snd_pcm_channel_area_t *src_channels, snd_pcm_uframes_t src_offset,
+		       const snd_pcm_channel_area_t *dst_channels, snd_pcm_uframes_t dst_offset,
+		       unsigned int channels, snd_pcm_uframes_t frames, int format);
+
+void *snd_pcm_plug_buf_alloc(snd_pcm_plug_t *plug, snd_pcm_uframes_t size);
+void snd_pcm_plug_buf_unlock(snd_pcm_plug_t *plug, void *ptr);
+snd_pcm_sframes_t snd_pcm_oss_write3(snd_pcm_substream_t *substream, const char *ptr, snd_pcm_uframes_t size, int in_kernel);
+snd_pcm_sframes_t snd_pcm_oss_read3(snd_pcm_substream_t *substream, char *ptr, snd_pcm_uframes_t size, int in_kernel);
+snd_pcm_sframes_t snd_pcm_oss_writev3(snd_pcm_substream_t *substream, void **bufs, snd_pcm_uframes_t frames, int in_kernel);
+snd_pcm_sframes_t snd_pcm_oss_readv3(snd_pcm_substream_t *substream, void **bufs, snd_pcm_uframes_t frames, int in_kernel);
+
+
+
+#define ROUTE_PLUGIN_RESOLUTION 16
+
+int getput_index(int format);
+int copy_index(int format);
+int conv_index(int src_format, int dst_format);
+
+void zero_channel(snd_pcm_plugin_t *plugin,
+		  const snd_pcm_plugin_channel_t *dst_channel,
+		  size_t samples);
+
+#ifdef TARGET_OS2
+#define pdprintf snd_printk
+#else
+#ifdef PLUGIN_DEBUG
+#define pdprintf( args... ) printk( "plugin: " ##args)
+#else
+#define pdprintf( args... ) { ; }
+#endif
+#endif
+
+#endif				/* __PCM_PLUGIN_H */
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/pcm_sgbuf.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/pcm_sgbuf.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/pcm_sgbuf.h	(revision 91)
@@ -0,0 +1,115 @@
+#ifndef __SOUND_PCM_SGBUF_H
+#define __SOUND_PCM_SGBUF_H
+
+/*
+ * Scatter-Gather PCM access
+ *
+ *  Copyright (c) by 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
+ *
+ */
+/*
+ * buffer device info
+ */
+struct snd_dma_device {
+	int type;			/* SNDRV_MEM_TYPE_XXX */
+	union {
+		struct pci_dev *pci;	/* for PCI and PCI-SG types */
+		unsigned int flags;	/* GFP_XXX for continous and ISA types */
+#ifdef CONFIG_SBUS
+		struct sbus_dev *sbus;	/* for SBUS type */
+#endif
+	} dev;
+	unsigned int id;		/* a unique ID */
+};
+
+/*
+ * buffer types
+ */
+#define SNDRV_DMA_TYPE_UNKNOWN		0	/* not defined */
+#define SNDRV_DMA_TYPE_CONTINUOUS	1	/* continuous no-DMA memory */
+#define SNDRV_DMA_TYPE_ISA		2	/* ISA continuous */
+#define SNDRV_DMA_TYPE_PCI		3	/* PCI continuous */
+#define SNDRV_DMA_TYPE_SBUS		4	/* SBUS continuous */
+#define SNDRV_DMA_TYPE_PCI_SG		5	/* PCI SG-buffer */
+
+#ifdef CONFIG_PCI
+/*
+ * compose a snd_dma_device struct for the PCI device
+ */
+static inline void snd_dma_device_pci(struct snd_dma_device *dev, struct pci_dev *pci, unsigned int id)
+{
+	memset(dev, 0, sizeof(*dev));
+	dev->type = SNDRV_DMA_TYPE_PCI;
+	dev->dev.pci = pci;
+	dev->id = id;
+}
+#endif
+
+
+/*
+ * info for buffer allocation
+ */
+struct snd_dma_buffer {
+	unsigned char *area;	/* virtual pointer */
+	dma_addr_t addr;	/* physical address */
+	size_t bytes;		/* buffer size in bytes */
+	void *private_data;	/* private for allocator; don't touch */
+};
+
+struct snd_sg_page {
+	void *buf;
+	dma_addr_t addr;
+};
+
+struct snd_sg_buf {
+	int size;	/* allocated byte size (= runtime->dma_bytes) */
+	int pages;	/* allocated pages */
+	int tblsize;	/* allocated table size */
+	struct snd_sg_page *table;
+	struct pci_dev *pci;
+};
+
+typedef struct snd_sg_buf snd_pcm_sgbuf_t; /* for magic cast */
+
+/*
+ * return the pages matching with the given byte size
+ */
+static inline unsigned int snd_pcm_sgbuf_pages(size_t size)
+{
+	return (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
+}
+
+/*
+ * return the physical address at the corresponding offset
+ */
+static inline dma_addr_t snd_pcm_sgbuf_get_addr(struct snd_sg_buf *sgbuf, size_t offset)
+{
+	return sgbuf->table[offset >> PAGE_SHIFT].addr + offset % PAGE_SIZE;
+}
+
+int snd_pcm_sgbuf_init(snd_pcm_substream_t *substream, struct pci_dev *pci, int tblsize);
+int snd_pcm_sgbuf_delete(snd_pcm_substream_t *substream);
+int snd_pcm_sgbuf_alloc(snd_pcm_substream_t *substream, size_t size);
+int snd_pcm_sgbuf_free(snd_pcm_substream_t *substream);
+
+int snd_pcm_sgbuf_ops_copy_playback(snd_pcm_substream_t *substream, int channel, snd_pcm_uframes_t hwoff, void *buf, snd_pcm_uframes_t count);
+int snd_pcm_sgbuf_ops_copy_capture(snd_pcm_substream_t *substream, int channel, snd_pcm_uframes_t hwoff, void *buf, snd_pcm_uframes_t count);
+int snd_pcm_sgbuf_ops_silence(snd_pcm_substream_t *substream, int channel, snd_pcm_uframes_t hwoff, snd_pcm_uframes_t count);
+void *snd_pcm_sgbuf_ops_page(snd_pcm_substream_t *substream, unsigned long offset);
+
+
+#endif /* __SOUND_PCM_SGBUF_H */
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/pmac.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/pmac.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/pmac.h	(revision 91)
@@ -0,0 +1,203 @@
+/*
+ * Driver for PowerMac onboard soundchips
+ * Copyright (c) 2001 by Takashi Iwai <tiwai@suse.de>
+ *   based on dmasound.c.
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#ifndef __PMAC_H
+#define __PMAC_H
+
+#include "control.h"
+#include "pcm.h"
+#include "awacs.h"
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
+#include <asm/adb.h>
+#include <asm/cuda.h>
+#include <asm/pmu.h>
+#else /* 2.4.0 kernel */
+#include <linux/adb.h>
+#ifdef CONFIG_ADB_CUDA
+#include <linux/cuda.h>
+#endif
+#ifdef CONFIG_ADB_PMU
+#include <linux/pmu.h>
+#endif
+#endif
+#include <linux/nvram.h>
+#include <linux/vt_kern.h>
+#include <asm/dbdma.h>
+#include <asm/prom.h>
+#include <asm/machdep.h>
+#include <asm/feature.h>
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
+#define pmu_suspend()	/**/
+#define pmu_resume()	/**/
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) || defined(CONFIG_ADB_CUDA)
+#define PMAC_AMP_AVAIL
+#endif
+
+/* maximum number of fragments */
+#define PMAC_MAX_FRAGS		32
+
+
+/*
+ * typedefs
+ */
+typedef struct snd_pmac pmac_t;
+typedef struct snd_pmac_stream pmac_stream_t;
+typedef struct snd_pmac_beep pmac_beep_t;
+typedef struct snd_pmac_dbdma pmac_dbdma_t;
+
+
+/*
+ * DBDMA space
+ */
+struct snd_pmac_dbdma {
+	unsigned long addr;
+	struct dbdma_cmd *cmds;
+	void *space;
+	int size;
+};
+
+/*
+ * playback/capture stream
+ */
+struct snd_pmac_stream {
+	int running;	/* boolean */
+
+	int stream;	/* PLAYBACK/CAPTURE */
+
+	int dma_size; /* in bytes */
+	int period_size; /* in bytes */
+	int buffer_size; /* in kbytes */
+	int nperiods, cur_period;
+
+	pmac_dbdma_t cmd;
+	volatile struct dbdma_regs *dma;
+
+	snd_pcm_substream_t *substream;
+
+	unsigned int cur_freqs;		/* currently available frequences */
+	unsigned int cur_formats;	/* currently available formats */
+};
+
+
+/*
+ * beep using pcm
+ */
+struct snd_pmac_beep {
+	int running;	/* boolean */
+	int volume;	/* mixer volume: 0-100 */
+	int volume_play;	/* currently playing volume */
+	int hz;
+	int nsamples;
+	short *buf;		/* allocated wave buffer */
+	unsigned long addr;	/* physical address of buffer */
+	struct timer_list timer;	/* timer list for stopping beep */
+	void (*orig_mksound)(unsigned int, unsigned int);
+				/* pointer to restore */
+	snd_kcontrol_t *control;	/* mixer element */
+};
+
+
+/*
+ */
+struct snd_pmac {
+	snd_card_t *card;
+
+	/* h/w info */
+	struct device_node *node;
+	unsigned int revision;
+	unsigned int subframe;
+	unsigned int device_id;
+	unsigned int has_iic : 1;
+
+	unsigned int is_burgundy : 1;
+	unsigned int is_daca : 1;
+	unsigned int is_pbook_3400 : 1;
+	unsigned int is_pbook_G3 : 1;
+	unsigned int is_screamer : 1;
+
+	unsigned int can_byte_swap : 1;
+	unsigned int can_duplex : 1;
+	unsigned int can_capture : 1;
+
+#ifdef PMAC_AMP_AVAIL
+	unsigned int amp_only;
+	int amp_vol[2];
+#endif
+
+	unsigned int initialized : 1;
+	unsigned int feature_is_set : 1;
+
+	unsigned int freqs_ok;		/* bit flags */
+	unsigned int formats_ok;	/* pcm hwinfo */
+	int active;
+	int rate_index;
+	int format;			/* current format */
+
+	spinlock_t reg_lock;
+	volatile struct awacs_regs *awacs;
+	int awacs_reg[8]; /* register cache */
+
+	unsigned char *latch_base;
+	unsigned char *macio_base;
+
+	pmac_stream_t playback;
+	pmac_stream_t capture;
+
+	pmac_dbdma_t extra_dma;
+
+	int irq, tx_irq, rx_irq;
+
+	snd_pcm_t *pcm;
+
+	pmac_beep_t *beep;
+
+	unsigned int control_mask;	/* control mask */
+
+	/* lowlevel callbacks */
+	void (*set_format)(pmac_t *chip);
+	void (*port_change)(pmac_t *chip);
+#ifdef CONFIG_PMAC_PBOOK
+	unsigned int sleep_registered : 1;
+	void (*suspend)(pmac_t *chip);
+	void (*resume)(pmac_t *chip);
+#endif
+
+};
+
+
+/* exported functions */
+int snd_pmac_new(snd_card_t *card, pmac_t **chip_return);
+int snd_pmac_pcm_new(pmac_t *chip);
+int snd_pmac_attach_beep(pmac_t *chip);
+#ifdef CONFIG_PMAC_PBOOK
+int snd_pmac_register_sleep_notifier(pmac_t *chip);
+int snd_pmac_unregister_sleep_notifier(pmac_t *chip);
+#endif
+
+/* initialize mixer */
+int snd_pmac_awacs_init(pmac_t *chip);
+int snd_pmac_burgundy_init(pmac_t *chip);
+
+#endif /* __PMAC_H */
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/rawmidi.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/rawmidi.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/rawmidi.h	(revision 91)
@@ -0,0 +1,183 @@
+#ifndef __SOUND_RAWMIDI_H
+#define __SOUND_RAWMIDI_H
+
+/*
+ *  Abstract layer for MIDI v1.0 stream
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <sound/asound.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/wait.h>
+#include <asm/semaphore.h>
+
+#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
+#include "seq_device.h"
+#endif
+
+/*
+ *  Raw MIDI interface
+ */
+
+#define SNDRV_RAWMIDI_DEVICES		8
+
+#define SNDRV_RAWMIDI_LFLG_OUTPUT	(1<<0)
+#define SNDRV_RAWMIDI_LFLG_INPUT	(1<<1)
+#define SNDRV_RAWMIDI_LFLG_OPEN		(3<<0)
+#define SNDRV_RAWMIDI_LFLG_APPEND	(1<<2)
+#define	SNDRV_RAWMIDI_LFLG_NOOPENLOCK	(1<<3)
+
+struct snd_rawmidi;
+struct snd_rawmidi_substream;
+
+struct snd_rawmidi_ops {
+	int (*open) (struct snd_rawmidi_substream * substream);
+	int (*close) (struct snd_rawmidi_substream * substream);
+	void (*trigger) (struct snd_rawmidi_substream * substream, int up);
+	void (*drain) (struct snd_rawmidi_substream * substream);
+};
+
+struct snd_rawmidi_global_ops {
+	int (*dev_register) (struct snd_rawmidi * rmidi);
+	int (*dev_unregister) (struct snd_rawmidi * rmidi);
+};
+
+struct snd_rawmidi_runtime {
+	unsigned int drain: 1,	/* drain stage */
+		     oss: 1;	/* OSS compatible mode */
+	/* midi stream buffer */
+	unsigned char *buffer;	/* buffer for MIDI data */
+	size_t buffer_size;	/* size of buffer */
+	size_t appl_ptr;	/* application pointer */
+	size_t hw_ptr;		/* hardware pointer */
+	size_t avail_min;	/* min avail for wakeup */
+	size_t avail;		/* max used buffer for wakeup */
+	size_t xruns;		/* over/underruns counter */
+	/* misc */
+	spinlock_t lock;
+	wait_queue_head_t sleep;
+	/* event handler (new bytes, input only) */
+	void (*event)(struct snd_rawmidi_substream *substream);
+	/* defers calls to event [input] or ops->trigger [output] */
+	struct tasklet_struct tasklet;
+	/* private data */
+	void *private_data;
+	void (*private_free)(struct snd_rawmidi_substream *substream);
+};
+
+struct snd_rawmidi_substream {
+	struct list_head list;		/* list of all substream for given stream */
+	int stream;			/* direction */
+	int number;			/* substream number */
+	unsigned int opened: 1,		/* open flag */
+		     append: 1,		/* append flag (merge more streams) */
+		     active_sensing: 1; /* send active sensing when close */
+	int use_count;			/* use counter (for output) */
+	size_t bytes;
+	struct snd_rawmidi *rmidi;
+	struct snd_rawmidi_str *pstr;
+	char name[32];
+	struct snd_rawmidi_runtime *runtime;
+	/* hardware layer */
+	struct snd_rawmidi_ops *ops;
+};
+
+struct snd_rawmidi_file {
+	struct snd_rawmidi *rmidi;
+	struct snd_rawmidi_substream *input;
+	struct snd_rawmidi_substream *output;
+};
+
+struct snd_rawmidi_str {
+	unsigned int substream_count;
+	unsigned int substream_opened;
+	struct list_head substreams;
+};
+
+struct snd_rawmidi {
+	struct snd_card *card;
+
+	unsigned int device;		/* device number */
+	unsigned int info_flags;	/* SNDRV_RAWMIDI_INFO_XXXX */
+	char id[64];
+	char name[80];
+
+#ifdef CONFIG_SND_OSSEMUL
+	int ossreg;
+#endif
+
+	struct snd_rawmidi_global_ops *ops;
+
+	struct snd_rawmidi_str streams[2];
+
+	void *private_data;
+	void (*private_free) (struct snd_rawmidi *rmidi);
+
+	struct semaphore open_mutex;
+	wait_queue_head_t open_wait;
+
+	struct snd_info_entry *dev;
+	struct snd_info_entry *proc_entry;
+
+#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
+	struct snd_seq_device *seq_dev;
+#endif
+};
+
+/* main rawmidi functions */
+
+int snd_rawmidi_new(struct snd_card *card, char *id, int device,
+		    int output_count, int input_count,
+		    struct snd_rawmidi **rmidi);
+void snd_rawmidi_set_ops(struct snd_rawmidi *rmidi, int stream,
+			 struct snd_rawmidi_ops *ops);
+
+/* callbacks */
+
+void snd_rawmidi_receive_reset(struct snd_rawmidi_substream *substream);
+int snd_rawmidi_receive(struct snd_rawmidi_substream *substream,
+			const unsigned char *buffer, int count);
+void snd_rawmidi_transmit_reset(struct snd_rawmidi_substream *substream);
+int snd_rawmidi_transmit_empty(struct snd_rawmidi_substream *substream);
+int snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream,
+			      unsigned char *buffer, int count);
+int snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, int count);
+int snd_rawmidi_transmit(struct snd_rawmidi_substream *substream,
+			 unsigned char *buffer, int count);
+
+/* main midi functions */
+
+int snd_rawmidi_info_select(struct snd_card *card, struct snd_rawmidi_info *info);
+int snd_rawmidi_kernel_open(int cardnum, int device, int subdevice, int mode,
+			    struct snd_rawmidi_file *rfile);
+int snd_rawmidi_kernel_release(struct snd_rawmidi_file *rfile);
+int snd_rawmidi_output_params(struct snd_rawmidi_substream *substream,
+			      struct snd_rawmidi_params *params);
+int snd_rawmidi_input_params(struct snd_rawmidi_substream *substream,
+			     struct snd_rawmidi_params *params);
+int snd_rawmidi_drop_output(struct snd_rawmidi_substream *substream);
+int snd_rawmidi_drain_output(struct snd_rawmidi_substream *substream);
+int snd_rawmidi_drain_input(struct snd_rawmidi_substream *substream);
+long snd_rawmidi_kernel_read(struct snd_rawmidi_substream *substream,
+			     unsigned char *buf, long count);
+long snd_rawmidi_kernel_write(struct snd_rawmidi_substream *substream,
+			      const unsigned char *buf, long count);
+
+#endif /* __SOUND_RAWMIDI_H */
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/sb.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/sb.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/sb.h	(revision 91)
@@ -0,0 +1,366 @@
+#ifndef __SOUND_SB_H
+#define __SOUND_SB_H
+
+/*
+ *  Header file for SoundBlaster cards
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include "pcm.h"
+#include "rawmidi.h"
+#include <linux/interrupt.h>
+#include <asm/io.h>
+
+enum sb_hw_type {
+	SB_HW_AUTO,
+	SB_HW_10,
+	SB_HW_20,
+	SB_HW_201,
+	SB_HW_PRO,
+	SB_HW_16,
+	SB_HW_16CSP,		/* SB16 with CSP chip */
+	SB_HW_ALS100,		/* Avance Logic ALS100 chip */
+	SB_HW_ALS4000,		/* Avance Logic ALS4000 chip */
+	SB_HW_DT019X,		/* Diamond Tech. DT-019X / Avance Logic ALS-007 */
+};
+
+#define SB_OPEN_PCM			0x01
+#define SB_OPEN_MIDI_INPUT		0x02
+#define SB_OPEN_MIDI_OUTPUT		0x04
+#define SB_OPEN_MIDI_INPUT_TRIGGER	0x08
+#define SB_OPEN_MIDI_OUTPUT_TRIGGER	0x10
+
+#define SB_MODE_HALT		0x00
+#define SB_MODE_PLAYBACK_8	0x01
+#define SB_MODE_PLAYBACK_16	0x02
+#define SB_MODE_PLAYBACK	(SB_MODE_PLAYBACK_8 | SB_MODE_PLAYBACK_16)
+#define SB_MODE_CAPTURE_8	0x04
+#define SB_MODE_CAPTURE_16	0x08
+#define SB_MODE_CAPTURE		(SB_MODE_CAPTURE_8 | SB_MODE_CAPTURE_16)
+
+#define SB_RATE_LOCK_PLAYBACK	0x10
+#define SB_RATE_LOCK_CAPTURE	0x20
+#define SB_RATE_LOCK		(SB_RATE_LOCK_PLAYBACK | SB_RATE_LOCK_CAPTURE)
+
+#define SB_MPU_INPUT		1
+
+struct snd_sb {
+	unsigned long port;		/* base port of DSP chip */
+	struct resource *res_port;
+	unsigned long mpu_port;		/* MPU port for SB DSP 4.0+ */
+	int irq;			/* IRQ number of DSP chip */
+	int dma8;			/* 8-bit DMA */
+	int dma16;			/* 16-bit DMA */
+	unsigned short version;		/* version of DSP chip */
+	enum sb_hw_type hardware;	/* see to SB_HW_XXXX */
+
+	unsigned long alt_port;		/* alternate port (ALS4000) */
+	struct pci_dev *pci;		/* ALS4000 */
+
+	unsigned int open;		/* see to SB_OPEN_XXXX for sb8 */
+					/* also SNDRV_SB_CSP_MODE_XXX for sb16_csp */
+	unsigned int mode;		/* current mode of stream */
+	unsigned int force_mode16;	/* force 16-bit mode of streams */
+	unsigned int locked_rate;	/* sb16 duplex */
+	unsigned int playback_format;
+	unsigned int capture_format;
+	struct timer_list midi_timer;
+	unsigned int p_dma_size;
+	unsigned int p_period_size;
+	unsigned int c_dma_size;
+	unsigned int c_period_size;
+
+	spinlock_t mixer_lock;
+
+	char name[32];
+
+	void *csp; /* used only when CONFIG_SND_SB16_CSP is set */
+
+	struct snd_card *card;
+	struct snd_pcm *pcm;
+	struct snd_pcm_substream *playback_substream;
+	struct snd_pcm_substream *capture_substream;
+
+	struct snd_rawmidi *rmidi;
+	struct snd_rawmidi_substream *midi_substream_input;
+	struct snd_rawmidi_substream *midi_substream_output;
+	irqreturn_t (*rmidi_callback)(int irq, void *dev_id, struct pt_regs *regs);
+
+	spinlock_t reg_lock;
+	spinlock_t open_lock;
+	spinlock_t midi_input_lock;
+
+	struct snd_info_entry *proc_entry;
+
+#ifdef CONFIG_PM
+	unsigned char saved_regs[0x20];
+#endif
+};
+
+/* I/O ports */
+
+#define SBP(chip, x)		((chip)->port + s_b_SB_##x)
+#define SBP1(port, x)		((port) + s_b_SB_##x)
+
+#define s_b_SB_RESET		0x6
+#define s_b_SB_READ		0xa
+#define s_b_SB_WRITE		0xc
+#define s_b_SB_COMMAND		0xc
+#define s_b_SB_STATUS		0xc
+#define s_b_SB_DATA_AVAIL	0xe
+#define s_b_SB_DATA_AVAIL_16 	0xf
+#define s_b_SB_MIXER_ADDR	0x4
+#define s_b_SB_MIXER_DATA	0x5
+#define s_b_SB_OPL3_LEFT	0x0
+#define s_b_SB_OPL3_RIGHT	0x2
+#define s_b_SB_OPL3_BOTH	0x8
+
+#define SB_DSP_OUTPUT		0x14
+#define SB_DSP_INPUT		0x24
+#define SB_DSP_BLOCK_SIZE	0x48
+#define SB_DSP_HI_OUTPUT	0x91
+#define SB_DSP_HI_INPUT		0x99
+#define SB_DSP_LO_OUTPUT_AUTO	0x1c
+#define SB_DSP_LO_INPUT_AUTO	0x2c
+#define SB_DSP_HI_OUTPUT_AUTO	0x90
+#define SB_DSP_HI_INPUT_AUTO	0x98
+#define SB_DSP_IMMED_INT	0xf2
+#define SB_DSP_GET_VERSION	0xe1
+#define SB_DSP_SPEAKER_ON	0xd1
+#define SB_DSP_SPEAKER_OFF	0xd3
+#define SB_DSP_DMA8_OFF		0xd0
+#define SB_DSP_DMA8_ON		0xd4
+#define SB_DSP_DMA8_EXIT	0xda
+#define SB_DSP_DMA16_OFF	0xd5
+#define SB_DSP_DMA16_ON		0xd6
+#define SB_DSP_DMA16_EXIT	0xd9
+#define SB_DSP_SAMPLE_RATE	0x40
+#define SB_DSP_SAMPLE_RATE_OUT	0x41
+#define SB_DSP_SAMPLE_RATE_IN	0x42
+#define SB_DSP_MONO_8BIT	0xa0
+#define SB_DSP_MONO_16BIT	0xa4
+#define SB_DSP_STEREO_8BIT	0xa8
+#define SB_DSP_STEREO_16BIT	0xac
+
+#define SB_DSP_MIDI_INPUT_IRQ	0x31
+#define SB_DSP_MIDI_UART_IRQ	0x35
+#define SB_DSP_MIDI_OUTPUT	0x38
+
+#define SB_DSP4_OUT8_AI		0xc6
+#define SB_DSP4_IN8_AI		0xce
+#define SB_DSP4_OUT16_AI	0xb6
+#define SB_DSP4_IN16_AI		0xbe
+#define SB_DSP4_MODE_UNS_MONO	0x00
+#define SB_DSP4_MODE_SIGN_MONO	0x10
+#define SB_DSP4_MODE_UNS_STEREO	0x20
+#define SB_DSP4_MODE_SIGN_STEREO 0x30
+
+#define SB_DSP4_OUTPUT		0x3c
+#define SB_DSP4_INPUT_LEFT	0x3d
+#define SB_DSP4_INPUT_RIGHT	0x3e
+
+/* registers for SB 2.0 mixer */
+#define SB_DSP20_MASTER_DEV	0x02
+#define SB_DSP20_PCM_DEV	0x0A
+#define SB_DSP20_CD_DEV		0x08
+#define SB_DSP20_FM_DEV		0x06
+
+/* registers for SB PRO mixer */
+#define SB_DSP_MASTER_DEV	0x22
+#define SB_DSP_PCM_DEV		0x04
+#define SB_DSP_LINE_DEV		0x2e
+#define SB_DSP_CD_DEV		0x28
+#define SB_DSP_FM_DEV		0x26
+#define SB_DSP_MIC_DEV		0x0a
+#define SB_DSP_CAPTURE_SOURCE	0x0c
+#define SB_DSP_CAPTURE_FILT	0x0c
+#define SB_DSP_PLAYBACK_FILT	0x0e
+#define SB_DSP_STEREO_SW	0x0e
+
+#define SB_DSP_MIXS_MIC0	0x00	/* same as MIC */
+#define SB_DSP_MIXS_CD		0x01
+#define SB_DSP_MIXS_MIC		0x02
+#define SB_DSP_MIXS_LINE	0x03
+
+/* registers (only for left channel) for SB 16 mixer */
+#define SB_DSP4_MASTER_DEV	0x30
+#define SB_DSP4_BASS_DEV	0x46
+#define SB_DSP4_TREBLE_DEV	0x44
+#define SB_DSP4_SYNTH_DEV	0x34
+#define SB_DSP4_PCM_DEV		0x32
+#define SB_DSP4_SPEAKER_DEV	0x3b
+#define SB_DSP4_LINE_DEV	0x38
+#define SB_DSP4_MIC_DEV		0x3a
+#define SB_DSP4_OUTPUT_SW	0x3c
+#define SB_DSP4_CD_DEV		0x36
+#define SB_DSP4_IGAIN_DEV	0x3f
+#define SB_DSP4_OGAIN_DEV	0x41
+#define SB_DSP4_MIC_AGC		0x43
+
+/* additional registers for SB 16 mixer */
+#define SB_DSP4_IRQSETUP	0x80
+#define SB_DSP4_DMASETUP	0x81
+#define SB_DSP4_IRQSTATUS	0x82
+#define SB_DSP4_MPUSETUP	0x84
+
+#define SB_DSP4_3DSE		0x90
+
+/* Registers for DT-019x / ALS-007 mixer */
+#define SB_DT019X_MASTER_DEV	0x62
+#define SB_DT019X_PCM_DEV	0x64
+#define SB_DT019X_SYNTH_DEV	0x66
+#define SB_DT019X_CD_DEV	0x68
+#define SB_DT019X_MIC_DEV	0x6a
+#define SB_DT019X_SPKR_DEV	0x6a
+#define SB_DT019X_LINE_DEV	0x6e
+#define SB_DT019X_OUTPUT_SW2	0x4c
+#define SB_DT019X_CAPTURE_SW	0x6c
+
+#define SB_DT019X_CAP_CD	0x02
+#define SB_DT019X_CAP_MIC	0x04
+#define SB_DT019X_CAP_LINE	0x06
+#define SB_DT019X_CAP_SYNTH	0x07
+#define SB_DT019X_CAP_MAIN	0x07
+
+#define SB_ALS4000_MONO_IO_CTRL	0x4b
+#define SB_ALS4000_MIC_IN_GAIN	0x4d
+#define SB_ALS4000_FMDAC	0x4f
+#define SB_ALS4000_3D_SND_FX	0x50
+#define SB_ALS4000_3D_TIME_DELAY	0x51
+#define SB_ALS4000_3D_AUTO_MUTE	0x52
+#define SB_ALS4000_QSOUND	0xdb
+
+/* IRQ setting bitmap */
+#define SB_IRQSETUP_IRQ9	0x01
+#define SB_IRQSETUP_IRQ5	0x02
+#define SB_IRQSETUP_IRQ7	0x04
+#define SB_IRQSETUP_IRQ10	0x08
+
+/* IRQ types */
+#define SB_IRQTYPE_8BIT		0x01
+#define SB_IRQTYPE_16BIT	0x02
+#define SB_IRQTYPE_MPUIN	0x04
+
+/* DMA setting bitmap */
+#define SB_DMASETUP_DMA0	0x01
+#define SB_DMASETUP_DMA1	0x02
+#define SB_DMASETUP_DMA3	0x08
+#define SB_DMASETUP_DMA5	0x20
+#define SB_DMASETUP_DMA6	0x40
+#define SB_DMASETUP_DMA7	0x80
+
+/*
+ *
+ */
+
+static inline void snd_sb_ack_8bit(struct snd_sb *chip)
+{
+	inb(SBP(chip, DATA_AVAIL));
+}
+
+static inline void snd_sb_ack_16bit(struct snd_sb *chip)
+{
+	inb(SBP(chip, DATA_AVAIL_16));
+}
+
+/* sb_common.c */
+int snd_sbdsp_command(struct snd_sb *chip, unsigned char val);
+int snd_sbdsp_get_byte(struct snd_sb *chip);
+int snd_sbdsp_reset(struct snd_sb *chip);
+int snd_sbdsp_create(struct snd_card *card,
+		     unsigned long port,
+		     int irq,
+		     irqreturn_t (*irq_handler)(int, void *, struct pt_regs *),
+		     int dma8, int dma16,
+		     unsigned short hardware,
+		     struct snd_sb **r_chip);
+/* sb_mixer.c */
+void snd_sbmixer_write(struct snd_sb *chip, unsigned char reg, unsigned char data);
+unsigned char snd_sbmixer_read(struct snd_sb *chip, unsigned char reg);
+int snd_sbmixer_new(struct snd_sb *chip);
+#ifdef CONFIG_PM
+void snd_sbmixer_suspend(struct snd_sb *chip);
+void snd_sbmixer_resume(struct snd_sb *chip);
+#endif
+
+/* sb8_init.c */
+int snd_sb8dsp_pcm(struct snd_sb *chip, int device, struct snd_pcm ** rpcm);
+/* sb8.c */
+irqreturn_t snd_sb8dsp_interrupt(struct snd_sb *chip);
+int snd_sb8_playback_open(struct snd_pcm_substream *substream);
+int snd_sb8_capture_open(struct snd_pcm_substream *substream);
+int snd_sb8_playback_close(struct snd_pcm_substream *substream);
+int snd_sb8_capture_close(struct snd_pcm_substream *substream);
+/* midi8.c */
+irqreturn_t snd_sb8dsp_midi_interrupt(struct snd_sb *chip);
+int snd_sb8dsp_midi(struct snd_sb *chip, int device, struct snd_rawmidi ** rrawmidi);
+
+/* sb16_init.c */
+int snd_sb16dsp_pcm(struct snd_sb *chip, int device, struct snd_pcm ** rpcm);
+const struct snd_pcm_ops *snd_sb16dsp_get_pcm_ops(int direction);
+int snd_sb16dsp_configure(struct snd_sb *chip);
+/* sb16.c */
+irqreturn_t snd_sb16dsp_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+
+/* exported mixer stuffs */
+enum {
+	SB_MIX_SINGLE,
+	SB_MIX_DOUBLE,
+	SB_MIX_INPUT_SW,
+	SB_MIX_CAPTURE_PRO,
+	SB_MIX_CAPTURE_DT019X
+};
+
+#define SB_MIXVAL_DOUBLE(left_reg, right_reg, left_shift, right_shift, mask) \
+  ((left_reg) | ((right_reg) << 8) | ((left_shift) << 16) | ((right_shift) << 19) | ((mask) << 24))
+#define SB_MIXVAL_SINGLE(reg, shift, mask) \
+  ((reg) | ((shift) << 16) | ((mask) << 24))
+#define SB_MIXVAL_INPUT_SW(reg1, reg2, left_shift, right_shift) \
+  ((reg1) | ((reg2) << 8) | ((left_shift) << 16) | ((right_shift) << 24))
+
+int snd_sbmixer_add_ctl(struct snd_sb *chip, const char *name, int index, int type, unsigned long value);
+
+/* for ease of use */
+struct sbmix_elem {
+	const char *name;
+	int type;
+	unsigned long private_value;
+};
+
+#define SB_SINGLE(xname, reg, shift, mask) \
+{ .name = xname, \
+  .type = SB_MIX_SINGLE, \
+  .private_value = SB_MIXVAL_SINGLE(reg, shift, mask) }
+
+#define SB_DOUBLE(xname, left_reg, right_reg, left_shift, right_shift, mask) \
+{ .name = xname, \
+  .type = SB_MIX_DOUBLE, \
+  .private_value = SB_MIXVAL_DOUBLE(left_reg, right_reg, left_shift, right_shift, mask) }
+
+#define SB16_INPUT_SW(xname, reg1, reg2, left_shift, right_shift) \
+{ .name = xname, \
+  .type = SB_MIX_INPUT_SW, \
+  .private_value = SB_MIXVAL_INPUT_SW(reg1, reg2, left_shift, right_shift) }
+
+static inline int snd_sbmixer_add_ctl_elem(struct snd_sb *chip, const struct sbmix_elem *c)
+{
+	return snd_sbmixer_add_ctl(chip, c->name, 0, c->type, c->private_value);
+}
+
+#endif /* __SOUND_SB_H */
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/sb16_csp.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/sb16_csp.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/sb16_csp.h	(revision 91)
@@ -0,0 +1,168 @@
+#ifndef __SB16_CSP_H
+#define __SB16_CSP_H
+
+/*
+ *  Copyright (c) 1999 by Uros Bizjak <uros@kss-loka.si>
+ *                        Takashi Iwai <tiwai@suse.de>
+ *
+ *  SB16ASP/AWE32 CSP control
+ *
+ *   This program is free software; you can redistribute it and/or modify 
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/* CSP modes */
+#define SNDRV_SB_CSP_MODE_NONE		0x00
+#define SNDRV_SB_CSP_MODE_DSP_READ	0x01	/* Record from DSP */
+#define SNDRV_SB_CSP_MODE_DSP_WRITE	0x02	/* Play to DSP */
+#define SNDRV_SB_CSP_MODE_QSOUND		0x04	/* QSound */
+
+/* CSP load flags */
+#define SNDRV_SB_CSP_LOAD_FROMUSER	0x01
+#define SNDRV_SB_CSP_LOAD_INITBLOCK	0x02
+
+/* CSP sample width */
+#define SNDRV_SB_CSP_SAMPLE_8BIT		0x01
+#define SNDRV_SB_CSP_SAMPLE_16BIT		0x02
+
+/* CSP channels */
+#define SNDRV_SB_CSP_MONO			0x01
+#define SNDRV_SB_CSP_STEREO		0x02
+
+/* CSP rates */
+#define SNDRV_SB_CSP_RATE_8000		0x01
+#define SNDRV_SB_CSP_RATE_11025		0x02
+#define SNDRV_SB_CSP_RATE_22050		0x04
+#define SNDRV_SB_CSP_RATE_44100		0x08
+#define SNDRV_SB_CSP_RATE_ALL		0x0f
+
+/* CSP running state */
+#define SNDRV_SB_CSP_ST_IDLE		0x00
+#define SNDRV_SB_CSP_ST_LOADED		0x01
+#define SNDRV_SB_CSP_ST_RUNNING		0x02
+#define SNDRV_SB_CSP_ST_PAUSED		0x04
+#define SNDRV_SB_CSP_ST_AUTO		0x08
+#define SNDRV_SB_CSP_ST_QSOUND		0x10
+
+/* maximum QSound value (180 degrees right) */
+#define SNDRV_SB_CSP_QSOUND_MAX_RIGHT	0x20
+
+/* maximum microcode RIFF file size */
+#define SNDRV_SB_CSP_MAX_MICROCODE_FILE_SIZE	0x3000
+
+/* microcode header */
+typedef struct snd_sb_csp_mc_header {
+	char codec_name[16];		/* id name of codec */
+	unsigned short func_req;	/* requested function */
+} snd_sb_csp_mc_header_t;
+
+/* microcode to be loaded */
+typedef struct snd_sb_csp_microcode {
+	snd_sb_csp_mc_header_t info;
+	unsigned char data[SNDRV_SB_CSP_MAX_MICROCODE_FILE_SIZE];
+} snd_sb_csp_microcode_t;
+
+/* start CSP with sample_width in mono/stereo */
+typedef struct snd_sb_csp_start {
+	int sample_width;	/* sample width, look above */
+	int channels;		/* channels, look above */
+} snd_sb_csp_start_t;
+
+/* CSP information */
+typedef struct snd_sb_csp_info {
+	char codec_name[16];		/* id name of codec */
+	unsigned short func_nr;		/* function number */
+	unsigned int acc_format;	/* accepted PCM formats */
+	unsigned short acc_channels;	/* accepted channels */
+	unsigned short acc_width;	/* accepted sample width */
+	unsigned short acc_rates;	/* accepted sample rates */
+	unsigned short csp_mode;	/* CSP mode, see above */
+	unsigned short run_channels;	/* current channels  */
+	unsigned short run_width;	/* current sample width */
+	unsigned short version;		/* version id: 0x10 - 0x1f */
+	unsigned short state;		/* state bits */
+} snd_sb_csp_info_t;
+
+/* HWDEP controls */
+/* get CSP information */
+#define SNDRV_SB_CSP_IOCTL_INFO		_IOR('H', 0x10, snd_sb_csp_info_t)
+/* load microcode to CSP */
+#define SNDRV_SB_CSP_IOCTL_LOAD_CODE	_IOW('H', 0x11, snd_sb_csp_microcode_t)
+/* unload microcode from CSP */
+#define SNDRV_SB_CSP_IOCTL_UNLOAD_CODE	_IO('H', 0x12)
+/* start CSP */
+#define SNDRV_SB_CSP_IOCTL_START		_IOW('H', 0x13, snd_sb_csp_start_t)
+/* stop CSP */
+#define SNDRV_SB_CSP_IOCTL_STOP		_IO('H', 0x14)
+/* pause CSP and DMA transfer */
+#define SNDRV_SB_CSP_IOCTL_PAUSE		_IO('H', 0x15)
+/* restart CSP and DMA transfer */
+#define SNDRV_SB_CSP_IOCTL_RESTART	_IO('H', 0x16)
+
+#ifdef __KERNEL__
+#include "sb.h"
+#include "hwdep.h"
+
+typedef struct snd_sb_csp snd_sb_csp_t;
+
+/*
+ * CSP operators
+ */
+typedef struct {
+	int (*csp_use) (snd_sb_csp_t * p);
+	int (*csp_unuse) (snd_sb_csp_t * p);
+	int (*csp_autoload) (snd_sb_csp_t * p, int pcm_sfmt, int play_rec_mode);
+	int (*csp_start) (snd_sb_csp_t * p, int sample_width, int channels);
+	int (*csp_stop) (snd_sb_csp_t * p);
+	int (*csp_qsound_transfer) (snd_sb_csp_t * p);
+} snd_sb_csp_ops_t;
+
+/*
+ * CSP private data
+ */
+struct snd_sb_csp {
+	sb_t *chip;		/* SB16 DSP */
+	int used;		/* usage flag - exclusive */
+	char codec_name[16];	/* name of codec */
+	unsigned short func_nr;	/* function number */
+	unsigned int acc_format;	/* accepted PCM formats */
+	int acc_channels;	/* accepted channels */
+	int acc_width;		/* accepted sample width */
+	int acc_rates;		/* accepted sample rates */
+	int mode;		/* MODE */
+	int run_channels;	/* current CSP channels */
+	int run_width;		/* current sample width */
+	int version;		/* CSP version (0x10 - 0x1f) */
+	int running;		/* running state */
+
+	snd_sb_csp_ops_t ops;	/* operators */
+
+	spinlock_t q_lock;	/* locking */
+	int q_enabled;		/* enabled flag */
+	int qpos_left;		/* left position */
+	int qpos_right;		/* right position */
+	int qpos_changed;	/* position changed flag */
+
+	snd_kcontrol_t *qsound_switch;
+	snd_kcontrol_t *qsound_space;
+
+	struct semaphore access_mutex;	/* locking */
+	snd_info_entry_t *proc;	/* proc interface */
+};
+
+int snd_sb_csp_new(sb_t *chip, int device, snd_hwdep_t ** rhwdep);
+#endif
+
+#endif
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/seq_device.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/seq_device.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/seq_device.h	(revision 91)
@@ -0,0 +1,84 @@
+#ifndef __SOUND_SEQ_DEVICE_H
+#define __SOUND_SEQ_DEVICE_H
+
+/*
+ *  ALSA sequencer device management
+ *  Copyright (c) 1999 by 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
+ *
+ */
+
+/*
+ * registered device information
+ */
+
+#define ID_LEN	32
+
+/* status flag */
+#define SNDRV_SEQ_DEVICE_FREE		0
+#define SNDRV_SEQ_DEVICE_REGISTERED	1
+
+struct snd_seq_device {
+	/* device info */
+	struct snd_card *card;	/* sound card */
+	int device;		/* device number */
+	char id[ID_LEN];	/* driver id */
+	char name[80];		/* device name */
+	int argsize;		/* size of the argument */
+	void *driver_data;	/* private data for driver */
+	int status;		/* flag - read only */
+	void *private_data;	/* private data for the caller */
+	void (*private_free)(struct snd_seq_device *device);
+	struct list_head list;	/* link to next device */
+};
+
+
+/* driver operators
+ * init_device:
+ *	Initialize the device with given parameters.
+ *	Typically,
+ *		1. call snd_hwdep_new
+ *		2. allocate private data and initialize it
+ *		3. call snd_hwdep_register
+ *		4. store the instance to dev->driver_data pointer.
+ *		
+ * free_device:
+ *	Release the private data.
+ *	Typically, call snd_device_free(dev->card, dev->driver_data)
+ */
+struct snd_seq_dev_ops {
+	int (*init_device)(struct snd_seq_device *dev);
+	int (*free_device)(struct snd_seq_device *dev);
+};
+
+/*
+ * prototypes
+ */
+void snd_seq_device_load_drivers(void);
+int snd_seq_device_new(struct snd_card *card, int device, char *id, int argsize, struct snd_seq_device **result);
+int snd_seq_device_register_driver(char *id, struct snd_seq_dev_ops *entry, int argsize);
+int snd_seq_device_unregister_driver(char *id);
+
+#define SNDRV_SEQ_DEVICE_ARGPTR(dev) (void *)((char *)(dev) + sizeof(struct snd_seq_device))
+
+
+/*
+ * id strings for generic devices
+ */
+#define SNDRV_SEQ_DEV_ID_MIDISYNTH	"seq-midi"
+#define SNDRV_SEQ_DEV_ID_OPL3		"opl3-synth"
+
+#endif /* __SOUND_SEQ_DEVICE_H */
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/seq_instr.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/seq_instr.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/seq_instr.h	(revision 91)
@@ -0,0 +1,110 @@
+#ifndef __SOUND_SEQ_INSTR_H
+#define __SOUND_SEQ_INSTR_H
+
+/*
+ *  Main kernel header file for the ALSA sequencer
+ *  Copyright (c) 1999 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 "seq_kernel.h"
+
+/* Instrument cluster */
+struct snd_seq_kcluster {
+	snd_seq_instr_cluster_t cluster;
+	char name[32];
+	int priority;
+	struct snd_seq_kcluster *next;
+};
+
+/* return pointer to private data */
+#define KINSTR_DATA(kinstr)	(void *)(((char *)kinstr) + sizeof(struct snd_seq_kinstr))
+
+/* Instrument structure */
+struct snd_seq_kinstr {
+	struct snd_seq_instr instr;
+	char name[32];
+	int type;			/* instrument type */
+	int use;			/* use count */
+	int busy;			/* not useable */
+	int add_len;			/* additional length */
+	struct snd_seq_kinstr_ops *ops;	/* operations */
+	struct snd_seq_kinstr *next;
+};
+
+#define SNDRV_SEQ_INSTR_HASH_SIZE		32
+
+/* Instrument flags */
+#define SNDRV_SEQ_INSTR_FLG_DIRECT	(1<<0)	/* accept only direct events */
+
+/* List of all instruments */
+struct snd_seq_kinstr_list {
+	struct snd_seq_kinstr *hash[SNDRV_SEQ_INSTR_HASH_SIZE];
+	int count;			/* count of all instruments */
+	
+	struct snd_seq_kcluster *chash[SNDRV_SEQ_INSTR_HASH_SIZE];
+	int ccount;			/* count of all clusters */
+
+	int owner;			/* current owner of the instrument list */
+	unsigned int flags;
+
+	spinlock_t lock;
+	spinlock_t ops_lock;
+	struct semaphore ops_mutex;
+	unsigned long ops_flags;
+};
+
+#define SNDRV_SEQ_INSTR_NOTIFY_REMOVE	0
+#define SNDRV_SEQ_INSTR_NOTIFY_CHANGE	1
+
+struct snd_seq_kinstr_ops {
+	void *private_data;
+	long add_len;			/* additional length */
+	char *instr_type;
+	int (*info)(void *private_data, char *info_data, long len);
+	int (*put)(void *private_data, struct snd_seq_kinstr *kinstr,
+		   char __user *instr_data, long len, int atomic, int cmd);
+	int (*get)(void *private_data, struct snd_seq_kinstr *kinstr,
+		   char __user *instr_data, long len, int atomic, int cmd);
+	int (*get_size)(void *private_data, struct snd_seq_kinstr *kinstr, long *size);
+	int (*remove)(void *private_data, struct snd_seq_kinstr *kinstr, int atomic);
+	void (*notify)(void *private_data, struct snd_seq_kinstr *kinstr, int what);
+	struct snd_seq_kinstr_ops *next;
+};
+
+
+/* instrument operations */
+struct snd_seq_kinstr_list *snd_seq_instr_list_new(void);
+void snd_seq_instr_list_free(struct snd_seq_kinstr_list **list);
+int snd_seq_instr_list_free_cond(struct snd_seq_kinstr_list *list,
+				 struct snd_seq_instr_header *ifree,
+				 int client,
+				 int atomic);
+struct snd_seq_kinstr *snd_seq_instr_find(struct snd_seq_kinstr_list *list,
+					  struct snd_seq_instr *instr,
+					  int exact,
+					  int follow_alias);
+void snd_seq_instr_free_use(struct snd_seq_kinstr_list *list,
+			    struct snd_seq_kinstr *instr);
+int snd_seq_instr_event(struct snd_seq_kinstr_ops *ops,
+			struct snd_seq_kinstr_list *list,
+			struct snd_seq_event *ev,
+			int client,
+			int atomic,
+			int hop);
+
+#endif /* __SOUND_SEQ_INSTR_H */
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/seq_kernel.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/seq_kernel.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/seq_kernel.h	(revision 91)
@@ -0,0 +1,124 @@
+#ifndef __SOUND_SEQ_KERNEL_H
+#define __SOUND_SEQ_KERNEL_H
+
+/*
+ *  Main kernel header file for the ALSA sequencer
+ *  Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
+ *
+ *
+ *   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 <linux/time.h>
+#include "asequencer.h"
+
+typedef struct snd_seq_real_time snd_seq_real_time_t;
+typedef union snd_seq_timestamp snd_seq_timestamp_t;
+
+/* maximum number of events dequeued per schedule interval */
+#define SNDRV_SEQ_MAX_DEQUEUE		50
+
+/* maximum number of queues */
+#define SNDRV_SEQ_MAX_QUEUES		8
+
+/* max number of concurrent clients */
+#define SNDRV_SEQ_MAX_CLIENTS 		192
+
+/* max number of concurrent ports */
+#define SNDRV_SEQ_MAX_PORTS 		254
+
+/* max number of events in memory pool */
+#define SNDRV_SEQ_MAX_EVENTS		2000
+
+/* default number of events in memory chunk */
+#define SNDRV_SEQ_DEFAULT_CHUNK_EVENTS	64
+
+/* default number of events in memory pool */
+#define SNDRV_SEQ_DEFAULT_EVENTS	500
+
+/* max number of events in memory pool for one client (outqueue) */
+#define SNDRV_SEQ_MAX_CLIENT_EVENTS	2000
+
+/* default number of events in memory pool for one client (outqueue) */
+#define SNDRV_SEQ_DEFAULT_CLIENT_EVENTS	200
+
+/* max delivery path length */
+#define SNDRV_SEQ_MAX_HOPS		10
+
+/* max size of event size */
+#define SNDRV_SEQ_MAX_EVENT_LEN		0x3fffffff
+
+/* call-backs for kernel client */
+
+struct snd_seq_client_callback {
+	void *private_data;
+	unsigned allow_input: 1,
+		 allow_output: 1;
+	/*...*/
+};
+
+/* call-backs for kernel port */
+struct snd_seq_port_callback {
+	struct module *owner;
+	void *private_data;
+	int (*subscribe)(void *private_data, struct snd_seq_port_subscribe *info);
+	int (*unsubscribe)(void *private_data, struct snd_seq_port_subscribe *info);
+	int (*use)(void *private_data, struct snd_seq_port_subscribe *info);
+	int (*unuse)(void *private_data, struct snd_seq_port_subscribe *info);
+	int (*event_input)(struct snd_seq_event *ev, int direct, void *private_data, int atomic, int hop);
+	void (*private_free)(void *private_data);
+	unsigned int callback_all;	/* call subscribe callbacks at each connection/disconnection */
+	/*...*/
+};
+
+/* interface for kernel client */
+int snd_seq_create_kernel_client(struct snd_card *card, int client_index,
+				 struct snd_seq_client_callback *callback);
+int snd_seq_delete_kernel_client(int client);
+int snd_seq_kernel_client_enqueue(int client, struct snd_seq_event *ev, int atomic, int hop);
+int snd_seq_kernel_client_dispatch(int client, struct snd_seq_event *ev, int atomic, int hop);
+int snd_seq_kernel_client_ctl(int client, unsigned int cmd, void *arg);
+
+#define SNDRV_SEQ_EXT_MASK	0xc0000000
+#define SNDRV_SEQ_EXT_USRPTR	0x80000000
+#define SNDRV_SEQ_EXT_CHAINED	0x40000000
+
+typedef int (*snd_seq_dump_func_t)(void *ptr, void *buf, int count);
+int snd_seq_expand_var_event(const struct snd_seq_event *event, int count, char *buf,
+			     int in_kernel, int size_aligned);
+int snd_seq_dump_var_event(const struct snd_seq_event *event,
+			   snd_seq_dump_func_t func, void *private_data);
+
+/* interface for OSS emulation */
+int snd_seq_set_queue_tempo(int client, struct snd_seq_queue_tempo *tempo);
+
+/* port callback routines */
+void snd_port_init_callback(struct snd_seq_port_callback *p);
+struct snd_seq_port_callback *snd_port_alloc_callback(void);
+
+/* port attach/detach */
+int snd_seq_event_port_attach(int client, struct snd_seq_port_callback *pcbp,
+			      int cap, int type, int midi_channels, int midi_voices, char *portname);
+int snd_seq_event_port_detach(int client, int port);
+
+#ifdef CONFIG_KMOD
+void snd_seq_autoload_lock(void);
+void snd_seq_autoload_unlock(void);
+#else
+#define snd_seq_autoload_lock()
+#define snd_seq_autoload_unlock()
+#endif
+
+#endif /* __SOUND_SEQ_KERNEL_H */
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/seq_midi_emul.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/seq_midi_emul.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/seq_midi_emul.h	(revision 91)
@@ -0,0 +1,197 @@
+#ifndef __SOUND_SEQ_MIDI_EMUL_H
+#define __SOUND_SEQ_MIDI_EMUL_H
+
+/*
+ *  Midi channel definition for optional channel management.
+ *
+ *  Copyright (C) 1999 Steve Ratcliffe
+ *
+ *   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 "seq_kernel.h"
+
+/*
+ * This structure is used to keep track of the current state on each
+ * channel.  All drivers for hardware that does not understand midi
+ * directly will probably need to use this structure.
+ */
+struct snd_midi_channel {
+	void *private;		/* A back pointer to driver data */
+	int  number;		/* The channel number */
+	int  client;		/* The client associated with this channel */
+	int  port;		/* The port associated with this channel */
+
+	unsigned char midi_mode;	/* GM, GS, XG etc */
+	unsigned int 
+		drum_channel:1,		/* Drum channel */
+		param_type:1		/* RPN/NRPN */
+		;
+
+	unsigned char midi_aftertouch;	/* Aftertouch (key pressure) */
+	unsigned char midi_pressure;	/* Channel pressure */
+	unsigned char midi_program;	/* Instrument number */
+	short midi_pitchbend;		/* Pitch bend amount */
+
+	unsigned char control[128];	/* Current value of all controls */
+	unsigned char note[128];	/* Current status for all notes */
+
+	short gm_rpn_pitch_bend_range;	/* Pitch bend range */
+	short gm_rpn_fine_tuning; 	/* Master fine tuning */
+	short gm_rpn_coarse_tuning;	/* Master coarse tuning */
+
+};
+
+/*
+ * A structure that represets a set of channels bound to a port.  There
+ * would usually be 16 channels per port.  But fewer could be used for
+ * particular cases.
+ * The channel set consists of information describing the client and
+ * port for this midi synth and an array of snd_midi_channel structures.
+ * A driver that had no need for snd_midi_channel could still use the
+ * channel set type if it wished with the channel array null.
+ */
+struct snd_midi_channel_set {
+	void *private_data;		/* Driver data */
+	int  client;			/* Client for this port */
+	int  port;			/* The port number */
+
+	int  max_channels;		/* Size of the channels array */
+	struct snd_midi_channel *channels;
+
+	unsigned char midi_mode;	/* MIDI operating mode */
+	unsigned char gs_master_volume;	/* SYSEX master volume: 0-127 */
+	unsigned char gs_chorus_mode;
+	unsigned char gs_reverb_mode;
+
+};
+
+struct snd_midi_op {
+	void (*note_on)(void *private_data, int note, int vel, struct snd_midi_channel *chan);
+	void (*note_off)(void *private_data,int note, int vel, struct snd_midi_channel *chan); /* release note */
+	void (*key_press)(void *private_data, int note, int vel, struct snd_midi_channel *chan);
+	void (*note_terminate)(void *private_data, int note, struct snd_midi_channel *chan); /* terminate note immediately */
+	void (*control)(void *private_data, int type, struct snd_midi_channel *chan);
+	void (*nrpn)(void *private_data, struct snd_midi_channel *chan,
+		     struct snd_midi_channel_set *chset);
+	void (*sysex)(void *private_data, unsigned char *buf, int len, int parsed,
+		      struct snd_midi_channel_set *chset);
+};
+
+/*
+ * These defines are used so that pitchbend, aftertouch etc, can be
+ * distinguished from controller values.
+ */
+/* 0-127 controller values */
+#define MIDI_CTL_PITCHBEND	0x80
+#define MIDI_CTL_AFTERTOUCH	0x81
+#define MIDI_CTL_CHAN_PRESSURE	0x82
+
+/*
+ * These names exist to allow symbolic access to the controls array.
+ * The usage is eg: chan->gm_bank_select.  Another implementation would
+ * be really have these members in the struct, and not the array.
+ */
+#define gm_bank_select		control[0]
+#define gm_modulation		control[1]
+#define gm_breath		control[2]
+#define gm_foot_pedal		control[4]
+#define gm_portamento_time	control[5]
+#define gm_data_entry		control[6]
+#define gm_volume		control[7]
+#define gm_balance		control[8]
+#define gm_pan			control[10]
+#define gm_expression		control[11]
+#define gm_effect_control1	control[12]
+#define gm_effect_control2	control[13]
+#define gm_slider1		control[16]
+#define gm_slider2		control[17]
+#define gm_slider3		control[18]
+#define gm_slider4		control[19]
+
+#define gm_bank_select_lsb	control[32]
+#define gm_modulation_wheel_lsb	control[33]
+#define gm_breath_lsb		control[34]
+#define gm_foot_pedal_lsb	control[36]
+#define gm_portamento_time_lsb	control[37]
+#define gm_data_entry_lsb	control[38]
+#define gm_volume_lsb		control[39]
+#define gm_balance_lsb		control[40]
+#define gm_pan_lsb		control[42]
+#define gm_expression_lsb	control[43]
+#define gm_effect_control1_lsb	control[44]
+#define gm_effect_control2_lsb	control[45]
+
+#define gm_sustain	 	control[MIDI_CTL_SUSTAIN]
+#define gm_hold			gm_sustain
+#define gm_portamento		control[MIDI_CTL_PORTAMENTO]
+#define gm_sostenuto		control[MIDI_CTL_SOSTENUTO]
+
+/*
+ * These macros give the complete value of the controls that consist
+ * of coarse and fine pairs.  Of course the fine controls are seldom used
+ * but there is no harm in being complete.
+ */
+#define SNDRV_GM_BANK_SELECT(cp)		(((cp)->control[0]<<7)|((cp)->control[32]))
+#define SNDRV_GM_MODULATION_WHEEL(cp)	(((cp)->control[1]<<7)|((cp)->control[33]))
+#define SNDRV_GM_BREATH(cp)		(((cp)->control[2]<<7)|((cp)->control[34]))
+#define SNDRV_GM_FOOT_PEDAL(cp)		(((cp)->control[4]<<7)|((cp)->control[36]))
+#define SNDRV_GM_PORTAMENTO_TIME(cp)	(((cp)->control[5]<<7)|((cp)->control[37]))
+#define SNDRV_GM_DATA_ENTRY(cp)		(((cp)->control[6]<<7)|((cp)->control[38]))
+#define SNDRV_GM_VOLUME(cp)		(((cp)->control[7]<<7)|((cp)->control[39]))
+#define SNDRV_GM_BALANCE(cp)		(((cp)->control[8]<<7)|((cp)->control[40]))
+#define SNDRV_GM_PAN(cp)			(((cp)->control[10]<<7)|((cp)->control[42]))
+#define SNDRV_GM_EXPRESSION(cp)		(((cp)->control[11]<<7)|((cp)->control[43]))
+
+
+/* MIDI mode */
+#define SNDRV_MIDI_MODE_NONE	0	/* Generic midi */
+#define SNDRV_MIDI_MODE_GM	1
+#define SNDRV_MIDI_MODE_GS	2
+#define SNDRV_MIDI_MODE_XG	3
+#define SNDRV_MIDI_MODE_MT32	4
+
+/* MIDI note state */
+#define SNDRV_MIDI_NOTE_OFF		0x00
+#define SNDRV_MIDI_NOTE_ON		0x01
+#define SNDRV_MIDI_NOTE_RELEASED		0x02
+#define SNDRV_MIDI_NOTE_SOSTENUTO		0x04
+ 
+#define SNDRV_MIDI_PARAM_TYPE_REGISTERED		0
+#define SNDRV_MIDI_PARAM_TYPE_NONREGISTERED	1
+
+/* SYSEX parse flag */
+enum {
+	SNDRV_MIDI_SYSEX_NOT_PARSED = 0,
+	SNDRV_MIDI_SYSEX_GM_ON,	
+	SNDRV_MIDI_SYSEX_GS_ON,	
+	SNDRV_MIDI_SYSEX_GS_RESET,	
+	SNDRV_MIDI_SYSEX_GS_CHORUS_MODE,
+	SNDRV_MIDI_SYSEX_GS_REVERB_MODE,
+	SNDRV_MIDI_SYSEX_GS_MASTER_VOLUME,
+	SNDRV_MIDI_SYSEX_GS_PROGRAM,
+	SNDRV_MIDI_SYSEX_GS_DRUM_CHANNEL,
+	SNDRV_MIDI_SYSEX_XG_ON,	
+};
+
+/* Prototypes for midi_process.c */
+void snd_midi_process_event(struct snd_midi_op *ops, struct snd_seq_event *ev,
+			    struct snd_midi_channel_set *chanset);
+void snd_midi_channel_set_clear(struct snd_midi_channel_set *chset);
+struct snd_midi_channel_set *snd_midi_channel_alloc_set(int n);
+void snd_midi_channel_free_set(struct snd_midi_channel_set *chset);
+
+#endif /* __SOUND_SEQ_MIDI_EMUL_H */
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/seq_midi_event.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/seq_midi_event.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/seq_midi_event.h	(revision 91)
@@ -0,0 +1,54 @@
+#ifndef __SOUND_SEQ_MIDI_EVENT_H
+#define __SOUND_SEQ_MIDI_EVENT_H
+
+/*
+ *  MIDI byte <-> sequencer event coder
+ *
+ *  Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>,
+ *                        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 "asequencer.h"
+
+#define MAX_MIDI_EVENT_BUF	256
+
+/* midi status */
+struct snd_midi_event {
+	int qlen;		/* queue length */
+	int read;		/* chars read */
+	int type;		/* current event type */
+	unsigned char lastcmd;	/* last command (for MIDI state handling) */
+	unsigned char nostat;	/* no state flag */
+	int bufsize;		/* allocated buffer size */
+	unsigned char *buf;	/* input buffer */
+	spinlock_t lock;
+};
+
+int snd_midi_event_new(int bufsize, struct snd_midi_event **rdev);
+void snd_midi_event_free(struct snd_midi_event *dev);
+void snd_midi_event_reset_encode(struct snd_midi_event *dev);
+void snd_midi_event_reset_decode(struct snd_midi_event *dev);
+void snd_midi_event_no_status(struct snd_midi_event *dev, int on);
+/* encode from byte stream - return number of written bytes if success */
+long snd_midi_event_encode(struct snd_midi_event *dev, unsigned char *buf, long count,
+			   struct snd_seq_event *ev);
+int snd_midi_event_encode_byte(struct snd_midi_event *dev, int c, struct snd_seq_event *ev);
+/* decode from event to bytes - return number of written bytes if success */
+long snd_midi_event_decode(struct snd_midi_event *dev, unsigned char *buf, long count,
+			   struct snd_seq_event *ev);
+
+#endif /* __SOUND_SEQ_MIDI_EVENT_H */
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/seq_oss.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/seq_oss.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/seq_oss.h	(revision 91)
@@ -0,0 +1,102 @@
+/*
+ * OSS compatible sequencer driver
+ *
+ * Copyright (C) 1998,99 Takashi Iwai
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __SEQ_OSS_H
+#define __SEQ_OSS_H
+
+#include "asequencer.h"
+#include "seq_kernel.h"
+
+/*
+ * type definitions
+ */
+typedef struct snd_seq_oss_arg_t snd_seq_oss_arg_t;
+typedef struct snd_seq_oss_callback_t snd_seq_oss_callback_t;
+
+/*
+ * argument structure for synthesizer operations
+ */
+struct snd_seq_oss_arg_t {
+	/* given by OSS sequencer */
+	int app_index;	/* application unique index */
+	int file_mode;	/* file mode - see below */
+	int seq_mode;	/* sequencer mode - see below */
+
+	/* following must be initialized in open callback */
+	snd_seq_addr_t addr;	/* opened port address */
+	void *private_data;	/* private data for lowlevel drivers */
+
+	/* note-on event passing mode: initially given by OSS seq,
+	 * but configurable by drivers - see below
+	 */
+	int event_passing;
+};
+
+
+/*
+ * synthesizer operation callbacks
+ */
+struct snd_seq_oss_callback_t {
+	struct module *owner;
+	int (*open)(snd_seq_oss_arg_t *p, void *closure);
+	int (*close)(snd_seq_oss_arg_t *p);
+	int (*ioctl)(snd_seq_oss_arg_t *p, unsigned int cmd, unsigned long arg);
+	int (*load_patch)(snd_seq_oss_arg_t *p, int format, const char *buf, int offs, int count);
+	int (*reset)(snd_seq_oss_arg_t *p);
+	int (*raw_event)(snd_seq_oss_arg_t *p, unsigned char *data);
+};
+
+/* flag: file_mode */
+#define SNDRV_SEQ_OSS_FILE_ACMODE		3
+#define SNDRV_SEQ_OSS_FILE_READ		1
+#define SNDRV_SEQ_OSS_FILE_WRITE		2
+#define SNDRV_SEQ_OSS_FILE_NONBLOCK	4
+
+/* flag: seq_mode */
+#define SNDRV_SEQ_OSS_MODE_SYNTH		0
+#define SNDRV_SEQ_OSS_MODE_MUSIC		1
+
+/* flag: event_passing */
+#define SNDRV_SEQ_OSS_PROCESS_EVENTS	0	/* key == 255 is processed as velocity change */
+#define SNDRV_SEQ_OSS_PASS_EVENTS		1	/* pass all events to callback */
+#define SNDRV_SEQ_OSS_PROCESS_KEYPRESS	2	/* key >= 128 will be processed as key-pressure */
+
+/* default control rate: fixed */
+#define SNDRV_SEQ_OSS_CTRLRATE		100
+
+/* default max queue length: configurable by module option */
+#define SNDRV_SEQ_OSS_MAX_QLEN		1024
+
+
+/*
+ * data pointer to snd_seq_register_device
+ */
+typedef struct snd_seq_oss_reg {
+	int type;
+	int subtype;
+	int nvoices;
+	snd_seq_oss_callback_t oper;
+	void *private_data;
+} snd_seq_oss_reg_t;
+
+/* device id */
+#define SNDRV_SEQ_DEV_ID_OSS		"seq-oss"
+
+#endif
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/seq_oss_legacy.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/seq_oss_legacy.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/seq_oss_legacy.h	(revision 91)
@@ -0,0 +1,30 @@
+/*
+ * OSS compatible macro definitions
+ *
+ * Copyright (C) 2000 Abramo Bagnara <abramo@alsa-project.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __SEQ_OSS_LEGACY_H
+#define __SEQ_OSS_LEGACY_H
+
+#include <linux/soundcard.h>
+
+#ifndef SAMPLE_TYPE_AWE32
+#define SAMPLE_TYPE_AWE32		0x20
+#endif
+
+#endif
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/seq_virmidi.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/seq_virmidi.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/seq_virmidi.h	(revision 91)
@@ -0,0 +1,81 @@
+#ifndef __SOUND_SEQ_VIRMIDI_H
+#define __SOUND_SEQ_VIRMIDI_H
+
+/*
+ *  Virtual Raw MIDI client on Sequencer
+ *  Copyright (c) 2000 by Takashi Iwai <tiwai@suse.de>,
+ *                        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 "rawmidi.h"
+#include "seq_midi_event.h"
+
+/*
+ * device file instance:
+ * This instance is created at each time the midi device file is
+ * opened.  Each instance has its own input buffer and MIDI parser
+ * (buffer), and is associated with the device instance.
+ */
+struct snd_virmidi {
+	struct list_head list;
+	int seq_mode;
+	int client;
+	int port;
+	unsigned int trigger: 1;
+	struct snd_midi_event *parser;
+	struct snd_seq_event event;
+	struct snd_virmidi_dev *rdev;
+	struct snd_rawmidi_substream *substream;
+};
+
+#define SNDRV_VIRMIDI_SUBSCRIBE		(1<<0)
+#define SNDRV_VIRMIDI_USE		(1<<1)
+
+/*
+ * device record:
+ * Each virtual midi device has one device instance.  It contains
+ * common information and the linked-list of opened files, 
+ */
+struct snd_virmidi_dev {
+	struct snd_card *card;		/* associated card */
+	struct snd_rawmidi *rmidi;		/* rawmidi device */
+	int seq_mode;			/* SNDRV_VIRMIDI_XXX */
+	int device;			/* sequencer device */
+	int client;			/* created/attached client */
+	int port;			/* created/attached port */
+	unsigned int flags;		/* SNDRV_VIRMIDI_* */
+	rwlock_t filelist_lock;
+	struct list_head filelist;
+};
+
+/* sequencer mode:
+ * ATTACH = input/output events from midi device are routed to the
+ *          attached sequencer port.  sequencer port is not created
+ *          by virmidi itself.
+ *          the input to rawmidi must be processed by passing the
+ *          incoming events via snd_virmidi_receive()
+ * DISPATCH = input/output events are routed to subscribers.
+ *            sequencer port is created in virmidi.
+ */
+#define SNDRV_VIRMIDI_SEQ_NONE		0
+#define SNDRV_VIRMIDI_SEQ_ATTACH	1
+#define SNDRV_VIRMIDI_SEQ_DISPATCH	2
+
+int snd_virmidi_new(struct snd_card *card, int device, struct snd_rawmidi **rrmidi);
+
+#endif /* __SOUND_SEQ_VIRMIDI */
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/seqmid.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/seqmid.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/seqmid.h	(revision 91)
@@ -0,0 +1,399 @@
+/**
+ * \file <alsa/seqmid.h>
+ * \brief Application interface library for the ALSA driver
+ * \author Jaroslav Kysela <perex@suse.cz>
+ * \author Abramo Bagnara <abramo@alsa-project.org>
+ * \author Takashi Iwai <tiwai@suse.de>
+ * \date 1998-2001
+ *
+ * Application interface library for the ALSA driver
+ *
+ *
+ *   This library is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU Lesser General Public License as
+ *   published by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this library; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#ifndef __ALSA_SEQMID_H
+#define __ALSA_SEQMID_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ *  \defgroup SeqMiddle Sequencer Middle Level Interface
+ *  Sequencer Middle Level Interface
+ *  \ingroup Sequencer
+ *  \{
+ */
+
+/**
+ * \brief initialize event record
+ * \param ev event record pointer
+ */
+#define snd_seq_ev_clear(ev) \
+	memset(ev, 0, sizeof(snd_seq_event_t))
+
+/**
+ * \brief set the explicit destination
+ * \param ev event record
+ * \param c destination client id
+ * \param p destination port id
+ */
+#define snd_seq_ev_set_dest(ev,c,p) \
+	((ev)->dest.client = (c), (ev)->dest.port = (p))
+
+/**
+ * \brief set broadcasting to subscribers
+ * \param ev event record
+ */
+#define snd_seq_ev_set_subs(ev) \
+	((ev)->dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS,\
+	 (ev)->dest.port = SNDRV_SEQ_ADDRESS_UNKNOWN)
+
+/**
+ * \brief set broadcasting to all clients/ports
+ * \param ev event record
+ */
+#define snd_seq_ev_set_broadcast(ev) \
+	((ev)->dest.client = SNDRV_SEQ_ADDRESS_BROADCAST,\
+	 (ev)->dest.port = SNDRV_SEQ_ADDRESS_BROADCAST)
+
+/**
+ * \brief set the source port
+ * \param ev event record
+ * \param p source port id
+ */
+#define snd_seq_ev_set_source(ev,p) \
+	((ev)->source.port = (p))
+
+/**
+ * \brief set direct passing mode (without queued)
+ * \param ev event instance
+ */
+#define snd_seq_ev_set_direct(ev) \
+	((ev)->queue = SNDRV_SEQ_QUEUE_DIRECT)
+
+/**
+ * \brief set tick-scheduling mode on queue
+ * \param ev event instance
+ * \param q queue id to schedule
+ * \param relative relative time-stamp if non-zero
+ * \param ttick tick time-stamp to be delivered
+ */
+#define snd_seq_ev_schedule_tick(ev, q, relative, ttick) \
+	((ev)->flags &= ~(SNDRV_SEQ_TIME_STAMP_MASK | SNDRV_SEQ_TIME_MODE_MASK),\
+	 (ev)->flags |= SNDRV_SEQ_TIME_STAMP_TICK,\
+	 (ev)->flags |= (relative) ? SNDRV_SEQ_TIME_MODE_REL : SNDRV_SEQ_TIME_MODE_ABS,\
+	 (ev)->time.tick = (ttick),\
+	 (ev)->queue = (q))
+
+/**
+ * \brief set real-time-scheduling mode on queue
+ * \param ev event instance
+ * \param q queue id to schedule
+ * \param relative relative time-stamp if non-zero
+ * \param rtime time-stamp to be delivered
+ */
+#define snd_seq_ev_schedule_real(ev, q, relative, rtime) \
+	((ev)->flags &= ~(SNDRV_SEQ_TIME_STAMP_MASK | SNDRV_SEQ_TIME_MODE_MASK),\
+	 (ev)->flags |= SNDRV_SEQ_TIME_STAMP_REAL,\
+	 (ev)->flags |= (relative) ? SNDRV_SEQ_TIME_MODE_REL : SNDRV_SEQ_TIME_MODE_ABS,\
+	 (ev)->time.time = *(rtime),\
+	 (ev)->queue = (q))
+
+/**
+ * \brief set event priority
+ * \param ev event instance
+ * \param high_prior 1 for high priority mode
+ */
+#define snd_seq_ev_set_priority(ev, high_prior) \
+	((ev)->flags &= ~SNDRV_SEQ_PRIORITY_MASK,\
+	 (ev)->flags |= (high_prior) ? SNDRV_SEQ_PRIORITY_HIGH : SNDRV_SEQ_PRIORITY_NORMAL)
+
+/**
+ * \brief set fixed data
+ * \param ev event instance
+ *
+ * Sets the event length mode as fixed size.
+ */
+#define snd_seq_ev_set_fixed(ev) \
+	((ev)->flags &= ~SNDRV_SEQ_EVENT_LENGTH_MASK,\
+	 (ev)->flags |= SNDRV_SEQ_EVENT_LENGTH_FIXED)
+
+/**
+ * \brief set variable data
+ * \param ev event instance
+ * \param datalen length of the external data
+ * \param dataptr pointer of the external data
+ *
+ * Sets the event length mode as variable length and stores the data.
+ */
+#define snd_seq_ev_set_variable(ev, datalen, dataptr) \
+	((ev)->flags &= ~SNDRV_SEQ_EVENT_LENGTH_MASK,\
+	 (ev)->flags |= SNDRV_SEQ_EVENT_LENGTH_VARIABLE,\
+	 (ev)->data.ext.len = (datalen),\
+	 (ev)->data.ext.ptr = (dataptr))
+
+/**
+ * \brief set varusr data
+ * \param ev event instance
+ * \param len length of the external data
+ * \param ptr pointer of the external data
+ *
+ * Sets the event length mode as variable user-space data and stores the data.
+ */
+#define snd_seq_ev_set_varusr(ev, datalen, dataptr) \
+	((ev)->flags &= ~SNDRV_SEQ_EVENT_LENGTH_MASK,\
+	 (ev)->flags |= SNDRV_SEQ_EVENT_LENGTH_VARUSR,\
+	 (ev)->data.ext.len = (datalen),\
+	 (ev)->data.ext.ptr = (dataptr))
+
+/**
+ * \brief set queue controls
+ * \param ev event record
+ * \param typ event type
+ * \param q queue id
+ * \param val control value
+ */
+#define snd_seq_ev_set_queue_control(ev, typ, q, val) \
+	((ev)->type = (typ),\
+	 snd_seq_ev_set_dest(ev, SNDRV_SEQ_CLIENT_SYSTEM, SNDRV_SEQ_PORT_SYSTEM_TIMER),\
+	 (ev)->data.queue.queue = (q),\
+	 (ev)->data.queue.param.value = (val))
+
+/**
+ * \brief set the start queue event
+ * \param ev event record
+ * \param q queue id to start
+ */
+#define snd_seq_ev_set_queue_start(ev, q) \
+	snd_seq_ev_set_queue_control(ev, SNDRV_SEQ_EVENT_START, q, 0)
+
+/**
+ * \brief set the stop queue event
+ * \param ev event record
+ * \param q queue id to stop
+ */
+#define snd_seq_ev_set_queue_stop(ev, q) \
+	snd_seq_ev_set_queue_control(ev, SNDRV_SEQ_EVENT_STOP, q, 0)
+
+/**
+ * \brief set the stop queue event
+ * \param ev event record
+ * \param q queue id to continue
+ */
+#define snd_seq_ev_set_queue_continue(ev, q) \
+	snd_seq_ev_set_queue_control(ev, SNDRV_SEQ_EVENT_CONTINUE, q, 0)
+
+/**
+ * \brief set the stop queue event
+ * \param ev event record
+ * \param q queue id to change tempo
+ * \param val the new tempo value
+ */
+#define snd_seq_ev_set_queue_tempo(ev, q, val) \
+	snd_seq_ev_set_queue_control(ev, SNDRV_SEQ_EVENT_TEMPO, q, val)
+
+/**
+ * \brief set the real-time position of a queue
+ * \param ev event record
+ * \param q queue id to change tempo
+ * \param rtime the new real-time pointer
+ */
+#define snd_seq_ev_set_queue_pos_real(ev, q, rtime) \
+	((ev)->type = SNDRV_SEQ_EVENT_SETPOS_TIME,\
+	 snd_seq_ev_set_dest(ev, SNDRV_SEQ_CLIENT_SYSTEM, SNDRV_SEQ_PORT_SYSTEM_TIMER),\
+	 (ev)->data.queue.queue = (q),\
+	 (ev)->data.queue.param.time.time = *(rtime))
+
+/**
+ * \brief set the tick-time position of a queue
+ * \param ev event record
+ * \param q queue id to change tempo
+ * \param ttime the new tick-time
+ */
+#define snd_seq_ev_set_queue_pos_tick(ev, q, ttime) \
+	((ev)->type = SNDRV_SEQ_EVENT_SETPOS_TICK,\
+	 snd_seq_ev_set_dest(ev, SNDRV_SEQ_CLIENT_SYSTEM, SNDRV_SEQ_PORT_SYSTEM_TIMER),\
+	 (ev)->data.queue.queue = (q),\
+	 (ev)->data.queue.param.time.tick = (ttime))
+
+/**
+ * \brief start the specified queue
+ * \param seq sequencer handle
+ * \param q queue id to start
+ * \param ev optional event record (see #snd_seq_control_queue)
+ */
+#define snd_seq_start_queue(seq, q, ev) \
+	snd_seq_control_queue(seq, q, SNDRV_SEQ_EVENT_START, 0, ev)
+
+/**
+ * \brief stop the specified queue
+ * \param seq sequencer handle
+ * \param q queue id to stop
+ * \param ev optional event record (see #snd_seq_control_queue)
+ */
+#define snd_seq_stop_queue(seq, q, ev) \
+	snd_seq_control_queue(seq, q, SNDRV_SEQ_EVENT_STOP, 0, ev)
+
+/**
+ * \brief continue the specified queue
+ * \param seq sequencer handle
+ * \param q queue id to continue
+ * \param ev optional event record (see #snd_seq_control_queue)
+ */
+#define snd_seq_continue_queue(seq, q, ev) \
+	snd_seq_control_queue(seq, q, SNDRV_SEQ_EVENT_CONTINUE, 0, ev)
+
+/**
+ * \brief change the tempo of the specified queue
+ * \param seq sequencer handle
+ * \param q queue id
+ * \param tempo the new tempo value
+ * \param ev optional event record (see #snd_seq_control_queue)
+ */
+#define snd_seq_change_queue_tempo(seq, q, tempo, ev) \
+	snd_seq_control_queue(seq, q, SNDRV_SEQ_EVENT_TEMPO, tempo, ev)
+
+
+/**
+ * \brief set note event
+ * \param ev event record
+ * \param ch channel number
+ * \param key note key
+ * \param vel velocity
+ * \param dur duration (in tick or msec)
+ */
+#define snd_seq_ev_set_note(ev, ch, key, vel, dur) \
+	((ev)->type = SNDRV_SEQ_EVENT_NOTE,\
+	 snd_seq_ev_set_fixed(ev),\
+	 (ev)->data.note.channel = (ch),\
+	 (ev)->data.note.note = (key),\
+	 (ev)->data.note.velocity = (vel),\
+	 (ev)->data.note.duration = (dur))
+
+/**
+ * \brief set note-on event
+ * \param ev event record
+ * \param ch channel number
+ * \param key note key
+ * \param vel velocity
+ */
+#define snd_seq_ev_set_noteon(ev, ch, key, vel) \
+	((ev)->type = SNDRV_SEQ_EVENT_NOTEON,\
+	 snd_seq_ev_set_fixed(ev),\
+	 (ev)->data.note.channel = (ch),\
+	 (ev)->data.note.note = (key),\
+	 (ev)->data.note.velocity = (vel))
+
+/**
+ * \brief set note-off event
+ * \param ev event record
+ * \param ch channel number
+ * \param key note key
+ * \param vel velocity
+ */
+#define snd_seq_ev_set_noteoff(ev, ch, key, vel) \
+	((ev)->type = SNDRV_SEQ_EVENT_NOTEOFF,\
+	 snd_seq_ev_set_fixed(ev),\
+	 (ev)->data.note.channel = (ch),\
+	 (ev)->data.note.note = (key),\
+	 (ev)->data.note.velocity = (vel))
+
+/**
+ * \brief set key-pressure event
+ * \param ev event record
+ * \param ch channel number
+ * \param key note key
+ * \param vel velocity
+ */
+#define snd_seq_ev_set_keypress(ev,ch,key,vel) \
+	((ev)->type = SNDRV_SEQ_EVENT_KEYPRESS,\
+	 snd_seq_ev_set_fixed(ev),\
+	 (ev)->data.note.channel = (ch),\
+	 (ev)->data.note.note = (key),\
+	 (ev)->data.note.velocity = (vel))
+
+/**
+ * \brief set MIDI controller event
+ * \param ev event record
+ * \param ch channel number
+ * \param cc controller number
+ * \param val control value
+ */
+#define snd_seq_ev_set_controller(ev,ch,cc,val) \
+	((ev)->type = SNDRV_SEQ_EVENT_CONTROLLER,\
+	 snd_seq_ev_set_fixed(ev),\
+	 (ev)->data.control.channel = (ch),\
+	 (ev)->data.control.param = (cc),\
+	 (ev)->data.control.value = (val))
+
+/**
+ * \brief set program change event
+ * \param ev event record
+ * \param ch channel number
+ * \param val program number
+ */
+#define snd_seq_ev_set_pgmchange(ev,ch,val) \
+	((ev)->type = SNDRV_SEQ_EVENT_PGMCHANGE,\
+	 snd_seq_ev_set_fixed(ev),\
+	 (ev)->data.control.channel = (ch),\
+	 (ev)->data.control.value = (val))
+
+/**
+ * \brief set pitch-bend event
+ * \param ev event record
+ * \param ch channel number
+ * \param val pitch bend; zero centered from -8192 to 8191
+ */
+#define snd_seq_ev_set_pitchbend(ev,ch,val) \
+	((ev)->type = SNDRV_SEQ_EVENT_PITCHBEND,\
+	 snd_seq_ev_set_fixed(ev),\
+	 (ev)->data.control.channel = (ch),\
+	 (ev)->data.control.value = (val))
+
+/**
+ * \brief set channel pressure event
+ * \param ev event record
+ * \param ch channel number
+ * \param val channel pressure value
+ */
+#define snd_seq_ev_set_chanpress(ev,ch,val) \
+	((ev)->type = SNDRV_SEQ_EVENT_CHANPRESS,\
+	 snd_seq_ev_set_fixed(ev),\
+	 (ev)->data.control.channel = (ch),\
+	 (ev)->data.control.value = (val))
+
+/**
+ * \brief set sysex event
+ * \param ev event record
+ * \param datalen length of sysex data
+ * \param dataptr sysex data pointer
+ *
+ * the sysex data must contain the start byte 0xf0 and the end byte 0xf7.
+ */
+#define snd_seq_ev_set_sysex(ev,datalen,dataptr) \
+	((ev)->type = SNDRV_SEQ_EVENT_SYSEX,\
+	 snd_seq_ev_set_variable(ev, datalen, dataptr))
+
+/** \} */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __ALSA_SEQMID_H */
+
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/sfnt_info.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/sfnt_info.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/sfnt_info.h	(revision 91)
@@ -0,0 +1,214 @@
+#ifndef __SOUND_SFNT_INFO_H
+#define __SOUND_SFNT_INFO_H
+
+/*
+ *  Patch record compatible with AWE driver on OSS
+ *
+ *  Copyright (C) 1999-2000 Takashi Iwai
+ *
+ *   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/asound.h>
+
+/*
+ * patch information record
+ */
+
+#ifdef SNDRV_BIG_ENDIAN
+#define SNDRV_OSS_PATCHKEY(id) (0xfd00|id)
+#else
+#define SNDRV_OSS_PATCHKEY(id) ((id<<8)|0xfd)
+#endif
+
+/* patch interface header: 16 bytes */
+struct soundfont_patch_info {
+	unsigned short key;		/* use the key below */
+#define SNDRV_OSS_SOUNDFONT_PATCH		SNDRV_OSS_PATCHKEY(0x07)
+
+	short device_no;		/* synthesizer number */
+	unsigned short sf_id;		/* file id (should be zero) */
+	short optarg;			/* optional argument */
+	int len;			/* data length (without this header) */
+
+	short type;			/* patch operation type */
+#define SNDRV_SFNT_LOAD_INFO		0	/* awe_voice_rec */
+#define SNDRV_SFNT_LOAD_DATA		1	/* awe_sample_info */
+#define SNDRV_SFNT_OPEN_PATCH	2	/* awe_open_parm */
+#define SNDRV_SFNT_CLOSE_PATCH	3	/* none */
+	/* 4 is obsolete */
+#define SNDRV_SFNT_REPLACE_DATA	5	/* awe_sample_info (optarg=#channels)*/
+#define SNDRV_SFNT_MAP_PRESET	6	/* awe_voice_map */
+	/* 7 is not used */
+#define SNDRV_SFNT_PROBE_DATA		8	/* optarg=sample */
+#define SNDRV_SFNT_REMOVE_INFO		9	/* optarg=(bank<<8)|instr */
+
+	short reserved;			/* word alignment data */
+
+	/* the actual patch data begins after this */
+};
+
+
+/*
+ * open patch
+ */
+
+#define SNDRV_SFNT_PATCH_NAME_LEN	32
+
+struct soundfont_open_parm {
+	unsigned short type;		/* sample type */
+#define SNDRV_SFNT_PAT_TYPE_MISC	0
+#define SNDRV_SFNT_PAT_TYPE_GUS	6
+#define SNDRV_SFNT_PAT_TYPE_MAP	7
+#define SNDRV_SFNT_PAT_LOCKED	0x100	/* lock the samples */
+#define SNDRV_SFNT_PAT_SHARED	0x200	/* sample is shared */
+
+	short reserved;
+	char name[SNDRV_SFNT_PATCH_NAME_LEN];
+};
+
+
+/*
+ * raw voice information record
+ */
+
+/* wave table envelope & effect parameters to control EMU8000 */
+struct soundfont_voice_parm {
+	unsigned short moddelay;	/* modulation delay (0x8000) */
+	unsigned short modatkhld;	/* modulation attack & hold time (0x7f7f) */
+	unsigned short moddcysus;	/* modulation decay & sustain (0x7f7f) */
+	unsigned short modrelease;	/* modulation release time (0x807f) */
+	short modkeyhold, modkeydecay;	/* envelope change per key (not used) */
+	unsigned short voldelay;	/* volume delay (0x8000) */
+	unsigned short volatkhld;	/* volume attack & hold time (0x7f7f) */
+	unsigned short voldcysus;	/* volume decay & sustain (0x7f7f) */
+	unsigned short volrelease;	/* volume release time (0x807f) */
+	short volkeyhold, volkeydecay;	/* envelope change per key (not used) */
+	unsigned short lfo1delay;	/* LFO1 delay (0x8000) */
+	unsigned short lfo2delay;	/* LFO2 delay (0x8000) */
+	unsigned short pefe;		/* modulation pitch & cutoff (0x0000) */
+	unsigned short fmmod;		/* LFO1 pitch & cutoff (0x0000) */
+	unsigned short tremfrq;		/* LFO1 volume & freq (0x0000) */
+	unsigned short fm2frq2;		/* LFO2 pitch & freq (0x0000) */
+	unsigned char cutoff;		/* initial cutoff (0xff) */
+	unsigned char filterQ;		/* initial filter Q [0-15] (0x0) */
+	unsigned char chorus;		/* chorus send (0x00) */
+	unsigned char reverb;		/* reverb send (0x00) */
+	unsigned short reserved[4];	/* not used */
+};
+
+
+/* wave table parameters: 92 bytes */
+struct soundfont_voice_info {
+	unsigned short sf_id;		/* file id (should be zero) */
+	unsigned short sample;		/* sample id */
+	int start, end;			/* sample offset correction */
+	int loopstart, loopend;		/* loop offset correction */
+	short rate_offset;		/* sample rate pitch offset */
+	unsigned short mode;		/* sample mode */
+#define SNDRV_SFNT_MODE_ROMSOUND		0x8000
+#define SNDRV_SFNT_MODE_STEREO		1
+#define SNDRV_SFNT_MODE_LOOPING		2
+#define SNDRV_SFNT_MODE_NORELEASE		4	/* obsolete */
+#define SNDRV_SFNT_MODE_INIT_PARM		8
+
+	short root;			/* midi root key */
+	short tune;			/* pitch tuning (in cents) */
+	unsigned char low, high;	/* key note range */
+	unsigned char vellow, velhigh;	/* velocity range */
+	signed char fixkey, fixvel;	/* fixed key, velocity */
+	signed char pan, fixpan;	/* panning, fixed panning */
+	short exclusiveClass;		/* exclusive class (0 = none) */
+	unsigned char amplitude;	/* sample volume (127 max) */
+	unsigned char attenuation;	/* attenuation (0.375dB) */
+	short scaleTuning;		/* pitch scale tuning(%), normally 100 */
+	struct soundfont_voice_parm parm;	/* voice envelope parameters */
+	unsigned short sample_mode;	/* sample mode_flag (set by driver) */
+};
+
+
+/* instrument info header: 4 bytes */
+struct soundfont_voice_rec_hdr {
+	unsigned char bank;		/* midi bank number */
+	unsigned char instr;		/* midi preset number */
+	char nvoices;			/* number of voices */
+	char write_mode;		/* write mode; normally 0 */
+#define SNDRV_SFNT_WR_APPEND		0	/* append anyway */
+#define SNDRV_SFNT_WR_EXCLUSIVE		1	/* skip if already exists */
+#define SNDRV_SFNT_WR_REPLACE		2	/* replace if already exists */
+};
+
+
+/*
+ * sample wave information
+ */
+
+/* wave table sample header: 32 bytes */
+struct soundfont_sample_info {
+	unsigned short sf_id;		/* file id (should be zero) */
+	unsigned short sample;		/* sample id */
+	int start, end;			/* start & end offset */
+	int loopstart, loopend;		/* loop start & end offset */
+	int size;			/* size (0 = ROM) */
+	short dummy;			/* not used */
+	unsigned short mode_flags;	/* mode flags */
+#define SNDRV_SFNT_SAMPLE_8BITS		1	/* wave data is 8bits */
+#define SNDRV_SFNT_SAMPLE_UNSIGNED	2	/* wave data is unsigned */
+#define SNDRV_SFNT_SAMPLE_NO_BLANK	4	/* no blank loop is attached */
+#define SNDRV_SFNT_SAMPLE_SINGLESHOT	8	/* single-shot w/o loop */
+#define SNDRV_SFNT_SAMPLE_BIDIR_LOOP	16	/* bidirectional looping */
+#define SNDRV_SFNT_SAMPLE_STEREO_LEFT	32	/* stereo left sound */
+#define SNDRV_SFNT_SAMPLE_STEREO_RIGHT	64	/* stereo right sound */
+#define SNDRV_SFNT_SAMPLE_REVERSE_LOOP	128	/* reverse looping */
+	unsigned int truesize;		/* used memory size (set by driver) */
+};
+
+
+/*
+ * voice preset mapping (aliasing)
+ */
+
+struct soundfont_voice_map {
+	int map_bank, map_instr, map_key;	/* key = -1 means all keys */
+	int src_bank, src_instr, src_key;
+};
+
+
+/*
+ * ioctls for hwdep
+ */
+
+#define SNDRV_EMUX_HWDEP_NAME	"Emux WaveTable"
+
+#define SNDRV_EMUX_VERSION	((1 << 16) | (0 << 8) | 0)	/* 1.0.0 */
+
+struct snd_emux_misc_mode {
+	int port;	/* -1 = all */
+	int mode;
+	int value;
+	int value2;	/* reserved */
+};
+
+enum {
+	SNDRV_EMUX_IOCTL_VERSION = _IOR('H', 0x80, unsigned int),
+	SNDRV_EMUX_IOCTL_LOAD_PATCH = _IOWR('H', 0x81, struct soundfont_patch_info),
+	SNDRV_EMUX_IOCTL_RESET_SAMPLES = _IO('H', 0x82),
+	SNDRV_EMUX_IOCTL_REMOVE_LAST_SAMPLES = _IO('H', 0x83),
+	SNDRV_EMUX_IOCTL_MEM_AVAIL = _IOW('H', 0x84, int),
+	SNDRV_EMUX_IOCTL_MISC_MODE = _IOWR('H', 0x84, struct snd_emux_misc_mode),
+};
+
+#endif /* __SOUND_SFNT_INFO_H */
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/snd_wavefront.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/snd_wavefront.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/snd_wavefront.h	(revision 91)
@@ -0,0 +1,142 @@
+#ifndef __SND_WAVEFRONT_H__
+#define __SND_WAVEFRONT_H__
+
+#include "cs4231.h"
+#include "mpu401.h"
+#include "hwdep.h"
+#include "rawmidi.h"
+#include "wavefront.h"  /* generic OSS/ALSA/user-level wavefront header */
+
+/* MIDI interface */
+
+struct _snd_wavefront_midi;
+struct _snd_wavefront_card;
+struct _snd_wavefront;
+
+typedef struct _snd_wavefront_midi snd_wavefront_midi_t;
+typedef struct _snd_wavefront_card snd_wavefront_card_t;
+typedef struct _snd_wavefront snd_wavefront_t;
+
+typedef enum { internal_mpu = 0, external_mpu = 1 } snd_wavefront_mpu_id;
+
+struct _snd_wavefront_midi {
+        unsigned long            base;        /* I/O port address */
+	char                     isvirtual;   /* doing virtual MIDI stuff ? */
+	char			 istimer;     /* timer is used */
+        snd_wavefront_mpu_id     output_mpu;  /* most-recently-used */
+        snd_wavefront_mpu_id     input_mpu;   /* most-recently-used */
+        unsigned int             mode[2];     /* MPU401_MODE_XXX */
+	snd_rawmidi_substream_t	 *substream_output[2];
+	snd_rawmidi_substream_t	 *substream_input[2];
+	struct timer_list	 timer;
+        spinlock_t               open;
+        spinlock_t               virtual;     /* protects isvirtual */
+};
+
+#define	OUTPUT_READY	0x40
+#define	INPUT_AVAIL	0x80
+#define	MPU_ACK		0xFE
+#define	UART_MODE_ON	0x3F
+
+extern snd_rawmidi_ops_t snd_wavefront_midi_output;
+extern snd_rawmidi_ops_t snd_wavefront_midi_input;
+
+extern void   snd_wavefront_midi_enable_virtual (snd_wavefront_card_t *);
+extern void   snd_wavefront_midi_disable_virtual (snd_wavefront_card_t *);
+extern void   snd_wavefront_midi_interrupt (snd_wavefront_card_t *);
+extern int    snd_wavefront_midi_start (snd_wavefront_card_t *);
+
+struct _snd_wavefront {
+	unsigned long    irq;   /* "you were one, one of the few ..." */
+	unsigned long    base;  /* low i/o port address */
+	struct resource	 *res_base; /* i/o port resource allocation */
+
+#define mpu_data_port    base 
+#define mpu_command_port base + 1 /* write semantics */
+#define mpu_status_port  base + 1 /* read semantics */
+#define data_port        base + 2 
+#define status_port      base + 3 /* read semantics */
+#define control_port     base + 3 /* write semantics  */
+#define block_port       base + 4 /* 16 bit, writeonly */
+#define last_block_port  base + 6 /* 16 bit, writeonly */
+
+	/* FX ports. These are mapped through the ICS2115 to the YS225.
+	   The ICS2115 takes care of flipping the relevant pins on the
+	   YS225 so that access to each of these ports does the right
+	   thing. Note: these are NOT documented by Turtle Beach.
+	*/
+
+#define fx_status       base + 8 
+#define fx_op           base + 8 
+#define fx_lcr          base + 9 
+#define fx_dsp_addr     base + 0xa
+#define fx_dsp_page     base + 0xb 
+#define fx_dsp_lsb      base + 0xc 
+#define fx_dsp_msb      base + 0xd 
+#define fx_mod_addr     base + 0xe
+#define fx_mod_data     base + 0xf 
+
+	volatile int irq_ok;               /* set by interrupt handler */
+        volatile int irq_cnt;              /* ditto */
+	char debug;                        /* debugging flags */
+	int freemem;                       /* installed RAM, in bytes */ 
+
+	char fw_version[2];                /* major = [0], minor = [1] */
+	char hw_version[2];                /* major = [0], minor = [1] */
+	char israw;                        /* needs Motorola microcode */
+	char has_fx;                       /* has FX processor (Tropez+) */
+	char prog_status[WF_MAX_PROGRAM];  /* WF_SLOT_* */
+	char patch_status[WF_MAX_PATCH];   /* WF_SLOT_* */
+	char sample_status[WF_MAX_SAMPLE]; /* WF_ST_* | WF_SLOT_* */
+	int samples_used;                  /* how many */
+	char interrupts_are_midi;          /* h/w MPU interrupts enabled ? */
+	char rom_samples_rdonly;           /* can we write on ROM samples */
+	wait_queue_head_t interrupt_sleeper; 
+        snd_wavefront_midi_t midi;         /* ICS2115 MIDI interface */
+};
+
+struct _snd_wavefront_card {
+	snd_wavefront_t wavefront;
+#ifdef __ISAPNP__
+	struct isapnp_dev *wss;
+	struct isapnp_dev *ctrl;
+	struct isapnp_dev *mpu;
+	struct isapnp_dev *synth;
+#endif /* CONFIG_ISAPNP */
+};
+
+extern void snd_wavefront_internal_interrupt (snd_wavefront_card_t *card);
+extern int  snd_wavefront_interrupt_bits (int irq);
+extern int  snd_wavefront_detect_irq (snd_wavefront_t *dev) ;
+extern int  snd_wavefront_check_irq (snd_wavefront_t *dev, int irq);
+extern int  snd_wavefront_restart (snd_wavefront_t *dev);
+extern int  snd_wavefront_start (snd_wavefront_t *dev);
+extern int  snd_wavefront_detect (snd_wavefront_card_t *card);
+extern int  snd_wavefront_config_midi (snd_wavefront_t *dev) ;
+extern int  snd_wavefront_cmd (snd_wavefront_t *, int, unsigned char *,
+			       unsigned char *);
+
+extern int snd_wavefront_synth_ioctl   (snd_hwdep_t *, 
+					struct file *,
+					unsigned int cmd, 
+					unsigned long arg);
+extern int  snd_wavefront_synth_open    (snd_hwdep_t *, struct file *);
+extern int  snd_wavefront_synth_release (snd_hwdep_t *, struct file *);
+
+/* FX processor - see also yss225.[ch] */
+
+extern int  snd_wavefront_fx_start  (snd_wavefront_t *);
+extern int  snd_wavefront_fx_detect (snd_wavefront_t *);
+extern int  snd_wavefront_fx_ioctl  (snd_hwdep_t *, 
+				     struct file *,
+				     unsigned int cmd, 
+				     unsigned long arg);
+extern int snd_wavefront_fx_open    (snd_hwdep_t *, struct file *);
+extern int snd_wavefront_fx_release (snd_hwdep_t *, struct file *);
+
+/* prefix in all snd_printk() delivered messages */
+
+#define LOGNAME "WaveFront: "
+
+#endif  /* __SND_WAVEFRONT_H__ */
+
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/sndmagic.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/sndmagic.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/sndmagic.h	(revision 91)
@@ -0,0 +1,214 @@
+#ifndef __SOUND_SNDMAGIC_H
+#define __SOUND_SNDMAGIC_H
+
+/*
+ *  Magic allocation, deallocation, check
+ *  Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
+ *
+ *
+ *   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
+ *
+ */
+
+
+#ifdef CONFIG_SND_DEBUG_MEMORY
+
+void *_snd_magic_kcalloc(unsigned long magic, size_t size, int flags);
+void *_snd_magic_kmalloc(unsigned long magic, size_t size, int flags);
+
+/**
+ * snd_magic_kmalloc - allocate a record with a magic-prefix
+ * @type: the type to allocate a record (like xxx_t)
+ * @extra: the extra size to allocate in bytes
+ * @flags: the allocation condition (GFP_XXX)
+ *
+ * Allocates a record of the given type with the extra space and
+ * returns its pointer.  The allocated record has a secret magic-key
+ * to be checked via snd_magic_cast() for safe casts.
+ *
+ * The allocated pointer must be released via snd_magic_kfree().
+ *
+ * The "struct xxx" style cannot be used as the type argument
+ * because the magic-key constant is generated from the type-name
+ * string.
+ */
+#define snd_magic_kmalloc(type, extra, flags) \
+	(type *) _snd_magic_kmalloc(type##_magic, sizeof(type) + extra, flags)
+/**
+ * snd_magic_kcalloc - allocate a record with a magic-prefix and initialize
+ * @type: the type to allocate a record (like xxx_t)
+ * @extra: the extra size to allocate in bytes
+ * @flags: the allocation condition (GFP_XXX)
+ *
+ * Works like snd_magic_kmalloc() but this clears the area with zero
+ * automatically.
+ */
+#define snd_magic_kcalloc(type, extra, flags) \
+	(type *) _snd_magic_kcalloc(type##_magic, sizeof(type) + extra, flags)
+
+/**
+ * snd_magic_kfree - release the allocated area
+ * @ptr: the pointer allocated via snd_magic_kmalloc() or snd_magic_kcalloc()
+ *
+ * Releases the memory area allocated via snd_magic_kmalloc() or
+ * snd_magic_kcalloc() function.
+ */
+void snd_magic_kfree(void *ptr);
+
+static inline unsigned long _snd_magic_value(void *obj)
+{
+	return obj == NULL ? (unsigned long)-1 : *(((unsigned long *)obj) - 1);
+}
+
+static inline int _snd_magic_bad(void *obj, unsigned long magic)
+{
+	return _snd_magic_value(obj) != magic;
+}
+
+#define snd_magic_cast1(t, expr, cmd) snd_magic_cast(t, expr, cmd)
+
+/**
+ * snd_magic_cast - check and cast the magic-allocated pointer
+ * @type: the type of record to cast
+ * @ptr: the magic-allocated pointer
+ * @action...: the action to do if failed
+ *
+ * This macro provides a safe cast for the given type, which was
+ * allocated via snd_magic_kmalloc() or snd_magic_kcallc().
+ * If the pointer is invalid, i.e. the cast-type doesn't match,
+ * the action arguments are called with a debug message.
+ */
+#define snd_magic_cast(type, ptr, action...) \
+	(type *) ({\
+	void *__ptr = ptr;\
+	unsigned long __magic = _snd_magic_value(__ptr);\
+	if (__magic != type##_magic) {\
+		snd_printk("bad MAGIC (0x%lx)\n", __magic);\
+		action;\
+	}\
+	__ptr;\
+})
+
+#define snd_device_t_magic			0xa15a00ff
+#define snd_pcm_t_magic				0xa15a0101
+#define snd_pcm_file_t_magic			0xa15a0102
+#define snd_pcm_substream_t_magic		0xa15a0103
+#define snd_pcm_proc_private_t_magic		0xa15a0104
+#define snd_pcm_oss_file_t_magic		0xa15a0105
+#define snd_mixer_oss_t_magic			0xa15a0106
+// #define snd_pcm_sgbuf_t_magic			0xa15a0107
+
+#define snd_info_private_data_t_magic		0xa15a0201
+#define snd_info_entry_t_magic			0xa15a0202
+#define snd_ctl_file_t_magic			0xa15a0301
+#define snd_kcontrol_t_magic			0xa15a0302
+#define snd_rawmidi_t_magic			0xa15a0401
+#define snd_rawmidi_file_t_magic		0xa15a0402
+#define snd_virmidi_t_magic			0xa15a0403
+#define snd_virmidi_dev_t_magic			0xa15a0404
+#define snd_timer_t_magic			0xa15a0501
+#define snd_timer_user_t_magic			0xa15a0502
+#define snd_hwdep_t_magic			0xa15a0601
+#define snd_seq_device_t_magic			0xa15a0701
+
+#define es18xx_t_magic				0xa15a1101
+#define trident_t_magic				0xa15a1201
+#define es1938_t_magic				0xa15a1301
+#define cs46xx_t_magic				0xa15a1401
+#define cs46xx_pcm_t_magic			0xa15a1402
+#define ensoniq_t_magic				0xa15a1501
+#define sonicvibes_t_magic			0xa15a1601
+#define mpu401_t_magic				0xa15a1701
+#define fm801_t_magic				0xa15a1801
+#define ac97_t_magic				0xa15a1901
+#define ac97_bus_t_magic			0xa15a1902
+#define ak4531_t_magic				0xa15a1a01
+#define snd_uart16550_t_magic			0xa15a1b01
+#define emu10k1_t_magic				0xa15a1c01
+#define emu10k1_pcm_t_magic			0xa15a1c02
+#define emu10k1_midi_t_magic			0xa15a1c03
+#define snd_gus_card_t_magic			0xa15a1d01
+#define gus_pcm_private_t_magic			0xa15a1d02
+#define gus_proc_private_t_magic		0xa15a1d03
+#define tea6330t_t_magic			0xa15a1e01
+#define ad1848_t_magic				0xa15a1f01
+#define cs4231_t_magic				0xa15a2001
+#define es1688_t_magic				0xa15a2101
+#define opti93x_t_magic				0xa15a2201
+#define emu8000_t_magic				0xa15a2301
+#define emu8000_proc_private_t_magic		0xa15a2302
+#define snd_emux_t_magic			0xa15a2303
+#define snd_emux_port_t_magic			0xa15a2304
+#define sb_t_magic				0xa15a2401
+#define snd_sb_csp_t_magic			0xa15a2402
+#define snd_card_dummy_t_magic			0xa15a2501
+#define snd_card_dummy_pcm_t_magic		0xa15a2502
+#define opl3_t_magic				0xa15a2601
+#define opl4_t_magic				0xa15a2602
+#define snd_seq_dummy_port_t_magic		0xa15a2701
+#define ice1712_t_magic				0xa15a2801
+#define ad1816a_t_magic				0xa15a2901
+#define intel8x0_t_magic			0xa15a2a01
+#define es1968_t_magic				0xa15a2b01
+#define esschan_t_magic				0xa15a2b02
+#define via82xx_t_magic				0xa15a2c01
+#define pdplus_t_magic				0xa15a2d01
+#define cmipci_t_magic				0xa15a2e01
+#define ymfpci_t_magic				0xa15a2f01
+#define ymfpci_pcm_t_magic			0xa15a2f02
+#define cs4281_t_magic				0xa15a3001
+#define snd_i2c_bus_t_magic			0xa15a3101
+#define snd_i2c_device_t_magic			0xa15a3102
+#define cs8427_t_magic				0xa15a3111
+#define m3_t_magic				0xa15a3201
+#define m3_dma_t_magic				0xa15a3202
+#define nm256_t_magic				0xa15a3301
+#define nm256_dma_t_magic			0xa15a3302
+#define sam9407_t_magic				0xa15a3401
+#define pmac_t_magic				0xa15a3501
+#define ali_t_magic				0xa15a3601
+#define mtpav_t_magic				0xa15a3701
+#define mtpav_port_t_magic			0xa15a3702
+#define korg1212_t_magic			0xa15a3800
+#define opl3sa2_t_magic				0xa15a3900
+#define serialmidi_t_magic			0xa15a3a00
+#define sa11xx_uda1341_t_magic			0xa15a3b00
+#define uda1341_t_magic                         0xa15a3c00
+#define l3_client_t_magic                       0xa15a3d00
+#define snd_usb_audio_t_magic			0xa15a3e01
+#define usb_mixer_elem_info_t_magic		0xa15a3e02
+#define snd_usb_stream_t_magic			0xa15a3e03
+#define snd_usb_midi_t_magic			0xa15a3f01
+#define snd_usb_midi_out_endpoint_t_magic	0xa15a3f02
+#define snd_usb_midi_in_endpoint_t_magic	0xa15a3f03
+#define ak4117_t_magic				0xa15a4000
+#define psic_t_magic				0xa15a4100
+#define vx_core_t_magic				0xa15a4110
+#define vx_pipe_t_magic				0xa15a4112
+#define azf3328_t_magic				0xa15a4200
+#define bt87x_t_magic                           0xa15a4400
+#define pdacf_t_magic                           0xa15a4500
+#else
+
+#define snd_magic_kcalloc(type, extra, flags) (type *) snd_kcalloc(sizeof(type) + extra, flags)
+#define snd_magic_kmalloc(type, extra, flags) (type *) kmalloc(sizeof(type) + extra, flags)
+#define snd_magic_cast(type, ptr, retval) (type *) ptr
+#define snd_magic_cast1(type, ptr, retval) snd_magic_cast(type, ptr, retval)
+#define snd_magic_kfree kfree
+
+#endif
+
+#endif /* __SOUND_SNDMAGIC_H */
+
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/soundfont.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/soundfont.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/soundfont.h	(revision 91)
@@ -0,0 +1,129 @@
+#ifndef __SOUND_SOUNDFONT_H
+#define __SOUND_SOUNDFONT_H
+
+/*
+ *  Soundfont defines and definitions.
+ *
+ *  Copyright (C) 1999 Steve Ratcliffe
+ *  Copyright (c) 1999-2000 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 "sfnt_info.h"
+#include "util_mem.h"
+
+#define SF_MAX_INSTRUMENTS	128	/* maximum instrument number */
+#define SF_MAX_PRESETS  256	/* drums are mapped from 128 to 256 */
+#define SF_IS_DRUM_BANK(z) ((z) == 128)
+
+struct snd_sf_zone {
+	struct snd_sf_zone *next;	/* Link to next */
+	unsigned char bank;		/* Midi bank for this zone */
+	unsigned char instr;		/* Midi program for this zone */
+	unsigned char mapped;		/* True if mapped to something else */
+
+	struct soundfont_voice_info v;	/* All the soundfont parameters */
+	int counter;
+	struct snd_sf_sample *sample;	/* Link to sample */
+
+	/* The following deals with preset numbers (programs) */
+	struct snd_sf_zone *next_instr;	/* Next zone of this instrument */
+	struct snd_sf_zone *next_zone;	/* Next zone in play list */
+};
+
+struct snd_sf_sample {
+	struct soundfont_sample_info v;
+	int counter;
+	struct snd_util_memblk *block;	/* allocated data block */
+	struct snd_sf_sample *next;
+};
+
+/*
+ * This represents all the information relating to a soundfont.
+ */
+struct snd_soundfont {
+	struct snd_soundfont *next;	/* Link to next */
+	/*struct snd_soundfont *prev;*/	/* Link to previous */
+	short  id;		/* file id */
+	short  type;		/* font type */
+	unsigned char name[SNDRV_SFNT_PATCH_NAME_LEN];	/* identifier */
+	struct snd_sf_zone *zones; /* Font information */
+	struct snd_sf_sample *samples; /* The sample headers */
+};
+
+/*
+ * Type of the sample access callback
+ */
+struct snd_sf_callback {
+	void *private_data;
+	int (*sample_new)(void *private_data, struct snd_sf_sample *sp,
+			  struct snd_util_memhdr *hdr,
+			  const void __user *buf, long count);
+	int (*sample_free)(void *private_data, struct snd_sf_sample *sp,
+			   struct snd_util_memhdr *hdr);
+	void (*sample_reset)(void *private);
+};
+
+/*
+ * List of soundfonts.
+ */
+struct snd_sf_list {
+	struct snd_soundfont *currsf; /* The currently open soundfont */
+	int open_client;	/* client pointer for lock */
+	int mem_used;		/* used memory size */
+	struct snd_sf_zone *presets[SF_MAX_PRESETS];
+	struct snd_soundfont *fonts; /* The list of soundfonts */
+	int fonts_size;	/* number of fonts allocated */
+	int zone_counter;	/* last allocated time for zone */
+	int sample_counter;	/* last allocated time for sample */
+	int zone_locked;	/* locked time for zone */
+	int sample_locked;	/* locked time for sample */
+	struct snd_sf_callback callback;	/* callback functions */
+	int presets_locked;
+	struct semaphore presets_mutex;
+	spinlock_t lock;
+	struct snd_util_memhdr *memhdr;
+};
+
+/* Prototypes for soundfont.c */
+int snd_soundfont_load(struct snd_sf_list *sflist, const void __user *data,
+		       long count, int client);
+int snd_soundfont_load_guspatch(struct snd_sf_list *sflist, const char __user *data,
+				long count, int client);
+int snd_soundfont_close_check(struct snd_sf_list *sflist, int client);
+
+struct snd_sf_list *snd_sf_new(struct snd_sf_callback *callback,
+			       struct snd_util_memhdr *hdr);
+void snd_sf_free(struct snd_sf_list *sflist);
+
+int snd_soundfont_remove_samples(struct snd_sf_list *sflist);
+int snd_soundfont_remove_unlocked(struct snd_sf_list *sflist);
+
+int snd_soundfont_search_zone(struct snd_sf_list *sflist, int *notep, int vel,
+			      int preset, int bank,
+			      int def_preset, int def_bank,
+			      struct snd_sf_zone **table, int max_layers);
+
+/* Parameter conversions */
+int snd_sf_calc_parm_hold(int msec);
+int snd_sf_calc_parm_attack(int msec);
+int snd_sf_calc_parm_decay(int msec);
+#define snd_sf_calc_parm_delay(msec) (0x8000 - (msec) * 1000 / 725);
+extern int snd_sf_vol_table[128];
+int snd_sf_linear_to_log(unsigned int amount, int offset, int ratio);
+
+
+#endif /* __SOUND_SOUNDFONT_H */
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/soundmem.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/soundmem.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/soundmem.h	(revision 91)
@@ -0,0 +1,8 @@
+/*
+ *  Onboard memory management
+ */
+
+struct SNDRV_STRU_BANK_INFO {
+	unsigned int address;
+	unsigned int size;
+};
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/tea6330t.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/tea6330t.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/tea6330t.h	(revision 91)
@@ -0,0 +1,42 @@
+#ifndef __TEA6330T_H
+#define __TEA6330T_H
+
+/*
+ *  Routines for control of TEA6330T circuit.
+ *  Sound fader control circuit for car radios.
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ */
+
+#include "control.h"
+#include "i2c.h"		/* generic i2c support */
+
+typedef struct {
+	snd_i2c_device_t *device;
+	int equalizer;
+	int fader;
+	unsigned char regs[8];
+	unsigned char mleft, mright;
+	unsigned char bass, treble;
+	unsigned char max_bass, max_treble;
+	spinlock_t reg_lock;
+} tea6330t_t;
+
+extern int snd_tea6330t_detect(snd_i2c_bus_t *bus, int equalizer);
+extern int snd_tea6330t_update_mixer(snd_card_t * card, snd_i2c_bus_t * bus, int equalizer, int fader);
+
+#endif				/* __TEA6330T_H */
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/timer.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/timer.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/timer.h	(revision 91)
@@ -0,0 +1,144 @@
+#ifndef __SOUND_TIMER_H
+#define __SOUND_TIMER_H
+
+/*
+ *  Timer abstract layer
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>,
+ *		     Abramo Bagnara <abramo@alsa-project.org>
+ *
+ *
+ *   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/asound.h>
+#include <linux/interrupt.h>
+
+#define snd_timer_chip(timer) ((timer)->private_data)
+
+#define SNDRV_TIMER_DEVICES	16
+
+#define SNDRV_TIMER_DEV_FLG_PCM	0x10000000
+
+#define SNDRV_TIMER_HW_AUTO	0x00000001	/* auto trigger is supported */
+#define SNDRV_TIMER_HW_STOP	0x00000002	/* call stop before start */
+#define SNDRV_TIMER_HW_SLAVE	0x00000004	/* only slave timer (variable resolution) */
+#define SNDRV_TIMER_HW_FIRST	0x00000008	/* first tick can be incomplete */
+#define SNDRV_TIMER_HW_TASKLET	0x00000010	/* timer is called from tasklet */
+
+#define SNDRV_TIMER_IFLG_SLAVE	  0x00000001
+#define SNDRV_TIMER_IFLG_RUNNING  0x00000002
+#define SNDRV_TIMER_IFLG_START	  0x00000004
+#define SNDRV_TIMER_IFLG_AUTO	  0x00000008	/* auto restart */
+#define SNDRV_TIMER_IFLG_FAST	  0x00000010	/* fast callback (do not use tasklet) */
+#define SNDRV_TIMER_IFLG_CALLBACK 0x00000020	/* timer callback is active */
+#define SNDRV_TIMER_IFLG_EXCLUSIVE 0x00000040	/* exclusive owner - no more instances */
+#define SNDRV_TIMER_IFLG_EARLY_EVENT 0x00000080	/* write early event to the poll queue */
+
+#define SNDRV_TIMER_FLG_CHANGE	0x00000001
+#define SNDRV_TIMER_FLG_RESCHED	0x00000002	/* need reschedule */
+
+struct snd_timer;
+
+struct snd_timer_hardware {
+	/* -- must be filled with low-level driver */
+	unsigned int flags;		/* various flags */
+	unsigned long resolution;	/* average timer resolution for one tick in nsec */
+	unsigned long resolution_min;	/* minimal resolution */
+	unsigned long resolution_max;	/* maximal resolution */
+	unsigned long ticks;		/* max timer ticks per interrupt */
+	/* -- low-level functions -- */
+	int (*open) (struct snd_timer * timer);
+	int (*close) (struct snd_timer * timer);
+	unsigned long (*c_resolution) (struct snd_timer * timer);
+	int (*start) (struct snd_timer * timer);
+	int (*stop) (struct snd_timer * timer);
+	int (*set_period) (struct snd_timer * timer, unsigned long period_num, unsigned long period_den);
+	int (*precise_resolution) (struct snd_timer * timer, unsigned long *num, unsigned long *den);
+};
+
+struct snd_timer {
+	int tmr_class;
+	struct snd_card *card;
+	struct module *module;
+	int tmr_device;
+	int tmr_subdevice;
+	char id[64];
+	char name[80];
+	unsigned int flags;
+	int running;			/* running instances */
+	unsigned long sticks;		/* schedule ticks */
+	void *private_data;
+	void (*private_free) (struct snd_timer *timer);
+	struct snd_timer_hardware hw;
+	spinlock_t lock;
+	struct list_head device_list;
+	struct list_head open_list_head;
+	struct list_head active_list_head;
+	struct list_head ack_list_head;
+	struct list_head sack_list_head; /* slow ack list head */
+	struct tasklet_struct task_queue;
+};
+
+struct snd_timer_instance {
+	struct snd_timer *timer;
+	char *owner;
+	unsigned int flags;
+	void *private_data;
+	void (*private_free) (struct snd_timer_instance *ti);
+	void (*callback) (struct snd_timer_instance *timeri,
+			  unsigned long ticks, unsigned long resolution);
+	void (*ccallback) (struct snd_timer_instance * timeri,
+			   int event,
+			   struct timespec * tstamp,
+			   unsigned long resolution);
+	void *callback_data;
+	unsigned long ticks;		/* auto-load ticks when expired */
+	unsigned long cticks;		/* current ticks */
+	unsigned long pticks;		/* accumulated ticks for callback */
+	unsigned long resolution;	/* current resolution for tasklet */
+	unsigned long lost;		/* lost ticks */
+	int slave_class;
+	unsigned int slave_id;
+	struct list_head open_list;
+	struct list_head active_list;
+	struct list_head ack_list;
+	struct list_head slave_list_head;
+	struct list_head slave_active_head;
+	struct snd_timer_instance *master;
+};
+
+/*
+ *  Registering
+ */
+
+int snd_timer_new(struct snd_card *card, char *id, struct snd_timer_id *tid, struct snd_timer **rtimer);
+void snd_timer_notify(struct snd_timer *timer, int event, struct timespec *tstamp);
+int snd_timer_global_new(char *id, int device, struct snd_timer **rtimer);
+int snd_timer_global_free(struct snd_timer *timer);
+int snd_timer_global_register(struct snd_timer *timer);
+int snd_timer_global_unregister(struct snd_timer *timer);
+
+int snd_timer_open(struct snd_timer_instance **ti, char *owner, struct snd_timer_id *tid, unsigned int slave_id);
+int snd_timer_close(struct snd_timer_instance *timeri);
+unsigned long snd_timer_resolution(struct snd_timer_instance *timeri);
+int snd_timer_start(struct snd_timer_instance *timeri, unsigned int ticks);
+int snd_timer_stop(struct snd_timer_instance *timeri);
+int snd_timer_continue(struct snd_timer_instance *timeri);
+int snd_timer_pause(struct snd_timer_instance *timeri);
+
+void snd_timer_interrupt(struct snd_timer *timer, unsigned long ticks_left);
+
+#endif /* __SOUND_TIMER_H */
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/tlv.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/tlv.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/tlv.h	(revision 91)
@@ -0,0 +1,43 @@
+#ifndef __SOUND_TLV_H
+#define __SOUND_TLV_H
+
+/*
+ *  Advanced Linux Sound Architecture - ALSA - Driver
+ *  Copyright (c) 2006 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
+ *
+ */
+
+/*
+ * TLV structure is right behind the struct snd_ctl_tlv:
+ *   unsigned int type  	- see SNDRV_CTL_TLVT_*
+ *   unsigned int length
+ *   .... data aligned to sizeof(unsigned int), use
+ *        block_length = (length + (sizeof(unsigned int) - 1)) &
+ *                       ~(sizeof(unsigned int) - 1)) ....
+ */
+
+#define SNDRV_CTL_TLVT_CONTAINER 0	/* one level down - group of TLVs */
+#define SNDRV_CTL_TLVT_DB_SCALE	1       /* dB scale */
+
+#define DECLARE_TLV_DB_SCALE(name, min, step, mute) \
+    unsigned int name[] = { \
+    SNDRV_CTL_TLVT_DB_SCALE, 2 * sizeof(unsigned int), \
+    (min), ((step) & 0xffff) | ((mute) ? 0x10000 : 0) \
+    }
+
+#endif /* __SOUND_TLV_H */
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/trident.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/trident.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/trident.h	(revision 91)
@@ -0,0 +1,477 @@
+#ifndef __SOUND_TRIDENT_H
+#define __SOUND_TRIDENT_H
+
+/*
+ *  audio@tridentmicro.com
+ *  Fri Feb 19 15:55:28 MST 1999
+ *  Definitions for Trident 4DWave DX/NX chips
+ *
+ *
+ *   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 "pcm.h"
+#include "mpu401.h"
+#include "ac97_codec.h"
+#include "seq_midi_emul.h"
+#include "seq_device.h"
+#include "util_mem.h"
+//#include "ainstr_iw.h"
+//#include "ainstr_gf1.h"
+#include "ainstr_simple.h"
+
+#ifndef PCI_DEVICE_ID_SI_7018
+#define PCI_DEVICE_ID_SI_7018           0x7018
+#endif
+#ifndef PCI_DEVICE_ID_TRIDENT_4DWAVE_DX
+#define PCI_DEVICE_ID_TRIDENT_4DWAVE_DX 0x2000
+#endif
+#ifndef PCI_DEVICE_ID_TRIDENT_4DWAVE_NX
+#define PCI_DEVICE_ID_TRIDENT_4DWAVE_NX 0x2001
+#endif
+
+#define TRIDENT_DEVICE_ID_DX		((PCI_VENDOR_ID_TRIDENT<<16)|PCI_DEVICE_ID_TRIDENT_4DWAVE_DX)
+#define TRIDENT_DEVICE_ID_NX		((PCI_VENDOR_ID_TRIDENT<<16)|PCI_DEVICE_ID_TRIDENT_4DWAVE_NX)
+#define TRIDENT_DEVICE_ID_SI7018	((PCI_VENDOR_ID_SI<<16)|PCI_DEVICE_ID_SI_7018)
+
+#define SNDRV_SEQ_DEV_ID_TRIDENT			"trident-synth"
+
+#define SNDRV_TRIDENT_VOICE_TYPE_PCM		0
+#define SNDRV_TRIDENT_VOICE_TYPE_SYNTH		1
+#define SNDRV_TRIDENT_VOICE_TYPE_MIDI		2
+
+#define SNDRV_TRIDENT_VFLG_RUNNING		(1<<0)
+
+/* TLB code constants */
+#define SNDRV_TRIDENT_PAGE_SIZE			4096
+#define SNDRV_TRIDENT_PAGE_SHIFT			12
+#define SNDRV_TRIDENT_PAGE_MASK			((1<<SNDRV_TRIDENT_PAGE_SHIFT)-1)
+#define SNDRV_TRIDENT_MAX_PAGES			4096
+
+/*
+ * Direct registers
+ */
+
+#define TRID_REG(trident, x) ((trident)->port + (x))
+
+#define ID_4DWAVE_DX        0x2000
+#define ID_4DWAVE_NX        0x2001
+
+/* Bank definitions */
+
+#define T4D_BANK_A	0
+#define T4D_BANK_B	1
+#define T4D_NUM_BANKS	2
+
+/* Register definitions */
+
+/* Global registers */
+
+enum global_control_bits {
+	CHANNEL_IDX	= 0x0000003f,
+	OVERRUN_IE	= 0x00000400,	/* interrupt enable: capture overrun */
+	UNDERRUN_IE	= 0x00000800,	/* interrupt enable: playback underrun */
+	ENDLP_IE	= 0x00001000,	/* interrupt enable: end of buffer */
+	MIDLP_IE	= 0x00002000,	/* interrupt enable: middle buffer */
+	ETOG_IE		= 0x00004000,	/* interrupt enable: envelope toggling */
+	EDROP_IE	= 0x00008000,	/* interrupt enable: envelope drop */
+	BANK_B_EN	= 0x00010000,	/* SiS: enable bank B (64 channels) */
+	PCMIN_B_MIX	= 0x00020000,	/* SiS: PCM IN B mixing enable */
+	I2S_OUT_ASSIGN	= 0x00040000,	/* SiS: I2S Out contains surround PCM */
+	SPDIF_OUT_ASSIGN= 0x00080000,	/* SiS: 0=S/PDIF L/R | 1=PCM Out FIFO */
+	MAIN_OUT_ASSIGN = 0x00100000,	/* SiS: 0=PCM Out FIFO | 1=MMC Out buffer */
+};
+
+enum miscint_bits {
+	PB_UNDERRUN_IRQ = 0x00000001, REC_OVERRUN_IRQ = 0x00000002,
+	SB_IRQ		= 0x00000004, MPU401_IRQ      = 0x00000008,
+	OPL3_IRQ        = 0x00000010, ADDRESS_IRQ     = 0x00000020,
+	ENVELOPE_IRQ    = 0x00000040, PB_UNDERRUN     = 0x00000100,
+	REC_OVERRUN	= 0x00000200, MIXER_UNDERFLOW = 0x00000400,
+	MIXER_OVERFLOW  = 0x00000800, NX_SB_IRQ_DISABLE = 0x00001000,
+        ST_TARGET_REACHED = 0x00008000,
+	PB_24K_MODE     = 0x00010000, ST_IRQ_EN       = 0x00800000,
+	ACGPIO_IRQ	= 0x01000000
+};
+
+/* T2 legacy dma control registers. */
+#define LEGACY_DMAR0                0x00  // ADR0
+#define LEGACY_DMAR4                0x04  // CNT0
+#define LEGACY_DMAR6		    0x06  // CNT0 - High bits
+#define LEGACY_DMAR11               0x0b  // MOD 
+#define LEGACY_DMAR15               0x0f  // MMR 
+
+#define T4D_START_A		     0x80
+#define T4D_STOP_A		     0x84
+#define T4D_DLY_A		     0x88
+#define T4D_SIGN_CSO_A		     0x8c
+#define T4D_CSPF_A		     0x90
+#define T4D_CSPF_B		     0xbc
+#define T4D_CEBC_A		     0x94
+#define T4D_AINT_A		     0x98
+#define T4D_AINTEN_A		     0x9c
+#define T4D_LFO_GC_CIR               0xa0
+#define T4D_MUSICVOL_WAVEVOL         0xa8
+#define T4D_SBDELTA_DELTA_R          0xac
+#define T4D_MISCINT                  0xb0
+#define T4D_START_B                  0xb4
+#define T4D_STOP_B                   0xb8
+#define T4D_SBBL_SBCL                0xc0
+#define T4D_SBCTRL_SBE2R_SBDD        0xc4
+#define T4D_STIMER		     0xc8
+#define T4D_AINT_B                   0xd8
+#define T4D_AINTEN_B                 0xdc
+#define T4D_RCI                      0x70
+
+/* MPU-401 UART */
+#define T4D_MPU401_BASE             0x20
+#define T4D_MPUR0                   0x20
+#define T4D_MPUR1                   0x21
+#define T4D_MPUR2                   0x22
+#define T4D_MPUR3                   0x23
+
+/* S/PDIF Registers */
+#define NX_SPCTRL_SPCSO             0x24
+#define NX_SPLBA                    0x28
+#define NX_SPESO                    0x2c
+#define NX_SPCSTATUS                0x64
+
+/* Joystick */
+#define GAMEPORT_GCR                0x30
+#define GAMEPORT_MODE_ADC           0x80
+#define GAMEPORT_LEGACY             0x31
+#define GAMEPORT_AXES               0x34
+
+/* NX Specific Registers */
+#define NX_TLBC                     0x6c
+
+/* Channel Registers */
+
+#define CH_START		    0xe0
+
+#define CH_DX_CSO_ALPHA_FMS         0xe0
+#define CH_DX_ESO_DELTA             0xe8
+#define CH_DX_FMC_RVOL_CVOL         0xec
+
+#define CH_NX_DELTA_CSO             0xe0
+#define CH_NX_DELTA_ESO             0xe8
+#define CH_NX_ALPHA_FMS_FMC_RVOL_CVOL 0xec
+
+#define CH_LBA                      0xe4
+#define CH_GVSEL_PAN_VOL_CTRL_EC    0xf0
+#define CH_EBUF1                    0xf4
+#define CH_EBUF2                    0xf8
+
+/* AC-97 Registers */
+
+#define DX_ACR0_AC97_W              0x40
+#define DX_ACR1_AC97_R              0x44
+#define DX_ACR2_AC97_COM_STAT       0x48
+
+#define NX_ACR0_AC97_COM_STAT       0x40
+#define NX_ACR1_AC97_W              0x44
+#define NX_ACR2_AC97_R_PRIMARY      0x48
+#define NX_ACR3_AC97_R_SECONDARY    0x4c
+
+#define SI_AC97_WRITE		    0x40
+#define SI_AC97_READ		    0x44
+#define SI_SERIAL_INTF_CTRL	    0x48
+#define SI_AC97_GPIO		    0x4c
+#define SI_ASR0			    0x50
+#define SI_SPDIF_CS		    0x70
+#define SI_GPIO			    0x7c
+
+enum trident_nx_ac97_bits {
+	/* ACR1-3 */
+	NX_AC97_BUSY_WRITE 	= 0x0800,
+	NX_AC97_BUSY_READ	= 0x0800,
+	NX_AC97_BUSY_DATA 	= 0x0400,
+	NX_AC97_WRITE_SECONDARY = 0x0100,
+	/* ACR0 */
+	NX_AC97_SECONDARY_READY = 0x0040,
+	NX_AC97_SECONDARY_RECORD = 0x0020,
+	NX_AC97_SURROUND_OUTPUT = 0x0010,
+	NX_AC97_PRIMARY_READY	= 0x0008,
+	NX_AC97_PRIMARY_RECORD	= 0x0004,
+	NX_AC97_PCM_OUTPUT	= 0x0002,
+	NX_AC97_WARM_RESET	= 0x0001
+};
+
+enum trident_dx_ac97_bits {
+	DX_AC97_BUSY_WRITE	= 0x8000,
+	DX_AC97_BUSY_READ	= 0x8000,
+	DX_AC97_READY		= 0x0010,
+	DX_AC97_RECORD		= 0x0008,
+	DX_AC97_PLAYBACK	= 0x0002
+};
+
+enum sis7018_ac97_bits {
+	SI_AC97_BUSY_WRITE =	0x00008000,
+	SI_AC97_AUDIO_BUSY =	0x00004000,
+	SI_AC97_MODEM_BUSY =	0x00002000,
+	SI_AC97_BUSY_READ =	0x00008000,
+	SI_AC97_SECONDARY =	0x00000080,
+};
+
+enum serial_intf_ctrl_bits {
+	WARM_RESET	= 0x00000001,
+	COLD_RESET	= 0x00000002,
+	I2S_CLOCK	= 0x00000004,
+	PCM_SEC_AC97	= 0x00000008,
+	AC97_DBL_RATE	= 0x00000010,
+	SPDIF_EN	= 0x00000020,
+	I2S_OUTPUT_EN	= 0x00000040,
+	I2S_INPUT_EN	= 0x00000080,
+	PCMIN		= 0x00000100,
+	LINE1IN		= 0x00000200,
+	MICIN		= 0x00000400,
+	LINE2IN		= 0x00000800,
+	HEAD_SET_IN	= 0x00001000,
+	GPIOIN		= 0x00002000,
+	/* 7018 spec says id = 01 but the demo board routed to 10
+	   SECONDARY_ID= 0x00004000, */
+	SECONDARY_ID	= 0x00004000,
+	PCMOUT		= 0x00010000,
+	SURROUT		= 0x00020000,
+	CENTEROUT	= 0x00040000,
+	LFEOUT		= 0x00080000,
+	LINE1OUT	= 0x00100000,
+	LINE2OUT	= 0x00200000,
+	GPIOOUT		= 0x00400000,
+	SI_AC97_PRIMARY_READY = 0x01000000,
+	SI_AC97_SECONDARY_READY = 0x02000000,
+	SI_AC97_POWERDOWN = 0x04000000,
+};
+                                                                                                                                   
+/* PCM defaults */
+
+#define T4D_DEFAULT_PCM_VOL	10	/* 0 - 255 */
+#define T4D_DEFAULT_PCM_PAN	0	/* 0 - 127 */
+#define T4D_DEFAULT_PCM_RVOL	127	/* 0 - 127 */
+#define T4D_DEFAULT_PCM_CVOL	127	/* 0 - 127 */
+
+struct snd_trident;
+struct snd_trident_voice;
+struct snd_trident_pcm_mixer;
+
+struct snd_trident_sample_ops {
+	void (*sample_start)(struct snd_trident *gus, struct snd_trident_voice *voice, snd_seq_position_t position);
+	void (*sample_stop)(struct snd_trident *gus, struct snd_trident_voice *voice, int mode);
+	void (*sample_freq)(struct snd_trident *gus, struct snd_trident_voice *voice, snd_seq_frequency_t freq);
+	void (*sample_volume)(struct snd_trident *gus, struct snd_trident_voice *voice, struct snd_seq_ev_volume *volume);
+	void (*sample_loop)(struct snd_trident *card, struct snd_trident_voice *voice, struct snd_seq_ev_loop *loop);
+	void (*sample_pos)(struct snd_trident *card, struct snd_trident_voice *voice, snd_seq_position_t position);
+	void (*sample_private1)(struct snd_trident *card, struct snd_trident_voice *voice, unsigned char *data);
+};
+
+struct snd_trident_port {
+	struct snd_midi_channel_set * chset;
+	struct snd_trident * trident;
+	int mode;		/* operation mode */
+	int client;		/* sequencer client number */
+	int port;		/* sequencer port number */
+	unsigned int midi_has_voices: 1;
+};
+
+struct snd_trident_memblk_arg {
+	short first_page, last_page;
+};
+
+struct snd_trident_tlb {
+	unsigned int * entries;		/* 16k-aligned TLB table */
+	dma_addr_t entries_dmaaddr;	/* 16k-aligned PCI address to TLB table */
+	unsigned long * shadow_entries;	/* shadow entries with virtual addresses */
+	struct snd_dma_buffer buffer;
+	struct snd_util_memhdr * memhdr;	/* page allocation list */
+	struct snd_dma_buffer silent_page;
+};
+
+struct snd_trident_voice {
+	unsigned int number;
+	unsigned int use: 1,
+	    pcm: 1,
+	    synth:1,
+	    midi: 1;
+	unsigned int flags;
+	unsigned char client;
+	unsigned char port;
+	unsigned char index;
+
+	struct snd_seq_instr instr;
+	struct snd_trident_sample_ops *sample_ops;
+
+	/* channel parameters */
+	unsigned int CSO;		/* 24 bits (16 on DX) */
+	unsigned int ESO;		/* 24 bits (16 on DX) */
+	unsigned int LBA;		/* 30 bits */
+	unsigned short EC;		/* 12 bits */
+	unsigned short Alpha;		/* 12 bits */
+	unsigned short Delta;		/* 16 bits */
+	unsigned short Attribute;	/* 16 bits - SiS 7018 */
+	unsigned short Vol;		/* 12 bits (6.6) */
+	unsigned char Pan;		/* 7 bits (1.4.2) */
+	unsigned char GVSel;		/* 1 bit */
+	unsigned char RVol;		/* 7 bits (5.2) */
+	unsigned char CVol;		/* 7 bits (5.2) */
+	unsigned char FMC;		/* 2 bits */
+	unsigned char CTRL;		/* 4 bits */
+	unsigned char FMS;		/* 4 bits */
+	unsigned char LFO;		/* 8 bits */
+
+	unsigned int negCSO;	/* nonzero - use negative CSO */
+
+	struct snd_util_memblk *memblk;	/* memory block if TLB enabled */
+
+	/* PCM data */
+
+	struct snd_trident *trident;
+	struct snd_pcm_substream *substream;
+	struct snd_trident_voice *extra;	/* extra PCM voice (acts as interrupt generator) */
+	unsigned int running: 1,
+            capture: 1,
+            spdif: 1,
+            foldback: 1,
+            isync: 1,
+            isync2: 1,
+            isync3: 1;
+	int foldback_chan;		/* foldback subdevice number */
+	unsigned int stimer;		/* global sample timer (to detect spurious interrupts) */
+	unsigned int spurious_threshold; /* spurious threshold */
+	unsigned int isync_mark;
+	unsigned int isync_max;
+	unsigned int isync_ESO;
+
+	/* --- */
+
+	void *private_data;
+	void (*private_free)(struct snd_trident_voice *voice);
+};
+
+struct snd_4dwave {
+	int seq_client;
+
+	struct snd_trident_port seq_ports[4];
+	struct snd_simple_ops simple_ops;
+	struct snd_seq_kinstr_list *ilist;
+
+	struct snd_trident_voice voices[64];	
+
+	int ChanSynthCount;		/* number of allocated synth channels */
+	int max_size;			/* maximum synth memory size in bytes */
+	int current_size;		/* current allocated synth mem in bytes */
+};
+
+struct snd_trident_pcm_mixer {
+	struct snd_trident_voice *voice;	/* active voice */
+	unsigned short vol;		/* front volume */
+	unsigned char pan;		/* pan control */
+	unsigned char rvol;		/* rear volume */
+	unsigned char cvol;		/* center volume */
+	unsigned char pad;
+};
+
+struct snd_trident {
+	int irq;
+
+	unsigned int device;	/* device ID */
+
+        unsigned char  bDMAStart;
+
+	unsigned long port;
+	unsigned long midi_port;
+
+	unsigned int spurious_irq_count;
+	unsigned int spurious_irq_max_delta;
+
+        struct snd_trident_tlb tlb;	/* TLB entries for NX cards */
+
+	unsigned char spdif_ctrl;
+	unsigned char spdif_pcm_ctrl;
+	unsigned int spdif_bits;
+	unsigned int spdif_pcm_bits;
+	struct snd_kcontrol *spdif_pcm_ctl;	/* S/PDIF settings */
+	unsigned int ac97_ctrl;
+        
+        unsigned int ChanMap[2];	/* allocation map for hardware channels */
+        
+        int ChanPCM;			/* max number of PCM channels */
+	int ChanPCMcnt;			/* actual number of PCM channels */
+
+	unsigned int ac97_detect: 1;	/* 1 = AC97 in detection phase */
+	unsigned int in_suspend: 1;	/* 1 during suspend/resume */
+
+	struct snd_4dwave synth;	/* synth specific variables */
+
+	spinlock_t event_lock;
+	spinlock_t voice_alloc;
+
+	struct snd_dma_device dma_dev;
+
+	struct pci_dev *pci;
+	struct snd_card *card;
+	struct snd_pcm *pcm;		/* ADC/DAC PCM */
+	struct snd_pcm *foldback;	/* Foldback PCM */
+	struct snd_pcm *spdif;	/* SPDIF PCM */
+	struct snd_rawmidi *rmidi;
+	struct snd_seq_device *seq_dev;
+
+	struct snd_ac97_bus *ac97_bus;
+	struct snd_ac97 *ac97;
+	struct snd_ac97 *ac97_sec;
+
+	unsigned int musicvol_wavevol;
+	struct snd_trident_pcm_mixer pcm_mixer[32];
+	struct snd_kcontrol *ctl_vol;	/* front volume */
+	struct snd_kcontrol *ctl_pan;	/* pan */
+	struct snd_kcontrol *ctl_rvol;	/* rear volume */
+	struct snd_kcontrol *ctl_cvol;	/* center volume */
+
+	spinlock_t reg_lock;
+
+	struct gameport *gameport;
+};
+
+int snd_trident_create(struct snd_card *card,
+		       struct pci_dev *pci,
+		       int pcm_streams,
+		       int pcm_spdif_device,
+		       int max_wavetable_size,
+		       struct snd_trident ** rtrident);
+int snd_trident_create_gameport(struct snd_trident *trident);
+
+int snd_trident_pcm(struct snd_trident * trident, int device, struct snd_pcm **rpcm);
+int snd_trident_foldback_pcm(struct snd_trident * trident, int device, struct snd_pcm **rpcm);
+int snd_trident_spdif_pcm(struct snd_trident * trident, int device, struct snd_pcm **rpcm);
+int snd_trident_attach_synthesizer(struct snd_trident * trident);
+struct snd_trident_voice *snd_trident_alloc_voice(struct snd_trident * trident, int type,
+					     int client, int port);
+void snd_trident_free_voice(struct snd_trident * trident, struct snd_trident_voice *voice);
+void snd_trident_start_voice(struct snd_trident * trident, unsigned int voice);
+void snd_trident_stop_voice(struct snd_trident * trident, unsigned int voice);
+void snd_trident_write_voice_regs(struct snd_trident * trident, struct snd_trident_voice *voice);
+int snd_trident_suspend(struct pci_dev *pci, pm_message_t state);
+int snd_trident_resume(struct pci_dev *pci);
+
+/* TLB memory allocation */
+struct snd_util_memblk *snd_trident_alloc_pages(struct snd_trident *trident,
+						struct snd_pcm_substream *substream);
+int snd_trident_free_pages(struct snd_trident *trident, struct snd_util_memblk *blk);
+struct snd_util_memblk *snd_trident_synth_alloc(struct snd_trident *trident, unsigned int size);
+int snd_trident_synth_free(struct snd_trident *trident, struct snd_util_memblk *blk);
+int snd_trident_synth_copy_from_user(struct snd_trident *trident, struct snd_util_memblk *blk,
+				     int offset, const char __user *data, int size);
+
+#endif /* __SOUND_TRIDENT_H */
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/typedefs.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/typedefs.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/typedefs.h	(revision 91)
@@ -0,0 +1,173 @@
+/*
+ * Typedef's for backward compatibility (for out-of-kernel drivers)
+ *
+ * This file will be removed soon in future
+ */
+
+/* core stuff */
+typedef struct snd_card snd_card_t;
+typedef struct snd_device snd_device_t;
+typedef struct snd_device_ops snd_device_ops_t;
+//typedef enum snd_card_type snd_card_type_t;
+typedef struct snd_minor snd_minor_t;
+
+/* info */
+typedef struct snd_info_entry snd_info_entry_t;
+typedef struct snd_info_buffer snd_info_buffer_t;
+
+/* control */
+typedef struct snd_ctl_file snd_ctl_file_t;
+typedef struct snd_kcontrol snd_kcontrol_t;
+typedef struct snd_kcontrol_new snd_kcontrol_new_t;
+typedef struct snd_kcontrol_volatile snd_kcontrol_volatile_t;
+typedef struct snd_kctl_event snd_kctl_event_t;
+typedef struct snd_aes_iec958 snd_aes_iec958_t;
+typedef struct snd_ctl_card_info snd_ctl_card_info_t;
+typedef struct snd_ctl_elem_id snd_ctl_elem_id_t;
+typedef struct snd_ctl_elem_list snd_ctl_elem_list_t;
+typedef struct snd_ctl_elem_info snd_ctl_elem_info_t;
+typedef struct snd_ctl_elem_value snd_ctl_elem_value_t;
+typedef struct snd_ctl_event snd_ctl_event_t;
+#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
+typedef struct snd_mixer_oss snd_mixer_oss_t;
+#endif
+
+/* timer */
+typedef struct snd_timer snd_timer_t;
+typedef struct snd_timer_instance snd_timer_instance_t;
+typedef struct snd_timer_id snd_timer_id_t;
+typedef struct snd_timer_ginfo snd_timer_ginfo_t;
+typedef struct snd_timer_gparams snd_timer_gparams_t;
+typedef struct snd_timer_gstatus snd_timer_gstatus_t;
+typedef struct snd_timer_select snd_timer_select_t;
+typedef struct snd_timer_info snd_timer_info_t;
+typedef struct snd_timer_params snd_timer_params_t;
+typedef struct snd_timer_status snd_timer_status_t;
+typedef struct snd_timer_read snd_timer_read_t;
+typedef struct snd_timer_tread snd_timer_tread_t;
+
+/* PCM */
+typedef struct snd_pcm snd_pcm_t;
+typedef struct snd_pcm_str snd_pcm_str_t;
+typedef struct snd_pcm_substream snd_pcm_substream_t;
+typedef struct snd_pcm_info snd_pcm_info_t;
+typedef struct snd_pcm_hw_params snd_pcm_hw_params_t;
+typedef struct snd_pcm_sw_params snd_pcm_sw_params_t;
+typedef struct snd_pcm_channel_info snd_pcm_channel_info_t;
+typedef struct snd_pcm_status snd_pcm_status_t;
+typedef struct snd_pcm_mmap_status snd_pcm_mmap_status_t;
+typedef struct snd_pcm_mmap_control snd_pcm_mmap_control_t;
+typedef struct snd_mask snd_mask_t;
+typedef struct snd_sg_buf snd_pcm_sgbuf_t;
+
+typedef struct snd_interval snd_interval_t;
+typedef struct snd_xferi snd_xferi_t;
+typedef struct snd_xfern snd_xfern_t;
+typedef struct snd_xferv snd_xferv_t;
+
+typedef struct snd_pcm_file snd_pcm_file_t;
+typedef struct snd_pcm_runtime snd_pcm_runtime_t;
+typedef struct snd_pcm_hardware snd_pcm_hardware_t;
+typedef struct snd_pcm_ops snd_pcm_ops_t;
+typedef struct snd_pcm_hw_rule snd_pcm_hw_rule_t;
+typedef struct snd_pcm_hw_constraints snd_pcm_hw_constraints_t;
+typedef struct snd_ratnum ratnum_t;
+typedef struct snd_ratden ratden_t;
+typedef struct snd_pcm_hw_constraint_ratnums snd_pcm_hw_constraint_ratnums_t;
+typedef struct snd_pcm_hw_constraint_ratdens snd_pcm_hw_constraint_ratdens_t;
+typedef struct snd_pcm_hw_constraint_list snd_pcm_hw_constraint_list_t;
+typedef struct snd_pcm_group snd_pcm_group_t;
+typedef struct snd_pcm_notify snd_pcm_notify_t;
+
+/* rawmidi */
+typedef struct snd_rawmidi snd_rawmidi_t;
+typedef struct snd_rawmidi_info snd_rawmidi_info_t;
+typedef struct snd_rawmidi_params snd_rawmidi_params_t;
+typedef struct snd_rawmidi_status snd_rawmidi_status_t;
+typedef struct snd_rawmidi_runtime snd_rawmidi_runtime_t;
+typedef struct snd_rawmidi_substream snd_rawmidi_substream_t;
+typedef struct snd_rawmidi_str snd_rawmidi_str_t;
+typedef struct snd_rawmidi_ops snd_rawmidi_ops_t;
+typedef struct snd_rawmidi_global_ops snd_rawmidi_global_ops_t;
+typedef struct snd_rawmidi_file snd_rawmidi_file_t;
+
+/* hwdep */
+typedef struct snd_hwdep snd_hwdep_t;
+typedef struct snd_hwdep_info snd_hwdep_info_t;
+typedef struct snd_hwdep_dsp_status snd_hwdep_dsp_status_t;
+typedef struct snd_hwdep_dsp_image snd_hwdep_dsp_image_t;
+typedef struct snd_hwdep_ops snd_hwdep_ops_t;
+
+/* sequencer */
+typedef struct snd_seq_port_info snd_seq_port_info_t;
+typedef struct snd_seq_port_subscribe snd_seq_port_subscribe_t;
+typedef struct snd_seq_event snd_seq_event_t;
+typedef struct snd_seq_addr snd_seq_addr_t;
+typedef struct snd_seq_ev_volume snd_seq_ev_volume_t;
+typedef struct snd_seq_ev_loop snd_seq_ev_loop_t;
+typedef struct snd_seq_remove_events snd_seq_remove_events_t;
+typedef struct snd_seq_query_subs snd_seq_query_subs_t;
+typedef struct snd_seq_system_info snd_seq_system_info_t;
+typedef struct snd_seq_client_info snd_seq_client_info_t;
+typedef struct snd_seq_queue_info snd_seq_queue_info_t;
+typedef struct snd_seq_queue_status snd_seq_queue_status_t;
+typedef struct snd_seq_queue_tempo snd_seq_queue_tempo_t;
+typedef struct snd_seq_queue_owner snd_seq_queue_owner_t;
+typedef struct snd_seq_queue_timer snd_seq_queue_timer_t;
+typedef struct snd_seq_queue_client snd_seq_queue_client_t;
+typedef struct snd_seq_client_pool snd_seq_client_pool_t;
+typedef struct snd_seq_instr snd_seq_instr_t;
+typedef struct snd_seq_instr_data snd_seq_instr_data_t;
+typedef struct snd_seq_instr_header snd_seq_instr_header_t;
+
+typedef struct snd_seq_user_client user_client_t;
+typedef struct snd_seq_kernel_client kernel_client_t;
+typedef struct snd_seq_client client_t;
+typedef struct snd_seq_queue queue_t;
+
+/* seq_device */
+typedef struct snd_seq_device snd_seq_device_t;
+typedef struct snd_seq_dev_ops snd_seq_dev_ops_t;
+
+/* seq_midi */
+typedef struct snd_midi_event snd_midi_event_t;
+
+/* seq_midi_emul */
+typedef struct snd_midi_channel snd_midi_channel_t;
+typedef struct snd_midi_channel_set snd_midi_channel_set_t;
+typedef struct snd_midi_op snd_midi_op_t;
+
+/* seq_oss */
+typedef struct snd_seq_oss_arg snd_seq_oss_arg_t;
+typedef struct snd_seq_oss_callback snd_seq_oss_callback_t;
+typedef struct snd_seq_oss_reg snd_seq_oss_reg_t;
+
+/* virmidi */
+typedef struct snd_virmidi_dev snd_virmidi_dev_t;
+typedef struct snd_virmidi snd_virmidi_t;
+
+/* seq_instr */
+typedef struct snd_seq_kcluster snd_seq_kcluster_t;
+typedef struct snd_seq_kinstr_ops snd_seq_kinstr_ops_t;
+typedef struct snd_seq_kinstr snd_seq_kinstr_t;
+typedef struct snd_seq_kinstr_list snd_seq_kinstr_list_t;
+
+/* ac97 */
+typedef struct snd_ac97_bus ac97_bus_t;
+typedef struct snd_ac97_bus_ops ac97_bus_ops_t;
+typedef struct snd_ac97_template ac97_template_t;
+typedef struct snd_ac97 ac97_t;
+
+/* opl3/4 */
+typedef struct snd_opl3 opl3_t;
+typedef struct snd_opl4 opl4_t;
+
+/* mpu401 */
+typedef struct snd_mpu401 mpu401_t;
+
+/* i2c */
+typedef struct snd_i2c_device snd_i2c_device_t;
+typedef struct snd_i2c_bus snd_i2c_bus_t;
+
+typedef struct snd_ak4531 ak4531_t;
+
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/util_mem.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/util_mem.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/util_mem.h	(revision 91)
@@ -0,0 +1,64 @@
+#ifndef __UTIL_MEM_H
+#define __UTIL_MEM_H
+/*
+ *  Copyright (C) 2000 Takashi Iwai <tiwai@suse.de>
+ *
+ *  Generic memory management routines for soundcard memory allocation
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+typedef struct snd_util_memblk snd_util_memblk_t;
+typedef struct snd_util_memhdr snd_util_memhdr_t;
+typedef unsigned int snd_util_unit_t;
+
+/*
+ * memory block
+ */
+struct snd_util_memblk {
+	snd_util_unit_t size;		/* size of this block */
+	snd_util_unit_t offset;		/* zero-offset of this block */
+	struct list_head list;		/* link */
+};
+
+#define snd_util_memblk_argptr(blk)	(void*)((char*)(blk) + sizeof(snd_util_memblk_t))
+
+/*
+ * memory management information
+ */
+struct snd_util_memhdr {
+	snd_util_unit_t size;		/* size of whole data */
+	struct list_head block;		/* block linked-list header */
+	int nblocks;			/* # of allocated blocks */
+	snd_util_unit_t used;		/* used memory size */
+	int block_extra_size;		/* extra data size of chunk */
+	struct semaphore block_mutex;	/* lock */
+};
+
+/*
+ * prototypes
+ */
+snd_util_memhdr_t *snd_util_memhdr_new(int memsize);
+void snd_util_memhdr_free(snd_util_memhdr_t *hdr);
+snd_util_memblk_t *snd_util_mem_alloc(snd_util_memhdr_t *hdr, int size);
+int snd_util_mem_free(snd_util_memhdr_t *hdr, snd_util_memblk_t *blk);
+int snd_util_mem_avail(snd_util_memhdr_t *hdr);
+
+/* functions without mutex */
+snd_util_memblk_t *__snd_util_mem_alloc(snd_util_memhdr_t *hdr, int size);
+void __snd_util_mem_free(snd_util_memhdr_t *hdr, snd_util_memblk_t *blk);
+snd_util_memblk_t *__snd_util_memblk_new(snd_util_memhdr_t *hdr, snd_util_unit_t units, struct list_head *prev);
+
+#endif /* __UTIL_MEM_H */
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/version.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/version.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/version.h	(revision 91)
@@ -0,0 +1,9 @@
+/*
+ *  Configuration header file for compilation of the ALSA driver
+ */
+
+#ifndef __ALSA_VERSION_H__
+#define __ALSA_VERSION_H__
+
+
+#endif //__ALSA_VERSION_H__
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/version.h.in
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/version.h.in	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/version.h.in	(revision 91)
@@ -0,0 +1,1 @@
+#undef CONFIG_SND_VERSION
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/wavefront.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/wavefront.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/wavefront.h	(revision 91)
@@ -0,0 +1,687 @@
+#ifndef __WAVEFRONT_H__
+#define __WAVEFRONT_H__
+
+/*
+ *  Driver for Turtle Beach Wavefront cards (Maui,Tropez,Tropez+)
+ *
+ *  Copyright (c) by Paul Barton-Davis <pbd@op.net>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#if (!defined(__GNUC__) && !defined(__GNUG__))
+
+     You will not be able to compile this file correctly without gcc, because
+     it is necessary to pack the "wavefront_alias" structure to a size
+     of 22 bytes, corresponding to 16-bit alignment (as would have been
+     the case on the original platform, MS-DOS). If this is not done,
+     then WavePatch-format files cannot be read/written correctly.
+     The method used to do this here ("__attribute__((packed)") is
+     completely compiler dependent.
+     
+     All other wavefront_* types end up aligned to 32 bit values and
+     still have the same (correct) size.
+
+#else
+
+     /* However, note that as of G++ 2.7.3.2, g++ was unable to
+	correctly parse *type* __attribute__ tags. It will do the
+	right thing if we use the "packed" attribute on each struct
+	member, which has the same semantics anyway. 
+     */
+
+#endif /* __GNUC__ */
+
+/***************************** WARNING ********************************
+  PLEASE DO NOT MODIFY THIS FILE IN ANY WAY THAT AFFECTS ITS ABILITY TO 
+  BE USED WITH EITHER C *OR* C++.
+ **********************************************************************/
+
+#ifndef NUM_MIDIKEYS 
+#define NUM_MIDIKEYS 128
+#endif  /* NUM_MIDIKEYS */
+
+#ifndef NUM_MIDICHANNELS
+#define NUM_MIDICHANNELS 16
+#endif  /* NUM_MIDICHANNELS */
+
+/* These are very useful/important. the original wavefront interface
+   was developed on a 16 bit system, where sizeof(int) = 2
+   bytes. Defining things like this makes the code much more portable, and
+   easier to understand without having to toggle back and forth
+   between a 16-bit view of the world and a 32-bit one. 
+ */   
+
+typedef short INT16;
+typedef unsigned short UINT16;
+typedef int INT32;
+typedef unsigned int UINT32;
+typedef char CHAR8;
+typedef unsigned char UCHAR8;
+
+/* Pseudo-commands not part of the WaveFront command set.
+   These are used for various driver controls and direct
+   hardware control.
+ */
+
+#define WFC_DEBUG_DRIVER                0
+#define WFC_FX_IOCTL                    1
+#define WFC_PATCH_STATUS                2
+#define WFC_PROGRAM_STATUS              3
+#define WFC_SAMPLE_STATUS               4
+#define WFC_DISABLE_INTERRUPTS          5
+#define WFC_ENABLE_INTERRUPTS           6
+#define WFC_INTERRUPT_STATUS            7
+#define WFC_ROMSAMPLES_RDONLY           8
+#define WFC_IDENTIFY_SLOT_TYPE          9
+
+/* Wavefront synth commands
+ */
+
+#define WFC_DOWNLOAD_SAMPLE		0x80
+#define WFC_DOWNLOAD_BLOCK		0x81
+#define WFC_DOWNLOAD_MULTISAMPLE	0x82
+#define WFC_DOWNLOAD_SAMPLE_ALIAS	0x83
+#define WFC_DELETE_SAMPLE		0x84
+#define WFC_REPORT_FREE_MEMORY		0x85
+#define WFC_DOWNLOAD_PATCH		0x86
+#define WFC_DOWNLOAD_PROGRAM		0x87
+#define WFC_SET_SYNTHVOL		0x89
+#define WFC_SET_NVOICES			0x8B
+#define WFC_DOWNLOAD_DRUM		0x90
+#define WFC_GET_SYNTHVOL		0x92
+#define WFC_GET_NVOICES			0x94
+#define WFC_DISABLE_CHANNEL		0x9A
+#define WFC_ENABLE_CHANNEL		0x9B
+#define WFC_MISYNTH_OFF			0x9D
+#define WFC_MISYNTH_ON			0x9E
+#define WFC_FIRMWARE_VERSION		0x9F
+#define WFC_GET_NSAMPLES		0xA0
+#define WFC_DISABLE_DRUM_PROGRAM	0xA2
+#define WFC_UPLOAD_PATCH		0xA3
+#define WFC_UPLOAD_PROGRAM		0xA4
+#define WFC_SET_TUNING			0xA6
+#define WFC_GET_TUNING			0xA7
+#define WFC_VMIDI_ON			0xA8
+#define WFC_VMIDI_OFF			0xA9
+#define WFC_MIDI_STATUS			0xAA
+#define WFC_GET_CHANNEL_STATUS		0xAB
+#define WFC_DOWNLOAD_SAMPLE_HEADER	0xAC
+#define WFC_UPLOAD_SAMPLE_HEADER	0xAD
+#define WFC_UPLOAD_MULTISAMPLE		0xAE
+#define WFC_UPLOAD_SAMPLE_ALIAS		0xAF
+#define WFC_IDENTIFY_SAMPLE_TYPE	0xB0
+#define WFC_DOWNLOAD_EDRUM_PROGRAM	0xB1
+#define WFC_UPLOAD_EDRUM_PROGRAM	0xB2
+#define WFC_SET_EDRUM_CHANNEL		0xB3
+#define WFC_INSTOUT_LEVELS		0xB4
+#define WFC_PEAKOUT_LEVELS		0xB5
+#define WFC_REPORT_CHANNEL_PROGRAMS	0xB6
+#define WFC_HARDWARE_VERSION		0xCF
+#define WFC_UPLOAD_SAMPLE_PARAMS	0xD7
+#define WFC_DOWNLOAD_OS			0xF1
+#define WFC_NOOP                        0xFF
+
+#define WF_MAX_SAMPLE   512
+#define WF_MAX_PATCH    256
+#define WF_MAX_PROGRAM  128
+
+#define WF_SECTION_MAX  44   /* longest OS section length */
+
+/* # of bytes we send to the board when sending it various kinds of
+   substantive data, such as samples, patches and programs.
+*/
+
+#define WF_PROGRAM_BYTES 32
+#define WF_PATCH_BYTES 132
+#define WF_SAMPLE_BYTES 27
+#define WF_SAMPLE_HDR_BYTES 25
+#define WF_ALIAS_BYTES 25
+#define WF_DRUM_BYTES 9
+#define WF_MSAMPLE_BYTES 259 /* (MIDI_KEYS * 2) + 3 */
+
+#define WF_ACK     0x80
+#define WF_DMA_ACK 0x81
+
+/* OR-values for MIDI status bits */
+
+#define WF_MIDI_VIRTUAL_ENABLED 0x1
+#define WF_MIDI_VIRTUAL_IS_EXTERNAL 0x2
+#define WF_MIDI_IN_TO_SYNTH_DISABLED 0x4
+
+/* slot indexes for struct address_info: makes code a little more mnemonic */
+
+#define WF_SYNTH_SLOT         0
+#define WF_INTERNAL_MIDI_SLOT 1
+#define WF_EXTERNAL_MIDI_SLOT 2
+
+/* Magic MIDI bytes used to switch I/O streams on the ICS2115 MPU401
+   emulation. Note these NEVER show up in output from the device and
+   should NEVER be used in input unless Virtual MIDI mode has been 
+   disabled. If they do show up as input, the results are unpredictable.
+*/
+
+#define WF_EXTERNAL_SWITCH  0xFD
+#define WF_INTERNAL_SWITCH  0xF9
+
+/* Debugging flags */
+
+#define WF_DEBUG_CMD 0x1
+#define WF_DEBUG_DATA 0x2
+#define WF_DEBUG_LOAD_PATCH 0x4
+#define WF_DEBUG_IO 0x8
+
+/* WavePatch file format stuff */
+
+#define WF_WAVEPATCH_VERSION     120;  /*  Current version number (1.2)  */
+#define WF_MAX_COMMENT           64    /*  Comment length */
+#define WF_NUM_LAYERS            4
+#define WF_NAME_LENGTH           32
+#define WF_SOURCE_LENGTH         260
+
+#define BankFileID     "Bank"
+#define DrumkitFileID  "DrumKit"
+#define ProgramFileID  "Program"
+
+struct wf_envelope
+{
+    UCHAR8 attack_time:7;
+    UCHAR8 Unused1:1;
+
+    UCHAR8 decay1_time:7;
+    UCHAR8 Unused2:1;
+
+    UCHAR8 decay2_time:7;
+    UCHAR8 Unused3:1;
+
+    UCHAR8 sustain_time:7;
+    UCHAR8 Unused4:1;
+
+    UCHAR8 release_time:7;
+    UCHAR8 Unused5:1;
+
+    UCHAR8 release2_time:7;
+    UCHAR8 Unused6:1;
+
+    CHAR8 attack_level;
+    CHAR8 decay1_level;
+    CHAR8 decay2_level;
+    CHAR8 sustain_level;
+    CHAR8 release_level;
+
+    UCHAR8 attack_velocity:7;
+    UCHAR8 Unused7:1;
+
+    UCHAR8 volume_velocity:7;
+    UCHAR8 Unused8:1;
+
+    UCHAR8 keyboard_scaling:7;
+    UCHAR8 Unused9:1;
+};
+typedef struct wf_envelope wavefront_envelope;
+
+struct wf_lfo
+{
+    UCHAR8 sample_number;
+
+    UCHAR8 frequency:7;
+    UCHAR8 Unused1:1;
+
+    UCHAR8 am_src:4;
+    UCHAR8 fm_src:4;
+
+    CHAR8 fm_amount;
+    CHAR8 am_amount;
+    CHAR8 start_level;
+    CHAR8 end_level;
+
+    UCHAR8 ramp_delay:7;
+    UCHAR8 wave_restart:1; /* for LFO2 only */
+
+    UCHAR8 ramp_time:7;
+    UCHAR8 Unused2:1;
+};
+typedef struct wf_lfo wavefront_lfo;
+
+struct wf_patch
+{
+    INT16  frequency_bias;         /*  ** THIS IS IN MOTOROLA FORMAT!! ** */
+
+    UCHAR8 amplitude_bias:7;
+    UCHAR8 Unused1:1;
+
+    UCHAR8 portamento:7;
+    UCHAR8 Unused2:1;
+
+    UCHAR8 sample_number;
+
+    UCHAR8 pitch_bend:4;
+    UCHAR8 sample_msb:1;
+    UCHAR8 Unused3:3;
+
+    UCHAR8 mono:1;
+    UCHAR8 retrigger:1;
+    UCHAR8 nohold:1;
+    UCHAR8 restart:1;
+    UCHAR8 filterconfig:2; /* SDK says "not used" */
+    UCHAR8 reuse:1;
+    UCHAR8 reset_lfo:1;    
+
+    UCHAR8 fm_src2:4;
+    UCHAR8 fm_src1:4;   
+
+    CHAR8 fm_amount1;
+    CHAR8 fm_amount2;
+
+    UCHAR8 am_src:4;
+    UCHAR8 Unused4:4;
+
+    CHAR8 am_amount;
+
+    UCHAR8 fc1_mode:4;
+    UCHAR8 fc2_mode:4;
+
+    CHAR8 fc1_mod_amount;
+    CHAR8 fc1_keyboard_scaling;
+    CHAR8 fc1_bias;
+    CHAR8 fc2_mod_amount;
+    CHAR8 fc2_keyboard_scaling;
+    CHAR8 fc2_bias;
+
+    UCHAR8 randomizer:7;
+    UCHAR8 Unused5:1;
+
+    struct wf_envelope envelope1;
+    struct wf_envelope envelope2;
+    struct wf_lfo lfo1;
+    struct wf_lfo lfo2;
+};
+typedef struct wf_patch wavefront_patch;
+
+struct wf_layer
+{
+    UCHAR8 patch_number;
+
+    UCHAR8 mix_level:7;
+    UCHAR8 mute:1;
+
+    UCHAR8 split_point:7;
+    UCHAR8 play_below:1;
+
+    UCHAR8 pan_mod_src:2;
+    UCHAR8 pan_or_mod:1;
+    UCHAR8 pan:4;
+    UCHAR8 split_type:1;
+};
+typedef struct wf_layer wavefront_layer;
+
+struct wf_program
+{
+    struct wf_layer layer[WF_NUM_LAYERS];
+};
+typedef struct wf_program wavefront_program;
+
+struct wf_sample_offset
+{
+    INT32 Fraction:4;
+    INT32 Integer:20;
+    INT32 Unused:8;
+};
+typedef struct wf_sample_offset wavefront_sample_offset;          
+     
+/* Sample slot types */
+
+#define WF_ST_SAMPLE      0
+#define WF_ST_MULTISAMPLE 1
+#define WF_ST_ALIAS       2
+#define WF_ST_EMPTY       3
+
+/* pseudo's */
+
+#define WF_ST_DRUM        4
+#define WF_ST_PROGRAM     5
+#define WF_ST_PATCH       6
+#define WF_ST_SAMPLEHDR   7
+
+#define WF_ST_MASK        0xf
+
+/* Flags for slot status. These occupy the upper bits of the same byte
+   as a sample type.
+*/
+
+#define WF_SLOT_USED      0x80   /* XXX don't rely on this being accurate */
+#define WF_SLOT_FILLED    0x40
+#define WF_SLOT_ROM       0x20
+
+#define WF_SLOT_MASK      0xf0
+
+/* channel constants */
+
+#define WF_CH_MONO  0
+#define WF_CH_LEFT  1
+#define WF_CH_RIGHT 2
+
+/* Sample formats */
+
+#define LINEAR_16BIT 0
+#define WHITE_NOISE  1
+#define LINEAR_8BIT  2
+#define MULAW_8BIT   3
+
+#define WF_SAMPLE_IS_8BIT(smpl) ((smpl)->SampleResolution&2)
+
+
+/* 
+
+  Because most/all of the sample data we pass in via pointers has
+  never been copied (just mmap-ed into user space straight from the
+  disk), it would be nice to allow handling of multi-channel sample
+  data without forcing user-level extraction of the relevant bytes.
+  
+  So, we need a way of specifying which channel to use (the WaveFront
+  only handles mono samples in a given slot), and the only way to do
+  this without using some struct other than wavefront_sample as the
+  interface is the awful hack of using the unused bits in a
+  wavefront_sample:
+  
+  Val      Meaning
+  ---      -------
+  0        no channel selection (use channel 1, sample is MONO)
+  1        use first channel, and skip one
+  2        use second channel, and skip one
+  3        use third channel, and skip two
+  4        use fourth channel, skip three
+  5        use fifth channel, skip four
+  6        use six channel, skip five
+
+
+  This can handle up to 4 channels, and anyone downloading >4 channels
+  of sample data just to select one of them needs to find some tools
+  like sox ...
+
+  NOTE: values 0, 1 and 2 correspond to WF_CH_* above. This is 
+  important.
+
+*/
+
+#define WF_SET_CHANNEL(samp,chn) \
+ (samp)->Unused1 = chn & 0x1; \
+ (samp)->Unused2 = chn & 0x2; \
+ (samp)->Unused3 = chn & 0x4 
+  
+#define WF_GET_CHANNEL(samp) \
+  (((samp)->Unused3 << 2)|((samp)->Unused2<<1)|(samp)->Unused1)
+  
+typedef struct wf_sample {
+    struct wf_sample_offset sampleStartOffset;
+    struct wf_sample_offset loopStartOffset;
+    struct wf_sample_offset loopEndOffset;
+    struct wf_sample_offset sampleEndOffset;
+    INT16 FrequencyBias;
+    UCHAR8 SampleResolution:2;  /* sample_format */
+    UCHAR8 Unused1:1;
+    UCHAR8 Loop:1;
+    UCHAR8 Bidirectional:1;
+    UCHAR8 Unused2:1;
+    UCHAR8 Reverse:1;
+    UCHAR8 Unused3:1;
+} wavefront_sample;
+
+typedef struct wf_multisample {
+    INT16 NumberOfSamples;   /* log2 of the number of samples */
+    INT16 SampleNumber[NUM_MIDIKEYS];
+} wavefront_multisample;
+
+typedef struct wf_alias {
+    INT16 OriginalSample __attribute__ ((packed));
+
+    struct wf_sample_offset sampleStartOffset __attribute__ ((packed));
+    struct wf_sample_offset loopStartOffset __attribute__ ((packed));
+    struct wf_sample_offset sampleEndOffset __attribute__ ((packed));
+    struct wf_sample_offset loopEndOffset __attribute__ ((packed));
+
+    INT16  FrequencyBias __attribute__ ((packed));
+
+    UCHAR8 SampleResolution:2  __attribute__ ((packed));
+    UCHAR8 Unused1:1  __attribute__ ((packed));
+    UCHAR8 Loop:1 __attribute__ ((packed));
+    UCHAR8 Bidirectional:1  __attribute__ ((packed));
+    UCHAR8 Unused2:1 __attribute__ ((packed));
+    UCHAR8 Reverse:1 __attribute__ ((packed));
+    UCHAR8 Unused3:1 __attribute__ ((packed)); 
+    
+    /* This structure is meant to be padded only to 16 bits on their
+       original. Of course, whoever wrote their documentation didn't
+       realize that sizeof(struct) can be >=
+       sum(sizeof(struct-fields)) and so thought that giving a C level
+       description of the structs used in WavePatch files was
+       sufficient. I suppose it was, as long as you remember the 
+       standard 16->32 bit issues.
+    */
+
+    UCHAR8 sixteen_bit_padding __attribute__ ((packed));
+} wavefront_alias;
+
+typedef struct wf_drum {
+    UCHAR8 PatchNumber;
+    UCHAR8 MixLevel:7;
+    UCHAR8 Unmute:1;
+    UCHAR8 Group:4;
+    UCHAR8 Unused1:4;
+    UCHAR8 PanModSource:2;
+    UCHAR8 PanModulated:1;
+    UCHAR8 PanAmount:4;
+    UCHAR8 Unused2:1;
+} wavefront_drum;
+
+typedef struct wf_drumkit {
+    struct wf_drum drum[NUM_MIDIKEYS];
+} wavefront_drumkit;
+
+typedef struct wf_channel_programs {
+    UCHAR8 Program[NUM_MIDICHANNELS];
+} wavefront_channel_programs;
+
+/* How to get MIDI channel status from the data returned by
+   a WFC_GET_CHANNEL_STATUS command (a struct wf_channel_programs)
+*/
+
+#define WF_CHANNEL_STATUS(ch,wcp) (wcp)[(ch/7)] & (1<<((ch)%7))
+
+typedef union wf_any {
+    wavefront_sample s;
+    wavefront_multisample ms;
+    wavefront_alias a;
+    wavefront_program pr;
+    wavefront_patch p;
+    wavefront_drum d;
+} wavefront_any;
+
+/* Hannu Solvainen hoped that his "patch_info" struct in soundcard.h
+   might work for other wave-table based patch loading situations.
+   Alas, his fears were correct. The WaveFront doesn't even come with
+   just "patches", but several different kind of structures that
+   control the sound generation process.
+ */
+
+typedef struct wf_patch_info {
+    
+    /* the first two fields are used by the OSS "patch loading" interface
+       only, and are unused by the current user-level library.
+    */
+
+    INT16   key;               /* Use WAVEFRONT_PATCH here */
+    UINT16  devno;             /* fill in when sending */
+    UCHAR8  subkey;            /* WF_ST_{SAMPLE,ALIAS,etc.} */
+
+#define WAVEFRONT_FIND_FREE_SAMPLE_SLOT 999
+
+    UINT16  number;            /* patch/sample/prog number */
+
+    UINT32  size;              /* size of any data included in 
+				  one of the fields in `hdrptr', or
+				  as `dataptr'.
+
+				  NOTE: for actual samples, this is
+				  the size of the *SELECTED CHANNEL*
+				  even if more data is actually available.
+				  
+				  So, a stereo sample (2 channels) of
+				  6000 bytes total has `size' = 3000.
+
+				  See the macros and comments for
+				  WF_{GET,SET}_CHANNEL above.
+
+			       */
+    wavefront_any *hdrptr;      /* user-space ptr to hdr bytes */
+    UINT16 *dataptr;            /* actual sample data */
+
+    wavefront_any hdr;          /* kernel-space copy of hdr bytes */         
+} wavefront_patch_info;
+
+/* The maximum number of bytes we will ever move to or from user space
+   in response to a WFC_* command.  This obviously doesn't cover
+   actual sample data.
+*/
+
+#define WF_MAX_READ sizeof(wavefront_multisample)
+#define WF_MAX_WRITE sizeof(wavefront_multisample)
+
+/*
+   This allows us to execute any WF command except the download/upload
+   ones, which are handled differently due to copyin/copyout issues as
+   well as data-nybbling to/from the card.
+ */
+
+typedef struct wavefront_control {
+    int cmd;                           /* WFC_* */
+    char status;                       /* return status to user-space */
+    unsigned char rbuf[WF_MAX_READ];   /* bytes read from card */
+    unsigned char wbuf[WF_MAX_WRITE];  /* bytes written to card */
+} wavefront_control;
+
+#define WFCTL_WFCMD    0x1
+#define WFCTL_LOAD_SPP 0x2
+
+/* Modulator table */
+
+#define WF_MOD_LFO1      0
+#define WF_MOD_LFO2      1
+#define WF_MOD_ENV1      2
+#define WF_MOD_ENV2      3
+#define WF_MOD_KEYBOARD  4
+#define WF_MOD_LOGKEY    5
+#define WF_MOD_VELOCITY  6
+#define WF_MOD_LOGVEL    7
+#define WF_MOD_RANDOM    8
+#define WF_MOD_PRESSURE  9
+#define WF_MOD_MOD_WHEEL 10
+#define WF_MOD_1         WF_MOD_MOD_WHEEL 
+#define WF_MOD_BREATH    11
+#define WF_MOD_2         WF_MOD_BREATH
+#define WF_MOD_FOOT      12
+#define WF_MOD_4         WF_MOD_FOOT
+#define WF_MOD_VOLUME    13
+#define WF_MOD_7         WF_MOD_VOLUME
+#define WF_MOD_PAN       14
+#define WF_MOD_10        WF_MOD_PAN
+#define WF_MOD_EXPR      15
+#define WF_MOD_11        WF_MOD_EXPR
+
+/* FX-related material */
+
+typedef struct wf_fx_info {
+    int request;             /* see list below */
+    long data[4];             /* we don't need much */
+} wavefront_fx_info;
+
+/* support for each of these will be forthcoming once I or someone 
+   else has figured out which of the addresses on page 6 and page 7 of 
+   the YSS225 control each parameter. Incidentally, these come from
+   the Windows driver interface, but again, Turtle Beach didn't
+   document the API to use them.
+*/
+
+#define WFFX_SETOUTGAIN		        0
+#define WFFX_SETSTEREOOUTGAIN		1
+#define WFFX_SETREVERBIN1GAIN		2
+#define WFFX_SETREVERBIN2GAIN		3
+#define WFFX_SETREVERBIN3GAIN		4
+#define WFFX_SETCHORUSINPORT		5
+#define WFFX_SETREVERBIN1PORT		6
+#define WFFX_SETREVERBIN2PORT		7
+#define WFFX_SETREVERBIN3PORT		8
+#define WFFX_SETEFFECTPORT		9
+#define WFFX_SETAUXPORT		        10
+#define WFFX_SETREVERBTYPE		11
+#define WFFX_SETREVERBDELAY		12
+#define WFFX_SETCHORUSLFO		13
+#define WFFX_SETCHORUSPMD		14
+#define WFFX_SETCHORUSAMD		15
+#define WFFX_SETEFFECT		        16
+#define WFFX_SETBASEALL		        17
+#define WFFX_SETREVERBALL		18
+#define WFFX_SETCHORUSALL		20
+#define WFFX_SETREVERBDEF		22
+#define WFFX_SETCHORUSDEF		23
+#define WFFX_DELAYSETINGAIN		24
+#define WFFX_DELAYSETFBGAIN	        25
+#define WFFX_DELAYSETFBLPF		26
+#define WFFX_DELAYSETGAIN		27
+#define WFFX_DELAYSETTIME		28
+#define WFFX_DELAYSETFBTIME		29
+#define WFFX_DELAYSETALL		30
+#define WFFX_DELAYSETDEF		32
+#define WFFX_SDELAYSETINGAIN		33
+#define WFFX_SDELAYSETFBGAIN		34
+#define WFFX_SDELAYSETFBLPF		35
+#define WFFX_SDELAYSETGAIN		36
+#define WFFX_SDELAYSETTIME		37
+#define WFFX_SDELAYSETFBTIME		38
+#define WFFX_SDELAYSETALL		39
+#define WFFX_SDELAYSETDEF		41
+#define WFFX_DEQSETINGAIN		42
+#define WFFX_DEQSETFILTER		43
+#define WFFX_DEQSETALL		        44
+#define WFFX_DEQSETDEF		        46
+#define WFFX_MUTE		        47
+#define WFFX_FLANGESETBALANCE	        48	
+#define WFFX_FLANGESETDELAY		49
+#define WFFX_FLANGESETDWFFX_TH		50
+#define WFFX_FLANGESETFBGAIN		51
+#define WFFX_FLANGESETINGAIN		52
+#define WFFX_FLANGESETLFO		53
+#define WFFX_FLANGESETALL		54
+#define WFFX_FLANGESETDEF		56
+#define WFFX_PITCHSETSHIFT		57
+#define WFFX_PITCHSETBALANCE		58
+#define WFFX_PITCHSETALL		59
+#define WFFX_PITCHSETDEF		61
+#define WFFX_SRSSETINGAIN		62
+#define WFFX_SRSSETSPACE		63
+#define WFFX_SRSSETCENTER		64
+#define WFFX_SRSSETGAIN		        65
+#define WFFX_SRSSETMODE	        	66
+#define WFFX_SRSSETDEF		        68
+
+/* Allow direct user-space control over FX memory/coefficient data.
+   In theory this could be used to download the FX microprogram,
+   but it would be a little slower, and involve some wierd code.
+ */
+
+#define WFFX_MEMSET              69
+
+#endif /* __WAVEFRONT_H__ */
+
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/wavefront_fx.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/wavefront_fx.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/wavefront_fx.h	(revision 91)
@@ -0,0 +1,9 @@
+#ifndef __WAVEFRONT_FX_H__
+#define __WAVEFRONT_FX_H__
+
+extern int  snd_wavefront_fx_detect (snd_wavefront_t *);
+extern void snd_wavefront_fx_ioctl  (snd_synth_t *sdev, 
+				     unsigned int cmd, 
+				     unsigned long arg);
+
+#endif  __WAVEFRONT_FX_H__
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/ymfpci.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/ymfpci.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/ymfpci.h	(revision 91)
@@ -0,0 +1,408 @@
+#ifndef __SOUND_YMFPCI_H
+#define __SOUND_YMFPCI_H
+
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  Definitions for Yahama YMF724/740/744/754 chips
+ *
+ *
+ *   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 "pcm.h"
+#include "rawmidi.h"
+#include "ac97_codec.h"
+
+#ifndef PCI_VENDOR_ID_YAMAHA
+#define PCI_VENDOR_ID_YAMAHA            0x1073
+#endif
+#ifndef PCI_DEVICE_ID_YAMAHA_724
+#define PCI_DEVICE_ID_YAMAHA_724	0x0004
+#endif
+#ifndef PCI_DEVICE_ID_YAMAHA_724F
+#define PCI_DEVICE_ID_YAMAHA_724F	0x000d
+#endif
+#ifndef PCI_DEVICE_ID_YAMAHA_740
+#define PCI_DEVICE_ID_YAMAHA_740	0x000a
+#endif
+#ifndef PCI_DEVICE_ID_YAMAHA_740C
+#define PCI_DEVICE_ID_YAMAHA_740C	0x000c
+#endif
+#ifndef PCI_DEVICE_ID_YAMAHA_744
+#define PCI_DEVICE_ID_YAMAHA_744	0x0010
+#endif
+#ifndef PCI_DEVICE_ID_YAMAHA_754
+#define PCI_DEVICE_ID_YAMAHA_754	0x0012
+#endif
+
+/*
+ *  Direct registers
+ */
+
+#define YMFREG(chip, reg)		(chip->port + YDSXGR_##reg)
+
+#define	YDSXGR_INTFLAG			0x0004
+#define	YDSXGR_ACTIVITY			0x0006
+#define	YDSXGR_GLOBALCTRL		0x0008
+#define	YDSXGR_ZVCTRL			0x000A
+#define	YDSXGR_TIMERCTRL		0x0010
+#define	YDSXGR_TIMERCOUNT		0x0012
+#define	YDSXGR_SPDIFOUTCTRL		0x0018
+#define	YDSXGR_SPDIFOUTSTATUS		0x001C
+#define	YDSXGR_EEPROMCTRL		0x0020
+#define	YDSXGR_SPDIFINCTRL		0x0034
+#define	YDSXGR_SPDIFINSTATUS		0x0038
+#define	YDSXGR_DSPPROGRAMDL		0x0048
+#define	YDSXGR_DLCNTRL			0x004C
+#define	YDSXGR_GPIOININTFLAG		0x0050
+#define	YDSXGR_GPIOININTENABLE		0x0052
+#define	YDSXGR_GPIOINSTATUS		0x0054
+#define	YDSXGR_GPIOOUTCTRL		0x0056
+#define	YDSXGR_GPIOFUNCENABLE		0x0058
+#define	YDSXGR_GPIOTYPECONFIG		0x005A
+#define	YDSXGR_AC97CMDDATA		0x0060
+#define	YDSXGR_AC97CMDADR		0x0062
+#define	YDSXGR_PRISTATUSDATA		0x0064
+#define	YDSXGR_PRISTATUSADR		0x0066
+#define	YDSXGR_SECSTATUSDATA		0x0068
+#define	YDSXGR_SECSTATUSADR		0x006A
+#define	YDSXGR_SECCONFIG		0x0070
+#define	YDSXGR_LEGACYOUTVOL		0x0080
+#define	YDSXGR_LEGACYOUTVOLL		0x0080
+#define	YDSXGR_LEGACYOUTVOLR		0x0082
+#define	YDSXGR_NATIVEDACOUTVOL		0x0084
+#define	YDSXGR_NATIVEDACOUTVOLL		0x0084
+#define	YDSXGR_NATIVEDACOUTVOLR		0x0086
+#define	YDSXGR_ZVOUTVOL			0x0088
+#define	YDSXGR_ZVOUTVOLL		0x0088
+#define	YDSXGR_ZVOUTVOLR		0x008A
+#define	YDSXGR_SECADCOUTVOL		0x008C
+#define	YDSXGR_SECADCOUTVOLL		0x008C
+#define	YDSXGR_SECADCOUTVOLR		0x008E
+#define	YDSXGR_PRIADCOUTVOL		0x0090
+#define	YDSXGR_PRIADCOUTVOLL		0x0090
+#define	YDSXGR_PRIADCOUTVOLR		0x0092
+#define	YDSXGR_LEGACYLOOPVOL		0x0094
+#define	YDSXGR_LEGACYLOOPVOLL		0x0094
+#define	YDSXGR_LEGACYLOOPVOLR		0x0096
+#define	YDSXGR_NATIVEDACLOOPVOL		0x0098
+#define	YDSXGR_NATIVEDACLOOPVOLL	0x0098
+#define	YDSXGR_NATIVEDACLOOPVOLR	0x009A
+#define	YDSXGR_ZVLOOPVOL		0x009C
+#define	YDSXGR_ZVLOOPVOLL		0x009E
+#define	YDSXGR_ZVLOOPVOLR		0x009E
+#define	YDSXGR_SECADCLOOPVOL		0x00A0
+#define	YDSXGR_SECADCLOOPVOLL		0x00A0
+#define	YDSXGR_SECADCLOOPVOLR		0x00A2
+#define	YDSXGR_PRIADCLOOPVOL		0x00A4
+#define	YDSXGR_PRIADCLOOPVOLL		0x00A4
+#define	YDSXGR_PRIADCLOOPVOLR		0x00A6
+#define	YDSXGR_NATIVEADCINVOL		0x00A8
+#define	YDSXGR_NATIVEADCINVOLL		0x00A8
+#define	YDSXGR_NATIVEADCINVOLR		0x00AA
+#define	YDSXGR_NATIVEDACINVOL		0x00AC
+#define	YDSXGR_NATIVEDACINVOLL		0x00AC
+#define	YDSXGR_NATIVEDACINVOLR		0x00AE
+#define	YDSXGR_BUF441OUTVOL		0x00B0
+#define	YDSXGR_BUF441OUTVOLL		0x00B0
+#define	YDSXGR_BUF441OUTVOLR		0x00B2
+#define	YDSXGR_BUF441LOOPVOL		0x00B4
+#define	YDSXGR_BUF441LOOPVOLL		0x00B4
+#define	YDSXGR_BUF441LOOPVOLR		0x00B6
+#define	YDSXGR_SPDIFOUTVOL		0x00B8
+#define	YDSXGR_SPDIFOUTVOLL		0x00B8
+#define	YDSXGR_SPDIFOUTVOLR		0x00BA
+#define	YDSXGR_SPDIFLOOPVOL		0x00BC
+#define	YDSXGR_SPDIFLOOPVOLL		0x00BC
+#define	YDSXGR_SPDIFLOOPVOLR		0x00BE
+#define	YDSXGR_ADCSLOTSR		0x00C0
+#define	YDSXGR_RECSLOTSR		0x00C4
+#define	YDSXGR_ADCFORMAT		0x00C8
+#define	YDSXGR_RECFORMAT		0x00CC
+#define	YDSXGR_P44SLOTSR		0x00D0
+#define	YDSXGR_STATUS			0x0100
+#define	YDSXGR_CTRLSELECT		0x0104
+#define	YDSXGR_MODE			0x0108
+#define	YDSXGR_SAMPLECOUNT		0x010C
+#define	YDSXGR_NUMOFSAMPLES		0x0110
+#define	YDSXGR_CONFIG			0x0114
+#define	YDSXGR_PLAYCTRLSIZE		0x0140
+#define	YDSXGR_RECCTRLSIZE		0x0144
+#define	YDSXGR_EFFCTRLSIZE		0x0148
+#define	YDSXGR_WORKSIZE			0x014C
+#define	YDSXGR_MAPOFREC			0x0150
+#define	YDSXGR_MAPOFEFFECT		0x0154
+#define	YDSXGR_PLAYCTRLBASE		0x0158
+#define	YDSXGR_RECCTRLBASE		0x015C
+#define	YDSXGR_EFFCTRLBASE		0x0160
+#define	YDSXGR_WORKBASE			0x0164
+#define	YDSXGR_DSPINSTRAM		0x1000
+#define	YDSXGR_CTRLINSTRAM		0x4000
+
+#define YDSXG_AC97READCMD		0x8000
+#define YDSXG_AC97WRITECMD		0x0000
+
+#define PCIR_DSXG_LEGACY		0x40
+#define PCIR_DSXG_ELEGACY		0x42
+#define PCIR_DSXG_CTRL			0x48
+#define PCIR_DSXG_PWRCTRL1		0x4a
+#define PCIR_DSXG_PWRCTRL2		0x4e
+#define PCIR_DSXG_FMBASE		0x60
+#define PCIR_DSXG_SBBASE		0x62
+#define PCIR_DSXG_MPU401BASE		0x64
+#define PCIR_DSXG_JOYBASE		0x66
+
+#define YDSXG_DSPLENGTH			0x0080
+#define YDSXG_CTRLLENGTH		0x3000
+
+#define YDSXG_DEFAULT_WORK_SIZE		0x0400
+
+#define YDSXG_PLAYBACK_VOICES		64
+#define YDSXG_CAPTURE_VOICES		2
+#define YDSXG_EFFECT_VOICES		5
+
+#define YMFPCI_LEGACY_SBEN	(1 << 0)	/* soundblaster enable */
+#define YMFPCI_LEGACY_FMEN	(1 << 1)	/* OPL3 enable */
+#define YMFPCI_LEGACY_JPEN	(1 << 2)	/* joystick enable */
+#define YMFPCI_LEGACY_MEN	(1 << 3)	/* MPU401 enable */
+#define YMFPCI_LEGACY_MIEN	(1 << 4)	/* MPU RX irq enable */
+#define YMFPCI_LEGACY_IOBITS	(1 << 5)	/* i/o bits range, 0 = 16bit, 1 =10bit */
+#define YMFPCI_LEGACY_SDMA	(3 << 6)	/* SB DMA select */
+#define YMFPCI_LEGACY_SBIRQ	(7 << 8)	/* SB IRQ select */
+#define YMFPCI_LEGACY_MPUIRQ	(7 << 11)	/* MPU IRQ select */
+#define YMFPCI_LEGACY_SIEN	(1 << 14)	/* serialized IRQ */
+#define YMFPCI_LEGACY_LAD	(1 << 15)	/* legacy audio disable */
+
+#define YMFPCI_LEGACY2_FMIO	(3 << 0)	/* OPL3 i/o address (724/740) */
+#define YMFPCI_LEGACY2_SBIO	(3 << 2)	/* SB i/o address (724/740) */
+#define YMFPCI_LEGACY2_MPUIO	(3 << 4)	/* MPU401 i/o address (724/740) */
+#define YMFPCI_LEGACY2_JSIO	(3 << 6)	/* joystick i/o address (724/740) */
+#define YMFPCI_LEGACY2_MAIM	(1 << 8)	/* MPU401 ack intr mask */
+#define YMFPCI_LEGACY2_SMOD	(3 << 11)	/* SB DMA mode */
+#define YMFPCI_LEGACY2_SBVER	(3 << 13)	/* SB version select */
+#define YMFPCI_LEGACY2_IMOD	(1 << 15)	/* legacy IRQ mode */
+/* SIEN:IMOD 0:0 = legacy irq, 0:1 = INTA, 1:0 = serialized IRQ */
+
+/*
+ *
+ */
+
+typedef struct snd_ymfpci_playback_bank {
+	u32 format;
+	u32 loop_default;
+	u32 base;			/* 32-bit address */
+	u32 loop_start;			/* 32-bit offset */
+	u32 loop_end;			/* 32-bit offset */
+	u32 loop_frac;			/* 8-bit fraction - loop_start */
+	u32 delta_end;			/* pitch delta end */
+	u32 lpfK_end;
+	u32 eg_gain_end;
+	u32 left_gain_end;
+	u32 right_gain_end;
+	u32 eff1_gain_end;
+	u32 eff2_gain_end;
+	u32 eff3_gain_end;
+	u32 lpfQ;
+	u32 status;
+	u32 num_of_frames;
+	u32 loop_count;
+	u32 start;
+	u32 start_frac;
+	u32 delta;
+	u32 lpfK;
+	u32 eg_gain;
+	u32 left_gain;
+	u32 right_gain;
+	u32 eff1_gain;
+	u32 eff2_gain;
+	u32 eff3_gain;
+	u32 lpfD1;
+	u32 lpfD2;
+} snd_ymfpci_playback_bank_t;
+
+typedef struct snd_ymfpci_capture_bank {
+	u32 base;			/* 32-bit address */
+	u32 loop_end;			/* 32-bit offset */
+	u32 start;			/* 32-bit offset */
+	u32 num_of_loops;		/* counter */
+} snd_ymfpci_capture_bank_t;
+
+typedef struct snd_ymfpci_effect_bank {
+	u32 base;			/* 32-bit address */
+	u32 loop_end;			/* 32-bit offset */
+	u32 start;			/* 32-bit offset */
+	u32 temp;
+} snd_ymfpci_effect_bank_t;
+
+typedef struct snd_ymfpci_voice ymfpci_voice_t;
+typedef struct snd_ymfpci_pcm ymfpci_pcm_t;
+typedef struct snd_ymfpci ymfpci_t;
+
+typedef enum {
+	YMFPCI_PCM,
+	YMFPCI_SYNTH,
+	YMFPCI_MIDI
+} ymfpci_voice_type_t;
+
+struct snd_ymfpci_voice {
+	ymfpci_t *chip;
+	int number;
+	int use: 1,
+	    pcm: 1,
+	    synth: 1,
+	    midi: 1;
+	snd_ymfpci_playback_bank_t *bank;
+	dma_addr_t bank_addr;
+	void (*interrupt)(ymfpci_t *chip, ymfpci_voice_t *voice);
+	ymfpci_pcm_t *ypcm;
+};
+
+typedef enum {
+	PLAYBACK_VOICE,
+	CAPTURE_REC,
+	CAPTURE_AC97,
+	EFFECT_DRY_LEFT,
+	EFFECT_DRY_RIGHT,
+	EFFECT_EFF1,
+	EFFECT_EFF2,
+	EFFECT_EFF3
+} snd_ymfpci_pcm_type_t;
+
+struct snd_ymfpci_pcm {
+	ymfpci_t *chip;
+	snd_ymfpci_pcm_type_t type;
+	snd_pcm_substream_t *substream;
+	ymfpci_voice_t *voices[2];	/* playback only */
+	int running: 1;
+	int output_front: 1;
+        int output_rear: 1;
+        unsigned int update_pcm_vol;
+	u32 period_size;		/* cached from runtime->period_size */
+	u32 buffer_size;		/* cached from runtime->buffer_size */
+	u32 period_pos;
+	u32 last_pos;
+	u32 capture_bank_number;
+	u32 shift;
+};
+
+struct snd_ymfpci {
+	int irq;
+
+	unsigned int device_id;	/* PCI device ID */
+	unsigned int rev;	/* PCI revision */
+	unsigned long reg_area_phys;
+	unsigned long reg_area_virt;
+	struct resource *res_reg_area;
+	struct resource *fm_res;
+	struct resource *mpu_res;
+
+	unsigned short old_legacy_ctrl;
+#if defined(CONFIG_GAMEPORT) || defined(CONFIG_GAMEPORT_MODULE)
+	unsigned int joystick_port;
+	struct semaphore joystick_mutex;
+	struct resource *joystick_res;
+	struct gameport gameport;
+#endif
+
+        struct snd_dma_buffer work_ptr;
+
+	unsigned int bank_size_playback;
+	unsigned int bank_size_capture;
+	unsigned int bank_size_effect;
+	unsigned int work_size;
+
+	void *bank_base_playback;
+	void *bank_base_capture;
+	void *bank_base_effect;
+	void *work_base;
+	dma_addr_t bank_base_playback_addr;
+	dma_addr_t bank_base_capture_addr;
+	dma_addr_t bank_base_effect_addr;
+        dma_addr_t work_base_addr;
+
+        struct snd_dma_buffer ac3_tmp_base;
+
+	u32 *ctrl_playback;
+	snd_ymfpci_playback_bank_t *bank_playback[YDSXG_PLAYBACK_VOICES][2];
+	snd_ymfpci_capture_bank_t *bank_capture[YDSXG_CAPTURE_VOICES][2];
+	snd_ymfpci_effect_bank_t *bank_effect[YDSXG_EFFECT_VOICES][2];
+
+	int start_count;
+
+	u32 active_bank;
+	ymfpci_voice_t voices[64];
+
+	ac97_bus_t *ac97_bus;
+	ac97_t *ac97;
+	snd_rawmidi_t *rawmidi;
+	snd_timer_t *timer;
+
+	struct pci_dev *pci;
+	snd_card_t *card;
+	snd_pcm_t *pcm;
+	snd_pcm_t *pcm2;
+	snd_pcm_t *pcm_spdif;
+	snd_pcm_t *pcm_4ch;
+	snd_pcm_substream_t *capture_substream[YDSXG_CAPTURE_VOICES];
+	snd_pcm_substream_t *effect_substream[YDSXG_EFFECT_VOICES];
+	snd_kcontrol_t *ctl_vol_recsrc;
+	snd_kcontrol_t *ctl_vol_adcrec;
+	snd_kcontrol_t *ctl_vol_spdifrec;
+	unsigned short spdif_bits, spdif_pcm_bits;
+	snd_kcontrol_t *spdif_pcm_ctl;
+	int mode_dup4ch;
+	int rear_opened;
+        int spdif_opened;
+        struct {
+            u16 left;
+            u16 right;
+            snd_kcontrol_t *ctl;
+        } pcm_mixer[32];
+
+	spinlock_t reg_lock;
+	spinlock_t voice_lock;
+	wait_queue_head_t interrupt_sleep;
+	atomic_t interrupt_sleep_count;
+	snd_info_entry_t *proc_entry;
+
+#ifdef CONFIG_PM
+	u32 *saved_regs;
+	u32 saved_ydsxgr_mode;
+#endif
+};
+
+int snd_ymfpci_create(snd_card_t * card,
+		      struct pci_dev *pci,
+		      unsigned short old_legacy_ctrl,
+		      ymfpci_t ** rcodec);
+
+int snd_ymfpci_suspend(struct pci_dev *pci, pm_message_t state);
+int snd_ymfpci_resume(struct pci_dev *pci);
+
+int snd_ymfpci_pcm(ymfpci_t *chip, int device, snd_pcm_t **rpcm);
+int snd_ymfpci_pcm2(ymfpci_t *chip, int device, snd_pcm_t **rpcm);
+int snd_ymfpci_pcm_spdif(ymfpci_t *chip, int device, snd_pcm_t **rpcm);
+int snd_ymfpci_pcm_4ch(ymfpci_t *chip, int device, snd_pcm_t **rpcm);
+int snd_ymfpci_mixer(ymfpci_t *chip, int rear_switch);
+int snd_ymfpci_timer(ymfpci_t *chip, int device);
+#if defined(CONFIG_GAMEPORT) || defined(CONFIG_GAMEPORT_MODULE)
+int snd_ymfpci_joystick(ymfpci_t *chip);
+#endif
+
+int snd_ymfpci_voice_alloc(ymfpci_t *chip, ymfpci_voice_type_t type, int pair, ymfpci_voice_t **rvoice);
+int snd_ymfpci_voice_free(ymfpci_t *chip, ymfpci_voice_t *pvoice);
+
+#endif /* __SOUND_YMFPCI_H */
Index: /GPL/branches/alsa-resync1/alsa-kernel/include/sound/yss225.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/include/sound/yss225.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/include/sound/yss225.h	(revision 91)
@@ -0,0 +1,24 @@
+#ifndef __yss255_h__
+#define __yss255_h__
+
+extern unsigned char page_zero[256];
+extern unsigned char page_one[256];
+extern unsigned char page_two[128];
+extern unsigned char page_three[128];
+extern unsigned char page_four[128];
+extern unsigned char page_six[192];
+extern unsigned char page_seven[256];
+extern unsigned char page_zero_v2[96];
+extern unsigned char page_one_v2[96];
+extern unsigned char page_two_v2[48];
+extern unsigned char page_three_v2[48];
+extern unsigned char page_four_v2[48];
+extern unsigned char page_seven_v2[96];
+extern unsigned char mod_v2[304];
+extern unsigned char coefficients[364];
+extern unsigned char coefficients2[56];
+extern unsigned char coefficients3[404];
+
+
+#endif /* __ys225_h__ */
+
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/ad1816a/ad1816a.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/ad1816a/ad1816a.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/ad1816a/ad1816a.c	(revision 91)
@@ -0,0 +1,390 @@
+
+/*
+    card-ad1816a.c - driver for ADI SoundPort AD1816A based soundcards.
+    Copyright (C) 2000 by Massimo Piccioni <dafastidio@libero.it>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#define SNDRV_MAIN_OBJECT_FILE
+#include <sound/driver.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+#include <sound/ad1816a.h>
+#include <sound/mpu401.h>
+#include <sound/opl3.h>
+
+#define chip_t ad1816a_t
+
+EXPORT_NO_SYMBOLS;
+MODULE_DESCRIPTION("AD1816A, AD1815");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{Highscreen,Sound-Boostar 16 3D},"
+		"{Analog Devices,AD1815},"
+		"{Analog Devices,AD1816A},"
+		"{TerraTec,Base 64},"
+		"{Aztech/Newcom SC-16 3D}}");
+
+static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 1-MAX */
+static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE;	/* Enable this card */
+static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* PnP setup */
+static long snd_mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* PnP setup */
+static long snd_fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* PnP setup */
+static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* Pnp setup */
+static int snd_mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* Pnp setup */
+static int snd_dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* PnP setup */
+static int snd_dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* PnP setup */
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for ad1816a based soundcard.");
+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 ad1816a based soundcard.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable ad1816a based soundcard.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_port, "Port # for ad1816a driver.");
+MODULE_PARM_SYNTAX(snd_port, SNDRV_PORT12_DESC);
+MODULE_PARM(snd_mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_mpu_port, "MPU-401 port # for ad1816a driver.");
+MODULE_PARM_SYNTAX(snd_mpu_port, SNDRV_PORT12_DESC);
+MODULE_PARM(snd_fm_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_fm_port, "FM port # for ad1816a driver.");
+MODULE_PARM_SYNTAX(snd_fm_port, SNDRV_PORT12_DESC);
+MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_irq, "IRQ # for ad1816a driver.");
+MODULE_PARM_SYNTAX(snd_irq, SNDRV_IRQ_DESC);
+MODULE_PARM(snd_mpu_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_mpu_irq, "MPU-401 IRQ # for ad1816a driver.");
+MODULE_PARM_SYNTAX(snd_mpu_irq, SNDRV_IRQ_DESC);
+MODULE_PARM(snd_dma1, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_dma1, "1st DMA # for ad1816a driver.");
+MODULE_PARM_SYNTAX(snd_dma1, SNDRV_DMA_DESC);
+MODULE_PARM(snd_dma2, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_dma2, "2nd DMA # for ad1816a driver.");
+MODULE_PARM_SYNTAX(snd_dma2, SNDRV_DMA_DESC);
+
+struct snd_card_ad1816a {
+#ifdef __ISAPNP__
+	struct isapnp_dev *dev;
+	struct isapnp_dev *devmpu;
+#endif	/* __ISAPNP__ */
+};
+
+static snd_card_t *snd_ad1816a_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+
+#ifdef __ISAPNP__
+
+static struct isapnp_card *snd_ad1816a_isapnp_cards[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR;
+static const struct isapnp_card_id *snd_ad1816a_isapnp_id[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR;
+
+#ifdef TARGET_OS2
+#define ISAPNP_AD1816A(_va, _vb, _vc, _device, _fa, _fb, _fc, _audio, _mpu401) \
+	{ \
+		0, ISAPNP_CARD_ID(_va, _vb, _vc, _device), \
+		{ ISAPNP_DEVICE_ID(_fa, _fb, _fc, _audio), \
+		  ISAPNP_DEVICE_ID(_fa, _fb, _fc, _mpu401), } \
+	}
+#else
+#define ISAPNP_AD1816A(_va, _vb, _vc, _device, _fa, _fb, _fc, _audio, _mpu401) \
+	{ \
+		ISAPNP_CARD_ID(_va, _vb, _vc, _device), \
+		devs : { ISAPNP_DEVICE_ID(_fa, _fb, _fc, _audio), \
+			 ISAPNP_DEVICE_ID(_fa, _fb, _fc, _mpu401), } \
+	}
+#endif
+
+static struct isapnp_card_id snd_ad1816a_pnpids[] __devinitdata = {
+	/* Highscreen Sound-Boostar 16 3D */
+	ISAPNP_AD1816A('M','D','K',0x1605,'A','D','S',0x7180,0x7181),
+	/* Highscreen Sound-Boostar 16 3D - added by Stefan Behnel */
+	ISAPNP_AD1816A('L','W','C',0x1061,'A','D','S',0x7180,0x7181),
+	/* Analog Devices AD1815 */
+	ISAPNP_AD1816A('A','D','S',0x7150,'A','D','S',0x7150,0x7151),
+	/* Analog Devices AD1816A - added by Kenneth Platz <kxp@atl.hp.com> */
+	ISAPNP_AD1816A('A','D','S',0x7181,'A','D','S',0x7180,0x7181),
+	/* Analog Devices AD1816A - Terratec Base 64 */
+	ISAPNP_AD1816A('T','E','R',0x1411,'A','D','S',0x7180,0x7181),
+	/* Analog Devices AD1816A - Aztech/Newcom SC-16 3D */
+	ISAPNP_AD1816A('A','Z','T',0x1022,'A','Z','T',0x1018,0x2002),
+	{ ISAPNP_CARD_END, }
+};
+
+ISAPNP_CARD_TABLE(snd_ad1816a_pnpids);
+
+#endif	/* __ISAPNP__ */
+
+#define	DRIVER_NAME	"snd-card-ad1816a"
+
+
+#ifdef __ISAPNP__
+static int __init snd_card_ad1816a_isapnp(int dev,
+					  struct snd_card_ad1816a *acard)
+{
+	const struct isapnp_card_id *id = snd_ad1816a_isapnp_id[dev];
+	struct isapnp_card *card = snd_ad1816a_isapnp_cards[dev];
+	struct isapnp_dev *pdev;
+
+	acard->dev = isapnp_find_dev(card, id->devs[0].vendor, id->devs[0].function, NULL);
+	if (acard->dev->active) {
+		acard->dev = NULL;
+		return -EBUSY;
+	}
+	acard->devmpu = isapnp_find_dev(card, id->devs[1].vendor, id->devs[1].function, NULL);
+	if (acard->devmpu->active) {
+		acard->dev = acard->devmpu = NULL;
+		return -EBUSY;
+	}
+
+	pdev = acard->dev;
+	if (pdev->prepare(pdev) < 0)
+		return -EAGAIN;
+
+	if (snd_port[dev] != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[2], snd_port[dev], 16);
+	if (snd_fm_port[dev] != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[1], snd_fm_port[dev], 4);
+	if (snd_dma1[dev] != SNDRV_AUTO_DMA)
+		isapnp_resource_change(&pdev->dma_resource[0], snd_dma1[dev],
+			1);
+	if (snd_dma2[dev] != SNDRV_AUTO_DMA)
+		isapnp_resource_change(&pdev->dma_resource[1], snd_dma2[dev],
+			1);
+	if (snd_irq[dev] != SNDRV_AUTO_IRQ)
+		isapnp_resource_change(&pdev->irq_resource[0], snd_irq[dev], 1);
+
+	if (pdev->activate(pdev) < 0) {
+		snd_printk("AUDIO isapnp configure failure\n");
+		return -EBUSY;
+	}
+
+	snd_port[dev] = pdev->resource[2].start;
+	snd_fm_port[dev] = pdev->resource[1].start;
+	snd_dma1[dev] = pdev->dma_resource[0].start;
+	snd_dma2[dev] = pdev->dma_resource[1].start;
+	snd_irq[dev] = pdev->irq_resource[0].start;
+
+	pdev = acard->devmpu;
+	if (pdev == NULL || pdev->prepare(pdev) < 0) {
+		snd_mpu_port[dev] = -1;
+		acard->devmpu = NULL;
+		return 0;
+	}
+
+	if (snd_mpu_port[dev] != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[0], snd_mpu_port[dev],
+			2);
+	if (snd_mpu_irq[dev] != SNDRV_AUTO_IRQ)
+		isapnp_resource_change(&pdev->irq_resource[0], snd_mpu_irq[dev],
+			1);
+
+	if (pdev->activate(pdev) < 0) {
+		/* not fatal error */
+		snd_printk("MPU-401 isapnp configure failure\n");
+		snd_mpu_port[dev] = -1;
+		acard->devmpu = NULL;
+	} else {
+		snd_mpu_port[dev] = pdev->resource[0].start;
+		snd_mpu_irq[dev] = pdev->irq_resource[0].start;
+	}
+
+	return 0;
+}
+
+static void snd_card_ad1816a_deactivate(struct snd_card_ad1816a *acard)
+{
+	if (acard->dev) {
+		acard->dev->deactivate(acard->dev);
+		acard->dev = NULL;
+	}
+	if (acard->devmpu) {
+		acard->devmpu->deactivate(acard->devmpu);
+		acard->devmpu = NULL;
+	}
+}
+#endif	/* __ISAPNP__ */
+
+static void snd_card_ad1816a_free(snd_card_t *card)
+{
+	struct snd_card_ad1816a *acard = (struct snd_card_ad1816a *)card->private_data;
+
+	if (acard) {
+#ifdef __ISAPNP__
+		snd_card_ad1816a_deactivate(acard);
+#endif	/* __ISAPNP__ */
+	}
+}
+
+static int __init snd_card_ad1816a_probe(int dev)
+{
+	int error;
+	snd_card_t *card;
+	struct snd_card_ad1816a *acard;
+	ad1816a_t *chip;
+	opl3_t *opl3;
+
+	if ((card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE,
+				 sizeof(struct snd_card_ad1816a))) == NULL)
+		return -ENOMEM;
+	acard = (struct snd_card_ad1816a *)card->private_data;
+	card->private_free = snd_card_ad1816a_free;
+
+#ifdef __ISAPNP__
+	if ((error = snd_card_ad1816a_isapnp(dev, acard))) {
+		snd_card_free(card);
+		return error;
+	}
+#else
+	snd_printk("you have to enable ISA PnP support.\n");
+	return -ENOSYS;
+#endif	/* __ISAPNP__ */
+
+	if ((error = snd_ad1816a_create(card, snd_port[dev],
+					snd_irq[dev],
+					snd_dma1[dev],
+					snd_dma2[dev],
+					&chip)) < 0) {
+		snd_card_free(card);
+		return error;
+	}
+
+	if ((error = snd_ad1816a_pcm(chip, 0, NULL)) < 0) {
+		snd_card_free(card);
+		return error;
+	}
+
+	if ((error = snd_ad1816a_mixer(chip)) < 0) {
+		snd_card_free(card);
+		return error;
+	}
+
+	if (snd_mpu_port[dev] > 0) {
+		if (snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
+					snd_mpu_port[dev], 0, snd_mpu_irq[dev], SA_INTERRUPT,
+					NULL) < 0)
+			snd_printk("no MPU-401 device at 0x%lx.\n", snd_mpu_port[dev]);
+	}
+
+	if (snd_fm_port[dev] > 0) {
+		if (snd_opl3_create(card,
+				    snd_fm_port[dev], snd_fm_port[dev] + 2,
+				    OPL3_HW_AUTO, 0, &opl3) < 0) {
+			snd_printk("no OPL device at 0x%lx-0x%lx.\n", snd_fm_port[dev], snd_fm_port[dev] + 2);
+		} else {
+			if ((error = snd_opl3_timer_new(opl3, 1, 2)) < 0) {
+				snd_card_free(card);
+				return error;
+			}
+			if ((error = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
+				snd_card_free(card);
+				return error;
+			}
+		}
+	}
+
+	strcpy(card->driver, "AD1816A");
+	strcpy(card->shortname, "ADI SoundPort AD1816A");
+	sprintf(card->longname, "%s soundcard, SS at 0x%lx, irq %d, dma %d&%d",
+		card->shortname, chip->port, snd_irq[dev], snd_dma1[dev], snd_dma2[dev]);
+
+	if ((error = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return error;
+	}
+	snd_ad1816a_cards[dev] = card;
+	return 0;
+}
+
+#ifdef __ISAPNP__
+static int __init snd_ad1816a_isapnp_detect(struct isapnp_card *card,
+					    const struct isapnp_card_id *id)
+{
+	static int dev = 0;
+	int res;
+
+	for ( ; dev < SNDRV_CARDS; dev++) {
+		if (!snd_enable[dev])
+			continue;
+		snd_ad1816a_isapnp_cards[dev] = card;
+		snd_ad1816a_isapnp_id[dev] = id;
+		res = snd_card_ad1816a_probe(dev);
+		if (res < 0)
+			return res;
+		dev++;
+		return 0;
+	}
+        return -ENODEV;
+}
+#endif
+
+static int __init alsa_card_ad1816a_init(void)
+{
+	int cards = 0;
+
+#ifdef __ISAPNP__
+	cards += isapnp_probe_cards(snd_ad1816a_pnpids, snd_ad1816a_isapnp_detect);
+#else
+	snd_printk("you have to enable ISA PnP support.\n");
+#endif
+#ifdef MODULE
+	if (!cards)
+		snd_printk("no AD1816A based soundcards found.\n");
+#endif	/* MODULE */
+	return cards ? 0 : -ENODEV;
+}
+
+static void __exit alsa_card_ad1816a_exit(void)
+{
+	int dev;
+
+	for (dev = 0; dev < SNDRV_CARDS; dev++)
+		snd_card_free(snd_ad1816a_cards[dev]);
+}
+
+module_init(alsa_card_ad1816a_init)
+module_exit(alsa_card_ad1816a_exit)
+
+#ifndef MODULE
+
+/* format is: snd-card-ad1816a=snd_enable,snd_index,snd_id,snd_port,
+			       snd_mpu_port,snd_fm_port,snd_irq,snd_mpu_irq,
+			       snd_dma1,snd_dma2 */
+
+static int __init alsa_card_ad1816a_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_port[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_mpu_port[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_fm_port[nr_dev]) == 2 &&
+	       get_option(&str,&snd_irq[nr_dev]) == 2 &&
+	       get_option(&str,&snd_mpu_irq[nr_dev]) == 2 &&
+	       get_option(&str,&snd_dma1[nr_dev]) == 2 &&
+	       get_option(&str,&snd_dma2[nr_dev]) == 2);
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-card-ad1816a=", alsa_card_ad1816a_setup);
+
+#endif /* ifndef MODULE */
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/ad1816a/ad1816a_lib.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/ad1816a/ad1816a_lib.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/ad1816a/ad1816a_lib.c	(revision 91)
@@ -0,0 +1,1054 @@
+
+/*
+    ad1816a.c - lowlevel code for Analog Devices AD1816A chip.
+    Copyright (C) 1999-2000 by Massimo Piccioni <dafastidio@libero.it>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#define SNDRV_MAIN_OBJECT_FILE
+#include <sound/driver.h>
+#include <sound/ad1816a.h>
+
+#define chip_t ad1816a_t
+
+static inline int snd_ad1816a_busy_wait(ad1816a_t *chip)
+{
+	int timeout;
+
+	for (timeout = 1000; timeout-- > 0; udelay(10))
+		if (inb(AD1816A_REG(AD1816A_CHIP_STATUS)) & AD1816A_READY)
+			return 0;
+
+	snd_printk("chip busy.\n");
+	return -EBUSY;
+}
+
+inline unsigned char snd_ad1816a_in(ad1816a_t *chip, unsigned char reg)
+{
+	snd_ad1816a_busy_wait(chip);
+	return inb(AD1816A_REG(reg));
+}
+
+inline void snd_ad1816a_out(ad1816a_t *chip, unsigned char reg,
+			    unsigned char value)
+{
+	snd_ad1816a_busy_wait(chip);
+	outb(value, AD1816A_REG(reg));
+}
+
+inline void snd_ad1816a_out_mask(ad1816a_t *chip, unsigned char reg,
+				 unsigned char mask, unsigned char value)
+{
+	snd_ad1816a_out(chip, reg,
+		(value & mask) | (snd_ad1816a_in(chip, reg) & ~mask));
+}
+
+static unsigned short snd_ad1816a_read(ad1816a_t *chip, unsigned char reg)
+{
+	snd_ad1816a_out(chip, AD1816A_INDIR_ADDR, reg & 0x3f);
+	return snd_ad1816a_in(chip, AD1816A_INDIR_DATA_LOW) |
+		(snd_ad1816a_in(chip, AD1816A_INDIR_DATA_HIGH) << 8);
+}
+
+static void snd_ad1816a_write(ad1816a_t *chip, unsigned char reg,
+			      unsigned short value)
+{
+	snd_ad1816a_out(chip, AD1816A_INDIR_ADDR, reg & 0x3f);
+	snd_ad1816a_out(chip, AD1816A_INDIR_DATA_LOW, value & 0xff);
+	snd_ad1816a_out(chip, AD1816A_INDIR_DATA_HIGH, (value >> 8) & 0xff);
+}
+
+static void snd_ad1816a_write_mask(ad1816a_t *chip, unsigned char reg,
+				   unsigned short mask, unsigned short value)
+{
+	snd_ad1816a_write(chip, reg,
+		(value & mask) | (snd_ad1816a_read(chip, reg) & ~mask));
+}
+
+
+static unsigned char snd_ad1816a_get_format(ad1816a_t *chip,
+					    unsigned int format, int channels)
+{
+	unsigned char retval = AD1816A_FMT_LINEAR_8;
+
+	switch (format) {
+	case SNDRV_PCM_FORMAT_MU_LAW:
+		retval = AD1816A_FMT_ULAW_8;
+		break;
+	case SNDRV_PCM_FORMAT_A_LAW:
+		retval = AD1816A_FMT_ALAW_8;
+		break;
+	case SNDRV_PCM_FORMAT_S16_LE:
+		retval = AD1816A_FMT_LINEAR_16_LIT;
+		break;
+	case SNDRV_PCM_FORMAT_S16_BE:
+		retval = AD1816A_FMT_LINEAR_16_BIG;
+	}
+	return (channels > 1) ? (retval | AD1816A_FMT_STEREO) : retval;
+}
+
+static int snd_ad1816a_open(ad1816a_t *chip, unsigned int mode)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->lock, flags);
+
+	if (chip->mode & mode) {
+		spin_unlock_irqrestore(&chip->lock, flags);
+		return -EAGAIN;
+	}
+
+	switch ((mode &= AD1816A_MODE_OPEN)) {
+	case AD1816A_MODE_PLAYBACK:
+		snd_ad1816a_out_mask(chip, AD1816A_INTERRUPT_STATUS,
+			AD1816A_PLAYBACK_IRQ_PENDING, 0x00);
+		snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE,
+			AD1816A_PLAYBACK_IRQ_ENABLE, 0xffff);
+		break;
+	case AD1816A_MODE_CAPTURE:
+		snd_ad1816a_out_mask(chip, AD1816A_INTERRUPT_STATUS,
+			AD1816A_CAPTURE_IRQ_PENDING, 0x00);
+		snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE,
+			AD1816A_CAPTURE_IRQ_ENABLE, 0xffff);
+		break;
+	case AD1816A_MODE_TIMER:
+		snd_ad1816a_out_mask(chip, AD1816A_INTERRUPT_STATUS,
+			AD1816A_TIMER_IRQ_PENDING, 0x00);
+		snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE,
+			AD1816A_TIMER_IRQ_ENABLE, 0xffff);
+	}
+	chip->mode |= mode;
+
+	spin_unlock_irqrestore(&chip->lock, flags);
+	return 0;
+}
+
+static void snd_ad1816a_close(ad1816a_t *chip, unsigned int mode)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->lock, flags);
+
+	switch ((mode &= AD1816A_MODE_OPEN)) {
+	case AD1816A_MODE_PLAYBACK:
+		snd_ad1816a_out_mask(chip, AD1816A_INTERRUPT_STATUS,
+			AD1816A_PLAYBACK_IRQ_PENDING, 0x00);
+		snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE,
+			AD1816A_PLAYBACK_IRQ_ENABLE, 0x0000);
+		break;
+	case AD1816A_MODE_CAPTURE:
+		snd_ad1816a_out_mask(chip, AD1816A_INTERRUPT_STATUS,
+			AD1816A_CAPTURE_IRQ_PENDING, 0x00);
+		snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE,
+			AD1816A_CAPTURE_IRQ_ENABLE, 0x0000);
+		break;
+	case AD1816A_MODE_TIMER:
+		snd_ad1816a_out_mask(chip, AD1816A_INTERRUPT_STATUS,
+			AD1816A_TIMER_IRQ_PENDING, 0x00);
+		snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE,
+			AD1816A_TIMER_IRQ_ENABLE, 0x0000);
+	}
+	if (!((chip->mode &= ~mode) & AD1816A_MODE_OPEN))
+		chip->mode = 0;
+
+	spin_unlock_irqrestore(&chip->lock, flags);
+}
+
+
+static int snd_ad1816a_trigger(ad1816a_t *chip, unsigned char what,
+			       int channel, int cmd)
+{
+	int error = 0;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_STOP:
+		spin_lock(&chip->lock);
+		cmd = (cmd == SNDRV_PCM_TRIGGER_START) ? 0xff: 0x00;
+		if (what & AD1816A_PLAYBACK_ENABLE)
+			snd_ad1816a_out_mask(chip, AD1816A_PLAYBACK_CONFIG,
+				AD1816A_PLAYBACK_ENABLE, cmd);
+		if (what & AD1816A_CAPTURE_ENABLE)
+			snd_ad1816a_out_mask(chip, AD1816A_CAPTURE_CONFIG,
+				AD1816A_CAPTURE_ENABLE, cmd);
+		spin_unlock(&chip->lock);
+		break;
+	default:
+		snd_printk("invalid trigger mode 0x%x.\n", what);
+		error = -EINVAL;
+	}
+
+	return error;
+}
+
+static int snd_ad1816a_playback_trigger(snd_pcm_substream_t *substream, int cmd)
+{
+	ad1816a_t *chip = snd_pcm_substream_chip(substream);
+	return snd_ad1816a_trigger(chip, AD1816A_PLAYBACK_ENABLE,
+		SNDRV_PCM_STREAM_PLAYBACK, cmd);
+}
+
+static int snd_ad1816a_capture_trigger(snd_pcm_substream_t *substream, int cmd)
+{
+	ad1816a_t *chip = snd_pcm_substream_chip(substream);
+	return snd_ad1816a_trigger(chip, AD1816A_CAPTURE_ENABLE,
+		SNDRV_PCM_STREAM_CAPTURE, cmd);
+}
+
+static int snd_ad1816a_hw_params(snd_pcm_substream_t * substream,
+				 snd_pcm_hw_params_t * hw_params)
+{
+	return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+static int snd_ad1816a_hw_free(snd_pcm_substream_t * substream)
+{
+	return snd_pcm_lib_free_pages(substream);
+}
+
+static int snd_ad1816a_playback_prepare(snd_pcm_substream_t *substream)
+{
+	ad1816a_t *chip = snd_pcm_substream_chip(substream);
+	unsigned long flags;
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	unsigned int size;
+
+	spin_lock_irqsave(&chip->lock, flags);
+
+	chip->p_dma_size = size = snd_pcm_lib_buffer_bytes(substream);
+	snd_ad1816a_out_mask(chip, AD1816A_PLAYBACK_CONFIG,
+		AD1816A_PLAYBACK_ENABLE | AD1816A_PLAYBACK_PIO, 0x00);
+
+	snd_dma_program(chip->dma1, runtime->dma_area, size,
+			DMA_MODE_WRITE | DMA_AUTOINIT);
+
+	snd_ad1816a_write(chip, AD1816A_PLAYBACK_SAMPLE_RATE, runtime->rate);
+	snd_ad1816a_out_mask(chip, AD1816A_PLAYBACK_CONFIG,
+		AD1816A_FMT_ALL | AD1816A_FMT_STEREO,
+		snd_ad1816a_get_format(chip, runtime->format,
+			runtime->channels));
+
+	snd_ad1816a_write(chip, AD1816A_PLAYBACK_BASE_COUNT,
+		snd_pcm_lib_period_bytes(substream) / 4 - 1);
+
+	spin_unlock_irqrestore(&chip->lock, flags);
+	return 0;
+}
+
+static int snd_ad1816a_capture_prepare(snd_pcm_substream_t *substream)
+{
+	ad1816a_t *chip = snd_pcm_substream_chip(substream);
+	unsigned long flags;
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	unsigned int size;
+
+	spin_lock_irqsave(&chip->lock, flags);
+
+	chip->c_dma_size = size = snd_pcm_lib_buffer_bytes(substream);
+	snd_ad1816a_out_mask(chip, AD1816A_CAPTURE_CONFIG,
+		AD1816A_CAPTURE_ENABLE | AD1816A_CAPTURE_PIO, 0x00);
+
+	snd_dma_program(chip->dma2, runtime->dma_area, size,
+			DMA_MODE_READ | DMA_AUTOINIT);
+
+	snd_ad1816a_write(chip, AD1816A_CAPTURE_SAMPLE_RATE, runtime->rate);
+	snd_ad1816a_out_mask(chip, AD1816A_CAPTURE_CONFIG,
+		AD1816A_FMT_ALL | AD1816A_FMT_STEREO,
+		snd_ad1816a_get_format(chip, runtime->format,
+			runtime->channels));
+
+	snd_ad1816a_write(chip, AD1816A_CAPTURE_BASE_COUNT,
+		snd_pcm_lib_period_bytes(substream) / 4 - 1);
+
+	spin_unlock_irqrestore(&chip->lock, flags);
+	return 0;
+}
+
+
+static snd_pcm_uframes_t snd_ad1816a_playback_pointer(snd_pcm_substream_t *substream)
+{
+	ad1816a_t *chip = snd_pcm_substream_chip(substream);
+	size_t ptr;
+	if (!(chip->mode & AD1816A_MODE_PLAYBACK))
+		return 0;
+	ptr = chip->p_dma_size - snd_dma_residue(chip->dma1);
+	return bytes_to_frames(substream->runtime, ptr);
+}
+
+static snd_pcm_uframes_t snd_ad1816a_capture_pointer(snd_pcm_substream_t *substream)
+{
+	ad1816a_t *chip = snd_pcm_substream_chip(substream);
+	size_t ptr;
+	if (!(chip->mode & AD1816A_MODE_CAPTURE))
+		return 0;
+	ptr = chip->c_dma_size - snd_dma_residue(chip->dma2);
+	return bytes_to_frames(substream->runtime, ptr);
+}
+
+
+static void snd_ad1816a_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	ad1816a_t *chip = snd_magic_cast(ad1816a_t, dev_id, return);
+	unsigned char status;
+
+	spin_lock(&chip->lock);
+	status = snd_ad1816a_in(chip, AD1816A_INTERRUPT_STATUS);
+	spin_unlock(&chip->lock);
+
+	if ((status & AD1816A_PLAYBACK_IRQ_PENDING) && chip->playback_substream)
+		snd_pcm_period_elapsed(chip->playback_substream);
+
+	if ((status & AD1816A_CAPTURE_IRQ_PENDING) && chip->capture_substream)
+		snd_pcm_period_elapsed(chip->capture_substream);
+
+	if ((status & AD1816A_TIMER_IRQ_PENDING) && chip->timer)
+		snd_timer_interrupt(chip->timer, chip->timer->sticks);
+
+	spin_lock(&chip->lock);
+	snd_ad1816a_out(chip, AD1816A_INTERRUPT_STATUS, 0x00);
+	spin_unlock(&chip->lock);
+}
+
+#ifdef TARGET_OS2
+static snd_pcm_hardware_t snd_ad1816a_playback = {
+/*	info:		  */	(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+/*	formats:	  */	(SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW |
+				 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE |
+				 SNDRV_PCM_FMTBIT_S16_BE),
+/*	rates:		  */	SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+/*	rate_min:	  */	4000,
+/*	rate_max:	  */	55200,
+/*	channels_min:	  */	1,
+/*	channels_max:	  */	2,
+/*	buffer_bytes_max:  */	(128*1024),
+/*	period_bytes_min:  */	64,
+/*	period_bytes_max:  */	(128*1024),
+/*	periods_min:	  */	1,
+/*	periods_max:	  */	1024,
+/*	fifo_size:	  */	0,
+};
+
+static snd_pcm_hardware_t snd_ad1816a_capture = {
+/*	info:		  */	(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+/*	formats:	  */	(SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW |
+				 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE |
+				 SNDRV_PCM_FMTBIT_S16_BE),
+/*	rates:		  */	SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+/*	rate_min:	  */	4000,
+/*	rate_max:	  */	55200,
+/*	channels_min:	  */	1,
+/*	channels_max:	  */	2,
+/*	buffer_bytes_max:  */	(128*1024),
+/*	period_bytes_min:  */	64,
+/*	period_bytes_max:  */	(128*1024),
+/*	periods_min:	  */	1,
+/*	periods_max:	  */	1024,
+/*	fifo_size:	  */	0,
+};
+#else
+static snd_pcm_hardware_t snd_ad1816a_playback = {
+	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+	formats:		(SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW |
+				 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE |
+				 SNDRV_PCM_FMTBIT_S16_BE),
+	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	rate_min:		4000,
+	rate_max:		55200,
+	channels_min:		1,
+	channels_max:		2,
+	buffer_bytes_max:	(128*1024),
+	period_bytes_min:	64,
+	period_bytes_max:	(128*1024),
+	periods_min:		1,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+
+static snd_pcm_hardware_t snd_ad1816a_capture = {
+	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+	formats:		(SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW |
+				 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE |
+				 SNDRV_PCM_FMTBIT_S16_BE),
+	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	rate_min:		4000,
+	rate_max:		55200,
+	channels_min:		1,
+	channels_max:		2,
+	buffer_bytes_max:	(128*1024),
+	period_bytes_min:	64,
+	period_bytes_max:	(128*1024),
+	periods_min:		1,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+#endif
+
+static int snd_ad1816a_timer_close(snd_timer_t *timer)
+{
+	ad1816a_t *chip = snd_timer_chip(timer);
+	snd_ad1816a_close(chip, AD1816A_MODE_TIMER);
+	return 0;
+}
+
+static int snd_ad1816a_timer_open(snd_timer_t *timer)
+{
+	ad1816a_t *chip = snd_timer_chip(timer);
+	snd_ad1816a_open(chip, AD1816A_MODE_TIMER);
+	return 0;
+}
+
+static unsigned long snd_ad1816a_timer_resolution(snd_timer_t *timer)
+{
+	snd_assert(timer != NULL, return 0);
+
+	return 10000;
+}
+
+static int snd_ad1816a_timer_start(snd_timer_t *timer)
+{
+	unsigned short bits;
+	unsigned long flags;
+	ad1816a_t *chip = snd_timer_chip(timer);
+	spin_lock_irqsave(&chip->lock, flags);
+	bits = snd_ad1816a_read(chip, AD1816A_INTERRUPT_ENABLE);
+
+	if (!(bits & AD1816A_TIMER_ENABLE)) {
+		snd_ad1816a_write(chip, AD1816A_TIMER_BASE_COUNT,
+			timer->sticks & 0xffff);
+
+		snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE,
+			AD1816A_TIMER_ENABLE, 0xffff);
+	}
+	spin_unlock_irqrestore(&chip->lock, flags);
+	return 0;
+}
+
+static int snd_ad1816a_timer_stop(snd_timer_t *timer)
+{
+	unsigned long flags;
+	ad1816a_t *chip = snd_timer_chip(timer);
+	spin_lock_irqsave(&chip->lock, flags);
+
+	snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE,
+		AD1816A_TIMER_ENABLE, 0x0000);
+
+	spin_unlock_irqrestore(&chip->lock, flags);
+	return 0;
+}
+
+#ifdef TARGET_OS2
+static struct _snd_timer_hardware snd_ad1816a_timer_table = {
+	SNDRV_TIMER_HW_AUTO,
+	10000,
+	65535,
+	snd_ad1816a_timer_open,
+	snd_ad1816a_timer_close,
+	snd_ad1816a_timer_resolution,
+	snd_ad1816a_timer_start,
+	snd_ad1816a_timer_stop,
+};
+#else
+static struct _snd_timer_hardware snd_ad1816a_timer_table = {
+	flags:		SNDRV_TIMER_HW_AUTO,
+	resolution:	10000,
+	ticks:		65535,
+	open:		snd_ad1816a_timer_open,
+	close:		snd_ad1816a_timer_close,
+	c_resolution:	snd_ad1816a_timer_resolution,
+	start:		snd_ad1816a_timer_start,
+	stop:		snd_ad1816a_timer_stop,
+};
+#endif
+
+static int snd_ad1816a_playback_open(snd_pcm_substream_t *substream)
+{
+	ad1816a_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int error;
+
+	if ((error = snd_ad1816a_open(chip, AD1816A_MODE_PLAYBACK)) < 0)
+		return error;
+	snd_pcm_set_sync(substream);
+	runtime->hw = snd_ad1816a_playback;
+	snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.buffer_bytes_max);
+	snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.period_bytes_max);
+	return 0;
+}
+
+static int snd_ad1816a_capture_open(snd_pcm_substream_t *substream)
+{
+	ad1816a_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int error;
+
+	if ((error = snd_ad1816a_open(chip, AD1816A_MODE_CAPTURE)) < 0)
+		return error;
+	snd_pcm_set_sync(substream);
+	runtime->hw = snd_ad1816a_capture;
+	snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.buffer_bytes_max);
+	snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.period_bytes_max);
+	return 0;
+}
+
+static int snd_ad1816a_playback_close(snd_pcm_substream_t *substream)
+{
+	ad1816a_t *chip = snd_pcm_substream_chip(substream);
+
+	chip->playback_substream = NULL;
+	snd_ad1816a_close(chip, AD1816A_MODE_PLAYBACK);
+	return 0;
+}
+
+static int snd_ad1816a_capture_close(snd_pcm_substream_t *substream)
+{
+	ad1816a_t *chip = snd_pcm_substream_chip(substream);
+
+	chip->capture_substream = NULL;
+	snd_ad1816a_close(chip, AD1816A_MODE_CAPTURE);
+	return 0;
+}
+
+
+static void snd_ad1816a_init(ad1816a_t *chip)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->lock, flags);
+
+	snd_ad1816a_out(chip, AD1816A_INTERRUPT_STATUS, 0x00);
+	snd_ad1816a_out_mask(chip, AD1816A_PLAYBACK_CONFIG,
+		AD1816A_PLAYBACK_ENABLE | AD1816A_PLAYBACK_PIO, 0x00);
+	snd_ad1816a_out_mask(chip, AD1816A_CAPTURE_CONFIG,
+		AD1816A_CAPTURE_ENABLE | AD1816A_CAPTURE_PIO, 0x00);
+	snd_ad1816a_write(chip, AD1816A_INTERRUPT_ENABLE, 0x0000);
+	snd_ad1816a_write_mask(chip, AD1816A_CHIP_CONFIG,
+		AD1816A_CAPTURE_NOT_EQUAL | AD1816A_WSS_ENABLE, 0xffff);
+	snd_ad1816a_write(chip, AD1816A_DSP_CONFIG, 0x0000);
+	snd_ad1816a_write(chip, AD1816A_POWERDOWN_CTRL, 0x0000);
+
+	spin_unlock_irqrestore(&chip->lock, flags);
+}
+
+static int snd_ad1816a_probe(ad1816a_t *chip)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->lock, flags);
+
+	switch (chip->version = snd_ad1816a_read(chip, AD1816A_VERSION_ID)) {
+	case 0:
+		chip->hardware = AD1816A_HW_AD1815;
+		break;
+	case 1:
+		chip->hardware = AD1816A_HW_AD18MAX10;
+		break;
+	case 3:
+		chip->hardware = AD1816A_HW_AD1816A;
+		break;
+	default:
+		chip->hardware = AD1816A_HW_AUTO;
+	}
+
+	spin_unlock_irqrestore(&chip->lock, flags);
+	return 0;
+}
+
+static int snd_ad1816a_free(ad1816a_t *chip)
+{
+	if (chip->res_port)
+		release_resource(chip->res_port);
+	if (chip->irq >= 0)
+		free_irq(chip->irq, (void *) chip);
+	if (chip->dma1 >= 0) {
+		snd_dma_disable(chip->dma1);
+		free_dma(chip->dma1);
+	}
+	if (chip->dma2 >= 0) {
+		snd_dma_disable(chip->dma2);
+		free_dma(chip->dma2);
+	}
+	snd_magic_kfree(chip);
+	return 0;
+}
+
+static int snd_ad1816a_dev_free(snd_device_t *device)
+{
+	ad1816a_t *chip = snd_magic_cast(ad1816a_t, device->device_data, return -ENXIO);
+	return snd_ad1816a_free(chip);
+}
+
+static const char *snd_ad1816a_chip_id(ad1816a_t *chip)
+{
+	switch (chip->hardware) {
+	case AD1816A_HW_AD1816A: return "AD1816A";
+	case AD1816A_HW_AD1815:	return "AD1815";
+	case AD1816A_HW_AD18MAX10: return "AD18max10";
+	default:
+		snd_printk("Unknown chip version %d:%d.\n",
+			chip->version, chip->hardware);
+		return "AD1816A - unknown";
+	}
+}
+
+int snd_ad1816a_create(snd_card_t *card,
+		       unsigned long port, int irq, int dma1, int dma2,
+		       ad1816a_t **rchip)
+{
+#ifdef TARGET_OS2
+        static snd_device_ops_t ops = {
+		snd_ad1816a_dev_free,0,0
+	};
+#else
+        static snd_device_ops_t ops = {
+		dev_free:       snd_ad1816a_dev_free,
+	};
+#endif
+	int error;
+	ad1816a_t *chip;
+
+	*rchip = NULL;
+
+	chip = snd_magic_kcalloc(ad1816a_t, 0, GFP_KERNEL);
+	if (chip == NULL)
+		return -ENOMEM;
+	chip->irq = -1;
+	chip->dma1 = -1;
+	chip->dma2 = -1;
+
+	if ((chip->res_port = request_region(port, 16, "AD1816A")) == NULL) {
+		snd_ad1816a_free(chip);
+		return -EBUSY;
+	}
+	if (request_irq(irq, snd_ad1816a_interrupt, SA_INTERRUPT, "AD1816A", (void *) chip)) {
+		snd_ad1816a_free(chip);
+		return -EBUSY;
+	}
+	chip->irq = irq;
+	if (request_dma(dma1, "AD1816A - 1")) {
+		snd_ad1816a_free(chip);
+		return -EBUSY;
+	}
+	chip->dma1 = dma1;
+	if (request_dma(dma2, "AD1816A - 2")) {
+		snd_ad1816a_free(chip);
+		return -EBUSY;
+	}
+	chip->dma2 = dma2;
+
+	chip->card = card;
+	chip->port = port;
+	spin_lock_init(&chip->lock);
+
+	if ((error = snd_ad1816a_probe(chip))) {
+		snd_ad1816a_free(chip);
+		return error;
+	}
+
+	snd_ad1816a_init(chip);
+
+	/* Register device */
+	if ((error = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+		snd_ad1816a_free(chip);
+		return error;
+	}
+
+	*rchip = chip;
+	return 0;
+}
+
+#ifdef TARGET_OS2
+static snd_pcm_ops_t snd_ad1816a_playback_ops = {
+	snd_ad1816a_playback_open,
+	snd_ad1816a_playback_close,
+	snd_pcm_lib_ioctl,
+	snd_ad1816a_hw_params,
+	snd_ad1816a_hw_free,
+	snd_ad1816a_playback_prepare,
+	snd_ad1816a_playback_trigger,
+	snd_ad1816a_playback_pointer,0,0
+};
+
+static snd_pcm_ops_t snd_ad1816a_capture_ops = {
+	snd_ad1816a_capture_open,
+	snd_ad1816a_capture_close,
+	snd_pcm_lib_ioctl,
+	snd_ad1816a_hw_params,
+	snd_ad1816a_hw_free,
+	snd_ad1816a_capture_prepare,
+	snd_ad1816a_capture_trigger,
+	snd_ad1816a_capture_pointer,0,0
+};
+#else
+static snd_pcm_ops_t snd_ad1816a_playback_ops = {
+	open:		snd_ad1816a_playback_open,
+	close:		snd_ad1816a_playback_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	hw_params:	snd_ad1816a_hw_params,
+	hw_free:	snd_ad1816a_hw_free,
+	prepare:	snd_ad1816a_playback_prepare,
+	trigger:	snd_ad1816a_playback_trigger,
+	pointer:	snd_ad1816a_playback_pointer,
+};
+
+static snd_pcm_ops_t snd_ad1816a_capture_ops = {
+	open:		snd_ad1816a_capture_open,
+	close:		snd_ad1816a_capture_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	hw_params:	snd_ad1816a_hw_params,
+	hw_free:	snd_ad1816a_hw_free,
+	prepare:	snd_ad1816a_capture_prepare,
+	trigger:	snd_ad1816a_capture_trigger,
+	pointer:	snd_ad1816a_capture_pointer,
+};
+#endif
+
+static void snd_ad1816a_pcm_free(snd_pcm_t *pcm)
+{
+	ad1816a_t *chip = snd_magic_cast(ad1816a_t, pcm->private_data, return);
+	chip->pcm = NULL;
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+int snd_ad1816a_pcm(ad1816a_t *chip, int device, snd_pcm_t **rpcm)
+{
+	int error;
+	snd_pcm_t *pcm;
+
+	if ((error = snd_pcm_new(chip->card, "AD1816A", device, 1, 1, &pcm)))
+		return error;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ad1816a_playback_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ad1816a_capture_ops);
+
+	pcm->private_data = chip;
+	pcm->private_free = snd_ad1816a_pcm_free;
+	pcm->info_flags = (chip->dma1 == chip->dma2 ) ? SNDRV_PCM_INFO_JOINT_DUPLEX : 0;
+
+	strcpy(pcm->name, snd_ad1816a_chip_id(chip));
+	snd_ad1816a_init(chip);
+
+	snd_pcm_lib_preallocate_pages_for_all(pcm, 64*1024, chip->dma1 > 3 || chip->dma2 > 3 ? 128*1024 : 64*1024, GFP_KERNEL|GFP_DMA);
+
+	chip->pcm = pcm;
+	if (rpcm)
+		*rpcm = pcm;
+	return 0;
+}
+
+static void snd_ad1816a_timer_free(snd_timer_t *timer)
+{
+	ad1816a_t *chip = snd_magic_cast(ad1816a_t, timer->private_data, return);
+	chip->timer = NULL;
+}
+
+int snd_ad1816a_timer(ad1816a_t *chip, int device, snd_timer_t **rtimer)
+{
+	snd_timer_t *timer;
+	snd_timer_id_t tid;
+	int error;
+
+	tid.dev_class = SNDRV_TIMER_CLASS_CARD;
+	tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE;
+	tid.card = chip->card->number;
+	tid.device = device;
+	tid.subdevice = 0;
+	if ((error = snd_timer_new(chip->card, "AD1816A", &tid, &timer)) < 0)
+		return error;
+	strcpy(timer->name, snd_ad1816a_chip_id(chip));
+	timer->private_data = chip;
+	timer->private_free = snd_ad1816a_timer_free;
+	chip->timer = timer;
+	timer->hw = snd_ad1816a_timer_table;
+	if (rtimer)
+		*rtimer = timer;
+	return 0;
+}
+
+/*
+ *
+ */
+
+static int snd_ad1816a_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	static char *texts[8] = {
+		"Line", "Mix", "CD", "Synth", "Video",
+		"Mic", "Phone",
+	};
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 2;
+	uinfo->value.enumerated.items = 7;
+	if (uinfo->value.enumerated.item > 6)
+		uinfo->value.enumerated.item = 6;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int snd_ad1816a_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ad1816a_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	unsigned short val;
+	
+	spin_lock_irqsave(&chip->lock, flags);
+	val = snd_ad1816a_read(chip, AD1816A_ADC_SOURCE_SEL);
+	spin_unlock_irqrestore(&chip->lock, flags);
+	ucontrol->value.enumerated.item[0] = (val >> 12) & 7;
+	ucontrol->value.enumerated.item[1] = (val >> 4) & 7;
+	return 0;
+}
+
+static int snd_ad1816a_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ad1816a_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	unsigned short val;
+	int change;
+	
+	if (ucontrol->value.enumerated.item[0] > 6 ||
+	    ucontrol->value.enumerated.item[1] > 6)
+		return -EINVAL;
+	val = (ucontrol->value.enumerated.item[0] << 12) |
+	      (ucontrol->value.enumerated.item[1] << 4);
+	spin_lock_irqsave(&chip->lock, flags);
+	change = snd_ad1816a_read(chip, AD1816A_ADC_SOURCE_SEL) != val;
+	snd_ad1816a_write(chip, AD1816A_ADC_SOURCE_SEL, val);
+	spin_unlock_irqrestore(&chip->lock, flags);
+	return change;
+}
+
+#ifdef TARGET_OS2
+#define AD1816A_SINGLE(xname, reg, shift, mask, invert) \
+{ SNDRV_CTL_ELEM_IFACE_MIXER, 0,0,xname, 0, 0, 0, snd_ad1816a_info_single, \
+  snd_ad1816a_get_single, snd_ad1816a_put_single, \
+  reg | (shift << 8) | (mask << 16) | (invert << 24) }
+#else
+#define AD1816A_SINGLE(xname, reg, shift, mask, invert) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, info: snd_ad1816a_info_single, \
+  get: snd_ad1816a_get_single, put: snd_ad1816a_put_single, \
+  private_value: reg | (shift << 8) | (mask << 16) | (invert << 24) }
+#endif
+
+static int snd_ad1816a_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+
+	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	return 0;
+}
+
+static int snd_ad1816a_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ad1816a_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 8) & 0xff;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0xff;
+	
+	spin_lock_irqsave(&chip->lock, flags);
+	ucontrol->value.integer.value[0] = (snd_ad1816a_read(chip, reg) >> shift) & mask;
+	spin_unlock_irqrestore(&chip->lock, flags);
+	if (invert)
+		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+	return 0;
+}
+
+static int snd_ad1816a_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ad1816a_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 8) & 0xff;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0xff;
+	int change;
+	unsigned short old_val, val;
+	
+	val = (ucontrol->value.integer.value[0] & mask);
+	if (invert)
+		val = mask - val;
+	val <<= shift;
+	spin_lock_irqsave(&chip->lock, flags);
+	old_val = snd_ad1816a_read(chip, reg);
+	val = (old_val & ~(mask << shift)) | val;
+	change = val != old_val;
+	snd_ad1816a_write(chip, reg, val);
+	spin_unlock_irqrestore(&chip->lock, flags);
+	return change;
+}
+
+#ifdef TARGET_OS2
+#define AD1816A_DOUBLE(xname, reg, shift_left, shift_right, mask, invert) \
+{ SNDRV_CTL_ELEM_IFACE_MIXER, 0, 0, xname, 0, 0, 0, snd_ad1816a_info_double, \
+  snd_ad1816a_get_double, snd_ad1816a_put_double, \
+  reg | (shift_left << 8) | (shift_right << 12) | (mask << 16) | (invert << 24) }
+#else
+#define AD1816A_DOUBLE(xname, reg, shift_left, shift_right, mask, invert) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, info: snd_ad1816a_info_double, \
+  get: snd_ad1816a_get_double, put: snd_ad1816a_put_double, \
+  private_value: reg | (shift_left << 8) | (shift_right << 12) | (mask << 16) | (invert << 24) }
+#endif
+
+static int snd_ad1816a_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+
+	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	return 0;
+}
+
+static int snd_ad1816a_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ad1816a_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int reg = kcontrol->private_value & 0xff;
+	int shift_left = (kcontrol->private_value >> 8) & 0x0f;
+	int shift_right = (kcontrol->private_value >> 12) & 0x0f;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0xff;
+	unsigned short val;
+	
+	spin_lock_irqsave(&chip->lock, flags);
+	val = snd_ad1816a_read(chip, reg);
+	ucontrol->value.integer.value[0] = (val >> shift_left) & mask;
+	ucontrol->value.integer.value[1] = (val >> shift_right) & mask;
+	spin_unlock_irqrestore(&chip->lock, flags);
+	if (invert) {
+		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+		ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1];
+	}
+	return 0;
+}
+
+static int snd_ad1816a_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ad1816a_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int reg = kcontrol->private_value & 0xff;
+	int shift_left = (kcontrol->private_value >> 8) & 0x0f;
+	int shift_right = (kcontrol->private_value >> 12) & 0x0f;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0xff;
+	int change;
+	unsigned short old_val, val1, val2;
+	
+	val1 = ucontrol->value.integer.value[0] & mask;
+	val2 = ucontrol->value.integer.value[1] & mask;
+	if (invert) {
+		val1 = mask - val1;
+		val2 = mask - val2;
+	}
+	val1 <<= shift_left;
+	val2 <<= shift_right;
+	spin_lock_irqsave(&chip->lock, flags);
+	old_val = snd_ad1816a_read(chip, reg);
+	val1 = (old_val & ~((mask << shift_left) | (mask << shift_right))) | val1 | val2;
+	change = val1 != old_val;
+	snd_ad1816a_write(chip, reg, val1);
+	spin_unlock_irqrestore(&chip->lock, flags);
+	return change;
+}
+
+#define AD1816A_CONTROLS (sizeof(snd_ad1816a_controls)/sizeof(snd_kcontrol_new_t))
+
+static snd_kcontrol_new_t snd_ad1816a_controls[] = {
+AD1816A_DOUBLE("Master Playback Switch", AD1816A_MASTER_ATT, 15, 7, 1, 1),
+AD1816A_DOUBLE("Master Playback Volume", AD1816A_MASTER_ATT, 8, 0, 31, 0),
+AD1816A_DOUBLE("PCM Playback Switch", AD1816A_VOICE_ATT, 15, 7, 1, 1),
+AD1816A_DOUBLE("PCM Playback Volume", AD1816A_VOICE_ATT, 8, 0, 63, 0),
+AD1816A_DOUBLE("Line Playback Switch", AD1816A_LINE_GAIN_ATT, 15, 7, 1, 1),
+AD1816A_DOUBLE("Line Playback Volume", AD1816A_LINE_GAIN_ATT, 8, 0, 31, 0),
+AD1816A_DOUBLE("CD Playback Switch", AD1816A_CD_GAIN_ATT, 15, 7, 1, 1),
+AD1816A_DOUBLE("CD Playback Volume", AD1816A_CD_GAIN_ATT, 8, 0, 31, 0),
+AD1816A_DOUBLE("Synth Playback Switch", AD1816A_SYNTH_GAIN_ATT, 15, 7, 1, 1),
+AD1816A_DOUBLE("Synth Playback Volume", AD1816A_SYNTH_GAIN_ATT, 8, 0, 31, 0),
+AD1816A_DOUBLE("FM Playback Switch", AD1816A_FM_ATT, 15, 7, 1, 1),
+AD1816A_DOUBLE("FM Playback Volume", AD1816A_FM_ATT, 8, 0, 63, 0),
+AD1816A_SINGLE("Mic Playback Switch", AD1816A_MIC_GAIN_ATT, 15, 1, 1),
+AD1816A_SINGLE("Mic Playback Volume", AD1816A_MIC_GAIN_ATT, 8, 63, 0),
+AD1816A_SINGLE("Mic Boost", AD1816A_MIC_GAIN_ATT, 14, 1, 0),
+AD1816A_DOUBLE("Video Playback Switch", AD1816A_VID_GAIN_ATT, 15, 7, 1, 1),
+AD1816A_DOUBLE("Video Playback Volume", AD1816A_VID_GAIN_ATT, 8, 0, 31, 0),
+AD1816A_SINGLE("Phone Capture Switch", AD1816A_PHONE_IN_GAIN_ATT, 15, 1, 1),
+AD1816A_SINGLE("Phone Capture Volume", AD1816A_PHONE_IN_GAIN_ATT, 0, 31, 0),
+AD1816A_SINGLE("Phone Playback Switch", AD1816A_PHONE_OUT_ATT, 7, 1, 1),
+AD1816A_SINGLE("Phone Playback Volume", AD1816A_PHONE_OUT_ATT, 0, 31, 0),
+{
+#ifdef TARGET_OS2
+	SNDRV_CTL_ELEM_IFACE_MIXER,0,0,
+	"Capture Source",0,0, 0,
+	snd_ad1816a_info_mux,
+	snd_ad1816a_get_mux,
+	snd_ad1816a_put_mux,0
+#else
+	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	name: "Capture Source",
+	info: snd_ad1816a_info_mux,
+	get: snd_ad1816a_get_mux,
+	put: snd_ad1816a_put_mux,
+#endif
+},
+AD1816A_DOUBLE("Capture Switch", AD1816A_ADC_PGA, 15, 7, 1, 1),
+AD1816A_DOUBLE("Capture Volume", AD1816A_ADC_PGA, 8, 0, 15, 0),
+AD1816A_SINGLE("3D Control - Switch", AD1816A_3D_PHAT_CTRL, 15, 1, 1),
+AD1816A_SINGLE("3D Control - Level", AD1816A_3D_PHAT_CTRL, 0, 15, 0),
+};
+                                        
+int snd_ad1816a_mixer(ad1816a_t *chip)
+{
+	snd_card_t *card;
+	int err, idx;
+
+	snd_assert(chip != NULL && chip->card != NULL, return -EINVAL);
+
+	card = chip->card;
+
+	strcpy(card->mixername, snd_ad1816a_chip_id(chip));
+
+	for (idx = 0; idx < AD1816A_CONTROLS; idx++) {
+		if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_ad1816a_controls[idx], chip))) < 0)
+			return err;
+	}
+	return 0;
+}
+
+EXPORT_SYMBOL(snd_ad1816a_create);
+EXPORT_SYMBOL(snd_ad1816a_pcm);
+EXPORT_SYMBOL(snd_ad1816a_mixer);
+
+static int __init alsa_ad1816a_init(void)
+{
+	return 0;
+}
+
+static void __exit alsa_ad1816a_exit(void)
+{
+}
+
+module_init(alsa_ad1816a_init)
+module_exit(alsa_ad1816a_exit)
+
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/ad1816a/makefile.os2
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/ad1816a/makefile.os2	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/ad1816a/makefile.os2	(revision 91)
@@ -0,0 +1,50 @@
+CINCLUDES = ..\..\..\include;..\..\include;$(%WATCOM)\H
+AINCLUDES = $(DDK_BASE)\H
+
+CDEFINES = -D__KERNEL__ -DMODULE -dTARGET_OS2 -dALSA_BUILD -D__ISAPNP__ -DCONFIG_ISAPNP -D__i386_
+
+32BIT=1
+!include ..\..\..\..\makefile.inc
+
+
+#===================================================================
+#
+#   List of source files
+#
+#===================================================================
+FILE1    = ad1816a_lib.obj ad1816a.obj
+FILE2    = 
+FILE3    = 
+FILE4    = 
+FILE5    = 
+FILE6    = 
+FILE7    = 
+FILE8    = 
+FILELAST =
+FILES    = $(FILE0) $(FILE1) $(FILE2) $(FILE3) $(FILE4) $(FILE5) $(FILE6) $(FILE7) $(FILE8) $(FILE9) $(FILE10) $(FILE11) $(FILE12)
+
+TARGET   = ad1816a
+
+LNKFILE  = $(OBJDIR)\$(TARGET).lnk
+
+
+#===================================================================
+#
+#   Specific dependencies
+#
+#===================================================================
+all: $(OBJDIR)\$(TARGET).lib 
+
+
+$(LNKFILE): makefile.os2
+    @%create $(LNKFILE)
+    @for %f in ($(FILES)) do @%append $(LNKFILE) -+$(OBJDIR)\%f
+
+$(OBJDIR)\$(TARGET).lib: $(LNKFILE) $(FILES)
+    $(LIB) $(OBJDIR)\$(TARGET).lib @$(LNKFILE)
+    copy $(OBJDIR)\$(TARGET).lib $(ALSA_LIB) 
+
+clean: .SYMBOLIC
+     cd $(OBJDIR)
+     -del *.obj *.err *.lnk *.sys *.sym *.lst *.map *.wmap *.lib
+     cd ..
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/ad1848/ad1848.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/ad1848/ad1848.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/ad1848/ad1848.c	(revision 91)
@@ -0,0 +1,174 @@
+/*
+ *  Generic driver for AD1848/AD1847/CS4248 chips (0.1 Alpha)
+ *  Copyright (c) by Tugrul Galatali <galatalt@stuy.edu>,
+ *                   Jaroslav Kysela <perex@suse.cz>
+ *  Based on card-4232.c by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#define SNDRV_MAIN_OBJECT_FILE
+#include <sound/driver.h>
+#include <sound/ad1848.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+#define chip_t ad1848_t
+
+EXPORT_NO_SYMBOLS;
+MODULE_DESCRIPTION("AD1848/AD1847/CS4248");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{Analog Devices,AD1848},"
+	        "{Analog Devices,AD1847},"
+		"{Crystal Semiconductors,CS4248}}");
+
+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;	/* Enable this card */
+static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* PnP setup */
+static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* 5,7,9,11,12,15 */
+static int snd_dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* 0,1,3,5,6,7 */
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for AD1848 soundcard.");
+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 AD1848 soundcard.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable AD1848 soundcard.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_port, "Port # for AD1848 driver.");
+MODULE_PARM_SYNTAX(snd_port, SNDRV_PORT12_DESC);
+MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_irq, "IRQ # for AD1848 driver.");
+MODULE_PARM_SYNTAX(snd_irq, SNDRV_IRQ_DESC);
+MODULE_PARM(snd_dma1, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_dma1, "DMA1 # for AD1848 driver.");
+MODULE_PARM_SYNTAX(snd_dma1, SNDRV_DMA_DESC);
+
+static snd_card_t *snd_ad1848_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+
+
+static int __init snd_card_ad1848_probe(int dev)
+{
+	snd_card_t *card;
+	ad1848_t *chip;
+	snd_pcm_t *pcm;
+	int err;
+
+	if (snd_port[dev] == SNDRV_AUTO_PORT) {
+		snd_printk("specify snd_port\n");
+		return -EINVAL;
+	}
+	if (snd_irq[dev] == SNDRV_AUTO_IRQ) {
+		snd_printk("specify snd_irq\n");
+		return -EINVAL;
+	}
+	if (snd_dma1[dev] == SNDRV_AUTO_DMA) {
+		snd_printk("specify snd_dma1\n");
+		return -EINVAL;
+	}
+
+	card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0);
+	if (card == NULL)
+		return -ENOMEM;
+
+	if ((err = snd_ad1848_create(card, snd_port[dev],
+				     snd_irq[dev],
+				     snd_dma1[dev],
+				     AD1848_HW_DETECT,
+				     &chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	if ((err = snd_ad1848_pcm(chip, 0, &pcm)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_ad1848_mixer(chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	strcpy(card->driver, "AD1848");
+	strcpy(card->shortname, pcm->name);
+
+	sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d",
+		pcm->name, chip->port, snd_irq[dev], snd_dma1[dev]);
+
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	snd_ad1848_cards[dev] = card;
+	return 0;
+}
+
+static int __init alsa_card_ad1848_init(void)
+{
+	int dev, cards;
+
+	for (dev = cards = 0; dev < SNDRV_CARDS && snd_enable[dev]; dev++)
+		if (snd_card_ad1848_probe(dev) >= 0)
+			cards++;
+
+	if (!cards) {
+#ifdef MODULE
+		snd_printk("AD1848 soundcard not found or device busy\n");
+#endif
+		return -ENODEV;
+	}
+	return 0;
+}
+
+static void __exit alsa_card_ad1848_exit(void)
+{
+	int idx;
+
+	for (idx = 0; idx < SNDRV_CARDS; idx++)
+		snd_card_free(snd_ad1848_cards[idx]);
+}
+
+module_init(alsa_card_ad1848_init)
+module_exit(alsa_card_ad1848_exit)
+
+#ifndef MODULE
+
+/* format is: snd-card-ad1848=snd_enable,snd_index,snd_id,snd_port,
+			      snd_irq,snd_dma1 */
+
+static int __init alsa_card_ad1848_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_port[nr_dev]) == 2 &&
+	       get_option(&str,&snd_irq[nr_dev]) == 2 &&
+	       get_option(&str,&snd_dma1[nr_dev]) == 2);
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-card-ad1848=", alsa_card_ad1848_setup);
+
+#endif /* ifndef MODULE */
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/ad1848/ad1848_lib.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/ad1848/ad1848_lib.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/ad1848/ad1848_lib.c	(revision 91)
@@ -0,0 +1,1240 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  Routines for control of AD1848/AD1847/CS4248
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#define SNDRV_MAIN_OBJECT_FILE
+#include <sound/driver.h>
+#include <sound/ad1848.h>
+
+#define chip_t ad1848_t
+
+#if 0
+#define SNDRV_DEBUG_MCE
+#endif
+
+/*
+ *  Some variables
+ */
+
+static unsigned char freq_bits[14] = {
+	/* 5510 */	0x00 | AD1848_XTAL2,
+	/* 6620 */	0x0E | AD1848_XTAL2,
+	/* 8000 */	0x00 | AD1848_XTAL1,
+	/* 9600 */	0x0E | AD1848_XTAL1,
+	/* 11025 */	0x02 | AD1848_XTAL2,
+	/* 16000 */	0x02 | AD1848_XTAL1,
+	/* 18900 */	0x04 | AD1848_XTAL2,
+	/* 22050 */	0x06 | AD1848_XTAL2,
+	/* 27042 */	0x04 | AD1848_XTAL1,
+	/* 32000 */	0x06 | AD1848_XTAL1,
+	/* 33075 */	0x0C | AD1848_XTAL2,
+	/* 37800 */	0x08 | AD1848_XTAL2,
+	/* 44100 */	0x0A | AD1848_XTAL2,
+	/* 48000 */	0x0C | AD1848_XTAL1
+};
+
+static unsigned int rates[14] = {
+	5510, 6620, 8000, 9600, 11025, 16000, 18900, 22050,
+	27042, 32000, 33075, 37800, 44100, 48000
+};
+
+#ifdef TARGET_OS2
+static snd_pcm_hw_constraint_list_t hw_constraints_rates = {
+	14,
+	rates,
+	0,
+};
+#else
+static snd_pcm_hw_constraint_list_t hw_constraints_rates = {
+	count: 14,
+	list: rates,
+	mask: 0,
+};
+#endif
+
+static unsigned char snd_ad1848_original_image[16] =
+{
+	0x00,			/* 00 - lic */
+	0x00,			/* 01 - ric */
+	0x9f,			/* 02 - la1ic */
+	0x9f,			/* 03 - ra1ic */
+	0x9f,			/* 04 - la2ic */
+	0x9f,			/* 05 - ra2ic */
+	0xbf,			/* 06 - loc */
+	0xbf,			/* 07 - roc */
+	0x20,			/* 08 - dfr */
+	AD1848_AUTOCALIB,	/* 09 - ic */
+	0x00,			/* 0a - pc */
+	0x00,			/* 0b - ti */
+	0x00,			/* 0c - mi */
+	0x00,			/* 0d - lbc */
+	0x00,			/* 0e - dru */
+	0x00,			/* 0f - drl */
+};
+
+/*
+ *  Basic I/O functions
+ */
+
+void snd_ad1848_out(ad1848_t *chip,
+			   unsigned char reg,
+			   unsigned char value)
+{
+	int timeout;
+
+	for (timeout = 250; timeout > 0 && (inb(AD1848P(chip, REGSEL)) & AD1848_INIT); timeout--)
+		udelay(100);
+#ifdef CONFIG_SND_DEBUG
+	if (inb(AD1848P(chip, REGSEL)) & AD1848_INIT)
+		snd_printk("auto calibration time out - reg = 0x%x, value = 0x%x\n", reg, value);
+#endif
+	outb(chip->mce_bit | reg, AD1848P(chip, REGSEL));
+	outb(chip->image[reg] = value, AD1848P(chip, REG));
+	mb();
+#if 0
+	printk("codec out - reg 0x%x = 0x%x\n", chip->mce_bit | reg, value);
+#endif
+}
+
+static void snd_ad1848_dout(ad1848_t *chip,
+			    unsigned char reg,
+			    unsigned char value)
+{
+	int timeout;
+
+	for (timeout = 250; timeout > 0 && (inb(AD1848P(chip, REGSEL)) & AD1848_INIT); timeout--)
+		udelay(100);
+	outb(chip->mce_bit | reg, AD1848P(chip, REGSEL));
+	outb(value, AD1848P(chip, REG));
+	mb();
+}
+
+static unsigned char snd_ad1848_in(ad1848_t *chip, unsigned char reg)
+{
+	int timeout;
+
+	for (timeout = 250; timeout > 0 && (inb(AD1848P(chip, REGSEL)) & AD1848_INIT); timeout--)
+		udelay(100);
+#ifdef CONFIG_SND_DEBUG
+	if (inb(AD1848P(chip, REGSEL)) & AD1848_INIT)
+		snd_printk("auto calibration time out - reg = 0x%x\n", reg);
+#endif
+	outb(chip->mce_bit | reg, AD1848P(chip, REGSEL));
+	mb();
+	return inb(AD1848P(chip, REG));
+}
+
+#ifdef CONFIG_SND_DEBUG
+
+void snd_ad1848_debug(ad1848_t *chip)
+{
+	printk("AD1848 REGS:      INDEX = 0x%02x  ", inb(AD1848P(chip, REGSEL)));
+	printk("                 STATUS = 0x%02x\n", inb(AD1848P(chip, STATUS)));
+	printk("  0x00: left input      = 0x%02x  ", snd_ad1848_in(chip, 0x00));
+	printk("  0x08: playback format = 0x%02x\n", snd_ad1848_in(chip, 0x08));
+	printk("  0x01: right input     = 0x%02x  ", snd_ad1848_in(chip, 0x01));
+	printk("  0x09: iface (CFIG 1)  = 0x%02x\n", snd_ad1848_in(chip, 0x09));
+	printk("  0x02: AUXA left       = 0x%02x  ", snd_ad1848_in(chip, 0x02));
+	printk("  0x0a: pin control     = 0x%02x\n", snd_ad1848_in(chip, 0x0a));
+	printk("  0x03: AUXA right      = 0x%02x  ", snd_ad1848_in(chip, 0x03));
+	printk("  0x0b: init & status   = 0x%02x\n", snd_ad1848_in(chip, 0x0b));
+	printk("  0x04: AUXB left       = 0x%02x  ", snd_ad1848_in(chip, 0x04));
+	printk("  0x0c: revision & mode = 0x%02x\n", snd_ad1848_in(chip, 0x0c));
+	printk("  0x05: AUXB right      = 0x%02x  ", snd_ad1848_in(chip, 0x05));
+	printk("  0x0d: loopback        = 0x%02x\n", snd_ad1848_in(chip, 0x0d));
+	printk("  0x06: left output     = 0x%02x  ", snd_ad1848_in(chip, 0x06));
+	printk("  0x0e: data upr count  = 0x%02x\n", snd_ad1848_in(chip, 0x0e));
+	printk("  0x07: right output    = 0x%02x  ", snd_ad1848_in(chip, 0x07));
+	printk("  0x0f: data lwr count  = 0x%02x\n", snd_ad1848_in(chip, 0x0f));
+}
+
+#endif
+
+/*
+ *  AD1848 detection / MCE routines
+ */
+
+static void snd_ad1848_mce_up(ad1848_t *chip)
+{
+	unsigned long flags;
+	int timeout;
+
+	for (timeout = 250; timeout > 0 && (inb(AD1848P(chip, REGSEL)) & AD1848_INIT); timeout--)
+		udelay(100);
+#ifdef CONFIG_SND_DEBUG
+	if (inb(AD1848P(chip, REGSEL)) & AD1848_INIT)
+		snd_printk("mce_up - auto calibration time out (0)\n");
+#endif
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	chip->mce_bit |= AD1848_MCE;
+	timeout = inb(AD1848P(chip, REGSEL));
+	if (timeout == 0x80)
+		snd_printk("mce_up [0x%lx]: serious init problem - codec still busy\n", chip->port);
+	if (!(timeout & AD1848_MCE))
+		outb(chip->mce_bit | (timeout & 0x1f), AD1848P(chip, REGSEL));
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+static void snd_ad1848_mce_down(ad1848_t *chip)
+{
+	unsigned long flags;
+	int timeout;
+	signed long time;
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	for (timeout = 5; timeout > 0; timeout--)
+		inb(AD1848P(chip, REGSEL));
+	/* end of cleanup sequence */
+	for (timeout = 12000; timeout > 0 && (inb(AD1848P(chip, REGSEL)) & AD1848_INIT); timeout--)
+		udelay(100);
+#if 0
+	printk("(1) timeout = %i\n", timeout);
+#endif
+#ifdef CONFIG_SND_DEBUG
+	if (inb(AD1848P(chip, REGSEL)) & AD1848_INIT)
+		snd_printk("mce_down [0x%lx] - auto calibration time out (0)\n", AD1848P(chip, REGSEL));
+#endif
+	chip->mce_bit &= ~AD1848_MCE;
+	timeout = inb(AD1848P(chip, REGSEL));
+	outb(chip->mce_bit | (timeout & 0x1f), AD1848P(chip, REGSEL));
+	if (timeout == 0x80)
+		snd_printk("mce_down [0x%lx]: serious init problem - codec still busy\n", chip->port);
+	if ((timeout & AD1848_MCE) == 0) {
+		spin_unlock_irqrestore(&chip->reg_lock, flags);
+		return;
+	}
+	/* calibration process */
+
+	for (timeout = 500; timeout > 0 && (snd_ad1848_in(chip, AD1848_TEST_INIT) & AD1848_CALIB_IN_PROGRESS) == 0; timeout--);
+	if ((snd_ad1848_in(chip, AD1848_TEST_INIT) & AD1848_CALIB_IN_PROGRESS) == 0) {
+		snd_printd("mce_down - auto calibration time out (1)\n");
+		spin_unlock_irqrestore(&chip->reg_lock, flags);
+		return;
+	}
+#if 0
+	printk("(2) timeout = %i, jiffies = %li\n", timeout, jiffies);
+#endif
+	time = HZ / 4;
+	while (snd_ad1848_in(chip, AD1848_TEST_INIT) & AD1848_CALIB_IN_PROGRESS) {
+		spin_unlock_irqrestore(&chip->reg_lock, flags);
+		if (time <= 0) {
+			snd_printk("mce_down - auto calibration time out (2)\n");
+			return;
+		}
+		set_current_state(TASK_INTERRUPTIBLE);
+		time = schedule_timeout(time);
+		spin_lock_irqsave(&chip->reg_lock, flags);
+	}
+#if 0
+	printk("(3) jiffies = %li\n", jiffies);
+#endif
+	time = HZ / 10;
+	while (inb(AD1848P(chip, REGSEL)) & AD1848_INIT) {
+		spin_unlock_irqrestore(&chip->reg_lock, flags);
+		if (time <= 0) {
+			snd_printk("mce_down - auto calibration time out (3)\n");
+			return;
+		}
+		set_current_state(TASK_INTERRUPTIBLE);
+		time = schedule_timeout(time);
+		spin_lock_irqsave(&chip->reg_lock, flags);
+	}
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+#if 0
+	printk("(4) jiffies = %li\n", jiffies);
+	snd_printk("mce_down - exit = 0x%x\n", inb(AD1848P(chip, REGSEL)));
+#endif
+}
+
+static unsigned int snd_ad1848_get_count(unsigned char format,
+				         unsigned int size)
+{
+	switch (format & 0xe0) {
+	case AD1848_LINEAR_16:
+		size >>= 1;
+		break;
+	}
+	if (format & AD1848_STEREO)
+		size >>= 1;
+	return size;
+}
+
+static int snd_ad1848_trigger(ad1848_t *chip, unsigned char what,
+			      int channel, int cmd)
+{
+	int result = 0;
+
+#if 0
+	printk("codec trigger!!! - what = %i, enable = %i, status = 0x%x\n", what, enable, inb(AD1848P(card, STATUS)));
+#endif
+	spin_lock(&chip->reg_lock);
+	if (cmd == SNDRV_PCM_TRIGGER_START) {
+		if (chip->image[AD1848_IFACE_CTRL] & what) {
+			spin_unlock(&chip->reg_lock);
+			return 0;
+		}
+		snd_ad1848_out(chip, AD1848_IFACE_CTRL, chip->image[AD1848_IFACE_CTRL] |= what);
+	} else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+		if (!(chip->image[AD1848_IFACE_CTRL] & what)) {
+			spin_unlock(&chip->reg_lock);
+			return 0;
+		}
+		snd_ad1848_out(chip, AD1848_IFACE_CTRL, chip->image[AD1848_IFACE_CTRL] &= ~what);
+	} else {
+		result = -EINVAL;
+	}
+	spin_unlock(&chip->reg_lock);
+	return result;
+}
+
+/*
+ *  CODEC I/O
+ */
+
+static unsigned char snd_ad1848_get_rate(unsigned int rate)
+{
+	int i;
+
+	for (i = 0; i < 14; i++)
+		if (rate == rates[i])
+			return freq_bits[i];
+	snd_BUG();
+	return freq_bits[13];
+}
+
+static int snd_ad1848_ioctl(snd_pcm_substream_t * substream,
+			    unsigned int cmd, void *arg)
+{
+	return snd_pcm_lib_ioctl(substream, cmd, arg);
+}
+
+static unsigned char snd_ad1848_get_format(int format, int channels)
+{
+	unsigned char rformat;
+
+	rformat = AD1848_LINEAR_8;
+	switch (format) {
+	case SNDRV_PCM_FORMAT_A_LAW:	rformat = AD1848_ALAW_8; break;
+	case SNDRV_PCM_FORMAT_MU_LAW:	rformat = AD1848_ULAW_8; break;
+	case SNDRV_PCM_FORMAT_S16_LE:	rformat = AD1848_LINEAR_16; break;
+	}
+	if (channels > 1)
+		rformat |= AD1848_STEREO;
+#if 0
+	snd_printk("get_format: 0x%x (mode=0x%x)\n", format, mode);
+#endif
+	return rformat;
+}
+
+static void snd_ad1848_calibrate_mute(ad1848_t *chip, int mute)
+{
+	unsigned long flags;
+	
+	mute = mute ? 1 : 0;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	if (chip->calibrate_mute == mute) {
+		spin_unlock_irqrestore(&chip->reg_lock, flags);
+		return;
+	}
+	if (!mute) {
+		snd_ad1848_dout(chip, AD1848_LEFT_INPUT, chip->image[AD1848_LEFT_INPUT]);
+		snd_ad1848_dout(chip, AD1848_RIGHT_INPUT, chip->image[AD1848_RIGHT_INPUT]);
+	}
+	snd_ad1848_dout(chip, AD1848_AUX1_LEFT_INPUT, mute ? 0x80 : chip->image[AD1848_AUX1_LEFT_INPUT]);
+	snd_ad1848_dout(chip, AD1848_AUX1_RIGHT_INPUT, mute ? 0x80 : chip->image[AD1848_AUX1_RIGHT_INPUT]);
+	snd_ad1848_dout(chip, AD1848_AUX2_LEFT_INPUT, mute ? 0x80 : chip->image[AD1848_AUX2_LEFT_INPUT]);
+	snd_ad1848_dout(chip, AD1848_AUX2_RIGHT_INPUT, mute ? 0x80 : chip->image[AD1848_AUX2_RIGHT_INPUT]);
+	snd_ad1848_dout(chip, AD1848_LEFT_OUTPUT, mute ? 0x80 : chip->image[AD1848_LEFT_OUTPUT]);
+	snd_ad1848_dout(chip, AD1848_RIGHT_OUTPUT, mute ? 0x80 : chip->image[AD1848_RIGHT_OUTPUT]);
+	chip->calibrate_mute = mute;
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+static void snd_ad1848_set_data_format(ad1848_t *chip, snd_pcm_hw_params_t *hw_params)
+{
+	if (hw_params == NULL) {
+		chip->image[AD1848_DATA_FORMAT] = 0x20;
+	} else {
+		chip->image[AD1848_DATA_FORMAT] =
+		    snd_ad1848_get_format(params_format(hw_params), params_channels(hw_params)) |
+		    snd_ad1848_get_rate(params_rate(hw_params));
+	}
+	// snd_printk(">>> pmode = 0x%x, dfr = 0x%x\n", pstr->mode, chip->image[AD1848_DATA_FORMAT]);
+}
+
+static int snd_ad1848_open(ad1848_t *chip, unsigned int mode)
+{
+	unsigned long flags;
+
+	down(&chip->open_mutex);
+	if (chip->mode & AD1848_MODE_OPEN) {
+		up(&chip->open_mutex);
+		return -EAGAIN;
+	}
+	snd_ad1848_mce_down(chip);
+
+#ifdef SNDRV_DEBUG_MCE
+	snd_printk("open: (1)\n");
+#endif
+	snd_ad1848_mce_up(chip);
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	chip->image[AD1848_IFACE_CTRL] &= ~(AD1848_PLAYBACK_ENABLE | AD1848_PLAYBACK_PIO |
+			     AD1848_CAPTURE_ENABLE | AD1848_CAPTURE_PIO |
+			     AD1848_CALIB_MODE);
+	chip->image[AD1848_IFACE_CTRL] |= AD1848_AUTOCALIB;
+	snd_ad1848_out(chip, AD1848_IFACE_CTRL, chip->image[AD1848_IFACE_CTRL]);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	snd_ad1848_mce_down(chip);
+
+#ifdef SNDRV_DEBUG_MCE
+	snd_printk("open: (2)\n");
+#endif
+
+	snd_ad1848_set_data_format(chip, NULL);
+
+	snd_ad1848_mce_up(chip);
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_ad1848_out(chip, AD1848_DATA_FORMAT, chip->image[AD1848_DATA_FORMAT]);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	snd_ad1848_mce_down(chip);
+
+#ifdef SNDRV_DEBUG_MCE
+	snd_printk("open: (3)\n");
+#endif
+
+	/* ok. now enable and ack CODEC IRQ */
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	outb(0, AD1848P(chip, STATUS));	/* clear IRQ */
+	outb(0, AD1848P(chip, STATUS));	/* clear IRQ */
+	chip->image[AD1848_PIN_CTRL] |= AD1848_IRQ_ENABLE;
+	snd_ad1848_out(chip, AD1848_PIN_CTRL, chip->image[AD1848_PIN_CTRL]);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+	chip->mode = mode;
+	up(&chip->open_mutex);
+
+	return 0;
+}
+
+static void snd_ad1848_close(ad1848_t *chip)
+{
+	unsigned long flags;
+
+	down(&chip->open_mutex);
+	if (!chip->mode) {
+		up(&chip->open_mutex);
+		return;
+	}
+	/* disable IRQ */
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	outb(0, AD1848P(chip, STATUS));	/* clear IRQ */
+	outb(0, AD1848P(chip, STATUS));	/* clear IRQ */
+	chip->image[AD1848_PIN_CTRL] &= ~AD1848_IRQ_ENABLE;
+	snd_ad1848_out(chip, AD1848_PIN_CTRL, chip->image[AD1848_PIN_CTRL]);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+	/* now disable capture & playback */
+
+	snd_ad1848_mce_up(chip);
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	chip->image[AD1848_IFACE_CTRL] &= ~(AD1848_PLAYBACK_ENABLE | AD1848_PLAYBACK_PIO |
+			     AD1848_CAPTURE_ENABLE | AD1848_CAPTURE_PIO);
+	snd_ad1848_out(chip, AD1848_IFACE_CTRL, chip->image[AD1848_IFACE_CTRL]);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	snd_ad1848_mce_down(chip);
+
+	/* clear IRQ again */
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	outb(0, AD1848P(chip, STATUS));	/* clear IRQ */
+	outb(0, AD1848P(chip, STATUS));	/* clear IRQ */
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+	chip->mode = 0;
+	up(&chip->open_mutex);
+}
+
+/*
+ *  ok.. exported functions..
+ */
+
+static int snd_ad1848_playback_trigger(snd_pcm_substream_t * substream,
+				       int cmd)
+{
+	ad1848_t *chip = snd_pcm_substream_chip(substream);
+	return snd_ad1848_trigger(chip, AD1848_PLAYBACK_ENABLE, SNDRV_PCM_STREAM_PLAYBACK, cmd);
+}
+
+static int snd_ad1848_capture_trigger(snd_pcm_substream_t * substream,
+				      int cmd)
+{
+	ad1848_t *chip = snd_pcm_substream_chip(substream);
+	return snd_ad1848_trigger(chip, AD1848_CAPTURE_ENABLE, SNDRV_PCM_STREAM_CAPTURE, cmd);
+}
+
+static int snd_ad1848_playback_hw_params(snd_pcm_substream_t * substream,
+					 snd_pcm_hw_params_t * hw_params)
+{
+	ad1848_t *chip = snd_pcm_substream_chip(substream);
+	unsigned long flags;
+	int err;
+
+	if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
+		return err;
+	snd_ad1848_calibrate_mute(chip, 1);
+	snd_ad1848_set_data_format(chip, hw_params);
+	snd_ad1848_mce_up(chip);
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_ad1848_out(chip, AD1848_DATA_FORMAT, chip->image[AD1848_DATA_FORMAT]);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	snd_ad1848_mce_down(chip);
+	snd_ad1848_calibrate_mute(chip, 0);
+	return 0;
+}
+
+static int snd_ad1848_playback_hw_free(snd_pcm_substream_t * substream)
+{
+	return snd_pcm_lib_free_pages(substream);
+}
+
+static int snd_ad1848_playback_prepare(snd_pcm_substream_t * substream)
+{
+	ad1848_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	unsigned long flags;
+	unsigned int size = snd_pcm_lib_buffer_bytes(substream);
+	unsigned int count = snd_pcm_lib_period_bytes(substream);
+
+	chip->dma_size = size;
+	chip->image[AD1848_IFACE_CTRL] &= ~(AD1848_PLAYBACK_ENABLE | AD1848_PLAYBACK_PIO);
+	snd_dma_program(chip->dma, runtime->dma_area, size, DMA_MODE_WRITE | DMA_AUTOINIT);
+	count = snd_ad1848_get_count(chip->image[AD1848_DATA_FORMAT], count) - 1;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_ad1848_out(chip, AD1848_DATA_LWR_CNT, (unsigned char) count);
+	snd_ad1848_out(chip, AD1848_DATA_UPR_CNT, (unsigned char) (count >> 8));
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return 0;
+}
+
+static int snd_ad1848_capture_hw_params(snd_pcm_substream_t * substream,
+					snd_pcm_hw_params_t * hw_params)
+{
+	ad1848_t *chip = snd_pcm_substream_chip(substream);
+	unsigned long flags;
+	int err;
+
+	if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
+		return err;
+	snd_ad1848_calibrate_mute(chip, 1);
+	snd_ad1848_set_data_format(chip, hw_params);
+	snd_ad1848_mce_up(chip);
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_ad1848_out(chip, AD1848_DATA_FORMAT, chip->image[AD1848_DATA_FORMAT]);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	snd_ad1848_mce_down(chip);
+	snd_ad1848_calibrate_mute(chip, 0);
+	return 0;
+}
+
+static int snd_ad1848_capture_hw_free(snd_pcm_substream_t * substream)
+{
+	return snd_pcm_lib_free_pages(substream);
+}
+
+static int snd_ad1848_capture_prepare(snd_pcm_substream_t * substream)
+{
+	ad1848_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	unsigned long flags;
+	unsigned int size = snd_pcm_lib_buffer_bytes(substream);
+	unsigned int count = snd_pcm_lib_period_bytes(substream);
+
+	chip->dma_size = size;
+	chip->image[AD1848_IFACE_CTRL] &= ~(AD1848_CAPTURE_ENABLE | AD1848_CAPTURE_PIO);
+	snd_dma_program(chip->dma, runtime->dma_area, size, DMA_MODE_READ | DMA_AUTOINIT);
+	count = snd_ad1848_get_count(chip->image[AD1848_DATA_FORMAT], count) - 1;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_ad1848_out(chip, AD1848_DATA_LWR_CNT, (unsigned char) count);
+	snd_ad1848_out(chip, AD1848_DATA_UPR_CNT, (unsigned char) (count >> 8));
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return 0;
+}
+
+void snd_ad1848_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	ad1848_t *chip = snd_magic_cast(ad1848_t, dev_id, return);
+
+	if ((chip->mode & AD1848_MODE_PLAY) && chip->playback_substream)
+		snd_pcm_period_elapsed(chip->playback_substream);
+	if ((chip->mode & AD1848_MODE_CAPTURE) && chip->capture_substream)
+		snd_pcm_period_elapsed(chip->capture_substream);
+	outb(0, AD1848P(chip, STATUS));	/* clear global interrupt bit */
+}
+
+static snd_pcm_uframes_t snd_ad1848_playback_pointer(snd_pcm_substream_t * substream)
+{
+	ad1848_t *chip = snd_pcm_substream_chip(substream);
+	size_t ptr;
+	
+	if (!(chip->image[AD1848_IFACE_CTRL] & AD1848_PLAYBACK_ENABLE))
+		return 0;
+	ptr = chip->dma_size - snd_dma_residue(chip->dma);
+	return bytes_to_frames(substream->runtime, ptr);
+}
+
+static snd_pcm_uframes_t snd_ad1848_capture_pointer(snd_pcm_substream_t * substream)
+{
+	ad1848_t *chip = snd_pcm_substream_chip(substream);
+	size_t ptr;
+
+	if (!(chip->image[AD1848_IFACE_CTRL] & AD1848_CAPTURE_ENABLE))
+		return 0;
+	ptr = chip->dma_size - snd_dma_residue(chip->dma);
+	return bytes_to_frames(substream->runtime, ptr);
+}
+
+/*
+
+ */
+
+static int snd_ad1848_probe(ad1848_t * chip)
+{
+	unsigned long flags;
+	int i, id, rev, ad1847;
+	unsigned char *ptr;
+
+#if 0
+	snd_ad1848_debug(chip);
+#endif
+	id = ad1847 = 0;
+	for (i = 0; i < 1000; i++) {
+		mb();
+		if (inb(AD1848P(chip, REGSEL)) & AD1848_INIT)
+			udelay(500);
+		else {
+			spin_lock_irqsave(&chip->reg_lock, flags);
+			snd_ad1848_out(chip, AD1848_MISC_INFO, 0x00);
+			snd_ad1848_out(chip, AD1848_LEFT_INPUT, 0xaa);
+			snd_ad1848_out(chip, AD1848_RIGHT_INPUT, 0x45);
+			rev = snd_ad1848_in(chip, AD1848_RIGHT_INPUT);
+			if (rev == 0x65) {
+				id = 1;
+				ad1847 = 1;
+				break;
+			}
+			if (snd_ad1848_in(chip, AD1848_LEFT_INPUT) == 0xaa && rev == 0x45) {
+				id = 1;
+				break;
+			}
+			spin_unlock_irqrestore(&chip->reg_lock, flags);
+		}
+	}
+	if (id != 1)
+		return -ENODEV;	/* no valid device found */
+	if (chip->hardware == AD1848_HW_DETECT) {
+		if (ad1847) {
+			chip->hardware = AD1848_HW_AD1847;
+		} else {
+			chip->hardware = AD1848_HW_AD1848;
+			rev = snd_ad1848_in(chip, AD1848_MISC_INFO);
+			if (rev & 0x80) {
+				chip->hardware = AD1848_HW_CS4248;
+			} else if (rev & 0x0a) {
+				chip->hardware = AD1848_HW_CMI8330;
+			}
+		}
+	}
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	inb(AD1848P(chip, STATUS));	/* clear any pendings IRQ */
+	outb(0, AD1848P(chip, STATUS));
+	mb();
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+	chip->image[AD1848_MISC_INFO] = 0x00;
+	chip->image[AD1848_IFACE_CTRL] =
+	    (chip->image[AD1848_IFACE_CTRL] & ~AD1848_SINGLE_DMA) | AD1848_SINGLE_DMA;
+	ptr = (unsigned char *) &chip->image;
+	snd_ad1848_mce_down(chip);
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	for (i = 0; i < 16; i++)	/* ok.. fill all AD1848 registers */
+		snd_ad1848_out(chip, i, *ptr++);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	snd_ad1848_mce_up(chip);
+	snd_ad1848_mce_down(chip);
+	return 0;		/* all things are ok.. */
+}
+
+/*
+
+ */
+
+#ifdef TARGET_OS2
+static snd_pcm_hardware_t snd_ad1848_playback =
+{
+/*	info:		  */	(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+/*	formats:	  */	(SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW |
+				 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE),
+/*	rates:		  */	SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000,
+/*	rate_min:	  */	5510,
+/*	rate_max:	  */	48000,
+/*	channels_min:	  */	1,
+/*	channels_max:	  */	2,
+/*	buffer_bytes_max:  */	(128*1024),
+/*	period_bytes_min:  */	64,
+/*	period_bytes_max:  */	(128*1024),
+/*	periods_min:	  */	1,
+/*	periods_max:	  */	1024,
+/*	fifo_size:	  */	0,
+};
+
+static snd_pcm_hardware_t snd_ad1848_capture =
+{
+/*	info:		  */	(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+/*	formats:	  */	(SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW |
+				 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE),
+/*	rates:		  */	SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000,
+/*	rate_min:	  */	5510,
+/*	rate_max:	  */	48000,
+/*	channels_min:	  */	1,
+/*	channels_max:	  */	2,
+/*	buffer_bytes_max:  */	(128*1024),
+/*	period_bytes_min:  */	64,
+/*	period_bytes_max:  */	(128*1024),
+/*	periods_min:	  */	1,
+/*	periods_max:	  */	1024,
+/*	fifo_size:	  */	0,
+};
+#else
+static snd_pcm_hardware_t snd_ad1848_playback =
+{
+	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+	formats:		(SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW |
+				 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE),
+	rates:			SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000,
+	rate_min:		5510,
+	rate_max:		48000,
+	channels_min:		1,
+	channels_max:		2,
+	buffer_bytes_max:	(128*1024),
+	period_bytes_min:	64,
+	period_bytes_max:	(128*1024),
+	periods_min:		1,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+
+static snd_pcm_hardware_t snd_ad1848_capture =
+{
+	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+	formats:		(SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW |
+				 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE),
+	rates:			SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000,
+	rate_min:		5510,
+	rate_max:		48000,
+	channels_min:		1,
+	channels_max:		2,
+	buffer_bytes_max:	(128*1024),
+	period_bytes_min:	64,
+	period_bytes_max:	(128*1024),
+	periods_min:		1,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+#endif
+
+/*
+
+ */
+
+static int snd_ad1848_playback_open(snd_pcm_substream_t * substream)
+{
+	ad1848_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int err;
+
+	if ((err = snd_ad1848_open(chip, AD1848_MODE_PLAY)) < 0)
+		return err;
+	chip->playback_substream = substream;
+	runtime->hw = snd_ad1848_playback;
+	snd_pcm_limit_isa_dma_size(chip->dma, &runtime->hw.buffer_bytes_max);
+	snd_pcm_limit_isa_dma_size(chip->dma, &runtime->hw.period_bytes_max);
+	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates);
+	return 0;
+}
+
+static int snd_ad1848_capture_open(snd_pcm_substream_t * substream)
+{
+	ad1848_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int err;
+
+	if ((err = snd_ad1848_open(chip, AD1848_MODE_CAPTURE)) < 0)
+		return err;
+	chip->capture_substream = substream;
+	runtime->hw = snd_ad1848_capture;
+	snd_pcm_limit_isa_dma_size(chip->dma, &runtime->hw.buffer_bytes_max);
+	snd_pcm_limit_isa_dma_size(chip->dma, &runtime->hw.period_bytes_max);
+	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates);
+	return 0;
+}
+
+static int snd_ad1848_playback_close(snd_pcm_substream_t * substream)
+{
+	ad1848_t *chip = snd_pcm_substream_chip(substream);
+
+	chip->mode &= ~AD1848_MODE_PLAY;
+	chip->playback_substream = NULL;
+	snd_ad1848_close(chip);
+	return 0;
+}
+
+static int snd_ad1848_capture_close(snd_pcm_substream_t * substream)
+{
+	ad1848_t *chip = snd_pcm_substream_chip(substream);
+
+	chip->mode &= ~AD1848_MODE_CAPTURE;
+	chip->capture_substream = NULL;
+	snd_ad1848_close(chip);
+	return 0;
+}
+
+static int snd_ad1848_free(ad1848_t *chip)
+{
+	if (chip->res_port)
+		release_resource(chip->res_port);
+	if (chip->irq >= 0)
+		free_irq(chip->irq, (void *) chip);
+	if (chip->dma >= 0) {
+		snd_dma_disable(chip->dma);
+		free_dma(chip->dma);
+	}
+	snd_magic_kfree(chip);
+	return 0;
+}
+
+static int snd_ad1848_dev_free(snd_device_t *device)
+{
+	ad1848_t *chip = snd_magic_cast(ad1848_t, device->device_data, return -ENXIO);
+	return snd_ad1848_free(chip);
+}
+
+static const char *snd_ad1848_chip_id(ad1848_t *chip)
+{
+	switch (chip->hardware) {
+	case AD1848_HW_AD1847:	return "AD1847";
+	case AD1848_HW_AD1848:	return "AD1848";
+	case AD1848_HW_CS4248:	return "CS4248";
+	case AD1848_HW_CMI8330: return "CMI8330/C3D";
+	default:		return "???";
+	}
+}
+
+int snd_ad1848_create(snd_card_t * card,
+		      unsigned long port,
+		      int irq, int dma,
+		      unsigned short hardware,
+		      ad1848_t ** rchip)
+{
+#ifdef TARGET_OS2
+	static snd_device_ops_t ops = {
+		snd_ad1848_dev_free,0,0
+	};
+#else
+	static snd_device_ops_t ops = {
+		dev_free:       snd_ad1848_dev_free,
+	};
+#endif
+	ad1848_t *chip;
+	int err;
+
+	*rchip = NULL;
+	chip = snd_magic_kcalloc(ad1848_t, 0, GFP_KERNEL);
+	if (chip == NULL)
+		return -ENOMEM;
+	spin_lock_init(&chip->reg_lock);
+	init_MUTEX(&chip->open_mutex);
+	chip->card = card;
+	chip->port = port;
+	chip->irq = -1;
+	chip->dma = -1;
+	chip->hardware = hardware;
+	memcpy(&chip->image, &snd_ad1848_original_image, sizeof(snd_ad1848_original_image));
+	
+	if ((chip->res_port = request_region(port, 4, "AD1848")) == NULL) {
+		snd_ad1848_free(chip);
+		return -EBUSY;
+	}
+	if (request_irq(irq, snd_ad1848_interrupt, SA_INTERRUPT, "AD1848", (void *) chip)) {
+		snd_ad1848_free(chip);
+		return -EBUSY;
+	}
+	chip->irq = irq;
+	if (request_dma(dma, "AD1848")) {
+		snd_ad1848_free(chip);
+		return -EBUSY;
+	}
+	chip->dma = dma;
+
+	if (snd_ad1848_probe(chip) < 0) {
+		snd_ad1848_free(chip);
+		return -ENODEV;
+	}
+
+	/* Register device */
+	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+		snd_ad1848_free(chip);
+		return err;
+	}
+
+	*rchip = chip;
+	return 0;
+}
+
+#ifdef TARGET_OS2
+static snd_pcm_ops_t snd_ad1848_playback_ops = {
+	snd_ad1848_playback_open,
+	snd_ad1848_playback_close,
+	snd_ad1848_ioctl,
+	snd_ad1848_playback_hw_params,
+	snd_ad1848_playback_hw_free,
+	snd_ad1848_playback_prepare,
+	snd_ad1848_playback_trigger,
+	snd_ad1848_playback_pointer,0,0
+};
+
+static snd_pcm_ops_t snd_ad1848_capture_ops = {
+	snd_ad1848_capture_open,
+	snd_ad1848_capture_close,
+	snd_ad1848_ioctl,
+	snd_ad1848_capture_hw_params,
+	snd_ad1848_capture_hw_free,
+	snd_ad1848_capture_prepare,
+	snd_ad1848_capture_trigger,
+	snd_ad1848_capture_pointer,0,0
+};
+#else
+static snd_pcm_ops_t snd_ad1848_playback_ops = {
+	open:		snd_ad1848_playback_open,
+	close:		snd_ad1848_playback_close,
+	ioctl:		snd_ad1848_ioctl,
+	hw_params:	snd_ad1848_playback_hw_params,
+	hw_free:	snd_ad1848_playback_hw_free,
+	prepare:	snd_ad1848_playback_prepare,
+	trigger:	snd_ad1848_playback_trigger,
+	pointer:	snd_ad1848_playback_pointer,
+};
+
+static snd_pcm_ops_t snd_ad1848_capture_ops = {
+	open:		snd_ad1848_capture_open,
+	close:		snd_ad1848_capture_close,
+	ioctl:		snd_ad1848_ioctl,
+	hw_params:	snd_ad1848_capture_hw_params,
+	hw_free:	snd_ad1848_capture_hw_free,
+	prepare:	snd_ad1848_capture_prepare,
+	trigger:	snd_ad1848_capture_trigger,
+	pointer:	snd_ad1848_capture_pointer,
+};
+#endif
+
+static void snd_ad1848_pcm_free(snd_pcm_t *pcm)
+{
+	ad1848_t *chip = snd_magic_cast(ad1848_t, pcm->private_data, return);
+	chip->pcm = NULL;
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+int snd_ad1848_pcm(ad1848_t *chip, int device, snd_pcm_t **rpcm)
+{
+	snd_pcm_t *pcm;
+	int err;
+
+	if ((err = snd_pcm_new(chip->card, "AD1848", device, 1, 1, &pcm)) < 0)
+		return err;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ad1848_playback_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ad1848_capture_ops);
+
+	pcm->private_free = snd_ad1848_pcm_free;
+	pcm->private_data = chip;
+	pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX;
+	strcpy(pcm->name, snd_ad1848_chip_id(chip));
+
+	snd_pcm_lib_preallocate_pages_for_all(pcm, 64*1024, chip->dma > 3 ? 128*1024 : 64*1024, GFP_KERNEL|GFP_DMA);
+
+	chip->pcm = pcm;
+	if (rpcm)
+		*rpcm = pcm;
+	return 0;
+}
+
+/*
+ *  MIXER part
+ */
+
+static int snd_ad1848_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	static char *texts[4] = {
+		"Line1", "Aux", "Line2", "Mix"
+	};
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 2;
+	uinfo->value.enumerated.items = 4;
+	if (uinfo->value.enumerated.item > 3)
+		uinfo->value.enumerated.item = 3;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int snd_ad1848_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ad1848_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	ucontrol->value.enumerated.item[0] = (chip->image[AD1848_LEFT_INPUT] & AD1848_MIXS_ALL) >> 6;
+	ucontrol->value.enumerated.item[1] = (chip->image[AD1848_RIGHT_INPUT] & AD1848_MIXS_ALL) >> 6;
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return 0;
+}
+
+static int snd_ad1848_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ad1848_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	unsigned short left, right;
+	int change;
+	
+	if (ucontrol->value.enumerated.item[0] > 3 ||
+	    ucontrol->value.enumerated.item[1] > 3)
+		return -EINVAL;
+	left = ucontrol->value.enumerated.item[0] << 6;
+	right = ucontrol->value.enumerated.item[1] << 6;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	left = (chip->image[AD1848_LEFT_INPUT] & ~AD1848_MIXS_ALL) | left;
+	right = (chip->image[AD1848_RIGHT_INPUT] & ~AD1848_MIXS_ALL) | right;
+	change = left != chip->image[AD1848_LEFT_INPUT] ||
+	         right != chip->image[AD1848_RIGHT_INPUT];
+	snd_ad1848_out(chip, AD1848_LEFT_INPUT, left);
+	snd_ad1848_out(chip, AD1848_RIGHT_INPUT, right);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return change;
+}
+
+int snd_ad1848_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+
+	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	return 0;
+}
+
+int snd_ad1848_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ad1848_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 8) & 0xff;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0xff;
+	
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	ucontrol->value.integer.value[0] = (chip->image[reg] >> shift) & mask;
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	if (invert)
+		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+	return 0;
+}
+
+int snd_ad1848_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ad1848_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 8) & 0xff;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0xff;
+	int change;
+	unsigned short val;
+	
+	val = (ucontrol->value.integer.value[0] & mask);
+	if (invert)
+		val = mask - val;
+	val <<= shift;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	val = (chip->image[reg] & ~(mask << shift)) | val;
+	change = val != chip->image[reg];
+	snd_ad1848_out(chip, reg, val);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return change;
+}
+
+int snd_ad1848_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+
+	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	return 0;
+}
+
+int snd_ad1848_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ad1848_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int left_reg = kcontrol->private_value & 0xff;
+	int right_reg = (kcontrol->private_value >> 8) & 0xff;
+	int shift_left = (kcontrol->private_value >> 16) & 0x07;
+	int shift_right = (kcontrol->private_value >> 19) & 0x07;
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+	int invert = (kcontrol->private_value >> 22) & 1;
+	
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	ucontrol->value.integer.value[0] = (chip->image[left_reg] >> shift_left) & mask;
+	ucontrol->value.integer.value[1] = (chip->image[right_reg] >> shift_right) & mask;
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	if (invert) {
+		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+		ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1];
+	}
+	return 0;
+}
+
+int snd_ad1848_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ad1848_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int left_reg = kcontrol->private_value & 0xff;
+	int right_reg = (kcontrol->private_value >> 8) & 0xff;
+	int shift_left = (kcontrol->private_value >> 16) & 0x07;
+	int shift_right = (kcontrol->private_value >> 19) & 0x07;
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+	int invert = (kcontrol->private_value >> 22) & 1;
+	int change;
+	unsigned short val1, val2;
+	
+	val1 = ucontrol->value.integer.value[0] & mask;
+	val2 = ucontrol->value.integer.value[1] & mask;
+	if (invert) {
+		val1 = mask - val1;
+		val2 = mask - val2;
+	}
+	val1 <<= shift_left;
+	val2 <<= shift_right;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	if (left_reg != right_reg) {
+		val1 = (chip->image[left_reg] & ~(mask << shift_left)) | val1;
+		val2 = (chip->image[right_reg] & ~(mask << shift_right)) | val2;
+		change = val1 != chip->image[left_reg] || val2 != chip->image[right_reg];
+		snd_ad1848_out(chip, left_reg, val1);
+		snd_ad1848_out(chip, right_reg, val2);
+	} else {
+		val1 = (chip->image[left_reg] & ~((mask << shift_left) | (mask << shift_right))) | val1 | val2;
+		change = val1 != chip->image[left_reg];
+		snd_ad1848_out(chip, left_reg, val1);		
+	}
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return change;
+}
+
+#define AD1848_CONTROLS (sizeof(snd_ad1848_controls)/sizeof(snd_kcontrol_new_t))
+
+static snd_kcontrol_new_t snd_ad1848_controls[] = {
+AD1848_DOUBLE("PCM Playback Switch", 0, AD1848_LEFT_OUTPUT, AD1848_RIGHT_OUTPUT, 7, 7, 1, 1),
+AD1848_DOUBLE("PCM Playback Volume", 0, AD1848_LEFT_OUTPUT, AD1848_RIGHT_OUTPUT, 0, 0, 63, 1),
+AD1848_DOUBLE("Aux Playback Switch", 0, AD1848_AUX1_LEFT_INPUT, AD1848_AUX1_RIGHT_INPUT, 7, 7, 1, 1),
+AD1848_DOUBLE("Aux Playback Volume", 0, AD1848_AUX1_LEFT_INPUT, AD1848_AUX1_RIGHT_INPUT, 0, 0, 31, 1),
+AD1848_DOUBLE("Aux Playback Switch", 1, AD1848_AUX2_LEFT_INPUT, AD1848_AUX2_RIGHT_INPUT, 7, 7, 1, 1),
+AD1848_DOUBLE("Aux Playback Volume", 1, AD1848_AUX2_LEFT_INPUT, AD1848_AUX2_RIGHT_INPUT, 0, 0, 31, 1),
+AD1848_DOUBLE("Capture Volume", 0, AD1848_LEFT_INPUT, AD1848_LEFT_INPUT, 0, 0, 15, 0),
+{
+#ifdef TARGET_OS2
+	SNDRV_CTL_ELEM_IFACE_MIXER,0,0,
+	"Capture Source",0,0, 0,
+	snd_ad1848_info_mux,
+	snd_ad1848_get_mux,
+	snd_ad1848_put_mux,0
+#else
+	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	name: "Capture Source",
+	info: snd_ad1848_info_mux,
+	get: snd_ad1848_get_mux,
+	put: snd_ad1848_put_mux,
+#endif
+},
+AD1848_SINGLE("Loopback Capture Switch", 0, AD1848_LOOPBACK, 0, 1, 0),
+AD1848_SINGLE("Loopback Capture Volume", 0, AD1848_LOOPBACK, 1, 63, 0)
+};
+                                        
+int snd_ad1848_mixer(ad1848_t *chip)
+{
+	snd_card_t *card;
+	snd_pcm_t *pcm;
+	int err, idx;
+
+	snd_assert(chip != NULL && chip->pcm != NULL, return -EINVAL);
+
+	pcm = chip->pcm;
+	card = chip->card;
+
+	strcpy(card->mixername, pcm->name);
+
+	for (idx = 0; idx < AD1848_CONTROLS; idx++) {
+		if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_ad1848_controls[idx], chip))) < 0)
+			return err;
+	}
+	return 0;
+}
+
+EXPORT_SYMBOL(snd_ad1848_out);
+EXPORT_SYMBOL(snd_ad1848_interrupt);
+EXPORT_SYMBOL(snd_ad1848_create);
+EXPORT_SYMBOL(snd_ad1848_pcm);
+EXPORT_SYMBOL(snd_ad1848_mixer);
+EXPORT_SYMBOL(snd_ad1848_info_single);
+EXPORT_SYMBOL(snd_ad1848_get_single);
+EXPORT_SYMBOL(snd_ad1848_put_single);
+EXPORT_SYMBOL(snd_ad1848_info_double);
+EXPORT_SYMBOL(snd_ad1848_get_double);
+EXPORT_SYMBOL(snd_ad1848_put_double);
+
+/*
+ *  INIT part
+ */
+
+static int __init alsa_ad1848_init(void)
+{
+	return 0;
+}
+
+static void __exit alsa_ad1848_exit(void)
+{
+}
+
+module_init(alsa_ad1848_init)
+module_exit(alsa_ad1848_exit)
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/ad1848/makefile.os2
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/ad1848/makefile.os2	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/ad1848/makefile.os2	(revision 91)
@@ -0,0 +1,50 @@
+CINCLUDES = ..\..\..\include;..\..\include;$(%WATCOM)\H
+AINCLUDES = $(DDK_BASE)\H
+
+CDEFINES = -D__KERNEL__ -DMODULE -dTARGET_OS2 -dALSA_BUILD -D__ISAPNP__ -DCONFIG_ISAPNP -D__i386_
+
+32BIT=1
+!include ..\..\..\..\makefile.inc
+
+
+#===================================================================
+#
+#   List of source files
+#
+#===================================================================
+FILE1    = ad1848_lib.obj ad1848.obj
+FILE2    = 
+FILE3    = 
+FILE4    = 
+FILE5    = 
+FILE6    = 
+FILE7    = 
+FILE8    = 
+FILELAST =
+FILES    = $(FILE0) $(FILE1) $(FILE2) $(FILE3) $(FILE4) $(FILE5) $(FILE6) $(FILE7) $(FILE8) $(FILE9) $(FILE10) $(FILE11) $(FILE12)
+
+TARGET   = ad1848
+
+LNKFILE  = $(OBJDIR)\$(TARGET).lnk
+
+
+#===================================================================
+#
+#   Specific dependencies
+#
+#===================================================================
+all: $(OBJDIR)\$(TARGET).lib 
+
+
+$(LNKFILE): makefile.os2
+    @%create $(LNKFILE)
+    @for %f in ($(FILES)) do @%append $(LNKFILE) -+$(OBJDIR)\%f
+
+$(OBJDIR)\$(TARGET).lib: $(LNKFILE) $(FILES)
+    $(LIB) $(OBJDIR)\$(TARGET).lib @$(LNKFILE)
+    copy $(OBJDIR)\$(TARGET).lib $(ALSA_LIB) 
+
+clean: .SYMBOLIC
+     cd $(OBJDIR)
+     -del *.obj *.err *.lnk *.sys *.sym *.lst *.map *.wmap *.lib
+     cd ..
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/als100.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/als100.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/als100.c	(revision 91)
@@ -0,0 +1,415 @@
+
+/*
+    card-als100.c - driver for Avance Logic ALS100 based soundcards.
+    Copyright (C) 1999-2000 by Massimo Piccioni <dafastidio@libero.it>
+
+    Thanks to Pierfrancesco 'qM2' Passerini.
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#define SNDRV_MAIN_OBJECT_FILE
+#include <sound/driver.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+#include <sound/mpu401.h>
+#include <sound/opl3.h>
+#include <sound/sb.h>
+
+#define chip_t sb_t
+
+EXPORT_NO_SYMBOLS;
+MODULE_DESCRIPTION("Avance Logic ALS1X0");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{Avance Logic,ALS100 - PRO16PNP},"
+	        "{Avance Logic,ALS110},"
+	        "{3D Melody,MF1000},"
+	        "{Digimate,3D Sound},"
+	        "{Avance Logic,ALS120},"
+	        "{RTL,RTL3000}}");
+
+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;	/* Enable this card */
+static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* PnP setup */
+static long snd_mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* PnP setup */
+static long snd_fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* PnP setup */
+static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* PnP setup */
+static int snd_mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* PnP setup */
+static int snd_dma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* PnP setup */
+static int snd_dma16[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* PnP setup */
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for als100 based soundcard.");
+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 als100 based soundcard.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable als100 based soundcard.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_port, "Port # for als100 driver.");
+MODULE_PARM_SYNTAX(snd_port, SNDRV_PORT12_DESC);
+MODULE_PARM(snd_mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_mpu_port, "MPU-401 port # for als100 driver.");
+MODULE_PARM_SYNTAX(snd_mpu_port, SNDRV_PORT12_DESC);
+MODULE_PARM(snd_fm_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_fm_port, "FM port # for als100 driver.");
+MODULE_PARM_SYNTAX(snd_fm_port, SNDRV_PORT12_DESC);
+MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_irq, "IRQ # for als100 driver.");
+MODULE_PARM_SYNTAX(snd_irq, SNDRV_IRQ_DESC);
+MODULE_PARM(snd_mpu_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_mpu_irq, "MPU-401 IRQ # for als100 driver.");
+MODULE_PARM_SYNTAX(snd_mpu_irq, SNDRV_IRQ_DESC);
+MODULE_PARM(snd_dma8, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_dma8, "8-bit DMA # for als100 driver.");
+MODULE_PARM_SYNTAX(snd_dma8, SNDRV_DMA8_DESC);
+MODULE_PARM(snd_dma16, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_dma16, "16-bit DMA # for als100 driver.");
+MODULE_PARM_SYNTAX(snd_dma16, SNDRV_DMA16_DESC);
+
+struct snd_card_als100 {
+#ifdef __ISAPNP__
+	struct isapnp_dev *dev;
+	struct isapnp_dev *devmpu;
+	struct isapnp_dev *devopl;
+#endif	/* __ISAPNP__ */
+};
+
+static snd_card_t *snd_als100_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+
+#ifdef __ISAPNP__
+static struct isapnp_card *snd_als100_isapnp_cards[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR;
+static const struct isapnp_card_id *snd_als100_isapnp_id[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR;
+
+#ifdef TARGET_OS2
+#define ISAPNP_ALS100(_va, _vb, _vc, _device, _audio, _mpu401, _opl) \
+        { \
+                0, ISAPNP_CARD_ID(_va, _vb, _vc, _device), \
+                       { ISAPNP_DEVICE_ID('@', '@', '@', _audio), \
+                         ISAPNP_DEVICE_ID('@', 'X', '@', _mpu401), \
+			 ISAPNP_DEVICE_ID('@', 'H', '@', _opl) } \
+        }
+#else
+#define ISAPNP_ALS100(_va, _vb, _vc, _device, _audio, _mpu401, _opl) \
+        { \
+                ISAPNP_CARD_ID(_va, _vb, _vc, _device), \
+                devs : { ISAPNP_DEVICE_ID('@', '@', '@', _audio), \
+                         ISAPNP_DEVICE_ID('@', 'X', '@', _mpu401), \
+			 ISAPNP_DEVICE_ID('@', 'H', '@', _opl) } \
+        }
+#endif
+
+static struct isapnp_card_id snd_als100_pnpids[] __devinitdata = {
+	/* ALS100 - PRO16PNP */
+	ISAPNP_ALS100('A','L','S',0x0001,0x0001,0x0001,0x0001),
+	/* ALS110 - MF1000 - Digimate 3D Sound */
+	ISAPNP_ALS100('A','L','S',0x0110,0x1001,0x1001,0x1001),
+	/* ALS120 */
+	ISAPNP_ALS100('A','L','S',0x0120,0x2001,0x2001,0x2001),
+	/* RTL3000 */
+	ISAPNP_ALS100('R','T','L',0x3000,0x2001,0x2001,0x2001),
+	{ ISAPNP_CARD_END, }
+};
+
+ISAPNP_CARD_TABLE(snd_als100_pnpids);
+
+#endif	/* __ISAPNP__ */
+
+#define DRIVER_NAME	"snd-card-als100"
+
+
+#ifdef __ISAPNP__
+static int __init snd_card_als100_isapnp(int dev, struct snd_card_als100 *acard)
+{
+	const struct isapnp_card_id *id = snd_als100_isapnp_id[dev];
+	struct isapnp_card *card = snd_als100_isapnp_cards[dev];
+	struct isapnp_dev *pdev;
+
+	acard->dev = isapnp_find_dev(card, id->devs[0].vendor, id->devs[0].function, NULL);
+	if (acard->dev->active) {
+		acard->dev = NULL;
+		return -EBUSY;
+	}
+	acard->devmpu = isapnp_find_dev(card, id->devs[1].vendor, id->devs[1].function, NULL);
+	if (acard->devmpu->active) {
+		acard->dev = acard->devmpu = NULL;
+		return -EBUSY;
+	}
+	acard->devopl = isapnp_find_dev(card, id->devs[2].vendor, id->devs[2].function, NULL);
+	if (acard->devopl->active) {
+		acard->dev = acard->devmpu = acard->devopl = NULL;
+		return -EBUSY;
+	}
+
+	pdev = acard->dev;
+	if (pdev->prepare(pdev)<0)
+		return -EAGAIN;
+
+	if (snd_port[dev] != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[0], snd_port[dev], 16);
+	if (snd_dma8[dev] != SNDRV_AUTO_DMA)
+		isapnp_resource_change(&pdev->dma_resource[0], snd_dma8[dev],
+			1);
+	if (snd_dma16[dev] != SNDRV_AUTO_DMA)
+		isapnp_resource_change(&pdev->dma_resource[1], snd_dma16[dev],
+			1);
+	if (snd_irq[dev] != SNDRV_AUTO_IRQ)
+		isapnp_resource_change(&pdev->irq_resource[0], snd_irq[dev], 1);
+
+	if (pdev->activate(pdev)<0) {
+		snd_printk("AUDIO isapnp configure failure\n");
+		return -EBUSY;
+	}
+
+	snd_port[dev] = pdev->resource[0].start;
+	snd_dma8[dev] = pdev->dma_resource[1].start;
+	snd_dma16[dev] = pdev->dma_resource[0].start;
+	snd_irq[dev] = pdev->irq_resource[0].start;
+
+	pdev = acard->devmpu;
+	if (pdev == NULL || pdev->prepare(pdev)<0) {
+		snd_mpu_port[dev] = -1;
+		return 0;
+	}
+
+	if (snd_mpu_port[dev] != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[0], snd_mpu_port[dev],
+			2);
+	if (snd_mpu_irq[dev] != SNDRV_AUTO_IRQ)
+		isapnp_resource_change(&pdev->irq_resource[0], snd_mpu_irq[dev],
+			1);
+
+	if (pdev->activate(pdev)<0) {
+		snd_printk("MPU-401 isapnp configure failure\n");
+		snd_mpu_port[dev] = -1;
+		acard->devmpu = NULL;
+	} else {
+		snd_mpu_port[dev] = pdev->resource[0].start;
+		snd_mpu_irq[dev] = pdev->irq_resource[0].start;
+	}
+
+	pdev = acard->devopl;
+	if (pdev == NULL || pdev->prepare(pdev)<0) {
+		snd_fm_port[dev] = -1;
+		return 0;
+	}
+
+	if (snd_fm_port[dev] != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[0], snd_fm_port[dev], 4);
+
+	if (pdev->activate(pdev)<0) {
+		snd_printk("OPL isapnp configure failure\n");
+		snd_fm_port[dev] = -1;
+		acard->devopl = NULL;
+	} else {
+		snd_fm_port[dev] = pdev->resource[0].start;
+	}
+
+	return 0;
+}
+
+static void snd_card_als100_deactivate(struct snd_card_als100 *acard)
+{
+	if (acard->dev) {
+		acard->dev->deactivate(acard->dev);
+		acard->dev = NULL;
+	}
+	if (acard->devmpu) {
+		acard->devmpu->deactivate(acard->devmpu);
+		acard->devmpu = NULL;
+	}
+	if (acard->devopl) {
+		acard->devopl->deactivate(acard->devopl);
+		acard->devopl = NULL;
+	}
+}
+#endif	/* __ISAPNP__ */
+
+static void snd_card_als100_free(snd_card_t *card)
+{
+	struct snd_card_als100 *acard = (struct snd_card_als100 *)card->private_data;
+
+	if (acard) {
+#ifdef __ISAPNP__
+		snd_card_als100_deactivate(acard);
+#endif	/* __ISAPNP__ */
+	}
+}
+
+static int __init snd_card_als100_probe(int dev)
+{
+	int error;
+	sb_t *chip;
+	snd_card_t *card;
+	struct snd_card_als100 *acard;
+	opl3_t *opl3;
+
+	if ((card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE,
+				 sizeof(struct snd_card_als100))) == NULL)
+		return -ENOMEM;
+	acard = (struct snd_card_als100 *)card->private_data;
+	card->private_free = snd_card_als100_free;
+
+#ifdef __ISAPNP__
+	if ((error = snd_card_als100_isapnp(dev, acard))) {
+		snd_card_free(card);
+		return error;
+	}
+#else
+	snd_printk("you have to enable PnP support ...\n");
+	snd_card_free(card);
+	return -ENOSYS;
+#endif	/* __ISAPNP__ */
+
+	if ((error = snd_sbdsp_create(card, snd_port[dev],
+				      snd_irq[dev],
+				      snd_sb16dsp_interrupt,
+				      snd_dma8[dev],
+				      snd_dma16[dev],
+				      SB_HW_ALS100, &chip)) < 0) {
+		snd_card_free(card);
+		return error;
+	}
+
+	if ((error = snd_sb16dsp_pcm(chip, 0, NULL)) < 0) {
+		snd_card_free(card);
+		return error;
+	}
+
+	if ((error = snd_sbmixer_new(chip)) < 0) {
+		snd_card_free(card);
+		return error;
+	}
+
+	if (snd_mpu_port[dev] > 0) {
+		if (snd_mpu401_uart_new(card, 0, MPU401_HW_ALS100,
+					snd_mpu_port[dev], 0, 
+					snd_mpu_irq[dev], SA_INTERRUPT,
+					NULL) < 0)
+			snd_printk("no MPU-401 device at 0x%lx\n", snd_mpu_port[dev]);
+	}
+
+	if (snd_fm_port[dev] > 0) {
+		if (snd_opl3_create(card,
+				    snd_fm_port[dev], snd_fm_port[dev] + 2,
+				    OPL3_HW_AUTO, 0, &opl3) < 0) {
+			snd_printk("no OPL device at 0x%lx-0x%lx\n",
+				snd_fm_port[dev], snd_fm_port[dev] + 2);
+		} else {
+			if ((error = snd_opl3_timer_new(opl3, 0, 1)) < 0) {
+				snd_card_free(card);
+				return error;
+			}
+			if ((error = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
+				snd_card_free(card);
+				return error;
+			}
+		}
+	}
+
+	strcpy(card->driver, "ALS100");
+	strcpy(card->shortname, "Avance Logic ALS100");
+	sprintf(card->longname, "%s soundcard, %s at 0x%lx, irq %d, dma %d&%d",
+		card->shortname, chip->name, chip->port,
+		snd_irq[dev], snd_dma8[dev], snd_dma16[dev]);
+	if ((error = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return error;
+	}
+	snd_als100_cards[dev] = card;
+	return 0;
+}
+
+#ifdef __ISAPNP__
+static int __init snd_als100_isapnp_detect(struct isapnp_card *card,
+					   const struct isapnp_card_id *id)
+{
+	static int dev = 0;
+	int res;
+
+	for ( ; dev < SNDRV_CARDS; dev++) {
+		if (!snd_enable[dev])
+			continue;
+		snd_als100_isapnp_cards[dev] = card;
+		snd_als100_isapnp_id[dev] = id;
+		res = snd_card_als100_probe(dev);
+		if (res < 0)
+			return res;
+		dev++;
+		return 0;
+	}
+	return -ENODEV;
+}
+#endif
+
+static int __init alsa_card_als100_init(void)
+{
+	int cards = 0;
+
+#ifdef __ISAPNP__
+	cards += isapnp_probe_cards(snd_als100_pnpids, snd_als100_isapnp_detect);
+#else
+	snd_printk("you have to enable ISA PnP support.\n");
+#endif
+#ifdef MODULE
+	if (!cards)
+		snd_printk("no ALS100 based soundcards found\n");
+#endif
+	return cards ? 0 : -ENODEV;
+}
+
+static void __exit alsa_card_als100_exit(void)
+{
+	int dev;
+
+	for (dev = 0; dev < SNDRV_CARDS; dev++)
+		snd_card_free(snd_als100_cards[dev]);
+}
+
+module_init(alsa_card_als100_init)
+module_exit(alsa_card_als100_exit)
+
+#ifndef MODULE
+
+/* format is: snd-card-als100=snd_enable,snd_index,snd_id,snd_port,
+			      snd_mpu_port,snd_fm_port,snd_irq,snd_mpu_irq,
+			      snd_dma8,snd_dma16 */
+
+static int __init alsa_card_als100_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_port[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_mpu_port[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_fm_port[nr_dev]) == 2 &&
+	       get_option(&str,&snd_irq[nr_dev]) == 2 &&
+	       get_option(&str,&snd_mpu_irq[nr_dev]) == 2 &&
+	       get_option(&str,&snd_dma8[nr_dev]) == 2 &&
+	       get_option(&str,&snd_dma16[nr_dev]) == 2);
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-card-als100=", alsa_card_als100_setup);
+
+#endif /* ifndef MODULE */
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/azt2320.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/azt2320.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/azt2320.c	(revision 91)
@@ -0,0 +1,440 @@
+
+/*
+    card-azt2320.c - driver for Aztech Systems AZT2320 based soundcards.
+    Copyright (C) 1999-2000 by Massimo Piccioni <dafastidio@libero.it>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*
+    This driver should provide support for most Aztech AZT2320 based cards.
+    Several AZT2316 chips are also supported/tested, but autoprobe doesn't
+    work: all module option have to be set.
+
+    No docs available for us at Aztech headquarters !!!   Unbelievable ...
+    No other help obtained.
+
+    Thanks to Rainer Wiesner <rainer.wiesner@01019freenet.de> for the WSS
+    activation method (full-duplex audio!).
+*/
+
+#define SNDRV_MAIN_OBJECT_FILE
+#include <sound/driver.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+#include <sound/cs4231.h>
+#include <sound/mpu401.h>
+#include <sound/opl3.h>
+
+#define chip_t cs4231_t
+
+EXPORT_NO_SYMBOLS;
+MODULE_DESCRIPTION("Aztech Systems AZT2320");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{Aztech Systems,PRO16V},"
+		"{Aztech Systems,AZT2320},"
+		"{Aztech Systems,AZT3300},"
+		"{Aztech Systems,AZT2320},"
+		"{Aztech Systems,AZT3000}}");
+
+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;	/* Enable this card */
+static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* PnP setup */
+static long snd_wss_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* PnP setup */
+static long snd_mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* PnP setup */
+static long snd_fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* PnP setup */
+static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* Pnp setup */
+static int snd_mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* Pnp setup */
+static int snd_dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* PnP setup */
+static int snd_dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* PnP setup */
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for azt2320 based soundcard.");
+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 azt2320 based soundcard.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable azt2320 based soundcard.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_port, "Port # for azt2320 driver.");
+MODULE_PARM_SYNTAX(snd_port, SNDRV_PORT12_DESC);
+MODULE_PARM(snd_wss_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_wss_port, "WSS Port # for azt2320 driver.");
+MODULE_PARM_SYNTAX(snd_wss_port, SNDRV_PORT12_DESC);
+MODULE_PARM(snd_mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_mpu_port, "MPU-401 port # for azt2320 driver.");
+MODULE_PARM_SYNTAX(snd_mpu_port, SNDRV_PORT12_DESC);
+MODULE_PARM(snd_fm_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_fm_port, "FM port # for azt2320 driver.");
+MODULE_PARM_SYNTAX(snd_fm_port, SNDRV_PORT12_DESC);
+MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_irq, "IRQ # for azt2320 driver.");
+MODULE_PARM_SYNTAX(snd_irq, SNDRV_IRQ_DESC);
+MODULE_PARM(snd_mpu_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_mpu_irq, "MPU-401 IRQ # for azt2320 driver.");
+MODULE_PARM_SYNTAX(snd_mpu_irq, SNDRV_IRQ_DESC);
+MODULE_PARM(snd_dma1, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_dma1, "1st DMA # for azt2320 driver.");
+MODULE_PARM_SYNTAX(snd_dma1, SNDRV_DMA_DESC);
+MODULE_PARM(snd_dma2, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_dma2, "2nd DMA # for azt2320 driver.");
+MODULE_PARM_SYNTAX(snd_dma2, SNDRV_DMA_DESC);
+
+struct snd_card_azt2320 {
+#ifdef __ISAPNP__
+	struct isapnp_dev *dev;
+	struct isapnp_dev *devmpu;
+#endif	/* __ISAPNP__ */
+};
+
+static snd_card_t *snd_azt2320_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+
+#ifdef __ISAPNP__
+
+static struct isapnp_card *snd_azt2320_isapnp_cards[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR;
+static const struct isapnp_card_id *snd_azt2320_isapnp_id[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR;
+
+#ifdef TARGET_OS2
+#define ISAPNP_AZT2320(_va, _vb, _vc, _device, _audio, _mpu401) \
+	{ \
+		0, ISAPNP_CARD_ID(_va, _vb, _vc, _device), \
+		{ ISAPNP_DEVICE_ID(_va, _vb, _vc, _audio), \
+		  ISAPNP_DEVICE_ID(_va, _vb, _vc, _mpu401), } \
+	}
+#else
+#define ISAPNP_AZT2320(_va, _vb, _vc, _device, _audio, _mpu401) \
+	{ \
+		ISAPNP_CARD_ID(_va, _vb, _vc, _device), \
+		devs : { ISAPNP_DEVICE_ID(_va, _vb, _vc, _audio), \
+			 ISAPNP_DEVICE_ID(_va, _vb, _vc, _mpu401), } \
+	}
+#endif
+
+static struct isapnp_card_id snd_azt2320_pnpids[] __devinitdata = {
+	/* PRO16V */
+	ISAPNP_AZT2320('A','Z','T',0x1008,0x1008,0x2001),
+	/* --- */
+	ISAPNP_AZT2320('A','Z','T',0x2320,0x0001,0x0002),
+	/* Packard Bell Sound III 336 AM/SP */
+	ISAPNP_AZT2320('A','Z','T',0x3000,0x1003,0x2001),
+	/* AT3300 */
+	ISAPNP_AZT2320('A','Z','T',0x3002,0x1004,0x2001),
+	/* --- */
+	ISAPNP_AZT2320('A','Z','T',0x3005,0x1003,0x2001),
+	/* --- */
+	ISAPNP_AZT2320('A','Z','T',0x3011,0x1003,0x2001),
+	{ ISAPNP_CARD_END, }	/* end */
+};
+
+ISAPNP_CARD_TABLE(snd_azt2320_pnpids);
+
+#endif	/* __ISAPNP__ */
+
+#define	DRIVER_NAME	"snd-card-azt2320"
+
+
+#ifdef __ISAPNP__
+static int __init snd_card_azt2320_isapnp(int dev, struct snd_card_azt2320 *acard)
+{
+	const struct isapnp_card_id *id = snd_azt2320_isapnp_id[dev];
+	struct isapnp_card *card = snd_azt2320_isapnp_cards[dev];
+	struct isapnp_dev *pdev;
+
+	acard->dev = isapnp_find_dev(card, id->devs[0].vendor, id->devs[0].function, NULL);
+	if (acard->dev->active) {
+		acard->dev = NULL;
+		return -EBUSY;
+	}
+	acard->devmpu = isapnp_find_dev(card, id->devs[1].vendor, id->devs[1].function, NULL);
+	if (acard->devmpu->active) {
+		acard->dev = acard->devmpu = NULL;
+		return -EBUSY;
+	}
+
+	pdev = acard->dev;
+	if (pdev->prepare(pdev) < 0)
+		return -EAGAIN;
+
+	if (snd_port[dev] != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[0], snd_port[dev], 16);
+	if (snd_fm_port[dev] != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[1], snd_fm_port[dev], 4);
+	if (snd_wss_port[dev] != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[2], snd_wss_port[dev],
+			4);
+	if (snd_dma1[dev] != SNDRV_AUTO_DMA)
+		isapnp_resource_change(&pdev->dma_resource[0], snd_dma1[dev],
+			1);
+	if (snd_dma2[dev] != SNDRV_AUTO_DMA)
+		isapnp_resource_change(&pdev->dma_resource[1], snd_dma2[dev],
+			1);
+	if (snd_irq[dev] != SNDRV_AUTO_IRQ)
+		isapnp_resource_change(&pdev->irq_resource[0], snd_irq[dev], 1);
+
+	if (pdev->activate(pdev) < 0) {
+		snd_printk("AUDIO isapnp configure failure\n");
+		return -EBUSY;
+	}
+
+	snd_port[dev] = pdev->resource[0].start;
+	snd_fm_port[dev] = pdev->resource[1].start;
+	snd_wss_port[dev] = pdev->resource[2].start;
+	snd_dma1[dev] = pdev->dma_resource[0].start;
+	snd_dma2[dev] = pdev->dma_resource[1].start;
+	snd_irq[dev] = pdev->irq_resource[0].start;
+
+	pdev = acard->devmpu;
+	if (pdev == NULL || pdev->prepare(pdev) < 0) {
+		snd_mpu_port[dev] = -1;
+		return 0;
+	}
+
+	if (snd_mpu_port[dev] != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[0], snd_mpu_port[dev],
+			2);
+	if (snd_mpu_irq[dev] != SNDRV_AUTO_IRQ)
+		isapnp_resource_change(&pdev->irq_resource[0], snd_mpu_irq[dev],
+			1);
+
+	if (pdev->activate(pdev) < 0) {
+		/* not fatal error */
+		snd_printk("MPU-401 isapnp configure failure\n");
+		snd_mpu_port[dev] = -1;
+		acard->devmpu = NULL;
+	} else {
+		snd_mpu_port[dev] = pdev->resource[0].start;
+		snd_mpu_irq[dev] = pdev->irq_resource[0].start;
+	}
+
+	return 0;
+}
+
+static void snd_card_azt2320_deactivate(struct snd_card_azt2320 *acard)
+{
+	if (acard->dev)
+		acard->dev->deactivate(acard->dev);
+	if (acard->devmpu)
+		acard->devmpu->deactivate(acard->devmpu);
+}
+#endif	/* __ISAPNP__ */
+
+/* same of snd_sbdsp_command by Jaroslav Kysela */
+static int __init snd_card_azt2320_command(unsigned long port, unsigned char val)
+{
+	int i;
+	unsigned long limit;
+
+	limit = jiffies + HZ / 10;
+	for (i = 50000; i && (limit - jiffies) > 0; i--)
+		if (!(inb(port + 0x0c) & 0x80)) {
+			outb(val, port + 0x0c);
+			return 0;
+		}
+	return -EBUSY;
+}
+
+static int __init snd_card_azt2320_enable_wss(unsigned long port)
+{
+	int error;
+
+	if ((error = snd_card_azt2320_command(port, 0x09)))
+		return error;
+	if ((error = snd_card_azt2320_command(port, 0x00)))
+		return error;
+
+	mdelay(5);
+	return 0;
+}
+
+static void snd_card_azt2320_free(snd_card_t *card)
+{
+	struct snd_card_azt2320 *acard = (struct snd_card_azt2320 *)card->private_data;
+
+	if (acard) {
+#ifdef __ISAPNP__
+		snd_card_azt2320_deactivate(acard);
+#endif	/* __ISAPNP__ */
+	}
+}
+
+static int __init snd_card_azt2320_probe(int dev)
+{
+	int error;
+	snd_card_t *card;
+	struct snd_card_azt2320 *acard;
+	cs4231_t *chip;
+	opl3_t *opl3;
+
+	if ((card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE,
+				 sizeof(struct snd_card_azt2320))) == NULL)
+		return -ENOMEM;
+	acard = (struct snd_card_azt2320 *)card->private_data;
+	card->private_free = snd_card_azt2320_free;
+
+#ifdef __ISAPNP__
+	if ((error = snd_card_azt2320_isapnp(dev, acard))) {
+		snd_card_free(card);
+		return error;
+	}
+#endif	/* __ISAPNP__ */
+
+	if ((error = snd_card_azt2320_enable_wss(snd_port[dev]))) {
+		snd_card_free(card);
+		return error;
+	}
+
+	if ((error = snd_cs4231_create(card, snd_wss_port[dev], -1,
+				       snd_irq[dev],
+				       snd_dma1[dev],
+				       snd_dma2[dev],
+				       CS4231_HW_DETECT, 0, &chip)) < 0) {
+		snd_card_free(card);
+		return error;
+	}
+
+	if ((error = snd_cs4231_pcm(chip, 0, NULL)) < 0) {
+		snd_card_free(card);
+		return error;
+	}
+	if ((error = snd_cs4231_mixer(chip)) < 0) {
+		snd_card_free(card);
+		return error;
+	}
+	if ((error = snd_cs4231_timer(chip, 0, NULL)) < 0) {
+		snd_card_free(card);
+		return error;
+	}
+
+	if (snd_mpu_port[dev] > 0) {
+		if (snd_mpu401_uart_new(card, 0, MPU401_HW_AZT2320,
+				snd_mpu_port[dev], 0,
+				snd_mpu_irq[dev], SA_INTERRUPT,
+				NULL) < 0)
+			snd_printk("no MPU-401 device at 0x%lx\n",
+				snd_mpu_port[dev]);
+	}
+
+	if (snd_fm_port[dev] > 0) {
+		if (snd_opl3_create(card,
+				    snd_fm_port[dev], snd_fm_port[dev] + 2,
+				    OPL3_HW_AUTO, 0, &opl3) < 0) {
+			snd_printk("no OPL device at 0x%lx-0x%lx\n",
+				snd_fm_port[dev], snd_fm_port[dev] + 2);
+		} else {
+			if ((error = snd_opl3_timer_new(opl3, 1, 2)) < 0) {
+				snd_card_free(card);
+				return error;
+			}
+			if ((error = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
+				snd_card_free(card);
+				return error;
+			}
+		}
+	}
+
+	strcpy(card->driver, "AZT2320");
+	strcpy(card->shortname, "Aztech AZT2320");
+	sprintf(card->longname, "%s soundcard, WSS at 0x%lx, irq %i, dma %i&%i",
+		card->shortname, chip->port, snd_irq[dev], snd_dma1[dev], snd_dma2[dev]);
+
+	if ((error = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return error;
+	}
+	snd_azt2320_cards[dev] = card;
+	return 0;
+}
+
+#ifdef __ISAPNP__
+static int __init snd_azt2320_isapnp_detect(struct isapnp_card *card,
+                                            const struct isapnp_card_id *id)
+{
+	static int dev = 0;
+	int res;
+
+	for ( ; dev < SNDRV_CARDS; dev++) {
+		if (!snd_enable[dev])
+			continue;
+		snd_azt2320_isapnp_cards[dev] = card;
+		snd_azt2320_isapnp_id[dev] = id;
+                res = snd_card_azt2320_probe(dev);
+		if (res < 0)
+			return res;
+		dev++;
+		return 0;
+	}
+        return -ENODEV;
+}
+#endif
+
+static int __init alsa_card_azt2320_init(void)
+{
+	int cards = 0;
+
+#ifdef __ISAPNP__
+	cards += isapnp_probe_cards(snd_azt2320_pnpids, snd_azt2320_isapnp_detect);
+#else
+	snd_printk("you have to enable ISA PnP support.\n");
+#endif
+#ifdef MODULE
+	if (!cards)
+		snd_printk("no AZT2320 based soundcards found\n");
+#endif
+	return cards ? 0 : -ENODEV;
+}
+
+static void __exit alsa_card_azt2320_exit(void)
+{
+	int dev;
+
+	for (dev = 0; dev < SNDRV_CARDS; dev++)
+		snd_card_free(snd_azt2320_cards[dev]);
+}
+
+module_init(alsa_card_azt2320_init)
+module_exit(alsa_card_azt2320_exit)
+
+#ifndef MODULE
+
+/* format is: snd-card-azt2320=snd_enable,snd_index,snd_id,snd_port,
+			       snd_wss_port,snd_mpu_port,snd_fm_port,
+			       snd_irq,snd_mpu_irq,snd_dma1,snd_dma2 */
+
+static int __init alsa_card_azt2320_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_port[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_wss_port[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_mpu_port[nr_dev]) == 2 &&
+	       get_option(&str,&snd_irq[nr_dev]) == 2 &&
+	       get_option(&str,&snd_mpu_irq[nr_dev]) == 2 &&
+	       get_option(&str,&snd_dma1[nr_dev]) == 2 &&
+	       get_option(&str,&snd_dma2[nr_dev]) == 2);
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-card-azt2320=", alsa_card_azt2320_setup);
+
+#endif /* ifndef MODULE */
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/cmi8330.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/cmi8330.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/cmi8330.c	(revision 91)
@@ -0,0 +1,547 @@
+/*
+ *  Driver for C-Media's CMI8330 soundcards.
+ *  Copyright (c) by George Talusan <gstalusan@uwaterloo.ca>
+ *    http://www.undergrad.math.uwaterloo.ca/~gstalusa
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/*
+ * NOTES
+ *
+ *  The extended registers contain mixer settings which are largely
+ *  untapped for the time being.
+ *
+ *  MPU401 and SPDIF are not supported yet.  I don't have the hardware
+ *  to aid in coding and testing, so I won't bother.
+ *
+ *  To quickly load the module,
+ *
+ *  modprobe -a snd-card-cmi8330 snd_sbport=0x220 snd_sbirq=5 snd_sbdma8=1
+ *    snd_sbdma16=5 snd_wssport=0x530 snd_wssirq=11 snd_wssdma=0
+ *
+ *  This card has two mixers and two PCM devices.  I've cheesed it such
+ *  that recording and playback can be done through the same device.
+ *  The driver "magically" routes the capturing to the AD1848 codec,
+ *  and playback to the SB16 codec.  This allows for full-duplex mode
+ *  to some extent.
+ *  The utilities in alsa-utils are aware of both devices, so passing
+ *  the appropriate parameters to amixer and alsactl will give you
+ *  full control over both mixers.
+ */
+
+#define SNDRV_MAIN_OBJECT_FILE
+
+#include <sound/driver.h>
+#include <sound/ad1848.h>
+#include <sound/sb.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+EXPORT_NO_SYMBOLS;
+MODULE_DESCRIPTION("C-Media CMI8330");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{C-Media,CMI8330,isapnp:{CMI0001,@@@0001,@X@0001}}}");
+
+static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
+static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE;
+#ifdef __ISAPNP__
+#ifdef TARGET_OS2
+static int snd_isapnp[SNDRV_CARDS] = {REPEAT_SNDRV(1)};
+#else
+static int snd_isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
+#endif
+#endif
+static long snd_sbport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
+static int snd_sbirq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;
+static int snd_sbdma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;
+static int snd_sbdma16[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;
+static long snd_wssport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
+static int snd_wssirq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;
+static int snd_wssdma[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;
+
+MODULE_AUTHOR("George Talusan <gstalusan@uwaterloo.ca>");
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for CMI8330 soundcard.");
+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 CMI8330 soundcard.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable CMI8330 soundcard.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+#ifdef __ISAPNP__
+MODULE_PARM(snd_isapnp, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_isapnp, "ISA PnP detection for specified soundcard.");
+MODULE_PARM_SYNTAX(snd_isapnp, SNDRV_ISAPNP_DESC);
+#endif
+
+MODULE_PARM(snd_sbport, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_sbport, "Port # for CMI8330 SB driver.");
+MODULE_PARM_SYNTAX(snd_sbport, SNDRV_ENABLED ",allows:{{0x220,0x280,0x20}},prefers:{0x220},base:16,dialog:list");
+MODULE_PARM(snd_sbirq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_sbirq, "IRQ # for CMI8330 SB driver.");
+MODULE_PARM_SYNTAX(snd_sbirq, SNDRV_ENABLED ",allows:{{5},{7},{9},{10},{11},{12}},prefers:{5},dialog:list");
+MODULE_PARM(snd_sbdma8, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_sbdma8, "DMA8 for CMI8330 SB driver.");
+MODULE_PARM_SYNTAX(snd_sbdma8, SNDRV_DMA8_DESC ",prefers:{1}");
+MODULE_PARM(snd_sbdma16, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_sbdma16, "DMA16 for CMI8330 SB driver.");
+MODULE_PARM_SYNTAX(snd_sbdma16, SNDRV_ENABLED ",allows:{{5},{7}},prefers:{5},dialog:list");
+
+MODULE_PARM(snd_wssport, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_wssport, "Port # for CMI8330 WSS driver.");
+MODULE_PARM_SYNTAX(snd_wssport, SNDRV_ENABLED ",allows:{{0x530},{0xe80,0xf40,0xc0}},prefers:{0x530},base:16,dialog:list");
+MODULE_PARM(snd_wssirq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_wssirq, "IRQ # for CMI8330 WSS driver.");
+MODULE_PARM_SYNTAX(snd_wssirq, SNDRV_ENABLED ",allows:{{5},{7},{9},{10},{11},{12}},prefers:{11},dialog:list");
+MODULE_PARM(snd_wssdma, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_wssdma, "DMA for CMI8330 WSS driver.");
+MODULE_PARM_SYNTAX(snd_wssdma, SNDRV_DMA8_DESC ",prefers:{0}");
+
+#define CMI8330_RMUX3D    16
+#define CMI8330_MUTEMUX   17
+#define CMI8330_OUTPUTVOL 18
+#define CMI8330_MASTVOL   19
+#define CMI8330_LINVOL    20
+#define CMI8330_CDINVOL   21
+#define CMI8330_WAVVOL    22
+#define CMI8330_RECMUX    23
+#define CMI8330_WAVGAIN   24
+#define CMI8330_LINGAIN   25
+#define CMI8330_CDINGAIN  26
+
+static unsigned char snd_cmi8330_image[((CMI8330_CDINGAIN)-16) + 1] =
+{
+	0x0,			/* 16 - recording mux */
+	0x40,			/* 17 - mute mux */
+	0x0,			/* 18 - vol */
+	0x0,			/* 19 - master volume */
+	0x0,			/* 20 - line-in volume */
+	0x0,			/* 21 - cd-in volume */
+	0x0,			/* 22 - wave volume */
+	0x0,			/* 23 - mute/rec mux */
+	0x0,			/* 24 - wave rec gain */
+	0x0,			/* 25 - line-in rec gain */
+	0x0			/* 26 - cd-in rec gain */
+};
+
+struct snd_cmi8330 {
+#ifdef __ISAPNP__
+	struct isapnp_dev *cap;
+	struct isapnp_dev *play;
+#endif
+};
+
+static snd_card_t *snd_cmi8330_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+
+#ifdef __ISAPNP__
+
+static struct isapnp_card *snd_cmi8330_isapnp_cards[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR;
+static const struct isapnp_card_id *snd_cmi8330_isapnp_id[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR;
+
+#ifdef TARGET_OS2
+#define ISAPNP_CMI8330(_va, _vb, _vc, _device, _audio1, _audio2) \
+	{ \
+		0, ISAPNP_CARD_ID(_va, _vb, _vc, _device), \
+		 { ISAPNP_DEVICE_ID('@', '@', '@', _audio1), \
+		   ISAPNP_DEVICE_ID('@', 'X', '@', _audio2), } \
+	}
+#else
+#define ISAPNP_CMI8330(_va, _vb, _vc, _device, _audio1, _audio2) \
+	{ \
+		ISAPNP_CARD_ID(_va, _vb, _vc, _device), \
+		devs : { ISAPNP_DEVICE_ID('@', '@', '@', _audio1), \
+			 ISAPNP_DEVICE_ID('@', 'X', '@', _audio2), } \
+	}
+#endif
+
+static struct isapnp_card_id snd_cmi8330_pnpids[] __devinitdata =
+{
+	ISAPNP_CMI8330('C','M','I',0x0001,0x0001,0x0001),
+	{ ISAPNP_CARD_END, }
+};
+
+ISAPNP_CARD_TABLE(snd_cmi8330_pnpids);
+
+#endif
+
+#define CMI8330_CONTROLS (sizeof(snd_cmi8330_controls)/sizeof(snd_kcontrol_new_t))
+
+static snd_kcontrol_new_t snd_cmi8330_controls[] __devinitdata = {
+AD1848_DOUBLE("Master Playback Volume", 0, CMI8330_MASTVOL, CMI8330_MASTVOL, 4, 0, 15, 0),
+AD1848_SINGLE("Loud Playback Switch", 0, CMI8330_MUTEMUX, 6, 1, 1),
+AD1848_DOUBLE("PCM Playback Switch", 0, AD1848_LEFT_OUTPUT, AD1848_RIGHT_OUTPUT, 7, 7, 1, 1),
+AD1848_DOUBLE("PCM Playback Volume", 0, AD1848_LEFT_OUTPUT, AD1848_RIGHT_OUTPUT, 0, 0, 63, 0),
+AD1848_DOUBLE("Line Playback Switch", 0, CMI8330_MUTEMUX, CMI8330_MUTEMUX, 4, 3, 1, 0),
+AD1848_DOUBLE("Line Playback Volume", 0, CMI8330_LINVOL, CMI8330_LINVOL, 4, 0, 15, 0),
+AD1848_DOUBLE("Line Capture Switch", 0, CMI8330_RMUX3D, CMI8330_RMUX3D, 2, 1, 1, 0),
+AD1848_DOUBLE("Line Capture Volume", 0, CMI8330_LINGAIN, CMI8330_LINGAIN, 4, 0, 15, 0),
+AD1848_DOUBLE("CD Playback Switch", 0, CMI8330_MUTEMUX, CMI8330_MUTEMUX, 2, 1, 1, 0),
+AD1848_DOUBLE("CD Capture Switch", 0, CMI8330_RMUX3D, CMI8330_RMUX3D, 4, 3, 1, 0),
+AD1848_DOUBLE("CD Playback Volume", 0, CMI8330_CDINVOL, CMI8330_CDINVOL, 4, 0, 15, 0),
+AD1848_DOUBLE("CD Capture Switch", 0, CMI8330_CDINGAIN, CMI8330_CDINGAIN, 4, 0, 15, 0),
+AD1848_SINGLE("Mic Playback Switch", 0, CMI8330_MUTEMUX, 0, 1, 0),
+AD1848_SINGLE("Mic Playback Volume", 0, CMI8330_OUTPUTVOL, 0, 7, 0),
+AD1848_SINGLE("Mic Capture Switch", 0, CMI8330_RMUX3D, 0, 1, 0),
+AD1848_SINGLE("Mic Capture Volume", 0, CMI8330_OUTPUTVOL, 5, 7, 0),
+AD1848_DOUBLE("Wavetable Playback Switch", 0, CMI8330_RECMUX, CMI8330_RECMUX, 1, 0, 1, 0),
+AD1848_DOUBLE("Wavetable Playback Volume", 0, CMI8330_WAVVOL, CMI8330_WAVVOL, 4, 0, 15, 0),
+AD1848_DOUBLE("Wavetable Capture Switch", 0, CMI8330_RECMUX, CMI8330_RECMUX, 5, 4, 1, 0),
+AD1848_DOUBLE("Wavetable Capture Volume", 0, CMI8330_WAVGAIN, CMI8330_WAVGAIN, 4, 0, 15, 0),
+AD1848_SINGLE("3D Control - Switch", 0, CMI8330_RMUX3D, 5, 1, 1),
+AD1848_SINGLE("PC Speaker Playback Volume", 0, CMI8330_OUTPUTVOL, 3, 3, 0),
+AD1848_SINGLE("FM Playback Switch", 0, CMI8330_RECMUX, 3, 1, 1),
+AD1848_SINGLE("IEC958 Input Capture Switch", 0, CMI8330_RMUX3D, 7, 1, 1),
+AD1848_SINGLE("IEC958 Input Playback Switch", 0, CMI8330_MUTEMUX, 7, 1, 1),
+};
+
+static int __init snd_cmi8330_mixer(snd_card_t *card, ad1848_t *chip)
+{
+	int idx, err;
+
+	strcpy(card->mixername, "CMI8330/C3D");
+
+	for (idx = 0; idx < CMI8330_CONTROLS; idx++)
+		if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cmi8330_controls[idx], chip))) < 0)
+			return err;
+
+	return 0;
+}
+
+#ifdef __ISAPNP__
+static int __init snd_cmi8330_isapnp(int dev, struct snd_cmi8330 *acard)
+{
+	const struct isapnp_card_id *id = snd_cmi8330_isapnp_id[dev];
+	struct isapnp_card *card = snd_cmi8330_isapnp_cards[dev];
+	struct isapnp_dev *pdev;
+
+	acard->cap = isapnp_find_dev(card, id->devs[0].vendor, id->devs[0].function, NULL);
+	if (acard->cap->active) {
+		acard->cap = NULL;
+		return -EBUSY;
+	}
+	acard->play = isapnp_find_dev(card, id->devs[1].vendor, id->devs[1].function, NULL);
+	if (acard->play->active) {
+		acard->cap = acard->play = NULL;
+		return -EBUSY;
+	}
+
+	pdev = acard->cap;
+	if (pdev->prepare(pdev)<0)
+		return -EAGAIN;
+	/* allocate AD1848 resources */
+	if (snd_wssport[dev] != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[0], snd_wssport[dev], 8);
+	if (snd_wssdma[dev] != SNDRV_AUTO_DMA)
+		isapnp_resource_change(&pdev->dma_resource[0], snd_wssdma[dev], 1);
+	if (snd_wssirq[dev] != SNDRV_AUTO_IRQ)
+		isapnp_resource_change(&pdev->irq_resource[0], snd_wssirq[dev], 1);
+
+	if (pdev->activate(pdev)<0) {
+		snd_printk("(AD1848) PnP configure failure\n");
+		return -EBUSY;
+	}
+	snd_wssport[dev] = pdev->resource[0].start;
+	snd_wssdma[dev] = pdev->dma_resource[0].start;
+	snd_wssirq[dev] = pdev->irq_resource[0].start;
+
+	/* allocate SB16 resources */
+	pdev = acard->play;
+	if (pdev->prepare(pdev)<0) {
+		acard->cap->deactivate(acard->cap);
+		return -EAGAIN;
+	}
+	if (snd_sbport[dev] != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[0], snd_sbport[dev], 16);
+	if (snd_sbdma8[dev] != SNDRV_AUTO_DMA)
+		isapnp_resource_change(&pdev->dma_resource[0], snd_sbdma8[dev], 1);
+	if (snd_sbdma16[dev] != SNDRV_AUTO_DMA)
+		isapnp_resource_change(&pdev->dma_resource[1], snd_sbdma16[dev], 1);
+	if (snd_sbirq[dev] != SNDRV_AUTO_IRQ)
+		isapnp_resource_change(&pdev->irq_resource[0], snd_sbirq[dev], 1);
+
+	if (pdev->activate(pdev)<0) {
+		snd_printk("CMI8330/C3D (SB16) PnP configure failure\n");
+		acard->cap->deactivate(acard->cap);
+		return -EBUSY;
+	}
+	snd_sbport[dev] = pdev->resource[0].start;
+	snd_sbdma8[dev] = pdev->dma_resource[0].start;
+	snd_sbdma16[dev] = pdev->dma_resource[1].start;
+	snd_sbirq[dev] = pdev->irq_resource[0].start;
+
+	return 0;
+}
+
+static void snd_cmi8330_deactivate(struct snd_cmi8330 *acard)
+{
+	if (acard->cap) {
+		acard->cap->deactivate(acard->cap);
+		acard->cap = NULL;
+	}
+	if (acard->play) {
+		acard->play->deactivate(acard->play);
+		acard->play = NULL;
+	}
+}
+#endif
+
+static void snd_cmi8330_free(snd_card_t *card)
+{
+	struct snd_cmi8330 *acard = (struct snd_cmi8330 *)card->private_data;
+
+	if (acard) {
+#ifdef __ISAPNP__
+		snd_cmi8330_deactivate(acard);
+#endif
+	}
+}
+
+static int __init snd_cmi8330_probe(int dev)
+{
+	snd_card_t *card;
+	struct snd_cmi8330 *acard;
+	ad1848_t *chip_wss;
+	sb_t *chip_sb;
+	unsigned long flags;
+	int i, err;
+	snd_pcm_t *pcm, *wss_pcm, *sb_pcm;
+	snd_pcm_str_t *pstr;
+
+#ifdef __ISAPNP__
+	if (!snd_isapnp[dev]) {
+#endif
+		if (snd_wssport[dev] == SNDRV_AUTO_PORT) {
+			snd_printk("specify snd_wssport\n");
+			return -EINVAL;
+		}
+		if (snd_sbport[dev] == SNDRV_AUTO_PORT) {
+			snd_printk("specify snd_sbport\n");
+			return -EINVAL;
+		}
+#ifdef __ISAPNP__
+	}
+#endif
+	card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE,
+			    sizeof(struct snd_cmi8330));
+	if (card == NULL) {
+		snd_printk("could not get a new card\n");
+		return -ENOMEM;
+	}
+	acard = (struct snd_cmi8330 *)card->private_data;
+	card->private_free = snd_cmi8330_free;
+
+#ifdef __ISAPNP__
+	if (snd_isapnp[dev] && (err = snd_cmi8330_isapnp(dev, acard)) < 0) {
+		snd_printk("PnP detection failed\n");
+		snd_card_free(card);
+		return err;
+	}
+#endif
+
+	if ((err = snd_ad1848_create(card,
+				     snd_wssport[dev] + 4,
+				     snd_wssirq[dev],
+				     snd_wssdma[dev],
+				     AD1848_HW_DETECT,
+				     &chip_wss)) < 0) {
+		snd_printk("(AD1848) device busy??\n");
+		snd_card_free(card);
+		return err;
+	}
+	if (chip_wss->hardware != AD1848_HW_CMI8330) {
+		snd_printk("(AD1848) not found during probe\n");
+		snd_card_free(card);
+		return -ENODEV;
+	}
+	if ((err = snd_ad1848_pcm(chip_wss, 0, &wss_pcm)) < 0) {
+		snd_printk("(AD1848) no enough memory??\n");
+		snd_card_free(card);
+		return err;
+	}
+
+	if ((err = snd_sbdsp_create(card, snd_sbport[dev],
+				    snd_sbirq[dev],
+				    snd_sb16dsp_interrupt,
+				    snd_sbdma8[dev],
+				    snd_sbdma16[dev],
+				    SB_HW_AUTO, &chip_sb)) < 0) {
+		snd_printk("(SB16) device busy??\n");
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_sb16dsp_pcm(chip_sb, 1, &sb_pcm)) < 0) {
+		snd_printk("(SB16) no enough memory??\n");
+		snd_card_free(card);
+		return err;
+	}
+
+	if (chip_sb->hardware != SB_HW_16) {
+		snd_printk("(SB16) not found during probe\n");
+		snd_card_free(card);
+		return -ENODEV;
+	}
+
+	memcpy(&chip_wss->image[16], &snd_cmi8330_image, sizeof(snd_cmi8330_image));
+
+	spin_lock_irqsave(&chip_wss->reg_lock, flags);
+	snd_ad1848_out(chip_wss, AD1848_MISC_INFO,	/* switch on MODE2 */
+		       chip_wss->image[AD1848_MISC_INFO] |= 0x40);
+	spin_unlock_irqrestore(&chip_wss->reg_lock, flags);
+
+	if ((err = snd_cmi8330_mixer(card, chip_wss)) < 0) {
+		snd_printk("failed to create mixers\n");
+		snd_card_free(card);
+		return err;
+	}
+	spin_lock_irqsave(&chip_wss->reg_lock, flags);
+	for (i = CMI8330_RMUX3D; i <= CMI8330_CDINGAIN; i++)
+		snd_ad1848_out(chip_wss, i, chip_wss->image[i]);
+	spin_unlock_irqrestore(&chip_wss->reg_lock, flags);
+
+	/*
+	 * KLUDGE ALERT
+	 *  disable AD1848 playback
+	 *  disable SB16 capture
+	 */
+	pcm = wss_pcm;
+	pstr = &pcm->streams[SNDRV_PCM_STREAM_PLAYBACK];
+	snd_magic_kfree(pstr->substream);
+	pstr->substream = 0;
+	pstr->substream_count = 0;
+
+	pcm = sb_pcm;
+	pstr = &pcm->streams[SNDRV_PCM_STREAM_CAPTURE];
+	snd_magic_kfree(pstr->substream);
+	pstr->substream = 0;
+	pstr->substream_count = 0;
+
+	strcpy(card->driver, "CMI8330/C3D");
+	strcpy(card->shortname, "C-Media CMI8330/C3D");
+	sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d",
+		wss_pcm->name,
+		chip_wss->port,
+		snd_wssirq[dev],
+		snd_wssdma[dev]);
+
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	snd_cmi8330_cards[dev] = card;
+	return 0;
+}
+
+static void __exit alsa_card_cmi8330_exit(void)
+{
+	int i;
+
+	for (i = 0; i < SNDRV_CARDS; i++)
+		snd_card_free(snd_cmi8330_cards[i]);
+}
+
+#ifdef __ISAPNP__
+static int __init snd_cmi8330_isapnp_detect(struct isapnp_card *card,
+                                            const struct isapnp_card_id *id)
+{
+	static int dev = 0;
+	int res;
+
+	for ( ; dev < SNDRV_CARDS; dev++) {
+		if (!snd_enable[dev] || !snd_isapnp[dev])
+			continue;
+		snd_cmi8330_isapnp_cards[dev] = card;
+		snd_cmi8330_isapnp_id[dev] = id;
+		res = snd_cmi8330_probe(dev);
+		if (res < 0)
+			return res;
+		dev++;
+		return 0;
+	}
+	return -ENODEV;
+}
+#endif /* __ISAPNP__ */
+
+static int __init alsa_card_cmi8330_init(void)
+{
+	int dev, cards = 0;
+
+	for (dev = 0; dev < SNDRV_CARDS; dev++) {
+		if (!snd_enable[dev])
+			continue;
+#ifdef __ISAPNP__
+		if (snd_isapnp[dev])
+			continue;
+#endif
+		if (snd_cmi8330_probe(dev) >= 0)
+			cards++;
+	}
+#ifdef __ISAPNP__
+	cards += isapnp_probe_cards(snd_cmi8330_pnpids, snd_cmi8330_isapnp_detect);
+#endif
+
+	if (!cards) {
+#ifdef MODULE
+		snd_printk("CMI8330 not found or device busy\n");
+#endif
+		return -ENODEV;
+	}
+	return 0;
+}
+
+module_init(alsa_card_cmi8330_init)
+module_exit(alsa_card_cmi8330_exit)
+
+#ifndef MODULE
+
+/* format is: snd-card-cmi8330=snd_enable,snd_index,snd_id,snd_isapnp,
+			       snd_sbport,snd_sbirq,
+			       snd_sbdma8,snd_sbdma16,
+			       snd_wssport,snd_wssirq,
+			       snd_wssdma */
+
+static int __init alsa_card_cmi8330_setup(char *str)
+{
+	static unsigned __initdata nr_dev = 0;
+	int __attribute__ ((__unused__)) pnp = INT_MAX;
+
+	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,&pnp) == 2 &&
+	       get_option(&str,(int *)&snd_sbport[nr_dev]) == 2 &&
+	       get_option(&str,&snd_sbirq[nr_dev]) == 2 &&
+	       get_option(&str,&snd_sbdma8[nr_dev]) == 2 &&
+	       get_option(&str,&snd_sbdma16[nr_dev]) == 2 &&
+	       get_option(&str,&snd_wssport[nr_dev]) == 2 &&
+	       get_option(&str,&snd_wssirq[nr_dev]) == 2 &&
+	       get_option(&str,&snd_wssdma[nr_dev]) == 2);
+#ifdef __ISAPNP__
+	if (pnp != INT_MAX)
+		snd_isapnp[nr_dev] = pnp;
+#endif
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-card-cmi8330=", alsa_card_cmi8330_setup);
+
+#endif /* ifndef MODULE */
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/cs423x/cs4231.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/cs423x/cs4231.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/cs423x/cs4231.c	(revision 91)
@@ -0,0 +1,205 @@
+/*
+ *  Generic driver for CS4231 chips
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  Originally the CS4232/CS4232A driver, modified for use on CS4231 by
+ *  Tugrul Galatali <galatalt@stuy.edu>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#define SNDRV_MAIN_OBJECT_FILE
+#include <sound/driver.h>
+#include <sound/cs4231.h>
+#include <sound/mpu401.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+#define chip_t cs4231_t
+
+EXPORT_NO_SYMBOLS;
+MODULE_DESCRIPTION("Generic CS4231");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{Crystal Semiconductors,CS4231}}");
+
+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;	/* Enable this card */
+static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* PnP setup */
+static long snd_mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* PnP setup */
+static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* 5,7,9,11,12,15 */
+static int snd_mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* 9,11,12,15 */
+static int snd_dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* 0,1,3,5,6,7 */
+static int snd_dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* 0,1,3,5,6,7 */
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for CS4231 soundcard.");
+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 CS4231 soundcard.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable CS4231 soundcard.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_port, "Port # for CS4231 driver.");
+MODULE_PARM_SYNTAX(snd_port, SNDRV_PORT12_DESC);
+MODULE_PARM(snd_mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_mpu_port, "MPU-401 port # for CS4231 driver.");
+MODULE_PARM_SYNTAX(snd_mpu_port, SNDRV_PORT12_DESC);
+MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_irq, "IRQ # for CS4231 driver.");
+MODULE_PARM_SYNTAX(snd_irq, SNDRV_IRQ_DESC);
+MODULE_PARM(snd_mpu_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_mpu_irq, "MPU-401 IRQ # for CS4231 driver.");
+MODULE_PARM_SYNTAX(snd_mpu_irq, SNDRV_IRQ_DESC);
+MODULE_PARM(snd_dma1, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_dma1, "DMA1 # for CS4231 driver.");
+MODULE_PARM_SYNTAX(snd_dma1, SNDRV_DMA_DESC);
+MODULE_PARM(snd_dma2, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_dma2, "DMA2 # for CS4231 driver.");
+MODULE_PARM_SYNTAX(snd_dma2, SNDRV_DMA_DESC);
+
+static snd_card_t *snd_cs4231_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+
+
+static int __init snd_card_cs4231_probe(int dev)
+{
+	snd_card_t *card;
+	struct snd_card_cs4231 *acard;
+	snd_pcm_t *pcm = NULL;
+	cs4231_t *chip;
+	int err;
+
+	if (snd_port[dev] == SNDRV_AUTO_PORT) {
+		snd_printk("specify snd_port\n");
+		return -EINVAL;
+	}
+	if (snd_irq[dev] == SNDRV_AUTO_IRQ) {
+		snd_printk("specify snd_irq\n");
+		return -EINVAL;
+	}
+	if (snd_dma1[dev] == SNDRV_AUTO_DMA) {
+		snd_printk("specify snd_dma1\n");
+		return -EINVAL;
+	}
+	card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0);
+	if (card == NULL)
+		return -ENOMEM;
+	acard = (struct snd_card_cs4231 *)card->private_data;
+	if (snd_mpu_port[dev] < 0)
+		snd_mpu_port[dev] = SNDRV_AUTO_PORT;
+	if ((err = snd_cs4231_create(card, snd_port[dev], -1,
+				     snd_irq[dev],
+				     snd_dma1[dev],
+				     snd_dma2[dev],
+				     CS4231_HW_DETECT,
+				     0, &chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	if ((err = snd_cs4231_pcm(chip, 0, &pcm)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_cs4231_mixer(chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_cs4231_timer(chip, 0, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	if (snd_mpu_irq[dev] >= 0 && snd_mpu_irq[dev] != SNDRV_AUTO_IRQ) {
+		if (snd_mpu401_uart_new(card, 0, MPU401_HW_CS4232,
+					snd_mpu_port[dev], 0,
+					snd_mpu_irq[dev], SA_INTERRUPT,
+					NULL) < 0)
+			snd_printk("MPU401 not detected\n");
+	}
+	strcpy(card->driver, "CS4231");
+	strcpy(card->shortname, pcm->name);
+	sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d",
+		pcm->name, chip->port, snd_irq[dev], snd_dma1[dev]);
+	if (snd_dma2[dev] >= 0)
+		sprintf(card->longname + strlen(card->longname), "&%d", snd_dma2[dev]);
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	snd_cs4231_cards[dev] = card;
+	return 0;
+}
+
+static int __init alsa_card_cs4231_init(void)
+{
+	int dev, cards;
+
+	for (dev = cards = 0; dev < SNDRV_CARDS && snd_enable[dev]; dev++) {
+		if (snd_card_cs4231_probe(dev) >= 0)
+			cards++;
+	}
+	if (!cards) {
+#ifdef MODULE
+		snd_printk("CS4231 soundcard not found or device busy\n");
+#endif
+		return -ENODEV;
+	}
+	return 0;
+}
+
+static void __exit alsa_card_cs4231_exit(void)
+{
+	int idx;
+
+	for (idx = 0; idx < SNDRV_CARDS; idx++)
+		snd_card_free(snd_cs4231_cards[idx]);
+}
+
+module_init(alsa_card_cs4231_init)
+module_exit(alsa_card_cs4231_exit)
+
+#ifndef MODULE
+
+/* format is: snd-card-cs4231=snd_enable,snd_index,snd_id,
+			      snd_port,snd_mpu_port,snd_irq,snd_mpu_irq,
+			      snd_dma1,snd_dma2 */
+
+static int __init alsa_card_cs4231_setup(char *str)
+{
+	static unsigned __initdata nr_dev = 0;
+	int __attribute__ ((__unused__)) pnp = INT_MAX;
+
+	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,&pnp) == 2 &&
+	       get_option(&str,(int *)&snd_port[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_mpu_port[nr_dev]) == 2 &&
+	       get_option(&str,&snd_irq[nr_dev]) == 2 &&
+	       get_option(&str,&snd_mpu_irq[nr_dev]) == 2 &&
+	       get_option(&str,&snd_dma1[nr_dev]) == 2 &&
+	       get_option(&str,&snd_dma2[nr_dev]) == 2);
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-card-cs4231=", alsa_card_cs4231_setup);
+
+#endif /* ifndef MODULE */
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/cs423x/cs4231_lib.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/cs423x/cs4231_lib.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/cs423x/cs4231_lib.c	(revision 91)
@@ -0,0 +1,1924 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  Routines for control of CS4231(A)/CS4232/InterWave & compatible chips
+ *
+ *  Bugs:
+ *     - sometimes record brokes playback with WSS portion of 
+ *       Yamaha OPL3-SA3 chip
+ *     - CS4231 (GUS MAX) - still trouble with occasional noises
+ *                        - broken initialization?
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#define SNDRV_MAIN_OBJECT_FILE
+#include <sound/driver.h>
+#include <sound/cs4231.h>
+
+#define chip_t cs4231_t
+
+#if 0
+#define SNDRV_DEBUG_MCE
+#endif
+
+/*
+ *  Some variables
+ */
+
+static unsigned char freq_bits[14] = {
+	/* 5510 */	0x00 | CS4231_XTAL2,
+	/* 6620 */	0x0E | CS4231_XTAL2,
+	/* 8000 */	0x00 | CS4231_XTAL1,
+	/* 9600 */	0x0E | CS4231_XTAL1,
+	/* 11025 */	0x02 | CS4231_XTAL2,
+	/* 16000 */	0x02 | CS4231_XTAL1,
+	/* 18900 */	0x04 | CS4231_XTAL2,
+	/* 22050 */	0x06 | CS4231_XTAL2,
+	/* 27042 */	0x04 | CS4231_XTAL1,
+	/* 32000 */	0x06 | CS4231_XTAL1,
+	/* 33075 */	0x0C | CS4231_XTAL2,
+	/* 37800 */	0x08 | CS4231_XTAL2,
+	/* 44100 */	0x0A | CS4231_XTAL2,
+	/* 48000 */	0x0C | CS4231_XTAL1
+};
+
+static unsigned int rates[14] = {
+	5510, 6620, 8000, 9600, 11025, 16000, 18900, 22050,
+	27042, 32000, 33075, 37800, 44100, 48000
+};
+
+#ifdef TARGET_OS2
+static snd_pcm_hw_constraint_list_t hw_constraints_rates = {
+	14,
+	rates,
+	0,
+};
+#else
+static snd_pcm_hw_constraint_list_t hw_constraints_rates = {
+	count: 14,
+	list: rates,
+	mask: 0,
+};
+#endif
+
+static int snd_cs4231_xrate(snd_pcm_runtime_t *runtime)
+{
+	return snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates);
+}
+
+static unsigned char snd_cs4231_original_image[32] =
+{
+	0x00,			/* 00/00 - lic */
+	0x00,			/* 01/01 - ric */
+	0x9f,			/* 02/02 - la1ic */
+	0x9f,			/* 03/03 - ra1ic */
+	0x9f,			/* 04/04 - la2ic */
+	0x9f,			/* 05/05 - ra2ic */
+	0xbf,			/* 06/06 - loc */
+	0xbf,			/* 07/07 - roc */
+	0x20,			/* 08/08 - pdfr */
+	CS4231_AUTOCALIB,	/* 09/09 - ic */
+	0x00,			/* 0a/10 - pc */
+	0x00,			/* 0b/11 - ti */
+	CS4231_MODE2,		/* 0c/12 - mi */
+	0xfc,			/* 0d/13 - lbc */
+	0x00,			/* 0e/14 - pbru */
+	0x00,			/* 0f/15 - pbrl */
+	0x80,			/* 10/16 - afei */
+	0x01,			/* 11/17 - afeii */
+	0x9f,			/* 12/18 - llic */
+	0x9f,			/* 13/19 - rlic */
+	0x00,			/* 14/20 - tlb */
+	0x00,			/* 15/21 - thb */
+	0x00,			/* 16/22 - la3mic/reserved */
+	0x00,			/* 17/23 - ra3mic/reserved */
+	0x00,			/* 18/24 - afs */
+	0x00,			/* 19/25 - lamoc/version */
+	0xcf,			/* 1a/26 - mioc */
+	0x00,			/* 1b/27 - ramoc/reserved */
+	0x20,			/* 1c/28 - cdfr */
+	0x00,			/* 1d/29 - res4 */
+	0x00,			/* 1e/30 - cbru */
+	0x00,			/* 1f/31 - cbrl */
+};
+
+/*
+ *  Basic I/O functions
+ */
+
+void snd_cs4231_outm(cs4231_t *chip, unsigned char reg,
+		     unsigned char mask, unsigned char value)
+{
+	int timeout;
+	unsigned char tmp;
+
+	for (timeout = 250;
+	     timeout > 0 && (inb(CS4231P(chip, REGSEL)) & CS4231_INIT);
+	     timeout--)
+	     	udelay(100);
+#ifdef CONFIG_SND_DEBUG
+	if (inb(CS4231P(chip, REGSEL)) & CS4231_INIT)
+		snd_printk("outm: auto calibration time out - reg = 0x%x, value = 0x%x\n", reg, value);
+#endif
+	if (chip->calibrate_mute) {
+		chip->image[reg] &= mask;
+		chip->image[reg] |= value;
+	} else {
+		outb(chip->mce_bit | reg, CS4231P(chip, REGSEL));
+		mb();
+		tmp = (chip->image[reg] & mask) | value;
+		outb(tmp, CS4231P(chip, REG));
+		chip->image[reg] = tmp;
+		mb();
+	}
+}
+
+static void snd_cs4231_dout(cs4231_t *chip, unsigned char reg, unsigned char value)
+{
+	int timeout;
+
+	for (timeout = 250;
+	     timeout > 0 && (inb(CS4231P(chip, REGSEL)) & CS4231_INIT);
+	     timeout--)
+	     	udelay(10);
+	outb(chip->mce_bit | reg, CS4231P(chip, REGSEL));
+	outb(value, CS4231P(chip, REG));
+	mb();
+}
+
+void snd_cs4231_out(cs4231_t *chip, unsigned char reg, unsigned char value)
+{
+	int timeout;
+
+	for (timeout = 250;
+	     timeout > 0 && (inb(CS4231P(chip, REGSEL)) & CS4231_INIT);
+	     timeout--)
+	     	udelay(100);
+#ifdef CONFIG_SND_DEBUG
+	if (inb(CS4231P(chip, REGSEL)) & CS4231_INIT)
+		snd_printk("out: auto calibration time out - reg = 0x%x, value = 0x%x\n", reg, value);
+#endif
+	outb(chip->mce_bit | reg, CS4231P(chip, REGSEL));
+	outb(value, CS4231P(chip, REG));
+	chip->image[reg] = value;
+	mb();
+#if 0
+	printk("codec out - reg 0x%x = 0x%x\n", chip->mce_bit | reg, value);
+#endif
+}
+
+unsigned char snd_cs4231_in(cs4231_t *chip, unsigned char reg)
+{
+	int timeout;
+
+	for (timeout = 250;
+	     timeout > 0 && (inb(CS4231P(chip, REGSEL)) & CS4231_INIT);
+	     timeout--)
+	     	udelay(100);
+#ifdef CONFIG_SND_DEBUG
+	if (inb(CS4231P(chip, REGSEL)) & CS4231_INIT)
+		snd_printk("in: auto calibration time out - reg = 0x%x\n", reg);
+#endif
+	outb(chip->mce_bit | reg, CS4231P(chip, REGSEL));
+	mb();
+	return inb(CS4231P(chip, REG));
+}
+
+void snd_cs4236_ext_out(cs4231_t *chip, unsigned char reg, unsigned char val)
+{
+	outb(chip->mce_bit | 0x17, CS4231P(chip, REGSEL));
+	outb(reg | (chip->image[CS4236_EXT_REG] & 0x01), CS4231P(chip, REG));
+	outb(val, CS4231P(chip, REG));
+	chip->eimage[CS4236_REG(reg)] = val;
+#if 0
+	printk("ext out : reg = 0x%x, val = 0x%x\n", reg, val);
+#endif
+}
+
+unsigned char snd_cs4236_ext_in(cs4231_t *chip, unsigned char reg)
+{
+	outb(chip->mce_bit | 0x17, CS4231P(chip, REGSEL));
+	outb(reg | (chip->image[CS4236_EXT_REG] & 0x01), CS4231P(chip, REG));
+#if 1
+	return inb(CS4231P(chip, REG));
+#else
+	{
+		unsigned char res;
+		res = inb(CS4231P(chip, REG));
+		printk("ext in : reg = 0x%x, val = 0x%x\n", reg, res);
+		return res;
+	}
+#endif
+}
+
+#ifdef CONFIG_SND_DEBUG
+
+void snd_cs4231_debug(cs4231_t *chip)
+{
+	printk("CS4231 REGS:      INDEX = 0x%02x  ", inb(CS4231P(chip, REGSEL)));
+	printk("                 STATUS = 0x%02x\n", inb(CS4231P(chip, STATUS)));
+	printk("  0x00: left input      = 0x%02x  ", snd_cs4231_in(chip, 0x00));
+	printk("  0x10: alt 1 (CFIG 2)  = 0x%02x\n", snd_cs4231_in(chip, 0x10));
+	printk("  0x01: right input     = 0x%02x  ", snd_cs4231_in(chip, 0x01));
+	printk("  0x11: alt 2 (CFIG 3)  = 0x%02x\n", snd_cs4231_in(chip, 0x11));
+	printk("  0x02: GF1 left input  = 0x%02x  ", snd_cs4231_in(chip, 0x02));
+	printk("  0x12: left line in    = 0x%02x\n", snd_cs4231_in(chip, 0x12));
+	printk("  0x03: GF1 right input = 0x%02x  ", snd_cs4231_in(chip, 0x03));
+	printk("  0x13: right line in   = 0x%02x\n", snd_cs4231_in(chip, 0x13));
+	printk("  0x04: CD left input   = 0x%02x  ", snd_cs4231_in(chip, 0x04));
+	printk("  0x14: timer low       = 0x%02x\n", snd_cs4231_in(chip, 0x14));
+	printk("  0x05: CD right input  = 0x%02x  ", snd_cs4231_in(chip, 0x05));
+	printk("  0x15: timer high      = 0x%02x\n", snd_cs4231_in(chip, 0x15));
+	printk("  0x06: left output     = 0x%02x  ", snd_cs4231_in(chip, 0x06));
+	printk("  0x16: left MIC (PnP)  = 0x%02x\n", snd_cs4231_in(chip, 0x16));
+	printk("  0x07: right output    = 0x%02x  ", snd_cs4231_in(chip, 0x07));
+	printk("  0x17: right MIC (PnP) = 0x%02x\n", snd_cs4231_in(chip, 0x17));
+	printk("  0x08: playback format = 0x%02x  ", snd_cs4231_in(chip, 0x08));
+	printk("  0x18: IRQ status      = 0x%02x\n", snd_cs4231_in(chip, 0x18));
+	printk("  0x09: iface (CFIG 1)  = 0x%02x  ", snd_cs4231_in(chip, 0x09));
+	printk("  0x19: left line out   = 0x%02x\n", snd_cs4231_in(chip, 0x19));
+	printk("  0x0a: pin control     = 0x%02x  ", snd_cs4231_in(chip, 0x0a));
+	printk("  0x1a: mono control    = 0x%02x\n", snd_cs4231_in(chip, 0x1a));
+	printk("  0x0b: init & status   = 0x%02x  ", snd_cs4231_in(chip, 0x0b));
+	printk("  0x1b: right line out  = 0x%02x\n", snd_cs4231_in(chip, 0x1b));
+	printk("  0x0c: revision & mode = 0x%02x  ", snd_cs4231_in(chip, 0x0c));
+	printk("  0x1c: record format   = 0x%02x\n", snd_cs4231_in(chip, 0x1c));
+	printk("  0x0d: loopback        = 0x%02x  ", snd_cs4231_in(chip, 0x0d));
+	printk("  0x1d: var freq (PnP)  = 0x%02x\n", snd_cs4231_in(chip, 0x1d));
+	printk("  0x0e: ply upr count   = 0x%02x  ", snd_cs4231_in(chip, 0x0e));
+	printk("  0x1e: ply lwr count   = 0x%02x\n", snd_cs4231_in(chip, 0x1e));
+	printk("  0x0f: rec upr count   = 0x%02x  ", snd_cs4231_in(chip, 0x0f));
+	printk("  0x1f: rec lwr count   = 0x%02x\n", snd_cs4231_in(chip, 0x1f));
+}
+
+#endif
+
+/*
+ *  CS4231 detection / MCE routines
+ */
+
+static void snd_cs4231_busy_wait(cs4231_t *chip)
+{
+	int timeout;
+
+	/* huh.. looks like this sequence is proper for CS4231A chip (GUS MAX) */
+	for (timeout = 5; timeout > 0; timeout--)
+		inb(CS4231P(chip, REGSEL));
+	/* end of cleanup sequence */
+	for (timeout = 250;
+	     timeout > 0 && (inb(CS4231P(chip, REGSEL)) & CS4231_INIT);
+	     timeout--)
+	     	udelay(10);
+}
+
+void snd_cs4231_mce_up(cs4231_t *chip)
+{
+	unsigned long flags;
+	int timeout;
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	for (timeout = 250; timeout > 0 && (inb(CS4231P(chip, REGSEL)) & CS4231_INIT); timeout--)
+		udelay(100);
+#ifdef CONFIG_SND_DEBUG
+	if (inb(CS4231P(chip, REGSEL)) & CS4231_INIT)
+		snd_printk("mce_up - auto calibration time out (0)\n");
+#endif
+	chip->mce_bit |= CS4231_MCE;
+	timeout = inb(CS4231P(chip, REGSEL));
+	if (timeout == 0x80)
+		snd_printk("mce_up [0x%lx]: serious init problem - codec still busy\n", chip->port);
+	if (!(timeout & CS4231_MCE))
+		outb(chip->mce_bit | (timeout & 0x1f), CS4231P(chip, REGSEL));
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+void snd_cs4231_mce_down(cs4231_t *chip)
+{
+	unsigned long flags;
+	int timeout;
+	signed long time;
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_cs4231_busy_wait(chip);
+#if 0
+	printk("(1) timeout = %i\n", timeout);
+#endif
+#ifdef CONFIG_SND_DEBUG
+	if (inb(CS4231P(chip, REGSEL)) & CS4231_INIT)
+		snd_printk("mce_down [0x%lx] - auto calibration time out (0)\n", CS4231P(chip, REGSEL));
+#endif
+	chip->mce_bit &= ~CS4231_MCE;
+	timeout = inb(CS4231P(chip, REGSEL));
+	outb(chip->mce_bit | (timeout & 0x1f), CS4231P(chip, REGSEL));
+	if (timeout == 0x80)
+		snd_printk("mce_down [0x%lx]: serious init problem - codec still busy\n", chip->port);
+	if ((timeout & CS4231_MCE) == 0 ||
+	    !(chip->hardware & (CS4231_HW_CS4231_MASK | CS4231_HW_CS4232_MASK))) {
+		spin_unlock_irqrestore(&chip->reg_lock, flags);
+		return;
+	}
+	snd_cs4231_busy_wait(chip);
+
+	/* calibration process */
+
+	for (timeout = 500; timeout > 0 && (snd_cs4231_in(chip, CS4231_TEST_INIT) & CS4231_CALIB_IN_PROGRESS) == 0; timeout--)
+		udelay(10);
+	if ((snd_cs4231_in(chip, CS4231_TEST_INIT) & CS4231_CALIB_IN_PROGRESS) == 0) {
+		snd_printd("cs4231_mce_down - auto calibration time out (1)\n");
+		spin_unlock_irqrestore(&chip->reg_lock, flags);
+		return;
+	}
+#if 0
+	printk("(2) timeout = %i, jiffies = %li\n", timeout, jiffies);
+#endif
+	time = HZ / 4;
+	while (snd_cs4231_in(chip, CS4231_TEST_INIT) & CS4231_CALIB_IN_PROGRESS) {
+		spin_unlock_irqrestore(&chip->reg_lock, flags);
+		if (time <= 0) {
+			snd_printk("mce_down - auto calibration time out (2)\n");
+			return;
+		}
+		set_current_state(TASK_INTERRUPTIBLE);
+		time = schedule_timeout(time);
+		spin_lock_irqsave(&chip->reg_lock, flags);
+	}
+#if 0
+	printk("(3) jiffies = %li\n", jiffies);
+#endif
+	time = HZ / 10;
+	while (inb(CS4231P(chip, REGSEL)) & CS4231_INIT) {
+		spin_unlock_irqrestore(&chip->reg_lock, flags);
+		if (time <= 0) {
+			snd_printk("mce_down - auto calibration time out (3)\n");
+			return;
+		}
+		set_current_state(TASK_INTERRUPTIBLE);		
+		time = schedule_timeout(time);
+		spin_lock_irqsave(&chip->reg_lock, flags);
+	}
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+#if 0
+	printk("(4) jiffies = %li\n", jiffies);
+	snd_printk("mce_down - exit = 0x%x\n", inb(CS4231P(chip, REGSEL)));
+#endif
+}
+
+static unsigned int snd_cs4231_get_count(unsigned char format, unsigned int size)
+{
+	switch (format & 0xe0) {
+	case CS4231_LINEAR_16:
+	case CS4231_LINEAR_16_BIG:
+		size >>= 1;
+		break;
+	case CS4231_ADPCM_16:
+		return size >> 2;
+	}
+	if (format & CS4231_STEREO)
+		size >>= 1;
+	return size;
+}
+
+static int snd_cs4231_trigger(snd_pcm_substream_t *substream,
+			      int cmd)
+{
+	cs4231_t *chip = snd_pcm_substream_chip(substream);
+	int result = 0;
+
+#if 0
+	printk("codec trigger!!! - what = %i, enable = %i, status = 0x%x\n", what, enable, inb(CS4231P(card, STATUS)));
+#endif
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_STOP:
+	{
+		unsigned int what = 0;
+		snd_pcm_substream_t *s = substream;
+		do {
+			if (s == chip->playback_substream) {
+				what |= CS4231_PLAYBACK_ENABLE;
+				snd_pcm_trigger_done(s, substream);
+			} else if (s == chip->capture_substream) {
+				what |= CS4231_RECORD_ENABLE;
+				snd_pcm_trigger_done(s, substream);
+			}
+			s = s->link_next;
+		} while (s != substream);
+		spin_lock(&chip->reg_lock);
+		if (cmd == SNDRV_PCM_TRIGGER_START)
+			chip->image[CS4231_IFACE_CTRL] |= what;
+		else
+			chip->image[CS4231_IFACE_CTRL] &= ~what;
+		snd_cs4231_out(chip, CS4231_IFACE_CTRL, chip->image[CS4231_IFACE_CTRL]);
+		spin_unlock(&chip->reg_lock);
+		break;
+	}
+	default:
+		result = -EINVAL;
+		break;
+	}
+#if 0
+	snd_cs4231_debug(chip);
+#endif
+	return result;
+}
+
+/*
+ *  CODEC I/O
+ */
+
+static unsigned char snd_cs4231_get_rate(unsigned int rate)
+{
+	int i;
+
+	for (i = 0; i < 14; i++)
+		if (rate == rates[i])
+			return freq_bits[i];
+	// snd_BUG();
+	return freq_bits[13];
+}
+
+static unsigned char snd_cs4231_get_format(cs4231_t *chip,
+				           int format,
+                                           int channels)
+{
+	unsigned char rformat;
+
+	rformat = CS4231_LINEAR_8;
+	switch (format) {
+	case SNDRV_PCM_FORMAT_MU_LAW:	rformat = CS4231_ULAW_8; break;
+	case SNDRV_PCM_FORMAT_A_LAW:	rformat = CS4231_ALAW_8; break;
+	case SNDRV_PCM_FORMAT_S16_LE:	rformat = CS4231_LINEAR_16; break;
+	case SNDRV_PCM_FORMAT_S16_BE:	rformat = CS4231_LINEAR_16_BIG; break;
+	case SNDRV_PCM_FORMAT_IMA_ADPCM:	rformat = CS4231_ADPCM_16; break;
+	}
+	if (channels > 1)
+		rformat |= CS4231_STEREO;
+#if 0
+	snd_printk("get_format: 0x%x (mode=0x%x)\n", format, mode);
+#endif
+	return rformat;
+}
+
+static void snd_cs4231_calibrate_mute(cs4231_t *chip, int mute)
+{
+	unsigned long flags;
+
+	mute = mute ? 1 : 0;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	if (chip->calibrate_mute == mute) {
+		spin_unlock_irqrestore(&chip->reg_lock, flags);
+		return;
+	}
+	if (!mute) {
+		snd_cs4231_dout(chip, CS4231_LEFT_INPUT, chip->image[CS4231_LEFT_INPUT]);
+		snd_cs4231_dout(chip, CS4231_RIGHT_INPUT, chip->image[CS4231_RIGHT_INPUT]);
+		snd_cs4231_dout(chip, CS4231_LOOPBACK, chip->image[CS4231_LOOPBACK]);
+	}
+	snd_cs4231_dout(chip, CS4231_AUX1_LEFT_INPUT, mute ? 0x80 : chip->image[CS4231_AUX1_LEFT_INPUT]);
+	snd_cs4231_dout(chip, CS4231_AUX1_RIGHT_INPUT, mute ? 0x80 : chip->image[CS4231_AUX1_RIGHT_INPUT]);
+	snd_cs4231_dout(chip, CS4231_AUX2_LEFT_INPUT, mute ? 0x80 : chip->image[CS4231_AUX2_LEFT_INPUT]);
+	snd_cs4231_dout(chip, CS4231_AUX2_RIGHT_INPUT, mute ? 0x80 : chip->image[CS4231_AUX2_RIGHT_INPUT]);
+	snd_cs4231_dout(chip, CS4231_LEFT_OUTPUT, mute ? 0x80 : chip->image[CS4231_LEFT_OUTPUT]);
+	snd_cs4231_dout(chip, CS4231_RIGHT_OUTPUT, mute ? 0x80 : chip->image[CS4231_RIGHT_OUTPUT]);
+	snd_cs4231_dout(chip, CS4231_LEFT_LINE_IN, mute ? 0x80 : chip->image[CS4231_LEFT_LINE_IN]);
+	snd_cs4231_dout(chip, CS4231_RIGHT_LINE_IN, mute ? 0x80 : chip->image[CS4231_RIGHT_LINE_IN]);
+	snd_cs4231_dout(chip, CS4231_MONO_CTRL, mute ? 0xc0 : chip->image[CS4231_MONO_CTRL]);
+	if (chip->hardware == CS4231_HW_INTERWAVE) {
+		snd_cs4231_dout(chip, CS4231_LEFT_MIC_INPUT, mute ? 0x80 : chip->image[CS4231_LEFT_MIC_INPUT]);
+		snd_cs4231_dout(chip, CS4231_RIGHT_MIC_INPUT, mute ? 0x80 : chip->image[CS4231_RIGHT_MIC_INPUT]);		
+		snd_cs4231_dout(chip, CS4231_LINE_LEFT_OUTPUT, mute ? 0x80 : chip->image[CS4231_LINE_LEFT_OUTPUT]);
+		snd_cs4231_dout(chip, CS4231_LINE_RIGHT_OUTPUT, mute ? 0x80 : chip->image[CS4231_LINE_RIGHT_OUTPUT]);
+	}
+	chip->calibrate_mute = mute;
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+static void snd_cs4231_playback_format(cs4231_t *chip,
+				       snd_pcm_hw_params_t *params,
+				       unsigned char pdfr)
+{
+	unsigned long flags;
+	int full_calib = 1;
+
+	down(&chip->mce_mutex);
+	snd_cs4231_calibrate_mute(chip, 1);
+	if (chip->hardware == CS4231_HW_CS4231A ||
+	    (chip->hardware & CS4231_HW_CS4232_MASK)) {
+		spin_lock_irqsave(&chip->reg_lock, flags);
+		if ((chip->image[CS4231_PLAYBK_FORMAT] & 0x0f) == (pdfr & 0x0f)) {	/* rate is same? */
+			snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] | 0x10);
+			snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT, chip->image[CS4231_PLAYBK_FORMAT] = pdfr);
+			snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] &= ~0x10);
+			full_calib = 0;
+		}
+		spin_unlock_irqrestore(&chip->reg_lock, flags);
+	}
+	if (full_calib) {
+		snd_cs4231_mce_up(chip);
+		spin_lock_irqsave(&chip->reg_lock, flags);
+		if (chip->hardware != CS4231_HW_INTERWAVE && !chip->single_dma) {
+			snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT,
+					(chip->image[CS4231_IFACE_CTRL] & CS4231_RECORD_ENABLE) ?
+					(pdfr & 0xf0) | (chip->image[CS4231_REC_FORMAT] & 0x0f) :
+				        pdfr);
+		} else {
+			snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT, chip->image[CS4231_PLAYBK_FORMAT] = pdfr);
+		}
+		spin_unlock_irqrestore(&chip->reg_lock, flags);
+		snd_cs4231_mce_down(chip);
+	}
+	snd_cs4231_calibrate_mute(chip, 0);
+	up(&chip->mce_mutex);
+}
+
+static void snd_cs4231_capture_format(cs4231_t *chip,
+				      snd_pcm_hw_params_t *params,
+                                      unsigned char cdfr)
+{
+	unsigned long flags;
+	int full_calib = 1;
+
+	down(&chip->mce_mutex);
+	snd_cs4231_calibrate_mute(chip, 1);
+	if (chip->hardware == CS4231_HW_CS4231A ||
+	    (chip->hardware & CS4231_HW_CS4232_MASK)) {
+		spin_lock_irqsave(&chip->reg_lock, flags);
+		if ((chip->image[CS4231_PLAYBK_FORMAT] & 0x0f) == (cdfr & 0x0f) ||	/* rate is same? */
+		    (chip->image[CS4231_IFACE_CTRL] & CS4231_PLAYBACK_ENABLE)) {
+			snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] | 0x20);
+			snd_cs4231_out(chip, CS4231_REC_FORMAT, chip->image[CS4231_REC_FORMAT] = cdfr);
+			snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] &= ~0x20);
+			full_calib = 0;
+		}
+		spin_unlock_irqrestore(&chip->reg_lock, flags);
+	}
+	if (full_calib) {
+		snd_cs4231_mce_up(chip);
+		spin_lock_irqsave(&chip->reg_lock, flags);
+		if (chip->hardware != CS4231_HW_INTERWAVE) {
+			if (!(chip->image[CS4231_IFACE_CTRL] & CS4231_PLAYBACK_ENABLE)) {
+				snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT,
+					       ((chip->single_dma ? cdfr : chip->image[CS4231_PLAYBK_FORMAT]) & 0xf0) |
+					       (cdfr & 0x0f));
+				spin_unlock_irqrestore(&chip->reg_lock, flags);
+				snd_cs4231_mce_down(chip);
+				snd_cs4231_mce_up(chip);
+				spin_lock_irqsave(&chip->reg_lock, flags);
+			}
+		}
+		snd_cs4231_out(chip, CS4231_REC_FORMAT, cdfr);
+		spin_unlock_irqrestore(&chip->reg_lock, flags);
+		snd_cs4231_mce_down(chip);
+	}
+	snd_cs4231_calibrate_mute(chip, 0);
+	up(&chip->mce_mutex);
+}
+
+/*
+ *  Timer interface
+ */
+
+static unsigned long snd_cs4231_timer_resolution(snd_timer_t * timer)
+{
+	cs4231_t *chip = snd_timer_chip(timer);
+	if (chip->hardware & CS4231_HW_CS4236B_MASK)
+		return 14467;
+	else
+		return chip->image[CS4231_PLAYBK_FORMAT] & 1 ? 9969 : 9920;
+}
+
+static int snd_cs4231_timer_start(snd_timer_t * timer)
+{
+	unsigned long flags;
+	unsigned int ticks;
+	cs4231_t *chip = snd_timer_chip(timer);
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	ticks = timer->sticks;
+	if ((chip->image[CS4231_ALT_FEATURE_1] & CS4231_TIMER_ENABLE) == 0 ||
+	    (unsigned char)(ticks >> 8) != chip->image[CS4231_TIMER_HIGH] ||
+	    (unsigned char)ticks != chip->image[CS4231_TIMER_LOW]) {
+		snd_cs4231_out(chip, CS4231_TIMER_HIGH, chip->image[CS4231_TIMER_HIGH] = (unsigned char) (ticks >> 8));
+		snd_cs4231_out(chip, CS4231_TIMER_LOW, chip->image[CS4231_TIMER_LOW] = (unsigned char) ticks);
+		snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] | CS4231_TIMER_ENABLE);
+	}
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return 0;
+}
+
+static int snd_cs4231_timer_stop(snd_timer_t * timer)
+{
+	unsigned long flags;
+	cs4231_t *chip = snd_timer_chip(timer);
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] &= ~CS4231_TIMER_ENABLE);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return 0;
+}
+
+static void snd_cs4231_init(cs4231_t *chip)
+{
+	unsigned long flags;
+
+	snd_cs4231_mce_down(chip);
+
+#ifdef SNDRV_DEBUG_MCE
+	snd_printk("init: (1)\n");
+#endif
+	snd_cs4231_mce_up(chip);
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_PLAYBACK_ENABLE | CS4231_PLAYBACK_PIO |
+			     CS4231_RECORD_ENABLE | CS4231_RECORD_PIO |
+			     CS4231_CALIB_MODE);
+	chip->image[CS4231_IFACE_CTRL] |= CS4231_AUTOCALIB;
+	snd_cs4231_out(chip, CS4231_IFACE_CTRL, chip->image[CS4231_IFACE_CTRL]);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	snd_cs4231_mce_down(chip);
+
+#ifdef SNDRV_DEBUG_MCE
+	snd_printk("init: (2)\n");
+#endif
+
+	snd_cs4231_mce_up(chip);
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1]);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	snd_cs4231_mce_down(chip);
+
+#ifdef SNDRV_DEBUG_MCE
+	snd_printk("init: (3) - afei = 0x%x\n", chip->image[CS4231_ALT_FEATURE_1]);
+#endif
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_cs4231_out(chip, CS4231_ALT_FEATURE_2, chip->image[CS4231_ALT_FEATURE_2]);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+	snd_cs4231_mce_up(chip);
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT, chip->image[CS4231_PLAYBK_FORMAT]);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	snd_cs4231_mce_down(chip);
+
+#ifdef SNDRV_DEBUG_MCE
+	snd_printk("init: (4)\n");
+#endif
+
+	snd_cs4231_mce_up(chip);
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_cs4231_out(chip, CS4231_REC_FORMAT, chip->image[CS4231_REC_FORMAT]);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	snd_cs4231_mce_down(chip);
+
+#ifdef SNDRV_DEBUG_MCE
+	snd_printk("init: (5)\n");
+#endif
+}
+
+static int snd_cs4231_open(cs4231_t *chip, unsigned int mode)
+{
+	unsigned long flags;
+
+	down(&chip->open_mutex);
+	if ((chip->mode & mode) ||
+	    ((chip->mode & CS4231_MODE_OPEN) && chip->single_dma)) {
+		up(&chip->open_mutex);
+		return -EAGAIN;
+	}
+	if (chip->mode & CS4231_MODE_OPEN) {
+		chip->mode |= mode;
+		up(&chip->open_mutex);
+		return 0;
+	}
+	/* ok. now enable and ack CODEC IRQ */
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_cs4231_out(chip, CS4231_IRQ_STATUS, CS4231_PLAYBACK_IRQ |
+		       CS4231_RECORD_IRQ |
+		       CS4231_TIMER_IRQ);
+	snd_cs4231_out(chip, CS4231_IRQ_STATUS, 0);
+	outb(0, CS4231P(chip, STATUS));	/* clear IRQ */
+	outb(0, CS4231P(chip, STATUS));	/* clear IRQ */
+	chip->image[CS4231_PIN_CTRL] |= CS4231_IRQ_ENABLE;
+	snd_cs4231_out(chip, CS4231_PIN_CTRL, chip->image[CS4231_PIN_CTRL]);
+	snd_cs4231_out(chip, CS4231_IRQ_STATUS, CS4231_PLAYBACK_IRQ |
+		       CS4231_RECORD_IRQ |
+		       CS4231_TIMER_IRQ);
+	snd_cs4231_out(chip, CS4231_IRQ_STATUS, 0);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+	chip->mode = mode;
+	up(&chip->open_mutex);
+	return 0;
+}
+
+static void snd_cs4231_close(cs4231_t *chip, unsigned int mode)
+{
+	unsigned long flags;
+
+	down(&chip->open_mutex);
+	chip->mode &= ~mode;
+	if (chip->mode & CS4231_MODE_OPEN) {
+		up(&chip->open_mutex);
+		return;
+	}
+	snd_cs4231_calibrate_mute(chip, 1);
+
+	/* disable IRQ */
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_cs4231_out(chip, CS4231_IRQ_STATUS, 0);
+	outb(0, CS4231P(chip, STATUS));	/* clear IRQ */
+	outb(0, CS4231P(chip, STATUS));	/* clear IRQ */
+	chip->image[CS4231_PIN_CTRL] &= ~CS4231_IRQ_ENABLE;
+	snd_cs4231_out(chip, CS4231_PIN_CTRL, chip->image[CS4231_PIN_CTRL]);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+	/* now disable record & playback */
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	if (chip->image[CS4231_IFACE_CTRL] & (CS4231_PLAYBACK_ENABLE | CS4231_PLAYBACK_PIO |
+					       CS4231_RECORD_ENABLE | CS4231_RECORD_PIO)) {
+		spin_unlock_irqrestore(&chip->reg_lock, flags);
+		snd_cs4231_mce_up(chip);
+		spin_lock_irqsave(&chip->reg_lock, flags);
+		chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_PLAYBACK_ENABLE | CS4231_PLAYBACK_PIO |
+						     CS4231_RECORD_ENABLE | CS4231_RECORD_PIO);
+		snd_cs4231_out(chip, CS4231_IFACE_CTRL, chip->image[CS4231_IFACE_CTRL]);
+		spin_unlock_irqrestore(&chip->reg_lock, flags);
+		snd_cs4231_mce_down(chip);
+		spin_lock_irqsave(&chip->reg_lock, flags);
+	}
+
+	/* clear IRQ again */
+	snd_cs4231_out(chip, CS4231_IRQ_STATUS, 0);
+	outb(0, CS4231P(chip, STATUS));	/* clear IRQ */
+	outb(0, CS4231P(chip, STATUS));	/* clear IRQ */
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+	snd_cs4231_calibrate_mute(chip, 0);
+
+	chip->mode = 0;
+	up(&chip->open_mutex);
+}
+
+/*
+ *  timer open/close
+ */
+
+static int snd_cs4231_timer_open(snd_timer_t * timer)
+{
+	cs4231_t *chip = snd_timer_chip(timer);
+	snd_cs4231_open(chip, CS4231_MODE_TIMER);
+	return 0;
+}
+
+static int snd_cs4231_timer_close(snd_timer_t * timer)
+{
+	cs4231_t *chip = snd_timer_chip(timer);
+	snd_cs4231_close(chip, CS4231_MODE_TIMER);
+	return 0;
+}
+
+#ifdef TARGET_OS2
+static struct _snd_timer_hardware snd_cs4231_timer_table =
+{
+	SNDRV_TIMER_HW_AUTO,
+	9945,
+	65535,
+	snd_cs4231_timer_open,
+	snd_cs4231_timer_close,
+	snd_cs4231_timer_resolution,
+	snd_cs4231_timer_start,
+	snd_cs4231_timer_stop,
+};
+#else
+static struct _snd_timer_hardware snd_cs4231_timer_table =
+{
+	flags:		SNDRV_TIMER_HW_AUTO,
+	resolution:	9945,
+	ticks:		65535,
+	open:		snd_cs4231_timer_open,
+	close:		snd_cs4231_timer_close,
+	c_resolution:	snd_cs4231_timer_resolution,
+	start:		snd_cs4231_timer_start,
+	stop:		snd_cs4231_timer_stop,
+};
+#endif
+
+/*
+ *  ok.. exported functions..
+ */
+
+static int snd_cs4231_playback_hw_params(snd_pcm_substream_t * substream,
+					 snd_pcm_hw_params_t * hw_params)
+{
+	cs4231_t *chip = snd_pcm_substream_chip(substream);
+	unsigned char new_pdfr;
+	int err;
+
+	if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
+		return err;
+	new_pdfr = snd_cs4231_get_format(chip, params_format(hw_params), params_channels(hw_params)) |
+		   snd_cs4231_get_rate(params_rate(hw_params));
+	chip->set_playback_format(chip, hw_params, new_pdfr);
+	return 0;
+}
+
+static int snd_cs4231_playback_hw_free(snd_pcm_substream_t * substream)
+{
+	return snd_pcm_lib_free_pages(substream);
+}
+
+static int snd_cs4231_playback_prepare(snd_pcm_substream_t * substream)
+{
+	cs4231_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	unsigned long flags;
+	unsigned int size = snd_pcm_lib_buffer_bytes(substream);
+	unsigned int count = snd_pcm_lib_period_bytes(substream);
+
+	chip->p_dma_size = size;
+	chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_PLAYBACK_ENABLE | CS4231_PLAYBACK_PIO);
+	snd_dma_program(chip->dma1, runtime->dma_area, size, DMA_MODE_WRITE | DMA_AUTOINIT);
+	count = snd_cs4231_get_count(chip->image[CS4231_PLAYBK_FORMAT], count) - 1;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_cs4231_out(chip, CS4231_PLY_LWR_CNT, (unsigned char) count);
+	snd_cs4231_out(chip, CS4231_PLY_UPR_CNT, (unsigned char) (count >> 8));
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+#if 0
+	snd_cs4231_debug(chip);
+#endif
+	return 0;
+}
+
+static int snd_cs4231_capture_hw_params(snd_pcm_substream_t * substream,
+					snd_pcm_hw_params_t * hw_params)
+{
+	cs4231_t *chip = snd_pcm_substream_chip(substream);
+	unsigned char new_cdfr;
+	int err;
+
+	if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
+		return err;
+	new_cdfr = snd_cs4231_get_format(chip, params_format(hw_params), params_channels(hw_params)) |
+		   snd_cs4231_get_rate(params_rate(hw_params));
+	chip->set_capture_format(chip, hw_params, new_cdfr);
+	return 0;
+}
+
+static int snd_cs4231_capture_hw_free(snd_pcm_substream_t * substream)
+{
+	return snd_pcm_lib_free_pages(substream);
+}
+
+static int snd_cs4231_capture_prepare(snd_pcm_substream_t * substream)
+{
+	cs4231_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	unsigned long flags;
+	unsigned int size = snd_pcm_lib_buffer_bytes(substream);
+	unsigned int count = snd_pcm_lib_period_bytes(substream);
+
+	chip->c_dma_size = size;
+	chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_RECORD_ENABLE | CS4231_RECORD_PIO);
+	snd_dma_program(chip->dma2, runtime->dma_area, size, DMA_MODE_READ | DMA_AUTOINIT);
+	count = snd_cs4231_get_count(chip->image[CS4231_REC_FORMAT], count) - 1;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	if (chip->single_dma && chip->hardware != CS4231_HW_INTERWAVE) {
+		snd_cs4231_out(chip, CS4231_PLY_LWR_CNT, (unsigned char) count);
+		snd_cs4231_out(chip, CS4231_PLY_UPR_CNT, (unsigned char) (count >> 8));
+	} else {
+		snd_cs4231_out(chip, CS4231_REC_LWR_CNT, (unsigned char) count);
+		snd_cs4231_out(chip, CS4231_REC_UPR_CNT, (unsigned char) (count >> 8));
+	}
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return 0;
+}
+
+static void snd_cs4231_overrange(cs4231_t *chip)
+{
+	unsigned long flags;
+	unsigned char res;
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	res = snd_cs4231_in(chip, CS4231_TEST_INIT);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	if (res & (0x08 | 0x02))	/* detect overrange only above 0dB; may be user selectable? */
+		chip->capture_substream->runtime->overrange++;
+}
+
+void snd_cs4231_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	cs4231_t *chip = snd_magic_cast(cs4231_t, dev_id, return);
+	unsigned char status;
+
+	status = snd_cs4231_in(chip, CS4231_IRQ_STATUS);
+	if (status & CS4231_TIMER_IRQ) {
+		if (chip->timer)
+			snd_timer_interrupt(chip->timer, chip->timer->sticks);
+	}		
+	if (chip->single_dma && chip->hardware != CS4231_HW_INTERWAVE) {
+		if (status & CS4231_PLAYBACK_IRQ) {
+			if (chip->mode & CS4231_MODE_PLAY)
+				snd_pcm_period_elapsed(chip->playback_substream);
+			if (chip->mode & CS4231_MODE_RECORD) {
+				snd_cs4231_overrange(chip);
+				snd_pcm_period_elapsed(chip->capture_substream);
+			}
+		}
+	} else {
+		if (status & CS4231_PLAYBACK_IRQ)
+			snd_pcm_period_elapsed(chip->playback_substream);
+		if (status & CS4231_RECORD_IRQ) {
+			snd_cs4231_overrange(chip);
+			snd_pcm_period_elapsed(chip->capture_substream);
+		}
+	}
+
+	spin_lock(&chip->reg_lock);
+	snd_cs4231_outm(chip, CS4231_IRQ_STATUS, ~CS4231_ALL_IRQS | ~status, 0);
+	spin_unlock(&chip->reg_lock);
+}
+
+static snd_pcm_uframes_t snd_cs4231_playback_pointer(snd_pcm_substream_t * substream)
+{
+	cs4231_t *chip = snd_pcm_substream_chip(substream);
+	size_t ptr;
+
+	if (!(chip->image[CS4231_IFACE_CTRL] & CS4231_PLAYBACK_ENABLE))
+		return 0;
+	ptr = chip->p_dma_size - snd_dma_residue(chip->dma1);
+	return bytes_to_frames(substream->runtime, ptr);
+}
+
+static snd_pcm_uframes_t snd_cs4231_capture_pointer(snd_pcm_substream_t * substream)
+{
+	cs4231_t *chip = snd_pcm_substream_chip(substream);
+	size_t ptr;
+	
+	if (!(chip->image[CS4231_IFACE_CTRL] & CS4231_RECORD_ENABLE))
+		return 0;
+	ptr = chip->c_dma_size - snd_dma_residue(chip->dma2);
+	return bytes_to_frames(substream->runtime, ptr);
+}
+
+/*
+
+ */
+
+static int snd_cs4231_probe(cs4231_t *chip)
+{
+	unsigned long flags;
+	int i, id, rev;
+	unsigned char *ptr;
+	unsigned int hw;
+
+#if 0
+	snd_cs4231_debug(chip);
+#endif
+	id = 0;
+	for (i = 0; i < 50; i++) {
+		mb();
+		if (inb(CS4231P(chip, REGSEL)) & CS4231_INIT)
+			udelay(2000);
+		else {
+			spin_lock_irqsave(&chip->reg_lock, flags);
+			snd_cs4231_out(chip, CS4231_MISC_INFO, CS4231_MODE2);
+			id = snd_cs4231_in(chip, CS4231_MISC_INFO) & 0x0f;
+			spin_unlock_irqrestore(&chip->reg_lock, flags);
+			if (id == 0x0a)
+				break;	/* this is valid value */
+		}
+	}
+	snd_printdd("cs4231: port = 0x%lx, id = 0x%x\n", chip->port, id);
+	if (id != 0x0a)
+		return -ENODEV;	/* no valid device found */
+
+	if (((hw = chip->hardware) & CS4231_HW_TYPE_MASK) == CS4231_HW_DETECT) {
+		rev = snd_cs4231_in(chip, CS4231_VERSION) & 0xe7;
+		snd_printdd("CS4231: VERSION (I25) = 0x%x\n", rev);
+		if (rev == 0x80) {
+			chip->hardware = CS4231_HW_CS4231;
+		} else if (rev == 0xa0) {
+			chip->hardware = CS4231_HW_CS4231A;
+		} else if (rev == 0xa2) {
+			chip->hardware = CS4231_HW_CS4232;
+		} else if (rev == 0xb2) {
+			chip->hardware = CS4231_HW_CS4232A;
+		} else if (rev == 0x83) {
+			chip->hardware = CS4231_HW_CS4236;
+		} else if (rev == 0x03) {
+			chip->hardware = CS4231_HW_CS4236B;
+		} else {
+			snd_printk("unknown CS chip with version 0x%x\n", rev);
+			return -ENODEV;		/* unknown CS4231 chip? */
+		}
+	}
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	inb(CS4231P(chip, STATUS));	/* clear any pendings IRQ */
+	outb(0, CS4231P(chip, STATUS));
+	mb();
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+	chip->image[CS4231_MISC_INFO] = CS4231_MODE2;
+	switch (chip->hardware) {
+	case CS4231_HW_INTERWAVE:
+		chip->image[CS4231_MISC_INFO] = CS4231_IW_MODE3;
+		break;
+	case CS4231_HW_CS4235:
+	case CS4231_HW_CS4236B:
+	case CS4231_HW_CS4237B:
+	case CS4231_HW_CS4238B:
+	case CS4231_HW_CS4239:
+		if (hw == CS4231_HW_DETECT3)
+			chip->image[CS4231_MISC_INFO] = CS4231_4236_MODE3;
+		else
+			chip->hardware = CS4231_HW_CS4236;
+		break;
+	}
+
+	chip->image[CS4231_IFACE_CTRL] =
+	    (chip->image[CS4231_IFACE_CTRL] & ~CS4231_SINGLE_DMA) |
+	    (chip->single_dma ? CS4231_SINGLE_DMA : 0);
+	chip->image[CS4231_ALT_FEATURE_1] = 0x80;
+	chip->image[CS4231_ALT_FEATURE_2] = chip->hardware == CS4231_HW_INTERWAVE ? 0xc2 : 0x01;
+	ptr = (unsigned char *) &chip->image;
+	snd_cs4231_mce_down(chip);
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	for (i = 0; i < 32; i++)	/* ok.. fill all CS4231 registers */
+		snd_cs4231_out(chip, i, *ptr++);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	snd_cs4231_mce_up(chip);
+	snd_cs4231_mce_down(chip);
+
+	mdelay(2);
+
+	/* ok.. try check hardware version for CS4236+ chips */
+	if ((hw & CS4231_HW_TYPE_MASK) == CS4231_HW_DETECT) {
+		if (chip->hardware == CS4231_HW_CS4236B) {
+			rev = snd_cs4236_ext_in(chip, CS4236_VERSION);
+			snd_cs4236_ext_out(chip, CS4236_VERSION, 0xff);
+			id = snd_cs4236_ext_in(chip, CS4236_VERSION);
+			snd_cs4236_ext_out(chip, CS4236_VERSION, rev);
+			snd_printdd("CS4231: ext version; rev = 0x%x, id = 0x%x\n", rev, id);
+			if ((id & 0x1f) == 0x1d) {	/* CS4235 */
+				chip->hardware = CS4231_HW_CS4235;
+				switch (id >> 5) {
+				case 4:
+				case 5:
+				case 6:
+					break;
+				default:
+					snd_printk("unknown CS4235 chip (enhanced version = 0x%x)\n", id);
+				}
+			} else if ((id & 0x1f) == 0x0b) {	/* CS4236/B */
+				switch (id >> 5) {
+				case 4:
+				case 5:
+				case 6:
+				case 7:
+					chip->hardware = CS4231_HW_CS4236B;
+					break;
+				default:
+					snd_printk("unknown CS4236 chip (enhanced version = 0x%x)\n", id);
+				}
+			} else if ((id & 0x1f) == 0x08) {	/* CS4237B */
+				chip->hardware = CS4231_HW_CS4237B;
+				switch (id >> 5) {
+				case 4:
+				case 5:
+				case 6:
+				case 7:
+					break;
+				default:
+					snd_printk("unknown CS4237B chip (enhanced version = 0x%x)\n", id);
+				}
+			} else if ((id & 0x1f) == 0x09) {	/* CS4238B */
+				chip->hardware = CS4231_HW_CS4238B;
+				switch (id >> 5) {
+				case 5:
+				case 6:
+				case 7:
+					break;
+				default:
+					snd_printk("unknown CS4238B chip (enhanced version = 0x%x)\n", id);
+				}
+			} else if ((id & 0x1f) == 0x1e) {	/* CS4239 */
+				chip->hardware = CS4231_HW_CS4239;
+				switch (id >> 5) {
+				case 4:
+				case 5:
+				case 6:
+					break;
+				default:
+					snd_printk("unknown CS4239 chip (enhanced version = 0x%x)\n", id);
+				}
+			} else {
+				snd_printk("unknown CS4236/CS423xB chip (enhanced version = 0x%x)\n", id);
+			}
+		}
+	}
+	return 0;		/* all things are ok.. */
+}
+
+/*
+
+ */
+
+#ifdef TARGET_OS2
+static snd_pcm_hardware_t snd_cs4231_playback =
+{
+/*	info:		  */	(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START),
+/*	formats:	  */	(SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_IMA_ADPCM |
+			  	 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE),
+/*	rates:		  */	SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000,
+/*	rate_min:	  */	5510,
+/*	rate_max:	  */	48000,
+/*	channels_min:	  */	1,
+/*	channels_max:	  */	2,
+/*	buffer_bytes_max:  */	(128*1024),
+/*	period_bytes_min:  */	64,
+/*	period_bytes_max:  */	(128*1024),
+/*	periods_min:	  */	1,
+/*	periods_max:	  */	1024,
+/*	fifo_size:	  */	0,
+};
+
+static snd_pcm_hardware_t snd_cs4231_capture =
+{
+/*	info:		  */	(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START),
+/*	formats:	  */	(SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_IMA_ADPCM |
+				 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE),
+/*	rates:		  */	SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000,
+/*	rate_min:	  */	5510,
+/*	rate_max:	  */	48000,
+/*	channels_min:	  */	1,
+/*	channels_max:	  */	2,
+/*	buffer_bytes_max:  */	(128*1024),
+/*	period_bytes_min:  */	64,
+/*	period_bytes_max:  */	(128*1024),
+/*	periods_min:	  */	1,
+/*	periods_max:	  */	1024,
+/*	fifo_size:	  */	0,
+};
+#else
+static snd_pcm_hardware_t snd_cs4231_playback =
+{
+	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START),
+	formats:		(SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_IMA_ADPCM |
+				 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE),
+	rates:			SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000,
+	rate_min:		5510,
+	rate_max:		48000,
+	channels_min:		1,
+	channels_max:		2,
+	buffer_bytes_max:	(128*1024),
+	period_bytes_min:	64,
+	period_bytes_max:	(128*1024),
+	periods_min:		1,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+
+static snd_pcm_hardware_t snd_cs4231_capture =
+{
+	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START),
+	formats:		(SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_IMA_ADPCM |
+				 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE),
+	rates:			SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000,
+	rate_min:		5510,
+	rate_max:		48000,
+	channels_min:		1,
+	channels_max:		2,
+	buffer_bytes_max:	(128*1024),
+	period_bytes_min:	64,
+	period_bytes_max:	(128*1024),
+	periods_min:		1,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+#endif
+
+/*
+
+ */
+
+static int snd_cs4231_playback_open(snd_pcm_substream_t * substream)
+{
+	cs4231_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int err;
+
+	runtime->hw = snd_cs4231_playback;
+
+	/* hardware bug in InterWave chipset */
+	if (chip->hardware == CS4231_HW_INTERWAVE && chip->dma1 > 3)
+	    	runtime->hw.formats &= ~SNDRV_PCM_FMTBIT_MU_LAW;
+	
+	/* hardware limitation of cheap chips */
+	if (chip->hardware == CS4231_HW_CS4235 ||
+	    chip->hardware == CS4231_HW_CS4239)
+		runtime->hw.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE;
+
+	snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.buffer_bytes_max);
+	snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.period_bytes_max);
+
+	if (chip->claim_dma) {
+		if ((err = chip->claim_dma(chip, chip->dma_private_data, chip->dma1)) < 0)
+			return err;
+	}
+
+	if ((err = snd_cs4231_open(chip, CS4231_MODE_PLAY)) < 0) {
+		if (chip->release_dma)
+			chip->release_dma(chip, chip->dma_private_data, chip->dma1);
+		snd_free_pages(runtime->dma_area, runtime->dma_bytes);
+		return err;
+	}
+	chip->playback_substream = substream;
+	snd_pcm_set_sync(substream);
+	chip->rate_constraint(runtime);
+	return 0;
+}
+
+static int snd_cs4231_capture_open(snd_pcm_substream_t * substream)
+{
+	cs4231_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int err;
+
+	runtime->hw = snd_cs4231_capture;
+
+	/* hardware limitation of cheap chips */
+	if (chip->hardware == CS4231_HW_CS4235 ||
+	    chip->hardware == CS4231_HW_CS4239)
+		runtime->hw.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE;
+
+	if (chip->claim_dma) {
+		if ((err = chip->claim_dma(chip, chip->dma_private_data, chip->dma2)) < 0)
+			return err;
+	}
+
+	snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.buffer_bytes_max);
+	snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.period_bytes_max);
+
+	if ((err = snd_cs4231_open(chip, CS4231_MODE_RECORD)) < 0) {
+		if (chip->release_dma)
+			chip->release_dma(chip, chip->dma_private_data, chip->dma2);
+		snd_free_pages(runtime->dma_area, runtime->dma_bytes);
+		return err;
+	}
+	chip->capture_substream = substream;
+	snd_pcm_set_sync(substream);
+	chip->rate_constraint(runtime);
+	return 0;
+}
+
+static int snd_cs4231_playback_close(snd_pcm_substream_t * substream)
+{
+	cs4231_t *chip = snd_pcm_substream_chip(substream);
+
+	chip->playback_substream = NULL;
+	snd_cs4231_close(chip, CS4231_MODE_PLAY);
+	return 0;
+}
+
+static int snd_cs4231_capture_close(snd_pcm_substream_t * substream)
+{
+	cs4231_t *chip = snd_pcm_substream_chip(substream);
+
+	chip->capture_substream = NULL;
+	snd_cs4231_close(chip, CS4231_MODE_RECORD);
+	return 0;
+}
+
+#ifdef CONFIG_PM
+
+static void snd_cs4231_suspend(cs4231_t *chip)
+{
+	int reg;
+	unsigned long flags;
+	
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	for (reg = 0; reg < 32; reg++)
+		chip->image[reg] = snd_cs4231_in(chip, reg);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+static void snd_cs4231_resume(cs4231_t *chip)
+{
+	int reg;
+	unsigned long flags;
+	int timeout;
+	
+	snd_cs4231_mce_up(chip);
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	for (reg = 0; reg < 32; reg++) {
+		switch (reg) {
+		case CS4231_VERSION:
+			break;
+		default:
+			snd_cs4231_out(chip, reg, chip->image[reg]);
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+#if 0
+	snd_cs4231_mce_down(chip);
+#else
+	/* The following is a workaround to avoid freeze after resume on TP600E.
+	   This is the first half of copy of snd_cs4231_mce_down(), but doesn't
+	   include rescheduling.  -- iwai
+	   */
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_cs4231_busy_wait(chip);
+	chip->mce_bit &= ~CS4231_MCE;
+	timeout = inb(CS4231P(chip, REGSEL));
+	outb(chip->mce_bit | (timeout & 0x1f), CS4231P(chip, REGSEL));
+	if (timeout == 0x80)
+		snd_printk("down [0x%lx]: serious init problem - codec still busy\n", chip->port);
+	if ((timeout & CS4231_MCE) == 0 ||
+	    !(chip->hardware & (CS4231_HW_CS4231_MASK | CS4231_HW_CS4232_MASK))) {
+		spin_unlock_irqrestore(&chip->reg_lock, flags);
+		return;
+	}
+	snd_cs4231_busy_wait(chip);
+	return;
+#endif
+}
+
+static int snd_cs4231_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data)
+{
+	cs4231_t *chip = snd_magic_cast(cs4231_t, dev->data, return 0);
+
+	switch (rqst) {
+	case PM_SUSPEND:
+		if (chip->suspend)
+			(*chip->suspend)(chip);
+		break;
+	case PM_RESUME:
+		if (chip->resume)
+			(*chip->resume)(chip);
+		break;
+	}
+	return 0;
+}
+
+#endif /* CONFIG_PM */
+
+static int snd_cs4231_free(cs4231_t *chip)
+{
+	if (chip->res_port)
+		release_resource(chip->res_port);
+	if (chip->res_cport)
+		release_resource(chip->res_cport);
+	if (chip->irq >= 0) {
+		disable_irq(chip->irq);
+		if (!(chip->hwshare & CS4231_HWSHARE_IRQ))
+			free_irq(chip->irq, (void *) chip);
+	}
+	if (!(chip->hwshare & CS4231_HWSHARE_DMA1) && chip->dma1 >= 0) {
+		snd_dma_disable(chip->dma1);
+		free_dma(chip->dma1);
+	}
+	if (!(chip->hwshare & CS4231_HWSHARE_DMA2) && chip->dma2 >= 0 && chip->dma2 != chip->dma1) {
+		snd_dma_disable(chip->dma2);
+		free_dma(chip->dma2);
+	}
+#ifdef CONFIG_PM
+	if (chip->pm_dev)
+		pm_unregister(chip->pm_dev);
+#endif
+	if (chip->timer)
+		snd_device_free(chip->card, chip->timer);
+	snd_magic_kfree(chip);
+	return 0;
+}
+
+static int snd_cs4231_dev_free(snd_device_t *device)
+{
+	cs4231_t *chip = snd_magic_cast(cs4231_t, device->device_data, return -ENXIO);
+	return snd_cs4231_free(chip);	
+}
+
+const char *snd_cs4231_chip_id(cs4231_t *chip)
+{
+	switch (chip->hardware) {
+	case CS4231_HW_CS4231:	return "CS4231";
+	case CS4231_HW_CS4231A: return "CS4231A";
+	case CS4231_HW_CS4232:	return "CS4232";
+	case CS4231_HW_CS4232A:	return "CS4232A";
+	case CS4231_HW_CS4235:	return "CS4235";
+	case CS4231_HW_CS4236B: return "CS4236B";
+	case CS4231_HW_CS4237B: return "CS4237B";
+	case CS4231_HW_CS4238B: return "CS4238B";
+	case CS4231_HW_CS4239:	return "CS4239";
+	case CS4231_HW_INTERWAVE: return "AMD InterWave";
+	case CS4231_HW_OPL3SA2: return chip->card->shortname;
+	default: return "???";
+	}
+}
+
+int snd_cs4231_create(snd_card_t * card,
+	              unsigned long port,
+	              unsigned long cport,
+		      int irq, int dma1, int dma2,
+		      unsigned short hardware,
+		      unsigned short hwshare,
+		      cs4231_t ** rchip)
+{
+#ifdef TARGET_OS2
+	static snd_device_ops_t ops = {
+		snd_cs4231_dev_free,0,0
+	};                                
+#else
+	static snd_device_ops_t ops = {
+		dev_free:       snd_cs4231_dev_free,
+	};                                
+#endif
+	cs4231_t *chip;
+	int err;
+
+	*rchip = NULL;
+	chip = snd_magic_kcalloc(cs4231_t, 0, GFP_KERNEL);
+	if (chip == NULL)
+		return -ENOMEM;
+	chip->irq = -1;
+	chip->dma1 = -1;
+	chip->dma2 = -1;
+	chip->hardware = hardware;
+	chip->hwshare = hwshare;
+
+	if ((chip->res_port = request_region(port, 4, "CS4231")) == NULL) {
+		snd_cs4231_free(chip);
+		return -EBUSY;
+	}
+	chip->port = port;
+	if ((long)cport >= 0 && (chip->res_cport = request_region(cport, 8, "CS4232 Control")) == NULL) {
+		snd_cs4231_free(chip);
+		return -ENODEV;
+	}
+	chip->cport = cport;
+	if (!(hwshare & CS4231_HWSHARE_IRQ) && request_irq(irq, snd_cs4231_interrupt, SA_INTERRUPT, "CS4231", (void *) chip)) {
+		snd_cs4231_free(chip);
+		return -EBUSY;
+	}
+	chip->irq = irq;
+	if (!(hwshare & CS4231_HWSHARE_DMA1) && request_dma(dma1, "CS4231 - 1")) {
+		snd_cs4231_free(chip);
+		return -EBUSY;
+	}
+	chip->dma1 = dma1;
+	if (!(hwshare & CS4231_HWSHARE_DMA2) && dma1 != dma2 && dma2 >= 0 && request_dma(dma2, "CS4231 - 2")) {
+		snd_cs4231_free(chip);
+		return -EBUSY;
+	}
+	if (dma1 == dma2 || dma2 < 0) {
+		chip->single_dma = 1;
+		chip->dma2 = chip->dma1;
+	} else
+		chip->dma2 = dma2;
+
+	spin_lock_init(&chip->reg_lock);
+	init_MUTEX(&chip->mce_mutex);
+	init_MUTEX(&chip->open_mutex);
+	chip->card = card;
+	chip->rate_constraint = snd_cs4231_xrate;
+	chip->set_playback_format = snd_cs4231_playback_format;
+	chip->set_capture_format = snd_cs4231_capture_format;
+        memcpy(&chip->image, &snd_cs4231_original_image, sizeof(snd_cs4231_original_image));
+
+	/* global setup */
+	if (snd_cs4231_probe(chip) < 0) {
+		snd_cs4231_free(chip);
+		return -ENODEV;
+	}
+	snd_cs4231_init(chip);
+
+	if (chip->hardware & CS4231_HW_CS4232_MASK) {
+		if (chip->res_cport == NULL)
+			snd_printk("CS4232 control port features are not accessible\n");
+	}
+
+	/* Register device */
+	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+		snd_cs4231_free(chip);
+		return err;
+	}
+
+#ifdef CONFIG_PM
+	/* Power Management */
+	chip->suspend = snd_cs4231_suspend;
+	chip->resume = snd_cs4231_resume;
+	chip->pm_dev = pm_register(PM_ISA_DEV, 0, snd_cs4231_pm_callback);
+	if (chip->pm_dev)
+		chip->pm_dev->data = chip;
+#endif
+
+	*rchip = chip;
+	return 0;
+}
+
+#ifdef TARGET_OS2
+static snd_pcm_ops_t snd_cs4231_playback_ops = {
+	snd_cs4231_playback_open,
+	snd_cs4231_playback_close,
+	snd_pcm_lib_ioctl,
+	snd_cs4231_playback_hw_params,
+	snd_cs4231_playback_hw_free,
+	snd_cs4231_playback_prepare,
+	snd_cs4231_trigger,
+	snd_cs4231_playback_pointer,0,0
+};
+
+static snd_pcm_ops_t snd_cs4231_capture_ops = {
+	snd_cs4231_capture_open,
+	snd_cs4231_capture_close,
+	snd_pcm_lib_ioctl,
+	snd_cs4231_capture_hw_params,
+	snd_cs4231_capture_hw_free,
+	snd_cs4231_capture_prepare,
+	snd_cs4231_trigger,
+	snd_cs4231_capture_pointer,0,0
+};
+#else
+static snd_pcm_ops_t snd_cs4231_playback_ops = {
+	open:		snd_cs4231_playback_open,
+	close:		snd_cs4231_playback_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	hw_params:	snd_cs4231_playback_hw_params,
+	hw_free:	snd_cs4231_playback_hw_free,
+	prepare:	snd_cs4231_playback_prepare,
+	trigger:	snd_cs4231_trigger,
+	pointer:	snd_cs4231_playback_pointer,
+};
+
+static snd_pcm_ops_t snd_cs4231_capture_ops = {
+	open:		snd_cs4231_capture_open,
+	close:		snd_cs4231_capture_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	hw_params:	snd_cs4231_capture_hw_params,
+	hw_free:	snd_cs4231_capture_hw_free,
+	prepare:	snd_cs4231_capture_prepare,
+	trigger:	snd_cs4231_trigger,
+	pointer:	snd_cs4231_capture_pointer,
+};
+#endif
+
+static void snd_cs4231_pcm_free(snd_pcm_t *pcm)
+{
+	cs4231_t *chip = snd_magic_cast(cs4231_t, pcm->private_data, return);
+	chip->pcm = NULL;
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+int snd_cs4231_pcm(cs4231_t *chip, int device, snd_pcm_t **rpcm)
+{
+	snd_pcm_t *pcm;
+	int err;
+
+	if ((err = snd_pcm_new(chip->card, "CS4231", device, 1, 1, &pcm)) < 0)
+		return err;
+
+	spin_lock_init(&chip->reg_lock);
+	init_MUTEX(&chip->mce_mutex);
+	init_MUTEX(&chip->open_mutex);
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_cs4231_playback_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_cs4231_capture_ops);
+	
+	/* global setup */
+	pcm->private_data = chip;
+	pcm->private_free = snd_cs4231_pcm_free;
+	pcm->info_flags = 0;
+	if (chip->single_dma)
+		pcm->info_flags |= SNDRV_PCM_INFO_HALF_DUPLEX;
+	if (chip->hardware != CS4231_HW_INTERWAVE)
+		pcm->info_flags |= SNDRV_PCM_INFO_JOINT_DUPLEX;
+	strcpy(pcm->name, snd_cs4231_chip_id(chip));
+
+	snd_pcm_lib_preallocate_pages_for_all(pcm, 64*1024, chip->dma1 > 3 || chip->dma2 > 3 ? 128*1024 : 64*1024, GFP_KERNEL|GFP_DMA);
+
+	chip->pcm = pcm;
+	if (rpcm)
+		*rpcm = pcm;
+	return 0;
+}
+
+static void snd_cs4231_timer_free(snd_timer_t *timer)
+{
+	cs4231_t *chip = snd_magic_cast(cs4231_t, timer->private_data, return);
+	chip->timer = NULL;
+}
+
+int snd_cs4231_timer(cs4231_t *chip, int device, snd_timer_t **rtimer)
+{
+	snd_timer_t *timer;
+	snd_timer_id_t tid;
+	int err;
+
+	/* Timer initialization */
+	tid.dev_class = SNDRV_TIMER_CLASS_CARD;
+	tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE;
+	tid.card = chip->card->number;
+	tid.device = device;
+	tid.subdevice = 0;
+	if ((err = snd_timer_new(chip->card, "CS4231", &tid, &timer)) < 0)
+		return err;
+	strcpy(timer->name, snd_cs4231_chip_id(chip));
+	timer->private_data = chip;
+	timer->private_free = snd_cs4231_timer_free;
+	timer->hw = snd_cs4231_timer_table;
+	chip->timer = timer;
+	if (rtimer)
+		*rtimer = timer;
+	return 0;
+}
+	
+/*
+ *  MIXER part
+ */
+
+static int snd_cs4231_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	static char *texts[4] = {
+		"Line", "Aux", "Mic", "Mix"
+	};
+	static char *opl3sa_texts[4] = {
+		"Line", "CD", "Mic", "Mix"
+	};
+	static char *gusmax_texts[4] = {
+		"Line", "Synth", "Mic", "Mix"
+	};
+	char **ptexts = texts;
+	cs4231_t *chip = snd_kcontrol_chip(kcontrol);
+
+	snd_assert(chip->card != NULL, return -EINVAL);
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 2;
+	uinfo->value.enumerated.items = 4;
+	if (uinfo->value.enumerated.item > 3)
+		uinfo->value.enumerated.item = 3;
+	if (!strcmp(chip->card->driver, "GUS MAX"))
+		ptexts = gusmax_texts;
+	switch (chip->hardware) {
+	case CS4231_HW_INTERWAVE: ptexts = gusmax_texts; break;
+	case CS4231_HW_OPL3SA2: ptexts = opl3sa_texts; break;
+	}
+	strcpy(uinfo->value.enumerated.name, ptexts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int snd_cs4231_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	cs4231_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	ucontrol->value.enumerated.item[0] = (chip->image[CS4231_LEFT_INPUT] & CS4231_MIXS_ALL) >> 6;
+	ucontrol->value.enumerated.item[1] = (chip->image[CS4231_RIGHT_INPUT] & CS4231_MIXS_ALL) >> 6;
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return 0;
+}
+
+static int snd_cs4231_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	cs4231_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	unsigned short left, right;
+	int change;
+	
+	if (ucontrol->value.enumerated.item[0] > 3 ||
+	    ucontrol->value.enumerated.item[1] > 3)
+		return -EINVAL;
+	left = ucontrol->value.enumerated.item[0] << 6;
+	right = ucontrol->value.enumerated.item[1] << 6;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	left = (chip->image[CS4231_LEFT_INPUT] & ~CS4231_MIXS_ALL) | left;
+	right = (chip->image[CS4231_RIGHT_INPUT] & ~CS4231_MIXS_ALL) | right;
+	change = left != chip->image[CS4231_LEFT_INPUT] ||
+	         right != chip->image[CS4231_RIGHT_INPUT];
+	snd_cs4231_out(chip, CS4231_LEFT_INPUT, left);
+	snd_cs4231_out(chip, CS4231_RIGHT_INPUT, right);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return change;
+}
+
+int snd_cs4231_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+
+	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	return 0;
+}
+
+int snd_cs4231_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	cs4231_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 8) & 0xff;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0xff;
+	
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	ucontrol->value.integer.value[0] = (chip->image[reg] >> shift) & mask;
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	if (invert)
+		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+	return 0;
+}
+
+int snd_cs4231_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	cs4231_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 8) & 0xff;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0xff;
+	int change;
+	unsigned short val;
+	
+	val = (ucontrol->value.integer.value[0] & mask);
+	if (invert)
+		val = mask - val;
+	val <<= shift;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	val = (chip->image[reg] & ~(mask << shift)) | val;
+	change = val != chip->image[reg];
+	snd_cs4231_out(chip, reg, val);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return change;
+}
+
+int snd_cs4231_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+
+	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	return 0;
+}
+
+int snd_cs4231_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	cs4231_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int left_reg = kcontrol->private_value & 0xff;
+	int right_reg = (kcontrol->private_value >> 8) & 0xff;
+	int shift_left = (kcontrol->private_value >> 16) & 0x07;
+	int shift_right = (kcontrol->private_value >> 19) & 0x07;
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+	int invert = (kcontrol->private_value >> 22) & 1;
+	
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	ucontrol->value.integer.value[0] = (chip->image[left_reg] >> shift_left) & mask;
+	ucontrol->value.integer.value[1] = (chip->image[right_reg] >> shift_right) & mask;
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	if (invert) {
+		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+		ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1];
+	}
+	return 0;
+}
+
+int snd_cs4231_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	cs4231_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int left_reg = kcontrol->private_value & 0xff;
+	int right_reg = (kcontrol->private_value >> 8) & 0xff;
+	int shift_left = (kcontrol->private_value >> 16) & 0x07;
+	int shift_right = (kcontrol->private_value >> 19) & 0x07;
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+	int invert = (kcontrol->private_value >> 22) & 1;
+	int change;
+	unsigned short val1, val2;
+	
+	val1 = ucontrol->value.integer.value[0] & mask;
+	val2 = ucontrol->value.integer.value[1] & mask;
+	if (invert) {
+		val1 = mask - val1;
+		val2 = mask - val2;
+	}
+	val1 <<= shift_left;
+	val2 <<= shift_right;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	val1 = (chip->image[left_reg] & ~(mask << shift_left)) | val1;
+	val2 = (chip->image[right_reg] & ~(mask << shift_right)) | val2;
+	change = val1 != chip->image[left_reg] || val2 != chip->image[right_reg];
+	snd_cs4231_out(chip, left_reg, val1);
+	snd_cs4231_out(chip, right_reg, val2);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return change;
+}
+
+#define CS4231_CONTROLS (sizeof(snd_cs4231_controls)/sizeof(snd_kcontrol_new_t))
+
+static snd_kcontrol_new_t snd_cs4231_controls[] = {
+CS4231_DOUBLE("PCM Playback Switch", 0, CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1),
+CS4231_DOUBLE("PCM Playback Volume", 0, CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1),
+CS4231_DOUBLE("Line Playback Switch", 0, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1),
+CS4231_DOUBLE("Line Playback Volume", 0, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 31, 1),
+CS4231_DOUBLE("Aux Playback Switch", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1),
+CS4231_DOUBLE("Aux Playback Volume", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1),
+CS4231_DOUBLE("Aux Playback Switch", 1, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1),
+CS4231_DOUBLE("Aux Playback Volume", 1, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1),
+CS4231_SINGLE("Mono Playback Switch", 0, CS4231_MONO_CTRL, 7, 1, 1),
+CS4231_SINGLE("Mono Playback Volume", 0, CS4231_MONO_CTRL, 0, 15, 1),
+CS4231_SINGLE("Mono Output Playback Switch", 0, CS4231_MONO_CTRL, 6, 1, 1),
+CS4231_SINGLE("Mono Output Playback Bypass", 0, CS4231_MONO_CTRL, 5, 1, 0),
+CS4231_DOUBLE("Capture Volume", 0, CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 0, 0, 15, 0),
+{
+#ifdef TARGET_OS2
+	SNDRV_CTL_ELEM_IFACE_MIXER,0,0,
+	"Capture Source",0,0, 0,
+	snd_cs4231_info_mux,
+	snd_cs4231_get_mux,
+	snd_cs4231_put_mux,0
+#else
+	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	name: "Capture Source",
+	info: snd_cs4231_info_mux,
+	get: snd_cs4231_get_mux,
+	put: snd_cs4231_put_mux,
+#endif
+},
+CS4231_DOUBLE("Mic Boost", 0, CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 5, 5, 1, 0),
+CS4231_SINGLE("Loopback Capture Switch", 0, CS4231_LOOPBACK, 0, 1, 0),
+CS4231_SINGLE("Loopback Capture Volume", 0, CS4231_LOOPBACK, 2, 63, 1)
+};
+                                        
+int snd_cs4231_mixer(cs4231_t *chip)
+{
+	snd_card_t *card;
+	int err, idx;
+
+	snd_assert(chip != NULL && chip->pcm != NULL, return -EINVAL);
+
+	card = chip->card;
+
+	strcpy(card->mixername, chip->pcm->name);
+
+	for (idx = 0; idx < CS4231_CONTROLS; idx++) {
+		if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4231_controls[idx], chip))) < 0)
+			return err;
+	}
+	return 0;
+}
+
+EXPORT_SYMBOL(snd_cs4231_out);
+EXPORT_SYMBOL(snd_cs4231_in);
+EXPORT_SYMBOL(snd_cs4231_outm);
+EXPORT_SYMBOL(snd_cs4236_ext_out);
+EXPORT_SYMBOL(snd_cs4236_ext_in);
+EXPORT_SYMBOL(snd_cs4231_mce_up);
+EXPORT_SYMBOL(snd_cs4231_mce_down);
+EXPORT_SYMBOL(snd_cs4231_interrupt);
+EXPORT_SYMBOL(snd_cs4231_chip_id);
+EXPORT_SYMBOL(snd_cs4231_create);
+EXPORT_SYMBOL(snd_cs4231_pcm);
+EXPORT_SYMBOL(snd_cs4231_mixer);
+EXPORT_SYMBOL(snd_cs4231_timer);
+EXPORT_SYMBOL(snd_cs4231_info_single);
+EXPORT_SYMBOL(snd_cs4231_get_single);
+EXPORT_SYMBOL(snd_cs4231_put_single);
+EXPORT_SYMBOL(snd_cs4231_info_double);
+EXPORT_SYMBOL(snd_cs4231_get_double);
+EXPORT_SYMBOL(snd_cs4231_put_double);
+
+/*
+ *  INIT part
+ */
+
+static int __init alsa_cs4231_init(void)
+{
+	return 0;
+}
+
+static void __exit alsa_cs4231_exit(void)
+{
+}
+
+module_init(alsa_cs4231_init)
+module_exit(alsa_cs4231_exit)
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/cs423x/cs4232.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/cs423x/cs4232.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/cs423x/cs4232.c	(revision 91)
@@ -0,0 +1,2 @@
+#define CS4232
+#include "cs4236.c"
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/cs423x/cs4236.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/cs423x/cs4236.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/cs423x/cs4236.c	(revision 91)
@@ -0,0 +1,642 @@
+/*
+ *  Driver for generic CS4232/CS4235/CS4236/CS4236B/CS4237B/CS4238B/CS4239 chips
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#define SNDRV_MAIN_OBJECT_FILE
+#include <sound/driver.h>
+#include <sound/cs4231.h>
+#include <sound/mpu401.h>
+#include <sound/opl3.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+#define chip_t cs4231_t
+
+EXPORT_NO_SYMBOLS;
+#ifdef CS4232
+MODULE_DESCRIPTION("Cirrus Logic CS4232");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{Turtle Beach,TBS-2000},"
+		"{Turtle Beach,Tropez Plus},"
+		"{SIC CrystalWave 32},"
+		"{HP,Omnibook 5500}},"
+		"{TerraTec,Maestro 32/96}");
+#else
+MODULE_DESCRIPTION("Cirrus Logic CS4235-9");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{Crystal Semiconductors,CS4235},"
+		"{Crystal Semiconductors,CS4236},"
+		"{Crystal Semiconductors,CS4237},"
+		"{Crystal Semiconductors,CS4238},"
+		"{Crystal Semiconductors,CS4239},"
+		"{Genius,Sound Maker 3DJ},"
+		"{Hewlett Packard,HP6330 sound},"
+		"{Crystal Computer,TidalWave128},"
+		"{Acer,AW37},"
+		"{EliteGroup,P5TX-LA sound},"
+		"{Crystal,3D},"
+		"{Typhoon Soundsystem,CS4236B},"
+		"{TerraTec, AudioSystem EWS64XL},"
+		"{NewClear,3D},"
+		"{Dell,Optiplex GX1},"
+		"{Dell,Workstation 400 sound},"
+		"{Turtle Beach,Malibu},"
+		"{Crystal Semiconductors,CS4235},"
+		"{IBM,Adaptiva 2137 E24},"
+		"{Maxi Sound,16 PnP},"
+		"{Gallant,SC-70P},"
+		"{Acer,AW37/Pro},"
+		"{Acer,AW35/Pro},"
+		"{Intel, Marlin Spike Mobo CS4235},"
+		"{IBM,IntelliStation M Pro}}");
+#endif
+
+#ifdef CS4232
+#define IDENT "CS4232"
+#else
+#define IDENT "CS4236+"
+#endif
+
+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;	/* Enable this card */
+#ifdef __ISAPNP__
+#ifdef TARGET_OS2
+static int snd_isapnp[SNDRV_CARDS] = {1,1,1,1,1,1,1,1};
+#else
+static int snd_isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
+#endif
+#endif
+static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* PnP setup */
+static long snd_cport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* PnP setup */
+static long snd_mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;/* PnP setup */
+static long snd_fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* PnP setup */
+static long snd_sb_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* PnP setup */
+static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* 5,7,9,11,12,15 */
+static int snd_mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* 9,11,12,15 */
+static int snd_dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* 0,1,3,5,6,7 */
+static int snd_dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* 0,1,3,5,6,7 */
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for " IDENT " soundcard.");
+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 " IDENT " soundcard.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable " IDENT " soundcard.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+#ifdef __ISAPNP__
+MODULE_PARM(snd_isapnp, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_isapnp, "ISA PnP detection for specified soundcard.");
+MODULE_PARM_SYNTAX(snd_isapnp, SNDRV_ISAPNP_DESC);
+#endif
+MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_port, "Port # for " IDENT " driver.");
+MODULE_PARM_SYNTAX(snd_port, SNDRV_PORT12_DESC);
+MODULE_PARM(snd_cport, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_cport, "Control port # for " IDENT " driver.");
+MODULE_PARM_SYNTAX(snd_cport, SNDRV_PORT12_DESC);
+MODULE_PARM(snd_mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_mpu_port, "MPU-401 port # for " IDENT " driver.");
+MODULE_PARM_SYNTAX(snd_mpu_port, SNDRV_PORT12_DESC);
+MODULE_PARM(snd_fm_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_fm_port, "FM port # for " IDENT " driver.");
+MODULE_PARM_SYNTAX(snd_fm_port, SNDRV_PORT12_DESC);
+MODULE_PARM(snd_sb_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_sb_port, "SB port # for " IDENT " driver (optional).");
+MODULE_PARM_SYNTAX(snd_sb_port, SNDRV_PORT12_DESC);
+MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_irq, "IRQ # for " IDENT " driver.");
+MODULE_PARM_SYNTAX(snd_irq, SNDRV_IRQ_DESC);
+MODULE_PARM(snd_mpu_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_mpu_irq, "MPU-401 IRQ # for " IDENT " driver.");
+MODULE_PARM_SYNTAX(snd_mpu_irq, SNDRV_IRQ_DESC);
+MODULE_PARM(snd_dma1, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_dma1, "DMA1 # for " IDENT " driver.");
+MODULE_PARM_SYNTAX(snd_dma1, SNDRV_DMA_DESC);
+MODULE_PARM(snd_dma2, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_dma2, "DMA2 # for " IDENT " driver.");
+MODULE_PARM_SYNTAX(snd_dma2, SNDRV_DMA_DESC);
+
+struct snd_card_cs4236 {
+	struct resource *res_sb_port;
+#ifdef __ISAPNP__
+	struct isapnp_dev *wss;
+	struct isapnp_dev *ctrl;
+	struct isapnp_dev *mpu;
+#endif
+};
+
+static snd_card_t *snd_cs4236_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+
+#ifdef __ISAPNP__
+
+static struct isapnp_card *snd_cs4236_isapnp_cards[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR;
+static const struct isapnp_card_id *snd_cs4236_isapnp_id[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR;
+
+#ifdef TARGET_OS2
+#define ISAPNP_CS4232(_va, _vb, _vc, _device, _wss, _ctrl, _mpu401) \
+	{ \
+		0, ISAPNP_CARD_ID(_va, _vb, _vc, _device), \
+		{ ISAPNP_DEVICE_ID(_va, _vb, _vc, _wss), \
+                  ISAPNP_DEVICE_ID(_va, _vb, _vc, _ctrl), \
+		  ISAPNP_DEVICE_ID(_va, _vb, _vc, _mpu401) } \
+        }
+#define ISAPNP_CS4232_1(_va, _vb, _vc, _device, _wss, _ctrl, _mpu401) \
+	{ \
+		0, ISAPNP_CARD_ID(_va, _vb, _vc, _device), \
+		{ ISAPNP_DEVICE_ID(_va, _vb, _vc, _wss), \
+                  ISAPNP_DEVICE_ID(_va, _vb, _vc, _ctrl), \
+		  ISAPNP_DEVICE_ID('P', 'N', 'P', _mpu401) } \
+        }
+#define ISAPNP_CS4232_WOMPU(_va, _vb, _vc, _device, _wss, _ctrl) \
+	{ \
+		0, ISAPNP_CARD_ID(_va, _vb, _vc, _device), \
+		{ ISAPNP_DEVICE_ID(_va, _vb, _vc, _wss), \
+                  ISAPNP_DEVICE_ID(_va, _vb, _vc, _ctrl) } \
+        }
+#else
+#define ISAPNP_CS4232(_va, _vb, _vc, _device, _wss, _ctrl, _mpu401) \
+	{ \
+		ISAPNP_CARD_ID(_va, _vb, _vc, _device), \
+		devs : { ISAPNP_DEVICE_ID(_va, _vb, _vc, _wss), \
+                         ISAPNP_DEVICE_ID(_va, _vb, _vc, _ctrl), \
+			 ISAPNP_DEVICE_ID(_va, _vb, _vc, _mpu401) } \
+        }
+#define ISAPNP_CS4232_1(_va, _vb, _vc, _device, _wss, _ctrl, _mpu401) \
+	{ \
+		ISAPNP_CARD_ID(_va, _vb, _vc, _device), \
+		devs : { ISAPNP_DEVICE_ID(_va, _vb, _vc, _wss), \
+                         ISAPNP_DEVICE_ID(_va, _vb, _vc, _ctrl), \
+			 ISAPNP_DEVICE_ID('P', 'N', 'P', _mpu401) } \
+        }
+#define ISAPNP_CS4232_WOMPU(_va, _vb, _vc, _device, _wss, _ctrl) \
+	{ \
+		ISAPNP_CARD_ID(_va, _vb, _vc, _device), \
+		devs : { ISAPNP_DEVICE_ID(_va, _vb, _vc, _wss), \
+                         ISAPNP_DEVICE_ID(_va, _vb, _vc, _ctrl) } \
+        }
+#endif
+
+
+#ifdef CS4232
+static struct isapnp_card_id snd_card_pnpids[] __devinitdata = {
+	/* HP Omnibook 5500 onboard */
+	ISAPNP_CS4232('C','S','C',0x4232,0x0000,0x0002,0x0003),
+	/* Turtle Beach TBS-2000 (CS4232) */
+	ISAPNP_CS4232('C','S','C',0x7532,0x0000,0x0010,0xb006),
+	/* Turtle Beach Tropez Plus (CS4232) */
+	ISAPNP_CS4232_1('C','S','C',0x7632,0x0000,0x0010,0xb006),
+	/* SIC CrystalWave 32 (CS4232) */
+	ISAPNP_CS4232('C','S','C',0xf032,0x0000,0x0010,0x0003),
+	/* TerraTec Maestro 32/96 (CS4232) */
+	ISAPNP_CS4232('C','S','C',0x1a32,0x0000,0x0010,0x0003),
+	/* --- */
+	{ ISAPNP_CARD_END, }	/* end */
+};
+#else /* CS4236 */
+static struct isapnp_card_id snd_card_pnpids[] __devinitdata = {
+	/* Intel Marlin Spike Motherboard - CS4235 */
+	ISAPNP_CS4232('C','S','C',0x0225,0x0000,0x0010,0x0003),
+	/* Intel Marlin Spike Motherboard (#2) - CS4235 */
+	ISAPNP_CS4232('C','S','C',0x0225,0x0100,0x0110,0x0103),
+	/* Genius Sound Maker 3DJ - CS4237B */
+	ISAPNP_CS4232('C','S','C',0x0437,0x0000,0x0010,0x0003),
+	/* some uknown CS4236B */
+	ISAPNP_CS4232('C','S','C',0x0b35,0x0000,0x0010,0x0003),
+	/* CS4235 on mainboard without MPU */
+	ISAPNP_CS4232_WOMPU('C','S','C',0x1425,0x0100,0x0110),
+	/* HP 6330 Onboard sound */
+	ISAPNP_CS4232('C','S','C',0x1525,0x0100,0x0110,0x0103),
+	/* Crystal Computer TidalWave128 */
+	ISAPNP_CS4232('C','S','C',0x1e37,0x0000,0x0010,0x0003),
+	/* ACER AW37 - CS4235 */
+	ISAPNP_CS4232('C','S','C',0x4236,0x0000,0x0010,0x0003),
+	/* build-in soundcard in EliteGroup P5TX-LA motherboard - CS4237B */
+	ISAPNP_CS4232('C','S','C',0x4237,0x0000,0x0010,0x0003),
+	/* Crystal 3D - CS4237B */
+	ISAPNP_CS4232('C','S','C',0x4336,0x0000,0x0010,0x0003),
+	/* Typhoon Soundsystem PnP - CS4236B */
+	ISAPNP_CS4232('C','S','C',0x4536,0x0000,0x0010,0x0003),
+	/* TerraTec AudioSystem EWS64XL - CS4236B */
+	ISAPNP_CS4232('C','S','C',0xa836,0xa800,0xa810,0xa803),
+	/* Crystal Semiconductors CS4237B */
+	ISAPNP_CS4232('C','S','C',0x4637,0x0000,0x0010,0x0003),
+	/* NewClear 3D - CX4237B-XQ3 */
+	ISAPNP_CS4232('C','S','C',0x4837,0x0000,0x0010,0x0003),
+	/* Dell Optiplex GX1 - CS4236B */
+	ISAPNP_CS4232('C','S','C',0x6835,0x0000,0x0010,0x0003),
+	/* Dell P410 motherboard - CS4236B */
+	ISAPNP_CS4232_WOMPU('C','S','C',0x6835,0x0000,0x0010),
+	/* Dell Workstation 400 Onboard - CS4236B */
+	ISAPNP_CS4232('C','S','C',0x6836,0x0000,0x0010,0x0003),
+	/* Turtle Beach Malibu - CS4237B */
+	ISAPNP_CS4232('C','S','C',0x7537,0x0000,0x0010,0x0003),
+	/* CS4235 - onboard */
+	ISAPNP_CS4232('C','S','C',0x8025,0x0100,0x0110,0x0103),
+	/* IBM Aptiva 2137 E24 Onboard - CS4237B */
+	ISAPNP_CS4232('C','S','C',0x8037,0x0000,0x0010,0x0003),
+	/* Maxi Sound 16 PnP - CS4236B */
+	ISAPNP_CS4232('C','S','C',0x9836,0x0000,0x0010,0x0003),
+	/* Gallant SC-70P */
+	ISAPNP_CS4232('C','S','C',0x9837,0x0000,0x0010,0x0003),
+	/* IBM IntelliStation M Pro motherboard */
+	ISAPNP_CS4232_WOMPU('C','S','C',0xc835,0x0000,0x0010),
+	/* ACER AW37/Pro - CS4235 */
+	ISAPNP_CS4232('C','S','C',0xd925,0x0000,0x0010,0x0003),
+	/* ACER AW35/Pro - CS4237B */
+	ISAPNP_CS4232('C','S','C',0xd937,0x0000,0x0010,0x0003),
+	/* CS4235 without MPU401 */
+	ISAPNP_CS4232_WOMPU('C','S','C',0xe825,0x0100,0x0110),
+	/* CS4236B */
+	ISAPNP_CS4232('C','S','C',0xf235,0x0000,0x0010,0x0003),
+	/* CS4236B */
+	ISAPNP_CS4232('C','S','C',0xf238,0x0000,0x0010,0x0003),
+	/* --- */
+	{ ISAPNP_CARD_END, }	/* end */
+};
+#endif
+
+ISAPNP_CARD_TABLE(snd_card_pnpids);
+
+static int __init snd_card_cs4236_isapnp(int dev, struct snd_card_cs4236 *acard)
+{
+	const struct isapnp_card_id *id = snd_cs4236_isapnp_id[dev];
+	struct isapnp_card *card = snd_cs4236_isapnp_cards[dev];
+	struct isapnp_dev *pdev;
+	
+	acard->wss = isapnp_find_dev(card, id->devs[0].vendor, id->devs[0].function, NULL);
+	if (acard->wss->active) {
+		acard->wss = NULL;
+		return -EBUSY;
+	}
+	acard->ctrl = isapnp_find_dev(card, id->devs[1].vendor, id->devs[1].function, NULL);
+	if (acard->ctrl->active) {
+		acard->wss = acard->ctrl = NULL;
+		return -EBUSY;
+	}
+	if (id->devs[2].vendor && id->devs[2].function) {
+		acard->mpu = isapnp_find_dev(card, id->devs[2].vendor, id->devs[2].function, NULL);
+		if (acard->mpu->active) {
+			acard->wss = acard->ctrl = acard->mpu = NULL;
+			return -EBUSY;
+		}
+	}
+
+	/* WSS initialization */
+	pdev = acard->wss;
+	if (pdev->prepare(pdev) < 0)
+		return -EAGAIN;
+	if (snd_port[dev] != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[0], snd_port[dev], 4);
+	if (snd_fm_port[dev] != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[1], snd_fm_port[dev], 4);
+	if (snd_sb_port[dev] != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[2], snd_sb_port[dev], 16);
+	if (snd_irq[dev] != SNDRV_AUTO_IRQ)
+		isapnp_resource_change(&pdev->irq_resource[0], snd_irq[dev], 1);
+	if (snd_dma1[dev] != SNDRV_AUTO_DMA)
+		isapnp_resource_change(&pdev->dma_resource[0], snd_dma1[dev], 1);
+	if (snd_dma2[dev] != SNDRV_AUTO_DMA)
+		isapnp_resource_change(&pdev->dma_resource[1], snd_dma2[dev] < 0 ? 4 : snd_dma2[dev], 1);
+	if (pdev->activate(pdev)<0) {
+		snd_printk(IDENT " isapnp configure failed for WSS (out of resources?)\n");
+		return -EBUSY;
+	}
+	snd_port[dev] = pdev->resource[0].start;
+	snd_fm_port[dev] = pdev->resource[1].start;
+	snd_sb_port[dev] = pdev->resource[2].start;
+	snd_irq[dev] = pdev->irq_resource[0].start;
+	snd_dma1[dev] = pdev->dma_resource[0].start;
+	snd_dma2[dev] = pdev->dma_resource[1].start == 4 ? -1 : pdev->dma_resource[1].start;
+	snd_printdd("isapnp WSS: wss port=0x%lx, fm port=0x%lx, sb port=0x%lx\n",
+			snd_port[dev], snd_fm_port[dev], snd_sb_port[dev]);
+	snd_printdd("isapnp WSS: irq=%i, dma1=%i, dma2=%i\n",
+			snd_irq[dev], snd_dma1[dev], snd_dma2[dev]);
+	/* CTRL initialization */
+	pdev = acard->ctrl;
+	if (pdev->prepare(pdev) < 0) {
+		acard->wss->deactivate(acard->wss);
+		return -EAGAIN;
+	}
+	if (snd_cport[dev] != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[0], snd_cport[dev], 8);
+	if (pdev->activate(pdev)<0) {
+		snd_printk(IDENT " isapnp configure failed for control (out of resources?)\n");
+		acard->wss->deactivate(acard->wss);
+		return -EBUSY;
+	}
+	snd_cport[dev] = pdev->resource[0].start;
+	snd_printdd("isapnp CTRL: control port=0x%lx\n", snd_cport[dev]);
+	/* MPU initialization */
+	if (acard->mpu) {
+		pdev = acard->mpu;
+		if (pdev->prepare(pdev) < 0) {
+			acard->wss->deactivate(acard->wss);
+			acard->ctrl->deactivate(acard->ctrl);
+			return -EAGAIN;
+		}
+		if (snd_mpu_port[dev] != SNDRV_AUTO_PORT)
+			isapnp_resource_change(&pdev->resource[0], snd_mpu_port[dev], 2);
+		if (snd_mpu_irq[dev] != SNDRV_AUTO_IRQ)
+			isapnp_resource_change(&pdev->irq_resource[0], snd_mpu_irq[dev], 1);
+		if (pdev->activate(pdev)<0) {
+			snd_mpu_port[dev] = SNDRV_AUTO_PORT;
+			snd_printk(IDENT " isapnp configure failed for MPU (out of resources?)\n");
+		} else {
+			snd_mpu_port[dev] = pdev->resource[0].start;
+			snd_mpu_irq[dev] = pdev->irq_resource[0].start;
+		}
+		snd_printdd("isapnp MPU: port=0x%lx, irq=%i\n", snd_mpu_port[dev], snd_mpu_irq[dev]);
+	}
+	return 0;
+}
+
+static void snd_card_cs4236_deactivate(struct snd_card_cs4236 *acard)
+{
+	if (acard->wss) {
+		acard->wss->deactivate(acard->wss);
+		acard->wss = NULL;
+	}
+	if (acard->ctrl) {
+		acard->ctrl->deactivate(acard->ctrl);
+		acard->ctrl = NULL;
+	}
+	if (acard->mpu) {
+		acard->mpu->deactivate(acard->mpu);
+		acard->mpu = NULL;
+	}
+}
+#endif
+
+static void snd_card_cs4236_free(snd_card_t *card)
+{
+	struct snd_card_cs4236 *acard = (struct snd_card_cs4236 *)card->private_data;
+
+	if (acard) {
+#ifdef __ISAPNP__
+		snd_card_cs4236_deactivate(acard);
+#endif
+		if (acard->res_sb_port)
+			release_resource(acard->res_sb_port);
+	}
+}
+
+static int __init snd_card_cs4236_probe(int dev)
+{
+	snd_card_t *card;
+	struct snd_card_cs4236 *acard;
+	snd_pcm_t *pcm = NULL;
+	cs4231_t *chip;
+	opl3_t *opl3;
+	int err;
+
+#ifdef __ISAPNP__
+	if (!snd_isapnp[dev]) {
+#endif
+		if (snd_port[dev] == SNDRV_AUTO_PORT) {
+			snd_printk("specify snd_port\n");
+			return -EINVAL;
+		}
+		if (snd_cport[dev] == SNDRV_AUTO_PORT) {
+			snd_printk("specify snd_cport\n");
+			return -EINVAL;
+		}
+#ifdef __ISAPNP__
+	}
+#endif
+	card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE,
+			    sizeof(struct snd_card_cs4236));
+	if (card == NULL)
+		return -ENOMEM;
+	acard = (struct snd_card_cs4236 *)card->private_data;
+	card->private_free = snd_card_cs4236_free;
+#ifdef __ISAPNP__
+	if (snd_isapnp[dev] && (err = snd_card_cs4236_isapnp(dev, acard))<0) {
+		snd_printk("isapnp detection failed and probing for " IDENT " is not supported\n");
+		snd_card_free(card);
+		return -ENXIO;
+	}
+#endif
+	if (snd_mpu_port[dev] < 0)
+		snd_mpu_port[dev] = SNDRV_AUTO_PORT;
+	if (snd_fm_port[dev] < 0)
+		snd_fm_port[dev] = SNDRV_AUTO_PORT;
+	if (snd_sb_port[dev] < 0)
+		snd_sb_port[dev] = SNDRV_AUTO_PORT;
+	if (snd_sb_port[dev] != SNDRV_AUTO_PORT)
+		if ((acard->res_sb_port = request_region(snd_sb_port[dev], 16, IDENT " SB")) == NULL) {
+			snd_printk("unable to register SB port at 0x%lx\n", snd_sb_port[dev]);
+			snd_card_free(card);
+			return -ENOMEM;
+		}
+
+#ifdef CS4232
+	if ((err = snd_cs4231_create(card,
+				     snd_port[dev],
+				     snd_cport[dev],
+				     snd_irq[dev],
+				     snd_dma1[dev],
+				     snd_dma2[dev],
+				     CS4231_HW_DETECT,
+				     0,
+				     &chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_cs4231_pcm(chip, 0, &pcm)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_cs4231_mixer(chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+#else /* CS4236 */
+	if ((err = snd_cs4236_create(card,
+				     snd_port[dev],
+				     snd_cport[dev],
+				     snd_irq[dev],
+				     snd_dma1[dev],
+				     snd_dma2[dev],
+				     CS4231_HW_DETECT,
+				     0,
+				     &chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_cs4236_pcm(chip, 0, &pcm)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_cs4236_mixer(chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+#endif
+
+	if ((err = snd_cs4231_timer(chip, 0, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	if (snd_fm_port[dev] != SNDRV_AUTO_PORT) {
+		if (snd_opl3_create(card,
+				    snd_fm_port[dev], snd_fm_port[dev] + 2,
+				    OPL3_HW_OPL3_CS, 0, &opl3) < 0) {
+			snd_printk(IDENT ": OPL3 not detected\n");
+		} else {
+			if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
+				snd_card_free(card);
+				return err;
+			}
+		}
+	}
+
+	if (snd_mpu_irq[dev] >= 0 && snd_mpu_irq[dev] != SNDRV_AUTO_IRQ) {
+		if (snd_mpu401_uart_new(card, 0, MPU401_HW_CS4232,
+					snd_mpu_port[dev], 0,
+					snd_mpu_irq[dev], SA_INTERRUPT, NULL) < 0)
+			snd_printk(IDENT ": MPU401 not detected\n");
+	}
+	strcpy(card->driver, pcm->name);
+	strcpy(card->shortname, pcm->name);
+	sprintf(card->longname, "%s at 0x%lx, irq %i, dma %i",
+		pcm->name,
+		chip->port,
+		snd_irq[dev],
+		snd_dma1[dev]);
+	if (snd_dma1[dev] >= 0)
+		sprintf(card->longname + strlen(card->longname), "&%d", snd_dma2[dev]);
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	snd_cs4236_cards[dev] = card;
+	return 0;
+}
+
+#ifdef __ISAPNP__
+static int __init snd_cs4236_isapnp_detect(struct isapnp_card *card,
+                                           const struct isapnp_card_id *id)
+{
+	static int dev = 0;
+	int res;
+
+	for ( ; dev < SNDRV_CARDS; dev++) {
+		if (!snd_enable[dev])
+			continue;
+		snd_cs4236_isapnp_cards[dev] = card;
+		snd_cs4236_isapnp_id[dev] = id;
+		res = snd_card_cs4236_probe(dev);
+		if (res < 0)
+			return res;
+		dev++;
+		return 0;
+	}
+	return -ENODEV;
+}
+#endif /* __ISAPNP__ */
+
+static int __init alsa_card_cs423x_init(void)
+{
+	int dev, cards = 0;
+
+	for (dev = 0; dev < SNDRV_CARDS; dev++) {
+		if (!snd_enable[dev])
+			continue;
+#ifdef __ISAPNP__
+		if (snd_isapnp[dev])
+			continue;
+#endif
+		if (snd_card_cs4236_probe(dev) >= 0)
+			cards++;
+	}
+#ifdef __ISAPNP__
+	cards += isapnp_probe_cards(snd_card_pnpids, snd_cs4236_isapnp_detect);
+#endif
+	if (!cards) {
+#ifdef MODULE
+		snd_printk(IDENT " soundcard not found or device busy\n");
+#endif
+		return -ENODEV;
+	}
+	return 0;
+}
+
+static void __exit alsa_card_cs423x_exit(void)
+{
+	int idx;
+
+	for (idx = 0; idx < SNDRV_CARDS; idx++)
+		snd_card_free(snd_cs4236_cards[idx]);
+}
+
+module_init(alsa_card_cs423x_init)
+module_exit(alsa_card_cs423x_exit)
+
+#ifndef MODULE
+
+/* format is: snd-card-cs4232=snd_enable,snd_index,snd_id,snd_isapnp,snd_port,
+			      snd_cport,snd_mpu_port,snd_fm_port,snd_sb_port,
+			      snd_irq,snd_mpu_irq,snd_dma1,snd_dma1_size,
+			      snd_dma2,snd_dma2_size */
+/* format is: snd-card-cs4236=snd_enable,snd_index,snd_id,snd_isapnp,snd_port,
+			      snd_cport,snd_mpu_port,snd_fm_port,snd_sb_port,
+			      snd_irq,snd_mpu_irq,snd_dma1,snd_dma1_size,
+			      snd_dma2,snd_dma2_size */
+
+static int __init alsa_card_cs423x_setup(char *str)
+{
+	static unsigned __initdata nr_dev = 0;
+	int __attribute__ ((__unused__)) pnp = INT_MAX;
+
+	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,&pnp) == 2 &&
+	       get_option(&str,(int *)&snd_port[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_cport[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_mpu_port[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_fm_port[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_sb_port[nr_dev]) == 2 &&
+	       get_option(&str,&snd_irq[nr_dev]) == 2 &&
+	       get_option(&str,&snd_mpu_irq[nr_dev]) == 2 &&
+	       get_option(&str,&snd_dma1[nr_dev]) == 2 &&
+	       get_option(&str,&snd_dma2[nr_dev]) == 2);
+#ifdef __ISAPNP__
+	if (pnp != INT_MAX)
+		snd_isapnp[nr_dev] = pnp;
+#endif
+	nr_dev++;
+	return 1;
+}
+
+#ifdef CS4232
+__setup("snd-card-cs4232=", alsa_card_cs423x_setup);
+#else /* CS4236 */
+__setup("snd-card-cs4236=", alsa_card_cs423x_setup);
+#endif
+
+#endif /* ifndef MODULE */
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/cs423x/cs4236_lib.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/cs423x/cs4236_lib.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/cs423x/cs4236_lib.c	(revision 91)
@@ -0,0 +1,1034 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  Routines for control of CS4235/4236B/4237B/4238B/4239 chips
+ *
+ *  Note:
+ *     -----
+ *
+ *  Bugs:
+ *     -----
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/*
+ *  Indirect control registers (CS4236B+)
+ * 
+ *  C0
+ *     D8: WSS reset (all chips)
+ *
+ *  C1 (all chips except CS4236)
+ *     D7-D5: version 
+ *     D4-D0: chip id
+ *             11101 - CS4235
+ *             01011 - CS4236B
+ *             01000 - CS4237B
+ *             01001 - CS4238B
+ *             11110 - CS4239
+ *
+ *  C2
+ *     D7-D4: 3D Space (CS4235,CS4237B,CS4238B,CS4239)
+ *     D3-D0: 3D Center (CS4237B); 3D Volume (CS4238B)
+ * 
+ *  C3
+ *     D7: 3D Enable (CS4237B)
+ *     D6: 3D Mono Enable (CS4237B)
+ *     D5: 3D Serial Output (CS4237B,CS4238B)
+ *     D4: 3D Enable (CS4235,CS4238B,CS4239)
+ *
+ *  C4
+ *     D7: consumer serial port enable (CS4237B,CS4238B)
+ *     D6: channels status block reset (CS4237B,CS4238B)
+ *     D5: user bit in sub-frame of digital audio data (CS4237B,CS4238B)
+ *     D4: validity bit bit in sub-frame of digital audio data (CS4237B,CS4238B)
+ * 
+ *  C5  lower channel status (digital serial data description) (CS4237B,CS4238B)
+ *     D7-D6: first two bits of category code
+ *     D5: lock
+ *     D4-D3: pre-emphasis (0 = none, 1 = 50/15us)
+ *     D2: copy/copyright (0 = copy inhibited)
+ *     D1: 0 = digital audio / 1 = non-digital audio
+ *     
+ *  C6  upper channel status (digital serial data description) (CS4237B,CS4238B)
+ *     D7-D6: sample frequency (0 = 44.1kHz)
+ *     D5: generation status (0 = no indication, 1 = original/commercially precaptureed data)
+ *     D4-D0: category code (upper bits)
+ *
+ *  C7  reserved (must write 0)
+ *
+ *  C8  wavetable control
+ *     D7: volume control interrupt enable (CS4235,CS4239)
+ *     D6: hardware volume control format (CS4235,CS4239)
+ *     D3: wavetable serial port enable (all chips)
+ *     D2: DSP serial port switch (all chips)
+ *     D1: disable MCLK (all chips)
+ *     D0: force BRESET low (all chips)
+ *
+ */
+
+#define SNDRV_MAIN_OBJECT_FILE
+#include <sound/driver.h>
+#include <sound/cs4231.h>
+
+#define chip_t cs4231_t
+
+/*
+ *
+ */
+
+static unsigned char snd_cs4236_ext_map[18] = {
+	/* CS4236_LEFT_LINE */		0xff,
+	/* CS4236_RIGHT_LINE */		0xff,
+	/* CS4236_LEFT_MIC */		0xdf,
+	/* CS4236_RIGHT_MIC */		0xdf,
+	/* CS4236_LEFT_MIX_CTRL */	0xe0 | 0x18,
+	/* CS4236_RIGHT_MIX_CTRL */	0xe0,
+	/* CS4236_LEFT_FM */		0xbf,
+	/* CS4236_RIGHT_FM */		0xbf,
+	/* CS4236_LEFT_DSP */		0xbf,
+	/* CS4236_RIGHT_DSP */		0xbf,
+	/* CS4236_RIGHT_LOOPBACK */	0xbf,
+	/* CS4236_DAC_MUTE */		0xe0,
+	/* CS4236_ADC_RATE */		0x01,	/* 48kHz */
+	/* CS4236_DAC_RATE */		0x01,	/* 48kHz */
+	/* CS4236_LEFT_MASTER */	0xbf,
+	/* CS4236_RIGHT_MASTER */	0xbf,
+	/* CS4236_LEFT_WAVE */		0xbf,
+	/* CS4236_RIGHT_WAVE */		0xbf
+};
+
+/*
+ *
+ */
+
+static void snd_cs4236_ctrl_out(cs4231_t *chip, unsigned char reg, unsigned char val)
+{
+	outb(reg, chip->cport + 3);
+	outb(chip->cimage[reg] = val, chip->cport + 4);
+}
+
+static unsigned char snd_cs4236_ctrl_in(cs4231_t *chip, unsigned char reg)
+{
+	outb(reg, chip->cport + 3);
+	return inb(chip->cport + 4);
+}
+
+/*
+ *  PCM
+ */
+
+#define CLOCKS 8
+
+#ifdef TARGET_OS2
+static ratnum_t clocks[CLOCKS] = {
+	{ 16934400, 353, 353, 1 },
+	{ 16934400, 529, 529, 1 },
+	{ 16934400, 617, 617, 1 },
+	{ 16934400, 1058, 1058, 1 },
+	{ 16934400, 1764, 1764, 1 },
+	{ 16934400, 2117, 2117, 1 },
+	{ 16934400, 2558, 2558, 1 },
+	{ 16934400/16, 21, 192, 1 }
+};
+
+static snd_pcm_hw_constraint_ratnums_t hw_constraints_clocks = {
+	CLOCKS,
+	clocks,
+};
+#else
+static ratnum_t clocks[CLOCKS] = {
+	{ num: 16934400, den_min: 353, den_max: 353, den_step: 1 },
+	{ num: 16934400, den_min: 529, den_max: 529, den_step: 1 },
+	{ num: 16934400, den_min: 617, den_max: 617, den_step: 1 },
+	{ num: 16934400, den_min: 1058, den_max: 1058, den_step: 1 },
+	{ num: 16934400, den_min: 1764, den_max: 1764, den_step: 1 },
+	{ num: 16934400, den_min: 2117, den_max: 2117, den_step: 1 },
+	{ num: 16934400, den_min: 2558, den_max: 2558, den_step: 1 },
+	{ num: 16934400/16, den_min: 21, den_max: 192, den_step: 1 }
+};
+
+static snd_pcm_hw_constraint_ratnums_t hw_constraints_clocks = {
+	nrats: CLOCKS,
+	rats: clocks,
+};
+#endif
+
+static int snd_cs4236_xrate(snd_pcm_runtime_t *runtime)
+{
+	return snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+					     &hw_constraints_clocks);
+}
+
+static unsigned char divisor_to_rate_register(unsigned int divisor)
+{
+	switch (divisor) {
+	case 353:	return 1;
+	case 529:	return 2;
+	case 617:	return 3;
+	case 1058:	return 4;
+	case 1764:	return 5;
+	case 2117:	return 6;
+	case 2558:	return 7;
+	default:
+		snd_runtime_check(divisor >= 21 && divisor <= 192, return 192);
+		return divisor;
+	}
+}
+
+static void snd_cs4236_playback_format(cs4231_t *chip, snd_pcm_hw_params_t *params, unsigned char pdfr)
+{
+	unsigned long flags;
+	unsigned char rate = divisor_to_rate_register(params->rate_den);
+	
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	/* set fast playback format change and clean playback FIFO */
+	snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] | 0x10);
+	snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT, pdfr & 0xf0);
+	snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] & ~0x10);
+	snd_cs4236_ext_out(chip, CS4236_DAC_RATE, rate);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+static void snd_cs4236_capture_format(cs4231_t *chip, snd_pcm_hw_params_t *params, unsigned char cdfr)
+{
+	unsigned long flags;
+	unsigned char rate = divisor_to_rate_register(params->rate_den);
+	
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	/* set fast capture format change and clean capture FIFO */
+	snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] | 0x20);
+	snd_cs4231_out(chip, CS4231_REC_FORMAT, cdfr & 0xf0);
+	snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] & ~0x20);
+	snd_cs4236_ext_out(chip, CS4236_ADC_RATE, rate);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+#ifdef CONFIG_PM
+
+static void snd_cs4236_suspend(cs4231_t *chip)
+{
+	int reg;
+	unsigned long flags;
+	
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	for (reg = 0; reg < 32; reg++)
+		chip->image[reg] = snd_cs4231_in(chip, reg);
+	for (reg = 0; reg < 18; reg++)
+		chip->eimage[reg] = snd_cs4236_ext_in(chip, CS4236_I23VAL(reg));
+	for (reg = 2; reg < 9; reg++)
+		chip->cimage[reg] = snd_cs4236_ctrl_in(chip, reg);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+static void snd_cs4236_resume(cs4231_t *chip)
+{
+	int reg;
+	unsigned long flags;
+	
+	snd_cs4231_mce_up(chip);
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	for (reg = 0; reg < 32; reg++) {
+		switch (reg) {
+		case CS4236_EXT_REG:
+		case CS4231_VERSION:
+		case 27:	/* why? CS4235 - master left */
+		case 29:	/* why? CS4235 - master right */
+			break;
+		default:
+			snd_cs4231_out(chip, reg, chip->image[reg]);
+			break;
+		}
+	}
+	for (reg = 0; reg < 18; reg++)
+		snd_cs4236_ext_out(chip, CS4236_I23VAL(reg), chip->eimage[reg]);
+	for (reg = 2; reg < 9; reg++) {
+		switch (reg) {
+		case 7:
+			break;
+		default:
+			snd_cs4236_ctrl_out(chip, reg, chip->cimage[reg]);
+		}
+	}
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	snd_cs4231_mce_down(chip);
+}
+
+#endif /* CONFIG_PM */
+
+int snd_cs4236_create(snd_card_t * card,
+		      unsigned long port,
+		      unsigned long cport,
+		      int irq, int dma1, int dma2,
+		      unsigned short hardware,
+		      unsigned short hwshare,
+		      cs4231_t ** rchip)
+{
+	cs4231_t *chip;
+	unsigned char ver1, ver2;
+	int err, reg;
+
+	*rchip = NULL;
+	if (hardware == CS4231_HW_DETECT)
+		hardware = CS4231_HW_DETECT3;
+	if (cport < 0x100) {
+		snd_printk("please, specify control port for CS4236+ chips\n");
+		return -ENODEV;
+	}
+	if ((err = snd_cs4231_create(card, port, cport, irq, dma1, dma2, hardware, hwshare, &chip)) < 0)
+		return err;
+	if (!(chip->hardware & CS4231_HW_CS4236B_MASK)) {
+		snd_device_free(card, chip);
+		return -ENODEV;
+	}
+#if 0
+	{
+		int idx;
+		for (idx = 0; idx < 8; idx++)
+			snd_printk("CD%i = 0x%x\n", idx, inb(chip->cport + idx));
+		for (idx = 0; idx < 9; idx++)
+			snd_printk("C%i = 0x%x\n", idx, snd_cs4236_ctrl_in(chip, idx));
+	}
+#endif
+	ver1 = snd_cs4236_ctrl_in(chip, 1);
+	ver2 = snd_cs4236_ext_in(chip, CS4236_VERSION);
+	snd_printdd("CS4236: [0x%x] C1 (version) = 0x%x, ext = 0x%x\n", cport, ver1, ver2);
+	if (ver1 != ver2) {
+		snd_printk("CS4236+ chip detected, but control port 0x%lx is not valid\n", cport);
+		snd_device_free(card, chip);
+		return -ENODEV;
+	}
+	snd_cs4236_ctrl_out(chip, 0, 0x00);
+	snd_cs4236_ctrl_out(chip, 2, 0xff);
+	snd_cs4236_ctrl_out(chip, 3, 0x00);
+	snd_cs4236_ctrl_out(chip, 4, 0x00);
+	snd_cs4236_ctrl_out(chip, 5, 0x00);
+	snd_cs4236_ctrl_out(chip, 6, 0x00);
+	snd_cs4236_ctrl_out(chip, 7, 0x00);
+	/* 0x07 for C8 is valid for Turtle Beach Malibu - the IEC-958 output */
+	/* is working with this setup, other hardware should have */
+	/* different signal paths and this value should be selectable */
+	/* in the future */
+	snd_cs4236_ctrl_out(chip, 8, 0x07);
+	chip->rate_constraint = snd_cs4236_xrate;
+	chip->set_playback_format = snd_cs4236_playback_format;
+	chip->set_capture_format = snd_cs4236_capture_format;
+#ifdef CONFIG_PM
+	chip->suspend = snd_cs4236_suspend;
+	chip->resume = snd_cs4236_resume;
+#endif
+
+	/* initialize extended registers */
+	for (reg = 0; reg < sizeof(snd_cs4236_ext_map); reg++)
+		snd_cs4236_ext_out(chip, CS4236_I23VAL(reg), snd_cs4236_ext_map[reg]);
+
+        /* initialize compatible but more featured registers */
+	snd_cs4231_out(chip, CS4231_LEFT_INPUT, 0x40);
+	snd_cs4231_out(chip, CS4231_RIGHT_INPUT, 0x40);
+	snd_cs4231_out(chip, CS4231_AUX1_LEFT_INPUT, 0xff);
+	snd_cs4231_out(chip, CS4231_AUX1_RIGHT_INPUT, 0xff);
+	snd_cs4231_out(chip, CS4231_AUX2_LEFT_INPUT, 0xdf);
+	snd_cs4231_out(chip, CS4231_AUX2_RIGHT_INPUT, 0xdf);
+	snd_cs4231_out(chip, CS4231_RIGHT_LINE_IN, 0xff);
+	snd_cs4231_out(chip, CS4231_LEFT_LINE_IN, 0xff);
+	snd_cs4231_out(chip, CS4231_RIGHT_LINE_IN, 0xff);
+	switch (chip->hardware) {
+	case CS4231_HW_CS4235:
+	case CS4231_HW_CS4239:
+		snd_cs4231_out(chip, CS4235_LEFT_MASTER, 0xff);
+		snd_cs4231_out(chip, CS4235_RIGHT_MASTER, 0xff);
+		break;
+	}
+
+	*rchip = chip;
+	return 0;
+}
+
+int snd_cs4236_pcm(cs4231_t *chip, int device, snd_pcm_t **rpcm)
+{
+	snd_pcm_t *pcm;
+	int err;
+	
+	if ((err = snd_cs4231_pcm(chip, device, &pcm)) < 0)
+		return err;
+	pcm->info_flags &= ~SNDRV_PCM_INFO_JOINT_DUPLEX;
+	if (rpcm)
+		*rpcm = pcm;
+	return 0;
+}
+
+/*
+ *  MIXER
+ */
+
+#ifdef TARGET_OS2
+#define CS4236_SINGLE(xname, xindex, reg, shift, mask, invert) \
+{ SNDRV_CTL_ELEM_IFACE_MIXER, 0,0, xname, xindex, \
+  0, 0, snd_cs4236_info_single, \
+  snd_cs4236_get_single, snd_cs4236_put_single, \
+  reg | (shift << 8) | (mask << 16) | (invert << 24) }
+#else
+#define CS4236_SINGLE(xname, xindex, reg, shift, mask, invert) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
+  info: snd_cs4236_info_single, \
+  get: snd_cs4236_get_single, put: snd_cs4236_put_single, \
+  private_value: reg | (shift << 8) | (mask << 16) | (invert << 24) }
+#endif
+
+static int snd_cs4236_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+
+	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	return 0;
+}
+
+static int snd_cs4236_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	cs4231_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 8) & 0xff;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0xff;
+	
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	ucontrol->value.integer.value[0] = (chip->eimage[CS4236_REG(reg)] >> shift) & mask;
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	if (invert)
+		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+	return 0;
+}
+
+static int snd_cs4236_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	cs4231_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 8) & 0xff;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0xff;
+	int change;
+	unsigned short val;
+	
+	val = (ucontrol->value.integer.value[0] & mask);
+	if (invert)
+		val = mask - val;
+	val <<= shift;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	val = (chip->eimage[CS4236_REG(reg)] & ~(mask << shift)) | val;
+	change = val != chip->eimage[CS4236_REG(reg)];
+	snd_cs4236_ext_out(chip, reg, val);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return change;
+}
+
+#ifdef TARGET_OS2
+#define CS4236_SINGLEC(xname, xindex, reg, shift, mask, invert) \
+{ SNDRV_CTL_ELEM_IFACE_MIXER, 0,0,xname, xindex, \
+  0, 0, snd_cs4236_info_single, \
+  snd_cs4236_get_singlec, snd_cs4236_put_singlec, \
+  reg | (shift << 8) | (mask << 16) | (invert << 24) }
+#else
+#define CS4236_SINGLEC(xname, xindex, reg, shift, mask, invert) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
+  info: snd_cs4236_info_single, \
+  get: snd_cs4236_get_singlec, put: snd_cs4236_put_singlec, \
+  private_value: reg | (shift << 8) | (mask << 16) | (invert << 24) }
+#endif
+
+static int snd_cs4236_get_singlec(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	cs4231_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 8) & 0xff;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0xff;
+	
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	ucontrol->value.integer.value[0] = (chip->cimage[reg] >> shift) & mask;
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	if (invert)
+		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+	return 0;
+}
+
+static int snd_cs4236_put_singlec(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	cs4231_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 8) & 0xff;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0xff;
+	int change;
+	unsigned short val;
+	
+	val = (ucontrol->value.integer.value[0] & mask);
+	if (invert)
+		val = mask - val;
+	val <<= shift;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	val = (chip->cimage[reg] & ~(mask << shift)) | val;
+	change = val != chip->cimage[reg];
+	snd_cs4236_ctrl_out(chip, reg, val);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return change;
+}
+
+#ifdef TARGET_OS2
+#define CS4236_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \
+{ SNDRV_CTL_ELEM_IFACE_MIXER, 0,0, xname, xindex, \
+  0, 0, snd_cs4236_info_double, \
+  snd_cs4236_get_double, snd_cs4236_put_double, \
+  left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
+#else
+#define CS4236_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
+  info: snd_cs4236_info_double, \
+  get: snd_cs4236_get_double, put: snd_cs4236_put_double, \
+  private_value: left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
+#endif
+
+static int snd_cs4236_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+
+	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	return 0;
+}
+
+static int snd_cs4236_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	cs4231_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int left_reg = kcontrol->private_value & 0xff;
+	int right_reg = (kcontrol->private_value >> 8) & 0xff;
+	int shift_left = (kcontrol->private_value >> 16) & 0x07;
+	int shift_right = (kcontrol->private_value >> 19) & 0x07;
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+	int invert = (kcontrol->private_value >> 22) & 1;
+	
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	ucontrol->value.integer.value[0] = (chip->eimage[CS4236_REG(left_reg)] >> shift_left) & mask;
+	ucontrol->value.integer.value[1] = (chip->eimage[CS4236_REG(right_reg)] >> shift_right) & mask;
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	if (invert) {
+		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+		ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1];
+	}
+	return 0;
+}
+
+static int snd_cs4236_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	cs4231_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int left_reg = kcontrol->private_value & 0xff;
+	int right_reg = (kcontrol->private_value >> 8) & 0xff;
+	int shift_left = (kcontrol->private_value >> 16) & 0x07;
+	int shift_right = (kcontrol->private_value >> 19) & 0x07;
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+	int invert = (kcontrol->private_value >> 22) & 1;
+	int change;
+	unsigned short val1, val2;
+	
+	val1 = ucontrol->value.integer.value[0] & mask;
+	val2 = ucontrol->value.integer.value[1] & mask;
+	if (invert) {
+		val1 = mask - val1;
+		val2 = mask - val2;
+	}
+	val1 <<= shift_left;
+	val2 <<= shift_right;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	if (left_reg != right_reg) {
+		val1 = (chip->eimage[CS4236_REG(left_reg)] & ~(mask << shift_left)) | val1;
+		val2 = (chip->eimage[CS4236_REG(right_reg)] & ~(mask << shift_right)) | val2;
+		change = val1 != chip->eimage[CS4236_REG(left_reg)] || val2 != chip->eimage[CS4236_REG(right_reg)];
+		snd_cs4236_ext_out(chip, left_reg, val1);
+		snd_cs4236_ext_out(chip, right_reg, val2);
+	} else {
+		val1 = (chip->eimage[CS4236_REG(left_reg)] & ~((mask << shift_left) | (mask << shift_right))) | val1 | val2;
+		change = val1 != chip->eimage[CS4236_REG(left_reg)];
+		snd_cs4236_ext_out(chip, left_reg, val1);
+	}
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return change;
+}
+
+#ifdef TARGET_OS2
+#define CS4236_DOUBLE1(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \
+{ SNDRV_CTL_ELEM_IFACE_MIXER, 0,0, xname, xindex, \
+  0, 0, snd_cs4236_info_double, \
+  snd_cs4236_get_double1, snd_cs4236_put_double1, \
+  left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
+#else
+#define CS4236_DOUBLE1(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
+  info: snd_cs4236_info_double, \
+  get: snd_cs4236_get_double1, put: snd_cs4236_put_double1, \
+  private_value: left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
+#endif
+
+static int snd_cs4236_get_double1(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	cs4231_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int left_reg = kcontrol->private_value & 0xff;
+	int right_reg = (kcontrol->private_value >> 8) & 0xff;
+	int shift_left = (kcontrol->private_value >> 16) & 0x07;
+	int shift_right = (kcontrol->private_value >> 19) & 0x07;
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+	int invert = (kcontrol->private_value >> 22) & 1;
+	
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	ucontrol->value.integer.value[0] = (chip->image[left_reg] >> shift_left) & mask;
+	ucontrol->value.integer.value[1] = (chip->eimage[CS4236_REG(right_reg)] >> shift_right) & mask;
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	if (invert) {
+		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+		ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1];
+	}
+	return 0;
+}
+
+static int snd_cs4236_put_double1(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	cs4231_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int left_reg = kcontrol->private_value & 0xff;
+	int right_reg = (kcontrol->private_value >> 8) & 0xff;
+	int shift_left = (kcontrol->private_value >> 16) & 0x07;
+	int shift_right = (kcontrol->private_value >> 19) & 0x07;
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+	int invert = (kcontrol->private_value >> 22) & 1;
+	int change;
+	unsigned short val1, val2;
+	
+	val1 = ucontrol->value.integer.value[0] & mask;
+	val2 = ucontrol->value.integer.value[1] & mask;
+	if (invert) {
+		val1 = mask - val1;
+		val2 = mask - val2;
+	}
+	val1 <<= shift_left;
+	val2 <<= shift_right;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	val1 = (chip->image[left_reg] & ~(mask << shift_left)) | val1;
+	val2 = (chip->eimage[CS4236_REG(right_reg)] & ~(mask << shift_right)) | val2;
+	change = val1 != chip->image[left_reg] || val2 != chip->eimage[CS4236_REG(right_reg)];
+	snd_cs4231_out(chip, left_reg, val1);
+	snd_cs4236_ext_out(chip, right_reg, val2);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return change;
+}
+
+#ifdef TARGET_OS2
+#define CS4236_MASTER_DIGITAL(xname, xindex) \
+{ SNDRV_CTL_ELEM_IFACE_MIXER, 0,0,xname, xindex, \
+  0, 0, snd_cs4236_info_double, \
+  snd_cs4236_get_master_digital, snd_cs4236_put_master_digital, \
+  71 << 24 }
+#else
+#define CS4236_MASTER_DIGITAL(xname, xindex) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
+  info: snd_cs4236_info_double, \
+  get: snd_cs4236_get_master_digital, put: snd_cs4236_put_master_digital, \
+  private_value: 71 << 24 }
+#endif
+
+static inline int snd_cs4236_mixer_master_digital_invert_volume(int vol)
+{
+	return (vol < 64) ? 63 - vol : 64 + (71 - vol);
+}        
+
+static int snd_cs4236_get_master_digital(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	cs4231_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	ucontrol->value.integer.value[0] = snd_cs4236_mixer_master_digital_invert_volume(chip->eimage[CS4236_REG(CS4236_LEFT_MASTER)] & 0x7f);
+	ucontrol->value.integer.value[1] = snd_cs4236_mixer_master_digital_invert_volume(chip->eimage[CS4236_REG(CS4236_RIGHT_MASTER)] & 0x7f);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return 0;
+}
+
+static int snd_cs4236_put_master_digital(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	cs4231_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int change;
+	unsigned short val1, val2;
+	
+	val1 = snd_cs4236_mixer_master_digital_invert_volume(ucontrol->value.integer.value[0] & 0x7f);
+	val2 = snd_cs4236_mixer_master_digital_invert_volume(ucontrol->value.integer.value[1] & 0x7f);
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	val1 = (chip->eimage[CS4236_REG(CS4236_LEFT_MASTER)] & ~0x7f) | val1;
+	val2 = (chip->eimage[CS4236_REG(CS4236_RIGHT_MASTER)] & ~0x7f) | val2;
+	change = val1 != chip->eimage[CS4236_REG(CS4236_LEFT_MASTER)] || val2 != chip->eimage[CS4236_REG(CS4236_RIGHT_MASTER)];
+	snd_cs4236_ext_out(chip, CS4236_LEFT_MASTER, val1);
+	snd_cs4236_ext_out(chip, CS4236_RIGHT_MASTER, val1);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return change;
+}
+
+#ifdef TARGET_OS2
+#define CS4235_OUTPUT_ACCU(xname, xindex) \
+{ SNDRV_CTL_ELEM_IFACE_MIXER, 0,0,xname, xindex, \
+  0, 0, snd_cs4236_info_double, \
+  snd_cs4235_get_output_accu, snd_cs4235_put_output_accu, \
+  3 << 24 }
+#else
+#define CS4235_OUTPUT_ACCU(xname, xindex) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
+  info: snd_cs4236_info_double, \
+  get: snd_cs4235_get_output_accu, put: snd_cs4235_put_output_accu, \
+  private_value: 3 << 24 }
+#endif
+
+static inline int snd_cs4235_mixer_output_accu_get_volume(int vol)
+{
+	switch ((vol >> 5) & 3) {
+	case 0: return 1;
+	case 1: return 3;
+	case 2: return 2;
+	case 3: return 0;
+ 	}
+	return 3;
+}
+
+static inline int snd_cs4235_mixer_output_accu_set_volume(int vol)
+{
+	switch (vol & 3) {
+	case 0: return 3 << 5;
+	case 1: return 0 << 5;
+	case 2: return 2 << 5;
+	case 3: return 1 << 5;
+	}
+	return 1 << 5;
+}
+
+static int snd_cs4235_get_output_accu(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	cs4231_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	ucontrol->value.integer.value[0] = snd_cs4235_mixer_output_accu_get_volume(chip->image[CS4235_LEFT_MASTER]);
+	ucontrol->value.integer.value[1] = snd_cs4235_mixer_output_accu_get_volume(chip->image[CS4235_RIGHT_MASTER]);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return 0;
+}
+
+static int snd_cs4235_put_output_accu(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	cs4231_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int change;
+	unsigned short val1, val2;
+	
+	val1 = snd_cs4235_mixer_output_accu_set_volume(ucontrol->value.integer.value[0]);
+	val2 = snd_cs4235_mixer_output_accu_set_volume(ucontrol->value.integer.value[1]);
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	val1 = (chip->image[CS4235_LEFT_MASTER] & ~(3 << 5)) | val1;
+	val2 = (chip->image[CS4235_RIGHT_MASTER] & ~(3 << 5)) | val2;
+	change = val1 != chip->image[CS4235_LEFT_MASTER] || val2 != chip->image[CS4235_RIGHT_MASTER];
+	snd_cs4231_out(chip, CS4235_LEFT_MASTER, val1);
+	snd_cs4231_out(chip, CS4235_RIGHT_MASTER, val2);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return change;
+}
+
+#define CS4236_CONTROLS (sizeof(snd_cs4236_controls)/sizeof(snd_kcontrol_new_t))
+
+static snd_kcontrol_new_t snd_cs4236_controls[] = {
+
+CS4236_DOUBLE("Master Digital Playback Switch", 0, CS4236_LEFT_MASTER, CS4236_RIGHT_MASTER, 7, 7, 1, 1),
+CS4236_DOUBLE("Master Digital Capture Switch", 0, CS4236_DAC_MUTE, CS4236_DAC_MUTE, 7, 6, 1, 1),
+CS4236_MASTER_DIGITAL("Master Digital Volume", 0),
+
+CS4236_DOUBLE("Capture Boost Volume", 0, CS4236_LEFT_MIX_CTRL, CS4236_RIGHT_MIX_CTRL, 5, 5, 3, 1),
+
+CS4231_DOUBLE("PCM Playback Switch", 0, CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1),
+CS4231_DOUBLE("PCM Playback Volume", 0, CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1),
+
+CS4236_DOUBLE("DSP Playback Switch", 0, CS4236_LEFT_DSP, CS4236_RIGHT_DSP, 7, 7, 1, 1),
+CS4236_DOUBLE("DSP Playback Volume", 0, CS4236_LEFT_DSP, CS4236_RIGHT_DSP, 0, 0, 63, 1),
+
+CS4236_DOUBLE("FM Playback Switch", 0, CS4236_LEFT_FM, CS4236_RIGHT_FM, 7, 7, 1, 1),
+CS4236_DOUBLE("FM Playback Volume", 0, CS4236_LEFT_FM, CS4236_RIGHT_FM, 0, 0, 63, 1),
+
+CS4236_DOUBLE("Wavetable Playback Switch", 0, CS4236_LEFT_WAVE, CS4236_RIGHT_WAVE, 7, 7, 1, 1),
+CS4236_DOUBLE("Wavetable Playback Volume", 0, CS4236_LEFT_WAVE, CS4236_RIGHT_WAVE, 0, 0, 63, 1),
+
+CS4231_DOUBLE("Synth Playback Switch", 0, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1),
+CS4231_DOUBLE("Synth Volume", 0, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 31, 1),
+CS4231_DOUBLE("Synth Capture Switch", 0, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 6, 6, 1, 1),
+CS4231_DOUBLE("Synth Capture Bypass", 0, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 5, 5, 1, 1),
+
+CS4236_DOUBLE("Mic Playback Switch", 0, CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 6, 6, 1, 1),
+CS4236_DOUBLE("Mic Capture Switch", 0, CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 7, 7, 1, 1),
+CS4236_DOUBLE("Mic Volume", 0, CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 0, 0, 31, 1),
+CS4236_DOUBLE("Mic Playback Boost", 0, CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 5, 5, 1, 0),
+
+CS4231_DOUBLE("Line Playback Switch", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1),
+CS4231_DOUBLE("Line Volume", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1),
+CS4231_DOUBLE("Line Capture Switch", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 6, 6, 1, 1),
+CS4231_DOUBLE("Line Capture Bypass", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 5, 5, 1, 1),
+
+CS4231_DOUBLE("CD Playback Switch", 0, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1),
+CS4231_DOUBLE("CD Volume", 0, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1),
+CS4231_DOUBLE("CD Capture Switch", 0, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 6, 6, 1, 1),
+
+CS4236_DOUBLE1("Mono Output Playback Switch", 0, CS4231_MONO_CTRL, CS4236_RIGHT_MIX_CTRL, 6, 7, 1, 1),
+CS4236_DOUBLE1("Mono Playback Switch", 0, CS4231_MONO_CTRL, CS4236_LEFT_MIX_CTRL, 7, 7, 1, 1),
+CS4231_SINGLE("Mono Playback Volume", 0, CS4231_MONO_CTRL, 0, 15, 1),
+CS4231_SINGLE("Mono Playback Bypass", 0, CS4231_MONO_CTRL, 5, 1, 0),
+
+CS4231_DOUBLE("Capture Volume", 0, CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 0, 0, 15, 0),
+CS4231_DOUBLE("Analog Loopback Capture Switch", 0, CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 7, 7, 1, 0),
+
+CS4231_SINGLE("Digital Loopback Playback Switch", 0, CS4231_LOOPBACK, 0, 1, 0),
+CS4236_DOUBLE1("Digital Loopback Playback Volume", 0, CS4231_LOOPBACK, CS4236_RIGHT_LOOPBACK, 2, 0, 63, 1)
+};
+
+#define CS4235_CONTROLS (sizeof(snd_cs4235_controls)/sizeof(snd_kcontrol_new_t))
+
+static snd_kcontrol_new_t snd_cs4235_controls[] = {
+
+CS4231_DOUBLE("Master Switch", 0, CS4235_LEFT_MASTER, CS4235_RIGHT_MASTER, 7, 7, 1, 1),
+CS4231_DOUBLE("Master Volume", 0, CS4235_LEFT_MASTER, CS4235_RIGHT_MASTER, 0, 0, 31, 1),
+
+CS4235_OUTPUT_ACCU("Playback Volume", 0),
+
+CS4236_DOUBLE("Master Digital Playback Switch", 0, CS4236_LEFT_MASTER, CS4236_RIGHT_MASTER, 7, 7, 1, 1),
+CS4236_DOUBLE("Master Digital Capture Switch", 0, CS4236_DAC_MUTE, CS4236_DAC_MUTE, 7, 6, 1, 1),
+CS4236_MASTER_DIGITAL("Master Digital Volume", 0),
+
+CS4231_DOUBLE("Master Digital Playback Switch", 1, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1),
+CS4231_DOUBLE("Master Digital Capture Switch", 1, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 6, 6, 1, 1),
+CS4231_DOUBLE("Master Digital Volume", 1, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 31, 1),
+
+CS4236_DOUBLE("Capture Volume", 0, CS4236_LEFT_MIX_CTRL, CS4236_RIGHT_MIX_CTRL, 5, 5, 3, 1),
+
+CS4231_DOUBLE("PCM Switch", 0, CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1),
+CS4231_DOUBLE("PCM Volume", 0, CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1),
+
+CS4236_DOUBLE("DSP Switch", 0, CS4236_LEFT_DSP, CS4236_RIGHT_DSP, 7, 7, 1, 1),
+
+CS4236_DOUBLE("FM Switch", 0, CS4236_LEFT_FM, CS4236_RIGHT_FM, 7, 7, 1, 1),
+
+CS4236_DOUBLE("Wavetable Switch", 0, CS4236_LEFT_WAVE, CS4236_RIGHT_WAVE, 7, 7, 1, 1),
+
+CS4236_DOUBLE("Mic Capture Switch", 0, CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 7, 7, 1, 1),
+CS4236_DOUBLE("Mic Playback Switch", 0, CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 6, 6, 1, 1),
+CS4236_SINGLE("Mic Volume", 0, CS4236_LEFT_MIC, 0, 31, 1),
+CS4236_SINGLE("Mic Playback Boost", 0, CS4236_LEFT_MIC, 5, 1, 0),
+
+CS4231_DOUBLE("Aux Playback Switch", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1),
+CS4231_DOUBLE("Aux Capture Switch", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 6, 6, 1, 1),
+CS4231_DOUBLE("Aux Volume", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1),
+
+CS4231_DOUBLE("Aux Playback Switch", 1, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1),
+CS4231_DOUBLE("Aux Capture Switch", 1, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 6, 6, 1, 1),
+CS4231_DOUBLE("Aux Volume", 1, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1),
+
+CS4236_DOUBLE1("Master Mono Switch", 0, CS4231_MONO_CTRL, CS4236_RIGHT_MIX_CTRL, 6, 7, 1, 1),
+
+CS4236_DOUBLE1("Mono Switch", 0, CS4231_MONO_CTRL, CS4236_LEFT_MIX_CTRL, 7, 7, 1, 1),
+CS4231_SINGLE("Mono Volume", 0, CS4231_MONO_CTRL, 0, 15, 1),
+
+CS4231_DOUBLE("Analog Loopback Switch", 0, CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 7, 7, 1, 0),
+};
+
+#ifdef TARGET_OS2
+#define CS4236_IEC958_ENABLE(xname, xindex) \
+{ SNDRV_CTL_ELEM_IFACE_MIXER, 0,0,xname, xindex, \
+  0, 0, snd_cs4236_info_single, \
+  snd_cs4236_get_iec958_switch, snd_cs4236_put_iec958_switch, \
+  1 << 16 }
+#else
+#define CS4236_IEC958_ENABLE(xname, xindex) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
+  info: snd_cs4236_info_single, \
+  get: snd_cs4236_get_iec958_switch, put: snd_cs4236_put_iec958_switch, \
+  private_value: 1 << 16 }
+#endif
+
+static int snd_cs4236_get_iec958_switch(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	cs4231_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	ucontrol->value.integer.value[0] = chip->image[CS4231_ALT_FEATURE_1] & 0x02 ? 1 : 0;
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return 0;
+}
+
+static int snd_cs4236_put_iec958_switch(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	cs4231_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int change;
+	unsigned short enable, val;
+	
+	enable = ucontrol->value.integer.value[0] & 1;
+
+	down(&chip->mce_mutex);
+	snd_cs4231_mce_up(chip);
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	val = (chip->image[CS4231_ALT_FEATURE_1] & ~0x02) | (enable << 1);
+	change = val != chip->image[CS4231_ALT_FEATURE_1];
+	snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, val);
+	val = (snd_cs4236_ctrl_in(chip, 4) & ~0x80) | (enable << 7);
+	change |= snd_cs4236_ctrl_in(chip, 4) != val;
+	snd_cs4236_ctrl_out(chip, 4, val);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	snd_cs4231_mce_down(chip);
+	up(&chip->mce_mutex);
+
+#if 0
+	printk("set valid: ALT = 0x%x, C3 = 0x%x, C4 = 0x%x, C5 = 0x%x, C6 = 0x%x, C8 = 0x%x\n",
+			snd_cs4231_in(chip, CS4231_ALT_FEATURE_1),
+			snd_cs4236_ctrl_in(chip, 3),
+			snd_cs4236_ctrl_in(chip, 4),
+			snd_cs4236_ctrl_in(chip, 5),
+			snd_cs4236_ctrl_in(chip, 6),
+			snd_cs4236_ctrl_in(chip, 8));
+#endif
+	return change;
+}
+
+#define CS4236_IEC958_CONTROLS (sizeof(snd_cs4236_iec958_controls)/sizeof(snd_kcontrol_new_t))
+
+static snd_kcontrol_new_t snd_cs4236_iec958_controls[] = {
+CS4236_IEC958_ENABLE("S/PDIF Output Enable", 0),
+CS4236_SINGLEC("S/PDIF Output Validity", 0, 4, 4, 1, 0),
+CS4236_SINGLEC("S/PDIF Output User", 0, 4, 5, 1, 0),
+CS4236_SINGLEC("S/PDIF Output CSBR", 0, 4, 6, 1, 0),
+CS4236_SINGLEC("S/PDIF Output 3D", 0, 3, 5, 1, 0),
+CS4236_SINGLEC("S/PDIF Output Channel Status Low", 0, 5, 1, 127, 0),
+CS4236_SINGLEC("S/PDIF Output Channel Status High", 0, 6, 0, 255, 0)
+};
+
+#define CS4236_3D_CONTROLS_CS4235 (sizeof(snd_cs4236_3d_controls_cs4235)/sizeof(snd_kcontrol_new_t))
+
+static snd_kcontrol_new_t snd_cs4236_3d_controls_cs4235[] = {
+CS4236_SINGLEC("3D Control - Switch", 0, 3, 4, 1, 0),
+CS4236_SINGLEC("3D Control - Space", 0, 2, 4, 15, 1)
+};
+
+#define CS4236_3D_CONTROLS_CS4237 (sizeof(snd_cs4236_3d_controls_cs4237)/sizeof(snd_kcontrol_new_t))
+
+static snd_kcontrol_new_t snd_cs4236_3d_controls_cs4237[] = {
+CS4236_SINGLEC("3D Control - Switch", 0, 3, 7, 1, 0),
+CS4236_SINGLEC("3D Control - Space", 0, 2, 4, 15, 1),
+CS4236_SINGLEC("3D Control - Center", 0, 2, 0, 15, 1),
+CS4236_SINGLEC("3D Control - Mono", 0, 3, 6, 1, 0),
+CS4236_SINGLEC("3D Control - S/PDIF", 0, 3, 5, 1, 0)
+};
+
+#define CS4236_3D_CONTROLS_CS4238 (sizeof(snd_cs4236_3d_controls_cs4238)/sizeof(snd_kcontrol_new_t))
+
+static snd_kcontrol_new_t snd_cs4236_3d_controls_cs4238[] = {
+CS4236_SINGLEC("3D Control - Switch", 0, 3, 4, 1, 0),
+CS4236_SINGLEC("3D Control - Space", 0, 2, 4, 15, 1),
+CS4236_SINGLEC("3D Control - Volume", 0, 2, 0, 15, 1),
+CS4236_SINGLEC("3D Control - S/PDIF", 0, 3, 5, 1, 0)
+};
+
+int snd_cs4236_mixer(cs4231_t *chip)
+{
+	snd_card_t *card;
+	int err, idx, count;
+	snd_kcontrol_new_t *kcontrol;
+
+	snd_assert(chip != NULL && chip->card != NULL, return -EINVAL);
+	card = chip->card;
+	strcpy(card->mixername, snd_cs4231_chip_id(chip));
+
+	if (chip->hardware == CS4231_HW_CS4235 ||
+	    chip->hardware == CS4231_HW_CS4239) {
+		for (idx = 0; idx < CS4235_CONTROLS; idx++) {
+			if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4235_controls[idx], chip))) < 0)
+				return err;
+		}
+	} else {
+		for (idx = 0; idx < CS4236_CONTROLS; idx++) {
+			if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4236_controls[idx], chip))) < 0)
+				return err;
+		}
+	}
+	switch (chip->hardware) {
+	case CS4231_HW_CS4235:
+	case CS4231_HW_CS4239:
+		count = CS4236_3D_CONTROLS_CS4235;
+		kcontrol = snd_cs4236_3d_controls_cs4235;
+		break;
+	case CS4231_HW_CS4237B:
+		count = CS4236_3D_CONTROLS_CS4237;
+		kcontrol = snd_cs4236_3d_controls_cs4237;
+		break;
+	case CS4231_HW_CS4238B:
+		count = CS4236_3D_CONTROLS_CS4238;
+		kcontrol = snd_cs4236_3d_controls_cs4238;
+		break;
+	default:
+		count = -1;
+		kcontrol = NULL;
+	}
+	for (idx = 0; idx < count; idx++, kcontrol++) {
+		if ((err = snd_ctl_add(card, snd_ctl_new1(kcontrol, chip))) < 0)
+			return err;
+	}
+	if (chip->hardware == CS4231_HW_CS4237B ||
+	    chip->hardware == CS4231_HW_CS4238B) {
+		for (idx = 0; idx < CS4236_IEC958_CONTROLS; idx++) {
+			if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4236_iec958_controls[idx], chip))) < 0)
+				return err;
+		}
+	}
+	return 0;
+}
+
+EXPORT_SYMBOL(snd_cs4236_create);
+EXPORT_SYMBOL(snd_cs4236_pcm);
+EXPORT_SYMBOL(snd_cs4236_mixer);
+
+/*
+ *  INIT part
+ */
+
+static int __init alsa_cs4236_init(void)
+{
+	return 0;
+}
+
+static void __exit alsa_cs4236_exit(void)
+{
+}
+
+module_init(alsa_cs4236_init)
+module_exit(alsa_cs4236_exit)
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/cs423x/makefile.os2
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/cs423x/makefile.os2	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/cs423x/makefile.os2	(revision 91)
@@ -0,0 +1,50 @@
+CINCLUDES = ..\..\..\include;..\..\include;$(%WATCOM)\H
+AINCLUDES = $(DDK_BASE)\H
+
+CDEFINES = -D__KERNEL__ -DMODULE -dTARGET_OS2 -dALSA_BUILD -D__ISAPNP__ -DCONFIG_ISAPNP -D__i386_
+
+32BIT=1
+!include ..\..\..\..\makefile.inc
+
+
+#===================================================================
+#
+#   List of source files
+#
+#===================================================================
+FILE1    = cs4231_lib.obj cs4236_lib.obj cs4231.obj cs4232.obj cs4236.obj
+FILE2    = 
+FILE3    = 
+FILE4    = 
+FILE5    = 
+FILE6    = 
+FILE7    = 
+FILE8    = 
+FILELAST =
+FILES    = $(FILE0) $(FILE1) $(FILE2) $(FILE3) $(FILE4) $(FILE5) $(FILE6) $(FILE7) $(FILE8) $(FILE9) $(FILE10) $(FILE11) $(FILE12)
+
+TARGET   = cs423x
+
+LNKFILE  = $(OBJDIR)\$(TARGET).lnk
+
+
+#===================================================================
+#
+#   Specific dependencies
+#
+#===================================================================
+all: $(OBJDIR)\$(TARGET).lib 
+
+
+$(LNKFILE): makefile.os2
+    @%create $(LNKFILE)
+    @for %f in ($(FILES)) do @%append $(LNKFILE) -+$(OBJDIR)\%f
+
+$(OBJDIR)\$(TARGET).lib: $(LNKFILE) $(FILES)
+    $(LIB) $(OBJDIR)\$(TARGET).lib @$(LNKFILE)
+    copy $(OBJDIR)\$(TARGET).lib $(ALSA_LIB) 
+
+clean: .SYMBOLIC
+     cd $(OBJDIR)
+     -del *.obj *.err *.lnk *.sys *.sym *.lst *.map *.wmap *.lib
+     cd ..
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/dt0197h.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/dt0197h.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/dt0197h.c	(revision 91)
@@ -0,0 +1,387 @@
+
+/*
+    card-dt0197h.c - driver for Diamond Technologies DT-0197H based soundcards.
+    Copyright (C) 1999 by Massimo Piccioni <dafastidio@libero.it>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#define SNDRV_MAIN_OBJECT_FILE
+#include <sound/driver.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+#include <sound/mpu401.h>
+#include <sound/opl3.h>
+#include <sound/sb.h>
+
+#define chip_t sb_t
+
+EXPORT_NO_SYMBOLS;
+MODULE_DESCRIPTION("Diamond Technologies DT-0197H");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{Diamond Technologies,DT-0197H}}");
+
+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;	/* Enable this card */
+static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* PnP setup */
+static long snd_mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* PnP setup */
+static long snd_fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* PnP setup */
+static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* PnP setup */
+static int snd_mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* PnP setup */
+static int snd_dma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* PnP setup */
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for dt0197h based soundcard.");
+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 dt0197h based soundcard.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable dt0197h based soundcard.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_port, "Port # for dt0197h driver.");
+MODULE_PARM_SYNTAX(snd_port, SNDRV_PORT12_DESC);
+MODULE_PARM(snd_mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_mpu_port, "MPU-401 port # for dt0197h driver.");
+MODULE_PARM_SYNTAX(snd_mpu_port, SNDRV_PORT12_DESC);
+MODULE_PARM(snd_fm_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_fm_port, "FM port # for dt0197h driver.");
+MODULE_PARM_SYNTAX(snd_fm_port, SNDRV_PORT12_DESC);
+MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_irq, "IRQ # for dt0197h driver.");
+MODULE_PARM_SYNTAX(snd_irq, SNDRV_IRQ_DESC);
+MODULE_PARM(snd_mpu_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_mpu_irq, "MPU-401 IRQ # for dt0197h driver.");
+MODULE_PARM_SYNTAX(snd_mpu_irq, SNDRV_IRQ_DESC);
+MODULE_PARM(snd_dma8, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_dma8, "8-bit DMA # for dt0197h driver.");
+MODULE_PARM_SYNTAX(snd_dma8, SNDRV_DMA8_DESC);
+
+struct snd_card_dt0197h {
+#ifdef __ISAPNP__
+	struct isapnp_dev *dev;
+	struct isapnp_dev *devmpu;
+	struct isapnp_dev *devopl;
+#endif	/* __ISAPNP__ */
+};
+
+static snd_card_t *snd_dt0197h_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+
+#ifdef __ISAPNP__
+static struct isapnp_card *snd_dt0197h_isapnp_cards[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR;
+static const struct isapnp_card_id *snd_dt0197h_isapnp_id[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR;
+
+static struct isapnp_card_id snd_dt0197h_pnpids[] __devinitdata = {
+	/* DT197A30 */
+	{
+#ifdef TARGET_OS2
+		0, ISAPNP_CARD_ID('R','W','B',0x1688),
+		{ ISAPNP_DEVICE_ID('@','@','@',0x1688),
+		  ISAPNP_DEVICE_ID('@','X','@',0x0001),
+		  ISAPNP_DEVICE_ID('@','H','@',0x0001) }
+#else
+		ISAPNP_CARD_ID('R','W','B',0x1688),
+		devs: { ISAPNP_DEVICE_ID('@','@','@',0x1688),
+			ISAPNP_DEVICE_ID('@','X','@',0x0001),
+			ISAPNP_DEVICE_ID('@','H','@',0x0001) }
+#endif
+	},
+	{ ISAPNP_CARD_END, }
+};
+
+ISAPNP_CARD_TABLE(snd_dt0197h_pnpids);
+
+#endif	/* __ISAPNP__ */
+
+#define DRIVER_NAME	"snd-card-dt0197h"
+
+
+#ifdef __ISAPNP__
+static int __init snd_card_dt0197h_isapnp(int dev, struct snd_card_dt0197h *acard)
+{
+	const struct isapnp_card_id *id = snd_dt0197h_isapnp_id[dev];
+	struct isapnp_card *card = snd_dt0197h_isapnp_cards[dev];
+	struct isapnp_dev *pdev;
+
+	acard->dev = isapnp_find_dev(card, id->devs[0].vendor, id->devs[0].function, NULL);
+	if (acard->dev->active) {
+		acard->dev = NULL;
+		return -EBUSY;
+	}
+	acard->devmpu = isapnp_find_dev(card, id->devs[1].vendor, id->devs[1].function, NULL);
+	if (acard->devmpu->active) {
+		acard->dev = acard->devmpu = NULL;
+		return -EBUSY;
+	}
+	acard->devopl = isapnp_find_dev(card, id->devs[2].vendor, id->devs[2].function, NULL);
+	if (acard->devopl->active) {
+		acard->dev = acard->devmpu = acard->devopl = NULL;
+		return -EBUSY;
+	}
+
+	pdev = acard->dev;
+	if (pdev->prepare(pdev)<0)
+		return -EAGAIN;
+
+	if (snd_port[dev] != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[0], snd_port[dev], 16);
+	if (snd_dma8[dev] != SNDRV_AUTO_DMA)
+		isapnp_resource_change(&pdev->dma_resource[0], snd_dma8[dev],
+			1);
+	if (snd_irq[dev] != SNDRV_AUTO_IRQ)
+		isapnp_resource_change(&pdev->irq_resource[0], snd_irq[dev], 1);
+
+	if (pdev->activate(pdev)<0) {
+		snd_printk("AUDIO isapnp configure failure\n");
+		return -EBUSY;
+	}
+
+	snd_port[dev] = pdev->resource[0].start;
+	snd_dma8[dev] = pdev->dma_resource[0].start;
+	snd_irq[dev] = pdev->irq_resource[0].start;
+
+	pdev = acard->devmpu;
+	if (pdev || pdev->prepare(pdev)<0)
+		return 0;
+
+	if (snd_mpu_port[dev] != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[0], snd_mpu_port[dev],
+			2);
+	if (snd_mpu_irq[dev] != SNDRV_AUTO_IRQ)
+		isapnp_resource_change(&pdev->irq_resource[0], snd_mpu_irq[dev],
+			1);
+
+	if (pdev->activate(pdev)<0) {
+		snd_printk("MPU-401 isapnp configure failure\n");
+		snd_mpu_port[dev] = -1;
+		acard->devmpu = NULL;
+	} else {
+		snd_mpu_port[dev] = pdev->resource[0].start;
+		snd_mpu_irq[dev] = pdev->irq_resource[0].start;
+	}
+
+	pdev = acard->devopl;
+	if (pdev == NULL || pdev->prepare(pdev)<0)
+		return 0;
+
+	if (snd_fm_port[dev] != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[0], snd_fm_port[dev], 4);
+
+	if (pdev->activate(pdev)<0) {
+		snd_printk("OPL isapnp configure failure\n");
+		snd_fm_port[dev] = -1;
+		acard->devopl = NULL;
+	} else {
+		snd_fm_port[dev] = pdev->resource[0].start;
+	}
+
+	return 0;
+}
+
+static void snd_card_dt0197h_deactivate(struct snd_card_dt0197h *acard)
+{
+	if (acard->dev) {
+		acard->dev->deactivate(acard->dev);
+		acard->dev = NULL;
+	}
+	if (acard->devmpu) {
+		acard->devmpu->deactivate(acard->devmpu);
+		acard->devmpu = NULL;
+	}
+	if (acard->devopl) {
+		acard->devopl->deactivate(acard->devopl);
+		acard->devopl = NULL;
+	}
+}
+#endif	/* __ISAPNP__ */
+
+static void snd_card_dt0197h_free(snd_card_t *card)
+{
+	struct snd_card_dt0197h *acard = (struct snd_card_dt0197h *)card->private_data;
+
+	if (acard != NULL) {
+#ifdef __ISAPNP__
+		snd_card_dt0197h_deactivate(acard);
+#endif	/* __ISAPNP__ */
+	}
+}
+
+static int __init snd_card_dt0197h_probe(int dev)
+{
+	int error;
+	sb_t *chip;
+	snd_card_t *card;
+	struct snd_card_dt0197h *acard;
+	opl3_t *opl3;
+
+	if ((card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE,
+				 sizeof(struct snd_card_dt0197h))) == NULL)
+		return -ENOMEM;
+	acard = (struct snd_card_dt0197h *)card->private_data;
+	card->private_free = snd_card_dt0197h_free;
+
+#ifdef __ISAPNP__
+	if ((error = snd_card_dt0197h_isapnp(dev, acard))) {
+		snd_card_free(card);
+		return error;
+	}
+#else
+	snd_printk("you have to enable PnP support ...\n");
+	snd_card_free(card);
+	return -ENOSYS;
+#endif	/* __ISAPNP__ */
+
+	if ((error = snd_sbdsp_create(card, snd_port[dev],
+				      snd_irq[dev],
+				      snd_sb16dsp_interrupt,
+				      snd_dma8[dev],
+				      -1,
+				      SB_HW_AUTO,
+				      &chip)) < 0) {
+		snd_card_free(card);
+		return error;
+	}
+
+	if ((error = snd_sb16dsp_pcm(chip, 0, NULL)) < 0) {
+		snd_card_free(card);
+		return error;
+	}
+	if ((error = snd_sbmixer_new(chip)) < 0) {
+		snd_card_free(card);
+		return error;
+	}
+
+	if (snd_mpu_port[dev] > 0) {
+		if (snd_mpu401_uart_new(card, 0,
+					MPU401_HW_MPU401,
+					snd_mpu_port[dev], 0,
+					snd_mpu_irq[dev],
+					SA_INTERRUPT,
+					NULL) < 0)
+			snd_printk("no MPU-401 device at 0x%lx ?\n",
+				snd_mpu_port[dev]);
+	}
+
+	if (snd_fm_port[dev] > 0) {
+		if (snd_opl3_create(card,
+				    snd_fm_port[dev],
+				    snd_fm_port[dev] + 2,
+				    OPL3_HW_AUTO, 0, &opl3) < 0) {
+			snd_printk("no OPL device at 0x%lx-0x%lx ?\n",
+				snd_fm_port[dev], snd_fm_port[dev] + 2);
+		} else {
+			if ((error = snd_opl3_timer_new(opl3, 0, 1)) < 0) {
+				snd_card_free(card);
+				return error;
+			}
+			if ((error = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
+				snd_card_free(card);
+				return error;
+			}
+		}
+	}
+
+	strcpy(card->driver, "DT-0197H");
+	strcpy(card->shortname, "Diamond Tech. DT-0197H");
+	sprintf(card->longname, "%s soundcard, %s at 0x%lx, irq %d, dma %d",
+		card->shortname, chip->name, chip->port,
+		snd_irq[dev], snd_dma8[dev]);
+	if ((error = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return error;
+	}
+	snd_dt0197h_cards[dev] = card;
+	return 0;
+}
+
+#ifdef __ISAPNP__
+static int __init snd_dt0197h_isapnp_detect(struct isapnp_card *card,
+					    const struct isapnp_card_id *id)
+{
+	static int dev = 0;
+	int res;
+
+	for ( ; dev < SNDRV_CARDS; dev++) {
+		if (!snd_enable[dev])
+			continue;
+		snd_dt0197h_isapnp_cards[dev] = card;
+		snd_dt0197h_isapnp_id[dev] = id;
+		res = snd_card_dt0197h_probe(dev);
+		if (res < 0)
+			return res;
+		dev++;
+		return 0;
+        }
+        return -ENODEV;
+}
+#endif /* __ISAPNP__ */
+
+static int __init alsa_card_dt0197h_init(void)
+{
+	int cards = 0;
+
+#ifdef __ISAPNP__
+	cards += isapnp_probe_cards(snd_dt0197h_pnpids, snd_dt0197h_isapnp_detect);
+#else
+	snd_printk("you have to enable ISA PnP support.\n");
+#endif
+#ifdef MODULE
+	if (!cards)
+		snd_printk("no DT-0197H based soundcards found\n");
+#endif
+	return cards ? 0 : -ENODEV;
+}
+
+static void __exit alsa_card_dt0197h_exit(void)
+{
+	int dev;
+
+	for (dev = 0; dev < SNDRV_CARDS; dev++)
+		snd_card_free(snd_dt0197h_cards[dev]);
+}
+
+module_init(alsa_card_dt0197h_init)
+module_exit(alsa_card_dt0197h_exit)
+
+#ifndef MODULE
+
+/* format is: snd-card-dt0197h=snd_enable,snd_index,snd_id,snd_isapnp,
+			       snd_port,snd_mpu_port,snd_fm_port,
+			       snd_irq,snd_mpu_irq,snd_dma8,snd_dma8_size */
+
+static int __init alsa_card_dt0197h_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_port[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_mpu_port[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_fm_port[nr_dev]) == 2 &&
+	       get_option(&str,&snd_irq[nr_dev]) == 2 &&
+	       get_option(&str,&snd_mpu_irq[nr_dev]) == 2 &&
+	       get_option(&str,&snd_dma8[nr_dev]) == 2);
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-card-dt0197h=", alsa_card_dt0197h_setup);
+
+#endif /* ifndef MODULE */
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/es1688/es1688.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/es1688/es1688.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/es1688/es1688.c	(revision 91)
@@ -0,0 +1,239 @@
+/*
+ *  Driver for generic ESS AudioDrive ESx688 soundcards
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#define SNDRV_MAIN_OBJECT_FILE
+#include <sound/driver.h>
+#include <sound/es1688.h>
+#include <sound/mpu401.h>
+#include <sound/opl3.h>
+#define SNDRV_LEGACY_AUTO_PROBE
+#define SNDRV_LEGACY_FIND_FREE_IRQ
+#define SNDRV_LEGACY_FIND_FREE_DMA
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+#define chip_t es1688_t
+
+EXPORT_NO_SYMBOLS;
+MODULE_DESCRIPTION("ESS ESx688 AudioDrive");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{ESS,ES688 PnP AudioDrive,pnp:ESS0100},"
+	        "{ESS,ES1688 PnP AudioDrive,pnp:ESS0102},"
+	        "{ESS,ES688 AudioDrive,pnp:ESS6881},"
+	        "{ESS,ES1688 AudioDrive,pnp:ESS1681}}");
+
+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;	/* Enable this card */
+static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* 0x220,0x240,0x260 */
+#ifdef TARGET_OS2
+static long snd_mpu_port[SNDRV_CARDS] = {-1,-1,-1,-1,-1,-1,-1,-1};
+#else
+static long snd_mpu_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -1};
+#endif
+static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* 5,7,9,10 */
+static int snd_mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* 5,7,9,10 */
+static int snd_dma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* 0,1,3 */
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for ESx688 soundcard.");
+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 ESx688 soundcard.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable ESx688 soundcard.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_port, "Port # for ESx688 driver.");
+MODULE_PARM_SYNTAX(snd_port, SNDRV_PORT12_DESC);
+MODULE_PARM(snd_mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_mpu_port, "MPU-401 port # for ESx688 driver.");
+MODULE_PARM_SYNTAX(snd_mpu_port, SNDRV_PORT12_DESC);
+MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_irq, "IRQ # for ESx688 driver.");
+MODULE_PARM_SYNTAX(snd_irq, SNDRV_IRQ_DESC);
+MODULE_PARM(snd_mpu_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_mpu_irq, "MPU-401 IRQ # for ESx688 driver.");
+MODULE_PARM_SYNTAX(snd_mpu_irq, SNDRV_IRQ_DESC);
+MODULE_PARM(snd_dma8, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_dma8, "8-bit DMA # for ESx688 driver.");
+MODULE_PARM_SYNTAX(snd_dma8, SNDRV_DMA8_DESC);
+
+static snd_card_t *snd_audiodrive_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+
+
+static int __init snd_audiodrive_probe(int dev)
+{
+	static int possible_irqs[] = {5, 9, 10, 7, -1};
+	static int possible_dmas[] = {1, 3, 0, -1};
+	int irq, dma, mpu_irq;
+	snd_card_t *card;
+	es1688_t *chip;
+	opl3_t *opl3;
+	snd_pcm_t *pcm;
+	int err;
+
+	card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0);
+	if (card == NULL)
+		return -ENOMEM;
+
+	irq = snd_irq[dev];
+	if (irq == SNDRV_AUTO_IRQ) {
+		if ((irq = snd_legacy_find_free_irq(possible_irqs)) < 0) {
+			snd_card_free(card);
+			snd_printk("unable to find a free IRQ\n");
+			return -EBUSY;
+		}
+	}
+	mpu_irq = snd_mpu_irq[dev];
+	dma = snd_dma8[dev];
+	if (dma == SNDRV_AUTO_DMA) {
+		if ((dma = snd_legacy_find_free_dma(possible_dmas)) < 0) {
+			snd_card_free(card);
+			snd_printk("unable to find a free DMA\n");
+			return -EBUSY;
+		}
+	}
+
+	if ((err = snd_es1688_create(card, snd_port[dev], snd_mpu_port[dev],
+				     irq, mpu_irq, dma,
+				     ES1688_HW_AUTO, &chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_es1688_pcm(chip, 0, &pcm)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_es1688_mixer(chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	if ((snd_opl3_create(card, chip->port, chip->port + 2, OPL3_HW_OPL3, 0, &opl3)) < 0) {
+		snd_printk("opl3 not detected at 0x%lx\n", chip->port);
+	} else {
+		if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
+			snd_card_free(card);
+			return err;
+		}
+	}
+
+	if (mpu_irq >= 0) {
+		if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_ES1688,
+					       chip->mpu_port, 0,
+					       mpu_irq,
+					       SA_INTERRUPT,
+					       NULL)) < 0) {
+			snd_card_free(card);
+			return err;
+		}
+	}
+	strcpy(card->driver, "ES1688");
+	strcpy(card->shortname, pcm->name);
+	sprintf(card->longname, "%s at 0x%lx, irq %i, dma %i", pcm->name, chip->port, irq, dma);
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	snd_audiodrive_cards[dev] = card;
+	return 0;
+
+}
+
+static int __init snd_audiodrive_legacy_auto_probe(unsigned long port)
+{
+	static int dev = 0;
+	int res;
+	
+	for ( ; dev < SNDRV_CARDS; dev++) {
+		if (!snd_enable[dev] || snd_port[dev] != SNDRV_AUTO_PORT)
+			continue;
+		snd_port[dev] = port;
+		res = snd_audiodrive_probe(dev);
+		if (res < 0)
+			snd_port[dev] = SNDRV_AUTO_PORT;
+		return res;
+	}
+	return -ENODEV;
+}
+
+static int __init alsa_card_es1688_init(void)
+{
+	static unsigned long possible_ports[] = {0x220, 0x240, 0x260, -1};
+	int dev, cards = 0;
+
+	for (dev = cards = 0; dev < SNDRV_CARDS && snd_enable[dev]; dev++) {
+		if (snd_port[dev] == SNDRV_AUTO_PORT)
+			continue;
+		if (snd_audiodrive_probe(dev) >= 0)
+			cards++;
+	}
+	cards += snd_legacy_auto_probe(possible_ports, snd_audiodrive_legacy_auto_probe);
+	if (!cards) {
+#ifdef MODULE
+		snd_printk("ESS AudioDrive ES1688 soundcard not found or device busy\n");
+#endif
+		return -ENODEV;
+	}
+	return 0;
+}
+
+static void __exit alsa_card_es1688_exit(void)
+{
+	int idx;
+
+	for (idx = 0; idx < SNDRV_CARDS; idx++)
+		snd_card_free(snd_audiodrive_cards[idx]);
+}
+
+module_init(alsa_card_es1688_init)
+module_exit(alsa_card_es1688_exit)
+
+#ifndef MODULE
+
+/* format is: snd-card-es1688=snd_enable,snd_index,snd_id,
+			      snd_port,snd_mpu_port,
+			      snd_irq,snd_mpu_irq,
+			      snd_dma8 */
+
+static int __init alsa_card_es1688_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_port[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_mpu_port[nr_dev]) == 2 &&
+	       get_option(&str,&snd_irq[nr_dev]) == 2 &&
+	       get_option(&str,&snd_mpu_irq[nr_dev]) == 2 &&
+	       get_option(&str,&snd_dma8[nr_dev]) == 2);
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-card-es1688=", alsa_card_es1688_setup);
+
+#endif /* ifndef MODULE */
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/es1688/es1688_lib.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/es1688/es1688_lib.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/es1688/es1688_lib.c	(revision 91)
@@ -0,0 +1,1160 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  Routines for control of ESS ES1688/688/488 chip
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#define SNDRV_MAIN_OBJECT_FILE
+#include <sound/driver.h>
+#include <sound/es1688.h>
+
+#define chip_t es1688_t
+
+static int snd_es1688_dsp_command(es1688_t *chip, unsigned char val)
+{
+	int i;
+
+	for (i = 10000; i; i--)
+		if ((inb(ES1688P(chip, STATUS)) & 0x80) == 0) {
+			outb(val, ES1688P(chip, COMMAND));
+			return 1;
+		}
+#ifdef CONFIG_SND_DEBUG
+	printk("snd_es1688_dsp_command: timeout (0x%x)\n", val);
+#endif
+	return 0;
+}
+
+static int snd_es1688_dsp_get_byte(es1688_t *chip)
+{
+	int i;
+
+	for (i = 1000; i; i--)
+		if (inb(ES1688P(chip, DATA_AVAIL)) & 0x80)
+			return inb(ES1688P(chip, READ));
+	snd_printd("es1688 get byte failed: 0x%lx = 0x%x!!!\n", ES1688P(chip, DATA_AVAIL), inb(ES1688P(chip, DATA_AVAIL)));
+	return -ENODEV;
+}
+
+static int snd_es1688_write(es1688_t *chip,
+			    unsigned char reg, unsigned char data)
+{
+	if (!snd_es1688_dsp_command(chip, reg))
+		return 0;
+	return snd_es1688_dsp_command(chip, data);
+}
+
+int snd_es1688_read(es1688_t *chip, unsigned char reg)
+{
+	/* Read a byte from an extended mode register of ES1688 */
+	if (!snd_es1688_dsp_command(chip, 0xc0))
+		return -1;
+	if (!snd_es1688_dsp_command(chip, reg))
+		return -1;
+	return snd_es1688_dsp_get_byte(chip);
+}
+
+void snd_es1688_mixer_write(es1688_t *chip,
+			    unsigned char reg, unsigned char data)
+{
+	outb(reg, ES1688P(chip, MIXER_ADDR));
+	udelay(10);
+	outb(data, ES1688P(chip, MIXER_DATA));
+	udelay(10);
+}
+
+unsigned char snd_es1688_mixer_read(es1688_t *chip, unsigned char reg)
+{
+	unsigned char result;
+
+	outb(reg, ES1688P(chip, MIXER_ADDR));
+	udelay(10);
+	result = inb(ES1688P(chip, MIXER_DATA));
+	udelay(10);
+	return result;
+}
+
+static int snd_es1688_reset(es1688_t *chip)
+{
+	int i;
+
+	outb(3, ES1688P(chip, RESET));		/* valid only for ESS chips, SB -> 1 */
+	udelay(10);
+	outb(0, ES1688P(chip, RESET));
+	udelay(30);
+	for (i = 0; i < 1000 && !(inb(ES1688P(chip, DATA_AVAIL)) & 0x80); i++);
+	if (inb(ES1688P(chip, READ)) != 0xaa) {
+		snd_printd("ess_reset at 0x%lx: failed!!!\n", chip->port);
+		return -ENODEV;
+	}
+	snd_es1688_dsp_command(chip, 0xc6);	/* enable extended mode */
+	return 0;
+}
+
+static int snd_es1688_probe(es1688_t *chip)
+{
+	unsigned long flags;
+	unsigned short major, minor, hw;
+	int i;
+
+	/*
+	 *  initialization sequence
+	 */
+
+	spin_lock_irqsave(&chip->reg_lock, flags);	/* Some ESS1688 cards need this */
+	inb(ES1688P(chip, ENABLE1));	/* ENABLE1 */
+	inb(ES1688P(chip, ENABLE1));	/* ENABLE1 */
+	inb(ES1688P(chip, ENABLE1));	/* ENABLE1 */
+	inb(ES1688P(chip, ENABLE2));	/* ENABLE2 */
+	inb(ES1688P(chip, ENABLE1));	/* ENABLE1 */
+	inb(ES1688P(chip, ENABLE2));	/* ENABLE2 */
+	inb(ES1688P(chip, ENABLE1));	/* ENABLE1 */
+	inb(ES1688P(chip, ENABLE1));	/* ENABLE1 */
+	inb(ES1688P(chip, ENABLE2));	/* ENABLE2 */
+	inb(ES1688P(chip, ENABLE1));	/* ENABLE1 */
+	inb(ES1688P(chip, ENABLE0));	/* ENABLE0 */
+
+	if (snd_es1688_reset(chip) < 0) {
+		snd_printdd("ESS: [0x%lx] reset failed... 0x%x\n", chip->port, inb(ES1688P(chip, READ)));
+		spin_unlock_irqrestore(&chip->reg_lock, flags);
+		return -ENODEV;
+	}
+	snd_es1688_dsp_command(chip, 0xe7);	/* return identification */
+
+	for (i = 1000, major = minor = 0; i; i--) {
+		if (inb(ES1688P(chip, DATA_AVAIL)) & 0x80) {
+			if (major == 0) {
+				major = inb(ES1688P(chip, READ));
+			} else {
+				minor = inb(ES1688P(chip, READ));
+			}
+		}
+	}
+
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+	snd_printdd("ESS: [0x%lx] found.. major = 0x%x, minor = 0x%x\n", chip->port, major, minor);
+
+	chip->version = (major << 8) | minor;
+	if (!chip->version)
+		return -ENODEV;	/* probably SB */
+
+	hw = ES1688_HW_AUTO;
+	switch (chip->version & 0xfff0) {
+	case 0x4880:
+		snd_printk("[0x%lx] ESS: AudioDrive ES488 detected, but driver is in another place\n", chip->port);
+		return -ENODEV;
+	case 0x6880:
+		hw = (chip->version & 0x0f) >= 8 ? ES1688_HW_1688 : ES1688_HW_688;
+		break;
+	default:
+		snd_printk("[0x%lx] ESS: unknown AudioDrive chip with version 0x%x (Jazz16 soundcard?)\n", chip->port, chip->version);
+		return -ENODEV;
+	}
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_es1688_write(chip, 0xb1, 0x10);	/* disable IRQ */
+	snd_es1688_write(chip, 0xb2, 0x00);	/* disable DMA */
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+	/* enable joystick, but disable OPL3 */
+	spin_lock_irqsave(&chip->mixer_lock, flags);
+	snd_es1688_mixer_write(chip, 0x40, 0x01);
+	spin_unlock_irqrestore(&chip->mixer_lock, flags);
+
+	return 0;
+}
+
+static int snd_es1688_init(es1688_t * chip, int enable)
+{
+	static int irqs[16] = {-1, -1, 0, -1, -1, 1, -1, 2, -1, 0, 3, -1, -1, -1, -1, -1};
+	unsigned long flags;
+	int cfg, irq_bits, dma, dma_bits, tmp, tmp1;
+
+	/* ok.. setup MPU-401 port and joystick and OPL3 */
+	cfg = 0x01;		/* enable joystick, but disable OPL3 */
+	if (enable && chip->mpu_port >= 0x300 && chip->mpu_irq > 0 && chip->hardware != ES1688_HW_688) {
+		tmp = (chip->mpu_port & 0x0f0) >> 4;
+		if (tmp <= 3) {
+			switch (chip->mpu_irq) {
+			case 9:
+				tmp1 = 4;
+				break;
+			case 5:
+				tmp1 = 5;
+				break;
+			case 7:
+				tmp1 = 6;
+				break;
+			case 10:
+				tmp1 = 7;
+				break;
+			default:
+				tmp1 = 0;
+			}
+			if (tmp1) {
+				cfg |= (tmp << 3) | (tmp1 << 5);
+			}
+		}
+	}
+#if 0
+	snd_printk("mpu cfg = 0x%x\n", cfg);
+#endif
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_es1688_mixer_write(chip, 0x40, cfg);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	/* --- */
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_es1688_read(chip, 0xb1);
+	snd_es1688_read(chip, 0xb2);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	if (enable) {
+		cfg = 0xf0;	/* enable only DMA counter interrupt */
+		irq_bits = irqs[chip->irq & 0x0f];
+		if (irq_bits < 0) {
+			snd_printk("[0x%lx] ESS: bad IRQ %d for ES1688 chip!!\n", chip->port, chip->irq);
+#if 0
+			irq_bits = 0;
+			cfg = 0x10;
+#endif
+			return -EINVAL;
+		}
+		spin_lock_irqsave(&chip->reg_lock, flags);
+		snd_es1688_write(chip, 0xb1, cfg | (irq_bits << 2));
+		spin_unlock_irqrestore(&chip->reg_lock, flags);
+		cfg = 0xf0;	/* extended mode DMA enable */
+		dma = chip->dma8;
+		if (dma > 3 || dma == 2) {
+			snd_printk("[0x%lx] ESS: bad DMA channel %d for ES1688 chip!!\n", chip->port, dma);
+#if 0
+			dma_bits = 0;
+			cfg = 0x00;	/* disable all DMA */
+#endif
+			return -EINVAL;
+		} else {
+			dma_bits = dma;
+			if (dma != 3)
+				dma_bits++;
+		}
+		spin_lock_irqsave(&chip->reg_lock, flags);
+		snd_es1688_write(chip, 0xb2, cfg | (dma_bits << 2));
+		spin_unlock_irqrestore(&chip->reg_lock, flags);
+	} else {
+		spin_lock_irqsave(&chip->reg_lock, flags);
+		snd_es1688_write(chip, 0xb1, 0x10);	/* disable IRQ */
+		snd_es1688_write(chip, 0xb2, 0x00);	/* disable DMA */
+		spin_unlock_irqrestore(&chip->reg_lock, flags);
+	}
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_es1688_read(chip, 0xb1);
+	snd_es1688_read(chip, 0xb2);
+	snd_es1688_reset(chip);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return 0;
+}
+
+/*
+
+ */
+
+#ifdef TARGET_OS2
+static ratnum_t clocks[2] = {
+	{
+		795444,
+		1,
+		128,
+		1,
+	},
+	{
+		397722,
+		1,
+		128,
+		1,
+	}
+};
+
+static snd_pcm_hw_constraint_ratnums_t hw_constraints_clocks  = {
+	2,
+	clocks,
+};
+#else
+static ratnum_t clocks[2] = {
+	{
+		num: 795444,
+		den_min: 1,
+		den_max: 128,
+		den_step: 1,
+	},
+	{
+		num: 397722,
+		den_min: 1,
+		den_max: 128,
+		den_step: 1,
+	}
+};
+
+static snd_pcm_hw_constraint_ratnums_t hw_constraints_clocks  = {
+	nrats: 2,
+	rats: clocks,
+};
+#endif
+
+static void snd_es1688_set_rate(es1688_t *chip, snd_pcm_substream_t *substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	unsigned int bits, divider;
+
+	if (runtime->rate_num == clocks[0].num)
+		bits = 256 - runtime->rate_den;
+	else
+		bits = 128 - runtime->rate_den;
+	/* set filter register */
+	divider = 256 - 7160000*20/(8*82*runtime->rate);
+	/* write result to hardware */
+	snd_es1688_write(chip, 0xa1, bits);
+	snd_es1688_write(chip, 0xa2, divider);
+}
+
+static int snd_es1688_ioctl(snd_pcm_substream_t * substream,
+			    unsigned int cmd, void *arg)
+{
+	return snd_pcm_lib_ioctl(substream, cmd, arg);
+}
+
+static int snd_es1688_trigger(es1688_t *chip, int cmd, unsigned char value)
+{
+	int val;
+
+	if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+		value = 0x00;
+	} else if (cmd != SNDRV_PCM_TRIGGER_START) {
+		return -EINVAL;
+	}
+	spin_lock(&chip->reg_lock);
+	chip->trigger_value = value;
+	val = snd_es1688_read(chip, 0xb8);
+	if ((val < 0) || (val & 0x0f) == value) {
+		spin_unlock(&chip->reg_lock);
+		return -EINVAL;	/* something is wrong */
+	}
+#if 0
+	printk("trigger: val = 0x%x, value = 0x%x\n", val, value);
+	printk("trigger: residue = 0x%x\n", get_dma_residue(chip->dma8));
+#endif
+	snd_es1688_write(chip, 0xb8, (val & 0xf0) | value);
+	spin_unlock(&chip->reg_lock);
+	return 0;
+}
+
+static int snd_es1688_hw_params(snd_pcm_substream_t * substream,
+				snd_pcm_hw_params_t * hw_params)
+{
+	return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+static int snd_es1688_hw_free(snd_pcm_substream_t * substream)
+{
+	return snd_pcm_lib_free_pages(substream);
+}
+
+static int snd_es1688_playback_prepare(snd_pcm_substream_t * substream)
+{
+	unsigned long flags;
+	es1688_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	unsigned int size = snd_pcm_lib_buffer_bytes(substream);
+	unsigned int count = snd_pcm_lib_period_bytes(substream);
+
+	chip->dma_size = size;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_es1688_reset(chip);
+	snd_es1688_set_rate(chip, substream);
+	snd_es1688_write(chip, 0xb8, 4);	/* auto init DMA mode */
+	snd_es1688_write(chip, 0xa8, (snd_es1688_read(chip, 0xa8) & ~0x03) | (3 - runtime->channels));
+	snd_es1688_write(chip, 0xb9, 2);	/* demand mode (4 bytes/request) */
+	if (runtime->channels == 1) {
+		if (snd_pcm_format_width(runtime->format) == 8) {
+			/* 8. bit mono */
+			snd_es1688_write(chip, 0xb6, 0x80);
+			snd_es1688_write(chip, 0xb7, 0x51);
+			snd_es1688_write(chip, 0xb7, 0xd0);
+		} else {
+			/* 16. bit mono */
+			snd_es1688_write(chip, 0xb6, 0x00);
+			snd_es1688_write(chip, 0xb7, 0x71);
+			snd_es1688_write(chip, 0xb7, 0xf4);
+		}
+	} else {
+		if (snd_pcm_format_width(runtime->format) == 8) {
+			/* 8. bit stereo */
+			snd_es1688_write(chip, 0xb6, 0x80);
+			snd_es1688_write(chip, 0xb7, 0x51);
+			snd_es1688_write(chip, 0xb7, 0x98);
+		} else {
+			/* 16. bit stereo */
+			snd_es1688_write(chip, 0xb6, 0x00);
+			snd_es1688_write(chip, 0xb7, 0x71);
+			snd_es1688_write(chip, 0xb7, 0xbc);
+		}
+	}
+	snd_es1688_write(chip, 0xb1, (snd_es1688_read(chip, 0xb1) & 0x0f) | 0x50);
+	snd_es1688_write(chip, 0xb2, (snd_es1688_read(chip, 0xb2) & 0x0f) | 0x50);
+	snd_es1688_dsp_command(chip, ES1688_DSP_CMD_SPKON);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	/* --- */
+	count = -count;
+	snd_dma_program(chip->dma8, runtime->dma_area, size, DMA_MODE_WRITE | DMA_AUTOINIT);
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_es1688_write(chip, 0xa4, (unsigned char) count);
+	snd_es1688_write(chip, 0xa5, (unsigned char) (count >> 8));
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return 0;
+}
+
+static int snd_es1688_playback_trigger(snd_pcm_substream_t * substream,
+				       int cmd)
+{
+	es1688_t *chip = snd_pcm_substream_chip(substream);
+	return snd_es1688_trigger(chip, cmd, 0x05);
+}
+
+static int snd_es1688_capture_prepare(snd_pcm_substream_t * substream)
+{
+	unsigned long flags;
+	es1688_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	unsigned int size = snd_pcm_lib_buffer_bytes(substream);
+	unsigned int count = snd_pcm_lib_period_bytes(substream);
+
+	chip->dma_size = size;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_es1688_reset(chip);
+	snd_es1688_set_rate(chip, substream);
+	snd_es1688_dsp_command(chip, ES1688_DSP_CMD_SPKOFF);
+	snd_es1688_write(chip, 0xb8, 0x0e);	/* auto init DMA mode */
+	snd_es1688_write(chip, 0xa8, (snd_es1688_read(chip, 0xa8) & ~0x03) | (3 - runtime->channels));
+	snd_es1688_write(chip, 0xb9, 2);	/* demand mode (4 bytes/request) */
+	if (runtime->channels == 1) {
+		if (snd_pcm_format_width(runtime->format) == 8) {
+			/* 8. bit mono */
+			snd_es1688_write(chip, 0xb7, 0x51);
+			snd_es1688_write(chip, 0xb7, 0xd0);
+		} else {
+			/* 16. bit mono */
+			snd_es1688_write(chip, 0xb7, 0x71);
+			snd_es1688_write(chip, 0xb7, 0xf4);
+		}
+	} else {
+		if (snd_pcm_format_width(runtime->format) == 8) {
+			/* 8. bit stereo */
+			snd_es1688_write(chip, 0xb7, 0x51);
+			snd_es1688_write(chip, 0xb7, 0x98);
+		} else {
+			/* 16. bit stereo */
+			snd_es1688_write(chip, 0xb7, 0x71);
+			snd_es1688_write(chip, 0xb7, 0xbc);
+		}
+	}
+	snd_es1688_write(chip, 0xb1, (snd_es1688_read(chip, 0xb1) & 0x0f) | 0x50);
+	snd_es1688_write(chip, 0xb2, (snd_es1688_read(chip, 0xb2) & 0x0f) | 0x50);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	/* --- */
+	count = -count;
+	snd_dma_program(chip->dma8, runtime->dma_area, size, DMA_MODE_READ | DMA_AUTOINIT);
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_es1688_write(chip, 0xa4, (unsigned char) count);
+	snd_es1688_write(chip, 0xa5, (unsigned char) (count >> 8));
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return 0;
+}
+
+static int snd_es1688_capture_trigger(snd_pcm_substream_t * substream,
+				      int cmd)
+{
+	es1688_t *chip = snd_pcm_substream_chip(substream);
+	return snd_es1688_trigger(chip, cmd, 0x0f);
+}
+
+void snd_es1688_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	es1688_t *chip = snd_magic_cast(es1688_t, dev_id, return);
+
+	if (chip->trigger_value == 0x05)	/* ok.. playback is active */
+		snd_pcm_period_elapsed(chip->playback_substream);
+	if (chip->trigger_value == 0x0f)	/* ok.. capture is active */
+		snd_pcm_period_elapsed(chip->capture_substream);
+
+	inb(ES1688P(chip, DATA_AVAIL));	/* ack interrupt */
+}
+
+static snd_pcm_uframes_t snd_es1688_playback_pointer(snd_pcm_substream_t * substream)
+{
+	es1688_t *chip = snd_pcm_substream_chip(substream);
+	size_t ptr;
+	
+	if (chip->trigger_value != 0x05)
+		return 0;
+	ptr = chip->dma_size - snd_dma_residue(chip->dma8);
+	return bytes_to_frames(substream->runtime, ptr);
+}
+
+static snd_pcm_uframes_t snd_es1688_capture_pointer(snd_pcm_substream_t * substream)
+{
+	es1688_t *chip = snd_pcm_substream_chip(substream);
+	size_t ptr;
+	
+	if (chip->trigger_value != 0x0f)
+		return 0;
+	ptr = chip->dma_size - snd_dma_residue(chip->dma8);
+	return bytes_to_frames(substream->runtime, ptr);
+}
+
+/*
+
+ */
+
+#ifdef TARGET_OS2
+static snd_pcm_hardware_t snd_es1688_playback =
+{
+/*	info:		  */	(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+/*	formats:	  */	SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+/*	rates:		  */	SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+/*	rate_min:	  */	4000,
+/*	rate_max:	  */	48000,
+/*	channels_min:	  */	1,
+/*	channels_max:	  */	2,
+/*	buffer_bytes_max:  */	65536,
+/*	period_bytes_min:  */	64,
+/*	period_bytes_max:  */	65536,
+/*	periods_min:	  */	1,
+/*	periods_max:	  */	1024,
+/*	fifo_size:	  */	0,
+};
+
+static snd_pcm_hardware_t snd_es1688_capture =
+{
+/*	info:		  */	(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+/*	formats:	  */	SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+/*	rates:		  */	SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+/*	rate_min:	  */	4000,
+/*	rate_max:	  */	48000,
+/*	channels_min:	  */	1,
+/*	channels_max:	  */	2,
+/*	buffer_bytes_max:  */	65536,
+/*	period_bytes_min:  */	64,
+/*	period_bytes_max:  */	65536,
+/*	periods_min:	  */	1,
+/*	periods_max:	  */	1024,
+/*	fifo_size:	  */	0,
+};
+#else
+static snd_pcm_hardware_t snd_es1688_playback =
+{
+	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+	formats:		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	rate_min:		4000,
+	rate_max:		48000,
+	channels_min:		1,
+	channels_max:		2,
+	buffer_bytes_max:	65536,
+	period_bytes_min:	64,
+	period_bytes_max:	65536,
+	periods_min:		1,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+
+static snd_pcm_hardware_t snd_es1688_capture =
+{
+	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+	formats:		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	rate_min:		4000,
+	rate_max:		48000,
+	channels_min:		1,
+	channels_max:		2,
+	buffer_bytes_max:	65536,
+	period_bytes_min:	64,
+	period_bytes_max:	65536,
+	periods_min:		1,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+#endif
+
+/*
+
+ */
+
+static int snd_es1688_playback_open(snd_pcm_substream_t * substream)
+{
+	es1688_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	if (chip->capture_substream != NULL)
+		return -EAGAIN;
+	chip->playback_substream = substream;
+	runtime->hw = snd_es1688_playback;
+	snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+				      &hw_constraints_clocks);
+	return 0;
+}
+
+static int snd_es1688_capture_open(snd_pcm_substream_t * substream)
+{
+	es1688_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	if (chip->playback_substream != NULL)
+		return -EAGAIN;
+	chip->capture_substream = substream;
+	runtime->hw = snd_es1688_capture;
+	snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+				      &hw_constraints_clocks);
+	return 0;
+}
+
+static int snd_es1688_playback_close(snd_pcm_substream_t * substream)
+{
+	es1688_t *chip = snd_pcm_substream_chip(substream);
+
+	chip->playback_substream = NULL;
+	return 0;
+}
+
+static int snd_es1688_capture_close(snd_pcm_substream_t * substream)
+{
+	es1688_t *chip = snd_pcm_substream_chip(substream);
+
+	chip->capture_substream = NULL;
+	return 0;
+}
+
+static int snd_es1688_free(es1688_t *chip)
+{
+	if (chip->res_port) {
+		snd_es1688_init(chip, 0);
+		release_resource(chip->res_port);
+	}
+	if (chip->irq >= 0)
+		free_irq(chip->irq, (void *) chip);
+	if (chip->dma8 >= 0) {
+		disable_dma(chip->dma8);
+		free_dma(chip->dma8);
+	}
+	snd_magic_kfree(chip);
+	return 0;
+}
+
+static int snd_es1688_dev_free(snd_device_t *device)
+{
+	es1688_t *chip = snd_magic_cast(es1688_t, device->device_data, return -ENXIO);
+	return snd_es1688_free(chip);
+}
+
+static const char *snd_es1688_chip_id(es1688_t *chip)
+{
+	static char tmp[16];
+	sprintf(tmp, "ES%s688 rev %i", chip->hardware == ES1688_HW_688 ? "" : "1", chip->version & 0x0f);
+	return tmp;
+}
+
+int snd_es1688_create(snd_card_t * card,
+		      unsigned long port,
+		      unsigned long mpu_port,
+		      int irq,
+		      int mpu_irq,
+		      int dma8,
+		      unsigned short hardware,
+		      es1688_t **rchip)
+{
+#ifdef TARGET_OS2
+	static snd_device_ops_t ops = {
+		snd_es1688_dev_free,0,0
+	};
+#else
+	static snd_device_ops_t ops = {
+		dev_free:	snd_es1688_dev_free,
+	};
+#endif                                
+	es1688_t *chip;
+	int err;
+
+	*rchip = NULL;
+	chip = snd_magic_kcalloc(es1688_t, 0, GFP_KERNEL);
+	if (chip == NULL)
+		return -ENOMEM;
+	chip->irq = -1;
+	chip->dma8 = -1;
+	
+	if ((chip->res_port = request_region(port + 4, 12, "ES1688")) == NULL) {
+		snd_es1688_free(chip);
+		return -EBUSY;
+	}
+	if (request_irq(irq, snd_es1688_interrupt, SA_INTERRUPT, "ES1688", (void *) chip)) {
+		snd_es1688_free(chip);
+		return -EBUSY;
+	}
+	chip->irq = irq;
+	if (request_dma(dma8, "ES1688")) {
+		snd_es1688_free(chip);
+		return -EBUSY;
+	}
+	chip->dma8 = dma8;
+
+	spin_lock_init(&chip->reg_lock);
+	spin_lock_init(&chip->mixer_lock);
+	chip->card = card;
+	chip->port = port;
+	mpu_port &= ~0x000f;
+	if (mpu_port < 0x300 || mpu_port > 0x330)
+		mpu_port = 0;
+	chip->mpu_port = mpu_port;
+	chip->mpu_irq = mpu_irq;
+	chip->hardware = hardware;
+
+	if ((err = snd_es1688_probe(chip)) < 0) {
+		snd_es1688_free(chip);
+		return err;
+	}
+	if ((err = snd_es1688_init(chip, 1)) < 0) {
+		snd_es1688_free(chip);
+		return err;
+	}
+
+	/* Register device */
+	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+		snd_es1688_free(chip);
+		return err;
+	}
+
+	*rchip = chip;
+	return 0;
+}
+
+#ifdef TARGET_OS2
+static snd_pcm_ops_t snd_es1688_playback_ops = {
+	snd_es1688_playback_open,
+	snd_es1688_playback_close,
+	snd_es1688_ioctl,
+	snd_es1688_hw_params,
+	snd_es1688_hw_free,
+	snd_es1688_playback_prepare,
+	snd_es1688_playback_trigger,
+	snd_es1688_playback_pointer,0,0
+};
+
+static snd_pcm_ops_t snd_es1688_capture_ops = {
+	snd_es1688_capture_open,
+	snd_es1688_capture_close,
+	snd_es1688_ioctl,
+	snd_es1688_hw_params,
+	snd_es1688_hw_free,
+	snd_es1688_capture_prepare,
+	snd_es1688_capture_trigger,
+	snd_es1688_capture_pointer,
+};
+#else
+static snd_pcm_ops_t snd_es1688_playback_ops = {
+	open:			snd_es1688_playback_open,
+	close:			snd_es1688_playback_close,
+	ioctl:			snd_es1688_ioctl,
+	hw_params:		snd_es1688_hw_params,
+	hw_free:		snd_es1688_hw_free,
+	prepare:		snd_es1688_playback_prepare,
+	trigger:		snd_es1688_playback_trigger,
+	pointer:		snd_es1688_playback_pointer,
+};
+
+static snd_pcm_ops_t snd_es1688_capture_ops = {
+	open:			snd_es1688_capture_open,
+	close:			snd_es1688_capture_close,
+	ioctl:			snd_es1688_ioctl,
+	hw_params:		snd_es1688_hw_params,
+	hw_free:		snd_es1688_hw_free,
+	prepare:		snd_es1688_capture_prepare,
+	trigger:		snd_es1688_capture_trigger,
+	pointer:		snd_es1688_capture_pointer,
+};
+#endif
+
+static void snd_es1688_pcm_free(snd_pcm_t *pcm)
+{
+	es1688_t *chip = snd_magic_cast(es1688_t, pcm->private_data, return);
+	chip->pcm = NULL;
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+int snd_es1688_pcm(es1688_t * chip, int device, snd_pcm_t ** rpcm)
+{
+	snd_pcm_t *pcm;
+	int err;
+
+	if ((err = snd_pcm_new(chip->card, "ESx688", device, 1, 1, &pcm)) < 0)
+		return err;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_es1688_playback_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_es1688_capture_ops);
+
+	pcm->private_data = chip;
+	pcm->private_free = snd_es1688_pcm_free;
+	pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX;
+	sprintf(pcm->name, snd_es1688_chip_id(chip));
+	chip->pcm = pcm;
+
+	snd_pcm_lib_preallocate_pages_for_all(pcm, 64*1024, 64*1024, GFP_KERNEL|GFP_DMA);
+
+	if (rpcm)
+		*rpcm = NULL;
+	return 0;
+}
+
+/*
+ *  MIXER part
+ */
+
+static int snd_es1688_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	static char *texts[9] = {
+		"Mic", "Mic Master", "CD", "AOUT",
+		"Mic1", "Mix", "Line", "Master"
+	};
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 8;
+	if (uinfo->value.enumerated.item > 7)
+		uinfo->value.enumerated.item = 7;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int snd_es1688_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	es1688_t *chip = snd_kcontrol_chip(kcontrol);
+	ucontrol->value.enumerated.item[0] = snd_es1688_mixer_read(chip, ES1688_REC_DEV) & 7;
+	return 0;
+}
+
+static int snd_es1688_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	es1688_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	unsigned char oval, nval;
+	int change;
+	
+	if (ucontrol->value.enumerated.item[0] > 8)
+		return -EINVAL;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	oval = snd_es1688_mixer_read(chip, ES1688_REC_DEV);
+	nval = (ucontrol->value.enumerated.item[0] & 7) | (oval & ~15);
+	change = nval != oval;
+	if (change)
+		snd_es1688_mixer_write(chip, ES1688_REC_DEV, nval);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return change;
+}
+
+#ifdef TARGET_OS2
+#define ES1688_SINGLE(xname, xindex, reg, shift, mask, invert) \
+{ SNDRV_CTL_ELEM_IFACE_MIXER, 0,0,xname, xindex, \
+  0, 0, snd_es1688_info_single, \
+  snd_es1688_get_single, snd_es1688_put_single, \
+  reg | (shift << 8) | (mask << 16) | (invert << 24) }
+#else
+#define ES1688_SINGLE(xname, xindex, reg, shift, mask, invert) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
+  info: snd_es1688_info_single, \
+  get: snd_es1688_get_single, put: snd_es1688_put_single, \
+  private_value: reg | (shift << 8) | (mask << 16) | (invert << 24) }
+#endif
+
+static int snd_es1688_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+
+	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	return 0;
+}
+
+static int snd_es1688_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	es1688_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 8) & 0xff;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0xff;
+	
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	ucontrol->value.integer.value[0] = (snd_es1688_mixer_read(chip, reg) >> shift) & mask;
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	if (invert)
+		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+	return 0;
+}
+
+static int snd_es1688_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	es1688_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 8) & 0xff;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0xff;
+	int change;
+	unsigned char oval, nval;
+	
+	nval = (ucontrol->value.integer.value[0] & mask);
+	if (invert)
+		nval = mask - nval;
+	nval <<= shift;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	oval = snd_es1688_mixer_read(chip, reg);
+	nval = (oval & ~(mask << shift)) | nval;
+	change = nval != oval;
+	if (change)
+		snd_es1688_mixer_write(chip, reg, nval);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return change;
+}
+
+#ifdef TARGET_OS2
+#define ES1688_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \
+{ SNDRV_CTL_ELEM_IFACE_MIXER, 0,0,xname, xindex, \
+  0, 0, snd_es1688_info_double, \
+  snd_es1688_get_double, snd_es1688_put_double, \
+  left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
+#else
+#define ES1688_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
+  info: snd_es1688_info_double, \
+  get: snd_es1688_get_double, put: snd_es1688_put_double, \
+  private_value: left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
+#endif
+
+static int snd_es1688_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+
+	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	return 0;
+}
+
+static int snd_es1688_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	es1688_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int left_reg = kcontrol->private_value & 0xff;
+	int right_reg = (kcontrol->private_value >> 8) & 0xff;
+	int shift_left = (kcontrol->private_value >> 16) & 0x07;
+	int shift_right = (kcontrol->private_value >> 19) & 0x07;
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+	int invert = (kcontrol->private_value >> 22) & 1;
+	unsigned char left, right;
+	
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	if (left_reg < 0xa0)
+		left = snd_es1688_mixer_read(chip, left_reg);
+	else
+		left = snd_es1688_read(chip, left_reg);
+	if (left_reg != right_reg) {
+		if (right_reg < 0xa0) 
+			right = snd_es1688_mixer_read(chip, right_reg);
+		else
+			right = snd_es1688_read(chip, right_reg);
+	} else
+		right = left;
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	ucontrol->value.integer.value[0] = (left >> shift_left) & mask;
+	ucontrol->value.integer.value[1] = (right >> shift_right) & mask;
+	if (invert) {
+		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+		ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1];
+	}
+	return 0;
+}
+
+static int snd_es1688_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	es1688_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int left_reg = kcontrol->private_value & 0xff;
+	int right_reg = (kcontrol->private_value >> 8) & 0xff;
+	int shift_left = (kcontrol->private_value >> 16) & 0x07;
+	int shift_right = (kcontrol->private_value >> 19) & 0x07;
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+	int invert = (kcontrol->private_value >> 22) & 1;
+	int change;
+	unsigned char val1, val2, oval1, oval2;
+	
+	val1 = ucontrol->value.integer.value[0] & mask;
+	val2 = ucontrol->value.integer.value[1] & mask;
+	if (invert) {
+		val1 = mask - val1;
+		val2 = mask - val2;
+	}
+	val1 <<= shift_left;
+	val2 <<= shift_right;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	if (left_reg != right_reg) {
+		if (left_reg < 0xa0)
+			oval1 = snd_es1688_mixer_read(chip, left_reg);
+		else
+			oval1 = snd_es1688_read(chip, left_reg);
+		if (right_reg < 0xa0)
+			oval2 = snd_es1688_mixer_read(chip, right_reg);
+		else
+			oval2 = snd_es1688_read(chip, right_reg);
+		val1 = (oval1 & ~(mask << shift_left)) | val1;
+		val2 = (oval2 & ~(mask << shift_right)) | val2;
+		change = val1 != oval1 || val2 != oval2;
+		if (change) {
+			if (left_reg < 0xa0)
+				snd_es1688_mixer_write(chip, left_reg, val1);
+			else
+				snd_es1688_write(chip, left_reg, val1);
+			if (right_reg < 0xa0)
+				snd_es1688_mixer_write(chip, right_reg, val1);
+			else
+				snd_es1688_write(chip, right_reg, val1);
+		}
+	} else {
+		if (left_reg < 0xa0)
+			oval1 = snd_es1688_mixer_read(chip, left_reg);
+		else
+			oval1 = snd_es1688_read(chip, left_reg);
+		val1 = (oval1 & ~((mask << shift_left) | (mask << shift_right))) | val1 | val2;
+		change = val1 != oval1;
+		if (change) {
+			if (left_reg < 0xa0)
+				snd_es1688_mixer_write(chip, left_reg, val1);
+			else
+				snd_es1688_write(chip, left_reg, val1);
+		}
+			
+	}
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return change;
+}
+
+#define ES1688_CONTROLS (sizeof(snd_es1688_controls)/sizeof(snd_kcontrol_new_t))
+
+static snd_kcontrol_new_t snd_es1688_controls[] = {
+ES1688_DOUBLE("Master Playback Volume", 0, ES1688_MASTER_DEV, ES1688_MASTER_DEV, 4, 0, 15, 0),
+ES1688_DOUBLE("PCM Playback Volume", 0, ES1688_PCM_DEV, ES1688_PCM_DEV, 4, 0, 15, 0),
+ES1688_DOUBLE("Line Playback Volume", 0, ES1688_LINE_DEV, ES1688_LINE_DEV, 4, 0, 15, 0),
+ES1688_DOUBLE("CD Playback Volume", 0, ES1688_CD_DEV, ES1688_CD_DEV, 4, 0, 15, 0),
+ES1688_DOUBLE("FM Playback Volume", 0, ES1688_FM_DEV, ES1688_FM_DEV, 4, 0, 15, 0),
+ES1688_DOUBLE("Mic Playback Volume", 0, ES1688_MIC_DEV, ES1688_MIC_DEV, 4, 0, 15, 0),
+ES1688_DOUBLE("Aux Playback Volume", 0, ES1688_AUX_DEV, ES1688_AUX_DEV, 4, 0, 15, 0),
+ES1688_SINGLE("PC Speaker Playback Volume", 0, ES1688_SPEAKER_DEV, 0, 7, 0),
+ES1688_DOUBLE("Capture Volume", 0, ES1688_RECLEV_DEV, ES1688_RECLEV_DEV, 4, 0, 15, 0),
+ES1688_SINGLE("Capture Switch", 0, ES1688_REC_DEV, 4, 1, 1),
+{
+#ifdef TARGET_OS2
+	SNDRV_CTL_ELEM_IFACE_MIXER,0,0,
+	"Capture Source",0,0, 0,
+	snd_es1688_info_mux,
+	snd_es1688_get_mux,
+	snd_es1688_put_mux,0
+#else
+	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	name: "Capture Source",
+	info: snd_es1688_info_mux,
+	get: snd_es1688_get_mux,
+	put: snd_es1688_put_mux,
+#endif
+},
+};
+
+#define ES1688_INIT_TABLE_SIZE (sizeof(snd_es1688_init_table)/2)
+
+static unsigned char snd_es1688_init_table[][2] = {
+	{ ES1688_MASTER_DEV, 0 },
+	{ ES1688_PCM_DEV, 0 },
+	{ ES1688_LINE_DEV, 0 },
+	{ ES1688_CD_DEV, 0 },
+	{ ES1688_FM_DEV, 0 },
+	{ ES1688_MIC_DEV, 0 },
+	{ ES1688_AUX_DEV, 0 },
+	{ ES1688_SPEAKER_DEV, 0 },
+	{ ES1688_RECLEV_DEV, 0 },
+	{ ES1688_REC_DEV, 0x17 }
+};
+                                        
+int snd_es1688_mixer(es1688_t *chip)
+{
+	snd_card_t *card;
+	int err, idx;
+	unsigned char reg, val;
+
+	snd_assert(chip != NULL && chip->card != NULL, return -EINVAL);
+
+	card = chip->card;
+
+	strcpy(card->mixername, snd_es1688_chip_id(chip));
+
+	for (idx = 0; idx < ES1688_CONTROLS; idx++) {
+		if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es1688_controls[idx], chip))) < 0)
+			return err;
+	}
+	for (idx = 0; idx < ES1688_INIT_TABLE_SIZE; idx++) {
+		reg = snd_es1688_init_table[idx][0];
+		val = snd_es1688_init_table[idx][1];
+		if (reg < 0xa0)
+			snd_es1688_mixer_write(chip, reg, val);
+		else
+			snd_es1688_write(chip, reg, val);
+	}
+	return 0;
+}
+
+EXPORT_SYMBOL(snd_es1688_mixer_write);
+EXPORT_SYMBOL(snd_es1688_mixer_read);
+EXPORT_SYMBOL(snd_es1688_interrupt);
+EXPORT_SYMBOL(snd_es1688_create);
+EXPORT_SYMBOL(snd_es1688_pcm);
+EXPORT_SYMBOL(snd_es1688_mixer);
+
+/*
+ *  INIT part
+ */
+
+static int __init alsa_es1688_init(void)
+{
+	return 0;
+}
+
+static void __exit alsa_es1688_exit(void)
+{
+}
+
+module_init(alsa_es1688_init)
+module_exit(alsa_es1688_exit)
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/es1688/makefile.os2
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/es1688/makefile.os2	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/es1688/makefile.os2	(revision 91)
@@ -0,0 +1,50 @@
+CINCLUDES = ..\..\..\include;..\..\include;$(%WATCOM)\H
+AINCLUDES = $(DDK_BASE)\H
+
+CDEFINES = -D__KERNEL__ -DMODULE -dTARGET_OS2 -dALSA_BUILD -D__ISAPNP__ -DCONFIG_ISAPNP -D__i386_
+
+32BIT=1
+!include ..\..\..\..\makefile.inc
+
+
+#===================================================================
+#
+#   List of source files
+#
+#===================================================================
+FILE1    = es1688_lib.obj es1688.obj
+FILE2    = 
+FILE3    = 
+FILE4    = 
+FILE5    = 
+FILE6    = 
+FILE7    = 
+FILE8    = 
+FILELAST =
+FILES    = $(FILE0) $(FILE1) $(FILE2) $(FILE3) $(FILE4) $(FILE5) $(FILE6) $(FILE7) $(FILE8) $(FILE9) $(FILE10) $(FILE11) $(FILE12)
+
+TARGET   = es1688
+
+LNKFILE  = $(OBJDIR)\$(TARGET).lnk
+
+
+#===================================================================
+#
+#   Specific dependencies
+#
+#===================================================================
+all: $(OBJDIR)\$(TARGET).lib 
+
+
+$(LNKFILE): makefile.os2
+    @%create $(LNKFILE)
+    @for %f in ($(FILES)) do @%append $(LNKFILE) -+$(OBJDIR)\%f
+
+$(OBJDIR)\$(TARGET).lib: $(LNKFILE) $(FILES)
+    $(LIB) $(OBJDIR)\$(TARGET).lib @$(LNKFILE)
+    copy $(OBJDIR)\$(TARGET).lib $(ALSA_LIB) 
+
+clean: .SYMBOLIC
+     cd $(OBJDIR)
+     -del *.obj *.err *.lnk *.sys *.sym *.lst *.map *.wmap *.lib
+     cd ..
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/es18xx.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/es18xx.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/es18xx.c	(revision 91)
@@ -0,0 +1,2376 @@
+/*
+ *  Driver for generic ESS AudioDrive ES18xx soundcards
+ *  Copyright (c) by Christian Fischbach <fishbach@pool.informatik.rwth-aachen.de>
+ *  Copyright (c) by Abramo Bagnara <abramo@alsa-project.org>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+/* GENERAL NOTES:
+ *
+ * BUGS:
+ * - There are pops (we can't delay in trigger function, cause midlevel 
+ *   often need to trigger down and then up very quickly).
+ *   Any ideas?
+ * - Support for 16 bit DMA seems to be broken. I've no hardware to tune it.
+ */
+
+/*
+ * ES1868  NOTES:
+ * - The chip has one half duplex pcm (with very limited full duplex support).
+ *
+ * - Duplex stereophonic sound is impossible.
+ * - Record and playback must share the same frequency rate.
+ *
+ * - The driver use dma2 for playback and dma1 for capture.
+ */
+
+/*
+ * ES1869 NOTES:
+ *
+ * - there are a first full duplex pcm and a second playback only pcm
+ *   (incompatible with first pcm capture)
+ * 
+ * - there is support for the capture volume and ESS Spatializer 3D effect.
+ *
+ * - contrarily to some pages in DS_1869.PDF the rates can be set
+ *   independently.
+ *
+ * BUGS:
+ *
+ * - There is a major trouble I noted:
+ *
+ *   using both channel for playback stereo 16 bit samples at 44100 Hz
+ *   the second pcm (Audio1) DMA slows down irregularly and sound is garbled.
+ *   
+ *   The same happens using Audio1 for captureing.
+ *
+ *   The Windows driver does not suffer of this (although it use Audio1
+ *   only for captureing). I'm unable to discover why.
+ *
+ */
+
+
+#define SNDRV_MAIN_OBJECT_FILE
+#include <sound/driver.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/mpu401.h>
+#include <sound/opl3.h>
+#define SNDRV_LEGACY_AUTO_PROBE
+#define SNDRV_LEGACY_FIND_FREE_IRQ
+#define SNDRV_LEGACY_FIND_FREE_DMA
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+struct _snd_es18xx {
+	unsigned long port;		/* port of ESS chip */
+	unsigned long mpu_port;		/* MPU-401 port of ESS chip */
+	unsigned long fm_port;		/* FM port */
+	unsigned long ctrl_port;	/* Control port of ESS chip */
+	struct resource *res_port;
+	struct resource *res_mpu_port;
+	struct resource *res_ctrl_port;
+	int irq;			/* IRQ number of ESS chip */
+	int dma1;			/* DMA1 */
+	int dma2;			/* DMA2 */
+	unsigned short version;		/* version of ESS chip */
+	int caps;			/* Chip capabilities */
+	unsigned short audio2_vol;	/* volume level of audio2 */
+
+	unsigned short active;		/* active channel mask */
+	unsigned int dma1_size;
+	unsigned int dma2_size;
+	unsigned int dma1_shift;
+	unsigned int dma2_shift;
+
+	snd_card_t *card;
+	snd_pcm_t *pcm;
+	snd_pcm_substream_t *playback_a_substream;
+	snd_pcm_substream_t *capture_a_substream;
+	snd_pcm_substream_t *playback_b_substream;
+
+	snd_rawmidi_t *rmidi;
+
+	snd_kcontrol_t *hw_volume;
+	snd_kcontrol_t *hw_switch;
+	snd_kcontrol_t *master_volume;
+	snd_kcontrol_t *master_switch;
+
+	spinlock_t reg_lock;
+	spinlock_t mixer_lock;
+	spinlock_t ctrl_lock;
+};
+
+#define AUDIO1_IRQ	0x01
+#define AUDIO2_IRQ	0x02
+#define HWV_IRQ		0x04
+#define MPU_IRQ		0x08
+
+#define ES18XX_PCM2	0x0001	/* Has two useable PCM */
+#define ES18XX_SPATIALIZER 0x0002	/* Has 3D Spatializer */
+#define ES18XX_RECMIX	0x0004	/* Has record mixer */
+#define ES18XX_DUPLEX_MONO 0x0008	/* Has mono duplex only */
+#define ES18XX_DUPLEX_SAME 0x0010	/* Playback and record must share the same rate */
+#define ES18XX_NEW_RATE	0x0020	/* More precise rate setting */
+#define ES18XX_AUXB	0x0040	/* AuxB mixer control */
+#define ES18XX_HWV	0x0080	/* Has hardware volume */
+#define ES18XX_MONO	0x0100	/* Mono_in mixer control */
+#define ES18XX_I2S	0x0200	/* I2S mixer control */
+#define ES18XX_MUTEREC	0x0400	/* Record source can be muted */
+#define ES18XX_CONTROL	0x0800	/* Has control ports */
+
+typedef struct _snd_es18xx es18xx_t;
+
+#define chip_t es18xx_t
+
+/* Lowlevel */
+
+#define DAC1 0x01
+#define ADC1 0x02
+#define DAC2 0x04
+#define MILLISECOND 10000
+
+static int snd_es18xx_dsp_command(es18xx_t *chip, unsigned char val)
+{
+        int i;
+
+        for(i = MILLISECOND; i; i--)
+                if ((inb(chip->port + 0x0C) & 0x80) == 0) {
+                        outb(val, chip->port + 0x0C);
+                        return 0;
+                }
+        snd_printk("dsp_command: timeout (0x%x)\n", val);
+        return -EINVAL;
+}
+
+static int snd_es18xx_dsp_get_byte(es18xx_t *chip)
+{
+        int i;
+
+        for(i = MILLISECOND/10; i; i--)
+                if (inb(chip->port + 0x0C) & 0x40)
+                        return inb(chip->port + 0x0A);
+        snd_printk("dsp_get_byte failed: 0x%lx = 0x%x!!!\n", chip->port + 0x0A, inb(chip->port + 0x0A));
+        return -ENODEV;
+}
+
+#undef REG_DEBUG
+
+static int snd_es18xx_write(es18xx_t *chip,
+			    unsigned char reg, unsigned char data)
+{
+	unsigned long flags;
+	int ret;
+	
+        spin_lock_irqsave(&chip->reg_lock, flags);
+	ret = snd_es18xx_dsp_command(chip, reg);
+	if (ret < 0)
+		goto end;
+        ret = snd_es18xx_dsp_command(chip, data);
+ end:
+        spin_unlock_irqrestore(&chip->reg_lock, flags);
+#ifdef REG_DEBUG
+	snd_printk("Reg %02x set to %02x\n", reg, data);
+#endif
+	return ret;
+}
+
+static int snd_es18xx_read(es18xx_t *chip, unsigned char reg)
+{
+	unsigned long flags;
+	int ret, data;
+        spin_lock_irqsave(&chip->reg_lock, flags);
+	ret = snd_es18xx_dsp_command(chip, 0xC0);
+	if (ret < 0)
+		goto end;
+        ret = snd_es18xx_dsp_command(chip, reg);
+	if (ret < 0)
+		goto end;
+	data = snd_es18xx_dsp_get_byte(chip);
+	ret = data;
+#ifdef REG_DEBUG
+	snd_printk("Reg %02x now is %02x (%d)\n", reg, data, ret);
+#endif
+ end:
+        spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return ret;
+}
+
+/* Return old value */
+static int snd_es18xx_bits(es18xx_t *chip, unsigned char reg,
+			   unsigned char mask, unsigned char val)
+{
+        int ret;
+	unsigned char old, new, oval;
+	unsigned long flags;
+        spin_lock_irqsave(&chip->reg_lock, flags);
+        ret = snd_es18xx_dsp_command(chip, 0xC0);
+	if (ret < 0)
+		goto end;
+        ret = snd_es18xx_dsp_command(chip, reg);
+	if (ret < 0)
+		goto end;
+	ret = snd_es18xx_dsp_get_byte(chip);
+	if (ret < 0) {
+		goto end;
+	}
+	old = ret;
+	oval = old & mask;
+	if (val != oval) {
+		ret = snd_es18xx_dsp_command(chip, reg);
+		if (ret < 0)
+			goto end;
+		new = (old & ~mask) | (val & mask);
+		ret = snd_es18xx_dsp_command(chip, new);
+		if (ret < 0)
+			goto end;
+#ifdef REG_DEBUG
+		snd_printk("Reg %02x was %02x, set to %02x (%d)\n", reg, old, new, ret);
+#endif
+	}
+	ret = oval;
+ end:
+        spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return ret;
+}
+
+inline void snd_es18xx_mixer_write(es18xx_t *chip,
+			    unsigned char reg, unsigned char data)
+{
+	unsigned long flags;
+        spin_lock_irqsave(&chip->mixer_lock, flags);
+        outb(reg, chip->port + 0x04);
+        outb(data, chip->port + 0x05);
+        spin_unlock_irqrestore(&chip->mixer_lock, flags);
+#ifdef REG_DEBUG
+	snd_printk("Mixer reg %02x set to %02x\n", reg, data);
+#endif
+}
+
+inline int snd_es18xx_mixer_read(es18xx_t *chip, unsigned char reg)
+{
+	unsigned long flags;
+	int data;
+        spin_lock_irqsave(&chip->mixer_lock, flags);
+        outb(reg, chip->port + 0x04);
+	data = inb(chip->port + 0x05);
+        spin_unlock_irqrestore(&chip->mixer_lock, flags);
+#ifdef REG_DEBUG
+	snd_printk("Mixer reg %02x now is %02x\n", reg, data);
+#endif
+        return data;
+}
+
+/* Return old value */
+static inline int snd_es18xx_mixer_bits(es18xx_t *chip, unsigned char reg,
+					unsigned char mask, unsigned char val)
+{
+	unsigned char old, new, oval;
+	unsigned long flags;
+        spin_lock_irqsave(&chip->mixer_lock, flags);
+        outb(reg, chip->port + 0x04);
+	old = inb(chip->port + 0x05);
+	oval = old & mask;
+	if (val != oval) {
+		new = (old & ~mask) | (val & mask);
+		outb(new, chip->port + 0x05);
+#ifdef REG_DEBUG
+		snd_printk("Mixer reg %02x was %02x, set to %02x\n", reg, old, new);
+#endif
+	}
+        spin_unlock_irqrestore(&chip->mixer_lock, flags);
+	return oval;
+}
+
+static inline int snd_es18xx_mixer_writable(es18xx_t *chip, unsigned char reg,
+					    unsigned char mask)
+{
+	int old, expected, new;
+	unsigned long flags;
+        spin_lock_irqsave(&chip->mixer_lock, flags);
+        outb(reg, chip->port + 0x04);
+	old = inb(chip->port + 0x05);
+	expected = old ^ mask;
+	outb(expected, chip->port + 0x05);
+	new = inb(chip->port + 0x05);
+        spin_unlock_irqrestore(&chip->mixer_lock, flags);
+#ifdef REG_DEBUG
+	snd_printk("Mixer reg %02x was %02x, set to %02x, now is %02x\n", reg, old, expected, new);
+#endif
+	return expected == new;
+}
+
+
+static int snd_es18xx_reset(es18xx_t *chip)
+{
+	int i;
+        outb(0x03, chip->port + 0x06);
+        inb(chip->port + 0x06);
+        outb(0x00, chip->port + 0x06);
+        for(i = 0; i < MILLISECOND && !(inb(chip->port + 0x0E) & 0x80); i++);
+        if (inb(chip->port + 0x0A) != 0xAA)
+                return -1;
+	return 0;
+}
+
+static int snd_es18xx_reset_fifo(es18xx_t *chip)
+{
+        outb(0x02, chip->port + 0x06);
+        inb(chip->port + 0x06);
+        outb(0x00, chip->port + 0x06);
+	return 0;
+}
+
+#ifdef TARGET_OS2
+static ratnum_t new_clocks[2] = {
+	{
+/*		num: */ 793800,
+/*		den_min: */ 1,
+/*		den_max: */ 128,
+/*		den_step: */ 1,
+	},
+	{
+/*		num: */ 768000,
+/*		den_min: */ 1,
+/*		den_max: */ 128,
+/*		den_step: */ 1,
+	}
+};
+
+static snd_pcm_hw_constraint_ratnums_t new_hw_constraints_clocks = {
+/*	nrats: */ 2,
+/*	rats: */ new_clocks,
+};
+
+static ratnum_t old_clocks[2] = {
+	{
+/*		num: */ 795444,
+/*		den_min: */ 1,
+/*		den_max: */ 128,
+/*		den_step: */ 1,
+	},
+	{
+/*		num: */ 397722,
+/*		den_min: */ 1,
+/*		den_max: */ 128,
+/*		den_step: */ 1,
+	}
+};
+
+static snd_pcm_hw_constraint_ratnums_t old_hw_constraints_clocks  = {
+/*	nrats: */ 2,
+/*	rats: */ old_clocks,
+};
+#else
+static ratnum_t new_clocks[2] = {
+	{
+		num: 793800,
+		den_min: 1,
+		den_max: 128,
+		den_step: 1,
+	},
+	{
+		num: 768000,
+		den_min: 1,
+		den_max: 128,
+		den_step: 1,
+	}
+};
+
+static snd_pcm_hw_constraint_ratnums_t new_hw_constraints_clocks = {
+	nrats: 2,
+	rats: new_clocks,
+};
+
+static ratnum_t old_clocks[2] = {
+	{
+		num: 795444,
+		den_min: 1,
+		den_max: 128,
+		den_step: 1,
+	},
+	{
+		num: 397722,
+		den_min: 1,
+		den_max: 128,
+		den_step: 1,
+	}
+};
+
+static snd_pcm_hw_constraint_ratnums_t old_hw_constraints_clocks  = {
+	nrats: 2,
+	rats: old_clocks,
+};
+#endif
+
+static void snd_es18xx_rate_set(es18xx_t *chip, 
+				snd_pcm_substream_t *substream,
+				int mode)
+{
+	unsigned int bits, div0;
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	if (chip->caps & ES18XX_NEW_RATE) {
+		if (runtime->rate_num == new_clocks[0].num)
+			bits = 128 - runtime->rate_den;
+		else
+			bits = 256 - runtime->rate_den;
+	} else {
+		if (runtime->rate_num == old_clocks[0].num)
+			bits = 256 - runtime->rate_den;
+		else
+			bits = 128 - runtime->rate_den;
+	}
+
+	/* set filter register */
+	div0 = 256 - 7160000*20/(8*82*runtime->rate);
+		
+	if ((chip->caps & ES18XX_PCM2) && mode == DAC2) {
+		snd_es18xx_mixer_write(chip, 0x70, bits);
+		snd_es18xx_mixer_write(chip, 0x72, div0);
+	} else {
+		snd_es18xx_write(chip, 0xA1, bits);
+		snd_es18xx_write(chip, 0xA2, div0);
+	}
+}
+
+static int snd_es18xx_playback_hw_params(snd_pcm_substream_t * substream,
+					 snd_pcm_hw_params_t * hw_params)
+{
+	es18xx_t *chip = snd_pcm_substream_chip(substream);
+	int shift, err;
+
+	shift = 0;
+	if (params_channels(hw_params) == 2)
+		shift++;
+	if (snd_pcm_format_width(params_format(hw_params)) == 16)
+		shift++;
+
+	switch (substream->number) {
+	case 0:
+		if ((chip->caps & ES18XX_DUPLEX_MONO) &&
+		    (chip->capture_a_substream) &&
+		    params_channels(hw_params) != 1) {
+			_snd_pcm_hw_param_setempty(hw_params, SNDRV_PCM_HW_PARAM_CHANNELS);
+			return -EBUSY;
+		}
+		chip->dma2_shift = shift;
+		break;
+	case 1:
+		chip->dma1_shift = shift;
+		break;
+	}
+	if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
+		return err;
+	return 0;
+}
+
+static int snd_es18xx_playback1_prepare(es18xx_t *chip,
+					snd_pcm_substream_t *substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	unsigned int size = snd_pcm_lib_buffer_bytes(substream);
+	unsigned int count = snd_pcm_lib_period_bytes(substream);
+
+	chip->dma2_size = size;
+
+        snd_es18xx_rate_set(chip, substream, DAC2);
+
+        /* Transfer Count Reload */
+        count = 0x10000 - count;
+        snd_es18xx_mixer_write(chip, 0x74, count & 0xff);
+        snd_es18xx_mixer_write(chip, 0x76, count >> 8);
+
+	/* Set format */
+        snd_es18xx_mixer_bits(chip, 0x7A, 0x07,
+			      ((runtime->channels == 1) ? 0x00 : 0x02) |
+			      (snd_pcm_format_width(runtime->format) == 16 ? 0x01 : 0x00) |
+			      (snd_pcm_format_unsigned(runtime->format) ? 0x00 : 0x04));
+
+        /* Set DMA controller */
+        snd_dma_program(chip->dma2, runtime->dma_area, size, DMA_MODE_WRITE | DMA_AUTOINIT);
+
+	return 0;
+}
+
+static int snd_es18xx_playback1_trigger(es18xx_t *chip,
+					snd_pcm_substream_t * substream,
+					int cmd)
+{
+        if (cmd == SNDRV_PCM_TRIGGER_START) {
+		if (chip->active & DAC2)
+			return 0;
+		chip->active |= DAC2;
+	} else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+		if (!(chip->active & DAC2))
+			return 0;
+		chip->active &= ~DAC2;
+	} else {
+		return -EINVAL;
+	}
+
+	if (cmd == SNDRV_PCM_TRIGGER_START) {	
+                /* Start DMA */
+		if (chip->dma2 >= 4)
+			snd_es18xx_mixer_write(chip, 0x78, 0xb3);
+		else
+			snd_es18xx_mixer_write(chip, 0x78, 0x93);
+#ifdef AVOID_POPS
+		/* Avoid pops */
+                udelay(100000);
+		if (chip->caps & ES18XX_PCM2)
+			/* Restore Audio 2 volume */
+			snd_es18xx_mixer_write(chip, 0x7C, chip->audio2_vol);
+		else
+			/* Enable PCM output */
+			snd_es18xx_dsp_command(chip, 0xD1);
+#endif
+        }
+        else {
+                /* Stop DMA */
+                snd_es18xx_mixer_write(chip, 0x78, 0x00);
+#ifdef AVOID_POPS
+                udelay(25000);
+		if (chip->caps & ES18XX_PCM2)
+			/* Set Audio 2 volume to 0 */
+			snd_es18xx_mixer_write(chip, 0x7C, 0);
+		else
+			/* Disable PCM output */
+			snd_es18xx_dsp_command(chip, 0xD3);
+#endif
+        }
+
+	return 0;
+}
+
+static int snd_es18xx_capture_hw_params(snd_pcm_substream_t * substream,
+					snd_pcm_hw_params_t * hw_params)
+{
+	es18xx_t *chip = snd_pcm_substream_chip(substream);
+	int shift, err;
+
+	shift = 0;
+	if ((chip->caps & ES18XX_DUPLEX_MONO) &&
+	    chip->playback_a_substream &&
+	    params_channels(hw_params) != 1) {
+		_snd_pcm_hw_param_setempty(hw_params, SNDRV_PCM_HW_PARAM_CHANNELS);
+		return -EBUSY;
+	}
+	if (params_channels(hw_params) == 2)
+		shift++;
+	if (snd_pcm_format_width(params_format(hw_params)) == 16)
+		shift++;
+	chip->dma1_shift = shift;
+	if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
+		return err;
+	return 0;
+}
+
+static int snd_es18xx_capture_prepare(snd_pcm_substream_t *substream)
+{
+        es18xx_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	unsigned int size = snd_pcm_lib_buffer_bytes(substream);
+	unsigned int count = snd_pcm_lib_period_bytes(substream);
+
+	chip->dma1_size = size;
+
+	snd_es18xx_reset_fifo(chip);
+
+        /* Set stereo/mono */
+        snd_es18xx_bits(chip, 0xA8, 0x03, runtime->channels == 1 ? 0x02 : 0x01);
+
+        snd_es18xx_rate_set(chip, substream, ADC1);
+
+        /* Transfer Count Reload */
+	count = 0x10000 - count;
+	snd_es18xx_write(chip, 0xA4, count & 0xff);
+	snd_es18xx_write(chip, 0xA5, count >> 8);
+
+#ifdef AVOID_POPS
+	udelay(100000);
+#endif
+
+        /* Set format */
+        snd_es18xx_write(chip, 0xB7, 
+                         snd_pcm_format_unsigned(runtime->format) ? 0x51 : 0x71);
+        snd_es18xx_write(chip, 0xB7, 0x90 |
+                         ((runtime->channels == 1) ? 0x40 : 0x08) |
+                         (snd_pcm_format_width(runtime->format) == 16 ? 0x04 : 0x00) |
+                         (snd_pcm_format_unsigned(runtime->format) ? 0x00 : 0x20));
+
+        /* Set DMA controler */
+        snd_dma_program(chip->dma1, runtime->dma_area, size, DMA_MODE_READ | DMA_AUTOINIT);
+
+	return 0;
+}
+
+static int snd_es18xx_capture_trigger(snd_pcm_substream_t *substream,
+				      int cmd)
+{
+        es18xx_t *chip = snd_pcm_substream_chip(substream);
+
+        if (cmd == SNDRV_PCM_TRIGGER_START) {
+		if (chip->active & ADC1)
+			return 0;
+		chip->active |= ADC1;
+	} else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+		if (!(chip->active & ADC1))
+			return 0;
+		chip->active &= ~ADC1;
+	} else {
+		return -EINVAL;
+	}
+
+	if (cmd == SNDRV_PCM_TRIGGER_START)
+                /* Start DMA */
+                snd_es18xx_write(chip, 0xB8, 0x0f);
+        else
+                /* Stop DMA */
+                snd_es18xx_write(chip, 0xB8, 0x00);
+	return 0;
+}
+
+static int snd_es18xx_playback2_prepare(es18xx_t *chip,
+					snd_pcm_substream_t *substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	unsigned int size = snd_pcm_lib_buffer_bytes(substream);
+	unsigned int count = snd_pcm_lib_period_bytes(substream);
+
+	chip->dma1_size = size;
+
+	snd_es18xx_reset_fifo(chip);
+
+        /* Set stereo/mono */
+        snd_es18xx_bits(chip, 0xA8, 0x03, runtime->channels == 1 ? 0x02 : 0x01);
+
+        snd_es18xx_rate_set(chip, substream, DAC1);
+
+        /* Transfer Count Reload */
+	count = 0x10000 - count;
+	snd_es18xx_write(chip, 0xA4, count & 0xff);
+	snd_es18xx_write(chip, 0xA5, count >> 8);
+
+        /* Set format */
+        snd_es18xx_write(chip, 0xB6,
+                         snd_pcm_format_unsigned(runtime->format) ? 0x80 : 0x00);
+        snd_es18xx_write(chip, 0xB7, 
+                         snd_pcm_format_unsigned(runtime->format) ? 0x51 : 0x71);
+        snd_es18xx_write(chip, 0xB7, 0x90 |
+                         (runtime->channels == 1 ? 0x40 : 0x08) |
+                         (snd_pcm_format_width(runtime->format) == 16 ? 0x04 : 0x00) |
+                         (snd_pcm_format_unsigned(runtime->format) ? 0x00 : 0x20));
+
+        /* Set DMA controler */
+        snd_dma_program(chip->dma1, runtime->dma_area, size, DMA_MODE_WRITE | DMA_AUTOINIT);
+
+	return 0;
+}
+
+static int snd_es18xx_playback2_trigger(es18xx_t *chip,
+					snd_pcm_substream_t *substream,
+					int cmd)
+{
+        if (cmd == SNDRV_PCM_TRIGGER_START) {
+		if (chip->active & DAC1)
+			return 0;
+		chip->active |= DAC1;
+        } else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+		if (!(chip->active & DAC1))
+			return 0;
+		chip->active &= ~DAC1;
+	} else {
+		return -EINVAL;
+	}
+
+	if (cmd == SNDRV_PCM_TRIGGER_START) {
+                /* Start DMA */
+                snd_es18xx_write(chip, 0xB8, 0x05);
+#ifdef AVOID_POPS
+		/* Avoid pops */
+                udelay(100000);
+                /* Enable Audio 1 */
+                snd_es18xx_dsp_command(chip, 0xD1);
+#endif
+        }
+        else {
+                /* Stop DMA */
+                snd_es18xx_write(chip, 0xB8, 0x00);
+#ifdef AVOID_POPS
+		/* Avoid pops */
+                udelay(25000);
+                /* Disable Audio 1 */
+                snd_es18xx_dsp_command(chip, 0xD3);
+#endif
+        }
+	return 0;
+}
+
+static int snd_es18xx_playback_prepare(snd_pcm_substream_t *substream)
+{
+        es18xx_t *chip = snd_pcm_substream_chip(substream);
+	switch (substream->number) {
+	case 0:
+		return snd_es18xx_playback1_prepare(chip, substream);
+	case 1:
+		return snd_es18xx_playback2_prepare(chip, substream);
+	}
+	return -EINVAL;
+}
+
+static int snd_es18xx_playback_trigger(snd_pcm_substream_t *substream,
+				       int cmd)
+{
+        es18xx_t *chip = snd_pcm_substream_chip(substream);
+	switch (substream->number) {
+	case 0:
+		return snd_es18xx_playback1_trigger(chip, substream, cmd);
+	case 1:
+		return snd_es18xx_playback2_trigger(chip, substream, cmd);
+	}
+	return -EINVAL;
+}
+
+static void snd_es18xx_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	es18xx_t *chip = snd_magic_cast(es18xx_t, dev_id, return);
+	unsigned char status;
+
+
+	if (chip->caps & ES18XX_CONTROL) {
+		/* Read Interrupt status */
+		status = inb(chip->ctrl_port + 6);
+	} else {
+		/* Read Interrupt status */
+		status = snd_es18xx_mixer_read(chip, 0x7f) >> 4;
+	}
+#if 0
+	else {
+		status = 0;
+		if (inb(chip->port + 0x0C) & 0x01)
+			status |= AUDIO1_IRQ;
+		if (snd_es18xx_mixer_read(chip, 0x7A) & 0x80)
+			status |= AUDIO2_IRQ;
+		if ((chip->caps & ES18XX_HWV) &&
+		    snd_es18xx_mixer_read(chip, 0x64) & 0x10)
+			status |= HWV_IRQ;
+	}
+#endif
+
+	/* Audio 1 & Audio 2 */
+        if (status & AUDIO2_IRQ) {
+                if (chip->active & DAC2)
+                	snd_pcm_period_elapsed(chip->playback_a_substream);
+		/* ack interrupt */
+                snd_es18xx_mixer_bits(chip, 0x7A, 0x80, 0x00);
+        }
+        if (status & AUDIO1_IRQ) {
+                /* ok.. capture is active */
+                if (chip->active & ADC1)
+                	snd_pcm_period_elapsed(chip->capture_a_substream);
+                /* ok.. playback2 is active */
+                else if (chip->active & DAC1)
+                	snd_pcm_period_elapsed(chip->playback_b_substream);
+		/* ack interrupt */
+		inb(chip->port + 0x0E);
+        }
+
+	/* MPU */
+	if ((status & MPU_IRQ) && chip->rmidi)
+		snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data, regs);
+
+	/* Hardware volume */
+	if (status & HWV_IRQ) {
+		int split = snd_es18xx_mixer_read(chip, 0x64) & 0x80;
+		snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->hw_switch->id);
+		snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->hw_volume->id);
+		if (!split) {
+			snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->master_switch->id);
+			snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->master_volume->id);
+		}
+		/* ack interrupt */
+		snd_es18xx_mixer_write(chip, 0x66, 0x00);
+	}
+
+}
+
+static snd_pcm_uframes_t snd_es18xx_playback_pointer(snd_pcm_substream_t * substream)
+{
+        es18xx_t *chip = snd_pcm_substream_chip(substream);
+	int pos;
+
+	switch (substream->number) {
+	case 0:
+		if (!(chip->active & DAC2))
+			return 0;
+		pos = chip->dma2_size - snd_dma_residue(chip->dma2);
+		return pos >> chip->dma2_shift;
+	case 1:
+		if (!(chip->active & DAC1))
+			return 0;
+		pos = chip->dma1_size - snd_dma_residue(chip->dma1);
+		return pos >> chip->dma1_shift;
+	}
+	return 0;
+}
+
+static snd_pcm_uframes_t snd_es18xx_capture_pointer(snd_pcm_substream_t * substream)
+{
+        es18xx_t *chip = snd_pcm_substream_chip(substream);
+	int pos;
+
+        if (!(chip->active & ADC1))
+                return 0;
+	pos = chip->dma1_size - snd_dma_residue(chip->dma1);
+	return pos >> chip->dma1_shift;
+}
+
+#ifdef TARGET_OS2
+static snd_pcm_hardware_t snd_es18xx_playback =
+{
+/*	info:		  */	(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+/*	formats:	  */	(SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 | 
+				 SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE),
+/*	rates:		  */	SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+/*	rate_min:	  */	4000,
+/*	rate_max:	  */	48000,
+/*	channels_min:	  */	1,
+/*	channels_max:	  */	2,
+/*	buffer_bytes_max: */	65536,
+/*	period_bytes_min: */	64,
+/*	period_bytes_max: */	65536,
+/*	periods_min:	  */	1,
+/*	periods_max:	  */	1024,
+/*	fifo_size:	  */	0,
+};
+
+static snd_pcm_hardware_t snd_es18xx_capture =
+{
+/*	info:		  */	(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+/*	formats:	  */	(SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 | 
+				 SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE),
+/*	rates:		  */	SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+/*	rate_min:	  */	4000,
+/*	rate_max:	  */	48000,
+/*	channels_min:	  */	1,
+/*	channels_max:	  */	2,
+/*	buffer_bytes_max: */	65536,
+/*	period_bytes_min: */	64,
+/*	period_bytes_max: */	65536,
+/*	periods_min:	  */	1,
+/*	periods_max:	  */	1024,
+/*	fifo_size:	  */	0,
+};
+#else
+static snd_pcm_hardware_t snd_es18xx_playback =
+{
+	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+	formats:		(SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 | 
+				 SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE),
+	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	rate_min:		4000,
+	rate_max:		48000,
+	channels_min:		1,
+	channels_max:		2,
+	buffer_bytes_max:	65536,
+	period_bytes_min:	64,
+	period_bytes_max:	65536,
+	periods_min:		1,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+
+static snd_pcm_hardware_t snd_es18xx_capture =
+{
+	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+	formats:		(SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 | 
+				 SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE),
+	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	rate_min:		4000,
+	rate_max:		48000,
+	channels_min:		1,
+	channels_max:		2,
+	buffer_bytes_max:	65536,
+	period_bytes_min:	64,
+	period_bytes_max:	65536,
+	periods_min:		1,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+#endif
+
+static int snd_es18xx_playback_open(snd_pcm_substream_t * substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+        es18xx_t *chip = snd_pcm_substream_chip(substream);
+
+	switch (substream->number) {
+	case 0:
+		if ((chip->caps & ES18XX_DUPLEX_MONO) &&
+		    chip->capture_a_substream && 
+		    chip->capture_a_substream->runtime->channels != 1)
+			return -EAGAIN;
+		chip->playback_a_substream = substream;
+		break;
+	case 1:
+		if (chip->capture_a_substream)
+			return -EAGAIN;
+		chip->playback_b_substream = substream;
+		break;
+	default:
+		snd_BUG();
+		return -EINVAL;
+	}
+	substream->runtime->hw = snd_es18xx_playback;
+	snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+				      (chip->caps & ES18XX_NEW_RATE) ? &new_hw_constraints_clocks : &old_hw_constraints_clocks);
+        return 0;
+}
+
+static int snd_es18xx_capture_open(snd_pcm_substream_t * substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+        es18xx_t *chip = snd_pcm_substream_chip(substream);
+
+        if (chip->playback_b_substream)
+                return -EAGAIN;
+	if ((chip->caps & ES18XX_DUPLEX_MONO) &&
+	    chip->playback_a_substream &&
+	    chip->playback_a_substream->runtime->channels != 1)
+		return -EAGAIN;
+        chip->capture_a_substream = substream;
+	substream->runtime->hw = snd_es18xx_capture;
+	snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+				      (chip->caps & ES18XX_NEW_RATE) ? &new_hw_constraints_clocks : &old_hw_constraints_clocks);
+        return 0;
+}
+
+static int snd_es18xx_playback_close(snd_pcm_substream_t * substream)
+{
+        es18xx_t *chip = snd_pcm_substream_chip(substream);
+
+	switch (substream->number) {
+	case 0:
+		chip->playback_a_substream = NULL;
+		break;
+	case 1:
+		chip->playback_b_substream = NULL;
+		break;
+	default:
+		snd_BUG();
+		return -EINVAL;
+	}
+	
+	snd_pcm_lib_free_pages(substream);
+	return 0;
+}
+
+static int snd_es18xx_capture_close(snd_pcm_substream_t * substream)
+{
+        es18xx_t *chip = snd_pcm_substream_chip(substream);
+
+        chip->capture_a_substream = NULL;
+	snd_pcm_lib_free_pages(substream);
+        return 0;
+}
+
+/*
+ *  MIXER part
+ */
+
+static int snd_es18xx_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	static char *texts[8] = {
+		"Mic", "Mic Master", "CD", "AOUT",
+		"Mic1", "Mix", "Line", "Master"
+	};
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 8;
+	if (uinfo->value.enumerated.item > 7)
+		uinfo->value.enumerated.item = 7;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int snd_es18xx_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	es18xx_t *chip = snd_kcontrol_chip(kcontrol);
+	ucontrol->value.enumerated.item[0] = snd_es18xx_mixer_read(chip, 0x1c) & 0x07;
+	return 0;
+}
+
+static int snd_es18xx_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	es18xx_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned char val = ucontrol->value.enumerated.item[0];
+	
+	if (val > 7)
+		return -EINVAL;
+	return snd_es18xx_mixer_bits(chip, 0x1c, 0x07, val) != val;
+}
+
+static int snd_es18xx_info_spatializer_enable(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_es18xx_get_spatializer_enable(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	es18xx_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned char val = snd_es18xx_mixer_read(chip, 0x50);
+	ucontrol->value.integer.value[0] = !!(val & 8);
+	return 0;
+}
+
+static int snd_es18xx_put_spatializer_enable(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	es18xx_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned char oval, nval;
+	int change;
+	nval = ucontrol->value.integer.value[0] ? 0x0c : 0x04;
+	oval = snd_es18xx_mixer_read(chip, 0x50) & 0x0c;
+	change = nval != oval;
+	if (change) {
+		snd_es18xx_mixer_write(chip, 0x50, nval & ~0x04);
+		snd_es18xx_mixer_write(chip, 0x50, nval);
+	}
+	return change;
+}
+
+static int snd_es18xx_info_hw_volume(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 = 63;
+	return 0;
+}
+
+static int snd_es18xx_get_hw_volume(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	es18xx_t *chip = snd_kcontrol_chip(kcontrol);
+	ucontrol->value.integer.value[0] = snd_es18xx_mixer_read(chip, 0x61) & 0x3f;
+	ucontrol->value.integer.value[1] = snd_es18xx_mixer_read(chip, 0x63) & 0x3f;
+	return 0;
+}
+
+static int snd_es18xx_info_hw_switch(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+	return 0;
+}
+
+static int snd_es18xx_get_hw_switch(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	es18xx_t *chip = snd_kcontrol_chip(kcontrol);
+	ucontrol->value.integer.value[0] = !(snd_es18xx_mixer_read(chip, 0x61) & 0x40);
+	ucontrol->value.integer.value[1] = !(snd_es18xx_mixer_read(chip, 0x63) & 0x40);
+	return 0;
+}
+
+static void snd_es18xx_hwv_free(snd_kcontrol_t *kcontrol)
+{
+	es18xx_t *chip = snd_magic_cast(es18xx_t, _snd_kcontrol_chip(kcontrol), return);
+	chip->master_volume = NULL;
+	chip->master_switch = NULL;
+	chip->hw_volume = NULL;
+	chip->hw_switch = NULL;
+}
+
+static int snd_es18xx_reg_bits(es18xx_t *chip, unsigned char reg,
+			       unsigned char mask, unsigned char val)
+{
+	if (reg < 0xa0)
+		return snd_es18xx_mixer_bits(chip, reg, mask, val);
+	else
+		return snd_es18xx_bits(chip, reg, mask, val);
+}
+
+static int snd_es18xx_reg_read(es18xx_t *chip, unsigned char reg)
+{
+	if (reg < 0xa0)
+		return snd_es18xx_mixer_read(chip, reg);
+	else
+		return snd_es18xx_read(chip, reg);
+}
+
+#ifdef TARGET_OS2
+#define ES18XX_SINGLE(xname, xindex, reg, shift, mask, invert) \
+{ SNDRV_CTL_ELEM_IFACE_MIXER, 0, 0, xname, xindex, \
+  0, 0, snd_es18xx_info_single, \
+  snd_es18xx_get_single, snd_es18xx_put_single, \
+  reg | (shift << 8) | (mask << 16) | (invert << 24) }
+#else
+#define ES18XX_SINGLE(xname, xindex, reg, shift, mask, invert) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
+  info: snd_es18xx_info_single, \
+  get: snd_es18xx_get_single, put: snd_es18xx_put_single, \
+  private_value: reg | (shift << 8) | (mask << 16) | (invert << 24) }
+#endif
+
+static int snd_es18xx_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+
+	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	return 0;
+}
+
+static int snd_es18xx_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	es18xx_t *chip = snd_kcontrol_chip(kcontrol);
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 8) & 0xff;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0xff;
+	int val;
+	
+	val = snd_es18xx_reg_read(chip, reg);
+	ucontrol->value.integer.value[0] = (val >> shift) & mask;
+	if (invert)
+		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+	return 0;
+}
+
+static int snd_es18xx_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	es18xx_t *chip = snd_kcontrol_chip(kcontrol);
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 8) & 0xff;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0xff;
+	unsigned char val;
+	
+	val = (ucontrol->value.integer.value[0] & mask);
+	if (invert)
+		val = mask - val;
+	mask <<= shift;
+	val <<= shift;
+	return snd_es18xx_reg_bits(chip, reg, mask, val) != val;
+}
+
+#ifdef TARGET_OS2
+#define ES18XX_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \
+{ SNDRV_CTL_ELEM_IFACE_MIXER, 0, 0, xname, xindex, \
+  0, 0, snd_es18xx_info_double, \
+  snd_es18xx_get_double, snd_es18xx_put_double, \
+  left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
+#else
+#define ES18XX_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
+  info: snd_es18xx_info_double, \
+  get: snd_es18xx_get_double, put: snd_es18xx_put_double, \
+  private_value: left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
+#endif
+
+static int snd_es18xx_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+
+	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	return 0;
+}
+
+static int snd_es18xx_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	es18xx_t *chip = snd_kcontrol_chip(kcontrol);
+	int left_reg = kcontrol->private_value & 0xff;
+	int right_reg = (kcontrol->private_value >> 8) & 0xff;
+	int shift_left = (kcontrol->private_value >> 16) & 0x07;
+	int shift_right = (kcontrol->private_value >> 19) & 0x07;
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+	int invert = (kcontrol->private_value >> 22) & 1;
+	unsigned char left, right;
+	
+	left = snd_es18xx_reg_read(chip, left_reg);
+	if (left_reg != right_reg)
+		right = snd_es18xx_reg_read(chip, right_reg);
+	else
+		right = left;
+	ucontrol->value.integer.value[0] = (left >> shift_left) & mask;
+	ucontrol->value.integer.value[1] = (right >> shift_right) & mask;
+	if (invert) {
+		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+		ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1];
+	}
+	return 0;
+}
+
+static int snd_es18xx_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	es18xx_t *chip = snd_kcontrol_chip(kcontrol);
+	int left_reg = kcontrol->private_value & 0xff;
+	int right_reg = (kcontrol->private_value >> 8) & 0xff;
+	int shift_left = (kcontrol->private_value >> 16) & 0x07;
+	int shift_right = (kcontrol->private_value >> 19) & 0x07;
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+	int invert = (kcontrol->private_value >> 22) & 1;
+	int change;
+	unsigned char val1, val2, mask1, mask2;
+	
+	val1 = ucontrol->value.integer.value[0] & mask;
+	val2 = ucontrol->value.integer.value[1] & mask;
+	if (invert) {
+		val1 = mask - val1;
+		val2 = mask - val2;
+	}
+	val1 <<= shift_left;
+	val2 <<= shift_right;
+	mask1 = mask << shift_left;
+	mask2 = mask << shift_right;
+	if (left_reg != right_reg) {
+		change = 0;
+		if (snd_es18xx_reg_bits(chip, left_reg, mask1, val1) != val1)
+			change = 1;
+		if (snd_es18xx_reg_bits(chip, right_reg, mask2, val2) != val2)
+			change = 1;
+	} else {
+		change = (snd_es18xx_reg_bits(chip, left_reg, mask1 | mask2, 
+					      val1 | val2) != (val1 | val2));
+	}
+	return change;
+}
+
+static snd_kcontrol_new_t snd_es18xx_base_controls[] = {
+ES18XX_DOUBLE("Master Playback Volume", 0, 0x60, 0x62, 0, 0, 63, 0),
+ES18XX_DOUBLE("Master Playback Switch", 0, 0x60, 0x62, 6, 6, 1, 1),
+ES18XX_DOUBLE("Line Playback Volume", 0, 0x3e, 0x3e, 4, 0, 15, 0),
+ES18XX_DOUBLE("CD Playback Volume", 0, 0x38, 0x38, 4, 0, 15, 0),
+ES18XX_DOUBLE("FM Playback Volume", 0, 0x36, 0x36, 4, 0, 15, 0),
+ES18XX_DOUBLE("Mono Playback Volume", 0, 0x6d, 0x6d, 4, 0, 15, 0),
+ES18XX_DOUBLE("Mic Playback Volume", 0, 0x1a, 0x1a, 4, 0, 15, 0),
+ES18XX_DOUBLE("Aux Playback Volume", 0, 0x3a, 0x3a, 4, 0, 15, 0),
+ES18XX_SINGLE("PC Speaker Playback Volume", 0, 0x3c, 0, 7, 0),
+ES18XX_SINGLE("Record Monitor", 0, 0xa8, 3, 1, 0),
+ES18XX_DOUBLE("Capture Volume", 0, 0xb4, 0xb4, 4, 0, 15, 0),
+ES18XX_SINGLE("Capture Switch", 0, 0x1c, 4, 1, 1),
+{
+#ifdef TARGET_OS2
+	SNDRV_CTL_ELEM_IFACE_MIXER,
+        0,0,
+	"Capture Source", 0, 0, 0,
+	snd_es18xx_info_mux,
+	snd_es18xx_get_mux,
+	snd_es18xx_put_mux, 0
+#else
+	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	name: "Capture Source",
+	info: snd_es18xx_info_mux,
+	get: snd_es18xx_get_mux,
+	put: snd_es18xx_put_mux,
+#endif
+}
+};
+
+static snd_kcontrol_new_t snd_es18xx_mono_in_control = 
+ES18XX_DOUBLE("Mono Input Playback Volume", 0, 0x6d, 0x6d, 4, 0, 15, 0);
+
+static snd_kcontrol_new_t snd_es18xx_recmix_controls[] = {
+ES18XX_DOUBLE("PCM Capture Volume", 0, 0x69, 0x69, 4, 0, 15, 0),
+ES18XX_DOUBLE("Mic Capture Volume", 0, 0x68, 0x68, 4, 0, 15, 0),
+ES18XX_DOUBLE("Line Capture Volume", 0, 0x6e, 0x6e, 4, 0, 15, 0),
+ES18XX_DOUBLE("FM Capture Volume", 0, 0x6b, 0x6b, 4, 0, 15, 0),
+ES18XX_DOUBLE("Mono Capture Volume", 0, 0x6f, 0x6f, 4, 0, 15, 0),
+ES18XX_DOUBLE("CD Capture Volume", 0, 0x6a, 0x6a, 4, 0, 15, 0),
+ES18XX_DOUBLE("Aux Capture Volume", 0, 0x6c, 0x6c, 4, 0, 15, 0)
+};
+
+static snd_kcontrol_new_t snd_es18xx_pcm1_controls[] = {
+ES18XX_DOUBLE("PCM Playback Volume", 0, 0x14, 0x14, 4, 0, 15, 0),
+};
+
+static snd_kcontrol_new_t snd_es18xx_pcm2_controls[] = {
+ES18XX_DOUBLE("PCM Playback Volume", 0, 0x7c, 0x7c, 4, 0, 15, 0),
+ES18XX_DOUBLE("PCM Playback Volume", 1, 0x14, 0x14, 4, 0, 15, 0)
+};
+
+static snd_kcontrol_new_t snd_es18xx_spatializer_controls[] = {
+ES18XX_SINGLE("3D Control - Level", 0, 0x52, 0, 63, 0),
+{
+#ifdef TARGET_OS2
+	SNDRV_CTL_ELEM_IFACE_MIXER,
+        0,0,
+	"3D Control - Switch", 0, 0, 0,
+	snd_es18xx_info_spatializer_enable,
+	snd_es18xx_get_spatializer_enable,
+	snd_es18xx_put_spatializer_enable,
+#else
+	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	name: "3D Control - Switch",
+	info: snd_es18xx_info_spatializer_enable,
+	get: snd_es18xx_get_spatializer_enable,
+	put: snd_es18xx_put_spatializer_enable,
+#endif
+}
+};
+
+static snd_kcontrol_new_t snd_es18xx_micpre1_control = 
+ES18XX_SINGLE("Mic Boost (+26dB)", 0, 0xa9, 2, 1, 0);
+
+static snd_kcontrol_new_t snd_es18xx_micpre2_control =
+ES18XX_SINGLE("Mic Boost (+26dB)", 0, 0x7d, 3, 1, 0);
+
+static snd_kcontrol_new_t snd_es18xx_hw_volume_controls[] = {
+#ifdef TARGET_OS2
+{
+	SNDRV_CTL_ELEM_IFACE_MIXER, 0, 0,
+	"Hardware Master Playback Volume",0,
+	SNDRV_CTL_ELEM_ACCESS_READ, 0,
+	snd_es18xx_info_hw_volume,
+	snd_es18xx_get_hw_volume,0, 0
+},
+{
+	SNDRV_CTL_ELEM_IFACE_MIXER, 0, 0,
+	"Hardware Master Playback Switch",0,
+	SNDRV_CTL_ELEM_ACCESS_READ, 0,
+	snd_es18xx_info_hw_switch,
+	snd_es18xx_get_hw_switch,0, 0
+},
+#else
+{
+	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	name: "Hardware Master Playback Volume",
+	access: SNDRV_CTL_ELEM_ACCESS_READ,
+	info: snd_es18xx_info_hw_volume,
+	get: snd_es18xx_get_hw_volume,
+},
+{
+	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	name: "Hardware Master Playback Switch",
+	access: SNDRV_CTL_ELEM_ACCESS_READ,
+	info: snd_es18xx_info_hw_switch,
+	get: snd_es18xx_get_hw_switch,
+},
+#endif
+ES18XX_SINGLE("Hardware Master Volume Split", 0, 0x64, 7, 1, 0),
+};
+
+#if 0
+static int __init snd_es18xx_config_read(es18xx_t *chip, unsigned char reg)
+{
+	int data;
+	unsigned long flags;
+        spin_lock_irqsave(&chip->ctrl_lock, flags);
+	outb(reg, chip->ctrl_port);
+	data = inb(chip->ctrl_port + 1);
+        spin_unlock_irqrestore(&chip->ctrl_lock, flags);
+	return data;
+}
+#endif
+
+static void __init snd_es18xx_config_write(es18xx_t *chip, 
+					   unsigned char reg, unsigned char data)
+{
+	/* No need for spinlocks, this function is used only in
+	   otherwise protected init code */
+	outb(reg, chip->ctrl_port);
+	outb(data, chip->ctrl_port + 1);
+#ifdef REG_DEBUG
+	snd_printk("Config reg %02x set to %02x\n", reg, data);
+#endif
+}
+
+static int __init snd_es18xx_initialize(es18xx_t *chip)
+{
+	int mask = 0;
+
+        /* enable extended mode */
+        snd_es18xx_dsp_command(chip, 0xC6);
+	/* Reset mixer registers */
+	snd_es18xx_mixer_write(chip, 0x00, 0x00);
+
+        /* Audio 1 DMA demand mode (4 bytes/request) */
+        snd_es18xx_write(chip, 0xB9, 2);
+	if (chip->caps & ES18XX_CONTROL) {
+		/* Hardware volume IRQ */
+		snd_es18xx_config_write(chip, 0x27, chip->irq);
+		if (chip->fm_port > SNDRV_AUTO_PORT) {
+			/* FM I/O */
+			snd_es18xx_config_write(chip, 0x62, chip->fm_port >> 8);
+			snd_es18xx_config_write(chip, 0x63, chip->fm_port & 0xff);
+		}
+		if (chip->mpu_port > SNDRV_AUTO_PORT) {
+			/* MPU-401 I/O */
+			snd_es18xx_config_write(chip, 0x64, chip->mpu_port >> 8);
+			snd_es18xx_config_write(chip, 0x65, chip->mpu_port & 0xff);
+			/* MPU-401 IRQ */
+			snd_es18xx_config_write(chip, 0x28, chip->irq);
+		}
+		/* Audio1 IRQ */
+		snd_es18xx_config_write(chip, 0x70, chip->irq);
+		/* Audio2 IRQ */
+		snd_es18xx_config_write(chip, 0x72, chip->irq);
+		/* Audio1 DMA */
+		snd_es18xx_config_write(chip, 0x74, chip->dma1);
+		/* Audio2 DMA */
+		snd_es18xx_config_write(chip, 0x75, chip->dma2);
+
+		/* Enable Audio 1 IRQ */
+		snd_es18xx_write(chip, 0xB1, 0x50);
+		/* Enable Audio 2 IRQ */
+		snd_es18xx_mixer_write(chip, 0x7A, 0x40);
+		/* Enable Audio 1 DMA */
+		snd_es18xx_write(chip, 0xB2, 0x50);
+		/* Enable MPU and hardware volume interrupt */
+		snd_es18xx_mixer_write(chip, 0x64, 0x42);
+	}
+	else {
+		int irqmask, dma1mask, dma2mask;
+		switch (chip->irq) {
+		case 2:
+		case 9:
+			irqmask = 0;
+			break;
+		case 5:
+			irqmask = 1;
+			break;
+		case 7:
+			irqmask = 2;
+			break;
+		case 10:
+			irqmask = 3;
+			break;
+		default:
+			snd_printk("invalid irq %d\n", chip->irq);
+			return -ENODEV;
+		}
+		switch (chip->dma1) {
+		case 0:
+			dma1mask = 1;
+			break;
+		case 1:
+			dma1mask = 2;
+			break;
+		case 3:
+			dma1mask = 3;
+			break;
+		default:
+			snd_printk("invalid dma1 %d\n", chip->dma1);
+			return -ENODEV;
+		}
+		switch (chip->dma2) {
+		case 0:
+			dma2mask = 0;
+			break;
+		case 1:
+			dma2mask = 1;
+			break;
+		case 3:
+			dma2mask = 2;
+			break;
+		case 5:
+			dma2mask = 3;
+			break;
+		default:
+			snd_printk("invalid dma2 %d\n", chip->dma2);
+			return -ENODEV;
+		}
+
+		/* Enable and set Audio 1 IRQ */
+		snd_es18xx_write(chip, 0xB1, 0x50 | (irqmask << 2));
+		/* Enable and set Audio 1 DMA */
+		snd_es18xx_write(chip, 0xB2, 0x50 | (dma1mask << 2));
+		/* Set Audio 2 DMA */
+		snd_es18xx_mixer_bits(chip, 0x7d, 0x07, 0x04 | dma2mask);
+		/* Enable Audio 2 IRQ and DMA
+		   Set capture mixer input */
+		snd_es18xx_mixer_write(chip, 0x7A, 0x68);
+		/* Enable and set hardware volume interrupt */
+		snd_es18xx_mixer_write(chip, 0x64, 0x06);
+		if (chip->mpu_port > SNDRV_AUTO_PORT) {
+			/* MPU401 share irq with audio
+			   Joystick enabled
+			   FM enabled */
+			snd_es18xx_mixer_write(chip, 0x40, 0x43 | (chip->mpu_port & 0xf0) >> 1);
+		}
+		snd_es18xx_mixer_write(chip, 0x7f, ((irqmask + 1) << 1) | 0x01);
+	}
+	if (chip->caps & ES18XX_NEW_RATE) {
+		/* Change behaviour of register A1
+		   4x oversampling
+		   2nd channel DAC asynchronous */
+		snd_es18xx_mixer_write(chip, 0x71, 0x32);
+	}
+	if (!(chip->caps & ES18XX_PCM2)) {
+		/* Enable DMA FIFO */
+		snd_es18xx_write(chip, 0xB7, 0x80);
+	}
+	if (chip->caps & ES18XX_SPATIALIZER) {
+		/* Set spatializer parameters to recommended values */
+		snd_es18xx_mixer_write(chip, 0x54, 0x8f);
+		snd_es18xx_mixer_write(chip, 0x56, 0x95);
+		snd_es18xx_mixer_write(chip, 0x58, 0x94);
+		snd_es18xx_mixer_write(chip, 0x5a, 0x80);
+	}
+	/* Mute input source */
+	if (chip->caps & ES18XX_MUTEREC)
+		mask = 0x10;
+	if (chip->caps & ES18XX_RECMIX)
+		snd_es18xx_mixer_write(chip, 0x1c, 0x05 | mask);
+	else {
+		snd_es18xx_mixer_write(chip, 0x1c, 0x00 | mask);
+		snd_es18xx_write(chip, 0xb4, 0x00);
+	}
+#ifndef AVOID_POPS
+	/* Enable PCM output */
+	snd_es18xx_dsp_command(chip, 0xD1);
+#endif
+
+        return 0;
+}
+
+static int __init snd_es18xx_identify(es18xx_t *chip)
+{
+	int hi,lo;
+
+	/* reset */
+	if (snd_es18xx_reset(chip) < 0) {
+                snd_printk("reset at 0x%lx failed!!!\n", chip->port);
+		return -ENODEV;
+	}
+
+	snd_es18xx_dsp_command(chip, 0xe7);
+	hi = snd_es18xx_dsp_get_byte(chip);
+	if (hi < 0) {
+		return hi;
+	}
+	lo = snd_es18xx_dsp_get_byte(chip);
+	if ((lo & 0xf0) != 0x80) {
+		return -ENODEV;
+	}
+	if (hi == 0x48) {
+		chip->version = 0x488;
+		return 0;
+	}
+	if (hi != 0x68) {
+		return -ENODEV;
+	}
+	if ((lo & 0x0f) < 8) {
+		chip->version = 0x688;
+		return 0;
+	}
+			
+        outb(0x40, chip->port + 0x04);
+	hi = inb(chip->port + 0x05);
+	lo = inb(chip->port + 0x05);
+	if (hi != lo) {
+		chip->version = hi << 8 | lo;
+		chip->ctrl_port = inb(chip->port + 0x05) << 8;
+		chip->ctrl_port += inb(chip->port + 0x05);
+
+		if ((chip->res_ctrl_port = request_region(chip->ctrl_port, 8, "ES18xx - CTRL")) == NULL)
+			return -EBUSY;
+
+		return 0;
+	}
+
+	/* If has Hardware volume */
+	if (snd_es18xx_mixer_writable(chip, 0x64, 0x04)) {
+		/* If has Audio2 */
+		if (snd_es18xx_mixer_writable(chip, 0x70, 0x7f)) {
+			/* If has volume count */
+			if (snd_es18xx_mixer_writable(chip, 0x64, 0x20)) {
+				chip->version = 0x1887;
+			} else {
+				chip->version = 0x1888;
+			}
+		} else {
+			chip->version = 0x1788;
+		}
+	}
+	else
+		chip->version = 0x1688;
+	return 0;
+}
+
+static int __init snd_es18xx_probe(es18xx_t *chip)
+{
+	if (snd_es18xx_identify(chip) < 0) {
+                snd_printk("[0x%lx] ESS chip not found\n", chip->port);
+                return -ENODEV;
+	}
+
+	switch (chip->version) {
+	case 0x1868:
+		chip->caps = ES18XX_DUPLEX_MONO | ES18XX_DUPLEX_SAME | ES18XX_CONTROL | ES18XX_HWV;
+		break;
+	case 0x1869:
+		chip->caps = ES18XX_PCM2 | ES18XX_SPATIALIZER | ES18XX_RECMIX | ES18XX_NEW_RATE | ES18XX_AUXB | ES18XX_MONO | ES18XX_MUTEREC | ES18XX_CONTROL | ES18XX_HWV;
+		break;
+	case 0x1878:
+		chip->caps = ES18XX_DUPLEX_MONO | ES18XX_DUPLEX_SAME | ES18XX_I2S | ES18XX_CONTROL | ES18XX_HWV;
+		break;
+	case 0x1879:
+		chip->caps = ES18XX_PCM2 | ES18XX_SPATIALIZER | ES18XX_RECMIX | ES18XX_NEW_RATE | ES18XX_AUXB | ES18XX_I2S | ES18XX_CONTROL | ES18XX_HWV;
+		break;
+	case 0x1887:
+		chip->caps = ES18XX_PCM2 | ES18XX_RECMIX | ES18XX_AUXB | ES18XX_DUPLEX_SAME | ES18XX_HWV;
+		break;
+	case 0x1888:
+		chip->caps = ES18XX_PCM2 | ES18XX_RECMIX | ES18XX_AUXB | ES18XX_DUPLEX_SAME | ES18XX_HWV;
+		break;
+	default:
+                snd_printk("[0x%lx] unsupported chip ES%x\n",
+                           chip->port, chip->version);
+                return -ENODEV;
+        }
+
+        snd_printd("[0x%lx] ESS%x chip found\n", chip->port, chip->version);
+
+        return snd_es18xx_initialize(chip);
+}
+
+#ifdef TARGET_OS2
+static snd_pcm_ops_t snd_es18xx_playback_ops = {
+/*	open:	  */	snd_es18xx_playback_open,
+/*	close:	  */	snd_es18xx_playback_close,
+/*	ioctl:	  */	snd_pcm_lib_ioctl,
+/*	hw_params:*/	snd_es18xx_playback_hw_params,
+        0,
+/*	prepare:  */	snd_es18xx_playback_prepare,
+/*	trigger:  */	snd_es18xx_playback_trigger,
+/*	pointer:  */	snd_es18xx_playback_pointer,
+        0, 0
+};
+
+static snd_pcm_ops_t snd_es18xx_capture_ops = {
+/*	open:	  */	snd_es18xx_capture_open,
+/*	close:	  */	snd_es18xx_capture_close,
+/*	ioctl:	  */	snd_pcm_lib_ioctl,
+/*	hw_params:*/	snd_es18xx_capture_hw_params,
+        0,
+/*	prepare:  */	snd_es18xx_capture_prepare,
+/*	trigger:  */	snd_es18xx_capture_trigger,
+/*	pointer:  */	snd_es18xx_capture_pointer,
+        0, 0
+};
+#else
+static snd_pcm_ops_t snd_es18xx_playback_ops = {
+	open:		snd_es18xx_playback_open,
+	close:		snd_es18xx_playback_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	hw_params:	snd_es18xx_playback_hw_params,
+	prepare:	snd_es18xx_playback_prepare,
+	trigger:	snd_es18xx_playback_trigger,
+	pointer:	snd_es18xx_playback_pointer,
+};
+
+static snd_pcm_ops_t snd_es18xx_capture_ops = {
+	open:		snd_es18xx_capture_open,
+	close:		snd_es18xx_capture_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	hw_params:	snd_es18xx_capture_hw_params,
+	prepare:	snd_es18xx_capture_prepare,
+	trigger:	snd_es18xx_capture_trigger,
+	pointer:	snd_es18xx_capture_pointer,
+};
+#endif
+
+static void snd_es18xx_pcm_free(snd_pcm_t *pcm)
+{
+	es18xx_t *codec = snd_magic_cast(es18xx_t, pcm->private_data, return);
+	codec->pcm = NULL;
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+int __init snd_es18xx_pcm(es18xx_t *chip, int device, snd_pcm_t ** rpcm)
+{
+        snd_pcm_t *pcm;
+	char str[16];
+	int err;
+
+	if (rpcm)
+		*rpcm = NULL;
+	sprintf(str, "ES%x", chip->version);
+	if (chip->caps & ES18XX_PCM2) {
+		err = snd_pcm_new(chip->card, str, device, 2, 1, &pcm);
+	} else {
+		err = snd_pcm_new(chip->card, str, device, 1, 1, &pcm);
+	}
+        if (err < 0)
+                return err;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_es18xx_playback_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_es18xx_capture_ops);
+
+	/* global setup */
+        pcm->private_data = chip;
+	pcm->private_free = snd_es18xx_pcm_free;
+        pcm->info_flags = 0;
+	if (chip->caps & ES18XX_DUPLEX_SAME)
+		pcm->info_flags |= SNDRV_PCM_INFO_JOINT_DUPLEX;
+	sprintf(pcm->name, "ESS AudioDrive ES%x", chip->version);
+        chip->pcm = pcm;
+
+	snd_pcm_lib_preallocate_pages_for_all(pcm, 64*1024, chip->dma1 > 3 || chip->dma2 > 3 ? 128*1024 : 64*1024, GFP_KERNEL|GFP_DMA);
+
+        if (rpcm)
+        	*rpcm = pcm;
+	return 0;
+}
+
+static int snd_es18xx_free(es18xx_t *chip)
+{
+	if (chip->res_port)
+		release_resource(chip->res_port);
+	if (chip->res_ctrl_port)
+		release_resource(chip->res_ctrl_port);
+	if (chip->res_mpu_port)
+		release_resource(chip->res_mpu_port);
+	if (chip->irq >= 0)
+		free_irq(chip->irq, (void *) chip);
+	if (chip->dma1 >= 0) {
+		disable_dma(chip->dma1);
+		free_dma(chip->dma1);
+	}
+	if (chip->dma2 >= 0) {
+		disable_dma(chip->dma2);
+		free_dma(chip->dma2);
+	}
+	snd_magic_kfree(chip);
+	return 0;
+}
+
+static int snd_es18xx_dev_free(snd_device_t *device)
+{
+	es18xx_t *chip = snd_magic_cast(es18xx_t, device->device_data, return -ENXIO);
+	return snd_es18xx_free(chip);
+}
+
+static int __init snd_es18xx_new_device(snd_card_t * card,
+					unsigned long port,
+					unsigned long mpu_port,
+					unsigned long fm_port,
+					int irq, int dma1, int dma2,
+					es18xx_t ** rchip)
+{
+        es18xx_t *chip;
+#ifdef TARGET_OS2
+	static snd_device_ops_t ops = {
+		snd_es18xx_dev_free,0,0
+        };
+#else
+	static snd_device_ops_t ops = {
+		dev_free:	snd_es18xx_dev_free,
+        };
+#endif
+	int err;
+
+	*rchip = NULL;
+        chip = snd_magic_kcalloc(es18xx_t, 0, GFP_KERNEL);
+	if (chip == NULL)
+		return -ENOMEM;
+	spin_lock_init(&chip->reg_lock);
+ 	spin_lock_init(&chip->mixer_lock);
+ 	spin_lock_init(&chip->ctrl_lock);
+        chip->card = card;
+        chip->port = port;
+        chip->mpu_port = mpu_port;
+        chip->fm_port = fm_port;
+        chip->irq = -1;
+        chip->dma1 = -1;
+        chip->dma2 = -1;
+        chip->audio2_vol = 0x00;
+	chip->active = 0;
+
+	if ((chip->res_port = request_region(port, 16, "ES18xx")) == NULL) {
+		snd_es18xx_free(chip);
+		snd_printk("unable to grap ports 0x%lx-0x%lx\n", port, port + 16 - 1);
+		return -EBUSY;
+	}
+
+	if (mpu_port > SNDRV_AUTO_PORT && (chip->res_mpu_port = request_region(mpu_port, 2, "ES18xx - MPU401")) == NULL) {
+		snd_es18xx_free(chip);
+		snd_printk("unable to grap MPU401 ports 0x%lx-0x%lx\n", mpu_port, mpu_port + 2 - 1);
+		return -EBUSY;
+	}
+
+	if (request_irq(irq, snd_es18xx_interrupt, SA_INTERRUPT, "ES18xx", (void *) chip)) {
+		snd_es18xx_free(chip);
+		snd_printk("unable to grap IRQ %d\n", irq);
+		return -EBUSY;
+	}
+	chip->irq = irq;
+
+	if (request_dma(dma1, "ES18xx DMA 1")) {
+		snd_es18xx_free(chip);
+		snd_printk("unable to grap DMA1 %d\n", dma1);
+		return -EBUSY;
+	}
+	chip->dma1 = dma1;
+
+	if (request_dma(dma2, "ES18xx DMA 2")) {
+		snd_es18xx_free(chip);
+		snd_printk("unable to grap DMA2 %d\n", dma2);
+		return -EBUSY;
+	}
+	chip->dma2 = dma2;
+
+        if (snd_es18xx_probe(chip) < 0) {
+                snd_es18xx_free(chip);
+                return -ENODEV;
+        }
+	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+		snd_es18xx_free(chip);
+		return err;
+	}
+        *rchip = chip;
+        return 0;
+}
+
+static int __init snd_es18xx_mixer(es18xx_t *chip)
+{
+	snd_card_t *card;
+	int err, idx;
+
+	card = chip->card;
+
+	strcpy(card->mixername, chip->pcm->name);
+
+	for (idx = 0; idx < sizeof(snd_es18xx_base_controls) / 
+		     sizeof(snd_es18xx_base_controls[0]); idx++) {
+		snd_kcontrol_t *kctl;
+		kctl = snd_ctl_new1(&snd_es18xx_base_controls[idx], chip);
+		if (chip->caps & ES18XX_HWV) {
+			switch (idx) {
+			case 0:
+				chip->master_volume = kctl;
+				kctl->private_free = snd_es18xx_hwv_free;
+				break;
+			case 1:
+				chip->master_switch = kctl;
+				kctl->private_free = snd_es18xx_hwv_free;
+				break;
+			}
+		}
+		if ((err = snd_ctl_add(card, kctl)) < 0)
+			return err;
+	}
+	if (chip->caps & ES18XX_PCM2) {
+		for (idx = 0; idx < sizeof(snd_es18xx_pcm2_controls) / 
+			     sizeof(snd_es18xx_pcm2_controls[0]); idx++) {
+			if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_pcm2_controls[idx], chip))) < 0)
+				return err;
+		} 
+	} else {
+		for (idx = 0; idx < sizeof(snd_es18xx_pcm1_controls) / 
+			     sizeof(snd_es18xx_pcm1_controls[0]); idx++) {
+			if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_pcm1_controls[idx], chip))) < 0)
+				return err;
+		}
+	}
+
+	if (chip->caps & ES18XX_MONO) {
+		if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_mono_in_control, chip))) < 0)
+			return err;
+	}
+	if (chip->caps & ES18XX_RECMIX) {
+		for (idx = 0; idx < sizeof(snd_es18xx_recmix_controls) / 
+			     sizeof(snd_es18xx_recmix_controls[0]); idx++) {
+			if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_recmix_controls[idx], chip))) < 0)
+				return err;
+		}
+	}
+	switch (chip->version) {
+	default:
+		if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_micpre1_control, chip))) < 0)
+			return err;
+	case 0x1869:
+	case 0x1879:
+		if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_micpre2_control, chip))) < 0)
+			return err;
+	}
+	if (chip->caps & ES18XX_SPATIALIZER) {
+		for (idx = 0; idx < sizeof(snd_es18xx_spatializer_controls) / 
+			     sizeof(snd_es18xx_spatializer_controls[0]); idx++) {
+			if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_spatializer_controls[idx], chip))) < 0)
+				return err;
+		}
+	}
+	if (chip->caps & ES18XX_HWV) {
+		for (idx = 0; idx < sizeof(snd_es18xx_hw_volume_controls) / 
+			     sizeof(snd_es18xx_hw_volume_controls[0]); idx++) {
+			snd_kcontrol_t *kctl;
+			kctl = snd_ctl_new1(&snd_es18xx_hw_volume_controls[idx], chip);
+			if (idx == 0)
+				chip->hw_volume = kctl;
+			else
+				chip->hw_switch = kctl;
+			kctl->private_free = snd_es18xx_hwv_free;
+			if ((err = snd_ctl_add(card, kctl)) < 0)
+				return err;
+			
+		}
+	}
+	return 0;
+}
+       
+
+/* Card level */
+
+EXPORT_NO_SYMBOLS;
+MODULE_DESCRIPTION("ESS ES18xx AudioDrive");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{ESS,ES1868 PnP AudioDrive},"
+		"{ESS,ES1869 PnP AudioDrive},"
+		"{ESS,ES1878 PnP AudioDrive},"
+		"{ESS,ES1879 PnP AudioDrive},"
+		"{ESS,ES1887 PnP AudioDrive},"
+		"{ESS,ES1888 PnP AudioDrive},"
+		"{ESS,ES1887 AudioDrive}"
+		"{ESS,ES1888 AudioDrive}}");
+
+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;	/* Enable this card */
+#ifdef __ISAPNP__
+#ifdef TARGET_OS2
+static int snd_isapnp[SNDRV_CARDS] = {1,1,1,1,1,1,1,1};
+#else
+static int snd_isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
+#endif
+#endif
+static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* 0x220,0x240,0x260,0x280 */
+#ifndef __ISAPNP__
+#ifdef TARGET_OS2
+static long snd_mpu_port[SNDRV_CARDS] = {-1,-1,-1,-1,-1,-1,-1,-1};
+#else
+static long snd_mpu_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -1};
+#endif
+#else
+static long snd_mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
+#endif
+static long snd_fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
+static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* 5,7,9,10 */
+static int snd_dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* 0,1,3 */
+static int snd_dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* 0,1,3 */
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for ES18xx soundcard.");
+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 ES18xx soundcard.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable ES18xx soundcard.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+#ifdef __ISAPNP__
+MODULE_PARM(snd_isapnp, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_isapnp, "ISA PnP detection for specified soundcard.");
+MODULE_PARM_SYNTAX(snd_isapnp, SNDRV_ISAPNP_DESC);
+#endif
+MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_port, "Port # for ES18xx driver.");
+MODULE_PARM_SYNTAX(snd_port, SNDRV_ENABLED ",allows:{{0x220,0x280,0x20}},prefers:{0x220},base:16,dialog:list");
+MODULE_PARM(snd_mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_mpu_port, "MPU-401 port # for ES18xx driver.");
+MODULE_PARM_SYNTAX(snd_mpu_port, SNDRV_ENABLED ",allows:{{0x300,0x330,0x30},{0x800,0xffe,0x2}},prefers:{0x330,0x300},base:16,dialog:combo");
+MODULE_PARM(snd_fm_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_fm_port, "FM port # for ES18xx driver.");
+MODULE_PARM_SYNTAX(snd_fm_port, SNDRV_ENABLED ",allows:{{0x388},{0x800,0xffc,0x4}},prefers:{0x388},base:16,dialog:combo");
+MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_irq, "IRQ # for ES18xx driver.");
+MODULE_PARM_SYNTAX(snd_irq, SNDRV_IRQ_DESC ",prefers:{5}");
+MODULE_PARM(snd_dma1, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_dma1, "DMA 1 # for ES18xx driver.");
+MODULE_PARM_SYNTAX(snd_dma1, SNDRV_DMA8_DESC ",prefers:{1}");
+MODULE_PARM(snd_dma2, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_dma2, "DMA 2 # for ES18xx driver.");
+MODULE_PARM_SYNTAX(snd_dma2, SNDRV_ENABLED ",allows:{{0},{1},{3},{5}},dialog:list,prefers:{0}");
+
+struct snd_audiodrive {
+#ifdef __ISAPNP__
+	struct isapnp_dev *dev;
+	struct isapnp_dev *devc;
+#endif
+};
+
+static snd_card_t *snd_audiodrive_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+
+#ifdef __ISAPNP__
+
+static struct isapnp_card *snd_audiodrive_isapnp_cards[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR;
+static const struct isapnp_card_id *snd_audiodrive_isapnp_id[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR;
+
+#ifdef TARGET_OS2
+#define ISAPNP_ES18XX(_va, _vb, _vc, _device, _audio, _control) \
+        { \
+                0, ISAPNP_CARD_ID(_va, _vb, _vc, _device), \
+                { ISAPNP_DEVICE_ID(_va, _vb, _vc, _audio), \
+                  ISAPNP_DEVICE_ID(_va, _vb, _vc, _control) } \
+        }
+#else
+#define ISAPNP_ES18XX(_va, _vb, _vc, _device, _audio, _control) \
+        { \
+                ISAPNP_CARD_ID(_va, _vb, _vc, _device), \
+                devs : { ISAPNP_DEVICE_ID(_va, _vb, _vc, _audio), \
+                         ISAPNP_DEVICE_ID(_va, _vb, _vc, _control) } \
+        }
+#endif
+
+static struct isapnp_card_id snd_audiodrive_pnpids[] __devinitdata = {
+	/* ESS 1868 (integrated on Compaq dual P-Pro motherboard and Genius 18PnP 3D) */
+	ISAPNP_ES18XX('E','S','S',0x1868,0x1868,0x0000),
+	/* ESS 1868 (integrated on Maxisound Cards) */
+	ISAPNP_ES18XX('E','S','S',0x1868,0x8601,0x8600),
+	/* ESS 1868 (integrated on Maxisound Cards) */
+	ISAPNP_ES18XX('E','S','S',0x1868,0x8611,0x8610),
+	/* ESS ES1869 Plug and Play AudioDrive */
+	ISAPNP_ES18XX('E','S','S',0x0003,0x1869,0x0006),
+	/* ESS 1869 */
+	ISAPNP_ES18XX('E','S','S',0x1869,0x1869,0x0006),
+	/* ESS 1878 */
+	ISAPNP_ES18XX('E','S','S',0x1878,0x1878,0x0004),
+	/* ESS 1879 */
+	ISAPNP_ES18XX('E','S','S',0x1879,0x1879,0x0009),
+	/* --- */
+	{ ISAPNP_CARD_END, } /* end */
+};
+
+ISAPNP_CARD_TABLE(snd_audiodrive_pnpids);
+
+static int __init snd_audiodrive_isapnp(int dev, struct snd_audiodrive *acard)
+{
+	const struct isapnp_card_id *id = snd_audiodrive_isapnp_id[dev];
+	struct isapnp_card *card = snd_audiodrive_isapnp_cards[dev];
+	struct isapnp_dev *pdev;
+
+	acard->dev = isapnp_find_dev(card, id->devs[0].vendor, id->devs[0].function, NULL);
+	if (acard->dev->active) {
+		acard->dev = NULL;
+		return -EBUSY;
+	}
+	acard->devc = isapnp_find_dev(card, id->devs[1].vendor, id->devs[1].function, NULL);
+	if (acard->devc->active) {
+		acard->dev = acard->devc = NULL;
+		return -EBUSY;
+	}
+	/* Control port initialization */
+	if (acard->devc->prepare(acard->devc)<0)
+		return -EAGAIN;
+	if (acard->devc->activate(acard->devc)<0) {
+		snd_printk("isapnp control configure failure (out of resources?)\n");
+		return -EAGAIN;
+	}
+	snd_printdd("isapnp: port=0x%lx\n", acard->devc->resource[0].start);
+	/* PnP initialization */
+	pdev = acard->dev;
+	if (pdev->prepare(pdev)<0) {
+		acard->devc->deactivate(acard->devc);
+		return -EAGAIN;
+	}
+	if (snd_port[dev] != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[0], snd_port[dev], 16);
+	if (snd_fm_port[dev] != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[1], snd_fm_port[dev], 4);
+	if (snd_mpu_port[dev] != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[2], snd_mpu_port[dev], 2);
+	if (snd_dma1[dev] != SNDRV_AUTO_DMA)
+		isapnp_resource_change(&pdev->dma_resource[0], snd_dma1[dev], 1);
+	if (snd_dma2[dev] != SNDRV_AUTO_DMA)
+		isapnp_resource_change(&pdev->dma_resource[1], snd_dma2[dev], 1);
+	if (snd_irq[dev] != SNDRV_AUTO_IRQ)
+		isapnp_resource_change(&pdev->irq_resource[0], snd_irq[dev], 1);
+	if (pdev->activate(pdev)<0) {
+		snd_printk("isapnp configure failure (out of resources?)\n");
+		acard->devc->deactivate(acard->devc);
+		return -EBUSY;
+	}
+	/* ok. hack using Vendor-Defined Card-Level registers */
+	/* skip csn and logdev initialization - already done in isapnp_configure */
+	isapnp_cfg_begin(pdev->bus->number, pdev->devfn);
+	isapnp_write_byte(0x27, pdev->irq_resource[0].start);	/* Hardware Volume IRQ Number */
+	if (snd_mpu_port[dev] > SNDRV_AUTO_PORT)
+		isapnp_write_byte(0x28, pdev->irq);		/* MPU-401 IRQ Number */
+	isapnp_write_byte(0x72, pdev->irq_resource[0].start);	/* second IRQ */
+	isapnp_cfg_end();
+	snd_port[dev] = pdev->resource[0].start;
+	snd_fm_port[dev] = pdev->resource[1].start;
+	snd_mpu_port[dev] = pdev->resource[2].start;
+	snd_dma1[dev] = pdev->dma_resource[0].start;
+	snd_dma2[dev] = pdev->dma_resource[1].start;
+	snd_irq[dev] = pdev->irq_resource[0].start;
+	snd_printdd("isapnp ES18xx: port=0x%lx, fm port=0x%lx, mpu port=0x%lx\n", snd_port[dev], snd_fm_port[dev], snd_mpu_port[dev]);
+	snd_printdd("isapnp ES18xx: dma1=%i, dma2=%i, irq=%i\n", snd_dma1[dev], snd_dma2[dev], snd_irq[dev]);
+	return 0;
+}
+
+static void snd_audiodrive_deactivate(struct snd_audiodrive *acard)
+{
+	if (acard->devc) {
+		acard->devc->deactivate(acard->devc);
+		acard->devc = NULL;
+	}
+	if (acard->dev) {
+		acard->dev->deactivate(acard->dev);
+		acard->dev = NULL;
+	}
+}
+#endif /* __ISAPNP__ */
+
+static void snd_audiodrive_free(snd_card_t *card)
+{
+	struct snd_audiodrive *acard = (struct snd_audiodrive *)card->private_data;
+
+	if (acard) {
+#ifdef __ISAPNP__
+		snd_audiodrive_deactivate(acard);
+#endif
+	}
+}
+
+static int __init snd_audiodrive_probe(int dev)
+{
+	static int possible_irqs[] = {5, 9, 10, 7, 11, 12, -1};
+	static int possible_dmas[] = {1, 0, 3, 5, -1};
+	int irq, dma1, dma2;
+	snd_card_t *card;
+	struct snd_audiodrive *acard;
+	snd_rawmidi_t *rmidi = NULL;
+	es18xx_t *chip;
+	opl3_t *opl3;
+	int err;
+
+	card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE,
+			    sizeof(struct snd_audiodrive));
+	if (card == NULL)
+		return -ENOMEM;
+	acard = (struct snd_audiodrive *)card->private_data;
+	card->private_free = snd_audiodrive_free;
+#ifdef __ISAPNP__
+	if (snd_isapnp[dev] && (err = snd_audiodrive_isapnp(dev, acard)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+#endif
+
+	irq = snd_irq[dev];
+	if (irq == SNDRV_AUTO_IRQ) {
+		if ((irq = snd_legacy_find_free_irq(possible_irqs)) < 0) {
+			snd_card_free(card);
+			snd_printk("unable to find a free IRQ\n");
+			return -EBUSY;
+		}
+	}
+        dma1 = snd_dma1[dev];
+        if (dma1 == SNDRV_AUTO_DMA) {
+                if ((dma1 = snd_legacy_find_free_dma(possible_dmas)) < 0) {
+                        snd_card_free(card);
+                        snd_printk("unable to find a free DMA1\n");
+                        return -EBUSY;
+                }
+        }
+        dma2 = snd_dma2[dev];
+        if (dma2 == SNDRV_AUTO_DMA) {
+                if ((dma2 = snd_legacy_find_free_dma(possible_dmas)) < 0) {
+                        snd_card_free(card);
+                        snd_printk("unable to find a free DMA2\n");
+                        return -EBUSY;
+                }
+        }
+
+	if ((err = snd_es18xx_new_device(card,
+					 snd_port[dev],
+					 snd_mpu_port[dev],
+					 snd_fm_port[dev],
+					 irq, dma1, dma2,
+					 &chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_es18xx_pcm(chip, 0, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_es18xx_mixer(chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	if (snd_opl3_create(card, chip->fm_port, chip->fm_port + 2, OPL3_HW_OPL3, 0, &opl3) < 0) {
+		snd_printk("opl3 not detected at 0x%lx\n", chip->port);
+	} else {
+		if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
+			snd_card_free(card);
+			return err;
+		}
+	}
+
+	if (snd_mpu_port[dev] > SNDRV_AUTO_PORT) {
+		if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_ES18XX,
+					       chip->mpu_port, 0,
+					       irq, 0,
+					       &rmidi)) < 0) {
+			snd_card_free(card);
+			return err;
+		}
+		chip->rmidi = rmidi;
+	}
+	sprintf(card->driver, "ES%x", chip->version);
+	sprintf(card->shortname, "ESS AudioDrive ES%x", chip->version);
+	sprintf(card->longname, "%s at 0x%lx, irq %d, dma1 %d, dma2 %d",
+		card->shortname,
+		chip->port,
+		irq, dma1, dma2);
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	snd_audiodrive_cards[dev] = card;
+	return 0;
+}
+
+static int __init snd_audiodrive_probe_legacy_port(unsigned long port)
+{
+	static int dev = 0;
+	int res;
+
+	for ( ; dev < SNDRV_CARDS; dev++) {
+		if (!snd_enable[dev] || snd_port[dev] != SNDRV_AUTO_PORT)
+			continue;
+#ifdef __ISAPNP__
+		if (snd_isapnp[dev])
+			continue;
+#endif
+		snd_port[dev] = port;
+		res = snd_audiodrive_probe(dev);
+		if (res < 0)
+			snd_port[dev] = SNDRV_AUTO_PORT;
+		return res;
+	}
+	return -ENODEV;
+}
+
+
+#ifdef __ISAPNP__
+static int __init snd_audiodrive_isapnp_detect(struct isapnp_card *card,
+					       const struct isapnp_card_id *id)
+{
+	static int dev = 0;
+	int res;
+
+	for ( ; dev < SNDRV_CARDS; dev++) {
+		if (!snd_enable[dev] || !snd_isapnp[dev])
+			continue;
+		snd_audiodrive_isapnp_cards[dev] = card;
+                snd_audiodrive_isapnp_id[dev] = id;
+                res = snd_audiodrive_probe(dev);
+		if (res < 0)
+			return res;
+		dev++;
+		return 0;
+        }
+
+        return -ENODEV;
+}
+#endif /* __ISAPNP__ */
+
+static int __init alsa_card_es18xx_init(void)
+{
+	static unsigned long possible_ports[] = {0x220, 0x240, 0x260, 0x280, -1};
+	int dev, cards = 0;
+
+	/* legacy non-auto cards at first */
+	for (dev = 0; dev < SNDRV_CARDS; dev++) {
+		if (!snd_enable[dev] || snd_port[dev] == SNDRV_AUTO_PORT)
+			continue;
+#ifdef __ISAPNP__
+		if (snd_isapnp[dev])
+			continue;
+#endif
+		if (snd_audiodrive_probe(dev) >= 0)
+			cards++;
+	}
+	/* legacy auto configured cards */
+	cards += snd_legacy_auto_probe(possible_ports, snd_audiodrive_probe_legacy_port);
+#ifdef __ISAPNP__
+	/* ISA PnP cards at last */
+	cards += isapnp_probe_cards(snd_audiodrive_pnpids, snd_audiodrive_isapnp_detect);
+#endif
+	if(!cards) {
+#ifdef MODULE
+		snd_printk("ESS AudioDrive ES18xx soundcard not found or device busy\n");
+#endif
+		return -ENODEV;
+	}
+	return 0;
+}
+
+static void __exit alsa_card_es18xx_exit(void)
+{
+	int idx;
+
+	for(idx = 0; idx < SNDRV_CARDS; idx++)
+		snd_card_free(snd_audiodrive_cards[idx]);
+}
+
+module_init(alsa_card_es18xx_init)
+module_exit(alsa_card_es18xx_exit)
+
+#ifndef MODULE
+
+/* format is: snd-card-es18xx=snd_enable,snd_index,snd_id,snd_isapnp,
+			      snd_port,snd_mpu_port,snd_fm_port,snd_irq,
+			      snd_dma1,snd_dma2 */
+
+static int __init alsa_card_es18xx_setup(char *str)
+{
+	static unsigned __initdata nr_dev = 0;
+	int __attribute__ ((__unused__)) pnp = INT_MAX;
+
+	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,&pnp) == 2 &&
+	       get_option(&str,(int *)&snd_port[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_mpu_port[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_fm_port[nr_dev]) == 2 &&
+	       get_option(&str,&snd_irq[nr_dev]) == 2 &&
+	       get_option(&str,&snd_dma1[nr_dev]) == 2 &&
+	       get_option(&str,&snd_dma2[nr_dev]) == 2);
+#ifdef __ISAPNP__
+	if (pnp != INT_MAX)
+		snd_isapnp[nr_dev] = pnp;
+#endif
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-card-es18xx=", alsa_card_es18xx_setup);
+
+#endif /* ifndef MODULE */
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/gus_dma.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/gus_dma.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/gus_dma.c	(revision 91)
@@ -0,0 +1,241 @@
+/*
+ *  Routines for GF1 DMA control
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <sound/driver.h>
+#include <sound/gus.h>
+
+void snd_gf1_dma_ack(snd_gus_card_t * gus)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	snd_gf1_write8(gus, SNDRV_GF1_GB_DRAM_DMA_CONTROL, 0x00);
+	snd_gf1_look8(gus, SNDRV_GF1_GB_DRAM_DMA_CONTROL);
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+}
+
+void snd_gf1_dma_program(snd_gus_card_t * gus,
+			 unsigned int addr,
+			 const void *buf,
+			 unsigned int count,
+			 unsigned int cmd)
+{
+	unsigned long flags;
+	unsigned int address;
+	unsigned char dma_cmd;
+	unsigned int address_high;
+
+	// snd_printk("dma_transfer: addr=0x%x, buf=0x%lx, count=0x%x\n", addr, (long) buf, count);
+
+	if (gus->gf1.dma1 > 3) {
+		if (gus->gf1.enh_mode) {
+			address = addr >> 1;
+		} else {
+			if (addr & 0x1f) {
+				snd_printd("snd_gf1_dma_transfer: unaligned address (0x%x)?\n", addr);
+				return;
+			}
+			address = (addr & 0x000c0000) | ((addr & 0x0003ffff) >> 1);
+		}
+	} else {
+		address = addr;
+	}
+
+	dma_cmd = SNDRV_GF1_DMA_ENABLE | (unsigned short) cmd;
+#if 0
+	dma_cmd |= 0x08;
+#endif
+	if (dma_cmd & SNDRV_GF1_DMA_16BIT) {
+		count++;
+		count &= ~1;	/* align */
+	}
+	if (gus->gf1.dma1 > 3) {
+		dma_cmd |= SNDRV_GF1_DMA_WIDTH16;
+		count++;
+		count &= ~1;	/* align */
+	}
+	snd_gf1_dma_ack(gus);
+	snd_dma_program(gus->gf1.dma1, buf, count, dma_cmd & SNDRV_GF1_DMA_READ ? DMA_MODE_READ : DMA_MODE_WRITE);
+#if 0
+	snd_printk("address = 0x%x, count = 0x%x, dma_cmd = 0x%x\n", address << 1, count, dma_cmd);
+#endif
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	if (gus->gf1.enh_mode) {
+		address_high = ((address >> 16) & 0x000000f0) | (address & 0x0000000f);
+		snd_gf1_write16(gus, SNDRV_GF1_GW_DRAM_DMA_LOW, (unsigned short) (address >> 4));
+		snd_gf1_write8(gus, SNDRV_GF1_GB_DRAM_DMA_HIGH, (unsigned char) address_high);
+	} else
+		snd_gf1_write16(gus, SNDRV_GF1_GW_DRAM_DMA_LOW, (unsigned short) (address >> 4));
+	snd_gf1_write8(gus, SNDRV_GF1_GB_DRAM_DMA_CONTROL, dma_cmd);
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+}
+
+static snd_gf1_dma_block_t *snd_gf1_dma_next_block(snd_gus_card_t * gus)
+{
+	snd_gf1_dma_block_t *block;
+
+	/* PCM block have bigger priority than synthesizer one */
+	if (gus->gf1.dma_data_pcm) {
+		block = gus->gf1.dma_data_pcm;
+		if (gus->gf1.dma_data_pcm_last == block) {
+			gus->gf1.dma_data_pcm =
+			gus->gf1.dma_data_pcm_last = NULL;
+		} else {
+			gus->gf1.dma_data_pcm = block->next;
+		}
+	} else if (gus->gf1.dma_data_synth) {
+		block = gus->gf1.dma_data_synth;
+		if (gus->gf1.dma_data_synth_last == block) {
+			gus->gf1.dma_data_synth =
+			gus->gf1.dma_data_synth_last = NULL;
+		} else {
+			gus->gf1.dma_data_synth = block->next;
+		}
+	} else {
+		block = NULL;
+	}
+	if (block) {
+		gus->gf1.dma_ack = block->ack;
+		gus->gf1.dma_private_data = block->private_data;
+	}
+	return block;
+}
+
+
+static void snd_gf1_dma_interrupt(snd_gus_card_t * gus)
+{
+	snd_gf1_dma_block_t *block;
+
+	snd_gf1_dma_ack(gus);
+	if (gus->gf1.dma_ack)
+		gus->gf1.dma_ack(gus, gus->gf1.dma_private_data);
+	spin_lock(&gus->dma_lock);
+	if (gus->gf1.dma_data_pcm == NULL &&
+	    gus->gf1.dma_data_synth == NULL) {
+	    	gus->gf1.dma_ack = NULL;
+		gus->gf1.dma_flags &= ~SNDRV_GF1_DMA_TRIGGER;
+		spin_unlock(&gus->dma_lock);
+		return;
+	}
+	block = snd_gf1_dma_next_block(gus);
+	spin_unlock(&gus->dma_lock);
+	snd_gf1_dma_program(gus, block->addr, block->buffer, block->count, (unsigned short) block->cmd);
+	kfree(block);
+#if 0
+	printk("program dma (IRQ) - addr = 0x%x, buffer = 0x%lx, count = 0x%x, cmd = 0x%x\n", addr, (long) buffer, count, cmd);
+#endif
+}
+
+int snd_gf1_dma_init(snd_gus_card_t * gus)
+{
+	down(&gus->dma_mutex);
+	gus->gf1.dma_shared++;
+	if (gus->gf1.dma_shared > 1) {
+		up(&gus->dma_mutex);
+		return 0;
+	}
+	gus->gf1.interrupt_handler_dma_write = snd_gf1_dma_interrupt;
+	gus->gf1.dma_data_pcm = 
+	gus->gf1.dma_data_pcm_last =
+	gus->gf1.dma_data_synth = 
+	gus->gf1.dma_data_synth_last = NULL;
+	up(&gus->dma_mutex);
+	return 0;
+}
+
+int snd_gf1_dma_done(snd_gus_card_t * gus)
+{
+	snd_gf1_dma_block_t *block;
+
+	down(&gus->dma_mutex);
+	gus->gf1.dma_shared--;
+	if (!gus->gf1.dma_shared) {
+		snd_dma_disable(gus->gf1.dma1);
+		snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_DMA_WRITE);
+		snd_gf1_dma_ack(gus);
+		while ((block = gus->gf1.dma_data_pcm)) {
+			gus->gf1.dma_data_pcm = block->next;
+			kfree(block);
+		}
+		while ((block = gus->gf1.dma_data_synth)) {
+			gus->gf1.dma_data_synth = block->next;
+			kfree(block);
+		}
+		gus->gf1.dma_data_pcm_last =
+		gus->gf1.dma_data_synth_last = NULL;
+	}
+	up(&gus->dma_mutex);
+	return 0;
+}
+
+int snd_gf1_dma_transfer_block(snd_gus_card_t * gus,
+			       snd_gf1_dma_block_t * __block,
+			       int atomic,
+			       int synth)
+{
+	unsigned long flags;
+	snd_gf1_dma_block_t *block;
+
+	block = kmalloc(sizeof(*block), atomic ? GFP_ATOMIC : GFP_KERNEL);
+	if (block == NULL) {
+		snd_printk("gf1: DMA transfer failure; not enough memory\n");
+		return -ENOMEM;
+	}
+	*block = *__block;
+	block->next = NULL;
+#if 0
+	printk("addr = 0x%x, buffer = 0x%lx, count = 0x%x, cmd = 0x%x\n", block->addr, (long) block->buffer, block->count, block->cmd);
+#endif
+#if 0
+	printk("gus->gf1.dma_data_pcm_last = 0x%lx\n", (long)gus->gf1.dma_data_pcm_last);
+	printk("gus->gf1.dma_data_pcm = 0x%lx\n", (long)gus->gf1.dma_data_pcm);
+#endif
+	spin_lock_irqsave(&gus->dma_lock, flags);
+	if (synth) {
+		if (gus->gf1.dma_data_synth_last) {
+			gus->gf1.dma_data_synth_last->next = block;
+			gus->gf1.dma_data_synth_last = block;
+		} else {
+			gus->gf1.dma_data_synth = 
+			gus->gf1.dma_data_synth_last = block;
+		}
+	} else {
+		if (gus->gf1.dma_data_pcm_last) {
+			gus->gf1.dma_data_pcm_last->next = block;
+			gus->gf1.dma_data_pcm_last = block;
+		} else {
+			gus->gf1.dma_data_pcm = 
+			gus->gf1.dma_data_pcm_last = block;
+		}
+	}
+	if (!(gus->gf1.dma_flags & SNDRV_GF1_DMA_TRIGGER)) {
+		gus->gf1.dma_flags |= SNDRV_GF1_DMA_TRIGGER;
+		block = snd_gf1_dma_next_block(gus);
+		spin_unlock_irqrestore(&gus->dma_lock, flags);
+		if (block == NULL)
+			return 0;
+		snd_gf1_dma_program(gus, block->addr, block->buffer, block->count, (unsigned short) block->cmd);
+		kfree(block);
+		return 0;
+	}
+	spin_unlock_irqrestore(&gus->dma_lock, flags);
+	return 0;
+}
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/gus_dram.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/gus_dram.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/gus_dram.c	(revision 91)
@@ -0,0 +1,101 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  DRAM access routines
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <sound/driver.h>
+#include <sound/gus.h>
+#include <sound/info.h>
+
+
+static int snd_gus_dram_poke(snd_gus_card_t *gus, char *_buffer,
+			     unsigned int address, unsigned int size)
+{
+	unsigned long flags;
+	unsigned int size1, size2;
+	char buffer[512], *pbuffer;
+
+	while (size > 0) {
+		if (copy_from_user(buffer, _buffer, 512))
+			return -EFAULT;
+		size1 = size > 512 ? 512 : size;
+		if (gus->interwave) {
+			spin_lock_irqsave(&gus->reg_lock, flags);
+			snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x01);
+			snd_gf1_dram_addr(gus, address);
+			outsb(GUSP(gus, DRAM), buffer, size1);
+			spin_unlock_irqrestore(&gus->reg_lock, flags);
+			address += size1;
+		} else {
+			pbuffer = buffer;
+			size2 = size1;
+			while (size2--)
+				snd_gf1_poke(gus, address++, *pbuffer++);
+		}
+		size -= size1;
+		_buffer += size1;
+	}
+	return 0;
+}
+
+
+int snd_gus_dram_write(snd_gus_card_t *gus, char *buffer,
+		       unsigned int address, unsigned int size)
+{
+	return snd_gus_dram_poke(gus, buffer, address, size);
+}
+
+static int snd_gus_dram_peek(snd_gus_card_t *gus, char *_buffer,
+			     unsigned int address, unsigned int size,
+			     int rom)
+{
+	unsigned long flags;
+	unsigned int size1, size2;
+	char buffer[512], *pbuffer;
+
+	while (size > 0) {
+		size1 = size > 512 ? 512 : size;
+		if (gus->interwave) {
+			spin_lock_irqsave(&gus->reg_lock, flags);
+			snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, rom ? 0x03 : 0x01);
+			snd_gf1_dram_addr(gus, address);
+			insb(GUSP(gus, DRAM), buffer, size1);
+			snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x01);
+			spin_unlock_irqrestore(&gus->reg_lock, flags);
+			address += size1;
+		} else {
+			pbuffer = buffer;
+			size2 = size1;
+			while (size2--)
+				*pbuffer++ = snd_gf1_peek(gus, address++);
+		}
+		if (copy_to_user(_buffer, buffer, size1))
+			return -EFAULT;
+		size -= size1;
+		_buffer += size1;
+	}
+	return 0;
+}
+
+int snd_gus_dram_read(snd_gus_card_t *gus, char *buffer,
+		      unsigned int address, unsigned int size,
+		      int rom)
+{
+	return snd_gus_dram_peek(gus, buffer, address, size, rom);
+}
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/gus_instr.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/gus_instr.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/gus_instr.c	(revision 91)
@@ -0,0 +1,171 @@
+/*
+ *  Routines for Gravis UltraSound soundcards - Synthesizer
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <sound/driver.h>
+#include <sound/gus.h>
+
+/*
+ *
+ */
+
+int snd_gus_iwffff_put_sample(void *private_data, iwffff_wave_t *wave,
+			      char *data, long len, int atomic)
+{
+	snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, private_data, return -ENXIO);
+	snd_gf1_mem_block_t *block;
+	int err;
+
+	if (wave->format & IWFFFF_WAVE_ROM)
+		return 0;	/* it's probably ok - verify the address? */
+	if (wave->format & IWFFFF_WAVE_STEREO)
+		return -EINVAL;	/* not supported */
+	block = snd_gf1_mem_alloc(&gus->gf1.mem_alloc,
+				  SNDRV_GF1_MEM_OWNER_WAVE_IWFFFF,
+				  NULL, wave->size,
+				  wave->format & IWFFFF_WAVE_16BIT, 1,
+				  wave->share_id);
+	if (block == NULL)
+		return -ENOMEM;
+	err = snd_gus_dram_write(gus, data,
+				 block->ptr, wave->size);
+	if (err < 0) {
+		snd_gf1_mem_lock(&gus->gf1.mem_alloc, 0);
+		snd_gf1_mem_xfree(&gus->gf1.mem_alloc, block);
+		snd_gf1_mem_lock(&gus->gf1.mem_alloc, 1);
+		return err;
+	}
+	wave->address.memory = block->ptr;
+	return 0;
+}
+
+int snd_gus_iwffff_get_sample(void *private_data, iwffff_wave_t *wave,
+			      char *data, long len, int atomic)
+{
+	snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, private_data, return -ENXIO);
+
+	return snd_gus_dram_read(gus, data, wave->address.memory, wave->size,
+				 wave->format & IWFFFF_WAVE_ROM ? 1 : 0);
+}
+
+int snd_gus_iwffff_remove_sample(void *private_data, iwffff_wave_t *wave,
+				 int atomic)
+{
+	snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, private_data, return -ENXIO);
+
+	if (wave->format & IWFFFF_WAVE_ROM)
+		return 0;	/* it's probably ok - verify the address? */	
+	return snd_gf1_mem_free(&gus->gf1.mem_alloc, wave->address.memory);
+}
+
+/*
+ *
+ */
+
+int snd_gus_gf1_put_sample(void *private_data, gf1_wave_t *wave,
+			   char *data, long len, int atomic)
+{
+	snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, private_data, return -ENXIO);
+	snd_gf1_mem_block_t *block;
+	int err;
+
+	if (wave->format & GF1_WAVE_STEREO)
+		return -EINVAL;	/* not supported */
+	block = snd_gf1_mem_alloc(&gus->gf1.mem_alloc,
+				  SNDRV_GF1_MEM_OWNER_WAVE_GF1,
+				  NULL, wave->size,
+				  wave->format & GF1_WAVE_16BIT, 1,
+				  wave->share_id);
+	if (block == NULL)
+		return -ENOMEM;
+	err = snd_gus_dram_write(gus, data,
+				 block->ptr, wave->size);
+	if (err < 0) {
+		snd_gf1_mem_lock(&gus->gf1.mem_alloc, 0);
+		snd_gf1_mem_xfree(&gus->gf1.mem_alloc, block);
+		snd_gf1_mem_lock(&gus->gf1.mem_alloc, 1);
+		return err;
+	}
+	wave->address.memory = block->ptr;
+	return 0;
+}
+
+int snd_gus_gf1_get_sample(void *private_data, gf1_wave_t *wave,
+			   char *data, long len, int atomic)
+{
+	snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, private_data, return -ENXIO);
+
+	return snd_gus_dram_read(gus, data, wave->address.memory, wave->size, 0);
+}
+
+int snd_gus_gf1_remove_sample(void *private_data, gf1_wave_t *wave,
+			      int atomic)
+{
+	snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, private_data, return -ENXIO);
+
+	return snd_gf1_mem_free(&gus->gf1.mem_alloc, wave->address.memory);
+}
+
+/*
+ *
+ */
+
+int snd_gus_simple_put_sample(void *private_data, simple_instrument_t *instr,
+			      char *data, long len, int atomic)
+{
+	snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, private_data, return -ENXIO);
+	snd_gf1_mem_block_t *block;
+	int err;
+
+	if (instr->format & SIMPLE_WAVE_STEREO)
+		return -EINVAL;	/* not supported */
+	block = snd_gf1_mem_alloc(&gus->gf1.mem_alloc,
+				  SNDRV_GF1_MEM_OWNER_WAVE_SIMPLE,
+				  NULL, instr->size,
+				  instr->format & SIMPLE_WAVE_16BIT, 1,
+				  instr->share_id);
+	if (block == NULL)
+		return -ENOMEM;
+	err = snd_gus_dram_write(gus, data, block->ptr, instr->size);
+	if (err < 0) {
+		snd_gf1_mem_lock(&gus->gf1.mem_alloc, 0);
+		snd_gf1_mem_xfree(&gus->gf1.mem_alloc, block);
+		snd_gf1_mem_lock(&gus->gf1.mem_alloc, 1);
+		return err;
+	}
+	instr->address.memory = block->ptr;
+	return 0;
+}
+
+int snd_gus_simple_get_sample(void *private_data, simple_instrument_t *instr,
+			      char *data, long len, int atomic)
+{
+	snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, private_data, return -ENXIO);
+
+	return snd_gus_dram_read(gus, data, instr->address.memory, instr->size, 0);
+}
+
+int snd_gus_simple_remove_sample(void *private_data, simple_instrument_t *instr,
+			         int atomic)
+{
+	snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, private_data, return -ENXIO);
+
+	return snd_gf1_mem_free(&gus->gf1.mem_alloc, instr->address.memory);
+}
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/gus_io.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/gus_io.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/gus_io.c	(revision 91)
@@ -0,0 +1,528 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  I/O routines for GF1/InterWave synthesizer chips
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <sound/driver.h>
+#include <sound/gus.h>
+
+void snd_gf1_delay(snd_gus_card_t * gus)
+{
+	int i;
+
+	for (i = 0; i < 6; i++) {
+		mb();
+		inb(GUSP(gus, DRAM));
+	}
+}
+
+/*
+ *  =======================================================================
+ */
+
+/*
+ *  ok.. stop of control registers (wave & ramp) need some special things..
+ *       big UltraClick (tm) elimination...
+ */
+
+static inline void __snd_gf1_ctrl_stop(snd_gus_card_t * gus, unsigned char reg)
+{
+	unsigned char value;
+
+	outb(reg | 0x80, gus->gf1.reg_regsel);
+	mb();
+	value = inb(gus->gf1.reg_data8);
+	mb();
+	outb(reg, gus->gf1.reg_regsel);
+	mb();
+	outb((value | 0x03) & ~(0x80 | 0x20), gus->gf1.reg_data8);
+	mb();
+}
+
+static inline void __snd_gf1_write8(snd_gus_card_t * gus,
+				    unsigned char reg,
+				    unsigned char data)
+{
+	outb(reg, gus->gf1.reg_regsel);
+	mb();
+	outb(data, gus->gf1.reg_data8);
+	mb();
+}
+
+static inline unsigned char __snd_gf1_look8(snd_gus_card_t * gus,
+					    unsigned char reg)
+{
+	outb(reg, gus->gf1.reg_regsel);
+	mb();
+	return inb(gus->gf1.reg_data8);
+}
+
+static inline void __snd_gf1_write16(snd_gus_card_t * gus,
+				     unsigned char reg, unsigned int data)
+{
+	outb(reg, gus->gf1.reg_regsel);
+	mb();
+	outw((unsigned short) data, gus->gf1.reg_data16);
+	mb();
+}
+
+static inline unsigned short __snd_gf1_look16(snd_gus_card_t * gus,
+					      unsigned char reg)
+{
+	outb(reg, gus->gf1.reg_regsel);
+	mb();
+	return inw(gus->gf1.reg_data16);
+}
+
+static inline void __snd_gf1_adlib_write(snd_gus_card_t * gus,
+					 unsigned char reg, unsigned char data)
+{
+	outb(reg, gus->gf1.reg_timerctrl);
+	inb(gus->gf1.reg_timerctrl);
+	inb(gus->gf1.reg_timerctrl);
+	outb(data, gus->gf1.reg_timerdata);
+	inb(gus->gf1.reg_timerctrl);
+	inb(gus->gf1.reg_timerctrl);
+}
+
+static inline void __snd_gf1_write_addr(snd_gus_card_t * gus, unsigned char reg,
+                                        unsigned int addr, int w_16bit)
+{
+	if (gus->gf1.enh_mode) {
+		if (w_16bit)
+			addr = ((addr >> 1) & ~0x0000000f) | (addr & 0x0000000f);
+		__snd_gf1_write8(gus, SNDRV_GF1_VB_UPPER_ADDRESS, (unsigned char) ((addr >> 26) & 0x03));
+	} else if (w_16bit)
+		addr = (addr & 0x00c0000f) | ((addr & 0x003ffff0) >> 1);
+	__snd_gf1_write16(gus, reg, (unsigned short) (addr >> 11));
+	__snd_gf1_write16(gus, reg + 1, (unsigned short) (addr << 5));
+}
+
+static inline unsigned int __snd_gf1_read_addr(snd_gus_card_t * gus,
+					       unsigned char reg, short w_16bit)
+{
+	unsigned int res;
+
+	res = ((unsigned int) __snd_gf1_look16(gus, reg | 0x80) << 11) & 0xfff800;
+	res |= ((unsigned int) __snd_gf1_look16(gus, (reg + 1) | 0x80) >> 5) & 0x0007ff;
+	if (gus->gf1.enh_mode) {
+		res |= (unsigned int) __snd_gf1_look8(gus, SNDRV_GF1_VB_UPPER_ADDRESS | 0x80) << 26;
+		if (w_16bit)
+			res = ((res << 1) & 0xffffffe0) | (res & 0x0000000f);
+	} else if (w_16bit)
+		res = ((res & 0x001ffff0) << 1) | (res & 0x00c0000f);
+	return res;
+}
+
+
+/*
+ *  =======================================================================
+ */
+
+void snd_gf1_ctrl_stop(snd_gus_card_t * gus, unsigned char reg)
+{
+	__snd_gf1_ctrl_stop(gus, reg);
+}
+
+void snd_gf1_write8(snd_gus_card_t * gus,
+		    unsigned char reg,
+		    unsigned char data)
+{
+	__snd_gf1_write8(gus, reg, data);
+}
+
+unsigned char snd_gf1_look8(snd_gus_card_t * gus, unsigned char reg)
+{
+	return __snd_gf1_look8(gus, reg);
+}
+
+void snd_gf1_write16(snd_gus_card_t * gus,
+		     unsigned char reg,
+		     unsigned int data)
+{
+	__snd_gf1_write16(gus, reg, data);
+}
+
+unsigned short snd_gf1_look16(snd_gus_card_t * gus, unsigned char reg)
+{
+	return __snd_gf1_look16(gus, reg);
+}
+
+void snd_gf1_adlib_write(snd_gus_card_t * gus,
+                         unsigned char reg,
+                         unsigned char data)
+{
+	__snd_gf1_adlib_write(gus, reg, data);
+}
+
+void snd_gf1_write_addr(snd_gus_card_t * gus, unsigned char reg,
+                        unsigned int addr, short w_16bit)
+{
+	__snd_gf1_write_addr(gus, reg, addr, w_16bit);
+}
+
+unsigned int snd_gf1_read_addr(snd_gus_card_t * gus,
+                               unsigned char reg,
+                               short w_16bit)
+{
+	return __snd_gf1_read_addr(gus, reg, w_16bit);
+}
+
+/*
+
+ */
+
+void snd_gf1_i_ctrl_stop(snd_gus_card_t * gus, unsigned char reg)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	__snd_gf1_ctrl_stop(gus, reg);
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+}
+
+void snd_gf1_i_write8(snd_gus_card_t * gus,
+		      unsigned char reg,
+                      unsigned char data)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	__snd_gf1_write8(gus, reg, data);
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+}
+
+unsigned char snd_gf1_i_look8(snd_gus_card_t * gus, unsigned char reg)
+{
+	unsigned long flags;
+	unsigned char res;
+
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	res = __snd_gf1_look8(gus, reg);
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+	return res;
+}
+
+void snd_gf1_i_write16(snd_gus_card_t * gus,
+		       unsigned char reg,
+		       unsigned int data)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	__snd_gf1_write16(gus, reg, data);
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+}
+
+unsigned short snd_gf1_i_look16(snd_gus_card_t * gus, unsigned char reg)
+{
+	unsigned long flags;
+	unsigned short res;
+
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	res = __snd_gf1_look16(gus, reg);
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+	return res;
+}
+
+void snd_gf1_i_adlib_write(snd_gus_card_t * gus,
+		           unsigned char reg,
+		           unsigned char data)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	__snd_gf1_adlib_write(gus, reg, data);
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+}
+
+void snd_gf1_i_write_addr(snd_gus_card_t * gus, unsigned char reg,
+			  unsigned int addr, short w_16bit)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	__snd_gf1_write_addr(gus, reg, addr, w_16bit);
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+}
+
+unsigned int snd_gf1_i_read_addr(snd_gus_card_t * gus,
+				 unsigned char reg, short w_16bit)
+{
+	unsigned int res;
+	unsigned long flags;
+
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	res = __snd_gf1_read_addr(gus, reg, w_16bit);
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+	return res;
+}
+
+/*
+
+ */
+
+void snd_gf1_dram_addr(snd_gus_card_t * gus, unsigned int addr)
+{
+	outb(0x43, gus->gf1.reg_regsel);
+	mb();
+	outw((unsigned short) addr, gus->gf1.reg_data16);
+	mb();
+	outb(0x44, gus->gf1.reg_regsel);
+	mb();
+	outb((unsigned char) (addr >> 16), gus->gf1.reg_data8);
+	mb();
+}
+
+void snd_gf1_poke(snd_gus_card_t * gus, unsigned int addr, unsigned char data)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	outb(SNDRV_GF1_GW_DRAM_IO_LOW, gus->gf1.reg_regsel);
+	mb();
+	outw((unsigned short) addr, gus->gf1.reg_data16);
+	mb();
+	outb(SNDRV_GF1_GB_DRAM_IO_HIGH, gus->gf1.reg_regsel);
+	mb();
+	outb((unsigned char) (addr >> 16), gus->gf1.reg_data8);
+	mb();
+	outb(data, gus->gf1.reg_dram);
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+}
+
+unsigned char snd_gf1_peek(snd_gus_card_t * gus, unsigned int addr)
+{
+	unsigned long flags;
+	unsigned char res;
+
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	outb(SNDRV_GF1_GW_DRAM_IO_LOW, gus->gf1.reg_regsel);
+	mb();
+	outw((unsigned short) addr, gus->gf1.reg_data16);
+	mb();
+	outb(SNDRV_GF1_GB_DRAM_IO_HIGH, gus->gf1.reg_regsel);
+	mb();
+	outb((unsigned char) (addr >> 16), gus->gf1.reg_data8);
+	mb();
+	res = inb(gus->gf1.reg_dram);
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+	return res;
+}
+
+void snd_gf1_pokew(snd_gus_card_t * gus, unsigned int addr, unsigned short data)
+{
+	unsigned long flags;
+
+#ifdef CONFIG_SND_DEBUG
+	if (!gus->interwave)
+		snd_printk("snd_gf1_pokew - GF1!!!\n");
+#endif
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	outb(SNDRV_GF1_GW_DRAM_IO_LOW, gus->gf1.reg_regsel);
+	mb();
+	outw((unsigned short) addr, gus->gf1.reg_data16);
+	mb();
+	outb(SNDRV_GF1_GB_DRAM_IO_HIGH, gus->gf1.reg_regsel);
+	mb();
+	outb((unsigned char) (addr >> 16), gus->gf1.reg_data8);
+	mb();
+	outb(SNDRV_GF1_GW_DRAM_IO16, gus->gf1.reg_regsel);
+	mb();
+	outw(data, gus->gf1.reg_data16);
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+}
+
+unsigned short snd_gf1_peekw(snd_gus_card_t * gus, unsigned int addr)
+{
+	unsigned long flags;
+	unsigned short res;
+
+#ifdef CONFIG_SND_DEBUG
+	if (!gus->interwave)
+		snd_printk("snd_gf1_peekw - GF1!!!\n");
+#endif
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	outb(SNDRV_GF1_GW_DRAM_IO_LOW, gus->gf1.reg_regsel);
+	mb();
+	outw((unsigned short) addr, gus->gf1.reg_data16);
+	mb();
+	outb(SNDRV_GF1_GB_DRAM_IO_HIGH, gus->gf1.reg_regsel);
+	mb();
+	outb((unsigned char) (addr >> 16), gus->gf1.reg_data8);
+	mb();
+	outb(SNDRV_GF1_GW_DRAM_IO16, gus->gf1.reg_regsel);
+	mb();
+	res = inw(gus->gf1.reg_data16);
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+	return res;
+}
+
+void snd_gf1_dram_setmem(snd_gus_card_t * gus, unsigned int addr,
+			 unsigned short value, unsigned int count)
+{
+	unsigned long port;
+	unsigned long flags;
+
+#ifdef CONFIG_SND_DEBUG
+	if (!gus->interwave)
+		snd_printk("snd_gf1_dram_setmem - GF1!!!\n");
+#endif
+	addr &= ~1;
+	count >>= 1;
+	port = GUSP(gus, GF1DATALOW);
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	outb(SNDRV_GF1_GW_DRAM_IO_LOW, gus->gf1.reg_regsel);
+	mb();
+	outw((unsigned short) addr, gus->gf1.reg_data16);
+	mb();
+	outb(SNDRV_GF1_GB_DRAM_IO_HIGH, gus->gf1.reg_regsel);
+	mb();
+	outb((unsigned char) (addr >> 16), gus->gf1.reg_data8);
+	mb();
+	outb(SNDRV_GF1_GW_DRAM_IO16, gus->gf1.reg_regsel);
+	while (count--)
+		outw(value, port);
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+}
+
+/*
+
+ */
+
+void snd_gf1_select_active_voices(snd_gus_card_t * gus)
+{
+	unsigned short voices;
+
+	static unsigned short voices_tbl[32 - 14 + 1] =
+	{
+	    44100, 41160, 38587, 36317, 34300, 32494, 30870, 29400, 28063, 26843,
+	    25725, 24696, 23746, 22866, 22050, 21289, 20580, 19916, 19293
+	};
+
+	voices = gus->gf1.active_voices;
+	if (voices > 32)
+		voices = 32;
+	if (voices < 14)
+		voices = 14;
+	if (gus->gf1.enh_mode)
+		voices = 32;
+	gus->gf1.active_voices = voices;
+	gus->gf1.playback_freq =
+	    gus->gf1.enh_mode ? 44100 : voices_tbl[voices - 14];
+	if (!gus->gf1.enh_mode) {
+		snd_gf1_i_write8(gus, SNDRV_GF1_GB_ACTIVE_VOICES, 0xc0 | (voices - 1));
+		udelay(100);
+	}
+}
+
+#ifdef CONFIG_SND_DEBUG
+
+void snd_gf1_print_voice_registers(snd_gus_card_t * gus)
+{
+	unsigned char mode;
+	int voice, ctrl;
+
+	voice = gus->gf1.active_voice;
+	printk(" -%i- GF1  voice ctrl, ramp ctrl  = 0x%x, 0x%x\n", voice, ctrl = snd_gf1_i_read8(gus, 0), snd_gf1_i_read8(gus, 0x0d));
+	printk(" -%i- GF1  frequency              = 0x%x\n", voice, snd_gf1_i_read16(gus, 1));
+	printk(" -%i- GF1  loop start, end        = 0x%x (0x%x), 0x%x (0x%x)\n", voice, snd_gf1_i_read_addr(gus, 2, ctrl & 4), snd_gf1_i_read_addr(gus, 2, (ctrl & 4) ^ 4), snd_gf1_i_read_addr(gus, 4, ctrl & 4), snd_gf1_i_read_addr(gus, 4, (ctrl & 4) ^ 4));
+	printk(" -%i- GF1  ramp start, end, rate  = 0x%x, 0x%x, 0x%x\n", voice, snd_gf1_i_read8(gus, 7), snd_gf1_i_read8(gus, 8), snd_gf1_i_read8(gus, 6));
+	printk(" -%i- GF1  volume                 = 0x%x\n", voice, snd_gf1_i_read16(gus, 9));
+	printk(" -%i- GF1  position               = 0x%x (0x%x)\n", voice, snd_gf1_i_read_addr(gus, 0x0a, ctrl & 4), snd_gf1_i_read_addr(gus, 0x0a, (ctrl & 4) ^ 4));
+	if (gus->interwave && snd_gf1_i_read8(gus, 0x19) & 0x01) {	/* enhanced mode */
+		mode = snd_gf1_i_read8(gus, 0x15);
+		printk(" -%i- GFA1 mode                   = 0x%x\n", voice, mode);
+		if (mode & 0x01) {	/* Effect processor */
+			printk(" -%i- GFA1 effect address         = 0x%x\n", voice, snd_gf1_i_read_addr(gus, 0x11, ctrl & 4));
+			printk(" -%i- GFA1 effect volume          = 0x%x\n", voice, snd_gf1_i_read16(gus, 0x16));
+			printk(" -%i- GFA1 effect volume final    = 0x%x\n", voice, snd_gf1_i_read16(gus, 0x1d));
+			printk(" -%i- GFA1 effect acumulator      = 0x%x\n", voice, snd_gf1_i_read8(gus, 0x14));
+		}
+		if (mode & 0x20) {
+			printk(" -%i- GFA1 left offset            = 0x%x (%i)\n", voice, snd_gf1_i_read16(gus, 0x13), snd_gf1_i_read16(gus, 0x13) >> 4);
+			printk(" -%i- GFA1 left offset final      = 0x%x (%i)\n", voice, snd_gf1_i_read16(gus, 0x1c), snd_gf1_i_read16(gus, 0x1c) >> 4);
+			printk(" -%i- GFA1 right offset           = 0x%x (%i)\n", voice, snd_gf1_i_read16(gus, 0x0c), snd_gf1_i_read16(gus, 0x0c) >> 4);
+			printk(" -%i- GFA1 right offset final     = 0x%x (%i)\n", voice, snd_gf1_i_read16(gus, 0x1b), snd_gf1_i_read16(gus, 0x1b) >> 4);
+		} else
+			printk(" -%i- GF1  pan                    = 0x%x\n", voice, snd_gf1_i_read8(gus, 0x0c));
+	} else
+		printk(" -%i- GF1  pan                    = 0x%x\n", voice, snd_gf1_i_read8(gus, 0x0c));
+}
+
+void snd_gf1_print_global_registers(snd_gus_card_t * gus)
+{
+	unsigned char global_mode = 0x00;
+
+	printk(" -G- GF1 active voices            = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_ACTIVE_VOICES));
+	if (gus->interwave) {
+		global_mode = snd_gf1_i_read8(gus, SNDRV_GF1_GB_GLOBAL_MODE);
+		printk(" -G- GF1 global mode              = 0x%x\n", global_mode);
+	}
+	if (global_mode & 0x02)	/* LFO enabled? */
+		printk(" -G- GF1 LFO base                 = 0x%x\n", snd_gf1_i_look16(gus, SNDRV_GF1_GW_LFO_BASE));
+	printk(" -G- GF1 voices IRQ read          = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_VOICES_IRQ_READ));
+	printk(" -G- GF1 DRAM DMA control         = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_DRAM_DMA_CONTROL));
+	printk(" -G- GF1 DRAM DMA high/low        = 0x%x/0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_DRAM_DMA_HIGH), snd_gf1_i_read16(gus, SNDRV_GF1_GW_DRAM_DMA_LOW));
+	printk(" -G- GF1 DRAM IO high/low         = 0x%x/0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_DRAM_IO_HIGH), snd_gf1_i_read16(gus, SNDRV_GF1_GW_DRAM_IO_LOW));
+	if (!gus->interwave)
+		printk(" -G- GF1 record DMA control       = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL));
+	printk(" -G- GF1 DRAM IO 16               = 0x%x\n", snd_gf1_i_look16(gus, SNDRV_GF1_GW_DRAM_IO16));
+	if (gus->gf1.enh_mode) {
+		printk(" -G- GFA1 memory config           = 0x%x\n", snd_gf1_i_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG));
+		printk(" -G- GFA1 memory control          = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_MEMORY_CONTROL));
+		printk(" -G- GFA1 FIFO record base        = 0x%x\n", snd_gf1_i_look16(gus, SNDRV_GF1_GW_FIFO_RECORD_BASE_ADDR));
+		printk(" -G- GFA1 FIFO playback base      = 0x%x\n", snd_gf1_i_look16(gus, SNDRV_GF1_GW_FIFO_PLAY_BASE_ADDR));
+		printk(" -G- GFA1 interleave control      = 0x%x\n", snd_gf1_i_look16(gus, SNDRV_GF1_GW_INTERLEAVE));
+	}
+}
+
+void snd_gf1_print_setup_registers(snd_gus_card_t * gus)
+{
+	printk(" -S- mix control                  = 0x%x\n", inb(GUSP(gus, MIXCNTRLREG)));
+	printk(" -S- IRQ status                   = 0x%x\n", inb(GUSP(gus, IRQSTAT)));
+	printk(" -S- timer control                = 0x%x\n", inb(GUSP(gus, TIMERCNTRL)));
+	printk(" -S- timer data                   = 0x%x\n", inb(GUSP(gus, TIMERDATA)));
+	printk(" -S- status read                  = 0x%x\n", inb(GUSP(gus, REGCNTRLS)));
+	printk(" -S- Sound Blaster control        = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL));
+	printk(" -S- AdLib timer 1/2              = 0x%x/0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_ADLIB_TIMER_1), snd_gf1_i_look8(gus, SNDRV_GF1_GB_ADLIB_TIMER_2));
+	printk(" -S- reset                        = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET));
+	if (gus->interwave) {
+		printk(" -S- compatibility                = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_COMPATIBILITY));
+		printk(" -S- decode control               = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_DECODE_CONTROL));
+		printk(" -S- version number               = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_VERSION_NUMBER));
+		printk(" -S- MPU-401 emul. control A/B    = 0x%x/0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_MPU401_CONTROL_A), snd_gf1_i_look8(gus, SNDRV_GF1_GB_MPU401_CONTROL_B));
+		printk(" -S- emulation IRQ                = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_EMULATION_IRQ));
+	}
+}
+
+void snd_gf1_peek_print_block(snd_gus_card_t * gus, unsigned int addr, int count, int w_16bit)
+{
+	if (!w_16bit) {
+		while (count-- > 0)
+			printk(count > 0 ? "%02x:" : "%02x", snd_gf1_peek(gus, addr++));
+	} else {
+		while (count-- > 0) {
+			printk(count > 0 ? "%04x:" : "%04x", snd_gf1_peek(gus, addr) | (snd_gf1_peek(gus, addr + 1) << 8));
+			addr += 2;
+		}
+	}
+}
+
+#endif
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/gus_irq.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/gus_irq.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/gus_irq.c	(revision 91)
@@ -0,0 +1,156 @@
+/*
+ *  Routine for IRQ handling from GF1/InterWave chip
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <sound/driver.h>
+#include <sound/info.h>
+#include <sound/gus.h>
+
+#ifdef CONFIG_SND_DEBUG
+#define STAT_ADD(x)	((x)++)
+#else
+#define STAT_ADD(x)	while (0) { ; }
+#endif
+
+void snd_gus_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	snd_gus_card_t * gus = snd_magic_cast(snd_gus_card_t, dev_id, return);
+	unsigned char status;
+	int loop = 100;
+	
+      __again:
+	status = inb(gus->gf1.reg_irqstat);
+	if (status == 0)
+		return;
+	// snd_printk("IRQ: status = 0x%x\n", status);
+	if (status & 0x02) {
+		STAT_ADD(gus->gf1.interrupt_stat_midi_in);
+		gus->gf1.interrupt_handler_midi_in(gus);
+	}
+	if (status & 0x01) {
+		STAT_ADD(gus->gf1.interrupt_stat_midi_out);
+		gus->gf1.interrupt_handler_midi_out(gus);
+	}
+	if (status & (0x20 | 0x40)) {
+		unsigned int already, _current_;
+		unsigned char voice_status, voice;
+		snd_gus_voice_t *pvoice;
+
+		already = 0;
+		while (((voice_status = snd_gf1_i_read8(gus, SNDRV_GF1_GB_VOICES_IRQ)) & 0xc0) != 0xc0) {
+			voice = voice_status & 0x1f;
+			_current_ = 1 << voice;
+			if (already & _current_)
+				continue;	/* multi request */
+			already |= _current_;	/* mark request */
+#if 0
+			printk("voice = %i, voice_status = 0x%x, voice_verify = %i\n", voice, voice_status, inb(GUSP(gus, GF1PAGE)));
+#endif
+			pvoice = &gus->gf1.voices[voice]; 
+			if (pvoice->use) {
+				if (!(voice_status & 0x80)) {	/* voice position IRQ */
+					STAT_ADD(pvoice->interrupt_stat_wave);
+					pvoice->handler_wave(gus, pvoice);
+				}
+				if (!(voice_status & 0x40)) {	/* volume ramp IRQ */
+					STAT_ADD(pvoice->interrupt_stat_volume);
+					pvoice->handler_volume(gus, pvoice);
+				}
+			} else {
+				STAT_ADD(gus->gf1.interrupt_stat_voice_lost);
+				snd_gf1_i_ctrl_stop(gus, SNDRV_GF1_VB_ADDRESS_CONTROL);
+				snd_gf1_i_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL);
+			}
+		}
+	}
+	if (status & 0x04) {
+		STAT_ADD(gus->gf1.interrupt_stat_timer1);
+		gus->gf1.interrupt_handler_timer1(gus);
+	}
+	if (status & 0x08) {
+		STAT_ADD(gus->gf1.interrupt_stat_timer2);
+		gus->gf1.interrupt_handler_timer2(gus);
+	}
+	if (status & 0x80) {
+		if (snd_gf1_i_look8(gus, SNDRV_GF1_GB_DRAM_DMA_CONTROL) & 0x40) {
+			STAT_ADD(gus->gf1.interrupt_stat_dma_write);
+			gus->gf1.interrupt_handler_dma_write(gus);
+		}
+		if (snd_gf1_i_look8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL) & 0x40) {
+			STAT_ADD(gus->gf1.interrupt_stat_dma_read);
+			gus->gf1.interrupt_handler_dma_read(gus);
+		}
+	}
+	if (--loop > 0)
+		goto __again;
+}
+
+#ifdef CONFIG_SND_DEBUG
+static void snd_gus_irq_info_read(snd_info_entry_t *entry, 
+				  snd_info_buffer_t * buffer)
+{
+	snd_gus_card_t *gus;
+	snd_gus_voice_t *pvoice;
+	int idx;
+
+	gus = snd_magic_cast(snd_gus_card_t, entry->private_data, return);
+	snd_iprintf(buffer, "midi out = %u\n", gus->gf1.interrupt_stat_midi_out);
+	snd_iprintf(buffer, "midi in = %u\n", gus->gf1.interrupt_stat_midi_in);
+	snd_iprintf(buffer, "timer1 = %u\n", gus->gf1.interrupt_stat_timer1);
+	snd_iprintf(buffer, "timer2 = %u\n", gus->gf1.interrupt_stat_timer2);
+	snd_iprintf(buffer, "dma write = %u\n", gus->gf1.interrupt_stat_dma_write);
+	snd_iprintf(buffer, "dma read = %u\n", gus->gf1.interrupt_stat_dma_read);
+	snd_iprintf(buffer, "voice lost = %u\n", gus->gf1.interrupt_stat_voice_lost);
+	for (idx = 0; idx < 32; idx++) {
+		pvoice = &gus->gf1.voices[idx];
+		snd_iprintf(buffer, "voice %i: wave = %u, volume = %u\n",
+					idx,
+					pvoice->interrupt_stat_wave,
+					pvoice->interrupt_stat_volume);
+	}
+}
+
+void snd_gus_irq_profile_init(snd_gus_card_t *gus)
+{
+	snd_info_entry_t *entry;
+
+	gus->irq_entry = NULL;
+	entry = snd_info_create_card_entry(gus->card, "gusirq", gus->card->proc_root);
+	if (entry) {
+		entry->content = SNDRV_INFO_CONTENT_TEXT;
+		entry->c.text.read_size = 512;
+		entry->c.text.read = snd_gus_irq_info_read;
+		entry->private_data = gus;
+		if (snd_info_register(entry) < 0) {
+			snd_info_free_entry(entry);
+			entry = NULL;
+		}
+	}
+	gus->irq_entry = entry;	
+}
+
+void snd_gus_irq_profile_done(snd_gus_card_t *gus)
+{
+	if (gus->irq_entry) {
+		snd_info_unregister(gus->irq_entry);
+		gus->irq_entry = NULL;
+	}
+}
+#endif
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/gus_lfo.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/gus_lfo.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/gus_lfo.c	(revision 91)
@@ -0,0 +1,428 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  Routines for control of LFO generators (tremolo & vibrato) for
+ *  GF1/InterWave chips...
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <sound/driver.h>
+#include <sound/gus.h>
+
+/*
+ *  called by engine routines
+ */
+
+static signed char snd_gf1_lfo_compute_value(snd_gus_card_t * gus,
+					     unsigned char *ptr)
+{
+	unsigned int twaveinc, depth_delta;
+	signed int result;
+	unsigned short control, twave, depth, depth_final;
+	unsigned char *ptr1;
+
+	control = *(unsigned short *) (ptr + 0x00);
+	ptr1 = ptr + ((control & 0x4000) >> 12);
+	/* 1. add TWAVEINC to TWAVE and write the result back */
+	/* LFO update rate is 689Hz, effect timer is in ms */
+	if (gus->gf1.timer_slave)
+		twaveinc = (689 * gus->gf1.timer_master_gus->gf1.effect_timer) / 1000;
+	else
+		twaveinc = (689 * gus->gf1.effect_timer) / 1000;
+	if (!twaveinc)
+		twaveinc++;
+#if 0
+	printk("twaveinc = 0x%x, effect_timer = %i\n", twaveinc, gus->gf1.effect_timer);
+#endif
+
+	depth = *(unsigned short *) (ptr1 + 0x0a);
+	depth_final = *(unsigned char *) (ptr + 0x02) << 5;
+	if (depth != depth_final) {
+		depth_delta = ((twaveinc * *(ptr + 0x03)) + *(unsigned short *) (ptr + 0x04));
+		*(unsigned short *) (ptr + 0x04) = depth_delta % 8000;
+		depth_delta /= 8000;
+		if (depth < depth_final) {
+			if (depth + depth_delta > depth_final)
+				depth = depth_final;
+			else
+				depth += depth_delta;
+		}
+		if (depth > depth_final) {
+			if (depth - depth_delta < depth_final)
+				depth = depth_final;
+			else
+				depth -= depth_delta;
+		}
+		*(unsigned short *) (ptr1 + 0x0a) = depth;
+	}
+	twaveinc *= (unsigned int) control & 0x7ff;
+	twaveinc += *(unsigned short *) (ptr + 0x06);
+	*(unsigned short *) (ptr + 0x06) = twaveinc % 1000;
+
+	twave = *(unsigned short *) (ptr1 + 0x08);
+	twave += (unsigned short) (twaveinc / (unsigned int) 1000);
+	*(unsigned short *) (ptr1 + 0x08) = twave;
+
+	if (!(control & 0x2000)) {
+		/* 2. if shift is low */
+		if (twave & 0x4000) {	/* bit 14 high -> invert TWAVE 13-0 */
+			twave ^= 0x3fff;
+			twave &= ~0x4000;
+		}
+		/* TWAVE bit 15 is exclusive or'd with the invert bit (12) */
+		twave ^= (control & 0x1000) << 3;
+	} else {
+		/* 2. if shift is high */
+		if (twave & 0x8000)	/* bit 15 high -> invert TWAVE 14-0 */
+			twave ^= 0x7fff;
+		/* the invert bit (12) is used as sign bit */
+		if (control & 0x1000)
+			twave |= 0x8000;
+		else
+			twave &= ~0x8000;
+	}
+	/* 3. multiply the 14-bit LFO waveform magnitude by 13-bit DEPTH */
+#if 0
+	printk("c=0x%x,tw=0x%x,to=0x%x,d=0x%x,df=0x%x,di=0x%x,r=0x%x,r1=%i\n",
+	       control, twave,
+	       *(unsigned short *) (ptr1 + 0x08),
+	       depth, depth_final, *(ptr + 0x03),
+	     (twave & 0x7fff) * depth, ((twave & 0x7fff) * depth) >> 21);
+#endif
+	result = (twave & 0x7fff) * depth;
+	if (result) {
+		/* shift */
+		result >>= 21;
+		result &= 0x3f;
+	}
+	/* add sign */
+	if (twave & 0x8000)
+		result = -result;
+#if 0
+	printk("lfo final value = %i\n", result);
+#endif
+	return result;
+}
+
+static void snd_gf1_lfo_register_setup(snd_gus_card_t * gus,
+				       snd_gf1_voice_t * voice,
+				       int lfo_type)
+{
+	unsigned long flags;
+
+	if (gus->gf1.enh_mode) {
+		CLI(&flags);
+		gf1_select_voice(gus, voice->number);
+		if (lfo_type & 1) {
+			snd_gf1_write8(gus, GF1_VB_FREQUENCY_LFO, voice->lfo_fc);
+			voice->lfo_fc = 0;
+		}
+		if (lfo_type & 2) {
+			snd_gf1_write8(gus, GF1_VB_VOLUME_LFO, voice->lfo_volume);
+			voice->lfo_volume = 0;
+		}
+		STI(&flags);
+	} else {
+		/*
+		 * ok.. with old GF1 chip can be only vibrato emulated...
+		 * volume register can be in volume ramp state, so tremolo isn't simple..
+		 */
+		if (!(lfo_type & 1))
+			return;
+#if 0
+		if (voice->lfo_fc)
+			printk("setup - %i = %i\n", voice->number, voice->lfo_fc);
+#endif
+		CLI(&flags);
+		gf1_select_voice(gus, voice->number);
+		snd_gf1_write16(gus, GF1_VW_FREQUENCY, voice->fc_register + voice->lfo_fc);
+		STI(&flags);
+	}
+}
+
+void snd_gf1_lfo_effect_interrupt(snd_gus_card_t * gus, snd_gf1_voice_t * voice)
+{
+	unsigned char *ptr;
+
+#if 0
+	if (voice->number != 0)
+		return;
+#endif
+	ptr = gus->gf1.lfos + ((voice->number) << 5);
+	/* 1. vibrato */
+	if (*(unsigned short *) (ptr + 0x00) & 0x8000)
+		voice->lfo_fc = snd_gf1_lfo_compute_value(gus, ptr);
+	/* 2. tremolo */
+	ptr += 16;
+	if (*(unsigned short *) (ptr + 0x00) & 0x8000)
+		voice->lfo_volume = snd_gf1_lfo_compute_value(gus, ptr);
+	/* 3. register setup */
+	snd_gf1_lfo_register_setup(gus, voice, 3);
+}
+
+/*
+
+ */
+
+void snd_gf1_lfo_init(snd_gus_card_t * gus)
+{
+	if (gus->gf1.hw_lfo) {
+		snd_gf1_i_write16(gus, GF1_GW_LFO_BASE, 0x0000);
+		snd_gf1_dram_setmem(gus, 0, 0x0000, 1024);
+		/* now enable LFO */
+		snd_gf1_i_write8(gus, GF1_GB_GLOBAL_MODE, snd_gf1_i_look8(gus, GF1_GB_GLOBAL_MODE) | 0x02);
+	}
+	if (gus->gf1.sw_lfo) {
+#if 1
+		gus->gf1.lfos = snd_calloc(1024);
+		if (!gus->gf1.lfos)
+#endif
+			gus->gf1.sw_lfo = 0;
+	}
+}
+
+void snd_gf1_lfo_done(snd_gus_card_t * gus)
+{
+	if (gus->gf1.sw_lfo) {
+		if (gus->gf1.lfos) {
+			snd_gf1_free(gus->gf1.lfos, 1024);
+			gus->gf1.lfos = NULL;
+		}
+	}
+}
+
+void snd_gf1_lfo_program(snd_gus_card_t * gus, int voice, int lfo_type,
+                         struct ULTRA_STRU_IW_LFO_PROGRAM *program)
+{
+	unsigned int lfo_addr, wave_select;
+
+	wave_select = (program->freq_and_control & 0x4000) >> 12;
+	lfo_addr = (voice << 5) | (lfo_type << 4);
+	if (gus->gf1.hw_lfo) {
+#if 0
+		printk("LMCI = 0x%x\n", snd_gf1_i_look8(gus, 0x53));
+		printk("lfo_program: lfo_addr=0x%x,wave_sel=0x%x,fac=0x%x,df=0x%x,di=0x%x,twave=0x%x,depth=0x%x\n",
+		       lfo_addr, wave_select,
+		       program->freq_and_control,
+		       program->depth_final,
+		       program->depth_inc,
+		       program->twave,
+		       program->depth);
+#endif
+		snd_gf1_poke(gus, lfo_addr + 0x02, program->depth_final);
+		snd_gf1_poke(gus, lfo_addr + 0x03, program->depth_inc);
+		snd_gf1_pokew(gus, lfo_addr + 0x08 + wave_select, program->twave);
+		snd_gf1_pokew(gus, lfo_addr + 0x0a + wave_select, program->depth);
+		snd_gf1_pokew(gus, lfo_addr + 0x00, program->freq_and_control);
+#if 0
+		{
+			int i = 0;
+			for (i = 0; i < 16; i++)
+				printk("%02x:", snd_gf1_peek(gus, lfo_addr + i));
+			printk("\n");
+		}
+#endif
+	}
+	if (gus->gf1.sw_lfo) {
+		unsigned char *ptr = gus->gf1.lfos + lfo_addr;
+
+		*(ptr + 0x02) = program->depth_final;
+		*(ptr + 0x03) = program->depth_inc;
+		*(unsigned short *) (ptr + 0x08 + wave_select) = program->twave;
+		*(unsigned short *) (ptr + 0x0a + wave_select) = program->depth;
+		*(unsigned short *) (ptr + 0x00) = program->freq_and_control;
+	}
+}
+
+void snd_gf1_lfo_enable(snd_gus_card_t * gus, int voice, int lfo_type)
+{
+	unsigned int lfo_addr;
+
+	lfo_addr = (voice << 5) | (lfo_type << 4);
+	if (gus->gf1.hw_lfo)
+		snd_gf1_pokew(gus, lfo_addr + 0x00, snd_gf1_peekw(gus, lfo_addr + 0x00) | 0x8000);
+	if (gus->gf1.sw_lfo) {
+		unsigned char *ptr = gus->gf1.lfos + lfo_addr;
+
+		*(unsigned short *) (ptr + 0x00) |= 0x8000;
+	}
+}
+
+void snd_gf1_lfo_disable(snd_gus_card_t * gus, int voice, int lfo_type)
+{
+	unsigned int lfo_addr;
+
+	lfo_addr = (voice << 5) | (lfo_type << 4);
+	if (gus->gf1.hw_lfo)
+		snd_gf1_pokew(gus, lfo_addr + 0x00,
+				snd_gf1_peekw(gus, lfo_addr + 0x00) & ~0x8000);
+	if (gus->gf1.sw_lfo) {
+		unsigned char *ptr = gus->gf1.lfos + lfo_addr;
+
+		*(unsigned short *) (ptr + 0x00) &= ~0x8000;
+	}
+}
+
+void snd_gf1_lfo_change_freq(snd_gus_card_t * gus, int voice,
+		             int lfo_type, int freq)
+{
+	unsigned int lfo_addr;
+
+	lfo_addr = (voice << 5) | (lfo_type << 4);
+	if (gus->gf1.hw_lfo)
+		snd_gf1_pokew(gus, lfo_addr + 0x00,
+				(snd_gf1_peekw(gus, lfo_addr + 0x00) & ~0x7ff) | (freq & 0x7ff));
+	if (gus->gf1.sw_lfo) {
+		unsigned long flags;
+		unsigned char *ptr = gus->gf1.lfos + lfo_addr;
+
+		CLI(&flags);
+		*(unsigned short *) (ptr + 0x00) &= ~0x7ff;
+		*(unsigned short *) (ptr + 0x00) |= freq & 0x7ff;
+		STI(&flags);
+	}
+}
+
+void snd_gf1_lfo_change_depth(snd_gus_card_t * gus, int voice,
+			      int lfo_type, int depth)
+{
+	unsigned long flags;
+	unsigned int lfo_addr;
+	unsigned short control = 0;
+	unsigned char *ptr;
+
+	lfo_addr = (voice << 5) | (lfo_type << 4);
+	ptr = gus->gf1.lfos + lfo_addr;
+	if (gus->gf1.hw_lfo)
+		control = snd_gf1_peekw(gus, lfo_addr + 0x00);
+	if (gus->gf1.sw_lfo)
+		control = *(unsigned short *) (ptr + 0x00);
+	if (depth < 0) {
+		control |= 0x1000;
+		depth = -depth;
+	} else
+		control &= ~0x1000;
+	if (gus->gf1.hw_lfo) {
+		CLI(&flags);
+		snd_gf1_poke(gus, lfo_addr + 0x02, (unsigned char) depth);
+		snd_gf1_pokew(gus, lfo_addr + 0x0a + ((control & 0x4000) >> 12), depth << 5);
+		snd_gf1_pokew(gus, lfo_addr + 0x00, control);
+		STI(&flags);
+	}
+	if (gus->gf1.sw_lfo) {
+		unsigned char *ptr = gus->gf1.lfos + lfo_addr;
+
+		CLI(&flags);
+		*(ptr + 0x02) = (unsigned char) depth;
+		*(unsigned short *) (ptr + 0x0a + ((control & 0x4000) >> 12)) = depth << 5;
+		*(unsigned short *) (ptr + 0x00) = control;
+		STI(&flags);
+	}
+}
+
+void snd_gf1_lfo_setup(snd_gus_card_t * gus, int voice, int lfo_type,
+		       int freq, int current_depth, int depth, int sweep,
+		       int shape)
+{
+	struct ULTRA_STRU_IW_LFO_PROGRAM program;
+
+	program.freq_and_control = 0x8000 | (freq & 0x7ff);
+	if (shape & ULTRA_STRU_IW_LFO_SHAPE_POSTRIANGLE)
+		program.freq_and_control |= 0x2000;
+	if (depth < 0) {
+		program.freq_and_control |= 0x1000;
+		depth = -depth;
+	}
+	program.twave = 0;
+	program.depth = current_depth;
+	program.depth_final = depth;
+	if (sweep) {
+		program.depth_inc = (unsigned char) (((int) ((depth << 5) - current_depth) << 9) / (sweep * 4410L));
+		if (!program.depth_inc)
+			program.depth_inc++;
+	} else
+		program.depth = (unsigned short) (depth << 5);
+	snd_gf1_lfo_program(gus, voice, lfo_type, &program);
+}
+
+void snd_gf1_lfo_shutdown(snd_gus_card_t * gus, int voice, int lfo_type)
+{
+	unsigned long flags;
+	unsigned int lfo_addr;
+
+	lfo_addr = (voice << 5) | (lfo_type << 4);
+	if (gus->gf1.hw_lfo) {
+		snd_gf1_pokew(gus, lfo_addr + 0x00, 0x0000);
+		CLI(&flags);
+		gf1_select_voice(gus, voice);
+		snd_gf1_write8(gus, lfo_type == ULTRA_LFO_VIBRATO ? GF1_VB_FREQUENCY_LFO : GF1_VB_VOLUME_LFO, 0);
+		STI(&flags);
+	}
+	if (gus->gf1.sw_lfo) {
+		unsigned char *ptr = gus->gf1.lfos + lfo_addr;
+		snd_gf1_voice_t *pvoice;
+
+		*(unsigned short *) (ptr + 0x00) = 0;
+		*(unsigned short *) (ptr + 0x04) = 0;
+		*(unsigned short *) (ptr + 0x06) = 0;
+		if (gus->gf1.syn_voices) {
+			pvoice = gus->gf1.syn_voices + voice;
+			if (lfo_type == ULTRA_LFO_VIBRATO)
+				pvoice->lfo_fc = 0;
+			else
+				pvoice->lfo_volume = 0;
+			snd_gf1_lfo_register_setup(gus, pvoice, lfo_type == ULTRA_LFO_VIBRATO ? 1 : 2);
+		} else if (gus->gf1.enh_mode) {
+			CLI(&flags);
+			gf1_select_voice(gus, voice);
+			snd_gf1_write8(gus, lfo_type == ULTRA_LFO_VIBRATO ? GF1_VB_FREQUENCY_LFO : GF1_VB_VOLUME_LFO, 0);
+			STI(&flags);
+		}
+	}
+}
+
+void snd_gf1_lfo_command(snd_gus_card_t * gus, int voice, unsigned char *data)
+{
+	int lfo_type;
+	int lfo_command;
+	int temp1, temp2;
+
+	lfo_type = *data >> 7;
+	lfo_command = *data & 0x7f;
+	switch (lfo_command) {
+	case ULTRA_LFO_SETUP:	/* setup */
+		temp1 = snd_gf1_get_word(data, 2);
+		temp2 = snd_gf1_get_word(data, 4);
+		snd_gf1_lfo_setup(gus, voice, lfo_type, temp1 & 0x7ff, 0, temp2 > 255 ? 255 : temp2, snd_gf1_get_byte(data, 1), (temp1 & 0x2000) >> 13);
+		break;
+	case ULTRA_LFO_FREQ:	/* freq */
+		snd_gf1_lfo_change_depth(gus, voice, lfo_type, snd_gf1_get_word(data, 2));
+		break;
+	case ULTRA_LFO_DEPTH:	/* depth */
+		snd_gf1_lfo_change_freq(gus, voice, lfo_type, snd_gf1_get_word(data, 2));
+		break;
+	case ULTRA_LFO_ENABLE:	/* enable */
+		snd_gf1_lfo_enable(gus, voice, lfo_type);
+		break;
+	case ULTRA_LFO_DISABLE:	/* disable */
+		snd_gf1_lfo_disable(gus, voice, lfo_type);
+		break;
+	case ULTRA_LFO_SHUTDOWN:	/* shutdown */
+		snd_gf1_lfo_shutdown(gus, voice, lfo_type);
+		break;
+	}
+}
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/gus_main.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/gus_main.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/gus_main.c	(revision 91)
@@ -0,0 +1,516 @@
+/*
+ *  Routines for Gravis UltraSound soundcards
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#define SNDRV_MAIN_OBJECT_FILE
+#include <sound/driver.h>
+#include <sound/gus.h>
+#include <sound/control.h>
+
+#define chip_t snd_gus_card_t
+
+static int snd_gus_init_dma_irq(snd_gus_card_t * gus, int latches);
+
+int snd_gus_use_inc(snd_gus_card_t * gus)
+{
+	MOD_INC_USE_COUNT;
+	if (!try_inc_mod_count(gus->card->module)) {
+		MOD_DEC_USE_COUNT;
+		return 0;
+	}
+	return 1;
+}
+
+void snd_gus_use_dec(snd_gus_card_t * gus)
+{
+	dec_mod_count(gus->card->module);
+	MOD_DEC_USE_COUNT;
+}
+
+static int snd_gus_joystick_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 31;
+	return 0;
+}
+
+static int snd_gus_joystick_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	snd_gus_card_t *gus = snd_kcontrol_chip(kcontrol);
+	
+	ucontrol->value.integer.value[0] = gus->joystick_dac & 31;
+	return 0;
+}
+
+static int snd_gus_joystick_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	snd_gus_card_t *gus = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int change;
+	unsigned char nval;
+	
+	nval = ucontrol->value.integer.value[0] & 31;
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	change = gus->joystick_dac != nval;
+	gus->joystick_dac = nval;
+	snd_gf1_write8(gus, SNDRV_GF1_GB_JOYSTICK_DAC_LEVEL, gus->joystick_dac);
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+	return change;
+}
+
+#ifdef TARGET_OS2
+static snd_kcontrol_new_t snd_gus_joystick_control = {
+	SNDRV_CTL_ELEM_IFACE_CARD,0,0,
+	"Joystick Speed",0,0, 0,
+	snd_gus_joystick_info,
+	snd_gus_joystick_get,
+	snd_gus_joystick_put,0
+};
+#else
+static snd_kcontrol_new_t snd_gus_joystick_control = {
+	iface: SNDRV_CTL_ELEM_IFACE_CARD,
+	name: "Joystick Speed",
+	info: snd_gus_joystick_info,
+	get: snd_gus_joystick_get,
+	put: snd_gus_joystick_put
+};
+#endif
+
+static void snd_gus_init_control(snd_gus_card_t *gus)
+{
+	if (!gus->ace_flag)
+		snd_ctl_add(gus->card, snd_ctl_new1(&snd_gus_joystick_control, gus));
+}
+
+/*
+ *
+ */
+
+static int snd_gus_free(snd_gus_card_t *gus)
+{
+	if (gus->gf1.res_port2 == NULL)
+		goto __hw_end;
+#ifdef CONFIG_SND_SEQUENCER
+	if (gus->seq_dev) {
+		snd_device_free(gus->card, gus->seq_dev);
+		gus->seq_dev = NULL;
+	}
+#endif
+	snd_gf1_stop(gus);
+	snd_gus_init_dma_irq(gus, 0);
+      __hw_end:
+	if (gus->gf1.res_port1)
+		release_resource(gus->gf1.res_port1);
+	if (gus->gf1.res_port2)
+		release_resource(gus->gf1.res_port2);
+	if (gus->gf1.irq >= 0)
+		free_irq(gus->gf1.irq, (void *) gus);
+	if (gus->gf1.dma1 >= 0) {
+		disable_dma(gus->gf1.dma1);
+		free_dma(gus->gf1.dma1);
+	}
+	if (!gus->equal_dma && gus->gf1.dma2 >= 0) {
+		disable_dma(gus->gf1.dma2);
+		free_dma(gus->gf1.dma2);
+	}
+	snd_magic_kfree(gus);
+	return 0;
+}
+
+static int snd_gus_dev_free(snd_device_t *device)
+{
+	snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, device->device_data, return -ENXIO);
+	return snd_gus_free(gus);
+}
+
+int snd_gus_create(snd_card_t * card,
+		   unsigned long port,
+		   int irq, int dma1, int dma2,
+		   int timer_dev,
+		   int voices,
+		   int pcm_channels,
+		   int effect,
+		   snd_gus_card_t **rgus)
+{
+	snd_gus_card_t *gus;
+	int err;
+#ifdef TARGET_OS2
+	static snd_device_ops_t ops = {
+		snd_gus_dev_free,0,0
+	};
+#else
+	static snd_device_ops_t ops = {
+		dev_free:	snd_gus_dev_free,
+	};
+#endif
+
+	*rgus = NULL;
+	gus = snd_magic_kcalloc(snd_gus_card_t, 0, GFP_KERNEL);
+	if (gus == NULL)
+		return -ENOMEM;
+	gus->gf1.irq = -1;
+	gus->gf1.dma1 = -1;
+	gus->gf1.dma2 = -1;
+	gus->card = card;
+	gus->gf1.port = port;
+	/* fill register variables for speedup */
+	gus->gf1.reg_page = GUSP(gus, GF1PAGE);
+	gus->gf1.reg_regsel = GUSP(gus, GF1REGSEL);
+	gus->gf1.reg_data8 = GUSP(gus, GF1DATAHIGH);
+	gus->gf1.reg_data16 = GUSP(gus, GF1DATALOW);
+	gus->gf1.reg_irqstat = GUSP(gus, IRQSTAT);
+	gus->gf1.reg_dram = GUSP(gus, DRAM);
+	gus->gf1.reg_timerctrl = GUSP(gus, TIMERCNTRL);
+	gus->gf1.reg_timerdata = GUSP(gus, TIMERDATA);
+	/* allocate resources */
+	if ((gus->gf1.res_port1 = request_region(port, 16, "GUS GF1 (Adlib/SB)")) == NULL) {
+		snd_gus_free(gus);
+		return -EBUSY;
+	}
+	if ((gus->gf1.res_port2 = request_region(port + 0x100, 12, "GUS GF1 (Synth)")) == NULL) {
+		snd_gus_free(gus);
+		return -EBUSY;
+	}
+	if (irq >= 0 && request_irq(irq, snd_gus_interrupt, SA_INTERRUPT, "GUS GF1", (void *) gus)) {
+		snd_gus_free(gus);
+		return -EBUSY;
+	}
+	gus->gf1.irq = irq;
+	if (request_dma(dma1, "GUS - 1")) {
+		snd_gus_free(gus);
+		return -EBUSY;
+	}
+	gus->gf1.dma1 = dma1;
+	if (dma2 >= 0 && dma1 != dma2) {
+		if (request_dma(dma2, "GUS - 2")) {
+			snd_gus_free(gus);
+			return -EBUSY;
+		}
+		gus->gf1.dma2 = dma2;
+	} else {
+		gus->gf1.dma2 = gus->gf1.dma1;
+		gus->equal_dma = 1;
+	}
+	gus->timer_dev = timer_dev;
+	if (voices < 14)
+		voices = 14;
+	if (voices > 32)
+		voices = 32;
+	if (pcm_channels < 0)
+		pcm_channels = 0;
+	if (pcm_channels > 8)
+		pcm_channels = 8;
+	pcm_channels++;
+	pcm_channels &= ~1;
+	gus->gf1.effect = effect ? 1 : 0;
+	gus->gf1.active_voices = voices;
+	gus->gf1.pcm_channels = pcm_channels;
+	gus->gf1.volume_ramp = 25;
+	gus->gf1.smooth_pan = 1;
+	spin_lock_init(&gus->reg_lock);
+	spin_lock_init(&gus->voice_alloc);
+	spin_lock_init(&gus->active_voice_lock);
+	spin_lock_init(&gus->event_lock);
+	spin_lock_init(&gus->dma_lock);
+	spin_lock_init(&gus->pcm_volume_level_lock);
+	spin_lock_init(&gus->uart_cmd_lock);
+	init_MUTEX(&gus->dma_mutex);
+	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, gus, &ops)) < 0) {
+		snd_gus_free(gus);
+		return err;
+	}
+	*rgus = gus;
+	return 0;
+}
+
+/*
+ *  Memory detection routine for plain GF1 soundcards
+ */
+
+static int snd_gus_detect_memory(snd_gus_card_t * gus)
+{
+	int l, idx, local;
+	unsigned char d;
+
+	snd_gf1_poke(gus, 0L, 0xaa);
+	snd_gf1_poke(gus, 1L, 0x55);
+	if (snd_gf1_peek(gus, 0L) != 0xaa || snd_gf1_peek(gus, 1L) != 0x55) {
+		snd_printk("plain GF1 card at 0x%lx without onboard DRAM?\n", gus->gf1.port);
+		return -ENOMEM;
+	}
+	for (idx = 1, d = 0xab; idx < 4; idx++, d++) {
+		local = idx << 18;
+		snd_gf1_poke(gus, local, d);
+		snd_gf1_poke(gus, local + 1, d + 1);
+		if (snd_gf1_peek(gus, local) != d ||
+		    snd_gf1_peek(gus, local + 1) != d + 1 ||
+		    snd_gf1_peek(gus, 0L) != 0xaa)
+			break;
+	}
+#if 1
+	gus->gf1.memory = idx << 18;
+#else
+	gus->gf1.memory = 256 * 1024;
+#endif
+	for (l = 0, local = gus->gf1.memory; l < 4; l++, local -= 256 * 1024) {
+		gus->gf1.mem_alloc.banks_8[l].address =
+		    gus->gf1.mem_alloc.banks_8[l].size = 0;
+		gus->gf1.mem_alloc.banks_16[l].address = l << 18;
+		gus->gf1.mem_alloc.banks_16[l].size = local > 0 ? 256 * 1024 : 0;
+	}
+	gus->gf1.mem_alloc.banks_8[0].size = gus->gf1.memory;
+	return 0;		/* some memory were detected */
+}
+
+static int snd_gus_init_dma_irq(snd_gus_card_t * gus, int latches)
+{
+	snd_card_t *card;
+	unsigned long flags;
+	int irq, dma1, dma2;
+	static unsigned char irqs[16] =
+		{0, 0, 1, 3, 0, 2, 0, 4, 0, 1, 0, 5, 6, 0, 0, 7};
+	static unsigned char dmas[8] =
+		{6, 1, 0, 2, 0, 3, 4, 5};
+
+	snd_assert(gus != NULL, return -EINVAL);
+	card = gus->card;
+	snd_assert(card != NULL, return -EINVAL);
+
+	gus->mix_cntrl_reg &= 0xf8;
+	gus->mix_cntrl_reg |= 0x01;	/* disable MIC, LINE IN, enable LINE OUT */
+	if (gus->codec_flag || gus->ess_flag) {
+		gus->mix_cntrl_reg &= ~1;	/* enable LINE IN */
+		gus->mix_cntrl_reg |= 4;	/* enable MIC */
+	}
+	dma1 = gus->gf1.dma1;
+	dma1 = dma1 < 0 ? -dma1 : dma1;
+	dma1 = dmas[dma1 & 7];
+	dma2 = gus->gf1.dma2;
+	dma2 = dma2 < 0 ? -dma2 : dma2;
+	dma2 = dmas[dma2 & 7];
+#if 0
+	printk("dma1 = %i, dma2 = %i\n", gus->gf1.dma1, gus->gf1.dma2);
+#endif
+	dma1 |= gus->equal_dma ? 0x40 : (dma2 << 3);
+
+	if ((dma1 & 7) == 0 || (dma2 & 7) == 0) {
+		snd_printk("Error! DMA isn't defined.\n");
+		return -EINVAL;
+	}
+	irq = gus->gf1.irq;
+	irq = irq < 0 ? -irq : irq;
+	irq = irqs[irq & 0x0f];
+	if (irq == 0) {
+		snd_printk("Error! IRQ isn't defined.\n");
+		return -EINVAL;
+	}
+	irq |= 0x40;
+#if 0
+	card->mixer.mix_ctrl_reg |= 0x10;
+#endif
+
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	outb(5, GUSP(gus, REGCNTRLS));
+	outb(gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG));
+	outb(0x00, GUSP(gus, IRQDMACNTRLREG));
+	outb(0, GUSP(gus, REGCNTRLS));
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+
+	udelay(100);
+
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	outb(0x00 | gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG));
+	outb(dma1, GUSP(gus, IRQDMACNTRLREG));
+	if (latches) {
+		outb(0x40 | gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG));
+		outb(irq, GUSP(gus, IRQDMACNTRLREG));
+	}
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+
+	udelay(100);
+
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	outb(0x00 | gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG));
+	outb(dma1, GUSP(gus, IRQDMACNTRLREG));
+	if (latches) {
+		outb(0x40 | gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG));
+		outb(irq, GUSP(gus, IRQDMACNTRLREG));
+	}
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+
+	snd_gf1_delay(gus);
+
+	if (latches)
+		gus->mix_cntrl_reg |= 0x08;	/* enable latches */
+	else
+		gus->mix_cntrl_reg &= ~0x08;	/* disable latches */
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	outb(gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG));
+	outb(0, GUSP(gus, GF1PAGE));
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+
+	return 0;
+}
+
+static int snd_gus_check_version(snd_gus_card_t * gus)
+{
+	unsigned long flags;
+	unsigned char val, rev;
+	snd_card_t *card;
+
+	card = gus->card;
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	outb(0x20, GUSP(gus, REGCNTRLS));
+	val = inb(GUSP(gus, REGCNTRLS));
+	rev = inb(GUSP(gus, BOARDVERSION));
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+	snd_printdd("GF1 [0x%lx] init - val = 0x%x, rev = 0x%x\n", gus->gf1.port, val, rev);
+	strcpy(card->driver, "GUS");
+	strcpy(card->longname, "Gravis UltraSound Classic (2.4)");
+	if ((val != 255 && (val & 0x06)) || (rev >= 5 && rev != 255)) {
+		if (rev >= 5 && rev <= 9) {
+			gus->ics_flag = 1;
+			if (rev == 5)
+				gus->ics_flipped = 1;
+			card->longname[27] = '3';
+			card->longname[29] = rev == 5 ? '5' : '7';
+		}
+		if (rev >= 10 && rev != 255) {
+			if (rev >= 10 && rev <= 11) {
+				strcpy(card->driver, "GUS MAX");
+				strcpy(card->longname, "Gravis UltraSound MAX");
+				gus->max_flag = 1;
+			} else if (rev == 0x30) {
+				strcpy(card->driver, "GUS ACE");
+				strcpy(card->longname, "Gravis UltraSound Ace");
+				gus->ace_flag = 1;
+			} else if (rev == 0x50) {
+				strcpy(card->driver, "GUS Extreme");
+				strcpy(card->longname, "Gravis UltraSound Extreme");
+				gus->ess_flag = 1;
+			} else {
+				snd_printk("unknown GF1 revision number at 0x%lx - 0x%x (0x%x)\n", gus->gf1.port, rev, val);
+				snd_printk("  please - report to <perex@suse.cz>\n");
+			}
+		}
+	}
+	strcpy(card->shortname, card->longname);
+	gus->uart_enable = 1;	/* standard GUSes doesn't have midi uart trouble */
+	snd_gus_init_control(gus);
+	return 0;
+}
+
+static void snd_gus_seq_dev_free(snd_seq_device_t *seq_dev)
+{
+	snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, seq_dev->private_data, return);
+	gus->seq_dev = NULL;
+}
+
+int snd_gus_initialize(snd_gus_card_t *gus)
+{
+	int err;
+
+	if (!gus->interwave) {
+		if ((err = snd_gus_check_version(gus)) < 0) {
+			snd_printk("version check failed\n");
+			return err;
+		}
+		if ((err = snd_gus_detect_memory(gus)) < 0)
+			return err;
+	}
+	if ((err = snd_gus_init_dma_irq(gus, 1)) < 0)
+		return err;
+#ifdef CONFIG_SND_SEQUENCER
+	if (snd_seq_device_new(gus->card, 1, SNDRV_SEQ_DEV_ID_GUS,
+			       sizeof(snd_gus_card_t*), &gus->seq_dev) >= 0) {
+		strcpy(gus->seq_dev->name, "GUS");
+		*(snd_gus_card_t**)SNDRV_SEQ_DEVICE_ARGPTR(gus->seq_dev) = gus;
+		gus->seq_dev->private_data = gus;
+		gus->seq_dev->private_free = snd_gus_seq_dev_free;
+	}
+#endif
+	snd_gf1_start(gus);
+	gus->initialized = 1;
+	return 0;
+}
+
+  /* gus_io.c */
+EXPORT_SYMBOL(snd_gf1_delay);
+EXPORT_SYMBOL(snd_gf1_write8);
+EXPORT_SYMBOL(snd_gf1_look8);
+EXPORT_SYMBOL(snd_gf1_write16);
+EXPORT_SYMBOL(snd_gf1_look16);
+EXPORT_SYMBOL(snd_gf1_i_write8);
+EXPORT_SYMBOL(snd_gf1_i_look8);
+EXPORT_SYMBOL(snd_gf1_i_write16);
+EXPORT_SYMBOL(snd_gf1_i_look16);
+EXPORT_SYMBOL(snd_gf1_dram_addr);
+EXPORT_SYMBOL(snd_gf1_write_addr);
+EXPORT_SYMBOL(snd_gf1_poke);
+EXPORT_SYMBOL(snd_gf1_peek);
+  /* gus_reset.c */
+EXPORT_SYMBOL(snd_gf1_alloc_voice);
+EXPORT_SYMBOL(snd_gf1_free_voice);
+EXPORT_SYMBOL(snd_gf1_ctrl_stop);
+EXPORT_SYMBOL(snd_gf1_stop_voice);
+EXPORT_SYMBOL(snd_gf1_start);
+EXPORT_SYMBOL(snd_gf1_stop);
+  /* gus_mixer.c */
+EXPORT_SYMBOL(snd_gf1_new_mixer);
+  /* gus_pcm.c */
+EXPORT_SYMBOL(snd_gf1_pcm_new);
+  /* gus.c */
+EXPORT_SYMBOL(snd_gus_use_inc);
+EXPORT_SYMBOL(snd_gus_use_dec);
+EXPORT_SYMBOL(snd_gus_create);
+EXPORT_SYMBOL(snd_gus_initialize);
+  /* gus_irq.c */
+EXPORT_SYMBOL(snd_gus_interrupt);
+  /* gus_uart.c */
+EXPORT_SYMBOL(snd_gf1_rawmidi_new);
+  /* gus_dram.c */
+EXPORT_SYMBOL(snd_gus_dram_write);
+EXPORT_SYMBOL(snd_gus_dram_read);
+  /* gus_volume.c */
+EXPORT_SYMBOL(snd_gf1_lvol_to_gvol_raw);
+EXPORT_SYMBOL(snd_gf1_translate_freq);
+  /* gus_mem.c */
+EXPORT_SYMBOL(snd_gf1_mem_alloc);
+EXPORT_SYMBOL(snd_gf1_mem_xfree);
+EXPORT_SYMBOL(snd_gf1_mem_free);
+EXPORT_SYMBOL(snd_gf1_mem_lock);
+
+/*
+ *  INIT part
+ */
+
+static int __init alsa_gus_init(void)
+{
+	return 0;
+}
+
+static void __exit alsa_gus_exit(void)
+{
+}
+
+module_init(alsa_gus_init)
+module_exit(alsa_gus_exit)
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/gus_mem.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/gus_mem.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/gus_mem.c	(revision 91)
@@ -0,0 +1,365 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  GUS's memory allocation routines / bottom layer
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <sound/driver.h>
+#include <sound/gus.h>
+#include <sound/info.h>
+
+#ifdef CONFIG_SND_DEBUG
+static void snd_gf1_mem_info_read(snd_info_entry_t *entry, 
+				  snd_info_buffer_t * buffer);
+#endif
+
+void snd_gf1_mem_lock(snd_gf1_mem_t * alloc, int xup)
+{
+	if (!xup) {
+		down(&alloc->memory_mutex);
+	} else {
+		up(&alloc->memory_mutex);
+	}
+}
+
+snd_gf1_mem_block_t *snd_gf1_mem_xalloc(snd_gf1_mem_t * alloc,
+				        snd_gf1_mem_block_t * block)
+{
+	snd_gf1_mem_block_t *pblock, *nblock;
+
+	nblock = (snd_gf1_mem_block_t *) kmalloc(sizeof(snd_gf1_mem_block_t), GFP_KERNEL);
+	if (nblock == NULL)
+		return NULL;
+	*nblock = *block;
+	pblock = alloc->first;
+	while (pblock) {
+		if (pblock->ptr > nblock->ptr) {
+			nblock->prev = pblock->prev;
+			nblock->next = pblock;
+			pblock->prev = nblock;
+			if (pblock == alloc->first)
+				alloc->first = nblock;
+			else
+				nblock->prev->next = nblock;
+			up(&alloc->memory_mutex);
+			return 0;
+		}
+		pblock = pblock->next;
+	}
+	nblock->next = NULL;
+	if (alloc->last == NULL) {
+		nblock->prev = NULL;
+		alloc->first = alloc->last = nblock;
+	} else {
+		nblock->prev = alloc->last;
+		alloc->last->next = nblock;
+		alloc->last = nblock;
+	}
+	return nblock;
+}
+
+int snd_gf1_mem_xfree(snd_gf1_mem_t * alloc, snd_gf1_mem_block_t * block)
+{
+	if (block->share) {	/* ok.. shared block */
+		block->share--;
+		up(&alloc->memory_mutex);
+		return 0;
+	}
+	if (alloc->first == block) {
+		alloc->first = block->next;
+		if (block->next)
+			block->next->prev = NULL;
+	} else {
+		block->prev->next = block->next;
+		if (block->next)
+			block->next->prev = block->prev;
+	}
+	if (alloc->last == block) {
+		alloc->last = block->prev;
+		if (block->prev)
+			block->prev->next = NULL;
+	} else {
+		block->next->prev = block->prev;
+		if (block->prev)
+			block->prev->next = block->next;
+	}
+	if (block->name)
+		kfree(block->name);
+	kfree(block);
+	return 0;
+}
+
+snd_gf1_mem_block_t *snd_gf1_mem_look(snd_gf1_mem_t * alloc,
+				      unsigned int address)
+{
+	snd_gf1_mem_block_t *block;
+
+	for (block = alloc->first; block; block = block->next) {
+		if (block->ptr == address) {
+			return block;
+		}
+	}
+	return NULL;
+}
+
+snd_gf1_mem_block_t *snd_gf1_mem_share(snd_gf1_mem_t * alloc,
+				       unsigned int *share_id)
+{
+	snd_gf1_mem_block_t *block;
+
+	if (!share_id[0] && !share_id[1] &&
+	    !share_id[2] && !share_id[3])
+		return NULL;
+	for (block = alloc->first; block; block = block->next)
+		if (!memcmp(share_id, block->share_id, sizeof(share_id)))
+			return block;
+	return NULL;
+}
+
+static int snd_gf1_mem_find(snd_gf1_mem_t * alloc,
+			    snd_gf1_mem_block_t * block,
+			    unsigned int size, int w_16, int align)
+{
+	snd_gf1_bank_info_t *info = w_16 ? alloc->banks_16 : alloc->banks_8;
+	unsigned int idx, boundary;
+	int size1;
+	snd_gf1_mem_block_t *pblock;
+	unsigned int ptr1, ptr2;
+
+	align--;
+	if (w_16 && align < 1)
+		align = 1;
+	block->flags = w_16 ? SNDRV_GF1_MEM_BLOCK_16BIT : 0;
+	block->owner = SNDRV_GF1_MEM_OWNER_DRIVER;
+	block->share = 0;
+	block->share_id[0] = block->share_id[1] =
+	block->share_id[2] = block->share_id[3] = 0;
+	block->name = NULL;
+	block->prev = block->next = NULL;
+	for (pblock = alloc->first, idx = 0; pblock; pblock = pblock->next) {
+		while (pblock->ptr >= (boundary = info[idx].address + info[idx].size))
+			idx++;
+		while (pblock->ptr + pblock->size >= (boundary = info[idx].address + info[idx].size))
+			idx++;
+		ptr2 = boundary;
+		if (pblock->next) {
+			if (pblock->ptr + pblock->size == pblock->next->ptr)
+				continue;
+			if (pblock->next->ptr < boundary)
+				ptr2 = pblock->next->ptr;
+		}
+		ptr1 = (pblock->ptr + pblock->size + align) & ~align;
+		if (ptr1 >= ptr2)
+			continue;
+		size1 = ptr2 - ptr1;
+		if (size <= size1) {
+			block->ptr = ptr1;
+			block->size = size;
+			return 0;
+		}
+	}
+	while (++idx < 4) {
+		if (size <= info[idx].size) {
+			/* I assume that bank address is already aligned.. */
+			block->ptr = info[idx].address;
+			block->size = size;
+			return 0;
+		}
+	}
+	return -ENOMEM;
+}
+
+snd_gf1_mem_block_t *snd_gf1_mem_alloc(snd_gf1_mem_t * alloc, int owner,
+				       char *name, int size, int w_16, int align,
+				       unsigned int *share_id)
+{
+	snd_gf1_mem_block_t block, *nblock;
+
+	snd_gf1_mem_lock(alloc, 0);
+	if (share_id != NULL) {
+		nblock = snd_gf1_mem_share(alloc, share_id);
+		if (nblock != NULL) {
+			if (size != nblock->size) {
+				/* TODO: remove in the future */
+				snd_printk("snd_gf1_mem_alloc - share: sizes differ\n");
+				goto __std;
+			}
+			nblock->share++;
+			snd_gf1_mem_lock(alloc, 1);
+			return NULL;
+		}
+	}
+      __std:
+	if (snd_gf1_mem_find(alloc, &block, size, w_16, align) < 0) {
+		snd_gf1_mem_lock(alloc, 1);
+		return NULL;
+	}
+	if (share_id != NULL)
+		memcpy(&block.share_id, share_id, sizeof(block.share_id));
+	block.owner = owner;
+	block.name = snd_kmalloc_strdup(name, GFP_KERNEL);
+	nblock = snd_gf1_mem_xalloc(alloc, &block);
+	snd_gf1_mem_lock(alloc, 1);
+	return nblock;
+}
+
+int snd_gf1_mem_free(snd_gf1_mem_t * alloc, unsigned int address)
+{
+	int result;
+	snd_gf1_mem_block_t *block;
+
+	snd_gf1_mem_lock(alloc, 0);
+	if ((block = snd_gf1_mem_look(alloc, address)) != NULL) {
+		result = snd_gf1_mem_xfree(alloc, block);
+		snd_gf1_mem_lock(alloc, 1);
+		return result;
+	}
+	snd_gf1_mem_lock(alloc, 1);
+	return -EINVAL;
+}
+
+int snd_gf1_mem_init(snd_gus_card_t * gus)
+{
+	snd_gf1_mem_t *alloc;
+	snd_gf1_mem_block_t block;
+#ifdef CONFIG_SND_DEBUG
+	snd_info_entry_t *entry;
+#endif
+
+	alloc = &gus->gf1.mem_alloc;
+	init_MUTEX(&alloc->memory_mutex);
+	alloc->first = alloc->last = NULL;
+	if (!gus->gf1.memory)
+		return 0;
+
+	memset(&block, 0, sizeof(block));
+	block.owner = SNDRV_GF1_MEM_OWNER_DRIVER;
+	if (gus->gf1.enh_mode) {
+		block.ptr = 0;
+		block.size = 1024;
+		block.name = snd_kmalloc_strdup("InterWave LFOs", GFP_KERNEL);
+		if (snd_gf1_mem_xalloc(alloc, &block) == NULL)
+			return -ENOMEM;
+	}
+	block.ptr = gus->gf1.default_voice_address;
+	block.size = 4;
+	block.name = snd_kmalloc_strdup("Voice default (NULL's)", GFP_KERNEL);
+	if (snd_gf1_mem_xalloc(alloc, &block) == NULL)
+		return -ENOMEM;
+#ifdef CONFIG_SND_DEBUG
+	alloc->info_entry = NULL;
+	entry = snd_info_create_card_entry(gus->card, "gusmem", gus->card->proc_root);
+	if (entry) {
+		entry->content = SNDRV_INFO_CONTENT_TEXT;
+		entry->c.text.read_size = 256 * 1024;
+		entry->c.text.read = snd_gf1_mem_info_read;
+		entry->private_data = gus;
+		if (snd_info_register(entry) < 0) {
+			snd_info_free_entry(entry);
+			entry = NULL;
+		}
+	}
+	alloc->info_entry = entry;
+#endif
+	return 0;
+}
+
+int snd_gf1_mem_done(snd_gus_card_t * gus)
+{
+	snd_gf1_mem_t *alloc;
+	snd_gf1_mem_block_t *block, *nblock;
+
+	alloc = &gus->gf1.mem_alloc;
+	block = alloc->first;
+	while (block) {
+		nblock = block->next;
+		snd_gf1_mem_xfree(alloc, block);
+		block = nblock;
+	}
+#ifdef CONFIG_SND_DEBUG
+	if (alloc->info_entry)
+		snd_info_unregister(alloc->info_entry);
+#endif
+	return 0;
+}
+
+#ifdef CONFIG_SND_DEBUG
+static void snd_gf1_mem_info_read(snd_info_entry_t *entry, 
+				  snd_info_buffer_t * buffer)
+{
+	snd_gus_card_t *gus;
+	snd_gf1_mem_t *alloc;
+	snd_gf1_mem_block_t *block;
+	unsigned int total, used;
+	int i;
+
+	gus = snd_magic_cast(snd_gus_card_t, entry->private_data, return);
+	alloc = &gus->gf1.mem_alloc;
+	down(&alloc->memory_mutex);
+	snd_iprintf(buffer, "8-bit banks       : \n    ");
+	for (i = 0; i < 4; i++)
+		snd_iprintf(buffer, "0x%06x (%04ik)%s", alloc->banks_8[i].address, alloc->banks_8[i].size >> 10, i + 1 < 4 ? "," : "");
+	snd_iprintf(buffer, "\n"
+		    "16-bit banks      : \n    ");
+	for (i = total = 0; i < 4; i++) {
+		snd_iprintf(buffer, "0x%06x (%04ik)%s", alloc->banks_16[i].address, alloc->banks_16[i].size >> 10, i + 1 < 4 ? "," : "");
+		total += alloc->banks_16[i].size;
+	}
+	snd_iprintf(buffer, "\n");
+	used = 0;
+	for (block = alloc->first, i = 0; block; block = block->next, i++) {
+		used += block->size;
+		snd_iprintf(buffer, "Block %i at 0x%lx onboard 0x%x size %i (0x%x):\n", i, (long) block, block->ptr, block->size, block->size);
+		if (block->share ||
+		    block->share_id[0] || block->share_id[1] ||
+		    block->share_id[2] || block->share_id[3])
+			snd_iprintf(buffer, "  Share           : %i [id0 0x%x] [id1 0x%x] [id2 0x%x] [id3 0x%x]\n",
+				block->share,
+				block->share_id[0], block->share_id[1],
+				block->share_id[2], block->share_id[3]);
+		snd_iprintf(buffer, "  Flags           :%s\n",
+		block->flags & SNDRV_GF1_MEM_BLOCK_16BIT ? " 16-bit" : "");
+		snd_iprintf(buffer, "  Owner           : ");
+		switch (block->owner) {
+		case SNDRV_GF1_MEM_OWNER_DRIVER:
+			snd_iprintf(buffer, "driver - %s\n", block->name);
+			break;
+		case SNDRV_GF1_MEM_OWNER_WAVE_SIMPLE:
+			snd_iprintf(buffer, "SIMPLE wave\n");
+			break;
+		case SNDRV_GF1_MEM_OWNER_WAVE_GF1:
+			snd_iprintf(buffer, "GF1 wave\n");
+			break;
+		case SNDRV_GF1_MEM_OWNER_WAVE_IWFFFF:
+			snd_iprintf(buffer, "IWFFFF wave\n");
+			break;
+		default:
+			snd_iprintf(buffer, "unknown\n");
+		}
+	}
+	snd_iprintf(buffer, "  Total: memory = %i, used = %i, free = %i\n",
+		    total, used, total - used);
+	up(&alloc->memory_mutex);
+#if 0
+	ultra_iprintf(buffer, "  Verify: free = %i, max 8-bit block = %i, max 16-bit block = %i\n",
+		      ultra_memory_free_size(card, &card->gf1.mem_alloc),
+		  ultra_memory_free_block(card, &card->gf1.mem_alloc, 0),
+		 ultra_memory_free_block(card, &card->gf1.mem_alloc, 1));
+#endif
+}
+#endif
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/gus_mem_proc.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/gus_mem_proc.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/gus_mem_proc.c	(revision 91)
@@ -0,0 +1,182 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  GUS's memory access via proc filesystem
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <sound/driver.h>
+#include <sound/gus.h>
+#include <sound/info.h>
+
+typedef struct gus_proc_private {
+	int rom;		/* data are in ROM */
+	unsigned int address;
+	unsigned int size;
+	snd_gus_card_t * gus;
+} gus_proc_private_t;
+
+static long snd_gf1_mem_proc_dump(snd_info_entry_t *entry, void *file_private_data,
+			          struct file *file, char *buf, long count)
+{
+	long size;
+	gus_proc_private_t *priv = snd_magic_cast(gus_proc_private_t, entry->private_data, return -ENXIO);
+	snd_gus_card_t *gus = priv->gus;
+	int err;
+
+	size = count;
+	if (file->f_pos + size > priv->size)
+		size = (long)priv->size - file->f_pos;
+	if (size > 0) {
+		if ((err = snd_gus_dram_read(gus, buf, file->f_pos, size, priv->rom)) < 0)
+			return err;
+		file->f_pos += size;
+		return size;
+	}
+	return 0;
+}			
+
+#ifdef TARGET_OS2
+static int64_t snd_gf1_mem_proc_llseek(snd_info_entry_t *entry,
+					void *private_file_data,
+					struct file *file,
+					int64_t offset,
+					int orig)
+#else
+static long long snd_gf1_mem_proc_llseek(snd_info_entry_t *entry,
+					void *private_file_data,
+					struct file *file,
+					long long offset,
+					int orig)
+#endif
+{
+	gus_proc_private_t *priv = snd_magic_cast(gus_proc_private_t, entry->private_data, return -ENXIO);
+
+	switch (orig) {
+	case 0:	/* SEEK_SET */
+		file->f_pos = offset;
+		break;
+	case 1:	/* SEEK_CUR */
+		file->f_pos += offset;
+		break;
+	case 2: /* SEEK_END */
+		file->f_pos = priv->size - offset;
+		break;
+	default:
+		return -EINVAL;
+	}
+	if (file->f_pos > priv->size)
+		file->f_pos = priv->size;
+	return file->f_pos;
+}
+
+static void snd_gf1_mem_proc_free(snd_info_entry_t *entry)
+{
+	gus_proc_private_t *priv = snd_magic_cast(gus_proc_private_t, entry->private_data, return);
+	snd_magic_kfree(priv);
+}
+
+#ifdef TARGET_OS2
+static struct snd_info_entry_ops snd_gf1_mem_proc_ops = {
+        0,0,
+	snd_gf1_mem_proc_dump,0,
+	snd_gf1_mem_proc_llseek,0,0,0
+};
+#else
+static struct snd_info_entry_ops snd_gf1_mem_proc_ops = {
+	read: snd_gf1_mem_proc_dump,
+	llseek: snd_gf1_mem_proc_llseek,
+};
+#endif
+
+int snd_gf1_mem_proc_init(snd_gus_card_t * gus)
+{
+	int idx;
+	char name[16];
+	gus_proc_private_t *priv;
+	snd_info_entry_t *entry;
+
+	memset(&gus->gf1.rom_entries, 0, sizeof(gus->gf1.rom_entries));
+	memset(&gus->gf1.ram_entries, 0, sizeof(gus->gf1.ram_entries));
+	for (idx = 0; idx < 4; idx++) {
+		if (gus->gf1.mem_alloc.banks_8[idx].size > 0) {
+			priv = snd_magic_kcalloc(gus_proc_private_t, 0, GFP_KERNEL);
+			if (priv == NULL) {
+				snd_gf1_mem_proc_done(gus);
+				return -ENOMEM;
+			}
+			priv->gus = gus;
+			sprintf(name, "gus-ram-%i", idx);
+			entry = snd_info_create_card_entry(gus->card, name, gus->card->proc_root);
+			if (entry) {
+				entry->content = SNDRV_INFO_CONTENT_DATA;
+				entry->private_data = priv;
+				entry->private_free = snd_gf1_mem_proc_free;
+				entry->c.ops = &snd_gf1_mem_proc_ops;
+				priv->address = gus->gf1.mem_alloc.banks_8[idx].address;
+				priv->size = entry->size = gus->gf1.mem_alloc.banks_8[idx].size;
+				if (snd_info_register(entry) < 0) {
+					snd_info_free_entry(entry);
+					entry = NULL;
+				}
+			}
+			gus->gf1.ram_entries[idx] = entry;
+		}
+	}
+	for (idx = 0; idx < 4; idx++) {
+		if (gus->gf1.rom_present & (1 << idx)) {
+			priv = snd_magic_kcalloc(gus_proc_private_t, 0, GFP_KERNEL);
+			if (priv == NULL) {
+				snd_gf1_mem_proc_done(gus);
+				return -ENOMEM;
+			}
+			priv->rom = 1;
+			priv->gus = gus;
+			sprintf(name, "gus-rom-%i", idx);
+			entry = snd_info_create_card_entry(gus->card, name, gus->card->proc_root);
+			if (entry) {
+				entry->content = SNDRV_INFO_CONTENT_DATA;
+				entry->private_data = priv;
+				entry->private_free = snd_gf1_mem_proc_free;
+				entry->c.ops = &snd_gf1_mem_proc_ops;
+				priv->address = idx * 4096 * 1024;
+				priv->size = entry->size = gus->gf1.rom_memory;
+				if (snd_info_register(entry) < 0) {
+					snd_info_free_entry(entry);
+					entry = NULL;
+				}
+			}
+			gus->gf1.rom_entries[idx] = entry;
+		}
+	}
+	return 0;
+}
+
+int snd_gf1_mem_proc_done(snd_gus_card_t * gus)
+{
+	int idx;
+
+	for (idx = 0; idx < 4; idx++) {
+		if (gus->gf1.ram_entries[idx])
+			snd_info_unregister(gus->gf1.ram_entries[idx]);
+	}
+	for (idx = 0; idx < 4; idx++) {
+		if (gus->gf1.rom_entries[idx])
+			snd_info_unregister(gus->gf1.rom_entries[idx]);
+	}
+	return 0;
+}
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/gus_mixer.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/gus_mixer.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/gus_mixer.c	(revision 91)
@@ -0,0 +1,217 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  Routines for control of ICS 2101 chip and "mixer" in GF1 chip
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <sound/driver.h>
+#include <sound/control.h>
+#include <sound/gus.h>
+
+#define chip_t snd_gus_card_t
+
+/*
+ *
+ */
+
+#ifdef TARGET_OS2
+#define GF1_SINGLE(xname, xindex, shift, invert) \
+{ SNDRV_CTL_ELEM_IFACE_MIXER, 0,0, xname, xindex, \
+  0, 0, snd_gf1_info_single, \
+  snd_gf1_get_single, snd_gf1_put_single, \
+  shift | (invert << 8) }
+#else
+#define GF1_SINGLE(xname, xindex, shift, invert) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
+  info: snd_gf1_info_single, \
+  get: snd_gf1_get_single, put: snd_gf1_put_single, \
+  private_value: shift | (invert << 8) }
+#endif
+
+static int snd_gf1_info_single(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_gf1_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	snd_gus_card_t *gus = snd_kcontrol_chip(kcontrol);
+	int shift = kcontrol->private_value & 0xff;
+	int invert = (kcontrol->private_value >> 8) & 1;
+	
+	ucontrol->value.integer.value[0] = (gus->mix_cntrl_reg >> shift) & 1;
+	if (invert)
+		ucontrol->value.integer.value[0] ^= 1;
+	return 0;
+}
+
+static int snd_gf1_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	snd_gus_card_t *gus = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int shift = kcontrol->private_value & 0xff;
+	int invert = (kcontrol->private_value >> 8) & 1;
+	int change;
+	unsigned char oval, nval;
+	
+	nval = ucontrol->value.integer.value[0] & 1;
+	if (invert)
+		nval ^= 1;
+	nval <<= shift;
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	oval = gus->mix_cntrl_reg;
+	nval = (oval & ~(1 << shift)) | nval;
+	change = nval != oval;
+	outb(gus->mix_cntrl_reg = nval, GUSP(gus, MIXCNTRLREG));
+	outb(gus->gf1.active_voice = 0, GUSP(gus, GF1PAGE));
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+	return change;
+}
+
+#ifdef TARGET_OS2
+#define ICS_DOUBLE(xname, xindex, addr) \
+{ SNDRV_CTL_ELEM_IFACE_MIXER, 0,0, xname, xindex, \
+  0, 0, snd_ics_info_double, \
+  snd_ics_get_double, snd_ics_put_double, \
+  addr }
+#else
+#define ICS_DOUBLE(xname, xindex, addr) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
+  info: snd_ics_info_double, \
+  get: snd_ics_get_double, put: snd_ics_put_double, \
+  private_value: addr }
+#endif
+
+static int snd_ics_info_double(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 = 127;
+	return 0;
+}
+
+static int snd_ics_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	snd_gus_card_t *gus = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int addr = kcontrol->private_value & 0xff;
+	unsigned char left, right;
+	
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	left = gus->gf1.ics_regs[addr][0];
+	right = gus->gf1.ics_regs[addr][1];
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+	ucontrol->value.integer.value[0] = left & 127;
+	ucontrol->value.integer.value[1] = right & 127;
+	return 0;
+}
+
+static int snd_ics_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	snd_gus_card_t *gus = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int addr = kcontrol->private_value & 0xff;
+	int change;
+	unsigned char val1, val2, oval1, oval2, tmp;
+	
+	val1 = ucontrol->value.integer.value[0] & 127;
+	val2 = ucontrol->value.integer.value[1] & 127;
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	oval1 = gus->gf1.ics_regs[addr][0];
+	oval2 = gus->gf1.ics_regs[addr][1];
+	change = val1 != oval1 || val2 != oval2;
+	gus->gf1.ics_regs[addr][0] = val1;
+	gus->gf1.ics_regs[addr][1] = val2;
+	if (gus->ics_flag && gus->ics_flipped &&
+	    (addr == SNDRV_ICS_GF1_DEV || addr == SNDRV_ICS_MASTER_DEV)) {
+		tmp = val1;
+		val1 = val2;
+		val2 = tmp;
+	}
+	addr <<= 3;
+	outb(addr | 0, GUSP(gus, MIXCNTRLPORT));
+	outb(1, GUSP(gus, MIXDATAPORT));
+	outb(addr | 2, GUSP(gus, MIXCNTRLPORT));
+	outb((unsigned char) val1, GUSP(gus, MIXDATAPORT));
+	outb(addr | 1, GUSP(gus, MIXCNTRLPORT));
+	outb(2, GUSP(gus, MIXDATAPORT));
+	outb(addr | 3, GUSP(gus, MIXCNTRLPORT));
+	outb((unsigned char) val2, GUSP(gus, MIXDATAPORT));
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+	return change;
+}
+
+#define GF1_CONTROLS (sizeof(snd_gf1_controls)/sizeof(snd_kcontrol_new_t))
+
+static snd_kcontrol_new_t snd_gf1_controls[] = {
+GF1_SINGLE("Master Playback Switch", 0, 1, 1),
+GF1_SINGLE("Line Switch", 0, 0, 1),
+GF1_SINGLE("Mic Switch", 0, 2, 0)
+};
+
+#define ICS_CONTROLS (sizeof(snd_ics_controls)/sizeof(snd_kcontrol_new_t))
+
+static snd_kcontrol_new_t snd_ics_controls[] = {
+GF1_SINGLE("Master Playback Switch", 0, 1, 1),
+ICS_DOUBLE("Master Playback Volume", 0, SNDRV_ICS_MASTER_DEV),
+ICS_DOUBLE("Synth Playback Volume", 0, SNDRV_ICS_GF1_DEV),
+GF1_SINGLE("Line Switch", 0, 0, 1),
+ICS_DOUBLE("Line Playback Volume", 0, SNDRV_ICS_LINE_DEV),
+GF1_SINGLE("Mic Switch", 0, 2, 0),
+ICS_DOUBLE("Mic Playback Volume", 0, SNDRV_ICS_MIC_DEV),
+ICS_DOUBLE("CD Playback Volume", 0, SNDRV_ICS_CD_DEV)
+};
+
+int snd_gf1_new_mixer(snd_gus_card_t * gus)
+{
+	snd_card_t *card;
+	int idx, err, max;
+
+	snd_assert(gus != NULL, return -EINVAL);
+	card = gus->card;
+	snd_assert(card != NULL, return -EINVAL);
+
+	if (gus->ics_flag)
+		snd_component_add(card, "ICS2101");
+	if (card->mixername[0] == '\0') {
+		strcpy(card->mixername, gus->ics_flag ? "GF1,ICS2101" : "GF1");
+	} else {
+		if (gus->ics_flag)
+			strcat(card->mixername, ",ICS2101");
+		strcat(card->mixername, ",GF1");
+	}
+
+	if (!gus->ics_flag) {
+		max = gus->ess_flag ? 1 : GF1_CONTROLS;
+		for (idx = 0; idx < max; idx++) {
+			if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_gf1_controls[idx], gus))) < 0)
+				return err;
+		}
+	} else {
+		for (idx = 0; idx < ICS_CONTROLS; idx++) {
+			if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_ics_controls[idx], gus))) < 0)
+				return err;
+		}
+	}
+	return 0;
+}
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/gus_pcm.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/gus_pcm.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/gus_pcm.c	(revision 91)
@@ -0,0 +1,972 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  Routines for control of GF1 chip (PCM things)
+ *
+ *  InterWave chips supports interleaved DMA, but this feature isn't used in
+ *  this code.
+ *  
+ *  This code emulates autoinit DMA transfer for playback, recording by GF1
+ *  chip doesn't support autoinit DMA.
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <sound/driver.h>
+#include <sound/control.h>
+#include <sound/gus.h>
+#include "gus_tables.h"
+
+#define chip_t snd_gus_card_t
+
+/* maximum rate */
+
+#define SNDRV_GF1_PCM_RATE		48000
+
+#define SNDRV_GF1_PCM_PFLG_NONE		0
+#define SNDRV_GF1_PCM_PFLG_ACTIVE	(1<<0)
+#define SNDRV_GF1_PCM_PFLG_NEUTRAL	(2<<0)
+
+typedef struct {
+	snd_gus_card_t * gus;
+	snd_pcm_substream_t * substream;
+	spinlock_t lock;
+	int voices;
+	snd_gus_voice_t *pvoices[2];
+	unsigned int memory;
+	unsigned short flags;
+	unsigned char voice_ctrl, ramp_ctrl;
+	unsigned int bpos;
+	unsigned int blocks;
+	unsigned int block_size;
+	unsigned int dma_size;
+	wait_queue_head_t sleep;
+	atomic_t dma_count;
+	int final_volume;
+} gus_pcm_private_t;
+
+static int snd_gf1_pcm_use_dma = 1;
+
+static void snd_gf1_pcm_block_change_ack(snd_gus_card_t * gus, void *private_data)
+{
+	gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, private_data, return);
+
+	if (pcmp) {
+		atomic_dec(&pcmp->dma_count);
+		wake_up(&pcmp->sleep);
+	}
+}
+
+static int snd_gf1_pcm_block_change(snd_pcm_substream_t * substream,
+				    unsigned int offset,
+				    unsigned int addr,
+				    unsigned int count)
+{
+	snd_gf1_dma_block_t block;
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, runtime->private_data, return -ENXIO);
+
+	count += offset & 31;
+	offset &= ~31;
+	// snd_printk("block change - offset = 0x%x, count = 0x%x\n", offset, count);
+	memset(&block, 0, sizeof(block));
+	block.cmd = SNDRV_GF1_DMA_IRQ;
+	if (snd_pcm_format_unsigned(runtime->format))
+		block.cmd |= SNDRV_GF1_DMA_UNSIGNED;
+	if (snd_pcm_format_width(runtime->format) == 16)
+		block.cmd |= SNDRV_GF1_DMA_16BIT;
+	block.addr = addr & ~31;
+	block.buffer = runtime->dma_area + offset;
+	block.count = count;
+	block.private_data = pcmp;
+	block.ack = snd_gf1_pcm_block_change_ack;
+	if (!snd_gf1_dma_transfer_block(pcmp->gus, &block, 0, 0))
+		atomic_inc(&pcmp->dma_count);
+	return 0;
+}
+
+static void snd_gf1_pcm_trigger_up(snd_pcm_substream_t * substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, runtime->private_data, return);
+	snd_gus_card_t * gus = pcmp->gus;
+	unsigned long flags;
+	unsigned char voice_ctrl, ramp_ctrl;
+	unsigned short rate;
+	unsigned int curr, begin, end;
+	unsigned short vol;
+	unsigned char pan;
+	unsigned int voice;
+
+	if (substream == NULL)
+		return;
+	spin_lock_irqsave(&pcmp->lock, flags);
+	if (pcmp->flags & SNDRV_GF1_PCM_PFLG_ACTIVE) {
+		spin_unlock_irqrestore(&pcmp->lock, flags);
+		return;
+	}
+	pcmp->flags |= SNDRV_GF1_PCM_PFLG_ACTIVE;
+	pcmp->final_volume = 0;
+	spin_unlock_irqrestore(&pcmp->lock, flags);
+	rate = snd_gf1_translate_freq(gus, runtime->rate << 4);
+	/* enable WAVE IRQ */
+	voice_ctrl = snd_pcm_format_width(runtime->format) == 16 ? 0x24 : 0x20;
+	/* enable RAMP IRQ + rollover */
+	ramp_ctrl = 0x24;
+	if (pcmp->blocks == 1) {
+		voice_ctrl |= 0x08;	/* loop enable */
+		ramp_ctrl &= ~0x04;	/* disable rollover */
+	}
+	for (voice = 0; voice < pcmp->voices; voice++) {
+		begin = pcmp->memory + voice * (pcmp->dma_size / runtime->channels);
+		curr = begin + (pcmp->bpos * pcmp->block_size) / runtime->channels;
+		end = curr + (pcmp->block_size / runtime->channels);
+		end -= snd_pcm_format_width(runtime->format) == 16 ? 2 : 1;
+		// snd_printk("init: curr=0x%x, begin=0x%x, end=0x%x, ctrl=0x%x, ramp=0x%x, rate=0x%x\n", curr, begin, end, voice_ctrl, ramp_ctrl, rate);
+		pan = runtime->channels == 2 ? (!voice ? 1 : 14) : 8;
+		vol = !voice ? gus->gf1.pcm_volume_level_left : gus->gf1.pcm_volume_level_right;
+		spin_lock_irqsave(&gus->reg_lock, flags);
+		snd_gf1_select_voice(gus, pcmp->pvoices[voice]->number);
+		snd_gf1_write8(gus, SNDRV_GF1_VB_PAN, pan);
+		snd_gf1_write16(gus, SNDRV_GF1_VW_FREQUENCY, rate);
+		snd_gf1_write_addr(gus, SNDRV_GF1_VA_START, begin << 4, voice_ctrl & 4);
+		snd_gf1_write_addr(gus, SNDRV_GF1_VA_END, end << 4, voice_ctrl & 4);
+		snd_gf1_write_addr(gus, SNDRV_GF1_VA_CURRENT, curr << 4, voice_ctrl & 4);
+		snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, SNDRV_GF1_MIN_VOLUME << 4);
+		snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_RATE, 0x2f);
+		snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_START, SNDRV_GF1_MIN_OFFSET);
+		snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_END, vol >> 8);
+		snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, ramp_ctrl);
+		if (!gus->gf1.enh_mode) {
+			snd_gf1_delay(gus);
+			snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, ramp_ctrl);
+		}
+		spin_unlock_irqrestore(&gus->reg_lock, flags);
+	}
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	for (voice = 0; voice < pcmp->voices; voice++) {
+		snd_gf1_select_voice(gus, pcmp->pvoices[voice]->number);
+		if (gus->gf1.enh_mode)
+			snd_gf1_write8(gus, SNDRV_GF1_VB_MODE, 0x00);	/* deactivate voice */
+		snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice_ctrl);
+		voice_ctrl &= ~0x20;
+	}
+	voice_ctrl |= 0x20;
+	if (!gus->gf1.enh_mode) {
+		snd_gf1_delay(gus);
+		for (voice = 0; voice < pcmp->voices; voice++) {
+			snd_gf1_select_voice(gus, pcmp->pvoices[voice]->number);
+			snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice_ctrl);
+			voice_ctrl &= ~0x20;	/* disable IRQ for next voice */
+		}
+	}
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+}
+
+static void snd_gf1_pcm_interrupt_wave(snd_gus_card_t * gus, snd_gus_voice_t *pvoice)
+{
+	gus_pcm_private_t * pcmp;
+	snd_pcm_runtime_t * runtime;
+	unsigned char voice_ctrl, ramp_ctrl;
+	int idx;
+	unsigned int end, step;
+
+	if (!pvoice->private_data) {
+		snd_printd("snd_gf1_pcm: unknown wave irq?\n");
+		snd_gf1_smart_stop_voice(gus, pvoice->number);
+		return;
+	}
+	pcmp = snd_magic_cast(gus_pcm_private_t, pvoice->private_data, return);
+	if (pcmp == NULL) {
+		snd_printd("snd_gf1_pcm: unknown wave irq?\n");
+		snd_gf1_smart_stop_voice(gus, pvoice->number);
+		return;
+	}		
+	gus = pcmp->gus;
+	runtime = pcmp->substream->runtime;
+
+	spin_lock(&gus->reg_lock);
+	snd_gf1_select_voice(gus, pvoice->number);
+	voice_ctrl = snd_gf1_read8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL) & ~0x8b;
+	ramp_ctrl = (snd_gf1_read8(gus, SNDRV_GF1_VB_VOLUME_CONTROL) & ~0xa4) | 0x03;
+#if 0
+	snd_gf1_select_voice(gus, pvoice->number);
+	printk("position = 0x%x\n", (snd_gf1_read_addr(gus, SNDRV_GF1_VA_CURRENT, voice_ctrl & 4) >> 4));
+	snd_gf1_select_voice(gus, pcmp->pvoices[1]->number);
+	printk("position = 0x%x\n", (snd_gf1_read_addr(gus, SNDRV_GF1_VA_CURRENT, voice_ctrl & 4) >> 4));
+	snd_gf1_select_voice(gus, pvoice->number);
+#endif
+	pcmp->bpos++;
+	pcmp->bpos %= pcmp->blocks;
+	if (pcmp->bpos + 1 >= pcmp->blocks) {	/* last block? */
+		voice_ctrl |= 0x08;	/* enable loop */
+	} else {
+		ramp_ctrl |= 0x04;	/* enable rollover */
+	}
+	end = pcmp->memory + (((pcmp->bpos + 1) * pcmp->block_size) / runtime->channels);
+	end -= voice_ctrl & 4 ? 2 : 1;
+	step = pcmp->dma_size / runtime->channels;
+	voice_ctrl |= 0x20;
+	if (!pcmp->final_volume) {
+		ramp_ctrl |= 0x20;
+		ramp_ctrl &= ~0x03;
+	}
+	for (idx = 0; idx < pcmp->voices; idx++, end += step) {
+		snd_gf1_select_voice(gus, pcmp->pvoices[idx]->number);
+		snd_gf1_write_addr(gus, SNDRV_GF1_VA_END, end << 4, voice_ctrl & 4);
+		snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice_ctrl);
+		snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, ramp_ctrl);
+		voice_ctrl &= ~0x20;
+	}
+	if (!gus->gf1.enh_mode) {
+		snd_gf1_delay(gus);
+		voice_ctrl |= 0x20;
+		for (idx = 0; idx < pcmp->voices; idx++) {
+			snd_gf1_select_voice(gus, pcmp->pvoices[idx]->number);
+			snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice_ctrl);
+			snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, ramp_ctrl);
+			voice_ctrl &= ~0x20;
+		}
+	}
+	spin_unlock(&gus->reg_lock);
+
+	snd_pcm_period_elapsed(pcmp->substream);
+#if 0
+	if ((runtime->flags & SNDRV_PCM_FLG_MMAP) &&
+	    *runtime->state == SNDRV_PCM_STATE_RUNNING) {
+		end = pcmp->bpos * pcmp->block_size;
+		if (runtime->channels > 1) {
+			snd_gf1_pcm_block_change(pcmp->substream, end, pcmp->memory + (end / 2), pcmp->block_size / 2);
+			snd_gf1_pcm_block_change(pcmp->substream, end + (pcmp->block_size / 2), pcmp->memory + (pcmp->dma_size / 2) + (end / 2), pcmp->block_size / 2);
+		} else {
+			snd_gf1_pcm_block_change(pcmp->substream, end, pcmp->memory + end, pcmp->block_size);
+		}
+	}
+#endif
+}
+
+static void snd_gf1_pcm_interrupt_volume(snd_gus_card_t * gus, snd_gus_voice_t * pvoice)
+{
+	unsigned short vol;
+	int cvoice;
+	gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, pvoice->private_data, return);
+
+	/* stop ramp, but leave rollover bit untouched */
+	spin_lock(&gus->reg_lock);
+	snd_gf1_select_voice(gus, pvoice->number);
+	snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL);
+	spin_unlock(&gus->reg_lock);
+	if (pcmp == NULL)
+		return;
+	/* are we active? */
+	if (!(pcmp->flags & SNDRV_GF1_PCM_PFLG_ACTIVE))
+		return;
+	/* load real volume - better precision */
+	cvoice = pcmp->pvoices[0] == pvoice ? 0 : 1;
+	if (pcmp->substream == NULL)
+		return;
+	vol = !cvoice ? gus->gf1.pcm_volume_level_left : gus->gf1.pcm_volume_level_right;
+	spin_lock(&gus->reg_lock);
+	snd_gf1_select_voice(gus, pvoice->number);
+	snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, vol);
+	pcmp->final_volume = 1;
+	spin_unlock(&gus->reg_lock);
+}
+
+static void snd_gf1_pcm_volume_change(snd_gus_card_t * gus)
+{
+}
+
+static int snd_gf1_pcm_poke_block(snd_gus_card_t *gus, unsigned char *buf,
+				  unsigned int pos, unsigned int count,
+				  int w16, int invert)
+{
+	unsigned int len;
+	unsigned long flags;
+
+	// printk("poke block; buf = 0x%x, pos = %i, count = %i, port = 0x%x\n", (int)buf, pos, count, gus->gf1.port);
+	while (count > 0) {
+		len = count;
+		if (len > 512)		/* limit, to allow IRQ */
+			len = 512;
+		count -= len;
+		if (gus->interwave) {
+			spin_lock_irqsave(&gus->reg_lock, flags);
+			snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x01 | (invert ? 0x08 : 0x00));
+			snd_gf1_dram_addr(gus, pos);
+			if (w16) {
+				outb(SNDRV_GF1_GW_DRAM_IO16, GUSP(gus, GF1REGSEL));
+				outsw(GUSP(gus, GF1DATALOW), buf, len >> 1);
+			} else {
+				outsb(GUSP(gus, DRAM), buf, len);
+			}
+			spin_unlock_irqrestore(&gus->reg_lock, flags);
+			buf += 512;
+			pos += 512;
+		} else {
+			invert = invert ? 0x80 : 0x00;
+			if (w16) {
+				len >>= 1;
+				while (len--) {
+					snd_gf1_poke(gus, pos++, *buf++);
+					snd_gf1_poke(gus, pos++, *buf++ ^ invert);
+				}
+			} else {
+				while (len--)
+					snd_gf1_poke(gus, pos++, *buf++ ^ invert);
+			}
+		}
+		schedule_timeout(1);
+		if (signal_pending(current))
+			return -EAGAIN;
+	}
+	return 0;
+}
+
+static int snd_gf1_pcm_playback_copy(snd_pcm_substream_t *substream,
+				     int voice,
+				     snd_pcm_uframes_t pos,
+				     void *src,
+				     snd_pcm_uframes_t count)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, runtime->private_data, return -ENXIO);
+	unsigned int bpos, len;
+	
+	bpos = samples_to_bytes(runtime, pos) + (voice * (pcmp->dma_size / 2));
+	len = samples_to_bytes(runtime, count);
+	snd_assert(bpos <= pcmp->dma_size, return -EIO);
+	snd_assert(bpos + len <= pcmp->dma_size, return -EIO);
+	if (copy_from_user(runtime->dma_area + bpos, src, len))
+		return -EFAULT;
+	if (snd_gf1_pcm_use_dma && len > 32) {
+		return snd_gf1_pcm_block_change(substream, bpos, pcmp->memory + bpos, len);
+	} else {
+		snd_gus_card_t *gus = pcmp->gus;
+		int err, w16, invert;
+
+		w16 = (snd_pcm_format_width(runtime->format) == 16);
+		invert = snd_pcm_format_unsigned(runtime->format);
+		if ((err = snd_gf1_pcm_poke_block(gus, runtime->dma_area + bpos, pcmp->memory + bpos, len, w16, invert)) < 0)
+			return err;
+	}
+	return 0;
+}
+
+static int snd_gf1_pcm_playback_silence(snd_pcm_substream_t *substream,
+					int voice,
+					snd_pcm_uframes_t pos,
+					snd_pcm_uframes_t count)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, runtime->private_data, return -ENXIO);
+	unsigned int bpos, len;
+	
+	bpos = samples_to_bytes(runtime, pos) + (voice * (pcmp->dma_size / 2));
+	len = samples_to_bytes(runtime, count);
+	snd_assert(bpos <= pcmp->dma_size, return -EIO);
+	snd_assert(bpos + len <= pcmp->dma_size, return -EIO);
+	snd_pcm_format_set_silence(runtime->format, runtime->dma_area + bpos, count);
+	if (snd_gf1_pcm_use_dma && len > 32) {
+		return snd_gf1_pcm_block_change(substream, bpos, pcmp->memory + bpos, len);
+	} else {
+		snd_gus_card_t *gus = pcmp->gus;
+		int err, w16, invert;
+
+		w16 = (snd_pcm_format_width(runtime->format) == 16);
+		invert = snd_pcm_format_unsigned(runtime->format);
+		if ((err = snd_gf1_pcm_poke_block(gus, runtime->dma_area + bpos, pcmp->memory + bpos, len, w16, invert)) < 0)
+			return err;
+	}
+	return 0;
+}
+
+static int snd_gf1_pcm_playback_hw_params(snd_pcm_substream_t * substream,
+					  snd_pcm_hw_params_t * hw_params)
+{
+	snd_gus_card_t *gus = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, runtime->private_data, return -ENXIO);
+	int err;
+	
+	if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
+		return err;
+	if (err > 0) {	/* change */
+		snd_gf1_mem_block_t *block;
+		if (pcmp->memory > 0) {
+			snd_gf1_mem_free(&gus->gf1.mem_alloc, pcmp->memory);
+			pcmp->memory = 0;
+		}
+		if ((block = snd_gf1_mem_alloc(&gus->gf1.mem_alloc,
+		                               SNDRV_GF1_MEM_OWNER_DRIVER,
+					       "GF1 PCM",
+		                               runtime->dma_bytes, 1, 32,
+		                               NULL)) == NULL)
+			return -ENOMEM;
+		pcmp->memory = block->ptr;
+	}
+	pcmp->voices = params_channels(hw_params);
+	if (pcmp->pvoices[0] == NULL) {
+		if ((pcmp->pvoices[0] = snd_gf1_alloc_voice(pcmp->gus, SNDRV_GF1_VOICE_TYPE_PCM, 0, 0)) == NULL)
+			return -ENOMEM;
+		pcmp->pvoices[0]->handler_wave = snd_gf1_pcm_interrupt_wave;
+		pcmp->pvoices[0]->handler_volume = snd_gf1_pcm_interrupt_volume;
+		pcmp->pvoices[0]->volume_change = snd_gf1_pcm_volume_change;
+		pcmp->pvoices[0]->private_data = pcmp;
+	}
+	if (pcmp->voices > 1 && pcmp->pvoices[1] == NULL) {
+		if ((pcmp->pvoices[1] = snd_gf1_alloc_voice(pcmp->gus, SNDRV_GF1_VOICE_TYPE_PCM, 0, 0)) == NULL)
+			return -ENOMEM;
+		pcmp->pvoices[1]->handler_wave = snd_gf1_pcm_interrupt_wave;
+		pcmp->pvoices[1]->handler_volume = snd_gf1_pcm_interrupt_volume;
+		pcmp->pvoices[1]->volume_change = snd_gf1_pcm_volume_change;
+		pcmp->pvoices[1]->private_data = pcmp;
+	} else if (pcmp->voices == 1) {
+		if (pcmp->pvoices[1]) {
+			snd_gf1_free_voice(pcmp->gus, pcmp->pvoices[1]);
+			pcmp->pvoices[1] = NULL;
+		}
+	}
+	return 0;
+}
+
+static int snd_gf1_pcm_playback_hw_free(snd_pcm_substream_t * substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, runtime->private_data, return -ENXIO);
+
+	snd_pcm_lib_free_pages(substream);
+	if (pcmp->pvoices[0]) {
+		snd_gf1_free_voice(pcmp->gus, pcmp->pvoices[0]);
+		pcmp->pvoices[0] = NULL;
+	}
+	if (pcmp->pvoices[1]) {
+		snd_gf1_free_voice(pcmp->gus, pcmp->pvoices[1]);
+		pcmp->pvoices[1] = NULL;
+	}
+	if (pcmp->memory > 0) {
+		snd_gf1_mem_free(&pcmp->gus->gf1.mem_alloc, pcmp->memory);
+		pcmp->memory = 0;
+	}
+	return 0;
+}
+
+static int snd_gf1_pcm_playback_prepare(snd_pcm_substream_t * substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, runtime->private_data, return -ENXIO);
+
+	pcmp->bpos = 0;
+	pcmp->dma_size = snd_pcm_lib_buffer_bytes(substream);
+	pcmp->block_size = snd_pcm_lib_period_bytes(substream);
+	pcmp->blocks = pcmp->dma_size / pcmp->block_size;
+	return 0;
+}
+
+static int snd_gf1_pcm_playback_trigger(snd_pcm_substream_t * substream,
+					int cmd)
+{
+	snd_gus_card_t *gus = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, runtime->private_data, return -ENXIO);
+	int voice;
+
+	if (cmd == SNDRV_PCM_TRIGGER_START) {
+		snd_gf1_pcm_trigger_up(substream);
+	} else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+		spin_lock(&pcmp->lock);
+		pcmp->flags &= ~SNDRV_GF1_PCM_PFLG_ACTIVE;
+		spin_unlock(&pcmp->lock);
+		voice = pcmp->pvoices[0]->number;
+		snd_gf1_stop_voices(gus, voice, voice);
+		if (pcmp->pvoices[1]) {
+			voice = pcmp->pvoices[1]->number;
+			snd_gf1_stop_voices(gus, voice, voice);
+		}
+	} else {
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static snd_pcm_uframes_t snd_gf1_pcm_playback_pointer(snd_pcm_substream_t * substream)
+{
+	snd_gus_card_t *gus = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, runtime->private_data, return -ENXIO);
+	unsigned int pos;
+	unsigned char voice_ctrl;
+
+	pos = 0;
+	spin_lock(&gus->reg_lock);
+	if (pcmp->flags & SNDRV_GF1_PCM_PFLG_ACTIVE) {
+		snd_gf1_select_voice(gus, pcmp->pvoices[0]->number);
+		voice_ctrl = snd_gf1_read8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL);
+		pos = (snd_gf1_read_addr(gus, SNDRV_GF1_VA_CURRENT, voice_ctrl & 4) >> 4) - pcmp->memory;
+		if (substream->runtime->channels > 1)
+			pos <<= 1;
+		pos = bytes_to_frames(runtime, pos);
+	}
+	spin_unlock(&gus->reg_lock);
+	return pos;
+}
+
+#ifdef TARGET_OS2
+static ratnum_t clock = {
+	9878400/16,
+	2,
+	257,
+	1,
+};
+
+static snd_pcm_hw_constraint_ratnums_t hw_constraints_clocks  = {
+	1,
+	&clock,
+};
+#else
+static ratnum_t clock = {
+	num: 9878400/16,
+	den_min: 2,
+	den_max: 257,
+	den_step: 1,
+};
+
+static snd_pcm_hw_constraint_ratnums_t hw_constraints_clocks  = {
+	nrats: 1,
+	rats: &clock,
+};
+#endif
+
+static int snd_gf1_pcm_capture_hw_params(snd_pcm_substream_t * substream,
+					 snd_pcm_hw_params_t * hw_params)
+{
+	snd_gus_card_t *gus = snd_pcm_substream_chip(substream);
+
+	gus->c_dma_size = params_buffer_bytes(hw_params);
+	gus->c_period_size = params_period_bytes(hw_params);
+	gus->c_pos = 0;
+	gus->gf1.pcm_rcntrl_reg = 0x21;		/* IRQ at end, enable & start */
+	if (params_channels(hw_params) > 1)
+		gus->gf1.pcm_rcntrl_reg |= 2;
+	if (gus->gf1.dma2 > 3)
+		gus->gf1.pcm_rcntrl_reg |= 4;
+	if (snd_pcm_format_unsigned(params_format(hw_params)))
+		gus->gf1.pcm_rcntrl_reg |= 0x80;
+	return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+static int snd_gf1_pcm_capture_hw_free(snd_pcm_substream_t * substream)
+{
+	return snd_pcm_lib_free_pages(substream);
+}
+
+static int snd_gf1_pcm_capture_prepare(snd_pcm_substream_t * substream)
+{
+	snd_gus_card_t *gus = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	snd_gf1_i_write8(gus, SNDRV_GF1_GB_RECORD_RATE, runtime->rate_den - 2);
+	snd_gf1_i_write8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL, 0);	/* disable sampling */
+	snd_gf1_i_look8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL);	/* Sampling Control Register */
+	snd_dma_program(gus->gf1.dma2, &runtime->dma_area, gus->c_period_size, DMA_MODE_READ);
+	return 0;
+}
+
+static int snd_gf1_pcm_capture_trigger(snd_pcm_substream_t * substream,
+				       int cmd)
+{
+	snd_gus_card_t *gus = snd_pcm_substream_chip(substream);
+	int val;
+	
+	if (cmd == SNDRV_PCM_TRIGGER_START) {
+		val = gus->gf1.pcm_rcntrl_reg;
+	} else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+		val = 0;
+	} else {
+		return -EINVAL;
+	}
+
+	spin_lock(&gus->reg_lock);
+	snd_gf1_write8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL, val);
+	snd_gf1_look8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL);
+	spin_unlock(&gus->reg_lock);
+	return 0;
+}
+
+static snd_pcm_uframes_t snd_gf1_pcm_capture_pointer(snd_pcm_substream_t * substream)
+{
+	snd_gus_card_t *gus = snd_pcm_substream_chip(substream);
+	int pos = gus->c_period_size - snd_dma_residue(gus->gf1.dma2);
+	pos = bytes_to_frames(substream->runtime, (gus->c_pos + pos) % gus->c_dma_size);
+	return pos;
+}
+
+static void snd_gf1_pcm_interrupt_dma_read(snd_gus_card_t * gus)
+{
+	snd_gf1_i_write8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL, 0);	/* disable sampling */
+	snd_gf1_i_look8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL);	/* Sampling Control Register */
+	if (gus->pcm_cap_substream != NULL) {
+		snd_gf1_pcm_capture_prepare(gus->pcm_cap_substream); 
+		snd_gf1_pcm_capture_trigger(gus->pcm_cap_substream, SNDRV_PCM_TRIGGER_START);
+		gus->c_pos += gus->c_period_size;
+		snd_pcm_period_elapsed(gus->pcm_cap_substream);
+	}
+}
+
+#ifdef TARGET_OS2
+static snd_pcm_hardware_t snd_gf1_pcm_playback =
+{
+/*	info:		  */	SNDRV_PCM_INFO_NONINTERLEAVED,
+/*	formats:	  */	(SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 |
+			  	 SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE),
+/*	rates:		  */	SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_44100,
+/*	rate_min:	  */	5510,
+/*	rate_max:	  */	48000,
+/*	channels_min:	  */	1,
+/*	channels_max:	  */	2,
+/*	buffer_bytes_max:  */	(128*1024),
+/*	period_bytes_min:  */	64,
+/*	period_bytes_max:  */	(128*1024),
+/*	periods_min:	  */	1,
+/*	periods_max:	  */	1024,
+/*	fifo_size:	  */	0,
+};
+
+static snd_pcm_hardware_t snd_gf1_pcm_capture =
+{
+/*	info:		  */	(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+/*	formats:	  */	SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8,
+/*	rates:		  */	SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_44100,
+/*	rate_min:	  */	5510,
+/*	rate_max:	  */	44100,
+/*	channels_min:	  */	1,
+/*	channels_max:	  */	2,
+/*	buffer_bytes_max:  */	(128*1024),
+/*	period_bytes_min:  */	64,
+/*	period_bytes_max:  */	(128*1024),
+/*	periods_min:	  */	1,
+/*	periods_max:	  */	1024,
+/*	fifo_size:	  */	0,
+};
+#else
+static snd_pcm_hardware_t snd_gf1_pcm_playback =
+{
+	info:			SNDRV_PCM_INFO_NONINTERLEAVED,
+	formats:		(SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 |
+				 SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE),
+	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_44100,
+	rate_min:		5510,
+	rate_max:		48000,
+	channels_min:		1,
+	channels_max:		2,
+	buffer_bytes_max:	(128*1024),
+	period_bytes_min:	64,
+	period_bytes_max:	(128*1024),
+	periods_min:		1,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+
+static snd_pcm_hardware_t snd_gf1_pcm_capture =
+{
+	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+	formats:		SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8,
+	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_44100,
+	rate_min:		5510,
+	rate_max:		44100,
+	channels_min:		1,
+	channels_max:		2,
+	buffer_bytes_max:	(128*1024),
+	period_bytes_min:	64,
+	period_bytes_max:	(128*1024),
+	periods_min:		1,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+#endif
+
+static void snd_gf1_pcm_playback_free(snd_pcm_runtime_t *runtime)
+{
+	gus_pcm_private_t * pcmp = snd_magic_cast(gus_pcm_private_t, runtime->private_data, return);
+	snd_magic_kfree(pcmp);
+}
+
+static int snd_gf1_pcm_playback_open(snd_pcm_substream_t *substream)
+{
+	gus_pcm_private_t *pcmp;
+	snd_gus_card_t *gus = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int err;
+
+	pcmp = snd_magic_kcalloc(gus_pcm_private_t, 0, GFP_KERNEL);
+	if (pcmp == NULL)
+		return -ENOMEM;
+	pcmp->gus = gus;
+	spin_lock_init(&pcmp->lock);
+	init_waitqueue_head(&pcmp->sleep);
+	atomic_set(&pcmp->dma_count, 0);
+
+	runtime->private_data = pcmp;
+	runtime->private_free = snd_gf1_pcm_playback_free;
+
+#if 0
+	printk("playback.buffer = 0x%lx, gf1.pcm_buffer = 0x%lx\n", (long) pcm->playback.buffer, (long) gus->gf1.pcm_buffer);
+#endif
+	if ((err = snd_gf1_dma_init(gus)) < 0)
+		return err;
+	pcmp->flags = SNDRV_GF1_PCM_PFLG_NONE;
+	pcmp->substream = substream;
+	runtime->hw = snd_gf1_pcm_playback;
+	snd_pcm_limit_isa_dma_size(gus->gf1.dma1, &runtime->hw.buffer_bytes_max);
+	snd_pcm_limit_isa_dma_size(gus->gf1.dma1, &runtime->hw.period_bytes_max);
+	snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 64);
+	return 0;
+}
+
+static int snd_gf1_pcm_playback_close(snd_pcm_substream_t * substream)
+{
+	snd_gus_card_t *gus = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, runtime->private_data, return -ENXIO);
+	unsigned long jiffies_old;
+
+	jiffies_old = jiffies;
+	while (atomic_read(&pcmp->dma_count) > 0) {
+		interruptible_sleep_on_timeout(&pcmp->sleep, 1);
+		if ((signed long)(jiffies - jiffies_old) > 2*HZ) {
+			snd_printk("gf1 pcm - serious DMA problem\n");
+			break;
+		}
+	}
+	snd_gf1_dma_done(gus);	
+	return 0;
+}
+
+static int snd_gf1_pcm_capture_open(snd_pcm_substream_t * substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_gus_card_t *gus = snd_pcm_substream_chip(substream);
+
+	gus->gf1.interrupt_handler_dma_read = snd_gf1_pcm_interrupt_dma_read;
+	gus->pcm_cap_substream = substream;
+	substream->runtime->hw = snd_gf1_pcm_capture;
+	snd_pcm_limit_isa_dma_size(gus->gf1.dma2, &runtime->hw.buffer_bytes_max);
+	snd_pcm_limit_isa_dma_size(gus->gf1.dma2, &runtime->hw.period_bytes_max);
+	snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+				      &hw_constraints_clocks);
+	return 0;
+}
+
+static int snd_gf1_pcm_capture_close(snd_pcm_substream_t * substream)
+{
+	snd_gus_card_t *gus = snd_pcm_substream_chip(substream);
+
+	gus->pcm_cap_substream = NULL;
+	snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_DMA_READ);
+	return 0;
+}
+
+static void snd_gf1_pcm_free(snd_pcm_t *pcm)
+{
+	snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, pcm->private_data, return);
+	gus->pcm = NULL;
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static int snd_gf1_pcm_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 127;
+	return 0;
+}
+
+static int snd_gf1_pcm_volume_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	snd_gus_card_t *gus = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	
+	spin_lock_irqsave(&gus->pcm_volume_level_lock, flags);
+	ucontrol->value.integer.value[0] = gus->gf1.pcm_volume_level_left1;
+	ucontrol->value.integer.value[1] = gus->gf1.pcm_volume_level_right1;
+	spin_unlock_irqrestore(&gus->pcm_volume_level_lock, flags);
+	return 0;
+}
+
+static int snd_gf1_pcm_volume_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	snd_gus_card_t *gus = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int change, idx;
+	unsigned short val1, val2, vol;
+	gus_pcm_private_t *pcmp;
+	snd_gus_voice_t *pvoice;
+	
+	val1 = ucontrol->value.integer.value[0] & 127;
+	val2 = ucontrol->value.integer.value[1] & 127;
+	spin_lock_irqsave(&gus->pcm_volume_level_lock, flags);
+	change = val1 != gus->gf1.pcm_volume_level_left1 ||
+	         val2 != gus->gf1.pcm_volume_level_right1;
+	gus->gf1.pcm_volume_level_left1 = val1;
+	gus->gf1.pcm_volume_level_right1 = val2;
+	gus->gf1.pcm_volume_level_left = snd_gf1_lvol_to_gvol_raw(val1 << 9) << 4;
+	gus->gf1.pcm_volume_level_right = snd_gf1_lvol_to_gvol_raw(val2 << 9) << 4;
+	spin_unlock_irqrestore(&gus->pcm_volume_level_lock, flags);
+	/* are we active? */
+	spin_lock_irqsave(&gus->voice_alloc, flags);
+	for (idx = 0; idx < 32; idx++) {
+		pvoice = &gus->gf1.voices[idx];
+		if (!pvoice->pcm)
+			continue;
+		pcmp = snd_magic_cast(gus_pcm_private_t, pvoice->private_data, return -ENXIO);
+		if (!(pcmp->flags & SNDRV_GF1_PCM_PFLG_ACTIVE))
+			continue;
+		/* load real volume - better precision */
+		spin_lock_irqsave(&gus->reg_lock, flags);
+		snd_gf1_select_voice(gus, pvoice->number);
+		snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL);
+		vol = pvoice == pcmp->pvoices[0] ? gus->gf1.pcm_volume_level_left : gus->gf1.pcm_volume_level_right;
+		snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, vol);
+		pcmp->final_volume = 1;
+		spin_unlock_irqrestore(&gus->reg_lock, flags);
+	}
+	spin_unlock_irqrestore(&gus->voice_alloc, flags);
+	return change;
+}
+
+#ifdef TARGET_OS2
+static snd_kcontrol_new_t snd_gf1_pcm_volume_control =
+{
+	SNDRV_CTL_ELEM_IFACE_MIXER,0,0,
+	"PCM Playback Volume",0,0, 0,
+	snd_gf1_pcm_volume_info,
+	snd_gf1_pcm_volume_get,
+	snd_gf1_pcm_volume_put,0
+};
+
+static snd_pcm_ops_t snd_gf1_pcm_playback_ops = {
+	snd_gf1_pcm_playback_open,
+	snd_gf1_pcm_playback_close,
+	snd_pcm_lib_ioctl,
+	snd_gf1_pcm_playback_hw_params,
+	snd_gf1_pcm_playback_hw_free,
+	snd_gf1_pcm_playback_prepare,
+	snd_gf1_pcm_playback_trigger,
+	snd_gf1_pcm_playback_pointer,
+	snd_gf1_pcm_playback_copy,
+	snd_gf1_pcm_playback_silence
+};
+
+static snd_pcm_ops_t snd_gf1_pcm_capture_ops = {
+	snd_gf1_pcm_capture_open,
+	snd_gf1_pcm_capture_close,
+	snd_pcm_lib_ioctl,
+	snd_gf1_pcm_capture_hw_params,
+	snd_gf1_pcm_capture_hw_free,
+	snd_gf1_pcm_capture_prepare,
+	snd_gf1_pcm_capture_trigger,
+	snd_gf1_pcm_capture_pointer,0,0
+};
+#else
+static snd_kcontrol_new_t snd_gf1_pcm_volume_control =
+{
+	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	name: "PCM Playback Volume",
+	info: snd_gf1_pcm_volume_info,
+	get: snd_gf1_pcm_volume_get,
+	put: snd_gf1_pcm_volume_put
+};
+
+static snd_pcm_ops_t snd_gf1_pcm_playback_ops = {
+	open:		snd_gf1_pcm_playback_open,
+	close:		snd_gf1_pcm_playback_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	hw_params:	snd_gf1_pcm_playback_hw_params,
+	hw_free:	snd_gf1_pcm_playback_hw_free,
+	prepare:	snd_gf1_pcm_playback_prepare,
+	trigger:	snd_gf1_pcm_playback_trigger,
+	pointer:	snd_gf1_pcm_playback_pointer,
+	copy:		snd_gf1_pcm_playback_copy,
+	silence:	snd_gf1_pcm_playback_silence,
+};
+
+static snd_pcm_ops_t snd_gf1_pcm_capture_ops = {
+	open:		snd_gf1_pcm_capture_open,
+	close:		snd_gf1_pcm_capture_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	hw_params:	snd_gf1_pcm_capture_hw_params,
+	hw_free:	snd_gf1_pcm_capture_hw_free,
+	prepare:	snd_gf1_pcm_capture_prepare,
+	trigger:	snd_gf1_pcm_capture_trigger,
+	pointer:	snd_gf1_pcm_capture_pointer,
+};
+#endif
+
+int snd_gf1_pcm_new(snd_gus_card_t * gus, int pcm_dev, int control_index, snd_pcm_t ** rpcm)
+{
+	snd_card_t *card;
+	snd_kcontrol_t *kctl;
+	snd_pcm_t *pcm;
+	snd_pcm_substream_t *substream;
+	int err;
+
+	if (rpcm)
+		*rpcm = NULL;
+	card = gus->card;
+	err = snd_pcm_new(card,
+			  gus->interwave ? "AMD InterWave" : "GF1",
+			  pcm_dev,
+			  gus->gf1.pcm_channels / 2,
+			  !gus->interwave && !gus->ess_flag ? 1 : 0,
+			  &pcm);
+	if (err < 0)
+		return err;
+	pcm->private_data = gus;
+	pcm->private_free = snd_gf1_pcm_free;
+	/* playback setup */
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_gf1_pcm_playback_ops);
+
+	for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next)
+		snd_pcm_lib_preallocate_pages(substream, 64*1024, gus->gf1.dma1 > 3 ? 128*1024 : 64*1024, GFP_KERNEL|GFP_DMA);
+	
+	pcm->info_flags = 0;
+	pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
+	if (gus->interwave && !gus->ess_flag && !gus->ace_flag) {
+		/* capture setup */
+		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_gf1_pcm_capture_ops);
+		if (gus->gf1.dma2 == gus->gf1.dma1)
+			pcm->info_flags |= SNDRV_PCM_INFO_HALF_DUPLEX;
+		snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream, 64*1024, gus->gf1.dma2 > 3 ? 128*1024 : 64*1024, GFP_KERNEL|GFP_DMA);
+	}
+	strcpy(pcm->name, pcm->id);
+	if (gus->interwave) {
+		sprintf(pcm->name + strlen(pcm->name), " rev %c", gus->revision + 'A');
+	}
+	strcat(pcm->name, " (synth)");
+	gus->pcm = pcm;
+
+	if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_gf1_pcm_volume_control, gus))) < 0)
+		return err;
+	kctl->id.index = control_index;
+
+	if (rpcm)
+		*rpcm = pcm;
+	return 0;
+}
+
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/gus_reset.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/gus_reset.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/gus_reset.c	(revision 91)
@@ -0,0 +1,418 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <sound/driver.h>
+#include <sound/gus.h>
+
+extern void snd_gf1_timers_init(snd_gus_card_t * gus);
+extern void snd_gf1_timers_done(snd_gus_card_t * gus);
+extern int snd_gf1_synth_init(snd_gus_card_t * gus);
+extern void snd_gf1_synth_done(snd_gus_card_t * gus);
+
+/*
+ *  ok.. default interrupt handlers...
+ */
+
+static void snd_gf1_default_interrupt_handler_midi_out(snd_gus_card_t * gus)
+{
+	snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd &= ~0x20);
+}
+
+static void snd_gf1_default_interrupt_handler_midi_in(snd_gus_card_t * gus)
+{
+	snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd &= ~0x80);
+}
+
+static void snd_gf1_default_interrupt_handler_timer1(snd_gus_card_t * gus)
+{
+	snd_gf1_i_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, gus->gf1.timer_enabled &= ~4);
+}
+
+static void snd_gf1_default_interrupt_handler_timer2(snd_gus_card_t * gus)
+{
+	snd_gf1_i_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, gus->gf1.timer_enabled &= ~8);
+}
+
+static void snd_gf1_default_interrupt_handler_wave_and_volume(snd_gus_card_t * gus, snd_gus_voice_t * voice)
+{
+	snd_gf1_i_ctrl_stop(gus, 0x00);
+	snd_gf1_i_ctrl_stop(gus, 0x0d);
+}
+
+static void snd_gf1_default_interrupt_handler_dma_write(snd_gus_card_t * gus)
+{
+	snd_gf1_i_write8(gus, 0x41, 0x00);
+}
+
+static void snd_gf1_default_interrupt_handler_dma_read(snd_gus_card_t * gus)
+{
+	snd_gf1_i_write8(gus, 0x49, 0x00);
+}
+
+void snd_gf1_set_default_handlers(snd_gus_card_t * gus, unsigned int what)
+{
+	if (what & SNDRV_GF1_HANDLER_MIDI_OUT)
+		gus->gf1.interrupt_handler_midi_out = snd_gf1_default_interrupt_handler_midi_out;
+	if (what & SNDRV_GF1_HANDLER_MIDI_IN)
+		gus->gf1.interrupt_handler_midi_in = snd_gf1_default_interrupt_handler_midi_in;
+	if (what & SNDRV_GF1_HANDLER_TIMER1)
+		gus->gf1.interrupt_handler_timer1 = snd_gf1_default_interrupt_handler_timer1;
+	if (what & SNDRV_GF1_HANDLER_TIMER2)
+		gus->gf1.interrupt_handler_timer2 = snd_gf1_default_interrupt_handler_timer2;
+	if (what & SNDRV_GF1_HANDLER_VOICE) {
+		snd_gus_voice_t *voice;
+		
+		voice = &gus->gf1.voices[what & 0xffff];
+		voice->handler_wave =
+		voice->handler_volume = snd_gf1_default_interrupt_handler_wave_and_volume;
+		voice->handler_effect = NULL;
+		voice->volume_change = NULL;
+	}
+	if (what & SNDRV_GF1_HANDLER_DMA_WRITE)
+		gus->gf1.interrupt_handler_dma_write = snd_gf1_default_interrupt_handler_dma_write;
+	if (what & SNDRV_GF1_HANDLER_DMA_READ)
+		gus->gf1.interrupt_handler_dma_read = snd_gf1_default_interrupt_handler_dma_read;
+}
+
+/*
+
+ */
+
+static void snd_gf1_clear_regs(snd_gus_card_t * gus)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	inb(GUSP(gus, IRQSTAT));
+	snd_gf1_write8(gus, 0x41, 0);	/* DRAM DMA Control Register */
+	snd_gf1_write8(gus, 0x45, 0);	/* Timer Control */
+	snd_gf1_write8(gus, 0x49, 0);	/* Sampling Control Register */
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+}
+
+static void snd_gf1_look_regs(snd_gus_card_t * gus)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	snd_gf1_look8(gus, 0x41);	/* DRAM DMA Control Register */
+	snd_gf1_look8(gus, 0x49);	/* Sampling Control Register */
+	inb(GUSP(gus, IRQSTAT));
+	snd_gf1_read8(gus, 0x0f);	/* IRQ Source Register */
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+}
+
+/*
+ *  put selected GF1 voices to initial stage...
+ */
+
+void snd_gf1_smart_stop_voice(snd_gus_card_t * gus, unsigned short voice)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	snd_gf1_select_voice(gus, voice);
+#if 0
+	printk(" -%i- smart stop voice - volume = 0x%x\n", voice, snd_gf1_i_read16(gus, SNDRV_GF1_VW_VOLUME));
+#endif
+	snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_ADDRESS_CONTROL);
+	snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL);
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+}
+
+void snd_gf1_stop_voice(snd_gus_card_t * gus, unsigned short voice)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	snd_gf1_select_voice(gus, voice);
+#if 0
+	printk(" -%i- stop voice - volume = 0x%x\n", voice, snd_gf1_i_read16(gus, SNDRV_GF1_VW_VOLUME));
+#endif
+	snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_ADDRESS_CONTROL);
+	snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL);
+	if (gus->gf1.enh_mode)
+		snd_gf1_write8(gus, SNDRV_GF1_VB_ACCUMULATOR, 0);
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+#if 0
+	snd_gf1_lfo_shutdown(gus, voice, ULTRA_LFO_VIBRATO);
+	snd_gf1_lfo_shutdown(gus, voice, ULTRA_LFO_TREMOLO);
+#endif
+}
+
+void snd_gf1_clear_voices(snd_gus_card_t * gus, unsigned short v_min, unsigned short v_max)
+{
+	unsigned long flags;
+	unsigned int daddr;
+	unsigned short i, w_16;
+
+	daddr = gus->gf1.default_voice_address << 4;
+	for (i = v_min; i <= v_max; i++) {
+#if 0
+		if (gus->gf1.syn_voices)
+			gus->gf1.syn_voices[i].flags = ~VFLG_DYNAMIC;
+#endif
+		spin_lock_irqsave(&gus->reg_lock, flags);
+		snd_gf1_select_voice(gus, i);
+		snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_ADDRESS_CONTROL);	/* Voice Control Register = voice stop */
+		snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL);	/* Volume Ramp Control Register = ramp off */
+		if (gus->gf1.enh_mode)
+			snd_gf1_write8(gus, SNDRV_GF1_VB_MODE, gus->gf1.memory ? 0x02 : 0x82);	/* Deactivate voice */
+		w_16 = snd_gf1_read8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL) & 0x04;
+		snd_gf1_write16(gus, SNDRV_GF1_VW_FREQUENCY, 0x400);
+		snd_gf1_write_addr(gus, SNDRV_GF1_VA_START, daddr, w_16);
+		snd_gf1_write_addr(gus, SNDRV_GF1_VA_END, daddr, w_16);
+		snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_START, 0);
+		snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_END, 0);
+		snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_RATE, 0);
+		snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, 0);
+		snd_gf1_write_addr(gus, SNDRV_GF1_VA_CURRENT, daddr, w_16);
+		snd_gf1_write8(gus, SNDRV_GF1_VB_PAN, 7);
+		if (gus->gf1.enh_mode) {
+			snd_gf1_write8(gus, SNDRV_GF1_VB_ACCUMULATOR, 0);
+			snd_gf1_write16(gus, SNDRV_GF1_VW_EFFECT_VOLUME, 0);
+			snd_gf1_write16(gus, SNDRV_GF1_VW_EFFECT_VOLUME_FINAL, 0);
+		}
+		spin_unlock_irqrestore(&gus->reg_lock, flags);
+#if 0
+		snd_gf1_lfo_shutdown(gus, i, ULTRA_LFO_VIBRATO);
+		snd_gf1_lfo_shutdown(gus, i, ULTRA_LFO_TREMOLO);
+#endif
+	}
+}
+
+void snd_gf1_stop_voices(snd_gus_card_t * gus, unsigned short v_min, unsigned short v_max)
+{
+	unsigned long flags;
+	short i, ramp_ok;
+	unsigned short ramp_end;
+	long time;
+
+	if (!in_interrupt()) {	/* this can't be done in interrupt */
+		for (i = v_min, ramp_ok = 0; i <= v_max; i++) {
+			spin_lock_irqsave(&gus->reg_lock, flags);
+			snd_gf1_select_voice(gus, i);
+			ramp_end = snd_gf1_read16(gus, 9) >> 8;
+			if (ramp_end > SNDRV_GF1_MIN_OFFSET) {
+				ramp_ok++;
+				snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_RATE, 20);	/* ramp rate */
+				snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_START, SNDRV_GF1_MIN_OFFSET);	/* ramp start */
+				snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_END, ramp_end);	/* ramp end */
+				snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, 0x40);	/* ramp down */
+				if (gus->gf1.enh_mode) {
+					snd_gf1_delay(gus);
+					snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, 0x40);
+				}
+			}
+			spin_unlock_irqrestore(&gus->reg_lock, flags);
+		}
+		time = HZ / 20;
+		while (time > 0 && !signal_pending(current)) {
+			set_current_state(TASK_INTERRUPTIBLE);
+			time = schedule_timeout(time);
+		}
+	}
+	snd_gf1_clear_voices(gus, v_min, v_max);
+}
+
+static void snd_gf1_alloc_voice_use(snd_gus_card_t * gus, 
+				    snd_gus_voice_t * pvoice,
+				    int type, int client, int port)
+{
+	pvoice->use = 1;
+	switch (type) {
+	case SNDRV_GF1_VOICE_TYPE_PCM:
+		gus->gf1.pcm_alloc_voices++;
+		pvoice->pcm = 1;
+		break;
+	case SNDRV_GF1_VOICE_TYPE_SYNTH:
+		pvoice->synth = 1;
+		pvoice->client = client;
+		pvoice->port = port;
+		break;
+	case SNDRV_GF1_VOICE_TYPE_MIDI:
+		pvoice->midi = 1;
+		pvoice->client = client;
+		pvoice->port = port;
+		break;
+	}
+}
+
+snd_gus_voice_t *snd_gf1_alloc_voice(snd_gus_card_t * gus, int type, int client, int port)
+{
+	snd_gus_voice_t *pvoice;
+	unsigned long flags;
+	int idx;
+
+	spin_lock_irqsave(&gus->voice_alloc, flags);
+	if (type == SNDRV_GF1_VOICE_TYPE_PCM) {
+		if (gus->gf1.pcm_alloc_voices >= gus->gf1.pcm_channels) {
+			spin_unlock_irqrestore(&gus->voice_alloc, flags);
+			return NULL;
+		}
+	}
+	for (idx = 0; idx < 32; idx++) {
+		pvoice = &gus->gf1.voices[idx];
+		if (!pvoice->use) {
+			snd_gf1_alloc_voice_use(gus, pvoice, type, client, port);
+			spin_unlock_irqrestore(&gus->voice_alloc, flags);
+			return pvoice;
+		}
+	} 
+	for (idx = 0; idx < 32; idx++) {
+		pvoice = &gus->gf1.voices[idx];
+		if (pvoice->midi && !pvoice->client) {
+			snd_gf1_clear_voices(gus, pvoice->number, pvoice->number);
+			snd_gf1_alloc_voice_use(gus, pvoice, type, client, port);
+			spin_unlock_irqrestore(&gus->voice_alloc, flags);
+			return pvoice;
+		}
+	} 
+	spin_unlock_irqrestore(&gus->voice_alloc, flags);
+	return NULL;
+}
+
+void snd_gf1_free_voice(snd_gus_card_t * gus, snd_gus_voice_t *voice)
+{
+	unsigned long flags;
+	void (*private_free)(snd_gus_voice_t *voice);
+	void *private_data;
+
+	if (voice == NULL || !voice->use)
+		return;
+	snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_VOICE | voice->number);
+	snd_gf1_clear_voices(gus, voice->number, voice->number);
+	spin_lock_irqsave(&gus->voice_alloc, flags);
+	private_free = voice->private_free;
+	private_data = voice->private_data;
+	voice->private_free = NULL;
+	voice->private_data = NULL;
+	if (voice->pcm)
+		gus->gf1.pcm_alloc_voices--;
+	voice->use = voice->pcm = 0;
+	voice->sample_ops = NULL;
+	spin_unlock_irqrestore(&gus->voice_alloc, flags);
+	if (private_free)
+		private_free(voice);
+}
+
+/*
+ *  call this function only by start of driver
+ */
+
+int snd_gf1_start(snd_gus_card_t * gus)
+{
+	unsigned long flags;
+	unsigned int i;
+
+	snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 0);	/* reset GF1 */
+	udelay(160);
+	snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1);	/* disable IRQ & DAC */
+	udelay(160);
+	snd_gf1_i_write8(gus, SNDRV_GF1_GB_JOYSTICK_DAC_LEVEL, gus->joystick_dac);
+
+	snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_ALL);
+	for (i = 0; i < 32; i++) {
+		gus->gf1.voices[i].number = i;
+		snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_VOICE | i);
+	}
+
+	snd_gf1_uart_cmd(gus, 0x03);	/* huh.. this cleanup took me some time... */
+
+	if (gus->gf1.enh_mode) {	/* enhanced mode !!!! */
+		snd_gf1_i_write8(gus, SNDRV_GF1_GB_GLOBAL_MODE, snd_gf1_i_look8(gus, SNDRV_GF1_GB_GLOBAL_MODE) | 0x01);
+		snd_gf1_i_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x01);
+	}
+	snd_gf1_clear_regs(gus);
+	snd_gf1_select_active_voices(gus);
+	snd_gf1_delay(gus);
+	gus->gf1.default_voice_address = gus->gf1.memory > 0 ? 0 : 512 - 8;
+	/* initialize LFOs & clear LFOs memory */
+	if (gus->gf1.enh_mode && gus->gf1.memory) {
+		gus->gf1.hw_lfo = 1;
+		gus->gf1.default_voice_address += 1024;
+	} else {
+		gus->gf1.sw_lfo = 1;
+	}
+#if 0
+	snd_gf1_lfo_init(gus);
+#endif
+	if (gus->gf1.memory > 0)
+		for (i = 0; i < 4; i++)
+			snd_gf1_poke(gus, gus->gf1.default_voice_address + i, 0);
+	snd_gf1_clear_regs(gus);
+	snd_gf1_clear_voices(gus, 0, 31);
+	snd_gf1_look_regs(gus);
+	udelay(160);
+	snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 7);	/* Reset Register = IRQ enable, DAC enable */
+	udelay(160);
+	snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 7);	/* Reset Register = IRQ enable, DAC enable */
+	if (gus->gf1.enh_mode) {	/* enhanced mode !!!! */
+		snd_gf1_i_write8(gus, SNDRV_GF1_GB_GLOBAL_MODE, snd_gf1_i_look8(gus, SNDRV_GF1_GB_GLOBAL_MODE) | 0x01);
+		snd_gf1_i_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x01);
+	}
+	while ((snd_gf1_i_read8(gus, SNDRV_GF1_GB_VOICES_IRQ) & 0xc0) != 0xc0);
+
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	outb(gus->gf1.active_voice = 0, GUSP(gus, GF1PAGE));
+	outb(gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG));
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+
+	snd_gf1_timers_init(gus);
+	snd_gf1_look_regs(gus);
+	snd_gf1_mem_init(gus);
+	snd_gf1_mem_proc_init(gus);
+#ifdef CONFIG_SND_DEBUG
+	snd_gus_irq_profile_init(gus);
+#endif
+
+#if 0
+	if (gus->pnp_flag) {
+		if (gus->chip.playback_fifo_size > 0)
+			snd_gf1_i_write16(gus, SNDRV_GF1_GW_FIFO_RECORD_BASE_ADDR, gus->chip.playback_fifo_block->ptr >> 8);
+		if (gus->chip.record_fifo_size > 0)
+			snd_gf1_i_write16(gus, SNDRV_GF1_GW_FIFO_PLAY_BASE_ADDR, gus->chip.record_fifo_block->ptr >> 8);
+		snd_gf1_i_write16(gus, SNDRV_GF1_GW_FIFO_SIZE, gus->chip.interwave_fifo_reg);
+	}
+#endif
+
+	return 0;
+}
+
+/*
+ *  call this function only by shutdown of driver
+ */
+
+int snd_gf1_stop(snd_gus_card_t * gus)
+{
+	snd_gf1_i_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, 0); /* stop all timers */
+	snd_gf1_stop_voices(gus, 0, 31);		/* stop all voices */
+	snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1);	/* disable IRQ & DAC */
+	snd_gf1_timers_done(gus);
+#ifdef CONFIG_SND_DEBUG
+	snd_gus_irq_profile_done(gus);
+#endif
+	snd_gf1_mem_proc_done(gus);
+	snd_gf1_mem_done(gus);
+#if 0
+	snd_gf1_lfo_done(gus);
+#endif
+	return 0;
+}
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/gus_sample.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/gus_sample.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/gus_sample.c	(revision 91)
@@ -0,0 +1,153 @@
+/*
+ *  Routines for Gravis UltraSound soundcards - Sample support
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <sound/driver.h>
+#include <sound/gus.h>
+
+/*
+ *
+ */
+
+static void select_instrument(snd_gus_card_t * gus, snd_gus_voice_t * v)
+{
+	snd_seq_kinstr_t *instr;
+
+#if 0
+	printk("select instrument: cluster = %li, std = 0x%x, bank = %i, prg = %i\n",
+					v->instr.cluster,
+					v->instr.std,
+					v->instr.bank,
+					v->instr.prg);
+#endif
+	instr = snd_seq_instr_find(gus->gf1.ilist, &v->instr, 0, 1);
+	if (instr != NULL) {
+		if (instr->ops) {
+			if (instr->ops->instr_type == snd_seq_simple_id)
+				snd_gf1_simple_init(v);
+		}
+		snd_seq_instr_free_use(gus->gf1.ilist, instr);
+	}
+}
+
+/*
+ *
+ */
+
+static void event_sample(snd_seq_event_t *ev, snd_gus_port_t *p, snd_gus_voice_t *v)
+{
+	if (v->sample_ops && v->sample_ops->sample_stop)
+		v->sample_ops->sample_stop(p->gus, v, SAMPLE_STOP_IMMEDIATELY);
+	v->instr.std = ev->data.sample.param.sample.std;
+	if (v->instr.std & 0xff000000) {        /* private instrument */
+		v->instr.std &= 0x00ffffff;
+		v->instr.std |= (unsigned int)ev->source.client << 24;
+	}                                                
+	v->instr.bank = ev->data.sample.param.sample.bank;
+	v->instr.prg = ev->data.sample.param.sample.prg;
+	select_instrument(p->gus, v);
+}
+
+static void event_cluster(snd_seq_event_t *ev, snd_gus_port_t *p, snd_gus_voice_t *v)
+{
+	if (v->sample_ops && v->sample_ops->sample_stop)
+		v->sample_ops->sample_stop(p->gus, v, SAMPLE_STOP_IMMEDIATELY);
+	v->instr.cluster = ev->data.sample.param.cluster.cluster;
+	select_instrument(p->gus, v);
+}
+
+static void event_start(snd_seq_event_t *ev, snd_gus_port_t *p, snd_gus_voice_t *v)
+{
+	if (v->sample_ops && v->sample_ops->sample_start)
+		v->sample_ops->sample_start(p->gus, v, ev->data.sample.param.position);
+}
+
+static void event_stop(snd_seq_event_t *ev, snd_gus_port_t *p, snd_gus_voice_t *v)
+{
+	if (v->sample_ops && v->sample_ops->sample_stop)
+		v->sample_ops->sample_stop(p->gus, v, ev->data.sample.param.stop_mode);
+}
+
+static void event_freq(snd_seq_event_t *ev, snd_gus_port_t *p, snd_gus_voice_t *v)
+{
+	if (v->sample_ops && v->sample_ops->sample_freq)
+		v->sample_ops->sample_freq(p->gus, v, ev->data.sample.param.frequency);
+}
+
+static void event_volume(snd_seq_event_t *ev, snd_gus_port_t *p, snd_gus_voice_t *v)
+{
+	if (v->sample_ops && v->sample_ops->sample_volume)
+		v->sample_ops->sample_volume(p->gus, v, &ev->data.sample.param.volume);
+}
+
+static void event_loop(snd_seq_event_t *ev, snd_gus_port_t *p, snd_gus_voice_t *v)
+{
+	if (v->sample_ops && v->sample_ops->sample_loop)
+		v->sample_ops->sample_loop(p->gus, v, &ev->data.sample.param.loop);
+}
+
+static void event_position(snd_seq_event_t *ev, snd_gus_port_t *p, snd_gus_voice_t *v)
+{
+	if (v->sample_ops && v->sample_ops->sample_pos)
+		v->sample_ops->sample_pos(p->gus, v, ev->data.sample.param.position);
+}
+
+static void event_private1(snd_seq_event_t *ev, snd_gus_port_t *p, snd_gus_voice_t *v)
+{
+	if (v->sample_ops && v->sample_ops->sample_private1)
+		v->sample_ops->sample_private1(p->gus, v, (unsigned char *)&ev->data.sample.param.raw8);
+}
+
+typedef void (gus_sample_event_handler_t)(snd_seq_event_t *ev, snd_gus_port_t *p, snd_gus_voice_t *v);
+
+static gus_sample_event_handler_t *gus_sample_event_handlers[9] = {
+	event_sample,
+	event_cluster,
+	event_start,
+	event_stop,
+	event_freq,
+	event_volume,
+	event_loop,
+	event_position,
+	event_private1
+};
+
+void snd_gus_sample_event(snd_seq_event_t *ev, snd_gus_port_t *p)
+{
+	int idx, voice;
+	snd_gus_card_t *gus = p->gus;
+	snd_gus_voice_t *v;
+	unsigned long flags;
+	
+	idx = ev->type - SNDRV_SEQ_EVENT_SAMPLE;
+	if (idx < 0 || idx > 8)
+		return;
+	for (voice = 0; voice < 32; voice++) {
+		v = &gus->gf1.voices[voice];
+		if (v->use && v->client == ev->source.client &&
+		    v->port == ev->source.port &&
+		    v->index == ev->data.sample.channel) {
+		    	spin_lock_irqsave(&gus->event_lock, flags);
+			gus_sample_event_handlers[idx](ev, p, v);
+			spin_unlock_irqrestore(&gus->event_lock, flags);
+			return;
+		}
+	}
+}
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/gus_simple.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/gus_simple.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/gus_simple.c	(revision 91)
@@ -0,0 +1,632 @@
+/*
+ *  Routines for Gravis UltraSound soundcards - Simple instrument handlers
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <sound/driver.h>
+#include <sound/gus.h>
+#include "gus_tables.h"
+
+/*
+ *
+ */
+
+static void interrupt_wave(snd_gus_card_t *gus, snd_gus_voice_t *voice);
+static void interrupt_volume(snd_gus_card_t *gus, snd_gus_voice_t *voice);
+static void interrupt_effect(snd_gus_card_t *gus, snd_gus_voice_t *voice);
+
+static void sample_start(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_position_t position);
+static void sample_stop(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_stop_mode_t mode);
+static void sample_freq(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_frequency_t freq);
+static void sample_volume(snd_gus_card_t *card, snd_gus_voice_t *voice, snd_seq_ev_volume_t *volume);
+static void sample_loop(snd_gus_card_t *card, snd_gus_voice_t *voice, snd_seq_ev_loop_t *loop);
+static void sample_pos(snd_gus_card_t *card, snd_gus_voice_t *voice, snd_seq_position_t position);
+static void sample_private1(snd_gus_card_t *card, snd_gus_voice_t *voice, unsigned char *data);
+
+static snd_gus_sample_ops_t sample_ops = {
+	sample_start,
+	sample_stop,
+	sample_freq,
+	sample_volume,
+	sample_loop,
+	sample_pos,
+	sample_private1
+};
+
+#if 0
+
+static void note_stop(snd_gus_card_t *gus, snd_gus_voice_t *voice, int wait);
+static void note_wait(snd_gus_card_t *gus, snd_gus_voice_t *voice);
+static void note_off(snd_gus_card_t *gus, snd_gus_voice_t *voice);
+static void note_volume(snd_gus_card_t *card, snd_gus_voice_t *voice);
+static void note_pitchbend(snd_gus_card_t *card, snd_gus_voice_t *voice);
+static void note_vibrato(snd_gus_card_t *card, snd_gus_voice_t *voice);
+static void note_tremolo(snd_gus_card_t *card, snd_gus_voice_t *voice);
+
+static struct snd_gus_note_handlers note_commands = {
+	note_stop,
+	note_wait,
+	note_off,
+	note_volume,
+	note_pitchbend,
+	note_vibrato,
+	note_tremolo
+};
+
+static void chn_trigger_down(snd_gus_card_t *card, ultra_channel_t *channel, ultra_instrument_t *instrument, unsigned char note, unsigned char velocity, unsigned char priority );
+static void chn_trigger_up( ultra_card_t *card, ultra_note_t *note );
+static void chn_control( ultra_card_t *card, ultra_channel_t *channel, unsigned short p1, unsigned short p2 );
+
+static struct ULTRA_STRU_INSTRUMENT_CHANNEL_COMMANDS channel_commands = {
+  chn_trigger_down,
+  chn_trigger_up,
+  chn_control
+};
+
+#endif
+
+static void do_volume_envelope(snd_gus_card_t *card, snd_gus_voice_t *voice);
+static void do_pan_envelope(snd_gus_card_t *card, snd_gus_voice_t *voice);
+
+/*
+ *
+ */
+
+static void interrupt_wave(snd_gus_card_t *gus, snd_gus_voice_t *voice)
+{
+	spin_lock(&gus->event_lock);
+	snd_gf1_stop_voice(gus, voice->number);
+	spin_lock(&gus->reg_lock);
+	snd_gf1_select_voice(gus, voice->number);
+	snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, 0);
+	spin_unlock(&gus->reg_lock);
+	voice->flags &= ~SNDRV_GF1_VFLG_RUNNING;
+	spin_unlock(&gus->event_lock);
+}
+
+static void interrupt_volume(snd_gus_card_t *gus, snd_gus_voice_t *voice)
+{
+	spin_lock(&gus->event_lock);
+	if (voice->flags & SNDRV_GF1_VFLG_RUNNING)
+		do_volume_envelope(gus, voice);
+	else
+		snd_gf1_stop_voice(gus, voice->number);
+	spin_unlock(&gus->event_lock);
+}
+
+static void interrupt_effect(snd_gus_card_t *gus, snd_gus_voice_t *voice)
+{
+	spin_lock(&gus->event_lock);
+	if ((voice->flags & (SNDRV_GF1_VFLG_RUNNING|SNDRV_GF1_VFLG_EFFECT_TIMER1)) ==
+	                    (SNDRV_GF1_VFLG_RUNNING|SNDRV_GF1_VFLG_EFFECT_TIMER1))
+		do_pan_envelope(gus, voice);
+	spin_unlock(&gus->event_lock);
+}
+
+/*
+ *
+ */
+
+static void do_volume_envelope(snd_gus_card_t *gus, snd_gus_voice_t *voice)
+{
+	unsigned short next, rate, old_volume;
+	int program_next_ramp;
+	unsigned long flags;
+  
+	if (!gus->gf1.volume_ramp) {
+		spin_lock_irqsave(&gus->reg_lock, flags);
+		snd_gf1_select_voice(gus, voice->number);
+		snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL);
+		snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, voice->gf1_volume);
+		printk("gf1_volume = 0x%x\n", voice->gf1_volume);
+		spin_unlock_irqrestore(&gus->reg_lock, flags);
+		return;
+	}
+	program_next_ramp = 0;
+	rate = next = 0;
+	while (1) {
+		program_next_ramp = 0;
+		rate = next = 0;
+		switch (voice->venv_state) {
+		case VENV_BEFORE:
+			voice->venv_state = VENV_ATTACK;
+			voice->venv_value_next = 0;
+			spin_lock_irqsave(&gus->reg_lock, flags);
+			snd_gf1_select_voice(gus, voice->number);
+			snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL);
+			snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, SNDRV_GF1_MIN_VOLUME);
+			spin_unlock_irqrestore(&gus->reg_lock, flags);
+			break;
+		case VENV_ATTACK:
+			voice->venv_state = VENV_SUSTAIN;
+			program_next_ramp++;
+			next = 255;
+			rate = gus->gf1.volume_ramp;
+			break;
+		case VENV_SUSTAIN:
+			voice->venv_state = VENV_RELEASE;
+			spin_lock_irqsave(&gus->reg_lock, flags);
+			snd_gf1_select_voice(gus, voice->number);
+			snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL);
+ 			snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, ((int)voice->gf1_volume * (int)voice->venv_value_next) / 255);
+			spin_unlock_irqrestore(&gus->reg_lock, flags);
+			return;
+		case VENV_RELEASE:
+			voice->venv_state = VENV_DONE;
+			program_next_ramp++;
+			next = 0;
+			rate = gus->gf1.volume_ramp;
+			break;
+		case VENV_DONE:
+			snd_gf1_stop_voice(gus, voice->number);
+			voice->flags &= ~SNDRV_GF1_VFLG_RUNNING;
+			return;
+		case VENV_VOLUME:
+			program_next_ramp++;
+			next = voice->venv_value_next;
+			rate = gus->gf1.volume_ramp;
+			voice->venv_state = voice->venv_state_prev;
+			break;
+		}
+		voice->venv_value_next = next;
+		if (!program_next_ramp)
+			continue;
+		spin_lock_irqsave(&gus->reg_lock, flags);
+		snd_gf1_select_voice(gus, voice->number);
+		snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL);
+		old_volume = snd_gf1_read16(gus, SNDRV_GF1_VW_VOLUME) >> 8;
+		if (!rate) {
+			spin_unlock_irqrestore(&gus->reg_lock, flags);			
+			continue;
+		}
+		next = (((int)voice->gf1_volume * (int)next) / 255) >> 8;
+		if (old_volume < SNDRV_GF1_MIN_OFFSET)
+			old_volume = SNDRV_GF1_MIN_OFFSET;
+		if (next < SNDRV_GF1_MIN_OFFSET)
+			next = SNDRV_GF1_MIN_OFFSET;
+		if (next > SNDRV_GF1_MAX_OFFSET)
+			next = SNDRV_GF1_MAX_OFFSET;
+		if (old_volume == next) {
+			spin_unlock_irqrestore(&gus->reg_lock, flags);
+			continue;
+		}
+		voice->volume_control &= ~0xc3;
+		voice->volume_control |= 0x20;
+		if (old_volume > next) {
+			snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_START, next);
+			snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_END, old_volume);
+			voice->volume_control |= 0x40;
+		} else {
+			snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_START, old_volume);
+			snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_END, next);
+		}
+		snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_RATE, rate);
+		snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, voice->volume_control);
+		if (!gus->gf1.enh_mode) {
+			snd_gf1_delay(gus);
+			snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, voice->volume_control);
+		}
+		spin_unlock_irqrestore(&gus->reg_lock, flags);			
+		return;
+	}
+}
+
+static void do_pan_envelope(snd_gus_card_t *gus, snd_gus_voice_t *voice)
+{
+	unsigned long flags;
+	unsigned char old_pan;
+
+#if 0
+	snd_gf1_select_voice(gus, voice->number);
+	printk(" -%i- do_pan_envelope - flags = 0x%x (0x%x -> 0x%x)\n",
+		voice->number,
+		voice->flags,
+		voice->gf1_pan,
+		snd_gf1_i_read8(gus, SNDRV_GF1_VB_PAN) & 0x0f);
+#endif
+	if (gus->gf1.enh_mode) {
+		voice->flags &= ~(SNDRV_GF1_VFLG_EFFECT_TIMER1|SNDRV_GF1_VFLG_PAN);
+		return;
+	}
+	if (!gus->gf1.smooth_pan) {
+		spin_lock_irqsave(&gus->reg_lock, flags);			
+		snd_gf1_select_voice(gus, voice->number);
+		snd_gf1_write8(gus, SNDRV_GF1_VB_PAN, voice->gf1_pan);
+		spin_unlock_irqrestore(&gus->reg_lock, flags);
+		return;
+	}
+	if (!(voice->flags & SNDRV_GF1_VFLG_PAN))		/* before */
+		voice->flags |= SNDRV_GF1_VFLG_EFFECT_TIMER1|SNDRV_GF1_VFLG_PAN;
+	spin_lock_irqsave(&gus->reg_lock, flags);			
+	snd_gf1_select_voice(gus, voice->number);
+	old_pan = snd_gf1_read8(gus, SNDRV_GF1_VB_PAN) & 0x0f;
+	if (old_pan > voice->gf1_pan )
+		old_pan--;
+	if (old_pan < voice->gf1_pan)
+		old_pan++;
+	snd_gf1_write8(gus, SNDRV_GF1_VB_PAN, old_pan);
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+	if (old_pan == voice->gf1_pan)			/* the goal was reached */
+		voice->flags &= ~(SNDRV_GF1_VFLG_EFFECT_TIMER1|SNDRV_GF1_VFLG_PAN);
+#if 0
+	snd_gf1_select_voice(gus, voice->number);
+	printk(" -%i- (1) do_pan_envelope - flags = 0x%x (0x%x -> 0x%x)\n",
+	       voice->number,
+	       voice->flags,
+	       voice->gf1_pan,
+	       snd_gf1_i_read8(gus, GF1_VB_PAN) & 0x0f);
+#endif
+}
+
+static void set_enhanced_pan(snd_gus_card_t *gus, snd_gus_voice_t *voice, unsigned short pan)
+{
+	unsigned long flags;
+	unsigned short vlo, vro;
+  
+	vlo = SNDRV_GF1_ATTEN((SNDRV_GF1_ATTEN_TABLE_SIZE-1) - pan);
+	vro = SNDRV_GF1_ATTEN(pan);
+	if (pan != SNDRV_GF1_ATTEN_TABLE_SIZE - 1 && pan != 0) {
+		vlo >>= 1;
+		vro >>= 1;
+	}
+	vlo <<= 4;
+	vro <<= 4;
+#if 0
+	printk("vlo = 0x%x (0x%x), vro = 0x%x (0x%x)\n",
+			vlo, snd_gf1_i_read16(gus, GF1_VW_OFFSET_LEFT),
+			vro, snd_gf1_i_read16(gus, GF1_VW_OFFSET_RIGHT));
+#endif
+	spin_lock_irqsave(&gus->reg_lock, flags);			
+	snd_gf1_select_voice(gus, voice->number);
+        snd_gf1_write16(gus, SNDRV_GF1_VW_OFFSET_LEFT_FINAL, vlo);
+	snd_gf1_write16(gus, SNDRV_GF1_VW_OFFSET_RIGHT_FINAL, vro);
+	spin_unlock_irqrestore(&gus->reg_lock, flags);			
+	voice->vlo = vlo;
+	voice->vro = vro;
+}
+
+/*
+ *
+ */
+
+static void sample_start(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_position_t position)
+{
+	unsigned long flags;
+	unsigned int begin, addr, addr_end, addr_start;
+	int w_16;
+	simple_instrument_t *simple;
+	snd_seq_kinstr_t *instr;
+
+	instr = snd_seq_instr_find(gus->gf1.ilist, &voice->instr, 0, 1);
+	if (instr == NULL)
+		return;
+	voice->instr = instr->instr;	/* copy ID to speedup aliases */
+	simple = KINSTR_DATA(instr);
+	begin = simple->address.memory << 4;
+	w_16 = simple->format & SIMPLE_WAVE_16BIT ? 0x04 : 0;
+	addr_start = simple->loop_start;
+	if (simple->format & SIMPLE_WAVE_LOOP) {
+		addr_end = simple->loop_end;
+	} else {
+		addr_end = (simple->size << 4) - (w_16 ? 40 : 24);
+	}
+	if (simple->format & SIMPLE_WAVE_BACKWARD) {
+		addr = simple->loop_end;
+		if (position < simple->loop_end)
+			addr -= position;
+	} else {
+		addr = position;
+	}
+	voice->control = 0x00;
+	voice->mode = 0x20;		/* enable offset registers */
+	if (simple->format & SIMPLE_WAVE_16BIT)
+		voice->control |= 0x04;
+	if (simple->format & SIMPLE_WAVE_BACKWARD)
+		voice->control |= 0x40;
+	if (simple->format & SIMPLE_WAVE_LOOP) {
+		voice->control |= 0x08;
+	} else {
+		voice->control |= 0x20;
+	}
+	if (simple->format & SIMPLE_WAVE_BIDIR)
+		voice->control |= 0x10;
+	if (simple->format & SIMPLE_WAVE_ULAW)
+		voice->mode |= 0x40;
+	if (w_16) {
+		addr = ((addr << 1) & ~0x1f) | (addr & 0x0f);
+		addr_start = ((addr_start << 1) & ~0x1f) | (addr_start & 0x0f);
+		addr_end = ((addr_end << 1) & ~0x1f) | (addr_end & 0x0f);
+	}
+	addr += begin;
+	addr_start += begin;
+	addr_end += begin;
+	snd_gf1_stop_voice(gus, voice->number);	
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	snd_gf1_select_voice(gus, voice->number);
+	snd_gf1_write16(gus, SNDRV_GF1_VW_FREQUENCY, voice->fc_register + voice->fc_lfo);
+	voice->venv_state = VENV_BEFORE;
+	voice->volume_control = 0x03;
+	snd_gf1_write_addr(gus, SNDRV_GF1_VA_START, addr_start, w_16);
+	snd_gf1_write_addr(gus, SNDRV_GF1_VA_END, addr_end, w_16);
+	snd_gf1_write_addr(gus, SNDRV_GF1_VA_CURRENT, addr, w_16);
+	if (!gus->gf1.enh_mode) {
+		snd_gf1_write8(gus, SNDRV_GF1_VB_PAN, voice->gf1_pan);
+	} else {
+		snd_gf1_write16(gus, SNDRV_GF1_VW_OFFSET_LEFT, voice->vlo);
+		snd_gf1_write16(gus, SNDRV_GF1_VW_OFFSET_LEFT_FINAL, voice->vlo);
+		snd_gf1_write16(gus, SNDRV_GF1_VW_OFFSET_RIGHT, voice->vro);
+		snd_gf1_write16(gus, SNDRV_GF1_VW_OFFSET_RIGHT_FINAL, voice->vro);
+		snd_gf1_write8(gus, SNDRV_GF1_VB_ACCUMULATOR, voice->effect_accumulator);
+		snd_gf1_write16(gus, SNDRV_GF1_VW_EFFECT_VOLUME, voice->gf1_effect_volume);
+		snd_gf1_write16(gus, SNDRV_GF1_VW_EFFECT_VOLUME_FINAL, voice->gf1_effect_volume);
+	}
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+	do_volume_envelope(gus, voice);
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	snd_gf1_select_voice(gus, voice->number);
+	if (gus->gf1.enh_mode)
+		snd_gf1_write8(gus, SNDRV_GF1_VB_MODE, voice->mode);
+	snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice->control);
+	if (!gus->gf1.enh_mode) {
+		snd_gf1_delay(gus);
+		snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice->control );
+	}
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+#if 0
+	snd_gf1_print_voice_registers(gus);
+#endif
+	voice->flags |= SNDRV_GF1_VFLG_RUNNING;
+	snd_seq_instr_free_use(gus->gf1.ilist, instr);
+}
+
+static void sample_stop(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_stop_mode_t mode)
+{
+	unsigned char control;
+	unsigned long flags;
+
+	if (!(voice->flags & SNDRV_GF1_VFLG_RUNNING))
+		return;
+	switch (mode) {
+	default:
+		if (gus->gf1.volume_ramp > 0) {
+			if (voice->venv_state < VENV_RELEASE) {
+				voice->venv_state = VENV_RELEASE;
+				do_volume_envelope(gus, voice);
+			}
+		}
+		if (mode != SAMPLE_STOP_VENVELOPE) {
+			snd_gf1_stop_voice(gus, voice->number);
+			spin_lock_irqsave(&gus->reg_lock, flags);
+			snd_gf1_select_voice(gus, voice->number);
+			snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, SNDRV_GF1_MIN_VOLUME);
+			spin_unlock_irqrestore(&gus->reg_lock, flags);
+			voice->flags &= ~SNDRV_GF1_VFLG_RUNNING;
+		}
+		break;
+	case SAMPLE_STOP_LOOP:		/* disable loop only */
+		spin_lock_irqsave(&gus->reg_lock, flags);
+		snd_gf1_select_voice(gus, voice->number);
+		control = snd_gf1_read8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL);
+		control &= ~(0x83 | 0x04);
+		control |= 0x20;
+		snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, control);
+		spin_unlock_irqrestore(&gus->reg_lock, flags);
+		break;
+	}
+}
+
+static void sample_freq(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_frequency_t freq)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	voice->fc_register = snd_gf1_translate_freq(gus, freq);
+	snd_gf1_select_voice(gus, voice->number);
+	snd_gf1_write16(gus, SNDRV_GF1_VW_FREQUENCY, voice->fc_register + voice->fc_lfo);
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+}
+
+static void sample_volume(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_ev_volume_t *volume)
+{
+	if (volume->volume >= 0) {
+		volume->volume &= 0x3fff;
+		voice->gf1_volume = snd_gf1_lvol_to_gvol_raw(volume->volume << 2) << 4;
+		voice->venv_state_prev = VENV_SUSTAIN;
+		voice->venv_state = VENV_VOLUME;
+		do_volume_envelope(gus, voice);
+        }
+	if (volume->lr >= 0) {
+		volume->lr &= 0x3fff;
+		if (!gus->gf1.enh_mode) {
+			voice->gf1_pan = (volume->lr >> 10) & 15;
+			if (!gus->gf1.full_range_pan) {
+				if (voice->gf1_pan == 0)
+					voice->gf1_pan++;
+				if (voice->gf1_pan == 15)
+					voice->gf1_pan--;
+			}
+			voice->flags &= ~SNDRV_GF1_VFLG_PAN;	/* before */
+			do_pan_envelope(gus, voice);
+		} else {
+			set_enhanced_pan(gus, voice, volume->lr >> 7);
+		}
+	}
+}
+
+static void sample_loop(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_ev_loop_t *loop)
+{
+	unsigned long flags;
+	int w_16 = voice->control & 0x04;
+	unsigned int begin, addr_start, addr_end;
+	simple_instrument_t *simple;
+	snd_seq_kinstr_t *instr;
+
+#if 0
+	printk("voice_loop: start = 0x%x, end = 0x%x\n", loop->start, loop->end);
+#endif
+	instr = snd_seq_instr_find(gus->gf1.ilist, &voice->instr, 0, 1);
+	if (instr == NULL)
+		return;
+	voice->instr = instr->instr;	/* copy ID to speedup aliases */
+	simple = KINSTR_DATA(instr);
+	begin = simple->address.memory;
+	addr_start = loop->start;
+	addr_end = loop->end;
+	addr_start = (((addr_start << 1) & ~0x1f) | (addr_start & 0x0f)) + begin;
+	addr_end = (((addr_end << 1) & ~0x1f) | (addr_end & 0x0f)) + begin;
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	snd_gf1_select_voice(gus, voice->number);
+	snd_gf1_write_addr(gus, SNDRV_GF1_VA_START, addr_start, w_16);
+	snd_gf1_write_addr(gus, SNDRV_GF1_VA_END, addr_end, w_16);
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+	snd_seq_instr_free_use(gus->gf1.ilist, instr);
+}
+
+static void sample_pos(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_position_t position)
+{
+	unsigned long flags;
+	int w_16 = voice->control & 0x04;
+	unsigned int begin, addr;
+	simple_instrument_t *simple;
+	snd_seq_kinstr_t *instr;
+
+#if 0
+	printk("voice_loop: start = 0x%x, end = 0x%x\n", loop->start, loop->end);
+#endif
+	instr = snd_seq_instr_find(gus->gf1.ilist, &voice->instr, 0, 1);
+	if (instr == NULL)
+		return;
+	voice->instr = instr->instr;	/* copy ID to speedup aliases */
+	simple = KINSTR_DATA(instr);
+	begin = simple->address.memory;
+	addr = (((position << 1) & ~0x1f) | (position & 0x0f)) + begin;
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	snd_gf1_select_voice(gus, voice->number);
+	snd_gf1_write_addr(gus, SNDRV_GF1_VA_CURRENT, addr, w_16);
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+	snd_seq_instr_free_use(gus->gf1.ilist, instr);
+}
+
+#if 0
+
+static unsigned char get_effects_mask( ultra_card_t *card, int value )
+{
+  if ( value > 7 ) return 0;
+  if ( card -> gf1.effects && card -> gf1.effects -> chip_type == ULTRA_EFFECT_CHIP_INTERWAVE )
+    return card -> gf1.effects -> chip.interwave.voice_output[ value ];
+  return 0;
+}
+
+#endif
+
+static void sample_private1(snd_gus_card_t *card, snd_gus_voice_t *voice, unsigned char *data)
+{
+#if 0
+  unsigned long flags;
+  unsigned char uc;
+
+  switch ( *data ) {
+    case ULTRA_PRIV1_IW_EFFECT:
+      uc = get_effects_mask( card, ultra_get_byte( data, 4 ) );
+      uc |= get_effects_mask( card, ultra_get_byte( data, 4 ) >> 4 );
+      uc |= get_effects_mask( card, ultra_get_byte( data, 5 ) );
+      uc |= get_effects_mask( card, ultra_get_byte( data, 5 ) >> 4 );
+      voice -> data.simple.effect_accumulator = uc;
+      voice -> data.simple.effect_volume = ultra_translate_voice_volume( card, ultra_get_word( data, 2 ) ) << 4;
+      if ( !card -> gf1.enh_mode ) return;
+      if ( voice -> flags & VFLG_WAIT_FOR_START ) return;
+      if ( voice -> flags & VFLG_RUNNING )
+        {
+          CLI( &flags );
+          gf1_select_voice( card, voice -> number );
+          ultra_write8( card, GF1_VB_ACCUMULATOR, voice -> data.simple.effect_accumulator );
+          ultra_write16( card, GF1_VW_EFFECT_VOLUME_FINAL, voice -> data.simple.effect_volume );
+          STI( &flags );
+        }
+      break;
+   case ULTRA_PRIV1_IW_LFO:
+     ultra_lfo_command( card, voice -> number, data );
+  }
+#endif
+}
+
+#if 0
+
+/*
+ *
+ */
+
+static void note_stop( ultra_card_t *card, ultra_voice_t *voice, int wait )
+{
+}
+
+static void note_wait( ultra_card_t *card, ultra_voice_t *voice )
+{
+}
+
+static void note_off( ultra_card_t *card, ultra_voice_t *voice )
+{
+}
+
+static void note_volume( ultra_card_t *card, ultra_voice_t *voice )
+{
+}
+
+static void note_pitchbend( ultra_card_t *card, ultra_voice_t *voice )
+{
+}
+
+static void note_vibrato( ultra_card_t *card, ultra_voice_t *voice )
+{
+}
+
+static void note_tremolo( ultra_card_t *card, ultra_voice_t *voice )
+{
+}
+
+/*
+ *
+ */
+ 
+static void chn_trigger_down( ultra_card_t *card, ultra_channel_t *channel, ultra_instrument_t *instrument, unsigned char note, unsigned char velocity, unsigned char priority )
+{
+}
+
+static void chn_trigger_up( ultra_card_t *card, ultra_note_t *note )
+{
+}
+
+static void chn_control( ultra_card_t *card, ultra_channel_t *channel, unsigned short p1, unsigned short p2 )
+{
+}
+
+/*
+ *
+ */
+ 
+#endif
+
+void snd_gf1_simple_init(snd_gus_voice_t *voice)
+{
+	voice->handler_wave = interrupt_wave;
+	voice->handler_volume = interrupt_volume;
+	voice->handler_effect = interrupt_effect;
+	voice->volume_change = NULL;
+	voice->sample_ops = &sample_ops;
+}
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/gus_synth.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/gus_synth.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/gus_synth.c	(revision 91)
@@ -0,0 +1,317 @@
+/*
+ *  Routines for Gravis UltraSound soundcards - Synthesizer
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#define SNDRV_MAIN_OBJECT_FILE
+#include <sound/driver.h>
+#include <sound/gus.h>
+#include <sound/seq_device.h>
+
+/*
+ *
+ */
+
+static void snd_gus_synth_free_voices(snd_gus_card_t * gus, int client, int port)
+{
+	int idx;
+	snd_gus_voice_t * voice;
+	
+	for (idx = 0; idx < 32; idx++) {
+		voice = &gus->gf1.voices[idx];
+		if (voice->use && voice->client == client && voice->port == port)
+			snd_gf1_free_voice(gus, voice);
+	}
+}
+
+static int snd_gus_synth_use(void *private_data, snd_seq_port_subscribe_t *info)
+{
+	snd_gus_port_t * port = (snd_gus_port_t *)private_data;
+	snd_gus_card_t * gus = port->gus;
+	snd_gus_voice_t * voice;
+	int idx;
+
+	if (info->voices > 32)
+		return -EINVAL;
+	down(&gus->register_mutex);
+	if (!snd_gus_use_inc(gus)) {
+		up(&gus->register_mutex);
+		return -EFAULT;
+	}
+	for (idx = 0; idx < info->voices; idx++) {
+		voice = snd_gf1_alloc_voice(gus, SNDRV_GF1_VOICE_TYPE_SYNTH, info->sender.client, info->sender.port);
+		if (voice == NULL) {
+			snd_gus_synth_free_voices(gus, info->sender.client, info->sender.port);
+			up(&gus->register_mutex);
+			return -EBUSY;
+		}
+		voice->index = idx;
+	}
+	up(&gus->register_mutex);
+	return 0;
+}
+
+static int snd_gus_synth_unuse(void *private_data, snd_seq_port_subscribe_t *info)
+{
+	snd_gus_port_t * port = (snd_gus_port_t *)private_data;
+	snd_gus_card_t * gus = port->gus;
+
+	down(&gus->register_mutex);
+	snd_gus_synth_free_voices(gus, info->sender.client, info->sender.port);
+	up(&gus->register_mutex);
+	return 0;
+}
+
+/*
+ *
+ */
+
+static void snd_gus_synth_free_private_instruments(snd_gus_port_t *p, int client)
+{
+	snd_seq_instr_header_t ifree;
+
+	memset(&ifree, 0, sizeof(ifree));
+	ifree.cmd = SNDRV_SEQ_INSTR_FREE_CMD_PRIVATE;
+	snd_seq_instr_list_free_cond(p->gus->gf1.ilist, &ifree, client, 0);
+}
+ 
+int snd_gus_synth_event_input(snd_seq_event_t *ev, int direct, void *private_data, int atomic, int hop)
+{
+	snd_gus_port_t * p = (snd_gus_port_t *) private_data;
+
+	snd_assert(p != NULL, return -EINVAL);
+	if (ev->type >= SNDRV_SEQ_EVENT_SAMPLE &&
+	    ev->type <= SNDRV_SEQ_EVENT_SAMPLE_PRIVATE1) {
+		snd_gus_sample_event(ev, p);
+		return 0;
+	}
+	if (ev->source.client == SNDRV_SEQ_CLIENT_SYSTEM &&
+	    ev->source.port == SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE) {
+		if (ev->type == SNDRV_SEQ_EVENT_CLIENT_EXIT) {
+			snd_gus_synth_free_private_instruments(p, ev->data.addr.client);
+			return 0;
+		}
+	}
+	if (direct) {
+		if (ev->type >= SNDRV_SEQ_EVENT_INSTR_BEGIN) {
+			snd_seq_instr_event(&p->gus->gf1.iwffff_ops.kops,
+					    p->gus->gf1.ilist,
+					    ev,
+					    p->gus->gf1.seq_client,
+					    atomic, hop);
+			return 0;
+		}
+	}
+	return 0;
+}
+
+static void snd_gus_synth_instr_notify(void *private_data,
+				       snd_seq_kinstr_t *instr,
+				       int what)
+{
+	int idx;
+	snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, private_data, return);
+	snd_gus_voice_t *pvoice;
+	unsigned long flags;
+	
+	spin_lock_irqsave(&gus->event_lock, flags);
+	for (idx = 0; idx < 32; idx++) {
+		pvoice = &gus->gf1.voices[idx];
+		if (pvoice->use && !memcmp(&pvoice->instr, &instr->instr, sizeof(pvoice->instr))) {
+			if (pvoice->sample_ops && pvoice->sample_ops->sample_stop) {
+				pvoice->sample_ops->sample_stop(gus, pvoice, SAMPLE_STOP_IMMEDIATELY);
+			} else {
+				snd_gf1_stop_voice(gus, pvoice->number);
+				pvoice->flags &= ~SNDRV_GF1_VFLG_RUNNING;
+			}
+		}
+	}
+	spin_unlock_irqrestore(&gus->event_lock, flags);
+}
+
+/*
+ *
+ */
+
+static void snd_gus_synth_free_port(void *private_data)
+{
+	snd_gus_port_t * p = (snd_gus_port_t *)private_data;
+	
+	if (p)
+		snd_midi_channel_free_set(p->chset);
+}
+
+static int snd_gus_synth_create_port(snd_gus_card_t * gus, int idx)
+{
+	snd_gus_port_t * p;
+	snd_seq_port_callback_t callbacks;
+	char name[32];
+	int result;
+	
+	p = &gus->gf1.seq_ports[idx];
+	p->chset = snd_midi_channel_alloc_set(16);
+	if (p->chset == NULL)
+		return -ENOMEM;
+	p->chset->private_data = p;
+	p->gus = gus;
+	p->client = gus->gf1.seq_client;
+
+	memset(&callbacks, 0, sizeof(callbacks));
+	callbacks.owner = THIS_MODULE;
+	callbacks.use = snd_gus_synth_use;
+	callbacks.unuse = snd_gus_synth_unuse;
+	callbacks.event_input = snd_gus_synth_event_input;
+	callbacks.private_free = snd_gus_synth_free_port;
+	callbacks.private_data = p;
+	
+	sprintf(name, "%s port %i", gus->interwave ? "AMD InterWave" : "GF1", idx);
+	p->chset->port = snd_seq_event_port_attach(gus->gf1.seq_client,
+						   &callbacks,
+						   SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE,
+						   SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC |
+						   SNDRV_SEQ_PORT_TYPE_MIDI_GM |
+						   SNDRV_SEQ_PORT_TYPE_MIDI_GS |
+						   SNDRV_SEQ_PORT_TYPE_DIRECT_SAMPLE |
+						   SNDRV_SEQ_PORT_TYPE_SYNTH,
+						   16, 0,
+						   name);
+	if (p->chset->port < 0) {
+		result = p->chset->port;
+		snd_gus_synth_free_port(p);
+		return result;
+	}
+	p->port = p->chset->port;
+	return 0;
+}						 
+
+/*
+ *
+ */
+
+static int snd_gus_synth_new_device(snd_seq_device_t *dev)
+{
+	snd_gus_card_t *gus;
+	int client, i;
+	snd_seq_client_callback_t callbacks;
+	snd_seq_client_info_t cinfo;
+	snd_seq_port_subscribe_t sub;
+	snd_iwffff_ops_t *iwops;
+	snd_gf1_ops_t *gf1ops;
+	snd_simple_ops_t *simpleops;
+
+	gus = *(snd_gus_card_t**)SNDRV_SEQ_DEVICE_ARGPTR(dev);
+	if (gus == NULL)
+		return -EINVAL;
+
+	init_MUTEX(&gus->register_mutex);
+	gus->gf1.seq_client = -1;
+	
+	/* allocate new client */
+	memset(&callbacks, 0, sizeof(callbacks));
+	callbacks.private_data = gus;
+	callbacks.allow_output = callbacks.allow_input = 1;
+	client = gus->gf1.seq_client =
+			snd_seq_create_kernel_client(gus->card, 1, &callbacks);
+	if (client < 0)
+		return client;
+
+	/* change name of client */
+	memset(&cinfo, 0, sizeof(cinfo));
+	cinfo.client = client;
+	cinfo.type = KERNEL_CLIENT;
+	sprintf(cinfo.name, gus->interwave ? "AMD InterWave" : "GF1");
+	snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, &cinfo);
+
+	for (i = 0; i < 4; i++)
+		snd_gus_synth_create_port(gus, i);
+		
+	gus->gf1.ilist = snd_seq_instr_list_new();
+	if (gus->gf1.ilist == NULL) {
+		snd_seq_delete_kernel_client(client);	
+		gus->gf1.seq_client = -1;
+		return -ENOMEM;
+	}
+	gus->gf1.ilist->flags = SNDRV_SEQ_INSTR_FLG_DIRECT;
+
+	simpleops = &gus->gf1.simple_ops;
+	snd_seq_simple_init(simpleops, gus, NULL);
+	simpleops->put_sample = snd_gus_simple_put_sample;
+	simpleops->get_sample = snd_gus_simple_get_sample;
+	simpleops->remove_sample = snd_gus_simple_remove_sample;
+	simpleops->notify = snd_gus_synth_instr_notify;
+
+	gf1ops = &gus->gf1.gf1_ops;
+	snd_seq_gf1_init(gf1ops, gus, &simpleops->kops);
+	gf1ops->put_sample = snd_gus_gf1_put_sample;
+	gf1ops->get_sample = snd_gus_gf1_get_sample;
+	gf1ops->remove_sample = snd_gus_gf1_remove_sample;
+	gf1ops->notify = snd_gus_synth_instr_notify;
+
+	iwops = &gus->gf1.iwffff_ops;
+	snd_seq_iwffff_init(iwops, gus, &gf1ops->kops);
+	iwops->put_sample = snd_gus_iwffff_put_sample;
+	iwops->get_sample = snd_gus_iwffff_get_sample;
+	iwops->remove_sample = snd_gus_iwffff_remove_sample;
+	iwops->notify = snd_gus_synth_instr_notify;
+
+	memset(&sub, 0, sizeof(sub));
+	sub.sender.client = SNDRV_SEQ_CLIENT_SYSTEM;
+	sub.sender.port = SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE;
+	sub.dest.client = client;
+	sub.dest.port = 0;
+	snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &sub);
+
+	return 0;
+}
+
+static int snd_gus_synth_delete_device(snd_seq_device_t *dev)
+{
+	snd_gus_card_t *gus;
+
+	gus = *(snd_gus_card_t**)SNDRV_SEQ_DEVICE_ARGPTR(dev);
+	if (gus == NULL)
+		return -EINVAL;
+
+	if (gus->gf1.seq_client >= 0) {
+		snd_seq_delete_kernel_client(gus->gf1.seq_client);	
+		gus->gf1.seq_client = -1;
+	}
+	if (gus->gf1.ilist)
+		snd_seq_instr_list_free(&gus->gf1.ilist);
+	return 0;
+}
+
+static int __init alsa_gus_synth_init(void)
+{
+	static snd_seq_dev_ops_t ops = {
+		snd_gus_synth_new_device,
+		snd_gus_synth_delete_device
+	};
+
+	return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_GUS, &ops,
+					      sizeof(snd_gus_card_t*));
+}
+
+static void __exit alsa_gus_synth_exit(void)
+{
+	snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_GUS);
+}
+
+module_init(alsa_gus_synth_init)
+module_exit(alsa_gus_synth_exit)
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/gus_tables.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/gus_tables.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/gus_tables.h	(revision 91)
@@ -0,0 +1,86 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#define SNDRV_GF1_SCALE_TABLE_SIZE	128
+#define SNDRV_GF1_ATTEN_TABLE_SIZE	128
+
+#ifdef __GUS_TABLES_ALLOC__
+
+unsigned int snd_gf1_scale_table[SNDRV_GF1_SCALE_TABLE_SIZE] =
+{
+      8372,      8870,      9397,      9956,     10548,     11175,
+     11840,     12544,     13290,     14080,     14917,     15804,
+     16744,     17740,     18795,     19912,     21096,     22351,
+     23680,     25088,     26580,     28160,     29834,     31609,
+     33488,     35479,     37589,     39824,     42192,     44701,
+     47359,     50175,     53159,     56320,     59669,     63217,
+     66976,     70959,     75178,     79649,     84385,     89402,
+     94719,    100351,    106318,    112640,    119338,    126434,
+    133952,    141918,    150356,    159297,    168769,    178805,
+    189437,    200702,    212636,    225280,    238676,    252868,
+    267905,    283835,    300713,    318594,    337539,    357610,
+    378874,    401403,    425272,    450560,    477352,    505737,
+    535809,    567670,    601425,    637188,    675077,    715219,
+    757749,    802807,    850544,    901120,    954703,   1011473,
+   1071618,   1135340,   1202851,   1274376,   1350154,   1430439,
+   1515497,   1605613,   1701088,   1802240,   1909407,   2022946,
+   2143237,   2270680,   2405702,   2548752,   2700309,   2860878,
+   3030994,   3211227,   3402176,   3604480,   3818814,   4045892,
+   4286473,   4541360,   4811404,   5097505,   5400618,   5721755,
+   6061989,   6422453,   6804352,   7208960,   7637627,   8091784,
+   8572947,   9082720,   9622807,  10195009,  10801236,  11443511,
+  12123977,  12844906
+};
+
+unsigned short snd_gf1_atten_table[SNDRV_GF1_ATTEN_TABLE_SIZE] = {
+  4095 /* 0   */,1789 /* 1   */,1533 /* 2   */,1383 /* 3   */,1277 /* 4   */,
+  1195 /* 5   */,1127 /* 6   */,1070 /* 7   */,1021 /* 8   */,978  /* 9   */,
+  939  /* 10  */,903  /* 11  */,871  /* 12  */,842  /* 13  */,814  /* 14  */,
+  789  /* 15  */,765  /* 16  */,743  /* 17  */,722  /* 18  */,702  /* 19  */,
+  683  /* 20  */,665  /* 21  */,647  /* 22  */,631  /* 23  */,615  /* 24  */,
+  600  /* 25  */,586  /* 26  */,572  /* 27  */,558  /* 28  */,545  /* 29  */,
+  533  /* 30  */,521  /* 31  */,509  /* 32  */,498  /* 33  */,487  /* 34  */,
+  476  /* 35  */,466  /* 36  */,455  /* 37  */,446  /* 38  */,436  /* 39  */,
+  427  /* 40  */,418  /* 41  */,409  /* 42  */,400  /* 43  */,391  /* 44  */,
+  383  /* 45  */,375  /* 46  */,367  /* 47  */,359  /* 48  */,352  /* 49  */,
+  344  /* 50  */,337  /* 51  */,330  /* 52  */,323  /* 53  */,316  /* 54  */,
+  309  /* 55  */,302  /* 56  */,296  /* 57  */,289  /* 58  */,283  /* 59  */,
+  277  /* 60  */,271  /* 61  */,265  /* 62  */,259  /* 63  */,253  /* 64  */,
+  247  /* 65  */,242  /* 66  */,236  /* 67  */,231  /* 68  */,225  /* 69  */,
+  220  /* 70  */,215  /* 71  */,210  /* 72  */,205  /* 73  */,199  /* 74  */,
+  195  /* 75  */,190  /* 76  */,185  /* 77  */,180  /* 78  */,175  /* 79  */,
+  171  /* 80  */,166  /* 81  */,162  /* 82  */,157  /* 83  */,153  /* 84  */,
+  148  /* 85  */,144  /* 86  */,140  /* 87  */,135  /* 88  */,131  /* 89  */,
+  127  /* 90  */,123  /* 91  */,119  /* 92  */,115  /* 93  */,111  /* 94  */,
+  107  /* 95  */,103  /* 96  */,100  /* 97  */,96   /* 98  */,92   /* 99  */,
+  88   /* 100 */,85   /* 101 */,81   /* 102 */,77   /* 103 */,74   /* 104 */,
+  70   /* 105 */,67   /* 106 */,63   /* 107 */,60   /* 108 */,56   /* 109 */,
+  53   /* 110 */,50   /* 111 */,46   /* 112 */,43   /* 113 */,40   /* 114 */,
+  37   /* 115 */,33   /* 116 */,30   /* 117 */,27   /* 118 */,24   /* 119 */,
+  21   /* 120 */,18   /* 121 */,15   /* 122 */,12   /* 123 */,9    /* 124 */,
+  6    /* 125 */,3    /* 126 */,0    /* 127 */,
+};
+
+#else
+
+unsigned int snd_gf1_scale_table[SNDRV_GF1_SCALE_TABLE_SIZE];
+unsigned short snd_gf1_atten_table[SNDRV_GF1_ATTEN_TABLE_SIZE];
+
+#endif
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/gus_timer.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/gus_timer.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/gus_timer.c	(revision 91)
@@ -0,0 +1,226 @@
+/*
+ *  Routines for Gravis UltraSound soundcards - Timers
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *  GUS have similar timers as AdLib (OPL2/OPL3 chips).
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <sound/driver.h>
+#include <sound/gus.h>
+
+#define chip_t snd_gus_card_t
+
+/*
+ *  Timer 1 - 80us
+ */
+
+static int snd_gf1_timer1_start(snd_timer_t * timer)
+{
+	unsigned long flags;
+	unsigned char tmp;
+	unsigned int ticks;
+	snd_gus_card_t *gus;
+
+	gus = snd_timer_chip(timer);
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	ticks = timer->sticks;
+	tmp = (gus->gf1.timer_enabled |= 4);
+	snd_gf1_write8(gus, SNDRV_GF1_GB_ADLIB_TIMER_1, 256 - ticks);	/* timer 1 count */
+	snd_gf1_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, tmp);	/* enable timer 1 IRQ */
+	snd_gf1_adlib_write(gus, 0x04, tmp >> 2);	/* timer 2 start */
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+	return 0;
+}
+
+static int snd_gf1_timer1_stop(snd_timer_t * timer)
+{
+	unsigned long flags;
+	unsigned char tmp;
+	snd_gus_card_t *gus;
+
+	gus = snd_timer_chip(timer);
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	tmp = (gus->gf1.timer_enabled &= ~4);
+	snd_gf1_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, tmp);	/* disable timer #1 */
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+	return 0;
+}
+
+/*
+ *  Timer 2 - 320us
+ */
+
+static int snd_gf1_timer2_start(snd_timer_t * timer)
+{
+	unsigned long flags;
+	unsigned char tmp;
+	unsigned int ticks;
+	snd_gus_card_t *gus;
+
+	gus = snd_timer_chip(timer);
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	ticks = timer->sticks;
+	tmp = (gus->gf1.timer_enabled |= 8);
+	snd_gf1_write8(gus, SNDRV_GF1_GB_ADLIB_TIMER_2, 256 - ticks);	/* timer 2 count */
+	snd_gf1_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, tmp);	/* enable timer 2 IRQ */
+	snd_gf1_adlib_write(gus, 0x04, tmp >> 2);	/* timer 2 start */
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+	return 0;
+}
+
+static int snd_gf1_timer2_stop(snd_timer_t * timer)
+{
+	unsigned long flags;
+	unsigned char tmp;
+	snd_gus_card_t *gus;
+
+	gus = snd_timer_chip(timer);
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	tmp = (gus->gf1.timer_enabled &= ~8);
+	snd_gf1_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, tmp);	/* disable timer #1 */
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+	return 0;
+}
+
+/*
+
+ */
+
+static void snd_gf1_interrupt_timer1(snd_gus_card_t * gus)
+{
+	snd_timer_t *timer = gus->gf1.timer1;
+
+	if (timer == NULL)
+		return;
+	snd_timer_interrupt(timer, timer->sticks);
+}
+
+static void snd_gf1_interrupt_timer2(snd_gus_card_t * gus)
+{
+	snd_timer_t *timer = gus->gf1.timer2;
+
+	if (timer == NULL)
+		return;
+	snd_timer_interrupt(timer, timer->sticks);
+}
+
+/*
+
+ */
+
+#ifdef TARGET_OS2
+static struct _snd_timer_hardware snd_gf1_timer1 =
+{
+	SNDRV_TIMER_HW_STOP,
+	80000,
+	256,
+        0,0,0,
+	snd_gf1_timer1_start,
+	snd_gf1_timer1_stop,
+};
+
+static struct _snd_timer_hardware snd_gf1_timer2 =
+{
+	SNDRV_TIMER_HW_STOP,
+	320000,
+	256,
+        0,0,0,
+	snd_gf1_timer2_start,
+	snd_gf1_timer2_stop,
+};
+#else
+static struct _snd_timer_hardware snd_gf1_timer1 =
+{
+	flags:          SNDRV_TIMER_HW_STOP,
+	resolution:     80000,
+	ticks:          256,
+	start:          snd_gf1_timer1_start,
+	stop:           snd_gf1_timer1_stop,
+};
+
+static struct _snd_timer_hardware snd_gf1_timer2 =
+{
+	flags:          SNDRV_TIMER_HW_STOP,
+	resolution:     320000,
+	ticks:          256,
+	start:          snd_gf1_timer2_start,
+	stop:           snd_gf1_timer2_stop,
+};
+#endif
+
+static void snd_gf1_timer1_free(snd_timer_t *timer)
+{
+	snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, timer->private_data, return);
+	gus->gf1.timer1 = NULL;
+}
+
+static void snd_gf1_timer2_free(snd_timer_t *timer)
+{
+	snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, timer->private_data, return);
+	gus->gf1.timer2 = NULL;
+}
+
+void snd_gf1_timers_init(snd_gus_card_t * gus)
+{
+	snd_timer_t *timer;
+	snd_timer_id_t tid;
+
+	if (gus->gf1.timer1 != NULL || gus->gf1.timer2 != NULL)
+		return;
+
+	gus->gf1.interrupt_handler_timer1 = snd_gf1_interrupt_timer1;
+	gus->gf1.interrupt_handler_timer2 = snd_gf1_interrupt_timer2;
+
+	tid.dev_class = SNDRV_TIMER_CLASS_CARD;
+	tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE;
+	tid.card = gus->card->number;
+	tid.device = gus->timer_dev;
+	tid.subdevice = 0;
+
+	if (snd_timer_new(gus->card, "GF1 timer", &tid, &timer) >= 0) {
+		strcpy(timer->name, "GF1 timer #1");
+		timer->private_data = gus;
+		timer->private_free = snd_gf1_timer1_free;
+		timer->hw = snd_gf1_timer1;
+	}
+	gus->gf1.timer1 = timer;
+
+	tid.device++;
+
+	if (snd_timer_new(gus->card, "GF1 timer", &tid, &timer) >= 0) {
+		strcpy(timer->name, "GF1 timer #2");
+		timer->private_data = gus;
+		timer->private_free = snd_gf1_timer2_free;
+		timer->hw = snd_gf1_timer2;
+	}
+	gus->gf1.timer2 = timer;
+}
+
+void snd_gf1_timers_done(snd_gus_card_t * gus)
+{
+	snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_TIMER1 | SNDRV_GF1_HANDLER_TIMER2);
+	if (gus->gf1.timer1) {
+		snd_device_free(gus->card, gus->gf1.timer1);
+		gus->gf1.timer1 = NULL;
+	}
+	if (gus->gf1.timer2) {
+		snd_device_free(gus->card, gus->gf1.timer2);
+		gus->gf1.timer2 = NULL;
+	}
+}
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/gus_uart.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/gus_uart.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/gus_uart.c	(revision 91)
@@ -0,0 +1,271 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  Routines for the GF1 MIDI interface - like UART 6850
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <sound/driver.h>
+#include <sound/gus.h>
+
+static void snd_gf1_interrupt_midi_in(snd_gus_card_t * gus)
+{
+	int count;
+	unsigned char stat, data, byte;
+	unsigned long flags;
+
+	count = 10;
+	while (count) {
+		spin_lock_irqsave(&gus->uart_cmd_lock, flags);
+		stat = snd_gf1_uart_stat(gus);
+		if (!(stat & 0x01)) {	/* data in Rx FIFO? */
+			spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
+			count--;
+			continue;
+		}
+		count = 100;	/* arm counter to new value */
+		data = snd_gf1_uart_get(gus);
+		if (!(gus->gf1.uart_cmd & 0x80)) {
+			spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
+			continue;
+		}			
+		if (stat & 0x10) {	/* framing error */
+			gus->gf1.uart_framing++;
+			spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
+			snd_rawmidi_receive_reset(gus->midi_substream_input);
+			continue;
+		}
+		byte = snd_gf1_uart_get(gus);
+		spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
+		snd_rawmidi_receive(gus->midi_substream_input, &byte, 1);
+		if (stat & 0x20) {
+			gus->gf1.uart_overrun++;
+			snd_rawmidi_receive_reset(gus->midi_substream_input);
+		}
+	}
+}
+
+static void snd_gf1_interrupt_midi_out(snd_gus_card_t * gus)
+{
+	char byte;
+	unsigned long flags;
+
+	/* try unlock output */
+	if (snd_gf1_uart_stat(gus) & 0x01)
+		snd_gf1_interrupt_midi_in(gus);
+
+	spin_lock_irqsave(&gus->uart_cmd_lock, flags);
+	if (snd_gf1_uart_stat(gus) & 0x02) {	/* Tx FIFO free? */
+		if (snd_rawmidi_transmit(gus->midi_substream_output, &byte, 1) != 1) {	/* no other bytes or error */
+			snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd & ~0x20); /* disable Tx interrupt */
+		} else {
+			snd_gf1_uart_put(gus, byte);
+		}
+	}
+	spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
+}
+
+static void snd_gf1_uart_reset(snd_gus_card_t * gus, int close)
+{
+	snd_gf1_uart_cmd(gus, 0x03);	/* reset */
+	if (!close && gus->uart_enable) {
+		udelay(160);
+		snd_gf1_uart_cmd(gus, 0x00);	/* normal operations */
+	}
+}
+
+static int snd_gf1_uart_output_open(snd_rawmidi_substream_t * substream)
+{
+	unsigned long flags;
+	snd_gus_card_t *gus;
+
+	gus = snd_magic_cast(snd_gus_card_t, substream->rmidi->private_data, return -ENXIO);
+	spin_lock_irqsave(&gus->uart_cmd_lock, flags);
+	if (!(gus->gf1.uart_cmd & 0x80)) {	/* input active? */
+		snd_gf1_uart_reset(gus, 0);
+	}
+	gus->gf1.interrupt_handler_midi_out = snd_gf1_interrupt_midi_out;
+	gus->midi_substream_output = substream;
+	spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
+#if 0
+	snd_printk("write init - cmd = 0x%x, stat = 0x%x\n", gus->gf1.uart_cmd, snd_gf1_uart_stat(gus));
+#endif
+	return 0;
+}
+
+static int snd_gf1_uart_input_open(snd_rawmidi_substream_t * substream)
+{
+	unsigned long flags;
+	snd_gus_card_t *gus;
+	int i;
+
+	gus = snd_magic_cast(snd_gus_card_t, substream->rmidi->private_data, return -ENXIO);
+	spin_lock_irqsave(&gus->uart_cmd_lock, flags);
+	if (gus->gf1.interrupt_handler_midi_out != snd_gf1_interrupt_midi_out) {
+		snd_gf1_uart_reset(gus, 0);
+	}
+	gus->gf1.interrupt_handler_midi_in = snd_gf1_interrupt_midi_in;
+	gus->midi_substream_input = substream;
+	if (gus->uart_enable) {
+		for (i = 0; i < 1000 && (snd_gf1_uart_stat(gus) & 0x01); i++)
+			snd_gf1_uart_get(gus);	/* clean Rx */
+		if (i >= 1000)
+			snd_printk("gus midi uart init read - cleanup error\n");
+	}
+	spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
+#if 0
+	snd_printk("read init - enable = %i, cmd = 0x%x, stat = 0x%x\n", gus->uart_enable, gus->gf1.uart_cmd, snd_gf1_uart_stat(gus));
+	snd_printk("[0x%x] reg (ctrl/status) = 0x%x, reg (data) = 0x%x (page = 0x%x)\n", gus->gf1.port + 0x100, inb(gus->gf1.port + 0x100), inb(gus->gf1.port + 0x101), inb(gus->gf1.port + 0x102));
+#endif
+	return 0;
+}
+
+static int snd_gf1_uart_output_close(snd_rawmidi_substream_t * substream)
+{
+	unsigned long flags;
+	snd_gus_card_t *gus;
+
+	gus = snd_magic_cast(snd_gus_card_t, substream->rmidi->private_data, return -ENXIO);
+	spin_lock_irqsave(&gus->uart_cmd_lock, flags);
+	if (gus->gf1.interrupt_handler_midi_in != snd_gf1_interrupt_midi_in)
+		snd_gf1_uart_reset(gus, 1);
+	snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_MIDI_OUT);
+	gus->midi_substream_output = NULL;
+	spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
+	return 0;
+}
+
+static int snd_gf1_uart_input_close(snd_rawmidi_substream_t * substream)
+{
+	unsigned long flags;
+	snd_gus_card_t *gus;
+
+	gus = snd_magic_cast(snd_gus_card_t, substream->rmidi->private_data, return -ENXIO);
+	spin_lock_irqsave(&gus->uart_cmd_lock, flags);
+	if (gus->gf1.interrupt_handler_midi_out != snd_gf1_interrupt_midi_out)
+		snd_gf1_uart_reset(gus, 1);
+	snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_MIDI_IN);
+	gus->midi_substream_input = NULL;
+	spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
+	return 0;
+}
+
+static void snd_gf1_uart_input_trigger(snd_rawmidi_substream_t * substream, int up)
+{
+	snd_gus_card_t *gus;
+	unsigned long flags;
+
+	gus = snd_magic_cast(snd_gus_card_t, substream->rmidi->private_data, return);
+
+	spin_lock_irqsave(&gus->uart_cmd_lock, flags);
+	if (up) {
+		if ((gus->gf1.uart_cmd & 0x80) == 0)
+			snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd | 0x80); /* enable Rx interrupts */
+	} else {
+		if (gus->gf1.uart_cmd & 0x80)
+			snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd & ~0x80); /* disable Rx interrupts */
+	}
+	spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
+}
+
+static void snd_gf1_uart_output_trigger(snd_rawmidi_substream_t * substream, int up)
+{
+	unsigned long flags;
+	snd_gus_card_t *gus;
+	char byte;
+	int timeout;
+
+	gus = snd_magic_cast(snd_gus_card_t, substream->rmidi->private_data, return);
+
+	spin_lock_irqsave(&gus->uart_cmd_lock, flags);
+	if (up) {
+		if ((gus->gf1.uart_cmd & 0x20) == 0) {
+			spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
+			/* wait for empty Rx - Tx is probably unlocked */
+			timeout = 10000;
+			while (timeout-- > 0 && snd_gf1_uart_stat(gus) & 0x01);
+			/* Tx FIFO free? */
+			spin_lock_irqsave(&gus->uart_cmd_lock, flags);
+			if (gus->gf1.uart_cmd & 0x20) {
+				spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
+				return;
+			}
+			if (snd_gf1_uart_stat(gus) & 0x02) {
+				if (snd_rawmidi_transmit(substream, &byte, 1) != 1) {
+					spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
+					return;
+				}
+				snd_gf1_uart_put(gus, byte);
+			}
+			snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd | 0x20);	/* enable Tx interrupt */
+		}
+	} else {
+		if (gus->gf1.uart_cmd & 0x20)
+			snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd & ~0x20);
+	}
+	spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
+}
+
+#ifdef TARGET_OS2
+static snd_rawmidi_ops_t snd_gf1_uart_output =
+{
+	snd_gf1_uart_output_open,
+	snd_gf1_uart_output_close,
+	snd_gf1_uart_output_trigger,0
+};
+
+static snd_rawmidi_ops_t snd_gf1_uart_input =
+{
+	snd_gf1_uart_input_open,
+	snd_gf1_uart_input_close,
+	snd_gf1_uart_input_trigger,0
+};
+#else
+static snd_rawmidi_ops_t snd_gf1_uart_output =
+{
+	open:		snd_gf1_uart_output_open,
+	close:		snd_gf1_uart_output_close,
+	trigger:	snd_gf1_uart_output_trigger,
+};
+
+static snd_rawmidi_ops_t snd_gf1_uart_input =
+{
+	open:           snd_gf1_uart_input_open,
+	close:          snd_gf1_uart_input_close,
+	trigger:        snd_gf1_uart_input_trigger,
+};
+#endif
+
+int snd_gf1_rawmidi_new(snd_gus_card_t * gus, int device, snd_rawmidi_t ** rrawmidi)
+{
+	snd_rawmidi_t *rmidi;
+	int err;
+
+	if (rrawmidi)
+		*rrawmidi = NULL;
+	if ((err = snd_rawmidi_new(gus->card, "GF1", device, 1, 1, &rmidi)) < 0)
+		return err;
+	strcpy(rmidi->name, gus->interwave ? "AMD InterWave" : "GF1");
+	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_gf1_uart_output);
+	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_gf1_uart_input);
+	rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX;
+	rmidi->private_data = gus;
+	gus->midi_uart = rmidi;
+	if (rrawmidi)
+		*rrawmidi = rmidi;
+	return err;
+}
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/gus_volume.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/gus_volume.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/gus_volume.c	(revision 91)
@@ -0,0 +1,206 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <sound/driver.h>
+#include <sound/gus.h>
+#define __GUS_TABLES_ALLOC__
+#include "gus_tables.h"
+
+unsigned short snd_gf1_lvol_to_gvol_raw(unsigned int vol)
+{
+	unsigned short e, m, tmp;
+
+	if (vol > 65535)
+		vol = 65535;
+	tmp = vol;
+	e = 7;
+	if (tmp < 128) {
+		while (e > 0 && tmp < (1 << e))
+			e--;
+	} else {
+		while (tmp > 255) {
+			tmp >>= 1;
+			e++;
+		}
+	}
+	m = vol - (1 << e);
+	if (m > 0) {
+		if (e > 8)
+			m >>= e - 8;
+		else if (e < 8)
+			m <<= 8 - e;
+		m &= 255;
+	}
+	return (e << 8) | m;
+}
+
+unsigned int snd_gf1_gvol_to_lvol_raw(unsigned short gf1_vol)
+{
+	unsigned int rvol;
+	unsigned short e, m;
+
+	if (!gf1_vol)
+		return 0;
+	e = gf1_vol >> 8;
+	m = (unsigned char) gf1_vol;
+	rvol = 1 << e;
+	if (e > 8)
+		return rvol | (m << (e - 8));
+	return rvol | (m >> (8 - e));
+}
+
+unsigned int snd_gf1_calc_ramp_rate(snd_gus_card_t * gus,
+				    unsigned short start,
+				    unsigned short end,
+				    unsigned int us)
+{
+	static unsigned char vol_rates[19] =
+	{
+		23, 24, 26, 28, 29, 31, 32, 34,
+		36, 37, 39, 40, 42, 44, 45, 47,
+		49, 50, 52
+	};
+	unsigned short range, increment, value, i;
+
+	start >>= 4;
+	end >>= 4;
+	if (start < end)
+		us /= end - start;
+	else
+		us /= start - end;
+	range = 4;
+	value = gus->gf1.enh_mode ?
+	    vol_rates[0] :
+	    vol_rates[gus->gf1.active_voices - 14];
+	for (i = 0; i < 3; i++) {
+		if (us < value) {
+			range = i;
+			break;
+		} else
+			value <<= 3;
+	}
+	if (range == 4) {
+		range = 3;
+		increment = 1;
+	} else
+		increment = (value + (value >> 1)) / us;
+	return (range << 6) | (increment & 0x3f);
+}
+
+unsigned short snd_gf1_translate_freq(snd_gus_card_t * gus, unsigned int freq16)
+{
+	freq16 >>= 3;
+	if (freq16 < 50)
+		freq16 = 50;
+	if (freq16 & 0xf8000000) {
+		freq16 = ~0xf8000000;
+		snd_printk("snd_gf1_translate_freq: overflow - freq = 0x%x\n", freq16);
+	}
+	return ((freq16 << 9) + (gus->gf1.playback_freq >> 1)) / gus->gf1.playback_freq;
+}
+
+short snd_gf1_compute_vibrato(short cents, unsigned short fc_register)
+{
+	static short vibrato_table[] =
+	{
+		0, 0, 32, 592, 61, 1175, 93, 1808,
+		124, 2433, 152, 3007, 182, 3632, 213, 4290,
+		241, 4834, 255, 5200
+	};
+
+	long depth;
+	short *vi1, *vi2, pcents, v1;
+
+	pcents = cents < 0 ? -cents : cents;
+	for (vi1 = vibrato_table, vi2 = vi1 + 2; pcents > *vi2; vi1 = vi2, vi2 += 2);
+	v1 = *(vi1 + 1);
+	/* The FC table above is a list of pairs. The first number in the pair     */
+	/* is the cents index from 0-255 cents, and the second number in the       */
+	/* pair is the FC adjustment needed to change the pitch by the indexed     */
+	/* number of cents. The table was created for an FC of 32768.              */
+	/* The following expression does a linear interpolation against the        */
+	/* approximated log curve in the table above, and then scales the number   */
+	/* by the FC before the LFO. This calculation also adjusts the output      */
+	/* value to produce the appropriate depth for the hardware. The depth      */
+	/* is 2 * desired FC + 1.                                                  */
+	depth = (((int) (*(vi2 + 1) - *vi1) * (pcents - *vi1) / (*vi2 - *vi1)) + v1) * fc_register >> 14;
+	if (depth)
+		depth++;
+	if (depth > 255)
+		depth = 255;
+	return cents < 0 ? -(short) depth : (short) depth;
+}
+
+unsigned short snd_gf1_compute_pitchbend(unsigned short pitchbend, unsigned short sens)
+{
+	static long log_table[] = {1024, 1085, 1149, 1218, 1290, 1367, 1448, 1534, 1625, 1722, 1825, 1933};
+	int wheel, sensitivity;
+	unsigned int mantissa, f1, f2;
+	unsigned short semitones, f1_index, f2_index, f1_power, f2_power;
+	char bend_down = 0;
+	int bend;
+
+	if (!sens)
+		return 1024;
+	wheel = (int) pitchbend - 8192;
+	sensitivity = ((int) sens * wheel) / 128;
+	if (sensitivity < 0) {
+		bend_down = 1;
+		sensitivity = -sensitivity;
+	}
+	semitones = (unsigned int) (sensitivity >> 13);
+	mantissa = sensitivity % 8192;
+	f1_index = semitones % 12;
+	f2_index = (semitones + 1) % 12;
+	f1_power = semitones / 12;
+	f2_power = (semitones + 1) / 12;
+	f1 = log_table[f1_index] << f1_power;
+	f2 = log_table[f2_index] << f2_power;
+	bend = (int) ((((f2 - f1) * mantissa) >> 13) + f1);
+	if (bend_down)
+		bend = 1048576L / bend;
+	return bend;
+}
+
+unsigned short snd_gf1_compute_freq(unsigned int freq,
+				    unsigned int rate,
+				    unsigned short mix_rate)
+{
+	unsigned int fc;
+	int scale = 0;
+
+	while (freq >= 4194304L) {
+		scale++;
+		freq >>= 1;
+	}
+	fc = (freq << 10) / rate;
+	if (fc > 97391L) {
+		fc = 97391;
+		snd_printk("patch: (1) fc frequency overflow - %u\n", fc);
+	}
+	fc = (fc * 44100UL) / mix_rate;
+	while (scale--)
+		fc <<= 1;
+	if (fc > 65535L) {
+		fc = 65535;
+		snd_printk("patch: (2) fc frequency overflow - %u\n", fc);
+	}
+	return (unsigned short) fc;
+}
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/gusclassic.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/gusclassic.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/gusclassic.c	(revision 91)
@@ -0,0 +1,302 @@
+/*
+ *  Driver for Gravis UltraSound Classic soundcard
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#define SNDRV_MAIN_OBJECT_FILE
+#include <sound/driver.h>
+#include <sound/gus.h>
+#define SNDRV_LEGACY_AUTO_PROBE
+#define SNDRV_LEGACY_FIND_FREE_IRQ
+#define SNDRV_LEGACY_FIND_FREE_DMA
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+EXPORT_NO_SYMBOLS;
+MODULE_DESCRIPTION("Gravis UltraSound Classic");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{Gravis,UltraSound Classic}}");
+
+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;	/* Enable this card */
+static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* 0x220,0x230,0x240,0x250,0x260 */
+static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* 3,5,9,11,12,15 */
+static int snd_dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* 1,3,5,6,7 */
+static int snd_dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* 1,3,5,6,7 */
+#ifdef TARGET_OS2
+static int snd_joystick_dac[SNDRV_CARDS] = {REPEAT_SNDRV(29)};
+				/* 0 to 31, (0.59V-4.52V or 0.389V-2.98V) */
+static int snd_channels[SNDRV_CARDS] = {REPEAT_SNDRV(24)};
+static int snd_pcm_channels[SNDRV_CARDS] = {REPEAT_SNDRV(2)};
+#else
+static int snd_joystick_dac[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 29};
+				/* 0 to 31, (0.59V-4.52V or 0.389V-2.98V) */
+static int snd_channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 24};
+static int snd_pcm_channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2};
+#endif
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for GUS Classic soundcard.");
+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 GUS Classic soundcard.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable GUS Classic soundcard.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_port, "Port # for GUS Classic driver.");
+MODULE_PARM_SYNTAX(snd_port, SNDRV_ENABLED ",allows:{{0x220,0x260,0x10}},dialog:list");
+MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_irq, "IRQ # for GUS Classic driver.");
+MODULE_PARM_SYNTAX(snd_irq, SNDRV_ENABLED ",allows:{{3},{5},{9},{11},{12},{15}},dialog:list");
+MODULE_PARM(snd_dma1, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_dma1, "DMA1 # for GUS Classic driver.");
+MODULE_PARM_SYNTAX(snd_dma1, SNDRV_ENABLED ",allows:{{1},{3},{5},{6},{7}},dialog:list");
+MODULE_PARM(snd_dma2, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_dma2, "DMA2 # for GUS Classic driver.");
+MODULE_PARM_SYNTAX(snd_dma2, SNDRV_ENABLED ",allows:{{1},{3},{5},{6},{7}},dialog:list");
+MODULE_PARM(snd_joystick_dac, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_joystick_dac, "Joystick DAC level 0.59V-4.52V or 0.389V-2.98V for GUS Classic driver.");
+MODULE_PARM_SYNTAX(snd_joystick_dac, SNDRV_ENABLED ",allows:{{0,31}}");
+MODULE_PARM(snd_channels, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_channels, "GF1 channels for GUS Classic driver.");
+MODULE_PARM_SYNTAX(snd_channels,  SNDRV_ENABLED ",allows:{{14,32}}");
+MODULE_PARM(snd_pcm_channels, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_pcm_channels, "Reserved PCM channels for GUS Classic driver.");
+MODULE_PARM_SYNTAX(snd_pcm_channels, SNDRV_ENABLED ",allows:{{2,16}}");
+
+static snd_card_t *snd_gusclassic_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+
+
+static int __init snd_gusclassic_detect(snd_gus_card_t * gus)
+{
+	snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 0);	/* reset GF1 */
+#ifdef CONFIG_SND_DEBUG_DETECT
+	{
+		unsigned char d;
+
+		if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 0) {
+			snd_printk("[0x%lx] check 1 failed - 0x%x\n", gus->gf1.port, d);
+			return -ENODEV;
+		}
+	}
+#else
+	if ((snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET) & 0x07) != 0)
+		return -ENODEV;
+#endif
+	udelay(160);
+	snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1);	/* release reset */
+	udelay(160);
+#ifdef CONFIG_SND_DEBUG_DETECT
+	{
+		unsigned char d;
+
+		if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 1) {
+			snd_printk("[0x%lx] check 2 failed - 0x%x\n", gus->gf1.port, d);
+			return -ENODEV;
+		}
+	}
+#else
+	if ((snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET) & 0x07) != 1)
+		return -ENODEV;
+#endif
+
+	return 0;
+}
+
+static void __init snd_gusclassic_init(int dev, snd_gus_card_t * gus)
+{
+	gus->equal_irq = 0;
+	gus->codec_flag = 0;
+	gus->max_flag = 0;
+	gus->joystick_dac = snd_joystick_dac[dev];
+}
+
+static int __init snd_gusclassic_probe(int dev)
+{
+	static int possible_irqs[] = {5, 11, 12, 9, 7, 15, 3, 4, -1};
+	static int possible_dmas[] = {5, 6, 7, 1, 3, -1};
+	int irq, dma1, dma2;
+	snd_card_t *card;
+	struct snd_gusclassic *guscard;
+	snd_gus_card_t *gus = NULL;
+	int err;
+
+	card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0);
+	if (card == NULL)
+		return -ENOMEM;
+	guscard = (struct snd_gusclassic *)card->private_data;
+	if (snd_pcm_channels[dev] < 2)
+		snd_pcm_channels[dev] = 2;
+
+	irq = snd_irq[dev];
+	if (irq == SNDRV_AUTO_IRQ) {
+		if ((irq = snd_legacy_find_free_irq(possible_irqs)) < 0) {
+			snd_card_free(card);
+			snd_printk("unable to find a free IRQ\n");
+			return -EBUSY;
+		}
+	}
+	dma1 = snd_dma1[dev];
+	if (dma1 == SNDRV_AUTO_DMA) {
+		if ((dma1 = snd_legacy_find_free_dma(possible_dmas)) < 0) {
+			snd_card_free(card);
+			snd_printk("unable to find a free DMA1\n");
+			return -EBUSY;
+		}
+	}
+	dma2 = snd_dma2[dev];
+	if (dma2 == SNDRV_AUTO_DMA) {
+		if ((dma2 = snd_legacy_find_free_dma(possible_dmas)) < 0) {
+			snd_card_free(card);
+			snd_printk("unable to find a free DMA2\n");
+			return -EBUSY;
+		}
+	}
+
+
+	if ((err = snd_gus_create(card,
+				  snd_port[dev],
+				  irq, dma1, dma2,
+			          0, snd_channels[dev], snd_pcm_channels[dev],
+			          0, &gus)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_gusclassic_detect(gus)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	snd_gusclassic_init(dev, gus);
+	if ((err = snd_gus_initialize(gus)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if (gus->max_flag || gus->ess_flag) {
+		snd_card_free(card);
+		snd_printdd("GUS Classic or ACE soundcard was not detected at 0x%lx\n", gus->gf1.port);
+		return -ENODEV;
+	}
+	if ((err = snd_gf1_new_mixer(gus)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_gf1_pcm_new(gus, 0, 0, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if (!gus->ace_flag) {
+		if ((err = snd_gf1_rawmidi_new(gus, 0, NULL)) < 0) {
+			snd_card_free(card);
+			return err;
+		}
+	}
+	sprintf(card->longname + strlen(card->longname), " at 0x%lx, irq %d, dma %d", gus->gf1.port, irq, dma1);
+	if (dma2 >= 0)
+		sprintf(card->longname + strlen(card->longname), "&%d", dma2);
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	snd_gusclassic_cards[dev] = card;
+	return 0;
+}
+
+static int __init snd_gusclassic_legacy_auto_probe(unsigned long port)
+{
+	static int dev = 0;
+	int res;
+
+	for ( ; dev < SNDRV_CARDS; dev++) {
+		if (!snd_enable[dev] || snd_port[dev] != SNDRV_AUTO_PORT)
+			continue;
+		snd_port[dev] = port;
+		res = snd_gusclassic_probe(dev);
+		if (res < 0)
+			snd_port[dev] = SNDRV_AUTO_PORT;
+		return res;
+	}
+	return -ENODEV;
+}
+
+static int __init alsa_card_gusclassic_init(void)
+{
+	static unsigned long possible_ports[] = {0x220, 0x230, 0x240, 0x250, 0x260, -1};
+	int dev, cards;
+
+	for (dev = cards = 0; dev < SNDRV_CARDS && snd_enable[dev]; dev++) {
+		if (snd_port[dev] == SNDRV_AUTO_PORT)
+			continue;
+		if (snd_gusclassic_probe(dev) >= 0)
+			cards++;
+	}
+	cards += snd_legacy_auto_probe(possible_ports, snd_gusclassic_legacy_auto_probe);
+	if (!cards) {
+#ifdef MODULE
+		snd_printk("GUS Classic soundcard not found or device busy\n");
+#endif
+		return -ENODEV;
+	}
+	return 0;
+}
+
+static void __exit alsa_card_gusclassic_exit(void)
+{
+	int idx;
+	
+	for (idx = 0; idx < SNDRV_CARDS; idx++)
+		snd_card_free(snd_gusclassic_cards[idx]);
+}
+
+module_init(alsa_card_gusclassic_init)
+module_exit(alsa_card_gusclassic_exit)
+
+#ifndef MODULE
+
+/* format is: snd-card-gusclassic=snd_enable,snd_index,snd_id,
+				  snd_port,snd_irq,
+				  snd_dma1,snd_dma2,
+				  snd_joystick_dac,
+				  snd_channels,snd_pcm_channels */
+
+static int __init alsa_card_gusclassic_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_port[nr_dev]) == 2 &&
+	       get_option(&str,&snd_irq[nr_dev]) == 2 &&
+	       get_option(&str,&snd_dma1[nr_dev]) == 2 &&
+	       get_option(&str,&snd_dma2[nr_dev]) == 2 &&
+	       get_option(&str,&snd_joystick_dac[nr_dev]) == 2 &&
+	       get_option(&str,&snd_channels[nr_dev]) == 2 &&
+	       get_option(&str,&snd_pcm_channels[nr_dev]) == 2);
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-card-gusclassic=", alsa_card_gusclassic_setup);
+
+#endif /* ifndef MODULE */
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/gusextreme.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/gusextreme.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/gusextreme.c	(revision 91)
@@ -0,0 +1,441 @@
+/*
+ *  Driver for Gravis UltraSound Extreme soundcards
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#define SNDRV_MAIN_OBJECT_FILE
+#include <sound/driver.h>
+#include <sound/gus.h>
+#include <sound/es1688.h>
+#include <sound/mpu401.h>
+#include <sound/opl3.h>
+#define SNDRV_LEGACY_AUTO_PROBE
+#define SNDRV_LEGACY_FIND_FREE_IRQ
+#define SNDRV_LEGACY_FIND_FREE_DMA
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+EXPORT_NO_SYMBOLS;
+MODULE_DESCRIPTION("Gravis UltraSound Extreme");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{Gravis,UltraSound Extreme}}");
+
+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;	/* Enable this card */
+static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* 0x220,0x240,0x260 */
+#ifdef TARGET_OS2
+static long snd_gf1_port[SNDRV_CARDS] = {-1,-1,-1,-1,-1,-1,-1,-1}; /* 0x210,0x220,0x230,0x240,0x250,0x260,0x270 */
+static long snd_mpu_port[SNDRV_CARDS] = {-1,-1,-1,-1,-1,-1,-1,-1}; /* 0x300,0x310,0x320 */
+#else
+static long snd_gf1_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS) - 1] = -1}; /* 0x210,0x220,0x230,0x240,0x250,0x260,0x270 */
+static long snd_mpu_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS) - 1] = -1}; /* 0x300,0x310,0x320 */
+#endif
+static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* 5,7,9,10 */
+static int snd_mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* 5,7,9,10 */
+static int snd_gf1_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* 2,3,5,9,11,12,15 */
+static int snd_dma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* 0,1,3 */
+static int snd_dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;
+#ifdef TARGET_OS2
+static int snd_joystick_dac[SNDRV_CARDS] = {REPEAT_SNDRV(29)};
+				/* 0 to 31, (0.59V-4.52V or 0.389V-2.98V) */
+static int snd_channels[SNDRV_CARDS] = {REPEAT_SNDRV(24)};
+static int snd_pcm_channels[SNDRV_CARDS] = {REPEAT_SNDRV(2)};
+#else
+static int snd_joystick_dac[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 29};
+				/* 0 to 31, (0.59V-4.52V or 0.389V-2.98V) */
+static int snd_channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 24};
+static int snd_pcm_channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2};
+#endif
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for GUS Extreme soundcard.");
+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 GUS Extreme soundcard.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable GUS Extreme soundcard.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_port, "Port # for GUS Extreme driver.");
+MODULE_PARM_SYNTAX(snd_port, SNDRV_ENABLED ",allows:{{0x220,0x260,0x20}},dialog:list");
+MODULE_PARM(snd_gf1_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_gf1_port, "GF1 port # for GUS Extreme driver (optional).");
+MODULE_PARM_SYNTAX(snd_gf1_port, SNDRV_ENABLED ",allows:{{0x210,0x270,0x10}},dialog:list");
+MODULE_PARM(snd_mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_mpu_port, "MPU-401 port # for GUS Extreme driver.");
+MODULE_PARM_SYNTAX(snd_mpu_port, SNDRV_ENABLED ",allows:{{0x300,0x320,0x10}},dialog:list");
+MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_irq, "IRQ # for GUS Extreme driver.");
+MODULE_PARM_SYNTAX(snd_irq, SNDRV_ENABLED ",allows:{{5},{7},{9},{10}},dialog:list");
+MODULE_PARM(snd_mpu_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_mpu_irq, "MPU-401 IRQ # for GUS Extreme driver.");
+MODULE_PARM_SYNTAX(snd_mpu_irq, SNDRV_ENABLED ",allows:{{5},{7},{9},{10}},dialog:list");
+MODULE_PARM(snd_gf1_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_gf1_irq, "GF1 IRQ # for GUS Extreme driver.");
+MODULE_PARM_SYNTAX(snd_gf1_irq, SNDRV_ENABLED ",allows:{{2},{3},{5},{9},{11},{12},{15}},dialog:list");
+MODULE_PARM(snd_dma8, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_dma8, "8-bit DMA # for GUS Extreme driver.");
+MODULE_PARM_SYNTAX(snd_dma8, SNDRV_DMA8_DESC);
+MODULE_PARM(snd_dma1, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_dma1, "GF1 DMA # for GUS Extreme driver.");
+MODULE_PARM_SYNTAX(snd_dma1, SNDRV_DMA_DESC);
+MODULE_PARM(snd_joystick_dac, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_joystick_dac, "Joystick DAC level 0.59V-4.52V or 0.389V-2.98V for GUS Extreme driver.");
+MODULE_PARM_SYNTAX(snd_joystick_dac, SNDRV_ENABLED ",allows:{{0,31}}");
+MODULE_PARM(snd_channels, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_channels, "GF1 channels for GUS Extreme driver.");
+MODULE_PARM_SYNTAX(snd_channels, SNDRV_ENABLED ",allows:{{14,32}}");
+MODULE_PARM(snd_pcm_channels, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_pcm_channels, "Reserved PCM channels for GUS Extreme driver.");
+MODULE_PARM_SYNTAX(snd_pcm_channels, SNDRV_ENABLED ",allows:{{2,16}}");
+
+static snd_card_t *snd_gusextreme_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+
+
+static int __init snd_gusextreme_detect(int dev,
+					snd_card_t * card,
+					snd_gus_card_t * gus,
+					es1688_t *es1688)
+{
+	unsigned long flags;
+
+	/*
+	 * This is main stuff - enable access to GF1 chip...
+	 * I'm not sure, if this will work for card which have
+	 * ES1688 chip in another place than 0x220.
+         *
+         * I used reverse-engineering in DOSEMU. [--jk]
+	 *
+	 * ULTRINIT.EXE:
+	 * 0x230 = 0,2,3
+	 * 0x240 = 2,0,1
+	 * 0x250 = 2,0,3
+	 * 0x260 = 2,2,1
+	 */
+
+	spin_lock_irqsave(&es1688->mixer_lock, flags);
+	snd_es1688_mixer_write(es1688, 0x40, 0x0b);	/* don't change!!! */
+	spin_unlock_irqrestore(&es1688->mixer_lock, flags);
+	spin_lock_irqsave(&es1688->reg_lock, flags);
+	outb(snd_gf1_port[dev] & 0x040 ? 2 : 0, ES1688P(es1688, INIT1));
+	outb(0, 0x201);
+	outb(snd_gf1_port[dev] & 0x020 ? 2 : 0, ES1688P(es1688, INIT1));
+	outb(0, 0x201);
+	outb(snd_gf1_port[dev] & 0x010 ? 3 : 1, ES1688P(es1688, INIT1));
+	spin_unlock_irqrestore(&es1688->reg_lock, flags);
+
+	udelay(100);
+
+	snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 0);	/* reset GF1 */
+#ifdef CONFIG_SND_DEBUG_DETECT
+	{
+		unsigned char d;
+
+		if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 0) {
+			snd_printk("[0x%lx] check 1 failed - 0x%x\n", gus->gf1.port, d);
+			return -EIO;
+		}
+	}
+#else
+	if ((snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET) & 0x07) != 0)
+		return -EIO;
+#endif
+	udelay(160);
+	snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1);	/* release reset */
+	udelay(160);
+#ifdef CONFIG_SND_DEBUG_DETECT
+	{
+		unsigned char d;
+
+		if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 1) {
+			snd_printk("[0x%lx] check 2 failed - 0x%x\n", gus->gf1.port, d);
+			return -EIO;
+		}
+	}
+#else
+	if ((snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET) & 0x07) != 1)
+		return -EIO;
+#endif
+
+	return 0;
+}
+
+static void __init snd_gusextreme_init(int dev, snd_gus_card_t * gus)
+{
+	gus->joystick_dac = snd_joystick_dac[dev];
+}
+
+static int __init snd_gusextreme_mixer(es1688_t *chip)
+{
+	snd_card_t *card = chip->card;
+	snd_ctl_elem_id_t id1, id2;
+	int err;
+
+	memset(&id1, 0, sizeof(id1));
+	memset(&id2, 0, sizeof(id2));
+	id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+	/* reassign AUX to SYNTHESIZER */
+	strcpy(id1.name, "Aux Playback Volume");
+	strcpy(id2.name, "Synth Playback Volume");
+	if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
+		return err;
+	/* reassign Master Playback Switch to Synth Playback Switch */
+	strcpy(id1.name, "Master Playback Switch");
+	strcpy(id2.name, "Synth Playback Switch");
+	if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
+		return err;
+	return 0;
+}
+
+static int __init snd_gusextreme_probe(int dev)
+{
+	static int possible_ess_irqs[] = {5, 9, 10, 7, -1};
+	static int possible_ess_dmas[] = {1, 3, 0, -1};
+	static int possible_gf1_irqs[] = {5, 11, 12, 9, 7, 15, 3, -1};
+	static int possible_gf1_dmas[] = {5, 6, 7, 1, 3, -1};
+	int gf1_irq, gf1_dma, ess_irq, mpu_irq, ess_dma;
+	snd_card_t *card;
+	struct snd_gusextreme *acard;
+	snd_gus_card_t *gus;
+	es1688_t *es1688;
+	opl3_t *opl3;
+	int err;
+
+	card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0);
+	if (card == NULL)
+		return -ENOMEM;
+	acard = (struct snd_gusextreme *)card->private_data;
+
+	gf1_irq = snd_gf1_irq[dev];
+	if (gf1_irq == SNDRV_AUTO_IRQ) {
+		if ((gf1_irq = snd_legacy_find_free_irq(possible_gf1_irqs)) < 0) {
+			snd_card_free(card);
+			snd_printk("unable to find a free IRQ for GF1\n");
+			return -EBUSY;
+		}
+	}
+	ess_irq = snd_irq[dev];
+	if (ess_irq == SNDRV_AUTO_IRQ) {
+		if ((ess_irq = snd_legacy_find_free_irq(possible_ess_irqs)) < 0) {
+			snd_card_free(card);
+			snd_printk("unable to find a free IRQ for ES1688\n");
+			return -EBUSY;
+		}
+	}
+	if (snd_mpu_port[dev] == SNDRV_AUTO_PORT)
+		snd_mpu_port[dev] = 0;
+	mpu_irq = snd_mpu_irq[dev];
+	if (mpu_irq > 15)
+		mpu_irq = -1;
+	gf1_dma = snd_dma1[dev];
+	if (gf1_dma == SNDRV_AUTO_DMA) {
+		if ((gf1_dma = snd_legacy_find_free_dma(possible_gf1_dmas)) < 0) {
+			snd_card_free(card);
+			snd_printk("unable to find a free DMA for GF1\n");
+			return -EBUSY;
+		}
+	}
+	ess_dma = snd_dma8[dev];
+	if (ess_dma == SNDRV_AUTO_DMA) {
+		if ((ess_dma = snd_legacy_find_free_dma(possible_ess_dmas)) < 0) {
+			snd_card_free(card);
+			snd_printk("unable to find a free DMA for ES1688\n");
+			return -EBUSY;
+		}
+	}
+
+	if ((err = snd_es1688_create(card, snd_port[dev], snd_mpu_port[dev],
+				     ess_irq, mpu_irq, ess_dma,
+				     ES1688_HW_1688, &es1688)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if (snd_gf1_port[dev] < 0)
+		snd_gf1_port[dev] = snd_port[dev] + 0x20;
+	if ((err = snd_gus_create(card,
+				  snd_gf1_port[dev],
+				  gf1_irq,
+				  gf1_dma,
+				  -1,
+				  0, snd_channels[dev],
+				  snd_pcm_channels[dev], 0,
+				  &gus)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_gusextreme_detect(dev, card, gus, es1688)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	snd_gusextreme_init(dev, gus);
+	if ((err = snd_gus_initialize(gus)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if (!gus->ess_flag) {
+		snd_card_free(card);
+		snd_printdd("GUS Extreme soundcard was not detected at 0x%lx\n", gus->gf1.port);
+		return -ENODEV;
+	}
+	if ((err = snd_es1688_pcm(es1688, 0, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_es1688_mixer(es1688)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	snd_component_add(card, "ES1688");
+	if (snd_pcm_channels[dev] > 0) {
+		if ((err = snd_gf1_pcm_new(gus, 1, 1, NULL)) < 0) {
+			snd_card_free(card);
+			return err;
+		}
+	}
+	if ((err = snd_gf1_new_mixer(gus)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_gusextreme_mixer(es1688)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	if (snd_opl3_create(card, es1688->port, es1688->port + 2,
+			    OPL3_HW_OPL3, 0, &opl3) < 0) {
+		snd_printk("opl3 not detected at 0x%lx\n", es1688->port);
+	} else {
+		if ((err = snd_opl3_hwdep_new(opl3, 0, 2, NULL)) < 0) {
+			snd_card_free(card);
+			return err;
+		}
+	}
+
+	if (es1688->mpu_port >= 0x300) {
+		if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_ES1688,
+					       es1688->mpu_port, 0,
+					       mpu_irq,
+					       SA_INTERRUPT,
+					       NULL)) < 0) {
+			snd_card_free(card);
+			return err;
+		}
+	}
+
+	sprintf(card->longname, "Gravis UltraSound Extreme at 0x%lx, irq %i&%i, dma %i&%i",
+		es1688->port, gf1_irq, ess_irq, gf1_dma, ess_dma);
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	snd_gusextreme_cards[dev] = card;
+	return 0;
+}
+
+static int __init snd_gusextreme_legacy_auto_probe(unsigned long port)
+{
+        static int dev = 0;
+        int res;
+
+        for ( ; dev < SNDRV_CARDS; dev++) {
+                if (!snd_enable[dev] || snd_port[dev] != SNDRV_AUTO_PORT)
+                        continue;
+                snd_port[dev] = port;
+                res = snd_gusextreme_probe(dev);
+                if (res < 0)
+                        snd_port[dev] = SNDRV_AUTO_PORT;
+                return res;
+        }
+        return -ENODEV;
+}
+
+static int __init alsa_card_gusextreme_init(void)
+{
+	static unsigned long possible_ports[] = {0x220, 0x240, 0x260, -1};
+	int dev, cards;
+
+	for (dev = cards = 0; dev < SNDRV_CARDS && snd_enable[dev] > 0; dev++) {
+		if (snd_port[dev] == SNDRV_AUTO_PORT)
+			continue;
+		if (snd_gusextreme_probe(dev) >= 0)
+			cards++;
+	}
+	cards += snd_legacy_auto_probe(possible_ports, snd_gusextreme_legacy_auto_probe);
+	if (!cards) {
+#ifdef MODULE
+		snd_printk("GUS Extreme soundcard not found or device busy\n");
+#endif
+		return -ENODEV;
+	}
+	return 0;
+}
+
+static void __exit alsa_card_gusextreme_exit(void)
+{
+	int idx;
+	snd_card_t *card;
+	struct snd_gusextreme *acard;
+
+	for (idx = 0; idx < SNDRV_CARDS; idx++) {
+		card = snd_gusextreme_cards[idx];
+		if (card == NULL)
+			continue;
+		acard = (struct snd_gusextreme *)card->private_data;
+		snd_card_free(snd_gusextreme_cards[idx]);
+	}
+}
+
+module_init(alsa_card_gusextreme_init)
+module_exit(alsa_card_gusextreme_exit)
+
+#ifndef MODULE
+
+/* format is: snd-card-gusextreme=snd_enable,snd_index,snd_id,
+				  snd_port,snd_gf1_port,snd_mpu_port,
+				  snd_irq,snd_gf1_irq,snd_mpu_irq,
+				  snd_dma8,snd_dma1,
+				  snd_joystick_dac,
+				  snd_channels,snd_pcm_channels */
+
+static int __init alsa_card_gusextreme_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_port[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_gf1_port[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_mpu_port[nr_dev]) == 2 &&
+	       get_option(&str,&snd_irq[nr_dev]) == 2 &&
+	       get_option(&str,&snd_gf1_irq[nr_dev]) == 2 &&
+	       get_option(&str,&snd_mpu_irq[nr_dev]) == 2 &&
+	       get_option(&str,&snd_dma8[nr_dev]) == 2 &&
+	       get_option(&str,&snd_dma1[nr_dev]) == 2);
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-card-gusextreme=", alsa_card_gusextreme_setup);
+
+#endif /* ifndef MODULE */
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/gusmax.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/gusmax.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/gusmax.c	(revision 91)
@@ -0,0 +1,438 @@
+/*
+ *  Driver for Gravis UltraSound MAX soundcard
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#define SNDRV_MAIN_OBJECT_FILE
+#include <sound/driver.h>
+#include <sound/gus.h>
+#include <sound/cs4231.h>
+#define SNDRV_LEGACY_AUTO_PROBE
+#define SNDRV_LEGACY_FIND_FREE_IRQ
+#define SNDRV_LEGACY_FIND_FREE_DMA
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+EXPORT_NO_SYMBOLS;
+MODULE_DESCRIPTION("Gravis UltraSound MAX");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{Gravis,UltraSound MAX}}");
+
+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;	/* Enable this card */
+static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* 0x220,0x230,0x240,0x250,0x260 */
+static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* 2,3,5,9,11,12,15 */
+static int snd_dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* 1,3,5,6,7 */
+static int snd_dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* 1,3,5,6,7 */
+#ifdef TARGET_OS2
+static int snd_joystick_dac[SNDRV_CARDS] = {REPEAT_SNDRV(29)};
+				/* 0 to 31, (0.59V-4.52V or 0.389V-2.98V) */
+static int snd_channels[SNDRV_CARDS] = {REPEAT_SNDRV(24)};
+static int snd_pcm_channels[SNDRV_CARDS] = {REPEAT_SNDRV(2)};
+#else
+static int snd_joystick_dac[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 29};
+				/* 0 to 31, (0.59V-4.52V or 0.389V-2.98V) */
+static int snd_channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 24};
+static int snd_pcm_channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2};
+#endif
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for GUS MAX soundcard.");
+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 GUS MAX soundcard.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable GUS MAX soundcard.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_port, "Port # for GUS MAX driver.");
+MODULE_PARM_SYNTAX(snd_port, SNDRV_ENABLED ",allows:{{0x220},{0x230},{0x240},{0x250},{0x260}},dialog:list");
+MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_irq, "IRQ # for GUS MAX driver.");
+MODULE_PARM_SYNTAX(snd_irq, SNDRV_ENABLED ",allows:{{3},{5},{9},{11},{12},{15}},dialog:list");
+MODULE_PARM(snd_dma1, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_dma1, "DMA1 # for GUS MAX driver.");
+MODULE_PARM_SYNTAX(snd_dma1, SNDRV_DMA_DESC);
+MODULE_PARM(snd_dma2, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_dma2, "DMA2 # for GUS MAX driver.");
+MODULE_PARM_SYNTAX(snd_dma2, SNDRV_DMA_DESC);
+MODULE_PARM(snd_joystick_dac, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_joystick_dac, "Joystick DAC level 0.59V-4.52V or 0.389V-2.98V for GUS MAX driver.");
+MODULE_PARM_SYNTAX(snd_joystick_dac, SNDRV_ENABLED ",allows:{{0,31}}");
+MODULE_PARM(snd_channels, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_channels, "Used GF1 channels for GUS MAX driver.");
+MODULE_PARM_SYNTAX(snd_channels, SNDRV_ENABLED ",allows:{{14,32}}");
+MODULE_PARM(snd_pcm_channels, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_pcm_channels, "Reserved PCM channels for GUS MAX driver.");
+MODULE_PARM_SYNTAX(snd_pcm_channels, SNDRV_ENABLED ",allows:{{2,16}}");
+
+struct snd_gusmax {
+	int irq;
+	snd_card_t *card;
+	snd_gus_card_t *gus;
+	cs4231_t *cs4231;
+	unsigned short gus_status_reg;
+	unsigned short pcm_status_reg;
+};
+
+static snd_card_t *snd_gusmax_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+
+
+static int __init snd_gusmax_detect(snd_gus_card_t * gus)
+{
+	snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 0);	/* reset GF1 */
+#ifdef CONFIG_SND_DEBUG_DETECT
+	{
+		unsigned char d;
+
+		if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 0) {
+			snd_printk("[0x%lx] check 1 failed - 0x%x\n", gus->gf1.port, d);
+			return -ENODEV;
+		}
+	}
+#else
+	if ((snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET) & 0x07) != 0)
+		return -ENODEV;
+#endif
+	udelay(160);
+	snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1);	/* release reset */
+	udelay(160);
+#ifdef CONFIG_SND_DEBUG_DETECT
+	{
+		unsigned char d;
+
+		if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 1) {
+			snd_printk("[0x%lx] check 2 failed - 0x%x\n", gus->gf1.port, d);
+			return -ENODEV;
+		}
+	}
+#else
+	if ((snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET) & 0x07) != 1)
+		return -ENODEV;
+#endif
+	return 0;
+}
+
+static void snd_gusmax_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct snd_gusmax *maxcard = (struct snd_gusmax *) dev_id;
+	int loop, max = 5;
+
+	do {
+		loop = 0;
+		if (inb(maxcard->gus_status_reg)) {
+			snd_gus_interrupt(irq, maxcard->gus, regs);
+			loop++;
+		}
+		if (inb(maxcard->pcm_status_reg) & 0x01) { /* IRQ bit is set? */
+			snd_cs4231_interrupt(irq, maxcard->cs4231, regs);
+			loop++;
+		}
+	} while (loop && --max > 0);
+}
+
+static void __init snd_gusmax_init(int dev, snd_card_t * card, snd_gus_card_t * gus)
+{
+	gus->equal_irq = 1;
+	gus->codec_flag = 1;
+	gus->joystick_dac = snd_joystick_dac[dev];
+	/* init control register */
+	gus->max_cntrl_val = (gus->gf1.port >> 4) & 0x0f;
+	if (gus->gf1.dma1 > 3)
+		gus->max_cntrl_val |= 0x10;
+	if (gus->gf1.dma2 > 3)
+		gus->max_cntrl_val |= 0x20;
+	gus->max_cntrl_val |= 0x40;
+	outb(gus->max_cntrl_val, GUSP(gus, MAXCNTRLPORT));
+}
+
+#define CS4231_PRIVATE( left, right, shift, mute ) \
+			((left << 24)|(right << 16)|(shift<<8)|mute)
+
+static int __init snd_gusmax_mixer(cs4231_t *chip)
+{
+	snd_card_t *card = chip->card;
+	snd_ctl_elem_id_t id1, id2;
+	int err;
+	
+	memset(&id1, 0, sizeof(id1));
+	memset(&id2, 0, sizeof(id2));
+	id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+	/* reassign AUXA to SYNTHESIZER */
+	strcpy(id1.name, "Aux Playback Switch");
+	strcpy(id2.name, "Synth Playback Switch");
+	if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
+		return err;
+	strcpy(id1.name, "Aux Playback Volume");
+	strcpy(id2.name, "Synth Playback Volume");
+	if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
+		return err;
+	/* reassign AUXB to CD */
+	strcpy(id1.name, "Aux Playback Switch"); id1.index = 1;
+	strcpy(id2.name, "CD Playback Switch");
+	if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
+		return err;
+	strcpy(id1.name, "Aux Playback Volume");
+	strcpy(id2.name, "CD Playback Volume");
+	if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
+		return err;
+#if 0
+	/* reassign Mono Input to MIC */
+	if (snd_mixer_group_rename(mixer,
+				SNDRV_MIXER_IN_MONO, 0,
+				SNDRV_MIXER_IN_MIC, 0) < 0)
+		goto __error;
+	if (snd_mixer_elem_rename(mixer,
+				SNDRV_MIXER_IN_MONO, 0, SNDRV_MIXER_ETYPE_INPUT,
+				SNDRV_MIXER_IN_MIC, 0) < 0)
+		goto __error;
+	if (snd_mixer_elem_rename(mixer,
+				"Mono Capture Volume", 0, SNDRV_MIXER_ETYPE_VOLUME1,
+				"Mic Capture Volume", 0) < 0)
+		goto __error;
+	if (snd_mixer_elem_rename(mixer,
+				"Mono Capture Switch", 0, SNDRV_MIXER_ETYPE_SWITCH1,
+				"Mic Capture Switch", 0) < 0)
+		goto __error;
+#endif
+	return 0;
+}
+
+static void snd_gusmax_free(snd_card_t *card)
+{
+	struct snd_gusmax *maxcard = (struct snd_gusmax *)card->private_data;
+	
+	if (maxcard == NULL)
+		return;
+	if (maxcard->irq >= 0)
+		free_irq(maxcard->irq, (void *)maxcard);
+}
+
+static int __init snd_gusmax_probe(int dev)
+{
+	static int possible_irqs[] = {5, 11, 12, 9, 7, 15, 3, -1};
+	static int possible_dmas[] = {5, 6, 7, 1, 3, -1};
+	int irq, dma1, dma2, err;
+	snd_card_t *card;
+	snd_gus_card_t *gus = NULL;
+	cs4231_t *cs4231;
+	struct snd_gusmax *maxcard;
+
+	card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE,
+			    sizeof(struct snd_gusmax));
+	if (card == NULL)
+		return -ENOMEM;
+	card->private_free = snd_gusmax_free;
+	maxcard = (struct snd_gusmax *)card->private_data;
+	maxcard->card = card;
+	maxcard->irq = -1;
+	
+	irq = snd_irq[dev];
+	if (irq == SNDRV_AUTO_IRQ) {
+		if ((irq = snd_legacy_find_free_irq(possible_irqs)) < 0) {
+			snd_card_free(card);
+			snd_printk("unable to find a free IRQ\n");
+			return -EBUSY;
+		}
+	}
+	dma1 = snd_dma1[dev];
+	if (dma1 == SNDRV_AUTO_DMA) {
+		if ((dma1 = snd_legacy_find_free_dma(possible_dmas)) < 0) {
+			snd_card_free(card);
+			snd_printk("unable to find a free DMA1\n");
+			return -EBUSY;
+		}
+	}
+	dma2 = snd_dma2[dev];
+	if (dma2 == SNDRV_AUTO_DMA) {
+		if ((dma2 = snd_legacy_find_free_dma(possible_dmas)) < 0) {
+			snd_card_free(card);
+			snd_printk("unable to find a free DMA2\n");
+			return -EBUSY;
+		}
+	}
+
+	if ((err = snd_gus_create(card,
+				  snd_port[dev],
+				  -irq, dma1, dma2,
+				  0, snd_channels[dev],
+				  snd_pcm_channels[dev],
+				  0, &gus)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_gusmax_detect(gus)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	maxcard->gus_status_reg = gus->gf1.reg_irqstat;
+	maxcard->pcm_status_reg = gus->gf1.port + 0x10c + 2;
+	snd_gusmax_init(dev, card, gus);
+	if ((err = snd_gus_initialize(gus)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if (!gus->max_flag) {
+		snd_card_free(card);
+		snd_printk("GUS MAX soundcard was not detected at 0x%lx\n", gus->gf1.port);
+		return -ENODEV;
+	}
+
+	if (request_irq(irq, snd_gusmax_interrupt, SA_INTERRUPT, "GUS MAX", (void *)maxcard)) {
+		snd_card_free(card);
+		snd_printk("unable to grab IRQ %d\n", irq);
+		return -EBUSY;
+	}
+	maxcard->irq = irq;
+	
+	if ((err = snd_cs4231_create(card,
+				     gus->gf1.port + 0x10c, -1, irq,
+				     dma2 < 0 ? dma1 : dma2, dma1,
+				     CS4231_HW_DETECT,
+				     CS4231_HWSHARE_IRQ |
+				     CS4231_HWSHARE_DMA1 |
+				     CS4231_HWSHARE_DMA2,
+				     &cs4231)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_cs4231_pcm(cs4231, 0, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_cs4231_mixer(cs4231)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_cs4231_timer(cs4231, 2, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if (snd_pcm_channels[dev] > 0) {
+		if ((err = snd_gf1_pcm_new(gus, 1, 1, NULL)) < 0) {
+			snd_card_free(card);
+			return err;
+		}
+	}
+	if ((err = snd_gusmax_mixer(cs4231)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	if ((err = snd_gf1_rawmidi_new(gus, 0, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	sprintf(card->longname + strlen(card->longname), " at 0x%lx, irq %i, dma %i", gus->gf1.port, irq, dma1);
+	if (dma2 >= 0)
+		sprintf(card->longname + strlen(card->longname), "&%i", dma2);
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+		
+	maxcard->gus = gus;
+	maxcard->cs4231 = cs4231;
+	snd_gusmax_cards[dev] = card;
+	return 0;
+}
+
+static int __init snd_gusmax_legacy_auto_probe(unsigned long port)
+{
+	static int dev = 0;
+	int res;
+
+	for ( ; dev < SNDRV_CARDS; dev++) {
+		if (!snd_enable[dev] || snd_port[dev] != SNDRV_AUTO_PORT)
+			continue;
+		snd_port[dev] = port;
+		res = snd_gusmax_probe(dev);
+		if (res < 0)
+			snd_port[dev] = SNDRV_AUTO_PORT;
+		return res;
+	}
+	return -ENODEV;
+}
+
+static int __init alsa_card_gusmax_init(void)
+{
+	static unsigned long possible_ports[] = {0x220, 0x230, 0x240, 0x250, 0x260, -1};
+	int dev, cards;
+
+	for (dev = cards = 0; dev < SNDRV_CARDS && snd_enable[dev] > 0; dev++) {
+		if (snd_port[dev] == SNDRV_AUTO_PORT)
+			continue;
+		if (snd_gusmax_probe(dev) >= 0)
+			cards++;
+	}
+	cards += snd_legacy_auto_probe(possible_ports, snd_gusmax_legacy_auto_probe);
+	if (!cards) {
+#ifdef MODULE
+		snd_printk("GUS MAX soundcard not found or device busy\n");
+#endif
+		return -ENODEV;
+	}
+	return 0;
+}
+
+static void __exit alsa_card_gusmax_exit(void)
+{
+	int idx;
+
+	for (idx = 0; idx < SNDRV_CARDS; idx++)
+		snd_card_free(snd_gusmax_cards[idx]);
+}
+
+module_init(alsa_card_gusmax_init)
+module_exit(alsa_card_gusmax_exit)
+
+#ifndef MODULE
+
+/* format is: snd-card-gusmax=snd_enable,snd_index,snd_id,
+			      snd_port,snd_irq,
+			      snd_dma1,snd_dma2,
+			      snd_joystick_dac,
+			      snd_channels,snd_pcm_channels */
+
+static int __init alsa_card_gusmax_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_port[nr_dev]) == 2 &&
+	       get_option(&str,&snd_irq[nr_dev]) == 2 &&
+	       get_option(&str,&snd_dma1[nr_dev]) == 2 &&
+	       get_option(&str,&snd_dma2[nr_dev]) == 2 &&
+	       get_option(&str,&snd_joystick_dac[nr_dev]) == 2 &&
+	       get_option(&str,&snd_channels[nr_dev]) == 2 &&
+	       get_option(&str,&snd_pcm_channels[nr_dev]) == 2);
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-card-gusmax=", alsa_card_gusmax_setup);
+
+#endif /* ifndef MODULE */
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/interwave-stb.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/interwave-stb.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/interwave-stb.c	(revision 91)
@@ -0,0 +1,2 @@
+#define SNDRV_STB
+#include "interwave.c"
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/interwave.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/interwave.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/interwave.c	(revision 91)
@@ -0,0 +1,1037 @@
+/*
+ *  Driver for AMD InterWave soundcard
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *   1999/07/22		Erik Inge Bolso <knan@mo.himolde.no>
+ *			* mixer group handlers
+ *
+ */
+
+#define SNDRV_MAIN_OBJECT_FILE
+#include <sound/driver.h>
+#include <sound/gus.h>
+#include <sound/cs4231.h>
+#ifdef SNDRV_STB
+#include <sound/tea6330t.h>
+#endif
+#define SNDRV_LEGACY_AUTO_PROBE
+#define SNDRV_LEGACY_FIND_FREE_IRQ
+#define SNDRV_LEGACY_FIND_FREE_DMA
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+EXPORT_NO_SYMBOLS;
+#ifndef SNDRV_STB
+MODULE_DESCRIPTION("AMD InterWave");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{Gravis,UltraSound Plug & Play},"
+		"{STB,SoundRage32},"
+		"{MED,MED3210},"
+		"{Dynasonix,Dynasonix Pro},"
+		"{Panasonic,PCA761AW}}");
+#else
+MODULE_DESCRIPTION("AMD InterWave STB with TEA6330T");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{AMD,InterWave STB with TEA6330T}}");
+#endif
+
+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;	/* Enable this card */
+#ifdef __ISAPNP__
+#ifdef TARGET_OS2
+static int snd_isapnp[SNDRV_CARDS] = {1,1,1,1,1,1,1,1};
+#else
+static int snd_isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
+#endif
+#endif
+static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* 0x210,0x220,0x230,0x240,0x250,0x260 */
+#ifdef SNDRV_STB
+static long snd_port_tc[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* 0x350,0x360,0x370,0x380 */
+#endif
+static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* 2,3,5,9,11,12,15 */
+static int snd_dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* 0,1,3,5,6,7 */
+static int snd_dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* 0,1,3,5,6,7 */
+#ifdef TARGET_OS2
+static int snd_joystick_dac[SNDRV_CARDS] = {REPEAT_SNDRV(29)};
+				/* 0 to 31, (0.59V-4.52V or 0.389V-2.98V) */
+static int snd_midi[SNDRV_CARDS] = {REPEAT_SNDRV(0)};
+static int snd_pcm_channels[SNDRV_CARDS] = {REPEAT_SNDRV(2)};
+static int snd_effect[SNDRV_CARDS] = {REPEAT_SNDRV(0)};
+#else
+static int snd_joystick_dac[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 29};
+				/* 0 to 31, (0.59V-4.52V or 0.389V-2.98V) */
+static int snd_midi[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0};
+static int snd_pcm_channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2};
+static int snd_effect[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0};
+#endif
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for InterWave soundcard.");
+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 InterWave soundcard.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable InterWave soundcard.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(snd_isapnp, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_isapnp, "ISA PnP detection for specified soundcard.");
+MODULE_PARM_SYNTAX(snd_isapnp, SNDRV_ISAPNP_DESC);
+MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_port, "Port # for InterWave driver.");
+MODULE_PARM_SYNTAX(snd_port, SNDRV_ENABLED ",allows:{{0x210,0x260,0x10}},dialog:list");
+#ifdef SNDRV_STB
+MODULE_PARM(snd_port_tc, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_port_tc, "Tone control (TEA6330T - i2c bus) port # for InterWave driver.");
+MODULE_PARM_SYNTAX(snd_port_tc, SNDRV_ENABLED ",allows:{{0x350,0x380,0x10}},dialog:list");
+#endif
+MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_irq, "IRQ # for InterWave driver.");
+MODULE_PARM_SYNTAX(snd_irq, SNDRV_ENABLED ",allows:{{3},{5},{9},{11},{12},{15}},dialog:list");
+MODULE_PARM(snd_dma1, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_dma1, "DMA1 # for InterWave driver.");
+MODULE_PARM_SYNTAX(snd_dma1, SNDRV_DMA_DESC);
+MODULE_PARM(snd_dma2, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_dma2, "DMA2 # for InterWave driver.");
+MODULE_PARM_SYNTAX(snd_dma2, SNDRV_DMA_DESC);
+MODULE_PARM(snd_joystick_dac, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_joystick_dac, "Joystick DAC level 0.59V-4.52V or 0.389V-2.98V for InterWave driver.");
+MODULE_PARM_SYNTAX(snd_joystic_dac, SNDRV_ENABLED ",allows:{{0,31}}");
+MODULE_PARM(snd_midi, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_midi, "MIDI UART enable for InterWave driver.");
+MODULE_PARM_SYNTAX(snd_midi, SNDRV_ENABLED "," SNDRV_ENABLE_DESC);
+MODULE_PARM(snd_pcm_channels, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_pcm_channels, "Reserved PCM channels for InterWave driver.");
+MODULE_PARM_SYNTAX(snd_pcm_channels, SNDRV_ENABLED ",allows:{{2,16}}");
+MODULE_PARM(snd_effect, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_effect, "Effects enable for InterWave driver.");
+MODULE_PARM_SYNTAX(snd_effect, SNDRV_ENABLED "," SNDRV_ENABLE_DESC);
+
+struct snd_interwave {
+	int irq;
+	snd_card_t *card;
+	snd_gus_card_t *gus;
+	cs4231_t *cs4231;
+#ifdef SNDRV_STB
+	struct resource *i2c_res;
+#endif
+	unsigned short gus_status_reg;
+	unsigned short pcm_status_reg;
+#ifdef __ISAPNP__
+	struct isapnp_dev *dev;
+#ifdef SNDRV_STB
+	struct isapnp_dev *devtc;
+#endif
+#endif
+};
+
+static snd_card_t *snd_interwave_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+
+#ifdef __ISAPNP__
+
+static struct isapnp_card *snd_interwave_isapnp_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+static const struct isapnp_card_id *snd_interwave_isapnp_id[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+
+#ifdef TARGET_OS2
+#define ISAPNP_INTERWAVE(_va, _vb, _vc, _device, _audio) \
+	{ \
+		0, ISAPNP_CARD_ID(_va, _vb, _vc, _device), \
+		{ ISAPNP_DEVICE_ID(_va, _vb, _vc, _audio)}  \
+	}
+#define ISAPNP_INTERWAVE_STB(_va, _vb, _vc, _device, _audio, _tone) \
+	{ \
+		0, ISAPNP_CARD_ID(_va, _vb, _vc, _device), \
+		{ ISAPNP_DEVICE_ID(_va, _vb, _vc, _audio), \
+		  ISAPNP_DEVICE_ID(_va, _vb, _vc, _tone) }\
+	}
+#else
+#define ISAPNP_INTERWAVE(_va, _vb, _vc, _device, _audio) \
+	{ \
+		ISAPNP_CARD_ID(_va, _vb, _vc, _device), \
+		devs : { ISAPNP_DEVICE_ID(_va, _vb, _vc, _audio), } \
+	}
+#define ISAPNP_INTERWAVE_STB(_va, _vb, _vc, _device, _audio, _tone) \
+	{ \
+		ISAPNP_CARD_ID(_va, _vb, _vc, _device), \
+		devs : { ISAPNP_DEVICE_ID(_va, _vb, _vc, _audio), \
+			 ISAPNP_DEVICE_ID(_va, _vb, _vc, _tone), } \
+	}
+#endif
+
+static struct isapnp_card_id snd_interwave_pnpids[] __devinitdata = {
+#ifndef SNDRV_STB
+	/* Gravis UltraSound Plug & Play */
+	ISAPNP_INTERWAVE('G','R','V',0x0001,0x0000),
+	/* STB SoundRage32 */
+	ISAPNP_INTERWAVE('S','T','B',0x011a,0x0010),
+	/* MED3210 */
+	ISAPNP_INTERWAVE('D','X','P',0x3201,0x0010),
+	/* Dynasonic Pro */
+	/* This device also have CDC1117:DynaSonix Pro Audio Effects Processor */
+	ISAPNP_INTERWAVE('C','D','C',0x1111,0x1112),
+	/* Panasonic PCA761AW Audio Card */
+	ISAPNP_INTERWAVE('A','D','V',0x55ff,0x0010),
+#else
+	/* InterWave STB with TEA6330T */
+	ISAPNP_INTERWAVE_STB('A','D','V',0x550a,0x0010,0x0015),
+#endif
+	{ ISAPNP_CARD_END, }
+};
+
+ISAPNP_CARD_TABLE(snd_interwave_pnpids);
+
+#endif /* __ISAPNP__ */
+
+
+#ifdef SNDRV_STB
+static void snd_interwave_i2c_setlines(snd_i2c_bus_t *bus, int ctrl, int data)
+{
+	unsigned long port = bus->private_value;
+
+#if 0
+	printk("i2c_setlines - 0x%lx <- %i,%i\n", port, ctrl, data);
+#endif
+	outb((data << 1) | ctrl, port);
+	udelay(10);
+}
+
+static int snd_interwave_i2c_getdataline(snd_i2c_bus_t *bus)
+{
+	unsigned long port = bus->private_value;
+	unsigned char res;
+
+	res = (inb(port) & 2) >> 1;
+#if 0
+	printk("i2c_getdataline - 0x%lx -> %i\n", port, res);
+#endif
+	return res;
+}
+
+static int __init snd_interwave_detect_stb(struct snd_interwave *iwcard,
+					   snd_gus_card_t * gus, int dev,
+					   snd_i2c_bus_t **rbus)
+{
+	unsigned long port;
+	snd_i2c_bus_t *bus;
+	snd_card_t *card = iwcard->card;
+	unsigned long flags;
+	char name[32];
+	int err;
+
+	*rbus = NULL;
+	port = snd_port_tc[dev];
+	if (port == SNDRV_AUTO_PORT) {
+		port = 0x350;
+		if (gus->gf1.port == 0x250) {
+			port = 0x360;
+		}
+		while (port <= 0x380) {
+			if ((iwcard->i2c_res = request_region(port, 1, "InterWave (I2C bus)")) != NULL)
+				break;
+			port += 0x10;
+		}
+		if (port > 0x380)
+			return -ENODEV;
+	} else {
+		if ((iwcard->i2c_res = request_region(port, 1, "InterWave (I2C bus)")) != NULL)
+			return -ENODEV;
+	}
+	sprintf(name, "InterWave-%i", card->number);
+	if ((err = snd_i2c_bus_create(card, name, &bus)) < 0)
+		return err;
+	bus->private_value = port;
+	bus->i2c_setlines = snd_interwave_i2c_setlines;
+	bus->i2c_getdataline = snd_interwave_i2c_getdataline;
+	SNDRV_LOCK_I2C_BUS(bus);
+	snd_i2c_reset(bus);
+#if 0
+	{
+		int idx, ack;
+		for (idx = 0; idx < 256; idx += 2) {
+			snd_i2c_start(bus);
+			ack = snd_i2c_sendbyte(bus, idx, 0);
+			snd_i2c_stop(bus);
+			if (!ack) {
+				printk("i2c: scanning bus %s: found device at addr=0x%x\n",
+				       bus->name, idx);
+			}
+		}
+	}
+#endif
+	SNDRV_UNLOCK_I2C_BUS(bus);
+	if ((err = snd_tea6330t_detect(bus, 0)) < 0)
+		return err;
+	*rbus = bus;
+	return 0;
+}
+#endif
+
+static int __init snd_interwave_detect(struct snd_interwave *iwcard,
+				       snd_gus_card_t * gus,
+				       int dev
+#ifdef SNDRV_STB
+				       , snd_i2c_bus_t **rbus
+#endif
+				       )
+{
+	unsigned long flags;
+	unsigned char rev1, rev2;
+
+	snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 0);	/* reset GF1 */
+#ifdef CONFIG_SND_DEBUG_DETECT
+	{
+		int d;
+
+		if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 0) {
+			snd_printk("[0x%lx] check 1 failed - 0x%x\n", gus->gf1.port, d);
+			return -ENODEV;
+		}
+	}
+#else
+	if ((snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET) & 0x07) != 0)
+		return -ENODEV;
+#endif
+	udelay(160);
+	snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1);	/* release reset */
+	udelay(160);
+#ifdef CONFIG_SND_DEBUG_DETECT
+	{
+		int d;
+
+		if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 1) {
+			snd_printk("[0x%lx] check 2 failed - 0x%x\n", gus->gf1.port, d);
+			return -ENODEV;
+		}
+	}
+#else
+	if ((snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET) & 0x07) != 1)
+		return -ENODEV;
+#endif
+
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	rev1 = snd_gf1_look8(gus, SNDRV_GF1_GB_VERSION_NUMBER);
+	snd_gf1_write8(gus, SNDRV_GF1_GB_VERSION_NUMBER, ~rev1);
+	rev2 = snd_gf1_look8(gus, SNDRV_GF1_GB_VERSION_NUMBER);
+	snd_gf1_write8(gus, SNDRV_GF1_GB_VERSION_NUMBER, rev1);
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+	snd_printdd("[0x%lx] InterWave check - rev1=0x%x, rev2=0x%x\n", gus->gf1.port, rev1, rev2);
+	if ((rev1 & 0xf0) == (rev2 & 0xf0) &&
+	    (rev1 & 0x0f) != (rev2 & 0x0f)) {
+		snd_printdd("[0x%lx] InterWave check - passed\n", gus->gf1.port);
+		gus->interwave = 1;
+		strcpy(gus->card->shortname, "AMD InterWave");
+		gus->revision = rev1 >> 4;
+#ifndef SNDRV_STB
+		return 0;	/* ok.. We have an InterWave board */
+#else
+		return snd_interwave_detect_stb(iwcard, gus, dev, rbus);
+#endif
+	}
+	snd_printdd("[0x%lx] InterWave check - failed\n", gus->gf1.port);
+	return -ENODEV;
+}
+
+static void snd_interwave_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct snd_interwave *iwcard = (struct snd_interwave *) dev_id;
+	int loop, max = 5;
+
+	do {
+		loop = 0;
+		if (inb(iwcard->gus_status_reg)) {
+			snd_gus_interrupt(irq, iwcard->gus, regs);
+			loop++;
+		}
+		if (inb(iwcard->pcm_status_reg) & 0x01) {	/* IRQ bit is set? */
+			snd_cs4231_interrupt(irq, iwcard->cs4231, regs);
+			loop++;
+		}
+	} while (loop && --max > 0);
+}
+
+static void __init snd_interwave_reset(snd_gus_card_t * gus)
+{
+	snd_gf1_write8(gus, SNDRV_GF1_GB_RESET, 0x00);
+	udelay(160);
+	snd_gf1_write8(gus, SNDRV_GF1_GB_RESET, 0x01);
+	udelay(160);
+}
+
+static void __init snd_interwave_bank_sizes(snd_gus_card_t * gus, int *sizes)
+{
+	unsigned int idx;
+	unsigned int local;
+	unsigned char d;
+
+	for (idx = 0; idx < 4; idx++) {
+		sizes[idx] = 0;
+		d = 0x55;
+		for (local = idx << 22;
+		     local < (idx << 22) + 0x400000;
+		     local += 0x40000, d++) {
+			snd_gf1_poke(gus, local, d);
+			snd_gf1_poke(gus, local + 1, d + 1);
+#if 0
+			printk("d = 0x%x, local = 0x%x, local + 1 = 0x%x, idx << 22 = 0x%x\n",
+			       d,
+			       snd_gf1_peek(gus, local),
+			       snd_gf1_peek(gus, local + 1),
+			       snd_gf1_peek(gus, idx << 22));
+#endif
+			if (snd_gf1_peek(gus, local) != d ||
+			    snd_gf1_peek(gus, local + 1) != d + 1 ||
+			    snd_gf1_peek(gus, idx << 22) != 0x55)
+				break;
+			sizes[idx]++;
+		}
+	}
+#if 0
+	printk("sizes: %i %i %i %i\n", sizes[0], sizes[1], sizes[2], sizes[3]);
+#endif
+}
+
+struct rom_hdr {
+	/* 000 */ unsigned char iwave[8];
+	/* 008 */ unsigned char rom_hdr_revision;
+	/* 009 */ unsigned char series_number;
+	/* 010 */ unsigned char series_name[16];
+	/* 026 */ unsigned char date[10];
+	/* 036 */ unsigned short vendor_revision_major;
+	/* 038 */ unsigned short vendor_revision_minor;
+	/* 040 */ unsigned int rom_size;
+	/* 044 */ unsigned char copyright[128];
+	/* 172 */ unsigned char vendor_name[64];
+	/* 236 */ unsigned char rom_description[128];
+	/* 364 */ unsigned char pad[147];
+	/* 511 */ unsigned char csum;
+};
+
+static void __init snd_interwave_detect_memory(snd_gus_card_t * gus)
+{
+	static unsigned int lmc[13] =
+	{
+		0x00000001, 0x00000101, 0x01010101, 0x00000401,
+		0x04040401, 0x00040101, 0x04040101, 0x00000004,
+		0x00000404, 0x04040404, 0x00000010, 0x00001010,
+		0x10101010
+	};
+
+	int bank_pos, pages;
+	unsigned int i, lmct;
+	int psizes[4];
+	unsigned char csum;
+	struct rom_hdr romh;
+
+	snd_interwave_reset(gus);
+	snd_gf1_write8(gus, SNDRV_GF1_GB_GLOBAL_MODE, snd_gf1_read8(gus, SNDRV_GF1_GB_GLOBAL_MODE) | 0x01);		/* enhanced mode */
+	snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x01);	/* DRAM I/O cycles selected */
+	snd_gf1_write16(gus, SNDRV_GF1_GW_MEMORY_CONFIG, (snd_gf1_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG) & 0xff10) | 0x004c);
+	/* ok.. simple test of memory size */
+	pages = 0;
+	snd_gf1_poke(gus, 0, 0x55);
+	snd_gf1_poke(gus, 1, 0xaa);
+#if 1
+	if (snd_gf1_peek(gus, 0) == 0x55 && snd_gf1_peek(gus, 1) == 0xaa)
+#else
+	if (0)			/* ok.. for testing of 0k RAM */
+#endif
+	{
+		snd_interwave_bank_sizes(gus, psizes);
+		lmct = (psizes[3] << 24) | (psizes[2] << 16) |
+		    (psizes[1] << 8) | psizes[0];
+#if 0
+		printk("lmct = 0x%08x\n", lmct);
+#endif
+		for (i = 0; i < sizeof(lmc) / sizeof(unsigned int); i++)
+			if (lmct == lmc[i]) {
+#if 0
+				printk("found !!! %i\n", i);
+#endif
+				snd_gf1_write16(gus, SNDRV_GF1_GW_MEMORY_CONFIG, (snd_gf1_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG) & 0xfff0) | i);
+				snd_interwave_bank_sizes(gus, psizes);
+				break;
+			}
+		if (i >= sizeof(lmc) / sizeof(unsigned int) && !gus->gf1.enh_mode)
+			 snd_gf1_write16(gus, SNDRV_GF1_GW_MEMORY_CONFIG, (snd_gf1_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG) & 0xfff0) | 2);
+		for (i = 0; i < 4; i++) {
+			gus->gf1.mem_alloc.banks_8[i].address =
+			    gus->gf1.mem_alloc.banks_16[i].address = i << 22;
+			gus->gf1.mem_alloc.banks_8[i].size =
+			    gus->gf1.mem_alloc.banks_16[i].size = psizes[i] << 18;
+			pages += psizes[i];
+		}
+	}
+	pages <<= 18;
+	gus->gf1.memory = pages;
+
+	snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x03);	/* select ROM */
+	snd_gf1_write16(gus, SNDRV_GF1_GW_MEMORY_CONFIG, (snd_gf1_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG) & 0xff1f) | (4 << 5));
+	gus->gf1.rom_banks = 0;
+	gus->gf1.rom_memory = 0;
+	for (bank_pos = 0; bank_pos < 16L * 1024L * 1024L; bank_pos += 4L * 1024L * 1024L) {
+		for (i = 0; i < sizeof(struct rom_hdr); i++)
+			*(((unsigned char *) &romh) + i) = snd_gf1_peek(gus, i + bank_pos);
+#ifdef CONFIG_SND_DEBUG_ROM
+		printk("ROM at 0x%06x = %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", bank_pos,
+		       romh.iwave[0], romh.iwave[1], romh.iwave[2], romh.iwave[3],
+		       romh.iwave[4], romh.iwave[5], romh.iwave[6], romh.iwave[7]);
+#endif
+		if (strncmp(romh.iwave, "INTRWAVE", 8))
+			continue;	/* first check */
+		csum = 0;
+		for (i = 0; i < sizeof(struct rom_hdr) - 1; i++)
+			csum += *(((unsigned char *) &romh) + i);
+#ifdef CONFIG_SND_DEBUG_ROM
+		printk("ROM checksum = 0x%x == 0x%x (computed)\n", romh.csum, (unsigned char) (256 - csum));
+#endif
+		if (256 - csum != romh.csum)
+			continue;	/* not valid rom */
+		gus->gf1.rom_banks++;
+		gus->gf1.rom_present |= 1 << (bank_pos >> 22);
+#ifdef SNDRV_LITTLE_ENDIAN
+		gus->gf1.rom_memory = romh.rom_size;
+#else
+		gus->gf1.rom_memory = ((romh.rom_size >> 24) & 0x000000ff) |
+				      ((romh.rom_size >>  8) & 0x0000ff00) |
+				      ((romh.rom_size <<  8) & 0x00ff0000) |
+				      ((romh.rom_size << 24) & 0xff000000);
+#endif
+	}
+#if 0
+	if (gus->gf1.rom_memory > 0) {
+		if (gus->gf1.rom_banks == 1 && gus->gf1.rom_present == 8)
+			gus->card->type = SNDRV_CARD_TYPE_IW_DYNASONIC;
+	}
+#endif
+	snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x00);	/* select RAM */
+
+	if (!gus->gf1.enh_mode)
+		snd_interwave_reset(gus);
+}
+
+static void __init snd_interwave_init(int dev, snd_gus_card_t * gus)
+{
+	unsigned long flags;
+
+	/* ok.. some InterWave specific initialization */
+	spin_lock_irqsave(&gus->reg_lock, flags);
+	snd_gf1_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, 0x00);
+	snd_gf1_write8(gus, SNDRV_GF1_GB_COMPATIBILITY, 0x1f);
+	snd_gf1_write8(gus, SNDRV_GF1_GB_DECODE_CONTROL, 0x49);
+	snd_gf1_write8(gus, SNDRV_GF1_GB_VERSION_NUMBER, 0x11);
+	snd_gf1_write8(gus, SNDRV_GF1_GB_MPU401_CONTROL_A, 0x00);
+	snd_gf1_write8(gus, SNDRV_GF1_GB_MPU401_CONTROL_B, 0x30);
+	snd_gf1_write8(gus, SNDRV_GF1_GB_EMULATION_IRQ, 0x00);
+	spin_unlock_irqrestore(&gus->reg_lock, flags);
+	gus->equal_irq = 1;
+	gus->codec_flag = 1;
+	gus->interwave = 1;
+	gus->max_flag = 1;
+	gus->joystick_dac = snd_joystick_dac[dev];
+
+}
+
+#define INTERWAVE_CONTROLS (sizeof(snd_interwave_controls)/sizeof(snd_kcontrol_new_t))
+
+static snd_kcontrol_new_t snd_interwave_controls[] = {
+CS4231_DOUBLE("Master Playback Switch", 0, CS4231_LINE_LEFT_OUTPUT, CS4231_LINE_RIGHT_OUTPUT, 7, 7, 1, 1),
+CS4231_DOUBLE("Master Playback Volume", 0, CS4231_LINE_LEFT_OUTPUT, CS4231_LINE_RIGHT_OUTPUT, 0, 0, 31, 1),
+CS4231_DOUBLE("Mic Playback Switch", 0, CS4231_LEFT_MIC_INPUT, CS4231_RIGHT_MIC_INPUT, 7, 7, 1, 1),
+CS4231_DOUBLE("Mic Playback Volume", 0, CS4231_LEFT_MIC_INPUT, CS4231_RIGHT_MIC_INPUT, 0, 0, 31, 1)
+};
+
+static int __init snd_interwave_mixer(cs4231_t *chip)
+{
+	snd_card_t *card = chip->card;
+	snd_ctl_elem_id_t id1, id2;
+	int idx, err;
+
+	memset(&id1, 0, sizeof(id1));
+	memset(&id2, 0, sizeof(id2));
+	id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+#if 0
+	/* remove mono microphone controls */
+	strcpy(id1.name, "Mic Playback Switch");
+	if ((err = snd_ctl_remove_id(card, &id1)) < 0)
+		return err;
+	strcpy(id1.name, "Mic Playback Volume");
+	if ((err = snd_ctl_remove_id(card, &id1)) < 0)
+		return err;
+#endif
+	/* add new master and mic controls */
+	for (idx = 0; idx < INTERWAVE_CONTROLS; idx++)
+		if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_interwave_controls[idx], chip))) < 0)
+			return err;
+	snd_cs4231_out(chip, CS4231_LINE_LEFT_OUTPUT, 0x9f);
+	snd_cs4231_out(chip, CS4231_LINE_RIGHT_OUTPUT, 0x9f);
+	snd_cs4231_out(chip, CS4231_LEFT_MIC_INPUT, 0x9f);
+	snd_cs4231_out(chip, CS4231_RIGHT_MIC_INPUT, 0x9f);
+	/* reassign AUXA to SYNTHESIZER */
+	strcpy(id1.name, "Aux Playback Switch");
+	strcpy(id2.name, "Synth Playback Switch");
+	if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
+		return err;
+	strcpy(id1.name, "Aux Playback Volume");
+	strcpy(id2.name, "Synth Playback Volume");
+	if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
+		return err;
+	/* reassign AUXB to CD */
+	strcpy(id1.name, "Aux Playback Switch"); id1.index = 1;
+	strcpy(id2.name, "CD Playback Switch");
+	if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
+		return err;
+	strcpy(id1.name, "Aux Playback Volume");
+	strcpy(id2.name, "CD Playback Volume");
+	if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
+		return err;
+	return 0;
+}
+
+#ifdef __ISAPNP__
+
+static int __init snd_interwave_isapnp(int dev, struct snd_interwave *iwcard)
+{
+	const struct isapnp_card_id *id = snd_interwave_isapnp_id[dev];
+	struct isapnp_card *card = snd_interwave_isapnp_cards[dev];
+	struct isapnp_dev *pdev;
+
+	iwcard->dev = isapnp_find_dev(card, id->devs[0].vendor, id->devs[0].function, NULL);
+	if (iwcard->dev->active) {
+		iwcard->dev = NULL;
+		return -EBUSY;
+	}
+#ifdef SNDRV_STB
+	iwcard->devtc = isapnp_find_dev(card, id->devs[1].vendor, id->devs[1].function, NULL);
+	if (iwcard->devtc->active) {
+		iwcard->dev = iwcard->devtc = NULL;
+		return -EBUSY;
+	}
+#endif
+	/* Synth & Codec initialization */
+	pdev = iwcard->dev;
+	if (pdev->prepare(pdev)<0)
+		return -EAGAIN;
+	if (snd_port[dev] != SNDRV_AUTO_PORT) {
+		isapnp_resource_change(&pdev->resource[0], snd_port[dev], 16);
+		isapnp_resource_change(&pdev->resource[1], snd_port[dev] + 0x100, 12);
+		isapnp_resource_change(&pdev->resource[2], snd_port[dev] + 0x10c, 4);
+	}
+	if (snd_dma1[dev] != SNDRV_AUTO_DMA)
+		isapnp_resource_change(&pdev->dma_resource[0], snd_dma1[dev], 1);
+	if (snd_dma2[dev] != SNDRV_AUTO_DMA)
+		isapnp_resource_change(&pdev->dma_resource[1], snd_dma2[dev], 1);
+	if (snd_dma2[dev] < 0)
+		isapnp_resource_change(&pdev->dma_resource[1], 4, 1);
+	if (snd_irq[dev] != SNDRV_AUTO_IRQ)
+		isapnp_resource_change(&pdev->irq_resource[0], snd_irq[dev], 1);
+	if (pdev->activate(pdev)<0) {
+		snd_printk("isapnp configure failure (out of resources?)\n");
+		return -EBUSY;
+	}
+	if (pdev->resource[0].start + 0x100 != pdev->resource[1].start ||
+	    pdev->resource[0].start + 0x10c != pdev->resource[2].start) {
+		snd_printk("isapnp configure failure (wrong ports)\n");
+		pdev->deactivate(pdev);
+		return -ENOENT;
+	}
+	snd_port[dev] = pdev->resource[0].start;
+	snd_dma1[dev] = pdev->dma_resource[0].start;
+	if (snd_dma2[dev] >= 0)
+		snd_dma2[dev] = pdev->dma_resource[1].start;
+	snd_irq[dev] = pdev->irq_resource[0].start;
+	snd_printdd("isapnp IW: sb port=0x%lx, gf1 port=0x%lx, codec port=0x%lx\n",
+				pdev->resource[0].start,
+				pdev->resource[1].start,
+				pdev->resource[2].start);
+	snd_printdd("isapnp IW: dma1=%i, dma2=%i, irq=%i\n", snd_dma1[dev], snd_dma2[dev], snd_irq[dev]);
+#ifdef SNDRV_STB
+	/* Tone Control initialization */
+	pdev = iwcard->devtc;
+	if (pdev->prepare(pdev)<0) {
+		iwcard->dev->deactivate(iwcard->dev);
+		return -EAGAIN;
+	}
+	if (snd_port_tc[dev] != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[0], snd_port_tc[dev], 1);
+	if (pdev->activate(pdev)<0) {
+		snd_printk("Tone Control isapnp configure failure (out of resources?)\n");
+		iwcard->dev->deactivate(iwcard->dev);
+		return -EBUSY;
+	}
+	snd_port_tc[dev] = pdev->resource[0].start;
+	snd_printdd("isapnp IW: tone control port=0x%lx\n", snd_port_tc[dev]);
+#endif
+	return 0;
+}
+
+static void snd_interwave_deactivate(struct snd_interwave *iwcard)
+{
+	if (iwcard->dev) {
+		iwcard->dev->deactivate(iwcard->dev);
+		iwcard->dev = NULL;
+	}
+#ifdef SNDRV_STB
+	if (iwcard->devtc) {
+		iwcard->devtc->deactivate(iwcard->devtc);
+		iwcard->devtc = NULL;
+	}
+#endif
+}
+
+#endif /* __ISAPNP__ */
+
+static void snd_interwave_free(snd_card_t *card)
+{
+	struct snd_interwave *iwcard = (struct snd_interwave *)card->private_data;
+
+	if (iwcard == NULL)
+		return;
+#ifdef __ISAPNP__
+	snd_interwave_deactivate(iwcard);
+#endif
+#ifdef SNDRV_STB
+	if (iwcard->i2c_res)
+		release_resource(iwcard->i2c_res);
+#endif
+	if (iwcard->irq >= 0)
+		free_irq(iwcard->irq, (void *)iwcard);
+}
+
+static int __init snd_interwave_probe(int dev)
+{
+	static int possible_irqs[] = {5, 11, 12, 9, 7, 15, 3, -1};
+	static int possible_dmas[] = {0, 1, 3, 5, 6, 7, -1};
+	int irq, dma1, dma2;
+	snd_card_t *card;
+	struct snd_interwave *iwcard;
+	cs4231_t *cs4231;
+	snd_gus_card_t *gus;
+#ifdef SNDRV_STB
+	snd_i2c_bus_t *i2c_bus;
+#endif
+	snd_pcm_t *pcm;
+	char *str;
+	int err;
+
+	card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE,
+			    sizeof(struct snd_interwave));
+	if (card == NULL)
+		return -ENOMEM;
+	iwcard = (struct snd_interwave *)card->private_data;
+	iwcard->card = card;
+	iwcard->irq = -1;
+	card->private_free = snd_interwave_free;
+#ifdef __ISAPNP__
+	if (snd_isapnp[dev] && snd_interwave_isapnp(dev, iwcard)) {
+		snd_card_free(card);
+		return -ENODEV;
+	}
+#endif
+	irq = snd_irq[dev];
+	if (irq == SNDRV_AUTO_IRQ) {
+		if ((irq = snd_legacy_find_free_irq(possible_irqs)) < 0) {
+			snd_card_free(card);
+			snd_printk("unable to find a free IRQ\n");
+			return -EBUSY;
+		}
+	}
+	dma1 = snd_dma1[dev];
+	if (dma1 == SNDRV_AUTO_DMA) {
+		if ((dma1 = snd_legacy_find_free_dma(possible_dmas)) < 0) {
+			snd_card_free(card);
+			snd_printk("unable to find a free DMA1\n");
+			return -EBUSY;
+		}
+	}
+	dma2 = snd_dma2[dev];
+	if (dma2 == SNDRV_AUTO_DMA) {
+		if ((dma2 = snd_legacy_find_free_dma(possible_dmas)) < 0) {
+			snd_card_free(card);
+			snd_printk("unable to find a free DMA2\n");
+			return -EBUSY;
+		}
+	}
+
+	if ((err = snd_gus_create(card,
+				  snd_port[dev],
+				  -irq, dma1, dma2,
+				  0, 32,
+				  snd_pcm_channels[dev], snd_effect[dev], &gus)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_interwave_detect(iwcard, gus, dev
+#ifdef SNDRV_STB
+            , &i2c_bus
+#endif
+	    )) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	iwcard->gus_status_reg = gus->gf1.reg_irqstat;
+	iwcard->pcm_status_reg = gus->gf1.port + 0x10c + 2;
+
+	snd_interwave_init(dev, gus);
+	snd_interwave_detect_memory(gus);
+	if ((err = snd_gus_initialize(gus)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	if (request_irq(irq, snd_interwave_interrupt, SA_INTERRUPT, "InterWave", (void *)iwcard)) {
+		snd_card_free(card);
+		snd_printk("unable to grab IRQ %d\n", irq);
+		return -EBUSY;
+	}
+	iwcard->irq = irq;
+
+	if ((err = snd_cs4231_create(card,
+				     gus->gf1.port + 0x10c, -1, irq,
+				     dma2 < 0 ? dma1 : dma2, dma1,
+				     CS4231_HW_INTERWAVE,
+				     CS4231_HWSHARE_IRQ |
+				     CS4231_HWSHARE_DMA1 |
+				     CS4231_HWSHARE_DMA2,
+				     &cs4231)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_cs4231_pcm(cs4231, 0, &pcm)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	sprintf(pcm->name + strlen(pcm->name), " rev %c", gus->revision + 'A');
+	strcat(pcm->name, " (chip)");
+	if ((err = snd_cs4231_timer(cs4231, 2, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_cs4231_mixer(cs4231)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if (snd_pcm_channels[dev] > 0) {
+		if ((err = snd_gf1_pcm_new(gus, 1, 1, NULL)) < 0) {
+			snd_card_free(card);
+			return err;
+		}
+	}
+	if ((err = snd_interwave_mixer(cs4231)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+#ifdef SNDRV_STB
+	{
+		snd_ctl_elem_id_t id1, id2;
+		memset(&id1, 0, sizeof(id1));
+		memset(&id2, 0, sizeof(id2));
+		id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+		strcpy(id1.name, "Master Playback Switch");
+		strcpy(id2.name, id1.name);
+		id2.index = 1;
+		if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) {
+			snd_card_free(card);
+			return err;
+		}
+		strcpy(id1.name, "Master Playback Volume");
+		strcpy(id2.name, id1.name);
+		if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) {
+			snd_card_free(card);
+			return err;
+		}
+		if ((err = snd_tea6330t_update_mixer(card, i2c_bus, 0, 1)) < 0) {
+			snd_card_free(card);
+			return err;
+		}
+	}
+#endif
+
+	gus->uart_enable = snd_midi[dev];
+	if ((err = snd_gf1_rawmidi_new(gus, 0, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+#ifndef SNDRV_STB
+	str = "AMD InterWave";
+	if (gus->gf1.rom_banks == 1 && gus->gf1.rom_present == 8)
+		str = "Dynasonic 3-D";
+#else
+	str = "InterWave STB";
+#endif
+	strcpy(card->driver, str);
+	strcpy(card->shortname, str);
+	sprintf(card->longname, "%s at 0x%lx, irq %i, dma %d",
+		str,
+		gus->gf1.port,
+		irq,
+		dma1);
+	if (dma2 >= 0)
+		sprintf(card->longname + strlen(card->longname), "&%d", dma2);
+
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	
+	iwcard->cs4231 = cs4231;
+	iwcard->gus = gus;
+	snd_interwave_cards[dev++] = card;
+	return 0;
+}
+
+static int __init snd_interwave_probe_legacy_port(unsigned long port)
+{
+	static int dev = 0;
+	int res;
+
+	for ( ; dev < SNDRV_CARDS; dev++) {
+		if (!snd_enable[dev] || snd_port[dev] != SNDRV_AUTO_PORT)
+                        continue;
+#ifdef __ISAPNP__
+		if (snd_isapnp[dev])
+			continue;
+#endif
+		snd_port[dev] = port;
+		res = snd_interwave_probe(dev);
+		if (res < 0)
+			snd_port[dev] = SNDRV_AUTO_PORT;
+		return res;
+	}
+	return -ENODEV;
+}
+
+#ifdef __ISAPNP__
+
+static int __init snd_interwave_isapnp_detect(struct isapnp_card *card,
+					      const struct isapnp_card_id *id)
+{
+	static int dev = 0;
+	int res;
+
+	for ( ; dev < SNDRV_CARDS; dev++) {
+		if (!snd_enable[dev] || !snd_isapnp[dev])
+			continue;
+		snd_interwave_isapnp_cards[dev] = card;
+		snd_interwave_isapnp_id[dev] = id;
+		res = snd_interwave_probe(dev);
+		if (res < 0)
+			return res;
+		dev++;
+		return 0;
+        }
+
+        return -ENODEV;
+}
+
+#endif /* __ISAPNP__ */
+
+static int __init alsa_card_interwave_init(void)
+{
+	int cards = 0;
+	static long possible_ports[] = {0x210, 0x220, 0x230, 0x240, 0x250, 0x260, -1};
+	int dev;
+
+	for (dev = 0; dev < SNDRV_CARDS; dev++) {
+		if (!snd_enable[dev] || snd_port[dev] == SNDRV_AUTO_PORT)
+			continue;
+#ifdef __ISAPNP__
+		if (snd_isapnp[dev])
+			continue;
+#endif
+		if (!snd_interwave_probe(dev)) {
+			cards++;
+			continue;
+		}
+#ifdef MODULE
+		snd_printk("InterWave soundcard #%i not found at 0x%lx or device busy\n", dev, snd_port[dev]);
+#endif
+	}
+	/* legacy auto configured cards */
+	cards += snd_legacy_auto_probe(possible_ports, snd_interwave_probe_legacy_port);
+#ifdef __ISAPNP__
+        /* ISA PnP cards */
+        cards += isapnp_probe_cards(snd_interwave_pnpids, snd_interwave_isapnp_detect);
+#endif
+
+	if (!cards) {
+#ifdef MODULE
+		snd_printk("InterWave soundcard not found or device busy\n");
+#endif
+		return -ENODEV;
+	}
+	return 0;
+}
+
+static void __exit alsa_card_interwave_exit(void)
+{
+	int dev;
+
+	for (dev = 0; dev < SNDRV_CARDS; dev++)
+		snd_card_free(snd_interwave_cards[dev]);
+}
+
+module_init(alsa_card_interwave_init)
+module_exit(alsa_card_interwave_exit)
+
+#ifndef MODULE
+
+/* format is: snd-card-interwave=snd_enable,snd_index,snd_id,snd_isapnp,
+				 snd_port[,snd_port_tc],snd_irq,
+				 snd_dma1,snd_dma2,
+				 snd_joystick_dac,snd_midi,
+				 snd_pcm_channels,snd_effect */
+
+static int __init alsa_card_interwave_setup(char *str)
+{
+	static unsigned __initdata nr_dev = 0;
+	int __attribute__ ((__unused__)) pnp = INT_MAX;
+
+	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,&pnp) == 2 &&
+	       get_option(&str,(int *)&snd_port[nr_dev]) == 2 &&
+#ifdef SNDRV_STB
+	       get_option(&str,(int *)&snd_port_tc[nr_dev]) == 2 &&
+#endif
+	       get_option(&str,&snd_irq[nr_dev]) == 2 &&
+	       get_option(&str,&snd_dma1[nr_dev]) == 2 &&
+	       get_option(&str,&snd_dma2[nr_dev]) == 2 &&
+	       get_option(&str,&snd_joystick_dac[nr_dev]) == 2 &&
+	       get_option(&str,&snd_midi[nr_dev]) == 2 &&
+	       get_option(&str,&snd_pcm_channels[nr_dev]) == 2 &&
+	       get_option(&str,&snd_effect[nr_dev]) == 2);
+#ifdef __ISAPNP__
+	if (pnp != INT_MAX)
+		snd_isapnp[nr_dev] = pnp;
+#endif
+	nr_dev++;
+	return 1;
+}
+
+#ifndef SNDRV_STB
+__setup("snd-card-interwave=", alsa_card_interwave_setup);
+#else
+__setup("snd-card-interwave-stb=", alsa_card_interwave_setup);
+#endif
+
+#endif /* ifndef MODULE */
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/makefile.os2
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/makefile.os2	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/gus/makefile.os2	(revision 91)
@@ -0,0 +1,50 @@
+CINCLUDES = ..\..\..\include;..\..\include;$(%WATCOM)\H
+AINCLUDES = $(DDK_BASE)\H
+
+CDEFINES = -D__KERNEL__ -DMODULE -dTARGET_OS2 -dALSA_BUILD -D__ISAPNP__ -DCONFIG_ISAPNP -D__i386_
+
+32BIT=1
+!include ..\..\..\..\makefile.inc
+
+
+#===================================================================
+#
+#   List of source files
+#
+#===================================================================
+FILE1    = gus_main.obj gus_io.obj gus_irq.obj gus_timer.obj
+FILE2    = gus_mem.obj gus_mem_proc.obj gus_dram.obj gus_dma.obj gus_volume.obj
+FILE3    = gus_pcm.obj gus_mixer.obj gus_uart.obj gus_reset.obj 
+FILE4    = gus_synth.obj gus_sample.obj gus_simple.obj gus_instr.obj
+FILE5    = gusclassic.obj gusextreme.obj gusmax.obj interwave.obj interwave-stb.obj
+FILE6    = 
+FILE7    = 
+FILE8    = 
+FILELAST =
+FILES    = $(FILE0) $(FILE1) $(FILE2) $(FILE3) $(FILE4) $(FILE5) $(FILE6) $(FILE7) $(FILE8) $(FILE9) $(FILE10) $(FILE11) $(FILE12)
+
+TARGET   = gus
+
+LNKFILE  = $(OBJDIR)\$(TARGET).lnk
+
+
+#===================================================================
+#
+#   Specific dependencies
+#
+#===================================================================
+all: $(OBJDIR)\$(TARGET).lib 
+
+
+$(LNKFILE): makefile.os2
+    @%create $(LNKFILE)
+    @for %f in ($(FILES)) do @%append $(LNKFILE) -+$(OBJDIR)\%f
+
+$(OBJDIR)\$(TARGET).lib: $(LNKFILE) $(FILES)
+    $(LIB) $(OBJDIR)\$(TARGET).lib @$(LNKFILE)
+    copy $(OBJDIR)\$(TARGET).lib $(ALSA_LIB) 
+
+clean: .SYMBOLIC
+     cd $(OBJDIR)
+     -del *.obj *.err *.lnk *.sys *.sym *.lst *.map *.wmap *.lib
+     cd ..
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/makefile.os2
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/makefile.os2	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/makefile.os2	(revision 91)
@@ -0,0 +1,114 @@
+CINCLUDES = ..\..\include;..\include;$(%WATCOM)\H
+AINCLUDES = $(DDK_BASE)\H
+
+CDEFINES = -D__KERNEL__ -DMODULE -dTARGET_OS2 -dALSA_BUILD -D__ISAPNP__ -DCONFIG_ISAPNP -D__i386_
+
+32BIT=1
+!include ..\..\..\makefile.inc
+
+NMAKEOPTS=-f makefile.os2 $(LOGO)
+
+!if "$(DEBUG)" == "1"
+NMAKEOPTS += DEBUG=1
+!endif
+
+!if "$(KEE)" == "0"
+NMAKEOPTS += KEE=0
+KEE=0
+!else
+NMAKEOPTS += KEE=1
+KEE=1
+!endif
+
+!if "$(DEBUG)" == "1"
+!Message BUILDING DEBUG VERSION
+!else
+!Message BUILDING RELEASE VERSION
+!endif
+
+#===================================================================
+#
+#   List of source files
+#
+#===================================================================
+FILE1    = opl3sa2.obj
+#als100.obj azt2320.obj cmi8330.obj dt0197h.obj es18xx.obj
+FILE2    = 
+# sgalaxy.obj
+FILE3    = 
+FILE4    = 
+FILE5    = 
+FILE6    = 
+FILE7    = 
+FILE8    = 
+FILELAST =
+FILES    = $(FILE0) $(FILE1) $(FILE2) $(FILE3) $(FILE4) $(FILE5) $(FILE6) $(FILE7) $(FILE8) $(FILE9) $(FILE10) $(FILE11) $(FILE12)
+
+TARGET   = isa
+
+LNKFILE  = $(OBJDIR)\$(TARGET).lnk
+
+
+#===================================================================
+#
+#   Specific dependencies
+#
+#===================================================================
+all: $(OBJDIR)\$(TARGET).lib subdirs
+
+
+$(LNKFILE): makefile.os2
+    @%create $(LNKFILE)
+    @for %f in ($(FILES)) do @%append $(LNKFILE) -+$(OBJDIR)\%f
+
+$(OBJDIR)\$(TARGET).lib: $(LNKFILE) $(FILES)
+    $(LIB) $(OBJDIR)\$(TARGET).lib @$(LNKFILE)
+    copy $(OBJDIR)\$(TARGET).lib $(ALSA_LIB)
+
+
+subdirs: .SYMBOLIC
+#    @echo BUILDING ad1816a
+#    @cd ad1816a
+#    @wmake $(NMAKEOPTS)
+#    @cd ..
+#    @echo BUILDING ad1848
+#    @cd ad1848
+#    @wmake $(NMAKEOPTS)
+#    @cd ..
+#    @echo BUILDING cs423x
+#    @cd cs423x
+#    @wmake $(NMAKEOPTS)
+#    @cd ..
+#    @echo BUILDING es1688
+#    @cd es1688
+#    @wmake $(NMAKEOPTS)
+#    @cd ..
+#    @echo BUILDING gus
+#    @cd gus
+#    @wmake $(NMAKEOPTS)
+#    @cd ..
+#    @echo BUILDING opti9xx
+#    @cd opti9xx
+#    @wmake $(NMAKEOPTS)
+#    @cd ..
+    @echo BUILDING sb
+    @cd sb
+    @wmake $(NMAKEOPTS)
+    @cd ..
+
+
+##    @echo BUILDING wavefront
+##    @cd wavefront
+##    @nmake $(NMAKEOPTS)
+##    @cd ..
+    
+
+clean: .SYMBOLIC
+    @echo CLEANING sb
+    @cd sb
+    @wmake $(NMAKEOPTS) clean
+    @cd ..
+    -@rm  *.err 2>nul
+    @cd $(OBJDIR)
+    -rm *.obj *.lnk *.sys *.sym *.lst *.map *.wmap *.lib 2>nul
+    @cd ..
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/opl3sa2.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/opl3sa2.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/opl3sa2.c	(revision 91)
@@ -0,0 +1,817 @@
+/*
+ *  Driver for Yamaha OPL3-SA[2,3] soundcards
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#define SNDRV_MAIN_OBJECT_FILE
+#include <sound/driver.h>
+#include <sound/cs4231.h>
+#include <sound/mpu401.h>
+#include <sound/opl3.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+EXPORT_NO_SYMBOLS;
+MODULE_DESCRIPTION("Yamaha OPL3SA2+");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{Yamaha,YMF719E-S},"
+		"{Genius,Sound Maker 3DX},"
+		"{Yamaha,OPL3SA3},"
+		"{Intel,AL440LX sound},"
+	        "{NeoMagic,MagicWave 3DX}}");
+
+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;	/* Enable this card */
+#ifdef __ISAPNP__
+#ifdef TARGET_OS2
+static int snd_isapnp[SNDRV_CARDS] = {1,1,1,1,1,1,1,1};
+#else
+static int snd_isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
+#endif
+#endif
+static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* 0xf86,0x370,0x100 */
+static long snd_sb_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* 0x220,0x240,0x260 */
+static long snd_wss_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;/* 0x530,0xe80,0xf40,0x604 */
+static long snd_fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* 0x388 */
+static long snd_midi_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;/* 0x330,0x300 */
+static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* 0,1,3,5,9,11,12,15 */
+static int snd_dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* 1,3,5,6,7 */
+static int snd_dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* 1,3,5,6,7 */
+#ifdef TARGET_OS2
+static int snd_opl3sa3_ymode[SNDRV_CARDS] = { 0,0,0,0,0,0,0,0 };   /* 0,1,2,3 */ /*SL Added*/
+#else
+static int snd_opl3sa3_ymode[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = 0 };   /* 0,1,2,3 */ /*SL Added*/
+#endif
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for OPL3-SA soundcard.");
+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 OPL3-SA soundcard.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable OPL3-SA soundcard.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(snd_isapnp, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_isapnp, "ISA PnP detection for specified soundcard.");
+MODULE_PARM_SYNTAX(snd_isapnp, SNDRV_ISAPNP_DESC);
+MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_port, "Port # for OPL3-SA driver.");
+MODULE_PARM_SYNTAX(snd_port, SNDRV_ENABLED ",allows:{{0xf86},{0x370},{0x100}},dialog:list");
+MODULE_PARM(snd_sb_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_sb_port, "SB port # for OPL3-SA driver.");
+MODULE_PARM_SYNTAX(snd_sb_port, SNDRV_ENABLED ",allows:{{0x220},{0x240},{0x260}},dialog:list");
+MODULE_PARM(snd_wss_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_wss_port, "WSS port # for OPL3-SA driver.");
+MODULE_PARM_SYNTAX(snd_wss_port, SNDRV_ENABLED ",allows:{{0x530},{0xe80},{0xf40},{0x604}},dialog:list");
+MODULE_PARM(snd_fm_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_fm_port, "FM port # for OPL3-SA driver.");
+MODULE_PARM_SYNTAX(snd_fm_port, SNDRV_ENABLED ",allows:{{0x388}},dialog:list");
+MODULE_PARM(snd_midi_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_midi_port, "MIDI port # for OPL3-SA driver.");
+MODULE_PARM_SYNTAX(snd_midi_port, SNDRV_ENABLED ",allows:{{0x330},{0x300}},dialog:list");
+MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_irq, "IRQ # for OPL3-SA driver.");
+MODULE_PARM_SYNTAX(snd_irq, SNDRV_ENABLED ",allows:{{0},{1},{3},{5},{9},{11},{12},{15}},dialog:list");
+MODULE_PARM(snd_dma1, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_dma1, "DMA1 # for OPL3-SA driver.");
+MODULE_PARM_SYNTAX(snd_dma1, SNDRV_ENABLED ",allows:{{1},{3},{5},{6},{7}},dialog:list");
+MODULE_PARM(snd_dma2, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_dma2, "DMA2 # for OPL3-SA driver.");
+MODULE_PARM_SYNTAX(snd_dma2, SNDRV_ENABLED ",allows:{{1},{3},{5},{6},{7}},dialog:list");
+MODULE_PARM(snd_opl3sa3_ymode, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); /* SL Added */
+MODULE_PARM_DESC(snd_opl3sa3_ymode, "Speaker size selection for 3D Enhancement mode: Desktop/Large Notebook/Small Notebook/HiFi.");
+MODULE_PARM_SYNTAX(snd_opl3sa3_ymode, SNDRV_ENABLED ",allows:{{0,3}},dialog:list");  /* SL Added */
+
+struct snd_opl3sa {
+	snd_card_t *card;
+	int version;		/* 2 or 3 */
+	unsigned long port;	/* control port */
+	struct resource *res_port; /* control port resource */
+	int irq;
+	int single_dma;
+	spinlock_t reg_lock;
+	snd_hwdep_t *synth;
+	snd_rawmidi_t *rmidi;
+	cs4231_t *cs4231;
+#ifdef __ISAPNP__
+	struct isapnp_dev *dev;
+#endif
+	unsigned char ctlregs[0x20];
+	int ymode;		/* SL added */
+	snd_kcontrol_t *master_switch;
+	snd_kcontrol_t *master_volume;
+};
+
+static snd_card_t *snd_opl3sa_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+
+#ifdef __ISAPNP__
+
+static struct isapnp_card *snd_opl3sa2_isapnp_cards[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR;
+static const struct isapnp_card_id *snd_opl3sa2_isapnp_id[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR;
+
+#ifdef TARGET_OS2
+#define ISAPNP_OPL3SA2(_va, _vb, _vc, _device, _function) \
+        { \
+                0, ISAPNP_CARD_ID(_va, _vb, _vc, _device), \
+                { ISAPNP_DEVICE_ID(_va, _vb, _vc, _function), } \
+        }
+#else
+#define ISAPNP_OPL3SA2(_va, _vb, _vc, _device, _function) \
+        { \
+                ISAPNP_CARD_ID(_va, _vb, _vc, _device), \
+                devs : { ISAPNP_DEVICE_ID(_va, _vb, _vc, _function), } \
+        }
+#endif
+
+static struct isapnp_card_id snd_opl3sa2_pnpids[] __devinitdata = {
+	/* Yamaha YMF719E-S (Genius Sound Maker 3DX) */
+	ISAPNP_OPL3SA2('Y','M','H',0x0020,0x0021),
+	/* Yamaha OPL3-SA3 (integrated on Intel's Pentium II AL440LX motherboard) */
+	ISAPNP_OPL3SA2('Y','M','H',0x0030,0x0021),
+	/* ??? */
+	ISAPNP_OPL3SA2('Y','M','H',0x0800,0x0021),
+	/* NeoMagic MagicWave 3DX */
+	ISAPNP_OPL3SA2('N','M','X',0x2200,0x2210),
+	/* --- */
+	{ ISAPNP_CARD_END, }	/* end */
+};
+
+ISAPNP_CARD_TABLE(snd_opl3sa2_pnpids);
+
+#endif /* __ISAPNP__ */
+
+static unsigned char snd_opl3sa_read(unsigned long port, unsigned char reg)
+{
+	unsigned long flags;
+	unsigned char result;
+
+	save_flags(flags);
+	cli();
+#if 0
+	outb(0x1d, port);	/* password */
+	printk("read [0x%lx] = 0x%x\n", port, inb(port));
+#endif
+	outb(reg, port);	/* register */
+	result = inb(port + 1);
+	restore_flags(flags);
+#if 0
+	printk("read [0x%lx] = 0x%x [0x%x]\n", port, result, inb(port));
+#endif
+	return result;
+}
+
+static void snd_opl3sa_write(unsigned long port,
+			     unsigned char reg, unsigned char value)
+{
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+#if 0
+	outb(0x1d, port);	/* password */
+#endif
+	outb(reg, port);	/* register */
+	outb(value, port + 1);
+	restore_flags(flags);
+}
+
+static int __init snd_opl3sa_detect(struct snd_opl3sa *oplcard)
+{
+	snd_card_t *card;
+	unsigned long port;
+	unsigned char tmp, tmp1;
+	char str[2];
+
+	card = oplcard->card;
+	port = oplcard->port;
+	if ((oplcard->res_port = request_region(port, 2, "OPL3-SA control")) == NULL)
+		return -EBUSY;
+	// snd_printk("REG 0A = 0x%x\n", snd_opl3sa_read(port, 0x0a));
+	oplcard->version = 0;
+	tmp = snd_opl3sa_read(port, 0x0a);
+	if (tmp == 0xff) {
+		snd_printd("OPL3-SA [0x%lx] detect = 0x%x\n", port, tmp);
+		return -ENODEV;
+	}
+	switch (tmp & 0x07) {
+	case 0x01:
+		oplcard->version = 2;
+		break;
+	default:
+		oplcard->version = 3;
+		/* 0x02 - standard */
+		/* 0x03 - YM715B */
+		/* 0x04 - YM719 - OPL-SA4? */
+		/* 0x05 - OPL3-SA3 - Libretto 100 */
+		break;
+	}
+	str[0] = oplcard->version + '0';
+	str[1] = 0;
+	strcat(card->shortname, str);
+	snd_opl3sa_write(port, 0x0a, tmp ^ 7);
+	if ((tmp1 = snd_opl3sa_read(port, 0x0a)) != tmp) {
+		snd_printd("OPL3-SA [0x%lx] detect (1) = 0x%x (0x%x)\n", port, tmp, tmp1);
+		return -ENODEV;
+	}
+	/* try if the MIC register is accesible */
+	tmp = snd_opl3sa_read(port, 0x09);
+	snd_opl3sa_write(port, 0x09, 0x8a);
+	if (((tmp1 = snd_opl3sa_read(port, 0x09)) & 0x9f) != 0x8a) {
+		snd_printd("OPL3-SA [0x%lx] detect (2) = 0x%x (0x%x)\n", port, tmp, tmp1);
+		return -ENODEV;
+	}
+	snd_opl3sa_write(port, 0x09, 0x9f);
+	/* initialization */
+	snd_opl3sa_write(port, 0x01, 0x00);	/* Power Management - default */
+	if (oplcard->version > 2) {   /* SL Added */
+		snd_opl3sa_write(port, 0x02, (oplcard->ymode << 4)); /* SL Modified - System Control - ymode is bits 4&5 (of 0 to 7) on all but opl3sa2 versions */
+	} else { /* SL Added */
+		snd_opl3sa_write(port, 0x02, 0x00);	/* SL Modified - System Control - default for opl3sa2 versions */
+	} /* SL Added */
+	snd_opl3sa_write(port, 0x03, 0x0d);	/* Interrupt Channel Configuration - IRQ A = OPL3 + MPU + WSS */
+	if (oplcard->single_dma) {
+		snd_opl3sa_write(port, 0x06, 0x03);	/* DMA Configuration - DMA A = WSS-R + WSS-P */
+	} else {
+		snd_opl3sa_write(port, 0x06, 0x21);	/* DMA Configuration - DMA B = WSS-R, DMA A = WSS-P */
+	}
+	snd_opl3sa_write(port, 0x0a, 0x80 | (tmp & 7));	/* Miscellaneous - default */
+	if (oplcard->version > 2) {
+		snd_opl3sa_write(port, 0x12, 0x00);	/* Digital Block Partial Power Down - default */
+		snd_opl3sa_write(port, 0x13, 0x00);	/* Analog Block Partial Power Down - default */
+	}
+	return 0;
+}
+
+static void snd_opl3sa_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	unsigned short status;
+	struct snd_opl3sa *oplcard = (struct snd_opl3sa *) dev_id;
+
+	if (oplcard == NULL || oplcard->card == NULL)
+		return;
+
+	spin_lock(&oplcard->reg_lock);
+	outb(0x04, oplcard->port);	/* register - Interrupt IRQ-A status */
+	status = inb(oplcard->port + 1);
+	spin_unlock(&oplcard->reg_lock);
+
+	if (status & 0x20)
+		snd_opl3_interrupt(oplcard->synth);
+
+	if ((status & 0x10) && oplcard->rmidi != NULL)
+		snd_mpu401_uart_interrupt(irq, oplcard->rmidi->private_data, regs);
+
+	if (status & 0x07)	/* TI,CI,PI */
+		snd_cs4231_interrupt(irq, oplcard->cs4231, regs);
+
+	if (status & 0x40) {
+		/* reading from Master Lch register at 0x07 clears this bit */
+		snd_opl3sa_read(oplcard->port, 0x08);
+		snd_opl3sa_read(oplcard->port, 0x07);
+		snd_ctl_notify(oplcard->card, SNDRV_CTL_EVENT_MASK_VALUE, &oplcard->master_switch->id);
+		snd_ctl_notify(oplcard->card, SNDRV_CTL_EVENT_MASK_VALUE, &oplcard->master_volume->id);
+	}
+}
+
+#ifdef TARGET_OS2
+#define OPL3SA_SINGLE(xname, xindex, reg, shift, mask, invert) \
+{ SNDRV_CTL_ELEM_IFACE_MIXER, 0, 0, xname, xindex, \
+  0, 0, snd_opl3sa_info_single, \
+  snd_opl3sa_get_single, snd_opl3sa_put_single, \
+  reg | (shift << 8) | (mask << 16) | (invert << 24) }
+
+#else
+#define OPL3SA_SINGLE(xname, xindex, reg, shift, mask, invert) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
+  info: snd_opl3sa_info_single, \
+  get: snd_opl3sa_get_single, put: snd_opl3sa_put_single, \
+  private_value: reg | (shift << 8) | (mask << 16) | (invert << 24) }
+#endif
+
+static int snd_opl3sa_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+
+	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	return 0;
+}
+
+int snd_opl3sa_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	struct snd_opl3sa *oplcard = (struct snd_opl3sa *)_snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 8) & 0xff;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0xff;
+
+	spin_lock_irqsave(&oplcard->reg_lock, flags);
+	ucontrol->value.integer.value[0] = (oplcard->ctlregs[reg] >> shift) & mask;
+	spin_unlock_irqrestore(&oplcard->reg_lock, flags);
+	if (invert)
+		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+	return 0;
+}
+
+int snd_opl3sa_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	struct snd_opl3sa *oplcard = (struct snd_opl3sa *)_snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 8) & 0xff;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0xff;
+	int change;
+	unsigned short val, oval;
+	
+	val = (ucontrol->value.integer.value[0] & mask);
+	if (invert)
+		val = mask - val;
+	val <<= shift;
+	spin_lock_irqsave(&oplcard->reg_lock, flags);
+	oval = oplcard->ctlregs[reg];
+	val = (oval & ~(mask << shift)) | val;
+	change = val != oval;
+	snd_opl3sa_write(oplcard->port, reg, oplcard->ctlregs[reg] = val);
+	spin_unlock_irqrestore(&oplcard->reg_lock, flags);
+	return change;
+}
+
+#ifdef TARGET_OS2
+#define OPL3SA_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \
+{ SNDRV_CTL_ELEM_IFACE_MIXER, 0, 0, xname, xindex, \
+  0, 0, snd_opl3sa_info_double, \
+  snd_opl3sa_get_double, snd_opl3sa_put_double, \
+  left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
+
+#else
+#define OPL3SA_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
+  info: snd_opl3sa_info_double, \
+  get: snd_opl3sa_get_double, put: snd_opl3sa_put_double, \
+  private_value: left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
+#endif
+
+int snd_opl3sa_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+
+	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	return 0;
+}
+
+int snd_opl3sa_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	struct snd_opl3sa *oplcard = (struct snd_opl3sa *)_snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int left_reg = kcontrol->private_value & 0xff;
+	int right_reg = (kcontrol->private_value >> 8) & 0xff;
+	int shift_left = (kcontrol->private_value >> 16) & 0x07;
+	int shift_right = (kcontrol->private_value >> 19) & 0x07;
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+	int invert = (kcontrol->private_value >> 22) & 1;
+	
+	spin_lock_irqsave(&oplcard->reg_lock, flags);
+	ucontrol->value.integer.value[0] = (oplcard->ctlregs[left_reg] >> shift_left) & mask;
+	ucontrol->value.integer.value[1] = (oplcard->ctlregs[right_reg] >> shift_right) & mask;
+	spin_unlock_irqrestore(&oplcard->reg_lock, flags);
+	if (invert) {
+		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+		ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1];
+	}
+	return 0;
+}
+
+int snd_opl3sa_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	struct snd_opl3sa *oplcard = (struct snd_opl3sa *)_snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int left_reg = kcontrol->private_value & 0xff;
+	int right_reg = (kcontrol->private_value >> 8) & 0xff;
+	int shift_left = (kcontrol->private_value >> 16) & 0x07;
+	int shift_right = (kcontrol->private_value >> 19) & 0x07;
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+	int invert = (kcontrol->private_value >> 22) & 1;
+	int change;
+	unsigned short val1, val2, oval1, oval2;
+	
+	val1 = ucontrol->value.integer.value[0] & mask;
+	val2 = ucontrol->value.integer.value[1] & mask;
+	if (invert) {
+		val1 = mask - val1;
+		val2 = mask - val2;
+	}
+	val1 <<= shift_left;
+	val2 <<= shift_right;
+	spin_lock_irqsave(&oplcard->reg_lock, flags);
+	if (left_reg != right_reg) {
+		oval1 = oplcard->ctlregs[left_reg];
+		oval2 = oplcard->ctlregs[right_reg];
+		val1 = (oval1 & ~(mask << shift_left)) | val1;
+		val2 = (oval2 & ~(mask << shift_right)) | val2;
+		change = val1 != oval1 || val2 != oval2;
+		snd_opl3sa_write(oplcard->port, left_reg, oplcard->ctlregs[left_reg] = val1);
+		snd_opl3sa_write(oplcard->port, right_reg, oplcard->ctlregs[right_reg] = val2);
+	} else {
+		oval1 = oplcard->ctlregs[left_reg];
+		val1 = (oval1 & ~((mask << shift_left) | (mask << shift_right))) | val1 | val2;
+		change = val1 != oval1;
+		snd_opl3sa_write(oplcard->port, left_reg, oplcard->ctlregs[left_reg] = val1);
+	}
+	spin_unlock_irqrestore(&oplcard->reg_lock, flags);
+	return change;
+}
+
+#define OPL3SA_CONTROLS (sizeof(snd_opl3sa_controls)/sizeof(snd_kcontrol_new_t))
+
+static snd_kcontrol_new_t snd_opl3sa_controls[] = {
+OPL3SA_DOUBLE("Master Playback Switch", 0, 0x07, 0x08, 7, 7, 1, 1),
+OPL3SA_DOUBLE("Master Playback Volume", 0, 0x07, 0x08, 0, 0, 15, 1),
+OPL3SA_SINGLE("Mic Playback Switch", 0, 0x09, 7, 1, 1),
+OPL3SA_SINGLE("Mic Playback Volume", 0, 0x09, 0, 31, 1)
+};
+
+#define OPL3SA_TONE_CONTROLS (sizeof(snd_opl3sa_tone_controls)/sizeof(snd_kcontrol_new_t))
+
+static snd_kcontrol_new_t snd_opl3sa_tone_controls[] = {
+OPL3SA_DOUBLE("3D Control - Wide", 0, 0x14, 0x14, 4, 0, 7, 0),
+OPL3SA_DOUBLE("Tone Control - Bass", 0, 0x15, 0x15, 4, 0, 7, 0),
+OPL3SA_DOUBLE("Tone Control - Treble", 0, 0x16, 0x16, 4, 0, 7, 0)
+};
+
+static void snd_opl3sa_master_free(snd_kcontrol_t *kcontrol)
+{
+	struct snd_opl3sa *oplcard = (struct snd_opl3sa *)_snd_kcontrol_chip(kcontrol);
+	
+	oplcard->master_switch = NULL;
+	oplcard->master_volume = NULL;
+}
+
+static int __init snd_opl3sa_mixer(struct snd_opl3sa *oplcard)
+{
+	snd_card_t *card = oplcard->card;
+	snd_ctl_elem_id_t id1, id2;
+	snd_kcontrol_t *kctl;
+	int idx, err;
+
+	memset(&id1, 0, sizeof(id1));
+	memset(&id2, 0, sizeof(id2));
+	id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+	/* reassign AUX0 to CD */
+        strcpy(id1.name, "Aux Playback Switch");
+        strcpy(id2.name, "CD Playback Switch");
+        if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
+                return err;
+        strcpy(id1.name, "Aux Playback Volume");
+        strcpy(id2.name, "CD Playback Volume");
+        if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
+                return err;
+	/* reassign AUX1 to FM */
+        strcpy(id1.name, "Aux Playback Switch"); id1.index = 1;
+        strcpy(id2.name, "FM Playback Switch");
+        if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
+                return err;
+        strcpy(id1.name, "Aux Playback Volume");
+        strcpy(id2.name, "FM Playback Volume");
+        if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
+                return err;
+	/* add OPL3SA2 controls */
+	for (idx = 0; idx < OPL3SA_CONTROLS; idx++) {
+		if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_opl3sa_controls[idx], oplcard))) < 0)
+			return err;
+		switch (idx) {
+		case 0: oplcard->master_switch = kctl; kctl->private_free = snd_opl3sa_master_free; break;
+		case 1: oplcard->master_volume = kctl; kctl->private_free = snd_opl3sa_master_free; break;
+		}
+	}
+	if (oplcard->version > 2) {
+		for (idx = 0; idx < OPL3SA_TONE_CONTROLS; idx++)
+			if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_opl3sa_tone_controls[idx], oplcard))) < 0)
+				return err;
+	}
+	return 0;
+}
+
+#ifdef __ISAPNP__
+static int __init snd_opl3sa_isapnp(int dev, struct snd_opl3sa *oplcard)
+{
+        const struct isapnp_card_id *id = snd_opl3sa2_isapnp_id[dev];
+        struct isapnp_card *card = snd_opl3sa2_isapnp_cards[dev];
+	struct isapnp_dev *pdev;
+
+	oplcard->dev = isapnp_find_dev(card, id->devs[0].vendor, id->devs[0].function, NULL);
+	if (oplcard->dev->active) {
+		oplcard->dev = NULL;
+		return -EBUSY;
+	}
+	/* PnP initialization */
+	pdev = oplcard->dev;
+	if (pdev->prepare(pdev)<0)
+		return -EAGAIN;
+	if (snd_sb_port[dev] != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[0], snd_sb_port[dev], 16);
+	if (snd_wss_port[dev] != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[1], snd_wss_port[dev], 8);
+	if (snd_fm_port[dev] != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[2], snd_fm_port[dev], 4);
+	if (snd_midi_port[dev] != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[3], snd_midi_port[dev], 2);
+	if (snd_port[dev] != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[4], snd_port[dev], 2);
+	if (snd_dma1[dev] != SNDRV_AUTO_DMA)
+		isapnp_resource_change(&pdev->dma_resource[0], snd_dma1[dev], 1);
+	if (snd_dma2[dev] != SNDRV_AUTO_DMA)
+		isapnp_resource_change(&pdev->dma_resource[1], snd_dma2[dev], 1);
+	if (snd_irq[dev] != SNDRV_AUTO_IRQ)
+		isapnp_resource_change(&pdev->irq_resource[0], snd_irq[dev], 1);
+	if (pdev->activate(pdev)<0) {
+		snd_printk("isapnp configure failure (out of resources?)\n");
+		return -EBUSY;
+	}
+	snd_sb_port[dev] = pdev->resource[0].start;
+	snd_wss_port[dev] = pdev->resource[1].start;
+	snd_fm_port[dev] = pdev->resource[2].start;
+	snd_midi_port[dev] = pdev->resource[3].start;
+	snd_port[dev] = pdev->resource[4].start;
+	snd_dma1[dev] = pdev->dma_resource[0].start;
+	snd_dma2[dev] = pdev->dma_resource[1].start;
+	snd_irq[dev] = pdev->irq_resource[0].start;
+	snd_printdd("isapnp OPL3-SA: sb port=0x%lx, wss port=0x%lx, fm port=0x%lx, midi port=0x%lx\n",
+		snd_sb_port[dev], snd_wss_port[dev], snd_fm_port[dev], snd_midi_port[dev]);
+	snd_printdd("isapnp OPL3-SA: control port=0x%lx, dma1=%i, dma2=%i, irq=%i\n",
+		snd_port[dev], snd_dma1[dev], snd_dma2[dev], snd_irq[dev]);
+	return 0;
+}
+
+static void snd_opl3sa_deactivate(struct snd_opl3sa *oplcard)
+{
+	if (oplcard->dev) {
+		oplcard->dev->deactivate(oplcard->dev);
+		oplcard->dev = NULL;
+	}
+}
+#endif /* __ISAPNP__ */
+
+static void snd_card_opl3sa2_free(snd_card_t *card)
+{
+	struct snd_opl3sa *acard = (struct snd_opl3sa *)card->private_data;
+
+	if (acard) {
+#ifdef __ISAPNP__
+		snd_opl3sa_deactivate(acard);
+#endif
+		if (acard->irq >= 0)
+			free_irq(acard->irq, (void *)acard);
+		if (acard->res_port)
+			release_resource(acard->res_port);
+	}
+}
+
+static int __init snd_opl3sa_probe(int dev)
+{
+	int irq, dma1, dma2;
+	snd_card_t *card;
+	struct snd_opl3sa *oplcard;
+	cs4231_t *cs4231;
+	opl3_t *opl3;
+	int err;
+
+#ifdef __ISAPNP__
+	if (!snd_isapnp[dev]) {
+#endif
+		if (snd_port[dev] == SNDRV_AUTO_PORT) {
+			snd_printk("specify snd_port\n");
+			return -EINVAL;
+		}
+		if (snd_wss_port[dev] == SNDRV_AUTO_PORT) {
+			snd_printk("specify snd_wss_port\n");
+			return -EINVAL;
+		}
+		if (snd_fm_port[dev] == SNDRV_AUTO_PORT) {
+			snd_printk("specify snd_fm_port\n");
+			return -EINVAL;
+		}
+		if (snd_midi_port[dev] == SNDRV_AUTO_PORT) {
+			snd_printk("specify snd_midi_port\n");
+			return -EINVAL;
+		}
+#ifdef __ISAPNP__
+	}
+#endif
+	card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE,
+			    sizeof(struct snd_opl3sa));
+	if (card == NULL)
+		return -ENOMEM;
+	oplcard = (struct snd_opl3sa *)card->private_data;
+	oplcard->irq = -1;
+	card->private_free = snd_card_opl3sa2_free;
+	strcpy(card->driver, "OPL3SA2");
+	strcpy(card->shortname, "Yamaha OPL3-SA2");
+#ifdef __ISAPNP__
+	if (snd_isapnp[dev] && snd_opl3sa_isapnp(dev, oplcard) < 0) {
+		snd_card_free(card);
+		return -EBUSY;
+	}
+#endif
+	oplcard->ymode = snd_opl3sa3_ymode[dev] & 0x03 ; /* SL Added - initialise this card from supplied (or default) parameter*/ 
+	oplcard->card = card;
+	oplcard->port = snd_port[dev];
+	irq = snd_irq[dev];
+	dma1 = snd_dma1[dev];
+	dma2 = snd_dma2[dev];
+	if (dma2 < 0)
+		oplcard->single_dma = 1;
+	if ((snd_opl3sa_detect(oplcard)) < 0) {
+		snd_card_free(card);
+		return -ENODEV;
+	}
+	if (request_irq(irq, snd_opl3sa_interrupt, SA_INTERRUPT, "OPL3-SA2/3", (void *)oplcard)) {
+		snd_card_free(card);
+		return -ENODEV;
+	}
+	oplcard->irq = irq;
+	if ((err = snd_cs4231_create(card,
+				     snd_wss_port[dev] + 4, -1,
+				     irq, dma1, dma2,
+				     CS4231_HW_OPL3SA2,
+				     CS4231_HWSHARE_IRQ,
+				     &cs4231)) < 0) {
+		snd_printd("Oops, WSS not detected at 0x%lx\n", snd_wss_port[dev] + 4);
+		snd_card_free(card);
+		return err;
+	}
+	oplcard->cs4231 = cs4231;
+	if ((err = snd_cs4231_pcm(cs4231, 0, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_cs4231_mixer(cs4231)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_opl3sa_mixer(oplcard)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_cs4231_timer(cs4231, 0, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if (snd_fm_port[dev] >= 0x340 && snd_fm_port[dev] < 0x400) {
+		if ((err = snd_opl3_create(card, snd_fm_port[dev],
+					   snd_fm_port[dev] + 2,
+					   OPL3_HW_OPL3, 0, &opl3)) < 0) {
+			snd_card_free(card);
+			return err;
+		}
+		if ((err = snd_opl3_timer_new(opl3, 1, 2)) < 0) {
+			snd_card_free(card);
+			return err;
+		}
+		if ((err = snd_opl3_hwdep_new(opl3, 0, 1, &oplcard->synth)) < 0) {
+			snd_card_free(card);
+			return err;
+		}
+	}
+	if (snd_midi_port[dev] >= 0x300 && snd_midi_port[dev] < 0x340) {
+		if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_OPL3SA2,
+					       snd_midi_port[dev], 0,
+					       irq, 0, &oplcard->rmidi)) < 0) {
+			snd_card_free(card);
+			return err;
+		}
+	}
+	sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d",
+		card->shortname, oplcard->port, irq, dma1);
+	if (dma2 >= 0)
+		sprintf(card->longname + strlen(card->longname), "&%d", dma2);
+
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	snd_opl3sa_cards[dev] = card;
+	return 0;
+}
+
+#ifdef __ISAPNP__
+static int __init snd_opl3sa2_isapnp_detect(struct isapnp_card *card,
+					    const struct isapnp_card_id *id)
+{
+        static int dev = 0;
+        int res;
+
+        for ( ; dev < SNDRV_CARDS; dev++) {
+                if (!snd_enable[dev])
+                        continue;
+                snd_opl3sa2_isapnp_cards[dev] = card;
+                snd_opl3sa2_isapnp_id[dev] = id;
+                res = snd_opl3sa_probe(dev);
+                if (res < 0)
+                        return res;
+                dev++;
+                return 0;
+        }
+        return -ENODEV;
+}
+#endif /* __ISAPNP__ */
+
+static int __init alsa_card_opl3sa2_init(void)
+{
+	int dev, cards = 0;
+
+	for (dev = 0; dev < SNDRV_CARDS; dev++) {
+		if (!snd_enable[dev])
+			continue;
+#ifdef __ISAPNP__
+		if (snd_isapnp[dev])
+			continue;
+#endif
+		if (snd_opl3sa_probe(dev) >= 0)
+			cards++;
+	}
+#ifdef __ISAPNP__
+	cards += isapnp_probe_cards(snd_opl3sa2_pnpids, snd_opl3sa2_isapnp_detect);
+#endif
+	if (!cards) {
+#ifdef MODULE
+		snd_printk("Yamaha OPL3-SA soundcard not found or device busy\n");
+#endif
+		return -ENODEV;
+	}
+	return 0;
+}
+
+static void __exit alsa_card_opl3sa2_exit(void)
+{
+	int idx;
+
+	for (idx = 0; idx < SNDRV_CARDS; idx++)
+		snd_card_free(snd_opl3sa_cards[idx]);
+}
+
+module_init(alsa_card_opl3sa2_init)
+module_exit(alsa_card_opl3sa2_exit)
+
+#ifndef MODULE
+
+/* format is: snd-card-opl3sa2=snd_enable,snd_index,snd_id,snd_isapnp,
+			       snd_port,snd_sb_port,snd_wss_port,snd_fm_port,
+			       snd_midi_port,snd_irq,snd_dma1,snd_dma2,
+			       snd_opl3sa3_ymode */
+
+static int __init alsa_card_opl3sa2_setup(char *str)
+{
+	static unsigned __initdata nr_dev = 0;
+	int __attribute__ ((__unused__)) pnp = INT_MAX;
+
+	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,&pnp) == 2 &&
+	       get_option(&str,(int *)&snd_port[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_sb_port[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_wss_port[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_fm_port[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_midi_port[nr_dev]) == 2 &&
+	       get_option(&str,&snd_irq[nr_dev]) == 2 &&
+	       get_option(&str,&snd_dma1[nr_dev]) == 2 &&
+	       get_option(&str,&snd_dma2[nr_dev]) == 2 &&
+	       get_option(&str,&snd_opl3sa3_ymode[nr_dev]) == 2);
+#ifdef __ISAPNP__
+	if (pnp != INT_MAX)
+		snd_isapnp[nr_dev] = pnp;
+#endif
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-card-opl3sa2=", alsa_card_opl3sa2_setup);
+
+#endif /* ifndef MODULE */
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/opti9xx/makefile.os2
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/opti9xx/makefile.os2	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/opti9xx/makefile.os2	(revision 91)
@@ -0,0 +1,50 @@
+CINCLUDES = ..\..\..\include;..\..\include;$(%WATCOM)\H
+AINCLUDES = $(DDK_BASE)\H
+
+CDEFINES = -D__KERNEL__ -DMODULE -dTARGET_OS2 -dALSA_BUILD -D__ISAPNP__ -DCONFIG_ISAPNP -D__i386_
+
+32BIT=1
+!include ..\..\..\..\makefile.inc
+
+
+#===================================================================
+#
+#   List of source files
+#
+#===================================================================
+FILE1    = opti92x-ad1848.obj opti92x-cs4231.obj opti93x.obj
+FILE2    = 
+FILE3    = 
+FILE4    = 
+FILE5    = 
+FILE6    = 
+FILE7    = 
+FILE8    = 
+FILELAST =
+FILES    = $(FILE0) $(FILE1) $(FILE2) $(FILE3) $(FILE4) $(FILE5) $(FILE6) $(FILE7) $(FILE8) $(FILE9) $(FILE10) $(FILE11) $(FILE12)
+
+TARGET   = opti9xx
+
+LNKFILE  = $(OBJDIR)\$(TARGET).lnk
+
+
+#===================================================================
+#
+#   Specific dependencies
+#
+#===================================================================
+all: $(OBJDIR)\$(TARGET).lib 
+
+
+$(LNKFILE): makefile.os2
+    @%create $(LNKFILE)
+    @for %f in ($(FILES)) do @%append $(LNKFILE) -+$(OBJDIR)\%f
+
+$(OBJDIR)\$(TARGET).lib: $(LNKFILE) $(FILES)
+    $(LIB) $(OBJDIR)\$(TARGET).lib @$(LNKFILE)
+    copy $(OBJDIR)\$(TARGET).lib $(ALSA_LIB) 
+
+clean: .SYMBOLIC
+     cd $(OBJDIR)
+     -del *.obj *.err *.lnk *.sys *.sym *.lst *.map *.wmap *.lib
+     cd ..
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/opti9xx/opti92x-ad1848.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/opti9xx/opti92x-ad1848.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/opti9xx/opti92x-ad1848.c	(revision 91)
@@ -0,0 +1,2312 @@
+/*
+    card-opti92x-ad1848.c - driver for OPTi 82c92x based soundcards.
+    Copyright (C) 1998-2000 by Massimo Piccioni <dafastidio@libero.it>
+
+    Part of this code was developed at the Italian Ministry of Air Defence,
+    Sixth Division (oh, che pace ...), Rome.
+
+    Thanks to Maria Grazia Pollarini, Salvatore Vassallo.
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#define SNDRV_MAIN_OBJECT_FILE
+#include <sound/driver.h>
+#ifdef CS4231
+#include <sound/cs4231.h>
+#else
+#ifndef OPTi93X
+#include <sound/ad1848.h>
+#else
+#include <sound/control.h>
+#include <sound/pcm.h>
+#endif	/* OPTi93X */
+#endif	/* CS4231 */
+#include <sound/mpu401.h>
+#include <sound/opl3.h>
+#define SNDRV_LEGACY_FIND_FREE_IOPORT
+#define SNDRV_LEGACY_FIND_FREE_IRQ
+#define SNDRV_LEGACY_FIND_FREE_DMA
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+EXPORT_NO_SYMBOLS;
+#ifdef OPTi93X
+MODULE_DESCRIPTION("OPTi93X");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{OPTi,82C931/3}}");
+#else	/* OPTi93X */
+#ifdef CS4231
+MODULE_DESCRIPTION("OPTi92X - CS4231");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{OPTi,82C924 (CS4231)},"
+		"{OPTi,82C925 (CS4231)}}");
+#else	/* CS4231 */
+MODULE_DESCRIPTION("OPTi92X - AD1848");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{OPTi,82C924 (AD1848)},"
+		"{OPTi,82C925 (AD1848)}}");
+#endif	/* CS4231 */
+#endif	/* OPTi93X */
+
+static int snd_index = SNDRV_DEFAULT_IDX1;	/* Index 0-MAX */
+static char *snd_id = SNDRV_DEFAULT_STR1;		/* ID for this card */
+//static int snd_enable = SNDRV_DEFAULT_ENABLE1;	/* Enable this card */
+static int snd_isapnp = 1;			/* Enable ISA PnP detection */
+static long snd_port = SNDRV_DEFAULT_PORT1; 	/* 0x530,0xe80,0xf40,0x604 */
+static long snd_mpu_port = SNDRV_DEFAULT_PORT1;	/* 0x300,0x310,0x320,0x330 */
+static long snd_fm_port = SNDRV_DEFAULT_PORT1;	/* 0x388 */
+static int snd_irq = SNDRV_DEFAULT_IRQ1;		/* 5,7,9,10,11 */
+static int snd_mpu_irq = SNDRV_DEFAULT_IRQ1;	/* 5,7,9,10 */
+static int snd_dma1 = SNDRV_DEFAULT_DMA1;		/* 0,1,3 */
+#if defined(CS4231) || defined(OPTi93X)
+static int snd_dma2 = SNDRV_DEFAULT_DMA1;		/* 0,1,3 */
+#endif	/* CS4231 || OPTi93X */
+
+MODULE_PARM(snd_index, "i");
+MODULE_PARM_DESC(snd_index, "Index value for opti9xx based soundcard.");
+MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC);
+MODULE_PARM(snd_id, "s");
+MODULE_PARM_DESC(snd_id, "ID string for opti9xx based soundcard.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+//MODULE_PARM(snd_enable, "i");
+//MODULE_PARM_DESC(snd_enable, "Enable opti9xx soundcard.");
+//MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(snd_isapnp, "i");
+MODULE_PARM_DESC(snd_isapnp, "Enable ISA PnP detection for specified soundcard.");
+MODULE_PARM_SYNTAX(snd_isapnp, SNDRV_ISAPNP_DESC);
+MODULE_PARM(snd_port, "l");
+MODULE_PARM_DESC(snd_port, "WSS port # for opti9xx driver.");
+MODULE_PARM_SYNTAX(snd_port, SNDRV_PORT_DESC);
+MODULE_PARM(snd_mpu_port, "l");
+MODULE_PARM_DESC(snd_mpu_port, "MPU-401 port # for opti9xx driver.");
+MODULE_PARM_SYNTAX(snd_mpu_port, SNDRV_PORT_DESC);
+MODULE_PARM(snd_fm_port, "l");
+MODULE_PARM_DESC(snd_fm_port, "FM port # for opti9xx driver.");
+MODULE_PARM_SYNTAX(snd_fm_port, SNDRV_PORT_DESC);
+MODULE_PARM(snd_irq, "i");
+MODULE_PARM_DESC(snd_irq, "WSS irq # for opti9xx driver.");
+MODULE_PARM_SYNTAX(snd_irq, SNDRV_IRQ_DESC);
+MODULE_PARM(snd_mpu_irq, "i");
+MODULE_PARM_DESC(snd_mpu_irq, "MPU-401 irq # for opti9xx driver.");
+MODULE_PARM_SYNTAX(snd_mpu_irq, SNDRV_IRQ_DESC);
+MODULE_PARM(snd_dma1, "i");
+MODULE_PARM_DESC(snd_dma1, "1st dma # for opti9xx driver.");
+MODULE_PARM_SYNTAX(snd_dma1, SNDRV_DMA_DESC);
+#if defined(CS4231) || defined(OPTi93X)
+MODULE_PARM(snd_dma2, "i");
+MODULE_PARM_DESC(snd_dma2, "2nd dma # for opti9xx driver.");
+MODULE_PARM_SYNTAX(snd_dma2, SNDRV_DMA_DESC);
+#endif	/* CS4231 || OPTi93X */
+
+#define OPTi9XX_HW_DETECT	0
+#define OPTi9XX_HW_82C928	1
+#define OPTi9XX_HW_82C929	2
+#define OPTi9XX_HW_82C924	3
+#define OPTi9XX_HW_82C925	4
+#define OPTi9XX_HW_82C930	5
+#define OPTi9XX_HW_82C931	6
+#define OPTi9XX_HW_82C933	7
+#define OPTi9XX_HW_LAST		OPTi9XX_HW_82C933
+
+#define OPTi9XX_MC_REG(n)	n
+
+typedef struct _snd_opti9xx opti9xx_t;
+
+#ifdef OPTi93X
+
+#define OPTi93X_INDEX			0x00
+#define OPTi93X_DATA			0x01
+#define OPTi93X_STATUS			0x02
+#define OPTi93X_DDATA			0x03
+#define OPTi93X_PORT(chip, r)		((chip)->port + OPTi93X_##r)
+
+#define OPTi93X_MIXOUT_LEFT		0x00
+#define OPTi93X_MIXOUT_RIGHT		0x01
+#define OPTi93X_CD_LEFT_INPUT		0x02
+#define OPTi93X_CD_RIGHT_INPUT		0x03
+#define OPTi930_AUX_LEFT_INPUT		0x04
+#define OPTi930_AUX_RIGHT_INPUT		0x05
+#define OPTi931_FM_LEFT_INPUT		0x04
+#define OPTi931_FM_RIGHT_INPUT		0x05
+#define OPTi93X_DAC_LEFT		0x06
+#define OPTi93X_DAC_RIGHT		0x07
+#define OPTi93X_PLAY_FORMAT		0x08
+#define OPTi93X_IFACE_CONF		0x09
+#define OPTi93X_PIN_CTRL		0x0a
+#define OPTi93X_ERR_INIT		0x0b
+#define OPTi93X_ID			0x0c
+#define OPTi93X_PLAY_UPR_CNT		0x0e
+#define OPTi93X_PLAY_LWR_CNT		0x0f
+#define OPTi931_AUX_LEFT_INPUT		0x10
+#define OPTi931_AUX_RIGHT_INPUT		0x11
+#define OPTi93X_LINE_LEFT_INPUT		0x12
+#define OPTi93X_LINE_RIGHT_INPUT	0x13
+#define OPTi93X_MIC_LEFT_INPUT		0x14
+#define OPTi93X_MIC_RIGHT_INPUT		0x15
+#define OPTi93X_OUT_LEFT		0x16
+#define OPTi93X_OUT_RIGHT		0x17
+#define OPTi93X_CAPT_FORMAT		0x1c
+#define OPTi93X_CAPT_UPR_CNT		0x1e
+#define OPTi93X_CAPT_LWR_CNT		0x1f
+
+#define OPTi93X_TRD			0x20
+#define OPTi93X_MCE			0x40
+#define OPTi93X_INIT			0x80
+
+#define OPTi93X_MIXOUT_MIC_GAIN		0x20
+#define OPTi93X_MIXOUT_LINE		0x00
+#define OPTi93X_MIXOUT_CD		0x40
+#define OPTi93X_MIXOUT_MIC		0x80
+#define OPTi93X_MIXOUT_MIXER		0xc0
+
+#define OPTi93X_STEREO			0x10
+#define OPTi93X_LINEAR_8		0x00
+#define OPTi93X_ULAW_8			0x20
+#define OPTi93X_LINEAR_16_LIT		0x40
+#define OPTi93X_ALAW_8			0x60
+#define OPTi93X_ADPCM_16		0xa0
+#define OPTi93X_LINEAR_16_BIG		0xc0
+
+#define OPTi93X_CAPTURE_PIO		0x80
+#define OPTi93X_PLAYBACK_PIO		0x40
+#define OPTi93X_AUTOCALIB		0x08
+#define OPTi93X_SINGLE_DMA		0x04
+#define OPTi93X_CAPTURE_ENABLE		0x02
+#define OPTi93X_PLAYBACK_ENABLE		0x01
+
+#define OPTi93X_IRQ_ENABLE		0x02
+
+#define OPTi93X_DMA_REQUEST		0x10
+#define OPTi93X_CALIB_IN_PROGRESS	0x20
+
+#define OPTi93X_IRQ_PLAYBACK		0x04
+#define OPTi93X_IRQ_CAPTURE		0x08
+
+
+typedef struct _snd_opti93x opti93x_t;
+
+struct _snd_opti93x {
+	unsigned long port;
+	struct resource *res_port;
+	int irq;
+	int dma1;
+	int dma2;
+
+	opti9xx_t *chip;
+	unsigned short hardware;
+	unsigned char image[32];
+
+	unsigned char mce_bit;
+	unsigned short mode;
+	int mute;
+
+	spinlock_t lock;
+
+	snd_card_t *card;
+	snd_pcm_t *pcm;
+	snd_pcm_substream_t *playback_substream;
+	snd_pcm_substream_t *capture_substream;
+	unsigned int p_dma_size;
+	unsigned int c_dma_size;
+};
+
+#define OPTi93X_MODE_NONE	0x00
+#define OPTi93X_MODE_PLAY	0x01
+#define OPTi93X_MODE_CAPTURE	0x02
+#define OPTi93X_MODE_OPEN	(OPTi93X_MODE_PLAY | OPTi93X_MODE_CAPTURE)
+
+#endif /* OPTi93X */
+
+struct _snd_opti9xx {
+	unsigned short hardware;
+	unsigned char password;
+	char name[7];
+
+	unsigned long mc_base;
+	struct resource *res_mc_base;
+	unsigned long mc_base_size;
+#ifdef OPTi93X
+	unsigned long mc_indir_index;
+#endif	/* OPTi93X */
+	unsigned long pwd_reg;
+
+	spinlock_t lock;
+
+	long wss_base;
+	int irq;
+	int dma1;
+#if defined(CS4231) || defined(OPTi93X)
+	int dma2;
+#endif	/* CS4231 || OPTi93X */
+
+	long fm_port;
+
+	long mpu_port;
+	int mpu_irq;
+
+#if defined(OPTi93X)
+	opti93x_t *opti93x;
+#elif defined(CS4231)
+	cs4231_t *cs4231;
+#else
+	ad1848_t *ad1848;
+#endif	/* AD1848 */
+	snd_rawmidi_t *rmidi;
+#ifdef __ISAPNP__
+	struct isapnp_dev *dev;
+	struct isapnp_dev *devmpu;
+#endif	/* __ISAPNP__ */
+};
+
+static snd_card_t *snd_opti9xx_card = SNDRV_DEFAULT_PTR1;
+
+#ifdef __ISAPNP__
+
+#ifdef TARGET_OS2
+#define ISAPNP_OPTI9XX(_va, _vb, _vc, _device, _fa, _fb, _fc, _audio, _mpu401) \
+	{ \
+		0, ISAPNP_CARD_ID(_va, _vb, _vc, _device), \
+		{ ISAPNP_DEVICE_ID(_fa, _fb, _fc, _audio), \
+		  ISAPNP_DEVICE_ID(_fa, _fb, _fc, _mpu401), } \
+	}
+#else
+#define ISAPNP_OPTI9XX(_va, _vb, _vc, _device, _fa, _fb, _fc, _audio, _mpu401) \
+	{ \
+		ISAPNP_CARD_ID(_va, _vb, _vc, _device), \
+		devs: { ISAPNP_DEVICE_ID(_fa, _fb, _fc, _audio), \
+			ISAPNP_DEVICE_ID(_fa, _fb, _fc, _mpu401), } \
+	}
+#endif
+
+static struct isapnp_card_id snd_card_opti9xx_pnpids[] = {
+#ifndef OPTi93X
+	/* OPTi 82C924 */
+	ISAPNP_OPTI9XX('O','P','T',0x0924,'O','P','T',0x0000,0x0002),
+	/* OPTi 82C925 */
+	ISAPNP_OPTI9XX('O','P','T',0x0925,'O','P','T',0x9250,0x0002),
+#else
+	/* OPTi 82C931/3 */
+	ISAPNP_OPTI9XX('O','P','T',0x0931,'O','P','T',0x9310,0x0002),
+#endif	/* OPTi93X */
+	{ ISAPNP_CARD_END, }
+};
+
+ISAPNP_CARD_TABLE(snd_card_opti9xx_pnpids);
+
+#endif	/* __ISAPNP__ */
+
+#ifdef OPTi93X
+#define DRIVER_NAME	"snd-card-opti93x"
+#else
+#define DRIVER_NAME	"snd-card-opti92x"
+#endif	/* OPTi93X */
+
+static char * snd_opti9xx_names[] = {
+	"unkown",
+	"82C928",	"82C929",
+	"82C924",	"82C925",
+	"82C930",	"82C931",	"82C933"
+};
+
+
+static int __init snd_opti9xx_init(opti9xx_t *chip, unsigned short hardware)
+{
+	chip->hardware = hardware;
+	strcpy(chip->name, snd_opti9xx_names[hardware]);
+
+	spin_lock_init(&chip->lock);
+
+	chip->wss_base = -1;
+	chip->irq = -1;
+	chip->dma1 = -1;
+#if defined(CS4231) || defined (OPTi93X)
+	chip->dma2 = -1;
+#endif 	/* CS4231 || OPTi93X */
+	chip->fm_port = -1;
+	chip->mpu_port = -1;
+	chip->mpu_irq = -1;
+
+	switch (hardware) {
+#ifndef OPTi93X
+	case OPTi9XX_HW_82C928:
+	case OPTi9XX_HW_82C929:
+		chip->mc_base = 0xf8c;
+		chip->password = (hardware == OPTi9XX_HW_82C928) ? 0xe2 : 0xe3;
+		chip->pwd_reg = 3;
+		break;
+
+	case OPTi9XX_HW_82C924:
+	case OPTi9XX_HW_82C925:
+		chip->mc_base = 0xf8c;
+		chip->password = 0xe5;
+		chip->pwd_reg = 3;
+		break;
+#else	/* OPTi93X */
+
+	case OPTi9XX_HW_82C930:
+	case OPTi9XX_HW_82C931:
+	case OPTi9XX_HW_82C933:
+		chip->mc_base = (hardware == OPTi9XX_HW_82C930) ? 0xf8f : 0xf8d;
+		chip->mc_indir_index = 0xe0e;
+		chip->password = 0xe4;
+		chip->pwd_reg = 0;
+		break;
+#endif	/* OPTi93X */
+
+	default:
+		snd_printk("chip %d not supported\n", hardware);
+		return -ENODEV;
+	}
+	return 0;
+}
+
+static unsigned char snd_opti9xx_read(opti9xx_t *chip,
+				      unsigned char reg)
+{
+	unsigned long flags;
+	unsigned char retval = 0xff;
+
+	spin_lock_irqsave(&chip->lock, flags);
+	outb(chip->password, chip->mc_base + chip->pwd_reg);
+
+	switch (chip->hardware) {
+#ifndef OPTi93X
+	case OPTi9XX_HW_82C924:
+	case OPTi9XX_HW_82C925:
+		if (reg > 7) {
+			outb(reg, chip->mc_base + 8);
+			outb(chip->password, chip->mc_base + chip->pwd_reg);
+			retval = inb(chip->mc_base + 9);
+			break;
+		}
+
+	case OPTi9XX_HW_82C928:
+	case OPTi9XX_HW_82C929:
+		retval = inb(chip->mc_base + reg);
+		break;
+#else	/* OPTi93X */
+
+	case OPTi9XX_HW_82C930:
+	case OPTi9XX_HW_82C931:
+	case OPTi9XX_HW_82C933:
+		outb(reg, chip->mc_indir_index);
+		outb(chip->password, chip->mc_base + chip->pwd_reg);
+		retval = inb(chip->mc_indir_index + 1);
+		break;
+#endif	/* OPTi93X */
+
+	default:
+		snd_printk("chip %d not supported\n", chip->hardware);
+	}
+
+	spin_unlock_irqrestore(&chip->lock, flags);
+	return retval;
+}
+	
+static void snd_opti9xx_write(opti9xx_t *chip, unsigned char reg,
+			      unsigned char value)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->lock, flags);
+	outb(chip->password, chip->mc_base + chip->pwd_reg);
+
+	switch (chip->hardware) {
+#ifndef OPTi93X
+	case OPTi9XX_HW_82C924:
+	case OPTi9XX_HW_82C925:
+		if (reg > 7) {
+			outb(reg, chip->mc_base + 8);
+			outb(chip->password, chip->mc_base + chip->pwd_reg);
+			outb(value, chip->mc_base + 9);
+			break;
+		}
+
+	case OPTi9XX_HW_82C928:
+	case OPTi9XX_HW_82C929:
+		outb(value, chip->mc_base + reg);
+		break;
+#else	/* OPTi93X */
+
+	case OPTi9XX_HW_82C930:
+	case OPTi9XX_HW_82C931:
+	case OPTi9XX_HW_82C933:
+		outb(reg, chip->mc_indir_index);
+		outb(chip->password, chip->mc_base + chip->pwd_reg);
+		outb(value, chip->mc_indir_index + 1);
+		break;
+#endif	/* OPTi93X */
+
+	default:
+		snd_printk("chip %d not supported\n", chip->hardware);
+	}
+
+	spin_unlock_irqrestore(&chip->lock, flags);
+}
+
+
+#define snd_opti9xx_write_mask(chip, reg, value, mask)	\
+	snd_opti9xx_write(chip, reg,			\
+		(snd_opti9xx_read(chip, reg) & ~(mask)) | ((value) & (mask)))
+
+
+static int __init snd_opti9xx_configure(opti9xx_t *chip)
+{
+	unsigned char wss_base_bits;
+	unsigned char irq_bits;
+	unsigned char dma_bits;
+	unsigned char mpu_port_bits = 0;
+	unsigned char mpu_irq_bits;
+	unsigned long flags;
+
+	switch (chip->hardware) {
+#ifndef OPTi93X
+	case OPTi9XX_HW_82C924:
+		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(4), 0xf0, 0xfc);
+		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6), 0x02, 0x02);
+
+	case OPTi9XX_HW_82C925:
+		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(1), 0x80, 0x80);
+		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2), 0x00, 0x20);
+		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(3), 0xf0, 0xff);
+#ifdef CS4231
+		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x02, 0x02);
+#else
+		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x00, 0x02);
+#endif	/* CS4231 */
+		break;
+
+	case OPTi9XX_HW_82C928:
+	case OPTi9XX_HW_82C929:
+		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(1), 0x80, 0x80);
+		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2), 0x00, 0x20);
+		/*
+		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(3), 0xa2, 0xae);
+		*/
+		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(4), 0x00, 0x0c);
+#ifdef CS4231
+		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x02, 0x02);
+#else
+		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x00, 0x02);
+#endif	/* CS4231 */
+		break;
+
+#else	/* OPTi93X */
+	case OPTi9XX_HW_82C930:
+	case OPTi9XX_HW_82C931:
+	case OPTi9XX_HW_82C933:
+		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6), 0x02, 0x03);
+		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(3), 0x00, 0xff);
+		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(4), 0x10 |
+			(chip->hardware == OPTi9XX_HW_82C930 ? 0x00 : 0x04),
+			0x34);
+		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x20, 0xbf);
+		break;
+#endif	/* OPTi93X */
+
+	default:
+		snd_printk("chip %d not supported\n", chip->hardware);
+		return -EINVAL;
+	}
+
+	switch (chip->wss_base) {
+	case 0x530:
+		wss_base_bits = 0x00;
+		break;
+	case 0x604:
+		wss_base_bits = 0x03;
+		break;
+	case 0xe80:
+		wss_base_bits = 0x01;
+		break;
+	case 0xf40:
+		wss_base_bits = 0x02;
+		break;
+	default:
+		snd_printk("WSS port 0x%lx not valid\n", chip->wss_base);
+		goto __skip_base;
+	}
+	snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(1), wss_base_bits << 4, 0x30);
+
+__skip_base:
+	switch (chip->irq) {
+#ifdef OPTi93X
+	case 5:
+		irq_bits = 0x05;
+		break;
+#endif	/* OPTi93X */
+	case 7:
+		irq_bits = 0x01;
+		break;
+	case 9:
+		irq_bits = 0x02;
+		break;
+	case 10:
+		irq_bits = 0x03;
+		break;
+	case 11:
+		irq_bits = 0x04;
+		break;
+	default:
+		snd_printk("WSS irq # %d not valid\n", chip->irq);
+		goto __skip_resources;
+	}
+
+	switch (chip->dma1) {
+	case 0:
+		dma_bits = 0x01;
+		break;
+	case 1:
+		dma_bits = 0x02;
+		break;
+	case 3:
+		dma_bits = 0x03;
+		break;
+	default:
+		snd_printk("WSS dma1 # %d not valid\n", chip->dma1);
+		goto __skip_resources;
+	}
+
+#if defined(CS4231) || defined(OPTi93X)
+	if (chip->dma1 == chip->dma2) {
+		snd_printk("don't want to share dmas\n");
+		return -EBUSY;
+	}
+
+	switch (chip->dma2) {
+	case 0:
+	case 1:
+		break;
+	default:
+		snd_printk("WSS dma2 # %d not valid\n", chip->dma2);
+		goto __skip_resources;
+	}
+	dma_bits |= 0x04;
+#endif	/* CS4231 || OPTi93X */
+
+	spin_lock_irqsave(&chip->lock, flags);
+	outb(irq_bits << 3 | dma_bits, chip->wss_base);
+	spin_unlock_irqrestore(&chip->lock, flags);
+
+__skip_resources:
+	if (chip->hardware > OPTi9XX_HW_82C928) {
+		switch (chip->mpu_port) {
+		case -1:
+			break;
+		case 0x300:
+			mpu_port_bits = 0x03;
+			break;
+		case 0x310:
+			mpu_port_bits = 0x02;
+			break;
+		case 0x320:
+			mpu_port_bits = 0x01;
+			break;
+		case 0x330:
+			mpu_port_bits = 0x00;
+			break;
+		default:
+			snd_printk("MPU-401 port 0x%lx not valid\n",
+				chip->mpu_port);
+			goto __skip_mpu;
+		}
+
+		switch (chip->mpu_irq) {
+		case 5:
+			mpu_irq_bits = 0x02;
+			break;
+		case 7:
+			mpu_irq_bits = 0x03;
+			break;
+		case 9:
+			mpu_irq_bits = 0x00;
+			break;
+		case 10:
+			mpu_irq_bits = 0x01;
+			break;
+		default:
+			snd_printk("MPU-401 irq # %d not valid\n",
+				chip->mpu_irq);
+			goto __skip_mpu;
+		}
+
+		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6),
+			(chip->mpu_port == -1) ? 0x00 :
+				0x80 | mpu_port_bits << 5 | mpu_irq_bits << 3,
+			0xf8);
+	}
+__skip_mpu:
+
+	return 0;
+}
+
+#ifdef OPTi93X
+
+#define chip_t opti93x_t
+
+static unsigned char snd_opti93x_default_image[32] =
+{
+	0x00,		/* 00/00 - l_mixout_outctrl */
+	0x00,		/* 01/01 - r_mixout_outctrl */
+	0x88,		/* 02/02 - l_cd_inctrl */
+	0x88,		/* 03/03 - r_cd_inctrl */
+	0x88,		/* 04/04 - l_a1/fm_inctrl */
+	0x88,		/* 05/05 - r_a1/fm_inctrl */
+	0x80,		/* 06/06 - l_dac_inctrl */
+	0x80,		/* 07/07 - r_dac_inctrl */
+	0x00,		/* 08/08 - ply_dataform_reg */
+	0x00,		/* 09/09 - if_conf */
+	0x00,		/* 0a/10 - pin_ctrl */
+	0x00,		/* 0b/11 - err_init_reg */
+	0x0a,		/* 0c/12 - id_reg */
+	0x00,		/* 0d/13 - reserved */
+	0x00,		/* 0e/14 - ply_upcount_reg */
+	0x00,		/* 0f/15 - ply_lowcount_reg */
+	0x88,		/* 10/16 - reserved/l_a1_inctrl */
+	0x88,		/* 11/17 - reserved/r_a1_inctrl */
+	0x88,		/* 12/18 - l_line_inctrl */
+	0x88,		/* 13/19 - r_line_inctrl */
+	0x88,		/* 14/20 - l_mic_inctrl */
+	0x88,		/* 15/21 - r_mic_inctrl */
+	0x80,		/* 16/22 - l_out_outctrl */
+	0x80,		/* 17/23 - r_out_outctrl */
+	0x00,		/* 18/24 - reserved */
+	0x00,		/* 19/25 - reserved */
+	0x00,		/* 1a/26 - reserved */
+	0x00,		/* 1b/27 - reserved */
+	0x00,		/* 1c/28 - cap_dataform_reg */
+	0x00,		/* 1d/29 - reserved */
+	0x00,		/* 1e/30 - cap_upcount_reg */
+	0x00		/* 1f/31 - cap_lowcount_reg */
+};
+
+
+static int snd_opti93x_busy_wait(opti93x_t *chip)
+{
+	int timeout;
+
+	for (timeout = 250; timeout-- > 0; udelay(10))
+		if (!(inb(OPTi93X_PORT(chip, INDEX)) & OPTi93X_INIT))
+			return 0;
+
+	snd_printk("chip still busy.\n");
+	return -EBUSY;
+}
+
+static unsigned char snd_opti93x_in(opti93x_t *chip, unsigned char reg)
+{
+	snd_opti93x_busy_wait(chip);
+	outb(chip->mce_bit | (reg & 0x1f), OPTi93X_PORT(chip, INDEX));
+	return inb(OPTi93X_PORT(chip, DATA));
+}
+
+static void snd_opti93x_out(opti93x_t *chip, unsigned char reg,
+			    unsigned char value)
+{
+	snd_opti93x_busy_wait(chip);
+	outb(chip->mce_bit | (reg & 0x1f), OPTi93X_PORT(chip, INDEX));
+	outb(value, OPTi93X_PORT(chip, DATA));
+}
+
+static void snd_opti93x_out_image(opti93x_t *chip, unsigned char reg,
+				  unsigned char value)
+{
+	snd_opti93x_out(chip, reg, chip->image[reg] = value);
+}
+
+static void snd_opti93x_out_mask(opti93x_t *chip, unsigned char reg,
+				 unsigned char mask, unsigned char value)
+{
+	snd_opti93x_out_image(chip, reg,
+		(chip->image[reg] & ~mask) | (value & mask));
+}
+
+
+static void snd_opti93x_mce_up(opti93x_t *chip)
+{
+	snd_opti93x_busy_wait(chip);
+
+	chip->mce_bit = OPTi93X_MCE;
+	if (!(inb(OPTi93X_PORT(chip, INDEX)) & OPTi93X_MCE))
+		outb(chip->mce_bit, OPTi93X_PORT(chip, INDEX));
+}
+
+static void snd_opti93x_mce_down(opti93x_t *chip)
+{
+	snd_opti93x_busy_wait(chip);
+
+	chip->mce_bit = 0;
+	if (inb(OPTi93X_PORT(chip, INDEX)) & OPTi93X_MCE)
+		outb(chip->mce_bit, OPTi93X_PORT(chip, INDEX));
+}
+
+#define snd_opti93x_mute_reg(chip, reg, mute)	\
+	snd_opti93x_out(chip, reg, mute ? 0x80 : chip->image[reg]);
+
+static void snd_opti93x_mute(opti93x_t *chip, int mute)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->lock, flags);
+
+	mute = mute ? 1 : 0;
+	if (chip->mute == mute) {
+		spin_unlock_irqrestore(&chip->lock, flags);
+		return;
+	}
+	chip->mute = mute;
+
+	snd_opti93x_mute_reg(chip, OPTi93X_CD_LEFT_INPUT, mute);
+	snd_opti93x_mute_reg(chip, OPTi93X_CD_RIGHT_INPUT, mute);
+	switch (chip->hardware) {
+	case OPTi9XX_HW_82C930:
+		snd_opti93x_mute_reg(chip, OPTi930_AUX_LEFT_INPUT, mute);
+		snd_opti93x_mute_reg(chip, OPTi930_AUX_RIGHT_INPUT, mute);
+		break;
+	case OPTi9XX_HW_82C931:
+	case OPTi9XX_HW_82C933:
+		snd_opti93x_mute_reg(chip, OPTi931_FM_LEFT_INPUT, mute);
+		snd_opti93x_mute_reg(chip, OPTi931_FM_RIGHT_INPUT, mute);
+		snd_opti93x_mute_reg(chip, OPTi931_AUX_LEFT_INPUT, mute);
+		snd_opti93x_mute_reg(chip, OPTi931_AUX_RIGHT_INPUT, mute);
+	}
+	snd_opti93x_mute_reg(chip, OPTi93X_DAC_LEFT, mute);
+	snd_opti93x_mute_reg(chip, OPTi93X_DAC_RIGHT, mute);
+	snd_opti93x_mute_reg(chip, OPTi93X_LINE_LEFT_INPUT, mute);
+	snd_opti93x_mute_reg(chip, OPTi93X_LINE_RIGHT_INPUT, mute);
+	snd_opti93x_mute_reg(chip, OPTi93X_MIC_LEFT_INPUT, mute);
+	snd_opti93x_mute_reg(chip, OPTi93X_MIC_RIGHT_INPUT, mute);
+	snd_opti93x_mute_reg(chip, OPTi93X_OUT_LEFT, mute);
+	snd_opti93x_mute_reg(chip, OPTi93X_OUT_RIGHT, mute);
+
+	spin_unlock_irqrestore(&chip->lock, flags);
+}
+
+
+static unsigned int snd_opti93x_get_count(unsigned char format,
+					  unsigned int size)
+{
+	switch (format & 0xe0) {
+	case OPTi93X_LINEAR_16_LIT:
+	case OPTi93X_LINEAR_16_BIG:
+		size >>= 1;
+		break;
+	case OPTi93X_ADPCM_16:
+		return size >> 2;
+	}
+	return (format & OPTi93X_STEREO) ? (size >> 1) : size;
+}
+
+unsigned int rates[] = {  5512,  6615,  8000,  9600, 11025, 16000, 18900,
+			 22050, 27428, 32000, 33075, 37800, 44100, 48000 };
+#define RATES sizeof(rates) / sizeof(rates[0])
+
+#ifdef TARGET_OS2
+static snd_pcm_hw_constraint_list_t hw_constraints_rates = {
+	RATES,
+	rates,
+	0,
+};
+#else
+static snd_pcm_hw_constraint_list_t hw_constraints_rates = {
+	count: RATES,
+	list: rates,
+	mask: 0,
+};
+#endif
+
+unsigned char bits[] = {  0x01,  0x0f,  0x00,  0x0e,  0x03,  0x02,  0x05,
+			  0x07,  0x04,  0x06,  0x0d,  0x09,  0x0b,  0x0c};
+
+static unsigned char snd_opti93x_get_freq(unsigned int rate)
+{
+	int i;
+
+	for (i = 0; i < RATES; i++) {
+		if (rate == rates[i])
+			return bits[i];
+	}
+	snd_BUG();
+	return bits[RATES-1];
+}
+
+static unsigned char snd_opti93x_get_format(opti93x_t *chip,
+					    unsigned int format, int channels)
+{
+	unsigned char retval = OPTi93X_LINEAR_8;
+
+	switch (format) {
+	case SNDRV_PCM_FORMAT_MU_LAW:
+		retval = OPTi93X_ULAW_8;
+		break;
+	case SNDRV_PCM_FORMAT_A_LAW:
+		retval = OPTi93X_ALAW_8;
+		break;
+	case SNDRV_PCM_FORMAT_S16_LE:
+		retval = OPTi93X_LINEAR_16_LIT;
+		break;
+	case SNDRV_PCM_FORMAT_S16_BE:
+		retval = OPTi93X_LINEAR_16_BIG;
+		break;
+	case SNDRV_PCM_FORMAT_IMA_ADPCM:
+		retval = OPTi93X_ADPCM_16;
+	}
+	return (channels > 1) ? (retval | OPTi93X_STEREO) : retval;
+}
+
+
+static void snd_opti93x_playback_format(opti93x_t *chip, unsigned char fmt)
+{
+	unsigned long flags;
+	unsigned char mask;
+
+	spin_lock_irqsave(&chip->lock, flags);
+	snd_opti93x_mute(chip, 1);
+
+	snd_opti93x_mce_up(chip);
+	mask = (chip->mode & OPTi93X_MODE_CAPTURE) ? 0xf0 : 0xff;
+	snd_opti93x_out_mask(chip, OPTi93X_PLAY_FORMAT, mask, fmt);
+	snd_opti93x_mce_down(chip);
+
+	snd_opti93x_mute(chip, 0);
+	spin_unlock_irqrestore(&chip->lock, flags);
+}
+
+static void snd_opti93x_capture_format(opti93x_t *chip, unsigned char fmt)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->lock, flags);
+	snd_opti93x_mute(chip, 1);
+
+	snd_opti93x_mce_up(chip);
+	if (!(chip->mode & OPTi93X_MODE_PLAY))
+		snd_opti93x_out_mask(chip, OPTi93X_PLAY_FORMAT, 0x0f, fmt);
+	else
+		fmt = chip->image[OPTi93X_PLAY_FORMAT] & 0xf0;
+	snd_opti93x_out_image(chip, OPTi93X_CAPT_FORMAT, fmt);
+	snd_opti93x_mce_down(chip);
+
+	snd_opti93x_mute(chip, 0);
+	spin_unlock_irqrestore(&chip->lock, flags);
+}
+
+
+static int snd_opti93x_open(opti93x_t *chip, unsigned int mode)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->lock, flags);
+
+	if (chip->mode & mode) {
+		spin_unlock_irqrestore(&chip->lock, flags);
+		return -EAGAIN;
+	}
+
+	if (!(chip->mode & OPTi93X_MODE_OPEN)) {
+		outb(0x00, OPTi93X_PORT(chip, STATUS));
+		snd_opti93x_out_mask(chip, OPTi93X_PIN_CTRL,
+			OPTi93X_IRQ_ENABLE, OPTi93X_IRQ_ENABLE);
+		chip->mode = mode;
+	}
+	else
+		chip->mode |= mode;
+
+	spin_unlock_irqrestore(&chip->lock, flags);
+	return 0;
+}
+
+static void snd_opti93x_close(opti93x_t *chip, unsigned int mode)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->lock, flags);
+
+	chip->mode &= ~mode;
+	if (chip->mode & OPTi93X_MODE_OPEN) {
+		spin_unlock_irqrestore(&chip->lock, flags);
+		return;
+	}
+
+	snd_opti93x_mute(chip, 1);
+
+	outb(0, OPTi93X_PORT(chip, STATUS));
+	snd_opti93x_out_mask(chip, OPTi93X_PIN_CTRL, OPTi93X_IRQ_ENABLE,
+		~OPTi93X_IRQ_ENABLE);
+
+	snd_opti93x_mce_up(chip);
+	snd_opti93x_out_image(chip, OPTi93X_IFACE_CONF, 0x00);
+	snd_opti93x_mce_down(chip);
+	chip->mode = 0;
+
+	snd_opti93x_mute(chip, 0);
+	spin_unlock_irqrestore(&chip->lock, flags);
+}
+
+static int snd_opti93x_trigger(snd_pcm_substream_t *substream, 
+			       unsigned char what, int cmd)
+{
+	opti93x_t *chip = snd_pcm_substream_chip(substream);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_STOP:
+	{
+		unsigned int what = 0;
+		snd_pcm_substream_t *s = substream;
+		do {
+			if (s == chip->playback_substream) {
+				what |= OPTi93X_PLAYBACK_ENABLE;
+				snd_pcm_trigger_done(s, substream);
+			} else if (s == chip->capture_substream) {
+				what |= OPTi93X_CAPTURE_ENABLE;
+				snd_pcm_trigger_done(s, substream);
+			}
+			s = s->link_next;
+		} while (s != substream);
+		spin_lock(&chip->lock);
+		if (SNDRV_PCM_TRIGGER_START)
+			snd_opti93x_out_mask(chip, OPTi93X_IFACE_CONF, what, what);
+		else
+			snd_opti93x_out_mask(chip, OPTi93X_IFACE_CONF, what, 0x00);
+		spin_unlock(&chip->lock);
+		break;
+	}
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int snd_opti93x_playback_trigger(snd_pcm_substream_t *substream, int cmd)
+{
+	return snd_opti93x_trigger(substream,
+				   OPTi93X_PLAYBACK_ENABLE, cmd);
+}
+
+static int snd_opti93x_capture_trigger(snd_pcm_substream_t * substream, int cmd)
+{
+	return snd_opti93x_trigger(substream,
+				   OPTi93X_CAPTURE_ENABLE, cmd);
+}
+
+static int snd_opti93x_hw_params(snd_pcm_substream_t * substream,
+				 snd_pcm_hw_params_t * hw_params)
+{
+	return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+
+static int snd_opti93x_hw_free(snd_pcm_substream_t * substream)
+{
+	snd_pcm_lib_free_pages(substream);
+	return 0;
+}
+
+
+static int snd_opti93x_playback_prepare(snd_pcm_substream_t * substream)
+{
+	opti93x_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	unsigned long flags;
+	unsigned char format;
+	unsigned int count = snd_pcm_lib_period_bytes(substream);
+	unsigned int size = snd_pcm_lib_buffer_bytes(substream);
+
+	spin_lock_irqsave(&chip->lock, flags);
+
+	chip->p_dma_size = size;
+	snd_opti93x_out_mask(chip, OPTi93X_IFACE_CONF,
+		OPTi93X_PLAYBACK_ENABLE | OPTi93X_PLAYBACK_PIO,
+		~(OPTi93X_PLAYBACK_ENABLE | OPTi93X_PLAYBACK_PIO));
+
+	snd_dma_program(chip->dma1, runtime->dma_area, size,
+		DMA_MODE_WRITE | DMA_AUTOINIT);
+
+	format = snd_opti93x_get_freq(runtime->rate);
+	format |= snd_opti93x_get_format(chip, runtime->format,
+		runtime->channels);
+	snd_opti93x_playback_format(chip, format);
+	format = chip->image[OPTi93X_PLAY_FORMAT];
+
+	count = snd_opti93x_get_count(format, count) - 1;
+	snd_opti93x_out_image(chip, OPTi93X_PLAY_LWR_CNT, count);
+	snd_opti93x_out_image(chip, OPTi93X_PLAY_UPR_CNT, count >> 8);
+
+	spin_unlock_irqrestore(&chip->lock, flags);
+	return 0;
+}
+
+static int snd_opti93x_capture_prepare(snd_pcm_substream_t *substream)
+{
+	opti93x_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	unsigned long flags;
+	unsigned char format;
+	unsigned int count = snd_pcm_lib_period_bytes(substream);
+	unsigned int size = snd_pcm_lib_buffer_bytes(substream);
+
+	spin_lock_irqsave(&chip->lock, flags);
+
+	chip->c_dma_size = size;
+	snd_opti93x_out_mask(chip, OPTi93X_IFACE_CONF,
+		OPTi93X_CAPTURE_ENABLE | OPTi93X_CAPTURE_PIO,
+		(unsigned char)~(OPTi93X_CAPTURE_ENABLE | OPTi93X_CAPTURE_PIO));
+
+	snd_dma_program(chip->dma2, runtime->dma_area, size,
+		DMA_MODE_READ | DMA_AUTOINIT);
+
+	format = snd_opti93x_get_freq(runtime->rate);
+	format |= snd_opti93x_get_format(chip, runtime->format,
+		runtime->channels);
+	snd_opti93x_capture_format(chip, format);
+	format = chip->image[OPTi93X_CAPT_FORMAT];
+
+	count = snd_opti93x_get_count(format, count) - 1;
+	snd_opti93x_out_image(chip, OPTi93X_CAPT_LWR_CNT, count);
+	snd_opti93x_out_image(chip, OPTi93X_CAPT_UPR_CNT, count >> 8);
+
+	spin_unlock_irqrestore(&chip->lock, flags);
+	return 0;
+}
+
+static snd_pcm_uframes_t snd_opti93x_playback_pointer(snd_pcm_substream_t *substream)
+{
+	opti93x_t *chip = snd_pcm_substream_chip(substream);
+	size_t ptr;
+
+	if (!(chip->image[OPTi93X_IFACE_CONF] & OPTi93X_PLAYBACK_ENABLE))
+		return 0;
+
+	ptr = chip->p_dma_size - snd_dma_residue(chip->dma1);
+	return bytes_to_frames(substream->runtime, ptr);
+}
+
+static snd_pcm_uframes_t snd_opti93x_capture_pointer(snd_pcm_substream_t *substream)
+{
+	opti93x_t *chip = snd_pcm_substream_chip(substream);
+	size_t ptr;
+	
+	if (!(chip->image[OPTi93X_IFACE_CONF] & OPTi93X_CAPTURE_ENABLE))
+		return 0;
+
+	ptr = chip->c_dma_size - snd_dma_residue(chip->dma2);
+	return bytes_to_frames(substream->runtime, ptr);
+}
+
+
+static void snd_opti93x_overrange(opti93x_t *chip)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->lock, flags);
+
+	if (snd_opti93x_in(chip, OPTi93X_ERR_INIT) & (0x08 | 0x02))
+		chip->capture_substream->runtime->overrange++;
+
+	spin_unlock_irqrestore(&chip->lock, flags);
+}
+
+void snd_opti93x_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	opti93x_t *codec = snd_magic_cast(opti93x_t, dev_id, return);
+	unsigned char status;
+
+	status = snd_opti9xx_read(codec->chip, OPTi9XX_MC_REG(11));
+	if ((status & OPTi93X_IRQ_PLAYBACK) && codec->playback_substream)
+		snd_pcm_period_elapsed(codec->playback_substream);
+	if ((status & OPTi93X_IRQ_CAPTURE) && codec->capture_substream) {
+		snd_opti93x_overrange(codec);
+		snd_pcm_period_elapsed(codec->capture_substream);
+	}
+	outb(0x00, OPTi93X_PORT(codec, STATUS));
+}
+
+
+#ifdef TARGET_OS2
+static snd_pcm_hardware_t snd_opti93x_playback = {
+/*	info:		  */	(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START),
+/*	formats:	  */	(SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_IMA_ADPCM |
+				 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE),
+/*	rates:		  */	SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000,
+/*	rate_min:	  */	5512,
+/*	rate_max:	  */	48000,
+/*	channels_min:	  */	1,
+/*	channels_max:	  */	2,
+/*	buffer_bytes_max: */	(128*1024),
+/*	period_bytes_min: */	64,
+/*	period_bytes_max: */	(128*1024),
+/*	periods_min:	  */	1,
+/*	periods_max:	  */	1024,
+/*	fifo_size:	  */	0,
+};
+
+static snd_pcm_hardware_t snd_opti93x_capture = {
+/*	info:		  */	(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START),
+/*	formats:	  */	(SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_IMA_ADPCM |
+				 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE),
+/*	rates:		  */	SNDRV_PCM_RATE_8000_48000,
+/*	rate_min:	  */	5512,
+/*	rate_max:	  */	48000,
+/*	channels_min:	  */	1,
+/*	channels_max:	  */	2,
+/*	buffer_bytes_max: */	(128*1024),
+/*	period_bytes_min: */	64,
+/*	period_bytes_max: */	(128*1024),
+/*	periods_min:	  */	1,
+/*	periods_max:	  */	1024,
+/*	fifo_size:	  */	0,
+};
+#else
+static snd_pcm_hardware_t snd_opti93x_playback = {
+	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START),
+	formats:		(SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_IMA_ADPCM |
+				 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE),
+	rates:			SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000,
+	rate_min:		5512,
+	rate_max:		48000,
+	channels_min:		1,
+	channels_max:		2,
+	buffer_bytes_max:	(128*1024),
+	period_bytes_min:	64,
+	period_bytes_max:	(128*1024),
+	periods_min:		1,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+
+static snd_pcm_hardware_t snd_opti93x_capture = {
+	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START),
+	formats:		(SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_IMA_ADPCM |
+				 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE),
+	rates:			SNDRV_PCM_RATE_8000_48000,
+	rate_min:		5512,
+	rate_max:		48000,
+	channels_min:		1,
+	channels_max:		2,
+	buffer_bytes_max:	(128*1024),
+	period_bytes_min:	64,
+	period_bytes_max:	(128*1024),
+	periods_min:		1,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+#endif
+
+static int snd_opti93x_playback_open(snd_pcm_substream_t *substream)
+{
+	int error;
+	opti93x_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	if ((error = snd_opti93x_open(chip, OPTi93X_MODE_PLAY)) < 0)
+		return error;
+	snd_pcm_set_sync(substream);
+	chip->playback_substream = substream;
+	runtime->hw = snd_opti93x_playback;
+	snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.buffer_bytes_max);
+	snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.period_bytes_max);
+	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates);
+	return error;
+}
+
+static int snd_opti93x_capture_open(snd_pcm_substream_t *substream)
+{
+	int error;
+	opti93x_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	if ((error = snd_opti93x_open(chip, OPTi93X_MODE_CAPTURE)) < 0)
+		return error;
+	runtime->hw = snd_opti93x_capture;
+	snd_pcm_set_sync(substream);
+	snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.buffer_bytes_max);
+	snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.period_bytes_max);
+	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates);
+	return error;
+}
+
+static int snd_opti93x_playback_close(snd_pcm_substream_t *substream)
+{
+	opti93x_t *chip = snd_pcm_substream_chip(substream);
+
+	chip->playback_substream = NULL;
+	snd_opti93x_close(chip, OPTi93X_MODE_PLAY);
+	return 0;
+}
+
+static int snd_opti93x_capture_close(snd_pcm_substream_t *substream)
+{
+	opti93x_t *chip = snd_pcm_substream_chip(substream);
+
+	chip->capture_substream = NULL;
+	snd_opti93x_close(chip, OPTi93X_MODE_CAPTURE);
+	return 0;
+}
+
+
+static void snd_opti93x_init(opti93x_t *chip)
+{
+	unsigned long flags;
+	int i;
+
+	spin_lock_irqsave(&chip->lock, flags);
+	snd_opti93x_mce_up(chip);
+
+	for (i = 0; i < 32; i++)
+		snd_opti93x_out_image(chip, i, snd_opti93x_default_image[i]);
+
+	snd_opti93x_mce_down(chip);
+	spin_unlock_irqrestore(&chip->lock, flags);
+}
+
+static int snd_opti93x_probe(opti93x_t *chip)
+{
+	unsigned long flags;
+	unsigned char val;
+
+	spin_lock_irqsave(&chip->lock, flags);
+	val = snd_opti93x_in(chip, OPTi93X_ID) & 0x0f;
+	spin_unlock_irqrestore(&chip->lock, flags);
+
+	return (val == 0x0a) ? 0 : -ENODEV;
+}
+
+static int snd_opti93x_free(opti93x_t *chip)
+{
+	if (chip->res_port)
+		release_resource(chip->res_port);
+	if (chip->dma1 >= 0) {
+		disable_dma(chip->dma1);
+		free_dma(chip->dma1);
+	}
+	if (chip->dma2 >= 0) {
+		disable_dma(chip->dma2);
+		free_dma(chip->dma2);
+	}
+	snd_magic_kfree(chip);
+	return 0;
+}
+
+static int snd_opti93x_dev_free(snd_device_t *device)
+{
+	opti93x_t *chip = snd_magic_cast(opti93x_t, device->device_data, return -ENXIO);
+	return snd_opti93x_free(chip);
+}
+
+static const char *snd_opti93x_chip_id(opti93x_t *codec)
+{
+	switch (codec->hardware) {
+	case OPTi9XX_HW_82C930: return "82C930";
+	case OPTi9XX_HW_82C931: return "82C931";
+	case OPTi9XX_HW_82C933: return "82C933";
+	default:		return "???";
+	}
+}
+
+int snd_opti93x_create(snd_card_t *card, opti9xx_t *chip,
+		       int dma1, int dma2,
+		       opti93x_t **rcodec)
+{
+#ifdef TARGET_OS2
+	static snd_device_ops_t ops = {
+		snd_opti93x_dev_free,0, 0
+	};
+#else
+	static snd_device_ops_t ops = {
+		dev_free:	snd_opti93x_dev_free,
+	};
+#endif
+	int error;
+	opti93x_t *codec;
+
+	*rcodec = NULL;
+	codec = snd_magic_kcalloc(opti93x_t, 0, GFP_KERNEL);
+	if (codec == NULL)
+		return -ENOMEM;
+	codec->irq = -1;
+	codec->dma1 = -1;
+	codec->dma2 = -1;
+
+	if ((codec->res_port = request_region(chip->wss_base + 4, 4, "OPTI93x CODEC")) == NULL) {
+		snd_opti93x_free(codec);
+		return -EBUSY;
+	}
+	if (request_dma(dma1, "OPTI93x - 1")) {
+		snd_opti93x_free(codec);
+		return -EBUSY;
+	}
+	codec->dma1 = chip->dma1;
+	if (request_dma(dma2, "OPTI93x - 2")) {
+		snd_opti93x_free(codec);
+		return -EBUSY;
+	}
+	codec->dma2 = chip->dma2;
+
+	codec->card = card;
+	codec->port = chip->wss_base + 4;
+	codec->irq = chip->irq;
+
+	spin_lock_init(&codec->lock);
+	codec->hardware = chip->hardware;
+	codec->chip = chip;
+
+	if ((error = snd_opti93x_probe(codec))) {
+		snd_opti93x_free(codec);
+		return error;
+	}
+
+	snd_opti93x_init(codec);
+
+	/* Register device */
+	if ((error = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+		snd_opti93x_free(codec);
+		return error;
+	}
+
+	*rcodec = codec;
+	return 0;
+}
+
+#ifdef TARGET_OS2
+static snd_pcm_ops_t snd_opti93x_playback_ops = {
+/*	open:	  */	snd_opti93x_playback_open,
+/*	close:	  */	snd_opti93x_playback_close,
+/*	ioctl:	  */	snd_pcm_lib_ioctl,
+/*	hw_params:*/	snd_opti93x_hw_params,
+/*	hw_free:  */	snd_opti93x_hw_free,
+/*	prepare:  */	snd_opti93x_playback_prepare,
+/*	trigger:  */	snd_opti93x_playback_trigger,
+/*	pointer:  */	snd_opti93x_playback_pointer,
+        0, 0
+};
+
+static snd_pcm_ops_t snd_opti93x_capture_ops = {
+/*	open:	  */	snd_opti93x_capture_open,
+/*	close:	  */	snd_opti93x_capture_close,
+/*	ioctl:	  */	snd_pcm_lib_ioctl,
+/*	hw_params:*/	snd_opti93x_hw_params,
+/*	hw_free:  */	snd_opti93x_hw_free,
+/*	prepare:  */	snd_opti93x_capture_prepare,
+/*	trigger:  */	snd_opti93x_capture_trigger,
+/*	pointer:  */	snd_opti93x_capture_pointer,
+        0, 0
+}; 
+#else
+static snd_pcm_ops_t snd_opti93x_playback_ops = {
+	open:		snd_opti93x_playback_open,
+	close:		snd_opti93x_playback_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	hw_params:	snd_opti93x_hw_params,
+	hw_free:	snd_opti93x_hw_free,
+	prepare:	snd_opti93x_playback_prepare,
+	trigger:	snd_opti93x_playback_trigger,
+	pointer:	snd_opti93x_playback_pointer,
+};
+
+static snd_pcm_ops_t snd_opti93x_capture_ops = {
+	open:		snd_opti93x_capture_open,
+	close:		snd_opti93x_capture_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	hw_params:	snd_opti93x_hw_params,
+	hw_free:	snd_opti93x_hw_free,
+	prepare:	snd_opti93x_capture_prepare,
+	trigger:	snd_opti93x_capture_trigger,
+	pointer:	snd_opti93x_capture_pointer,
+};
+#endif
+
+static void snd_opti93x_pcm_free(snd_pcm_t *pcm)
+{
+	opti93x_t *codec = snd_magic_cast(opti93x_t, pcm->private_data, return);
+	codec->pcm = NULL;
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+int snd_opti93x_pcm(opti93x_t *codec, int device, snd_pcm_t **rpcm)
+{
+	int error;
+	snd_pcm_t *pcm;
+
+	if ((error = snd_pcm_new(codec->card, "OPTi 82C93X", device, 1, 1, &pcm)))
+		return error;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_opti93x_playback_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_opti93x_capture_ops);
+
+	pcm->private_data = codec;
+	pcm->private_free = snd_opti93x_pcm_free;
+	pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX;
+
+	strcpy(pcm->name, snd_opti93x_chip_id(codec));
+
+	snd_pcm_lib_preallocate_pages_for_all(pcm, 64*1024, codec->dma1 > 3 || codec->dma2 > 3 ? 128*1024 : 64*1024, GFP_KERNEL|GFP_DMA);
+
+	codec->pcm = pcm;
+	if (rpcm)
+		*rpcm = pcm;
+	return 0;
+}
+
+/*
+ *  MIXER part
+ */
+
+static int snd_opti93x_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	static char *texts[4] = {
+		"Line1", "Aux", "Mic", "Mix"
+	};
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 2;
+	uinfo->value.enumerated.items = 4;
+	if (uinfo->value.enumerated.item > 3)
+		uinfo->value.enumerated.item = 3;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int snd_opti93x_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	opti93x_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	
+	spin_lock_irqsave(&chip->lock, flags);
+	ucontrol->value.enumerated.item[0] = (chip->image[OPTi93X_MIXOUT_LEFT] & OPTi93X_MIXOUT_MIXER) >> 6;
+	ucontrol->value.enumerated.item[1] = (chip->image[OPTi93X_MIXOUT_RIGHT] & OPTi93X_MIXOUT_MIXER) >> 6;
+	spin_unlock_irqrestore(&chip->lock, flags);
+	return 0;
+}
+
+static int snd_opti93x_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	opti93x_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	unsigned short left, right;
+	int change;
+	
+	if (ucontrol->value.enumerated.item[0] > 3 ||
+	    ucontrol->value.enumerated.item[1] > 3)
+		return -EINVAL;
+	left = ucontrol->value.enumerated.item[0] << 6;
+	right = ucontrol->value.enumerated.item[1] << 6;
+	spin_lock_irqsave(&chip->lock, flags);
+	left = (chip->image[OPTi93X_MIXOUT_LEFT] & ~OPTi93X_MIXOUT_MIXER) | left;
+	right = (chip->image[OPTi93X_MIXOUT_RIGHT] & ~OPTi93X_MIXOUT_MIXER) | right;
+	change = left != chip->image[OPTi93X_MIXOUT_LEFT] ||
+	         right != chip->image[OPTi93X_MIXOUT_RIGHT];
+	snd_opti93x_out(chip, OPTi93X_MIXOUT_LEFT, left);
+	snd_opti93x_out(chip, OPTi93X_MIXOUT_RIGHT, right);
+	spin_unlock_irqrestore(&chip->lock, flags);
+	return change;
+}
+
+#if 0
+
+#ifdef TARGET_OS2
+#define OPTi93X_SINGLE(xname, xindex, reg, shift, mask, invert) \
+{ SNDRV_CTL_ELEM_IFACE_MIXER, 0, 0, xname, xindex, \
+  0, 0, snd_opti93x_info_single, \
+  snd_opti93x_get_single, snd_opti93x_put_single, \
+  reg | (shift << 8) | (mask << 16) | (invert << 24) }
+#else
+#define OPTi93X_SINGLE(xname, xindex, reg, shift, mask, invert) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
+  info: snd_opti93x_info_single, \
+  get: snd_opti93x_get_single, put: snd_opti93x_put_single, \
+  private_value: reg | (shift << 8) | (mask << 16) | (invert << 24) }
+#endif
+
+static int snd_opti93x_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+
+	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	return 0;
+}
+
+static int snd_opti93x_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	opti93x_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 8) & 0xff;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0xff;
+	
+	spin_lock_irqsave(&chip->lock, flags);
+	ucontrol->value.integer.value[0] = (chip->image[reg] >> shift) & mask;
+	spin_unlock_irqrestore(&chip->lock, flags);
+	if (invert)
+		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+	return 0;
+}
+
+static int snd_opti93x_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	opti93x_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 8) & 0xff;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0xff;
+	int change;
+	unsigned short val;
+	
+	val = (ucontrol->value.integer.value[0] & mask);
+	if (invert)
+		val = mask - val;
+	val <<= shift;
+	spin_lock_irqsave(&chip->lock, flags);
+	val = (chip->image[reg] & ~(mask << shift)) | val;
+	change = val != chip->image[reg];
+	snd_opti93x_out(chip, reg, val);
+	spin_unlock_irqrestore(&chip->lock, flags);
+	return change;
+}
+
+#endif /* single */
+
+#ifdef TARGET_OS2
+#define OPTi93X_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \
+{ SNDRV_CTL_ELEM_IFACE_MIXER, 0, 0, xname, xindex, \
+  0, 0, snd_opti93x_info_double, \
+  snd_opti93x_get_double, snd_opti93x_put_double, \
+  left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
+#else
+#define OPTi93X_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
+  info: snd_opti93x_info_double, \
+  get: snd_opti93x_get_double, put: snd_opti93x_put_double, \
+  private_value: left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
+#endif
+
+#define OPTi93X_DOUBLE_INVERT_INVERT(xctl) \
+	do { xctl.private_value ^= 22; } while (0)
+#define OPTi93X_DOUBLE_CHANGE_REGS(xctl, left_reg, right_reg) \
+	do { xctl.private_value &= ~0x0000ffff; \
+	     xctl.private_value |= left_reg | (right_reg << 8); } while (0)
+
+static int snd_opti93x_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+
+	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	return 0;
+}
+
+static int snd_opti93x_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	opti93x_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int left_reg = kcontrol->private_value & 0xff;
+	int right_reg = (kcontrol->private_value >> 8) & 0xff;
+	int shift_left = (kcontrol->private_value >> 16) & 0x07;
+	int shift_right = (kcontrol->private_value >> 19) & 0x07;
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+	int invert = (kcontrol->private_value >> 22) & 1;
+	
+	spin_lock_irqsave(&chip->lock, flags);
+	ucontrol->value.integer.value[0] = (chip->image[left_reg] >> shift_left) & mask;
+	ucontrol->value.integer.value[1] = (chip->image[right_reg] >> shift_right) & mask;
+	spin_unlock_irqrestore(&chip->lock, flags);
+	if (invert) {
+		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+		ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1];
+	}
+	return 0;
+}
+
+static int snd_opti93x_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	opti93x_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int left_reg = kcontrol->private_value & 0xff;
+	int right_reg = (kcontrol->private_value >> 8) & 0xff;
+	int shift_left = (kcontrol->private_value >> 16) & 0x07;
+	int shift_right = (kcontrol->private_value >> 19) & 0x07;
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+	int invert = (kcontrol->private_value >> 22) & 1;
+	int change;
+	unsigned short val1, val2;
+	
+	val1 = ucontrol->value.integer.value[0] & mask;
+	val2 = ucontrol->value.integer.value[1] & mask;
+	if (invert) {
+		val1 = mask - val1;
+		val2 = mask - val2;
+	}
+	val1 <<= shift_left;
+	val2 <<= shift_right;
+	spin_lock_irqsave(&chip->lock, flags);
+	val1 = (chip->image[left_reg] & ~(mask << shift_left)) | val1;
+	val2 = (chip->image[right_reg] & ~(mask << shift_right)) | val2;
+	change = val1 != chip->image[left_reg] || val2 != chip->image[right_reg];
+	snd_opti93x_out(chip, left_reg, val1);
+	snd_opti93x_out(chip, right_reg, val1);
+	spin_unlock_irqrestore(&chip->lock, flags);
+	return change;
+}
+
+#define OPTi93X_CONTROLS (sizeof(snd_opti93x_controls)/sizeof(snd_kcontrol_new_t))
+
+static snd_kcontrol_new_t snd_opti93x_controls[] = {
+OPTi93X_DOUBLE("Master Playback Switch", 0, OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 7, 7, 1, 1),
+OPTi93X_DOUBLE("Master Playback Volume", 0, OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 0, 0, 31, 1),
+OPTi93X_DOUBLE("PCM Playback Switch", 0, OPTi93X_DAC_LEFT, OPTi93X_DAC_RIGHT, 7, 7, 1, 1),
+OPTi93X_DOUBLE("PCM Playback Volume", 0, OPTi93X_DAC_LEFT, OPTi93X_DAC_RIGHT, 0, 0, 31, 0),
+OPTi93X_DOUBLE("FM Playback Switch", 0, OPTi931_FM_LEFT_INPUT, OPTi931_FM_RIGHT_INPUT, 7, 7, 1, 1),
+OPTi93X_DOUBLE("FM Playback Volume", 0, OPTi931_FM_LEFT_INPUT, OPTi931_FM_RIGHT_INPUT, 0, 0, 15, 1),
+OPTi93X_DOUBLE("Line Playback Switch", 0, OPTi93X_LINE_LEFT_INPUT, OPTi93X_LINE_RIGHT_INPUT, 7, 7, 1, 1),
+OPTi93X_DOUBLE("Line Playback Volume", 0, OPTi93X_LINE_LEFT_INPUT, OPTi93X_LINE_RIGHT_INPUT, 0, 0, 15, 1),
+OPTi93X_DOUBLE("Mic Playback Switch", 0, OPTi93X_MIC_LEFT_INPUT, OPTi93X_MIC_RIGHT_INPUT, 7, 7, 1, 1),
+OPTi93X_DOUBLE("Mic Playback Volume", 0, OPTi93X_MIC_LEFT_INPUT, OPTi93X_MIC_RIGHT_INPUT, 0, 0, 15, 1),
+OPTi93X_DOUBLE("Mic Boost", 0, OPTi93X_MIXOUT_LEFT, OPTi93X_MIXOUT_RIGHT, 5, 5, 1, 1),
+OPTi93X_DOUBLE("CD Playback Switch", 0, OPTi93X_CD_LEFT_INPUT, OPTi93X_CD_RIGHT_INPUT, 7, 7, 1, 1),
+OPTi93X_DOUBLE("CD Playback Volume", 0, OPTi93X_CD_LEFT_INPUT, OPTi93X_CD_RIGHT_INPUT, 0, 0, 15, 1),
+OPTi93X_DOUBLE("Aux Playback Switch", 0, OPTi931_AUX_LEFT_INPUT, OPTi931_AUX_RIGHT_INPUT, 7, 7, 1, 1),
+OPTi93X_DOUBLE("Aux Playback Volume", 0, OPTi931_AUX_LEFT_INPUT, OPTi931_AUX_RIGHT_INPUT, 0, 0, 15, 1),
+OPTi93X_DOUBLE("Capture Volume", 0, OPTi93X_MIXOUT_LEFT, OPTi93X_MIXOUT_RIGHT, 0, 0, 15, 0),
+{
+#ifdef TARGET_OS2
+	SNDRV_CTL_ELEM_IFACE_MIXER, 0, 0, 
+	"Capture Source", 0, 0, 0,
+	snd_opti93x_info_mux,
+	snd_opti93x_get_mux,
+	snd_opti93x_put_mux,
+#else
+	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	name: "Capture Source",
+	info: snd_opti93x_info_mux,
+	get: snd_opti93x_get_mux,
+	put: snd_opti93x_put_mux,
+#endif
+}
+};
+                                        
+int snd_opti93x_mixer(opti93x_t *chip)
+{
+	snd_card_t *card;
+	snd_kcontrol_new_t knew;
+	int err, idx;
+
+	snd_assert(chip != NULL && chip->card != NULL, return -EINVAL);
+	card = chip->card;
+
+	strcpy(card->mixername, snd_opti93x_chip_id(chip));
+
+	for (idx = 0; idx < OPTi93X_CONTROLS; idx++) {
+		knew = snd_opti93x_controls[idx];
+		if (chip->hardware == OPTi9XX_HW_82C930) {
+			if (strstr(knew.name, "FM"))	/* skip FM controls */
+				continue;
+			else if (strcmp(knew.name, "Mic Playback Volume"))
+				OPTi93X_DOUBLE_INVERT_INVERT(knew);
+			else if (strstr(knew.name, "Aux"))
+				OPTi93X_DOUBLE_CHANGE_REGS(knew, OPTi930_AUX_LEFT_INPUT, OPTi930_AUX_RIGHT_INPUT);
+			else if (strcmp(knew.name, "PCM Playback Volume"))
+				OPTi93X_DOUBLE_INVERT_INVERT(knew);
+			else if (strcmp(knew.name, "Master Playback Volume"))
+				OPTi93X_DOUBLE_INVERT_INVERT(knew);
+		}
+		if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_opti93x_controls[idx], chip))) < 0)
+			return err;
+	}
+	return 0;
+}
+
+#endif /* OPTi93X */
+
+static int __init snd_card_opti9xx_detect(snd_card_t *card, opti9xx_t *chip)
+{
+	int i, err;
+	static int opti9xx_mc_size[] = {7, 7, 10, 10, 2, 2, 2};
+
+#ifndef OPTi93X
+	for (i = OPTi9XX_HW_82C928; i < OPTi9XX_HW_82C930; i++) {
+		unsigned char value;
+
+		if ((err = snd_opti9xx_init(chip, i)) < 0)
+			return err;
+		chip->mc_base_size = opti9xx_mc_size[i];
+		if (check_region(chip->mc_base, chip->mc_base_size))
+			continue;
+
+		value = snd_opti9xx_read(chip, OPTi9XX_MC_REG(1));
+		if ((value != 0xff) && (value != inb(chip->mc_base + 1)))
+			if (value == snd_opti9xx_read(chip, OPTi9XX_MC_REG(1)))
+				return 1;
+	}
+#else	/* OPTi93X */
+	for (i = OPTi9XX_HW_82C931; i >= OPTi9XX_HW_82C930; i--) {
+		unsigned long flags;
+		unsigned char value;
+
+		if ((err = snd_opti9xx_init(chip, i)) < 0)
+			return err;
+		chip->mc_base_size = opti9xx_mc_size[i];
+		if (check_region(chip->mc_base, chip->mc_base_size))
+			continue;
+
+		spin_lock_irqsave(&chip->lock, flags);
+		outb(chip->password, chip->mc_base + chip->pwd_reg);
+		outb(((chip->mc_indir_index & (1 << 8)) >> 4) |
+			((chip->mc_indir_index & 0xf0) >> 4), chip->mc_base);
+		spin_unlock_irqrestore(&chip->lock, flags);
+
+		value = snd_opti9xx_read(chip, OPTi9XX_MC_REG(7));
+		snd_opti9xx_write(chip, OPTi9XX_MC_REG(7), 0xff - value);
+		if (snd_opti9xx_read(chip, OPTi9XX_MC_REG(7)) == 0xff - value)
+			return 1;
+	}
+#endif	/* OPTi93X */
+
+	return -ENODEV;
+}
+
+#ifdef __ISAPNP__
+static int __init snd_card_opti9xx_isapnp(opti9xx_t *chip)
+{
+	struct isapnp_dev *pdev = NULL;
+	const struct isapnp_card_id *pid = snd_card_opti9xx_pnpids-1;
+	static struct isapnp_card *card = NULL;
+
+      __again:
+	while (1) {
+		pid++;
+		if (pid->card_vendor == 0)
+			return -ENODEV;
+		if ((card = isapnp_find_card(pid->card_vendor, pid->card_device, card)))
+			break;
+	}
+	if (card == NULL)
+		return -ENODEV;
+
+	chip->dev = isapnp_find_dev(card, pid->devs[0].vendor, pid->devs[0].function, NULL);
+	if (chip->dev == NULL)
+		goto __again;
+
+	chip->devmpu = isapnp_find_dev(card, pid->devs[1].vendor, pid->devs[1].function, NULL);
+
+	pdev = chip->dev;
+	if (pdev->prepare(pdev) < 0)
+		return -EAGAIN;
+
+#ifdef OPTi93X
+	if (snd_port != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[0], snd_port + 4, 4);
+#else
+	if ((pid->card_device != ISAPNP_DEVICE(0x0924)) && (snd_port != SNDRV_AUTO_PORT))
+		isapnp_resource_change(&pdev->resource[1], snd_port, 4);
+#endif	/* OPTi93X */
+	if (snd_irq != SNDRV_AUTO_IRQ)
+		isapnp_resource_change(&pdev->irq_resource[0], snd_irq, 1);
+	if (snd_dma1 != SNDRV_AUTO_DMA)
+		isapnp_resource_change(&pdev->dma_resource[0], snd_dma1, 1);
+#if defined(CS4231) || defined(OPTi93X)
+	if (snd_dma2 != SNDRV_AUTO_DMA)
+		isapnp_resource_change(&pdev->dma_resource[1], snd_dma2, 1);
+#endif	/* CS4231 || OPTi93X */
+	if (snd_fm_port != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[1], snd_fm_port, 4);
+
+	if (pdev->activate(pdev) < 0) {
+		snd_printk("AUDIO isapnp configure failure\n");
+		return -EBUSY;
+	}
+
+#ifdef OPTi93X
+	snd_port = pdev->resource[0].start - 4;
+	snd_fm_port = pdev->resource[1].start;
+#else
+	if (pid->card_device != ISAPNP_DEVICE(0x0924))
+		snd_port = pdev->resource[1].start;
+	snd_fm_port = pdev->resource[2].start;
+#endif	/* OPTi93X */
+	snd_irq = pdev->irq_resource[0].start;
+	snd_dma1 = pdev->dma_resource[0].start;
+#if defined(CS4231) || defined(OPTi93X)
+	snd_dma2 = pdev->dma_resource[1].start;
+#endif	/* CS4231 || OPTi93X */
+
+	pdev = chip->devmpu;
+	if (pdev == NULL || pdev->prepare(pdev) < 0) {
+		snd_mpu_port = -1;
+		chip->devmpu = NULL;
+		return pid->card_device;
+	}
+
+	if (snd_mpu_port != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[0], snd_mpu_port, 2);
+	if (snd_mpu_irq != SNDRV_AUTO_IRQ)
+		isapnp_resource_change(&pdev->irq_resource[0], snd_mpu_irq, 1);
+
+	if (pdev->activate(pdev) < 0) {
+		snd_printk("MPU-401 isapnp configure failure\n");
+		snd_mpu_port = -1;
+		chip->devmpu = NULL;
+	} else {
+		snd_mpu_port = pdev->resource[0].start;
+		snd_mpu_irq = pdev->irq_resource[0].start;
+	}
+	return pid->card_device;
+}
+
+static void snd_card_opti9xx_deactivate(opti9xx_t *chip)
+{
+	if (chip->dev)
+		chip->dev->deactivate(chip->dev);
+	if (chip->devmpu)
+		chip->devmpu->deactivate(chip->devmpu);
+}
+#endif	/* __ISAPNP__ */
+
+#if 0
+static int __init snd_card_opti9xx_resources(struct snd_card_opti9xx *chip,
+					     snd_card_t *card)
+{
+	int error, i, pnp = 0;
+
+#ifdef __ISAPNP__
+	pnp = chip->dev != NULL;
+#endif	/* __ISAPNP__ */
+
+#ifndef OPTi93X
+	if (chip->chip->hardware == OPTi9XX_HW_82C928)
+		snd_mpu_port = -1;
+#endif	/* OPTi93X */
+	error = 0;
+	if (!pnp && (snd_mpu_port == SNDRV_DEFAULT_PORT1)) {
+		for (i = 0; possible_mpu_ports[i] != -1; i++)
+			if (!snd_register_ioport(card, possible_mpu_ports[i], 2,
+					DRIVER_NAME" - MPU-401", NULL)) {
+				snd_mpu_port = possible_mpu_ports[i];
+				break;
+			}
+		if (snd_mpu_port == SNDRV_DEFAULT_PORT1)
+			error = -EBUSY;
+	}
+	else
+		error = (snd_mpu_port == -1) ? -ENODEV :
+			snd_register_ioport(card, snd_mpu_port, 2,
+			DRIVER_NAME" - MPU-401", NULL);
+	if (error)
+		chip->chip->mpu_port = -1;
+	else if (pnp && (snd_irq == snd_mpu_irq))
+		chip->chip->mpu_irq = snd_mpu_irq;
+	else if (!snd_register_interrupt(card,
+			DRIVER_NAME" - MPU-401",
+			snd_mpu_irq, SNDRV_IRQ_TYPE_ISA,
+			snd_card_opti9xx_mpu_interrupt, chip,
+			pnp ? no_alternatives : possible_mpu_irqs,
+			&chip->mpuirqptr)) {
+		chip->chip->mpu_port = snd_mpu_port;
+		chip->chip->mpu_irq = chip->mpuirqptr->irq;
+	}
+	else
+		chip->chip->mpu_port = -1;
+
+	if (!pnp && (snd_port == SNDRV_DEFAULT_PORT1)) {
+		for (i = 0; possible_ports[i] != -1; i++)
+			if (!snd_register_ioport(card, possible_ports[i], 8,
+					DRIVER_NAME" - WSS", NULL)) {
+				snd_port = possible_ports[i];
+				break;
+			}
+		if (snd_port == SNDRV_DEFAULT_PORT1)
+			return -EBUSY;
+	}
+	else if ((error = snd_register_ioport(card, snd_port, 8,
+			DRIVER_NAME" - WSS", NULL)) < 0)
+		return error;
+	chip->chip->wss_base = snd_port;
+	if ((error = snd_register_interrupt(card, DRIVER_NAME" - WSS",
+			snd_irq, SNDRV_IRQ_TYPE_ISA,
+			snd_card_opti9xx_interrupt, chip,
+			pnp ? no_alternatives : possible_irqs,
+			&chip->irqptr)) < 0)
+		return error;
+	chip->chip->irq = chip->irqptr->irq;
+	if ((error = snd_register_dma_channel(card,
+#if defined(CS4231) || defined(OPTi93X)
+			DRIVER_NAME" - WSS playback",
+#else
+			DRIVER_NAME" - WSS",
+#endif	/* CS4231 || OPTi93X */
+			snd_dma1, SNDRV_DMA_TYPE_ISA, snd_dma1_size,
+			pnp ? no_alternatives : possible_dma1s,
+			&chip->dma1ptr)) < 0)
+		return error;
+	chip->chip->dma1 = chip->dma1ptr->dma;
+#if defined(CS4231) || defined(OPTi93X)
+	if ((error = snd_register_dma_channel(card, DRIVER_NAME" - WSS capture",
+			snd_dma2, SNDRV_DMA_TYPE_ISA, snd_dma2_size,
+			pnp ? no_alternatives :
+				possible_dma2s[chip->dma1ptr->dma],
+			&chip->dma2ptr)) < 0)
+		return error;
+	chip->chip->dma2 = chip->dma2ptr->dma;
+#endif	/* CS4231 || OPTi93X */
+
+	if (snd_register_ioport(card,
+			pnp ? snd_fm_port : snd_fm_port = 0x388, 4,
+			DRIVER_NAME" - OPL", NULL) < 0)
+		snd_fm_port = -1;
+	chip->chip->fm_port = snd_fm_port;
+
+	return 0;
+}
+#endif
+
+static void snd_card_opti9xx_free(snd_card_t *card)
+{
+	opti9xx_t *chip = (opti9xx_t *)card->private_data;
+        
+	if (chip) {
+#ifdef __ISAPNP__
+		snd_card_opti9xx_deactivate(chip);
+#endif	/* __ISAPNP__ */
+		if (chip->res_mc_base)
+			release_resource(chip->res_mc_base);
+	}
+}
+
+static int __init snd_card_opti9xx_probe(void)
+{
+	static long possible_ports[] = {0x530, 0xe80, 0xf40, 0x604, -1};
+	static long possible_mpu_ports[] = {0x300, 0x310, 0x320, 0x310, -1};
+#ifdef OPTi93X
+	static int possible_irqs[] = {5, 9, 10, 11, 7, -1};
+#else
+	static int possible_irqs[] = {9, 10, 11, 7, -1};
+#endif	/* OPTi93X */
+	static int possible_mpu_irqs[] = {5, 9, 10, 7, -1};
+	static int possible_dma1s[] = {3, 1, 0, -1};
+#if defined(CS4231) || defined(OPTi93X)
+	static int possible_dma2s[][2] = {{1,-1}, {0,-1}, {-1,-1}, {0,-1}};
+#endif	/* CS4231 || OPTi93X */
+	int error;
+	opti9xx_t *chip;
+#if defined(OPTi93X)
+	opti93x_t *codec;
+#elif defined(CS4231)
+	cs4231_t *codec;
+	snd_timer_t *timer;
+#else
+	ad1848_t *codec;
+#endif
+	snd_card_t *card;
+	snd_pcm_t *pcm;
+	snd_rawmidi_t *rmidi;
+	snd_hwdep_t *synth;
+#ifdef __ISAPNP__
+	int hw;
+#endif	/* __ISAPNP__ */
+
+	if (!(card = snd_card_new(snd_index, snd_id, THIS_MODULE,
+				  sizeof(opti9xx_t))))
+		return -ENOMEM;
+	card->private_free = snd_card_opti9xx_free;
+	chip = (opti9xx_t *)card->private_data;
+
+#ifdef __ISAPNP__
+	if (snd_isapnp && (hw = snd_card_opti9xx_isapnp(chip)) > 0) {
+		switch (hw) {
+		case ISAPNP_DEVICE(0x0924):
+			hw = OPTi9XX_HW_82C924;
+			break;
+		case ISAPNP_DEVICE(0x0925):
+			hw = OPTi9XX_HW_82C925;
+			break;
+		case ISAPNP_DEVICE(0x0931):
+			hw = OPTi9XX_HW_82C931;
+			break;
+		default:
+			snd_card_free(card);
+			return -ENODEV;
+		}
+
+		if ((error = snd_opti9xx_init(chip, hw))) {
+			snd_card_free(card);
+			return error;
+		}
+		if (hw <= OPTi9XX_HW_82C930)
+			chip->mc_base -= 0x80;
+	} else {
+#endif	/* __ISAPNP__ */
+		if ((error = snd_card_opti9xx_detect(card, chip)) < 0) {
+			snd_card_free(card);
+			return error;
+		}
+#ifdef __ISAPNP__
+	}
+#endif	/* __ISAPNP__ */
+
+	if ((chip->res_mc_base = request_region(chip->mc_base, chip->mc_base_size, "OPTi9xx MC")) == NULL) {
+		snd_card_free(card);
+		return -ENOMEM;
+	}
+
+	chip->wss_base = snd_port;
+	chip->fm_port = snd_fm_port;
+	chip->mpu_port = snd_mpu_port;
+	chip->irq = snd_irq;
+	chip->mpu_irq = snd_mpu_irq;
+	chip->dma1 = snd_dma1;
+#if defined(CS4231) || defined(OPTi93X)
+	chip->dma2 = snd_dma2;
+#endif
+
+#ifdef __ISAPNP__
+	if (!snd_isapnp) {
+#endif
+	if (chip->wss_base == SNDRV_AUTO_PORT) {
+		if ((chip->wss_base = snd_legacy_find_free_ioport(possible_ports, 4)) < 0) {
+			snd_card_free(card);
+			snd_printk("unable to find a free WSS port\n");
+			return -EBUSY;
+		}
+	}
+	if (chip->mpu_port == SNDRV_AUTO_PORT) {
+		if ((chip->mpu_port = snd_legacy_find_free_ioport(possible_mpu_ports, 2)) < 0) {
+			snd_card_free(card);
+			snd_printk("unable to find a free MPU401 port\n");
+			return -EBUSY;
+		}
+	}
+	if (chip->irq == SNDRV_AUTO_IRQ) {
+		if ((chip->irq = snd_legacy_find_free_irq(possible_irqs)) < 0) {
+			snd_card_free(card);
+			snd_printk("unable to find a free IRQ\n");
+			return -EBUSY;
+		}
+	}
+	if (chip->mpu_irq == SNDRV_AUTO_IRQ) {
+		if ((chip->mpu_irq = snd_legacy_find_free_irq(possible_mpu_irqs)) < 0) {
+			snd_card_free(card);
+			snd_printk("unable to find a free MPU401 IRQ\n");
+			return -EBUSY;
+		}
+	}
+	if (chip->dma1 == SNDRV_AUTO_DMA) {
+                if ((chip->dma1 = snd_legacy_find_free_dma(possible_dma1s)) < 0) {
+                        snd_card_free(card);
+			snd_printk("unable to find a free DMA1\n");
+			return -EBUSY;
+		}
+        }
+#if defined(CS4231) || defined(OPTi93X)
+	if (chip->dma2 == SNDRV_AUTO_DMA) {
+                if ((chip->dma2 = snd_legacy_find_free_dma(possible_dma2s[chip->dma1 % 4])) < 0) {
+                        snd_card_free(card);
+			snd_printk("unable to find a free DMA2\n");
+			return -EBUSY;
+		}
+        }
+#endif
+
+#ifdef __ISAPNP__
+	}
+#endif
+
+	if ((error = snd_opti9xx_configure(chip))) {
+		snd_card_free(card);
+		return error;
+	}
+
+#if defined(OPTi93X)
+	if ((error = snd_opti93x_create(card, chip, chip->dma1, chip->dma2, &codec))) {
+		snd_card_free(card);
+		return error;
+	}
+	if ((error = snd_opti93x_pcm(codec, 0, &pcm)) < 0) {
+		snd_card_free(card);
+		return error;
+	}
+	if ((error = snd_opti93x_mixer(codec)) < 0) {
+		snd_card_free(card);
+		return error;
+	}
+#elif defined(CS4231)
+	if ((error = snd_cs4231_create(card, chip->wss_base + 4, -1,
+				       chip->irq, chip->dma1, chip->dma2,
+				       CS4231_HW_DETECT,
+				       0,
+				       &codec)) < 0) {
+		snd_card_free(card);
+		return error;
+	}
+	if ((error = snd_cs4231_pcm(codec, 0, &pcm)) < 0) {
+		snd_card_free(card);
+		return error;
+	}
+	if ((error = snd_cs4231_mixer(codec)) < 0) {
+		snd_card_free(card);
+		return error;
+	}
+	if ((error = snd_cs4231_timer(codec, 0, &timer)) < 0) {
+		snd_card_free(card);
+		return error;
+	}
+#else
+	if ((error = snd_ad1848_create(card, chip->wss_base + 4,
+				       chip->irq, chip->dma1,
+				       AD1848_HW_DETECT, &codec)) < 0) {
+		snd_card_free(card);
+		return error;
+	}
+	if ((error = snd_ad1848_pcm(codec, 0, &pcm)) < 0) {
+		snd_card_free(card);
+		return error;
+	}
+	if ((error = snd_ad1848_mixer(codec)) < 0) {
+		snd_card_free(card);
+		return error;
+	}
+#endif
+
+	if (chip->mpu_port <= 0)
+		rmidi = NULL;
+	else
+		if ((error = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
+				chip->mpu_port, 0, chip->mpu_irq, SA_INTERRUPT,
+				&rmidi)))
+			snd_printk("no MPU-401 device at 0x%lx?\n", chip->mpu_port);
+
+	if (chip->fm_port > 0) {
+		opl3_t *opl3;
+		if (snd_opl3_create(card,
+				    chip->fm_port,
+				    chip->fm_port + 2,
+				    OPL3_HW_AUTO, 0, &opl3) < 0) {
+			snd_printk("no OPL device at 0x%lx-0x%lx\n",
+				chip->fm_port, chip->fm_port + 4 - 1);
+		} else {
+			if ((error = snd_opl3_timer_new(opl3,
+#ifdef CS4231
+							1, 2)) < 0) {
+#else
+							0, 1)) < 0) {
+#endif	/* CS4231 */
+				snd_card_free(card);
+				return error;
+			}
+			if ((error = snd_opl3_hwdep_new(opl3, 0, 1, &synth)) < 0) {
+				snd_card_free(card);
+				return error;
+			}
+		}
+	}
+
+	strcpy(card->driver, chip->name);
+	sprintf(card->shortname, "OPTi %s", card->driver);
+#if defined(CS4231) || defined(OPTi93X)
+	sprintf(card->longname, "%s soundcard, %s at 0x%lx, irq %d, dma %d&%d",
+		card->shortname, pcm->name, chip->wss_base + 4,
+		chip->irq, chip->dma1, chip->dma2);
+#else
+	sprintf(card->longname, "%s soundcard, %s at 0x%lx, irq %d, dma %d",
+		card->shortname, pcm->name, chip->wss_base + 4,
+		chip->irq, chip->dma1);
+#endif	/* CS4231 || OPTi93X */
+	if ((error = snd_card_register(card))) {
+		snd_card_free(card);
+		return error;
+	}
+	snd_opti9xx_card = card;
+	return 0;
+}
+
+static int __init alsa_card_opti9xx_init(void)
+{
+	int error;
+
+	if ((error = snd_card_opti9xx_probe())) {
+#ifdef MODULE
+#ifdef OPTi93X
+		snd_printk("no OPTi 82C93x soundcard found\n");
+#else
+		snd_printk("no OPTi 82C92x soundcard found\n");
+#endif	/* OPTi93X */
+#endif
+		return error;
+	}
+	return 0;
+}
+
+static void __exit alsa_card_opti9xx_exit(void)
+{
+	if (snd_opti9xx_card)
+		snd_card_free(snd_opti9xx_card);
+}
+
+module_init(alsa_card_opti9xx_init)
+module_exit(alsa_card_opti9xx_exit)
+
+#ifndef MODULE
+
+/* format is: snd-card-opti9xx=snd_enable,snd_index,snd_id,snd_isapnp,
+			       snd_port,snd_mpu_port,snd_fm_port,
+			       snd_irq,snd_mpu_irq,
+			       snd_dma1,[snd_dma2] */
+
+static int __init alsa_card_opti9xx_setup(char *str)
+{
+	int __attribute__ ((__unused__)) enable = 1;
+	int __attribute__ ((__unused__)) pnp = INT_MAX;
+
+	(void)(get_option(&str,&enable) == 2 &&
+	       get_option(&str,&snd_index) == 2 &&
+	       get_id(&str,&snd_id) == 2 &&
+	       get_option(&str,&pnp) == 2 &&
+	       get_option(&str,(int *)&snd_port) == 2 &&
+	       get_option(&str,(int *)&snd_mpu_port) == 2 &&
+	       get_option(&str,(int *)&snd_fm_port) == 2 &&
+	       get_option(&str,&snd_irq) == 2 &&
+	       get_option(&str,&snd_mpu_irq) == 2 &&
+	       get_option(&str,&snd_dma1) == 2
+#if defined(CS4231) || defined(OPTi93X)
+	       &&
+	       get_option(&str,&snd_dma2) == 2
+#endif
+	       );
+#ifdef __ISAPNP__
+	if (pnp != INT_MAX)
+		snd_isapnp = pnp;
+#endif
+	return 1;
+}
+
+#if defined(OPTi93X)
+__setup("snd-card-opti93x=", alsa_card_opti9xx_setup);
+#elif defined(CS4231)
+__setup("snd-card-opti92x-cs4231=", alsa_card_opti9xx_setup);
+#else
+__setup("snd-card-opti92x-ad1848=", alsa_card_opti9xx_setup);
+#endif
+
+#endif /* ifndef MODULE */
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/opti9xx/opti92x-cs4231.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/opti9xx/opti92x-cs4231.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/opti9xx/opti92x-cs4231.c	(revision 91)
@@ -0,0 +1,2 @@
+#define CS4231
+#include "opti92x-ad1848.c"
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/opti9xx/opti93x.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/opti9xx/opti93x.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/opti9xx/opti93x.c	(revision 91)
@@ -0,0 +1,3 @@
+#define OPTi93X
+#include "opti92x-ad1848.c"
+
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/sb/emu8000.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/sb/emu8000.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/sb/emu8000.c	(revision 91)
@@ -0,0 +1,1242 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *     and (c) 1999 Steve Ratcliffe <steve@parabola.demon.co.uk>
+ *  Copyright (C) 1999-2000 Takashi Iwai <tiwai@suse.de>
+ *
+ *  Routines for control of EMU8000 chip
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define SNDRV_MAIN_OBJECT_FILE
+#include <sound/emu8000.h>
+#include <sound/emu8000_reg.h>
+#include <sound/control.h>
+#include <sound/initval.h>
+
+MODULE_CLASSES("{sound}");
+MODULE_AUTHOR("Takashi Iwai, Steve Ratcliffe");
+
+/*
+ * emu8000 register controls
+ */
+
+/*
+ * The following routines read and write registers on the emu8000.  They
+ * should always be called via the EMU8000*READ/WRITE macros and never
+ * directly.  The macros handle the port number and command word.
+ */
+/* Write a word */
+void snd_emu8000_poke(emu8000_t *emu, unsigned int port, unsigned int reg, unsigned int val)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&emu->reg_lock, flags);
+	if (reg != emu->last_reg) {
+		outw((unsigned short)reg, EMU8000_PTR(emu)); /* Set register */
+		emu->last_reg = reg;
+	}
+	outw((unsigned short)val, port); /* Send data */
+	spin_unlock_irqrestore(&emu->reg_lock, flags);
+}
+
+/* Read a word */
+unsigned short snd_emu8000_peek(emu8000_t *emu, unsigned int port, unsigned int reg)
+{
+	unsigned short res;
+	unsigned long flags;
+	spin_lock_irqsave(&emu->reg_lock, flags);
+	if (reg != emu->last_reg) {
+		outw((unsigned short)reg, EMU8000_PTR(emu)); /* Set register */
+		emu->last_reg = reg;
+	}
+	res = inw(port);	/* Read data */
+	spin_unlock_irqrestore(&emu->reg_lock, flags);
+	return res;
+}
+
+/* Write a double word */
+void snd_emu8000_poke_dw(emu8000_t *emu, unsigned int port, unsigned int reg, unsigned int val)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&emu->reg_lock, flags);
+	if (reg != emu->last_reg) {
+		outw((unsigned short)reg, EMU8000_PTR(emu)); /* Set register */
+		emu->last_reg = reg;
+	}
+	outw((unsigned short)val, port); /* Send low word of data */
+	outw((unsigned short)(val>>16), port+2); /* Send high word of data */
+	spin_unlock_irqrestore(&emu->reg_lock, flags);
+}
+
+/* Read a double word */
+unsigned int snd_emu8000_peek_dw(emu8000_t *emu, unsigned int port, unsigned int reg)
+{
+	unsigned short low;
+	unsigned int res;
+	unsigned long flags;
+	spin_lock_irqsave(&emu->reg_lock, flags);
+	if (reg != emu->last_reg) {
+		outw((unsigned short)reg, EMU8000_PTR(emu)); /* Set register */
+		emu->last_reg = reg;
+	}
+	low = inw(port);	/* Read low word of data */
+	res = low + (inw(port+2) << 16);
+	spin_unlock_irqrestore(&emu->reg_lock, flags);
+	return res;
+}
+
+/*
+ * Set up / close a channel to be used for DMA.
+ */
+/*exported*/ void
+snd_emu8000_dma_chan(emu8000_t *emu, int ch, int mode)
+{
+	if (mode == EMU8000_RAM_CLOSE) {
+		EMU8000_CCCA_WRITE(emu, ch, 0);
+		EMU8000_DCYSUSV_WRITE(emu, ch, 0x807F);
+		return;
+	}
+	EMU8000_DCYSUSV_WRITE(emu, ch, 0x80);
+	EMU8000_VTFT_WRITE(emu, ch, 0);
+	EMU8000_CVCF_WRITE(emu, ch, 0);
+	EMU8000_PTRX_WRITE(emu, ch, 0x40000000);
+	EMU8000_CPF_WRITE(emu, ch, 0x40000000);
+	EMU8000_PSST_WRITE(emu, ch, 0);
+	EMU8000_CSL_WRITE(emu, ch, 0);
+	if (mode == EMU8000_RAM_WRITE) /* DMA write */
+		EMU8000_CCCA_WRITE(emu, ch, 0x06000000);
+	else	   /* DMA read */
+		EMU8000_CCCA_WRITE(emu, ch, 0x04000000);
+}
+
+/*
+ */
+static void /*__init*/
+snd_emu8000_read_wait(emu8000_t *emu)
+{
+	while ((EMU8000_SMALR_READ(emu) & 0x80000000) != 0) {
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule_timeout(1);
+		if (signal_pending(current))
+			break;
+	}
+}
+
+/*
+ */
+static void /*__init*/
+snd_emu8000_write_wait(emu8000_t *emu)
+{
+	while ((EMU8000_SMALW_READ(emu) & 0x80000000) != 0) {
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule_timeout(1);
+		if (signal_pending(current))
+			break;
+	}
+}
+
+/*
+ * detect a card at the given port
+ */
+static int /*__init*/
+snd_emu8000_detect(emu8000_t *emu)
+{
+	/* Initialise */
+	EMU8000_HWCF1_WRITE(emu, 0x0059);
+	EMU8000_HWCF2_WRITE(emu, 0x0020);
+	EMU8000_HWCF3_WRITE(emu, 0x0000);
+	/* Check for a recognisable emu8000 */
+	/*
+	if ((EMU8000_U1_READ(emu) & 0x000f) != 0x000c)
+		return -ENODEV;
+		*/
+	if ((EMU8000_HWCF1_READ(emu) & 0x007e) != 0x0058)
+		return -ENODEV;
+	if ((EMU8000_HWCF2_READ(emu) & 0x0003) != 0x0003)
+		return -ENODEV;
+
+	snd_printdd("EMU8000 [0x%lx]: Synth chip found\n",
+                    emu->port1);
+	return 0;
+}
+
+
+/*
+ * intiailize audio channels
+ */
+static void /*__init*/
+init_audio(emu8000_t *emu)
+{
+	int ch;
+
+	/* turn off envelope engines */
+	for (ch = 0; ch < EMU8000_CHANNELS; ch++)
+		EMU8000_DCYSUSV_WRITE(emu, ch, 0x80);
+  
+	/* reset all other parameters to zero */
+	for (ch = 0; ch < EMU8000_CHANNELS; ch++) {
+		EMU8000_ENVVOL_WRITE(emu, ch, 0);
+		EMU8000_ENVVAL_WRITE(emu, ch, 0);
+		EMU8000_DCYSUS_WRITE(emu, ch, 0);
+		EMU8000_ATKHLDV_WRITE(emu, ch, 0);
+		EMU8000_LFO1VAL_WRITE(emu, ch, 0);
+		EMU8000_ATKHLD_WRITE(emu, ch, 0);
+		EMU8000_LFO2VAL_WRITE(emu, ch, 0);
+		EMU8000_IP_WRITE(emu, ch, 0);
+		EMU8000_IFATN_WRITE(emu, ch, 0);
+		EMU8000_PEFE_WRITE(emu, ch, 0);
+		EMU8000_FMMOD_WRITE(emu, ch, 0);
+		EMU8000_TREMFRQ_WRITE(emu, ch, 0);
+		EMU8000_FM2FRQ2_WRITE(emu, ch, 0);
+		EMU8000_PTRX_WRITE(emu, ch, 0);
+		EMU8000_VTFT_WRITE(emu, ch, 0);
+		EMU8000_PSST_WRITE(emu, ch, 0);
+		EMU8000_CSL_WRITE(emu, ch, 0);
+		EMU8000_CCCA_WRITE(emu, ch, 0);
+	}
+
+	for (ch = 0; ch < EMU8000_CHANNELS; ch++) {
+		EMU8000_CPF_WRITE(emu, ch, 0);
+		EMU8000_CVCF_WRITE(emu, ch, 0);
+	}
+}
+
+
+/*
+ * initialize DMA address
+ */
+static void /*__init*/
+init_dma(emu8000_t *emu)
+{
+	EMU8000_SMALR_WRITE(emu, 0);
+	EMU8000_SMARR_WRITE(emu, 0);
+	EMU8000_SMALW_WRITE(emu, 0);
+	EMU8000_SMARW_WRITE(emu, 0);
+}
+
+/*
+ * initialization arrays; from ADIP
+ */
+static unsigned short init1[128] /*__devinitdata*/ = {
+	0x03ff, 0x0030,  0x07ff, 0x0130, 0x0bff, 0x0230,  0x0fff, 0x0330,
+	0x13ff, 0x0430,  0x17ff, 0x0530, 0x1bff, 0x0630,  0x1fff, 0x0730,
+	0x23ff, 0x0830,  0x27ff, 0x0930, 0x2bff, 0x0a30,  0x2fff, 0x0b30,
+	0x33ff, 0x0c30,  0x37ff, 0x0d30, 0x3bff, 0x0e30,  0x3fff, 0x0f30,
+
+	0x43ff, 0x0030,  0x47ff, 0x0130, 0x4bff, 0x0230,  0x4fff, 0x0330,
+	0x53ff, 0x0430,  0x57ff, 0x0530, 0x5bff, 0x0630,  0x5fff, 0x0730,
+	0x63ff, 0x0830,  0x67ff, 0x0930, 0x6bff, 0x0a30,  0x6fff, 0x0b30,
+	0x73ff, 0x0c30,  0x77ff, 0x0d30, 0x7bff, 0x0e30,  0x7fff, 0x0f30,
+
+	0x83ff, 0x0030,  0x87ff, 0x0130, 0x8bff, 0x0230,  0x8fff, 0x0330,
+	0x93ff, 0x0430,  0x97ff, 0x0530, 0x9bff, 0x0630,  0x9fff, 0x0730,
+	0xa3ff, 0x0830,  0xa7ff, 0x0930, 0xabff, 0x0a30,  0xafff, 0x0b30,
+	0xb3ff, 0x0c30,  0xb7ff, 0x0d30, 0xbbff, 0x0e30,  0xbfff, 0x0f30,
+
+	0xc3ff, 0x0030,  0xc7ff, 0x0130, 0xcbff, 0x0230,  0xcfff, 0x0330,
+	0xd3ff, 0x0430,  0xd7ff, 0x0530, 0xdbff, 0x0630,  0xdfff, 0x0730,
+	0xe3ff, 0x0830,  0xe7ff, 0x0930, 0xebff, 0x0a30,  0xefff, 0x0b30,
+	0xf3ff, 0x0c30,  0xf7ff, 0x0d30, 0xfbff, 0x0e30,  0xffff, 0x0f30,
+};
+
+static unsigned short init2[128] /*__devinitdata*/ = {
+	0x03ff, 0x8030, 0x07ff, 0x8130, 0x0bff, 0x8230, 0x0fff, 0x8330,
+	0x13ff, 0x8430, 0x17ff, 0x8530, 0x1bff, 0x8630, 0x1fff, 0x8730,
+	0x23ff, 0x8830, 0x27ff, 0x8930, 0x2bff, 0x8a30, 0x2fff, 0x8b30,
+	0x33ff, 0x8c30, 0x37ff, 0x8d30, 0x3bff, 0x8e30, 0x3fff, 0x8f30,
+
+	0x43ff, 0x8030, 0x47ff, 0x8130, 0x4bff, 0x8230, 0x4fff, 0x8330,
+	0x53ff, 0x8430, 0x57ff, 0x8530, 0x5bff, 0x8630, 0x5fff, 0x8730,
+	0x63ff, 0x8830, 0x67ff, 0x8930, 0x6bff, 0x8a30, 0x6fff, 0x8b30,
+	0x73ff, 0x8c30, 0x77ff, 0x8d30, 0x7bff, 0x8e30, 0x7fff, 0x8f30,
+
+	0x83ff, 0x8030, 0x87ff, 0x8130, 0x8bff, 0x8230, 0x8fff, 0x8330,
+	0x93ff, 0x8430, 0x97ff, 0x8530, 0x9bff, 0x8630, 0x9fff, 0x8730,
+	0xa3ff, 0x8830, 0xa7ff, 0x8930, 0xabff, 0x8a30, 0xafff, 0x8b30,
+	0xb3ff, 0x8c30, 0xb7ff, 0x8d30, 0xbbff, 0x8e30, 0xbfff, 0x8f30,
+
+	0xc3ff, 0x8030, 0xc7ff, 0x8130, 0xcbff, 0x8230, 0xcfff, 0x8330,
+	0xd3ff, 0x8430, 0xd7ff, 0x8530, 0xdbff, 0x8630, 0xdfff, 0x8730,
+	0xe3ff, 0x8830, 0xe7ff, 0x8930, 0xebff, 0x8a30, 0xefff, 0x8b30,
+	0xf3ff, 0x8c30, 0xf7ff, 0x8d30, 0xfbff, 0x8e30, 0xffff, 0x8f30,
+};
+
+static unsigned short init3[128] /*__devinitdata*/ = {
+	0x0C10, 0x8470, 0x14FE, 0xB488, 0x167F, 0xA470, 0x18E7, 0x84B5,
+	0x1B6E, 0x842A, 0x1F1D, 0x852A, 0x0DA3, 0x8F7C, 0x167E, 0xF254,
+	0x0000, 0x842A, 0x0001, 0x852A, 0x18E6, 0x8BAA, 0x1B6D, 0xF234,
+	0x229F, 0x8429, 0x2746, 0x8529, 0x1F1C, 0x86E7, 0x229E, 0xF224,
+
+	0x0DA4, 0x8429, 0x2C29, 0x8529, 0x2745, 0x87F6, 0x2C28, 0xF254,
+	0x383B, 0x8428, 0x320F, 0x8528, 0x320E, 0x8F02, 0x1341, 0xF264,
+	0x3EB6, 0x8428, 0x3EB9, 0x8528, 0x383A, 0x8FA9, 0x3EB5, 0xF294,
+	0x3EB7, 0x8474, 0x3EBA, 0x8575, 0x3EB8, 0xC4C3, 0x3EBB, 0xC5C3,
+
+	0x0000, 0xA404, 0x0001, 0xA504, 0x141F, 0x8671, 0x14FD, 0x8287,
+	0x3EBC, 0xE610, 0x3EC8, 0x8C7B, 0x031A, 0x87E6, 0x3EC8, 0x86F7,
+	0x3EC0, 0x821E, 0x3EBE, 0xD208, 0x3EBD, 0x821F, 0x3ECA, 0x8386,
+	0x3EC1, 0x8C03, 0x3EC9, 0x831E, 0x3ECA, 0x8C4C, 0x3EBF, 0x8C55,
+
+	0x3EC9, 0xC208, 0x3EC4, 0xBC84, 0x3EC8, 0x8EAD, 0x3EC8, 0xD308,
+	0x3EC2, 0x8F7E, 0x3ECB, 0x8219, 0x3ECB, 0xD26E, 0x3EC5, 0x831F,
+	0x3EC6, 0xC308, 0x3EC3, 0xB2FF, 0x3EC9, 0x8265, 0x3EC9, 0x8319,
+	0x1342, 0xD36E, 0x3EC7, 0xB3FF, 0x0000, 0x8365, 0x1420, 0x9570,
+};
+
+static unsigned short init4[128] /*__devinitdata*/ = {
+	0x0C10, 0x8470, 0x14FE, 0xB488, 0x167F, 0xA470, 0x18E7, 0x84B5,
+	0x1B6E, 0x842A, 0x1F1D, 0x852A, 0x0DA3, 0x0F7C, 0x167E, 0x7254,
+	0x0000, 0x842A, 0x0001, 0x852A, 0x18E6, 0x0BAA, 0x1B6D, 0x7234,
+	0x229F, 0x8429, 0x2746, 0x8529, 0x1F1C, 0x06E7, 0x229E, 0x7224,
+
+	0x0DA4, 0x8429, 0x2C29, 0x8529, 0x2745, 0x07F6, 0x2C28, 0x7254,
+	0x383B, 0x8428, 0x320F, 0x8528, 0x320E, 0x0F02, 0x1341, 0x7264,
+	0x3EB6, 0x8428, 0x3EB9, 0x8528, 0x383A, 0x0FA9, 0x3EB5, 0x7294,
+	0x3EB7, 0x8474, 0x3EBA, 0x8575, 0x3EB8, 0x44C3, 0x3EBB, 0x45C3,
+
+	0x0000, 0xA404, 0x0001, 0xA504, 0x141F, 0x0671, 0x14FD, 0x0287,
+	0x3EBC, 0xE610, 0x3EC8, 0x0C7B, 0x031A, 0x07E6, 0x3EC8, 0x86F7,
+	0x3EC0, 0x821E, 0x3EBE, 0xD208, 0x3EBD, 0x021F, 0x3ECA, 0x0386,
+	0x3EC1, 0x0C03, 0x3EC9, 0x031E, 0x3ECA, 0x8C4C, 0x3EBF, 0x0C55,
+
+	0x3EC9, 0xC208, 0x3EC4, 0xBC84, 0x3EC8, 0x0EAD, 0x3EC8, 0xD308,
+	0x3EC2, 0x8F7E, 0x3ECB, 0x0219, 0x3ECB, 0xD26E, 0x3EC5, 0x031F,
+	0x3EC6, 0xC308, 0x3EC3, 0x32FF, 0x3EC9, 0x0265, 0x3EC9, 0x8319,
+	0x1342, 0xD36E, 0x3EC7, 0x33FF, 0x0000, 0x8365, 0x1420, 0x9570,
+};
+
+/* send an initialization array
+ * Taken from the oss driver, not obvious from the doc how this
+ * is meant to work
+ */
+static void /*__init*/
+send_array(emu8000_t *emu, unsigned short *data, int size)
+{
+	int i;
+	unsigned short *p;
+
+	p = data;
+	for (i = 0; i < size; i++, p++)
+		EMU8000_INIT1_WRITE(emu, i, *p);
+	for (i = 0; i < size; i++, p++)
+		EMU8000_INIT2_WRITE(emu, i, *p);
+	for (i = 0; i < size; i++, p++)
+		EMU8000_INIT3_WRITE(emu, i, *p);
+	for (i = 0; i < size; i++, p++)
+		EMU8000_INIT4_WRITE(emu, i, *p);
+}
+
+#define NELEM(arr) (sizeof(arr)/sizeof((arr)[0]))
+
+
+/*
+ * Send initialization arrays to start up, this just follows the
+ * initialisation sequence in the adip.
+ */
+static void /*__init*/
+init_arrays(emu8000_t *emu)
+{
+	send_array(emu, init1, NELEM(init1)/4);
+
+	set_current_state(TASK_INTERRUPTIBLE);
+	schedule_timeout((HZ * (44099 + 1024)) / 44100); /* wait for 1024 clocks */
+	send_array(emu, init2, NELEM(init2)/4);
+	send_array(emu, init3, NELEM(init3)/4);
+
+	EMU8000_HWCF4_WRITE(emu, 0);
+	EMU8000_HWCF5_WRITE(emu, 0x83);
+	EMU8000_HWCF6_WRITE(emu, 0x8000);
+
+	send_array(emu, init4, NELEM(init4)/4);
+}
+
+
+#define UNIQUE_ID1	0xa5b9
+#define UNIQUE_ID2	0x9d53
+
+/*
+ * Size the onboard memory.
+ * This is written so as not to need arbitary delays after the write. It
+ * seems that the only way to do this is to use the one channel and keep
+ * reallocating between read and write.
+ */
+static void /*__init*/
+size_dram(emu8000_t *emu)
+{
+	int i, size;
+
+	if (emu->dram_checked)
+		return;
+
+	size = 0;
+
+	/* write out a magic number */
+	snd_emu8000_dma_chan(emu, 0, EMU8000_RAM_WRITE);
+	snd_emu8000_dma_chan(emu, 1, EMU8000_RAM_READ);
+	EMU8000_SMALW_WRITE(emu, EMU8000_DRAM_OFFSET);
+	EMU8000_SMLD_WRITE(emu, UNIQUE_ID1);
+	snd_emu8000_init_fm(emu); /* This must really be here and not 2 lines back even */
+
+	while (size < EMU8000_MAX_DRAM) {
+
+		size += 512 * 1024;  /* increment 512kbytes */
+
+		/* Write a unique data on the test address.
+		 * if the address is out of range, the data is written on
+		 * 0x200000(=EMU8000_DRAM_OFFSET).  Then the id word is
+		 * changed by this data.
+		 */
+		/*snd_emu8000_dma_chan(emu, 0, EMU8000_RAM_WRITE);*/
+		EMU8000_SMALW_WRITE(emu, EMU8000_DRAM_OFFSET + (size>>1));
+		EMU8000_SMLD_WRITE(emu, UNIQUE_ID2);
+		snd_emu8000_write_wait(emu);
+
+		/*
+		 * read the data on the just written DRAM address
+		 * if not the same then we have reached the end of ram.
+		 */
+		/*snd_emu8000_dma_chan(emu, 0, EMU8000_RAM_READ);*/
+		EMU8000_SMALR_WRITE(emu, EMU8000_DRAM_OFFSET + (size>>1));
+		/*snd_emu8000_read_wait(emu);*/
+		EMU8000_SMLD_READ(emu); /* discard stale data  */
+		if (EMU8000_SMLD_READ(emu) != UNIQUE_ID2)
+			break; /* we must have wrapped around */
+
+		snd_emu8000_read_wait(emu);
+
+		/*
+		 * If it is the same it could be that the address just
+		 * wraps back to the beginning; so check to see if the
+		 * initial value has been overwritten.
+		 */
+		EMU8000_SMALR_WRITE(emu, EMU8000_DRAM_OFFSET);
+		EMU8000_SMLD_READ(emu); /* discard stale data  */
+		if (EMU8000_SMLD_READ(emu) != UNIQUE_ID1)
+			break; /* we must have wrapped around */
+		snd_emu8000_read_wait(emu);
+	}
+
+	/* wait until FULL bit in SMAxW register is false */
+	for (i = 0; i < 10000; i++) {
+		if ((EMU8000_SMALW_READ(emu) & 0x80000000) == 0)
+			break;
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule_timeout(1);
+		if (signal_pending(current))
+			break;
+	}
+	snd_emu8000_dma_chan(emu, 0, EMU8000_RAM_CLOSE);
+	snd_emu8000_dma_chan(emu, 1, EMU8000_RAM_CLOSE);
+
+	snd_printdd("EMU8000 [0x%lx]: %d Kb on-board memory detected\n",
+		    emu->port1, size/1024);
+
+	emu->mem_size = size;
+	emu->dram_checked = 1;
+}
+
+
+/*
+ * Initiailise the FM section.  You have to do this to use sample RAM
+ * and therefore lose 2 voices.
+ */
+/*exported*/ void
+snd_emu8000_init_fm(emu8000_t *emu)
+{
+	unsigned long flags;
+
+	/* Initialize the last two channels for DRAM refresh and producing
+	   the reverb and chorus effects for Yamaha OPL-3 synthesizer */
+
+	/* 31: FM left channel, 0xffffe0-0xffffe8 */
+	EMU8000_DCYSUSV_WRITE(emu, 30, 0x80);
+	EMU8000_PSST_WRITE(emu, 30, 0xFFFFFFE0); /* full left */
+	EMU8000_CSL_WRITE(emu, 30, 0x00FFFFE8 | (emu->fm_chorus_depth << 24));
+	EMU8000_PTRX_WRITE(emu, 30, (emu->fm_reverb_depth << 8));
+	EMU8000_CPF_WRITE(emu, 30, 0);
+	EMU8000_CCCA_WRITE(emu, 30, 0x00FFFFE3);
+
+	/* 32: FM right channel, 0xfffff0-0xfffff8 */
+	EMU8000_DCYSUSV_WRITE(emu, 31, 0x80);
+	EMU8000_PSST_WRITE(emu, 31, 0x00FFFFF0); /* full right */
+	EMU8000_CSL_WRITE(emu, 31, 0x00FFFFF8 | (emu->fm_chorus_depth << 24));
+	EMU8000_PTRX_WRITE(emu, 31, (emu->fm_reverb_depth << 8));
+	EMU8000_CPF_WRITE(emu, 31, 0x8000);
+	EMU8000_CCCA_WRITE(emu, 31, 0x00FFFFF3);
+
+	snd_emu8000_poke((emu), EMU8000_DATA0(emu), EMU8000_CMD(1, (30)), 0);
+
+	spin_lock_irqsave(&emu->reg_lock, flags);
+	while (!(inw(EMU8000_PTR(emu)) & 0x1000))
+		;
+	while ((inw(EMU8000_PTR(emu)) & 0x1000))
+		;
+	spin_unlock_irqrestore(&emu->reg_lock, flags);
+	snd_emu8000_poke((emu), EMU8000_DATA0(emu), EMU8000_CMD(1, (30)), 0x4828);
+	/* this is really odd part.. */
+	outb(0x3C, EMU8000_PTR(emu));
+	outb(0, EMU8000_DATA1(emu));
+
+	/* skew volume & cutoff */
+	EMU8000_VTFT_WRITE(emu, 30, 0x8000FFFF);
+	EMU8000_VTFT_WRITE(emu, 31, 0x8000FFFF);
+}
+
+
+/*
+ * The main initialization routine.
+ */
+static void /*__init*/
+snd_emu8000_init_hw(emu8000_t *emu)
+{
+	int i;
+
+	emu->last_reg = 0xffff; /* reset the last register index */
+
+	/* initialize hardware configuration */
+	EMU8000_HWCF1_WRITE(emu, 0x0059);
+	EMU8000_HWCF2_WRITE(emu, 0x0020);
+
+	/* disable audio; this seems to reduce a clicking noise a bit.. */
+	EMU8000_HWCF3_WRITE(emu, 0);
+
+	/* initialize audio channels */
+	init_audio(emu);
+
+	/* initialize DMA */
+	init_dma(emu);
+
+	/* initialize init arrays */
+	init_arrays(emu);
+
+	/*
+	 * Initialize the FM section of the AWE32, this is needed
+	 * for DRAM refresh as well
+	 */
+	snd_emu8000_init_fm(emu);
+
+	/* terminate all voices */
+	for (i = 0; i < EMU8000_DRAM_VOICES; i++)
+		EMU8000_DCYSUSV_WRITE(emu, 0, 0x807F);
+	
+	/* check DRAM memory size */
+	size_dram(emu);
+
+	/* enable audio */
+	EMU8000_HWCF3_WRITE(emu, 0x4);
+
+	/* set equzlier, chorus and reverb modes */
+	snd_emu8000_update_equalizer(emu);
+	snd_emu8000_update_chorus_mode(emu);
+	snd_emu8000_update_reverb_mode(emu);
+}
+
+
+/*----------------------------------------------------------------
+ * Bass/Treble Equalizer
+ *----------------------------------------------------------------*/
+
+static unsigned short bass_parm[12][3] = {
+	{0xD26A, 0xD36A, 0x0000}, /* -12 dB */
+	{0xD25B, 0xD35B, 0x0000}, /*  -8 */
+	{0xD24C, 0xD34C, 0x0000}, /*  -6 */
+	{0xD23D, 0xD33D, 0x0000}, /*  -4 */
+	{0xD21F, 0xD31F, 0x0000}, /*  -2 */
+	{0xC208, 0xC308, 0x0001}, /*   0 (HW default) */
+	{0xC219, 0xC319, 0x0001}, /*  +2 */
+	{0xC22A, 0xC32A, 0x0001}, /*  +4 */
+	{0xC24C, 0xC34C, 0x0001}, /*  +6 */
+	{0xC26E, 0xC36E, 0x0001}, /*  +8 */
+	{0xC248, 0xC384, 0x0002}, /* +10 */
+	{0xC26A, 0xC36A, 0x0002}, /* +12 dB */
+};
+
+static unsigned short treble_parm[12][9] = {
+	{0x821E, 0xC26A, 0x031E, 0xC36A, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001}, /* -12 dB */
+	{0x821E, 0xC25B, 0x031E, 0xC35B, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001},
+	{0x821E, 0xC24C, 0x031E, 0xC34C, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001},
+	{0x821E, 0xC23D, 0x031E, 0xC33D, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001},
+	{0x821E, 0xC21F, 0x031E, 0xC31F, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001},
+	{0x821E, 0xD208, 0x031E, 0xD308, 0x021E, 0xD208, 0x831E, 0xD308, 0x0002},
+	{0x821E, 0xD208, 0x031E, 0xD308, 0x021D, 0xD219, 0x831D, 0xD319, 0x0002},
+	{0x821E, 0xD208, 0x031E, 0xD308, 0x021C, 0xD22A, 0x831C, 0xD32A, 0x0002},
+	{0x821E, 0xD208, 0x031E, 0xD308, 0x021A, 0xD24C, 0x831A, 0xD34C, 0x0002},
+	{0x821E, 0xD208, 0x031E, 0xD308, 0x0219, 0xD26E, 0x8319, 0xD36E, 0x0002}, /* +8 (HW default) */
+	{0x821D, 0xD219, 0x031D, 0xD319, 0x0219, 0xD26E, 0x8319, 0xD36E, 0x0002},
+	{0x821C, 0xD22A, 0x031C, 0xD32A, 0x0219, 0xD26E, 0x8319, 0xD36E, 0x0002}  /* +12 dB */
+};
+
+
+/*
+ * set Emu8000 digital equalizer; from 0 to 11 [-12dB - 12dB]
+ */
+/*exported*/ void
+snd_emu8000_update_equalizer(emu8000_t *emu)
+{
+	unsigned short w;
+	int bass = emu->bass_level;
+	int treble = emu->treble_level;
+
+	if (bass < 0 || bass > 11 || treble < 0 || treble > 11)
+		return;
+	EMU8000_INIT4_WRITE(emu, 0x01, bass_parm[bass][0]);
+	EMU8000_INIT4_WRITE(emu, 0x11, bass_parm[bass][1]);
+	EMU8000_INIT3_WRITE(emu, 0x11, treble_parm[treble][0]);
+	EMU8000_INIT3_WRITE(emu, 0x13, treble_parm[treble][1]);
+	EMU8000_INIT3_WRITE(emu, 0x1b, treble_parm[treble][2]);
+	EMU8000_INIT4_WRITE(emu, 0x07, treble_parm[treble][3]);
+	EMU8000_INIT4_WRITE(emu, 0x0b, treble_parm[treble][4]);
+	EMU8000_INIT4_WRITE(emu, 0x0d, treble_parm[treble][5]);
+	EMU8000_INIT4_WRITE(emu, 0x17, treble_parm[treble][6]);
+	EMU8000_INIT4_WRITE(emu, 0x19, treble_parm[treble][7]);
+	w = bass_parm[bass][2] + treble_parm[treble][8];
+	EMU8000_INIT4_WRITE(emu, 0x15, (unsigned short)(w + 0x0262));
+	EMU8000_INIT4_WRITE(emu, 0x1d, (unsigned short)(w + 0x8362));
+}
+
+
+/*----------------------------------------------------------------
+ * Chorus mode control
+ *----------------------------------------------------------------*/
+
+/*
+ * chorus mode parameters
+ */
+#define SNDRV_EMU8000_CHORUS_1		0
+#define	SNDRV_EMU8000_CHORUS_2		1
+#define	SNDRV_EMU8000_CHORUS_3		2
+#define	SNDRV_EMU8000_CHORUS_4		3
+#define	SNDRV_EMU8000_CHORUS_FEEDBACK	4
+#define	SNDRV_EMU8000_CHORUS_FLANGER	5
+#define	SNDRV_EMU8000_CHORUS_SHORTDELAY	6
+#define	SNDRV_EMU8000_CHORUS_SHORTDELAY2	7
+#define SNDRV_EMU8000_CHORUS_PREDEFINED	8
+/* user can define chorus modes up to 32 */
+#define SNDRV_EMU8000_CHORUS_NUMBERS	32
+
+typedef struct soundfont_chorus_fx_t {
+	unsigned short feedback;	/* feedback level (0xE600-0xE6FF) */
+	unsigned short delay_offset;	/* delay (0-0x0DA3) [1/44100 sec] */
+	unsigned short lfo_depth;	/* LFO depth (0xBC00-0xBCFF) */
+	unsigned int delay;	/* right delay (0-0xFFFFFFFF) [1/256/44100 sec] */
+	unsigned int lfo_freq;		/* LFO freq LFO freq (0-0xFFFFFFFF) */
+} soundfont_chorus_fx_t;
+
+/* 5 parameters for each chorus mode; 3 x 16bit, 2 x 32bit */
+static char chorus_defined[SNDRV_EMU8000_CHORUS_NUMBERS];
+static soundfont_chorus_fx_t chorus_parm[SNDRV_EMU8000_CHORUS_NUMBERS] = {
+	{0xE600, 0x03F6, 0xBC2C ,0x00000000, 0x0000006D}, /* chorus 1 */
+	{0xE608, 0x031A, 0xBC6E, 0x00000000, 0x0000017C}, /* chorus 2 */
+	{0xE610, 0x031A, 0xBC84, 0x00000000, 0x00000083}, /* chorus 3 */
+	{0xE620, 0x0269, 0xBC6E, 0x00000000, 0x0000017C}, /* chorus 4 */
+	{0xE680, 0x04D3, 0xBCA6, 0x00000000, 0x0000005B}, /* feedback */
+	{0xE6E0, 0x044E, 0xBC37, 0x00000000, 0x00000026}, /* flanger */
+	{0xE600, 0x0B06, 0xBC00, 0x0006E000, 0x00000083}, /* short delay */
+	{0xE6C0, 0x0B06, 0xBC00, 0x0006E000, 0x00000083}, /* short delay + feedback */
+};
+
+/*exported*/ int
+snd_emu8000_load_chorus_fx(emu8000_t *emu, int mode, const void *buf, long len)
+{
+	soundfont_chorus_fx_t rec;
+	if (mode < SNDRV_EMU8000_CHORUS_PREDEFINED || mode >= SNDRV_EMU8000_CHORUS_NUMBERS) {
+		snd_printk("illegal chorus mode %d for uploading\n", mode);
+		return -EINVAL;
+	}
+	if (len < sizeof(rec) || copy_from_user(&rec, buf, sizeof(rec)))
+		return -EFAULT;
+	chorus_parm[mode] = rec;
+	chorus_defined[mode] = 1;
+	return 0;
+}
+
+/*exported*/ void
+snd_emu8000_update_chorus_mode(emu8000_t *emu)
+{
+	int effect = emu->chorus_mode;
+	if (effect < 0 || effect >= SNDRV_EMU8000_CHORUS_NUMBERS ||
+	    (effect >= SNDRV_EMU8000_CHORUS_PREDEFINED && !chorus_defined[effect]))
+		return;
+	EMU8000_INIT3_WRITE(emu, 0x09, chorus_parm[effect].feedback);
+	EMU8000_INIT3_WRITE(emu, 0x0c, chorus_parm[effect].delay_offset);
+	EMU8000_INIT4_WRITE(emu, 0x03, chorus_parm[effect].lfo_depth);
+	EMU8000_HWCF4_WRITE(emu, chorus_parm[effect].delay);
+	EMU8000_HWCF5_WRITE(emu, chorus_parm[effect].lfo_freq);
+	EMU8000_HWCF6_WRITE(emu, 0x8000);
+	EMU8000_HWCF7_WRITE(emu, 0x0000);
+}
+
+/*----------------------------------------------------------------
+ * Reverb mode control
+ *----------------------------------------------------------------*/
+
+/*
+ * reverb mode parameters
+ */
+#define	SNDRV_EMU8000_REVERB_ROOM1	0
+#define SNDRV_EMU8000_REVERB_ROOM2	1
+#define	SNDRV_EMU8000_REVERB_ROOM3	2
+#define	SNDRV_EMU8000_REVERB_HALL1	3
+#define	SNDRV_EMU8000_REVERB_HALL2	4
+#define	SNDRV_EMU8000_REVERB_PLATE	5
+#define	SNDRV_EMU8000_REVERB_DELAY	6
+#define	SNDRV_EMU8000_REVERB_PANNINGDELAY 7
+#define SNDRV_EMU8000_REVERB_PREDEFINED	8
+/* user can define reverb modes up to 32 */
+#define SNDRV_EMU8000_REVERB_NUMBERS	32
+
+typedef struct soundfont_reverb_fx_t {
+	unsigned short parms[28];
+} soundfont_reverb_fx_t;
+
+/* reverb mode settings; write the following 28 data of 16 bit length
+ *   on the corresponding ports in the reverb_cmds array
+ */
+static char reverb_defined[SNDRV_EMU8000_CHORUS_NUMBERS];
+static soundfont_reverb_fx_t reverb_parm[SNDRV_EMU8000_REVERB_NUMBERS] = {
+{{  /* room 1 */
+	0xB488, 0xA450, 0x9550, 0x84B5, 0x383A, 0x3EB5, 0x72F4,
+	0x72A4, 0x7254, 0x7204, 0x7204, 0x7204, 0x4416, 0x4516,
+	0xA490, 0xA590, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429,
+	0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528,
+}},
+{{  /* room 2 */
+	0xB488, 0xA458, 0x9558, 0x84B5, 0x383A, 0x3EB5, 0x7284,
+	0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4448, 0x4548,
+	0xA440, 0xA540, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429,
+	0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528,
+}},
+{{  /* room 3 */
+	0xB488, 0xA460, 0x9560, 0x84B5, 0x383A, 0x3EB5, 0x7284,
+	0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4416, 0x4516,
+	0xA490, 0xA590, 0x842C, 0x852C, 0x842C, 0x852C, 0x842B,
+	0x852B, 0x842B, 0x852B, 0x842A, 0x852A, 0x842A, 0x852A,
+}},
+{{  /* hall 1 */
+	0xB488, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7284,
+	0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4448, 0x4548,
+	0xA440, 0xA540, 0x842B, 0x852B, 0x842B, 0x852B, 0x842A,
+	0x852A, 0x842A, 0x852A, 0x8429, 0x8529, 0x8429, 0x8529,
+}},
+{{  /* hall 2 */
+	0xB488, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7254,
+	0x7234, 0x7224, 0x7254, 0x7264, 0x7294, 0x44C3, 0x45C3,
+	0xA404, 0xA504, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429,
+	0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528,
+}},
+{{  /* plate */
+	0xB4FF, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7234,
+	0x7234, 0x7234, 0x7234, 0x7234, 0x7234, 0x4448, 0x4548,
+	0xA440, 0xA540, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429,
+	0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528,
+}},
+{{  /* delay */
+	0xB4FF, 0xA470, 0x9500, 0x84B5, 0x333A, 0x39B5, 0x7204,
+	0x7204, 0x7204, 0x7204, 0x7204, 0x72F4, 0x4400, 0x4500,
+	0xA4FF, 0xA5FF, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420,
+	0x8520, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, 0x8520,
+}},
+{{  /* panning delay */
+	0xB4FF, 0xA490, 0x9590, 0x8474, 0x333A, 0x39B5, 0x7204,
+	0x7204, 0x7204, 0x7204, 0x7204, 0x72F4, 0x4400, 0x4500,
+	0xA4FF, 0xA5FF, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420,
+	0x8520, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, 0x8520,
+}},
+};
+
+enum { DATA1, DATA2 };
+#define AWE_INIT1(c)	EMU8000_CMD(2,c), DATA1
+#define AWE_INIT2(c)	EMU8000_CMD(2,c), DATA2
+#define AWE_INIT3(c)	EMU8000_CMD(3,c), DATA1
+#define AWE_INIT4(c)	EMU8000_CMD(3,c), DATA2
+
+static struct reverb_cmd_pair {
+	unsigned short cmd, port;
+} reverb_cmds[28] = {
+  {AWE_INIT1(0x03)}, {AWE_INIT1(0x05)}, {AWE_INIT4(0x1F)}, {AWE_INIT1(0x07)},
+  {AWE_INIT2(0x14)}, {AWE_INIT2(0x16)}, {AWE_INIT1(0x0F)}, {AWE_INIT1(0x17)},
+  {AWE_INIT1(0x1F)}, {AWE_INIT2(0x07)}, {AWE_INIT2(0x0F)}, {AWE_INIT2(0x17)},
+  {AWE_INIT2(0x1D)}, {AWE_INIT2(0x1F)}, {AWE_INIT3(0x01)}, {AWE_INIT3(0x03)},
+  {AWE_INIT1(0x09)}, {AWE_INIT1(0x0B)}, {AWE_INIT1(0x11)}, {AWE_INIT1(0x13)},
+  {AWE_INIT1(0x19)}, {AWE_INIT1(0x1B)}, {AWE_INIT2(0x01)}, {AWE_INIT2(0x03)},
+  {AWE_INIT2(0x09)}, {AWE_INIT2(0x0B)}, {AWE_INIT2(0x11)}, {AWE_INIT2(0x13)},
+};
+
+/*exported*/ int
+snd_emu8000_load_reverb_fx(emu8000_t *emu, int mode, const void *buf, long len)
+{
+	soundfont_reverb_fx_t rec;
+
+	if (mode < SNDRV_EMU8000_REVERB_PREDEFINED || mode >= SNDRV_EMU8000_REVERB_NUMBERS) {
+		snd_printk("illegal reverb mode %d for uploading\n", mode);
+		return -EINVAL;
+	}
+	if (len < sizeof(rec) || copy_from_user(&rec, buf, sizeof(rec)))
+		return -EFAULT;
+	reverb_parm[mode] = rec;
+	reverb_defined[mode] = 1;
+	return 0;
+}
+
+/*exported*/ void
+snd_emu8000_update_reverb_mode(emu8000_t *emu)
+{
+	int effect = emu->reverb_mode;
+	int i;
+
+	if (effect < 0 || effect >= SNDRV_EMU8000_REVERB_NUMBERS ||
+	    (effect >= SNDRV_EMU8000_REVERB_PREDEFINED && !reverb_defined[effect]))
+		return;
+	for (i = 0; i < 28; i++) {
+		int port;
+		if (reverb_cmds[i].port == DATA1)
+			port = EMU8000_DATA1(emu);
+		else
+			port = EMU8000_DATA2(emu);
+		snd_emu8000_poke(emu, port, reverb_cmds[i].cmd, reverb_parm[effect].parms[i]);
+	}
+}
+
+
+/*----------------------------------------------------------------
+ * mixer interface
+ *----------------------------------------------------------------*/
+
+#define chip_t	emu8000_t
+
+/*
+ * bass/treble
+ */
+static int mixer_bass_treble_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 11;
+	return 0;
+}
+
+static int mixer_bass_treble_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	emu8000_t *emu = snd_kcontrol_chip(kcontrol);
+	
+	ucontrol->value.integer.value[0] = kcontrol->private_value ? emu->treble_level : emu->bass_level;
+	return 0;
+}
+
+static int mixer_bass_treble_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	emu8000_t *emu = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int change;
+	unsigned short val1;
+	
+	val1 = ucontrol->value.integer.value[0] % 12;
+	spin_lock_irqsave(&emu->control_lock, flags);
+	if (kcontrol->private_value) {
+		change = val1 != emu->treble_level;
+		emu->treble_level = val1;
+	} else {
+		change = val1 != emu->bass_level;
+		emu->bass_level = val1;
+	}
+	spin_unlock_irqrestore(&emu->control_lock, flags);
+	snd_emu8000_update_equalizer(emu);
+	return change;
+}
+
+#ifdef TARGET_OS2
+static snd_kcontrol_new_t mixer_bass_control =
+{
+	SNDRV_CTL_ELEM_IFACE_MIXER,0,0,
+	"Synth Tone Control - Bass",0,0, 0,
+	mixer_bass_treble_info,
+	mixer_bass_treble_get,
+	mixer_bass_treble_put,
+	0,
+};
+
+static snd_kcontrol_new_t mixer_treble_control =
+{
+	SNDRV_CTL_ELEM_IFACE_MIXER,0,0,
+	"Synth Tone Control - Treble",0,0, 0,
+	mixer_bass_treble_info,
+	mixer_bass_treble_get,
+	mixer_bass_treble_put,
+	1,
+};
+#else
+static snd_kcontrol_new_t mixer_bass_control =
+{
+	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	name: "Synth Tone Control - Bass",
+	info: mixer_bass_treble_info,
+	get: mixer_bass_treble_get,
+	put: mixer_bass_treble_put,
+	private_value: 0,
+};
+
+static snd_kcontrol_new_t mixer_treble_control =
+{
+	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	name: "Synth Tone Control - Treble",
+	info: mixer_bass_treble_info,
+	get: mixer_bass_treble_get,
+	put: mixer_bass_treble_put,
+	private_value: 1,
+};
+#endif
+
+/*
+ * chorus/reverb mode
+ */
+static int mixer_chorus_reverb_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = kcontrol->private_value ? (SNDRV_EMU8000_CHORUS_NUMBERS-1) : (SNDRV_EMU8000_REVERB_NUMBERS-1);
+	return 0;
+}
+
+static int mixer_chorus_reverb_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	emu8000_t *emu = snd_kcontrol_chip(kcontrol);
+	
+	ucontrol->value.integer.value[0] = kcontrol->private_value ? emu->chorus_mode : emu->reverb_mode;
+	return 0;
+}
+
+static int mixer_chorus_reverb_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	emu8000_t *emu = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int change;
+	unsigned short val1;
+	
+	spin_lock_irqsave(&emu->control_lock, flags);
+	if (kcontrol->private_value) {
+		val1 = ucontrol->value.integer.value[0] % SNDRV_EMU8000_CHORUS_NUMBERS;
+		change = val1 != emu->chorus_mode;
+		emu->chorus_mode = val1;
+	} else {
+		val1 = ucontrol->value.integer.value[0] % SNDRV_EMU8000_REVERB_NUMBERS;
+		change = val1 != emu->reverb_mode;
+		emu->reverb_mode = val1;
+	}
+	spin_unlock_irqrestore(&emu->control_lock, flags);
+	if (change) {
+		if (kcontrol->private_value)
+			snd_emu8000_update_chorus_mode(emu);
+		else
+			snd_emu8000_update_reverb_mode(emu);
+	}
+	return change;
+}
+
+#ifdef TARGET_OS2
+static snd_kcontrol_new_t mixer_chorus_mode_control =
+{
+	SNDRV_CTL_ELEM_IFACE_MIXER,0,0,
+	"Chorus Mode",0,0, 0,
+	mixer_chorus_reverb_info,
+	mixer_chorus_reverb_get,
+	mixer_chorus_reverb_put,
+	1,
+};
+
+static snd_kcontrol_new_t mixer_reverb_mode_control =
+{
+	SNDRV_CTL_ELEM_IFACE_MIXER,0,0,
+	"Reverb Mode",0,0,0,
+	mixer_chorus_reverb_info,
+	mixer_chorus_reverb_get,
+	mixer_chorus_reverb_put,
+	0,
+};
+#else
+static snd_kcontrol_new_t mixer_chorus_mode_control =
+{
+	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	name: "Chorus Mode",
+	info: mixer_chorus_reverb_info,
+	get: mixer_chorus_reverb_get,
+	put: mixer_chorus_reverb_put,
+	private_value: 1,
+};
+
+static snd_kcontrol_new_t mixer_reverb_mode_control =
+{
+	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	name: "Reverb Mode",
+	info: mixer_chorus_reverb_info,
+	get: mixer_chorus_reverb_get,
+	put: mixer_chorus_reverb_put,
+	private_value: 0,
+};
+#endif
+
+/*
+ * FM OPL3 chorus/reverb depth
+ */
+static int mixer_fm_depth_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 255;
+	return 0;
+}
+
+static int mixer_fm_depth_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	emu8000_t *emu = snd_kcontrol_chip(kcontrol);
+	
+	ucontrol->value.integer.value[0] = kcontrol->private_value ? emu->fm_chorus_depth : emu->fm_reverb_depth;
+	return 0;
+}
+
+static int mixer_fm_depth_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	emu8000_t *emu = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int change;
+	unsigned short val1;
+	
+	val1 = ucontrol->value.integer.value[0] % 256;
+	spin_lock_irqsave(&emu->control_lock, flags);
+	if (kcontrol->private_value) {
+		change = val1 != emu->fm_chorus_depth;
+		emu->fm_chorus_depth = val1;
+	} else {
+		change = val1 != emu->fm_reverb_depth;
+		emu->fm_reverb_depth = val1;
+	}
+	spin_unlock_irqrestore(&emu->control_lock, flags);
+	if (change)
+		snd_emu8000_init_fm(emu);
+	return change;
+}
+
+#ifdef TARGET_OS2
+static snd_kcontrol_new_t mixer_fm_chorus_depth_control =
+{
+	SNDRV_CTL_ELEM_IFACE_MIXER,0,0,
+	"FM Chorus Depth",0,0,0,
+	mixer_fm_depth_info,
+	mixer_fm_depth_get,
+	mixer_fm_depth_put,
+	1,
+};
+
+static snd_kcontrol_new_t mixer_fm_reverb_depth_control =
+{
+	SNDRV_CTL_ELEM_IFACE_MIXER,0,0,
+	"FM Reverb Depth",0,0,0,
+	mixer_fm_depth_info,
+	mixer_fm_depth_get,
+	mixer_fm_depth_put,
+	0,
+};
+#else
+static snd_kcontrol_new_t mixer_fm_chorus_depth_control =
+{
+	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	name: "FM Chorus Depth",
+	info: mixer_fm_depth_info,
+	get: mixer_fm_depth_get,
+	put: mixer_fm_depth_put,
+	private_value: 1,
+};
+
+static snd_kcontrol_new_t mixer_fm_reverb_depth_control =
+{
+	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	name: "FM Reverb Depth",
+	info: mixer_fm_depth_info,
+	get: mixer_fm_depth_get,
+	put: mixer_fm_depth_put,
+	private_value: 0,
+};
+#endif
+
+static snd_kcontrol_new_t *mixer_defs[EMU8000_NUM_CONTROLS] = {
+	&mixer_bass_control,
+	&mixer_treble_control,
+	&mixer_chorus_mode_control,
+	&mixer_reverb_mode_control,
+	&mixer_fm_chorus_depth_control,
+	&mixer_fm_reverb_depth_control,
+};
+
+/*
+ * create and attach mixer elements for WaveTable treble/bass controls
+ */
+static int /*__init*/
+snd_emu8000_create_mixer(snd_card_t *card, emu8000_t *emu)
+{
+	int i, err = 0;
+
+	snd_assert(emu != NULL && card != NULL, return -EINVAL);
+
+	spin_lock_init(&emu->control_lock);
+
+	memset(emu->controls, 0, sizeof(emu->controls));
+	for (i = 0; i < EMU8000_NUM_CONTROLS; i++) {
+		if ((err = snd_ctl_add(card, emu->controls[i] = snd_ctl_new1(mixer_defs[i], emu))) < 0)
+			goto __error;
+	}
+	return 0;
+
+__error:
+	for (i = 0; i < EMU8000_NUM_CONTROLS; i++) {
+		if (emu->controls[i])
+			snd_ctl_remove(card, emu->controls[i]);
+	}
+	return err;
+}
+
+
+/*
+ * free resources
+ */
+static int snd_emu8000_free(emu8000_t *hw)
+{
+	if (hw->res_port1)
+		release_resource(hw->res_port1);
+	if (hw->res_port2)
+		release_resource(hw->res_port2);
+	if (hw->res_port3)
+		release_resource(hw->res_port3);
+	snd_magic_kfree(hw);
+	return 0;
+}
+
+/*
+ */
+static int snd_emu8000_dev_free(snd_device_t *device)
+{
+	emu8000_t *hw = snd_magic_cast(emu8000_t, device->device_data, return -ENXIO);
+	return snd_emu8000_free(hw);
+}
+
+/*
+ * initialize and register emu8000 synth device.
+ */
+/*exported*/ int
+snd_emu8000_new(snd_card_t *card, int index, long port, int seq_ports, snd_seq_device_t **awe_ret)
+{
+	snd_seq_device_t *awe;
+	emu8000_t *hw;
+	int err;
+#ifdef TARGET_OS2
+	static snd_device_ops_t ops = {
+		snd_emu8000_dev_free,0,0
+	};
+#else
+	static snd_device_ops_t ops = {
+		dev_free: snd_emu8000_dev_free,
+	};
+#endif
+
+	if (awe_ret)
+		*awe_ret = NULL;
+
+	if (seq_ports <= 0)
+		return 0;
+
+	hw = snd_magic_kcalloc(emu8000_t, 0, GFP_KERNEL);
+	if (hw == NULL)
+		return -ENOMEM;
+	spin_lock_init(&hw->reg_lock);
+	hw->index = index;
+	hw->port1 = port;
+	hw->port2 = port + 0x400;
+	hw->port3 = port + 0x800;
+	if (!(hw->res_port1 = request_region(hw->port1, 4, "Emu8000-1")) ||
+	    !(hw->res_port2 = request_region(hw->port2, 4, "Emu8000-2")) ||
+	    !(hw->res_port3 = request_region(hw->port3, 4, "Emu8000-3"))) {
+		snd_emu8000_free(hw);
+		return -EBUSY;
+	}
+	hw->mem_size = 0;
+	hw->card = card;
+	hw->seq_ports = seq_ports;
+	hw->bass_level = 5;
+	hw->treble_level = 9;
+	hw->chorus_mode = 2;
+	hw->reverb_mode = 4;
+	hw->fm_chorus_depth = 0;
+	hw->fm_reverb_depth = 0;
+
+	if (snd_emu8000_detect(hw) < 0) {
+		snd_emu8000_free(hw);
+		return -ENODEV;
+	}
+
+	snd_emu8000_init_hw(hw);
+	if ((err = snd_emu8000_create_mixer(card, hw)) < 0) {
+		snd_emu8000_free(hw);
+		return err;
+	}
+	
+	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, hw, &ops)) < 0) {
+		snd_emu8000_free(hw);
+		return err;
+	}
+	if (snd_seq_device_new(card, index, SNDRV_SEQ_DEV_ID_EMU8000,
+			       sizeof(emu8000_t*), &awe) >= 0) {
+		strcpy(awe->name, "EMU-8000");
+		*(emu8000_t**)SNDRV_SEQ_DEVICE_ARGPTR(awe) = hw;
+	}
+	if (awe_ret)
+		*awe_ret = awe;
+
+	return 0;
+}
+
+
+/*
+ * exported stuff
+ */
+
+EXPORT_SYMBOL(snd_emu8000_new);
+EXPORT_SYMBOL(snd_emu8000_poke);
+EXPORT_SYMBOL(snd_emu8000_peek);
+EXPORT_SYMBOL(snd_emu8000_poke_dw);
+EXPORT_SYMBOL(snd_emu8000_peek_dw);
+EXPORT_SYMBOL(snd_emu8000_dma_chan);
+EXPORT_SYMBOL(snd_emu8000_init_fm);
+EXPORT_SYMBOL(snd_emu8000_load_chorus_fx);
+EXPORT_SYMBOL(snd_emu8000_load_reverb_fx);
+EXPORT_SYMBOL(snd_emu8000_update_chorus_mode);
+EXPORT_SYMBOL(snd_emu8000_update_reverb_mode);
+EXPORT_SYMBOL(snd_emu8000_update_equalizer);
+
+/*
+ *  INIT part
+ */
+
+static int __init alsa_emu8000_init(void)
+{
+	return 0;
+}
+
+static void __exit alsa_emu8000_exit(void)
+{
+}
+
+module_init(alsa_emu8000_init)
+module_exit(alsa_emu8000_exit)
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/sb/emu8000_callback.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/sb/emu8000_callback.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/sb/emu8000_callback.c	(revision 91)
@@ -0,0 +1,557 @@
+/*
+ *  synth callback routines for the emu8000 (AWE32/64)
+ *
+ *  Copyright (C) 1999 Steve Ratcliffe
+ *  Copyright (C) 1999-2000 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "emu8000_local.h"
+
+/*
+ * prototypes
+ */
+static snd_emux_voice_t *get_voice(snd_emux_t *emu, snd_emux_port_t *port);
+static void start_voice(snd_emux_voice_t *vp);
+static void trigger_voice(snd_emux_voice_t *vp);
+static void release_voice(snd_emux_voice_t *vp);
+static void update_voice(snd_emux_voice_t *vp, int update);
+static void reset_voice(snd_emux_t *emu, int ch);
+static void terminate_voice(snd_emux_voice_t *vp);
+static void sysex(snd_emux_t *emu, char *buf, int len, int parsed, snd_midi_channel_set_t *chset);
+#ifdef CONFIG_SND_OSSEMUL
+static int oss_ioctl(snd_emux_t *emu, int cmd, int p1, int p2);
+#endif
+static int load_fx(snd_emux_t *emu, int type, int mode, const void *buf, long len);
+
+static void set_pitch(emu8000_t *hw, snd_emux_voice_t *vp);
+static void set_volume(emu8000_t *hw, snd_emux_voice_t *vp);
+static void set_pan(emu8000_t *hw, snd_emux_voice_t *vp);
+static void set_fmmod(emu8000_t *hw, snd_emux_voice_t *vp);
+static void set_tremfreq(emu8000_t *hw, snd_emux_voice_t *vp);
+static void set_fm2frq2(emu8000_t *hw, snd_emux_voice_t *vp);
+static void set_filterQ(emu8000_t *hw, snd_emux_voice_t *vp);
+static void snd_emu8000_tweak_voice(emu8000_t *emu, int ch);
+
+/*
+ * Ensure a value is between two points
+ * macro evaluates its args more than once, so changed to upper-case.
+ */
+#define LIMITVALUE(x, a, b) do { if ((x) < (a)) (x) = (a); else if ((x) > (b)) (x) = (b); } while (0)
+#define LIMITMAX(x, a) do {if ((x) > (a)) (x) = (a); } while (0)
+
+
+/*
+ * set up operators
+ */
+#ifdef TARGET_OS2
+static snd_emux_operators_t emu8000_ops = {
+	THIS_MODULE,
+	get_voice,
+	start_voice,
+	trigger_voice,
+	release_voice,
+	update_voice,
+	terminate_voice,
+        0,
+	reset_voice,
+	snd_emu8000_sample_new,
+	snd_emu8000_sample_free,
+	snd_emu8000_sample_reset,
+	load_fx,
+	sysex,
+#ifdef CONFIG_SND_OSSEMUL
+	oss_ioctl,
+#endif
+};
+#else
+static snd_emux_operators_t emu8000_ops = {
+	owner:		THIS_MODULE,
+	get_voice:	get_voice,
+	prepare:	start_voice,
+	trigger:	trigger_voice,
+	release:	release_voice,
+	update:		update_voice,
+	terminate:	terminate_voice,
+	reset:		reset_voice,
+	sample_new:	snd_emu8000_sample_new,
+	sample_free:	snd_emu8000_sample_free,
+	sample_reset:	snd_emu8000_sample_reset,
+	load_fx:	load_fx,
+	sysex:		sysex,
+#ifdef CONFIG_SND_OSSEMUL
+	oss_ioctl:	oss_ioctl,
+#endif
+};
+#endif
+
+void
+snd_emu8000_ops_setup(emu8000_t *hw)
+{
+	hw->emu->ops = emu8000_ops;
+}
+
+
+
+/*
+ * Terminate a voice
+ */
+static void
+release_voice(snd_emux_voice_t *vp)
+{
+	int dcysusv;
+	emu8000_t *hw;
+
+	hw = snd_magic_cast(emu8000_t, vp->hw, return);
+	dcysusv = 0x8000 | (unsigned char)vp->reg.parm.modrelease;
+	EMU8000_DCYSUS_WRITE(hw, vp->ch, dcysusv);
+	dcysusv = 0x8000 | (unsigned char)vp->reg.parm.volrelease;
+	EMU8000_DCYSUSV_WRITE(hw, vp->ch, dcysusv);
+}
+
+
+/*
+ */
+static void
+terminate_voice(snd_emux_voice_t *vp)
+{
+	emu8000_t *hw; 
+
+	hw = snd_magic_cast(emu8000_t, vp->hw, return);
+	EMU8000_DCYSUSV_WRITE(hw, vp->ch, 0x807F);
+}
+
+
+/*
+ */
+static void
+update_voice(snd_emux_voice_t *vp, int update)
+{
+	emu8000_t *hw;
+
+	hw = snd_magic_cast(emu8000_t, vp->hw, return);
+	if (update & SNDRV_EMUX_UPDATE_VOLUME)
+		set_volume(hw, vp);
+	if (update & SNDRV_EMUX_UPDATE_PITCH)
+		set_pitch(hw, vp);
+	if ((update & SNDRV_EMUX_UPDATE_PAN) &&
+	    vp->port->ctrls[EMUX_MD_REALTIME_PAN])
+		set_pan(hw, vp);
+	if (update & SNDRV_EMUX_UPDATE_FMMOD)
+		set_fmmod(hw, vp);
+	if (update & SNDRV_EMUX_UPDATE_TREMFREQ)
+		set_tremfreq(hw, vp);
+	if (update & SNDRV_EMUX_UPDATE_FM2FRQ2)
+		set_fm2frq2(hw, vp);
+	if (update & SNDRV_EMUX_UPDATE_Q)
+		set_filterQ(hw, vp);
+}
+
+
+/*
+ * Find a channel (voice) within the EMU that is not in use or at least
+ * less in use than other channels.  Always returns a valid pointer
+ * no matter what.  If there is a real shortage of voices then one
+ * will be cut. Such is life.
+ *
+ * The channel index (vp->ch) must be initialized in this routine.
+ * In Emu8k, it is identical with the array index.
+ */
+static snd_emux_voice_t *
+get_voice(snd_emux_t *emu, snd_emux_port_t *port)
+{
+	int  i;
+	snd_emux_voice_t *vp;
+	emu8000_t *hw;
+
+	/* what we are looking for, in order of preference */
+	enum {
+		OFF=0, RELEASED, PLAYING, END
+	};
+
+	/* Keeps track of what we are finding */
+	struct best {
+		unsigned int  time;
+		int voice;
+	} best[END];
+	struct best *bp;
+
+	hw = snd_magic_cast(emu8000_t, emu->hw, return NULL);
+
+	for (i = 0; i < END; i++) {
+		best[i].time = (unsigned int)(-1); /* XXX MAX_?INT really */;
+		best[i].voice = -1;
+	}
+
+	/*
+	 * Go through them all and get a best one to use.
+	 */
+	for (i = 0; i < emu->max_voices; i++) {
+		int state, val;
+
+		vp = &emu->voices[i];
+		state = vp->state;
+
+		if (state == SNDRV_EMUX_ST_OFF)
+			bp = best + OFF;
+		else if (state == SNDRV_EMUX_ST_RELEASED) {
+			bp = best + RELEASED;
+			val = (EMU8000_CVCF_READ(hw, vp->ch) >> 16) & 0xffff;
+			if (! val)
+				bp = best + OFF;
+		}
+		else if (state & SNDRV_EMUX_ST_ON)
+			bp = best + PLAYING;
+		else
+			continue;
+
+		/* check if sample is finished playing (non-looping only) */
+		if (state != SNDRV_EMUX_ST_OFF &&
+		    (vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_SINGLESHOT)) {
+			val = EMU8000_CCCA_READ(hw, vp->ch) & 0xffffff;
+			if (val >= vp->reg.loopstart)
+				bp = best + OFF;
+		}
+
+		if (vp->time < bp->time) {
+			bp->time = vp->time;
+			bp->voice = i;
+		}
+	}
+
+	for (i = 0; i < END; i++) {
+		if (best[i].voice >= 0) {
+			vp = &emu->voices[best[i].voice];
+			vp->ch = best[i].voice;
+			return vp;
+		}
+	}
+
+	/* not found */
+	return NULL;
+}
+
+/*
+ */
+static void
+start_voice(snd_emux_voice_t *vp)
+{
+	unsigned int temp;
+	int ch;
+	int addr;
+	snd_midi_channel_t *chan;
+	emu8000_t *hw;
+
+	hw = snd_magic_cast(emu8000_t, vp->hw, return);
+	ch = vp->ch;
+	chan = vp->chan;
+
+	/* channel to be silent and idle */
+	EMU8000_DCYSUSV_WRITE(hw, ch, 0x0080);
+	EMU8000_VTFT_WRITE(hw, ch, 0x0000FFFF);
+	EMU8000_CVCF_WRITE(hw, ch, 0x0000FFFF);
+	EMU8000_PTRX_WRITE(hw, ch, 0);
+	EMU8000_CPF_WRITE(hw, ch, 0);
+
+	/* set pitch offset */
+	set_pitch(hw, vp);
+
+	/* set envelope parameters */
+	EMU8000_ENVVAL_WRITE(hw, ch, vp->reg.parm.moddelay);
+	EMU8000_ATKHLD_WRITE(hw, ch, vp->reg.parm.modatkhld);
+	EMU8000_DCYSUS_WRITE(hw, ch, vp->reg.parm.moddcysus);
+	EMU8000_ENVVOL_WRITE(hw, ch, vp->reg.parm.voldelay);
+	EMU8000_ATKHLDV_WRITE(hw, ch, vp->reg.parm.volatkhld);
+	/* decay/sustain parameter for volume envelope is used
+	   for triggerg the voice */
+
+	/* cutoff and volume */
+	set_volume(hw, vp);
+
+	/* modulation envelope heights */
+	EMU8000_PEFE_WRITE(hw, ch, vp->reg.parm.pefe);
+
+	/* lfo1/2 delay */
+	EMU8000_LFO1VAL_WRITE(hw, ch, vp->reg.parm.lfo1delay);
+	EMU8000_LFO2VAL_WRITE(hw, ch, vp->reg.parm.lfo2delay);
+
+	/* lfo1 pitch & cutoff shift */
+	set_fmmod(hw, vp);
+	/* lfo1 volume & freq */
+	set_tremfreq(hw, vp);
+	/* lfo2 pitch & freq */
+	set_fm2frq2(hw, vp);
+	/* pan & loop start */
+	set_pan(hw, vp);
+
+	/* chorus & loop end (chorus 8bit, MSB) */
+	addr = vp->reg.loopend - 1;
+	temp = vp->reg.parm.chorus;
+	temp += (int)chan->control[MIDI_CTL_E3_CHORUS_DEPTH] * 9 / 10;
+	LIMITMAX(temp, 255);
+	temp = (temp <<24) | (unsigned int)addr;
+	EMU8000_CSL_WRITE(hw, ch, temp);
+
+	/* Q & current address (Q 4bit value, MSB) */
+	addr = vp->reg.start - 1;
+	temp = vp->reg.parm.filterQ;
+	temp = (temp<<28) | (unsigned int)addr;
+	EMU8000_CCCA_WRITE(hw, ch, temp);
+
+	/* clear unknown registers */
+	EMU8000_00A0_WRITE(hw, ch, 0);
+	EMU8000_0080_WRITE(hw, ch, 0);
+
+	/* reset volume */
+	temp = vp->vtarget << 16;
+	EMU8000_VTFT_WRITE(hw, ch, temp | vp->ftarget);
+	EMU8000_CVCF_WRITE(hw, ch, temp | 0xff00);
+}
+
+/*
+ * Start envelope
+ */
+static void
+trigger_voice(snd_emux_voice_t *vp)
+{
+	int ch = vp->ch;
+	unsigned int temp;
+	emu8000_t *hw;
+
+	hw = snd_magic_cast(emu8000_t, vp->hw, return);
+
+	/* set reverb and pitch target */
+	temp = vp->reg.parm.reverb;
+	temp += (int)vp->chan->control[MIDI_CTL_E1_REVERB_DEPTH] * 9 / 10;
+	LIMITMAX(temp, 255);
+	temp = (temp << 8) | (vp->ptarget << 16) | vp->aaux;
+	EMU8000_PTRX_WRITE(hw, ch, temp);
+	EMU8000_CPF_WRITE(hw, ch, vp->ptarget << 16);
+	EMU8000_DCYSUSV_WRITE(hw, ch, vp->reg.parm.voldcysus);
+}
+
+/*
+ * reset voice parameters
+ */
+static void
+reset_voice(snd_emux_t *emu, int ch)
+{
+	emu8000_t *hw;
+
+	hw = snd_magic_cast(emu8000_t, emu->hw, return);
+	EMU8000_DCYSUSV_WRITE(hw, ch, 0x807F);
+	snd_emu8000_tweak_voice(hw, ch);
+}
+
+/*
+ * Set the pitch of a possibly playing note.
+ */
+static void
+set_pitch(emu8000_t *hw, snd_emux_voice_t *vp)
+{
+	EMU8000_IP_WRITE(hw, vp->ch, vp->apitch);
+}
+
+/*
+ * Set the volume of a possibly already playing note
+ */
+static void
+set_volume(emu8000_t *hw, snd_emux_voice_t *vp)
+{
+	int  ifatn;
+
+	ifatn = (unsigned char)vp->acutoff;
+	ifatn = (ifatn << 8);
+	ifatn |= (unsigned char)vp->avol;
+	EMU8000_IFATN_WRITE(hw, vp->ch, ifatn);
+}
+
+/*
+ * Set pan and loop start address.
+ */
+static void
+set_pan(emu8000_t *hw, snd_emux_voice_t *vp)
+{
+	unsigned int temp;
+
+	temp = ((unsigned int)vp->apan<<24) | ((unsigned int)vp->reg.loopstart - 1);
+	EMU8000_PSST_WRITE(hw, vp->ch, temp);
+}
+
+#define MOD_SENSE 18
+
+static void
+set_fmmod(emu8000_t *hw, snd_emux_voice_t *vp)
+{
+	unsigned short fmmod;
+	short pitch;
+	unsigned char cutoff;
+	int modulation;
+
+	pitch = (char)(vp->reg.parm.fmmod>>8);
+	cutoff = (vp->reg.parm.fmmod & 0xff);
+	modulation = vp->chan->gm_modulation + vp->chan->midi_pressure;
+	pitch += (MOD_SENSE * modulation) / 1200;
+	LIMITVALUE(pitch, -128, 127);
+	fmmod = ((unsigned char)pitch<<8) | cutoff;
+	EMU8000_FMMOD_WRITE(hw, vp->ch, fmmod);
+}
+
+/* set tremolo (lfo1) volume & frequency */
+static void
+set_tremfreq(emu8000_t *hw, snd_emux_voice_t *vp)
+{
+	EMU8000_TREMFRQ_WRITE(hw, vp->ch, vp->reg.parm.tremfrq);
+}
+
+/* set lfo2 pitch & frequency */
+static void
+set_fm2frq2(emu8000_t *hw, snd_emux_voice_t *vp)
+{
+	unsigned short fm2frq2;
+	short pitch;
+	unsigned char freq;
+	int modulation;
+
+	pitch = (char)(vp->reg.parm.fm2frq2>>8);
+	freq = vp->reg.parm.fm2frq2 & 0xff;
+	modulation = vp->chan->gm_modulation + vp->chan->midi_pressure;
+	pitch += (MOD_SENSE * modulation) / 1200;
+	LIMITVALUE(pitch, -128, 127);
+	fm2frq2 = ((unsigned char)pitch<<8) | freq;
+	EMU8000_FM2FRQ2_WRITE(hw, vp->ch, fm2frq2);
+}
+
+/* set filterQ */
+static void
+set_filterQ(emu8000_t *hw, snd_emux_voice_t *vp)
+{
+	unsigned int addr;
+	addr = EMU8000_CCCA_READ(hw, vp->ch) & 0xffffff;
+	addr |= (vp->reg.parm.filterQ << 28);
+	EMU8000_CCCA_WRITE(hw, vp->ch, addr);
+}
+
+/*
+ * set the envelope & LFO parameters to the default values
+ */
+static void
+snd_emu8000_tweak_voice(emu8000_t *emu, int i)
+{
+	/* set all mod/vol envelope shape to minimum */
+	EMU8000_ENVVOL_WRITE(emu, i, 0x8000);
+	EMU8000_ENVVAL_WRITE(emu, i, 0x8000);
+	EMU8000_DCYSUS_WRITE(emu, i, 0x7F7F);
+	EMU8000_ATKHLDV_WRITE(emu, i, 0x7F7F);
+	EMU8000_ATKHLD_WRITE(emu, i, 0x7F7F);
+	EMU8000_PEFE_WRITE(emu, i, 0);  /* mod envelope height to zero */
+	EMU8000_LFO1VAL_WRITE(emu, i, 0x8000); /* no delay for LFO1 */
+	EMU8000_LFO2VAL_WRITE(emu, i, 0x8000);
+	EMU8000_IP_WRITE(emu, i, 0xE000);	/* no pitch shift */
+	EMU8000_IFATN_WRITE(emu, i, 0xFF00);	/* volume to minimum */
+	EMU8000_FMMOD_WRITE(emu, i, 0);
+	EMU8000_TREMFRQ_WRITE(emu, i, 0);
+	EMU8000_FM2FRQ2_WRITE(emu, i, 0);
+}
+
+/*
+ * sysex callback
+ */
+static void
+sysex(snd_emux_t *emu, char *buf, int len, int parsed, snd_midi_channel_set_t *chset)
+{
+	emu8000_t *hw;
+
+	hw = snd_magic_cast(emu8000_t, emu->hw, return);
+
+	switch (parsed) {
+	case SNDRV_MIDI_SYSEX_GS_CHORUS_MODE:
+		hw->chorus_mode = chset->gs_chorus_mode;
+		snd_emu8000_update_chorus_mode(hw);
+		break;
+
+	case SNDRV_MIDI_SYSEX_GS_REVERB_MODE:
+		hw->reverb_mode = chset->gs_reverb_mode;
+		snd_emu8000_update_reverb_mode(hw);
+		break;
+	}
+}
+
+
+#ifdef CONFIG_SND_OSSEMUL
+/*
+ * OSS ioctl callback
+ */
+static int
+oss_ioctl(snd_emux_t *emu, int cmd, int p1, int p2)
+{
+	emu8000_t *hw;
+
+	hw = snd_magic_cast(emu8000_t, emu->hw, return -EINVAL);
+
+	switch (cmd) {
+	case _EMUX_OSS_REVERB_MODE:
+		hw->reverb_mode = p1;
+		snd_emu8000_update_reverb_mode(hw);
+		break;
+
+	case _EMUX_OSS_CHORUS_MODE:
+		hw->chorus_mode = p1;
+		snd_emu8000_update_chorus_mode(hw);
+		break;
+
+	case _EMUX_OSS_INITIALIZE_CHIP:
+		/* snd_emu8000_init(hw); */ /*ignored*/
+		break;
+
+	case _EMUX_OSS_EQUALIZER:
+		hw->bass_level = p1;
+		hw->treble_level = p2;
+		snd_emu8000_update_equalizer(hw);
+		break;
+	}
+	return 0;
+}
+#endif
+
+
+/*
+ * additional patch keys
+ */
+
+#define SNDRV_EMU8000_LOAD_CHORUS_FX	0x10	/* optarg=mode */
+#define SNDRV_EMU8000_LOAD_REVERB_FX	0x11	/* optarg=mode */
+
+
+/*
+ * callback routine
+ */
+
+static int
+load_fx(snd_emux_t *emu, int type, int mode, const void *buf, long len)
+{
+	emu8000_t *hw;
+	hw = snd_magic_cast(emu8000_t, emu->hw, return -EINVAL);
+
+	switch (type) {
+	case SNDRV_EMU8000_LOAD_CHORUS_FX:
+		return snd_emu8000_load_chorus_fx(hw, mode, buf, len);
+	case SNDRV_EMU8000_LOAD_REVERB_FX:
+		return snd_emu8000_load_reverb_fx(hw, mode, buf, len);
+	}
+	return -EINVAL;
+}
+
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/sb/emu8000_local.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/sb/emu8000_local.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/sb/emu8000_local.h	(revision 91)
@@ -0,0 +1,37 @@
+#ifndef __EMU8000_LOCAL_H
+#define __EMU8000_LOCAL_H
+/*
+ *  Local defininitons for the emu8000 (AWE32/64)
+ *
+ *  Copyright (C) 1999 Steve Ratcliffe
+ *  Copyright (C) 1999-2000 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <sound/emu8000.h>
+#include <sound/emu8000_reg.h>
+
+#define NELEM(arr) (sizeof(arr)/sizeof((arr)[0]))
+
+/* emu8000_patch.c */
+int snd_emu8000_sample_new(snd_emux_t *rec, snd_sf_sample_t *sp, snd_util_memhdr_t *hdr, const void *data, long count);
+int snd_emu8000_sample_free(snd_emux_t *rec, snd_sf_sample_t *sp, snd_util_memhdr_t *hdr);
+void snd_emu8000_sample_reset(snd_emux_t *rec);
+
+/* emu8000_callback.c */
+void snd_emu8000_ops_setup(emu8000_t *emu);
+
+#endif	/* __EMU8000_LOCAL_H */
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/sb/emu8000_patch.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/sb/emu8000_patch.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/sb/emu8000_patch.c	(revision 91)
@@ -0,0 +1,297 @@
+/*
+ *  Patch routines for the emu8000 (AWE32/64)
+ *
+ *  Copyright (C) 1999 Steve Ratcliffe
+ *  Copyright (C) 1999-2000 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "emu8000_local.h"
+
+MODULE_PARM(emu8000_reset_addr, "i");
+MODULE_PARM_DESC(emu8000_reset_addr, "reset write address at each time (makes slowdown)");
+
+int emu8000_reset_addr = 0;
+
+
+/*
+ * Open up channels.
+ */
+static int
+snd_emu8000_open_dma(emu8000_t *emu, int write)
+{
+	int i;
+
+	/* reserve all 30 voices for loading */
+	for (i = 0; i < EMU8000_DRAM_VOICES; i++) {
+		snd_emux_lock_voice(emu->emu, i);
+		snd_emu8000_dma_chan(emu, i, write);
+	}
+
+	/* assign voice 31 and 32 to ROM */
+	EMU8000_VTFT_WRITE(emu, 30, 0);
+	EMU8000_PSST_WRITE(emu, 30, 0x1d8);
+	EMU8000_CSL_WRITE(emu, 30, 0x1e0);
+	EMU8000_CCCA_WRITE(emu, 30, 0x1d8);
+	EMU8000_VTFT_WRITE(emu, 31, 0);
+	EMU8000_PSST_WRITE(emu, 31, 0x1d8);
+	EMU8000_CSL_WRITE(emu, 31, 0x1e0);
+	EMU8000_CCCA_WRITE(emu, 31, 0x1d8);
+
+	return 0;
+}
+
+/*
+ * Close all dram channels.
+ */
+static void
+snd_emu8000_close_dma(emu8000_t *emu)
+{
+	int i;
+
+	for (i = 0; i < EMU8000_DRAM_VOICES; i++) {
+		snd_emu8000_dma_chan(emu, i, EMU8000_RAM_CLOSE);
+		snd_emux_unlock_voice(emu->emu, i);
+	}
+}
+
+/*
+ */
+
+#define BLANK_LOOP_START	4
+#define BLANK_LOOP_END		8
+#define BLANK_LOOP_SIZE		12
+#define BLANK_HEAD_SIZE		48
+
+/*
+ * Read a word from userland, taking care of conversions from
+ * 8bit samples etc.
+ */
+static unsigned short
+read_word(const void *buf, int offset, int mode)
+{
+	unsigned short c;
+	if (mode & SNDRV_SFNT_SAMPLE_8BITS) {
+		unsigned char cc;
+		get_user(cc, (unsigned char*)buf + offset);
+		c = cc << 8; /* convert 8bit -> 16bit */
+	} else {
+#ifdef SNDRV_LITTLE_ENDIAN
+		get_user(c, (unsigned short*)buf + offset);
+#else
+		unsigned short cc;
+		get_user(cc, (unsigned short*)buf + offset);
+		c = swab16(cc);
+#endif
+	}
+	if (mode & SNDRV_SFNT_SAMPLE_UNSIGNED)
+		c ^= 0x8000; /* unsigned -> signed */
+	return c;
+}
+
+/*
+ */
+static void
+snd_emu8000_write_wait(emu8000_t *emu)
+{
+	while ((EMU8000_SMALW_READ(emu) & 0x80000000) != 0) {
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule_timeout(1);
+		if (signal_pending(current))
+			break;
+	}
+}
+
+/*
+ * write sample word data
+ *
+ * You should not have to keep resetting the address each time
+ * as the chip is supposed to step on the next address automatically.
+ * It mostly does, but during writes of some samples at random it
+ * completely loses words (every one in 16 roughly but with no
+ * obvious pattern).
+ *
+ * This is therefore much slower than need be, but is at least
+ * working.
+ */
+inline static void
+write_word(emu8000_t *emu, int *offset, unsigned short data)
+{
+	if (emu8000_reset_addr) {
+		if (emu8000_reset_addr > 1)
+			snd_emu8000_write_wait(emu);
+		EMU8000_SMALW_WRITE(emu, *offset);
+	}
+	EMU8000_SMLD_WRITE(emu, data);
+	*offset += 1;
+}
+
+/*
+ * Write the sample to EMU800 memory.  This routine is invoked out of
+ * the generic soundfont routines as a callback.
+ */
+int
+snd_emu8000_sample_new(snd_emux_t *rec, snd_sf_sample_t *sp,
+		       snd_util_memhdr_t *hdr, const void *data, long count)
+{
+	int  i;
+	int  rc;
+	int  offset;
+	int  truesize;
+	int  dram_offset, dram_start;
+	emu8000_t *emu;
+
+	emu = snd_magic_cast(emu8000_t, rec->hw, return -EINVAL);
+	snd_assert(sp != NULL, return -EINVAL);
+
+	if (sp->v.size == 0)
+		return 0;
+
+	/* be sure loop points start < end */
+	if (sp->v.loopstart > sp->v.loopend) {
+		int tmp = sp->v.loopstart;
+		sp->v.loopstart = sp->v.loopend;
+		sp->v.loopend = tmp;
+	}
+
+	/* compute true data size to be loaded */
+	truesize = sp->v.size;
+	if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP))
+		truesize += sp->v.loopend - sp->v.loopstart;
+	if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK)
+		truesize += BLANK_LOOP_SIZE;
+
+	sp->block = snd_util_mem_alloc(hdr, truesize * 2);
+	if (sp->block == NULL) {
+		/*snd_printd("EMU8000: out of memory\n");*/
+		/* not ENOMEM (for compatibility) */
+		return -ENOSPC;
+	}
+
+	if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS) {
+		if (verify_area(VERIFY_READ, data, sp->v.size))
+			return -EFAULT;
+	} else {
+		if (verify_area(VERIFY_READ, data, sp->v.size * 2))
+			return -EFAULT;
+	}
+
+	/* recalculate address offset */
+	sp->v.end -= sp->v.start;
+	sp->v.loopstart -= sp->v.start;
+	sp->v.loopend -= sp->v.start;
+	sp->v.start = 0;
+
+	/* dram position (in word) -- mem_offset is byte */
+	dram_offset = EMU8000_DRAM_OFFSET + (sp->block->offset >> 1);
+	dram_start = dram_offset;
+
+	/* set the total size (store onto obsolete checksum value) */
+	sp->v.truesize = truesize * 2; /* in bytes */
+
+	snd_emux_terminate_all(emu->emu);
+	if ((rc = snd_emu8000_open_dma(emu, EMU8000_RAM_WRITE)) != 0)
+		return rc;
+
+	/* Set the address to start writing at */
+	snd_emu8000_write_wait(emu);
+	EMU8000_SMALW_WRITE(emu, dram_offset);
+
+	/*snd_emu8000_init_fm(emu);*/
+
+#if 0
+	/* first block - write 48 samples for silence */
+	if (! sp->block->offset) {
+		for (i = 0; i < BLANK_HEAD_SIZE; i++) {
+			write_word(emu, &dram_offset, 0);
+		}
+	}
+#endif
+
+	offset = 0;
+	for (i = 0; i < sp->v.size; i++) {
+		unsigned short s;
+
+		s = read_word(data, offset, sp->v.mode_flags);
+		offset++;
+		write_word(emu, &dram_offset, s);
+
+		if (i == sp->v.loopend &&
+		    (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP)))
+		{
+			int looplen = sp->v.loopend - sp->v.loopstart;
+			int k;
+
+			/* copy reverse loop */
+			for (k = 1; k <= looplen; k++) {
+				s = read_word(data, offset - k, sp->v.mode_flags);
+				write_word(emu, &dram_offset, s);
+			}
+			if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_BIDIR_LOOP) {
+				sp->v.loopend += looplen;
+			} else {
+				sp->v.loopstart += looplen;
+				sp->v.loopend += looplen;
+			}
+			sp->v.end += looplen;
+		}
+	}
+
+	/* if no blank loop is attached in the sample, add it */
+	if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK) {
+		for (i = 0; i < BLANK_LOOP_SIZE; i++) {
+			write_word(emu, &dram_offset, 0);
+		}
+		if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_SINGLESHOT) {
+			sp->v.loopstart = sp->v.end + BLANK_LOOP_START;
+			sp->v.loopend = sp->v.end + BLANK_LOOP_END;
+		}
+	}
+
+	/* add dram offset */
+	sp->v.start += dram_start;
+	sp->v.end += dram_start;
+	sp->v.loopstart += dram_start;
+	sp->v.loopend += dram_start;
+
+	snd_emu8000_close_dma(emu);
+	snd_emu8000_init_fm(emu);
+
+	return 0;
+}
+
+/*
+ * free a sample block
+ */
+int
+snd_emu8000_sample_free(snd_emux_t *rec, snd_sf_sample_t *sp, snd_util_memhdr_t *hdr)
+{
+	if (sp->block) {
+		snd_util_mem_free(hdr, sp->block);
+		sp->block = NULL;
+	}
+	return 0;
+}
+
+
+/*
+ * sample_reset callback - terminate voices
+ */
+void
+snd_emu8000_sample_reset(snd_emux_t *rec)
+{
+	snd_emux_terminate_all(rec);
+}
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/sb/emu8000_synth.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/sb/emu8000_synth.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/sb/emu8000_synth.c	(revision 91)
@@ -0,0 +1,127 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *     and (c) 1999 Steve Ratcliffe <steve@parabola.demon.co.uk>
+ *  Copyright (C) 1999-2000 Takashi Iwai <tiwai@suse.de>
+ *
+ *  Emu8000 synth plug-in routine
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define SNDRV_MAIN_OBJECT_FILE
+#include "emu8000_local.h"
+#include <sound/initval.h>
+
+EXPORT_NO_SYMBOLS;
+MODULE_CLASSES("{sound}");
+MODULE_AUTHOR("Takashi Iwai, Steve Ratcliffe");
+
+/*----------------------------------------------------------------*/
+
+/*
+ * create a new hardware dependent device for Emu8000
+ */
+static int snd_emu8000_new_device(snd_seq_device_t *dev)
+{
+	emu8000_t *hw;
+	snd_emux_t *emu;
+
+	hw = *(emu8000_t**)SNDRV_SEQ_DEVICE_ARGPTR(dev);
+	if (hw == NULL)
+		return -EINVAL;
+
+	if (hw->emu)
+		return -EBUSY; /* already exists..? */
+
+	if (snd_emux_new(&emu) < 0)
+		return -ENOMEM;
+
+	hw->emu = emu;
+	snd_emu8000_ops_setup(hw);
+
+	emu->hw = hw;
+	emu->max_voices = EMU8000_DRAM_VOICES;
+	emu->num_ports = hw->seq_ports;
+
+	if (hw->memhdr) {
+		snd_printk("memhdr is already initialized!?\n");
+		snd_util_memhdr_free(hw->memhdr);
+	}
+	hw->memhdr = snd_util_memhdr_new(hw->mem_size);
+	if (hw->memhdr == NULL) {
+		snd_emux_free(emu);
+		hw->emu = NULL;
+		return -ENOMEM;
+	}
+
+	emu->memhdr = hw->memhdr;
+	emu->midi_ports = hw->seq_ports < 2 ? hw->seq_ports : 2; /* number of virmidi ports */
+	emu->midi_devidx = 1;
+
+	if (snd_emux_register(emu, dev->card, hw->index, "Emu8000") < 0) {
+		snd_emux_free(emu);
+		snd_util_memhdr_free(hw->memhdr);
+		hw->emu = NULL;
+		hw->memhdr = NULL;
+		return -ENOMEM;
+	}
+
+	dev->driver_data = hw;
+
+	return 0;
+}
+
+
+/*
+ * free all resources
+ */
+static int snd_emu8000_delete_device(snd_seq_device_t *dev)
+{
+	emu8000_t *hw;
+
+	if (dev->driver_data == NULL)
+		return 0; /* no synth was allocated actually */
+
+	hw = dev->driver_data;
+	if (hw->emu)
+		snd_emux_free(hw->emu);
+	if (hw->memhdr)
+		snd_util_memhdr_free(hw->memhdr);
+	hw->emu = NULL;
+	hw->memhdr = NULL;
+	return 0;
+}
+
+/*
+ *  INIT part
+ */
+
+static int __init alsa_emu8000_init(void)
+{
+	
+	static snd_seq_dev_ops_t ops = {
+		snd_emu8000_new_device,
+		snd_emu8000_delete_device,
+	};
+	return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_EMU8000, &ops, sizeof(emu8000_t*));
+}
+
+static void __exit alsa_emu8000_exit(void)
+{
+	snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_EMU8000);
+}
+
+module_init(alsa_emu8000_init)
+module_exit(alsa_emu8000_exit)
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/sb/es968.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/sb/es968.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/sb/es968.c	(revision 91)
@@ -0,0 +1,295 @@
+
+/*
+ card-es968.c - driver for ESS AudioDrive ES968 based soundcards.
+ Copyright (C) 1999 by Massimo Piccioni <dafastidio@libero.it>
+
+ Thanks to Pierfrancesco 'qM2' Passerini.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define SNDRV_MAIN_OBJECT_FILE
+#include <sound/driver.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+#include <sound/sb.h>
+
+EXPORT_NO_SYMBOLS;
+MODULE_DESCRIPTION("ESS AudioDrive ES968");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{ESS,AudioDrive ES968}}");
+
+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;	/* Enable this card */
+static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* PnP setup */
+static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* Pnp setup */
+static int snd_dma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* PnP setup */
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for es968 based soundcard.");
+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 es968 based soundcard.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable es968 based soundcard.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_port, "Port # for es968 driver.");
+MODULE_PARM_SYNTAX(snd_port, SNDRV_PORT12_DESC);
+MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_irq, "IRQ # for es968 driver.");
+MODULE_PARM_SYNTAX(snd_irq, SNDRV_IRQ_DESC);
+MODULE_PARM(snd_dma8, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_dma8, "8-bit DMA # for es968 driver.");
+MODULE_PARM_SYNTAX(snd_dma8, SNDRV_DMA8_DESC);
+
+struct snd_card_es968 {
+#ifdef __ISAPNP__
+    struct isapnp_dev *dev;
+#endif	/* __ISAPNP__ */
+};
+
+static snd_card_t *snd_es968_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+
+#ifdef __ISAPNP__
+static struct isapnp_card *snd_es968_isapnp_cards[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR;
+static const struct isapnp_card_id *snd_es968_isapnp_id[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PTR;
+
+static struct isapnp_card_id snd_es968_pnpids[] __devinitdata = {
+    {
+#ifdef TARGET_OS2
+        0, ISAPNP_CARD_ID('E','S','S',0x0968),
+        { ISAPNP_DEVICE_ID('E','S','S',0x0968), }
+#else
+        ISAPNP_CARD_ID('E','S','S',0x0968),
+    devs: { ISAPNP_DEVICE_ID('E','S','S',0x0968), }
+#endif
+    },
+    { ISAPNP_CARD_END, }
+};
+
+ISAPNP_CARD_TABLE(snd_es968_pnpids);
+
+#endif	/* __ISAPNP__ */
+
+#define	DRIVER_NAME	"snd-card-es968"
+
+
+static void snd_card_es968_interrupt(int irq, void *dev_id,
+                                     struct pt_regs *regs)
+{
+    sb_t *chip = dev_id;
+
+    if (chip->open & SB_OPEN_PCM) {
+        snd_sb8dsp_interrupt(chip);
+    } else {
+        snd_sb8dsp_midi_interrupt(chip);
+    }
+}
+
+#ifdef __ISAPNP__
+static int __init snd_card_es968_isapnp(int dev, struct snd_card_es968 *acard)
+{
+    const struct isapnp_card_id *id = snd_es968_isapnp_id[dev];
+    struct isapnp_card *card = snd_es968_isapnp_cards[dev];
+    struct isapnp_dev *pdev;
+
+    acard->dev = isapnp_find_dev(card, id->devs[0].vendor, id->devs[0].function, NULL);
+    if (acard->dev->active) {
+        acard->dev = NULL;
+        return -EBUSY;
+    }
+
+    pdev = acard->dev;
+    if (pdev->prepare(pdev)<0)
+        return -EAGAIN;
+
+    if (snd_port[dev] != SNDRV_AUTO_PORT)
+        isapnp_resource_change(&pdev->resource[0], snd_port[dev], 16);
+    if (snd_dma8[dev] != SNDRV_AUTO_DMA)
+        isapnp_resource_change(&pdev->dma_resource[0], snd_dma8[dev],
+                               1);
+    if (snd_irq[dev] != SNDRV_AUTO_IRQ)
+        isapnp_resource_change(&pdev->irq_resource[0], snd_irq[dev], 1);
+
+    if (pdev->activate(pdev)<0) {
+        snd_printk("AUDIO isapnp configure failure\n");
+        return -EBUSY;
+    }
+
+    snd_port[dev] = pdev->resource[0].start;
+    snd_dma8[dev] = pdev->dma_resource[0].start;
+    snd_irq[dev] = pdev->irq_resource[0].start;
+
+    return 0;
+}
+
+static void snd_card_es968_deactivate(struct snd_card_es968 *acard)
+{
+    if (acard->dev) {
+        acard->dev->deactivate(acard->dev);
+        acard->dev = NULL;
+    }
+}
+#endif	/* __ISAPNP__ */
+
+static void __exit snd_card_es968_free(snd_card_t *card)
+{
+    struct snd_card_es968 *acard = (struct snd_card_es968 *)card->private_data;
+
+    if (acard) {
+#ifdef __ISAPNP__
+        snd_card_es968_deactivate(acard);
+#endif	/* __ISAPNP__ */
+    }
+}
+
+static int __init snd_card_es968_probe(int dev)
+{
+    int error;
+    sb_t *chip;
+    snd_card_t *card;
+    struct snd_card_es968 *acard;
+
+    if ((card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE,
+                             sizeof(struct snd_card_es968))) == NULL)
+        return -ENOMEM;
+    acard = (struct snd_card_es968 *)card->private_data;
+    card->private_free = snd_card_es968_free;
+
+#ifdef __ISAPNP__
+    if ((error = snd_card_es968_isapnp(dev, acard))) {
+        snd_card_free(card);
+        return error;
+    }
+#else
+    snd_printk("you have to enable PnP support ...\n");
+    snd_card_free(card);
+    return -ENOSYS;
+#endif	/* __ISAPNP__ */
+
+    if ((error = snd_sbdsp_create(card, snd_port[dev],
+                                  snd_irq[dev],
+                                  snd_card_es968_interrupt,
+                                  snd_dma8[dev],
+                                  -1,
+                                  SB_HW_AUTO, &chip)) < 0) {
+        snd_card_free(card);
+        return error;
+    }
+
+    if ((error = snd_sb8dsp_pcm(chip, 0, NULL)) < 0) {
+        snd_card_free(card);
+        return error;
+    }
+
+    if ((error = snd_sbmixer_new(chip)) < 0) {
+        snd_card_free(card);
+        return error;
+    }
+
+    if ((error = snd_sb8dsp_midi(chip, 0, NULL)) < 0) {
+        snd_card_free(card);
+        return error;
+    }
+
+    strcpy(card->driver, "ES968");
+    strcpy(card->shortname, "ESS ES968");
+    sprintf(card->longname, "%s soundcard, %s at 0x%lx, irq %d, dma %d",
+            card->shortname, chip->name, chip->port, snd_irq[dev], snd_dma8[dev]);
+
+    if ((error = snd_card_register(card)) < 0) {
+        snd_card_free(card);
+        return error;
+    }
+    snd_es968_cards[dev] = card;
+    return 0;
+}
+
+#ifdef __ISAPNP__
+static int __init snd_es968_isapnp_detect(struct isapnp_card *card,
+                                          const struct isapnp_card_id *id)
+{
+    static int dev = 0;
+    int res;
+
+    for ( ; dev < SNDRV_CARDS; dev++) {
+        if (!snd_enable[dev])
+            continue;
+        snd_es968_isapnp_cards[dev] = card;
+        snd_es968_isapnp_id[dev] = id;
+        res = snd_card_es968_probe(dev);
+        if (res < 0)
+            return res;
+        dev++;
+        return 0;
+    }
+    return -ENODEV;
+}
+#endif /* __ISAPNP__ */
+
+static int __init alsa_card_es968_init(void)
+{
+    int cards = 0;
+
+#ifdef __ISAPNP__
+    cards += isapnp_probe_cards(snd_es968_pnpids, snd_es968_isapnp_detect);
+#else
+    snd_printk("you have to enable ISA PnP support.\n");
+#endif
+#ifdef MODULE
+    if (!cards)
+        snd_printk("no ES968 based soundcards found\n");
+#endif
+    return cards ? 0 : -ENODEV;
+}
+
+static void __exit alsa_card_es968_exit(void)
+{
+    int dev;
+
+    for (dev = 0; dev < SNDRV_CARDS; dev++)
+        snd_card_free(snd_es968_cards[dev]);
+}
+
+module_init(alsa_card_es968_init)
+module_exit(alsa_card_es968_exit)
+
+#ifndef MODULE
+
+/* format is: snd-card-es968=snd_enable,snd_index,snd_id,
+ snd_port,snd_irq,snd_dma1 */
+
+static int __init alsa_card_es968_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_port[nr_dev]) == 2 &&
+           get_option(&str,&snd_irq[nr_dev]) == 2 &&
+           get_option(&str,&snd_dma8[nr_dev]) == 2);
+    nr_dev++;
+    return 1;
+}
+
+__setup("snd-card-es968=", alsa_card_es968_setup);
+
+#endif /* ifndef MODULE */
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/sb/makefile.os2
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/sb/makefile.os2	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/sb/makefile.os2	(revision 91)
@@ -0,0 +1,55 @@
+CINCLUDES = ..\..\..\include;..\..\include;$(%WATCOM)\H
+AINCLUDES = $(DDK_BASE)\H
+
+CDEFINES = -D__KERNEL__ -DMODULE -dTARGET_OS2 -dALSA_BUILD -D__ISAPNP__ -DCONFIG_ISAPNP -D__i386_
+
+32BIT=1
+!include ..\..\..\..\makefile.inc
+
+
+#===================================================================
+#
+#   List of source files
+#
+#===================================================================
+FILE1    = sb_common.obj sb_mixer.obj 
+#sb8_main.obj sb8_midi.obj
+FILE2    = 
+#sb16_main.obj sb16_csp.obj sb8.obj sb16.obj sbawe.obj emu8000.obj
+FILE3    = 
+#emu8000_synth.obj emu8000_callback.obj emu8000_patch.obj
+FILE4    =
+# es968.obj
+FILE5    = 
+FILE6    = 
+FILE7    = 
+FILE8    = 
+FILELAST =
+FILES    = $(FILE0) $(FILE1) $(FILE2) $(FILE3) $(FILE4) $(FILE5) $(FILE6) $(FILE7) $(FILE8) $(FILE9) $(FILE10) $(FILE11) $(FILE12)
+
+TARGET   = sb
+
+LNKFILE  = $(OBJDIR)\$(TARGET).lnk
+
+
+#===================================================================
+#
+#   Specific dependencies
+#
+#===================================================================
+all: $(OBJDIR)\$(TARGET).lib 
+
+
+$(LNKFILE): makefile.os2
+    @%create $(LNKFILE)
+    @for %f in ($(FILES)) do @%append $(LNKFILE) -+$(OBJDIR)\%f
+
+$(OBJDIR)\$(TARGET).lib: $(LNKFILE) $(FILES)
+    $(LIB) $(OBJDIR)\$(TARGET).lib @$(LNKFILE)
+    copy $(OBJDIR)\$(TARGET).lib $(ALSA_LIB) 
+
+clean: .SYMBOLIC
+    -@rm  *.err 2>nul
+    @cd $(OBJDIR)
+    -rm *.obj *.lnk *.sys *.sym *.lst *.map *.wmap *.lib 2>nul
+    @cd ../..
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/sb/sb16.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/sb/sb16.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/sb/sb16.c	(revision 91)
@@ -0,0 +1,711 @@
+/*
+ *  Driver for SoundBlaster 16/AWE32/AWE64 soundcards
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#define SNDRV_MAIN_OBJECT_FILE
+#include <sound/driver.h>
+#include <sound/sb.h>
+#include <sound/sb16_csp.h>
+#include <sound/mpu401.h>
+#include <sound/opl3.h>
+#include <sound/emu8000.h>
+#include <sound/seq_device.h>
+#define SNDRV_LEGACY_AUTO_PROBE
+#define SNDRV_LEGACY_FIND_FREE_IRQ
+#define SNDRV_LEGACY_FIND_FREE_DMA
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+EXPORT_NO_SYMBOLS;
+#ifndef SNDRV_SBAWE
+MODULE_DESCRIPTION("Sound Blaster 16");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{Creative Labs,SB 16},"
+               "{Creative Labs,SB Vibra16S},"
+               "{Creative Labs,SB Vibra16C},"
+               "{Creative Labs,SB Vibra16CL},"
+               "{Creative Labs,SB Vibra16X}}");
+#else
+MODULE_DESCRIPTION("Sound Blaster AWE");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{Creative Labs,SB AWE 32},"
+               "{Creative Labs,SB AWE 64},"
+               "{Creative Labs,SB AWE 64 Gold}}");
+#endif
+
+#if 0
+#define SNDRV_DEBUG_IRQ
+#endif
+
+#if defined(SNDRV_SBAWE) && defined(CONFIG_SND_SEQUENCER) && defined(CONFIG_SND_SYNTH_EMU8000)
+#define SNDRV_SBAWE_EMU8000
+#endif
+
+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;	/* Enable this card */
+#ifdef __ISAPNP__
+#ifdef TARGET_OS2
+static int snd_isapnp[SNDRV_CARDS] = {1,1,1,1,1,1,1,1};
+#else
+static int snd_isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
+#endif
+#endif
+static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* 0x220,0x240,0x260,0x280 */
+#ifdef TARGET_OS2
+static long snd_mpu_port[SNDRV_CARDS] = {0x330, 0x300, -1,-1,-1,-1,-1,-1};
+#else
+static long snd_mpu_port[SNDRV_CARDS] = {0x330, 0x300,[2 ... (SNDRV_CARDS - 1)] = -1};
+#endif
+static long snd_fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
+#ifdef SNDRV_SBAWE_EMU8000
+static long snd_awe_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
+#endif
+static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* 5,7,9,10 */
+static int snd_dma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* 0,1,3 */
+static int snd_dma16[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* 5,6,7 */
+#ifdef TARGET_OS2
+static int snd_mic_agc[SNDRV_CARDS] = {1,1,1,1,1,1,1,1};
+#ifdef CONFIG_SND_SB16_CSP
+static int snd_csp[SNDRV_CARDS] = {0,0,0,0,0,0,0,0};
+#endif
+#ifdef SNDRV_SBAWE_EMU8000
+static int snd_seq_ports[SNDRV_CARDS] = {4,4,4,4,4,4,4,4};
+#endif
+#else
+static int snd_mic_agc[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
+#ifdef CONFIG_SND_SB16_CSP
+static int snd_csp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0};
+#endif
+#ifdef SNDRV_SBAWE_EMU8000
+static int snd_seq_ports[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 4};
+#endif
+#endif
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for SoundBlaster 16 soundcard.");
+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 SoundBlaster 16 soundcard.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable SoundBlaster 16 soundcard.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+#ifdef __ISAPNP__
+MODULE_PARM(snd_isapnp, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_isapnp, "ISA PnP detection for specified soundcard.");
+MODULE_PARM_SYNTAX(snd_isapnp, SNDRV_ISAPNP_DESC);
+#endif
+MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_port, "Port # for SB16 driver.");
+MODULE_PARM_SYNTAX(snd_port, SNDRV_ENABLED ",allows:{{0x220},{0x240},{0x260},{0x280}},dialog:list");
+MODULE_PARM(snd_mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_mpu_port, "MPU-401 port # for SB16 driver.");
+MODULE_PARM_SYNTAX(snd_mpu_port, SNDRV_ENABLED ",allows:{{0x330},{0x300}},dialog:list");
+MODULE_PARM(snd_fm_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_fm_port, "FM port # for SB16 PnP driver.");
+MODULE_PARM_SYNTAX(snd_fm_port, SNDRV_ENABLED ",allows:{{0x388},{0x38c},{0x390},{0x394}},dialog:list");
+#ifdef SNDRV_SBAWE_EMU8000
+MODULE_PARM(snd_awe_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_awe_port, "AWE port # for SB16 PnP driver.");
+MODULE_PARM_SYNTAX(snd_awe_port, SNDRV_ENABLED ",allows:{{0x620},{0x640},{0x660},{0x680}},dialog:list");
+#endif
+MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_irq, "IRQ # for SB16 driver.");
+MODULE_PARM_SYNTAX(snd_irq, SNDRV_IRQ_DESC);
+MODULE_PARM(snd_dma8, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_dma8, "8-bit DMA # for SB16 driver.");
+MODULE_PARM_SYNTAX(snd_dma8, SNDRV_DMA8_DESC);
+MODULE_PARM(snd_dma16, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_dma16, "16-bit DMA # for SB16 driver.");
+MODULE_PARM_SYNTAX(snd_dma16, SNDRV_DMA16_DESC);
+MODULE_PARM(snd_mic_agc, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_mic_agc, "Mic Auto-Gain-Control switch.");
+MODULE_PARM_SYNTAX(snd_mic_agcm, SNDRV_ENABLED "," SNDRV_BOOLEAN_TRUE_DESC);
+#ifdef CONFIG_SND_SB16_CSP
+MODULE_PARM(snd_csp, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_csp, "ASP/CSP chip support.");
+MODULE_PARM_SYNTAX(snd_csp, SNDRV_ENABLED "," SNDRV_ENABLE_DESC);
+#endif
+#ifdef SNDRV_SBAWE_EMU8000
+MODULE_PARM(snd_seq_ports, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_seq_ports, "Number of sequencer ports for WaveTable synth.");
+MODULE_PARM_SYNTAX(snd_seq_ports, SNDRV_ENABLED ",allows:{{0,8}},skill:advanced");
+#endif
+
+struct snd_sb16 {
+    struct resource *fm_res;	/* used to block FM i/o region for legacy cards */
+#ifdef __ISAPNP__
+    struct isapnp_dev *dev;
+#ifdef SNDRV_SBAWE_EMU8000
+    struct isapnp_dev *devwt;
+#endif
+#endif
+};
+
+static snd_card_t *snd_sb16_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+
+#ifdef __ISAPNP__
+
+static struct isapnp_card *snd_sb16_isapnp_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+static const struct isapnp_card_id *snd_sb16_isapnp_id[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+
+#ifdef TARGET_OS2
+#define ISAPNP_SB16(_va, _vb, _vc, _device, _audio) \
+    { \
+    0, ISAPNP_CARD_ID(_va, _vb, _vc, _device), \
+    { ISAPNP_DEVICE_ID(_va, _vb, _vc, _audio), } \
+    }
+#define ISAPNP_SBAWE(_va, _vb, _vc, _device, _audio, _awe) \
+    { \
+    0, ISAPNP_CARD_ID(_va, _vb, _vc, _device), \
+    { ISAPNP_DEVICE_ID(_va, _vb, _vc, _audio), \
+    ISAPNP_DEVICE_ID(_va, _vb, _vc, _awe), } \
+    }
+#else
+#define ISAPNP_SB16(_va, _vb, _vc, _device, _audio) \
+    { \
+    ISAPNP_CARD_ID(_va, _vb, _vc, _device), \
+    devs : { ISAPNP_DEVICE_ID(_va, _vb, _vc, _audio), } \
+    }
+#define ISAPNP_SBAWE(_va, _vb, _vc, _device, _audio, _awe) \
+    { \
+    ISAPNP_CARD_ID(_va, _vb, _vc, _device), \
+    devs : { ISAPNP_DEVICE_ID(_va, _vb, _vc, _audio), \
+    ISAPNP_DEVICE_ID(_va, _vb, _vc, _awe), } \
+    }
+#endif
+
+static struct isapnp_card_id snd_sb16_pnpids[] __devinitdata = {
+#ifndef SNDRV_SBAWE
+    /* Sound Blaster 16 PnP */
+    ISAPNP_SB16('C','T','L',0x0024,0x0031),
+    /* Sound Blaster 16 PnP */
+    ISAPNP_SB16('C','T','L',0x0026,0x0031),
+    /* Sound Blaster 16 PnP */
+    ISAPNP_SB16('C','T','L',0x0027,0x0031),
+    /* Sound Blaster 16 PnP */
+    ISAPNP_SB16('C','T','L',0x0028,0x0031),
+    /* Sound Blaster 16 PnP */
+    ISAPNP_SB16('C','T','L',0x0029,0x0031),
+    /* Sound Blaster 16 PnP */
+    ISAPNP_SB16('C','T','L',0x002a,0x0031),
+    /* Sound Blaster 16 PnP */
+    /* Note: This card has also a CTL0051:StereoEnhance device!!! */
+    ISAPNP_SB16('C','T','L',0x002b,0x0031),
+    /* Sound Blaster 16 PnP */
+    ISAPNP_SB16('C','T','L',0x002c,0x0031),
+    /* Sound Blaster Vibra16S */
+    ISAPNP_SB16('C','T','L',0x0051,0x0001),
+    /* Sound Blaster Vibra16C */
+    ISAPNP_SB16('C','T','L',0x0070,0x0001),
+    /* Sound Blaster Vibra16CL - added by ctm@ardi.com */
+    ISAPNP_SB16('C','T','L',0x0080,0x0041),
+    /* Sound Blaster Vibra16X */
+    ISAPNP_SB16('C','T','L',0x00f0,0x0043),
+#else  /* SNDRV_SBAWE defined */
+    /* Sound Blaster AWE 32 PnP */
+    ISAPNP_SBAWE('C','T','L',0x0035,0x0031,0x0021),
+    /* Sound Blaster AWE 32 PnP */
+    ISAPNP_SBAWE('C','T','L',0x0039,0x0031,0x0021),
+    /* Sound Blaster AWE 32 PnP */
+    ISAPNP_SBAWE('C','T','L',0x0042,0x0031,0x0021),
+    /* Sound Blaster AWE 32 PnP */
+    ISAPNP_SBAWE('C','T','L',0x0043,0x0031,0x0021),
+    /* Sound Blaster AWE 32 PnP */
+    /* Note: This card has also a CTL0051:StereoEnhance device!!! */
+    ISAPNP_SBAWE('C','T','L',0x0044,0x0031,0x0021),
+    /* Sound Blaster AWE 32 PnP */
+    /* Note: This card has also a CTL0051:StereoEnhance device!!! */
+    ISAPNP_SBAWE('C','T','L',0x0045,0x0031,0x0021),
+    /* Sound Blaster AWE 32 PnP */
+    ISAPNP_SBAWE('C','T','L',0x0047,0x0031,0x0021),
+    /* Sound Blaster AWE 32 PnP */
+    ISAPNP_SBAWE('C','T','L',0x0048,0x0031,0x0021),
+    /* Sound Blaster AWE 32 PnP */
+    ISAPNP_SBAWE('C','T','L',0x0054,0x0031,0x0021),
+    /* Sound Blaster AWE 32 PnP */
+    ISAPNP_SBAWE('C','T','L',0x009a,0x0041,0x0021),
+    /* Sound Blaster AWE 32 PnP */
+    ISAPNP_SBAWE('C','T','L',0x009c,0x0041,0x0021),
+    /* Sound Blaster 32 PnP */
+    ISAPNP_SBAWE('C','T','L',0x009f,0x0041,0x0021),
+    /* Sound Blaster AWE 64 PnP */
+    ISAPNP_SBAWE('C','T','L',0x009d,0x0042,0x0022),
+    /* Sound Blaster AWE 64 PnP Gold */
+    ISAPNP_SBAWE('C','T','L',0x009e,0x0044,0x0023),
+    /* Sound Blaster AWE 64 PnP Gold */
+    ISAPNP_SBAWE('C','T','L',0x00b2,0x0044,0x0023),
+    /* Sound Blaster AWE 64 PnP */
+    ISAPNP_SBAWE('C','T','L',0x00c1,0x0042,0x0022),
+    /* Sound Blaster AWE 64 PnP */
+    ISAPNP_SBAWE('C','T','L',0x00c3,0x0045,0x0022),
+    /* Sound Blaster AWE 64 PnP */
+    ISAPNP_SBAWE('C','T','L',0x00c5,0x0045,0x0022),
+    /* Sound Blaster AWE 64 PnP */
+    ISAPNP_SBAWE('C','T','L',0x00c7,0x0045,0x0022),
+    /* Sound Blaster AWE 64 PnP */
+    ISAPNP_SBAWE('C','T','L',0x00e4,0x0045,0x0022),
+    /* Sound Blaster 16 PnP (AWE) */
+    ISAPNP_SBAWE('C','T','L',0x00ed,0x0041,0x0070),
+    /* Generic entries */
+    ISAPNP_SBAWE('C','T','L',ISAPNP_ANY_ID,0x0031,0x0021),
+    ISAPNP_SBAWE('C','T','L',ISAPNP_ANY_ID,0x0041,0x0021),
+    ISAPNP_SBAWE('C','T','L',ISAPNP_ANY_ID,0x0042,0x0022),
+    ISAPNP_SBAWE('C','T','L',ISAPNP_ANY_ID,0x0044,0x0023),
+    ISAPNP_SBAWE('C','T','L',ISAPNP_ANY_ID,0x0045,0x0022),
+#endif /* SNDRV_SBAWE */
+    { ISAPNP_CARD_END, }
+};
+
+ISAPNP_CARD_TABLE(snd_sb16_pnpids);
+
+static int __init snd_sb16_isapnp(int dev, struct snd_sb16 *acard)
+{
+    const struct isapnp_card_id *id = snd_sb16_isapnp_id[dev];
+    struct isapnp_card *card = snd_sb16_isapnp_cards[dev];
+    struct isapnp_dev *pdev;
+
+    acard->dev = isapnp_find_dev(card, id->devs[0].vendor, id->devs[0].function, NULL);
+    if (acard->dev->active) {
+        acard->dev = NULL;
+        return -EBUSY;
+    }
+#ifdef SNDRV_SBAWE_EMU8000
+    acard->devwt = isapnp_find_dev(card, id->devs[1].vendor, id->devs[1].function, NULL);
+    if (acard->devwt->active) {
+        acard->dev = acard->devwt = NULL;
+        return -EBUSY;
+    }
+#endif
+    /* Audio initialization */
+    pdev = acard->dev;
+    if (pdev->prepare(pdev) < 0)
+        return -EAGAIN;
+    if (snd_port[dev] != SNDRV_AUTO_PORT)
+        isapnp_resource_change(&pdev->resource[0], snd_port[dev], 16);
+    if (snd_mpu_port[dev] != SNDRV_AUTO_PORT)
+        isapnp_resource_change(&pdev->resource[1], snd_mpu_port[dev], 2);
+    if (snd_fm_port[dev] != SNDRV_AUTO_PORT)
+        isapnp_resource_change(&pdev->resource[2], snd_fm_port[dev], 4);
+    if (snd_dma8[dev] != SNDRV_AUTO_DMA)
+        isapnp_resource_change(&pdev->dma_resource[0], snd_dma8[dev], 1);
+    if (snd_dma16[dev] != SNDRV_AUTO_DMA)
+        isapnp_resource_change(&pdev->dma_resource[1], snd_dma16[dev], 1);
+    if (snd_irq[dev] != SNDRV_AUTO_IRQ)
+        isapnp_resource_change(&pdev->irq_resource[0], snd_irq[dev], 1);
+    if (pdev->activate(pdev) < 0) {
+        snd_printk("isapnp configure failure (out of resources?)\n");
+        return -EBUSY;
+    }
+    snd_port[dev] = pdev->resource[0].start;
+    snd_mpu_port[dev] = pdev->resource[1].start;
+    snd_fm_port[dev] = pdev->resource[2].start;
+    snd_dma8[dev] = pdev->dma_resource[0].start;
+    snd_dma16[dev] = pdev->dma_resource[1].start;
+    snd_irq[dev] = pdev->irq_resource[0].start;
+    snd_printdd("isapnp SB16: port=0x%lx, mpu port=0x%lx, fm port=0x%lx\n",
+                snd_port[dev], snd_mpu_port[dev], snd_fm_port[dev]);
+    snd_printdd("isapnp SB16: dma1=%i, dma2=%i, irq=%i\n",
+                snd_dma8[dev], snd_dma16[dev], snd_irq[dev]);
+#ifdef SNDRV_SBAWE_EMU8000
+    /* WaveTable initialization */
+    pdev = acard->devwt;
+    if (pdev->prepare(pdev)<0) {
+        acard->dev->deactivate(acard->dev);
+        return -EAGAIN;
+    }
+    if (snd_awe_port[dev] != SNDRV_AUTO_PORT)
+        isapnp_resource_change(&pdev->resource[0], snd_awe_port[dev], 4);
+    if (pdev->activate(pdev)<0) {
+        snd_printk("WaveTable isapnp configure failure (out of resources?)\n");
+        acard->dev->deactivate(acard->dev);
+        return -EBUSY;
+    }
+    snd_awe_port[dev] = pdev->resource[0].start;
+    snd_printdd("isapnp SB16: wavetable port=0x%lx\n", pdev->resource[0].start);
+#endif
+    return 0;
+}
+
+static void snd_sb16_deactivate(struct snd_sb16 *acard)
+{
+    if (acard->dev) {
+        acard->dev->deactivate(acard->dev);
+        acard->dev = NULL;
+    }
+#ifdef SNDRV_SBAWE_EMU8000
+    if (acard->devwt) {
+        acard->devwt->deactivate(acard->devwt);
+        acard->devwt = NULL;
+    }
+#endif
+}
+
+#endif /* __ISAPNP__ */
+
+static void snd_sb16_free(snd_card_t *card)
+{
+    struct snd_sb16 *acard = (struct snd_sb16 *)card->private_data;
+
+    if (acard == NULL)
+        return;
+    if (acard->fm_res)
+        release_resource(acard->fm_res);
+#ifdef __ISAPNP__
+    snd_sb16_deactivate(acard);
+#endif
+}
+
+static int __init snd_sb16_probe(int dev)
+{
+    static int possible_irqs[] = {5, 9, 10, 7, -1};
+    static int possible_dmas8[] = {1, 3, 0, -1};
+    static int possible_dmas16[] = {5, 6, 7, -1};
+    int irq, dma8, dma16;
+    sb_t *chip;
+    snd_card_t *card;
+    struct snd_sb16 *acard;
+    opl3_t *opl3;
+    snd_hwdep_t *synth = NULL;
+    snd_hwdep_t *csp = NULL;
+    unsigned long flags;
+    int err;
+
+    card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE,
+                        sizeof(struct snd_sb16));
+    if (card == NULL)
+        return -ENOMEM;
+    acard = (struct snd_sb16 *) card->private_data;
+    card->private_free = snd_sb16_free;
+#ifdef __ISAPNP__
+    if (snd_isapnp[dev] && snd_sb16_isapnp(dev, acard) < 0) {
+        snd_card_free(card);
+        return -EBUSY;
+    }
+#endif
+
+    irq = snd_irq[dev];
+    dma8 = snd_dma8[dev];
+    dma16 = snd_dma16[dev];
+#ifdef __ISAPNP__
+    if (!snd_isapnp[dev]) {
+#endif
+        if (irq == SNDRV_AUTO_IRQ) {
+            if ((irq = snd_legacy_find_free_irq(possible_irqs)) < 0) {
+                snd_card_free(card);
+                snd_printk("unable to find a free IRQ\n");
+                return -EBUSY;
+            }
+        }
+        if (dma8 == SNDRV_AUTO_DMA) {
+            if ((dma8 = snd_legacy_find_free_dma(possible_dmas8)) < 0) {
+                snd_card_free(card);
+                snd_printk("unable to find a free 8-bit DMA\n");
+                return -EBUSY;
+            }
+        }
+        if (dma16 == SNDRV_AUTO_DMA) {
+            if ((dma16 = snd_legacy_find_free_dma(possible_dmas16)) < 0) {
+                snd_card_free(card);
+                snd_printk("unable to find a free 16-bit DMA\n");
+                return -EBUSY;
+            }
+        }
+        /* non-PnP FM port address is hardwired with base port address */
+        snd_fm_port[dev] = snd_port[dev];
+        /* block the 0x388 port to avoid PnP conflicts */
+        acard->fm_res = request_region(0x388, 4, "SoundBlaster FM");
+#ifdef SNDRV_SBAWE_EMU8000
+        /* non-PnP AWE port address is hardwired with base port address */
+        snd_awe_port[dev] = snd_port[dev] + 0x400;
+#endif
+#ifdef __ISAPNP__
+    }
+#endif
+
+    if ((err = snd_sbdsp_create(card,
+                                snd_port[dev],
+                                irq,
+                                snd_sb16dsp_interrupt,
+                                dma8,
+                                dma16,
+                                SB_HW_AUTO,
+                                &chip)) < 0) {
+        snd_card_free(card);
+        return err;
+    }
+    if (chip->hardware != SB_HW_16) {
+        snd_card_free(card);
+        snd_printdd("SB 16 chip was not detected at 0x%lx\n", snd_port[dev]);
+        return -ENODEV;
+    }
+    chip->mpu_port = snd_mpu_port[dev];
+#ifdef __ISAPNP__
+    if (!snd_isapnp[dev] && (err = snd_sb16dsp_configure(chip)) < 0) {
+#else
+        if ((err = snd_sb16dsp_configure(chip)) < 0) {
+#endif
+            snd_card_free(card);
+            return -ENXIO;
+        }
+        if ((err = snd_sb16dsp_pcm(chip, 0, NULL)) < 0) {
+            snd_card_free(card);
+            return -ENXIO;
+        }
+
+        if (chip->mpu_port) {
+            if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_SB,
+                                           chip->mpu_port, 0,
+                                           irq, 0, &chip->rmidi)) < 0) {
+                snd_card_free(card);
+                return -ENXIO;
+            }
+        }
+
+        if (snd_fm_port[dev] > 0) {
+            if (snd_opl3_create(card, snd_fm_port[dev], snd_fm_port[dev] + 2,
+                                OPL3_HW_OPL3, snd_fm_port[dev] == snd_port[dev],
+                                &opl3) < 0) {
+                snd_printk("no OPL device at 0x%lx-0x%lx\n",
+                           snd_fm_port[dev], snd_fm_port[dev] + 2);
+            } else {
+#ifdef SNDRV_SBAWE_EMU8000
+                int seqdev = snd_awe_port[dev] > 0 ? 2 : 1;
+#else
+                int seqdev = 1;
+#endif
+                if ((err = snd_opl3_hwdep_new(opl3, 0, seqdev, &synth)) < 0) {
+                    snd_card_free(card);
+                    return -ENXIO;
+                }
+            }
+        }
+
+        if ((err = snd_sbmixer_new(chip)) < 0) {
+            snd_card_free(card);
+            return -ENXIO;
+        }
+
+#ifdef CONFIG_SND_SB16_CSP
+        /* CSP chip on SB16ASP/AWE32 */
+        if ((chip->hardware == SB_HW_16) && snd_csp[dev]) {
+            snd_sb_csp_new(chip, synth != NULL ? 1 : 0, &csp);
+            if (csp) {
+                chip->csp = csp->private_data;
+                chip->hardware = SB_HW_16CSP;
+            } else {
+                snd_printk("warning - CSP chip not detected on soundcard #%i\n", dev + 1);
+            }
+        }
+#endif
+#ifdef SNDRV_SBAWE_EMU8000
+        if (snd_awe_port[dev] > 0) {
+            if (snd_emu8000_new(card, 1, snd_awe_port[dev],
+                                snd_seq_ports[dev], NULL) < 0) {
+                snd_printk("fatal error - EMU-8000 synthesizer not detected at 0x%lx\n", snd_awe_port[dev]);
+                snd_card_free(card);
+                return -ENXIO;
+            }
+        }
+#endif
+
+        /* setup Mic AGC */
+        spin_lock_irqsave(&chip->mixer_lock, flags);
+        snd_sbmixer_write(chip, SB_DSP4_MIC_AGC,
+                          (snd_sbmixer_read(chip, SB_DSP4_MIC_AGC) & 0x01) |
+                          (snd_mic_agc[dev] ? 0x00 : 0x01));
+        spin_unlock_irqrestore(&chip->mixer_lock, flags);
+
+        strcpy(card->driver,
+#ifdef SNDRV_SBAWE_EMU8000
+               snd_awe_port[dev] > 0 ? "SB AWE" :
+#endif
+               "SB16");
+        strcpy(card->shortname, chip->name);
+        sprintf(card->longname, "%s at 0x%lx, irq %i, dma ",
+                chip->name,
+                chip->port,
+                irq);
+        if (dma8 >= 0)
+            sprintf(card->longname + strlen(card->longname), "%d", dma8);
+        if (dma16 >= 0)
+            sprintf(card->longname + strlen(card->longname), "%s%d",
+                    dma8 >= 0 ? "&" : "", dma16);
+        if ((err = snd_card_register(card)) < 0) {
+            snd_card_free(card);
+            return err;
+        }
+        snd_sb16_cards[dev] = card;
+        return 0;
+    }
+
+    static int __init snd_sb16_probe_legacy_port(unsigned long port)
+    {
+        static int dev = 0;
+        int res;
+
+        for ( ; dev < SNDRV_CARDS; dev++) {
+            if (!snd_enable[dev] || snd_port[dev] != SNDRV_AUTO_PORT)
+                continue;
+#ifdef __ISAPNP__
+            if (snd_isapnp[dev])
+                continue;
+#endif
+            snd_port[dev] = port;
+            res = snd_sb16_probe(dev);
+            if (res < 0)
+                snd_port[dev] = SNDRV_AUTO_PORT;
+            return res;
+        }
+        return -ENODEV;
+    }
+
+#ifdef __ISAPNP__
+
+    static int __init snd_sb16_isapnp_detect(struct isapnp_card *card,
+                                             const struct isapnp_card_id *id)
+    {
+        static int dev = 0;
+        int res;
+
+        for ( ; dev < SNDRV_CARDS; dev++) {
+            if (!snd_enable[dev] || !snd_isapnp[dev])
+                continue;
+            snd_sb16_isapnp_cards[dev] = card;
+            snd_sb16_isapnp_id[dev] = id;
+            res = snd_sb16_probe(dev);
+            if (res < 0)
+                return res;
+            dev++;
+            return 0;
+        }
+
+        return -ENODEV;
+    }
+
+#endif /* __ISAPNP__ */
+
+    static int __init alsa_card_sb16_init(void)
+    {
+        int dev, cards = 0;
+        static unsigned long possible_ports[] = {0x220, 0x240, 0x260, 0x280, -1};
+
+        /* legacy non-auto cards at first */
+        for (dev = 0; dev < SNDRV_CARDS; dev++) {
+            if (!snd_enable[dev] || snd_port[dev] == SNDRV_AUTO_PORT)
+                continue;
+#ifdef __ISAPNP__
+            if (snd_isapnp[dev])
+                continue;
+#endif
+            if (!snd_sb16_probe(dev)) {
+                cards++;
+                continue;
+            }
+#ifdef MODULE
+            snd_printk("Sound Blaster 16+ soundcard #%i not found at 0x%lx or device busy\n", dev, snd_port[dev]);
+#endif
+        }
+        /* legacy auto configured cards */
+        cards += snd_legacy_auto_probe(possible_ports, snd_sb16_probe_legacy_port);
+#ifdef __ISAPNP__
+        /* ISA PnP cards at last */
+        cards += isapnp_probe_cards(snd_sb16_pnpids, snd_sb16_isapnp_detect);
+#endif
+
+        if (!cards) {
+#ifdef MODULE
+            snd_printk("Sound Blaster 16 soundcard not found or device busy\n");
+#ifdef SNDRV_SBAWE_EMU8000
+            snd_printk("In case, if you have non-AWE card, try snd-card-sb16 module\n");
+#else
+            snd_printk("In case, if you have AWE card, try snd-card-sbawe module\n");
+#endif
+#endif
+            return -ENODEV;
+        }
+        return 0;
+    }
+
+    static void __exit alsa_card_sb16_exit(void)
+    {
+        int dev;
+
+        for (dev = 0; dev < SNDRV_CARDS; dev++)
+            snd_card_free(snd_sb16_cards[dev]);
+    }
+
+    module_init(alsa_card_sb16_init)
+        module_exit(alsa_card_sb16_exit)
+
+#ifndef MODULE
+
+        /* format is: snd-card-sb16=snd_enable,snd_index,snd_id,snd_isapnp,
+         snd_port,snd_mpu_port,snd_fm_port,
+         snd_irq,snd_dma8,snd_dma16,
+         snd_mic_agc,snd_csp,
+         [snd_awe_port,snd_seq_ports] */
+
+        static int __init alsa_card_sb16_setup(char *str)
+    {
+        static unsigned __initdata nr_dev = 0;
+        int __attribute__ ((__unused__)) pnp = INT_MAX;
+        int __attribute__ ((__unused__)) csp = INT_MAX;
+
+        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,&pnp) == 2 &&
+               get_option(&str,(int *)&snd_port[nr_dev]) == 2 &&
+               get_option(&str,(int *)&snd_mpu_port[nr_dev]) == 2 &&
+               get_option(&str,(int *)&snd_fm_port[nr_dev]) == 2 &&
+               get_option(&str,&snd_irq[nr_dev]) == 2 &&
+               get_option(&str,&snd_dma8[nr_dev]) == 2 &&
+               get_option(&str,&snd_dma16[nr_dev]) == 2 &&
+               get_option(&str,&snd_mic_agc[nr_dev]) == 2 &&
+               get_option(&str,&snd_csp[nr_dev]) == 2
+#ifdef SNDRV_SBAWE_EMU8000
+               &&
+               get_option(&str,(int *)&snd_awe_port[nr_dev]) == 2 &&
+               get_option(&str,&snd_seq_ports[nr_dev]) == 2
+#endif
+              );
+#ifdef __ISAPNP__
+        if (pnp != INT_MAX)
+            snd_isapnp[nr_dev] = pnp;
+#endif
+#ifdef CONFIG_SND_SB16_CSP
+        if (csp != INT_MAX)
+            snd_csp[nr_dev] = csp;
+#endif
+        nr_dev++;
+        return 1;
+    }
+
+#ifndef SNDRV_SBAWE_EMU8000
+    __setup("snd-card-sb16=", alsa_card_sb16_setup);
+#else
+    __setup("snd-card-sbawe=", alsa_card_sb16_setup);
+#endif
+
+#endif /* ifndef MODULE */
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/sb/sb16_csp.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/sb/sb16_csp.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/sb/sb16_csp.c	(revision 91)
@@ -0,0 +1,1209 @@
+/*
+ *  Copyright (c) 1999 by Uros Bizjak <uros@kss-loka.si>
+ *                        Takashi Iwai <tiwai@suse.de>
+ *
+ *  SB16ASP/AWE32 CSP control
+ *
+ *  CSP microcode loader:
+ *   alsa-tools/sb16_csp/
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#define SNDRV_MAIN_OBJECT_FILE
+#include <sound/driver.h>
+#include <sound/control.h>
+#include <sound/info.h>
+#include <sound/sb16_csp.h>
+#include <sound/initval.h>
+
+MODULE_AUTHOR("Uros Bizjak <uros@kss-loka.si>");
+MODULE_DESCRIPTION("ALSA driver for SB16 Creative Signal Processor");
+MODULE_CLASSES("{sound}");
+
+#ifdef SNDRV_LITTLE_ENDIAN
+#define CSP_HDR_VALUE(a,b,c,d)	((a) | ((b)<<8) | ((c)<<16) | ((d)<<24))
+#define LE_SHORT(v)		(v)
+#define LE_INT(v)		(v)
+#else
+#include <byteswap.h>
+#define CSP_HDR_VALUE(a,b,c,d)	((d) | ((c)<<8) | ((b)<<16) | ((a)<<24))
+#define LE_SHORT(v)		bswap_16(v)
+#define LE_INT(v)		bswap_32(v)
+#endif
+
+#define RIFF_HEADER	CSP_HDR_VALUE('R', 'I', 'F', 'F')
+#define CSP__HEADER	CSP_HDR_VALUE('C', 'S', 'P', ' ')
+#define LIST_HEADER	CSP_HDR_VALUE('L', 'I', 'S', 'T')
+#define FUNC_HEADER	CSP_HDR_VALUE('f', 'u', 'n', 'c')
+#define CODE_HEADER	CSP_HDR_VALUE('c', 'o', 'd', 'e')
+#define INIT_HEADER	CSP_HDR_VALUE('i', 'n', 'i', 't')
+#define MAIN_HEADER	CSP_HDR_VALUE('m', 'a', 'i', 'n')
+
+/*
+ * RIFF data format
+ */
+typedef struct riff_header {
+    __u32 name;
+    __u32 len;
+} riff_header_t;
+
+typedef struct desc_header {
+    riff_header_t info;
+    __u16 func_nr;
+    __u16 VOC_type;
+    __u16 flags_play_rec;
+    __u16 flags_16bit_8bit;
+    __u16 flags_stereo_mono;
+    __u16 flags_rates;
+} desc_header_t;
+
+/*
+ * prototypes
+ */
+static void snd_sb_csp_free(snd_hwdep_t *hw);
+static int snd_sb_csp_open(snd_hwdep_t * hw, struct file *file);
+static int snd_sb_csp_ioctl(snd_hwdep_t * hw, struct file *file, unsigned int cmd, unsigned long arg);
+static int snd_sb_csp_release(snd_hwdep_t * hw, struct file *file);
+
+static int csp_detect(sb_t *chip, int *version);
+static int set_codec_parameter(sb_t *chip, unsigned char par, unsigned char val);
+static int set_register(sb_t *chip, unsigned char reg, unsigned char val);
+static int read_register(sb_t *chip, unsigned char reg);
+static int set_mode_register(sb_t *chip, unsigned char mode);
+static int get_version(sb_t *chip);
+
+static int snd_sb_csp_riff_load(snd_sb_csp_t * p, snd_sb_csp_microcode_t * code);
+static int snd_sb_csp_unload(snd_sb_csp_t * p);
+static int snd_sb_csp_load(snd_sb_csp_t * p, const unsigned char *buf, int size, int load_flags);
+static int snd_sb_csp_autoload(snd_sb_csp_t * p, int pcm_sfmt, int play_rec_mode);
+static int snd_sb_csp_check_version(snd_sb_csp_t * p);
+
+static int snd_sb_csp_use(snd_sb_csp_t * p);
+static int snd_sb_csp_unuse(snd_sb_csp_t * p);
+static int snd_sb_csp_start(snd_sb_csp_t * p, int sample_width, int channels);
+static int snd_sb_csp_stop(snd_sb_csp_t * p);
+static int snd_sb_csp_pause(snd_sb_csp_t * p);
+static int snd_sb_csp_restart(snd_sb_csp_t * p);
+
+static int snd_sb_qsound_build(snd_sb_csp_t * p);
+static void snd_sb_qsound_destroy(snd_sb_csp_t * p);
+static int snd_sb_csp_qsound_transfer(snd_sb_csp_t * p);
+
+static int init_proc_entry(snd_sb_csp_t * p, int device);
+static void delete_proc_entry(snd_sb_csp_t * p);
+static void info_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer);
+
+/*
+ * Detect CSP chip and create a new instance
+ */
+int snd_sb_csp_new(sb_t *chip, int device, snd_hwdep_t ** rhwdep)
+{
+    snd_sb_csp_t *p;
+    int version, err;
+    snd_hwdep_t *hw;
+
+    if (rhwdep)
+        *rhwdep = NULL;
+
+    if (csp_detect(chip, &version))
+        return -ENODEV;
+
+    if ((err = snd_hwdep_new(chip->card, "SB16-CSP", device, &hw)) < 0)
+        return err;
+
+    if ((p = kcalloc(1, sizeof(*p), GFP_KERNEL)) == NULL) {
+        snd_device_free(chip->card, hw);
+        return -ENOMEM;
+    }
+    p->chip = chip;
+    p->version = version;
+
+    /* CSP operators */
+    p->ops.csp_use = snd_sb_csp_use;
+    p->ops.csp_unuse = snd_sb_csp_unuse;
+    p->ops.csp_autoload = snd_sb_csp_autoload;
+    p->ops.csp_start = snd_sb_csp_start;
+    p->ops.csp_stop = snd_sb_csp_stop;
+    p->ops.csp_qsound_transfer = snd_sb_csp_qsound_transfer;
+
+    init_MUTEX(&p->access_mutex);
+    sprintf(hw->name, "CSP v%d.%d", (version >> 4), (version & 0x0f));
+    hw->iface = SNDRV_HWDEP_IFACE_SB16CSP;
+    hw->private_data = p;
+    hw->private_free = snd_sb_csp_free;
+
+    /* operators - only write/ioctl */
+    hw->ops.open = snd_sb_csp_open;
+    hw->ops.ioctl = snd_sb_csp_ioctl;
+    hw->ops.release = snd_sb_csp_release;
+
+    /* create a proc entry */
+    init_proc_entry(p, device);
+    if (rhwdep)
+        *rhwdep = hw;
+    return 0;
+}
+
+/*
+ * free_private for hwdep instance
+ */
+static void snd_sb_csp_free(snd_hwdep_t *hwdep)
+{
+    snd_sb_csp_t *p = hwdep->private_data;
+    if (p) {
+        if (p->running & SNDRV_SB_CSP_ST_RUNNING)
+            snd_sb_csp_stop(p);
+        delete_proc_entry(p);
+        kfree(p);
+    }
+}
+
+/* ------------------------------ */
+
+/*
+ * open the device exclusively
+ */
+static int snd_sb_csp_open(snd_hwdep_t * hw, struct file *file)
+{
+    snd_sb_csp_t *p = hw->private_data;
+    return (snd_sb_csp_use(p));
+}
+
+/*
+ * ioctl for hwdep device:
+ */
+static int snd_sb_csp_ioctl(snd_hwdep_t * hw, struct file *file, unsigned int cmd, unsigned long arg)
+{
+    snd_sb_csp_t *p = hw->private_data;
+    snd_sb_csp_info_t info;
+    snd_sb_csp_start_t start_info;
+    int err;
+
+    snd_assert(p != NULL, return -EINVAL);
+
+    if (snd_sb_csp_check_version(p))
+        return -ENODEV;
+
+    switch (cmd) {
+        /* get information */
+    case SNDRV_SB_CSP_IOCTL_INFO:
+        *info.codec_name = *p->codec_name;
+        info.func_nr = p->func_nr;
+        info.acc_format = p->acc_format;
+        info.acc_channels = p->acc_channels;
+        info.acc_width = p->acc_width;
+        info.acc_rates = p->acc_rates;
+        info.csp_mode = p->mode;
+        info.run_channels = p->run_channels;
+        info.run_width = p->run_width;
+        info.version = p->version;
+        info.state = p->running;
+        err = copy_to_user((void *) arg, &info, sizeof(info));
+        break;
+
+        /* load CSP microcode */
+    case SNDRV_SB_CSP_IOCTL_LOAD_CODE:
+        err = (p->running & SNDRV_SB_CSP_ST_RUNNING ?
+               -EBUSY : snd_sb_csp_riff_load(p, (snd_sb_csp_microcode_t *) arg));
+        break;
+    case SNDRV_SB_CSP_IOCTL_UNLOAD_CODE:
+        err = (p->running & SNDRV_SB_CSP_ST_RUNNING ?
+               -EBUSY : snd_sb_csp_unload(p));
+        break;
+
+        /* change CSP running state */
+    case SNDRV_SB_CSP_IOCTL_START:
+        if (copy_from_user(&start_info, (void *) arg, sizeof(start_info)))
+            err = -EFAULT;
+        else
+            err = snd_sb_csp_start(p, start_info.sample_width, start_info.channels);
+        break;
+    case SNDRV_SB_CSP_IOCTL_STOP:
+        err = snd_sb_csp_stop(p);
+        break;
+    case SNDRV_SB_CSP_IOCTL_PAUSE:
+        err = snd_sb_csp_pause(p);
+        break;
+    case SNDRV_SB_CSP_IOCTL_RESTART:
+        err = snd_sb_csp_restart(p);
+        break;
+    default:
+        err = -ENOTTY;
+        break;
+    }
+
+    return err;
+}
+
+/*
+ * close the device
+ */
+static int snd_sb_csp_release(snd_hwdep_t * hw, struct file *file)
+{
+    snd_sb_csp_t *p = hw->private_data;
+    return (snd_sb_csp_unuse(p));
+}
+
+/* ------------------------------ */
+
+/*
+ * acquire device
+ */
+static int snd_sb_csp_use(snd_sb_csp_t * p)
+{
+    down(&p->access_mutex);
+    if (p->used) {
+        up(&p->access_mutex);
+        return -EAGAIN;
+    }
+    p->used++;
+    up(&p->access_mutex);
+
+    return 0;
+
+}
+
+/*
+ * release device
+ */
+static int snd_sb_csp_unuse(snd_sb_csp_t * p)
+{
+    down(&p->access_mutex);
+    p->used--;
+    up(&p->access_mutex);
+
+    return 0;
+}
+
+/*
+ * load microcode via ioctl:
+ * code is user-space pointer
+ */
+static int snd_sb_csp_riff_load(snd_sb_csp_t * p, snd_sb_csp_microcode_t * mcode)
+{
+    snd_sb_csp_mc_header_t info;
+
+    unsigned char *data_ptr, *data_end;
+    unsigned short func_nr = 0;
+
+    riff_header_t file_h, item_h, code_h;
+    __u32 item_type;
+    desc_header_t funcdesc_h;
+
+    unsigned long flags;
+    int err;
+
+    if (copy_from_user(&info, mcode, sizeof(info)))
+        return -EFAULT;
+    data_ptr = mcode->data;
+
+    if (copy_from_user(&file_h, data_ptr, sizeof(file_h)))
+        return -EFAULT;
+    if ((file_h.name != RIFF_HEADER) ||
+        (LE_INT(file_h.len) >= SNDRV_SB_CSP_MAX_MICROCODE_FILE_SIZE - sizeof(file_h))) {
+        snd_printd(__FUNCTION__ ": Invalid RIFF header\n");
+        return -EINVAL;
+    }
+    data_ptr += sizeof(file_h);
+    data_end = data_ptr + LE_INT(file_h.len);
+
+    if (copy_from_user(&item_type, data_ptr, sizeof(item_type)))
+        return -EFAULT;
+    if (item_type != CSP__HEADER) {
+        snd_printd(__FUNCTION__ ": Invalid RIFF file type\n");
+        return -EINVAL;
+    }
+    data_ptr += sizeof (item_type);
+
+    for (; data_ptr < data_end; data_ptr += LE_INT(item_h.len)) {
+        if (copy_from_user(&item_h, data_ptr, sizeof(item_h)))
+            return -EFAULT;
+        data_ptr += sizeof(item_h);
+        if (item_h.name != LIST_HEADER)
+            continue;
+
+        if (copy_from_user(&item_type, data_ptr, sizeof(item_type)))
+            return -EFAULT;
+        switch (item_type) {
+        case FUNC_HEADER:
+            if (copy_from_user(&funcdesc_h, data_ptr + sizeof(item_type), sizeof(funcdesc_h)))
+                return -EFAULT;
+            func_nr = LE_SHORT(funcdesc_h.func_nr);
+            break;
+        case CODE_HEADER:
+            if (func_nr != info.func_req)
+                break;	/* not required function, try next */
+            data_ptr += sizeof(item_type);
+
+            /* destroy QSound mixer element */
+            if (p->mode == SNDRV_SB_CSP_MODE_QSOUND) {
+                snd_sb_qsound_destroy(p);
+            }
+            /* Clear all flags */
+            p->running = 0;
+            p->mode = 0;
+
+            /* load microcode blocks */
+            for (;;) {
+                if (data_ptr >= data_end)
+                    return -EINVAL;
+                if (copy_from_user(&code_h, data_ptr, sizeof(code_h)))
+                    return -EFAULT;
+
+                /* init microcode blocks */
+                if (code_h.name != INIT_HEADER)
+                    break;
+                data_ptr += sizeof(code_h);
+                err = snd_sb_csp_load(p, data_ptr, LE_INT(code_h.len),
+                                      SNDRV_SB_CSP_LOAD_INITBLOCK | SNDRV_SB_CSP_LOAD_FROMUSER);
+                if (err)
+                    return err;
+                data_ptr += LE_INT(code_h.len);
+            }
+            /* main microcode block */
+            if (copy_from_user(&code_h, data_ptr, sizeof(code_h)))
+                return -EFAULT;
+
+            if (code_h.name != MAIN_HEADER) {
+                snd_printd(__FUNCTION__ ": Missing 'main' microcode\n");
+                return -EINVAL;
+            }
+            data_ptr += sizeof(code_h);
+            err = snd_sb_csp_load(p, data_ptr, LE_INT(code_h.len),
+                                  SNDRV_SB_CSP_LOAD_FROMUSER);
+            if (err)
+                return err;
+
+            /* fill in codec header */
+            strncpy(p->codec_name, info.codec_name, sizeof(p->codec_name) - 1);
+            p->codec_name[sizeof(p->codec_name) - 1] = 0;
+            p->func_nr = func_nr;
+            p->mode = LE_SHORT(funcdesc_h.flags_play_rec);
+            switch (LE_SHORT(funcdesc_h.VOC_type)) {
+            case 0x0001:	/* QSound decoder */
+                if (LE_SHORT(funcdesc_h.flags_play_rec) == SNDRV_SB_CSP_MODE_DSP_WRITE) {
+                    if (snd_sb_qsound_build(p) == 0)
+                        /* set QSound flag and clear all other mode flags */
+                        p->mode = SNDRV_SB_CSP_MODE_QSOUND;
+                }
+                p->acc_format = 0;
+                break;
+            case 0x0006:	/* A Law codec */
+                p->acc_format = SNDRV_PCM_FMTBIT_A_LAW;
+                break;
+            case 0x0007:	/* Mu Law codec */
+                p->acc_format = SNDRV_PCM_FMTBIT_MU_LAW;
+                break;
+            case 0x0011:	/* what Creative thinks is IMA ADPCM codec */
+            case 0x0200:	/* Creative ADPCM codec */
+                p->acc_format = SNDRV_PCM_FMTBIT_IMA_ADPCM;
+                break;
+            case    201:	/* Text 2 Speech decoder */
+                /* TODO: Text2Speech handling routines */
+                p->acc_format = 0;
+                break;
+            case 0x0202:	/* Fast Speech 8 codec */
+            case 0x0203:	/* Fast Speech 10 codec */
+                p->acc_format = SNDRV_PCM_FMTBIT_SPECIAL;
+                break;
+            default:	/* other codecs are unsupported */
+                p->acc_format = p->acc_width = p->acc_rates = 0;
+                p->mode = 0;
+                snd_printd(__FUNCTION__ ": Unsupported CSP codec type: 0x%04x\n",
+                           LE_SHORT(funcdesc_h.VOC_type));
+                return -EINVAL;
+            }
+            p->acc_channels = LE_SHORT(funcdesc_h.flags_stereo_mono);
+            p->acc_width = LE_SHORT(funcdesc_h.flags_16bit_8bit);
+            p->acc_rates = LE_SHORT(funcdesc_h.flags_rates);
+
+            /* Decouple CSP from IRQ and DMAREQ lines */
+            spin_lock_irqsave(&p->chip->reg_lock, flags);
+            set_mode_register(p->chip, 0xfc);
+            set_mode_register(p->chip, 0x00);
+            spin_unlock_irqrestore(&p->chip->reg_lock, flags);
+
+            /* finished loading successfully */
+            p->running = SNDRV_SB_CSP_ST_LOADED;	/* set LOADED flag */
+            return 0;
+        }
+    }
+    snd_printd(__FUNCTION__ ": Function #%d not found\n", info.func_req);
+    return -EINVAL;
+}
+
+/*
+ * unload CSP microcode
+ */
+static int snd_sb_csp_unload(snd_sb_csp_t * p)
+{
+    if (p->running & SNDRV_SB_CSP_ST_RUNNING)
+        return -EBUSY;
+    if (!(p->running & SNDRV_SB_CSP_ST_LOADED))
+        return -ENXIO;
+
+    /* clear supported formats */
+    p->acc_format = 0;
+    p->acc_channels = p->acc_width = p->acc_rates = 0;
+    /* destroy QSound mixer element */
+    if (p->mode == SNDRV_SB_CSP_MODE_QSOUND) {
+        snd_sb_qsound_destroy(p);
+    }
+    /* clear all flags */
+    p->running = 0;
+    p->mode = 0;
+    return 0;
+}
+
+/*
+ * send command sequence to DSP
+ */
+static inline int command_seq(sb_t *chip, const unsigned char *seq, int size)
+{
+    int i;
+    for (i = 0; i < size; i++) {
+        if (!snd_sbdsp_command(chip, seq[i]))
+            return -EIO;
+    }
+    return 0;
+}
+
+/*
+ * set CSP codec parameter
+ */
+static int set_codec_parameter(sb_t *chip, unsigned char par, unsigned char val)
+{
+    unsigned char dsp_cmd[3];
+
+    dsp_cmd[0] = 0x05;	/* CSP set codec parameter */
+    dsp_cmd[1] = val;	/* Parameter value */
+    dsp_cmd[2] = par;	/* Parameter */
+    command_seq(chip, dsp_cmd, 3);
+    snd_sbdsp_command(chip, 0x03);	/* DSP read? */
+    if (snd_sbdsp_get_byte(chip) != par)
+        return -EIO;
+    return 0;
+}
+
+/*
+ * set CSP register
+ */
+static int set_register(sb_t *chip, unsigned char reg, unsigned char val)
+{
+    unsigned char dsp_cmd[3];
+
+    dsp_cmd[0] = 0x0e;	/* CSP set register */
+    dsp_cmd[1] = reg;	/* CSP Register */
+    dsp_cmd[2] = val;	/* value */
+    return command_seq(chip, dsp_cmd, 3);
+}
+
+/*
+ * read CSP register
+ * return < 0 -> error
+ */
+static int read_register(sb_t *chip, unsigned char reg)
+{
+    unsigned char dsp_cmd[2];
+
+    dsp_cmd[0] = 0x0f;	/* CSP read register */
+    dsp_cmd[1] = reg;	/* CSP Register */
+    command_seq(chip, dsp_cmd, 2);
+    return snd_sbdsp_get_byte(chip);	/* Read DSP value */
+}
+
+/*
+ * set CSP mode register
+ */
+static int set_mode_register(sb_t *chip, unsigned char mode)
+{
+    unsigned char dsp_cmd[2];
+
+    dsp_cmd[0] = 0x04;	/* CSP set mode register */
+    dsp_cmd[1] = mode;	/* mode */
+    return command_seq(chip, dsp_cmd, 2);
+}
+
+/*
+ * Detect CSP
+ * return 0 if CSP exists.
+ */
+static int csp_detect(sb_t *chip, int *version)
+{
+    unsigned char csp_test1, csp_test2;
+    unsigned long flags;
+    int result = -ENODEV;
+
+    spin_lock_irqsave(&chip->reg_lock, flags);
+
+    set_codec_parameter(chip, 0x00, 0x00);
+    set_mode_register(chip, 0xfc);		/* 0xfc = ?? */
+
+    csp_test1 = read_register(chip, 0x83);
+    set_register(chip, 0x83, ~csp_test1);
+    csp_test2 = read_register(chip, 0x83);
+    if (csp_test2 != (csp_test1 ^ 0xff))
+        goto __fail;
+
+    set_register(chip, 0x83, csp_test1);
+    csp_test2 = read_register(chip, 0x83);
+    if (csp_test2 != csp_test1)
+        goto __fail;
+
+    set_mode_register(chip, 0x00);		/* 0x00 = ? */
+
+    *version = get_version(chip);
+    snd_sbdsp_reset(chip);	/* reset DSP after getversion! */
+    if (*version >= 0x10 && *version <= 0x1f)
+        result = 0;		/* valid version id */
+
+__fail:
+    spin_unlock_irqrestore(&chip->reg_lock, flags);
+    return result;
+}
+
+/*
+ * get CSP version number
+ */
+static int get_version(sb_t *chip)
+{
+    unsigned char dsp_cmd[2];
+
+    dsp_cmd[0] = 0x08;	/* SB_DSP_!something! */
+    dsp_cmd[1] = 0x03;	/* get chip version id? */
+    command_seq(chip, dsp_cmd, 2);
+
+    return (snd_sbdsp_get_byte(chip));
+}
+
+/*
+ * check if the CSP version is valid
+ */
+static int snd_sb_csp_check_version(snd_sb_csp_t * p)
+{
+    if (p->version < 0x10 || p->version > 0x1f) {
+        snd_printd(__FUNCTION__ ": Invalid CSP version: 0x%x\n", p->version);
+        return 1;
+    }
+    return 0;
+}
+
+/*
+ * download microcode to CSP (microcode should have one "main" block).
+ */
+static int snd_sb_csp_load(snd_sb_csp_t * p, const unsigned char *buf, int size, int load_flags)
+{
+    int status, i;
+    int err;
+    int result = -EIO;
+    unsigned long flags;
+
+    spin_lock_irqsave(&p->chip->reg_lock, flags);
+    snd_sbdsp_command(p->chip, 0x01);	/* CSP download command */
+    if (snd_sbdsp_get_byte(p->chip)) {
+        snd_printd(__FUNCTION__ ": Download command failed\n");
+        goto __fail;
+    }
+    /* Send CSP low byte (size - 1) */
+    snd_sbdsp_command(p->chip, (unsigned char)(size - 1));
+    /* Send high byte */
+    snd_sbdsp_command(p->chip, (unsigned char)((size - 1) >> 8));
+    /* send microcode sequence */
+    if (load_flags & SNDRV_SB_CSP_LOAD_FROMUSER) {
+        /* copy microcode from user space */
+        unsigned char *kbuf, *_kbuf;
+        _kbuf = kbuf = kmalloc (size, GFP_KERNEL);
+        if (copy_from_user(kbuf, buf, size)) {
+            result = -EFAULT;
+            kfree (_kbuf);
+            goto __fail;
+        }
+        while (size--) {
+            if (!snd_sbdsp_command(p->chip, *kbuf++)) {
+                kfree (_kbuf);
+                goto __fail;
+            }
+        }
+        kfree (_kbuf);
+    } else {
+        /* load from kernel space */
+        while (size--) {
+            if (!snd_sbdsp_command(p->chip, *buf++))
+                goto __fail;
+        }
+    }
+    if (snd_sbdsp_get_byte(p->chip))
+        goto __fail;
+
+    if (load_flags & SNDRV_SB_CSP_LOAD_INITBLOCK) {
+        i = 0;
+        /* some codecs (FastSpeech) take some time to initialize */
+        while (1) {
+            snd_sbdsp_command(p->chip, 0x03);
+            status = snd_sbdsp_get_byte(p->chip);
+            if (status == 0x55 || ++i >= 10)
+                break;
+            udelay (10);
+        }
+        if (status != 0x55) {
+            snd_printd(__FUNCTION__ ": Microcode initialization failed\n");
+            goto __fail;
+        }
+    } else {
+        /*
+         * Read mixer register SB_DSP4_DMASETUP after loading 'main' code.
+         * Start CSP chip if no 16bit DMA channel is set - some kind
+         * of autorun or perhaps a bugfix?
+         */
+        spin_lock(&p->chip->mixer_lock);
+        status = snd_sbmixer_read(p->chip, SB_DSP4_DMASETUP);
+        spin_unlock(&p->chip->mixer_lock);
+        if (!(status & (SB_DMASETUP_DMA7 | SB_DMASETUP_DMA6 | SB_DMASETUP_DMA5))) {
+            err = (set_codec_parameter(p->chip, 0xaa, 0x00) ||
+                   set_codec_parameter(p->chip, 0xff, 0x00));
+            snd_sbdsp_reset(p->chip);		/* really! */
+            if (err)
+                goto __fail;
+            set_mode_register(p->chip, 0xc0);	/* c0 = STOP */
+            set_mode_register(p->chip, 0x70);	/* 70 = RUN */
+        }
+    }
+    result = 0;
+
+__fail:
+    spin_unlock_irqrestore(&p->chip->reg_lock, flags);
+    return result;
+}
+
+#include "sb16_csp_codecs.h"
+
+/*
+ * autoload hardware codec if necessary
+ * return 0 if CSP is loaded and ready to run (p->running != 0)
+ */
+static int snd_sb_csp_autoload(snd_sb_csp_t * p, int pcm_sfmt, int play_rec_mode)
+{
+    unsigned long flags;
+    int err = 0;
+
+    /* if CSP is running or manually loaded then exit */
+    if (p->running & (SNDRV_SB_CSP_ST_RUNNING | SNDRV_SB_CSP_ST_LOADED))
+        return -EBUSY;
+
+    /* autoload microcode only if requested hardware codec is not already loaded */
+    if (((1 << pcm_sfmt) & p->acc_format) && (play_rec_mode & p->mode)) {
+        p->running = SNDRV_SB_CSP_ST_AUTO;
+    } else {
+        switch (pcm_sfmt) {
+        case SNDRV_PCM_FORMAT_MU_LAW:
+            err = snd_sb_csp_load(p, &mulaw_main[0], sizeof(mulaw_main), 0);
+            p->acc_format = SNDRV_PCM_FMTBIT_MU_LAW;
+            p->mode = SNDRV_SB_CSP_MODE_DSP_READ | SNDRV_SB_CSP_MODE_DSP_WRITE;
+            break;
+        case SNDRV_PCM_FORMAT_A_LAW:
+            err = snd_sb_csp_load(p, &alaw_main[0], sizeof(alaw_main), 0);
+            p->acc_format = SNDRV_PCM_FMTBIT_A_LAW;
+            p->mode = SNDRV_SB_CSP_MODE_DSP_READ | SNDRV_SB_CSP_MODE_DSP_WRITE;
+            break;
+        case SNDRV_PCM_FORMAT_IMA_ADPCM:
+            err = snd_sb_csp_load(p, &ima_adpcm_init[0], sizeof(ima_adpcm_init),
+                                  SNDRV_SB_CSP_LOAD_INITBLOCK);
+            if (err)
+                break;
+            if (play_rec_mode == SNDRV_SB_CSP_MODE_DSP_WRITE) {
+                err = snd_sb_csp_load(p, &ima_adpcm_playback[0],
+                                      sizeof(ima_adpcm_playback), 0);
+                p->mode = SNDRV_SB_CSP_MODE_DSP_WRITE;
+            } else {
+                err = snd_sb_csp_load(p, &ima_adpcm_capture[0],
+                                      sizeof(ima_adpcm_capture), 0);
+                p->mode = SNDRV_SB_CSP_MODE_DSP_READ;
+            }
+            p->acc_format = SNDRV_PCM_FMTBIT_IMA_ADPCM;
+            break;
+        default:
+            /* Decouple CSP from IRQ and DMAREQ lines */
+            if (p->running & SNDRV_SB_CSP_ST_AUTO) {
+                spin_lock_irqsave(&p->chip->reg_lock, flags);
+                set_mode_register(p->chip, 0xfc);
+                set_mode_register(p->chip, 0x00);
+                spin_unlock_irqrestore(&p->chip->reg_lock, flags);
+                p->running = 0;			/* clear autoloaded flag */
+            }
+            return -EINVAL;
+        }
+        if (err) {
+            p->acc_format = 0;
+            p->acc_channels = p->acc_width = p->acc_rates = 0;
+
+            p->running = 0;				/* clear autoloaded flag */
+            p->mode = 0;
+            return (err);
+        } else {
+            p->running = SNDRV_SB_CSP_ST_AUTO;	/* set autoloaded flag */
+            p->acc_width = SNDRV_SB_CSP_SAMPLE_16BIT;	/* only 16 bit data */
+            p->acc_channels = SNDRV_SB_CSP_MONO | SNDRV_SB_CSP_STEREO;
+            p->acc_rates = SNDRV_SB_CSP_RATE_ALL;	/* HW codecs accept all rates */
+        }
+
+    }
+    return (p->running & SNDRV_SB_CSP_ST_AUTO) ? 0 : -ENXIO;
+}
+
+/*
+ * start CSP
+ */
+static int snd_sb_csp_start(snd_sb_csp_t * p, int sample_width, int channels)
+{
+    unsigned char s_type;	/* sample type */
+    unsigned char mixL, mixR;
+    int result = -EIO;
+    unsigned long flags;
+
+    if (!(p->running & (SNDRV_SB_CSP_ST_LOADED | SNDRV_SB_CSP_ST_AUTO))) {
+        snd_printd(__FUNCTION__ ": Microcode not loaded\n");
+        return -ENXIO;
+    }
+    if (p->running & SNDRV_SB_CSP_ST_RUNNING) {
+        snd_printd(__FUNCTION__ ": CSP already running\n");
+        return -EBUSY;
+    }
+    if (!(sample_width & p->acc_width)) {
+        snd_printd(__FUNCTION__ ": Unsupported PCM sample width\n");
+        return -EINVAL;
+    }
+    if (!(channels & p->acc_channels)) {
+        snd_printd(__FUNCTION__ ": Invalid number of channels\n");
+        return -EINVAL;
+    }
+
+    /* Mute PCM volume */
+    spin_lock_irqsave(&p->chip->mixer_lock, flags);
+    mixL = snd_sbmixer_read(p->chip, SB_DSP4_PCM_DEV);
+    mixR = snd_sbmixer_read(p->chip, SB_DSP4_PCM_DEV + 1);
+    snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV, mixL & 0x7);
+    snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV + 1, mixR & 0x7);
+
+    spin_lock(&p->chip->reg_lock);
+    set_mode_register(p->chip, 0xc0);	/* c0 = STOP */
+    set_mode_register(p->chip, 0x70);	/* 70 = RUN */
+
+    s_type = 0x00;
+    if (channels == SNDRV_SB_CSP_MONO)
+        s_type = 0x11;	/* 000n 000n    (n = 1 if mono) */
+    if (sample_width == SNDRV_SB_CSP_SAMPLE_8BIT)
+        s_type |= 0x22;	/* 00dX 00dX    (d = 1 if 8 bit samples) */
+
+    if (set_codec_parameter(p->chip, 0x81, s_type)) {
+        snd_printd(__FUNCTION__ ": Set sample type command failed\n");
+        goto __fail;
+    }
+    if (set_codec_parameter(p->chip, 0x80, 0x00)) {
+        snd_printd(__FUNCTION__ ": Codec start command failed\n");
+        goto __fail;
+    }
+    p->run_width = sample_width;
+    p->run_channels = channels;
+
+    p->running |= SNDRV_SB_CSP_ST_RUNNING;
+
+    if (p->mode & SNDRV_SB_CSP_MODE_QSOUND) {
+        set_codec_parameter(p->chip, 0xe0, 0x01);
+        /* enable QSound decoder */
+        set_codec_parameter(p->chip, 0x00, 0xff);
+        set_codec_parameter(p->chip, 0x01, 0xff);
+        p->running |= SNDRV_SB_CSP_ST_QSOUND;
+        /* set QSound startup value */
+        snd_sb_csp_qsound_transfer(p);
+    }
+    result = 0;
+
+__fail:
+    spin_unlock(&p->chip->reg_lock);
+
+    /* restore PCM volume */
+    snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV, mixL);
+    snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV + 1, mixR);
+    spin_unlock_irqrestore(&p->chip->mixer_lock, flags);
+
+    return result;
+}
+
+/*
+ * stop CSP
+ */
+static int snd_sb_csp_stop(snd_sb_csp_t * p)
+{
+    int result;
+    unsigned char mixL, mixR;
+    unsigned long flags;
+
+    if (!(p->running & SNDRV_SB_CSP_ST_RUNNING))
+        return 0;
+
+    /* Mute PCM volume */
+    spin_lock_irqsave(&p->chip->mixer_lock, flags);
+    mixL = snd_sbmixer_read(p->chip, SB_DSP4_PCM_DEV);
+    mixR = snd_sbmixer_read(p->chip, SB_DSP4_PCM_DEV + 1);
+    snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV, mixL & 0x7);
+    snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV + 1, mixR & 0x7);
+
+    spin_lock(&p->chip->reg_lock);
+    if (p->running & SNDRV_SB_CSP_ST_QSOUND) {
+        set_codec_parameter(p->chip, 0xe0, 0x01);
+        /* disable QSound decoder */
+        set_codec_parameter(p->chip, 0x00, 0x00);
+        set_codec_parameter(p->chip, 0x01, 0x00);
+
+        p->running &= ~SNDRV_SB_CSP_ST_QSOUND;
+    }
+    result = set_mode_register(p->chip, 0xc0);	/* c0 = STOP */
+    spin_unlock(&p->chip->reg_lock);
+
+    /* restore PCM volume */
+    snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV, mixL);
+    snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV + 1, mixR);
+    spin_unlock_irqrestore(&p->chip->mixer_lock, flags);
+
+    if (!(result))
+        p->running &= ~(SNDRV_SB_CSP_ST_PAUSED | SNDRV_SB_CSP_ST_RUNNING);
+    return result;
+}
+
+/*
+ * pause CSP codec and hold DMA transfer
+ */
+static int snd_sb_csp_pause(snd_sb_csp_t * p)
+{
+    int result;
+    unsigned long flags;
+
+    if (!(p->running & SNDRV_SB_CSP_ST_RUNNING))
+        return -EBUSY;
+
+    spin_lock_irqsave(&p->chip->reg_lock, flags);
+    result = set_codec_parameter(p->chip, 0x80, 0xff);
+    spin_unlock_irqrestore(&p->chip->reg_lock, flags);
+    if (!(result))
+        p->running |= SNDRV_SB_CSP_ST_PAUSED;
+
+    return result;
+}
+
+/*
+ * restart CSP codec and resume DMA transfer
+ */
+static int snd_sb_csp_restart(snd_sb_csp_t * p)
+{
+    int result;
+    unsigned long flags;
+
+    if (!(p->running & SNDRV_SB_CSP_ST_PAUSED))
+        return -EBUSY;
+
+    spin_lock_irqsave(&p->chip->reg_lock, flags);
+    result = set_codec_parameter(p->chip, 0x80, 0x00);
+    spin_unlock_irqrestore(&p->chip->reg_lock, flags);
+    if (!(result))
+        p->running &= ~SNDRV_SB_CSP_ST_PAUSED;
+
+    return result;
+}
+
+/* ------------------------------ */
+
+/*
+ * QSound mixer control for PCM
+ */
+
+static int snd_sb_qsound_switch_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_sb_qsound_switch_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+    snd_sb_csp_t *p = snd_kcontrol_chip(kcontrol);
+
+    ucontrol->value.integer.value[0] = p->q_enabled ? 1 : 0;
+    return 0;
+}
+
+static int snd_sb_qsound_switch_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+    snd_sb_csp_t *p = snd_kcontrol_chip(kcontrol);
+    unsigned long flags;
+    int change;
+    unsigned char nval;
+
+    nval = ucontrol->value.integer.value[0] & 0x01;
+    spin_lock_irqsave(&p->q_lock, flags);
+    change = p->q_enabled != nval;
+    p->q_enabled = nval;
+    spin_unlock_irqrestore(&p->q_lock, flags);
+    return change;
+}
+
+static int snd_sb_qsound_space_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+    uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+    uinfo->count = 2;
+    uinfo->value.integer.min = 0;
+    uinfo->value.integer.max = SNDRV_SB_CSP_QSOUND_MAX_RIGHT;
+    return 0;
+}
+
+static int snd_sb_qsound_space_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+    snd_sb_csp_t *p = snd_kcontrol_chip(kcontrol);
+    unsigned long flags;
+
+    spin_lock_irqsave(&p->q_lock, flags);
+    ucontrol->value.integer.value[0] = p->qpos_left;
+    ucontrol->value.integer.value[1] = p->qpos_right;
+    spin_unlock_irqrestore(&p->q_lock, flags);
+    return 0;
+}
+
+static int snd_sb_qsound_space_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+    snd_sb_csp_t *p = snd_kcontrol_chip(kcontrol);
+    unsigned long flags;
+    int change;
+    unsigned char nval1, nval2;
+
+    nval1 = ucontrol->value.integer.value[0];
+    if (nval1 > SNDRV_SB_CSP_QSOUND_MAX_RIGHT)
+        nval1 = SNDRV_SB_CSP_QSOUND_MAX_RIGHT;
+    nval2 = ucontrol->value.integer.value[1];
+    if (nval2 > SNDRV_SB_CSP_QSOUND_MAX_RIGHT)
+        nval2 = SNDRV_SB_CSP_QSOUND_MAX_RIGHT;
+    spin_lock_irqsave(&p->q_lock, flags);
+    change = p->qpos_left != nval1 || p->qpos_right != nval2;
+    p->qpos_left = nval1;
+    p->qpos_right = nval2;
+    p->qpos_changed = change;
+    spin_unlock_irqrestore(&p->q_lock, flags);
+    return change;
+}
+#ifdef TARGET_OS2
+static snd_kcontrol_new_t snd_sb_qsound_switch = {
+    SNDRV_CTL_ELEM_IFACE_MIXER,0,0,
+    "3D Control - Switch",0,0, 0,
+    snd_sb_qsound_switch_info,
+    snd_sb_qsound_switch_get,
+    snd_sb_qsound_switch_put,0
+};
+
+static snd_kcontrol_new_t snd_sb_qsound_space = {
+    SNDRV_CTL_ELEM_IFACE_MIXER,0,0,
+    "3D Control - Space",0,0, 0,
+    snd_sb_qsound_space_info,
+    snd_sb_qsound_space_get,
+    snd_sb_qsound_space_put,0
+};
+#else
+static snd_kcontrol_new_t snd_sb_qsound_switch = {
+iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+    name: "3D Control - Switch",
+    info: snd_sb_qsound_switch_info,
+    get: snd_sb_qsound_switch_get,
+    put: snd_sb_qsound_switch_put
+};
+
+static snd_kcontrol_new_t snd_sb_qsound_space = {
+iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+    name: "3D Control - Space",
+    info: snd_sb_qsound_space_info,
+    get: snd_sb_qsound_space_get,
+    put: snd_sb_qsound_space_put
+};
+#endif
+
+static int snd_sb_qsound_build(snd_sb_csp_t * p)
+{
+    snd_card_t * card;
+    int err;
+
+    snd_assert(p != NULL, return -EINVAL);
+
+    card = p->chip->card;
+    p->qpos_left = p->qpos_right = SNDRV_SB_CSP_QSOUND_MAX_RIGHT / 2;
+    p->qpos_changed = 0;
+
+    spin_lock_init(&p->q_lock);
+
+    if ((err = snd_ctl_add(card, p->qsound_switch = snd_ctl_new1(&snd_sb_qsound_switch, p))) < 0)
+        goto __error;
+    if ((err = snd_ctl_add(card, p->qsound_space = snd_ctl_new1(&snd_sb_qsound_space, p))) < 0)
+        goto __error;
+
+    return 0;
+
+__error:
+    snd_sb_qsound_destroy(p);
+    return err;
+}
+
+static void snd_sb_qsound_destroy(snd_sb_csp_t * p)
+{
+    snd_card_t * card;
+    unsigned long flags;
+
+    snd_assert(p != NULL, return);
+
+    card = p->chip->card;
+
+    if (p->qsound_switch)
+        snd_ctl_remove(card, p->qsound_switch);
+    if (p->qsound_space)
+        snd_ctl_remove(card, p->qsound_space);
+
+    /* cancel pending transfer of QSound parameters */
+    spin_lock_irqsave (&p->q_lock, flags);
+    p->qpos_changed = 0;
+    spin_unlock_irqrestore (&p->q_lock, flags);
+}
+
+/*
+ * Transfer qsound parameters to CSP,
+ * function should be called from interrupt routine
+ */
+static int snd_sb_csp_qsound_transfer(snd_sb_csp_t * p)
+{
+    int err = -ENXIO;
+
+    spin_lock(&p->q_lock);
+    if (p->running & SNDRV_SB_CSP_ST_QSOUND) {
+        set_codec_parameter(p->chip, 0xe0, 0x01);
+        /* left channel */
+        set_codec_parameter(p->chip, 0x00, p->qpos_left);
+        set_codec_parameter(p->chip, 0x02, 0x00);
+        /* right channel */
+        set_codec_parameter(p->chip, 0x00, p->qpos_right);
+        set_codec_parameter(p->chip, 0x03, 0x00);
+        err = 0;
+    }
+    p->qpos_changed = 0;
+    spin_unlock(&p->q_lock);
+    return err;
+}
+
+/* ------------------------------ */
+
+/*
+ * proc interface
+ */
+static int init_proc_entry(snd_sb_csp_t * p, int device)
+{
+    char name[16];
+    snd_info_entry_t *entry;
+    sprintf(name, "cspD%d", device);
+    entry = p->proc = snd_info_create_card_entry(p->chip->card, name, p->chip->card->proc_root);
+    if (entry) {
+        entry->content = SNDRV_INFO_CONTENT_TEXT;
+        entry->c.text.read_size = 256;
+        entry->c.text.read = info_read;
+        entry->private_data = p;
+        if (snd_info_register(entry) < 0) {
+            snd_info_free_entry(entry);
+            p->proc = NULL;
+        }
+    }
+    return 0;
+}
+
+static void delete_proc_entry(snd_sb_csp_t * p)
+{
+    if (p->proc) {
+        snd_info_unregister(p->proc);
+        p->proc = NULL;
+    }
+}
+
+static void info_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer)
+{
+    snd_sb_csp_t *p = entry->private_data;
+
+    snd_iprintf(buffer, "Creative Signal Processor [v%d.%d]\n", (p->version >> 4), (p->version & 0x0f));
+    snd_iprintf(buffer, "State: %cx%c%c%c\n", ((p->running & SNDRV_SB_CSP_ST_QSOUND) ? 'Q' : '-'),
+                ((p->running & SNDRV_SB_CSP_ST_PAUSED) ? 'P' : '-'),
+                ((p->running & SNDRV_SB_CSP_ST_RUNNING) ? 'R' : '-'),
+                ((p->running & SNDRV_SB_CSP_ST_LOADED) ? 'L' : '-'));
+    if (p->running & SNDRV_SB_CSP_ST_LOADED) {
+        snd_iprintf(buffer, "Codec: %s [func #%d]\n", p->codec_name, p->func_nr);
+        snd_iprintf(buffer, "Sample rates: ");
+        if (p->acc_rates == SNDRV_SB_CSP_RATE_ALL) {
+            snd_iprintf(buffer, "All\n");
+        } else {
+            snd_iprintf(buffer, "%s%s%s%s\n",
+                        ((p->acc_rates & SNDRV_SB_CSP_RATE_8000) ? "8000Hz " : ""),
+                        ((p->acc_rates & SNDRV_SB_CSP_RATE_11025) ? "11025Hz " : ""),
+                        ((p->acc_rates & SNDRV_SB_CSP_RATE_22050) ? "22050Hz " : ""),
+                        ((p->acc_rates & SNDRV_SB_CSP_RATE_44100) ? "44100Hz" : ""));
+        }
+        if (p->mode == SNDRV_SB_CSP_MODE_QSOUND) {
+            snd_iprintf(buffer, "QSound decoder %sabled\n",
+                        p->q_enabled ? "en" : "dis");
+        } else {
+            snd_iprintf(buffer, "PCM format ID: 0x%x (%s/%s) [%s/%s] [%s/%s]\n",
+                        p->acc_format,
+                        ((p->acc_width & SNDRV_SB_CSP_SAMPLE_16BIT) ? "16bit" : "-"),
+                        ((p->acc_width & SNDRV_SB_CSP_SAMPLE_8BIT) ? "8bit" : "-"),
+                        ((p->acc_channels & SNDRV_SB_CSP_MONO) ? "mono" : "-"),
+                        ((p->acc_channels & SNDRV_SB_CSP_STEREO) ? "stereo" : "-"),
+                        ((p->mode & SNDRV_SB_CSP_MODE_DSP_WRITE) ? "playback" : "-"),
+                        ((p->mode & SNDRV_SB_CSP_MODE_DSP_READ) ? "capture" : "-"));
+        }
+    }
+    if (p->running & SNDRV_SB_CSP_ST_AUTO) {
+        snd_iprintf(buffer, "Autoloaded Mu-Law, A-Law or Ima-ADPCM hardware codec\n");
+    }
+    if (p->running & SNDRV_SB_CSP_ST_RUNNING) {
+        snd_iprintf(buffer, "Processing %dbit %s PCM samples\n",
+                    ((p->run_width & SNDRV_SB_CSP_SAMPLE_16BIT) ? 16 : 8),
+                    ((p->run_channels & SNDRV_SB_CSP_MONO) ? "mono" : "stereo"));
+    }
+    if (p->running & SNDRV_SB_CSP_ST_QSOUND) {
+        snd_iprintf(buffer, "Qsound position: left = 0x%x, right = 0x%x\n",
+                    p->qpos_left, p->qpos_right);
+    }
+}
+
+/* */
+
+EXPORT_SYMBOL(snd_sb_csp_new);
+
+/*
+ * INIT part
+ */
+
+static int __init alsa_sb_csp_init(void)
+{
+    return 0;
+}
+
+static void __exit alsa_sb_csp_exit(void)
+{
+}
+
+module_init(alsa_sb_csp_init)
+module_exit(alsa_sb_csp_exit)
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/sb/sb16_csp_codecs.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/sb/sb16_csp_codecs.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/sb/sb16_csp_codecs.h	(revision 91)
@@ -0,0 +1,949 @@
+/*
+ *  Copyright (c) 1994 Creative Technology Ltd.
+ *  Microcode files for SB16 Advanced Signal Processor
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+static unsigned char mulaw_main[] = {
+	0x00, 0x10, 0x00, 0x44, 0x08, 0x00, 0x00, 0x44,
+	0x00, 0xb1, 0x00, 0x44, 0x00, 0x61, 0x00, 0x44,
+	0x08, 0x50, 0x00, 0x44, 0x0d, 0xf2, 0x61, 0xa8,
+	0x44, 0x04, 0x04, 0x19, 0x00, 0x00, 0x40, 0x45,
+	0x40, 0x49, 0x39, 0xac, 0x55, 0x55, 0x71, 0x8b,
+	0x50, 0x05, 0x63, 0x80, 0x00, 0x00, 0x06, 0x39,
+	0xff, 0x2e, 0x21, 0x49, 0xff, 0x0f, 0xd4, 0x49,
+	0x20, 0x01, 0x09, 0x0e, 0x20, 0x00, 0x71, 0x8b,
+	0xa8, 0x01, 0xa8, 0x80, 0x88, 0x01, 0xa8, 0x80,
+	0xa8, 0x00, 0x00, 0x80, 0xd2, 0x00, 0x71, 0x8b,
+	0x88, 0x00, 0xa8, 0x80, 0xa8, 0x04, 0xb3, 0x80,
+	0x20, 0x07, 0xb3, 0x80, 0x88, 0x03, 0xb1, 0x80,
+	0xc0, 0x00, 0x09, 0x5c, 0xc2, 0x01, 0x00, 0x82,
+	0xa1, 0x00, 0x71, 0x8b, 0xcd, 0x00, 0x04, 0x19,
+	0xa2, 0x20, 0x71, 0x8b, 0xcf, 0x00, 0x04, 0x19,
+	0x00, 0x00, 0xb1, 0x80, 0xc2, 0x00, 0x04, 0x19,
+	0x00, 0x40, 0x00, 0x14, 0x08, 0x40, 0x04, 0x24,
+	0x00, 0x00, 0x34, 0x49, 0x0c, 0x40, 0x00, 0x44,
+	0x44, 0x04, 0x04, 0x39, 0x00, 0x00, 0x40, 0x45,
+	0x32, 0x00, 0x09, 0x5c, 0x00, 0x00, 0x0c, 0x39,
+	0x00, 0x00, 0x40, 0x45, 0x40, 0x40, 0x09, 0xef,
+	0xff, 0x20, 0x09, 0xcf, 0x00, 0x04, 0x63, 0xa1,
+	0x50, 0x03, 0x33, 0x80, 0x00, 0x04, 0xa3, 0x80,
+	0x00, 0xff, 0xc2, 0x8b, 0x00, 0xd0, 0x04, 0x54,
+	0x04, 0xe0, 0x00, 0xc4, 0x20, 0x03, 0x80, 0xc0,
+	0x30, 0x00, 0x00, 0x88, 0x00, 0x00, 0x7a, 0x0a,
+	0xd0, 0x01, 0x00, 0x82, 0x00, 0x60, 0x00, 0x44,
+	0xc0, 0x00, 0x00, 0x99, 0x00, 0x60, 0x00, 0x44,
+	0x00, 0xff, 0xc2, 0x8b, 0x20, 0x00, 0x00, 0x80,
+	0x00, 0x0d, 0x42, 0x8b, 0x08, 0x32, 0x00, 0xc4,
+	0x00, 0x0e, 0x42, 0x8b, 0x00, 0xa2, 0x00, 0xc4,
+	0x00, 0x1e, 0x42, 0x8b, 0x0c, 0xb2, 0x00, 0xc4,
+	0x00, 0x8e, 0x42, 0x8b, 0x00, 0x62, 0x00, 0xc4,
+	0x00, 0x9e, 0x42, 0x8b, 0x08, 0x52, 0x00, 0xc4,
+	0x00, 0xbe, 0x42, 0x8b, 0x08, 0x52, 0x00, 0xc4,
+	0x00, 0x04, 0x42, 0x8b, 0x04, 0x72, 0x00, 0xc4,
+	0x00, 0x24, 0x42, 0x8b, 0x00, 0xd2, 0x00, 0xc4,
+	0x00, 0x55, 0x42, 0x8b, 0x00, 0x60, 0x00, 0xc4,
+	0x00, 0x00, 0x40, 0x45, 0x20, 0x01, 0x79, 0x80,
+	0x00, 0x30, 0x42, 0x8b, 0x08, 0x82, 0x00, 0xc4,
+	0x00, 0x00, 0x40, 0x45, 0x00, 0x00, 0x71, 0x8b,
+	0x40, 0x01, 0x00, 0x80, 0x00, 0x60, 0x00, 0x44,
+	0xff, 0x00, 0xe2, 0xab, 0x00, 0xb2, 0x00, 0xc4,
+	0x0f, 0xf2, 0xa8, 0xa8, 0x20, 0x00, 0xb1, 0x88,
+	0x00, 0x00, 0x41, 0x02, 0x4d, 0xf2, 0x00, 0x39,
+	0xc0, 0x01, 0x00, 0x82, 0x00, 0x60, 0x00, 0x44,
+	0x0d, 0xf2, 0xa3, 0xa8, 0x4d, 0xf2, 0x00, 0x39,
+	0x00, 0x60, 0x00, 0x44, 0xff, 0x00, 0xe2, 0xab,
+	0x20, 0x00, 0x00, 0x88, 0x00, 0x00, 0x61, 0x02,
+	0x4d, 0xf2, 0x04, 0x19, 0x00, 0x60, 0x00, 0x44,
+	0xff, 0x00, 0xe2, 0xab, 0xa0, 0x00, 0x00, 0x88,
+	0x00, 0x00, 0x61, 0x10, 0x4d, 0xf2, 0x04, 0x19,
+	0x00, 0x60, 0x00, 0x44, 0xff, 0x20, 0xe2, 0xab,
+	0x60, 0x00, 0x00, 0x88, 0x00, 0x00, 0x71, 0xc0,
+	0x4d, 0xf2, 0x04, 0x19, 0x00, 0x60, 0x00, 0x44,
+	0x00, 0x00, 0x79, 0x80, 0x00, 0xe2, 0x00, 0x84,
+	0x03, 0x03, 0x04, 0x49, 0x08, 0xc2, 0x00, 0x54,
+	0x00, 0x60, 0x04, 0x64, 0x00, 0x60, 0x00, 0x44,
+	0x00, 0x00, 0x63, 0x80, 0x00, 0x00, 0x06, 0x19,
+	0x03, 0x00, 0x04, 0x49, 0x00, 0x60, 0x00, 0x44,
+	0x20, 0x01, 0x63, 0x80, 0x00, 0x00, 0x06, 0x19,
+	0x00, 0x20, 0xe2, 0x8b, 0x0c, 0xf2, 0x00, 0x84,
+	0x3e, 0x00, 0x51, 0x8b, 0xc0, 0x20, 0x00, 0x39,
+	0x08, 0x01, 0x00, 0x44, 0x6c, 0x00, 0x51, 0x8b,
+	0xc0, 0x20, 0x00, 0x39, 0x00, 0x02, 0xe2, 0x8b,
+	0x04, 0x21, 0x00, 0x84, 0xfd, 0x00, 0x51, 0x8b,
+	0xc2, 0x20, 0x00, 0x39, 0x00, 0x11, 0x00, 0x44,
+	0xfe, 0x00, 0x51, 0x8b, 0xc2, 0x20, 0x00, 0x39,
+	0xe5, 0x00, 0x71, 0x8b, 0xcd, 0x00, 0x00, 0x39,
+	0x00, 0x00, 0xb1, 0x80, 0xc9, 0x20, 0x04, 0x19,
+	0xcb, 0x20, 0x04, 0x19, 0xc1, 0x20, 0x04, 0x19,
+	0xc3, 0x20, 0x04, 0x19, 0x10, 0x00, 0x71, 0x8b,
+	0xc7, 0x20, 0x04, 0x19, 0x5e, 0x00, 0x71, 0x8b,
+	0xcf, 0x00, 0x00, 0x39, 0x00, 0x00, 0xb1, 0x80,
+	0xc4, 0x20, 0x04, 0x19, 0xc6, 0x20, 0x04, 0x19,
+	0xc8, 0x20, 0x04, 0x19, 0xca, 0x20, 0x04, 0x19,
+	0x20, 0x00, 0x71, 0x8b, 0xcc, 0x20, 0x04, 0x19,
+	0x03, 0x00, 0x04, 0x49, 0x00, 0x60, 0x00, 0x44,
+	0x09, 0x04, 0x61, 0xa8, 0xc1, 0x00, 0x04, 0x19,
+	0x0b, 0x04, 0x61, 0xa8, 0xca, 0x00, 0x04, 0x19,
+	0x04, 0x60, 0x00, 0xd4, 0x0d, 0x00, 0x61, 0x0a,
+	0x90, 0x40, 0x09, 0x8f, 0x00, 0x01, 0x00, 0x45,
+	0x0f, 0x00, 0x61, 0x0a, 0x00, 0x40, 0x09, 0x8f,
+	0x00, 0x01, 0x00, 0x45, 0x82, 0x00, 0x09, 0x2e,
+	0x80, 0x40, 0x09, 0xcf, 0x02, 0x00, 0x61, 0x22,
+	0x43, 0x25, 0x61, 0x22, 0x40, 0x33, 0x00, 0x80,
+	0x08, 0xa8, 0x00, 0x44, 0x20, 0x31, 0x49, 0x5c,
+	0x92, 0x00, 0x09, 0x4e, 0x02, 0x03, 0x09, 0x2e,
+	0x00, 0x00, 0xa3, 0x02, 0xc0, 0x00, 0x71, 0xc0,
+	0x20, 0x00, 0xeb, 0x80, 0x00, 0x04, 0xc2, 0x8b,
+	0x20, 0x04, 0x61, 0x80, 0x00, 0x04, 0x7a, 0x02,
+	0xcb, 0x00, 0xa8, 0x58, 0xb0, 0x05, 0xf3, 0x80,
+	0x20, 0x04, 0xa8, 0x10, 0x00, 0x00, 0x10, 0x39,
+	0xb0, 0x00, 0xe0, 0x8b, 0x20, 0x01, 0x00, 0x80,
+	0x00, 0x00, 0x63, 0xcb, 0x00, 0x00, 0x7a, 0x02,
+	0x40, 0x00, 0x01, 0x5b, 0x20, 0x00, 0x00, 0x80,
+	0x00, 0x00, 0x4a, 0xcb, 0x20, 0x00, 0x13, 0x80,
+	0x20, 0x00, 0x7a, 0x80, 0xe0, 0x21, 0x00, 0xc0,
+	0x08, 0x00, 0x08, 0x49, 0x10, 0x41, 0x09, 0x8e,
+	0xff, 0xff, 0x62, 0x8b, 0x00, 0x04, 0x61, 0x22,
+	0x00, 0x03, 0x00, 0x45, 0x22, 0x01, 0x33, 0x80,
+	0x20, 0x01, 0xa3, 0x02, 0x00, 0x00, 0x7a, 0x80,
+	0xc0, 0x00, 0x00, 0x82, 0x07, 0x20, 0x40, 0x0a,
+	0x08, 0x83, 0x00, 0x84, 0x40, 0x21, 0x00, 0x80,
+	0x40, 0x05, 0x93, 0x10, 0xc7, 0x20, 0x00, 0x39,
+	0x00, 0x00, 0x40, 0x45, 0x07, 0x20, 0x40, 0x0a,
+	0x0c, 0xa3, 0x00, 0x84, 0x08, 0x00, 0x00, 0x82,
+	0x0c, 0x24, 0x61, 0x50, 0x40, 0x01, 0x00, 0x80,
+	0xc7, 0x20, 0x00, 0x39, 0x00, 0x00, 0x40, 0x45,
+	0x00, 0x04, 0x63, 0x80, 0x00, 0x00, 0x06, 0x39,
+	0x42, 0x01, 0x09, 0x0e, 0x02, 0x20, 0x61, 0x0a,
+	0x00, 0x01, 0x00, 0x45, 0x0c, 0x20, 0x60, 0x0a,
+	0x00, 0x73, 0x00, 0x84, 0x00, 0x04, 0xb1, 0x80,
+	0x00, 0x00, 0x06, 0x39, 0x0c, 0x61, 0x04, 0xd4,
+	0x00, 0x24, 0x71, 0xc0, 0x20, 0x33, 0x33, 0xc0,
+	0xe0, 0x01, 0xa3, 0x82, 0x22, 0x03, 0x7a, 0x02,
+	0xc3, 0x01, 0xa3, 0x82, 0x20, 0x01, 0x33, 0x80,
+	0x00, 0x00, 0x7a, 0x80, 0xc2, 0x01, 0xb3, 0x50,
+	0xcc, 0x20, 0x00, 0x39, 0x00, 0x00, 0x71, 0x80,
+	0x00, 0xf3, 0x00, 0x44, 0x0c, 0x20, 0x60, 0x0a,
+	0x00, 0xd3, 0x00, 0x84, 0x00, 0x04, 0xb1, 0x80,
+	0x00, 0x00, 0x06, 0x39, 0x0c, 0x61, 0x04, 0xd4,
+	0x00, 0x00, 0xb3, 0x10, 0xcc, 0x20, 0x00, 0x39,
+	0x00, 0x00, 0x71, 0xc0, 0x00, 0xf3, 0x00, 0x44,
+	0xcc, 0x20, 0x00, 0x39, 0x00, 0x20, 0x71, 0xc0,
+	0x00, 0x30, 0x71, 0xc0, 0x00, 0xf3, 0x00, 0x44,
+	0x20, 0x01, 0x00, 0x80, 0xff, 0xff, 0x62, 0x8b,
+	0x20, 0x01, 0x33, 0x80, 0x00, 0x00, 0x83, 0x80,
+	0x20, 0x00, 0x7a, 0x80, 0x20, 0xe1, 0x09, 0x5c,
+	0x82, 0x00, 0x09, 0x2f, 0x80, 0x4a, 0x09, 0x8e,
+	0xe0, 0x01, 0xb3, 0x82, 0x20, 0x04, 0xa3, 0x80,
+	0x00, 0x00, 0x7a, 0xcb, 0x03, 0x00, 0xa8, 0x18,
+	0x00, 0x00, 0x10, 0x39, 0x08, 0x04, 0xea, 0x10,
+	0x08, 0x04, 0x7a, 0x10, 0x20, 0x00, 0x00, 0x80,
+	0x40, 0x00, 0x21, 0xcb, 0x0c, 0x00, 0xe8, 0x10,
+	0x00, 0x00, 0x41, 0x02, 0x0c, 0x00, 0xeb, 0x10,
+	0xf2, 0x01, 0x00, 0x82, 0x40, 0x21, 0x33, 0x02,
+	0x08, 0x20, 0x61, 0x0a, 0xc4, 0x00, 0x04, 0x19,
+	0xc7, 0x00, 0x00, 0x99, 0x02, 0x00, 0x61, 0x0a,
+	0x0c, 0xe8, 0x04, 0x14, 0x01, 0x00, 0x61, 0x0a,
+	0x03, 0x00, 0x48, 0x0a, 0x00, 0xb8, 0x04, 0x54,
+	0xc3, 0x00, 0x04, 0x19, 0x0c, 0xb8, 0x00, 0x44,
+	0x08, 0x00, 0xc8, 0x0a, 0x0c, 0xb8, 0x04, 0x54,
+	0xc8, 0x00, 0x04, 0x19, 0x0a, 0x00, 0x61, 0x0a,
+	0x09, 0x00, 0x48, 0x0a, 0x00, 0x68, 0x04, 0x54,
+	0xc9, 0x00, 0x04, 0x19, 0x0c, 0x68, 0x00, 0x44,
+	0x0b, 0x00, 0xc8, 0x0a, 0x0c, 0x68, 0x04, 0x54,
+	0xcb, 0x00, 0x04, 0x19, 0x04, 0x00, 0x61, 0x0a,
+	0x06, 0x00, 0x48, 0x0a, 0x00, 0x78, 0x04, 0x54,
+	0xc6, 0x00, 0x04, 0x19, 0x0c, 0x78, 0x00, 0x44,
+	0x05, 0x00, 0xc8, 0x0a, 0x0c, 0x78, 0x04, 0x54,
+	0xc5, 0x00, 0x04, 0x19, 0x07, 0x00, 0x61, 0x0a,
+	0x0c, 0x00, 0x48, 0x0a, 0x00, 0xe8, 0x04, 0x54,
+	0xcc, 0x00, 0x04, 0x19, 0x0c, 0xe8, 0x00, 0x44,
+	0x0e, 0x00, 0xc8, 0x0a, 0x0c, 0xe8, 0x04, 0x54,
+	0xce, 0x00, 0x04, 0x19, 0x00, 0x00, 0x40, 0x45,
+	0x20, 0x10, 0x71, 0x8b, 0x09, 0x3f, 0x07, 0x00
+};
+
+static unsigned char alaw_main[] = {
+	0x00, 0x10, 0x00, 0x44, 0x08, 0x00, 0x00, 0x44,
+	0x00, 0xb1, 0x00, 0x44, 0x00, 0x61, 0x00, 0x44,
+	0x08, 0x50, 0x00, 0x44, 0x0d, 0xf2, 0x61, 0xa8,
+	0x44, 0x04, 0x04, 0x19, 0x00, 0x00, 0x40, 0x45,
+	0x40, 0x49, 0x39, 0xac, 0x55, 0x55, 0x71, 0x8b,
+	0x50, 0x05, 0x63, 0x80, 0x00, 0x00, 0x06, 0x39,
+	0xff, 0x2e, 0x21, 0x49, 0xff, 0x0f, 0xd4, 0x49,
+	0x20, 0x01, 0x09, 0x0e, 0x20, 0x00, 0x71, 0x8b,
+	0xa8, 0x01, 0xa8, 0x80, 0x88, 0x01, 0xa8, 0x80,
+	0xa8, 0x00, 0x00, 0x80, 0xd2, 0x00, 0x71, 0x8b,
+	0x88, 0x00, 0xa8, 0x80, 0xa8, 0x04, 0xb3, 0x80,
+	0x20, 0x07, 0xb3, 0x80, 0x88, 0x03, 0xb1, 0x80,
+	0xc0, 0x00, 0x09, 0x5c, 0xc2, 0x01, 0x00, 0x82,
+	0xa1, 0x00, 0x71, 0x8b, 0xcd, 0x00, 0x04, 0x19,
+	0x21, 0x20, 0x71, 0x8b, 0xcf, 0x00, 0x04, 0x19,
+	0x00, 0x00, 0xb1, 0x80, 0xc2, 0x00, 0x04, 0x19,
+	0x00, 0x40, 0x00, 0x14, 0x08, 0x40, 0x04, 0x24,
+	0x00, 0x00, 0x34, 0x49, 0x0c, 0x40, 0x00, 0x44,
+	0x44, 0x04, 0x04, 0x39, 0x00, 0x00, 0x40, 0x45,
+	0x32, 0x00, 0x09, 0x5c, 0x00, 0x00, 0x0c, 0x39,
+	0x00, 0x00, 0x40, 0x45, 0x40, 0x40, 0x09, 0xef,
+	0xff, 0x20, 0x09, 0xcf, 0x00, 0x04, 0x63, 0xa1,
+	0x50, 0x03, 0x33, 0x80, 0x00, 0x04, 0xa3, 0x80,
+	0x00, 0xff, 0xc2, 0x8b, 0x00, 0xd0, 0x04, 0x54,
+	0x04, 0xe0, 0x00, 0xc4, 0x20, 0x03, 0x80, 0xc0,
+	0x30, 0x00, 0x00, 0x88, 0x00, 0x00, 0x7a, 0x0a,
+	0xd0, 0x01, 0x00, 0x82, 0x00, 0x60, 0x00, 0x44,
+	0xc0, 0x00, 0x00, 0x99, 0x00, 0x60, 0x00, 0x44,
+	0x00, 0xff, 0xc2, 0x8b, 0x20, 0x00, 0x00, 0x80,
+	0x00, 0x0d, 0x42, 0x8b, 0x08, 0x32, 0x00, 0xc4,
+	0x00, 0x0e, 0x42, 0x8b, 0x00, 0xa2, 0x00, 0xc4,
+	0x00, 0x1e, 0x42, 0x8b, 0x0c, 0xb2, 0x00, 0xc4,
+	0x00, 0x8e, 0x42, 0x8b, 0x00, 0x62, 0x00, 0xc4,
+	0x00, 0x9e, 0x42, 0x8b, 0x08, 0x52, 0x00, 0xc4,
+	0x00, 0xbe, 0x42, 0x8b, 0x08, 0x52, 0x00, 0xc4,
+	0x00, 0x04, 0x42, 0x8b, 0x04, 0x72, 0x00, 0xc4,
+	0x00, 0x24, 0x42, 0x8b, 0x00, 0xd2, 0x00, 0xc4,
+	0x00, 0x55, 0x42, 0x8b, 0x00, 0x60, 0x00, 0xc4,
+	0x00, 0x00, 0x40, 0x45, 0x20, 0x01, 0x79, 0x80,
+	0x00, 0x30, 0x42, 0x8b, 0x08, 0x82, 0x00, 0xc4,
+	0x00, 0x00, 0x40, 0x45, 0x00, 0x00, 0x71, 0x8b,
+	0x40, 0x01, 0x00, 0x80, 0x00, 0x60, 0x00, 0x44,
+	0xff, 0x00, 0xe2, 0xab, 0x00, 0xb2, 0x00, 0xc4,
+	0x0f, 0xf2, 0xa8, 0xa8, 0x20, 0x00, 0xb1, 0x88,
+	0x00, 0x00, 0x41, 0x02, 0x4d, 0xf2, 0x00, 0x39,
+	0xc0, 0x01, 0x00, 0x82, 0x00, 0x60, 0x00, 0x44,
+	0x0d, 0xf2, 0xa3, 0xa8, 0x4d, 0xf2, 0x00, 0x39,
+	0x00, 0x60, 0x00, 0x44, 0xff, 0x00, 0xe2, 0xab,
+	0x20, 0x00, 0x00, 0x88, 0x00, 0x00, 0x61, 0x02,
+	0x4d, 0xf2, 0x04, 0x19, 0x00, 0x60, 0x00, 0x44,
+	0xff, 0x00, 0xe2, 0xab, 0xa0, 0x00, 0x00, 0x88,
+	0x00, 0x00, 0x61, 0x10, 0x4d, 0xf2, 0x04, 0x19,
+	0x00, 0x60, 0x00, 0x44, 0xff, 0x20, 0xe2, 0xab,
+	0x60, 0x00, 0x00, 0x88, 0x00, 0x00, 0x71, 0xc0,
+	0x4d, 0xf2, 0x04, 0x19, 0x00, 0x60, 0x00, 0x44,
+	0x00, 0x00, 0x79, 0x80, 0x00, 0xe2, 0x00, 0x84,
+	0x03, 0x03, 0x04, 0x49, 0x04, 0xc2, 0x00, 0x54,
+	0x00, 0x60, 0x04, 0x64, 0x00, 0x60, 0x00, 0x44,
+	0x00, 0x00, 0x63, 0x80, 0x00, 0x00, 0x06, 0x19,
+	0x03, 0x00, 0x04, 0x49, 0x00, 0x60, 0x00, 0x44,
+	0x20, 0x01, 0x63, 0x80, 0x00, 0x00, 0x06, 0x19,
+	0x00, 0x20, 0xe2, 0x8b, 0x0c, 0xf2, 0x00, 0x84,
+	0xbe, 0x00, 0x51, 0x8b, 0xc0, 0x20, 0x00, 0x39,
+	0x08, 0x01, 0x00, 0x44, 0xec, 0x00, 0x51, 0x8b,
+	0xc0, 0x20, 0x00, 0x39, 0x00, 0x02, 0xe2, 0x8b,
+	0x04, 0x21, 0x00, 0x84, 0x3f, 0x00, 0x51, 0x8b,
+	0xc2, 0x20, 0x00, 0x39, 0x00, 0x11, 0x00, 0x44,
+	0x3d, 0x00, 0x51, 0x8b, 0xc2, 0x20, 0x00, 0x39,
+	0xe5, 0x00, 0x71, 0x8b, 0xcd, 0x00, 0x00, 0x39,
+	0x00, 0x00, 0xb1, 0x80, 0xc9, 0x20, 0x04, 0x19,
+	0xcb, 0x20, 0x04, 0x19, 0xc1, 0x20, 0x04, 0x19,
+	0xc3, 0x20, 0x04, 0x19, 0x10, 0x00, 0x71, 0x8b,
+	0xc7, 0x20, 0x04, 0x19, 0xde, 0x00, 0x51, 0x8b,
+	0xcf, 0x00, 0x00, 0x39, 0x00, 0x01, 0xb1, 0x80,
+	0xc4, 0x20, 0x04, 0x19, 0xc6, 0x20, 0x04, 0x19,
+	0xc8, 0x20, 0x04, 0x19, 0xca, 0x20, 0x04, 0x19,
+	0x20, 0x00, 0x71, 0x8b, 0xcc, 0x20, 0x04, 0x19,
+	0x03, 0x00, 0x04, 0x49, 0x00, 0x60, 0x00, 0x44,
+	0x09, 0x04, 0x61, 0xa8, 0xc1, 0x00, 0x04, 0x19,
+	0x0b, 0x04, 0x61, 0xa8, 0xca, 0x00, 0x04, 0x19,
+	0x04, 0x60, 0x00, 0xd4, 0x0d, 0x00, 0x61, 0x0a,
+	0x90, 0x40, 0x09, 0x8f, 0x00, 0x01, 0x00, 0x45,
+	0x0f, 0x00, 0x61, 0x0a, 0x00, 0x40, 0x09, 0x8f,
+	0x00, 0x01, 0x00, 0x45, 0x82, 0x00, 0x09, 0x2e,
+	0x80, 0x40, 0x09, 0xcf, 0x02, 0x00, 0x61, 0x22,
+	0x43, 0x25, 0x61, 0x22, 0x40, 0x33, 0x00, 0x80,
+	0x08, 0x48, 0x00, 0x44, 0x20, 0xb1, 0x49, 0x5c,
+	0x92, 0x00, 0x09, 0x4e, 0x02, 0x03, 0x09, 0x2e,
+	0x00, 0x00, 0xa3, 0x02, 0xc0, 0x00, 0x71, 0xc0,
+	0x20, 0x00, 0xeb, 0x80, 0x00, 0x04, 0xc2, 0x8b,
+	0x20, 0x04, 0x61, 0x80, 0x00, 0x04, 0x7a, 0x02,
+	0xc0, 0x00, 0x00, 0x82, 0x0c, 0xc3, 0x08, 0x49,
+	0xb0, 0x01, 0xf3, 0x80, 0x00, 0x00, 0x10, 0x39,
+	0x20, 0x00, 0x0c, 0x89, 0x0c, 0x88, 0x08, 0x49,
+	0x03, 0x00, 0xa8, 0x18, 0x00, 0x00, 0x10, 0x39,
+	0xbd, 0xff, 0x62, 0x8b, 0x20, 0x01, 0x00, 0x80,
+	0x00, 0x00, 0x63, 0xcb, 0x00, 0x00, 0x7a, 0x02,
+	0x40, 0x00, 0x01, 0x5b, 0x20, 0x00, 0x00, 0x80,
+	0x00, 0x00, 0x4a, 0xcb, 0x20, 0x00, 0x13, 0x80,
+	0x20, 0x00, 0x7a, 0x80, 0xe0, 0x21, 0x00, 0xc0,
+	0x08, 0x00, 0x08, 0x49, 0x10, 0x41, 0x09, 0x8e,
+	0xae, 0xae, 0x62, 0x8b, 0x00, 0x04, 0x61, 0x22,
+	0x00, 0x03, 0x00, 0x45, 0x22, 0x01, 0x33, 0x80,
+	0x20, 0x01, 0xa3, 0x02, 0x00, 0x00, 0x7a, 0x80,
+	0xc0, 0x00, 0x00, 0x82, 0x07, 0x20, 0x40, 0x0a,
+	0x08, 0xa3, 0x00, 0x84, 0x40, 0x21, 0x00, 0x80,
+	0x40, 0x05, 0x93, 0x10, 0xc7, 0x20, 0x00, 0x39,
+	0x00, 0x00, 0x40, 0x45, 0x07, 0x20, 0x40, 0x0a,
+	0x0c, 0x93, 0x00, 0x84, 0x08, 0x00, 0x00, 0x82,
+	0x0c, 0x24, 0x61, 0x50, 0x40, 0x01, 0x00, 0x80,
+	0xc7, 0x20, 0x00, 0x39, 0x00, 0x00, 0x40, 0x45,
+	0x00, 0x04, 0x63, 0x80, 0x00, 0x00, 0x06, 0x39,
+	0x42, 0x01, 0x09, 0x0e, 0x02, 0x20, 0x61, 0x0a,
+	0x00, 0x01, 0x00, 0x45, 0x0c, 0x20, 0x60, 0x0a,
+	0x00, 0xc3, 0x00, 0x84, 0x00, 0x04, 0xb1, 0x80,
+	0x00, 0x00, 0x06, 0x39, 0x0c, 0x61, 0x04, 0xd4,
+	0x00, 0x24, 0x71, 0xc0, 0x20, 0x33, 0x33, 0xc0,
+	0xe0, 0x01, 0xa3, 0x82, 0x22, 0x03, 0x7a, 0x02,
+	0xc3, 0x01, 0xa3, 0x82, 0x20, 0x01, 0x33, 0x80,
+	0x00, 0x00, 0x7a, 0x80, 0xc2, 0x01, 0xb3, 0x50,
+	0xcc, 0x20, 0x00, 0x39, 0x00, 0x00, 0x71, 0x80,
+	0x00, 0x08, 0x00, 0x44, 0x0c, 0x20, 0x60, 0x0a,
+	0x00, 0xf3, 0x00, 0x84, 0x00, 0x04, 0xb1, 0x80,
+	0x00, 0x00, 0x06, 0x39, 0x0c, 0x61, 0x04, 0xd4,
+	0x00, 0x00, 0x71, 0xc0, 0x00, 0x00, 0x93, 0x10,
+	0xcc, 0x20, 0x00, 0x39, 0x00, 0x08, 0x00, 0x44,
+	0xcc, 0x20, 0x00, 0x39, 0x00, 0x20, 0x00, 0xc0,
+	0x00, 0x30, 0x71, 0xc0, 0x00, 0x08, 0x00, 0x44,
+	0x20, 0x01, 0x00, 0x80, 0xae, 0xae, 0x62, 0x8b,
+	0x20, 0x01, 0x33, 0x80, 0x00, 0x00, 0x83, 0x80,
+	0x20, 0x00, 0x7a, 0x80, 0x20, 0xa1, 0x49, 0x5c,
+	0x82, 0x00, 0x09, 0x6e, 0x80, 0x4a, 0x09, 0x8e,
+	0xe0, 0x01, 0xb3, 0x82, 0x20, 0x04, 0xa3, 0x80,
+	0x00, 0x00, 0x7a, 0xcb, 0x28, 0x04, 0xea, 0x10,
+	0x0c, 0x04, 0x7a, 0x10, 0x70, 0x00, 0xc0, 0x8b,
+	0x00, 0x00, 0x10, 0x39, 0x90, 0x03, 0x00, 0x80,
+	0x40, 0x00, 0x21, 0x5b, 0x90, 0x00, 0x61, 0x80,
+	0x0c, 0x8a, 0x08, 0x49, 0x00, 0x00, 0x1c, 0x19,
+	0x40, 0x00, 0x08, 0x5b, 0x08, 0x00, 0x08, 0x49,
+	0x20, 0x02, 0x00, 0x80, 0x03, 0x00, 0xa8, 0x18,
+	0x00, 0x00, 0x14, 0x19, 0x40, 0x00, 0x21, 0xcb,
+	0x00, 0x00, 0x41, 0x02, 0x00, 0x00, 0xeb, 0x80,
+	0xf2, 0x01, 0x00, 0x82, 0x40, 0x21, 0x33, 0x02,
+	0x08, 0x20, 0x61, 0x0a, 0xc4, 0x00, 0x04, 0x19,
+	0xc7, 0x00, 0x00, 0x99, 0x02, 0x00, 0x61, 0x0a,
+	0x0c, 0x0a, 0x04, 0x14, 0x01, 0x00, 0x61, 0x0a,
+	0x03, 0x00, 0x48, 0x0a, 0x00, 0x58, 0x04, 0x54,
+	0xc3, 0x00, 0x04, 0x19, 0x0c, 0x58, 0x00, 0x44,
+	0x08, 0x00, 0xc8, 0x0a, 0x0c, 0x58, 0x04, 0x54,
+	0xc8, 0x00, 0x04, 0x19, 0x0a, 0x00, 0x61, 0x0a,
+	0x09, 0x00, 0x48, 0x0a, 0x00, 0xc8, 0x04, 0x54,
+	0xc9, 0x00, 0x04, 0x19, 0x0c, 0xc8, 0x00, 0x44,
+	0x0b, 0x00, 0xc8, 0x0a, 0x0c, 0xc8, 0x04, 0x54,
+	0xcb, 0x00, 0x04, 0x19, 0x04, 0x00, 0x61, 0x0a,
+	0x06, 0x00, 0x48, 0x0a, 0x00, 0xd8, 0x04, 0x54,
+	0xc6, 0x00, 0x04, 0x19, 0x0c, 0xd8, 0x00, 0x44,
+	0x05, 0x00, 0xc8, 0x0a, 0x0c, 0xd8, 0x04, 0x54,
+	0xc5, 0x00, 0x04, 0x19, 0x07, 0x00, 0x61, 0x0a,
+	0x0c, 0x00, 0x48, 0x0a, 0x00, 0x0a, 0x04, 0x54,
+	0xcc, 0x00, 0x04, 0x19, 0x0c, 0x0a, 0x00, 0x44,
+	0x0e, 0x00, 0xc8, 0x0a, 0x0c, 0x0a, 0x04, 0x54,
+	0xce, 0x00, 0x04, 0x19, 0x00, 0x00, 0x40, 0x45,
+	0x20, 0x10, 0x71, 0x8b, 0x08, 0x42, 0x06, 0x00
+};
+
+
+static unsigned char ima_adpcm_init[] = {
+	0x00, 0x10, 0x00, 0x44, 0x00, 0x00, 0x40, 0x45,
+	0x00, 0x00, 0x40, 0x45, 0x00, 0x00, 0x40, 0x45,
+	0x00, 0x00, 0x40, 0x45, 0xaa, 0xaa, 0x71, 0x8b,
+	0x44, 0x04, 0x04, 0x19, 0x00, 0x00, 0x40, 0x45,
+	0xff, 0x6e, 0x21, 0x49, 0xff, 0x0f, 0xd4, 0x49,
+	0x40, 0x49, 0x39, 0xac, 0x55, 0x55, 0x71, 0x8b,
+	0x50, 0x05, 0xb1, 0x80, 0x62, 0x00, 0x19, 0x0e,
+	0x21, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xb0, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x40, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x60, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x50, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x70, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xc0, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xe0, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xd0, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x02, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x22, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x32, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xa2, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xb2, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x62, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xc2, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xf2, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x11, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xa1, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x61, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xe1, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x13, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xb3, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xc3, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x18, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x68, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x0a, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x4a, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x29, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x79, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x9b, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x14, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xf4, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xe6, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xe5, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xd7, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x2e, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x9d, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xef, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xb2, 0x20, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x33, 0x20, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x2a, 0x20, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x3b, 0x20, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x46, 0x20, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x2c, 0x20, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xdd, 0x20, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x01, 0x10, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x9a, 0x10, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x16, 0x10, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x8e, 0x10, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xc2, 0x30, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xc9, 0x30, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x3c, 0x30, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x81, 0x80, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xd4, 0x80, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x10, 0xa0, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x34, 0xa0, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x02, 0x90, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x75, 0x90, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x9a, 0xb0, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x12, 0x40, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x0d, 0x40, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x3c, 0x60, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xe7, 0x50, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x0e, 0x70, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xff, 0xc0, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xc8, 0xd0, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x57, 0xf0, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xc8, 0x22, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xb0, 0x32, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xdd, 0x82, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x90, 0xb2, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x8a, 0x62, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xce, 0x72, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xa5, 0xd2, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x97, 0x21, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xa2, 0xa1, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x5c, 0x41, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xfe, 0xc1, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x7a, 0x23, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x78, 0x93, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x67, 0x73, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x17, 0x28, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x88, 0x48, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xdb, 0xf8, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x2b, 0xba, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xf1, 0x09, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xdc, 0x69, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x19, 0x8b, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0xff, 0xfb, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x20, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+	0x52, 0x00, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82,
+	0xff, 0xff, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82,
+	0xc2, 0x00, 0x00, 0x82, 0xc2, 0x00, 0x00, 0x82,
+	0xc2, 0x00, 0x00, 0x82, 0x10, 0x00, 0x71, 0x8b,
+	0xc2, 0x00, 0x00, 0x82, 0x80, 0x00, 0x71, 0x8b,
+	0xc2, 0x00, 0x00, 0x82, 0x90, 0x00, 0x71, 0x8b,
+	0xc2, 0x00, 0x00, 0x82, 0x40, 0x00, 0x71, 0x8b,
+	0xc2, 0x00, 0x00, 0x82, 0xff, 0xff, 0x71, 0x8b,
+	0xc2, 0x00, 0x00, 0x82, 0xc2, 0x00, 0x00, 0x82,
+	0xc2, 0x00, 0x00, 0x82, 0xc2, 0x00, 0x00, 0x82,
+	0x10, 0x00, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82,
+	0x80, 0x00, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82,
+	0x90, 0x00, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82,
+	0x40, 0x00, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82,
+	0xff, 0xfb, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82,
+	0x00, 0x04, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82,
+	0x4a, 0x00, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82,
+	0x00, 0x00, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82,
+	0x00, 0x00, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82,
+	0xc2, 0x00, 0x00, 0x82, 0xc2, 0x30, 0x04, 0x19,
+	0x10, 0x00, 0x09, 0x4f, 0xc2, 0x01, 0x00, 0x82,
+	0xc2, 0x01, 0x00, 0x82, 0xc2, 0x01, 0x00, 0x82,
+	0xc2, 0x01, 0x00, 0x82, 0xc2, 0x01, 0x00, 0x82,
+	0xc2, 0x01, 0x00, 0x82, 0xc2, 0x01, 0x00, 0x82,
+	0xc2, 0x01, 0x00, 0x82, 0xc2, 0x01, 0x00, 0x82,
+	0xc2, 0x01, 0x00, 0x82, 0xc2, 0x01, 0x00, 0x82,
+	0xc2, 0x01, 0x00, 0x82, 0xc2, 0x01, 0x00, 0x82,
+	0x00, 0x10, 0x71, 0x8b, 0xc1, 0x30, 0x04, 0x19,
+	0x93, 0x00, 0x01, 0x4f, 0xcd, 0x30, 0x00, 0x09,
+	0xcf, 0x30, 0x00, 0x09, 0x00, 0x00, 0x34, 0x49,
+	0x00, 0x08, 0x00, 0x44, 0xc8, 0x54, 0x11, 0x00
+};
+
+static unsigned char ima_adpcm_playback[] = {
+	0x00, 0x10, 0x00, 0x44, 0x08, 0x00, 0x00, 0x44,
+	0x0c, 0x50, 0x00, 0x44, 0x00, 0x70, 0x00, 0x44,
+	0x04, 0x70, 0x00, 0x44, 0x0d, 0xf2, 0x61, 0xa8,
+	0x44, 0x04, 0x04, 0x19, 0x00, 0x00, 0x40, 0x45,
+	0x00, 0x04, 0x63, 0x80, 0x00, 0x00, 0x06, 0x39,
+	0xff, 0x2e, 0x21, 0x49, 0xff, 0x0d, 0xd4, 0x49,
+	0x40, 0x49, 0x39, 0xac, 0x55, 0x55, 0x71, 0x8b,
+	0x50, 0x01, 0xb1, 0x80, 0x00, 0x01, 0xb1, 0x80,
+	0xc9, 0x20, 0x04, 0x19, 0x51, 0x00, 0x71, 0x8b,
+	0xcd, 0x00, 0x04, 0x19, 0xe4, 0x20, 0x71, 0x8b,
+	0xcf, 0x00, 0x04, 0x19, 0x80, 0x00, 0x71, 0x8b,
+	0xcb, 0x20, 0x04, 0x19, 0x10, 0x00, 0x71, 0x8b,
+	0xc4, 0x20, 0x04, 0x19, 0x65, 0x00, 0x51, 0x8b,
+	0xc2, 0x20, 0x00, 0x39, 0x00, 0x00, 0xb1, 0x80,
+	0xc2, 0x30, 0x04, 0x19, 0x00, 0x00, 0x63, 0x80,
+	0xc1, 0xa0, 0x04, 0x19, 0x93, 0x00, 0x01, 0x4f,
+	0xcd, 0x30, 0x00, 0x09, 0xcf, 0x30, 0x00, 0x09,
+	0x04, 0x40, 0x00, 0x14, 0x0c, 0x40, 0x00, 0x14,
+	0x00, 0x04, 0x61, 0xa8, 0x02, 0x04, 0x61, 0xa8,
+	0x04, 0x60, 0x04, 0x24, 0x00, 0x00, 0x34, 0x49,
+	0x00, 0x50, 0x00, 0x44, 0x44, 0x04, 0x04, 0x39,
+	0x00, 0x00, 0x40, 0x45, 0x00, 0x00, 0x40, 0x45,
+	0x0f, 0x00, 0x61, 0x0a, 0x00, 0x01, 0x00, 0x45,
+	0x40, 0x40, 0x09, 0xef, 0xff, 0x20, 0x09, 0xcf,
+	0x00, 0x04, 0x63, 0xa1, 0x50, 0x03, 0x33, 0x80,
+	0x00, 0x04, 0xa3, 0x80, 0x00, 0xff, 0xc2, 0x8b,
+	0x08, 0xf0, 0x04, 0x54, 0x0c, 0xd0, 0x00, 0xc4,
+	0x20, 0x03, 0x80, 0xc0, 0x30, 0x00, 0x00, 0x88,
+	0x00, 0x00, 0x7a, 0x0a, 0xd0, 0x01, 0x00, 0x82,
+	0x08, 0x50, 0x00, 0x44, 0xc0, 0x00, 0x00, 0x99,
+	0x08, 0x50, 0x00, 0x44, 0x00, 0xff, 0xc2, 0x8b,
+	0x20, 0x00, 0x00, 0x80, 0x00, 0x0d, 0x42, 0x8b,
+	0x00, 0xa2, 0x00, 0xc4, 0x00, 0x0e, 0x42, 0x8b,
+	0x0c, 0x92, 0x00, 0xc4, 0x00, 0x1e, 0x42, 0x8b,
+	0x04, 0x62, 0x00, 0xc4, 0x00, 0x8e, 0x42, 0x8b,
+	0x0c, 0x52, 0x00, 0xc4, 0x00, 0x9e, 0x42, 0x8b,
+	0x00, 0xc2, 0x00, 0xc4, 0x00, 0xbe, 0x42, 0x8b,
+	0x00, 0xc2, 0x00, 0xc4, 0x00, 0x04, 0x42, 0x8b,
+	0x00, 0xf2, 0x00, 0xc4, 0x00, 0x24, 0x42, 0x8b,
+	0x00, 0x91, 0x00, 0xc4, 0x00, 0x55, 0x42, 0x8b,
+	0x08, 0x50, 0x00, 0xc4, 0x00, 0x3f, 0x42, 0x8b,
+	0x08, 0xe2, 0x00, 0xc4, 0x00, 0x00, 0x40, 0x45,
+	0x20, 0x01, 0x79, 0x80, 0x00, 0x30, 0x42, 0x8b,
+	0x00, 0x92, 0x00, 0xc4, 0x00, 0x00, 0x40, 0x45,
+	0x00, 0x00, 0x71, 0x8b, 0x40, 0x01, 0x00, 0x80,
+	0x08, 0x50, 0x00, 0x44, 0xff, 0x00, 0xe2, 0xab,
+	0x0c, 0x42, 0x00, 0xc4, 0x0f, 0xf2, 0xa8, 0xa8,
+	0x20, 0x00, 0xb1, 0x88, 0x00, 0x00, 0x41, 0x02,
+	0x4d, 0xf2, 0x00, 0x39, 0xc0, 0x01, 0x00, 0x82,
+	0x08, 0x50, 0x00, 0x44, 0x0d, 0xf2, 0xa3, 0xa8,
+	0x4d, 0xf2, 0x00, 0x39, 0x08, 0x50, 0x00, 0x44,
+	0xff, 0x00, 0xe2, 0xab, 0x20, 0x00, 0x00, 0x88,
+	0x00, 0x00, 0x61, 0x02, 0x4d, 0xf2, 0x04, 0x19,
+	0x08, 0x50, 0x00, 0x44, 0xff, 0x00, 0xe2, 0xab,
+	0xa0, 0x00, 0x00, 0x88, 0x00, 0x00, 0x61, 0x10,
+	0x4d, 0xf2, 0x04, 0x19, 0x08, 0x50, 0x00, 0x44,
+	0xff, 0x20, 0xe2, 0xab, 0x60, 0x00, 0x00, 0x88,
+	0x00, 0x00, 0x71, 0xc0, 0x4d, 0xf2, 0x04, 0x19,
+	0x08, 0x50, 0x00, 0x44, 0x00, 0x00, 0x7a, 0x0a,
+	0x20, 0x01, 0xf0, 0x80, 0x01, 0xa0, 0x41, 0x0a,
+	0x04, 0xd2, 0x00, 0xc4, 0x20, 0x01, 0xf0, 0x80,
+	0xc1, 0x30, 0x04, 0x19, 0x08, 0x50, 0x00, 0x44,
+	0x00, 0x00, 0x79, 0x80, 0x00, 0xa1, 0x00, 0x84,
+	0xb5, 0x00, 0x51, 0x8b, 0xcf, 0x00, 0x00, 0x39,
+	0x00, 0x01, 0xb1, 0x80, 0x88, 0x00, 0x04, 0x19,
+	0x8a, 0x00, 0x04, 0x19, 0xc8, 0x20, 0x04, 0x19,
+	0xca, 0x20, 0x04, 0x19, 0xc2, 0x30, 0x04, 0x19,
+	0xcd, 0x10, 0x04, 0x19, 0xcf, 0x10, 0x04, 0x19,
+	0xb0, 0x00, 0x71, 0x8b, 0x8c, 0x00, 0x04, 0x19,
+	0x8e, 0x00, 0x04, 0x19, 0x10, 0x00, 0x71, 0x8b,
+	0xc4, 0x20, 0x04, 0x19, 0x93, 0x00, 0x01, 0x4f,
+	0xcd, 0x30, 0x00, 0x09, 0xcf, 0x30, 0x00, 0x09,
+	0x03, 0x03, 0x04, 0x49, 0x04, 0x81, 0x00, 0x54,
+	0x08, 0x50, 0x04, 0x64, 0x08, 0x50, 0x00, 0x44,
+	0x00, 0x00, 0x63, 0x80, 0x00, 0x00, 0x06, 0x19,
+	0x03, 0x00, 0x04, 0x49, 0x08, 0x50, 0x00, 0x44,
+	0x20, 0x01, 0x63, 0x80, 0x00, 0x00, 0x06, 0x19,
+	0x00, 0x02, 0xe2, 0x8b, 0x08, 0x41, 0x00, 0x84,
+	0x65, 0x00, 0x51, 0x8b, 0xc2, 0x20, 0x00, 0x39,
+	0x00, 0x00, 0x63, 0x80, 0xc1, 0xa0, 0x04, 0x19,
+	0x08, 0x61, 0x00, 0x44, 0x2d, 0x00, 0x51, 0x8b,
+	0xc2, 0x20, 0x00, 0x39, 0x00, 0x00, 0xb1, 0x80,
+	0xc1, 0xa0, 0x04, 0x19, 0x03, 0x00, 0x04, 0x49,
+	0x08, 0x50, 0x00, 0x44, 0x02, 0x20, 0x61, 0x0a,
+	0x00, 0x01, 0x00, 0x45, 0x02, 0x30, 0x61, 0x0a,
+	0x04, 0x03, 0x00, 0xc4, 0x05, 0xb0, 0xc8, 0x18,
+	0x04, 0x71, 0x00, 0xc4, 0x00, 0x13, 0x00, 0x44,
+	0x00, 0x79, 0x08, 0x44, 0x00, 0x04, 0x79, 0x80,
+	0x00, 0x49, 0x00, 0xc4, 0xca, 0x20, 0x04, 0x19,
+	0x4a, 0x04, 0x04, 0x19, 0xff, 0x00, 0xe2, 0x8b,
+	0x0c, 0xf9, 0x08, 0x44, 0xcf, 0x10, 0x04, 0x19,
+	0x0c, 0x2b, 0x08, 0x44, 0x8e, 0x00, 0x04, 0x19,
+	0x03, 0x30, 0x61, 0x0a, 0xc8, 0x20, 0x00, 0x39,
+	0x48, 0x04, 0x00, 0x39, 0x0a, 0x30, 0x61, 0x0a,
+	0x0c, 0xf9, 0x08, 0x44, 0xcd, 0x10, 0x04, 0x19,
+	0x0c, 0x2b, 0x08, 0x44, 0x8c, 0x00, 0x04, 0x19,
+	0x0c, 0xd9, 0x08, 0x44, 0x0c, 0x5a, 0x00, 0x44,
+	0x00, 0x79, 0x08, 0x44, 0x00, 0x04, 0x79, 0x80,
+	0x00, 0x49, 0x00, 0xc4, 0xc3, 0x30, 0x04, 0x19,
+	0xca, 0x30, 0x00, 0x99, 0x0c, 0xd9, 0x08, 0x44,
+	0x42, 0x0a, 0x09, 0x0e, 0x00, 0x01, 0x33, 0x11,
+	0x8c, 0x01, 0xa3, 0x80, 0x00, 0x01, 0x7a, 0x10,
+	0x80, 0x05, 0xb1, 0x80, 0x05, 0xb0, 0xe0, 0x18,
+	0x00, 0x93, 0x00, 0x84, 0x00, 0x79, 0x08, 0x44,
+	0x00, 0x04, 0x79, 0x80, 0x00, 0x49, 0x00, 0xc4,
+	0x0c, 0x1b, 0x08, 0x44, 0x88, 0x00, 0x04, 0x19,
+	0x8a, 0x00, 0x00, 0x99, 0x0c, 0xd9, 0x08, 0x44,
+	0x42, 0x0a, 0x09, 0x0e, 0x80, 0x00, 0x71, 0x8b,
+	0xc0, 0x04, 0xb1, 0x82, 0x10, 0x00, 0xe0, 0x0b,
+	0x00, 0x43, 0x00, 0x84, 0x02, 0x30, 0x61, 0x0a,
+	0x01, 0x30, 0xc8, 0x0a, 0x00, 0x43, 0x00, 0x84,
+	0x00, 0x00, 0xb1, 0x80, 0xc2, 0x30, 0x04, 0x19,
+	0x0c, 0xa8, 0x00, 0x44, 0x02, 0x30, 0x61, 0x0a,
+	0x00, 0xd3, 0x00, 0xc4, 0x05, 0xb0, 0xc8, 0x18,
+	0x04, 0x63, 0x00, 0xc4, 0x08, 0xf3, 0x00, 0x44,
+	0x00, 0x79, 0x08, 0x44, 0x00, 0x04, 0x79, 0x80,
+	0x00, 0x49, 0x00, 0xc4, 0x20, 0x00, 0x04, 0x19,
+	0xff, 0x00, 0xe2, 0x8b, 0x0c, 0xf9, 0x08, 0x44,
+	0xcd, 0x10, 0x04, 0x19, 0xcf, 0x10, 0x04, 0x19,
+	0x0c, 0x2b, 0x08, 0x44, 0x8c, 0x00, 0x04, 0x19,
+	0x8e, 0x00, 0x04, 0x19, 0x03, 0x30, 0x61, 0x0a,
+	0xc8, 0x20, 0x00, 0x39, 0xca, 0x20, 0x00, 0x39,
+	0x48, 0x04, 0x00, 0x39, 0x4a, 0x04, 0x00, 0x39,
+	0x0c, 0xd9, 0x08, 0x44, 0x0c, 0x5a, 0x00, 0x44,
+	0x00, 0x79, 0x08, 0x44, 0x00, 0x04, 0x79, 0x80,
+	0x00, 0x49, 0x00, 0xc4, 0xc3, 0x30, 0x04, 0x19,
+	0x0c, 0xd9, 0x08, 0x44, 0x42, 0x0a, 0x09, 0x0e,
+	0x05, 0xb0, 0xe0, 0x18, 0x00, 0x18, 0x00, 0x84,
+	0x00, 0x79, 0x08, 0x44, 0x00, 0x04, 0x79, 0x80,
+	0x00, 0x49, 0x00, 0xc4, 0x0c, 0x1b, 0x08, 0x44,
+	0x80, 0x01, 0x00, 0x80, 0x0c, 0xd9, 0x08, 0x44,
+	0x42, 0x0a, 0x09, 0x0e, 0x80, 0x00, 0x71, 0x8b,
+	0xc0, 0x04, 0xb1, 0x82, 0x10, 0x00, 0xe0, 0x0b,
+	0x00, 0x88, 0x00, 0x84, 0x02, 0x30, 0x61, 0x0a,
+	0x01, 0x30, 0xc8, 0x0a, 0x00, 0x88, 0x00, 0x84,
+	0x00, 0x00, 0xb1, 0x80, 0xc2, 0x30, 0x04, 0x19,
+	0x00, 0x01, 0x00, 0x11, 0x00, 0x0f, 0xe2, 0x8b,
+	0x00, 0x00, 0x41, 0xcb, 0x8c, 0x00, 0x00, 0x80,
+	0x00, 0x00, 0x48, 0xcb, 0x20, 0x00, 0x7a, 0x80,
+	0x80, 0x01, 0x00, 0x80, 0x82, 0x0c, 0x09, 0x6e,
+	0x03, 0x08, 0x09, 0x0e, 0x80, 0x40, 0x09, 0xcf,
+	0x00, 0x01, 0x71, 0xc2, 0x00, 0x08, 0xc2, 0x1b,
+	0x04, 0xb8, 0x00, 0xc4, 0x20, 0x05, 0xa8, 0x80,
+	0x20, 0x01, 0xf0, 0x80, 0x00, 0x01, 0xc2, 0x1b,
+	0x04, 0x48, 0x00, 0xc4, 0x20, 0x05, 0xa8, 0x80,
+	0x20, 0x01, 0xf0, 0x80, 0x00, 0x02, 0xc2, 0x1b,
+	0x04, 0x68, 0x00, 0xc4, 0x20, 0x05, 0xa8, 0x80,
+	0x20, 0x01, 0xf0, 0x80, 0x20, 0x03, 0xa8, 0x80,
+	0x00, 0x01, 0x00, 0x11, 0x00, 0x04, 0xc2, 0x8b,
+	0x08, 0x78, 0x00, 0xc4, 0x00, 0x00, 0xe9, 0x80,
+	0x05, 0xb0, 0xa8, 0x18, 0x00, 0x00, 0x4a, 0xcb,
+	0x20, 0x00, 0xa8, 0x22, 0xd0, 0x01, 0x00, 0x82,
+	0x40, 0x01, 0x00, 0x80, 0xc4, 0x00, 0x04, 0x19,
+	0xb0, 0x00, 0xe2, 0x8b, 0x06, 0x20, 0xa8, 0x0a,
+	0x2d, 0x10, 0x61, 0x0a, 0xd1, 0x08, 0x09, 0x2e,
+	0x00, 0x01, 0xa8, 0x02, 0x0c, 0xf9, 0x08, 0x44,
+	0xcd, 0x10, 0x04, 0x19, 0x0c, 0x2b, 0x08, 0x44,
+	0x03, 0x08, 0x09, 0x0e, 0x9a, 0x25, 0xb1, 0x60,
+	0xa2, 0x0e, 0x09, 0x6e, 0x03, 0x00, 0x09, 0x0f,
+	0x00, 0x01, 0x71, 0x82, 0x20, 0x01, 0x00, 0x80,
+	0x00, 0x00, 0x61, 0xcb, 0x80, 0x01, 0x00, 0x80,
+	0x03, 0x00, 0x09, 0x0f, 0x00, 0x01, 0x71, 0xc2,
+	0x00, 0x08, 0xc2, 0x1b, 0x0c, 0x2a, 0x00, 0xc4,
+	0x20, 0x05, 0xa8, 0x80, 0x20, 0x01, 0xf0, 0x80,
+	0x00, 0x01, 0xc2, 0x1b, 0x0c, 0x1a, 0x00, 0xc4,
+	0x20, 0x05, 0xa8, 0x80, 0x20, 0x01, 0xf0, 0x80,
+	0x00, 0x02, 0xc2, 0x1b, 0x0c, 0x3a, 0x00, 0xc4,
+	0x20, 0x05, 0xa8, 0x80, 0x20, 0x01, 0xf0, 0x80,
+	0x20, 0x03, 0xa8, 0x80, 0x00, 0x01, 0x00, 0x11,
+	0x00, 0x04, 0xc2, 0x8b, 0x04, 0xaa, 0x00, 0xc4,
+	0x00, 0x00, 0xe9, 0x80, 0x05, 0xb0, 0xa8, 0x18,
+	0x00, 0x00, 0x4a, 0xcb, 0x20, 0x00, 0xa8, 0x22,
+	0xd0, 0x01, 0x00, 0x82, 0x40, 0x01, 0x00, 0x80,
+	0xc7, 0x00, 0x04, 0x19, 0xb0, 0x00, 0xe2, 0x8b,
+	0x06, 0x20, 0xa8, 0x0a, 0x2f, 0x10, 0x61, 0x0a,
+	0xf1, 0x08, 0x09, 0x2e, 0x00, 0x01, 0xa8, 0x02,
+	0x0c, 0xf9, 0x08, 0x44, 0xcf, 0x10, 0x04, 0x19,
+	0x0c, 0x2b, 0x08, 0x44, 0x9f, 0x35, 0xb1, 0x60,
+	0x03, 0x08, 0x09, 0x0e, 0x00, 0x01, 0x71, 0x82,
+	0x20, 0x01, 0x00, 0x80, 0x00, 0x00, 0x61, 0xcb,
+	0x80, 0x01, 0x00, 0x80, 0xe4, 0x20, 0x71, 0x8b,
+	0x00, 0x01, 0x00, 0x45, 0x90, 0x40, 0x09, 0x8f,
+	0x00, 0x05, 0x63, 0x80, 0x00, 0x00, 0x06, 0x39,
+	0x08, 0x19, 0x04, 0xd4, 0x93, 0x00, 0x01, 0x4f,
+	0xe7, 0x00, 0x01, 0x6f, 0x0d, 0x30, 0x61, 0x0a,
+	0x20, 0x04, 0x61, 0xa8, 0xc2, 0x00, 0x00, 0x82,
+	0x02, 0x04, 0x61, 0xa8, 0xc2, 0x00, 0x00, 0x82,
+	0xcd, 0x30, 0x00, 0x09, 0x02, 0x00, 0x00, 0x02,
+	0x02, 0x00, 0x00, 0x02, 0xc0, 0x80, 0x00, 0x09,
+	0x20, 0x00, 0x09, 0x49, 0x0f, 0x30, 0x61, 0x0a,
+	0x0d, 0x30, 0xc8, 0x0a, 0x00, 0x29, 0x00, 0xc4,
+	0x00, 0x80, 0xc8, 0x0a, 0x00, 0x29, 0x00, 0xc4,
+	0x00, 0x04, 0xb1, 0x80, 0x00, 0x00, 0x06, 0x39,
+	0xc9, 0x20, 0x04, 0x39, 0x00, 0x39, 0x00, 0x44,
+	0x00, 0x04, 0x63, 0x80, 0x00, 0x00, 0x06, 0x39,
+	0x00, 0x04, 0xb1, 0x80, 0xc9, 0x20, 0x04, 0x39,
+	0x00, 0x39, 0x00, 0x44, 0x09, 0x20, 0x23, 0x0a,
+	0x00, 0x00, 0x06, 0x19, 0xc9, 0x20, 0x04, 0x19,
+	0x00, 0x00, 0x40, 0x45, 0x02, 0x00, 0x61, 0x0a,
+	0x0c, 0xb9, 0x04, 0x14, 0x04, 0x00, 0x61, 0x0a,
+	0x06, 0x00, 0x48, 0x0a, 0x00, 0xa9, 0x04, 0x54,
+	0xc6, 0x00, 0x04, 0x19, 0x0c, 0xa9, 0x00, 0x44,
+	0x05, 0x00, 0xc8, 0x0a, 0x0c, 0xa9, 0x04, 0x54,
+	0xc5, 0x00, 0x04, 0x19, 0x07, 0x00, 0x61, 0x0a,
+	0x0c, 0x00, 0x48, 0x0a, 0x00, 0xb9, 0x04, 0x54,
+	0xcc, 0x00, 0x04, 0x19, 0x0c, 0xb9, 0x00, 0x44,
+	0x0e, 0x00, 0xc8, 0x0a, 0x0c, 0xb9, 0x04, 0x54,
+	0xce, 0x00, 0x04, 0x19, 0x0c, 0x5a, 0x00, 0x44,
+	0x82, 0x0d, 0x09, 0x2e, 0x80, 0x40, 0x09, 0xcf,
+	0x00, 0xdf, 0x71, 0x8b, 0x80, 0x01, 0x00, 0x80,
+	0x02, 0xc1, 0x00, 0x22, 0x03, 0xc1, 0x00, 0x22,
+	0x00, 0x01, 0x65, 0x80, 0xd2, 0x05, 0x65, 0x82,
+	0x40, 0x21, 0x00, 0x80, 0xd3, 0x03, 0x00, 0x82,
+	0x40, 0x33, 0x00, 0x80, 0x0c, 0x5a, 0x00, 0x44,
+	0x0f, 0x30, 0x61, 0x0a, 0x0d, 0x30, 0xc8, 0x0a,
+	0x08, 0xd9, 0x00, 0xc4, 0x93, 0x00, 0x01, 0x4f,
+	0xe7, 0x00, 0x01, 0x6f, 0x0f, 0x30, 0x61, 0x0a,
+	0x20, 0x00, 0x00, 0x88, 0x02, 0x00, 0x61, 0x02,
+	0x02, 0x00, 0x00, 0x03, 0xcf, 0x30, 0x00, 0x09,
+	0x20, 0x00, 0x09, 0x49, 0x00, 0x04, 0x63, 0x80,
+	0x04, 0xd9, 0x00, 0x44, 0x00, 0x04, 0xb1, 0x80,
+	0x00, 0x00, 0x00, 0x46, 0x02, 0x30, 0x61, 0x0a,
+	0x05, 0xb0, 0xa8, 0x18, 0xc2, 0x30, 0x04, 0x19,
+	0x00, 0x00, 0x00, 0x46, 0x0e, 0x10, 0xc8, 0x0a,
+	0x0c, 0x0b, 0x04, 0x14, 0x0e, 0x10, 0x61, 0x0a,
+	0x04, 0x2b, 0x00, 0x44, 0x0c, 0x10, 0xc8, 0x0a,
+	0x04, 0x2b, 0x04, 0x54, 0x0c, 0x10, 0x61, 0x0a,
+	0x00, 0x00, 0x00, 0x46, 0x00, 0x10, 0xa8, 0x18,
+	0xa0, 0x00, 0x00, 0x88, 0x00, 0x01, 0x71, 0x82,
+	0x00, 0x00, 0x00, 0x46, 0x00, 0x04, 0x33, 0x80,
+	0x00, 0x00, 0x83, 0x80, 0x20, 0x04, 0x7a, 0x80,
+	0x20, 0x01, 0x33, 0x80, 0x00, 0x00, 0x83, 0x80,
+	0x20, 0x00, 0x7a, 0x80, 0x20, 0x03, 0x00, 0x80,
+	0x00, 0x00, 0x00, 0x46, 0x16, 0xce, 0x11, 0x00
+};
+
+static unsigned char ima_adpcm_capture[] = {
+	0x00, 0x10, 0x00, 0x44, 0x08, 0x00, 0x00, 0x44,
+	0x00, 0x70, 0x00, 0x44, 0x08, 0xd0, 0x00, 0x44,
+	0x00, 0xf0, 0x00, 0x44, 0x0d, 0xf2, 0x61, 0xa8,
+	0x44, 0x04, 0x04, 0x19, 0x00, 0x00, 0x40, 0x45,
+	0x00, 0x04, 0x63, 0x80, 0x00, 0x00, 0x06, 0x39,
+	0xff, 0x2e, 0x21, 0x49, 0xff, 0x0c, 0xd4, 0x49,
+	0x40, 0x49, 0x39, 0xac, 0x55, 0x55, 0x71, 0x8b,
+	0x50, 0x01, 0xb1, 0x80, 0x00, 0x00, 0x71, 0x8b,
+	0xc2, 0x30, 0x04, 0x19, 0xc0, 0xa0, 0x04, 0x19,
+	0xc2, 0xa0, 0x04, 0x19, 0x89, 0x00, 0x71, 0x8b,
+	0xc8, 0x30, 0x04, 0x19, 0x71, 0x00, 0x71, 0x8b,
+	0xcd, 0x00, 0x04, 0x19, 0xcf, 0x00, 0x04, 0x19,
+	0x80, 0x00, 0x71, 0x8b, 0xcb, 0x20, 0x04, 0x19,
+	0x20, 0x00, 0x71, 0x8b, 0xc4, 0x20, 0x04, 0x19,
+	0x47, 0x00, 0x51, 0x8b, 0xc0, 0x20, 0x00, 0x39,
+	0x00, 0x00, 0x63, 0x80, 0xc1, 0xa0, 0x04, 0x19,
+	0x93, 0x00, 0x01, 0x4f, 0xcd, 0x30, 0x00, 0x09,
+	0xcf, 0x30, 0x00, 0x09, 0x0c, 0x40, 0x00, 0x14,
+	0x00, 0x60, 0x00, 0x14, 0x00, 0x04, 0x61, 0xa8,
+	0x02, 0x04, 0x61, 0xa8, 0x0c, 0x60, 0x04, 0x24,
+	0x00, 0x00, 0x34, 0x49, 0x08, 0x50, 0x00, 0x44,
+	0x44, 0x04, 0x04, 0x39, 0x00, 0x00, 0x40, 0x45,
+	0x08, 0x30, 0x61, 0x0a, 0x05, 0xb0, 0xe8, 0x18,
+	0x0c, 0xc0, 0x04, 0x54, 0xc8, 0x30, 0x04, 0x19,
+	0x09, 0x04, 0x00, 0xa8, 0x0b, 0x04, 0x00, 0xa8,
+	0x00, 0x00, 0x40, 0x45, 0x09, 0x04, 0x61, 0xa8,
+	0xc1, 0x00, 0x04, 0x19, 0x0b, 0x04, 0x61, 0xa8,
+	0xca, 0x00, 0x04, 0x19, 0x0d, 0x00, 0x61, 0x0a,
+	0x00, 0x01, 0x00, 0x45, 0x0f, 0x00, 0x61, 0x0a,
+	0x00, 0x40, 0x09, 0x8f, 0x00, 0x01, 0x00, 0x45,
+	0x40, 0x40, 0x09, 0xef, 0xff, 0x20, 0x09, 0xcf,
+	0x00, 0x04, 0x63, 0xa1, 0x50, 0x03, 0x33, 0x80,
+	0x00, 0x04, 0xa3, 0x80, 0x00, 0xff, 0xc2, 0x8b,
+	0x0c, 0x12, 0x04, 0x54, 0x08, 0x12, 0x00, 0xc4,
+	0x20, 0x03, 0x80, 0xc0, 0x30, 0x00, 0x00, 0x88,
+	0x00, 0x00, 0x7a, 0x0a, 0xd0, 0x01, 0x00, 0x82,
+	0x04, 0x50, 0x00, 0x44, 0xc0, 0x00, 0x00, 0x99,
+	0x04, 0x50, 0x00, 0x44, 0x00, 0xff, 0xc2, 0x8b,
+	0x20, 0x00, 0x00, 0x80, 0x00, 0x0d, 0x42, 0x8b,
+	0x04, 0x42, 0x00, 0xc4, 0x00, 0x0e, 0x42, 0x8b,
+	0x08, 0x52, 0x00, 0xc4, 0x00, 0x1e, 0x42, 0x8b,
+	0x00, 0xe2, 0x00, 0xc4, 0x00, 0x8e, 0x42, 0x8b,
+	0x08, 0xd2, 0x00, 0xc4, 0x00, 0x9e, 0x42, 0x8b,
+	0x04, 0xf2, 0x00, 0xc4, 0x00, 0xbe, 0x42, 0x8b,
+	0x04, 0xf2, 0x00, 0xc4, 0x00, 0x04, 0x42, 0x8b,
+	0x04, 0x11, 0x00, 0xc4, 0x00, 0x24, 0x42, 0x8b,
+	0x0c, 0x61, 0x00, 0xc4, 0x00, 0x55, 0x42, 0x8b,
+	0x04, 0x50, 0x00, 0xc4, 0x00, 0x3f, 0x42, 0x8b,
+	0x0c, 0x01, 0x00, 0xc4, 0x00, 0x00, 0x40, 0x45,
+	0x20, 0x01, 0x79, 0x80, 0x00, 0x30, 0x42, 0x8b,
+	0x04, 0x62, 0x00, 0xc4, 0x00, 0x00, 0x40, 0x45,
+	0x00, 0x00, 0x71, 0x8b, 0x40, 0x01, 0x00, 0x80,
+	0x04, 0x50, 0x00, 0x44, 0xff, 0x00, 0xe2, 0xab,
+	0x08, 0xc2, 0x00, 0xc4, 0x0f, 0xf2, 0xa8, 0xa8,
+	0x20, 0x00, 0xb1, 0x88, 0x00, 0x00, 0x41, 0x02,
+	0x4d, 0xf2, 0x00, 0x39, 0xc0, 0x01, 0x00, 0x82,
+	0x04, 0x50, 0x00, 0x44, 0x0d, 0xf2, 0xa3, 0xa8,
+	0x4d, 0xf2, 0x00, 0x39, 0x04, 0x50, 0x00, 0x44,
+	0xff, 0x00, 0xe2, 0xab, 0x20, 0x00, 0x00, 0x88,
+	0x00, 0x00, 0x61, 0x02, 0x4d, 0xf2, 0x04, 0x19,
+	0x04, 0x50, 0x00, 0x44, 0xff, 0x00, 0xe2, 0xab,
+	0xa0, 0x00, 0x00, 0x88, 0x00, 0x00, 0x61, 0x10,
+	0x4d, 0xf2, 0x04, 0x19, 0x04, 0x50, 0x00, 0x44,
+	0xff, 0x20, 0xe2, 0xab, 0x60, 0x00, 0x00, 0x88,
+	0x00, 0x00, 0x71, 0xc0, 0x4d, 0xf2, 0x04, 0x19,
+	0x04, 0x50, 0x00, 0x44, 0x00, 0x00, 0x7a, 0x0a,
+	0x20, 0x01, 0xf0, 0x80, 0x01, 0xa0, 0x41, 0x0a,
+	0x00, 0x11, 0x00, 0xc4, 0x20, 0x01, 0xf0, 0x80,
+	0xc1, 0x30, 0x04, 0x19, 0x04, 0x50, 0x00, 0x44,
+	0x00, 0x00, 0x79, 0x80, 0x0c, 0x41, 0x00, 0x84,
+	0x89, 0x00, 0x71, 0x8b, 0xc8, 0x30, 0x04, 0x19,
+	0x97, 0x00, 0x71, 0x8b, 0xcd, 0x00, 0x00, 0x39,
+	0x00, 0x01, 0xb1, 0x80, 0x80, 0x00, 0x04, 0x19,
+	0x82, 0x00, 0x04, 0x19, 0xc1, 0x20, 0x04, 0x19,
+	0xc3, 0x20, 0x04, 0x19, 0xc2, 0x30, 0x04, 0x19,
+	0xcd, 0x10, 0x04, 0x19, 0xcf, 0x10, 0x04, 0x19,
+	0xb0, 0x00, 0x71, 0x8b, 0x84, 0x00, 0x04, 0x19,
+	0x86, 0x00, 0x04, 0x19, 0x80, 0x00, 0x71, 0x8b,
+	0xcb, 0x20, 0x04, 0x19, 0x93, 0x00, 0x01, 0x4f,
+	0xcd, 0x30, 0x00, 0x09, 0xcf, 0x30, 0x00, 0x09,
+	0x03, 0x02, 0x04, 0x49, 0x08, 0x41, 0x00, 0x14,
+	0x04, 0x50, 0x00, 0x44, 0x00, 0x00, 0x63, 0x80,
+	0x00, 0x00, 0x06, 0x19, 0x03, 0x00, 0x04, 0x49,
+	0x04, 0x50, 0x00, 0x44, 0x20, 0x01, 0x63, 0x80,
+	0x00, 0x00, 0x06, 0x19, 0x00, 0x20, 0xe2, 0x8b,
+	0x00, 0xc1, 0x00, 0x84, 0x47, 0x00, 0x51, 0x8b,
+	0xc0, 0x20, 0x00, 0x39, 0x00, 0x00, 0x63, 0x80,
+	0xc1, 0xa0, 0x04, 0x19, 0x00, 0xe1, 0x00, 0x44,
+	0xbd, 0x00, 0x51, 0x8b, 0xc0, 0x20, 0x00, 0x39,
+	0x00, 0x00, 0xb1, 0x80, 0xc1, 0xa0, 0x04, 0x19,
+	0x03, 0x00, 0x04, 0x49, 0x04, 0x50, 0x00, 0x44,
+	0x00, 0x20, 0x61, 0x0a, 0x00, 0x01, 0x00, 0x45,
+	0x02, 0x30, 0x61, 0x0a, 0x0c, 0x83, 0x00, 0xc4,
+	0x0c, 0x78, 0x08, 0x44, 0x04, 0x5a, 0x08, 0x44,
+	0xb2, 0x00, 0x09, 0x4f, 0x10, 0x42, 0x09, 0x8e,
+	0x05, 0xb0, 0xe0, 0x18, 0x04, 0x23, 0x00, 0x84,
+	0x0c, 0x01, 0x00, 0x11, 0x08, 0x05, 0x61, 0x10,
+	0x00, 0x49, 0x08, 0x44, 0x00, 0x48, 0x08, 0x44,
+	0xb2, 0x00, 0x09, 0x4f, 0x80, 0x00, 0x71, 0x8b,
+	0xc0, 0x00, 0x00, 0x82, 0x0c, 0x01, 0x33, 0x10,
+	0x28, 0x01, 0xa3, 0x10, 0x00, 0x01, 0x7a, 0x80,
+	0x8c, 0x01, 0x00, 0x80, 0x02, 0x30, 0x61, 0x0a,
+	0x20, 0x00, 0x04, 0x19, 0x0c, 0x83, 0x00, 0xc4,
+	0x05, 0xb0, 0xc8, 0x18, 0x08, 0x43, 0x00, 0xc4,
+	0x01, 0x30, 0xc8, 0x0a, 0x0c, 0x38, 0x00, 0xc4,
+	0x08, 0x88, 0x00, 0x44, 0x0c, 0x78, 0x08, 0x44,
+	0x04, 0x5a, 0x08, 0x44, 0x00, 0x00, 0xa3, 0x18,
+	0x80, 0x00, 0x04, 0x19, 0x0b, 0x04, 0x61, 0xa8,
+	0xc3, 0x20, 0x00, 0x39, 0xc3, 0x30, 0x04, 0x19,
+	0x0f, 0x10, 0x61, 0x0a, 0xca, 0x30, 0x04, 0x19,
+	0x09, 0x04, 0x41, 0xa8, 0xe1, 0x20, 0x00, 0x39,
+	0xd1, 0x00, 0x09, 0x4f, 0x00, 0x04, 0x61, 0x02,
+	0x08, 0x63, 0x00, 0x44, 0x03, 0x30, 0x41, 0x0a,
+	0x20, 0x00, 0x00, 0x39, 0xa3, 0x00, 0x09, 0x4f,
+	0x00, 0x04, 0x61, 0x02, 0x00, 0x48, 0x08, 0x44,
+	0x08, 0x88, 0x00, 0x44, 0x02, 0x30, 0x61, 0x0a,
+	0x00, 0x08, 0x00, 0xc4, 0x0c, 0x78, 0x08, 0x44,
+	0x04, 0x5a, 0x08, 0x44, 0xb2, 0x00, 0x09, 0x0f,
+	0x10, 0x40, 0x09, 0x8e, 0x00, 0x00, 0x68, 0x5b,
+	0x20, 0x04, 0xb1, 0x80, 0x02, 0x00, 0x61, 0x5b,
+	0x88, 0x03, 0x7a, 0x80, 0xac, 0x01, 0x00, 0x80,
+	0x05, 0xb0, 0xe0, 0x18, 0x00, 0xd3, 0x00, 0x84,
+	0x00, 0x49, 0x08, 0x44, 0x00, 0x48, 0x08, 0x44,
+	0xb2, 0x00, 0x09, 0x0f, 0x80, 0x00, 0x71, 0x8b,
+	0xc0, 0x00, 0x00, 0x82, 0x02, 0x30, 0x61, 0x0a,
+	0x00, 0x08, 0x00, 0xc4, 0x05, 0xb0, 0xc8, 0x18,
+	0x0c, 0x18, 0x00, 0xc4, 0x01, 0x30, 0xc8, 0x0a,
+	0x0c, 0x38, 0x00, 0xc4, 0x08, 0x88, 0x00, 0x44,
+	0x0c, 0x78, 0x08, 0x44, 0x00, 0x00, 0x61, 0x18,
+	0x20, 0x05, 0xb1, 0x80, 0x00, 0x00, 0x68, 0xcb,
+	0x80, 0x00, 0x04, 0x19, 0x0d, 0x10, 0x61, 0x0a,
+	0xc3, 0x30, 0x04, 0x19, 0x0b, 0x04, 0x41, 0xa8,
+	0x09, 0x04, 0x41, 0xa8, 0xe1, 0x20, 0x00, 0x39,
+	0x08, 0x38, 0x00, 0x44, 0x03, 0x30, 0x41, 0x0a,
+	0x20, 0x04, 0xb1, 0x80, 0x00, 0x48, 0x08, 0x44,
+	0x08, 0x88, 0x00, 0x44, 0x00, 0x00, 0xb1, 0x80,
+	0xc2, 0x30, 0x04, 0x19, 0x0c, 0xb8, 0x00, 0xd4,
+	0x0f, 0x30, 0x61, 0x0a, 0x0d, 0x30, 0xc8, 0x0a,
+	0x0c, 0xb8, 0x00, 0xc4, 0x93, 0x00, 0x01, 0x4f,
+	0xe7, 0x00, 0x01, 0x6f, 0x0f, 0x30, 0x61, 0x0a,
+	0x20, 0x00, 0x00, 0x88, 0x02, 0x00, 0x61, 0x02,
+	0x41, 0x04, 0x04, 0x19, 0x02, 0x04, 0x61, 0x02,
+	0x43, 0x04, 0x04, 0x39, 0xcf, 0x30, 0x00, 0x09,
+	0x20, 0x00, 0x09, 0x49, 0x00, 0x59, 0x00, 0x44,
+	0x93, 0x00, 0x01, 0x4f, 0xe7, 0x00, 0x01, 0x6f,
+	0x0d, 0x30, 0x61, 0x0a, 0x20, 0x00, 0x61, 0x88,
+	0xc2, 0x00, 0x00, 0x82, 0xc2, 0x03, 0x00, 0x82,
+	0xcd, 0x30, 0x00, 0x09, 0x20, 0x00, 0x09, 0x49,
+	0x0f, 0x30, 0x61, 0x0a, 0x0d, 0x30, 0xc8, 0x0a,
+	0x0c, 0x58, 0x00, 0x84, 0x02, 0x30, 0x61, 0x0a,
+	0x05, 0xb0, 0xa8, 0x18, 0xc2, 0x30, 0x04, 0x19,
+	0x00, 0x00, 0x00, 0x46, 0x90, 0x40, 0x09, 0x8f,
+	0x12, 0x04, 0x09, 0x6e, 0x03, 0x00, 0x09, 0x0e,
+	0x00, 0x01, 0x71, 0x82, 0x20, 0x01, 0x00, 0x80,
+	0x00, 0x00, 0x61, 0xcb, 0x80, 0x04, 0xb1, 0x80,
+	0x00, 0x01, 0xe0, 0x60, 0x0c, 0xd8, 0x04, 0x14,
+	0x00, 0x01, 0xeb, 0x80, 0x40, 0x00, 0x52, 0x1b,
+	0x80, 0x00, 0x79, 0x80, 0xc0, 0x01, 0x71, 0xc2,
+	0x20, 0x00, 0xc0, 0x80, 0x08, 0x0a, 0x04, 0x54,
+	0xc0, 0x04, 0xa8, 0x82, 0x80, 0x00, 0x72, 0x1b,
+	0x80, 0x00, 0x00, 0x80, 0x00, 0x01, 0xf0, 0x80,
+	0x20, 0x00, 0xc0, 0x80, 0x0c, 0x2a, 0x04, 0x54,
+	0xc0, 0x04, 0xa8, 0x82, 0x10, 0x00, 0x72, 0x1b,
+	0x80, 0x00, 0x00, 0x80, 0x00, 0x01, 0xf0, 0x80,
+	0x20, 0x00, 0xc0, 0x80, 0x08, 0x3a, 0x04, 0x54,
+	0xc0, 0x04, 0xa8, 0x82, 0x20, 0x00, 0x72, 0x1b,
+	0x80, 0x00, 0x00, 0x80, 0xc0, 0x03, 0xf0, 0x82,
+	0x20, 0x00, 0xa0, 0x80, 0x00, 0x01, 0x00, 0x11,
+	0x40, 0x00, 0xc2, 0x8b, 0x00, 0xaa, 0x00, 0xc4,
+	0x00, 0x00, 0xe9, 0x80, 0x05, 0xb0, 0xa8, 0x18,
+	0x00, 0x01, 0xa8, 0x22, 0xd0, 0x01, 0x00, 0x82,
+	0xf0, 0x00, 0xe2, 0x1b, 0x06, 0x20, 0xa8, 0x0a,
+	0x2d, 0x10, 0x61, 0x0a, 0xd1, 0x00, 0x09, 0x2e,
+	0x00, 0x01, 0xa8, 0x02, 0x0e, 0x10, 0xc8, 0x0a,
+	0x0c, 0xba, 0x04, 0x14, 0x0e, 0x10, 0x61, 0x0a,
+	0x04, 0x4a, 0x00, 0x44, 0x0c, 0x10, 0xc8, 0x0a,
+	0x04, 0x4a, 0x04, 0x54, 0x0c, 0x10, 0x61, 0x0a,
+	0xd0, 0x01, 0x00, 0x82, 0x00, 0x10, 0xa8, 0x18,
+	0xa0, 0x00, 0x00, 0x88, 0x00, 0x01, 0x71, 0x82,
+	0x03, 0x00, 0x09, 0x0e, 0x9a, 0x01, 0x00, 0x60,
+	0x32, 0x00, 0x09, 0x2e, 0x00, 0x00, 0x00, 0x46,
+	0x00, 0x01, 0x71, 0x82, 0x20, 0x01, 0x00, 0x80,
+	0x00, 0x00, 0x61, 0xcb, 0x80, 0x24, 0xb1, 0xc0,
+	0x00, 0x31, 0xe0, 0x60, 0x0c, 0xca, 0x04, 0x14,
+	0x00, 0x01, 0xeb, 0x80, 0x40, 0x00, 0x52, 0x1b,
+	0x80, 0x00, 0x79, 0x80, 0xc0, 0x01, 0x71, 0xc2,
+	0x20, 0x00, 0xc0, 0x80, 0x08, 0xda, 0x04, 0x54,
+	0xc0, 0x04, 0xa8, 0x82, 0x80, 0x00, 0x72, 0x1b,
+	0x80, 0x00, 0x00, 0x80, 0x00, 0x01, 0xf0, 0x80,
+	0x20, 0x00, 0xc0, 0x80, 0x0c, 0xfa, 0x04, 0x54,
+	0xc0, 0x04, 0xa8, 0x82, 0x10, 0x00, 0x72, 0x1b,
+	0x80, 0x00, 0x00, 0x80, 0x00, 0x01, 0xf0, 0x80,
+	0x20, 0x00, 0xc0, 0x80, 0x08, 0x29, 0x04, 0x54,
+	0xc0, 0x04, 0xa8, 0x82, 0x20, 0x00, 0x72, 0x1b,
+	0x80, 0x00, 0x00, 0x80, 0xc0, 0x03, 0xf0, 0x82,
+	0x20, 0x00, 0xa0, 0x80, 0x00, 0x01, 0x00, 0x11,
+	0x40, 0x00, 0xc2, 0x8b, 0x00, 0x39, 0x00, 0xc4,
+	0x00, 0x00, 0xe9, 0x80, 0x05, 0xb0, 0xa8, 0x18,
+	0x00, 0x01, 0xa8, 0x22, 0xd0, 0x01, 0x00, 0x82,
+	0xb0, 0x00, 0xe2, 0x1b, 0x06, 0x20, 0xa8, 0x0a,
+	0x2f, 0x10, 0x61, 0x0a, 0xf1, 0x00, 0x09, 0x2e,
+	0x00, 0x01, 0xa8, 0x02, 0x0e, 0x10, 0xc8, 0x0a,
+	0x0c, 0xa9, 0x04, 0x14, 0x0e, 0x10, 0x61, 0x0a,
+	0x04, 0x99, 0x00, 0x44, 0x0c, 0x10, 0xc8, 0x0a,
+	0x04, 0x99, 0x04, 0x54, 0x0c, 0x10, 0x61, 0x0a,
+	0xd0, 0x01, 0x00, 0x82, 0x00, 0x10, 0xa8, 0x18,
+	0xa0, 0x00, 0x00, 0x88, 0x00, 0x01, 0x71, 0x82,
+	0x9f, 0x01, 0x00, 0x60, 0x00, 0x00, 0x00, 0x46,
+	0x00, 0x00, 0x33, 0x80, 0x00, 0x00, 0x83, 0x80,
+	0x20, 0x00, 0x7a, 0x80, 0x20, 0x07, 0x33, 0x80,
+	0x00, 0x00, 0x83, 0x80, 0x20, 0x04, 0x7a, 0x80,
+	0x20, 0x01, 0x00, 0x80, 0x00, 0x00, 0x00, 0x46,
+	0x02, 0x00, 0x61, 0x0a, 0x04, 0x1b, 0x04, 0x14,
+	0x01, 0x00, 0x61, 0x0a, 0x03, 0x00, 0x48, 0x0a,
+	0x0c, 0x79, 0x04, 0x54, 0xc3, 0x00, 0x04, 0x19,
+	0x04, 0xc9, 0x00, 0x44, 0x08, 0x00, 0xc8, 0x0a,
+	0x04, 0xc9, 0x04, 0x54, 0xc8, 0x00, 0x04, 0x19,
+	0x0a, 0x00, 0x61, 0x0a, 0x09, 0x00, 0x48, 0x0a,
+	0x0c, 0xe9, 0x04, 0x54, 0xc9, 0x00, 0x04, 0x19,
+	0x04, 0xd9, 0x00, 0x44, 0x0b, 0x00, 0xc8, 0x0a,
+	0x04, 0xd9, 0x04, 0x54, 0xcb, 0x00, 0x04, 0x19,
+	0x04, 0x00, 0x61, 0x0a, 0x06, 0x00, 0x48, 0x0a,
+	0x0c, 0xf9, 0x04, 0x54, 0xc6, 0x00, 0x04, 0x19,
+	0x04, 0x0b, 0x00, 0x44, 0x05, 0x00, 0xc8, 0x0a,
+	0x04, 0x0b, 0x04, 0x54, 0xc5, 0x00, 0x04, 0x19,
+	0x07, 0x00, 0x61, 0x0a, 0x0c, 0x00, 0x48, 0x0a,
+	0x0c, 0x2b, 0x04, 0x54, 0xcc, 0x00, 0x04, 0x19,
+	0x04, 0x1b, 0x00, 0x44, 0x0e, 0x00, 0xc8, 0x0a,
+	0x04, 0x1b, 0x04, 0x54, 0xce, 0x00, 0x04, 0x19,
+	0x00, 0x00, 0x40, 0x45, 0x92, 0x20, 0x71, 0x8b,
+	0xa6, 0xc5, 0x11, 0x00
+};
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/sb/sb16_main.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/sb/sb16_main.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/sb/sb16_main.c	(revision 91)
@@ -0,0 +1,959 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  Routines for control of 16-bit SoundBlaster cards and clones
+ *  Note: This is very ugly hardware which uses one 8-bit DMA channel and
+ *        second 16-bit DMA channel. Unfortunately 8-bit DMA channel can't
+ *        transfer 16-bit samples and 16-bit DMA channels can't transfer
+ *        8-bit samples. This make full duplex more complicated than
+ *        can be... People, don't buy these soundcards for full 16-bit
+ *        duplex!!!
+ *  Note: 16-bit wide is assigned to first direction which made request.
+ *        With full duplex - playback is preferred with abstract layer.
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#define SNDRV_MAIN_OBJECT_FILE
+#include <sound/driver.h>
+#include <sound/sb.h>
+#include <sound/sb16_csp.h>
+#include <sound/mpu401.h>
+#include <sound/control.h>
+#include <sound/info.h>
+
+#ifdef CONFIG_SND_SB16_CSP
+static void snd_sb16_csp_playback_prepare(sb_t *chip, snd_pcm_runtime_t *runtime)
+{
+    if (chip->hardware == SB_HW_16CSP) {
+        snd_sb_csp_t *csp = chip->csp;
+
+        if (csp->running & SNDRV_SB_CSP_ST_LOADED) {
+            /* manually loaded codec */
+            if ((csp->mode & SNDRV_SB_CSP_MODE_DSP_WRITE) &&
+                ((1 << runtime->format) == csp->acc_format)) {
+                /* Supported runtime PCM format for playback */
+                if (csp->ops.csp_use(csp) == 0) {
+                    /* If CSP was successfully acquired */
+                    goto __start_CSP;
+                }
+            } else if ((csp->mode & SNDRV_SB_CSP_MODE_QSOUND) && (csp->q_enabled)) {
+                /* QSound decoder is loaded and enabled */
+                if ((1 << runtime->format) & (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 |
+                                              SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE)) {
+                    /* Only for simple PCM formats */
+                    if (csp->ops.csp_use(csp) == 0) {
+                        /* If CSP was successfully acquired */
+                        goto __start_CSP;
+                    }
+                }
+            }
+        } else if (csp->ops.csp_use(csp) == 0) {
+            /* Acquire CSP and try to autoload hardware codec */
+            if (csp->ops.csp_autoload(csp, runtime->format, SNDRV_SB_CSP_MODE_DSP_WRITE)) {
+                /* Unsupported format, release CSP */
+                csp->ops.csp_unuse(csp);
+            } else {
+            __start_CSP:
+                /* Try to start CSP */
+                if (csp->ops.csp_start(csp, (chip->mode & SB_MODE_PLAYBACK_16) ?
+                                       SNDRV_SB_CSP_SAMPLE_16BIT : SNDRV_SB_CSP_SAMPLE_8BIT,
+                                       (runtime->channels > 1) ?
+                                       SNDRV_SB_CSP_STEREO : SNDRV_SB_CSP_MONO)) {
+                    /* Failed, release CSP */
+                    csp->ops.csp_unuse(csp);
+                } else {
+                    /* Success, CSP acquired and running */
+                    chip->open = SNDRV_SB_CSP_MODE_DSP_WRITE;
+                }
+            }
+        }
+    }
+}
+
+static void snd_sb16_csp_capture_prepare(sb_t *chip, snd_pcm_runtime_t *runtime)
+{
+    if (chip->hardware == SB_HW_16CSP) {
+        snd_sb_csp_t *csp = chip->csp;
+
+        if (csp->running & SNDRV_SB_CSP_ST_LOADED) {
+            /* manually loaded codec */
+            if ((csp->mode & SNDRV_SB_CSP_MODE_DSP_READ) &&
+                ((1 << runtime->format) == csp->acc_format)) {
+                /* Supported runtime PCM format for capture */
+                if (csp->ops.csp_use(csp) == 0) {
+                    /* If CSP was successfully acquired */
+                    goto __start_CSP;
+                }
+            }
+        } else if (csp->ops.csp_use(csp) == 0) {
+            /* Acquire CSP and try to autoload hardware codec */
+            if (csp->ops.csp_autoload(csp, runtime->format, SNDRV_SB_CSP_MODE_DSP_READ)) {
+                /* Unsupported format, release CSP */
+                csp->ops.csp_unuse(csp);
+            } else {
+            __start_CSP:
+                /* Try to start CSP */
+                if (csp->ops.csp_start(csp, (chip->mode & SB_MODE_CAPTURE_16) ?
+                                       SNDRV_SB_CSP_SAMPLE_16BIT : SNDRV_SB_CSP_SAMPLE_8BIT,
+                                       (runtime->channels > 1) ?
+                                       SNDRV_SB_CSP_STEREO : SNDRV_SB_CSP_MONO)) {
+                    /* Failed, release CSP */
+                    csp->ops.csp_unuse(csp);
+                } else {
+                    /* Success, CSP acquired and running */
+                    chip->open = SNDRV_SB_CSP_MODE_DSP_READ;
+                }
+            }
+        }
+    }
+}
+
+static void snd_sb16_csp_update(sb_t *chip)
+{
+    if (chip->hardware == SB_HW_16CSP) {
+        snd_sb_csp_t *csp = chip->csp;
+
+        if (csp->qpos_changed) {
+            spin_lock(&chip->reg_lock);
+            csp->ops.csp_qsound_transfer (csp);
+            spin_unlock(&chip->reg_lock);
+        }
+    }
+}
+
+static void snd_sb16_csp_playback_open(sb_t *chip, snd_pcm_runtime_t *runtime)
+{
+    /* CSP decoders (QSound excluded) support only 16bit transfers */
+    if (chip->hardware == SB_HW_16CSP) {
+        snd_sb_csp_t *csp = chip->csp;
+
+        if (csp->running & SNDRV_SB_CSP_ST_LOADED) {
+            /* manually loaded codec */
+            if (csp->mode & SNDRV_SB_CSP_MODE_DSP_WRITE) {
+                runtime->hw.formats |= csp->acc_format;
+            }
+        } else {
+            /* autoloaded codecs */
+            runtime->hw.formats |= SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW |
+                SNDRV_PCM_FMTBIT_IMA_ADPCM;
+        }
+    }
+}
+
+static void snd_sb16_csp_playback_close(sb_t *chip)
+{
+    if ((chip->hardware == SB_HW_16CSP) && (chip->open == SNDRV_SB_CSP_MODE_DSP_WRITE)) {
+        snd_sb_csp_t *csp = chip->csp;
+
+        if (csp->ops.csp_stop(csp) == 0) {
+            csp->ops.csp_unuse(csp);
+            chip->open = 0;
+        }
+    }
+}
+
+static void snd_sb16_csp_capture_open(sb_t *chip, snd_pcm_runtime_t *runtime)
+{
+    /* CSP coders support only 16bit transfers */
+    if (chip->hardware == SB_HW_16CSP) {
+        snd_sb_csp_t *csp = chip->csp;
+
+        if (csp->running & SNDRV_SB_CSP_ST_LOADED) {
+            /* manually loaded codec */
+            if (csp->mode & SNDRV_SB_CSP_MODE_DSP_READ) {
+                runtime->hw.formats |= csp->acc_format;
+            }
+        } else {
+            /* autoloaded codecs */
+            runtime->hw.formats |= SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW |
+                SNDRV_PCM_FMTBIT_IMA_ADPCM;
+        }
+    }
+}
+
+static void snd_sb16_csp_capture_close(sb_t *chip)
+{
+    if ((chip->hardware == SB_HW_16CSP) && (chip->open == SNDRV_SB_CSP_MODE_DSP_READ)) {
+        snd_sb_csp_t *csp = chip->csp;
+
+        if (csp->ops.csp_stop(csp) == 0) {
+            csp->ops.csp_unuse(csp);
+            chip->open = 0;
+        }
+    }
+}
+#else
+#define snd_sb16_csp_playback_prepare(chip, runtime)	/*nop*/
+#define snd_sb16_csp_capture_prepare(chip, runtime)	/*nop*/
+#define snd_sb16_csp_update(chip)		/*nop*/
+#define snd_sb16_csp_playback_open(chip, runtime)	/*nop*/
+#define snd_sb16_csp_playback_close(chip)	/*nop*/
+#define snd_sb16_csp_capture_open(chip, runtime)	/*nop*/
+#define snd_sb16_csp_capture_close(chip)       	/*nop*/
+#endif
+
+
+static void snd_sb16_setup_rate(sb_t *chip,
+                                unsigned short rate,
+                                int channel)
+{
+    unsigned long flags;
+
+    spin_lock_irqsave(&chip->reg_lock, flags);
+    if (chip->mode & (channel == SNDRV_PCM_STREAM_PLAYBACK ? SB_MODE_PLAYBACK_16 : SB_MODE_CAPTURE_16))
+        snd_sb_ack_16bit(chip);
+    else
+        snd_sb_ack_8bit(chip);
+    if (!(chip->mode & SB_RATE_LOCK)) {
+        chip->locked_rate = rate;
+        snd_sbdsp_command(chip, SB_DSP_SAMPLE_RATE_IN);
+        snd_sbdsp_command(chip, rate >> 8);
+        snd_sbdsp_command(chip, rate & 0xff);
+        snd_sbdsp_command(chip, SB_DSP_SAMPLE_RATE_OUT);
+        snd_sbdsp_command(chip, rate >> 8);
+        snd_sbdsp_command(chip, rate & 0xff);
+    }
+    spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+static int snd_sb16_hw_params(snd_pcm_substream_t * substream,
+                              snd_pcm_hw_params_t * hw_params)
+{
+    return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+static int snd_sb16_hw_free(snd_pcm_substream_t * substream)
+{
+    snd_pcm_lib_free_pages(substream);
+    return 0;
+}
+
+static int snd_sb16_playback_prepare(snd_pcm_substream_t * substream)
+{
+    unsigned long flags;
+    sb_t *chip = snd_pcm_substream_chip(substream);
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    unsigned char format;
+    unsigned int size, count, dma;
+
+    snd_sb16_csp_playback_prepare(chip, runtime);
+    if (snd_pcm_format_unsigned(runtime->format) > 0) {
+        format = runtime->channels > 1 ? SB_DSP4_MODE_UNS_STEREO : SB_DSP4_MODE_UNS_MONO;
+    } else {
+        format = runtime->channels > 1 ? SB_DSP4_MODE_SIGN_STEREO : SB_DSP4_MODE_SIGN_MONO;
+    }
+
+    snd_sb16_setup_rate(chip, runtime->rate, SNDRV_PCM_STREAM_PLAYBACK);
+    size = chip->p_dma_size = snd_pcm_lib_buffer_bytes(substream);
+    dma = (chip->mode & SB_MODE_PLAYBACK_8) ? chip->dma8 : chip->dma16;
+    snd_dma_program(dma, runtime->dma_area, size, DMA_MODE_WRITE | DMA_AUTOINIT);
+
+    count = snd_pcm_lib_period_bytes(substream);
+    spin_lock_irqsave(&chip->reg_lock, flags);
+    if (chip->mode & SB_MODE_PLAYBACK_16) {
+        count >>= 1;
+        count--;
+        snd_sbdsp_command(chip, SB_DSP4_OUT16_AI);
+        snd_sbdsp_command(chip, format);
+        snd_sbdsp_command(chip, count & 0xff);
+        snd_sbdsp_command(chip, count >> 8);
+        snd_sbdsp_command(chip, SB_DSP_DMA16_OFF);
+    } else {
+        count--;
+        snd_sbdsp_command(chip, SB_DSP4_OUT8_AI);
+        snd_sbdsp_command(chip, format);
+        snd_sbdsp_command(chip, count & 0xff);
+        snd_sbdsp_command(chip, count >> 8);
+        snd_sbdsp_command(chip, SB_DSP_DMA8_OFF);
+    }
+    spin_unlock_irqrestore(&chip->reg_lock, flags);
+    return 0;
+}
+
+static int snd_sb16_playback_trigger(snd_pcm_substream_t * substream,
+                                     int cmd)
+{
+    sb_t *chip = snd_pcm_substream_chip(substream);
+    int result = 0;
+
+    spin_lock(&chip->reg_lock);
+    switch (cmd) {
+    case SNDRV_PCM_TRIGGER_START:
+        chip->mode |= SB_RATE_LOCK_PLAYBACK;
+        snd_sbdsp_command(chip, chip->mode & SB_MODE_PLAYBACK_16 ? SB_DSP_DMA16_ON : SB_DSP_DMA8_ON);
+        break;
+    case SNDRV_PCM_TRIGGER_STOP:
+        snd_sbdsp_command(chip, chip->mode & SB_MODE_PLAYBACK_16 ? SB_DSP_DMA16_OFF : SB_DSP_DMA8_OFF);
+        /* next two lines are needed for some types of DSP4 (SB AWE 32 - 4.13) */
+        if (chip->mode & SB_RATE_LOCK_CAPTURE)
+            snd_sbdsp_command(chip, chip->mode & SB_MODE_CAPTURE_16 ? SB_DSP_DMA16_ON : SB_DSP_DMA8_ON);
+        chip->mode &= ~SB_RATE_LOCK_PLAYBACK;
+        break;
+    default:
+        result = -EINVAL;
+    }
+    spin_unlock(&chip->reg_lock);
+    return result;
+}
+
+static int snd_sb16_capture_prepare(snd_pcm_substream_t * substream)
+{
+    unsigned long flags;
+    sb_t *chip = snd_pcm_substream_chip(substream);
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    unsigned char format;
+    unsigned int size, count, dma;
+
+    snd_sb16_csp_capture_prepare(chip, runtime);
+    if (snd_pcm_format_unsigned(runtime->format) > 0) {
+        format = runtime->channels > 1 ? SB_DSP4_MODE_UNS_STEREO : SB_DSP4_MODE_UNS_MONO;
+    } else {
+        format = runtime->channels > 1 ? SB_DSP4_MODE_SIGN_STEREO : SB_DSP4_MODE_SIGN_MONO;
+    }
+    snd_sb16_setup_rate(chip, runtime->rate, SNDRV_PCM_STREAM_CAPTURE);
+    size = chip->c_dma_size = snd_pcm_lib_buffer_bytes(substream);
+    dma = (chip->mode & SB_MODE_CAPTURE_8) ? chip->dma8 : chip->dma16;
+    snd_dma_program(dma, runtime->dma_area, size, DMA_MODE_READ | DMA_AUTOINIT);
+
+    count = snd_pcm_lib_period_bytes(substream);
+    spin_lock_irqsave(&chip->reg_lock, flags);
+    if (chip->mode & SB_MODE_CAPTURE_16) {
+        count >>= 1;
+        count--;
+        snd_sbdsp_command(chip, SB_DSP4_IN16_AI);
+        snd_sbdsp_command(chip, format);
+        snd_sbdsp_command(chip, count & 0xff);
+        snd_sbdsp_command(chip, count >> 8);
+        snd_sbdsp_command(chip, SB_DSP_DMA16_OFF);
+    } else {
+        count--;
+        snd_sbdsp_command(chip, SB_DSP4_IN8_AI);
+        snd_sbdsp_command(chip, format);
+        snd_sbdsp_command(chip, count & 0xff);
+        snd_sbdsp_command(chip, count >> 8);
+        snd_sbdsp_command(chip, SB_DSP_DMA8_OFF);
+    }
+    spin_unlock_irqrestore(&chip->reg_lock, flags);
+    return 0;
+}
+
+static int snd_sb16_capture_trigger(snd_pcm_substream_t * substream,
+                                    int cmd)
+{
+    sb_t *chip = snd_pcm_substream_chip(substream);
+    int result = 0;
+
+    spin_lock(&chip->reg_lock);
+    switch (cmd) {
+    case SNDRV_PCM_TRIGGER_START:
+        chip->mode |= SB_RATE_LOCK_CAPTURE;
+        snd_sbdsp_command(chip, chip->mode & SB_MODE_CAPTURE_16 ? SB_DSP_DMA16_ON : SB_DSP_DMA8_ON);
+        break;
+    case SNDRV_PCM_TRIGGER_STOP:
+        snd_sbdsp_command(chip, chip->mode & SB_MODE_CAPTURE_16 ? SB_DSP_DMA16_OFF : SB_DSP_DMA8_OFF);
+        /* next two lines are needed for some types of DSP4 (SB AWE 32 - 4.13) */
+        if (chip->mode & SB_RATE_LOCK_PLAYBACK)
+            snd_sbdsp_command(chip, chip->mode & SB_MODE_PLAYBACK_16 ? SB_DSP_DMA16_ON : SB_DSP_DMA8_ON);
+        chip->mode &= ~SB_RATE_LOCK_CAPTURE;
+        break;
+    default:
+        result = -EINVAL;
+    }
+    spin_unlock(&chip->reg_lock);
+    return result;
+}
+
+void snd_sb16dsp_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+    sb_t *chip = dev_id;
+    unsigned char status;
+    int ok;
+
+    spin_lock(&chip->mixer_lock);
+    status = snd_sbmixer_read(chip, SB_DSP4_IRQSTATUS);
+    spin_unlock(&chip->mixer_lock);
+    if ((status & SB_IRQTYPE_MPUIN) && chip->rmidi)
+        snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data, regs);
+    if (status & SB_IRQTYPE_8BIT) {
+        ok = 0;
+        if (chip->mode & SB_MODE_PLAYBACK_8) {
+            snd_pcm_period_elapsed(chip->playback_substream);
+            snd_sb16_csp_update(chip);
+            ok++;
+        }
+        if (chip->mode & SB_MODE_CAPTURE_8) {
+            snd_pcm_period_elapsed(chip->capture_substream);
+            ok++;
+        }
+        spin_lock(&chip->reg_lock);
+        if (!ok)
+            snd_sbdsp_command(chip, SB_DSP_DMA8_OFF);
+        snd_sb_ack_8bit(chip);
+        spin_unlock(&chip->reg_lock);
+    }
+    if (status & SB_IRQTYPE_16BIT) {
+        ok = 0;
+        if (chip->mode & SB_MODE_PLAYBACK_16) {
+            snd_pcm_period_elapsed(chip->playback_substream);
+            snd_sb16_csp_update(chip);
+            ok++;
+        }
+        if (chip->mode & SB_MODE_CAPTURE_16) {
+            snd_pcm_period_elapsed(chip->capture_substream);
+            ok++;
+        }
+        spin_lock(&chip->reg_lock);
+        if (!ok)
+            snd_sbdsp_command(chip, SB_DSP_DMA16_OFF);
+        snd_sb_ack_16bit(chip);
+        spin_unlock(&chip->reg_lock);
+    }
+}
+
+/*
+
+*/
+
+static snd_pcm_uframes_t snd_sb16_playback_pointer(snd_pcm_substream_t * substream)
+{
+    sb_t *chip = snd_pcm_substream_chip(substream);
+    unsigned int dma;
+    size_t ptr;
+
+    dma = (chip->mode & SB_MODE_PLAYBACK_8) ? chip->dma8 : chip->dma16;
+    ptr = chip->p_dma_size - snd_dma_residue(dma);
+    return bytes_to_frames(substream->runtime, ptr);
+}
+
+static snd_pcm_uframes_t snd_sb16_capture_pointer(snd_pcm_substream_t * substream)
+{
+    sb_t *chip = snd_pcm_substream_chip(substream);
+    unsigned int dma;
+    size_t ptr;
+
+    dma = (chip->mode & SB_MODE_CAPTURE_8) ? chip->dma8 : chip->dma16;
+    ptr = chip->c_dma_size - snd_dma_residue(dma);
+    return bytes_to_frames(substream->runtime, ptr);
+}
+
+/*
+
+*/
+
+#ifdef TARGET_OS2
+static snd_pcm_hardware_t snd_sb16_playback =
+{
+    /*	info:		  */	(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+                                 SNDRV_PCM_INFO_MMAP_VALID),
+                                 /*	formats:	  */	0,
+                                 /*	rates:		  */	SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_44100,
+                                 /*	rate_min:	  */	4000,
+                                 /*	rate_max:	  */	44100,
+                                 /*	channels_min:	  */	1,
+                                 /*	channels_max:	  */	2,
+                                 /*	buffer_bytes_max:  */	(128*1024),
+                                 /*	period_bytes_min:  */	64,
+                                 /*	period_bytes_max:  */	(128*1024),
+                                 /*	periods_min:	  */	1,
+                                 /*	periods_max:	  */	1024,
+                                 /*	fifo_size:	  */	0,
+};
+
+static snd_pcm_hardware_t snd_sb16_capture =
+{
+    /*	info:		  */	(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+                                 SNDRV_PCM_INFO_MMAP_VALID),
+                                 /*	formats:	  */	0,
+                                 /*	rates:		  */	SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_44100,
+                                 /*	rate_min:	  */	4000,
+                                 /*	rate_max:	  */	44100,
+                                 /*	channels_min:	  */	1,
+                                 /*	channels_max:	  */	2,
+                                 /*	buffer_bytes_max:  */	(128*1024),
+                                 /*	period_bytes_min:  */	64,
+                                 /*	period_bytes_max:  */	(128*1024),
+                                 /*	periods_min:	  */	1,
+                                 /*	periods_max:	  */	1024,
+                                 /*	fifo_size:	  */	0,
+};
+#else
+static snd_pcm_hardware_t snd_sb16_playback =
+{
+info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+                         SNDRV_PCM_INFO_MMAP_VALID),
+    formats:		0,
+    rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_44100,
+    rate_min:		4000,
+    rate_max:		44100,
+    channels_min:		1,
+    channels_max:		2,
+    buffer_bytes_max:	(128*1024),
+    period_bytes_min:	64,
+    period_bytes_max:	(128*1024),
+    periods_min:		1,
+    periods_max:		1024,
+    fifo_size:		0,
+};
+
+static snd_pcm_hardware_t snd_sb16_capture =
+{
+info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+                         SNDRV_PCM_INFO_MMAP_VALID),
+    formats:		0,
+    rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_44100,
+    rate_min:		4000,
+    rate_max:		44100,
+    channels_min:		1,
+    channels_max:		2,
+    buffer_bytes_max:	(128*1024),
+    period_bytes_min:	64,
+    period_bytes_max:	(128*1024),
+    periods_min:		1,
+    periods_max:		1024,
+    fifo_size:		0,
+};
+#endif
+
+/*
+ *  open/close
+ */
+
+int snd_sb16_playback_open(snd_pcm_substream_t * substream)
+{
+    unsigned long flags;
+    sb_t *chip = snd_pcm_substream_chip(substream);
+    snd_pcm_runtime_t *runtime = substream->runtime;
+
+    spin_lock_irqsave(&chip->open_lock, flags);
+    if (chip->mode & SB_MODE_PLAYBACK) {
+        spin_unlock_irqrestore(&chip->open_lock, flags);
+        return -EAGAIN;
+    }
+    runtime->hw = snd_sb16_playback;
+
+    /* skip if 16 bit DMA was reserved for capture */
+    if (chip->force_mode16 & SB_MODE_CAPTURE_16)
+        goto __skip_16bit;
+
+    if (chip->dma16 >= 0 && !(chip->mode & SB_MODE_CAPTURE_16)) {
+        chip->mode |= SB_MODE_PLAYBACK_16;
+        runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE;
+        /* Vibra16X hack */
+        if (chip->dma16 <= 3) {
+            runtime->hw.buffer_bytes_max =
+                runtime->hw.period_bytes_max = 64 * 1024;
+        } else {
+            snd_sb16_csp_playback_open(chip, runtime);
+        }
+        goto __open_ok;
+    }
+
+__skip_16bit:
+    if (chip->dma8 >= 0 && !(chip->mode & SB_MODE_CAPTURE_8)) {
+        chip->mode |= SB_MODE_PLAYBACK_8;
+        /* DSP v 4.xx can transfer 16bit data through 8bit DMA channel, SBHWPG 2-7 */
+        if (chip->dma16 < 0) {
+            runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE;
+            chip->mode |= SB_MODE_PLAYBACK_16;
+        } else {
+            runtime->hw.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8;
+        }
+        runtime->hw.buffer_bytes_max =
+            runtime->hw.period_bytes_max = 64 * 1024;
+        goto __open_ok;
+    }
+    spin_unlock_irqrestore(&chip->open_lock, flags);
+    return -EAGAIN;
+
+__open_ok:
+    if (chip->hardware == SB_HW_ALS100)
+        runtime->hw.rate_max = 48000;
+    if (chip->mode & SB_RATE_LOCK)
+        runtime->hw.rate_min = runtime->hw.rate_max = chip->locked_rate;
+    chip->playback_substream = substream;
+    spin_unlock_irqrestore(&chip->open_lock, flags);
+    return 0;
+}
+
+int snd_sb16_playback_close(snd_pcm_substream_t * substream)
+{
+    unsigned long flags;
+    sb_t *chip = snd_pcm_substream_chip(substream);
+
+    snd_sb16_csp_playback_close(chip);
+    spin_lock_irqsave(&chip->open_lock, flags);
+    chip->playback_substream = NULL;
+    chip->mode &= ~SB_MODE_PLAYBACK;
+    spin_unlock_irqrestore(&chip->open_lock, flags);
+    return 0;
+}
+
+int snd_sb16_capture_open(snd_pcm_substream_t * substream)
+{
+    unsigned long flags;
+    sb_t *chip = snd_pcm_substream_chip(substream);
+    snd_pcm_runtime_t *runtime = substream->runtime;
+
+    spin_lock_irqsave(&chip->open_lock, flags);
+    if (chip->mode & SB_MODE_CAPTURE) {
+        spin_unlock_irqrestore(&chip->open_lock, flags);
+        return -EAGAIN;
+    }
+    runtime->hw = snd_sb16_capture;
+
+    /* skip if 16 bit DMA was reserved for playback */
+    if (chip->force_mode16 & SB_MODE_PLAYBACK_16)
+        goto __skip_16bit;
+
+    if (chip->dma16 >= 0 && !(chip->mode & SB_MODE_PLAYBACK_16)) {
+        chip->mode |= SB_MODE_CAPTURE_16;
+        runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE;
+        /* Vibra16X hack */
+        if (chip->dma16 <= 3) {
+            runtime->hw.buffer_bytes_max =
+                runtime->hw.period_bytes_max = 64 * 1024;
+        } else {
+            snd_sb16_csp_capture_open(chip, runtime);
+        }
+        goto __open_ok;
+    }
+
+__skip_16bit:
+    if (chip->dma8 >= 0 && !(chip->mode & SB_MODE_PLAYBACK_8)) {
+        chip->mode |= SB_MODE_CAPTURE_8;
+        /* DSP v 4.xx can transfer 16bit data through 8bit DMA channel, SBHWPG 2-7 */
+        if (chip->dma16 < 0) {
+            runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE;
+            chip->mode |= SB_MODE_CAPTURE_16;
+        } else {
+            runtime->hw.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8;
+        }
+        runtime->hw.buffer_bytes_max =
+            runtime->hw.period_bytes_max = 64 * 1024;
+        goto __open_ok;
+    }
+    spin_unlock_irqrestore(&chip->open_lock, flags);
+    return -EAGAIN;
+
+__open_ok:
+    if (chip->hardware == SB_HW_ALS100)
+        runtime->hw.rate_max = 48000;
+    if (chip->mode & SB_RATE_LOCK)
+        runtime->hw.rate_min = runtime->hw.rate_max = chip->locked_rate;
+    chip->capture_substream = substream;
+    spin_unlock_irqrestore(&chip->open_lock, flags);
+    return 0;
+}
+
+int snd_sb16_capture_close(snd_pcm_substream_t * substream)
+{
+    unsigned long flags;
+    sb_t *chip = snd_pcm_substream_chip(substream);
+
+    snd_sb16_csp_capture_close(chip);
+    spin_lock_irqsave(&chip->open_lock, flags);
+    chip->capture_substream = NULL;
+    chip->mode &= ~SB_MODE_CAPTURE;
+    spin_unlock_irqrestore(&chip->open_lock, flags);
+    return 0;
+}
+
+/*
+ *  DMA control interface
+ */
+
+static int snd_sb16_set_dma_mode(sb_t *chip, int what)
+{
+    if (chip->dma8 < 0 || chip->dma16 < 0) {
+        snd_assert(what == 0, return -EINVAL);
+        return 0;
+    }
+    if (what == 0) {
+        chip->force_mode16 = 0;
+    } else if (what == 1) {
+        chip->force_mode16 = SB_MODE_PLAYBACK_16;
+    } else if (what == 2) {
+        chip->force_mode16 = SB_MODE_CAPTURE_16;
+    } else {
+        return -EINVAL;
+    }
+    return 0;
+}
+
+static int snd_sb16_get_dma_mode(sb_t *chip)
+{
+    if (chip->dma8 < 0 || chip->dma16 < 0)
+        return 0;
+    switch (chip->force_mode16) {
+    case SB_MODE_PLAYBACK_16:
+        return 1;
+    case SB_MODE_CAPTURE_16:
+        return 2;
+    default:
+        return 0;
+    }
+}
+
+static int snd_sb16_dma_control_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+    static char *texts[3] = {
+        "Auto", "Playback", "Capture"
+    };
+
+    uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+    uinfo->count = 1;
+    uinfo->value.enumerated.items = 3;
+    if (uinfo->value.enumerated.item > 2)
+        uinfo->value.enumerated.item = 2;
+    strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+    return 0;
+}
+
+static int snd_sb16_dma_control_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+    sb_t *chip = snd_kcontrol_chip(kcontrol);
+    unsigned long flags;
+
+    spin_lock_irqsave(&chip->reg_lock, flags);
+    ucontrol->value.enumerated.item[0] = snd_sb16_get_dma_mode(chip);
+    spin_unlock_irqrestore(&chip->reg_lock, flags);
+    return 0;
+}
+
+static int snd_sb16_dma_control_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+    sb_t *chip = snd_kcontrol_chip(kcontrol);
+    unsigned long flags;
+    unsigned char nval, oval;
+    int change;
+
+    if ((nval = ucontrol->value.enumerated.item[0]) > 2)
+        return -EINVAL;
+    spin_lock_irqsave(&chip->reg_lock, flags);
+    oval = snd_sb16_get_dma_mode(chip);
+    change = nval != oval;
+    snd_sb16_set_dma_mode(chip, nval);
+    spin_unlock_irqrestore(&chip->reg_lock, flags);
+    return change;
+}
+
+snd_kcontrol_new_t snd_sb16_dma_control = {
+#ifdef TARGET_OS2
+    SNDRV_CTL_ELEM_IFACE_PCM,0,0,
+    "16-bit DMA Allocation",0,0, 0,
+    snd_sb16_dma_control_info,
+    snd_sb16_dma_control_get,
+    snd_sb16_dma_control_put,0
+#else
+iface: SNDRV_CTL_ELEM_IFACE_PCM,
+name: "16-bit DMA Allocation",
+info: snd_sb16_dma_control_info,
+get: snd_sb16_dma_control_get,
+put: snd_sb16_dma_control_put
+#endif
+};
+
+/*
+ *  Initialization part
+ */
+
+int snd_sb16dsp_configure(sb_t * chip)
+{
+    unsigned long flags;
+    unsigned char irqreg = 0, dmareg = 0, mpureg;
+    unsigned char realirq, realdma, realmpureg;
+    /* note: mpu register should be present only on SB16 Vibra soundcards */
+
+    // printk("codec->irq=%i, codec->dma8=%i, codec->dma16=%i\n", chip->irq, chip->dma8, chip->dma16);
+    spin_lock_irqsave(&chip->mixer_lock, flags);
+    mpureg = snd_sbmixer_read(chip, SB_DSP4_MPUSETUP) & ~0x06;
+    spin_unlock_irqrestore(&chip->mixer_lock, flags);
+    switch (chip->irq) {
+    case 2:
+    case 9:
+        irqreg |= SB_IRQSETUP_IRQ9;
+        break;
+    case 5:
+        irqreg |= SB_IRQSETUP_IRQ5;
+        break;
+    case 7:
+        irqreg |= SB_IRQSETUP_IRQ7;
+        break;
+    case 10:
+        irqreg |= SB_IRQSETUP_IRQ10;
+        break;
+    default:
+        return -EINVAL;
+    }
+    if (chip->dma8 >= 0) {
+        switch (chip->dma8) {
+        case 0:
+            dmareg |= SB_DMASETUP_DMA0;
+            break;
+        case 1:
+            dmareg |= SB_DMASETUP_DMA1;
+            break;
+        case 3:
+            dmareg |= SB_DMASETUP_DMA3;
+            break;
+        default:
+            return -EINVAL;
+        }
+    }
+    if (chip->dma16 >= 0) {
+        switch (chip->dma16) {
+        case 5:
+            dmareg |= SB_DMASETUP_DMA5;
+            break;
+        case 6:
+            dmareg |= SB_DMASETUP_DMA6;
+            break;
+        case 7:
+            dmareg |= SB_DMASETUP_DMA7;
+            break;
+        default:
+            return -EINVAL;
+        }
+    }
+    switch (chip->mpu_port) {
+    case 0x300:
+        mpureg |= 0x04;
+        break;
+    case 0x330:
+        mpureg |= 0x00;
+        break;
+    default:
+        mpureg |= 0x02;	/* disable MPU */
+    }
+    spin_lock_irqsave(&chip->mixer_lock, flags);
+
+    snd_sbmixer_write(chip, SB_DSP4_IRQSETUP, irqreg);
+    realirq = snd_sbmixer_read(chip, SB_DSP4_IRQSETUP);
+
+    snd_sbmixer_write(chip, SB_DSP4_DMASETUP, dmareg);
+    realdma = snd_sbmixer_read(chip, SB_DSP4_DMASETUP);
+
+    snd_sbmixer_write(chip, SB_DSP4_MPUSETUP, mpureg);
+    realmpureg = snd_sbmixer_read(chip, SB_DSP4_MPUSETUP);
+
+    spin_unlock_irqrestore(&chip->mixer_lock, flags);
+    if ((~realirq) & irqreg || (~realdma) & dmareg) {
+        snd_printk("SB16 [0x%lx]: unable to set DMA & IRQ (PnP device?)\n", chip->port);
+        snd_printk("SB16 [0x%lx]: wanted: irqreg=0x%x, dmareg=0x%x, mpureg = 0x%x\n", chip->port, realirq, realdma, realmpureg);
+        snd_printk("SB16 [0x%lx]:    got: irqreg=0x%x, dmareg=0x%x, mpureg = 0x%x\n", chip->port, irqreg, dmareg, mpureg);
+        return -ENODEV;
+    }
+    return 0;
+}
+
+#ifdef TARGET_OS2
+static snd_pcm_ops_t snd_sb16_playback_ops = {
+    snd_sb16_playback_open,
+    snd_sb16_playback_close,
+    snd_pcm_lib_ioctl,
+    snd_sb16_hw_params,
+    snd_sb16_hw_free,
+    snd_sb16_playback_prepare,
+    snd_sb16_playback_trigger,
+    snd_sb16_playback_pointer,0,0
+};
+
+static snd_pcm_ops_t snd_sb16_capture_ops = {
+    snd_sb16_capture_open,
+    snd_sb16_capture_close,
+    snd_pcm_lib_ioctl,
+    snd_sb16_hw_params,
+    snd_sb16_hw_free,
+    snd_sb16_capture_prepare,
+    snd_sb16_capture_trigger,
+    snd_sb16_capture_pointer,0
+};
+#else
+static snd_pcm_ops_t snd_sb16_playback_ops = {
+open:			snd_sb16_playback_open,
+    close:			snd_sb16_playback_close,
+    ioctl:			snd_pcm_lib_ioctl,
+    hw_params:		snd_sb16_hw_params,
+    hw_free:		snd_sb16_hw_free,
+    prepare:		snd_sb16_playback_prepare,
+    trigger:		snd_sb16_playback_trigger,
+    pointer:		snd_sb16_playback_pointer,
+};
+
+static snd_pcm_ops_t snd_sb16_capture_ops = {
+open:			snd_sb16_capture_open,
+    close:			snd_sb16_capture_close,
+    ioctl:			snd_pcm_lib_ioctl,
+    hw_params:		snd_sb16_hw_params,
+    hw_free:		snd_sb16_hw_free,
+    prepare:		snd_sb16_capture_prepare,
+    trigger:		snd_sb16_capture_trigger,
+    pointer:		snd_sb16_capture_pointer,
+};
+#endif
+
+static void snd_sb16dsp_pcm_free(snd_pcm_t *pcm)
+{
+    snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+int snd_sb16dsp_pcm(sb_t * chip, int device, snd_pcm_t ** rpcm)
+{
+    snd_card_t *card = chip->card;
+    snd_pcm_t *pcm;
+    int err;
+
+    if (rpcm)
+        *rpcm = NULL;
+    if ((err = snd_pcm_new(card, "SB16 DSP", device, 1, 1, &pcm)) < 0)
+        return err;
+    sprintf(pcm->name, "DSP v%i.%i", chip->version >> 8, chip->version & 0xff);
+    pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX;
+    pcm->private_data = chip;
+    pcm->private_free = snd_sb16dsp_pcm_free;
+
+    snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_sb16_playback_ops);
+    snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_sb16_capture_ops);
+
+    snd_ctl_add(card, snd_ctl_new1(&snd_sb16_dma_control, chip));
+
+    snd_pcm_lib_preallocate_pages_for_all(pcm, 64*1024, 128*1024, GFP_KERNEL|GFP_DMA);
+
+    if (rpcm)
+        *rpcm = pcm;
+    return 0;
+}
+
+EXPORT_SYMBOL(snd_sb16dsp_pcm);
+EXPORT_SYMBOL(snd_sb16dsp_configure);
+EXPORT_SYMBOL(snd_sb16dsp_interrupt);
+
+/*
+ *  INIT part
+ */
+
+static int __init alsa_sb16_init(void)
+{
+    return 0;
+}
+
+static void __exit alsa_sb16_exit(void)
+{
+}
+
+module_init(alsa_sb16_init)
+module_exit(alsa_sb16_exit)
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/sb/sb8.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/sb/sb8.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/sb/sb8.c	(revision 91)
@@ -0,0 +1,246 @@
+/*
+ *  Driver for SoundBlaster 1.0/2.0/Pro soundcards and compatible
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#define SNDRV_MAIN_OBJECT_FILE
+#include <sound/driver.h>
+#include <sound/sb.h>
+#include <sound/opl3.h>
+#define SNDRV_LEGACY_AUTO_PROBE
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+EXPORT_NO_SYMBOLS;
+MODULE_DESCRIPTION("Sound Blaster 1.0/2.0/Pro");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{Creative Labs,SB 1.0/SB 2.0/SB Pro}}");
+
+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;	/* Enable this card */
+static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* 0x220,0x240,0x260 */
+static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* 5,7,9,10 */
+static int snd_dma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* 1,3 */
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for Sound Blaster soundcard.");
+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 Sound Blaster soundcard.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable Sound Blaster soundcard.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_port, "Port # for SB8 driver.");
+MODULE_PARM_SYNTAX(snd_port, SNDRV_PORT12_DESC);
+MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_irq, "IRQ # for SB8 driver.");
+MODULE_PARM_SYNTAX(snd_irq, SNDRV_IRQ_DESC);
+MODULE_PARM(snd_dma8, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_dma8, "8-bit DMA # for SB8 driver.");
+MODULE_PARM_SYNTAX(snd_dma8, SNDRV_DMA8_DESC);
+
+struct snd_sb8 {
+    struct resource *fm_res;	/* used to block FM i/o region for legacy cards */
+};
+
+static snd_card_t *snd_sb8_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+
+static void snd_sb8_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+    sb_t *chip = dev_id;
+
+    if (chip->open & SB_OPEN_PCM) {
+        snd_sb8dsp_interrupt(chip);
+    } else {
+        snd_sb8dsp_midi_interrupt(chip);
+    }
+}
+
+static void snd_sb8_free(snd_card_t *card)
+{
+    struct snd_sb8 *acard = (struct snd_sb8 *)card->private_data;
+
+    if (acard == NULL)
+        return;
+    if (acard->fm_res)
+        release_resource(acard->fm_res);
+}
+
+static int __init snd_sb8_probe(int dev)
+{
+    sb_t *chip;
+    snd_card_t *card;
+    struct snd_sb8 *acard;
+    opl3_t *opl3;
+    int err;
+
+    card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE,
+                        sizeof(struct snd_sb8));
+    if (card == NULL)
+        return -ENOMEM;
+    acard = (struct snd_sb8 *)card->private_data;
+    card->private_free = snd_sb8_free;
+
+    /* block the 0x388 port to avoid PnP conflicts */
+    acard->fm_res = request_region(0x388, 4, "SoundBlaster FM");
+
+    if ((err = snd_sbdsp_create(card, snd_port[dev], snd_irq[dev],
+                                snd_sb8_interrupt,
+                                snd_dma8[dev],
+                                -1,
+                                SB_HW_AUTO,
+                                &chip)) < 0) {
+        snd_card_free(card);
+        return err;
+    }
+    if (chip->hardware >= SB_HW_16) {
+        snd_card_free(card);
+        if (chip->hardware == SB_HW_ALS100)
+            snd_printdd("ALS100 chip detected at 0x%lx, try snd-card-als100 module\n",
+                        snd_port[dev]);
+        else
+            snd_printdd("SB 16 chip detected at 0x%lx, try snd-card-sb16 module\n",
+                        snd_port[dev]);
+        return -ENODEV;
+    }
+
+    if ((err = snd_sb8dsp_pcm(chip, 0, NULL)) < 0) {
+        snd_card_free(card);
+        return err;
+    }
+    if ((err = snd_sbmixer_new(chip)) < 0) {
+        snd_card_free(card);
+        return err;
+    }
+    if (chip->hardware == SB_HW_10 || chip->hardware == SB_HW_20) {
+        if ((err = snd_opl3_create(card, chip->port + 8, 0,
+                                   OPL3_HW_AUTO, 1,
+                                   &opl3)) < 0) {
+            snd_printk("no OPL device at 0x%lx\n", chip->port + 8);
+        }
+    } else {
+        if ((err = snd_opl3_create(card, chip->port, chip->port + 2,
+                                   OPL3_HW_AUTO, 1,
+                                   &opl3)) < 0) {
+            snd_printk("no OPL device at 0x%lx-0x%lx\n",
+                       chip->port, chip->port + 2);
+        }
+    }
+    if (err >= 0) {
+        if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
+            snd_card_free(card);
+            return err;
+        }
+    }
+
+    if ((err = snd_sb8dsp_midi(chip, 0, NULL)) < 0) {
+        snd_card_free(card);
+        return err;
+    }
+
+    strcpy(card->driver, chip->hardware == SB_HW_PRO ? "SB Pro" : "SB8");
+    strcpy(card->shortname, chip->name);
+    sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d",
+            chip->name,
+            chip->port,
+            snd_irq[dev], snd_dma8[dev]);
+    if ((err = snd_card_register(card)) < 0) {
+        snd_card_free(card);
+        return err;
+    }
+    snd_sb8_cards[dev] = card;
+    return 0;
+}
+
+static int __init snd_card_sb8_legacy_auto_probe(unsigned long port)
+{
+    static int dev = 0;
+    int res;
+
+    for ( ; dev < SNDRV_CARDS; dev++) {
+        if (!snd_enable[dev] || snd_port[dev] != SNDRV_AUTO_PORT)
+            continue;
+        snd_port[dev] = port;
+        res = snd_sb8_probe(dev);
+        if (res < 0)
+            snd_port[dev] = SNDRV_AUTO_PORT;
+        return res;
+    }
+    return -ENODEV;
+}
+
+static int __init alsa_card_sb8_init(void)
+{
+    static unsigned long possible_ports[] = {0x220, 0x240, 0x260, -1};
+    int dev, cards;
+
+    for (dev = cards = 0; dev < SNDRV_CARDS && snd_enable[dev]; dev++) {
+        if (snd_port[dev] == SNDRV_AUTO_PORT)
+            continue;
+        if (snd_sb8_probe(dev) >= 0)
+            cards++;
+    }
+    cards += snd_legacy_auto_probe(possible_ports, snd_card_sb8_legacy_auto_probe);
+    if (!cards) {
+#ifdef MODULE
+        snd_printk("Sound Blaster soundcard not found or device busy\n");
+#endif
+        return -ENODEV;
+    }
+    return 0;
+}
+
+static void __exit alsa_card_sb8_exit(void)
+{
+    int idx;
+
+    for (idx = 0; idx < SNDRV_CARDS; idx++)
+        snd_card_free(snd_sb8_cards[idx]);
+}
+
+module_init(alsa_card_sb8_init)
+module_exit(alsa_card_sb8_exit)
+
+#ifndef MODULE
+
+/* format is: snd-card-sb8=snd_enable,snd_index,snd_id,
+ snd_port,snd_irq,snd_dma8 */
+
+static int __init alsa_card_sb8_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_port[nr_dev]) == 2 &&
+           get_option(&str,&snd_irq[nr_dev]) == 2 &&
+           get_option(&str,&snd_dma8[nr_dev]) == 2);
+    nr_dev++;
+    return 1;
+}
+
+__setup("snd-card-sb8=", alsa_card_sb8_setup);
+
+#endif /* ifndef MODULE */
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/sb/sb8_main.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/sb/sb8_main.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/sb/sb8_main.c	(revision 91)
@@ -0,0 +1,651 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *                   Uros Bizjak <uros@kss-loka.si>
+ *
+ *  Routines for control of 8-bit SoundBlaster cards and clones
+ *  Please note: I don't have access to old SB8 soundcards.
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * --
+ *
+ * Thu Apr 29 20:36:17 BST 1999 George David Morrison <gdm@gedamo.demon.co.uk>
+ *   DSP can't respond to commands whilst in "high speed" mode. Caused
+ *   glitching during playback. Fixed.
+ *
+ * Wed Jul 12 22:02:55 CEST 2000 Uros Bizjak <uros@kss-loka.si>
+ *   Cleaned up and rewrote lowlevel routines.
+ */
+
+#define SNDRV_MAIN_OBJECT_FILE
+#include <sound/driver.h>
+#include <sound/sb.h>
+
+#define SB8_CLOCK	1000000
+#define SB8_DEN(v)	((SB8_CLOCK + (v) / 2) / (v))
+#define SB8_RATE(v)	(SB8_CLOCK / SB8_DEN(v))
+
+#ifdef TARGET_OS2
+static ratnum_t clock = {
+    SB8_CLOCK,
+    1,
+    256,
+    1,
+};
+
+static snd_pcm_hw_constraint_ratnums_t hw_constraints_clock = {
+    1,
+    &clock,
+};
+
+static ratnum_t stereo_clocks[] = {
+    {
+        SB8_CLOCK,
+        SB8_DEN(22050),
+        SB8_DEN(22050),
+        1,
+    },
+    {
+        SB8_CLOCK,
+        SB8_DEN(11025),
+        SB8_DEN(11025),
+        1,
+    }
+};
+#else
+static ratnum_t clock = {
+num: SB8_CLOCK,
+    den_min: 1,
+    den_max: 256,
+    den_step: 1,
+};
+
+static snd_pcm_hw_constraint_ratnums_t hw_constraints_clock = {
+nrats: 1,
+    rats: &clock,
+};
+
+static ratnum_t stereo_clocks[] = {
+    {
+    num: SB8_CLOCK,
+        den_min: SB8_DEN(22050),
+        den_max: SB8_DEN(22050),
+        den_step: 1,
+    },
+    {
+    num: SB8_CLOCK,
+        den_min: SB8_DEN(11025),
+        den_max: SB8_DEN(11025),
+        den_step: 1,
+    }
+};
+#endif
+
+static int snd_sb8_hw_constraint_rate_channels(snd_pcm_hw_params_t *params,
+                                               snd_pcm_hw_rule_t *rule)
+{
+    snd_interval_t *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+    if (c->min > 1) {
+        unsigned int num = 0, den = 0;
+        int err = snd_interval_ratnum(hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE),
+                                      2, stereo_clocks, &num, &den);
+        if (err >= 0 && den) {
+            params->rate_num = num;
+            params->rate_den = den;
+        }
+        return err;
+    }
+    return 0;
+}
+
+static int snd_sb8_hw_constraint_channels_rate(snd_pcm_hw_params_t *params,
+                                               snd_pcm_hw_rule_t *rule)
+{
+    snd_interval_t *r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+    if (r->min > SB8_RATE(22050) || r->max <= SB8_RATE(11025)) {
+#ifdef TARGET_OS2
+        snd_interval_t t = { 1, 1 };
+#else
+        snd_interval_t t = { min: 1, max: 1 };
+#endif
+        return snd_interval_refine(hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS), &t);
+    }
+    return 0;
+}
+
+static int snd_sb8_playback_prepare(snd_pcm_substream_t * substream)
+{
+    unsigned long flags;
+    sb_t *chip = snd_pcm_substream_chip(substream);
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    unsigned int mixreg, rate, size, count;
+
+    rate = runtime->rate;
+    switch (chip->hardware) {
+    case SB_HW_PRO:
+        if (runtime->channels > 1) {
+            snd_assert(rate == SB8_RATE(11025) || rate == SB8_RATE(22050), return -EINVAL);
+            chip->playback_format = SB_DSP_HI_OUTPUT_AUTO;
+            break;
+        }
+        /* fallthru */
+    case SB_HW_201:
+        if (rate > 23000) {
+            chip->playback_format = SB_DSP_HI_OUTPUT_AUTO;
+            break;
+        }
+        /* fallthru */
+    case SB_HW_20:
+        chip->playback_format = SB_DSP_LO_OUTPUT_AUTO;
+        break;
+    case SB_HW_10:
+        chip->playback_format = SB_DSP_OUTPUT;
+        break;
+    default:
+        return -EINVAL;
+    }
+    size = chip->p_dma_size = snd_pcm_lib_buffer_bytes(substream);
+    count = chip->p_period_size = snd_pcm_lib_period_bytes(substream);
+    spin_lock_irqsave(&chip->reg_lock, flags);
+    snd_sbdsp_command(chip, SB_DSP_SPEAKER_ON);
+    if (runtime->channels > 1) {
+        /* set playback stereo mode */
+        spin_lock(&chip->mixer_lock);
+        mixreg = snd_sbmixer_read(chip, SB_DSP_STEREO_SW);
+        snd_sbmixer_write(chip, SB_DSP_STEREO_SW, mixreg | 0x02);
+        spin_unlock(&chip->mixer_lock);
+
+        /* Soundblaster hardware programming reference guide, 3-23 */
+        snd_sbdsp_command(chip, SB_DSP_DMA8_EXIT);
+        runtime->dma_area[0] = 0x80;
+        snd_dma_program(chip->dma8, runtime->dma_area, 1, DMA_MODE_WRITE);
+        /* force interrupt */
+        chip->mode = SB_MODE_HALT;
+        snd_sbdsp_command(chip, SB_DSP_OUTPUT);
+        snd_sbdsp_command(chip, 0);
+        snd_sbdsp_command(chip, 0);
+    }
+    snd_sbdsp_command(chip, SB_DSP_SAMPLE_RATE);
+    if (runtime->channels > 1) {
+        snd_sbdsp_command(chip, 256 - runtime->rate_den / 2);
+        spin_lock(&chip->mixer_lock);
+        /* save output filter status and turn it off */
+        mixreg = snd_sbmixer_read(chip, SB_DSP_PLAYBACK_FILT);
+        snd_sbmixer_write(chip, SB_DSP_PLAYBACK_FILT, mixreg | 0x20);
+        spin_unlock(&chip->mixer_lock);
+        /* just use force_mode16 for temporary storate... */
+        chip->force_mode16 = mixreg;
+    } else {
+        snd_sbdsp_command(chip, 256 - runtime->rate_den);
+    }
+    if (chip->playback_format != SB_DSP_OUTPUT) {
+        count--;
+        snd_sbdsp_command(chip, SB_DSP_BLOCK_SIZE);
+        snd_sbdsp_command(chip, count & 0xff);
+        snd_sbdsp_command(chip, count >> 8);
+    }
+    spin_unlock_irqrestore(&chip->reg_lock, flags);
+    snd_dma_program(chip->dma8, runtime->dma_area,
+                    size, DMA_MODE_WRITE | DMA_AUTOINIT);
+    return 0;
+}
+
+static int snd_sb8_playback_trigger(snd_pcm_substream_t * substream,
+                                    int cmd)
+{
+    unsigned long flags;
+    sb_t *chip = snd_pcm_substream_chip(substream);
+    unsigned int count;
+
+    spin_lock_irqsave(&chip->reg_lock, flags);
+    switch (cmd) {
+    case SNDRV_PCM_TRIGGER_START:
+        snd_sbdsp_command(chip, chip->playback_format);
+        if (chip->playback_format == SB_DSP_OUTPUT) {
+            count = chip->p_period_size - 1;
+            snd_sbdsp_command(chip, count & 0xff);
+            snd_sbdsp_command(chip, count >> 8);
+        }
+        break;
+    case SNDRV_PCM_TRIGGER_STOP:
+        if (chip->playback_format == SB_DSP_HI_OUTPUT_AUTO) {
+            snd_pcm_runtime_t *runtime = substream->runtime;
+            snd_sbdsp_reset(chip);
+            if (runtime->channels > 1) {
+                spin_lock(&chip->mixer_lock);
+                /* restore output filter and set hardware to mono mode */
+                snd_sbmixer_write(chip, SB_DSP_STEREO_SW, chip->force_mode16 & ~0x02);
+                spin_unlock(&chip->mixer_lock);
+            }
+        } else {
+            snd_sbdsp_command(chip, SB_DSP_DMA8_OFF);
+        }
+        snd_sbdsp_command(chip, SB_DSP_SPEAKER_OFF);
+    }
+    spin_unlock_irqrestore(&chip->reg_lock, flags);
+    chip->mode = (cmd == SNDRV_PCM_TRIGGER_START) ? SB_MODE_PLAYBACK_8 : SB_MODE_HALT;
+    return 0;
+}
+
+static int snd_sb8_hw_params(snd_pcm_substream_t * substream,
+                             snd_pcm_hw_params_t * hw_params)
+{
+    return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+static int snd_sb8_hw_free(snd_pcm_substream_t * substream)
+{
+    snd_pcm_lib_free_pages(substream);
+    return 0;
+}
+
+static int snd_sb8_capture_prepare(snd_pcm_substream_t * substream)
+{
+    unsigned long flags;
+    sb_t *chip = snd_pcm_substream_chip(substream);
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    unsigned int mixreg, rate, size, count;
+
+    rate = runtime->rate;
+    switch (chip->hardware) {
+    case SB_HW_PRO:
+        if (runtime->channels > 1) {
+            snd_assert(rate == SB8_RATE(11025) || rate == SB8_RATE(22050), return -EINVAL);
+            chip->capture_format = SB_DSP_HI_INPUT_AUTO;
+            break;
+        }
+        chip->capture_format = (rate > 23000) ? SB_DSP_HI_INPUT_AUTO : SB_DSP_LO_INPUT_AUTO;
+        break;
+    case SB_HW_201:
+        if (rate > 13000) {
+            chip->capture_format = SB_DSP_HI_INPUT_AUTO;
+            break;
+        }
+        /* fallthru */
+    case SB_HW_20:
+        chip->capture_format = SB_DSP_LO_INPUT_AUTO;
+        break;
+    case SB_HW_10:
+        chip->capture_format = SB_DSP_INPUT;
+        break;
+    default:
+        return -EINVAL;
+    }
+    size = chip->c_dma_size = snd_pcm_lib_buffer_bytes(substream);
+    count = chip->c_period_size = snd_pcm_lib_period_bytes(substream);
+    spin_lock_irqsave(&chip->reg_lock, flags);
+    snd_sbdsp_command(chip, SB_DSP_SPEAKER_OFF);
+    if (runtime->channels > 1)
+        snd_sbdsp_command(chip, SB_DSP_STEREO_8BIT);
+    snd_sbdsp_command(chip, SB_DSP_SAMPLE_RATE);
+    if (runtime->channels > 1) {
+        snd_sbdsp_command(chip, 256 - runtime->rate_den / 2);
+        spin_lock(&chip->mixer_lock);
+        /* save input filter status and turn it off */
+        mixreg = snd_sbmixer_read(chip, SB_DSP_CAPTURE_FILT);
+        snd_sbmixer_write(chip, SB_DSP_CAPTURE_FILT, mixreg | 0x20);
+        spin_unlock(&chip->mixer_lock);
+        /* just use force_mode16 for temporary storate... */
+        chip->force_mode16 = mixreg;
+    } else {
+        snd_sbdsp_command(chip, 256 - runtime->rate_den);
+    }
+    if (chip->capture_format != SB_DSP_OUTPUT) {
+        count--;
+        snd_sbdsp_command(chip, SB_DSP_BLOCK_SIZE);
+        snd_sbdsp_command(chip, count & 0xff);
+        snd_sbdsp_command(chip, count >> 8);
+    }
+    spin_unlock_irqrestore(&chip->reg_lock, flags);
+    snd_dma_program(chip->dma8, runtime->dma_area,
+                    size, DMA_MODE_READ | DMA_AUTOINIT);
+    return 0;
+}
+
+static int snd_sb8_capture_trigger(snd_pcm_substream_t * substream,
+                                   int cmd)
+{
+    unsigned long flags;
+    sb_t *chip = snd_pcm_substream_chip(substream);
+    unsigned int count;
+
+    spin_lock_irqsave(&chip->reg_lock, flags);
+    switch (cmd) {
+    case SNDRV_PCM_TRIGGER_START:
+        snd_sbdsp_command(chip, chip->capture_format);
+        if (chip->capture_format == SB_DSP_INPUT) {
+            count = chip->c_period_size - 1;
+            snd_sbdsp_command(chip, count & 0xff);
+            snd_sbdsp_command(chip, count >> 8);
+        }
+        break;
+    case SNDRV_PCM_TRIGGER_STOP:
+        if (chip->capture_format == SB_DSP_HI_INPUT_AUTO) {
+            snd_pcm_runtime_t *runtime = substream->runtime;
+            snd_sbdsp_reset(chip);
+            if (runtime->channels > 1) {
+                /* restore input filter status */
+                spin_lock(&chip->mixer_lock);
+                snd_sbmixer_write(chip, SB_DSP_CAPTURE_FILT, chip->force_mode16);
+                spin_unlock(&chip->mixer_lock);
+                /* set hardware to mono mode */
+                snd_sbdsp_command(chip, SB_DSP_MONO_8BIT);
+            }
+        } else {
+            snd_sbdsp_command(chip, SB_DSP_DMA8_OFF);
+        }
+        snd_sbdsp_command(chip, SB_DSP_SPEAKER_OFF);
+    }
+    spin_unlock_irqrestore(&chip->reg_lock, flags);
+    chip->mode = (cmd == SNDRV_PCM_TRIGGER_START) ? SB_MODE_CAPTURE_8 : SB_MODE_HALT;
+    return 0;
+}
+
+void snd_sb8dsp_interrupt(sb_t *chip)
+{
+    snd_pcm_substream_t *substream;
+    snd_pcm_runtime_t *runtime;
+
+#if 0
+    snd_printk("sb8: interrupt\n");
+#endif
+    snd_sb_ack_8bit(chip);
+    switch (chip->mode) {
+    case SB_MODE_PLAYBACK_8:	/* ok.. playback is active */
+        substream = chip->playback_substream;
+        runtime = substream->runtime;
+        if (chip->playback_format == SB_DSP_OUTPUT)
+            snd_sb8_playback_trigger(substream, SNDRV_PCM_TRIGGER_START);
+        snd_pcm_period_elapsed(substream);
+        break;
+    case SB_MODE_CAPTURE_8:
+        substream = chip->capture_substream;
+        runtime = substream->runtime;
+        if (chip->capture_format == SB_DSP_INPUT)
+            snd_sb8_capture_trigger(substream, SNDRV_PCM_TRIGGER_START);
+        snd_pcm_period_elapsed(substream);
+        break;
+    }
+}
+
+static snd_pcm_uframes_t snd_sb8_playback_pointer(snd_pcm_substream_t * substream)
+{
+    sb_t *chip = snd_pcm_substream_chip(substream);
+    size_t ptr;
+
+    if (chip->mode != SB_MODE_PLAYBACK_8)
+        return 0;
+    ptr = chip->p_dma_size - snd_dma_residue(chip->dma8);
+    return bytes_to_frames(substream->runtime, ptr);
+}
+
+static snd_pcm_uframes_t snd_sb8_capture_pointer(snd_pcm_substream_t * substream)
+{
+    sb_t *chip = snd_pcm_substream_chip(substream);
+    size_t ptr;
+
+    if (chip->mode != SB_MODE_CAPTURE_8)
+        return 0;
+    ptr = chip->c_dma_size - snd_dma_residue(chip->dma8);
+    return bytes_to_frames(substream->runtime, ptr);
+}
+
+/*
+
+*/
+
+#ifdef TARGET_OS2
+static snd_pcm_hardware_t snd_sb8_playback =
+{
+    /*	info:		  */	(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+                                 SNDRV_PCM_INFO_MMAP_VALID),
+                                 /*	formats:	  */	SNDRV_PCM_FMTBIT_U8,
+                                 /*	rates:		  */	(SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000 |
+                                                                 SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_22050),
+                                                                 /*	rate_min:	  */	4000,
+                                                                 /*	rate_max:	  */	23000,
+                                                                 /*	channels_min:	  */	1,
+                                                                 /*	channels_max:	  */	1,
+                                                                 /*	buffer_bytes_max:  */	65536,
+                                                                 /*	period_bytes_min:  */	64,
+                                                                 /*	period_bytes_max:  */	65536,
+                                                                 /*	periods_min:	  */	1,
+                                                                 /*	periods_max:	  */	1024,
+                                                                 /*	fifo_size:	  */	0,
+};
+
+static snd_pcm_hardware_t snd_sb8_capture =
+{
+    /*	info:		  */	(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+                                 SNDRV_PCM_INFO_MMAP_VALID),
+                                 /*	formats:	  */	SNDRV_PCM_FMTBIT_U8,
+                                 /*	rates:		  */	(SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000 |
+                                                                 SNDRV_PCM_RATE_11025),
+                                                                 /*	rate_min:	  */	4000,
+                                                                 /*	rate_max:	  */	13000,
+                                                                 /*	channels_min:	  */	1,
+                                                                 /*	channels_max:	  */	1,
+                                                                 /*	buffer_bytes_max:  */	65536,
+                                                                 /*	period_bytes_min:  */	64,
+                                                                 /*	period_bytes_max:  */	65536,
+                                                                 /*	periods_min:	  */	1,
+                                                                 /*	periods_max:	  */	1024,
+                                                                 /*	fifo_size:	  */	0,
+};
+#else
+static snd_pcm_hardware_t snd_sb8_playback =
+{
+info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+                         SNDRV_PCM_INFO_MMAP_VALID),
+    formats:		SNDRV_PCM_FMTBIT_U8,
+    rates:			(SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000 |
+                                 SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_22050),
+    rate_min:		4000,
+    rate_max:		23000,
+    channels_min:		1,
+    channels_max:		1,
+    buffer_bytes_max:	65536,
+    period_bytes_min:	64,
+    period_bytes_max:	65536,
+    periods_min:		1,
+    periods_max:		1024,
+    fifo_size:		0,
+};
+
+static snd_pcm_hardware_t snd_sb8_capture =
+{
+info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+                         SNDRV_PCM_INFO_MMAP_VALID),
+    formats:		SNDRV_PCM_FMTBIT_U8,
+    rates:			(SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000 |
+                                 SNDRV_PCM_RATE_11025),
+    rate_min:		4000,
+    rate_max:		13000,
+    channels_min:		1,
+    channels_max:		1,
+    buffer_bytes_max:	65536,
+    period_bytes_min:	64,
+    period_bytes_max:	65536,
+    periods_min:		1,
+    periods_max:		1024,
+    fifo_size:		0,
+};
+#endif
+
+/*
+ *
+ */
+
+int snd_sb8_open(snd_pcm_substream_t *substream)
+{
+    sb_t *chip = snd_pcm_substream_chip(substream);
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    unsigned long flags;
+
+    spin_lock_irqsave(&chip->open_lock, flags);
+    if (chip->open) {
+        spin_unlock_irqrestore(&chip->open_lock, flags);
+        return -EAGAIN;
+    }
+    chip->open |= SB_OPEN_PCM;
+    spin_unlock_irqrestore(&chip->open_lock, flags);
+    if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+        chip->playback_substream = substream;
+        runtime->hw = snd_sb8_playback;
+    } else {
+        chip->capture_substream = substream;
+        runtime->hw = snd_sb8_capture;
+    }
+    switch (chip->hardware) {
+    case SB_HW_PRO:
+        runtime->hw.rate_max = 44100;
+        runtime->hw.channels_max = 2;
+        snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+                            snd_sb8_hw_constraint_rate_channels, 0,
+                            SNDRV_PCM_HW_PARAM_CHANNELS,
+                            SNDRV_PCM_HW_PARAM_RATE, -1);
+        snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+                            snd_sb8_hw_constraint_channels_rate, 0,
+                            SNDRV_PCM_HW_PARAM_RATE, -1);
+        break;
+    case SB_HW_201:
+        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+            runtime->hw.rate_max = 44100;
+        } else {
+            runtime->hw.rate_max = 15000;
+        }
+    default:
+        break;
+    }
+    snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+                                  &hw_constraints_clock);
+    return 0;
+}
+
+int snd_sb8_close(snd_pcm_substream_t *substream)
+{
+    unsigned long flags;
+    sb_t *chip = snd_pcm_substream_chip(substream);
+
+    chip->playback_substream = NULL;
+    chip->capture_substream = NULL;
+    spin_lock_irqsave(&chip->open_lock, flags);
+    chip->open &= ~SB_OPEN_PCM;
+    spin_unlock_irqrestore(&chip->open_lock, flags);
+    return 0;
+}
+
+/*
+ *  Initialization part
+ */
+
+#ifdef TARGET_OS2
+static snd_pcm_ops_t snd_sb8_playback_ops = {
+    snd_sb8_open,
+    snd_sb8_close,
+    snd_pcm_lib_ioctl,
+    snd_sb8_hw_params,
+    snd_sb8_hw_free,
+    snd_sb8_playback_prepare,
+    snd_sb8_playback_trigger,
+    snd_sb8_playback_pointer,0,0
+};
+
+static snd_pcm_ops_t snd_sb8_capture_ops = {
+    snd_sb8_open,
+    snd_sb8_close,
+    snd_pcm_lib_ioctl,
+    snd_sb8_hw_params,
+    snd_sb8_hw_free,
+    snd_sb8_capture_prepare,
+    snd_sb8_capture_trigger,
+    snd_sb8_capture_pointer,0,0
+};
+#else
+static snd_pcm_ops_t snd_sb8_playback_ops = {
+open:			snd_sb8_open,
+    close:			snd_sb8_close,
+    ioctl:			snd_pcm_lib_ioctl,
+    hw_params:		snd_sb8_hw_params,
+    hw_free:		snd_sb8_hw_free,
+    prepare:		snd_sb8_playback_prepare,
+    trigger:		snd_sb8_playback_trigger,
+    pointer:		snd_sb8_playback_pointer,
+};
+
+static snd_pcm_ops_t snd_sb8_capture_ops = {
+open:			snd_sb8_open,
+    close:			snd_sb8_close,
+    ioctl:			snd_pcm_lib_ioctl,
+    hw_params:		snd_sb8_hw_params,
+    hw_free:		snd_sb8_hw_free,
+    prepare:		snd_sb8_capture_prepare,
+    trigger:		snd_sb8_capture_trigger,
+    pointer:		snd_sb8_capture_pointer,
+};
+#endif
+
+static void snd_sb8dsp_pcm_free(snd_pcm_t *pcm)
+{
+    snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+int snd_sb8dsp_pcm(sb_t *chip, int device, snd_pcm_t ** rpcm)
+{
+    snd_card_t *card = chip->card;
+    snd_pcm_t *pcm;
+    int err;
+
+    if (rpcm)
+        *rpcm = NULL;
+    if ((err = snd_pcm_new(card, "SB8 DSP", device, 1, 1, &pcm)) < 0)
+        return err;
+    sprintf(pcm->name, "DSP v%i.%i", chip->version >> 8, chip->version & 0xff);
+    pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX;
+    pcm->private_data = chip;
+    pcm->private_free = snd_sb8dsp_pcm_free;
+
+    snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_sb8_playback_ops);
+    snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_sb8_capture_ops);
+
+    snd_pcm_lib_preallocate_pages_for_all(pcm, 64*1024, 64*1024, GFP_KERNEL|GFP_DMA);
+
+    if (rpcm)
+        *rpcm = pcm;
+    return 0;
+}
+
+EXPORT_SYMBOL(snd_sb8dsp_pcm);
+EXPORT_SYMBOL(snd_sb8dsp_interrupt);
+/* sb8_midi.c */
+EXPORT_SYMBOL(snd_sb8dsp_midi_interrupt);
+EXPORT_SYMBOL(snd_sb8dsp_midi);
+
+/*
+ *  INIT part
+ */
+
+static int __init alsa_sb8_init(void)
+{
+    return 0;
+}
+
+static void __exit alsa_sb8_exit(void)
+{
+}
+
+module_init(alsa_sb8_init)
+module_exit(alsa_sb8_exit)
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/sb/sb8_midi.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/sb/sb8_midi.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/sb/sb8_midi.c	(revision 91)
@@ -0,0 +1,274 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  Routines for control of SoundBlaster cards - MIDI interface
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * --
+ *
+ * Sun May  9 22:54:38 BST 1999 George David Morrison <gdm@gedamo.demon.co.uk>
+ *   Fixed typo in snd_sb8dsp_midi_new_device which prevented midi from
+ *   working.
+ */
+
+#include <sound/driver.h>
+#include <sound/sb.h>
+
+/*
+
+*/
+
+void snd_sb8dsp_midi_interrupt(sb_t * chip)
+{
+    snd_rawmidi_t *rmidi;
+    int max = 64;
+    char byte;
+
+    if (chip == NULL || (rmidi = chip->rmidi) == NULL) {
+        inb(SBP(chip, READ));	/* ack interrupt */
+        return;
+    }
+    while (max-- > 0) {
+        spin_lock(&chip->midi_input_lock);
+        if (inb(SBP(chip, DATA_AVAIL)) & 0x80) {
+            byte = inb(SBP(chip, READ));
+            spin_unlock(&chip->midi_input_lock);
+            snd_rawmidi_receive(chip->midi_substream_input, &byte, 1);
+        } else {
+            spin_unlock(&chip->midi_input_lock);
+        }
+    }
+}
+
+/*
+
+*/
+
+static int snd_sb8dsp_midi_input_open(snd_rawmidi_substream_t * substream)
+{
+    unsigned long flags;
+    sb_t *chip;
+
+    chip = snd_magic_cast(sb_t, substream->rmidi->private_data, return -ENXIO);
+    spin_lock_irqsave(&chip->open_lock, flags);
+    if (chip->open) {
+        spin_unlock_irqrestore(&chip->open_lock, flags);
+        return -EAGAIN;
+    }
+    chip->open |= SB_OPEN_MIDI_INPUT;
+    chip->midi_substream_input = substream;
+    if (!(chip->open & SB_OPEN_MIDI_OUTPUT)) {
+        spin_unlock_irqrestore(&chip->open_lock, flags);
+        snd_sbdsp_reset(chip);		/* reset DSP */
+    } else {
+        spin_unlock_irqrestore(&chip->open_lock, flags);
+    }
+    return 0;
+}
+
+static int snd_sb8dsp_midi_output_open(snd_rawmidi_substream_t * substream)
+{
+    unsigned long flags;
+    sb_t *chip;
+
+    chip = substream->rmidi->private_data;
+    spin_lock_irqsave(&chip->open_lock, flags);
+    if (chip->open) {
+        spin_unlock_irqrestore(&chip->open_lock, flags);
+        return -EAGAIN;
+    }
+    chip->open |= SB_OPEN_MIDI_OUTPUT;
+    chip->midi_substream_output = substream;
+    if (!(chip->open & SB_OPEN_MIDI_INPUT)) {
+        spin_unlock_irqrestore(&chip->open_lock, flags);
+        snd_sbdsp_reset(chip);		/* reset DSP */
+    } else {
+        spin_unlock_irqrestore(&chip->open_lock, flags);
+    }
+    return 0;
+}
+
+static int snd_sb8dsp_midi_input_close(snd_rawmidi_substream_t * substream)
+{
+    unsigned long flags;
+    sb_t *chip;
+
+    chip = substream->rmidi->private_data;
+    spin_lock_irqsave(&chip->open_lock, flags);
+    chip->open &= ~(SB_OPEN_MIDI_INPUT | SB_OPEN_MIDI_TRIGGER);
+    chip->midi_substream_input = NULL;
+    if (!(chip->open & SB_OPEN_MIDI_OUTPUT)) {
+        spin_unlock_irqrestore(&chip->open_lock, flags);
+        snd_sbdsp_reset(chip);		/* reset DSP */
+    } else {
+        spin_unlock_irqrestore(&chip->open_lock, flags);
+    }
+    return 0;
+}
+
+static int snd_sb8dsp_midi_output_close(snd_rawmidi_substream_t * substream)
+{
+    unsigned long flags;
+    sb_t *chip;
+
+    chip = substream->rmidi->private_data;
+    spin_lock_irqsave(&chip->open_lock, flags);
+    chip->open &= ~SB_OPEN_MIDI_OUTPUT;
+    chip->midi_substream_output = NULL;
+    if (!(chip->open & SB_OPEN_MIDI_INPUT)) {
+        spin_unlock_irqrestore(&chip->open_lock, flags);
+        snd_sbdsp_reset(chip);		/* reset DSP */
+    } else {
+        spin_unlock_irqrestore(&chip->open_lock, flags);
+    }
+    return 0;
+}
+
+static void snd_sb8dsp_midi_input_trigger(snd_rawmidi_substream_t * substream, int up)
+{
+    unsigned long flags;
+    sb_t *chip;
+
+    chip = substream->rmidi->private_data;
+    spin_lock_irqsave(&chip->open_lock, flags);
+    if (up) {
+        if (!(chip->open & SB_OPEN_MIDI_TRIGGER)) {
+            snd_sbdsp_command(chip, SB_DSP_MIDI_INPUT_IRQ);
+            chip->open |= SB_OPEN_MIDI_TRIGGER;
+        }
+    } else {
+        if (chip->open & SB_OPEN_MIDI_TRIGGER) {
+            snd_sbdsp_command(chip, SB_DSP_MIDI_INPUT_IRQ);
+            chip->open &= ~SB_OPEN_MIDI_TRIGGER;
+        }
+    }
+    spin_unlock_irqrestore(&chip->open_lock, flags);
+}
+
+static void snd_sb8dsp_midi_output_write(snd_rawmidi_substream_t * substream)
+{
+    unsigned long flags;
+    sb_t *chip;
+    char byte;
+    int max = 32;
+
+    /* how big is Tx FIFO? */
+    chip = substream->rmidi->private_data;
+    while (max-- > 0) {
+        spin_lock_irqsave(&chip->open_lock, flags);
+        if (snd_rawmidi_transmit(substream, &byte, 1) != 1) {
+            chip->open &= ~SB_OPEN_MIDI_TRIGGER;
+            del_timer(&chip->midi_timer);
+            spin_unlock_irqrestore(&chip->open_lock, flags);
+            return;
+        }
+        snd_sbdsp_command(chip, SB_DSP_MIDI_OUTPUT);
+        snd_sbdsp_command(chip, byte);
+        spin_unlock_irqrestore(&chip->open_lock, flags);
+    }
+}
+
+static void snd_sb8dsp_midi_output_timer(unsigned long data)
+{
+    snd_rawmidi_substream_t * substream = (snd_rawmidi_substream_t *) data;
+    sb_t * chip = substream->rmidi->private_data;
+    unsigned long flags;
+
+    spin_lock_irqsave(&chip->open_lock, flags);
+    chip->midi_timer.expires = 1 + jiffies;
+    add_timer(&chip->midi_timer);
+    spin_unlock_irqrestore(&chip->open_lock, flags);
+    snd_sb8dsp_midi_output_write(substream);
+}
+
+static void snd_sb8dsp_midi_output_trigger(snd_rawmidi_substream_t * substream, int up)
+{
+    unsigned long flags;
+    sb_t *chip;
+
+    chip = substream->rmidi->private_data;
+    spin_lock_irqsave(&chip->open_lock, flags);
+    if (up) {
+        if (!(chip->open & SB_OPEN_MIDI_TRIGGER)) {
+            chip->midi_timer.function = snd_sb8dsp_midi_output_timer;
+            chip->midi_timer.data = (unsigned long) substream;
+            chip->midi_timer.expires = 1 + jiffies;
+            add_timer(&chip->midi_timer);
+            chip->open |= SB_OPEN_MIDI_TRIGGER;
+        }
+    } else {
+        if (chip->open & SB_OPEN_MIDI_TRIGGER) {
+            chip->open &= ~SB_OPEN_MIDI_TRIGGER;
+        }
+    }
+    spin_unlock_irqrestore(&chip->open_lock, flags);
+
+    if (up)
+        snd_sb8dsp_midi_output_write(substream);
+}
+
+/*
+
+*/
+
+#ifdef TARGET_OS2
+static snd_rawmidi_ops_t snd_sb8dsp_midi_output =
+{
+    snd_sb8dsp_midi_output_open,
+    snd_sb8dsp_midi_output_close,
+    snd_sb8dsp_midi_output_trigger,0
+};
+
+static snd_rawmidi_ops_t snd_sb8dsp_midi_input =
+{
+    snd_sb8dsp_midi_input_open,
+    snd_sb8dsp_midi_input_close,
+    snd_sb8dsp_midi_input_trigger,0
+};
+#else
+static snd_rawmidi_ops_t snd_sb8dsp_midi_output =
+{
+open:           snd_sb8dsp_midi_output_open,
+    close:          snd_sb8dsp_midi_output_close,
+    trigger:	snd_sb8dsp_midi_output_trigger,
+};
+
+static snd_rawmidi_ops_t snd_sb8dsp_midi_input =
+{
+open:           snd_sb8dsp_midi_input_open,
+    close:          snd_sb8dsp_midi_input_close,
+    trigger:        snd_sb8dsp_midi_input_trigger,
+};
+#endif
+
+int snd_sb8dsp_midi(sb_t *chip, int device, snd_rawmidi_t ** rrawmidi)
+{
+    snd_rawmidi_t *rmidi;
+    int err;
+
+    if (rrawmidi)
+        *rrawmidi = NULL;
+    if ((err = snd_rawmidi_new(chip->card, "SB8 MIDI", device, 1, 1, &rmidi)) < 0)
+        return err;
+    strcpy(rmidi->name, "SB8 MIDI");
+    snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_sb8dsp_midi_output);
+    snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_sb8dsp_midi_input);
+    rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX;
+    rmidi->private_data = chip;
+    chip->rmidi = rmidi;
+    if (rrawmidi)
+        *rrawmidi = rmidi;
+    return 0;
+}
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/sb/sb_common.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/sb/sb_common.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/sb/sb_common.c	(revision 91)
@@ -0,0 +1,292 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *                   Uros Bizjak <uros@kss-loka.si>
+ *
+ *  Lowlevel routines for control of Sound Blaster cards
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#define SNDRV_MAIN_OBJECT_FILE
+#include <sound/driver.h>
+#include <sound/sb.h>
+#include <sound/initval.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("ALSA lowlevel driver for Sound Blaster cards");
+MODULE_CLASSES("{sound}");
+
+#define BUSY_LOOPS 100000
+
+#undef IO_DEBUG
+
+int snd_sbdsp_command(struct snd_sb *chip, unsigned char val)
+{
+    int i;
+#ifdef IO_DEBUG
+    snd_printk("command 0x%x\n", val);
+#endif
+    for (i = BUSY_LOOPS; i; i--)
+        if ((inb(SBP(chip, STATUS)) & 0x80) == 0) {
+            outb(val, SBP(chip, COMMAND));
+            return 1;
+        }
+    snd_printd(__FUNCTION__ " [0x%lx]: timeout (0x%x)\n", chip->port, val);
+    return 0;
+}
+
+int snd_sbdsp_get_byte(struct snd_sb *chip)
+{
+    int val;
+    int i;
+    for (i = BUSY_LOOPS; i; i--) {
+        if (inb(SBP(chip, DATA_AVAIL)) & 0x80) {
+            val = inb(SBP(chip, READ));
+#ifdef IO_DEBUG
+            snd_printk("get_byte 0x%x\n", val);
+#endif
+            return val;
+        }
+    }
+    snd_printd(__FUNCTION__ " [0x%lx]: timeout\n", chip->port);
+    return -ENODEV;
+}
+
+int snd_sbdsp_reset(struct snd_sb *chip)
+{
+    int i;
+
+    outb(1, SBP(chip, RESET));
+    udelay(10);
+    outb(0, SBP(chip, RESET));
+    udelay(30);
+    for (i = BUSY_LOOPS; i; i--)
+        if (inb(SBP(chip, DATA_AVAIL)) & 0x80) {
+            if (inb(SBP(chip, READ)) == 0xaa)
+                return 0;
+            else
+                break;
+        }
+    snd_printdd(__FUNCTION__ " [0x%lx] failed...\n", chip->port);
+    return -ENODEV;
+}
+
+int snd_sbdsp_version(struct snd_sb * chip)
+{
+    unsigned int result = -ENODEV;
+
+    snd_sbdsp_command(chip, SB_DSP_GET_VERSION);
+    result = (short) snd_sbdsp_get_byte(chip) << 8;
+    result |= (short) snd_sbdsp_get_byte(chip);
+    return result;
+}
+
+static int snd_sbdsp_probe(struct snd_sb * chip)
+{
+    int version;
+    int major, minor;
+    char *str;
+    unsigned long flags;
+
+    /*
+     *  initialization sequence
+     */
+
+    spin_lock_irqsave(&chip->reg_lock, flags);
+    if (snd_sbdsp_reset(chip) < 0) {
+        spin_unlock_irqrestore(&chip->reg_lock, flags);
+        return -ENODEV;
+    }
+    version = snd_sbdsp_version(chip);
+    if (version < 0) {
+        spin_unlock_irqrestore(&chip->reg_lock, flags);
+        return -ENODEV;
+    }
+    spin_unlock_irqrestore(&chip->reg_lock, flags);
+    major = version >> 8;
+    minor = version & 0xff;
+    snd_printdd("SB [0x%lx]: DSP chip found, version = %i.%i\n",
+                chip->port, major, minor);
+
+    switch (chip->hardware) {
+    case SB_HW_AUTO:
+        switch (major) {
+        case 1:
+            chip->hardware = SB_HW_10;
+            str = "1.0";
+            break;
+        case 2:
+            if (minor) {
+                chip->hardware = SB_HW_201;
+                str = "2.01+";
+            } else {
+                chip->hardware = SB_HW_20;
+                str = "2.0";
+            }
+            break;
+        case 3:
+            chip->hardware = SB_HW_PRO;
+            str = "Pro";
+            break;
+        case 4:
+            chip->hardware = SB_HW_16;
+            str = "16";
+            break;
+        default:
+            snd_printk("SB [0x%lx]: unknown DSP chip version %i.%i\n",
+                       chip->port, major, minor);
+            return -ENODEV;
+        }
+        break;
+    case SB_HW_ALS100:
+        str = "16 (ALS-100)";
+        break;
+    case SB_HW_ALS4000:
+        str = "16 (ALS-4000)";
+        break;
+    default:
+        return -ENODEV;
+    }
+    sprintf(chip->name, "Sound Blaster %s", str);
+    chip->version = (major << 8) | minor;
+    return 0;
+}
+
+static int snd_sbdsp_free(struct snd_sb *chip)
+{
+    if (chip->res_port)
+        release_resource(chip->res_port);
+    if (chip->irq >= 0)
+        free_irq(chip->irq, (void *) chip);
+    if (chip->dma8 >= 0) {
+        disable_dma(chip->dma8);
+        free_dma(chip->dma8);
+    }
+    if (chip->dma16 >= 0) {
+        disable_dma(chip->dma16);
+        free_dma(chip->dma16);
+    }
+    kfree(chip);
+    return 0;
+}
+
+static int snd_sbdsp_dev_free(snd_device_t *device)
+{
+    struct snd_sb *chip = device->device_data;
+    return snd_sbdsp_free(chip);
+}
+
+int snd_sbdsp_create(snd_card_t *card,
+                     unsigned long port,
+                     int irq,
+                     irqreturn_t (*irq_handler)(int, void *, struct pt_regs *),
+                     int dma8,
+                     int dma16,
+                     unsigned short hardware,
+                     struct snd_sb **r_chip)
+{
+    struct snd_sb *chip;
+    int err;
+#ifdef TARGET_OS2
+    static snd_device_ops_t ops = {
+        snd_sbdsp_dev_free,0,0
+    };
+#else
+    static snd_device_ops_t ops = {
+    dev_free:       snd_sbdsp_dev_free,
+    };
+#endif
+
+    snd_assert(r_chip != NULL, return -EINVAL);
+    *r_chip = NULL;
+    chip = kcalloc(1, sizeof(*chip), GFP_KERNEL);
+    if (chip == NULL)
+        return -ENOMEM;
+    spin_lock_init(&chip->reg_lock);
+    spin_lock_init(&chip->open_lock);
+    spin_lock_init(&chip->midi_input_lock);
+    spin_lock_init(&chip->mixer_lock);
+    chip->irq = -1;
+    chip->dma8 = -1;
+    chip->dma16 = -1;
+    chip->port = port;
+
+    if (request_irq(irq, irq_handler, hardware == SB_HW_ALS4000 ?
+                    SA_INTERRUPT | SA_SHIRQ : SA_INTERRUPT,
+                    "SoundBlaster", (void *) chip)) {
+        snd_sbdsp_free(chip);
+        return -EBUSY;
+    }
+    chip->irq = irq;
+
+    if (hardware == SB_HW_ALS4000)
+        goto __skip_allocation;
+
+    if ((chip->res_port = request_region(port, 16, "SoundBlaster")) == NULL) {
+        snd_sbdsp_free(chip);
+        return -EBUSY;
+    }
+
+    if (dma8 >= 0 && request_dma(dma8, "SoundBlaster - 8bit")) {
+        snd_sbdsp_free(chip);
+        return -EBUSY;
+    }
+    chip->dma8 = dma8;
+    if (dma16 >= 0 && request_dma(dma16, "SoundBlaster - 16bit")) {
+        snd_sbdsp_free(chip);
+        return -EBUSY;
+    }
+    chip->dma16 = dma16;
+
+__skip_allocation:
+    chip->card = card;
+    chip->hardware = hardware;
+    if ((err = snd_sbdsp_probe(chip)) < 0) {
+        snd_sbdsp_free(chip);
+        return err;
+    }
+    if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+        snd_sbdsp_free(chip);
+        return err;
+    }
+    *r_chip = chip;
+    return 0;
+}
+
+EXPORT_SYMBOL(snd_sbdsp_command);
+EXPORT_SYMBOL(snd_sbdsp_get_byte);
+EXPORT_SYMBOL(snd_sbdsp_reset);
+EXPORT_SYMBOL(snd_sbdsp_create);
+/* sb_mixer.c */
+EXPORT_SYMBOL(snd_sbmixer_write);
+EXPORT_SYMBOL(snd_sbmixer_read);
+EXPORT_SYMBOL(snd_sbmixer_new);
+
+/*
+ *  INIT part
+ */
+
+static int __init alsa_sb_common_init(void)
+{
+    return 0;
+}
+
+static void __exit alsa_sb_common_exit(void)
+{
+}
+
+module_init(alsa_sb_common_init)
+module_exit(alsa_sb_common_exit)
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/sb/sb_mixer.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/sb/sb_mixer.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/sb/sb_mixer.c	(revision 91)
@@ -0,0 +1,992 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  Routines for Sound Blaster mixer control
+ *
+ *
+ *   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/time.h>
+#include <sound/core.h>
+#include <sound/sb.h>
+#include <sound/control.h>
+
+#undef IO_DEBUG
+
+void snd_sbmixer_write(struct snd_sb *chip, unsigned char reg, unsigned char data)
+{
+	outb(reg, SBP(chip, MIXER_ADDR));
+	udelay(10);
+	outb(data, SBP(chip, MIXER_DATA));
+	udelay(10);
+#ifdef IO_DEBUG
+	snd_printk(KERN_DEBUG "mixer_write 0x%x 0x%x\n", reg, data);
+#endif
+}
+
+unsigned char snd_sbmixer_read(struct snd_sb *chip, unsigned char reg)
+{
+	unsigned char result;
+
+	outb(reg, SBP(chip, MIXER_ADDR));
+	udelay(10);
+	result = inb(SBP(chip, MIXER_DATA));
+	udelay(10);
+#ifdef IO_DEBUG
+	snd_printk(KERN_DEBUG "mixer_read 0x%x 0x%x\n", reg, result);
+#endif
+	return result;
+}
+
+/*
+ * Single channel mixer element
+ */
+
+static int snd_sbmixer_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+
+	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	return 0;
+}
+
+static int snd_sbmixer_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 16) & 0xff;
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+	unsigned char val;
+
+	spin_lock_irqsave(&sb->mixer_lock, flags);
+	val = (snd_sbmixer_read(sb, reg) >> shift) & mask;
+	spin_unlock_irqrestore(&sb->mixer_lock, flags);
+	ucontrol->value.integer.value[0] = val;
+	return 0;
+}
+
+static int snd_sbmixer_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 16) & 0x07;
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+	int change;
+	unsigned char val, oval;
+
+	val = (ucontrol->value.integer.value[0] & mask) << shift;
+	spin_lock_irqsave(&sb->mixer_lock, flags);
+	oval = snd_sbmixer_read(sb, reg);
+	val = (oval & ~(mask << shift)) | val;
+	change = val != oval;
+	if (change)
+		snd_sbmixer_write(sb, reg, val);
+	spin_unlock_irqrestore(&sb->mixer_lock, flags);
+	return change;
+}
+
+/*
+ * Double channel mixer element
+ */
+
+static int snd_sbmixer_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+
+	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	return 0;
+}
+
+static int snd_sbmixer_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int left_reg = kcontrol->private_value & 0xff;
+	int right_reg = (kcontrol->private_value >> 8) & 0xff;
+	int left_shift = (kcontrol->private_value >> 16) & 0x07;
+	int right_shift = (kcontrol->private_value >> 19) & 0x07;
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+	unsigned char left, right;
+
+	spin_lock_irqsave(&sb->mixer_lock, flags);
+	left = (snd_sbmixer_read(sb, left_reg) >> left_shift) & mask;
+	right = (snd_sbmixer_read(sb, right_reg) >> right_shift) & mask;
+	spin_unlock_irqrestore(&sb->mixer_lock, flags);
+	ucontrol->value.integer.value[0] = left;
+	ucontrol->value.integer.value[1] = right;
+	return 0;
+}
+
+static int snd_sbmixer_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int left_reg = kcontrol->private_value & 0xff;
+	int right_reg = (kcontrol->private_value >> 8) & 0xff;
+	int left_shift = (kcontrol->private_value >> 16) & 0x07;
+	int right_shift = (kcontrol->private_value >> 19) & 0x07;
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+	int change;
+	unsigned char left, right, oleft, oright;
+
+	left = (ucontrol->value.integer.value[0] & mask) << left_shift;
+	right = (ucontrol->value.integer.value[1] & mask) << right_shift;
+	spin_lock_irqsave(&sb->mixer_lock, flags);
+	if (left_reg == right_reg) {
+		oleft = snd_sbmixer_read(sb, left_reg);
+		left = (oleft & ~((mask << left_shift) | (mask << right_shift))) | left | right;
+		change = left != oleft;
+		if (change)
+			snd_sbmixer_write(sb, left_reg, left);
+	} else {
+		oleft = snd_sbmixer_read(sb, left_reg);
+		oright = snd_sbmixer_read(sb, right_reg);
+		left = (oleft & ~(mask << left_shift)) | left;
+		right = (oright & ~(mask << right_shift)) | right;
+		change = left != oleft || right != oright;
+		if (change) {
+			snd_sbmixer_write(sb, left_reg, left);
+			snd_sbmixer_write(sb, right_reg, right);
+		}
+	}
+	spin_unlock_irqrestore(&sb->mixer_lock, flags);
+	return change;
+}
+
+/*
+ * DT-019x / ALS-007 capture/input switch
+ */
+
+static int snd_dt019x_input_sw_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+	static char *texts[5] = {
+		"CD", "Mic", "Line", "Synth", "Master"
+	};
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 5;
+	if (uinfo->value.enumerated.item > 4)
+		uinfo->value.enumerated.item = 4;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int snd_dt019x_input_sw_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	unsigned char oval;
+	
+	spin_lock_irqsave(&sb->mixer_lock, flags);
+	oval = snd_sbmixer_read(sb, SB_DT019X_CAPTURE_SW);
+	spin_unlock_irqrestore(&sb->mixer_lock, flags);
+	switch (oval & 0x07) {
+	case SB_DT019X_CAP_CD:
+		ucontrol->value.enumerated.item[0] = 0;
+		break;
+	case SB_DT019X_CAP_MIC:
+		ucontrol->value.enumerated.item[0] = 1;
+		break;
+	case SB_DT019X_CAP_LINE:
+		ucontrol->value.enumerated.item[0] = 2;
+		break;
+	case SB_DT019X_CAP_MAIN:
+		ucontrol->value.enumerated.item[0] = 4;
+		break;
+	/* To record the synth on these cards you must record the main.   */
+	/* Thus SB_DT019X_CAP_SYNTH == SB_DT019X_CAP_MAIN and would cause */
+	/* duplicate case labels if left uncommented. */
+	/* case SB_DT019X_CAP_SYNTH:
+	 *	ucontrol->value.enumerated.item[0] = 3;
+	 *	break;
+	 */
+	default:
+		ucontrol->value.enumerated.item[0] = 4;
+		break;
+	}
+	return 0;
+}
+
+static int snd_dt019x_input_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int change;
+	unsigned char nval, oval;
+	
+	if (ucontrol->value.enumerated.item[0] > 4)
+		return -EINVAL;
+	switch (ucontrol->value.enumerated.item[0]) {
+	case 0:
+		nval = SB_DT019X_CAP_CD;
+		break;
+	case 1:
+		nval = SB_DT019X_CAP_MIC;
+		break;
+	case 2:
+		nval = SB_DT019X_CAP_LINE;
+		break;
+	case 3:
+		nval = SB_DT019X_CAP_SYNTH;
+		break;
+	case 4:
+		nval = SB_DT019X_CAP_MAIN;
+		break;
+	default:
+		nval = SB_DT019X_CAP_MAIN;
+	}
+	spin_lock_irqsave(&sb->mixer_lock, flags);
+	oval = snd_sbmixer_read(sb, SB_DT019X_CAPTURE_SW);
+	change = nval != oval;
+	if (change)
+		snd_sbmixer_write(sb, SB_DT019X_CAPTURE_SW, nval);
+	spin_unlock_irqrestore(&sb->mixer_lock, flags);
+	return change;
+}
+
+/*
+ * SBPRO input multiplexer
+ */
+
+static int snd_sb8mixer_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+	static char *texts[3] = {
+		"Mic", "CD", "Line"
+	};
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 3;
+	if (uinfo->value.enumerated.item > 2)
+		uinfo->value.enumerated.item = 2;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+
+static int snd_sb8mixer_get_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	unsigned char oval;
+	
+	spin_lock_irqsave(&sb->mixer_lock, flags);
+	oval = snd_sbmixer_read(sb, SB_DSP_CAPTURE_SOURCE);
+	spin_unlock_irqrestore(&sb->mixer_lock, flags);
+	switch ((oval >> 0x01) & 0x03) {
+	case SB_DSP_MIXS_CD:
+		ucontrol->value.enumerated.item[0] = 1;
+		break;
+	case SB_DSP_MIXS_LINE:
+		ucontrol->value.enumerated.item[0] = 2;
+		break;
+	default:
+		ucontrol->value.enumerated.item[0] = 0;
+		break;
+	}
+	return 0;
+}
+
+static int snd_sb8mixer_put_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int change;
+	unsigned char nval, oval;
+	
+	if (ucontrol->value.enumerated.item[0] > 2)
+		return -EINVAL;
+	switch (ucontrol->value.enumerated.item[0]) {
+	case 1:
+		nval = SB_DSP_MIXS_CD;
+		break;
+	case 2:
+		nval = SB_DSP_MIXS_LINE;
+		break;
+	default:
+		nval = SB_DSP_MIXS_MIC;
+	}
+	nval <<= 1;
+	spin_lock_irqsave(&sb->mixer_lock, flags);
+	oval = snd_sbmixer_read(sb, SB_DSP_CAPTURE_SOURCE);
+	nval |= oval & ~0x06;
+	change = nval != oval;
+	if (change)
+		snd_sbmixer_write(sb, SB_DSP_CAPTURE_SOURCE, nval);
+	spin_unlock_irqrestore(&sb->mixer_lock, flags);
+	return change;
+}
+
+/*
+ * SB16 input switch
+ */
+
+static int snd_sb16mixer_info_input_sw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = 4;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+	return 0;
+}
+
+static int snd_sb16mixer_get_input_sw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int reg1 = kcontrol->private_value & 0xff;
+	int reg2 = (kcontrol->private_value >> 8) & 0xff;
+	int left_shift = (kcontrol->private_value >> 16) & 0x0f;
+	int right_shift = (kcontrol->private_value >> 24) & 0x0f;
+	unsigned char val1, val2;
+
+	spin_lock_irqsave(&sb->mixer_lock, flags);
+	val1 = snd_sbmixer_read(sb, reg1);
+	val2 = snd_sbmixer_read(sb, reg2);
+	spin_unlock_irqrestore(&sb->mixer_lock, flags);
+	ucontrol->value.integer.value[0] = (val1 >> left_shift) & 0x01;
+	ucontrol->value.integer.value[1] = (val2 >> left_shift) & 0x01;
+	ucontrol->value.integer.value[2] = (val1 >> right_shift) & 0x01;
+	ucontrol->value.integer.value[3] = (val2 >> right_shift) & 0x01;
+	return 0;
+}                                                                                                                   
+
+static int snd_sb16mixer_put_input_sw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int reg1 = kcontrol->private_value & 0xff;
+	int reg2 = (kcontrol->private_value >> 8) & 0xff;
+	int left_shift = (kcontrol->private_value >> 16) & 0x0f;
+	int right_shift = (kcontrol->private_value >> 24) & 0x0f;
+	int change;
+	unsigned char val1, val2, oval1, oval2;
+
+	spin_lock_irqsave(&sb->mixer_lock, flags);
+	oval1 = snd_sbmixer_read(sb, reg1);
+	oval2 = snd_sbmixer_read(sb, reg2);
+	val1 = oval1 & ~((1 << left_shift) | (1 << right_shift));
+	val2 = oval2 & ~((1 << left_shift) | (1 << right_shift));
+	val1 |= (ucontrol->value.integer.value[0] & 1) << left_shift;
+	val2 |= (ucontrol->value.integer.value[1] & 1) << left_shift;
+	val1 |= (ucontrol->value.integer.value[2] & 1) << right_shift;
+	val2 |= (ucontrol->value.integer.value[3] & 1) << right_shift;
+	change = val1 != oval1 || val2 != oval2;
+	if (change) {
+		snd_sbmixer_write(sb, reg1, val1);
+		snd_sbmixer_write(sb, reg2, val2);
+	}
+	spin_unlock_irqrestore(&sb->mixer_lock, flags);
+	return change;
+}
+
+
+/*
+ */
+/*
+ */
+int snd_sbmixer_add_ctl(struct snd_sb *chip, const char *name, int index, int type, unsigned long value)
+{
+	static struct snd_kcontrol_new newctls[] = {
+		[SB_MIX_SINGLE] = {
+			.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+			.info = snd_sbmixer_info_single,
+			.get = snd_sbmixer_get_single,
+			.put = snd_sbmixer_put_single,
+		},
+		[SB_MIX_DOUBLE] = {
+			.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+			.info = snd_sbmixer_info_double,
+			.get = snd_sbmixer_get_double,
+			.put = snd_sbmixer_put_double,
+		},
+		[SB_MIX_INPUT_SW] = {
+			.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+			.info = snd_sb16mixer_info_input_sw,
+			.get = snd_sb16mixer_get_input_sw,
+			.put = snd_sb16mixer_put_input_sw,
+		},
+		[SB_MIX_CAPTURE_PRO] = {
+			.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+			.info = snd_sb8mixer_info_mux,
+			.get = snd_sb8mixer_get_mux,
+			.put = snd_sb8mixer_put_mux,
+		},
+		[SB_MIX_CAPTURE_DT019X] = {
+			.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+			.info = snd_dt019x_input_sw_info,
+			.get = snd_dt019x_input_sw_get,
+			.put = snd_dt019x_input_sw_put,
+		},
+	};
+	struct snd_kcontrol *ctl;
+	int err;
+
+	ctl = snd_ctl_new1(&newctls[type], chip);
+	if (! ctl)
+		return -ENOMEM;
+	strlcpy(ctl->id.name, name, sizeof(ctl->id.name));
+	ctl->id.index = index;
+	ctl->private_value = value;
+	if ((err = snd_ctl_add(chip->card, ctl)) < 0) {
+		snd_ctl_free_one(ctl);
+		return err;
+	}
+	return 0;
+}
+
+/*
+ * SB 2.0 specific mixer elements
+ */
+
+static struct sbmix_elem snd_sb20_ctl_master_play_vol =
+	SB_SINGLE("Master Playback Volume", SB_DSP20_MASTER_DEV, 1, 7);
+static struct sbmix_elem snd_sb20_ctl_pcm_play_vol =
+	SB_SINGLE("PCM Playback Volume", SB_DSP20_PCM_DEV, 1, 3);
+static struct sbmix_elem snd_sb20_ctl_synth_play_vol =
+	SB_SINGLE("Synth Playback Volume", SB_DSP20_FM_DEV, 1, 7);
+static struct sbmix_elem snd_sb20_ctl_cd_play_vol =
+	SB_SINGLE("CD Playback Volume", SB_DSP20_CD_DEV, 1, 7);
+
+static struct sbmix_elem *snd_sb20_controls[] = {
+	&snd_sb20_ctl_master_play_vol,
+	&snd_sb20_ctl_pcm_play_vol,
+	&snd_sb20_ctl_synth_play_vol,
+	&snd_sb20_ctl_cd_play_vol
+};
+
+static unsigned char snd_sb20_init_values[][2] = {
+	{ SB_DSP20_MASTER_DEV, 0 },
+	{ SB_DSP20_FM_DEV, 0 },
+};
+
+/*
+ * SB Pro specific mixer elements
+ */
+static struct sbmix_elem snd_sbpro_ctl_master_play_vol =
+	SB_DOUBLE("Master Playback Volume", SB_DSP_MASTER_DEV, SB_DSP_MASTER_DEV, 5, 1, 7);
+static struct sbmix_elem snd_sbpro_ctl_pcm_play_vol =
+	SB_DOUBLE("PCM Playback Volume", SB_DSP_PCM_DEV, SB_DSP_PCM_DEV, 5, 1, 7);
+static struct sbmix_elem snd_sbpro_ctl_pcm_play_filter =
+	SB_SINGLE("PCM Playback Filter", SB_DSP_PLAYBACK_FILT, 5, 1);
+static struct sbmix_elem snd_sbpro_ctl_synth_play_vol =
+	SB_DOUBLE("Synth Playback Volume", SB_DSP_FM_DEV, SB_DSP_FM_DEV, 5, 1, 7);
+static struct sbmix_elem snd_sbpro_ctl_cd_play_vol =
+	SB_DOUBLE("CD Playback Volume", SB_DSP_CD_DEV, SB_DSP_CD_DEV, 5, 1, 7);
+static struct sbmix_elem snd_sbpro_ctl_line_play_vol =
+	SB_DOUBLE("Line Playback Volume", SB_DSP_LINE_DEV, SB_DSP_LINE_DEV, 5, 1, 7);
+static struct sbmix_elem snd_sbpro_ctl_mic_play_vol =
+	SB_SINGLE("Mic Playback Volume", SB_DSP_MIC_DEV, 1, 3);
+static struct sbmix_elem snd_sbpro_ctl_capture_source =
+	{
+		.name = "Capture Source",
+		.type = SB_MIX_CAPTURE_PRO
+	};
+static struct sbmix_elem snd_sbpro_ctl_capture_filter =
+	SB_SINGLE("Capture Filter", SB_DSP_CAPTURE_FILT, 5, 1);
+static struct sbmix_elem snd_sbpro_ctl_capture_low_filter =
+	SB_SINGLE("Capture Low-Pass Filter", SB_DSP_CAPTURE_FILT, 3, 1);
+
+static struct sbmix_elem *snd_sbpro_controls[] = {
+	&snd_sbpro_ctl_master_play_vol,
+	&snd_sbpro_ctl_pcm_play_vol,
+	&snd_sbpro_ctl_pcm_play_filter,
+	&snd_sbpro_ctl_synth_play_vol,
+	&snd_sbpro_ctl_cd_play_vol,
+	&snd_sbpro_ctl_line_play_vol,
+	&snd_sbpro_ctl_mic_play_vol,
+	&snd_sbpro_ctl_capture_source,
+	&snd_sbpro_ctl_capture_filter,
+	&snd_sbpro_ctl_capture_low_filter
+};
+
+static unsigned char snd_sbpro_init_values[][2] = {
+	{ SB_DSP_MASTER_DEV, 0 },
+	{ SB_DSP_PCM_DEV, 0 },
+	{ SB_DSP_FM_DEV, 0 },
+};
+
+/*
+ * SB16 specific mixer elements
+ */
+static struct sbmix_elem snd_sb16_ctl_master_play_vol =
+	SB_DOUBLE("Master Playback Volume", SB_DSP4_MASTER_DEV, (SB_DSP4_MASTER_DEV + 1), 3, 3, 31);
+static struct sbmix_elem snd_sb16_ctl_3d_enhance_switch =
+	SB_SINGLE("3D Enhancement Switch", SB_DSP4_3DSE, 0, 1);
+static struct sbmix_elem snd_sb16_ctl_tone_bass =
+	SB_DOUBLE("Tone Control - Bass", SB_DSP4_BASS_DEV, (SB_DSP4_BASS_DEV + 1), 4, 4, 15);
+static struct sbmix_elem snd_sb16_ctl_tone_treble =
+	SB_DOUBLE("Tone Control - Treble", SB_DSP4_TREBLE_DEV, (SB_DSP4_TREBLE_DEV + 1), 4, 4, 15);
+static struct sbmix_elem snd_sb16_ctl_pcm_play_vol =
+	SB_DOUBLE("PCM Playback Volume", SB_DSP4_PCM_DEV, (SB_DSP4_PCM_DEV + 1), 3, 3, 31);
+static struct sbmix_elem snd_sb16_ctl_synth_capture_route =
+	SB16_INPUT_SW("Synth Capture Route", SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 6, 5);
+static struct sbmix_elem snd_sb16_ctl_synth_play_vol =
+	SB_DOUBLE("Synth Playback Volume", SB_DSP4_SYNTH_DEV, (SB_DSP4_SYNTH_DEV + 1), 3, 3, 31);
+static struct sbmix_elem snd_sb16_ctl_cd_capture_route =
+	SB16_INPUT_SW("CD Capture Route", SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 2, 1);
+static struct sbmix_elem snd_sb16_ctl_cd_play_switch =
+	SB_DOUBLE("CD Playback Switch", SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, 2, 1, 1);
+static struct sbmix_elem snd_sb16_ctl_cd_play_vol =
+	SB_DOUBLE("CD Playback Volume", SB_DSP4_CD_DEV, (SB_DSP4_CD_DEV + 1), 3, 3, 31);
+static struct sbmix_elem snd_sb16_ctl_line_capture_route =
+	SB16_INPUT_SW("Line Capture Route", SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 4, 3);
+static struct sbmix_elem snd_sb16_ctl_line_play_switch =
+	SB_DOUBLE("Line Playback Switch", SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, 4, 3, 1);
+static struct sbmix_elem snd_sb16_ctl_line_play_vol =
+	SB_DOUBLE("Line Playback Volume", SB_DSP4_LINE_DEV, (SB_DSP4_LINE_DEV + 1), 3, 3, 31);
+static struct sbmix_elem snd_sb16_ctl_mic_capture_route =
+	SB16_INPUT_SW("Mic Capture Route", SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 0, 0);
+static struct sbmix_elem snd_sb16_ctl_mic_play_switch =
+	SB_SINGLE("Mic Playback Switch", SB_DSP4_OUTPUT_SW, 0, 1);
+static struct sbmix_elem snd_sb16_ctl_mic_play_vol =
+	SB_SINGLE("Mic Playback Volume", SB_DSP4_MIC_DEV, 3, 31);
+static struct sbmix_elem snd_sb16_ctl_pc_speaker_vol =
+	SB_SINGLE("PC Speaker Volume", SB_DSP4_SPEAKER_DEV, 6, 3);
+static struct sbmix_elem snd_sb16_ctl_capture_vol =
+	SB_DOUBLE("Capture Volume", SB_DSP4_IGAIN_DEV, (SB_DSP4_IGAIN_DEV + 1), 6, 6, 3);
+static struct sbmix_elem snd_sb16_ctl_play_vol =
+	SB_DOUBLE("Playback Volume", SB_DSP4_OGAIN_DEV, (SB_DSP4_OGAIN_DEV + 1), 6, 6, 3);
+static struct sbmix_elem snd_sb16_ctl_auto_mic_gain =
+	SB_SINGLE("Mic Auto Gain", SB_DSP4_MIC_AGC, 0, 1);
+
+static struct sbmix_elem *snd_sb16_controls[] = {
+	&snd_sb16_ctl_master_play_vol,
+	&snd_sb16_ctl_3d_enhance_switch,
+	&snd_sb16_ctl_tone_bass,
+	&snd_sb16_ctl_tone_treble,
+	&snd_sb16_ctl_pcm_play_vol,
+	&snd_sb16_ctl_synth_capture_route,
+	&snd_sb16_ctl_synth_play_vol,
+	&snd_sb16_ctl_cd_capture_route,
+	&snd_sb16_ctl_cd_play_switch,
+	&snd_sb16_ctl_cd_play_vol,
+	&snd_sb16_ctl_line_capture_route,
+	&snd_sb16_ctl_line_play_switch,
+	&snd_sb16_ctl_line_play_vol,
+	&snd_sb16_ctl_mic_capture_route,
+	&snd_sb16_ctl_mic_play_switch,
+	&snd_sb16_ctl_mic_play_vol,
+	&snd_sb16_ctl_pc_speaker_vol,
+	&snd_sb16_ctl_capture_vol,
+	&snd_sb16_ctl_play_vol,
+	&snd_sb16_ctl_auto_mic_gain
+};
+
+static unsigned char snd_sb16_init_values[][2] = {
+	{ SB_DSP4_MASTER_DEV + 0, 0 },
+	{ SB_DSP4_MASTER_DEV + 1, 0 },
+	{ SB_DSP4_PCM_DEV + 0, 0 },
+	{ SB_DSP4_PCM_DEV + 1, 0 },
+	{ SB_DSP4_SYNTH_DEV + 0, 0 },
+	{ SB_DSP4_SYNTH_DEV + 1, 0 },
+	{ SB_DSP4_INPUT_LEFT, 0 },
+	{ SB_DSP4_INPUT_RIGHT, 0 },
+	{ SB_DSP4_OUTPUT_SW, 0 },
+	{ SB_DSP4_SPEAKER_DEV, 0 },
+};
+
+/*
+ * DT019x specific mixer elements
+ */
+static struct sbmix_elem snd_dt019x_ctl_master_play_vol =
+	SB_DOUBLE("Master Playback Volume", SB_DT019X_MASTER_DEV, SB_DT019X_MASTER_DEV, 4,0, 15);
+static struct sbmix_elem snd_dt019x_ctl_pcm_play_vol =
+	SB_DOUBLE("PCM Playback Volume", SB_DT019X_PCM_DEV, SB_DT019X_PCM_DEV, 4,0, 15);
+static struct sbmix_elem snd_dt019x_ctl_synth_play_vol =
+	SB_DOUBLE("Synth Playback Volume", SB_DT019X_SYNTH_DEV, SB_DT019X_SYNTH_DEV, 4,0, 15);
+static struct sbmix_elem snd_dt019x_ctl_cd_play_vol =
+	SB_DOUBLE("CD Playback Volume", SB_DT019X_CD_DEV, SB_DT019X_CD_DEV, 4,0, 15);
+static struct sbmix_elem snd_dt019x_ctl_mic_play_vol =
+	SB_SINGLE("Mic Playback Volume", SB_DT019X_MIC_DEV, 4, 7);
+static struct sbmix_elem snd_dt019x_ctl_pc_speaker_vol =
+	SB_SINGLE("PC Speaker Volume", SB_DT019X_SPKR_DEV, 0,  7);
+static struct sbmix_elem snd_dt019x_ctl_line_play_vol =
+	SB_DOUBLE("Line Playback Volume", SB_DT019X_LINE_DEV, SB_DT019X_LINE_DEV, 4,0, 15);
+static struct sbmix_elem snd_dt019x_ctl_pcm_play_switch =
+	SB_DOUBLE("PCM Playback Switch", SB_DT019X_OUTPUT_SW2, SB_DT019X_OUTPUT_SW2, 2,1, 1);
+static struct sbmix_elem snd_dt019x_ctl_synth_play_switch =
+	SB_DOUBLE("Synth Playback Switch", SB_DT019X_OUTPUT_SW2, SB_DT019X_OUTPUT_SW2, 4,3, 1);
+static struct sbmix_elem snd_dt019x_ctl_capture_source =
+	{
+		.name = "Capture Source",
+		.type = SB_MIX_CAPTURE_DT019X
+	};
+
+static struct sbmix_elem *snd_dt019x_controls[] = {
+	&snd_dt019x_ctl_master_play_vol,
+	&snd_dt019x_ctl_pcm_play_vol,
+	&snd_dt019x_ctl_synth_play_vol,
+	&snd_dt019x_ctl_cd_play_vol,
+	&snd_dt019x_ctl_mic_play_vol,
+	&snd_dt019x_ctl_pc_speaker_vol,
+	&snd_dt019x_ctl_line_play_vol,
+	&snd_sb16_ctl_mic_play_switch,
+	&snd_sb16_ctl_cd_play_switch,
+	&snd_sb16_ctl_line_play_switch,
+	&snd_dt019x_ctl_pcm_play_switch,
+	&snd_dt019x_ctl_synth_play_switch,
+	&snd_dt019x_ctl_capture_source
+};
+
+static unsigned char snd_dt019x_init_values[][2] = {
+        { SB_DT019X_MASTER_DEV, 0 },
+        { SB_DT019X_PCM_DEV, 0 },
+        { SB_DT019X_SYNTH_DEV, 0 },
+        { SB_DT019X_CD_DEV, 0 },
+        { SB_DT019X_MIC_DEV, 0 },	/* Includes PC-speaker in high nibble */
+        { SB_DT019X_LINE_DEV, 0 },
+        { SB_DSP4_OUTPUT_SW, 0 },
+        { SB_DT019X_OUTPUT_SW2, 0 },
+        { SB_DT019X_CAPTURE_SW, 0x06 },
+};
+
+/*
+ * ALS4000 specific mixer elements
+ */
+/* FIXME: SB_ALS4000_MONO_IO_CTRL needs output select ctrl! */
+static struct sbmix_elem snd_als4000_ctl_master_mono_playback_switch =
+	SB_SINGLE("Master Mono Playback Switch", SB_ALS4000_MONO_IO_CTRL, 5, 1);
+static struct sbmix_elem snd_als4000_ctl_master_mono_capture_route =
+	SB_SINGLE("Master Mono Capture Route", SB_ALS4000_MONO_IO_CTRL, 6, 0x03);
+/* FIXME: mono playback switch also available on DT019X? */
+static struct sbmix_elem snd_als4000_ctl_mono_playback_switch =
+	SB_SINGLE("Mono Playback Switch", SB_DT019X_OUTPUT_SW2, 0, 1);
+static struct sbmix_elem snd_als4000_ctl_mic_20db_boost =
+	SB_SINGLE("Mic Boost (+20dB)", SB_ALS4000_MIC_IN_GAIN, 0, 0x03);
+static struct sbmix_elem snd_als4000_ctl_mixer_loopback =
+	SB_SINGLE("Analog Loopback", SB_ALS4000_MIC_IN_GAIN, 7, 0x01);
+/* FIXME: functionality of 3D controls might be swapped, I didn't find
+ * a description of how to identify what is supposed to be what */
+static struct sbmix_elem snd_als4000_3d_control_switch =
+	SB_SINGLE("3D Control - Switch", SB_ALS4000_3D_SND_FX, 6, 0x01);
+static struct sbmix_elem snd_als4000_3d_control_ratio =
+	SB_SINGLE("3D Control - Level", SB_ALS4000_3D_SND_FX, 0, 0x07);
+static struct sbmix_elem snd_als4000_3d_control_freq =
+	/* FIXME: maybe there's actually some standard 3D ctrl name for it?? */
+	SB_SINGLE("3D Control - Freq", SB_ALS4000_3D_SND_FX, 4, 0x03);
+static struct sbmix_elem snd_als4000_3d_control_delay =
+	/* FIXME: ALS4000a.pdf mentions BBD (Bucket Brigade Device) time delay,
+	 * but what ALSA 3D attribute is that actually? "Center", "Depth",
+	 * "Wide" or "Space" or even "Level"? Assuming "Wide" for now... */
+	SB_SINGLE("3D Control - Wide", SB_ALS4000_3D_TIME_DELAY, 0, 0x0f);
+static struct sbmix_elem snd_als4000_3d_control_poweroff_switch =
+	SB_SINGLE("3D PowerOff Switch", SB_ALS4000_3D_TIME_DELAY, 4, 0x01);
+#ifdef NOT_AVAILABLE
+static struct sbmix_elem snd_als4000_ctl_fmdac =
+	SB_SINGLE("FMDAC Switch (Option ?)", SB_ALS4000_FMDAC, 0, 0x01);
+static struct sbmix_elem snd_als4000_ctl_qsound =
+	SB_SINGLE("QSound Mode", SB_ALS4000_QSOUND, 1, 0x1f);
+#endif
+
+static struct sbmix_elem *snd_als4000_controls[] = {
+	&snd_sb16_ctl_master_play_vol,
+	&snd_dt019x_ctl_pcm_play_switch,
+	&snd_sb16_ctl_pcm_play_vol,
+	&snd_sb16_ctl_synth_capture_route,
+	&snd_dt019x_ctl_synth_play_switch,
+	&snd_sb16_ctl_synth_play_vol,
+	&snd_sb16_ctl_cd_capture_route,
+	&snd_sb16_ctl_cd_play_switch,
+	&snd_sb16_ctl_cd_play_vol,
+	&snd_sb16_ctl_line_capture_route,
+	&snd_sb16_ctl_line_play_switch,
+	&snd_sb16_ctl_line_play_vol,
+	&snd_sb16_ctl_mic_capture_route,
+	&snd_als4000_ctl_mic_20db_boost,
+	&snd_sb16_ctl_auto_mic_gain,
+	&snd_sb16_ctl_mic_play_switch,
+	&snd_sb16_ctl_mic_play_vol,
+	&snd_sb16_ctl_pc_speaker_vol,
+	&snd_sb16_ctl_capture_vol,
+	&snd_sb16_ctl_play_vol,
+	&snd_als4000_ctl_master_mono_playback_switch,
+	&snd_als4000_ctl_master_mono_capture_route,
+	&snd_als4000_ctl_mono_playback_switch,
+	&snd_als4000_ctl_mixer_loopback,
+	&snd_als4000_3d_control_switch,
+	&snd_als4000_3d_control_ratio,
+	&snd_als4000_3d_control_freq,
+	&snd_als4000_3d_control_delay,
+	&snd_als4000_3d_control_poweroff_switch,
+#ifdef NOT_AVAILABLE
+	&snd_als4000_ctl_fmdac,
+	&snd_als4000_ctl_qsound,
+#endif
+};
+
+static unsigned char snd_als4000_init_values[][2] = {
+	{ SB_DSP4_MASTER_DEV + 0, 0 },
+	{ SB_DSP4_MASTER_DEV + 1, 0 },
+	{ SB_DSP4_PCM_DEV + 0, 0 },
+	{ SB_DSP4_PCM_DEV + 1, 0 },
+	{ SB_DSP4_SYNTH_DEV + 0, 0 },
+	{ SB_DSP4_SYNTH_DEV + 1, 0 },
+	{ SB_DSP4_SPEAKER_DEV, 0 },
+	{ SB_DSP4_OUTPUT_SW, 0 },
+	{ SB_DSP4_INPUT_LEFT, 0 },
+	{ SB_DSP4_INPUT_RIGHT, 0 },
+	{ SB_DT019X_OUTPUT_SW2, 0 },
+	{ SB_ALS4000_MIC_IN_GAIN, 0 },
+};
+
+
+/*
+ */
+static int snd_sbmixer_init(struct snd_sb *chip,
+			    struct sbmix_elem **controls,
+			    int controls_count,
+			    unsigned char map[][2],
+			    int map_count,
+			    char *name)
+{
+	unsigned long flags;
+	struct snd_card *card = chip->card;
+	int idx, err;
+
+	/* mixer reset */
+	spin_lock_irqsave(&chip->mixer_lock, flags);
+	snd_sbmixer_write(chip, 0x00, 0x00);
+	spin_unlock_irqrestore(&chip->mixer_lock, flags);
+
+	/* mute and zero volume channels */
+	for (idx = 0; idx < map_count; idx++) {
+		spin_lock_irqsave(&chip->mixer_lock, flags);
+		snd_sbmixer_write(chip, map[idx][0], map[idx][1]);
+		spin_unlock_irqrestore(&chip->mixer_lock, flags);
+	}
+
+	for (idx = 0; idx < controls_count; idx++) {
+		if ((err = snd_sbmixer_add_ctl_elem(chip, controls[idx])) < 0)
+			return err;
+	}
+	snd_component_add(card, name);
+	strcpy(card->mixername, name);
+	return 0;
+}
+
+int snd_sbmixer_new(struct snd_sb *chip)
+{
+	struct snd_card *card;
+	int err;
+
+	snd_assert(chip != NULL && chip->card != NULL, return -EINVAL);
+
+	card = chip->card;
+
+	switch (chip->hardware) {
+	case SB_HW_10:
+		return 0; /* no mixer chip on SB1.x */
+	case SB_HW_20:
+	case SB_HW_201:
+		if ((err = snd_sbmixer_init(chip,
+					    snd_sb20_controls,
+					    ARRAY_SIZE(snd_sb20_controls),
+					    snd_sb20_init_values,
+					    ARRAY_SIZE(snd_sb20_init_values),
+					    "CTL1335")) < 0)
+			return err;
+		break;
+	case SB_HW_PRO:
+		if ((err = snd_sbmixer_init(chip,
+					    snd_sbpro_controls,
+					    ARRAY_SIZE(snd_sbpro_controls),
+					    snd_sbpro_init_values,
+					    ARRAY_SIZE(snd_sbpro_init_values),
+					    "CTL1345")) < 0)
+			return err;
+		break;
+	case SB_HW_16:
+	case SB_HW_ALS100:
+		if ((err = snd_sbmixer_init(chip,
+					    snd_sb16_controls,
+					    ARRAY_SIZE(snd_sb16_controls),
+					    snd_sb16_init_values,
+					    ARRAY_SIZE(snd_sb16_init_values),
+					    "CTL1745")) < 0)
+			return err;
+		break;
+	case SB_HW_ALS4000:
+		if ((err = snd_sbmixer_init(chip,
+					    snd_als4000_controls,
+					    ARRAY_SIZE(snd_als4000_controls),
+					    snd_als4000_init_values,
+					    ARRAY_SIZE(snd_als4000_init_values),
+					    "ALS4000")) < 0)
+			return err;
+		break;
+	case SB_HW_DT019X:
+		if ((err = snd_sbmixer_init(chip,
+					    snd_dt019x_controls,
+					    ARRAY_SIZE(snd_dt019x_controls),
+					    snd_dt019x_init_values,
+					    ARRAY_SIZE(snd_dt019x_init_values),
+					    "DT019X")) < 0)
+		break;
+	default:
+		strcpy(card->mixername, "???");
+	}
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static unsigned char sb20_saved_regs[] = {
+	SB_DSP20_MASTER_DEV,
+	SB_DSP20_PCM_DEV,
+	SB_DSP20_FM_DEV,
+	SB_DSP20_CD_DEV,
+};
+
+static unsigned char sbpro_saved_regs[] = {
+	SB_DSP_MASTER_DEV,
+	SB_DSP_PCM_DEV,
+	SB_DSP_PLAYBACK_FILT,
+	SB_DSP_FM_DEV,
+	SB_DSP_CD_DEV,
+	SB_DSP_LINE_DEV,
+	SB_DSP_MIC_DEV,
+	SB_DSP_CAPTURE_SOURCE,
+	SB_DSP_CAPTURE_FILT,
+};
+
+static unsigned char sb16_saved_regs[] = {
+	SB_DSP4_MASTER_DEV, SB_DSP4_MASTER_DEV + 1,
+	SB_DSP4_3DSE,
+	SB_DSP4_BASS_DEV, SB_DSP4_BASS_DEV + 1,
+	SB_DSP4_TREBLE_DEV, SB_DSP4_TREBLE_DEV + 1,
+	SB_DSP4_PCM_DEV, SB_DSP4_PCM_DEV + 1,
+	SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT,
+	SB_DSP4_SYNTH_DEV, SB_DSP4_SYNTH_DEV + 1,
+	SB_DSP4_OUTPUT_SW,
+	SB_DSP4_CD_DEV, SB_DSP4_CD_DEV + 1,
+	SB_DSP4_LINE_DEV, SB_DSP4_LINE_DEV + 1,
+	SB_DSP4_MIC_DEV,
+	SB_DSP4_SPEAKER_DEV,
+	SB_DSP4_IGAIN_DEV, SB_DSP4_IGAIN_DEV + 1,
+	SB_DSP4_OGAIN_DEV, SB_DSP4_OGAIN_DEV + 1,
+	SB_DSP4_MIC_AGC
+};
+
+static unsigned char dt019x_saved_regs[] = {
+	SB_DT019X_MASTER_DEV,
+	SB_DT019X_PCM_DEV,
+	SB_DT019X_SYNTH_DEV,
+	SB_DT019X_CD_DEV,
+	SB_DT019X_MIC_DEV,
+	SB_DT019X_SPKR_DEV,
+	SB_DT019X_LINE_DEV,
+	SB_DSP4_OUTPUT_SW,
+	SB_DT019X_OUTPUT_SW2,
+	SB_DT019X_CAPTURE_SW,
+};
+
+static unsigned char als4000_saved_regs[] = {
+	SB_DSP4_MASTER_DEV, SB_DSP4_MASTER_DEV + 1,
+	SB_DSP4_OUTPUT_SW,
+	SB_DSP4_PCM_DEV, SB_DSP4_PCM_DEV + 1,
+	SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT,
+	SB_DSP4_SYNTH_DEV, SB_DSP4_SYNTH_DEV + 1,
+	SB_DSP4_CD_DEV, SB_DSP4_CD_DEV + 1,
+	SB_DSP4_MIC_AGC,
+	SB_DSP4_MIC_DEV,
+	SB_DSP4_SPEAKER_DEV,
+	SB_DSP4_IGAIN_DEV, SB_DSP4_IGAIN_DEV + 1,
+	SB_DSP4_OGAIN_DEV, SB_DSP4_OGAIN_DEV + 1,
+	SB_DT019X_OUTPUT_SW2,
+	SB_ALS4000_MONO_IO_CTRL,
+	SB_ALS4000_MIC_IN_GAIN,
+	SB_ALS4000_3D_SND_FX,
+	SB_ALS4000_3D_TIME_DELAY,
+};
+
+static void save_mixer(struct snd_sb *chip, unsigned char *regs, int num_regs)
+{
+	unsigned char *val = chip->saved_regs;
+	snd_assert(num_regs > ARRAY_SIZE(chip->saved_regs), return);
+	for (; num_regs; num_regs--)
+		*val++ = snd_sbmixer_read(chip, *regs++);
+}
+
+static void restore_mixer(struct snd_sb *chip, unsigned char *regs, int num_regs)
+{
+	unsigned char *val = chip->saved_regs;
+	snd_assert(num_regs > ARRAY_SIZE(chip->saved_regs), return);
+	for (; num_regs; num_regs--)
+		snd_sbmixer_write(chip, *regs++, *val++);
+}
+
+void snd_sbmixer_suspend(struct snd_sb *chip)
+{
+	switch (chip->hardware) {
+	case SB_HW_20:
+	case SB_HW_201:
+		save_mixer(chip, sb20_saved_regs, ARRAY_SIZE(sb20_saved_regs));
+		break;
+	case SB_HW_PRO:
+		save_mixer(chip, sbpro_saved_regs, ARRAY_SIZE(sbpro_saved_regs));
+		break;
+	case SB_HW_16:
+	case SB_HW_ALS100:
+		save_mixer(chip, sb16_saved_regs, ARRAY_SIZE(sb16_saved_regs));
+		break;
+	case SB_HW_ALS4000:
+		save_mixer(chip, als4000_saved_regs, ARRAY_SIZE(als4000_saved_regs));
+		break;
+	case SB_HW_DT019X:
+		save_mixer(chip, dt019x_saved_regs, ARRAY_SIZE(dt019x_saved_regs));
+		break;
+	default:
+		break;
+	}
+}
+
+void snd_sbmixer_resume(struct snd_sb *chip)
+{
+	switch (chip->hardware) {
+	case SB_HW_20:
+	case SB_HW_201:
+		restore_mixer(chip, sb20_saved_regs, ARRAY_SIZE(sb20_saved_regs));
+		break;
+	case SB_HW_PRO:
+		restore_mixer(chip, sbpro_saved_regs, ARRAY_SIZE(sbpro_saved_regs));
+		break;
+	case SB_HW_16:
+	case SB_HW_ALS100:
+		restore_mixer(chip, sb16_saved_regs, ARRAY_SIZE(sb16_saved_regs));
+		break;
+	case SB_HW_ALS4000:
+		restore_mixer(chip, als4000_saved_regs, ARRAY_SIZE(als4000_saved_regs));
+		break;
+	case SB_HW_DT019X:
+		restore_mixer(chip, dt019x_saved_regs, ARRAY_SIZE(dt019x_saved_regs));
+		break;
+	default:
+		break;
+	}
+}
+#endif
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/sb/sbawe.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/sb/sbawe.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/sb/sbawe.c	(revision 91)
@@ -0,0 +1,2 @@
+#define SNDRV_SBAWE
+#include "sb16.c"
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/sgalaxy.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/sgalaxy.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/sgalaxy.c	(revision 91)
@@ -0,0 +1,345 @@
+/*
+ *  Driver for Aztech Sound Galaxy cards
+ *  Copyright (c) by Christopher Butler <chrisb@sandy.force9.co.uk.
+ *
+ *  I don't have documentation for this card, I based this driver on the
+ *  driver for OSS/Free included in the kernel source (drivers/sound/sgalaxy.c)
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#define SNDRV_MAIN_OBJECT_FILE
+#include <sound/driver.h>
+#include <sound/sb.h>
+#include <sound/ad1848.h>
+#define SNDRV_LEGACY_FIND_FREE_IRQ
+#define SNDRV_LEGACY_FIND_FREE_DMA
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+EXPORT_NO_SYMBOLS;
+MODULE_DESCRIPTION("Aztech Sound Galaxy");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{Aztech Systems,Sound Galaxy}}");
+
+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;	/* Enable this card */
+static long snd_sbport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* 0x220,0x240 */
+static long snd_wssport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* 0x530,0xe80,0xf40,0x604 */
+static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* 7,9,10,11 */
+static int snd_dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* 0,1,3 */
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for Sound Galaxy soundcard.");
+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 Sound Galaxy soundcard.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_sbport, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_sbport, "Port # for Sound Galaxy SB driver.");
+MODULE_PARM_SYNTAX(snd_sbport, SNDRV_ENABLED ",allows:{{0x220},{0x240}},dialog:list");
+MODULE_PARM(snd_wssport, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_wssport, "Port # for Sound Galaxy WSS driver.");
+MODULE_PARM_SYNTAX(snd_wssport, SNDRV_ENABLED ",allows:{{0x530},{0xe80},{0xf40},{0x604}},dialog:list");
+MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_irq, "IRQ # for Sound Galaxy driver.");
+MODULE_PARM_SYNTAX(snd_irq, SNDRV_ENABLED ",allows:{{7},{9},{10},{11}},dialog:list");
+MODULE_PARM(snd_dma1, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_dma1, "DMA1 # for Sound Galaxy driver.");
+MODULE_PARM_SYNTAX(snd_dma1, SNDRV_DMA8_DESC);
+
+#define SGALAXY_AUXC_LEFT 18
+#define SGALAXY_AUXC_RIGHT 19
+
+static snd_card_t *snd_sgalaxy_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+
+/*
+
+ */
+
+#define AD1848P1( port, x ) ( port + c_d_c_AD1848##x )
+
+/* from lowlevel/sb/sb.c - to avoid having to allocate a sb_t for the */
+/* short time we actually need it.. */
+
+static int snd_sgalaxy_sbdsp_reset(unsigned long port)
+{
+	int i;
+
+	outb(1, SBP1(port, RESET));
+	udelay(10);
+	outb(0, SBP1(port, RESET));
+	udelay(30);
+	for (i = 0; i < 1000 && !(inb(SBP1(port, DATA_AVAIL)) & 0x80); i++);
+	if (inb(SBP1(port, READ)) != 0xaa) {
+		snd_printd("sb_reset: failed at 0x%lx!!!\n", port);
+		return -ENODEV;
+	}
+	return 0;
+}
+
+static int __init snd_sgalaxy_sbdsp_command(unsigned long port, unsigned char val)
+{
+	int i;
+       	
+	for (i = 10000; i; i--)
+		if ((inb(SBP1(port, STATUS)) & 0x80) == 0) {
+			outb(val, SBP1(port, COMMAND));
+			return 1;
+		}
+
+	return 0;
+}
+
+static int __init snd_sgalaxy_setup_wss(unsigned long port, int irq, int dma)
+{
+	static int interrupt_bits[] = {-1, -1, -1, -1, -1, -1, -1, 0x08, -1, 
+				       0x10, 0x18, 0x20, -1, -1, -1, -1};
+	static int dma_bits[] = {1, 2, 0, 3};
+	int tmp, tmp1;
+
+	unsigned int flags;
+
+	if ((tmp = inb(port + 3)) == 0xff)
+	{
+		snd_printdd("I/O address dead (0x%lx)\n", tmp);
+		return 0;
+	}
+#if 0
+	snd_printdd("WSS signature = 0x%x\n", tmp);
+#endif
+
+        if ((tmp & 0x3f) != 0x04 &&
+            (tmp & 0x3f) != 0x0f &&
+            (tmp & 0x3f) != 0x00) {
+		snd_printdd("No WSS signature detected on port 0x%lx\n",
+			    port + 3);
+		return 0;
+	}
+
+#if 0
+	snd_printdd("sgalaxy - setting up IRQ/DMA for WSS\n");
+#endif
+
+	save_flags(flags);
+	cli();
+
+        /* initialize IRQ for WSS codec */
+        tmp = interrupt_bits[irq % 16];
+        if (tmp < 0) {
+		restore_flags(flags);
+                return -EINVAL;
+	}
+        outb(tmp | 0x40, port);
+        tmp1 = dma_bits[dma % 4];
+        outb(tmp | tmp1, port);
+
+	restore_flags(flags);
+	return 0;
+}
+
+static int __init snd_sgalaxy_detect(int dev, int irq, int dma)
+{
+#if 0
+	snd_printdd("sgalaxy - switching to WSS mode\n");
+#endif
+
+	/* switch to WSS mode */
+	snd_sgalaxy_sbdsp_reset(snd_sbport[dev]);
+
+	snd_sgalaxy_sbdsp_command(snd_sbport[dev], 9);
+	snd_sgalaxy_sbdsp_command(snd_sbport[dev], 0);
+
+	udelay(400);
+	return snd_sgalaxy_setup_wss(snd_wssport[dev], irq, dma);
+}
+
+#define SGALAXY_CONTROLS 2
+
+static snd_kcontrol_new_t snd_sgalaxy_controls[2] = {
+AD1848_DOUBLE("Aux Playback Switch", 0, SGALAXY_AUXC_LEFT, SGALAXY_AUXC_RIGHT, 7, 7, 1, 1),
+AD1848_DOUBLE("Aux Playback Volume", 0, SGALAXY_AUXC_LEFT, SGALAXY_AUXC_RIGHT, 0, 0, 31, 0)
+};
+
+static int __init snd_sgalaxy_mixer(ad1848_t *chip)
+{
+	snd_card_t *card = chip->card;
+	snd_ctl_elem_id_t id1, id2;
+	int idx, err;
+
+	memset(&id1, 0, sizeof(id1));
+	memset(&id2, 0, sizeof(id2));
+	id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+	/* reassign AUX0 to LINE */
+	strcpy(id1.name, "Aux Playback Switch");
+	strcpy(id2.name, "Line Playback Switch");
+	if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
+		return err;
+	strcpy(id1.name, "Aux Playback Volume");
+	strcpy(id2.name, "Line Playback Volume");
+	if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
+		return err;
+	/* reassign AUX1 to FM */
+	strcpy(id1.name, "Aux Playback Switch"); id1.index = 1;
+	strcpy(id2.name, "FM Playback Switch");
+	if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
+		return err;
+	strcpy(id1.name, "Aux Playback Volume");
+	strcpy(id2.name, "FM Playback Volume");
+	if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
+		return err;
+	/* build AUX2 input */
+	for (idx = 0; idx < SGALAXY_CONTROLS; idx++) {
+		if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_sgalaxy_controls[idx], chip))) < 0)
+			return err;
+	}
+	return 0;
+}
+
+static int __init snd_sgalaxy_probe(int dev)
+{
+	static int possible_irqs[] = {7, 9, 10, 11, -1};
+	static int possible_dmas[] = {1, 3, 0, -1};
+	int err, irq, dma1;
+	snd_card_t *card;
+	ad1848_t *chip;
+
+	if (snd_sbport[dev] == SNDRV_AUTO_PORT) {
+		snd_printk("specify SB port\n");
+		return -EINVAL;
+	}
+	if (snd_wssport[dev] == SNDRV_AUTO_PORT) {
+		snd_printk("specify WSS port\n");
+		return -EINVAL;
+	}
+	card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0);
+	if (card == NULL)
+		return -ENOMEM;
+
+	irq = snd_irq[dev];
+	if (irq == SNDRV_AUTO_IRQ) {
+		if ((irq = snd_legacy_find_free_irq(possible_irqs)) < 0) {
+			snd_card_free(card);
+			snd_printk("unable to find a free IRQ\n");
+			return -EBUSY;
+		}
+	}
+	dma1 = snd_dma1[dev];
+        if (dma1 == SNDRV_AUTO_DMA) {
+		if ((dma1 = snd_legacy_find_free_dma(possible_dmas)) < 0) {
+			snd_card_free(card);
+			snd_printk("unable to find a free DMA\n");
+			return -EBUSY;
+		}
+	}
+
+	if ((err = snd_sgalaxy_detect(dev, irq, dma1)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	if ((err = snd_ad1848_create(card, snd_wssport[dev] + 4,
+				     irq, dma1,
+				     AD1848_HW_DETECT, &chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	if ((err = snd_ad1848_pcm(chip, 0, NULL)) < 0) {
+		snd_printdd("sgalaxy - error creating new ad1848 PCM device\n");
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_ad1848_mixer(chip)) < 0) {
+		snd_printdd("sgalaxy - error creating new ad1848 mixer\n");
+		snd_card_free(card);
+		return err;
+	}
+	if (snd_sgalaxy_mixer(chip) < 0) {
+		snd_printdd("sgalaxy - the mixer rewrite failed\n");
+		snd_card_free(card);
+		return err;
+	}
+
+	strcpy(card->driver, "Sound Galaxy");
+	strcpy(card->shortname, "Sound Galaxy");
+	sprintf(card->longname, "Sound Galaxy at 0x%lx, irq %d, dma %d",
+		snd_wssport[dev], irq, dma1);
+
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	snd_sgalaxy_cards[dev] = card;
+	return 0;
+}
+
+static int __init alsa_card_sgalaxy_init(void)
+{
+	int dev, cards;
+
+	for (dev = cards = 0; dev < SNDRV_CARDS && snd_enable[dev]; dev++) {
+		if (snd_sgalaxy_probe(dev) >= 0)
+			cards++;
+	}
+	if (!cards) {
+#ifdef MODULE
+		snd_printk("Sound Galaxy soundcard not found or device busy\n");
+#endif
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static void __exit alsa_card_sgalaxy_exit(void)
+{
+	int idx;
+
+	for (idx = 0; idx < SNDRV_CARDS; idx++)
+		snd_card_free(snd_sgalaxy_cards[idx]);
+}
+
+module_init(alsa_card_sgalaxy_init)
+module_exit(alsa_card_sgalaxy_exit)
+
+#ifndef MODULE
+
+/* format is: snd-card-sgalaxy=snd_enable,snd_index,snd_id,
+			       snd_sbport,snd_wssport,
+			       snd_irq,snd_dma1 */
+
+static int __init alsa_card_sgalaxy_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_sbport[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_wssport[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_irq[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_dma1[nr_dev]) == 2);
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-card-sgalaxy=", alsa_card_sgalaxy_setup);
+
+#endif /* ifndef MODULE */
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/wavefront/makefile.os2
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/wavefront/makefile.os2	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/wavefront/makefile.os2	(revision 91)
@@ -0,0 +1,50 @@
+CINCLUDES = ..\..\..\include;..\..\include;$(%WATCOM)\H
+AINCLUDES = $(DDK_BASE)\H
+
+CDEFINES = -D__KERNEL__ -DMODULE -dTARGET_OS2 -dALSA_BUILD -D__ISAPNP__ -DCONFIG_ISAPNP -D__i386_
+
+32BIT=1
+!include ..\..\..\..\makefile.inc
+
+
+#===================================================================
+#
+#   List of source files
+#
+#===================================================================
+FILE1    = wavefront_fx.obj wavefront_synth.obj wavefront_midi.obj wavefront.obj
+FILE2    = 
+FILE3    = 
+FILE4    = 
+FILE5    = 
+FILE6    = 
+FILE7    = 
+FILE8    = 
+FILELAST =
+FILES    = $(FILE0) $(FILE1) $(FILE2) $(FILE3) $(FILE4) $(FILE5) $(FILE6) $(FILE7) $(FILE8) $(FILE9) $(FILE10) $(FILE11) $(FILE12)
+
+TARGET   = ad1816a
+
+LNKFILE  = $(OBJDIR)\$(TARGET).lnk
+
+
+#===================================================================
+#
+#   Specific dependencies
+#
+#===================================================================
+all: $(OBJDIR)\$(TARGET).lib 
+
+
+$(LNKFILE): makefile.os2
+    @%create $(LNKFILE)
+    @for %f in ($(FILES)) do @%append $(LNKFILE) -+$(OBJDIR)\%f
+
+$(OBJDIR)\$(TARGET).lib: $(LNKFILE) $(FILES)
+    $(LIB) $(OBJDIR)\$(TARGET).lib @$(LNKFILE)
+    copy $(OBJDIR)\$(TARGET).lib $(ALSA_LIB) 
+
+clean: .SYMBOLIC
+     cd $(OBJDIR)
+     -del *.obj *.err *.lnk *.sys *.sym *.lst *.map *.wmap *.lib
+     cd ..
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/wavefront/wavefront.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/wavefront/wavefront.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/wavefront/wavefront.c	(revision 91)
@@ -0,0 +1,821 @@
+/*
+ *  ALSA card-level driver for Turtle Beach Wavefront cards 
+ *                                              (Maui,Tropez,Tropez+)
+ *
+ *  Copyright (c) 1997-1999 by Paul Barton-Davis <pbd@op.net>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define SNDRV_MAIN_OBJECT_FILE
+#include <sound/driver.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+#include <sound/opl3.h>
+#include <sound/snd_wavefront.h>
+
+#define chip_t cs4231_t
+
+EXPORT_NO_SYMBOLS;
+MODULE_DESCRIPTION("Turtle Beach Wavefront");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{Turtle Beach,Maui/Tropez/Tropez+}}");
+
+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;	    /* Enable this card */
+static int snd_isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
+static long snd_cs4232_pcm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* PnP setup */
+static int snd_cs4232_pcm_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,11,12,15 */
+static long snd_cs4232_mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */
+static int snd_cs4232_mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 9,11,12,15 */
+static long snd_ics2115_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */
+static int snd_ics2115_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;    /* 2,9,11,12,15 */
+static long snd_fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	    /* PnP setup */
+static long snd_control_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */
+static long snd_wss_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	    /* PnP setup */
+static int snd_dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	    /* 0,1,3,5,6,7 */
+static int snd_dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	    /* 0,1,3,5,6,7 */
+
+MODULE_AUTHOR("Paul Barton-Davis <pbd@op.net>");
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_SYNTAX(snd_index, "Index value for WaveFront soundcard.");
+MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+MODULE_PARM_DESC(snd_id, "ID string for WaveFront soundcard.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable WaveFront soundcard.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+#ifdef __ISAPNP__
+MODULE_PARM(snd_isapnp, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_isapnp, "ISA PnP detection for WaveFront soundcards.");
+MODULE_PARM_SYNTAX(snd_isapnp, SNDRV_ISAPNP_DESC);
+#endif
+MODULE_PARM(snd_cs4232_pcm_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_4232_port, "Port # for CS4232 PCM interface.");
+MODULE_PARM_SYNTAX(snd_cs4232_port, SNDRV_PORT12_DESC);
+MODULE_PARM(snd_cs4232_pcm_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_cs4232_pcm_irq, "IRQ # for CS4232 PCM interface.");
+MODULE_PARM_SYNTAX(snd_cs4232_pcm_irq, SNDRV_ENABLED ",allows:{{5},{7},{9},{11},{12},{15}},dialog:list");
+MODULE_PARM(snd_dma1, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_dma1, "DMA1 # for CS4232 PCM interface.");
+MODULE_PARM_SYNTAX(snd_dma1, SNDRV_DMA_DESC);
+MODULE_PARM(snd_dma2, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_dma2, "DMA2 # for CS4232 PCM interface.");
+MODULE_PARM_SYNTAX(snd_dma2, SNDRV_DMA_DESC);
+MODULE_PARM(snd_cs4232_mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_cs4232_mpu_port, "port # for CS4232 MPU-401 interface.");
+MODULE_PARM_SYNTAX(snd_cs4232_mpu_port, SNDRV_PORT12_DESC);
+MODULE_PARM(snd_cs4232_mpu_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_cs4232_mpu_irq, "IRQ # for CS4232 MPU-401 interface.");
+MODULE_PARM_SYNTAX(snd_cs4232_mpu_irq, SNDRV_ENABLED ",allows:{{9},{11},{12},{15}},dialog:list");
+MODULE_PARM(snd_ics2115_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_ics2115_irq, "IRQ # for ICS2115.");
+MODULE_PARM_SYNTAX(snd_ics2115_irq, SNDRV_ENABLED ",allows:{{9},{11},{12},{15}},dialog:list");
+MODULE_PARM(snd_ics2115_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_ics2115_port, "Port # for ICS2115.");
+MODULE_PARM_SYNTAX(snd_ics2115_port, SNDRV_PORT12_DESC);
+MODULE_PARM(snd_fm_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_fm_port, "FM port #.");
+MODULE_PARM_SYNTAX(snd_fm_port, SNDRV_PORT12_DESC);
+MODULE_PARM(snd_wss_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_wss_port, "Windows Sound System port #.");
+MODULE_PARM_SYNTAX(snd_wss_port, SNDRV_PORT12_DESC);
+
+static snd_card_t *snd_wavefront_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+
+#ifdef __ISAPNP__
+
+static struct isapnp_card *snd_wavefront_isapnp_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+static const struct isapnp_card_id *snd_wavefront_isapnp_id[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+
+static struct isapnp_card_id snd_wavefront_pnpids[] __devinitdata = {
+	{
+		ISAPNP_CARD_ID('C','S','C',0x7532),	/* Tropez */
+		devs: { ISAPNP_DEVICE_ID('C','S','C',0x0000),	/* WSS */
+			ISAPNP_DEVICE_ID('C','S','C',0x0010),	/* CTRL */
+			ISAPNP_DEVICE_ID('P','n','P',0xb006),	/* MPU */
+			ISAPNP_DEVICE_ID('C','S','C',000004), }, /* SYNTH */
+	},
+	{
+		ISAPNP_CARD_ID('C','S','C',0x7632),	/* Tropez+ */
+		devs: { ISAPNP_DEVICE_ID('C','S','C',0x0000),	/* WSS */
+			ISAPNP_DEVICE_ID('C','S','C',0x0010),	/* CTRL */
+			ISAPNP_DEVICE_ID('P','n','P',0xb006),	/* MPU */
+			ISAPNP_DEVICE_ID('C','S','C',000004), }, /* SYNTH */
+	},
+	{ ISAPNP_CARD_END, }
+};
+
+ISAPNP_CARD_TABLE(snd_wavefront_pnpids);
+
+static int __init
+snd_wavefront_isapnp (int dev, snd_wavefront_card_t *acard)
+{
+	const struct isapnp_card_id *id = snd_wavefront_isapnp_id[dev];
+	struct isapnp_card *card = snd_wavefront_isapnp_cards[dev];
+	struct isapnp_dev *pdev;
+	int tmp;
+	
+	/* Check for each logical device. */
+
+	/* "windows sound system" has id 0 */
+
+	acard->wss = isapnp_find_dev(card, id->devs[0].vendor, id->devs[0].function, NULL);
+	if (acard->wss->active) {
+		acard->wss = NULL;
+		return -EBUSY;
+	}
+
+	/* control interface has id 1 */
+
+	acard->ctrl = isapnp_find_dev(card, id->devs[1].vendor, id->devs[1].function, NULL);
+	if (acard->ctrl->active) {
+		acard->wss = acard->ctrl = NULL;
+		return -EBUSY;
+	}
+
+	/* synth has id 3 */
+
+	acard->synth = isapnp_find_dev(card, id->devs[3].vendor, id->devs[3].function, NULL);
+	if (acard->synth->active) {
+		acard->wss = acard->ctrl = acard->synth = NULL;
+		return -EBUSY;
+	}
+
+	/* Only configure the CS4232 MIDI interface is its been
+	   requested. It has id 2.
+	*/
+
+	if (snd_cs4232_mpu_port[dev] != SNDRV_AUTO_PORT) {
+		acard->mpu = isapnp_find_dev(card, id->devs[2].vendor, id->devs[2].function, NULL);
+		if (acard->mpu->active) {
+			acard->wss = acard->ctrl = acard->synth = acard->mpu = NULL;
+			return -EBUSY;
+		}
+	}
+
+	/* PCM/FM initialization */
+
+	pdev = acard->wss;
+
+	if ((tmp = pdev->prepare (pdev)) < 0) {
+		if (tmp == -EBUSY) {
+			snd_printk ("ISA PnP configuration appears to have been "
+				    "done. Restart the isa-pnp module.\n");
+			return 0;
+		}
+				    
+		snd_printk ("isapnp WSS preparation failed\n");
+		return -EAGAIN;
+	}
+
+	if (snd_cs4232_pcm_port[dev] != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[0], snd_cs4232_pcm_port[dev], 4);
+	if (snd_fm_port[dev] != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[1], snd_fm_port[dev], 4);
+	if (snd_wss_port[dev] != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[2], snd_wss_port[dev], 4);
+
+	if (snd_ics2115_port[dev] != SNDRV_AUTO_PORT)
+		isapnp_resource_change(&pdev->resource[3], snd_ics2115_port[dev], 8);
+	if (snd_ics2115_irq[dev] != SNDRV_AUTO_IRQ)
+		isapnp_resource_change(&pdev->irq_resource[3], snd_ics2115_irq[dev], 1);
+
+	if (snd_dma1[dev] != SNDRV_AUTO_DMA)
+		isapnp_resource_change(&pdev->dma_resource[0], snd_dma1[dev], 1);
+	if (snd_dma2[dev] != SNDRV_AUTO_DMA)
+		isapnp_resource_change(&pdev->dma_resource[1], snd_dma2[dev], 1);
+
+	if (snd_cs4232_pcm_irq[dev] != SNDRV_AUTO_IRQ)
+		isapnp_resource_change(&pdev->irq_resource[0], snd_cs4232_pcm_irq[dev], 1);
+
+	if (pdev->activate(pdev)<0) {
+		snd_printk ("isapnp WSS activation failed\n");
+		return -EBUSY;
+	}
+
+	snd_cs4232_pcm_port[dev] = pdev->resource[0].start;
+	snd_fm_port[dev] = pdev->resource[1].start;
+	snd_wss_port[dev] = pdev->resource[2].start;
+
+	snd_ics2115_port[dev] = pdev->resource[3].start;
+	snd_ics2115_irq[dev] = pdev->irq_resource[3].start;
+
+	snd_dma1[dev] = pdev->dma_resource[0].start;
+	snd_dma2[dev] = pdev->dma_resource[1].start == 4 ? -1 : pdev->dma_resource[1].start;
+
+	snd_cs4232_pcm_irq[dev] = pdev->irq_resource[0].start;
+
+	snd_printk ("CS4232: port=0x%lx, fm port=0x%lx, wss port=0x%lx\n"
+		    "CS4232: dma1=%i, dma2=%i, irq=%i\n"
+		    "ICS2115: port=0x%lx, irq=%i\n", 
+		    snd_cs4232_pcm_port[dev], snd_fm_port[dev], snd_wss_port[dev],
+		    snd_dma1[dev], snd_dma2[dev], snd_cs4232_pcm_irq[dev],
+		    snd_ics2115_port[dev], 
+		    snd_ics2115_irq[dev]);
+	
+	/* CTRL initialization */
+
+	if (snd_control_port[dev] != SNDRV_AUTO_PORT) {
+
+		pdev = acard->ctrl;
+
+		if (pdev->prepare(pdev)<0) {
+			acard->wss->deactivate(acard->wss);
+			snd_printk ("isapnp CTRL preparation failed\n");
+			return -EAGAIN;
+		}
+		
+		if (snd_control_port[dev] != SNDRV_AUTO_PORT)
+			isapnp_resource_change(&pdev->resource[0], snd_control_port[dev], 8);
+		
+		if (pdev->activate(pdev)<0) {
+			snd_printk("isapnp CTRL activation failed\n");
+			acard->wss->deactivate(acard->wss);
+			return -EBUSY;
+		}
+		
+		snd_control_port[dev] = pdev->resource[0].start;
+		snd_printk ("isapnp CTRL: control port=0x%lx\n", 
+			    snd_control_port[dev]);
+	}
+
+	/* Synth initialization */
+
+	if (snd_ics2115_port[dev] != SNDRV_AUTO_PORT) {
+
+		pdev = acard->synth;
+
+		if (pdev->prepare(pdev)<0) {
+			acard->wss->deactivate(acard->wss);
+			if (acard->ctrl)
+				acard->ctrl->deactivate(acard->ctrl);
+			snd_printk ("ICS2115 synth preparation failed\n");
+			return -EAGAIN;
+		}
+		
+		isapnp_resource_change(&pdev->resource[0], snd_ics2115_port[dev], 8);
+		if (snd_ics2115_irq[dev] != SNDRV_AUTO_IRQ) 
+			isapnp_resource_change(&pdev->irq_resource[0], snd_ics2115_irq[dev], 1);
+
+		if (pdev->activate(pdev)<0) {
+			snd_printk("synth configuration failed\n");
+			acard->wss->deactivate(acard->wss);
+			if (acard->ctrl)
+				acard->ctrl->deactivate(acard->ctrl);
+			return -EBUSY;
+		}
+		
+		snd_ics2115_port[dev] = pdev->resource[0].start;
+		snd_ics2115_irq[dev] = pdev->irq_resource[0].start;
+	}
+
+	/* CS4232 MPU initialization. Configure this only if
+	   explicitly requested, since its physically inaccessible and
+	   consumes another IRQ.
+	*/
+
+	if (snd_cs4232_mpu_port[dev] != SNDRV_AUTO_PORT) {
+
+		pdev = acard->mpu;
+
+		if (pdev->prepare(pdev)<0) {
+			acard->wss->deactivate(acard->wss);
+			if (acard->ctrl)
+				acard->ctrl->deactivate(acard->ctrl);
+			if (acard->synth)
+				acard->synth->deactivate(acard->synth);
+			snd_printk ("CS4232 MPU preparation failed\n");
+			return -EAGAIN;
+		}
+
+		if (snd_cs4232_mpu_port[dev] != SNDRV_AUTO_PORT)
+			isapnp_resource_change(&pdev->resource[0], snd_cs4232_mpu_port[dev], 2);
+		if (snd_cs4232_mpu_irq[dev] != SNDRV_AUTO_IRQ)
+			isapnp_resource_change(&pdev->resource[0], snd_cs4232_mpu_irq[dev], 1);
+
+		if (pdev->activate(pdev)<0) {
+			snd_printk("isapnp CS4232 MPU activation failed\n");
+			snd_cs4232_mpu_port[dev] = SNDRV_AUTO_PORT;
+		} else {
+			snd_cs4232_mpu_port[dev] = pdev->resource[0].start;
+			snd_cs4232_mpu_irq[dev] = pdev->irq_resource[0].start;
+		}
+
+		snd_printk ("CS4232 MPU: port=0x%lx, irq=%i\n", 
+			    snd_cs4232_mpu_port[dev], 
+			    snd_cs4232_mpu_irq[dev]);
+	}
+
+	return 0;
+}
+
+static void 
+snd_wavefront_deactivate (snd_wavefront_card_t *acard)
+{
+	snd_printk ("deactivating PnP devices\n");
+	if (acard->wss) {
+		acard->wss->deactivate(acard->wss);
+		acard->wss = NULL;
+	}
+	if (acard->ctrl) {
+		acard->ctrl->deactivate(acard->ctrl);
+		acard->ctrl = NULL;
+	}
+	if (acard->mpu) {
+		acard->mpu->deactivate(acard->mpu);
+		acard->mpu = NULL;
+	}
+	if (acard->synth) {
+		acard->synth->deactivate(acard->synth);
+		acard->synth = NULL;
+	}
+}
+
+#endif /* __ISAPNP__ */
+
+static void snd_wavefront_ics2115_interrupt(int irq, 
+					    void *dev_id, 
+					    struct pt_regs *regs)
+{
+	snd_wavefront_card_t *acard;
+
+	acard = (snd_wavefront_card_t *) dev_id;
+
+	if (acard == NULL) 
+		return;
+
+	if (acard->wavefront.interrupts_are_midi) {
+		snd_wavefront_midi_interrupt (acard);
+	} else {
+		snd_wavefront_internal_interrupt (acard);
+	}
+}
+
+snd_hwdep_t * __init
+snd_wavefront_new_synth (snd_card_t *card,
+			 int hw_dev,
+			 snd_wavefront_card_t *acard)
+{
+	snd_hwdep_t *wavefront_synth;
+
+	if (snd_wavefront_detect (acard) < 0) {
+		return NULL;
+	}
+
+	if (snd_wavefront_start (&acard->wavefront) < 0) {
+		return NULL;
+	}
+
+	if (snd_hwdep_new(card, "WaveFront", hw_dev, &wavefront_synth) < 0)
+		return NULL;
+	strcpy (wavefront_synth->name, 
+		"WaveFront (ICS2115) wavetable synthesizer");
+	wavefront_synth->ops.open = snd_wavefront_synth_open;
+	wavefront_synth->ops.release = snd_wavefront_synth_release;
+	wavefront_synth->ops.ioctl = snd_wavefront_synth_ioctl;
+
+	return wavefront_synth;
+}
+
+snd_hwdep_t * __init
+snd_wavefront_new_fx (snd_card_t *card,
+		      int hw_dev,
+		      snd_wavefront_card_t *acard,
+		      unsigned long port)
+
+{
+	snd_hwdep_t *fx_processor;
+
+	if (snd_wavefront_fx_start (&acard->wavefront)) {
+		snd_printk ("cannot initialize YSS225 FX processor");
+		return NULL;
+	}
+
+	if (snd_hwdep_new (card, "YSS225", hw_dev, &fx_processor) < 0)
+		return NULL;
+	sprintf (fx_processor->name, "YSS225 FX Processor at 0x%lx", port);
+	fx_processor->ops.open = snd_wavefront_fx_open;
+	fx_processor->ops.release = snd_wavefront_fx_release;
+	fx_processor->ops.ioctl = snd_wavefront_fx_ioctl;
+	
+	return fx_processor;
+}
+
+static snd_wavefront_mpu_id internal_id = internal_mpu;
+static snd_wavefront_mpu_id external_id = external_mpu;
+
+snd_rawmidi_t * __init
+snd_wavefront_new_midi (snd_card_t *card,
+			int midi_dev,
+			snd_wavefront_card_t *acard,
+			unsigned long port,
+			snd_wavefront_mpu_id mpu)
+
+{
+	snd_rawmidi_t *rmidi;
+	static int first = 1;
+
+	if (first) {
+		first = 0;
+		acard->wavefront.midi.base = port;
+		if (snd_wavefront_midi_start (acard)) {
+			snd_printk ("cannot initialize MIDI interface\n");
+			return NULL;
+		}
+	}
+
+	if (snd_rawmidi_new (card, "WaveFront MIDI", midi_dev, 1, 1, &rmidi) < 0)
+		return NULL;
+
+	if (mpu == internal_mpu) {
+		strcpy(rmidi->name, "WaveFront MIDI (Internal)");
+		rmidi->private_data = &internal_id;
+	} else {
+		strcpy(rmidi->name, "WaveFront MIDI (External)");
+		rmidi->private_data = &external_id;
+	}
+
+	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_wavefront_midi_output);
+	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_wavefront_midi_input);
+
+	rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT |
+	                     SNDRV_RAWMIDI_INFO_INPUT |
+	                     SNDRV_RAWMIDI_INFO_DUPLEX;
+
+	return rmidi;
+}
+
+static void
+snd_wavefront_free(snd_card_t *card)
+{
+	snd_wavefront_card_t *acard = (snd_wavefront_card_t *)card->private_data;
+	
+	if (acard) {
+#ifdef __ISAPNP__
+		snd_wavefront_deactivate(acard);
+#endif
+		if (acard->wavefront.res_base != NULL)
+			release_resource(acard->wavefront.res_base);
+		if (acard->wavefront.irq > 0)
+			free_irq(acard->wavefront.irq, (void *)acard);
+	}
+}
+
+static int __init
+snd_wavefront_probe (int dev)
+{
+	snd_card_t *card;
+	snd_wavefront_card_t *acard;
+	cs4231_t *chip;
+	snd_hwdep_t *wavefront_synth;
+	snd_rawmidi_t *ics2115_internal_rmidi = NULL;
+	snd_rawmidi_t *ics2115_external_rmidi = NULL;
+	snd_hwdep_t *fx_processor;
+	int hw_dev = 0, midi_dev = 0, err;
+
+	if (snd_cs4232_mpu_port[dev] < 0)
+		snd_cs4232_mpu_port[dev] = SNDRV_AUTO_PORT;
+	if (snd_fm_port[dev] < 0)
+		snd_fm_port[dev] = SNDRV_AUTO_PORT;
+	if (snd_ics2115_port[dev] < 0)
+		snd_ics2115_port[dev] = SNDRV_AUTO_PORT;
+
+#ifdef __ISAPNP__
+	if (!snd_isapnp[dev]) {
+#endif
+		if (snd_cs4232_pcm_port[dev] == SNDRV_AUTO_PORT) {
+			snd_printk("specify CS4232 port\n");
+			return -EINVAL;
+		}
+		if (snd_ics2115_port[dev] == SNDRV_AUTO_PORT) {
+			snd_printk("specify ICS2115 port\n");
+			return -ENODEV;
+		}
+#ifdef __ISAPNP__
+	}
+#endif
+	card = snd_card_new (snd_index[dev], 
+			     snd_id[dev],
+			     THIS_MODULE,
+			     sizeof(snd_wavefront_card_t));
+
+	if (card == NULL) {
+		return -ENOMEM;
+	}
+	acard = (snd_wavefront_card_t *)card->private_data;
+	acard->wavefront.irq = -1;
+	init_waitqueue_head(&acard->wavefront.interrupt_sleeper);
+	spin_lock_init(&acard->wavefront.midi.open);
+	spin_lock_init(&acard->wavefront.midi.virtual);
+	card->private_free = snd_wavefront_free;
+
+#ifdef __ISAPNP__
+	if (snd_isapnp[dev] && snd_wavefront_isapnp (dev, acard) < 0) {
+		if (snd_cs4232_pcm_port[dev] == SNDRV_AUTO_PORT ||
+		    snd_control_port[dev] == SNDRV_AUTO_PORT) {
+			snd_printk ("isapnp detection failed\n");
+			snd_card_free (card);
+			return -ENODEV;
+		}
+	}
+#endif /* __ISAPNP__ */
+
+	/* --------- PCM --------------- */
+
+	if ((err = snd_cs4231_create (card,
+				      snd_cs4232_pcm_port[dev],
+				      snd_control_port[dev],
+				      snd_cs4232_pcm_irq[dev],
+				      snd_dma1[dev],
+				      snd_dma2[dev],
+				      CS4231_HW_DETECT, 0, &chip)) < 0) {
+		snd_card_free(card);
+		snd_printk ("can't allocate CS4231 device\n");
+		return err;
+	}
+
+	if ((err = snd_cs4231_pcm (chip, 0, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_cs4231_timer (chip, 0, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	/* ---------- OPL3 synth --------- */
+
+	if (snd_fm_port[dev] != SNDRV_AUTO_PORT) {
+		opl3_t *opl3;
+
+	        if ((err = snd_opl3_create(card,
+					   snd_fm_port[dev],
+					   snd_fm_port[dev] + 2,
+					   OPL3_HW_OPL3_CS,
+					   0, &opl3)) < 0) {
+			snd_printk ("can't allocate or detect OPL3 synth\n");
+			snd_card_free(card);
+			return err;
+		}
+
+		if ((err = snd_opl3_hwdep_new(opl3, hw_dev, 1, NULL)) < 0) {
+			snd_card_free(card);
+			return err;
+		}
+		hw_dev++;
+	}
+
+	/* ------- ICS2115 Wavetable synth ------- */
+
+	if ((acard->wavefront.res_base = request_region(snd_ics2115_port[dev], 16, "ICS2115")) == NULL) {
+		snd_printk("unable to grab ICS2115 i/o region 0x%lx-0x%lx\n", snd_ics2115_port[dev], snd_ics2115_port[dev] + 16 - 1);
+		snd_card_free(card);
+		return -EBUSY;
+	}
+	if (request_irq(snd_ics2115_irq[dev], snd_wavefront_ics2115_interrupt, SA_INTERRUPT, "ICS2115", (void *)acard)) {
+		snd_printk("unable to use ICS2115 IRQ %d\n", snd_ics2115_irq[dev]);
+		snd_card_free(card);
+		return -EBUSY;
+	}
+	
+	acard->wavefront.irq = snd_ics2115_irq[dev];
+	acard->wavefront.base = snd_ics2115_port[dev];
+
+	if ((wavefront_synth = snd_wavefront_new_synth (card, hw_dev, acard)) == NULL) {
+		snd_printk ("can't create WaveFront synth device\n");
+		snd_card_free(card);
+		return -ENOMEM;
+	}
+
+	strcpy (wavefront_synth->name, "ICS2115 Wavetable MIDI Synthesizer");
+	wavefront_synth->iface = SNDRV_HWDEP_IFACE_ICS2115;
+	hw_dev++;
+
+	/* --------- Mixer ------------ */
+
+	if ((err = snd_cs4231_mixer(chip)) < 0) {
+		snd_printk ("can't allocate mixer device\n");
+		snd_card_free(card);
+		return err;
+	}
+
+	/* -------- CS4232 MPU-401 interface -------- */
+
+	if (snd_cs4232_mpu_port[dev] > 0 && snd_cs4232_mpu_port[dev] != SNDRV_AUTO_PORT) {
+		if ((err = snd_mpu401_uart_new(card, midi_dev, MPU401_HW_CS4232,
+					       snd_cs4232_mpu_port[dev], 0,
+					       snd_cs4232_mpu_irq[dev],
+					       SA_INTERRUPT,
+					       NULL)) < 0) {
+			snd_printk ("can't allocate CS4232 MPU-401 device\n");
+			snd_card_free(card);
+			return err;
+		}
+		midi_dev++;
+	}
+
+	/* ------ ICS2115 internal MIDI ------------ */
+
+	if (snd_ics2115_port[dev] >= 0 && snd_ics2115_port[dev] != SNDRV_AUTO_PORT) {
+		ics2115_internal_rmidi = 
+			snd_wavefront_new_midi (card, 
+						midi_dev,
+						acard,
+						snd_ics2115_port[dev],
+						internal_mpu);
+		if (ics2115_internal_rmidi == NULL) {
+			snd_printk ("can't setup ICS2115 internal MIDI device\n");
+			snd_card_free(card);
+			return -ENOMEM;
+		}
+		midi_dev++;
+	}
+
+	/* ------ ICS2115 external MIDI ------------ */
+
+	if (snd_ics2115_port[dev] >= 0 && snd_ics2115_port[dev] != SNDRV_AUTO_PORT) {
+		ics2115_external_rmidi = 
+			snd_wavefront_new_midi (card, 
+						midi_dev,
+						acard,
+						snd_ics2115_port[dev],
+						external_mpu);
+		if (ics2115_external_rmidi == NULL) {
+			snd_printk ("can't setup ICS2115 external MIDI device\n");
+			snd_card_free(card);
+			return -ENOMEM;
+		}
+		midi_dev++;
+	}
+
+	/* FX processor for Tropez+ */
+
+	if (acard->wavefront.has_fx) {
+		fx_processor = snd_wavefront_new_fx (card,
+						     hw_dev,
+						     acard,
+						     snd_ics2115_port[dev]);
+		if (fx_processor == NULL) {
+			snd_printk ("can't setup FX device\n");
+			snd_card_free(card);
+			return -ENOMEM;
+		}
+
+		hw_dev++;
+
+		strcpy(card->driver, "Tropez+");
+		strcpy(card->shortname, "Turtle Beach Tropez+");
+	} else {
+		/* Need a way to distinguish between Maui and Tropez */
+		strcpy(card->driver, "WaveFront");
+		strcpy(card->shortname, "Turtle Beach WaveFront");
+	}
+
+	/* ----- Register the card --------- */
+
+	/* Not safe to include "Turtle Beach" in longname, due to 
+	   length restrictions
+	*/
+
+	sprintf(card->longname, "%s PCM 0x%lx irq %d dma %d",
+		card->driver,
+		chip->port,
+		snd_cs4232_pcm_irq[dev],
+		snd_dma1[dev]);
+
+	if (snd_dma2[dev] >= 0 && snd_dma2[dev] < 8)
+		sprintf(card->longname + strlen(card->longname), "&%d", snd_dma2[dev]);
+
+	if (snd_cs4232_mpu_port[dev] != SNDRV_AUTO_PORT) {
+		sprintf (card->longname + strlen (card->longname), 
+			 " MPU-401 0x%lx irq %d",
+			 snd_cs4232_mpu_port[dev],
+			 snd_cs4232_mpu_irq[dev]);
+	}
+
+	sprintf (card->longname + strlen (card->longname), 
+		 " SYNTH 0x%lx irq %d",
+		 snd_ics2115_port[dev],
+		 snd_ics2115_irq[dev]);
+
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	snd_wavefront_cards[dev] = card;
+	return 0;
+}	
+
+#ifdef __ISAPNP__
+
+static int __init snd_wavefront_isapnp_detect(struct isapnp_card *card,
+                                              const struct isapnp_card_id *id)
+{
+        static int dev = 0;
+        int res;
+
+        for ( ; dev < SNDRV_CARDS; dev++) {
+                if (!snd_enable[dev] || !snd_isapnp[dev])
+                        continue;
+                snd_wavefront_isapnp_cards[dev] = card;
+                snd_wavefront_isapnp_id[dev] = id;
+                res = snd_wavefront_probe(dev);
+                if (res < 0)
+                        return res;
+                dev++;
+                return 0;
+        }
+
+        return -ENODEV;
+}
+
+#endif /* __ISAPNP__ */
+
+static int __init alsa_card_wavefront_init(void)
+{
+	int cards = 0;
+	int dev;
+	for (dev = 0; dev < SNDRV_CARDS; dev++) {
+		if (!snd_enable[dev])
+			continue;
+#ifdef __ISAPNP__
+		if (snd_isapnp[dev])
+			continue;
+#endif
+		if (snd_wavefront_probe(dev) >= 0)
+			cards++;
+	}
+#ifdef __ISAPNP__
+	cards += isapnp_probe_cards(snd_wavefront_pnpids, snd_wavefront_isapnp_detect);
+#endif
+	if (!cards) {
+#ifdef MODULE
+		snd_printk ("No cards found or devices busy\n");
+#endif
+		return -ENODEV;
+	}
+	return 0;
+}
+
+static void __exit alsa_card_wavefront_exit(void)
+{
+	int idx;
+
+	for (idx = 0; idx < SNDRV_CARDS; idx++)
+		snd_card_free(snd_wavefront_cards[idx]);
+}
+
+module_init(alsa_card_wavefront_init)
+module_exit(alsa_card_wavefront_exit)
+
+#ifndef MODULE
+
+/* format is: snd-card-wavefront=snd_enable,snd_index,snd_id,snd_isapnp,
+				 snd_cs4232_pcm_port,snd_cs4232_pcm_irq,
+				 snd_cs4232_mpu_port,snd_cs4232_mpu_irq,
+				 snd_ics2115_port,snd_ics2115_irq,
+				 snd_fm_port,snd_control_port,snd_wss_port,
+				 snd_dma1,snd_dma2 */
+
+static int __init alsa_card_wavefront_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_isapnp[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_cs4232_pcm_port[nr_dev]) == 2 &&
+	       get_option(&str,&snd_cs4232_pcm_irq[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_cs4232_mpu_port[nr_dev]) == 2 &&
+	       get_option(&str,&snd_cs4232_mpu_irq[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_ics2115_port[nr_dev]) == 2 &&
+	       get_option(&str,&snd_ics2115_irq[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_fm_port[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_control_port[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_wss_port[nr_dev]) == 2 &&
+	       get_option(&str,&snd_dma1[nr_dev]) == 2 &&
+	       get_option(&str,&snd_dma2[nr_dev]) == 2);
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-card-wavefront=", alsa_card_wavefront_setup);
+
+#endif /* ifndef MODULE */
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/wavefront/wavefront_fx.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/wavefront/wavefront_fx.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/wavefront/wavefront_fx.c	(revision 91)
@@ -0,0 +1,1034 @@
+/*
+ *  Copyright (c) 1998-1999 by Paul Barton-Davis <pbd@op.net>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define SNDRV_MAIN_OBJECT_FILE
+#include <sound/driver.h>
+#include <sound/snd_wavefront.h>
+#include <sound/yss225.h>
+#include <sound/initval.h>
+
+MODULE_AUTHOR("Paul Barton-Davis <pbd@op.net>");
+MODULE_DESCRIPTION("ALSA driver for Turtle Beach Tropez+ YSS225 FX Processor");
+MODULE_CLASSES("{sound}");
+
+/* Control bits for the Load Control Register
+ */
+
+#define FX_LSB_TRANSFER 0x01    /* transfer after DSP LSB byte written */
+#define FX_MSB_TRANSFER 0x02    /* transfer after DSP MSB byte written */
+#define FX_AUTO_INCR    0x04    /* auto-increment DSP address after transfer */
+
+static int
+wavefront_fx_idle (snd_wavefront_t *dev) 
+    
+{
+	int i;
+	unsigned int x = 0x80;
+    
+	for (i = 0; i < 1000; i++) {
+		x = inb (dev->fx_status);
+		if ((x & 0x80) == 0) {
+			break;
+		}
+	}
+    
+	if (x & 0x80) {
+		snd_printk ("FX device never idle.\n");
+		return 0;
+	}
+    
+	return (1);
+}
+
+static void
+wavefront_fx_mute (snd_wavefront_t *dev, int onoff)
+    
+{
+	if (!wavefront_fx_idle(dev)) {
+		return;
+	}
+    
+	outb (onoff ? 0x02 : 0x00, dev->fx_op);
+}
+
+static int
+wavefront_fx_memset (snd_wavefront_t *dev, 
+		     int page,
+		     int addr, 
+		     int cnt, 
+		     unsigned short *data)
+{
+	if (page < 0 || page > 7) {
+		snd_printk ("FX memset: "
+			"page must be >= 0 and <= 7\n");
+		return -(EINVAL);
+	}
+
+	if (addr < 0 || addr > 0x7f) {
+		snd_printk ("FX memset: "
+			"addr must be >= 0 and <= 7f\n");
+		return -(EINVAL);
+	}
+
+	if (cnt == 1) {
+
+		outb (FX_LSB_TRANSFER, dev->fx_lcr);
+		outb (page, dev->fx_dsp_page);
+		outb (addr, dev->fx_dsp_addr);
+		outb ((data[0] >> 8), dev->fx_dsp_msb);
+		outb ((data[0] & 0xff), dev->fx_dsp_lsb);
+
+		snd_printk ("FX: addr %d:%x set to 0x%x\n",
+			page, addr, data[0]);
+	
+	} else {
+		int i;
+
+		outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr);
+		outb (page, dev->fx_dsp_page);
+		outb (addr, dev->fx_dsp_addr);
+
+		for (i = 0; i < cnt; i++) {
+			outb ((data[i] >> 8), dev->fx_dsp_msb);
+			outb ((data[i] & 0xff), dev->fx_dsp_lsb);
+			if (!wavefront_fx_idle (dev)) {
+				break;
+			}
+		}
+
+		if (i != cnt) {
+			snd_printk ("FX memset "
+				    "(0x%x, 0x%x, 0x%lx, %d) incomplete\n",
+				    page, addr, (unsigned long) data, cnt);
+			return -(EIO);
+		}
+	}
+
+	return 0;
+}
+
+int 
+snd_wavefront_fx_detect (snd_wavefront_t *dev)
+
+{
+	/* This is a crude check, but its the best one I have for now.
+	   Certainly on the Maui and the Tropez, wavefront_fx_idle() will
+	   report "never idle", which suggests that this test should
+	   work OK.
+	*/
+
+	if (inb (dev->fx_status) & 0x80) {
+		snd_printk ("Hmm, probably a Maui or Tropez.\n");
+		return -1;
+	}
+
+	return 0;
+}	
+
+int 
+snd_wavefront_fx_open (snd_hwdep_t *hw, struct file *file)
+
+{
+	MOD_INC_USE_COUNT;
+	if (!try_inc_mod_count(hw->card->module)) {
+		MOD_DEC_USE_COUNT;
+		return -EFAULT;
+	}
+	file->private_data = hw;
+	return 0;
+}
+
+int 
+snd_wavefront_fx_release (snd_hwdep_t *hw, struct file *file)
+
+{
+	dec_mod_count(hw->card->module);
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+int
+snd_wavefront_fx_ioctl (snd_hwdep_t *sdev, struct file *file,
+			unsigned int cmd, unsigned long arg)
+
+{
+	snd_card_t *card;
+	snd_wavefront_card_t *acard;
+	snd_wavefront_t *dev;
+	wavefront_fx_info r;
+	unsigned short page_data[256];
+	unsigned short *pd;
+
+	snd_assert(sdev->card != NULL, return -ENODEV);
+	
+	card = sdev->card;
+
+	snd_assert(card->private_data != NULL, return -ENODEV);
+
+	acard = card->private_data;
+	dev = &acard->wavefront;
+
+	if (copy_from_user (&r, (unsigned char *) arg, sizeof (wavefront_fx_info)))
+		return -EFAULT;
+
+	switch (r.request) {
+	case WFFX_MUTE:
+		wavefront_fx_mute (dev, r.data[0]);
+		return -EIO;
+
+	case WFFX_MEMSET:
+		if (r.data[2] <= 0) {
+			snd_printk ("cannot write "
+				"<= 0 bytes to FX\n");
+			return -EIO;
+		} else if (r.data[2] == 1) {
+			pd = (unsigned short *) &r.data[3];
+		} else {
+			if (r.data[2] > sizeof (page_data)) {
+				snd_printk ("cannot write "
+					    "> 255 bytes to FX\n");
+				return -EIO;
+			}
+			if (copy_from_user (page_data,
+					    (unsigned char *) r.data[3],
+					    r.data[2]))
+				return -EFAULT;
+			pd = page_data;
+		}
+
+		wavefront_fx_memset (dev,
+			     r.data[0], /* page */
+			     r.data[1], /* addr */
+			     r.data[2], /* cnt */
+			     pd);
+		break;
+
+	default:
+		snd_printk ("FX: ioctl %d not yet supported\n",
+			    r.request);
+		return -ENOTTY;
+	}
+	return 0;
+}
+
+/* YSS225 initialization.
+
+   This code was developed using DOSEMU. The Turtle Beach SETUPSND
+   utility was run with I/O tracing in DOSEMU enabled, and a reconstruction
+   of the port I/O done, using the Yamaha faxback document as a guide
+   to add more logic to the code. Its really pretty wierd.
+
+   There was an alternative approach of just dumping the whole I/O
+   sequence as a series of port/value pairs and a simple loop
+   that output it. However, I hope that eventually I'll get more
+   control over what this code does, and so I tried to stick with
+   a somewhat "algorithmic" approach.
+*/
+
+
+int
+snd_wavefront_fx_start (snd_wavefront_t *dev)
+
+{
+	unsigned int i, j;
+
+	/* Set all bits for all channels on the MOD unit to zero */
+	/* XXX But why do this twice ? */
+
+	for (j = 0; j < 2; j++) {
+		for (i = 0x10; i <= 0xff; i++) {
+	    
+			if (!wavefront_fx_idle (dev)) {
+				return (-1);
+			}
+	    
+			outb (i, dev->fx_mod_addr);
+			outb (0x0, dev->fx_mod_data);
+		}
+	}
+
+	if (!wavefront_fx_idle (dev)) return (-1);
+	outb (0x02, dev->fx_op);                        /* mute on */
+
+	if (!wavefront_fx_idle (dev)) return (-1);
+	outb (0x07, dev->fx_dsp_page);
+	outb (0x44, dev->fx_dsp_addr);
+	outb (0x00, dev->fx_dsp_msb);
+	outb (0x00, dev->fx_dsp_lsb);
+	if (!wavefront_fx_idle (dev)) return (-1);
+	outb (0x07, dev->fx_dsp_page);
+	outb (0x42, dev->fx_dsp_addr);
+	outb (0x00, dev->fx_dsp_msb);
+	outb (0x00, dev->fx_dsp_lsb);
+	if (!wavefront_fx_idle (dev)) return (-1);
+	outb (0x07, dev->fx_dsp_page);
+	outb (0x43, dev->fx_dsp_addr);
+	outb (0x00, dev->fx_dsp_msb);
+	outb (0x00, dev->fx_dsp_lsb);
+	if (!wavefront_fx_idle (dev)) return (-1);
+	outb (0x07, dev->fx_dsp_page);
+	outb (0x7c, dev->fx_dsp_addr);
+	outb (0x00, dev->fx_dsp_msb);
+	outb (0x00, dev->fx_dsp_lsb);
+	if (!wavefront_fx_idle (dev)) return (-1);
+	outb (0x07, dev->fx_dsp_page);
+	outb (0x7e, dev->fx_dsp_addr);
+	outb (0x00, dev->fx_dsp_msb);
+	outb (0x00, dev->fx_dsp_lsb);
+	if (!wavefront_fx_idle (dev)) return (-1);
+	outb (0x07, dev->fx_dsp_page);
+	outb (0x46, dev->fx_dsp_addr);
+	outb (0x00, dev->fx_dsp_msb);
+	outb (0x00, dev->fx_dsp_lsb);
+	if (!wavefront_fx_idle (dev)) return (-1);
+	outb (0x07, dev->fx_dsp_page);
+	outb (0x49, dev->fx_dsp_addr);
+	outb (0x00, dev->fx_dsp_msb);
+	outb (0x00, dev->fx_dsp_lsb);
+	if (!wavefront_fx_idle (dev)) return (-1);
+	outb (0x07, dev->fx_dsp_page);
+	outb (0x47, dev->fx_dsp_addr);
+	outb (0x00, dev->fx_dsp_msb);
+	outb (0x00, dev->fx_dsp_lsb);
+	if (!wavefront_fx_idle (dev)) return (-1);
+	outb (0x07, dev->fx_dsp_page);
+	outb (0x4a, dev->fx_dsp_addr);
+	outb (0x00, dev->fx_dsp_msb);
+	outb (0x00, dev->fx_dsp_lsb);
+
+	/* either because of stupidity by TB's programmers, or because it
+	   actually does something, rezero the MOD page.
+	*/
+	for (i = 0x10; i <= 0xff; i++) {
+	
+		if (!wavefront_fx_idle (dev)) {
+			return (-1);
+		}
+	
+		outb (i, dev->fx_mod_addr);
+		outb (0x0, dev->fx_mod_data);
+	}
+	/* load page zero */
+
+	outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr);
+	outb (0x00, dev->fx_dsp_page);
+	outb (0x00, dev->fx_dsp_addr);
+
+	for (i = 0; i < sizeof (page_zero); i += 2) {
+		outb (page_zero[i], dev->fx_dsp_msb);
+		outb (page_zero[i+1], dev->fx_dsp_lsb);
+		if (!wavefront_fx_idle (dev)) return (-1);
+	}
+
+	/* Now load page one */
+
+	outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr);
+	outb (0x01, dev->fx_dsp_page);
+	outb (0x00, dev->fx_dsp_addr);
+
+	for (i = 0; i < sizeof (page_one); i += 2) {
+		outb (page_one[i], dev->fx_dsp_msb);
+		outb (page_one[i+1], dev->fx_dsp_lsb);
+		if (!wavefront_fx_idle (dev)) return (-1);
+	}
+    
+	outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr);
+	outb (0x02, dev->fx_dsp_page);
+	outb (0x00, dev->fx_dsp_addr);
+
+	for (i = 0; i < sizeof (page_two); i++) {
+		outb (page_two[i], dev->fx_dsp_lsb);
+		if (!wavefront_fx_idle (dev)) return (-1);
+	}
+    
+	outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr);
+	outb (0x03, dev->fx_dsp_page);
+	outb (0x00, dev->fx_dsp_addr);
+
+	for (i = 0; i < sizeof (page_three); i++) {
+		outb (page_three[i], dev->fx_dsp_lsb);
+		if (!wavefront_fx_idle (dev)) return (-1);
+	}
+    
+	outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr);
+	outb (0x04, dev->fx_dsp_page);
+	outb (0x00, dev->fx_dsp_addr);
+
+	for (i = 0; i < sizeof (page_four); i++) {
+		outb (page_four[i], dev->fx_dsp_lsb);
+		if (!wavefront_fx_idle (dev)) return (-1);
+	}
+
+	/* Load memory area (page six) */
+    
+	outb (FX_LSB_TRANSFER, dev->fx_lcr); 
+	outb (0x06, dev->fx_dsp_page); 
+
+	for (i = 0; i < sizeof (page_six); i += 3) {
+		outb (page_six[i], dev->fx_dsp_addr);
+		outb (page_six[i+1], dev->fx_dsp_msb);
+		outb (page_six[i+2], dev->fx_dsp_lsb);
+		if (!wavefront_fx_idle (dev)) return (-1);
+	}
+    
+	outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr);
+	outb (0x07, dev->fx_dsp_page);
+	outb (0x00, dev->fx_dsp_addr);
+
+	for (i = 0; i < sizeof (page_seven); i += 2) {
+		outb (page_seven[i], dev->fx_dsp_msb);
+		outb (page_seven[i+1], dev->fx_dsp_lsb);
+		if (!wavefront_fx_idle (dev)) return (-1);
+	}
+
+	/* Now setup the MOD area. We do this algorithmically in order to
+	   save a little data space. It could be done in the same fashion
+	   as the "pages".
+	*/
+
+	for (i = 0x00; i <= 0x0f; i++) {
+		outb (0x01, dev->fx_mod_addr);
+		outb (i, dev->fx_mod_data);
+		if (!wavefront_fx_idle (dev)) return (-1);
+		outb (0x02, dev->fx_mod_addr);
+		outb (0x00, dev->fx_mod_data);
+		if (!wavefront_fx_idle (dev)) return (-1);
+	}
+
+	for (i = 0xb0; i <= 0xbf; i++) {
+		outb (i, dev->fx_mod_addr);
+		outb (0x20, dev->fx_mod_data);
+		if (!wavefront_fx_idle (dev)) return (-1);
+	}
+
+	for (i = 0xf0; i <= 0xff; i++) {
+		outb (i, dev->fx_mod_addr);
+		outb (0x20, dev->fx_mod_data);
+		if (!wavefront_fx_idle (dev)) return (-1);
+	}
+
+	for (i = 0x10; i <= 0x1d; i++) {
+		outb (i, dev->fx_mod_addr);
+		outb (0xff, dev->fx_mod_data);
+		if (!wavefront_fx_idle (dev)) return (-1);
+	}
+
+	outb (0x1e, dev->fx_mod_addr);
+	outb (0x40, dev->fx_mod_data);
+	if (!wavefront_fx_idle (dev)) return (-1);
+
+	for (i = 0x1f; i <= 0x2d; i++) {
+		outb (i, dev->fx_mod_addr);
+		outb (0xff, dev->fx_mod_data);
+		if (!wavefront_fx_idle (dev)) return (-1);
+	}
+
+	outb (0x2e, dev->fx_mod_addr);
+	outb (0x00, dev->fx_mod_data);
+	if (!wavefront_fx_idle (dev)) return (-1);
+
+	for (i = 0x2f; i <= 0x3e; i++) {
+		outb (i, dev->fx_mod_addr);
+		outb (0x00, dev->fx_mod_data);
+		if (!wavefront_fx_idle (dev)) return (-1);
+	}
+
+	outb (0x3f, dev->fx_mod_addr);
+	outb (0x20, dev->fx_mod_data);
+	if (!wavefront_fx_idle (dev)) return (-1);
+
+	for (i = 0x40; i <= 0x4d; i++) {
+		outb (i, dev->fx_mod_addr);
+		outb (0x00, dev->fx_mod_data);
+		if (!wavefront_fx_idle (dev)) return (-1);
+	}
+
+	outb (0x4e, dev->fx_mod_addr);
+	outb (0x0e, dev->fx_mod_data);
+	if (!wavefront_fx_idle (dev)) return (-1);
+	outb (0x4f, dev->fx_mod_addr);
+	outb (0x0e, dev->fx_mod_data);
+	if (!wavefront_fx_idle (dev)) return (-1);
+
+
+	for (i = 0x50; i <= 0x6b; i++) {
+		outb (i, dev->fx_mod_addr);
+		outb (0x00, dev->fx_mod_data);
+		if (!wavefront_fx_idle (dev)) return (-1);
+	}
+
+	outb (0x6c, dev->fx_mod_addr);
+	outb (0x40, dev->fx_mod_data);
+	if (!wavefront_fx_idle (dev)) return (-1);
+
+	outb (0x6d, dev->fx_mod_addr);
+	outb (0x00, dev->fx_mod_data);
+	if (!wavefront_fx_idle (dev)) return (-1);
+
+	outb (0x6e, dev->fx_mod_addr);
+	outb (0x40, dev->fx_mod_data);
+	if (!wavefront_fx_idle (dev)) return (-1);
+
+	outb (0x6f, dev->fx_mod_addr);
+	outb (0x40, dev->fx_mod_data);
+	if (!wavefront_fx_idle (dev)) return (-1);
+
+	for (i = 0x70; i <= 0x7f; i++) {
+		outb (i, dev->fx_mod_addr);
+		outb (0xc0, dev->fx_mod_data);
+		if (!wavefront_fx_idle (dev)) return (-1);
+	}
+    
+	for (i = 0x80; i <= 0xaf; i++) {
+		outb (i, dev->fx_mod_addr);
+		outb (0x00, dev->fx_mod_data);
+		if (!wavefront_fx_idle (dev)) return (-1);
+	}
+
+	for (i = 0xc0; i <= 0xdd; i++) {
+		outb (i, dev->fx_mod_addr);
+		outb (0x00, dev->fx_mod_data);
+		if (!wavefront_fx_idle (dev)) return (-1);
+	}
+
+	outb (0xde, dev->fx_mod_addr);
+	outb (0x10, dev->fx_mod_data);
+	if (!wavefront_fx_idle (dev)) return (-1);
+	outb (0xdf, dev->fx_mod_addr);
+	outb (0x10, dev->fx_mod_data);
+	if (!wavefront_fx_idle (dev)) return (-1);
+
+	for (i = 0xe0; i <= 0xef; i++) {
+		outb (i, dev->fx_mod_addr);
+		outb (0x00, dev->fx_mod_data);
+		if (!wavefront_fx_idle (dev)) return (-1);
+	}
+
+	for (i = 0x00; i <= 0x0f; i++) {
+		outb (0x01, dev->fx_mod_addr);
+		outb (i, dev->fx_mod_data);
+		outb (0x02, dev->fx_mod_addr);
+		outb (0x01, dev->fx_mod_data);
+		if (!wavefront_fx_idle (dev)) return (-1);
+	}
+
+	outb (0x02, dev->fx_op); /* mute on */
+
+	/* Now set the coefficients and so forth for the programs above */
+
+	for (i = 0; i < sizeof (coefficients); i += 4) {
+		outb (coefficients[i], dev->fx_dsp_page);
+		outb (coefficients[i+1], dev->fx_dsp_addr);
+		outb (coefficients[i+2], dev->fx_dsp_msb);
+		outb (coefficients[i+3], dev->fx_dsp_lsb);
+		if (!wavefront_fx_idle (dev)) return (-1);
+	}
+
+	/* Some settings (?) that are too small to bundle into loops */
+
+	if (!wavefront_fx_idle (dev)) return (-1);
+	outb (0x1e, dev->fx_mod_addr);
+	outb (0x14, dev->fx_mod_data);
+	if (!wavefront_fx_idle (dev)) return (-1);
+	outb (0xde, dev->fx_mod_addr);
+	outb (0x20, dev->fx_mod_data);
+	if (!wavefront_fx_idle (dev)) return (-1);
+	outb (0xdf, dev->fx_mod_addr);
+	outb (0x20, dev->fx_mod_data);
+    
+	/* some more coefficients */
+
+	if (!wavefront_fx_idle (dev)) return (-1);
+	outb (0x06, dev->fx_dsp_page);
+	outb (0x78, dev->fx_dsp_addr);
+	outb (0x00, dev->fx_dsp_msb);
+	outb (0x40, dev->fx_dsp_lsb);
+	if (!wavefront_fx_idle (dev)) return (-1);
+	outb (0x07, dev->fx_dsp_page);
+	outb (0x03, dev->fx_dsp_addr);
+	outb (0x0f, dev->fx_dsp_msb);
+	outb (0xff, dev->fx_dsp_lsb);
+	if (!wavefront_fx_idle (dev)) return (-1);
+	outb (0x07, dev->fx_dsp_page);
+	outb (0x0b, dev->fx_dsp_addr);
+	outb (0x0f, dev->fx_dsp_msb);
+	outb (0xff, dev->fx_dsp_lsb);
+	if (!wavefront_fx_idle (dev)) return (-1);
+	outb (0x07, dev->fx_dsp_page);
+	outb (0x02, dev->fx_dsp_addr);
+	outb (0x00, dev->fx_dsp_msb);
+	outb (0x00, dev->fx_dsp_lsb);
+	if (!wavefront_fx_idle (dev)) return (-1);
+	outb (0x07, dev->fx_dsp_page);
+	outb (0x0a, dev->fx_dsp_addr);
+	outb (0x00, dev->fx_dsp_msb);
+	outb (0x00, dev->fx_dsp_lsb);
+	if (!wavefront_fx_idle (dev)) return (-1);
+	outb (0x07, dev->fx_dsp_page);
+	outb (0x46, dev->fx_dsp_addr);
+	outb (0x00, dev->fx_dsp_msb);
+	outb (0x00, dev->fx_dsp_lsb);
+	if (!wavefront_fx_idle (dev)) return (-1);
+	outb (0x07, dev->fx_dsp_page);
+	outb (0x49, dev->fx_dsp_addr);
+	outb (0x00, dev->fx_dsp_msb);
+	outb (0x00, dev->fx_dsp_lsb);
+    
+	/* Now, for some strange reason, lets reload every page
+	   and all the coefficients over again. I have *NO* idea
+	   why this is done. I do know that no sound is produced
+	   is this phase is omitted.
+	*/
+
+	outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr);
+	outb (0x00, dev->fx_dsp_page);  
+	outb (0x10, dev->fx_dsp_addr);
+
+	for (i = 0; i < sizeof (page_zero_v2); i += 2) {
+		outb (page_zero_v2[i], dev->fx_dsp_msb);
+		outb (page_zero_v2[i+1], dev->fx_dsp_lsb);
+		if (!wavefront_fx_idle (dev)) return (-1);
+	}
+    
+	outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr);
+	outb (0x01, dev->fx_dsp_page);
+	outb (0x10, dev->fx_dsp_addr);
+
+	for (i = 0; i < sizeof (page_one_v2); i += 2) {
+		outb (page_one_v2[i], dev->fx_dsp_msb);
+		outb (page_one_v2[i+1], dev->fx_dsp_lsb);
+		if (!wavefront_fx_idle (dev)) return (-1);
+	}
+    
+	if (!wavefront_fx_idle (dev)) return (-1);
+	if (!wavefront_fx_idle (dev)) return (-1);
+    
+	outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr);
+	outb (0x02, dev->fx_dsp_page);
+	outb (0x10, dev->fx_dsp_addr);
+
+	for (i = 0; i < sizeof (page_two_v2); i++) {
+		outb (page_two_v2[i], dev->fx_dsp_lsb);
+		if (!wavefront_fx_idle (dev)) return (-1);
+	}
+	outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr);
+	outb (0x03, dev->fx_dsp_page);
+	outb (0x10, dev->fx_dsp_addr);
+
+	for (i = 0; i < sizeof (page_three_v2); i++) {
+		outb (page_three_v2[i], dev->fx_dsp_lsb);
+		if (!wavefront_fx_idle (dev)) return (-1);
+	}
+    
+	outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr);
+	outb (0x04, dev->fx_dsp_page);
+	outb (0x10, dev->fx_dsp_addr);
+
+	for (i = 0; i < sizeof (page_four_v2); i++) {
+		outb (page_four_v2[i], dev->fx_dsp_lsb);
+		if (!wavefront_fx_idle (dev)) return (-1);
+	}
+    
+	outb (FX_LSB_TRANSFER, dev->fx_lcr);
+	outb (0x06, dev->fx_dsp_page);
+
+	/* Page six v.2 is algorithmic */
+    
+	for (i = 0x10; i <= 0x3e; i += 2) {
+		outb (i, dev->fx_dsp_addr);
+		outb (0x00, dev->fx_dsp_msb);
+		outb (0x00, dev->fx_dsp_lsb);
+		if (!wavefront_fx_idle (dev)) return (-1);
+	}
+
+	outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev->fx_lcr);
+	outb (0x07, dev->fx_dsp_page);
+	outb (0x10, dev->fx_dsp_addr);
+
+	for (i = 0; i < sizeof (page_seven_v2); i += 2) {
+		outb (page_seven_v2[i], dev->fx_dsp_msb);
+		outb (page_seven_v2[i+1], dev->fx_dsp_lsb);
+		if (!wavefront_fx_idle (dev)) return (-1);
+	}
+
+	for (i = 0x00; i < sizeof(mod_v2); i += 2) {
+		outb (mod_v2[i], dev->fx_mod_addr);
+		outb (mod_v2[i+1], dev->fx_mod_data);
+		if (!wavefront_fx_idle (dev)) return (-1);
+	}
+
+	for (i = 0; i < sizeof (coefficients2); i += 4) {
+		outb (coefficients2[i], dev->fx_dsp_page);
+		outb (coefficients2[i+1], dev->fx_dsp_addr);
+		outb (coefficients2[i+2], dev->fx_dsp_msb);
+		outb (coefficients2[i+3], dev->fx_dsp_lsb);
+		if (!wavefront_fx_idle (dev)) return (-1);
+	}
+
+	for (i = 0; i < sizeof (coefficients3); i += 2) {
+		int x;
+
+		outb (0x07, dev->fx_dsp_page);
+		x = (i % 4) ? 0x4e : 0x4c;
+		outb (x, dev->fx_dsp_addr);
+		outb (coefficients3[i], dev->fx_dsp_msb);
+		outb (coefficients3[i+1], dev->fx_dsp_lsb);
+	}
+
+	outb (0x00, dev->fx_op); /* mute off */
+	if (!wavefront_fx_idle (dev)) return (-1);
+
+	return (0);
+}
+
+/* wierd stuff, derived from port I/O tracing with dosemu */
+
+unsigned char page_zero[] = {
+0x01, 0x7c, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf5, 0x00,
+0x11, 0x00, 0x20, 0x00, 0x32, 0x00, 0x40, 0x00, 0x13, 0x00, 0x00,
+0x00, 0x14, 0x02, 0x76, 0x00, 0x60, 0x00, 0x80, 0x02, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x19,
+0x01, 0x1a, 0x01, 0x20, 0x01, 0x40, 0x01, 0x17, 0x00, 0x00, 0x01,
+0x80, 0x01, 0x20, 0x00, 0x10, 0x01, 0xa0, 0x03, 0xd1, 0x00, 0x00,
+0x01, 0xf2, 0x02, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0xf4, 0x02,
+0xe0, 0x00, 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x17,
+0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x50, 0x00, 0x00, 0x00,
+0x40, 0x00, 0x00, 0x00, 0x71, 0x02, 0x00, 0x00, 0x60, 0x00, 0x00,
+0x00, 0x92, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xb3, 0x02,
+0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0xd4, 0x00, 0x00, 0x00, 0x40,
+0x00, 0x80, 0x00, 0xf5, 0x00, 0x20, 0x00, 0x70, 0x00, 0xa0, 0x02,
+0x11, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20,
+0x02, 0x00, 0x00, 0x20, 0x00, 0x10, 0x00, 0x17, 0x00, 0x1b, 0x00,
+0x1d, 0x02, 0xdf
+};    
+
+unsigned char page_one[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x19, 0x00,
+0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xd8, 0x00, 0x00,
+0x02, 0x20, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x01,
+0xc0, 0x01, 0xfa, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x40, 0x02, 0x60,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xc0, 0x02, 0x80, 0x00,
+0x00, 0x02, 0xfb, 0x02, 0xa0, 0x00, 0x00, 0x00, 0x1b, 0x02, 0xd7,
+0x00, 0x00, 0x02, 0xf7, 0x03, 0x20, 0x03, 0x00, 0x00, 0x00, 0x00,
+0x1c, 0x03, 0x3c, 0x00, 0x00, 0x03, 0x3f, 0x00, 0x00, 0x03, 0xc0,
+0x00, 0x00, 0x03, 0xdf, 0x00, 0x00, 0x00, 0x00, 0x03, 0x5d, 0x00,
+0x00, 0x03, 0xc0, 0x00, 0x00, 0x03, 0x7d, 0x00, 0x00, 0x03, 0xc0,
+0x00, 0x00, 0x03, 0x9e, 0x00, 0x00, 0x03, 0xc0, 0x00, 0x00, 0x03,
+0xbe, 0x00, 0x00, 0x03, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+0xdb, 0x00, 0x00, 0x02, 0xdb, 0x00, 0x00, 0x02, 0xe0, 0x00, 0x00,
+0x02, 0xfb, 0x00, 0x00, 0x02, 0xc0, 0x02, 0x40, 0x02, 0xfb, 0x02,
+0x60, 0x00, 0x1b
+};
+
+unsigned char page_two[] = {
+0xc4, 0x00, 0x44, 0x07, 0x44, 0x00, 0x40, 0x25, 0x01, 0x06, 0xc4,
+0x07, 0x40, 0x25, 0x01, 0x00, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x07,
+0x05, 0x05, 0x05, 0x04, 0x07, 0x05, 0x04, 0x07, 0x05, 0x44, 0x46,
+0x44, 0x46, 0x46, 0x07, 0x05, 0x44, 0x46, 0x05, 0x46, 0x05, 0x46,
+0x05, 0x46, 0x05, 0x44, 0x46, 0x05, 0x07, 0x44, 0x46, 0x05, 0x07,
+0x44, 0x46, 0x05, 0x07, 0x44, 0x46, 0x05, 0x07, 0x44, 0x05, 0x05,
+0x05, 0x44, 0x05, 0x05, 0x05, 0x46, 0x05, 0x46, 0x05, 0x46, 0x05,
+0x46, 0x05, 0x46, 0x07, 0x46, 0x07, 0x44
+};
+
+unsigned char page_three[] = {
+0x07, 0x40, 0x00, 0x00, 0x00, 0x47, 0x00, 0x40, 0x00, 0x40, 0x06,
+0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80,
+0xc0, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x40, 0x00,
+0x60, 0x00, 0x70, 0x00, 0x40, 0x00, 0x40, 0x00, 0x42, 0x00, 0x40,
+0x00, 0x02, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
+0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00,
+0x00, 0x42, 0x00, 0x40, 0x00, 0x42, 0x00, 0x02, 0x00, 0x02, 0x00,
+0x02, 0x00, 0x42, 0x00, 0xc0, 0x00, 0x40
+};
+
+unsigned char page_four[] = {
+0x63, 0x03, 0x26, 0x02, 0x2c, 0x00, 0x24, 0x00, 0x2e, 0x02, 0x02,
+0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+0x20, 0x00, 0x60, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20,
+0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x60, 0x00,
+0x20, 0x00, 0x60, 0x00, 0x20, 0x00, 0x60, 0x00, 0x20, 0x00, 0x60,
+0x00, 0x20, 0x00, 0x60, 0x00, 0x20, 0x00, 0x60, 0x00, 0x20, 0x00,
+0x20, 0x00, 0x22, 0x02, 0x22, 0x02, 0x20, 0x00, 0x60, 0x00, 0x22,
+0x02, 0x62, 0x02, 0x20, 0x01, 0x21, 0x01
+};
+
+unsigned char page_six[] = {
+0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x04, 0x00, 0x00, 0x06, 0x00,
+0x00, 0x08, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0e,
+0x00, 0x00, 0x10, 0x00, 0x00, 0x12, 0x00, 0x00, 0x14, 0x00, 0x00,
+0x16, 0x00, 0x00, 0x18, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x1c, 0x00,
+0x00, 0x1e, 0x00, 0x00, 0x20, 0x00, 0x00, 0x22, 0x00, 0x00, 0x24,
+0x00, 0x00, 0x26, 0x00, 0x00, 0x28, 0x00, 0x00, 0x2a, 0x00, 0x00,
+0x2c, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x30, 0x00, 0x00, 0x32, 0x00,
+0x00, 0x34, 0x00, 0x00, 0x36, 0x00, 0x00, 0x38, 0x00, 0x00, 0x3a,
+0x00, 0x00, 0x3c, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x40, 0x00, 0x00,
+0x42, 0x03, 0x00, 0x44, 0x01, 0x00, 0x46, 0x0a, 0x21, 0x48, 0x0d,
+0x23, 0x4a, 0x23, 0x1b, 0x4c, 0x37, 0x8f, 0x4e, 0x45, 0x77, 0x50,
+0x52, 0xe2, 0x52, 0x1c, 0x92, 0x54, 0x1c, 0x52, 0x56, 0x07, 0x00,
+0x58, 0x2f, 0xc6, 0x5a, 0x0b, 0x00, 0x5c, 0x30, 0x06, 0x5e, 0x17,
+0x00, 0x60, 0x3d, 0xda, 0x62, 0x29, 0x00, 0x64, 0x3e, 0x41, 0x66,
+0x39, 0x00, 0x68, 0x4c, 0x48, 0x6a, 0x49, 0x00, 0x6c, 0x4c, 0x6c,
+0x6e, 0x11, 0xd2, 0x70, 0x16, 0x0c, 0x72, 0x00, 0x00, 0x74, 0x00,
+0x80, 0x76, 0x0f, 0x00, 0x78, 0x00, 0x80, 0x7a, 0x13, 0x00, 0x7c,
+0x80, 0x00, 0x7e, 0x80, 0x80
+};
+
+unsigned char page_seven[] = {
+0x0f, 0xff, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0x02, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00,
+0x08, 0x00, 0x00, 0x00, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x0f,
+0xff, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0x0f, 0xff,
+0x0f, 0xff, 0x0f, 0xff, 0x02, 0xe9, 0x06, 0x8c, 0x06, 0x8c, 0x0f,
+0xff, 0x1a, 0x75, 0x0d, 0x8b, 0x04, 0xe9, 0x0b, 0x16, 0x1a, 0x38,
+0x0d, 0xc8, 0x04, 0x6f, 0x0b, 0x91, 0x0f, 0xff, 0x06, 0x40, 0x06,
+0x40, 0x02, 0x8f, 0x0f, 0xff, 0x06, 0x62, 0x06, 0x62, 0x02, 0x7b,
+0x0f, 0xff, 0x06, 0x97, 0x06, 0x97, 0x02, 0x52, 0x0f, 0xff, 0x06,
+0xf6, 0x06, 0xf6, 0x02, 0x19, 0x05, 0x55, 0x05, 0x55, 0x05, 0x55,
+0x05, 0x55, 0x05, 0x55, 0x05, 0x55, 0x05, 0x55, 0x05, 0x55, 0x14,
+0xda, 0x0d, 0x93, 0x04, 0xda, 0x05, 0x93, 0x14, 0xda, 0x0d, 0x93,
+0x04, 0xda, 0x05, 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x02, 0x00
+};
+
+unsigned char page_zero_v2[] = {
+0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+unsigned char page_one_v2[] = {
+0x01, 0xc0, 0x01, 0xfa, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+unsigned char page_two_v2[] = {
+0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00
+};
+unsigned char page_three_v2[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00
+};
+unsigned char page_four_v2[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00
+};
+
+unsigned char page_seven_v2[] = {
+0x0f, 0xff, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+unsigned char mod_v2[] = {
+0x01, 0x00, 0x02, 0x00, 0x01, 0x01, 0x02, 0x00, 0x01, 0x02, 0x02,
+0x00, 0x01, 0x03, 0x02, 0x00, 0x01, 0x04, 0x02, 0x00, 0x01, 0x05,
+0x02, 0x00, 0x01, 0x06, 0x02, 0x00, 0x01, 0x07, 0x02, 0x00, 0xb0,
+0x20, 0xb1, 0x20, 0xb2, 0x20, 0xb3, 0x20, 0xb4, 0x20, 0xb5, 0x20,
+0xb6, 0x20, 0xb7, 0x20, 0xf0, 0x20, 0xf1, 0x20, 0xf2, 0x20, 0xf3,
+0x20, 0xf4, 0x20, 0xf5, 0x20, 0xf6, 0x20, 0xf7, 0x20, 0x10, 0xff,
+0x11, 0xff, 0x12, 0xff, 0x13, 0xff, 0x14, 0xff, 0x15, 0xff, 0x16,
+0xff, 0x17, 0xff, 0x20, 0xff, 0x21, 0xff, 0x22, 0xff, 0x23, 0xff,
+0x24, 0xff, 0x25, 0xff, 0x26, 0xff, 0x27, 0xff, 0x30, 0x00, 0x31,
+0x00, 0x32, 0x00, 0x33, 0x00, 0x34, 0x00, 0x35, 0x00, 0x36, 0x00,
+0x37, 0x00, 0x40, 0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x44,
+0x00, 0x45, 0x00, 0x46, 0x00, 0x47, 0x00, 0x50, 0x00, 0x51, 0x00,
+0x52, 0x00, 0x53, 0x00, 0x54, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57,
+0x00, 0x60, 0x00, 0x61, 0x00, 0x62, 0x00, 0x63, 0x00, 0x64, 0x00,
+0x65, 0x00, 0x66, 0x00, 0x67, 0x00, 0x70, 0xc0, 0x71, 0xc0, 0x72,
+0xc0, 0x73, 0xc0, 0x74, 0xc0, 0x75, 0xc0, 0x76, 0xc0, 0x77, 0xc0,
+0x80, 0x00, 0x81, 0x00, 0x82, 0x00, 0x83, 0x00, 0x84, 0x00, 0x85,
+0x00, 0x86, 0x00, 0x87, 0x00, 0x90, 0x00, 0x91, 0x00, 0x92, 0x00,
+0x93, 0x00, 0x94, 0x00, 0x95, 0x00, 0x96, 0x00, 0x97, 0x00, 0xa0,
+0x00, 0xa1, 0x00, 0xa2, 0x00, 0xa3, 0x00, 0xa4, 0x00, 0xa5, 0x00,
+0xa6, 0x00, 0xa7, 0x00, 0xc0, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0xc3,
+0x00, 0xc4, 0x00, 0xc5, 0x00, 0xc6, 0x00, 0xc7, 0x00, 0xd0, 0x00,
+0xd1, 0x00, 0xd2, 0x00, 0xd3, 0x00, 0xd4, 0x00, 0xd5, 0x00, 0xd6,
+0x00, 0xd7, 0x00, 0xe0, 0x00, 0xe1, 0x00, 0xe2, 0x00, 0xe3, 0x00,
+0xe4, 0x00, 0xe5, 0x00, 0xe6, 0x00, 0xe7, 0x00, 0x01, 0x00, 0x02,
+0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x03,
+0x02, 0x01, 0x01, 0x04, 0x02, 0x01, 0x01, 0x05, 0x02, 0x01, 0x01,
+0x06, 0x02, 0x01, 0x01, 0x07, 0x02, 0x01
+};
+unsigned char coefficients[] = {
+0x07, 0x46, 0x00, 0x00, 0x07, 0x49, 0x00, 0x00, 0x00, 0x4b, 0x03,
+0x11, 0x00, 0x4d, 0x01, 0x32, 0x07, 0x46, 0x00, 0x00, 0x07, 0x49,
+0x00, 0x00, 0x07, 0x40, 0x00, 0x00, 0x07, 0x41, 0x00, 0x00, 0x01,
+0x40, 0x02, 0x40, 0x01, 0x41, 0x02, 0x60, 0x07, 0x40, 0x00, 0x00,
+0x07, 0x41, 0x00, 0x00, 0x07, 0x47, 0x00, 0x00, 0x07, 0x4a, 0x00,
+0x00, 0x00, 0x47, 0x01, 0x00, 0x00, 0x4a, 0x01, 0x20, 0x07, 0x47,
+0x00, 0x00, 0x07, 0x4a, 0x00, 0x00, 0x07, 0x7c, 0x00, 0x00, 0x07,
+0x7e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x1c, 0x07, 0x7c, 0x00, 0x00,
+0x07, 0x7e, 0x00, 0x00, 0x07, 0x44, 0x00, 0x00, 0x00, 0x44, 0x01,
+0x00, 0x07, 0x44, 0x00, 0x00, 0x07, 0x42, 0x00, 0x00, 0x07, 0x43,
+0x00, 0x00, 0x00, 0x42, 0x01, 0x1a, 0x00, 0x43, 0x01, 0x20, 0x07,
+0x42, 0x00, 0x00, 0x07, 0x43, 0x00, 0x00, 0x07, 0x40, 0x00, 0x00,
+0x07, 0x41, 0x00, 0x00, 0x01, 0x40, 0x02, 0x40, 0x01, 0x41, 0x02,
+0x60, 0x07, 0x40, 0x00, 0x00, 0x07, 0x41, 0x00, 0x00, 0x07, 0x44,
+0x0f, 0xff, 0x07, 0x42, 0x00, 0x00, 0x07, 0x43, 0x00, 0x00, 0x07,
+0x40, 0x00, 0x00, 0x07, 0x41, 0x00, 0x00, 0x07, 0x51, 0x06, 0x40,
+0x07, 0x50, 0x06, 0x40, 0x07, 0x4f, 0x03, 0x81, 0x07, 0x53, 0x1a,
+0x76, 0x07, 0x54, 0x0d, 0x8b, 0x07, 0x55, 0x04, 0xe9, 0x07, 0x56,
+0x0b, 0x17, 0x07, 0x57, 0x1a, 0x38, 0x07, 0x58, 0x0d, 0xc9, 0x07,
+0x59, 0x04, 0x6f, 0x07, 0x5a, 0x0b, 0x91, 0x07, 0x73, 0x14, 0xda,
+0x07, 0x74, 0x0d, 0x93, 0x07, 0x75, 0x04, 0xd9, 0x07, 0x76, 0x05,
+0x93, 0x07, 0x77, 0x14, 0xda, 0x07, 0x78, 0x0d, 0x93, 0x07, 0x79,
+0x04, 0xd9, 0x07, 0x7a, 0x05, 0x93, 0x07, 0x5e, 0x03, 0x68, 0x07,
+0x5c, 0x04, 0x31, 0x07, 0x5d, 0x04, 0x31, 0x07, 0x62, 0x03, 0x52,
+0x07, 0x60, 0x04, 0x76, 0x07, 0x61, 0x04, 0x76, 0x07, 0x66, 0x03,
+0x2e, 0x07, 0x64, 0x04, 0xda, 0x07, 0x65, 0x04, 0xda, 0x07, 0x6a,
+0x02, 0xf6, 0x07, 0x68, 0x05, 0x62, 0x07, 0x69, 0x05, 0x62, 0x06,
+0x46, 0x0a, 0x22, 0x06, 0x48, 0x0d, 0x24, 0x06, 0x6e, 0x11, 0xd3,
+0x06, 0x70, 0x15, 0xcb, 0x06, 0x52, 0x20, 0x93, 0x06, 0x54, 0x20,
+0x54, 0x06, 0x4a, 0x27, 0x1d, 0x06, 0x58, 0x2f, 0xc8, 0x06, 0x5c,
+0x30, 0x07, 0x06, 0x4c, 0x37, 0x90, 0x06, 0x60, 0x3d, 0xdb, 0x06,
+0x64, 0x3e, 0x42, 0x06, 0x4e, 0x45, 0x78, 0x06, 0x68, 0x4c, 0x48,
+0x06, 0x6c, 0x4c, 0x6c, 0x06, 0x50, 0x52, 0xe2, 0x06, 0x42, 0x02,
+0xba
+};
+unsigned char coefficients2[] = {
+0x07, 0x46, 0x00, 0x00, 0x07, 0x49, 0x00, 0x00, 0x07, 0x45, 0x0f,
+0xff, 0x07, 0x48, 0x0f, 0xff, 0x07, 0x7b, 0x04, 0xcc, 0x07, 0x7d,
+0x04, 0xcc, 0x07, 0x7c, 0x00, 0x00, 0x07, 0x7e, 0x00, 0x00, 0x07,
+0x46, 0x00, 0x00, 0x07, 0x49, 0x00, 0x00, 0x07, 0x47, 0x00, 0x00,
+0x07, 0x4a, 0x00, 0x00, 0x07, 0x4c, 0x00, 0x00, 0x07, 0x4e, 0x00, 0x00
+};
+unsigned char coefficients3[] = { 
+0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x28, 0x00, 0x51, 0x00,
+0x51, 0x00, 0x7a, 0x00, 0x7a, 0x00, 0xa3, 0x00, 0xa3, 0x00, 0xcc,
+0x00, 0xcc, 0x00, 0xf5, 0x00, 0xf5, 0x01, 0x1e, 0x01, 0x1e, 0x01,
+0x47, 0x01, 0x47, 0x01, 0x70, 0x01, 0x70, 0x01, 0x99, 0x01, 0x99,
+0x01, 0xc2, 0x01, 0xc2, 0x01, 0xeb, 0x01, 0xeb, 0x02, 0x14, 0x02,
+0x14, 0x02, 0x3d, 0x02, 0x3d, 0x02, 0x66, 0x02, 0x66, 0x02, 0x8f,
+0x02, 0x8f, 0x02, 0xb8, 0x02, 0xb8, 0x02, 0xe1, 0x02, 0xe1, 0x03,
+0x0a, 0x03, 0x0a, 0x03, 0x33, 0x03, 0x33, 0x03, 0x5c, 0x03, 0x5c,
+0x03, 0x85, 0x03, 0x85, 0x03, 0xae, 0x03, 0xae, 0x03, 0xd7, 0x03,
+0xd7, 0x04, 0x00, 0x04, 0x00, 0x04, 0x28, 0x04, 0x28, 0x04, 0x51,
+0x04, 0x51, 0x04, 0x7a, 0x04, 0x7a, 0x04, 0xa3, 0x04, 0xa3, 0x04,
+0xcc, 0x04, 0xcc, 0x04, 0xf5, 0x04, 0xf5, 0x05, 0x1e, 0x05, 0x1e,
+0x05, 0x47, 0x05, 0x47, 0x05, 0x70, 0x05, 0x70, 0x05, 0x99, 0x05,
+0x99, 0x05, 0xc2, 0x05, 0xc2, 0x05, 0xeb, 0x05, 0xeb, 0x06, 0x14,
+0x06, 0x14, 0x06, 0x3d, 0x06, 0x3d, 0x06, 0x66, 0x06, 0x66, 0x06,
+0x8f, 0x06, 0x8f, 0x06, 0xb8, 0x06, 0xb8, 0x06, 0xe1, 0x06, 0xe1,
+0x07, 0x0a, 0x07, 0x0a, 0x07, 0x33, 0x07, 0x33, 0x07, 0x5c, 0x07,
+0x5c, 0x07, 0x85, 0x07, 0x85, 0x07, 0xae, 0x07, 0xae, 0x07, 0xd7,
+0x07, 0xd7, 0x08, 0x00, 0x08, 0x00, 0x08, 0x28, 0x08, 0x28, 0x08,
+0x51, 0x08, 0x51, 0x08, 0x7a, 0x08, 0x7a, 0x08, 0xa3, 0x08, 0xa3,
+0x08, 0xcc, 0x08, 0xcc, 0x08, 0xf5, 0x08, 0xf5, 0x09, 0x1e, 0x09,
+0x1e, 0x09, 0x47, 0x09, 0x47, 0x09, 0x70, 0x09, 0x70, 0x09, 0x99,
+0x09, 0x99, 0x09, 0xc2, 0x09, 0xc2, 0x09, 0xeb, 0x09, 0xeb, 0x0a,
+0x14, 0x0a, 0x14, 0x0a, 0x3d, 0x0a, 0x3d, 0x0a, 0x66, 0x0a, 0x66,
+0x0a, 0x8f, 0x0a, 0x8f, 0x0a, 0xb8, 0x0a, 0xb8, 0x0a, 0xe1, 0x0a,
+0xe1, 0x0b, 0x0a, 0x0b, 0x0a, 0x0b, 0x33, 0x0b, 0x33, 0x0b, 0x5c,
+0x0b, 0x5c, 0x0b, 0x85, 0x0b, 0x85, 0x0b, 0xae, 0x0b, 0xae, 0x0b,
+0xd7, 0x0b, 0xd7, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x28, 0x0c, 0x28,
+0x0c, 0x51, 0x0c, 0x51, 0x0c, 0x7a, 0x0c, 0x7a, 0x0c, 0xa3, 0x0c,
+0xa3, 0x0c, 0xcc, 0x0c, 0xcc, 0x0c, 0xf5, 0x0c, 0xf5, 0x0d, 0x1e,
+0x0d, 0x1e, 0x0d, 0x47, 0x0d, 0x47, 0x0d, 0x70, 0x0d, 0x70, 0x0d,
+0x99, 0x0d, 0x99, 0x0d, 0xc2, 0x0d, 0xc2, 0x0d, 0xeb, 0x0d, 0xeb,
+0x0e, 0x14, 0x0e, 0x14, 0x0e, 0x3d, 0x0e, 0x3d, 0x0e, 0x66, 0x0e,
+0x66, 0x0e, 0x8f, 0x0e, 0x8f, 0x0e, 0xb8, 0x0e, 0xb8, 0x0e, 0xe1,
+0x0e, 0xe1, 0x0f, 0x0a, 0x0f, 0x0a, 0x0f, 0x33, 0x0f, 0x33, 0x0f,
+0x5c, 0x0f, 0x5c, 0x0f, 0x85, 0x0f, 0x85, 0x0f, 0xae, 0x0f, 0xae,
+0x0f, 0xd7, 0x0f, 0xd7, 0x0f, 0xff, 0x0f, 0xff
+};
+
+EXPORT_SYMBOL(snd_wavefront_fx_start);
+EXPORT_SYMBOL(snd_wavefront_fx_detect);
+EXPORT_SYMBOL(snd_wavefront_fx_ioctl);
+EXPORT_SYMBOL(snd_wavefront_fx_open);
+EXPORT_SYMBOL(snd_wavefront_fx_release);
+
+static int __init alsa_wavefront_fx_init(void)
+{
+	return 0;
+}
+
+static void __exit alsa_wavefront_fx_exit(void)
+{
+}
+
+module_init(alsa_wavefront_fx_init)
+module_exit(alsa_wavefront_fx_exit)
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/wavefront/wavefront_midi.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/wavefront/wavefront_midi.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/wavefront/wavefront_midi.c	(revision 91)
@@ -0,0 +1,568 @@
+/*
+ * Copyright (C) by Paul Barton-Davis 1998-1999
+ *
+ * This file is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
+ * Version 2 (June 1991). See the "COPYING" file distributed with this
+ * software for more info.  
+ */
+
+/* The low level driver for the WaveFront ICS2115 MIDI interface(s)
+ *
+ * Note that there is also an MPU-401 emulation (actually, a UART-401
+ * emulation) on the CS4232 on the Tropez and Tropez Plus. This code
+ * has nothing to do with that interface at all.
+ *
+ * The interface is essentially just a UART-401, but is has the
+ * interesting property of supporting what Turtle Beach called
+ * "Virtual MIDI" mode. In this mode, there are effectively *two*
+ * MIDI buses accessible via the interface, one that is routed
+ * solely to/from the external WaveFront synthesizer and the other
+ * corresponding to the pin/socket connector used to link external
+ * MIDI devices to the board.
+ *
+ * This driver fully supports this mode, allowing two distinct MIDI
+ * busses to be used completely independently, giving 32 channels of
+ * MIDI routing, 16 to the WaveFront synth and 16 to the external MIDI
+ * bus. The devices are named /dev/snd/midiCnD0 and /dev/snd/midiCnD1,
+ * where `n' is the card number. Note that the device numbers may be
+ * something other than 0 and 1 if the CS4232 UART/MPU-401 interface
+ * is enabled.
+ *
+ * Switching between the two is accomplished externally by the driver
+ * using the two otherwise unused MIDI bytes. See the code for more details.
+ *
+ * NOTE: VIRTUAL MIDI MODE IS ON BY DEFAULT (see lowlevel/isa/wavefront.c)
+ *
+ * The main reason to turn off Virtual MIDI mode is when you want to
+ * tightly couple the WaveFront synth with an external MIDI
+ * device. You won't be able to distinguish the source of any MIDI
+ * data except via SysEx ID, but thats probably OK, since for the most
+ * part, the WaveFront won't be sending any MIDI data at all.
+ *  
+ * The main reason to turn on Virtual MIDI Mode is to provide two
+ * completely independent 16-channel MIDI buses, one to the
+ * WaveFront and one to any external MIDI devices. Given the 32
+ * voice nature of the WaveFront, its pretty easy to find a use
+ * for all 16 channels driving just that synth.
+ *  
+ */
+
+#include <sound/driver.h>
+#include <sound/snd_wavefront.h>
+
+static inline int 
+wf_mpu_status (snd_wavefront_midi_t *midi)
+
+{
+	return inb (midi->mpu_status_port);
+}
+
+static inline int 
+input_avail (snd_wavefront_midi_t *midi)
+
+{
+	return !(wf_mpu_status(midi) & INPUT_AVAIL);
+}
+
+static inline int
+output_ready (snd_wavefront_midi_t *midi)
+
+{
+	return !(wf_mpu_status(midi) & OUTPUT_READY);
+}
+
+static inline int 
+read_data (snd_wavefront_midi_t *midi)
+
+{
+	return inb (midi->mpu_data_port);
+}
+
+static inline void 
+write_data (snd_wavefront_midi_t *midi, unsigned char byte)
+
+{
+	outb (byte, midi->mpu_data_port);
+}
+
+static snd_wavefront_midi_t *
+get_wavefront_midi (snd_rawmidi_substream_t *substream)
+
+{
+	snd_card_t *card;
+	snd_wavefront_card_t *acard;
+
+	if (substream == NULL || substream->rmidi == NULL) 
+	        return NULL;
+
+	card = substream->rmidi->card;
+
+	if (card == NULL) 
+	        return NULL;
+
+	if (card->private_data == NULL) 
+ 	        return NULL;
+
+	acard = card->private_data;
+
+	return &acard->wavefront.midi;
+}
+
+static void snd_wavefront_midi_output_write(snd_wavefront_card_t *card)
+{
+	snd_wavefront_midi_t *midi = &card->wavefront.midi;
+	snd_wavefront_mpu_id  mpu;
+	unsigned long flags;
+	unsigned char midi_byte;
+	int max = 256, mask = 1;
+	int timeout;
+
+	/* Its not OK to try to change the status of "virtuality" of
+	   the MIDI interface while we're outputting stuff.  See
+	   snd_wavefront_midi_{enable,disable}_virtual () for the
+	   other half of this.  
+
+	   The first loop attempts to flush any data from the
+	   current output device, and then the second 
+	   emits the switch byte (if necessary), and starts
+	   outputting data for the output device currently in use.
+	*/
+
+	if (midi->substream_output[midi->output_mpu] == NULL) {
+		goto __second;
+	}
+
+	while (max > 0) {
+
+		/* XXX fix me - no hard timing loops allowed! */
+
+		for (timeout = 30000; timeout > 0; timeout--) {
+			if (output_ready (midi))
+				break;
+		}
+	
+		spin_lock_irqsave (&midi->virtual, flags);
+		if ((midi->mode[midi->output_mpu] & MPU401_MODE_OUTPUT) == 0) {
+			spin_unlock_irqrestore (&midi->virtual, flags);
+			goto __second;
+		}
+		if (output_ready (midi)) {
+			if (snd_rawmidi_transmit(midi->substream_output[midi->output_mpu], &midi_byte, 1) == 1) {
+				if (!midi->isvirtual ||
+					(midi_byte != WF_INTERNAL_SWITCH &&
+					 midi_byte != WF_EXTERNAL_SWITCH))
+					write_data(midi, midi_byte);
+				max--;
+			} else {
+				if (midi->istimer) {
+					if (--midi->istimer <= 0)
+						del_timer(&midi->timer);
+				}
+				midi->mode[midi->output_mpu] &= ~MPU401_MODE_OUTPUT_TRIGGER;
+				spin_unlock_irqrestore (&midi->virtual, flags);
+				goto __second;
+			}
+		} else {
+			spin_unlock_irqrestore (&midi->virtual, flags);
+			return;
+		}
+		spin_unlock_irqrestore (&midi->virtual, flags);
+	}
+
+      __second:
+
+	if (midi->substream_output[!midi->output_mpu] == NULL) {
+		return;
+	}
+
+	while (max > 0) {
+
+		/* XXX fix me - no hard timing loops allowed! */
+
+		for (timeout = 30000; timeout > 0; timeout--) {
+			if (output_ready (midi))
+				break;
+		}
+	
+		spin_lock_irqsave (&midi->virtual, flags);
+		if (!midi->isvirtual)
+			mask = 0;
+		mpu = midi->output_mpu ^ mask;
+		mask = 0;	/* don't invert the value from now */
+		if ((midi->mode[mpu] & MPU401_MODE_OUTPUT) == 0) {
+			spin_unlock_irqrestore (&midi->virtual, flags);
+			return;
+		}
+		if (snd_rawmidi_transmit_empty(midi->substream_output[mpu]))
+			goto __timer;
+		if (output_ready (midi)) {
+			if (mpu != midi->output_mpu) {
+				write_data(midi, mpu == internal_mpu ?
+							WF_INTERNAL_SWITCH :
+							WF_EXTERNAL_SWITCH);
+				midi->output_mpu = mpu;
+			} else if (snd_rawmidi_transmit(midi->substream_output[mpu], &midi_byte, 1) == 1) {
+				if (!midi->isvirtual ||
+					(midi_byte != WF_INTERNAL_SWITCH &&
+					 midi_byte != WF_EXTERNAL_SWITCH))
+					write_data(midi, midi_byte);
+				max--;
+			} else {
+			      __timer:
+				if (midi->istimer) {
+					if (--midi->istimer <= 0)
+						del_timer(&midi->timer);
+				}
+				midi->mode[mpu] &= ~MPU401_MODE_OUTPUT_TRIGGER;
+				spin_unlock_irqrestore (&midi->virtual, flags);
+				return;
+			}
+		} else {
+			spin_unlock_irqrestore (&midi->virtual, flags);
+			return;
+		}
+		spin_unlock_irqrestore (&midi->virtual, flags);
+	}
+}
+
+static int snd_wavefront_midi_input_open(snd_rawmidi_substream_t * substream)
+{
+	unsigned long flags;
+	snd_wavefront_midi_t *midi;
+	snd_wavefront_mpu_id mpu;
+
+	snd_assert(substream != NULL && substream->rmidi != NULL, return -EIO);
+	snd_assert(substream->rmidi->private_data != NULL, return -EIO);
+
+	mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data);
+
+	if ((midi = get_wavefront_midi (substream)) == NULL)
+	        return -EIO;
+
+	spin_lock_irqsave (&midi->open, flags);
+	midi->mode[mpu] |= MPU401_MODE_INPUT;
+	midi->substream_input[mpu] = substream;
+	spin_unlock_irqrestore (&midi->open, flags);
+
+	return 0;
+}
+
+static int snd_wavefront_midi_output_open(snd_rawmidi_substream_t * substream)
+{
+	unsigned long flags;
+	snd_wavefront_midi_t *midi;
+	snd_wavefront_mpu_id mpu;
+
+	snd_assert(substream != NULL && substream->rmidi != NULL, return -EIO);
+	snd_assert(substream->rmidi->private_data != NULL, return -EIO);
+
+	mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data);
+
+	if ((midi = get_wavefront_midi (substream)) == NULL)
+	        return -EIO;
+
+	spin_lock_irqsave (&midi->open, flags);
+	midi->mode[mpu] |= MPU401_MODE_OUTPUT;
+	midi->substream_output[mpu] = substream;
+	spin_unlock_irqrestore (&midi->open, flags);
+
+	return 0;
+}
+
+static int snd_wavefront_midi_input_close(snd_rawmidi_substream_t * substream)
+{
+	unsigned long flags;
+	snd_wavefront_midi_t *midi;
+	snd_wavefront_mpu_id mpu;
+
+	snd_assert(substream != NULL && substream->rmidi != NULL, return -EIO);
+	snd_assert(substream->rmidi->private_data != NULL, return -EIO);
+
+	mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data);
+
+	if ((midi = get_wavefront_midi (substream)) == NULL)
+	        return -EIO;
+
+	spin_lock_irqsave (&midi->open, flags);
+	midi->mode[mpu] &= ~MPU401_MODE_INPUT;
+	spin_unlock_irqrestore (&midi->open, flags);
+
+	return 0;
+}
+
+static int snd_wavefront_midi_output_close(snd_rawmidi_substream_t * substream)
+{
+	unsigned long flags;
+	snd_wavefront_midi_t *midi;
+	snd_wavefront_mpu_id mpu;
+
+	snd_assert(substream != NULL && substream->rmidi != NULL, return -EIO);
+	snd_assert(substream->rmidi->private_data != NULL, return -EIO);
+
+	mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data);
+
+	if ((midi = get_wavefront_midi (substream)) == NULL)
+	        return -EIO;
+
+	spin_lock_irqsave (&midi->open, flags);
+	midi->mode[mpu] &= ~MPU401_MODE_OUTPUT;
+	spin_unlock_irqrestore (&midi->open, flags);
+	return 0;
+}
+
+static void snd_wavefront_midi_input_trigger(snd_rawmidi_substream_t * substream, int up)
+{
+	unsigned long flags;
+	snd_wavefront_midi_t *midi;
+	snd_wavefront_mpu_id mpu;
+
+	if (substream == NULL || substream->rmidi == NULL) 
+	        return;
+
+	if (substream->rmidi->private_data == NULL)
+	        return;
+
+	mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data);
+
+	if ((midi = get_wavefront_midi (substream)) == NULL) {
+		return;
+	}
+
+	spin_lock_irqsave (&midi->virtual, flags);
+	if (up) {
+		midi->mode[mpu] |= MPU401_MODE_INPUT_TRIGGER;
+	} else {
+		midi->mode[mpu] &= ~MPU401_MODE_INPUT_TRIGGER;
+	}
+	spin_unlock_irqrestore (&midi->virtual, flags);
+}
+
+static void snd_wavefront_midi_output_timer(unsigned long data)
+{
+	snd_wavefront_card_t *card = (snd_wavefront_card_t *)data;
+	snd_wavefront_midi_t *midi = &card->wavefront.midi;
+	unsigned long flags;
+	
+	spin_lock_irqsave (&midi->virtual, flags);
+	midi->timer.expires = 1 + jiffies;
+	add_timer(&midi->timer);
+	spin_unlock_irqrestore (&midi->virtual, flags);
+	snd_wavefront_midi_output_write(card);
+}
+
+static void snd_wavefront_midi_output_trigger(snd_rawmidi_substream_t * substream, int up)
+{
+	unsigned long flags;
+	snd_wavefront_midi_t *midi;
+	snd_wavefront_mpu_id mpu;
+
+	if (substream == NULL || substream->rmidi == NULL) 
+	        return;
+
+	if (substream->rmidi->private_data == NULL)
+	        return;
+
+	mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data);
+
+	if ((midi = get_wavefront_midi (substream)) == NULL) {
+		return;
+	}
+
+	spin_lock_irqsave (&midi->virtual, flags);
+	if (up) {
+		if ((midi->mode[mpu] & MPU401_MODE_OUTPUT_TRIGGER) == 0) {
+			if (!midi->istimer) {
+				midi->timer.function = snd_wavefront_midi_output_timer;
+				midi->timer.data = (unsigned long) substream->rmidi->card->private_data;
+				midi->timer.expires = 1 + jiffies;
+				add_timer(&midi->timer);
+			}
+			midi->istimer++;
+			midi->mode[mpu] |= MPU401_MODE_OUTPUT_TRIGGER;
+		}
+	} else {
+		midi->mode[mpu] &= ~MPU401_MODE_OUTPUT_TRIGGER;
+	}
+	spin_unlock_irqrestore (&midi->virtual, flags);
+
+	if (up)
+		snd_wavefront_midi_output_write((snd_wavefront_card_t *)substream->rmidi->card->private_data);
+}
+
+void
+snd_wavefront_midi_interrupt (snd_wavefront_card_t *card)
+
+{
+	unsigned long flags;
+	snd_wavefront_midi_t *midi;
+	static snd_rawmidi_substream_t *substream = NULL;
+	static int mpu = external_mpu; 
+	int max = 128;
+	unsigned char byte;
+
+	midi = &card->wavefront.midi;
+
+	if (!input_avail (midi)) { /* not for us */
+		snd_wavefront_midi_output_write(card);
+		return;
+	}
+
+	while (--max) {
+		spin_lock_irqsave (&midi->virtual, flags);
+
+		if (input_avail (midi)) {
+			byte = read_data (midi);
+
+			if (midi->isvirtual) {				
+				if (byte == WF_EXTERNAL_SWITCH) {
+					substream = midi->substream_input[external_mpu];
+					mpu = external_mpu;
+				} else if (byte == WF_INTERNAL_SWITCH) { 
+					substream = midi->substream_output[internal_mpu];
+					mpu = internal_mpu;
+				} /* else just leave it as it is */
+			} else {
+				substream = midi->substream_input[internal_mpu];
+				mpu = internal_mpu;
+			}
+
+			if (substream == NULL) {
+				spin_unlock_irqrestore (&midi->virtual, flags);
+				continue;
+			}
+
+			if (midi->mode[mpu] & MPU401_MODE_INPUT_TRIGGER) {
+				spin_unlock_irqrestore (&midi->virtual, flags);
+				snd_rawmidi_receive(substream, &byte, 1);
+			} else {
+				spin_unlock_irqrestore (&midi->virtual, flags);
+			}
+		} else {
+			spin_unlock_irqrestore (&midi->virtual, flags);
+			break;
+		}
+	} 
+
+	snd_wavefront_midi_output_write(card);
+}
+
+void
+snd_wavefront_midi_enable_virtual (snd_wavefront_card_t *card)
+
+{
+	unsigned long flags;
+
+	spin_lock_irqsave (&card->wavefront.midi.virtual, flags);
+	card->wavefront.midi.isvirtual = 1;
+	card->wavefront.midi.output_mpu = internal_mpu;
+	card->wavefront.midi.input_mpu = internal_mpu;
+	spin_unlock_irqrestore (&card->wavefront.midi.virtual, flags);
+}
+
+void
+snd_wavefront_midi_disable_virtual (snd_wavefront_card_t *card)
+
+{
+	unsigned long flags;
+
+	spin_lock_irqsave (&card->wavefront.midi.virtual, flags);
+	// snd_wavefront_midi_input_close (card->ics2115_external_rmidi);
+	// snd_wavefront_midi_output_close (card->ics2115_external_rmidi);
+	card->wavefront.midi.isvirtual = 0;
+	spin_unlock_irqrestore (&card->wavefront.midi.virtual, flags);
+}
+
+int
+snd_wavefront_midi_start (snd_wavefront_card_t *card)
+
+{
+	int ok, i;
+	unsigned char rbuf[4], wbuf[4];
+	snd_wavefront_t *dev;
+	snd_wavefront_midi_t *midi;
+
+	dev = &card->wavefront;
+	midi = &dev->midi;
+
+	/* The ICS2115 MPU-401 interface doesn't do anything
+	   until its set into UART mode.
+	*/
+
+	/* XXX fix me - no hard timing loops allowed! */
+
+	for (i = 0; i < 30000 && !output_ready (midi); i++);
+
+	if (!output_ready (midi)) {
+		snd_printk ("MIDI interface not ready for command\n");
+		return -1;
+	}
+
+	/* Any interrupts received from now on
+	   are owned by the MIDI side of things.
+	*/
+
+	dev->interrupts_are_midi = 1;
+	
+	outb (UART_MODE_ON, midi->mpu_command_port);
+
+	for (ok = 0, i = 50000; i > 0 && !ok; i--) {
+		if (input_avail (midi)) {
+			if (read_data (midi) == MPU_ACK) {
+				ok = 1;
+				break;
+			}
+		}
+	}
+
+	if (!ok) {
+		snd_printk ("cannot set UART mode for MIDI interface");
+		dev->interrupts_are_midi = 0;
+		return -1;
+	}
+
+	/* Route external MIDI to WaveFront synth (by default) */
+    
+	if (snd_wavefront_cmd (dev, WFC_MISYNTH_ON, rbuf, wbuf)) {
+		snd_printk ("can't enable MIDI-IN-2-synth routing.\n");
+		/* XXX error ? */
+	}
+
+	/* Turn on Virtual MIDI, but first *always* turn it off,
+	   since otherwise consectutive reloads of the driver will
+	   never cause the hardware to generate the initial "internal" or 
+	   "external" source bytes in the MIDI data stream. This
+	   is pretty important, since the internal hardware generally will
+	   be used to generate none or very little MIDI output, and
+	   thus the only source of MIDI data is actually external. Without
+	   the switch bytes, the driver will think it all comes from
+	   the internal interface. Duh.
+	*/
+
+	if (snd_wavefront_cmd (dev, WFC_VMIDI_OFF, rbuf, wbuf)) { 
+		snd_printk ("virtual MIDI mode not disabled\n");
+		return 0; /* We're OK, but missing the external MIDI dev */
+	}
+
+	snd_wavefront_midi_enable_virtual (card);
+
+	if (snd_wavefront_cmd (dev, WFC_VMIDI_ON, rbuf, wbuf)) {
+		snd_printk ("cannot enable virtual MIDI mode.\n");
+		snd_wavefront_midi_disable_virtual (card);
+	} 
+	return 0;
+}
+
+snd_rawmidi_ops_t snd_wavefront_midi_output =
+{
+	open:		snd_wavefront_midi_output_open,
+	close:		snd_wavefront_midi_output_close,
+	trigger:	snd_wavefront_midi_output_trigger,
+};
+
+snd_rawmidi_ops_t snd_wavefront_midi_input =
+{
+	open:		snd_wavefront_midi_input_open,
+	close:		snd_wavefront_midi_input_close,
+	trigger:	snd_wavefront_midi_input_trigger,
+};
+
Index: /GPL/branches/alsa-resync1/alsa-kernel/isa/wavefront/wavefront_synth.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/isa/wavefront/wavefront_synth.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/isa/wavefront/wavefront_synth.c	(revision 91)
@@ -0,0 +1,2321 @@
+/* Copyright (C) by Paul Barton-Davis 1998-1999
+ *
+ * Some portions of this file are taken from work that is
+ * copyright (C) by Hannu Savolainen 1993-1996
+ *
+ * This program is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
+ * Version 2 (June 1991). See the "COPYING" file distributed with this software
+ * for more info.  
+ */
+
+/*  
+ * An ALSA lowlevel driver for Turtle Beach ICS2115 wavetable synth
+ *                                             (Maui, Tropez, Tropez Plus)
+ *
+ * This driver supports the onboard wavetable synthesizer (an ICS2115),
+ * including patch, sample and program loading and unloading, conversion
+ * of GUS patches during loading, and full user-level access to all
+ * WaveFront commands. It tries to provide semi-intelligent patch and
+ * sample management as well.
+ *
+ */
+
+#define SNDRV_MAIN_OBJECT_FILE
+#include <sound/driver.h>
+#include <sound/snd_wavefront.h>
+#include <sound/initval.h>
+
+int wf_raw = 0; /* we normally check for "raw state" to firmware
+		   loading. if non-zero, then during driver loading, the
+		   state of the board is ignored, and we reset the
+		   board and load the firmware anyway.
+		*/
+		   
+int fx_raw = 1; /* if this is zero, we'll leave the FX processor in
+		   whatever state it is when the driver is loaded.
+		   The default is to download the microprogram and
+		   associated coefficients to set it up for "default"
+		   operation, whatever that means.
+		*/
+
+int debug_default = 0;  /* you can set this to control debugging
+			      during driver loading. it takes any combination
+			      of the WF_DEBUG_* flags defined in
+			      wavefront.h
+			   */
+
+/* XXX this needs to be made firmware and hardware version dependent */
+
+char *ospath = "/etc/sound/wavefront.os"; /* where to find a processed
+					     version of the WaveFront OS
+					  */
+
+int wait_usecs = 150; /* This magic number seems to give pretty optimal
+			 throughput based on my limited experimentation.
+			 If you want to play around with it and find a better
+			 value, be my guest. Remember, the idea is to
+			 get a number that causes us to just busy wait
+			 for as many WaveFront commands as possible, without
+			 coming up with a number so large that we hog the
+			 whole CPU.
+
+			 Specifically, with this number, out of about 134,000
+			 status waits, only about 250 result in a sleep.
+		      */
+
+int sleep_interval = 100;   /* HZ/sleep_interval seconds per sleep */
+int sleep_tries = 50;       /* number of times we'll try to sleep */
+
+int reset_time = 2;        /* hundreths of a second we wait after a HW
+			      reset for the expected interrupt.
+			   */
+
+int ramcheck_time = 20;    /* time in seconds to wait while ROM code
+			      checks on-board RAM.
+			   */
+
+int osrun_time = 10;       /* time in seconds we wait for the OS to
+			      start running.
+			   */
+MODULE_AUTHOR("Paul Barton-Davis <pbd@op.net>");
+MODULE_DESCRIPTION("ALSA driver for Turtle Beach WaveFront ICS2215 Synth");
+MODULE_CLASSES("{sound}");
+MODULE_PARM(wf_raw,"i");
+MODULE_PARM_DESC(wf_raw, "if non-zero, assume that we need to boot the OS");
+MODULE_PARM(fx_raw,"i");
+MODULE_PARM_DESC(fx_raw, "if non-zero, assume that the FX process needs help");
+MODULE_PARM(debug_default,"i");
+MODULE_PARM_DESC(debug_default, "debug parameters for card initialization");
+MODULE_PARM(wait_usecs,"i");
+MODULE_PARM_DESC(wait_usecs, "how long to wait without sleeping, usecs");
+MODULE_PARM(sleep_interval,"i");
+MODULE_PARM_DESC(sleep_interval, "how long to sleep when waiting for reply");
+MODULE_PARM(sleep_tries,"i");
+MODULE_PARM_DESC(sleep_tries, "how many times to try sleeping during a wait");
+MODULE_PARM(ospath,"s");
+MODULE_PARM_DESC(ospath, "full pathname to processed ICS2115 OS firmware");
+MODULE_PARM(reset_time,"i");
+MODULE_PARM_DESC(reset_time, "how long to wait for a reset to take effect");
+MODULE_PARM(ramcheck_time,"i");
+MODULE_PARM_DESC(ramcheck_time, "how many seconds to wait for the RAM test");
+MODULE_PARM(osrun_time,"i");
+MODULE_PARM_DESC(osrun_time, "how many seconds to wait for the ICS2115 OS");
+
+/*
+ *	This sucks, hopefully it'll get standardised
+ */
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,18) && LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
+#define loops_per_sec loops_per_jiffy*HZ
+#elif LINUX_VERSION_CODE == KERNEL_VERSION(2,4,0) && defined(I_DIRTY_PAGES) /* linux/fs.h */
+#define loops_per_sec loops_per_jiffy*HZ
+#elif LINUX_VERSION_CODE > KERNEL_VERSION(2,4,0)
+#define loops_per_sec loops_per_jiffy*HZ
+#endif
+ 
+#if defined(__alpha__) || defined(__powerpc__)
+#ifdef __SMP__
+#define LOOPS_PER_SEC	(cpu_data[smp_processor_id()].loops_per_sec)
+#else
+#define LOOPS_PER_SEC	(loops_per_sec)
+#endif
+#endif
+
+#if defined(__i386__)
+#define LOOPS_PER_SEC	(current_cpu_data.loops_per_sec)
+#endif
+ 
+/* if WF_DEBUG not defined, no run-time debugging messages will
+   be available via the debug flag setting. Given the current
+   beta state of the driver, this will remain set until a future 
+   version.
+*/
+
+#define WF_DEBUG 1
+
+#ifdef WF_DEBUG
+
+#ifdef NEW_MACRO_VARARGS
+#define DPRINT(cond, ...) \
+       if ((dev->debug & (cond)) == (cond)) { \
+	     snd_printk (__VA_ARGS__); \
+       }
+#else
+#define DPRINT(cond, args...) \
+       if ((dev->debug & (cond)) == (cond)) { \
+	     snd_printk (##args); \
+       }
+#endif
+#else
+#define DPRINT(cond, args...)
+#endif /* WF_DEBUG */
+
+#define LOGNAME "WaveFront: "
+
+/* bitmasks for WaveFront status port value */
+
+#define STAT_RINTR_ENABLED	0x01
+#define STAT_CAN_READ		0x02
+#define STAT_INTR_READ		0x04
+#define STAT_WINTR_ENABLED	0x10
+#define STAT_CAN_WRITE		0x20
+#define STAT_INTR_WRITE		0x40
+
+static int wavefront_delete_sample (snd_wavefront_t *, int sampnum);
+static int wavefront_find_free_sample (snd_wavefront_t *);
+
+typedef struct {
+	int cmd;
+	char *action;
+	unsigned int read_cnt;
+	unsigned int write_cnt;
+	int need_ack;
+} wavefront_command;
+
+static struct {
+	int errno;
+	const char *errstr;
+} wavefront_errors[] = {
+	{ 0x01, "Bad sample number" },
+	{ 0x02, "Out of sample memory" },
+	{ 0x03, "Bad patch number" },
+	{ 0x04, "Error in number of voices" },
+	{ 0x06, "Sample load already in progress" },
+	{ 0x0B, "No sample load request pending" },
+	{ 0x0E, "Bad MIDI channel number" },
+	{ 0x10, "Download Record Error" },
+	{ 0x80, "Success" },
+	{ 0x0, 0x0 }
+};
+
+#define NEEDS_ACK 1
+
+static wavefront_command wavefront_commands[] = {
+	{ WFC_SET_SYNTHVOL, "set synthesizer volume", 0, 1, NEEDS_ACK },
+	{ WFC_GET_SYNTHVOL, "get synthesizer volume", 1, 0, 0},
+	{ WFC_SET_NVOICES, "set number of voices", 0, 1, NEEDS_ACK },
+	{ WFC_GET_NVOICES, "get number of voices", 1, 0, 0 },
+	{ WFC_SET_TUNING, "set synthesizer tuning", 0, 2, NEEDS_ACK },
+	{ WFC_GET_TUNING, "get synthesizer tuning", 2, 0, 0 },
+	{ WFC_DISABLE_CHANNEL, "disable synth channel", 0, 1, NEEDS_ACK },
+	{ WFC_ENABLE_CHANNEL, "enable synth channel", 0, 1, NEEDS_ACK },
+	{ WFC_GET_CHANNEL_STATUS, "get synth channel status", 3, 0, 0 },
+	{ WFC_MISYNTH_OFF, "disable midi-in to synth", 0, 0, NEEDS_ACK },
+	{ WFC_MISYNTH_ON, "enable midi-in to synth", 0, 0, NEEDS_ACK },
+	{ WFC_VMIDI_ON, "enable virtual midi mode", 0, 0, NEEDS_ACK },
+	{ WFC_VMIDI_OFF, "disable virtual midi mode", 0, 0, NEEDS_ACK },
+	{ WFC_MIDI_STATUS, "report midi status", 1, 0, 0 },
+	{ WFC_FIRMWARE_VERSION, "report firmware version", 2, 0, 0 },
+	{ WFC_HARDWARE_VERSION, "report hardware version", 2, 0, 0 },
+	{ WFC_GET_NSAMPLES, "report number of samples", 2, 0, 0 },
+	{ WFC_INSTOUT_LEVELS, "report instantaneous output levels", 7, 0, 0 },
+	{ WFC_PEAKOUT_LEVELS, "report peak output levels", 7, 0, 0 },
+	{ WFC_DOWNLOAD_SAMPLE, "download sample",
+	  0, WF_SAMPLE_BYTES, NEEDS_ACK },
+	{ WFC_DOWNLOAD_BLOCK, "download block", 0, 0, NEEDS_ACK},
+	{ WFC_DOWNLOAD_SAMPLE_HEADER, "download sample header",
+	  0, WF_SAMPLE_HDR_BYTES, NEEDS_ACK },
+	{ WFC_UPLOAD_SAMPLE_HEADER, "upload sample header", 13, 2, 0 },
+
+	/* This command requires a variable number of bytes to be written.
+	   There is a hack in snd_wavefront_cmd() to support this. The actual
+	   count is passed in as the read buffer ptr, cast appropriately.
+	   Ugh.
+	*/
+
+	{ WFC_DOWNLOAD_MULTISAMPLE, "download multisample", 0, 0, NEEDS_ACK },
+
+	/* This one is a hack as well. We just read the first byte of the
+	   response, don't fetch an ACK, and leave the rest to the 
+	   calling function. Ugly, ugly, ugly.
+	*/
+
+	{ WFC_UPLOAD_MULTISAMPLE, "upload multisample", 2, 1, 0 },
+	{ WFC_DOWNLOAD_SAMPLE_ALIAS, "download sample alias",
+	  0, WF_ALIAS_BYTES, NEEDS_ACK },
+	{ WFC_UPLOAD_SAMPLE_ALIAS, "upload sample alias", WF_ALIAS_BYTES, 2, 0},
+	{ WFC_DELETE_SAMPLE, "delete sample", 0, 2, NEEDS_ACK },
+	{ WFC_IDENTIFY_SAMPLE_TYPE, "identify sample type", 5, 2, 0 },
+	{ WFC_UPLOAD_SAMPLE_PARAMS, "upload sample parameters" },
+	{ WFC_REPORT_FREE_MEMORY, "report free memory", 4, 0, 0 },
+	{ WFC_DOWNLOAD_PATCH, "download patch", 0, 134, NEEDS_ACK },
+	{ WFC_UPLOAD_PATCH, "upload patch", 132, 2, 0 },
+	{ WFC_DOWNLOAD_PROGRAM, "download program", 0, 33, NEEDS_ACK },
+	{ WFC_UPLOAD_PROGRAM, "upload program", 32, 1, 0 },
+	{ WFC_DOWNLOAD_EDRUM_PROGRAM, "download enhanced drum program", 0, 9,
+	  NEEDS_ACK},
+	{ WFC_UPLOAD_EDRUM_PROGRAM, "upload enhanced drum program", 8, 1, 0},
+	{ WFC_SET_EDRUM_CHANNEL, "set enhanced drum program channel",
+	  0, 1, NEEDS_ACK },
+	{ WFC_DISABLE_DRUM_PROGRAM, "disable drum program", 0, 1, NEEDS_ACK },
+	{ WFC_REPORT_CHANNEL_PROGRAMS, "report channel program numbers",
+	  32, 0, 0 },
+	{ WFC_NOOP, "the no-op command", 0, 0, NEEDS_ACK },
+	{ 0x00 }
+};
+
+static const char *
+wavefront_errorstr (int errnum)
+
+{
+	int i;
+
+	for (i = 0; wavefront_errors[i].errstr; i++) {
+		if (wavefront_errors[i].errno == errnum) {
+			return wavefront_errors[i].errstr;
+		}
+	}
+
+	return "Unknown WaveFront error";
+}
+
+static wavefront_command *
+wavefront_get_command (int cmd) 
+
+{
+	int i;
+
+	for (i = 0; wavefront_commands[i].cmd != 0; i++) {
+		if (cmd == wavefront_commands[i].cmd) {
+			return &wavefront_commands[i];
+		}
+	}
+
+	return (wavefront_command *) 0;
+}
+
+static inline int
+wavefront_status (snd_wavefront_t *dev) 
+
+{
+	return inb (dev->status_port);
+}
+
+static int
+wavefront_sleep (int limit)
+
+{
+	set_current_state(TASK_INTERRUPTIBLE);
+	schedule_timeout(limit);
+
+	return signal_pending(current);
+}
+
+static int
+wavefront_wait (snd_wavefront_t *dev, int mask)
+
+{
+	int             i;
+	static int      short_loop_cnt = 0;
+
+	/* Compute the loop count that lets us sleep for about the
+	   right amount of time, cache issues, bus speeds and all
+	   other issues being unequal but largely irrelevant.
+	*/
+
+	if (short_loop_cnt == 0) {
+		short_loop_cnt = wait_usecs *
+			(LOOPS_PER_SEC / 1000000);
+	}
+
+	/* Spin for a short period of time, because >99% of all
+	   requests to the WaveFront can be serviced inline like this.
+	*/
+
+	for (i = 0; i < short_loop_cnt; i++) {
+		if (wavefront_status (dev) & mask) {
+			return 1;
+		}
+	}
+
+	for (i = 0; i < sleep_tries; i++) {
+
+		if (wavefront_status (dev) & mask) {
+			return 1;
+		}
+
+		if (wavefront_sleep (HZ/sleep_interval)) {
+			return (0);
+		}
+	}
+
+	return (0);
+}
+
+static int
+wavefront_read (snd_wavefront_t *dev)
+
+{
+	if (wavefront_wait (dev, STAT_CAN_READ))
+		return inb (dev->data_port);
+
+	DPRINT (WF_DEBUG_DATA, "read timeout.\n");
+
+	return -1;
+}
+
+static int
+wavefront_write (snd_wavefront_t *dev, unsigned char data)
+
+{
+	if (wavefront_wait (dev, STAT_CAN_WRITE)) {
+		outb (data, dev->data_port);
+		return 0;
+	}
+
+	DPRINT (WF_DEBUG_DATA, "write timeout.\n");
+
+	return -1;
+}
+
+int
+snd_wavefront_cmd (snd_wavefront_t *dev, 
+		   int cmd, unsigned char *rbuf, unsigned char *wbuf)
+
+{
+	int ack;
+	unsigned int i;
+	int c;
+	wavefront_command *wfcmd;
+
+	if ((wfcmd = wavefront_get_command (cmd)) == (wavefront_command *) 0) {
+		snd_printk ("command 0x%x not supported.\n",
+			cmd);
+		return 1;
+	}
+
+	/* Hack to handle the one variable-size write command. See
+	   wavefront_send_multisample() for the other half of this
+	   gross and ugly strategy.
+	*/
+
+	if (cmd == WFC_DOWNLOAD_MULTISAMPLE) {
+		wfcmd->write_cnt = (unsigned long) rbuf;
+		rbuf = 0;
+	}
+
+	DPRINT (WF_DEBUG_CMD, "0x%x [%s] (%d,%d,%d)\n",
+			       cmd, wfcmd->action, wfcmd->read_cnt,
+			       wfcmd->write_cnt, wfcmd->need_ack);
+    
+	if (wavefront_write (dev, cmd)) { 
+		DPRINT ((WF_DEBUG_IO|WF_DEBUG_CMD), "cannot request "
+						     "0x%x [%s].\n",
+						     cmd, wfcmd->action);
+		return 1;
+	} 
+
+	if (wfcmd->write_cnt > 0) {
+		DPRINT (WF_DEBUG_DATA, "writing %d bytes "
+					"for 0x%x\n",
+					wfcmd->write_cnt, cmd);
+
+		for (i = 0; i < wfcmd->write_cnt; i++) {
+			if (wavefront_write (dev, wbuf[i])) {
+				DPRINT (WF_DEBUG_IO, "bad write for byte "
+						      "%d of 0x%x [%s].\n",
+						      i, cmd, wfcmd->action);
+				return 1;
+			}
+
+			DPRINT (WF_DEBUG_DATA, "write[%d] = 0x%x\n",
+						i, wbuf[i]);
+		}
+	}
+
+	if (wfcmd->read_cnt > 0) {
+		DPRINT (WF_DEBUG_DATA, "reading %d ints "
+					"for 0x%x\n",
+					wfcmd->read_cnt, cmd);
+
+		for (i = 0; i < wfcmd->read_cnt; i++) {
+
+			if ((c = wavefront_read (dev)) == -1) {
+				DPRINT (WF_DEBUG_IO, "bad read for byte "
+						      "%d of 0x%x [%s].\n",
+						      i, cmd, wfcmd->action);
+				return 1;
+			}
+
+			/* Now handle errors. Lots of special cases here */
+	    
+			if (c == 0xff) { 
+				if ((c = wavefront_read (dev)) == -1) {
+					DPRINT (WF_DEBUG_IO, "bad read for "
+							      "error byte at "
+							      "read byte %d "
+							      "of 0x%x [%s].\n",
+							      i, cmd,
+							      wfcmd->action);
+					return 1;
+				}
+
+				/* Can you believe this madness ? */
+
+				if (c == 1 &&
+				    wfcmd->cmd == WFC_IDENTIFY_SAMPLE_TYPE) {
+					rbuf[0] = WF_ST_EMPTY;
+					return (0);
+
+				} else if (c == 3 &&
+					   wfcmd->cmd == WFC_UPLOAD_PATCH) {
+
+					return 3;
+
+				} else if (c == 1 &&
+					   wfcmd->cmd == WFC_UPLOAD_PROGRAM) {
+
+					return 1;
+
+				} else {
+
+					DPRINT (WF_DEBUG_IO, "error %d (%s) "
+							      "during "
+							      "read for byte "
+							      "%d of 0x%x "
+							      "[%s].\n",
+							      c,
+							      wavefront_errorstr (c),
+							      i, cmd,
+							      wfcmd->action);
+					return 1;
+
+				}
+		
+		} else {
+				rbuf[i] = c;
+			}
+			
+			DPRINT (WF_DEBUG_DATA, "read[%d] = 0x%x\n",i, rbuf[i]);
+		}
+	}
+	
+	if ((wfcmd->read_cnt == 0 && wfcmd->write_cnt == 0) || wfcmd->need_ack) {
+
+		DPRINT (WF_DEBUG_CMD, "reading ACK for 0x%x\n", cmd);
+
+		/* Some commands need an ACK, but return zero instead
+		   of the standard value.
+		*/
+	    
+		if ((ack = wavefront_read (dev)) == 0) {
+			ack = WF_ACK;
+		}
+	
+		if (ack != WF_ACK) {
+			if (ack == -1) {
+				DPRINT (WF_DEBUG_IO, "cannot read ack for "
+						      "0x%x [%s].\n",
+						      cmd, wfcmd->action);
+				return 1;
+		
+			} else {
+				int err = -1; /* something unknown */
+
+				if (ack == 0xff) { /* explicit error */
+		    
+					if ((err = wavefront_read (dev)) == -1) {
+						DPRINT (WF_DEBUG_DATA,
+							"cannot read err "
+							"for 0x%x [%s].\n",
+							cmd, wfcmd->action);
+					}
+				}
+				
+				DPRINT (WF_DEBUG_IO, "0x%x [%s] "
+					"failed (0x%x, 0x%x, %s)\n",
+					cmd, wfcmd->action, ack, err,
+					wavefront_errorstr (err));
+				
+				return -err;
+			}
+		}
+		
+		DPRINT (WF_DEBUG_DATA, "ack received "
+					"for 0x%x [%s]\n",
+					cmd, wfcmd->action);
+	} else {
+
+		DPRINT (WF_DEBUG_CMD, "0x%x [%s] does not need "
+				       "ACK (%d,%d,%d)\n",
+				       cmd, wfcmd->action, wfcmd->read_cnt,
+				       wfcmd->write_cnt, wfcmd->need_ack);
+	}
+
+	return 0;
+	
+}
+
+
+/***********************************************************************
+WaveFront data munging   
+
+Things here are wierd. All data written to the board cannot 
+have its most significant bit set. Any data item with values 
+potentially > 0x7F (127) must be split across multiple bytes.
+
+Sometimes, we need to munge numeric values that are represented on
+the x86 side as 8-32 bit values. Sometimes, we need to munge data
+that is represented on the x86 side as an array of bytes. The most
+efficient approach to handling both cases seems to be to use 2
+different functions for munging and 2 for de-munging. This avoids
+wierd casting and worrying about bit-level offsets.
+
+**********************************************************************/
+
+static unsigned char *
+munge_int32 (unsigned int src,
+	     unsigned char *dst,
+	     unsigned int dst_size)
+{
+	unsigned int i;
+
+	for (i = 0; i < dst_size; i++) {
+		*dst = src & 0x7F;  /* Mask high bit of LSB */
+		src = src >> 7;     /* Rotate Right 7 bits  */
+	                            /* Note: we leave the upper bits in place */ 
+
+		dst++;
+ 	};
+	return dst;
+};
+
+static int 
+demunge_int32 (unsigned char* src, int src_size)
+
+{
+	int i;
+ 	int outval = 0;
+	
+ 	for (i = src_size - 1; i >= 0; i--) {
+		outval=(outval<<7)+src[i];
+	}
+
+	return outval;
+};
+
+static 
+unsigned char *
+munge_buf (unsigned char *src, unsigned char *dst, unsigned int dst_size)
+
+{
+	unsigned int i;
+	unsigned int last = dst_size / 2;
+
+	for (i = 0; i < last; i++) {
+		*dst++ = src[i] & 0x7f;
+		*dst++ = src[i] >> 7;
+	}
+	return dst;
+}
+
+static 
+unsigned char *
+demunge_buf (unsigned char *src, unsigned char *dst, unsigned int src_bytes)
+
+{
+	int i;
+	unsigned char *end = src + src_bytes;
+    
+	end = src + src_bytes;
+
+	/* NOTE: src and dst *CAN* point to the same address */
+
+	for (i = 0; src != end; i++) {
+		dst[i] = *src++;
+		dst[i] |= (*src++)<<7;
+	}
+
+	return dst;
+}
+
+
+/***********************************************************************
+WaveFront: sample, patch and program management.
+***********************************************************************/
+
+static int
+wavefront_delete_sample (snd_wavefront_t *dev, int sample_num)
+
+{
+	unsigned char wbuf[2];
+	int x;
+
+	wbuf[0] = sample_num & 0x7f;
+	wbuf[1] = sample_num >> 7;
+
+	if ((x = snd_wavefront_cmd (dev, WFC_DELETE_SAMPLE, 0, wbuf)) == 0) {
+		dev->sample_status[sample_num] = WF_ST_EMPTY;
+	}
+
+	return x;
+}
+
+static int
+wavefront_get_sample_status (snd_wavefront_t *dev, int assume_rom)
+
+{
+	int i;
+	unsigned char rbuf[32], wbuf[32];
+	unsigned int    sc_real, sc_alias, sc_multi;
+
+	/* check sample status */
+    
+	if (snd_wavefront_cmd (dev, WFC_GET_NSAMPLES, rbuf, wbuf)) {
+		snd_printk ("cannot request sample count.\n");
+		return -1;
+	} 
+    
+	sc_real = sc_alias = sc_multi = dev->samples_used = 0;
+    
+	for (i = 0; i < WF_MAX_SAMPLE; i++) {
+	
+		wbuf[0] = i & 0x7f;
+		wbuf[1] = i >> 7;
+
+		if (snd_wavefront_cmd (dev, WFC_IDENTIFY_SAMPLE_TYPE, rbuf, wbuf)) {
+			snd_printk("cannot identify sample "
+				   "type of slot %d\n", i);
+			dev->sample_status[i] = WF_ST_EMPTY;
+			continue;
+		}
+
+		dev->sample_status[i] = (WF_SLOT_FILLED|rbuf[0]);
+
+		if (assume_rom) {
+			dev->sample_status[i] |= WF_SLOT_ROM;
+		}
+
+		switch (rbuf[0] & WF_ST_MASK) {
+		case WF_ST_SAMPLE:
+			sc_real++;
+			break;
+		case WF_ST_MULTISAMPLE:
+			sc_multi++;
+			break;
+		case WF_ST_ALIAS:
+			sc_alias++;
+			break;
+		case WF_ST_EMPTY:
+			break;
+
+		default:
+			snd_printk ("unknown sample type for "
+				    "slot %d (0x%x)\n", 
+				    i, rbuf[0]);
+		}
+
+		if (rbuf[0] != WF_ST_EMPTY) {
+			dev->samples_used++;
+		} 
+	}
+
+	snd_printk ("%d samples used (%d real, %d aliases, %d multi), "
+		    "%d empty\n", dev->samples_used, sc_real, sc_alias, sc_multi,
+		    WF_MAX_SAMPLE - dev->samples_used);
+
+
+	return (0);
+
+}
+
+static int
+wavefront_get_patch_status (snd_wavefront_t *dev)
+
+{
+	unsigned char patchbuf[WF_PATCH_BYTES];
+	unsigned char patchnum[2];
+	wavefront_patch *p;
+	int i, x, cnt, cnt2;
+
+	for (i = 0; i < WF_MAX_PATCH; i++) {
+		patchnum[0] = i & 0x7f;
+		patchnum[1] = i >> 7;
+
+		if ((x = snd_wavefront_cmd (dev, WFC_UPLOAD_PATCH, patchbuf,
+					patchnum)) == 0) {
+
+			dev->patch_status[i] |= WF_SLOT_FILLED;
+			p = (wavefront_patch *) patchbuf;
+			dev->sample_status
+				[p->sample_number|(p->sample_msb<<7)] |=
+				WF_SLOT_USED;
+	    
+		} else if (x == 3) { /* Bad patch number */
+			dev->patch_status[i] = 0;
+		} else {
+			snd_printk ("upload patch "
+				    "error 0x%x\n", x);
+			dev->patch_status[i] = 0;
+			return 1;
+		}
+	}
+
+	/* program status has already filled in slot_used bits */
+
+	for (i = 0, cnt = 0, cnt2 = 0; i < WF_MAX_PATCH; i++) {
+		if (dev->patch_status[i] & WF_SLOT_FILLED) {
+			cnt++;
+		}
+		if (dev->patch_status[i] & WF_SLOT_USED) {
+			cnt2++;
+		}
+	
+	}
+	snd_printk ("%d patch slots filled, %d in use\n", cnt, cnt2);
+
+	return (0);
+}
+
+static int
+wavefront_get_program_status (snd_wavefront_t *dev)
+
+{
+	unsigned char progbuf[WF_PROGRAM_BYTES];
+	wavefront_program prog;
+	unsigned char prognum;
+	int i, x, l, cnt;
+
+	for (i = 0; i < WF_MAX_PROGRAM; i++) {
+		prognum = i;
+
+		if ((x = snd_wavefront_cmd (dev, WFC_UPLOAD_PROGRAM, progbuf,
+					&prognum)) == 0) {
+
+			dev->prog_status[i] |= WF_SLOT_USED;
+
+			demunge_buf (progbuf, (unsigned char *) &prog,
+				     WF_PROGRAM_BYTES);
+
+			for (l = 0; l < WF_NUM_LAYERS; l++) {
+				if (prog.layer[l].mute) {
+					dev->patch_status
+						[prog.layer[l].patch_number] |=
+						WF_SLOT_USED;
+				}
+			}
+		} else if (x == 1) { /* Bad program number */
+			dev->prog_status[i] = 0;
+		} else {
+			snd_printk ("upload program "
+				    "error 0x%x\n", x);
+			dev->prog_status[i] = 0;
+		}
+	}
+
+	for (i = 0, cnt = 0; i < WF_MAX_PROGRAM; i++) {
+		if (dev->prog_status[i]) {
+			cnt++;
+		}
+	}
+
+	snd_printk ("%d programs slots in use\n", cnt);
+
+	return (0);
+}
+
+static int
+wavefront_send_patch (snd_wavefront_t *dev, wavefront_patch_info *header)
+
+{
+	unsigned char buf[WF_PATCH_BYTES+2];
+	unsigned char *bptr;
+
+	DPRINT (WF_DEBUG_LOAD_PATCH, "downloading patch %d\n",
+				      header->number);
+
+	dev->patch_status[header->number] |= WF_SLOT_FILLED;
+
+	bptr = buf;
+	bptr = munge_int32 (header->number, buf, 2);
+	munge_buf ((unsigned char *)&header->hdr.p, bptr, WF_PATCH_BYTES);
+    
+	if (snd_wavefront_cmd (dev, WFC_DOWNLOAD_PATCH, 0, buf)) {
+		snd_printk ("download patch failed\n");
+		return -(EIO);
+	}
+
+	return (0);
+}
+
+static int
+wavefront_send_program (snd_wavefront_t *dev, wavefront_patch_info *header)
+
+{
+	unsigned char buf[WF_PROGRAM_BYTES+1];
+	int i;
+
+	DPRINT (WF_DEBUG_LOAD_PATCH, "downloading program %d\n",
+		header->number);
+
+	dev->prog_status[header->number] = WF_SLOT_USED;
+
+	/* XXX need to zero existing SLOT_USED bit for program_status[i]
+	   where `i' is the program that's being (potentially) overwritten.
+	*/
+    
+	for (i = 0; i < WF_NUM_LAYERS; i++) {
+		if (header->hdr.pr.layer[i].mute) {
+			dev->patch_status[header->hdr.pr.layer[i].patch_number] |=
+				WF_SLOT_USED;
+
+			/* XXX need to mark SLOT_USED for sample used by
+			   patch_number, but this means we have to load it. Ick.
+			*/
+		}
+	}
+
+	buf[0] = header->number;
+	munge_buf ((unsigned char *)&header->hdr.pr, &buf[1], WF_PROGRAM_BYTES);
+    
+	if (snd_wavefront_cmd (dev, WFC_DOWNLOAD_PROGRAM, 0, buf)) {
+		snd_printk ("download patch failed\n");	
+		return -(EIO);
+	}
+
+	return (0);
+}
+
+static int
+wavefront_freemem (snd_wavefront_t *dev)
+
+{
+	char rbuf[8];
+
+	if (snd_wavefront_cmd (dev, WFC_REPORT_FREE_MEMORY, rbuf, 0)) {
+		snd_printk ("can't get memory stats.\n");
+		return -1;
+	} else {
+		return demunge_int32 (rbuf, 4);
+	}
+}
+
+static int
+wavefront_send_sample (snd_wavefront_t *dev, 
+		       wavefront_patch_info *header,
+		       UINT16 *dataptr,
+		       int data_is_unsigned)
+
+{
+	/* samples are downloaded via a 16-bit wide i/o port
+	   (you could think of it as 2 adjacent 8-bit wide ports
+	   but its less efficient that way). therefore, all
+	   the blocksizes and so forth listed in the documentation,
+	   and used conventionally to refer to sample sizes,
+	   which are given in 8-bit units (bytes), need to be
+	   divided by 2.
+        */
+
+	UINT16 sample_short;
+	UINT32 length;
+	UINT16 *data_end = 0;
+	unsigned int i;
+	const int max_blksize = 4096/2;
+	unsigned int written;
+	unsigned int blocksize;
+	int dma_ack;
+	int blocknum;
+	unsigned char sample_hdr[WF_SAMPLE_HDR_BYTES];
+	unsigned char *shptr;
+	int skip = 0;
+	int initial_skip = 0;
+
+	DPRINT (WF_DEBUG_LOAD_PATCH, "sample %sdownload for slot %d, "
+				      "type %d, %d bytes from 0x%lx\n",
+				      header->size ? "" : "header ", 
+				      header->number, header->subkey,
+				      header->size,
+				      (unsigned long) header->dataptr);
+
+	if (header->number == WAVEFRONT_FIND_FREE_SAMPLE_SLOT) {
+		int x;
+
+		if ((x = wavefront_find_free_sample (dev)) < 0) {
+			return -ENOMEM;
+		}
+		snd_printk ("unspecified sample => %d\n", x);
+		header->number = x;
+	}
+
+	if (header->size) {
+
+		/* XXX its a debatable point whether or not RDONLY semantics
+		   on the ROM samples should cover just the sample data or
+		   the sample header. For now, it only covers the sample data,
+		   so anyone is free at all times to rewrite sample headers.
+
+		   My reason for this is that we have the sample headers
+		   available in the WFB file for General MIDI, and so these
+		   can always be reset if needed. The sample data, however,
+		   cannot be recovered without a complete reset and firmware
+		   reload of the ICS2115, which is a very expensive operation.
+
+		   So, doing things this way allows us to honor the notion of
+		   "RESETSAMPLES" reasonably cheaply. Note however, that this
+		   is done purely at user level: there is no WFB parser in
+		   this driver, and so a complete reset (back to General MIDI,
+		   or theoretically some other configuration) is the
+		   responsibility of the user level library. 
+
+		   To try to do this in the kernel would be a little
+		   crazy: we'd need 158K of kernel space just to hold
+		   a copy of the patch/program/sample header data.
+		*/
+
+		if (dev->rom_samples_rdonly) {
+			if (dev->sample_status[header->number] & WF_SLOT_ROM) {
+				snd_printk ("sample slot %d "
+					    "write protected\n",
+					    header->number);
+				return -EACCES;
+			}
+		}
+
+		wavefront_delete_sample (dev, header->number);
+	}
+
+	if (header->size) {
+		dev->freemem = wavefront_freemem (dev);
+
+		if (dev->freemem < header->size) {
+			snd_printk ("insufficient memory to "
+				    "load %d byte sample.\n",
+				    header->size);
+			return -ENOMEM;
+		}
+	
+	}
+
+	skip = WF_GET_CHANNEL(&header->hdr.s);
+
+	if (skip > 0 && header->hdr.s.SampleResolution != LINEAR_16BIT) {
+		snd_printk ("channel selection only "
+			    "possible on 16-bit samples");
+		return -(EINVAL);
+	}
+
+	switch (skip) {
+	case 0:
+		initial_skip = 0;
+		skip = 1;
+		break;
+	case 1:
+		initial_skip = 0;
+		skip = 2;
+		break;
+	case 2:
+		initial_skip = 1;
+		skip = 2;
+		break;
+	case 3:
+		initial_skip = 2;
+		skip = 3;
+		break;
+	case 4:
+		initial_skip = 3;
+		skip = 4;
+		break;
+	case 5:
+		initial_skip = 4;
+		skip = 5;
+		break;
+	case 6:
+		initial_skip = 5;
+		skip = 6;
+		break;
+	}
+
+	DPRINT (WF_DEBUG_LOAD_PATCH, "channel selection: %d => "
+				      "initial skip = %d, skip = %d\n",
+				      WF_GET_CHANNEL (&header->hdr.s),
+				      initial_skip, skip);
+    
+	/* Be safe, and zero the "Unused" bits ... */
+
+	WF_SET_CHANNEL(&header->hdr.s, 0);
+
+	/* adjust size for 16 bit samples by dividing by two.  We always
+	   send 16 bits per write, even for 8 bit samples, so the length
+	   is always half the size of the sample data in bytes.
+	*/
+
+	length = header->size / 2;
+
+	/* the data we're sent has not been munged, and in fact, the
+	   header we have to send isn't just a munged copy either.
+	   so, build the sample header right here.
+	*/
+
+	shptr = &sample_hdr[0];
+
+	shptr = munge_int32 (header->number, shptr, 2);
+
+	if (header->size) {
+		shptr = munge_int32 (length, shptr, 4);
+	}
+
+	/* Yes, a 4 byte result doesn't contain all of the offset bits,
+	   but the offset only uses 24 bits.
+	*/
+
+	shptr = munge_int32 (*((UINT32 *) &header->hdr.s.sampleStartOffset),
+			     shptr, 4);
+	shptr = munge_int32 (*((UINT32 *) &header->hdr.s.loopStartOffset),
+			     shptr, 4);
+	shptr = munge_int32 (*((UINT32 *) &header->hdr.s.loopEndOffset),
+			     shptr, 4);
+	shptr = munge_int32 (*((UINT32 *) &header->hdr.s.sampleEndOffset),
+			     shptr, 4);
+	
+	/* This one is truly wierd. What kind of wierdo decided that in
+	   a system dominated by 16 and 32 bit integers, they would use
+	   a just 12 bits ?
+	*/
+	
+	shptr = munge_int32 (header->hdr.s.FrequencyBias, shptr, 3);
+	
+	/* Why is this nybblified, when the MSB is *always* zero ? 
+	   Anyway, we can't take address of bitfield, so make a
+	   good-faith guess at where it starts.
+	*/
+	
+	shptr = munge_int32 (*(&header->hdr.s.FrequencyBias+1),
+			     shptr, 2);
+
+	if (snd_wavefront_cmd (dev, 
+			   header->size ?
+			   WFC_DOWNLOAD_SAMPLE : WFC_DOWNLOAD_SAMPLE_HEADER,
+			   0, sample_hdr)) {
+		snd_printk ("sample %sdownload refused.\n",
+			    header->size ? "" : "header ");
+		return -(EIO);
+	}
+
+	if (header->size == 0) {
+		goto sent; /* Sorry. Just had to have one somewhere */
+	}
+    
+	data_end = dataptr + length;
+
+	/* Do any initial skip over an unused channel's data */
+
+	dataptr += initial_skip;
+    
+	for (written = 0, blocknum = 0;
+	     written < length; written += max_blksize, blocknum++) {
+	
+		if ((length - written) > max_blksize) {
+			blocksize = max_blksize;
+		} else {
+			/* round to nearest 16-byte value */
+			blocksize = ((length-written+7)&~0x7);
+		}
+
+		if (snd_wavefront_cmd (dev, WFC_DOWNLOAD_BLOCK, 0, 0)) {
+			snd_printk ("download block "
+				    "request refused.\n");
+			return -(EIO);
+		}
+
+		for (i = 0; i < blocksize; i++) {
+
+			if (dataptr < data_end) {
+		
+				__get_user (sample_short, dataptr);
+				dataptr += skip;
+		
+				if (data_is_unsigned) { /* GUS ? */
+
+					if (WF_SAMPLE_IS_8BIT(&header->hdr.s)) {
+			
+						/* 8 bit sample
+						 resolution, sign
+						 extend both bytes.
+						*/
+			
+						((unsigned char*)
+						 &sample_short)[0] += 0x7f;
+						((unsigned char*)
+						 &sample_short)[1] += 0x7f;
+			
+					} else {
+			
+						/* 16 bit sample
+						 resolution, sign
+						 extend the MSB.
+						*/
+			
+						sample_short += 0x7fff;
+					}
+				}
+
+			} else {
+
+				/* In padding section of final block:
+
+				   Don't fetch unsupplied data from
+				   user space, just continue with
+				   whatever the final value was.
+				*/
+			}
+	    
+			if (i < blocksize - 1) {
+				outw (sample_short, dev->block_port);
+			} else {
+				outw (sample_short, dev->last_block_port);
+			}
+		}
+
+		/* Get "DMA page acknowledge", even though its really
+		   nothing to do with DMA at all.
+		*/
+	
+		if ((dma_ack = wavefront_read (dev)) != WF_DMA_ACK) {
+			if (dma_ack == -1) {
+				snd_printk ("upload sample "
+					    "DMA ack timeout\n");
+				return -(EIO);
+			} else {
+				snd_printk ("upload sample "
+					    "DMA ack error 0x%x\n",
+					    dma_ack);
+				return -(EIO);
+			}
+		}
+	}
+
+	dev->sample_status[header->number] = (WF_SLOT_FILLED|WF_ST_SAMPLE);
+
+	/* Note, label is here because sending the sample header shouldn't
+	   alter the sample_status info at all.
+	*/
+
+ sent:
+	return (0);
+}
+
+static int
+wavefront_send_alias (snd_wavefront_t *dev, wavefront_patch_info *header)
+
+{
+	unsigned char alias_hdr[WF_ALIAS_BYTES];
+
+	DPRINT (WF_DEBUG_LOAD_PATCH, "download alias, %d is "
+				      "alias for %d\n",
+				      header->number,
+				      header->hdr.a.OriginalSample);
+    
+	munge_int32 (header->number, &alias_hdr[0], 2);
+	munge_int32 (header->hdr.a.OriginalSample, &alias_hdr[2], 2);
+	munge_int32 (*((unsigned int *)&header->hdr.a.sampleStartOffset),
+		     &alias_hdr[4], 4);
+	munge_int32 (*((unsigned int *)&header->hdr.a.loopStartOffset),
+		     &alias_hdr[8], 4);
+	munge_int32 (*((unsigned int *)&header->hdr.a.loopEndOffset),
+		     &alias_hdr[12], 4);
+	munge_int32 (*((unsigned int *)&header->hdr.a.sampleEndOffset),
+		     &alias_hdr[16], 4);
+	munge_int32 (header->hdr.a.FrequencyBias, &alias_hdr[20], 3);
+	munge_int32 (*(&header->hdr.a.FrequencyBias+1), &alias_hdr[23], 2);
+
+	if (snd_wavefront_cmd (dev, WFC_DOWNLOAD_SAMPLE_ALIAS, 0, alias_hdr)) {
+		snd_printk ("download alias failed.\n");
+		return -(EIO);
+	}
+
+	dev->sample_status[header->number] = (WF_SLOT_FILLED|WF_ST_ALIAS);
+
+	return (0);
+}
+
+static int
+wavefront_send_multisample (snd_wavefront_t *dev, wavefront_patch_info *header)
+{
+	int i;
+	int num_samples;
+	unsigned char msample_hdr[WF_MSAMPLE_BYTES];
+
+	munge_int32 (header->number, &msample_hdr[0], 2);
+
+	/* You'll recall at this point that the "number of samples" value
+	   in a wavefront_multisample struct is actually the log2 of the
+	   real number of samples.
+	*/
+
+	num_samples = (1<<(header->hdr.ms.NumberOfSamples&7));
+	msample_hdr[2] = (unsigned char) header->hdr.ms.NumberOfSamples;
+
+	DPRINT (WF_DEBUG_LOAD_PATCH, "multi %d with %d=%d samples\n",
+				      header->number,
+				      header->hdr.ms.NumberOfSamples,
+				      num_samples);
+
+	for (i = 0; i < num_samples; i++) {
+		DPRINT(WF_DEBUG_LOAD_PATCH|WF_DEBUG_DATA, "sample[%d] = %d\n",
+		       i, header->hdr.ms.SampleNumber[i]);
+		munge_int32 (header->hdr.ms.SampleNumber[i],
+		     &msample_hdr[3+(i*2)], 2);
+	}
+    
+	/* Need a hack here to pass in the number of bytes
+	   to be written to the synth. This is ugly, and perhaps
+	   one day, I'll fix it.
+	*/
+
+	if (snd_wavefront_cmd (dev, WFC_DOWNLOAD_MULTISAMPLE, 
+			   (unsigned char *) (long) ((num_samples*2)+3),
+			   msample_hdr)) {
+		snd_printk ("download of multisample failed.\n");
+		return -(EIO);
+	}
+
+	dev->sample_status[header->number] = (WF_SLOT_FILLED|WF_ST_MULTISAMPLE);
+
+	return (0);
+}
+
+static int
+wavefront_fetch_multisample (snd_wavefront_t *dev, 
+			     wavefront_patch_info *header)
+{
+	int i;
+	unsigned char log_ns[1];
+	unsigned char number[2];
+	int num_samples;
+
+	munge_int32 (header->number, number, 2);
+    
+	if (snd_wavefront_cmd (dev, WFC_UPLOAD_MULTISAMPLE, log_ns, number)) {
+		snd_printk ("upload multisample failed.\n");
+		return -(EIO);
+	}
+    
+	DPRINT (WF_DEBUG_DATA, "msample %d has %d samples\n",
+				header->number, log_ns[0]);
+
+	header->hdr.ms.NumberOfSamples = log_ns[0];
+
+	/* get the number of samples ... */
+
+	num_samples = (1 << log_ns[0]);
+    
+	for (i = 0; i < num_samples; i++) {
+		char d[2];
+	
+		if ((d[0] = wavefront_read (dev)) == -1) {
+			snd_printk ("upload multisample failed "
+				    "during sample loop.\n");
+			return -(EIO);
+		}
+
+		if ((d[1] = wavefront_read (dev)) == -1) {
+			snd_printk ("upload multisample failed "
+				    "during sample loop.\n");
+			return -(EIO);
+		}
+	
+		header->hdr.ms.SampleNumber[i] =
+			demunge_int32 ((unsigned char *) d, 2);
+	
+		DPRINT (WF_DEBUG_DATA, "msample sample[%d] = %d\n",
+					i, header->hdr.ms.SampleNumber[i]);
+	}
+
+	return (0);
+}
+
+
+static int
+wavefront_send_drum (snd_wavefront_t *dev, wavefront_patch_info *header)
+
+{
+	unsigned char drumbuf[WF_DRUM_BYTES];
+	wavefront_drum *drum = &header->hdr.d;
+	int i;
+
+	DPRINT (WF_DEBUG_LOAD_PATCH, "downloading edrum for MIDI "
+		"note %d, patch = %d\n", 
+		header->number, drum->PatchNumber);
+
+	drumbuf[0] = header->number & 0x7f;
+
+	for (i = 0; i < 4; i++) {
+		munge_int32 (((unsigned char *)drum)[i], &drumbuf[1+(i*2)], 2);
+	}
+
+	if (snd_wavefront_cmd (dev, WFC_DOWNLOAD_EDRUM_PROGRAM, 0, drumbuf)) {
+		snd_printk ("download drum failed.\n");
+		return -(EIO);
+	}
+
+	return (0);
+}
+
+static int 
+wavefront_find_free_sample (snd_wavefront_t *dev)
+
+{
+	int i;
+
+	for (i = 0; i < WF_MAX_SAMPLE; i++) {
+		if (!(dev->sample_status[i] & WF_SLOT_FILLED)) {
+			return i;
+		}
+	}
+	snd_printk ("no free sample slots!\n");
+	return -1;
+}
+
+#if 0
+static int 
+wavefront_find_free_patch (snd_wavefront_t *dev)
+
+{
+	int i;
+
+	for (i = 0; i < WF_MAX_PATCH; i++) {
+		if (!(dev->patch_status[i] & WF_SLOT_FILLED)) {
+			return i;
+		}
+	}
+	snd_printk ("no free patch slots!\n");
+	return -1;
+}
+#endif
+
+static int
+wavefront_load_patch (snd_wavefront_t *dev, const char *addr)
+
+{
+	wavefront_patch_info header;
+	
+	if (copy_from_user (&header, addr, sizeof(wavefront_patch_info) -
+			    sizeof(wavefront_any))) {
+		snd_printk ("bad address for load patch.\n");
+		return -(EFAULT);
+	}
+
+	DPRINT (WF_DEBUG_LOAD_PATCH, "download "
+				      "Sample type: %d "
+				      "Sample number: %d "
+				      "Sample size: %d\n",
+				      header.subkey,
+				      header.number,
+				      header.size);
+
+	switch (header.subkey) {
+	case WF_ST_SAMPLE:  /* sample or sample_header, based on patch->size */
+
+		if (copy_from_user ((unsigned char *) &header.hdr.s,
+				    (unsigned char *) header.hdrptr,
+				    sizeof (wavefront_sample)))
+			return -EFAULT;
+
+		return wavefront_send_sample (dev, &header, header.dataptr, 0);
+
+	case WF_ST_MULTISAMPLE:
+
+		if (copy_from_user ((unsigned char *) &header.hdr.s,
+				    (unsigned char *) header.hdrptr,
+				    sizeof (wavefront_multisample)))
+			return -EFAULT;
+
+		return wavefront_send_multisample (dev, &header);
+
+
+	case WF_ST_ALIAS:
+
+		if (copy_from_user ((unsigned char *) &header.hdr.a,
+				    (unsigned char *) header.hdrptr,
+				    sizeof (wavefront_alias)))
+			return -EFAULT;
+
+		return wavefront_send_alias (dev, &header);
+
+	case WF_ST_DRUM:
+		if (copy_from_user ((unsigned char *) &header.hdr.d, 
+				    (unsigned char *) header.hdrptr,
+				    sizeof (wavefront_drum)))
+			return -EFAULT;
+
+		return wavefront_send_drum (dev, &header);
+
+	case WF_ST_PATCH:
+		if (copy_from_user ((unsigned char *) &header.hdr.p, 
+				    (unsigned char *) header.hdrptr,
+				    sizeof (wavefront_patch)))
+			return -EFAULT;
+
+		return wavefront_send_patch (dev, &header);
+
+	case WF_ST_PROGRAM:
+		if (copy_from_user ((unsigned char *) &header.hdr.pr, 
+				    (unsigned char *) header.hdrptr,
+				    sizeof (wavefront_program)))
+			return -EFAULT;
+
+		return wavefront_send_program (dev, &header);
+
+	default:
+		snd_printk ("unknown patch type %d.\n",
+			    header.subkey);
+		return -(EINVAL);
+	}
+
+	return 0;
+}
+
+
+/***********************************************************************
+WaveFront: hardware-dependent interface
+***********************************************************************/
+
+static void
+process_sample_hdr (UCHAR8 *buf)
+
+{
+	wavefront_sample s;
+	UCHAR8 *ptr;
+
+	ptr = buf;
+
+	/* The board doesn't send us an exact copy of a "wavefront_sample"
+	   in response to an Upload Sample Header command. Instead, we 
+	   have to convert the data format back into our data structure,
+	   just as in the Download Sample command, where we have to do
+	   something very similar in the reverse direction.
+	*/
+
+	*((UINT32 *) &s.sampleStartOffset) = demunge_int32 (ptr, 4); ptr += 4;
+	*((UINT32 *) &s.loopStartOffset) = demunge_int32 (ptr, 4); ptr += 4;
+	*((UINT32 *) &s.loopEndOffset) = demunge_int32 (ptr, 4); ptr += 4;
+	*((UINT32 *) &s.sampleEndOffset) = demunge_int32 (ptr, 4); ptr += 4;
+	*((UINT32 *) &s.FrequencyBias) = demunge_int32 (ptr, 3); ptr += 3;
+
+	s.SampleResolution = *ptr & 0x3;
+	s.Loop = *ptr & 0x8;
+	s.Bidirectional = *ptr & 0x10;
+	s.Reverse = *ptr & 0x40;
+
+	/* Now copy it back to where it came from */
+
+	memcpy (buf, (unsigned char *) &s, sizeof (wavefront_sample));
+}
+
+static int
+wavefront_synth_control (snd_wavefront_card_t *acard, 
+			 wavefront_control *wc)
+
+{
+	snd_wavefront_t *dev = &acard->wavefront;
+	unsigned char patchnumbuf[2];
+	int i;
+
+	DPRINT (WF_DEBUG_CMD, "synth control with "
+		"cmd 0x%x\n", wc->cmd);
+
+	/* Pre-handling of or for various commands */
+
+	switch (wc->cmd) {
+		
+	case WFC_DISABLE_INTERRUPTS:
+		snd_printk ("interrupts disabled.\n");
+		outb (0x80|0x20, dev->control_port);
+		dev->interrupts_are_midi = 1;
+		return 0;
+
+	case WFC_ENABLE_INTERRUPTS:
+		snd_printk ("interrupts enabled.\n");
+		outb (0x80|0x40|0x20, dev->control_port);
+		dev->interrupts_are_midi = 1;
+		return 0;
+
+	case WFC_INTERRUPT_STATUS:
+		wc->rbuf[0] = dev->interrupts_are_midi;
+		return 0;
+
+	case WFC_ROMSAMPLES_RDONLY:
+		dev->rom_samples_rdonly = wc->wbuf[0];
+		wc->status = 0;
+		return 0;
+
+	case WFC_IDENTIFY_SLOT_TYPE:
+		i = wc->wbuf[0] | (wc->wbuf[1] << 7);
+		if (i <0 || i >= WF_MAX_SAMPLE) {
+			snd_printk ("invalid slot ID %d\n",
+				i);
+			wc->status = EINVAL;
+			return -EINVAL;
+		}
+		wc->rbuf[0] = dev->sample_status[i];
+		wc->status = 0;
+		return 0;
+
+	case WFC_DEBUG_DRIVER:
+		dev->debug = wc->wbuf[0];
+		snd_printk ("debug = 0x%x\n", dev->debug);
+		return 0;
+
+	case WFC_UPLOAD_PATCH:
+		munge_int32 (*((UINT32 *) wc->wbuf), patchnumbuf, 2);
+		memcpy (wc->wbuf, patchnumbuf, 2);
+		break;
+
+	case WFC_UPLOAD_MULTISAMPLE:
+		/* multisamples have to be handled differently, and
+		   cannot be dealt with properly by snd_wavefront_cmd() alone.
+		*/
+		wc->status = wavefront_fetch_multisample
+			(dev, (wavefront_patch_info *) wc->rbuf);
+		return 0;
+
+	case WFC_UPLOAD_SAMPLE_ALIAS:
+		snd_printk ("support for sample alias upload "
+			"being considered.\n");
+		wc->status = EINVAL;
+		return -EINVAL;
+	}
+
+	wc->status = snd_wavefront_cmd (dev, wc->cmd, wc->rbuf, wc->wbuf);
+
+	/* Post-handling of certain commands.
+
+	   In particular, if the command was an upload, demunge the data
+	   so that the user-level doesn't have to think about it.
+	*/
+
+	if (wc->status == 0) {
+		switch (wc->cmd) {
+			/* intercept any freemem requests so that we know
+			   we are always current with the user-level view
+			   of things.
+			*/
+
+		case WFC_REPORT_FREE_MEMORY:
+			dev->freemem = demunge_int32 (wc->rbuf, 4);
+			break;
+
+		case WFC_UPLOAD_PATCH:
+			demunge_buf (wc->rbuf, wc->rbuf, WF_PATCH_BYTES);
+			break;
+
+		case WFC_UPLOAD_PROGRAM:
+			demunge_buf (wc->rbuf, wc->rbuf, WF_PROGRAM_BYTES);
+			break;
+
+		case WFC_UPLOAD_EDRUM_PROGRAM:
+			demunge_buf (wc->rbuf, wc->rbuf, WF_DRUM_BYTES - 1);
+			break;
+
+		case WFC_UPLOAD_SAMPLE_HEADER:
+			process_sample_hdr (wc->rbuf);
+			break;
+
+		case WFC_UPLOAD_SAMPLE_ALIAS:
+			snd_printk ("support for "
+				    "sample aliases still "
+				    "being considered.\n");
+			break;
+
+		case WFC_VMIDI_OFF:
+			snd_wavefront_midi_disable_virtual (acard);
+			break;
+
+		case WFC_VMIDI_ON:
+			snd_wavefront_midi_enable_virtual (acard);
+			break;
+		}
+	}
+
+	return 0;
+}
+
+int 
+snd_wavefront_synth_open (snd_hwdep_t *hw, struct file *file)
+
+{
+	MOD_INC_USE_COUNT;
+	if (!try_inc_mod_count(hw->card->module)) {
+		MOD_DEC_USE_COUNT;
+		return -EFAULT;
+	}
+	file->private_data = hw;
+	return 0;
+}
+
+int 
+snd_wavefront_synth_release (snd_hwdep_t *hw, struct file *file)
+
+{
+	dec_mod_count(hw->card->module);
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+int
+snd_wavefront_synth_ioctl (snd_hwdep_t *hw, struct file *file,
+			   unsigned int cmd, unsigned long arg)
+
+{
+	snd_card_t *card;
+	snd_wavefront_t *dev;
+	snd_wavefront_card_t *acard;
+	wavefront_control wc;
+
+	card = (snd_card_t *) hw->card;
+
+	snd_assert(card != NULL, return -ENODEV);
+
+	snd_assert(card->private_data != NULL, return -ENODEV);
+
+	acard = card->private_data;
+	dev = &acard->wavefront;
+	
+	switch (cmd) {
+	case WFCTL_LOAD_SPP:
+		if (wavefront_load_patch (dev, (char *) arg) != 0) {
+			return -EIO;
+		}
+		break;
+
+	case WFCTL_WFCMD:
+		if (copy_from_user (&wc, (void *) arg, sizeof (wc)))
+			return -EFAULT;
+		if (wavefront_synth_control (acard, &wc) < 0) {
+			return -EIO;
+		}
+		if (copy_to_user ((void *) arg, &wc, sizeof (wc)))
+			return -EFAULT;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+
+
+/***********************************************************************/
+/*  WaveFront: interface for card-level wavefront module               */
+/***********************************************************************/
+
+void
+snd_wavefront_internal_interrupt (snd_wavefront_card_t *card)
+{
+	snd_wavefront_t *dev = &card->wavefront;
+
+	/*
+	   Some comments on interrupts. I attempted a version of this
+	   driver that used interrupts throughout the code instead of
+	   doing busy and/or sleep-waiting. Alas, it appears that once
+	   the Motorola firmware is downloaded, the card *never*
+	   generates an RX interrupt. These are successfully generated
+	   during firmware loading, and after that wavefront_status()
+	   reports that an interrupt is pending on the card from time
+	   to time, but it never seems to be delivered to this
+	   driver. Note also that wavefront_status() continues to
+	   report that RX interrupts are enabled, suggesting that I
+	   didn't goof up and disable them by mistake.
+
+	   Thus, I stepped back to a prior version of
+	   wavefront_wait(), the only place where this really
+	   matters. Its sad, but I've looked through the code to check
+	   on things, and I really feel certain that the Motorola
+	   firmware prevents RX-ready interrupts.
+	*/
+
+	if ((wavefront_status(dev) & (STAT_INTR_READ|STAT_INTR_WRITE)) == 0) {
+		return;
+	}
+
+	dev->irq_ok = 1;
+	dev->irq_cnt++;
+	wake_up_interruptible (&dev->interrupt_sleeper);
+}
+
+/* STATUS REGISTER 
+
+0 Host Rx Interrupt Enable (1=Enabled)
+1 Host Rx Register Full (1=Full)
+2 Host Rx Interrupt Pending (1=Interrupt)
+3 Unused
+4 Host Tx Interrupt (1=Enabled)
+5 Host Tx Register empty (1=Empty)
+6 Host Tx Interrupt Pending (1=Interrupt)
+7 Unused
+*/
+
+int
+snd_wavefront_interrupt_bits (int irq)
+
+{
+	int bits;
+
+	switch (irq) {
+	case 9:
+		bits = 0x00;
+		break;
+	case 5:
+		bits = 0x08;
+		break;
+	case 12:
+		bits = 0x10;
+		break;
+	case 15:
+		bits = 0x18;
+		break;
+	
+	default:
+		snd_printk ("invalid IRQ %d\n", irq);
+		bits = -1;
+	}
+
+	return bits;
+}
+
+static void
+wavefront_should_cause_interrupt (snd_wavefront_t *dev, 
+				  int val, int port, int timeout)
+
+{
+	unsigned long flags;
+
+	save_flags (flags);
+	cli();
+	dev->irq_ok = 0;
+	outb (val,port);
+	interruptible_sleep_on_timeout (&dev->interrupt_sleeper, timeout);
+	restore_flags (flags);
+}
+
+int
+snd_wavefront_detect_irq (snd_wavefront_t *dev) 
+
+{
+	int i;
+	int possible_irqs[] = { 5, 9, 12, 15, -1 };
+
+	/* Note: according to the PnP dump, 7 and 11 are possible too, but the
+	   WaveFront SDK doesn't tell us how to set the card to use them.  
+	*/
+
+	snd_printk ("autodetecting WaveFront IRQ\n");
+
+	for (i = 0; possible_irqs[i] > 0; i++) {
+		if (snd_wavefront_check_irq (dev, possible_irqs[i]) == 0) {
+			snd_printk ("autodetected IRQ %d\n", 
+				    possible_irqs[i]);
+			return possible_irqs[i];
+		}
+	}
+
+	return -1;
+}
+
+int
+snd_wavefront_check_irq (snd_wavefront_t *dev, int irq)
+
+{
+	int bits;
+	unsigned long irq_mask;
+	short reported_irq;
+
+	bits = snd_wavefront_interrupt_bits (irq);
+
+	irq_mask = probe_irq_on ();
+
+	outb (0x0, dev->control_port); 
+	outb (0x80 | 0x40 | bits, dev->data_port);	
+	wavefront_should_cause_interrupt(dev, 0x80|0x40|0x10|0x1,
+					 dev->control_port,
+					 (reset_time*HZ)/100);
+
+	reported_irq = probe_irq_off (irq_mask);
+
+	if (reported_irq == 0) {
+		snd_printk ("No unassigned interrupts detected "
+			    "after h/w reset\n");
+		return -1;
+	} else if (reported_irq < 0) {
+		snd_printk ("Multiple unassigned interrupts detected "
+			    "after h/w reset\n");
+		return -1;
+	} else if (reported_irq != irq) {
+		return -1;
+	}
+
+	return 0; /* OK */
+}
+
+static int
+wavefront_reset_to_cleanliness (snd_wavefront_t *dev)
+
+{
+	int bits;
+	int hwv[2];
+
+	/* IRQ already checked */
+
+	bits = snd_wavefront_interrupt_bits (dev->irq);
+
+	/* try reset of port */
+
+	outb (0x0, dev->control_port); 
+  
+	/* At this point, the board is in reset, and the H/W initialization
+	   register is accessed at the same address as the data port.
+     
+	   Bit 7 - Enable IRQ Driver	
+	   0 - Tri-state the Wave-Board drivers for the PC Bus IRQs
+	   1 - Enable IRQ selected by bits 5:3 to be driven onto the PC Bus.
+     
+	   Bit 6 - MIDI Interface Select
+
+	   0 - Use the MIDI Input from the 26-pin WaveBlaster
+	   compatible header as the serial MIDI source
+	   1 - Use the MIDI Input from the 9-pin D connector as the
+	   serial MIDI source.
+     
+	   Bits 5:3 - IRQ Selection
+	   0 0 0 - IRQ 2/9
+	   0 0 1 - IRQ 5
+	   0 1 0 - IRQ 12
+	   0 1 1 - IRQ 15
+	   1 0 0 - Reserved
+	   1 0 1 - Reserved
+	   1 1 0 - Reserved
+	   1 1 1 - Reserved
+     
+	   Bits 2:1 - Reserved
+	   Bit 0 - Disable Boot ROM
+	   0 - memory accesses to 03FC30-03FFFFH utilize the internal Boot ROM
+	   1 - memory accesses to 03FC30-03FFFFH are directed to external 
+	   storage.
+     
+	*/
+
+	/* configure hardware: IRQ, enable interrupts, 
+	   plus external 9-pin MIDI interface selected
+	*/
+
+	outb (0x80 | 0x40 | bits, dev->data_port);	
+  
+	/* CONTROL REGISTER
+
+	   0 Host Rx Interrupt Enable (1=Enabled)      0x1
+	   1 Unused                                    0x2
+	   2 Unused                                    0x4
+	   3 Unused                                    0x8
+	   4 Host Tx Interrupt Enable                 0x10
+	   5 Mute (0=Mute; 1=Play)                    0x20
+	   6 Master Interrupt Enable (1=Enabled)      0x40
+	   7 Master Reset (0=Reset; 1=Run)            0x80
+
+	   Take us out of reset, mute output, master + TX + RX interrupts on.
+	   
+	   We'll get an interrupt presumably to tell us that the TX
+	   register is clear.
+	*/
+
+	wavefront_should_cause_interrupt(dev, 0x80|0x40|0x10|0x1,
+					 dev->control_port,
+					 (reset_time*HZ)/100);
+
+	/* Note: data port is now the data port, not the h/w initialization
+	   port.
+	 */
+
+	if (!dev->irq_ok) {
+		snd_printk ("intr not received after h/w un-reset.\n");
+		goto gone_bad;
+	} 
+
+	/* Note: data port is now the data port, not the h/w initialization
+	   port.
+
+	   At this point, only "HW VERSION" or "DOWNLOAD OS" commands
+	   will work. So, issue one of them, and wait for TX
+	   interrupt. This can take a *long* time after a cold boot,
+	   while the ISC ROM does its RAM test. The SDK says up to 4
+	   seconds - with 12MB of RAM on a Tropez+, it takes a lot
+	   longer than that (~16secs). Note that the card understands
+	   the difference between a warm and a cold boot, so
+	   subsequent ISC2115 reboots (say, caused by module
+	   reloading) will get through this much faster.
+
+	   XXX Interesting question: why is no RX interrupt received first ?
+	*/
+
+	wavefront_should_cause_interrupt(dev, WFC_HARDWARE_VERSION, 
+					 dev->data_port, ramcheck_time*HZ);
+
+	if (!dev->irq_ok) {
+		snd_printk ("post-RAM-check interrupt not received.\n");
+		goto gone_bad;
+	} 
+
+	if (!wavefront_wait (dev, STAT_CAN_READ)) {
+		snd_printk ("no response to HW version cmd.\n");
+		goto gone_bad;
+	}
+	
+	if ((hwv[0] = wavefront_read (dev)) == -1) {
+		snd_printk ("board not responding correctly.\n");
+		goto gone_bad;
+	}
+
+	if (hwv[0] == 0xFF) { /* NAK */
+
+		/* Board's RAM test failed. Try to read error code,
+		   and tell us about it either way.
+		*/
+		
+		if ((hwv[0] = wavefront_read (dev)) == -1) {
+			snd_printk ("on-board RAM test failed "
+				    "(bad error code).\n");
+		} else {
+			snd_printk ("on-board RAM test failed "
+				    "(error code: 0x%x).\n",
+				hwv[0]);
+		}
+		goto gone_bad;
+	}
+
+	/* We're OK, just get the next byte of the HW version response */
+
+	if ((hwv[1] = wavefront_read (dev)) == -1) {
+		snd_printk ("incorrect h/w response.\n");
+		goto gone_bad;
+	}
+
+	snd_printk ("hardware version %d.%d\n",
+		    hwv[0], hwv[1]);
+
+	return 0;
+
+
+     gone_bad:
+	return (1);
+}
+
+#define __KERNEL_SYSCALLS__
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/unistd.h>
+#include <asm/uaccess.h>
+
+static int errno;
+
+static int
+wavefront_download_firmware (snd_wavefront_t *dev, char *path)
+
+{
+	unsigned char section[WF_SECTION_MAX];
+	char section_length; /* yes, just a char; max value is WF_SECTION_MAX */
+	int section_cnt_downloaded = 0;
+	int fd;
+	int c;
+	int i;
+	mm_segment_t fs;
+
+	/* This tries to be a bit cleverer than the stuff Alan Cox did for
+	   the generic sound firmware, in that it actually knows
+	   something about the structure of the Motorola firmware. In
+	   particular, it uses a version that has been stripped of the
+	   20K of useless header information, and had section lengths
+	   added, making it possible to load the entire OS without any
+	   [kv]malloc() activity, since the longest entity we ever read is
+	   42 bytes (well, WF_SECTION_MAX) long.
+	*/
+
+	fs = get_fs();
+	set_fs (get_ds());
+
+	if ((fd = open (path, 0, 0)) < 0) {
+		snd_printk ("Unable to load \"%s\".\n",
+			path);
+		return 1;
+	}
+
+	while (1) {
+		int x;
+
+		if ((x = read (fd, &section_length, sizeof (section_length))) !=
+		    sizeof (section_length)) {
+			snd_printk ("firmware read error.\n");
+			goto failure;
+		}
+
+		if (section_length == 0) {
+			break;
+		}
+
+		if (read (fd, section, section_length) != section_length) {
+			snd_printk ("firmware section "
+				"read error.\n");
+			goto failure;
+		}
+
+		/* Send command */
+	
+		if (wavefront_write (dev, WFC_DOWNLOAD_OS)) {
+			goto failure;
+		}
+	
+		for (i = 0; i < section_length; i++) {
+			if (wavefront_write (dev, section[i])) {
+				goto failure;
+			}
+		}
+	
+		/* get ACK */
+	
+		if (wavefront_wait (dev, STAT_CAN_READ)) {
+
+			if ((c = inb (dev->data_port)) != WF_ACK) {
+
+				snd_printk ("download "
+					    "of section #%d not "
+					    "acknowledged, ack = 0x%x\n",
+					    section_cnt_downloaded + 1, c);
+				goto failure;
+		
+			}
+
+		} else {
+			snd_printk ("time out for firmware ACK.\n");
+			goto failure;
+		}
+
+	}
+
+	close (fd);
+	set_fs (fs);
+	return 0;
+
+ failure:
+	close (fd);
+	set_fs (fs);
+	snd_printk ("firmware download failed!!!\n");
+	return 1;
+}
+
+
+static int
+wavefront_do_reset (snd_wavefront_t *dev)
+
+{
+	char voices[1];
+
+	if (wavefront_reset_to_cleanliness (dev)) {
+		snd_printk ("hw reset failed.\n");
+		goto gone_bad;
+	}
+
+	if (dev->israw) {
+		if (wavefront_download_firmware (dev, ospath)) {
+			goto gone_bad;
+		}
+
+		dev->israw = 0;
+
+		/* Wait for the OS to get running. The protocol for
+		   this is non-obvious, and was determined by
+		   using port-IO tracing in DOSemu and some
+		   experimentation here.
+		   
+		   Rather than using timed waits, use interrupts creatively.
+		*/
+
+		wavefront_should_cause_interrupt (dev, WFC_NOOP,
+						  dev->data_port,
+						  (osrun_time*HZ));
+
+		if (!dev->irq_ok) {
+			snd_printk ("no post-OS interrupt.\n");
+			goto gone_bad;
+		}
+		
+		/* Now, do it again ! */
+		
+		wavefront_should_cause_interrupt (dev, WFC_NOOP,
+						  dev->data_port, (10*HZ));
+		
+		if (!dev->irq_ok) {
+			snd_printk ("no post-OS interrupt(2).\n");
+			goto gone_bad;
+		}
+
+		/* OK, no (RX/TX) interrupts any more, but leave mute
+		   in effect. 
+		*/
+		
+		outb (0x80|0x40, dev->control_port); 
+	}
+
+	/* SETUPSND.EXE asks for sample memory config here, but since i
+	   have no idea how to interpret the result, we'll forget
+	   about it.
+	*/
+	
+	if ((dev->freemem = wavefront_freemem (dev)) < 0) {
+		goto gone_bad;
+	}
+		
+	snd_printk ("available DRAM %dk\n", dev->freemem / 1024);
+
+	if (wavefront_write (dev, 0xf0) ||
+	    wavefront_write (dev, 1) ||
+	    (wavefront_read (dev) < 0)) {
+		dev->debug = 0;
+		snd_printk ("MPU emulation mode not set.\n");
+		goto gone_bad;
+	}
+
+	voices[0] = 32;
+
+	if (snd_wavefront_cmd (dev, WFC_SET_NVOICES, 0, voices)) {
+		snd_printk ("cannot set number of voices to 32.\n");
+		goto gone_bad;
+	}
+
+
+	return 0;
+
+ gone_bad:
+	/* reset that sucker so that it doesn't bother us. */
+
+	outb (0x0, dev->control_port);
+	dev->interrupts_are_midi = 0;
+	return 1;
+}
+
+int 
+snd_wavefront_start (snd_wavefront_t *dev)
+
+{
+	int samples_are_from_rom;
+
+	/* IMPORTANT: assumes that snd_wavefront_detect() and/or
+	   wavefront_reset_to_cleanliness() has already been called 
+	*/
+
+	if (dev->israw) {
+		samples_are_from_rom = 1;
+	} else {
+		/* XXX is this always true ? */
+		samples_are_from_rom = 0;
+	}
+
+	if (dev->israw || fx_raw) {
+		if (wavefront_do_reset (dev)) {
+			return -1;
+		}
+	}
+	/* Check for FX device, present only on Tropez+ */
+
+	dev->has_fx = (snd_wavefront_fx_detect (dev) == 0);
+
+	if (dev->has_fx && fx_raw) {
+		snd_wavefront_fx_start (dev);
+	}
+
+	wavefront_get_sample_status (dev, samples_are_from_rom);
+	wavefront_get_program_status (dev);
+	wavefront_get_patch_status (dev);
+
+	/* Start normal operation: unreset, master interrupt enabled, no mute
+	*/
+
+	outb (0x80|0x40|0x20, dev->control_port); 
+
+	return (0);
+}
+
+int
+snd_wavefront_detect (snd_wavefront_card_t *card)
+
+{
+	unsigned char   rbuf[4], wbuf[4];
+	snd_wavefront_t *dev = &card->wavefront;
+	
+	/* returns zero if a WaveFront card is successfully detected.
+	   negative otherwise.
+	*/
+
+	dev->israw = 0;
+	dev->has_fx = 0;
+	dev->debug = debug_default;
+	dev->interrupts_are_midi = 0;
+	dev->irq_cnt = 0;
+	dev->rom_samples_rdonly = 1;
+
+	if (snd_wavefront_cmd (dev, WFC_FIRMWARE_VERSION, rbuf, wbuf) == 0) {
+
+		dev->fw_version[0] = rbuf[0];
+		dev->fw_version[1] = rbuf[1];
+
+		snd_printk ("firmware %d.%d already loaded.\n",
+			    rbuf[0], rbuf[1]);
+
+		/* check that a command actually works */
+      
+		if (snd_wavefront_cmd (dev, WFC_HARDWARE_VERSION,
+				       rbuf, wbuf) == 0) {
+			dev->hw_version[0] = rbuf[0];
+			dev->hw_version[1] = rbuf[1];
+		} else {
+			snd_printk ("not raw, but no "
+				    "hardware version!\n");
+			return -1;
+		}
+
+		if (!wf_raw) {
+			return 0;
+		} else {
+			snd_printk ("reloading firmware as you requested.\n");
+			dev->israw = 1;
+		}
+
+	} else {
+
+		dev->israw = 1;
+		snd_printk ("no response to firmware probe, assume raw.\n");
+
+	}
+
+	return 0;
+}
+
+EXPORT_SYMBOL(snd_wavefront_synth_ioctl);
+EXPORT_SYMBOL(snd_wavefront_synth_open);
+EXPORT_SYMBOL(snd_wavefront_synth_release);
+EXPORT_SYMBOL(snd_wavefront_internal_interrupt);
+EXPORT_SYMBOL(snd_wavefront_interrupt_bits);
+EXPORT_SYMBOL(snd_wavefront_detect_irq);
+EXPORT_SYMBOL(snd_wavefront_check_irq);
+EXPORT_SYMBOL(snd_wavefront_start);
+EXPORT_SYMBOL(snd_wavefront_detect);
+EXPORT_SYMBOL(snd_wavefront_cmd);
+  /* wavefront_midi.c */
+EXPORT_SYMBOL(snd_wavefront_midi_interrupt);
+EXPORT_SYMBOL(snd_wavefront_midi_enable_virtual);
+EXPORT_SYMBOL(snd_wavefront_midi_disable_virtual);
+EXPORT_SYMBOL(snd_wavefront_midi_start);
+EXPORT_SYMBOL(snd_wavefront_midi_input);
+EXPORT_SYMBOL(snd_wavefront_midi_output);
+
+static int __init alsa_wavefront_init(void)
+{
+	return 0;
+}
+
+static void __exit alsa_wavefront_exit(void)
+{
+}
+
+module_init(alsa_wavefront_init)
+module_exit(alsa_wavefront_exit)
Index: /GPL/branches/alsa-resync1/alsa-kernel/makefile.os2
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/makefile.os2	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/makefile.os2	(revision 91)
@@ -0,0 +1,74 @@
+LOGO=-h
+
+NMAKEOPTS=-f makefile.os2 $(LOGO)
+
+!if "$(DEBUG)" == "1"
+NMAKEOPTS += DEBUG=1
+!endif
+
+!if "$(KEE)" == "0"
+NMAKEOPTS += KEE=0
+KEE=0
+!else
+NMAKEOPTS += KEE=1
+KEE=1
+!endif
+
+
+!if "$(DEBUG)" == "1"
+!Message BUILDING DEBUG VERSION
+!else
+!Message BUILDING RELEASE VERSION
+!endif
+
+
+#===================================================================
+#
+#   Specific dependencies
+#
+#===================================================================
+all: subdirs
+
+subdirs: .SYMBOLIC
+    @echo BUILDING core
+    @cd core
+    @wmake $(NMAKEOPTS)
+    @cd ..
+    @echo BUILDING drivers
+    @cd drivers
+    @wmake $(NMAKEOPTS)
+    @cd ..
+    @echo BUILDING isa
+    @cd isa
+    @wmake $(NMAKEOPTS)
+    @cd ..
+    @echo BUILDING pci
+    @cd pci
+    @wmake $(NMAKEOPTS)
+    @cd ..
+    @echo BUILDING synth
+    @cd synth
+    @wmake $(NMAKEOPTS)
+    @cd ..
+
+clean: .SYMBOLIC
+    @echo CLEANING core
+    @cd core
+    @wmake $(NMAKEOPTS) clean
+    @cd ..
+    @echo CLEANING drivers
+    @cd drivers
+    @wmake $(NMAKEOPTS) clean
+    @cd .. 
+    @echo CLEANING isa
+    @cd isa
+    @wmake $(NMAKEOPTS) clean
+    @cd ..
+    @echo CLEANING pci
+    @cd pci
+    @wmake $(NMAKEOPTS) clean
+    @cd ..
+    @echo CLEANING synth
+    @cd synth
+    @wmake $(NMAKEOPTS) clean
+    @cd ..
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/ac97/Makefile
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/ac97/Makefile	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/ac97/Makefile	(revision 91)
@@ -0,0 +1,27 @@
+#
+# Makefile for ALSA
+# Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz>
+#
+
+snd-ac97-codec-objs := ac97_codec.o ac97_proc.o ac97_patch.o
+snd-ak4531-codec-objs := ak4531_codec.o
+
+# Toplevel Module Dependency
+obj-$(CONFIG_SND_CS4281) += snd-ac97-codec.o
+obj-$(CONFIG_SND_ENS1370) += snd-ak4531-codec.o
+obj-$(CONFIG_SND_ENS1371) += snd-ac97-codec.o
+obj-$(CONFIG_SND_ES1968) += snd-ac97-codec.o
+obj-$(CONFIG_SND_FM801) += snd-ac97-codec.o
+obj-$(CONFIG_SND_ICE1712) += snd-ac97-codec.o
+obj-$(CONFIG_SND_ICE1724) += snd-ac97-codec.o
+obj-$(CONFIG_SND_INTEL8X0) += snd-ac97-codec.o
+obj-$(CONFIG_SND_MAESTRO3) += 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
+obj-$(CONFIG_SND_NM256) += snd-ac97-codec.o
+obj-$(CONFIG_SND_TRIDENT) += snd-ac97-codec.o
+obj-$(CONFIG_SND_YMFPCI) += snd-ac97-codec.o
+
+obj-m := $(sort $(obj-m))
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/ac97/ac97_codec.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/ac97/ac97_codec.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/ac97/ac97_codec.c	(revision 91)
@@ -0,0 +1,2921 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  Universal interface for Audio Codec '97
+ *
+ *  For more details look to AC '97 component specification revision 2.2
+ *  by Intel Corporation (http://developer.intel.com).
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/moduleparam.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/tlv.h>
+#include <sound/ac97_codec.h>
+#include <sound/asoundef.h>
+#include <sound/initval.h>
+#include "ac97_local.h"
+#include "ac97_id.h"
+#include "ac97_patch.h"
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Universal interface for Audio Codec '97");
+MODULE_LICENSE("GPL");
+
+static int enable_loopback;
+
+//module_param(enable_loopback, bool, 0444);
+MODULE_PARM_DESC(enable_loopback, "Enable AC97 ADC/DAC Loopback Control");
+
+#ifdef CONFIG_SND_AC97_POWER_SAVE
+static int power_save = 1;
+//module_param(power_save, bool, 0644);
+MODULE_PARM_DESC(power_save, "Enable AC97 power-saving control");
+#endif
+/*
+
+ */
+
+struct ac97_codec_id {
+	unsigned int id;
+	unsigned int mask;
+	const char *name;
+	int (*patch)(struct snd_ac97 *ac97);
+	int (*mpatch)(struct snd_ac97 *ac97);
+	unsigned int flags;
+};
+
+static const struct ac97_codec_id snd_ac97_codec_id_vendors[] = {
+{ 0x414b4d00, 0xffffff00, "Asahi Kasei",	NULL,	NULL },
+{ 0x41445300, 0xffffff00, "Analog Devices",	NULL,	NULL },
+{ 0x414c4300, 0xffffff00, "Realtek",		NULL,	NULL },
+{ 0x414c4700, 0xffffff00, "Realtek",		NULL,	NULL },
+{ 0x434d4900, 0xffffff00, "C-Media Electronics", NULL,	NULL },
+{ 0x43525900, 0xffffff00, "Cirrus Logic",	NULL,	NULL },
+{ 0x43585400, 0xffffff00, "Conexant",           NULL,	NULL },
+{ 0x44543000, 0xffffff00, "Diamond Technology", NULL,	NULL },
+{ 0x454d4300, 0xffffff00, "eMicro",		NULL,	NULL },
+{ 0x45838300, 0xffffff00, "ESS Technology",	NULL,	NULL },
+{ 0x48525300, 0xffffff00, "Intersil",		NULL,	NULL },
+{ 0x49434500, 0xffffff00, "ICEnsemble",		NULL,	NULL },
+{ 0x49544500, 0xffffff00, "ITE Tech.Inc",	NULL,	NULL },
+{ 0x4e534300, 0xffffff00, "National Semiconductor", NULL, NULL },
+{ 0x50534300, 0xffffff00, "Philips",		NULL,	NULL },
+{ 0x53494c00, 0xffffff00, "Silicon Laboratory",	NULL,	NULL },
+{ 0x54524100, 0xffffff00, "TriTech",		NULL,	NULL },
+{ 0x54584e00, 0xffffff00, "Texas Instruments",	NULL,	NULL },
+{ 0x56494100, 0xffffff00, "VIA Technologies",   NULL,	NULL },
+{ 0x57454300, 0xffffff00, "Winbond",		NULL,	NULL },
+{ 0x574d4c00, 0xffffff00, "Wolfson",		NULL,	NULL },
+{ 0x594d4800, 0xffffff00, "Yamaha",		NULL,	NULL },
+{ 0x83847600, 0xffffff00, "SigmaTel",		NULL,	NULL },
+{ 0,	      0, 	  NULL,			NULL,	NULL }
+};
+
+static const struct ac97_codec_id snd_ac97_codec_ids[] = {
+{ 0x414b4d00, 0xffffffff, "AK4540",		NULL,		NULL },
+{ 0x414b4d01, 0xffffffff, "AK4542",		NULL,		NULL },
+{ 0x414b4d02, 0xffffffff, "AK4543",		NULL,		NULL },
+{ 0x414b4d06, 0xffffffff, "AK4544A",		NULL,		NULL },
+{ 0x414b4d07, 0xffffffff, "AK4545",		NULL,		NULL },
+{ 0x41445303, 0xffffffff, "AD1819",		patch_ad1819,	NULL },
+{ 0x41445340, 0xffffffff, "AD1881",		patch_ad1881,	NULL },
+{ 0x41445348, 0xffffffff, "AD1881A",		patch_ad1881,	NULL },
+{ 0x41445360, 0xffffffff, "AD1885",		patch_ad1885,	NULL },
+{ 0x41445361, 0xffffffff, "AD1886",		patch_ad1886,	NULL },
+{ 0x41445362, 0xffffffff, "AD1887",		patch_ad1881,	NULL },
+{ 0x41445363, 0xffffffff, "AD1886A",		patch_ad1881,	NULL },
+{ 0x41445368, 0xffffffff, "AD1888",		patch_ad1888,	NULL },
+{ 0x41445370, 0xffffffff, "AD1980",		patch_ad1980,	NULL },
+{ 0x41445372, 0xffffffff, "AD1981A",		patch_ad1981a,	NULL },
+{ 0x41445374, 0xffffffff, "AD1981B",		patch_ad1981b,	NULL },
+{ 0x41445375, 0xffffffff, "AD1985",		patch_ad1985,	NULL },
+{ 0x41445378, 0xffffffff, "AD1986",		patch_ad1986,	NULL },
+{ 0x414c4300, 0xffffff00, "ALC100,100P", 	NULL,		NULL },
+{ 0x414c4710, 0xfffffff0, "ALC200,200P",	NULL,		NULL },
+{ 0x414c4721, 0xffffffff, "ALC650D",		NULL,	NULL }, /* already patched */
+{ 0x414c4722, 0xffffffff, "ALC650E",		NULL,	NULL }, /* already patched */
+{ 0x414c4723, 0xffffffff, "ALC650F",		NULL,	NULL }, /* already patched */
+{ 0x414c4720, 0xfffffff0, "ALC650",		patch_alc650,	NULL },
+{ 0x414c4760, 0xfffffff0, "ALC655",		patch_alc655,	NULL },
+{ 0x414c4781, 0xffffffff, "ALC658D",		NULL,	NULL }, /* already patched */
+{ 0x414c4780, 0xfffffff0, "ALC658",		patch_alc655,	NULL },
+{ 0x414c4790, 0xfffffff0, "ALC850",		patch_alc850,	NULL },
+{ 0x414c4730, 0xffffffff, "ALC101",		NULL,		NULL },
+{ 0x414c4740, 0xfffffff0, "ALC202",		NULL,		NULL },
+{ 0x414c4750, 0xfffffff0, "ALC250",		NULL,		NULL },
+{ 0x414c4770, 0xfffffff0, "ALC203",		NULL,		NULL },
+{ 0x434d4941, 0xffffffff, "CMI9738",		patch_cm9738,	NULL },
+{ 0x434d4961, 0xffffffff, "CMI9739",		patch_cm9739,	NULL },
+{ 0x434d4969, 0xffffffff, "CMI9780",		patch_cm9780,	NULL },
+{ 0x434d4978, 0xffffffff, "CMI9761A",		patch_cm9761,	NULL },
+{ 0x434d4982, 0xffffffff, "CMI9761B",		patch_cm9761,	NULL },
+{ 0x434d4983, 0xffffffff, "CMI9761A+",		patch_cm9761,	NULL },
+{ 0x43525900, 0xfffffff8, "CS4297",		NULL,		NULL },
+{ 0x43525910, 0xfffffff8, "CS4297A",		patch_cirrus_spdif,	NULL },
+{ 0x43525920, 0xfffffff8, "CS4298",		patch_cirrus_spdif,		NULL },
+{ 0x43525928, 0xfffffff8, "CS4294",		NULL,		NULL },
+{ 0x43525930, 0xfffffff8, "CS4299",		patch_cirrus_cs4299,	NULL },
+{ 0x43525948, 0xfffffff8, "CS4201",		NULL,		NULL },
+{ 0x43525958, 0xfffffff8, "CS4205",		patch_cirrus_spdif,	NULL },
+{ 0x43525960, 0xfffffff8, "CS4291",		NULL,		NULL },
+{ 0x43525970, 0xfffffff8, "CS4202",		NULL,		NULL },
+{ 0x43585421, 0xffffffff, "HSD11246",		NULL,		NULL },	// SmartMC II
+{ 0x43585428, 0xfffffff8, "Cx20468",		patch_conexant,	NULL }, // SmartAMC fixme: the mask might be different
+{ 0x44543031, 0xfffffff0, "DT0398",		NULL,		NULL },
+{ 0x454d4328, 0xffffffff, "EM28028",		NULL,		NULL },  // same as TR28028?
+{ 0x45838308, 0xffffffff, "ESS1988",		NULL,		NULL },
+{ 0x48525300, 0xffffff00, "HMP9701",		NULL,		NULL },
+{ 0x49434501, 0xffffffff, "ICE1230",		NULL,		NULL },
+{ 0x49434511, 0xffffffff, "ICE1232",		NULL,		NULL }, // alias VIA VT1611A?
+{ 0x49434514, 0xffffffff, "ICE1232A",		NULL,		NULL },
+{ 0x49434551, 0xffffffff, "VT1616", 		patch_vt1616,	NULL },
+{ 0x49434552, 0xffffffff, "VT1616i",		patch_vt1616,	NULL }, // VT1616 compatible (chipset integrated)
+{ 0x49544520, 0xffffffff, "IT2226E",		NULL,		NULL },
+{ 0x49544561, 0xffffffff, "IT2646E",		patch_it2646,	NULL },
+{ 0x4e534300, 0xffffffff, "LM4540,43,45,46,48",	NULL,		NULL }, // only guess --jk
+{ 0x4e534331, 0xffffffff, "LM4549",		NULL,		NULL },
+{ 0x4e534350, 0xffffffff, "LM4550",             patch_lm4550,   NULL }, // volume wrap fix
+{ 0x50534304, 0xffffffff, "UCB1400",		patch_ucb1400,	NULL },
+{ 0x53494c20, 0xffffffe0, "Si3036,8",		mpatch_si3036,	mpatch_si3036, AC97_MODEM_PATCH },
+{ 0x54524102, 0xffffffff, "TR28022",		NULL,		NULL },
+{ 0x54524106, 0xffffffff, "TR28026",		NULL,		NULL },
+{ 0x54524108, 0xffffffff, "TR28028",		patch_tritech_tr28028,	NULL }, // added by xin jin [07/09/99]
+{ 0x54524123, 0xffffffff, "TR28602",		NULL,		NULL }, // only guess --jk [TR28023 = eMicro EM28023 (new CT1297)]
+{ 0x54584e20, 0xffffffff, "TLC320AD9xC",	NULL,		NULL },
+{ 0x56494161, 0xffffffff, "VIA1612A",		NULL,		NULL }, // modified ICE1232 with S/PDIF
+{ 0x56494170, 0xffffffff, "VIA1617A",		patch_vt1617a,	NULL }, // modified VT1616 with S/PDIF
+{ 0x56494182, 0xffffffff, "VIA1618",		NULL,		NULL },
+{ 0x57454301, 0xffffffff, "W83971D",		NULL,		NULL },
+{ 0x574d4c00, 0xffffffff, "WM9701A",		NULL,		NULL },
+{ 0x574d4C03, 0xffffffff, "WM9703,WM9707,WM9708,WM9717", patch_wolfson03, NULL},
+{ 0x574d4C04, 0xffffffff, "WM9704M,WM9704Q",	patch_wolfson04, NULL},
+{ 0x574d4C05, 0xffffffff, "WM9705,WM9710",	patch_wolfson05, NULL},
+{ 0x574d4C09, 0xffffffff, "WM9709",		NULL,		NULL},
+{ 0x574d4C12, 0xffffffff, "WM9711,WM9712",	patch_wolfson11, NULL},
+{ 0x574d4c13, 0xffffffff, "WM9713,WM9714",	patch_wolfson13, NULL, AC97_DEFAULT_POWER_OFF},
+{ 0x594d4800, 0xffffffff, "YMF743",		NULL,		NULL },
+{ 0x594d4802, 0xffffffff, "YMF752",		NULL,		NULL },
+{ 0x594d4803, 0xffffffff, "YMF753",		patch_yamaha_ymf753,	NULL },
+{ 0x83847600, 0xffffffff, "STAC9700,83,84",	patch_sigmatel_stac9700,	NULL },
+{ 0x83847604, 0xffffffff, "STAC9701,3,4,5",	NULL,		NULL },
+{ 0x83847605, 0xffffffff, "STAC9704",		NULL,		NULL },
+{ 0x83847608, 0xffffffff, "STAC9708,11",	patch_sigmatel_stac9708,	NULL },
+{ 0x83847609, 0xffffffff, "STAC9721,23",	patch_sigmatel_stac9721,	NULL },
+{ 0x83847644, 0xffffffff, "STAC9744",		patch_sigmatel_stac9744,	NULL },
+{ 0x83847650, 0xffffffff, "STAC9750,51",	NULL,		NULL },	// patch?
+{ 0x83847652, 0xffffffff, "STAC9752,53",	NULL,		NULL }, // patch?
+{ 0x83847656, 0xffffffff, "STAC9756,57",	patch_sigmatel_stac9756,	NULL },
+{ 0x83847658, 0xffffffff, "STAC9758,59",	patch_sigmatel_stac9758,	NULL },
+{ 0x83847666, 0xffffffff, "STAC9766,67",	NULL,		NULL }, // patch?
+{ 0, 	      0,	  NULL,			NULL,		NULL }
+};
+
+static void update_power_regs(struct snd_ac97 *ac97);
+#ifdef CONFIG_SND_AC97_POWER_SAVE
+#define ac97_is_power_save_mode(ac97) \
+	((ac97->scaps & AC97_SCAP_POWER_SAVE) && power_save)
+#else
+#define ac97_is_power_save_mode(ac97) 0
+#endif
+
+
+/*
+ *  I/O routines
+ */
+
+static int snd_ac97_valid_reg(struct snd_ac97 *ac97, unsigned short reg)
+{
+	/* filter some registers for buggy codecs */
+	switch (ac97->id) {
+	case AC97_ID_AK4540:
+	case AC97_ID_AK4542:
+		if (reg <= 0x1c || reg == 0x20 || reg == 0x26 || reg >= 0x7c)
+			return 1;
+		return 0;
+	case AC97_ID_AD1819:	/* AD1819 */
+	case AC97_ID_AD1881:	/* AD1881 */
+	case AC97_ID_AD1881A:	/* AD1881A */
+		if (reg >= 0x3a && reg <= 0x6e)	/* 0x59 */
+			return 0;
+		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;
+		if (reg >= 0x3c && reg <= 0x6e)	/* 0x59 */
+			return 0;
+		return 1;
+	case AC97_ID_STAC9700:
+	case AC97_ID_STAC9704:
+	case AC97_ID_STAC9705:
+	case AC97_ID_STAC9708:
+	case AC97_ID_STAC9721:
+	case AC97_ID_STAC9744:
+	case AC97_ID_STAC9756:
+		if (reg <= 0x3a || reg >= 0x5a)
+			return 1;
+		return 0;
+	}
+	return 1;
+}
+
+/**
+ * snd_ac97_write - write a value on the given register
+ * @ac97: the ac97 instance
+ * @reg: the register to change
+ * @value: the value to set
+ *
+ * Writes a value on the given register.  This will invoke the write
+ * callback directly after the register check.
+ * This function doesn't change the register cache unlike
+ * #snd_ca97_write_cache(), so use this only when you don't want to
+ * reflect the change to the suspend/resume state.
+ */
+void snd_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short value)
+{
+	if (!snd_ac97_valid_reg(ac97, reg))
+		return;
+	if ((ac97->id & 0xffffff00) == AC97_ID_ALC100) {
+		/* Fix H/W bug of ALC100/100P */
+		if (reg == AC97_MASTER || reg == AC97_HEADPHONE)
+			ac97->bus->ops->write(ac97, AC97_RESET, 0);	/* reset audio codec */
+	}
+	ac97->bus->ops->write(ac97, reg, value);
+}
+
+EXPORT_SYMBOL(snd_ac97_write);
+
+/**
+ * snd_ac97_read - read a value from the given register
+ *
+ * @ac97: the ac97 instance
+ * @reg: the register to read
+ *
+ * Reads a value from the given register.  This will invoke the read
+ * callback directly after the register check.
+ *
+ * Returns the read value.
+ */
+unsigned short snd_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
+{
+	if (!snd_ac97_valid_reg(ac97, reg))
+		return 0;
+	return ac97->bus->ops->read(ac97, reg);
+}
+
+EXPORT_SYMBOL(snd_ac97_read);
+
+/* read a register - return the cached value if already read */
+static inline unsigned short snd_ac97_read_cache(struct snd_ac97 *ac97, unsigned short reg)
+{
+	if (! test_bit(reg, ac97->reg_accessed)) {
+		ac97->regs[reg] = ac97->bus->ops->read(ac97, reg);
+		// set_bit(reg, ac97->reg_accessed);
+	}
+	return ac97->regs[reg];
+}
+
+/**
+ * snd_ac97_write_cache - write a value on the given register and update the cache
+ * @ac97: the ac97 instance
+ * @reg: the register to change
+ * @value: the value to set
+ *
+ * Writes a value on the given register and updates the register
+ * cache.  The cached values are used for the cached-read and the
+ * suspend/resume.
+ */
+void snd_ac97_write_cache(struct snd_ac97 *ac97, unsigned short reg, unsigned short value)
+{
+	if (!snd_ac97_valid_reg(ac97, reg))
+		return;
+	down(&ac97->reg_mutex);
+	ac97->regs[reg] = value;
+	ac97->bus->ops->write(ac97, reg, value);
+	set_bit(reg, ac97->reg_accessed);
+	up(&ac97->reg_mutex);
+}
+
+/**
+ * snd_ac97_update - update the value on the given register
+ * @ac97: the ac97 instance
+ * @reg: the register to change
+ * @value: the value to set
+ *
+ * Compares the value with the register cache and updates the value
+ * only when the value is changed.
+ *
+ * Returns 1 if the value is changed, 0 if no change, or a negative
+ * code on failure.
+ */
+int snd_ac97_update(struct snd_ac97 *ac97, unsigned short reg, unsigned short value)
+{
+	int change;
+
+	if (!snd_ac97_valid_reg(ac97, reg))
+		return -EINVAL;
+	down(&ac97->reg_mutex);
+	change = ac97->regs[reg] != value;
+	if (change) {
+		ac97->regs[reg] = value;
+		ac97->bus->ops->write(ac97, reg, value);
+	}
+	set_bit(reg, ac97->reg_accessed);
+	up(&ac97->reg_mutex);
+	return change;
+}
+
+/**
+ * snd_ac97_update_bits - update the bits on the given register
+ * @ac97: the ac97 instance
+ * @reg: the register to change
+ * @mask: the bit-mask to change
+ * @value: the value to set
+ *
+ * Updates the masked-bits on the given register only when the value
+ * is changed.
+ *
+ * Returns 1 if the bits are changed, 0 if no change, or a negative
+ * code on failure.
+ */
+int snd_ac97_update_bits(struct snd_ac97 *ac97, unsigned short reg, unsigned short mask, unsigned short value)
+{
+	int change;
+
+	if (!snd_ac97_valid_reg(ac97, reg))
+		return -EINVAL;
+	down(&ac97->reg_mutex);
+	change = snd_ac97_update_bits_nolock(ac97, reg, mask, value);
+	up(&ac97->reg_mutex);
+	return change;
+}
+
+/* no lock version - see snd_ac97_updat_bits() */
+int snd_ac97_update_bits_nolock(struct snd_ac97 *ac97, unsigned short reg,
+				unsigned short mask, unsigned short value)
+{
+	int change;
+	unsigned short old, new;
+
+        old = snd_ac97_read_cache(ac97, reg);
+        new = (old & ~mask) | (value & mask);
+	change = old != new;
+	if (change) {
+		ac97->regs[reg] = new;
+		ac97->bus->ops->write(ac97, reg, new);
+	}
+	set_bit(reg, ac97->reg_accessed);
+	return change;
+}
+
+static int snd_ac97_ad18xx_update_pcm_bits(struct snd_ac97 *ac97, int codec, unsigned short mask, unsigned short value)
+{
+	int change;
+	unsigned short old, new, cfg;
+
+	down(&ac97->page_mutex);
+        old = ac97->spec.ad18xx.pcmreg[codec];
+        new = (old & ~mask) | (value & mask);
+	change = old != new;
+	if (change) {
+		down(&ac97->reg_mutex);
+		cfg = snd_ac97_read_cache(ac97, AC97_AD_SERIAL_CFG);
+		ac97->spec.ad18xx.pcmreg[codec] = new;
+		/* select single codec */
+		ac97->bus->ops->write(ac97, AC97_AD_SERIAL_CFG,
+				 (cfg & ~0x7000) |
+				 ac97->spec.ad18xx.unchained[codec] | ac97->spec.ad18xx.chained[codec]);
+		/* update PCM bits */
+		ac97->bus->ops->write(ac97, AC97_PCM, new);
+		/* select all codecs */
+		ac97->bus->ops->write(ac97, AC97_AD_SERIAL_CFG,
+				 cfg | 0x7000);
+		up(&ac97->reg_mutex);
+	}
+	up(&ac97->page_mutex);
+	return change;
+}
+
+/*
+ * Controls
+ */
+
+int snd_ac97_info_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+	struct ac97_enum *e = (struct ac97_enum *)kcontrol->private_value;
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = e->shift_l == e->shift_r ? 1 : 2;
+	uinfo->value.enumerated.items = e->mask;
+
+	if (uinfo->value.enumerated.item > e->mask - 1)
+		uinfo->value.enumerated.item = e->mask - 1;
+	strcpy(uinfo->value.enumerated.name, e->texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+int snd_ac97_get_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+	struct ac97_enum *e = (struct ac97_enum *)kcontrol->private_value;
+	unsigned short val, bitmask;
+
+	for (bitmask = 1; bitmask < e->mask; bitmask <<= 1)
+		;
+	val = snd_ac97_read_cache(ac97, e->reg);
+	ucontrol->value.enumerated.item[0] = (val >> e->shift_l) & (bitmask - 1);
+	if (e->shift_l != e->shift_r)
+		ucontrol->value.enumerated.item[1] = (val >> e->shift_r) & (bitmask - 1);
+
+	return 0;
+}
+
+int snd_ac97_put_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+	struct ac97_enum *e = (struct ac97_enum *)kcontrol->private_value;
+	unsigned short val;
+	unsigned short mask, bitmask;
+
+	for (bitmask = 1; bitmask < e->mask; bitmask <<= 1)
+		;
+	if (ucontrol->value.enumerated.item[0] > e->mask - 1)
+		return -EINVAL;
+	val = ucontrol->value.enumerated.item[0] << e->shift_l;
+	mask = (bitmask - 1) << e->shift_l;
+	if (e->shift_l != e->shift_r) {
+		if (ucontrol->value.enumerated.item[1] > e->mask - 1)
+			return -EINVAL;
+		val |= ucontrol->value.enumerated.item[1] << e->shift_r;
+		mask |= (bitmask - 1) << e->shift_r;
+	}
+	return snd_ac97_update_bits(ac97, e->reg, mask, val);
+}
+
+/* save/restore ac97 v2.3 paging */
+static int snd_ac97_page_save(struct snd_ac97 *ac97, int reg, struct snd_kcontrol *kcontrol)
+{
+	int page_save = -1;
+	if ((kcontrol->private_value & (1<<25)) &&
+	    (ac97->ext_id & AC97_EI_REV_MASK) >= AC97_EI_REV_23 &&
+	    (reg >= 0x60 && reg < 0x70)) {
+		unsigned short page = (kcontrol->private_value >> 26) & 0x0f;
+		down(&ac97->page_mutex); /* lock paging */
+		page_save = snd_ac97_read(ac97, AC97_INT_PAGING) & AC97_PAGE_MASK;
+		snd_ac97_update_bits(ac97, AC97_INT_PAGING, AC97_PAGE_MASK, page);
+	}
+	return page_save;
+}
+
+static void snd_ac97_page_restore(struct snd_ac97 *ac97, int page_save)
+{
+	if (page_save >= 0) {
+		snd_ac97_update_bits(ac97, AC97_INT_PAGING, AC97_PAGE_MASK, page_save);
+		up(&ac97->page_mutex); /* unlock paging */
+	}
+}
+
+/* volume and switch controls */
+int snd_ac97_info_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int shift = (kcontrol->private_value >> 8) & 0x0f;
+	int rshift = (kcontrol->private_value >> 12) & 0x0f;
+
+	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = shift == rshift ? 1 : 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	return 0;
+}
+
+int snd_ac97_get_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 8) & 0x0f;
+	int rshift = (kcontrol->private_value >> 12) & 0x0f;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0x01;
+	int page_save;
+
+	page_save = snd_ac97_page_save(ac97, reg, kcontrol);
+	ucontrol->value.integer.value[0] = (snd_ac97_read_cache(ac97, reg) >> shift) & mask;
+	if (shift != rshift)
+		ucontrol->value.integer.value[1] = (snd_ac97_read_cache(ac97, reg) >> rshift) & mask;
+	if (invert) {
+		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+		if (shift != rshift)
+			ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1];
+	}
+	snd_ac97_page_restore(ac97, page_save);
+	return 0;
+}
+
+int snd_ac97_put_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 8) & 0x0f;
+	int rshift = (kcontrol->private_value >> 12) & 0x0f;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0x01;
+	int err, page_save;
+	unsigned short val, val2, val_mask;
+
+	page_save = snd_ac97_page_save(ac97, reg, kcontrol);
+	val = (ucontrol->value.integer.value[0] & mask);
+	if (invert)
+		val = mask - val;
+	val_mask = mask << shift;
+	val = val << shift;
+	if (shift != rshift) {
+		val2 = (ucontrol->value.integer.value[1] & mask);
+		if (invert)
+			val2 = mask - val2;
+		val_mask |= mask << rshift;
+		val |= val2 << rshift;
+	}
+	err = snd_ac97_update_bits(ac97, reg, val_mask, val);
+        snd_ac97_page_restore(ac97, page_save);
+#ifdef CONFIG_SND_AC97_POWER_SAVE
+	/* check analog mixer power-down */
+	if ((val_mask & 0x8000) &&
+	    (kcontrol->private_value & (1<<30))) {
+		if (val & 0x8000)
+			ac97->power_up &= ~(1 << (reg>>1));
+		else
+                    ac97->power_up |= 1 << (reg>>1);
+                update_power_regs(ac97);
+	}
+#endif
+	return err;
+}
+
+static const struct snd_kcontrol_new snd_ac97_controls_master_mono[2] = {
+AC97_SINGLE("Master Mono Playback Switch", AC97_MASTER_MONO, 15, 1, 1),
+AC97_SINGLE("Master Mono Playback Volume", AC97_MASTER_MONO, 0, 31, 1)
+};
+
+static const struct snd_kcontrol_new snd_ac97_controls_tone[2] = {
+AC97_SINGLE("Tone Control - Bass", AC97_MASTER_TONE, 8, 15, 1),
+AC97_SINGLE("Tone Control - Treble", AC97_MASTER_TONE, 0, 15, 1)
+};
+
+static const struct snd_kcontrol_new snd_ac97_controls_pc_beep[2] = {
+AC97_SINGLE("PC Speaker Playback Switch", AC97_PC_BEEP, 15, 1, 1),
+AC97_SINGLE("PC Speaker Playback Volume", AC97_PC_BEEP, 1, 15, 1)
+};
+
+static const struct snd_kcontrol_new snd_ac97_controls_mic_boost =
+	AC97_SINGLE("Mic Boost (+20dB)", AC97_MIC, 6, 1, 0);
+
+
+static const char* std_rec_sel[] = {"Mic", "CD", "Video", "Aux", "Line", "Mix", "Mix Mono", "Phone"};
+static const char* std_3d_path[] = {"pre 3D", "post 3D"};
+static const char* std_mix[] = {"Mix", "Mic"};
+static const char* std_mic[] = {"Mic1", "Mic2"};
+
+static const struct ac97_enum std_enum[] = {
+AC97_ENUM_DOUBLE(AC97_REC_SEL, 8, 0, 8, std_rec_sel),
+AC97_ENUM_SINGLE(AC97_GENERAL_PURPOSE, 15, 2, std_3d_path),
+AC97_ENUM_SINGLE(AC97_GENERAL_PURPOSE, 9, 2, std_mix),
+AC97_ENUM_SINGLE(AC97_GENERAL_PURPOSE, 8, 2, std_mic),
+};
+
+static const struct snd_kcontrol_new snd_ac97_control_capture_src =
+AC97_ENUM("Capture Source", std_enum[0]);
+
+static const struct snd_kcontrol_new snd_ac97_control_capture_vol =
+AC97_DOUBLE("Capture Volume", AC97_REC_GAIN, 8, 0, 15, 0);
+
+static const struct snd_kcontrol_new snd_ac97_controls_mic_capture[2] = {
+AC97_SINGLE("Mic Capture Switch", AC97_REC_GAIN_MIC, 15, 1, 1),
+AC97_SINGLE("Mic Capture Volume", AC97_REC_GAIN_MIC, 0, 15, 0)
+};
+
+enum {
+	AC97_GENERAL_PCM_OUT = 0,
+	AC97_GENERAL_STEREO_ENHANCEMENT,
+	AC97_GENERAL_3D,
+	AC97_GENERAL_LOUDNESS,
+	AC97_GENERAL_MONO,
+	AC97_GENERAL_MIC,
+	AC97_GENERAL_LOOPBACK
+};
+
+static const struct snd_kcontrol_new snd_ac97_controls_general[7] = {
+AC97_ENUM("PCM Out Path & Mute", std_enum[1]),
+AC97_SINGLE("Simulated Stereo Enhancement", AC97_GENERAL_PURPOSE, 14, 1, 0),
+AC97_SINGLE("3D Control - Switch", AC97_GENERAL_PURPOSE, 13, 1, 0),
+AC97_SINGLE("Loudness (bass boost)", AC97_GENERAL_PURPOSE, 12, 1, 0),
+AC97_ENUM("Mono Output Select", std_enum[2]),
+AC97_ENUM("Mic Select", std_enum[3]),
+AC97_SINGLE("ADC/DAC Loopback", AC97_GENERAL_PURPOSE, 7, 1, 0)
+};
+
+const struct snd_kcontrol_new snd_ac97_controls_3d[2] = {
+AC97_SINGLE("3D Control - Center", AC97_3D_CONTROL, 8, 15, 0),
+AC97_SINGLE("3D Control - Depth", AC97_3D_CONTROL, 0, 15, 0)
+};
+
+static const struct snd_kcontrol_new snd_ac97_controls_center[2] = {
+AC97_SINGLE("Center Playback Switch", AC97_CENTER_LFE_MASTER, 7, 1, 1),
+AC97_SINGLE("Center Playback Volume", AC97_CENTER_LFE_MASTER, 0, 31, 1)
+};
+
+static const struct snd_kcontrol_new snd_ac97_controls_lfe[2] = {
+AC97_SINGLE("LFE Playback Switch", AC97_CENTER_LFE_MASTER, 15, 1, 1),
+AC97_SINGLE("LFE Playback Volume", AC97_CENTER_LFE_MASTER, 8, 31, 1)
+};
+
+static const struct snd_kcontrol_new snd_ac97_control_eapd =
+AC97_SINGLE("External Amplifier", AC97_POWERDOWN, 15, 1, 1);
+
+static const struct snd_kcontrol_new snd_ac97_controls_modem_switches[2] = {
+AC97_SINGLE("Off-hook Switch", AC97_GPIO_STATUS, 0, 1, 0),
+AC97_SINGLE("Caller ID Switch", AC97_GPIO_STATUS, 2, 1, 0)
+};
+
+/* change the existing EAPD control as inverted */
+static void set_inv_eapd(struct snd_ac97 *ac97, struct snd_kcontrol *kctl)
+{
+	kctl->private_value = AC97_SINGLE_VALUE(AC97_POWERDOWN, 15, 1, 0);
+	snd_ac97_update_bits(ac97, AC97_POWERDOWN, (1<<15), (1<<15)); /* EAPD up */
+	ac97->scaps |= AC97_SCAP_INV_EAPD;
+}
+
+static int snd_ac97_spdif_mask_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+	uinfo->count = 1;
+	return 0;
+}
+
+static int snd_ac97_spdif_cmask_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.iec958.status[0] = IEC958_AES0_PROFESSIONAL |
+					   IEC958_AES0_NONAUDIO |
+					   IEC958_AES0_CON_EMPHASIS_5015 |
+					   IEC958_AES0_CON_NOT_COPYRIGHT;
+	ucontrol->value.iec958.status[1] = IEC958_AES1_CON_CATEGORY |
+					   IEC958_AES1_CON_ORIGINAL;
+	ucontrol->value.iec958.status[3] = IEC958_AES3_CON_FS;
+	return 0;
+}
+
+static int snd_ac97_spdif_pmask_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	/* FIXME: AC'97 spec doesn't say which bits are used for what */
+	ucontrol->value.iec958.status[0] = IEC958_AES0_PROFESSIONAL |
+					   IEC958_AES0_NONAUDIO |
+					   IEC958_AES0_PRO_FS |
+					   IEC958_AES0_PRO_EMPHASIS_5015;
+	return 0;
+}
+
+static int snd_ac97_spdif_default_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+
+	down(&ac97->reg_mutex);
+	ucontrol->value.iec958.status[0] = ac97->spdif_status & 0xff;
+	ucontrol->value.iec958.status[1] = (ac97->spdif_status >> 8) & 0xff;
+	ucontrol->value.iec958.status[2] = (ac97->spdif_status >> 16) & 0xff;
+	ucontrol->value.iec958.status[3] = (ac97->spdif_status >> 24) & 0xff;
+	up(&ac97->reg_mutex);
+	return 0;
+}
+
+static int snd_ac97_spdif_default_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+	unsigned int new = 0;
+	unsigned short val = 0;
+	int change;
+
+	new = val = ucontrol->value.iec958.status[0] & (IEC958_AES0_PROFESSIONAL|IEC958_AES0_NONAUDIO);
+	if (ucontrol->value.iec958.status[0] & IEC958_AES0_PROFESSIONAL) {
+		new |= ucontrol->value.iec958.status[0] & (IEC958_AES0_PRO_FS|IEC958_AES0_PRO_EMPHASIS_5015);
+		switch (new & IEC958_AES0_PRO_FS) {
+		case IEC958_AES0_PRO_FS_44100: val |= 0<<12; break;
+		case IEC958_AES0_PRO_FS_48000: val |= 2<<12; break;
+		case IEC958_AES0_PRO_FS_32000: val |= 3<<12; break;
+		default:		       val |= 1<<12; break;
+		}
+		if ((new & IEC958_AES0_PRO_EMPHASIS) == IEC958_AES0_PRO_EMPHASIS_5015)
+			val |= 1<<3;
+	} else {
+		new |= ucontrol->value.iec958.status[0] & (IEC958_AES0_CON_EMPHASIS_5015|IEC958_AES0_CON_NOT_COPYRIGHT);
+		new |= ((ucontrol->value.iec958.status[1] & (IEC958_AES1_CON_CATEGORY|IEC958_AES1_CON_ORIGINAL)) << 8);
+		new |= ((ucontrol->value.iec958.status[3] & IEC958_AES3_CON_FS) << 24);
+		if ((new & IEC958_AES0_CON_EMPHASIS) == IEC958_AES0_CON_EMPHASIS_5015)
+			val |= 1<<3;
+		if (!(new & IEC958_AES0_CON_NOT_COPYRIGHT))
+			val |= 1<<2;
+		val |= ((new >> 8) & 0xff) << 4;	// category + original
+		switch ((new >> 24) & 0xff) {
+		case IEC958_AES3_CON_FS_44100: val |= 0<<12; break;
+		case IEC958_AES3_CON_FS_48000: val |= 2<<12; break;
+		case IEC958_AES3_CON_FS_32000: val |= 3<<12; break;
+		default:		       val |= 1<<12; break;
+		}
+	}
+
+	down(&ac97->reg_mutex);
+	change = ac97->spdif_status != new;
+	ac97->spdif_status = new;
+
+	if (ac97->flags & AC97_CS_SPDIF) {
+		int x = (val >> 12) & 0x03;
+		switch (x) {
+		case 0: x = 1; break;  // 44.1
+		case 2: x = 0; break;  // 48.0
+		default: x = 0; break; // illegal.
+		}
+		change |= snd_ac97_update_bits_nolock(ac97, AC97_CSR_SPDIF, 0x3fff, ((val & 0xcfff) | (x << 12)));
+	} else if (ac97->flags & AC97_CX_SPDIF) {
+		int v;
+		v = new & (IEC958_AES0_CON_EMPHASIS_5015|IEC958_AES0_CON_NOT_COPYRIGHT) ? 0 : AC97_CXR_COPYRGT;
+		v |= new & IEC958_AES0_NONAUDIO ? AC97_CXR_SPDIF_AC3 : AC97_CXR_SPDIF_PCM;
+		change |= snd_ac97_update_bits_nolock(ac97, AC97_CXR_AUDIO_MISC,
+						      AC97_CXR_SPDIF_MASK | AC97_CXR_COPYRGT,
+						      v);
+	} else {
+		unsigned short extst = snd_ac97_read_cache(ac97, AC97_EXTENDED_STATUS);
+		snd_ac97_update_bits_nolock(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, 0); /* turn off */
+
+		change |= snd_ac97_update_bits_nolock(ac97, AC97_SPDIF, 0x3fff, val);
+		if (extst & AC97_EA_SPDIF) {
+			snd_ac97_update_bits_nolock(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, AC97_EA_SPDIF); /* turn on again */
+                }
+	}
+	up(&ac97->reg_mutex);
+
+	return change;
+}
+
+static int snd_ac97_put_spsa(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 8) & 0xff;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	// int invert = (kcontrol->private_value >> 24) & 0xff;
+	unsigned short value, old, new;
+	int change;
+
+	value = (ucontrol->value.integer.value[0] & mask);
+
+	down(&ac97->reg_mutex);
+	mask <<= shift;
+	value <<= shift;
+	old = snd_ac97_read_cache(ac97, reg);
+	new = (old & ~mask) | value;
+	change = old != new;
+
+	if (change) {
+		unsigned short extst = snd_ac97_read_cache(ac97, AC97_EXTENDED_STATUS);
+		snd_ac97_update_bits_nolock(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, 0); /* turn off */
+		change = snd_ac97_update_bits_nolock(ac97, reg, mask, value);
+		if (extst & AC97_EA_SPDIF)
+			snd_ac97_update_bits_nolock(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, AC97_EA_SPDIF); /* turn on again */
+	}
+	up(&ac97->reg_mutex);
+	return change;
+}
+
+const struct snd_kcontrol_new snd_ac97_controls_spdif[5] = {
+	{
+		.access = SNDRV_CTL_ELEM_ACCESS_READ,
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK),
+		.info = snd_ac97_spdif_mask_info,
+		.get = snd_ac97_spdif_cmask_get,
+	},
+	{
+		.access = SNDRV_CTL_ELEM_ACCESS_READ,
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PRO_MASK),
+		.info = snd_ac97_spdif_mask_info,
+		.get = snd_ac97_spdif_pmask_get,
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
+		.info = snd_ac97_spdif_mask_info,
+		.get = snd_ac97_spdif_default_get,
+		.put = snd_ac97_spdif_default_put,
+	},
+
+	AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH),AC97_EXTENDED_STATUS, 2, 1, 0),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "AC97-SPSA",
+		.info = snd_ac97_info_volsw,
+		.get = snd_ac97_get_volsw,
+		.put = snd_ac97_put_spsa,
+		.private_value = AC97_SINGLE_VALUE(AC97_EXTENDED_STATUS, 4, 3, 0)
+	},
+};
+
+#define AD18XX_PCM_BITS(xname, codec, lshift, rshift, mask) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_ac97_ad18xx_pcm_info_bits, \
+  .get = snd_ac97_ad18xx_pcm_get_bits, .put = snd_ac97_ad18xx_pcm_put_bits, \
+  .private_value = (codec) | ((lshift) << 8) | ((rshift) << 12) | ((mask) << 16) }
+
+static int snd_ac97_ad18xx_pcm_info_bits(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+	int mask = (kcontrol->private_value >> 16) & 0x0f;
+	int lshift = (kcontrol->private_value >> 8) & 0x0f;
+	int rshift = (kcontrol->private_value >> 12) & 0x0f;
+
+	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	if (lshift != rshift && (ac97->flags & AC97_STEREO_MUTES))
+		uinfo->count = 2;
+	else
+		uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	return 0;
+}
+
+static int snd_ac97_ad18xx_pcm_get_bits(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+	int codec = kcontrol->private_value & 3;
+	int lshift = (kcontrol->private_value >> 8) & 0x0f;
+	int rshift = (kcontrol->private_value >> 12) & 0x0f;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+
+	ucontrol->value.integer.value[0] = mask - ((ac97->spec.ad18xx.pcmreg[codec] >> lshift) & mask);
+	if (lshift != rshift && (ac97->flags & AC97_STEREO_MUTES))
+		ucontrol->value.integer.value[1] = mask - ((ac97->spec.ad18xx.pcmreg[codec] >> rshift) & mask);
+	return 0;
+}
+
+static int snd_ac97_ad18xx_pcm_put_bits(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+	int codec = kcontrol->private_value & 3;
+	int lshift = (kcontrol->private_value >> 8) & 0x0f;
+	int rshift = (kcontrol->private_value >> 12) & 0x0f;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	unsigned short val, valmask;
+
+	val = (mask - (ucontrol->value.integer.value[0] & mask)) << lshift;
+	valmask = mask << lshift;
+	if (lshift != rshift && (ac97->flags & AC97_STEREO_MUTES)) {
+		val |= (mask - (ucontrol->value.integer.value[1] & mask)) << rshift;
+		valmask |= mask << rshift;
+	}
+	return snd_ac97_ad18xx_update_pcm_bits(ac97, codec, valmask, val);
+}
+
+#define AD18XX_PCM_VOLUME(xname, codec) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_ac97_ad18xx_pcm_info_volume, \
+  .get = snd_ac97_ad18xx_pcm_get_volume, .put = snd_ac97_ad18xx_pcm_put_volume, \
+  .private_value = codec }
+
+static int snd_ac97_ad18xx_pcm_info_volume(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 31;
+	return 0;
+}
+
+static int snd_ac97_ad18xx_pcm_get_volume(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+	int codec = kcontrol->private_value & 3;
+
+	down(&ac97->page_mutex);
+	ucontrol->value.integer.value[0] = 31 - ((ac97->spec.ad18xx.pcmreg[codec] >> 0) & 31);
+	ucontrol->value.integer.value[1] = 31 - ((ac97->spec.ad18xx.pcmreg[codec] >> 8) & 31);
+	up(&ac97->page_mutex);
+	return 0;
+}
+
+static int snd_ac97_ad18xx_pcm_put_volume(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+	int codec = kcontrol->private_value & 3;
+	unsigned short val1, val2;
+
+	val1 = 31 - (ucontrol->value.integer.value[0] & 31);
+	val2 = 31 - (ucontrol->value.integer.value[1] & 31);
+	return snd_ac97_ad18xx_update_pcm_bits(ac97, codec, 0x1f1f, (val1 << 8) | val2);
+}
+
+static const struct snd_kcontrol_new snd_ac97_controls_ad18xx_pcm[2] = {
+AD18XX_PCM_BITS("PCM Playback Switch", 0, 15, 7, 1),
+AD18XX_PCM_VOLUME("PCM Playback Volume", 0)
+};
+
+static const struct snd_kcontrol_new snd_ac97_controls_ad18xx_surround[2] = {
+AD18XX_PCM_BITS("Surround Playback Switch", 1, 15, 7, 1),
+AD18XX_PCM_VOLUME("Surround Playback Volume", 1)
+};
+
+static const struct snd_kcontrol_new snd_ac97_controls_ad18xx_center[2] = {
+AD18XX_PCM_BITS("Center Playback Switch", 2, 15, 15, 1),
+AD18XX_PCM_BITS("Center Playback Volume", 2, 8, 8, 31)
+};
+
+static const struct snd_kcontrol_new snd_ac97_controls_ad18xx_lfe[2] = {
+AD18XX_PCM_BITS("LFE Playback Switch", 2, 7, 7, 1),
+AD18XX_PCM_BITS("LFE Playback Volume", 2, 0, 0, 31)
+};
+
+/*
+ *
+ */
+
+static void snd_ac97_powerdown(struct snd_ac97 *ac97);
+
+static int snd_ac97_bus_free(struct snd_ac97_bus *bus)
+{
+	if (bus) {
+		snd_ac97_bus_proc_done(bus);
+		kfree(bus->pcms);
+		if (bus->private_free)
+			bus->private_free(bus);
+		kfree(bus);
+	}
+	return 0;
+}
+
+static int snd_ac97_bus_dev_free(struct snd_device *device)
+{
+	struct snd_ac97_bus *bus = device->device_data;
+	return snd_ac97_bus_free(bus);
+}
+
+static int snd_ac97_free(struct snd_ac97 *ac97)
+{
+    if (ac97) {
+#ifdef CONFIG_SND_AC97_POWER_SAVE
+        if (ac97->power_workq)
+            destroy_workqueue(ac97->power_workq);
+#endif
+        snd_ac97_proc_done(ac97);
+        if (ac97->bus)
+            ac97->bus->codec[ac97->num] = NULL;
+        if (ac97->private_free)
+            ac97->private_free(ac97);
+        kfree(ac97);
+    }
+    return 0;
+}
+
+static int snd_ac97_dev_free(struct snd_device *device)
+{
+	struct snd_ac97 *ac97 = device->device_data;
+	snd_ac97_powerdown(ac97); /* for avoiding click noises during shut down */
+	return snd_ac97_free(ac97);
+}
+
+static int snd_ac97_try_volume_mix(struct snd_ac97 * ac97, int reg)
+{
+	unsigned short val, mask = 0x8000;
+
+	if (! snd_ac97_valid_reg(ac97, reg))
+		return 0;
+
+	switch (reg) {
+	case AC97_MASTER_TONE:
+		return ac97->caps & 0x04 ? 1 : 0;
+	case AC97_HEADPHONE:
+		return ac97->caps & 0x10 ? 1 : 0;
+	case AC97_REC_GAIN_MIC:
+		return ac97->caps & 0x01 ? 1 : 0;
+	case AC97_3D_CONTROL:
+		if (ac97->caps & 0x7c00) {
+			val = snd_ac97_read(ac97, reg);
+			/* if nonzero - fixed and we can't set it */
+			return val == 0;
+		}
+		return 0;
+	case AC97_CENTER_LFE_MASTER:	/* center */
+		if ((ac97->ext_id & AC97_EI_CDAC) == 0)
+			return 0;
+		break;
+	case AC97_CENTER_LFE_MASTER+1:	/* lfe */
+		if ((ac97->ext_id & AC97_EI_LDAC) == 0)
+			return 0;
+		reg = AC97_CENTER_LFE_MASTER;
+		mask = 0x0080;
+		break;
+	case AC97_SURROUND_MASTER:
+		if ((ac97->ext_id & AC97_EI_SDAC) == 0)
+			return 0;
+		break;
+	}
+
+	val = snd_ac97_read(ac97, reg);
+	if (!(val & mask)) {
+		/* nothing seems to be here - mute flag is not set */
+		/* try another test */
+		snd_ac97_write_cache(ac97, reg, val | mask);
+		val = snd_ac97_read(ac97, reg);
+		val = snd_ac97_read(ac97, reg);
+		if (!(val & mask))
+			return 0;	/* nothing here */
+	}
+	return 1;		/* success, useable */
+}
+
+static void check_volume_resolution(struct snd_ac97 *ac97, int reg, unsigned char *lo_max, unsigned char *hi_max)
+{
+	unsigned short cbit[3] = { 0x20, 0x10, 0x01 };
+	unsigned char max[3] = { 63, 31, 15 };
+	int i;
+
+        /* first look up the static resolution table */
+        if (ac97->res_table) {
+            const struct snd_ac97_res_table *tbl;
+            for (tbl = ac97->res_table; tbl->reg; tbl++) {
+                if (tbl->reg == reg) {
+                    *lo_max = tbl->bits & 0xff;
+                    *hi_max = (tbl->bits >> 8) & 0xff;
+                    return;
+                }
+            }
+        }
+
+        *lo_max = *hi_max = 0;
+	for (i = 0 ; i < ARRAY_SIZE(cbit); i++) {
+		unsigned short val;
+		snd_ac97_write(ac97, reg, 0x8080 | cbit[i] | (cbit[i] << 8));
+		/* Do the read twice due to buffers on some ac97 codecs.
+		 * e.g. The STAC9704 returns exactly what you wrote the the register
+		 * if you read it immediately. This causes the detect routine to fail.
+		 */
+		val = snd_ac97_read(ac97, reg);
+		val = snd_ac97_read(ac97, reg);
+		if (! *lo_max && (val & 0x7f) == cbit[i])
+			*lo_max = max[i];
+		if (! *hi_max && ((val >> 8) & 0x7f) == cbit[i])
+			*hi_max = max[i];
+		if (*lo_max && *hi_max)
+			break;
+	}
+}
+
+int snd_ac97_try_bit(struct snd_ac97 * ac97, int reg, int bit)
+{
+	unsigned short mask, val, orig, res;
+
+	mask = 1 << bit;
+	orig = snd_ac97_read(ac97, reg);
+	val = orig ^ mask;
+	snd_ac97_write(ac97, reg, val);
+	res = snd_ac97_read(ac97, reg);
+	snd_ac97_write_cache(ac97, reg, orig);
+	return res == val;
+}
+
+/* check the volume resolution of center/lfe */
+static void snd_ac97_change_volume_params2(struct snd_ac97 * ac97, int reg, int shift, unsigned char *max)
+{
+	unsigned short val, val1;
+
+	*max = 63;
+	val = 0x8080 | (0x20 << shift);
+	snd_ac97_write(ac97, reg, val);
+	val1 = snd_ac97_read(ac97, reg);
+	if (val != val1) {
+		*max = 31;
+	}
+	/* reset volume to zero */
+	snd_ac97_write_cache(ac97, reg, 0x8080);
+}
+
+static inline int printable(unsigned int x)
+{
+	x &= 0xff;
+	if (x < ' ' || x >= 0x71) {
+		if (x <= 0x89)
+			return x - 0x71 + 'A';
+		return '?';
+	}
+	return x;
+}
+
+struct snd_kcontrol *snd_ac97_cnew(const struct snd_kcontrol_new *_template, struct snd_ac97 * ac97)
+{
+	struct snd_kcontrol_new template;
+	memcpy(&template, _template, sizeof(template));
+	template.index = ac97->num;
+	return snd_ctl_new1(&template, ac97);
+}
+
+/*
+ * create mute switch(es) for normal stereo controls
+ */
+static int snd_ac97_cmute_new_stereo(struct snd_card *card, char *name, int reg,
+				     int check_stereo, int check_amix,
+				     struct snd_ac97 *ac97)
+{
+	struct snd_kcontrol *kctl;
+	int err;
+        unsigned short val, val1, mute_mask;
+        snd_kcontrol_new_t tmp = {0};
+
+	if (! snd_ac97_valid_reg(ac97, reg))
+		return 0;
+
+	mute_mask = 0x8000;
+	val = snd_ac97_read(ac97, reg);
+	if (check_stereo || (ac97->flags & AC97_STEREO_MUTES)) {
+		/* check whether both mute bits work */
+		val1 = val | 0x8080;
+		snd_ac97_write(ac97, reg, val1);
+		if (val1 == snd_ac97_read(ac97, reg))
+			mute_mask = 0x8080;
+        }
+#if 1
+        tmp.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+        tmp.name = name;
+        tmp.info = snd_ac97_info_volsw;
+        tmp.get = snd_ac97_get_volsw;
+        tmp.put = snd_ac97_put_volsw;
+        tmp.index = ac97->num;
+        if (mute_mask == 0x8080) {
+            tmp.private_value = reg | (15 << 8) | (7 << 12) | (1 << 16) | (1 << 24);
+            if (check_amix)
+                tmp.private_value |= (1 << 30);
+        } else {
+            tmp.private_value = reg | (15 << 8) | (1 << 16) | (1 << 24);
+            if (check_amix)
+                tmp.private_value |= (1 << 30);
+        }
+        kctl = snd_ctl_new1(&tmp, ac97);
+#else
+	if (mute_mask == 0x8080) {
+		struct snd_kcontrol_new tmp = AC97_DOUBLE(name, reg, 15, 7, 1, 1);
+		tmp.index = ac97->num;
+		kctl = snd_ctl_new1(&tmp, ac97);
+	} else {
+            struct snd_kcontrol_new tmp = AC97_SINGLE(name, reg, 15, 1, 1);
+            if (check_amix)
+                tmp.private_value |= (1 << 30);
+
+		tmp.index = ac97->num;
+		kctl = snd_ctl_new1(&tmp, ac97);
+        }
+#endif
+	err = snd_ctl_add(card, kctl);
+	if (err < 0)
+		return err;
+	/* mute as default */
+	snd_ac97_write_cache(ac97, reg, val | mute_mask);
+	return 0;
+}
+
+/*
+ * set dB information
+ */
+static DECLARE_TLV_DB_SCALE(db_scale_4bit, -4500, 300, 0);
+static DECLARE_TLV_DB_SCALE(db_scale_5bit, -4650, 150, 0);
+static DECLARE_TLV_DB_SCALE(db_scale_6bit, -9450, 150, 0);
+static DECLARE_TLV_DB_SCALE(db_scale_5bit_12db_max, -3450, 150, 0);
+static DECLARE_TLV_DB_SCALE(db_scale_rec_gain, 0, 150, 0);
+
+static unsigned int *find_db_scale(unsigned int maxval)
+{
+	switch (maxval) {
+	case 0x0f: return db_scale_4bit;
+	case 0x1f: return db_scale_5bit;
+	case 0x3f: return db_scale_6bit;
+	}
+	return NULL;
+}
+
+static void set_tlv_db_scale(struct snd_kcontrol *kctl, unsigned int *tlv)
+{	
+	kctl->tlv.p = tlv;
+	if (tlv)
+		kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
+}
+
+/*
+
+ * create a volume for normal stereo/mono controls
+ */
+static int snd_ac97_cvol_new(struct snd_card *card, char *name, int reg, unsigned int lo_max,
+			     unsigned int hi_max, struct snd_ac97 *ac97)
+{
+	int err;
+        struct snd_kcontrol *kctl;
+        snd_kcontrol_new_t tmp = {0};
+
+
+	if (! snd_ac97_valid_reg(ac97, reg))
+            return 0;
+#if 1
+    tmp.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+    tmp.name = name;
+    tmp.info = snd_ac97_info_volsw;
+    tmp.get = snd_ac97_get_volsw;
+    tmp.put = snd_ac97_put_volsw;
+    tmp.index = ac97->num;
+    if (hi_max) {
+        /* invert */
+        tmp.private_value = reg | (8 << 8) | (0 << 12) | (lo_max << 16) | (1 << 24);
+    } else {
+        /* invert */
+        tmp.private_value = reg | (0 << 8) | (0 << 12) | (lo_max << 16) | (1 << 24);
+    }
+    kctl = snd_ctl_new1(&tmp, ac97);
+
+#else
+        if (hi_max) {
+		/* invert */
+		struct snd_kcontrol_new tmp = AC97_DOUBLE(name, reg, 8, 0, lo_max, 1);
+		tmp.index = ac97->num;
+		kctl = snd_ctl_new1(&tmp, ac97);
+	} else {
+		/* invert */
+		struct snd_kcontrol_new tmp = AC97_SINGLE(name, reg, 0, lo_max, 1);
+		tmp.index = ac97->num;
+		kctl = snd_ctl_new1(&tmp, ac97);
+        }
+#endif
+	if (reg >= AC97_PHONE && reg <= AC97_PCM)
+		set_tlv_db_scale(kctl, db_scale_5bit_12db_max);
+	else
+		set_tlv_db_scale(kctl, find_db_scale(lo_max));
+        err = snd_ctl_add(card, kctl);
+	if (err < 0)
+		return err;
+	snd_ac97_write_cache(ac97, reg,
+			     (snd_ac97_read(ac97, reg) & 0x8080) |
+			     lo_max | (hi_max << 8));
+	return 0;
+}
+
+/*
+ * create a mute-switch and a volume for normal stereo/mono controls
+ */
+static int snd_ac97_cmix_new_stereo(struct snd_card *card, const char *pfx,
+				    int reg, int check_stereo, int check_amix,
+				    struct snd_ac97 *ac97)
+{
+	int err;
+	char name[44];
+	unsigned char lo_max, hi_max;
+
+	if (! snd_ac97_valid_reg(ac97, reg))
+		return 0;
+
+	if (snd_ac97_try_bit(ac97, reg, 15)) {
+            sprintf(name, "%s Switch", pfx);
+            if ((err = snd_ac97_cmute_new_stereo(card, name, reg,
+                                                 check_stereo, check_amix,
+                                                 ac97)) < 0)
+			return err;
+	}
+	check_volume_resolution(ac97, reg, &lo_max, &hi_max);
+	if (lo_max) {
+		sprintf(name, "%s Volume", pfx);
+		if ((err = snd_ac97_cvol_new(card, name, reg, lo_max, hi_max, ac97)) < 0)
+			return err;
+	}
+	return 0;
+}
+
+#define snd_ac97_cmix_new(card, pfx, reg, acheck, ac97) \
+	snd_ac97_cmix_new_stereo(card, pfx, reg, 0, acheck, ac97)
+#define snd_ac97_cmute_new(card, name, reg, acheck, ac97) \
+	snd_ac97_cmute_new_stereo(card, name, reg, 0, acheck, ac97)
+
+static unsigned int snd_ac97_determine_spdif_rates(struct snd_ac97 *ac97);
+
+static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
+{
+	struct snd_card *card = ac97->bus->card;
+	struct snd_kcontrol *kctl;
+	int err;
+	unsigned int idx;
+	unsigned char max;
+
+	/* build master controls */
+	/* AD claims to remove this control from AD1887, although spec v2.2 does not allow this */
+        if (snd_ac97_try_volume_mix(ac97, AC97_MASTER)) {
+#ifndef TARGET_OS2
+            if (ac97->flags & AC97_HAS_NO_MASTER_VOL)
+                err = snd_ac97_cmute_new(card, "Master Playback Switch",
+                                         AC97_MASTER, 0, ac97);
+            else
+#endif
+                err = snd_ac97_cmix_new(card, "Master Playback",
+                                        AC97_MASTER, 0, ac97);
+            if (err < 0)
+                return err;
+	}
+
+	ac97->regs[AC97_CENTER_LFE_MASTER] = 0x8080;
+
+        /* build center controls */
+        if ((snd_ac97_try_volume_mix(ac97, AC97_CENTER_LFE_MASTER))
+            && !(ac97->flags & AC97_AD_MULTI)) {
+		if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_center[0], ac97))) < 0)
+			return err;
+		if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_center[1], ac97))) < 0)
+			return err;
+		snd_ac97_change_volume_params2(ac97, AC97_CENTER_LFE_MASTER, 0, &max);
+		kctl->private_value &= ~(0xff << 16);
+                kctl->private_value |= (int)max << 16;
+                set_tlv_db_scale(kctl, find_db_scale(max));
+		snd_ac97_write_cache(ac97, AC97_CENTER_LFE_MASTER, ac97->regs[AC97_CENTER_LFE_MASTER] | max);
+	}
+
+        /* build LFE controls */
+        if ((snd_ac97_try_volume_mix(ac97, AC97_CENTER_LFE_MASTER+1))
+            && !(ac97->flags & AC97_AD_MULTI)) {
+		if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_lfe[0], ac97))) < 0)
+			return err;
+		if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_lfe[1], ac97))) < 0)
+			return err;
+		snd_ac97_change_volume_params2(ac97, AC97_CENTER_LFE_MASTER, 8, &max);
+		kctl->private_value &= ~(0xff << 16);
+                kctl->private_value |= (int)max << 16;
+                set_tlv_db_scale(kctl, find_db_scale(max));
+		snd_ac97_write_cache(ac97, AC97_CENTER_LFE_MASTER, ac97->regs[AC97_CENTER_LFE_MASTER] | max << 8);
+	}
+
+        /* build surround controls */
+        if ((snd_ac97_try_volume_mix(ac97, AC97_SURROUND_MASTER))
+            && !(ac97->flags & AC97_AD_MULTI)) {
+            /* Surround Master (0x38) is with stereo mutes */
+            if ((err = snd_ac97_cmix_new_stereo(card, "Surround Playback",
+                                                AC97_SURROUND_MASTER, 1, 0,
+                                                ac97)) < 0)
+			return err;
+	}
+
+	/* build headphone controls */
+        if (snd_ac97_try_volume_mix(ac97, AC97_HEADPHONE)) {
+            if ((err = snd_ac97_cmix_new(card, "Headphone Playback",
+                                         AC97_HEADPHONE, 0, ac97)) < 0)
+                return err;
+	}
+
+	/* build master mono controls */
+        if (snd_ac97_try_volume_mix(ac97, AC97_MASTER_MONO)) {
+            if ((err = snd_ac97_cmix_new(card, "Master Mono Playback",
+                                         AC97_MASTER_MONO, 0, ac97)) < 0)
+                return err;
+	}
+
+	/* build master tone controls */
+	if (!(ac97->flags & AC97_HAS_NO_TONE)) {
+		if (snd_ac97_try_volume_mix(ac97, AC97_MASTER_TONE)) {
+			for (idx = 0; idx < 2; idx++) {
+				if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_tone[idx], ac97))) < 0)
+					return err;
+				if (ac97->id == AC97_ID_YMF753) {
+					kctl->private_value &= ~(0xff << 16);
+					kctl->private_value |= 7 << 16;
+				}
+			}
+			snd_ac97_write_cache(ac97, AC97_MASTER_TONE, 0x0f0f);
+		}
+	}
+
+	/* build PC Speaker controls */
+	if (!(ac97->flags & AC97_HAS_NO_PC_BEEP) &&
+		((ac97->flags & AC97_HAS_PC_BEEP) ||
+	    snd_ac97_try_volume_mix(ac97, AC97_PC_BEEP))) {
+		for (idx = 0; idx < 2; idx++)
+			if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_pc_beep[idx], ac97))) < 0)
+ 				return err;
+		set_tlv_db_scale(kctl, db_scale_4bit);
+		snd_ac97_write_cache(ac97, AC97_PC_BEEP,
+				     snd_ac97_read(ac97, AC97_PC_BEEP) | 0x801e);
+	}
+
+	/* build Phone controls */
+	if (!(ac97->flags & AC97_HAS_NO_PHONE)) {
+            if (snd_ac97_try_volume_mix(ac97, AC97_PHONE)) {
+                if ((err = snd_ac97_cmix_new(card, "Phone Playback",
+                                             AC97_PHONE, 1, ac97)) < 0)
+                    return err;
+            }
+        }
+
+	/* build MIC controls */
+	if (!(ac97->flags & AC97_HAS_NO_MIC)) {
+            if (snd_ac97_try_volume_mix(ac97, AC97_MIC)) {
+                if ((err = snd_ac97_cmix_new(card, "Mic Playback",
+                                             AC97_MIC, 1, ac97)) < 0)
+                    return err;
+                if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_mic_boost, ac97))) < 0)
+                    return err;
+            }
+        }
+
+	/* build Line controls */
+        if (snd_ac97_try_volume_mix(ac97, AC97_LINE)) {
+            if ((err = snd_ac97_cmix_new(card, "Line Playback",
+                                         AC97_LINE, 1, ac97)) < 0)
+                return err;
+	}
+
+	/* build CD controls */
+	if (!(ac97->flags & AC97_HAS_NO_CD)) {
+            if (snd_ac97_try_volume_mix(ac97, AC97_CD)) {
+                if ((err = snd_ac97_cmix_new(card, "CD Playback",
+                                             AC97_CD, 1, ac97)) < 0)
+                    return err;
+            }
+        }
+
+	/* build Video controls */
+	if (!(ac97->flags & AC97_HAS_NO_VIDEO)) {
+            if (snd_ac97_try_volume_mix(ac97, AC97_VIDEO)) {
+                if ((err = snd_ac97_cmix_new(card, "Video Playback",
+                                             AC97_VIDEO, 1, ac97)) < 0)
+                    return err;
+            }
+	}
+
+        /* build Aux controls */
+        if (!(ac97->flags & AC97_HAS_NO_AUX)) {
+            if (snd_ac97_try_volume_mix(ac97, AC97_AUX)) {
+                if ((err = snd_ac97_cmix_new(card, "Aux Playback",
+                                             AC97_AUX, 1, ac97)) < 0)
+                    return err;
+            }
+	}
+
+	/* build PCM controls */
+	if (ac97->flags & AC97_AD_MULTI) {
+		unsigned short init_val;
+		if (ac97->flags & AC97_STEREO_MUTES)
+			init_val = 0x9f9f;
+		else
+			init_val = 0x9f1f;
+                for (idx = 0; idx < 2; idx++)
+                    if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_ad18xx_pcm[idx], ac97))) < 0)
+                        return err;
+                set_tlv_db_scale(kctl, db_scale_5bit);
+		ac97->spec.ad18xx.pcmreg[0] = init_val;
+		if (ac97->scaps & AC97_SCAP_SURROUND_DAC) {
+                    for (idx = 0; idx < 2; idx++)
+                        if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_ad18xx_surround[idx], ac97))) < 0)
+                            return err;
+                    set_tlv_db_scale(kctl, db_scale_5bit);
+                    ac97->spec.ad18xx.pcmreg[1] = init_val;
+		}
+		if (ac97->scaps & AC97_SCAP_CENTER_LFE_DAC) {
+                    for (idx = 0; idx < 2; idx++)
+                        if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_ad18xx_center[idx], ac97))) < 0)
+                            return err;
+                    set_tlv_db_scale(kctl, db_scale_5bit);
+                    for (idx = 0; idx < 2; idx++)
+                        if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_ad18xx_lfe[idx], ac97))) < 0)
+                            return err;
+                    set_tlv_db_scale(kctl, db_scale_5bit);
+			ac97->spec.ad18xx.pcmreg[2] = init_val;
+		}
+		snd_ac97_write_cache(ac97, AC97_PCM, init_val);
+	} else {
+            if (!(ac97->flags & AC97_HAS_NO_STD_PCM)) {
+#ifndef TARGET_OS2
+                if (ac97->flags & AC97_HAS_NO_PCM_VOL)
+                    err = snd_ac97_cmute_new(card,
+                                             "PCM Playback Switch",
+                                             AC97_PCM, 0, ac97);
+                else
+#endif
+                    err = snd_ac97_cmix_new(card, "PCM Playback",
+                                            AC97_PCM, 0, ac97);
+                if (err < 0)
+                    return err;
+            }
+	}
+
+	/* build Capture controls */
+	if (!(ac97->flags & AC97_HAS_NO_REC_GAIN)) {
+		if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_control_capture_src, ac97))) < 0)
+			return err;
+                if (snd_ac97_try_bit(ac97, AC97_REC_GAIN, 15)) {
+                    err = snd_ac97_cmute_new(card, "Capture Switch",
+                                             AC97_REC_GAIN, 0, ac97);
+                    if (err < 0)
+                        return err;
+                }
+                if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_control_capture_vol, ac97))) < 0)
+                    return err;
+                set_tlv_db_scale(kctl, db_scale_rec_gain);
+		snd_ac97_write_cache(ac97, AC97_REC_SEL, 0x0000);
+		snd_ac97_write_cache(ac97, AC97_REC_GAIN, 0x0000);
+	}
+	/* build MIC Capture controls */
+	if (snd_ac97_try_volume_mix(ac97, AC97_REC_GAIN_MIC)) {
+            for (idx = 0; idx < 2; idx++)
+                if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_mic_capture[idx], ac97))) < 0)
+                    return err;
+            set_tlv_db_scale(kctl, db_scale_rec_gain);
+            snd_ac97_write_cache(ac97, AC97_REC_GAIN_MIC, 0x0000);
+	}
+
+	/* build PCM out path & mute control */
+	if (snd_ac97_try_bit(ac97, AC97_GENERAL_PURPOSE, 15)) {
+		if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_PCM_OUT], ac97))) < 0)
+			return err;
+	}
+
+	/* build Simulated Stereo Enhancement control */
+	if (ac97->caps & 0x0008) {
+		if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_STEREO_ENHANCEMENT], ac97))) < 0)
+			return err;
+	}
+
+	/* build 3D Stereo Enhancement control */
+	if (snd_ac97_try_bit(ac97, AC97_GENERAL_PURPOSE, 13)) {
+		if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_3D], ac97))) < 0)
+			return err;
+	}
+
+	/* build Loudness control */
+	if (ac97->caps & 0x0020) {
+		if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_LOUDNESS], ac97))) < 0)
+			return err;
+	}
+
+	/* build Mono output select control */
+	if (snd_ac97_try_bit(ac97, AC97_GENERAL_PURPOSE, 9)) {
+		if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_MONO], ac97))) < 0)
+			return err;
+	}
+
+	/* build Mic select control */
+	if (snd_ac97_try_bit(ac97, AC97_GENERAL_PURPOSE, 8)) {
+		if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_MIC], ac97))) < 0)
+			return err;
+	}
+
+	/* build ADC/DAC loopback control */
+	if (enable_loopback && snd_ac97_try_bit(ac97, AC97_GENERAL_PURPOSE, 7)) {
+		if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_LOOPBACK], ac97))) < 0)
+			return err;
+	}
+
+	snd_ac97_update_bits(ac97, AC97_GENERAL_PURPOSE, ~AC97_GP_DRSS_MASK, 0x0000);
+
+	/* build 3D controls */
+	if (ac97->build_ops->build_3d) {
+		ac97->build_ops->build_3d(ac97);
+	} else {
+		if (snd_ac97_try_volume_mix(ac97, AC97_3D_CONTROL)) {
+			unsigned short val;
+			val = 0x0707;
+			snd_ac97_write(ac97, AC97_3D_CONTROL, val);
+			val = snd_ac97_read(ac97, AC97_3D_CONTROL);
+			val = val == 0x0606;
+			if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_3d[0], ac97))) < 0)
+				return err;
+			if (val)
+				kctl->private_value = AC97_3D_CONTROL | (9 << 8) | (7 << 16);
+			if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_3d[1], ac97))) < 0)
+				return err;
+			if (val)
+				kctl->private_value = AC97_3D_CONTROL | (1 << 8) | (7 << 16);
+			snd_ac97_write_cache(ac97, AC97_3D_CONTROL, 0x0000);
+		}
+	}
+
+        /* build S/PDIF controls */
+
+	/* Hack for ASUS P5P800-VM, which does not indicate S/PDIF capability */
+	if (ac97->subsystem_vendor == 0x1043 &&
+	    ac97->subsystem_device == 0x810f)
+		ac97->ext_id |= AC97_EI_SPDIF;
+
+	if ((ac97->ext_id & AC97_EI_SPDIF) && !(ac97->scaps & AC97_SCAP_NO_SPDIF)) {
+		if (ac97->build_ops->build_spdif) {
+			if ((err = ac97->build_ops->build_spdif(ac97)) < 0)
+				return err;
+		} else {
+			for (idx = 0; idx < 5; idx++)
+				if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_spdif[idx], ac97))) < 0)
+					return err;
+			if (ac97->build_ops->build_post_spdif) {
+				if ((err = ac97->build_ops->build_post_spdif(ac97)) < 0)
+					return err;
+			}
+			/* set default PCM S/PDIF params */
+			/* consumer,PCM audio,no copyright,no preemphasis,PCM coder,original,48000Hz */
+			snd_ac97_write_cache(ac97, AC97_SPDIF, 0x2a20);
+			ac97->rates[AC97_RATES_SPDIF] = snd_ac97_determine_spdif_rates(ac97);
+		}
+		ac97->spdif_status = SNDRV_PCM_DEFAULT_CON_SPDIF;
+	}
+
+	/* build chip specific controls */
+	if (ac97->build_ops->build_specific)
+		if ((err = ac97->build_ops->build_specific(ac97)) < 0)
+			return err;
+
+	if (snd_ac97_try_bit(ac97, AC97_POWERDOWN, 15)) {
+		kctl = snd_ac97_cnew(&snd_ac97_control_eapd, ac97);
+		if (! kctl)
+			return -ENOMEM;
+		if (ac97->scaps & AC97_SCAP_INV_EAPD)
+			set_inv_eapd(ac97, kctl);
+		if ((err = snd_ctl_add(card, kctl)) < 0)
+			return err;
+	}
+
+	return 0;
+}
+
+static int snd_ac97_modem_build(struct snd_card *card, struct snd_ac97 * ac97)
+{
+	int err, idx;
+
+	//printk("AC97_GPIO_CFG = %x\n",snd_ac97_read(ac97,AC97_GPIO_CFG));
+	snd_ac97_write(ac97, AC97_GPIO_CFG, 0xffff & ~(AC97_GPIO_LINE1_OH));
+	snd_ac97_write(ac97, AC97_GPIO_POLARITY, 0xffff & ~(AC97_GPIO_LINE1_OH));
+	snd_ac97_write(ac97, AC97_GPIO_STICKY, 0xffff);
+	snd_ac97_write(ac97, AC97_GPIO_WAKEUP, 0x0);
+	snd_ac97_write(ac97, AC97_MISC_AFE, 0x0);
+
+	/* build modem switches */
+	for (idx = 0; idx < ARRAY_SIZE(snd_ac97_controls_modem_switches); idx++)
+		if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_ac97_controls_modem_switches[idx], ac97))) < 0)
+			return err;
+
+	/* build chip specific controls */
+	if (ac97->build_ops->build_specific)
+		if ((err = ac97->build_ops->build_specific(ac97)) < 0)
+			return err;
+
+	return 0;
+}
+
+static int snd_ac97_test_rate(struct snd_ac97 *ac97, int reg, int shadow_reg, int rate)
+{
+	unsigned short val;
+	unsigned int tmp;
+
+	tmp = ((unsigned int)rate * ac97->bus->clock) / 48000;
+	snd_ac97_write_cache(ac97, reg, tmp & 0xffff);
+	if (shadow_reg)
+		snd_ac97_write_cache(ac97, shadow_reg, tmp & 0xffff);
+	val = snd_ac97_read(ac97, reg);
+	return val == (tmp & 0xffff);
+}
+
+static void snd_ac97_determine_rates(struct snd_ac97 *ac97, int reg, int shadow_reg, unsigned int *r_result)
+{
+	unsigned int result = 0;
+	unsigned short saved;
+
+        if (ac97->bus->no_vra) {
+		*r_result = SNDRV_PCM_RATE_48000;
+		if ((ac97->flags & AC97_DOUBLE_RATE) &&
+		    reg == AC97_PCM_FRONT_DAC_RATE)
+			*r_result |= SNDRV_PCM_RATE_96000;
+		return;
+	}
+
+	saved = snd_ac97_read(ac97, reg);
+	if ((ac97->ext_id & AC97_EI_DRA) && reg == AC97_PCM_FRONT_DAC_RATE)
+		snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS,
+				     AC97_EA_DRA, 0);
+	/* test a non-standard rate */
+	if (snd_ac97_test_rate(ac97, reg, shadow_reg, 11000))
+		result |= SNDRV_PCM_RATE_CONTINUOUS;
+	/* let's try to obtain standard rates */
+	if (snd_ac97_test_rate(ac97, reg, shadow_reg, 8000))
+		result |= SNDRV_PCM_RATE_8000;
+	if (snd_ac97_test_rate(ac97, reg, shadow_reg, 11025))
+		result |= SNDRV_PCM_RATE_11025;
+	if (snd_ac97_test_rate(ac97, reg, shadow_reg, 16000))
+		result |= SNDRV_PCM_RATE_16000;
+	if (snd_ac97_test_rate(ac97, reg, shadow_reg, 22050))
+		result |= SNDRV_PCM_RATE_22050;
+	if (snd_ac97_test_rate(ac97, reg, shadow_reg, 32000))
+		result |= SNDRV_PCM_RATE_32000;
+	if (snd_ac97_test_rate(ac97, reg, shadow_reg, 44100))
+		result |= SNDRV_PCM_RATE_44100;
+	if (snd_ac97_test_rate(ac97, reg, shadow_reg, 48000))
+		result |= SNDRV_PCM_RATE_48000;
+	if ((ac97->flags & AC97_DOUBLE_RATE) &&
+	    reg == AC97_PCM_FRONT_DAC_RATE) {
+		/* test standard double rates */
+		snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS,
+				     AC97_EA_DRA, AC97_EA_DRA);
+		if (snd_ac97_test_rate(ac97, reg, shadow_reg, 64000 / 2))
+			result |= SNDRV_PCM_RATE_64000;
+		if (snd_ac97_test_rate(ac97, reg, shadow_reg, 88200 / 2))
+			result |= SNDRV_PCM_RATE_88200;
+		if (snd_ac97_test_rate(ac97, reg, shadow_reg, 96000 / 2))
+			result |= SNDRV_PCM_RATE_96000;
+		/* some codecs don't support variable double rates */
+		if (!snd_ac97_test_rate(ac97, reg, shadow_reg, 76100 / 2))
+			result &= ~SNDRV_PCM_RATE_CONTINUOUS;
+		snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS,
+				     AC97_EA_DRA, 0);
+	}
+	/* restore the default value */
+	snd_ac97_write_cache(ac97, reg, saved);
+	if (shadow_reg)
+		snd_ac97_write_cache(ac97, shadow_reg, saved);
+	*r_result = result;
+}
+
+/* check AC97_SPDIF register to accept which sample rates */
+static unsigned int snd_ac97_determine_spdif_rates(struct snd_ac97 *ac97)
+{
+	unsigned int result = 0;
+	int i;
+	static unsigned short ctl_bits[] = {
+		AC97_SC_SPSR_44K, AC97_SC_SPSR_32K, AC97_SC_SPSR_48K
+	};
+	static unsigned int rate_bits[] = {
+		SNDRV_PCM_RATE_44100, SNDRV_PCM_RATE_32000, SNDRV_PCM_RATE_48000
+	};
+
+	for (i = 0; i < (int)ARRAY_SIZE(ctl_bits); i++) {
+		snd_ac97_update_bits(ac97, AC97_SPDIF, AC97_SC_SPSR_MASK, ctl_bits[i]);
+		if ((snd_ac97_read(ac97, AC97_SPDIF) & AC97_SC_SPSR_MASK) == ctl_bits[i])
+			result |= rate_bits[i];
+	}
+	return result;
+}
+
+/* look for the codec id table matching with the given id */
+static const struct ac97_codec_id *look_for_codec_id(const struct ac97_codec_id *table,
+						     unsigned int id)
+{
+	const struct ac97_codec_id *pid;
+
+	for (pid = table; pid->id; pid++)
+		if (pid->id == (id & pid->mask))
+			return pid;
+	return NULL;
+}
+
+void snd_ac97_get_name(struct snd_ac97 *ac97, unsigned int id, char *name, int modem)
+{
+	const struct ac97_codec_id *pid;
+
+	sprintf(name, "0x%x %c%c%c", id,
+		printable(id >> 24),
+		printable(id >> 16),
+		printable(id >> 8));
+	pid = look_for_codec_id(snd_ac97_codec_id_vendors, id);
+	if (! pid)
+		return;
+
+	strcpy(name, pid->name);
+	if (ac97 && pid->patch) {
+		if ((modem && (pid->flags & AC97_MODEM_PATCH)) ||
+		    (! modem && ! (pid->flags & AC97_MODEM_PATCH)))
+			pid->patch(ac97);
+	}
+
+	pid = look_for_codec_id(snd_ac97_codec_ids, id);
+	if (pid) {
+		strcat(name, " ");
+		strcat(name, pid->name);
+		if (pid->mask != 0xffffffff)
+			sprintf(name + strlen(name), " rev %d", id & ~pid->mask);
+		if (ac97 && pid->patch) {
+			if ((modem && (pid->flags & AC97_MODEM_PATCH)) ||
+			    (! modem && ! (pid->flags & AC97_MODEM_PATCH)))
+				pid->patch(ac97);
+		}
+	} else
+		sprintf(name + strlen(name), " id %x", id & 0xff);
+}
+
+/**
+ * snd_ac97_get_short_name - retrieve codec name
+ * @ac97: the codec instance
+ *
+ * Returns the short identifying name of the codec.
+ */
+const char *snd_ac97_get_short_name(struct snd_ac97 *ac97)
+{
+	const struct ac97_codec_id *pid;
+
+	for (pid = snd_ac97_codec_ids; pid->id; pid++)
+		if (pid->id == (ac97->id & pid->mask))
+			return pid->name;
+	return "unknown codec";
+}
+
+
+/* wait for a while until registers are accessible after RESET
+ * return 0 if ok, negative not ready
+ */
+static int ac97_reset_wait(struct snd_ac97 *ac97, int timeout, int with_modem)
+{
+	unsigned long end_time;
+	unsigned short val;
+
+	end_time = jiffies + timeout;
+	do {
+
+		/* use preliminary reads to settle the communication */
+		snd_ac97_read(ac97, AC97_RESET);
+		snd_ac97_read(ac97, AC97_VENDOR_ID1);
+		snd_ac97_read(ac97, AC97_VENDOR_ID2);
+		/* modem? */
+		if (with_modem) {
+			val = snd_ac97_read(ac97, AC97_EXTENDED_MID);
+			if (val != 0xffff && (val & 1) != 0)
+				return 0;
+		}
+		if (ac97->scaps & AC97_SCAP_DETECT_BY_VENDOR) {
+			/* probably only Xbox issue - all registers are read as zero */
+			val = snd_ac97_read(ac97, AC97_VENDOR_ID1);
+			if (val != 0 && val != 0xffff)
+				return 0;
+		} else {
+			/* because the PCM or MASTER volume registers can be modified,
+			 * the REC_GAIN register is used for tests
+			 */
+			/* test if we can write to the record gain volume register */
+			snd_ac97_write_cache(ac97, AC97_REC_GAIN, 0x8a05);
+			if ((snd_ac97_read(ac97, AC97_REC_GAIN) & 0x7fff) == 0x0a05)
+				return 0;
+		}
+		schedule_timeout_uninterruptible(1);
+	} while (time_after_eq(end_time, jiffies));
+	return -ENODEV;
+}
+
+/**
+ * snd_ac97_bus - create an AC97 bus component
+ * @card: the card instance
+ * @num: the bus number
+ * @ops: the bus callbacks table
+ * @private_data: private data pointer for the new instance
+ * @rbus: the pointer to store the new AC97 bus instance.
+ *
+ * Creates an AC97 bus component.  An struct snd_ac97_bus instance is newly
+ * allocated and initialized.
+ *
+ * The ops table must include valid callbacks (at least read and
+ * write).  The other callbacks, wait and reset, are not mandatory.
+ *
+ * The clock is set to 48000.  If another clock is needed, set
+ * (*rbus)->clock manually.
+ *
+ * The AC97 bus instance is registered as a low-level device, so you don't
+ * have to release it manually.
+ *
+ * Returns zero if successful, or a negative error code on failure.
+ */
+int snd_ac97_bus(struct snd_card *card, int num, struct snd_ac97_bus_ops *ops,
+		 void *private_data, struct snd_ac97_bus **rbus)
+{
+
+    int err;
+	struct snd_ac97_bus *bus;
+	static struct snd_device_ops dev_ops = {
+		.dev_free =	snd_ac97_bus_dev_free,
+	};
+
+	snd_assert(card != NULL, return -EINVAL);
+	snd_assert(rbus != NULL, return -EINVAL);
+	bus = kzalloc(sizeof(*bus), GFP_KERNEL);
+	if (bus == NULL)
+		return -ENOMEM;
+	bus->card = card;
+	bus->num = num;
+	bus->ops = ops;
+	bus->private_data = private_data;
+	bus->clock = 48000;
+	spin_lock_init(&bus->bus_lock);
+	snd_ac97_bus_proc_init(bus);
+	if ((err = snd_device_new(card, SNDRV_DEV_BUS, bus, &dev_ops)) < 0) {
+		snd_ac97_bus_free(bus);
+		return err;
+	}
+	*rbus = bus;
+	return 0;
+}
+
+/* stop no dev release warning */
+static void ac97_device_release(struct device * dev)
+{
+}
+
+/* register ac97 codec to bus */
+static int snd_ac97_dev_register(struct snd_device *device)
+{
+#if 0
+    struct snd_ac97 *ac97 = device->device_data;
+	int err;
+
+	ac97->dev.bus = &ac97_bus_type;
+	ac97->dev.parent = ac97->bus->card->dev;
+	ac97->dev.release = ac97_device_release;
+	snprintf(ac97->dev.bus_id, BUS_ID_SIZE, "%d-%d:%s",
+		 ac97->bus->card->number, ac97->num,
+		 snd_ac97_get_short_name(ac97));
+	if ((err = device_register(&ac97->dev)) < 0) {
+		snd_printk(KERN_ERR "Can't register ac97 bus\n");
+		ac97->dev.bus = NULL;
+		return err;
+        }
+#endif
+	return 0;
+}
+
+/* unregister ac97 codec */
+static int snd_ac97_dev_unregister(struct snd_device *device)
+{
+#if 0
+	struct snd_ac97 *ac97 = device->device_data;
+	if (ac97->dev.bus)
+		device_unregister(&ac97->dev);
+        return snd_ac97_free(ac97);
+#else
+        return 0;
+#endif
+}
+
+/* build_ops to do nothing */
+static struct snd_ac97_build_ops null_build_ops;
+
+#ifdef CONFIG_SND_AC97_POWER_SAVE
+static void do_update_power(void *data)
+{
+    update_power_regs(data);
+}
+#endif
+
+/**
+ * snd_ac97_mixer - create an Codec97 component
+ * @bus: the AC97 bus which codec is attached to
+ * @template: the template of ac97, including index, callbacks and
+ *         the private data.
+ * @rac97: the pointer to store the new ac97 instance.
+ *
+ * Creates an Codec97 component.  An struct snd_ac97 instance is newly
+ * allocated and initialized from the template.  The codec
+ * is then initialized by the standard procedure.
+ *
+ * The template must include the codec number (num) and address (addr),
+ * and the private data (private_data).
+ *
+ * The ac97 instance is registered as a low-level device, so you don't
+ * have to release it manually.
+ *
+ * Returns zero if successful, or a negative error code on failure.
+ */
+int snd_ac97_mixer(struct snd_ac97_bus *bus, struct snd_ac97_template *template, struct snd_ac97 **rac97)
+{
+	int err;
+	struct snd_ac97 *ac97;
+	struct snd_card *card;
+	char name[64];
+	unsigned long end_time;
+	unsigned int reg;
+	const struct ac97_codec_id *pid;
+	static struct snd_device_ops ops = {
+		.dev_free =	snd_ac97_dev_free,
+		.dev_register =	snd_ac97_dev_register,
+		.dev_unregister =	snd_ac97_dev_unregister,
+	};
+
+	snd_assert(rac97 != NULL, return -EINVAL);
+	*rac97 = NULL;
+	snd_assert(bus != NULL && template != NULL, return -EINVAL);
+	snd_assert(template->num < 4 && bus->codec[template->num] == NULL, return -EINVAL);
+
+	card = bus->card;
+	ac97 = kzalloc(sizeof(*ac97), GFP_KERNEL);
+	if (ac97 == NULL)
+		return -ENOMEM;
+	ac97->private_data = template->private_data;
+	ac97->private_free = template->private_free;
+	ac97->bus = bus;
+	ac97->pci = template->pci;
+	ac97->num = template->num;
+	ac97->addr = template->addr;
+	ac97->scaps = template->scaps;
+        ac97->res_table = template->res_table;
+	bus->codec[ac97->num] = ac97;
+	init_MUTEX(&ac97->reg_mutex);
+	init_MUTEX(&ac97->page_mutex);
+#ifdef CONFIG_SND_AC97_POWER_SAVE
+	ac97->power_workq = create_workqueue("ac97");
+	INIT_WORK(&ac97->power_work, do_update_power, ac97);
+#endif
+#ifdef CONFIG_PCI
+	if (ac97->pci) {
+		pci_read_config_word(ac97->pci, PCI_SUBSYSTEM_VENDOR_ID, &ac97->subsystem_vendor);
+		pci_read_config_word(ac97->pci, PCI_SUBSYSTEM_ID, &ac97->subsystem_device);
+	}
+#endif
+	if (bus->ops->reset) {
+		bus->ops->reset(ac97);
+		goto __access_ok;
+	}
+
+	ac97->id = snd_ac97_read(ac97, AC97_VENDOR_ID1) << 16;
+	ac97->id |= snd_ac97_read(ac97, AC97_VENDOR_ID2);
+	if (ac97->id && ac97->id != (unsigned int)-1) {
+		pid = look_for_codec_id(snd_ac97_codec_ids, ac97->id);
+		if (pid && (pid->flags & AC97_DEFAULT_POWER_OFF))
+			goto __access_ok;
+	}
+
+	/* reset to defaults */
+	if (!(ac97->scaps & AC97_SCAP_SKIP_AUDIO))
+		snd_ac97_write(ac97, AC97_RESET, 0);
+	if (!(ac97->scaps & AC97_SCAP_SKIP_MODEM))
+		snd_ac97_write(ac97, AC97_EXTENDED_MID, 0);
+	if (bus->ops->wait)
+		bus->ops->wait(ac97);
+	else {
+		udelay(50);
+		if (ac97->scaps & AC97_SCAP_SKIP_AUDIO)
+			err = ac97_reset_wait(ac97, HZ/2, 1);
+		else {
+			err = ac97_reset_wait(ac97, HZ/2, 0);
+			if (err < 0)
+				err = ac97_reset_wait(ac97, HZ/2, 1);
+		}
+		if (err < 0) {
+			snd_printk(KERN_WARNING "AC'97 %d does not respond - RESET\n", ac97->num);
+			/* proceed anyway - it's often non-critical */
+		}
+	}
+      __access_ok:
+	ac97->id = snd_ac97_read(ac97, AC97_VENDOR_ID1) << 16;
+	ac97->id |= snd_ac97_read(ac97, AC97_VENDOR_ID2);
+	if (! (ac97->scaps & AC97_SCAP_DETECT_BY_VENDOR) &&
+	    (ac97->id == 0x00000000 || ac97->id == 0xffffffff)) {
+		snd_printk(KERN_ERR "AC'97 %d access is not valid [0x%x], removing mixer.\n", ac97->num, ac97->id);
+		snd_ac97_free(ac97);
+		return -EIO;
+	}
+	pid = look_for_codec_id(snd_ac97_codec_ids, ac97->id);
+	if (pid)
+		ac97->flags |= pid->flags;
+
+	/* test for AC'97 */
+	if (!(ac97->scaps & AC97_SCAP_SKIP_AUDIO) && !(ac97->scaps & AC97_SCAP_AUDIO)) {
+		/* test if we can write to the record gain volume register */
+		snd_ac97_write_cache(ac97, AC97_REC_GAIN, 0x8a06);
+		if (((err = snd_ac97_read(ac97, AC97_REC_GAIN)) & 0x7fff) == 0x0a06)
+			ac97->scaps |= AC97_SCAP_AUDIO;
+	}
+	if (ac97->scaps & AC97_SCAP_AUDIO) {
+		ac97->caps = snd_ac97_read(ac97, AC97_RESET);
+		ac97->ext_id = snd_ac97_read(ac97, AC97_EXTENDED_ID);
+		if (ac97->ext_id == 0xffff)	/* invalid combination */
+			ac97->ext_id = 0;
+	}
+
+	/* test for MC'97 */
+	if (!(ac97->scaps & AC97_SCAP_SKIP_MODEM) && !(ac97->scaps & AC97_SCAP_MODEM)) {
+		ac97->ext_mid = snd_ac97_read(ac97, AC97_EXTENDED_MID);
+		if (ac97->ext_mid == 0xffff)	/* invalid combination */
+			ac97->ext_mid = 0;
+		if (ac97->ext_mid & 1)
+			ac97->scaps |= AC97_SCAP_MODEM;
+	}
+
+	if (!ac97_is_audio(ac97) && !ac97_is_modem(ac97)) {
+		if (!(ac97->scaps & (AC97_SCAP_SKIP_AUDIO|AC97_SCAP_SKIP_MODEM)))
+			snd_printk(KERN_ERR "AC'97 %d access error (not audio or modem codec)\n", ac97->num);
+		snd_ac97_free(ac97);
+		return -EACCES;
+	}
+
+	if (bus->ops->reset) // FIXME: always skipping?
+		goto __ready_ok;
+
+	/* FIXME: add powerdown control */
+	if (ac97_is_audio(ac97)) {
+		/* nothing should be in powerdown mode */
+		snd_ac97_write_cache(ac97, AC97_POWERDOWN, 0);
+		if (! (ac97->flags & AC97_DEFAULT_POWER_OFF)) {
+			snd_ac97_write_cache(ac97, AC97_RESET, 0); /* reset to defaults */
+			udelay(100);
+			snd_ac97_write_cache(ac97, AC97_POWERDOWN, 0);
+		}
+		/* nothing should be in powerdown mode */
+		snd_ac97_write_cache(ac97, AC97_GENERAL_PURPOSE, 0);
+		end_time = jiffies + (HZ / 10);
+		do {
+			if ((snd_ac97_read(ac97, AC97_POWERDOWN) & 0x0f) == 0x0f)
+				goto __ready_ok;
+			schedule_timeout_uninterruptible(1);
+		} while (time_after_eq(end_time, jiffies));
+		snd_printk(KERN_WARNING "AC'97 %d analog subsections not ready\n", ac97->num);
+	}
+
+	/* FIXME: add powerdown control */
+	if (ac97_is_modem(ac97)) {
+		unsigned char tmp;
+
+		/* nothing should be in powerdown mode */
+		/* note: it's important to set the rate at first */
+		tmp = AC97_MEA_GPIO;
+		if (ac97->ext_mid & AC97_MEI_LINE1) {
+			snd_ac97_write_cache(ac97, AC97_LINE1_RATE, 8000);
+			tmp |= AC97_MEA_ADC1 | AC97_MEA_DAC1;
+		}
+		if (ac97->ext_mid & AC97_MEI_LINE2) {
+			snd_ac97_write_cache(ac97, AC97_LINE2_RATE, 8000);
+			tmp |= AC97_MEA_ADC2 | AC97_MEA_DAC2;
+		}
+		if (ac97->ext_mid & AC97_MEI_HANDSET) {
+			snd_ac97_write_cache(ac97, AC97_HANDSET_RATE, 8000);
+			tmp |= AC97_MEA_HADC | AC97_MEA_HDAC;
+		}
+		snd_ac97_write_cache(ac97, AC97_EXTENDED_MSTATUS, 0);
+		udelay(100);
+		/* nothing should be in powerdown mode */
+		snd_ac97_write_cache(ac97, AC97_EXTENDED_MSTATUS, 0);
+		end_time = jiffies + (HZ / 10);
+		do {
+			if ((snd_ac97_read(ac97, AC97_EXTENDED_MSTATUS) & tmp) == tmp)
+				goto __ready_ok;
+			schedule_timeout_uninterruptible(1);
+		} while (time_after_eq(end_time, jiffies));
+		snd_printk(KERN_WARNING "MC'97 %d converters and GPIO not ready (0x%x)\n", ac97->num, snd_ac97_read(ac97, AC97_EXTENDED_MSTATUS));
+	}
+
+      __ready_ok:
+	if (ac97_is_audio(ac97))
+		ac97->addr = (ac97->ext_id & AC97_EI_ADDR_MASK) >> AC97_EI_ADDR_SHIFT;
+	else
+		ac97->addr = (ac97->ext_mid & AC97_MEI_ADDR_MASK) >> AC97_MEI_ADDR_SHIFT;
+	if (ac97->ext_id & 0x01c9) {	/* L/R, MIC, SDAC, LDAC VRA support */
+		reg = snd_ac97_read(ac97, AC97_EXTENDED_STATUS);
+		reg |= ac97->ext_id & 0x01c0; /* LDAC/SDAC/CDAC */
+		if (! bus->no_vra)
+			reg |= ac97->ext_id & 0x0009; /* VRA/VRM */
+		snd_ac97_write_cache(ac97, AC97_EXTENDED_STATUS, reg);
+	}
+	if ((ac97->ext_id & AC97_EI_DRA) && bus->dra) {
+		/* Intel controllers require double rate data to be put in
+		 * slots 7+8, so let's hope the codec supports it. */
+		snd_ac97_update_bits(ac97, AC97_GENERAL_PURPOSE, AC97_GP_DRSS_MASK, AC97_GP_DRSS_78);
+		if ((snd_ac97_read(ac97, AC97_GENERAL_PURPOSE) & AC97_GP_DRSS_MASK) == AC97_GP_DRSS_78)
+			ac97->flags |= AC97_DOUBLE_RATE;
+		/* restore to slots 10/11 to avoid the confliction with surrounds */
+		snd_ac97_update_bits(ac97, AC97_GENERAL_PURPOSE, AC97_GP_DRSS_MASK, 0);
+	}
+	if (ac97->ext_id & AC97_EI_VRA) {	/* VRA support */
+		snd_ac97_determine_rates(ac97, AC97_PCM_FRONT_DAC_RATE, 0, &ac97->rates[AC97_RATES_FRONT_DAC]);
+		snd_ac97_determine_rates(ac97, AC97_PCM_LR_ADC_RATE, 0, &ac97->rates[AC97_RATES_ADC]);
+	} else {
+		ac97->rates[AC97_RATES_FRONT_DAC] = SNDRV_PCM_RATE_48000;
+		if (ac97->flags & AC97_DOUBLE_RATE)
+			ac97->rates[AC97_RATES_FRONT_DAC] |= SNDRV_PCM_RATE_96000;
+		ac97->rates[AC97_RATES_ADC] = SNDRV_PCM_RATE_48000;
+	}
+	if (ac97->ext_id & AC97_EI_SPDIF) {
+		/* codec specific code (patch) should override these values */
+		ac97->rates[AC97_RATES_SPDIF] = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_32000;
+	}
+	if (ac97->ext_id & AC97_EI_VRM) {	/* MIC VRA support */
+		snd_ac97_determine_rates(ac97, AC97_PCM_MIC_ADC_RATE, 0, &ac97->rates[AC97_RATES_MIC_ADC]);
+	} else {
+		ac97->rates[AC97_RATES_MIC_ADC] = SNDRV_PCM_RATE_48000;
+	}
+	if (ac97->ext_id & AC97_EI_SDAC) {	/* SDAC support */
+		snd_ac97_determine_rates(ac97, AC97_PCM_SURR_DAC_RATE, AC97_PCM_FRONT_DAC_RATE, &ac97->rates[AC97_RATES_SURR_DAC]);
+		ac97->scaps |= AC97_SCAP_SURROUND_DAC;
+	}
+	if (ac97->ext_id & AC97_EI_LDAC) {	/* LDAC support */
+		snd_ac97_determine_rates(ac97, AC97_PCM_LFE_DAC_RATE, AC97_PCM_FRONT_DAC_RATE, &ac97->rates[AC97_RATES_LFE_DAC]);
+		ac97->scaps |= AC97_SCAP_CENTER_LFE_DAC;
+	}
+	/* additional initializations */
+	if (bus->ops->init)
+		bus->ops->init(ac97);
+	snd_ac97_get_name(ac97, ac97->id, name, !ac97_is_audio(ac97));
+	snd_ac97_get_name(NULL, ac97->id, name, !ac97_is_audio(ac97));  // ac97->id might be changed in the special setup code
+	if (! ac97->build_ops)
+		ac97->build_ops = &null_build_ops;
+
+	if (ac97_is_audio(ac97)) {
+		char comp[16];
+		if (card->mixername[0] == '\0') {
+			strcpy(card->mixername, name);
+		} else {
+			if (strlen(card->mixername) + 1 + strlen(name) + 1 <= sizeof(card->mixername)) {
+				strcat(card->mixername, ",");
+				strcat(card->mixername, name);
+			}
+		}
+		sprintf(comp, "AC97a:%08x", ac97->id);
+		if ((err = snd_component_add(card, comp)) < 0) {
+			snd_ac97_free(ac97);
+			return err;
+		}
+		if (snd_ac97_mixer_build(ac97) < 0) {
+			snd_ac97_free(ac97);
+			return -ENOMEM;
+		}
+	}
+	if (ac97_is_modem(ac97)) {
+		char comp[16];
+		if (card->mixername[0] == '\0') {
+			strcpy(card->mixername, name);
+		} else {
+			if (strlen(card->mixername) + 1 + strlen(name) + 1 <= sizeof(card->mixername)) {
+				strcat(card->mixername, ",");
+				strcat(card->mixername, name);
+			}
+		}
+		sprintf(comp, "AC97m:%08x", ac97->id);
+		if ((err = snd_component_add(card, comp)) < 0) {
+			snd_ac97_free(ac97);
+			return err;
+		}
+		if (snd_ac97_modem_build(card, ac97) < 0) {
+			snd_ac97_free(ac97);
+			return -ENOMEM;
+		}
+	}
+        /* make sure the proper powerdown bits are cleared */
+        if (ac97_is_audio(ac97))
+            update_power_regs(ac97);
+	snd_ac97_proc_init(ac97);
+	if ((err = snd_device_new(card, SNDRV_DEV_CODEC, ac97, &ops)) < 0) {
+		snd_ac97_free(ac97);
+		return err;
+	}
+	*rac97 = ac97;
+	return 0;
+}
+
+
+/*
+ * Power down the chip.
+ *
+ * MASTER and HEADPHONE registers are muted but the register cache values
+ * are not changed, so that the values can be restored in snd_ac97_resume().
+ */
+static void snd_ac97_powerdown(struct snd_ac97 *ac97)
+{
+	unsigned short power;
+
+	if (ac97_is_audio(ac97)) {
+		/* some codecs have stereo mute bits */
+		snd_ac97_write(ac97, AC97_MASTER, 0x9f9f);
+		snd_ac97_write(ac97, AC97_HEADPHONE, 0x9f9f);
+	}
+
+/* surround, CLFE, mic powerdown */
+	power = ac97->regs[AC97_EXTENDED_STATUS];
+	if (ac97->scaps & AC97_SCAP_SURROUND_DAC)
+		power |= AC97_EA_PRJ;
+	if (ac97->scaps & AC97_SCAP_CENTER_LFE_DAC)
+		power |= AC97_EA_PRI | AC97_EA_PRK;
+	power |= AC97_EA_PRL;
+	snd_ac97_write(ac97, AC97_EXTENDED_STATUS, power);
+
+	/* powerdown external amplifier */
+	if (ac97->scaps & AC97_SCAP_INV_EAPD)
+		power = ac97->regs[AC97_POWERDOWN] & ~AC97_PD_EAPD;
+	else if (! (ac97->scaps & AC97_SCAP_EAPD_LED))
+		power = ac97->regs[AC97_POWERDOWN] | AC97_PD_EAPD;
+	power |= AC97_PD_PR6;	/* Headphone amplifier powerdown */
+	power |= AC97_PD_PR0 | AC97_PD_PR1;	/* ADC & DAC powerdown */
+	snd_ac97_write(ac97, AC97_POWERDOWN, power);
+	udelay(100);
+        power |= AC97_PD_PR2 | AC97_PD_PR3;	/* Analog Mixer powerdown */
+        snd_ac97_write(ac97, AC97_POWERDOWN, power);
+        if (ac97_is_power_save_mode(ac97)) {
+            udelay(100);
+            /* AC-link powerdown, internal Clk disable */
+            /* FIXME: this may cause click noises on some boards */
+            power |= AC97_PD_PR4 | AC97_PD_PR5;
+            snd_ac97_write(ac97, AC97_POWERDOWN, power);
+	}
+}
+
+
+struct ac97_power_reg {
+    unsigned short reg;
+    unsigned short power_reg;
+    unsigned short mask;
+};
+
+enum { PWIDX_ADC, PWIDX_FRONT, PWIDX_CLFE, PWIDX_SURR, PWIDX_MIC, PWIDX_SIZE };
+
+static struct ac97_power_reg power_regs[PWIDX_SIZE] = {
+    [PWIDX_ADC] = { AC97_PCM_LR_ADC_RATE, AC97_POWERDOWN, AC97_PD_PR0},
+    [PWIDX_FRONT] = { AC97_PCM_FRONT_DAC_RATE, AC97_POWERDOWN, AC97_PD_PR1},
+    [PWIDX_CLFE] = { AC97_PCM_LFE_DAC_RATE, AC97_EXTENDED_STATUS,
+    AC97_EA_PRI | AC97_EA_PRK},
+    [PWIDX_SURR] = { AC97_PCM_SURR_DAC_RATE, AC97_EXTENDED_STATUS,
+    AC97_EA_PRJ},
+    [PWIDX_MIC] = { AC97_PCM_MIC_ADC_RATE, AC97_EXTENDED_STATUS,
+    AC97_EA_PRL},
+};
+
+#ifdef CONFIG_SND_AC97_POWER_SAVE
+/**
+ * snd_ac97_update_power - update the powerdown register
+ * @ac97: the codec instance
+ * @reg: the rate register, e.g. AC97_PCM_FRONT_DAC_RATE
+ * @powerup: non-zero when power up the part
+ *
+ * Update the AC97 powerdown register bits of the given part.
+ */
+int snd_ac97_update_power(struct snd_ac97 *ac97, int reg, int powerup)
+{
+    int i;
+
+    if (! ac97)
+        return 0;
+
+    if (reg) {
+        /* SPDIF requires DAC power, too */
+        if (reg == AC97_SPDIF)
+            reg = AC97_PCM_FRONT_DAC_RATE;
+        for (i = 0; i < PWIDX_SIZE; i++) {
+            if (power_regs[i].reg == reg) {
+                if (powerup)
+                    ac97->power_up |= (1 << i);
+                else
+                    ac97->power_up &= ~(1 << i);
+                break;
+            }
+        }
+    }
+
+    if (ac97_is_power_save_mode(ac97) && !powerup)
+        /* adjust power-down bits after two seconds delay
+         * (for avoiding loud click noises for many (OSS) apps
+         *  that open/close frequently)
+         */
+        queue_delayed_work(ac97->power_workq, &ac97->power_work, HZ*2);
+    else
+        update_power_regs(ac97);
+
+    return 0;
+}
+
+EXPORT_SYMBOL(snd_ac97_update_power);
+#endif /* CONFIG_SND_AC97_POWER_SAVE */
+
+static void update_power_regs(struct snd_ac97 *ac97)
+{
+    unsigned int power_up, bits;
+    int i;
+
+        power_up = (1 << PWIDX_FRONT) | (1 << PWIDX_ADC);
+        power_up |= (1 << PWIDX_MIC);
+        if (ac97->scaps & AC97_SCAP_SURROUND_DAC)
+            power_up |= (1 << PWIDX_SURR);
+        if (ac97->scaps & AC97_SCAP_CENTER_LFE_DAC)
+            power_up |= (1 << PWIDX_CLFE);
+#ifdef CONFIG_SND_AC97_POWER_SAVE
+	if (ac97_is_power_save_mode(ac97))
+		power_up = ac97->power_up;
+#endif
+    if (power_up) {
+        if (ac97->regs[AC97_POWERDOWN] & AC97_PD_PR2) {
+            /* needs power-up analog mix and vref */
+            snd_ac97_update_bits(ac97, AC97_POWERDOWN,
+                                 AC97_PD_PR3, 0);
+            msleep(1);
+            snd_ac97_update_bits(ac97, AC97_POWERDOWN,
+                                 AC97_PD_PR2, 0);
+        }
+    }
+    for (i = 0; i < PWIDX_SIZE; i++) {
+        if (power_up & (1 << i))
+            bits = 0;
+        else
+            bits = power_regs[i].mask;
+        snd_ac97_update_bits(ac97, power_regs[i].power_reg,
+                             power_regs[i].mask, bits);
+    }
+    if (! power_up) {
+        if (! (ac97->regs[AC97_POWERDOWN] & AC97_PD_PR2)) {
+            /* power down analog mix and vref */
+            snd_ac97_update_bits(ac97, AC97_POWERDOWN,
+                                 AC97_PD_PR2, AC97_PD_PR2);
+            snd_ac97_update_bits(ac97, AC97_POWERDOWN,
+                                 AC97_PD_PR3, AC97_PD_PR3);
+        }
+    }
+}
+
+#ifdef CONFIG_PM
+/**
+ * snd_ac97_suspend - General suspend function for AC97 codec
+ * @ac97: the ac97 instance
+ *
+ * Suspends the codec, power down the chip.
+ */
+void snd_ac97_suspend(struct snd_ac97 *ac97)
+{
+	if (! ac97)
+		return;
+	if (ac97->build_ops->suspend)
+		ac97->build_ops->suspend(ac97);
+	snd_ac97_powerdown(ac97);
+}
+
+/*
+ * restore ac97 status
+ */
+void snd_ac97_restore_status(struct snd_ac97 *ac97)
+{
+	int i;
+
+	for (i = 2; i < 0x7c ; i += 2) {
+		if (i == AC97_POWERDOWN || i == AC97_EXTENDED_ID)
+			continue;
+		/* restore only accessible registers
+		 * some chip (e.g. nm256) may hang up when unsupported registers
+		 * are accessed..!
+		 */
+		if (test_bit(i, ac97->reg_accessed)) {
+			snd_ac97_write(ac97, i, ac97->regs[i]);
+			snd_ac97_read(ac97, i);
+		}
+	}
+}
+
+/*
+ * restore IEC958 status
+ */
+void snd_ac97_restore_iec958(struct snd_ac97 *ac97)
+{
+	if (ac97->ext_id & AC97_EI_SPDIF) {
+		if (ac97->regs[AC97_EXTENDED_STATUS] & AC97_EA_SPDIF) {
+			/* reset spdif status */
+			snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, 0);
+			snd_ac97_write(ac97, AC97_EXTENDED_STATUS, ac97->regs[AC97_EXTENDED_STATUS]);
+			if (ac97->flags & AC97_CS_SPDIF)
+				snd_ac97_write(ac97, AC97_CSR_SPDIF, ac97->regs[AC97_CSR_SPDIF]);
+			else
+				snd_ac97_write(ac97, AC97_SPDIF, ac97->regs[AC97_SPDIF]);
+			snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, AC97_EA_SPDIF); /* turn on again */
+		}
+	}
+}
+
+/**
+ * snd_ac97_resume - General resume function for AC97 codec
+ * @ac97: the ac97 instance
+ *
+ * Do the standard resume procedure, power up and restoring the
+ * old register values.
+ */
+void snd_ac97_resume(struct snd_ac97 *ac97)
+{
+	unsigned long end_time;
+
+	if (! ac97)
+		return;
+
+	if (ac97->bus->ops->reset) {
+		ac97->bus->ops->reset(ac97);
+		goto  __reset_ready;
+	}
+
+	snd_ac97_write(ac97, AC97_POWERDOWN, 0);
+	if (! (ac97->flags & AC97_DEFAULT_POWER_OFF)) {
+		snd_ac97_write(ac97, AC97_RESET, 0);
+		udelay(100);
+		snd_ac97_write(ac97, AC97_POWERDOWN, 0);
+	}
+	snd_ac97_write(ac97, AC97_GENERAL_PURPOSE, 0);
+
+	snd_ac97_write(ac97, AC97_POWERDOWN, ac97->regs[AC97_POWERDOWN]);
+	if (ac97_is_audio(ac97)) {
+		ac97->bus->ops->write(ac97, AC97_MASTER, 0x8101);
+		end_time = jiffies + msecs_to_jiffies(100);
+		do {
+			if (snd_ac97_read(ac97, AC97_MASTER) == 0x8101)
+				break;
+			schedule_timeout_uninterruptible(1);
+		} while (time_after_eq(end_time, jiffies));
+		/* FIXME: extra delay */
+		ac97->bus->ops->write(ac97, AC97_MASTER, 0x8000);
+		if (snd_ac97_read(ac97, AC97_MASTER) != 0x8000)
+			msleep(250);
+	} else {
+		end_time = jiffies + msecs_to_jiffies(100);
+		do {
+			unsigned short val = snd_ac97_read(ac97, AC97_EXTENDED_MID);
+			if (val != 0xffff && (val & 1) != 0)
+				break;
+			schedule_timeout_uninterruptible(1);
+		} while (time_after_eq(end_time, jiffies));
+	}
+__reset_ready:
+
+	if (ac97->bus->ops->init)
+		ac97->bus->ops->init(ac97);
+
+	if (ac97->build_ops->resume)
+		ac97->build_ops->resume(ac97);
+	else {
+		snd_ac97_restore_status(ac97);
+		snd_ac97_restore_iec958(ac97);
+	}
+}
+#endif
+
+
+/*
+ * Hardware tuning
+ */
+static void set_ctl_name(char *dst, const char *src, const char *suffix)
+{
+	if (suffix)
+		sprintf(dst, "%s %s", src, suffix);
+	else
+		strcpy(dst, src);
+}
+
+/* remove the control with the given name and optional suffix */
+int snd_ac97_remove_ctl(struct snd_ac97 *ac97, const char *name, const char *suffix)
+{
+	struct snd_ctl_elem_id id;
+	memset(&id, 0, sizeof(id));
+	set_ctl_name(id.name, name, suffix);
+	id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+	return snd_ctl_remove_id(ac97->bus->card, &id);
+}
+
+static struct snd_kcontrol *ctl_find(struct snd_ac97 *ac97, const char *name, const char *suffix)
+{
+	struct snd_ctl_elem_id sid;
+	memset(&sid, 0, sizeof(sid));
+	set_ctl_name(sid.name, name, suffix);
+	sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+	return snd_ctl_find_id(ac97->bus->card, &sid);
+}
+
+/* rename the control with the given name and optional suffix */
+int snd_ac97_rename_ctl(struct snd_ac97 *ac97, const char *src, const char *dst, const char *suffix)
+{
+	struct snd_kcontrol *kctl = ctl_find(ac97, src, suffix);
+	if (kctl) {
+		set_ctl_name(kctl->id.name, dst, suffix);
+		return 0;
+	}
+	return -ENOENT;
+}
+
+/* rename both Volume and Switch controls - don't check the return value */
+void snd_ac97_rename_vol_ctl(struct snd_ac97 *ac97, const char *src, const char *dst)
+{
+	snd_ac97_rename_ctl(ac97, src, dst, "Switch");
+	snd_ac97_rename_ctl(ac97, src, dst, "Volume");
+}
+
+/* swap controls */
+int snd_ac97_swap_ctl(struct snd_ac97 *ac97, const char *s1, const char *s2, const char *suffix)
+{
+	struct snd_kcontrol *kctl1, *kctl2;
+	kctl1 = ctl_find(ac97, s1, suffix);
+	kctl2 = ctl_find(ac97, s2, suffix);
+	if (kctl1 && kctl2) {
+		set_ctl_name(kctl1->id.name, s2, suffix);
+		set_ctl_name(kctl2->id.name, s1, suffix);
+		return 0;
+	}
+	return -ENOENT;
+}
+
+#if 1
+/* bind hp and master controls instead of using only hp control */
+static int bind_hp_volsw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	int err = snd_ac97_put_volsw(kcontrol, ucontrol);
+	if (err > 0) {
+		unsigned long priv_saved = kcontrol->private_value;
+		kcontrol->private_value = (kcontrol->private_value & ~0xff) | AC97_HEADPHONE;
+		snd_ac97_put_volsw(kcontrol, ucontrol);
+		kcontrol->private_value = priv_saved;
+	}
+	return err;
+}
+
+/* ac97 tune: bind Master and Headphone controls */
+static int tune_hp_only(struct snd_ac97 *ac97)
+{
+	struct snd_kcontrol *msw = ctl_find(ac97, "Master Playback Switch", NULL);
+	struct snd_kcontrol *mvol = ctl_find(ac97, "Master Playback Volume", NULL);
+	if (! msw || ! mvol)
+		return -ENOENT;
+	msw->put = bind_hp_volsw_put;
+	mvol->put = bind_hp_volsw_put;
+	snd_ac97_remove_ctl(ac97, "Headphone Playback", "Switch");
+	snd_ac97_remove_ctl(ac97, "Headphone Playback", "Volume");
+	return 0;
+}
+
+#else
+/* ac97 tune: use Headphone control as master */
+static int tune_hp_only(struct snd_ac97 *ac97)
+{
+	if (ctl_find(ac97, "Headphone Playback Switch", NULL) == NULL)
+		return -ENOENT;
+	snd_ac97_remove_ctl(ac97, "Master Playback", "Switch");
+	snd_ac97_remove_ctl(ac97, "Master Playback", "Volume");
+	snd_ac97_rename_vol_ctl(ac97, "Headphone Playback", "Master Playback");
+	return 0;
+}
+#endif
+
+/* ac97 tune: swap Headphone and Master controls */
+static int tune_swap_hp(struct snd_ac97 *ac97)
+{
+	if (ctl_find(ac97, "Headphone Playback Switch", NULL) == NULL)
+		return -ENOENT;
+	snd_ac97_rename_vol_ctl(ac97, "Master Playback", "Line-Out Playback");
+	snd_ac97_rename_vol_ctl(ac97, "Headphone Playback", "Master Playback");
+	return 0;
+}
+
+/* ac97 tune: swap Surround and Master controls */
+static int tune_swap_surround(struct snd_ac97 *ac97)
+{
+	if (snd_ac97_swap_ctl(ac97, "Master Playback", "Surround Playback", "Switch") ||
+	    snd_ac97_swap_ctl(ac97, "Master Playback", "Surround Playback", "Volume"))
+		return -ENOENT;
+	return 0;
+}
+
+/* ac97 tune: set up mic sharing for AD codecs */
+static int tune_ad_sharing(struct snd_ac97 *ac97)
+{
+	unsigned short scfg;
+	if ((ac97->id & 0xffffff00) != 0x41445300) {
+		snd_printk(KERN_ERR "ac97_quirk AD_SHARING is only for AD codecs\n");
+		return -EINVAL;
+	}
+	/* Turn on OMS bit to route microphone to back panel */
+	scfg = snd_ac97_read(ac97, AC97_AD_SERIAL_CFG);
+	snd_ac97_write_cache(ac97, AC97_AD_SERIAL_CFG, scfg | 0x0200);
+	return 0;
+}
+
+static const struct snd_kcontrol_new snd_ac97_alc_jack_detect =
+AC97_SINGLE("Jack Detect", AC97_ALC650_CLOCK, 5, 1, 0);
+
+/* ac97 tune: set up ALC jack-select */
+static int tune_alc_jack(struct snd_ac97 *ac97)
+{
+	if ((ac97->id & 0xffffff00) != 0x414c4700) {
+		snd_printk(KERN_ERR "ac97_quirk ALC_JACK is only for Realtek codecs\n");
+		return -EINVAL;
+	}
+	snd_ac97_update_bits(ac97, 0x7a, 0x20, 0x20); /* select jack detect function */
+	snd_ac97_update_bits(ac97, 0x7a, 0x01, 0x01); /* Line-out auto mute */
+	if (ac97->id == AC97_ID_ALC658D)
+		snd_ac97_update_bits(ac97, 0x74, 0x0800, 0x0800);
+	return snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&snd_ac97_alc_jack_detect, ac97));
+}
+
+/* ac97 tune: inversed EAPD bit */
+static int tune_inv_eapd(struct snd_ac97 *ac97)
+{
+	struct snd_kcontrol *kctl = ctl_find(ac97, "External Amplifier", NULL);
+	if (! kctl)
+		return -ENOENT;
+	set_inv_eapd(ac97, kctl);
+	return 0;
+}
+
+static int master_mute_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	int err = snd_ac97_put_volsw(kcontrol, ucontrol);
+	if (err > 0) {
+		struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+		int shift = (kcontrol->private_value >> 8) & 0x0f;
+		int rshift = (kcontrol->private_value >> 12) & 0x0f;
+		unsigned short mask;
+		if (shift != rshift)
+			mask = 0x8080;
+		else
+			mask = 0x8000;
+		snd_ac97_update_bits(ac97, AC97_POWERDOWN, 0x8000,
+				     (ac97->regs[AC97_MASTER] & mask) == mask ?
+				     0x8000 : 0);
+	}
+	return err;
+}
+
+/* ac97 tune: EAPD controls mute LED bound with the master mute */
+static int tune_mute_led(struct snd_ac97 *ac97)
+{
+	struct snd_kcontrol *msw = ctl_find(ac97, "Master Playback Switch", NULL);
+	if (! msw)
+		return -ENOENT;
+	msw->put = master_mute_sw_put;
+	snd_ac97_remove_ctl(ac97, "External Amplifier", NULL);
+        snd_ac97_update_bits(ac97, AC97_POWERDOWN, 0x8000, 0x8000); /* mute LED on */
+        ac97->scaps |= AC97_SCAP_EAPD_LED;
+	return 0;
+}
+
+static int hp_master_mute_sw_put(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol)
+{
+	int err = bind_hp_volsw_put(kcontrol, ucontrol);
+	if (err > 0) {
+		struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+		int shift = (kcontrol->private_value >> 8) & 0x0f;
+		int rshift = (kcontrol->private_value >> 12) & 0x0f;
+		unsigned short mask;
+		if (shift != rshift)
+			mask = 0x8080;
+		else
+			mask = 0x8000;
+		snd_ac97_update_bits(ac97, AC97_POWERDOWN, 0x8000,
+				     (ac97->regs[AC97_MASTER] & mask) == mask ?
+				     0x8000 : 0);
+	}
+	return err;
+}
+
+static int tune_hp_mute_led(struct snd_ac97 *ac97)
+{
+	struct snd_kcontrol *msw = ctl_find(ac97, "Master Playback Switch", NULL);
+	struct snd_kcontrol *mvol = ctl_find(ac97, "Master Playback Volume", NULL);
+	if (! msw || ! mvol)
+		return -ENOENT;
+	msw->put = hp_master_mute_sw_put;
+	mvol->put = bind_hp_volsw_put;
+	snd_ac97_remove_ctl(ac97, "External Amplifier", NULL);
+	snd_ac97_remove_ctl(ac97, "Headphone Playback", "Switch");
+	snd_ac97_remove_ctl(ac97, "Headphone Playback", "Volume");
+	snd_ac97_update_bits(ac97, AC97_POWERDOWN, 0x8000, 0x8000); /* mute LED on */
+	return 0;
+}
+
+struct quirk_table {
+	const char *name;
+	int (*func)(struct snd_ac97 *);
+};
+
+static struct quirk_table applicable_quirks[] = {
+	{ "none", NULL },
+	{ "hp_only", tune_hp_only },
+	{ "swap_hp", tune_swap_hp },
+	{ "swap_surround", tune_swap_surround },
+	{ "ad_sharing", tune_ad_sharing },
+	{ "alc_jack", tune_alc_jack },
+	{ "inv_eapd", tune_inv_eapd },
+	{ "mute_led", tune_mute_led },
+	{ "hp_mute_led", tune_hp_mute_led },
+};
+
+/* apply the quirk with the given type */
+static int apply_quirk(struct snd_ac97 *ac97, int type)
+{
+	if (type <= 0)
+		return 0;
+	else if (type >= ARRAY_SIZE(applicable_quirks))
+		return -EINVAL;
+	if (applicable_quirks[type].func)
+		return applicable_quirks[type].func(ac97);
+	return 0;
+}
+
+/* apply the quirk with the given name */
+static int apply_quirk_str(struct snd_ac97 *ac97, const char *typestr)
+{
+	int i;
+	struct quirk_table *q;
+
+	for (i = 0; i < ARRAY_SIZE(applicable_quirks); i++) {
+		q = &applicable_quirks[i];
+		if (q->name && ! strcmp(typestr, q->name))
+			return apply_quirk(ac97, i);
+	}
+	/* for compatibility, accept the numbers, too */
+	if (*typestr >= '0' && *typestr <= '9')
+		return apply_quirk(ac97, (int)simple_strtoul(typestr, NULL, 10));
+	return -EINVAL;
+}
+
+/**
+ * snd_ac97_tune_hardware - tune up the hardware
+ * @ac97: the ac97 instance
+ * @quirk: quirk list
+ * @override: explicit quirk value (overrides the list if non-NULL)
+ *
+ * Do some workaround for each pci device, such as renaming of the
+ * headphone (true line-out) control as "Master".
+ * The quirk-list must be terminated with a zero-filled entry.
+ *
+ * Returns zero if successful, or a negative error code on failure.
+ */
+
+int snd_ac97_tune_hardware(struct snd_ac97 *ac97, struct ac97_quirk *quirk, const char *override)
+{
+	int result;
+
+	/* quirk overriden? */
+	if (override && strcmp(override, "-1") && strcmp(override, "default")) {
+		result = apply_quirk_str(ac97, override);
+		if (result < 0)
+			snd_printk(KERN_ERR "applying quirk type %s failed (%d)\n", override, result);
+		return result;
+	}
+
+	if (! quirk)
+		return -EINVAL;
+
+	for (; quirk->subvendor; quirk++) {
+		if (quirk->subvendor != ac97->subsystem_vendor)
+			continue;
+		if ((! quirk->mask && quirk->subdevice == ac97->subsystem_device) ||
+		    quirk->subdevice == (quirk->mask & ac97->subsystem_device)) {
+			if (quirk->codec_id && quirk->codec_id != ac97->id)
+				continue;
+			snd_printdd("ac97 quirk for %s (%04x:%04x)\n", quirk->name, ac97->subsystem_vendor, ac97->subsystem_device);
+			result = apply_quirk(ac97, quirk->type);
+			if (result < 0)
+				snd_printk(KERN_ERR "applying quirk type %d for %s failed (%d)\n", quirk->type, quirk->name, result);
+			return result;
+		}
+	}
+	return 0;
+}
+
+
+/*
+ *  Exported symbols
+ */
+
+EXPORT_SYMBOL(snd_ac97_write);
+EXPORT_SYMBOL(snd_ac97_read);
+EXPORT_SYMBOL(snd_ac97_write_cache);
+EXPORT_SYMBOL(snd_ac97_update);
+EXPORT_SYMBOL(snd_ac97_update_bits);
+EXPORT_SYMBOL(snd_ac97_get_short_name);
+EXPORT_SYMBOL(snd_ac97_bus);
+EXPORT_SYMBOL(snd_ac97_mixer);
+EXPORT_SYMBOL(snd_ac97_pcm_assign);
+EXPORT_SYMBOL(snd_ac97_pcm_open);
+EXPORT_SYMBOL(snd_ac97_pcm_close);
+EXPORT_SYMBOL(snd_ac97_pcm_double_rate_rules);
+EXPORT_SYMBOL(snd_ac97_tune_hardware);
+EXPORT_SYMBOL(snd_ac97_set_rate);
+#ifdef CONFIG_PM
+EXPORT_SYMBOL(snd_ac97_resume);
+EXPORT_SYMBOL(snd_ac97_suspend);
+#endif
+
+/*
+ *  INIT part
+ */
+
+static int __init alsa_ac97_init(void)
+{
+	return 0;
+}
+
+static void __exit alsa_ac97_exit(void)
+{
+}
+
+module_init(alsa_ac97_init)
+module_exit(alsa_ac97_exit)
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/ac97/ac97_id.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/ac97/ac97_id.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/ac97/ac97_id.h	(revision 91)
@@ -0,0 +1,63 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  Universal interface for Audio Codec '97
+ *
+ *  For more details look to AC '97 component specification revision 2.2
+ *  by Intel Corporation (http://developer.intel.com).
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#define AC97_ID_AK4540		0x414b4d00
+#define AC97_ID_AK4542		0x414b4d01
+#define AC97_ID_AD1819		0x41445303
+#define AC97_ID_AD1881		0x41445340
+#define AC97_ID_AD1881A		0x41445348
+#define AC97_ID_AD1885		0x41445360
+#define AC97_ID_AD1886		0x41445361
+#define AC97_ID_AD1887		0x41445362
+#define AC97_ID_AD1886A		0x41445363
+#define AC97_ID_AD1980 		0x41445370
+#define AC97_ID_TR28028		0x54524108
+#define AC97_ID_STAC9700	0x83847600
+#define AC97_ID_STAC9704	0x83847604
+#define AC97_ID_STAC9705	0x83847605
+#define AC97_ID_STAC9708	0x83847608
+#define AC97_ID_STAC9721	0x83847609
+#define AC97_ID_STAC9744	0x83847644
+#define AC97_ID_STAC9756	0x83847656
+#define AC97_ID_CS4297A		0x43525910
+#define AC97_ID_CS4299		0x43525930
+#define AC97_ID_CS4201		0x43525948
+#define AC97_ID_CS4205		0x43525958
+#define AC97_ID_CS_MASK		0xfffffff8	/* bit 0-2: rev */
+#define AC97_ID_ALC100		0x414c4300
+#define AC97_ID_ALC650		0x414c4720
+#define AC97_ID_ALC650D		0x414c4721
+#define AC97_ID_ALC650E		0x414c4722
+#define AC97_ID_ALC650F		0x414c4723
+#define AC97_ID_ALC655		0x414c4760
+#define AC97_ID_ALC658		0x414c4780
+#define AC97_ID_ALC658D		0x414c4781
+#define AC97_ID_ALC850		0x414c4790
+#define AC97_ID_YMF753		0x594d4803
+#define AC97_ID_VT1616		0x49434551
+#define AC97_ID_CM9738		0x434d4941
+#define AC97_ID_CM9739		0x434d4961
+#define AC97_ID_CM9761_78	0x434d4978
+#define AC97_ID_CM9761_82	0x434d4982
+#define AC97_ID_CM9761_83	0x434d4983
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/ac97/ac97_local.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/ac97/ac97_local.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/ac97/ac97_local.h	(revision 91)
@@ -0,0 +1,92 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  Universal interface for Audio Codec '97
+ *
+ *  For more details look to AC '97 component specification revision 2.2
+ *  by Intel Corporation (http://developer.intel.com).
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#define AC97_SINGLE_VALUE(reg,shift,mask,invert) ((reg) | ((shift) << 8) | ((shift) << 12) | ((mask) << 16) | ((invert) << 24))
+#define AC97_PAGE_SINGLE_VALUE(reg,shift,mask,invert,page) (AC97_SINGLE_VALUE(reg,shift,mask,invert) | (1<<25) | ((page) << 26))
+#define AC97_SINGLE(xname, reg, shift, mask, invert) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_ac97_info_volsw, \
+  .get = snd_ac97_get_volsw, .put = snd_ac97_put_volsw, \
+  .private_value =  AC97_SINGLE_VALUE(reg, shift, mask, invert) }
+#define AC97_PAGE_SINGLE(xname, reg, shift, mask, invert, page)		\
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_ac97_info_volsw, \
+  .get = snd_ac97_get_volsw, .put = snd_ac97_put_volsw, \
+  .private_value =  AC97_PAGE_SINGLE_VALUE(reg, shift, mask, invert, page) }
+#define AC97_DOUBLE(xname, reg, shift_left, shift_right, mask, invert) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), .info = snd_ac97_info_volsw, \
+  .get = snd_ac97_get_volsw, .put = snd_ac97_put_volsw, \
+  .private_value = (reg) | ((shift_left) << 8) | ((shift_right) << 12) | ((mask) << 16) | ((invert) << 24) }
+
+/* enum control */
+struct ac97_enum {
+	unsigned char reg;
+	unsigned char shift_l;
+	unsigned char shift_r;
+	unsigned short mask;
+	const char **texts;
+};
+
+#define AC97_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xmask, xtexts) \
+{ .reg = xreg, .shift_l = xshift_l, .shift_r = xshift_r, \
+  .mask = xmask, .texts = xtexts }
+#define AC97_ENUM_SINGLE(xreg, xshift, xmask, xtexts) \
+	AC97_ENUM_DOUBLE(xreg, xshift, xshift, xmask, xtexts)
+#define AC97_ENUM(xname, xenum) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_ac97_info_enum_double, \
+  .get = snd_ac97_get_enum_double, .put = snd_ac97_put_enum_double, \
+  .private_value = (unsigned long)&xenum }
+
+/* ac97_codec.c */
+extern const struct snd_kcontrol_new snd_ac97_controls_3d[];
+extern const struct snd_kcontrol_new snd_ac97_controls_spdif[];
+struct snd_kcontrol *snd_ac97_cnew(const struct snd_kcontrol_new *_template, struct snd_ac97 * ac97);
+void snd_ac97_get_name(struct snd_ac97 *ac97, unsigned int id, char *name, int modem);
+int snd_ac97_info_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo);
+int snd_ac97_get_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+int snd_ac97_put_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+int snd_ac97_try_bit(struct snd_ac97 * ac97, int reg, int bit);
+int snd_ac97_remove_ctl(struct snd_ac97 *ac97, const char *name, const char *suffix);
+int snd_ac97_rename_ctl(struct snd_ac97 *ac97, const char *src, const char *dst, const char *suffix);
+int snd_ac97_swap_ctl(struct snd_ac97 *ac97, const char *s1, const char *s2, const char *suffix);
+void snd_ac97_rename_vol_ctl(struct snd_ac97 *ac97, const char *src, const char *dst);
+void snd_ac97_restore_status(struct snd_ac97 *ac97);
+void snd_ac97_restore_iec958(struct snd_ac97 *ac97);
+int snd_ac97_info_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo);
+int snd_ac97_get_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+int snd_ac97_put_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+
+int snd_ac97_update_bits_nolock(struct snd_ac97 *ac97, unsigned short reg,
+				unsigned short mask, unsigned short value);
+
+/* ac97_proc.c */
+#ifdef CONFIG_PROC_FS
+void snd_ac97_bus_proc_init(struct snd_ac97_bus * ac97);
+void snd_ac97_bus_proc_done(struct snd_ac97_bus * ac97);
+void snd_ac97_proc_init(struct snd_ac97 * ac97);
+void snd_ac97_proc_done(struct snd_ac97 * ac97);
+#else
+#define snd_ac97_bus_proc_init(ac97_bus_t) do { } while (0)
+#define snd_ac97_bus_proc_done(ac97_bus_t) do { } while (0)
+#define snd_ac97_proc_init(ac97_t) do { } while (0)
+#define snd_ac97_proc_done(ac97_t) do { } while (0)
+#endif
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/ac97/ac97_patch.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/ac97/ac97_patch.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/ac97/ac97_patch.c	(revision 91)
@@ -0,0 +1,3436 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  Universal interface for Audio Codec '97
+ *
+ *  For more details look to AC '97 component specification revision 2.2
+ *  by Intel Corporation (http://developer.intel.com) and to datasheets
+ *  for specific codecs.
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/control.h>
+#include <sound/tlv.h>
+#include <sound/ac97_codec.h>
+#include "ac97_patch.h"
+#include "ac97_id.h"
+#include "ac97_local.h"
+
+/*
+ *  Chip specific initialization
+ */
+
+static int patch_build_controls(struct snd_ac97 * ac97, const struct snd_kcontrol_new *controls, int count)
+{
+	int idx, err;
+
+	for (idx = 0; idx < count; idx++)
+		if ((err = snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&controls[idx], ac97))) < 0)
+			return err;
+	return 0;
+}
+
+/* replace with a new TLV */
+static void reset_tlv(struct snd_ac97 *ac97, const char *name,
+		      unsigned int *tlv)
+{
+	struct snd_ctl_elem_id sid;
+	struct snd_kcontrol *kctl;
+	memset(&sid, 0, sizeof(sid));
+	strcpy(sid.name, name);
+	sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+	kctl = snd_ctl_find_id(ac97->bus->card, &sid);
+	if (kctl && kctl->tlv.p)
+		kctl->tlv.p = tlv;
+}
+
+/* set to the page, update bits and restore the page */
+static int ac97_update_bits_page(struct snd_ac97 *ac97, unsigned short reg, unsigned short mask, unsigned short value, unsigned short page)
+{
+	unsigned short page_save;
+	int ret;
+
+	down(&ac97->page_mutex);
+	page_save = snd_ac97_read(ac97, AC97_INT_PAGING) & AC97_PAGE_MASK;
+	snd_ac97_update_bits(ac97, AC97_INT_PAGING, AC97_PAGE_MASK, page);
+	ret = snd_ac97_update_bits(ac97, reg, mask, value);
+	snd_ac97_update_bits(ac97, AC97_INT_PAGING, AC97_PAGE_MASK, page_save);
+	up(&ac97->page_mutex); /* unlock paging */
+	return ret;
+}
+
+/*
+ * shared line-in/mic controls
+ */
+static int ac97_enum_text_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo,
+			       const char **texts, unsigned int nums)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = nums;
+	if (uinfo->value.enumerated.item > nums - 1)
+		uinfo->value.enumerated.item = nums - 1;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int ac97_surround_jack_mode_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+	static const char *texts[] = { "Shared", "Independent" };
+	return ac97_enum_text_info(kcontrol, uinfo, texts, 2);
+}
+
+static int ac97_surround_jack_mode_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+
+	ucontrol->value.enumerated.item[0] = ac97->indep_surround;
+	return 0;
+}
+
+static int ac97_surround_jack_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+	unsigned char indep = !!ucontrol->value.enumerated.item[0];
+
+	if (indep != ac97->indep_surround) {
+		ac97->indep_surround = indep;
+		if (ac97->build_ops->update_jacks)
+			ac97->build_ops->update_jacks(ac97);
+		return 1;
+	}
+	return 0;
+}
+
+static int ac97_channel_mode_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+	static const char *texts[] = { "2ch", "4ch", "6ch" };
+	if (kcontrol->private_value)
+		return ac97_enum_text_info(kcontrol, uinfo, texts, 2); /* 4ch only */
+	return ac97_enum_text_info(kcontrol, uinfo, texts, 3);
+}
+
+static int ac97_channel_mode_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+
+	ucontrol->value.enumerated.item[0] = ac97->channel_mode;
+	return 0;
+}
+
+static int ac97_channel_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+	unsigned char mode = ucontrol->value.enumerated.item[0];
+
+	if (mode != ac97->channel_mode) {
+		ac97->channel_mode = mode;
+		if (ac97->build_ops->update_jacks)
+			ac97->build_ops->update_jacks(ac97);
+		return 1;
+	}
+	return 0;
+}
+
+#define AC97_SURROUND_JACK_MODE_CTL \
+	{ \
+		.iface	= SNDRV_CTL_ELEM_IFACE_MIXER, \
+		.name	= "Surround Jack Mode", \
+		.info = ac97_surround_jack_mode_info, \
+		.get = ac97_surround_jack_mode_get, \
+		.put = ac97_surround_jack_mode_put, \
+	}
+#define AC97_CHANNEL_MODE_CTL \
+	{ \
+		.iface	= SNDRV_CTL_ELEM_IFACE_MIXER, \
+		.name	= "Channel Mode", \
+		.info = ac97_channel_mode_info, \
+		.get = ac97_channel_mode_get, \
+		.put = ac97_channel_mode_put, \
+	}
+#define AC97_CHANNEL_MODE_4CH_CTL \
+	{ \
+		.iface	= SNDRV_CTL_ELEM_IFACE_MIXER, \
+		.name	= "Channel Mode", \
+		.info = ac97_channel_mode_info, \
+		.get = ac97_channel_mode_get, \
+		.put = ac97_channel_mode_put, \
+		.private_value = 1, \
+	}
+
+static inline int is_surround_on(struct snd_ac97 *ac97)
+{
+	return ac97->channel_mode >= 1;
+}
+
+static inline int is_clfe_on(struct snd_ac97 *ac97)
+{
+	return ac97->channel_mode >= 2;
+}
+
+/* system has shared jacks with surround out enabled */
+static inline int is_shared_surrout(struct snd_ac97 *ac97)
+{
+	return ! ac97->indep_surround && is_surround_on(ac97);
+}
+
+/* system has shared jacks with center/lfe out enabled */
+static inline int is_shared_clfeout(struct snd_ac97 *ac97)
+{
+	return ! ac97->indep_surround && is_clfe_on(ac97);
+}
+
+/* system has shared jacks with line in enabled */
+static inline int is_shared_linein(struct snd_ac97 *ac97)
+{
+	return !ac97->indep_surround && !is_surround_on(ac97);
+}
+
+/* system has shared jacks with mic in enabled */
+static inline int is_shared_micin(struct snd_ac97 *ac97)
+{
+	return !ac97->indep_surround && !is_clfe_on(ac97);
+}
+
+/* The following snd_ac97_ymf753_... items added by David Shust (dshust@shustring.com) */
+
+/* It is possible to indicate to the Yamaha YMF753 the type of speakers being used. */
+static int snd_ac97_ymf753_info_speaker(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+	static char *texts[3] = {
+		"Standard", "Small", "Smaller"
+	};
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 3;
+	if (uinfo->value.enumerated.item > 2)
+		uinfo->value.enumerated.item = 2;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int snd_ac97_ymf753_get_speaker(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+	unsigned short val;
+
+	val = ac97->regs[AC97_YMF753_3D_MODE_SEL];
+	val = (val >> 10) & 3;
+	if (val > 0)    /* 0 = invalid */
+		val--;
+	ucontrol->value.enumerated.item[0] = val;
+	return 0;
+}
+
+static int snd_ac97_ymf753_put_speaker(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+	unsigned short val;
+
+	if (ucontrol->value.enumerated.item[0] > 2)
+		return -EINVAL;
+	val = (ucontrol->value.enumerated.item[0] + 1) << 10;
+	return snd_ac97_update(ac97, AC97_YMF753_3D_MODE_SEL, val);
+}
+
+static const struct snd_kcontrol_new snd_ac97_ymf753_controls_speaker =
+{
+	.iface  = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name   = "3D Control - Speaker",
+	.info   = snd_ac97_ymf753_info_speaker,
+	.get    = snd_ac97_ymf753_get_speaker,
+	.put    = snd_ac97_ymf753_put_speaker,
+};
+
+/* It is possible to indicate to the Yamaha YMF753 the source to direct to the S/PDIF output. */
+static int snd_ac97_ymf753_spdif_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+	static char *texts[2] = { "AC-Link", "A/D Converter" };
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 2;
+	if (uinfo->value.enumerated.item > 1)
+		uinfo->value.enumerated.item = 1;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int snd_ac97_ymf753_spdif_source_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+	unsigned short val;
+
+	val = ac97->regs[AC97_YMF753_DIT_CTRL2];
+	ucontrol->value.enumerated.item[0] = (val >> 1) & 1;
+	return 0;
+}
+
+static int snd_ac97_ymf753_spdif_source_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+	unsigned short val;
+
+	if (ucontrol->value.enumerated.item[0] > 1)
+		return -EINVAL;
+	val = ucontrol->value.enumerated.item[0] << 1;
+	return snd_ac97_update_bits(ac97, AC97_YMF753_DIT_CTRL2, 0x0002, val);
+}
+
+/* The AC'97 spec states that the S/PDIF signal is to be output at pin 48.
+   The YMF753 will output the S/PDIF signal to pin 43, 47 (EAPD), or 48.
+   By default, no output pin is selected, and the S/PDIF signal is not output.
+   There is also a bit to mute S/PDIF output in a vendor-specific register. */
+static int snd_ac97_ymf753_spdif_output_pin_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+	static char *texts[3] = { "Disabled", "Pin 43", "Pin 48" };
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 3;
+	if (uinfo->value.enumerated.item > 2)
+		uinfo->value.enumerated.item = 2;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int snd_ac97_ymf753_spdif_output_pin_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+	unsigned short val;
+
+	val = ac97->regs[AC97_YMF753_DIT_CTRL2];
+	ucontrol->value.enumerated.item[0] = (val & 0x0008) ? 2 : (val & 0x0020) ? 1 : 0;
+	return 0;
+}
+
+static int snd_ac97_ymf753_spdif_output_pin_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+	unsigned short val;
+
+	if (ucontrol->value.enumerated.item[0] > 2)
+		return -EINVAL;
+	val = (ucontrol->value.enumerated.item[0] == 2) ? 0x0008 :
+	      (ucontrol->value.enumerated.item[0] == 1) ? 0x0020 : 0;
+	return snd_ac97_update_bits(ac97, AC97_YMF753_DIT_CTRL2, 0x0028, val);
+	/* The following can be used to direct S/PDIF output to pin 47 (EAPD).
+	   snd_ac97_write_cache(ac97, 0x62, snd_ac97_read(ac97, 0x62) | 0x0008); */
+}
+
+static const struct snd_kcontrol_new snd_ac97_ymf753_controls_spdif[3] = {
+	{
+		.iface	= SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name	= SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
+		.info	= snd_ac97_ymf753_spdif_source_info,
+		.get	= snd_ac97_ymf753_spdif_source_get,
+		.put	= snd_ac97_ymf753_spdif_source_put,
+	},
+	{
+		.iface	= SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name	= SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Output Pin",
+		.info	= snd_ac97_ymf753_spdif_output_pin_info,
+		.get	= snd_ac97_ymf753_spdif_output_pin_get,
+		.put	= snd_ac97_ymf753_spdif_output_pin_put,
+	},
+	AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",NONE,NONE) "Mute", AC97_YMF753_DIT_CTRL2, 2, 1, 1)
+};
+
+static int patch_yamaha_ymf753_3d(struct snd_ac97 * ac97)
+{
+	struct snd_kcontrol *kctl;
+	int err;
+
+	if ((err = snd_ctl_add(ac97->bus->card, kctl = snd_ac97_cnew(&snd_ac97_controls_3d[0], ac97))) < 0)
+		return err;
+	strcpy(kctl->id.name, "3D Control - Wide");
+	kctl->private_value = AC97_SINGLE_VALUE(AC97_3D_CONTROL, 9, 7, 0);
+	snd_ac97_write_cache(ac97, AC97_3D_CONTROL, 0x0000);
+	if ((err = snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&snd_ac97_ymf753_controls_speaker, ac97))) < 0)
+		return err;
+	snd_ac97_write_cache(ac97, AC97_YMF753_3D_MODE_SEL, 0x0c00);
+	return 0;
+}
+
+static int patch_yamaha_ymf753_post_spdif(struct snd_ac97 * ac97)
+{
+	int err;
+
+	if ((err = patch_build_controls(ac97, snd_ac97_ymf753_controls_spdif, ARRAY_SIZE(snd_ac97_ymf753_controls_spdif))) < 0)
+		return err;
+	return 0;
+}
+
+static struct snd_ac97_build_ops patch_yamaha_ymf753_ops = {
+	.build_3d	= patch_yamaha_ymf753_3d,
+	.build_post_spdif = patch_yamaha_ymf753_post_spdif
+};
+
+int patch_yamaha_ymf753(struct snd_ac97 * ac97)
+{
+	/* Patch for Yamaha YMF753, Copyright (c) by David Shust, dshust@shustring.com.
+	   This chip has nonstandard and extended behaviour with regard to its S/PDIF output.
+	   The AC'97 spec states that the S/PDIF signal is to be output at pin 48.
+	   The YMF753 will ouput the S/PDIF signal to pin 43, 47 (EAPD), or 48.
+	   By default, no output pin is selected, and the S/PDIF signal is not output.
+	   There is also a bit to mute S/PDIF output in a vendor-specific register.
+	*/
+	ac97->build_ops = &patch_yamaha_ymf753_ops;
+	ac97->caps |= AC97_BC_BASS_TREBLE;
+	ac97->caps |= 0x04 << 10; /* Yamaha 3D enhancement */
+	return 0;
+}
+
+/*
+ * May 2, 2003 Liam Girdwood <liam.girdwood@wolfsonmicro.com>
+ *  removed broken wolfson00 patch.
+ *  added support for WM9705,WM9708,WM9709,WM9710,WM9711,WM9712 and WM9717.
+ */
+
+static const struct snd_kcontrol_new wm97xx_snd_ac97_controls[] = {
+AC97_DOUBLE("Front Playback Volume", AC97_WM97XX_FMIXER_VOL, 8, 0, 31, 1),
+AC97_SINGLE("Front Playback Switch", AC97_WM97XX_FMIXER_VOL, 15, 1, 1),
+};
+
+static int patch_wolfson_wm9703_specific(struct snd_ac97 * ac97)
+{
+	/* This is known to work for the ViewSonic ViewPad 1000
+	 * Randolph Bentson <bentson@holmsjoen.com>
+	 * WM9703/9707/9708/9717
+	 */
+	int err, i;
+
+	for (i = 0; i < ARRAY_SIZE(wm97xx_snd_ac97_controls); i++) {
+		if ((err = snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&wm97xx_snd_ac97_controls[i], ac97))) < 0)
+			return err;
+	}
+	snd_ac97_write_cache(ac97,  AC97_WM97XX_FMIXER_VOL, 0x0808);
+	return 0;
+}
+
+static struct snd_ac97_build_ops patch_wolfson_wm9703_ops = {
+	.build_specific = patch_wolfson_wm9703_specific,
+};
+
+int patch_wolfson03(struct snd_ac97 * ac97)
+{
+	ac97->build_ops = &patch_wolfson_wm9703_ops;
+	return 0;
+}
+
+static const struct snd_kcontrol_new wm9704_snd_ac97_controls[] = {
+AC97_DOUBLE("Front Playback Volume", AC97_WM97XX_FMIXER_VOL, 8, 0, 31, 1),
+AC97_SINGLE("Front Playback Switch", AC97_WM97XX_FMIXER_VOL, 15, 1, 1),
+AC97_DOUBLE("Rear Playback Volume", AC97_WM9704_RMIXER_VOL, 8, 0, 31, 1),
+AC97_SINGLE("Rear Playback Switch", AC97_WM9704_RMIXER_VOL, 15, 1, 1),
+AC97_DOUBLE("Rear DAC Volume", AC97_WM9704_RPCM_VOL, 8, 0, 31, 1),
+AC97_DOUBLE("Surround Volume", AC97_SURROUND_MASTER, 8, 0, 31, 1),
+};
+
+static int patch_wolfson_wm9704_specific(struct snd_ac97 * ac97)
+{
+	int err, i;
+	for (i = 0; i < ARRAY_SIZE(wm9704_snd_ac97_controls); i++) {
+		if ((err = snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&wm9704_snd_ac97_controls[i], ac97))) < 0)
+			return err;
+	}
+	/* patch for DVD noise */
+	snd_ac97_write_cache(ac97, AC97_WM9704_TEST, 0x0200);
+	return 0;
+}
+
+static struct snd_ac97_build_ops patch_wolfson_wm9704_ops = {
+	.build_specific = patch_wolfson_wm9704_specific,
+};
+
+int patch_wolfson04(struct snd_ac97 * ac97)
+{
+	/* WM9704M/9704Q */
+	ac97->build_ops = &patch_wolfson_wm9704_ops;
+	return 0;
+}
+
+static int patch_wolfson_wm9705_specific(struct snd_ac97 * ac97)
+{
+	int err, i;
+	for (i = 0; i < ARRAY_SIZE(wm97xx_snd_ac97_controls); i++) {
+		if ((err = snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&wm97xx_snd_ac97_controls[i], ac97))) < 0)
+			return err;
+	}
+	snd_ac97_write_cache(ac97,  0x72, 0x0808);
+	return 0;
+}
+
+static struct snd_ac97_build_ops patch_wolfson_wm9705_ops = {
+	.build_specific = patch_wolfson_wm9705_specific,
+};
+
+int patch_wolfson05(struct snd_ac97 * ac97)
+{
+    /* WM9705, WM9710 */
+    ac97->build_ops = &patch_wolfson_wm9705_ops;
+#ifdef CONFIG_TOUCHSCREEN_WM9705
+    /* WM9705 touchscreen uses AUX and VIDEO for touch */
+    ac97->flags |= AC97_HAS_NO_VIDEO | AC97_HAS_NO_AUX;
+#endif
+    return 0;
+}
+
+static const char* wm9711_alc_select[] = {"None", "Left", "Right", "Stereo"};
+static const char* wm9711_alc_mix[] = {"Stereo", "Right", "Left", "None"};
+static const char* wm9711_out3_src[] = {"Left", "VREF", "Left + Right", "Mono"};
+static const char* wm9711_out3_lrsrc[] = {"Master Mix", "Headphone Mix"};
+static const char* wm9711_rec_adc[] = {"Stereo", "Left", "Right", "Mute"};
+static const char* wm9711_base[] = {"Linear Control", "Adaptive Boost"};
+static const char* wm9711_rec_gain[] = {"+1.5dB Steps", "+0.75dB Steps"};
+static const char* wm9711_mic[] = {"Mic 1", "Differential", "Mic 2", "Stereo"};
+static const char* wm9711_rec_sel[] =
+	{"Mic 1", "NC", "NC", "Master Mix", "Line", "Headphone Mix", "Phone Mix", "Phone"};
+static const char* wm9711_ng_type[] = {"Constant Gain", "Mute"};
+
+static const struct ac97_enum wm9711_enum[] = {
+AC97_ENUM_SINGLE(AC97_PCI_SVID, 14, 4, wm9711_alc_select),
+AC97_ENUM_SINGLE(AC97_VIDEO, 10, 4, wm9711_alc_mix),
+AC97_ENUM_SINGLE(AC97_AUX, 9, 4, wm9711_out3_src),
+AC97_ENUM_SINGLE(AC97_AUX, 8, 2, wm9711_out3_lrsrc),
+AC97_ENUM_SINGLE(AC97_REC_SEL, 12, 4, wm9711_rec_adc),
+AC97_ENUM_SINGLE(AC97_MASTER_TONE, 15, 2, wm9711_base),
+AC97_ENUM_DOUBLE(AC97_REC_GAIN, 14, 6, 2, wm9711_rec_gain),
+AC97_ENUM_SINGLE(AC97_MIC, 5, 4, wm9711_mic),
+AC97_ENUM_DOUBLE(AC97_REC_SEL, 8, 0, 8, wm9711_rec_sel),
+AC97_ENUM_SINGLE(AC97_PCI_SVID, 5, 2, wm9711_ng_type),
+};
+
+static const struct snd_kcontrol_new wm9711_snd_ac97_controls[] = {
+AC97_SINGLE("ALC Target Volume", AC97_CODEC_CLASS_REV, 12, 15, 0),
+AC97_SINGLE("ALC Hold Time", AC97_CODEC_CLASS_REV, 8, 15, 0),
+AC97_SINGLE("ALC Decay Time", AC97_CODEC_CLASS_REV, 4, 15, 0),
+AC97_SINGLE("ALC Attack Time", AC97_CODEC_CLASS_REV, 0, 15, 0),
+AC97_ENUM("ALC Function", wm9711_enum[0]),
+AC97_SINGLE("ALC Max Volume", AC97_PCI_SVID, 11, 7, 1),
+AC97_SINGLE("ALC ZC Timeout", AC97_PCI_SVID, 9, 3, 1),
+AC97_SINGLE("ALC ZC Switch", AC97_PCI_SVID, 8, 1, 0),
+AC97_SINGLE("ALC NG Switch", AC97_PCI_SVID, 7, 1, 0),
+AC97_ENUM("ALC NG Type", wm9711_enum[9]),
+AC97_SINGLE("ALC NG Threshold", AC97_PCI_SVID, 0, 31, 1),
+
+AC97_SINGLE("Side Tone Switch", AC97_VIDEO, 15, 1, 1),
+AC97_SINGLE("Side Tone Volume", AC97_VIDEO, 12, 7, 1),
+AC97_ENUM("ALC Headphone Mux", wm9711_enum[1]),
+AC97_SINGLE("ALC Headphone Volume", AC97_VIDEO, 7, 7, 1),
+
+AC97_SINGLE("Out3 Switch", AC97_AUX, 15, 1, 1),
+AC97_SINGLE("Out3 ZC Switch", AC97_AUX, 7, 1, 0),
+AC97_ENUM("Out3 Mux", wm9711_enum[2]),
+AC97_ENUM("Out3 LR Mux", wm9711_enum[3]),
+AC97_SINGLE("Out3 Volume", AC97_AUX, 0, 31, 1),
+
+AC97_SINGLE("Beep to Headphone Switch", AC97_PC_BEEP, 15, 1, 1),
+AC97_SINGLE("Beep to Headphone Volume", AC97_PC_BEEP, 12, 7, 1),
+AC97_SINGLE("Beep to Side Tone Switch", AC97_PC_BEEP, 11, 1, 1),
+AC97_SINGLE("Beep to Side Tone Volume", AC97_PC_BEEP, 8, 7, 1),
+AC97_SINGLE("Beep to Phone Switch", AC97_PC_BEEP, 7, 1, 1),
+AC97_SINGLE("Beep to Phone Volume", AC97_PC_BEEP, 4, 7, 1),
+
+AC97_SINGLE("Aux to Headphone Switch", AC97_CD, 15, 1, 1),
+AC97_SINGLE("Aux to Headphone Volume", AC97_CD, 12, 7, 1),
+AC97_SINGLE("Aux to Side Tone Switch", AC97_CD, 11, 1, 1),
+AC97_SINGLE("Aux to Side Tone Volume", AC97_CD, 8, 7, 1),
+AC97_SINGLE("Aux to Phone Switch", AC97_CD, 7, 1, 1),
+AC97_SINGLE("Aux to Phone Volume", AC97_CD, 4, 7, 1),
+
+AC97_SINGLE("Phone to Headphone Switch", AC97_PHONE, 15, 1, 1),
+AC97_SINGLE("Phone to Master Switch", AC97_PHONE, 14, 1, 1),
+
+AC97_SINGLE("Line to Headphone Switch", AC97_LINE, 15, 1, 1),
+AC97_SINGLE("Line to Master Switch", AC97_LINE, 14, 1, 1),
+AC97_SINGLE("Line to Phone Switch", AC97_LINE, 13, 1, 1),
+
+AC97_SINGLE("PCM Playback to Headphone Switch", AC97_PCM, 15, 1, 1),
+AC97_SINGLE("PCM Playback to Master Switch", AC97_PCM, 14, 1, 1),
+AC97_SINGLE("PCM Playback to Phone Switch", AC97_PCM, 13, 1, 1),
+
+AC97_SINGLE("Capture 20dB Boost Switch", AC97_REC_SEL, 14, 1, 0),
+AC97_ENUM("Capture to Phone Mux", wm9711_enum[4]),
+AC97_SINGLE("Capture to Phone 20dB Boost Switch", AC97_REC_SEL, 11, 1, 1),
+AC97_ENUM("Capture Select", wm9711_enum[8]),
+
+AC97_SINGLE("3D Upper Cut-off Switch", AC97_3D_CONTROL, 5, 1, 1),
+AC97_SINGLE("3D Lower Cut-off Switch", AC97_3D_CONTROL, 4, 1, 1),
+
+AC97_ENUM("Bass Control", wm9711_enum[5]),
+AC97_SINGLE("Bass Cut-off Switch", AC97_MASTER_TONE, 12, 1, 1),
+AC97_SINGLE("Tone Cut-off Switch", AC97_MASTER_TONE, 4, 1, 1),
+AC97_SINGLE("Playback Attenuate (-6dB) Switch", AC97_MASTER_TONE, 6, 1, 0),
+
+AC97_SINGLE("ADC Switch", AC97_REC_GAIN, 15, 1, 1),
+AC97_ENUM("Capture Volume Steps", wm9711_enum[6]),
+AC97_DOUBLE("Capture Volume", AC97_REC_GAIN, 8, 0, 63, 1),
+AC97_SINGLE("Capture ZC Switch", AC97_REC_GAIN, 7, 1, 0),
+
+AC97_SINGLE("Mic 1 to Phone Switch", AC97_MIC, 14, 1, 1),
+AC97_SINGLE("Mic 2 to Phone Switch", AC97_MIC, 13, 1, 1),
+AC97_ENUM("Mic Select Source", wm9711_enum[7]),
+AC97_SINGLE("Mic 1 Volume", AC97_MIC, 8, 31, 1),
+AC97_SINGLE("Mic 2 Volume", AC97_MIC, 0, 31, 1),
+AC97_SINGLE("Mic 20dB Boost Switch", AC97_MIC, 7, 1, 0),
+
+AC97_SINGLE("Master ZC Switch", AC97_MASTER, 7, 1, 0),
+AC97_SINGLE("Headphone ZC Switch", AC97_HEADPHONE, 7, 1, 0),
+AC97_SINGLE("Mono ZC Switch", AC97_MASTER_MONO, 7, 1, 0),
+};
+
+static int patch_wolfson_wm9711_specific(struct snd_ac97 * ac97)
+{
+	int err, i;
+
+	for (i = 0; i < ARRAY_SIZE(wm9711_snd_ac97_controls); i++) {
+		if ((err = snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&wm9711_snd_ac97_controls[i], ac97))) < 0)
+			return err;
+	}
+	snd_ac97_write_cache(ac97,  AC97_CODEC_CLASS_REV, 0x0808);
+	snd_ac97_write_cache(ac97,  AC97_PCI_SVID, 0x0808);
+	snd_ac97_write_cache(ac97,  AC97_VIDEO, 0x0808);
+	snd_ac97_write_cache(ac97,  AC97_AUX, 0x0808);
+	snd_ac97_write_cache(ac97,  AC97_PC_BEEP, 0x0808);
+	snd_ac97_write_cache(ac97,  AC97_CD, 0x0000);
+	return 0;
+}
+
+static struct snd_ac97_build_ops patch_wolfson_wm9711_ops = {
+	.build_specific = patch_wolfson_wm9711_specific,
+};
+
+int patch_wolfson11(struct snd_ac97 * ac97)
+{
+	/* WM9711, WM9712 */
+	ac97->build_ops = &patch_wolfson_wm9711_ops;
+
+	ac97->flags |= AC97_HAS_NO_REC_GAIN | AC97_STEREO_MUTES | AC97_HAS_NO_MIC |
+		AC97_HAS_NO_PC_BEEP | AC97_HAS_NO_VIDEO | AC97_HAS_NO_CD;
+
+	return 0;
+}
+
+static const char* wm9713_mic_mixer[] = {"Stereo", "Mic 1", "Mic 2", "Mute"};
+static const char* wm9713_rec_mux[] = {"Stereo", "Left", "Right", "Mute"};
+static const char* wm9713_rec_src[] =
+	{"Mic 1", "Mic 2", "Line", "Mono In", "Headphone Mix", "Master Mix",
+	"Mono Mix", "Zh"};
+static const char* wm9713_rec_gain[] = {"+1.5dB Steps", "+0.75dB Steps"};
+static const char* wm9713_alc_select[] = {"None", "Left", "Right", "Stereo"};
+static const char* wm9713_mono_pga[] = {"Vmid", "Zh", "Mono Mix", "Inv 1"};
+static const char* wm9713_spk_pga[] =
+	{"Vmid", "Zh", "Headphone Mix", "Master Mix", "Inv", "NC", "NC", "NC"};
+static const char* wm9713_hp_pga[] = {"Vmid", "Zh", "Headphone Mix", "NC"};
+static const char* wm9713_out3_pga[] = {"Vmid", "Zh", "Inv 1", "NC"};
+static const char* wm9713_out4_pga[] = {"Vmid", "Zh", "Inv 2", "NC"};
+static const char* wm9713_dac_inv[] =
+	{"Off", "Mono Mix", "Master Mix", "Headphone Mix L", "Headphone Mix R",
+	"Headphone Mix Mono", "NC", "Vmid"};
+static const char* wm9713_base[] = {"Linear Control", "Adaptive Boost"};
+static const char* wm9713_ng_type[] = {"Constant Gain", "Mute"};
+
+static const struct ac97_enum wm9713_enum[] = {
+AC97_ENUM_SINGLE(AC97_LINE, 3, 4, wm9713_mic_mixer),
+AC97_ENUM_SINGLE(AC97_VIDEO, 14, 4, wm9713_rec_mux),
+AC97_ENUM_SINGLE(AC97_VIDEO, 9, 4, wm9713_rec_mux),
+AC97_ENUM_DOUBLE(AC97_VIDEO, 3, 0, 8, wm9713_rec_src),
+AC97_ENUM_DOUBLE(AC97_CD, 14, 6, 2, wm9713_rec_gain),
+AC97_ENUM_SINGLE(AC97_PCI_SVID, 14, 4, wm9713_alc_select),
+AC97_ENUM_SINGLE(AC97_REC_GAIN, 14, 4, wm9713_mono_pga),
+AC97_ENUM_DOUBLE(AC97_REC_GAIN, 11, 8, 8, wm9713_spk_pga),
+AC97_ENUM_DOUBLE(AC97_REC_GAIN, 6, 4, 4, wm9713_hp_pga),
+AC97_ENUM_SINGLE(AC97_REC_GAIN, 2, 4, wm9713_out3_pga),
+AC97_ENUM_SINGLE(AC97_REC_GAIN, 0, 4, wm9713_out4_pga),
+AC97_ENUM_DOUBLE(AC97_REC_GAIN_MIC, 13, 10, 8, wm9713_dac_inv),
+AC97_ENUM_SINGLE(AC97_GENERAL_PURPOSE, 15, 2, wm9713_base),
+AC97_ENUM_SINGLE(AC97_PCI_SVID, 5, 2, wm9713_ng_type),
+};
+
+static const struct snd_kcontrol_new wm13_snd_ac97_controls[] = {
+AC97_DOUBLE("Line In Volume", AC97_PC_BEEP, 8, 0, 31, 1),
+AC97_SINGLE("Line In to Headphone Switch", AC97_PC_BEEP, 15, 1, 1),
+AC97_SINGLE("Line In to Master Switch", AC97_PC_BEEP, 14, 1, 1),
+AC97_SINGLE("Line In to Mono Switch", AC97_PC_BEEP, 13, 1, 1),
+
+AC97_DOUBLE("PCM Playback Volume", AC97_PHONE, 8, 0, 31, 1),
+AC97_SINGLE("PCM Playback to Headphone Switch", AC97_PHONE, 15, 1, 1),
+AC97_SINGLE("PCM Playback to Master Switch", AC97_PHONE, 14, 1, 1),
+AC97_SINGLE("PCM Playback to Mono Switch", AC97_PHONE, 13, 1, 1),
+
+AC97_SINGLE("Mic 1 Volume", AC97_MIC, 8, 31, 1),
+AC97_SINGLE("Mic 2 Volume", AC97_MIC, 0, 31, 1),
+AC97_SINGLE("Mic 1 to Mono Switch", AC97_LINE, 7, 1, 1),
+AC97_SINGLE("Mic 2 to Mono Switch", AC97_LINE, 6, 1, 1),
+AC97_SINGLE("Mic Boost (+20dB) Switch", AC97_LINE, 5, 1, 0),
+AC97_ENUM("Mic to Headphone Mux", wm9713_enum[0]),
+AC97_SINGLE("Mic Headphone Mixer Volume", AC97_LINE, 0, 7, 1),
+
+AC97_SINGLE("Capture Switch", AC97_CD, 15, 1, 1),
+AC97_ENUM("Capture Volume Steps", wm9713_enum[4]),
+AC97_DOUBLE("Capture Volume", AC97_CD, 8, 0, 15, 0),
+AC97_SINGLE("Capture ZC Switch", AC97_CD, 7, 1, 0),
+
+AC97_ENUM("Capture to Headphone Mux", wm9713_enum[1]),
+AC97_SINGLE("Capture to Headphone Volume", AC97_VIDEO, 11, 7, 1),
+AC97_ENUM("Capture to Mono Mux", wm9713_enum[2]),
+AC97_SINGLE("Capture to Mono Boost (+20dB) Switch", AC97_VIDEO, 8, 1, 0),
+AC97_SINGLE("Capture ADC Boost (+20dB) Switch", AC97_VIDEO, 6, 1, 0),
+AC97_ENUM("Capture Select", wm9713_enum[3]),
+
+AC97_SINGLE("ALC Target Volume", AC97_CODEC_CLASS_REV, 12, 15, 0),
+AC97_SINGLE("ALC Hold Time", AC97_CODEC_CLASS_REV, 8, 15, 0),
+AC97_SINGLE("ALC Decay Time ", AC97_CODEC_CLASS_REV, 4, 15, 0),
+AC97_SINGLE("ALC Attack Time", AC97_CODEC_CLASS_REV, 0, 15, 0),
+AC97_ENUM("ALC Function", wm9713_enum[5]),
+AC97_SINGLE("ALC Max Volume", AC97_PCI_SVID, 11, 7, 0),
+AC97_SINGLE("ALC ZC Timeout", AC97_PCI_SVID, 9, 3, 0),
+AC97_SINGLE("ALC ZC Switch", AC97_PCI_SVID, 8, 1, 0),
+AC97_SINGLE("ALC NG Switch", AC97_PCI_SVID, 7, 1, 0),
+AC97_ENUM("ALC NG Type", wm9713_enum[13]),
+AC97_SINGLE("ALC NG Threshold", AC97_PCI_SVID, 0, 31, 0),
+
+AC97_DOUBLE("Master ZC Switch", AC97_MASTER, 14, 6, 1, 0),
+AC97_DOUBLE("Headphone ZC Switch", AC97_HEADPHONE, 14, 6, 1, 0),
+AC97_DOUBLE("Out3/4 ZC Switch", AC97_MASTER_MONO, 14, 6, 1, 0),
+AC97_SINGLE("Master Right Switch", AC97_MASTER, 7, 1, 1),
+AC97_SINGLE("Headphone Right Switch", AC97_HEADPHONE, 7, 1, 1),
+AC97_SINGLE("Out3/4 Right Switch", AC97_MASTER_MONO, 7, 1, 1),
+
+AC97_SINGLE("Mono In to Headphone Switch", AC97_MASTER_TONE, 15, 1, 1),
+AC97_SINGLE("Mono In to Master Switch", AC97_MASTER_TONE, 14, 1, 1),
+AC97_SINGLE("Mono In Volume", AC97_MASTER_TONE, 8, 31, 1),
+AC97_SINGLE("Mono Switch", AC97_MASTER_TONE, 7, 1, 1),
+AC97_SINGLE("Mono ZC Switch", AC97_MASTER_TONE, 6, 1, 0),
+AC97_SINGLE("Mono Volume", AC97_MASTER_TONE, 0, 31, 1),
+
+AC97_SINGLE("PC Beep to Headphone Switch", AC97_AUX, 15, 1, 1),
+AC97_SINGLE("PC Beep to Headphone Volume", AC97_AUX, 12, 7, 1),
+AC97_SINGLE("PC Beep to Master Switch", AC97_AUX, 11, 1, 1),
+AC97_SINGLE("PC Beep to Master Volume", AC97_AUX, 8, 7, 1),
+AC97_SINGLE("PC Beep to Mono Switch", AC97_AUX, 7, 1, 1),
+AC97_SINGLE("PC Beep to Mono Volume", AC97_AUX, 4, 7, 1),
+
+AC97_SINGLE("Voice to Headphone Switch", AC97_PCM, 15, 1, 1),
+AC97_SINGLE("Voice to Headphone Volume", AC97_PCM, 12, 7, 1),
+AC97_SINGLE("Voice to Master Switch", AC97_PCM, 11, 1, 1),
+AC97_SINGLE("Voice to Master Volume", AC97_PCM, 8, 7, 1),
+AC97_SINGLE("Voice to Mono Switch", AC97_PCM, 7, 1, 1),
+AC97_SINGLE("Voice to Mono Volume", AC97_PCM, 4, 7, 1),
+
+AC97_SINGLE("Aux to Headphone Switch", AC97_REC_SEL, 15, 1, 1),
+AC97_SINGLE("Aux to Headphone Volume", AC97_REC_SEL, 12, 7, 1),
+AC97_SINGLE("Aux to Master Switch", AC97_REC_SEL, 11, 1, 1),
+AC97_SINGLE("Aux to Master Volume", AC97_REC_SEL, 8, 7, 1),
+AC97_SINGLE("Aux to Mono Switch", AC97_REC_SEL, 7, 1, 1),
+AC97_SINGLE("Aux to Mono Volume", AC97_REC_SEL, 4, 7, 1),
+
+AC97_ENUM("Mono Input Mux", wm9713_enum[6]),
+AC97_ENUM("Master Input Mux", wm9713_enum[7]),
+AC97_ENUM("Headphone Input Mux", wm9713_enum[8]),
+AC97_ENUM("Out 3 Input Mux", wm9713_enum[9]),
+AC97_ENUM("Out 4 Input Mux", wm9713_enum[10]),
+
+AC97_ENUM("Bass Control", wm9713_enum[12]),
+AC97_SINGLE("Bass Cut-off Switch", AC97_GENERAL_PURPOSE, 12, 1, 1),
+AC97_SINGLE("Tone Cut-off Switch", AC97_GENERAL_PURPOSE, 4, 1, 1),
+AC97_SINGLE("Playback Attenuate (-6dB) Switch", AC97_GENERAL_PURPOSE, 6, 1, 0),
+AC97_SINGLE("Bass Volume", AC97_GENERAL_PURPOSE, 8, 15, 1),
+AC97_SINGLE("Tone Volume", AC97_GENERAL_PURPOSE, 0, 15, 1),
+};
+
+static const struct snd_kcontrol_new wm13_snd_ac97_controls_3d[] = {
+AC97_ENUM("Inv Input Mux", wm9713_enum[11]),
+AC97_SINGLE("3D Upper Cut-off Switch", AC97_REC_GAIN_MIC, 5, 1, 0),
+AC97_SINGLE("3D Lower Cut-off Switch", AC97_REC_GAIN_MIC, 4, 1, 0),
+AC97_SINGLE("3D Depth", AC97_REC_GAIN_MIC, 0, 15, 1),
+};
+
+static int patch_wolfson_wm9713_3d (struct snd_ac97 * ac97)
+{
+	int err, i;
+
+	for (i = 0; i < ARRAY_SIZE(wm13_snd_ac97_controls_3d); i++) {
+		if ((err = snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&wm13_snd_ac97_controls_3d[i], ac97))) < 0)
+			return err;
+	}
+	return 0;
+}
+
+static int patch_wolfson_wm9713_specific(struct snd_ac97 * ac97)
+{
+	int err, i;
+
+	for (i = 0; i < ARRAY_SIZE(wm13_snd_ac97_controls); i++) {
+		if ((err = snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&wm13_snd_ac97_controls[i], ac97))) < 0)
+			return err;
+	}
+	snd_ac97_write_cache(ac97, AC97_PC_BEEP, 0x0808);
+	snd_ac97_write_cache(ac97, AC97_PHONE, 0x0808);
+	snd_ac97_write_cache(ac97, AC97_MIC, 0x0808);
+	snd_ac97_write_cache(ac97, AC97_LINE, 0x00da);
+	snd_ac97_write_cache(ac97, AC97_CD, 0x0808);
+	snd_ac97_write_cache(ac97, AC97_VIDEO, 0xd612);
+	snd_ac97_write_cache(ac97, AC97_REC_GAIN, 0x1ba0);
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static void patch_wolfson_wm9713_suspend (struct snd_ac97 * ac97)
+{
+	snd_ac97_write_cache(ac97, AC97_EXTENDED_MID, 0xfeff);
+	snd_ac97_write_cache(ac97, AC97_EXTENDED_MSTATUS, 0xffff);
+}
+
+static void patch_wolfson_wm9713_resume (struct snd_ac97 * ac97)
+{
+	snd_ac97_write_cache(ac97, AC97_EXTENDED_MID, 0xda00);
+	snd_ac97_write_cache(ac97, AC97_EXTENDED_MSTATUS, 0x3810);
+	snd_ac97_write_cache(ac97, AC97_POWERDOWN, 0x0);
+}
+#endif
+
+static struct snd_ac97_build_ops patch_wolfson_wm9713_ops = {
+	.build_specific = patch_wolfson_wm9713_specific,
+	.build_3d = patch_wolfson_wm9713_3d,
+#ifdef CONFIG_PM
+	.suspend = patch_wolfson_wm9713_suspend,
+	.resume = patch_wolfson_wm9713_resume
+#endif
+};
+
+int patch_wolfson13(struct snd_ac97 * ac97)
+{
+	/* WM9713, WM9714 */
+	ac97->build_ops = &patch_wolfson_wm9713_ops;
+
+	ac97->flags |= AC97_HAS_NO_REC_GAIN | AC97_STEREO_MUTES | AC97_HAS_NO_PHONE |
+		AC97_HAS_NO_PC_BEEP | AC97_HAS_NO_VIDEO | AC97_HAS_NO_CD | AC97_HAS_NO_TONE |
+		AC97_HAS_NO_STD_PCM;
+    	ac97->scaps &= ~AC97_SCAP_MODEM;
+
+	snd_ac97_write_cache(ac97, AC97_EXTENDED_MID, 0xda00);
+	snd_ac97_write_cache(ac97, AC97_EXTENDED_MSTATUS, 0x3810);
+	snd_ac97_write_cache(ac97, AC97_POWERDOWN, 0x0);
+
+	return 0;
+}
+
+/*
+ * Tritech codec
+ */
+int patch_tritech_tr28028(struct snd_ac97 * ac97)
+{
+	snd_ac97_write_cache(ac97, 0x26, 0x0300);
+	snd_ac97_write_cache(ac97, 0x26, 0x0000);
+	snd_ac97_write_cache(ac97, AC97_SURROUND_MASTER, 0x0000);
+	snd_ac97_write_cache(ac97, AC97_SPDIF, 0x0000);
+	return 0;
+}
+
+/*
+ * Sigmatel STAC97xx codecs
+ */
+static int patch_sigmatel_stac9700_3d(struct snd_ac97 * ac97)
+{
+	struct snd_kcontrol *kctl;
+	int err;
+
+	if ((err = snd_ctl_add(ac97->bus->card, kctl = snd_ac97_cnew(&snd_ac97_controls_3d[0], ac97))) < 0)
+		return err;
+	strcpy(kctl->id.name, "3D Control Sigmatel - Depth");
+	kctl->private_value = AC97_SINGLE_VALUE(AC97_3D_CONTROL, 2, 3, 0);
+	snd_ac97_write_cache(ac97, AC97_3D_CONTROL, 0x0000);
+	return 0;
+}
+
+static int patch_sigmatel_stac9708_3d(struct snd_ac97 * ac97)
+{
+	struct snd_kcontrol *kctl;
+	int err;
+
+	if ((err = snd_ctl_add(ac97->bus->card, kctl = snd_ac97_cnew(&snd_ac97_controls_3d[0], ac97))) < 0)
+		return err;
+	strcpy(kctl->id.name, "3D Control Sigmatel - Depth");
+	kctl->private_value = AC97_SINGLE_VALUE(AC97_3D_CONTROL, 0, 3, 0);
+	if ((err = snd_ctl_add(ac97->bus->card, kctl = snd_ac97_cnew(&snd_ac97_controls_3d[0], ac97))) < 0)
+		return err;
+	strcpy(kctl->id.name, "3D Control Sigmatel - Rear Depth");
+	kctl->private_value = AC97_SINGLE_VALUE(AC97_3D_CONTROL, 2, 3, 0);
+	snd_ac97_write_cache(ac97, AC97_3D_CONTROL, 0x0000);
+	return 0;
+}
+
+static const struct snd_kcontrol_new snd_ac97_sigmatel_4speaker =
+AC97_SINGLE("Sigmatel 4-Speaker Stereo Playback Switch", AC97_SIGMATEL_DAC2INVERT, 2, 1, 0);
+
+static const struct snd_kcontrol_new snd_ac97_sigmatel_phaseinvert =
+AC97_SINGLE("Sigmatel Surround Phase Inversion Playback Switch", AC97_SIGMATEL_DAC2INVERT, 3, 1, 0);
+
+static const struct snd_kcontrol_new snd_ac97_sigmatel_controls[] = {
+AC97_SINGLE("Sigmatel DAC 6dB Attenuate", AC97_SIGMATEL_ANALOG, 1, 1, 0),
+AC97_SINGLE("Sigmatel ADC 6dB Attenuate", AC97_SIGMATEL_ANALOG, 0, 1, 0)
+};
+
+static int patch_sigmatel_stac97xx_specific(struct snd_ac97 * ac97)
+{
+	int err;
+
+	snd_ac97_write_cache(ac97, AC97_SIGMATEL_ANALOG, snd_ac97_read(ac97, AC97_SIGMATEL_ANALOG) & ~0x0003);
+	if (snd_ac97_try_bit(ac97, AC97_SIGMATEL_ANALOG, 1))
+		if ((err = patch_build_controls(ac97, &snd_ac97_sigmatel_controls[0], 1)) < 0)
+			return err;
+	if (snd_ac97_try_bit(ac97, AC97_SIGMATEL_ANALOG, 0))
+		if ((err = patch_build_controls(ac97, &snd_ac97_sigmatel_controls[1], 1)) < 0)
+			return err;
+	if (snd_ac97_try_bit(ac97, AC97_SIGMATEL_DAC2INVERT, 2))
+		if ((err = patch_build_controls(ac97, &snd_ac97_sigmatel_4speaker, 1)) < 0)
+			return err;
+	if (snd_ac97_try_bit(ac97, AC97_SIGMATEL_DAC2INVERT, 3))
+		if ((err = patch_build_controls(ac97, &snd_ac97_sigmatel_phaseinvert, 1)) < 0)
+			return err;
+	return 0;
+}
+
+static struct snd_ac97_build_ops patch_sigmatel_stac9700_ops = {
+	.build_3d	= patch_sigmatel_stac9700_3d,
+	.build_specific	= patch_sigmatel_stac97xx_specific
+};
+
+int patch_sigmatel_stac9700(struct snd_ac97 * ac97)
+{
+	ac97->build_ops = &patch_sigmatel_stac9700_ops;
+	return 0;
+}
+
+static int snd_ac97_stac9708_put_bias(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+	int err;
+
+	down(&ac97->page_mutex);
+	snd_ac97_write(ac97, AC97_SIGMATEL_BIAS1, 0xabba);
+	err = snd_ac97_update_bits(ac97, AC97_SIGMATEL_BIAS2, 0x0010,
+				   (ucontrol->value.integer.value[0] & 1) << 4);
+	snd_ac97_write(ac97, AC97_SIGMATEL_BIAS1, 0);
+	up(&ac97->page_mutex);
+	return err;
+}
+
+static const struct snd_kcontrol_new snd_ac97_stac9708_bias_control = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "Sigmatel Output Bias Switch",
+	.info = snd_ac97_info_volsw,
+	.get = snd_ac97_get_volsw,
+	.put = snd_ac97_stac9708_put_bias,
+	.private_value = AC97_SINGLE_VALUE(AC97_SIGMATEL_BIAS2, 4, 1, 0),
+};
+
+static int patch_sigmatel_stac9708_specific(struct snd_ac97 *ac97)
+{
+	int err;
+
+	/* the register bit is writable, but the function is not implemented: */
+	snd_ac97_remove_ctl(ac97, "PCM Out Path & Mute", NULL);
+
+	snd_ac97_rename_vol_ctl(ac97, "Headphone Playback", "Sigmatel Surround Playback");
+	if ((err = patch_build_controls(ac97, &snd_ac97_stac9708_bias_control, 1)) < 0)
+		return err;
+	return patch_sigmatel_stac97xx_specific(ac97);
+}
+
+static struct snd_ac97_build_ops patch_sigmatel_stac9708_ops = {
+	.build_3d	= patch_sigmatel_stac9708_3d,
+	.build_specific	= patch_sigmatel_stac9708_specific
+};
+
+int patch_sigmatel_stac9708(struct snd_ac97 * ac97)
+{
+	unsigned int codec72, codec6c;
+
+	ac97->build_ops = &patch_sigmatel_stac9708_ops;
+	ac97->caps |= 0x10;	/* HP (sigmatel surround) support */
+
+	codec72 = snd_ac97_read(ac97, AC97_SIGMATEL_BIAS2) & 0x8000;
+	codec6c = snd_ac97_read(ac97, AC97_SIGMATEL_ANALOG);
+
+	if ((codec72==0) && (codec6c==0)) {
+		snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC1, 0xabba);
+		snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC2, 0x1000);
+		snd_ac97_write_cache(ac97, AC97_SIGMATEL_BIAS1, 0xabba);
+		snd_ac97_write_cache(ac97, AC97_SIGMATEL_BIAS2, 0x0007);
+	} else if ((codec72==0x8000) && (codec6c==0)) {
+		snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC1, 0xabba);
+		snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC2, 0x1001);
+		snd_ac97_write_cache(ac97, AC97_SIGMATEL_DAC2INVERT, 0x0008);
+	} else if ((codec72==0x8000) && (codec6c==0x0080)) {
+		/* nothing */
+	}
+	snd_ac97_write_cache(ac97, AC97_SIGMATEL_MULTICHN, 0x0000);
+	return 0;
+}
+
+int patch_sigmatel_stac9721(struct snd_ac97 * ac97)
+{
+	ac97->build_ops = &patch_sigmatel_stac9700_ops;
+	if (snd_ac97_read(ac97, AC97_SIGMATEL_ANALOG) == 0) {
+		// patch for SigmaTel
+		snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC1, 0xabba);
+		snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC2, 0x4000);
+		snd_ac97_write_cache(ac97, AC97_SIGMATEL_BIAS1, 0xabba);
+		snd_ac97_write_cache(ac97, AC97_SIGMATEL_BIAS2, 0x0002);
+	}
+	snd_ac97_write_cache(ac97, AC97_SIGMATEL_MULTICHN, 0x0000);
+	return 0;
+}
+
+int patch_sigmatel_stac9744(struct snd_ac97 * ac97)
+{
+	// patch for SigmaTel
+	ac97->build_ops = &patch_sigmatel_stac9700_ops;
+	snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC1, 0xabba);
+	snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC2, 0x0000);	/* is this correct? --jk */
+	snd_ac97_write_cache(ac97, AC97_SIGMATEL_BIAS1, 0xabba);
+	snd_ac97_write_cache(ac97, AC97_SIGMATEL_BIAS2, 0x0002);
+	snd_ac97_write_cache(ac97, AC97_SIGMATEL_MULTICHN, 0x0000);
+	return 0;
+}
+
+int patch_sigmatel_stac9756(struct snd_ac97 * ac97)
+{
+	// patch for SigmaTel
+	ac97->build_ops = &patch_sigmatel_stac9700_ops;
+	snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC1, 0xabba);
+	snd_ac97_write_cache(ac97, AC97_SIGMATEL_CIC2, 0x0000);	/* is this correct? --jk */
+	snd_ac97_write_cache(ac97, AC97_SIGMATEL_BIAS1, 0xabba);
+	snd_ac97_write_cache(ac97, AC97_SIGMATEL_BIAS2, 0x0002);
+	snd_ac97_write_cache(ac97, AC97_SIGMATEL_MULTICHN, 0x0000);
+	return 0;
+}
+
+static int snd_ac97_stac9758_output_jack_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+	static char *texts[5] = { "Input/Disabled", "Front Output",
+		"Rear Output", "Center/LFE Output", "Mixer Output" };
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 5;
+	if (uinfo->value.enumerated.item > 4)
+		uinfo->value.enumerated.item = 4;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int snd_ac97_stac9758_output_jack_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+	int shift = kcontrol->private_value;
+	unsigned short val;
+
+	val = ac97->regs[AC97_SIGMATEL_OUTSEL] >> shift;
+	if (!(val & 4))
+		ucontrol->value.enumerated.item[0] = 0;
+	else
+		ucontrol->value.enumerated.item[0] = 1 + (val & 3);
+	return 0;
+}
+
+static int snd_ac97_stac9758_output_jack_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+	int shift = kcontrol->private_value;
+	unsigned short val;
+
+	if (ucontrol->value.enumerated.item[0] > 4)
+		return -EINVAL;
+	if (ucontrol->value.enumerated.item[0] == 0)
+		val = 0;
+	else
+		val = 4 | (ucontrol->value.enumerated.item[0] - 1);
+	return ac97_update_bits_page(ac97, AC97_SIGMATEL_OUTSEL,
+				     7 << shift, val << shift, 0);
+}
+
+static int snd_ac97_stac9758_input_jack_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+	static char *texts[7] = { "Mic2 Jack", "Mic1 Jack", "Line In Jack",
+		"Front Jack", "Rear Jack", "Center/LFE Jack", "Mute" };
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 7;
+	if (uinfo->value.enumerated.item > 6)
+		uinfo->value.enumerated.item = 6;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int snd_ac97_stac9758_input_jack_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+	int shift = kcontrol->private_value;
+	unsigned short val;
+
+	val = ac97->regs[AC97_SIGMATEL_INSEL];
+	ucontrol->value.enumerated.item[0] = (val >> shift) & 7;
+	return 0;
+}
+
+static int snd_ac97_stac9758_input_jack_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+	int shift = kcontrol->private_value;
+
+	return ac97_update_bits_page(ac97, AC97_SIGMATEL_INSEL, 7 << shift,
+				     ucontrol->value.enumerated.item[0] << shift, 0);
+}
+
+static int snd_ac97_stac9758_phonesel_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+	static char *texts[3] = { "None", "Front Jack", "Rear Jack" };
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 3;
+	if (uinfo->value.enumerated.item > 2)
+		uinfo->value.enumerated.item = 2;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int snd_ac97_stac9758_phonesel_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+
+	ucontrol->value.enumerated.item[0] = ac97->regs[AC97_SIGMATEL_IOMISC] & 3;
+	return 0;
+}
+
+static int snd_ac97_stac9758_phonesel_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+
+	return ac97_update_bits_page(ac97, AC97_SIGMATEL_IOMISC, 3,
+				     ucontrol->value.enumerated.item[0], 0);
+}
+
+#define STAC9758_OUTPUT_JACK(xname, shift) \
+{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+	.info = snd_ac97_stac9758_output_jack_info, \
+	.get = snd_ac97_stac9758_output_jack_get, \
+	.put = snd_ac97_stac9758_output_jack_put, \
+	.private_value = shift }
+#define STAC9758_INPUT_JACK(xname, shift) \
+{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+	.info = snd_ac97_stac9758_input_jack_info, \
+	.get = snd_ac97_stac9758_input_jack_get, \
+	.put = snd_ac97_stac9758_input_jack_put, \
+	.private_value = shift }
+static const struct snd_kcontrol_new snd_ac97_sigmatel_stac9758_controls[] = {
+	STAC9758_OUTPUT_JACK("Mic1 Jack", 1),
+	STAC9758_OUTPUT_JACK("LineIn Jack", 4),
+	STAC9758_OUTPUT_JACK("Front Jack", 7),
+	STAC9758_OUTPUT_JACK("Rear Jack", 10),
+	STAC9758_OUTPUT_JACK("Center/LFE Jack", 13),
+	STAC9758_INPUT_JACK("Mic Input Source", 0),
+	STAC9758_INPUT_JACK("Line Input Source", 8),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Headphone Amp",
+		.info = snd_ac97_stac9758_phonesel_info,
+		.get = snd_ac97_stac9758_phonesel_get,
+		.put = snd_ac97_stac9758_phonesel_put
+	},
+	AC97_SINGLE("Exchange Center/LFE", AC97_SIGMATEL_IOMISC, 4, 1, 0),
+	AC97_SINGLE("Headphone +3dB Boost", AC97_SIGMATEL_IOMISC, 8, 1, 0)
+};
+
+static int patch_sigmatel_stac9758_specific(struct snd_ac97 *ac97)
+{
+	int err;
+
+	err = patch_sigmatel_stac97xx_specific(ac97);
+	if (err < 0)
+		return err;
+	err = patch_build_controls(ac97, snd_ac97_sigmatel_stac9758_controls,
+				   ARRAY_SIZE(snd_ac97_sigmatel_stac9758_controls));
+	if (err < 0)
+		return err;
+	/* DAC-A direct */
+	snd_ac97_rename_vol_ctl(ac97, "Headphone Playback", "Front Playback");
+	/* DAC-A to Mix = PCM */
+	/* DAC-B direct = Surround */
+	/* DAC-B to Mix */
+	snd_ac97_rename_vol_ctl(ac97, "Video Playback", "Surround Mix Playback");
+	/* DAC-C direct = Center/LFE */
+
+	return 0;
+}
+
+static struct snd_ac97_build_ops patch_sigmatel_stac9758_ops = {
+	.build_3d	= patch_sigmatel_stac9700_3d,
+	.build_specific	= patch_sigmatel_stac9758_specific
+};
+
+int patch_sigmatel_stac9758(struct snd_ac97 * ac97)
+{
+	static unsigned short regs[4] = {
+		AC97_SIGMATEL_OUTSEL,
+		AC97_SIGMATEL_IOMISC,
+		AC97_SIGMATEL_INSEL,
+		AC97_SIGMATEL_VARIOUS
+	};
+	static unsigned short def_regs[4] = {
+		/* OUTSEL */ 0xd794, /* CL:CL, SR:SR, LO:MX, LI:DS, MI:DS */
+		/* IOMISC */ 0x2001,
+		/* INSEL */ 0x0201, /* LI:LI, MI:M1 */
+		/* VARIOUS */ 0x0040
+	};
+	static unsigned short m675_regs[4] = {
+		/* OUTSEL */ 0xfc70, /* CL:MX, SR:MX, LO:DS, LI:MX, MI:DS */
+		/* IOMISC */ 0x2102, /* HP amp on */
+		/* INSEL */ 0x0203, /* LI:LI, MI:FR */
+		/* VARIOUS */ 0x0041 /* stereo mic */
+	};
+	unsigned short *pregs = def_regs;
+	int i;
+
+	/* Gateway M675 notebook */
+	if (ac97->pci &&
+	    ac97->subsystem_vendor == 0x107b &&
+	    ac97->subsystem_device == 0x0601)
+	    	pregs = m675_regs;
+
+	// patch for SigmaTel
+	ac97->build_ops = &patch_sigmatel_stac9758_ops;
+	/* FIXME: assume only page 0 for writing cache */
+	snd_ac97_update_bits(ac97, AC97_INT_PAGING, AC97_PAGE_MASK, AC97_PAGE_VENDOR);
+	for (i = 0; i < 4; i++)
+		snd_ac97_write_cache(ac97, regs[i], pregs[i]);
+
+	ac97->flags |= AC97_STEREO_MUTES;
+	return 0;
+}
+
+/*
+ * Cirrus Logic CS42xx codecs
+ */
+static const struct snd_kcontrol_new snd_ac97_cirrus_controls_spdif[2] = {
+	AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH), AC97_CSR_SPDIF, 15, 1, 0),
+	AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "AC97-SPSA", AC97_CSR_ACMODE, 0, 3, 0)
+};
+
+static int patch_cirrus_build_spdif(struct snd_ac97 * ac97)
+{
+	int err;
+
+	/* con mask, pro mask, default */
+	if ((err = patch_build_controls(ac97, &snd_ac97_controls_spdif[0], 3)) < 0)
+		return err;
+	/* switch, spsa */
+	if ((err = patch_build_controls(ac97, &snd_ac97_cirrus_controls_spdif[0], 1)) < 0)
+		return err;
+	switch (ac97->id & AC97_ID_CS_MASK) {
+	case AC97_ID_CS4205:
+		if ((err = patch_build_controls(ac97, &snd_ac97_cirrus_controls_spdif[1], 1)) < 0)
+			return err;
+		break;
+	}
+	/* set default PCM S/PDIF params */
+	/* consumer,PCM audio,no copyright,no preemphasis,PCM coder,original,48000Hz */
+	snd_ac97_write_cache(ac97, AC97_CSR_SPDIF, 0x0a20);
+	return 0;
+}
+
+static struct snd_ac97_build_ops patch_cirrus_ops = {
+	.build_spdif = patch_cirrus_build_spdif
+};
+
+int patch_cirrus_spdif(struct snd_ac97 * ac97)
+{
+	/* Basically, the cs4201/cs4205/cs4297a has non-standard sp/dif registers.
+	   WHY CAN'T ANYONE FOLLOW THE BLOODY SPEC?  *sigh*
+	   - sp/dif EA ID is not set, but sp/dif is always present.
+	   - enable/disable is spdif register bit 15.
+	   - sp/dif control register is 0x68.  differs from AC97:
+	   - valid is bit 14 (vs 15)
+	   - no DRS
+	   - only 44.1/48k [00 = 48, 01=44,1] (AC97 is 00=44.1, 10=48)
+	   - sp/dif ssource select is in 0x5e bits 0,1.
+	*/
+
+	ac97->build_ops = &patch_cirrus_ops;
+	ac97->flags |= AC97_CS_SPDIF;
+	ac97->rates[AC97_RATES_SPDIF] &= ~SNDRV_PCM_RATE_32000;
+        ac97->ext_id |= AC97_EI_SPDIF;	/* force the detection of spdif */
+	snd_ac97_write_cache(ac97, AC97_CSR_ACMODE, 0x0080);
+	return 0;
+}
+
+int patch_cirrus_cs4299(struct snd_ac97 * ac97)
+{
+	/* force the detection of PC Beep */
+	ac97->flags |= AC97_HAS_PC_BEEP;
+
+	return patch_cirrus_spdif(ac97);
+}
+
+/*
+ * Conexant codecs
+ */
+static const struct snd_kcontrol_new snd_ac97_conexant_controls_spdif[1] = {
+	AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH), AC97_CXR_AUDIO_MISC, 3, 1, 0),
+};
+
+static int patch_conexant_build_spdif(struct snd_ac97 * ac97)
+{
+	int err;
+
+	/* con mask, pro mask, default */
+	if ((err = patch_build_controls(ac97, &snd_ac97_controls_spdif[0], 3)) < 0)
+		return err;
+	/* switch */
+	if ((err = patch_build_controls(ac97, &snd_ac97_conexant_controls_spdif[0], 1)) < 0)
+		return err;
+	/* set default PCM S/PDIF params */
+	/* consumer,PCM audio,no copyright,no preemphasis,PCM coder,original,48000Hz */
+	snd_ac97_write_cache(ac97, AC97_CXR_AUDIO_MISC,
+			     snd_ac97_read(ac97, AC97_CXR_AUDIO_MISC) & ~(AC97_CXR_SPDIFEN|AC97_CXR_COPYRGT|AC97_CXR_SPDIF_MASK));
+	return 0;
+}
+
+static struct snd_ac97_build_ops patch_conexant_ops = {
+	.build_spdif = patch_conexant_build_spdif
+};
+
+int patch_conexant(struct snd_ac97 * ac97)
+{
+	ac97->build_ops = &patch_conexant_ops;
+	ac97->flags |= AC97_CX_SPDIF;
+        ac97->ext_id |= AC97_EI_SPDIF;	/* force the detection of spdif */
+	ac97->rates[AC97_RATES_SPDIF] = SNDRV_PCM_RATE_48000; /* 48k only */
+	return 0;
+}
+
+/*
+ * Analog Device AD18xx, AD19xx codecs
+ */
+#ifdef CONFIG_PM
+static void ad18xx_resume(struct snd_ac97 *ac97)
+{
+	static unsigned short setup_regs[] = {
+		AC97_AD_MISC, AC97_AD_SERIAL_CFG, AC97_AD_JACK_SPDIF,
+	};
+	int i, codec;
+
+	for (i = 0; i < (int)ARRAY_SIZE(setup_regs); i++) {
+		unsigned short reg = setup_regs[i];
+		if (test_bit(reg, ac97->reg_accessed)) {
+			snd_ac97_write(ac97, reg, ac97->regs[reg]);
+			snd_ac97_read(ac97, reg);
+		}
+	}
+
+	if (! (ac97->flags & AC97_AD_MULTI))
+		/* normal restore */
+		snd_ac97_restore_status(ac97);
+	else {
+		/* restore the AD18xx codec configurations */
+		for (codec = 0; codec < 3; codec++) {
+			if (! ac97->spec.ad18xx.id[codec])
+				continue;
+			/* select single codec */
+			snd_ac97_update_bits(ac97, AC97_AD_SERIAL_CFG, 0x7000,
+					     ac97->spec.ad18xx.unchained[codec] | ac97->spec.ad18xx.chained[codec]);
+			ac97->bus->ops->write(ac97, AC97_AD_CODEC_CFG, ac97->spec.ad18xx.codec_cfg[codec]);
+		}
+		/* select all codecs */
+		snd_ac97_update_bits(ac97, AC97_AD_SERIAL_CFG, 0x7000, 0x7000);
+
+		/* restore status */
+		for (i = 2; i < 0x7c ; i += 2) {
+			if (i == AC97_POWERDOWN || i == AC97_EXTENDED_ID)
+				continue;
+			if (test_bit(i, ac97->reg_accessed)) {
+				/* handle multi codecs for AD18xx */
+				if (i == AC97_PCM) {
+					for (codec = 0; codec < 3; codec++) {
+						if (! ac97->spec.ad18xx.id[codec])
+							continue;
+						/* select single codec */
+						snd_ac97_update_bits(ac97, AC97_AD_SERIAL_CFG, 0x7000,
+								     ac97->spec.ad18xx.unchained[codec] | ac97->spec.ad18xx.chained[codec]);
+						/* update PCM bits */
+						ac97->bus->ops->write(ac97, AC97_PCM, ac97->spec.ad18xx.pcmreg[codec]);
+					}
+					/* select all codecs */
+					snd_ac97_update_bits(ac97, AC97_AD_SERIAL_CFG, 0x7000, 0x7000);
+					continue;
+				} else if (i == AC97_AD_TEST ||
+					   i == AC97_AD_CODEC_CFG ||
+					   i == AC97_AD_SERIAL_CFG)
+					continue; /* ignore */
+			}
+			snd_ac97_write(ac97, i, ac97->regs[i]);
+			snd_ac97_read(ac97, i);
+		}
+	}
+
+	snd_ac97_restore_iec958(ac97);
+}
+
+static void ad1888_resume(struct snd_ac97 *ac97)
+{
+    ad18xx_resume(ac97);
+    snd_ac97_write_cache(ac97, AC97_CODEC_CLASS_REV, 0x8080);
+}
+
+#endif
+
+static const struct snd_ac97_res_table ad1819_restbl[] = {
+	{ AC97_PHONE, 0x9f1f },
+	{ AC97_MIC, 0x9f1f },
+	{ AC97_LINE, 0x9f1f },
+	{ AC97_CD, 0x9f1f },
+	{ AC97_VIDEO, 0x9f1f },
+	{ AC97_AUX, 0x9f1f },
+	{ AC97_PCM, 0x9f1f },
+	{0} /* terminator */
+};
+
+int patch_ad1819(struct snd_ac97 * ac97)
+{
+	unsigned short scfg;
+
+	// patch for Analog Devices
+	scfg = snd_ac97_read(ac97, AC97_AD_SERIAL_CFG);
+        snd_ac97_write_cache(ac97, AC97_AD_SERIAL_CFG, scfg | 0x7000); /* select all codecs */
+        ac97->res_table = ad1819_restbl;
+	return 0;
+}
+
+static unsigned short patch_ad1881_unchained(struct snd_ac97 * ac97, int idx, unsigned short mask)
+{
+	unsigned short val;
+
+	// test for unchained codec
+	snd_ac97_update_bits(ac97, AC97_AD_SERIAL_CFG, 0x7000, mask);
+	snd_ac97_write_cache(ac97, AC97_AD_CODEC_CFG, 0x0000);	/* ID0C, ID1C, SDIE = off */
+	val = snd_ac97_read(ac97, AC97_VENDOR_ID2);
+	if ((val & 0xff40) != 0x5340)
+		return 0;
+	ac97->spec.ad18xx.unchained[idx] = mask;
+	ac97->spec.ad18xx.id[idx] = val;
+	ac97->spec.ad18xx.codec_cfg[idx] = 0x0000;
+	return mask;
+}
+
+static int patch_ad1881_chained1(struct snd_ac97 * ac97, int idx, unsigned short codec_bits)
+{
+	static int cfg_bits[3] = { 1<<12, 1<<14, 1<<13 };
+	unsigned short val;
+
+	snd_ac97_update_bits(ac97, AC97_AD_SERIAL_CFG, 0x7000, cfg_bits[idx]);
+	snd_ac97_write_cache(ac97, AC97_AD_CODEC_CFG, 0x0004);	// SDIE
+	val = snd_ac97_read(ac97, AC97_VENDOR_ID2);
+	if ((val & 0xff40) != 0x5340)
+		return 0;
+	if (codec_bits)
+		snd_ac97_write_cache(ac97, AC97_AD_CODEC_CFG, codec_bits);
+	ac97->spec.ad18xx.chained[idx] = cfg_bits[idx];
+	ac97->spec.ad18xx.id[idx] = val;
+	ac97->spec.ad18xx.codec_cfg[idx] = codec_bits ? codec_bits : 0x0004;
+	return 1;
+}
+
+static void patch_ad1881_chained(struct snd_ac97 * ac97, int unchained_idx, int cidx1, int cidx2)
+{
+	// already detected?
+	if (ac97->spec.ad18xx.unchained[cidx1] || ac97->spec.ad18xx.chained[cidx1])
+		cidx1 = -1;
+	if (ac97->spec.ad18xx.unchained[cidx2] || ac97->spec.ad18xx.chained[cidx2])
+		cidx2 = -1;
+	if (cidx1 < 0 && cidx2 < 0)
+		return;
+	// test for chained codecs
+	snd_ac97_update_bits(ac97, AC97_AD_SERIAL_CFG, 0x7000,
+			     ac97->spec.ad18xx.unchained[unchained_idx]);
+	snd_ac97_write_cache(ac97, AC97_AD_CODEC_CFG, 0x0002);		// ID1C
+	ac97->spec.ad18xx.codec_cfg[unchained_idx] = 0x0002;
+	if (cidx1 >= 0) {
+		if (cidx2 < 0)
+			patch_ad1881_chained1(ac97, cidx1, 0);
+		else if (patch_ad1881_chained1(ac97, cidx1, 0x0006))	// SDIE | ID1C
+			patch_ad1881_chained1(ac97, cidx2, 0);
+		else if (patch_ad1881_chained1(ac97, cidx2, 0x0006))	// SDIE | ID1C
+			patch_ad1881_chained1(ac97, cidx1, 0);
+	} else if (cidx2 >= 0) {
+		patch_ad1881_chained1(ac97, cidx2, 0);
+	}
+}
+
+static struct snd_ac97_build_ops patch_ad1881_build_ops = {
+#ifdef CONFIG_PM
+	.resume = ad18xx_resume
+#endif
+};
+
+int patch_ad1881(struct snd_ac97 * ac97)
+{
+	static const char cfg_idxs[3][2] = {
+		{2, 1},
+		{0, 2},
+		{0, 1}
+	};
+
+	// patch for Analog Devices
+	unsigned short codecs[3];
+	unsigned short val;
+	int idx, num;
+
+	val = snd_ac97_read(ac97, AC97_AD_SERIAL_CFG);
+	snd_ac97_write_cache(ac97, AC97_AD_SERIAL_CFG, val);
+	codecs[0] = patch_ad1881_unchained(ac97, 0, (1<<12));
+	codecs[1] = patch_ad1881_unchained(ac97, 1, (1<<14));
+	codecs[2] = patch_ad1881_unchained(ac97, 2, (1<<13));
+
+	if (! (codecs[0] || codecs[1] || codecs[2]))
+		goto __end;
+
+	for (idx = 0; idx < 3; idx++)
+		if (ac97->spec.ad18xx.unchained[idx])
+			patch_ad1881_chained(ac97, idx, cfg_idxs[idx][0], cfg_idxs[idx][1]);
+
+	if (ac97->spec.ad18xx.id[1]) {
+		ac97->flags |= AC97_AD_MULTI;
+		ac97->scaps |= AC97_SCAP_SURROUND_DAC;
+	}
+	if (ac97->spec.ad18xx.id[2]) {
+		ac97->flags |= AC97_AD_MULTI;
+		ac97->scaps |= AC97_SCAP_CENTER_LFE_DAC;
+	}
+
+      __end:
+	/* select all codecs */
+	snd_ac97_update_bits(ac97, AC97_AD_SERIAL_CFG, 0x7000, 0x7000);
+	/* check if only one codec is present */
+	for (idx = num = 0; idx < 3; idx++)
+		if (ac97->spec.ad18xx.id[idx])
+			num++;
+	if (num == 1) {
+		/* ok, deselect all ID bits */
+		snd_ac97_write_cache(ac97, AC97_AD_CODEC_CFG, 0x0000);
+		ac97->spec.ad18xx.codec_cfg[0] =
+			ac97->spec.ad18xx.codec_cfg[1] =
+			ac97->spec.ad18xx.codec_cfg[2] = 0x0000;
+	}
+	/* required for AD1886/AD1885 combination */
+	ac97->ext_id = snd_ac97_read(ac97, AC97_EXTENDED_ID);
+	if (ac97->spec.ad18xx.id[0]) {
+		ac97->id &= 0xffff0000;
+		ac97->id |= ac97->spec.ad18xx.id[0];
+	}
+	ac97->build_ops = &patch_ad1881_build_ops;
+	return 0;
+}
+
+static const struct snd_kcontrol_new snd_ac97_controls_ad1885[] = {
+	AC97_SINGLE("Digital Mono Direct", AC97_AD_MISC, 11, 1, 0),
+	/* AC97_SINGLE("Digital Audio Mode", AC97_AD_MISC, 12, 1, 0), */ /* seems problematic */
+	AC97_SINGLE("Low Power Mixer", AC97_AD_MISC, 14, 1, 0),
+	AC97_SINGLE("Zero Fill DAC", AC97_AD_MISC, 15, 1, 0),
+	AC97_SINGLE("Headphone Jack Sense", AC97_AD_JACK_SPDIF, 9, 1, 1), /* inverted */
+	AC97_SINGLE("Line Jack Sense", AC97_AD_JACK_SPDIF, 8, 1, 1), /* inverted */
+};
+
+static DECLARE_TLV_DB_SCALE(db_scale_6bit_6db_max, -8850, 150, 0);
+
+static int patch_ad1885_specific(struct snd_ac97 * ac97)
+{
+	int err;
+
+	if ((err = patch_build_controls(ac97, snd_ac97_controls_ad1885, ARRAY_SIZE(snd_ac97_controls_ad1885))) < 0)
+		return err;
+	reset_tlv(ac97, "Headphone Playback Volume",
+		  db_scale_6bit_6db_max);
+	return 0;
+}
+
+static struct snd_ac97_build_ops patch_ad1885_build_ops = {
+	.build_specific = &patch_ad1885_specific,
+#ifdef CONFIG_PM
+	.resume = ad18xx_resume
+#endif
+};
+
+int patch_ad1885(struct snd_ac97 * ac97)
+{
+	patch_ad1881(ac97);
+	/* This is required to deal with the Intel D815EEAL2 */
+	/* i.e. Line out is actually headphone out from codec */
+
+	/* set default */
+	snd_ac97_write_cache(ac97, AC97_AD_MISC, 0x0404);
+
+	ac97->build_ops = &patch_ad1885_build_ops;
+	return 0;
+}
+
+static int patch_ad1886_specific(struct snd_ac97 * ac97)
+{
+	reset_tlv(ac97, "Headphone Playback Volume",
+		  db_scale_6bit_6db_max);
+	return 0;
+}
+
+static struct snd_ac97_build_ops patch_ad1886_build_ops = {
+	.build_specific = &patch_ad1886_specific,
+#ifdef CONFIG_PM
+	.resume = ad18xx_resume
+#endif
+};
+
+int patch_ad1886(struct snd_ac97 * ac97)
+{
+	patch_ad1881(ac97);
+	/* Presario700 workaround */
+	/* for Jack Sense/SPDIF Register misetting causing */
+        snd_ac97_write_cache(ac97, AC97_AD_JACK_SPDIF, 0x0010);
+        ac97->build_ops = &patch_ad1886_build_ops;
+	return 0;
+}
+
+/* MISC bits (AD1888/AD1980/AD1985 register 0x76) */
+#define AC97_AD198X_MBC		0x0003	/* mic boost */
+#define AC97_AD198X_MBC_20	0x0000	/* +20dB */
+#define AC97_AD198X_MBC_10	0x0001	/* +10dB */
+#define AC97_AD198X_MBC_30	0x0002	/* +30dB */
+#define AC97_AD198X_VREFD	0x0004	/* VREF high-Z */
+#define AC97_AD198X_VREFH	0x0008	/* 0=2.25V, 1=3.7V */
+#define AC97_AD198X_VREF_0	0x000c	/* 0V (AD1985 only) */
+#define AC97_AD198X_VREF_MASK	(AC97_AD198X_VREFH | AC97_AD198X_VREFD)
+#define AC97_AD198X_VREF_SHIFT	2
+#define AC97_AD198X_SRU		0x0010	/* sample rate unlock */
+#define AC97_AD198X_LOSEL	0x0020	/* LINE_OUT amplifiers input select */
+#define AC97_AD198X_2MIC	0x0040	/* 2-channel mic select */
+#define AC97_AD198X_SPRD	0x0080	/* SPREAD enable */
+#define AC97_AD198X_DMIX0	0x0100	/* downmix mode: */
+					/*  0 = 6-to-4, 1 = 6-to-2 downmix */
+#define AC97_AD198X_DMIX1	0x0200	/* downmix mode: 1 = enabled */
+#define AC97_AD198X_HPSEL	0x0400	/* headphone amplifier input select */
+#define AC97_AD198X_CLDIS	0x0800	/* center/lfe disable */
+#define AC97_AD198X_LODIS	0x1000	/* LINE_OUT disable */
+#define AC97_AD198X_MSPLT	0x2000	/* mute split */
+#define AC97_AD198X_AC97NC	0x4000	/* AC97 no compatible mode */
+#define AC97_AD198X_DACZ	0x8000	/* DAC zero-fill mode */
+
+/* MISC 1 bits (AD1986 register 0x76) */
+#define AC97_AD1986_MBC		0x0003	/* mic boost */
+#define AC97_AD1986_MBC_20	0x0000	/* +20dB */
+#define AC97_AD1986_MBC_10	0x0001	/* +10dB */
+#define AC97_AD1986_MBC_30	0x0002	/* +30dB */
+#define AC97_AD1986_LISEL0	0x0004	/* LINE_IN select bit 0 */
+#define AC97_AD1986_LISEL1	0x0008	/* LINE_IN select bit 1 */
+#define AC97_AD1986_LISEL_MASK	(AC97_AD1986_LISEL1 | AC97_AD1986_LISEL0)
+#define AC97_AD1986_LISEL_LI	0x0000  /* LINE_IN pins as LINE_IN source */
+#define AC97_AD1986_LISEL_SURR	0x0004  /* SURROUND pins as LINE_IN source */
+#define AC97_AD1986_LISEL_MIC	0x0008  /* MIC_1/2 pins as LINE_IN source */
+#define AC97_AD1986_SRU		0x0010	/* sample rate unlock */
+#define AC97_AD1986_SOSEL	0x0020	/* SURROUND_OUT amplifiers input sel */
+#define AC97_AD1986_2MIC	0x0040	/* 2-channel mic select */
+#define AC97_AD1986_SPRD	0x0080	/* SPREAD enable */
+#define AC97_AD1986_DMIX0	0x0100	/* downmix mode: */
+					/*  0 = 6-to-4, 1 = 6-to-2 downmix */
+#define AC97_AD1986_DMIX1	0x0200	/* downmix mode: 1 = enabled */
+#define AC97_AD1986_CLDIS	0x0800	/* center/lfe disable */
+#define AC97_AD1986_SODIS	0x1000	/* SURROUND_OUT disable */
+#define AC97_AD1986_MSPLT	0x2000	/* mute split (read only 1) */
+#define AC97_AD1986_AC97NC	0x4000	/* AC97 no compatible mode (r/o 1) */
+#define AC97_AD1986_DACZ	0x8000	/* DAC zero-fill mode */
+
+/* MISC 2 bits (AD1986 register 0x70) */
+#define AC97_AD_MISC2		0x70	/* Misc Control Bits 2 (AD1986) */
+
+#define AC97_AD1986_CVREF0	0x0004	/* C/LFE VREF_OUT 2.25V */
+#define AC97_AD1986_CVREF1	0x0008	/* C/LFE VREF_OUT 0V */
+#define AC97_AD1986_CVREF2	0x0010	/* C/LFE VREF_OUT 3.7V */
+#define AC97_AD1986_CVREF_MASK \
+	(AC97_AD1986_CVREF2 | AC97_AD1986_CVREF1 | AC97_AD1986_CVREF0)
+#define AC97_AD1986_JSMAP	0x0020	/* Jack Sense Mapping 1 = alternate */
+#define AC97_AD1986_MMDIS	0x0080	/* Mono Mute Disable */
+#define AC97_AD1986_MVREF0	0x0400	/* MIC VREF_OUT 2.25V */
+#define AC97_AD1986_MVREF1	0x0800	/* MIC VREF_OUT 0V */
+#define AC97_AD1986_MVREF2	0x1000	/* MIC VREF_OUT 3.7V */
+#define AC97_AD1986_MVREF_MASK \
+	(AC97_AD1986_MVREF2 | AC97_AD1986_MVREF1 | AC97_AD1986_MVREF0)
+
+/* MISC 3 bits (AD1986 register 0x7a) */
+#define AC97_AD_MISC3		0x7a	/* Misc Control Bits 3 (AD1986) */
+
+#define AC97_AD1986_MMIX	0x0004	/* Mic Mix, left/right */
+#define AC97_AD1986_GPO		0x0008	/* General Purpose Out */
+#define AC97_AD1986_LOHPEN	0x0010	/* LINE_OUT headphone drive */
+#define AC97_AD1986_LVREF0	0x0100	/* LINE_OUT VREF_OUT 2.25V */
+#define AC97_AD1986_LVREF1	0x0200	/* LINE_OUT VREF_OUT 0V */
+#define AC97_AD1986_LVREF2	0x0400	/* LINE_OUT VREF_OUT 3.7V */
+#define AC97_AD1986_LVREF_MASK \
+	(AC97_AD1986_LVREF2 | AC97_AD1986_LVREF1 | AC97_AD1986_LVREF0)
+#define AC97_AD1986_JSINVA	0x0800	/* Jack Sense Invert SENSE_A */
+#define AC97_AD1986_LOSEL	0x1000	/* LINE_OUT amplifiers input select */
+#define AC97_AD1986_HPSEL0	0x2000	/* Headphone amplifiers */
+					/*   input select Surround DACs */
+#define AC97_AD1986_HPSEL1	0x4000	/* Headphone amplifiers input */
+					/*   select C/LFE DACs */
+#define AC97_AD1986_JSINVB	0x8000	/* Jack Sense Invert SENSE_B */
+
+/* Serial Config bits (AD1986 register 0x74) (incomplete) */
+#define AC97_AD1986_OMS0	0x0100	/* Optional Mic Selector bit 0 */
+#define AC97_AD1986_OMS1	0x0200	/* Optional Mic Selector bit 1 */
+#define AC97_AD1986_OMS2	0x0400	/* Optional Mic Selector bit 2 */
+#define AC97_AD1986_OMS_MASK \
+	(AC97_AD1986_OMS2 | AC97_AD1986_OMS1 | AC97_AD1986_OMS0)
+#define AC97_AD1986_OMS_M	0x0000  /* MIC_1/2 pins are MIC sources */
+#define AC97_AD1986_OMS_L	0x0100  /* LINE_IN pins are MIC sources */
+#define AC97_AD1986_OMS_C	0x0200  /* Center/LFE pins are MCI sources */
+#define AC97_AD1986_OMS_MC	0x0400  /* Mix of MIC and C/LFE pins */
+					/*   are MIC sources */
+#define AC97_AD1986_OMS_ML	0x0500  /* MIX of MIC and LINE_IN pins */
+					/*   are MIC sources */
+#define AC97_AD1986_OMS_LC	0x0600  /* MIX of LINE_IN and C/LFE pins */
+					/*   are MIC sources */
+#define AC97_AD1986_OMS_MLC	0x0700  /* MIX of MIC, LINE_IN, C/LFE pins */
+					/*   are MIC sources */
+
+
+static int snd_ac97_ad198x_spdif_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+	static char *texts[2] = { "AC-Link", "A/D Converter" };
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 2;
+	if (uinfo->value.enumerated.item > 1)
+		uinfo->value.enumerated.item = 1;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int snd_ac97_ad198x_spdif_source_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+	unsigned short val;
+
+	val = ac97->regs[AC97_AD_SERIAL_CFG];
+	ucontrol->value.enumerated.item[0] = (val >> 2) & 1;
+	return 0;
+}
+
+static int snd_ac97_ad198x_spdif_source_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+	unsigned short val;
+
+	if (ucontrol->value.enumerated.item[0] > 1)
+		return -EINVAL;
+	val = ucontrol->value.enumerated.item[0] << 2;
+	return snd_ac97_update_bits(ac97, AC97_AD_SERIAL_CFG, 0x0004, val);
+}
+
+static const struct snd_kcontrol_new snd_ac97_ad198x_spdif_source = {
+	.iface	= SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name	= SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
+	.info	= snd_ac97_ad198x_spdif_source_info,
+	.get	= snd_ac97_ad198x_spdif_source_get,
+	.put	= snd_ac97_ad198x_spdif_source_put,
+};
+
+static int patch_ad198x_post_spdif(struct snd_ac97 * ac97)
+{
+ 	return patch_build_controls(ac97, &snd_ac97_ad198x_spdif_source, 1);
+}
+
+static const struct snd_kcontrol_new snd_ac97_ad1981x_jack_sense[] = {
+	AC97_SINGLE("Headphone Jack Sense", AC97_AD_JACK_SPDIF, 11, 1, 0),
+	AC97_SINGLE("Line Jack Sense", AC97_AD_JACK_SPDIF, 12, 1, 0),
+};
+
+/* black list to avoid HP/Line jack-sense controls
+ * (SS vendor << 16 | device)
+ */
+static unsigned int ad1981_jacks_blacklist[] = {
+    0x10140537, /* Thinkpad T41p */
+    0x10140554, /* Thinkpad T42p/R50p */
+    0 /* end */
+};
+
+static int check_list(struct snd_ac97 *ac97, const unsigned int *list)
+{
+	u32 subid = ((u32)ac97->subsystem_vendor << 16) | ac97->subsystem_device;
+	for (; *list; list++)
+		if (*list == subid)
+			return 1;
+	return 0;
+}
+
+static int patch_ad1981a_specific(struct snd_ac97 * ac97)
+{
+	if (check_list(ac97, ad1981_jacks_blacklist))
+		return 0;
+	return patch_build_controls(ac97, snd_ac97_ad1981x_jack_sense,
+				    ARRAY_SIZE(snd_ac97_ad1981x_jack_sense));
+}
+
+static struct snd_ac97_build_ops patch_ad1981a_build_ops = {
+	.build_post_spdif = patch_ad198x_post_spdif,
+	.build_specific = patch_ad1981a_specific,
+#ifdef CONFIG_PM
+	.resume = ad18xx_resume
+#endif
+};
+
+/* white list to enable HP jack-sense bits
+ * (SS vendor << 16 | device)
+ */
+static unsigned int ad1981_jacks_whitelist[] = {
+	0x0e11005a, /* HP nc4000/4010 */
+	0x103c0890, /* HP nc6000 */
+	0x103c0938, /* HP nc4220 */
+	0x103c099c, /* HP nx6110 */
+	0x103c0944, /* HP nc6220 */
+	0x103c0934, /* HP nc8220 */
+	0x103c006d, /* HP nx9105 */
+	0x17340088, /* FSC Scenic-W */
+	0 /* end */
+};
+
+static void check_ad1981_hp_jack_sense(struct snd_ac97 *ac97)
+{
+	if (check_list(ac97, ad1981_jacks_whitelist))
+		/* enable headphone jack sense */
+		snd_ac97_update_bits(ac97, AC97_AD_JACK_SPDIF, 1<<11, 1<<11);
+}
+
+int patch_ad1981a(struct snd_ac97 *ac97)
+{
+	patch_ad1881(ac97);
+	ac97->build_ops = &patch_ad1981a_build_ops;
+	snd_ac97_update_bits(ac97, AC97_AD_MISC, AC97_AD198X_MSPLT, AC97_AD198X_MSPLT);
+	ac97->flags |= AC97_STEREO_MUTES;
+	check_ad1981_hp_jack_sense(ac97);
+	return 0;
+}
+
+static const struct snd_kcontrol_new snd_ac97_ad198x_2cmic =
+AC97_SINGLE("Stereo Mic", AC97_AD_MISC, 6, 1, 0);
+
+static int patch_ad1981b_specific(struct snd_ac97 *ac97)
+{
+	int err;
+
+	if ((err = patch_build_controls(ac97, &snd_ac97_ad198x_2cmic, 1)) < 0)
+		return err;
+	if (check_list(ac97, ad1981_jacks_blacklist))
+		return 0;
+	return patch_build_controls(ac97, snd_ac97_ad1981x_jack_sense,
+				    ARRAY_SIZE(snd_ac97_ad1981x_jack_sense));
+}
+
+static struct snd_ac97_build_ops patch_ad1981b_build_ops = {
+	.build_post_spdif = patch_ad198x_post_spdif,
+	.build_specific = patch_ad1981b_specific,
+#ifdef CONFIG_PM
+	.resume = ad18xx_resume
+#endif
+};
+
+int patch_ad1981b(struct snd_ac97 *ac97)
+{
+	patch_ad1881(ac97);
+	ac97->build_ops = &patch_ad1981b_build_ops;
+	snd_ac97_update_bits(ac97, AC97_AD_MISC, AC97_AD198X_MSPLT, AC97_AD198X_MSPLT);
+	ac97->flags |= AC97_STEREO_MUTES;
+	check_ad1981_hp_jack_sense(ac97);
+	return 0;
+}
+
+static int snd_ac97_ad1888_lohpsel_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *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_ac97_ad1888_lohpsel_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+	unsigned short val;
+
+	val = ac97->regs[AC97_AD_MISC];
+	ucontrol->value.integer.value[0] = !(val & AC97_AD198X_LOSEL);
+	return 0;
+}
+
+static int snd_ac97_ad1888_lohpsel_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+	unsigned short val;
+
+	val = !ucontrol->value.integer.value[0]
+		? (AC97_AD198X_LOSEL | AC97_AD198X_HPSEL) : 0;
+	return snd_ac97_update_bits(ac97, AC97_AD_MISC,
+				    AC97_AD198X_LOSEL | AC97_AD198X_HPSEL, val);
+}
+
+static int snd_ac97_ad1888_downmix_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+	static char *texts[3] = {"Off", "6 -> 4", "6 -> 2"};
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 3;
+	if (uinfo->value.enumerated.item > 2)
+		uinfo->value.enumerated.item = 2;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int snd_ac97_ad1888_downmix_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+	unsigned short val;
+
+	val = ac97->regs[AC97_AD_MISC];
+	if (!(val & AC97_AD198X_DMIX1))
+		ucontrol->value.enumerated.item[0] = 0;
+	else
+		ucontrol->value.enumerated.item[0] = 1 + ((val >> 8) & 1);
+	return 0;
+}
+
+static int snd_ac97_ad1888_downmix_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+	unsigned short val;
+
+	if (ucontrol->value.enumerated.item[0] > 2)
+		return -EINVAL;
+	if (ucontrol->value.enumerated.item[0] == 0)
+		val = 0;
+	else
+		val = AC97_AD198X_DMIX1 |
+			((ucontrol->value.enumerated.item[0] - 1) << 8);
+	return snd_ac97_update_bits(ac97, AC97_AD_MISC,
+				    AC97_AD198X_DMIX0 | AC97_AD198X_DMIX1, val);
+}
+
+static void ad1888_update_jacks(struct snd_ac97 *ac97)
+{
+	unsigned short val = 0;
+	if (! is_shared_linein(ac97))
+		val |= (1 << 12);
+	if (! is_shared_micin(ac97))
+		val |= (1 << 11);
+	/* shared Line-In */
+	snd_ac97_update_bits(ac97, AC97_AD_MISC, (1 << 11) | (1 << 12), val);
+}
+
+static const struct snd_kcontrol_new snd_ac97_ad1888_controls[] = {
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Exchange Front/Surround",
+		.info = snd_ac97_ad1888_lohpsel_info,
+		.get = snd_ac97_ad1888_lohpsel_get,
+		.put = snd_ac97_ad1888_lohpsel_put
+        },
+        AC97_SINGLE("V_REFOUT Enable", AC97_AD_MISC, 2, 1, 1),
+	AC97_SINGLE("High Pass Filter Enable", AC97_AD_TEST2, 12, 1, 1),
+	AC97_SINGLE("Spread Front to Surround and Center/LFE", AC97_AD_MISC, 7, 1, 0),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Downmix",
+		.info = snd_ac97_ad1888_downmix_info,
+		.get = snd_ac97_ad1888_downmix_get,
+		.put = snd_ac97_ad1888_downmix_put
+	},
+	AC97_SURROUND_JACK_MODE_CTL,
+	AC97_CHANNEL_MODE_CTL,
+
+	AC97_SINGLE("Headphone Jack Sense", AC97_AD_JACK_SPDIF, 10, 1, 0),
+	AC97_SINGLE("Line Jack Sense", AC97_AD_JACK_SPDIF, 12, 1, 0),
+};
+
+static int patch_ad1888_specific(struct snd_ac97 *ac97)
+{
+	/* rename 0x04 as "Master" and 0x02 as "Master Surround" */
+	snd_ac97_rename_vol_ctl(ac97, "Master Playback", "Master Surround Playback");
+	snd_ac97_rename_vol_ctl(ac97, "Headphone Playback", "Master Playback");
+	return patch_build_controls(ac97, snd_ac97_ad1888_controls, ARRAY_SIZE(snd_ac97_ad1888_controls));
+}
+
+static struct snd_ac97_build_ops patch_ad1888_build_ops = {
+	.build_post_spdif = patch_ad198x_post_spdif,
+	.build_specific = patch_ad1888_specific,
+#ifdef CONFIG_PM
+        .resume = ad1888_resume,
+#endif
+	.update_jacks = ad1888_update_jacks,
+};
+
+int patch_ad1888(struct snd_ac97 * ac97)
+{
+	unsigned short misc;
+
+	patch_ad1881(ac97);
+	ac97->build_ops = &patch_ad1888_build_ops;
+	/* Switch FRONT/SURROUND LINE-OUT/HP-OUT default connection */
+	/* it seems that most vendors connect line-out connector to headphone out of AC'97 */
+	/* AD-compatible mode */
+	/* Stereo mutes enabled */
+	misc = snd_ac97_read(ac97, AC97_AD_MISC);
+	snd_ac97_write_cache(ac97, AC97_AD_MISC, misc |
+			     AC97_AD198X_LOSEL |
+			     AC97_AD198X_HPSEL |
+			     AC97_AD198X_MSPLT |
+			     AC97_AD198X_AC97NC);
+	ac97->flags |= AC97_STEREO_MUTES;
+	return 0;
+}
+
+static int patch_ad1980_specific(struct snd_ac97 *ac97)
+{
+	int err;
+
+	if ((err = patch_ad1888_specific(ac97)) < 0)
+		return err;
+	return patch_build_controls(ac97, &snd_ac97_ad198x_2cmic, 1);
+}
+
+static struct snd_ac97_build_ops patch_ad1980_build_ops = {
+	.build_post_spdif = patch_ad198x_post_spdif,
+	.build_specific = patch_ad1980_specific,
+#ifdef CONFIG_PM
+	.resume = ad18xx_resume,
+#endif
+	.update_jacks = ad1888_update_jacks,
+};
+
+int patch_ad1980(struct snd_ac97 * ac97)
+{
+	patch_ad1888(ac97);
+	ac97->build_ops = &patch_ad1980_build_ops;
+	return 0;
+}
+
+static int snd_ac97_ad1985_vrefout_info(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_info *uinfo)
+{
+	static char *texts[4] = {"High-Z", "3.7 V", "2.25 V", "0 V"};
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 4;
+	if (uinfo->value.enumerated.item > 3)
+		uinfo->value.enumerated.item = 3;
+	strcpy(uinfo->value.enumerated.name,
+	       texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int snd_ac97_ad1985_vrefout_get(struct snd_kcontrol *kcontrol,
+				       struct snd_ctl_elem_value *ucontrol)
+{
+	static const int reg2ctrl[4] = {2, 0, 1, 3};
+	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+	unsigned short val;
+	val = (ac97->regs[AC97_AD_MISC] & AC97_AD198X_VREF_MASK)
+	      >> AC97_AD198X_VREF_SHIFT;
+	ucontrol->value.enumerated.item[0] = reg2ctrl[val];
+	return 0;
+}
+
+static int snd_ac97_ad1985_vrefout_put(struct snd_kcontrol *kcontrol, 
+				       struct snd_ctl_elem_value *ucontrol)
+{
+	static const int ctrl2reg[4] = {1, 2, 0, 3};
+	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+	unsigned short val;
+
+	if (ucontrol->value.enumerated.item[0] > 3
+	    || ucontrol->value.enumerated.item[0] < 0)
+		return -EINVAL;
+	val = ctrl2reg[ucontrol->value.enumerated.item[0]]
+	      << AC97_AD198X_VREF_SHIFT;
+	return snd_ac97_update_bits(ac97, AC97_AD_MISC,
+				    AC97_AD198X_VREF_MASK, val);
+}
+
+
+static const struct snd_kcontrol_new snd_ac97_ad1985_controls[] = {
+	AC97_SINGLE("Exchange Center/LFE", AC97_AD_SERIAL_CFG, 3, 1, 0),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Exchange Front/Surround",
+		.info = snd_ac97_ad1888_lohpsel_info,
+		.get = snd_ac97_ad1888_lohpsel_get,
+		.put = snd_ac97_ad1888_lohpsel_put
+	},
+	AC97_SINGLE("High Pass Filter Enable", AC97_AD_TEST2, 12, 1, 1),
+	AC97_SINGLE("Spread Front to Surround and Center/LFE",
+		    AC97_AD_MISC, 7, 1, 0),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Downmix",
+		.info = snd_ac97_ad1888_downmix_info,
+		.get = snd_ac97_ad1888_downmix_get,
+		.put = snd_ac97_ad1888_downmix_put
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "V_REFOUT",
+		.info = snd_ac97_ad1985_vrefout_info,
+		.get = snd_ac97_ad1985_vrefout_get,
+		.put = snd_ac97_ad1985_vrefout_put
+	},
+	AC97_SURROUND_JACK_MODE_CTL,
+	AC97_CHANNEL_MODE_CTL,
+
+	AC97_SINGLE("Headphone Jack Sense", AC97_AD_JACK_SPDIF, 10, 1, 0),
+	AC97_SINGLE("Line Jack Sense", AC97_AD_JACK_SPDIF, 12, 1, 0),
+};
+
+static void ad1985_update_jacks(struct snd_ac97 *ac97)
+{
+	ad1888_update_jacks(ac97);
+	snd_ac97_update_bits(ac97, AC97_AD_SERIAL_CFG, 1 << 9,
+			     is_shared_micin(ac97) ? 0 : 1 << 9);
+}
+
+static int patch_ad1985_specific(struct snd_ac97 *ac97)
+{
+	int err;
+
+	/* rename 0x04 as "Master" and 0x02 as "Master Surround" */
+	snd_ac97_rename_vol_ctl(ac97, "Master Playback",
+				"Master Surround Playback");
+	snd_ac97_rename_vol_ctl(ac97, "Headphone Playback", "Master Playback");
+
+	if ((err = patch_build_controls(ac97, &snd_ac97_ad198x_2cmic, 1)) < 0)
+		return err;
+
+	return patch_build_controls(ac97, snd_ac97_ad1985_controls,
+				    ARRAY_SIZE(snd_ac97_ad1985_controls));
+}
+
+static struct snd_ac97_build_ops patch_ad1985_build_ops = {
+	.build_post_spdif = patch_ad198x_post_spdif,
+	.build_specific = patch_ad1985_specific,
+#ifdef CONFIG_PM
+	.resume = ad18xx_resume,
+#endif
+	.update_jacks = ad1985_update_jacks,
+};
+
+int patch_ad1985(struct snd_ac97 * ac97)
+{
+	unsigned short misc;
+
+	patch_ad1881(ac97);
+	ac97->build_ops = &patch_ad1985_build_ops;
+	misc = snd_ac97_read(ac97, AC97_AD_MISC);
+	/* switch front/surround line-out/hp-out */
+	/* AD-compatible mode */
+	/* Stereo mutes enabled */
+	snd_ac97_write_cache(ac97, AC97_AD_MISC, misc |
+			     AC97_AD198X_LOSEL |
+			     AC97_AD198X_HPSEL |
+			     AC97_AD198X_MSPLT |
+			     AC97_AD198X_AC97NC);
+	ac97->flags |= AC97_STEREO_MUTES;
+
+	/* update current jack configuration */
+	ad1985_update_jacks(ac97);
+
+        /* on AD1985 rev. 3, AC'97 revision bits are zero */
+	ac97->ext_id = (ac97->ext_id & ~AC97_EI_REV_MASK) | AC97_EI_REV_23;
+	return 0;
+}
+
+static int snd_ac97_ad1986_bool_info(struct snd_kcontrol *kcontrol,
+				     struct snd_ctl_elem_info *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_ac97_ad1986_lososel_get(struct snd_kcontrol *kcontrol,
+				       struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+	unsigned short val;
+
+	val = ac97->regs[AC97_AD_MISC3];
+	ucontrol->value.integer.value[0] = (val & AC97_AD1986_LOSEL) != 0;
+	return 0;
+}
+
+static int snd_ac97_ad1986_lososel_put(struct snd_kcontrol *kcontrol,
+				       struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+	int ret0;
+	int ret1;
+	int sprd = (ac97->regs[AC97_AD_MISC] & AC97_AD1986_SPRD) != 0;
+
+	ret0 = snd_ac97_update_bits(ac97, AC97_AD_MISC3, AC97_AD1986_LOSEL,
+					ucontrol->value.integer.value[0] != 0
+				    ? AC97_AD1986_LOSEL : 0);
+	if (ret0 < 0)
+		return ret0;
+
+	/* SOSEL is set to values of "Spread" or "Exchange F/S" controls */
+	ret1 = snd_ac97_update_bits(ac97, AC97_AD_MISC, AC97_AD1986_SOSEL,
+				    (ucontrol->value.integer.value[0] != 0
+				     || sprd)
+				    ? AC97_AD1986_SOSEL : 0);
+	if (ret1 < 0)
+		return ret1;
+
+	return (ret0 > 0 || ret1 > 0) ? 1 : 0;
+}
+
+static int snd_ac97_ad1986_spread_get(struct snd_kcontrol *kcontrol,
+				      struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+	unsigned short val;
+
+	val = ac97->regs[AC97_AD_MISC];
+	ucontrol->value.integer.value[0] = (val & AC97_AD1986_SPRD) != 0;
+	return 0;
+}
+
+static int snd_ac97_ad1986_spread_put(struct snd_kcontrol *kcontrol,
+				      struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+	int ret0;
+	int ret1;
+	int sprd = (ac97->regs[AC97_AD_MISC3] & AC97_AD1986_LOSEL) != 0;
+
+	ret0 = snd_ac97_update_bits(ac97, AC97_AD_MISC, AC97_AD1986_SPRD,
+					ucontrol->value.integer.value[0] != 0
+				    ? AC97_AD1986_SPRD : 0);
+	if (ret0 < 0)
+		return ret0;
+
+	/* SOSEL is set to values of "Spread" or "Exchange F/S" controls */
+	ret1 = snd_ac97_update_bits(ac97, AC97_AD_MISC, AC97_AD1986_SOSEL,
+				    (ucontrol->value.integer.value[0] != 0
+				     || sprd)
+				    ? AC97_AD1986_SOSEL : 0);
+	if (ret1 < 0)
+		return ret1;
+
+	return (ret0 > 0 || ret1 > 0) ? 1 : 0;
+}
+
+static int snd_ac97_ad1986_miclisel_get(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+
+	ucontrol->value.integer.value[0] = ac97->spec.ad18xx.swap_mic_linein;
+	return 0;
+}
+
+static int snd_ac97_ad1986_miclisel_put(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+	unsigned char swap = ucontrol->value.integer.value[0] != 0;
+
+	if (swap != ac97->spec.ad18xx.swap_mic_linein) {
+		ac97->spec.ad18xx.swap_mic_linein = swap;
+		if (ac97->build_ops->update_jacks)
+			ac97->build_ops->update_jacks(ac97);
+		return 1;
+	}
+	return 0;
+}
+
+static int snd_ac97_ad1986_vrefout_get(struct snd_kcontrol *kcontrol,
+				       struct snd_ctl_elem_value *ucontrol)
+{
+	/* Use MIC_1/2 V_REFOUT as the "get" value */
+	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+	unsigned short val;
+	unsigned short reg = ac97->regs[AC97_AD_MISC2];
+	if ((reg & AC97_AD1986_MVREF0) != 0)
+		val = 2;
+	else if ((reg & AC97_AD1986_MVREF1) != 0)
+		val = 3;
+	else if ((reg & AC97_AD1986_MVREF2) != 0)
+		val = 1;
+	else
+		val = 0;
+	ucontrol->value.enumerated.item[0] = val;
+	return 0;
+}
+
+static int snd_ac97_ad1986_vrefout_put(struct snd_kcontrol *kcontrol,
+				       struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+	unsigned short cval;
+	unsigned short lval;
+	unsigned short mval;
+	int cret;
+	int lret;
+	int mret;
+
+	switch (ucontrol->value.enumerated.item[0])
+	{
+	case 0: /* High-Z */
+		cval = 0;
+		lval = 0;
+		mval = 0;
+		break;
+	case 1: /* 3.7 V */
+		cval = AC97_AD1986_CVREF2;
+		lval = AC97_AD1986_LVREF2;
+		mval = AC97_AD1986_MVREF2;
+		break;
+	case 2: /* 2.25 V */
+		cval = AC97_AD1986_CVREF0;
+		lval = AC97_AD1986_LVREF0;
+		mval = AC97_AD1986_MVREF0;
+		break;
+	case 3: /* 0 V */
+		cval = AC97_AD1986_CVREF1;
+		lval = AC97_AD1986_LVREF1;
+		mval = AC97_AD1986_MVREF1;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	cret = snd_ac97_update_bits(ac97, AC97_AD_MISC2,
+				    AC97_AD1986_CVREF_MASK, cval);
+	if (cret < 0)
+		return cret;
+	lret = snd_ac97_update_bits(ac97, AC97_AD_MISC3,
+				    AC97_AD1986_LVREF_MASK, lval);
+	if (lret < 0)
+		return lret;
+	mret = snd_ac97_update_bits(ac97, AC97_AD_MISC2,
+				    AC97_AD1986_MVREF_MASK, mval);
+	if (mret < 0)
+		return mret;
+
+	return (cret > 0 || lret > 0 || mret > 0) ? 1 : 0;
+}
+
+static const struct snd_kcontrol_new snd_ac97_ad1986_controls[] = {
+	AC97_SINGLE("Exchange Center/LFE", AC97_AD_SERIAL_CFG, 3, 1, 0),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Exchange Front/Surround",
+		.info = snd_ac97_ad1986_bool_info,
+		.get = snd_ac97_ad1986_lososel_get,
+		.put = snd_ac97_ad1986_lososel_put
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Exchange Mic/Line In",
+		.info = snd_ac97_ad1986_bool_info,
+		.get = snd_ac97_ad1986_miclisel_get,
+		.put = snd_ac97_ad1986_miclisel_put
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Spread Front to Surround and Center/LFE",
+		.info = snd_ac97_ad1986_bool_info,
+		.get = snd_ac97_ad1986_spread_get,
+		.put = snd_ac97_ad1986_spread_put
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Downmix",
+		.info = snd_ac97_ad1888_downmix_info,
+		.get = snd_ac97_ad1888_downmix_get,
+		.put = snd_ac97_ad1888_downmix_put
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "V_REFOUT",
+		.info = snd_ac97_ad1985_vrefout_info,
+		.get = snd_ac97_ad1986_vrefout_get,
+		.put = snd_ac97_ad1986_vrefout_put
+	},
+	AC97_SURROUND_JACK_MODE_CTL,
+	AC97_CHANNEL_MODE_CTL,
+
+	AC97_SINGLE("Headphone Jack Sense", AC97_AD_JACK_SPDIF, 10, 1, 0),
+	AC97_SINGLE("Line Jack Sense", AC97_AD_JACK_SPDIF, 12, 1, 0)
+};
+
+static void ad1986_update_jacks(struct snd_ac97 *ac97)
+{
+	unsigned short misc_val = 0;
+	unsigned short ser_val;
+
+	/* disable SURROUND and CENTER/LFE if not surround mode */
+	if (! is_surround_on(ac97))
+		misc_val |= AC97_AD1986_SODIS;
+	if (! is_clfe_on(ac97))
+		misc_val |= AC97_AD1986_CLDIS;
+
+	/* select line input (default=LINE_IN, SURROUND or MIC_1/2) */
+	if (is_shared_linein(ac97))
+		misc_val |= AC97_AD1986_LISEL_SURR;
+	else if (ac97->spec.ad18xx.swap_mic_linein != 0)
+		misc_val |= AC97_AD1986_LISEL_MIC;
+	snd_ac97_update_bits(ac97, AC97_AD_MISC,
+			     AC97_AD1986_SODIS | AC97_AD1986_CLDIS |
+			     AC97_AD1986_LISEL_MASK,
+			     misc_val);
+
+	/* select microphone input (MIC_1/2, Center/LFE or LINE_IN) */
+	if (is_shared_micin(ac97))
+		ser_val = AC97_AD1986_OMS_C;
+	else if (ac97->spec.ad18xx.swap_mic_linein != 0)
+		ser_val = AC97_AD1986_OMS_L;
+	else
+		ser_val = AC97_AD1986_OMS_M;
+	snd_ac97_update_bits(ac97, AC97_AD_SERIAL_CFG,
+			     AC97_AD1986_OMS_MASK,
+			     ser_val);
+}
+
+static int patch_ad1986_specific(struct snd_ac97 *ac97)
+{
+	int err;
+
+	if ((err = patch_build_controls(ac97, &snd_ac97_ad198x_2cmic, 1)) < 0)
+		return err;
+
+	return patch_build_controls(ac97, snd_ac97_ad1986_controls,
+				    ARRAY_SIZE(snd_ac97_ad1985_controls));
+}
+
+static struct snd_ac97_build_ops patch_ad1986_build_ops = {
+	.build_post_spdif = patch_ad198x_post_spdif,
+	.build_specific = patch_ad1986_specific,
+#ifdef CONFIG_PM
+	.resume = ad18xx_resume,
+#endif
+	.update_jacks = ad1986_update_jacks,
+};
+
+int patch_ad1986(struct snd_ac97 * ac97)
+{
+	patch_ad1881(ac97);
+	ac97->build_ops = &patch_ad1986_build_ops;
+	ac97->flags |= AC97_STEREO_MUTES;
+
+	/* update current jack configuration */
+	ad1986_update_jacks(ac97);
+
+	return 0;
+}
+
+
+/*
+ * realtek ALC65x/850 codecs
+ */
+static void alc650_update_jacks(struct snd_ac97 *ac97)
+{
+	int shared;
+
+	/* shared Line-In / Surround Out */
+	shared = is_shared_surrout(ac97);
+	snd_ac97_update_bits(ac97, AC97_ALC650_MULTICH, 1 << 9,
+			     shared ? (1 << 9) : 0);
+	/* update shared Mic In / Center/LFE Out */
+	shared = is_shared_clfeout(ac97);
+	/* disable/enable vref */
+	snd_ac97_update_bits(ac97, AC97_ALC650_CLOCK, 1 << 12,
+			     shared ? (1 << 12) : 0);
+	/* turn on/off center-on-mic */
+	snd_ac97_update_bits(ac97, AC97_ALC650_MULTICH, 1 << 10,
+			     shared ? (1 << 10) : 0);
+	/* GPIO0 high for mic */
+	snd_ac97_update_bits(ac97, AC97_ALC650_GPIO_STATUS, 0x100,
+			     shared ? 0 : 0x100);
+}
+
+static const struct snd_kcontrol_new snd_ac97_controls_alc650[] = {
+	AC97_SINGLE("Duplicate Front", AC97_ALC650_MULTICH, 0, 1, 0),
+	AC97_SINGLE("Surround Down Mix", AC97_ALC650_MULTICH, 1, 1, 0),
+	AC97_SINGLE("Center/LFE Down Mix", AC97_ALC650_MULTICH, 2, 1, 0),
+	AC97_SINGLE("Exchange Center/LFE", AC97_ALC650_MULTICH, 3, 1, 0),
+	/* 4: Analog Input To Surround */
+	/* 5: Analog Input To Center/LFE */
+	/* 6: Independent Master Volume Right */
+	/* 7: Independent Master Volume Left */
+	/* 8: reserved */
+	/* 9: Line-In/Surround share */
+	/* 10: Mic/CLFE share */
+	/* 11-13: in IEC958 controls */
+	AC97_SINGLE("Swap Surround Slot", AC97_ALC650_MULTICH, 14, 1, 0),
+#if 0 /* always set in patch_alc650 */
+	AC97_SINGLE("IEC958 Input Clock Enable", AC97_ALC650_CLOCK, 0, 1, 0),
+	AC97_SINGLE("IEC958 Input Pin Enable", AC97_ALC650_CLOCK, 1, 1, 0),
+	AC97_SINGLE("Surround DAC Switch", AC97_ALC650_SURR_DAC_VOL, 15, 1, 1),
+	AC97_DOUBLE("Surround DAC Volume", AC97_ALC650_SURR_DAC_VOL, 8, 0, 31, 1),
+	AC97_SINGLE("Center/LFE DAC Switch", AC97_ALC650_LFE_DAC_VOL, 15, 1, 1),
+	AC97_DOUBLE("Center/LFE DAC Volume", AC97_ALC650_LFE_DAC_VOL, 8, 0, 31, 1),
+#endif
+	AC97_SURROUND_JACK_MODE_CTL,
+	AC97_CHANNEL_MODE_CTL,
+};
+
+static const struct snd_kcontrol_new snd_ac97_spdif_controls_alc650[] = {
+        AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",CAPTURE,SWITCH), AC97_ALC650_MULTICH, 11, 1, 0),
+        AC97_SINGLE("Analog to IEC958 Output", AC97_ALC650_MULTICH, 12, 1, 0),
+	/* disable this controls since it doesn't work as expected */
+	/* AC97_SINGLE("IEC958 Input Monitor", AC97_ALC650_MULTICH, 13, 1, 0), */
+};
+
+static DECLARE_TLV_DB_SCALE(db_scale_5bit_3db_max, -4350, 150, 0);
+
+static int patch_alc650_specific(struct snd_ac97 * ac97)
+{
+	int err;
+
+	if ((err = patch_build_controls(ac97, snd_ac97_controls_alc650, ARRAY_SIZE(snd_ac97_controls_alc650))) < 0)
+		return err;
+	if (ac97->ext_id & AC97_EI_SPDIF) {
+		if ((err = patch_build_controls(ac97, snd_ac97_spdif_controls_alc650, ARRAY_SIZE(snd_ac97_spdif_controls_alc650))) < 0)
+			return err;
+	}
+	if (ac97->id != AC97_ID_ALC650F)
+		reset_tlv(ac97, "Master Playback Volume",
+			  db_scale_5bit_3db_max);
+	return 0;
+}
+
+static struct snd_ac97_build_ops patch_alc650_ops = {
+	.build_specific	= patch_alc650_specific,
+	.update_jacks = alc650_update_jacks
+};
+
+int patch_alc650(struct snd_ac97 * ac97)
+{
+	unsigned short val;
+
+	ac97->build_ops = &patch_alc650_ops;
+
+	/* determine the revision */
+	val = snd_ac97_read(ac97, AC97_ALC650_REVISION) & 0x3f;
+	if (val < 3)
+		ac97->id = 0x414c4720;          /* Old version */
+	else if (val < 0x10)
+		ac97->id = 0x414c4721;          /* D version */
+	else if (val < 0x20)
+		ac97->id = 0x414c4722;          /* E version */
+	else if (val < 0x30)
+		ac97->id = 0x414c4723;          /* F version */
+
+	/* revision E or F */
+	/* FIXME: what about revision D ? */
+	ac97->spec.dev_flags = (ac97->id == 0x414c4722 ||
+				ac97->id == 0x414c4723);
+
+	/* enable AC97_ALC650_GPIO_SETUP, AC97_ALC650_CLOCK for R/W */
+	snd_ac97_write_cache(ac97, AC97_ALC650_GPIO_STATUS,
+		snd_ac97_read(ac97, AC97_ALC650_GPIO_STATUS) | 0x8000);
+
+	/* Enable SPDIF-IN only on Rev.E and above */
+	val = snd_ac97_read(ac97, AC97_ALC650_CLOCK);
+        /* SPDIF IN with pin 47 */
+        if (ac97->spec.dev_flags &&
+            /* ASUS A6KM requires EAPD */
+            ! (ac97->subsystem_vendor == 0x1043 &&
+               ac97->subsystem_device == 0x1103))
+            val |= 0x03; /* enable */
+        else
+            val &= ~0x03; /* disable */
+	snd_ac97_write_cache(ac97, AC97_ALC650_CLOCK, val);
+
+	/* set default: slot 3,4,7,8,6,9
+	   spdif-in monitor off, analog-spdif off, spdif-in off
+	   center on mic off, surround on line-in off
+	   downmix off, duplicate front off
+	*/
+	snd_ac97_write_cache(ac97, AC97_ALC650_MULTICH, 0);
+
+	/* set GPIO0 for mic bias */
+	/* GPIO0 pin output, no interrupt, high */
+	snd_ac97_write_cache(ac97, AC97_ALC650_GPIO_SETUP,
+			     snd_ac97_read(ac97, AC97_ALC650_GPIO_SETUP) | 0x01);
+	snd_ac97_write_cache(ac97, AC97_ALC650_GPIO_STATUS,
+			     (snd_ac97_read(ac97, AC97_ALC650_GPIO_STATUS) | 0x100) & ~0x10);
+
+	/* full DAC volume */
+	snd_ac97_write_cache(ac97, AC97_ALC650_SURR_DAC_VOL, 0x0808);
+	snd_ac97_write_cache(ac97, AC97_ALC650_LFE_DAC_VOL, 0x0808);
+	return 0;
+}
+
+static void alc655_update_jacks(struct snd_ac97 *ac97)
+{
+	int shared;
+
+	/* shared Line-In / Surround Out */
+	shared = is_shared_surrout(ac97);
+	ac97_update_bits_page(ac97, AC97_ALC650_MULTICH, 1 << 9,
+			      shared ? (1 << 9) : 0, 0);
+	/* update shared Mic In / Center/LFE Out */
+	shared = is_shared_clfeout(ac97);
+	/* misc control; vrefout disable */
+	snd_ac97_update_bits(ac97, AC97_ALC650_CLOCK, 1 << 12,
+			     shared ? (1 << 12) : 0);
+	ac97_update_bits_page(ac97, AC97_ALC650_MULTICH, 1 << 10,
+			      shared ? (1 << 10) : 0, 0);
+}
+
+static const struct snd_kcontrol_new snd_ac97_controls_alc655[] = {
+	AC97_PAGE_SINGLE("Duplicate Front", AC97_ALC650_MULTICH, 0, 1, 0, 0),
+	AC97_SURROUND_JACK_MODE_CTL,
+	AC97_CHANNEL_MODE_CTL,
+};
+
+static int alc655_iec958_route_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+	static char *texts_655[3] = { "PCM", "Analog In", "IEC958 In" };
+	static char *texts_658[4] = { "PCM", "Analog1 In", "Analog2 In", "IEC958 In" };
+	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = ac97->spec.dev_flags ? 4 : 3;
+	if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+		uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
+	strcpy(uinfo->value.enumerated.name,
+	       ac97->spec.dev_flags ?
+	       texts_658[uinfo->value.enumerated.item] :
+	       texts_655[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int alc655_iec958_route_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+	unsigned short val;
+
+	val = ac97->regs[AC97_ALC650_MULTICH];
+	val = (val >> 12) & 3;
+	if (ac97->spec.dev_flags && val == 3)
+		val = 0;
+	ucontrol->value.enumerated.item[0] = val;
+	return 0;
+}
+
+static int alc655_iec958_route_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+
+	return ac97_update_bits_page(ac97, AC97_ALC650_MULTICH, 3 << 12,
+				     (unsigned short)ucontrol->value.enumerated.item[0] << 12,
+				     0);
+}
+
+static const struct snd_kcontrol_new snd_ac97_spdif_controls_alc655[] = {
+        AC97_PAGE_SINGLE(SNDRV_CTL_NAME_IEC958("",CAPTURE,SWITCH), AC97_ALC650_MULTICH, 11, 1, 0, 0),
+	/* disable this controls since it doesn't work as expected */
+        /* AC97_PAGE_SINGLE("IEC958 Input Monitor", AC97_ALC650_MULTICH, 14, 1, 0, 0), */
+	{
+		.iface  = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name   = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
+		.info   = alc655_iec958_route_info,
+		.get    = alc655_iec958_route_get,
+		.put    = alc655_iec958_route_put,
+	},
+};
+
+static int patch_alc655_specific(struct snd_ac97 * ac97)
+{
+	int err;
+
+	if ((err = patch_build_controls(ac97, snd_ac97_controls_alc655, ARRAY_SIZE(snd_ac97_controls_alc655))) < 0)
+		return err;
+	if (ac97->ext_id & AC97_EI_SPDIF) {
+		if ((err = patch_build_controls(ac97, snd_ac97_spdif_controls_alc655, ARRAY_SIZE(snd_ac97_spdif_controls_alc655))) < 0)
+			return err;
+	}
+	return 0;
+}
+
+static struct snd_ac97_build_ops patch_alc655_ops = {
+	.build_specific	= patch_alc655_specific,
+	.update_jacks = alc655_update_jacks
+};
+
+int patch_alc655(struct snd_ac97 * ac97)
+{
+	unsigned int val;
+
+	if (ac97->id == AC97_ID_ALC658) {
+		ac97->spec.dev_flags = 1; /* ALC658 */
+		if ((snd_ac97_read(ac97, AC97_ALC650_REVISION) & 0x3f) == 2) {
+			ac97->id = AC97_ID_ALC658D;
+			ac97->spec.dev_flags = 2;
+		}
+	}
+
+	ac97->build_ops = &patch_alc655_ops;
+
+	/* assume only page 0 for writing cache */
+	snd_ac97_update_bits(ac97, AC97_INT_PAGING, AC97_PAGE_MASK, AC97_PAGE_VENDOR);
+
+	/* adjust default values */
+	val = snd_ac97_read(ac97, 0x7a); /* misc control */
+	if (ac97->spec.dev_flags) /* ALC658 */
+		val &= ~(1 << 1); /* Pin 47 is spdif input pin */
+	else { /* ALC655 */
+		if (ac97->subsystem_vendor == 0x1462 &&
+		    (ac97->subsystem_device == 0x0131 || /* MSI S270 laptop */
+		     ac97->subsystem_device == 0x0161 || /* LG K1 Express */
+		     ac97->subsystem_device == 0x0351 || /* MSI L725 laptop */
+		     ac97->subsystem_device == 0x0061))  /* MSI S250 laptop */
+			val &= ~(1 << 1); /* Pin 47 is EAPD (for internal speaker) */
+		else
+			val |= (1 << 1); /* Pin 47 is spdif input pin */
+	}
+	val &= ~(1 << 12); /* vref enable */
+	snd_ac97_write_cache(ac97, 0x7a, val);
+	/* set default: spdif-in enabled,
+	   spdif-in monitor off, spdif-in PCM off
+	   center on mic off, surround on line-in off
+	   duplicate front off
+	*/
+	snd_ac97_write_cache(ac97, AC97_ALC650_MULTICH, 1<<15);
+
+	/* full DAC volume */
+	snd_ac97_write_cache(ac97, AC97_ALC650_SURR_DAC_VOL, 0x0808);
+	snd_ac97_write_cache(ac97, AC97_ALC650_LFE_DAC_VOL, 0x0808);
+
+	/* update undocumented bit... */
+	if (ac97->id == AC97_ID_ALC658D)
+		snd_ac97_update_bits(ac97, 0x74, 0x0800, 0x0800);
+
+	return 0;
+}
+
+
+#define AC97_ALC850_JACK_SELECT	0x76
+#define AC97_ALC850_MISC1	0x7a
+
+static void alc850_update_jacks(struct snd_ac97 *ac97)
+{
+	int shared;
+
+	/* shared Line-In / Surround Out */
+	shared = is_shared_surrout(ac97);
+	/* SURR 1kOhm (bit4), Amp (bit5) */
+	snd_ac97_update_bits(ac97, AC97_ALC850_MISC1, (1<<4)|(1<<5),
+			     shared ? (1<<5) : (1<<4));
+	/* LINE-IN = 0, SURROUND = 2 */
+	snd_ac97_update_bits(ac97, AC97_ALC850_JACK_SELECT, 7 << 12,
+			     shared ? (2<<12) : (0<<12));
+	/* update shared Mic In / Center/LFE Out */
+	shared = is_shared_clfeout(ac97);
+	/* Vref disable (bit12), 1kOhm (bit13) */
+	snd_ac97_update_bits(ac97, AC97_ALC850_MISC1, (1<<12)|(1<<13),
+			     shared ? (1<<12) : (1<<13));
+	/* MIC-IN = 1, CENTER-LFE = 5 */
+	snd_ac97_update_bits(ac97, AC97_ALC850_JACK_SELECT, 7 << 4,
+			     shared ? (5<<4) : (1<<4));
+}
+
+static const struct snd_kcontrol_new snd_ac97_controls_alc850[] = {
+	AC97_PAGE_SINGLE("Duplicate Front", AC97_ALC650_MULTICH, 0, 1, 0, 0),
+	AC97_SINGLE("Mic Front Input Switch", AC97_ALC850_JACK_SELECT, 15, 1, 1),
+	AC97_SURROUND_JACK_MODE_CTL,
+	AC97_CHANNEL_MODE_CTL,
+};
+
+static int patch_alc850_specific(struct snd_ac97 *ac97)
+{
+	int err;
+
+	if ((err = patch_build_controls(ac97, snd_ac97_controls_alc850, ARRAY_SIZE(snd_ac97_controls_alc850))) < 0)
+		return err;
+	if (ac97->ext_id & AC97_EI_SPDIF) {
+		if ((err = patch_build_controls(ac97, snd_ac97_spdif_controls_alc655, ARRAY_SIZE(snd_ac97_spdif_controls_alc655))) < 0)
+			return err;
+	}
+	return 0;
+}
+
+static struct snd_ac97_build_ops patch_alc850_ops = {
+	.build_specific	= patch_alc850_specific,
+	.update_jacks = alc850_update_jacks
+};
+
+int patch_alc850(struct snd_ac97 *ac97)
+{
+	ac97->build_ops = &patch_alc850_ops;
+
+	ac97->spec.dev_flags = 0; /* for IEC958 playback route - ALC655 compatible */
+
+	/* assume only page 0 for writing cache */
+	snd_ac97_update_bits(ac97, AC97_INT_PAGING, AC97_PAGE_MASK, AC97_PAGE_VENDOR);
+
+	/* adjust default values */
+	/* set default: spdif-in enabled,
+	   spdif-in monitor off, spdif-in PCM off
+	   center on mic off, surround on line-in off
+	   duplicate front off
+	*/
+	snd_ac97_write_cache(ac97, AC97_ALC650_MULTICH, 1<<15);
+	/* SURR_OUT: on, Surr 1kOhm: on, Surr Amp: off, Front 1kOhm: off
+	 * Front Amp: on, Vref: enable, Center 1kOhm: on, Mix: on
+	 */
+	snd_ac97_write_cache(ac97, 0x7a, (1<<1)|(1<<4)|(0<<5)|(1<<6)|
+			     (1<<7)|(0<<12)|(1<<13)|(0<<14));
+	/* detection UIO2,3: all path floating, UIO3: MIC, Vref2: disable,
+	 * UIO1: FRONT, Vref3: disable, UIO3: LINE, Front-Mic: mute
+	 */
+	snd_ac97_write_cache(ac97, 0x76, (0<<0)|(0<<2)|(1<<4)|(1<<7)|(2<<8)|
+			     (1<<11)|(0<<12)|(1<<15));
+
+	/* full DAC volume */
+	snd_ac97_write_cache(ac97, AC97_ALC650_SURR_DAC_VOL, 0x0808);
+	snd_ac97_write_cache(ac97, AC97_ALC650_LFE_DAC_VOL, 0x0808);
+	return 0;
+}
+
+
+/*
+ * C-Media CM97xx codecs
+ */
+static void cm9738_update_jacks(struct snd_ac97 *ac97)
+{
+	/* shared Line-In / Surround Out */
+	snd_ac97_update_bits(ac97, AC97_CM9738_VENDOR_CTRL, 1 << 10,
+			     is_shared_surrout(ac97) ? (1 << 10) : 0);
+}
+
+static const struct snd_kcontrol_new snd_ac97_cm9738_controls[] = {
+	AC97_SINGLE("Duplicate Front", AC97_CM9738_VENDOR_CTRL, 13, 1, 0),
+	AC97_SURROUND_JACK_MODE_CTL,
+	AC97_CHANNEL_MODE_4CH_CTL,
+};
+
+static int patch_cm9738_specific(struct snd_ac97 * ac97)
+{
+	return patch_build_controls(ac97, snd_ac97_cm9738_controls, ARRAY_SIZE(snd_ac97_cm9738_controls));
+}
+
+static struct snd_ac97_build_ops patch_cm9738_ops = {
+	.build_specific	= patch_cm9738_specific,
+	.update_jacks = cm9738_update_jacks
+};
+
+int patch_cm9738(struct snd_ac97 * ac97)
+{
+	ac97->build_ops = &patch_cm9738_ops;
+	/* FIXME: can anyone confirm below? */
+	/* CM9738 has no PCM volume although the register reacts */
+	ac97->flags |= AC97_HAS_NO_PCM_VOL;
+	snd_ac97_write_cache(ac97, AC97_PCM, 0x8000);
+
+	return 0;
+}
+
+static int snd_ac97_cmedia_spdif_playback_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+	static char *texts[] = { "Analog", "Digital" };
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 2;
+	if (uinfo->value.enumerated.item > 1)
+		uinfo->value.enumerated.item = 1;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int snd_ac97_cmedia_spdif_playback_source_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+	unsigned short val;
+
+	val = ac97->regs[AC97_CM9739_SPDIF_CTRL];
+	ucontrol->value.enumerated.item[0] = (val >> 1) & 0x01;
+	return 0;
+}
+
+static int snd_ac97_cmedia_spdif_playback_source_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+
+	return snd_ac97_update_bits(ac97, AC97_CM9739_SPDIF_CTRL,
+				    0x01 << 1,
+				    (ucontrol->value.enumerated.item[0] & 0x01) << 1);
+}
+
+static const struct snd_kcontrol_new snd_ac97_cm9739_controls_spdif[] = {
+	/* BIT 0: SPDI_EN - always true */
+	{ /* BIT 1: SPDIFS */
+		.iface	= SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name	= SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
+		.info	= snd_ac97_cmedia_spdif_playback_source_info,
+		.get	= snd_ac97_cmedia_spdif_playback_source_get,
+		.put	= snd_ac97_cmedia_spdif_playback_source_put,
+	},
+	/* BIT 2: IG_SPIV */
+	AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",CAPTURE,NONE) "Valid Switch", AC97_CM9739_SPDIF_CTRL, 2, 1, 0),
+	/* BIT 3: SPI2F */
+	AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",CAPTURE,NONE) "Monitor", AC97_CM9739_SPDIF_CTRL, 3, 1, 0),
+	/* BIT 4: SPI2SDI */
+	AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",CAPTURE,SWITCH), AC97_CM9739_SPDIF_CTRL, 4, 1, 0),
+	/* BIT 8: SPD32 - 32bit SPDIF - not supported yet */
+};
+
+static void cm9739_update_jacks(struct snd_ac97 *ac97)
+{
+	/* shared Line-In / Surround Out */
+	snd_ac97_update_bits(ac97, AC97_CM9739_MULTI_CHAN, 1 << 10,
+			     is_shared_surrout(ac97) ? (1 << 10) : 0);
+	/* shared Mic In / Center/LFE Out **/
+	snd_ac97_update_bits(ac97, AC97_CM9739_MULTI_CHAN, 0x3000,
+			     is_shared_clfeout(ac97) ? 0x1000 : 0x2000);
+}
+
+static const struct snd_kcontrol_new snd_ac97_cm9739_controls[] = {
+	AC97_SURROUND_JACK_MODE_CTL,
+	AC97_CHANNEL_MODE_CTL,
+};
+
+static int patch_cm9739_specific(struct snd_ac97 * ac97)
+{
+	return patch_build_controls(ac97, snd_ac97_cm9739_controls, ARRAY_SIZE(snd_ac97_cm9739_controls));
+}
+
+static int patch_cm9739_post_spdif(struct snd_ac97 * ac97)
+{
+	return patch_build_controls(ac97, snd_ac97_cm9739_controls_spdif, ARRAY_SIZE(snd_ac97_cm9739_controls_spdif));
+}
+
+static struct snd_ac97_build_ops patch_cm9739_ops = {
+	.build_specific	= patch_cm9739_specific,
+	.build_post_spdif = patch_cm9739_post_spdif,
+	.update_jacks = cm9739_update_jacks
+};
+
+int patch_cm9739(struct snd_ac97 * ac97)
+{
+	unsigned short val;
+
+	ac97->build_ops = &patch_cm9739_ops;
+
+	/* CM9739/A has no Master and PCM volume although the register reacts */
+	ac97->flags |= AC97_HAS_NO_MASTER_VOL | AC97_HAS_NO_PCM_VOL;
+	snd_ac97_write_cache(ac97, AC97_MASTER, 0x8000);
+	snd_ac97_write_cache(ac97, AC97_PCM, 0x8000);
+
+	/* check spdif */
+	val = snd_ac97_read(ac97, AC97_EXTENDED_STATUS);
+	if (val & AC97_EA_SPCV) {
+		/* enable spdif in */
+		snd_ac97_write_cache(ac97, AC97_CM9739_SPDIF_CTRL,
+				     snd_ac97_read(ac97, AC97_CM9739_SPDIF_CTRL) | 0x01);
+		ac97->rates[AC97_RATES_SPDIF] = SNDRV_PCM_RATE_48000; /* 48k only */
+	} else {
+		ac97->ext_id &= ~AC97_EI_SPDIF; /* disable extended-id */
+		ac97->rates[AC97_RATES_SPDIF] = 0;
+	}
+
+	/* set-up multi channel */
+	/* bit 14: 0 = SPDIF, 1 = EAPD */
+	/* bit 13: enable internal vref output for mic */
+	/* bit 12: disable center/lfe (swithable) */
+	/* bit 10: disable surround/line (switchable) */
+	/* bit 9: mix 2 surround off */
+	/* bit 4: undocumented; 0 mutes the CM9739A, which defaults to 1 */
+	/* bit 3: undocumented; surround? */
+	/* bit 0: dB */
+	val = snd_ac97_read(ac97, AC97_CM9739_MULTI_CHAN) & (1 << 4);
+	val |= (1 << 3);
+	val |= (1 << 13);
+	if (! (ac97->ext_id & AC97_EI_SPDIF))
+		val |= (1 << 14);
+	snd_ac97_write_cache(ac97, AC97_CM9739_MULTI_CHAN, val);
+
+	/* FIXME: set up GPIO */
+	snd_ac97_write_cache(ac97, 0x70, 0x0100);
+	snd_ac97_write_cache(ac97, 0x72, 0x0020);
+	/* Special exception for ASUS W1000/CMI9739. It does not have an SPDIF in. */
+	if (ac97->pci &&
+	     ac97->subsystem_vendor == 0x1043 &&
+	     ac97->subsystem_device == 0x1843) {
+		snd_ac97_write_cache(ac97, AC97_CM9739_SPDIF_CTRL,
+			snd_ac97_read(ac97, AC97_CM9739_SPDIF_CTRL) & ~0x01);
+		snd_ac97_write_cache(ac97, AC97_CM9739_MULTI_CHAN,
+			snd_ac97_read(ac97, AC97_CM9739_MULTI_CHAN) | (1 << 14));
+	}
+
+	return 0;
+}
+
+#define AC97_CM9761_MULTI_CHAN	0x64
+#define AC97_CM9761_FUNC	0x66
+#define AC97_CM9761_SPDIF_CTRL	0x6c
+
+static void cm9761_update_jacks(struct snd_ac97 *ac97)
+{
+	/* FIXME: check the bits for each model
+	 *        model 83 is confirmed to work
+	 */
+	static unsigned short surr_on[3][2] = {
+		{ 0x0008, 0x0000 }, /* 9761-78 & 82 */
+		{ 0x0000, 0x0008 }, /* 9761-82 rev.B */
+		{ 0x0000, 0x0008 }, /* 9761-83 */
+	};
+	static unsigned short clfe_on[3][2] = {
+		{ 0x0000, 0x1000 }, /* 9761-78 & 82 */
+		{ 0x1000, 0x0000 }, /* 9761-82 rev.B */
+		{ 0x0000, 0x1000 }, /* 9761-83 */
+	};
+	static unsigned short surr_shared[3][2] = {
+		{ 0x0000, 0x0400 }, /* 9761-78 & 82 */
+		{ 0x0000, 0x0400 }, /* 9761-82 rev.B */
+		{ 0x0000, 0x0400 }, /* 9761-83 */
+	};
+	static unsigned short clfe_shared[3][2] = {
+		{ 0x2000, 0x0880 }, /* 9761-78 & 82 */
+		{ 0x0000, 0x2880 }, /* 9761-82 rev.B */
+		{ 0x2000, 0x0800 }, /* 9761-83 */
+	};
+	unsigned short val = 0;
+
+	val |= surr_on[ac97->spec.dev_flags][is_surround_on(ac97)];
+	val |= clfe_on[ac97->spec.dev_flags][is_clfe_on(ac97)];
+	val |= surr_shared[ac97->spec.dev_flags][is_shared_surrout(ac97)];
+	val |= clfe_shared[ac97->spec.dev_flags][is_shared_clfeout(ac97)];
+
+	snd_ac97_update_bits(ac97, AC97_CM9761_MULTI_CHAN, 0x3c88, val);
+}
+
+static const struct snd_kcontrol_new snd_ac97_cm9761_controls[] = {
+	AC97_SURROUND_JACK_MODE_CTL,
+	AC97_CHANNEL_MODE_CTL,
+};
+
+static int cm9761_spdif_out_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+	static char *texts[] = { "AC-Link", "ADC", "SPDIF-In" };
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 3;
+	if (uinfo->value.enumerated.item > 2)
+		uinfo->value.enumerated.item = 2;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int cm9761_spdif_out_source_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+
+	if (ac97->regs[AC97_CM9761_FUNC] & 0x1)
+		ucontrol->value.enumerated.item[0] = 2; /* SPDIF-loopback */
+	else if (ac97->regs[AC97_CM9761_SPDIF_CTRL] & 0x2)
+		ucontrol->value.enumerated.item[0] = 1; /* ADC loopback */
+	else
+		ucontrol->value.enumerated.item[0] = 0; /* AC-link */
+	return 0;
+}
+
+static int cm9761_spdif_out_source_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+
+	if (ucontrol->value.enumerated.item[0] == 2)
+		return snd_ac97_update_bits(ac97, AC97_CM9761_FUNC, 0x1, 0x1);
+	snd_ac97_update_bits(ac97, AC97_CM9761_FUNC, 0x1, 0);
+	return snd_ac97_update_bits(ac97, AC97_CM9761_SPDIF_CTRL, 0x2,
+				    ucontrol->value.enumerated.item[0] == 1 ? 0x2 : 0);
+}
+
+static const char *cm9761_dac_clock[] = { "AC-Link", "SPDIF-In", "Both" };
+static const struct ac97_enum cm9761_dac_clock_enum =
+	AC97_ENUM_SINGLE(AC97_CM9761_SPDIF_CTRL, 9, 3, cm9761_dac_clock);
+
+static const struct snd_kcontrol_new snd_ac97_cm9761_controls_spdif[] = {
+	{ /* BIT 1: SPDIFS */
+		.iface	= SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name	= SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
+		.info = cm9761_spdif_out_source_info,
+		.get = cm9761_spdif_out_source_get,
+		.put = cm9761_spdif_out_source_put,
+	},
+	/* BIT 2: IG_SPIV */
+	AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",CAPTURE,NONE) "Valid Switch", AC97_CM9761_SPDIF_CTRL, 2, 1, 0),
+	/* BIT 3: SPI2F */
+	AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",CAPTURE,NONE) "Monitor", AC97_CM9761_SPDIF_CTRL, 3, 1, 0),
+	/* BIT 4: SPI2SDI */
+	AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",CAPTURE,SWITCH), AC97_CM9761_SPDIF_CTRL, 4, 1, 0),
+	/* BIT 9-10: DAC_CTL */
+	AC97_ENUM("DAC Clock Source", cm9761_dac_clock_enum),
+};
+
+static int patch_cm9761_post_spdif(struct snd_ac97 * ac97)
+{
+	return patch_build_controls(ac97, snd_ac97_cm9761_controls_spdif, ARRAY_SIZE(snd_ac97_cm9761_controls_spdif));
+}
+
+static int patch_cm9761_specific(struct snd_ac97 * ac97)
+{
+	return patch_build_controls(ac97, snd_ac97_cm9761_controls, ARRAY_SIZE(snd_ac97_cm9761_controls));
+}
+
+static struct snd_ac97_build_ops patch_cm9761_ops = {
+	.build_specific	= patch_cm9761_specific,
+	.build_post_spdif = patch_cm9761_post_spdif,
+	.update_jacks = cm9761_update_jacks
+};
+
+int patch_cm9761(struct snd_ac97 *ac97)
+{
+	unsigned short val;
+
+	/* CM9761 has no PCM volume although the register reacts */
+	/* Master volume seems to have _some_ influence on the analog
+	 * input sounds
+	 */
+	ac97->flags |= /*AC97_HAS_NO_MASTER_VOL |*/ AC97_HAS_NO_PCM_VOL;
+	snd_ac97_write_cache(ac97, AC97_MASTER, 0x8808);
+	snd_ac97_write_cache(ac97, AC97_PCM, 0x8808);
+
+	ac97->spec.dev_flags = 0; /* 1 = model 82 revision B, 2 = model 83 */
+	if (ac97->id == AC97_ID_CM9761_82) {
+		unsigned short tmp;
+		/* check page 1, reg 0x60 */
+		val = snd_ac97_read(ac97, AC97_INT_PAGING);
+		snd_ac97_write_cache(ac97, AC97_INT_PAGING, (val & ~0x0f) | 0x01);
+		tmp = snd_ac97_read(ac97, 0x60);
+		ac97->spec.dev_flags = tmp & 1; /* revision B? */
+		snd_ac97_write_cache(ac97, AC97_INT_PAGING, val);
+	} else if (ac97->id == AC97_ID_CM9761_83)
+		ac97->spec.dev_flags = 2;
+
+	ac97->build_ops = &patch_cm9761_ops;
+
+	/* enable spdif */
+	/* force the SPDIF bit in ext_id - codec doesn't set this bit! */
+        ac97->ext_id |= AC97_EI_SPDIF;
+	/* to be sure: we overwrite the ext status bits */
+	snd_ac97_write_cache(ac97, AC97_EXTENDED_STATUS, 0x05c0);
+	/* Don't set 0x0200 here.  This results in the silent analog output */
+	snd_ac97_write_cache(ac97, AC97_CM9761_SPDIF_CTRL, 0x0001); /* enable spdif-in */
+	ac97->rates[AC97_RATES_SPDIF] = SNDRV_PCM_RATE_48000; /* 48k only */
+
+	/* set-up multi channel */
+	/* bit 15: pc master beep off
+	 * bit 14: pin47 = EAPD/SPDIF
+	 * bit 13: vref ctl [= cm9739]
+	 * bit 12: CLFE control (reverted on rev B)
+	 * bit 11: Mic/center share (reverted on rev B)
+	 * bit 10: suddound/line share
+	 * bit  9: Analog-in mix -> surround
+	 * bit  8: Analog-in mix -> CLFE
+	 * bit  7: Mic/LFE share (mic/center/lfe)
+	 * bit  5: vref select (9761A)
+	 * bit  4: front control
+	 * bit  3: surround control (revereted with rev B)
+	 * bit  2: front mic
+	 * bit  1: stereo mic
+	 * bit  0: mic boost level (0=20dB, 1=30dB)
+	 */
+
+#if 0
+	if (ac97->spec.dev_flags)
+		val = 0x0214;
+	else
+		val = 0x321c;
+#endif
+	val = snd_ac97_read(ac97, AC97_CM9761_MULTI_CHAN);
+	val |= (1 << 4); /* front on */
+	snd_ac97_write_cache(ac97, AC97_CM9761_MULTI_CHAN, val);
+
+	/* FIXME: set up GPIO */
+	snd_ac97_write_cache(ac97, 0x70, 0x0100);
+	snd_ac97_write_cache(ac97, 0x72, 0x0020);
+
+	return 0;
+}
+
+#define AC97_CM9780_SIDE	0x60
+#define AC97_CM9780_JACK	0x62
+#define AC97_CM9780_MIXER	0x64
+#define AC97_CM9780_MULTI_CHAN	0x66
+#define AC97_CM9780_SPDIF	0x6c
+
+static const char *cm9780_ch_select[] = { "Front", "Side", "Center/LFE", "Rear" };
+static const struct ac97_enum cm9780_ch_select_enum =
+	AC97_ENUM_SINGLE(AC97_CM9780_MULTI_CHAN, 6, 4, cm9780_ch_select);
+static const struct snd_kcontrol_new cm9780_controls[] = {
+	AC97_DOUBLE("Side Playback Switch", AC97_CM9780_SIDE, 15, 7, 1, 1),
+	AC97_DOUBLE("Side Playback Volume", AC97_CM9780_SIDE, 8, 0, 31, 0),
+	AC97_ENUM("Side Playback Route", cm9780_ch_select_enum),
+};
+
+static int patch_cm9780_specific(struct snd_ac97 *ac97)
+{
+	return patch_build_controls(ac97, cm9780_controls, ARRAY_SIZE(cm9780_controls));
+}
+
+static struct snd_ac97_build_ops patch_cm9780_ops = {
+	.build_specific	= patch_cm9780_specific,
+	.build_post_spdif = patch_cm9761_post_spdif	/* identical with CM9761 */
+};
+
+int patch_cm9780(struct snd_ac97 *ac97)
+{
+	unsigned short val;
+
+	ac97->build_ops = &patch_cm9780_ops;
+
+	/* enable spdif */
+	if (ac97->ext_id & AC97_EI_SPDIF) {
+		ac97->rates[AC97_RATES_SPDIF] = SNDRV_PCM_RATE_48000; /* 48k only */
+		val = snd_ac97_read(ac97, AC97_CM9780_SPDIF);
+		val |= 0x1; /* SPDI_EN */
+		snd_ac97_write_cache(ac97, AC97_CM9780_SPDIF, val);
+	}
+
+	return 0;
+}
+
+/*
+ * VIA VT1616 codec
+ */
+static const struct snd_kcontrol_new snd_ac97_controls_vt1616[] = {
+AC97_SINGLE("DC Offset removal", 0x5a, 10, 1, 0),
+AC97_SINGLE("Alternate Level to Surround Out", 0x5a, 15, 1, 0),
+AC97_SINGLE("Downmix LFE and Center to Front", 0x5a, 12, 1, 0),
+AC97_SINGLE("Downmix Surround to Front", 0x5a, 11, 1, 0),
+};
+
+static int patch_vt1616_specific(struct snd_ac97 * ac97)
+{
+	int err;
+
+	if (snd_ac97_try_bit(ac97, 0x5a, 9))
+		if ((err = patch_build_controls(ac97, &snd_ac97_controls_vt1616[0], 1)) < 0)
+			return err;
+	if ((err = patch_build_controls(ac97, &snd_ac97_controls_vt1616[1], ARRAY_SIZE(snd_ac97_controls_vt1616) - 1)) < 0)
+		return err;
+	return 0;
+}
+
+static struct snd_ac97_build_ops patch_vt1616_ops = {
+	.build_specific	= patch_vt1616_specific
+};
+
+int patch_vt1616(struct snd_ac97 * ac97)
+{
+	ac97->build_ops = &patch_vt1616_ops;
+	return 0;
+}
+
+/*
+ * VT1617A codec
+ */
+int patch_vt1617a(struct snd_ac97 * ac97)
+{
+	/* bring analog power consumption to normal, like WinXP driver
+	 * for EPIA SP
+	 */
+	snd_ac97_write_cache(ac97, 0x5c, 0x20);
+	ac97->ext_id |= AC97_EI_SPDIF;	/* force the detection of spdif */
+        ac97->rates[AC97_RATES_SPDIF] = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000;
+        ac97->build_ops = &patch_vt1616_ops;
+	return 0;
+}
+
+/*
+ */
+static void it2646_update_jacks(struct snd_ac97 *ac97)
+{
+    /* shared Line-In / Surround Out */
+    snd_ac97_update_bits(ac97, 0x76, 1 << 9,
+			     is_shared_surrout(ac97) ? (1<<9) : 0);
+	/* shared Mic / Center/LFE Out */
+
+	snd_ac97_update_bits(ac97, 0x76, 1 << 10,
+			     is_shared_clfeout(ac97) ? (1<<10) : 0);
+}
+
+static const struct snd_kcontrol_new snd_ac97_controls_it2646[] = {
+	AC97_SURROUND_JACK_MODE_CTL,
+	AC97_CHANNEL_MODE_CTL,
+};
+
+static const struct snd_kcontrol_new snd_ac97_spdif_controls_it2646[] = {
+	AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",CAPTURE,SWITCH), 0x76, 11, 1, 0),
+	AC97_SINGLE("Analog to IEC958 Output", 0x76, 12, 1, 0),
+	AC97_SINGLE("IEC958 Input Monitor", 0x76, 13, 1, 0),
+};
+
+static int patch_it2646_specific(struct snd_ac97 * ac97)
+{
+	int err;
+	if ((err = patch_build_controls(ac97, snd_ac97_controls_it2646, ARRAY_SIZE(snd_ac97_controls_it2646))) < 0)
+		return err;
+	if ((err = patch_build_controls(ac97, snd_ac97_spdif_controls_it2646, ARRAY_SIZE(snd_ac97_spdif_controls_it2646))) < 0)
+		return err;
+	return 0;
+}
+
+static struct snd_ac97_build_ops patch_it2646_ops = {
+	.build_specific	= patch_it2646_specific,
+	.update_jacks = it2646_update_jacks
+};
+
+int patch_it2646(struct snd_ac97 * ac97)
+{
+	ac97->build_ops = &patch_it2646_ops;
+	/* full DAC volume */
+	snd_ac97_write_cache(ac97, 0x5E, 0x0808);
+	snd_ac97_write_cache(ac97, 0x7A, 0x0808);
+	return 0;
+}
+
+/*
+ * Si3036 codec
+ */
+
+#define AC97_SI3036_CHIP_ID     0x5a
+#define AC97_SI3036_LINE_CFG    0x5c
+
+static const struct snd_kcontrol_new snd_ac97_controls_si3036[] = {
+AC97_DOUBLE("Modem Speaker Volume", 0x5c, 14, 12, 3, 1)
+};
+
+static int patch_si3036_specific(struct snd_ac97 * ac97)
+{
+	int idx, err;
+	for (idx = 0; idx < ARRAY_SIZE(snd_ac97_controls_si3036); idx++)
+		if ((err = snd_ctl_add(ac97->bus->card, snd_ctl_new1(&snd_ac97_controls_si3036[idx], ac97))) < 0)
+			return err;
+	return 0;
+}
+
+static struct snd_ac97_build_ops patch_si3036_ops = {
+	.build_specific	= patch_si3036_specific,
+};
+
+int mpatch_si3036(struct snd_ac97 * ac97)
+{
+	ac97->build_ops = &patch_si3036_ops;
+	snd_ac97_write_cache(ac97, 0x5c, 0xf210 );
+	snd_ac97_write_cache(ac97, 0x68, 0);
+	return 0;
+}
+
+/*
+ * LM 4550 Codec
+ *
+ * We use a static resolution table since LM4550 codec cannot be
+ * properly autoprobed to determine the resolution via
+ * check_volume_resolution().
+ */
+
+static struct snd_ac97_res_table lm4550_restbl[] = {
+    { AC97_MASTER, 0x1f1f },
+    { AC97_HEADPHONE, 0x1f1f },
+    { AC97_MASTER_MONO, 0x001f },
+    { AC97_PC_BEEP, 0x001f },       /* LSB is ignored */
+    { AC97_PHONE, 0x001f },
+    { AC97_MIC, 0x001f },
+    { AC97_LINE, 0x1f1f },
+    { AC97_CD, 0x1f1f },
+    { AC97_VIDEO, 0x1f1f },
+    { AC97_AUX, 0x1f1f },
+    { AC97_PCM, 0x1f1f },
+    { AC97_REC_GAIN, 0x0f0f },
+    {0} /* terminator */
+};
+
+int patch_lm4550(struct snd_ac97 *ac97)
+{
+    ac97->res_table = lm4550_restbl;
+    return 0;
+}
+
+/*
+ *  UCB1400 codec (http://www.semiconductors.philips.com/acrobat_download/datasheets/UCB1400-02.pdf)
+ */
+static const struct snd_kcontrol_new snd_ac97_controls_ucb1400[] = {
+    /* enable/disable headphone driver which allows direct connection to
+     stereo headphone without the use of external DC blocking
+     capacitors */
+    AC97_SINGLE("Headphone Driver", 0x6a, 6, 1, 0),
+    /* Filter used to compensate the DC offset is added in the ADC to remove idle
+     tones from the audio band. */
+    AC97_SINGLE("DC Filter", 0x6a, 4, 1, 0),
+    /* Control smart-low-power mode feature. Allows automatic power down
+     of unused blocks in the ADC analog front end and the PLL. */
+    AC97_SINGLE("Smart Low Power Mode", 0x6c, 4, 3, 0),
+};
+
+static int patch_ucb1400_specific(struct snd_ac97 * ac97)
+{
+    int idx, err;
+    for (idx = 0; idx < ARRAY_SIZE(snd_ac97_controls_ucb1400); idx++)
+        if ((err = snd_ctl_add(ac97->bus->card, snd_ctl_new1(&snd_ac97_controls_ucb1400[idx], ac97))) < 0)
+            return err;
+    return 0;
+}
+
+static struct snd_ac97_build_ops patch_ucb1400_ops = {
+    .build_specific	= patch_ucb1400_specific,
+};
+
+int patch_ucb1400(struct snd_ac97 * ac97)
+{
+    ac97->build_ops = &patch_ucb1400_ops;
+    /* enable headphone driver and smart low power mode by default */
+    snd_ac97_write(ac97, 0x6a, 0x0050);
+    snd_ac97_write(ac97, 0x6c, 0x0030);
+    return 0;
+}
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/ac97/ac97_patch.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/ac97/ac97_patch.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/ac97/ac97_patch.h	(revision 91)
@@ -0,0 +1,64 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  Universal interface for Audio Codec '97
+ *
+ *  For more details look to AC '97 component specification revision 2.2
+ *  by Intel Corporation (http://developer.intel.com).
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+int patch_yamaha_ymf753(struct snd_ac97 * ac97);
+int patch_wolfson00(struct snd_ac97 * ac97);
+int patch_wolfson03(struct snd_ac97 * ac97);
+int patch_wolfson04(struct snd_ac97 * ac97);
+int patch_wolfson05(struct snd_ac97 * ac97);
+int patch_wolfson11(struct snd_ac97 * ac97);
+int patch_wolfson13(struct snd_ac97 * ac97);
+int patch_tritech_tr28028(struct snd_ac97 * ac97);
+int patch_sigmatel_stac9700(struct snd_ac97 * ac97);
+int patch_sigmatel_stac9708(struct snd_ac97 * ac97);
+int patch_sigmatel_stac9721(struct snd_ac97 * ac97);
+int patch_sigmatel_stac9744(struct snd_ac97 * ac97);
+int patch_sigmatel_stac9756(struct snd_ac97 * ac97);
+int patch_sigmatel_stac9758(struct snd_ac97 * ac97);
+int patch_cirrus_cs4299(struct snd_ac97 * ac97);
+int patch_cirrus_spdif(struct snd_ac97 * ac97);
+int patch_conexant(struct snd_ac97 * ac97);
+int patch_ad1819(struct snd_ac97 * ac97);
+int patch_ad1881(struct snd_ac97 * ac97);
+int patch_ad1885(struct snd_ac97 * ac97);
+int patch_ad1886(struct snd_ac97 * ac97);
+int patch_ad1888(struct snd_ac97 * ac97);
+int patch_ad1980(struct snd_ac97 * ac97);
+int patch_ad1981a(struct snd_ac97 * ac97);
+int patch_ad1981b(struct snd_ac97 * ac97);
+int patch_ad1985(struct snd_ac97 * ac97);
+int patch_ad1986(struct snd_ac97 * ac97);
+int patch_alc650(struct snd_ac97 * ac97);
+int patch_alc655(struct snd_ac97 * ac97);
+int patch_alc850(struct snd_ac97 * ac97);
+int patch_cm9738(struct snd_ac97 * ac97);
+int patch_cm9739(struct snd_ac97 * ac97);
+int patch_cm9761(struct snd_ac97 * ac97);
+int patch_cm9780(struct snd_ac97 * ac97);
+int patch_vt1616(struct snd_ac97 * ac97);
+int patch_vt1617a(struct snd_ac97 * ac97);
+int patch_it2646(struct snd_ac97 * ac97);
+int patch_ucb1400(struct snd_ac97 * ac97);
+int mpatch_si3036(struct snd_ac97 * ac97);
+int patch_lm4550(struct snd_ac97 * ac97);
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/ac97/ac97_pcm.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/ac97/ac97_pcm.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/ac97/ac97_pcm.c	(revision 91)
@@ -0,0 +1,727 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  Universal interface for Audio Codec '97
+ *
+ *  For more details look to AC '97 component specification revision 2.2
+ *  by Intel Corporation (http://developer.intel.com) and to datasheets
+ *  for specific codecs.
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/control.h>
+#include <sound/ac97_codec.h>
+#include <sound/asoundef.h>
+#include "ac97_patch.h"
+#include "ac97_id.h"
+#include "ac97_local.h"
+
+/*
+ *  PCM support
+ */
+
+static unsigned char rate_reg_tables[2][4][9] = {
+{
+  /* standard rates */
+  {
+  	/* 3&4 front, 7&8 rear, 6&9 center/lfe */
+	AC97_PCM_FRONT_DAC_RATE,	/* slot 3 */
+	AC97_PCM_FRONT_DAC_RATE,	/* slot 4 */
+	0xff,				/* slot 5 */
+	AC97_PCM_LFE_DAC_RATE,		/* slot 6 */
+	AC97_PCM_SURR_DAC_RATE,		/* slot 7 */
+	AC97_PCM_SURR_DAC_RATE,		/* slot 8 */
+	AC97_PCM_LFE_DAC_RATE,		/* slot 9 */
+	0xff,				/* slot 10 */
+	0xff,				/* slot 11 */
+  },
+  {
+  	/* 7&8 front, 6&9 rear, 10&11 center/lfe */
+	0xff,				/* slot 3 */
+	0xff,				/* slot 4 */
+	0xff,				/* slot 5 */
+	AC97_PCM_SURR_DAC_RATE,		/* slot 6 */
+	AC97_PCM_FRONT_DAC_RATE,	/* slot 7 */
+	AC97_PCM_FRONT_DAC_RATE,	/* slot 8 */
+	AC97_PCM_SURR_DAC_RATE,		/* slot 9 */
+	AC97_PCM_LFE_DAC_RATE,		/* slot 10 */
+	AC97_PCM_LFE_DAC_RATE,		/* slot 11 */
+  },
+  {
+  	/* 6&9 front, 10&11 rear, 3&4 center/lfe */
+	AC97_PCM_LFE_DAC_RATE,		/* slot 3 */
+	AC97_PCM_LFE_DAC_RATE,		/* slot 4 */
+	0xff,				/* slot 5 */
+	AC97_PCM_FRONT_DAC_RATE,	/* slot 6 */
+	0xff,				/* slot 7 */
+	0xff,				/* slot 8 */
+	AC97_PCM_FRONT_DAC_RATE,	/* slot 9 */
+	AC97_PCM_SURR_DAC_RATE,		/* slot 10 */
+	AC97_PCM_SURR_DAC_RATE,		/* slot 11 */
+  },
+  {
+  	/* 10&11 front, 3&4 rear, 7&8 center/lfe */
+	AC97_PCM_SURR_DAC_RATE,		/* slot 3 */
+	AC97_PCM_SURR_DAC_RATE,		/* slot 4 */
+	0xff,				/* slot 5 */
+	0xff,				/* slot 6 */
+	AC97_PCM_LFE_DAC_RATE,		/* slot 7 */
+	AC97_PCM_LFE_DAC_RATE,		/* slot 8 */
+	0xff,				/* slot 9 */
+	AC97_PCM_FRONT_DAC_RATE,	/* slot 10 */
+	AC97_PCM_FRONT_DAC_RATE,	/* slot 11 */
+  },
+},
+{
+  /* double rates */
+  {
+  	/* 3&4 front, 7&8 front (t+1) */
+	AC97_PCM_FRONT_DAC_RATE,	/* slot 3 */
+	AC97_PCM_FRONT_DAC_RATE,	/* slot 4 */
+	0xff,				/* slot 5 */
+	0xff,				/* slot 6 */
+	AC97_PCM_FRONT_DAC_RATE,	/* slot 7 */
+	AC97_PCM_FRONT_DAC_RATE,	/* slot 8 */
+	0xff,				/* slot 9 */
+	0xff,				/* slot 10 */
+	0xff,				/* slot 11 */
+  },
+  {
+	/* not specified in the specification */
+	0xff,				/* slot 3 */
+	0xff,				/* slot 4 */
+	0xff,				/* slot 5 */
+	0xff,				/* slot 6 */
+	0xff,				/* slot 7 */
+	0xff,				/* slot 8 */
+	0xff,				/* slot 9 */
+	0xff,				/* slot 10 */
+	0xff,				/* slot 11 */
+  },
+  {
+	0xff,				/* slot 3 */
+	0xff,				/* slot 4 */
+	0xff,				/* slot 5 */
+	0xff,				/* slot 6 */
+	0xff,				/* slot 7 */
+	0xff,				/* slot 8 */
+	0xff,				/* slot 9 */
+	0xff,				/* slot 10 */
+	0xff,				/* slot 11 */
+  },
+  {
+	0xff,				/* slot 3 */
+	0xff,				/* slot 4 */
+	0xff,				/* slot 5 */
+	0xff,				/* slot 6 */
+	0xff,				/* slot 7 */
+	0xff,				/* slot 8 */
+	0xff,				/* slot 9 */
+	0xff,				/* slot 10 */
+	0xff,				/* slot 11 */
+  }
+}};
+
+/* FIXME: more various mappings for ADC? */
+static unsigned char rate_cregs[9] = {
+	AC97_PCM_LR_ADC_RATE,	/* 3 */
+	AC97_PCM_LR_ADC_RATE,	/* 4 */
+	0xff,			/* 5 */
+	AC97_PCM_MIC_ADC_RATE,	/* 6 */
+	0xff,			/* 7 */
+	0xff,			/* 8 */
+	0xff,			/* 9 */
+	0xff,			/* 10 */
+	0xff,			/* 11 */
+};
+
+static unsigned char get_slot_reg(struct ac97_pcm *pcm, unsigned short cidx,
+				  unsigned short slot, int dbl)
+{
+	if (slot < 3)
+		return 0xff;
+	if (slot > 11)
+		return 0xff;
+	if (pcm->spdif)
+		return AC97_SPDIF; /* pseudo register */
+	if (pcm->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		return rate_reg_tables[dbl][pcm->r[dbl].rate_table[cidx]][slot - 3];
+	else
+		return rate_cregs[slot - 3];
+}
+
+static int set_spdif_rate(struct snd_ac97 *ac97, unsigned short rate)
+{
+	unsigned short old, bits, reg, mask;
+	unsigned int sbits;
+
+	if (! (ac97->ext_id & AC97_EI_SPDIF))
+		return -ENODEV;
+
+	/* TODO: double rate support */
+	if (ac97->flags & AC97_CS_SPDIF) {
+		switch (rate) {
+		case 48000: bits = 0; break;
+		case 44100: bits = 1 << AC97_SC_SPSR_SHIFT; break;
+		default: /* invalid - disable output */
+			snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, 0);
+			return -EINVAL;
+		}
+		reg = AC97_CSR_SPDIF;
+		mask = 1 << AC97_SC_SPSR_SHIFT;
+	} else {
+		if (ac97->id == AC97_ID_CM9739 && rate != 48000) {
+			snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, 0);
+			return -EINVAL;
+		}
+		switch (rate) {
+		case 44100: bits = AC97_SC_SPSR_44K; break;
+		case 48000: bits = AC97_SC_SPSR_48K; break;
+		case 32000: bits = AC97_SC_SPSR_32K; break;
+		default: /* invalid - disable output */
+			snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, 0);
+			return -EINVAL;
+		}
+		reg = AC97_SPDIF;
+		mask = AC97_SC_SPSR_MASK;
+	}
+
+	down(&ac97->reg_mutex);
+	old = snd_ac97_read(ac97, reg) & mask;
+	if (old != bits) {
+		snd_ac97_update_bits_nolock(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, 0);
+		snd_ac97_update_bits_nolock(ac97, reg, mask, bits);
+		/* update the internal spdif bits */
+		sbits = ac97->spdif_status;
+		if (sbits & IEC958_AES0_PROFESSIONAL) {
+			sbits &= ~IEC958_AES0_PRO_FS;
+			switch (rate) {
+			case 44100: sbits |= IEC958_AES0_PRO_FS_44100; break;
+			case 48000: sbits |= IEC958_AES0_PRO_FS_48000; break;
+			case 32000: sbits |= IEC958_AES0_PRO_FS_32000; break;
+			}
+		} else {
+			sbits &= ~(IEC958_AES3_CON_FS << 24);
+			switch (rate) {
+			case 44100: sbits |= IEC958_AES3_CON_FS_44100<<24; break;
+			case 48000: sbits |= IEC958_AES3_CON_FS_48000<<24; break;
+			case 32000: sbits |= IEC958_AES3_CON_FS_32000<<24; break;
+			}
+		}
+		ac97->spdif_status = sbits;
+	}
+	snd_ac97_update_bits_nolock(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, AC97_EA_SPDIF);
+	up(&ac97->reg_mutex);
+	return 0;
+}
+
+/**
+ * snd_ac97_set_rate - change the rate of the given input/output.
+ * @ac97: the ac97 instance
+ * @reg: the register to change
+ * @rate: the sample rate to set
+ *
+ * Changes the rate of the given input/output on the codec.
+ * If the codec doesn't support VAR, the rate must be 48000 (except
+ * for SPDIF).
+ *
+ * The valid registers are AC97_PMC_MIC_ADC_RATE,
+ * AC97_PCM_FRONT_DAC_RATE, AC97_PCM_LR_ADC_RATE.
+ * AC97_PCM_SURR_DAC_RATE and AC97_PCM_LFE_DAC_RATE are accepted
+ * if the codec supports them.
+ * AC97_SPDIF is accepted as a pseudo register to modify the SPDIF
+ * status bits.
+ *
+ * Returns zero if successful, or a negative error code on failure.
+ */
+int snd_ac97_set_rate(struct snd_ac97 *ac97, int reg, unsigned int rate)
+{
+	int dbl;
+	unsigned int tmp;
+
+	dbl = rate > 48000;
+	if (dbl) {
+		if (!(ac97->flags & AC97_DOUBLE_RATE))
+			return -EINVAL;
+		if (reg != AC97_PCM_FRONT_DAC_RATE)
+			return -EINVAL;
+	}
+
+        snd_ac97_update_power(ac97, reg, 1);
+	switch (reg) {
+	case AC97_PCM_MIC_ADC_RATE:
+		if ((ac97->regs[AC97_EXTENDED_STATUS] & AC97_EA_VRM) == 0)	/* MIC VRA */
+			if (rate != 48000)
+				return -EINVAL;
+		break;
+	case AC97_PCM_FRONT_DAC_RATE:
+	case AC97_PCM_LR_ADC_RATE:
+		if ((ac97->regs[AC97_EXTENDED_STATUS] & AC97_EA_VRA) == 0)	/* VRA */
+			if (rate != 48000 && rate != 96000)
+				return -EINVAL;
+		break;
+	case AC97_PCM_SURR_DAC_RATE:
+		if (! (ac97->scaps & AC97_SCAP_SURROUND_DAC))
+			return -EINVAL;
+		break;
+	case AC97_PCM_LFE_DAC_RATE:
+		if (! (ac97->scaps & AC97_SCAP_CENTER_LFE_DAC))
+			return -EINVAL;
+		break;
+	case AC97_SPDIF:
+		/* special case */
+		return set_spdif_rate(ac97, rate);
+	default:
+		return -EINVAL;
+	}
+	if (dbl)
+		rate /= 2;
+	tmp = (rate * ac97->bus->clock) / 48000;
+	if (tmp > 65535)
+		return -EINVAL;
+	if ((ac97->ext_id & AC97_EI_DRA) && reg == AC97_PCM_FRONT_DAC_RATE)
+		snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS,
+				     AC97_EA_DRA, dbl ? AC97_EA_DRA : 0);
+	snd_ac97_update(ac97, reg, tmp & 0xffff);
+	snd_ac97_read(ac97, reg);
+	if ((ac97->ext_id & AC97_EI_DRA) && reg == AC97_PCM_FRONT_DAC_RATE) {
+		/* Intel controllers require double rate data to be put in
+		 * slots 7+8
+		 */
+		snd_ac97_update_bits(ac97, AC97_GENERAL_PURPOSE,
+				     AC97_GP_DRSS_MASK,
+				     dbl ? AC97_GP_DRSS_78 : 0);
+		snd_ac97_read(ac97, AC97_GENERAL_PURPOSE);
+	}
+	return 0;
+}
+
+static unsigned short get_pslots(struct snd_ac97 *ac97, unsigned char *rate_table, unsigned short *spdif_slots)
+{
+	if (!ac97_is_audio(ac97))
+		return 0;
+	if (ac97_is_rev22(ac97) || ac97_can_amap(ac97)) {
+		unsigned short slots = 0;
+		if (ac97_is_rev22(ac97)) {
+			/* Note: it's simply emulation of AMAP behaviour */
+			u16 es;
+			es = ac97->regs[AC97_EXTENDED_ID] &= ~AC97_EI_DACS_SLOT_MASK;
+			switch (ac97->addr) {
+			case 1:
+			case 2: es |= (1<<AC97_EI_DACS_SLOT_SHIFT); break;
+			case 3: es |= (2<<AC97_EI_DACS_SLOT_SHIFT); break;
+			}
+			snd_ac97_write_cache(ac97, AC97_EXTENDED_ID, es);
+		}
+		switch (ac97->addr) {
+		case 0:
+			slots |= (1<<AC97_SLOT_PCM_LEFT)|(1<<AC97_SLOT_PCM_RIGHT);
+			if (ac97->scaps & AC97_SCAP_SURROUND_DAC)
+				slots |= (1<<AC97_SLOT_PCM_SLEFT)|(1<<AC97_SLOT_PCM_SRIGHT);
+			if (ac97->scaps & AC97_SCAP_CENTER_LFE_DAC)
+				slots |= (1<<AC97_SLOT_PCM_CENTER)|(1<<AC97_SLOT_LFE);
+			if (ac97->ext_id & AC97_EI_SPDIF) {
+				if (!(ac97->scaps & AC97_SCAP_SURROUND_DAC))
+					*spdif_slots = (1<<AC97_SLOT_SPDIF_LEFT)|(1<<AC97_SLOT_SPDIF_RIGHT);
+				else if (!(ac97->scaps & AC97_SCAP_CENTER_LFE_DAC))
+					*spdif_slots = (1<<AC97_SLOT_SPDIF_LEFT1)|(1<<AC97_SLOT_SPDIF_RIGHT1);
+				else
+					*spdif_slots = (1<<AC97_SLOT_SPDIF_LEFT2)|(1<<AC97_SLOT_SPDIF_RIGHT2);
+			}
+			*rate_table = 0;
+			break;
+		case 1:
+		case 2:
+			slots |= (1<<AC97_SLOT_PCM_SLEFT)|(1<<AC97_SLOT_PCM_SRIGHT);
+			if (ac97->scaps & AC97_SCAP_SURROUND_DAC)
+				slots |= (1<<AC97_SLOT_PCM_CENTER)|(1<<AC97_SLOT_LFE);
+			if (ac97->ext_id & AC97_EI_SPDIF) {
+				if (!(ac97->scaps & AC97_SCAP_SURROUND_DAC))
+					*spdif_slots = (1<<AC97_SLOT_SPDIF_LEFT1)|(1<<AC97_SLOT_SPDIF_RIGHT1);
+				else
+					*spdif_slots = (1<<AC97_SLOT_SPDIF_LEFT2)|(1<<AC97_SLOT_SPDIF_RIGHT2);
+			}
+			*rate_table = 1;
+			break;
+		case 3:
+			slots |= (1<<AC97_SLOT_PCM_CENTER)|(1<<AC97_SLOT_LFE);
+			if (ac97->ext_id & AC97_EI_SPDIF)
+				*spdif_slots = (1<<AC97_SLOT_SPDIF_LEFT2)|(1<<AC97_SLOT_SPDIF_RIGHT2);
+			*rate_table = 2;
+			break;
+		}
+		return slots;
+	} else {
+		unsigned short slots;
+		slots = (1<<AC97_SLOT_PCM_LEFT)|(1<<AC97_SLOT_PCM_RIGHT);
+		if (ac97->scaps & AC97_SCAP_SURROUND_DAC)
+			slots |= (1<<AC97_SLOT_PCM_SLEFT)|(1<<AC97_SLOT_PCM_SRIGHT);
+		if (ac97->scaps & AC97_SCAP_CENTER_LFE_DAC)
+			slots |= (1<<AC97_SLOT_PCM_CENTER)|(1<<AC97_SLOT_LFE);
+		if (ac97->ext_id & AC97_EI_SPDIF) {
+			if (!(ac97->scaps & AC97_SCAP_SURROUND_DAC))
+				*spdif_slots = (1<<AC97_SLOT_SPDIF_LEFT)|(1<<AC97_SLOT_SPDIF_RIGHT);
+			else if (!(ac97->scaps & AC97_SCAP_CENTER_LFE_DAC))
+				*spdif_slots = (1<<AC97_SLOT_SPDIF_LEFT1)|(1<<AC97_SLOT_SPDIF_RIGHT1);
+			else
+				*spdif_slots = (1<<AC97_SLOT_SPDIF_LEFT2)|(1<<AC97_SLOT_SPDIF_RIGHT2);
+		}
+		*rate_table = 0;
+		return slots;
+	}
+}
+
+static unsigned short get_cslots(struct snd_ac97 *ac97)
+{
+	unsigned short slots;
+
+	if (!ac97_is_audio(ac97))
+		return 0;
+	slots = (1<<AC97_SLOT_PCM_LEFT)|(1<<AC97_SLOT_PCM_RIGHT);
+	slots |= (1<<AC97_SLOT_MIC);
+	return slots;
+}
+
+static unsigned int get_rates(struct ac97_pcm *pcm, unsigned int cidx, unsigned short slots, int dbl)
+{
+	int i, idx;
+	unsigned int rates = ~0;
+	unsigned char reg;
+
+	for (i = 3; i < 12; i++) {
+		if (!(slots & (1 << i)))
+			continue;
+		reg = get_slot_reg(pcm, cidx, i, dbl);
+		switch (reg) {
+		case AC97_PCM_FRONT_DAC_RATE:	idx = AC97_RATES_FRONT_DAC; break;
+		case AC97_PCM_SURR_DAC_RATE:	idx = AC97_RATES_SURR_DAC; break;
+		case AC97_PCM_LFE_DAC_RATE:	idx = AC97_RATES_LFE_DAC; break;
+		case AC97_PCM_LR_ADC_RATE:	idx = AC97_RATES_ADC; break;
+		case AC97_PCM_MIC_ADC_RATE:	idx = AC97_RATES_MIC_ADC; break;
+		default:			idx = AC97_RATES_SPDIF; break;
+		}
+		rates &= pcm->r[dbl].codec[cidx]->rates[idx];
+	}
+	if (!dbl)
+		rates &= ~(SNDRV_PCM_RATE_64000 | SNDRV_PCM_RATE_88200 |
+			   SNDRV_PCM_RATE_96000);
+	return rates;
+}
+
+/**
+ * snd_ac97_pcm_assign - assign AC97 slots to given PCM streams
+ * @bus: the ac97 bus instance
+ * @pcms_count: count of PCMs to be assigned
+ * @pcms: PCMs to be assigned
+ *
+ * It assigns available AC97 slots for given PCMs. If none or only
+ * some slots are available, pcm->xxx.slots and pcm->xxx.rslots[] members
+ * are reduced and might be zero.
+ */
+int snd_ac97_pcm_assign(struct snd_ac97_bus *bus,
+			unsigned short pcms_count,
+			const struct ac97_pcm *pcms)
+{
+	int i, j, k;
+	const struct ac97_pcm *pcm;
+	struct ac97_pcm *rpcms, *rpcm;
+	unsigned short avail_slots[2][4];
+	unsigned char rate_table[2][4];
+	unsigned short tmp, slots;
+	unsigned short spdif_slots[4];
+	unsigned int rates;
+	struct snd_ac97 *codec;
+
+	rpcms = kcalloc(pcms_count, sizeof(struct ac97_pcm), GFP_KERNEL);
+	if (rpcms == NULL)
+		return -ENOMEM;
+	memset(avail_slots, 0, sizeof(avail_slots));
+	memset(rate_table, 0, sizeof(rate_table));
+	memset(spdif_slots, 0, sizeof(spdif_slots));
+	for (i = 0; i < 4; i++) {
+		codec = bus->codec[i];
+		if (!codec)
+			continue;
+		avail_slots[0][i] = get_pslots(codec, &rate_table[0][i], &spdif_slots[i]);
+		avail_slots[1][i] = get_cslots(codec);
+		if (!(codec->scaps & AC97_SCAP_INDEP_SDIN)) {
+			for (j = 0; j < i; j++) {
+				if (bus->codec[j])
+					avail_slots[1][i] &= ~avail_slots[1][j];
+			}
+		}
+	}
+	/* first step - exclusive devices */
+	for (i = 0; i < pcms_count; i++) {
+		pcm = &pcms[i];
+		rpcm = &rpcms[i];
+		/* low-level driver thinks that it's more clever */
+		if (pcm->copy_flag) {
+			*rpcm = *pcm;
+			continue;
+		}
+		rpcm->stream = pcm->stream;
+		rpcm->exclusive = pcm->exclusive;
+		rpcm->spdif = pcm->spdif;
+		rpcm->private_value = pcm->private_value;
+		rpcm->bus = bus;
+		rpcm->rates = ~0;
+		slots = pcm->r[0].slots;
+		for (j = 0; j < 4 && slots; j++) {
+			if (!bus->codec[j])
+				continue;
+			rates = ~0;
+			if (pcm->spdif && pcm->stream == 0)
+				tmp = spdif_slots[j];
+			else
+				tmp = avail_slots[pcm->stream][j];
+			if (pcm->exclusive) {
+				/* exclusive access */
+				tmp &= slots;
+				for (k = 0; k < i; k++) {
+					if (rpcm->stream == rpcms[k].stream)
+						tmp &= ~rpcms[k].r[0].rslots[j];
+				}
+			} else {
+				/* non-exclusive access */
+				tmp &= pcm->r[0].slots;
+			}
+			if (tmp) {
+				rpcm->r[0].rslots[j] = tmp;
+				rpcm->r[0].codec[j] = bus->codec[j];
+				rpcm->r[0].rate_table[j] = rate_table[pcm->stream][j];
+				if (bus->no_vra)
+					rates = SNDRV_PCM_RATE_48000;
+				else
+					rates = get_rates(rpcm, j, tmp, 0);
+				if (pcm->exclusive)
+					avail_slots[pcm->stream][j] &= ~tmp;
+			}
+			slots &= ~tmp;
+			rpcm->r[0].slots |= tmp;
+			rpcm->rates &= rates;
+		}
+		/* for double rate, we check the first codec only */
+		if (pcm->stream == SNDRV_PCM_STREAM_PLAYBACK &&
+		    bus->codec[0] && (bus->codec[0]->flags & AC97_DOUBLE_RATE) &&
+		    rate_table[pcm->stream][0] == 0) {
+			tmp = (1<<AC97_SLOT_PCM_LEFT) | (1<<AC97_SLOT_PCM_RIGHT) |
+			      (1<<AC97_SLOT_PCM_LEFT_0) | (1<<AC97_SLOT_PCM_RIGHT_0);
+			if ((tmp & pcm->r[1].slots) == tmp) {
+				rpcm->r[1].slots = tmp;
+				rpcm->r[1].rslots[0] = tmp;
+				rpcm->r[1].rate_table[0] = 0;
+				rpcm->r[1].codec[0] = bus->codec[0];
+				if (pcm->exclusive)
+					avail_slots[pcm->stream][0] &= ~tmp;
+				if (bus->no_vra)
+					rates = SNDRV_PCM_RATE_96000;
+				else
+					rates = get_rates(rpcm, 0, tmp, 1);
+				rpcm->rates |= rates;
+			}
+		}
+		if (rpcm->rates == ~0)
+			rpcm->rates = 0; /* not used */
+	}
+	bus->pcms_count = pcms_count;
+	bus->pcms = rpcms;
+	return 0;
+}
+
+/**
+ * snd_ac97_pcm_open - opens the given AC97 pcm
+ * @pcm: the ac97 pcm instance
+ * @rate: rate in Hz, if codec does not support VRA, this value must be 48000Hz
+ * @cfg: output stream characteristics
+ * @slots: a subset of allocated slots (snd_ac97_pcm_assign) for this pcm
+ *
+ * It locks the specified slots and sets the given rate to AC97 registers.
+ */
+int snd_ac97_pcm_open(struct ac97_pcm *pcm, unsigned int rate,
+		      enum ac97_pcm_cfg cfg, unsigned short slots)
+{
+	struct snd_ac97_bus *bus;
+	int i, cidx, r, ok_flag;
+	unsigned int reg_ok[4] = {0,0,0,0};
+	unsigned char reg;
+	int err = 0;
+
+	r = rate > 48000;
+	bus = pcm->bus;
+	if (cfg == AC97_PCM_CFG_SPDIF) {
+		int err;
+		for (cidx = 0; cidx < 4; cidx++)
+			if (bus->codec[cidx] && (bus->codec[cidx]->ext_id & AC97_EI_SPDIF)) {
+				err = set_spdif_rate(bus->codec[cidx], rate);
+				if (err < 0)
+					return err;
+			}
+	}
+	spin_lock_irq(&pcm->bus->bus_lock);
+	for (i = 3; i < 12; i++) {
+		if (!(slots & (1 << i)))
+			continue;
+		ok_flag = 0;
+		for (cidx = 0; cidx < 4; cidx++) {
+			if (bus->used_slots[pcm->stream][cidx] & (1 << i)) {
+				spin_unlock_irq(&pcm->bus->bus_lock);
+				err = -EBUSY;
+				goto error;
+			}
+			if (pcm->r[r].rslots[cidx] & (1 << i)) {
+				bus->used_slots[pcm->stream][cidx] |= (1 << i);
+				ok_flag++;
+			}
+		}
+		if (!ok_flag) {
+			spin_unlock_irq(&pcm->bus->bus_lock);
+			snd_printk(KERN_ERR "cannot find configuration for AC97 slot %i\n", i);
+			err = -EAGAIN;
+			goto error;
+		}
+        }
+        pcm->cur_dbl = r;
+	spin_unlock_irq(&pcm->bus->bus_lock);
+	for (i = 3; i < 12; i++) {
+		if (!(slots & (1 << i)))
+			continue;
+		for (cidx = 0; cidx < 4; cidx++) {
+			if (pcm->r[r].rslots[cidx] & (1 << i)) {
+				reg = get_slot_reg(pcm, cidx, i, r);
+				if (reg == 0xff) {
+					snd_printk(KERN_ERR "invalid AC97 slot %i?\n", i);
+					continue;
+				}
+				if (reg_ok[cidx] & (1 << (reg - AC97_PCM_FRONT_DAC_RATE)))
+					continue;
+				//printk(KERN_DEBUG "setting ac97 reg 0x%x to rate %d\n", reg, rate);
+				err = snd_ac97_set_rate(pcm->r[r].codec[cidx], reg, rate);
+				if (err < 0)
+					snd_printk(KERN_ERR "error in snd_ac97_set_rate: cidx=%d, reg=0x%x, rate=%d, err=%d\n", cidx, reg, rate, err);
+				else
+					reg_ok[cidx] |= (1 << (reg - AC97_PCM_FRONT_DAC_RATE));
+			}
+		}
+	}
+	pcm->aslots = slots;
+	return 0;
+
+ error:
+	pcm->aslots = slots;
+	snd_ac97_pcm_close(pcm);
+	return err;
+}
+
+/**
+ * snd_ac97_pcm_close - closes the given AC97 pcm
+ * @pcm: the ac97 pcm instance
+ *
+ * It frees the locked AC97 slots.
+ */
+int snd_ac97_pcm_close(struct ac97_pcm *pcm)
+{
+    struct snd_ac97_bus *bus;
+    unsigned short slots = pcm->aslots;
+    int i, cidx;
+
+#ifdef CONFIG_SND_AC97_POWER_SAVE
+    int r = pcm->cur_dbl;
+    for (i = 3; i < 12; i++) {
+        if (!(slots & (1 << i)))
+            continue;
+        for (cidx = 0; cidx < 4; cidx++) {
+            if (pcm->r[r].rslots[cidx] & (1 << i)) {
+                int reg = get_slot_reg(pcm, cidx, i, r);
+                snd_ac97_update_power(pcm->r[r].codec[cidx],
+                                      reg, 0);
+            }
+        }
+    }
+#endif
+
+    bus = pcm->bus;
+    spin_lock_irq(&pcm->bus->bus_lock);
+    for (i = 3; i < 12; i++) {
+        if (!(slots & (1 << i)))
+            continue;
+        for (cidx = 0; cidx < 4; cidx++)
+            bus->used_slots[pcm->stream][cidx] &= ~(1 << i);
+    }
+    pcm->aslots = 0;
+    pcm->cur_dbl = 0;
+    spin_unlock_irq(&pcm->bus->bus_lock);
+    return 0;
+}
+
+static int double_rate_hw_constraint_rate(struct snd_pcm_hw_params *params,
+					  struct snd_pcm_hw_rule *rule)
+{
+	struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+	if (channels->min > 2) {
+		static const struct snd_interval single_rates = {
+			.min = 1,
+			.max = 48000,
+		};
+		struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+		return snd_interval_refine(rate, &single_rates);
+	}
+	return 0;
+}
+
+static int double_rate_hw_constraint_channels(struct snd_pcm_hw_params *params,
+					      struct snd_pcm_hw_rule *rule)
+{
+	struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+	if (rate->min > 48000) {
+		static const struct snd_interval double_rate_channels = {
+			.min = 2,
+			.max = 2,
+		};
+		struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+		return snd_interval_refine(channels, &double_rate_channels);
+	}
+	return 0;
+}
+
+/**
+ * snd_ac97_pcm_double_rate_rules - set double rate constraints
+ * @runtime: the runtime of the ac97 front playback pcm
+ *
+ * Installs the hardware constraint rules to prevent using double rates and
+ * more than two channels at the same time.
+ */
+int snd_ac97_pcm_double_rate_rules(struct snd_pcm_runtime *runtime)
+{
+	int err;
+
+	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+				  double_rate_hw_constraint_rate, NULL,
+				  SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+	if (err < 0)
+		return err;
+	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+				  double_rate_hw_constraint_channels, NULL,
+				  SNDRV_PCM_HW_PARAM_RATE, -1);
+	return err;
+}
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/ac97/ac97_proc.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/ac97/ac97_proc.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/ac97/ac97_proc.c	(revision 91)
@@ -0,0 +1,491 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  Universal interface for Audio Codec '97
+ *
+ *  For more details look to AC '97 component specification revision 2.2
+ *  by Intel Corporation (http://developer.intel.com).
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/ac97_codec.h>
+#include <sound/asoundef.h>
+#include "ac97_local.h"
+#include "ac97_id.h"
+
+/*
+ * proc interface
+ */
+
+static void snd_ac97_proc_read_functions(struct snd_ac97 *ac97, struct snd_info_buffer *buffer)
+{
+	int header = 0, function;
+	unsigned short info, sense_info;
+	static const char *function_names[12] = {
+		"Master Out", "AUX Out", "Center/LFE Out", "SPDIF Out",
+		"Phone In", "Mic 1", "Mic 2", "Line In", "CD In", "Video In",
+		"Aux In", "Mono Out"
+	};
+	static const char *locations[8] = {
+		"Rear I/O Panel", "Front Panel", "Motherboard", "Dock/External",
+		"reserved", "reserved", "reserved", "NC/unused"
+	};
+
+	for (function = 0; function < 12; ++function) {
+		snd_ac97_write(ac97, AC97_FUNC_SELECT, function << 1);
+		info = snd_ac97_read(ac97, AC97_FUNC_INFO);
+		if (!(info & 0x0001))
+			continue;
+		if (!header) {
+			snd_iprintf(buffer, "\n                    Gain     Inverted  Buffer delay  Location\n");
+			header = 1;
+		}
+		sense_info = snd_ac97_read(ac97, AC97_SENSE_INFO);
+		snd_iprintf(buffer, "%-17s: %3d.%d dBV    %c      %2d/fs         %s\n",
+			    function_names[function],
+			    (info & 0x8000 ? -1 : 1) * ((info & 0x7000) >> 12) * 3 / 2,
+			    ((info & 0x0800) >> 11) * 5,
+			    info & 0x0400 ? 'X' : '-',
+			    (info & 0x03e0) >> 5,
+			    locations[sense_info >> 13]);
+	}
+}
+
+static const char *snd_ac97_stereo_enhancements[] =
+{
+  /*   0 */ "No 3D Stereo Enhancement",
+  /*   1 */ "Analog Devices Phat Stereo",
+  /*   2 */ "Creative Stereo Enhancement",
+  /*   3 */ "National Semi 3D Stereo Enhancement",
+  /*   4 */ "YAMAHA Ymersion",
+  /*   5 */ "BBE 3D Stereo Enhancement",
+  /*   6 */ "Crystal Semi 3D Stereo Enhancement",
+  /*   7 */ "Qsound QXpander",
+  /*   8 */ "Spatializer 3D Stereo Enhancement",
+  /*   9 */ "SRS 3D Stereo Enhancement",
+  /*  10 */ "Platform Tech 3D Stereo Enhancement",
+  /*  11 */ "AKM 3D Audio",
+  /*  12 */ "Aureal Stereo Enhancement",
+  /*  13 */ "Aztech 3D Enhancement",
+  /*  14 */ "Binaura 3D Audio Enhancement",
+  /*  15 */ "ESS Technology Stereo Enhancement",
+  /*  16 */ "Harman International VMAx",
+  /*  17 */ "Nvidea/IC Ensemble/KS Waves 3D Stereo Enhancement",
+  /*  18 */ "Philips Incredible Sound",
+  /*  19 */ "Texas Instruments 3D Stereo Enhancement",
+  /*  20 */ "VLSI Technology 3D Stereo Enhancement",
+  /*  21 */ "TriTech 3D Stereo Enhancement",
+  /*  22 */ "Realtek 3D Stereo Enhancement",
+  /*  23 */ "Samsung 3D Stereo Enhancement",
+  /*  24 */ "Wolfson Microelectronics 3D Enhancement",
+  /*  25 */ "Delta Integration 3D Enhancement",
+  /*  26 */ "SigmaTel 3D Enhancement",
+  /*  27 */ "IC Ensemble/KS Waves",
+  /*  28 */ "Rockwell 3D Stereo Enhancement",
+  /*  29 */ "Reserved 29",
+  /*  30 */ "Reserved 30",
+  /*  31 */ "Reserved 31"
+};
+
+static void snd_ac97_proc_read_main(struct snd_ac97 *ac97, struct snd_info_buffer *buffer, int subidx)
+{
+	char name[64];
+	unsigned short val, tmp, ext, mext;
+	static const char *spdif_slots[4] = { " SPDIF=3/4", " SPDIF=7/8", " SPDIF=6/9", " SPDIF=10/11" };
+	static const char *spdif_rates[4] = { " Rate=44.1kHz", " Rate=res", " Rate=48kHz", " Rate=32kHz" };
+	static const char *spdif_rates_cs4205[4] = { " Rate=48kHz", " Rate=44.1kHz", " Rate=res", " Rate=res" };
+	static const char *double_rate_slots[4] = { "10/11", "7/8", "reserved", "reserved" };
+
+	snd_ac97_get_name(NULL, ac97->id, name, 0);
+	snd_iprintf(buffer, "%d-%d/%d: %s\n\n", ac97->addr, ac97->num, subidx, name);
+
+	if ((ac97->scaps & AC97_SCAP_AUDIO) == 0)
+		goto __modem;
+
+        snd_iprintf(buffer, "PCI Subsys Vendor: 0x%04x\n",
+	            ac97->subsystem_vendor);
+        snd_iprintf(buffer, "PCI Subsys Device: 0x%04x\n\n",
+                    ac97->subsystem_device);
+
+	if ((ac97->ext_id & AC97_EI_REV_MASK) >= AC97_EI_REV_23) {
+		val = snd_ac97_read(ac97, AC97_INT_PAGING);
+		snd_ac97_update_bits(ac97, AC97_INT_PAGING,
+				     AC97_PAGE_MASK, AC97_PAGE_1);
+		tmp = snd_ac97_read(ac97, AC97_CODEC_CLASS_REV);
+		snd_iprintf(buffer, "Revision         : 0x%02x\n", tmp & 0xff);
+		snd_iprintf(buffer, "Compat. Class    : 0x%02x\n", (tmp >> 8) & 0x1f);
+		snd_iprintf(buffer, "Subsys. Vendor ID: 0x%04x\n",
+			    snd_ac97_read(ac97, AC97_PCI_SVID));
+		snd_iprintf(buffer, "Subsys. ID       : 0x%04x\n\n",
+			    snd_ac97_read(ac97, AC97_PCI_SID));
+		snd_ac97_update_bits(ac97, AC97_INT_PAGING,
+				     AC97_PAGE_MASK, val & AC97_PAGE_MASK);
+	}
+
+	// val = snd_ac97_read(ac97, AC97_RESET);
+	val = ac97->caps;
+	snd_iprintf(buffer, "Capabilities     :%s%s%s%s%s%s\n",
+	    	    val & AC97_BC_DEDICATED_MIC ? " -dedicated MIC PCM IN channel-" : "",
+		    val & AC97_BC_RESERVED1 ? " -reserved1-" : "",
+		    val & AC97_BC_BASS_TREBLE ? " -bass & treble-" : "",
+		    val & AC97_BC_SIM_STEREO ? " -simulated stereo-" : "",
+		    val & AC97_BC_HEADPHONE ? " -headphone out-" : "",
+		    val & AC97_BC_LOUDNESS ? " -loudness-" : "");
+	tmp = ac97->caps & AC97_BC_DAC_MASK;
+	snd_iprintf(buffer, "DAC resolution   : %s%s%s%s\n",
+		    tmp == AC97_BC_16BIT_DAC ? "16-bit" : "",
+		    tmp == AC97_BC_18BIT_DAC ? "18-bit" : "",
+		    tmp == AC97_BC_20BIT_DAC ? "20-bit" : "",
+		    tmp == AC97_BC_DAC_MASK ? "???" : "");
+	tmp = ac97->caps & AC97_BC_ADC_MASK;
+	snd_iprintf(buffer, "ADC resolution   : %s%s%s%s\n",
+		    tmp == AC97_BC_16BIT_ADC ? "16-bit" : "",
+		    tmp == AC97_BC_18BIT_ADC ? "18-bit" : "",
+		    tmp == AC97_BC_20BIT_ADC ? "20-bit" : "",
+		    tmp == AC97_BC_ADC_MASK ? "???" : "");
+	snd_iprintf(buffer, "3D enhancement   : %s\n",
+		snd_ac97_stereo_enhancements[(val >> 10) & 0x1f]);
+	snd_iprintf(buffer, "\nCurrent setup\n");
+	val = snd_ac97_read(ac97, AC97_MIC);
+	snd_iprintf(buffer, "Mic gain         : %s [%s]\n", val & 0x0040 ? "+20dB" : "+0dB", ac97->regs[AC97_MIC] & 0x0040 ? "+20dB" : "+0dB");
+	val = snd_ac97_read(ac97, AC97_GENERAL_PURPOSE);
+	snd_iprintf(buffer, "POP path         : %s 3D\n"
+		    "Sim. stereo      : %s\n"
+		    "3D enhancement   : %s\n"
+		    "Loudness         : %s\n"
+		    "Mono output      : %s\n"
+		    "Mic select       : %s\n"
+		    "ADC/DAC loopback : %s\n",
+		    val & 0x8000 ? "post" : "pre",
+		    val & 0x4000 ? "on" : "off",
+		    val & 0x2000 ? "on" : "off",
+		    val & 0x1000 ? "on" : "off",
+		    val & 0x0200 ? "Mic" : "MIX",
+		    val & 0x0100 ? "Mic2" : "Mic1",
+		    val & 0x0080 ? "on" : "off");
+	if (ac97->ext_id & AC97_EI_DRA)
+		snd_iprintf(buffer, "Double rate slots: %s\n",
+			    double_rate_slots[(val >> 10) & 3]);
+
+	ext = snd_ac97_read(ac97, AC97_EXTENDED_ID);
+	if (ext == 0)
+		goto __modem;
+		
+	snd_iprintf(buffer, "Extended ID      : codec=%i rev=%i%s%s%s%s DSA=%i%s%s%s%s\n",
+			(ext & AC97_EI_ADDR_MASK) >> AC97_EI_ADDR_SHIFT,
+			(ext & AC97_EI_REV_MASK) >> AC97_EI_REV_SHIFT,
+			ext & AC97_EI_AMAP ? " AMAP" : "",
+			ext & AC97_EI_LDAC ? " LDAC" : "",
+			ext & AC97_EI_SDAC ? " SDAC" : "",
+			ext & AC97_EI_CDAC ? " CDAC" : "",
+			(ext & AC97_EI_DACS_SLOT_MASK) >> AC97_EI_DACS_SLOT_SHIFT,
+			ext & AC97_EI_VRM ? " VRM" : "",
+			ext & AC97_EI_SPDIF ? " SPDIF" : "",
+			ext & AC97_EI_DRA ? " DRA" : "",
+			ext & AC97_EI_VRA ? " VRA" : "");
+	val = snd_ac97_read(ac97, AC97_EXTENDED_STATUS);
+	snd_iprintf(buffer, "Extended status  :%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
+			val & AC97_EA_PRL ? " PRL" : "",
+			val & AC97_EA_PRK ? " PRK" : "",
+			val & AC97_EA_PRJ ? " PRJ" : "",
+			val & AC97_EA_PRI ? " PRI" : "",
+			val & AC97_EA_SPCV ? " SPCV" : "",
+			val & AC97_EA_MDAC ? " MADC" : "",
+			val & AC97_EA_LDAC ? " LDAC" : "",
+			val & AC97_EA_SDAC ? " SDAC" : "",
+			val & AC97_EA_CDAC ? " CDAC" : "",
+			ext & AC97_EI_SPDIF ? spdif_slots[(val & AC97_EA_SPSA_SLOT_MASK) >> AC97_EA_SPSA_SLOT_SHIFT] : "",
+			val & AC97_EA_VRM ? " VRM" : "",
+			val & AC97_EA_SPDIF ? " SPDIF" : "",
+			val & AC97_EA_DRA ? " DRA" : "",
+			val & AC97_EA_VRA ? " VRA" : "");
+	if (ext & AC97_EI_VRA) {	/* VRA */
+		val = snd_ac97_read(ac97, AC97_PCM_FRONT_DAC_RATE);
+		snd_iprintf(buffer, "PCM front DAC    : %iHz\n", val);
+		if (ext & AC97_EI_SDAC) {
+			val = snd_ac97_read(ac97, AC97_PCM_SURR_DAC_RATE);
+			snd_iprintf(buffer, "PCM Surr DAC     : %iHz\n", val);
+		}
+		if (ext & AC97_EI_LDAC) {
+			val = snd_ac97_read(ac97, AC97_PCM_LFE_DAC_RATE);
+			snd_iprintf(buffer, "PCM LFE DAC      : %iHz\n", val);
+		}
+		val = snd_ac97_read(ac97, AC97_PCM_LR_ADC_RATE);
+		snd_iprintf(buffer, "PCM ADC          : %iHz\n", val);
+	}
+	if (ext & AC97_EI_VRM) {
+		val = snd_ac97_read(ac97, AC97_PCM_MIC_ADC_RATE);
+		snd_iprintf(buffer, "PCM MIC ADC      : %iHz\n", val);
+	}
+	if ((ext & AC97_EI_SPDIF) || (ac97->flags & AC97_CS_SPDIF)) {
+	        if (ac97->flags & AC97_CS_SPDIF)
+			val = snd_ac97_read(ac97, AC97_CSR_SPDIF);
+		else
+			val = snd_ac97_read(ac97, AC97_SPDIF);
+
+		snd_iprintf(buffer, "SPDIF Control    :%s%s%s%s Category=0x%x Generation=%i%s%s%s\n",
+			val & AC97_SC_PRO ? " PRO" : " Consumer",
+			val & AC97_SC_NAUDIO ? " Non-audio" : " PCM",
+			val & AC97_SC_COPY ? "" : " Copyright",
+			val & AC97_SC_PRE ? " Preemph50/15" : "",
+			(val & AC97_SC_CC_MASK) >> AC97_SC_CC_SHIFT,
+			(val & AC97_SC_L) >> 11,
+			(ac97->flags & AC97_CS_SPDIF) ?
+			    spdif_rates_cs4205[(val & AC97_SC_SPSR_MASK) >> AC97_SC_SPSR_SHIFT] :
+			    spdif_rates[(val & AC97_SC_SPSR_MASK) >> AC97_SC_SPSR_SHIFT],
+			(ac97->flags & AC97_CS_SPDIF) ?
+			    (val & AC97_SC_DRS ? " Validity" : "") :
+			    (val & AC97_SC_DRS ? " DRS" : ""),
+			(ac97->flags & AC97_CS_SPDIF) ?
+			    (val & AC97_SC_V ? " Enabled" : "") :
+			    (val & AC97_SC_V ? " Validity" : ""));
+		/* ALC650 specific*/
+		if ((ac97->id & 0xfffffff0) == 0x414c4720 &&
+		    (snd_ac97_read(ac97, AC97_ALC650_CLOCK) & 0x01)) {
+			val = snd_ac97_read(ac97, AC97_ALC650_SPDIF_INPUT_STATUS2);
+			if (val & AC97_ALC650_CLOCK_LOCK) {
+				val = snd_ac97_read(ac97, AC97_ALC650_SPDIF_INPUT_STATUS1);
+				snd_iprintf(buffer, "SPDIF In Status  :%s%s%s%s Category=0x%x Generation=%i",
+					    val & AC97_ALC650_PRO ? " PRO" : " Consumer",
+					    val & AC97_ALC650_NAUDIO ? " Non-audio" : " PCM",
+					    val & AC97_ALC650_COPY ? "" : " Copyright",
+					    val & AC97_ALC650_PRE ? " Preemph50/15" : "",
+					    (val & AC97_ALC650_CC_MASK) >> AC97_ALC650_CC_SHIFT,
+					    (val & AC97_ALC650_L) >> 15);
+				val = snd_ac97_read(ac97, AC97_ALC650_SPDIF_INPUT_STATUS2);
+				snd_iprintf(buffer, "%s Accuracy=%i%s%s\n",
+					    spdif_rates[(val & AC97_ALC650_SPSR_MASK) >> AC97_ALC650_SPSR_SHIFT],
+					    (val & AC97_ALC650_CLOCK_ACCURACY) >> AC97_ALC650_CLOCK_SHIFT,
+					    (val & AC97_ALC650_CLOCK_LOCK ? " Locked" : " Unlocked"),
+					    (val & AC97_ALC650_V ? " Validity?" : ""));
+			} else {
+				snd_iprintf(buffer, "SPDIF In Status  : Not Locked\n");
+			}
+		}
+	}
+	if ((ac97->ext_id & AC97_EI_REV_MASK) >= AC97_EI_REV_23) {
+		val = snd_ac97_read(ac97, AC97_INT_PAGING);
+		snd_ac97_update_bits(ac97, AC97_INT_PAGING,
+				     AC97_PAGE_MASK, AC97_PAGE_1);
+		snd_ac97_proc_read_functions(ac97, buffer);
+		snd_ac97_update_bits(ac97, AC97_INT_PAGING,
+				     AC97_PAGE_MASK, val & AC97_PAGE_MASK);
+	}
+
+
+      __modem:
+	mext = snd_ac97_read(ac97, AC97_EXTENDED_MID);
+	if (mext == 0)
+		return;
+	
+	snd_iprintf(buffer, "Extended modem ID: codec=%i%s%s%s%s%s\n",
+			(mext & AC97_MEI_ADDR_MASK) >> AC97_MEI_ADDR_SHIFT,
+			mext & AC97_MEI_CID2 ? " CID2" : "",
+			mext & AC97_MEI_CID1 ? " CID1" : "",
+			mext & AC97_MEI_HANDSET ? " HSET" : "",
+			mext & AC97_MEI_LINE2 ? " LIN2" : "",
+			mext & AC97_MEI_LINE1 ? " LIN1" : "");
+	val = snd_ac97_read(ac97, AC97_EXTENDED_MSTATUS);
+	snd_iprintf(buffer, "Modem status     :%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
+			val & AC97_MEA_GPIO ? " GPIO" : "",
+			val & AC97_MEA_MREF ? " MREF" : "",
+			val & AC97_MEA_ADC1 ? " ADC1" : "",
+			val & AC97_MEA_DAC1 ? " DAC1" : "",
+			val & AC97_MEA_ADC2 ? " ADC2" : "",
+			val & AC97_MEA_DAC2 ? " DAC2" : "",
+			val & AC97_MEA_HADC ? " HADC" : "",
+			val & AC97_MEA_HDAC ? " HDAC" : "",
+			val & AC97_MEA_PRA ? " PRA(GPIO)" : "",
+			val & AC97_MEA_PRB ? " PRB(res)" : "",
+			val & AC97_MEA_PRC ? " PRC(ADC1)" : "",
+			val & AC97_MEA_PRD ? " PRD(DAC1)" : "",
+			val & AC97_MEA_PRE ? " PRE(ADC2)" : "",
+			val & AC97_MEA_PRF ? " PRF(DAC2)" : "",
+			val & AC97_MEA_PRG ? " PRG(HADC)" : "",
+			val & AC97_MEA_PRH ? " PRH(HDAC)" : "");
+	if (mext & AC97_MEI_LINE1) {
+		val = snd_ac97_read(ac97, AC97_LINE1_RATE);
+		snd_iprintf(buffer, "Line1 rate       : %iHz\n", val);
+	}
+	if (mext & AC97_MEI_LINE2) {
+		val = snd_ac97_read(ac97, AC97_LINE2_RATE);
+		snd_iprintf(buffer, "Line2 rate       : %iHz\n", val);
+	}
+	if (mext & AC97_MEI_HANDSET) {
+		val = snd_ac97_read(ac97, AC97_HANDSET_RATE);
+		snd_iprintf(buffer, "Headset rate     : %iHz\n", val);
+	}
+}
+
+static void snd_ac97_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
+{
+	struct snd_ac97 *ac97 = entry->private_data;
+	
+	down(&ac97->page_mutex);
+	if ((ac97->id & 0xffffff40) == AC97_ID_AD1881) {	// Analog Devices AD1881/85/86
+		int idx;
+		for (idx = 0; idx < 3; idx++)
+			if (ac97->spec.ad18xx.id[idx]) {
+				/* select single codec */
+				snd_ac97_update_bits(ac97, AC97_AD_SERIAL_CFG, 0x7000,
+						     ac97->spec.ad18xx.unchained[idx] | ac97->spec.ad18xx.chained[idx]);
+				snd_ac97_proc_read_main(ac97, buffer, idx);
+				snd_iprintf(buffer, "\n\n");
+			}
+		/* select all codecs */
+		snd_ac97_update_bits(ac97, AC97_AD_SERIAL_CFG, 0x7000, 0x7000);
+		
+		snd_iprintf(buffer, "\nAD18XX configuration\n");
+		snd_iprintf(buffer, "Unchained        : 0x%04x,0x%04x,0x%04x\n",
+			ac97->spec.ad18xx.unchained[0],
+			ac97->spec.ad18xx.unchained[1],
+			ac97->spec.ad18xx.unchained[2]);
+		snd_iprintf(buffer, "Chained          : 0x%04x,0x%04x,0x%04x\n",
+			ac97->spec.ad18xx.chained[0],
+			ac97->spec.ad18xx.chained[1],
+			ac97->spec.ad18xx.chained[2]);
+	} else {
+		snd_ac97_proc_read_main(ac97, buffer, 0);
+	}
+	up(&ac97->page_mutex);
+}
+
+#ifdef CONFIG_SND_DEBUG
+/* direct register write for debugging */
+static void snd_ac97_proc_regs_write(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
+{
+	struct snd_ac97 *ac97 = entry->private_data;
+	char line[64];
+	unsigned int reg, val;
+	down(&ac97->page_mutex);
+	while (!snd_info_get_line(buffer, line, sizeof(line))) {
+		if (sscanf(line, "%x %x", &reg, &val) != 2)
+			continue;
+		/* register must be even */
+		if (reg < 0x80 && (reg & 1) == 0 && val <= 0xffff)
+			snd_ac97_write_cache(ac97, reg, val);
+	}
+	up(&ac97->page_mutex);
+}
+#endif
+
+static void snd_ac97_proc_regs_read_main(struct snd_ac97 *ac97, struct snd_info_buffer *buffer, int subidx)
+{
+	int reg, val;
+
+	for (reg = 0; reg < 0x80; reg += 2) {
+		val = snd_ac97_read(ac97, reg);
+		snd_iprintf(buffer, "%i:%02x = %04x\n", subidx, reg, val);
+	}
+}
+
+static void snd_ac97_proc_regs_read(struct snd_info_entry *entry, 
+				    struct snd_info_buffer *buffer)
+{
+	struct snd_ac97 *ac97 = entry->private_data;
+
+	down(&ac97->page_mutex);
+	if ((ac97->id & 0xffffff40) == AC97_ID_AD1881) {	// Analog Devices AD1881/85/86
+
+		int idx;
+		for (idx = 0; idx < 3; idx++)
+			if (ac97->spec.ad18xx.id[idx]) {
+				/* select single codec */
+				snd_ac97_update_bits(ac97, AC97_AD_SERIAL_CFG, 0x7000,
+						     ac97->spec.ad18xx.unchained[idx] | ac97->spec.ad18xx.chained[idx]);
+				snd_ac97_proc_regs_read_main(ac97, buffer, idx);
+			}
+		/* select all codecs */
+		snd_ac97_update_bits(ac97, AC97_AD_SERIAL_CFG, 0x7000, 0x7000);
+	} else {
+		snd_ac97_proc_regs_read_main(ac97, buffer, 0);
+	}	
+	up(&ac97->page_mutex);
+}
+
+void snd_ac97_proc_init(struct snd_ac97 * ac97)
+{
+	struct snd_info_entry *entry;
+	char name[32];
+	const char *prefix;
+
+	if (ac97->bus->proc == NULL)
+		return;
+	prefix = ac97_is_audio(ac97) ? "ac97" : "mc97";
+	sprintf(name, "%s#%d-%d", prefix, ac97->addr, ac97->num);
+	if ((entry = snd_info_create_card_entry(ac97->bus->card, name, ac97->bus->proc)) != NULL) {
+		snd_info_set_text_ops(entry, ac97, 1024, snd_ac97_proc_read);
+		if (snd_info_register(entry) < 0) {
+			snd_info_free_entry(entry);
+			entry = NULL;
+		}
+	}
+	ac97->proc = entry;
+	sprintf(name, "%s#%d-%d+regs", prefix, ac97->addr, ac97->num);
+	if ((entry = snd_info_create_card_entry(ac97->bus->card, name, ac97->bus->proc)) != NULL) {
+		snd_info_set_text_ops(entry, ac97, 1024, snd_ac97_proc_regs_read);
+#ifdef CONFIG_SND_DEBUG
+		entry->mode |= S_IWUSR;
+		entry->c.text.write_size = 1024;
+		entry->c.text.write = snd_ac97_proc_regs_write;
+#endif
+		if (snd_info_register(entry) < 0) {
+			snd_info_free_entry(entry);
+			entry = NULL;
+		}
+	}
+	ac97->proc_regs = entry;
+}
+
+void snd_ac97_proc_done(struct snd_ac97 * ac97)
+{
+	if (ac97->proc_regs) {
+		snd_info_unregister(ac97->proc_regs);
+		ac97->proc_regs = NULL;
+	}
+	if (ac97->proc) {
+		snd_info_unregister(ac97->proc);
+		ac97->proc = NULL;
+	}
+}
+
+void snd_ac97_bus_proc_init(struct snd_ac97_bus * bus)
+{
+	struct snd_info_entry *entry;
+	char name[32];
+
+	sprintf(name, "codec97#%d", bus->num);
+	if ((entry = snd_info_create_card_entry(bus->card, name, bus->card->proc_root)) != NULL) {
+		entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
+		if (snd_info_register(entry) < 0) {
+			snd_info_free_entry(entry);
+			entry = NULL;
+		}
+	}
+	bus->proc = entry;
+}
+
+void snd_ac97_bus_proc_done(struct snd_ac97_bus * bus)
+{
+	if (bus->proc) {
+		snd_info_unregister(bus->proc);
+		bus->proc = NULL;
+	}
+}
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/ac97/ak4531_codec.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/ac97/ak4531_codec.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/ac97/ak4531_codec.c	(revision 91)
@@ -0,0 +1,478 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  Universal routines for AK4531 codec
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/ak4531_codec.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Universal routines for AK4531 codec");
+MODULE_LICENSE("GPL");
+
+#ifdef CONFIG_PROC_FS
+static void snd_ak4531_proc_init(struct snd_card *card, struct snd_ak4531 *ak4531);
+#else
+#define snd_ak4531_proc_init(card,ak)
+#endif
+
+/*
+ *
+ */
+ 
+#if 0
+
+static void snd_ak4531_dump(struct snd_ak4531 *ak4531)
+{
+	int idx;
+	
+	for (idx = 0; idx < 0x19; idx++)
+		printk("ak4531 0x%x: 0x%x\n", idx, ak4531->regs[idx]);
+}
+
+#endif
+
+/*
+ *
+ */
+
+#define AK4531_SINGLE(xname, xindex, reg, shift, mask, invert) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+  .info = snd_ak4531_info_single, \
+  .get = snd_ak4531_get_single, .put = snd_ak4531_put_single, \
+  .private_value = reg | (shift << 16) | (mask << 24) | (invert << 22) }
+
+static int snd_ak4531_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+
+	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	return 0;
+}
+ 
+static int snd_ak4531_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ak4531 *ak4531 = snd_kcontrol_chip(kcontrol);
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 16) & 0x07;
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+	int invert = (kcontrol->private_value >> 22) & 1;
+	int val;
+
+	down(&ak4531->reg_mutex);
+	val = (ak4531->regs[reg] >> shift) & mask;
+	up(&ak4531->reg_mutex);
+	if (invert) {
+		val = mask - val;
+	}
+	ucontrol->value.integer.value[0] = val;
+	return 0;
+}
+
+static int snd_ak4531_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ak4531 *ak4531 = snd_kcontrol_chip(kcontrol);
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 16) & 0x07;
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+	int invert = (kcontrol->private_value >> 22) & 1;
+	int change;
+	int val;
+
+	val = ucontrol->value.integer.value[0] & mask;
+	if (invert) {
+		val = mask - val;
+	}
+	val <<= shift;
+	down(&ak4531->reg_mutex);
+	val = (ak4531->regs[reg] & ~(mask << shift)) | val;
+	change = val != ak4531->regs[reg];
+	ak4531->write(ak4531, reg, ak4531->regs[reg] = val);
+	up(&ak4531->reg_mutex);
+	return change;
+}
+
+#define AK4531_DOUBLE(xname, xindex, left_reg, right_reg, left_shift, right_shift, mask, invert) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+  .info = snd_ak4531_info_double, \
+  .get = snd_ak4531_get_double, .put = snd_ak4531_put_double, \
+  .private_value = left_reg | (right_reg << 8) | (left_shift << 16) | (right_shift << 19) | (mask << 24) | (invert << 22) }
+
+static int snd_ak4531_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+
+	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	return 0;
+}
+ 
+static int snd_ak4531_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ak4531 *ak4531 = snd_kcontrol_chip(kcontrol);
+	int left_reg = kcontrol->private_value & 0xff;
+	int right_reg = (kcontrol->private_value >> 8) & 0xff;
+	int left_shift = (kcontrol->private_value >> 16) & 0x07;
+	int right_shift = (kcontrol->private_value >> 19) & 0x07;
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+	int invert = (kcontrol->private_value >> 22) & 1;
+	int left, right;
+
+	down(&ak4531->reg_mutex);
+	left = (ak4531->regs[left_reg] >> left_shift) & mask;
+	right = (ak4531->regs[right_reg] >> right_shift) & mask;
+	up(&ak4531->reg_mutex);
+	if (invert) {
+		left = mask - left;
+		right = mask - right;
+	}
+	ucontrol->value.integer.value[0] = left;
+	ucontrol->value.integer.value[1] = right;
+	return 0;
+}
+
+static int snd_ak4531_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ak4531 *ak4531 = snd_kcontrol_chip(kcontrol);
+	int left_reg = kcontrol->private_value & 0xff;
+	int right_reg = (kcontrol->private_value >> 8) & 0xff;
+	int left_shift = (kcontrol->private_value >> 16) & 0x07;
+	int right_shift = (kcontrol->private_value >> 19) & 0x07;
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+	int invert = (kcontrol->private_value >> 22) & 1;
+	int change;
+	int left, right;
+
+	left = ucontrol->value.integer.value[0] & mask;
+	right = ucontrol->value.integer.value[1] & mask;
+	if (invert) {
+		left = mask - left;
+		right = mask - right;
+	}
+	left <<= left_shift;
+	right <<= right_shift;
+	down(&ak4531->reg_mutex);
+	if (left_reg == right_reg) {
+		left = (ak4531->regs[left_reg] & ~((mask << left_shift) | (mask << right_shift))) | left | right;
+		change = left != ak4531->regs[left_reg];
+		ak4531->write(ak4531, left_reg, ak4531->regs[left_reg] = left);
+	} else {
+		left = (ak4531->regs[left_reg] & ~(mask << left_shift)) | left;
+		right = (ak4531->regs[right_reg] & ~(mask << right_shift)) | right;
+		change = left != ak4531->regs[left_reg] || right != ak4531->regs[right_reg];
+		ak4531->write(ak4531, left_reg, ak4531->regs[left_reg] = left);
+		ak4531->write(ak4531, right_reg, ak4531->regs[right_reg] = right);
+	}
+	up(&ak4531->reg_mutex);
+	return change;
+}
+
+#define AK4531_INPUT_SW(xname, xindex, reg1, reg2, left_shift, right_shift) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+  .info = snd_ak4531_info_input_sw, \
+  .get = snd_ak4531_get_input_sw, .put = snd_ak4531_put_input_sw, \
+  .private_value = reg1 | (reg2 << 8) | (left_shift << 16) | (right_shift << 24) }
+
+static int snd_ak4531_info_input_sw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = 4;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+	return 0;
+}
+ 
+static int snd_ak4531_get_input_sw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ak4531 *ak4531 = snd_kcontrol_chip(kcontrol);
+	int reg1 = kcontrol->private_value & 0xff;
+	int reg2 = (kcontrol->private_value >> 8) & 0xff;
+	int left_shift = (kcontrol->private_value >> 16) & 0x0f;
+	int right_shift = (kcontrol->private_value >> 24) & 0x0f;
+
+	down(&ak4531->reg_mutex);
+	ucontrol->value.integer.value[0] = (ak4531->regs[reg1] >> left_shift) & 1;
+	ucontrol->value.integer.value[1] = (ak4531->regs[reg2] >> left_shift) & 1;
+	ucontrol->value.integer.value[2] = (ak4531->regs[reg1] >> right_shift) & 1;
+	ucontrol->value.integer.value[3] = (ak4531->regs[reg2] >> right_shift) & 1;
+	up(&ak4531->reg_mutex);
+	return 0;
+}
+
+static int snd_ak4531_put_input_sw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ak4531 *ak4531 = snd_kcontrol_chip(kcontrol);
+	int reg1 = kcontrol->private_value & 0xff;
+	int reg2 = (kcontrol->private_value >> 8) & 0xff;
+	int left_shift = (kcontrol->private_value >> 16) & 0x0f;
+	int right_shift = (kcontrol->private_value >> 24) & 0x0f;
+	int change;
+	int val1, val2;
+
+	down(&ak4531->reg_mutex);
+	val1 = ak4531->regs[reg1] & ~((1 << left_shift) | (1 << right_shift));
+	val2 = ak4531->regs[reg2] & ~((1 << left_shift) | (1 << right_shift));
+	val1 |= (ucontrol->value.integer.value[0] & 1) << left_shift;
+	val2 |= (ucontrol->value.integer.value[1] & 1) << left_shift;
+	val1 |= (ucontrol->value.integer.value[2] & 1) << right_shift;
+	val2 |= (ucontrol->value.integer.value[3] & 1) << right_shift;
+	change = val1 != ak4531->regs[reg1] || val2 != ak4531->regs[reg2];
+	ak4531->write(ak4531, reg1, ak4531->regs[reg1] = val1);
+	ak4531->write(ak4531, reg2, ak4531->regs[reg2] = val2);
+	up(&ak4531->reg_mutex);
+	return change;
+}
+
+static struct snd_kcontrol_new snd_ak4531_controls[] = {
+
+AK4531_DOUBLE("Master Playback Switch", 0, AK4531_LMASTER, AK4531_RMASTER, 7, 7, 1, 1),
+AK4531_DOUBLE("Master Playback Volume", 0, AK4531_LMASTER, AK4531_RMASTER, 0, 0, 0x1f, 1),
+
+AK4531_SINGLE("Master Mono Playback Switch", 0, AK4531_MONO_OUT, 7, 1, 1),
+AK4531_SINGLE("Master Mono Playback Volume", 0, AK4531_MONO_OUT, 0, 0x07, 1),
+
+AK4531_DOUBLE("PCM Switch", 0, AK4531_LVOICE, AK4531_RVOICE, 7, 7, 1, 1),
+AK4531_DOUBLE("PCM Volume", 0, AK4531_LVOICE, AK4531_RVOICE, 0, 0, 0x1f, 1),
+AK4531_DOUBLE("PCM Playback Switch", 0, AK4531_OUT_SW2, AK4531_OUT_SW2, 3, 2, 1, 0),
+AK4531_DOUBLE("PCM Capture Switch", 0, AK4531_LIN_SW2, AK4531_RIN_SW2, 2, 2, 1, 0),
+
+AK4531_DOUBLE("PCM Switch", 1, AK4531_LFM, AK4531_RFM, 7, 7, 1, 1),
+AK4531_DOUBLE("PCM Volume", 1, AK4531_LFM, AK4531_RFM, 0, 0, 0x1f, 1),
+AK4531_DOUBLE("PCM Playback Switch", 1, AK4531_OUT_SW1, AK4531_OUT_SW1, 6, 5, 1, 0),
+AK4531_INPUT_SW("PCM Capture Route", 1, AK4531_LIN_SW1, AK4531_RIN_SW1, 6, 5),
+
+AK4531_DOUBLE("CD Switch", 0, AK4531_LCD, AK4531_RCD, 7, 7, 1, 1),
+AK4531_DOUBLE("CD Volume", 0, AK4531_LCD, AK4531_RCD, 0, 0, 0x1f, 1),
+AK4531_DOUBLE("CD Playback Switch", 0, AK4531_OUT_SW1, AK4531_OUT_SW1, 2, 1, 1, 0),
+AK4531_INPUT_SW("CD Capture Route", 0, AK4531_LIN_SW1, AK4531_RIN_SW1, 2, 1),
+
+AK4531_DOUBLE("Line Switch", 0, AK4531_LLINE, AK4531_RLINE, 7, 7, 1, 1),
+AK4531_DOUBLE("Line Volume", 0, AK4531_LLINE, AK4531_RLINE, 0, 0, 0x1f, 1),
+AK4531_DOUBLE("Line Playback Switch", 0, AK4531_OUT_SW1, AK4531_OUT_SW1, 4, 3, 1, 0),
+AK4531_INPUT_SW("Line Capture Route", 0, AK4531_LIN_SW1, AK4531_RIN_SW1, 4, 3),
+
+AK4531_DOUBLE("Aux Switch", 0, AK4531_LAUXA, AK4531_RAUXA, 7, 7, 1, 1),
+AK4531_DOUBLE("Aux Volume", 0, AK4531_LAUXA, AK4531_RAUXA, 0, 0, 0x1f, 1),
+AK4531_DOUBLE("Aux Playback Switch", 0, AK4531_OUT_SW2, AK4531_OUT_SW2, 5, 4, 1, 0),
+AK4531_INPUT_SW("Aux Capture Route", 0, AK4531_LIN_SW2, AK4531_RIN_SW2, 4, 3),
+
+AK4531_SINGLE("Mono Switch", 0, AK4531_MONO1, 7, 1, 1),
+AK4531_SINGLE("Mono Volume", 0, AK4531_MONO1, 0, 0x1f, 1),
+AK4531_SINGLE("Mono Playback Switch", 0, AK4531_OUT_SW2, 0, 1, 0),
+AK4531_DOUBLE("Mono Capture Switch", 0, AK4531_LIN_SW2, AK4531_RIN_SW2, 0, 0, 1, 0),
+
+AK4531_SINGLE("Mono Switch", 1, AK4531_MONO2, 7, 1, 1),
+AK4531_SINGLE("Mono Volume", 1, AK4531_MONO2, 0, 0x1f, 1),
+AK4531_SINGLE("Mono Playback Switch", 1, AK4531_OUT_SW2, 1, 1, 0),
+AK4531_DOUBLE("Mono Capture Switch", 1, AK4531_LIN_SW2, AK4531_RIN_SW2, 1, 1, 1, 0),
+
+AK4531_SINGLE("Mic Volume", 0, AK4531_MIC, 0, 0x1f, 1),
+AK4531_SINGLE("Mic Switch", 0, AK4531_MIC, 7, 1, 1),
+AK4531_SINGLE("Mic Playback Switch", 0, AK4531_OUT_SW1, 0, 1, 0),
+AK4531_DOUBLE("Mic Capture Switch", 0, AK4531_LIN_SW1, AK4531_RIN_SW1, 0, 0, 1, 0),
+
+AK4531_DOUBLE("Mic Bypass Capture Switch", 0, AK4531_LIN_SW2, AK4531_RIN_SW2, 7, 7, 1, 0),
+AK4531_DOUBLE("Mono1 Bypass Capture Switch", 0, AK4531_LIN_SW2, AK4531_RIN_SW2, 6, 6, 1, 0),
+AK4531_DOUBLE("Mono2 Bypass Capture Switch", 0, AK4531_LIN_SW2, AK4531_RIN_SW2, 5, 5, 1, 0),
+
+AK4531_SINGLE("AD Input Select", 0, AK4531_AD_IN, 0, 1, 0),
+AK4531_SINGLE("Mic Boost (+30dB)", 0, AK4531_MIC_GAIN, 0, 1, 0)
+};
+
+static int snd_ak4531_free(struct snd_ak4531 *ak4531)
+{
+	if (ak4531) {
+		if (ak4531->private_free)
+			ak4531->private_free(ak4531);
+		kfree(ak4531);
+	}
+	return 0;
+}
+
+static int snd_ak4531_dev_free(struct snd_device *device)
+{
+	struct snd_ak4531 *ak4531 = device->device_data;
+	return snd_ak4531_free(ak4531);
+}
+
+static u8 snd_ak4531_initial_map[0x19 + 1] = {
+	0x9f,		/* 00: Master Volume Lch */
+	0x9f,		/* 01: Master Volume Rch */
+	0x9f,		/* 02: Voice Volume Lch */
+	0x9f,		/* 03: Voice Volume Rch */
+	0x9f,		/* 04: FM Volume Lch */
+	0x9f,		/* 05: FM Volume Rch */
+	0x9f,		/* 06: CD Audio Volume Lch */
+	0x9f,		/* 07: CD Audio Volume Rch */
+	0x9f,		/* 08: Line Volume Lch */
+	0x9f,		/* 09: Line Volume Rch */
+	0x9f,		/* 0a: Aux Volume Lch */
+	0x9f,		/* 0b: Aux Volume Rch */
+	0x9f,		/* 0c: Mono1 Volume */
+	0x9f,		/* 0d: Mono2 Volume */
+	0x9f,		/* 0e: Mic Volume */
+	0x87,		/* 0f: Mono-out Volume */
+	0x00,		/* 10: Output Mixer SW1 */
+	0x00,		/* 11: Output Mixer SW2 */
+	0x00,		/* 12: Lch Input Mixer SW1 */
+	0x00,		/* 13: Rch Input Mixer SW1 */
+	0x00,		/* 14: Lch Input Mixer SW2 */
+	0x00,		/* 15: Rch Input Mixer SW2 */
+	0x00,		/* 16: Reset & Power Down */
+	0x00,		/* 17: Clock Select */
+	0x00,		/* 18: AD Input Select */
+	0x01		/* 19: Mic Amp Setup */
+};
+
+int snd_ak4531_mixer(struct snd_card *card, struct snd_ak4531 *_ak4531,
+		     struct snd_ak4531 **rak4531)
+{
+	unsigned int idx;
+	int err;
+	struct snd_ak4531 *ak4531;
+	static struct snd_device_ops ops = {
+		.dev_free =	snd_ak4531_dev_free,
+	};
+
+	snd_assert(rak4531 != NULL, return -EINVAL);
+	*rak4531 = NULL;
+	snd_assert(card != NULL && _ak4531 != NULL, return -EINVAL);
+	ak4531 = kzalloc(sizeof(*ak4531), GFP_KERNEL);
+	if (ak4531 == NULL)
+		return -ENOMEM;
+	*ak4531 = *_ak4531;
+	init_MUTEX(&ak4531->reg_mutex);
+	if ((err = snd_component_add(card, "AK4531")) < 0) {
+		snd_ak4531_free(ak4531);
+		return err;
+	}
+	strcpy(card->mixername, "Asahi Kasei AK4531");
+	ak4531->write(ak4531, AK4531_RESET, 0x03);	/* no RST, PD */
+	udelay(100);
+	ak4531->write(ak4531, AK4531_CLOCK, 0x00);	/* CODEC ADC and CODEC DAC use {LR,B}CLK2 and run off LRCLK2 PLL */
+	for (idx = 0; idx <= 0x19; idx++) {
+		if (idx == AK4531_RESET || idx == AK4531_CLOCK)
+			continue;
+		ak4531->write(ak4531, idx, ak4531->regs[idx] = snd_ak4531_initial_map[idx]);	/* recording source is mixer */
+	}
+	for (idx = 0; idx < ARRAY_SIZE(snd_ak4531_controls); idx++) {
+		if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_ak4531_controls[idx], ak4531))) < 0) {
+			snd_ak4531_free(ak4531);
+			return err;
+		}
+	}
+	snd_ak4531_proc_init(card, ak4531);
+	if ((err = snd_device_new(card, SNDRV_DEV_CODEC, ak4531, &ops)) < 0) {
+		snd_ak4531_free(ak4531);
+		return err;
+	}
+
+#if 0
+	snd_ak4531_dump(ak4531);
+#endif
+	*rak4531 = ak4531;
+	return 0;
+}
+
+/*
+ * power management
+ */
+#ifdef CONFIG_PM
+void snd_ak4531_suspend(struct snd_ak4531 *ak4531)
+{
+	/* mute */
+	ak4531->write(ak4531, AK4531_LMASTER, 0x9f);
+	ak4531->write(ak4531, AK4531_RMASTER, 0x9f);
+	/* powerdown */
+	ak4531->write(ak4531, AK4531_RESET, 0x01);
+}
+
+void snd_ak4531_resume(struct snd_ak4531 *ak4531)
+{
+	int idx;
+
+	/* initialize */
+	ak4531->write(ak4531, AK4531_RESET, 0x03);
+	udelay(100);
+	ak4531->write(ak4531, AK4531_CLOCK, 0x00);
+	/* restore mixer registers */
+	for (idx = 0; idx <= 0x19; idx++) {
+		if (idx == AK4531_RESET || idx == AK4531_CLOCK)
+			continue;
+		ak4531->write(ak4531, idx, ak4531->regs[idx]);
+	}
+}
+#endif
+
+#ifdef CONFIG_PROC_FS
+/*
+ * /proc interface
+ */
+
+static void snd_ak4531_proc_read(struct snd_info_entry *entry, 
+				 struct snd_info_buffer *buffer)
+{
+	struct snd_ak4531 *ak4531 = entry->private_data;
+
+	snd_iprintf(buffer, "Asahi Kasei AK4531\n\n");
+	snd_iprintf(buffer, "Recording source   : %s\n"
+		    "MIC gain           : %s\n",
+		    ak4531->regs[AK4531_AD_IN] & 1 ? "external" : "mixer",
+		    ak4531->regs[AK4531_MIC_GAIN] & 1 ? "+30dB" : "+0dB");
+}
+
+static void snd_ak4531_proc_init(struct snd_card *card, struct snd_ak4531 *ak4531)
+{
+	struct snd_info_entry *entry;
+
+	if (! snd_card_proc_new(card, "ak4531", &entry))
+		snd_info_set_text_ops(entry, ak4531, 1024, snd_ak4531_proc_read);
+}
+#endif
+
+EXPORT_SYMBOL(snd_ak4531_mixer);
+#ifdef CONFIG_PM
+EXPORT_SYMBOL(snd_ak4531_suspend);
+EXPORT_SYMBOL(snd_ak4531_resume);
+#endif
+
+/*
+ *  INIT part
+ */
+
+static int __init alsa_ak4531_init(void)
+{
+	return 0;
+}
+
+static void __exit alsa_ak4531_exit(void)
+{
+}
+
+module_init(alsa_ak4531_init)
+module_exit(alsa_ak4531_exit)
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/ac97/diff.cmd
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/ac97/diff.cmd	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/ac97/diff.cmd	(revision 91)
@@ -0,0 +1,1 @@
+diff.exe -Nuwr E:\DEV\USS\from_z\ua2\GPL\alsa-kernel\pci\ac97\%1 E:\DEV\alsa\alsa-kernel\pci\ac97\%1 >%1.diff
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/ac97/diff1.cmd
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/ac97/diff1.cmd	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/ac97/diff1.cmd	(revision 91)
@@ -0,0 +1,1 @@
+diff.exe -Nuwr z:\ua2\GPL\alsa-kernel\pci\ac97\%1 E:\DEV\uss\ua2\gpl\alsa-kernel\pci\ac97\%1 >%1.diff
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/ac97/makefile.os2
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/ac97/makefile.os2	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/ac97/makefile.os2	(revision 91)
@@ -0,0 +1,51 @@
+CINCLUDES = ..\..\..\include;..\..\include;$(%WATCOM)\H
+AINCLUDES = $(DDK_BASE)\H
+
+CDEFINES = -D__KERNEL__ -DMODULE -dTARGET_OS2 -dALSA_BUILD -D__ISAPNP__ -DCONFIG_ISAPNP -D__i386_
+
+32BIT=1
+!include ..\..\..\..\makefile.inc
+
+
+#===================================================================
+#
+#   List of source files
+#
+#===================================================================
+FILE1    = ac97_codec.obj ac97_patch.obj ak4531_codec.obj ac97_proc.obj ac97_pcm.obj
+FILE2    =
+FILE3    =
+FILE4    =
+FILE5    =
+FILE6    =
+FILE7    =
+FILE8    =
+FILELAST =
+FILES    = $(FILE0) $(FILE1) $(FILE2) $(FILE3) $(FILE4) $(FILE5) $(FILE6) $(FILE7) $(FILE8) $(FILE9) $(FILE10) $(FILE11) $(FILE12)
+
+TARGET   = ac97
+
+LNKFILE  = $(OBJDIR)\$(TARGET).lnk
+
+
+#===================================================================
+#
+#   Specific dependencies
+#
+#===================================================================
+all: $(OBJDIR)\$(TARGET).lib
+
+
+$(LNKFILE): makefile.os2
+    @%create $(LNKFILE)
+    @for %f in ($(FILES)) do @%append $(LNKFILE) -+$(OBJDIR)\%f
+
+$(OBJDIR)\$(TARGET).lib: $(LNKFILE) $(FILES)
+    $(LIB) $(OBJDIR)\$(TARGET).lib @$(LNKFILE)
+    copy $(OBJDIR)\$(TARGET).lib $(ALSA_LIB)
+
+clean: .SYMBOLIC
+    -@rm  *.err 2>nul
+    @cd $(OBJDIR)
+    -rm *.obj *.lnk *.sys *.sym *.lst *.map *.wmap *.lib 2>nul
+    @cd ../..
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/ali5451/ali5451.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/ali5451/ali5451.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/ali5451/ali5451.c	(revision 91)
@@ -0,0 +1,2360 @@
+/*
+ *  Matt Wu <Matt_Wu@acersoftech.com.cn>
+ *  Apr 26, 2001
+ *  Routines for control of ALi pci audio M5451
+ *
+ *  BUGS:
+ *    --
+ *
+ *  TODO:
+ *    --
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public Lcodecnse as published by
+ *   the Free Software Foundation; either version 2 of the Lcodecnse, 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 Lcodecnse for more details.
+ *
+ *   You should have received a copy of the GNU General Public Lcodecnse
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#define __SNDRV_OSS_COMPAT__
+#define SNDRV_MAIN_OBJECT_FILE
+
+#include "sound/driver.h"
+#include "sound/pcm.h"
+#include "sound/info.h"
+#include "sound/ac97_codec.h"
+#include "sound/mpu401.h"
+#define SNDRV_GET_ID
+#include "sound/initval.h"
+
+MODULE_AUTHOR("Matt Wu <Matt_Wu@acersoftech.com.cn>");
+MODULE_DESCRIPTION("ALI M5451");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{ALI,M5451,pci},{ALI,M5451}}");
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+#ifdef TARGET_OS2
+static int pcm_channels[SNDRV_CARDS] = {REPEAT_SNDRV(32)};
+#else
+static int pcm_channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 32};
+#endif
+static int spdif[SNDRV_CARDS] = {REPEAT_SNDRV(0)};
+
+MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(index, "Index value for ALI M5451 PCI Audio.");
+MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC);
+MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+MODULE_PARM_DESC(id, "ID string for ALI M5451 PCI Audio.");
+MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC);
+MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(enable, "Enable ALI 5451 PCI Audio.");
+MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(pcm_channels, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(pcm_channels, "PCM Channels");
+MODULE_PARM_SYNTAX(pcm_channels, SNDRV_ENABLED ",default:32,allows:{{1,32}}");
+MODULE_PARM(spdif, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(spdif, "Support SPDIF I/O");
+MODULE_PARM_SYNTAX(spdif, SNDRV_ENABLED "," SNDRV_BOOLEAN_FALSE_DESC);
+
+/*
+ *  Debug part definitions
+ */
+
+//#define ALI_DEBUG
+
+#ifdef TARGET_OS2
+#ifdef ALI_DEBUG
+#define snd_ali_printk printk
+#else
+#define snd_ali_printk 1 ? (void)0 : (void)((int (*)(char *, ...)) NULL)
+#endif
+#else
+#ifdef ALI_DEBUG
+#define snd_ali_printk(a...) printk(a);
+#else
+#define snd_ali_printk(a...)
+#endif
+#endif
+
+/*
+ *  Constants definition
+ */
+
+#ifndef PCI_VENDOR_ID_ALI
+#define PCI_VENDOR_ID_ALI	0x10b9
+#endif
+
+#ifndef PCI_DEVICE_ID_ALI_5451
+#define PCI_DEVICE_ID_ALI_5451	0x5451
+#endif
+
+#define DEVICE_ID_ALI5451	((PCI_VENDOR_ID_ALI<<16)|PCI_DEVICE_ID_ALI_5451)
+
+
+#define ALI_CHANNELS		32
+
+#define ALI_PCM_IN_CHANNEL	31
+#define ALI_SPDIF_IN_CHANNEL	19
+#define ALI_SPDIF_OUT_CHANNEL	15
+#define ALI_CENTER_CHANNEL	24
+#define ALI_LEF_CHANNEL		23
+#define ALI_SURR_LEFT_CHANNEL	26
+#define ALI_SURR_RIGHT_CHANNEL	25
+
+#define	SNDRV_ALI_VOICE_TYPE_PCM	01
+#define SNDRV_ALI_VOICE_TYPE_OTH	02
+
+#define	ALI_5451_V02		0x02
+
+/*
+ *  Direct Registers
+ */
+
+#define ALI_LEGACY_DMAR0        0x00  // ADR0
+#define ALI_LEGACY_DMAR4        0x04  // CNT0
+#define ALI_LEGACY_DMAR11       0x0b  // MOD
+#define ALI_LEGACY_DMAR15       0x0f  // MMR
+#define ALI_MPUR0		0x20
+#define ALI_MPUR1		0x21
+#define ALI_MPUR2		0x22
+#define ALI_MPUR3		0x23
+
+#define	ALI_AC97_WRITE		0x40
+#define ALI_AC97_READ		0x44
+
+#define ALI_SCTRL		0x48
+#define   ALI_SPDIF_OUT_ENABLE		0x20
+#define ALI_AC97_GPIO		0x4c
+#define ALI_SPDIF_CS		0x70
+#define ALI_SPDIF_CTRL		0x74
+#define   ALI_SPDIF_IN_FUNC_ENABLE	0x02
+#define   ALI_SPDIF_IN_CH_STATUS	0x40
+#define   ALI_SPDIF_OUT_CH_STATUS	0xbf
+#define ALI_START		0x80
+#define ALI_STOP		0x84
+#define ALI_CSPF		0x90
+#define ALI_AINT		0x98
+#define ALI_GC_CIR		0xa0
+#define ENDLP_IE		0x00001000
+#define MIDLP_IE		0x00002000
+#define ALI_AINTEN		0xa4
+#define ALI_VOLUME		0xa8
+#define ALI_SBDELTA_DELTA_R     0xac
+#define ALI_MISCINT		0xb0
+#define ADDRESS_IRQ		0x00000020
+#define TARGET_REACHED		0x00008000
+#define MIXER_OVERFLOW		0x00000800
+#define MIXER_UNDERFLOW		0x00000400
+#define ALI_SBBL_SBCL           0xc0
+#define ALI_SBCTRL_SBE2R_SBDD   0xc4
+#define ALI_STIMER		0xc8
+#define ALI_GLOBAL_CONTROL	0xd4
+#define   ALI_SPDIF_OUT_SEL_PCM		0x00000400 /* bit 10 */
+#define   ALI_SPDIF_IN_SUPPORT		0x00000800 /* bit 11 */
+#define   ALI_SPDIF_OUT_CH_ENABLE	0x00008000 /* bit 15 */
+#define   ALI_SPDIF_IN_CH_ENABLE	0x00080000 /* bit 19 */
+#define   ALI_PCM_IN_ENABLE		0x80000000 /* bit 31 */
+
+#define ALI_CSO_ALPHA_FMS	0xe0
+#define ALI_LBA			0xe4
+#define ALI_ESO_DELTA		0xe8
+#define ALI_GVSEL_PAN_VOC_CTRL_EC	0xf0
+#define ALI_EBUF1		0xf4
+#define ALI_EBUF2		0xf8
+
+#define ALI_REG(codec, x) ((codec)->port + x)
+
+typedef struct snd_stru_ali ali_t;
+typedef struct snd_ali_stru_voice snd_ali_voice_t;
+
+typedef struct snd_ali_channel_control {
+    // register data
+    struct REGDATA {
+        unsigned int start;
+        unsigned int stop;
+        unsigned int aint;
+        unsigned int ainten;
+    } data;
+
+    // register addresses
+    struct REGS {
+        unsigned int start;
+        unsigned int stop;
+        unsigned int aint;
+        unsigned int ainten;
+        unsigned int ac97read;
+        unsigned int ac97write;
+    } regs;
+
+} snd_ali_channel_control_t;
+
+struct snd_ali_stru_voice {
+    unsigned int number;
+    int use: 1,
+    pcm: 1,
+    midi: 1,
+    mode: 1,
+    synth: 1;
+
+    /* PCM data */
+    ali_t *codec;
+    snd_pcm_substream_t *substream;
+    snd_ali_voice_t *extra;
+
+    int running: 1;
+
+    int eso;                /* final ESO value for channel */
+    int count;              /* runtime->period_size */
+
+    /* --- */
+
+    void *private_data;
+    void (*private_free)(void *private_data);
+};
+
+
+typedef struct snd_stru_alidev {
+
+    snd_ali_voice_t voices[ALI_CHANNELS];
+
+    unsigned int	chcnt;			/* num of opened channels */
+    unsigned int	chmap;			/* bitmap for opened channels */
+    unsigned int synthcount;
+
+} alidev_t;
+
+
+#ifdef CONFIG_PM
+#define ALI_GLOBAL_REGS		56
+#define ALI_CHANNEL_REGS	8
+typedef struct snd_ali_image {
+    unsigned long regs[ALI_GLOBAL_REGS];
+    unsigned long channel_regs[ALI_CHANNELS][ALI_CHANNEL_REGS];
+} ali_image_t;
+#endif
+
+
+struct snd_stru_ali {
+    unsigned long	irq;
+    unsigned long	port;
+    unsigned char	revision;
+
+    unsigned int hw_initialized: 1;
+    unsigned int spdif_support: 1;
+
+    struct pci_dev	*pci;
+    struct pci_dev	*pci_m1533;
+    struct pci_dev	*pci_m7101;
+
+    snd_card_t	*card;
+    snd_pcm_t	*pcm;
+    alidev_t	synth;
+    snd_ali_channel_control_t chregs;
+
+    /* S/PDIF Mask */
+    unsigned int	spdif_mask;
+
+    unsigned int spurious_irq_count;
+    unsigned int spurious_irq_max_delta;
+
+    ac97_bus_t *ac97_bus;
+    ac97_t *ac97;
+    unsigned short	ac97_ext_id;
+    unsigned short	ac97_ext_status;
+
+    spinlock_t	reg_lock;
+    spinlock_t	voice_alloc;
+
+#ifdef CONFIG_PM
+    ali_image_t *image;
+#endif
+};
+
+static struct pci_device_id snd_ali_ids[] = {
+    {0x10b9, 0x5451, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+    {0 }
+};
+
+MODULE_DEVICE_TABLE(pci, snd_ali_ids);
+
+static void snd_ali_clear_voices(ali_t *, unsigned int, unsigned int);
+static unsigned short snd_ali_codec_peek(ali_t *, int, unsigned short);
+static void snd_ali_codec_poke(ali_t *, int, unsigned short, unsigned short);
+
+/*
+ *  Debug Part
+ */
+
+#ifdef ALI_DEBUG
+
+static void ali_read_regs(ali_t *codec, int channel)
+{
+    int i,j;
+    unsigned int dwVal;
+
+    printk("channel %d registers map:\n", channel);
+    outb((unsigned char)(channel & 0x001f), ALI_REG(codec,ALI_GC_CIR));
+
+    printk("    ");
+    for(j=0;j<8;j++)
+        printk("%2.2x       ", j*4);
+    printk("\n");
+
+    for (i=0; i<=0xf8/4;i++) {
+        if(i%8 == 0)
+            printk("%2.2x  ", (i*4/0x10)*0x10);
+        dwVal = inl(ALI_REG(codec,i*4));
+        printk("%8.8x ", dwVal);
+        if ((i+1)%8 == 0)
+            printk("\n");
+    }
+    printk("\n");
+}
+static void ali_read_cfg(unsigned int vendor, unsigned deviceid)
+{
+    unsigned int dwVal;
+    struct pci_dev *pci_dev = NULL;
+    int i,j;
+
+
+    pci_dev = pci_find_device(vendor, deviceid, pci_dev);
+    if (pci_dev == NULL)
+        return ;
+
+    printk("\nM%x PCI CFG\n", deviceid);
+    printk("    ");
+    for(j=0;j<8;j++)
+        printk("%d        ",j);
+    printk("\n");
+
+    for(i=0;i<8;i++) {
+        printk("%d   ",i);
+        for(j=0;j<8;j++)
+        {
+            pci_read_config_dword(pci_dev, i*0x20+j*4, &dwVal);
+            printk("%8.8x ", dwVal);
+        }
+        printk("\n");
+    }
+}
+static void ali_read_ac97regs(ali_t *codec, int secondary)
+{
+    unsigned short i,j;
+    unsigned short wVal;
+
+    printk("\ncodec %d registers map:\n", secondary);
+
+    printk("    ");
+    for(j=0;j<8;j++)
+        printk("%2.2x   ",j*2);
+    printk("\n");
+
+    for (i=0; i<64;i++) {
+        if(i%8 == 0)
+            printk("%2.2x  ", (i/8)*0x10);
+        wVal = snd_ali_codec_peek(codec, secondary, i*2);
+        printk("%4.4x ", wVal);
+        if ((i+1)%8 == 0)
+            printk("\n");
+    }
+    printk("\n");
+}
+
+#endif
+
+/*
+ *  AC97 ACCESS
+ */
+
+static inline unsigned int snd_ali_5451_peek(ali_t *codec,
+                                             unsigned int port )
+{
+    return (unsigned int)inl(ALI_REG(codec, port));
+}
+
+static inline void snd_ali_5451_poke(	ali_t *codec,
+                                     unsigned int port,
+                                     unsigned int val )
+{
+    outl((unsigned int)val, ALI_REG(codec, port));
+}
+
+static int snd_ali_codec_ready(	ali_t *codec,
+                               unsigned int port,
+                               int sched )
+{
+    unsigned long end_time;
+    unsigned int res;
+
+    end_time = jiffies + 10 * msecs_to_jiffies(250);
+    do {
+        res = snd_ali_5451_peek(codec,port);
+        if (! (res & 0x8000))
+            return 0;
+        if (sched) {
+            set_current_state(TASK_UNINTERRUPTIBLE);
+            schedule_timeout(1);
+        }
+    } while (time_after_eq(end_time, jiffies));
+    snd_ali_5451_poke(codec, port, res & ~0x8000);
+    snd_printdd("ali_codec_ready: codec is not ready.\n ");
+    return -EIO;
+}
+
+static int snd_ali_stimer_ready(ali_t *codec, int sched)
+{
+    unsigned long end_time;
+    unsigned long dwChk1,dwChk2;
+
+    dwChk1 = snd_ali_5451_peek(codec, ALI_STIMER);
+    dwChk2 = snd_ali_5451_peek(codec, ALI_STIMER);
+
+    end_time = jiffies + 10 * msecs_to_jiffies(250);
+    do {
+        dwChk2 = snd_ali_5451_peek(codec, ALI_STIMER);
+        if (dwChk2 != dwChk1)
+            return 0;
+        if (sched) {
+            set_current_state(TASK_UNINTERRUPTIBLE);
+            schedule_timeout(1);
+        }
+    } while (time_after_eq(end_time, jiffies));
+    snd_printk("ali_stimer_read: stimer is not ready.\n");
+    return -EIO;
+}
+
+static void snd_ali_codec_poke(ali_t *codec,int secondary,
+                               unsigned short reg,
+                               unsigned short val)
+{
+    unsigned int dwVal = 0;
+    unsigned int port = 0;
+
+    if (reg >= 0x80) {
+        snd_printk("ali_codec_poke: reg(%xh) invalid.\n", reg);
+        return;
+    }
+
+    port = codec->chregs.regs.ac97write;
+
+    if (snd_ali_codec_ready(codec, port, 0) < 0)
+        return;
+    if (snd_ali_stimer_ready(codec, 0) < 0)
+        return;
+
+    dwVal  = (unsigned int) (reg & 0xff);
+    dwVal |= 0x8000 | (val << 16);
+    if (secondary) dwVal |= 0x0080;
+    if (codec->revision == ALI_5451_V02) dwVal |= 0x0100;
+
+    snd_ali_5451_poke(codec,port,dwVal);
+
+    return ;
+}
+
+static unsigned short snd_ali_codec_peek( ali_t *codec,
+                                         int secondary,
+                                         unsigned short reg)
+{
+    unsigned int dwVal = 0;
+    unsigned int port = 0;
+
+    if (reg >= 0x80) {
+        snd_printk("ali_codec_peek: reg(%xh) invalid.\n", reg);
+        return ~0;
+    }
+
+    port = codec->chregs.regs.ac97read;
+
+    if (snd_ali_codec_ready(codec, port, 0) < 0)
+        return ~0;
+    if (snd_ali_stimer_ready(codec, 0) < 0)
+        return ~0;
+
+    dwVal  = (unsigned int) (reg & 0xff);
+    dwVal |= 0x8000;				/* bit 15*/
+    if (secondary) dwVal |= 0x0080;
+
+    snd_ali_5451_poke(codec, port, dwVal);
+
+    if (snd_ali_stimer_ready(codec, 0) < 0)
+        return ~0;
+    if (snd_ali_codec_ready(codec, port, 0) < 0)
+        return ~0;
+
+    return (snd_ali_5451_peek(codec, port) & 0xffff0000)>>16;
+}
+
+static void snd_ali_codec_write(ac97_t *ac97,
+                                unsigned short reg,
+                                unsigned short val )
+{
+    ali_t *codec = ac97->private_data;
+
+    snd_ali_printk("codec_write: reg=%xh data=%xh.\n", reg, val);
+    snd_ali_codec_poke(codec, 0, reg, val);
+    return ;
+}
+
+
+static unsigned short snd_ali_codec_read(ac97_t *ac97, unsigned short reg)
+{
+    ali_t *codec = ac97->private_data;
+
+    snd_ali_printk("codec_read reg=%xh.\n", reg);
+    return (snd_ali_codec_peek(codec, 0, reg));
+}
+
+/*
+ *	AC97 Reset
+ */
+
+static int snd_ali_reset_5451(ali_t *codec)
+{
+    struct pci_dev *pci_dev = NULL;
+    unsigned short wCount, wReg;
+    unsigned int   dwVal;
+
+    if ((pci_dev = codec->pci_m1533) != NULL) {
+        pci_read_config_dword(pci_dev, 0x7c, &dwVal);
+        pci_write_config_dword(pci_dev, 0x7c, dwVal | 0x08000000);
+        udelay(5000);
+        pci_read_config_dword(pci_dev, 0x7c, &dwVal);
+        pci_write_config_dword(pci_dev, 0x7c, dwVal & 0xf7ffffff);
+        udelay(5000);
+    }
+
+    pci_dev = codec->pci;
+    pci_read_config_dword(pci_dev, 0x44, &dwVal);
+    pci_write_config_dword(pci_dev, 0x44, dwVal | 0x000c0000);
+    udelay(500);
+    pci_read_config_dword(pci_dev, 0x44, &dwVal);
+    pci_write_config_dword(pci_dev, 0x44, dwVal & 0xfffbffff);
+    udelay(5000);
+
+    wCount = 200;
+    while(wCount--) {
+        wReg = snd_ali_codec_peek(codec, 0, AC97_POWERDOWN);
+        if((wReg & 0x000f) == 0x000f)
+            return 0;
+        udelay(5000);
+    }
+
+    /* non-fatal if you have a non PM capable codec */
+    /* snd_printk(KERN_WARNING "ali5451: reset time out\n"); */
+    return 0;
+}
+
+#ifdef CODEC_RESET
+
+static int snd_ali_reset_codec(ali_t *codec)
+{
+    struct pci_dev *pci_dev = NULL;
+    unsigned char bVal = 0;
+    unsigned int   dwVal;
+    unsigned short wCount, wReg;
+
+    pci_dev = codec->pci_m1533;
+
+    pci_read_config_dword(pci_dev, 0x7c, &dwVal);
+    pci_write_config_dword(pci_dev, 0x7c, dwVal | 0x08000000);
+    udelay(5000);
+    pci_read_config_dword(pci_dev, 0x7c, &dwVal);
+    pci_write_config_dword(pci_dev, 0x7c, dwVal & 0xf7ffffff);
+    udelay(5000);
+
+    bVal = inb(ALI_REG(codec,ALI_SCTRL));
+    bVal |= 0x02;
+    outb(ALI_REG(codec,ALI_SCTRL),bVal);
+    udelay(5000);
+    bVal = inb(ALI_REG(codec,ALI_SCTRL));
+    bVal &= 0xfd;
+    outb(ALI_REG(codec,ALI_SCTRL),bVal);
+    udelay(15000);
+
+    wCount = 200;
+    while(wCount--) {
+        wReg = snd_ali_codec_read(codec->ac97, AC97_POWERDOWN);
+        if((wReg & 0x000f) == 0x000f)
+            return 0;
+        udelay(5000);
+    }
+    return -1;
+}
+
+#endif
+
+/*
+ *  ALI 5451 Controller
+ */
+
+static void snd_ali_enable_special_channel(ali_t *codec, unsigned int channel)
+{
+    unsigned long dwVal = 0;
+
+    dwVal  = inl(ALI_REG(codec,ALI_GLOBAL_CONTROL));
+    dwVal |= 1 << (channel & 0x0000001f);
+    outl(dwVal, ALI_REG(codec,ALI_GLOBAL_CONTROL));
+}
+
+static void snd_ali_disable_special_channel(ali_t *codec, unsigned int channel)
+{
+    unsigned long dwVal = 0;
+
+    dwVal  = inl(ALI_REG(codec,ALI_GLOBAL_CONTROL));
+    dwVal &= ~(1 << (channel & 0x0000001f));
+    outl(dwVal, ALI_REG(codec,ALI_GLOBAL_CONTROL));
+}
+
+static void snd_ali_enable_address_interrupt(ali_t * codec)
+{
+    unsigned int gc;
+
+    gc  = inl(ALI_REG(codec, ALI_GC_CIR));
+    gc |= ENDLP_IE;
+    gc |= MIDLP_IE;
+    outl( gc, ALI_REG(codec, ALI_GC_CIR));
+}
+
+static void snd_ali_disable_address_interrupt(ali_t * codec)
+{
+    unsigned int gc;
+
+    gc  = inl(ALI_REG(codec, ALI_GC_CIR));
+    gc &= ~ENDLP_IE;
+    gc &= ~MIDLP_IE;
+    outl(gc, ALI_REG(codec, ALI_GC_CIR));
+}
+
+#if 0 // not used
+static void snd_ali_enable_voice_irq(ali_t *codec, unsigned int channel)
+{
+    unsigned int mask;
+    snd_ali_channel_control_t *pchregs = &(codec->chregs);
+
+    snd_ali_printk("enable_voice_irq channel=%d\n",channel);
+
+    mask = 1 << (channel & 0x1f);
+    pchregs->data.ainten  = inl(ALI_REG(codec,pchregs->regs.ainten));
+    pchregs->data.ainten |= mask;
+    outl(pchregs->data.ainten,ALI_REG(codec,pchregs->regs.ainten));
+}
+#endif
+
+static void snd_ali_disable_voice_irq(ali_t *codec, unsigned int channel)
+{
+    unsigned int mask;
+    snd_ali_channel_control_t *pchregs = &(codec->chregs);
+
+    snd_ali_printk("disable_voice_irq channel=%d\n",channel);
+
+    mask = 1 << (channel & 0x1f);
+    pchregs->data.ainten  = inl(ALI_REG(codec,pchregs->regs.ainten));
+    pchregs->data.ainten &= ~mask;
+    outl(pchregs->data.ainten,ALI_REG(codec,pchregs->regs.ainten));
+}
+
+static int snd_ali_alloc_pcm_channel(ali_t *codec, int channel)
+{
+    unsigned int idx =  channel & 0x1f;
+
+    if (codec->synth.chcnt >= ALI_CHANNELS){
+        snd_printk("ali_alloc_pcm_channel: no free channels.\n");
+        return -1;
+    }
+
+    if (!(codec->synth.chmap & (1 << idx))) {
+        codec->synth.chmap |= 1 << idx;
+        codec->synth.chcnt++;
+        snd_ali_printk("alloc_pcm_channel no. %d.\n",idx);
+        return idx;
+    }
+    return -1;
+}
+
+static int snd_ali_find_free_channel(ali_t * codec, int rec)
+{
+    int idx;
+    int result = -1;
+
+    snd_ali_printk("find_free_channel: for %s\n",rec ? "rec" : "pcm");
+
+    // recording
+    if (rec) {
+        if (codec->spdif_support &&
+            (inl(ALI_REG(codec, ALI_GLOBAL_CONTROL)) & ALI_SPDIF_IN_SUPPORT))
+            idx = ALI_SPDIF_IN_CHANNEL;
+        else
+            idx = ALI_PCM_IN_CHANNEL;
+
+        if ((result = snd_ali_alloc_pcm_channel(codec,idx)) >= 0) {
+            return result;
+        } else {
+            snd_printk("ali_find_free_channel: record channel is busy now.\n");
+            return -1;
+        }
+    }
+
+    //playback...
+    if (codec->spdif_support &&
+        (inl(ALI_REG(codec, ALI_GLOBAL_CONTROL)) & ALI_SPDIF_OUT_CH_ENABLE)) {
+        idx = ALI_SPDIF_OUT_CHANNEL;
+        if ((result = snd_ali_alloc_pcm_channel(codec,idx)) >= 0) {
+            return result;
+        } else {
+            snd_printk("ali_find_free_channel: S/PDIF out channel is in busy now.\n");
+        }
+    }
+
+    for (idx = 0; idx < ALI_CHANNELS; idx++) {
+        if ((result = snd_ali_alloc_pcm_channel(codec,idx)) >= 0)
+            return result;
+    }
+    snd_printk("ali_find_free_channel: no free channels.\n");
+    return -1;
+}
+
+static void snd_ali_free_channel_pcm(ali_t *codec, int channel)
+{
+    unsigned int idx = channel & 0x0000001f;
+
+    snd_ali_printk("free_channel_pcm channel=%d\n",channel);
+
+    if (channel < 0 || channel >= ALI_CHANNELS)
+        return;
+
+    if (!(codec->synth.chmap & (1 << idx))) {
+        snd_printk("ali_free_channel_pcm: channel %d is not in use.\n",channel);
+        return;
+    } else {
+        codec->synth.chmap &= ~(1 << idx);
+        codec->synth.chcnt--;
+    }
+}
+
+#if 0 // not used
+static void snd_ali_start_voice(ali_t * codec, unsigned int channel)
+{
+    unsigned int mask = 1 << (channel & 0x1f);
+
+    snd_ali_printk("start_voice: channel=%d\n",channel);
+    outl(mask, ALI_REG(codec,codec->chregs.regs.start));
+}
+#endif
+
+static void snd_ali_stop_voice(ali_t * codec, unsigned int channel)
+{
+    unsigned int mask = 1 << (channel & 0x1f);
+
+    snd_ali_printk("stop_voice: channel=%d\n",channel);
+    outl(mask, ALI_REG(codec, codec->chregs.regs.stop));
+}
+
+/*
+ *    S/PDIF Part
+ */
+
+static void snd_ali_delay(ali_t *codec,int interval)
+{
+    unsigned long  begintimer,currenttimer;
+
+    begintimer   = inl(ALI_REG(codec, ALI_STIMER));
+    currenttimer = inl(ALI_REG(codec, ALI_STIMER));
+
+    while (currenttimer < begintimer + interval) {
+        if(snd_ali_stimer_ready(codec, 1) < 0)
+            break;
+        currenttimer = inl(ALI_REG(codec,  ALI_STIMER));
+    }
+}
+
+static void snd_ali_detect_spdif_rate(ali_t *codec)
+{
+    u16 wval  = 0;
+    u16 count = 0;
+    u8  bval = 0, R1 = 0, R2 = 0;
+
+    bval  = inb(ALI_REG(codec,ALI_SPDIF_CTRL + 1));
+    bval |= 0x1F;
+    outb(bval,ALI_REG(codec,ALI_SPDIF_CTRL + 1));
+
+    while (((R1 < 0x0B )||(R1 > 0x0E)) && (R1 != 0x12) && count <= 50000) {
+        count ++;
+        snd_ali_delay(codec, 6);
+        bval = inb(ALI_REG(codec,ALI_SPDIF_CTRL + 1));
+        R1 = bval & 0x1F;
+    }
+
+    if (count > 50000) {
+        snd_printk("ali_detect_spdif_rate: timeout!\n");
+        return;
+    }
+
+    count = 0;
+    while (count++ <= 50000) {
+        snd_ali_delay(codec, 6);
+        bval = inb(ALI_REG(codec,ALI_SPDIF_CTRL + 1));
+        R2 = bval & 0x1F;
+        if (R2 != R1) R1 = R2; else break;
+    }
+
+    if (count > 50000) {
+        snd_printk("ali_detect_spdif_rate: timeout!\n");
+        return;
+    }
+
+    if (R2 >= 0x0b && R2 <= 0x0e) {
+        wval  = inw(ALI_REG(codec,ALI_SPDIF_CTRL + 2));
+        wval &= 0xE0F0;
+        wval |= (u16)0x09 << 8 | (u16)0x05;
+        outw(wval,ALI_REG(codec,ALI_SPDIF_CTRL + 2));
+
+        bval  = inb(ALI_REG(codec,ALI_SPDIF_CS +3)) & 0xF0;
+        outb(bval|0x02,ALI_REG(codec,ALI_SPDIF_CS + 3));
+    } else if (R2 == 0x12) {
+        wval  = inw(ALI_REG(codec,ALI_SPDIF_CTRL + 2));
+        wval &= 0xE0F0;
+        wval |= (u16)0x0E << 8 | (u16)0x08;
+        outw(wval,ALI_REG(codec,ALI_SPDIF_CTRL + 2));
+
+        bval  = inb(ALI_REG(codec,ALI_SPDIF_CS +3)) & 0xF0;
+        outb(bval|0x03,ALI_REG(codec,ALI_SPDIF_CS + 3));
+    }
+}
+
+static unsigned int snd_ali_get_spdif_in_rate(ali_t *codec)
+{
+    u32	dwRate = 0;
+    u8	bval = 0;
+
+    bval  = inb(ALI_REG(codec,ALI_SPDIF_CTRL));
+    bval &= 0x7F;
+    bval |= 0x40;
+    outb(bval, ALI_REG(codec,ALI_SPDIF_CTRL));
+
+    snd_ali_detect_spdif_rate(codec);
+
+    bval  = inb(ALI_REG(codec,ALI_SPDIF_CS + 3));
+    bval &= 0x0F;
+
+    if (bval == 0) dwRate = 44100;
+    if (bval == 1) dwRate = 48000;
+    if (bval == 2) dwRate = 32000;
+
+    return dwRate;
+}
+
+static void snd_ali_enable_spdif_in(ali_t *codec)
+{
+    unsigned int dwVal;
+
+    dwVal = inl(ALI_REG(codec, ALI_GLOBAL_CONTROL));
+    dwVal |= ALI_SPDIF_IN_SUPPORT;
+    outl(dwVal, ALI_REG(codec, ALI_GLOBAL_CONTROL));
+
+    dwVal = inb(ALI_REG(codec, ALI_SPDIF_CTRL));
+    dwVal |= 0x02;
+    outb(dwVal, ALI_REG(codec, ALI_SPDIF_CTRL));
+
+    snd_ali_enable_special_channel(codec, ALI_SPDIF_IN_CHANNEL);
+}
+
+static void snd_ali_disable_spdif_in(ali_t *codec)
+{
+    unsigned int dwVal;
+
+    dwVal = inl(ALI_REG(codec, ALI_GLOBAL_CONTROL));
+    dwVal &= ~ALI_SPDIF_IN_SUPPORT;
+    outl(dwVal, ALI_REG(codec, ALI_GLOBAL_CONTROL));
+
+    snd_ali_disable_special_channel(codec, ALI_SPDIF_IN_CHANNEL);
+}
+
+
+static void snd_ali_set_spdif_out_rate(ali_t *codec, unsigned int rate)
+{
+    unsigned char  bVal;
+    unsigned int  dwRate = 0;
+
+    if (rate == 32000) dwRate = 0x300;
+    if (rate == 44100) dwRate = 0;
+    if (rate == 48000) dwRate = 0x200;
+
+    bVal  = inb(ALI_REG(codec, ALI_SPDIF_CTRL));
+    bVal &= (unsigned char)(~(1<<6));
+
+    bVal |= 0x80;		//select right
+    outb(bVal, ALI_REG(codec, ALI_SPDIF_CTRL));
+    outb(dwRate | 0x20, ALI_REG(codec, ALI_SPDIF_CS + 2));
+
+    bVal &= (~0x80);	//select left
+    outb(bVal, ALI_REG(codec, ALI_SPDIF_CTRL));
+    outw(rate | 0x10, ALI_REG(codec, ALI_SPDIF_CS + 2));
+}
+
+static void snd_ali_enable_spdif_out(ali_t *codec)
+{
+    unsigned short wVal;
+    unsigned char bVal;
+
+    struct pci_dev *pci_dev = NULL;
+
+    pci_dev = codec->pci_m1533;
+    if (pci_dev == NULL)
+        return;
+    pci_read_config_byte(pci_dev, 0x61, &bVal);
+    bVal |= 0x40;
+    pci_write_config_byte(pci_dev, 0x61, bVal);
+    pci_read_config_byte(pci_dev, 0x7d, &bVal);
+    bVal |= 0x01;
+    pci_write_config_byte(pci_dev, 0x7d, bVal);
+
+    pci_read_config_byte(pci_dev, 0x7e, &bVal);
+    bVal &= (~0x20);
+    bVal |= 0x10;
+    pci_write_config_byte(pci_dev, 0x7e, bVal);
+
+    bVal = inb(ALI_REG(codec, ALI_SCTRL));
+    outb(bVal | ALI_SPDIF_OUT_ENABLE, ALI_REG(codec, ALI_SCTRL));
+
+    bVal = inb(ALI_REG(codec, ALI_SPDIF_CTRL));
+    outb(bVal & ALI_SPDIF_OUT_CH_STATUS, ALI_REG(codec, ALI_SPDIF_CTRL));
+
+    {
+        wVal = inw(ALI_REG(codec, ALI_GLOBAL_CONTROL));
+        wVal |= ALI_SPDIF_OUT_SEL_PCM;
+        outw(wVal, ALI_REG(codec, ALI_GLOBAL_CONTROL));
+        snd_ali_disable_special_channel(codec,ALI_SPDIF_OUT_CHANNEL);
+    }
+}
+
+static void snd_ali_enable_spdif_chnout(ali_t *codec)
+{
+    unsigned short wVal = 0;
+
+    wVal  = inw(ALI_REG(codec, ALI_GLOBAL_CONTROL));
+    wVal &= ~ALI_SPDIF_OUT_SEL_PCM;
+    outw(wVal, ALI_REG(codec, ALI_GLOBAL_CONTROL));
+    /*
+     wVal = inw(ALI_REG(codec, ALI_SPDIF_CS));
+     if (flag & ALI_SPDIF_OUT_NON_PCM)
+     wVal |= 0x0002;
+     else
+     wVal &= (~0x0002);
+     outw(wVal, ALI_REG(codec, ALI_SPDIF_CS));
+     */
+    snd_ali_enable_special_channel(codec,ALI_SPDIF_OUT_CHANNEL);
+}
+
+static void snd_ali_disable_spdif_chnout(ali_t *codec)
+{
+    unsigned short wVal = 0;
+    wVal  = inw(ALI_REG(codec, ALI_GLOBAL_CONTROL));
+    wVal |= ALI_SPDIF_OUT_SEL_PCM;
+    outw(wVal, ALI_REG(codec, ALI_GLOBAL_CONTROL));
+
+    snd_ali_enable_special_channel(codec, ALI_SPDIF_OUT_CHANNEL);
+}
+
+static void snd_ali_disable_spdif_out(ali_t *codec)
+{
+    unsigned char  bVal;
+
+    bVal = inb(ALI_REG(codec, ALI_SCTRL));
+    outb(bVal & ~ALI_SPDIF_OUT_ENABLE, ALI_REG(codec, ALI_SCTRL));
+
+    snd_ali_disable_spdif_chnout(codec);
+}
+
+static void snd_ali_update_ptr(ali_t *codec,int channel)
+{
+    snd_ali_voice_t *pvoice = NULL;
+    snd_pcm_runtime_t *runtime;
+    snd_ali_channel_control_t *pchregs = NULL;
+    unsigned int old, mask;
+#ifdef ALI_DEBUG
+    unsigned int temp, cspf;
+#endif
+
+    pchregs = &(codec->chregs);
+
+    // check if interrupt occurred for channel
+    old  = pchregs->data.aint;
+    mask = ((unsigned int) 1L) << (channel & 0x1f);
+
+    if (!(old & mask))
+        return;
+
+    pvoice = &codec->synth.voices[channel];
+    runtime = pvoice->substream->runtime;
+
+    udelay(100);
+    spin_lock(&codec->reg_lock);
+
+    if (pvoice->pcm && pvoice->substream) {
+        /* pcm interrupt */
+#ifdef ALI_DEBUG
+        outb((u8)(pvoice->number), ALI_REG(codec, ALI_GC_CIR));
+        temp = inw(ALI_REG(codec, ALI_CSO_ALPHA_FMS + 2));
+        cspf = (inl(ALI_REG(codec, ALI_CSPF)) & mask) == mask;
+#endif
+        if (pvoice->running) {
+#ifndef TARGET_OS2
+            snd_ali_printk("update_ptr: cso=%4.4x cspf=%d.\n",(u16)temp,cspf);
+#endif
+            spin_unlock(&codec->reg_lock);
+            snd_pcm_period_elapsed(pvoice->substream);
+            spin_lock(&codec->reg_lock);
+        } else {
+            snd_ali_stop_voice(codec, channel);
+            snd_ali_disable_voice_irq(codec, channel);
+        }
+    } else if (codec->synth.voices[channel].synth) {
+        /* synth interrupt */
+    } else if (codec->synth.voices[channel].midi) {
+        /* midi interrupt */
+    } else {
+        /* unknown interrupt */
+        snd_ali_stop_voice(codec, channel);
+        snd_ali_disable_voice_irq(codec, channel);
+    }
+    spin_unlock(&codec->reg_lock);
+    outl(mask,ALI_REG(codec,pchregs->regs.aint));
+    pchregs->data.aint = old & (~mask);
+}
+
+static void snd_ali_interrupt(ali_t * codec)
+{
+    int channel;
+    unsigned int audio_int;
+    snd_ali_channel_control_t *pchregs = NULL;
+    pchregs = &(codec->chregs);
+
+    audio_int = inl(ALI_REG(codec, ALI_MISCINT));
+    if (audio_int & ADDRESS_IRQ) {
+        // get interrupt status for all channels
+        pchregs->data.aint = inl(ALI_REG(codec,pchregs->regs.aint));
+        for (channel = 0; channel < ALI_CHANNELS; channel++) {
+            snd_ali_update_ptr(codec, channel);
+        }
+    }
+    outl((TARGET_REACHED | MIXER_OVERFLOW | MIXER_UNDERFLOW),
+         ALI_REG(codec,ALI_MISCINT));
+}
+
+
+static irqreturn_t snd_ali_card_interrupt(int irq,
+                                          void *dev_id,
+                                          struct pt_regs *regs)
+{
+    ali_t   *codec = dev_id;
+#ifdef TARGET_OS2
+    int fOurIrq = FALSE;
+#endif
+
+    if (codec == NULL)
+        return IRQ_NONE;
+#ifdef TARGET_OS2
+    fOurIrq = TRUE;
+#endif
+    snd_ali_interrupt(codec);
+#ifdef TARGET_OS2
+    if (fOurIrq) {
+        eoi_irq(irq);
+    }
+#endif //TARGET_OS2
+
+    return IRQ_HANDLED;
+}
+
+
+static snd_ali_voice_t *snd_ali_alloc_voice(ali_t * codec, int type, int rec)
+{
+    snd_ali_voice_t *pvoice = NULL;
+    unsigned long flags;
+    int idx;
+
+    snd_ali_printk("alloc_voice: type=%d rec=%d\n",type,rec);
+
+    spin_lock_irqsave(&codec->voice_alloc, flags);
+    if (type == SNDRV_ALI_VOICE_TYPE_PCM) {
+        idx = snd_ali_find_free_channel(codec,rec);
+        if(idx < 0) {
+            snd_printk("ali_alloc_voice: err.\n");
+            spin_unlock_irqrestore(&codec->voice_alloc, flags);
+            return NULL;
+        }
+        pvoice = &(codec->synth.voices[idx]);
+        pvoice->use = 1;
+        pvoice->pcm = 1;
+        pvoice->mode = rec;
+        spin_unlock_irqrestore(&codec->voice_alloc, flags);
+        return pvoice;
+    }
+    spin_unlock_irqrestore(&codec->voice_alloc, flags);
+    return NULL;
+}
+
+
+static void snd_ali_free_voice(ali_t * codec, snd_ali_voice_t *pvoice)
+{
+    unsigned long flags;
+    void (*private_free)(void *);
+    void *private_data;
+
+    snd_ali_printk("free_voice: channel=%d\n",pvoice->number);
+    if (pvoice == NULL || !pvoice->use)
+        return;
+    snd_ali_clear_voices(codec, pvoice->number, pvoice->number);
+    spin_lock_irqsave(&codec->voice_alloc, flags);
+    private_free = pvoice->private_free;
+    private_data = pvoice->private_data;
+    pvoice->private_free = NULL;
+    pvoice->private_data = NULL;
+    if (pvoice->pcm) {
+        snd_ali_free_channel_pcm(codec, pvoice->number);
+    }
+    pvoice->use = pvoice->pcm = pvoice->synth = 0;
+    pvoice->substream = NULL;
+    spin_unlock_irqrestore(&codec->voice_alloc, flags);
+    if (private_free)
+        private_free(private_data);
+}
+
+
+static void snd_ali_clear_voices(ali_t * codec,
+                                 unsigned int v_min,
+                                 unsigned int v_max)
+{
+    unsigned int i;
+
+    for (i = v_min; i <= v_max; i++) {
+        snd_ali_stop_voice(codec, i);
+        snd_ali_disable_voice_irq(codec, i);
+    }
+}
+
+static void snd_ali_write_voice_regs(ali_t * codec,
+                                     unsigned int Channel,
+                                     unsigned int LBA,
+                                     unsigned int CSO,
+                                     unsigned int ESO,
+                                     unsigned int DELTA,
+                                     unsigned int ALPHA_FMS,
+                                     unsigned int GVSEL,
+                                     unsigned int PAN,
+                                     unsigned int VOL,
+                                     unsigned int CTRL,
+                                     unsigned int EC)
+{
+    unsigned int ctlcmds[4];
+
+    outb((unsigned char)(Channel & 0x001f),ALI_REG(codec,ALI_GC_CIR));
+
+    ctlcmds[0] =  (CSO << 16) | (ALPHA_FMS & 0x0000ffff);
+    ctlcmds[1] =  LBA;
+    ctlcmds[2] =  (ESO << 16) | (DELTA & 0x0ffff);
+    ctlcmds[3] =  (GVSEL << 31) |
+        ((PAN & 0x0000007f) << 24) |
+        ((VOL & 0x000000ff) << 16) |
+        ((CTRL & 0x0000000f) << 12) |
+        (EC & 0x00000fff);
+
+    outb(Channel, ALI_REG(codec, ALI_GC_CIR));
+
+    outl(ctlcmds[0], ALI_REG(codec,ALI_CSO_ALPHA_FMS));
+    outl(ctlcmds[1], ALI_REG(codec,ALI_LBA));
+    outl(ctlcmds[2], ALI_REG(codec,ALI_ESO_DELTA));
+    outl(ctlcmds[3], ALI_REG(codec,ALI_GVSEL_PAN_VOC_CTRL_EC));
+
+    outl(0x30000000, ALI_REG(codec, ALI_EBUF1));	/* Still Mode */
+    outl(0x30000000, ALI_REG(codec, ALI_EBUF2));	/* Still Mode */
+}
+
+static unsigned int snd_ali_convert_rate(unsigned int rate, int rec)
+{
+    unsigned int delta;
+
+    if (rate < 4000)  rate = 4000;
+    if (rate > 48000) rate = 48000;
+
+    if (rec) {
+        if (rate == 44100)
+            delta = 0x116a;
+        else if (rate == 8000)
+            delta = 0x6000;
+        else if (rate == 48000)
+            delta = 0x1000;
+        else
+            delta = ((48000 << 12) / rate) & 0x0000ffff;
+    } else {
+        if (rate == 44100)
+            delta = 0xeb3;
+        else if (rate == 8000)
+            delta = 0x2ab;
+        else if (rate == 48000)
+            delta = 0x1000;
+        else
+            delta = (((rate << 12) + rate) / 48000) & 0x0000ffff;
+    }
+
+    return delta;
+}
+
+static unsigned int snd_ali_control_mode(snd_pcm_substream_t *substream)
+{
+    unsigned int CTRL;
+    snd_pcm_runtime_t *runtime = substream->runtime;
+
+    /* set ctrl mode
+     CTRL default: 8-bit (unsigned) mono, loop mode enabled
+     */
+    CTRL = 0x00000001;
+    if (snd_pcm_format_width(runtime->format) == 16)
+        CTRL |= 0x00000008;	// 16-bit data
+    if (!snd_pcm_format_unsigned(runtime->format))
+        CTRL |= 0x00000002;	// signed data
+    if (runtime->channels > 1)
+        CTRL |= 0x00000004;	// stereo data
+    return CTRL;
+}
+
+/*
+ *  PCM part
+ */
+
+static int snd_ali_ioctl(snd_pcm_substream_t * substream,
+                         unsigned int cmd, void *arg)
+{
+    return snd_pcm_lib_ioctl(substream, cmd, arg);
+}
+
+static int snd_ali_trigger(snd_pcm_substream_t *substream,
+                           int cmd)
+
+{
+    ali_t *codec = snd_pcm_substream_chip(substream);
+    struct list_head *pos;
+    snd_pcm_substream_t *s;
+    unsigned int what, whati, capture_flag;
+    snd_ali_voice_t *pvoice = NULL, *evoice = NULL;
+    unsigned int val;
+    int do_start;
+
+    switch (cmd) {
+    case SNDRV_PCM_TRIGGER_START:
+    case SNDRV_PCM_TRIGGER_RESUME:
+        do_start = 1; break;
+    case SNDRV_PCM_TRIGGER_STOP:
+    case SNDRV_PCM_TRIGGER_SUSPEND:
+        do_start = 0; break;
+    default:
+        return -EINVAL;
+    }
+
+    what = whati = capture_flag = 0;
+    snd_pcm_group_for_each(pos, substream) {
+        s = snd_pcm_group_substream_entry(pos);
+        if ((ali_t *) snd_pcm_substream_chip(s) == codec) {
+            pvoice = (snd_ali_voice_t *) s->runtime->private_data;
+            evoice = pvoice->extra;
+            what |= 1 << (pvoice->number & 0x1f);
+            if (evoice == NULL) {
+                whati |= 1 << (pvoice->number & 0x1f);
+            } else {
+                whati |= 1 << (evoice->number & 0x1f);
+                what |= 1 << (evoice->number & 0x1f);
+            }
+            if (do_start) {
+                pvoice->running = 1;
+                if (evoice != NULL)
+                    evoice->running = 1;
+            } else {
+                pvoice->running = 0;
+                if (evoice != NULL)
+                    evoice->running = 0;
+            }
+            snd_pcm_trigger_done(s, substream);
+            if (pvoice->mode)
+                capture_flag = 1;
+        }
+    }
+    spin_lock(&codec->reg_lock);
+    if (! do_start) {
+        outl(what, ALI_REG(codec, ALI_STOP));
+    }
+    val = inl(ALI_REG(codec, ALI_AINTEN));
+    if (do_start) {
+        val |= whati;
+    } else {
+        val &= ~whati;
+    }
+    outl(val, ALI_REG(codec, ALI_AINTEN));
+    if (do_start) {
+        outl(what, ALI_REG(codec, ALI_START));
+    }
+    snd_ali_printk("trigger: what=%xh whati=%xh\n",what,whati);
+    spin_unlock(&codec->reg_lock);
+
+    return 0;
+}
+
+static int snd_ali_playback_hw_params(snd_pcm_substream_t * substream,
+                                      snd_pcm_hw_params_t * hw_params)
+{
+    ali_t *codec = snd_pcm_substream_chip(substream);
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    snd_ali_voice_t *pvoice = (snd_ali_voice_t *) runtime->private_data;
+    snd_ali_voice_t *evoice = pvoice->extra;
+    int err;
+    err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+    if (err < 0) return err;
+
+    /* voice management */
+
+    if (params_buffer_size(hw_params)/2 != params_period_size(hw_params)) {
+        if (evoice == NULL) {
+            evoice = snd_ali_alloc_voice(codec, SNDRV_ALI_VOICE_TYPE_PCM, 0);
+            if (evoice == NULL)
+                return -ENOMEM;
+            pvoice->extra = evoice;
+            evoice->substream = substream;
+        }
+    } else {
+        if (evoice != NULL) {
+            snd_ali_free_voice(codec, evoice);
+            pvoice->extra = evoice = NULL;
+        }
+    }
+
+    return 0;
+}
+
+static int snd_ali_playback_hw_free(snd_pcm_substream_t * substream)
+{
+    ali_t *codec = snd_pcm_substream_chip(substream);
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    snd_ali_voice_t *pvoice = (snd_ali_voice_t *) runtime->private_data;
+    snd_ali_voice_t *evoice = pvoice ? pvoice->extra : NULL;
+
+    snd_pcm_lib_free_pages(substream);
+    if (evoice != NULL) {
+        snd_ali_free_voice(codec, evoice);
+        pvoice->extra = NULL;
+    }
+    return 0;
+}
+
+static int snd_ali_capture_hw_params(snd_pcm_substream_t * substream,
+                                     snd_pcm_hw_params_t * hw_params)
+{
+    return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+static int snd_ali_capture_hw_free(snd_pcm_substream_t * substream)
+{
+    return snd_pcm_lib_free_pages(substream);
+}
+
+static int snd_ali_playback_prepare(snd_pcm_substream_t * substream)
+{
+    ali_t *codec = snd_pcm_substream_chip(substream);
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    snd_ali_voice_t *pvoice = (snd_ali_voice_t *) runtime->private_data;
+    snd_ali_voice_t *evoice = pvoice->extra;
+    unsigned long flags;
+
+    unsigned int LBA;
+    unsigned int Delta;
+    unsigned int ESO;
+    unsigned int CTRL;
+    unsigned int GVSEL;
+    unsigned int PAN;
+    unsigned int VOL;
+    unsigned int EC;
+
+    snd_ali_printk("playback_prepare ...\n");
+
+    spin_lock_irqsave(&codec->reg_lock, flags);
+
+    /* set Delta (rate) value */
+    Delta = snd_ali_convert_rate(runtime->rate, 0);
+
+    if ((pvoice->number == ALI_SPDIF_IN_CHANNEL) ||
+        (pvoice->number == ALI_PCM_IN_CHANNEL))
+        snd_ali_disable_special_channel(codec, pvoice->number);
+    else if (codec->spdif_support &&
+             (inl(ALI_REG(codec, ALI_GLOBAL_CONTROL)) & ALI_SPDIF_OUT_CH_ENABLE)
+             && (pvoice->number == ALI_SPDIF_OUT_CHANNEL)) {
+        snd_ali_set_spdif_out_rate(codec, runtime->rate);
+        Delta = 0x1000;
+    }
+
+    /* set Loop Back Address */
+    LBA = runtime->dma_addr;
+
+    /* set interrupt count size */
+    pvoice->count = runtime->period_size;
+
+    /* set target ESO for channel */
+    pvoice->eso = runtime->buffer_size;
+
+    snd_ali_printk("playback_prepare: eso=%xh count=%xh\n",pvoice->eso,pvoice->count);
+
+    /* set ESO to capture first MIDLP interrupt */
+    ESO = pvoice->eso -1;
+    /* set ctrl mode */
+    CTRL = snd_ali_control_mode(substream);
+
+    GVSEL = 1;
+    PAN = 0;
+    VOL = 0;
+    EC = 0;
+    snd_ali_printk("playback_prepare:\n    ch=%d, Rate=%d Delta=%xh,GVSEL=%xh,PAN=%xh,CTRL=%xh\n",pvoice->number,runtime->rate,Delta,GVSEL,PAN,CTRL);
+    snd_ali_write_voice_regs(    codec,
+                             pvoice->number,
+                             LBA,
+                             0,	/* cso */
+                             ESO,
+                             Delta,
+                             0,	/* alpha */
+                             GVSEL,
+                             PAN,
+                             VOL,
+                             CTRL,
+                             EC);
+    if (evoice != NULL) {
+        evoice->count = pvoice->count;
+        evoice->eso = pvoice->count << 1;
+        ESO = evoice->eso - 1;
+        snd_ali_write_voice_regs(codec,
+                                 evoice->number,
+                                 LBA,
+                                 0,	/* cso */
+                                 ESO,
+                                 Delta,
+                                 0,	/* alpha */
+                                 GVSEL,
+                                 (unsigned int)0x7f,
+                                 (unsigned int)0x3ff,
+                                 CTRL,
+                                 EC);
+    }
+    spin_unlock_irqrestore(&codec->reg_lock, flags);
+    return 0;
+}
+
+
+static int snd_ali_capture_prepare(snd_pcm_substream_t * substream)
+{
+    ali_t *codec = snd_pcm_substream_chip(substream);
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    snd_ali_voice_t *pvoice = (snd_ali_voice_t *) runtime->private_data;
+    unsigned long flags;
+    unsigned int LBA;
+    unsigned int Delta;
+    unsigned int ESO;
+    unsigned int CTRL;
+    unsigned int GVSEL;
+    unsigned int PAN;
+    unsigned int VOL;
+    unsigned int EC;
+    u8	 bValue;
+
+    spin_lock_irqsave(&codec->reg_lock, flags);
+
+    snd_ali_printk("capture_prepare...\n");
+
+    snd_ali_enable_special_channel(codec,pvoice->number);
+
+    Delta = snd_ali_convert_rate(runtime->rate, 1);
+
+    // Prepare capture intr channel
+    if (pvoice->number == ALI_SPDIF_IN_CHANNEL) {
+
+        unsigned int rate;
+
+        if (codec->revision != ALI_5451_V02) {
+            spin_unlock_irqrestore(&codec->reg_lock, flags);
+            return -1;
+        }
+        rate = snd_ali_get_spdif_in_rate(codec);
+        if (rate == 0) {
+            snd_printk("ali_capture_preapre: spdif rate detect err!\n");
+            rate = 48000;
+        }
+        bValue = inb(ALI_REG(codec,ALI_SPDIF_CTRL));
+        if (bValue & 0x10) {
+            outb(bValue,ALI_REG(codec,ALI_SPDIF_CTRL));
+            printk("clear SPDIF parity error flag.\n");
+        }
+
+        if (rate != 48000)
+            Delta = ((rate << 12)/runtime->rate)&0x00ffff;
+    }
+
+    // set target ESO for channel
+    pvoice->eso = runtime->buffer_size;
+
+    // set interrupt count size
+    pvoice->count = runtime->period_size;
+
+    // set Loop Back Address
+    LBA = runtime->dma_addr;
+
+    // set ESO to capture first MIDLP interrupt
+    ESO = pvoice->eso - 1;
+    CTRL = snd_ali_control_mode(substream);
+    GVSEL = 0;
+    PAN = 0x00;
+    VOL = 0x00;
+    EC = 0;
+
+    snd_ali_write_voice_regs(    codec,
+                             pvoice->number,
+                             LBA,
+                             0,	/* cso */
+                             ESO,
+                             Delta,
+                             0,	/* alpha */
+                             GVSEL,
+                             PAN,
+                             VOL,
+                             CTRL,
+                             EC);
+
+
+    spin_unlock_irqrestore(&codec->reg_lock, flags);
+
+    return 0;
+}
+
+
+static snd_pcm_uframes_t snd_ali_playback_pointer(snd_pcm_substream_t *substream)
+{
+    ali_t *codec = snd_pcm_substream_chip(substream);
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    snd_ali_voice_t *pvoice = (snd_ali_voice_t *) runtime->private_data;
+    unsigned int cso;
+
+    spin_lock(&codec->reg_lock);
+    if (!pvoice->running) {
+        spin_unlock(&codec->reg_lock);
+        return 0;
+    }
+    outb(pvoice->number, ALI_REG(codec, ALI_GC_CIR));
+    cso = inw(ALI_REG(codec, ALI_CSO_ALPHA_FMS + 2));
+    spin_unlock(&codec->reg_lock);
+    snd_ali_printk("playback pointer returned cso=%xh.\n", cso);
+
+    return cso;
+}
+
+
+static snd_pcm_uframes_t snd_ali_capture_pointer(snd_pcm_substream_t *substream)
+{
+    ali_t *codec = snd_pcm_substream_chip(substream);
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    snd_ali_voice_t *pvoice = (snd_ali_voice_t *) runtime->private_data;
+    unsigned int cso;
+    unsigned long flags;
+
+    spin_lock_irqsave(&codec->reg_lock, flags);
+    if (!pvoice->running) {
+        spin_unlock_irqrestore(&codec->reg_lock, flags);
+        return 0;
+    }
+    outb(pvoice->number, ALI_REG(codec, ALI_GC_CIR));
+    cso = inw(ALI_REG(codec, ALI_CSO_ALPHA_FMS + 2));
+    spin_unlock_irqrestore(&codec->reg_lock, flags);
+
+    return cso;
+}
+
+static snd_pcm_hardware_t snd_ali_playback =
+{
+    /*	info:		  */	(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+                                 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+                                 SNDRV_PCM_INFO_MMAP_VALID |
+                                 SNDRV_PCM_INFO_RESUME |
+                                 SNDRV_PCM_INFO_SYNC_START),
+                                 /*	formats:	  */	(SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE |
+                                                                 SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE),
+                                                                 /*	rates:		  */	SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+                                                                 /*	rate_min:	  */	4000,
+                                                                 /*	rate_max:	  */	48000,
+                                                                 /*	channels_min:	  */	1,
+                                                                 /*	channels_max:	  */	2,
+                                                                 /*	buffer_bytes_max:  */	(256*1024),
+                                                                 /*	period_bytes_min:  */	64,
+                                                                 /*	period_bytes_max:  */	(256*1024),
+                                                                 /*	periods_min:	  */	1,
+                                                                 /*	periods_max:	  */	1024,
+                                                                 /*	fifo_size:	  */	0,
+};
+
+/*
+ *  Capture support device description
+ */
+
+static snd_pcm_hardware_t snd_ali_capture =
+{
+    /*	info:		  */	(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+                                 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+                                 SNDRV_PCM_INFO_MMAP_VALID |
+                                 SNDRV_PCM_INFO_RESUME |
+                                 SNDRV_PCM_INFO_SYNC_START),
+                                 /*	formats:	  */	(SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE |
+                                                                 SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE),
+                                                                 /*	rates:		  */	SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+                                                                 /*	rate_min:	  */	4000,
+                                                                 /*	rate_max:	  */	48000,
+                                                                 /*	channels_min:	  */	1,
+                                                                 /*	channels_max:	  */	2,
+                                                                 /*	buffer_bytes_max:  */	(128*1024),
+                                                                 /*	period_bytes_min:  */	64,
+                                                                 /*	period_bytes_max:  */	(128*1024),
+                                                                 /*	periods_min:	  */	1,
+                                                                 /*	periods_max:	  */	1024,
+                                                                 /*	fifo_size:	  */	0,
+};
+
+static void snd_ali_pcm_free_substream(snd_pcm_runtime_t *runtime)
+{
+    unsigned long flags;
+    snd_ali_voice_t *pvoice = (snd_ali_voice_t *) runtime->private_data;
+    ali_t *codec;
+
+    if (pvoice) {
+        codec = pvoice->codec;
+        spin_lock_irqsave(&codec->reg_lock, flags);
+        snd_ali_free_voice(pvoice->codec, pvoice);
+        spin_unlock_irqrestore(&codec->reg_lock, flags);
+    }
+}
+
+static int snd_ali_playback_open(snd_pcm_substream_t * substream)
+{
+    ali_t *codec = snd_pcm_substream_chip(substream);
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    snd_ali_voice_t *pvoice;
+    unsigned long flags = 0;
+
+    spin_lock_irqsave(&codec->reg_lock, flags);
+    pvoice = snd_ali_alloc_voice(codec, SNDRV_ALI_VOICE_TYPE_PCM, 0);
+    if (pvoice == NULL) {
+        spin_unlock_irqrestore(&codec->reg_lock, flags);
+        return -EAGAIN;
+    }
+    pvoice->codec = codec;
+    spin_unlock_irqrestore(&codec->reg_lock, flags);
+
+    pvoice->substream = substream;
+    runtime->private_data = pvoice;
+    runtime->private_free = snd_ali_pcm_free_substream;
+
+    runtime->hw = snd_ali_playback;
+    snd_pcm_set_sync(substream);
+    snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 64*1024);
+    return 0;
+}
+
+
+static int snd_ali_capture_open(snd_pcm_substream_t * substream)
+{
+    ali_t *codec = snd_pcm_substream_chip(substream);
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    snd_ali_voice_t *pvoice;
+    unsigned long flags;
+
+    spin_lock_irqsave(&codec->reg_lock, flags);
+    pvoice = snd_ali_alloc_voice(codec, SNDRV_ALI_VOICE_TYPE_PCM, 1);
+    if (pvoice == NULL) {
+        spin_unlock_irqrestore(&codec->reg_lock, flags);
+        return -EAGAIN;
+    }
+    pvoice->codec = codec;
+    spin_unlock_irqrestore(&codec->reg_lock, flags);
+
+    pvoice->substream = substream;
+    runtime->private_data = pvoice;
+    runtime->private_free = snd_ali_pcm_free_substream;
+    runtime->hw = snd_ali_capture;
+    snd_pcm_set_sync(substream);
+    snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 64*1024);
+    return 0;
+}
+
+
+static int snd_ali_playback_close(snd_pcm_substream_t * substream)
+{
+    return 0;
+}
+
+static int snd_ali_capture_close(snd_pcm_substream_t * substream)
+{
+    ali_t *codec = snd_pcm_substream_chip(substream);
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    snd_ali_voice_t *pvoice = (snd_ali_voice_t *) runtime->private_data;
+
+    snd_ali_disable_special_channel(codec,pvoice->number);
+
+    return 0;
+}
+
+#ifdef TARGET_OS2
+static snd_pcm_ops_t snd_ali_playback_ops = {
+    snd_ali_playback_open,
+    snd_ali_playback_close,
+    snd_ali_ioctl,
+    snd_ali_playback_hw_params,
+    snd_ali_playback_hw_free,
+    snd_ali_playback_prepare,
+    snd_ali_trigger,
+    snd_ali_playback_pointer,0,0
+};
+
+static snd_pcm_ops_t snd_ali_capture_ops = {
+    snd_ali_capture_open,
+    snd_ali_capture_close,
+    snd_ali_ioctl,
+    snd_ali_capture_hw_params,
+    snd_ali_capture_hw_free,
+    snd_ali_capture_prepare,
+    snd_ali_trigger,
+    snd_ali_capture_pointer,0,0
+};
+#else
+static snd_pcm_ops_t snd_ali_playback_ops = {
+open:		snd_ali_playback_open,
+    close:		snd_ali_playback_close,
+    ioctl:		snd_ali_ioctl,
+    hw_params:	snd_ali_playback_hw_params,
+    hw_free:	snd_ali_playback_hw_free,
+    prepare:	snd_ali_playback_prepare,
+    trigger:	snd_ali_trigger,
+    pointer:	snd_ali_playback_pointer,
+};
+
+static snd_pcm_ops_t snd_ali_capture_ops = {
+open:		snd_ali_capture_open,
+    close:		snd_ali_capture_close,
+    ioctl:		snd_ali_ioctl,
+    hw_params:	snd_ali_capture_hw_params,
+    hw_free:	snd_ali_capture_hw_free,
+    prepare:	snd_ali_capture_prepare,
+    trigger:	snd_ali_trigger,
+    pointer:	snd_ali_capture_pointer,
+};
+#endif
+
+static void snd_ali_pcm_free(snd_pcm_t *pcm)
+{
+    ali_t *codec = pcm->private_data;
+    codec->pcm = NULL;
+}
+
+static int __devinit snd_ali_pcm(ali_t * codec, int device, snd_pcm_t ** rpcm)
+{
+	snd_pcm_t *pcm;
+	int err;
+
+	if (rpcm) *rpcm = NULL;
+	err = snd_pcm_new(codec->card, "ALI 5451", device, ALI_CHANNELS, 1, &pcm);
+	if (err < 0) {
+		snd_printk("snd_ali_pcm: err called snd_pcm_new.\n");
+		return err;
+	}
+	pcm->private_data = codec;
+	pcm->private_free = snd_ali_pcm_free;
+	pcm->info_flags = 0;
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ali_playback_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ali_capture_ops);
+
+	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+					      snd_dma_pci_data(codec->pci), 64*1024, 128*1024);
+
+	pcm->info_flags = 0;
+	pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
+	strcpy(pcm->name, "ALI 5451");
+	codec->pcm = pcm;
+	if (rpcm) *rpcm = pcm;
+	return 0;
+}
+
+
+#ifdef TARGET_OS2
+#define ALI5451_SPDIF(xname, xindex, value) \
+    { SNDRV_CTL_ELEM_IFACE_MIXER, 0,0, xname, xindex,\
+    0, 0, snd_ali5451_spdif_info, snd_ali5451_spdif_get, \
+    snd_ali5451_spdif_put, value}
+#else
+#define ALI5451_SPDIF(xname, xindex, value) \
+    { iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex,\
+    info: snd_ali5451_spdif_info, get: snd_ali5451_spdif_get, \
+    put: snd_ali5451_spdif_put, private_value: value}
+#endif
+
+static int snd_ali5451_spdif_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_ali5451_spdif_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+    unsigned long flags;
+    ali_t *codec = kcontrol->private_data;
+    unsigned int enable;
+
+    enable = ucontrol->value.integer.value[0] ? 1 : 0;
+
+    spin_lock_irqsave(&codec->reg_lock, flags);
+    switch(kcontrol->private_value) {
+    case 0:
+        enable = (codec->spdif_mask & 0x02) ? 1 : 0;
+        break;
+    case 1:
+        enable = ((codec->spdif_mask & 0x02) && (codec->spdif_mask & 0x04)) ? 1 : 0;
+        break;
+    case 2:
+        enable = (codec->spdif_mask & 0x01) ? 1 : 0;
+        break;
+    default:
+        break;
+    }
+    ucontrol->value.integer.value[0] = enable;
+    spin_unlock_irqrestore(&codec->reg_lock, flags);
+    return 0;
+}
+
+static int snd_ali5451_spdif_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+    unsigned long flags;
+    ali_t *codec = kcontrol->private_data;
+    unsigned int change = 0, enable = 0;
+
+    enable = ucontrol->value.integer.value[0] ? 1 : 0;
+
+    spin_lock_irqsave(&codec->reg_lock, flags);
+    switch (kcontrol->private_value) {
+    case 0:
+        change = (codec->spdif_mask & 0x02) ? 1 : 0;
+        change = change ^ enable;
+        if (change) {
+            if (enable) {
+                codec->spdif_mask |= 0x02;
+                snd_ali_enable_spdif_out(codec);
+            } else {
+                codec->spdif_mask &= ~(0x02);
+                codec->spdif_mask &= ~(0x04);
+                snd_ali_disable_spdif_out(codec);
+            }
+        }
+        break;
+    case 1:
+        change = (codec->spdif_mask & 0x04) ? 1 : 0;
+        change = change ^ enable;
+        if (change && (codec->spdif_mask & 0x02)) {
+            if (enable) {
+                codec->spdif_mask |= 0x04;
+                snd_ali_enable_spdif_chnout(codec);
+            } else {
+                codec->spdif_mask &= ~(0x04);
+                snd_ali_disable_spdif_chnout(codec);
+            }
+        }
+        break;
+    case 2:
+        change = (codec->spdif_mask & 0x01) ? 1 : 0;
+        change = change ^ enable;
+        if (change) {
+            if (enable) {
+                codec->spdif_mask |= 0x01;
+                snd_ali_enable_spdif_in(codec);
+            } else {
+                codec->spdif_mask &= ~(0x01);
+                snd_ali_disable_spdif_in(codec);
+            }
+        }
+        break;
+    default:
+        break;
+    }
+    spin_unlock_irqrestore(&codec->reg_lock, flags);
+
+    return change;
+}
+
+static snd_kcontrol_new_t snd_ali5451_mixer_spdif[] __devinitdata = {
+    ALI5451_SPDIF("S/PDIF Out", 0, 0),
+    ALI5451_SPDIF("S/PDIF Out to S/PDIF Channel",0, 1),
+    ALI5451_SPDIF("S/PDIF In from S/PDIF Channel",0, 2) };
+
+static void snd_ali_mixer_free_ac97_bus(ac97_bus_t *bus)
+{
+    ali_t *codec = bus->private_data;
+    codec->ac97_bus = NULL;
+}
+
+static void snd_ali_mixer_free_ac97(ac97_t *ac97)
+{
+    ali_t *codec = ac97->private_data;
+    codec->ac97 = NULL;
+}
+
+static int __devinit snd_ali_mixer(ali_t * codec)
+{
+    ac97_template_t ac97;
+    unsigned int idx;
+    int err;
+    static ac97_bus_ops_t ops = {
+        0,snd_ali_codec_write,
+        snd_ali_codec_read,0,0
+    };
+
+    if ((err = snd_ac97_bus(codec->card, 0, &ops, codec, &codec->ac97_bus)) < 0)
+        return err;
+    codec->ac97_bus->private_free = snd_ali_mixer_free_ac97_bus;
+
+    memset(&ac97, 0, sizeof(ac97));
+    ac97.private_data = codec;
+    ac97.private_free = snd_ali_mixer_free_ac97;
+    if ((err = snd_ac97_mixer(codec->ac97_bus, &ac97, &codec->ac97)) < 0) {
+        snd_printk("ali mixer creating error.\n");
+        return err;
+    }
+    if (codec->spdif_support) {
+        for(idx = 0; idx < ARRAY_SIZE(snd_ali5451_mixer_spdif); idx++) {
+            err=snd_ctl_add(codec->card, snd_ctl_new1(&snd_ali5451_mixer_spdif[idx], codec));
+            if (err < 0) return err;
+        }
+    }
+    return 0;
+}
+
+#ifdef CONFIG_PM
+static int ali_suspend(snd_card_t *card, unsigned int state)
+{
+    ali_t *chip = card->pm_private_data;
+    ali_image_t *im;
+    int i, j;
+
+    im = chip->image;
+    if (! im)
+        return 0;
+
+    snd_pcm_suspend_all(chip->pcm);
+    snd_ac97_suspend(chip->ac97);
+
+    spin_lock_irq(&chip->reg_lock);
+
+    im->regs[ALI_MISCINT >> 2] = inl(ALI_REG(chip, ALI_MISCINT));
+    // im->regs[ALI_START >> 2] = inl(ALI_REG(chip, ALI_START));
+    im->regs[ALI_STOP >> 2] = inl(ALI_REG(chip, ALI_STOP));
+
+    // disable all IRQ bits
+    outl(0, ALI_REG(chip, ALI_MISCINT));
+
+    for (i = 0; i < ALI_GLOBAL_REGS; i++) {
+        if ((i*4 == ALI_MISCINT) || (i*4 == ALI_STOP))
+            continue;
+        im->regs[i] = inl(ALI_REG(chip, i*4));
+    }
+
+    for (i = 0; i < ALI_CHANNELS; i++) {
+        outb(i, ALI_REG(chip, ALI_GC_CIR));
+        for (j = 0; j < ALI_CHANNEL_REGS; j++)
+            im->channel_regs[i][j] = inl(ALI_REG(chip, j*4 + 0xe0));
+    }
+
+    // stop all HW channel
+    outl(0xffffffff, ALI_REG(chip, ALI_STOP));
+
+    spin_unlock_irq(&chip->reg_lock);
+    return 0;
+}
+
+static int ali_resume(snd_card_t *card, unsigned int state)
+{
+    ali_t *chip = card->pm_private_data;
+    ali_image_t *im;
+    int i, j;
+
+    im = chip->image;
+    if (! im)
+        return 0;
+
+    pci_enable_device(chip->pci);
+
+    spin_lock_irq(&chip->reg_lock);
+
+    for (i = 0; i < ALI_CHANNELS; i++) {
+        outb(i, ALI_REG(chip, ALI_GC_CIR));
+        for (j = 0; j < ALI_CHANNEL_REGS; j++)
+            outl(im->channel_regs[i][j], ALI_REG(chip, j*4 + 0xe0));
+    }
+
+    for (i = 0; i < ALI_GLOBAL_REGS; i++) {
+        if ((i*4 == ALI_MISCINT) || (i*4 == ALI_STOP) || (i*4 == ALI_START))
+            continue;
+        outl(im->regs[i], ALI_REG(chip, i*4));
+    }
+
+    // start HW channel
+    outl(im->regs[ALI_START >> 2], ALI_REG(chip, ALI_START));
+    // restore IRQ enable bits
+    outl(im->regs[ALI_MISCINT >> 2], ALI_REG(chip, ALI_MISCINT));
+
+    spin_unlock_irq(&chip->reg_lock);
+
+    snd_ac97_resume(chip->ac97);
+    return 0;
+}
+#endif /* CONFIG_PM */
+
+static int snd_ali_free(ali_t * codec)
+{
+    if (codec->hw_initialized)
+        snd_ali_disable_address_interrupt(codec);
+    if (codec->irq >= 0) {
+        synchronize_irq(codec->irq);
+        free_irq(codec->irq, (void *)codec);
+    }
+    if (codec->port)
+        pci_release_regions(codec->pci);
+#ifdef CONFIG_PM
+    if (codec->image)
+        kfree(codec->image);
+#endif
+    kfree(codec);
+    return 0;
+}
+
+static int snd_ali_chip_init(ali_t *codec)
+{
+    unsigned int legacy;
+    unsigned char temp;
+    struct pci_dev *pci_dev = NULL;
+
+    snd_ali_printk("chip initializing ... \n");
+
+    if (snd_ali_reset_5451(codec)) {
+        snd_printk("ali_chip_init: reset 5451 error.\n");
+        return -1;
+    }
+
+    if (codec->revision == ALI_5451_V02) {
+        pci_dev = codec->pci_m1533;
+        pci_read_config_byte(pci_dev, 0x59, &temp);
+        temp |= 0x80;
+        pci_write_config_byte(pci_dev, 0x59, temp);
+
+        pci_dev = codec->pci_m7101;
+        pci_read_config_byte(pci_dev,0xb8,&temp);
+        temp |= 0x20;
+        pci_write_config_byte(pci_dev, 0xB8, temp);
+    }
+
+    pci_read_config_dword(codec->pci, 0x44, &legacy);
+    legacy &= 0xff00ff00;
+    legacy |= 0x000800aa;
+    pci_write_config_dword(codec->pci, 0x44, legacy);
+
+    outl(0x80000001, ALI_REG(codec, ALI_GLOBAL_CONTROL));
+    outl(0x00000000, ALI_REG(codec, ALI_AINTEN));
+    outl(0xffffffff, ALI_REG(codec, ALI_AINT));
+    outl(0x00000000, ALI_REG(codec, ALI_VOLUME));
+    outb(0x10, 	 ALI_REG(codec, ALI_MPUR2));
+
+    codec->ac97_ext_id = snd_ali_codec_peek(codec, 0, AC97_EXTENDED_ID);
+    codec->ac97_ext_status = snd_ali_codec_peek(codec, 0, AC97_EXTENDED_STATUS);
+    if (codec->spdif_support) {
+        snd_ali_enable_spdif_out(codec);
+        codec->spdif_mask = 0x00000002;
+    }
+
+    snd_ali_printk("chip initialize succeed.\n");
+    return 0;
+
+}
+
+static int __devinit snd_ali_resources(ali_t *codec)
+{
+    int err;
+
+    snd_ali_printk("resouces allocation ...\n");
+    if ((err = pci_request_regions(codec->pci, "ALI 5451")) < 0)
+        return err;
+    codec->port = pci_resource_start(codec->pci, 0);
+
+    if (request_irq(codec->pci->irq, snd_ali_card_interrupt, SA_INTERRUPT|SA_SHIRQ, "ALI 5451", (void *)codec)) {
+        snd_printk("Unable to request irq.\n");
+        return -EBUSY;
+    }
+    codec->irq = codec->pci->irq;
+    snd_ali_printk("resouces allocated.\n");
+    return 0;
+}
+static int snd_ali_dev_free(snd_device_t *device)
+{
+    ali_t *codec=device->device_data;
+    snd_ali_free(codec);
+    return 0;
+}
+
+static int __devinit snd_ali_create(snd_card_t * card,
+                                    struct pci_dev *pci,
+                                    int pcm_streams,
+                                    int spdif_support,
+                                    ali_t ** r_ali)
+{
+    ali_t *codec;
+    int i, err;
+    unsigned short cmdw = 0;
+    struct pci_dev *pci_dev = NULL;
+    static struct snd_device_ops ops = {
+        .dev_free = snd_ali_dev_free,
+    };
+
+    *r_ali = NULL;
+
+    snd_ali_printk("creating ...\n");
+
+    /* enable PCI device */
+    if ((err = pci_enable_device(pci)) < 0)
+        return err;
+    /* check, if we can restrict PCI DMA transfers to 31 bits */
+    if (!pci_dma_supported(pci, 0x7fffffff)) {
+        snd_printk("architecture does not support 31bit PCI busmaster DMA\n");
+        return -ENXIO;
+    }
+    pci_set_dma_mask(pci, 0x7fffffff);
+
+    if ((codec = kcalloc(1, sizeof(*codec), GFP_KERNEL)) == NULL)
+        return -ENOMEM;
+
+    spin_lock_init(&codec->reg_lock);
+    spin_lock_init(&codec->voice_alloc);
+
+    codec->card = card;
+    codec->pci = pci;
+    codec->irq = -1;
+    pci_read_config_byte(pci, PCI_REVISION_ID, &codec->revision);
+    codec->spdif_support = spdif_support;
+
+    if (pcm_streams < 1)
+        pcm_streams = 1;
+    if (pcm_streams > 32)
+        pcm_streams = 32;
+
+    pci_set_master(pci);
+    pci_read_config_word(pci, PCI_COMMAND, &cmdw);
+    if ((cmdw & PCI_COMMAND_IO) != PCI_COMMAND_IO) {
+        cmdw |= PCI_COMMAND_IO;
+        pci_write_config_word(pci, PCI_COMMAND, cmdw);
+    }
+    pci_set_master(pci);
+
+    if (snd_ali_resources(codec)) {
+        snd_ali_free(codec);
+        return -EBUSY;
+    }
+
+    synchronize_irq(pci->irq);
+
+    codec->synth.chmap = 0;
+    codec->synth.chcnt = 0;
+    codec->spdif_mask = 0;
+    codec->synth.synthcount = 0;
+
+    if (codec->revision == ALI_5451_V02)
+        codec->chregs.regs.ac97read = ALI_AC97_WRITE;
+    else
+        codec->chregs.regs.ac97read = ALI_AC97_READ;
+    codec->chregs.regs.ac97write = ALI_AC97_WRITE;
+
+    codec->chregs.regs.start  = ALI_START;
+    codec->chregs.regs.stop   = ALI_STOP;
+    codec->chregs.regs.aint   = ALI_AINT;
+    codec->chregs.regs.ainten = ALI_AINTEN;
+
+    codec->chregs.data.start  = 0x00;
+    codec->chregs.data.stop   = 0x00;
+    codec->chregs.data.aint   = 0x00;
+    codec->chregs.data.ainten = 0x00;
+
+    /* M1533: southbridge */
+    pci_dev = pci_find_device(0x10b9, 0x1533, NULL);
+    codec->pci_m1533 = pci_dev;
+    if (! codec->pci_m1533) {
+        snd_printk(KERN_ERR "ali5451: cannot find ALi 1533 chip.\n");
+        snd_ali_free(codec);
+        return -ENODEV;
+    }
+    /* M7101: power management */
+    pci_dev = pci_find_device(0x10b9, 0x7101, NULL);
+    codec->pci_m7101 = pci_dev;
+    if (! codec->pci_m7101 && codec->revision == ALI_5451_V02) {
+        snd_printk(KERN_ERR "ali5451: cannot find ALi 7101 chip.\n");
+        snd_ali_free(codec);
+        return -ENODEV;
+    }
+
+    snd_ali_printk("snd_device_new is called.\n");
+    if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, codec, &ops)) < 0) {
+        snd_ali_free(codec);
+        return err;
+    }
+
+    /* initialise synth voices*/
+    for (i = 0; i < ALI_CHANNELS; i++ ) {
+        codec->synth.voices[i].number = i;
+    }
+
+    if ((err = snd_ali_chip_init(codec)) < 0) {
+        snd_printk("ali create: chip init error.\n");
+        return err;
+    }
+
+#ifdef CONFIG_PM
+    codec->image = kmalloc(sizeof(*codec->image), GFP_KERNEL);
+    if (! codec->image)
+        snd_printk("can't allocate apm buffer\n");
+    else
+        snd_card_set_pm_callback(card, ali_suspend, ali_resume, codec);
+#endif
+
+    snd_ali_enable_address_interrupt(codec);
+    codec->hw_initialized = 1;
+
+    *r_ali = codec;
+    snd_ali_printk("created.\n");
+    return 0;
+}
+
+static int __devinit snd_ali_probe(struct pci_dev *pci,
+                                   const struct pci_device_id *pci_id)
+{
+    static int dev;
+    snd_card_t *card;
+    ali_t *codec;
+    int err;
+
+    snd_ali_printk("probe ...\n");
+
+    if (dev >= SNDRV_CARDS)
+        return -ENODEV;
+    if (!enable[dev]) {
+        dev++;
+        return -ENOENT;
+    }
+
+    card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+    if (card == NULL)
+        return -ENOMEM;
+
+    if ((err = snd_ali_create(card, pci, pcm_channels[dev], spdif[dev], &codec)) < 0) {
+        snd_card_free(card);
+        return err;
+    }
+
+    snd_ali_printk("mixer building ...\n");
+    if ((err = snd_ali_mixer(codec)) < 0) {
+        snd_card_free(card);
+        return err;
+    }
+
+    snd_ali_printk("pcm building ...\n");
+    if ((err = snd_ali_pcm(codec, 0, NULL)) < 0) {
+        snd_card_free(card);
+        return err;
+    }
+
+    strcpy(card->driver, "ALI5451");
+    strcpy(card->shortname, "ALI 5451");
+
+    sprintf(card->longname, "%s at 0x%lx, irq %li",
+            card->shortname, codec->port, codec->irq);
+
+    snd_ali_printk("register card.\n");
+    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_ali_remove(struct pci_dev *pci)
+{
+    snd_card_free(pci_get_drvdata(pci));
+    pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver driver = {
+    0,0,0,"ALI 5451",
+    snd_ali_ids,
+    snd_ali_probe,
+    snd_ali_remove,
+    SND_PCI_PM_CALLBACKS
+};
+
+static int __init alsa_card_ali_init(void)
+{
+    int err;
+
+    if ((err = pci_module_init(&driver)) < 0) {
+#ifdef MODULE
+        //		snd_printk("ALi pci audio not found or device busy.\n");
+#endif
+        return err;
+    }
+    return 0;
+}
+
+static void __exit alsa_card_ali_exit(void)
+{
+    pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_ali_init)
+module_exit(alsa_card_ali_exit)
+
+#ifndef MODULE
+
+/* format is: snd-ali5451=enable,index,id,pcm_channels */
+
+static int __init alsa_card_ali_setup(char *str)
+{
+    static unsigned __initdata nr_dev = 0;
+
+    if (nr_dev >= SNDRV_CARDS)
+        return 0;
+    (void)(get_option(&str,&enable[nr_dev]) == 2 &&
+           get_option(&str,&index[nr_dev]) == 2 &&
+           get_id(&str,&id[nr_dev]) == 2 &&
+           get_option(&str,&pcm_channels[nr_dev]) == 2);
+    nr_dev++;
+    return 1;
+}
+
+__setup("snd-ali5451=", alsa_card_ali_setup);
+
+#endif /* ifndef */
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/ali5451/diff.cmd
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/ali5451/diff.cmd	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/ali5451/diff.cmd	(revision 91)
@@ -0,0 +1,1 @@
+diff.exe -Nuwr z:\ua2\GPL\alsa-kernel\pci\ali5451\%1 E:\DEV\alsa\alsa-kernel\pci\ali5451\%1 >%1.diff
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/ali5451/makefile.os2
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/ali5451/makefile.os2	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/ali5451/makefile.os2	(revision 91)
@@ -0,0 +1,51 @@
+CINCLUDES = ..\..\..\include;..\..\include;$(%WATCOM)\H
+AINCLUDES = $(DDK_BASE)\H
+
+CDEFINES = -D__KERNEL__ -DMODULE -dTARGET_OS2 -dALSA_BUILD -D__ISAPNP__ -DCONFIG_ISAPNP -D__i386_
+
+32BIT=1
+!include ..\..\..\..\makefile.inc
+
+
+#===================================================================
+#
+#   List of source files
+#
+#===================================================================
+FILE1    = ali5451.obj
+FILE2    = 
+FILE3    = 
+FILE4    = 
+FILE5    = 
+FILE6    = 
+FILE7    = 
+FILE8    = 
+FILELAST =
+FILES    = $(FILE0) $(FILE1) $(FILE2) $(FILE3) $(FILE4) $(FILE5) $(FILE6) $(FILE7) $(FILE8) $(FILE9) $(FILE10) $(FILE11) $(FILE12)
+
+TARGET   = ali5451
+
+LNKFILE  = $(OBJDIR)\$(TARGET).lnk
+
+
+#===================================================================
+#
+#   Specific dependencies
+#
+#===================================================================
+all: $(OBJDIR)\$(TARGET).lib 
+
+
+$(LNKFILE): makefile.os2
+    @%create $(LNKFILE)
+    @for %f in ($(FILES)) do @%append $(LNKFILE) -+$(OBJDIR)\%f
+
+$(OBJDIR)\$(TARGET).lib: $(LNKFILE) $(FILES)
+    $(LIB) $(OBJDIR)\$(TARGET).lib @$(LNKFILE)
+    copy $(OBJDIR)\$(TARGET).lib $(ALSA_LIB) 
+
+clean: .SYMBOLIC
+    -@rm  *.err 2>nul
+    @cd $(OBJDIR)
+    -rm *.obj *.lnk *.sys *.sym *.lst *.map *.wmap *.lib 2>nul
+    @cd ..\..
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/als4000.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/als4000.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/als4000.c	(revision 91)
@@ -0,0 +1,833 @@
+/*
+ *  card-als4000.c - driver for Avance Logic ALS4000 based soundcards.
+ *  Copyright (C) 2000 by Bart Hartgers <bart@etpmod.phys.tue.nl>,
+ *			  Jaroslav Kysela <perex@suse.cz>
+ *  Copyright (C) 2002 by Andreas Mohr <hw7oshyuv3001@sneakemail.com>
+ *
+ *  Framework borrowed from Massimo Piccioni's card-als100.c.
+ *
+ * NOTES
+ *
+ *  Since Avance does not provide any meaningful documentation, and I
+ *  bought an ALS4000 based soundcard, I was forced to base this driver
+ *  on reverse engineering.
+ *
+ *  Note: this is no longer true. Pretty verbose chip docu (ALS4000a.PDF)
+ *  can be found on the ALSA web site.
+ *
+ *  The ALS4000 seems to be the PCI-cousin of the ALS100. It contains an
+ *  ALS100-like SB DSP/mixer, an OPL3 synth, a MPU401 and a gameport
+ *  interface. These subsystems can be mapped into ISA io-port space,
+ *  using the PCI-interface. In addition, the PCI-bit provides DMA and IRQ
+ *  services to the subsystems.
+ *
+ * While ALS4000 is very similar to a SoundBlaster, the differences in
+ * DMA and capturing require more changes to the SoundBlaster than
+ * desirable, so I made this separate driver.
+ *
+ * The ALS4000 can do real full duplex playback/capture.
+ *
+ * FMDAC:
+ * - 0x4f -> port 0x14
+ * - port 0x15 |= 1
+ *
+ * Enable/disable 3D sound:
+ * - 0x50 -> port 0x14
+ * - change bit 6 (0x40) of port 0x15
+ *
+ * Set QSound:
+ * - 0xdb -> port 0x14
+ * - set port 0x15:
+ *   0x3e (mode 3), 0x3c (mode 2), 0x3a (mode 1), 0x38 (mode 0)
+ *
+ * Set KSound:
+ * - value -> some port 0x0c0d
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include <sound/driver.h>
+#include <sound/pcm.h>
+#include <sound/rawmidi.h>
+#include <sound/mpu401.h>
+#include <sound/opl3.h>
+#include <sound/sb.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+EXPORT_NO_SYMBOLS;
+MODULE_DESCRIPTION("Avance Logic ALS4000");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{Avance Logic,ALS4000}}");
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;	/* Enable this card */
+static int joystick_port[SNDRV_CARDS] =
+#ifdef CONFIG_ISA
+{0x200};	/* enable as default */
+#else
+{0};	/* disabled */
+#endif
+
+MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(index, "Index value for ALS4000 soundcard.");
+MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC);
+MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+MODULE_PARM_DESC(id, "ID string for ALS4000 soundcard.");
+MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC);
+MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(enable, "Enable ALS4000 soundcard.");
+MODULE_PARM_SYNTAX(enable, SNDRV_INDEX_DESC);
+MODULE_PARM(joystick_port, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(joystick_port, "Joystick port address for ALS4000 soundcard. (0 = disabled)");
+MODULE_PARM_SYNTAX(joystick_port, SNDRV_ENABLED);
+
+typedef struct {
+    struct pci_dev *pci;
+    unsigned long gcr;
+#ifdef SUPPORT_JOYSTICK
+    struct gameport gameport;
+    struct resource *res_joystick;
+#endif
+} snd_card_als4000_t;
+
+static struct pci_device_id snd_als4000_ids[] = {
+    { 0x4005, 0x4000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },   /* ALS4000 */
+    { 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, snd_als4000_ids);
+
+static inline void snd_als4000_gcr_write_addr(unsigned long port, u32 reg, u32 val)
+{
+    outb(reg, port+0x0c);
+    outl(val, port+0x08);
+}
+
+static inline void snd_als4000_gcr_write(struct snd_sb *sb, u32 reg, u32 val)
+{
+    snd_als4000_gcr_write_addr(sb->alt_port, reg, val);
+}
+
+static inline u32 snd_als4000_gcr_read_addr(unsigned long port, u32 reg)
+{
+    outb(reg, port+0x0c);
+    return inl(port+0x08);
+}
+
+static inline u32 snd_als4000_gcr_read(struct snd_sb *sb, u32 reg)
+{
+    return snd_als4000_gcr_read_addr(sb->alt_port, reg);
+}
+
+static void snd_als4000_set_rate(struct snd_sb *chip, unsigned int rate)
+{
+    if (!(chip->mode & SB_RATE_LOCK)) {
+        snd_sbdsp_command(chip, SB_DSP_SAMPLE_RATE_OUT);
+        snd_sbdsp_command(chip, rate>>8);
+        snd_sbdsp_command(chip, rate);
+    }
+}
+
+static void snd_als4000_set_capture_dma(struct snd_sb *chip, dma_addr_t addr, unsigned size)
+{
+    snd_als4000_gcr_write(chip, 0xa2, addr);
+    snd_als4000_gcr_write(chip, 0xa3, (size-1));
+}
+
+static void snd_als4000_set_playback_dma(struct snd_sb *chip, dma_addr_t addr, unsigned size)
+{
+    snd_als4000_gcr_write(chip, 0x91, addr);
+    snd_als4000_gcr_write(chip, 0x92, (size-1)|0x180000);
+}
+
+#define ALS4000_FORMAT_SIGNED	(1<<0)
+#define ALS4000_FORMAT_16BIT	(1<<1)
+#define ALS4000_FORMAT_STEREO	(1<<2)
+
+static int snd_als4000_get_format(snd_pcm_runtime_t *runtime)
+{
+    int result;
+
+    result = 0;
+    if (snd_pcm_format_signed(runtime->format))
+        result |= ALS4000_FORMAT_SIGNED;
+    if (snd_pcm_format_physical_width(runtime->format) == 16)
+        result |= ALS4000_FORMAT_16BIT;
+    if (runtime->channels > 1)
+        result |= ALS4000_FORMAT_STEREO;
+    return result;
+}
+
+/* structure for setting up playback */
+static struct {
+    unsigned char dsp_cmd, dma_on, dma_off, format;
+} playback_cmd_vals[]={
+    /* ALS4000_FORMAT_U8_MONO */
+    { SB_DSP4_OUT8_AI, SB_DSP_DMA8_ON, SB_DSP_DMA8_OFF, SB_DSP4_MODE_UNS_MONO },
+    /* ALS4000_FORMAT_S8_MONO */
+    { SB_DSP4_OUT8_AI, SB_DSP_DMA8_ON, SB_DSP_DMA8_OFF, SB_DSP4_MODE_SIGN_MONO },
+    /* ALS4000_FORMAT_U16L_MONO */
+    { SB_DSP4_OUT16_AI, SB_DSP_DMA16_ON, SB_DSP_DMA16_OFF, SB_DSP4_MODE_UNS_MONO },
+    /* ALS4000_FORMAT_S16L_MONO */
+    { SB_DSP4_OUT16_AI, SB_DSP_DMA16_ON, SB_DSP_DMA16_OFF, SB_DSP4_MODE_SIGN_MONO },
+    /* ALS4000_FORMAT_U8_STEREO */
+    { SB_DSP4_OUT8_AI, SB_DSP_DMA8_ON, SB_DSP_DMA8_OFF, SB_DSP4_MODE_UNS_STEREO },
+    /* ALS4000_FORMAT_S8_STEREO */
+    { SB_DSP4_OUT8_AI, SB_DSP_DMA8_ON, SB_DSP_DMA8_OFF, SB_DSP4_MODE_SIGN_STEREO },
+    /* ALS4000_FORMAT_U16L_STEREO */
+    { SB_DSP4_OUT16_AI, SB_DSP_DMA16_ON, SB_DSP_DMA16_OFF, SB_DSP4_MODE_UNS_STEREO },
+    /* ALS4000_FORMAT_S16L_STEREO */
+    { SB_DSP4_OUT16_AI, SB_DSP_DMA16_ON, SB_DSP_DMA16_OFF, SB_DSP4_MODE_SIGN_STEREO },
+};
+#define playback_cmd(chip) (playback_cmd_vals[(chip)->playback_format])
+
+/* structure for setting up capture */
+enum { CMD_WIDTH8=0x04, CMD_SIGNED=0x10, CMD_MONO=0x80, CMD_STEREO=0xA0 };
+static unsigned char capture_cmd_vals[]=
+{
+    CMD_WIDTH8|CMD_MONO,			/* ALS4000_FORMAT_U8_MONO */
+    CMD_WIDTH8|CMD_SIGNED|CMD_MONO,		/* ALS4000_FORMAT_S8_MONO */
+    CMD_MONO,				/* ALS4000_FORMAT_U16L_MONO */
+    CMD_SIGNED|CMD_MONO,			/* ALS4000_FORMAT_S16L_MONO */
+    CMD_WIDTH8|CMD_STEREO,			/* ALS4000_FORMAT_U8_STEREO */
+    CMD_WIDTH8|CMD_SIGNED|CMD_STEREO,	/* ALS4000_FORMAT_S8_STEREO */
+    CMD_STEREO,				/* ALS4000_FORMAT_U16L_STEREO */
+    CMD_SIGNED|CMD_STEREO,			/* ALS4000_FORMAT_S16L_STEREO */
+};
+#define capture_cmd(chip) (capture_cmd_vals[(chip)->capture_format])
+
+static int snd_als4000_hw_params(snd_pcm_substream_t * substream,
+                                 snd_pcm_hw_params_t * hw_params)
+{
+    return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+static int snd_als4000_hw_free(snd_pcm_substream_t * substream)
+{
+    snd_pcm_lib_free_pages(substream);
+    return 0;
+}
+
+static int snd_als4000_capture_prepare(snd_pcm_substream_t * substream)
+{
+    unsigned long flags;
+    struct snd_sb *chip = snd_pcm_substream_chip(substream);
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    unsigned long size;
+    unsigned count;
+
+    chip->capture_format = snd_als4000_get_format(runtime);
+
+    size = snd_pcm_lib_buffer_bytes(substream);
+    count = snd_pcm_lib_period_bytes(substream);
+
+    if (chip->capture_format & ALS4000_FORMAT_16BIT)
+        count >>=1;
+    count--;
+
+    spin_lock_irqsave(&chip->reg_lock, flags);
+    snd_als4000_set_rate(chip, runtime->rate);
+    snd_als4000_set_capture_dma(chip, runtime->dma_addr, size);
+    spin_unlock_irqrestore(&chip->reg_lock, flags);
+    spin_lock_irqsave(&chip->mixer_lock, flags );
+    snd_sbmixer_write(chip, 0xdc, count);
+    snd_sbmixer_write(chip, 0xdd, count>>8);
+    spin_unlock_irqrestore(&chip->mixer_lock, flags );
+    return 0;
+}
+
+static int snd_als4000_playback_prepare(snd_pcm_substream_t *substream)
+{
+    unsigned long flags;
+    struct snd_sb *chip = snd_pcm_substream_chip(substream);
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    unsigned long size;
+    unsigned count;
+
+    chip->playback_format = snd_als4000_get_format(runtime);
+
+    size = snd_pcm_lib_buffer_bytes(substream);
+    count = snd_pcm_lib_period_bytes(substream);
+
+    if (chip->playback_format & ALS4000_FORMAT_16BIT)
+        count >>=1;
+    count--;
+
+    /* FIXME: from second playback on, there's a lot more clicks and pops
+     * involved here than on first playback. Fiddling with
+     * tons of different settings didn't help (DMA, speaker on/off,
+     * reordering, ...). Something seems to get enabled on playback
+     * that I haven't found out how to disable again, which then causes
+     * the switching pops to reach the speakers the next time here. */
+    spin_lock_irqsave(&chip->reg_lock, flags);
+    snd_als4000_set_rate(chip, runtime->rate);
+    snd_als4000_set_playback_dma(chip, runtime->dma_addr, size);
+
+    /* SPEAKER_ON not needed, since dma_on seems to also enable speaker */
+    /* snd_sbdsp_command(chip, SB_DSP_SPEAKER_ON); */
+    snd_sbdsp_command(chip, playback_cmd(chip).dsp_cmd);
+    snd_sbdsp_command(chip, playback_cmd(chip).format);
+    snd_sbdsp_command(chip, count);
+    snd_sbdsp_command(chip, count>>8);
+    snd_sbdsp_command(chip, playback_cmd(chip).dma_off);
+    spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+    return 0;
+}
+
+static int snd_als4000_capture_trigger(snd_pcm_substream_t * substream, int cmd)
+{
+    struct snd_sb *chip = snd_pcm_substream_chip(substream);
+    int result = 0;
+
+    spin_lock(&chip->mixer_lock);
+    if (cmd == SNDRV_PCM_TRIGGER_START) {
+        chip->mode |= SB_RATE_LOCK_CAPTURE;
+        snd_sbmixer_write(chip, 0xde, capture_cmd(chip));
+    } else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+        chip->mode &= ~SB_RATE_LOCK_CAPTURE;
+        snd_sbmixer_write(chip, 0xde, 0);
+    } else {
+        result = -EINVAL;
+    }
+    spin_unlock(&chip->mixer_lock);
+    return result;
+}
+
+static int snd_als4000_playback_trigger(snd_pcm_substream_t * substream, int cmd)
+{
+    struct snd_sb *chip = snd_pcm_substream_chip(substream);
+    int result = 0;
+
+    spin_lock(&chip->reg_lock);
+    if (cmd == SNDRV_PCM_TRIGGER_START) {
+        chip->mode |= SB_RATE_LOCK_PLAYBACK;
+        snd_sbdsp_command(chip, playback_cmd(chip).dma_on);
+    } else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+        snd_sbdsp_command(chip, playback_cmd(chip).dma_off);
+        chip->mode &= ~SB_RATE_LOCK_PLAYBACK;
+    } else {
+        result = -EINVAL;
+    }
+    spin_unlock(&chip->reg_lock);
+    return result;
+}
+
+static snd_pcm_uframes_t snd_als4000_capture_pointer(snd_pcm_substream_t * substream)
+{
+    struct snd_sb *chip = snd_pcm_substream_chip(substream);
+    unsigned int result;
+
+    spin_lock(&chip->reg_lock);
+    result = snd_als4000_gcr_read(chip, 0xa4) & 0xffff;
+    spin_unlock(&chip->reg_lock);
+    return bytes_to_frames( substream->runtime, result );
+}
+
+static snd_pcm_uframes_t snd_als4000_playback_pointer(snd_pcm_substream_t * substream)
+{
+    struct snd_sb *chip = snd_pcm_substream_chip(substream);
+    unsigned result;
+
+    spin_lock(&chip->reg_lock);
+    result = snd_als4000_gcr_read(chip, 0xa0) & 0xffff;
+    spin_unlock(&chip->reg_lock);
+    return bytes_to_frames( substream->runtime, result );
+}
+
+static irqreturn_t snd_als4000_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+    struct snd_sb *chip = dev_id;
+    unsigned gcr_status;
+    unsigned sb_status;
+#ifdef TARGET_OS2
+    int fOurIrq = FALSE;
+#endif
+
+    /* find out which bit of the ALS4000 produced the interrupt */
+    gcr_status = inb(chip->alt_port + 0xe);
+
+#ifdef TARGET_OS2
+    if(gcr_status & (0x80|0x40|0x10)) fOurIrq = TRUE;
+#endif
+
+    if ((gcr_status & 0x80) && (chip->playback_substream)) /* playback */
+        snd_pcm_period_elapsed(chip->playback_substream);
+    if ((gcr_status & 0x40) && (chip->capture_substream)) /* capturing */
+        snd_pcm_period_elapsed(chip->capture_substream);
+    if ((gcr_status & 0x10) && (chip->rmidi)) /* MPU401 interrupt */
+        snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data, regs);
+    /* release the gcr */
+    outb(gcr_status, chip->alt_port + 0xe);
+
+    spin_lock(&chip->mixer_lock);
+    sb_status = snd_sbmixer_read(chip, SB_DSP4_IRQSTATUS);
+    spin_unlock(&chip->mixer_lock);
+
+    if (sb_status & SB_IRQTYPE_8BIT)
+        snd_sb_ack_8bit(chip);
+    if (sb_status & SB_IRQTYPE_16BIT)
+        snd_sb_ack_16bit(chip);
+    if (sb_status & SB_IRQTYPE_MPUIN)
+        inb(chip->mpu_port);
+    if (sb_status & 0x20)
+        inb(SBP(chip, RESET));
+
+#ifdef TARGET_OS2
+    if (fOurIrq) {
+        eoi_irq(irq);
+    }
+#endif //TARGET_OS2
+    return IRQ_HANDLED;
+}
+
+/*****************************************************************/
+
+#ifdef TARGET_OS2
+static snd_pcm_hardware_t snd_als4000_playback =
+{
+    /*	info:        	  */	(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+                                 SNDRV_PCM_INFO_MMAP_VALID),
+                                 /*	formats:	  */	SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 |
+                                 SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE,	/* formats */
+                                 /*	rates:		  */	SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+                                 /*	rate_min:	  */	4000,
+                                 /*	rate_max:	  */	48000,
+                                 /*	channels_min:	  */	1,
+                                 /*	channels_max:	  */	2,
+                                 /*	buffer_bytes_max:  */	65536,
+                                 /*	period_bytes_min:  */	64,
+                                 /*	period_bytes_max:  */	65536,
+                                 /*	periods_min:	  */	1,
+                                 /*	periods_max:	  */	1024,
+                                 /*	fifo_size:	  */	0
+};
+
+static snd_pcm_hardware_t snd_als4000_capture =
+{
+    /*	info:		  */	(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+                                 SNDRV_PCM_INFO_MMAP_VALID),
+                                 /*	formats:	  */	SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 |
+                                 SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE,	/* formats */
+                                 /*	rates:		  */	SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+                                 /*	rate_min:	  */	4000,
+                                 /*	rate_max:	  */	48000,
+                                 /*	channels_min:	  */	1,
+                                 /*	channels_max:	  */	2,
+                                 /*	buffer_bytes_max:  */	65536,
+                                 /*	period_bytes_min:  */	64,
+                                 /*	period_bytes_max:  */	65536,
+                                 /*	periods_min:	  */	1,
+                                 /*	periods_max:	  */	1024,
+                                 /*	fifo_size:	  */	0
+};
+#else
+static snd_pcm_hardware_t snd_als4000_playback =
+{
+info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+                         SNDRV_PCM_INFO_MMAP_VALID),
+    formats:		SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 |
+    SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE,	/* formats */
+    rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+    rate_min:		4000,
+    rate_max:		48000,
+    channels_min:		1,
+    channels_max:		2,
+    buffer_bytes_max:	65536,
+    period_bytes_min:	64,
+    period_bytes_max:	65536,
+    periods_min:		1,
+    periods_max:		1024,
+    fifo_size:		0
+};
+
+static snd_pcm_hardware_t snd_als4000_capture =
+{
+info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+                         SNDRV_PCM_INFO_MMAP_VALID),
+    formats:		SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 |
+    SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE,	/* formats */
+    rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+    rate_min:		4000,
+    rate_max:		48000,
+    channels_min:		1,
+    channels_max:		2,
+    buffer_bytes_max:	65536,
+    period_bytes_min:	64,
+    period_bytes_max:	65536,
+    periods_min:		1,
+    periods_max:		1024,
+    fifo_size:		0
+};
+#endif
+
+/*****************************************************************/
+
+static int snd_als4000_playback_open(snd_pcm_substream_t * substream)
+{
+    struct snd_sb *chip = snd_pcm_substream_chip(substream);
+    snd_pcm_runtime_t *runtime = substream->runtime;
+
+    chip->playback_substream = substream;
+    runtime->hw = snd_als4000_playback;
+    return 0;
+}
+
+static int snd_als4000_playback_close(snd_pcm_substream_t * substream)
+{
+    struct snd_sb *chip = snd_pcm_substream_chip(substream);
+
+    chip->playback_substream = NULL;
+    snd_pcm_lib_free_pages(substream);
+    return 0;
+}
+
+static int snd_als4000_capture_open(snd_pcm_substream_t * substream)
+{
+    struct snd_sb *chip = snd_pcm_substream_chip(substream);
+    snd_pcm_runtime_t *runtime = substream->runtime;
+
+    chip->capture_substream = substream;
+    runtime->hw = snd_als4000_capture;
+    return 0;
+}
+
+static int snd_als4000_capture_close(snd_pcm_substream_t * substream)
+{
+    struct snd_sb *chip = snd_pcm_substream_chip(substream);
+
+    chip->capture_substream = NULL;
+    snd_pcm_lib_free_pages(substream);
+    return 0;
+}
+
+/******************************************************************/
+
+#ifdef TARGET_OS2
+static snd_pcm_ops_t snd_als4000_playback_ops = {
+    snd_als4000_playback_open,
+    snd_als4000_playback_close,
+    snd_pcm_lib_ioctl,
+    snd_als4000_hw_params,
+    snd_als4000_hw_free,
+    snd_als4000_playback_prepare,
+    snd_als4000_playback_trigger,
+    snd_als4000_playback_pointer,0,0
+};
+
+static snd_pcm_ops_t snd_als4000_capture_ops = {
+    snd_als4000_capture_open,
+    snd_als4000_capture_close,
+    snd_pcm_lib_ioctl,
+    snd_als4000_hw_params,
+    snd_als4000_hw_free,
+    snd_als4000_capture_prepare,
+    snd_als4000_capture_trigger,
+    snd_als4000_capture_pointer,0,0
+};
+#else
+static snd_pcm_ops_t snd_als4000_playback_ops = {
+open:		snd_als4000_playback_open,
+    close:		snd_als4000_playback_close,
+    ioctl:		snd_pcm_lib_ioctl,
+    hw_params:	snd_als4000_hw_params,
+    hw_free:	snd_als4000_hw_free,
+    prepare:	snd_als4000_playback_prepare,
+    trigger:	snd_als4000_playback_trigger,
+    pointer:	snd_als4000_playback_pointer
+};
+
+static snd_pcm_ops_t snd_als4000_capture_ops = {
+open:		snd_als4000_capture_open,
+    close:		snd_als4000_capture_close,
+    ioctl:		snd_pcm_lib_ioctl,
+    hw_params:	snd_als4000_hw_params,
+    hw_free:	snd_als4000_hw_free,
+    prepare:	snd_als4000_capture_prepare,
+    trigger:	snd_als4000_capture_trigger,
+    pointer:	snd_als4000_capture_pointer
+};
+#endif
+
+static void snd_als4000_pcm_free(snd_pcm_t *pcm)
+{
+    struct snd_sb *chip = pcm->private_data;
+    chip->pcm = NULL;
+    snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static int __devinit snd_als4000_pcm(struct snd_sb *chip, int device)
+{
+    snd_pcm_t *pcm;
+    int err;
+
+    if ((err = snd_pcm_new(chip->card, "ALS4000 DSP", device, 1, 1, &pcm)) < 0)
+        return err;
+    pcm->private_free = snd_als4000_pcm_free;
+    pcm->private_data = chip;
+    pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX;
+    snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_als4000_playback_ops);
+    snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_als4000_capture_ops);
+
+    snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci),
+                                          64*1024, 64*1024);
+
+    chip->pcm = pcm;
+
+    return 0;
+}
+
+/******************************************************************/
+
+static void __devinit snd_als4000_set_addr(unsigned long gcr,
+                                           unsigned int sb,
+                                           unsigned int mpu,
+                                           unsigned int opl,
+                                           unsigned int game)
+{
+    u32 confA = 0;
+    u32 confB = 0;
+
+    if (mpu > 0)
+        confB |= (mpu | 1) << 16;
+    if (sb > 0)
+        confB |= (sb | 1);
+    if (game > 0)
+        confA |= (game | 1) << 16;
+    if (opl > 0)
+        confA |= (opl | 1);
+    snd_als4000_gcr_write_addr(gcr, 0xa8, confA);
+    snd_als4000_gcr_write_addr(gcr, 0xa9, confB);
+}
+
+static void __devinit snd_als4000_configure(struct snd_sb *chip)
+{
+    unsigned tmp;
+    int i;
+
+    /* do some more configuration */
+    spin_lock_irq(&chip->mixer_lock);
+    tmp = snd_sbmixer_read(chip, 0xc0);
+    snd_sbmixer_write(chip, 0xc0, tmp|0x80);
+    /* always select DMA channel 0, since we do not actually use DMA */
+    snd_sbmixer_write(chip, SB_DSP4_DMASETUP, SB_DMASETUP_DMA0);
+    snd_sbmixer_write(chip, 0xc0, tmp&0x7f);
+    spin_unlock_irq(&chip->mixer_lock);
+
+    spin_lock_irq(&chip->reg_lock);
+    /* magic number. Enables interrupts(?) */
+    snd_als4000_gcr_write(chip, 0x8c, 0x28000);
+    for(i = 0x91; i <= 0x96; ++i)
+        snd_als4000_gcr_write(chip, i, 0);
+
+    snd_als4000_gcr_write(chip, 0x99, snd_als4000_gcr_read(chip, 0x99));
+    spin_unlock_irq(&chip->reg_lock);
+}
+
+static void snd_card_als4000_free( snd_card_t *card )
+{
+    snd_card_als4000_t * acard = (snd_card_als4000_t *)card->private_data;
+    /* make sure that interrupts are disabled */
+    snd_als4000_gcr_write_addr( acard->gcr, 0x8c, 0);
+    /* free resources */
+#ifdef SUPPORT_JOYSTICK
+    if (acard->res_joystick) {
+        if (acard->gameport.io)
+            gameport_unregister_port(&acard->gameport);
+        snd_als4000_set_addr(acard->gcr, 0, 0, 0, 0); /* disable joystick */
+        release_resource(acard->res_joystick);
+        kfree_nocheck(acard->res_joystick);
+    }
+#endif
+    pci_release_regions(acard->pci);
+}
+
+static int __devinit snd_card_als4000_probe(struct pci_dev *pci,
+                                            const struct pci_device_id *pci_id)
+{
+    static int dev;
+    snd_card_t *card;
+    snd_card_als4000_t *acard;
+    unsigned long gcr;
+    struct snd_sb *chip;
+    opl3_t *opl3;
+    unsigned short word;
+    int err;
+
+    if (dev >= SNDRV_CARDS)
+        return -ENODEV;
+    if (!enable[dev]) {
+        dev++;
+        return -ENOENT;
+    }
+
+    /* enable PCI device */
+    if ((err = pci_enable_device(pci)) < 0) {
+        return err;
+    }
+    /* check, if we can restrict PCI DMA transfers to 24 bits */
+    if (!pci_dma_supported(pci, 0x00ffffff)) {
+        snd_printk("architecture does not support 24bit PCI busmaster DMA\n");
+        return -ENXIO;
+    }
+    pci_set_dma_mask(pci, 0x00ffffff);
+
+    if ((err = pci_request_regions(pci, "ALS4000")) < 0)
+        return err;
+    gcr = pci_resource_start(pci, 0);
+
+    pci_read_config_word(pci, PCI_COMMAND, &word);
+    pci_write_config_word(pci, PCI_COMMAND, word | PCI_COMMAND_IO);
+    pci_set_master(pci);
+
+    /* disable all legacy ISA stuff except for joystick */
+    snd_als4000_set_addr(gcr, 0, 0, 0, joystick_port[dev]);
+
+    card = snd_card_new(index[dev], id[dev], THIS_MODULE,
+                        sizeof( snd_card_als4000_t ) );
+    if (card == NULL) {
+        pci_release_regions(pci);
+        return -ENOMEM;
+    }
+
+    acard = (snd_card_als4000_t *)card->private_data;
+    acard->pci = pci;
+    acard->gcr = gcr;
+    card->private_free = snd_card_als4000_free;
+
+    if ((err = snd_sbdsp_create(card,
+                                gcr + 0x10,
+                                pci->irq,
+                                snd_als4000_interrupt,
+                                -1,
+                                -1,
+                                SB_HW_ALS4000,
+                                &chip)) < 0) {
+        snd_card_free(card);
+        return err;
+    }
+
+    chip->pci = pci;
+    chip->alt_port = gcr;
+
+    snd_als4000_configure(chip);
+
+    if ((err = snd_mpu401_uart_new( card, 0, MPU401_HW_ALS4000,
+                                   gcr+0x30, 1, pci->irq, 0,
+                                   &chip->rmidi)) < 0) {
+        snd_card_free(card);
+        snd_printk("no MPU-401device at 0x%lx ?\n", gcr+0x30);
+        return err;
+    }
+
+    if ((err = snd_als4000_pcm(chip, 0)) < 0) {
+        snd_card_free(card);
+        return err;
+    }
+    if ((err = snd_sbmixer_new(chip)) < 0) {
+        snd_card_free(card);
+        return err;
+    }
+
+    if (snd_opl3_create(card, gcr+0x10, gcr+0x12,
+                        OPL3_HW_AUTO, 1, &opl3) < 0) {
+        snd_printk("no OPL device at 0x%lx-0x%lx ?\n",
+                   gcr+0x10, gcr+0x12 );
+    } else {
+        if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
+            snd_card_free(card);
+            return err;
+        }
+    }
+
+    strcpy(card->driver, "ALS4000");
+    strcpy(card->shortname, "Avance Logic ALS4000");
+    sprintf(card->longname, "%s at 0x%lx, irq %i",
+            card->shortname, chip->alt_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_card_als4000_remove(struct pci_dev *pci)
+{
+    snd_card_free(pci_get_drvdata(pci));
+    pci_set_drvdata(pci, NULL);
+}
+
+#ifdef TARGET_OS2
+static struct pci_driver driver = {
+    0,0,0,"ALS4000",
+    snd_als4000_ids,
+    snd_card_als4000_probe,
+    snd_card_als4000_remove,0,0
+};
+#else
+static struct pci_driver driver = {
+name: "ALS4000",
+    id_table: snd_als4000_ids,
+    probe: snd_card_als4k_probe,
+    remove: snd_card_als4k_remove,
+};
+#endif
+
+static int __init alsa_card_als4000_init(void)
+{
+    int err;
+
+    if ((err = pci_module_init(&driver)) < 0) {
+#ifdef MODULE
+        //		snd_printk("no ALS4000 based soundcards found or device busy\n");
+#endif
+        return err;
+    }
+    return 0;
+}
+
+static void __exit alsa_card_als4000_exit(void)
+{
+    pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_als4000_init)
+module_exit(alsa_card_als4000_exit)
+
+#ifndef MODULE
+
+/* format is: snd-als4000=enable,index,id */
+
+static int __init alsa_card_als4000_setup(char *str)
+{
+    static unsigned __initdata nr_dev = 0;
+
+    if (nr_dev >= SNDRV_CARDS)
+        return 0;
+    (void)(get_option(&str,&enable[nr_dev]) == 2 &&
+           get_option(&str,&index[nr_dev]) == 2 &&
+           get_id(&str,&id[nr_dev]) == 2);
+    nr_dev++;
+    return 1;
+}
+
+__setup("snd-als4000=", alsa_card_als4000_setup);
+
+#endif /* ifndef MODULE */
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/atiixp.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/atiixp.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/atiixp.c	(revision 91)
@@ -0,0 +1,1686 @@
+/*
+ *   ALSA driver for ATI IXP 150/200/250/300 AC97 controllers
+ *
+ *	Copyright (c) 2004 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 <linux/moduleparam.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/info.h>
+#include <sound/ac97_codec.h>
+#include <sound/initval.h>
+
+MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
+MODULE_DESCRIPTION("ATI IXP AC97 controller");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{ATI,IXP150/200/250/300/400}}");
+
+static int index = SNDRV_DEFAULT_IDX1;	/* Index 0-MAX */
+static char *id = SNDRV_DEFAULT_STR1;	/* ID for this card */
+static int ac97_clock = 48000;
+static char *ac97_quirk;
+static int spdif_aclink = 1;
+
+//module_param(index, int, 0444);
+MODULE_PARM_DESC(index, "Index value for ATI IXP controller.");
+//module_param(id, charp, 0444);
+MODULE_PARM_DESC(id, "ID string for ATI IXP controller.");
+//module_param(ac97_clock, int, 0444);
+MODULE_PARM_DESC(ac97_clock, "AC'97 codec clock (default 48000Hz).");
+//module_param(ac97_quirk, charp, 0444);
+MODULE_PARM_DESC(ac97_quirk, "AC'97 workaround for strange hardware.");
+//module_param(spdif_aclink, bool, 0444);
+MODULE_PARM_DESC(spdif_aclink, "S/PDIF over AC-link.");
+
+/* just for backward compatibility */
+static int enable;
+//module_param(enable, bool, 0444);
+
+
+/*
+ */
+
+#define ATI_REG_ISR			0x00	/* interrupt source */
+#define  ATI_REG_ISR_IN_XRUN		(1U<<0)
+#define  ATI_REG_ISR_IN_STATUS		(1U<<1)
+#define  ATI_REG_ISR_OUT_XRUN		(1U<<2)
+#define  ATI_REG_ISR_OUT_STATUS		(1U<<3)
+#define  ATI_REG_ISR_SPDF_XRUN		(1U<<4)
+#define  ATI_REG_ISR_SPDF_STATUS	(1U<<5)
+#define  ATI_REG_ISR_PHYS_INTR		(1U<<8)
+#define  ATI_REG_ISR_PHYS_MISMATCH	(1U<<9)
+#define  ATI_REG_ISR_CODEC0_NOT_READY	(1U<<10)
+#define  ATI_REG_ISR_CODEC1_NOT_READY	(1U<<11)
+#define  ATI_REG_ISR_CODEC2_NOT_READY	(1U<<12)
+#define  ATI_REG_ISR_NEW_FRAME		(1U<<13)
+
+#define ATI_REG_IER			0x04	/* interrupt enable */
+#define  ATI_REG_IER_IN_XRUN_EN		(1U<<0)
+#define  ATI_REG_IER_IO_STATUS_EN	(1U<<1)
+#define  ATI_REG_IER_OUT_XRUN_EN	(1U<<2)
+#define  ATI_REG_IER_OUT_XRUN_COND	(1U<<3)
+#define  ATI_REG_IER_SPDF_XRUN_EN	(1U<<4)
+#define  ATI_REG_IER_SPDF_STATUS_EN	(1U<<5)
+#define  ATI_REG_IER_PHYS_INTR_EN	(1U<<8)
+#define  ATI_REG_IER_PHYS_MISMATCH_EN	(1U<<9)
+#define  ATI_REG_IER_CODEC0_INTR_EN	(1U<<10)
+#define  ATI_REG_IER_CODEC1_INTR_EN	(1U<<11)
+#define  ATI_REG_IER_CODEC2_INTR_EN	(1U<<12)
+#define  ATI_REG_IER_NEW_FRAME_EN	(1U<<13)	/* (RO */
+#define  ATI_REG_IER_SET_BUS_BUSY	(1U<<14)	/* (WO) audio is running */
+
+#define ATI_REG_CMD			0x08	/* command */
+#define  ATI_REG_CMD_POWERDOWN		(1U<<0)
+#define  ATI_REG_CMD_RECEIVE_EN		(1U<<1)
+#define  ATI_REG_CMD_SEND_EN		(1U<<2)
+#define  ATI_REG_CMD_STATUS_MEM		(1U<<3)
+#define  ATI_REG_CMD_SPDF_OUT_EN	(1U<<4)
+#define  ATI_REG_CMD_SPDF_STATUS_MEM	(1U<<5)
+#define  ATI_REG_CMD_SPDF_THRESHOLD	(3U<<6)
+#define  ATI_REG_CMD_SPDF_THRESHOLD_SHIFT	6
+#define  ATI_REG_CMD_IN_DMA_EN		(1U<<8)
+#define  ATI_REG_CMD_OUT_DMA_EN		(1U<<9)
+#define  ATI_REG_CMD_SPDF_DMA_EN	(1U<<10)
+#define  ATI_REG_CMD_SPDF_OUT_STOPPED	(1U<<11)
+#define  ATI_REG_CMD_SPDF_CONFIG_MASK	(7U<<12)
+#define   ATI_REG_CMD_SPDF_CONFIG_34	(1U<<12)
+#define   ATI_REG_CMD_SPDF_CONFIG_78	(2U<<12)
+#define   ATI_REG_CMD_SPDF_CONFIG_69	(3U<<12)
+#define   ATI_REG_CMD_SPDF_CONFIG_01	(4U<<12)
+#define  ATI_REG_CMD_INTERLEAVE_SPDF	(1U<<16)
+#define  ATI_REG_CMD_AUDIO_PRESENT	(1U<<20)
+#define  ATI_REG_CMD_INTERLEAVE_IN	(1U<<21)
+#define  ATI_REG_CMD_INTERLEAVE_OUT	(1U<<22)
+#define  ATI_REG_CMD_LOOPBACK_EN	(1U<<23)
+#define  ATI_REG_CMD_PACKED_DIS		(1U<<24)
+#define  ATI_REG_CMD_BURST_EN		(1U<<25)
+#define  ATI_REG_CMD_PANIC_EN		(1U<<26)
+#define  ATI_REG_CMD_MODEM_PRESENT	(1U<<27)
+#define  ATI_REG_CMD_ACLINK_ACTIVE	(1U<<28)
+#define  ATI_REG_CMD_AC_SOFT_RESET	(1U<<29)
+#define  ATI_REG_CMD_AC_SYNC		(1U<<30)
+#define  ATI_REG_CMD_AC_RESET		(1U<<31)
+
+#define ATI_REG_PHYS_OUT_ADDR		0x0c
+#define  ATI_REG_PHYS_OUT_CODEC_MASK	(3U<<0)
+#define  ATI_REG_PHYS_OUT_RW		(1U<<2)
+#define  ATI_REG_PHYS_OUT_ADDR_EN	(1U<<8)
+#define  ATI_REG_PHYS_OUT_ADDR_SHIFT	9
+#define  ATI_REG_PHYS_OUT_DATA_SHIFT	16
+
+#define ATI_REG_PHYS_IN_ADDR		0x10
+#define  ATI_REG_PHYS_IN_READ_FLAG	(1U<<8)
+#define  ATI_REG_PHYS_IN_ADDR_SHIFT	9
+#define  ATI_REG_PHYS_IN_DATA_SHIFT	16
+
+#define ATI_REG_SLOTREQ			0x14
+
+#define ATI_REG_COUNTER			0x18
+#define  ATI_REG_COUNTER_SLOT		(3U<<0)	/* slot # */
+#define  ATI_REG_COUNTER_BITCLOCK	(31U<<8)
+
+#define ATI_REG_IN_FIFO_THRESHOLD	0x1c
+
+#define ATI_REG_IN_DMA_LINKPTR		0x20
+#define ATI_REG_IN_DMA_DT_START		0x24	/* RO */
+#define ATI_REG_IN_DMA_DT_NEXT		0x28	/* RO */
+#define ATI_REG_IN_DMA_DT_CUR		0x2c	/* RO */
+#define ATI_REG_IN_DMA_DT_SIZE		0x30
+
+#define ATI_REG_OUT_DMA_SLOT		0x34
+#define  ATI_REG_OUT_DMA_SLOT_BIT(x)	(1U << ((x) - 3))
+#define  ATI_REG_OUT_DMA_SLOT_MASK	0x1ff
+#define  ATI_REG_OUT_DMA_THRESHOLD_MASK	0xf800
+#define  ATI_REG_OUT_DMA_THRESHOLD_SHIFT	11
+
+#define ATI_REG_OUT_DMA_LINKPTR		0x38
+#define ATI_REG_OUT_DMA_DT_START	0x3c	/* RO */
+#define ATI_REG_OUT_DMA_DT_NEXT		0x40	/* RO */
+#define ATI_REG_OUT_DMA_DT_CUR		0x44	/* RO */
+#define ATI_REG_OUT_DMA_DT_SIZE		0x48
+
+#define ATI_REG_SPDF_CMD		0x4c
+#define  ATI_REG_SPDF_CMD_LFSR		(1U<<4)
+#define  ATI_REG_SPDF_CMD_SINGLE_CH	(1U<<5)
+#define  ATI_REG_SPDF_CMD_LFSR_ACC	(0xff<<8)	/* RO */
+
+#define ATI_REG_SPDF_DMA_LINKPTR	0x50
+#define ATI_REG_SPDF_DMA_DT_START	0x54	/* RO */
+#define ATI_REG_SPDF_DMA_DT_NEXT	0x58	/* RO */
+#define ATI_REG_SPDF_DMA_DT_CUR		0x5c	/* RO */
+#define ATI_REG_SPDF_DMA_DT_SIZE	0x60
+
+#define ATI_REG_MODEM_MIRROR		0x7c
+#define ATI_REG_AUDIO_MIRROR		0x80
+
+#define ATI_REG_6CH_REORDER		0x84	/* reorder slots for 6ch */
+#define  ATI_REG_6CH_REORDER_EN		(1U<<0)	/* 3,4,7,8,6,9 -> 3,4,6,9,7,8 */
+
+#define ATI_REG_FIFO_FLUSH		0x88
+#define  ATI_REG_FIFO_OUT_FLUSH		(1U<<0)
+#define  ATI_REG_FIFO_IN_FLUSH		(1U<<1)
+
+/* LINKPTR */
+#define  ATI_REG_LINKPTR_EN		(1U<<0)
+
+/* [INT|OUT|SPDIF]_DMA_DT_SIZE */
+#define  ATI_REG_DMA_DT_SIZE		(0xffffU<<0)
+#define  ATI_REG_DMA_FIFO_USED		(0x1fU<<16)
+#define  ATI_REG_DMA_FIFO_FREE		(0x1fU<<21)
+#define  ATI_REG_DMA_STATE		(7U<<26)
+
+
+#define ATI_MAX_DESCRIPTORS	256	/* max number of descriptor packets */
+
+
+struct atiixp;
+
+/*
+ * DMA packate descriptor
+ */
+
+struct atiixp_dma_desc {
+	u32 addr;	/* DMA buffer address */
+	u16 status;	/* status bits */
+	u16 size;	/* size of the packet in dwords */
+	u32 next;	/* address of the next packet descriptor */
+};
+
+/*
+ * stream enum
+ */
+enum { ATI_DMA_PLAYBACK, ATI_DMA_CAPTURE, ATI_DMA_SPDIF, NUM_ATI_DMAS }; /* DMAs */
+enum { ATI_PCM_OUT, ATI_PCM_IN, ATI_PCM_SPDIF, NUM_ATI_PCMS }; /* AC97 pcm slots */
+enum { ATI_PCMDEV_ANALOG, ATI_PCMDEV_DIGITAL, NUM_ATI_PCMDEVS }; /* pcm devices */
+
+#define NUM_ATI_CODECS	3
+
+
+/*
+ * constants and callbacks for each DMA type
+ */
+struct atiixp_dma_ops {
+	int type;			/* ATI_DMA_XXX */
+	unsigned int llp_offset;	/* LINKPTR offset */
+	unsigned int dt_cur;		/* DT_CUR offset */
+	/* called from open callback */
+	void (*enable_dma)(struct atiixp *chip, int on);
+	/* called from trigger (START/STOP) */
+	void (*enable_transfer)(struct atiixp *chip, int on);
+ 	/* called from trigger (STOP only) */
+	void (*flush_dma)(struct atiixp *chip);
+};
+
+/*
+ * DMA stream
+ */
+struct atiixp_dma {
+	const struct atiixp_dma_ops *ops;
+	struct snd_dma_buffer desc_buf;
+	struct snd_pcm_substream *substream;	/* assigned PCM substream */
+	unsigned int buf_addr, buf_bytes;	/* DMA buffer address, bytes */
+	unsigned int period_bytes, periods;
+	int opened;
+	int running;
+	int suspended;
+	int pcm_open_flag;
+	int ac97_pcm_type;	/* index # of ac97_pcm to access, -1 = not used */
+	unsigned int saved_curptr;
+};
+
+/*
+ * ATI IXP chip
+ */
+struct atiixp {
+	struct snd_card *card;
+	struct pci_dev *pci;
+
+	unsigned long addr;
+	void __iomem *remap_addr;
+	int irq;
+	
+	struct snd_ac97_bus *ac97_bus;
+	struct snd_ac97 *ac97[NUM_ATI_CODECS];
+
+	spinlock_t reg_lock;
+
+	struct atiixp_dma dmas[NUM_ATI_DMAS];
+	struct ac97_pcm *pcms[NUM_ATI_PCMS];
+	struct snd_pcm *pcmdevs[NUM_ATI_PCMDEVS];
+
+	int max_channels;		/* max. channels for PCM out */
+
+	unsigned int codec_not_ready_bits;	/* for codec detection */
+
+	int spdif_over_aclink;		/* passed from the module option */
+	struct semaphore open_mutex;	/* playback open mutex */
+};
+
+
+/*
+ */
+static struct pci_device_id snd_atiixp_ids[] = {
+	{ 0x1002, 0x4341, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* SB200 */
+	{ 0x1002, 0x4361, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* SB300 */
+	{ 0x1002, 0x4370, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* SB400 */
+	{ 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, snd_atiixp_ids);
+
+
+/*
+ * lowlevel functions
+ */
+
+/*
+ * update the bits of the given register.
+ * return 1 if the bits changed.
+ */
+static int snd_atiixp_update_bits(struct atiixp *chip, unsigned int reg,
+				 unsigned int mask, unsigned int value)
+{
+	void __iomem *addr = (char*)chip->remap_addr + reg;
+	unsigned int data, old_data;
+	old_data = data = readl(addr);
+	data &= ~mask;
+	data |= value;
+	if (old_data == data)
+		return 0;
+	writel(data, addr);
+	return 1;
+}
+
+/*
+ * macros for easy use
+ */
+#define atiixp_write(chip,reg,value) \
+	writel(value, (char*)chip->remap_addr + ATI_REG_##reg)
+#define atiixp_read(chip,reg) \
+	readl((char*)chip->remap_addr + ATI_REG_##reg)
+#define atiixp_update(chip,reg,mask,val) \
+	snd_atiixp_update_bits(chip, ATI_REG_##reg, mask, val)
+
+/*
+ * handling DMA packets
+ *
+ * we allocate a linear buffer for the DMA, and split it to  each packet.
+ * in a future version, a scatter-gather buffer should be implemented.
+ */
+
+#define ATI_DESC_LIST_SIZE \
+	PAGE_ALIGN(ATI_MAX_DESCRIPTORS * sizeof(struct atiixp_dma_desc))
+
+/*
+ * build packets ring for the given buffer size.
+ *
+ * IXP handles the buffer descriptors, which are connected as a linked
+ * list.  although we can change the list dynamically, in this version,
+ * a static RING of buffer descriptors is used.
+ *
+ * the ring is built in this function, and is set up to the hardware. 
+ */
+static int atiixp_build_dma_packets(struct atiixp *chip, struct atiixp_dma *dma,
+				    struct snd_pcm_substream *substream,
+				    unsigned int periods,
+				    unsigned int period_bytes)
+{
+	unsigned int i;
+	u32 addr, desc_addr;
+	unsigned long flags;
+
+	if (periods > ATI_MAX_DESCRIPTORS)
+		return -ENOMEM;
+
+	if (dma->desc_buf.area == NULL) {
+		if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
+					snd_dma_pci_data(chip->pci),
+					ATI_DESC_LIST_SIZE,
+					&dma->desc_buf) < 0)
+			return -ENOMEM;
+		dma->period_bytes = dma->periods = 0; /* clear */
+	}
+
+	/* reset DMA before changing the descriptor table */
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	writel(0, (char*)chip->remap_addr + dma->ops->llp_offset);
+	dma->ops->enable_dma(chip, 0);
+	dma->ops->enable_dma(chip, 1);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+        if (dma->periods == periods && dma->period_bytes == period_bytes)
+        {
+            writel((u32)dma->desc_buf.addr | ATI_REG_LINKPTR_EN,
+                   (char*)chip->remap_addr + dma->ops->llp_offset);
+            return 0;
+        }
+
+	/* fill the entries */
+	addr = (u32)substream->runtime->dma_addr;
+	desc_addr = (u32)dma->desc_buf.addr;
+	for (i = 0; i < periods; i++) {
+		struct atiixp_dma_desc *desc;
+		desc = &((struct atiixp_dma_desc *)dma->desc_buf.area)[i];
+		desc->addr = cpu_to_le32(addr);
+		desc->status = 0;
+		desc->size = period_bytes >> 2; /* in dwords */
+		desc_addr += sizeof(struct atiixp_dma_desc);
+		if (i == periods - 1)
+			desc->next = cpu_to_le32((u32)dma->desc_buf.addr);
+		else
+			desc->next = cpu_to_le32(desc_addr);
+		addr += period_bytes;
+	}
+
+	writel((u32)dma->desc_buf.addr | ATI_REG_LINKPTR_EN,
+	       (char*)chip->remap_addr + dma->ops->llp_offset);
+
+	dma->period_bytes = period_bytes;
+	dma->periods = periods;
+
+	return 0;
+}
+
+/*
+ * remove the ring buffer and release it if assigned
+ */
+static void atiixp_clear_dma_packets(struct atiixp *chip, struct atiixp_dma *dma,
+				     struct snd_pcm_substream *substream)
+{
+	if (dma->desc_buf.area) {
+		writel(0, (char*)chip->remap_addr + dma->ops->llp_offset);
+		snd_dma_free_pages(&dma->desc_buf);
+		dma->desc_buf.area = NULL;
+	}
+}
+
+/*
+ * AC97 interface
+ */
+static int snd_atiixp_acquire_codec(struct atiixp *chip)
+{
+	int timeout = 1000;
+
+	while (atiixp_read(chip, PHYS_OUT_ADDR) & ATI_REG_PHYS_OUT_ADDR_EN) {
+		if (! timeout--) {
+			snd_printk(KERN_WARNING "atiixp: codec acquire timeout\n");
+			return -EBUSY;
+		}
+		udelay(1);
+	}
+	return 0;
+}
+
+static unsigned short snd_atiixp_codec_read(struct atiixp *chip, unsigned short codec, unsigned short reg)
+{
+	unsigned int data;
+	int timeout;
+
+	if (snd_atiixp_acquire_codec(chip) < 0)
+		return 0xffff;
+	data = (reg << ATI_REG_PHYS_OUT_ADDR_SHIFT) |
+		ATI_REG_PHYS_OUT_ADDR_EN |
+		ATI_REG_PHYS_OUT_RW |
+		codec;
+	atiixp_write(chip, PHYS_OUT_ADDR, data);
+	if (snd_atiixp_acquire_codec(chip) < 0)
+		return 0xffff;
+	timeout = 1000;
+	do {
+		data = atiixp_read(chip, PHYS_IN_ADDR);
+		if (data & ATI_REG_PHYS_IN_READ_FLAG)
+			return data >> ATI_REG_PHYS_IN_DATA_SHIFT;
+		udelay(1);
+	} while (--timeout);
+	/* time out may happen during reset */
+	if (reg < 0x7c)
+		snd_printk(KERN_WARNING "atiixp: codec read timeout (reg %x)\n", reg);
+	return 0xffff;
+}
+
+
+static void snd_atiixp_codec_write(struct atiixp *chip, unsigned short codec,
+				   unsigned short reg, unsigned short val)
+{
+	unsigned int data;
+    
+	if (snd_atiixp_acquire_codec(chip) < 0)
+		return;
+	data = ((unsigned int)val << ATI_REG_PHYS_OUT_DATA_SHIFT) |
+		((unsigned int)reg << ATI_REG_PHYS_OUT_ADDR_SHIFT) |
+		ATI_REG_PHYS_OUT_ADDR_EN | codec;
+	atiixp_write(chip, PHYS_OUT_ADDR, data);
+}
+
+
+static unsigned short snd_atiixp_ac97_read(struct snd_ac97 *ac97,
+					   unsigned short reg)
+{
+	struct atiixp *chip = ac97->private_data;
+	return snd_atiixp_codec_read(chip, ac97->num, reg);
+    
+}
+
+static void snd_atiixp_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
+				  unsigned short val)
+{
+	struct atiixp *chip = ac97->private_data;
+	snd_atiixp_codec_write(chip, ac97->num, reg, val);
+}
+
+/*
+ * reset AC link
+ */
+static int snd_atiixp_aclink_reset(struct atiixp *chip)
+{
+	int timeout;
+
+	/* reset powerdoewn */
+	if (atiixp_update(chip, CMD, ATI_REG_CMD_POWERDOWN, 0))
+		udelay(10);
+
+	/* perform a software reset */
+	atiixp_update(chip, CMD, ATI_REG_CMD_AC_SOFT_RESET, ATI_REG_CMD_AC_SOFT_RESET);
+	atiixp_read(chip, CMD);
+	udelay(10);
+	atiixp_update(chip, CMD, ATI_REG_CMD_AC_SOFT_RESET, 0);
+    
+	timeout = 10;
+	while (! (atiixp_read(chip, CMD) & ATI_REG_CMD_ACLINK_ACTIVE)) {
+		/* do a hard reset */
+		atiixp_update(chip, CMD, ATI_REG_CMD_AC_SYNC|ATI_REG_CMD_AC_RESET,
+			      ATI_REG_CMD_AC_SYNC);
+		atiixp_read(chip, CMD);
+		mdelay(1);
+		atiixp_update(chip, CMD, ATI_REG_CMD_AC_RESET, ATI_REG_CMD_AC_RESET);
+		if (--timeout) {
+			snd_printk(KERN_ERR "atiixp: codec reset timeout\n");
+			break;
+		}
+	}
+
+	/* deassert RESET and assert SYNC to make sure */
+	atiixp_update(chip, CMD, ATI_REG_CMD_AC_SYNC|ATI_REG_CMD_AC_RESET,
+		      ATI_REG_CMD_AC_SYNC|ATI_REG_CMD_AC_RESET);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int snd_atiixp_aclink_down(struct atiixp *chip)
+{
+	// if (atiixp_read(chip, MODEM_MIRROR) & 0x1) /* modem running, too? */
+	//	return -EBUSY;
+	atiixp_update(chip, CMD,
+		     ATI_REG_CMD_POWERDOWN | ATI_REG_CMD_AC_RESET,
+		     ATI_REG_CMD_POWERDOWN);
+	return 0;
+}
+#endif
+
+/*
+ * auto-detection of codecs
+ *
+ * the IXP chip can generate interrupts for the non-existing codecs.
+ * NEW_FRAME interrupt is used to make sure that the interrupt is generated
+ * even if all three codecs are connected.
+ */
+
+#define ALL_CODEC_NOT_READY \
+	    (ATI_REG_ISR_CODEC0_NOT_READY |\
+	     ATI_REG_ISR_CODEC1_NOT_READY |\
+	     ATI_REG_ISR_CODEC2_NOT_READY)
+#define CODEC_CHECK_BITS (ALL_CODEC_NOT_READY|ATI_REG_ISR_NEW_FRAME)
+
+static int snd_atiixp_codec_detect(struct atiixp *chip)
+{
+	int timeout;
+
+	chip->codec_not_ready_bits = 0;
+	atiixp_write(chip, IER, CODEC_CHECK_BITS);
+	/* wait for the interrupts */
+	timeout = 50;
+	while (timeout-- > 0) {
+		mdelay(1);
+		if (chip->codec_not_ready_bits)
+			break;
+	}
+	atiixp_write(chip, IER, 0); /* disable irqs */
+
+	if ((chip->codec_not_ready_bits & ALL_CODEC_NOT_READY) == ALL_CODEC_NOT_READY) {
+		snd_printk(KERN_ERR "atiixp: no codec detected!\n");
+		return -ENXIO;
+	}
+	return 0;
+}
+
+
+/*
+ * enable DMA and irqs
+ */
+static int snd_atiixp_chip_start(struct atiixp *chip)
+{
+	unsigned int reg;
+
+	/* set up spdif, enable burst mode */
+	reg = atiixp_read(chip, CMD);
+	reg |= 0x02 << ATI_REG_CMD_SPDF_THRESHOLD_SHIFT;
+	reg |= ATI_REG_CMD_BURST_EN;
+	atiixp_write(chip, CMD, reg);
+
+	reg = atiixp_read(chip, SPDF_CMD);
+	reg &= ~(ATI_REG_SPDF_CMD_LFSR|ATI_REG_SPDF_CMD_SINGLE_CH);
+	atiixp_write(chip, SPDF_CMD, reg);
+
+	/* clear all interrupt source */
+	atiixp_write(chip, ISR, 0xffffffff);
+	/* enable irqs */
+	atiixp_write(chip, IER,
+		     ATI_REG_IER_IO_STATUS_EN |
+		     ATI_REG_IER_IN_XRUN_EN |
+		     ATI_REG_IER_OUT_XRUN_EN |
+		     ATI_REG_IER_SPDF_XRUN_EN |
+		     ATI_REG_IER_SPDF_STATUS_EN);
+	return 0;
+}
+
+
+/*
+ * disable DMA and IRQs
+ */
+static int snd_atiixp_chip_stop(struct atiixp *chip)
+{
+	/* clear interrupt source */
+	atiixp_write(chip, ISR, atiixp_read(chip, ISR));
+	/* disable irqs */
+	atiixp_write(chip, IER, 0);
+	return 0;
+}
+
+
+/*
+ * PCM section
+ */
+
+/*
+ * pointer callback simplly reads XXX_DMA_DT_CUR register as the current
+ * position.  when SG-buffer is implemented, the offset must be calculated
+ * correctly...
+ */
+static snd_pcm_uframes_t snd_atiixp_pcm_pointer(struct snd_pcm_substream *substream)
+{
+	struct atiixp *chip = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct atiixp_dma *dma = runtime->private_data;
+	unsigned int curptr;
+	int timeout = 1000;
+
+	while (timeout--) {
+		curptr = readl((char*)chip->remap_addr + dma->ops->dt_cur);
+		if (curptr < dma->buf_addr)
+			continue;
+		curptr -= dma->buf_addr;
+		if (curptr >= dma->buf_bytes)
+			continue;
+		return bytes_to_frames(runtime, curptr);
+	}
+	snd_printd("atiixp: invalid DMA pointer read 0x%x (buf=%x)\n",
+		   readl((char*)chip->remap_addr + dma->ops->dt_cur), dma->buf_addr);
+	return 0;
+}
+
+/*
+ * XRUN detected, and stop the PCM substream
+ */
+static void snd_atiixp_xrun_dma(struct atiixp *chip, struct atiixp_dma *dma)
+{
+	if (! dma->substream || ! dma->running)
+		return;
+	snd_printdd("atiixp: XRUN detected (DMA %d)\n", dma->ops->type);
+	snd_pcm_stop(dma->substream, SNDRV_PCM_STATE_XRUN);
+}
+
+/*
+ * the period ack.  update the substream.
+ */
+static void snd_atiixp_update_dma(struct atiixp *chip, struct atiixp_dma *dma)
+{
+	if (! dma->substream || ! dma->running)
+		return;
+	snd_pcm_period_elapsed(dma->substream);
+}
+
+/* set BUS_BUSY interrupt bit if any DMA is running */
+/* call with spinlock held */
+static void snd_atiixp_check_bus_busy(struct atiixp *chip)
+{
+	unsigned int bus_busy;
+	if (atiixp_read(chip, CMD) & (ATI_REG_CMD_SEND_EN |
+				      ATI_REG_CMD_RECEIVE_EN |
+				      ATI_REG_CMD_SPDF_OUT_EN))
+		bus_busy = ATI_REG_IER_SET_BUS_BUSY;
+	else
+		bus_busy = 0;
+	atiixp_update(chip, IER, ATI_REG_IER_SET_BUS_BUSY, bus_busy);
+}
+
+/* common trigger callback
+ * calling the lowlevel callbacks in it
+ */
+static int snd_atiixp_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct atiixp *chip = snd_pcm_substream_chip(substream);
+	struct atiixp_dma *dma = substream->runtime->private_data;
+	int err = 0;
+
+	snd_assert(dma->ops->enable_transfer && dma->ops->flush_dma, return -EINVAL);
+
+	spin_lock(&chip->reg_lock);
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		dma->ops->enable_transfer(chip, 1);
+		dma->running = 1;
+		dma->suspended = 0;
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		dma->ops->enable_transfer(chip, 0);
+		dma->running = 0;
+		dma->suspended = cmd == SNDRV_PCM_TRIGGER_SUSPEND;
+		break;
+	default:
+		err = -EINVAL;
+		break;
+	}
+	if (! err) {
+		snd_atiixp_check_bus_busy(chip);
+		if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+			dma->ops->flush_dma(chip);
+			snd_atiixp_check_bus_busy(chip);
+		}
+	}
+	spin_unlock(&chip->reg_lock);
+	return err;
+}
+
+
+/*
+ * lowlevel callbacks for each DMA type
+ *
+ * every callback is supposed to be called in chip->reg_lock spinlock
+ */
+
+/* flush FIFO of analog OUT DMA */
+static void atiixp_out_flush_dma(struct atiixp *chip)
+{
+	atiixp_write(chip, FIFO_FLUSH, ATI_REG_FIFO_OUT_FLUSH);
+}
+
+/* enable/disable analog OUT DMA */
+static void atiixp_out_enable_dma(struct atiixp *chip, int on)
+{
+	unsigned int data;
+	data = atiixp_read(chip, CMD);
+	if (on) {
+		if (data & ATI_REG_CMD_OUT_DMA_EN)
+			return;
+		atiixp_out_flush_dma(chip);
+		data |= ATI_REG_CMD_OUT_DMA_EN;
+	} else
+		data &= ~ATI_REG_CMD_OUT_DMA_EN;
+	atiixp_write(chip, CMD, data);
+}
+
+/* start/stop transfer over OUT DMA */
+static void atiixp_out_enable_transfer(struct atiixp *chip, int on)
+{
+	atiixp_update(chip, CMD, ATI_REG_CMD_SEND_EN,
+		      on ? ATI_REG_CMD_SEND_EN : 0);
+}
+
+/* enable/disable analog IN DMA */
+static void atiixp_in_enable_dma(struct atiixp *chip, int on)
+{
+	atiixp_update(chip, CMD, ATI_REG_CMD_IN_DMA_EN,
+		      on ? ATI_REG_CMD_IN_DMA_EN : 0);
+}
+
+/* start/stop analog IN DMA */
+static void atiixp_in_enable_transfer(struct atiixp *chip, int on)
+{
+	if (on) {
+		unsigned int data = atiixp_read(chip, CMD);
+		if (! (data & ATI_REG_CMD_RECEIVE_EN)) {
+			data |= ATI_REG_CMD_RECEIVE_EN;
+#if 0 /* FIXME: this causes the endless loop */
+			/* wait until slot 3/4 are finished */
+			while ((atiixp_read(chip, COUNTER) &
+				ATI_REG_COUNTER_SLOT) != 5)
+				;
+#endif
+			atiixp_write(chip, CMD, data);
+		}
+	} else
+		atiixp_update(chip, CMD, ATI_REG_CMD_RECEIVE_EN, 0);
+}
+
+/* flush FIFO of analog IN DMA */
+static void atiixp_in_flush_dma(struct atiixp *chip)
+{
+	atiixp_write(chip, FIFO_FLUSH, ATI_REG_FIFO_IN_FLUSH);
+}
+
+/* enable/disable SPDIF OUT DMA */
+static void atiixp_spdif_enable_dma(struct atiixp *chip, int on)
+{
+	atiixp_update(chip, CMD, ATI_REG_CMD_SPDF_DMA_EN,
+		      on ? ATI_REG_CMD_SPDF_DMA_EN : 0);
+}
+
+/* start/stop SPDIF OUT DMA */
+static void atiixp_spdif_enable_transfer(struct atiixp *chip, int on)
+{
+	unsigned int data;
+	data = atiixp_read(chip, CMD);
+	if (on)
+		data |= ATI_REG_CMD_SPDF_OUT_EN;
+	else
+		data &= ~ATI_REG_CMD_SPDF_OUT_EN;
+	atiixp_write(chip, CMD, data);
+}
+
+/* flush FIFO of SPDIF OUT DMA */
+static void atiixp_spdif_flush_dma(struct atiixp *chip)
+{
+	int timeout;
+
+	/* DMA off, transfer on */
+	atiixp_spdif_enable_dma(chip, 0);
+	atiixp_spdif_enable_transfer(chip, 1);
+	
+	timeout = 100;
+	do {
+		if (! (atiixp_read(chip, SPDF_DMA_DT_SIZE) & ATI_REG_DMA_FIFO_USED))
+			break;
+		udelay(1);
+	} while (timeout-- > 0);
+
+	atiixp_spdif_enable_transfer(chip, 0);
+}
+
+/* set up slots and formats for SPDIF OUT */
+static int snd_atiixp_spdif_prepare(struct snd_pcm_substream *substream)
+{
+	struct atiixp *chip = snd_pcm_substream_chip(substream);
+
+	spin_lock_irq(&chip->reg_lock);
+	if (chip->spdif_over_aclink) {
+		unsigned int data;
+		/* enable slots 10/11 */
+		atiixp_update(chip, CMD, ATI_REG_CMD_SPDF_CONFIG_MASK,
+			      ATI_REG_CMD_SPDF_CONFIG_01);
+		data = atiixp_read(chip, OUT_DMA_SLOT) & ~ATI_REG_OUT_DMA_SLOT_MASK;
+		data |= ATI_REG_OUT_DMA_SLOT_BIT(10) |
+			ATI_REG_OUT_DMA_SLOT_BIT(11);
+		data |= 0x04 << ATI_REG_OUT_DMA_THRESHOLD_SHIFT;
+		atiixp_write(chip, OUT_DMA_SLOT, data);
+		atiixp_update(chip, CMD, ATI_REG_CMD_INTERLEAVE_OUT,
+			      substream->runtime->format == SNDRV_PCM_FORMAT_S16_LE ?
+			      ATI_REG_CMD_INTERLEAVE_OUT : 0);
+	} else {
+		atiixp_update(chip, CMD, ATI_REG_CMD_SPDF_CONFIG_MASK, 0);
+		atiixp_update(chip, CMD, ATI_REG_CMD_INTERLEAVE_SPDF, 0);
+	}
+	spin_unlock_irq(&chip->reg_lock);
+	return 0;
+}
+
+/* set up slots and formats for analog OUT */
+static int snd_atiixp_playback_prepare(struct snd_pcm_substream *substream)
+{
+	struct atiixp *chip = snd_pcm_substream_chip(substream);
+	unsigned int data;
+
+	spin_lock_irq(&chip->reg_lock);
+	data = atiixp_read(chip, OUT_DMA_SLOT) & ~ATI_REG_OUT_DMA_SLOT_MASK;
+	switch (substream->runtime->channels) {
+	case 8:
+		data |= ATI_REG_OUT_DMA_SLOT_BIT(10) |
+			ATI_REG_OUT_DMA_SLOT_BIT(11);
+		/* fallthru */
+	case 6:
+		data |= ATI_REG_OUT_DMA_SLOT_BIT(7) |
+			ATI_REG_OUT_DMA_SLOT_BIT(8);
+		/* fallthru */
+	case 4:
+		data |= ATI_REG_OUT_DMA_SLOT_BIT(6) |
+			ATI_REG_OUT_DMA_SLOT_BIT(9);
+		/* fallthru */
+	default:
+		data |= ATI_REG_OUT_DMA_SLOT_BIT(3) |
+			ATI_REG_OUT_DMA_SLOT_BIT(4);
+		break;
+	}
+
+	/* set output threshold */
+	data |= 0x04 << ATI_REG_OUT_DMA_THRESHOLD_SHIFT;
+	atiixp_write(chip, OUT_DMA_SLOT, data);
+
+	atiixp_update(chip, CMD, ATI_REG_CMD_INTERLEAVE_OUT,
+		      substream->runtime->format == SNDRV_PCM_FORMAT_S16_LE ?
+		      ATI_REG_CMD_INTERLEAVE_OUT : 0);
+
+	/*
+	 * enable 6 channel re-ordering bit if needed
+	 */
+	atiixp_update(chip, 6CH_REORDER, ATI_REG_6CH_REORDER_EN,
+		      substream->runtime->channels >= 6 ? ATI_REG_6CH_REORDER_EN: 0);
+    
+	spin_unlock_irq(&chip->reg_lock);
+	return 0;
+}
+
+/* set up slots and formats for analog IN */
+static int snd_atiixp_capture_prepare(struct snd_pcm_substream *substream)
+{
+	struct atiixp *chip = snd_pcm_substream_chip(substream);
+
+	spin_lock_irq(&chip->reg_lock);
+	atiixp_update(chip, CMD, ATI_REG_CMD_INTERLEAVE_IN,
+		      substream->runtime->format == SNDRV_PCM_FORMAT_S16_LE ?
+		      ATI_REG_CMD_INTERLEAVE_IN : 0);
+	spin_unlock_irq(&chip->reg_lock);
+	return 0;
+}
+
+/*
+ * hw_params - allocate the buffer and set up buffer descriptors
+ */
+static int snd_atiixp_pcm_hw_params(struct snd_pcm_substream *substream,
+				    struct snd_pcm_hw_params *hw_params)
+{
+	struct atiixp *chip = snd_pcm_substream_chip(substream);
+	struct atiixp_dma *dma = substream->runtime->private_data;
+	int err;
+
+	err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+	if (err < 0)
+		return err;
+	dma->buf_addr = substream->runtime->dma_addr;
+	dma->buf_bytes = params_buffer_bytes(hw_params);
+
+	err = atiixp_build_dma_packets(chip, dma, substream,
+				       params_periods(hw_params),
+				       params_period_bytes(hw_params));
+	if (err < 0)
+		return err;
+
+	if (dma->ac97_pcm_type >= 0) {
+		struct ac97_pcm *pcm = chip->pcms[dma->ac97_pcm_type];
+		/* PCM is bound to AC97 codec(s)
+		 * set up the AC97 codecs
+		 */
+		if (dma->pcm_open_flag) {
+			snd_ac97_pcm_close(pcm);
+			dma->pcm_open_flag = 0;
+		}
+		err = snd_ac97_pcm_open(pcm, params_rate(hw_params),
+					params_channels(hw_params),
+					pcm->r[0].slots);
+		if (err >= 0)
+			dma->pcm_open_flag = 1;
+	}
+
+	return err;
+}
+
+static int snd_atiixp_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+	struct atiixp *chip = snd_pcm_substream_chip(substream);
+	struct atiixp_dma *dma = substream->runtime->private_data;
+
+	if (dma->pcm_open_flag) {
+		struct ac97_pcm *pcm = chip->pcms[dma->ac97_pcm_type];
+		snd_ac97_pcm_close(pcm);
+		dma->pcm_open_flag = 0;
+	}
+	atiixp_clear_dma_packets(chip, dma, substream);
+	snd_pcm_lib_free_pages(substream);
+	return 0;
+}
+
+
+/*
+ * pcm hardware definition, identical for all DMA types
+ */
+static struct snd_pcm_hardware snd_atiixp_pcm_hw =
+{
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_PAUSE |
+				 SNDRV_PCM_INFO_RESUME |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+	.formats =		SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
+	.rates =		SNDRV_PCM_RATE_48000,
+	.rate_min =		48000,
+	.rate_max =		48000,
+	.channels_min =		2,
+	.channels_max =		2,
+	.buffer_bytes_max =	256 * 1024,
+	.period_bytes_min =	32,
+	.period_bytes_max =	128 * 1024,
+	.periods_min =		2,
+	.periods_max =		ATI_MAX_DESCRIPTORS,
+};
+
+static int snd_atiixp_pcm_open(struct snd_pcm_substream *substream,
+			       struct atiixp_dma *dma, int pcm_type)
+{
+	struct atiixp *chip = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	int err;
+
+	snd_assert(dma->ops && dma->ops->enable_dma, return -EINVAL);
+
+	if (dma->opened)
+		return -EBUSY;
+	dma->substream = substream;
+	runtime->hw = snd_atiixp_pcm_hw;
+	dma->ac97_pcm_type = pcm_type;
+	if (pcm_type >= 0) {
+		runtime->hw.rates = chip->pcms[pcm_type]->rates;
+		snd_pcm_limit_hw_rates(runtime);
+	} else {
+		/* direct SPDIF */
+		runtime->hw.formats = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE;
+	}
+	if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+		return err;
+	runtime->private_data = dma;
+
+	/* enable DMA bits */
+	spin_lock_irq(&chip->reg_lock);
+	dma->ops->enable_dma(chip, 1);
+	spin_unlock_irq(&chip->reg_lock);
+	dma->opened = 1;
+
+	return 0;
+}
+
+static int snd_atiixp_pcm_close(struct snd_pcm_substream *substream,
+				struct atiixp_dma *dma)
+{
+	struct atiixp *chip = snd_pcm_substream_chip(substream);
+	/* disable DMA bits */
+	snd_assert(dma->ops && dma->ops->enable_dma, return -EINVAL);
+	spin_lock_irq(&chip->reg_lock);
+	dma->ops->enable_dma(chip, 0);
+	spin_unlock_irq(&chip->reg_lock);
+	dma->substream = NULL;
+	dma->opened = 0;
+	return 0;
+}
+
+/*
+ */
+static int snd_atiixp_playback_open(struct snd_pcm_substream *substream)
+{
+	struct atiixp *chip = snd_pcm_substream_chip(substream);
+	int err;
+
+	down(&chip->open_mutex);
+	err = snd_atiixp_pcm_open(substream, &chip->dmas[ATI_DMA_PLAYBACK], 0);
+	up(&chip->open_mutex);
+	if (err < 0)
+		return err;
+	substream->runtime->hw.channels_max = chip->max_channels;
+	if (chip->max_channels > 2)
+		/* channels must be even */
+		snd_pcm_hw_constraint_step(substream->runtime, 0,
+					   SNDRV_PCM_HW_PARAM_CHANNELS, 2);
+	return 0;
+}
+
+static int snd_atiixp_playback_close(struct snd_pcm_substream *substream)
+{
+	struct atiixp *chip = snd_pcm_substream_chip(substream);
+	int err;
+	down(&chip->open_mutex);
+	err = snd_atiixp_pcm_close(substream, &chip->dmas[ATI_DMA_PLAYBACK]);
+	up(&chip->open_mutex);
+	return err;
+}
+
+static int snd_atiixp_capture_open(struct snd_pcm_substream *substream)
+{
+	struct atiixp *chip = snd_pcm_substream_chip(substream);
+	return snd_atiixp_pcm_open(substream, &chip->dmas[ATI_DMA_CAPTURE], 1);
+}
+
+static int snd_atiixp_capture_close(struct snd_pcm_substream *substream)
+{
+	struct atiixp *chip = snd_pcm_substream_chip(substream);
+	return snd_atiixp_pcm_close(substream, &chip->dmas[ATI_DMA_CAPTURE]);
+}
+
+static int snd_atiixp_spdif_open(struct snd_pcm_substream *substream)
+{
+	struct atiixp *chip = snd_pcm_substream_chip(substream);
+	int err;
+	down(&chip->open_mutex);
+	if (chip->spdif_over_aclink) /* share DMA_PLAYBACK */
+		err = snd_atiixp_pcm_open(substream, &chip->dmas[ATI_DMA_PLAYBACK], 2);
+	else
+		err = snd_atiixp_pcm_open(substream, &chip->dmas[ATI_DMA_SPDIF], -1);
+	up(&chip->open_mutex);
+	return err;
+}
+
+static int snd_atiixp_spdif_close(struct snd_pcm_substream *substream)
+{
+	struct atiixp *chip = snd_pcm_substream_chip(substream);
+	int err;
+	down(&chip->open_mutex);
+	if (chip->spdif_over_aclink)
+		err = snd_atiixp_pcm_close(substream, &chip->dmas[ATI_DMA_PLAYBACK]);
+	else
+		err = snd_atiixp_pcm_close(substream, &chip->dmas[ATI_DMA_SPDIF]);
+	up(&chip->open_mutex);
+	return err;
+}
+
+/* AC97 playback */
+static struct snd_pcm_ops snd_atiixp_playback_ops = {
+	.open =		snd_atiixp_playback_open,
+	.close =	snd_atiixp_playback_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_atiixp_pcm_hw_params,
+	.hw_free =	snd_atiixp_pcm_hw_free,
+	.prepare =	snd_atiixp_playback_prepare,
+	.trigger =	snd_atiixp_pcm_trigger,
+	.pointer =	snd_atiixp_pcm_pointer,
+};
+
+/* AC97 capture */
+static struct snd_pcm_ops snd_atiixp_capture_ops = {
+	.open =		snd_atiixp_capture_open,
+	.close =	snd_atiixp_capture_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_atiixp_pcm_hw_params,
+	.hw_free =	snd_atiixp_pcm_hw_free,
+	.prepare =	snd_atiixp_capture_prepare,
+	.trigger =	snd_atiixp_pcm_trigger,
+	.pointer =	snd_atiixp_pcm_pointer,
+};
+
+/* SPDIF playback */
+static struct snd_pcm_ops snd_atiixp_spdif_ops = {
+	.open =		snd_atiixp_spdif_open,
+	.close =	snd_atiixp_spdif_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_atiixp_pcm_hw_params,
+	.hw_free =	snd_atiixp_pcm_hw_free,
+	.prepare =	snd_atiixp_spdif_prepare,
+	.trigger =	snd_atiixp_pcm_trigger,
+	.pointer =	snd_atiixp_pcm_pointer,
+};
+
+static struct ac97_pcm atiixp_pcm_defs[] __devinitdata = {
+	/* front PCM */
+	{
+		.exclusive = 1,
+		.r = {	{
+				.slots = (1 << AC97_SLOT_PCM_LEFT) |
+					 (1 << AC97_SLOT_PCM_RIGHT) |
+					 (1 << AC97_SLOT_PCM_CENTER) |
+					 (1 << AC97_SLOT_PCM_SLEFT) |
+					 (1 << AC97_SLOT_PCM_SRIGHT) |
+					 (1 << AC97_SLOT_LFE)
+			}
+		}
+	},
+	/* PCM IN #1 */
+	{
+		.stream = 1,
+		.exclusive = 1,
+		.r = {	{
+				.slots = (1 << AC97_SLOT_PCM_LEFT) |
+					 (1 << AC97_SLOT_PCM_RIGHT)
+			}
+		}
+	},
+	/* S/PDIF OUT (optional) */
+	{
+		.exclusive = 1,
+		.spdif = 1,
+		.r = {	{
+				.slots = (1 << AC97_SLOT_SPDIF_LEFT2) |
+					 (1 << AC97_SLOT_SPDIF_RIGHT2)
+			}
+		}
+	},
+};
+
+static struct atiixp_dma_ops snd_atiixp_playback_dma_ops = {
+	.type = ATI_DMA_PLAYBACK,
+	.llp_offset = ATI_REG_OUT_DMA_LINKPTR,
+	.dt_cur = ATI_REG_OUT_DMA_DT_CUR,
+	.enable_dma = atiixp_out_enable_dma,
+	.enable_transfer = atiixp_out_enable_transfer,
+	.flush_dma = atiixp_out_flush_dma,
+};
+	
+static struct atiixp_dma_ops snd_atiixp_capture_dma_ops = {
+	.type = ATI_DMA_CAPTURE,
+	.llp_offset = ATI_REG_IN_DMA_LINKPTR,
+	.dt_cur = ATI_REG_IN_DMA_DT_CUR,
+	.enable_dma = atiixp_in_enable_dma,
+	.enable_transfer = atiixp_in_enable_transfer,
+	.flush_dma = atiixp_in_flush_dma,
+};
+	
+static struct atiixp_dma_ops snd_atiixp_spdif_dma_ops = {
+	.type = ATI_DMA_SPDIF,
+	.llp_offset = ATI_REG_SPDF_DMA_LINKPTR,
+	.dt_cur = ATI_REG_SPDF_DMA_DT_CUR,
+	.enable_dma = atiixp_spdif_enable_dma,
+	.enable_transfer = atiixp_spdif_enable_transfer,
+	.flush_dma = atiixp_spdif_flush_dma,
+};
+	
+
+static int __devinit snd_atiixp_pcm_new(struct atiixp *chip)
+{
+	struct snd_pcm *pcm;
+	struct snd_ac97_bus *pbus = chip->ac97_bus;
+	int err, i, num_pcms;
+
+	/* initialize constants */
+	chip->dmas[ATI_DMA_PLAYBACK].ops = &snd_atiixp_playback_dma_ops;
+	chip->dmas[ATI_DMA_CAPTURE].ops = &snd_atiixp_capture_dma_ops;
+	if (! chip->spdif_over_aclink)
+		chip->dmas[ATI_DMA_SPDIF].ops = &snd_atiixp_spdif_dma_ops;
+
+	/* assign AC97 pcm */
+	if (chip->spdif_over_aclink)
+		num_pcms = 3;
+	else
+		num_pcms = 2;
+	err = snd_ac97_pcm_assign(pbus, num_pcms, atiixp_pcm_defs);
+	if (err < 0)
+		return err;
+	for (i = 0; i < num_pcms; i++)
+		chip->pcms[i] = &pbus->pcms[i];
+
+	chip->max_channels = 2;
+	if (pbus->pcms[ATI_PCM_OUT].r[0].slots & (1 << AC97_SLOT_PCM_SLEFT)) {
+		if (pbus->pcms[ATI_PCM_OUT].r[0].slots & (1 << AC97_SLOT_LFE))
+			chip->max_channels = 6;
+		else
+			chip->max_channels = 4;
+	}
+
+	/* PCM #0: analog I/O */
+	err = snd_pcm_new(chip->card, "ATI IXP AC97",
+			  ATI_PCMDEV_ANALOG, 1, 1, &pcm);
+	if (err < 0)
+		return err;
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_atiixp_playback_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_atiixp_capture_ops);
+	pcm->private_data = chip;
+	strcpy(pcm->name, "ATI IXP AC97");
+	chip->pcmdevs[ATI_PCMDEV_ANALOG] = pcm;
+
+	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+					      snd_dma_pci_data(chip->pci),
+					      64*1024, 128*1024);
+
+	/* no SPDIF support on codec? */
+	if (chip->pcms[ATI_PCM_SPDIF] && ! chip->pcms[ATI_PCM_SPDIF]->rates)
+		return 0;
+		
+	/* FIXME: non-48k sample rate doesn't work on my test machine with AD1888 */
+	if (chip->pcms[ATI_PCM_SPDIF])
+		chip->pcms[ATI_PCM_SPDIF]->rates = SNDRV_PCM_RATE_48000;
+
+	/* PCM #1: spdif playback */
+	err = snd_pcm_new(chip->card, "ATI IXP IEC958",
+			  ATI_PCMDEV_DIGITAL, 1, 0, &pcm);
+	if (err < 0)
+		return err;
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_atiixp_spdif_ops);
+	pcm->private_data = chip;
+	if (chip->spdif_over_aclink)
+		strcpy(pcm->name, "ATI IXP IEC958 (AC97)");
+	else
+		strcpy(pcm->name, "ATI IXP IEC958 (Direct)");
+	chip->pcmdevs[ATI_PCMDEV_DIGITAL] = pcm;
+
+	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+					      snd_dma_pci_data(chip->pci),
+					      64*1024, 128*1024);
+
+	/* pre-select AC97 SPDIF slots 10/11 */
+	for (i = 0; i < NUM_ATI_CODECS; i++) {
+		if (chip->ac97[i])
+			snd_ac97_update_bits(chip->ac97[i],
+					     AC97_EXTENDED_STATUS,
+					     0x03 << 4, 0x03 << 4);
+	}
+
+	return 0;
+}
+
+
+
+/*
+ * interrupt handler
+ */
+static irqreturn_t snd_atiixp_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct atiixp *chip = dev_id;
+	unsigned int status;
+
+	status = atiixp_read(chip, ISR);
+
+	if (! status)
+		return IRQ_NONE;
+
+	/* process audio DMA */
+	if (status & ATI_REG_ISR_OUT_XRUN)
+		snd_atiixp_xrun_dma(chip,  &chip->dmas[ATI_DMA_PLAYBACK]);
+	else if (status & ATI_REG_ISR_OUT_STATUS)
+		snd_atiixp_update_dma(chip, &chip->dmas[ATI_DMA_PLAYBACK]);
+	if (status & ATI_REG_ISR_IN_XRUN)
+		snd_atiixp_xrun_dma(chip,  &chip->dmas[ATI_DMA_CAPTURE]);
+	else if (status & ATI_REG_ISR_IN_STATUS)
+		snd_atiixp_update_dma(chip, &chip->dmas[ATI_DMA_CAPTURE]);
+	if (! chip->spdif_over_aclink) {
+		if (status & ATI_REG_ISR_SPDF_XRUN)
+			snd_atiixp_xrun_dma(chip,  &chip->dmas[ATI_DMA_SPDIF]);
+		else if (status & ATI_REG_ISR_SPDF_STATUS)
+			snd_atiixp_update_dma(chip, &chip->dmas[ATI_DMA_SPDIF]);
+	}
+
+	/* for codec detection */
+	if (status & CODEC_CHECK_BITS) {
+		unsigned int detected;
+		detected = status & CODEC_CHECK_BITS;
+		spin_lock(&chip->reg_lock);
+		chip->codec_not_ready_bits |= detected;
+		atiixp_update(chip, IER, detected, 0); /* disable the detected irqs */
+		spin_unlock(&chip->reg_lock);
+	}
+
+	/* ack */
+	atiixp_write(chip, ISR, status);
+
+	return IRQ_HANDLED;
+}
+
+
+/*
+ * ac97 mixer section
+ */
+
+static struct ac97_quirk ac97_quirks[] __devinitdata = {
+	{
+		.subvendor = 0x103c,
+		.subdevice = 0x006b,
+		.name = "HP Pavilion ZV5030US",
+		.type = AC97_TUNE_MUTE_LED
+	},
+	{
+		.subvendor = 0x103c,
+		.subdevice = 0x308b,
+		.name = "HP nx6125",
+		.type = AC97_TUNE_MUTE_LED
+	},
+	{0} /* terminator */
+};
+
+static int __devinit snd_atiixp_mixer_new(struct atiixp *chip, int clock,
+					  const char *quirk_override)
+{
+	struct snd_ac97_bus *pbus;
+	struct snd_ac97_template ac97;
+	int i, err;
+	int codec_count;
+	static struct snd_ac97_bus_ops ops = {
+		.write = snd_atiixp_ac97_write,
+		.read = snd_atiixp_ac97_read,
+	};
+	static unsigned int codec_skip[NUM_ATI_CODECS] = {
+		ATI_REG_ISR_CODEC0_NOT_READY,
+		ATI_REG_ISR_CODEC1_NOT_READY,
+		ATI_REG_ISR_CODEC2_NOT_READY,
+	};
+
+	if (snd_atiixp_codec_detect(chip) < 0)
+		return -ENXIO;
+
+	if ((err = snd_ac97_bus(chip->card, 0, &ops, chip, &pbus)) < 0)
+		return err;
+	pbus->clock = clock;
+	chip->ac97_bus = pbus;
+
+	codec_count = 0;
+	for (i = 0; i < NUM_ATI_CODECS; i++) {
+		if (chip->codec_not_ready_bits & codec_skip[i])
+			continue;
+		memset(&ac97, 0, sizeof(ac97));
+		ac97.private_data = chip;
+		ac97.pci = chip->pci;
+		ac97.num = i;
+		ac97.scaps = AC97_SCAP_SKIP_MODEM;
+		if (! chip->spdif_over_aclink)
+			ac97.scaps |= AC97_SCAP_NO_SPDIF;
+		if ((err = snd_ac97_mixer(pbus, &ac97, &chip->ac97[i])) < 0) {
+			chip->ac97[i] = NULL; /* to be sure */
+			snd_printdd("atiixp: codec %d not available for audio\n", i);
+			continue;
+		}
+		codec_count++;
+	}
+
+	if (! codec_count) {
+		snd_printk(KERN_ERR "atiixp: no codec available\n");
+		return -ENODEV;
+	}
+
+	snd_ac97_tune_hardware(chip->ac97[0], ac97_quirks, quirk_override);
+
+	return 0;
+}
+
+
+#ifdef CONFIG_PM
+/*
+ * power management
+ */
+static int snd_atiixp_suspend(struct pci_dev *pci, pm_message_t state)
+{
+	struct snd_card *card = pci_get_drvdata(pci);
+	struct atiixp *chip = card->private_data;
+	int i;
+
+	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+	for (i = 0; i < NUM_ATI_PCMDEVS; i++)
+		if (chip->pcmdevs[i]) {
+			struct atiixp_dma *dma = &chip->dmas[i];
+			if (dma->substream && dma->running)
+				dma->saved_curptr = readl((char*)chip->remap_addr +
+							  dma->ops->dt_cur);
+			snd_pcm_suspend_all(chip->pcmdevs[i]);
+		}
+	for (i = 0; i < NUM_ATI_CODECS; i++)
+		snd_ac97_suspend(chip->ac97[i]);
+	snd_atiixp_aclink_down(chip);
+	snd_atiixp_chip_stop(chip);
+
+	pci_set_power_state(pci, PCI_D3hot);
+	pci_disable_device(pci);
+	pci_save_state(pci);
+	return 0;
+}
+
+static int snd_atiixp_resume(struct pci_dev *pci)
+{
+	struct snd_card *card = pci_get_drvdata(pci);
+	struct atiixp *chip = card->private_data;
+	int i;
+
+	pci_restore_state(pci);
+	pci_enable_device(pci);
+	pci_set_power_state(pci, PCI_D0);
+	pci_set_master(pci);
+
+	snd_atiixp_aclink_reset(chip);
+	snd_atiixp_chip_start(chip);
+
+	for (i = 0; i < NUM_ATI_CODECS; i++)
+		snd_ac97_resume(chip->ac97[i]);
+
+	for (i = 0; i < NUM_ATI_PCMDEVS; i++)
+		if (chip->pcmdevs[i]) {
+			struct atiixp_dma *dma = &chip->dmas[i];
+			if (dma->substream && dma->suspended) {
+				dma->ops->enable_dma(chip, 1);
+				dma->substream->ops->prepare(dma->substream);
+				writel((u32)dma->desc_buf.addr | ATI_REG_LINKPTR_EN,
+				       (char*)chip->remap_addr + dma->ops->llp_offset);
+				writel(dma->saved_curptr, (char*)chip->remap_addr +
+				       dma->ops->dt_cur);
+			}
+		}
+
+	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+	return 0;
+}
+#endif /* CONFIG_PM */
+
+
+#ifdef CONFIG_PROC_FS
+/*
+ * proc interface for register dump
+ */
+
+static void snd_atiixp_proc_read(struct snd_info_entry *entry,
+				 struct snd_info_buffer *buffer)
+{
+	struct atiixp *chip = entry->private_data;
+	int i;
+
+	for (i = 0; i < 256; i += 4)
+		snd_iprintf(buffer, "%02x: %08x\n", i, readl((char*)chip->remap_addr + i));
+}
+
+static void __devinit snd_atiixp_proc_init(struct atiixp *chip)
+{
+	struct snd_info_entry *entry;
+
+	if (! snd_card_proc_new(chip->card, "atiixp", &entry))
+		snd_info_set_text_ops(entry, chip, 1024, snd_atiixp_proc_read);
+}
+#else /* !CONFIG_PROC_FS */
+#define snd_atiixp_proc_init(chip)
+#endif
+
+
+/*
+ * destructor
+ */
+
+static int snd_atiixp_free(struct atiixp *chip)
+{
+	if (chip->irq < 0)
+		goto __hw_end;
+	snd_atiixp_chip_stop(chip);
+	synchronize_irq(chip->irq);
+      __hw_end:
+	if (chip->irq >= 0)
+		free_irq(chip->irq, chip);
+	if (chip->remap_addr)
+		iounmap(chip->remap_addr);
+	pci_release_regions(chip->pci);
+	pci_disable_device(chip->pci);
+	kfree(chip);
+	return 0;
+}
+
+static int snd_atiixp_dev_free(struct snd_device *device)
+{
+	struct atiixp *chip = device->device_data;
+	return snd_atiixp_free(chip);
+}
+
+/*
+ * constructor for chip instance
+ */
+static int __devinit snd_atiixp_create(struct snd_card *card,
+				      struct pci_dev *pci,
+				      struct atiixp **r_chip)
+{
+	static struct snd_device_ops ops = {
+		.dev_free =	snd_atiixp_dev_free,
+	};
+	struct atiixp *chip;
+	int err;
+
+	if ((err = pci_enable_device(pci)) < 0)
+		return err;
+
+	chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+	if (chip == NULL) {
+		pci_disable_device(pci);
+		return -ENOMEM;
+	}
+
+	spin_lock_init(&chip->reg_lock);
+	init_MUTEX(&chip->open_mutex);
+	chip->card = card;
+	chip->pci = pci;
+	chip->irq = -1;
+	if ((err = pci_request_regions(pci, "ATI IXP AC97")) < 0) {
+		pci_disable_device(pci);
+		kfree(chip);
+		return err;
+	}
+	chip->addr = pci_resource_start(pci, 0);
+	chip->remap_addr = ioremap_nocache(chip->addr, pci_resource_len(pci, 0));
+	if (chip->remap_addr == NULL) {
+		snd_printk(KERN_ERR "AC'97 space ioremap problem\n");
+		snd_atiixp_free(chip);
+		return -EIO;
+	}
+
+	if (request_irq(pci->irq, snd_atiixp_interrupt, SA_INTERRUPT|SA_SHIRQ,
+			card->shortname, chip)) {
+		snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
+		snd_atiixp_free(chip);
+		return -EBUSY;
+	}
+	chip->irq = pci->irq;
+	pci_set_master(pci);
+	synchronize_irq(chip->irq);
+
+	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+		snd_atiixp_free(chip);
+		return err;
+	}
+
+	snd_card_set_dev(card, &pci->dev);
+
+	*r_chip = chip;
+	return 0;
+}
+
+
+static int __devinit snd_atiixp_probe(struct pci_dev *pci,
+				     const struct pci_device_id *pci_id)
+{
+	struct snd_card *card;
+	struct atiixp *chip;
+	unsigned char revision;
+	int err;
+
+	card = snd_card_new(index, id, THIS_MODULE, 0);
+	if (card == NULL)
+		return -ENOMEM;
+
+	pci_read_config_byte(pci, PCI_REVISION_ID, &revision);
+
+	strcpy(card->driver, spdif_aclink ? "ATIIXP" : "ATIIXP-SPDMA");
+	strcpy(card->shortname, "ATI IXP");
+	if ((err = snd_atiixp_create(card, pci, &chip)) < 0)
+		goto __error;
+	card->private_data = chip;
+
+	if ((err = snd_atiixp_aclink_reset(chip)) < 0)
+		goto __error;
+
+	chip->spdif_over_aclink = spdif_aclink;
+
+	if ((err = snd_atiixp_mixer_new(chip, ac97_clock, ac97_quirk)) < 0)
+		goto __error;
+
+	if ((err = snd_atiixp_pcm_new(chip)) < 0)
+		goto __error;
+	
+	snd_atiixp_proc_init(chip);
+
+	snd_atiixp_chip_start(chip);
+
+	sprintf(card->longname,
+		 "%s rev %x with %s at %#lx, irq %i", card->shortname, revision,
+		 chip->ac97[0] ? snd_ac97_get_short_name(chip->ac97[0]) : "?",
+		 chip->addr, chip->irq);
+
+	if ((err = snd_card_register(card)) < 0)
+		goto __error;
+
+	pci_set_drvdata(pci, card);
+	return 0;
+
+ __error:
+	snd_card_free(card);
+	return err;
+}
+
+static void __devexit snd_atiixp_remove(struct pci_dev *pci)
+{
+	snd_card_free(pci_get_drvdata(pci));
+	pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver driver = {
+	.name = "ATI IXP AC97 controller",
+	.id_table = snd_atiixp_ids,
+	.probe = snd_atiixp_probe,
+	.remove = __devexit_p(snd_atiixp_remove),
+#ifdef CONFIG_PM
+	.suspend = snd_atiixp_suspend,
+	.resume = snd_atiixp_resume,
+#endif
+};
+
+
+static int __init alsa_card_atiixp_init(void)
+{
+	return pci_register_driver(&driver);
+}
+
+static void __exit alsa_card_atiixp_exit(void)
+{
+	pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_atiixp_init)
+module_exit(alsa_card_atiixp_exit)
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/CVS/Entries
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/CVS/Entries	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/CVS/Entries	(revision 91)
@@ -0,0 +1,40 @@
+/.cvsignore/1.1/Mon Oct  6 14:01:02 2003/-ko/
+/Makefile/1.1/Mon Oct  6 14:01:02 2003/-ko/
+/au8810.c/1.1/Mon Oct  6 14:01:02 2003/-ko/
+/au8810.h/1.1/Mon Oct  6 14:01:02 2003/-ko/
+/au8810_core.c/1.1/Mon Oct  6 14:01:02 2003/-ko/
+/au8810_eq.c/1.1/Mon Oct  6 14:01:02 2003/-ko/
+/au8810_game.c/1.1/Mon Oct  6 14:01:02 2003/-ko/
+/au8810_mixer.c/1.1/Mon Oct  6 14:01:02 2003/-ko/
+/au8810_mpu401.c/1.1/Mon Oct  6 14:01:02 2003/-ko/
+/au8810_pcm.c/1.1/Mon Oct  6 14:01:02 2003/-ko/
+/au8820.c/1.1/Mon Oct  6 14:01:02 2003/-ko/
+/au8820.h/1.1/Mon Oct  6 14:01:02 2003/-ko/
+/au8820_core.c/1.1/Mon Oct  6 14:01:02 2003/-ko/
+/au8820_game.c/1.1/Mon Oct  6 14:01:02 2003/-ko/
+/au8820_mixer.c/1.1/Mon Oct  6 14:01:02 2003/-ko/
+/au8820_mpu401.c/1.1/Mon Oct  6 14:01:02 2003/-ko/
+/au8820_pcm.c/1.1/Mon Oct  6 14:01:02 2003/-ko/
+/au8820_synth.c/1.1/Mon Oct  6 14:01:02 2003/-ko/
+/au8830.c/1.1/Mon Oct  6 14:01:02 2003/-ko/
+/au8830.h/1.1/Mon Oct  6 14:01:02 2003/-ko/
+/au8830_core.c/1.1/Mon Oct  6 14:01:02 2003/-ko/
+/au8830_eq.c/1.1/Mon Oct  6 14:01:02 2003/-ko/
+/au8830_game.c/1.1/Mon Oct  6 14:01:02 2003/-ko/
+/au8830_mixer.c/1.1/Mon Oct  6 14:01:04 2003/-ko/
+/au8830_mpu401.c/1.1/Mon Oct  6 14:01:04 2003/-ko/
+/au8830_pcm.c/1.1/Mon Oct  6 14:01:04 2003/-ko/
+/au8830_synth.c/1.1/Mon Oct  6 14:01:04 2003/-ko/
+/au88x0.c/1.1/Mon Oct  6 14:01:04 2003/-ko/
+/au88x0.h/1.1/Mon Oct  6 14:01:04 2003/-ko/
+/au88x0_core.c/1.2/Sat Oct 11 08:15:24 2003/-ko/
+/au88x0_eq.c/1.1/Mon Oct  6 14:01:04 2003/-ko/
+/au88x0_eq.h/1.1/Mon Oct  6 14:01:04 2003/-ko/
+/au88x0_eqdata.c/1.1/Mon Oct  6 14:01:04 2003/-ko/
+/au88x0_game.c/1.1/Mon Oct  6 14:01:04 2003/-ko/
+/au88x0_mixer.c/1.1/Mon Oct  6 14:01:04 2003/-ko/
+/au88x0_mpu401.c/1.1/Mon Oct  6 14:01:04 2003/-ko/
+/au88x0_pcm.c/1.1/Mon Oct  6 14:01:04 2003/-ko/
+/au88x0_synth.c/1.1/Mon Oct  6 14:01:04 2003/-ko/
+/migrate/1.1/Mon Oct  6 14:01:04 2003/-ko/
+D/test////
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/CVS/Repository
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/CVS/Repository	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/CVS/Repository	(revision 91)
@@ -0,0 +1,1 @@
+alsa-driver/pci/au88x0
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/CVS/Root
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/CVS/Root	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/CVS/Root	(revision 91)
@@ -0,0 +1,1 @@
+:pserver:anonymous@cvs.alsa-project.org:/cvsroot/alsa
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/Makefile
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/Makefile	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/Makefile	(revision 91)
@@ -0,0 +1,14 @@
+TOPDIR = ../..
+
+include $(TOPDIR)/toplevel.config
+include $(TOPDIR)/Makefile.conf
+
+snd-au8810-objs := au8810.o au8810_core.o au8810_pcm.o au8810_mixer.o au8810_mpu401.o au8810_game.o au8810_eq.o
+snd-au8820-objs := au8820.o au8820_core.o au8820_pcm.o au8820_mixer.o au8820_mpu401.o au8820_game.o au8820_synth.o
+snd-au8830-objs := au8830.o au8830_core.o au8830_pcm.o au8830_mixer.o au8830_mpu401.o au8830_game.o au8830_eq.o au8830_synth.o
+
+obj-$(CONFIG_SND_AU8810) += snd-au8810.o
+obj-$(CONFIG_SND_AU8820) += snd-au8820.o
+obj-$(CONFIG_SND_AU8830) += snd-au8830.o
+
+include $(TOPDIR)/Rules.make
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8810.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8810.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8810.c	(revision 91)
@@ -0,0 +1,8 @@
+#include "au8810.h"
+#include "au88x0.h"
+//static struct pci_device_id snd_vortex_ids[] = {
+//    {PCI_VENDOR_ID_AUREAL, PCI_DEVICE_ID_AUREAL_ADVANTAGE,
+//     PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1,},
+//    {0,}
+//};
+#include "au88x0.c"
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8810.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8810.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8810.h	(revision 91)
@@ -0,0 +1,222 @@
+/*
+    Aureal Advantage Soundcard driver.
+ */
+
+#define CHIP_AU8810
+
+#define CARD_NAME "Aureal Advantage 3D Sound Processor"
+#define CARD_NAME_SHORT "au8810"
+
+#ifndef PCI_VENDOR_ID_AUREAL
+#define PCI_VENDOR_ID_AUREAL 0x12eb
+#endif
+#ifndef PCI_VENDOR_ID_AUREAL_ADVANTAGE
+#define PCI_DEVICE_ID_AUREAL_ADVANTAGE 0x0003
+#endif
+
+#define hwread(x,y) readl((x)+((y)>>2))
+#define hwwrite(x,y,z) writel((z),(x)+((y)>>2))
+
+#define NR_ADB 0x10
+#define NR_SRC 0x10
+#define NR_A3D 0x10
+#define NR_MIXIN 0x20
+#define NR_MIXOUT 0x10
+#define NR_WT 0x20
+
+/* ADBDMA */
+#define VORTEX_ADBDMA_STAT 0x27e00 /* read only, subbuffer, DMA pos */
+#define		POS_MASK 0x00000fff
+#define     POS_SHIFT 0x0
+#define 	ADB_SUBBUF_MASK 0x00003000  /* ADB only. */
+#define     ADB_SUBBUF_SHIFT 0xc        /* ADB only. */
+#define VORTEX_ADBDMA_CTRL 0x27180 	/* write only; format, flags, DMA pos */
+#define		OFFSET_MASK 0x00000fff
+#define     OFFSET_SHIFT 0x0
+#define		IE_MASK 0x00001000 /* interrupt enable. */
+#define     IE_SHIFT 0xc
+#define     U_MASK 0x00002000  /* Unknown. If you know, tell me. */
+#define     U_SHIFT 0xd
+#define		FMT_MASK 0x0003c000
+#define		FMT_SHIFT 0xe
+// The ADB masks and shift also are valid for the wtdma, except if specified otherwise.
+#define VORTEX_ADBDMA_BUFCFG0 0x27100
+#define VORTEX_ADBDMA_BUFCFG1 0x27104
+#define VORTEX_ADBDMA_BUFBASE 0x27000
+#define VORTEX_ADBDMA_START 0x27c00 /* Which subbuffer starts */
+
+/* WTDMA */
+#define VORTEX_WTDMA_CTRL 0x27fd8  /* format, DMA pos */
+#define VORTEX_WTDMA_STAT 0x27fe8  /* DMA subbuf, DMA pos */
+#define     WT_SUBBUF_MASK 0x3
+#define     WT_SUBBUF_SHIFT 0xc
+#define VORTEX_WTDMA_BUFBASE 0x27fc0
+#define VORTEX_WTDMA_BUFCFG0 0x27fd0
+#define VORTEX_WTDMA_BUFCFG1 0x27fd4
+#define VORTEX_WTDMA_START 0x27fe4 /* which subbuffer is first */
+
+/* ADB */
+#define VORTEX_ADB_SR 0x28400 /* Samplerates enable/disable */
+#define VORTEX_ADB_RTBASE 0x28000
+#define VORTEX_ADB_RTBASE_SIZE (VORTEX_ADB_CHNBASE - VORTEX_ADB_RTBASE)
+#define VORTEX_ADB_CHNBASE 0x282b4
+#define VORTEX_ADB_CHNBASE_SIZE (ADB_MASK - VORTEX_ADB_RTBASE_SIZE)
+#define 	ROUTE_MASK	0xffff
+#define		SOURCE_MASK	0xff00
+#define     ADB_MASK   0xff
+#define		ADB_SHIFT 0x8
+/* ADB address */
+#define		OFFSET_ADBDMA	0x00
+#define		OFFSET_SRCIN	0x40
+#define		OFFSET_SRCOUT	0x20
+#define		OFFSET_MIXIN	0x50
+#define		OFFSET_MIXOUT	0x30
+#define		OFFSET_CODECIN	0x70
+#define		OFFSET_CODECOUT	0x88
+#define		OFFSET_SPORTIN	0x78 /* ch 0x13 */
+#define		OFFSET_SPORTOUT	0x90
+#define		OFFSET_SPDIFOUT	0x92 /* ch 0x14 check this! */
+#define		OFFSET_EQIN		0xa0
+#define		OFFSET_EQOUT	0x7e /* 2 routes on ch 0x11 */
+
+/* ADB route translate helper */
+#define ADB_DMA(x) (x)
+#define ADB_SRCOUT(x) (x + OFFSET_SRCOUT)
+#define ADB_SRCIN(x) (x + OFFSET_SRCIN)
+#define ADB_MIXOUT(x) (x + OFFSET_MIXOUT)
+#define ADB_MIXIN(x) (x + OFFSET_MIXIN)
+#define ADB_CODECIN(x) (x + OFFSET_CODECIN)
+#define ADB_CODECOUT(x) (x + OFFSET_CODECOUT)
+#define ADB_SPORTIN(x) (x + OFFSET_SPORTIN)
+#define ADB_SPORTOUT(x) (x + OFFSET_SPORTOUT)
+#define ADB_SPDIFOUT(x)	(x + OFFSET_SPDIFOUT)
+#define ADB_EQIN(x) (x + OFFSET_EQIN)
+#define ADB_EQOUT(x) (x + OFFSET_EQOUT)
+#define ADB_A3DOUT(x) (x + 0x50) /* A3D blocks */
+#define ADB_A3DIN(x) (x + 0x70)
+
+#define MIX_OUTL    0xe
+#define MIX_OUTR    0xf
+#define MIX_INL     0x1e
+#define MIX_INR     0x1f
+#define MIX_DEFIGAIN 0x00 /* 0x8 => 6dB */
+#define MIX_DEFOGAIN 0x00
+
+/* MIXER */
+#define VORTEX_MIXER_SR 0x21f00
+#define VORTEX_MIXER_CHNBASE 0x21e40
+#define VORTEX_MIXER_RTBASE 0x21e00
+#define 	MIXER_RTBASE_SIZE 0x38
+#define VORTEX_MIX_U0 0x21c00 /* AU8820: 0x9c00 */
+
+/* MIX */
+#define VORTEX_MIX_INVOL_A 0x21000 /* in? */
+#define VORTEX_MIX_INVOL_B 0x20000 /* out? */
+#define VORTEX_MIX_VOL_A 0x21800
+#define VORTEX_MIX_VOL_B 0x20800
+#define VORTEX_MIX_ENIN 0x21a00 /* Input enable bits. 4 bits wide. */
+#define 	VOL_MIN 0x80 /* Input volume when muted. */
+#define		VOL_MAX 0x7f /* FIXME: Not confirmed! Just guessed. */
+
+/* SRC */
+#define VORTEX_SRCBLOCK_SR	0x26cc0
+#define VORTEX_SRC_CHNBASE	0x26c40
+#define VORTEX_SRC_RTBASE	0x26c00
+#define VORTEX_SRC_SOURCE	0x26cc4
+#define VORTEX_SRC_CONVRATIO 0x26e40
+#define VORTEX_SRC_DRIFT0	0x26e80
+#define VORTEX_SRC_DRIFT1	0x26ec0
+#define VORTEX_SRC_DRIFT2	0x26f40
+#define VORTEX_SRC_U0		0x26e00
+#define VORTEX_SRC_U1		0x26f00
+#define VORTEX_SRC_U3		0x26f80
+#define VORTEX_SRC_DATA		0x26800 /* 0xc800 */
+#define VORTEX_SRC_DATA0	0x26000
+
+/* FIFO */
+#define VORTEX_FIFO_ADBCTRL 0x16100 /* Control bits. */
+#define VORTEX_FIFO_WTCTRL 0x16000
+#define		FIFO_RDONLY	0x00000001
+#define		FIFO_CTRL	0x00000002 /* Allow ctrl. ? */
+#define		FIFO_VALID	0x00000010
+#define 	FIFO_EMPTY	0x00000020
+#define		FIFO_U0		0x00001000 /* Unknown. */
+#define		FIFO_U1		0x00010000
+#define		FIFO_SIZE_BITS 5
+#define		FIFO_SIZE	(1<<FIFO_SIZE_BITS) // 0x20
+#define 	FIFO_MASK	(FIFO_SIZE-1) //0x1f	/* at shift left 0xc */
+//#define 	FIFO_MASK	0x1f	/* at shift left 0xb */
+//#define		FIFO_SIZE	0x20
+#define 	FIFO_BITS	0x03880000
+#define VORTEX_FIFO_ADBDATA 0x14000
+#define VORTEX_FIFO_WTDATA 0x10000
+
+/* CODEC */
+#define VORTEX_CODEC_CTRL 0x29184
+#define VORTEX_CODEC_EN 0x29190
+#define		EN_CODEC0	0x00000300
+#define		EN_CODEC1	0x00003000
+#define		EN_CODEC	(EN_CODEC0 | EN_CODEC1)
+#define		EN_SPORT	0x00030000
+#define		EN_SPDIF	0x000c0000
+#define VORTEX_CODEC_CHN 0x29080
+#define VORTEX_CODEC_WRITE 0x00800000
+#define VORTEX_CODEC_ADDSHIFT 16
+#define VORTEX_CODEC_ADDMASK 0x7f0000 /* 0x000f0000*/
+#define VORTEX_CODEC_DATSHIFT 0
+#define VORTEX_CODEC_DATMASK 0xffff
+#define VORTEX_CODEC_IO 0x29188
+
+/* SPDIF */
+#define VORTEX_SPDIF_FLAGS		0x2205c
+#define VORTEX_SPDIF_CFG0		0x291D0
+#define VORTEX_SPDIF_CFG1		0x291D4
+#define VORTEX_SPDIF_SMPRATE	0x29194
+
+/* Sample timer */
+#define VORTEX_SMP_TIME  0x29198
+
+/* IRQ */
+#define VORTEX_IRQ_SOURCE 0x2a000 /* Interrupt source flags. */
+#define 	IRQ_FATAL    0x0001
+#define 	IRQ_PARITY   0x0002
+#define 	IRQ_PCMOUT   0x0020 /* ?? */
+#define 	IRQ_TIMER    0x1000
+#define 	IRQ_MIDI     0x2000
+#define		IRQ_MODEM	0x4000
+
+#define VORTEX_IRQ_U0	0x2a008 /* ?? */
+
+#define VORTEX_CTRL		0x2a00c
+#define 	CTRL_MIDI_EN	0x00000001
+#define 	CTRL_MIDI_PORT	0x00000060
+#define 	CTRL_GAME_EN	0x00000008
+#define 	CTRL_GAME_PORT	0x00000e00
+//#define 	CTRL_IRQ_ENABLE	0x01004000
+#define 	CTRL_IRQ_ENABLE	0x00004000
+
+#define VORTEX_IRQ_CTRL 0x2a004 /* Interrupt source mask. */
+#define 	IRQ_FATAL	0x0001
+#define 	IRQ_PARITY	0x0002
+#define 	IRQ_PCMOUT	0x0020 /* PCM OUT page crossing */
+#define 	IRQ_TIMER	0x1000
+#define 	IRQ_MIDI	0x2000
+#define		IRQ_MODEM	0x4000
+
+/* write: Timer period config / read: TIMER IRQ ack. */
+#define VORTEX_IRQ_STAT 0x2919c
+
+/* DMA */
+#define VORTEX_ENGINE_CTRL 0x27ae8
+#define 	ENGINE_INIT 0x1380000
+
+/* MIDI */ /* GAME. */
+#define VORTEX_MIDI_DATA 0x28800
+#define VORTEX_MIDI_CMD 0x28804  /* Write command / Read status */
+
+#define VORTEX_CTRL2 0x2880c
+#define		CTRL2_GAME_ADCMODE 0x40
+#define VORTEX_GAME_LEGACY 0x28808
+#define VORTEX_GAME_AXIS 0x28810
+#define		AXIS_SIZE 4
+#define		AXIS_RANGE 0x1fff
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8810_core.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8810_core.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8810_core.c	(revision 91)
@@ -0,0 +1,2 @@
+#include "au8810.h"
+#include "au88x0_core.c"
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8810_eq.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8810_eq.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8810_eq.c	(revision 91)
@@ -0,0 +1,2 @@
+#include "au8810.h"
+#include "au88x0_eq.c"
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8810_game.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8810_game.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8810_game.c	(revision 91)
@@ -0,0 +1,2 @@
+#include "au8810.h"
+#include "au88x0_game.c"
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8810_mixer.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8810_mixer.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8810_mixer.c	(revision 91)
@@ -0,0 +1,2 @@
+#include "au8810.h"
+#include "au88x0_mixer.c"
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8810_mpu401.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8810_mpu401.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8810_mpu401.c	(revision 91)
@@ -0,0 +1,2 @@
+#include "au8810.h"
+#include "au88x0_mpu401.c"
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8810_pcm.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8810_pcm.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8810_pcm.c	(revision 91)
@@ -0,0 +1,2 @@
+#include "au8810.h"
+#include "au88x0_pcm.c"
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8820.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8820.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8820.c	(revision 91)
@@ -0,0 +1,8 @@
+#include "au8820.h"
+#include "au88x0.h"
+//static struct pci_device_id snd_vortex_ids[] = {
+//    {PCI_VENDOR_ID_AUREAL, PCI_DEVICE_ID_AUREAL_VORTEX,
+//     PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0,},
+//    {0,}
+//};
+#include "au88x0.c"
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8820.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8820.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8820.h	(revision 91)
@@ -0,0 +1,227 @@
+/*
+    Aureal Vortex Soundcard driver.
+
+    IO addr collected from asp4core.vxd:
+    function    address
+    0005D5A0    13004
+    00080674    14004
+    00080AFF    12818
+
+ */
+
+#define CHIP_AU8820
+
+#define CARD_NAME "Aureal Vortex 3D Sound Processor"
+#define CARD_NAME_SHORT "au8820"
+
+#ifndef PCI_VENDOR_ID_AUREAL
+#define PCI_VENDOR_ID_AUREAL 0x12eb
+#endif
+
+#ifndef PCI_VENDOR_ID_AUREAL_VORTEX
+#define PCI_DEVICE_ID_AUREAL_VORTEX 0x0001
+#endif
+
+/* Number of ADB and WT channels */
+#define NR_ADB 0x10
+#define NR_WT 0x20
+#define NR_SRC 0x10
+#define NR_A3D 0x8
+#define NR_MIXIN 0x10
+#define NR_MIXOUT 0x10
+#define NR_WT 0x20
+
+/* ADBDMA */
+#define VORTEX_ADBDMA_STAT 0x105c0 /* read only, subbuffer, DMA pos */
+#define		POS_MASK 0x00000fff
+#define     POS_SHIFT 0x0
+#define 	ADB_SUBBUF_MASK 0x00003000  /* ADB only. */
+#define     ADB_SUBBUF_SHIFT 0xc        /* ADB only. */
+#define VORTEX_ADBDMA_CTRL 0x10580  /* write only, format, flags, DMA pos */
+#define		OFFSET_MASK 0x00000fff
+#define     OFFSET_SHIFT 0x0
+#define		IE_MASK 0x00001000 /* interrupt enable. */
+#define     IE_SHIFT 0xc
+#define     U_MASK 0x00002000  /* Unknown. If you know tell me. */
+#define     U_SHIFT 0xd
+#define		FMT_MASK 0x0003c000
+#define		FMT_SHIFT 0xe
+// The masks and shift also work for the wtdma, if not specified otherwise.
+#define VORTEX_ADBDMA_BUFCFG0 0x10400
+#define VORTEX_ADBDMA_BUFCFG1 0x10404
+#define VORTEX_ADBDMA_BUFBASE 0x10200
+#define VORTEX_ADBDMA_START 0x106c0 /* Which subbuffer starts */
+
+/* ADB */
+#define VORTEX_ADB_SR 0x10a00 /* Samplerates enable/disable */
+#define VORTEX_ADB_RTBASE 0x10800
+#define VORTEX_ADB_RTBASE_SIZE (VORTEX_ADB_CHNBASE - VORTEX_ADB_RTBASE)
+#define VORTEX_ADB_CHNBASE 0x1099c
+#define VORTEX_ADB_CHNBASE_SIZE (ADB_MASK - VORTEX_ADB_RTBASE_SIZE)
+#define 	ROUTE_MASK	0x3fff
+#define     ADB_MASK   0x7f
+#define		ADB_SHIFT 0x7
+//#define     ADB_MIX_MASK 0xf
+/* ADB address */
+#define		OFFSET_ADBDMA	0x00
+#define		OFFSET_SRCOUT	0x10  /* on channel 0x11 */
+#define		OFFSET_SRCIN	0x10  /* on channel < 0x11 */
+#define		OFFSET_MIXOUT	0x20
+#define		OFFSET_MIXIN	0x30
+#define		OFFSET_CODECIN	0x48 /* ADB source */
+#define		OFFSET_CODECOUT	0x58 /* ADB sink/target */
+#define		OFFSET_SPORTOUT	0x60
+#define		OFFSET_SPORTIN	0x78
+#define		OFFSET_SPDIFOUT	0x62
+#define		OFFSET_A3DOUT	0x50
+#define		OFFSET_A3DIN	0x70
+#define		OFFSET_WTOUT	0x00 /* FIXME */
+
+/* ADB route translate helper */
+#define ADB_DMA(x) (x + OFFSET_ADBDMA)
+#define ADB_SRCOUT(x) (x + OFFSET_SRCOUT)
+#define ADB_SRCIN(x) (x + OFFSET_SRCIN)
+#define ADB_MIXOUT(x) (x + OFFSET_MIXOUT)
+#define ADB_MIXIN(x) (x + OFFSET_MIXIN)
+#define ADB_CODECIN(x) (x + OFFSET_CODECIN)
+#define ADB_CODECOUT(x) (x + OFFSET_CODECOUT)
+#define ADB_SPORTOUT(x) (x + OFFSET_SPORTOUT)
+#define ADB_SPORTIN(x) (x + OFFSET_SPORTIN) /* 0x72 ? */
+#define ADB_SPDIFOUT(x) (x + OFFSET_SPDIFOUT)
+#define ADB_A3DOUT(x) (x + OFFSET_A3DOUT) /* 8 A3D blocks */
+#define ADB_A3DIN(x) (x + OFFSET_A3DIN)
+#define ADB_WTOUT(x) (x + OFFSET_WTOUT)
+
+/* WTDMA */
+#define VORTEX_WTDMA_CTRL 0x10500 /* format, DMA pos */
+#define VORTEX_WTDMA_STAT 0x10500 /* DMA subbuf, DMA pos */
+#define     WT_SUBBUF_MASK (0x3 << WT_SUBBUF_SHIFT)
+#define     WT_SUBBUF_SHIFT 0x15
+#define VORTEX_WTDMA_BUFBASE 0x10000
+#define VORTEX_WTDMA_BUFCFG0 0x10300
+#define VORTEX_WTDMA_BUFCFG1 0x10304
+#define VORTEX_WTDMA_START 0x10640 /* which subbuffer is first */
+
+#define VORTEX_WT_BASE 0x9000
+
+/* MIXER */
+#define VORTEX_MIXER_SR 0x9f00
+#define VORTEX_MIXER_CHNBASE 0x9e40
+#define VORTEX_MIXER_RTBASE 0x9e00
+#define 	MIXER_RTBASE_SIZE 0x26
+#define VORTEX_MIX_U0 0x9c00
+
+/* MIX */
+#define VORTEX_MIX_INVOL_A 0x9000 /* in? */
+#define VORTEX_MIX_INVOL_B 0x8000 /* out? */
+#define VORTEX_MIX_VOL_A 0x9800
+#define VORTEX_MIX_VOL_B 0x8800
+#define VORTEX_MIX_ENIN 0x9a00 /* Input enable bits. 4 bits wide. */
+#define 	VOL_MIN 0x80 /* Input volume when muted. */
+#define		VOL_MAX 0x7f /* FIXME: Not confirmed! Just guessed. */
+
+//#define MIX_OUTL    0xe
+//#define MIX_OUTR    0xf
+//#define MIX_INL     0xe
+//#define MIX_INR     0xf
+#define MIX_DEFIGAIN 0x08 /* 0x8 => 6dB */
+#define MIX_DEFOGAIN 0x08
+
+/* SRC */
+#define VORTEX_SRCBLOCK_SR	0xccc0
+#define VORTEX_SRC_CHNBASE	0xcc40
+#define VORTEX_SRC_RTBASE	0xcc00
+#define VORTEX_SRC_SOURCE	0xccc4
+#define VORTEX_SRC_U0		0xce00
+#define VORTEX_SRC_DRIFT0	0xce80
+#define VORTEX_SRC_DRIFT1	0xcec0
+#define VORTEX_SRC_U1		0xcf00
+#define VORTEX_SRC_DRIFT2	0xcf40
+#define VORTEX_SRC_U3		0xcf80
+#define VORTEX_SRC_DATA		0xc800
+#define VORTEX_SRC_DATA0	0xc000
+#define VORTEX_SRC_CONVRATIO 0xce40
+//#define     SRC_RATIO(x) ((((x<<15)/48000) + 1)/2) /* Playback */
+//#define     SRC_RATIO2(x) ((((48000<<15)/x) + 1)/2) /* Recording */
+
+
+/* FIFO */
+#define VORTEX_FIFO_ADBCTRL 0xf800 /* Control bits. */
+#define VORTEX_FIFO_WTCTRL 0xf840
+#define		FIFO_RDONLY	0x00000001
+#define		FIFO_CTRL	0x00000002 /* Allow ctrl. ? */
+#define		FIFO_VALID	0x00000010
+#define 	FIFO_EMPTY	0x00000020
+#define		FIFO_U0		0x00001000 /* Unknown. */
+#define		FIFO_U1		0x00010000
+#define		FIFO_SIZE_BITS 5
+#define		FIFO_SIZE	(1<<FIFO_SIZE_BITS) // 0x20
+#define 	FIFO_MASK	(FIFO_SIZE-1) //0x1f	/* at shift left 0xc */
+#define VORTEX_FIFO_ADBDATA 0xe000
+#define VORTEX_FIFO_WTDATA 0xe800
+
+/* CODEC */
+#define VORTEX_CODEC_CTRL 0x11984
+#define VORTEX_CODEC_EN 0x11990
+#define		EN_CODEC	0x00000300
+#define		EN_SPORT	0x00030000
+#define		EN_SPDIF	0x000c0000
+#define VORTEX_CODEC_CHN 0x11880
+#define VORTEX_CODEC_WRITE 0x00800000
+#define VORTEX_CODEC_ADDSHIFT 16
+#define VORTEX_CODEC_ADDMASK 0x7f0000 /* 0x000f0000*/
+#define VORTEX_CODEC_DATSHIFT 0
+#define VORTEX_CODEC_DATMASK 0xffff
+#define VORTEX_CODEC_IO 0x11988
+
+
+#define VORTEX_SPDIF_FLAGS		0x1005c /* FIXME */
+#define VORTEX_SPDIF_CFG0		0x119D0
+#define VORTEX_SPDIF_CFG1		0x119D4
+#define VORTEX_SPDIF_SMPRATE	0x11994
+
+
+/* Sample timer */
+#define VORTEX_SMP_TIME 0x11998
+
+/* IRQ */
+#define VORTEX_IRQ_SOURCE 0x12800 /* Interrupt source flags. */
+#define 	IRQ_FATAL    0x0001
+#define 	IRQ_PARITY   0x0002
+#define 	IRQ_PCMOUT   0x0020 /* ?? */
+#define 	IRQ_TIMER    0x1000
+#define 	IRQ_MIDI     0x2000
+
+#define VORTEX_IRQ_U0	0x12808 /* ?? */
+
+#define VORTEX_CTRL 0x1280c
+#define 	CTRL_MIDI_EN 0x00000001
+#define 	CTRL_MIDI_PORT 0x00000060
+#define 	CTRL_GAME_EN 0x00000008
+#define 	CTRL_GAME_PORT 0x00000e00
+#define 	CTRL_IRQ_ENABLE 0x4000
+
+#define VORTEX_IRQ_CTRL 0x12804 /* Interrupt source mask. */
+#define 	IRQ_FATAL    0x0001
+#define 	IRQ_PARITY   0x0002
+#define 	IRQ_PCMOUT   0x0020 /* PCM OUT page crossing */
+#define 	IRQ_TIMER    0x1000
+#define 	IRQ_MIDI     0x2000
+
+/* write: Timer period config / read: TIMER IRQ ack. */
+#define VORTEX_IRQ_STAT 0x1199c
+
+/* DMA */
+#define VORTEX_DMA_BUFFER 0x10200
+#define VORTEX_ENGINE_CTRL 0x1060c
+#define 	ENGINE_INIT 0x0L
+
+/* MIDI */ /* GAME. */
+#define VORTEX_MIDI_DATA 0x11000
+#define VORTEX_MIDI_CMD 0x11004  /* Write command / Read status */
+#define VORTEX_GAME_LEGACY 0x11008
+#define VORTEX_CTRL2 0x1100c
+#define 	CTRL2_GAME_ADCMODE 0x40
+#define VORTEX_GAME_AXIS 0x11010
+#define 	AXIS_SIZE 4
+#define		AXIS_RANGE 0x1fff
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8820_core.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8820_core.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8820_core.c	(revision 91)
@@ -0,0 +1,2 @@
+#include "au8820.h"
+#include "au88x0_core.c"
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8820_game.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8820_game.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8820_game.c	(revision 91)
@@ -0,0 +1,2 @@
+#include "au8820.h"
+#include "au88x0_game.c"
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8820_mixer.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8820_mixer.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8820_mixer.c	(revision 91)
@@ -0,0 +1,2 @@
+#include "au8820.h"
+#include "au88x0_mixer.c"
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8820_mpu401.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8820_mpu401.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8820_mpu401.c	(revision 91)
@@ -0,0 +1,2 @@
+#include "au8820.h"
+#include "au88x0_mpu401.c"
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8820_pcm.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8820_pcm.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8820_pcm.c	(revision 91)
@@ -0,0 +1,2 @@
+#include "au8820.h"
+#include "au88x0_pcm.c"
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8820_synth.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8820_synth.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8820_synth.c	(revision 91)
@@ -0,0 +1,2 @@
+#include "au8820.h"
+#include "au88x0_synth.c"
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8830.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8830.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8830.c	(revision 91)
@@ -0,0 +1,8 @@
+#include "au8830.h"
+#include "au88x0.h"
+//static struct pci_device_id snd_vortex_ids[] = {
+//    {PCI_VENDOR_ID_AUREAL, PCI_DEVICE_ID_AUREAL_VORTEX2,
+//     PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0,},
+//    {0,}
+//};
+#include "au88x0.c"
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8830.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8830.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8830.h	(revision 91)
@@ -0,0 +1,236 @@
+/*
+    Aureal Vortex Soundcard driver.
+
+    IO addr collected from asp4core.vxd:
+    function    address
+    0005D5A0    13004
+    00080674    14004
+    00080AFF    12818
+
+ */
+
+#define CHIP_AU8830
+
+#define CARD_NAME "Aureal Vortex 2 3D Sound Processor"
+#define CARD_NAME_SHORT "au8830"
+
+#ifndef PCI_VENDOR_ID_AUREAL
+#define PCI_VENDOR_ID_AUREAL 0x12eb
+#endif
+#ifndef PCI_VENDOR_ID_AUREAL_VORTEX2
+#define PCI_DEVICE_ID_AUREAL_VORTEX2 0x0002
+#endif
+
+#define hwread(x,y) readl((x)+((y)>>2))
+#define hwwrite(x,y,z) writel((z),(x)+((y)>>2))
+
+#define NR_ADB 0x20
+#define NR_SRC 0x10
+#define NR_A3D 0x10
+#define NR_MIXIN 0x20
+#define NR_MIXOUT 0x10
+#define NR_WT 0x40
+
+/* ADBDMA */
+#define VORTEX_ADBDMA_STAT 0x27e00 /* read only, subbuffer, DMA pos */
+#define		POS_MASK 0x00000fff
+#define     POS_SHIFT 0x0
+#define 	ADB_SUBBUF_MASK 0x00003000  /* ADB only. */
+#define     ADB_SUBBUF_SHIFT 0xc        /* ADB only. */
+#define VORTEX_ADBDMA_CTRL 0x27a00 		/* write only; format, flags, DMA pos */
+#define		OFFSET_MASK 0x00000fff
+#define     OFFSET_SHIFT 0x0
+#define		IE_MASK 0x00001000 /* interrupt enable. */
+#define     IE_SHIFT 0xc
+#define     U_MASK 0x00002000  /* Unknown. If you know, tell me. */
+#define     U_SHIFT 0xd
+#define		FMT_MASK 0x0003c000
+#define		FMT_SHIFT 0xe
+#define		ADB_FIFO_EN_SHIFT	0x15
+#define		ADB_FIFO_EN			(1 << 0x15)
+// The ADB masks and shift also are valid for the wtdma, except if specified otherwise.
+#define VORTEX_ADBDMA_BUFCFG0 0x27800
+#define VORTEX_ADBDMA_BUFCFG1 0x27804
+#define VORTEX_ADBDMA_BUFBASE 0x27400
+#define VORTEX_ADBDMA_START 0x27c00 /* Which subbuffer starts */
+
+/* WTDMA */
+#define VORTEX_WTDMA_CTRL 0x27900 /* format, DMA pos */
+#define VORTEX_WTDMA_STAT 0x27d00 /* DMA subbuf, DMA pos */
+#define     WT_SUBBUF_MASK 0x3
+#define     WT_SUBBUF_SHIFT 0xc
+#define VORTEX_WTDMA_BUFBASE 0x27000
+#define VORTEX_WTDMA_BUFCFG0 0x27600
+#define VORTEX_WTDMA_BUFCFG1 0x27604
+#define VORTEX_WTDMA_START 0x27b00 /* which subbuffer is first */
+
+#define VORTEX_WT_BASE (0x420 << 7)
+
+/* ADB */
+#define VORTEX_ADB_SR 0x28400 /* Samplerates enable/disable */
+#define VORTEX_ADB_RTBASE 0x28000
+#define VORTEX_ADB_RTBASE_SIZE (VORTEX_ADB_CHNBASE - VORTEX_ADB_RTBASE)
+#define VORTEX_ADB_CHNBASE 0x282b4
+#define VORTEX_ADB_CHNBASE_SIZE (ADB_MASK - VORTEX_ADB_RTBASE_SIZE)
+#define 	ROUTE_MASK	0xffff
+#define		SOURCE_MASK	0xff00
+#define     ADB_MASK   0xff
+#define		ADB_SHIFT 0x8
+/* ADB address */
+#define		OFFSET_ADBDMA	0x00
+#define		OFFSET_SRCIN	0x40
+#define		OFFSET_SRCOUT	0x20 /* ch 0x11 */
+#define		OFFSET_MIXIN	0x50 /* ch 0x11 */
+#define		OFFSET_MIXOUT	0x30 /* ch 0x11 */
+#define		OFFSET_CODECIN	0x70 /* ch 0x11 */ /* adb source */
+#define		OFFSET_CODECOUT	0x88 /* ch 0x11 */ /* adb target */
+#define		OFFSET_SPORTIN	0x78 /* ch 0x13 */
+#define		OFFSET_SPORTOUT	0x90 /* ch 0x13 */
+#define		OFFSET_SPDIFOUT	0x92 /* ch 0x14 */
+#define		OFFSET_EQIN		0xa0 /* ch 0x11 */
+#define		OFFSET_EQOUT	0x7e /* ch 0x11 */ /* 2 routes on ch 0x11 */
+#define		OFFSET_WTOUT	0x62 /* 0x64, 0x65, 0xA2, 0xA4, 0xA5 */
+#define		OFFSET_UNKNOWNOUT0	0x66 /* ch 0x11 */
+#define		OFFSET_UNKNOWNOUT1	0x67 /* ch 0x11 */
+#define		OFFSET_UNKNOWNIN0	0x96 /* ch 0x11 */
+#define		OFFSET_UNKNOWNIN1	0x9b /* ch 0x11 */
+
+/* ADB route translate helper */
+#define ADB_DMA(x) (x)
+#define ADB_SRCOUT(x) (x + OFFSET_SRCOUT)
+#define ADB_SRCIN(x) (x + OFFSET_SRCIN)
+#define ADB_MIXOUT(x) (x + OFFSET_MIXOUT)
+#define ADB_MIXIN(x) (x + OFFSET_MIXIN)
+#define ADB_CODECIN(x) (x + OFFSET_CODECIN)
+#define ADB_CODECOUT(x) (x + OFFSET_CODECOUT)
+#define ADB_SPORTIN(x) (x + OFFSET_SPORTIN)
+#define ADB_SPORTOUT(x) (x + OFFSET_SPORTOUT)
+#define ADB_SPDIFOUT(x)	(x + OFFSET_SPDIFOUT)
+#define ADB_EQIN(x) (x + OFFSET_EQIN)
+#define ADB_EQOUT(x) (x + OFFSET_EQOUT)
+#define ADB_A3DOUT(x) (x + 0x50) /* 0x10 A3D blocks */
+#define ADB_A3DIN(x) (x + 0x70)
+#define ADB_WTOUT(x) (x + OFFSET_WTOUT)
+
+#define MIX_DEFIGAIN 0x00
+#define MIX_DEFOGAIN 0x00 /* 0x8->6dB  (6dB = x4) 16 to 18 bit conversion? */
+
+/* MIXER */
+#define VORTEX_MIXER_SR 0x21f00
+#define VORTEX_MIXER_CHNBASE 0x21e40
+#define VORTEX_MIXER_RTBASE 0x21e00
+#define 	MIXER_RTBASE_SIZE 0x38
+#define VORTEX_MIX_U0 0x21c00 /* AU8820: 0x9c00 */
+
+/* MIX */
+#define VORTEX_MIX_INVOL_A 0x21000 /* in? */
+#define VORTEX_MIX_INVOL_B 0x20000 /* out? */
+#define VORTEX_MIX_VOL_A 0x21800
+#define VORTEX_MIX_VOL_B 0x20800
+#define VORTEX_MIX_ENIN 0x21a00 /* Input enable bits. 4 bits wide. */
+#define 	VOL_MIN 0x80 /* Input volume when muted. */
+#define		VOL_MAX 0x7f /* FIXME: Not confirmed! Just guessed. */
+
+/* SRC */
+#define VORTEX_SRCBLOCK_SR	0x26cc0
+#define VORTEX_SRC_CHNBASE	0x26c40
+#define VORTEX_SRC_RTBASE	0x26c00
+#define VORTEX_SRC_SOURCE	0x26cc4
+#define VORTEX_SRC_CONVRATIO 0x26e40
+#define VORTEX_SRC_DRIFT0	0x26e80
+#define VORTEX_SRC_DRIFT1	0x26ec0
+#define VORTEX_SRC_DRIFT2	0x26f40
+#define VORTEX_SRC_U0		0x26e00
+#define		U0_SLOWLOCK		0x200
+#define VORTEX_SRC_U1		0x26f00
+#define VORTEX_SRC_U3		0x26f80
+#define VORTEX_SRC_DATA		0x26800 /* 0xc800 */
+#define VORTEX_SRC_DATA0	0x26000
+
+/* FIFO */
+#define VORTEX_FIFO_ADBCTRL 0x16100 /* Control bits. */
+#define VORTEX_FIFO_WTCTRL 0x16000
+#define		FIFO_RDONLY	0x00000001
+#define		FIFO_CTRL	0x00000002 /* Allow ctrl. ? */
+#define		FIFO_VALID	0x00000010
+#define 	FIFO_EMPTY	0x00000020
+#define		FIFO_U0		0x00002000 /* Unknown. */
+#define		FIFO_U1		0x00040000
+#define		FIFO_SIZE_BITS 6
+#define		FIFO_SIZE	(1<<(FIFO_SIZE_BITS)) // 0x40
+#define 	FIFO_MASK	(FIFO_SIZE-1) //0x3f	/* at shift left 0xc */
+#define 	FIFO_BITS	0x1c400000
+#define VORTEX_FIFO_ADBDATA 0x14000
+#define VORTEX_FIFO_WTDATA 0x10000
+
+/* CODEC */
+#define VORTEX_CODEC_CTRL 0x29184
+#define VORTEX_CODEC_EN 0x29190
+#define		EN_AUDIO0		0x00000300
+#define		EN_MODEM		0x00000c00
+#define		EN_AUDIO1		0x00003000
+#define		EN_SPORT		0x00030000
+#define		EN_SPDIF		0x000c0000
+#define		EN_CODEC		(EN_AUDIO1 | EN_AUDIO0)
+#define VORTEX_CODEC_CHN 0x29080 /* The name "CHN" is wrong. */
+#define VORTEX_CODEC_WRITE 0x00800000
+#define VORTEX_CODEC_ADDSHIFT 16
+#define VORTEX_CODEC_ADDMASK 0x7f0000 /* 0x000f0000*/
+#define VORTEX_CODEC_DATSHIFT 0
+#define VORTEX_CODEC_DATMASK 0xffff
+#define VORTEX_CODEC_IO 0x29188
+
+#define VORTEX_SPDIF_FLAGS		0x2205c
+#define VORTEX_SPDIF_CFG0		0x291D0
+#define VORTEX_SPDIF_CFG1		0x291D4
+#define VORTEX_SPDIF_SMPRATE	0x29194
+
+/* Sample timer */
+#define VORTEX_SMP_TIME  0x29198
+
+/* IRQ */
+#define VORTEX_IRQ_SOURCE 0x2a000 /* Interrupt source flags. */
+#define 	IRQ_FATAL    0x0001
+#define 	IRQ_PARITY   0x0002
+#define 	IRQ_PCMOUT   0x0020 /* ?? */
+#define 	IRQ_TIMER    0x1000
+#define 	IRQ_MIDI     0x2000
+#define		IRQ_MODEM	0x4000
+
+#define VORTEX_IRQ_U0	0x2a008 /* ?? */
+
+#define VORTEX_CTRL		0x2a00c
+#define 	CTRL_MIDI_EN	0x00000001
+#define 	CTRL_MIDI_PORT	0x00000060
+#define 	CTRL_GAME_EN	0x00000008
+#define 	CTRL_GAME_PORT	0x00000e00
+#define		CTRL_SPDIF		0x00000000 /* unknown. Please find this value*/
+#define 	CTRL_SPORT		0x00200000
+#define 	CTRL_UNKNOWN	0x01000000
+#define 	CTRL_IRQ_ENABLE	0x00004000
+
+#define VORTEX_IRQ_CTRL 0x2a004 /* Interrupt source mask. */
+#define 	IRQ_FATAL	0x0001
+#define 	IRQ_PARITY	0x0002
+#define 	IRQ_PCMOUT	0x0020 /* PCM OUT page crossing */
+#define 	IRQ_TIMER	0x1000
+#define 	IRQ_MIDI	0x2000
+#define		IRQ_MODEM	0x4000
+
+/* write: Timer period config / read: TIMER IRQ ack. */
+#define VORTEX_IRQ_STAT 0x2919c
+
+/* DMA */
+#define VORTEX_ENGINE_CTRL 0x27ae8
+#define 	ENGINE_INIT 0x1380000
+
+/* MIDI */ /* GAME. */
+#define VORTEX_MIDI_DATA 0x28800
+#define VORTEX_MIDI_CMD 0x28804  /* Write command / Read status */
+
+#define VORTEX_CTRL2 0x2880c
+#define		CTRL2_GAME_ADCMODE 0x40
+#define VORTEX_GAME_LEGACY 0x28808
+#define VORTEX_GAME_AXIS 0x28810
+#define		AXIS_SIZE 4
+#define		AXIS_RANGE 0x1fff
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8830_core.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8830_core.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8830_core.c	(revision 91)
@@ -0,0 +1,2 @@
+#include "au8830.h"
+#include "au88x0_core.c"
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8830_eq.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8830_eq.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8830_eq.c	(revision 91)
@@ -0,0 +1,2 @@
+#include "au8830.h"
+#include "au88x0_eq.c"
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8830_game.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8830_game.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8830_game.c	(revision 91)
@@ -0,0 +1,2 @@
+#include "au8830.h"
+#include "au88x0_game.c"
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8830_mixer.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8830_mixer.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8830_mixer.c	(revision 91)
@@ -0,0 +1,2 @@
+#include "au8830.h"
+#include "au88x0_mixer.c"
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8830_mpu401.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8830_mpu401.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8830_mpu401.c	(revision 91)
@@ -0,0 +1,2 @@
+#include "au8830.h"
+#include "au88x0_mpu401.c"
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8830_pcm.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8830_pcm.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8830_pcm.c	(revision 91)
@@ -0,0 +1,2 @@
+#include "au8830.h"
+#include "au88x0_pcm.c"
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8830_synth.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8830_synth.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au8830_synth.c	(revision 91)
@@ -0,0 +1,2 @@
+#include "au8830.h"
+#include "au88x0_synth.c"
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au88x0.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au88x0.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au88x0.c	(revision 91)
@@ -0,0 +1,451 @@
+/*
+ * ALSA driver for the Aureal Vortex family of soundprocessors.
+ * Author: Manuel Jander (mjander@embedded.cl)
+ *
+ *   This driver is the result of the OpenVortex Project from Savannah
+ * (savannah.nongnu.org/projects/openvortex). I would like to thank
+ * the developers of OpenVortex, Jeff Muizelar and Kester Maddock, from
+ * whom i got plenty of help, and their codebase was invaluable.
+ *   Thanks to the ALSA developers, they helped a lot working out
+ * the ALSA part.
+ *   Thanks also to Sourceforge for maintaining the old binary drivers,
+ * and the forum, where developers could comunicate.
+ *
+ * Now at least i can play Legacy DOOM with MIDI music :-)
+ */
+
+#include "au88x0.h"
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+// module parameters (see "Module Parameters")
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+
+MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard.");
+MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC);
+MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard.");
+MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC);
+MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard.");
+MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC);
+
+MODULE_DESCRIPTION("Aureal vortex");
+MODULE_CLASSES("{sound}");
+MODULE_LICENSE("GPL");
+MODULE_DEVICES("{{Aureal Semiconductor Inc., Aureal Vortex Sound Processor}}");
+
+static struct pci_device_id snd_vortex_ids[] = {
+    {PCI_VENDOR_ID_AUREAL, PCI_DEVICE_ID_AUREAL_ADVANTAGE,PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1,},
+    {PCI_VENDOR_ID_AUREAL, PCI_DEVICE_ID_AUREAL_VORTEX,PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0,},
+    {PCI_VENDOR_ID_AUREAL, PCI_DEVICE_ID_AUREAL_VORTEX2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0,},
+    {0,}
+};
+
+#ifndef MODULE
+/* format is: snd-mychip=enable,index,id */
+static int __init alsa_card_vortex_setup(char *str) {
+    static unsigned __initdata nr_dev = 0;
+
+    if (nr_dev >= SNDRV_CARDS)
+        return 0;
+    (void) (get_option(&str, &enable[nr_dev]) == 2 &&
+            get_option(&str, &index[nr_dev]) == 2 &&
+            get_id(&str, &id[nr_dev]) == 2);
+    nr_dev++;
+    return 1;
+}
+__setup("snd-au88x0=", alsa_card_vortex_setup);
+#endif /* ifndef MODULE */
+
+
+MODULE_DEVICE_TABLE(pci, snd_vortex_ids);
+#define PCI_DEVICE_ID_VIA_8365_1 0x8305
+#define PCI_VENDOR_ID_VIA 0x1106
+static void __devinit snd_vortex_workaround(struct pci_dev *vortex, int fix) {
+    struct pci_dev *via=NULL;
+    int rc;
+    /* autodetect if workarounds are required */
+    while( (via = pci_find_device(PCI_VENDOR_ID_VIA,
+                                  PCI_DEVICE_ID_VIA_8365_1, via)) ) {
+        if(fix == 255) {
+            printk("detected VIA KT133/KM133. activating workaround...\n");
+            fix = 3; // do latency and via bridge workaround
+        }
+        break;
+    }
+
+    /* fix vortex latency */
+    if(fix & 0x01) {
+        if( !(rc = pci_write_config_byte(vortex, 0x40, 0xff)) ) {
+            printk("vortex latency is 0xff\n");
+        }
+        else {
+            printk("could not set vortex latency: pci error 0x%x\n", rc);
+        }
+    }
+
+    /* fix via agp bridge */
+    if(via && (fix & 0x02)) {
+        u8 value;
+
+        /*
+         * only set the bit (Extend PCI#2 Internal Master for
+         * Efficient Handling of Dummy Requests) if the can
+         * read the config and it is not already set
+         */
+
+        if( !(rc = pci_read_config_byte(via, 0x42, &value)) && (
+                                                                (value & 0x10) ||
+                                                                !(rc=pci_write_config_byte(via, 0x42, value|0x10)) ) ) {
+
+            printk("bridge config is 0x%x\n", value|0x10);
+        }
+        else {
+            printk("could not set vortex latency: pci error 0x%x\n", rc);
+        }
+    }
+}
+
+// component-destructor
+// (see "Management of Cards and Components")
+static int snd_vortex_dev_free(snd_device_t *device) {
+    vortex_t *vortex = snd_magic_cast(vortex_t, device->device_data,
+                                      return -ENXIO);
+
+#if defined(CONFIG_GAMEPORT)
+    vortex_gameport_unregister(vortex);
+#endif
+    vortex_core_shutdown(vortex);
+    // Take down PCI interface.
+    synchronize_irq(vortex->irq);
+    free_irq(vortex->irq, vortex);
+//    pci_release_regions(vortex->pci_dev);
+//    pci_disable_device(vortex->pci_dev);
+    snd_magic_kfree(vortex);
+
+    return 0;
+}
+
+// chip-specific constructor
+// (see "Management of Cards and Components")
+static int __devinit
+snd_vortex_create(snd_card_t *card, struct pci_dev *pci, vortex_t **rchip) {
+    vortex_t *chip;
+    int err;
+    struct resource * reg_temp;
+    unsigned long res;
+    static snd_device_ops_t ops = {
+        snd_vortex_dev_free,0,0,0
+    };
+
+    *rchip = NULL;
+
+    // check PCI availability (DMA).
+    if ((err = pci_enable_device(pci)) < 0)
+        return err;
+    if (!pci_dma_supported(pci, VORTEX_DMA_MASK)) {
+        printk(KERN_ERR "error to set DMA mask\n");
+        return -ENXIO;
+    }
+    pci_set_dma_mask(pci, VORTEX_DMA_MASK);
+
+    chip = snd_magic_kcalloc(vortex_t, 0, GFP_KERNEL);
+    if (chip == NULL)
+        return -ENOMEM;
+
+    chip->card = card;
+
+    // initialize the stuff
+    chip->pci_dev = pci;
+    chip->io = pci_resource_start(pci, 0);
+    chip->vendor = pci->vendor;
+    chip->device = pci->device;
+    chip->card = card;
+    chip->irq = -1;
+    spin_lock_init(&chip->lock);
+
+    // (1) PCI resource allocation
+    // Get MMIO area
+    //
+//    if ((err = pci_request_regions(pci, CARD_NAME_SHORT)) != 0)
+    //        goto regions_out;
+    res = pci_resource_start(pci, 0);
+    if ((reg_temp = request_region(res, pci_resource_len(pci, 0), CARD_NAME_SHORT)) == NULL) {
+                    snd_printk("unable to grab VORTEX I/O memory \n");
+                    return -EBUSY;
+    }
+
+    res = pci_resource_start(pci, 1);
+    if ((reg_temp = request_region(res, pci_resource_len(pci, 1), CARD_NAME_SHORT)) == NULL) {
+                    snd_printk("unable to grab VORTEX I/O memory \n");
+                    return -EBUSY;
+    }
+
+    res = pci_resource_start(pci, 2);
+
+    if ((reg_temp = request_mem_region(res, pci_resource_len(pci, 2), CARD_NAME_SHORT)) == NULL) {
+        snd_printk("unable to grab I/O memory 0x%lx-0x%lx\n", res, res+pci_resource_len(pci, 2));
+        return -EBUSY;
+    }
+
+    chip->mmio = ioremap_nocache(res, pci_resource_len(pci, 2));
+#ifdef DEBUG
+    dprintf(("got MMIO: %x, IO: %x",chip->mmio, res));
+#endif
+    if (!chip->mmio) {
+        printk(KERN_ERR "MMIO area remap failed.\n");
+        err = -ENOMEM;
+        goto ioremap_out;
+    }
+
+    //by Eleph
+    //pci_set_latency_time(pci, 0x40);
+
+//    pci_write_config_byte(pci, 0x40,0xFF); // Vortex specific patch
+    /* Init audio core.
+     * This must be done before we do request_irq otherwise we can get spurious
+     * interupts that we do not handle properly and make a mess of things */
+    if ((err = vortex_core_init(chip)) != 0) {
+	printk(KERN_ERR "hw core init failed\n");
+	goto core_out;
+    }
+
+    if ((err = request_irq(pci->irq, vortex_interrupt, SA_INTERRUPT | SA_SHIRQ,
+                    CARD_NAME_SHORT, (void *) chip)) != 0) {
+        printk(KERN_ERR "cannot grab irq\n");
+        goto irq_out;
+    }
+    chip->irq = pci->irq;
+
+    pci_set_master(pci);
+    // End of PCI setup.
+
+
+    // Register alsa root device.
+    if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+    	goto alloc_out;
+    }
+
+    *rchip = chip;
+
+    return 0;
+
+alloc_out:
+    synchronize_irq(chip->irq);
+    free_irq(chip->irq, chip);
+irq_out:
+    vortex_core_shutdown(chip);
+core_out:
+    //FIXME: the type of chip->mmio might need to be changed??
+    iounmap((void*)chip->mmio);
+ioremap_out:
+//    pci_release_regions(chip->pci_dev);
+regions_out:
+//    pci_disable_device(chip->pci_dev);
+    //FIXME: this not the right place to unregister the gameport
+#if defined(CONFIG_GAMEPORT)
+    vortex_gameport_unregister(chip);
+#endif
+    return err;
+}
+
+// constructor -- see "Constructor" sub-section
+static int __devinit
+snd_vortex_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) {
+    static int dev;
+    snd_card_t *card;
+    vortex_t *chip;
+    int err;
+
+    // (1)
+#ifdef DEBUG
+    dprintf(("snd_vortex_probe"));
+#endif
+    if (dev >= SNDRV_CARDS)
+        return -ENODEV;
+    if (!enable[dev]) {
+        dev++;
+        return -ENOENT;
+    }
+    // (2)
+#ifdef DEBUG
+    dprintf(("snd_vortex_probe 2"));
+#endif
+    card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+    if (card == NULL)
+        return -ENOMEM;
+
+    // (3)
+#ifdef DEBUG
+    dprintf(("snd_vortex_probe 3"));
+#endif
+    if ((err = snd_vortex_create(card, pci, &chip)) < 0) {
+        snd_card_free(card);
+        return err;
+    }
+
+    snd_vortex_workaround(pci, 255);
+
+    // (4) Alloc components.
+    // ADB pcm.
+#ifdef DEBUG
+    dprintf(("snd_vortex_probe 4"));
+#endif
+    if ((err = snd_vortex_new_pcm(chip, VORTEX_PCM_ADB, NR_ADB)) < 0) {
+        snd_card_free(card);
+        return err;
+    }
+#ifndef CHIP_AU8820
+#ifdef DEBUG
+    dprintf(("snd_vortex_probe 4/1"));
+#endif
+	// ADB SPDIF
+    if ((err = snd_vortex_new_pcm(chip, VORTEX_PCM_SPDIF, 1)) < 0) {
+        snd_card_free(card);
+        return err;
+    }
+#endif
+	/*
+	// ADB I2S
+    if ((err = snd_vortex_new_pcm(chip, VORTEX_PCM_I2S, 1)) < 0) {
+        snd_card_free(card);
+        return err;
+    }
+	*/
+#ifndef CHIP_AU8810
+    // WT pcm.
+    if ((err = snd_vortex_new_pcm(chip, VORTEX_PCM_WT, NR_WT)) < 0) {
+        snd_card_free(card);
+        return err;
+    }
+#endif
+    // snd_ac97_mixer and Vortex mixer.
+#ifdef DEBUG
+    dprintf(("snd_vortex_probe.mixer init"));
+#endif
+    if ((err = snd_vortex_mixer(chip)) < 0) {
+        snd_card_free(card);
+        return err;
+    }
+#ifdef DEBUG
+    dprintf(("snd_vortex_probe. midi init"));
+#endif
+    if ((err = snd_vortex_midi(chip)) < 0) {
+        snd_card_free(card);
+        return err;
+    }
+#if defined(CONFIG_GAMEPORT)
+    if ((err = vortex_gameport_register(chip)) < 0) {
+        snd_card_free(card);
+        return err;
+    }
+#endif
+#if 0
+    if (snd_seq_device_new(card, 1, SNDRV_SEQ_DEV_ID_VORTEX_SYNTH,
+                           sizeof(snd_vortex_synth_arg_t), &wave) < 0
+        || wave == NULL) {
+        snd_printk("Can't initialize Aureal wavetable synth\n");
+    } else {
+        snd_vortex_synth_arg_t *arg;
+
+        arg = SNDRV_SEQ_DEVICE_ARGPTR(wave);
+        strcpy(wave->name, "Aureal Synth");
+        arg->hwptr = vortex;
+        arg->index = 1;
+        arg->seq_ports = seq_ports[dev];
+        arg->max_voices = max_synth_voices[dev];
+    }
+#endif
+
+    // (5)
+    strcpy(card->driver, "Aureal Vortex");
+    strcpy(card->shortname, CARD_NAME_SHORT);
+    sprintf(card->longname, "%s at 0x%lx irq %i",
+            card->shortname, chip->io, chip->irq);
+
+#ifdef CHIP_AU8830
+    {
+        unsigned char revision;
+        if ((err = pci_read_config_byte(pci, PCI_REVISION_ID, &revision)) < 0) {
+            snd_card_free(card);
+            return err;
+    	}
+
+	if (revision != 0xfe && revision != 0xfa) {
+            printk(KERN_ALERT "vortex: The revision (%x) of your card has not been seen before.\n", revision);
+            printk(KERN_ALERT "vortex: Please email the results of 'lspci -vv' to openvortex-dev@nongnu.org.\n");
+            snd_card_free(card);
+            err = -ENODEV;
+            return err;
+        }
+    }
+#endif
+    // (6)
+#ifdef DEBUG
+    dprintf(("snd_vortex_probe 6"));
+#endif
+    if ((err = snd_card_register(card)) < 0) {
+        snd_card_free(card);
+        return err;
+    }
+    // (7)
+#ifdef DEBUG
+    dprintf(("snd_vortex_probe 7"));
+#endif
+    pci_set_drvdata(pci, chip);
+    dev++;
+	vortex_connect_default(chip, 1);
+    vortex_enable_int(chip);
+    return 0;
+}
+
+// destructor -- see "Destructor" sub-section
+static void __devexit snd_vortex_remove(struct pci_dev *pci) {
+    vortex_t *vortex = snd_magic_cast(vortex_t,
+                                      pci_get_drvdata(pci), return);
+
+    if (vortex) {
+        // Release ALSA stuff.
+        snd_card_free(vortex->card);
+        // Free Vortex struct.
+        pci_set_drvdata(pci, NULL);
+    } else
+        printk("snd_vortex_remove called more than one time!\n");
+}
+
+// pci_driver definition
+static struct pci_driver driver = {
+    0,0,0,CARD_NAME_SHORT,
+    snd_vortex_ids,
+    snd_vortex_probe,
+    snd_vortex_remove,0,0
+};
+
+// initialization of the module
+static int __init alsa_card_vortex_init(void) {
+    int err;
+
+    if ((err = pci_module_init(&driver)) < 0) {
+//#ifdef MODULE
+//        printk(KERN_ERR "Aureal soundcard not found " "or device busy\n");
+//#endif
+        return err;
+    }
+    return 0;
+}
+
+// clean up the module
+static void __exit alsa_card_vortex_exit(void) {
+    pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_vortex_init)
+module_exit(alsa_card_vortex_exit)
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au88x0.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au88x0.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au88x0.h	(revision 91)
@@ -0,0 +1,212 @@
+/*
+    Aureal Vortex Soundcard driver.
+
+    IO addr collected from asp4core.vxd:
+    function    address
+    0005D5A0    13004
+    00080674    14004
+    00080AFF    12818
+
+ */
+
+#ifndef __SOUND_AU88X0_H
+#define __SOUND_AU88X0_H
+
+#ifdef __KERNEL__
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/rawmidi.h>
+#include <sound/mpu401.h>
+#include <sound/hwdep.h>
+#include <sound/ac97_codec.h>
+#include <asm/io.h>
+#include <linux/init.h>
+#include "au88x0_eq.h"
+
+/*
+#ifndef	PCI_VENDOR_ID_AUREAL
+#define	PCI_VENDOR_ID_AUREAL 0x12eb
+#endif
+#ifndef	PCI_VENDOR_ID_AUREAL_VORTEX
+#define	PCI_DEVICE_ID_AUREAL_VORTEX 0x0001
+#endif
+#ifndef	PCI_VENDOR_ID_AUREAL_VORTEX2
+#define	PCI_DEVICE_ID_AUREAL_VORTEX2 0x0002
+#endif
+#ifndef	PCI_VENDOR_ID_AUREAL_ADVANTAGE
+#define	PCI_DEVICE_ID_AUREAL_ADVANTAGE 0x0003
+#endif
+*/
+#endif
+
+#define	VORTEX_DMA_MASK	0xffffffff
+
+#define	hwread(x,y) readl((x)+((y)>>2))
+#define	hwwrite(x,y,z) writel((z),(x)+((y)>>2))
+
+/* Vortex MPU401 defines. */
+#define	MIDI_CLOCK_DIV		0x61
+/* Standart MPU401 defines. */
+#define	MPU401_RESET		0xff
+#define	MPU401_ENTER_UART	0x3f
+#define	MPU401_ACK		0xfe
+
+// Get src register value to convert from x to y.
+#define	SRC_RATIO(x,y)		((((x<<15)/y) + 1)/2)
+
+/* FIFO software state constants. */
+#define FIFO_STOP 0
+#define FIFO_START 1
+#define FIFO_PAUSE 2
+
+/* ADB Resource */
+#define VORTEX_RESOURCE_DMA		0x00000000
+#define VORTEX_RESOURCE_SRC		0x00000001
+#define VORTEX_RESOURCE_MIXIN	0x00000002
+#define VORTEX_RESOURCE_MIXOUT	0x00000003
+#define VORTEX_RESOURCE_A3D		0x00000004
+#define VORTEX_RESOURCE_LAST	0x00000005
+
+/* Check for SDAC bit in "Extended audio ID" AC97 register */
+#define VORTEX_IS_QUAD(x) ((x->codec == NULL) ?  0 : (x->codec->ext_id|0x80))
+
+/* PCM devices */
+#define VORTEX_PCM_ADB		0
+#define VORTEX_PCM_SPDIF	1
+#define VORTEX_PCM_I2S		2
+#define VORTEX_PCM_A3D		3
+#define VORTEX_PCM_WT		4
+#define VORTEX_PCM_LAST		5
+
+#define MIX_CAPT(x) (vortex->mixcapt[x])
+#define MIX_PLAYB(x) (vortex->mixplayb[x])
+#define MIX_SPDIF(x) (vortex->mixspdif[x])
+
+/* Structs */
+typedef struct {
+    int fifo_enabled;   /* this_24 */
+    int fifo_status;    /* this_1c */
+    int dma_ctrl;       /* this_78 (ADB), this_7c (WT) */
+    int dma_unknown;    /* this_74 (ADB), this_78 (WT) */
+    int cfg0;
+    int cfg1;
+
+    int nr_ch;		/* Nr of PCM channels */
+	int type;		/* Output type (ac97, spdif, i2s, dsp)*/
+	int dma;		/* Hardware DMA index. */
+	int dir;		/* Stream Direction. */
+	u32 resources[5];
+	
+	/* Virtual page extender stuff */
+	int nr_periods;
+	int period_bytes;
+	unsigned long buf_addr;
+	int period_real;
+	int period_virt;
+
+    snd_pcm_substream_t *substream;
+} stream_t;
+
+typedef struct snd_vortex vortex_t;
+struct snd_vortex {
+    /* ALSA structs. */
+	snd_card_t *card;
+	snd_pcm_t *pcm[VORTEX_PCM_LAST];
+	
+    snd_rawmidi_t *rmidi;   /* Legacy Midi interface. */
+	ac97_t *codec;
+
+	/* Stream structs. */
+    stream_t dma_adb[NR_ADB];
+	int spdif_sr;
+#ifndef CHIP_AU8810
+    stream_t dma_wt[NR_WT];
+	unsigned char mixwt[6];	/* WT mixin objects */
+#endif
+	/* Global resources */
+	unsigned char mixcapt[2];
+	unsigned char mixplayb[4];
+	unsigned char mixspdif[2];
+	u32 fixed_res[5];
+	
+	/* Hardware equalizer structs */
+	eqlzr_t eq; 
+	
+	/* Extra controls */
+	//snd_kcontrol_t *eqctrl[20];
+	
+	/* Gameport stuff. */
+	struct gameport *gameport;
+
+	/* PCI hardware resources */
+	unsigned long io;
+	u32 *mmio;
+        unsigned int irq;
+
+        spinlock_t lock;
+
+	/* PCI device */
+	struct pci_dev * pci_dev;
+	u16 vendor;
+	u16 device;
+	u8 rev;
+};
+
+#define chip_t vortex_t
+
+/* Functions. */
+
+/* SRC */
+void vortex_adb_setsrc(vortex_t *vortex, int adbdma, unsigned int cvrt, int dir);
+
+/* DMA Engines. */
+void vortex_adbdma_setbuffers(vortex_t *vortex, int adbdma, unsigned int addr, int size, int count);
+void vortex_wtdma_setbuffers(vortex_t *vortex, int wtdma, unsigned int addr, int size, int count);
+void vortex_adbdma_setmode(vortex_t *vortex, int adbdma, int ie, int b, int fmt, int d, unsigned long offset);
+void vortex_wtdma_setmode(vortex_t *vortex, int wtdma, int ie, int b, int fmt, int d, unsigned long offset);
+void vortex_adbdma_setstartbuffer(vortex_t *vortex, int adbdma, int sb);
+void vortex_wtdma_setstartbuffer(vortex_t *vortex, int wtdma, int sb);
+
+void vortex_adbdma_startfifo(vortex_t *vortex, int adbdma);
+void vortex_adbdma_stopfifo(vortex_t *vortex, int adbdma);
+void vortex_wtdma_startfifo(vortex_t *vortex, int wtdma);
+void vortex_wtdma_stopfifo(vortex_t *vortex, int wtdma);
+void vortex_adbdma_pausefifo(vortex_t *vortex, int adbdma);
+void vortex_adbdma_resumefifo(vortex_t *vortex, int adbdma);
+void vortex_wtdma_pausefifo(vortex_t *vortex, int wtdma);
+void vortex_wtdma_resumefifo(vortex_t *vortex, int wtdma);
+
+//inline int vortex_adbdma_getlinearpos(vortex_t *vortex, int adbdma);
+//inline int vortex_wtdma_getlinearpos(vortex_t *vortex, int wtdma);
+
+/* global stuff. */
+void vortex_codec_init(vortex_t *vortex);
+void vortex_codec_write(ac97_t *codec, unsigned short addr,unsigned short data);
+unsigned short vortex_codec_read(ac97_t *codec, unsigned short addr);
+void vortex_spdif_init(vortex_t *vortex, int spdif_sr, int spdif_mode);
+
+int  vortex_core_init(vortex_t *card);
+int  vortex_core_shutdown(vortex_t *card);
+void vortex_enable_int(vortex_t *card);
+irqreturn_t vortex_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+int  vortex_alsafmt_aspfmt(int alsafmt);
+
+/* Connection  stuff. */
+void vortex_connect_default(vortex_t *vortex, int en);
+int  vortex_adb_allocroute(vortex_t *vortex, int dma, int nr_ch, int dir, int type);
+int  vortex_wt_allocroute(vortex_t *vortex, int dma, int nr_ch);
+int  vortex_adb_checkinout(vortex_t *vortex, u32 resmap[], int out, int restype);
+void vortex_wt_connect(vortex_t *vortex, int en, unsigned char mixers[]);
+void vortex_wt_InitializeWTRegs(vortex_t * vortex);
+
+/* Driver stuff. */
+int vortex_gameport_register(vortex_t *card);
+int vortex_gameport_unregister(vortex_t *card);
+int vortex_eq_init(vortex_t *vortex);
+int vortex_eq_free(vortex_t *vortex);
+/* ALSA stuff. */
+int snd_vortex_new_pcm(vortex_t *vortex, int idx, int nr);
+int snd_vortex_mixer(vortex_t *vortex);
+int snd_vortex_midi(vortex_t *vortex);
+#endif
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au88x0_core.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au88x0_core.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au88x0_core.c	(revision 91)
@@ -0,0 +1,2371 @@
+/*
+    Vortex core low level functions.
+	
+ Author: Manuel Jander (mjander@users.sourceforge.cl)
+ These functions are mainly the result of translations made
+ from the original disassembly of the au88x0 binary drivers,
+ written by Aureal before they went down.
+ Many thanks to the Jeff Muizelar, Kester Maddock, and whoever
+ contributed to the OpenVortex project.
+ The author of this file, put the few available pieces together
+ and translated the rest of the riddle (Mix, Src and connection stuff).
+ Some things are still to be discovered, and their meanings are unclear.
+
+ Some of these functions aren't intended to be really used, rather
+ to help to understand how does the AU88X0 chips work. Keep them in, because
+ they could be used somewhere in the future.
+
+ This code hasn't been tested or proof read thoroughly. If you wanna help,
+ take a look at the AU88X0 assembly and check if this matches.
+ Functions tested ok so far are (they show the desired effect
+ at least):
+   vortex_routes(); (1 bug fixed).
+   vortex_adb_addroute();
+   vortex_adb_addroutes();
+   vortex_connect_codecplay();
+   vortex_src_flushbuffers();
+   vortex_adbdma_setmode();  note: still some unknown arguments!
+   vortex_adbdma_startfifo();
+   vortex_adbdma_stopfifo();
+   vortex_fifo_setadbctrl(); note: still some unknown arguments!
+   vortex_mix_setinputvolumebyte();
+   vortex_mix_enableinput();
+   vortex_mixer_addWTD(); (fixed)
+   vortex_connection_adbdma_src_src();
+   vortex_connection_adbdma_src();
+   vortex_src_change_convratio();
+   vortex_src_addWTD(); (fixed)
+
+ History:
+
+ 01-03-2003 First revision.
+ 01-21-2003 Some bug fixes.
+ 17-02-2003 many bugfixes after a big versioning mess.
+ 18-02-2003 JAAAAAHHHUUUUUU!!!! The mixer works !! I'm just so happy !
+			 (2 hours later...) I cant believe it! Im really lucky today.
+			 Now the SRC is working too! Yeah! XMMS works !
+ 20-02-2003 First steps into the ALSA world.
+ 28-02-2003 As my birthday present, i discovered how the DMA buffer pages really
+            work :-). It was all wrong.
+ 12-03-2003 ALSA driver starts working (2 channels).
+ 16-03-2003 More srcblock_setupchannel discoveries.
+ 12-04-2003 AU8830 playback support. Recording in the works.
+ 17-04-2003 vortex_route() and vortex_routes() bug fixes. AU8830 recording
+ 			works now, but chipn' dale effect is still there.
+ 16-05-2003 SrcSetupChannel cleanup. Moved the Src setup stuff entirely
+            into au88x0_pcm.c .
+ 06-06-2003 Buffer shifter bugfix. Mixer volume fix.
+ 
+*/
+
+#include "au88x0.h"
+#include <linux/delay.h>
+
+
+/*  MIXER (CAsp4Mix.s and CAsp4Mixer.s) */
+
+// FIXME: get rid of this.
+int mchannels[NR_MIXIN];
+int rampchs[NR_MIXIN];
+
+void vortex_mixer_en_sr(vortex_t *vortex, int channel) {
+    hwwrite(vortex->mmio, VORTEX_MIXER_SR, hwread(vortex->mmio, VORTEX_MIXER_SR) | (0x1 << channel));
+}
+void vortex_mixer_dis_sr(vortex_t *vortex, int channel) {
+    hwwrite(vortex->mmio, VORTEX_MIXER_SR,
+	    hwread(vortex->mmio, VORTEX_MIXER_SR) & ~(0x1 << channel));
+}
+
+void vortex_mix_muteinputgain(vortex_t *vortex, unsigned char mix, unsigned char channel) {
+    hwwrite(vortex->mmio, VORTEX_MIX_INVOL_A + ((mix << 5) + channel), 0x80);
+    hwwrite(vortex->mmio, VORTEX_MIX_INVOL_B + ((mix << 5) + channel), 0x80);
+}
+
+int  vortex_mix_getvolume(vortex_t *vortex, unsigned char mix) {
+    int a;
+    a = hwread(vortex->mmio, VORTEX_MIX_VOL_A + (mix << 2)) & 0xff;
+    //FP2LinearFrac(a);
+    return (a);
+}
+
+void vortex_mix_setvolumebyte(vortex_t *vortex, unsigned char mix, unsigned char vol) {
+    int temp;
+    hwwrite(vortex->mmio, VORTEX_MIX_VOL_A + (mix << 2), vol);
+    if (1) {			/*if (this_10) */
+		temp = hwread(vortex->mmio, VORTEX_MIX_VOL_B + (mix << 2));
+		if ((temp != 0x80) || (vol == 0x80))
+	    	return;
+    }
+    hwwrite(vortex->mmio, VORTEX_MIX_VOL_B + (mix << 2), vol);
+}
+
+int  vortex_mix_getinputvolume(vortex_t *vortex, unsigned char mix, int channel, int *vol) {
+    int a;
+    if (!(mchannels[mix] & (1 << channel)))
+	return 0;
+    a = hwread(vortex->mmio,
+	       VORTEX_MIX_INVOL_A + (((mix << 5) + channel) << 2));
+    /*
+       if (rampchs[mix] == 0)
+       a = FP2LinearFrac(a);
+       else
+       a = FP2LinearFracWT(a);
+     */
+    *vol = a;
+    return (0);
+}
+
+void vortex_mix_setinputvolumebyte(vortex_t *vortex, unsigned char mix, int mixin, unsigned char vol) {
+    int temp;
+
+    hwwrite(vortex->mmio, VORTEX_MIX_INVOL_A + (((mix << 5) + mixin) << 2), vol);
+    if (1) {			/* this_10, initialized to 1. */
+    	temp = hwread(vortex->mmio, VORTEX_MIX_INVOL_B + (((mix << 5) + mixin) << 2));
+    	if ((temp != 0x80) || (vol == 0x80))
+	        return;
+    }
+    hwwrite(vortex->mmio, VORTEX_MIX_INVOL_B + (((mix << 5) + mixin) << 2), vol);
+}
+
+int  vortex_mix_getenablebit(vortex_t *vortex, unsigned char mix, int mixin) {
+    int addr, temp;
+    if (mixin >= 0)
+	    addr = mixin;
+    else
+	    addr = mixin + 3;
+    addr = ((mix << 3) + (addr >> 2)) << 2;
+    temp = hwread(vortex->mmio, VORTEX_MIX_ENIN + addr);
+    return ((temp >> (mixin & 3)) & 1);
+}
+
+void vortex_mix_setenablebit(vortex_t *vortex, unsigned char mix, int mixin, int en) {
+    int temp, addr;
+
+    if (mixin < 0)
+        addr = (mixin + 3);
+    else
+	    addr = mixin;
+    addr = ((mix << 3) + (addr >> 2)) << 2;
+    temp = hwread(vortex->mmio, VORTEX_MIX_ENIN + addr);
+    if (en)
+	    temp |= (1 << (mixin & 3));
+    else
+	    temp &= ~(1 << (mixin & 3));
+    /* Mute input. Avoid crackling? */
+    hwwrite(vortex->mmio, VORTEX_MIX_INVOL_B + (((mix << 5) + mixin) << 2), 0x80);
+    /* No idea what this does. */
+    hwwrite(vortex->mmio, VORTEX_MIX_U0 + (mixin << 2), 0x0);
+    hwwrite(vortex->mmio, VORTEX_MIX_U0 + 4 + (mixin << 2), 0x0);
+    /* Write enable bit. */
+    hwwrite(vortex->mmio, VORTEX_MIX_ENIN + addr, temp);
+}
+
+void vortex_mix_killinput(vortex_t *vortex, unsigned char mix, int mixin) {
+    rampchs[mix] &= ~(1 << mixin);
+    vortex_mix_setinputvolumebyte(vortex, mix, mixin, 0x80);
+    mchannels[mix] &= ~(1 << mixin);
+    vortex_mix_setenablebit(vortex, mix, mixin, 0);
+}
+
+void vortex_mix_enableinput(vortex_t *vortex, unsigned char mix, int mixin) {
+    vortex_mix_killinput(vortex, mix, mixin);
+    if ((mchannels[mix] & (1 << mixin)) == 0) {
+	    vortex_mix_setinputvolumebyte(vortex, mix, mixin, 0x80);	/*0x80 : mute */
+	    mchannels[mix] |= (1 << mixin);
+    }
+    vortex_mix_setenablebit(vortex, mix, mixin, 1);
+}
+
+void vortex_mix_disableinput(vortex_t *vortex, unsigned char mix, int channel, int ramp) {
+    if (ramp) {
+	    rampchs[mix] |= (1 << channel);
+	    // Register callback.
+	    //vortex_mix_startrampvolume(vortex);
+	    vortex_mix_killinput(vortex, mix, channel);
+    } else
+	    vortex_mix_killinput(vortex, mix, channel);
+}
+
+int  vortex_mixer_addWTD(vortex_t *vortex, unsigned char mix, unsigned char ch) {
+    int temp, lifeboat = 0, prev;
+
+
+    temp = hwread(vortex->mmio, VORTEX_MIXER_SR);
+    if ((temp & (1 << ch)) == 0) {
+	    hwwrite(vortex->mmio, VORTEX_MIXER_CHNBASE + (ch << 2), mix);
+	    vortex_mixer_en_sr(vortex, ch);
+	    return 1;
+    }
+    prev = VORTEX_MIXER_CHNBASE + (ch << 2);
+    temp = hwread(vortex->mmio, prev);
+    while (temp & 0x10) {
+	    prev = VORTEX_MIXER_RTBASE + ((temp & 0xf) << 2);
+	    temp = hwread(vortex->mmio, prev);
+		//printk(KERN_INFO "vortex: mixAddWTD: while addr=%x, val=%x\n", prev, temp);
+	    if ((++lifeboat) > 0xf) {
+    	    printk(KERN_ERR "vortex_mixer_addWTD: lifeboat overflow\n");
+	        return 0;
+	    }
+    }
+    hwwrite(vortex->mmio, VORTEX_MIXER_RTBASE + ((temp & 0xf) << 2), mix);
+    hwwrite(vortex->mmio, prev, (temp & 0xf) | 0x10);
+    return 1;
+}
+
+int  vortex_mixer_delWTD(vortex_t *vortex, unsigned char mix, unsigned char ch) {
+	int esp14=-1, esp18, eax, ebx, edx, ebp, esi=0;
+	//int esp1f=edi(while)=src, esp10=ch;
+
+    eax = hwread(vortex->mmio, VORTEX_MIXER_SR);
+    if (((1 << ch) & eax) == 0) {
+		printk(KERN_ERR "mix ALARM %x\n", eax);
+		return 0;
+	}
+	ebp = VORTEX_MIXER_CHNBASE + (ch << 2);
+    esp18 = hwread(vortex->mmio, ebp);
+    if (esp18 & 0x10) {
+		ebx = (esp18 & 0xf);
+		if (mix == ebx) {
+			ebx = VORTEX_MIXER_RTBASE + (mix << 2);
+			edx = hwread(vortex->mmio, ebx);
+			//7b60
+			hwwrite(vortex->mmio, ebp, edx);
+			hwwrite(vortex->mmio, ebx, 0);
+		} else {
+			//7ad3
+			edx = hwread(vortex->mmio, VORTEX_MIXER_RTBASE + (ebx << 2));
+			//printk(KERN_INFO "vortex: mixdelWTD: 1 addr=%x, val=%x, src=%x\n", ebx, edx, src);
+			while ((edx & 0xf) != mix) {
+				if ((esi) > 0xf) {
+					printk(KERN_ERR "vortex: mixdelWTD: error lifeboat overflow\n");
+					return 0;
+				}
+				esp14 = ebx;
+				ebx = edx & 0xf;
+				ebp = ebx << 2;
+				edx = hwread(vortex->mmio, VORTEX_MIXER_RTBASE + ebp);
+				//printk(KERN_INFO "vortex: mixdelWTD: while addr=%x, val=%x\n", ebp, edx);
+				esi++;
+			}
+			//7b30
+			ebp = ebx << 2;
+			if (edx & 0x10) {	/* Delete entry in between others */
+				ebx = VORTEX_MIXER_RTBASE + ((edx & 0xf) << 2);
+				edx = hwread(vortex->mmio, ebx);
+				//7b60
+				hwwrite(vortex->mmio, VORTEX_MIXER_RTBASE + ebp, edx);
+				hwwrite(vortex->mmio, ebx, 0);
+				//printk(KERN_INFO "vortex mixdelWTD between addr= 0x%x, val= 0x%x\n", ebp, edx);
+			} else {			/* Delete last entry */
+				//7b83
+				if (esp14 == -1)
+					hwwrite(vortex->mmio, VORTEX_MIXER_CHNBASE + (ch << 2), esp18 & 0xef);
+				else {
+					ebx = (0xffffffe0 & edx) | (0xf & ebx);
+					hwwrite(vortex->mmio, VORTEX_MIXER_RTBASE + (esp14 << 2), ebx);
+					//printk(KERN_INFO "vortex mixdelWTD last addr= 0x%x, val= 0x%x\n", esp14, ebx);
+				}
+				hwwrite(vortex->mmio, VORTEX_MIXER_RTBASE + ebp, 0);
+				return 1;
+			}
+	    }
+    } else {
+		//printk(KERN_INFO "removed last mix\n");
+		//7be0
+		vortex_mixer_dis_sr(vortex, ch);
+		hwwrite(vortex->mmio, ebp, 0);
+    }
+    return 1;
+}
+
+unsigned int vortex_mix_boost6db(unsigned char vol) {
+    return (vol + 8);		/* WOW! what a complex function! */
+}
+
+void vortex_mix_rampvolume(vortex_t *vortex, int mix) {
+    int ch;
+    char a;
+    // This function is intended for ramping down only (see vortex_disableinput()).
+    for (ch = 0; ch < 0x20; ch++) {
+	if (((1 << ch) & rampchs[mix]) == 0)
+	    continue;
+	a = hwread(vortex->mmio,
+		   VORTEX_MIX_INVOL_B + (((mix << 5) + ch) << 2));
+	if (a > -126) {
+	    a -= 2;
+	    hwwrite(vortex->mmio,
+		    VORTEX_MIX_INVOL_A + (((mix << 5) + ch) << 2), a);
+	    hwwrite(vortex->mmio,
+		    VORTEX_MIX_INVOL_B + (((mix << 5) + ch) << 2), a);
+	} else
+	    vortex_mix_killinput(vortex, mix, ch);
+    }
+}
+
+void vortex_mixer_init(vortex_t *vortex) {
+    unsigned long addr;
+    int x;
+
+	// FIXME: get rid of this crap.
+	memset(mchannels, 0, NR_MIXOUT*sizeof(int));
+	memset(rampchs, 0, NR_MIXOUT*sizeof(int));
+
+	addr = VORTEX_MIX_U0 + 0x17c;
+    for (x = 0x5f; x >= 0; x--) {
+		hwwrite(vortex->mmio, addr, 0);
+		addr -= 4;
+    }
+	addr = VORTEX_MIX_ENIN + 0x1fc;
+    for (x = 0x7f; x >= 0; x--) {
+		hwwrite(vortex->mmio, addr, 0);
+		addr -= 4;
+    }
+	addr = VORTEX_MIX_U0 + 0x17c;
+    for (x = 0x5f; x >= 0; x--) {
+		hwwrite(vortex->mmio, addr, 0);
+		addr -= 4;
+    }
+	addr = VORTEX_MIX_INVOL_A + 0x7fc;
+    for (x = 0x1ff; x >= 0; x--) {
+		hwwrite(vortex->mmio, addr, 0x80);
+		addr -= 4;
+    }
+	addr = VORTEX_MIX_VOL_A + 0x3c;
+    for (x = 0xf; x >= 0; x--) {
+		hwwrite(vortex->mmio, addr, 0x80);
+		addr -= 4;
+    }
+	addr = VORTEX_MIX_INVOL_B + 0x7fc;
+    for (x = 0x1ff; x >= 0; x--) {
+		hwwrite(vortex->mmio, addr, 0x80);
+		addr -= 4;
+    }
+	addr = VORTEX_MIX_VOL_B + 0x3c;
+    for (x = 0xf; x >= 0; x--) {
+		hwwrite(vortex->mmio, addr, 0x80);
+		addr -= 4;
+    }
+	addr = VORTEX_MIXER_RTBASE + (MIXER_RTBASE_SIZE-1)*4;
+	for (x = (MIXER_RTBASE_SIZE-1); x >= 0; x--) {
+		hwwrite(vortex->mmio, addr, 0x0);
+		addr -= 4;
+    }
+    hwwrite(vortex->mmio, VORTEX_MIXER_SR, 0);
+    /*
+       call CAsp4Mix__Initialize_CAsp4HwIO____CAsp4Mixer____
+       Register ISR callback for volume smooth fade out.
+       Maybe this avoids clicks when press "stop" ?
+     */
+}
+
+/*  SRC (CAsp4Src.s and CAsp4SrcBlock) */
+
+void vortex_src_en_sr(vortex_t *vortex, int channel) {
+    hwwrite(vortex->mmio, VORTEX_SRCBLOCK_SR, hwread(vortex->mmio, VORTEX_SRCBLOCK_SR) | (0x1 << channel));
+}
+
+void vortex_src_dis_sr(vortex_t *vortex, int channel) {
+    hwwrite(vortex->mmio, VORTEX_SRCBLOCK_SR, hwread(vortex->mmio, VORTEX_SRCBLOCK_SR) & ~(0x1 << channel));
+}
+
+void vortex_src_flushbuffers(vortex_t *vortex, unsigned char src) {
+    int i;
+
+    for (i = 0x1f; i >= 0; i--)
+		hwwrite(vortex->mmio, VORTEX_SRC_DATA0 + (src << 7) + (i << 2), 0);
+    hwwrite(vortex->mmio, VORTEX_SRC_DATA + (src << 3), 0);
+    hwwrite(vortex->mmio, VORTEX_SRC_DATA + (src << 3) + 4, 0);
+}
+
+void vortex_src_cleardrift(vortex_t *vortex, unsigned char src) {
+    hwwrite(vortex->mmio, VORTEX_SRC_DRIFT0 + (src << 2), 0);
+    hwwrite(vortex->mmio, VORTEX_SRC_DRIFT1 + (src << 2), 0);
+    hwwrite(vortex->mmio, VORTEX_SRC_DRIFT2 + (src << 2), 1);
+}
+
+void vortex_src_slowlock(vortex_t *vortex, unsigned char src) {
+    int temp;
+
+    hwwrite(vortex->mmio, VORTEX_SRC_DRIFT2 + (src << 2), 1);
+    hwwrite(vortex->mmio, VORTEX_SRC_DRIFT0 + (src << 2), 0);
+    temp = hwread(vortex->mmio, VORTEX_SRC_U0 + (src << 2));
+    if (temp & 0x200)
+		hwwrite(vortex->mmio, VORTEX_SRC_U0 + (src << 2), temp & ~0x200L);
+}
+
+void vortex_src_set_throttlesource(vortex_t *vortex, unsigned char src, int en) {
+    int temp;
+
+    temp = hwread(vortex->mmio, VORTEX_SRC_SOURCE);
+    if (en)
+		temp |= 1 << src;
+    else
+		temp &= ~(1 << src);
+    hwwrite(vortex->mmio, VORTEX_SRC_SOURCE, temp);
+}
+
+int  vortex_src_persist_convratio(vortex_t *vortex, unsigned char src, int ratio) {
+    int temp, lifeboat = 0;
+
+    do {
+		hwwrite(vortex->mmio, VORTEX_SRC_CONVRATIO + (src << 2), ratio);
+		temp = hwread(vortex->mmio, VORTEX_SRC_CONVRATIO + (src << 2));
+		if ((++lifeboat) > 0x9) {
+            printk(KERN_ERR "Vortex: Src cvr fail\n");
+			break;
+        }
+    } while (temp != ratio);
+    return temp;
+}
+
+void vortex_src_change_convratio(vortex_t *vortex, unsigned char src, int ratio) {
+    int temp, a;
+
+    if ((ratio & 0x10000) && (ratio != 0x10000)) {
+		if (ratio & 0x3fff)
+			a = (0x11 - ((ratio >> 0xe) & 0x3)) - 1;
+		else
+			a = (0x11 - ((ratio >> 0xe) & 0x3)) - 2;
+	} else
+		a = 0xc;
+    temp = hwread(vortex->mmio, VORTEX_SRC_U0 + (src << 2));
+    if (((temp >> 4) & 0xf) != a)
+		hwwrite(vortex->mmio, VORTEX_SRC_U0 + (src << 2), (temp & 0xf) | ((a & 0xf) << 4));
+
+    vortex_src_persist_convratio(vortex, src, ratio);
+}
+
+int  vortex_src_checkratio(vortex_t *vortex, unsigned char src, unsigned int desired_ratio) {
+    int hw_ratio, lifeboat = 0;
+
+    hw_ratio = hwread(vortex->mmio, VORTEX_SRC_CONVRATIO + (src << 2));
+    
+    while (hw_ratio != desired_ratio) {
+		hwwrite(vortex->mmio, VORTEX_SRC_CONVRATIO + (src << 2), desired_ratio);
+		
+		if ((lifeboat++) > 15){
+			printk(KERN_ERR "Vortex: could not set src-%d from %d to %d\n", src, hw_ratio, desired_ratio);
+			break;
+		}
+    }
+    
+    return hw_ratio;
+}
+
+void vortex_src_setupchannel(vortex_t *card, unsigned char src, unsigned int cvr,
+    unsigned int b, int c, int d, int dirplay, int f, unsigned int g, int thsource) {
+    // noplayback: d=2,4,7,0xa,0xb when using first 2 src's.
+	// c: enables pitch sweep.
+	// looks like g is c related. Maybe g is a sweep parameter ?
+	// g = cvr
+	// dirplay: 0 = recording, 1 = playback
+	// d = src hw index.
+		
+    int esi, ebp=0, esp10;
+
+    vortex_src_flushbuffers(card, src);
+
+    if (c) {
+		if ((g & 0x10000) && (g != 0x10000)) {
+			g = 0;
+			esi = 0x7;
+		} else {
+			if ((((short)g) < 0) && (g != 0x8000)) {
+				g = 0;
+				esi = 0x8;
+			} else {
+				g = 1;
+				esi = 0xc;
+			}
+		}
+    } else {
+		if ((cvr & 0x10000) && (cvr != 0x10000)) {
+			g = 0; /*ebx = 0*/
+			esi = 0x11 - ((cvr >> 0xe) & 7);
+			if (cvr & 0x3fff)
+				esi -= 1;
+			else
+				esi -= 2;
+		} else {
+			g = 1;
+			esi = 0xc;
+		}
+    }
+    vortex_src_cleardrift(card, src);
+    vortex_src_set_throttlesource(card, src, thsource);
+
+    if ((dirplay == 0)&&(c == 0)) {
+        if (g)
+            esp10 = 0xf;
+        else
+            esp10 = 0xc;
+        ebp = 0;
+    } else {
+    	if (g)
+		    ebp = 0xf;
+		else
+    		ebp = 0xc;
+		esp10 = 0;       
+    }        
+	hwwrite(card->mmio, VORTEX_SRC_U0 + (src << 2), (f << 0x9) | (c << 0x8) | ((esi&0xf)<<4) | d);
+    /* 0xc0   esi=0xc c=f=0 d=0*/
+	vortex_src_persist_convratio(card, src, cvr);
+    hwwrite(card->mmio, VORTEX_SRC_U1 + (src << 2), b & 0xffff);
+	/* 0   b=0 */
+    hwwrite(card->mmio, VORTEX_SRC_U3 + (src << 2), (g << 0x11) | (dirplay << 0x10) | (ebp << 0x8) | esp10);
+	/* 0x30f00 e=g=1 esp10=0 ebp=f */
+	//printk(KERN_INFO "vortex: SRC %d, d=0x%x, esi=0x%x, esp10=0x%x, ebp=0x%x\n", src, d, esi, esp10, ebp);
+}
+
+void vortex_srcblock_init(vortex_t *vortex) {
+    unsigned long addr;
+    int x;
+    hwwrite(vortex->mmio, VORTEX_SRC_SOURCE+4, 0x1ff);
+    /*
+    for (x=0; x<0x10; x++) {
+		vortex_src_init(&vortex_src[x], x);
+    }
+    */
+    //addr = 0xcc3c;
+	//addr = 0x26c3c;
+	addr = VORTEX_SRC_RTBASE + 0x3c;
+    for (x = 0xf; x >= 0; x--) {
+		hwwrite(vortex->mmio, addr, 0);
+		addr -= 4;
+    }
+    //addr = 0xcc94;
+	//addr = 0x26c94;
+	addr = VORTEX_SRC_CHNBASE + 0x54;
+    for (x = 0x15; x >= 0; x--) {
+		hwwrite(vortex->mmio, addr, 0);
+		addr -= 4;
+    }
+}
+
+int  vortex_src_addWTD(vortex_t *vortex, unsigned char src, unsigned char ch) {
+    int temp, lifeboat = 0, prev;
+	// esp13 = src
+
+    temp = hwread(vortex->mmio, VORTEX_SRCBLOCK_SR);
+    if ((temp & (1 << ch)) == 0) {
+    	hwwrite(vortex->mmio, VORTEX_SRC_CHNBASE + (ch << 2), src);
+    	vortex_src_en_sr(vortex, ch);
+    	return 1;
+    }
+    prev = VORTEX_SRC_CHNBASE + (ch << 2); /*ebp*/
+    temp = hwread(vortex->mmio, prev);
+    //while (temp & NR_SRC) {
+	while (temp & 0x10) {
+	    prev = VORTEX_SRC_RTBASE + ((temp & 0xf) << 2); /*esp12*/
+		//prev = VORTEX_SRC_RTBASE + ((temp & (NR_SRC-1)) << 2); /*esp12*/
+	    temp = hwread(vortex->mmio, prev);
+		//printk(KERN_INFO "vortex: srcAddWTD: while addr=%x, val=%x\n", prev, temp);
+	    if ((++lifeboat) > 0xf) {
+    	    printk(KERN_ERR "vortex_src_addWTD: lifeboat overflow\n");
+	        return 0;
+	    }
+    }
+    hwwrite(vortex->mmio, VORTEX_SRC_RTBASE + ((temp & 0xf) << 2), src);
+    //hwwrite(vortex->mmio, prev, (temp & (NR_SRC-1)) | NR_SRC);
+	hwwrite(vortex->mmio, prev, (temp & 0xf) | 0x10);
+    return 1;
+}
+
+int  vortex_src_delWTD(vortex_t *vortex, unsigned char src, unsigned char ch) {
+	int esp14=-1, esp18, eax, ebx, edx, ebp, esi=0;
+	//int esp1f=edi(while)=src, esp10=ch;
+
+    eax = hwread(vortex->mmio, VORTEX_SRCBLOCK_SR);
+    if (((1 << ch) & eax) == 0) {
+		printk(KERN_ERR "src alarm\n");
+		return 0;
+	}
+	ebp = VORTEX_SRC_CHNBASE + (ch << 2);
+    esp18 = hwread(vortex->mmio, ebp);
+    if (esp18 & 0x10) {
+		ebx = (esp18 & 0xf);
+		if (src == ebx) {
+			ebx = VORTEX_SRC_RTBASE + (src << 2);
+			edx = hwread(vortex->mmio, ebx);
+			//7b60
+			hwwrite(vortex->mmio, ebp, edx);
+			hwwrite(vortex->mmio, ebx, 0);
+		} else {
+			//7ad3
+			edx = hwread(vortex->mmio, VORTEX_SRC_RTBASE + (ebx << 2));
+			//printk(KERN_INFO "vortex: srcdelWTD: 1 addr=%x, val=%x, src=%x\n", ebx, edx, src);
+			while ((edx & 0xf) != src) {
+				if ((esi) > 0xf) {
+					printk("vortex: srcdelWTD: error, lifeboat overflow\n");
+					return 0;
+				}
+				esp14 = ebx;
+				ebx = edx & 0xf;
+				ebp = ebx << 2;
+				edx = hwread(vortex->mmio, VORTEX_SRC_RTBASE + ebp);
+				//printk(KERN_INFO "vortex: srcdelWTD: while addr=%x, val=%x\n", ebp, edx);
+				esi++;
+			}
+			//7b30
+			ebp = ebx << 2;
+			if (edx & 0x10) {	/* Delete entry in between others */
+				ebx = VORTEX_SRC_RTBASE + ((edx & 0xf) << 2);
+				edx = hwread(vortex->mmio, ebx);
+				//7b60
+				hwwrite(vortex->mmio, VORTEX_SRC_RTBASE + ebp, edx);
+				hwwrite(vortex->mmio, ebx, 0);
+				//printk(KERN_INFO "vortex srcdelWTD between addr= 0x%x, val= 0x%x\n", ebp, edx);
+			} else {			/* Delete last entry */
+				//7b83
+				if (esp14 == -1)
+					hwwrite(vortex->mmio, VORTEX_SRC_CHNBASE + (ch << 2), esp18 & 0xef);
+				else {
+					ebx = (0xffffffe0 & edx) | (0xf & ebx);
+					hwwrite(vortex->mmio, VORTEX_SRC_RTBASE + (esp14 << 2), ebx);
+					//printk(KERN_INFO"vortex srcdelWTD last addr= 0x%x, val= 0x%x\n", esp14, ebx);
+				}
+				hwwrite(vortex->mmio, VORTEX_SRC_RTBASE + ebp, 0);
+				return 1;
+			}
+	    }
+    } else {
+		//7be0
+		vortex_src_dis_sr(vortex, ch);
+		hwwrite(vortex->mmio, ebp, 0);
+    }
+    return 1;
+}
+
+ /*FIFO*/
+
+void vortex_fifo_clearadbdata(vortex_t *vortex, int fifo, int x) {
+	for (x--; x >= 0; x--)
+		hwwrite(vortex->mmio, VORTEX_FIFO_ADBDATA + (((fifo << FIFO_SIZE_BITS) + x) << 2), 0);
+}
+
+void vortex_fifo_clearwtdata(vortex_t *vortex, int fifo, int x) {
+	if (x < 1)
+		return;
+	for (x--; x >= 0; x--)
+		hwwrite(vortex->mmio, VORTEX_FIFO_WTDATA + (((fifo << FIFO_SIZE_BITS) + x) << 2), 0);
+}
+
+void vortex_fifo_init(vortex_t *vortex) {
+    int x;
+    unsigned long addr;
+
+    /* ADB DMA channels fifos. */
+	addr = VORTEX_FIFO_ADBCTRL + ((NR_ADB-1)*4);
+    for (x = NR_ADB-1; x >= 0; x--) {
+	    hwwrite(vortex->mmio, addr, (FIFO_U0 | FIFO_U1));
+	    if (hwread(vortex->mmio, addr) != (FIFO_U0 | FIFO_U1))
+	        printk(KERN_ERR "bad adb fifo reset!");
+	    vortex_fifo_clearadbdata(vortex, x, FIFO_SIZE);
+	    addr -= 4;
+    }
+
+#ifndef CHIP_AU8810
+    /* WT DMA channels fifos. */
+	addr = VORTEX_FIFO_WTCTRL + ((NR_WT-1)*4);
+    for (x = NR_WT-1; x >= 0; x--) {
+	    hwwrite(vortex->mmio, addr, FIFO_U0);
+	    if (hwread(vortex->mmio, addr) != FIFO_U0)
+    	    printk(KERN_ERR "bad wt fifo reset (0x%08lx, 0x%08x)!\n", addr, hwread(vortex->mmio, addr));
+	    vortex_fifo_clearwtdata(vortex, x, FIFO_SIZE);
+	    addr -= 4;
+    }
+#endif
+    /* below is weird stuff... */
+#ifndef CHIP_AU8820
+    hwwrite(vortex->mmio, 0xf8c0, 0xd03); //0x0843 0xd6b
+#else
+	hwwrite(vortex->mmio, 0x17000, 0x61);
+	hwwrite(vortex->mmio, 0x17004, 0x61);
+	hwwrite(vortex->mmio, 0x17008, 0x61);
+#endif
+}
+
+void vortex_fifo_adbinitialize(vortex_t *vortex, int fifo, int j) {
+    vortex_fifo_clearadbdata(vortex, fifo, FIFO_SIZE);
+#ifdef CHIP_AU8820
+    hwwrite(vortex->mmio, VORTEX_FIFO_ADBCTRL + (fifo << 2), (FIFO_U1 | ((j & FIFO_MASK) << 0xb)));
+#else
+	hwwrite(vortex->mmio, VORTEX_FIFO_ADBCTRL + (fifo << 2), (FIFO_U1 | ((j & FIFO_MASK) << 0xc)));
+#endif
+}
+
+void vortex_fifo_wtinitialize(vortex_t *vortex, int fifo, int j) {
+    vortex_fifo_clearwtdata(vortex, fifo, FIFO_SIZE);
+#ifdef CHIP_AU8820
+    hwwrite(vortex->mmio, VORTEX_FIFO_WTCTRL + (fifo << 2), (FIFO_U1 | ((j & FIFO_MASK) << 0xb)));
+#else
+	hwwrite(vortex->mmio, VORTEX_FIFO_WTCTRL + (fifo << 2), (FIFO_U1 | ((j & FIFO_MASK) << 0xc)));
+#endif
+}
+
+void vortex_fifo_setadbvalid(vortex_t *vortex, int fifo, int en) {
+    hwwrite(vortex->mmio, VORTEX_FIFO_ADBCTRL + (fifo << 2), (hwread(vortex->mmio,
+		VORTEX_FIFO_ADBCTRL + (fifo << 2)) & 0xffffffef) | ((1 & en) << 4) | FIFO_U1);
+}
+
+void vortex_fifo_setwtvalid(vortex_t *vortex, int fifo, int en) {
+    hwwrite(vortex->mmio, VORTEX_FIFO_WTCTRL + (fifo << 2), (hwread(vortex->mmio,
+		VORTEX_FIFO_WTCTRL + (fifo << 2)) & 0xffffffef) | ((en & 1) << 4) | FIFO_U1);
+}
+
+void vortex_fifo_setadbctrl(vortex_t *vortex, int fifo, int b, int priority, int empty, int valid, int f) {
+    int temp, lifeboat=0;
+    //int this_8[NR_ADB] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; /* position */
+	int	this_4=0x2;
+	/* f seems priority related.
+	 * CAsp4AdbDma::SetPriority is the only place that calls SetAdbCtrl with f set to 1
+	 * every where else it is set to 0. It seems, however, that CAsp4AdbDma::SetPriority
+	 * is never called, thus the f related bits remain a mystery for now.
+	 */
+    do {
+		temp = hwread(vortex->mmio, VORTEX_FIFO_ADBCTRL + (fifo << 2));
+		if (lifeboat++ > 0xbb8) {
+			printk(KERN_ERR "Vortex: vortex_fifo_setadbctrl fail\n");
+			break;
+		}
+    } while (temp & FIFO_RDONLY);
+
+	// AU8830 semes to take some special care about fifo content (data).
+	// But i'm just to lazy to translate that :)
+    if (valid) {
+		if ((temp & FIFO_VALID) == 0) {
+			//this_8[fifo] = 0;
+			vortex_fifo_clearadbdata(vortex, fifo, FIFO_SIZE); // this_4
+#ifdef CHIP_AU8820
+			temp = (this_4 & 0x1f) << 0xb;
+#else
+			temp = (this_4 & 0x3f) << 0xc;
+#endif
+			temp = (temp & 0xfffffffd) | ((b & 1) << 1);
+			temp = (temp & 0xfffffff3) | ((priority & 3) << 2);
+			temp = (temp & 0xffffffef) | ((valid & 1) << 4);
+			temp |= FIFO_U1;
+			temp = (temp & 0xffffffdf) | ((empty & 1) << 5);
+#ifdef CHIP_AU8820
+			temp = (temp & 0xfffbffff) | ((f & 1) << 0x12);
+#endif
+#ifdef CHIP_AU8830
+			temp = (temp & 0xf7ffffff) | ((f & 1) << 0x1b);
+			temp = (temp & 0xefffffff) | ((f & 1) << 0x1c);
+#endif
+#ifdef CHIP_AU8810
+			temp = (temp & 0xfeffffff) | ((f & 1) << 0x18);
+			temp = (temp & 0xfdffffff) | ((f & 1) << 0x19);
+#endif
+		}
+    } else {
+		if (temp & FIFO_VALID) {
+#ifdef CHIP_AU8820
+			temp = ((f & 1) << 0x12) | (temp & 0xfffbffef);
+#endif
+#ifdef CHIP_AU8830
+			temp = ((f & 1) << 0x1b) | (temp & 0xe7ffffef) | FIFO_BITS;
+#endif
+#ifdef CHIP_AU8810
+			temp = ((f & 1) << 0x1b) | (temp & 0xfcffffef) | FIFO_BITS;
+#endif			
+		} else
+			/*if (this_8[fifo])*/ vortex_fifo_clearadbdata(vortex, fifo, FIFO_SIZE);
+	}
+	hwwrite(vortex->mmio, VORTEX_FIFO_ADBCTRL + (fifo << 2), temp);
+	hwread(vortex->mmio, VORTEX_FIFO_ADBCTRL + (fifo << 2));
+}
+
+void vortex_fifo_setwtctrl(vortex_t *vortex, int fifo, int b, int priority, int empty, int valid, int f) {
+	int temp=0;
+	int this_4=2;
+
+	if (valid) {
+		vortex_fifo_clearwtdata(vortex, fifo, 0x20);
+		temp = (this_4 & 0x1f) << 0xb;
+	}
+	temp = (temp & 0xfffffffd) | ((b & 1) << 1);
+	temp = (temp & 0xfffdffff) | ((f & 1) << 0x11);
+	temp = (temp & 0xfffffff3) | ((priority & 3) << 2);
+	temp = (temp & 0xffffffef) | ((valid & 1) << 4);
+	temp = (temp & 0xffffffdf) | ((empty & 1) << 5);
+#ifdef FIFO_BITS
+	temp |= FIFO_BITS;
+#endif
+	hwwrite(vortex->mmio, VORTEX_FIFO_WTCTRL + (fifo << 2), temp);
+}
+
+/* ADBDMA */
+
+void vortex_adbdma_init(vortex_t *vortex) {
+}
+
+void vortex_adbdma_setupbuffer(vortex_t *vortex, int adbdma, int sb, u32 addr, u32 size, int sb_next, int c) {
+    // if "c" is 0, then DMA stops when reaching that buffer.
+
+    int offset, shift, mask;
+    stream_t *dma = &vortex->dma_adb[adbdma];
+    int *cfg;
+
+    if (sb_next == -1) {
+        sb_next = sb;
+        if (dma->dma_ctrl & IE_MASK)
+            c = 0;
+    }
+
+    if ((sb >= 0)&&(sb < 4)) {
+        if (sb & 1) {
+            // subbuffer 1 and 3
+            shift = 0x0;
+            mask = 0xf8fff000;
+        } else {
+            // subbuffer 0 and 2
+            shift = 0xc;
+            mask = 0x8f000fff;
+        }
+        if (sb & 2) {
+            // subbuffer 2 and 3
+            cfg = &dma->cfg1;
+            offset = 4;
+        } else {
+            // subbuffer 0 and 1
+            cfg = &dma->cfg0;
+            offset = 0;
+        }
+        *cfg = (*cfg & (mask|0xffffff)) | ((((sb_next & 3)<<4) | ((c&1)<<6))<<24) | 0x80;
+        if (size)
+           *cfg = (*cfg & (mask|0xff000000)) | (((size-1) & 0xfff) << shift);
+        hwwrite(vortex->mmio, VORTEX_ADBDMA_BUFCFG0 + offset + (adbdma << 3), *cfg);
+    }
+    hwwrite(vortex->mmio, VORTEX_ADBDMA_BUFBASE + (((adbdma<<2)+sb)<<2), addr);
+}
+
+void vortex_adbdma_setfirstbuffer(vortex_t *vortex, int adbdma) {
+    stream_t *dma = &vortex->dma_adb[adbdma];
+
+    hwwrite(vortex->mmio, VORTEX_ADBDMA_CTRL + (adbdma << 2), dma->dma_ctrl);
+}
+
+void vortex_adbdma_setstartbuffer(vortex_t *vortex, int adbdma, int sb) {
+    stream_t *dma = &vortex->dma_adb[adbdma];
+	hwwrite(vortex->mmio, VORTEX_ADBDMA_START + (adbdma << 2), (sb << ((NR_ADB-adbdma)*2)));
+	dma->period_real = dma->period_virt = sb;
+}
+
+void vortex_adbdma_setbuffers(vortex_t *vortex, int adbdma, unsigned int addr, int size, int count) {
+	stream_t *dma = &vortex->dma_adb[adbdma];
+
+	dma->period_bytes = size;
+	dma->nr_periods = count;
+	dma->buf_addr = addr;
+
+	// Adb Buffers. Simple linear buffer.
+	hwwrite(vortex->mmio, VORTEX_ADBDMA_BUFBASE + (adbdma << 4), addr);
+	hwwrite(vortex->mmio, VORTEX_ADBDMA_BUFBASE + 0x4 + (adbdma << 4), addr + size);
+	hwwrite(vortex->mmio, VORTEX_ADBDMA_BUFBASE + 0x8 + (adbdma << 4), addr + (size*2));
+	hwwrite(vortex->mmio, VORTEX_ADBDMA_BUFBASE + 0xc + (adbdma << 4), addr + (size*3));
+	
+	size--;
+	switch (count) {
+	case 1:
+		dma->cfg0 = 0x80000000 | 0x40000000 | 0x00000000 | (size << 0xc);
+		dma->cfg1 = 0x00000000 | 0x00000000 | 0x00000000;
+		break;
+	case 2:
+		dma->cfg0 = 0x88000000 | 0x44000000 | 0x10000000 | (size << 0xc) | size;
+		dma->cfg1 = 0x00000000 | 0x00000000 | 0x00000000;
+		break;
+	case 3:
+		dma->cfg0 = 0x88000000 | 0x44000000 | 0x12000000 | (size << 0xc) | size;
+		dma->cfg1 = 0x80000000 | 0x40000000 | 0x00000000 | (size << 0xc);
+		break;
+	case 4:
+	default:
+		dma->cfg0 = 0x88000000 | 0x44000000 | 0x12000000 | (size << 0xc) | size;
+		dma->cfg1 = 0x88000000 | 0x44000000 | 0x30000000 | (size << 0xc) | size;
+		break;
+	}	
+	hwwrite(vortex->mmio, VORTEX_ADBDMA_BUFCFG0 + (adbdma << 3), dma->cfg0);
+	hwwrite(vortex->mmio, VORTEX_ADBDMA_BUFCFG1 + (adbdma << 3), dma->cfg1);
+
+	vortex_adbdma_setfirstbuffer(vortex, adbdma);
+	vortex_adbdma_setstartbuffer(vortex, adbdma, 0);
+}
+
+void vortex_adbdma_setmode(vortex_t *vortex, int adbdma, int ie, int dir, int fmt, int d, unsigned long offset) {
+    stream_t *dma = &vortex->dma_adb[adbdma];
+    int temp;
+
+    dma->dma_unknown = d;
+    dma->dma_ctrl = ((offset & OFFSET_MASK) | (dma->dma_ctrl & ~OFFSET_MASK));
+    /* Enable PCMOUT interrupts. */
+    dma->dma_ctrl = (dma->dma_ctrl & ~IE_MASK) | ((ie << IE_SHIFT) & IE_MASK);
+
+    dma->dma_ctrl = (dma->dma_ctrl & ~U_MASK) | ((dir << U_SHIFT) & U_MASK);
+    dma->dma_ctrl = (dma->dma_ctrl & ~FMT_MASK) | ((fmt << FMT_SHIFT) & FMT_MASK);
+
+    hwwrite(vortex->mmio, VORTEX_ADBDMA_CTRL + (adbdma << 2), dma->dma_ctrl);
+    hwread(vortex->mmio, VORTEX_ADBDMA_CTRL + (adbdma << 2));
+}
+
+int  vortex_adbdma_bufshift(vortex_t *vortex, int adbdma) {
+	stream_t *dma = &vortex->dma_adb[adbdma];
+	int page, p, pp, delta, i;
+	
+ 	page = (hwread(vortex->mmio, VORTEX_ADBDMA_STAT + (adbdma << 2)) & ADB_SUBBUF_MASK) >> ADB_SUBBUF_SHIFT;
+	if (dma->nr_periods >= 4)
+		delta = (page - dma->period_real) & 3;
+	else {
+		delta = (page - dma->period_real);
+		if (delta < 0)
+			delta += dma->nr_periods;
+        }
+	if (delta == 0)
+		goto _exit;
+#ifdef DEBUG
+        dprintf(("delta: %x, page: %x, nr_per: %x, per r: %x, per v:  %x",delta, page, dma->nr_periods,dma->period_real,dma->period_virt));
+#endif
+	/* refresh hw page table */
+	if (dma->nr_periods > 4) {
+		for (i=0 ; i < delta; i++) {
+			/* p: audio buffer page index */
+			p = dma->period_virt + i + 4;
+			if (p >= dma->nr_periods)
+				p -= dma->nr_periods;
+			/* pp: hardware DMA page index. */
+			pp = dma->period_real + i;
+			if (pp >= 4)
+				pp -= 4;
+#ifdef DEBUG
+                        dprintf(("p: %x, pp: %x",p, pp));
+#endif
+			hwwrite(vortex->mmio, VORTEX_ADBDMA_BUFBASE+(((adbdma << 2)+pp) << 2), dma->buf_addr+((dma->period_bytes)*p));
+			/* Force write thru cache. */
+			hwread(vortex->mmio, VORTEX_ADBDMA_BUFBASE + (((adbdma << 2)+pp) << 2));
+		}
+	}
+	dma->period_virt += delta;
+	dma->period_real = page;
+	if (dma->period_virt >= dma->nr_periods)
+            dma->period_virt -= dma->nr_periods;
+    _exit:
+#ifdef DEBUG
+	if ((delta != 1) || (delta < 0))
+            printk(KERN_INFO "vortex: % d virt=0x%d, real=0x%x, delta = %d \n", adbdma, dma->period_virt, dma->period_real, delta );
+#endif
+	return delta;
+}
+
+void vortex_adbdma_setctrl(vortex_t *vortex, int a, int b, int c, int d, int e, int f) {
+    //FIXME
+}
+
+void vortex_adbdma_getposition(vortex_t *vortex, int adbdma, int *subbuf, int *pos) {
+    int temp, edi, eax, ecx, esp10, edx;
+    stream_t *dma = &vortex->dma_adb[adbdma];
+
+    // dma->cfg0: this_7c,  dma->cfg1: this_80.
+
+    // Looks like several hardware bug workarounds are in here.
+    // The most weird function. What a nightmare.
+    do {
+        // Potential lockup, no lifeboat.
+	    temp = hwread(vortex->mmio, VORTEX_ADBDMA_STAT + (adbdma << 2));
+        if (temp == -1) {
+		    // 3ae1
+            //*subbuf = this_88;
+            //*pos = this_84;
+		    return;
+        }
+        esp10 = adbdma;
+		if ((esp10 == ((temp >> 0x13) & 0xf)) && ((char)((temp >> 0x10) & 0xff) < 0))
+            continue;
+        edx = temp >> 0x18;
+        if ((edx & 0xf) != adbdma)
+		    break;
+    } while (edx | 0x10);
+
+    ecx = hwread(vortex->mmio, VORTEX_FIFO_ADBCTRL + (adbdma << 2));
+    *subbuf = (ecx >> 0xc) & 3;
+
+    switch (*subbuf) {
+        case 0:
+            edi = (dma->cfg0 >> 0xc) & 0xfff;
+            eax = (dma->cfg0 >> 24) & 0xff;
+            break;
+        case 1:
+            edi = dma->cfg0 & 0xfff;
+            eax = (dma->cfg0 >> 24) & 0xff;
+            break;
+        case 2:
+            edi = (dma->cfg1 >> 0xc) & 0xfff;
+            eax = (dma->cfg1 >> 24) & 0xff;
+            break;
+        case 3:
+            edi = dma->cfg1 & 0xfff;
+            eax = (dma->cfg1 >> 24) & 0xff;
+            break;
+        default:
+            return;
+    }
+    edi++;
+    if (dma->fifo_enabled == 0)
+	    *subbuf = 0;
+    else {
+        esp10 = dma->dma_unknown;
+        temp = (temp & 0xfff83fff) | ((((temp >> 0xe) & 0x1f) & ~esp10) << 0xe);
+        if (dma->dma_ctrl & 0x2000) {
+            if (esp10) {
+                if ((dma->dma_ctrl & 0x3c000) == 0x20000) /* if stereo */
+                    eax = (0x1c - ((temp >> 0xe) & 0x1f))*2;
+                else
+                    eax = 0x1c - ((temp >> 0xe) & 0x1f);
+            } else {
+                if ((dma->dma_ctrl & 0x3c000) == 0x20000) /* if stereo */
+                    eax = ((~(temp >> 0xe)) & 0x1f)*2;
+                else
+                    eax = (~(temp >> 0xe)) & 0x1f;
+            }
+            if (((ecx & 0x10) == 0) && (dma->fifo_status != FIFO_PAUSE))
+                eax = edi - eax;
+            else
+                eax = (temp & 0xfff) - eax;
+            if (edi > eax)
+                eax = edi;
+        } else {
+            // 3a87
+            if ((dma->dma_ctrl & 0x3c000) == 0x20000) /* if stereo */
+                eax = (temp >>  0xd) & 0x3e;
+            else
+                eax = (temp >> 0xe) & 0x1f;
+        }
+        // 3aa8
+        edi--;
+        if ((ecx & 0x10) == 0) {
+            if (dma->fifo_status == FIFO_PAUSE) {
+                edx = (temp & 0xfff) + eax;
+                eax = edi + 1;
+                if (eax < edx)
+                    eax = edx;
+            } else
+                eax = ((ecx >> 5) & 1) + edi;
+        } else {
+             // 3ad3
+             eax += temp & 0xfff;
+             if (edi >= eax)
+                 eax = edi; // 3b00
+        }
+    }
+    // 3b02
+    *subbuf = eax;
+    // 3b08
+    //this_84 = *pos;
+    //this_88 = *subbuf;
+}
+
+int  vortex_adbdma_getcursubuffer(vortex_t *vortex, int adbdma) {
+    return((hwread(vortex->mmio, VORTEX_ADBDMA_STAT + (adbdma << 2)) & ADB_SUBBUF_MASK) >> ADB_SUBBUF_SHIFT);
+}
+
+void vortex_adbdma_startfifo(vortex_t *vortex, int adbdma) {
+    int this_8=0/*empty*/, this_4=0/*priority*/;
+	stream_t *dma = &vortex->dma_adb[adbdma];
+
+    switch (dma->fifo_status) {
+        case FIFO_START:
+            vortex_fifo_setadbvalid(vortex, adbdma, dma->fifo_enabled ? 0xff:0);
+            break;
+        case FIFO_STOP:
+			this_8 = 1;
+            hwwrite(vortex->mmio, VORTEX_ADBDMA_CTRL + (adbdma << 2), dma->dma_ctrl);
+            vortex_fifo_setadbctrl(vortex, adbdma, dma->dma_unknown, this_4,
+				this_8, dma->fifo_enabled ? 0xff:0, 0);
+            break;
+        case FIFO_PAUSE:
+            vortex_fifo_setadbctrl(vortex, adbdma, dma->dma_unknown, this_4,
+				this_8, dma->fifo_enabled ? 0xff:0, 0);
+            break;
+    }
+    dma->fifo_status = FIFO_START;
+}
+
+void vortex_adbdma_resumefifo(vortex_t *vortex, int adbdma) {
+	stream_t *dma = &vortex->dma_adb[adbdma];
+
+    int this_8=1, this_4=0;
+    switch (dma->fifo_status) {
+        case FIFO_STOP:
+            hwwrite(vortex->mmio, VORTEX_ADBDMA_CTRL + (adbdma << 2), dma->dma_ctrl);
+            vortex_fifo_setadbctrl(vortex, adbdma, dma->dma_unknown, this_4, this_8,
+				dma->fifo_enabled ? 0xff:0, 0);
+            break;
+        case FIFO_PAUSE:
+            vortex_fifo_setadbctrl(vortex, adbdma, dma->dma_unknown, this_4, this_8,
+				dma->fifo_enabled ? 0xff:0, 0);
+            break;
+    }
+   	dma->fifo_status = FIFO_START;
+}
+
+void vortex_adbdma_pausefifo(vortex_t *vortex, int adbdma) {
+	stream_t *dma = &vortex->dma_adb[adbdma];
+
+    int this_8=0, this_4=0;
+    switch (dma->fifo_status) {
+        case FIFO_START:
+            vortex_fifo_setadbctrl(vortex, adbdma, dma->dma_unknown, this_4, this_8, 0, 0);
+            break;
+        case FIFO_STOP:
+            hwwrite(vortex->mmio, VORTEX_ADBDMA_CTRL + (adbdma << 2), dma->dma_ctrl);
+            vortex_fifo_setadbctrl(vortex, adbdma, dma->dma_unknown, this_4, this_8, 0, 0);
+            break;
+    }
+    dma->fifo_status = FIFO_PAUSE;
+}
+
+void vortex_adbdma_stopfifo(vortex_t *vortex, int adbdma) {
+	stream_t *dma = &vortex->dma_adb[adbdma];
+
+    int this_4=0, this_8=0;
+    if (dma->fifo_status == FIFO_START)
+        vortex_fifo_setadbctrl(vortex, adbdma, dma->dma_unknown, this_4, this_8, 0, 0);
+    else if (dma->fifo_status == FIFO_STOP)
+        return;
+    dma->fifo_status = FIFO_STOP;
+    dma->fifo_enabled = 0;
+}
+
+/* WTDMA */
+#ifndef CHIP_AU8810
+void vortex_wtdma_init(vortex_t *vortex) {
+}
+
+void vortex_wtdma_setupbuffer(vortex_t *vortex, int wtdma, int sb, u32 addr, u32 size, int sb_next, int c) {
+    int offset, shift, mask;
+    stream_t *dma = &vortex->dma_wt[wtdma];
+    int *cfg;
+
+    if (sb_next == -1) {
+        sb_next = sb;
+        if (dma->dma_ctrl & IE_MASK)
+            c = 0;
+    }
+
+    if ((sb >= 0)&&(sb < 4)) {
+        if (sb & 1) {
+            /* subbuffer 1 and 3 */
+            shift = 0x0;
+            mask = 0xf8fff000;
+        } else {
+            /* subbuffer 0 and 2 */
+            shift = 0xc;
+            mask = 0x8f000fff;
+        }
+        if (sb & 2) {
+            /* subbuffer 2 and 3 */
+            cfg = &dma->cfg1;
+            offset = 4;
+        } else {
+            /* subbuffer 0 and 1 */
+            cfg = &dma->cfg0;
+            offset = 0;
+        }
+        *cfg = (*cfg & (mask|0xffffff)) | ((((sb_next & 3)<<4) | ((c&1)<<6))<<24) | 0x80;
+        if (size)
+           *cfg = (*cfg & (mask|0xff000000)) | (((size-1) & 0xfff) << shift);
+        hwwrite(vortex->mmio, VORTEX_WTDMA_BUFCFG0 + offset + (wtdma << 3), *cfg);
+    }
+    hwwrite(vortex->mmio, VORTEX_WTDMA_BUFBASE + (((wtdma<<2)+sb)<<2), addr);
+}
+
+void vortex_wtdma_setfirstbuffer(vortex_t *vortex, int wtdma) {
+    //int this_7c=dma_ctrl;
+    stream_t *dma = &vortex->dma_wt[wtdma];
+
+    hwwrite(vortex->mmio, VORTEX_WTDMA_CTRL + (wtdma << 2), dma->dma_ctrl);
+}
+
+void vortex_wtdma_setstartbuffer(vortex_t *vortex, int wtdma, int sb) {
+	stream_t *dma = &vortex->dma_wt[wtdma];
+    hwwrite(vortex->mmio, VORTEX_WTDMA_START + (wtdma << 2), (sb << ((~wtdma)&0xf)*2));
+	dma->period_real = dma->period_virt = sb;
+}
+
+void vortex_wtdma_setbuffers(vortex_t *vortex, int wtdma, unsigned int addr, int size, int count) {
+    stream_t *dma = &vortex->dma_wt[wtdma];
+
+	/* Wt buffer. */
+	hwwrite(vortex->mmio, VORTEX_WTDMA_BUFBASE + (wtdma << 4), addr);
+	hwwrite(vortex->mmio, VORTEX_WTDMA_BUFBASE + 0x4 + (wtdma << 4), addr + size);
+	hwwrite(vortex->mmio, VORTEX_WTDMA_BUFBASE + 0x8 + (wtdma << 4), addr + (size*2));
+	hwwrite(vortex->mmio, VORTEX_WTDMA_BUFBASE + 0xc + (wtdma << 4), addr + (size*3));
+
+	switch (count) {
+        	case 1:
+			dma->cfg0 = 0xce000000;
+			dma->cfg1 = 0xfc000000;
+			break;
+		case 2:
+			dma->cfg0 = 0xdc000000;
+			dma->cfg1 = 0xfc000000;
+			break;
+		case 3:
+			dma->cfg0 = 0xde000000;
+			dma->cfg1 = 0xcc000000;
+			break;
+		case 4:
+		default:
+			dma->cfg0 = 0xde000000;
+			dma->cfg1 = 0xfc000000;
+			break;
+	}
+	size--;
+	dma->cfg0 |= (size << 0xc) | size;
+	dma->cfg1 |= (size << 0xc) | size;
+	//printk(KERN_INFO "vortex: wt buffer cfg: 0x%8x  0x%8x\n", dma->cfg0, dma->cfg1);
+	hwwrite(vortex->mmio, VORTEX_WTDMA_BUFCFG0 + (wtdma << 3), dma->cfg0);
+	hwwrite(vortex->mmio, VORTEX_WTDMA_BUFCFG1 + (wtdma << 3), dma->cfg1);
+
+	vortex_wtdma_setfirstbuffer(vortex, wtdma);
+	vortex_wtdma_setstartbuffer(vortex, wtdma, 0);
+}
+
+void vortex_wtdma_setmode(vortex_t *vortex, int wtdma, int ie, int dir, int fmt, int d, unsigned long offset) {
+	stream_t *dma = &vortex->dma_wt[wtdma];
+
+    dma->dma_unknown = d;
+    dma->dma_ctrl = ((offset & OFFSET_MASK) | (dma->dma_ctrl & ~OFFSET_MASK));
+    /* PCMOUT interrupt */
+    dma->dma_ctrl = (dma->dma_ctrl & ~IE_MASK) | ((ie << IE_SHIFT) & IE_MASK);
+
+    dma->dma_ctrl = (dma->dma_ctrl & U_MASK) | ((dir << U_SHIFT) & U_MASK);
+    dma->dma_ctrl = (dma->dma_ctrl & FMT_MASK) | ((fmt << FMT_SHIFT) & FMT_MASK);
+
+    hwwrite(vortex->mmio, VORTEX_WTDMA_CTRL + (wtdma << 2), dma->dma_ctrl);
+}
+
+int  vortex_wtdma_bufshift(vortex_t *vortex, int wtdma) {
+	stream_t *dma = &vortex->dma_wt[wtdma];
+	int page, p, delta, i;
+
+	page = (hwread(vortex->mmio, VORTEX_WTDMA_STAT + (wtdma << 2)) >> 0xc) & 0x3;
+	delta = (page - dma->period_real) & 3;
+
+	/* refresh hw page table */
+	if (dma->nr_periods > 4) {
+		for (i=0 ; i < delta; i++) {
+			p = dma->period_virt + i;
+			if (p >= dma->nr_periods)
+				p -= dma->nr_periods;
+			hwwrite(vortex->mmio, VORTEX_WTDMA_BUFBASE+(((wtdma << 2)+(p & 3)) << 2), dma->buf_addr + ((dma->period_bytes)*p));
+		}
+	}
+	dma->period_virt += delta;
+	if (dma->period_virt >= dma->nr_periods)
+		dma->period_virt -= dma->nr_periods;
+	dma->period_real = page;
+
+	if (delta != 1)
+		printk(KERN_WARNING "vortex: wt page = %d, delta = %d\n", dma->period_virt, delta);
+
+	return delta;
+}
+
+void vortex_wtdma_getposition(vortex_t *vortex, int wtdma, int *subbuf, int *pos) {
+    int temp;
+    temp = hwread(vortex->mmio, VORTEX_WTDMA_STAT + (wtdma << 2));
+    *subbuf = (temp >> WT_SUBBUF_SHIFT) & WT_SUBBUF_MASK;
+    *pos = temp & POS_MASK;
+}
+
+int  vortex_wtdma_getcursubuffer(vortex_t *vortex, int wtdma) {
+    return((hwread(vortex->mmio, VORTEX_ADBDMA_STAT + (wtdma << 2)) >> POS_SHIFT) & POS_MASK);
+}
+
+void vortex_wtdma_startfifo(vortex_t *vortex, int wtdma) {
+	stream_t *dma = &vortex->dma_wt[wtdma];
+	int this_8=0, this_4=0;
+
+	switch (dma->fifo_status) {
+        case FIFO_START:
+            vortex_fifo_setwtvalid(vortex, wtdma, dma->fifo_enabled ? 0xff:0);
+            break;
+        case FIFO_STOP:
+			this_8 = 1;
+            hwwrite(vortex->mmio, VORTEX_WTDMA_CTRL + (wtdma << 2), dma->dma_ctrl);
+            vortex_fifo_setwtctrl(vortex, wtdma, dma->dma_unknown, this_4,
+				this_8, dma->fifo_enabled ? 0xff:0, 0);
+            break;
+        case FIFO_PAUSE:
+            vortex_fifo_setwtctrl(vortex, wtdma, dma->dma_unknown, this_4,
+				this_8, dma->fifo_enabled ? 0xff:0, 0);
+            break;
+	}
+	dma->fifo_status = FIFO_START;
+}
+
+void vortex_wtdma_resumefifo(vortex_t *vortex, int wtdma) {
+	stream_t *dma = &vortex->dma_wt[wtdma];
+
+    int this_8=0, this_4=0;
+    switch (dma->fifo_status) {
+        case FIFO_STOP:
+            hwwrite(vortex->mmio, VORTEX_WTDMA_CTRL + (wtdma << 2), dma->dma_ctrl);
+            vortex_fifo_setwtctrl(vortex, wtdma, dma->dma_unknown, this_4, this_8,
+				dma->fifo_enabled ? 0xff:0, 0);
+            break;
+        case FIFO_PAUSE:
+            vortex_fifo_setwtctrl(vortex, wtdma, dma->dma_unknown, this_4, this_8,
+				dma->fifo_enabled ? 0xff:0, 0);
+            break;
+    }
+    dma->fifo_status = FIFO_START;
+}
+
+void vortex_wtdma_pausefifo(vortex_t *vortex, int wtdma) {
+	stream_t *dma = &vortex->dma_wt[wtdma];
+
+    int this_8=0, this_4=0;
+    switch (dma->fifo_status) {
+        case FIFO_START:
+            vortex_fifo_setwtctrl(vortex, wtdma, dma->dma_unknown, this_4, this_8, 0, 0);
+            break;
+        case FIFO_STOP:
+            hwwrite(vortex->mmio, VORTEX_WTDMA_CTRL + (wtdma << 2), dma->dma_ctrl);
+            vortex_fifo_setwtctrl(vortex, wtdma, dma->dma_unknown, this_4, this_8, 0, 0);
+            break;
+    }
+    dma->fifo_status = FIFO_PAUSE;
+}
+
+void vortex_wtdma_stopfifo(vortex_t *vortex, int wtdma) {
+	stream_t *dma = &vortex->dma_wt[wtdma];
+
+    int this_4=0, this_8=0;
+    if (dma->fifo_status == FIFO_START)
+        vortex_fifo_setwtctrl(vortex, wtdma, dma->dma_unknown, this_4, this_8, 0, 0);
+    else if (dma->fifo_status == FIFO_STOP)
+        return;
+    dma->fifo_status = FIFO_STOP;
+    dma->fifo_enabled = 0;
+}
+
+void vortex_wtdma_chain(vortex_t *vortex, int wtdma, signed char sb1, signed char sb0) {
+    stream_t *dma = &vortex->dma_wt[wtdma];
+    int *cfg;
+    int offset;
+
+    if (sb0 == -1)
+        sb1 = sb0;
+    // this_0x80: cfg0, this_0x84: cfg1.
+
+    switch (sb0) {
+        case 0:
+            dma->cfg0 = (dma->cfg0 & 0xcfffffff) | ((((sb1 & 0x3) | 4) <<4 )  << 24);
+            cfg = &dma->cfg0;
+            offset = VORTEX_WTDMA_BUFCFG0;
+            break;
+        case 1:
+            dma->cfg0 = (dma->cfg0 & 0xfcffffff) | (((sb1 & 0x3) | 0x4) << 24);
+            cfg = &dma->cfg0;
+            offset = VORTEX_WTDMA_BUFCFG0;
+            break;
+        case 2:
+            dma->cfg1 = (dma->cfg1 & 0xfcffffff) | ((((sb1 & 0x3) | 4) << 4) << 24);
+            cfg = &dma->cfg1;
+            offset = VORTEX_WTDMA_BUFCFG1;
+            break;
+        case 3:
+            dma->cfg1 = (dma->cfg1 & 0xfcffffff) | (((sb1 & 0x3) | 0x4) << 24);
+            cfg = &dma->cfg1;
+            offset = VORTEX_WTDMA_BUFCFG1;
+            break;
+        default:
+            return;
+    }
+    hwwrite(vortex->mmio, offset + (wtdma << 3), *cfg);
+}
+
+#endif
+/* ADB */
+
+typedef int ADBRamLink;
+void vortex_adb_init(vortex_t *vortex) {
+	int i;
+	/* it looks like we are writing more than we need to...
+	 * if we write what we are supposed to it breaks things... */
+	hwwrite(vortex->mmio, VORTEX_ADB_SR, 0);
+	for(i=0; i<VORTEX_ADB_RTBASE_SIZE; i++)
+		hwwrite(vortex->mmio, VORTEX_ADB_RTBASE + (i<<2), 
+				hwread(vortex->mmio, VORTEX_ADB_RTBASE + (i<<2)) | ROUTE_MASK);
+	for(i=0; i<VORTEX_ADB_CHNBASE_SIZE; i++){
+		hwwrite(vortex->mmio, VORTEX_ADB_CHNBASE + (i<<2), 
+				hwread(vortex->mmio, VORTEX_ADB_CHNBASE + (i<<2)) | ROUTE_MASK);
+	}
+}
+
+void vortex_adb_en_sr(vortex_t *vortex, int channel) {
+    hwwrite(vortex->mmio, VORTEX_ADB_SR,
+	    hwread(vortex->mmio, VORTEX_ADB_SR) | (0x1 << channel));
+}
+
+void vortex_adb_dis_sr(vortex_t *vortex, int channel) {
+    hwwrite(vortex->mmio, VORTEX_ADB_SR,
+	    hwread(vortex->mmio, VORTEX_ADB_SR) & ~(0x1 << channel));
+}
+
+void vortex_adb_addroutes(vortex_t *vortex, unsigned char channel, ADBRamLink *route, int rnum) {
+    int temp, prev, lifeboat = 0;
+
+    if ((rnum <= 0)||(route == NULL))
+		return;
+	/* Write last routes. */
+	rnum--;
+    hwwrite(vortex->mmio, VORTEX_ADB_RTBASE + ((route[rnum] & ADB_MASK) << 2), ROUTE_MASK);
+    while (rnum > 0) {
+	    hwwrite(vortex->mmio,	VORTEX_ADB_RTBASE + ((route[rnum-1] & ADB_MASK) << 2), route[rnum]);
+	    rnum--;
+    }
+    /* Write first route. */
+    temp = hwread(vortex->mmio, VORTEX_ADB_CHNBASE + (channel << 2)) & ADB_MASK;
+    if (temp == ADB_MASK) {
+	    /* First entry on this channel. */
+	    hwwrite(vortex->mmio, VORTEX_ADB_CHNBASE + (channel << 2), route[0]);
+	    vortex_adb_en_sr(vortex, channel);
+	    return;
+    }
+    /* Not first entry on this channel. Need to link. */
+    do {
+		prev = temp;
+	    temp = hwread(vortex->mmio, VORTEX_ADB_RTBASE + (temp << 2)) & ADB_MASK;
+	    if ((lifeboat++) > ADB_MASK) {
+	        printk(KERN_ERR "vortex_adb_addroutes: unending route!\n");
+	        return;
+	    }
+    } while (temp != ADB_MASK);
+    hwwrite(vortex->mmio, VORTEX_ADB_RTBASE + (prev << 2), route[0]);
+}
+
+void vortex_adb_delroutes(vortex_t *vortex, unsigned char channel, ADBRamLink route0, ADBRamLink route1) {
+    int temp, lifeboat = 0, prev;
+
+    /* Find route. */
+    temp = hwread(vortex->mmio, VORTEX_ADB_CHNBASE + (channel << 2)) & ADB_MASK;
+    if (temp == (route0 & ADB_MASK)) {
+	    temp = hwread(vortex->mmio, VORTEX_ADB_RTBASE + ((route1 & ADB_MASK) << 2));
+	    if ((temp & ADB_MASK) == ADB_MASK)
+    	    vortex_adb_dis_sr(vortex, channel);
+    	hwwrite(vortex->mmio, VORTEX_ADB_CHNBASE + (channel << 2), temp);
+    	return;
+    }
+    do {
+	    prev = temp;
+	    temp = hwread(vortex->mmio, VORTEX_ADB_RTBASE + (prev << 2)) & ADB_MASK;
+	    if (((lifeboat++) > ADB_MASK) || (temp == ADB_MASK)) {
+    	    printk(KERN_ERR "vortex_adb_delroutes: route not found!\n");
+	        return;
+	    }
+    } while (temp != (route0 & ADB_MASK));
+    temp = hwread(vortex->mmio, VORTEX_ADB_RTBASE + (temp << 2));
+    if ((temp & ADB_MASK) == route1)
+        temp = hwread(vortex->mmio, VORTEX_ADB_RTBASE + (temp << 2));
+    /* Make bridge over deleted route. */
+    hwwrite(vortex->mmio, VORTEX_ADB_RTBASE + (prev << 2), temp);
+}
+
+void vortex_route(vortex_t *vortex, int en, unsigned char channel, unsigned char source, unsigned char dest) {
+    ADBRamLink route;
+
+    route = ((source & ADB_MASK) << ADB_SHIFT) | (dest & ADB_MASK);
+    if (en) {
+		vortex_adb_addroutes(vortex, channel, &route, 1);
+		if ((source < (OFFSET_SRCOUT+NR_SRC)) && (source >= OFFSET_SRCOUT))
+			vortex_src_addWTD(vortex, (source - OFFSET_SRCOUT), channel);
+		else if ((source < (OFFSET_MIXOUT+NR_MIXOUT)) && (source >= OFFSET_MIXOUT))
+			vortex_mixer_addWTD(vortex, (source - OFFSET_MIXOUT), channel);
+    } else {
+		vortex_adb_delroutes(vortex, channel, route, route);
+		if ((source < (OFFSET_SRCOUT+NR_SRC)) && (source >= OFFSET_SRCOUT))
+			vortex_src_delWTD(vortex, (source - OFFSET_SRCOUT), channel);
+		else if ((source < (OFFSET_MIXOUT+NR_MIXOUT)) && (source >= OFFSET_MIXOUT))
+			vortex_mixer_delWTD(vortex, (source - OFFSET_MIXOUT), channel);
+    }
+}
+
+void vortex_routes(vortex_t *vortex, int en, unsigned char channel, unsigned char source,
+    unsigned char dest0, unsigned char dest1) {
+    ADBRamLink route[2];
+
+    route[0] = ((source & ADB_MASK) << ADB_SHIFT) | (dest0 & ADB_MASK);
+    route[1] = ((source & ADB_MASK) << ADB_SHIFT) | (dest1 & ADB_MASK);
+
+    if (en) {
+		vortex_adb_addroutes(vortex, channel, route, 2);
+		if ((source < (OFFSET_SRCOUT+NR_SRC)) && (source >= (OFFSET_SRCOUT)))
+			vortex_src_addWTD(vortex, (source - OFFSET_SRCOUT), channel);
+		else if ((source < (OFFSET_MIXOUT+NR_MIXOUT)) && (source >= (OFFSET_MIXOUT)))
+			vortex_mixer_addWTD(vortex, (source - OFFSET_MIXOUT), channel);
+    } else {
+		vortex_adb_delroutes(vortex, channel, route[0], route[1]);
+		if ((source < (OFFSET_SRCOUT+NR_SRC)) && (source >= (OFFSET_SRCOUT)))
+			vortex_src_delWTD(vortex, (source - OFFSET_SRCOUT), channel);
+		else if ((source < (OFFSET_MIXOUT+NR_MIXOUT)) && (source >= (OFFSET_MIXOUT)))
+			vortex_mixer_delWTD(vortex, (source - OFFSET_MIXOUT), channel);
+    }
+}
+
+/* Route two sources to same target. Sources must be of same class !!! */
+void vortex_routeLRT(vortex_t *vortex, int en, unsigned char ch, unsigned char source0,
+    unsigned char source1, unsigned char dest) {
+    ADBRamLink route[2];
+
+    route[0] = ((source0 & ADB_MASK) << ADB_SHIFT) | (dest & ADB_MASK);
+    route[1] = ((source1 & ADB_MASK) << ADB_SHIFT) | (dest & ADB_MASK);
+
+	if (dest < 0x10)
+		route[1] = (route[1] & ~ADB_MASK) | (dest + 0x20);
+
+    if (en) {
+		vortex_adb_addroutes(vortex, ch, route, 2);
+		if ((source0 < (OFFSET_SRCOUT+NR_SRC)) && (source0 >= OFFSET_SRCOUT)) {
+			vortex_src_addWTD(vortex, (source0 - OFFSET_SRCOUT), ch);
+			vortex_src_addWTD(vortex, (source1 - OFFSET_SRCOUT), ch);
+		}
+		else if ((source0 < (OFFSET_MIXOUT+NR_MIXOUT)) && (source0 >= OFFSET_MIXOUT)) {
+			vortex_mixer_addWTD(vortex, (source0 - OFFSET_MIXOUT), ch);
+			vortex_mixer_addWTD(vortex, (source1 - OFFSET_MIXOUT), ch);
+		}
+    } else {
+		vortex_adb_delroutes(vortex, ch, route[0], route[1]);
+		if ((source0 < (OFFSET_SRCOUT+NR_SRC)) && (source0 >= OFFSET_SRCOUT)) {
+			vortex_src_delWTD(vortex, (source0 - OFFSET_SRCOUT), ch);
+			vortex_src_delWTD(vortex, (source1 - OFFSET_SRCOUT), ch);
+		}
+		else if ((source0 < (OFFSET_MIXOUT+NR_MIXOUT)) && (source0 >= OFFSET_MIXOUT)) {
+			vortex_mixer_delWTD(vortex, (source0 - OFFSET_MIXOUT), ch);
+			vortex_mixer_delWTD(vortex, (source1 - OFFSET_MIXOUT), ch);
+		}
+    }
+}
+
+/* Connection stuff */
+
+// Connect adbdma to src('s).
+void vortex_connection_adbdma_src(vortex_t *vortex, int en, unsigned char ch,
+    unsigned char adbdma, unsigned char src) {
+    vortex_route(vortex, en, ch, ADB_DMA(adbdma), ADB_SRCIN(src));
+}
+
+void vortex_connection_adbdma_src_src(vortex_t *vortex, int en, unsigned char channel,
+    unsigned char adbdma, unsigned char src0, unsigned char src1) {
+    vortex_routes(vortex, en, channel, ADB_DMA(adbdma), ADB_SRCIN(src0), ADB_SRCIN(src1));
+}
+
+// Connect SRC to mixin.
+void vortex_connection_src_mixin(vortex_t *vortex, int en, unsigned char channel,
+    unsigned char src, unsigned char mixin) {
+    vortex_route(vortex, en, channel, ADB_SRCOUT(src), ADB_MIXIN(mixin));
+}
+
+// Connect mixin with mix output.
+void vortex_connection_mixin_mix(vortex_t *vortex, int en, unsigned char mixin, unsigned char mix, int a) {
+    if (en) {
+		vortex_mix_enableinput(vortex, mix, mixin);
+        vortex_mix_setinputvolumebyte(vortex, mix, mixin, MIX_DEFIGAIN);	// added to original code.
+    } else
+	    vortex_mix_disableinput(vortex, mix, mixin, a);
+}
+
+// Connect absolut address to mixin.
+void vortex_connection_adb_mixin(vortex_t *vortex, int en, unsigned char channel,
+    unsigned char source, unsigned char mixin) {
+    vortex_route(vortex, en, channel, source, ADB_MIXIN(mixin));
+}
+
+// Connect two mix to AdbDma.
+void vortex_connection_mix_mix_adbdma(vortex_t *vortex, int en, unsigned char ch,
+    unsigned char mix0, unsigned char mix1, unsigned char adbdma) {
+
+    ADBRamLink routes[2];
+    routes[0] = (((mix0 + OFFSET_MIXOUT) & ADB_MASK) << ADB_SHIFT) | (adbdma & ADB_MASK);
+    routes[1] = (((mix1 + OFFSET_MIXOUT) & ADB_MASK) << ADB_SHIFT) | ((adbdma + 0x20 /*OFFSET_MIXOUT*/) & ADB_MASK);
+    if (en) {
+	    vortex_adb_addroutes(vortex, ch, routes, 0x2);
+		vortex_mixer_addWTD(vortex, mix0, ch);
+		vortex_mixer_addWTD(vortex, mix1, ch);
+    }
+	else {
+		vortex_adb_delroutes(vortex, ch, routes[0], routes[1]);
+		vortex_mixer_delWTD(vortex, mix0, ch);
+		vortex_mixer_delWTD(vortex, mix1, ch);
+    }
+}
+
+void vortex_connection_src_adbdma(vortex_t *vortex, int en, unsigned char ch,
+    unsigned char src, unsigned char adbdma) {
+    vortex_route(vortex, en, ch, ADB_SRCOUT(src), ADB_DMA(adbdma));
+}
+
+void vortex_connection_src_src_adbdma(vortex_t *vortex, int en, unsigned char ch,
+    unsigned char src0, unsigned char src1, unsigned char adbdma) {
+
+	vortex_routeLRT(vortex, en, ch, ADB_SRCOUT(src0), ADB_SRCOUT(src1), ADB_DMA(adbdma));
+}
+
+// mix to absolut address.
+void vortex_connection_mix_adb(vortex_t *vortex, int en, unsigned char ch,
+    unsigned char mix, unsigned char dest) {
+    vortex_route(vortex, en, ch, ADB_MIXOUT(mix), dest);
+    vortex_mix_setvolumebyte(vortex, mix, MIX_DEFOGAIN);	// added to original code.
+}
+
+// mixer to src.
+void vortex_connection_mix_src(vortex_t *vortex, int en, unsigned char ch,
+    unsigned char mix, unsigned char src) {
+    vortex_route(vortex, en, ch, ADB_MIXOUT(mix), ADB_SRCIN(src));
+    vortex_mix_setvolumebyte(vortex, mix, MIX_DEFOGAIN);	// added to original code.
+}
+
+/* CODEC connect. */
+void vortex_connect_codecplay(vortex_t *vortex, int en, unsigned char mixers[]) {
+#ifdef CHIP_AU8820
+	vortex_connection_mix_adb(vortex, en, 0x11, mixers[0], ADB_CODECOUT(0));
+	vortex_connection_mix_adb(vortex, en, 0x11, mixers[1], ADB_CODECOUT(1));
+#else
+#if 1
+	// Connect front channels through EQ.
+	vortex_connection_mix_adb(vortex, en, 0x11, mixers[0], ADB_EQIN(0));
+	vortex_connection_mix_adb(vortex, en, 0x11, mixers[1], ADB_EQIN(1));
+    vortex_route(vortex, en, 0x11, ADB_EQOUT(0), ADB_CODECOUT(0));
+    vortex_route(vortex, en, 0x11, ADB_EQOUT(1), ADB_CODECOUT(1));
+	/* Check if reg 0x28 has SDAC bit set. */
+	if (VORTEX_IS_QUAD(vortex)) {
+		/* Rear channel. Note: ADB_CODECOUT(0+2) and (1+2) is for AC97 modem */
+		vortex_connection_mix_adb(vortex, en, 0x11, mixers[2], ADB_CODECOUT(0+4));
+		vortex_connection_mix_adb(vortex, en, 0x11, mixers[3], ADB_CODECOUT(1+4));
+		printk("SDAC detected ");
+	}
+#else
+	// Use plain direct output to codec.
+	vortex_connection_mix_adb(vortex, en, 0x11, mixers[0], ADB_CODECOUT(0));
+	vortex_connection_mix_adb(vortex, en, 0x11, mixers[1], ADB_CODECOUT(1));
+#endif	
+#endif
+}
+
+void vortex_connect_codecrec(vortex_t *vortex, int en, unsigned char mixin0, unsigned char mixin1) {
+	/*
+	 Enable: 0x1, 0x1
+	 Channel: 0x11, 0x11
+	 ADB Source address: 0x48, 0x49
+	 Destination Asp4Topology_0x9c,0x98
+	*/
+	vortex_connection_adb_mixin(vortex, en, 0x11, ADB_CODECIN(0), mixin0);
+	vortex_connection_adb_mixin(vortex, en, 0x11, ADB_CODECIN(1), mixin1);
+}
+
+// Higher level audio path (de)allocator.
+
+/* Resource manager */
+static int resnum[VORTEX_RESOURCE_LAST] = {NR_ADB, NR_SRC, NR_MIXIN, NR_MIXOUT, NR_A3D};
+/*
+ Checkout/Checkin resource of given type. 
+ stream: resource map to be used. If NULL means that we want to allocate
+ the DMA resource (root of all other resources).
+ out: Mean checkout if != 0. Else mean Checkin resource.
+ restype: Indicates type of resource to be checked in or out.
+*/
+int  vortex_adb_checkinout(vortex_t *vortex, u32 resmap[], int out, int restype) {
+	int i, qty = resnum[restype], resinuse=0;
+         
+	if (out) {
+		/* Gather used resources by all streams. */
+		for (i=0; i<NR_ADB; i++) {
+			resinuse |= vortex->dma_adb[i].resources[restype];
+		}
+		resinuse |= vortex->fixed_res[restype];
+		/* Find and take free resource. */
+		for (i=0; i<qty; i++) {
+			if ((resinuse & (1 << i)) == 0) {
+				if (resmap != NULL)
+					resmap[restype] |= (1 << i);
+				else
+					vortex->dma_adb[i].resources[restype] |= (1 << i);
+				//printk("vortex: ResManager: type %d out %d\n", restype, i);
+				return i;
+			}
+		}
+	} else {
+		if (resmap == NULL)
+			return -EINVAL;
+		/* Checkin first resource of type restype. */
+		for (i=0; i<qty; i++) {
+			if (resmap[restype] & (1 << i)) {
+				resmap[restype] &= ~(1 << i);
+				//printk("vortex: ResManager: type %d in %d\n",restype, i);
+				return i;
+			}
+		}
+		if (vortex->fixed_res[restype] & (1 << i)) {
+				vortex->fixed_res[restype] &= ~(1 << i);
+				//printk("vortex: ResManager (fixed rsc): type %d in %d\n",restype, i);
+				return i;
+		}
+	}
+	printk("vortex: ResManager: type %d FATAL\n", restype);
+	return -ENOMEM;
+}
+
+/* Default Connections  */
+void vortex_connect_default(vortex_t *vortex, int en) {
+    
+	// FIXME: check if checkout was succesful.
+	// Connect AC97 codec.
+	vortex->mixplayb[0] = vortex_adb_checkinout(vortex, vortex->fixed_res, en, VORTEX_RESOURCE_MIXOUT);
+	vortex->mixplayb[1] = vortex_adb_checkinout(vortex, vortex->fixed_res, en, VORTEX_RESOURCE_MIXOUT);
+	if (VORTEX_IS_QUAD(vortex)) {
+		vortex->mixplayb[2] = vortex_adb_checkinout(vortex, vortex->fixed_res, en, VORTEX_RESOURCE_MIXOUT);
+		vortex->mixplayb[3] = vortex_adb_checkinout(vortex, vortex->fixed_res, en, VORTEX_RESOURCE_MIXOUT);
+	}
+	vortex_connect_codecplay(vortex, en, vortex->mixplayb);
+	
+	vortex->mixcapt[0] = vortex_adb_checkinout(vortex, vortex->fixed_res, en, VORTEX_RESOURCE_MIXIN);
+	vortex->mixcapt[1] = vortex_adb_checkinout(vortex, vortex->fixed_res, en, VORTEX_RESOURCE_MIXIN);
+	vortex_connect_codecrec(vortex, en, MIX_CAPT(0), MIX_CAPT(1));
+	
+	// Connect SPDIF
+	vortex->mixspdif[0] = vortex_adb_checkinout(vortex, vortex->fixed_res, en, VORTEX_RESOURCE_MIXOUT);
+	vortex->mixspdif[1] = vortex_adb_checkinout(vortex, vortex->fixed_res, en, VORTEX_RESOURCE_MIXOUT);
+	vortex_connection_mix_adb(vortex, en, 0x14, vortex->mixspdif[0], ADB_SPDIFOUT(0));
+	vortex_connection_mix_adb(vortex, en, 0x14, vortex->mixspdif[1], ADB_SPDIFOUT(1));
+	
+	// Connect I2S
+	
+	// Connect DSP interface (not here i think...)
+	
+	// Connect MODEM
+	
+#ifndef CHIP_AU8810
+	//vortex_wt_connect(vortex, en, vortex->mixplayb);
+#endif	
+	/* Fast Play Workaround */
+#ifndef CHIP_AU8820
+	vortex->fixed_res[VORTEX_RESOURCE_DMA] = 0x00000001;
+#endif
+}
+
+/*
+  Allocate nr_ch pcm audio routes if dma < 0. If dma >= 0, existing routes
+  are deallocated.
+  dma: DMA engine routes to be deallocated when dma >= 0.
+  nr_ch: Number of channels to be de/allocated.
+  dir: direction of stream. Uses same values as substream->stream.
+  type: Type of audio output/source (codec, spdif, i2s, dsp, etc)
+  Return: Return allocated DMA or same DMA passed as "dma" when dma >= 0.
+*/
+int  vortex_adb_allocroute(vortex_t *vortex, int dma, int nr_ch, int dir, int type) {
+	stream_t *stream;
+	int i, en;
+	
+	if ((nr_ch == 3) || ((dir == SNDRV_PCM_STREAM_CAPTURE)&&(nr_ch > 2)))
+		return -EBUSY;
+	
+	spin_lock(&vortex->lock);
+	if (dma >= 0) {
+   		en = 0;
+		vortex_adb_checkinout(vortex, vortex->dma_adb[dma].resources, en, VORTEX_RESOURCE_DMA);
+	} else {
+		en = 1;
+		if ((dma = vortex_adb_checkinout(vortex, NULL, en, VORTEX_RESOURCE_DMA)) < 0)
+			return -EBUSY;
+	}
+	
+	stream = &vortex->dma_adb[dma];
+	stream->dma = dma;
+	stream->dir = dir;
+	stream->type = type;	
+	
+	// FIXME: check for success of checkout or checkin.
+	if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
+		int src[4], mix[4], a3d=0, ch_top;
+		
+		/* Get SRC and MIXER hardware resources. */
+		if (stream->type != VORTEX_PCM_SPDIF) {
+			for (i=0; i<nr_ch; i++) {
+				if ((src[i] = vortex_adb_checkinout(vortex, stream->resources, en, VORTEX_RESOURCE_SRC))<0) {
+					memset(stream->resources, 0, sizeof(unsigned char)*VORTEX_RESOURCE_LAST);
+					return -EBUSY;
+				}
+				if ((mix[i] = vortex_adb_checkinout(vortex, stream->resources, en, VORTEX_RESOURCE_MIXIN))<0) {
+					memset(stream->resources, 0, sizeof(unsigned char)*VORTEX_RESOURCE_LAST);
+					return -EBUSY;
+				}
+			}
+		}
+		if (stream->type == VORTEX_PCM_A3D) {
+			if ((a3d = vortex_adb_checkinout(vortex, stream->resources, en, VORTEX_RESOURCE_A3D))<0) {
+				memset(stream->resources, 0, sizeof(unsigned char)*VORTEX_RESOURCE_LAST);
+				return -EBUSY;
+			}
+		}
+		/* Make SPDIF out exclusive to "spdif" device when in use. */
+		if ((stream->type == VORTEX_PCM_SPDIF)&&(en)) {
+			vortex_route(vortex, 0, 0x14, ADB_MIXOUT(vortex->mixspdif[0]), ADB_SPDIFOUT(0));
+			vortex_route(vortex, 0, 0x14, ADB_MIXOUT(vortex->mixspdif[1]), ADB_SPDIFOUT(1));
+		}
+		/* Make playback routes. */
+		for (i=0; i<nr_ch; i++) {
+			if (stream->type == VORTEX_PCM_ADB) {
+				vortex_connection_adbdma_src(vortex, en, src[nr_ch-1], dma, src[i]);
+				vortex_connection_src_mixin(vortex, en, 0x11, src[i], mix[i]);
+				vortex_connection_mixin_mix(vortex, en, mix[i], MIX_PLAYB(i), 0);
+				vortex_connection_mixin_mix(vortex, en, mix[i], MIX_SPDIF(i % 2), 0);
+				vortex_mix_setinputvolumebyte(vortex, MIX_SPDIF(i % 2), mix[i], MIX_DEFIGAIN);
+			}
+			if (stream->type == VORTEX_PCM_A3D) {
+				vortex_connection_adbdma_src(vortex, en, src[nr_ch-1], dma, src[i]);
+				vortex_route(vortex, en, 0x11, ADB_SRCOUT(src[i]), ADB_A3DIN(a3d));
+				vortex_route(vortex, en, 0x11, ADB_A3DOUT(a3d), ADB_MIXIN(mix[i]));
+				vortex_connection_mixin_mix(vortex, en, mix[i], MIX_PLAYB(i), 0);
+				vortex_connection_mixin_mix(vortex, en, mix[i], MIX_SPDIF(i % 2), 0);				
+				vortex_mix_setinputvolumebyte(vortex, MIX_SPDIF(i % 2), mix[i], MIX_DEFIGAIN);
+			}
+			if (stream->type == VORTEX_PCM_SPDIF)
+				vortex_route(vortex, en, 0x14, ADB_DMA(stream->dma), ADB_SPDIFOUT(i));
+		}
+		if (stream->type != VORTEX_PCM_SPDIF) {
+			ch_top = (VORTEX_IS_QUAD(vortex) ? 4 : 2);
+				for (i=nr_ch; i<ch_top; i++) {
+					vortex_connection_mixin_mix(vortex, en, mix[i % nr_ch], MIX_PLAYB(i), 0);
+					vortex_connection_mixin_mix(vortex, en, mix[i % nr_ch], MIX_SPDIF(i % 2), 0);
+					vortex_mix_setinputvolumebyte(vortex, MIX_SPDIF(i % 2), mix[i % nr_ch], MIX_DEFIGAIN);
+				}
+		} else {
+			if (nr_ch == 1)
+				vortex_route(vortex, en, 0x14, ADB_DMA(stream->dma), ADB_SPDIFOUT(1));
+		}
+		/* Reconnect SPDIF out when "spdif" device is down. */
+		if ((stream->type == VORTEX_PCM_SPDIF)&&(!en)) {
+			vortex_route(vortex, 1, 0x14, ADB_MIXOUT(vortex->mixspdif[0]), ADB_SPDIFOUT(0));
+			vortex_route(vortex, 1, 0x14, ADB_MIXOUT(vortex->mixspdif[1]), ADB_SPDIFOUT(1));
+		}		
+	} else {
+		int src[2], mix[2];
+		
+		/* Get SRC and MIXER hardware resources. */
+		for (i=0; i<nr_ch; i++) {
+			if ((mix[i] = vortex_adb_checkinout(vortex, stream->resources, en, VORTEX_RESOURCE_MIXOUT))<0) {
+				memset(stream->resources, 0, sizeof(unsigned char)*VORTEX_RESOURCE_LAST);
+				return -EBUSY;
+			}
+			if ((src[i] = vortex_adb_checkinout(vortex, stream->resources, en, VORTEX_RESOURCE_SRC))<0) {
+				memset(stream->resources, 0, sizeof(unsigned char)*VORTEX_RESOURCE_LAST);
+				return -EBUSY;
+			}
+		}
+		
+		/* Make capture routes. */
+		vortex_connection_mixin_mix(vortex, en, MIX_CAPT(0), mix[0], 0);
+		vortex_connection_mix_src(vortex, en, 0x11, mix[0], src[0]);
+		if (nr_ch == 1) {
+			vortex_connection_mixin_mix(vortex, en, MIX_CAPT(1), mix[0], 0);
+			vortex_connection_src_adbdma(vortex, en, src[nr_ch-1], src[0], dma);
+		} else {
+			vortex_connection_mixin_mix(vortex, en, MIX_CAPT(1), mix[1], 0);
+			vortex_connection_mix_src(vortex, en, 0x11, mix[1], src[1]);
+			vortex_connection_src_src_adbdma(vortex, en, src[0], src[0], src[1], dma);
+		}
+	}
+	vortex->dma_adb[dma].nr_ch = nr_ch;
+	spin_unlock(&vortex->lock);
+	
+#if 0
+	/* AC97 Codec channel setup. FIXME: this has no effect !! */
+	if (nr_ch < 4) {
+		/* Copy stereo to rear channel (surround) */
+		snd_ac97_write_cache(vortex->codec, AC97_SIGMATEL_DAC2INVERT, snd_ac97_read(vortex->codec, AC97_SIGMATEL_DAC2INVERT) | 4);
+	} else {
+		/* Allow separate front a rear channels. */
+		snd_ac97_write_cache(vortex->codec, AC97_SIGMATEL_DAC2INVERT, snd_ac97_read(vortex->codec, AC97_SIGMATEL_DAC2INVERT) & ~((u32)4));
+	}
+#endif
+	return dma;
+}
+/*
+ Set the SampleRate of the SRC's attached to the given DMA engine.
+ */
+void vortex_adb_setsrc(vortex_t *vortex, int adbdma, unsigned int rate, int dir) {
+    stream_t *stream = &(vortex->dma_adb[adbdma]);
+    int i, cvrt;
+    	
+	/* dir=1:play ; dir=0:rec */
+    if (dir)
+        cvrt = SRC_RATIO(rate,48000);
+    else
+        cvrt = SRC_RATIO(48000,rate);
+	
+	/* Setup SRC's */
+	for (i=0; i<NR_SRC; i++) {
+		if (stream->resources[VORTEX_RESOURCE_SRC] & (1<<i))
+			vortex_src_setupchannel(vortex, i, cvrt, 0, 0, i, dir, 0, cvrt, dir);
+	}
+}
+
+// Timer and ISR functions.
+
+void vortex_settimer(vortex_t *vortex, int period) {
+    //set the timer period to <period> 48000ths of a second.
+	hwwrite(vortex->mmio, VORTEX_IRQ_STAT, period);
+}
+
+void vortex_enable_timer_int(vortex_t *card){
+	hwwrite(card->mmio, VORTEX_IRQ_CTRL, hwread(card->mmio, VORTEX_IRQ_CTRL) | IRQ_TIMER | 0x60);
+}
+
+void vortex_disable_timer_int(vortex_t *card){
+	hwwrite(card->mmio, VORTEX_IRQ_CTRL, hwread(card->mmio, VORTEX_IRQ_CTRL) & ~IRQ_TIMER);
+}
+
+void vortex_enable_int(vortex_t *card){
+	// CAsp4ISR__EnableVortexInt_void_
+	hwwrite(card->mmio, VORTEX_CTRL, hwread(card->mmio, VORTEX_CTRL) | CTRL_IRQ_ENABLE);
+	hwwrite(card->mmio, VORTEX_IRQ_CTRL, (hwread(card->mmio, VORTEX_IRQ_CTRL) & 0xffffefc0) | 0x24);
+}
+
+void vortex_disable_int(vortex_t *card){
+	hwwrite(card->mmio, VORTEX_CTRL, hwread(card->mmio, VORTEX_CTRL) & ~CTRL_IRQ_ENABLE);
+}
+
+irqreturn_t vortex_interrupt(int irq, void *dev_id, struct pt_regs *regs) {
+	vortex_t *vortex = snd_magic_cast(vortex_t, dev_id, return IRQ_NONE);
+	int i, handled, temp;
+        u32 source;
+#ifdef TARGET_OS2
+	int fOurIrq = FALSE;
+#endif
+
+	//check if the interrupt is ours.
+	if (!(hwread(vortex->mmio, VORTEX_IRQ_U0) & 0x1))
+		return IRQ_NONE;
+	
+	// This is the Interrrupt Enable flag we set before (consistency check).
+        if ((hwread(vortex->mmio, VORTEX_CTRL) & 0x0000ff00) == CTRL_IRQ_ENABLE)
+        {
+#ifdef TARGET_OS2
+            eoi_irq(irq);
+#endif
+            return IRQ_HANDLED;
+        }
+
+#ifdef TARGET_OS2
+	fOurIrq = TRUE;
+#endif
+	
+	source = hwread(vortex->mmio, VORTEX_IRQ_SOURCE);
+	// Reset IRQ flags.
+	hwwrite(vortex->mmio, VORTEX_IRQ_SOURCE, source);
+	hwread(vortex->mmio, VORTEX_IRQ_SOURCE);
+	// Is at least one IRQ flag set?
+	if (source == 0) {
+#ifdef DEBUG
+            printk(KERN_ERR "vortex: missing irq source\n");
+#endif
+		return IRQ_NONE;
+	}
+	
+	//printk(KERN_INFO "IRQ: 0x%x\n", source);
+
+	handled = 0;
+	// Attend every interrupt source.
+        if (source & IRQ_FATAL) {
+#ifdef DEBUG
+            printk(KERN_ERR "vortex: IRQ fatal error\n");
+#endif
+		handled = 1;
+	}
+	if (source & IRQ_PARITY) {
+#ifdef DEBUG
+            printk(KERN_ERR "vortex: IRQ parity error\n");
+#endif
+		handled = 1;
+	}
+	if (source & IRQ_PCMOUT) {
+		/* ALSA period acknowledge. */
+		for (i=0; i<NR_ADB; i++) {
+			if (vortex->dma_adb[i].fifo_status == FIFO_START) {
+                            if (vortex_adbdma_bufshift(vortex, i));
+                            snd_pcm_period_elapsed(vortex->dma_adb[i].substream);
+			}
+                }
+#ifndef CHIP_AU8810
+		for (i=0; i<NR_WT; i++) {
+			if (vortex->dma_wt[i].fifo_status == FIFO_START) {
+				if (vortex_wtdma_bufshift(vortex, i));
+				snd_pcm_period_elapsed(vortex->dma_wt[i].substream);
+			}
+		}
+#endif
+		handled = 1;
+	}
+	//Acknowledge the Timer interrupt
+	if (source & IRQ_TIMER) {
+		hwread(vortex->mmio, VORTEX_IRQ_STAT);
+		handled = 1;
+	}
+	if (source & IRQ_MIDI) {
+		snd_mpu401_uart_interrupt(vortex->irq, vortex->rmidi->private_data, regs);
+		handled = 1;
+	}
+	
+	if (!handled) {
+#ifdef DEBUG
+            printk(KERN_ERR "vortex: unknown irq source %x\n", source);
+#endif
+        }
+#ifdef TARGET_OS2
+	if (fOurIrq) {
+		eoi_irq(irq);
+	}
+#endif //TARGET_OS2
+
+	return IRQ_RETVAL(handled);
+}
+
+/* Codec */
+
+#define POLL_COUNT 1000
+void vortex_codec_init(vortex_t *vortex) {
+    int i;
+
+	for(i =0; i<32; i++){
+		hwwrite(vortex->mmio,(VORTEX_CODEC_CHN+(i<<2)),0);
+		udelay(2000);
+	}
+	if (0) {
+		hwwrite(vortex->mmio,VORTEX_CODEC_CTRL,0x8068);
+		udelay(1000);
+		hwwrite(vortex->mmio,VORTEX_CODEC_CTRL,0x00e8);
+		udelay(1000);
+	} else {
+		hwwrite(vortex->mmio,VORTEX_CODEC_CTRL,0x00a8);
+		udelay(2000);
+		hwwrite(vortex->mmio,VORTEX_CODEC_CTRL,0x80a8);
+		udelay(2000);
+		hwwrite(vortex->mmio,VORTEX_CODEC_CTRL,0x80e8);
+		udelay(2000);
+		hwwrite(vortex->mmio,VORTEX_CODEC_CTRL,0x80a8);
+		udelay(2000);
+		hwwrite(vortex->mmio,VORTEX_CODEC_CTRL,0x00a8);
+		udelay(2000);
+		hwwrite(vortex->mmio,VORTEX_CODEC_CTRL,0x00e8);
+	}
+	for(i =0; i<32; i++){
+		hwwrite(vortex->mmio,(VORTEX_CODEC_CHN+(i<<2)),0);
+		udelay(5000);
+	}
+	hwwrite(vortex->mmio,VORTEX_CODEC_CTRL,0xe8);
+	udelay(1000);
+	/* Enable codec channels 0 and 1. */
+	hwwrite(vortex->mmio,VORTEX_CODEC_EN, hwread(vortex->mmio,VORTEX_CODEC_EN) | EN_CODEC);
+}
+
+void vortex_codec_write(ac97_t *codec, unsigned short addr, unsigned short data){
+
+	vortex_t *card = (vortex_t*)codec->private_data;
+	unsigned long flags;
+        unsigned int lifeboat = 0;
+        unsigned int temp_addr, temp_data;
+        int temp;
+
+//        temp_addr = (unsigned int) addr;
+//        temp_data = (unsigned int) data;
+
+	spin_lock_irqsave(&card->lock, flags);
+	
+	/* wait for transactions to clear */
+	while (!(hwread(card->mmio, VORTEX_CODEC_CTRL) & 0x100)) {
+		udelay(100);
+		if (lifeboat++ > POLL_COUNT) {
+			printk(KERN_ERR "vortex: ac97 codec stuck busy\n");
+			spin_unlock_irqrestore(&card->lock, flags);
+			return;
+		}
+	}
+	/* write register */
+ 	hwwrite(card->mmio, VORTEX_CODEC_IO, ((addr << VORTEX_CODEC_ADDSHIFT) & VORTEX_CODEC_ADDMASK) |
+	 		((data << VORTEX_CODEC_DATSHIFT) & VORTEX_CODEC_DATMASK) | VORTEX_CODEC_WRITE);
+	
+	/* Flush Caches.*/
+	hwread(card->mmio, VORTEX_CODEC_IO);
+	
+	spin_unlock_irqrestore(&card->lock, flags);
+}
+
+unsigned short vortex_codec_read(ac97_t *codec, unsigned short addr) {
+
+	vortex_t *card = (vortex_t*)codec->private_data;
+	u32 read_addr, data, temp_addr;
+	unsigned long flags;
+	unsigned lifeboat = 0;
+
+//        temp_addr = (u32)addr;
+
+	spin_lock_irqsave(&card->lock, flags);
+
+	/* wait for transactions to clear */
+	while (!(hwread(card->mmio, VORTEX_CODEC_CTRL) & 0x100)) {
+		udelay(100);
+		if (lifeboat++ > POLL_COUNT) {
+			printk(KERN_ERR "vortex: ac97 codec stuck busy\n");
+			spin_unlock_irqrestore(&card->lock, flags);
+			return 0xffff;
+		}
+	}	
+	/* set up read address */
+	read_addr = ((addr << VORTEX_CODEC_ADDSHIFT) & VORTEX_CODEC_ADDMASK);
+	hwwrite(card->mmio, VORTEX_CODEC_IO, read_addr);
+
+	/* wait for address */
+	{
+		udelay(100);
+		data = hwread(card->mmio, VORTEX_CODEC_IO);
+		if (lifeboat++ > POLL_COUNT) {
+			printk(KERN_ERR "vortex: ac97 address never arrived\n");
+			spin_unlock_irqrestore(&card->lock, flags);
+			return 0xffff;
+		}
+	} while ((data & VORTEX_CODEC_ADDMASK) != (addr << VORTEX_CODEC_ADDSHIFT));
+	
+	/* Unlock. */
+	spin_unlock_irqrestore(&card->lock, flags);
+	
+	/* return data. */
+	return (u16)(data & VORTEX_CODEC_DATMASK);
+}
+
+/* SPDIF support  */
+void vortex_spdif_init(vortex_t *vortex, int spdif_sr, int spdif_mode) {
+	int i, this_38 = 0, this_04=0, this_08=0, this_0c=0;
+	
+	/* CAsp4Spdif::InitializeSpdifHardware(void) */
+	hwwrite(vortex->mmio, VORTEX_SPDIF_FLAGS, hwread(vortex->mmio, VORTEX_SPDIF_FLAGS) & 0xfff3fffd);
+	//for (i=0x291D4; i<0x29200; i+=4)
+	for (i=0; i<11; i++)
+		hwwrite(vortex->mmio, VORTEX_SPDIF_CFG1 + (i<<2), 0);
+	//hwwrite(vortex->mmio, 0x29190, hwread(vortex->mmio, 0x29190) | 0xc0000);
+	hwwrite(vortex->mmio, VORTEX_CODEC_EN, hwread(vortex->mmio, VORTEX_CODEC_EN) | EN_SPDIF);
+
+	/* CAsp4Spdif::ProgramSRCInHardware(enum  SPDIF_SR,enum	 SPDIFMODE) */
+	if (this_04 && this_08) {
+		int edi;
+		
+		i = (((0x5DC00000 / spdif_sr) + 1) >> 1);
+		if (i > 0x800) {
+			if (i < 0x1ffff)
+				edi = (i >> 1);
+			else 
+				edi = 0x1ffff;
+		} else {
+			i = edi = 0x800;
+		}
+		/* this_04 and this_08 are the CASp4Src's (samplerate converters) */
+		vortex_src_setupchannel(vortex, this_04, edi, 0, 1, this_0c, 1, 0, edi, 1);
+		vortex_src_setupchannel(vortex, this_08, edi, 0, 1, this_0c, 1, 0, edi, 1);
+	}
+	
+	i = spdif_sr;
+	spdif_sr |= 0x8c;
+	switch (i) {
+		case 32000:
+			this_38 &= 0xFFFFFFFE;
+			this_38 &= 0xFFFFFFFD;
+			this_38 &= 0xF3FFFFFF;
+			this_38 |= 0x03000000;
+			this_38 &= 0xFFFFFF3F;
+			spdif_sr &= 0xFFFFFFFD;
+			spdif_sr |= 1;
+			break;
+		case 44100:
+			this_38 &= 0xFFFFFFFE;
+			this_38 &= 0xFFFFFFFD;
+			this_38 &= 0xF0FFFFFF;
+			this_38 |= 0x03000000;
+			this_38 &= 0xFFFFFF3F;
+			spdif_sr &= 0xFFFFFFFC;
+			break;
+		case 48000:
+			if (spdif_mode == 1) {
+				this_38 &= 0xFFFFFFFE;
+				this_38 &= 0xFFFFFFFD;
+				this_38 &= 0xF2FFFFFF;
+				this_38 |= 0x02000000;
+				this_38 &= 0xFFFFFF3F;
+			} else {
+				this_38 |= 0x00000003;
+				this_38 &= 0xFFFFFFBF;
+				this_38 |= 0x80;
+			}
+			spdif_sr |= 2;
+			spdif_sr &= 0xFFFFFFFE;
+			break;
+		
+	}
+	hwwrite(vortex->mmio, VORTEX_SPDIF_CFG0, this_38 & 0xffff);
+	hwwrite(vortex->mmio, VORTEX_SPDIF_CFG1, this_38 >> 0x10);
+	hwwrite(vortex->mmio, VORTEX_SPDIF_SMPRATE, spdif_sr);
+}
+
+/* Initialization */
+
+int vortex_core_init(vortex_t *vortex) {
+    int temp;
+
+	printk(KERN_INFO "Vortex: hardware init.... ");
+	/* Hardware Init. */
+	hwwrite(vortex->mmio, VORTEX_CTRL, 0xffffffff);
+    udelay(5000);
+    hwwrite(vortex->mmio, VORTEX_CTRL, hwread(vortex->mmio, VORTEX_CTRL) & 0xffdfffff);
+	udelay(5000);
+	/* Reset IRQ flags */
+	hwwrite(vortex->mmio, VORTEX_IRQ_SOURCE, 0xffffffff);
+	hwread(vortex->mmio, VORTEX_IRQ_STAT);
+
+	vortex_codec_init(vortex);
+	
+#ifdef CHIP_AU8830
+	hwwrite(vortex->mmio, VORTEX_CTRL, hwread(vortex->mmio, VORTEX_CTRL) | 0x1000000);
+#endif	
+	
+    /* Audio engine init. */
+	vortex_fifo_init(vortex);
+	hwwrite(vortex->mmio, VORTEX_ENGINE_CTRL, 0x0);	//, 0xc83c7e58, 0xc5f93e58
+	vortex_adb_init(vortex);
+	/* Routing blocks init. */
+	vortex_adbdma_init(vortex);
+	vortex_mixer_init(vortex);
+	vortex_srcblock_init(vortex);
+#ifndef CHIP_AU8820
+	vortex_eq_init(vortex);
+	vortex_spdif_init(vortex, 48000, 1);
+#endif
+#ifndef CHIP_AU8810
+	vortex_wt_InitializeWTRegs(vortex);
+#endif
+	// Moved to au88x0.c
+	//vortex_connect_default(vortex, 1);
+
+    vortex_settimer(vortex, 0x90);
+    // Enable Interrupts.
+    // vortex_enable_int() must be first !!
+    //	hwwrite(vortex->mmio, VORTEX_IRQ_CTRL, 0);
+    // vortex_enable_int(vortex);
+    //vortex_enable_timer_int(vortex);
+    //vortex_disable_timer_int(vortex);
+
+    printk(KERN_INFO "done.\n");
+
+    return 0;
+}
+
+int vortex_core_shutdown(vortex_t *vortex) {
+
+    printk(KERN_INFO "Vortex: hardware shutdown...");
+#ifndef CHIP_AU8820	
+	vortex_eq_free(vortex);
+#endif
+    vortex_disable_timer_int(vortex);
+	vortex_disable_int(vortex);
+    vortex_connect_default(vortex, 0);
+    /* Reset all DMA fifos. */
+	vortex_fifo_init(vortex);
+    /* Erase all audio routes. */
+	vortex_adb_init(vortex);
+
+    /* Disable MPU401 */
+	//hwwrite(vortex->mmio, VORTEX_IRQ_CTRL, hwread(vortex->mmio, VORTEX_IRQ_CTRL) & ~IRQ_MIDI);
+	//hwwrite(vortex->mmio, VORTEX_CTRL, hwread(vortex->mmio, VORTEX_CTRL) & ~CTRL_MIDI_EN);
+
+    hwwrite(vortex->mmio, VORTEX_IRQ_CTRL, 0);
+	hwwrite(vortex->mmio, VORTEX_CTRL, 0);
+	udelay(5000);
+	hwwrite(vortex->mmio, VORTEX_IRQ_SOURCE, 0xffff);
+
+    printk(KERN_INFO "done.\n");
+    return 0;
+}
+
+/* Alsa support. */
+
+int vortex_alsafmt_aspfmt(int alsafmt) {
+    int fmt;
+
+    switch (alsafmt) {
+        case SNDRV_PCM_FORMAT_U8: fmt = 0x1;
+            break;
+        case SNDRV_PCM_FORMAT_MU_LAW: fmt = 0x2;
+            break;
+        case SNDRV_PCM_FORMAT_A_LAW: fmt = 0x3;
+            break;
+        case SNDRV_PCM_FORMAT_SPECIAL: fmt = 0x4; /* guess. */
+            break;
+        case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE: fmt = 0x5; /* guess. */
+            break;
+        case SNDRV_PCM_FORMAT_S16_LE: fmt = 0x8;
+            break;
+        default: fmt = 0x8;
+            printk(KERN_ERR "vortex: format unsupported %d\n", alsafmt);
+            break;
+    }
+    return fmt;
+}
+
+/* Some not yet useful translations. */
+
+typedef enum {
+	ASPFMTLINEAR16 = 0, /* 0x8 */
+	ASPFMTLINEAR8,      /* 0x1 */
+	ASPFMTULAW,         /* 0x2 */
+	ASPFMTALAW,         /* 0x3 */
+	ASPFMTSPORT,        /* ? */
+	ASPFMTSPDIF,        /* ? */
+} ASPENCODING;
+
+int  vortex_translateformat(vortex_t *vortex, char bits, char nch, int encod) {
+	int a, this_194;
+
+	if ((bits != 8) || (bits != 16))
+		return -1;
+
+	switch (encod) {
+		case 0:
+			if (bits == 0x10)
+				a = 8; // 16 bit
+			break;
+		case 1:
+			if (bits == 8)
+				a = 1; // 8 bit
+			break;
+		case 2: a = 2; // U_LAW
+			break;
+		case 3: a = 3; // A_LAW
+			break;
+	}
+	switch (nch) {
+		case 1: this_194 = 0;
+			break;
+		case 2:	this_194 = 1;
+			break;
+		case 4: this_194 = 1;
+			break;
+		case 6: this_194 = 1;
+			break;
+	}
+	return(a);
+}
+
+void vortex_cdmacore_setformat(vortex_t *vortex, int bits, int nch) {
+	short int d, this_148;
+
+	d = ((bits >> 3)*nch);
+	this_148 = 0xbb80 / d;
+}
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au88x0_eq.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au88x0_eq.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au88x0_eq.c	(revision 91)
@@ -0,0 +1,839 @@
+/***************************************************************************
+ *            au88x0_eq.c
+ *  Aureal Vortex Hardware EQ control/access.
+ *
+ *  Sun Jun  8 18:19:19 2003
+ *  2003  Manuel Jander (mjander@users.sourceforge.net)
+ *  
+ *  02 July 2003: First time something works :)
+ *  
+ *  TODO:
+ *     - Debug (testing)
+ *     - Implement peak visualization support.
+ *
+ ****************************************************************************/
+#include "au88x0.h"
+#include "au88x0_eq.h"
+#include "au88x0_eqdata.c"
+
+/* CEqHw.s */
+void vortex_EqHw_SetTimeConsts(vortex_t *vortex, u16 a, u16 b) {
+	hwwrite(vortex->mmio, 0x2b3c4, a);
+	hwwrite(vortex->mmio, 0x2b3c8, b);
+}
+
+void vortex_EqHw_GetTimeConsts(vortex_t *vortex, u16 *a, u16 *b) {
+	*a = hwread(vortex->mmio, 0x2b3c4);
+	*b = hwread(vortex->mmio, 0x2b3c8);
+}
+
+void vortex_EqHw_SetLeftCoefs(vortex_t *vortex, u16 a[]) {
+	eqhw_t *eqhw = &(vortex->eq.this04);
+	int eax, i = 0, n /*esp2c*/ = 0;
+	
+	if (eqhw->this04 <= n)
+		return;
+		
+	do {
+		hwwrite(vortex->mmio, 0x2b000 + n*0x30, a[i+0]);
+		hwwrite(vortex->mmio, 0x2b004 + n*0x30, a[i+1]);
+		
+		if (eqhw->this08 == 0) {
+			hwwrite(vortex->mmio, 0x2b008 + n*0x30, a[i+2]);
+			hwwrite(vortex->mmio, 0x2b00c + n*0x30, a[i+3]);
+			eax = a[i+4]; //esp24;
+		} else {
+			if (a[2+i] == 0x8000)
+				eax = 0x7fff;
+			else
+				eax = ~a[2+i];
+			hwwrite(vortex->mmio, 0x2b008 + n*0x30, eax & 0xffff);
+			if (a[3+i] == 0x8000)
+				eax = 0x7fff;
+			else
+				eax = ~a[3+i];
+			hwwrite(vortex->mmio, 0x2b00c + n*0x30, eax & 0xffff);
+			if (a[4+i] == 0x8000)
+				eax = 0x7fff;
+			else
+				eax = ~a[4+i];
+		}
+		hwwrite(vortex->mmio, 0x2b010 + n*0x30, eax);
+		
+		n++;
+		i += 5;
+	} while(n < eqhw->this04);	
+}
+
+void vortex_EqHw_GetLeftCoefs(vortex_t *vortex, u16 a[]) {
+	
+	
+}
+
+void vortex_EqHw_SetRightCoefs(vortex_t *vortex, u16 a[]) {
+	eqhw_t *eqhw = &(vortex->eq.this04);
+	int i = 0, n /*esp2c*/ = 0, eax;
+	
+	if (eqhw->this04 <= n)
+		return;
+	
+	do {
+		hwwrite(vortex->mmio, 0x2b1e0 + n*0x30, a[0+i]);
+		hwwrite(vortex->mmio, 0x2b1e4 + n*0x30, a[1+i]);
+		
+		if (eqhw->this08 == 0) {
+			hwwrite(vortex->mmio, 0x2b1e8 + n*0x30, a[2+i]);
+			hwwrite(vortex->mmio, 0x2b1ec + n*0x30, a[3+i]);
+			eax = a[4+i]; //*esp24;
+		} else {
+			if (a[2+i] == 0x8000)
+				eax = 0x7fff;
+			else
+				eax = ~(a[2+i]);
+			hwwrite(vortex->mmio, 0x2b1e8 + n*0x30, eax & 0xffff);
+			if (a[3+i] == 0x8000)
+				eax = 0x7fff;
+			else
+				eax = ~a[3+i];
+			hwwrite(vortex->mmio, 0x2b1ec + n*0x30, eax & 0xffff);
+			if (a[4+i] == 0x8000)
+				eax = 0x7fff;
+			else
+				eax = ~a[4+i];
+		}
+		hwwrite(vortex->mmio, 0x2b1f0 + n*0x30, eax);
+		i += 5;
+		n++;	
+	} while (n < eqhw->this04);
+	
+}
+
+void vortex_EqHw_GetRightCoefs(vortex_t *vortex, u16 a[]) {
+	
+	
+}
+
+void vortex_EqHw_SetLeftStates(vortex_t *vortex, u16 a[], u16 b[]) {
+	eqhw_t *eqhw = &(vortex->eq.this04);
+	int i = 0, ebx = 0;
+	
+	hwwrite(vortex->mmio, 0x2b3fc, a[0]);
+	hwwrite(vortex->mmio, 0x2b400, a[1]);
+	
+	if (eqhw->this04 < 0)
+		return;
+	
+	do {
+		hwwrite(vortex->mmio, 0x2b014 + (i*0xc), b[i]);
+		hwwrite(vortex->mmio, 0x2b018 + (i*0xc), b[1+i]);
+		hwwrite(vortex->mmio, 0x2b01c + (i*0xc), b[2+i]);
+		hwwrite(vortex->mmio, 0x2b020 + (i*0xc), b[3+i]);
+		i += 4;
+		ebx++;
+	} while (eqhw->this04 > ebx);
+}
+
+void vortex_EqHw_GetLeftStates(vortex_t *vortex, u16 *a, u16 b[]) {
+
+	
+}
+
+void vortex_EqHw_SetRightStates(vortex_t *vortex, u16 a[], u16 b[]) {
+	eqhw_t *eqhw = &(vortex->eq.this04);
+	int i = 0, ebx = 0;
+	
+	hwwrite(vortex->mmio, 0x2b404, a[0]);
+	hwwrite(vortex->mmio, 0x2b408, a[1]);
+	
+	if (eqhw->this04 < 0)
+		return;
+		
+	do {
+		hwwrite(vortex->mmio, 0x2b1f4 + (i*0xc), b[i]);
+		hwwrite(vortex->mmio, 0x2b1f8 + (i*0xc), b[1+i]);
+		hwwrite(vortex->mmio, 0x2b1fc + (i*0xc), b[2+i]);
+		hwwrite(vortex->mmio, 0x2b200 + (i*0xc), b[3+i]);		
+		i += 4;
+		ebx++;
+	} while (ebx < eqhw->this04);
+}
+
+void vortex_EqHw_GetRightStates(vortex_t *vortex, u16 *a, u16 b[]) {
+	
+}
+
+void vortex_EqHw_SetBypassGain(vortex_t *vortex, u16 a, u16 b) {
+	eqhw_t *eqhw = &(vortex->eq.this04);
+	int eax;
+	
+	if (eqhw->this08 == 0) {
+		hwwrite(vortex->mmio, 0x2b3d4, a);
+		hwwrite(vortex->mmio, 0x2b3ec, b);
+	} else {
+		if (a == 0x8000)
+			eax = 0x7fff;
+		else
+			eax = ~a;
+		hwwrite(vortex->mmio, 0x2b3d4, eax & 0xffff);
+		if (b == 0x8000)
+			eax = 0x7fff;
+		else
+			eax = ~b;
+		hwwrite(vortex->mmio, 0x2b3ec, eax & 0xffff);
+	}
+}
+
+void vortex_EqHw_SetA3DBypassGain(vortex_t *vortex, u16 a, u16 b) {
+	
+	hwwrite(vortex->mmio, 0x2b3e0, a);
+	hwwrite(vortex->mmio, 0x2b3f8, b);
+}
+
+void vortex_EqHw_SetCurrBypassGain(vortex_t *vortex, u16 a, u16 b) {
+	
+	hwwrite(vortex->mmio, 0x2b3d0, a);
+	hwwrite(vortex->mmio, 0x2b3e8, b);
+}
+
+void vortex_EqHw_SetCurrA3DBypassGain(vortex_t *vortex, u16 a, u16 b) {
+	
+	hwwrite(vortex->mmio, 0x2b3dc, a);
+	hwwrite(vortex->mmio, 0x2b3f4, b);
+}
+
+void vortex_EqHw_SetLeftGainsSingleTarget(vortex_t *vortex, u16 index, u16 b) {
+	hwwrite(vortex->mmio, 0x2b02c + (index*0x30), b);
+}
+
+void vortex_EqHw_SetRightGainsSingleTarget(vortex_t *vortex, u16 index, u16 b) {
+	hwwrite(vortex->mmio, 0x2b20c + (index*0x30), b);
+}
+
+void vortex_EqHw_SetLeftGainsTarget(vortex_t *vortex, u16 a[]) {
+	eqhw_t *eqhw = &(vortex->eq.this04);
+	int ebx=0;
+	
+	if (eqhw->this04 < 0)
+		return;
+	do {
+		hwwrite(vortex->mmio, 0x2b02c + ebx*0x30, a[ebx]);
+		ebx++;
+	} while (ebx < eqhw->this04);
+}
+
+void vortex_EqHw_SetRightGainsTarget(vortex_t *vortex, u16 a[]) {
+	eqhw_t *eqhw = &(vortex->eq.this04);
+	int ebx=0;
+	
+	if (eqhw->this04 < 0)
+		return;
+	
+	do {
+		hwwrite(vortex->mmio, 0x2b20c + ebx*0x30, a[ebx]);
+		ebx++;
+	} while (ebx < eqhw->this04);	
+}
+
+void vortex_EqHw_GetLeftGainsTarget(vortex_t *vortex, u16 a[]) {
+	eqhw_t *eqhw = &(vortex->eq.this04);
+	int ebx=0;
+	
+	if (eqhw->this04 < 0)
+		return;
+	
+	do {
+		a[ebx] = hwread(vortex->mmio, 0x2b02c + ebx*0x30);
+		ebx++;
+	} while (ebx < eqhw->this04);
+}
+
+void vortex_EqHw_GetRightGainsTarget(vortex_t *vortex, u16 a[]) {
+	eqhw_t *eqhw = &(vortex->eq.this04);
+	int ebx=0;
+	
+	if (eqhw->this04 < 0)
+		return;
+	
+	do {
+		a[ebx] = hwread(vortex->mmio, 0x2b20c + ebx*0x30);
+		ebx++;
+	} while (ebx < eqhw->this04);	
+}
+
+void vortex_EqHw_SetLeftGainsCurrent(vortex_t *vortex, u16 a[]) {
+	eqhw_t *eqhw = &(vortex->eq.this04);
+	int ebx=0;
+	
+	if (eqhw->this04 < 0)
+		return;
+	
+	do {
+		hwwrite(vortex->mmio, 0x2b028 + ebx*0x30, a[ebx]);
+		ebx++;
+	} while (ebx < eqhw->this04);
+}
+
+void vortex_EqHw_SetRightGainsCurrent(vortex_t *vortex, u16 a[]) {
+	eqhw_t *eqhw = &(vortex->eq.this04);
+	int ebx=0;
+	
+	if (eqhw->this04 < 0)
+		return;
+	
+	do {
+		hwwrite(vortex->mmio, 0x2b208 + ebx*0x30, a[ebx]);
+		ebx++;
+	} while (ebx < eqhw->this04);	
+}
+
+void vortex_EqHw_GetLeftGainsCurrent(vortex_t *vortex, u16 a[]) {
+	eqhw_t *eqhw = &(vortex->eq.this04);
+	int ebx=0;
+	
+	if (eqhw->this04 < 0)
+		return;
+	
+	do {
+		a[ebx] = hwread(vortex->mmio, 0x2b028 + ebx*0x30);
+		ebx++;
+	} while (ebx < eqhw->this04);
+}
+
+void vortex_EqHw_GetRightGainsCurrent(vortex_t *vortex, u16 a[]) {
+	eqhw_t *eqhw = &(vortex->eq.this04);
+	int ebx=0;
+	
+	if (eqhw->this04 < 0)
+		return;
+	
+	do {
+		a[ebx] = hwread(vortex->mmio, 0x2b208 + ebx*0x30);
+		ebx++;
+	} while (ebx < eqhw->this04);	
+}
+
+void vortex_EqHw_SetLevels(vortex_t *vortex, u16 a[]) {
+	eqhw_t *eqhw = &(vortex->eq.this04);
+	int ebx;
+	
+	if (eqhw->this04 < 0)
+		return;
+	
+	ebx = 0;
+	do {
+		hwwrite(vortex->mmio, 0x2b024 + ebx*0x30, a[ebx]);
+		ebx++;
+	} while (ebx < eqhw->this04);
+	
+	hwwrite(vortex->mmio, 0x2b3cc, a[eqhw->this04]);
+	hwwrite(vortex->mmio, 0x2b3d8, a[eqhw->this04+1]);
+	
+	ebx = 0;
+	do {
+		hwwrite(vortex->mmio, 0x2b204 + ebx*0x30, a[ebx + (eqhw->this04+2)]);
+		ebx++;
+	} while (ebx < eqhw->this04);
+	
+	hwwrite(vortex->mmio, 0x2b3e4, a[2+(eqhw->this04*2)]);
+	hwwrite(vortex->mmio, 0x2b3f0, a[3+(eqhw->this04*2)]);
+}
+
+void vortex_EqHw_GetLevels(vortex_t *vortex, u16 a[]) {
+	eqhw_t *eqhw = &(vortex->eq.this04);
+	int ebx;
+	
+	if (eqhw->this04 < 0)
+		return;
+	
+	ebx = 0;
+	do {
+		a[ebx] = hwread(vortex->mmio, 0x2b024 + ebx*0x30);
+		ebx++;
+	} while (ebx < eqhw->this04);
+	
+	a[eqhw->this04] = hwread(vortex->mmio, 0x2b3cc);
+	a[eqhw->this04+1] = hwread(vortex->mmio, 0x2b3d8);
+	
+	ebx = 0;
+	do {
+		a[ebx+(eqhw->this04+2)] = hwread(vortex->mmio, 0x2b204 + ebx*0x30);
+		ebx++;
+	} while (ebx < eqhw->this04);
+	
+	a[2+(eqhw->this04*2)] = hwread(vortex->mmio, 0x2b3e4);
+	a[3+(eqhw->this04*2)] = hwread(vortex->mmio, 0x2b3f0);
+}
+
+void vortex_EqHw_SetControlReg(vortex_t *vortex, unsigned long reg) {
+	hwwrite(vortex->mmio, 0x2b440, reg);
+}
+
+void vortex_EqHw_GetControlReg(vortex_t *vortex, unsigned long *reg) {
+	*reg = hwread(vortex->mmio, 0x2b440);
+}
+
+void vortex_EqHw_SetSampleRate(vortex_t *vortex, int sr) {
+	hwwrite(vortex->mmio, 0x2b440, ((sr & 0x1f) << 3) | 0xb800);
+}
+
+void vortex_EqHw_GetSampleRate(vortex_t *vortex, int *sr) {
+	*sr = (hwread(vortex->mmio, 0x2b440) >> 3) & 0x1f;
+}
+
+void vortex_EqHw_Enable(vortex_t *vortex) {
+	hwwrite(vortex->mmio, 0x2b440, 0xf001);
+}
+
+void vortex_EqHw_Disable(vortex_t *vortex) {
+	hwwrite(vortex->mmio, 0x2b440, 0xf000);
+}
+
+/* Reset (zero) buffers */
+void vortex_EqHw_ZeroIO(vortex_t *vortex) {
+	int i;
+	for (i=0; i<0x8; i++)
+		hwwrite(vortex->mmio, 0x2b410 + (i<<2), 0x0);
+	for (i=0; i<0x4; i++)
+		hwwrite(vortex->mmio, 0x2b430 + (i<<2), 0x0);
+}
+
+void vortex_EqHw_ZeroA3DIO(vortex_t *vortex) {
+	int i;
+	for (i=0; i<0x4; i++)
+		hwwrite(vortex->mmio, 0x2b410 + (i<<2), 0x0);	
+}
+
+void vortex_EqHw_ZeroState(vortex_t *vortex) {
+	
+	vortex_EqHw_SetControlReg(vortex, 0);
+	vortex_EqHw_ZeroIO(vortex);
+	hwwrite(vortex->mmio, 0x2b3c0, 0);
+	
+	vortex_EqHw_SetTimeConsts(vortex, 0, 0);
+	
+	vortex_EqHw_SetLeftCoefs(vortex, asEqCoefsZeros);
+	vortex_EqHw_SetRightCoefs(vortex, asEqCoefsZeros);
+	
+	vortex_EqHw_SetLeftGainsCurrent(vortex, eq_gains_zero);
+	vortex_EqHw_SetRightGainsCurrent(vortex, eq_gains_zero);
+	vortex_EqHw_SetLeftGainsTarget(vortex, eq_gains_zero);
+	vortex_EqHw_SetRightGainsTarget(vortex, eq_gains_zero);
+	
+	vortex_EqHw_SetBypassGain(vortex, 0, 0);
+	vortex_EqHw_SetA3DBypassGain(vortex, 0, 0);
+	vortex_EqHw_SetLeftStates(vortex, eq_states_zero, asEqOutStateZeros);
+	vortex_EqHw_SetRightStates(vortex, eq_states_zero, asEqOutStateZeros);
+	vortex_EqHw_SetLevels(vortex, (u16*)eq_levels);
+}
+
+/* Program coeficients as pass through */
+void vortex_EqHw_ProgramPipe(vortex_t *vortex) {
+	vortex_EqHw_SetTimeConsts(vortex, 0, 0);
+	
+	vortex_EqHw_SetLeftCoefs(vortex, asEqCoefsPipes);
+	vortex_EqHw_SetRightCoefs(vortex, asEqCoefsPipes);
+	
+	vortex_EqHw_SetLeftGainsCurrent(vortex, eq_gains_current);
+	vortex_EqHw_SetRightGainsCurrent(vortex, eq_gains_current);
+	vortex_EqHw_SetLeftGainsTarget(vortex, eq_gains_current);
+	vortex_EqHw_SetRightGainsTarget(vortex, eq_gains_current);
+}
+/* Program EQ block as 10 band Equalizer */
+void vortex_EqHw_Program10Band(vortex_t *vortex, auxxEqCoeffSet_t *coefset) {
+	
+	vortex_EqHw_SetTimeConsts(vortex, 0xc, 0x7fe0);
+	
+	vortex_EqHw_SetLeftCoefs(vortex, coefset->LeftCoefs);
+	vortex_EqHw_SetRightCoefs(vortex, coefset->RightCoefs);
+	
+	vortex_EqHw_SetLeftGainsCurrent(vortex, coefset->LeftGains);
+	
+	vortex_EqHw_SetRightGainsTarget(vortex, coefset->RightGains);
+	vortex_EqHw_SetLeftGainsTarget(vortex, coefset->LeftGains);
+	
+	vortex_EqHw_SetRightGainsCurrent(vortex, coefset->RightGains);
+}
+
+
+void vortex_EqHw_GetTenBandLevels(vortex_t *vortex, u16 peaks[]) {
+	eqhw_t *eqhw = &(vortex->eq.this04);
+	int i; 
+	
+	if (eqhw->this04 > 0)
+		return;
+	
+	for (i=0; i<eqhw->this04; i++)
+		peaks[i] = hwread(vortex->mmio, 0x2B024 + i*0x30);
+	for (i=0; i<eqhw->this04; i++)
+		peaks[i+eqhw->this04] = hwread(vortex->mmio, 0x2B204 + i*0x30);
+}
+/* CEqlzr.s */
+
+int  vortex_Eqlzr_GetLeftGain(vortex_t *vortex, u16 index, u16 *gain) {
+	eqlzr_t *eq = &(vortex->eq);
+	
+	if (eq->this28) {
+		*gain = eq->this130[index];
+		return 0;
+	}
+	return 1;
+}
+
+void vortex_Eqlzr_SetLeftGain(vortex_t *vortex, u16 index, u16 gain) {
+	eqlzr_t *eq = &(vortex->eq);
+
+	if (eq->this28 == 0)
+		return;
+	
+	eq->this130[index] = gain;
+	if (eq->this54)
+		return;
+	
+	vortex_EqHw_SetLeftGainsSingleTarget(vortex, index, gain);
+}
+
+int  vortex_Eqlzr_GetRightGain(vortex_t *vortex, u16 index, u16 *gain) {
+	eqlzr_t *eq = &(vortex->eq);
+	
+	if (eq->this28) {
+		*gain = eq->this130[index + eq->this10];
+		return 0;
+	}
+	return 1;
+}
+
+void vortex_Eqlzr_SetRightGain(vortex_t *vortex, u16 index, u16 gain) {
+	eqlzr_t *eq = &(vortex->eq);
+	
+	if (eq->this28 == 0)
+		return;
+	
+	eq->this130[index + eq->this10] = gain;
+	if (eq->this54)
+		return;
+	
+	vortex_EqHw_SetRightGainsSingleTarget(vortex, index, gain);
+}
+
+int  vortex_Eqlzr_GetAllBands(vortex_t *vortex, u16 *gains, unsigned long *cnt) {
+	eqlzr_t *eq = &(vortex->eq);
+	int si=0;
+	
+	if (eq->this10 == 0)
+		return 1;
+	
+	{
+		if (vortex_Eqlzr_GetLeftGain(vortex, si, &gains[si]))
+			return 1;			
+		if (vortex_Eqlzr_GetRightGain(vortex, si, &gains[si + eq->this10]))
+			return 1;
+		si++;
+	} while (eq->this10 > si);
+	*cnt = si*2;
+	return 0;
+}
+
+int  vortex_Eqlzr_SetAllBandsFromActiveCoeffSet(vortex_t *vortex) {
+	eqlzr_t *eq = &(vortex->eq);
+	
+	vortex_EqHw_SetLeftGainsTarget(vortex, eq->this130);
+	vortex_EqHw_SetRightGainsTarget(vortex, &(eq->this130[eq->this10]));
+	
+	return 0;
+}
+
+int  vortex_Eqlzr_SetAllBands(vortex_t *vortex, u16 gains[], unsigned long count) {
+	eqlzr_t *eq = &(vortex->eq);
+	int i;
+	
+	if (((eq->this10)*2 != count) || (eq->this28 == 0))
+		return 1;
+	
+	if (0 < count) {
+		for (i=0; i<count; i++) {
+			eq->this130[i] = gains[i];
+		}
+	}
+	if (eq->this54)
+		return 0;
+	return vortex_Eqlzr_SetAllBandsFromActiveCoeffSet(vortex);
+}
+
+void vortex_Eqlzr_ProgramA3dBypassGain(vortex_t *vortex) {
+	eqlzr_t *eq = &(vortex->eq);
+	int eax, ebx;
+	
+	if (eq->this54)
+		eax = eq->this0e;
+	else
+		eax = eq->this0a;
+	ebx = (eax * eq->this58) >> 0x10;
+	eax = (eax * eq->this5c) >> 0x10;
+	vortex_EqHw_SetA3DBypassGain(vortex, ebx, eax);
+}
+
+void vortex_Eqlzr_SetBypass(vortex_t *vortex, long bp) {
+	eqlzr_t *eq = &(vortex->eq);
+	
+	if ((eq->this28) && (bp == 0)) {
+		vortex_Eqlzr_SetAllBandsFromActiveCoeffSet(vortex);
+		vortex_EqHw_SetBypassGain(vortex, eq->this08, eq->this08);
+	} else {
+		vortex_EqHw_SetLeftGainsTarget(vortex, (u16*)(eq->this14));
+		vortex_EqHw_SetRightGainsTarget(vortex, (u16*)(eq->this14));
+		vortex_EqHw_SetBypassGain(vortex, eq->this0c, eq->this0c);
+	}
+	// FIXME: no yet implemented.
+	vortex_Eqlzr_ProgramA3dBypassGain(vortex);
+}
+
+void vortex_Eqlzr_ReadAndSetActiveCoefSet(vortex_t *vortex) {
+	eqlzr_t *eq = &(vortex->eq);
+	
+	/* Set EQ BiQuad filter coeficients */
+	memcpy(&(eq->coefset), &asEqCoefsNormal, sizeof(auxxEqCoeffSet_t));
+	/* Set EQ Band gain levels and dump into hardware registers. */
+	vortex_Eqlzr_SetAllBands(vortex, eq_gains_normal, eq->this10*2);	
+}
+
+#if 0
+void vortex_Eqlzr_vortex_SetA3dBypassVolume()
+void vortex_Eqlzr_ShutDownA3d()
+#endif
+
+int  vortex_Eqlzr_GetAllPeaks(vortex_t *vortex, u16 *peaks, int *count) {
+	eqlzr_t *eq = &(vortex->eq);
+	
+	if (eq->this10 == 0)
+		return 1;
+	*count = eq->this10 * 2;
+	vortex_EqHw_GetTenBandLevels(vortex, peaks);
+	return 0;
+}
+
+auxxEqCoeffSet_t *vortex_Eqlzr_GetActiveCoefSet(vortex_t *vortex) {
+	eqlzr_t *eq = &(vortex->eq);
+	
+	return (&(eq->coefset));
+}
+
+void vortex_Eqlzr_init(vortex_t *vortex) {
+	eqlzr_t *eq = &(vortex->eq);
+	
+	/* Object constructor */
+	//eq->this04 = 0;
+	eq->this08 = 0; /* Bypass gain with EQ in use. */
+	eq->this0a = 0x5999; 
+	eq->this0c = 0x5999; /* Bypass gain with EQ disabled. */
+	eq->this0e = 0x5999;
+	
+	eq->this10 = 0xa; /* 10 eq frequency bands. */
+	eq->this04.this04 = eq->this10;
+	eq->this28 = 0x1; /* if 1 => Allow read access to this130 (gains) */
+	eq->this54 = 0x0; /* if 1 => Dont Allow access to hardware (gains) */
+	eq->this58 = 0xffff;
+	eq->this5c = 0xffff;
+	
+	/* Set gains. */
+	memset(eq->this14, 0, 2*10);
+	
+	/* Actual init. */
+	vortex_EqHw_ZeroState(vortex);
+	vortex_EqHw_SetSampleRate(vortex, 0x11);
+	vortex_Eqlzr_ReadAndSetActiveCoefSet(vortex);
+	
+	vortex_EqHw_Program10Band(vortex, &(eq->coefset));
+	vortex_Eqlzr_SetBypass(vortex, eq->this54);
+	vortex_EqHw_Enable(vortex);
+}
+
+/* ALSA interface */
+
+/* Control interface */
+static int snd_vortex_eqtoggle_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_vortex_eqtoggle_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) {
+	vortex_t *vortex = snd_kcontrol_chip(kcontrol);
+	eqlzr_t *eq = &(vortex->eq);
+	//int i = kcontrol->private_value;
+	
+	ucontrol->value.integer.value[0] = eq->this54 ? 0 : 1;
+	
+	return 0;
+}
+
+static int snd_vortex_eqtoggle_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) {
+	vortex_t *vortex = snd_kcontrol_chip(kcontrol);
+	eqlzr_t *eq = &(vortex->eq);
+	//int i = kcontrol->private_value;
+	
+	eq->this54 = ucontrol->value.integer.value[0] ? 0 : 1;
+	vortex_Eqlzr_SetBypass(vortex, eq->this54);
+	
+	return 1; /* Allways changes */
+}
+
+static snd_kcontrol_new_t vortex_eqtoggle_kcontrol __devinitdata = {
+    /*	.iface = */ SNDRV_CTL_ELEM_IFACE_MIXER,
+    0,0,
+/*	.name =  */ "EQ Enable",
+/*	.index = */ 0,
+/*	.access = */ SNDRV_CTL_ELEM_ACCESS_READWRITE,
+0,
+/*	.info = */ snd_vortex_eqtoggle_info,
+/*	.get = */ snd_vortex_eqtoggle_get,
+/*	.put = */ snd_vortex_eqtoggle_put,
+/*	.private_value = */ 0
+};
+
+static int snd_vortex_eq_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) {
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0x0000;
+	uinfo->value.integer.max = 0x7fff;
+	return 0;
+}
+
+static int snd_vortex_eq_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) {
+	vortex_t *vortex = snd_kcontrol_chip(kcontrol);
+	int i = kcontrol->private_value;
+	u16 gainL, gainR;
+	
+	vortex_Eqlzr_GetLeftGain(vortex, i, &gainL);
+	vortex_Eqlzr_GetRightGain(vortex, i, &gainR);
+	ucontrol->value.integer.value[0] = gainL;
+	ucontrol->value.integer.value[1] = gainR;
+	return 0;
+}
+
+static int snd_vortex_eq_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) {
+	vortex_t *vortex = snd_kcontrol_chip(kcontrol);
+	int changed = 0, i = kcontrol->private_value;
+	u16 gainL, gainR;
+	
+	vortex_Eqlzr_GetLeftGain(vortex, i, &gainL);
+	vortex_Eqlzr_GetRightGain(vortex, i, &gainR);
+	
+	if (gainL != ucontrol->value.integer.value[0]) {
+		vortex_Eqlzr_SetLeftGain(vortex, i, ucontrol->value.integer.value[0]);
+		changed = 1;
+	}
+	if (gainR != ucontrol->value.integer.value[1]) {
+		vortex_Eqlzr_SetRightGain(vortex, i, ucontrol->value.integer.value[1]);
+		changed = 1;
+	}
+	return changed;
+}
+
+static snd_kcontrol_new_t vortex_eq_kcontrol __devinitdata = {
+    /*	.iface = */SNDRV_CTL_ELEM_IFACE_MIXER,
+    0,0,
+/*	.name = */"                        .",
+/*	.index = */0,
+/*	.access = */SNDRV_CTL_ELEM_ACCESS_READWRITE,
+0,
+/*	.info = */snd_vortex_eq_info,
+/*	.get = */snd_vortex_eq_get,
+/*	.put = */snd_vortex_eq_put,
+/*	.private_value = */0
+};
+
+static int snd_vortex_peaks_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) {
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 20;
+	uinfo->value.integer.min = 0x0000;
+	uinfo->value.integer.max = 0xffff;
+	return 0;
+}
+
+static int snd_vortex_peaks_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) {
+	vortex_t *vortex = snd_kcontrol_chip(kcontrol);
+	int i, count;
+	u16 peaks[20];
+	
+	vortex_Eqlzr_GetAllPeaks(vortex, peaks, &count);
+	if (count != 20) {
+		printk("vortex: peak count error 20 != %d \n", count);
+		return -1;
+	}
+	for (i=0; i<20; i++)
+		ucontrol->value.integer.value[i] = peaks[i];
+	
+	return 0;
+}
+
+static snd_kcontrol_new_t vortex_levels_kcontrol __devinitdata = {
+/*	.iface = */SNDRV_CTL_ELEM_IFACE_MIXER,0,0,
+/*	.name = */"EQ Peaks",0,
+/*	.access = */SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+0,
+/*	.info = */snd_vortex_peaks_info,
+/*	.get = */snd_vortex_peaks_get,
+NULL,0
+};
+
+/* EQ band gain labels. */
+static char *EqBandLabels[10] __devinitdata = {
+	"EQ0 31Hz\0",
+	"EQ1 63Hz\0",
+	"EQ2 125Hz\0",
+	"EQ3 250Hz\0",
+	"EQ4 500Hz\0",
+	"EQ5 1KHz\0",
+	"EQ6 2KHz\0",
+	"EQ7 4KHz\0",
+	"EQ8 8KHz\0",
+	"EQ9 16KHz\0",
+};
+
+/* ALSA driver entry points. Init and exit. */
+int vortex_eq_init(vortex_t *vortex) {
+	snd_kcontrol_t *kcontrol;
+	int err, i;
+	
+	vortex_Eqlzr_init(vortex);
+
+	if ((kcontrol = snd_ctl_new1(&vortex_eqtoggle_kcontrol, vortex)) == NULL)
+		return -ENOMEM;
+	kcontrol->private_value = 0;
+	if ((err = snd_ctl_add(vortex->card, kcontrol)) < 0)
+       	return err;
+
+	/* EQ gain controls */
+	for (i=0; i<10; i++) {
+		if ((kcontrol = snd_ctl_new1(&vortex_eq_kcontrol, vortex)) == NULL)
+			return -ENOMEM;
+		strcpy(kcontrol->id.name, EqBandLabels[i]);
+		kcontrol->private_value = i;
+		if ((err = snd_ctl_add(vortex->card, kcontrol)) < 0)
+        	return err;
+		//vortex->eqctrl[i] = kcontrol;
+	}
+	/* EQ band levels */
+	if ((kcontrol = snd_ctl_new1(&vortex_levels_kcontrol, vortex)) == NULL)
+		return -ENOMEM;
+	if ((err = snd_ctl_add(vortex->card, kcontrol)) < 0)
+       	return err;
+	
+	return 0;
+}
+
+int vortex_eq_free(vortex_t *vortex) {
+	/*
+	//FIXME: segfault because vortex->eqctrl[i] == 4
+	int i;
+	for (i=0; i<10; i++) {
+		if (vortex->eqctrl[i])
+			snd_ctl_remove(vortex->card, vortex->eqctrl[i]);
+	}
+	*/
+	return 0;
+}
+
+/* End */
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au88x0_eq.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au88x0_eq.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au88x0_eq.h	(revision 91)
@@ -0,0 +1,45 @@
+#ifndef AU88X0_EQ_H
+#define AU88X0_EQ_H
+
+/***************************************************************************
+ *            au88x0_eq.h
+ *
+ *  Definitions and constant data for the Aureal Hardware EQ.
+ *
+ *  Sun Jun  8 18:23:38 2003
+ *  Author: Manuel Jander (mjander@users.sourceforge.net)
+ ****************************************************************************/
+
+typedef struct {
+	u16 LeftCoefs[50]; //0x4
+	u16 RightCoefs[50]; // 0x68
+	u16	LeftGains[20]; //0xd0
+	u16 RightGains[20]; //0xe4
+} auxxEqCoeffSet_t;
+
+typedef struct {
+	unsigned int *this00; /*CAsp4HwIO*/
+	long this04;  /* How many filters for each side (default = 10) */
+	long this08;  /* inited to cero. Stereo flag? */
+} eqhw_t;
+
+typedef struct {
+	unsigned int *this00; /*CAsp4Core*/
+	eqhw_t this04; /* CHwEq */
+	short this08; /* Bad codec flag ? SetBypassGain: bypass gain */
+	short this0a;
+	short this0c; /* SetBypassGain: bypass gain when this28 is not set. */
+	short this0e;
+	
+	long this10; /* How many gains are used for each side (right or left). */
+	u16 this14[32]; /* SetLeftGainsTarget: Left (and right?) EQ gains  */
+	long this24;
+	long this28; /* flag related to EQ enabled or not. Gang flag ? */
+	long this54; /* SetBypass */
+	long this58;
+	long this5c;
+	/*0x60*/ auxxEqCoeffSet_t coefset; /* 50 u16 word each channel. */
+	u16 this130[20]; /* Left and Right gains */
+} eqlzr_t;
+
+#endif
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au88x0_eqdata.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au88x0_eqdata.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au88x0_eqdata.c	(revision 91)
@@ -0,0 +1,111 @@
+/* Data structs */
+
+static u16 asEqCoefsZeros[50] = {
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000,	0x0000, 0x0000,	
+};
+
+static u16 asEqCoefsPipes[64] = {
+	0x0000, 0x0000, 
+	0x0000, 0x0666, 0x0000, 0x0000, 0x0666, 
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+	0x0000, 0x0666, 0x0000, 0x0000, 0x0666, 
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+	0x0000, 0x0666, 0x0000, 0x0000, 0x0666, 
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+	0x0000, 0x0666, 0x0000, 0x0000, 0x0666, 
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+	0x0000, 0x0666, 0x0000, 0x0000, 0x066a, 
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+	
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+	0x0000, 0x0000
+};
+
+/* More coef sets can be found in the win2k "inf" file. */
+static auxxEqCoeffSet_t asEqCoefsNormal = {
+	/*.LeftCoefs = */{
+	0x7e60, 0xc19e, 0x0001, 0x0002, 0x0001,
+	0x7fa0, 0xc05f, 0x004f, 0x0000, 0xffb1,
+	0x7f3f, 0xc0bc, 0x00c2, 0x0000, 0xff3e,
+	0x7e78, 0xc177, 0x011f, 0x0000, 0xfee1,
+	0x7cd6, 0xc2e5, 0x025c, 0x0000, 0xfda4,
+	0x7949, 0xc5aa, 0x0467, 0x0000, 0xfb99,
+	0x7120, 0xcadf, 0x0864, 0x0000, 0xf79c,
+	0x5d33, 0xd430, 0x0f7e, 0x0000, 0xf082,
+	0x2beb, 0xe3ca, 0x1bd3, 0x0000, 0xe42d,
+	0xd740, 0xf01d, 0x2ac5, 0x0000, 0xd53b },
+ 
+	/*.RightCoefs =*/ {
+	0x7e60, 0xc19e, 0x0001, 0x0002, 0x0001,
+	0x7fa0, 0xc05f, 0x004f, 0x0000, 0xffb1,
+	0x7f3f, 0xc0bc, 0x00c2, 0x0000, 0xff3e,
+	0x7e78, 0xc177, 0x011f, 0x0000, 0xfee1,
+	0x7cd6, 0xc2e5, 0x025c, 0x0000, 0xfda4,
+	0x7949, 0xc5aa, 0x0467, 0x0000, 0xfb99,
+	0x7120, 0xcadf, 0x0864, 0x0000, 0xf79c,
+	0x5d33, 0xd430, 0x0f7e, 0x0000, 0xf082,
+	0x2beb, 0xe3ca, 0x1bd3, 0x0000, 0xe42d,
+	0xd740, 0xf01d, 0x2ac5, 0x0000, 0xd53b },
+
+	/*.LeftGains = */{
+	0x3e96, 0x3e96, 0x3e96, 0x3e96, 0x3e96, 
+	0x3e96, 0x3e96, 0x3e96, 0x3e96,	0x3e96 },
+	/*.RightGains = */{
+	0x3e96, 0x3e96, 0x3e96, 0x3e96, 0x3e96, 
+	0x3e96, 0x3e96, 0x3e96,	0x3e96, 0x3e96 }
+};
+
+static u16 eq_gains_normal[20] = {	
+	0x3e96, 0x3e96, 0x3e96, 0x3e96, 0x3e96, 
+	0x3e96, 0x3e96, 0x3e96, 0x3e96,	0x3e96,
+	0x3e96, 0x3e96, 0x3e96, 0x3e96, 0x3e96, 
+	0x3e96, 0x3e96, 0x3e96,	0x3e96, 0x3e96
+};
+
+/* _rodatab60 */
+static u16 eq_gains_zero[10] = { 
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000
+};
+
+/* _rodatab7c:  ProgramPipe */
+static u16 eq_gains_current[12] = { 
+	0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff,	0x7fff, 
+	0x7fff, 0x7fff, 0x7fff
+};
+
+/* _rodatab78 */
+static u16 eq_states_zero[2] = {0x0000, 0x0000};
+
+static u16 asEqOutStateZeros[48] = {
+	0x0000, 0x0000, 0x0000, 0x0000, 
+	0x0000, 0x0000, 0x0000, 0x0000, 
+	0x0000, 0x0000, 0x0000, 0x0000, 
+	0x0000, 0x0000, 0x0000, 0x0000, 
+	0x0000, 0x0000, 0x0000, 0x0000, 
+	0x0000, 0x0000, 0x0000, 0x0000, 
+	0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 
+	0x0000, 0x0000, 0x0000, 0x0000, 
+	0x0000, 0x0000, 0x0000, 0x0000, 
+	0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000 
+};
+
+/*_rodataba0:*/
+static long eq_levels[32] = {
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+	0x0000, 0x0000,	0x0000, 0x0000, 0x0000, 0x0000,	0x0000, 0x0000, 
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
+};
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au88x0_game.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au88x0_game.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au88x0_game.c	(revision 91)
@@ -0,0 +1,115 @@
+/*
+ * $Id: au88x0_game.c,v 1.9 2003/09/22 03:51:28 mjander Exp $
+ *
+ *  Manuel Jander.
+ *
+ *  Based on the work of:
+ *  Vojtech Pavlik
+ *  Raymond Ingles
+ *
+ * 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
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail:
+ * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
+ *
+ * Based 90% on Vojtech Pavlik pcigame driver.
+ * Merged and modified by Manuel Jander, for the OpenVortex
+ * driver. (email: mjander@embedded.cl).
+ */
+
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <linux/init.h>
+#include <sound/core.h>
+#include "au88x0.h"
+#include <linux/gameport.h>
+
+#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
+
+#define VORTEX_GAME_DWAIT	20	/* 20 ms */
+
+static struct gameport gameport;
+
+static unsigned char vortex_game_read(struct gameport *gameport) {
+	vortex_t *vortex = gameport->driver;
+	return hwread(vortex->mmio, VORTEX_GAME_LEGACY);
+}
+
+static void vortex_game_trigger(struct gameport *gameport) {
+	vortex_t *vortex = gameport->driver;
+	hwwrite(vortex->mmio, VORTEX_GAME_LEGACY, 0xff);
+}
+
+static int vortex_game_cooked_read(struct gameport *gameport, int *axes, int *buttons) {
+	vortex_t *vortex = gameport->driver;
+	int i;
+
+	*buttons = (~hwread(vortex->mmio, VORTEX_GAME_LEGACY) >> 4) & 0xf;
+
+	for (i = 0; i < 4; i++) {
+		axes[i] = hwread(vortex->mmio, VORTEX_GAME_AXIS + (i * AXIS_SIZE));
+		if (axes[i] == AXIS_RANGE) axes[i] = -1;
+	}
+	return 0;
+}
+
+static int vortex_game_open(struct gameport *gameport, int mode) {
+	vortex_t *vortex = gameport->driver;
+
+	switch (mode) {
+		case GAMEPORT_MODE_COOKED:
+			hwwrite(vortex->mmio, VORTEX_CTRL2, hwread(vortex->mmio, VORTEX_CTRL2) | CTRL2_GAME_ADCMODE);
+			wait_ms(VORTEX_GAME_DWAIT);
+			return 0;
+		case GAMEPORT_MODE_RAW:
+			hwwrite(vortex->mmio, VORTEX_CTRL2, hwread(vortex->mmio, VORTEX_CTRL2) & ~CTRL2_GAME_ADCMODE);
+			return 0;
+		default:
+			return -1;
+	}
+
+	return 0;
+}
+
+int vortex_gameport_register(vortex_t *vortex) {
+	vortex->gameport = &gameport;
+
+	vortex->gameport->driver = vortex;
+	vortex->gameport->fuzz = 64;
+
+	vortex->gameport->read = vortex_game_read;
+	vortex->gameport->trigger = vortex_game_trigger;
+	vortex->gameport->cooked_read = vortex_game_cooked_read;
+	vortex->gameport->open = vortex_game_open;
+
+	gameport_register_port((struct gameport *)vortex->gameport);
+
+/*	printk(KERN_INFO "gameport%d: %s at speed %d kHz\n",
+		vortex->gameport->number, vortex->pci_dev->name, vortex->gameport->speed);
+*/
+	return 0;
+}
+
+int vortex_gameport_unregister(vortex_t *vortex) {
+	if (vortex->gameport != NULL)
+		gameport_unregister_port(vortex->gameport);
+	return 0;
+}
+
+#else /* GAMEPORT */
+int vortex_gameport_register(vortex_t *vortex) { return 0; }
+int vortex_gameport_unregister(vortex_t *vortex) { return 0; }
+#endif /* GAMEPORT */
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au88x0_mixer.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au88x0_mixer.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au88x0_mixer.c	(revision 91)
@@ -0,0 +1,28 @@
+/*
+ * Vortex Mixer support.
+ *
+ * There is much more than just the AC97 mixer...
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <linux/init.h>
+#include <sound/core.h>
+#include "au88x0.h"
+
+int __devinit snd_vortex_mixer(vortex_t *vortex) {
+    ac97_bus_t bus, *pbus;
+    ac97_t ac97;
+    int err;
+	
+    memset(&bus, 0, sizeof(bus));
+    bus.write = vortex_codec_write;
+    bus.read = vortex_codec_read;
+    if ((err = snd_ac97_bus(vortex->card, &bus, &pbus)) < 0)
+        return err;
+    memset(&ac97, 0, sizeof(ac97));
+    // Intialize AC97 codec stuff.
+    ac97.private_data = vortex;
+    return snd_ac97_mixer(pbus, &ac97, &vortex->codec);
+}
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au88x0_mpu401.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au88x0_mpu401.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au88x0_mpu401.c	(revision 91)
@@ -0,0 +1,98 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  Routines for control of MPU-401 in UART mode
+ *
+ *   Modified for the Aureal Vortex based Soundcards
+ *   by Manuel Jander (mjande@embedded.cl).
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <linux/init.h>
+#include <sound/core.h>
+#include <sound/mpu401.h>
+#include "au88x0.h"
+
+/* Check for mpu401 mmio support. */
+/* MPU401 legacy support is only provided as a emergency fallback *
+ * for older versions of ALSA. Its usage is strongly discouraged. */
+#ifndef MPU401_HW_AUREAL
+#define VORTEX_MPU401_LEGACY
+#endif
+
+/* Vortex MPU401 defines. */
+#define MIDI_CLOCK_DIV      0x61
+/* Standart MPU401 defines. */
+#define MPU401_RESET		0xff
+#define MPU401_ENTER_UART	0x3f
+#define MPU401_ACK		    0xfe
+
+int __devinit snd_vortex_midi(vortex_t *vortex) {
+	snd_rawmidi_t *rmidi;
+	int temp, mode;
+    mpu401_t *mpu;
+    int port;
+
+#ifdef VORTEX_MPU401_LEGACY
+    /* EnableHardCodedMPU401Port() */
+    /* Enable Legacy MIDI Interface port. */
+    port  = (0x03 << 5); /* FIXME: static address. 0x330 */
+	temp = (hwread(vortex->mmio, VORTEX_CTRL) & ~CTRL_MIDI_PORT) | CTRL_MIDI_EN | port;
+	hwwrite(vortex->mmio, VORTEX_CTRL, temp);
+#else
+	/* Disable Legacy MIDI Interface port. */
+	temp = (hwread(vortex->mmio, VORTEX_CTRL) & ~CTRL_MIDI_PORT) & ~CTRL_MIDI_EN;
+	hwwrite(vortex->mmio, VORTEX_CTRL, temp);	
+#endif
+	/* Mpu401UartInit() */
+    mode = 1;
+	temp = hwread(vortex->mmio, VORTEX_CTRL2) & 0xffff00cf;
+	temp |= (MIDI_CLOCK_DIV << 8) | ((mode >> 24) & 0xff) << 4;
+	hwwrite(vortex->mmio, VORTEX_CTRL2, temp);
+	hwwrite(vortex->mmio, VORTEX_MIDI_CMD, MPU401_RESET);
+	/* Set some kind of mode */
+	if (mode)
+		hwwrite(vortex->mmio, VORTEX_MIDI_CMD, MPU401_ENTER_UART);
+
+	/* Check if anything is OK. */
+	temp = hwread(vortex->mmio, VORTEX_MIDI_DATA);
+	if (temp != MPU401_ACK /*0xfe*/) {
+		printk(KERN_ERR "midi port doesn't acknowledge!\n");
+		return -ENODEV;
+	}
+	/* Enable MPU401 interrupts. */
+	hwwrite(vortex->mmio, VORTEX_IRQ_CTRL, hwread(vortex->mmio, VORTEX_IRQ_CTRL) | IRQ_MIDI);
+
+    /* Create MPU401 instance. */
+#ifdef VORTEX_MPU401_LEGACY
+    if ((temp = snd_mpu401_uart_new(vortex->card, 0, MPU401_HW_MPU401, 0x330, 0, 0, 0, &rmidi)) != 0) {
+	    hwwrite(vortex->mmio, VORTEX_CTRL, (hwread(vortex->mmio, VORTEX_CTRL) & ~CTRL_MIDI_PORT) & ~CTRL_MIDI_EN);
+        return temp;
+    }
+#else
+    port = (unsigned long)(vortex->mmio + (VORTEX_MIDI_DATA >> 2));
+    if ((temp = snd_mpu401_uart_new(vortex->card, 0, MPU401_HW_AUREAL, port, 1, 0, 0, &rmidi)) != 0) {
+	    hwwrite(vortex->mmio, VORTEX_CTRL, (hwread(vortex->mmio, VORTEX_CTRL) & ~CTRL_MIDI_PORT) & ~CTRL_MIDI_EN);
+        return temp;
+    }
+    mpu = snd_magic_cast(mpu401_t, rmidi->private_data, return -ENOMEM);
+    mpu->cport = (unsigned long)(vortex->mmio + (VORTEX_MIDI_CMD >> 2));
+#endif
+    vortex->rmidi = rmidi;
+	return 0;
+}
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au88x0_pcm.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au88x0_pcm.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au88x0_pcm.c	(revision 91)
@@ -0,0 +1,472 @@
+/*
+ * Vortex PCM ALSA driver.
+ *
+ * Supports ADB and WT DMA. Unfortunately, WT routing is still a
+ * mistery. To discover that, we need to disassemble the windoze
+ * driver too.
+ *
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include "au88x0.h"
+
+#define chip_t vortex_t
+#define VORTEX_PCM_TYPE(x) (x->name[40])
+
+/* hardware definition */
+static snd_pcm_hardware_t snd_vortex_playback_hw_adb = {
+    /*.info = */ (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_RESUME| SNDRV_PCM_INFO_PAUSE |
+             SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP_VALID),
+    /*.formats =*/ SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U8 |
+             SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW,
+    /*.rates = */SNDRV_PCM_RATE_CONTINUOUS,
+    /*.rate_min =*/ 5000,
+    /*.rate_max =*/ 48000,
+    /*.channels_min = */1,
+#ifdef CHIP_AU8830
+    /*.channels_max = */4,
+#else
+    /*    .channels_max = */2,
+#endif
+    /*.buffer_bytes_max = */0x10000,
+//    /*.buffer_bytes_max = */0x20000,
+    /*.period_bytes_min = */0x100,
+//    /*.period_bytes_min = */32,
+    /*.period_bytes_max = */0x1000,
+//    /*.period_bytes_max = */0x20000,
+    /*.periods_min = */1,
+    /*.periods_max = */64,
+//    /*.periods_max = */1024,
+    0
+};
+static snd_pcm_hardware_t snd_vortex_playback_hw_spdif = {
+    /*.info = */(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_RESUME| SNDRV_PCM_INFO_PAUSE |
+             SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP_VALID),
+    /*.formats =*/ SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U8 |
+             SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE | SNDRV_PCM_FMTBIT_MU_LAW | 
+             SNDRV_PCM_FMTBIT_A_LAW,
+    /*.rates = */SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
+    /*.rate_min = */32000,
+    /*.rate_max = */48000,
+    /*.channels_min = */1,
+    /*    .channels_max = */2,
+    /*.buffer_bytes_max = */0x10000,
+    /*.period_bytes_min = */0x100,
+    /*.period_bytes_max = */0x1000,
+    /*.periods_min = */1,
+    /*.periods_max = */64,
+    0
+};
+
+#ifndef CHIP_AU8810
+static snd_pcm_hardware_t snd_vortex_playback_hw_wt = {
+    /*.info = */(SNDRV_PCM_INFO_MMAP |
+             SNDRV_PCM_INFO_INTERLEAVED |
+             SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID),
+    /*.formats = */SNDRV_PCM_FMTBIT_S16_LE,
+    /*.rates = */SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_CONTINUOUS, // SNDRV_PCM_RATE_48000,
+    /*.rate_min = */8000,
+    /*.rate_max = */48000,
+    /*.channels_min = */1,
+    /*.channels_max = */2,
+    /*.buffer_bytes_max = */0x10000,
+    /*.period_bytes_min = */0x0400,
+    /*.period_bytes_max = */0x1000,
+    /*.periods_min = */1,
+    /*.periods_max = */64,
+    0
+};
+#endif
+
+inline int vortex_wtdma_getlinearpos(vortex_t *vortex, int wtdma) {
+    stream_t *dma = &vortex->dma_wt[wtdma];
+    int temp;
+
+    temp = hwread(vortex->mmio, VORTEX_WTDMA_STAT + (wtdma << 2));
+    //temp = (temp & POS_MASK) + (((temp>>WT_SUBBUF_SHIFT) & WT_SUBBUF_MASK)*(dma->cfg0&POS_MASK));
+	temp = (temp & POS_MASK) + ((dma->period_virt)*(dma->period_bytes));
+    return temp;
+}
+
+inline int vortex_adbdma_getlinearpos(vortex_t *vortex, int adbdma) {
+    stream_t *dma = &vortex->dma_adb[adbdma];
+    int temp;
+
+    temp = hwread(vortex->mmio, VORTEX_ADBDMA_STAT + (adbdma << 2));
+#ifdef DEBUG
+//    dprintf(("adbdma: %x, temp %x, per virt %x, per bytes %x",adbdma,temp,dma->period_virt, dma->period_bytes));
+#endif
+    temp = (dma->period_virt * dma->period_bytes) + (temp & POS_MASK);
+    return (temp);
+}
+
+/* open callback */
+static int snd_vortex_pcm_open(snd_pcm_substream_t *substream) {
+    vortex_t *vortex = snd_pcm_substream_chip(substream);
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    int err;
+
+    /* Force equal size periods */
+    if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+        return err;
+    /* Force DMA 32 bit alignment */
+    if ((err = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 4)) < 0)
+        return err;
+
+    if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT) {
+        if (VORTEX_PCM_TYPE(substream->pcm) == VORTEX_PCM_SPDIF) {
+            runtime->hw = snd_vortex_playback_hw_spdif;
+            switch (vortex->spdif_sr) {
+            case 32000:
+                runtime->hw.rates = SNDRV_PCM_RATE_32000;
+                break;
+            case 44100:
+                runtime->hw.rates = SNDRV_PCM_RATE_44100;
+                break;
+            case 48000:
+                runtime->hw.rates = SNDRV_PCM_RATE_48000;
+                break;
+            }
+        } else
+            runtime->hw = snd_vortex_playback_hw_adb;
+        substream->runtime->private_data = NULL;
+#ifdef DEBUG
+        dprintf(("vortex: adb"));
+#endif
+    }
+#ifndef CHIP_AU8810
+    else {
+        runtime->hw = snd_vortex_playback_hw_wt;
+        substream->runtime->private_data = NULL;
+#ifdef DEBUG
+        dprintf(("vortex: wt"));
+#endif
+    }
+#endif
+    return 0;
+}
+
+/* close callback */
+static int snd_vortex_pcm_close(snd_pcm_substream_t *substream) {
+    //vortex_t *chip = snd_pcm_substream_chip(substream);
+	stream_t *stream = (stream_t*)substream->runtime->private_data;
+	
+    // the hardware-specific codes will be here
+	if (stream != NULL) {
+    	stream->substream = NULL;
+		stream->nr_ch = 0;
+	}
+    substream->runtime->private_data = NULL;
+    return 0;
+}
+
+/* hw_params callback */
+static int snd_vortex_pcm_hw_params(snd_pcm_substream_t *substream, snd_pcm_hw_params_t *hw_params) {
+	chip_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	stream_t *stream = (stream_t*)(substream->runtime->private_data);
+	int err;
+
+	// Alloc buffer memory.
+	err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+#ifdef DEBUG
+	dprintf(("Vortex: allocated bytes %d", params_buffer_bytes(hw_params)));
+#endif
+	if (err < 0) {
+#ifdef DEBUG
+            dprintf(("Vortex: pcm page alloc failed!"));
+#endif
+		return err;
+        }
+#ifdef DEBUG
+	dprintf(("Vortex: periods %d, period_bytes %d, channels = %d", params_periods(hw_params),
+               params_period_bytes(hw_params), params_channels(hw_params)));
+#endif
+	// Make audio routes and config buffer DMA.
+	if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT) {
+		int dma, type = VORTEX_PCM_TYPE(substream->pcm);
+		/* Dealloc any routes. */
+		if (stream != NULL)
+			vortex_adb_allocroute(chip, stream->dma, stream->nr_ch, stream->dir, stream->type);
+		/* Alloc routes. */
+		dma = vortex_adb_allocroute(chip, -1, params_channels(hw_params), substream->stream, type);
+		if (dma < 0)
+			return dma;
+		stream = substream->runtime->private_data = &chip->dma_adb[dma];
+		stream->substream = substream;
+		/* Setup Buffers. */
+		vortex_adbdma_setbuffers(chip, dma, (u32)(runtime->dma_addr), params_period_bytes(hw_params), params_periods(hw_params));
+	}
+#ifndef CHIP_AU8810
+	else {
+		if (stream != NULL)
+			vortex_wt_allocroute(chip, substream->number, 0);
+		vortex_wt_allocroute(chip, substream->number, params_channels(hw_params));
+		stream = substream->runtime->private_data = &chip->dma_wt[substream->number];
+		stream->substream = substream;
+		vortex_wtdma_setbuffers(chip, substream->number, (u32)(runtime->dma_addr), params_period_bytes(hw_params), params_periods(hw_params));
+	}
+#endif
+	return 0;
+}
+
+/* hw_free callback */
+static int snd_vortex_pcm_hw_free(snd_pcm_substream_t *substream) {
+    chip_t *chip = snd_pcm_substream_chip(substream);
+	stream_t *stream = (stream_t*)(substream->runtime->private_data);
+	
+    // Delete audio routes.
+	if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT) {
+		if (stream != NULL)
+        	vortex_adb_allocroute(chip, stream->dma, stream->nr_ch, stream->dir, stream->type);
+	}
+#ifndef CHIP_AU8810
+	else {
+		if (stream != NULL)
+        	vortex_wt_allocroute(chip, stream->dma, 0);	
+	}
+#endif
+	substream->runtime->private_data = NULL;
+
+    return snd_pcm_lib_free_pages(substream);
+}
+
+/* prepare callback */
+static int snd_vortex_pcm_prepare(snd_pcm_substream_t *substream) {
+    vortex_t *chip = snd_pcm_substream_chip(substream);
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    stream_t *stream = (stream_t*)substream->runtime->private_data;
+	int dma = stream->dma, fmt, dir;
+
+    // set up the hardware with the current configuration.
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		dir = 1;
+	else
+		dir = 0;
+    fmt = vortex_alsafmt_aspfmt(runtime->format);
+	if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT) {
+        vortex_adbdma_setmode(chip, dma, 1, dir, fmt, 0/*?*/, 0);
+        vortex_adbdma_setstartbuffer(chip, dma, 0);
+		if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_SPDIF)
+        	vortex_adb_setsrc(chip, dma, runtime->rate, dir);
+	}
+#ifndef CHIP_AU8810
+	else {
+        vortex_wtdma_setmode(chip, dma, 1, dir, fmt, 0, 0);
+        // FIXME: Set rate (i guess using vortex_wt_writereg() somehow).
+        vortex_wtdma_setstartbuffer(chip, dma, 0);
+    }
+#endif
+    return 0;
+}
+
+/* trigger callback */
+static int snd_vortex_pcm_trigger(snd_pcm_substream_t *substream, int cmd) {
+    chip_t *chip = snd_pcm_substream_chip(substream);
+    stream_t *stream = (stream_t*)substream->runtime->private_data;
+	int dma = stream->dma;
+	
+    switch (cmd) {
+    case SNDRV_PCM_TRIGGER_START:
+        // do something to start the PCM engine
+#ifdef DEBUG
+        dprintf(("vortex: start %d", dma));
+#endif
+		if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT) {
+            stream->fifo_enabled = 1;
+            vortex_adbdma_startfifo(chip, dma);
+        } 
+#ifndef CHIP_AU8810		
+		else {
+            stream->fifo_enabled = 1;
+            vortex_wtdma_startfifo(chip, dma);
+        }
+#endif
+    	break;
+    case SNDRV_PCM_TRIGGER_STOP:
+        // do something to stop the PCM engine
+#ifdef DEBUG
+        dprintf(("vortex: stop %d", dma));
+#endif
+		if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT) {
+            stream->fifo_enabled = 0;
+            vortex_adbdma_stopfifo(chip, dma);
+        }
+#ifndef CHIP_AU8810
+        else {
+            stream->fifo_enabled = 0;
+            vortex_wtdma_stopfifo(chip, dma);
+        }
+#endif
+        break;
+    case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+#ifdef DEBUG
+        dprintf(("vortex: pause %d", dma));
+#endif
+		if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT)
+            vortex_adbdma_pausefifo(chip, dma);
+#ifndef CHIP_AU8810
+        else
+            vortex_wtdma_pausefifo(chip, dma);
+#endif
+        break;
+    case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+#ifdef DEBUG
+        dprintf(("vortex: resume %d", dma));
+#endif
+		if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT)
+            vortex_adbdma_resumefifo(chip, dma);
+#ifndef CHIP_AU8810
+        else
+            vortex_wtdma_resumefifo(chip, dma);
+#endif
+        break;
+    default:
+        return -EINVAL;
+    }
+    return 0;
+}
+
+/* pointer callback */
+static snd_pcm_uframes_t snd_vortex_pcm_pointer(snd_pcm_substream_t *substream) {
+    vortex_t *chip = snd_pcm_substream_chip(substream);
+    stream_t *stream = (stream_t*)substream->runtime->private_data;
+    int dma = stream->dma;
+    snd_pcm_uframes_t current_ptr = 0;
+
+    spin_lock(&chip->lock);
+    if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT)
+        current_ptr = vortex_adbdma_getlinearpos(chip, dma);
+#ifndef CHIP_AU8810
+    else
+        current_ptr = vortex_wtdma_getlinearpos(chip, dma);
+#endif
+#ifdef DEBUG
+//        dprintf(("vortex: pointer = 0x%x", current_ptr));
+#endif
+    spin_unlock(&chip->lock);
+    return (bytes_to_frames(substream->runtime, current_ptr));
+}
+
+/* operators */
+static snd_pcm_ops_t snd_vortex_playback_ops = {
+    /*.open =      */ snd_vortex_pcm_open,
+    /*.close =     */ snd_vortex_pcm_close,
+    /*.ioctl =     */ snd_pcm_lib_ioctl,
+    /*.hw_params = */ snd_vortex_pcm_hw_params,
+    /*.hw_free =   */ snd_vortex_pcm_hw_free,
+    /*.prepare =   */ snd_vortex_pcm_prepare,
+    /*.trigger =   */ snd_vortex_pcm_trigger,
+    /*.pointer =   */ snd_vortex_pcm_pointer,
+    0,0,0,0
+    //.page = snd_pcm_sgbuf_ops_page,
+};
+
+/*
+*  definitions of capture are omitted here...
+*/
+
+static char *vortex_pcm_prettyname[VORTEX_PCM_LAST] = {
+	"AU88x0 ADB",
+	"AU88x0 SPDIF",
+	"AU88x0 I2S",
+	"AU88x0 A3D",
+	"AU88x0 WT",
+};
+static char *vortex_pcm_name[VORTEX_PCM_LAST] = {
+	"AU88x0 adb",
+	"AU88x0 spdif",
+	"AU88x0 i2s",
+	"AU88x0 a3d",
+	"AU88x0 wt",
+};
+
+/* SPDIF kcontrol */
+static int snd_vortex_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) {
+	static char *texts[] = { "32000", "44100", "48000"};
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 3;
+	if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+		uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+static int snd_vortex_spdif_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) {
+	vortex_t *vortex = snd_kcontrol_chip(kcontrol);
+	
+	if (vortex->spdif_sr == 32000)
+		ucontrol->value.enumerated.item[0]=0;
+	if (vortex->spdif_sr == 44100)
+		ucontrol->value.enumerated.item[0]=1;
+	if (vortex->spdif_sr == 48000)
+		ucontrol->value.enumerated.item[0]=2;
+	return 0;
+}
+static int snd_vortex_spdif_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) {
+	vortex_t * vortex = snd_kcontrol_chip(kcontrol);
+	static unsigned int sr[3] = {32000, 44100, 48000};
+	
+	//printk("vortex: spdif sr = %d\n", ucontrol->value.enumerated.item[0]);
+	vortex->spdif_sr = sr[ucontrol->value.enumerated.item[0] % 3];
+	vortex_spdif_init(vortex, sr[ucontrol->value.enumerated.item[0] % 3], 1);
+	return 1;
+}
+static snd_kcontrol_new_t vortex_spdif_kcontrol __devinitdata = {
+    /*.iface = */ SNDRV_CTL_ELEM_IFACE_MIXER,
+    0,0,
+    /*	.name = */ "SPDIF SR",
+    /*	.index = */ 0,
+    /*	.access = */ SNDRV_CTL_ELEM_ACCESS_READWRITE,
+    0,
+    /*	.info = */ snd_vortex_spdif_info,
+    /*	.get = */ snd_vortex_spdif_get,
+    /*	.put = */ snd_vortex_spdif_put,
+    /*	.private_value = */ 0
+};
+
+/* create a pcm device */
+int __devinit snd_vortex_new_pcm(vortex_t *chip, int idx, int nr) {
+    snd_pcm_t *pcm;
+    int err, nr_capt;
+	//static int __devinit pcm_idx=0;
+    if ((chip == 0)||(idx < 0)||(idx>VORTEX_PCM_LAST))
+        return -ENODEV;
+
+	/* idx indicates which kind of PCM device. ADB, SPDIF, I2S and A3D share the 
+	 * same dma engine. WT uses it own separate dma engine whcih cant capture. */
+	if (idx == VORTEX_PCM_WT)
+		nr_capt = 0;
+	else
+		nr_capt = nr;
+	if ((err = snd_pcm_new(chip->card, vortex_pcm_prettyname[idx], idx, nr, nr_capt, &pcm)) < 0)
+    	return err;
+	strcpy(pcm->name, vortex_pcm_name[idx]);
+	chip->pcm[idx] = pcm;
+	// This is an evil hack, but it saves a lot of duplicated code.
+	VORTEX_PCM_TYPE(pcm) = idx;
+    pcm->private_data = chip;
+    /* set operators */
+    snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_vortex_playback_ops);
+    if (idx == VORTEX_PCM_ADB)
+    	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_vortex_playback_ops);
+    /* pre-allocation of buffers */
+    snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci_dev, pcm, 0x10000, 0x10000);
+//    snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci_dev, pcm, 0x10000, 0x20000);
+
+	if (VORTEX_PCM_TYPE(pcm) == VORTEX_PCM_SPDIF) {
+		snd_kcontrol_t *kcontrol;
+		
+		if ((kcontrol = snd_ctl_new1(&vortex_spdif_kcontrol, chip)) == NULL)
+			return -ENOMEM;
+		if ((err = snd_ctl_add(chip->card, kcontrol)) < 0)
+			return err;
+	}
+    return 0;
+}
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au88x0_synth.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au88x0_synth.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/au88x0_synth.c	(revision 91)
@@ -0,0 +1,2256 @@
+/*
+ * Someday its supposed to make use of the WT DMA engine
+ * for a Wavetable synthesizer.
+ */
+#include "au88x0.h"
+
+void vortex_fifo_setwtvalid(vortex_t *vortex, int fifo, int en);
+void vortex_connection_adb_mixin(vortex_t *vortex, int en, unsigned char channel,
+    unsigned char source, unsigned char mixin);
+void vortex_connection_mixin_mix(vortex_t *vortex, int en, unsigned char mixin, unsigned char mix, int a);
+void vortex_fifo_wtinitialize(vortex_t *vortex, int fifo, int j);
+
+/* WT */
+
+void vortex_wt_setstereo(vortex_t *vortex, u32 wt, u32 stereo) {
+	int temp;
+	
+	temp = hwread(vortex->mmio, 0x80 + ((wt >> 0x5)<< 0xf) + (((wt & 0x1f) >> 1) << 2));
+	temp = (temp & 0xfe) | (stereo & 1);
+	hwwrite(vortex->mmio, 0x80 + ((wt >> 0x5)<< 0xf) + (((wt & 0x1f) >> 1) << 2), temp);
+}
+
+void vortex_wt_setdsout(vortex_t *vortex, u32 wt, int en) {
+#ifndef CHIP_AU8820
+	int temp;
+
+	temp = hwread(vortex->mmio, ((wt >> 0x5)<< 0xf) + 0x8);
+	if (en)
+		temp |= (1 << (wt & 0x1f));
+	else
+		temp &= (1 << ~(wt & 0x1f));
+	hwwrite(vortex->mmio, ((wt >> 0x5)<< 0xf) + 0x8, temp);
+#endif
+}
+
+// WT routing is still a mistery.
+int  vortex_wt_allocroute(vortex_t *vortex, int dma, int nr_ch) {
+    //FIXME: WT audio routing.
+	if (nr_ch) {
+		vortex_fifo_wtinitialize(vortex, dma, 1);
+		vortex_wt_setstereo(vortex, dma, nr_ch-1);
+	} else
+		vortex_fifo_setwtvalid(vortex, dma, 0);
+	vortex_wt_setdsout(vortex, dma, 1);
+    return 0;
+}
+void vortex_wt_connect(vortex_t *vortex, int en, unsigned char mixers[]) {
+	
+	vortex->mixwt[0] = vortex_adb_checkinout(vortex, vortex->fixed_res, en, VORTEX_RESOURCE_MIXIN);
+	vortex->mixwt[1] = vortex_adb_checkinout(vortex, vortex->fixed_res, en, VORTEX_RESOURCE_MIXIN);
+	
+	vortex_connection_adb_mixin(vortex, en, 0x11, ADB_WTOUT(0), vortex->mixwt[0]);
+	vortex_connection_adb_mixin(vortex, en, 0x11, ADB_WTOUT(1), vortex->mixwt[1]);
+	vortex_connection_mixin_mix(vortex, en, vortex->mixwt[0], mixers[0], 0);
+	vortex_connection_mixin_mix(vortex, en, vortex->mixwt[1], mixers[1], 0);
+	if (VORTEX_IS_QUAD(vortex)) {
+		vortex_connection_mixin_mix(vortex, en, vortex->mixwt[0], mixers[2], 0);
+		vortex_connection_mixin_mix(vortex, en, vortex->mixwt[1], mixers[3], 0);		
+	}
+}
+
+/* Read WT Register */
+int  vortex_wt_GetReg(vortex_t *vortex, char a, int *b) {
+	int eax, esi;
+	
+	if (a != 4) {
+		if (a == 7)
+			return 0;
+		return hwread(vortex->mmio, (*b << 0xf) + 0x10);
+	}
+	esi = b[1];
+	eax = ((((a & 0x1f) + b[1]) / 32) & 0xff) << 0xb;
+	if (esi < 0)
+		esi = ((esi-1)|0x0FFFFFFE0)+1;
+	esi &= 0xff;
+	hwread(vortex->mmio, ((eax + esi) << 4) + 0x20c);
+		
+	return 8;
+}
+
+/*
+ WT hardware abstraction layer generic register interface.
+ a: type of register or register set to be accessed.
+ b: Register offset
+ c: Register value (or values) to be written.
+*/
+int  vortex_wt_SetReg2(vortex_t *vortex, unsigned char a, int b, unsigned short c) {
+	int eax, edx;
+	
+	if (b >= NR_WT)  // 0x40 -> NR_WT
+		return 0;
+	
+	/* cdq: 64 bit sign extender */
+	eax = b;
+	if (eax<0)
+		edx = 0xffffffff & 0x1f;
+	else
+		edx = 0;
+	eax += edx;
+	edx = b & 0x8000001f;
+	eax = (eax / 32) | ((char)b);
+	if (eax < 0)
+		edx = ((edx-1)|0x0FFFFFFE0)+1;
+	
+	// esp+4 = edx & 0xff
+	
+	if ((eax & 0xff) >= 2)
+		return 0;
+	
+	if ((a & 0xff) - 0x20) {
+		if ((a & 0xff) - 0x21) 
+			return 0;
+		eax = ((((b & 0xff) << 0xb) + (edx & 0xff)) << 4) + 0x208;
+	} else {
+		eax = ((((b & 0xff) << 0xb) + (edx & 0xff)) << 4) + 0x20a;
+	}
+	hwwrite(vortex->mmio, eax, c);
+	return 1;
+}
+
+
+/*public: void __thiscall CWTHal::SetReg(unsigned char,int,unsigned long) */
+int  vortex_wt_SetReg(vortex_t *vortex, unsigned char a, int b, unsigned long c) {
+	// a : type
+	// b : voice
+	// c : data
+	
+	int  ecx, edx, esp4, eax;
+	
+	if (b >= NR_WT)  // 0x40 -> NR_WT
+		return 0;
+
+#if 0 //original_code	
+	eax = ecx = b;
+	/* cdq: 64 bit sign extender */
+	if (eax<0)
+		edx = 0xffffffff;
+	else
+		edx = 0;
+	eax += edx  & 0x1f;
+	edx = ecx & 0x8000001f;
+	eax = (eax / 32) | ((char)b);
+	// This "if" semes to be useless ...
+	if (eax < 0)
+		edx = ((edx-1)|0x0FFFFFFE0)+1;
+	
+	esp4 = (char)edx;
+	
+	// eax is the WT bank. AU8830 has 2 banks: 0 and 1.
+	if ((char)eax >= 2)
+		return 0;
+	
+#else //rewritting_code
+	esp4 = b & 0x1f;
+	b = b >> 5;
+	
+#endif
+	
+	if (a > 0xc)
+		return 0;
+	
+	switch (a) {
+		case 0 :
+			eax = ((b << 0xd) + esp4) << 2;
+			hwwrite(vortex->mmio, 0x180 + eax, c);
+			return 0xc;
+		break;
+		case 1 :
+			edx = ((b & 0xff) << 0xb) + (esp4 & 0xff);
+			hwwrite(vortex->mmio, 0x20 + (edx << 4), c);
+			return 0xc;			
+		break;
+		case 2 :
+			eax = ((b & 0xff) << 0xb) + (esp4 & 0xff);
+			hwwrite(vortex->mmio, 0x204 + (eax << 4), c);
+			return 0xc;
+		break;
+		case 3 :
+			eax = ((b & 0xff) << 0xb) + (esp4 & 0xff);
+			hwwrite(vortex->mmio, 0x208 + (eax << 4), c);
+			return 0xc;
+		break;
+		case 4 :
+			eax = ((b & 0xff) << 0xb) + (esp4 & 0xff);
+			hwwrite(vortex->mmio, 0x20c + (eax << 4), c);
+			return 0xc;
+		break;
+		case 6 :
+			edx = ((b & 0xff) << 0xd) + (esp4 & 0xff);
+			hwwrite(vortex->mmio, 0x100 + (edx << 2), c);
+			return 0xc;			
+		break;
+		case 0xb : 
+			{
+				int ebx, esi;
+				// FIXME: Here is something wrong ... (check admaud30.asm)
+				edx = ((b & 0xff) << 0xb) + (esp4 & 0xff);
+				ebx = b + esp4;
+				esi = ebx << 4;
+				
+				hwwrite(vortex->mmio, 0x40c + esi, c);
+				hwwrite(vortex->mmio, 0x408 + esi, c);
+				hwwrite(vortex->mmio, 0x404 + esi, c);
+				hwwrite(vortex->mmio, 0x400 + esi, c);
+				return 0xc;			
+			}
+		break;
+		case 5 :
+			ecx = (b << 0xf) + 0x4;
+		break;
+		case 8 :
+			ecx = (b << 0xf) + 0x14;
+		break;
+		case 9 :
+			ecx = (b << 0xf) + 0xc;
+		break;
+		case 0xa :
+			ecx = (b << 0xf) + 0x0;
+		break;
+		case 0xc :
+			ecx = (b << 0xf) + 0x8;
+		break;
+		default:
+			return 0;
+		break;
+	}
+	eax = ecx;
+	hwwrite(vortex->mmio, eax, c);
+	return 1;
+}
+
+void vortex_wt_WriteReg(vortex_t *vortex, int wt, char addr, int data) {
+	hwwrite(vortex->mmio, ((wt << 0xf) + wt) + addr, data);
+}
+void vortex_wt_ReadReg(vortex_t *vortex, int wt, char addr, int *data) {
+	*data = hwread(vortex->mmio, ((wt << 0xf) + wt) + addr);
+}
+
+void vortex_wt_InitializeWTRegs(vortex_t * vortex) {
+	int ebx=0, var4, var8, varc, var10=0, edi;
+	
+	var10 &= 0xe3;
+	var10 |= 0x22;
+	var10 &= 0xFFFFFEBF;
+	var10 |= 0x80;
+	var10 |= 0x2;
+	var10 &= 0xfffffffe;
+	var10 &= 0xfffffffb;
+	var10 |= 0x18;
+	var4 = 0x10000000;
+	varc = 0x00830000;
+	var8 = 0x00830000;
+	
+	for (edi=0; edi<2; edi++) {
+		vortex_wt_SetReg(vortex, 0xc, edi, ebx);
+		vortex_wt_SetReg(vortex, 0xa, edi, var10);
+		vortex_wt_SetReg(vortex, 0x9, edi, var4);
+		vortex_wt_SetReg(vortex, 0x8, edi, varc);
+		vortex_wt_SetReg(vortex, 0x5, edi, var8);
+	}
+	
+	for (edi=0; edi<NR_WT; edi++)  {
+		vortex_wt_SetReg(vortex, 0x4, edi, ebx);
+		vortex_wt_SetReg(vortex, 0x3, edi, ebx);
+		vortex_wt_SetReg(vortex, 0x2, edi, ebx);
+		vortex_wt_SetReg(vortex, 0x1, edi, ebx);
+		vortex_wt_SetReg(vortex, 0xb, edi, ebx);
+	}
+	var10 |= 1;
+	for (edi=0; edi<2; edi++)
+		vortex_wt_SetReg(vortex, 0xa, edi, var10);
+}
+/* Extract of CAdbTopology::SetVolume(struct _ASPVOLUME *) */
+void vortex_wt_SetVolume(vortex_t *vortex, int wt, int a, int vol[]) {
+	int x=0, y=0;
+	// CAdbTopology->this_1E4, CAdbTopology->this_1E8
+	vortex_wt_WriteReg(vortex, wt, 0x200 + (a<<4), (x & 0xff) << 0x10);
+	vortex_wt_WriteReg(vortex, wt, 0x204 + (a<<4), ((x & 0xff) << 0x10) | 1);
+	// In this case x semes to a set of 7 bit values.
+	// y = CAdbTopology->this_1F0
+	vortex_wt_WriteReg(vortex, wt, 0x20c + (a<<4), y);	
+}
+
+/* The rest of this file is only for information purpose. */
+#if 0
+void Asp4SynthTopology::Asp4SynthTopology(class	CResource * res, class Asp4Topology * asp) {
+	this00 = res;
+	this04 = asp;
+	
+	this08 = 0xff; // MixIn 0x65
+	this0c = 0xff; // MixIn 0x66
+	this10 = 0xff; // MixIn 0x67
+	this14 = 0xff; // MixIn 0x68
+	this18 = 0xff; // MixIn 0x6b
+	this1c = 0xff; // MixIn 0x6c
+	this20 = 0xff; // MixIn 0x6d
+	this24 = 0xff; // MixIn 0x6e
+	this28 = 0xff; // MixIn 0x6f
+	this2c = 0xff; // MixIn 0x70
+	this30 = 0; // MixOut 0x8b
+	this34 = 0; // MixOut 0x8c
+	this38 = 0; // MixOut 0x81
+	this3c = 0; // MixOut 0x82
+	this40 = 0; // MixOut 0x8f
+	this44 = 0; // MixOut 0x90
+	this48 = 0;
+	this4c = 0;
+	this50 = 0; // ADB DMA
+	this54 = 0; // ADB DMA
+	this58 = 0;
+	this5c = 0;
+	this60 = 0;
+	this64 = 0;
+	thisa0 = 0;
+	
+	//GetMixObject@CResource@@QAEJW4_eReservedFor@@PAPAVCAsp4Mix@@K@
+}
+
+void Asp4SynthTopology::Create(int a, int b, int c, int d) {
+	
+	eax = Asp4SynthTopology::GetMixerResources(a, b)
+	if (eax == 0)
+		return eax;
+	if ((a==0)||(b==0))
+		return eax;
+	/* Here goes a condition thats allways true ... */
+	if (eax) {
+		eax = _OsLibAllocate@12(0x4000, 0, 0x46425846, 8);
+		*esi = eax;
+		eax = __imp__IoAllocateMdl@20(eax, 0x4000, 0,0,0);
+		*(esi + 4) = eax;
+		__imp__MmBuildMdlForNonPagedPool@4(eax);
+	} else
+		esi = 0;
+	this_60 = esi;
+	if (esi == 0)
+		return eax;
+	CResource = this_00;
+	eax = CResource::GetAdbDmaObject(&this_50, 2);
+	if (eax == 0)
+		return 0;
+	esi = operator new(8);
+	if (esi) {
+		eax = _OsLibAllocate@12(0x4000, 0, 0x46425846, 8);
+		*esi = eax;
+		eax = __imp__IoAllocateMdl@20(eax, 0x4000, 0,0,0);
+		*(esi + 4) = eax;
+		__imp__MmBuildMdlForNonPagedPool@4(eax);
+	} else
+		esi = 0;
+	this_64 = esi;
+	if (esi == 0)
+		return eax;
+	CResource = this_00;
+	eax = CResource::GetAdbDmaObject(&this_54, 2);
+	if (eax == 0)
+		return 0;
+	return 1;
+}
+
+void ~Asp4SynthTopology(void) {
+	
+	asp = this04;
+	edi = asp->this74; /* CAsp4HwIO */
+	asp->this9c = 0x3e8;
+	
+	if (this38  && (this08 != 0xff))
+		asp->this64 = ReadDWORD(VORTEX_WT_BASE + ((this38->this08 << 5)+this08)<<2) & 0xff;
+	 (0x420 << 7)
+	if (this3c  && (this0c != 0xff))
+		asp->this68 = ReadDWORD(VORTEX_WT_BASE + ((this3c->this08 << 5)+this0c)<<2) & 0xff;
+	
+	if (this08 != 0xff) {
+#ifdef CHIP_AU8830
+		Route(0, 0x11, 0x62, this08 + 0x50);
+#elifdef CHIP_AU8820
+		Route(0, 0x11, 0x00, this08);
+#endif
+		if (this38)
+			DisableInput(this38, this08, 0);
+		if (this40)
+			DisableInput(this40, this08, 0);
+	}
+	if (this0c != 0xff) {
+#ifdef CHIP_AU8830
+		Route(0, 0x11, 0x62, this0c + 0x50);
+#elifdef CHIP_AU8820
+		Route(0, 0x11, 0x00, this0c);
+#endif
+		if (this3c)
+			DisableInput(this3c, this0c, 0);
+		if (this44)
+			DisableInput(this44, this0c, 0);
+	}
+#ifdef CHIP_AU8830
+	if (this18 != 0xff) {
+		Route(0, 0x11, 0xA2, this18 + 0x50);
+		if (this38)
+			DisableInput(this38, this18, 0);
+		if (this40)
+			DisableInput(this40, this18, 0);
+	}
+	if (this1c != 0xff) {
+		Route(0, 0x11, 0xA2, this1c + 0x50);
+		if (this38)
+			DisableInput(this38, this1c, 0);
+		if (this40)
+			DisableInput(this40, this1c, 0);
+	}
+#endif
+	if (this10 != 0xff) {
+		if (this38)
+			DisableInput(this38, this10, 0);
+		if (this40)
+			DisableInput(this40, this10, 0);
+	}
+	if (this14 != 0xff) {
+		if (this38)
+			DisableInput(this38, this14, 0);
+		if (this40)
+			DisableInput(this40, this14, 0);
+	}
+	if (this28 != 0xff) {
+		if (GetInputChannelRefCount(this28) == 1) {
+			Route(0, 0x11, 0x64, this28 + 0x50);
+			if (this34)
+				DisableInput(this34, this28, 0);
+		}
+	}
+	if (this2c != 0xff) {
+		if (GetInputChannelRefCount(this2c) == 1) {
+			Route(0, 0x11, 0xa4, this2c + 0x50);
+			if (this34)
+				DisableInput(this34, this2c, 0);
+		}
+	}
+	if (this20 != 0xff) {
+		if (GetInputChannelRefCount(this20) == 1) {
+			Route(0, 0x11, 0x65, this20 + 0x50);
+			if (this30)
+				DisableInput(this30, this20, 0);
+		}
+	}
+	if (this24 != 0xff) {
+		if (GetInputChannelRefCount(this24) == 1) {
+			Route(0, 0x11, 0xa5, this24 + 0x50);
+			if (this30)
+				DisableInput(this30, this24, 0);
+		}
+	}
+	
+	if (this50) {
+		SetADBValid(asp->thiscc, this50->this04, 0);
+		if (this30 && this34)
+			Asp4Topology::Connection(this04, 0, 0x11, (CAsp4Mix *) this34, (int) this30, (CAsp4AdbDma *)this50)
+	}
+	if (this54) {
+		SetADBValid(asp->thiscc, this54->this04, 0);
+		if (this10 && this14)
+			Asp4Topology::Connection(this04, 0, 0x11, (CAsp4Mix *) this14, (int) this10, (CAsp4AdbDma *)this54)
+	}
+	Asp4SynthTopology::ReleaseMixerResources(void);
+	if (this50)
+		CAsp4DmaChannel::CheckIn(this50);
+	if (this54)
+		CAsp4DmaChannel::CheckIn(this54);
+	free(thsis60);
+	free(tthis64);
+}
+
+// admaud30.asm: line 67609
+void Asp4Topology::ConfigureWT(int arg_0 ,int arg_4 ,int arg_8) {
+	// ...
+	ebx = arg_0;
+	// ...
+	
+	arg_0 = some_weird_operations(ebx) + 0xA4;
+	Asp4Topology::Route((int)ebp ,(uchar) 0x11, (uchar) arg_0, (uchar) (*edi + 0x50));
+	arg_0 = some_weird_operations(ebx) + 0xA5;
+	Asp4Topology::Route((int)ebp ,(uchar) 0x11, (uchar) arg_0, (uchar) (*(esi+ebx*4+0x16C) + 0x50));
+
+}
+
+int CResource::GetMixObject(enum	_eReservedFor, class CAsp4Mix **CAsp4Mix,unsigned long) {
+	
+	if (thisc4 == 0)
+		return 0x92EB0014;
+	if (CAsp4Mix == 0)
+		return 0x80070057;
+	
+	*CAsp4Mix = CAsp4Mixer::GetMixObject(_eReservedFor, ulong 0xFFFFFFFF)
+	
+	eax = ~CAsp4Mix;
+	
+	neg	eax
+	sbb	eax, eax
+	and	eax, 6D14FFF6h
+	add	eax, 92EB000Ah
+	
+	return eax;
+}
+
+CAsp4Mix * CAsp4Mixer::GetMixObject(_eReservedFor, ulong a) {
+	//CAsp4Mixer->this188 CMixArray.
+	
+	var04 = CMixArray::GetOutput(_eReservedFor, ulong a);
+	if (var04 == 0xff)
+		return 0;
+	var_A40 = *(this_188 + var04*0xc + 0x188);
+	if (var_a40 == 1) {	
+		CMixArray::GetPropertyIndexes(_eReservedFor, (_mixIndex *)&var_A1C);
+		
+		for (var08=0; var08<var_A1C; var08++) {
+			
+			eax = CMixArray::GetPendingProperty((ulong)var08*4+var_A18, (_mixProperty *)&var_A34);
+			if (eax == 0)
+				return 0;
+			var_A38 == 0;
+			var_A48 = var_A34;
+			if (var_A48 == 1) {
+				CAsp4Mix = var04*0x18 + this_08;
+				CAsp4Mix::SetVolume((ulong) var_A28);
+				var_A38 = 1;
+			}
+			if (var_A48 == 0) {
+				var_A3C = CMixArray::SearchInput(_eReservedFor,(ulong) 0x0FFFFFFFF);
+				if (var_a3c != 0xff) {
+					CAsp4Mix = var04*0x18 + this_08;
+					CAsp4Mix::SetInputVolume(var_A3C, var_A28, var_A24, 0);
+					var_A38 = 1;
+					
+				}
+			}
+			if (var_A38 == 0) {
+				CMixArray = this_188;
+				CMixArray::DeletePendingProperty((_mixProperty *)&var_A34);
+			}
+		}
+	}
+	return (var04*0x18 + &this_08);
+}
+
+void CMixArray::GetOutput(enum  _eReservedFor,unsigned long a) {
+	
+	eax = CMixArray::GiveIndex(_eReservedFor, ulong a);
+	if (eax != 0FFFFFFFF) {
+		*(eax*3*4 + CMixArray->this_188)++;
+		return;
+	}
+	eax = 0;
+	ecx = &this_180;
+	{
+		if (*ecx != 0xff) {
+			ecx = &this_00 + eax*3*4;
+			*(ecx + 0x184) = a;
+			*((eax*3 + 0x60)*4 + this_00) = _eReservedFor;
+			*(ecx + 0x188)++;
+			return;
+		}	
+		eax++;
+		ecx += 0xc;
+	} while (eax < 0x10);
+	return 0xff;
+}
+
+int CMixArray::GiveIndex(enum  _eReservedFor, unsigned long a) {
+	eax = 0;
+	if (_eReservedFor < 0x80) {
+		ecx = &this00;
+		{
+			if ((_eReservedFor == *ecx) && (a == *(ecx+4)))
+				return eax;
+			ecx += 0xc;
+			eax++;
+		} while(eax < 0x20);
+	} else {
+		ecx = &this184;
+		{
+			if ((_eReservedFor == *(ecx-4)) && (a == *ecx))
+				return eax;
+			ecx += 0xc;
+			eax++;
+		} while(eax < 0x10);	
+	}
+	eax |= 0xFFFFFFFF;
+}
+
+
+
+
+// Voice related stuff.
+void vortex_AspSynth_VoiceInit(void) {
+	int i;
+	struct voice_t *voice; 
+	//Asp4Synth::ResetPerformanceStats(void);
+	//Asp4Synth::ProfileSynth(void);
+	
+	// Hardware Voices.
+	for (i=0; i<NR_WT; i++) {
+		voice = (this_bc + i*0x198);
+		voice->this_b8 = 0xffff;
+		voice->this_114 = 0xffff;
+		
+		voice->this_174 = 0;
+		voice->this_5d = 0;
+		voice->this_60 = 0;
+		voice->this_190 = 0;
+		voice->this_58 = i;
+		voice->this_62 = 0;
+		voice->this_4c = 0;
+		voice->this_50 = 0;
+		
+		if (voice->this_180) {
+			vortex_SetReg(vortex, 1, i, 0);
+			vortex_SetReg(vortex, 2, i, 0);
+			//CSynth::Stop(void);
+			//CSynth::Release(void);
+			voice->this_180 = 0;
+			vortex_SetReg(vortex, 0, i, 1);
+		}
+	}
+	// Software voices ...
+	
+	//CDmDlsDownloadManager::DownloadInstrument(void * *,void	* *,_DMUS_DOWNLOADINFO *,void *	* const,void *);
+}
+
+void Asp4Synth::MuteVoices(uchar a,ulong b) {
+	int ebx=0;
+	if (ebx != this_180) {
+		vortex_SetReg(vortex, 6, i, 1);
+		vortex_SetReg(vortex, 1, i, ebx);
+		vortex_SetReg(vortex, 2, i, ebx);
+		CSynth::Stop(void);
+		CSynth::Release(void);
+		this_180 = ebx;
+	}
+	
+}
+void Asp4Synth::ResetControllers(unsigned char) {
+	
+}
+
+int CAdmHilObject::wtMidiDeviceOpen(void) {
+	if (this_2c == 0)
+		return 0;
+	this_2c->this52794 = 0;
+	if (this_2c->this52790)
+		return 0;
+	this_2c->this52790 = 1;
+	this_2c->Asp4Synth::VoiceInit(void);
+	this_2c->Asp4Synth::BankChange((CAbeSndFont *)this_2c->this_08, (int) 0);
+	if (this_2c) {
+		var_c = __imp__RtlConvertLongToLargeInteger@4(0x0FFFFB1E0);
+		__imp__KeDelayExecutionThread@12(0, 0, *var_c);
+		this_2c->Asp4Synth::Close((int) 0);
+		eax = this_2c->Asp4Synth::Open(void);
+		if (eax == 0)
+			return 0C0000001;
+		this_2c->this_53888 = 1;
+	}
+	// Some "repe" things i dont understand. Basically semes to be initializers.
+	
+}
+void Asp4MidiEvent::handler(void *) {
+	switch (a) {
+		
+		case x:
+			if (this_60)
+				CWTHal::SetReg((uchar) 6, (int) this_58, (ulong) 1);
+		break;
+		
+	}
+	
+}
+
+void CAsp4Core::CreateSynthBuffer(_ASPWAVEFORMAT *,CSynth * *) {
+	
+	
+}
+
+void fragment_of_wtcallback(void) { 
+	vortex_SetReg(vortex, 0xb, i, 0);
+	vortex_SetReg2(vortex, 0x21, i, 0);
+	CSynth::Start(void);
+	// ..
+	vortex_SetReg(vortex, 1, i, 0x50000000);
+	vortex_SetReg(vortex, 2, i, 0x50000000);
+	// ..
+	vortex_SetReg(vortex, 5, 0, 0x8800);
+	vortex_SetReg(vortex, 5, 1, 0x8800);
+	
+}
+#endif
+
+/*
+CSynth constructor
+
+; public: class	Asp4SynthTopology * __thiscall Asp4Topology::AllocSynth(class CResource	*)
+
+?AllocSynth@Asp4Topology@@QAEPAVAsp4SynthTopology@@PAVCResource@@@Z proc near
+					; CODE XREF: CAsp4Core::SetWavetableTopology(int)+3Fp
+					; .text:0002565Dp
+
+var_14		= dword	ptr -14h
+var_10		= dword	ptr -10h
+var_C		= dword	ptr -0Ch
+var_8		= dword	ptr -8
+var_4		= dword	ptr -4
+arg_0		= dword	ptr  4
+
+		sub	esp, 14h
+		push	ebx
+		push	ebp
+		push	esi
+		push	edi
+		mov	edi, ecx
+		push	70h
+		call	??2@YAPAXI@Z	; operator new(uint)
+
+		add	esp, 4
+		test	eax, eax
+		jz	short loc_2F3BA
+
+		mov	ebp, [esp+24h+arg_0]
+		push	edi
+		push	ebp
+		mov	ecx, eax
+		call	??0Asp4SynthTopology@@QAE@PAVCResource@@PAVAsp4Topology@@@Z ; Asp4SynthTopology::Asp4SynthTopology(CResource *,Asp4Topology *)
+
+		mov	esi, eax
+		test	esi, esi
+		jnz	short loc_2F3C6
+
+
+loc_2F3BA:				; CODE XREF: Asp4Topology::AllocSynth(CResource	*)+15j
+		pop	edi
+		pop	esi
+		pop	ebp
+		xor	eax, eax
+		pop	ebx
+		add	esp, 14h
+		retn	4
+
+; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
+
+loc_2F3C6:				; CODE XREF: Asp4Topology::AllocSynth(CResource	*)+28j
+		mov	eax, [ebp+3A4h]
+		mov	ecx, [ebp+3ACh]
+		mov	ebx, [ebp+3A8h]
+		lea	edx, [esp+24h+var_14]
+		push	edx
+		push	offset aA_0	; "A"
+		push	offset asc_814A0 ; "s"
+		push	80000002h
+		mov	[esp+34h+var_4], eax
+		mov	[esp+34h+arg_0], ecx
+		call	_AspReadRegDwordValue@16
+
+		test	eax, eax
+		jz	short loc_2F403
+
+		mov	eax, [esp+24h+var_14]
+		jmp	short loc_2F41C
+
+; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
+
+loc_2F403:				; CODE XREF: Asp4Topology::AllocSynth(CResource	*)+6Bj
+		cmp	ebx, 0C8h
+		jb	short loc_2F41A
+
+		mov	eax, [esp+24h+arg_0]
+		test	eax, eax
+		jz	short loc_2F41A
+
+		mov	eax, 1
+		jmp	short loc_2F41C
+
+; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
+
+loc_2F41A:				; CODE XREF: Asp4Topology::AllocSynth(CResource	*)+79j
+					; Asp4Topology::AllocSynth(CResource *)+81j
+		xor	eax, eax
+
+loc_2F41C:				; CODE XREF: Asp4Topology::AllocSynth(CResource	*)+71j
+					; Asp4Topology::AllocSynth(CResource *)+88j
+		lea	ecx, [esp+24h+var_14]
+		mov	[edi+88h], eax
+		push	ecx
+		push	offset asc_81514 ; "S"
+		push	offset asc_81528 ; "s"
+		push	80000002h
+		call	_AspReadRegDwordValue@16
+
+		test	eax, eax
+		jz	short loc_2F44B
+
+		mov	edx, [esp+24h+var_14]
+		mov	[edi+8Ch], edx
+		jmp	short loc_2F46A
+
+; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
+
+loc_2F44B:				; CODE XREF: Asp4Topology::AllocSynth(CResource	*)+ADj
+		cmp	ebx, 0C8h
+		jb	short loc_2F462
+
+		mov	eax, [esp+24h+arg_0]
+		test	eax, eax
+		jz	short loc_2F462
+
+		mov	eax, 1
+		jmp	short loc_2F464
+
+; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
+
+loc_2F462:				; CODE XREF: Asp4Topology::AllocSynth(CResource	*)+C1j
+					; Asp4Topology::AllocSynth(CResource *)+C9j
+		xor	eax, eax
+
+loc_2F464:				; CODE XREF: Asp4Topology::AllocSynth(CResource	*)+D0j
+		mov	[edi+8Ch], eax
+
+loc_2F46A:				; CODE XREF: Asp4Topology::AllocSynth(CResource	*)+B9j
+		lea	eax, [esp+24h+var_14]
+		push	eax
+		push	offset asc_8159C ; "H"
+		push	offset asc_815B8 ; "s"
+		push	80000002h
+		call	_AspReadRegDwordValue@16
+
+		test	eax, eax
+		jz	short loc_2F493
+
+		mov	ecx, [esp+24h+var_14]
+		mov	[edi+90h], ecx
+		jmp	short loc_2F49D
+
+; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
+
+loc_2F493:				; CODE XREF: Asp4Topology::AllocSynth(CResource	*)+F5j
+		mov	dword ptr [edi+90h], 1
+
+loc_2F49D:				; CODE XREF: Asp4Topology::AllocSynth(CResource	*)+101j
+		lea	edx, [esp+24h+var_14]
+		push	edx
+		push	offset aG_15	; "G"
+		push	offset asc_81640 ; "s"
+		push	80000002h
+		call	_AspReadRegDwordValue@16
+
+		test	eax, eax
+		jz	short loc_2F4C8
+
+		mov	eax, [esp+24h+var_14]
+		lea	ebx, [edi+94h]
+		mov	[ebx], eax
+		jmp	short loc_2F4F9
+
+; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
+
+loc_2F4C8:				; CODE XREF: Asp4Topology::AllocSynth(CResource	*)+128j
+		cmp	[esp+24h+var_4], 6
+		jnb	short loc_2F4ED
+
+		cmp	ebx, 0C8h
+		jb	short loc_2F4ED
+
+		mov	eax, [esp+24h+arg_0]
+		test	eax, eax
+		jz	short loc_2F4ED
+
+		lea	ebx, [edi+94h]
+		mov	dword ptr [ebx], 1
+		jmp	short loc_2F4F9
+
+; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
+
+loc_2F4ED:				; CODE XREF: Asp4Topology::AllocSynth(CResource	*)+13Dj
+					; Asp4Topology::AllocSynth(CResource *)+145j ...
+		lea	ebx, [edi+94h]
+		mov	dword ptr [ebx], 0
+
+loc_2F4F9:				; CODE XREF: Asp4Topology::AllocSynth(CResource	*)+136j
+					; Asp4Topology::AllocSynth(CResource *)+15Bj
+		xor	eax, eax
+		mov	dword ptr [edi+9Ch], 3E8h
+		mov	[esp+24h+var_10], eax
+		mov	[esp+24h+var_C], eax
+		cmp	dword ptr [edi+64h], 0FFFFFFFFh
+		jnz	short loc_2F564
+
+		lea	ecx, [esp+24h+var_10]
+		push	0FFFFFFFFh
+		push	ecx
+		push	81h
+		mov	ecx, ebp
+		call	?SearchMixerOutputChannel@CResource@@QAEJW4_eReservedFor@@PAKK@Z ; CResource::SearchMixerOutputChannel(_eReservedFor,ulong *,ulong)
+
+		test	eax, eax
+		jnz	short loc_2F564
+
+		lea	edx, [esp+24h+var_C]
+		push	0FFFFFFFFh
+		push	edx
+		push	65h
+		mov	ecx, ebp
+		call	?SearchMixerInputChannel@CResource@@QAEJW4_eReservedFor@@PAKK@Z	; CResource::SearchMixerInputChannel(_eReservedFor,ulong *,ulong)
+
+		test	eax, eax
+		jnz	short loc_2F564
+
+		mov	eax, [esp+24h+var_10]
+		mov	edx, [esp+24h+var_C]
+		add	eax, 420h
+		mov	ecx, [edi+74h]
+		shl	eax, 5
+		add	eax, edx
+		shl	eax, 2
+		push	eax
+		call	?ReadDWORD@CAsp4HwIO@@QAEKK@Z ;	CAsp4HwIO::ReadDWORD(ulong)
+
+		and	eax, 0FFh
+		mov	[edi+64h], eax
+
+loc_2F564:				; CODE XREF: Asp4Topology::AllocSynth(CResource	*)+181j
+					; Asp4Topology::AllocSynth(CResource *)+198j ...
+		cmp	dword ptr [edi+68h], 0FFFFFFFFh
+		jnz	short loc_2F5BB
+
+		lea	ecx, [esp+24h+var_10]
+		push	0FFFFFFFFh
+		push	ecx
+		push	82h
+		mov	ecx, ebp
+		call	?SearchMixerOutputChannel@CResource@@QAEJW4_eReservedFor@@PAKK@Z ; CResource::SearchMixerOutputChannel(_eReservedFor,ulong *,ulong)
+
+		test	eax, eax
+		jnz	short loc_2F5BB
+
+		lea	edx, [esp+24h+var_C]
+		push	0FFFFFFFFh
+		push	edx
+		push	66h
+		mov	ecx, ebp
+		call	?SearchMixerInputChannel@CResource@@QAEJW4_eReservedFor@@PAKK@Z	; CResource::SearchMixerInputChannel(_eReservedFor,ulong *,ulong)
+
+		test	eax, eax
+		jnz	short loc_2F5BB
+
+		mov	eax, [esp+24h+var_10]
+		mov	edx, [esp+24h+var_C]
+		add	eax, 420h
+		mov	ecx, [edi+74h]
+		shl	eax, 5
+		add	eax, edx
+		shl	eax, 2
+		push	eax
+		call	?ReadDWORD@CAsp4HwIO@@QAEKK@Z ;	CAsp4HwIO::ReadDWORD(ulong)
+
+		and	eax, 0FFh
+		mov	[edi+68h], eax
+
+loc_2F5BB:				; CODE XREF: Asp4Topology::AllocSynth(CResource	*)+1D8j
+					; Asp4Topology::AllocSynth(CResource *)+1EFj ...
+		mov	ecx, [ebp+0C4h]
+		lea	edx, [esp+24h+var_8]
+		push	edx
+		push	66h
+		push	82h
+		mov	[esp+30h+var_8], 0
+		call	?IsInputVolPending@CAsp4Mixer@@QAEHW4_eReservedFor@@0PAK@Z ; CAsp4Mixer::IsInputVolPending(_eReservedFor,_eReservedFor,ulong *)
+
+		test	eax, eax
+		jz	short loc_2F5F0
+
+		mov	eax, [esp+24h+var_8]
+		push	eax
+		call	?LinearFrac2WtFP@@YGEK@Z ; LinearFrac2WtFP(ulong)
+
+		and	eax, 0FFh
+		mov	[edi+68h], eax
+
+loc_2F5F0:				; CODE XREF: Asp4Topology::AllocSynth(CResource	*)+24Cj
+		mov	ecx, [ebp+0C4h]
+		lea	edx, [esp+24h+var_8]
+		push	edx
+		push	65h
+		push	81h
+		call	?IsInputVolPending@CAsp4Mixer@@QAEHW4_eReservedFor@@0PAK@Z ; CAsp4Mixer::IsInputVolPending(_eReservedFor,_eReservedFor,ulong *)
+
+		test	eax, eax
+		jz	short loc_2F61D
+
+		mov	eax, [esp+24h+var_8]
+		push	eax
+		call	?LinearFrac2WtFP@@YGEK@Z ; LinearFrac2WtFP(ulong)
+
+		and	eax, 0FFh
+		mov	[edi+64h], eax
+
+loc_2F61D:				; CODE XREF: Asp4Topology::AllocSynth(CResource	*)+279j
+		mov	ecx, [ebx]
+		mov	edx, [edi+90h]
+		mov	eax, [edi+8Ch]
+		push	ecx
+		mov	ecx, [edi+88h]
+		push	edx
+		push	eax
+		push	ecx
+		mov	ecx, esi
+		call	?Create@Asp4SynthTopology@@QAEHHHHH@Z ;	Asp4SynthTopology::Create(int,int,int,int)
+
+		test	eax, eax
+		jnz	loc_2F719
+
+		mov	ecx, esi
+		call	??1Asp4SynthTopology@@QAE@XZ ; Asp4SynthTopology::~Asp4SynthTopology(void)
+
+		push	esi
+		call	??3@YAXPAX@Z	; operator delete(void *)
+
+		add	esp, 4
+		lea	eax, [edi+88h]
+		lea	esi, [edi+8Ch]
+		mov	dword ptr [ebx], 0
+		push	eax
+		push	offset asc_816B4 ; "A"
+		push	offset asc_816E0 ; "s"
+		push	80000002h
+		mov	dword ptr [eax], 0
+		mov	dword ptr [esi], 0
+		call	_AspSetRegDwordValue@16
+
+		push	esi
+		push	offset asc_81754 ; "S"
+		push	offset asc_81768 ; "s"
+		push	80000002h
+		call	_AspSetRegDwordValue@16
+
+		push	ebx
+		push	offset aG_16	; "G"
+		push	offset asc_817F0 ; "s"
+		push	80000002h
+		call	_AspSetRegDwordValue@16
+
+		push	70h
+		call	??2@YAPAXI@Z	; operator new(uint)
+
+		add	esp, 4
+		test	eax, eax
+		jz	short loc_2F6CE
+
+		push	edi
+		push	ebp
+		mov	ecx, eax
+		call	??0Asp4SynthTopology@@QAE@PAVCResource@@PAVAsp4Topology@@@Z ; Asp4SynthTopology::Asp4SynthTopology(CResource *,Asp4Topology *)
+
+		mov	esi, eax
+		test	esi, esi
+		jnz	short loc_2F6DA
+
+
+loc_2F6CE:				; CODE XREF: Asp4Topology::AllocSynth(CResource	*)+32Dj
+		pop	edi
+		pop	esi
+		pop	ebp
+		xor	eax, eax
+		pop	ebx
+		add	esp, 14h
+		retn	4
+
+; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
+
+loc_2F6DA:				; CODE XREF: Asp4Topology::AllocSynth(CResource	*)+33Cj
+		mov	edx, [ebx]
+		mov	eax, [edi+90h]
+		mov	ecx, [edi+8Ch]
+		push	edx
+		mov	edx, [edi+88h]
+		push	eax
+		push	ecx
+		push	edx
+		mov	ecx, esi
+		call	?Create@Asp4SynthTopology@@QAEHHHHH@Z ;	Asp4SynthTopology::Create(int,int,int,int)
+
+		test	eax, eax
+		jnz	short loc_2F719
+
+		mov	ecx, esi
+		call	??1Asp4SynthTopology@@QAE@XZ ; Asp4SynthTopology::~Asp4SynthTopology(void)
+
+		push	esi
+		call	??3@YAXPAX@Z	; operator delete(void *)
+
+		add	esp, 4
+		xor	eax, eax
+		pop	edi
+		pop	esi
+		pop	ebp
+		pop	ebx
+		add	esp, 14h
+		retn	4
+
+; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
+
+loc_2F719:				; CODE XREF: Asp4Topology::AllocSynth(CResource	*)+2AEj
+					; Asp4Topology::AllocSynth(CResource *)+36Bj
+		mov	eax, [edi+90h]
+		test	eax, eax
+		jz	loc_2F823
+
+		mov	eax, [esi+8]
+		mov	ecx, edi
+		add	al, 50h
+		push	eax
+		push	62h
+		push	11h
+		push	1
+		call	?Route@Asp4Topology@@QAEXHEEE@Z	; Asp4Topology::Route(int,uchar,uchar,uchar)
+
+		mov	ecx, [esi+0Ch]
+		add	cl, 50h
+		push	ecx
+		push	63h
+		push	11h
+		push	1
+		mov	ecx, edi
+		call	?Route@Asp4Topology@@QAEXHEEE@Z	; Asp4Topology::Route(int,uchar,uchar,uchar)
+
+		mov	edx, [esi+18h]
+		mov	ecx, edi
+		add	dl, 50h
+		push	edx
+		push	0A2h
+		push	11h
+		push	1
+		call	?Route@Asp4Topology@@QAEXHEEE@Z	; Asp4Topology::Route(int,uchar,uchar,uchar)
+
+		mov	eax, [esi+1Ch]
+		mov	ecx, edi
+		add	al, 50h
+		push	eax
+		push	0A3h
+		push	11h
+		push	1
+		call	?Route@Asp4Topology@@QAEXHEEE@Z	; Asp4Topology::Route(int,uchar,uchar,uchar)
+
+		push	2
+		push	1
+		mov	ecx, ebp
+		call	?GetWTRefCount@CResource@@QAEKHW4WTSOURCE@@@Z ;	CResource::GetWTRefCount(int,WTSOURCE)
+
+		test	eax, eax
+		jnz	short loc_2F7CF
+
+		mov	eax, [edi+194h]
+		test	eax, eax
+		jnz	short loc_2F7CF
+
+		mov	ecx, [esi+18h]
+		push	ecx
+		mov	ecx, [esi+38h]
+		call	?EnableInput@CAsp4Mix@@QAEXH@Z ; CAsp4Mix::EnableInput(int)
+
+		mov	edx, [esi+1Ch]
+		mov	ecx, [esi+3Ch]
+		push	edx
+		call	?EnableInput@CAsp4Mix@@QAEXH@Z ; CAsp4Mix::EnableInput(int)
+
+		mov	ecx, [esi+40h]
+		test	ecx, ecx
+		jz	short loc_2F7CF
+
+		mov	eax, [esi+44h]
+		test	eax, eax
+		jz	short loc_2F7CF
+
+		mov	eax, [esi+18h]
+		push	eax
+		call	?EnableInput@CAsp4Mix@@QAEXH@Z ; CAsp4Mix::EnableInput(int)
+
+		mov	ecx, [esi+1Ch]
+		push	ecx
+		mov	ecx, [esi+44h]
+		call	?EnableInput@CAsp4Mix@@QAEXH@Z ; CAsp4Mix::EnableInput(int)
+
+
+loc_2F7CF:				; CODE XREF: Asp4Topology::AllocSynth(CResource	*)+3F8j
+					; Asp4Topology::AllocSynth(CResource *)+402j ...
+		push	2
+		push	0
+		mov	ecx, ebp
+		call	?GetWTRefCount@CResource@@QAEKHW4WTSOURCE@@@Z ;	CResource::GetWTRefCount(int,WTSOURCE)
+
+		test	eax, eax
+		jnz	short loc_2F823
+
+		mov	eax, [edi+190h]
+		test	eax, eax
+		jnz	short loc_2F823
+
+		mov	edx, [esi+8]
+		mov	ecx, [esi+38h]
+		push	edx
+		call	?EnableInput@CAsp4Mix@@QAEXH@Z ; CAsp4Mix::EnableInput(int)
+
+		mov	eax, [esi+0Ch]
+		mov	ecx, [esi+3Ch]
+		push	eax
+		call	?EnableInput@CAsp4Mix@@QAEXH@Z ; CAsp4Mix::EnableInput(int)
+
+		mov	ecx, [esi+40h]
+		test	ecx, ecx
+		jz	short loc_2F823
+
+		mov	eax, [esi+44h]
+		test	eax, eax
+		jz	short loc_2F823
+
+		mov	edx, [esi+8]
+		push	edx
+		call	?EnableInput@CAsp4Mix@@QAEXH@Z ; CAsp4Mix::EnableInput(int)
+
+		mov	eax, [esi+0Ch]
+		mov	ecx, [esi+44h]
+		push	eax
+		call	?EnableInput@CAsp4Mix@@QAEXH@Z ; CAsp4Mix::EnableInput(int)
+
+
+loc_2F823:				; CODE XREF: Asp4Topology::AllocSynth(CResource	*)+391j
+					; Asp4Topology::AllocSynth(CResource *)+44Cj ...
+		mov	eax, [edi+8Ch]
+		test	eax, eax
+		jnz	short loc_2F83B
+
+		mov	eax, [edi+88h]
+		test	eax, eax
+		jz	loc_2FB41
+
+
+loc_2F83B:				; CODE XREF: Asp4Topology::AllocSynth(CResource	*)+49Bj
+		mov	ecx, [esi+10h]
+		push	ecx
+		mov	ecx, [esi+38h]
+		call	?EnableInput@CAsp4Mix@@QAEXH@Z ; CAsp4Mix::EnableInput(int)
+
+		mov	edx, [esi+14h]
+		mov	ecx, [esi+3Ch]
+		push	edx
+		call	?EnableInput@CAsp4Mix@@QAEXH@Z ; CAsp4Mix::EnableInput(int)
+
+		mov	ecx, [esi+40h]
+		test	ecx, ecx
+		jz	short loc_2F876
+
+		mov	eax, [esi+44h]
+		test	eax, eax
+		jz	short loc_2F876
+
+		mov	eax, [esi+10h]
+		push	eax
+		call	?EnableInput@CAsp4Mix@@QAEXH@Z ; CAsp4Mix::EnableInput(int)
+
+		mov	ecx, [esi+14h]
+		push	ecx
+		mov	ecx, [esi+44h]
+		call	?EnableInput@CAsp4Mix@@QAEXH@Z ; CAsp4Mix::EnableInput(int)
+
+
+loc_2F876:				; CODE XREF: Asp4Topology::AllocSynth(CResource	*)+4C8j
+					; Asp4Topology::AllocSynth(CResource *)+4CFj
+		mov	eax, [esi+28h]
+		mov	ecx, ebp
+		push	eax
+		call	?GetInputChannelRefCount@CResource@@QAEKK@Z ; CResource::GetInputChannelRefCount(ulong)
+
+		cmp	eax, 1
+		jnz	short loc_2F8C4
+
+		mov	edx, [esi+28h]
+		mov	ecx, edi
+		add	dl, 50h
+		push	edx
+		push	64h
+		push	11h
+		push	eax
+		call	?Route@Asp4Topology@@QAEXHEEE@Z	; Asp4Topology::Route(int,uchar,uchar,uchar)
+
+		mov	eax, [esi+20h]
+		mov	ecx, edi
+		add	al, 50h
+		push	eax
+		push	65h
+		push	11h
+		push	1
+		call	?Route@Asp4Topology@@QAEXHEEE@Z	; Asp4Topology::Route(int,uchar,uchar,uchar)
+
+		mov	ecx, [esi+28h]
+		push	ecx
+		mov	ecx, [esi+34h]
+		call	?EnableInput@CAsp4Mix@@QAEXH@Z ; CAsp4Mix::EnableInput(int)
+
+		mov	edx, [esi+20h]
+		mov	ecx, [esi+30h]
+		push	edx
+		call	?EnableInput@CAsp4Mix@@QAEXH@Z ; CAsp4Mix::EnableInput(int)
+
+
+loc_2F8C4:				; CODE XREF: Asp4Topology::AllocSynth(CResource	*)+4F4j
+		mov	eax, [esi+2Ch]
+		mov	ecx, ebp
+		push	eax
+		call	?GetInputChannelRefCount@CResource@@QAEKK@Z ; CResource::GetInputChannelRefCount(ulong)
+
+		cmp	eax, 1
+		jnz	short loc_2F919
+
+		mov	eax, [esi+2Ch]
+		mov	ecx, edi
+		add	al, 50h
+		push	eax
+		push	0A4h
+		push	11h
+		push	1
+		call	?Route@Asp4Topology@@QAEXHEEE@Z	; Asp4Topology::Route(int,uchar,uchar,uchar)
+
+		mov	ecx, [esi+24h]
+		add	cl, 50h
+		push	ecx
+		push	0A5h
+		push	11h
+		push	1
+		mov	ecx, edi
+		call	?Route@Asp4Topology@@QAEXHEEE@Z	; Asp4Topology::Route(int,uchar,uchar,uchar)
+
+		mov	edx, [esi+2Ch]
+		mov	ecx, [esi+34h]
+		push	edx
+		call	?EnableInput@CAsp4Mix@@QAEXH@Z ; CAsp4Mix::EnableInput(int)
+
+		mov	eax, [esi+24h]
+		mov	ecx, [esi+30h]
+		push	eax
+		call	?EnableInput@CAsp4Mix@@QAEXH@Z ; CAsp4Mix::EnableInput(int)
+
+
+loc_2F919:				; CODE XREF: Asp4Topology::AllocSynth(CResource	*)+542j
+		mov	ecx, [esi+50h]
+		mov	edx, [esi+30h]
+		mov	eax, [esi+34h]
+		push	ecx
+		push	edx
+		push	eax
+		push	11h
+		push	1
+		mov	ecx, edi
+		call	?Connection@Asp4Topology@@QAEXHEPAVCAsp4Mix@@0PAVCAsp4AdbDma@@@Z ; Asp4Topology::Connection(int,uchar,CAsp4Mix *,int,CAsp4AdbDma *)
+
+		mov	ecx, [esi+50h]
+		test	ecx, ecx
+		jz	loc_2FA2D
+
+		mov	edx, [ecx]
+		push	0
+		push	0
+		push	1
+		push	8
+		push	0
+		push	0
+		call	dword ptr [edx+4]
+
+		mov	eax, [esi+50h]
+		push	56FFFFFFh
+		mov	ecx, [eax+4]
+		lea	edx, ds:27800h[ecx*8]
+		mov	ecx, [edi+74h]
+		push	edx
+		call	?Write@CAsp4HwIO@@QAEXKK@Z ; CAsp4HwIO::Write(ulong,ulong)
+
+		mov	eax, [esi+50h]
+		push	74FFFFFFh
+		mov	ecx, [eax+4]
+		lea	edx, ds:27804h[ecx*8]
+		mov	ecx, [edi+74h]
+		push	edx
+		call	?Write@CAsp4HwIO@@QAEXKK@Z ; CAsp4HwIO::Write(ulong,ulong)
+
+		mov	eax, [esi+60h]
+		mov	ecx, [eax+4]
+		mov	eax, [esi+50h]
+		mov	edx, [ecx+1Ch]
+		mov	ecx, [eax+4]
+		add	ecx, 2740h
+		shl	edx, 0Ch
+		shl	ecx, 4
+		push	edx
+		push	ecx
+		mov	ecx, [edi+74h]
+		call	?Write@CAsp4HwIO@@QAEXKK@Z ; CAsp4HwIO::Write(ulong,ulong)
+
+		mov	edx, [esi+60h]
+		mov	eax, [edx+4]
+		mov	edx, [esi+50h]
+		mov	ecx, [eax+20h]
+		mov	eax, [edx+4]
+		shl	eax, 4
+		shl	ecx, 0Ch
+		add	eax, (offset loc_27402+2)
+		push	ecx
+		mov	ecx, [edi+74h]
+		push	eax
+		call	?Write@CAsp4HwIO@@QAEXKK@Z ; CAsp4HwIO::Write(ulong,ulong)
+
+		mov	ecx, [esi+60h]
+		mov	edx, [ecx+4]
+		mov	ecx, [esi+50h]
+		mov	eax, [edx+24h]
+		mov	edx, [ecx+4]
+		mov	ecx, [edi+74h]
+		shl	edx, 4
+		shl	eax, 0Ch
+		add	edx, offset nullsub_2
+		push	eax
+		push	edx
+		call	?Write@CAsp4HwIO@@QAEXKK@Z ; CAsp4HwIO::Write(ulong,ulong)
+
+		mov	eax, [esi+60h]
+		mov	ecx, [eax+4]
+		mov	eax, [esi+50h]
+		mov	edx, [ecx+28h]
+		mov	ecx, [eax+4]
+		shl	ecx, 4
+		shl	edx, 0Ch
+		add	ecx, offset dword_2740C
+		push	edx
+		push	ecx
+		mov	ecx, [edi+74h]
+		call	?Write@CAsp4HwIO@@QAEXKK@Z ; CAsp4HwIO::Write(ulong,ulong)
+
+		mov	edx, [esi+50h]
+		push	0
+		mov	eax, [edx+4]
+		lea	ecx, ds:27C00h[eax*4]
+		push	ecx
+		mov	ecx, [edi+74h]
+		call	?Write@CAsp4HwIO@@QAEXKK@Z ; CAsp4HwIO::Write(ulong,ulong)
+
+
+loc_2FA2D:				; CODE XREF: Asp4Topology::AllocSynth(CResource	*)+5A5j
+		mov	edx, [esi+14h]
+		mov	eax, [esi+10h]
+		mov	ecx, [esi+54h]
+		add	dl, 50h
+		push	edx
+		add	al, 50h
+		mov	edx, [ecx+4]
+		push	eax
+		push	edx
+		push	11h
+		push	1
+		mov	ecx, edi
+		call	?Route@Asp4Topology@@QAEXHEEEE@Z ; Asp4Topology::Route(int,uchar,uchar,uchar,uchar)
+
+		mov	ecx, [esi+54h]
+		push	0
+		push	0
+		push	1
+		mov	eax, [ecx]
+		push	8
+		push	1
+		push	0
+		call	dword ptr [eax+4]
+
+		mov	ecx, [esi+54h]
+		push	56FFFFFFh
+		mov	edx, [ecx+4]
+		mov	ecx, [edi+74h]
+		lea	eax, ds:27800h[edx*8]
+		push	eax
+		call	?Write@CAsp4HwIO@@QAEXKK@Z ; CAsp4HwIO::Write(ulong,ulong)
+
+		mov	ecx, [esi+54h]
+		push	74FFFFFFh
+		mov	edx, [ecx+4]
+		mov	ecx, [edi+74h]
+		lea	eax, ds:27804h[edx*8]
+		push	eax
+		call	?Write@CAsp4HwIO@@QAEXKK@Z ; CAsp4HwIO::Write(ulong,ulong)
+
+		mov	ecx, [esi+64h]
+		mov	edx, [ecx+4]
+		mov	ecx, [esi+54h]
+		mov	eax, [edx+1Ch]
+		mov	edx, [ecx+4]
+		mov	ecx, [edi+74h]
+		add	edx, 2740h
+		shl	eax, 0Ch
+		shl	edx, 4
+		push	eax
+		push	edx
+		call	?Write@CAsp4HwIO@@QAEXKK@Z ; CAsp4HwIO::Write(ulong,ulong)
+
+		mov	eax, [esi+64h]
+		mov	ecx, [eax+4]
+		mov	eax, [esi+54h]
+		mov	edx, [ecx+20h]
+		mov	ecx, [eax+4]
+		shl	ecx, 4
+		shl	edx, 0Ch
+		add	ecx, (offset loc_27402+2)
+		push	edx
+		push	ecx
+		mov	ecx, [edi+74h]
+		call	?Write@CAsp4HwIO@@QAEXKK@Z ; CAsp4HwIO::Write(ulong,ulong)
+
+		mov	edx, [esi+64h]
+		mov	eax, [edx+4]
+		mov	edx, [esi+54h]
+		mov	ecx, [eax+24h]
+		mov	eax, [edx+4]
+		shl	eax, 4
+		shl	ecx, 0Ch
+		add	eax, offset nullsub_2
+		push	ecx
+		mov	ecx, [edi+74h]
+		push	eax
+		call	?Write@CAsp4HwIO@@QAEXKK@Z ; CAsp4HwIO::Write(ulong,ulong)
+
+		mov	ecx, [esi+64h]
+		mov	edx, [ecx+4]
+		mov	ecx, [esi+54h]
+		mov	eax, [edx+28h]
+		mov	edx, [ecx+4]
+		shl	eax, 0Ch
+		shl	edx, 4
+		push	eax
+		add	edx, offset dword_2740C
+		mov	ecx, [edi+74h]
+		push	edx
+		call	?Write@CAsp4HwIO@@QAEXKK@Z ; CAsp4HwIO::Write(ulong,ulong)
+
+		mov	eax, [esi+54h]
+		push	0
+		mov	ecx, [eax+4]
+		lea	edx, ds:27C00h[ecx*4]
+		mov	ecx, [edi+74h]
+		push	edx
+		call	?Write@CAsp4HwIO@@QAEXKK@Z ; CAsp4HwIO::Write(ulong,ulong)
+
+
+loc_2FB41:				; CODE XREF: Asp4Topology::AllocSynth(CResource	*)+4A5j
+		mov	ecx, [esi+38h]
+		mov	ebx, [esi+10h]
+		mov	eax, [edi+64h]
+		mov	edx, [ecx+8]
+		mov	ecx, [edi+74h]
+		add	edx, 420h
+		push	eax
+		shl	edx, 5
+		add	edx, ebx
+		shl	edx, 2
+		push	edx
+		call	?Write@CAsp4HwIO@@QAEXKK@Z ; CAsp4HwIO::Write(ulong,ulong)
+
+		mov	ecx, [esi+3Ch]
+		mov	eax, [edi+68h]
+		push	eax
+		mov	eax, [esi+14h]
+		mov	edx, [ecx+8]
+		mov	ecx, [edi+74h]
+		add	edx, 420h
+		shl	edx, 5
+		add	edx, eax
+		shl	edx, 2
+		push	edx
+		call	?Write@CAsp4HwIO@@QAEXKK@Z ; CAsp4HwIO::Write(ulong,ulong)
+
+		mov	eax, [esi+40h]
+		test	eax, eax
+		jz	short loc_2FBDC
+
+		mov	ecx, [esi+44h]
+		test	ecx, ecx
+		jz	short loc_2FBDC
+
+		mov	edx, [eax+8]
+		mov	eax, [esi+10h]
+		mov	ecx, [edi+64h]
+		add	edx, 420h
+		shl	edx, 5
+		add	edx, eax
+		push	ecx
+		mov	ecx, [edi+74h]
+		shl	edx, 2
+		push	edx
+		call	?Write@CAsp4HwIO@@QAEXKK@Z ; CAsp4HwIO::Write(ulong,ulong)
+
+		mov	ecx, [esi+44h]
+		mov	ebx, [esi+14h]
+		mov	eax, [edi+68h]
+		mov	edx, [ecx+8]
+		mov	ecx, [edi+74h]
+		add	edx, 420h
+		push	eax
+		shl	edx, 5
+		add	edx, ebx
+		shl	edx, 2
+		push	edx
+		call	?Write@CAsp4HwIO@@QAEXKK@Z ; CAsp4HwIO::Write(ulong,ulong)
+
+
+loc_2FBDC:				; CODE XREF: Asp4Topology::AllocSynth(CResource	*)+7FEj
+					; Asp4Topology::AllocSynth(CResource *)+805j
+		mov	ecx, [esi+38h]
+		mov	ebx, [esi+8]
+		mov	eax, [edi+64h]
+		mov	edx, [ecx+8]
+		mov	ecx, [edi+74h]
+		add	edx, 420h
+		push	eax
+		shl	edx, 5
+		add	edx, ebx
+		shl	edx, 2
+		push	edx
+		call	?Write@CAsp4HwIO@@QAEXKK@Z ; CAsp4HwIO::Write(ulong,ulong)
+
+		mov	ecx, [esi+3Ch]
+		mov	eax, [edi+68h]
+		push	eax
+		mov	eax, [esi+0Ch]
+		mov	edx, [ecx+8]
+		mov	ecx, [edi+74h]
+		add	edx, 420h
+		shl	edx, 5
+		add	edx, eax
+		shl	edx, 2
+		push	edx
+		call	?Write@CAsp4HwIO@@QAEXKK@Z ; CAsp4HwIO::Write(ulong,ulong)
+
+		mov	eax, [esi+40h]
+		test	eax, eax
+		jz	short loc_2FC77
+
+		mov	ecx, [esi+44h]
+		test	ecx, ecx
+		jz	short loc_2FC77
+
+		mov	edx, [eax+8]
+		mov	eax, [esi+8]
+		mov	ecx, [edi+64h]
+		add	edx, 420h
+		shl	edx, 5
+		add	edx, eax
+		push	ecx
+		mov	ecx, [edi+74h]
+		shl	edx, 2
+		push	edx
+		call	?Write@CAsp4HwIO@@QAEXKK@Z ; CAsp4HwIO::Write(ulong,ulong)
+
+		mov	ecx, [esi+44h]
+		mov	ebx, [esi+0Ch]
+		mov	eax, [edi+68h]
+		mov	edx, [ecx+8]
+		mov	ecx, [edi+74h]
+		add	edx, 420h
+		push	eax
+		shl	edx, 5
+		add	edx, ebx
+		shl	edx, 2
+		push	edx
+		call	?Write@CAsp4HwIO@@QAEXKK@Z ; CAsp4HwIO::Write(ulong,ulong)
+
+
+loc_2FC77:				; CODE XREF: Asp4Topology::AllocSynth(CResource	*)+899j
+					; Asp4Topology::AllocSynth(CResource *)+8A0j
+		mov	ecx, [esi+38h]
+		mov	ebx, [esi+18h]
+		mov	eax, [edi+64h]
+		mov	edx, [ecx+8]
+		mov	ecx, [edi+74h]
+		add	edx, 420h
+		push	eax
+		shl	edx, 5
+		add	edx, ebx
+		shl	edx, 2
+		push	edx
+		call	?Write@CAsp4HwIO@@QAEXKK@Z ; CAsp4HwIO::Write(ulong,ulong)
+
+		mov	ecx, [esi+3Ch]
+		mov	eax, [edi+68h]
+		push	eax
+		mov	eax, [esi+1Ch]
+		mov	edx, [ecx+8]
+		mov	ecx, [edi+74h]
+		add	edx, 420h
+		shl	edx, 5
+		add	edx, eax
+		shl	edx, 2
+		push	edx
+		call	?Write@CAsp4HwIO@@QAEXKK@Z ; CAsp4HwIO::Write(ulong,ulong)
+
+		mov	eax, [esi+40h]
+		test	eax, eax
+		jz	short loc_2FD12
+
+		mov	ecx, [esi+44h]
+		test	ecx, ecx
+		jz	short loc_2FD12
+
+		mov	edx, [eax+8]
+		mov	eax, [esi+18h]
+		mov	ecx, [edi+64h]
+		add	edx, 420h
+		shl	edx, 5
+		add	edx, eax
+		push	ecx
+		mov	ecx, [edi+74h]
+		shl	edx, 2
+		push	edx
+		call	?Write@CAsp4HwIO@@QAEXKK@Z ; CAsp4HwIO::Write(ulong,ulong)
+
+		mov	ecx, [esi+44h]
+		mov	ebx, [esi+1Ch]
+		mov	eax, [edi+68h]
+		mov	edx, [ecx+8]
+		mov	ecx, [edi+74h]
+		add	edx, 420h
+		push	eax
+		shl	edx, 5
+		add	edx, ebx
+		shl	edx, 2
+		push	edx
+		call	?Write@CAsp4HwIO@@QAEXKK@Z ; CAsp4HwIO::Write(ulong,ulong)
+
+
+loc_2FD12:				; CODE XREF: Asp4Topology::AllocSynth(CResource	*)+934j
+					; Asp4Topology::AllocSynth(CResource *)+93Bj
+		mov	eax, [edi+8Ch]
+		test	eax, eax
+		jnz	short loc_2FD26
+
+		mov	eax, [edi+88h]
+		test	eax, eax
+		jz	short loc_2FD72
+
+
+loc_2FD26:				; CODE XREF: Asp4Topology::AllocSynth(CResource	*)+98Aj
+		mov	eax, [esi+28h]
+		mov	ecx, [esi+34h]
+		push	8
+		push	eax
+		call	?SetInputVolumeByte@CAsp4Mix@@QAEXHE@Z ; CAsp4Mix::SetInputVolumeByte(int,uchar)
+
+		mov	ecx, [esi+2Ch]
+		push	8
+		push	ecx
+		mov	ecx, [esi+34h]
+		call	?SetInputVolumeByte@CAsp4Mix@@QAEXHE@Z ; CAsp4Mix::SetInputVolumeByte(int,uchar)
+
+		mov	edx, [esi+20h]
+		mov	ecx, [esi+30h]
+		push	8
+		push	edx
+		call	?SetInputVolumeByte@CAsp4Mix@@QAEXHE@Z ; CAsp4Mix::SetInputVolumeByte(int,uchar)
+
+		mov	eax, [esi+24h]
+		mov	ecx, [esi+30h]
+		push	8
+		push	eax
+		call	?SetInputVolumeByte@CAsp4Mix@@QAEXHE@Z ; CAsp4Mix::SetInputVolumeByte(int,uchar)
+
+		mov	ecx, [esi+30h]
+		push	8
+		call	?SetVolumeByte@CAsp4Mix@@QAEXE@Z ; CAsp4Mix::SetVolumeByte(uchar)
+
+		mov	ecx, [esi+34h]
+		push	8
+		call	?SetVolumeByte@CAsp4Mix@@QAEXE@Z ; CAsp4Mix::SetVolumeByte(uchar)
+
+
+loc_2FD72:				; CODE XREF: Asp4Topology::AllocSynth(CResource	*)+994j
+		mov	[edi+7Ch], esi
+		mov	eax, esi
+		pop	edi
+		pop	esi
+		pop	ebp
+		pop	ebx
+		add	esp, 14h
+		retn	4
+
+?AllocSynth@Asp4Topology@@QAEPAVAsp4SynthTopology@@PAVCResource@@@Z endp ; sp =	-30h
+
+; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
+		align 10h
+
+; ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ S U B	R O U T	I N E ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ
+
+
+; public: void __thiscall Asp4Topology::DeleteSynth(void)
+
+?DeleteSynth@Asp4Topology@@QAEXXZ proc near
+					; CODE XREF: CAsp4Core::SetWavetableTopology(int)+25p
+					; .text:0002564Bp
+		push	esi
+		push	edi
+		mov	edi, ecx
+		mov	esi, [edi+7Ch]
+		test	esi, esi
+		jz	short loc_2FDAB
+
+		mov	ecx, esi
+		call	??1Asp4SynthTopology@@QAE@XZ ; Asp4SynthTopology::~Asp4SynthTopology(void)
+
+		push	esi
+		call	??3@YAXPAX@Z	; operator delete(void *)
+
+		add	esp, 4
+
+loc_2FDAB:				; CODE XREF: Asp4Topology::DeleteSynth(void)+9j
+		mov	dword ptr [edi+7Ch], 0
+		pop	edi
+		pop	esi
+		retn
+
+?DeleteSynth@Asp4Topology@@QAEXXZ endp
+
+; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
+		align 10h
+
+; public: static void __cdecl Asp4Topology::Appy_Handler(void *,unsigned long)
+?Appy_Handler@Asp4Topology@@SAXPAXK@Z:
+		retn
+
+; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
+		align 10h
+
+; ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ S U B	R O U T	I N E ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ
+
+
+; public: void __thiscall Asp4Topology::Route(int,unsigned char,unsigned char,unsigned char)
+
+?Route@Asp4Topology@@QAEXHEEE@Z	proc near
+					; CODE XREF: Asp4Topology::MakeDefaultConnections(void)+2Ap
+					; Asp4Topology::MakeDefaultConnections(void)+75p ...
+
+arg_0		= dword	ptr  8
+arg_4		= dword	ptr  0Ch
+arg_8		= dword	ptr  10h
+arg_C		= dword	ptr  14h
+
+		push	ebx
+		mov	ebx, [esp+arg_8]
+		push	ebp
+		push	esi
+		push	edi
+		mov	edi, ebx
+		mov	esi, ecx
+		and	edi, 0FFh
+		mov	eax, [esp+0Ch+arg_8]
+		mov	ecx, edi
+		and	ah, 0
+		shl	ecx, 8
+		or	eax, ecx
+		mov	[esp+0Ch+arg_8], eax
+		mov	edx, [esp+0Ch+arg_8]
+		mov	eax, [esp+0Ch+arg_C]
+		and	dl, 0
+		and	eax, 0FFh
+		or	edx, eax
+		mov	[esp+0Ch+arg_8], edx
+		mov	eax, [esp+0Ch+arg_0]
+		test	eax, eax
+		jz	short loc_2FE74
+
+		mov	ebp, [esp+0Ch+arg_4]
+		lea	ecx, [esp+0Ch+arg_8]
+		push	1
+		push	ecx
+		mov	ecx, [esi+6Ch]
+		push	ebp
+		call	?AddRoutes@CAsp4Adb@@QAEXEPATADBRamLink@@H@Z ; CAsp4Adb::AddRoutes(uchar,ADBRamLink *,int)
+
+		cmp	bl, 20h
+		jb	short loc_2FE4D
+
+		lea	edx, [edi-20h]
+		cmp	edx, 10h
+		jge	short loc_2FE4D
+
+		mov	eax, [esi+70h]
+		sub	bl, 20h
+		push	ebp
+		push	ebx
+		mov	ecx, [eax+0C8h]
+		call	?AddWTD@CAsp4SrcBlock@@QAEHEE@Z	; CAsp4SrcBlock::AddWTD(uchar,uchar)
+
+		pop	edi
+		pop	esi
+		pop	ebp
+		pop	ebx
+		retn	10h
+
+; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
+
+loc_2FE4D:				; CODE XREF: Asp4Topology::Route(int,uchar,uchar,uchar)+59j
+					; Asp4Topology::Route(int,uchar,uchar,uchar)+61j
+		cmp	bl, 30h
+		jb	short loc_2FECE
+
+		add	edi, 0FFFFFFD0h
+		cmp	edi, 10h
+		jge	short loc_2FECE
+
+		mov	ecx, [esi+70h]
+		sub	bl, 30h
+		push	ebp
+		push	ebx
+		mov	ecx, [ecx+0C4h]
+		call	?AddWTD@CAsp4Mixer@@QAEHEE@Z ; CAsp4Mixer::AddWTD(uchar,uchar)
+
+		pop	edi
+		pop	esi
+		pop	ebp
+		pop	ebx
+		retn	10h
+
+; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
+
+loc_2FE74:				; CODE XREF: Asp4Topology::Route(int,uchar,uchar,uchar)+40j
+		mov	eax, [esp+0Ch+arg_8]
+		mov	ebp, [esp+0Ch+arg_4]
+		mov	ecx, [esi+6Ch]
+		push	eax
+		push	eax
+		push	ebp
+		call	?DeleteRoutes@CAsp4Adb@@QAEXETADBRamLink@@0@Z ;	CAsp4Adb::DeleteRoutes(uchar,ADBRamLink,uchar)
+
+		cmp	bl, 20h
+		jb	short loc_2FEAE
+
+		lea	edx, [edi-20h]
+		cmp	edx, 10h
+		jge	short loc_2FEAE
+
+		mov	eax, [esi+70h]
+		sub	bl, 20h
+		push	ebp
+		push	ebx
+		mov	ecx, [eax+0C8h]
+		call	?DeleteWTD@CAsp4SrcBlock@@QAEHEE@Z ; CAsp4SrcBlock::DeleteWTD(uchar,uchar)
+
+		pop	edi
+		pop	esi
+		pop	ebp
+		pop	ebx
+		retn	10h
+
+; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
+
+loc_2FEAE:				; CODE XREF: Asp4Topology::Route(int,uchar,uchar,uchar)+BAj
+					; Asp4Topology::Route(int,uchar,uchar,uchar)+C2j
+		cmp	bl, 30h
+		jb	short loc_2FECE
+
+		add	edi, 0FFFFFFD0h
+		cmp	edi, 10h
+		jge	short loc_2FECE
+
+		mov	ecx, [esi+70h]
+		sub	bl, 30h
+		push	ebp
+		push	ebx
+		mov	ecx, [ecx+0C4h]
+		call	?DeleteWTD@CAsp4Mixer@@QAEHEE@Z	; CAsp4Mixer::DeleteWTD(uchar,uchar)
+
+
+loc_2FECE:				; CODE XREF: Asp4Topology::Route(int,uchar,uchar,uchar)+80j
+					; Asp4Topology::Route(int,uchar,uchar,uchar)+88j ...
+		pop	edi
+		pop	esi
+		pop	ebp
+		pop	ebx
+		retn	10h
+
+?Route@Asp4Topology@@QAEXHEEE@Z	endp
+
+; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
+		align 10h
+
+
+
+
+*/
+
+/*
+
+
+
+; public: void __thiscall CWTHal::SetReg(unsigned char,int,unsigned short)
+
+?SetReg@CWTHal@@QAEXEHG@Z proc near	; CODE XREF: Asp4Synth::WTTimerCallback(ulong)+E45p
+					; Asp4Synth::WTTimerCallback(ulong)+1294p
+
+arg_0		= dword	ptr  0Ch
+arg_4		= dword	ptr  10h
+arg_8		= dword	ptr  14h
+
+		push	ecx
+		push	esi
+		mov	esi, [esp+arg_4]
+		cmp	esi, 40h
+		jge	loc_54507
+
+		mov	eax, esi
+		cdq
+		and	edx, 1Fh
+		add	eax, edx
+		mov	edx, esi
+		sar	eax, 5
+		and	edx, 8000001Fh
+		mov	byte ptr [esp+arg_4], al
+		jns	short loc_5448D
+
+		dec	edx
+		or	edx, 0FFFFFFE0h
+		inc	edx
+
+loc_5448D:				; CODE XREF: CWTHal::SetReg(uchar,int,ushort)+26j
+		cmp	al, 2
+		mov	[esp+4], dl
+		jnb	short loc_54507
+
+		mov	eax, [esp+arg_0]
+		and	eax, 0FFh
+		sub	eax, 20h
+		jz	short loc_544D9
+
+		dec	eax
+		jnz	short loc_54507
+
+		mov	eax, [esp+arg_4]
+		mov	edx, [esp+4]
+		and	eax, 0FFh
+		and	edx, 0FFh
+		shl	eax, 0Bh
+		add	eax, edx
+		mov	edx, [esp+arg_8]
+		shl	eax, 4
+		add	eax, 208h
+		push	edx
+		push	eax
+		mov	eax, [ecx]
+		push	eax
+		call	?WriteWORD@CAsp4HIO@@QAGXKG@Z ;	CAsp4HIO::WriteWORD(ulong,ushort)
+
+		pop	esi
+		pop	ecx
+		retn	0Ch
+
+; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
+
+loc_544D9:				; CODE XREF: CWTHal::SetReg(uchar,int,ushort)+41j
+		mov	eax, [esp+arg_4]
+		mov	edx, [esp+4]
+		and	eax, 0FFh
+		and	edx, 0FFh
+		shl	eax, 0Bh
+		add	eax, edx
+		mov	edx, [esp+arg_8]
+		shl	eax, 4
+		add	eax, 20Ah
+		push	edx
+		push	eax
+		mov	eax, [ecx]
+		push	eax
+		call	?WriteWORD@CAsp4HIO@@QAGXKG@Z ;	CAsp4HIO::WriteWORD(ulong,ushort)
+
+
+loc_54507:				; CODE XREF: CWTHal::SetReg(uchar,int,ushort)+9j
+					; CWTHal::SetReg(uchar,int,ushort)+33j	...
+		pop	esi
+		pop	ecx
+		retn	0Ch
+
+?SetReg@CWTHal@@QAEXEHG@Z endp
+
+; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
+		align 8
+
+; ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ S U B	R O U T	I N E ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ
+
+
+; public: void __thiscall CWTHal::InitializeWTRegs(void)
+
+?InitializeWTRegs@CWTHal@@QAEXXZ proc near ; CODE XREF:	Asp4Synth::init(void)+242p
+
+var_10		= dword	ptr -10h
+var_C		= dword	ptr -0Ch
+var_8		= dword	ptr -8
+var_4		= dword	ptr -4
+
+		sub	esp, 10h
+		push	ebx
+		push	ebp
+		push	esi
+		xor	ebx, ebx
+		push	edi
+		mov	esi, ecx
+		mov	[esp+20h+var_10], ebx
+		mov	eax, [esp+20h+var_10]
+		and	al, 0E3h
+		or	al, 22h
+		mov	[esp+20h+var_10], eax
+		mov	ecx, [esp+20h+var_10]
+		and	ecx, 0FFFFFEBFh
+		or	cl, 80h
+		mov	[esp+20h+var_10], ecx
+		mov	edx, [esp+20h+var_10]
+		or	dh, 2
+		mov	[esp+20h+var_10], edx
+		mov	eax, [esp+20h+var_10]
+		and	al, 0FEh
+		mov	[esp+20h+var_10], eax
+		mov	ecx, [esp+20h+var_10]
+		and	ch, 0FBh
+		or	ch, 18h
+		mov	[esp+20h+var_10], ecx
+		mov	[esp+20h+var_8], ebx
+		mov	edx, [esp+20h+var_8]
+		mov	[esp+20h+var_C], edx
+		mov	eax, [esp+20h+var_C]
+		mov	[esp+20h+var_4], eax
+		mov	word ptr [esp+20h+var_4+2], 1000h
+		mov	eax, 83h
+		mov	word ptr [esp+20h+var_C+2], ax
+		mov	word ptr [esp+20h+var_8+2], ax
+		xor	edi, edi
+		mov	ebp, 2
+
+loc_54590:				; CODE XREF: CWTHal::InitializeWTRegs(void)+C9j
+		push	ebx
+		push	edi
+		push	0Ch
+		mov	ecx, esi
+		call	?SetReg@CWTHal@@QAEXEHK@Z ; CWTHal::SetReg(uchar,int,ulong)
+
+		mov	ecx, [esp+20h+var_10]
+		push	ecx
+		push	edi
+		push	0Ah
+		mov	ecx, esi
+		call	?SetReg@CWTHal@@QAEXEHK@Z ; CWTHal::SetReg(uchar,int,ulong)
+
+		mov	edx, [esp+20h+var_4]
+		push	edx
+		push	edi
+		push	9
+		mov	ecx, esi
+		call	?SetReg@CWTHal@@QAEXEHK@Z ; CWTHal::SetReg(uchar,int,ulong)
+
+		mov	eax, [esp+20h+var_C]
+		push	eax
+		push	edi
+		push	8
+		mov	ecx, esi
+		call	?SetReg@CWTHal@@QAEXEHK@Z ; CWTHal::SetReg(uchar,int,ulong)
+
+		mov	ecx, [esp+20h+var_8]
+		push	ecx
+		push	edi
+		push	5
+		mov	ecx, esi
+		call	?SetReg@CWTHal@@QAEXEHK@Z ; CWTHal::SetReg(uchar,int,ulong)
+
+		inc	edi
+		dec	ebp
+		jnz	short loc_54590
+
+		mov	eax, ?g_num_hw_voices@@3HA ; int g_num_hw_voices
+		xor	bl, bl
+		test	eax, eax
+		jle	short loc_5463D
+
+		xor	edi, edi
+
+loc_545E8:				; CODE XREF: CWTHal::InitializeWTRegs(void)+12Bj
+		push	0
+		push	edi
+		push	4
+		mov	ecx, esi
+		call	?SetReg@CWTHal@@QAEXEHK@Z ; CWTHal::SetReg(uchar,int,ulong)
+
+		push	0
+		push	edi
+		push	3
+		mov	ecx, esi
+		call	?SetReg@CWTHal@@QAEXEHK@Z ; CWTHal::SetReg(uchar,int,ulong)
+
+		push	0
+		push	edi
+		push	2
+		mov	ecx, esi
+		call	?SetReg@CWTHal@@QAEXEHK@Z ; CWTHal::SetReg(uchar,int,ulong)
+
+		push	0
+		push	edi
+		push	1
+		mov	ecx, esi
+		call	?SetReg@CWTHal@@QAEXEHK@Z ; CWTHal::SetReg(uchar,int,ulong)
+
+		push	0
+		push	edi
+		push	0Bh
+		mov	ecx, esi
+		call	?SetReg@CWTHal@@QAEXEHK@Z ; CWTHal::SetReg(uchar,int,ulong)
+
+		mov	eax, ?g_num_hw_voices@@3HA ; int g_num_hw_voices
+		inc	bl
+		mov	byte ptr [esp+20h+var_4], bl
+		mov	edi, [esp+20h+var_4]
+		and	edi, 0FFh
+		cmp	edi, eax
+		jl	short loc_545E8
+
+
+loc_5463D:				; CODE XREF: CWTHal::InitializeWTRegs(void)+D4j
+		mov	edx, [esp+20h+var_10]
+		or	edx, 1
+		mov	[esp+20h+var_10], edx
+		xor	edi, edi
+		mov	ebx, 2
+
+loc_5464F:				; CODE XREF: CWTHal::InitializeWTRegs(void)+150j
+		mov	eax, [esp+20h+var_10]
+		push	eax
+		push	edi
+		push	0Ah
+		mov	ecx, esi
+		call	?SetReg@CWTHal@@QAEXEHK@Z ; CWTHal::SetReg(uchar,int,ulong)
+
+		inc	edi
+		dec	ebx
+		jnz	short loc_5464F
+
+		pop	edi
+		pop	esi
+		pop	ebp
+		pop	ebx
+		add	esp, 10h
+		retn
+
+?InitializeWTRegs@CWTHal@@QAEXXZ endp
+
+
+
+*/
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/diff.cmd
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/diff.cmd	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/diff.cmd	(revision 91)
@@ -0,0 +1,1 @@
+diff.exe -Nuwr z:\ua2\GPL\alsa-kernel\pci\au88x0\%1 E:\DEV\alsa\alsa-driver\pci\au88x0\%1 >%1.diff
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/diff1.cmd
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/diff1.cmd	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/diff1.cmd	(revision 91)
@@ -0,0 +1,1 @@
+diff.exe -Nuwr z:\ua2\GPL\alsa-kernel\pci\au88x0\%1 E:\DEV\vortex\alsa\pci\au88x0\%1 >%1.diff
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/makefile.os2
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/makefile.os2	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/makefile.os2	(revision 91)
@@ -0,0 +1,50 @@
+CINCLUDES = ..\..\..\include;..\..\include;$(%WATCOM)\H
+AINCLUDES = $(DDK_BASE)\H
+
+CDEFINES = -D__KERNEL__ -DMODULE -dTARGET_OS2 -dALSA_BUILD -D__ISAPNP__ -DCONFIG_ISAPNP -D__i386_
+
+32BIT=1
+!include ..\..\..\..\makefile.inc
+
+
+#===================================================================
+#
+#   List of source files
+#
+#===================================================================
+#FILE1    = au8810.obj au8810_core.obj au8810_pcm.obj au8810_mixer.obj au8810_mpu401.obj au8810_eq.obj
+FILE2    = au8820.obj au8820_core.obj au8820_pcm.obj au8820_mixer.obj au8820_mpu401.obj au8820_synth.obj
+#FILE3    = au8830.obj au8830_core.obj au8830_pcm.obj au8830_mixer.obj au8830_mpu401.obj au8830_eq.obj au8830_synth.obj
+FILE5    = 
+FILE6    = 
+FILE7    = 
+FILE8    = 
+FILELAST =
+FILES    = $(FILE0) $(FILE1) $(FILE2) $(FILE3) $(FILE4) $(FILE5) $(FILE6) $(FILE7) $(FILE8) $(FILE9) $(FILE10) $(FILE11) $(FILE12)
+
+TARGET   = au88xx
+
+LNKFILE  = $(OBJDIR)\$(TARGET).lnk
+
+
+#===================================================================
+#
+#   Specific dependencies
+#
+#===================================================================
+all: $(OBJDIR)\$(TARGET).lib 
+
+
+$(LNKFILE): makefile.os2
+    @%create $(LNKFILE)
+    @for %f in ($(FILES)) do @%append $(LNKFILE) -+$(OBJDIR)\%f
+
+$(OBJDIR)\$(TARGET).lib: $(LNKFILE) $(FILES)
+    $(LIB) $(OBJDIR)\$(TARGET).lib @$(LNKFILE)
+    copy $(OBJDIR)\$(TARGET).lib $(ALSA_LIB) 
+
+clean: .SYMBOLIC
+    -@rm  *.err 2>nul
+    @cd $(OBJDIR)
+    -rm *.obj *.lnk *.sys *.sym *.lst *.map *.wmap *.lib 2>nul
+    @cd ..\..
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/migrate
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/migrate	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/migrate	(revision 91)
@@ -0,0 +1,20 @@
+#!/bin/sh
+
+# This script is intended to be used AFTER moving this
+# source code files into the alsa-kernel/ directory
+# to create the .c and .h stub files for THIS directory.
+# It was written for the alsa-driver tarball. If aplied
+# to the CVS tree, you may need add another "../"
+# on the include path.
+
+FILES=$(ls -1 -A $1/*.c $1/*.h)
+
+for file in $FILES
+do
+  fil=$(basename $file)
+  echo $file -- $fil
+  echo "#define __NO_VERSION__" > $fil
+  echo "#include \"../../alsa-kernel/pci/au88x0/$fil\"" >> $fil
+  echo >> $fil
+done
+
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/test/CVS/Entries
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/test/CVS/Entries	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/test/CVS/Entries	(revision 91)
@@ -0,0 +1,3 @@
+/au88x0_a3d.c/1.1/Mon Oct  6 14:01:04 2003/-ko/
+/au88x0_a3d.h/1.1/Mon Oct  6 14:01:06 2003/-ko/
+D
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/test/CVS/Repository
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/test/CVS/Repository	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/test/CVS/Repository	(revision 91)
@@ -0,0 +1,1 @@
+alsa-driver/pci/au88x0/test
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/test/CVS/Root
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/test/CVS/Root	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/test/CVS/Root	(revision 91)
@@ -0,0 +1,1 @@
+:pserver:anonymous@cvs.alsa-project.org:/cvsroot/alsa
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/test/au88x0_a3d.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/test/au88x0_a3d.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/test/au88x0_a3d.c	(revision 91)
@@ -0,0 +1,569 @@
+/***************************************************************************
+ *            au88x0_a3d.c
+ *
+ *  Fri Jul 18 14:16:22 2003
+ *  Copyright  2003  mjander
+ *  mjander@users.sourceforge.net
+ *
+ * A3D. You may think i'm crazy, but this may work someday. Who knows...
+ ****************************************************************************/
+
+/* CA3dSourceHw */
+void Asp4A3DTopology::MakeConnections(void) {
+	if (this_34 == 0) {
+		/* Connect A3DOUT to MIXIN */
+		Asp4Topology::Route((int) 1, (uchar) this_0c->this_0c, 
+			(uchar) this_08->this_04, (uchar) this_10 + 0x50, (uchar) this_14+0x50);
+		/* Connect MIXIN's this_10 and this_14 to MIXOUT this_18 */
+		(CAsp4Mix)this_18::EnableInput(int this_10);
+		(CAsp4Mix)this_18::EnableInput(int this_14);
+		/* Connect MIXOUT this_18 to SRCIN this_0c */
+		Asp4Topology::Route((int) 1, (uchar) this_0c, 
+			(uchar) this_18->this_08 + 0x30, (uchar) this_0c + 0x40);
+		/* Connect SRCOUT this_0c to A3DIN this_20 */
+		Asp4Topology::Route((int) 1, (uchar) 0x11, 
+			(uchar) this_0c->this0c + 0x20, (uchar) this_20 + 0x70);
+	} else {
+		if (this_08) {
+			/* Connect this_08 to SRCIN this_0c */
+			Asp4Topology::Route((int) 1, (uchar) this_0c, 
+				(uchar) this_08->this_04, (uchar) this_0c + 0x40);
+			/* Connect SRCOUT this_0c to A3DIN this_20 */
+			Asp4Topology::Route((int) 1, (uchar) 0x11, 
+				(uchar) this_0c->this0c + 0x20, (uchar) this_20 + 0x70);		
+		} else {
+			// I'm tired. I going to sleep now .
+			
+		}
+	}
+	
+}
+ 
+void CA3dSourceHw::CA3dSourceHw(CAsp4HwIO *hwio, int a,int b) {
+	CA3dSourceHw->this00 = hwio;
+	CA3dSourceHw->this04 = a;
+	CA3dSourceHw->this08 = b;
+}
+
+void CA3dSourceHw::Initialize(int a, int b) {
+	CA3dSourceHw->this04 = a;
+	CA3dSourceHw->this08 = b;
+	CA3dSourceHw::ZeroState(void);
+}
+
+#define addr(a,b,c) (a + (c << 0xd) + (d*(1 + (8*4 - 3)*8))*4)
+
+void CA3dSourceHw::SetTimeConsts(short a, short b, short c, short d) {
+	hwwrite(vortex->mmio, addr(0x1837C, this04, this08),  a);
+	hwwrite(vortex->mmio, addr(0x18388, this04, this08),  b);
+	hwwrite(vortex->mmio, addr(0x18380, this04, this08),  c);
+	hwwrite(vortex->mmio, addr(0x18384, this04, this08),  d);
+}
+
+void CA3dSourceHw::GetTimeConsts(short a, short b, short c, short d) {
+	
+}
+
+/* Atmospheric absorbtion. */
+void CA3dSourceHw::SetAtmosTarget(short a,short b,short c,short d,short e) {
+	hwwrite(vortex->mmio, addr(0x190Ec, this04, this08), (e << 0x10) | d);
+	hwwrite(vortex->mmio, addr(0x190F4, this04, this08), (b << 0x10) | a);
+	hwwrite(vortex->mmio, addr(0x190FC, this04, this08), c);
+}
+
+void CA3dSourceHw::SetAtmosTarget(short *a,short *b,short *c,short *d,short *e) {
+}
+
+void CA3dSourceHw::SetAtmosCurrent(short a,short b,short c,short d,short e) {
+	hwwrite(vortex->mmio, addr(0x190E8, this04, this08), (e << 0x10) | d);
+	hwwrite(vortex->mmio, addr(0x190f0, this04, this08), (b << 0x10) | a);
+	hwwrite(vortex->mmio, addr(0x190f8, this04, this08), c);
+}
+
+void CA3dSourceHw::GetAtmosCurrent(short *a,short *b,short *c,short *d,short *e) {
+	*d = hwread(vortex->mmio, addr(0x180e8, this04, this08));
+	*e = hwread(vortex->mmio, addr(0x190e8, this04, this08));
+	*b = hwread(vortex->mmio, addr(0x180f0, this04, this08));
+	*a = hwread(vortex->mmio, addr(0x190f0, this04, this08));
+	*c = hwread(vortex->mmio, addr(0x180f8, this04, this08));
+}
+
+void CA3dSourceHw::SetAtmosState(short a,short b,short c,short d) {
+	hwwrite(vortex->mmio, addr(0x1838C, this04, this08), a);
+	hwwrite(vortex->mmio, addr(0x18390, this04, this08), b);
+	hwwrite(vortex->mmio, addr(0x18394, this04, this08), c);
+	hwwrite(vortex->mmio, addr(0x18398, this04, this08), d);
+}
+
+void CA3dSourceHw::GetAtmosState(short a,short b,short c,short d) {
+	
+}
+
+
+#define addr2(a,b,c, i) (a + (((this08<<0xb)+i)<<2) + (this04*(5*9*2 - 1)*4*2))
+void CA3dSourceHw::SetHrtfTarget(short const *a,short const	*b) {
+	int i;
+	
+	for (i=0; i<0x38; i++)
+		hwwrite(vortex->mmio, addr2(0x19100, this04, this08, i), (b[i]<<0x10) | a[i]);
+}
+
+void CA3dSourceHw::GetHrtfTarget(short const *a,short const	*b) {
+}
+
+void CA3dSourceHw::SetHrtfCurrent(short const *a,short const *b) {
+	int i;
+	
+	for (i=0; i<0x38; i++)
+		hwwrite(vortex->mmio, addr2(0x19000, this04, this08, i), (b[i]<<0x10) | a[i]);	
+}
+
+void CA3dSourceHw::GetHrtfCurrent(short const *a,short const *b) {
+	int i;
+	// FIXME: verify this!
+	for (i=0; i<0x38; i++)
+		a[i] = hwread(vortex->mmio, addr2(0, this04+0xc, this08, i));
+	for (i=0; i<0x38; i++)
+		b[i] = hwread(vortex->mmio, addr2(0x19000, this04, this08, i));	
+}
+void CA3dSourceHw::SetHrtfState(short const *a,short const *b) {
+	int i;
+	
+	for (i=0; i<0x38; i++)
+		hwwrite(vortex->mmio, addr2(0x191e8, this04, this08, i), (b[i]<<0x10) | a[i]);	
+}
+
+void CA3dSourceHw::GetHrtfState(short const *a,short const *b) {
+	int i;
+	// FIXME: verify this!
+	for (i=0; i<0x38; i++)
+		a[i] = hwread(vortex->mmio, addr2(0, this04+0x6a, this08, i));
+	for (i=0; i<0x38; i++)
+		b[i] = hwread(vortex->mmio, addr2(0x191e8, this04, this08, i));	
+}
+
+void CA3dSourceHw::GetHrtfOutput(short *left,short *right) {
+	*left = hwread(vortex->mmio, addr(0x1839C, this04, this08));
+	*right = hwread(vortex->mmio, addr(0x183A0, this04, this08));
+}
+
+void CA3dSourceHw::SetHrtfOutput(short left,short right) {
+	hwwrite(vortex->mmio, addr(0x1839C, this04, this08), left);
+	hwwrite(vortex->mmio, addr(0x183a0, this04, this08), right);
+}
+
+/* Interaural Time Difference. 
+ * "The other main clue that humans use to locate sounds, is called 
+ * Interaural Time Difference (ITD). The differences in distance from 
+ * the sound source to a listeners ears means  that the sound will 
+ * reach one ear slightly before the other...." */
+void CA3dSourceHw::SetItdTarget(short litd,short ritd) {
+	if (litd < 0)
+		litd = 0;
+	if (litd > 0x57FF)
+		litd = 0x57FF;
+	if (ritd < 0)
+		ritd = 0;
+	if (ritd > 0x57FF)
+		ritd = 0x57FF;
+	hwwrite(vortex->mmio, addr(0x191DF+5, this04, this08), (ritd<<0x10)|litd);
+}
+
+void CA3dSourceHw::GetItdTarget(short *litd, short *ritd) {
+	*ritd = hwread(vortex->mmio, addr(0x181E4, this04, this08));
+	*litd = hwread(vortex->mmio, addr(0x191E4, this04, this08));
+}
+
+void CA3dSourceHw::SetItdCurrent(short litd, short ritd) {
+		litd = 0;
+	if (litd > 0x57FF)
+		litd = 0x57FF;
+	if (ritd < 0)
+		ritd = 0;
+	if (ritd > 0x57FF)
+		ritd = 0x57FF;
+	hwwrite(vortex->mmio, addr(0x191DF+1, this04, this08), (ritd<<0x10)|litd);
+}
+
+void CA3dSourceHw::GetItdCurrent(short *litd,short *ritd) {
+	*ritd = hwread(vortex->mmio, addr(0x181E0, this04, this08));
+	*litd = hwread(vortex->mmio, addr(0x191E0, this04, this08));
+}
+
+void CA3dSourceHw::SetItdDline(short const	*dline) {
+	int i;
+	
+	for (i=0; i<0x28; i++)
+		hwwrite(vortex->mmio, addr2(0x182C8, this04, this08, i), dline[i])
+}
+
+void CA3dSourceHw::GetItdDline(short *dline) {
+	int i;
+	
+	for (i=0; i<0x28; i++)
+		dline[i] = hwwrite(vortex->mmio, addr2(0x182C8, this04, this08, i));	
+}
+
+/* This is maybe used for IID Interaural Intensity Difference. */
+void CA3dSourceHw::SetGainTarget(short a,short b) {
+	hwwrite(vortex->mmio, addr(0x190E4, this04, this08), (b<<0x10)|a);
+}
+
+void CA3dSourceHw::GetGainTarget(short *,short *) {
+	*b = hwread(vortex->mmio, addr(0x180e4, this04, this08));
+	*a = hwread(vortex->mmio, addr(0x190e4, this04, this08));
+}
+
+void CA3dSourceHw::SetGainCurrent(short a,short b) {
+	hwwrite(vortex->mmio, addr(0x190DC+4, this04, this08), (b<<0x10)|a);
+}
+
+void CA3dSourceHw::GetGainCurrent(short *a,short *b) {
+	*b = hwread(vortex->mmio, addr(0x180e0, this04, this08));
+	*a = hwread(vortex->mmio, addr(0x190e0, this04, this08));
+}
+
+/* Generic A3D stuff */
+void CA3dSourceHw::SetA3DSampleRate(int sr) {
+	int esp0 = 0;
+	esp0 = (((esp0 & 0x7fffffff)|0xB8000000)&0x7) | ((sr&0x1f)<<3);
+	hwwrite(vortex->mmio, 0x19C38 + (this08<<0xd), esp0);
+}
+
+void CA3dSourceHw::GetA3DSampleRate(int *sr) {
+	*sr = ((hwread(vortex->mmio, 0x19C38 + (this08<<0xd))>>3)&0x1f);
+	
+}
+
+void CA3dSourceHw::EnableA3D(void) {
+	hwwrite(vortex->mmio, 19C38 + (this08<<0xd), 0xF0000001);
+}
+
+void CA3dSourceHw::DisableA3D(void) {
+	hwwrite(vortex->mmio, 19C38 + (this08<<0xd), 0xF0000000);
+}
+
+void CA3dSourceHw::SetA3DControlReg(unsigned long ctrl) {
+	hwwrite(vortex->mmio, 19C38 + (this08<<0xd), ctrl);
+}
+
+void CA3dSourceHw::GetA3DControlReg(unsigned long *ctrl) {
+	*ctrl = hwread(vortex->mmio, 19C38 + (this08<<0xd));
+}
+
+void CA3dSourceHw::SetA3DPointerReg(unsigned long ptr) {
+	hwwrite(vortex->mmio, 19c40 + (this08<<0xd), ctrl);
+}
+
+void CA3dSourceHw::GetA3DPointerReg(unsigned long *ptr) {
+	*ptr = hwread(vortex->mmio, 19C40 + (this08<<0xd));
+}
+
+void CA3dSourceHw::ZeroSliceIO(void) {
+	int i;
+	
+	for (i=0; i<8; i++)
+		hwwrite(vortex->mmio, 0x19C00 + (((this08<<0xb)+i)*4), 0);
+	for (i=0; i<4; i++)
+		hwwrite(vortex->mmio, 0x19C20 + (((this08<<0xb)+i)*4), 0);	
+}
+
+void CA3dSourceHw::ZeroState(void) {
+	int i;
+	
+	CA3dSourceHw::SetAtmosState(0,0,0,0);
+	CA3dSourceHw::SetHrtfState(A3dHrirZeros, A3dHrirZeros);
+	CA3dSourceHw::SetItdDline(A3dItdDlineZeros, A3dItdDlineZeros);
+	CA3dSourceHw::SetHrtfOutput(0, 0);
+	CA3dSourceHw::SetTimeConsts(0,0,0,0);
+	CA3dSourceHw::SetAtmosCurrent(0,0,0,0,0);
+	CA3dSourceHw::SetAtmosTarget(0,0,0,0,0);
+	CA3dSourceHw::SetItdCurrent(0,0);
+	CA3dSourceHw::SetItdTarget(0,0);
+	CA3dSourceHw::SetGainCurrent(0,0);
+	/* The person who originally wrote this surely was smoking crack ...
+	/* (or his/her compiler) */
+	CA3dIO::WriteReg(190E4 + (this08<<0xd) + (this04*5*9*2 - this04)*8,0,0);
+	
+	CA3dSourceHw::SetHrtfCurrent(A3dHrirZeros, A3dHrirZeros);
+	CA3dSourceHw::SetHrtfTarget(A3dHrirZeros, A3dHrirZeros)
+}
+
+void CA3dSourceHw::ZeroStateA3D(void) {
+	int i, ii, var, var2;
+	
+	CA3dSourceHw::SetA3DControlReg(0);
+	CA3dSourceHw::SetA3DPointerReg(0);
+	var = this08;
+	var2 = this04; // ??
+	for (ii=0; ii<4; ii++) {
+		this08 = ii;
+		
+		CA3dSourceHw::ZeroSliceIO(void);
+		CA3dSourceHw::ZeroState(void);	
+	}
+	this04 = var2;
+	this08 = var;
+}
+
+/* Program A3D block as pass through */
+void CA3dSourceHw::ProgramPipe(void) {
+	CA3dSourceHw::SetTimeConsts(0,0,0,0);
+	CA3dSourceHw::SetAtmosCurrent(0, 0x4000, 0,0,0);
+	CA3dSourceHw::SetAtmosTarget(0x4000,0,0,0,0);
+	CA3dSourceHw::SetItdCurrent(0,0);
+	CA3dSourceHw::SetItdTarget(0,0);
+	CA3dSourceHw::SetGainCurrent(0x7fff,0x7fff);
+	CA3dSourceHw::SetGainTarget(0x7fff,0x7fff);
+	CA3dSourceHw::SetHrtfCurrent(A3dHrirImpulse, A3dHrirImpulse);
+	CA3dSourceHw::SetHrtfTarget(A3dHrirImpulse, A3dHrirImpulse);
+}
+/* VDB = Vortex audio Dataflow Bus */
+void CA3dSourceHw::ClearVDBData(unsigned long a) {
+	hwwrite(vortex->mmio, 0x19c00 + (((a>>2)*255*4)+a)*8, 0);
+	hwwrite(vortex->mmio, 0x19c04 + (((a>>2)*255*4)+a)*8, 0);
+}
+/*CA3dIO*/
+
+void CA3dIO::WriteReg(unsigned long addr, short a, short b) {
+	hwwrite(vortex->mmio, addr, (a<<0x10)|b);
+}
+
+/* CHrtf */
+void CHrtf::CHrtf(void) {
+	this_00 = 0;
+	this_04 = 0;
+	this_08 = 0;
+}
+void CHrtf::Initialize(int arg_0 ,int arg_4 ,short *arg_8) {
+	this_04 = arg_0;
+	this_00 = arg_4;
+	this_08 = *arg_8;
+}
+void CHrtf::InterpolateHrtf(short *arg_0, short *arg_4, short *arg_8) {
+	
+	
+}
+/* CVort3dRend */
+
+void CVort3DRend::CVort3DRend(class CAsp4Core *core, class CAsp4HwIO *hwio) {
+	this00 = core;
+	this04 = hwio;
+	this08 = 0;
+}
+
+void CVort3DRend::~CVort3DRend(void) {
+	if (this08)
+		operator delete(this08);
+}
+
+int CVort3DRend::Initialize(unsigned short a) {
+	if (this00 == 0)
+		return -1;
+	this14 = a;
+	CXtalkHw = operator new(4);
+	CXtalkHw::CXtalkHw(this04);
+
+	CXtalkHw::SetGains(XtalkGainsAllChan);
+	if (this14) {
+		if (this08==0xffff) {
+			/* Speaker type 1 ? */
+			CXtalkHw::ProgramXtalkNarrow(void);
+		} else{
+			/* Speaker type 2 ? */
+			CXtalkHw::ProgramXtalkWide(void)
+		}
+	} else {
+		/* Headphones ? */
+		CXtalkHw::ProgramPipe(void);
+	}
+	CXtalkHw::SetSampleRate(0x11);
+	CXtalkHw::Enable(void);
+}
+
+int CVort3DRend::SetGlobalControl(struct _XTALKctrl *xtctrl) {
+	if (xtctrl==0)
+		return 0;
+	
+	this0c->this04 = xtctrl->this04;
+	this0c->this08 = xtctrl->this08;
+	this0c->this0c = xtctrl->this0c;
+	
+	this0c = xtctrl->this00;
+	CXtalkHw = this08; 
+	if (this14) {
+		if (this08==0xffff)
+			CXtalkHw::ProgramXtalkNarrow(void);
+		else
+			CXtalkHw::ProgramXtalkWide(void)
+	} else {
+		CXtalkHw::ProgramPipe(void);
+	}
+}
+
+int  CVort3DRend::GetGlobalControl(struct _XTALKctrl *xtctrl) {
+}
+int CVort3DRend::AddBuffer(class CVort3dWave *a) {
+	return 0;
+}
+int CVort3DRend::RemoveBuffer(class CVort3dWave *a) {
+	return 0;
+}
+/* CVort3dWave */
+void CVort3dWave::CVort3dWave(class CAsp4Core *,class CAsp4HwIO	*,class	CHrtfMgr *) {
+	
+}
+//174743
+void CVort3dWave::Initialize(struct _ASPWAVEFORMAT * arg_0, void (__stdcall *)(class CAuWave *,char *,unsigned long,int,unsigned long,unsigned long),unsigned long arg_4 ,void *arg_c) {
+	a3dsource_t a3d;
+	CWave::Initialize(_ASPWAVEFORMAT *arg_0 ,ASPDIRECTION 0,void (*)(CAuWave *,char *,ulong ,int,ulong,ulong) arg_4,void *arg_c, ulong 1);
+	
+	this_320 = 0;
+	this_324 = 0;
+	this_334 = (short)0xffff;
+	this_338 = (short)0x8000;
+	this_328 = 0;
+	this_77c = 0;
+	this_790 = 0;
+	this_794 = 0;
+	this_7a4 = 0;
+	this_7c8 = 0; /* struct ? */
+	this_79c = 0;
+	this_7a0 = 0;
+	this_7b4 = 0;
+	this_7c0 = 0;
+	this_7b0 = 0;
+	this_7bc = 0;
+	this_788 = 0;
+	this_7cc = 0;
+		
+	if ((a3d = CAdbTopology::GetA3DSource(void)) < 0) {
+		(CAsp4Core)this_318::GetProperty((COREPROPERTY) 4, void *arg_c, (ulong) 4);
+		if (arg_c == 1)
+			return 8007000E;
+	} else {
+		if (this_340 == 0) {
+			operator new((uint) 0xc, (_POOL_TYPE) 0, (ulong) 44334143);
+		
+		}
+	}
+}
+	
+void CVort3DWave::SetLRGains(void) {
+	
+	
+}
+
+int  CVort3dWave::Set3dParms(unsigned long offset, unsigned long arg4, void * arg8) {
+	
+	if (offset)
+		return 80070057;
+	if (arg4 == 0xf6) {
+		CVort3dWave::Anzio3dToSuperCtrl((Anzio3d *) arg8, (A3DCTRL_SRC_SUPER *) this_370);
+		CVort3dWave::Set3dParms((A3DCTRL_SRC_SUPER *) this_370);
+		return 0;
+	}
+	if (arg4 == 0x40) {
+		CVort3dWave::Set3dParms((A3DCTRL_SRC_SUPER *) arg8)
+	}
+}
+
+int  CVort3dWave::Set3dParms(struct A3DCTRL_SRC_SUPER *) {
+	int var14;
+	struct var0 {
+		int a;
+		short b;
+		char c;
+	};
+
+	CAsp4Core = this_318;
+	CAsp4Core::GetProperty((COREPROPERTY) 4, (void *) &var14, (ulong) 4);
+	CAsp4Core::GetProperty((COREPROPERTY) 1, (void *) &var0+0x10, (ulong) 0x10);
+	if ((var0.b != this_354)||(var14 != this_350)) {
+		this_354 = var0.b;
+		this_350 = var14;
+		CHrtfMgr = this_344;
+		if (var14 == 1) {
+			if (var0.b)
+				CHrtfMgr::Initialize((ulong) 0xBB80, (ulong) 0x3a, (ulong) 0x102)
+			else
+				CHrtfMgr::Initialize((ulong) 0xBB80, (ulong) 2, (ulong) 0x101);
+		} else
+			CHrtfMgr::Initialize((ulong) 0xBB80, (ulong) 0x38, (ulong) 0);
+	}
+	if (var14 == 1) {
+		if (var0.b == 0)
+			CVort3dWave::RenderQuadPan((A3DCTRL_SRC_SUPER *) arg0);
+		else
+			CVort3dWave::RenderSuperQuad((A3DCTRL_SRC_SUPER *) arg0);
+		return 0;
+	}
+	eax = CAdbTopology::GetA3DSource(void);
+	if (eax >= 0) 
+		CVort3dWave::Render3d((A3DCTRL_SRC_SUPER *) arg0);
+	else
+		CVort3dWave::Render2d((A3DCTRL_SRC_SUPER *) arg0);
+	return 0;
+}
+
+void CVort3dWave::Render2d(struct A3DCTRL_SRC_SUPER *) {
+	
+	CVort3dWave::Calc2d(A3DCTRL_EAR *,A3DCTRL_EAR	*,float,ulong *,_ASPVOLUME *);
+	
+	CVort3dWave::SetLRGains(void);
+	CAdbTopology::SetFilter(ulong);
+	
+}
+void CVort3dWave::Render3d(struct A3DCTRL_SRC_SUPER *) {
+	
+	CA3dSourceHw::SetAtmosTarget(short,short,short,short,short);
+	CA3dSourceHw::SetAtmosCurrent(short,short,short,short,short);
+	CVort3dWave::SetLRGains(void);
+	CA3dSourceHw::SetHrtfTarget(short const *,short const *);
+	CA3dSourceHw::SetHrtfCurrent(short const *,short const *)
+	CA3dSourceHw::SetItdTarget(short,short);
+	
+	
+}
+
+void CVort3dWave::RenderQuadPan(struct A3DCTRL_SRC_SUPER *) {
+	
+	
+}
+
+void CAdbTopology::SetFilter(unsigned long a) {
+	int eax;
+	
+	if (this_1E0 == 0)
+		return;  // "CAdbTopology::SetFilter - m_pWtDma not available\n"
+	if (a) {
+		if (a & 0xFFFF0000)
+			eax = 0xcf;
+		else {
+			a <<= 0x10;
+			eax = 0xc;
+			while (((a & 0x80000000) == 0)&&(eax > 0)) {
+				a <<= 1;
+				eax--;
+			}
+			if (eax) 
+				a << 1;
+			eax <<= 4;
+			a >>= 0x1c;
+			eax |= a;
+		}
+	} else
+		eax = 0;
+	this_1E4 = (this_1E4 & 0xFFFFFF) | (eax << 0x18);
+	this_1E8 = this_1E4 | 1;
+	AuWt::WriteReg(0x204 + (this_1DC << 4), this_1E8);
+	AuWt::WriteReg(0x200 + (this_1DC << 4), this_1E4);
+}
+
+void AuWt::WriteReg(unsigned long addr, unsigned long data) {
+	hwwrite(vortex->mmio, (this_04 << 0xf) + addr, data);
+}
+
+int  AuWt::ReadReg(unsigned long addr) {
+	return hwread(vortex->mmio, (this_04 << 0xf) + addr);
+}
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/test/au88x0_a3d.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/test/au88x0_a3d.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/au88x0/test/au88x0_a3d.h	(revision 91)
@@ -0,0 +1,19 @@
+/***************************************************************************
+ *            au88x0_a3d.h
+ *
+ *  Fri Jul 18 14:16:03 2003
+ *  Copyright  2003  mjander
+ *  mjander@users.sourceforge.net
+ ****************************************************************************/
+
+typedef struct {
+	ulong this00; // CAsp4hwIO
+	ulong this04; 
+	ulong this08; // A3D module Index
+	
+} CA3dSourceHw_t;
+
+
+short A3dHrirZeros[0x1C] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+short A3dItdDlineZeros[0x16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+short A3dHrirImpulse[0x1c] = {7FFFh,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/bt87x.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/bt87x.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/bt87x.c	(revision 91)
@@ -0,0 +1,952 @@
+/*
+ * bt87x.c - Brooktree Bt878/Bt879 driver for ALSA
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ *
+ * based on btaudio.c by Gerd Knorr <kraxel@bytesex.org>
+ *
+ *
+ *  This driver 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 driver is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+//#include <linux/moduleparam.h>
+#include <linux/bitops.h>
+#include <asm/io.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/control.h>
+#include <sound/initval.h>
+
+MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
+MODULE_DESCRIPTION("Brooktree Bt87x audio driver");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{Brooktree,Bt878},"
+                        "{Brooktree,Bt879}}");
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;	/* Enable this card */
+static int digital_rate[SNDRV_CARDS] = { 0,0,0,0,0,0,0,0 }; /* digital input rate */
+static int load_all;	/* allow to load the non-whitelisted cards */
+
+//module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for Bt87x soundcard");
+//module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for Bt87x soundcard");
+//module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable Bt87x soundcard");
+//module_param_array(digital_rate, int, NULL, 0444);
+MODULE_PARM_DESC(digital_rate, "Digital input rate for Bt87x soundcard");
+//module_param(load_all, bool, 0444);
+MODULE_PARM_DESC(load_all, "Allow to load the non-whitelisted cards");
+
+
+#ifndef PCI_VENDOR_ID_BROOKTREE
+#define PCI_VENDOR_ID_BROOKTREE 0x109e
+#endif
+#ifndef PCI_DEVICE_ID_BROOKTREE_878
+#define PCI_DEVICE_ID_BROOKTREE_878 0x0878
+#endif
+#ifndef PCI_DEVICE_ID_BROOKTREE_879
+#define PCI_DEVICE_ID_BROOKTREE_879 0x0879
+#endif
+
+/* register offsets */
+#define REG_INT_STAT		0x100	/* interrupt status */
+#define REG_INT_MASK		0x104	/* interrupt mask */
+#define REG_GPIO_DMA_CTL	0x10c	/* audio control */
+#define REG_PACKET_LEN		0x110	/* audio packet lengths */
+#define REG_RISC_STRT_ADD	0x114	/* RISC program start address */
+#define REG_RISC_COUNT		0x120	/* RISC program counter */
+
+/* interrupt bits */
+#define INT_OFLOW	(1 <<  3)	/* audio A/D overflow */
+#define INT_RISCI	(1 << 11)	/* RISC instruction IRQ bit set */
+#define INT_FBUS	(1 << 12)	/* FIFO overrun due to bus access latency */
+#define INT_FTRGT	(1 << 13)	/* FIFO overrun due to target latency */
+#define INT_FDSR	(1 << 14)	/* FIFO data stream resynchronization */
+#define INT_PPERR	(1 << 15)	/* PCI parity error */
+#define INT_RIPERR	(1 << 16)	/* RISC instruction parity error */
+#define INT_PABORT	(1 << 17)	/* PCI master or target abort */
+#define INT_OCERR	(1 << 18)	/* invalid opcode */
+#define INT_SCERR	(1 << 19)	/* sync counter overflow */
+#define INT_RISC_EN	(1 << 27)	/* DMA controller running */
+#define INT_RISCS_SHIFT	      28	/* RISC status bits */
+
+/* audio control bits */
+#define CTL_FIFO_ENABLE		(1 <<  0)	/* enable audio data FIFO */
+#define CTL_RISC_ENABLE		(1 <<  1)	/* enable audio DMA controller */
+#define CTL_PKTP_4		(0 <<  2)	/* packet mode FIFO trigger point - 4 DWORDs */
+#define CTL_PKTP_8		(1 <<  2)	/* 8 DWORDs */
+#define CTL_PKTP_16		(2 <<  2)	/* 16 DWORDs */
+#define CTL_ACAP_EN		(1 <<  4)	/* enable audio capture */
+#define CTL_DA_APP		(1 <<  5)	/* GPIO input */
+#define CTL_DA_IOM_AFE		(0 <<  6)	/* audio A/D input */
+#define CTL_DA_IOM_DA		(1 <<  6)	/* digital audio input */
+#define CTL_DA_SDR_SHIFT	       8	/* DDF first stage decimation rate */
+#define CTL_DA_SDR_MASK		(0xf<< 8)
+#define CTL_DA_LMT		(1 << 12)	/* limit audio data values */
+#define CTL_DA_ES2		(1 << 13)	/* enable DDF stage 2 */
+#define CTL_DA_SBR		(1 << 14)	/* samples rounded to 8 bits */
+#define CTL_DA_DPM		(1 << 15)	/* data packet mode */
+#define CTL_DA_LRD_SHIFT	      16	/* ALRCK delay */
+#define CTL_DA_MLB		(1 << 21)	/* MSB/LSB format */
+#define CTL_DA_LRI		(1 << 22)	/* left/right indication */
+#define CTL_DA_SCE		(1 << 23)	/* sample clock edge */
+#define CTL_A_SEL_STV		(0 << 24)	/* TV tuner audio input */
+#define CTL_A_SEL_SFM		(1 << 24)	/* FM audio input */
+#define CTL_A_SEL_SML		(2 << 24)	/* mic/line audio input */
+#define CTL_A_SEL_SMXC		(3 << 24)	/* MUX bypass */
+#define CTL_A_SEL_SHIFT		      24
+#define CTL_A_SEL_MASK		(3 << 24)
+#define CTL_A_PWRDN		(1 << 26)	/* analog audio power-down */
+#define CTL_A_G2X		(1 << 27)	/* audio gain boost */
+#define CTL_A_GAIN_SHIFT	      28	/* audio input gain */
+#define CTL_A_GAIN_MASK		(0xf<<28)
+
+/* RISC instruction opcodes */
+#define RISC_WRITE	(0x1 << 28)	/* write FIFO data to memory at address */
+#define RISC_WRITEC	(0x5 << 28)	/* write FIFO data to memory at current address */
+#define RISC_SKIP	(0x2 << 28)	/* skip FIFO data */
+#define RISC_JUMP	(0x7 << 28)	/* jump to address */
+#define RISC_SYNC	(0x8 << 28)	/* synchronize with FIFO */
+
+/* RISC instruction bits */
+#define RISC_BYTES_ENABLE	(0xf << 12)	/* byte enable bits */
+#define RISC_RESYNC		(  1 << 15)	/* disable FDSR errors */
+#define RISC_SET_STATUS_SHIFT	        16	/* set status bits */
+#define RISC_RESET_STATUS_SHIFT	        20	/* clear status bits */
+#define RISC_IRQ		(  1 << 24)	/* interrupt */
+#define RISC_EOL		(  1 << 26)	/* end of line */
+#define RISC_SOL		(  1 << 27)	/* start of line */
+
+/* SYNC status bits values */
+#define RISC_SYNC_FM1	0x6
+#define RISC_SYNC_VRO	0xc
+
+#define ANALOG_CLOCK 1792000
+#ifdef CONFIG_SND_BT87X_OVERCLOCK
+#define CLOCK_DIV_MIN 1
+#else
+#define CLOCK_DIV_MIN 4
+#endif
+#define CLOCK_DIV_MAX 15
+
+#define ERROR_INTERRUPTS (INT_FBUS | INT_FTRGT | INT_PPERR | \
+    INT_RIPERR | INT_PABORT | INT_OCERR)
+#define MY_INTERRUPTS (INT_RISCI | ERROR_INTERRUPTS)
+
+/* SYNC, one WRITE per line, one extra WRITE per page boundary, SYNC, JUMP */
+#define MAX_RISC_SIZE ((1 + 255 + (PAGE_ALIGN(255 * 4092) / PAGE_SIZE - 1) + 1 + 1) * 8)
+
+typedef struct snd_bt87x bt87x_t;
+struct snd_bt87x {
+    snd_card_t *card;
+    struct pci_dev *pci;
+
+    void *mmio;
+    int irq;
+
+    int dig_rate;
+
+    spinlock_t reg_lock;
+    long opened;
+    snd_pcm_substream_t *substream;
+
+    struct snd_dma_buffer dma_risc;
+    unsigned int line_bytes;
+    unsigned int lines;
+
+    u32 reg_control;
+    u32 interrupt_mask;
+
+    int current_line;
+
+    int pci_parity_errors;
+};
+
+enum { DEVICE_DIGITAL, DEVICE_ANALOG };
+
+static inline u32 snd_bt87x_readl(bt87x_t *chip, u32 reg)
+{
+    return readl((char*)chip->mmio + reg);
+}
+
+static inline void snd_bt87x_writel(bt87x_t *chip, u32 reg, u32 value)
+{
+    writel(value, (char*)chip->mmio + reg);
+}
+
+static int snd_bt87x_create_risc(bt87x_t *chip, snd_pcm_substream_t *substream,
+                                 unsigned int periods, unsigned int period_bytes)
+{
+    struct snd_sg_buf *sgbuf = snd_pcm_substream_sgbuf(substream);
+    unsigned int i, offset;
+    u32 *risc;
+
+    if (chip->dma_risc.area == NULL) {
+        if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci),
+                                PAGE_ALIGN(MAX_RISC_SIZE), &chip->dma_risc) < 0)
+            return -ENOMEM;
+    }
+    risc = (u32 *)chip->dma_risc.area;
+    offset = 0;
+    *risc++ = cpu_to_le32(RISC_SYNC | RISC_SYNC_FM1);
+    *risc++ = cpu_to_le32(0);
+    for (i = 0; i < periods; ++i) {
+        u32 rest;
+
+        rest = period_bytes;
+        do {
+            u32 cmd, len;
+
+            len = PAGE_SIZE - (offset % PAGE_SIZE);
+            if (len > rest)
+                len = rest;
+            cmd = RISC_WRITE | len;
+            if (rest == period_bytes) {
+                u32 block = i * 16 / periods;
+                cmd |= RISC_SOL;
+                cmd |= block << RISC_SET_STATUS_SHIFT;
+                cmd |= (~block & 0xf) << RISC_RESET_STATUS_SHIFT;
+            }
+            if (len == rest)
+                cmd |= RISC_EOL | RISC_IRQ;
+            *risc++ = cpu_to_le32(cmd);
+            *risc++ = cpu_to_le32((u32)snd_pcm_sgbuf_get_addr(sgbuf, offset));
+            offset += len;
+            rest -= len;
+        } while (rest > 0);
+    }
+    *risc++ = cpu_to_le32(RISC_SYNC | RISC_SYNC_VRO);
+    *risc++ = cpu_to_le32(0);
+    *risc++ = cpu_to_le32(RISC_JUMP);
+    *risc++ = cpu_to_le32(chip->dma_risc.addr);
+    chip->line_bytes = period_bytes;
+    chip->lines = periods;
+    return 0;
+}
+
+static void snd_bt87x_free_risc(bt87x_t *chip)
+{
+    if (chip->dma_risc.area) {
+        snd_dma_free_pages(&chip->dma_risc);
+        chip->dma_risc.area = NULL;
+    }
+}
+
+static void snd_bt87x_pci_error(bt87x_t *chip, unsigned int status)
+{
+    u16 pci_status;
+
+    pci_read_config_word(chip->pci, PCI_STATUS, &pci_status);
+    pci_status &= PCI_STATUS_PARITY | PCI_STATUS_SIG_TARGET_ABORT |
+        PCI_STATUS_REC_TARGET_ABORT | PCI_STATUS_REC_MASTER_ABORT |
+        PCI_STATUS_SIG_SYSTEM_ERROR | PCI_STATUS_DETECTED_PARITY;
+    pci_write_config_word(chip->pci, PCI_STATUS, pci_status);
+    if (pci_status != PCI_STATUS_DETECTED_PARITY)
+        snd_printk(KERN_ERR "Aieee - PCI error! status %#08x, PCI status %#04x\n",
+                   status & ERROR_INTERRUPTS, pci_status);
+    else {
+        snd_printk(KERN_ERR "Aieee - PCI parity error detected!\n");
+        /* error 'handling' similar to aic7xxx_pci.c: */
+        chip->pci_parity_errors++;
+        if (chip->pci_parity_errors > 20) {
+            snd_printk(KERN_ERR "Too many PCI parity errors observed.\n");
+            snd_printk(KERN_ERR "Some device on this bus is generating bad parity.\n");
+            snd_printk(KERN_ERR "This is an error *observed by*, not *generated by*, this card.\n");
+            snd_printk(KERN_ERR "PCI parity error checking has been disabled.\n");
+            chip->interrupt_mask &= ~(INT_PPERR | INT_RIPERR);
+            snd_bt87x_writel(chip, REG_INT_MASK, chip->interrupt_mask);
+        }
+    }
+}
+
+static irqreturn_t snd_bt87x_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+    bt87x_t *chip = dev_id;
+    unsigned int status, irq_status;
+
+    status = snd_bt87x_readl(chip, REG_INT_STAT);
+    irq_status = status & chip->interrupt_mask;
+    if (!irq_status)
+        return IRQ_NONE;
+    snd_bt87x_writel(chip, REG_INT_STAT, irq_status);
+
+    if (irq_status & ERROR_INTERRUPTS) {
+        if (irq_status & (INT_FBUS | INT_FTRGT))
+            snd_printk(KERN_WARNING "FIFO overrun, status %#08x\n", status);
+        if (irq_status & INT_OCERR)
+            snd_printk(KERN_ERR "internal RISC error, status %#08x\n", status);
+        if (irq_status & (INT_PPERR | INT_RIPERR | INT_PABORT))
+            snd_bt87x_pci_error(chip, irq_status);
+    }
+    if ((irq_status & INT_RISCI) && (chip->reg_control & CTL_ACAP_EN)) {
+        int current_block, irq_block;
+
+        /* assume that exactly one line has been recorded */
+        chip->current_line = (chip->current_line + 1) % chip->lines;
+        /* but check if some interrupts have been skipped */
+        current_block = chip->current_line * 16 / chip->lines;
+        irq_block = status >> INT_RISCS_SHIFT;
+        if (current_block != irq_block)
+            chip->current_line = (irq_block * chip->lines + 15) / 16;
+
+        snd_pcm_period_elapsed(chip->substream);
+    }
+#ifdef TARGET_OS2
+    eoi_irq(irq);
+#endif //TARGET_OS2
+
+    return IRQ_HANDLED;
+}
+
+static snd_pcm_hardware_t snd_bt87x_digital_hw = {
+    /*.info = */SNDRV_PCM_INFO_MMAP |
+        SNDRV_PCM_INFO_INTERLEAVED |
+        SNDRV_PCM_INFO_BLOCK_TRANSFER |
+        SNDRV_PCM_INFO_MMAP_VALID,
+    /*    .formats = */SNDRV_PCM_FMTBIT_S16_LE,
+    /*    .rates = */0, /* set at runtime */
+    0,0,
+    /*    .channels_min = */2,
+    /*    .channels_max = */2,
+    /*    .buffer_bytes_max = */255 * 4092,
+    /*    .period_bytes_min = */32,
+    /*    .period_bytes_max = */4092,
+    /*    .periods_min = */2,
+    /*    .periods_max = */255,
+    0
+};
+
+static snd_pcm_hardware_t snd_bt87x_analog_hw = {
+    /*.info = */SNDRV_PCM_INFO_MMAP |
+        SNDRV_PCM_INFO_INTERLEAVED |
+        SNDRV_PCM_INFO_BLOCK_TRANSFER |
+        SNDRV_PCM_INFO_MMAP_VALID,
+    /*    .formats = */SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8,
+    /*    .rates = */SNDRV_PCM_RATE_KNOT,
+    /*    .rate_min = */ANALOG_CLOCK / CLOCK_DIV_MAX,
+    /*    .rate_max = */ANALOG_CLOCK / CLOCK_DIV_MIN,
+    /*    .channels_min = */1,
+    /*    .channels_max = */1,
+    /*    .buffer_bytes_max = */255 * 4092,
+    /*    .period_bytes_min = */32,
+    /*    .period_bytes_max = */4092,
+    /*    .periods_min = */2,
+    /*    .periods_max = */255,
+    0
+};
+
+static int snd_bt87x_set_digital_hw(bt87x_t *chip, snd_pcm_runtime_t *runtime)
+{
+    static struct {
+        int rate;
+        unsigned int bit;
+    } ratebits[] = {
+        {8000, SNDRV_PCM_RATE_8000},
+        {11025, SNDRV_PCM_RATE_11025},
+        {16000, SNDRV_PCM_RATE_16000},
+        {22050, SNDRV_PCM_RATE_22050},
+        {32000, SNDRV_PCM_RATE_32000},
+        {44100, SNDRV_PCM_RATE_44100},
+        {48000, SNDRV_PCM_RATE_48000}
+    };
+    int i;
+
+    chip->reg_control |= CTL_DA_IOM_DA;
+    runtime->hw = snd_bt87x_digital_hw;
+    runtime->hw.rates = SNDRV_PCM_RATE_KNOT;
+    for (i = 0; i < ARRAY_SIZE(ratebits); ++i)
+        if (chip->dig_rate == ratebits[i].rate) {
+            runtime->hw.rates = ratebits[i].bit;
+            break;
+        }
+    runtime->hw.rate_min = chip->dig_rate;
+    runtime->hw.rate_max = chip->dig_rate;
+    return 0;
+}
+
+static int snd_bt87x_set_analog_hw(bt87x_t *chip, snd_pcm_runtime_t *runtime)
+{
+    static ratnum_t analog_clock = {
+        /*.num = */ANALOG_CLOCK,
+        /*.den_min = */CLOCK_DIV_MIN,
+        /*.den_max = */CLOCK_DIV_MAX,
+        /*.den_step = */1
+    };
+    static snd_pcm_hw_constraint_ratnums_t constraint_rates = {
+        /*.nrats = */1,
+        /*.rats = */&analog_clock
+    };
+
+    chip->reg_control &= ~CTL_DA_IOM_DA;
+    runtime->hw = snd_bt87x_analog_hw;
+    return snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+                                         &constraint_rates);
+}
+
+static int snd_bt87x_pcm_open(snd_pcm_substream_t *substream)
+{
+    bt87x_t *chip = snd_pcm_substream_chip(substream);
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    int err;
+
+    if (test_and_set_bit(0, &chip->opened))
+        return -EBUSY;
+
+    if (substream->pcm->device == DEVICE_DIGITAL)
+        err = snd_bt87x_set_digital_hw(chip, runtime);
+    else
+        err = snd_bt87x_set_analog_hw(chip, runtime);
+    if (err < 0)
+        goto _error;
+
+    err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+    if (err < 0)
+        goto _error;
+
+    chip->substream = substream;
+    return 0;
+
+_error:
+    clear_bit(0, &chip->opened);
+#ifndef TARGET_OS2
+    smp_mb__after_clear_bit();
+#endif
+    return err;
+}
+
+static int snd_bt87x_close(snd_pcm_substream_t *substream)
+{
+    bt87x_t *chip = snd_pcm_substream_chip(substream);
+
+    chip->substream = NULL;
+    clear_bit(0, &chip->opened);
+#ifndef TARGET_OS2
+    smp_mb__after_clear_bit();
+#endif
+    return 0;
+}
+
+static int snd_bt87x_hw_params(snd_pcm_substream_t *substream,
+                               snd_pcm_hw_params_t *hw_params)
+{
+    bt87x_t *chip = snd_pcm_substream_chip(substream);
+    int err;
+
+    err = snd_pcm_lib_malloc_pages(substream,
+                                   params_buffer_bytes(hw_params));
+    if (err < 0)
+        return err;
+    return snd_bt87x_create_risc(chip, substream,
+                                 params_periods(hw_params),
+                                 params_period_bytes(hw_params));
+}
+
+static int snd_bt87x_hw_free(snd_pcm_substream_t *substream)
+{
+    bt87x_t *chip = snd_pcm_substream_chip(substream);
+
+    snd_bt87x_free_risc(chip);
+    snd_pcm_lib_free_pages(substream);
+    return 0;
+}
+
+static int snd_bt87x_prepare(snd_pcm_substream_t *substream)
+{
+    bt87x_t *chip = snd_pcm_substream_chip(substream);
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    int decimation;
+
+    spin_lock_irq(&chip->reg_lock);
+    chip->reg_control &= ~(CTL_DA_SDR_MASK | CTL_DA_SBR);
+    decimation = (ANALOG_CLOCK + runtime->rate / 4) / runtime->rate;
+    chip->reg_control |= decimation << CTL_DA_SDR_SHIFT;
+    if (runtime->format == SNDRV_PCM_FORMAT_S8)
+        chip->reg_control |= CTL_DA_SBR;
+    snd_bt87x_writel(chip, REG_GPIO_DMA_CTL, chip->reg_control);
+    spin_unlock_irq(&chip->reg_lock);
+    return 0;
+}
+
+static int snd_bt87x_start(bt87x_t *chip)
+{
+    spin_lock(&chip->reg_lock);
+    chip->current_line = 0;
+    chip->reg_control |= CTL_FIFO_ENABLE | CTL_RISC_ENABLE | CTL_ACAP_EN;
+    snd_bt87x_writel(chip, REG_RISC_STRT_ADD, chip->dma_risc.addr);
+    snd_bt87x_writel(chip, REG_PACKET_LEN,
+                     chip->line_bytes | (chip->lines << 16));
+    snd_bt87x_writel(chip, REG_INT_MASK, chip->interrupt_mask);
+    snd_bt87x_writel(chip, REG_GPIO_DMA_CTL, chip->reg_control);
+    spin_unlock(&chip->reg_lock);
+    return 0;
+}
+
+static int snd_bt87x_stop(bt87x_t *chip)
+{
+    spin_lock(&chip->reg_lock);
+    chip->reg_control &= ~(CTL_FIFO_ENABLE | CTL_RISC_ENABLE | CTL_ACAP_EN);
+    snd_bt87x_writel(chip, REG_GPIO_DMA_CTL, chip->reg_control);
+    snd_bt87x_writel(chip, REG_INT_MASK, 0);
+    snd_bt87x_writel(chip, REG_INT_STAT, MY_INTERRUPTS);
+    spin_unlock(&chip->reg_lock);
+    return 0;
+}
+
+static int snd_bt87x_trigger(snd_pcm_substream_t *substream, int cmd)
+{
+    bt87x_t *chip = snd_pcm_substream_chip(substream);
+
+    switch (cmd) {
+    case SNDRV_PCM_TRIGGER_START:
+        return snd_bt87x_start(chip);
+    case SNDRV_PCM_TRIGGER_STOP:
+        return snd_bt87x_stop(chip);
+    default:
+        return -EINVAL;
+    }
+}
+
+static snd_pcm_uframes_t snd_bt87x_pointer(snd_pcm_substream_t *substream)
+{
+    bt87x_t *chip = snd_pcm_substream_chip(substream);
+    snd_pcm_runtime_t *runtime = substream->runtime;
+
+    return (snd_pcm_uframes_t)bytes_to_frames(runtime, chip->current_line * chip->line_bytes);
+}
+
+static snd_pcm_ops_t snd_bt87x_pcm_ops = {
+    /*.open = */snd_bt87x_pcm_open,
+    /*.close = */snd_bt87x_close,
+    /*.ioctl = */snd_pcm_lib_ioctl,
+    /*.hw_params = */snd_bt87x_hw_params,
+    /*.hw_free = */snd_bt87x_hw_free,
+    /*.prepare = */snd_bt87x_prepare,
+    /*.trigger = */snd_bt87x_trigger,
+    /*.pointer = */snd_bt87x_pointer,0,0,
+    /*.page = */snd_pcm_sgbuf_ops_page,0
+};
+
+static int snd_bt87x_capture_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *info)
+{
+    info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+    info->count = 1;
+    info->value.integer.min = 0;
+    info->value.integer.max = 15;
+    return 0;
+}
+
+static int snd_bt87x_capture_volume_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *value)
+{
+    bt87x_t *chip = snd_kcontrol_chip(kcontrol);
+
+    value->value.integer.value[0] = (chip->reg_control & CTL_A_GAIN_MASK) >> CTL_A_GAIN_SHIFT;
+    return 0;
+}
+
+static int snd_bt87x_capture_volume_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *value)
+{
+    bt87x_t *chip = snd_kcontrol_chip(kcontrol);
+    u32 old_control;
+    int changed;
+
+    spin_lock_irq(&chip->reg_lock);
+    old_control = chip->reg_control;
+    chip->reg_control = (chip->reg_control & ~CTL_A_GAIN_MASK)
+        | (value->value.integer.value[0] << CTL_A_GAIN_SHIFT);
+    snd_bt87x_writel(chip, REG_GPIO_DMA_CTL, chip->reg_control);
+    changed = old_control != chip->reg_control;
+    spin_unlock_irq(&chip->reg_lock);
+    return changed;
+}
+
+static snd_kcontrol_new_t snd_bt87x_capture_volume = {
+    /*.iface = */SNDRV_CTL_ELEM_IFACE_MIXER,0,0,
+    /*.name = */"Capture Volume",0,0,0,
+    /*.info = */snd_bt87x_capture_volume_info,
+    /*.get = */snd_bt87x_capture_volume_get,
+    /*.put = */snd_bt87x_capture_volume_put,0
+};
+
+static int snd_bt87x_capture_boost_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *info)
+{
+    info->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+    info->count = 1;
+    info->value.integer.min = 0;
+    info->value.integer.max = 1;
+    return 0;
+}
+
+static int snd_bt87x_capture_boost_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *value)
+{
+    bt87x_t *chip = snd_kcontrol_chip(kcontrol);
+
+    value->value.integer.value[0] = !! (chip->reg_control & CTL_A_G2X);
+    return 0;
+}
+
+static int snd_bt87x_capture_boost_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *value)
+{
+    bt87x_t *chip = snd_kcontrol_chip(kcontrol);
+    u32 old_control;
+    int changed;
+
+    spin_lock_irq(&chip->reg_lock);
+    old_control = chip->reg_control;
+    chip->reg_control = (chip->reg_control & ~CTL_A_G2X)
+        | (value->value.integer.value[0] ? CTL_A_G2X : 0);
+    snd_bt87x_writel(chip, REG_GPIO_DMA_CTL, chip->reg_control);
+    changed = chip->reg_control != old_control;
+    spin_unlock_irq(&chip->reg_lock);
+    return changed;
+}
+
+static snd_kcontrol_new_t snd_bt87x_capture_boost = {
+    /*.iface = */SNDRV_CTL_ELEM_IFACE_MIXER,0,0,
+    /*.name = */"Capture Boost",0,0,0,
+    /*.info = */snd_bt87x_capture_boost_info,
+    /*.get = */snd_bt87x_capture_boost_get,
+    /*.put = */snd_bt87x_capture_boost_put,0
+};
+
+static int snd_bt87x_capture_source_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *info)
+{
+    static char *texts[3] = {"TV Tuner", "FM", "Mic/Line"};
+
+    info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+    info->count = 1;
+    info->value.enumerated.items = 3;
+    if (info->value.enumerated.item > 2)
+        info->value.enumerated.item = 2;
+    strcpy(info->value.enumerated.name, texts[info->value.enumerated.item]);
+    return 0;
+}
+
+static int snd_bt87x_capture_source_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *value)
+{
+    bt87x_t *chip = snd_kcontrol_chip(kcontrol);
+
+    value->value.enumerated.item[0] = (chip->reg_control & CTL_A_SEL_MASK) >> CTL_A_SEL_SHIFT;
+    return 0;
+}
+
+static int snd_bt87x_capture_source_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *value)
+{
+    bt87x_t *chip = snd_kcontrol_chip(kcontrol);
+    u32 old_control;
+    int changed;
+
+    spin_lock_irq(&chip->reg_lock);
+    old_control = chip->reg_control;
+    chip->reg_control = (chip->reg_control & ~CTL_A_SEL_MASK)
+        | (value->value.enumerated.item[0] << CTL_A_SEL_SHIFT);
+    snd_bt87x_writel(chip, REG_GPIO_DMA_CTL, chip->reg_control);
+    changed = chip->reg_control != old_control;
+    spin_unlock_irq(&chip->reg_lock);
+    return changed;
+}
+
+static snd_kcontrol_new_t snd_bt87x_capture_source = {
+    /*.iface =*/ SNDRV_CTL_ELEM_IFACE_MIXER,0,0,
+    /*.name = */"Capture Source",0,0,0,
+    /*.info = */snd_bt87x_capture_source_info,
+    /*.get = */snd_bt87x_capture_source_get,
+    /*.put = */snd_bt87x_capture_source_put,0
+};
+
+static int snd_bt87x_free(bt87x_t *chip)
+{
+    if (chip->mmio) {
+        snd_bt87x_stop(chip);
+        if (chip->irq >= 0)
+            synchronize_irq(chip->irq);
+
+        iounmap(chip->mmio);
+    }
+    if (chip->irq >= 0)
+        free_irq(chip->irq, chip);
+    pci_release_regions(chip->pci);
+    pci_disable_device(chip->pci);
+    kfree(chip);
+    return 0;
+}
+
+static int snd_bt87x_dev_free(snd_device_t *device)
+{
+    bt87x_t *chip = device->device_data;
+    return snd_bt87x_free(chip);
+}
+
+static int __devinit snd_bt87x_pcm(bt87x_t *chip, int device, char *name)
+{
+    int err;
+    snd_pcm_t *pcm;
+
+    err = snd_pcm_new(chip->card, name, device, 0, 1, &pcm);
+    if (err < 0)
+        return err;
+    pcm->private_data = chip;
+    strcpy(pcm->name, name);
+    snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_bt87x_pcm_ops);
+    return snd_pcm_lib_preallocate_pages_for_all(pcm,
+                                                 SNDRV_DMA_TYPE_DEV_SG,
+                                                 snd_dma_pci_data(chip->pci),
+                                                 128 * 1024,
+                                                 (255 * 4092 + 1023) & ~1023);
+}
+
+static int __devinit snd_bt87x_create(snd_card_t *card,
+                                      struct pci_dev *pci,
+                                      bt87x_t **rchip)
+{
+    bt87x_t *chip;
+    int err;
+    static snd_device_ops_t ops = {
+        /*.dev_free = */snd_bt87x_dev_free,0,0,0
+    };
+
+    *rchip = NULL;
+
+    err = pci_enable_device(pci);
+    if (err < 0)
+        return err;
+
+    chip = kcalloc(1, sizeof(*chip), GFP_KERNEL);
+    if (!chip) {
+        pci_disable_device(pci);
+        return -ENOMEM;
+    }
+    chip->card = card;
+    chip->pci = pci;
+    chip->irq = -1;
+    spin_lock_init(&chip->reg_lock);
+
+    if ((err = pci_request_regions(pci, "Bt87x audio")) < 0) {
+        kfree(chip);
+        pci_disable_device(pci);
+        return err;
+    }
+    chip->mmio = ioremap_nocache(pci_resource_start(pci, 0),
+                                 pci_resource_len(pci, 0));
+    if (!chip->mmio) {
+        snd_bt87x_free(chip);
+        snd_printk(KERN_ERR "cannot remap io memory\n");
+        return -ENOMEM;
+    }
+
+    chip->reg_control = CTL_DA_ES2 | CTL_PKTP_16 | (15 << CTL_DA_SDR_SHIFT);
+    chip->interrupt_mask = MY_INTERRUPTS;
+    snd_bt87x_writel(chip, REG_GPIO_DMA_CTL, chip->reg_control);
+    snd_bt87x_writel(chip, REG_INT_MASK, 0);
+    snd_bt87x_writel(chip, REG_INT_STAT, MY_INTERRUPTS);
+
+    if (request_irq(pci->irq, snd_bt87x_interrupt, SA_INTERRUPT | SA_SHIRQ,
+                    "Bt87x audio", chip)) {
+        snd_bt87x_free(chip);
+        snd_printk(KERN_ERR "cannot grab irq\n");
+        return -EBUSY;
+    }
+    chip->irq = pci->irq;
+    pci_set_master(pci);
+    synchronize_irq(chip->irq);
+
+    err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+    if (err < 0) {
+        snd_bt87x_free(chip);
+        return err;
+    }
+    snd_card_set_dev(card, &pci->dev);
+    *rchip = chip;
+    return 0;
+}
+
+#define BT_DEVICE(chip, subvend, subdev, rate) \
+    { PCI_VENDOR_ID_BROOKTREE, \
+      PCI_DEVICE_ID_BROOKTREE_##chip, \
+      subvend, subdev, 0,0,\
+      rate }
+
+/* driver_data is the default digital_rate value for that device */
+static struct pci_device_id snd_bt87x_ids[] = {
+    BT_DEVICE(878, 0x0070, 0x13eb, 32000), /* Hauppauge WinTV series */
+    BT_DEVICE(879, 0x0070, 0x13eb, 32000), /* Hauppauge WinTV series */
+    BT_DEVICE(878, 0x0070, 0xff01, 44100), /* Viewcast Osprey 200 */
+    /* Leadtek Winfast tv 2000xp delux */
+    BT_DEVICE(878, 0x107d, 0x6606, 32000),
+    /* Voodoo TV 200 */
+    BT_DEVICE(878, 0x121a, 0x3000, 32000),
+    /* AVerMedia Studio No. 103, 203, ...? */
+    BT_DEVICE(878, 0x1461, 0x0003, 48000),
+    {0}
+};
+MODULE_DEVICE_TABLE(pci, snd_bt87x_ids);
+
+/* cards known not to have audio
+ * (DVB cards use the audio function to transfer MPEG data) */
+static struct {
+    unsigned short subvendor, subdevice;
+} blacklist[] __devinitdata = {
+    {0x0071, 0x0101}, /* Nebula Electronics DigiTV */
+    {0x11bd, 0x001c}, /* Pinnacle PCTV Sat */
+    {0x11bd, 0x0026}, /* Pinnacle PCTV SAT CI */
+    {0x1461, 0x0761}, /* AVermedia AverTV DVB-T */
+    {0x1461, 0x0771}, /* AVermedia DVB-T 771 */
+    {0x1822, 0x0001}, /* Twinhan VisionPlus DVB-T */
+    {0x18ac, 0xd500}, /* DVICO FusionHDTV 5 Lite */
+    {0x18ac, 0xdb10}, /* DVICO FusionHDTV DVB-T Lite */
+    {0x270f, 0xfc00}, /* Chaintech Digitop DST-1000 DVB-S */
+    {0x7063, 0x2000}, /* pcHDTV HD-2000 TV */
+};
+
+/* return the rate of the card, or a negative value if it's blacklisted */
+static int __devinit snd_bt87x_detect_card(struct pci_dev *pci)
+{
+    int i;
+    const struct pci_device_id *supported;
+
+    supported = pci_match_device(snd_bt87x_ids, pci);
+    if (supported && supported->driver_data > 0)
+        return supported->driver_data;
+
+    for (i = 0; i < ARRAY_SIZE(blacklist); ++i)
+        if (blacklist[i].subvendor == pci->subsystem_vendor &&
+            blacklist[i].subdevice == pci->subsystem_device) {
+            snd_printdd(KERN_INFO "card %#04x-%#04x:%#04x has no audio\n",
+                        pci->device, pci->subsystem_vendor, pci->subsystem_device);
+            return -EBUSY;
+        }
+
+    snd_printk(KERN_INFO "unknown card %#04x-%#04x:%#04x, using default rate 32000\n",
+               pci->device, pci->subsystem_vendor, pci->subsystem_device);
+    snd_printk(KERN_DEBUG "please mail id, board name, and, "
+               "if it works, the correct digital_rate option to "
+               "<alsa-devel@lists.sf.net>\n");
+    return 32000; /* default rate */
+}
+
+static int __devinit snd_bt87x_probe(struct pci_dev *pci,
+                                     const struct pci_device_id *pci_id)
+{
+    static int dev;
+    snd_card_t *card;
+    bt87x_t *chip;
+    int err, rate;
+
+    rate = pci_id->driver_data;
+    if (! rate)
+        if ((rate = snd_bt87x_detect_card(pci)) <= 0)
+            return -ENODEV;
+
+    if (dev >= SNDRV_CARDS)
+        return -ENODEV;
+    if (!enable[dev]) {
+        ++dev;
+        return -ENOENT;
+    }
+
+    card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+    if (!card)
+        return -ENOMEM;
+
+    err = snd_bt87x_create(card, pci, &chip);
+    if (err < 0)
+        goto _error;
+
+    if (digital_rate[dev] > 0)
+        chip->dig_rate = digital_rate[dev];
+    else
+        chip->dig_rate = rate;
+
+    err = snd_bt87x_pcm(chip, DEVICE_DIGITAL, "Bt87x Digital");
+    if (err < 0)
+        goto _error;
+    err = snd_bt87x_pcm(chip, DEVICE_ANALOG, "Bt87x Analog");
+    if (err < 0)
+        goto _error;
+
+    err = snd_ctl_add(card, snd_ctl_new1(&snd_bt87x_capture_volume, chip));
+    if (err < 0)
+        goto _error;
+    err = snd_ctl_add(card, snd_ctl_new1(&snd_bt87x_capture_boost, chip));
+    if (err < 0)
+        goto _error;
+    err = snd_ctl_add(card, snd_ctl_new1(&snd_bt87x_capture_source, chip));
+    if (err < 0)
+        goto _error;
+
+    strcpy(card->driver, "Bt87x");
+    sprintf(card->shortname, "Brooktree Bt%x", pci->device);
+    sprintf(card->longname, "%s at %#lx, irq %i",
+            card->shortname, pci_resource_start(pci, 0), chip->irq);
+    strcpy(card->mixername, "Bt87x");
+
+    err = snd_card_register(card);
+    if (err < 0)
+        goto _error;
+
+    pci_set_drvdata(pci, card);
+    ++dev;
+    return 0;
+
+_error:
+    snd_card_free(card);
+    return err;
+}
+
+static void __devexit snd_bt87x_remove(struct pci_dev *pci)
+{
+    snd_card_free(pci_get_drvdata(pci));
+    pci_set_drvdata(pci, NULL);
+}
+
+/* default entries for all Bt87x cards - it's not exported */
+/* driver_data is set to 0 to call detection */
+static struct pci_device_id snd_bt87x_default_ids[] = {
+    BT_DEVICE(878, PCI_ANY_ID, PCI_ANY_ID, 0),
+    BT_DEVICE(879, PCI_ANY_ID, PCI_ANY_ID, 0),
+    {0}
+};
+
+static struct pci_driver driver = {
+    0,0,0,
+    /*.name =*/ "Bt87x",
+    /*.id_table = */snd_bt87x_ids,
+    /*.probe = */snd_bt87x_probe,
+    /*.remove = */snd_bt87x_remove,
+    0,0
+};
+
+static int __init alsa_card_bt87x_init(void)
+{
+    if (load_all)
+        driver.id_table = snd_bt87x_default_ids;
+    return pci_module_init(&driver);
+}
+
+static void __exit alsa_card_bt87x_exit(void)
+{
+    pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_bt87x_init)
+module_exit(alsa_card_bt87x_exit)
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/ca0106/CVS/Entries
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/ca0106/CVS/Entries	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/ca0106/CVS/Entries	(revision 91)
@@ -0,0 +1,6 @@
+/Makefile/1.1/Mon Dec 27 15:32:38 2004/-ko/
+/ca0106.h/1.1/Mon Dec 27 15:32:38 2004/-ko/
+/ca0106_main.c/1.1/Mon Dec 27 15:32:38 2004/-ko/
+/ca0106_mixer.c/1.1/Mon Dec 27 15:32:38 2004/-ko/
+/ca0106_proc.c/1.1/Mon Dec 27 15:32:38 2004/-ko/
+D
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/ca0106/CVS/Repository
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/ca0106/CVS/Repository	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/ca0106/CVS/Repository	(revision 91)
@@ -0,0 +1,1 @@
+alsa-kernel/pci/ca0106
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/ca0106/CVS/Root
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/ca0106/CVS/Root	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/ca0106/CVS/Root	(revision 91)
@@ -0,0 +1,1 @@
+:pserver:anonymous@cvs.alsa-project.org:/cvsroot/alsa
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/ca0106/Makefile
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/ca0106/Makefile	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/ca0106/Makefile	(revision 91)
@@ -0,0 +1,3 @@
+snd-ca0106-objs := ca0106_main.o ca0106_proc.o ca0106_mixer.o
+
+obj-$(CONFIG_SND_CA0106) += snd-ca0106.o
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/ca0106/ca0106.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/ca0106/ca0106.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/ca0106/ca0106.h	(revision 91)
@@ -0,0 +1,629 @@
+/*
+ *  Copyright (c) 2004 James Courtier-Dutton <James@superbug.demon.co.uk>
+ *  Driver CA0106 chips. e.g. Sound Blaster Audigy LS and Live 24bit
+ *  Version: 0.0.21
+ *
+ *  FEATURES currently supported:
+ *    See ca0106_main.c for features.
+ * 
+ *  Changelog:
+ *    Support interrupts per period.
+ *    Removed noise from Center/LFE channel when in Analog mode.
+ *    Rename and remove mixer controls.
+ *  0.0.6
+ *    Use separate card based DMA buffer for periods table list.
+ *  0.0.7
+ *    Change remove and rename ctrls into lists.
+ *  0.0.8
+ *    Try to fix capture sources.
+ *  0.0.9
+ *    Fix AC3 output.
+ *    Enable S32_LE format support.
+ *  0.0.10
+ *    Enable playback 48000 and 96000 rates. (Rates other that these do not work, even with "plug:front".)
+ *  0.0.11
+ *    Add Model name recognition.
+ *  0.0.12
+ *    Correct interrupt timing. interrupt at end of period, instead of in the middle of a playback period.
+ *    Remove redundent "voice" handling.
+ *  0.0.13
+ *    Single trigger call for multi channels.
+ *  0.0.14
+ *    Set limits based on what the sound card hardware can do.
+ *    playback periods_min=2, periods_max=8
+ *    capture hw constraints require period_size = n * 64 bytes.
+ *    playback hw constraints require period_size = n * 64 bytes.
+ *  0.0.15
+ *    Separated ca0106.c into separate functional .c files.
+ *  0.0.16
+ *    Implement 192000 sample rate.
+ *  0.0.17
+ *    Add support for SB0410 and SB0413.
+ *  0.0.18
+ *    Modified Copyright message.
+ *  0.0.19
+ *    Added I2C and SPI registers. Filled in interrupt enable.
+ *  0.0.20
+ *    Added GPIO info for SB Live 24bit.
+ *  0.0.21
+ *   Implement support for Line-in capture on SB Live 24bit.
+ *
+ *
+ *  This code was initally based on code from ALSA's emu10k1x.c which is:
+ *  Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+/************************************************************************************************/
+/* PCI function 0 registers, address = <val> + PCIBASE0						*/
+/************************************************************************************************/
+
+#define PTR			0x00		/* Indexed register set pointer register	*/
+						/* NOTE: The CHANNELNUM and ADDRESS words can	*/
+						/* be modified independently of each other.	*/
+						/* CNL[1:0], ADDR[27:16]                        */
+
+#define DATA			0x04		/* Indexed register set data register		*/
+						/* DATA[31:0]					*/
+
+#define IPR			0x08		/* Global interrupt pending register		*/
+						/* Clear pending interrupts by writing a 1 to	*/
+						/* the relevant bits and zero to the other bits	*/
+#define IPR_MIDI_RX_B		0x00020000	/* MIDI UART-B Receive buffer non-empty		*/
+#define IPR_MIDI_TX_B		0x00010000	/* MIDI UART-B Transmit buffer empty		*/
+#define IPR_SPDIF_IN_USER	0x00004000      /* SPDIF input user data has 16 more bits	*/
+#define IPR_SPDIF_OUT_USER	0x00002000      /* SPDIF output user data needs 16 more bits	*/
+#define IPR_SPDIF_OUT_FRAME	0x00001000      /* SPDIF frame about to start			*/
+#define IPR_SPI			0x00000800      /* SPI transaction completed			*/
+#define IPR_I2C_EEPROM		0x00000400      /* I2C EEPROM transaction completed		*/
+#define IPR_I2C_DAC		0x00000200      /* I2C DAC transaction completed		*/
+#define IPR_AI			0x00000100      /* Audio pending register changed. See PTR reg 0x76	*/
+#define IPR_GPI			0x00000080      /* General Purpose input changed		*/
+#define IPR_SRC_LOCKED          0x00000040      /* SRC lock status changed			*/
+#define IPR_SPDIF_STATUS        0x00000020      /* SPDIF status changed				*/
+#define IPR_TIMER2              0x00000010      /* 192000Hz Timer				*/
+#define IPR_TIMER1              0x00000008      /* 44100Hz Timer				*/
+#define IPR_MIDI_RX_A		0x00000004	/* MIDI UART-A Receive buffer non-empty		*/
+#define IPR_MIDI_TX_A		0x00000002	/* MIDI UART-A Transmit buffer empty		*/
+#define IPR_PCI			0x00000001	/* PCI Bus error				*/
+
+#define INTE			0x0c		/* Interrupt enable register			*/
+
+#define INTE_MIDI_RX_B		0x00020000	/* MIDI UART-B Receive buffer non-empty		*/
+#define INTE_MIDI_TX_B		0x00010000	/* MIDI UART-B Transmit buffer empty		*/
+#define INTE_SPDIF_IN_USER	0x00004000      /* SPDIF input user data has 16 more bits	*/
+#define INTE_SPDIF_OUT_USER	0x00002000      /* SPDIF output user data needs 16 more bits	*/
+#define INTE_SPDIF_OUT_FRAME	0x00001000      /* SPDIF frame about to start			*/
+#define INTE_SPI		0x00000800      /* SPI transaction completed			*/
+#define INTE_I2C_EEPROM		0x00000400      /* I2C EEPROM transaction completed		*/
+#define INTE_I2C_DAC		0x00000200      /* I2C DAC transaction completed		*/
+#define INTE_AI			0x00000100      /* Audio pending register changed. See PTR reg 0x75 */
+#define INTE_GPI		0x00000080      /* General Purpose input changed		*/
+#define INTE_SRC_LOCKED         0x00000040      /* SRC lock status changed			*/
+#define INTE_SPDIF_STATUS       0x00000020      /* SPDIF status changed				*/
+#define INTE_TIMER2             0x00000010      /* 192000Hz Timer				*/
+#define INTE_TIMER1             0x00000008      /* 44100Hz Timer				*/
+#define INTE_MIDI_RX_A		0x00000004	/* MIDI UART-A Receive buffer non-empty		*/
+#define INTE_MIDI_TX_A		0x00000002	/* MIDI UART-A Transmit buffer empty		*/
+#define INTE_PCI		0x00000001	/* PCI Bus error				*/
+
+#define UNKNOWN10		0x10		/* Unknown ??. Defaults to 0 */
+#define HCFG			0x14		/* Hardware config register			*/
+						/* 0x1000 causes AC3 to fails. It adds a dither bit. */
+
+#define HCFG_STAC		0x10000000	/* Special mode for STAC9460 Codec. */
+#define HCFG_CAPTURE_I2S_BYPASS	0x08000000	/* 1 = bypass I2S input async SRC. */
+#define HCFG_CAPTURE_SPDIF_BYPASS 0x04000000	/* 1 = bypass SPDIF input async SRC. */
+#define HCFG_PLAYBACK_I2S_BYPASS 0x02000000	/* 0 = I2S IN mixer output, 1 = I2S IN1. */
+#define HCFG_FORCE_LOCK		0x01000000	/* For test only. Force input SRC tracker to lock. */
+#define HCFG_PLAYBACK_ATTENUATION 0x00006000	/* Playback attenuation mask. 0 = 0dB, 1 = 6dB, 2 = 12dB, 3 = Mute. */
+#define HCFG_PLAYBACK_DITHER	0x00001000	/* 1 = Add dither bit to all playback channels. */
+#define HCFG_PLAYBACK_S32_LE	0x00000800	/* 1 = S32_LE, 0 = S16_LE                       */
+#define HCFG_CAPTURE_S32_LE	0x00000400	/* 1 = S32_LE, 0 = S16_LE (S32_LE current not working)	*/
+#define HCFG_8_CHANNEL_PLAY	0x00000200	/* 1 = 8 channels, 0 = 2 channels per substream.*/
+#define HCFG_8_CHANNEL_CAPTURE	0x00000100	/* 1 = 8 channels, 0 = 2 channels per substream.*/
+#define HCFG_MONO		0x00000080	/* 1 = I2S Input mono                           */
+#define HCFG_I2S_OUTPUT		0x00000010	/* 1 = I2S Output disabled                      */
+#define HCFG_AC97		0x00000008	/* 0 = AC97 1.0, 1 = AC97 2.0                   */
+#define HCFG_LOCK_PLAYBACK_CACHE 0x00000004	/* 1 = Cancel bustmaster accesses to soundcache */
+						/* NOTE: This should generally never be used.  	*/
+#define HCFG_LOCK_CAPTURE_CACHE	0x00000002	/* 1 = Cancel bustmaster accesses to soundcache */
+						/* NOTE: This should generally never be used.  	*/
+#define HCFG_AUDIOENABLE	0x00000001	/* 0 = CODECs transmit zero-valued samples	*/
+						/* Should be set to 1 when the EMU10K1 is	*/
+						/* completely initialized.			*/
+#define GPIO			0x18		/* Defaults: 005f03a3-Analog, 005f02a2-SPDIF.   */
+						/* Here pins 0,1,2,3,4,,6 are output. 5,7 are input */
+						/* For the Audigy LS, pin 0 (or bit 8) controls the SPDIF/Analog jack. */
+						/* SB Live 24bit:
+						 * bit 8 0 = SPDIF in and out / 1 = Analog (Mic or Line)-in.
+						 * bit 9 0 = Mute / 1 = Analog out.
+						 * bit 10 0 = Line-in / 1 = Mic-in.
+						 * bit 11 0 = ? / 1 = ?
+						 * bit 12 0 = 48 Khz / 1 = 96 Khz Analog out on SB Live 24bit.
+						 * bit 13 0 = ? / 1 = ?
+						 * bit 14 0 = Mute / 1 = Analog out
+						 * bit 15 0 = ? / 1 = ?
+						 * Both bit 9 and bit 14 have to be set for analog sound to work on the SB Live 24bit.
+						 */
+						/* 8 general purpose programmable In/Out pins.
+						 * GPI [8:0] Read only. Default 0.
+						 * GPO [15:8] Default 0x9. (Default to SPDIF jack enabled for SPDIF)
+						 * GPO Enable [23:16] Default 0x0f. Setting a bit to 1, causes the pin to be an output pin.
+						 */
+#define AC97DATA		0x1c		/* AC97 register set data register (16 bit)	*/
+
+#define AC97ADDRESS		0x1e		/* AC97 register set address register (8 bit)	*/
+
+/********************************************************************************************************/
+/* CA0106 pointer-offset register set, accessed through the PTR and DATA registers                     */
+/********************************************************************************************************/
+                                                                                                                           
+/* Initally all registers from 0x00 to 0x3f have zero contents. */
+#define PLAYBACK_LIST_ADDR	0x00		/* Base DMA address of a list of pointers to each period/size */
+						/* One list entry: 4 bytes for DMA address, 
+						 * 4 bytes for period_size << 16.
+						 * One list entry is 8 bytes long.
+						 * One list entry for each period in the buffer.
+						 */
+						/* ADDR[31:0], Default: 0x0 */
+#define PLAYBACK_LIST_SIZE	0x01		/* Size of list in bytes << 16. E.g. 8 periods -> 0x00380000  */
+						/* SIZE[21:16], Default: 0x8 */
+#define PLAYBACK_LIST_PTR	0x02		/* Pointer to the current period being played */
+						/* PTR[5:0], Default: 0x0 */
+#define PLAYBACK_UNKNOWN3	0x03		/* Not used ?? */
+#define PLAYBACK_DMA_ADDR	0x04		/* Playback DMA addresss */
+						/* DMA[31:0], Default: 0x0 */
+#define PLAYBACK_PERIOD_SIZE	0x05		/* Playback period size. win2000 uses 0x04000000 */
+						/* SIZE[31:16], Default: 0x0 */
+#define PLAYBACK_POINTER	0x06		/* Playback period pointer. Used with PLAYBACK_LIST_PTR to determine buffer position currently in DAC */
+						/* POINTER[15:0], Default: 0x0 */
+#define PLAYBACK_PERIOD_END_ADDR 0x07		/* Playback fifo end address */
+						/* END_ADDR[15:0], FLAG[16] 0 = don't stop, 1 = stop */
+#define PLAYBACK_FIFO_OFFSET_ADDRESS	0x08	/* Current fifo offset address [21:16] */
+						/* Cache size valid [5:0] */
+#define PLAYBACK_UNKNOWN9	0x09		/* 0x9 to 0xf Unused */
+#define CAPTURE_DMA_ADDR	0x10		/* Capture DMA address */
+						/* DMA[31:0], Default: 0x0 */
+#define CAPTURE_BUFFER_SIZE	0x11		/* Capture buffer size */
+						/* SIZE[31:16], Default: 0x0 */
+#define CAPTURE_POINTER		0x12		/* Capture buffer pointer. Sample currently in ADC */
+						/* POINTER[15:0], Default: 0x0 */
+#define CAPTURE_FIFO_OFFSET_ADDRESS	0x13	/* Current fifo offset address [21:16] */
+						/* Cache size valid [5:0] */
+#define PLAYBACK_LAST_SAMPLE    0x20		/* The sample currently being played */
+/* 0x21 - 0x3f unused */
+#define BASIC_INTERRUPT         0x40		/* Used by both playback and capture interrupt handler */
+						/* Playback (0x1<<channel_id) */
+						/* Capture  (0x100<<channel_id) */
+						/* Playback sample rate 96000 = 0x20000 */
+						/* Start Playback [3:0] (one bit per channel)
+						 * Start Capture [11:8] (one bit per channel)
+						 * Playback rate [23:16] (2 bits per channel) (0=48kHz, 1=44.1kHz, 2=96kHz, 3=192Khz)
+						 * Playback mixer in enable [27:24] (one bit per channel)
+						 * Playback mixer out enable [31:28] (one bit per channel)
+						 */
+/* The Digital out jack is shared with the Center/LFE Analogue output. 
+ * The jack has 4 poles. I will call 1 - Tip, 2 - Next to 1, 3 - Next to 2, 4 - Next to 3
+ * For Analogue: 1 -> Center Speaker, 2 -> Sub Woofer, 3 -> Ground, 4 -> Ground
+ * For Digital: 1 -> Front SPDIF, 2 -> Rear SPDIF, 3 -> Center/Subwoofer SPDIF, 4 -> Ground.
+ * Standard 4 pole Video A/V cable with RCA outputs: 1 -> White, 2 -> Yellow, 3 -> Sheild on all three, 4 -> Red.
+ * So, from this you can see that you cannot use a Standard 4 pole Video A/V cable with the SB Audigy LS card.
+ */
+/* The Front SPDIF PCM gets mixed with samples from the AC97 codec, so can only work for Stereo PCM and not AC3/DTS
+ * The Rear SPDIF can be used for Stereo PCM and also AC3/DTS
+ * The Center/LFE SPDIF cannot be used for AC3/DTS, but can be used for Stereo PCM.
+ * Summary: For ALSA we use the Rear channel for SPDIF Digital AC3/DTS output
+ */
+/* A standard 2 pole mono mini-jack to RCA plug can be used for SPDIF Stereo PCM output from the Front channel.
+ * A standard 3 pole stereo mini-jack to 2 RCA plugs can be used for SPDIF AC3/DTS and Stereo PCM output utilising the Rear channel and just one of the RCA plugs. 
+ */
+#define SPCS0			0x41		/* SPDIF output Channel Status 0 register. For Rear. default=0x02108004, non-audio=0x02108006	*/
+#define SPCS1			0x42		/* SPDIF output Channel Status 1 register. For Front */
+#define SPCS2			0x43		/* SPDIF output Channel Status 2 register. For Center/LFE */
+#define SPCS3			0x44		/* SPDIF output Channel Status 3 register. Unknown */
+						/* When Channel set to 0: */
+#define SPCS_CLKACCYMASK	0x30000000	/* Clock accuracy				*/
+#define SPCS_CLKACCY_1000PPM	0x00000000	/* 1000 parts per million			*/
+#define SPCS_CLKACCY_50PPM	0x10000000	/* 50 parts per million				*/
+#define SPCS_CLKACCY_VARIABLE	0x20000000	/* Variable accuracy				*/
+#define SPCS_SAMPLERATEMASK	0x0f000000	/* Sample rate					*/
+#define SPCS_SAMPLERATE_44	0x00000000	/* 44.1kHz sample rate				*/
+#define SPCS_SAMPLERATE_48	0x02000000	/* 48kHz sample rate				*/
+#define SPCS_SAMPLERATE_32	0x03000000	/* 32kHz sample rate				*/
+#define SPCS_CHANNELNUMMASK	0x00f00000	/* Channel number				*/
+#define SPCS_CHANNELNUM_UNSPEC	0x00000000	/* Unspecified channel number			*/
+#define SPCS_CHANNELNUM_LEFT	0x00100000	/* Left channel					*/
+#define SPCS_CHANNELNUM_RIGHT	0x00200000	/* Right channel				*/
+#define SPCS_SOURCENUMMASK	0x000f0000	/* Source number				*/
+#define SPCS_SOURCENUM_UNSPEC	0x00000000	/* Unspecified source number			*/
+#define SPCS_GENERATIONSTATUS	0x00008000	/* Originality flag (see IEC-958 spec)		*/
+#define SPCS_CATEGORYCODEMASK	0x00007f00	/* Category code (see IEC-958 spec)		*/
+#define SPCS_MODEMASK		0x000000c0	/* Mode (see IEC-958 spec)			*/
+#define SPCS_EMPHASISMASK	0x00000038	/* Emphasis					*/
+#define SPCS_EMPHASIS_NONE	0x00000000	/* No emphasis					*/
+#define SPCS_EMPHASIS_50_15	0x00000008	/* 50/15 usec 2 channel				*/
+#define SPCS_COPYRIGHT		0x00000004	/* Copyright asserted flag -- do not modify	*/
+#define SPCS_NOTAUDIODATA	0x00000002	/* 0 = Digital audio, 1 = not audio		*/
+#define SPCS_PROFESSIONAL	0x00000001	/* 0 = Consumer (IEC-958), 1 = pro (AES3-1992)	*/
+
+						/* When Channel set to 1: */
+#define SPCS_WORD_LENGTH_MASK	0x0000000f	/* Word Length Mask				*/
+#define SPCS_WORD_LENGTH_16	0x00000008	/* Word Length 16 bit				*/
+#define SPCS_WORD_LENGTH_17	0x00000006	/* Word Length 17 bit				*/
+#define SPCS_WORD_LENGTH_18	0x00000004	/* Word Length 18 bit				*/
+#define SPCS_WORD_LENGTH_19	0x00000002	/* Word Length 19 bit				*/
+#define SPCS_WORD_LENGTH_20A	0x0000000a	/* Word Length 20 bit				*/
+#define SPCS_WORD_LENGTH_20	0x00000009	/* Word Length 20 bit (both 0xa and 0x9 are 20 bit) */
+#define SPCS_WORD_LENGTH_21	0x00000007	/* Word Length 21 bit				*/
+#define SPCS_WORD_LENGTH_21	0x00000007	/* Word Length 21 bit				*/
+#define SPCS_WORD_LENGTH_22	0x00000005	/* Word Length 22 bit				*/
+#define SPCS_WORD_LENGTH_23	0x00000003	/* Word Length 23 bit				*/
+#define SPCS_WORD_LENGTH_24	0x0000000b	/* Word Length 24 bit				*/
+#define SPCS_ORIGINAL_SAMPLE_RATE_MASK	0x000000f0 /* Original Sample rate			*/
+#define SPCS_ORIGINAL_SAMPLE_RATE_NONE	0x00000000 /* Original Sample rate not indicated	*/
+#define SPCS_ORIGINAL_SAMPLE_RATE_16000	0x00000010 /* Original Sample rate	*/
+#define SPCS_ORIGINAL_SAMPLE_RATE_RES1	0x00000020 /* Original Sample rate	*/
+#define SPCS_ORIGINAL_SAMPLE_RATE_32000	0x00000030 /* Original Sample rate	*/
+#define SPCS_ORIGINAL_SAMPLE_RATE_12000	0x00000040 /* Original Sample rate	*/
+#define SPCS_ORIGINAL_SAMPLE_RATE_11025	0x00000050 /* Original Sample rate	*/
+#define SPCS_ORIGINAL_SAMPLE_RATE_8000	0x00000060 /* Original Sample rate	*/
+#define SPCS_ORIGINAL_SAMPLE_RATE_RES2	0x00000070 /* Original Sample rate	*/
+#define SPCS_ORIGINAL_SAMPLE_RATE_192000 0x00000080 /* Original Sample rate	*/
+#define SPCS_ORIGINAL_SAMPLE_RATE_24000	0x00000090 /* Original Sample rate	*/
+#define SPCS_ORIGINAL_SAMPLE_RATE_96000	0x000000a0 /* Original Sample rate	*/
+#define SPCS_ORIGINAL_SAMPLE_RATE_48000	0x000000b0 /* Original Sample rate	*/
+#define SPCS_ORIGINAL_SAMPLE_RATE_176400 0x000000c0 /* Original Sample rate	*/
+#define SPCS_ORIGINAL_SAMPLE_RATE_22050	0x000000d0 /* Original Sample rate	*/
+#define SPCS_ORIGINAL_SAMPLE_RATE_88200	0x000000e0 /* Original Sample rate	*/
+#define SPCS_ORIGINAL_SAMPLE_RATE_44100	0x000000f0 /* Original Sample rate	*/
+
+#define SPDIF_SELECT1		0x45		/* Enables SPDIF or Analogue outputs 0-SPDIF, 0xf00-Analogue */
+						/* 0x100 - Front, 0x800 - Rear, 0x200 - Center/LFE.
+						 * But as the jack is shared, use 0xf00.
+						 * The Windows2000 driver uses 0x0000000f for both digital and analog.
+						 * 0xf00 introduces interesting noises onto the Center/LFE.
+						 * If you turn the volume up, you hear computer noise,
+						 * e.g. mouse moving, changing between app windows etc.
+						 * So, I am going to set this to 0x0000000f all the time now,
+						 * same as the windows driver does.
+						 * Use register SPDIF_SELECT2(0x72) to switch between SPDIF and Analog.
+						 */
+						/* When Channel = 0:
+						 * Wide SPDIF format [3:0] (one bit for each channel) (0=20bit, 1=24bit)
+						 * Tristate SPDIF Output [11:8] (one bit for each channel) (0=Not tristate, 1=Tristate)
+						 * SPDIF Bypass enable [19:16] (one bit for each channel) (0=Not bypass, 1=Bypass)
+						 */
+						/* When Channel = 1:
+						 * SPDIF 0 User data [7:0]
+						 * SPDIF 1 User data [15:8]
+						 * SPDIF 0 User data [23:16]
+						 * SPDIF 0 User data [31:24]
+						 * User data can be sent by using the SPDIF output frame pending and SPDIF output user bit interrupts.
+						 */
+#define WATERMARK		0x46		/* Test bit to indicate cache usage level */
+#define SPDIF_INPUT_STATUS	0x49		/* SPDIF Input status register. Bits the same as SPCS.
+						 * When Channel = 0: Bits the same as SPCS channel 0.
+						 * When Channel = 1: Bits the same as SPCS channel 1.
+						 * When Channel = 2:
+						 * SPDIF Input User data [16:0]
+						 * SPDIF Input Frame count [21:16]
+						 */
+#define CAPTURE_CACHE_DATA	0x50		/* 0x50-0x5f Recorded samples. */
+#define CAPTURE_SOURCE          0x60            /* Capture Source 0 = MIC */
+#define CAPTURE_SOURCE_CHANNEL0 0xf0000000	/* Mask for selecting the Capture sources */
+#define CAPTURE_SOURCE_CHANNEL1 0x0f000000	/* 0 - SPDIF mixer output. */
+#define CAPTURE_SOURCE_CHANNEL2 0x00f00000      /* 1 - What you hear or . 2 - ?? */
+#define CAPTURE_SOURCE_CHANNEL3 0x000f0000	/* 3 - Mic in, Line in, TAD in, Aux in. */
+#define CAPTURE_SOURCE_RECORD_MAP 0x0000ffff	/* Default 0x00e4 */
+						/* Record Map [7:0] (2 bits per channel) 0=mapped to channel 0, 1=mapped to channel 1, 2=mapped to channel2, 3=mapped to channel3 
+						 * Record source select for channel 0 [18:16]
+						 * Record source select for channel 1 [22:20]
+						 * Record source select for channel 2 [26:24]
+						 * Record source select for channel 3 [30:28]
+						 * 0 - SPDIF mixer output.
+						 * 1 - i2s mixer output.
+						 * 2 - SPDIF input.
+						 * 3 - i2s input.
+						 * 4 - AC97 capture.
+						 * 5 - SRC output.
+						 */
+#define CAPTURE_VOLUME1         0x61            /* Capture  volume per channel 0-3 */
+#define CAPTURE_VOLUME2         0x62            /* Capture  volume per channel 4-7 */
+
+#define PLAYBACK_ROUTING1       0x63            /* Playback routing of channels 0-7. Effects AC3 output. Default 0x32765410 */
+#define ROUTING1_REAR           0x77000000      /* Channel_id 0 sends to 10, Channel_id 1 sends to 32 */
+#define ROUTING1_NULL           0x00770000      /* Channel_id 2 sends to 54, Channel_id 3 sends to 76 */
+#define ROUTING1_CENTER_LFE     0x00007700      /* 0x32765410 means, send Channel_id 0 to FRONT, Channel_id 1 to REAR */
+#define ROUTING1_FRONT          0x00000077	/* Channel_id 2 to CENTER_LFE, Channel_id 3 to NULL. */
+						/* Channel_id's handle stereo channels. Channel X is a single mono channel */
+						/* Host is input from the PCI bus. */
+						/* Host channel 0 [2:0] -> SPDIF Mixer/Router channel 0-7.
+						 * Host channel 1 [6:4] -> SPDIF Mixer/Router channel 0-7.
+						 * Host channel 2 [10:8] -> SPDIF Mixer/Router channel 0-7.
+						 * Host channel 3 [14:12] -> SPDIF Mixer/Router channel 0-7.
+						 * Host channel 4 [18:16] -> SPDIF Mixer/Router channel 0-7.
+						 * Host channel 5 [22:20] -> SPDIF Mixer/Router channel 0-7.
+						 * Host channel 6 [26:24] -> SPDIF Mixer/Router channel 0-7.
+						 * Host channel 7 [30:28] -> SPDIF Mixer/Router channel 0-7.
+						 */
+
+#define PLAYBACK_ROUTING2       0x64            /* Playback Routing . Feeding Capture channels back into Playback. Effects AC3 output. Default 0x76767676 */
+						/* SRC is input from the capture inputs. */
+						/* SRC channel 0 [2:0] -> SPDIF Mixer/Router channel 0-7.
+						 * SRC channel 1 [6:4] -> SPDIF Mixer/Router channel 0-7.
+						 * SRC channel 2 [10:8] -> SPDIF Mixer/Router channel 0-7.
+						 * SRC channel 3 [14:12] -> SPDIF Mixer/Router channel 0-7.
+						 * SRC channel 4 [18:16] -> SPDIF Mixer/Router channel 0-7.
+						 * SRC channel 5 [22:20] -> SPDIF Mixer/Router channel 0-7.
+						 * SRC channel 6 [26:24] -> SPDIF Mixer/Router channel 0-7.
+						 * SRC channel 7 [30:28] -> SPDIF Mixer/Router channel 0-7.
+						 */
+
+#define PLAYBACK_MUTE           0x65            /* Unknown. While playing 0x0, while silent 0x00fc0000 */
+						/* SPDIF Mixer input control:
+						 * Invert SRC to SPDIF Mixer [7-0] (One bit per channel)
+						 * Invert Host to SPDIF Mixer [15:8] (One bit per channel)
+						 * SRC to SPDIF Mixer disable [23:16] (One bit per channel)
+						 * Host to SPDIF Mixer disable [31:24] (One bit per channel)
+						 */
+#define PLAYBACK_VOLUME1        0x66            /* Playback SPDIF volume per channel. Set to the same PLAYBACK_VOLUME(0x6a) */
+						/* PLAYBACK_VOLUME1 must be set to 30303030 for SPDIF AC3 Playback */
+						/* SPDIF mixer input volume. 0=12dB, 0x30=0dB, 0xFE=-51.5dB, 0xff=Mute */
+						/* One register for each of the 4 stereo streams. */
+						/* SRC Right volume [7:0]
+						 * SRC Left  volume [15:8]
+						 * Host Right volume [23:16]
+						 * Host Left  volume [31:24]
+						 */
+#define CAPTURE_ROUTING1        0x67            /* Capture Routing. Default 0x32765410 */
+						/* Similar to register 0x63, except that the destination is the I2S mixer instead of the SPDIF mixer. I.E. Outputs to the Analog outputs instead of SPDIF. */
+#define CAPTURE_ROUTING2        0x68            /* Unknown Routing. Default 0x76767676 */
+						/* Similar to register 0x64, except that the destination is the I2S mixer instead of the SPDIF mixer. I.E. Outputs to the Analog outputs instead of SPDIF. */
+#define CAPTURE_MUTE            0x69            /* Unknown. While capturing 0x0, while silent 0x00fc0000 */
+						/* Similar to register 0x65, except that the destination is the I2S mixer instead of the SPDIF mixer. I.E. Outputs to the Analog outputs instead of SPDIF. */
+#define PLAYBACK_VOLUME2        0x6a            /* Playback Analog volume per channel. Does not effect AC3 output */
+						/* Similar to register 0x66, except that the destination is the I2S mixer instead of the SPDIF mixer. I.E. Outputs to the Analog outputs instead of SPDIF. */
+#define UNKNOWN6b               0x6b            /* Unknown. Readonly. Default 00400000 00400000 00400000 00400000 */
+#define MIDI_UART_A_DATA		0x6c            /* Midi Uart A Data */
+#define MIDI_UART_A_CMD		0x6d            /* Midi Uart A Command/Status */
+#define MIDI_UART_B_DATA		0x6e            /* Midi Uart B Data (currently unused) */
+#define MIDI_UART_B_CMD		0x6f            /* Midi Uart B Command/Status (currently unused) */
+
+/* unique channel identifier for midi->channel */
+
+#define CA0106_MIDI_CHAN_A		0x1
+#define CA0106_MIDI_CHAN_B		0x2
+
+/* from mpu401 */
+
+#define CA0106_MIDI_INPUT_AVAIL 	0x80
+#define CA0106_MIDI_OUTPUT_READY	0x40
+#define CA0106_MPU401_RESET		0xff
+#define CA0106_MPU401_ENTER_UART	0x3f
+#define CA0106_MPU401_ACK		0xfe
+
+#define SAMPLE_RATE_TRACKER_STATUS 0x70         /* Readonly. Default 00108000 00108000 00500000 00500000 */
+						/* Estimated sample rate [19:0] Relative to 48kHz. 0x8000 =  1.0
+						 * Rate Locked [20]
+						 * SPDIF Locked [21] For SPDIF channel only.
+						 * Valid Audio [22] For SPDIF channel only.
+						 */
+#define CAPTURE_CONTROL         0x71            /* Some sort of routing. default = 40c81000 30303030 30300000 00700000 */
+						/* Channel_id 0: 0x40c81000 must be changed to 0x40c80000 for SPDIF AC3 input or output. */
+						/* Channel_id 1: 0xffffffff(mute) 0x30303030(max) controls CAPTURE feedback into PLAYBACK. */
+						/* Sample rate output control register Channel=0
+						 * Sample output rate [1:0] (0=48kHz, 1=44.1kHz, 2=96kHz, 3=192Khz)
+						 * Sample input rate [3:2] (0=48kHz, 1=Not available, 2=96kHz, 3=192Khz)
+						 * SRC input source select [4] 0=Audio from digital mixer, 1=Audio from analog source.
+						 * Record rate [9:8] (0=48kHz, 1=Not available, 2=96kHz, 3=192Khz)
+						 * Record mixer output enable [12:10] 
+						 * I2S input rate master mode [15:14] (0=48kHz, 1=44.1kHz, 2=96kHz, 3=192Khz)
+						 * I2S output rate [17:16] (0=48kHz, 1=44.1kHz, 2=96kHz, 3=192Khz)
+						 * I2S output source select [18] (0=Audio from host, 1=Audio from SRC)
+						 * Record mixer I2S enable [20:19] (enable/disable i2sin1 and i2sin0)
+						 * I2S output master clock select [21] (0=256*I2S output rate, 1=512*I2S output rate.)
+						 * I2S input master clock select [22] (0=256*I2S input rate, 1=512*I2S input rate.)
+						 * I2S input mode [23] (0=Slave, 1=Master)
+						 * SPDIF output rate [25:24] (0=48kHz, 1=44.1kHz, 2=96kHz, 3=192Khz)
+						 * SPDIF output source select [26] (0=host, 1=SRC)
+						 * Not used [27]
+						 * Record Source 0 input [29:28] (0=SPDIF in, 1=I2S in, 2=AC97 Mic, 3=AC97 PCM)
+						 * Record Source 1 input [31:30] (0=SPDIF in, 1=I2S in, 2=AC97 Mic, 3=AC97 PCM)
+						 */ 
+						/* Sample rate output control register Channel=1
+						 * I2S Input 0 volume Right [7:0]
+						 * I2S Input 0 volume Left [15:8]
+						 * I2S Input 1 volume Right [23:16]
+						 * I2S Input 1 volume Left [31:24]
+						 */
+						/* Sample rate output control register Channel=2
+						 * SPDIF Input volume Right [23:16]
+						 * SPDIF Input volume Left [31:24]
+						 */
+						/* Sample rate output control register Channel=3
+						 * No used
+						 */
+#define SPDIF_SELECT2           0x72            /* Some sort of routing. Channel_id 0 only. default = 0x0f0f003f. Analog 0x000b0000, Digital 0x0b000000 */
+#define ROUTING2_FRONT_MASK     0x00010000      /* Enable for Front speakers. */
+#define ROUTING2_CENTER_LFE_MASK 0x00020000     /* Enable for Center/LFE speakers. */
+#define ROUTING2_REAR_MASK      0x00080000      /* Enable for Rear speakers. */
+						/* Audio output control
+						 * AC97 output enable [5:0]
+						 * I2S output enable [19:16]
+						 * SPDIF output enable [27:24]
+						 */ 
+#define UNKNOWN73               0x73            /* Unknown. Readonly. Default 0x0 */
+#define CHIP_VERSION            0x74            /* P17 Chip version. Channel_id 0 only. Default 00000071 */
+#define EXTENDED_INT_MASK       0x75            /* Used by both playback and capture interrupt handler */
+						/* Sets which Interrupts are enabled. */
+						/* 0x00000001 = Half period. Playback.
+						 * 0x00000010 = Full period. Playback.
+						 * 0x00000100 = Half buffer. Playback.
+						 * 0x00001000 = Full buffer. Playback.
+						 * 0x00010000 = Half buffer. Capture.
+						 * 0x00100000 = Full buffer. Capture.
+						 * Capture can only do 2 periods.
+						 * 0x01000000 = End audio. Playback.
+						 * 0x40000000 = Half buffer Playback,Caputre xrun.
+						 * 0x80000000 = Full buffer Playback,Caputre xrun.
+						 */
+#define EXTENDED_INT            0x76            /* Used by both playback and capture interrupt handler */
+						/* Shows which interrupts are active at the moment. */
+						/* Same bit layout as EXTENDED_INT_MASK */
+#define COUNTER77               0x77		/* Counter range 0 to 0x3fffff, 192000 counts per second. */
+#define COUNTER78               0x78		/* Counter range 0 to 0x3fffff, 44100 counts per second. */
+#define EXTENDED_INT_TIMER      0x79            /* Channel_id 0 only. Used by both playback and capture interrupt handler */
+						/* Causes interrupts based on timer intervals. */
+#define SPI			0x7a		/* SPI: Serial Interface Register */
+#define I2C_A			0x7b		/* I2C Address. 32 bit */
+#define I2C_D0			0x7c		/* I2C Data Port 0. 32 bit */
+#define I2C_D1			0x7d		/* I2C Data Port 1. 32 bit */
+//I2C values
+#define I2C_A_ADC_ADD_MASK	0x000000fe	//The address is a 7 bit address
+#define I2C_A_ADC_RW_MASK	0x00000001	//bit mask for R/W
+#define I2C_A_ADC_TRANS_MASK	0x00000010  	//Bit mask for I2c address DAC value
+#define I2C_A_ADC_ABORT_MASK	0x00000020	//Bit mask for I2C transaction abort flag
+#define I2C_A_ADC_LAST_MASK	0x00000040	//Bit mask for Last word transaction
+#define I2C_A_ADC_BYTE_MASK	0x00000080	//Bit mask for Byte Mode
+
+#define I2C_A_ADC_ADD		0x00000034	//This is the Device address for ADC 
+#define I2C_A_ADC_READ		0x00000001	//To perform a read operation
+#define I2C_A_ADC_START		0x00000100	//Start I2C transaction
+#define I2C_A_ADC_ABORT		0x00000200	//I2C transaction abort
+#define I2C_A_ADC_LAST		0x00000400	//I2C last transaction
+#define I2C_A_ADC_BYTE		0x00000800	//I2C one byte mode
+
+#define I2C_D_ADC_REG_MASK	0xfe000000  	//ADC address register 
+#define I2C_D_ADC_DAT_MASK	0x01ff0000  	//ADC data register
+
+#define ADC_TIMEOUT		0x00000007	//ADC Timeout Clock Disable
+#define ADC_IFC_CTRL		0x0000000b	//ADC Interface Control
+#define ADC_MASTER		0x0000000c	//ADC Master Mode Control
+#define ADC_POWER		0x0000000d	//ADC PowerDown Control
+#define ADC_ATTEN_ADCL		0x0000000e	//ADC Attenuation ADCL
+#define ADC_ATTEN_ADCR		0x0000000f	//ADC Attenuation ADCR
+#define ADC_ALC_CTRL1		0x00000010	//ADC ALC Control 1
+#define ADC_ALC_CTRL2		0x00000011	//ADC ALC Control 2
+#define ADC_ALC_CTRL3		0x00000012	//ADC ALC Control 3
+#define ADC_NOISE_CTRL		0x00000013	//ADC Noise Gate Control
+#define ADC_LIMIT_CTRL		0x00000014	//ADC Limiter Control
+#define ADC_MUX			0x00000015  	//ADC Mux offset
+
+#if 0
+/* FIXME: Not tested yet. */
+#define ADC_GAIN_MASK		0x000000ff	//Mask for ADC Gain
+#define ADC_ZERODB		0x000000cf	//Value to set ADC to 0dB
+#define ADC_MUTE_MASK		0x000000c0	//Mask for ADC mute
+#define ADC_MUTE		0x000000c0	//Value to mute ADC
+#define ADC_OSR			0x00000008	//Mask for ADC oversample rate select
+#define ADC_TIMEOUT_DISABLE	0x00000008	//Value and mask to disable Timeout clock
+#define ADC_HPF_DISABLE		0x00000100	//Value and mask to disable High pass filter
+#define ADC_TRANWIN_MASK	0x00000070	//Mask for Length of Transient Window
+#endif
+
+#define ADC_MUX_MASK		0x0000000f	//Mask for ADC Mux
+#define ADC_MUX_MIC		0x00000002	//Value to select Mic at ADC Mux
+#define ADC_MUX_LINEIN		0x00000004	//Value to select LineIn at ADC Mux
+#define ADC_MUX_PHONE		0x00000001	//Value to select TAD at ADC Mux (Not used)
+#define ADC_MUX_AUX		0x00000008	//Value to select Aux at ADC Mux
+
+#define SET_CHANNEL 0  /* Testing channel outputs 0=Front, 1=Center/LFE, 2=Unknown, 3=Rear */
+#define PCM_FRONT_CHANNEL 0
+#define PCM_REAR_CHANNEL 1
+#define PCM_CENTER_LFE_CHANNEL 2
+#define PCM_UNKNOWN_CHANNEL 3
+#define CONTROL_FRONT_CHANNEL 0
+#define CONTROL_REAR_CHANNEL 3
+#define CONTROL_CENTER_LFE_CHANNEL 1
+#define CONTROL_UNKNOWN_CHANNEL 2
+
+#include "ca_midi.h"
+
+struct snd_ca0106;
+
+struct snd_ca0106_channel {
+	struct snd_ca0106 *emu;
+	int number;
+	int use;
+	void (*interrupt)(struct snd_ca0106 *emu, struct snd_ca0106_channel *channel);
+	struct snd_ca0106_pcm *epcm;
+};
+
+struct snd_ca0106_pcm {
+	struct snd_ca0106 *emu;
+	struct snd_pcm_substream *substream;
+        int channel_id;
+	unsigned short running;
+};
+
+struct snd_ca0106_details {
+        u32 serial;
+        char * name;
+        int ac97;
+	int gpio_type;
+	int i2c_adc;
+	int spi_dac;
+};
+
+// definition of the chip-specific record
+struct snd_ca0106 {
+	struct snd_card *card;
+	struct snd_ca0106_details *details;
+	struct pci_dev *pci;
+
+	unsigned long port;
+	struct resource *res_port;
+	int irq;
+
+	unsigned int revision;		/* chip revision */
+	unsigned int serial;            /* serial number */
+	unsigned short model;		/* subsystem id */
+
+	spinlock_t emu_lock;
+
+	struct snd_ac97 *ac97;
+	struct snd_pcm *pcm;
+
+	struct snd_ca0106_channel playback_channels[4];
+	struct snd_ca0106_channel capture_channels[4];
+	u32 spdif_bits[4];             /* s/pdif out setup */
+	int spdif_enable;
+	int capture_source;
+	int capture_mic_line_in;
+
+	struct snd_dma_buffer buffer;
+
+	struct snd_ca_midi midi;
+	struct snd_ca_midi midi2;
+};
+
+int snd_ca0106_mixer(struct snd_ca0106 *emu);
+int snd_ca0106_proc_init(struct snd_ca0106 * emu);
+
+unsigned int snd_ca0106_ptr_read(struct snd_ca0106 * emu, 
+				 unsigned int reg, 
+				 unsigned int chn);
+
+void snd_ca0106_ptr_write(struct snd_ca0106 *emu, 
+			  unsigned int reg, 
+			  unsigned int chn, 
+			  unsigned int data);
+
+int snd_ca0106_i2c_write(struct snd_ca0106 *emu, u32 reg, u32 value);
+
+
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/ca0106/ca0106_main.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/ca0106/ca0106_main.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/ca0106/ca0106_main.c	(revision 91)
@@ -0,0 +1,1586 @@
+/*
+ *  Copyright (c) 2004 James Courtier-Dutton <James@superbug.demon.co.uk>
+ *  Driver CA0106 chips. e.g. Sound Blaster Audigy LS and Live 24bit
+ *  Version: 0.0.23
+ *
+ *  FEATURES currently supported:
+ *    Front, Rear and Center/LFE.
+ *    Surround40 and Surround51.
+ *    Capture from MIC an LINE IN input.
+ *    SPDIF digital playback of PCM stereo and AC3/DTS works.
+ *    (One can use a standard mono mini-jack to one RCA plugs cable.
+ *     or one can use a standard stereo mini-jack to two RCA plugs cable.
+ *     Plug one of the RCA plugs into the Coax input of the external decoder/receiver.)
+ *    ( In theory one could output 3 different AC3 streams at once, to 3 different SPDIF outputs. )
+ *    Notes on how to capture sound:
+ *      The AC97 is used in the PLAYBACK direction.
+ *      The output from the AC97 chip, instead of reaching the speakers, is fed into the Philips 1361T ADC.
+ *      So, to record from the MIC, set the MIC Playback volume to max,
+ *      unmute the MIC and turn up the MASTER Playback volume.
+ *      So, to prevent feedback when capturing, minimise the "Capture feedback into Playback" volume.
+ *   
+ *    The only playback controls that currently do anything are: -
+ *    Analog Front
+ *    Analog Rear
+ *    Analog Center/LFE
+ *    SPDIF Front
+ *    SPDIF Rear
+ *    SPDIF Center/LFE
+ *   
+ *    For capture from Mic in or Line in.
+ *    Digital/Analog ( switch must be in Analog mode for CAPTURE. )
+ * 
+ *    CAPTURE feedback into PLAYBACK
+ * 
+ *  Changelog:
+ *    Support interrupts per period.
+ *    Removed noise from Center/LFE channel when in Analog mode.
+ *    Rename and remove mixer controls.
+ *  0.0.6
+ *    Use separate card based DMA buffer for periods table list.
+ *  0.0.7
+ *    Change remove and rename ctrls into lists.
+ *  0.0.8
+ *    Try to fix capture sources.
+ *  0.0.9
+ *    Fix AC3 output.
+ *    Enable S32_LE format support.
+ *  0.0.10
+ *    Enable playback 48000 and 96000 rates. (Rates other that these do not work, even with "plug:front".)
+ *  0.0.11
+ *    Add Model name recognition.
+ *  0.0.12
+ *    Correct interrupt timing. interrupt at end of period, instead of in the middle of a playback period.
+ *    Remove redundent "voice" handling.
+ *  0.0.13
+ *    Single trigger call for multi channels.
+ *  0.0.14
+ *    Set limits based on what the sound card hardware can do.
+ *    playback periods_min=2, periods_max=8
+ *    capture hw constraints require period_size = n * 64 bytes.
+ *    playback hw constraints require period_size = n * 64 bytes.
+ *  0.0.15
+ *    Minor updates.
+ *  0.0.16
+ *    Implement 192000 sample rate.
+ *  0.0.17
+ *    Add support for SB0410 and SB0413.
+ *  0.0.18
+ *    Modified Copyright message.
+ *  0.0.19
+ *    Finally fix support for SB Live 24 bit. SB0410 and SB0413.
+ *    The output codec needs resetting, otherwise all output is muted.
+ *  0.0.20
+ *    Merge "pci_disable_device(pci);" fixes.
+ *  0.0.21
+ *    Add 4 capture channels. (SPDIF only comes in on channel 0. )
+ *    Add SPDIF capture using optional digital I/O module for SB Live 24bit. (Analog capture does not yet work.)
+ *  0.0.22
+ *    Add support for MSI K8N Diamond Motherboard with onboard SB Live 24bit without AC97. From kiksen, bug #901
+ *  0.0.23
+ *    Implement support for Line-in capture on SB Live 24bit.
+ *
+ *  BUGS:
+ *    Some stability problems when unloading the snd-ca0106 kernel module.
+ *    --
+ *
+ *  TODO:
+ *    4 Capture channels, only one implemented so far.
+ *    Other capture rates apart from 48khz not implemented.
+ *    MIDI
+ *    --
+ *  GENERAL INFO:
+ *    Model: SB0310
+ *    P17 Chip: CA0106-DAT
+ *    AC97 Codec: STAC 9721
+ *    ADC: Philips 1361T (Stereo 24bit)
+ *    DAC: WM8746EDS (6-channel, 24bit, 192Khz)
+ *
+ *  GENERAL INFO:
+ *    Model: SB0410
+ *    P17 Chip: CA0106-DAT
+ *    AC97 Codec: None
+ *    ADC: WM8775EDS (4 Channel)
+ *    DAC: CS4382 (114 dB, 24-Bit, 192 kHz, 8-Channel D/A Converter with DSD Support)
+ *    SPDIF Out control switches between Mic in and SPDIF out.
+ *    No sound out or mic input working yet.
+ * 
+ *  GENERAL INFO:
+ *    Model: SB0413
+ *    P17 Chip: CA0106-DAT
+ *    AC97 Codec: None.
+ *    ADC: Unknown
+ *    DAC: Unknown
+ *    Trying to handle it like the SB0410.
+ *
+ *  This code was initally based on code from ALSA's emu10k1x.c which is:
+ *  Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+//#include <linux/dma-mapping.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include <sound/info.h>
+
+MODULE_AUTHOR("James Courtier-Dutton <James@superbug.demon.co.uk>");
+MODULE_DESCRIPTION("CA0106");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{Creative,SB CA0106 chip}}");
+
+// module parameters (see "Module Parameters")
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+
+//module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for the CA0106 soundcard.");
+//module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for the CA0106 soundcard.");
+//module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable the CA0106 soundcard.");
+
+#include "ca0106.h"
+
+static struct snd_ca0106_details ca0106_chip_details[] = {
+	 /* AudigyLS[SB0310] */
+	 { .serial = 0x10021102,
+	   .name   = "AudigyLS [SB0310]",
+	   .ac97   = 1 } , 
+	 /* Unknown AudigyLS that also says SB0310 on it */
+	 { .serial = 0x10051102,
+	   .name   = "AudigyLS [SB0310b]",
+	   .ac97   = 1 } ,
+	 /* New Sound Blaster Live! 7.1 24bit. This does not have an AC97. 53SB041000001 */
+	 { .serial = 0x10061102,
+	   .name   = "Live! 7.1 24bit [SB0410]",
+	   .gpio_type = 1,
+	   .i2c_adc = 1 } ,
+	 /* New Dell Sound Blaster Live! 7.1 24bit. This does not have an AC97.  */
+	 { .serial = 0x10071102,
+	   .name   = "Live! 7.1 24bit [SB0413]",
+	   .gpio_type = 1,
+	   .i2c_adc = 1 } ,
+	 /* New Audigy SE. Has a different DAC. */
+	 /* SB0570:
+          * CTRL:CA0106-DAT
+          * ADC: WM8775EDS
+	  * DAC: WM8768GEDS
+	  */
+	 { .serial = 0x100a1102,
+	   .name   = "Audigy SE [SB0570]",
+	   .gpio_type = 1,
+	   .i2c_adc = 1,
+	   .spi_dac = 1 } ,
+	 /* MSI K8N Diamond Motherboard with onboard SB Live 24bit without AC97 */
+	 { .serial = 0x10091462,
+	   .name   = "MSI K8N Diamond MB [SB0438]",
+	   .gpio_type = 1,
+	   .i2c_adc = 1 } ,
+	 /* Shuttle XPC SD31P which has an onboard Creative Labs
+	  * Sound Blaster Live! 24-bit EAX
+	  * high-definition 7.1 audio processor".
+	  * Added using info from andrewvegan in alsa bug #1298
+	  */
+	 { .serial = 0x30381297,
+	   .name   = "Shuttle XPC SD31P [SD31P]",
+	   .gpio_type = 1,
+	   .i2c_adc = 1 } ,
+	/* Shuttle XPC SD11G5 which has an onboard Creative Labs
+	 * Sound Blaster Live! 24-bit EAX
+	 * high-definition 7.1 audio processor".
+	 * Fixes ALSA bug#1600
+         */
+	{ .serial = 0x30411297,
+	  .name = "Shuttle XPC SD11G5 [SD11G5]",
+	  .gpio_type = 1,
+	  .i2c_adc = 1 } ,
+	 { .serial = 0,
+	   .name   = "AudigyLS [Unknown]" }
+};
+
+/* hardware definition */
+static struct snd_pcm_hardware snd_ca0106_playback_hw = {
+	.info =			(SNDRV_PCM_INFO_MMAP | 
+				 SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+	.formats =		SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
+	.rates =		(SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+				 SNDRV_PCM_RATE_192000),
+	.rate_min =		48000,
+	.rate_max =		192000,
+	.channels_min =		2,  //1,
+	.channels_max =		2,  //6,
+	.buffer_bytes_max =	((65536 - 64) * 8),
+	.period_bytes_min =	64,
+	.period_bytes_max =	(65536 - 64),
+	.periods_min =		2,
+	.periods_max =		8,
+	.fifo_size =		0,
+};
+
+static struct snd_pcm_hardware snd_ca0106_capture_hw = {
+	.info =			(SNDRV_PCM_INFO_MMAP | 
+				 SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+	.formats =		SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
+	.rates =		(SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
+				 SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000),
+	.rate_min =		44100,
+	.rate_max =		192000,
+	.channels_min =		2,
+	.channels_max =		2,
+	.buffer_bytes_max =	((65536 - 64) * 8),
+	.period_bytes_min =	64,
+	.period_bytes_max =	(65536 - 64),
+	.periods_min =		2,
+	.periods_max =		2,
+	.fifo_size =		0,
+};
+
+unsigned int snd_ca0106_ptr_read(struct snd_ca0106 * emu, 
+					  unsigned int reg, 
+					  unsigned int chn)
+{
+	unsigned long flags;
+	unsigned int regptr, val;
+  
+	regptr = (reg << 16) | chn;
+
+	spin_lock_irqsave(&emu->emu_lock, flags);
+	outl(regptr, emu->port + PTR);
+	val = inl(emu->port + DATA);
+	spin_unlock_irqrestore(&emu->emu_lock, flags);
+	return val;
+}
+
+void snd_ca0106_ptr_write(struct snd_ca0106 *emu, 
+				   unsigned int reg, 
+				   unsigned int chn, 
+				   unsigned int data)
+{
+	unsigned int regptr;
+	unsigned long flags;
+
+	regptr = (reg << 16) | chn;
+
+	spin_lock_irqsave(&emu->emu_lock, flags);
+	outl(regptr, emu->port + PTR);
+	outl(data, emu->port + DATA);
+	spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+int snd_ca0106_spi_write(struct snd_ca0106 * emu,
+				   unsigned int data)
+{
+	unsigned int reset, set;
+	unsigned int reg, tmp;
+	int n, result;
+	reg = SPI;
+	if (data > 0xffff) /* Only 16bit values allowed */
+		return 1;
+	tmp = snd_ca0106_ptr_read(emu, reg, 0);
+	reset = (tmp & ~0x3ffff) | 0x20000; /* Set xxx20000 */
+	set = reset | 0x10000; /* Set xxx1xxxx */
+	snd_ca0106_ptr_write(emu, reg, 0, reset | data);
+	tmp = snd_ca0106_ptr_read(emu, reg, 0); /* write post */
+	snd_ca0106_ptr_write(emu, reg, 0, set | data);
+	result = 1;
+	/* Wait for status bit to return to 0 */
+	for (n = 0; n < 100; n++) {
+		udelay(10);
+		tmp = snd_ca0106_ptr_read(emu, reg, 0);
+		if (!(tmp & 0x10000)) {
+			result = 0;
+			break;
+		}
+	}
+	if (result) /* Timed out */
+		return 1;
+	snd_ca0106_ptr_write(emu, reg, 0, reset | data);
+	tmp = snd_ca0106_ptr_read(emu, reg, 0); /* Write post */
+	return 0;
+}
+
+int snd_ca0106_i2c_write(struct snd_ca0106 *emu,
+				u32 reg,
+				u32 value)
+{
+	u32 tmp;
+	int timeout = 0;
+	int status;
+	int retry;
+	if ((reg > 0x7f) || (value > 0x1ff)) {
+		snd_printk(KERN_ERR "i2c_write: invalid values.\n");
+		return -EINVAL;
+	}
+
+	tmp = reg << 25 | value << 16;
+	/* Not sure what this I2C channel controls. */
+	/* snd_ca0106_ptr_write(emu, I2C_D0, 0, tmp); */
+
+	/* This controls the I2C connected to the WM8775 ADC Codec */
+	snd_ca0106_ptr_write(emu, I2C_D1, 0, tmp);
+
+	for (retry = 0; retry < 10; retry++) {
+		/* Send the data to i2c */
+		tmp = snd_ca0106_ptr_read(emu, I2C_A, 0);
+		tmp = tmp & ~(I2C_A_ADC_READ|I2C_A_ADC_LAST|I2C_A_ADC_START|I2C_A_ADC_ADD_MASK);
+		tmp = tmp | (I2C_A_ADC_LAST|I2C_A_ADC_START|I2C_A_ADC_ADD);
+		snd_ca0106_ptr_write(emu, I2C_A, 0, tmp);
+
+		/* Wait till the transaction ends */
+		while (1) {
+			status = snd_ca0106_ptr_read(emu, I2C_A, 0);
+                	//snd_printk("I2C:status=0x%x\n", status);
+			timeout++;
+			if ((status & I2C_A_ADC_START) == 0)
+				break;
+
+			if (timeout > 1000)
+				break;
+		}
+		//Read back and see if the transaction is successful
+		if ((status & I2C_A_ADC_ABORT) == 0)
+			break;
+	}
+
+	if (retry == 10) {
+		snd_printk(KERN_ERR "Writing to ADC failed!\n");
+		return -EINVAL;
+	}
+    
+    	return 0;
+}
+
+
+static void snd_ca0106_intr_enable(struct snd_ca0106 *emu, unsigned int intrenb)
+{
+	unsigned long flags;
+	unsigned int enable;
+  
+	spin_lock_irqsave(&emu->emu_lock, flags);
+	enable = inl(emu->port + INTE) | intrenb;
+	outl(enable, emu->port + INTE);
+	spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+static void snd_ca0106_intr_disable(struct snd_ca0106 *emu, unsigned int intrenb)
+{
+	unsigned long flags;
+	unsigned int enable;
+  
+	spin_lock_irqsave(&emu->emu_lock, flags);
+	enable = inl(emu->port + INTE) & ~intrenb;
+	outl(enable, emu->port + INTE);
+	spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+
+static void snd_ca0106_pcm_free_substream(struct snd_pcm_runtime *runtime)
+{
+	kfree(runtime->private_data);
+}
+
+/* open_playback callback */
+static int snd_ca0106_pcm_open_playback_channel(struct snd_pcm_substream *substream,
+						int channel_id)
+{
+	struct snd_ca0106 *chip = snd_pcm_substream_chip(substream);
+        struct snd_ca0106_channel *channel = &(chip->playback_channels[channel_id]);
+	struct snd_ca0106_pcm *epcm;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	int err;
+
+	epcm = kzalloc(sizeof(*epcm), GFP_KERNEL);
+
+	if (epcm == NULL)
+		return -ENOMEM;
+	epcm->emu = chip;
+	epcm->substream = substream;
+        epcm->channel_id=channel_id;
+  
+	runtime->private_data = epcm;
+	runtime->private_free = snd_ca0106_pcm_free_substream;
+  
+	runtime->hw = snd_ca0106_playback_hw;
+
+        channel->emu = chip;
+        channel->number = channel_id;
+
+	channel->use = 1;
+        //printk("open:channel_id=%d, chip=%p, channel=%p\n",channel_id, chip, channel);
+        //channel->interrupt = snd_ca0106_pcm_channel_interrupt;
+	channel->epcm = epcm;
+	if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+                return err;
+	if ((err = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 64)) < 0)
+                return err;
+	return 0;
+}
+
+/* close callback */
+static int snd_ca0106_pcm_close_playback(struct snd_pcm_substream *substream)
+{
+	struct snd_ca0106 *chip = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+        struct snd_ca0106_pcm *epcm = runtime->private_data;
+	chip->playback_channels[epcm->channel_id].use = 0;
+	/* FIXME: maybe zero others */
+	return 0;
+}
+
+static int snd_ca0106_pcm_open_playback_front(struct snd_pcm_substream *substream)
+{
+	return snd_ca0106_pcm_open_playback_channel(substream, PCM_FRONT_CHANNEL);
+}
+
+static int snd_ca0106_pcm_open_playback_center_lfe(struct snd_pcm_substream *substream)
+{
+	return snd_ca0106_pcm_open_playback_channel(substream, PCM_CENTER_LFE_CHANNEL);
+}
+
+static int snd_ca0106_pcm_open_playback_unknown(struct snd_pcm_substream *substream)
+{
+	return snd_ca0106_pcm_open_playback_channel(substream, PCM_UNKNOWN_CHANNEL);
+}
+
+static int snd_ca0106_pcm_open_playback_rear(struct snd_pcm_substream *substream)
+{
+	return snd_ca0106_pcm_open_playback_channel(substream, PCM_REAR_CHANNEL);
+}
+
+/* open_capture callback */
+static int snd_ca0106_pcm_open_capture_channel(struct snd_pcm_substream *substream,
+					       int channel_id)
+{
+	struct snd_ca0106 *chip = snd_pcm_substream_chip(substream);
+        struct snd_ca0106_channel *channel = &(chip->capture_channels[channel_id]);
+	struct snd_ca0106_pcm *epcm;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	int err;
+
+	epcm = kzalloc(sizeof(*epcm), GFP_KERNEL);
+	if (epcm == NULL) {
+		snd_printk(KERN_ERR "open_capture_channel: failed epcm alloc\n");
+		return -ENOMEM;
+        }
+	epcm->emu = chip;
+	epcm->substream = substream;
+        epcm->channel_id=channel_id;
+  
+	runtime->private_data = epcm;
+	runtime->private_free = snd_ca0106_pcm_free_substream;
+  
+	runtime->hw = snd_ca0106_capture_hw;
+
+        channel->emu = chip;
+        channel->number = channel_id;
+
+	channel->use = 1;
+        //printk("open:channel_id=%d, chip=%p, channel=%p\n",channel_id, chip, channel);
+        //channel->interrupt = snd_ca0106_pcm_channel_interrupt;
+        channel->epcm = epcm;
+	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_PERIOD_SIZE, &hw_constraints_capture_period_sizes);
+	if ((err = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 64)) < 0)
+                return err;
+	return 0;
+}
+
+/* close callback */
+static int snd_ca0106_pcm_close_capture(struct snd_pcm_substream *substream)
+{
+	struct snd_ca0106 *chip = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+        struct snd_ca0106_pcm *epcm = runtime->private_data;
+	chip->capture_channels[epcm->channel_id].use = 0;
+	/* FIXME: maybe zero others */
+	return 0;
+}
+
+static int snd_ca0106_pcm_open_0_capture(struct snd_pcm_substream *substream)
+{
+	return snd_ca0106_pcm_open_capture_channel(substream, 0);
+}
+
+static int snd_ca0106_pcm_open_1_capture(struct snd_pcm_substream *substream)
+{
+	return snd_ca0106_pcm_open_capture_channel(substream, 1);
+}
+
+static int snd_ca0106_pcm_open_2_capture(struct snd_pcm_substream *substream)
+{
+	return snd_ca0106_pcm_open_capture_channel(substream, 2);
+}
+
+static int snd_ca0106_pcm_open_3_capture(struct snd_pcm_substream *substream)
+{
+	return snd_ca0106_pcm_open_capture_channel(substream, 3);
+}
+
+/* hw_params callback */
+static int snd_ca0106_pcm_hw_params_playback(struct snd_pcm_substream *substream,
+				      struct snd_pcm_hw_params *hw_params)
+{
+	return snd_pcm_lib_malloc_pages(substream,
+					params_buffer_bytes(hw_params));
+}
+
+/* hw_free callback */
+static int snd_ca0106_pcm_hw_free_playback(struct snd_pcm_substream *substream)
+{
+	return snd_pcm_lib_free_pages(substream);
+}
+
+/* hw_params callback */
+static int snd_ca0106_pcm_hw_params_capture(struct snd_pcm_substream *substream,
+				      struct snd_pcm_hw_params *hw_params)
+{
+	return snd_pcm_lib_malloc_pages(substream,
+					params_buffer_bytes(hw_params));
+}
+
+/* hw_free callback */
+static int snd_ca0106_pcm_hw_free_capture(struct snd_pcm_substream *substream)
+{
+	return snd_pcm_lib_free_pages(substream);
+}
+
+/* prepare playback callback */
+static int snd_ca0106_pcm_prepare_playback(struct snd_pcm_substream *substream)
+{
+	struct snd_ca0106 *emu = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_ca0106_pcm *epcm = runtime->private_data;
+	int channel = epcm->channel_id;
+	u32 *table_base = (u32 *)(emu->buffer.area+(8*16*channel));
+	u32 period_size_bytes = frames_to_bytes(runtime, runtime->period_size);
+	u32 hcfg_mask = HCFG_PLAYBACK_S32_LE;
+	u32 hcfg_set = 0x00000000;
+	u32 hcfg;
+	u32 reg40_mask = 0x30000 << (channel<<1);
+	u32 reg40_set = 0;
+	u32 reg40;
+	/* FIXME: Depending on mixer selection of SPDIF out or not, select the spdif rate or the DAC rate. */
+	u32 reg71_mask = 0x03030000 ; /* Global. Set SPDIF rate. We only support 44100 to spdif, not to DAC. */
+	u32 reg71_set = 0;
+	u32 reg71;
+	int i;
+	
+        //snd_printk("prepare:channel_number=%d, rate=%d, format=0x%x, channels=%d, buffer_size=%ld, period_size=%ld, periods=%u, frames_to_bytes=%d\n",channel, runtime->rate, runtime->format, runtime->channels, runtime->buffer_size, runtime->period_size, runtime->periods, frames_to_bytes(runtime, 1));
+        //snd_printk("dma_addr=%x, dma_area=%p, table_base=%p\n",runtime->dma_addr, runtime->dma_area, table_base);
+	//snd_printk("dma_addr=%x, dma_area=%p, dma_bytes(size)=%x\n",emu->buffer.addr, emu->buffer.area, emu->buffer.bytes);
+	/* Rate can be set per channel. */
+	/* reg40 control host to fifo */
+	/* reg71 controls DAC rate. */
+	switch (runtime->rate) {
+	case 44100:
+		reg40_set = 0x10000 << (channel<<1);
+		reg71_set = 0x01010000; 
+		break;
+        case 48000:
+		reg40_set = 0;
+		reg71_set = 0; 
+		break;
+	case 96000:
+		reg40_set = 0x20000 << (channel<<1);
+		reg71_set = 0x02020000; 
+		break;
+	case 192000:
+		reg40_set = 0x30000 << (channel<<1);
+		reg71_set = 0x03030000; 
+		break;
+	default:
+		reg40_set = 0;
+		reg71_set = 0; 
+		break;
+	}
+	/* Format is a global setting */
+	/* FIXME: Only let the first channel accessed set this. */
+	switch (runtime->format) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		hcfg_set = 0;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		hcfg_set = HCFG_PLAYBACK_S32_LE;
+		break;
+	default:
+		hcfg_set = 0;
+		break;
+	}
+	hcfg = inl(emu->port + HCFG) ;
+	hcfg = (hcfg & ~hcfg_mask) | hcfg_set;
+	outl(hcfg, emu->port + HCFG);
+	reg40 = snd_ca0106_ptr_read(emu, 0x40, 0);
+	reg40 = (reg40 & ~reg40_mask) | reg40_set;
+	snd_ca0106_ptr_write(emu, 0x40, 0, reg40);
+	reg71 = snd_ca0106_ptr_read(emu, 0x71, 0);
+	reg71 = (reg71 & ~reg71_mask) | reg71_set;
+	snd_ca0106_ptr_write(emu, 0x71, 0, reg71);
+
+	/* FIXME: Check emu->buffer.size before actually writing to it. */
+        for(i=0; i < runtime->periods; i++) {
+		table_base[i*2] = runtime->dma_addr + (i * period_size_bytes);
+		table_base[i*2+1] = period_size_bytes << 16;
+	}
+ 
+	snd_ca0106_ptr_write(emu, PLAYBACK_LIST_ADDR, channel, emu->buffer.addr+(8*16*channel));
+	snd_ca0106_ptr_write(emu, PLAYBACK_LIST_SIZE, channel, (runtime->periods - 1) << 19);
+	snd_ca0106_ptr_write(emu, PLAYBACK_LIST_PTR, channel, 0);
+	snd_ca0106_ptr_write(emu, PLAYBACK_DMA_ADDR, channel, runtime->dma_addr);
+	snd_ca0106_ptr_write(emu, PLAYBACK_PERIOD_SIZE, channel, frames_to_bytes(runtime, runtime->period_size)<<16); // buffer size in bytes
+	/* FIXME  test what 0 bytes does. */
+	snd_ca0106_ptr_write(emu, PLAYBACK_PERIOD_SIZE, channel, 0); // buffer size in bytes
+	snd_ca0106_ptr_write(emu, PLAYBACK_POINTER, channel, 0);
+	snd_ca0106_ptr_write(emu, 0x07, channel, 0x0);
+	snd_ca0106_ptr_write(emu, 0x08, channel, 0);
+        snd_ca0106_ptr_write(emu, PLAYBACK_MUTE, 0x0, 0x0); /* Unmute output */
+#if 0
+	snd_ca0106_ptr_write(emu, SPCS0, 0,
+			       SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
+			       SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
+			       SPCS_GENERATIONSTATUS | 0x00001200 |
+			       0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT );
+	}
+#endif
+
+	return 0;
+}
+
+/* prepare capture callback */
+static int snd_ca0106_pcm_prepare_capture(struct snd_pcm_substream *substream)
+{
+	struct snd_ca0106 *emu = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_ca0106_pcm *epcm = runtime->private_data;
+	int channel = epcm->channel_id;
+	u32 hcfg_mask = HCFG_CAPTURE_S32_LE;
+	u32 hcfg_set = 0x00000000;
+	u32 hcfg;
+	u32 over_sampling=0x2;
+	u32 reg71_mask = 0x0000c000 ; /* Global. Set ADC rate. */
+	u32 reg71_set = 0;
+	u32 reg71;
+	
+        //snd_printk("prepare:channel_number=%d, rate=%d, format=0x%x, channels=%d, buffer_size=%ld, period_size=%ld, periods=%u, frames_to_bytes=%d\n",channel, runtime->rate, runtime->format, runtime->channels, runtime->buffer_size, runtime->period_size, runtime->periods, frames_to_bytes(runtime, 1));
+        //snd_printk("dma_addr=%x, dma_area=%p, table_base=%p\n",runtime->dma_addr, runtime->dma_area, table_base);
+	//snd_printk("dma_addr=%x, dma_area=%p, dma_bytes(size)=%x\n",emu->buffer.addr, emu->buffer.area, emu->buffer.bytes);
+	/* reg71 controls ADC rate. */
+	switch (runtime->rate) {
+	case 44100:
+		reg71_set = 0x00004000;
+		break;
+        case 48000:
+		reg71_set = 0; 
+		break;
+	case 96000:
+		reg71_set = 0x00008000;
+		over_sampling=0xa;
+		break;
+	case 192000:
+		reg71_set = 0x0000c000; 
+		over_sampling=0xa;
+		break;
+	default:
+		reg71_set = 0; 
+		break;
+	}
+	/* Format is a global setting */
+	/* FIXME: Only let the first channel accessed set this. */
+	switch (runtime->format) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		hcfg_set = 0;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		hcfg_set = HCFG_CAPTURE_S32_LE;
+		break;
+	default:
+		hcfg_set = 0;
+		break;
+	}
+	hcfg = inl(emu->port + HCFG) ;
+	hcfg = (hcfg & ~hcfg_mask) | hcfg_set;
+	outl(hcfg, emu->port + HCFG);
+	reg71 = snd_ca0106_ptr_read(emu, 0x71, 0);
+	reg71 = (reg71 & ~reg71_mask) | reg71_set;
+	snd_ca0106_ptr_write(emu, 0x71, 0, reg71);
+        if (emu->details->i2c_adc == 1) { /* The SB0410 and SB0413 use I2C to control ADC. */
+	        snd_ca0106_i2c_write(emu, ADC_MASTER, over_sampling); /* Adjust the over sampler to better suit the capture rate. */
+	}
+
+
+        //printk("prepare:channel_number=%d, rate=%d, format=0x%x, channels=%d, buffer_size=%ld, period_size=%ld, frames_to_bytes=%d\n",channel, runtime->rate, runtime->format, runtime->channels, runtime->buffer_size, runtime->period_size,  frames_to_bytes(runtime, 1));
+	snd_ca0106_ptr_write(emu, 0x13, channel, 0);
+	snd_ca0106_ptr_write(emu, CAPTURE_DMA_ADDR, channel, runtime->dma_addr);
+	snd_ca0106_ptr_write(emu, CAPTURE_BUFFER_SIZE, channel, frames_to_bytes(runtime, runtime->buffer_size)<<16); // buffer size in bytes
+	snd_ca0106_ptr_write(emu, CAPTURE_POINTER, channel, 0);
+
+	return 0;
+}
+
+/* trigger_playback callback */
+static int snd_ca0106_pcm_trigger_playback(struct snd_pcm_substream *substream,
+				    int cmd)
+{
+	struct snd_ca0106 *emu = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime;
+	struct snd_ca0106_pcm *epcm;
+	int channel;
+	int result = 0;
+	struct list_head *pos;
+        struct snd_pcm_substream *s;
+	u32 basic = 0;
+	u32 extended = 0;
+	int running=0;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		running=1;
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	default:
+		running=0;
+		break;
+	}
+        snd_pcm_group_for_each(pos, substream) {
+                s = snd_pcm_group_substream_entry(pos);
+		runtime = s->runtime;
+		epcm = runtime->private_data;
+		channel = epcm->channel_id;
+		//snd_printk("channel=%d\n",channel);
+		epcm->running = running;
+		basic |= (0x1<<channel);
+		extended |= (0x10<<channel);
+                snd_pcm_trigger_done(s, substream);
+        }
+	//snd_printk("basic=0x%x, extended=0x%x\n",basic, extended);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		snd_ca0106_ptr_write(emu, EXTENDED_INT_MASK, 0, snd_ca0106_ptr_read(emu, EXTENDED_INT_MASK, 0) | (extended));
+		snd_ca0106_ptr_write(emu, BASIC_INTERRUPT, 0, snd_ca0106_ptr_read(emu, BASIC_INTERRUPT, 0)|(basic));
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		snd_ca0106_ptr_write(emu, BASIC_INTERRUPT, 0, snd_ca0106_ptr_read(emu, BASIC_INTERRUPT, 0) & ~(basic));
+		snd_ca0106_ptr_write(emu, EXTENDED_INT_MASK, 0, snd_ca0106_ptr_read(emu, EXTENDED_INT_MASK, 0) & ~(extended));
+		break;
+	default:
+		result = -EINVAL;
+		break;
+	}
+	return result;
+}
+
+/* trigger_capture callback */
+static int snd_ca0106_pcm_trigger_capture(struct snd_pcm_substream *substream,
+				    int cmd)
+{
+	struct snd_ca0106 *emu = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_ca0106_pcm *epcm = runtime->private_data;
+	int channel = epcm->channel_id;
+	int result = 0;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		snd_ca0106_ptr_write(emu, EXTENDED_INT_MASK, 0, snd_ca0106_ptr_read(emu, EXTENDED_INT_MASK, 0) | (0x110000<<channel));
+		snd_ca0106_ptr_write(emu, BASIC_INTERRUPT, 0, snd_ca0106_ptr_read(emu, BASIC_INTERRUPT, 0)|(0x100<<channel));
+		epcm->running = 1;
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		snd_ca0106_ptr_write(emu, BASIC_INTERRUPT, 0, snd_ca0106_ptr_read(emu, BASIC_INTERRUPT, 0) & ~(0x100<<channel));
+		snd_ca0106_ptr_write(emu, EXTENDED_INT_MASK, 0, snd_ca0106_ptr_read(emu, EXTENDED_INT_MASK, 0) & ~(0x110000<<channel));
+		epcm->running = 0;
+		break;
+	default:
+		result = -EINVAL;
+		break;
+	}
+	return result;
+}
+
+/* pointer_playback callback */
+static snd_pcm_uframes_t
+snd_ca0106_pcm_pointer_playback(struct snd_pcm_substream *substream)
+{
+	struct snd_ca0106 *emu = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_ca0106_pcm *epcm = runtime->private_data;
+	snd_pcm_uframes_t ptr, ptr1, ptr2,ptr3,ptr4 = 0;
+	int channel = epcm->channel_id;
+
+	if (!epcm->running)
+		return 0;
+
+	ptr3 = snd_ca0106_ptr_read(emu, PLAYBACK_LIST_PTR, channel);
+	ptr1 = snd_ca0106_ptr_read(emu, PLAYBACK_POINTER, channel);
+	ptr4 = snd_ca0106_ptr_read(emu, PLAYBACK_LIST_PTR, channel);
+	if (ptr3 != ptr4) ptr1 = snd_ca0106_ptr_read(emu, PLAYBACK_POINTER, channel);
+	ptr2 = bytes_to_frames(runtime, ptr1);
+	ptr2+= (ptr4 >> 3) * runtime->period_size;
+	ptr=ptr2;
+        if (ptr >= runtime->buffer_size)
+		ptr -= runtime->buffer_size;
+	//printk("ptr1 = 0x%lx, ptr2=0x%lx, ptr=0x%lx, buffer_size = 0x%x, period_size = 0x%x, bits=%d, rate=%d\n", ptr1, ptr2, ptr, (int)runtime->buffer_size, (int)runtime->period_size, (int)runtime->frame_bits, (int)runtime->rate);
+
+	return ptr;
+}
+
+/* pointer_capture callback */
+static snd_pcm_uframes_t
+snd_ca0106_pcm_pointer_capture(struct snd_pcm_substream *substream)
+{
+	struct snd_ca0106 *emu = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_ca0106_pcm *epcm = runtime->private_data;
+	snd_pcm_uframes_t ptr, ptr1, ptr2 = 0;
+	int channel = channel=epcm->channel_id;
+
+	if (!epcm->running)
+		return 0;
+
+	ptr1 = snd_ca0106_ptr_read(emu, CAPTURE_POINTER, channel);
+	ptr2 = bytes_to_frames(runtime, ptr1);
+	ptr=ptr2;
+        if (ptr >= runtime->buffer_size)
+		ptr -= runtime->buffer_size;
+	//printk("ptr1 = 0x%lx, ptr2=0x%lx, ptr=0x%lx, buffer_size = 0x%x, period_size = 0x%x, bits=%d, rate=%d\n", ptr1, ptr2, ptr, (int)runtime->buffer_size, (int)runtime->period_size, (int)runtime->frame_bits, (int)runtime->rate);
+
+	return ptr;
+}
+
+/* operators */
+static struct snd_pcm_ops snd_ca0106_playback_front_ops = {
+	.open =        snd_ca0106_pcm_open_playback_front,
+	.close =       snd_ca0106_pcm_close_playback,
+	.ioctl =       snd_pcm_lib_ioctl,
+	.hw_params =   snd_ca0106_pcm_hw_params_playback,
+	.hw_free =     snd_ca0106_pcm_hw_free_playback,
+	.prepare =     snd_ca0106_pcm_prepare_playback,
+	.trigger =     snd_ca0106_pcm_trigger_playback,
+	.pointer =     snd_ca0106_pcm_pointer_playback,
+};
+
+static struct snd_pcm_ops snd_ca0106_capture_0_ops = {
+	.open =        snd_ca0106_pcm_open_0_capture,
+	.close =       snd_ca0106_pcm_close_capture,
+	.ioctl =       snd_pcm_lib_ioctl,
+	.hw_params =   snd_ca0106_pcm_hw_params_capture,
+	.hw_free =     snd_ca0106_pcm_hw_free_capture,
+	.prepare =     snd_ca0106_pcm_prepare_capture,
+	.trigger =     snd_ca0106_pcm_trigger_capture,
+	.pointer =     snd_ca0106_pcm_pointer_capture,
+};
+
+static struct snd_pcm_ops snd_ca0106_capture_1_ops = {
+	.open =        snd_ca0106_pcm_open_1_capture,
+	.close =       snd_ca0106_pcm_close_capture,
+	.ioctl =       snd_pcm_lib_ioctl,
+	.hw_params =   snd_ca0106_pcm_hw_params_capture,
+	.hw_free =     snd_ca0106_pcm_hw_free_capture,
+	.prepare =     snd_ca0106_pcm_prepare_capture,
+	.trigger =     snd_ca0106_pcm_trigger_capture,
+	.pointer =     snd_ca0106_pcm_pointer_capture,
+};
+
+static struct snd_pcm_ops snd_ca0106_capture_2_ops = {
+	.open =        snd_ca0106_pcm_open_2_capture,
+	.close =       snd_ca0106_pcm_close_capture,
+	.ioctl =       snd_pcm_lib_ioctl,
+	.hw_params =   snd_ca0106_pcm_hw_params_capture,
+	.hw_free =     snd_ca0106_pcm_hw_free_capture,
+	.prepare =     snd_ca0106_pcm_prepare_capture,
+	.trigger =     snd_ca0106_pcm_trigger_capture,
+	.pointer =     snd_ca0106_pcm_pointer_capture,
+};
+
+static struct snd_pcm_ops snd_ca0106_capture_3_ops = {
+	.open =        snd_ca0106_pcm_open_3_capture,
+	.close =       snd_ca0106_pcm_close_capture,
+	.ioctl =       snd_pcm_lib_ioctl,
+	.hw_params =   snd_ca0106_pcm_hw_params_capture,
+	.hw_free =     snd_ca0106_pcm_hw_free_capture,
+	.prepare =     snd_ca0106_pcm_prepare_capture,
+	.trigger =     snd_ca0106_pcm_trigger_capture,
+	.pointer =     snd_ca0106_pcm_pointer_capture,
+};
+
+static struct snd_pcm_ops snd_ca0106_playback_center_lfe_ops = {
+        .open =         snd_ca0106_pcm_open_playback_center_lfe,
+        .close =        snd_ca0106_pcm_close_playback,
+        .ioctl =        snd_pcm_lib_ioctl,
+        .hw_params =    snd_ca0106_pcm_hw_params_playback,
+        .hw_free =      snd_ca0106_pcm_hw_free_playback,
+        .prepare =      snd_ca0106_pcm_prepare_playback,     
+        .trigger =      snd_ca0106_pcm_trigger_playback,  
+        .pointer =      snd_ca0106_pcm_pointer_playback, 
+};
+
+static struct snd_pcm_ops snd_ca0106_playback_unknown_ops = {
+        .open =         snd_ca0106_pcm_open_playback_unknown,
+        .close =        snd_ca0106_pcm_close_playback,
+        .ioctl =        snd_pcm_lib_ioctl,
+        .hw_params =    snd_ca0106_pcm_hw_params_playback,
+        .hw_free =      snd_ca0106_pcm_hw_free_playback,
+        .prepare =      snd_ca0106_pcm_prepare_playback,     
+        .trigger =      snd_ca0106_pcm_trigger_playback,  
+        .pointer =      snd_ca0106_pcm_pointer_playback, 
+};
+
+static struct snd_pcm_ops snd_ca0106_playback_rear_ops = {
+        .open =         snd_ca0106_pcm_open_playback_rear,
+        .close =        snd_ca0106_pcm_close_playback,
+        .ioctl =        snd_pcm_lib_ioctl,
+        .hw_params =    snd_ca0106_pcm_hw_params_playback,
+		.hw_free =      snd_ca0106_pcm_hw_free_playback,
+        .prepare =      snd_ca0106_pcm_prepare_playback,     
+        .trigger =      snd_ca0106_pcm_trigger_playback,  
+        .pointer =      snd_ca0106_pcm_pointer_playback, 
+};
+
+
+static unsigned short snd_ca0106_ac97_read(struct snd_ac97 *ac97,
+					     unsigned short reg)
+{
+	struct snd_ca0106 *emu = ac97->private_data;
+	unsigned long flags;
+	unsigned short val;
+
+	spin_lock_irqsave(&emu->emu_lock, flags);
+	outb(reg, emu->port + AC97ADDRESS);
+	val = inw(emu->port + AC97DATA);
+	spin_unlock_irqrestore(&emu->emu_lock, flags);
+	return val;
+}
+
+static void snd_ca0106_ac97_write(struct snd_ac97 *ac97,
+				    unsigned short reg, unsigned short val)
+{
+	struct snd_ca0106 *emu = ac97->private_data;
+	unsigned long flags;
+  
+	spin_lock_irqsave(&emu->emu_lock, flags);
+	outb(reg, emu->port + AC97ADDRESS);
+	outw(val, emu->port + AC97DATA);
+	spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+static int snd_ca0106_ac97(struct snd_ca0106 *chip)
+{
+	struct snd_ac97_bus *pbus;
+	struct snd_ac97_template ac97;
+	int err;
+	static struct snd_ac97_bus_ops ops = {
+		.write = snd_ca0106_ac97_write,
+		.read = snd_ca0106_ac97_read,
+	};
+  
+	if ((err = snd_ac97_bus(chip->card, 0, &ops, NULL, &pbus)) < 0)
+		return err;
+	pbus->no_vra = 1; /* we don't need VRA */
+
+	memset(&ac97, 0, sizeof(ac97));
+	ac97.private_data = chip;
+	ac97.scaps = AC97_SCAP_NO_SPDIF;
+	return snd_ac97_mixer(pbus, &ac97, &chip->ac97);
+}
+
+static int snd_ca0106_free(struct snd_ca0106 *chip)
+{
+	if (chip->res_port != NULL) {    /* avoid access to already used hardware */
+		// disable interrupts
+		snd_ca0106_ptr_write(chip, BASIC_INTERRUPT, 0, 0);
+		outl(0, chip->port + INTE);
+		snd_ca0106_ptr_write(chip, EXTENDED_INT_MASK, 0, 0);
+		udelay(1000);
+		// disable audio
+		//outl(HCFG_LOCKSOUNDCACHE, chip->port + HCFG);
+		outl(0, chip->port + HCFG);
+		/* FIXME: We need to stop and DMA transfers here.
+		 *        But as I am not sure how yet, we cannot from the dma pages.
+		 * So we can fix: snd-malloc: Memory leak?  pages not freed = 8
+		 */
+	}
+	// release the data
+#if 1
+	if (chip->buffer.area)
+		snd_dma_free_pages(&chip->buffer);
+#endif
+
+	// release the i/o port
+	release_and_free_resource(chip->res_port);
+
+	// release the irq
+	if (chip->irq >= 0)
+		free_irq(chip->irq, (void *)chip);
+	pci_disable_device(chip->pci);
+	kfree(chip);
+	return 0;
+}
+
+static int snd_ca0106_dev_free(struct snd_device *device)
+{
+	struct snd_ca0106 *chip = device->device_data;
+	return snd_ca0106_free(chip);
+}
+
+static irqreturn_t snd_ca0106_interrupt(int irq, void *dev_id,
+					  struct pt_regs *regs)
+{
+	unsigned int status;
+
+	struct snd_ca0106 *chip = dev_id;
+	int i;
+	int mask;
+        unsigned int stat76;
+	struct snd_ca0106_channel *pchannel;
+
+	status = inl(chip->port + IPR);
+	if (! status)
+		return IRQ_NONE;
+
+        stat76 = snd_ca0106_ptr_read(chip, EXTENDED_INT, 0);
+	//snd_printk("interrupt status = 0x%08x, stat76=0x%08x\n", status, stat76);
+	//snd_printk("ptr=0x%08x\n",snd_ca0106_ptr_read(chip, PLAYBACK_POINTER, 0));
+        mask = 0x11; /* 0x1 for one half, 0x10 for the other half period. */
+	for(i = 0; i < 4; i++) {
+		pchannel = &(chip->playback_channels[i]);
+		if (stat76 & mask) {
+/* FIXME: Select the correct substream for period elapsed */
+			if(pchannel->use) {
+				snd_pcm_period_elapsed(pchannel->epcm->substream);
+				//printk(KERN_INFO "interrupt [%d] used\n", i);
+                        }
+		}
+	        //printk(KERN_INFO "channel=%p\n",pchannel);
+	        //printk(KERN_INFO "interrupt stat76[%d] = %08x, use=%d, channel=%d\n", i, stat76, pchannel->use, pchannel->number);
+		mask <<= 1;
+	}
+        mask = 0x110000; /* 0x1 for one half, 0x10 for the other half period. */
+	for(i = 0; i < 4; i++) {
+		pchannel = &(chip->capture_channels[i]);
+		if (stat76 & mask) {
+/* FIXME: Select the correct substream for period elapsed */
+			if(pchannel->use) {
+				snd_pcm_period_elapsed(pchannel->epcm->substream);
+				//printk(KERN_INFO "interrupt [%d] used\n", i);
+                        }
+		}
+	        //printk(KERN_INFO "channel=%p\n",pchannel);
+	        //printk(KERN_INFO "interrupt stat76[%d] = %08x, use=%d, channel=%d\n", i, stat76, pchannel->use, pchannel->number);
+		mask <<= 1;
+	}
+
+        snd_ca0106_ptr_write(chip, EXTENDED_INT, 0, stat76);
+
+	if (chip->midi.dev_id &&
+	    (status & (chip->midi.ipr_tx|chip->midi.ipr_rx))) {
+		if (chip->midi.interrupt)
+			chip->midi.interrupt(&chip->midi, status);
+		else
+			chip->midi.interrupt_disable(&chip->midi, chip->midi.tx_enable | chip->midi.rx_enable);
+	}
+
+	// acknowledge the interrupt if necessary
+	outl(status, chip->port+IPR);
+
+	return IRQ_HANDLED;
+}
+
+static int __devinit snd_ca0106_pcm(struct snd_ca0106 *emu, int device, struct snd_pcm **rpcm)
+{
+	struct snd_pcm *pcm;
+	struct snd_pcm_substream *substream;
+	int err;
+  
+	if (rpcm)
+		*rpcm = NULL;
+	if ((err = snd_pcm_new(emu->card, "ca0106", device, 1, 1, &pcm)) < 0)
+		return err;
+  
+	pcm->private_data = emu;
+
+	switch (device) {
+	case 0:
+	  snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ca0106_playback_front_ops);
+	  snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ca0106_capture_0_ops);
+          break;
+	case 1:
+	  snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ca0106_playback_rear_ops);
+	  snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ca0106_capture_1_ops);
+          break;
+	case 2:
+	  snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ca0106_playback_center_lfe_ops);
+	  snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ca0106_capture_2_ops);
+          break;
+	case 3:
+	  snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ca0106_playback_unknown_ops);
+	  snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ca0106_capture_3_ops);
+          break;
+        }
+
+	pcm->info_flags = 0;
+	pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
+	strcpy(pcm->name, "CA0106");
+	emu->pcm = pcm;
+
+	for(substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; 
+	    substream; 
+	    substream = substream->next) {
+		if ((err = snd_pcm_lib_preallocate_pages(substream, 
+							 SNDRV_DMA_TYPE_DEV, 
+							 snd_dma_pci_data(emu->pci), 
+							 64*1024, 64*1024)) < 0) /* FIXME: 32*1024 for sound buffer, between 32and64 for Periods table. */
+			return err;
+	}
+
+	for (substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; 
+	      substream; 
+	      substream = substream->next) {
+ 		if ((err = snd_pcm_lib_preallocate_pages(substream, 
+	                                           SNDRV_DMA_TYPE_DEV, 
+	                                           snd_dma_pci_data(emu->pci), 
+	                                           64*1024, 64*1024)) < 0)
+			return err;
+	}
+  
+	if (rpcm)
+		*rpcm = pcm;
+  
+	return 0;
+}
+
+static unsigned int spi_dac_init[] = {
+	0x00ff,
+	0x02ff,
+	0x0400,
+        0x0520,
+        0x0620, /* Set 24 bit. Was 0x0600 */
+	0x08ff,
+	0x0aff,
+	0x0cff,
+	0x0eff,
+	0x10ff,
+	0x1200,
+	0x1400,
+	0x1480,
+	0x1800,
+	0x1aff,
+	0x1cff,
+	0x1e00,
+	0x0530,
+	0x0602,
+	0x0622,
+	0x1400,
+};
+
+static int __devinit snd_ca0106_create(struct snd_card *card,
+					 struct pci_dev *pci,
+					 struct snd_ca0106 **rchip)
+{
+	struct snd_ca0106 *chip;
+	struct snd_ca0106_details *c;
+	int err;
+	int ch;
+	static struct snd_device_ops ops = {
+		.dev_free = snd_ca0106_dev_free,
+	};
+  
+	*rchip = NULL;
+  
+	if ((err = pci_enable_device(pci)) < 0)
+		return err;
+	pci_set_dma_mask(pci, 0xFFFFFFFF);
+  
+	chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+	if (chip == NULL) {
+		pci_disable_device(pci);
+		return -ENOMEM;
+	}
+  
+	chip->card = card;
+	chip->pci = pci;
+	chip->irq = -1;
+
+	spin_lock_init(&chip->emu_lock);
+  
+	chip->port = pci_resource_start(pci, 0);
+	if ((chip->res_port = request_region(chip->port, 0x20,
+					     "snd_ca0106")) == NULL) { 
+		snd_ca0106_free(chip);
+		printk(KERN_ERR "cannot allocate the port\n");
+		return -EBUSY;
+	}
+
+	if (request_irq(pci->irq, snd_ca0106_interrupt,
+			SA_INTERRUPT|SA_SHIRQ, "snd_ca0106",
+			(void *)chip)) {
+		snd_ca0106_free(chip);
+		printk(KERN_ERR "cannot grab irq\n");
+		return -EBUSY;
+	}
+	chip->irq = pci->irq;
+  
+ 	/* This stores the periods table. */ 
+	if(snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci), 1024, &chip->buffer) < 0) {
+		snd_ca0106_free(chip);
+		return -ENOMEM;
+	}
+
+	pci_set_master(pci);
+	/* read revision & serial */
+	pci_read_config_byte(pci, PCI_REVISION_ID, (char *)&chip->revision);
+	pci_read_config_dword(pci, PCI_SUBSYSTEM_VENDOR_ID, &chip->serial);
+	pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &chip->model);
+#if 1
+	printk(KERN_INFO "Model %04x Rev %08x Serial %08x\n", chip->model,
+	       chip->revision, chip->serial);
+#endif
+	strcpy(card->driver, "CA0106");
+	strcpy(card->shortname, "CA0106");
+
+	for (c = ca0106_chip_details; c->serial; c++) {
+		if (c->serial == chip->serial)
+			break;
+	}
+	chip->details = c;
+	sprintf(card->longname, "%s at 0x%lx irq %i",
+		c->name, chip->port, chip->irq);
+
+	outl(0, chip->port + INTE);
+
+	/*
+	 *  Init to 0x02109204 :
+	 *  Clock accuracy    = 0     (1000ppm)
+	 *  Sample Rate       = 2     (48kHz)
+	 *  Audio Channel     = 1     (Left of 2)
+	 *  Source Number     = 0     (Unspecified)
+	 *  Generation Status = 1     (Original for Cat Code 12)
+	 *  Cat Code          = 12    (Digital Signal Mixer)
+	 *  Mode              = 0     (Mode 0)
+	 *  Emphasis          = 0     (None)
+	 *  CP                = 1     (Copyright unasserted)
+	 *  AN                = 0     (Audio data)
+	 *  P                 = 0     (Consumer)
+	 */
+	snd_ca0106_ptr_write(chip, SPCS0, 0,
+				chip->spdif_bits[0] =
+				SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
+				SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
+				SPCS_GENERATIONSTATUS | 0x00001200 |
+				0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT);
+	/* Only SPCS1 has been tested */
+	snd_ca0106_ptr_write(chip, SPCS1, 0,
+				chip->spdif_bits[1] =
+				SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
+				SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
+				SPCS_GENERATIONSTATUS | 0x00001200 |
+				0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT);
+	snd_ca0106_ptr_write(chip, SPCS2, 0,
+				chip->spdif_bits[2] =
+				SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
+				SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
+				SPCS_GENERATIONSTATUS | 0x00001200 |
+				0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT);
+	snd_ca0106_ptr_write(chip, SPCS3, 0,
+				chip->spdif_bits[3] =
+				SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
+				SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
+				SPCS_GENERATIONSTATUS | 0x00001200 |
+				0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT);
+
+        snd_ca0106_ptr_write(chip, PLAYBACK_MUTE, 0, 0x00fc0000);
+        snd_ca0106_ptr_write(chip, CAPTURE_MUTE, 0, 0x00fc0000);
+
+        /* Write 0x8000 to AC97_REC_GAIN to mute it. */
+        outb(AC97_REC_GAIN, chip->port + AC97ADDRESS);
+        outw(0x8000, chip->port + AC97DATA);
+#if 0
+	snd_ca0106_ptr_write(chip, SPCS0, 0, 0x2108006);
+	snd_ca0106_ptr_write(chip, 0x42, 0, 0x2108006);
+	snd_ca0106_ptr_write(chip, 0x43, 0, 0x2108006);
+	snd_ca0106_ptr_write(chip, 0x44, 0, 0x2108006);
+#endif
+
+	//snd_ca0106_ptr_write(chip, SPDIF_SELECT2, 0, 0xf0f003f); /* OSS drivers set this. */
+	/* Analog or Digital output */
+	snd_ca0106_ptr_write(chip, SPDIF_SELECT1, 0, 0xf);
+	snd_ca0106_ptr_write(chip, SPDIF_SELECT2, 0, 0x000f0000); /* 0x0b000000 for digital, 0x000b0000 for analog, from win2000 drivers. Use 0x000f0000 for surround71 */
+	chip->spdif_enable = 0; /* Set digital SPDIF output off */
+	chip->capture_source = 3; /* Set CAPTURE_SOURCE */
+	//snd_ca0106_ptr_write(chip, 0x45, 0, 0); /* Analogue out */
+	//snd_ca0106_ptr_write(chip, 0x45, 0, 0xf00); /* Digital out */
+
+	snd_ca0106_ptr_write(chip, CAPTURE_CONTROL, 0, 0x40c81000); /* goes to 0x40c80000 when doing SPDIF IN/OUT */
+	snd_ca0106_ptr_write(chip, CAPTURE_CONTROL, 1, 0xffffffff); /* (Mute) CAPTURE feedback into PLAYBACK volume. Only lower 16 bits matter. */
+	snd_ca0106_ptr_write(chip, CAPTURE_CONTROL, 2, 0x30300000); /* SPDIF IN Volume */
+	snd_ca0106_ptr_write(chip, CAPTURE_CONTROL, 3, 0x00700000); /* SPDIF IN Volume, 0x70 = (vol & 0x3f) | 0x40 */
+	snd_ca0106_ptr_write(chip, PLAYBACK_ROUTING1, 0, 0x32765410);
+	snd_ca0106_ptr_write(chip, PLAYBACK_ROUTING2, 0, 0x76767676);
+	snd_ca0106_ptr_write(chip, CAPTURE_ROUTING1, 0, 0x32765410);
+	snd_ca0106_ptr_write(chip, CAPTURE_ROUTING2, 0, 0x76767676);
+	for(ch = 0; ch < 4; ch++) {
+		snd_ca0106_ptr_write(chip, CAPTURE_VOLUME1, ch, 0x30303030); /* Only high 16 bits matter */
+		snd_ca0106_ptr_write(chip, CAPTURE_VOLUME2, ch, 0x30303030);
+		//snd_ca0106_ptr_write(chip, PLAYBACK_VOLUME1, ch, 0x40404040); /* Mute */
+		//snd_ca0106_ptr_write(chip, PLAYBACK_VOLUME2, ch, 0x40404040); /* Mute */
+		snd_ca0106_ptr_write(chip, PLAYBACK_VOLUME1, ch, 0xffffffff); /* Mute */
+		snd_ca0106_ptr_write(chip, PLAYBACK_VOLUME2, ch, 0xffffffff); /* Mute */
+	}
+        snd_ca0106_ptr_write(chip, CAPTURE_SOURCE, 0x0, 0x333300e4); /* Select MIC, Line in, TAD in, AUX in */
+	chip->capture_source = 3; /* Set CAPTURE_SOURCE */
+
+        if (chip->details->gpio_type == 1) { /* The SB0410 and SB0413 use GPIO differently. */
+		/* FIXME: Still need to find out what the other GPIO bits do. E.g. For digital spdif out. */
+		outl(0x0, chip->port+GPIO);
+		//outl(0x00f0e000, chip->port+GPIO); /* Analog */
+		outl(0x005f5301, chip->port+GPIO); /* Analog */
+	} else {
+		outl(0x0, chip->port+GPIO);
+		outl(0x005f03a3, chip->port+GPIO); /* Analog */
+		//outl(0x005f02a2, chip->port+GPIO);   /* SPDIF */
+	}
+	snd_ca0106_intr_enable(chip, 0x105); /* Win2000 uses 0x1e0 */
+
+	//outl(HCFG_LOCKSOUNDCACHE|HCFG_AUDIOENABLE, chip->port+HCFG);
+	//outl(0x00001409, chip->port+HCFG); /* 0x1000 causes AC3 to fails. Maybe it effects 24 bit output. */
+	//outl(0x00000009, chip->port+HCFG);
+	outl(HCFG_AC97 | HCFG_AUDIOENABLE, chip->port+HCFG); /* AC97 2.0, Enable outputs. */
+
+        if (chip->details->i2c_adc == 1) { /* The SB0410 and SB0413 use I2C to control ADC. */
+	        snd_ca0106_i2c_write(chip, ADC_MUX, ADC_MUX_LINEIN); /* Enable Line-in capture. MIC in currently untested. */
+	}
+        if (chip->details->spi_dac == 1) { /* The SB0570 use SPI to control DAC. */
+		int size, n;
+
+		size = ARRAY_SIZE(spi_dac_init);
+		for (n=0; n < size; n++)
+			snd_ca0106_spi_write(chip, spi_dac_init[n]);
+	}
+
+	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL,
+				  chip, &ops)) < 0) {
+		snd_ca0106_free(chip);
+		return err;
+	}
+	*rchip = chip;
+	return 0;
+}
+
+
+static void ca0106_midi_interrupt_enable(struct snd_ca_midi *midi, int intr)
+{
+	snd_ca0106_intr_enable((struct snd_ca0106 *)(midi->dev_id), intr);
+}
+
+static void ca0106_midi_interrupt_disable(struct snd_ca_midi *midi, int intr)
+{
+	snd_ca0106_intr_disable((struct snd_ca0106 *)(midi->dev_id), intr);
+}
+
+static unsigned char ca0106_midi_read(struct snd_ca_midi *midi, int idx)
+{
+	return (unsigned char)snd_ca0106_ptr_read((struct snd_ca0106 *)(midi->dev_id),
+						  midi->port + idx, 0);
+}
+
+static void ca0106_midi_write(struct snd_ca_midi *midi, int data, int idx)
+{
+	snd_ca0106_ptr_write((struct snd_ca0106 *)(midi->dev_id), midi->port + idx, 0, data);
+}
+
+static struct snd_card *ca0106_dev_id_card(void *dev_id)
+{
+	return ((struct snd_ca0106 *)dev_id)->card;
+}
+
+static int ca0106_dev_id_port(void *dev_id)
+{
+	return ((struct snd_ca0106 *)dev_id)->port;
+}
+
+static int __devinit snd_ca0106_midi(struct snd_ca0106 *chip, unsigned int channel)
+{
+	struct snd_ca_midi *midi;
+	char *name;
+	int err;
+
+	if (channel == CA0106_MIDI_CHAN_B) {
+		name = "CA0106 MPU-401 (UART) B";
+		midi =  &chip->midi2;
+		midi->tx_enable = INTE_MIDI_TX_B;
+		midi->rx_enable = INTE_MIDI_RX_B;
+		midi->ipr_tx = IPR_MIDI_TX_B;
+		midi->ipr_rx = IPR_MIDI_RX_B;
+		midi->port = MIDI_UART_B_DATA;
+	} else {
+		name = "CA0106 MPU-401 (UART)";
+		midi =  &chip->midi;
+		midi->tx_enable = INTE_MIDI_TX_A;
+		midi->rx_enable = INTE_MIDI_TX_B;
+		midi->ipr_tx = IPR_MIDI_TX_A;
+		midi->ipr_rx = IPR_MIDI_RX_A;
+		midi->port = MIDI_UART_A_DATA;
+	}
+
+	midi->reset = CA0106_MPU401_RESET;
+	midi->enter_uart = CA0106_MPU401_ENTER_UART;
+	midi->ack = CA0106_MPU401_ACK;
+
+	midi->input_avail = CA0106_MIDI_INPUT_AVAIL;
+	midi->output_ready = CA0106_MIDI_OUTPUT_READY;
+
+	midi->channel = channel;
+
+	midi->interrupt_enable = ca0106_midi_interrupt_enable;
+	midi->interrupt_disable = ca0106_midi_interrupt_disable;
+
+	midi->read = ca0106_midi_read;
+	midi->write = ca0106_midi_write;
+
+	midi->get_dev_id_card = ca0106_dev_id_card;
+	midi->get_dev_id_port = ca0106_dev_id_port;
+
+	midi->dev_id = chip;
+	
+	if ((err = ca_midi_init(chip, midi, 0, name)) < 0)
+		return err;
+
+	return 0;
+}
+
+
+static int __devinit snd_ca0106_probe(struct pci_dev *pci,
+					const struct pci_device_id *pci_id)
+{
+	static int dev;
+	struct snd_card *card;
+	struct snd_ca0106 *chip;
+	int err;
+
+	if (dev >= SNDRV_CARDS)
+		return -ENODEV;
+	if (!enable[dev]) {
+		dev++;
+		return -ENOENT;
+	}
+
+	card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+	if (card == NULL)
+		return -ENOMEM;
+
+	if ((err = snd_ca0106_create(card, pci, &chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	if ((err = snd_ca0106_pcm(chip, 0, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_ca0106_pcm(chip, 1, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_ca0106_pcm(chip, 2, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_ca0106_pcm(chip, 3, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+        if (chip->details->ac97 == 1) { /* The SB0410 and SB0413 do not have an AC97 chip. */
+		if ((err = snd_ca0106_ac97(chip)) < 0) {
+			snd_card_free(card);
+			return err;
+		}
+	}
+	if ((err = snd_ca0106_mixer(chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	snd_printdd("ca0106: probe for MIDI channel A ...");
+	if ((err = snd_ca0106_midi(chip,CA0106_MIDI_CHAN_A)) < 0) {
+		snd_card_free(card);
+		snd_printdd(" failed, err=0x%x\n",err);
+		return err;
+	}
+	snd_printdd(" done.\n");
+
+#ifdef CONFIG_PROC_FS
+	snd_ca0106_proc_init(chip);
+#endif
+
+	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_ca0106_remove(struct pci_dev *pci)
+{
+	snd_card_free(pci_get_drvdata(pci));
+	pci_set_drvdata(pci, NULL);
+}
+
+// PCI IDs
+static struct pci_device_id snd_ca0106_ids[] = {
+	{ 0x1102, 0x0007, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },	/* Audigy LS or Live 24bit */
+	{ 0, }
+};
+MODULE_DEVICE_TABLE(pci, snd_ca0106_ids);
+
+// pci_driver definition
+static struct pci_driver driver = {
+	.name = "CA0106",
+	.id_table = snd_ca0106_ids,
+	.probe = snd_ca0106_probe,
+	.remove = __devexit_p(snd_ca0106_remove),
+};
+
+// initialization of the module
+static int __init alsa_card_ca0106_init(void)
+{
+	return pci_register_driver(&driver);
+}
+
+// clean up the module
+static void __exit alsa_card_ca0106_exit(void)
+{
+	pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_ca0106_init)
+module_exit(alsa_card_ca0106_exit)
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/ca0106/ca0106_mixer.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/ca0106/ca0106_mixer.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/ca0106/ca0106_mixer.c	(revision 91)
@@ -0,0 +1,492 @@
+/*
+ *  Copyright (c) 2004 James Courtier-Dutton <James@superbug.demon.co.uk>
+ *  Driver CA0106 chips. e.g. Sound Blaster Audigy LS and Live 24bit
+ *  Version: 0.0.17
+ *
+ *  FEATURES currently supported:
+ *    See ca0106_main.c for features.
+ * 
+ *  Changelog:
+ *    Support interrupts per period.
+ *    Removed noise from Center/LFE channel when in Analog mode.
+ *    Rename and remove mixer controls.
+ *  0.0.6
+ *    Use separate card based DMA buffer for periods table list.
+ *  0.0.7
+ *    Change remove and rename ctrls into lists.
+ *  0.0.8
+ *    Try to fix capture sources.
+ *  0.0.9
+ *    Fix AC3 output.
+ *    Enable S32_LE format support.
+ *  0.0.10
+ *    Enable playback 48000 and 96000 rates. (Rates other that these do not work, even with "plug:front".)
+ *  0.0.11
+ *    Add Model name recognition.
+ *  0.0.12
+ *    Correct interrupt timing. interrupt at end of period, instead of in the middle of a playback period.
+ *    Remove redundent "voice" handling.
+ *  0.0.13
+ *    Single trigger call for multi channels.
+ *  0.0.14
+ *    Set limits based on what the sound card hardware can do.
+ *    playback periods_min=2, periods_max=8
+ *    capture hw constraints require period_size = n * 64 bytes.
+ *    playback hw constraints require period_size = n * 64 bytes.
+ *  0.0.15
+ *    Separated ca0106.c into separate functional .c files.
+ *  0.0.16
+ *    Modified Copyright message.
+ *  0.0.17
+ *    Implement Mic and Line in Capture.
+ *
+ *  This code was initally based on code from ALSA's emu10k1x.c which is:
+ *  Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include <sound/info.h>
+#include <sound/tlv.h>
+
+#include "ca0106.h"
+
+static DECLARE_TLV_DB_SCALE(snd_ca0106_db_scale1, -5175, 25, 1);
+static DECLARE_TLV_DB_SCALE(snd_ca0106_db_scale2, -10350, 50, 1);
+
+static int snd_ca0106_shared_spdif_info(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_info *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_ca0106_shared_spdif_get(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
+
+	ucontrol->value.enumerated.item[0] = emu->spdif_enable;
+	return 0;
+}
+
+static int snd_ca0106_shared_spdif_put(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
+	unsigned int val;
+	int change = 0;
+	u32 mask;
+
+	val = ucontrol->value.enumerated.item[0] ;
+	change = (emu->spdif_enable != val);
+	if (change) {
+		emu->spdif_enable = val;
+		if (val == 1) {
+			/* Digital */
+			snd_ca0106_ptr_write(emu, SPDIF_SELECT1, 0, 0xf);
+			snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x0b000000);
+			snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0,
+				snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) & ~0x1000);
+			mask = inl(emu->port + GPIO) & ~0x101;
+			outl(mask, emu->port + GPIO);
+
+		} else {
+			/* Analog */
+			snd_ca0106_ptr_write(emu, SPDIF_SELECT1, 0, 0xf);
+			snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x000f0000);
+			snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0,
+				snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) | 0x1000);
+			mask = inl(emu->port + GPIO) | 0x101;
+			outl(mask, emu->port + GPIO);
+		}
+	}
+        return change;
+}
+
+static int snd_ca0106_capture_source_info(struct snd_kcontrol *kcontrol,
+					  struct snd_ctl_elem_info *uinfo)
+{
+	static char *texts[6] = {
+		"IEC958 out", "i2s mixer out", "IEC958 in", "i2s in", "AC97 in", "SRC out"
+	};
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 6;
+	if (uinfo->value.enumerated.item > 5)
+                uinfo->value.enumerated.item = 5;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int snd_ca0106_capture_source_get(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
+
+	ucontrol->value.enumerated.item[0] = emu->capture_source;
+	return 0;
+}
+
+static int snd_ca0106_capture_source_put(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
+	unsigned int val;
+	int change = 0;
+	u32 mask;
+	u32 source;
+
+	val = ucontrol->value.enumerated.item[0] ;
+	change = (emu->capture_source != val);
+	if (change) {
+		emu->capture_source = val;
+		source = (val << 28) | (val << 24) | (val << 20) | (val << 16);
+		mask = snd_ca0106_ptr_read(emu, CAPTURE_SOURCE, 0) & 0xffff;
+		snd_ca0106_ptr_write(emu, CAPTURE_SOURCE, 0, source | mask);
+	}
+        return change;
+}
+
+static int snd_ca0106_capture_mic_line_in_info(struct snd_kcontrol *kcontrol,
+					       struct snd_ctl_elem_info *uinfo)
+{
+	static char *texts[2] = { "Line in", "Mic in" };
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 2;
+	if (uinfo->value.enumerated.item > 1)
+                uinfo->value.enumerated.item = 1;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int snd_ca0106_capture_mic_line_in_get(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
+
+	ucontrol->value.enumerated.item[0] = emu->capture_mic_line_in;
+	return 0;
+}
+
+static int snd_ca0106_capture_mic_line_in_put(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
+	unsigned int val;
+	int change = 0;
+	u32 tmp;
+
+	val = ucontrol->value.enumerated.item[0] ;
+	change = (emu->capture_mic_line_in != val);
+	if (change) {
+		emu->capture_mic_line_in = val;
+		if (val) {
+			snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_PHONE); /* Mute input */
+			tmp = inl(emu->port+GPIO) & ~0x400;
+			tmp = tmp | 0x400;
+			outl(tmp, emu->port+GPIO);
+			snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_MIC);
+		} else {
+			snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_PHONE); /* Mute input */
+			tmp = inl(emu->port+GPIO) & ~0x400;
+			outl(tmp, emu->port+GPIO);
+			snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_LINEIN);
+		}
+	}
+        return change;
+}
+
+static struct snd_kcontrol_new snd_ca0106_capture_mic_line_in __devinitdata =
+{
+	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name =		"Mic/Line in Capture",
+	.info =		snd_ca0106_capture_mic_line_in_info,
+	.get =		snd_ca0106_capture_mic_line_in_get,
+	.put =		snd_ca0106_capture_mic_line_in_put
+};
+
+static int snd_ca0106_spdif_info(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+	uinfo->count = 1;
+	return 0;
+}
+
+static int snd_ca0106_spdif_get(struct snd_kcontrol *kcontrol,
+                                 struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
+	unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+
+	ucontrol->value.iec958.status[0] = (emu->spdif_bits[idx] >> 0) & 0xff;
+	ucontrol->value.iec958.status[1] = (emu->spdif_bits[idx] >> 8) & 0xff;
+	ucontrol->value.iec958.status[2] = (emu->spdif_bits[idx] >> 16) & 0xff;
+	ucontrol->value.iec958.status[3] = (emu->spdif_bits[idx] >> 24) & 0xff;
+        return 0;
+}
+
+static int snd_ca0106_spdif_get_mask(struct snd_kcontrol *kcontrol,
+				      struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.iec958.status[0] = 0xff;
+	ucontrol->value.iec958.status[1] = 0xff;
+	ucontrol->value.iec958.status[2] = 0xff;
+	ucontrol->value.iec958.status[3] = 0xff;
+        return 0;
+}
+
+static int snd_ca0106_spdif_put(struct snd_kcontrol *kcontrol,
+                                 struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
+	unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+	int change;
+	unsigned int val;
+
+	val = (ucontrol->value.iec958.status[0] << 0) |
+	      (ucontrol->value.iec958.status[1] << 8) |
+	      (ucontrol->value.iec958.status[2] << 16) |
+	      (ucontrol->value.iec958.status[3] << 24);
+	change = val != emu->spdif_bits[idx];
+	if (change) {
+		snd_ca0106_ptr_write(emu, SPCS0 + idx, 0, val);
+		emu->spdif_bits[idx] = val;
+	}
+        return change;
+}
+
+static int snd_ca0106_volume_info(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_info *uinfo)
+{
+        uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+        uinfo->count = 2;
+        uinfo->value.integer.min = 0;
+        uinfo->value.integer.max = 255;
+        return 0;
+}
+
+static int snd_ca0106_volume_get(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol)
+{
+        struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
+        unsigned int value;
+	int channel_id, reg;
+
+	channel_id = (kcontrol->private_value >> 8) & 0xff;
+	reg = kcontrol->private_value & 0xff;
+
+        value = snd_ca0106_ptr_read(emu, reg, channel_id);
+        ucontrol->value.integer.value[0] = 0xff - ((value >> 24) & 0xff); /* Left */
+        ucontrol->value.integer.value[1] = 0xff - ((value >> 16) & 0xff); /* Right */
+        return 0;
+}
+
+static int snd_ca0106_volume_put(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol)
+{
+        struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
+        unsigned int oval, nval;
+	int channel_id, reg;
+
+	channel_id = (kcontrol->private_value >> 8) & 0xff;
+	reg = kcontrol->private_value & 0xff;
+
+	oval = snd_ca0106_ptr_read(emu, reg, channel_id);
+	nval = ((0xff - ucontrol->value.integer.value[0]) << 24) |
+		((0xff - ucontrol->value.integer.value[1]) << 16);
+        nval |= ((0xff - ucontrol->value.integer.value[0]) << 8) |
+		((0xff - ucontrol->value.integer.value[1]) );
+	if (oval == nval)
+		return 0;
+	snd_ca0106_ptr_write(emu, reg, channel_id, nval);
+	return 1;
+}
+
+#define CA_VOLUME(xname,chid,reg) \
+{								\
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,	\
+	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |		\
+	          SNDRV_CTL_ELEM_ACCESS_TLV_READ,		\
+	.info =	 snd_ca0106_volume_info,			\
+	.get =   snd_ca0106_volume_get,				\
+	.put =   snd_ca0106_volume_put,				\
+	.tlv = { .p = snd_ca0106_db_scale1 },			\
+	.private_value = ((chid) << 8) | (reg)			\
+}
+
+static struct snd_kcontrol_new snd_ca0106_volume_ctls[] __devinitdata = {
+	CA_VOLUME("Analog Front Playback Volume",
+		  CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME2),
+        CA_VOLUME("Analog Rear Playback Volume",
+		  CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME2),
+	CA_VOLUME("Analog Center/LFE Playback Volume",
+		  CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME2),
+        CA_VOLUME("Analog Side Playback Volume",
+		  CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME2),
+
+        CA_VOLUME("IEC958 Front Playback Volume",
+		  CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME1),
+	CA_VOLUME("IEC958 Rear Playback Volume",
+		  CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME1),
+	CA_VOLUME("IEC958 Center/LFE Playback Volume",
+		  CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME1),
+	CA_VOLUME("IEC958 Unknown Playback Volume",
+		  CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME1),
+
+        CA_VOLUME("CAPTURE feedback Playback Volume",
+		  1, CAPTURE_CONTROL),
+
+	{
+		.access =	SNDRV_CTL_ELEM_ACCESS_READ,
+		.iface =        SNDRV_CTL_ELEM_IFACE_PCM,
+		.name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK),
+		.count =	4,
+		.info =         snd_ca0106_spdif_info,
+		.get =          snd_ca0106_spdif_get_mask
+	},
+	{
+		.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name =		"IEC958 Playback Switch",
+		.info =		snd_ca0106_shared_spdif_info,
+		.get =		snd_ca0106_shared_spdif_get,
+		.put =		snd_ca0106_shared_spdif_put
+	},
+	{
+		.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name =		"Capture Source",
+		.info =		snd_ca0106_capture_source_info,
+		.get =		snd_ca0106_capture_source_get,
+		.put =		snd_ca0106_capture_source_put
+	},
+	{
+		.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
+		.name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
+		.count =	4,
+		.info =         snd_ca0106_spdif_info,
+		.get =          snd_ca0106_spdif_get,
+		.put =          snd_ca0106_spdif_put
+	},
+};
+
+static int __devinit remove_ctl(struct snd_card *card, const char *name)
+{
+	struct snd_ctl_elem_id id;
+	memset(&id, 0, sizeof(id));
+	strcpy(id.name, name);
+	id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+	return snd_ctl_remove_id(card, &id);
+}
+
+static struct snd_kcontrol __devinit *ctl_find(struct snd_card *card, const char *name)
+{
+	struct snd_ctl_elem_id sid;
+	memset(&sid, 0, sizeof(sid));
+	/* FIXME: strcpy is bad. */
+	strcpy(sid.name, name);
+	sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+	return snd_ctl_find_id(card, &sid);
+}
+
+static int __devinit rename_ctl(struct snd_card *card, const char *src, const char *dst)
+{
+	struct snd_kcontrol *kctl = ctl_find(card, src);
+	if (kctl) {
+		strcpy(kctl->id.name, dst);
+		return 0;
+	}
+	return -ENOENT;
+}
+
+int __devinit snd_ca0106_mixer(struct snd_ca0106 *emu)
+{
+	int i, err;
+        struct snd_card *card = emu->card;
+	char **c;
+	static char *ca0106_remove_ctls[] = {
+		"Master Mono Playback Switch",
+		"Master Mono Playback Volume",
+		"3D Control - Switch",
+		"3D Control Sigmatel - Depth",
+		"PCM Playback Switch",
+		"PCM Playback Volume",
+		"CD Playback Switch",
+		"CD Playback Volume",
+		"Phone Playback Switch",
+		"Phone Playback Volume",
+		"Video Playback Switch",
+		"Video Playback Volume",
+		"PC Speaker Playback Switch",
+		"PC Speaker Playback Volume",
+		"Mono Output Select",
+		"Capture Source",
+		"Capture Switch",
+		"Capture Volume",
+		"External Amplifier",
+		"Sigmatel 4-Speaker Stereo Playback Switch",
+		"Sigmatel Surround Phase Inversion Playback ",
+		NULL
+	};
+	static char *ca0106_rename_ctls[] = {
+		"Master Playback Switch", "Capture Switch",
+		"Master Playback Volume", "Capture Volume",
+		"Line Playback Switch", "AC97 Line Capture Switch",
+		"Line Playback Volume", "AC97 Line Capture Volume",
+		"Aux Playback Switch", "AC97 Aux Capture Switch",
+		"Aux Playback Volume", "AC97 Aux Capture Volume",
+		"Mic Playback Switch", "AC97 Mic Capture Switch",
+		"Mic Playback Volume", "AC97 Mic Capture Volume",
+		"Mic Select", "AC97 Mic Select",
+		"Mic Boost (+20dB)", "AC97 Mic Boost (+20dB)",
+		NULL
+	};
+#if 1
+	for (c = ca0106_remove_ctls; *c; c++)
+		remove_ctl(card, *c);
+	for (c = ca0106_rename_ctls; *c; c += 2)
+		rename_ctl(card, c[0], c[1]);
+#endif
+
+	for (i = 0; i < ARRAY_SIZE(snd_ca0106_volume_ctls); i++) {
+		err = snd_ctl_add(card, snd_ctl_new1(&snd_ca0106_volume_ctls[i], emu));
+		if (err < 0)
+			return err;
+	}
+	if (emu->details->i2c_adc == 1) {
+		err = snd_ctl_add(card, snd_ctl_new1(&snd_ca0106_capture_mic_line_in, emu));
+		if (err < 0)
+			return err;
+	}
+        return 0;
+}
+
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/ca0106/ca0106_proc.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/ca0106/ca0106_proc.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/ca0106/ca0106_proc.c	(revision 91)
@@ -0,0 +1,470 @@
+/*
+ *  Copyright (c) 2004 James Courtier-Dutton <James@superbug.demon.co.uk>
+ *  Driver CA0106 chips. e.g. Sound Blaster Audigy LS and Live 24bit
+ *  Version: 0.0.18
+ *
+ *  FEATURES currently supported:
+ *    See ca0106_main.c for features.
+ * 
+ *  Changelog:
+ *    Support interrupts per period.
+ *    Removed noise from Center/LFE channel when in Analog mode.
+ *    Rename and remove mixer controls.
+ *  0.0.6
+ *    Use separate card based DMA buffer for periods table list.
+ *  0.0.7
+ *    Change remove and rename ctrls into lists.
+ *  0.0.8
+ *    Try to fix capture sources.
+ *  0.0.9
+ *    Fix AC3 output.
+ *    Enable S32_LE format support.
+ *  0.0.10
+ *    Enable playback 48000 and 96000 rates. (Rates other that these do not work, even with "plug:front".)
+ *  0.0.11
+ *    Add Model name recognition.
+ *  0.0.12
+ *    Correct interrupt timing. interrupt at end of period, instead of in the middle of a playback period.
+ *    Remove redundent "voice" handling.
+ *  0.0.13
+ *    Single trigger call for multi channels.
+ *  0.0.14
+ *    Set limits based on what the sound card hardware can do.
+ *    playback periods_min=2, periods_max=8
+ *    capture hw constraints require period_size = n * 64 bytes.
+ *    playback hw constraints require period_size = n * 64 bytes.
+ *  0.0.15
+ *    Separate ca0106.c into separate functional .c files.
+ *  0.0.16
+ *    Modified Copyright message.
+ *  0.0.17
+ *    Add iec958 file in proc file system to show status of SPDIF in.
+ *  0.0.18
+ *    Implement support for Line-in capture on SB Live 24bit.
+ *
+ *  This code was initally based on code from ALSA's emu10k1x.c which is:
+ *  Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include <sound/info.h>
+#include <sound/asoundef.h>
+
+#include "ca0106.h"
+
+
+#ifdef CONFIG_PROC_FS
+
+struct snd_ca0106_category_str {
+	int val;
+	const char *name;
+};
+
+static struct snd_ca0106_category_str snd_ca0106_con_category[] = {
+	{ IEC958_AES1_CON_DAT, "DAT" },
+	{ IEC958_AES1_CON_VCR, "VCR" },
+	{ IEC958_AES1_CON_MICROPHONE, "microphone" },
+	{ IEC958_AES1_CON_SYNTHESIZER, "synthesizer" },
+	{ IEC958_AES1_CON_RATE_CONVERTER, "rate converter" },
+	{ IEC958_AES1_CON_MIXER, "mixer" },
+	{ IEC958_AES1_CON_SAMPLER, "sampler" },
+	{ IEC958_AES1_CON_PCM_CODER, "PCM coder" },
+	{ IEC958_AES1_CON_IEC908_CD, "CD" },
+	{ IEC958_AES1_CON_NON_IEC908_CD, "non-IEC908 CD" },
+	{ IEC958_AES1_CON_GENERAL, "general" },
+};
+
+
+static void snd_ca0106_proc_dump_iec958( struct snd_info_buffer *buffer, u32 value)
+{
+	int i;
+	u32 status[4];
+	status[0] = value & 0xff;
+	status[1] = (value >> 8) & 0xff;
+	status[2] = (value >> 16)  & 0xff;
+	status[3] = (value >> 24)  & 0xff;
+	
+	if (! (status[0] & IEC958_AES0_PROFESSIONAL)) {
+		/* consumer */
+		snd_iprintf(buffer, "Mode: consumer\n");
+		snd_iprintf(buffer, "Data: ");
+		if (!(status[0] & IEC958_AES0_NONAUDIO)) {
+			snd_iprintf(buffer, "audio\n");
+		} else {
+			snd_iprintf(buffer, "non-audio\n");
+		}
+		snd_iprintf(buffer, "Rate: ");
+		switch (status[3] & IEC958_AES3_CON_FS) {
+		case IEC958_AES3_CON_FS_44100:
+			snd_iprintf(buffer, "44100 Hz\n");
+			break;
+		case IEC958_AES3_CON_FS_48000:
+			snd_iprintf(buffer, "48000 Hz\n");
+			break;
+		case IEC958_AES3_CON_FS_32000:
+			snd_iprintf(buffer, "32000 Hz\n");
+			break;
+		default:
+			snd_iprintf(buffer, "unknown\n");
+			break;
+		}
+		snd_iprintf(buffer, "Copyright: ");
+		if (status[0] & IEC958_AES0_CON_NOT_COPYRIGHT) {
+			snd_iprintf(buffer, "permitted\n");
+		} else {
+			snd_iprintf(buffer, "protected\n");
+		}
+		snd_iprintf(buffer, "Emphasis: ");
+		if ((status[0] & IEC958_AES0_CON_EMPHASIS) != IEC958_AES0_CON_EMPHASIS_5015) {
+			snd_iprintf(buffer, "none\n");
+		} else {
+			snd_iprintf(buffer, "50/15us\n");
+		}
+		snd_iprintf(buffer, "Category: ");
+		for (i = 0; i < ARRAY_SIZE(snd_ca0106_con_category); i++) {
+			if ((status[1] & IEC958_AES1_CON_CATEGORY) == snd_ca0106_con_category[i].val) {
+				snd_iprintf(buffer, "%s\n", snd_ca0106_con_category[i].name);
+				break;
+			}
+		}
+		if (i >= ARRAY_SIZE(snd_ca0106_con_category)) {
+			snd_iprintf(buffer, "unknown 0x%x\n", status[1] & IEC958_AES1_CON_CATEGORY);
+		}
+		snd_iprintf(buffer, "Original: ");
+		if (status[1] & IEC958_AES1_CON_ORIGINAL) {
+			snd_iprintf(buffer, "original\n");
+		} else {
+			snd_iprintf(buffer, "1st generation\n");
+		}
+		snd_iprintf(buffer, "Clock: ");
+		switch (status[3] & IEC958_AES3_CON_CLOCK) {
+		case IEC958_AES3_CON_CLOCK_1000PPM:
+			snd_iprintf(buffer, "1000 ppm\n");
+			break;
+		case IEC958_AES3_CON_CLOCK_50PPM:
+			snd_iprintf(buffer, "50 ppm\n");
+			break;
+		case IEC958_AES3_CON_CLOCK_VARIABLE:
+			snd_iprintf(buffer, "variable pitch\n");
+			break;
+		default:
+			snd_iprintf(buffer, "unknown\n");
+			break;
+		}
+	} else {
+		snd_iprintf(buffer, "Mode: professional\n");
+		snd_iprintf(buffer, "Data: ");
+		if (!(status[0] & IEC958_AES0_NONAUDIO)) {
+			snd_iprintf(buffer, "audio\n");
+		} else {
+			snd_iprintf(buffer, "non-audio\n");
+		}
+		snd_iprintf(buffer, "Rate: ");
+		switch (status[0] & IEC958_AES0_PRO_FS) {
+		case IEC958_AES0_PRO_FS_44100:
+			snd_iprintf(buffer, "44100 Hz\n");
+			break;
+		case IEC958_AES0_PRO_FS_48000:
+			snd_iprintf(buffer, "48000 Hz\n");
+			break;
+		case IEC958_AES0_PRO_FS_32000:
+			snd_iprintf(buffer, "32000 Hz\n");
+			break;
+		default:
+			snd_iprintf(buffer, "unknown\n");
+			break;
+		}
+		snd_iprintf(buffer, "Rate Locked: ");
+		if (status[0] & IEC958_AES0_PRO_FREQ_UNLOCKED)
+			snd_iprintf(buffer, "no\n");
+		else
+			snd_iprintf(buffer, "yes\n");
+		snd_iprintf(buffer, "Emphasis: ");
+		switch (status[0] & IEC958_AES0_PRO_EMPHASIS) {
+		case IEC958_AES0_PRO_EMPHASIS_CCITT:
+			snd_iprintf(buffer, "CCITT J.17\n");
+			break;
+		case IEC958_AES0_PRO_EMPHASIS_NONE:
+			snd_iprintf(buffer, "none\n");
+			break;
+		case IEC958_AES0_PRO_EMPHASIS_5015:
+			snd_iprintf(buffer, "50/15us\n");
+			break;
+		case IEC958_AES0_PRO_EMPHASIS_NOTID:
+		default:
+			snd_iprintf(buffer, "unknown\n");
+			break;
+		}
+		snd_iprintf(buffer, "Stereophonic: ");
+		if ((status[1] & IEC958_AES1_PRO_MODE) == IEC958_AES1_PRO_MODE_STEREOPHONIC) {
+			snd_iprintf(buffer, "stereo\n");
+		} else {
+			snd_iprintf(buffer, "not indicated\n");
+		}
+		snd_iprintf(buffer, "Userbits: ");
+		switch (status[1] & IEC958_AES1_PRO_USERBITS) {
+		case IEC958_AES1_PRO_USERBITS_192:
+			snd_iprintf(buffer, "192bit\n");
+			break;
+		case IEC958_AES1_PRO_USERBITS_UDEF:
+			snd_iprintf(buffer, "user-defined\n");
+			break;
+		default:
+			snd_iprintf(buffer, "unkown\n");
+			break;
+		}
+		snd_iprintf(buffer, "Sample Bits: ");
+		switch (status[2] & IEC958_AES2_PRO_SBITS) {
+		case IEC958_AES2_PRO_SBITS_20:
+			snd_iprintf(buffer, "20 bit\n");
+			break;
+		case IEC958_AES2_PRO_SBITS_24:
+			snd_iprintf(buffer, "24 bit\n");
+			break;
+		case IEC958_AES2_PRO_SBITS_UDEF:
+			snd_iprintf(buffer, "user defined\n");
+			break;
+		default:
+			snd_iprintf(buffer, "unknown\n");
+			break;
+		}
+		snd_iprintf(buffer, "Word Length: ");
+		switch (status[2] & IEC958_AES2_PRO_WORDLEN) {
+		case IEC958_AES2_PRO_WORDLEN_22_18:
+			snd_iprintf(buffer, "22 bit or 18 bit\n");
+			break;
+		case IEC958_AES2_PRO_WORDLEN_23_19:
+			snd_iprintf(buffer, "23 bit or 19 bit\n");
+			break;
+		case IEC958_AES2_PRO_WORDLEN_24_20:
+			snd_iprintf(buffer, "24 bit or 20 bit\n");
+			break;
+		case IEC958_AES2_PRO_WORDLEN_20_16:
+			snd_iprintf(buffer, "20 bit or 16 bit\n");
+			break;
+		default:
+			snd_iprintf(buffer, "unknown\n");
+			break;
+		}
+	}
+}
+
+static void snd_ca0106_proc_iec958(struct snd_info_entry *entry, 
+				       struct snd_info_buffer *buffer)
+{
+	struct snd_ca0106 *emu = entry->private_data;
+	u32 value;
+
+        value = snd_ca0106_ptr_read(emu, SAMPLE_RATE_TRACKER_STATUS, 0);
+	snd_iprintf(buffer, "Status: %s, %s, %s\n",
+		  (value & 0x100000) ? "Rate Locked" : "Not Rate Locked",
+		  (value & 0x200000) ? "SPDIF Locked" : "No SPDIF Lock",
+		  (value & 0x400000) ? "Audio Valid" : "No valid audio" );
+	snd_iprintf(buffer, "Estimated sample rate: %u\n", 
+		  ((value & 0xfffff) * 48000) / 0x8000 );
+	if (value & 0x200000) {
+		snd_iprintf(buffer, "IEC958/SPDIF input status:\n");
+        	value = snd_ca0106_ptr_read(emu, SPDIF_INPUT_STATUS, 0);
+		snd_ca0106_proc_dump_iec958(buffer, value);
+	}
+
+	snd_iprintf(buffer, "\n");
+}
+
+static void snd_ca0106_proc_reg_write32(struct snd_info_entry *entry, 
+				       struct snd_info_buffer *buffer)
+{
+	struct snd_ca0106 *emu = entry->private_data;
+	unsigned long flags;
+        char line[64];
+        u32 reg, val;
+#if 0
+        while (!snd_info_get_line(buffer, line, sizeof(line))) {
+                if (sscanf(line, "%x %x", &reg, &val) != 2)
+                        continue;
+                if ((reg < 0x40) && (reg >=0) && (val <= 0xffffffff) ) {
+			spin_lock_irqsave(&emu->emu_lock, flags);
+			outl(val, emu->port + (reg & 0xfffffffc));
+			spin_unlock_irqrestore(&emu->emu_lock, flags);
+		}
+        }
+#endif
+}
+
+static void snd_ca0106_proc_reg_read32(struct snd_info_entry *entry, 
+				       struct snd_info_buffer *buffer)
+{
+	struct snd_ca0106 *emu = entry->private_data;
+	unsigned long value;
+	unsigned long flags;
+	int i;
+	snd_iprintf(buffer, "Registers:\n\n");
+	for(i = 0; i < 0x20; i+=4) {
+		spin_lock_irqsave(&emu->emu_lock, flags);
+		value = inl(emu->port + i);
+		spin_unlock_irqrestore(&emu->emu_lock, flags);
+		snd_iprintf(buffer, "Register %02X: %08lX\n", i, value);
+	}
+}
+
+static void snd_ca0106_proc_reg_read16(struct snd_info_entry *entry, 
+				       struct snd_info_buffer *buffer)
+{
+	struct snd_ca0106 *emu = entry->private_data;
+        unsigned int value;
+	unsigned long flags;
+	int i;
+	snd_iprintf(buffer, "Registers:\n\n");
+	for(i = 0; i < 0x20; i+=2) {
+		spin_lock_irqsave(&emu->emu_lock, flags);
+		value = inw(emu->port + i);
+		spin_unlock_irqrestore(&emu->emu_lock, flags);
+		snd_iprintf(buffer, "Register %02X: %04X\n", i, value);
+	}
+}
+
+static void snd_ca0106_proc_reg_read8(struct snd_info_entry *entry, 
+				       struct snd_info_buffer *buffer)
+{
+	struct snd_ca0106 *emu = entry->private_data;
+	unsigned int value;
+	unsigned long flags;
+	int i;
+	snd_iprintf(buffer, "Registers:\n\n");
+	for(i = 0; i < 0x20; i+=1) {
+		spin_lock_irqsave(&emu->emu_lock, flags);
+		value = inb(emu->port + i);
+		spin_unlock_irqrestore(&emu->emu_lock, flags);
+		snd_iprintf(buffer, "Register %02X: %02X\n", i, value);
+	}
+}
+
+static void snd_ca0106_proc_reg_read1(struct snd_info_entry *entry, 
+				       struct snd_info_buffer *buffer)
+{
+	struct snd_ca0106 *emu = entry->private_data;
+	unsigned long value;
+	int i,j;
+
+	snd_iprintf(buffer, "Registers\n");
+	for(i = 0; i < 0x40; i++) {
+		snd_iprintf(buffer, "%02X: ",i);
+		for (j = 0; j < 4; j++) {
+                  value = snd_ca0106_ptr_read(emu, i, j);
+		  snd_iprintf(buffer, "%08lX ", value);
+                }
+	        snd_iprintf(buffer, "\n");
+	}
+}
+
+static void snd_ca0106_proc_reg_read2(struct snd_info_entry *entry, 
+				       struct snd_info_buffer *buffer)
+{
+	struct snd_ca0106 *emu = entry->private_data;
+	unsigned long value;
+	int i,j;
+
+	snd_iprintf(buffer, "Registers\n");
+	for(i = 0x40; i < 0x80; i++) {
+		snd_iprintf(buffer, "%02X: ",i);
+		for (j = 0; j < 4; j++) {
+                  value = snd_ca0106_ptr_read(emu, i, j);
+		  snd_iprintf(buffer, "%08lX ", value);
+                }
+	        snd_iprintf(buffer, "\n");
+	}
+}
+
+static void snd_ca0106_proc_reg_write(struct snd_info_entry *entry, 
+				       struct snd_info_buffer *buffer)
+{
+	struct snd_ca0106 *emu = entry->private_data;
+        char line[64];
+        unsigned int reg, channel_id , val;
+#if 0
+        while (!snd_info_get_line(buffer, line, sizeof(line))) {
+                if (sscanf(line, "%x %x %x", &reg, &channel_id, &val) != 3)
+                        continue;
+                if ((reg < 0x80) && (reg >=0) && (val <= 0xffffffff) && (channel_id >=0) && (channel_id <= 3) )
+                        snd_ca0106_ptr_write(emu, reg, channel_id, val);
+        }
+#endif
+}
+
+static void snd_ca0106_proc_i2c_write(struct snd_info_entry *entry, 
+				       struct snd_info_buffer *buffer)
+{
+	struct snd_ca0106 *emu = entry->private_data;
+        char line[64];
+        unsigned int reg, val;
+#if 0
+        while (!snd_info_get_line(buffer, line, sizeof(line))) {
+                if (sscanf(line, "%x %x", &reg, &val) != 2)
+                        continue;
+                if ((reg <= 0x7f) || (val <= 0x1ff)) {
+                        snd_ca0106_i2c_write(emu, reg, val);
+		}
+        }
+#endif
+}
+
+int __devinit snd_ca0106_proc_init(struct snd_ca0106 * emu)
+{
+	struct snd_info_entry *entry;
+	
+	if(! snd_card_proc_new(emu->card, "iec958", &entry))
+		snd_info_set_text_ops(entry, emu, 1024, snd_ca0106_proc_iec958);
+	if(! snd_card_proc_new(emu->card, "ca0106_reg32", &entry)) {
+		snd_info_set_text_ops(entry, emu, 1024, snd_ca0106_proc_reg_read32);
+		entry->c.text.write_size = 64;
+		entry->c.text.write = snd_ca0106_proc_reg_write32;
+		entry->mode |= S_IWUSR;
+	}
+	if(! snd_card_proc_new(emu->card, "ca0106_reg16", &entry))
+		snd_info_set_text_ops(entry, emu, 1024, snd_ca0106_proc_reg_read16);
+	if(! snd_card_proc_new(emu->card, "ca0106_reg8", &entry))
+		snd_info_set_text_ops(entry, emu, 1024, snd_ca0106_proc_reg_read8);
+	if(! snd_card_proc_new(emu->card, "ca0106_regs1", &entry)) {
+		snd_info_set_text_ops(entry, emu, 1024, snd_ca0106_proc_reg_read1);
+		entry->c.text.write_size = 64;
+		entry->c.text.write = snd_ca0106_proc_reg_write;
+		entry->mode |= S_IWUSR;
+//		entry->private_data = emu;
+	}
+	if(! snd_card_proc_new(emu->card, "ca0106_i2c", &entry)) {
+		snd_info_set_text_ops(entry, emu, 1024, snd_ca0106_proc_i2c_write);
+		entry->c.text.write_size = 64;
+		entry->c.text.write = snd_ca0106_proc_i2c_write;
+		entry->mode |= S_IWUSR;
+//		entry->private_data = emu;
+	}
+	if(! snd_card_proc_new(emu->card, "ca0106_regs2", &entry)) 
+		snd_info_set_text_ops(entry, emu, 1024, snd_ca0106_proc_reg_read2);
+	return 0;
+}
+
+#endif /* CONFIG_PROC_FS */
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/ca0106/ca_midi.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/ca0106/ca_midi.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/ca0106/ca_midi.c	(revision 91)
@@ -0,0 +1,309 @@
+/* 
+ *  Copyright 10/16/2005 Tilman Kranz <tilde@tk-sls.de>
+ *  Creative Audio MIDI, for the CA0106 Driver
+ *  Version: 0.0.1
+ *
+ *  Changelog:
+ *    Implementation is based on mpu401 and emu10k1x and
+ *    tested with ca0106.
+ *    mpu401: Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *    emu10k1x: Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ *
+ */
+
+#include <linux/spinlock.h>
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/rawmidi.h>
+
+#include "ca_midi.h"
+
+#define ca_midi_write_data(midi, data)	midi->write(midi, data, 0)
+#define ca_midi_write_cmd(midi, data)	midi->write(midi, data, 1)
+#define ca_midi_read_data(midi)		midi->read(midi, 0)
+#define ca_midi_read_stat(midi)		midi->read(midi, 1)
+#define ca_midi_input_avail(midi)	(!(ca_midi_read_stat(midi) & midi->input_avail))
+#define ca_midi_output_ready(midi)	(!(ca_midi_read_stat(midi) & midi->output_ready))
+
+static void ca_midi_clear_rx(struct snd_ca_midi *midi)
+{
+	int timeout = 100000;
+	for (; timeout > 0 && ca_midi_input_avail(midi); timeout--)
+		ca_midi_read_data(midi);
+#ifdef CONFIG_SND_DEBUG
+	if (timeout <= 0)
+		snd_printk(KERN_ERR "ca_midi_clear_rx: timeout (status = 0x%x)\n",
+			   ca_midi_read_stat(midi));
+#endif
+}
+
+static void ca_midi_interrupt(struct snd_ca_midi *midi, unsigned int status)
+{
+	unsigned char byte;
+
+	if (midi->rmidi == NULL) {
+		midi->interrupt_disable(midi,midi->tx_enable | midi->rx_enable);
+		return;
+	}
+
+	spin_lock(&midi->input_lock);
+	if ((status & midi->ipr_rx) && ca_midi_input_avail(midi)) {
+		if (!(midi->midi_mode & CA_MIDI_MODE_INPUT)) {
+			ca_midi_clear_rx(midi);
+		} else {
+			byte = ca_midi_read_data(midi);
+			if(midi->substream_input)
+				snd_rawmidi_receive(midi->substream_input, &byte, 1);
+
+
+		}
+	}
+	spin_unlock(&midi->input_lock);
+
+	spin_lock(&midi->output_lock);
+	if ((status & midi->ipr_tx) && ca_midi_output_ready(midi)) {
+		if (midi->substream_output &&
+		    snd_rawmidi_transmit(midi->substream_output, &byte, 1) == 1) {
+			ca_midi_write_data(midi, byte);
+		} else {
+			midi->interrupt_disable(midi,midi->tx_enable);
+		}
+	}
+	spin_unlock(&midi->output_lock);
+
+}
+
+static void ca_midi_cmd(struct snd_ca_midi *midi, unsigned char cmd, int ack)
+{
+	unsigned long flags;
+	int timeout, ok;
+
+	spin_lock_irqsave(&midi->input_lock, flags);
+	ca_midi_write_data(midi, 0x00);
+	/* ca_midi_clear_rx(midi); */
+
+	ca_midi_write_cmd(midi, cmd);
+	if (ack) {
+		ok = 0;
+		timeout = 10000;
+		while (!ok && timeout-- > 0) {
+			if (ca_midi_input_avail(midi)) {
+				if (ca_midi_read_data(midi) == midi->ack)
+					ok = 1;
+			}
+		}
+		if (!ok && ca_midi_read_data(midi) == midi->ack)
+			ok = 1;
+	} else {
+		ok = 1;
+	}
+	spin_unlock_irqrestore(&midi->input_lock, flags);
+	if (!ok)
+		snd_printk(KERN_ERR "ca_midi_cmd: 0x%x failed at 0x%x (status = 0x%x, data = 0x%x)!!!\n",
+			   cmd,
+			   midi->get_dev_id_port(midi->dev_id),
+			   ca_midi_read_stat(midi),
+			   ca_midi_read_data(midi));
+}
+
+static int ca_midi_input_open(struct snd_rawmidi_substream *substream)
+{
+	struct snd_ca_midi *midi = substream->rmidi->private_data;
+	unsigned long flags;
+	
+	snd_assert(midi->dev_id, return -ENXIO);
+	spin_lock_irqsave(&midi->open_lock, flags);
+	midi->midi_mode |= CA_MIDI_MODE_INPUT;
+	midi->substream_input = substream;
+	if (!(midi->midi_mode & CA_MIDI_MODE_OUTPUT)) {
+		spin_unlock_irqrestore(&midi->open_lock, flags);
+		ca_midi_cmd(midi, midi->reset, 1);
+		ca_midi_cmd(midi, midi->enter_uart, 1);
+	} else {
+		spin_unlock_irqrestore(&midi->open_lock, flags);
+	}
+	return 0;
+}
+
+static int ca_midi_output_open(struct snd_rawmidi_substream *substream)
+{
+	struct snd_ca_midi *midi = substream->rmidi->private_data;
+	unsigned long flags;
+
+	snd_assert(midi->dev_id, return -ENXIO);
+	spin_lock_irqsave(&midi->open_lock, flags);
+	midi->midi_mode |= CA_MIDI_MODE_OUTPUT;
+	midi->substream_output = substream;
+	if (!(midi->midi_mode & CA_MIDI_MODE_INPUT)) {
+		spin_unlock_irqrestore(&midi->open_lock, flags);
+		ca_midi_cmd(midi, midi->reset, 1);
+		ca_midi_cmd(midi, midi->enter_uart, 1);
+	} else {
+		spin_unlock_irqrestore(&midi->open_lock, flags);
+	}
+	return 0;
+}
+
+static int ca_midi_input_close(struct snd_rawmidi_substream *substream)
+{
+	struct snd_ca_midi *midi = substream->rmidi->private_data;
+	unsigned long flags;
+
+	snd_assert(midi->dev_id, return -ENXIO);
+	spin_lock_irqsave(&midi->open_lock, flags);
+	midi->interrupt_disable(midi,midi->rx_enable);
+	midi->midi_mode &= ~CA_MIDI_MODE_INPUT;
+	midi->substream_input = NULL;
+	if (!(midi->midi_mode & CA_MIDI_MODE_OUTPUT)) {
+		spin_unlock_irqrestore(&midi->open_lock, flags);
+		ca_midi_cmd(midi, midi->reset, 0);
+	} else {
+		spin_unlock_irqrestore(&midi->open_lock, flags);
+	}
+	return 0;
+}
+
+static int ca_midi_output_close(struct snd_rawmidi_substream *substream)
+{
+	struct snd_ca_midi *midi = substream->rmidi->private_data;
+	unsigned long flags;
+	snd_assert(midi->dev_id, return -ENXIO);
+	
+	spin_lock_irqsave(&midi->open_lock, flags);
+
+	midi->interrupt_disable(midi,midi->tx_enable);
+	midi->midi_mode &= ~CA_MIDI_MODE_OUTPUT;
+	midi->substream_output = NULL;
+	
+	if (!(midi->midi_mode & CA_MIDI_MODE_INPUT)) {
+		spin_unlock_irqrestore(&midi->open_lock, flags);
+		ca_midi_cmd(midi, midi->reset, 0);
+	} else {
+		spin_unlock_irqrestore(&midi->open_lock, flags);
+	}
+	return 0;
+}
+
+static void ca_midi_input_trigger(struct snd_rawmidi_substream *substream, int up)
+{
+	struct snd_ca_midi *midi = substream->rmidi->private_data;
+	snd_assert(midi->dev_id, return);
+
+	if (up) {
+		midi->interrupt_enable(midi,midi->rx_enable);
+	} else {
+		midi->interrupt_disable(midi, midi->rx_enable);
+	}
+}
+
+static void ca_midi_output_trigger(struct snd_rawmidi_substream *substream, int up)
+{
+	struct snd_ca_midi *midi = substream->rmidi->private_data;
+	unsigned long flags;
+
+	snd_assert(midi->dev_id, return);
+
+	if (up) {
+		int max = 4;
+		unsigned char byte;
+
+		spin_lock_irqsave(&midi->output_lock, flags);
+	
+		/* try to send some amount of bytes here before interrupts */
+		while (max > 0) {
+			if (ca_midi_output_ready(midi)) {
+				if (!(midi->midi_mode & CA_MIDI_MODE_OUTPUT) ||
+				    snd_rawmidi_transmit(substream, &byte, 1) != 1) {
+					/* no more data */
+					spin_unlock_irqrestore(&midi->output_lock, flags);
+					return;
+				}
+				ca_midi_write_data(midi, byte);
+				max--;
+			} else {
+				break;
+			}
+		}
+
+		spin_unlock_irqrestore(&midi->output_lock, flags);
+		midi->interrupt_enable(midi,midi->tx_enable);
+
+	} else {
+		midi->interrupt_disable(midi,midi->tx_enable);
+	}
+}
+
+static struct snd_rawmidi_ops ca_midi_output =
+{
+	.open =		ca_midi_output_open,
+	.close =	ca_midi_output_close,
+	.trigger =	ca_midi_output_trigger,
+};
+
+static struct snd_rawmidi_ops ca_midi_input =
+{
+	.open =		ca_midi_input_open,
+	.close =	ca_midi_input_close,
+	.trigger =	ca_midi_input_trigger,
+};
+
+static void ca_midi_free(struct snd_ca_midi *midi)
+{
+	midi->interrupt = NULL;
+	midi->interrupt_enable = NULL;
+	midi->interrupt_disable = NULL;
+	midi->read = NULL;
+	midi->write = NULL;
+	midi->get_dev_id_card = NULL;
+	midi->get_dev_id_port = NULL;
+	midi->rmidi = NULL;
+}
+
+static void ca_rmidi_free(struct snd_rawmidi *rmidi)
+{
+	ca_midi_free(rmidi->private_data);
+}
+
+int __devinit ca_midi_init(void *dev_id, struct snd_ca_midi *midi, int device, char *name)
+{
+	struct snd_rawmidi *rmidi;
+	int err;
+
+	if ((err = snd_rawmidi_new(midi->get_dev_id_card(midi->dev_id), name, device, 1, 1, &rmidi)) < 0)
+		return err;
+
+	midi->dev_id = dev_id;
+	midi->interrupt = ca_midi_interrupt;
+
+	spin_lock_init(&midi->open_lock);
+	spin_lock_init(&midi->input_lock);
+	spin_lock_init(&midi->output_lock);
+
+	strcpy(rmidi->name, name);
+	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &ca_midi_output);
+	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &ca_midi_input);
+	rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT |
+	                     SNDRV_RAWMIDI_INFO_INPUT |
+	                     SNDRV_RAWMIDI_INFO_DUPLEX;
+	rmidi->private_data = midi;
+	rmidi->private_free = ca_rmidi_free;
+	
+	midi->rmidi = rmidi;
+	return 0;
+}
+
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/ca0106/ca_midi.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/ca0106/ca_midi.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/ca0106/ca_midi.h	(revision 91)
@@ -0,0 +1,66 @@
+/* 
+ *  Copyright 10/16/2005 Tilman Kranz <tilde@tk-sls.de>
+ *  Creative Audio MIDI, for the CA0106 Driver
+ *  Version: 0.0.1
+ *
+ *  Changelog:
+ *    See ca_midi.c
+ *
+ *   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<linux/spinlock.h>
+#include<sound/rawmidi.h>
+#include<sound/mpu401.h>
+
+#define CA_MIDI_MODE_INPUT	MPU401_MODE_INPUT
+#define CA_MIDI_MODE_OUTPUT	MPU401_MODE_OUTPUT
+
+struct snd_ca_midi {
+
+	struct snd_rawmidi *rmidi;
+	struct snd_rawmidi_substream *substream_input;
+	struct snd_rawmidi_substream *substream_output;
+
+	void *dev_id;
+
+	spinlock_t input_lock;
+	spinlock_t output_lock;
+	spinlock_t open_lock;
+
+	unsigned int channel;
+
+	unsigned int midi_mode;
+	int port;
+	int tx_enable, rx_enable;
+	int ipr_tx, ipr_rx;            
+	
+	int input_avail, output_ready;
+	int ack, reset, enter_uart;
+
+	void (*interrupt)(struct snd_ca_midi *midi, unsigned int status);
+	void (*interrupt_enable)(struct snd_ca_midi *midi, int intr);
+	void (*interrupt_disable)(struct snd_ca_midi *midi, int intr);
+
+	unsigned char (*read)(struct snd_ca_midi *midi, int idx);
+	void (*write)(struct snd_ca_midi *midi, int data, int idx);
+
+	/* get info from dev_id */
+	struct snd_card *(*get_dev_id_card)(void *dev_id);
+	int (*get_dev_id_port)(void *dev_id);
+};
+
+int ca_midi_init(void *card, struct snd_ca_midi *midi, int device, char *name);
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/ca0106/makefile.os2
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/ca0106/makefile.os2	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/ca0106/makefile.os2	(revision 91)
@@ -0,0 +1,51 @@
+CINCLUDES = ..\..\..\include;..\..\include;$(%WATCOM)\H
+AINCLUDES = $(DDK_BASE)\H
+
+CDEFINES = -D__KERNEL__ -DMODULE -dTARGET_OS2 -dALSA_BUILD -D__ISAPNP__ -DCONFIG_ISAPNP -D__i386_
+
+32BIT=1
+!include ..\..\..\..\makefile.inc
+
+
+#===================================================================
+#
+#   List of source files
+#
+#===================================================================
+FILE1    = ca0106_main.obj ca0106_proc.obj ca0106_mixer.obj
+FILE2    = ca_midi.obj
+FILE3    = 
+FILE4    = 
+FILE5    = 
+FILE6    = 
+FILE7    = 
+FILE8    = 
+FILELAST =
+FILES    = $(FILE0) $(FILE1) $(FILE2) $(FILE3) $(FILE4) $(FILE5) $(FILE6) $(FILE7) $(FILE8) $(FILE9) $(FILE10) $(FILE11) $(FILE12)
+
+TARGET   = ca0106
+
+LNKFILE  = $(OBJDIR)\$(TARGET).lnk
+
+
+#===================================================================
+#
+#   Specific dependencies
+#
+#===================================================================
+all: $(OBJDIR)\$(TARGET).lib 
+
+
+$(LNKFILE): makefile.os2
+    @%create $(LNKFILE)
+    @for %f in ($(FILES)) do @%append $(LNKFILE) -+$(OBJDIR)\%f
+
+$(OBJDIR)\$(TARGET).lib: $(LNKFILE) $(FILES)
+    $(LIB) $(OBJDIR)\$(TARGET).lib @$(LNKFILE)
+    copy $(OBJDIR)\$(TARGET).lib $(ALSA_LIB) 
+
+clean: .SYMBOLIC
+    -@rm  *.err 2>nul
+    @cd $(OBJDIR)
+    -rm *.obj *.lnk *.sys *.sym *.lst *.map *.wmap *.lib 2>nul
+    @cd ..\..
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/cmipci.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/cmipci.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/cmipci.c	(revision 91)
@@ -0,0 +1,3185 @@
+/*
+ * Driver for C-Media CMI8338 and 8738 PCI soundcards.
+ * Copyright (c) 2000 by 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
+ */
+
+/* Does not work. Warning may block system in capture mode */
+/* #define USE_VAR48KRATE */
+
+#include <sound/driver.h>
+#include <asm/io.h>
+//#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+//#include <linux/pci.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/rawmidi.h>
+#include <sound/mpu401.h>
+#include <sound/opl3.h>
+#include <sound/sb.h>
+#include <sound/asoundef.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
+MODULE_DESCRIPTION("C-Media CMI8x38 PCI");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{C-Media,CMI8738},"
+               "{C-Media,CMI8738B},"
+               "{C-Media,CMI8338A},"
+               "{C-Media,CMI8338B}}");
+
+extern int midi_port;
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;	/* Enable switches */
+//MPU disabled for now until it's fully supported by the MMPM2 driver
+static long mpu_port[SNDRV_CARDS] = {-1,-1,-1,-1,-1,-1,-1,-1};
+static long fm_port[SNDRV_CARDS] = {0x388, -1,-1,-1,-1,-1,-1,-1};
+
+static int soft_ac3[SNDRV_CARDS] = {0,1,1,1,1,1,1,1};
+
+MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(index, "Index value for C-Media PCI soundcard.");
+MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC);
+MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+MODULE_PARM_DESC(id, "ID string for C-Media PCI soundcard.");
+MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC);
+MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(enable, "Enable C-Media PCI soundcard.");
+MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(mpu_port, "MPU-401 port.");
+MODULE_PARM_SYNTAX(mpu_port, SNDRV_ENABLED ",allows:{{-1},{0x330},{0x320},{0x310},{0x300}},dialog:list");
+MODULE_PARM(fm_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(fm_port, "FM port.");
+MODULE_PARM_SYNTAX(fm_port, SNDRV_ENABLED ",allows:{{-1},{0x388},{0x3c8},{0x3e0},{0x3e8}},dialog:list");
+#ifdef DO_SOFT_AC3
+MODULE_PARM(soft_ac3, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(soft_ac3, "Sofware-conversion of raw SPDIF packets (model 033 only).");
+MODULE_PARM_SYNTAX(soft_ac3, SNDRV_ENABLED "," SNDRV_BOOLEAN_TRUE_DESC);
+#endif
+
+#ifndef PCI_DEVICE_ID_CMEDIA_CM8738
+#define PCI_DEVICE_ID_CMEDIA_CM8738	0x0111
+#endif
+#ifndef PCI_DEVICE_ID_CMEDIA_CM8738B
+#define PCI_DEVICE_ID_CMEDIA_CM8738B	0x0112
+#endif
+
+//#define SIX_CH_HACK
+/*
+ * CM8x38 registers definition
+ */
+
+#define CM_REG_FUNCTRL0		0x00
+#define CM_RST_CH1		0x00080000
+#define CM_RST_CH0		0x00040000
+#define CM_CHEN1		0x00020000	/* ch1: enable */
+#define CM_CHEN0		0x00010000	/* ch0: enable */
+#define CM_PAUSE1		0x00000008	/* ch1: pause */
+#define CM_PAUSE0		0x00000004	/* ch0: pause */
+#define CM_CHADC1		0x00000002	/* ch1, 0:playback, 1:record */
+#define CM_CHADC0		0x00000001	/* ch0, 0:playback, 1:record */
+
+#define CM_REG_FUNCTRL1		0x04
+#define CM_ASFC_MASK		0x0000E000	/* ADC sampling frequency */
+#define CM_ASFC_SHIFT		13
+#define CM_DSFC_MASK		0x00001C00	/* DAC sampling frequency */
+#define CM_DSFC_SHIFT		10
+#define CM_SPDF_1		0x00000200	/* SPDIF IN/OUT at channel B */
+#define CM_SPDF_0		0x00000100	/* SPDIF OUT only channel A */
+#define CM_SPDFLOOP		0x00000080	/* ext. SPDIIF/OUT -> IN loopback */
+#define CM_SPDO2DAC		0x00000040	/* SPDIF/OUT can be heard from internal DAC */
+#define CM_INTRM		0x00000020	/* master control block (MCB) interrupt enabled */
+#define CM_BREQ			0x00000010	/* bus master enabled */
+#define CM_VOICE_EN		0x00000008	/* legacy voice (SB16,FM) */
+#define CM_UART_EN		0x00000004	/* UART */
+#define CM_JYSTK_EN		0x00000002	/* joy stick */
+
+#define CM_REG_CHFORMAT		0x08
+
+#define CM_CHB3D5C		0x80000000	/* 5,6 channels */
+#define CM_CHB3D		0x20000000	/* 4 channels */
+
+#define CM_CHIP_MASK1		0x1f000000
+#define CM_CHIP_037		0x01000000
+
+#define CM_SPDIF_SELECT1	0x00080000	/* for model <= 037 ? */
+#define CM_AC3EN1		0x00100000	/* enable AC3: model 037 */
+#define CM_SPD24SEL		0x00020000	/* 24bit spdif: model 037 */
+/* #define CM_SPDIF_INVERSE	0x00010000 */ /* ??? */
+
+#define CM_ADCBITLEN_MASK	0x0000C000
+#define CM_ADCBITLEN_16		0x00000000
+#define CM_ADCBITLEN_15		0x00004000
+#define CM_ADCBITLEN_14		0x00008000
+#define CM_ADCBITLEN_13		0x0000C000
+
+#define CM_ADCDACLEN_MASK	0x00003000
+#define CM_ADCDACLEN_060	0x00000000
+#define CM_ADCDACLEN_066	0x00001000
+#define CM_ADCDACLEN_130	0x00002000
+#define CM_ADCDACLEN_280	0x00003000
+
+#define CM_CH1_SRATE_176K	0x00000800
+#define CM_CH1_SRATE_88K	0x00000400
+#define CM_CH0_SRATE_176K	0x00000200
+#define CM_CH0_SRATE_88K	0x00000100
+
+#define CM_SPDIF_INVERSE2	0x00000080	/* model 055? */
+
+#define CM_CH1FMT_MASK		0x0000000C
+#define CM_CH1FMT_SHIFT		2
+#define CM_CH0FMT_MASK		0x00000003
+#define CM_CH0FMT_SHIFT		0
+
+#define CM_REG_INT_HLDCLR	0x0C
+#define CM_CHIP_MASK2		0xff000000
+#define CM_CHIP_039		0x04000000
+#define CM_CHIP_039_6CH		0x01000000
+#define CM_CHIP_055             0x08000000
+#define CM_CHIP_8768            0x20000000
+#define CM_TDMA_INT_EN		0x00040000
+#define CM_CH1_INT_EN		0x00020000
+#define CM_CH0_INT_EN		0x00010000
+#define CM_INT_HOLD		0x00000002
+#define CM_INT_CLEAR		0x00000001
+
+#define CM_REG_INT_STATUS	0x10
+#define CM_INTR			0x80000000
+#define CM_VCO			0x08000000	/* Voice Control? CMI8738 */
+#define CM_MCBINT		0x04000000	/* Master Control Block abort cond.? */
+#define CM_UARTINT		0x00010000
+#define CM_LTDMAINT		0x00008000
+#define CM_HTDMAINT		0x00004000
+#define CM_XDO46		0x00000080	/* Modell 033? Direct programming EEPROM (read data register) */
+#define CM_LHBTOG		0x00000040	/* High/Low status from DMA ctrl register */
+#define CM_LEG_HDMA		0x00000020	/* Legacy is in High DMA channel */
+#define CM_LEG_STEREO		0x00000010	/* Legacy is in Stereo mode */
+#define CM_CH1BUSY		0x00000008
+#define CM_CH0BUSY		0x00000004
+#define CM_CHINT1		0x00000002
+#define CM_CHINT0		0x00000001
+
+#define CM_REG_LEGACY_CTRL	0x14
+#define CM_NXCHG		0x80000000	/* h/w multi channels? */
+#define CM_VMPU_MASK		0x60000000	/* MPU401 i/o port address */
+#define CM_VMPU_330		0x00000000
+#define CM_VMPU_320		0x20000000
+#define CM_VMPU_310		0x40000000
+#define CM_VMPU_300		0x60000000
+#define CM_VSBSEL_MASK		0x0C000000	/* SB16 base address */
+#define CM_VSBSEL_220		0x00000000
+#define CM_VSBSEL_240		0x04000000
+#define CM_VSBSEL_260		0x08000000
+#define CM_VSBSEL_280		0x0C000000
+#define CM_FMSEL_MASK		0x03000000	/* FM OPL3 base address */
+#define CM_FMSEL_388		0x00000000
+#define CM_FMSEL_3C8		0x01000000
+#define CM_FMSEL_3E0		0x02000000
+#define CM_FMSEL_3E8		0x03000000
+#define CM_ENSPDOUT		0x00800000	/* enable XPDIF/OUT to I/O interface */
+#define CM_SPDCOPYRHT		0x00400000	/* set copyright spdif in/out */
+#define CM_DAC2SPDO		0x00200000	/* enable wave+fm_midi -> SPDIF/OUT */
+#define CM_SETRETRY		0x00010000	/* 0: legacy i/o wait (default), 1: legacy i/o bus retry */
+#define CM_CHB3D6C		0x00008000	/* 5.1 channels support */
+#define CM_LINE_AS_BASS		0x00006000	/* use line-in as bass */
+
+#define CM_REG_MISC_CTRL	0x18
+#define CM_PWD			0x80000000
+#define CM_RESET		0x40000000
+#define CM_SFIL_MASK		0x30000000
+#define CM_TXVX			0x08000000
+#define CM_N4SPK3D		0x04000000	/* 4ch output */
+#define CM_SPDO5V		0x02000000	/* 5V spdif output (1 = 0.5v (coax)) */
+#define CM_SPDIF48K		0x01000000	/* write */
+#define CM_SPATUS48K		0x01000000	/* read */
+#define CM_ENDBDAC		0x00800000	/* enable dual dac */
+#define CM_XCHGDAC		0x00400000	/* 0: front=ch0, 1: front=ch1 */
+#define CM_SPD32SEL		0x00200000	/* 0: 16bit SPDIF, 1: 32bit */
+#define CM_SPDFLOOPI		0x00100000	/* int. SPDIF-IN -> int. OUT */
+#define CM_FM_EN		0x00080000	/* enalbe FM */
+#define CM_AC3EN2		0x00040000	/* enable AC3: model 039 */
+#define CM_VIDWPDSB		0x00010000
+#define CM_SPDF_AC97		0x00008000	/* 0: SPDIF/OUT 44.1K, 1: 48K */
+#define CM_MASK_EN		0x00004000
+#define CM_VIDWPPRT		0x00002000
+#define CM_SFILENB		0x00001000
+#define CM_MMODE_MASK		0x00000E00
+#define CM_SPDIF_SELECT2	0x00000100	/* for model > 039 ? */
+#define CM_ENCENTER		0x00000080
+#define CM_FLINKON		0x00000040
+#define CM_FLINKOFF		0x00000020
+#define CM_MIDSMP		0x00000010
+#define CM_UPDDMA_MASK		0x0000000C
+#define CM_TWAIT_MASK		0x00000003
+
+/* byte */
+#define CM_REG_MIXER0		0x20
+
+#define CM_REG_SB16_DATA	0x22
+#define CM_REG_SB16_ADDR	0x23
+
+#define CM_REFFREQ_XIN		(315*1000*1000)/22	/* 14.31818 Mhz reference clock frequency pin XIN */
+#define CM_ADCMULT_XIN		512			/* Guessed (487 best for 44.1kHz, not for 88/176kHz) */
+#define CM_TOLERANCE_RATE	0.001			/* Tolerance sample rate pitch (1000ppm) */
+#define CM_MAXIMUM_RATE		80000000		/* Note more than 80MHz */
+
+#define CM_REG_MIXER1		0x24
+#define CM_FMMUTE		0x80	/* mute FM */
+#define CM_FMMUTE_SHIFT		7
+#define CM_WSMUTE		0x40	/* mute PCM */
+#define CM_WSMUTE_SHIFT		6
+#define CM_SPK4			0x20	/* lin-in -> rear line out */
+#define CM_SPK4_SHIFT		5
+#define CM_REAR2FRONT		0x10	/* exchange rear/front */
+#define CM_REAR2FRONT_SHIFT	4
+#define CM_WAVEINL		0x08	/* digital wave rec. left chan */
+#define CM_WAVEINL_SHIFT	3
+#define CM_WAVEINR		0x04	/* digical wave rec. right */
+#define CM_WAVEINR_SHIFT	2
+#define CM_X3DEN		0x02	/* 3D surround enable */
+#define CM_X3DEN_SHIFT		1
+#define CM_CDPLAY		0x01	/* enable SPDIF/IN PCM -> DAC */
+#define CM_CDPLAY_SHIFT		0
+
+#define CM_REG_MIXER2		0x25
+#define CM_RAUXREN		0x80	/* AUX right capture */
+#define CM_RAUXREN_SHIFT	7
+#define CM_RAUXLEN		0x40	/* AUX left capture */
+#define CM_RAUXLEN_SHIFT	6
+#define CM_VAUXRM		0x20	/* AUX right mute */
+#define CM_VAUXRM_SHIFT		5
+#define CM_VAUXLM		0x10	/* AUX left mute */
+#define CM_VAUXLM_SHIFT		4
+#define CM_VADMIC_MASK		0x0e	/* mic gain level (0-3) << 1 */
+#define CM_VADMIC_SHIFT		1
+#define CM_MICGAINZ		0x01	/* mic boost */
+#define CM_MICGAINZ_SHIFT	0
+
+#define CM_REG_MIXER3           0x24
+#define CM_REG_AUX_VOL		0x26
+#define CM_VAUXL_MASK		0xf0
+#define CM_VAUXR_MASK		0x0f
+
+#define CM_REG_MISC		0x27
+#define CM_XGPO1		0x20
+// #define CM_XGPBIO		0x04
+#define CM_MIC_CENTER_LFE	0x04	/* mic as center/lfe out? (model 039 or later?) */
+#define CM_SPDIF_INVERSE	0x04	/* spdif input phase inverse (model 037) */
+#define CM_SPDVALID		0x02	/* spdif input valid check */
+#define CM_DMAUTO		0x01
+
+#define CM_REG_AC97		0x28	/* hmmm.. do we have ac97 link? */
+/*
+ * For CMI-8338 (0x28 - 0x2b) .. is this valid for CMI-8738
+ * or identical with AC97 codec?
+ */
+#define CM_REG_EXTERN_CODEC	CM_REG_AC97
+
+/*
+ * MPU401 pci port index address 0x40 - 0x4f (CMI-8738 spec ver. 0.6)
+ */
+#define CM_REG_MPU_PCI		0x40
+
+/*
+ * FM pci port index address 0x50 - 0x5f (CMI-8738 spec ver. 0.6)
+ */
+#define CM_REG_FM_PCI		0x50
+
+/*
+ * access from SB-mixer port
+ */
+#define CM_REG_EXTENT_IND	0xf0
+#define CM_VPHONE_MASK		0xe0	/* Phone volume control (0-3) << 5 */
+#define CM_VPHONE_SHIFT		5
+#define CM_VPHOM		0x10	/* Phone mute control */
+#define CM_VSPKM		0x08	/* Speaker mute control, default high */
+#define CM_RLOOPREN		0x04    /* Rec. R-channel enable */
+#define CM_RLOOPLEN		0x02	/* Rec. L-channel enable */
+#define CM_VADMIC3              0x01    /* Mic record boost */
+
+/*
+ * CMI-8338 spec ver 0.5 (this is not valid for CMI-8738):
+ * the 8 registers 0xf8 - 0xff are used for programming m/n counter by the PLL
+ * unit (readonly?).
+ */
+#define CM_REG_PLL		0xf8
+
+/*
+ * extended registers
+ */
+#define CM_REG_CH0_FRAME1	0x80	/* base address */
+#define CM_REG_CH0_FRAME2	0x84
+#define CM_REG_CH1_FRAME1	0x88	/* 0-15: count of samples at bus master; buffer size */
+#define CM_REG_CH1_FRAME2	0x8C	/* 16-31: count of samples at codec; fragment size */
+#define CM_REG_EXT_MISC         0x90
+#define CM_REG_MISC_CTRL_8768   0x92    /* reg. name the same as 0x18 */
+#define CM_CHB3D8C              0x20    /* 7.1 channels support */
+#define CM_SPD32FMT             0x10    /* SPDIF/IN 32k */
+#define CM_ADC2SPDIF            0x08    /* ADC output to SPDIF/OUT */
+#define CM_SHAREADC             0x04    /* DAC in ADC as Center/LFE */
+#define CM_REALTCMP             0x02    /* monitor the CMPL/CMPR of ADC */
+#define CM_INVLRCK              0x01    /* invert ZVPORT's LRCK */
+/*
+ * size of i/o region
+ */
+#define CM_EXTENT_CODEC	  0x100
+#define CM_EXTENT_MIDI	  0x2
+#define CM_EXTENT_SYNTH	  0x4
+
+/*
+ * pci ids
+ */
+#ifndef PCI_VENDOR_ID_CMEDIA
+#define PCI_VENDOR_ID_CMEDIA         0x13F6
+#endif
+#ifndef PCI_DEVICE_ID_CMEDIA_CM8338A
+#define PCI_DEVICE_ID_CMEDIA_CM8338A 0x0100
+#endif
+#ifndef PCI_DEVICE_ID_CMEDIA_CM8338B
+#define PCI_DEVICE_ID_CMEDIA_CM8338B 0x0101
+#endif
+#ifndef PCI_DEVICE_ID_CMEDIA_CM8738
+#define PCI_DEVICE_ID_CMEDIA_CM8738  0x0111
+#endif
+#ifndef PCI_DEVICE_ID_CMEDIA_CM8738B
+#define PCI_DEVICE_ID_CMEDIA_CM8738B 0x0112
+#endif
+
+/*
+ * channels for playback / capture
+ */
+#define CM_CH_PLAY	0
+#define CM_CH_CAPT	1
+
+/*
+ * flags to check device open/close
+ */
+#define CM_OPEN_NONE	0
+#define CM_OPEN_CH_MASK	0x01
+#define CM_OPEN_DAC	0x10
+#define CM_OPEN_ADC	0x20
+#define CM_OPEN_SPDIF	0x40
+#define CM_OPEN_MCHAN	0x80
+#define CM_OPEN_PLAYBACK	(CM_CH_PLAY | CM_OPEN_DAC)
+#define CM_OPEN_PLAYBACK2	(CM_CH_CAPT | CM_OPEN_DAC)
+#define CM_OPEN_PLAYBACK_MULTI	(CM_CH_PLAY | CM_OPEN_DAC | CM_OPEN_MCHAN)
+#define CM_OPEN_CAPTURE		(CM_CH_CAPT | CM_OPEN_ADC)
+#define CM_OPEN_SPDIF_PLAYBACK	(CM_CH_PLAY | CM_OPEN_DAC | CM_OPEN_SPDIF)
+#define CM_OPEN_SPDIF_CAPTURE	(CM_CH_CAPT | CM_OPEN_ADC | CM_OPEN_SPDIF)
+
+
+#if CM_CH_PLAY == 1
+#define CM_PLAYBACK_SRATE_176K	CM_CH1_SRATE_176K
+#define CM_PLAYBACK_SPDF	CM_SPDF_1
+#define CM_CAPTURE_SPDF		CM_SPDF_0
+#else
+#define CM_PLAYBACK_SRATE_176K CM_CH0_SRATE_176K
+#define CM_PLAYBACK_SPDF	CM_SPDF_0
+#define CM_CAPTURE_SPDF		CM_SPDF_1
+#endif
+
+
+/*
+ * driver data
+ */
+
+struct cmipci_pcm {
+    struct snd_pcm_substream *substream;
+    int running;		/* dac/adc running? */
+    unsigned int dma_size;	/* in frames */
+    unsigned int period_size;	/* in frames */
+    unsigned int offset;	/* physical address of the buffer */
+    unsigned int fmt;	/* format bits */
+    int ch;			/* channel (0/1) */
+    unsigned int is_dac;		/* is dac? */
+    int bytes_per_frame;
+    int shift;
+};
+
+/* mixer elements toggled/resumed during ac3 playback */
+struct cmipci_mixer_auto_switches {
+    const char *name;	/* switch to toggle */
+    int toggle_on;		/* value to change when ac3 mode */
+};
+static const struct cmipci_mixer_auto_switches cm_saved_mixer[] = {
+    {"PCM Playback Switch", 0},
+    {"IEC958 Output Switch", 1},
+    {"IEC958 Mix Analog", 0},
+    // {"IEC958 Out To DAC", 1}, // no longer used
+    {"IEC958 Loop", 0},
+};
+
+#define CM_SAVED_MIXERS		ARRAY_SIZE(cm_saved_mixer)
+
+struct cmipci {
+    struct snd_card *card;
+
+    struct pci_dev *pci;
+    unsigned int device;	/* device ID */
+    int irq;
+
+    unsigned long iobase;
+    unsigned int ctrl;	/* FUNCTRL0 current value */
+
+    struct snd_pcm *pcm;            /* DAC/ADC PCM */
+    struct snd_pcm *pcm2;   /* 2nd DAC */
+    struct snd_pcm *pcm_spdif;      /* SPDIF */
+
+    int chip_version;
+    int max_channels;
+    unsigned int has_dual_dac: 1;
+    unsigned int can_ac3_sw: 1;
+    unsigned int can_ac3_hw: 1;
+    unsigned int can_multi_ch: 1;
+    unsigned int do_soft_ac3: 1;
+
+    unsigned int spdif_playback_avail: 1;	/* spdif ready? */
+    unsigned int spdif_playback_enabled: 1;	/* spdif switch enabled? */
+    int spdif_counter;	/* for software AC3 */
+
+    unsigned int dig_status;
+    unsigned int dig_pcm_status;
+
+    struct snd_pcm_hardware_t *hw_info[3]; /* for playbacks */
+
+    int opened[2];	/* open mode */
+    struct semaphore open_mutex;
+
+    int mixer_insensitive: 1;
+    struct snd_kcontrol *mixer_res_ctl[CM_SAVED_MIXERS];
+    int mixer_res_status[CM_SAVED_MIXERS];
+
+    struct cmipci_pcm channel[2];	/* ch0 - DAC, ch1 - ADC or 2nd DAC */
+
+    /* external MIDI */
+    struct snd_rawmidi *rmidi;
+
+    spinlock_t reg_lock;
+
+#ifdef CONFIG_PM
+    unsigned int saved_regs[0x20];
+    unsigned char saved_mixers[0x20];
+#endif
+};
+
+
+/* read/write operations for dword register */
+static inline void snd_cmipci_write(struct cmipci *cm, unsigned int cmd, unsigned int data)
+{
+    outl(data, cm->iobase + cmd);
+}
+
+static inline unsigned int snd_cmipci_read(struct cmipci *cm, unsigned int cmd)
+{
+    return inl(cm->iobase + cmd);
+}
+
+/* read/write operations for word register */
+static inline void snd_cmipci_write_w(struct cmipci *cm, unsigned int cmd, unsigned short data)
+{
+    outw(data, cm->iobase + cmd);
+}
+
+static inline unsigned short snd_cmipci_read_w(struct cmipci *cm, unsigned int cmd)
+{
+    return inw(cm->iobase + cmd);
+}
+
+/* read/write operations for byte register */
+static inline void snd_cmipci_write_b(struct cmipci *cm, unsigned int cmd, unsigned char data)
+{
+    outb(data, cm->iobase + cmd);
+}
+
+static inline unsigned char snd_cmipci_read_b(struct cmipci *cm, unsigned int cmd)
+{
+    return inb(cm->iobase + cmd);
+}
+
+/* bit operations for dword register */
+static int snd_cmipci_set_bit(struct cmipci *cm, unsigned int cmd, unsigned int flag)
+{
+	unsigned int val, oval;
+	val = oval = inl(cm->iobase + cmd);
+	val |= flag;
+	if (val == oval)
+		return 0;
+	outl(val, cm->iobase + cmd);
+	return 1;
+}
+
+static int snd_cmipci_clear_bit(struct cmipci *cm, unsigned int cmd, unsigned int flag)
+{
+	unsigned int val, oval;
+	val = oval = inl(cm->iobase + cmd);
+	val &= ~flag;
+	if (val == oval)
+		return 0;
+	outl(val, cm->iobase + cmd);
+	return 1;
+}
+
+/* bit operations for byte register */
+static int snd_cmipci_set_bit_b(struct cmipci *cm, unsigned int cmd, unsigned char flag)
+{
+	unsigned char val, oval;
+	val = oval = inb(cm->iobase + cmd);
+	val |= flag;
+	if (val == oval)
+		return 0;
+	outb(val, cm->iobase + cmd);
+	return 1;
+}
+
+static int snd_cmipci_clear_bit_b(struct cmipci *cm, unsigned int cmd, unsigned char flag)
+{
+	unsigned char val, oval;
+	val = oval = inb(cm->iobase + cmd);
+	val &= ~flag;
+	if (val == oval)
+		return 0;
+	outb(val, cm->iobase + cmd);
+	return 1;
+}
+
+/*
+ * PCM interface
+ */
+
+/*
+ * calculate frequency
+ */
+
+static unsigned int rates[] = { 5512, 11025, 22050, 44100, 8000, 16000, 32000, 48000 };
+
+static unsigned int snd_cmipci_rate_freq(unsigned int rate)
+{
+    unsigned int i;
+    for (i = 0; i < ARRAY_SIZE(rates); i++) {
+        if (rates[i] == rate)
+            return i;
+    }
+    snd_BUG();
+    return 0;
+}
+
+#ifdef USE_VAR48KRATE
+/*
+ * Determine PLL values for frequency setup, maybe the CMI8338 (CMI8738???)
+ * does it this way .. maybe not.  Never get any information from C-Media about
+ * that <werner@suse.de>.
+ */
+static int snd_cmipci_pll_rmn(unsigned int rate, unsigned int adcmult, int *r, int *m, int *n)
+{
+    unsigned int delta, tolerance;
+    int xm, xn, xr;
+
+    for (*r = 0; rate < CM_MAXIMUM_RATE/adcmult; *r += (1<<5))
+        rate <<= 1;
+    *n = -1;
+    if (*r > 0xff)
+        goto out;
+    tolerance = rate*CM_TOLERANCE_RATE;
+
+    for (xn = (1+2); xn < (0x1f+2); xn++) {
+        for (xm = (1+2); xm < (0xff+2); xm++) {
+            xr = ((CM_REFFREQ_XIN/adcmult) * xm) / xn;
+
+            if (xr < rate)
+                delta = rate - xr;
+            else
+                delta = xr - rate;
+
+            /*
+             * If we found one, remember this,
+             * and try to find a closer one
+             */
+            if (delta < tolerance) {
+                tolerance = delta;
+                *m = xm - 2;
+                *n = xn - 2;
+            }
+        }
+    }
+out:
+    return (*n > -1);
+}
+
+/*
+ * Program pll register bits, I assume that the 8 registers 0xf8 upto 0xff
+ * are mapped onto the 8 ADC/DAC sampling frequency which can be choosen
+ * at the register CM_REG_FUNCTRL1 (0x04).
+ * Problem: other ways are also possible (any information about that?)
+ */
+static void snd_cmipci_set_pll(struct cmipci *cm, unsigned int rate, unsigned int slot)
+{
+    unsigned int reg = CM_REG_PLL + slot;
+    /*
+     * Guess that this programs at reg. 0x04 the pos 15:13/12:10
+     * for DSFC/ASFC (000 upto 111).
+     */
+
+    /* FIXME: Init (Do we've to set an other register first before programming?) */
+
+    /* FIXME: Is this correct? Or shouldn't the m/n/r values be used for that? */
+    snd_cmipci_write_b(cm, reg, rate>>8);
+    snd_cmipci_write_b(cm, reg, rate&0xff);
+
+    /* FIXME: Setup (Do we've to set an other register first to enable this?) */
+}
+#endif /* USE_VAR48KRATE */
+
+static int snd_cmipci_hw_params(struct snd_pcm_substream *substream,
+                                struct snd_pcm_hw_params *hw_params)
+{
+    return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+static int snd_cmipci_playback2_hw_params(struct snd_pcm_substream *substream,
+                                          struct snd_pcm_hw_params *hw_params)
+{
+    struct cmipci *cm = snd_pcm_substream_chip(substream);
+    if (params_channels(hw_params) > 2) {
+        down(&cm->open_mutex);
+#if 0 // vladest
+        if (cm->opened[CM_CH_PLAY]) {
+            up(&cm->open_mutex);
+            return -EBUSY;
+        }
+#endif
+        /* reserve the channel A */
+        cm->opened[CM_CH_PLAY] = CM_OPEN_PLAYBACK_MULTI;
+        up(&cm->open_mutex);
+    }
+    return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+static void snd_cmipci_ch_reset(struct cmipci *cm, int ch)
+{
+    int reset = CM_RST_CH0 << (cm->channel[ch].ch);
+    snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl | reset);
+    snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl & ~reset);
+    udelay(10);
+}
+
+static int snd_cmipci_hw_free(struct snd_pcm_substream *substream)
+{
+    return snd_pcm_lib_free_pages(substream);
+}
+
+
+/*
+ */
+
+static unsigned int hw_channels[] = {1, 2, 4, 5, 6, 8};
+static struct snd_pcm_hw_constraint_list hw_constraints_channels_4 = {
+    .count = 3,
+    .list = hw_channels,
+    .mask = 0,
+};
+static struct snd_pcm_hw_constraint_list hw_constraints_channels_6 = {
+	.count = 5,
+	.list = hw_channels,
+	.mask = 0,
+};
+static struct snd_pcm_hw_constraint_list hw_constraints_channels_8 = {
+    .count = 6,
+	.list = hw_channels,
+	.mask = 0,
+};
+
+static int set_dac_channels(struct cmipci *cm, struct cmipci_pcm *rec, int channels)
+{
+    if (channels > 2) {
+        if (! cm->can_multi_ch)
+            return -EINVAL;
+        if (rec->fmt != 0x03) /* stereo 16bit only */
+            return -EINVAL;
+
+        spin_lock_irq(&cm->reg_lock);
+        snd_cmipci_set_bit(cm, CM_REG_LEGACY_CTRL, CM_NXCHG);
+        snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_XCHGDAC);
+        if (channels > 4) {
+            snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_CHB3D);
+            snd_cmipci_set_bit(cm, CM_REG_CHFORMAT, CM_CHB3D5C);
+        } else {
+            snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_CHB3D5C);
+            snd_cmipci_set_bit(cm, CM_REG_CHFORMAT, CM_CHB3D);
+        }
+        if (channels >= 6) {
+            snd_cmipci_set_bit(cm, CM_REG_LEGACY_CTRL, CM_CHB3D6C);
+            snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_ENCENTER);
+        } else {
+            snd_cmipci_clear_bit(cm, CM_REG_LEGACY_CTRL, CM_CHB3D6C);
+            snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_ENCENTER);
+        }
+        if (cm->chip_version == 68) {
+            if (channels == 8) {
+                snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL_8768, CM_CHB3D8C);
+            } else {
+                snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL_8768, CM_CHB3D8C);
+            }
+        }
+        spin_unlock_irq(&cm->reg_lock);
+
+    } else {
+        if (cm->can_multi_ch) {
+            spin_lock_irq(&cm->reg_lock);
+            snd_cmipci_clear_bit(cm, CM_REG_LEGACY_CTRL, CM_NXCHG);
+            snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_CHB3D);
+            snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_CHB3D5C);
+            snd_cmipci_clear_bit(cm, CM_REG_LEGACY_CTRL, CM_CHB3D6C);
+            snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_ENCENTER);
+            snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_XCHGDAC);
+            spin_unlock_irq(&cm->reg_lock);
+        }
+    }
+    return 0;
+}
+
+
+/*
+ * prepare playback/capture channel
+ * channel to be used must have been set in rec->ch.
+ */
+static int snd_cmipci_pcm_prepare(struct cmipci *cm, struct cmipci_pcm *rec,
+                                  struct snd_pcm_substream *substream)
+{
+    unsigned int reg, freq, val;
+    struct snd_pcm_runtime *runtime = substream->runtime;
+
+    rec->fmt = 0;
+    rec->shift = 0;
+    if (snd_pcm_format_width(runtime->format) >= 16) {
+        rec->fmt |= 0x02;
+        if (snd_pcm_format_width(runtime->format) > 16)
+            rec->shift++; /* 24/32bit */
+    }
+    if (runtime->channels > 1)
+        rec->fmt |= 0x01;
+    if (rec->is_dac && set_dac_channels(cm, rec, runtime->channels) < 0) {
+        snd_printd("cannot set dac channels\n");
+        return -EINVAL;
+    }
+
+    rec->offset = runtime->dma_addr;
+    /* buffer and period sizes in frame */
+    rec->dma_size = runtime->buffer_size << rec->shift;
+    rec->period_size = runtime->period_size << rec->shift;
+    if (runtime->channels > 2) {
+        /* multi-channels */
+        rec->dma_size = (rec->dma_size * runtime->channels) / 2;
+        rec->period_size = (rec->period_size * runtime->channels) / 2;
+    }
+
+    spin_lock_irq(&cm->reg_lock);
+
+    /* set buffer address */
+    reg = rec->ch ? CM_REG_CH1_FRAME1 : CM_REG_CH0_FRAME1;
+    snd_cmipci_write(cm, reg, rec->offset);
+    /* program sample counts */
+    reg = rec->ch ? CM_REG_CH1_FRAME2 : CM_REG_CH0_FRAME2;
+    snd_cmipci_write_w(cm, reg, rec->dma_size - 1);
+    snd_cmipci_write_w(cm, reg + 2, rec->period_size - 1);
+
+    /* set adc/dac flag */
+    val = rec->ch ? CM_CHADC1 : CM_CHADC0;
+    if (rec->is_dac)
+        cm->ctrl &= ~val;
+    else
+        cm->ctrl |= val;
+    snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl);
+    //snd_printd("cmipci: functrl0 = %08x\n", cm->ctrl);
+
+    /* set sample rate */
+    freq = snd_cmipci_rate_freq(runtime->rate);
+    val = snd_cmipci_read(cm, CM_REG_FUNCTRL1);
+    if (rec->ch) {
+        val &= ~CM_ASFC_MASK;
+        val |= (freq << CM_ASFC_SHIFT) & CM_ASFC_MASK;
+    } else {
+        val &= ~CM_DSFC_MASK;
+        val |= (freq << CM_DSFC_SHIFT) & CM_DSFC_MASK;
+    }
+    snd_cmipci_write(cm, CM_REG_FUNCTRL1, val);
+    //snd_printd("cmipci: functrl1 = %08x\n", val);
+
+    /* set format */
+    val = snd_cmipci_read(cm, CM_REG_CHFORMAT);
+    if (rec->ch) {
+        val &= ~CM_CH1FMT_MASK;
+        val |= rec->fmt << CM_CH1FMT_SHIFT;
+    } else {
+        val &= ~CM_CH0FMT_MASK;
+        val |= rec->fmt << CM_CH0FMT_SHIFT;
+    }
+    snd_cmipci_write(cm, CM_REG_CHFORMAT, val);
+    //snd_printd("cmipci: chformat = %08x\n", val);
+
+    rec->running = 0;
+    spin_unlock_irq(&cm->reg_lock);
+
+    return 0;
+}
+
+/*
+ * PCM trigger/stop
+ */
+static int snd_cmipci_pcm_trigger(struct cmipci *cm, struct cmipci_pcm *rec,
+                                  struct snd_pcm_substream *substream, int cmd)
+{
+    unsigned int inthld, chen, reset, pause;
+    int result = 0;
+
+    inthld = CM_CH0_INT_EN << rec->ch;
+    chen = CM_CHEN0 << rec->ch;
+    reset = CM_RST_CH0 << rec->ch;
+    pause = CM_PAUSE0 << rec->ch;
+
+    spin_lock(&cm->reg_lock);
+    switch (cmd) {
+    case SNDRV_PCM_TRIGGER_START:
+        rec->running = 1;
+        /* set interrupt */
+        snd_cmipci_set_bit(cm, CM_REG_INT_HLDCLR, inthld);
+        cm->ctrl |= chen;
+        /* enable channel */
+        snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl);
+        //snd_printd("cmipci: functrl0 = %08x\n", cm->ctrl);
+        break;
+    case SNDRV_PCM_TRIGGER_STOP:
+        rec->running = 0;
+        /* disable interrupt */
+        snd_cmipci_clear_bit(cm, CM_REG_INT_HLDCLR, inthld);
+        /* reset */
+        cm->ctrl &= ~chen;
+        snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl | reset);
+        snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl & ~reset);
+        break;
+    case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+    case SNDRV_PCM_TRIGGER_SUSPEND:
+        cm->ctrl |= pause;
+        snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl);
+        break;
+    case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+    case SNDRV_PCM_TRIGGER_RESUME:
+        cm->ctrl &= ~pause;
+        snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl);
+        break;
+    default:
+        result = -EINVAL;
+        break;
+    }
+    spin_unlock(&cm->reg_lock);
+    return result;
+}
+
+/*
+ * return the current pointer
+ */
+static snd_pcm_uframes_t snd_cmipci_pcm_pointer(struct cmipci *cm, struct cmipci_pcm *rec,
+                                                struct snd_pcm_substream *substream)
+{
+    size_t ptr;
+    unsigned int reg;
+    if (!rec->running)
+        return 0;
+#if 1 // this seems better..
+    reg = rec->ch ? CM_REG_CH1_FRAME2 : CM_REG_CH0_FRAME2;
+    ptr = rec->dma_size - (snd_cmipci_read_w(cm, reg) + 1);
+    ptr >>= rec->shift;
+#else
+    reg = rec->ch ? CM_REG_CH1_FRAME1 : CM_REG_CH0_FRAME1;
+    ptr = snd_cmipci_read(cm, reg) - rec->offset;
+    ptr = bytes_to_frames(substream->runtime, ptr);
+#endif
+    if (substream->runtime->channels > 2)
+        ptr = (ptr * 2) / substream->runtime->channels;
+    return ptr;
+}
+
+/*
+ * playback
+ */
+static int snd_cmipci_playback_trigger(struct snd_pcm_substream *substream,
+                                       int cmd)
+{
+    struct cmipci *cm = snd_pcm_substream_chip(substream);
+    return snd_cmipci_pcm_trigger(cm, &cm->channel[CM_CH_PLAY], substream, cmd);
+}
+
+static snd_pcm_uframes_t snd_cmipci_playback_pointer(struct snd_pcm_substream *substream)
+{
+    struct cmipci *cm = snd_pcm_substream_chip(substream);
+    return snd_cmipci_pcm_pointer(cm, &cm->channel[CM_CH_PLAY], substream);
+}
+
+
+
+/*
+ * capture
+ */
+
+static int snd_cmipci_capture_trigger(struct snd_pcm_substream *substream,
+                                      int cmd)
+{
+    struct cmipci *cm = snd_pcm_substream_chip(substream);
+    return snd_cmipci_pcm_trigger(cm, &cm->channel[CM_CH_CAPT], substream, cmd);
+}
+
+static snd_pcm_uframes_t snd_cmipci_capture_pointer(struct snd_pcm_substream *substream)
+{
+    struct cmipci *cm = snd_pcm_substream_chip(substream);
+    return snd_cmipci_pcm_pointer(cm, &cm->channel[CM_CH_CAPT], substream);
+}
+
+
+/*
+ * hw preparation for spdif
+ */
+
+static int snd_cmipci_spdif_default_info(struct snd_kcontrol *kcontrol,
+                                         struct snd_ctl_elem_info *uinfo)
+{
+    uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+    uinfo->count = 1;
+    return 0;
+}
+
+static int snd_cmipci_spdif_default_get(struct snd_kcontrol *kcontrol,
+                                        struct snd_ctl_elem_value *ucontrol)
+{
+    struct cmipci *chip = snd_kcontrol_chip(kcontrol);
+    int i;
+
+    spin_lock_irq(&chip->reg_lock);
+    for (i = 0; i < 4; i++)
+        ucontrol->value.iec958.status[i] = (chip->dig_status >> (i * 8)) & 0xff;
+    spin_unlock_irq(&chip->reg_lock);
+    return 0;
+}
+
+static int snd_cmipci_spdif_default_put(struct snd_kcontrol *kcontrol,
+                                        struct snd_ctl_elem_value *ucontrol)
+{
+    struct cmipci *chip = snd_kcontrol_chip(kcontrol);
+    int i, change;
+    unsigned int val;
+
+    val = 0;
+    spin_lock_irq(&chip->reg_lock);
+    for (i = 0; i < 4; i++)
+        val |= (unsigned int)ucontrol->value.iec958.status[i] << (i * 8);
+    change = val != chip->dig_status;
+    chip->dig_status = val;
+    spin_unlock_irq(&chip->reg_lock);
+    return change;
+}
+
+static struct snd_kcontrol_new snd_cmipci_spdif_default __devinitdata =
+{
+    SNDRV_CTL_ELEM_IFACE_PCM,0,0,
+    SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),0,0,0,
+    snd_cmipci_spdif_default_info,
+    snd_cmipci_spdif_default_get,
+    snd_cmipci_spdif_default_put,0
+};
+
+static int snd_cmipci_spdif_mask_info(struct snd_kcontrol *kcontrol,
+                                      struct snd_ctl_elem_info *uinfo)
+{
+    uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+    uinfo->count = 1;
+    return 0;
+}
+
+static int snd_cmipci_spdif_mask_get(struct snd_kcontrol *kcontrol,
+                                     struct snd_ctl_elem_value *ucontrol)
+{
+    ucontrol->value.iec958.status[0] = 0xff;
+    ucontrol->value.iec958.status[1] = 0xff;
+    ucontrol->value.iec958.status[2] = 0xff;
+    ucontrol->value.iec958.status[3] = 0xff;
+    return 0;
+}
+
+static struct snd_kcontrol_new snd_cmipci_spdif_mask __devinitdata =
+{
+    SNDRV_CTL_ELEM_IFACE_MIXER,0,0,
+    SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK),0,
+    SNDRV_CTL_ELEM_ACCESS_READ,0,
+    snd_cmipci_spdif_mask_info,
+    snd_cmipci_spdif_mask_get,0,0
+};
+
+static int snd_cmipci_spdif_stream_info(struct snd_kcontrol *kcontrol,
+                                        struct snd_ctl_elem_info *uinfo)
+{
+    uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+    uinfo->count = 1;
+    return 0;
+}
+
+static int snd_cmipci_spdif_stream_get(struct snd_kcontrol *kcontrol,
+                                       struct snd_ctl_elem_value *ucontrol)
+{
+    struct cmipci *chip = snd_kcontrol_chip(kcontrol);
+    int i;
+
+    spin_lock_irq(&chip->reg_lock);
+    for (i = 0; i < 4; i++)
+        ucontrol->value.iec958.status[i] = (chip->dig_pcm_status >> (i * 8)) & 0xff;
+    spin_unlock_irq(&chip->reg_lock);
+    return 0;
+}
+
+static int snd_cmipci_spdif_stream_put(struct snd_kcontrol *kcontrol,
+                                       struct snd_ctl_elem_value *ucontrol)
+{
+    struct cmipci *chip = snd_kcontrol_chip(kcontrol);
+    int i, change;
+    unsigned int val;
+
+    val = 0;
+    spin_lock_irq(&chip->reg_lock);
+    for (i = 0; i < 4; i++)
+        val |= (unsigned int)ucontrol->value.iec958.status[i] << (i * 8);
+    change = val != chip->dig_pcm_status;
+    chip->dig_pcm_status = val;
+    spin_unlock_irq(&chip->reg_lock);
+    return change;
+}
+
+static struct snd_kcontrol_new snd_cmipci_spdif_stream __devinitdata =
+{
+    SNDRV_CTL_ELEM_IFACE_PCM,0,0,
+    SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM),0,
+    SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,0,
+    snd_cmipci_spdif_stream_info,
+    snd_cmipci_spdif_stream_get,
+    snd_cmipci_spdif_stream_put,0
+};
+
+/*
+ */
+
+/* save mixer setting and mute for AC3 playback */
+static int save_mixer_state(struct cmipci *cm)
+{
+    if (! cm->mixer_insensitive) {
+        struct snd_ctl_elem_value *val;
+        unsigned int i;
+        val = kmalloc(sizeof(*val), GFP_ATOMIC);
+        if (!val)
+            return -ENOMEM;
+        for (i = 0; i < CM_SAVED_MIXERS; i++) {
+            struct snd_kcontrol *ctl = cm->mixer_res_ctl[i];
+            if (ctl) {
+                int event;
+                memset(val, 0, sizeof(*val));
+                ctl->get(ctl, val);
+                cm->mixer_res_status[i] = val->value.integer.value[0];
+                val->value.integer.value[0] = cm_saved_mixer[i].toggle_on;
+                event = SNDRV_CTL_EVENT_MASK_INFO;
+                if (cm->mixer_res_status[i] != val->value.integer.value[0]) {
+                    ctl->put(ctl, val); /* toggle */
+                    event |= SNDRV_CTL_EVENT_MASK_VALUE;
+                }
+                ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+                snd_ctl_notify(cm->card, event, &ctl->id);
+            }
+        }
+        kfree(val);
+        cm->mixer_insensitive = 1;
+    }
+    return 0;
+}
+
+
+/* restore the previously saved mixer status */
+static void restore_mixer_state(struct cmipci *cm)
+{
+    if (cm->mixer_insensitive) {
+        struct snd_ctl_elem_value *val;
+        unsigned int i;
+
+        val = kmalloc(sizeof(*val), GFP_KERNEL);
+        if (!val)
+            return;
+        cm->mixer_insensitive = 0; /* at first clear this;
+        otherwise the changes will be ignored */
+        for (i = 0; i < CM_SAVED_MIXERS; i++) {
+            struct snd_kcontrol *ctl = cm->mixer_res_ctl[i];
+            if (ctl) {
+                int event;
+
+                memset(val, 0, sizeof(*val));
+                ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+                ctl->get(ctl, val);
+                event = SNDRV_CTL_EVENT_MASK_INFO;
+                if (val->value.integer.value[0] != cm->mixer_res_status[i]) {
+                    val->value.integer.value[0] = cm->mixer_res_status[i];
+                    ctl->put(ctl, val);
+                    event |= SNDRV_CTL_EVENT_MASK_VALUE;
+                }
+                snd_ctl_notify(cm->card, event, &ctl->id);
+            }
+        }
+        kfree(val);
+    }
+}
+
+/* spinlock held! */
+static void setup_ac3(struct cmipci *cm, struct snd_pcm_substream *subs, int do_ac3, int rate)
+{
+    if (do_ac3) {
+        /* AC3EN for 037 */
+        snd_cmipci_set_bit(cm, CM_REG_CHFORMAT, CM_AC3EN1);
+        /* AC3EN for 039 */
+        snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_AC3EN2);
+
+        if (cm->can_ac3_hw) {
+            /* SPD24SEL for 037, 0x02 */
+            /* SPD24SEL for 039, 0x20, but cannot be set */
+            snd_cmipci_set_bit(cm, CM_REG_CHFORMAT, CM_SPD24SEL);
+            snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_SPD32SEL);
+        } else { /* can_ac3_sw */
+            /* SPD32SEL for 037 & 039, 0x20 */
+            snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_SPD32SEL);
+            /* set 176K sample rate to fix 033 HW bug */
+            if (cm->chip_version == 33) {
+                if (rate >= 48000) {
+                    snd_cmipci_set_bit(cm, CM_REG_CHFORMAT, CM_PLAYBACK_SRATE_176K);
+                } else {
+                    snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_PLAYBACK_SRATE_176K);
+                }
+            }
+        }
+
+    } else {
+        snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_AC3EN1);
+        snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_AC3EN2);
+
+        if (cm->can_ac3_hw) {
+            /* chip model >= 37 */
+            if (snd_pcm_format_width(subs->runtime->format) > 16) {
+                snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_SPD32SEL);
+                snd_cmipci_set_bit(cm, CM_REG_CHFORMAT, CM_SPD24SEL);
+            } else {
+                snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_SPD32SEL);
+                snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_SPD24SEL);
+            }
+        } else {
+            snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_SPD32SEL);
+            snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_SPD24SEL);
+            snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_PLAYBACK_SRATE_176K);
+        }
+    }
+}
+
+static int setup_spdif_playback(struct cmipci *cm, struct snd_pcm_substream *subs, int up, int do_ac3)
+{
+    int rate, err;
+
+    rate = subs->runtime->rate;
+
+    if (up && do_ac3)
+        if ((err = save_mixer_state(cm)) < 0)
+            return err;
+
+    spin_lock_irq(&cm->reg_lock);
+    cm->spdif_playback_avail = up;
+    if (up) {
+        /* they are controlled via "IEC958 Output Switch" */
+        /* snd_cmipci_set_bit(cm, CM_REG_LEGACY_CTRL, CM_ENSPDOUT); */
+        /* snd_cmipci_set_bit(cm, CM_REG_FUNCTRL1, CM_SPDO2DAC); */
+        if (cm->spdif_playback_enabled)
+            snd_cmipci_set_bit(cm, CM_REG_FUNCTRL1, CM_PLAYBACK_SPDF);
+        setup_ac3(cm, subs, do_ac3, rate);
+
+        if (rate == 48000)
+            snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_SPDIF48K | CM_SPDF_AC97);
+        else
+            snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_SPDIF48K | CM_SPDF_AC97);
+
+    } else {
+        /* they are controlled via "IEC958 Output Switch" */
+        /* snd_cmipci_clear_bit(cm, CM_REG_LEGACY_CTRL, CM_ENSPDOUT); */
+        /* snd_cmipci_clear_bit(cm, CM_REG_FUNCTRL1, CM_SPDO2DAC); */
+        snd_cmipci_clear_bit(cm, CM_REG_FUNCTRL1, CM_PLAYBACK_SPDF);
+        setup_ac3(cm, subs, 0, 0);
+    }
+    spin_unlock_irq(&cm->reg_lock);
+    return 0;
+}
+
+
+/*
+ * preparation
+ */
+
+/* playback - enable spdif only on the certain condition */
+static int snd_cmipci_playback_prepare(struct snd_pcm_substream *substream)
+{
+    struct cmipci *cm = snd_pcm_substream_chip(substream);
+    int rate = substream->runtime->rate;
+    int err, do_spdif, do_ac3 = 0;
+    do_spdif = ((rate == 44100 || rate == 48000) &&
+                substream->runtime->format == SNDRV_PCM_FORMAT_S16_LE &&
+                substream->runtime->channels == 2);
+    if (do_spdif && cm->can_ac3_hw)
+        do_ac3 = cm->dig_pcm_status & IEC958_AES0_NONAUDIO;
+    if ((err = setup_spdif_playback(cm, substream, do_spdif, do_ac3)) < 0)
+        return err;
+    return snd_cmipci_pcm_prepare(cm, &cm->channel[CM_CH_PLAY], substream);
+}
+
+/* playback  (via device #2) - enable spdif always */
+static int snd_cmipci_playback_spdif_prepare(struct snd_pcm_substream *substream)
+{
+    struct cmipci *cm = snd_pcm_substream_chip(substream);
+    int err, do_ac3;
+    if (cm->can_ac3_hw)
+        do_ac3 = cm->dig_pcm_status & IEC958_AES0_NONAUDIO;
+    else
+        do_ac3 = 1; /* doesn't matter */
+    if ((err = setup_spdif_playback(cm, substream, 1, do_ac3)) < 0)
+        return err;
+    return snd_cmipci_pcm_prepare(cm, &cm->channel[CM_CH_PLAY], substream);
+}
+
+static int snd_cmipci_playback_hw_free(struct snd_pcm_substream *substream)
+{
+    struct cmipci *cm = snd_pcm_substream_chip(substream);
+    setup_spdif_playback(cm, substream, 0, 0);
+    restore_mixer_state(cm);
+    return snd_cmipci_hw_free(substream);
+}
+
+/* capture */
+static int snd_cmipci_capture_prepare(struct snd_pcm_substream *substream)
+{
+    struct cmipci *cm = snd_pcm_substream_chip(substream);
+    return snd_cmipci_pcm_prepare(cm, &cm->channel[CM_CH_CAPT], substream);
+}
+
+/* capture with spdif (via device #2) */
+static int snd_cmipci_capture_spdif_prepare(struct snd_pcm_substream *substream)
+{
+    struct cmipci *cm = snd_pcm_substream_chip(substream);
+
+    spin_lock_irq(&cm->reg_lock);
+    snd_cmipci_set_bit(cm, CM_REG_FUNCTRL1, CM_CAPTURE_SPDF);
+    spin_unlock_irq(&cm->reg_lock);
+
+    return snd_cmipci_pcm_prepare(cm, &cm->channel[CM_CH_CAPT], substream);
+}
+
+static int snd_cmipci_capture_spdif_hw_free(struct snd_pcm_substream *subs)
+{
+    struct cmipci *cm = snd_pcm_substream_chip(subs);
+
+    spin_lock_irq(&cm->reg_lock);
+    snd_cmipci_clear_bit(cm, CM_REG_FUNCTRL1, CM_CAPTURE_SPDF);
+    spin_unlock_irq(&cm->reg_lock);
+
+    return snd_cmipci_hw_free(subs);
+}
+
+
+/*
+ * interrupt handler
+ */
+static irqreturn_t snd_cmipci_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+    struct cmipci *cm = dev_id;
+    unsigned int status, mask = 0;
+#ifdef DEBUG
+    //        dprintf(("i"));
+#endif
+    /* fastpath out, to ease interrupt sharing */
+    status = snd_cmipci_read(cm, CM_REG_INT_STATUS);
+    if (!(status & CM_INTR))
+        return IRQ_NONE;
+
+
+    /* acknowledge interrupt */
+    spin_lock(&cm->reg_lock);
+    if (status & CM_CHINT0)
+        mask |= CM_CH0_INT_EN;
+    if (status & CM_CHINT1)
+        mask |= CM_CH1_INT_EN;
+    snd_cmipci_clear_bit(cm, CM_REG_INT_HLDCLR, mask);
+    snd_cmipci_set_bit(cm, CM_REG_INT_HLDCLR, mask);
+    spin_unlock(&cm->reg_lock);
+
+    if (cm->rmidi && (status & CM_UARTINT))
+        snd_mpu401_uart_interrupt(irq, cm->rmidi->private_data, regs);
+
+    if (cm->pcm) {
+        if ((status & CM_CHINT0) && cm->channel[0].running)
+            snd_pcm_period_elapsed(cm->channel[0].substream);
+        if ((status & CM_CHINT1) && cm->channel[1].running)
+            snd_pcm_period_elapsed(cm->channel[1].substream);
+    }
+    return IRQ_HANDLED;
+}
+
+/*
+ * h/w infos
+ */
+
+/* playback on channel A */
+static struct snd_pcm_hardware snd_cmipci_playback =
+{
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+                                 SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_PAUSE |
+                                 SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_MMAP_VALID),
+	.formats =		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+	.rates =		SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_48000,
+	.rate_min =		5512,
+	.rate_max =		48000,
+	.channels_min =		1,
+	.channels_max =		2,
+	.buffer_bytes_max =	(128*1024),
+	.period_bytes_min =	64,
+	.period_bytes_max =	(128*1024),
+	.periods_min =		2,
+	.periods_max =		1024,
+	.fifo_size =		0,
+};
+
+/* capture on channel B */
+static struct snd_pcm_hardware snd_cmipci_capture =
+{
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+                                 SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_PAUSE |
+                                 SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_MMAP_VALID),
+	.formats =		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+	.rates =		SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_48000,
+	.rate_min =		5512,
+	.rate_max =		48000,
+	.channels_min =		1,
+	.channels_max =		2,
+	.buffer_bytes_max =	(128*1024),
+	.period_bytes_min =	64,
+	.period_bytes_max =	(128*1024),
+	.periods_min =		2,
+	.periods_max =		1024,
+	.fifo_size =		0,
+};
+
+/* playback on channel B - stereo 16bit only? */
+static struct snd_pcm_hardware snd_cmipci_playback2 =
+{
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+                                 SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_PAUSE |
+                                 SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_MMAP_VALID),
+	.formats =		SNDRV_PCM_FMTBIT_S16_LE,
+	.rates =		SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_48000,
+	.rate_min =		5512,
+	.rate_max =		48000,
+	.channels_min =		2,
+	.channels_max =		2,
+	.buffer_bytes_max =	(128*1024),
+	.period_bytes_min =	64,
+	.period_bytes_max =	(128*1024),
+	.periods_min =		2,
+	.periods_max =		1024,
+	.fifo_size =		0,
+};
+
+/* spdif playback on channel A */
+static struct snd_pcm_hardware snd_cmipci_playback_spdif =
+{
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+                                 SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_PAUSE |
+                                 SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_MMAP_VALID),
+	.formats =		SNDRV_PCM_FMTBIT_S16_LE,
+	.rates =		SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
+	.rate_min =		44100,
+	.rate_max =		48000,
+	.channels_min =		2,
+	.channels_max =		2,
+	.buffer_bytes_max =	(128*1024),
+	.period_bytes_min =	64,
+	.period_bytes_max =	(128*1024),
+	.periods_min =		2,
+	.periods_max =		1024,
+	.fifo_size =		0,
+};
+
+/* spdif playback on channel A (32bit, IEC958 subframes) */
+static struct snd_pcm_hardware snd_cmipci_playback_iec958_subframe =
+{
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+                                 SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_PAUSE |
+                                 SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_MMAP_VALID),
+	.formats =		SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE,
+	.rates =		SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
+	.rate_min =		44100,
+	.rate_max =		48000,
+	.channels_min =		2,
+	.channels_max =		2,
+	.buffer_bytes_max =	(128*1024),
+	.period_bytes_min =	64,
+	.period_bytes_max =	(128*1024),
+	.periods_min =		2,
+	.periods_max =		1024,
+	.fifo_size =		0,
+};
+
+/* spdif capture on channel B */
+static struct snd_pcm_hardware snd_cmipci_capture_spdif =
+{
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+                                 SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_PAUSE |
+                                 SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_MMAP_VALID),
+	.formats =	        SNDRV_PCM_FMTBIT_S16_LE,
+	.rates =		SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
+	.rate_min =		44100,
+	.rate_max =		48000,
+	.channels_min =		2,
+	.channels_max =		2,
+	.buffer_bytes_max =	(128*1024),
+	.period_bytes_min =	64,
+	.period_bytes_max =	(128*1024),
+	.periods_min =		2,
+	.periods_max =		1024,
+	.fifo_size =		0,
+};
+
+
+/*
+ * check device open/close
+ */
+static int open_device_check(struct cmipci *cm, int mode, struct snd_pcm_substream *subs)
+{
+    int ch = mode & CM_OPEN_CH_MASK;
+
+    /* FIXME: a file should wait until the device becomes free
+     * when it's opened on blocking mode.  however, since the current
+     * pcm framework doesn't pass file pointer before actually opened,
+     * we can't know whether blocking mode or not in open callback..
+     */
+    down(&cm->open_mutex);
+    if (cm->opened[ch]) {
+        up(&cm->open_mutex);
+        return -EBUSY;
+    }
+    cm->opened[ch] = mode;
+    cm->channel[ch].substream = subs;
+    if (! (mode & CM_OPEN_DAC)) {
+        /* disable dual DAC mode */
+        cm->channel[ch].is_dac = 0;
+        spin_lock_irq(&cm->reg_lock);
+        snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_ENDBDAC);
+        spin_unlock_irq(&cm->reg_lock);
+    }
+    up(&cm->open_mutex);
+    return 0;
+}
+
+static void close_device_check(struct cmipci *cm, int mode)
+{
+    int ch = mode & CM_OPEN_CH_MASK;
+
+    down(&cm->open_mutex);
+
+    if (cm->opened[ch] == mode) {
+        if (cm->channel[ch].substream) {
+            snd_cmipci_ch_reset(cm, ch);
+            cm->channel[ch].running = 0;
+            cm->channel[ch].substream = NULL;
+        }
+        cm->opened[ch] = 0;
+        if (! cm->channel[ch].is_dac) {
+            /* enable dual DAC mode again */
+            cm->channel[ch].is_dac = 1;
+            spin_lock_irq(&cm->reg_lock);
+            snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_ENDBDAC);
+            spin_unlock_irq(&cm->reg_lock);
+        }
+    }
+    up(&cm->open_mutex);
+}
+
+/*
+ */
+
+static int snd_cmipci_playback_open(struct snd_pcm_substream *substream)
+{
+    struct cmipci *cm = snd_pcm_substream_chip(substream);
+    struct snd_pcm_runtime *runtime = substream->runtime;
+    int err;
+
+    if ((err = open_device_check(cm, CM_OPEN_PLAYBACK, substream)) < 0)
+        return err;
+    runtime->hw = snd_cmipci_playback;
+    runtime->hw.channels_max = cm->max_channels;
+    snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 0x10000);
+    return 0;
+}
+
+static int snd_cmipci_capture_open(struct snd_pcm_substream *substream)
+{
+    struct cmipci *cm = snd_pcm_substream_chip(substream);
+    struct snd_pcm_runtime *runtime = substream->runtime;
+    int err;
+
+    if ((err = open_device_check(cm, CM_OPEN_CAPTURE, substream)) < 0)
+        return err;
+    runtime->hw = snd_cmipci_capture;
+    if (cm->chip_version == 68) {   // 8768 only supports 44k/48k recording
+        runtime->hw.rate_min = 41000;
+        runtime->hw.rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000;
+    }
+    snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 0x10000);
+    return 0;
+}
+
+static int snd_cmipci_playback2_open(struct snd_pcm_substream *substream)
+{
+    struct cmipci *cm = snd_pcm_substream_chip(substream);
+    struct snd_pcm_runtime *runtime = substream->runtime;
+    int err;
+
+    if ((err = open_device_check(cm, CM_OPEN_PLAYBACK2, substream)) < 0) /* use channel B */
+        return err;
+    runtime->hw = snd_cmipci_playback2;
+    down(&cm->open_mutex);
+    //if (! cm->opened[CM_CH_PLAY]) {
+    if (1) { /* f&%$&g uniaud16 doesnt close stream */
+        if (cm->can_multi_ch) {
+            runtime->hw.channels_max = cm->max_channels;
+            if (cm->max_channels == 4)
+                snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &hw_constraints_channels_4);
+            else if (cm->max_channels == 6)
+                snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &hw_constraints_channels_6);
+            else if (cm->max_channels == 8)
+                snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &hw_constraints_channels_8);
+        }
+        snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 0x10000);
+    }
+    up(&cm->open_mutex);
+//    printk("opened: %i, can MC: %i, max ch: %i, hw max ch: %i\n",
+//           cm->opened[CM_CH_PLAY], cm->can_multi_ch, cm->max_channels, runtime->hw.channels_max);
+    return 0;
+}
+
+static int snd_cmipci_playback_spdif_open(struct snd_pcm_substream *substream)
+{
+    struct cmipci *cm = snd_pcm_substream_chip(substream);
+    struct snd_pcm_runtime *runtime = substream->runtime;
+    int err;
+
+    if ((err = open_device_check(cm, CM_OPEN_SPDIF_PLAYBACK, substream)) < 0) /* use channel A */
+        return err;
+    if (cm->can_ac3_hw) {
+        runtime->hw = snd_cmipci_playback_spdif;
+        if (cm->chip_version >= 37)
+            runtime->hw.formats |= SNDRV_PCM_FMTBIT_S32_LE;
+    } else {
+        runtime->hw = snd_cmipci_playback_iec958_subframe;
+    }
+    snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 0x40000);
+    cm->dig_pcm_status = cm->dig_status;
+    return 0;
+}
+
+static int snd_cmipci_capture_spdif_open(struct snd_pcm_substream *substream)
+{
+    struct cmipci *cm = snd_pcm_substream_chip(substream);
+    struct snd_pcm_runtime *runtime = substream->runtime;
+    int err;
+
+    if ((err = open_device_check(cm, CM_OPEN_SPDIF_CAPTURE, substream)) < 0) /* use channel B */
+        return err;
+    runtime->hw = snd_cmipci_capture_spdif;
+    snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 0x40000);
+    return 0;
+}
+
+
+/*
+ */
+
+static int snd_cmipci_playback_close(struct snd_pcm_substream *substream)
+{
+    struct cmipci *cm = snd_pcm_substream_chip(substream);
+    close_device_check(cm, CM_OPEN_PLAYBACK);
+    return 0;
+}
+
+static int snd_cmipci_capture_close(struct snd_pcm_substream *substream)
+{
+    struct cmipci *cm = snd_pcm_substream_chip(substream);
+    close_device_check(cm, CM_OPEN_CAPTURE);
+    return 0;
+}
+
+static int snd_cmipci_playback2_close(struct snd_pcm_substream *substream)
+{
+    struct cmipci *cm = snd_pcm_substream_chip(substream);
+    close_device_check(cm, CM_OPEN_PLAYBACK2);
+    close_device_check(cm, CM_OPEN_PLAYBACK_MULTI);
+    return 0;
+}
+
+static int snd_cmipci_playback_spdif_close(struct snd_pcm_substream *substream)
+{
+    struct cmipci *cm = snd_pcm_substream_chip(substream);
+    close_device_check(cm, CM_OPEN_SPDIF_PLAYBACK);
+    return 0;
+}
+
+static int snd_cmipci_capture_spdif_close(struct snd_pcm_substream *substream)
+{
+    struct cmipci *cm = snd_pcm_substream_chip(substream);
+    close_device_check(cm, CM_OPEN_SPDIF_CAPTURE);
+    return 0;
+}
+
+
+/*
+ */
+
+static struct snd_pcm_ops snd_cmipci_playback_ops = {
+	.open =		snd_cmipci_playback_open,
+	.close =	snd_cmipci_playback_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_cmipci_hw_params,
+	.hw_free =	snd_cmipci_playback_hw_free,
+	.prepare =	snd_cmipci_playback_prepare,
+	.trigger =	snd_cmipci_playback_trigger,
+	.pointer =	snd_cmipci_playback_pointer,
+};
+
+static struct snd_pcm_ops snd_cmipci_capture_ops = {
+	.open =		snd_cmipci_capture_open,
+	.close =	snd_cmipci_capture_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_cmipci_hw_params,
+	.hw_free =	snd_cmipci_hw_free,
+	.prepare =	snd_cmipci_capture_prepare,
+	.trigger =	snd_cmipci_capture_trigger,
+	.pointer =	snd_cmipci_capture_pointer,
+};
+
+static struct snd_pcm_ops snd_cmipci_playback2_ops = {
+	.open =		snd_cmipci_playback2_open,
+	.close =	snd_cmipci_playback2_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_cmipci_playback2_hw_params,
+	.hw_free =	snd_cmipci_hw_free,
+	.prepare =	snd_cmipci_capture_prepare,	/* channel B */
+	.trigger =	snd_cmipci_capture_trigger,	/* channel B */
+	.pointer =	snd_cmipci_capture_pointer,	/* channel B */
+};
+
+static struct snd_pcm_ops snd_cmipci_playback_spdif_ops = {
+	.open =		snd_cmipci_playback_spdif_open,
+	.close =	snd_cmipci_playback_spdif_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_cmipci_hw_params,
+	.hw_free =	snd_cmipci_playback_hw_free,
+	.prepare =	snd_cmipci_playback_spdif_prepare,	/* set up rate */
+	.trigger =	snd_cmipci_playback_trigger,
+	.pointer =	snd_cmipci_playback_pointer,
+};
+
+static struct snd_pcm_ops snd_cmipci_capture_spdif_ops = {
+	.open =		snd_cmipci_capture_spdif_open,
+	.close =	snd_cmipci_capture_spdif_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_cmipci_hw_params,
+	.hw_free =	snd_cmipci_capture_spdif_hw_free,
+	.prepare =	snd_cmipci_capture_spdif_prepare,
+	.trigger =	snd_cmipci_capture_trigger,
+	.pointer =	snd_cmipci_capture_pointer,
+};
+
+/*
+ */
+
+static void snd_cmipci_pcm_free(struct snd_pcm *pcm)
+{
+    snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static int __devinit snd_cmipci_pcm_new(struct cmipci *cm, int device)
+{
+    struct snd_pcm *pcm;
+    int err;
+
+    err = snd_pcm_new(cm->card, cm->card->driver, device, 1, 1, &pcm);
+    if (err < 0)
+        return err;
+
+    snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_cmipci_playback_ops);
+    snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_cmipci_capture_ops);
+
+    pcm->private_data = cm;
+    pcm->private_free = snd_cmipci_pcm_free;
+    pcm->info_flags = 0;
+    strcpy(pcm->name, "C-Media PCI DAC/ADC");
+    cm->pcm = pcm;
+
+    snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+                                          snd_dma_pci_data(cm->pci), 64*1024, 128*1024);
+
+    return 0;
+}
+
+static int __devinit snd_cmipci_pcm2_new(struct cmipci *cm, int device)
+{
+    struct snd_pcm *pcm;
+    int err;
+
+    err = snd_pcm_new(cm->card, cm->card->driver, device, 1, 0, &pcm);
+    if (err < 0)
+        return err;
+    snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_cmipci_playback2_ops);
+    pcm->private_data = cm;
+    pcm->private_free = snd_cmipci_pcm_free;
+    pcm->info_flags = 0;
+    strcpy(pcm->name, "C-Media PCI 2nd DAC");
+    cm->pcm2 = pcm;
+
+    snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+                                          snd_dma_pci_data(cm->pci), 64*1024, 128*1024);
+
+    return 0;
+}
+
+static int __devinit snd_cmipci_pcm_spdif_new(struct cmipci *cm, int device)
+{
+    struct snd_pcm *pcm;
+    int err;
+
+    err = snd_pcm_new(cm->card, cm->card->driver, device, 1, 1, &pcm);
+    if (err < 0)
+        return err;
+
+    snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_cmipci_playback_spdif_ops);
+    snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_cmipci_capture_spdif_ops);
+
+    pcm->private_data = cm;
+    pcm->private_free = snd_cmipci_pcm_free;
+    pcm->info_flags = 0;
+    strcpy(pcm->name, "C-Media PCI IEC958");
+    cm->pcm_spdif = pcm;
+
+    snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+                                          snd_dma_pci_data(cm->pci), 64*1024, 128*1024);
+
+    return 0;
+}
+
+/*
+ * mixer interface:
+ * - CM8338/8738 has a compatible mixer interface with SB16, but
+ *   lack of some elements like tone control, i/o gain and AGC.
+ * - Access to native registers:
+ *   - A 3D switch
+ *   - Output mute switches
+ */
+
+static void snd_cmipci_mixer_write(struct cmipci *s, unsigned char idx, unsigned char data)
+{
+    outb(idx, s->iobase + CM_REG_SB16_ADDR);
+    outb(data, s->iobase + CM_REG_SB16_DATA);
+}
+
+static unsigned char snd_cmipci_mixer_read(struct cmipci *s, unsigned char idx)
+{
+    unsigned char v;
+
+    outb(idx, s->iobase + CM_REG_SB16_ADDR);
+    v = inb(s->iobase + CM_REG_SB16_DATA);
+    return v;
+}
+
+/*
+ * general mixer element
+ */
+struct cmipci_sb_reg {
+    unsigned int left_reg, right_reg;
+    unsigned int left_shift, right_shift;
+    unsigned int mask;
+    unsigned int invert: 1;
+    unsigned int stereo: 1;
+};
+
+#define COMPOSE_SB_REG(lreg,rreg,lshift,rshift,mask,invert,stereo) \
+    ((lreg) | ((rreg) << 8) | (lshift << 16) | (rshift << 19) | (mask << 24) | (invert << 22) | (stereo << 23))
+
+#define CMIPCI_DOUBLE(xname, left_reg, right_reg, left_shift, right_shift, mask, invert, stereo) \
+    { SNDRV_CTL_ELEM_IFACE_MIXER, 0, 0, xname, 0, \
+    0, 0, snd_cmipci_info_volume, \
+    snd_cmipci_get_volume, snd_cmipci_put_volume, \
+    COMPOSE_SB_REG(left_reg, right_reg, left_shift, right_shift, mask, invert, stereo), \
+    }
+
+#define CMIPCI_SB_VOL_STEREO(xname,reg,shift,mask) CMIPCI_DOUBLE(xname, reg, reg+1, shift, shift, mask, 0, 1)
+#define CMIPCI_SB_VOL_MONO(xname,reg,shift,mask) CMIPCI_DOUBLE(xname, reg, reg, shift, shift, mask, 0, 0)
+#define CMIPCI_SB_SW_STEREO(xname,lshift,rshift) CMIPCI_DOUBLE(xname, SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, lshift, rshift, 1, 0, 1)
+#define CMIPCI_SB_SW_MONO(xname,shift) CMIPCI_DOUBLE(xname, SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, shift, shift, 1, 0, 0)
+
+static void cmipci_sb_reg_decode(struct cmipci_sb_reg *r, unsigned long val)
+{
+    r->left_reg = val & 0xff;
+    r->right_reg = (val >> 8) & 0xff;
+    r->left_shift = (val >> 16) & 0x07;
+    r->right_shift = (val >> 19) & 0x07;
+    r->invert = (val >> 22) & 1;
+    r->stereo = (val >> 23) & 1;
+    r->mask = (val >> 24) & 0xff;
+}
+
+static int snd_cmipci_info_volume(struct snd_kcontrol *kcontrol,
+                                  struct snd_ctl_elem_info *uinfo)
+{
+    struct cmipci_sb_reg reg;
+
+    cmipci_sb_reg_decode(&reg, kcontrol->private_value);
+    uinfo->type = reg.mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+    uinfo->count = reg.stereo + 1;
+    uinfo->value.integer.min = 0;
+    uinfo->value.integer.max = reg.mask;
+    return 0;
+}
+
+static int snd_cmipci_get_volume(struct snd_kcontrol *kcontrol,
+                                 struct snd_ctl_elem_value *ucontrol)
+{
+    struct cmipci *cm = snd_kcontrol_chip(kcontrol);
+    struct cmipci_sb_reg reg;
+    int val;
+
+    cmipci_sb_reg_decode(&reg, kcontrol->private_value);
+    spin_lock_irq(&cm->reg_lock);
+    val = (snd_cmipci_mixer_read(cm, reg.left_reg) >> reg.left_shift) & reg.mask;
+    if (reg.invert)
+        val = reg.mask - val;
+    ucontrol->value.integer.value[0] = val;
+    if (reg.stereo) {
+        val = (snd_cmipci_mixer_read(cm, reg.right_reg) >> reg.right_shift) & reg.mask;
+        if (reg.invert)
+            val = reg.mask - val;
+        ucontrol->value.integer.value[1] = val;
+    }
+    spin_unlock_irq(&cm->reg_lock);
+    return 0;
+}
+
+static int snd_cmipci_put_volume(struct snd_kcontrol *kcontrol,
+                                 struct snd_ctl_elem_value *ucontrol)
+{
+    struct cmipci *cm = snd_kcontrol_chip(kcontrol);
+    struct cmipci_sb_reg reg;
+    int change;
+    int left, right, oleft, oright;
+
+    cmipci_sb_reg_decode(&reg, kcontrol->private_value);
+    left = ucontrol->value.integer.value[0] & reg.mask;
+    if (reg.invert)
+        left = reg.mask - left;
+    left <<= reg.left_shift;
+    if (reg.stereo) {
+        right = ucontrol->value.integer.value[1] & reg.mask;
+        if (reg.invert)
+            right = reg.mask - right;
+        right <<= reg.right_shift;
+    } else
+        right = 0;
+    spin_lock_irq(&cm->reg_lock);
+    oleft = snd_cmipci_mixer_read(cm, reg.left_reg);
+    left |= oleft & ~(reg.mask << reg.left_shift);
+    change = left != oleft;
+    if (reg.stereo) {
+        if (reg.left_reg != reg.right_reg) {
+            snd_cmipci_mixer_write(cm, reg.left_reg, left);
+            oright = snd_cmipci_mixer_read(cm, reg.right_reg);
+        } else
+            oright = left;
+        right |= oright & ~(reg.mask << reg.right_shift);
+        change |= right != oright;
+        snd_cmipci_mixer_write(cm, reg.right_reg, right);
+    } else
+        snd_cmipci_mixer_write(cm, reg.left_reg, left);
+    spin_unlock_irq(&cm->reg_lock);
+    return change;
+}
+
+/*
+ * input route (left,right) -> (left,right)
+ */
+#define CMIPCI_SB_INPUT_SW(xname, left_shift, right_shift) \
+    { SNDRV_CTL_ELEM_IFACE_MIXER, 0, 0, xname, 0, \
+    0, 0, snd_cmipci_info_input_sw, \
+    snd_cmipci_get_input_sw, snd_cmipci_put_input_sw, \
+    COMPOSE_SB_REG(SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, left_shift, right_shift, 1, 0, 1), \
+    }
+static int snd_cmipci_info_input_sw(struct snd_kcontrol *kcontrol,
+                                    struct snd_ctl_elem_info *uinfo)
+{
+    uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+    uinfo->count = 4;
+    uinfo->value.integer.min = 0;
+    uinfo->value.integer.max = 1;
+    return 0;
+}
+
+static int snd_cmipci_get_input_sw(struct snd_kcontrol *kcontrol,
+                                   struct snd_ctl_elem_value *ucontrol)
+{
+    struct cmipci *cm = snd_kcontrol_chip(kcontrol);
+    struct cmipci_sb_reg reg;
+    int val1, val2;
+
+    cmipci_sb_reg_decode(&reg, kcontrol->private_value);
+    spin_lock_irq(&cm->reg_lock);
+    val1 = snd_cmipci_mixer_read(cm, reg.left_reg);
+    val2 = snd_cmipci_mixer_read(cm, reg.right_reg);
+    spin_unlock_irq(&cm->reg_lock);
+    ucontrol->value.integer.value[0] = (val1 >> reg.left_shift) & 1;
+    ucontrol->value.integer.value[1] = (val2 >> reg.left_shift) & 1;
+    ucontrol->value.integer.value[2] = (val1 >> reg.right_shift) & 1;
+    ucontrol->value.integer.value[3] = (val2 >> reg.right_shift) & 1;
+    return 0;
+}
+
+static int snd_cmipci_put_input_sw(struct snd_kcontrol *kcontrol,
+                                   struct snd_ctl_elem_value *ucontrol)
+{
+    struct cmipci *cm = snd_kcontrol_chip(kcontrol);
+    struct cmipci_sb_reg reg;
+    int change;
+    int val1, val2, oval1, oval2;
+
+    cmipci_sb_reg_decode(&reg, kcontrol->private_value);
+    spin_lock_irq(&cm->reg_lock);
+    oval1 = snd_cmipci_mixer_read(cm, reg.left_reg);
+    oval2 = snd_cmipci_mixer_read(cm, reg.right_reg);
+    val1 = oval1 & ~((1 << reg.left_shift) | (1 << reg.right_shift));
+    val2 = oval2 & ~((1 << reg.left_shift) | (1 << reg.right_shift));
+    val1 |= (ucontrol->value.integer.value[0] & 1) << reg.left_shift;
+    val2 |= (ucontrol->value.integer.value[1] & 1) << reg.left_shift;
+    val1 |= (ucontrol->value.integer.value[2] & 1) << reg.right_shift;
+    val2 |= (ucontrol->value.integer.value[3] & 1) << reg.right_shift;
+    change = val1 != oval1 || val2 != oval2;
+    snd_cmipci_mixer_write(cm, reg.left_reg, val1);
+    snd_cmipci_mixer_write(cm, reg.right_reg, val2);
+    spin_unlock_irq(&cm->reg_lock);
+    return change;
+}
+
+/*
+ * native mixer switches/volumes
+ */
+
+#define CMIPCI_MIXER_SW_STEREO(xname, reg, lshift, rshift, invert) \
+    { SNDRV_CTL_ELEM_IFACE_MIXER, 0, 0, xname, 0, \
+    0, 0, snd_cmipci_info_native_mixer, \
+    snd_cmipci_get_native_mixer, snd_cmipci_put_native_mixer, \
+    COMPOSE_SB_REG(reg, reg, lshift, rshift, 1, invert, 1), \
+    }
+
+#define CMIPCI_MIXER_SW_MONO(xname, reg, shift, invert) \
+    { SNDRV_CTL_ELEM_IFACE_MIXER, 0, 0, xname, 0, \
+    0, 0, snd_cmipci_info_native_mixer, \
+    snd_cmipci_get_native_mixer, snd_cmipci_put_native_mixer, \
+    COMPOSE_SB_REG(reg, reg, shift, shift, 1, invert, 0), \
+    }
+
+#define CMIPCI_MIXER_VOL_STEREO(xname, reg, lshift, rshift, mask) \
+    { SNDRV_CTL_ELEM_IFACE_MIXER, 0, 0, xname, 0, \
+    0, 0, snd_cmipci_info_native_mixer, \
+    snd_cmipci_get_native_mixer, snd_cmipci_put_native_mixer, \
+    COMPOSE_SB_REG(reg, reg, lshift, rshift, mask, 0, 1), \
+    }
+
+#define CMIPCI_MIXER_VOL_MONO(xname, reg, shift, mask) \
+    { SNDRV_CTL_ELEM_IFACE_MIXER, 0, 0, xname, 0, \
+    0, 0, snd_cmipci_info_native_mixer, \
+    snd_cmipci_get_native_mixer, snd_cmipci_put_native_mixer, \
+    COMPOSE_SB_REG(reg, reg, shift, shift, mask, 0, 0), \
+    }
+
+static int snd_cmipci_info_native_mixer(struct snd_kcontrol *kcontrol,
+                                        struct snd_ctl_elem_info *uinfo)
+{
+    struct cmipci_sb_reg reg;
+
+    cmipci_sb_reg_decode(&reg, kcontrol->private_value);
+    uinfo->type = reg.mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+    uinfo->count = reg.stereo + 1;
+    uinfo->value.integer.min = 0;
+    uinfo->value.integer.max = reg.mask;
+    return 0;
+
+}
+
+static int snd_cmipci_get_native_mixer(struct snd_kcontrol *kcontrol,
+                                       struct snd_ctl_elem_value *ucontrol)
+{
+    struct cmipci *cm = snd_kcontrol_chip(kcontrol);
+    struct cmipci_sb_reg reg;
+    unsigned char oreg, val;
+
+    cmipci_sb_reg_decode(&reg, kcontrol->private_value);
+    spin_lock_irq(&cm->reg_lock);
+    oreg = inb(cm->iobase + reg.left_reg);
+    val = (oreg >> reg.left_shift) & reg.mask;
+    if (reg.invert)
+        val = reg.mask - val;
+    ucontrol->value.integer.value[0] = val;
+    if (reg.stereo) {
+        val = (oreg >> reg.right_shift) & reg.mask;
+        if (reg.invert)
+            val = reg.mask - val;
+        ucontrol->value.integer.value[1] = val;
+    }
+    spin_unlock_irq(&cm->reg_lock);
+    return 0;
+}
+
+static int snd_cmipci_put_native_mixer(struct snd_kcontrol *kcontrol,
+                                       struct snd_ctl_elem_value *ucontrol)
+{
+    struct cmipci *cm = snd_kcontrol_chip(kcontrol);
+    struct cmipci_sb_reg reg;
+    unsigned char oreg, nreg, val;
+
+    cmipci_sb_reg_decode(&reg, kcontrol->private_value);
+    spin_lock_irq(&cm->reg_lock);
+    oreg = inb(cm->iobase + reg.left_reg);
+    val = ucontrol->value.integer.value[0] & reg.mask;
+    if (reg.invert)
+        val = reg.mask - val;
+    nreg = oreg & ~(reg.mask << reg.left_shift);
+    nreg |= (val << reg.left_shift);
+    if (reg.stereo) {
+        val = ucontrol->value.integer.value[1] & reg.mask;
+        if (reg.invert)
+            val = reg.mask - val;
+        nreg &= ~(reg.mask << reg.right_shift);
+        nreg |= (val << reg.right_shift);
+    }
+    outb(nreg, cm->iobase + reg.left_reg);
+    spin_unlock_irq(&cm->reg_lock);
+    return (nreg != oreg);
+}
+
+/*
+ * special case - check mixer sensitivity
+ */
+static int snd_cmipci_get_native_mixer_sensitive(struct snd_kcontrol *kcontrol,
+                                                 struct snd_ctl_elem_value *ucontrol)
+{
+    //struct cmipci *cm = snd_kcontrol_chip(kcontrol);
+    return snd_cmipci_get_native_mixer(kcontrol, ucontrol);
+}
+
+static int snd_cmipci_put_native_mixer_sensitive(struct snd_kcontrol *kcontrol,
+                                                 struct snd_ctl_elem_value *ucontrol)
+{
+    struct cmipci *cm = snd_kcontrol_chip(kcontrol);
+    if (cm->mixer_insensitive) {
+        /* ignored */
+        return 0;
+    }
+    return snd_cmipci_put_native_mixer(kcontrol, ucontrol);
+}
+
+static struct snd_kcontrol_new snd_cmipci_mixers[] __devinitdata = {
+    CMIPCI_SB_VOL_STEREO("Master Playback Volume", SB_DSP4_MASTER_DEV, 3, 31),
+    CMIPCI_MIXER_SW_MONO("3D Control - Switch", CM_REG_MIXER1, CM_X3DEN_SHIFT, 0),
+    CMIPCI_SB_VOL_STEREO("PCM Playback Volume", SB_DSP4_PCM_DEV, 3, 31),
+    //CMIPCI_MIXER_SW_MONO("PCM Playback Switch", CM_REG_MIXER1, CM_WSMUTE_SHIFT, 1),
+    { /* switch with sensitivity */
+        SNDRV_CTL_ELEM_IFACE_MIXER,0,0,
+        "PCM Playback Switch",0,0,0,
+        snd_cmipci_info_native_mixer,
+        snd_cmipci_get_native_mixer_sensitive,
+        snd_cmipci_put_native_mixer_sensitive,
+        COMPOSE_SB_REG(CM_REG_MIXER1, CM_REG_MIXER1, CM_WSMUTE_SHIFT, CM_WSMUTE_SHIFT, 1, 1, 0),
+    },
+    CMIPCI_MIXER_SW_STEREO("PCM Capture Switch", CM_REG_MIXER1, CM_WAVEINL_SHIFT, CM_WAVEINR_SHIFT, 0),
+    CMIPCI_SB_VOL_STEREO("Synth Playback Volume", SB_DSP4_SYNTH_DEV, 3, 31),
+    CMIPCI_MIXER_SW_MONO("Synth Playback Switch", CM_REG_MIXER1, CM_FMMUTE_SHIFT, 1),
+    CMIPCI_SB_INPUT_SW("Synth Capture Route", 6, 5),
+    CMIPCI_SB_VOL_STEREO("CD Playback Volume", SB_DSP4_CD_DEV, 3, 31),
+    CMIPCI_SB_SW_STEREO("CD Playback Switch", 2, 1),
+    CMIPCI_SB_INPUT_SW("CD Capture Route", 2, 1),
+    CMIPCI_SB_VOL_STEREO("Line Playback Volume", SB_DSP4_LINE_DEV, 3, 31),
+    CMIPCI_SB_SW_STEREO("Line Playback Switch", 4, 3),
+    CMIPCI_SB_INPUT_SW("Line Capture Route", 4, 3),
+    CMIPCI_SB_VOL_MONO("Mic Playback Volume", SB_DSP4_MIC_DEV, 3, 31),
+    CMIPCI_SB_SW_MONO("Mic Playback Switch", 0),
+    CMIPCI_DOUBLE("Mic Capture Switch", SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 0, 0, 1, 0, 0),
+    CMIPCI_SB_VOL_MONO("PC Speaker Playback Volume", SB_DSP4_SPEAKER_DEV, 6, 3),
+    CMIPCI_MIXER_VOL_STEREO("Aux Playback Volume", CM_REG_AUX_VOL, 4, 0, 15),
+    CMIPCI_MIXER_SW_STEREO("Aux Playback Switch", CM_REG_MIXER2, CM_VAUXLM_SHIFT, CM_VAUXRM_SHIFT, 0),
+    CMIPCI_MIXER_SW_STEREO("Aux Capture Switch", CM_REG_MIXER2, CM_RAUXLEN_SHIFT, CM_RAUXREN_SHIFT, 0),
+    CMIPCI_MIXER_SW_MONO("Mic Boost Playback Switch", CM_REG_MIXER2, CM_MICGAINZ_SHIFT, 1),
+    CMIPCI_MIXER_VOL_MONO("Mic Capture Volume", CM_REG_MIXER2, CM_VADMIC_SHIFT, 7),
+    CMIPCI_SB_VOL_MONO("Phone Playback Volume", CM_REG_EXTENT_IND, 5, 7),
+    CMIPCI_DOUBLE("Phone Playback Switch", CM_REG_EXTENT_IND, CM_REG_EXTENT_IND, 4, 4, 1, 0, 0),
+    CMIPCI_DOUBLE("PC Speaker Playback Switch", CM_REG_EXTENT_IND, CM_REG_EXTENT_IND, 3, 3, 1, 0, 0),
+    CMIPCI_DOUBLE("Mic Boost Capture Switch", CM_REG_EXTENT_IND, CM_REG_EXTENT_IND, 0, 0, 1, 0, 0),
+};
+
+/*
+ * other switches
+ */
+
+struct cmipci_switch_args {
+    int reg;		/* register index */
+    unsigned int mask;	/* mask bits */
+    unsigned int mask_on;	/* mask bits to turn on */
+    int is_byte: 1;		/* byte access? */
+    unsigned int ac3_sensitive: 1;  /* access forbidden during
+                                     * non-audio operation?
+                                     */
+};
+
+static int snd_cmipci_uswitch_info(struct snd_kcontrol *kcontrol,
+                                   struct snd_ctl_elem_info *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_cmipci_uswitch_get(struct snd_kcontrol *kcontrol,
+                                   struct snd_ctl_elem_value *ucontrol,
+                                   struct cmipci_switch_args *args)
+{
+    unsigned int val;
+    struct cmipci *cm = snd_kcontrol_chip(kcontrol);
+
+    spin_lock_irq(&cm->reg_lock);
+    if (args->ac3_sensitive && cm->mixer_insensitive) {
+        ucontrol->value.integer.value[0] = 0;
+        spin_unlock_irq(&cm->reg_lock);
+        return 0;
+    }
+    if (args->is_byte)
+        val = inb(cm->iobase + args->reg);
+    else
+        val = snd_cmipci_read(cm, args->reg);
+    ucontrol->value.integer.value[0] = ((val & args->mask) == args->mask_on) ? 1 : 0;
+    spin_unlock_irq(&cm->reg_lock);
+    return 0;
+}
+
+static int snd_cmipci_uswitch_get(struct snd_kcontrol *kcontrol,
+                                  struct snd_ctl_elem_value *ucontrol)
+{
+    struct cmipci_switch_args *args;
+    args = (struct cmipci_switch_args *)kcontrol->private_value;
+    snd_assert(args != NULL, return -EINVAL);
+    return _snd_cmipci_uswitch_get(kcontrol, ucontrol, args);
+}
+
+static int _snd_cmipci_uswitch_put(struct snd_kcontrol *kcontrol,
+                                   struct snd_ctl_elem_value *ucontrol,
+                                   struct cmipci_switch_args *args)
+{
+    unsigned int val;
+    int change;
+    struct cmipci *cm = snd_kcontrol_chip(kcontrol);
+
+    spin_lock_irq(&cm->reg_lock);
+    if (args->ac3_sensitive && cm->mixer_insensitive) {
+        /* ignored */
+        spin_unlock_irq(&cm->reg_lock);
+        return 0;
+    }
+    if (args->is_byte)
+        val = inb(cm->iobase + args->reg);
+    else
+        val = snd_cmipci_read(cm, args->reg);
+    change = (val & args->mask) != (ucontrol->value.integer.value[0] ? args->mask : 0);
+    if (change) {
+        val &= ~args->mask;
+        if (ucontrol->value.integer.value[0])
+            val |= args->mask_on;
+        else
+            val |= (args->mask & ~args->mask_on);
+        if (args->is_byte)
+            outb((unsigned char)val, cm->iobase + args->reg);
+        else
+            snd_cmipci_write(cm, args->reg, val);
+    }
+    spin_unlock_irq(&cm->reg_lock);
+    return change;
+}
+
+static int snd_cmipci_uswitch_put(struct snd_kcontrol *kcontrol,
+                                  struct snd_ctl_elem_value *ucontrol)
+{
+    struct cmipci_switch_args *args;
+    args = (struct cmipci_switch_args *)kcontrol->private_value;
+    snd_assert(args != NULL, return -EINVAL);
+    return _snd_cmipci_uswitch_put(kcontrol, ucontrol, args);
+}
+
+#define DEFINE_SWITCH_ARG(sname, xreg, xmask, xmask_on, xis_byte, xac3) \
+    static struct cmipci_switch_args cmipci_switch_arg_##sname = { \
+    xreg, \
+    xmask, \
+    xmask_on, \
+    xis_byte, \
+    xac3, \
+    }
+
+#define DEFINE_BIT_SWITCH_ARG(sname, xreg, xmask, xis_byte, xac3) \
+    DEFINE_SWITCH_ARG(sname, xreg, xmask, xmask, xis_byte, xac3)
+
+#if 0 /* these will be controlled in pcm device */
+DEFINE_BIT_SWITCH_ARG(spdif_in, CM_REG_FUNCTRL1, CM_SPDF_1, 0, 0);
+DEFINE_BIT_SWITCH_ARG(spdif_out, CM_REG_FUNCTRL1, CM_SPDF_0, 0, 0);
+#endif
+DEFINE_BIT_SWITCH_ARG(spdif_in_sel1, CM_REG_CHFORMAT, CM_SPDIF_SELECT1, 0, 0);
+DEFINE_BIT_SWITCH_ARG(spdif_in_sel2, CM_REG_MISC_CTRL, CM_SPDIF_SELECT2, 0, 0);
+DEFINE_BIT_SWITCH_ARG(spdif_enable, CM_REG_LEGACY_CTRL, CM_ENSPDOUT, 0, 0);
+DEFINE_BIT_SWITCH_ARG(spdo2dac, CM_REG_FUNCTRL1, CM_SPDO2DAC, 0, 1);
+DEFINE_BIT_SWITCH_ARG(spdi_valid, CM_REG_MISC, CM_SPDVALID, 1, 0);
+DEFINE_BIT_SWITCH_ARG(spdif_copyright, CM_REG_LEGACY_CTRL, CM_SPDCOPYRHT, 0, 0);
+DEFINE_BIT_SWITCH_ARG(spdif_dac_out, CM_REG_LEGACY_CTRL, CM_DAC2SPDO, 0, 1);
+DEFINE_SWITCH_ARG(spdo_5v, CM_REG_MISC_CTRL, CM_SPDO5V, 0, 0, 0); /* inverse: 0 = 5V */
+// DEFINE_BIT_SWITCH_ARG(spdo_48k, CM_REG_MISC_CTRL, CM_SPDF_AC97|CM_SPDIF48K, 0, 1);
+DEFINE_BIT_SWITCH_ARG(spdif_loop, CM_REG_FUNCTRL1, CM_SPDFLOOP, 0, 1);
+DEFINE_BIT_SWITCH_ARG(spdi_monitor, CM_REG_MIXER1, CM_CDPLAY, 1, 0);
+/* DEFINE_BIT_SWITCH_ARG(spdi_phase, CM_REG_CHFORMAT, CM_SPDIF_INVERSE, 0, 0); */
+DEFINE_BIT_SWITCH_ARG(spdi_phase, CM_REG_MISC, CM_SPDIF_INVERSE, 1, 0);
+DEFINE_BIT_SWITCH_ARG(spdi_phase2, CM_REG_CHFORMAT, CM_SPDIF_INVERSE2, 0, 0);
+#if CM_CH_PLAY == 1
+DEFINE_SWITCH_ARG(exchange_dac, CM_REG_MISC_CTRL, CM_XCHGDAC, 0, 0, 0); /* reversed */
+#else
+DEFINE_SWITCH_ARG(exchange_dac, CM_REG_MISC_CTRL, CM_XCHGDAC, CM_XCHGDAC, 0, 0);
+#endif
+DEFINE_BIT_SWITCH_ARG(fourch, CM_REG_MISC_CTRL, CM_N4SPK3D, 0, 0);
+DEFINE_BIT_SWITCH_ARG(line_rear, CM_REG_MIXER1, CM_SPK4, 1, 0);
+DEFINE_BIT_SWITCH_ARG(line_bass, CM_REG_LEGACY_CTRL, CM_LINE_AS_BASS, 0, 0);
+DEFINE_BIT_SWITCH_ARG(joystick, CM_REG_FUNCTRL1, CM_JYSTK_EN, 0, 0);
+DEFINE_SWITCH_ARG(modem, CM_REG_MISC_CTRL, CM_FLINKON|CM_FLINKOFF, CM_FLINKON, 0, 0);
+
+//Can't compile in non-KEE mode without extra pointer in structure
+//(watcom complains it can't convert a far ptr to ulong even though it's
+// safe to do so in this case)
+#define DEFINE_SWITCH(sname, stype, sarg) \
+    { stype, 0, 0, \
+    sname, 0, 0, 0, \
+    snd_cmipci_uswitch_info, \
+    snd_cmipci_uswitch_get, \
+    snd_cmipci_uswitch_put, \
+    (unsigned long)&cmipci_switch_arg_##sarg,\
+    }
+
+#define DEFINE_CARD_SWITCH(sname, sarg) DEFINE_SWITCH(sname, SNDRV_CTL_ELEM_IFACE_CARD, sarg)
+#define DEFINE_MIXER_SWITCH(sname, sarg) DEFINE_SWITCH(sname, SNDRV_CTL_ELEM_IFACE_MIXER, sarg)
+
+
+/*
+ * callbacks for spdif output switch
+ * needs toggle two registers..
+ */
+static int snd_cmipci_spdout_enable_get(struct snd_kcontrol *kcontrol,
+                                        struct snd_ctl_elem_value *ucontrol)
+{
+    int changed;
+    changed = _snd_cmipci_uswitch_get(kcontrol, ucontrol, &cmipci_switch_arg_spdif_enable);
+    changed |= _snd_cmipci_uswitch_get(kcontrol, ucontrol, &cmipci_switch_arg_spdo2dac);
+    return changed;
+}
+
+static int snd_cmipci_spdout_enable_put(struct snd_kcontrol *kcontrol,
+                                        struct snd_ctl_elem_value *ucontrol)
+{
+    struct cmipci *chip = snd_kcontrol_chip(kcontrol);
+    int changed;
+    changed = _snd_cmipci_uswitch_put(kcontrol, ucontrol, &cmipci_switch_arg_spdif_enable);
+    changed |= _snd_cmipci_uswitch_put(kcontrol, ucontrol, &cmipci_switch_arg_spdo2dac);
+    if (changed) {
+        if (ucontrol->value.integer.value[0]) {
+            if (chip->spdif_playback_avail)
+                snd_cmipci_set_bit(chip, CM_REG_FUNCTRL1, CM_PLAYBACK_SPDF);
+        } else {
+            if (chip->spdif_playback_avail)
+                snd_cmipci_clear_bit(chip, CM_REG_FUNCTRL1, CM_PLAYBACK_SPDF);
+        }
+    }
+    chip->spdif_playback_enabled = ucontrol->value.integer.value[0];
+    return changed;
+}
+
+static int snd_cmipci_line_in_mode_info(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_info *uinfo)
+{
+	struct cmipci *cm = snd_kcontrol_chip(kcontrol);
+	static char *texts[3] = { "Line-In", "Rear Output", "Bass Output" };
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = cm->chip_version >= 39 ? 3 : 2;
+	if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+		uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static inline unsigned int get_line_in_mode(struct cmipci *cm)
+{
+	unsigned int val;
+	if (cm->chip_version >= 39) {
+		val = snd_cmipci_read(cm, CM_REG_LEGACY_CTRL);
+		if (val & CM_LINE_AS_BASS)
+			return 2;
+	}
+	val = snd_cmipci_read_b(cm, CM_REG_MIXER1);
+	if (val & CM_SPK4)
+		return 1;
+	return 0;
+}
+
+static int snd_cmipci_line_in_mode_get(struct snd_kcontrol *kcontrol,
+				       struct snd_ctl_elem_value *ucontrol)
+{
+	struct cmipci *cm = snd_kcontrol_chip(kcontrol);
+
+	spin_lock_irq(&cm->reg_lock);
+	ucontrol->value.enumerated.item[0] = get_line_in_mode(cm);
+	spin_unlock_irq(&cm->reg_lock);
+	return 0;
+}
+
+static int snd_cmipci_line_in_mode_put(struct snd_kcontrol *kcontrol,
+				       struct snd_ctl_elem_value *ucontrol)
+{
+	struct cmipci *cm = snd_kcontrol_chip(kcontrol);
+	int change;
+
+	spin_lock_irq(&cm->reg_lock);
+	if (ucontrol->value.enumerated.item[0] == 2)
+		change = snd_cmipci_set_bit(cm, CM_REG_LEGACY_CTRL, CM_LINE_AS_BASS);
+	else
+		change = snd_cmipci_clear_bit(cm, CM_REG_LEGACY_CTRL, CM_LINE_AS_BASS);
+	if (ucontrol->value.enumerated.item[0] == 1)
+		change |= snd_cmipci_set_bit_b(cm, CM_REG_MIXER1, CM_SPK4);
+	else
+		change |= snd_cmipci_clear_bit_b(cm, CM_REG_MIXER1, CM_SPK4);
+	spin_unlock_irq(&cm->reg_lock);
+	return change;
+}
+
+static int snd_cmipci_mic_in_mode_info(struct snd_kcontrol *kcontrol,
+				       struct snd_ctl_elem_info *uinfo)
+{
+	static char *texts[2] = { "Mic-In", "Center/LFE Output" };
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 2;
+	if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+		uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int snd_cmipci_mic_in_mode_get(struct snd_kcontrol *kcontrol,
+				      struct snd_ctl_elem_value *ucontrol)
+{
+	struct cmipci *cm = snd_kcontrol_chip(kcontrol);
+	/* same bit as spdi_phase */
+	spin_lock_irq(&cm->reg_lock);
+	ucontrol->value.enumerated.item[0] =
+		(snd_cmipci_read_b(cm, CM_REG_MISC) & CM_SPDIF_INVERSE) ? 1 : 0;
+	spin_unlock_irq(&cm->reg_lock);
+	return 0;
+}
+
+static int snd_cmipci_mic_in_mode_put(struct snd_kcontrol *kcontrol,
+				      struct snd_ctl_elem_value *ucontrol)
+{
+	struct cmipci *cm = snd_kcontrol_chip(kcontrol);
+	int change;
+
+	spin_lock_irq(&cm->reg_lock);
+	if (ucontrol->value.enumerated.item[0])
+		change = snd_cmipci_set_bit_b(cm, CM_REG_MISC, CM_SPDIF_INVERSE);
+	else
+		change = snd_cmipci_clear_bit_b(cm, CM_REG_MISC, CM_SPDIF_INVERSE);
+	spin_unlock_irq(&cm->reg_lock);
+	return change;
+}
+
+/* both for CM8338/8738 */
+static struct snd_kcontrol_new snd_cmipci_mixer_switches[] __devinitdata = {
+	DEFINE_MIXER_SWITCH("Four Channel Mode", fourch),
+	{
+		.name = "Line-In Mode",
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.info = snd_cmipci_line_in_mode_info,
+		.get = snd_cmipci_line_in_mode_get,
+		.put = snd_cmipci_line_in_mode_put,
+	},
+};
+
+/* for non-multichannel chips */
+static struct snd_kcontrol_new snd_cmipci_nomulti_switch __devinitdata =
+DEFINE_MIXER_SWITCH("Exchange DAC", exchange_dac);
+
+/* only for CM8738 */
+static struct snd_kcontrol_new snd_cmipci_8738_mixer_switches[] __devinitdata = {
+#if 0 /* controlled in pcm device */
+    DEFINE_MIXER_SWITCH("IEC958 In Record", spdif_in),
+    DEFINE_MIXER_SWITCH("IEC958 Out", spdif_out),
+    DEFINE_MIXER_SWITCH("IEC958 Out To DAC", spdo2dac),
+#endif
+    // DEFINE_MIXER_SWITCH("IEC958 Output Switch", spdif_enable),
+    {
+        SNDRV_CTL_ELEM_IFACE_MIXER,0,0,
+        "IEC958 Output Switch",0,0, 0,
+        snd_cmipci_uswitch_info,
+        snd_cmipci_spdout_enable_get,
+        snd_cmipci_spdout_enable_put,0
+    },
+    DEFINE_MIXER_SWITCH("IEC958 In Valid", spdi_valid),
+    DEFINE_MIXER_SWITCH("IEC958 Copyright", spdif_copyright),
+    DEFINE_MIXER_SWITCH("IEC958 5V", spdo_5v),
+    //	DEFINE_MIXER_SWITCH("IEC958 In/Out 48KHz", spdo_48k),
+    DEFINE_MIXER_SWITCH("IEC958 Loop", spdif_loop),
+    DEFINE_MIXER_SWITCH("IEC958 In Monitor", spdi_monitor),
+};
+
+/* only for model 033/037 */
+static struct snd_kcontrol_new snd_cmipci_old_mixer_switches[] __devinitdata = {
+    DEFINE_MIXER_SWITCH("IEC958 Mix Analog", spdif_dac_out),
+    DEFINE_MIXER_SWITCH("IEC958 In Phase Inverse", spdi_phase),
+    DEFINE_MIXER_SWITCH("IEC958 In Select", spdif_in_sel1),
+};
+
+/* only for model 039 or later */
+static struct snd_kcontrol_new snd_cmipci_extra_mixer_switches[] __devinitdata = {
+	DEFINE_MIXER_SWITCH("IEC958 In Select", spdif_in_sel2),
+	DEFINE_MIXER_SWITCH("IEC958 In Phase Inverse", spdi_phase2),
+	{
+		.name = "Mic-In Mode",
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.info = snd_cmipci_mic_in_mode_info,
+		.get = snd_cmipci_mic_in_mode_get,
+		.put = snd_cmipci_mic_in_mode_put,
+	}
+};
+
+/* card control switches */
+static struct snd_kcontrol_new snd_cmipci_control_switches[] __devinitdata = {
+    DEFINE_CARD_SWITCH("Joystick", joystick),
+    DEFINE_CARD_SWITCH("Modem", modem),
+};
+
+static int __devinit snd_cmipci_mixer_new(struct cmipci *cm, int pcm_spdif_device)
+{
+    struct snd_card *card;
+    struct snd_kcontrol_new *sw;
+    struct snd_kcontrol *kctl;
+    unsigned int idx;
+    int err;
+
+    snd_assert(cm != NULL && cm->card != NULL, return -EINVAL);
+
+    card = cm->card;
+
+    strcpy(card->mixername, "CMedia PCI");
+
+    spin_lock_irq(&cm->reg_lock);
+    snd_cmipci_mixer_write(cm, 0x00, 0x00);		/* mixer reset */
+    spin_unlock_irq(&cm->reg_lock);
+
+    for (idx = 0; idx < ARRAY_SIZE(snd_cmipci_mixers); idx++) {
+        if (cm->chip_version == 68) {   // 8768 has no PCM volume
+            if (!strcmp(snd_cmipci_mixers[idx].name,
+                        "PCM Playback Volume"))
+                continue;
+        }
+        if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cmipci_mixers[idx], cm))) < 0)
+            return err;
+    }
+
+    /* mixer switches */
+    sw = snd_cmipci_mixer_switches;
+    for (idx = 0; idx < ARRAY_SIZE(snd_cmipci_mixer_switches); idx++, sw++) {
+        err = snd_ctl_add(cm->card, snd_ctl_new1(sw, cm));
+        if (err < 0)
+            return err;
+    }
+    if (! cm->can_multi_ch) {
+        err = snd_ctl_add(cm->card, snd_ctl_new1(&snd_cmipci_nomulti_switch, cm));
+        if (err < 0)
+            return err;
+    }
+    if (cm->device == PCI_DEVICE_ID_CMEDIA_CM8738 ||
+        cm->device == PCI_DEVICE_ID_CMEDIA_CM8738B) {
+        sw = snd_cmipci_8738_mixer_switches;
+        for (idx = 0; idx < ARRAY_SIZE(snd_cmipci_8738_mixer_switches); idx++, sw++) {
+            err = snd_ctl_add(cm->card, snd_ctl_new1(sw, cm));
+            if (err < 0)
+                return err;
+        }
+        if (cm->can_ac3_hw) {
+            if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_cmipci_spdif_default, cm))) < 0)
+                return err;
+            kctl->id.device = pcm_spdif_device;
+            if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_cmipci_spdif_mask, cm))) < 0)
+                return err;
+            kctl->id.device = pcm_spdif_device;
+            if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_cmipci_spdif_stream, cm))) < 0)
+                return err;
+            kctl->id.device = pcm_spdif_device;
+        }
+        if (cm->chip_version <= 37) {
+            sw = snd_cmipci_old_mixer_switches;
+            for (idx = 0; idx < ARRAY_SIZE(snd_cmipci_old_mixer_switches); idx++, sw++) {
+                err = snd_ctl_add(cm->card, snd_ctl_new1(sw, cm));
+                if (err < 0)
+                    return err;
+            }
+        }
+    }
+    if (cm->chip_version >= 39) {
+        sw = snd_cmipci_extra_mixer_switches;
+        for (idx = 0; idx < ARRAY_SIZE(snd_cmipci_extra_mixer_switches); idx++, sw++) {
+            err = snd_ctl_add(cm->card, snd_ctl_new1(sw, cm));
+            if (err < 0)
+                return err;
+        }
+    }
+
+    /* card switches */
+    sw = snd_cmipci_control_switches;
+    for (idx = 0; idx < ARRAY_SIZE(snd_cmipci_control_switches); idx++, sw++) {
+        err = snd_ctl_add(cm->card, snd_ctl_new1(sw, cm));
+        if (err < 0)
+            return err;
+    }
+
+    for (idx = 0; idx < CM_SAVED_MIXERS; idx++) {
+        struct snd_ctl_elem_id id;
+        struct snd_kcontrol *ctl;
+        memset(&id, 0, sizeof(id));
+        id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+        strcpy(id.name, cm_saved_mixer[idx].name);
+        if ((ctl = snd_ctl_find_id(cm->card, &id)) != NULL)
+            cm->mixer_res_ctl[idx] = ctl;
+    }
+
+    return 0;
+}
+
+
+/*
+ * proc interface
+ */
+
+static void snd_cmipci_proc_read(struct snd_info_entry *entry,
+                                 struct snd_info_buffer *buffer)
+{
+    struct cmipci *cm = entry->private_data;
+    int i;
+
+    snd_iprintf(buffer, "%s\n\n", cm->card->longname);
+    for (i = 0; i < 0x40; i++) {
+        int v = inb(cm->iobase + i);
+        if (i % 4 == 0)
+            snd_iprintf(buffer, "%02x: ", i);
+        snd_iprintf(buffer, "%02x", v);
+        if (i % 4 == 3)
+            snd_iprintf(buffer, "\n");
+        else
+            snd_iprintf(buffer, " ");
+    }
+}
+
+static void __devinit snd_cmipci_proc_init(struct cmipci *cm)
+{
+    struct snd_info_entry *entry;
+    if (! snd_card_proc_new(cm->card, "cmipci", &entry))
+        snd_info_set_text_ops(entry, cm, 1024, snd_cmipci_proc_read);
+}
+
+static struct pci_device_id snd_cmipci_ids[] __devinitdata = {
+    {PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8338A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+    {PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8338B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+    {PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8738, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+    {PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8738B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+    {PCI_VENDOR_ID_AL, PCI_DEVICE_ID_CMEDIA_CM8738, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+    {0,},
+};
+
+
+/*
+ * check chip version and capabilities
+ * driver name is modified according to the chip model
+ */
+static void __devinit query_chip(struct cmipci *cm)
+{
+    unsigned int detect;
+
+    /* check reg 0Ch, bit 24-31 */
+    detect = snd_cmipci_read(cm, CM_REG_INT_HLDCLR) & CM_CHIP_MASK2;
+    if (! detect) {
+        /* check reg 08h, bit 24-28 */
+        detect = snd_cmipci_read(cm, CM_REG_CHFORMAT) & CM_CHIP_MASK1;
+        if (! detect) {
+            cm->chip_version = 33;
+            cm->max_channels = 4;
+            if (cm->do_soft_ac3)
+                cm->can_ac3_sw = 1;
+            else
+                cm->can_ac3_hw = 1;
+            cm->has_dual_dac = 1;
+        } else {
+            //                    printk("detect = %x\n", detect);
+
+            cm->chip_version = 37;
+            cm->max_channels = 4;
+            cm->can_ac3_hw = 1;
+            cm->has_dual_dac = 1;
+            cm->can_multi_ch = 1;
+        }
+    } else {
+        /* check reg 0Ch, bit 26 */
+        if (detect & CM_CHIP_8768) {
+            cm->chip_version = 68;
+            cm->max_channels = 8;
+            cm->can_ac3_hw = 1;
+            cm->has_dual_dac = 1;
+            cm->can_multi_ch = 1;
+        } else if (detect & CM_CHIP_055) {
+            cm->chip_version = 55;
+            cm->max_channels  = 6;
+            cm->can_ac3_hw = 1;
+            cm->has_dual_dac = 1;
+            cm->can_multi_ch = 1;
+        } else if (detect & CM_CHIP_039) {
+            cm->chip_version = 39;
+            if (detect & CM_CHIP_039_6CH) /* 4 or 6 channels */
+                cm->max_channels  = 6;
+            else
+                cm->max_channels = 4;
+            cm->can_ac3_hw = 1;
+            cm->has_dual_dac = 1;
+            cm->can_multi_ch = 1;
+        } else {
+            printk(KERN_ERR "chip %x version not supported\n", detect);
+        }
+    }
+}
+
+static int snd_cmipci_free(struct cmipci *cm)
+{
+    if (cm->irq >= 0) {
+        snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_FM_EN);
+        snd_cmipci_clear_bit(cm, CM_REG_LEGACY_CTRL, CM_ENSPDOUT);
+        snd_cmipci_write(cm, CM_REG_INT_HLDCLR, 0);  /* disable ints */
+        snd_cmipci_ch_reset(cm, CM_CH_PLAY);
+        snd_cmipci_ch_reset(cm, CM_CH_CAPT);
+        snd_cmipci_write(cm, CM_REG_FUNCTRL0, 0); /* disable channels */
+        snd_cmipci_write(cm, CM_REG_FUNCTRL1, 0);
+
+        /* reset mixer */
+        snd_cmipci_mixer_write(cm, 0, 0);
+
+        synchronize_irq(cm->irq);
+
+        free_irq(cm->irq, cm);
+    }
+    pci_release_regions(cm->pci);
+    kfree(cm);
+    return 0;
+}
+
+static int snd_cmipci_dev_free(struct snd_device *device)
+{
+    struct cmipci *cm = device->device_data;
+    return snd_cmipci_free(cm);
+}
+
+static int __devinit snd_cmipci_create_fm(struct cmipci *cm, long fm_port)
+{
+    long iosynth;
+    unsigned int val;
+    struct snd_opl3 *opl3;
+    int err;
+
+    /* first try FM regs in PCI port range */
+    iosynth = cm->iobase + CM_REG_FM_PCI;
+    err = snd_opl3_create(cm->card, iosynth, iosynth + 2,
+                          OPL3_HW_OPL3, 1, &opl3);
+    if (err < 0) {
+        /* then try legacy ports */
+        val = snd_cmipci_read(cm, CM_REG_LEGACY_CTRL) & ~CM_FMSEL_MASK;
+        iosynth = fm_port;
+        switch (iosynth) {
+        case 0x3E8: val |= CM_FMSEL_3E8; break;
+        case 0x3E0: val |= CM_FMSEL_3E0; break;
+        case 0x3C8: val |= CM_FMSEL_3C8; break;
+        case 0x388: val |= CM_FMSEL_388; break;
+        default:
+            return 0;
+        }
+        snd_cmipci_write(cm, CM_REG_LEGACY_CTRL, val);
+        /* enable FM */
+        snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_FM_EN);
+
+        if (snd_opl3_create(cm->card, iosynth, iosynth + 2,
+                            OPL3_HW_OPL3, 0, &opl3) < 0) {
+            printk(KERN_ERR "cmipci: no OPL device at %#lx, "
+                   "skipping...\n", iosynth);
+            /* disable FM */
+            snd_cmipci_write(cm, CM_REG_LEGACY_CTRL,
+                             val & ~CM_FMSEL_MASK);
+            snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_FM_EN);
+            return 0;
+        }
+    }
+    if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
+        printk(KERN_ERR "cmipci: cannot create OPL3 hwdep\n");
+        return err;
+    }
+    return 0;
+}
+
+static int __devinit snd_cmipci_create(struct snd_card *card, struct pci_dev *pci,
+                                       int dev, struct cmipci **rcmipci)
+{
+    struct cmipci *cm;
+    int err;
+    static struct snd_device_ops ops = {
+        .dev_free = snd_cmipci_dev_free,
+    };
+    unsigned int val = 0;
+    unsigned long iomidi;
+    int integrated_midi;
+    int pcm_index, pcm_spdif_index;
+
+    *rcmipci = NULL;
+
+    if ((err = pci_enable_device(pci)) < 0)
+        return err;
+
+    cm = kcalloc(1, sizeof(*cm), GFP_KERNEL);
+    if (cm == NULL)
+        return -ENOMEM;
+
+    spin_lock_init(&cm->reg_lock);
+    init_MUTEX(&cm->open_mutex);
+    cm->device = pci->device;
+    cm->card = card;
+    cm->pci = pci;
+    cm->irq = -1;
+    cm->channel[0].ch = 0;
+    cm->channel[1].ch = 1;
+    cm->channel[0].is_dac = cm->channel[1].is_dac = 1; /* dual DAC mode */
+
+    if ((err = pci_request_regions(pci, card->driver)) < 0) {
+        kfree(cm);
+        return err;
+    }
+    cm->iobase = pci_resource_start(pci, 0);
+    if (request_irq(pci->irq, snd_cmipci_interrupt, SA_INTERRUPT|SA_SHIRQ, card->driver, cm)) {
+        snd_printk("unable to grab IRQ %d\n", pci->irq);
+        err = -EBUSY;
+        goto __error;
+    }
+    cm->irq = pci->irq;
+
+    pci_set_master(cm->pci);
+
+    /*
+     * check chip version, max channels and capabilities
+     */
+
+    cm->chip_version = 0;
+    cm->max_channels = 2;
+    cm->do_soft_ac3 = soft_ac3[dev];
+    if (pci->device != PCI_DEVICE_ID_CMEDIA_CM8338A &&
+        pci->device != PCI_DEVICE_ID_CMEDIA_CM8338B)
+        query_chip(cm);
+    /* added -MCx suffix for chip supporting multi-channels */
+    if (cm->can_multi_ch)
+        sprintf(cm->card->driver + strlen(cm->card->driver),
+                "-MC%d", cm->max_channels);
+    else if (cm->can_ac3_sw)
+        strcpy(cm->card->driver + strlen(cm->card->driver), "-SWIEC");
+    cm->dig_status = SNDRV_PCM_DEFAULT_CON_SPDIF;
+    cm->dig_pcm_status = SNDRV_PCM_DEFAULT_CON_SPDIF;
+
+#if CM_CH_PLAY == 1
+    cm->ctrl = CM_CHADC0;	/* default FUNCNTRL0 */
+#else
+    cm->ctrl = CM_CHADC1;	/* default FUNCNTRL0 */
+#endif
+
+    /* initialize codec registers */
+    snd_cmipci_write(cm, CM_REG_INT_HLDCLR, 0);	/* disable ints */
+    snd_cmipci_ch_reset(cm, CM_CH_PLAY);
+    snd_cmipci_ch_reset(cm, CM_CH_CAPT);
+    snd_cmipci_write(cm, CM_REG_FUNCTRL0, 0);	/* disable channels */
+    snd_cmipci_write(cm, CM_REG_FUNCTRL1, 0);
+
+    snd_cmipci_write(cm, CM_REG_CHFORMAT, 0);
+    snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_ENDBDAC|CM_N4SPK3D);
+#if CM_CH_PLAY == 1
+    snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_XCHGDAC);
+#else
+    snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_XCHGDAC);
+#endif
+    /* Set Bus Master Request */
+    snd_cmipci_set_bit(cm, CM_REG_FUNCTRL1, CM_BREQ);
+
+    /* Assume TX and compatible chip set (Autodetection required for VX chip sets) */
+    switch (pci->device) {
+    case PCI_DEVICE_ID_CMEDIA_CM8738:
+    case PCI_DEVICE_ID_CMEDIA_CM8738B:
+        /* PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82437VX */
+        if (! pci_find_device(0x8086, 0x7030, NULL))
+            snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_TXVX);
+        break;
+    default:
+        break;
+    }
+
+    integrated_midi = snd_cmipci_read_b(cm, CM_REG_MPU_PCI) != 0xff;
+    if (integrated_midi)
+        iomidi = cm->iobase + CM_REG_MPU_PCI;
+    else {
+        iomidi = mpu_port[dev];
+        switch (iomidi) {
+        case 0x320: val = CM_VMPU_320; break;
+        case 0x310: val = CM_VMPU_310; break;
+        case 0x300: val = CM_VMPU_300; break;
+        case 0x330: val = CM_VMPU_330; break;
+        default:
+            iomidi = 0; break;
+        }
+        if (iomidi > 0) {
+            snd_cmipci_write(cm, CM_REG_LEGACY_CTRL, val);
+            /* enable UART */
+            snd_cmipci_set_bit(cm, CM_REG_FUNCTRL1, CM_UART_EN);
+        }
+    }
+
+    if ((err = snd_cmipci_create_fm(cm, fm_port[dev])) < 0)
+        return err;
+
+    /* reset mixer */
+    snd_cmipci_mixer_write(cm, 0, 0);
+
+    snd_cmipci_proc_init(cm);
+
+    /* create pcm devices */
+    pcm_index = pcm_spdif_index = 0;
+#ifdef SIX_CH_HACK
+    if (cm->has_dual_dac) {
+        if ((err = snd_cmipci_pcm2_new(cm, pcm_index)) < 0)
+            goto __error;
+        pcm_index++;
+    }
+
+    if ((err = snd_cmipci_pcm_new(cm, pcm_index)) < 0)
+        goto __error;
+    pcm_index++;
+#else
+    if ((err = snd_cmipci_pcm_new(cm, pcm_index)) < 0)
+        goto __error;
+    pcm_index++;
+
+    if (cm->has_dual_dac) {
+        if ((err = snd_cmipci_pcm2_new(cm, pcm_index)) < 0)
+            goto __error;
+        pcm_index++;
+    }
+#endif
+    if (cm->can_ac3_hw || cm->can_ac3_sw) {
+        pcm_spdif_index = pcm_index;
+        if ((err = snd_cmipci_pcm_spdif_new(cm, pcm_index)) < 0)
+            goto __error;
+    }
+
+    /* create mixer interface & switches */
+    if ((err = snd_cmipci_mixer_new(cm, pcm_spdif_index)) < 0)
+        goto __error;
+
+    if (iomidi > 0) {
+        if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_CMIPCI,
+                                       iomidi,
+                                       (integrated_midi ?
+                                        MPU401_INFO_INTEGRATED : 0),
+                                       cm->irq, 0, &cm->rmidi)) < 0) {
+            printk(KERN_ERR "cmipci: no UART401 device at 0x%lx\n", iomidi);
+        }
+    }
+
+    if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, cm, &ops)) < 0) {
+        snd_cmipci_free(cm);
+        return err;
+    }
+#ifdef USE_VAR48KRATE
+    for (val = 0; val < ARRAY_SIZE(rates); val++)
+        snd_cmipci_set_pll(cm, rates[val], val);
+
+    /*
+     * (Re-)Enable external switch spdo_48k
+     */
+    snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_SPDIF48K|CM_SPDF_AC97);
+#endif /* USE_VAR48KRATE */
+
+    *rcmipci = cm;
+    return 0;
+
+    __error:
+        snd_cmipci_free(cm);
+        return err;
+}
+
+/*
+ */
+
+MODULE_DEVICE_TABLE(pci, snd_cmipci_ids);
+
+static int __devinit snd_cmipci_probe(struct pci_dev *pci,
+                                      const struct pci_device_id *pci_id)
+{
+    static int dev;
+    struct snd_card *card;
+    struct cmipci *cm;
+    int err;
+#ifdef DEBUG
+    dprintf(("snd_cmipci_probe"));
+#endif
+    if (dev >= SNDRV_CARDS)
+        return -ENODEV;
+    if (! enable[dev]) {
+        dev++;
+        return -ENOENT;
+    }
+
+    card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+    if (card == NULL)
+        return -ENOMEM;
+
+    switch (pci->device) {
+    case PCI_DEVICE_ID_CMEDIA_CM8738:
+    case PCI_DEVICE_ID_CMEDIA_CM8738B:
+        strcpy(card->driver, "CMI8738");
+        break;
+    case PCI_DEVICE_ID_CMEDIA_CM8338A:
+    case PCI_DEVICE_ID_CMEDIA_CM8338B:
+        strcpy(card->driver, "CMI8338");
+        break;
+    default:
+        strcpy(card->driver, "CMIPCI");
+        break;
+    }
+
+    if ((err = snd_cmipci_create(card, pci, dev, &cm)) < 0) {
+        snd_card_free(card);
+        return err;
+    }
+    card->private_data = cm;
+
+    sprintf(card->shortname, "C-Media PCI %s", card->driver);
+    sprintf(card->longname, "%s (model %d) at 0x%lx, irq %i",
+            card->shortname,
+            cm->chip_version,
+            cm->iobase,
+            cm->irq);
+
+
+#ifdef DEBUG
+    dprintf(("%s is detected\n", card->longname));
+#endif
+
+    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_cmipci_remove(struct pci_dev *pci)
+{
+    snd_card_free(pci_get_drvdata(pci));
+    pci_set_drvdata(pci, NULL);
+}
+
+#ifdef CONFIG_PM
+/*
+ * power management
+ */
+static unsigned char saved_regs[] = {
+    CM_REG_FUNCTRL1, CM_REG_CHFORMAT, CM_REG_LEGACY_CTRL, CM_REG_MISC_CTRL,
+    CM_REG_MIXER0, CM_REG_MIXER1, CM_REG_MIXER2, CM_REG_MIXER3, CM_REG_PLL,
+    CM_REG_CH0_FRAME1, CM_REG_CH0_FRAME2,
+    CM_REG_CH1_FRAME1, CM_REG_CH1_FRAME2, CM_REG_EXT_MISC,
+    CM_REG_INT_STATUS, CM_REG_INT_HLDCLR, CM_REG_FUNCTRL0,
+};
+
+static unsigned char saved_mixers[] = {
+    SB_DSP4_MASTER_DEV, SB_DSP4_MASTER_DEV + 1,
+    SB_DSP4_PCM_DEV, SB_DSP4_PCM_DEV + 1,
+    SB_DSP4_SYNTH_DEV, SB_DSP4_SYNTH_DEV + 1,
+    SB_DSP4_CD_DEV, SB_DSP4_CD_DEV + 1,
+    SB_DSP4_LINE_DEV, SB_DSP4_LINE_DEV + 1,
+    SB_DSP4_MIC_DEV, SB_DSP4_SPEAKER_DEV,
+    CM_REG_EXTENT_IND, SB_DSP4_OUTPUT_SW,
+    SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT,
+};
+
+static int snd_cmipci_suspend(struct pci_dev *pci, pm_message_t state)
+{
+    struct snd_card *card = pci_get_drvdata(pci);
+    struct cmipci *cm = card->private_data;
+    int i;
+
+    snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+
+    snd_pcm_suspend_all(cm->pcm);
+    snd_pcm_suspend_all(cm->pcm2);
+    snd_pcm_suspend_all(cm->pcm_spdif);
+
+    /* save registers */
+    for (i = 0; i < ARRAY_SIZE(saved_regs); i++)
+        cm->saved_regs[i] = snd_cmipci_read(cm, saved_regs[i]);
+    for (i = 0; i < ARRAY_SIZE(saved_mixers); i++)
+        cm->saved_mixers[i] = snd_cmipci_mixer_read(cm, saved_mixers[i]);
+
+    /* disable ints */
+    snd_cmipci_write(cm, CM_REG_INT_HLDCLR, 0);
+
+    pci_set_power_state(pci, PCI_D3hot);
+    pci_disable_device(pci);
+    pci_save_state(pci);
+    return 0;
+}
+
+static int snd_cmipci_resume(struct pci_dev *pci)
+{
+    struct snd_card *card = pci_get_drvdata(pci);
+    struct cmipci *cm = card->private_data;
+    int i;
+
+    pci_restore_state(pci);
+    pci_enable_device(pci);
+    pci_set_power_state(pci, PCI_D0);
+    pci_set_master(pci);
+
+    /* reset / initialize to a sane state */
+    snd_cmipci_write(cm, CM_REG_INT_HLDCLR, 0);
+    snd_cmipci_ch_reset(cm, CM_CH_PLAY);
+    snd_cmipci_ch_reset(cm, CM_CH_CAPT);
+    snd_cmipci_mixer_write(cm, 0, 0);
+
+    /* restore registers */
+    for (i = 0; i < ARRAY_SIZE(saved_regs); i++)
+        snd_cmipci_write(cm, saved_regs[i], cm->saved_regs[i]);
+    for (i = 0; i < ARRAY_SIZE(saved_mixers); i++)
+        snd_cmipci_mixer_write(cm, saved_mixers[i], cm->saved_mixers[i]);
+
+    snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+    return 0;
+}
+#endif /* CONFIG_PM */
+
+static struct pci_driver driver = {
+	.name = "C-Media PCI",
+	.id_table = snd_cmipci_ids,
+	.probe = snd_cmipci_probe,
+        .remove = snd_cmipci_remove,
+#ifdef CONFIG_PM
+        .suspend = snd_cmipci_suspend,
+        .resume = snd_cmipci_resume,
+  	 #endif
+};
+
+static int __init alsa_card_cmipci_init(void)
+{
+    int err;
+
+#ifdef TARGET_OS2
+    if (midi_port > 0) mpu_port[0] = midi_port;
+#endif
+
+    if ((err = pci_module_init(&driver)) < 0) {
+#ifdef MODULE
+        //		printk(KERN_ERR "C-Media PCI soundcard not found or device busy\n");
+#endif
+        return err;
+    }
+    return 0;
+}
+
+static void __exit alsa_card_cmipci_exit(void)
+{
+    pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_cmipci_init)
+module_exit(alsa_card_cmipci_exit)
+
+#ifndef MODULE
+
+/* format is: snd-cmipci=enable,index,id,
+ mpu_port,fm_port */
+
+static int __init alsa_card_cmipci_setup(char *str)
+{
+    static unsigned __initdata nr_dev = 0;
+
+    if (nr_dev >= SNDRV_CARDS)
+        return 0;
+    (void)(get_option(&str,&enable[nr_dev]) == 2 &&
+           get_option(&str,&index[nr_dev]) == 2 &&
+           get_id(&str,&id[nr_dev]) == 2 &&
+           get_option(&str,(int *)&mpu_port[nr_dev]) == 2 &&
+           get_option(&str,(int *)&fm_port[nr_dev]) == 2);
+    nr_dev++;
+    return 1;
+}
+
+__setup("snd-cmipci=", alsa_card_cmipci_setup);
+
+#endif /* ifndef MODULE */
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/cs4281.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/cs4281.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/cs4281.c	(revision 91)
@@ -0,0 +1,2179 @@
+/*
+ *  Driver for Cirrus Logic CS4281 based PCI soundcard
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>,
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <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/control.h>
+#include <sound/pcm.h>
+#include <sound/rawmidi.h>
+#include <sound/ac97_codec.h>
+#include <sound/opl3.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+#ifndef LINUX_2_2
+#include <linux/gameport.h>
+#endif
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Cirrus Logic CS4281");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{Cirrus Logic,CS4281}}");
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;	/* Enable switches */
+static int dual_codec[SNDRV_CARDS];	/* dual codec */
+
+MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(index, "Index value for CS4281 soundcard.");
+MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC);
+MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+MODULE_PARM_DESC(id, "ID string for CS4281 soundcard.");
+MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC);
+MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(enable, "Enable CS4281 soundcard.");
+MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(dual_codec, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(dual_codec, "Secondary Codec ID (0 = disabled).");
+MODULE_PARM_SYNTAX(dual_codec, SNDRV_ENABLED ",allows:{{0,3}}");
+
+/*
+ *
+ */
+
+#ifndef PCI_VENDOR_ID_CIRRUS
+#define PCI_VENDOR_ID_CIRRUS            0x1013
+#endif
+#ifndef PCI_DEVICE_ID_CIRRUS_4281
+#define PCI_DEVICE_ID_CIRRUS_4281	0x6005
+#endif
+
+/*
+ *  Direct registers
+ */
+
+#define CS4281_BA0_SIZE		0x1000
+#define CS4281_BA1_SIZE		0x10000
+
+/*
+ *  BA0 registers
+ */
+#define BA0_HISR		0x0000	/* Host Interrupt Status Register */
+#define BA0_HISR_INTENA		(1<<31)	/* Internal Interrupt Enable Bit */
+#define BA0_HISR_MIDI		(1<<22)	/* MIDI port interrupt */
+#define BA0_HISR_FIFOI		(1<<20)	/* FIFO polled interrupt */
+#define BA0_HISR_DMAI		(1<<18)	/* DMA interrupt (half or end) */
+#define BA0_HISR_FIFO(c)	(1<<(12+(c))) /* FIFO channel interrupt */
+#define BA0_HISR_DMA(c)		(1<<(8+(c)))  /* DMA channel interrupt */
+#define BA0_HISR_GPPI		(1<<5)	/* General Purpose Input (Primary chip) */
+#define BA0_HISR_GPSI		(1<<4)	/* General Purpose Input (Secondary chip) */
+#define BA0_HISR_GP3I		(1<<3)	/* GPIO3 pin Interrupt */
+#define BA0_HISR_GP1I		(1<<2)	/* GPIO1 pin Interrupt */
+#define BA0_HISR_VUPI		(1<<1)	/* VOLUP pin Interrupt */
+#define BA0_HISR_VDNI		(1<<0)	/* VOLDN pin Interrupt */
+
+#define BA0_HICR		0x0008	/* Host Interrupt Control Register */
+#define BA0_HICR_CHGM		(1<<1)	/* INTENA Change Mask */
+#define BA0_HICR_IEV		(1<<0)	/* INTENA Value */
+#define BA0_HICR_EOI		(3<<0)	/* End of Interrupt command */
+
+#define BA0_HIMR		0x000c	/* Host Interrupt Mask Register */
+/* Use same contants as for BA0_HISR */
+
+#define BA0_IIER		0x0010	/* ISA Interrupt Enable Register */
+
+#define BA0_HDSR0		0x00f0	/* Host DMA Engine 0 Status Register */
+#define BA0_HDSR1		0x00f4	/* Host DMA Engine 1 Status Register */
+#define BA0_HDSR2		0x00f8	/* Host DMA Engine 2 Status Register */
+#define BA0_HDSR3		0x00fc	/* Host DMA Engine 3 Status Register */
+
+#define BA0_HDSR_CH1P		(1<<25)	/* Channel 1 Pending */
+#define BA0_HDSR_CH2P		(1<<24)	/* Channel 2 Pending */
+#define BA0_HDSR_DHTC		(1<<17)	/* DMA Half Terminal Count */
+#define BA0_HDSR_DTC		(1<<16)	/* DMA Terminal Count */
+#define BA0_HDSR_DRUN		(1<<15)	/* DMA Running */
+#define BA0_HDSR_RQ		(1<<7)	/* Pending Request */
+
+#define BA0_DCA0		0x0110	/* Host DMA Engine 0 Current Address */
+#define BA0_DCC0		0x0114	/* Host DMA Engine 0 Current Count */
+#define BA0_DBA0		0x0118	/* Host DMA Engine 0 Base Address */
+#define BA0_DBC0		0x011c	/* Host DMA Engine 0 Base Count */
+#define BA0_DCA1		0x0120	/* Host DMA Engine 1 Current Address */
+#define BA0_DCC1		0x0124	/* Host DMA Engine 1 Current Count */
+#define BA0_DBA1		0x0128	/* Host DMA Engine 1 Base Address */
+#define BA0_DBC1		0x012c	/* Host DMA Engine 1 Base Count */
+#define BA0_DCA2		0x0130	/* Host DMA Engine 2 Current Address */
+#define BA0_DCC2		0x0134	/* Host DMA Engine 2 Current Count */
+#define BA0_DBA2		0x0138	/* Host DMA Engine 2 Base Address */
+#define BA0_DBC2		0x013c	/* Host DMA Engine 2 Base Count */
+#define BA0_DCA3		0x0140	/* Host DMA Engine 3 Current Address */
+#define BA0_DCC3		0x0144	/* Host DMA Engine 3 Current Count */
+#define BA0_DBA3		0x0148	/* Host DMA Engine 3 Base Address */
+#define BA0_DBC3		0x014c	/* Host DMA Engine 3 Base Count */
+#define BA0_DMR0		0x0150	/* Host DMA Engine 0 Mode */
+#define BA0_DCR0		0x0154	/* Host DMA Engine 0 Command */
+#define BA0_DMR1		0x0158	/* Host DMA Engine 1 Mode */
+#define BA0_DCR1		0x015c	/* Host DMA Engine 1 Command */
+#define BA0_DMR2		0x0160	/* Host DMA Engine 2 Mode */
+#define BA0_DCR2		0x0164	/* Host DMA Engine 2 Command */
+#define BA0_DMR3		0x0168	/* Host DMA Engine 3 Mode */
+#define BA0_DCR3		0x016c	/* Host DMA Engine 3 Command */
+
+#define BA0_DMR_DMA		(1<<29)	/* Enable DMA mode */
+#define BA0_DMR_POLL		(1<<28)	/* Enable poll mode */
+#define BA0_DMR_TBC		(1<<25)	/* Transfer By Channel */
+#define BA0_DMR_CBC		(1<<24)	/* Count By Channel (0 = frame resolution) */
+#define BA0_DMR_SWAPC		(1<<22)	/* Swap Left/Right Channels */
+#define BA0_DMR_SIZE20		(1<<20)	/* Sample is 20-bit */
+#define BA0_DMR_USIGN		(1<<19)	/* Unsigned */
+#define BA0_DMR_BEND		(1<<18)	/* Big Endian */
+#define BA0_DMR_MONO		(1<<17)	/* Mono */
+#define BA0_DMR_SIZE8		(1<<16)	/* Sample is 8-bit */
+#define BA0_DMR_TYPE_DEMAND	(0<<6)
+#define BA0_DMR_TYPE_SINGLE	(1<<6)
+#define BA0_DMR_TYPE_BLOCK	(2<<6)
+#define BA0_DMR_TYPE_CASCADE	(3<<6)	/* Not supported */
+#define BA0_DMR_DEC		(1<<5)	/* Access Increment (0) or Decrement (1) */
+#define BA0_DMR_AUTO		(1<<4)	/* Auto-Initialize */
+#define BA0_DMR_TR_VERIFY	(0<<2)	/* Verify Transfer */
+#define BA0_DMR_TR_WRITE	(1<<2)	/* Write Transfer */
+#define BA0_DMR_TR_READ		(2<<2)	/* Read Transfer */
+
+#define BA0_DCR_HTCIE		(1<<17)	/* Half Terminal Count Interrupt */
+#define BA0_DCR_TCIE		(1<<16)	/* Terminal Count Interrupt */
+#define BA0_DCR_MSK		(1<<0)	/* DMA Mask bit */
+
+#define BA0_FCR0		0x0180	/* FIFO Control 0 */
+#define BA0_FCR1		0x0184	/* FIFO Control 1 */
+#define BA0_FCR2		0x0188	/* FIFO Control 2 */
+#define BA0_FCR3		0x018c	/* FIFO Control 3 */
+
+#define BA0_FCR_FEN		(1<<31)	/* FIFO Enable bit */
+#define BA0_FCR_DACZ		(1<<30)	/* DAC Zero */
+#define BA0_FCR_PSH		(1<<29)	/* Previous Sample Hold */
+#define BA0_FCR_RS(x)		(((x)&0x1f)<<24) /* Right Slot Mapping */
+#define BA0_FCR_LS(x)		(((x)&0x1f)<<16) /* Left Slot Mapping */
+#define BA0_FCR_SZ(x)		(((x)&0x7f)<<8)	/* FIFO buffer size (in samples) */
+#define BA0_FCR_OF(x)		(((x)&0x7f)<<0)	/* FIFO starting offset (in samples) */
+
+#define BA0_FPDR0		0x0190	/* FIFO Polled Data 0 */
+#define BA0_FPDR1		0x0194	/* FIFO Polled Data 1 */
+#define BA0_FPDR2		0x0198	/* FIFO Polled Data 2 */
+#define BA0_FPDR3		0x019c	/* FIFO Polled Data 3 */
+
+#define BA0_FCHS		0x020c	/* FIFO Channel Status */
+#define BA0_FCHS_RCO(x)		(1<<(7+(((x)&3)<<3))) /* Right Channel Out */
+#define BA0_FCHS_LCO(x)		(1<<(6+(((x)&3)<<3))) /* Left Channel Out */
+#define BA0_FCHS_MRP(x)		(1<<(5+(((x)&3)<<3))) /* Move Read Pointer */
+#define BA0_FCHS_FE(x)		(1<<(4+(((x)&3)<<3))) /* FIFO Empty */
+#define BA0_FCHS_FF(x)		(1<<(3+(((x)&3)<<3))) /* FIFO Full */
+#define BA0_FCHS_IOR(x)		(1<<(2+(((x)&3)<<3))) /* Internal Overrun Flag */
+#define BA0_FCHS_RCI(x)		(1<<(1+(((x)&3)<<3))) /* Right Channel In */
+#define BA0_FCHS_LCI(x)		(1<<(0+(((x)&3)<<3))) /* Left Channel In */
+
+#define BA0_FSIC0		0x0210	/* FIFO Status and Interrupt Control 0 */
+#define BA0_FSIC1		0x0214	/* FIFO Status and Interrupt Control 1 */
+#define BA0_FSIC2		0x0218	/* FIFO Status and Interrupt Control 2 */
+#define BA0_FSIC3		0x021c	/* FIFO Status and Interrupt Control 3 */
+
+#define BA0_FSIC_FIC(x)		(((x)&0x7f)<<24) /* FIFO Interrupt Count */
+#define BA0_FSIC_FORIE		(1<<23) /* FIFO OverRun Interrupt Enable */
+#define BA0_FSIC_FURIE		(1<<22) /* FIFO UnderRun Interrupt Enable */
+#define BA0_FSIC_FSCIE		(1<<16)	/* FIFO Sample Count Interrupt Enable */
+#define BA0_FSIC_FSC(x)		(((x)&0x7f)<<8) /* FIFO Sample Count */
+#define BA0_FSIC_FOR		(1<<7)	/* FIFO OverRun */
+#define BA0_FSIC_FUR		(1<<6)	/* FIFO UnderRun */
+#define BA0_FSIC_FSCR		(1<<0)	/* FIFO Sample Count Reached */
+
+#define BA0_PMCS		0x0344	/* Power Management Control/Status */
+#define BA0_CWPR		0x03e0	/* Configuration Write Protect */
+
+#define BA0_EPPMC		0x03e4	/* Extended PCI Power Management Control */
+#define BA0_EPPMC_FPDN          (1<<14) /* Full Power DowN */
+
+#define BA0_GPIOR		0x03e8	/* GPIO Pin Interface Register */
+
+#define BA0_SPMC		0x03ec	/* Serial Port Power Management Control (& ASDIN2 enable) */
+#define BA0_SPMC_GIPPEN		(1<<15)	/* GP INT Primary PME# Enable */
+#define BA0_SPMC_GISPEN		(1<<14)	/* GP INT Secondary PME# Enable */
+#define BA0_SPMC_EESPD		(1<<9)	/* EEPROM Serial Port Disable */
+#define BA0_SPMC_ASDI2E		(1<<8)	/* ASDIN2 Enable */
+#define BA0_SPMC_ASDO		(1<<7)	/* Asynchronous ASDOUT Assertion */
+#define BA0_SPMC_WUP2		(1<<3)	/* Wakeup for Secondary Input */
+#define BA0_SPMC_WUP1		(1<<2)	/* Wakeup for Primary Input */
+#define BA0_SPMC_ASYNC		(1<<1)	/* Asynchronous ASYNC Assertion */
+#define BA0_SPMC_RSTN		(1<<0)	/* Reset Not! */
+
+#define BA0_CFLR		0x03f0	/* Configuration Load Register (EEPROM or BIOS) */
+#define BA0_CFLR_DEFAULT	0x00000001 /* CFLR must be in AC97 link mode */
+#define BA0_IISR		0x03f4	/* ISA Interrupt Select */
+#define BA0_TMS			0x03f8	/* Test Register */
+#define BA0_SSVID		0x03fc	/* Subsystem ID register */
+
+#define BA0_CLKCR1		0x0400	/* Clock Control Register 1 */
+#define BA0_CLKCR1_CLKON	(1<<25)	/* Read Only */
+#define BA0_CLKCR1_DLLRDY	(1<<24)	/* DLL Ready */
+#define BA0_CLKCR1_DLLOS	(1<<6)	/* DLL Output Select */
+#define BA0_CLKCR1_SWCE		(1<<5)	/* Clock Enable */
+#define BA0_CLKCR1_DLLP		(1<<4)	/* DLL PowerUp */
+#define BA0_CLKCR1_DLLSS	(((x)&3)<<3) /* DLL Source Select */
+
+#define BA0_FRR			0x0410	/* Feature Reporting Register */
+#define BA0_SLT12O		0x041c	/* Slot 12 GPIO Output Register for AC-Link */
+
+#define BA0_SERMC		0x0420	/* Serial Port Master Control */
+#define BA0_SERMC_FCRN		(1<<27)	/* Force Codec Ready Not */
+#define BA0_SERMC_ODSEN2	(1<<25)	/* On-Demand Support Enable ASDIN2 */
+#define BA0_SERMC_ODSEN1	(1<<24)	/* On-Demand Support Enable ASDIN1 */
+#define BA0_SERMC_SXLB		(1<<21)	/* ASDIN2 to ASDOUT Loopback */
+#define BA0_SERMC_SLB		(1<<20)	/* ASDOUT to ASDIN2 Loopback */
+#define BA0_SERMC_LOVF		(1<<19)	/* Loopback Output Valid Frame bit */
+#define BA0_SERMC_TCID(x)	(((x)&3)<<16) /* Target Secondary Codec ID */
+#define BA0_SERMC_PXLB		(5<<1)	/* Primary Port External Loopback */
+#define BA0_SERMC_PLB		(4<<1)	/* Primary Port Internal Loopback */
+#define BA0_SERMC_PTC		(7<<1)	/* Port Timing Configuration */
+#define BA0_SERMC_PTC_AC97	(1<<1)	/* AC97 mode */
+#define BA0_SERMC_MSPE		(1<<0)	/* Master Serial Port Enable */
+
+#define BA0_SERC1		0x0428	/* Serial Port Configuration 1 */
+#define BA0_SERC1_SO1F(x)	(((x)&7)>>1) /* Primary Output Port Format */
+#define BA0_SERC1_AC97		(1<<1)
+#define BA0_SERC1_SO1EN		(1<<0)	/* Primary Output Port Enable */
+
+#define BA0_SERC2		0x042c	/* Serial Port Configuration 2 */
+#define BA0_SERC2_SI1F(x)	(((x)&7)>>1) /* Primary Input Port Format */
+#define BA0_SERC2_AC97		(1<<1)
+#define BA0_SERC2_SI1EN		(1<<0)	/* Primary Input Port Enable */
+
+#define BA0_SLT12M		0x045c	/* Slot 12 Monitor Register for Primary AC-Link */
+
+#define BA0_ACCTL		0x0460	/* AC'97 Control */
+#define BA0_ACCTL_TC		(1<<6)	/* Target Codec */
+#define BA0_ACCTL_CRW		(1<<4)	/* 0=Write, 1=Read Command */
+#define BA0_ACCTL_DCV		(1<<3)	/* Dynamic Command Valid */
+#define BA0_ACCTL_VFRM		(1<<2)	/* Valid Frame */
+#define BA0_ACCTL_ESYN		(1<<1)	/* Enable Sync */
+
+#define BA0_ACSTS		0x0464	/* AC'97 Status */
+#define BA0_ACSTS_VSTS		(1<<1)	/* Valid Status */
+#define BA0_ACSTS_CRDY		(1<<0)	/* Codec Ready */
+
+#define BA0_ACOSV		0x0468	/* AC'97 Output Slot Valid */
+#define BA0_ACOSV_SLV(x)	(1<<((x)-3))
+
+#define BA0_ACCAD		0x046c	/* AC'97 Command Address */
+#define BA0_ACCDA		0x0470	/* AC'97 Command Data */
+
+#define BA0_ACISV		0x0474	/* AC'97 Input Slot Valid */
+#define BA0_ACISV_SLV(x)	(1<<((x)-3))
+
+#define BA0_ACSAD		0x0478	/* AC'97 Status Address */
+#define BA0_ACSDA		0x047c	/* AC'97 Status Data */
+#define BA0_JSPT		0x0480	/* Joystick poll/trigger */
+#define BA0_JSCTL		0x0484	/* Joystick control */
+#define BA0_JSC1		0x0488	/* Joystick control */
+#define BA0_JSC2		0x048c	/* Joystick control */
+#define BA0_JSIO		0x04a0
+
+#define BA0_MIDCR		0x0490	/* MIDI Control */
+#define BA0_MIDCR_MRST		(1<<5)	/* Reset MIDI Interface */
+#define BA0_MIDCR_MLB		(1<<4)	/* MIDI Loop Back Enable */
+#define BA0_MIDCR_TIE		(1<<3)	/* MIDI Transmuit Interrupt Enable */
+#define BA0_MIDCR_RIE		(1<<2)	/* MIDI Receive Interrupt Enable */
+#define BA0_MIDCR_RXE		(1<<1)	/* MIDI Receive Enable */
+#define BA0_MIDCR_TXE		(1<<0)	/* MIDI Transmit Enable */
+
+#define BA0_MIDCMD		0x0494	/* MIDI Command (wo) */
+
+#define BA0_MIDSR		0x0494	/* MIDI Status (ro) */
+#define BA0_MIDSR_RDA		(1<<15)	/* Sticky bit (RBE 1->0) */
+#define BA0_MIDSR_TBE		(1<<14) /* Sticky bit (TBF 0->1) */
+#define BA0_MIDSR_RBE		(1<<7)	/* Receive Buffer Empty */
+#define BA0_MIDSR_TBF		(1<<6)	/* Transmit Buffer Full */
+
+#define BA0_MIDWP		0x0498	/* MIDI Write */
+#define BA0_MIDRP		0x049c	/* MIDI Read (ro) */
+
+#define BA0_AODSD1		0x04a8	/* AC'97 On-Demand Slot Disable for primary link (ro) */
+#define BA0_AODSD1_NDS(x)	(1<<((x)-3))
+
+#define BA0_AODSD2		0x04ac	/* AC'97 On-Demand Slot Disable for secondary link (ro) */
+#define BA0_AODSD2_NDS(x)	(1<<((x)-3))
+
+#define BA0_CFGI		0x04b0	/* Configure Interface (EEPROM interface) */
+#define BA0_SLT12M2		0x04dc	/* Slot 12 Monitor Register 2 for secondary AC-link */
+#define BA0_ACSTS2		0x04e4	/* AC'97 Status Register 2 */
+#define BA0_ACISV2		0x04f4	/* AC'97 Input Slot Valid Register 2 */
+#define BA0_ACSAD2		0x04f8	/* AC'97 Status Address Register 2 */
+#define BA0_ACSDA2		0x04fc	/* AC'97 Status Data Register 2 */
+#define BA0_FMSR		0x0730	/* FM Synthesis Status (ro) */
+#define BA0_B0AP		0x0730	/* FM Bank 0 Address Port (wo) */
+#define BA0_FMDP		0x0734	/* FM Data Port */
+#define BA0_B1AP		0x0738	/* FM Bank 1 Address Port */
+#define BA0_B1DP		0x073c	/* FM Bank 1 Data Port */
+
+#define BA0_SSPM		0x0740	/* Sound System Power Management */
+#define BA0_SSPM_MIXEN		(1<<6)	/* Playback SRC + FM/Wavetable MIX */
+#define BA0_SSPM_CSRCEN		(1<<5)	/* Capture Sample Rate Converter Enable */
+#define BA0_SSPM_PSRCEN		(1<<4)	/* Playback Sample Rate Converter Enable */
+#define BA0_SSPM_JSEN		(1<<3)	/* Joystick Enable */
+#define BA0_SSPM_ACLEN		(1<<2)	/* Serial Port Engine and AC-Link Enable */
+#define BA0_SSPM_FMEN		(1<<1)	/* FM Synthesis Block Enable */
+
+#define BA0_DACSR		0x0744	/* DAC Sample Rate - Playback SRC */
+#define BA0_ADCSR		0x0748	/* ADC Sample Rate - Capture SRC */
+
+#define BA0_SSCR		0x074c	/* Sound System Control Register */
+#define BA0_SSCR_HVS1		(1<<23)	/* Hardwave Volume Step (0=1,1=2) */
+#define BA0_SSCR_MVCS		(1<<19)	/* Master Volume Codec Select */
+#define BA0_SSCR_MVLD		(1<<18)	/* Master Volume Line Out Disable */
+#define BA0_SSCR_MVAD		(1<<17)	/* Master Volume Alternate Out Disable */
+#define BA0_SSCR_MVMD		(1<<16)	/* Master Volume Mono Out Disable */
+#define BA0_SSCR_XLPSRC		(1<<8)	/* External SRC Loopback Mode */
+#define BA0_SSCR_LPSRC		(1<<7)	/* SRC Loopback Mode */
+#define BA0_SSCR_CDTX		(1<<5)	/* CD Transfer Data */
+#define BA0_SSCR_HVC		(1<<3)	/* Harware Volume Control Enable */
+
+#define BA0_FMLVC		0x0754	/* FM Synthesis Left Volume Control */
+#define BA0_FMRVC		0x0758	/* FM Synthesis Right Volume Control */
+#define BA0_SRCSA		0x075c	/* SRC Slot Assignments */
+#define BA0_PPLVC		0x0760	/* PCM Playback Left Volume Control */
+#define BA0_PPRVC		0x0764	/* PCM Playback Right Volume Control */
+#define BA0_PASR		0x0768	/* playback sample rate */
+#define BA0_CASR		0x076C	/* capture sample rate */
+
+/* Source Slot Numbers - Playback */
+#define SRCSLOT_LEFT_PCM_PLAYBACK		0
+#define SRCSLOT_RIGHT_PCM_PLAYBACK		1
+#define SRCSLOT_PHONE_LINE_1_DAC		2
+#define SRCSLOT_CENTER_PCM_PLAYBACK		3
+#define SRCSLOT_LEFT_SURROUND_PCM_PLAYBACK	4
+#define SRCSLOT_RIGHT_SURROUND_PCM_PLAYBACK	5
+#define SRCSLOT_LFE_PCM_PLAYBACK		6
+#define SRCSLOT_PHONE_LINE_2_DAC		7
+#define SRCSLOT_HEADSET_DAC			8
+#define SRCSLOT_LEFT_WT				29  /* invalid for BA0_SRCSA */
+#define SRCSLOT_RIGHT_WT			30  /* invalid for BA0_SRCSA */
+
+/* Source Slot Numbers - Capture */
+#define SRCSLOT_LEFT_PCM_RECORD			10
+#define SRCSLOT_RIGHT_PCM_RECORD		11
+#define SRCSLOT_PHONE_LINE_1_ADC		12
+#define SRCSLOT_MIC_ADC				13
+#define SRCSLOT_PHONE_LINE_2_ADC		17
+#define SRCSLOT_HEADSET_ADC			18
+#define SRCSLOT_SECONDARY_LEFT_PCM_RECORD	20
+#define SRCSLOT_SECONDARY_RIGHT_PCM_RECORD	21
+#define SRCSLOT_SECONDARY_PHONE_LINE_1_ADC	22
+#define SRCSLOT_SECONDARY_MIC_ADC		23
+#define SRCSLOT_SECONDARY_PHONE_LINE_2_ADC	27
+#define SRCSLOT_SECONDARY_HEADSET_ADC		28
+
+/* Source Slot Numbers - Others */
+#define SRCSLOT_POWER_DOWN			31
+
+/* MIDI modes */
+#define CS4281_MODE_OUTPUT		(1<<0)
+#define CS4281_MODE_INPUT		(1<<1)
+
+/* joystick bits */
+/* Bits for JSPT */
+#define JSPT_CAX                                0x00000001
+#define JSPT_CAY                                0x00000002
+#define JSPT_CBX                                0x00000004
+#define JSPT_CBY                                0x00000008
+#define JSPT_BA1                                0x00000010
+#define JSPT_BA2                                0x00000020
+#define JSPT_BB1                                0x00000040
+#define JSPT_BB2                                0x00000080
+
+/* Bits for JSCTL */
+#define JSCTL_SP_MASK                           0x00000003
+#define JSCTL_SP_SLOW                           0x00000000
+#define JSCTL_SP_MEDIUM_SLOW                    0x00000001
+#define JSCTL_SP_MEDIUM_FAST                    0x00000002
+#define JSCTL_SP_FAST                           0x00000003
+#define JSCTL_ARE                               0x00000004
+
+/* Data register pairs masks */
+#define JSC1_Y1V_MASK                           0x0000FFFF
+#define JSC1_X1V_MASK                           0xFFFF0000
+#define JSC1_Y1V_SHIFT                          0
+#define JSC1_X1V_SHIFT                          16
+#define JSC2_Y2V_MASK                           0x0000FFFF
+#define JSC2_X2V_MASK                           0xFFFF0000
+#define JSC2_Y2V_SHIFT                          0
+#define JSC2_X2V_SHIFT                          16
+
+/* JS GPIO */
+#define JSIO_DAX                                0x00000001
+#define JSIO_DAY                                0x00000002
+#define JSIO_DBX                                0x00000004
+#define JSIO_DBY                                0x00000008
+#define JSIO_AXOE                               0x00000010
+#define JSIO_AYOE                               0x00000020
+#define JSIO_BXOE                               0x00000040
+#define JSIO_BYOE                               0x00000080
+
+/*
+ *
+ */
+
+typedef struct snd_cs4281 cs4281_t;
+typedef struct snd_cs4281_dma cs4281_dma_t;
+
+struct snd_cs4281_dma {
+    snd_pcm_substream_t *substream;
+    unsigned int regDBA;		/* offset to DBA register */
+    unsigned int regDCA;		/* offset to DCA register */
+    unsigned int regDBC;		/* offset to DBC register */
+    unsigned int regDCC;		/* offset to DCC register */
+    unsigned int regDMR;		/* offset to DMR register */
+    unsigned int regDCR;		/* offset to DCR register */
+    unsigned int regHDSR;		/* offset to HDSR register */
+    unsigned int regFCR;		/* offset to FCR register */
+    unsigned int regFSIC;		/* offset to FSIC register */
+    unsigned int valDMR;		/* DMA mode */
+    unsigned int valDCR;		/* DMA command */
+    unsigned int valFCR;		/* FIFO control */
+    unsigned int fifo_offset;	/* FIFO offset within BA1 */
+    unsigned char left_slot;	/* FIFO left slot */
+    unsigned char right_slot;	/* FIFO right slot */
+    int frag;			/* period number */
+};
+
+#define SUSPEND_REGISTERS	20
+
+struct snd_cs4281 {
+    int irq;
+
+    unsigned long ba0;		/* virtual (accessible) address */
+    unsigned long ba1;		/* virtual (accessible) address */
+    unsigned long ba0_addr;
+    unsigned long ba1_addr;
+
+    int dual_codec;
+
+    ac97_bus_t *ac97_bus;
+    ac97_t *ac97;
+    ac97_t *ac97_secondary;
+
+    struct pci_dev *pci;
+    snd_card_t *card;
+    snd_pcm_t *pcm;
+    snd_rawmidi_t *rmidi;
+    snd_rawmidi_substream_t *midi_input;
+    snd_rawmidi_substream_t *midi_output;
+
+    cs4281_dma_t dma[4];
+
+    unsigned char src_left_play_slot;
+    unsigned char src_right_play_slot;
+    unsigned char src_left_rec_slot;
+    unsigned char src_right_rec_slot;
+
+    unsigned int spurious_dhtc_irq;
+    unsigned int spurious_dtc_irq;
+
+    spinlock_t reg_lock;
+    unsigned int midcr;
+    unsigned int uartm;
+
+    struct snd_cs4281_gameport *gameport;
+
+#ifdef CONFIG_PM
+    u32 suspend_regs[SUSPEND_REGISTERS];
+#endif
+
+};
+
+static irqreturn_t snd_cs4281_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+
+static struct pci_device_id snd_cs4281_ids[] = {
+    { 0x1013, 0x6005, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },	/* CS4281 */
+    { 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, snd_cs4281_ids);
+
+/*
+ *  constants
+ */
+#define CS4281_FIFO_SIZE	32
+
+/*
+ *  common I/O routines
+ */
+
+static void snd_cs4281_delay(unsigned int delay)
+{
+    if (delay > 999) {
+        unsigned long end_time;
+        delay = (delay * HZ) / 1000000;
+        if (delay < 1)
+            delay = 1;
+        end_time = jiffies + delay;
+        do {
+            set_current_state(TASK_UNINTERRUPTIBLE);
+            schedule_timeout(1);
+        } while (time_after_eq(end_time, jiffies));
+    } else {
+        udelay(delay);
+    }
+}
+
+inline static void snd_cs4281_delay_long(void)
+{
+    set_current_state(TASK_UNINTERRUPTIBLE);
+    schedule_timeout(1);
+}
+
+static inline void snd_cs4281_pokeBA0(cs4281_t *chip, unsigned long offset, unsigned int val)
+{
+    writel(val, chip->ba0 + offset);
+}
+
+static inline unsigned int snd_cs4281_peekBA0(cs4281_t *chip, unsigned long offset)
+{
+    return readl(chip->ba0 + offset);
+}
+
+static void snd_cs4281_ac97_write(ac97_t *ac97,
+                                  unsigned short reg, unsigned short val)
+{
+    /*
+     *  1. Write ACCAD = Command Address Register = 46Ch for AC97 register address
+     *  2. Write ACCDA = Command Data Register = 470h    for data to write to AC97
+     *  3. Write ACCTL = Control Register = 460h for initiating the write
+     *  4. Read ACCTL = 460h, DCV should be reset by now and 460h = 07h
+     *  5. if DCV not cleared, break and return error
+     */
+    cs4281_t *chip = ac97->private_data;
+    int count;
+
+    /*
+     *  Setup the AC97 control registers on the CS461x to send the
+     *  appropriate command to the AC97 to perform the read.
+     *  ACCAD = Command Address Register = 46Ch
+     *  ACCDA = Command Data Register = 470h
+     *  ACCTL = Control Register = 460h
+     *  set DCV - will clear when process completed
+     *  reset CRW - Write command
+     *  set VFRM - valid frame enabled
+     *  set ESYN - ASYNC generation enabled
+     *  set RSTN - ARST# inactive, AC97 codec not reset
+     */
+    snd_cs4281_pokeBA0(chip, BA0_ACCAD, reg);
+    snd_cs4281_pokeBA0(chip, BA0_ACCDA, val);
+    snd_cs4281_pokeBA0(chip, BA0_ACCTL, BA0_ACCTL_DCV | BA0_ACCTL_VFRM |
+                       BA0_ACCTL_ESYN | (ac97->num ? BA0_ACCTL_TC : 0));
+    for (count = 0; count < 2000; count++) {
+        /*
+         *  First, we want to wait for a short time.
+         */
+        udelay(10);
+        /*
+         *  Now, check to see if the write has completed.
+         *  ACCTL = 460h, DCV should be reset by now and 460h = 07h
+         */
+        if (!(snd_cs4281_peekBA0(chip, BA0_ACCTL) & BA0_ACCTL_DCV)) {
+            return;
+        }
+    }
+    snd_printk(KERN_ERR "AC'97 write problem, reg = 0x%x, val = 0x%x\n", reg, val);
+}
+
+static unsigned short snd_cs4281_ac97_read(ac97_t *ac97,
+                                           unsigned short reg)
+{
+    cs4281_t *chip = ac97->private_data;
+    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
+     *  2. Write ACCDA = Command Data Register = 470h    for data to write to AC97
+     *  3. Write ACCTL = Control Register = 460h for initiating the write
+     *  4. Read ACCTL = 460h, DCV should be reset by now and 460h = 17h
+     *  5. if DCV not cleared, break and return error
+     *  6. Read ACSTS = Status Register = 464h, check VSTS bit
+     */
+
+    snd_cs4281_peekBA0(chip, ac97_num ? BA0_ACSDA2 : BA0_ACSDA);
+
+    /*
+     *  Setup the AC97 control registers on the CS461x to send the
+     *  appropriate command to the AC97 to perform the read.
+     *  ACCAD = Command Address Register = 46Ch
+     *  ACCDA = Command Data Register = 470h
+     *  ACCTL = Control Register = 460h
+     *  set DCV - will clear when process completed
+     *  set CRW - Read command
+     *  set VFRM - valid frame enabled
+     *  set ESYN - ASYNC generation enabled
+     *  set RSTN - ARST# inactive, AC97 codec not reset
+     */
+
+    snd_cs4281_pokeBA0(chip, BA0_ACCAD, reg);
+    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));
+
+
+    /*
+     *  Wait for the read to occur.
+     */
+    for (count = 0; count < 500; count++) {
+        /*
+         *  First, we want to wait for a short time.
+         */
+        udelay(10);
+        /*
+         *  Now, check to see if the read has completed.
+         *  ACCTL = 460h, DCV should be reset by now and 460h = 17h
+         */
+        if (!(snd_cs4281_peekBA0(chip, BA0_ACCTL) & BA0_ACCTL_DCV))
+            goto __ok1;
+    }
+
+    snd_printk(KERN_ERR "AC'97 read problem (ACCTL_DCV), reg = 0x%x\n", reg);
+    result = 0xffff;
+    goto __end;
+
+__ok1:
+    /*
+     *  Wait for the valid status bit to go active.
+     */
+    for (count = 0; count < 100; count++) {
+        /*
+         *  Read the AC97 status register.
+         *  ACSTS = Status Register = 464h
+         *  VSTS - Valid Status
+         */
+        if (snd_cs4281_peekBA0(chip, ac97_num ? BA0_ACSTS2 : BA0_ACSTS) & BA0_ACSTS_VSTS)
+            goto __ok2;
+        udelay(10);
+    }
+
+    snd_printk(KERN_ERR "AC'97 read problem (ACSTS_VSTS), reg = 0x%x\n", reg);
+    result = 0xffff;
+    goto __end;
+
+__ok2:
+    /*
+     *  Read the data returned from the AC97 register.
+     *  ACSDA = Status Data Register = 474h
+     */
+    result = snd_cs4281_peekBA0(chip, ac97_num ? BA0_ACSDA2 : BA0_ACSDA);
+
+__end:
+    return result;
+}
+
+/*
+ *  PCM part
+ */
+
+static int snd_cs4281_trigger(snd_pcm_substream_t *substream, int cmd)
+{
+    cs4281_dma_t *dma = (cs4281_dma_t *)substream->runtime->private_data;
+    cs4281_t *chip = snd_pcm_substream_chip(substream);
+
+    spin_lock(&chip->reg_lock);
+    switch (cmd) {
+    case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+        dma->valDCR |= BA0_DCR_MSK;
+        dma->valFCR |= BA0_FCR_FEN;
+        break;
+    case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+        dma->valDCR &= ~BA0_DCR_MSK;
+        dma->valFCR &= ~BA0_FCR_FEN;
+        break;
+    case SNDRV_PCM_TRIGGER_START:
+    case SNDRV_PCM_TRIGGER_RESUME:
+        snd_cs4281_pokeBA0(chip, dma->regDMR, dma->valDMR & ~BA0_DMR_DMA);
+        dma->valDMR |= BA0_DMR_DMA;
+        dma->valDCR &= ~BA0_DCR_MSK;
+        dma->valFCR |= BA0_FCR_FEN;
+        break;
+    case SNDRV_PCM_TRIGGER_STOP:
+    case SNDRV_PCM_TRIGGER_SUSPEND:
+        dma->valDMR &= ~(BA0_DMR_DMA|BA0_DMR_POLL);
+        dma->valDCR |= BA0_DCR_MSK;
+        dma->valFCR &= ~BA0_FCR_FEN;
+        /* Leave wave playback FIFO enabled for FM */
+        if(dma->regFCR != BA0_FCR0)
+            dma->valFCR &= ~BA0_FCR_FEN;
+        break;
+    default:
+        spin_unlock(&chip->reg_lock);
+        return -EINVAL;
+    }
+    snd_cs4281_pokeBA0(chip, dma->regDMR, dma->valDMR);
+    snd_cs4281_pokeBA0(chip, dma->regFCR, dma->valFCR);
+    snd_cs4281_pokeBA0(chip, dma->regDCR, dma->valDCR);
+    spin_unlock(&chip->reg_lock);
+    return 0;
+}
+
+static unsigned int snd_cs4281_rate(unsigned int rate, unsigned int *real_rate)
+{
+    unsigned int val = ~0;
+
+    if (real_rate)
+        *real_rate = rate;
+    /* special "hardcoded" rates */
+    switch (rate) {
+    case 8000:	return 5;
+    case 11025:	return 4;
+    case 16000:	return 3;
+    case 22050:	return 2;
+    case 44100:	return 1;
+    case 48000:	return 0;
+    default:
+        goto __variable;
+    }
+__variable:
+    val = 1536000 / rate;
+    if (real_rate)
+        *real_rate = 1536000 / val;
+    return val;
+}
+
+static void snd_cs4281_mode(cs4281_t *chip, cs4281_dma_t *dma, snd_pcm_runtime_t *runtime, int capture, int src)
+{
+    int rec_mono;
+
+    dma->valDMR = BA0_DMR_TYPE_SINGLE | BA0_DMR_AUTO |
+        (capture ? BA0_DMR_TR_WRITE : BA0_DMR_TR_READ);
+    if (runtime->channels == 1)
+        dma->valDMR |= BA0_DMR_MONO;
+    if (snd_pcm_format_unsigned(runtime->format) > 0)
+        dma->valDMR |= BA0_DMR_USIGN;
+    if (snd_pcm_format_big_endian(runtime->format) > 0)
+        dma->valDMR |= BA0_DMR_BEND;
+    switch (snd_pcm_format_width(runtime->format)) {
+    case 8: dma->valDMR |= BA0_DMR_SIZE8;
+    if (runtime->channels == 1)
+        dma->valDMR |= BA0_DMR_SWAPC;
+    break;
+    case 32: dma->valDMR |= BA0_DMR_SIZE20; break;
+    }
+    dma->frag = 0;	/* for workaround */
+    dma->valDCR = BA0_DCR_TCIE | BA0_DCR_MSK;
+    if (runtime->buffer_size != runtime->period_size)
+        dma->valDCR |= BA0_DCR_HTCIE;
+    /* Initialize DMA */
+    snd_cs4281_pokeBA0(chip, dma->regDBA, runtime->dma_addr);
+    snd_cs4281_pokeBA0(chip, dma->regDBC, runtime->buffer_size - 1);
+    rec_mono = (chip->dma[1].valDMR & BA0_DMR_MONO) == BA0_DMR_MONO;
+    snd_cs4281_pokeBA0(chip, BA0_SRCSA, (chip->src_left_play_slot << 0) |
+                       (chip->src_right_play_slot << 8) |
+                       (chip->src_left_rec_slot << 16) |
+                       ((rec_mono ? 31 : chip->src_right_rec_slot) << 24));
+    if (!src)
+        goto __skip_src;
+    if (!capture) {
+        if (dma->left_slot == chip->src_left_play_slot) {
+            unsigned int val = snd_cs4281_rate(runtime->rate, NULL);
+            snd_assert(dma->right_slot == chip->src_right_play_slot, );
+            snd_cs4281_pokeBA0(chip, BA0_DACSR, val);
+        }
+    } else {
+        if (dma->left_slot == chip->src_left_rec_slot) {
+            unsigned int val = snd_cs4281_rate(runtime->rate, NULL);
+            snd_assert(dma->right_slot == chip->src_right_rec_slot, );
+            snd_cs4281_pokeBA0(chip, BA0_ADCSR, val);
+        }
+    }
+__skip_src:
+    /* Deactivate wave playback FIFO before changing slot assignments */
+    if(dma->regFCR == BA0_FCR0)
+        snd_cs4281_pokeBA0(chip, dma->regFCR, snd_cs4281_peekBA0(chip, dma->regFCR) & ~BA0_FCR_FEN);
+    /* Initialize FIFO */
+    dma->valFCR = BA0_FCR_LS(dma->left_slot) |
+        BA0_FCR_RS(capture && (dma->valDMR & BA0_DMR_MONO) ? 31 : dma->right_slot) |
+        BA0_FCR_SZ(CS4281_FIFO_SIZE) |
+        BA0_FCR_OF(dma->fifo_offset);
+    snd_cs4281_pokeBA0(chip, dma->regFCR, dma->valFCR | (capture ? BA0_FCR_PSH : 0));
+    /* Activate FIFO again for FM playback */
+    if(dma->regFCR == BA0_FCR0)
+        snd_cs4281_pokeBA0(chip, dma->regFCR, dma->valFCR | BA0_FCR_FEN);
+    /* Clear FIFO Status and Interrupt Control Register */
+    snd_cs4281_pokeBA0(chip, dma->regFSIC, 0);
+}
+
+static int snd_cs4281_hw_params(snd_pcm_substream_t * substream,
+                                snd_pcm_hw_params_t * hw_params)
+{
+    return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+static int snd_cs4281_hw_free(snd_pcm_substream_t * substream)
+{
+    return snd_pcm_lib_free_pages(substream);
+}
+
+static int snd_cs4281_playback_prepare(snd_pcm_substream_t * substream)
+{
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    cs4281_dma_t *dma = (cs4281_dma_t *)runtime->private_data;
+    cs4281_t *chip = snd_pcm_substream_chip(substream);
+
+    spin_lock_irq(&chip->reg_lock);
+    snd_cs4281_mode(chip, dma, runtime, 0, 1);
+    spin_unlock_irq(&chip->reg_lock);
+    return 0;
+}
+
+static int snd_cs4281_capture_prepare(snd_pcm_substream_t * substream)
+{
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    cs4281_dma_t *dma = (cs4281_dma_t *)runtime->private_data;
+    cs4281_t *chip = snd_pcm_substream_chip(substream);
+
+    spin_lock_irq(&chip->reg_lock);
+    snd_cs4281_mode(chip, dma, runtime, 1, 1);
+    spin_unlock_irq(&chip->reg_lock);
+    return 0;
+}
+
+static snd_pcm_uframes_t snd_cs4281_pointer(snd_pcm_substream_t * substream)
+{
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    cs4281_dma_t *dma = (cs4281_dma_t *)runtime->private_data;
+    cs4281_t *chip = snd_pcm_substream_chip(substream);
+
+    // printk("DCC = 0x%x, buffer_size = 0x%x, jiffies = %li\n", snd_cs4281_peekBA0(chip, dma->regDCC), runtime->buffer_size, jiffies);
+    return runtime->buffer_size -
+        snd_cs4281_peekBA0(chip, dma->regDCC) - 1;
+}
+
+static snd_pcm_hardware_t snd_cs4281_playback =
+{
+    /*	info:			*/	(SNDRV_PCM_INFO_MMAP |
+                                         SNDRV_PCM_INFO_INTERLEAVED |
+                                         SNDRV_PCM_INFO_MMAP_VALID |
+                                         SNDRV_PCM_INFO_PAUSE |
+                                         SNDRV_PCM_INFO_RESUME |
+                                         SNDRV_PCM_INFO_SYNC_START),
+                                         /*	formats:		*/	SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 |
+                                         SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_S16_LE |
+                                         SNDRV_PCM_FMTBIT_U16_BE | SNDRV_PCM_FMTBIT_S16_BE |
+                                         SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_S32_LE |
+                                         SNDRV_PCM_FMTBIT_U32_BE | SNDRV_PCM_FMTBIT_S32_BE,
+                                         /*	rates:			*/	SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+                                         /*	rate_min:		*/	4000,
+                                         /*	rate_max:		*/	48000,
+                                         /*	channels_min:		*/	1,
+                                         /*	channels_max:		*/	2,
+                                         /*	buffer_bytes_max:	*/	(512*1024),
+                                         /*	period_bytes_min:	*/	64,
+                                         /*	period_bytes_max:	*/	(512*1024),
+                                         /*	periods_min:		*/	1,
+                                         /*	periods_max:		*/	2,
+                                         /*	fifo_size:		*/	CS4281_FIFO_SIZE,
+};
+
+static snd_pcm_hardware_t snd_cs4281_capture =
+{
+    /*	info:			*/	(SNDRV_PCM_INFO_MMAP |
+                                         SNDRV_PCM_INFO_INTERLEAVED |
+                                         SNDRV_PCM_INFO_MMAP_VALID |
+                                         SNDRV_PCM_INFO_PAUSE |
+                                         SNDRV_PCM_INFO_RESUME |
+                                         SNDRV_PCM_INFO_SYNC_START),
+                                         /*	formats:		*/	//SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 |
+                                         SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_S16_LE |
+                                         SNDRV_PCM_FMTBIT_U16_BE | SNDRV_PCM_FMTBIT_S16_BE |
+                                         SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_S32_LE |
+                                         SNDRV_PCM_FMTBIT_U32_BE | SNDRV_PCM_FMTBIT_S32_BE,
+                                         /*	rates:			*/	SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+                                         /*	rate_min:		*/	4000,
+                                         /*	rate_max:		*/	48000,
+                                         /*	channels_min:		*/	1,
+                                         /*	channels_max:		*/	2,
+                                         /*	buffer_bytes_max:	*/	(512*1024),
+                                         /*	period_bytes_min:	*/	64,
+                                         /*	period_bytes_max:	*/	(512*1024),
+                                         /*	periods_min:		*/	1,
+                                         /*	periods_max:		*/	2,
+                                         /*	fifo_size:		*/	CS4281_FIFO_SIZE,
+};
+
+static int snd_cs4281_playback_open(snd_pcm_substream_t * substream)
+{
+    cs4281_t *chip = snd_pcm_substream_chip(substream);
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    cs4281_dma_t *dma;
+
+    dma = &chip->dma[0];
+    dma->substream = substream;
+    dma->left_slot = 0;
+    dma->right_slot = 1;
+    runtime->private_data = dma;
+    runtime->hw = snd_cs4281_playback;
+    snd_pcm_set_sync(substream);
+    /* should be detected from the AC'97 layer, but it seems
+     that although CS4297A rev B reports 18-bit ADC resolution,
+     samples are 20-bit */
+    snd_pcm_hw_constraint_msbits(runtime, 0, 32, 20);
+    return 0;
+}
+
+static int snd_cs4281_capture_open(snd_pcm_substream_t * substream)
+{
+    cs4281_t *chip = snd_pcm_substream_chip(substream);
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    cs4281_dma_t *dma;
+
+    dma = &chip->dma[1];
+    dma->substream = substream;
+    dma->left_slot = 10;
+    dma->right_slot = 11;
+    runtime->private_data = dma;
+    runtime->hw = snd_cs4281_capture;
+    snd_pcm_set_sync(substream);
+    /* should be detected from the AC'97 layer, but it seems
+     that although CS4297A rev B reports 18-bit ADC resolution,
+     samples are 20-bit */
+    snd_pcm_hw_constraint_msbits(runtime, 0, 32, 20);
+    return 0;
+}
+
+static int snd_cs4281_playback_close(snd_pcm_substream_t * substream)
+{
+    cs4281_dma_t *dma = (cs4281_dma_t *)substream->runtime->private_data;
+
+    dma->substream = NULL;
+    return 0;
+}
+
+static int snd_cs4281_capture_close(snd_pcm_substream_t * substream)
+{
+    cs4281_dma_t *dma = (cs4281_dma_t *)substream->runtime->private_data;
+
+    dma->substream = NULL;
+    return 0;
+}
+
+static snd_pcm_ops_t snd_cs4281_playback_ops = {
+    /*	.open =	      */  snd_cs4281_playback_open,
+    /*	.close =      */  snd_cs4281_playback_close,
+    /*	.ioctl =      */  snd_pcm_lib_ioctl,
+    /*	.hw_params =  */  snd_cs4281_hw_params,
+    /*	.hw_free =    */  snd_cs4281_hw_free,
+    /*	.prepare =    */  snd_cs4281_playback_prepare,
+    /*	.trigger =    */  snd_cs4281_trigger,
+    /*	.pointer =    */  snd_cs4281_pointer,
+    0,0,0,0
+};
+
+static snd_pcm_ops_t snd_cs4281_capture_ops = {
+    /*	.open =	      */  snd_cs4281_capture_open,
+    /*	.close =      */  snd_cs4281_capture_close,
+    /*	.ioctl =      */  snd_pcm_lib_ioctl,
+    /*	.hw_params =  */  snd_cs4281_hw_params,
+    /*	.hw_free =    */  snd_cs4281_hw_free,
+    /*	.prepare =    */  snd_cs4281_capture_prepare,
+    /*	.trigger =    */  snd_cs4281_trigger,
+    /*	.pointer =    */  snd_cs4281_pointer,
+    0,0,0,0
+};
+
+static void snd_cs4281_pcm_free(snd_pcm_t *pcm)
+{
+    cs4281_t *chip = pcm->private_data;
+    chip->pcm = NULL;
+    snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static int __devinit snd_cs4281_pcm(cs4281_t * chip, int device, snd_pcm_t ** rpcm)
+{
+    snd_pcm_t *pcm;
+    int err;
+
+    if (rpcm)
+        *rpcm = NULL;
+    err = snd_pcm_new(chip->card, "CS4281", device, 1, 1, &pcm);
+    if (err < 0)
+        return err;
+
+    snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_cs4281_playback_ops);
+    snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_cs4281_capture_ops);
+
+    pcm->private_data = chip;
+    pcm->private_free = snd_cs4281_pcm_free;
+    pcm->info_flags = 0;
+    strcpy(pcm->name, "CS4281");
+    chip->pcm = pcm;
+
+    snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+                                          snd_dma_pci_data(chip->pci), 64*1024, 512*1024);
+
+    if (rpcm)
+        *rpcm = pcm;
+    return 0;
+}
+
+/*
+ *  Mixer section
+ */
+#define CS_VOL_MASK	0x1f
+
+static int snd_cs4281_info_volume(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 = CS_VOL_MASK;
+    return 0;
+}
+
+static int snd_cs4281_get_volume(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+    cs4281_t *chip = snd_kcontrol_chip(kcontrol);
+    int regL = (kcontrol->private_value >> 16) & 0xffff;
+    int regR = kcontrol->private_value & 0xffff;
+    int volL, volR;
+
+    volL = CS_VOL_MASK - (snd_cs4281_peekBA0(chip, regL) & CS_VOL_MASK);
+    volR = CS_VOL_MASK - (snd_cs4281_peekBA0(chip, regR) & CS_VOL_MASK);
+
+    ucontrol->value.integer.value[0] = volL;
+    ucontrol->value.integer.value[1] = volR;
+    return 0;
+}
+
+static int snd_cs4281_put_volume(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+    cs4281_t *chip = snd_kcontrol_chip(kcontrol);
+    int change = 0;
+    int regL = (kcontrol->private_value >> 16) & 0xffff;
+    int regR = kcontrol->private_value & 0xffff;
+    int volL, volR;
+
+    volL = CS_VOL_MASK - (snd_cs4281_peekBA0(chip, regL) & CS_VOL_MASK);
+    volR = CS_VOL_MASK - (snd_cs4281_peekBA0(chip, regR) & CS_VOL_MASK);
+
+    if (ucontrol->value.integer.value[0] != volL) {
+        volL = CS_VOL_MASK - (ucontrol->value.integer.value[0] & CS_VOL_MASK);
+        snd_cs4281_pokeBA0(chip, regL, volL);
+        change = 1;
+    }
+    if (ucontrol->value.integer.value[0] != volR) {
+        volR = CS_VOL_MASK - (ucontrol->value.integer.value[1] & CS_VOL_MASK);
+        snd_cs4281_pokeBA0(chip, regR, volR);
+        change = 1;
+    }
+    return change;
+}
+
+static snd_kcontrol_new_t snd_cs4281_fm_vol =
+{
+    /*	.iface = */SNDRV_CTL_ELEM_IFACE_MIXER,0,0,
+    /*	.name = */"Synth Playback Volume",0,0,0,
+    /*	.info = */snd_cs4281_info_volume,
+    /*	.get = */snd_cs4281_get_volume,
+    /*	.put = */snd_cs4281_put_volume,
+    /*	.private_value = */((BA0_FMLVC << 16) | BA0_FMRVC),
+};
+
+static snd_kcontrol_new_t snd_cs4281_pcm_vol =
+{
+    /*	.iface = */SNDRV_CTL_ELEM_IFACE_MIXER,0,0,
+    /*	.name = */"PCM Stream Playback Volume",0,0,0,
+    /*	.info = */snd_cs4281_info_volume,
+    /*	.get = */snd_cs4281_get_volume,
+    /*	.put = */snd_cs4281_put_volume,
+    /*	.private_value = */ ((BA0_PPLVC << 16) | BA0_PPRVC),
+};
+
+static void snd_cs4281_mixer_free_ac97_bus(ac97_bus_t *bus)
+{
+    cs4281_t *chip = bus->private_data;
+    chip->ac97_bus = NULL;
+}
+
+
+static void snd_cs4281_mixer_free_ac97(ac97_t *ac97)
+{
+    cs4281_t *chip = ac97->private_data;
+    if (ac97->num)
+        chip->ac97_secondary = NULL;
+    else
+        chip->ac97 = NULL;
+}
+
+static int __devinit snd_cs4281_mixer(cs4281_t * chip)
+{
+    snd_card_t *card = chip->card;
+    ac97_template_t ac97;
+    int err;
+    static ac97_bus_ops_t ops = {
+        0,
+        snd_cs4281_ac97_write,
+        snd_cs4281_ac97_read,
+        0,0
+    };
+    if ((err = snd_ac97_bus(card, 0, &ops, chip, &chip->ac97_bus)) < 0)
+        return err;
+
+    chip->ac97_bus->private_free = snd_cs4281_mixer_free_ac97_bus;
+
+    memset(&ac97, 0, sizeof(ac97));
+    ac97.private_data = chip;
+    ac97.private_free = snd_cs4281_mixer_free_ac97;
+    if ((err = snd_ac97_mixer(chip->ac97_bus, &ac97, &chip->ac97)) < 0)
+        return err;
+    if (chip->dual_codec) {
+        ac97.num = 1;
+        if ((err = snd_ac97_mixer(chip->ac97_bus, &ac97, &chip->ac97_secondary)) < 0)
+            return err;
+    }
+    if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4281_fm_vol, chip))) < 0)
+        return err;
+    if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4281_pcm_vol, chip))) < 0)
+        return err;
+    return 0;
+}
+
+/*
+ * proc interface
+ */
+
+static void snd_cs4281_proc_read(snd_info_entry_t *entry,
+                                 snd_info_buffer_t * buffer)
+{
+    cs4281_t *chip = entry->private_data;
+
+    snd_iprintf(buffer, "Cirrus Logic CS4281\n\n");
+    snd_iprintf(buffer, "Spurious half IRQs   : %u\n", chip->spurious_dhtc_irq);
+    snd_iprintf(buffer, "Spurious end IRQs    : %u\n", chip->spurious_dtc_irq);
+}
+
+static long snd_cs4281_BA0_read(snd_info_entry_t *entry, void *file_private_data,
+                                struct file *file, char __user *buf,
+                                unsigned long count, unsigned long pos)
+{
+    long size;
+    cs4281_t *chip = entry->private_data;
+
+    size = count;
+    if (pos + size > CS4281_BA0_SIZE)
+        size = (long)CS4281_BA0_SIZE - pos;
+    if (size > 0) {
+        if (copy_to_user_fromio(buf, (char*)chip->ba0 + pos, size))
+            return -EFAULT;
+    }
+    return size;
+}
+
+static long snd_cs4281_BA1_read(snd_info_entry_t *entry, void *file_private_data,
+                                struct file *file, char __user *buf,
+                                unsigned long count, unsigned long pos)
+{
+    long size;
+    cs4281_t *chip = entry->private_data;
+
+    size = count;
+    if (pos + size > CS4281_BA1_SIZE)
+        size = (long)CS4281_BA1_SIZE - pos;
+    if (size > 0) {
+        if (copy_to_user_fromio(buf, (char*)chip->ba1 + pos, size))
+            return -EFAULT;
+    }
+    return size;
+}
+
+static struct snd_info_entry_ops snd_cs4281_proc_ops_BA0 = {
+	.read = snd_cs4281_BA0_read,
+};
+
+static struct snd_info_entry_ops snd_cs4281_proc_ops_BA1 = {
+	.read = snd_cs4281_BA1_read,
+};
+
+static void __devinit snd_cs4281_proc_init(cs4281_t * chip)
+{
+    snd_info_entry_t *entry;
+
+    if (! snd_card_proc_new(chip->card, "cs4281", &entry))
+        snd_info_set_text_ops(entry, chip, 1024, snd_cs4281_proc_read);
+    if (! snd_card_proc_new(chip->card, "cs4281_BA0", &entry)) {
+        entry->content = SNDRV_INFO_CONTENT_DATA;
+        entry->private_data = chip;
+        entry->c.ops = &snd_cs4281_proc_ops_BA0;
+        entry->size = CS4281_BA0_SIZE;
+    }
+    if (! snd_card_proc_new(chip->card, "cs4281_BA1", &entry)) {
+        entry->content = SNDRV_INFO_CONTENT_DATA;
+        entry->private_data = chip;
+        entry->c.ops = &snd_cs4281_proc_ops_BA1;
+        entry->size = CS4281_BA1_SIZE;
+    }
+}
+
+/*
+ * joystick support
+ */
+
+#ifndef LINUX_2_2
+
+typedef struct snd_cs4281_gameport {
+    struct gameport info;
+    cs4281_t *chip;
+} cs4281_gameport_t;
+
+static void snd_cs4281_gameport_trigger(struct gameport *gameport)
+{
+    cs4281_gameport_t *gp = (cs4281_gameport_t *)gameport;
+    cs4281_t *chip;
+    snd_assert(gp, return);
+    chip = gp->chip;
+    snd_cs4281_pokeBA0(chip, BA0_JSPT, 0xff);
+}
+
+static unsigned char snd_cs4281_gameport_read(struct gameport *gameport)
+{
+    cs4281_gameport_t *gp = (cs4281_gameport_t *)gameport;
+    cs4281_t *chip;
+    snd_assert(gp, return 0);
+    chip = gp->chip;
+    return snd_cs4281_peekBA0(chip, BA0_JSPT);
+}
+
+#ifdef COOKED_MODE
+static int snd_cs4281_gameport_cooked_read(struct gameport *gameport, int *axes, int *buttons)
+{
+    cs4281_gameport_t *gp = (cs4281_gameport_t *)gameport;
+    cs4281_t *chip;
+    unsigned js1, js2, jst;
+
+    snd_assert(gp, return 0);
+    chip = gp->chip;
+
+    js1 = snd_cs4281_peekBA0(chip, BA0_JSC1);
+    js2 = snd_cs4281_peekBA0(chip, BA0_JSC2);
+    jst = snd_cs4281_peekBA0(chip, BA0_JSPT);
+
+    *buttons = (~jst >> 4) & 0x0F;
+
+    axes[0] = ((js1 & JSC1_Y1V_MASK) >> JSC1_Y1V_SHIFT) & 0xFFFF;
+    axes[1] = ((js1 & JSC1_X1V_MASK) >> JSC1_X1V_SHIFT) & 0xFFFF;
+    axes[2] = ((js2 & JSC2_Y2V_MASK) >> JSC2_Y2V_SHIFT) & 0xFFFF;
+    axes[3] = ((js2 & JSC2_X2V_MASK) >> JSC2_X2V_SHIFT) & 0xFFFF;
+
+    for(jst=0;jst<4;++jst)
+        if(axes[jst]==0xFFFF) axes[jst] = -1;
+    return 0;
+}
+#endif
+
+static int snd_cs4281_gameport_open(struct gameport *gameport, int mode)
+{
+    switch (mode) {
+#ifdef COOKED_MODE
+    case GAMEPORT_MODE_COOKED:
+        return 0;
+#endif
+    case GAMEPORT_MODE_RAW:
+        return 0;
+    default:
+        return -1;
+    }
+    return 0;
+}
+
+static void __devinit snd_cs4281_gameport(cs4281_t *chip)
+{
+    cs4281_gameport_t *gp;
+    gp = kmalloc(sizeof(*gp), GFP_KERNEL);
+    if (! gp) {
+        snd_printk(KERN_ERR "cannot allocate gameport area\n");
+        return;
+    }
+    memset(gp, 0, sizeof(*gp));
+    gp->info.open = snd_cs4281_gameport_open;
+    gp->info.read = snd_cs4281_gameport_read;
+    gp->info.trigger = snd_cs4281_gameport_trigger;
+#ifdef COOKED_MODE
+    gp->info.cooked_read = snd_cs4281_gameport_cooked_read;
+#endif
+    gp->chip = chip;
+    chip->gameport = gp;
+
+    snd_cs4281_pokeBA0(chip, BA0_JSIO, 0xFF); // ?
+    snd_cs4281_pokeBA0(chip, BA0_JSCTL, JSCTL_SP_MEDIUM_SLOW);
+    gameport_register_port(&gp->info);
+}
+
+#endif /* !LINUX_2_2 */
+
+
+static int snd_cs4281_free(cs4281_t *chip)
+{
+#ifndef LINUX_2_2
+    if (chip->gameport) {
+        gameport_unregister_port(&chip->gameport->info);
+        kfree(chip->gameport);
+    }
+#endif
+    if (chip->irq >= 0)
+        synchronize_irq(chip->irq);
+
+    /* Mask interrupts */
+    snd_cs4281_pokeBA0(chip, BA0_HIMR, 0x7fffffff);
+    /* Stop the DLL Clock logic. */
+    snd_cs4281_pokeBA0(chip, BA0_CLKCR1, 0);
+    /* Sound System Power Management - Turn Everything OFF */
+    snd_cs4281_pokeBA0(chip, BA0_SSPM, 0);
+    /* PCI interface - D3 state */
+    pci_set_power_state(chip->pci, 3);
+
+    if (chip->irq >= 0)
+        free_irq(chip->irq, (void *)chip);
+    if (chip->ba0)
+        iounmap((void *) chip->ba0);
+    if (chip->ba1)
+        iounmap((void *) chip->ba1);
+    pci_release_regions(chip->pci);
+    kfree(chip);
+    return 0;
+}
+
+static int snd_cs4281_dev_free(snd_device_t *device)
+{
+    cs4281_t *chip = device->device_data;
+    return snd_cs4281_free(chip);
+}
+
+static int snd_cs4281_chip_init(cs4281_t *chip); /* defined below */
+#ifdef CONFIG_PM
+static int cs4281_suspend(snd_card_t *card, unsigned int state);
+static int cs4281_resume(snd_card_t *card, unsigned int state);
+#endif
+
+static int __devinit snd_cs4281_create(snd_card_t * card,
+                                       struct pci_dev *pci,
+                                       cs4281_t ** rchip,
+                                       int dual_codec)
+{
+    cs4281_t *chip;
+    unsigned int tmp;
+    int err;
+#ifdef TARGET_OS2
+    static snd_device_ops_t ops = {
+        snd_cs4281_dev_free,0,0,0
+    };
+#else
+    static snd_device_ops_t ops = {
+    dev_free:	snd_cs4281_dev_free,
+    };
+#endif
+
+    *rchip = NULL;
+    if ((err = pci_enable_device(pci)) < 0)
+        return err;
+    chip = kcalloc(1, sizeof(*chip), GFP_KERNEL);
+    if (chip == NULL)
+        return -ENOMEM;
+    spin_lock_init(&chip->reg_lock);
+    chip->card = card;
+    chip->pci = pci;
+    chip->irq = -1;
+    pci_set_master(pci);
+    if (dual_codec < 0 || dual_codec > 3) {
+        snd_printk(KERN_ERR "invalid snd_dual_codec option %d\n", dual_codec);
+        dual_codec = 0;
+    }
+    chip->dual_codec = dual_codec;
+
+    if ((err = pci_request_regions(pci, "CS4281")) < 0) {
+        kfree(chip);
+        return err;
+    }
+    chip->ba0_addr = pci_resource_start(pci, 0);
+    chip->ba1_addr = pci_resource_start(pci, 1);
+    chip->ba0 = (unsigned long) ioremap_nocache(chip->ba0_addr, pci_resource_len(pci, 0));
+    chip->ba1 = (unsigned long) ioremap_nocache(chip->ba1_addr, pci_resource_len(pci, 1));
+    if (!chip->ba0 || !chip->ba1) {
+        snd_cs4281_free(chip);
+        return -ENOMEM;
+    }
+
+    if (request_irq(pci->irq, snd_cs4281_interrupt, SA_INTERRUPT|SA_SHIRQ, "CS4281", (void *)chip)) {
+        snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
+        snd_cs4281_free(chip);
+        return -ENOMEM;
+    }
+    chip->irq = pci->irq;
+
+    tmp = snd_cs4281_chip_init(chip);
+    if(tmp) {
+        snd_cs4281_free(chip);
+        return tmp;
+    }
+
+    snd_cs4281_proc_init(chip);
+
+    snd_card_set_pm_callback(card, cs4281_suspend, cs4281_resume, chip);
+
+    if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+        snd_cs4281_free(chip);
+        return err;
+    }
+
+    *rchip = chip;
+    return 0;
+}
+
+static int snd_cs4281_chip_init(cs4281_t *chip)
+{
+    unsigned int tmp;
+    unsigned long end_time;
+    int retry_count = 2;
+
+    /* Having EPPMC.FPDN=1 prevent proper chip initialisation */
+    tmp = snd_cs4281_peekBA0(chip, BA0_EPPMC);
+    if (tmp & BA0_EPPMC_FPDN)
+        snd_cs4281_pokeBA0(chip, BA0_EPPMC, tmp & ~BA0_EPPMC_FPDN);
+__retry:
+    tmp = snd_cs4281_peekBA0(chip, BA0_CFLR);
+    if (tmp != BA0_CFLR_DEFAULT) {
+        snd_cs4281_pokeBA0(chip, BA0_CFLR, BA0_CFLR_DEFAULT);
+        tmp = snd_cs4281_peekBA0(chip, BA0_CFLR);
+        if (tmp != BA0_CFLR_DEFAULT) {
+            snd_printk(KERN_ERR "CFLR setup failed (0x%x)\n", tmp);
+            return -EIO;
+        }
+    }
+
+    /* Set the 'Configuration Write Protect' register
+     * to 4281h.  Allows vendor-defined configuration
+     * space between 0e4h and 0ffh to be written. */
+    snd_cs4281_pokeBA0(chip, BA0_CWPR, 0x4281);
+
+    if ((tmp = snd_cs4281_peekBA0(chip, BA0_SERC1)) != (BA0_SERC1_SO1EN | BA0_SERC1_AC97)) {
+        snd_printk(KERN_ERR "SERC1 AC'97 check failed (0x%x)\n", tmp);
+        return -EIO;
+    }
+    if ((tmp = snd_cs4281_peekBA0(chip, BA0_SERC2)) != (BA0_SERC2_SI1EN | BA0_SERC2_AC97)) {
+        snd_printk(KERN_ERR "SERC2 AC'97 check failed (0x%x)\n", tmp);
+        return -EIO;
+    }
+
+    /* Sound System Power Management */
+    snd_cs4281_pokeBA0(chip, BA0_SSPM, BA0_SSPM_MIXEN | BA0_SSPM_CSRCEN |
+                       BA0_SSPM_PSRCEN | BA0_SSPM_JSEN |
+                       BA0_SSPM_ACLEN | BA0_SSPM_FMEN);
+
+    /* Serial Port Power Management */
+    /* Blast the clock control register to zero so that the
+     * PLL starts out in a known state, and blast the master serial
+     * port control register to zero so that the serial ports also
+     * start out in a known state. */
+    snd_cs4281_pokeBA0(chip, BA0_CLKCR1, 0);
+    snd_cs4281_pokeBA0(chip, BA0_SERMC, 0);
+
+    /* Make ESYN go to zero to turn off
+     * the Sync pulse on the AC97 link. */
+    snd_cs4281_pokeBA0(chip, BA0_ACCTL, 0);
+    udelay(50);
+
+    /*  Drive the ARST# pin low for a minimum of 1uS (as defined in the AC97
+     *  spec) and then drive it high.  This is done for non AC97 modes since
+     *  there might be logic external to the CS4281 that uses the ARST# line
+     *  for a reset. */
+    snd_cs4281_pokeBA0(chip, BA0_SPMC, 0);
+    udelay(50);
+    snd_cs4281_pokeBA0(chip, BA0_SPMC, BA0_SPMC_RSTN);
+    snd_cs4281_delay(50000);
+
+    if (chip->dual_codec)
+        snd_cs4281_pokeBA0(chip, BA0_SPMC, BA0_SPMC_RSTN | BA0_SPMC_ASDI2E);
+
+    /*
+     *  Set the serial port timing configuration.
+     */
+    snd_cs4281_pokeBA0(chip, BA0_SERMC,
+                       (chip->dual_codec ? BA0_SERMC_TCID(chip->dual_codec) : BA0_SERMC_TCID(1)) |
+                       BA0_SERMC_PTC_AC97 | BA0_SERMC_MSPE);
+
+    /*
+     *  Start the DLL Clock logic.
+     */
+    snd_cs4281_pokeBA0(chip, BA0_CLKCR1, BA0_CLKCR1_DLLP);
+    snd_cs4281_delay(50000);
+    snd_cs4281_pokeBA0(chip, BA0_CLKCR1, BA0_CLKCR1_SWCE | BA0_CLKCR1_DLLP);
+
+    /*
+     * Wait for the DLL ready signal from the clock logic.
+     */
+    end_time = jiffies + HZ;
+    do {
+        /*
+         *  Read the AC97 status register to see if we've seen a CODEC
+         *  signal from the AC97 codec.
+         */
+        if (snd_cs4281_peekBA0(chip, BA0_CLKCR1) & BA0_CLKCR1_DLLRDY)
+            goto __ok0;
+        schedule_timeout_uninterruptible(1);
+    } while (time_after_eq(end_time, jiffies));
+
+    snd_printk(KERN_ERR "DLLRDY not seen\n");
+    return -EIO;
+
+__ok0:
+
+    /*
+     *  The first thing we do here is to enable sync generation.  As soon
+     *  as we start receiving bit clock, we'll start producing the SYNC
+     *  signal.
+     */
+    snd_cs4281_pokeBA0(chip, BA0_ACCTL, BA0_ACCTL_ESYN);
+
+    /*
+     * Wait for the codec ready signal from the AC97 codec.
+     */
+    end_time = jiffies + HZ;
+    do {
+        /*
+         *  Read the AC97 status register to see if we've seen a CODEC
+         *  signal from the AC97 codec.
+         */
+        if (snd_cs4281_peekBA0(chip, BA0_ACSTS) & BA0_ACSTS_CRDY)
+            goto __ok1;
+        schedule_timeout_uninterruptible(1);
+    } while (time_after_eq(end_time, jiffies));
+
+    snd_printk(KERN_ERR "never read codec ready from AC'97 (0x%x)\n", snd_cs4281_peekBA0(chip, BA0_ACSTS));
+    return -EIO;
+
+__ok1:
+    if (chip->dual_codec) {
+        end_time = jiffies + HZ;
+        do {
+            if (snd_cs4281_peekBA0(chip, BA0_ACSTS2) & BA0_ACSTS_CRDY)
+                goto __codec2_ok;
+            schedule_timeout_uninterruptible(1);
+        } while (time_after_eq(end_time, jiffies));
+        snd_printk(KERN_INFO "secondary codec doesn't respond. disable it...\n");
+        chip->dual_codec = 0;
+    __codec2_ok: ;
+    }
+
+    /*
+     *  Assert the valid frame signal so that we can start sending commands
+     *  to the AC97 codec.
+     */
+
+    snd_cs4281_pokeBA0(chip, BA0_ACCTL, BA0_ACCTL_VFRM | BA0_ACCTL_ESYN);
+
+    /*
+     *  Wait until we've sampled input slots 3 and 4 as valid, meaning that
+     *  the codec is pumping ADC data across the AC-link.
+     */
+    end_time = jiffies + HZ;
+    do {
+        /*
+         *  Read the input slot valid register and see if input slots 3
+         *  4 are valid yet.
+         */
+        if ((snd_cs4281_peekBA0(chip, BA0_ACISV) & (BA0_ACISV_SLV(3) | BA0_ACISV_SLV(4))) == (BA0_ACISV_SLV(3) | BA0_ACISV_SLV(4)))
+            goto __ok2;
+        schedule_timeout_uninterruptible(1);
+    } while (time_after_eq(end_time, jiffies));
+
+    if (--retry_count > 0)
+        goto __retry;
+    snd_printk(KERN_ERR "never read ISV3 and ISV4 from AC'97\n");
+    return -EIO;
+
+__ok2:
+
+    /*
+     *  Now, assert valid frame and the slot 3 and 4 valid bits.  This will
+     *  commense the transfer of digital audio data to the AC97 codec.
+     */
+    snd_cs4281_pokeBA0(chip, BA0_ACOSV, BA0_ACOSV_SLV(3) | BA0_ACOSV_SLV(4));
+
+    /*
+     *  Initialize DMA structures
+     */
+    for (tmp = 0; tmp < 4; tmp++) {
+        cs4281_dma_t *dma = &chip->dma[tmp];
+        dma->regDBA = BA0_DBA0 + (tmp * 0x10);
+        dma->regDCA = BA0_DCA0 + (tmp * 0x10);
+        dma->regDBC = BA0_DBC0 + (tmp * 0x10);
+        dma->regDCC = BA0_DCC0 + (tmp * 0x10);
+        dma->regDMR = BA0_DMR0 + (tmp * 8);
+        dma->regDCR = BA0_DCR0 + (tmp * 8);
+        dma->regHDSR = BA0_HDSR0 + (tmp * 4);
+        dma->regFCR = BA0_FCR0 + (tmp * 4);
+        dma->regFSIC = BA0_FSIC0 + (tmp * 4);
+        dma->fifo_offset = tmp * CS4281_FIFO_SIZE;
+
+        snd_cs4281_pokeBA0(chip, dma->regFCR,
+                           BA0_FCR_LS(31) |
+                           BA0_FCR_RS(31) |
+                           BA0_FCR_SZ(CS4281_FIFO_SIZE) |
+                           BA0_FCR_OF(dma->fifo_offset));
+    }
+
+    chip->src_left_play_slot = 0;	/* AC'97 left PCM playback (3) */
+    chip->src_right_play_slot = 1;	/* AC'97 right PCM playback (4) */
+    chip->src_left_rec_slot = 10;	/* AC'97 left PCM record (3) */
+    chip->src_right_rec_slot = 11;	/* AC'97 right PCM record (4) */
+
+    /* Activate wave playback FIFO for FM playback */
+    chip->dma[0].valFCR = BA0_FCR_FEN | BA0_FCR_LS(0) |
+        BA0_FCR_RS(1) |
+        BA0_FCR_SZ(CS4281_FIFO_SIZE) |
+        BA0_FCR_OF(chip->dma[0].fifo_offset);
+    snd_cs4281_pokeBA0(chip, chip->dma[0].regFCR, chip->dma[0].valFCR);
+    snd_cs4281_pokeBA0(chip, BA0_SRCSA, (chip->src_left_play_slot << 0) |
+                       (chip->src_right_play_slot << 8) |
+                       (chip->src_left_rec_slot << 16) |
+                       (chip->src_right_rec_slot << 24));
+
+    /* Initialize digital volume */
+    snd_cs4281_pokeBA0(chip, BA0_PPLVC, 0);
+    snd_cs4281_pokeBA0(chip, BA0_PPRVC, 0);
+
+    /* Enable IRQs */
+    snd_cs4281_pokeBA0(chip, BA0_HICR, BA0_HICR_EOI);
+    /* Unmask interrupts */
+    snd_cs4281_pokeBA0(chip, BA0_HIMR, 0x7fffffff & ~(
+                                                      BA0_HISR_MIDI |
+                                                      BA0_HISR_DMAI |
+                                                      BA0_HISR_DMA(0) |
+                                                      BA0_HISR_DMA(1) |
+                                                      BA0_HISR_DMA(2) |
+                                                      BA0_HISR_DMA(3)));
+    synchronize_irq(chip->irq);
+
+    return 0;
+}
+
+/*
+ *  MIDI section
+ */
+
+static void snd_cs4281_midi_reset(cs4281_t *chip)
+{
+    snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr | BA0_MIDCR_MRST);
+    udelay(100);
+    snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr);
+}
+
+static int snd_cs4281_midi_input_open(snd_rawmidi_substream_t * substream)
+{
+    cs4281_t *chip = substream->rmidi->private_data;
+
+    spin_lock_irq(&chip->reg_lock);
+    chip->midcr |= BA0_MIDCR_RXE;
+    chip->midi_output = substream;
+    if (!(chip->uartm & CS4281_MODE_OUTPUT)) {
+        snd_cs4281_midi_reset(chip);
+    } else {
+        snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr);
+    }
+    spin_unlock_irq(&chip->reg_lock);
+    return 0;
+}
+
+static int snd_cs4281_midi_input_close(snd_rawmidi_substream_t * substream)
+{
+    cs4281_t *chip = substream->rmidi->private_data;
+
+    spin_lock_irq(&chip->reg_lock);
+    chip->midcr &= ~(BA0_MIDCR_RXE | BA0_MIDCR_RIE);
+    chip->midi_input = NULL;
+    if (!(chip->uartm & CS4281_MODE_OUTPUT)) {
+        snd_cs4281_midi_reset(chip);
+    } else {
+        snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr);
+    }
+    chip->uartm &= ~CS4281_MODE_INPUT;
+    spin_unlock_irq(&chip->reg_lock);
+    return 0;
+}
+
+static int snd_cs4281_midi_output_open(snd_rawmidi_substream_t * substream)
+{
+    cs4281_t *chip = substream->rmidi->private_data;
+
+    spin_lock_irq(&chip->reg_lock);
+    chip->uartm |= CS4281_MODE_OUTPUT;
+    chip->midcr |= BA0_MIDCR_TXE;
+    chip->midi_input = substream;
+    if (!(chip->uartm & CS4281_MODE_INPUT)) {
+        snd_cs4281_midi_reset(chip);
+    } else {
+        snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr);
+    }
+    spin_unlock_irq(&chip->reg_lock);
+    return 0;
+}
+
+static int snd_cs4281_midi_output_close(snd_rawmidi_substream_t * substream)
+{
+    cs4281_t *chip = substream->rmidi->private_data;
+
+    spin_lock_irq(&chip->reg_lock);
+    chip->midcr &= ~(BA0_MIDCR_TXE | BA0_MIDCR_TIE);
+    chip->midi_output = NULL;
+    if (!(chip->uartm & CS4281_MODE_INPUT)) {
+        snd_cs4281_midi_reset(chip);
+    } else {
+        snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr);
+    }
+    chip->uartm &= ~CS4281_MODE_OUTPUT;
+    spin_unlock_irq(&chip->reg_lock);
+    return 0;
+}
+
+static void snd_cs4281_midi_input_trigger(snd_rawmidi_substream_t * substream, int up)
+{
+    unsigned long flags;
+    cs4281_t *chip = substream->rmidi->private_data;
+
+    spin_lock_irqsave(&chip->reg_lock, flags);
+    if (up) {
+        if ((chip->midcr & BA0_MIDCR_RIE) == 0) {
+            chip->midcr |= BA0_MIDCR_RIE;
+            snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr);
+        }
+    } else {
+        if (chip->midcr & BA0_MIDCR_RIE) {
+            chip->midcr &= ~BA0_MIDCR_RIE;
+            snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr);
+        }
+    }
+    spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+static void snd_cs4281_midi_output_trigger(snd_rawmidi_substream_t * substream, int up)
+{
+    unsigned long flags;
+    cs4281_t *chip = substream->rmidi->private_data;
+    unsigned char byte;
+
+    spin_lock_irqsave(&chip->reg_lock, flags);
+    if (up) {
+        if ((chip->midcr & BA0_MIDCR_TIE) == 0) {
+            chip->midcr |= BA0_MIDCR_TIE;
+            /* fill UART FIFO buffer at first, and turn Tx interrupts only if necessary */
+            while ((chip->midcr & BA0_MIDCR_TIE) &&
+                   (snd_cs4281_peekBA0(chip, BA0_MIDSR) & BA0_MIDSR_TBF) == 0) {
+                if (snd_rawmidi_transmit(substream, &byte, 1) != 1) {
+                    chip->midcr &= ~BA0_MIDCR_TIE;
+                } else {
+                    snd_cs4281_pokeBA0(chip, BA0_MIDWP, byte);
+                }
+            }
+            snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr);
+        }
+    } else {
+        if (chip->midcr & BA0_MIDCR_TIE) {
+            chip->midcr &= ~BA0_MIDCR_TIE;
+            snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr);
+        }
+    }
+    spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+static struct snd_rawmidi_ops snd_cs4281_midi_output =
+{
+    .open =		snd_cs4281_midi_output_open,
+    .close =	snd_cs4281_midi_output_close,
+    .trigger =	snd_cs4281_midi_output_trigger,
+};
+
+static struct snd_rawmidi_ops snd_cs4281_midi_input =
+{
+    .open = 	snd_cs4281_midi_input_open,
+    .close =	snd_cs4281_midi_input_close,
+    .trigger =	snd_cs4281_midi_input_trigger,
+};
+
+static int __devinit snd_cs4281_midi(cs4281_t * chip, int device, snd_rawmidi_t **rrawmidi)
+{
+    snd_rawmidi_t *rmidi;
+    int err;
+
+    if (rrawmidi)
+        *rrawmidi = NULL;
+    if ((err = snd_rawmidi_new(chip->card, "CS4281", device, 1, 1, &rmidi)) < 0)
+        return err;
+    strcpy(rmidi->name, "CS4281");
+    snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_cs4281_midi_output);
+    snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_cs4281_midi_input);
+    rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX;
+    rmidi->private_data = chip;
+    chip->rmidi = rmidi;
+    if (rrawmidi)
+        *rrawmidi = rmidi;
+    return 0;
+}
+
+/*
+ *  Interrupt handler
+ */
+
+static irqreturn_t snd_cs4281_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+    cs4281_t *chip = dev_id;
+    unsigned int status, dma, val;
+    cs4281_dma_t *cdma;
+
+    if (chip == NULL)
+        return IRQ_NONE;
+    status = snd_cs4281_peekBA0(chip, BA0_HISR);
+    if ((status & 0x7fffffff) == 0) {
+        snd_cs4281_pokeBA0(chip, BA0_HICR, BA0_HICR_EOI);
+        return IRQ_NONE;
+    }
+
+    if (status & (BA0_HISR_DMA(0)|BA0_HISR_DMA(1)|BA0_HISR_DMA(2)|BA0_HISR_DMA(3))) {
+        for (dma = 0; dma < 4; dma++)
+            if (status & BA0_HISR_DMA(dma)) {
+                cdma = &chip->dma[dma];
+                spin_lock(&chip->reg_lock);
+                /* ack DMA IRQ */
+                val = snd_cs4281_peekBA0(chip, cdma->regHDSR);
+                /* workaround, sometimes CS4281 acknowledges */
+                /* end or middle transfer position twice */
+                cdma->frag++;
+                if ((val & BA0_HDSR_DHTC) && !(cdma->frag & 1)) {
+                    cdma->frag--;
+                    chip->spurious_dhtc_irq++;
+                    spin_unlock(&chip->reg_lock);
+                    continue;
+                }
+                if ((val & BA0_HDSR_DTC) && (cdma->frag & 1)) {
+                    cdma->frag--;
+                    chip->spurious_dtc_irq++;
+                    spin_unlock(&chip->reg_lock);
+                    continue;
+                }
+                spin_unlock(&chip->reg_lock);
+                snd_pcm_period_elapsed(cdma->substream);
+            }
+    }
+
+    if ((status & BA0_HISR_MIDI) && chip->rmidi) {
+        unsigned char c;
+
+        spin_lock(&chip->reg_lock);
+        while ((snd_cs4281_peekBA0(chip, BA0_MIDSR) & BA0_MIDSR_RBE) == 0) {
+            c = snd_cs4281_peekBA0(chip, BA0_MIDRP);
+            if ((chip->midcr & BA0_MIDCR_RIE) == 0)
+                continue;
+            spin_unlock(&chip->reg_lock);
+            snd_rawmidi_receive(chip->midi_input, &c, 1);
+            spin_lock(&chip->reg_lock);
+        }
+        while ((snd_cs4281_peekBA0(chip, BA0_MIDSR) & BA0_MIDSR_TBF) == 0) {
+            if ((chip->midcr & BA0_MIDCR_TIE) == 0)
+                break;
+            if (snd_rawmidi_transmit(chip->midi_output, &c, 1) != 1) {
+                chip->midcr &= ~BA0_MIDCR_TIE;
+                snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr);
+                break;
+            }
+            snd_cs4281_pokeBA0(chip, BA0_MIDWP, c);
+        }
+        spin_unlock(&chip->reg_lock);
+    }
+
+    /* EOI to the PCI part... reenables interrupts */
+    snd_cs4281_pokeBA0(chip, BA0_HICR, BA0_HICR_EOI);
+    return IRQ_HANDLED;
+
+}
+
+/*
+ * OPL3 command
+ */
+static void snd_cs4281_opl3_command(opl3_t * opl3, unsigned short cmd, unsigned char val)
+{
+    unsigned long flags;
+    cs4281_t *chip = opl3->private_data;
+    void __iomem *port;
+
+    if (cmd & OPL3_RIGHT)
+        port = (unsigned long)chip->ba0 + BA0_B1AP; /* right port */
+    else
+        port = (unsigned long)chip->ba0 + BA0_B0AP; /* left port */
+
+    spin_lock_irqsave(&opl3->reg_lock, flags);
+
+    writel((unsigned int)cmd, port);
+    udelay(10);
+
+    writel((unsigned int)val, (unsigned long)port + 4);
+    udelay(30);
+
+    spin_unlock_irqrestore(&opl3->reg_lock, flags);
+}
+
+static int __devinit snd_cs4281_probe(struct pci_dev *pci,
+                                      const struct pci_device_id *pci_id)
+{
+    static int dev;
+    snd_card_t *card;
+    cs4281_t *chip;
+    opl3_t *opl3;
+    int err;
+
+    if (dev >= SNDRV_CARDS)
+        return -ENODEV;
+    if (!enable[dev]) {
+        dev++;
+        return -ENOENT;
+    }
+
+    card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+    if (card == NULL)
+        return -ENOMEM;
+
+    if ((err = snd_cs4281_create(card, pci, &chip, dual_codec[dev])) < 0) {
+        snd_card_free(card);
+        return err;
+    }
+
+    if ((err = snd_cs4281_mixer(chip)) < 0) {
+        snd_card_free(card);
+        return err;
+    }
+    if ((err = snd_cs4281_pcm(chip, 0, NULL)) < 0) {
+        snd_card_free(card);
+        return err;
+    }
+    if ((err = snd_cs4281_midi(chip, 0, NULL)) < 0) {
+        snd_card_free(card);
+        return err;
+    }
+    if ((err = snd_opl3_new(card, OPL3_HW_OPL3_CS4281, &opl3)) < 0) {
+        snd_card_free(card);
+        return err;
+    }
+    opl3->private_data = chip;
+    opl3->command = snd_cs4281_opl3_command;
+    snd_opl3_init(opl3);
+
+    if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
+        snd_card_free(card);
+        return err;
+    }
+#ifndef LINUX_2_2
+    snd_cs4281_gameport(chip);
+#endif
+    strcpy(card->driver, "CS4281");
+    strcpy(card->shortname, "Cirrus Logic CS4281");
+    sprintf(card->longname, "%s at 0x%lx, irq %d",
+            card->shortname,
+            chip->ba0_addr,
+            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_cs4281_remove(struct pci_dev *pci)
+{
+    snd_card_free(pci_get_drvdata(pci));
+    pci_set_drvdata(pci, NULL);
+}
+
+/*
+ * Power Management
+ */
+#ifdef CONFIG_PM
+
+static int saved_regs[SUSPEND_REGISTERS] = {
+    BA0_JSCTL,
+    BA0_GPIOR,
+    BA0_SSCR,
+    BA0_MIDCR,
+    BA0_SRCSA,
+    BA0_PASR,
+    BA0_CASR,
+    BA0_DACSR,
+    BA0_ADCSR,
+    BA0_FMLVC,
+    BA0_FMRVC,
+    BA0_PPLVC,
+    BA0_PPRVC,
+};
+
+#define number_of(array)	(sizeof(array) / sizeof(array[0]))
+
+#define CLKCR1_CKRA                             0x00010000L
+
+static int cs4281_suspend(snd_card_t *card, unsigned int state)
+{
+    cs4281_t *chip = card->pm_private_data;
+    u32 ulCLK;
+    unsigned int i;
+
+    snd_pcm_suspend_all(chip->pcm);
+
+    if (chip->ac97)
+        snd_ac97_suspend(chip->ac97);
+    if (chip->ac97_secondary)
+        snd_ac97_suspend(chip->ac97_secondary);
+
+    ulCLK = snd_cs4281_peekBA0(chip, BA0_CLKCR1);
+    ulCLK |= CLKCR1_CKRA;
+    snd_cs4281_pokeBA0(chip, BA0_CLKCR1, ulCLK);
+
+    /* Disable interrupts. */
+    snd_cs4281_pokeBA0(chip, BA0_HICR, BA0_HICR_CHGM);
+
+    /* remember the status registers */
+    for (i = 0; i < number_of(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);
+
+    /* Power off FM, Joystick, AC link, */
+    snd_cs4281_pokeBA0(chip, BA0_SSPM, 0);
+
+    /* DLL off. */
+    snd_cs4281_pokeBA0(chip, BA0_CLKCR1, 0);
+
+    /* AC link off. */
+    snd_cs4281_pokeBA0(chip, BA0_SPMC, 0);
+
+    ulCLK = snd_cs4281_peekBA0(chip, BA0_CLKCR1);
+    ulCLK &= ~CLKCR1_CKRA;
+    snd_cs4281_pokeBA0(chip, BA0_CLKCR1, ulCLK);
+
+    return 0;
+}
+
+static int cs4281_resume(snd_card_t *card, unsigned int state)
+{
+    cs4281_t *chip = card->pm_private_data;
+    unsigned int i;
+    u32 ulCLK;
+
+    pci_enable_device(chip->pci);
+
+    ulCLK = snd_cs4281_peekBA0(chip, BA0_CLKCR1);
+    ulCLK |= CLKCR1_CKRA;
+    snd_cs4281_pokeBA0(chip, BA0_CLKCR1, ulCLK);
+
+    snd_cs4281_chip_init(chip);
+
+    /* restore the status registers */
+    for (i = 0; i < number_of(saved_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);
+    if (chip->ac97_secondary)
+        snd_ac97_resume(chip->ac97_secondary);
+
+    ulCLK = snd_cs4281_peekBA0(chip, BA0_CLKCR1);
+    ulCLK &= ~CLKCR1_CKRA;
+    snd_cs4281_pokeBA0(chip, BA0_CLKCR1, ulCLK);
+
+    return 0;
+}
+
+
+#endif /* CONFIG_PM */
+
+static struct pci_driver driver = {
+    0, 0,0,
+    "CS4281",
+    snd_cs4281_ids,
+    snd_cs4281_probe,
+    snd_cs4281_remove,
+    SND_PCI_PM_CALLBACKS
+};
+
+static int __init alsa_card_cs4281_init(void)
+{
+    int err;
+
+    if ((err = pci_module_init(&driver)) < 0) {
+#ifdef MODULE
+        //		printk(KERN_ERR "CS4281 soundcard not found or device busy\n");
+#endif
+        return err;
+    }
+    return 0;
+}
+
+static void __exit alsa_card_cs4281_exit(void)
+{
+    pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_cs4281_init)
+module_exit(alsa_card_cs4281_exit)
+
+#ifndef MODULE
+
+/* format is: snd-cs4281=snd_enable,snd_index,snd_id */
+
+static int __init alsa_card_cs4281_setup(char *str)
+{
+    static unsigned __initdata nr_dev = 0;
+
+    if (nr_dev >= SNDRV_CARDS)
+        return 0;
+    (void)(get_option(&str,&enable[nr_dev]) == 2 &&
+           get_option(&str,&index[nr_dev]) == 2 &&
+           get_id(&str,&id[nr_dev]) == 2);
+    nr_dev++;
+    return 1;
+}
+
+__setup("snd-cs4281=", alsa_card_cs4281_setup);
+
+#endif /* ifndef MODULE */
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/cs46xx/aaa
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/cs46xx/aaa	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/cs46xx/aaa	(revision 91)
@@ -0,0 +1,78 @@
+Open Watcom Make Version 1.2
+Portions Copyright (c) 1988-2002 Sybase, Inc. All Rights Reserved.
+Source code is available under the Sybase Open Watcom Public License.
+See http://www.openwatcom.org/ for details.
+	WCC386 -bt=os2v2 -e60 -5r -omlinear -s -w4 -ze -zdp -zl -zq -nt=CODE32 -zff -zgf -zp4 -mf -DKEE -DFLATSTACK -D__KERNEL__ -DMODULE -dTARGET_OS2 -dALSA_BUILD -D__ISAPNP__ -DCONFIG_ISAPNP -D__i386_ -dCONFIG_SND_CS46XX_NEW_DSP -i..\..\..\include;..\..\include;e:\owatcom\H -fobin\Release.KEE\cs46xx.obj cs46xx.c
+..\..\include\sound\pcm.h(248): Warning! W202: Symbol 'constrs_interval' has been defined, but not referenced
+..\..\include\sound\pcm.h(242): Warning! W202: Symbol 'constrs_mask' has been defined, but not referenced
+..\..\include\sound\pcm.h(738): Warning! W202: Symbol 'hw_param_interval_c' has been defined, but not referenced
+..\..\include\sound\pcm.h(732): Warning! W202: Symbol 'hw_param_mask_c' has been defined, but not referenced
+..\..\include\sound\control.h(135): Warning! W202: Symbol 'snd_ctl_build_ioff' has been defined, but not referenced
+	WCC386 -bt=os2v2 -e60 -5r -omlinear -s -w4 -ze -zdp -zl -zq -nt=CODE32 -zff -zgf -zp4 -mf -DKEE -DFLATSTACK -D__KERNEL__ -DMODULE -dTARGET_OS2 -dALSA_BUILD -D__ISAPNP__ -DCONFIG_ISAPNP -D__i386_ -dCONFIG_SND_CS46XX_NEW_DSP -i..\..\..\include;..\..\include;e:\owatcom\H -fobin\Release.KEE\cs46xx_lib.obj cs46xx_lib.c
+..\..\include\sound/pcm_params.h(25): Error! E1009: Expecting ',' but found '*'
+..\..\include\sound/pcm_params.h(26): Error! E1116: An id list not allowed except for function definition
+..\..\include\sound/pcm_params.h(27): Error! E1009: Expecting ',' but found '*'
+..\..\include\sound/pcm_params.h(27): Error! E1009: Expecting ',' but found '*'
+..\..\include\sound/pcm_params.h(27): Error! E1127: Type required in parameter list
+..\..\include\sound/pcm_params.h(28): Error! E1127: Type required in parameter list
+..\..\include\sound/pcm_params.h(28): Error! E1022: Missing or misspelled data type near 'snd_pcm_hw_param_t'
+..\..\include\sound/pcm_params.h(29): Error! E1009: Expecting ',' but found '*'
+..\..\include\sound/pcm_params.h(29): Error! E1009: Expecting ',' but found '*'
+..\..\include\sound/pcm_params.h(29): Error! E1127: Type required in parameter list
+..\..\include\sound/pcm_params.h(30): Error! E1127: Type required in parameter list
+..\..\include\sound/pcm_params.h(30): Error! E1022: Missing or misspelled data type near 'snd_pcm_hw_param_t'
+..\..\include\sound/pcm_params.h(31): Error! E1009: Expecting ',' but found '*'
+..\..\include\sound/pcm_params.h(32): Error! E1116: An id list not allowed except for function definition
+..\..\include\sound/pcm_params.h(33): Error! E1009: Expecting ',' but found '*'
+..\..\include\sound/pcm_params.h(34): Error! E1116: An id list not allowed except for function definition
+..\..\include\sound/pcm_params.h(35): Error! E1009: Expecting ',' but found '*'
+..\..\include\sound/pcm_params.h(36): Error! E1116: An id list not allowed except for function definition
+..\..\include\sound/pcm_params.h(46): Error! E1009: Expecting ';' but found 'unsigned'
+..\..\include\sound/pcm_params.h(46): Error! E1009: Expecting ',' but found 'v'
+..\..\include\sound/pcm_params.h(50): Error! E1011: Symbol 'v' has not been declared
+..\..\include\sound/pcm_params.h(71): Error! E1022: Missing or misspelled data type near 'inline'
+..\..\include\sound/pcm_params.h(71): Error! E1022: Missing or misspelled data type near 'size_t'
+..\..\include\sound/pcm_params.h(73): Error! E1011: Symbol 'snd_mask_t' has not been declared
+..\..\include\sound/pcm_params.h(76): Error! E1009: Expecting ';' but found 'void'
+..\..\include\sound/pcm_params.h(76): Error! E1009: Expecting ',' but found '*'
+..\..\include\sound/pcm_params.h(78): Error! E1011: Symbol 'mask' has not been declared
+..\..\include\sound/pcm_params.h(78): Error! E1029: Expression must be 'pointer to ...'
+..\..\include\sound/pcm_params.h(81): Error! E1009: Expecting ';' but found 'void'
+..\..\include\sound/pcm_params.h(81): Error! E1009: Expecting ',' but found '*'
+..\..\include\sound/pcm_params.h(83): Error! E1011: Symbol 'mask' has not been declared
+..\..\include\sound/pcm_params.h(83): Error! E1011: Symbol 'u_int32_t' has not been declared
+..\..\include\sound/pcm_params.h(86): Error! E1009: Expecting ';' but found 'int'
+..\..\include\sound/pcm_params.h(86): Error! E1009: Expecting ',' but found '*'
+..\..\include\sound/pcm_params.h(86): Error! E1009: Expecting ',' but found '*'
+..\..\include\sound/pcm_params.h(86): Error! E1127: Type required in parameter list
+..\..\include\sound/pcm_params.h(90): Error! E1033: Expression for '->' must be 'pointer to struct or union'
+..\..\include\sound/pcm_params.h(90): Error! E1009: Expecting ')' but found ']'
+..\..\include\sound/pcm_params.h(90): Error! E1063: Missing operand
+..\..\include\sound/pcm_params.h(90): Warning! W111: Meaningless use of an expression
+..\..\include\sound/pcm_params.h(90): Error! E1009: Expecting ';' but found ')'
+..\..\include\sound/pcm_params.h(95): Error! E1009: Expecting ';' but found 'unsigned'
+..\..\include\sound/pcm_params.h(95): Error! E1009: Expecting ',' but found '*'
+..\..\include\sound/pcm_params.h(95): Error! E1009: Expecting ',' but found '*'
+..\..\include\sound/pcm_params.h(95): Error! E1127: Type required in parameter list
+..\..\include\sound/pcm_params.h(100): Error! E1033: Expression for '->' must be 'pointer to struct or union'
+..\..\include\sound/pcm_params.h(100): Error! E1009: Expecting ')' but found ']'
+..\..\include\sound/pcm_params.h(100): Error! E1063: Missing operand
+..\..\include\sound/pcm_params.h(100): Warning! W111: Meaningless use of an expression
+..\..\include\sound/pcm_params.h(100): Error! E1009: Expecting ';' but found ')'
+..\..\include\sound/pcm_params.h(101): Error! E1033: Expression for '->' must be 'pointer to struct or union'
+..\..\include\sound/pcm_params.h(101): Error! E1009: Expecting ')' but found ']'
+..\..\include\sound/pcm_params.h(101): Error! E1009: Expecting ';' but found ')'
+..\..\include\sound/pcm_params.h(101): Warning! W201: Unreachable code
+..\..\include\sound/pcm_params.h(101): Error! E1063: Missing operand
+..\..\include\sound/pcm_params.h(101): Warning! W111: Meaningless use of an expression
+..\..\include\sound/pcm_params.h(101): Error! E1009: Expecting ';' but found ')'
+..\..\include\sound/pcm_params.h(101): Warning! W111: Meaningless use of an expression
+..\..\include\sound/pcm_params.h(106): Error! E1009: Expecting ';' but found 'unsigned'
+..\..\include\sound/pcm_params.h(106): Error! E1009: Expecting ',' but found '*'
+..\..\include\sound/pcm_params.h(106): Error! E1009: Expecting ',' but found '*'
+..\..\include\sound/pcm_params.h(106): Error! E1127: Type required in parameter list
+..\..\include\sound/pcm_params.h(111): Error! E1033: Expression for '->' must be 'pointer to struct or union'
+..\..\include\sound/pcm_params.h(111): Error! E1009: Expecting ')' but found ']'
+..\..\include\sound/pcm_params.h(111): Error! E1063: Missing operand
+..\..\include\sound/pcm_params.h(111): Warning! W111: Meaningless use of an expression
+..\..\include\sound/pcm_params.h(111): Error! E1147: Too many errors: compilation aborted
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/cs46xx/cs46xx.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/cs46xx/cs46xx.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/cs46xx/cs46xx.c	(revision 91)
@@ -0,0 +1,187 @@
+/*
+ *  The driver for the Cirrus Logic's Sound Fusion CS46XX based soundcards
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+/*
+  NOTES:
+  - sometimes the sound is metallic and sibilant, unloading and 
+    reloading the module may solve this.
+*/
+
+#include <sound/driver.h>
+#include <linux/pci.h>
+#include <linux/time.h>
+#include <linux/init.h>
+#include <linux/moduleparam.h>
+#include <sound/core.h>
+#include <sound/cs46xx.h>
+#include <sound/initval.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Cirrus Logic Sound Fusion CS46XX");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{Cirrus Logic,Sound Fusion (CS4280)},"
+		"{Cirrus Logic,Sound Fusion (CS4610)},"
+		"{Cirrus Logic,Sound Fusion (CS4612)},"
+		"{Cirrus Logic,Sound Fusion (CS4615)},"
+		"{Cirrus Logic,Sound Fusion (CS4622)},"
+		"{Cirrus Logic,Sound Fusion (CS4624)},"
+		"{Cirrus Logic,Sound Fusion (CS4630)}}");
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;	/* Enable this card */
+static int external_amp[SNDRV_CARDS] = {0,0,0,0,0,0,0,0};
+static int thinkpad[SNDRV_CARDS] = {0,0,0,0,0,0,0,0};
+static int mmap_valid[SNDRV_CARDS] = {1,1,1,1,1,1,1,1};
+
+//module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for the CS46xx soundcard.");
+//module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for the CS46xx soundcard.");
+//module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable CS46xx soundcard.");
+//module_param_array(external_amp, bool, NULL, 0444);
+MODULE_PARM_DESC(external_amp, "Force to enable external amplifer.");
+//module_param_array(thinkpad, bool, NULL, 0444);
+MODULE_PARM_DESC(thinkpad, "Force to enable Thinkpad's CLKRUN control.");
+//module_param_array(mmap_valid, bool, NULL, 0444);
+MODULE_PARM_DESC(mmap_valid, "Support OSS mmap.");
+
+static struct pci_device_id snd_cs46xx_ids[] = {
+        { 0x1013, 0x6001, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },   /* CS4280 */
+        { 0x1013, 0x6003, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },   /* CS4612 */
+        { 0x1013, 0x6004, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },   /* CS4615 */
+	{ 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, snd_cs46xx_ids);
+
+static int __devinit snd_card_cs46xx_probe(struct pci_dev *pci,
+					   const struct pci_device_id *pci_id)
+{
+	static int dev;
+	struct snd_card *card;
+	struct snd_cs46xx *chip;
+	int err;
+
+	if (dev >= SNDRV_CARDS)
+		return -ENODEV;
+	if (!enable[dev]) {
+		dev++;
+		return -ENOENT;
+	}
+
+	card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+	if (card == NULL)
+		return -ENOMEM;
+	if ((err = snd_cs46xx_create(card, pci,
+				     external_amp[dev], thinkpad[dev],
+				     &chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	card->private_data = chip;
+	chip->accept_valid = mmap_valid[dev];
+	if ((err = snd_cs46xx_pcm(chip, 0, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+	if ((err = snd_cs46xx_pcm_rear(chip,1, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_cs46xx_pcm_iec958(chip,2,NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+#endif
+	if ((err = snd_cs46xx_mixer(chip, 2)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+	if (chip->nr_ac97_codecs ==2) {
+		if ((err = snd_cs46xx_pcm_center_lfe(chip,3,NULL)) < 0) {
+			snd_card_free(card);
+			return err;
+		}
+	}
+#endif
+	if ((err = snd_cs46xx_midi(chip, 0, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_cs46xx_start_dsp(chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+
+	snd_cs46xx_gameport(chip);
+
+	strcpy(card->driver, "CS46xx");
+	strcpy(card->shortname, "Sound Fusion CS46xx");
+	sprintf(card->longname, "%s at 0x%lx/0x%lx, irq %i",
+		card->shortname,
+		chip->ba0_addr,
+		chip->ba1_addr,
+		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_card_cs46xx_remove(struct pci_dev *pci)
+{
+	snd_card_free(pci_get_drvdata(pci));
+	pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver driver = {
+	.name = "Sound Fusion CS46xx",
+	.id_table = snd_cs46xx_ids,
+	.probe = snd_card_cs46xx_probe,
+	.remove = __devexit_p(snd_card_cs46xx_remove),
+#ifdef CONFIG_PM
+	.suspend = snd_cs46xx_suspend,
+	.resume = snd_cs46xx_resume,
+#endif
+};
+
+static int __init alsa_card_cs46xx_init(void)
+{
+	return pci_register_driver(&driver);
+}
+
+static void __exit alsa_card_cs46xx_exit(void)
+{
+	pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_cs46xx_init)
+module_exit(alsa_card_cs46xx_exit)
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/cs46xx/cs46xx_image.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/cs46xx/cs46xx_image.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/cs46xx/cs46xx_image.h	(revision 91)
@@ -0,0 +1,3468 @@
+struct BA1struct {
+	struct {
+		unsigned long offset;
+		unsigned long size;
+	} memory[BA1_MEMORY_COUNT];
+	u32 map[BA1_DWORD_SIZE];
+};
+
+
+static struct BA1struct BA1Struct = {
+{{ 0x00000000, 0x00003000 },{ 0x00010000, 0x00003800 },{ 0x00020000, 0x00007000 }},
+{0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000163,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00200040,0x00008010,0x00000000,
+0x00000000,0x80000001,0x00000001,0x00060000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00900080,0x00000173,0x00000000,
+0x00000000,0x00000010,0x00800000,0x00900000,
+0xf2c0000f,0x00000200,0x00000000,0x00010600,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000163,0x330300c2,
+0x06000000,0x00000000,0x80008000,0x80008000,
+0x3fc0000f,0x00000301,0x00010400,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00b00000,0x00d0806d,0x330480c3,
+0x04800000,0x00000001,0x00800001,0x0000ffff,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x066a0600,0x06350070,0x0000929d,0x929d929d,
+0x00000000,0x0000735a,0x00000600,0x00000000,
+0x929d735a,0x8734abfe,0x00010000,0x735a735a,
+0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x0000804f,0x000000c3,
+0x05000000,0x00a00010,0x00000000,0x80008000,
+0x00000000,0x00000000,0x00000700,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000080,0x00a00000,0x0000809a,0x000000c2,
+0x07400000,0x00000000,0x80008000,0xffffffff,
+0x00c80028,0x00005555,0x00000000,0x000107a0,
+0x00c80028,0x000000c2,0x06800000,0x00000000,
+0x06e00080,0x00300000,0x000080bb,0x000000c9,
+0x07a00000,0x04000000,0x80008000,0xffffffff,
+0x00c80028,0x00005555,0x00000000,0x00000780,
+0x00c80028,0x000000c5,0xff800000,0x00000000,
+0x00640080,0x00c00000,0x00008197,0x000000c9,
+0x07800000,0x04000000,0x80008000,0xffffffff,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x0000805e,0x000000c1,
+0x00000000,0x00800000,0x80008000,0x80008000,
+0x00020000,0x0000ffff,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x929d0600,0x929d929d,0x929d929d,0x929d0000,
+0x929d929d,0x929d929d,0x929d929d,0x929d929d,
+0x929d929d,0x00100635,0x060b013f,0x00000004,
+0x00000001,0x007a0002,0x00000000,0x066e0610,
+0x0105929d,0x929d929d,0x929d929d,0x929d929d,
+0x929d929d,0xa431ac75,0x0001735a,0xa431ac75,
+0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+0xa431ac75,0xa431ac75,0xa431ac75,0x735a0051,
+0x00000000,0x929d929d,0x929d929d,0x929d929d,
+0x929d929d,0x929d929d,0x929d929d,0x929d929d,
+0x929d929d,0x929d929d,0x00000000,0x06400136,
+0x0000270f,0x00010000,0x007a0000,0x00000000,
+0x068e0645,0x0105929d,0x929d929d,0x929d929d,
+0x929d929d,0x929d929d,0xa431ac75,0x0001735a,
+0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+0x735a0100,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00010004,
+0x00040730,0x00001002,0x000f619e,0x00001003,
+0x00001705,0x00001400,0x000a411e,0x00001003,
+0x00040730,0x00001002,0x000f619e,0x00001003,
+0x00009705,0x00001400,0x000a411e,0x00001003,
+0x00040730,0x00001002,0x000f619e,0x00001003,
+0x00011705,0x00001400,0x000a411e,0x00001003,
+0x00040730,0x00001002,0x000f619e,0x00001003,
+0x00019705,0x00001400,0x000a411e,0x00001003,
+0x00040730,0x00001002,0x000f619e,0x00001003,
+0x00021705,0x00001400,0x000a411e,0x00001003,
+0x00040730,0x00001002,0x000f619e,0x00001003,
+0x00029705,0x00001400,0x000a411e,0x00001003,
+0x00040730,0x00001002,0x000f619e,0x00001003,
+0x00031705,0x00001400,0x000a411e,0x00001003,
+0x00040730,0x00001002,0x000f619e,0x00001003,
+0x00039705,0x00001400,0x000a411e,0x00001003,
+0x000fe19e,0x00001003,0x0009c730,0x00001003,
+0x0008e19c,0x00001003,0x000083c1,0x00093040,
+0x00098730,0x00001002,0x000ee19e,0x00001003,
+0x00009705,0x00001400,0x000a211e,0x00001003,
+0x00098730,0x00001002,0x000ee19e,0x00001003,
+0x00011705,0x00001400,0x000a211e,0x00001003,
+0x00098730,0x00001002,0x000ee19e,0x00001003,
+0x00019705,0x00001400,0x000a211e,0x00001003,
+0x00098730,0x00001002,0x000ee19e,0x00001003,
+0x00021705,0x00001400,0x000a211e,0x00001003,
+0x00098730,0x00001002,0x000ee19e,0x00001003,
+0x00029705,0x00001400,0x000a211e,0x00001003,
+0x00098730,0x00001002,0x000ee19e,0x00001003,
+0x00031705,0x00001400,0x000a211e,0x00001003,
+0x00098730,0x00001002,0x000ee19e,0x00001003,
+0x00039705,0x00001400,0x000a211e,0x00001003,
+0x0000a730,0x00001008,0x000e2730,0x00001002,
+0x0000a731,0x00001002,0x0000a731,0x00001002,
+0x0000a731,0x00001002,0x0000a731,0x00001002,
+0x0000a731,0x00001002,0x0000a731,0x00001002,
+0x00000000,0x00000000,0x000f619c,0x00001003,
+0x0007f801,0x000c0000,0x00000037,0x00001000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x000c0000,0x00000000,0x00000000,
+0x0000373c,0x00001000,0x00000000,0x00000000,
+0x000ee19c,0x00001003,0x0007f801,0x000c0000,
+0x00000037,0x00001000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x0000273c,0x00001000,
+0x00000033,0x00001000,0x000e679e,0x00001003,
+0x00007705,0x00001400,0x000ac71e,0x00001003,
+0x00087fc1,0x000c3be0,0x0007f801,0x000c0000,
+0x00000037,0x00001000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x0000a730,0x00001003,
+0x00000033,0x00001000,0x0007f801,0x000c0000,
+0x00000037,0x00001000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x000c0000,
+0x00000032,0x00001000,0x0000273d,0x00001000,
+0x0004a730,0x00001003,0x00000f41,0x00097140,
+0x0000a841,0x0009b240,0x0000a0c1,0x0009f040,
+0x0001c641,0x00093540,0x0001cec1,0x0009b5c0,
+0x00000000,0x00000000,0x0001bf05,0x0003fc40,
+0x00002725,0x000aa400,0x00013705,0x00093a00,
+0x0000002e,0x0009d6c0,0x00038630,0x00001004,
+0x0004ef0a,0x000eb785,0x0003fc8a,0x00000000,
+0x00000000,0x000c70e0,0x0007d182,0x0002c640,
+0x00000630,0x00001004,0x000799b8,0x0002c6c0,
+0x00031705,0x00092240,0x00039f05,0x000932c0,
+0x0003520a,0x00000000,0x00040731,0x0000100b,
+0x00010705,0x000b20c0,0x00000000,0x000eba44,
+0x00032108,0x000c60c4,0x00065208,0x000c2917,
+0x000406b0,0x00001007,0x00012f05,0x00036880,
+0x0002818e,0x000c0000,0x0004410a,0x00000000,
+0x00040630,0x00001007,0x00029705,0x000c0000,
+0x00000000,0x00000000,0x00003fc1,0x0003fc40,
+0x000037c1,0x00091b40,0x00003fc1,0x000911c0,
+0x000037c1,0x000957c0,0x00003fc1,0x000951c0,
+0x000037c1,0x00000000,0x00003fc1,0x000991c0,
+0x000037c1,0x00000000,0x00003fc1,0x0009d1c0,
+0x000037c1,0x00000000,0x0001ccc1,0x000915c0,
+0x0001c441,0x0009d800,0x0009cdc1,0x00091240,
+0x0001c541,0x00091d00,0x0009cfc1,0x00095240,
+0x0001c741,0x00095c80,0x000e8ca9,0x00099240,
+0x000e85ad,0x00095640,0x00069ca9,0x00099d80,
+0x000e952d,0x00099640,0x000eaca9,0x0009d6c0,
+0x000ea5ad,0x00091a40,0x0006bca9,0x0009de80,
+0x000eb52d,0x00095a40,0x000ecca9,0x00099ac0,
+0x000ec5ad,0x0009da40,0x000edca9,0x0009d300,
+0x000a6e0a,0x00001000,0x000ed52d,0x00091e40,
+0x000eeca9,0x00095ec0,0x000ee5ad,0x00099e40,
+0x0006fca9,0x00002500,0x000fb208,0x000c59a0,
+0x000ef52d,0x0009de40,0x00068ca9,0x000912c1,
+0x000683ad,0x00095241,0x00020f05,0x000991c1,
+0x00000000,0x00000000,0x00086f88,0x00001000,
+0x0009cf81,0x000b5340,0x0009c701,0x000b92c0,
+0x0009de81,0x000bd300,0x0009d601,0x000b1700,
+0x0001fd81,0x000b9d80,0x0009f501,0x000b57c0,
+0x000a0f81,0x000bd740,0x00020701,0x000b5c80,
+0x000a1681,0x000b97c0,0x00021601,0x00002500,
+0x000a0701,0x000b9b40,0x000a0f81,0x000b1bc0,
+0x00021681,0x00002d00,0x00020f81,0x000bd800,
+0x000a0701,0x000b5bc0,0x00021601,0x00003500,
+0x000a0f81,0x000b5f40,0x000a0701,0x000bdbc0,
+0x00021681,0x00003d00,0x00020f81,0x000b1d00,
+0x000a0701,0x000b1fc0,0x00021601,0x00020500,
+0x00020f81,0x000b1341,0x000a0701,0x000b9fc0,
+0x00021681,0x00020d00,0x00020f81,0x000bde80,
+0x000a0701,0x000bdfc0,0x00021601,0x00021500,
+0x00020f81,0x000b9341,0x00020701,0x000b53c1,
+0x00021681,0x00021d00,0x000a0f81,0x000d0380,
+0x0000b601,0x000b15c0,0x00007b01,0x00000000,
+0x00007b81,0x000bd1c0,0x00007b01,0x00000000,
+0x00007b81,0x000b91c0,0x00007b01,0x000b57c0,
+0x00007b81,0x000b51c0,0x00007b01,0x000b1b40,
+0x00007b81,0x000b11c0,0x00087b01,0x000c3dc0,
+0x0007e488,0x000d7e45,0x00000000,0x000d7a44,
+0x0007e48a,0x00000000,0x00011f05,0x00084080,
+0x00000000,0x00000000,0x00001705,0x000b3540,
+0x00008a01,0x000bf040,0x00007081,0x000bb5c0,
+0x00055488,0x00000000,0x0000d482,0x0003fc40,
+0x0003fc88,0x00000000,0x0001e401,0x000b3a00,
+0x0001ec81,0x000bd6c0,0x0004ef08,0x000eb784,
+0x000c86b0,0x00001007,0x00008281,0x000bb240,
+0x0000b801,0x000b7140,0x00007888,0x00000000,
+0x0000073c,0x00001000,0x0007f188,0x000c0000,
+0x00000000,0x00000000,0x00055288,0x000c555c,
+0x0005528a,0x000c0000,0x0009fa88,0x000c5d00,
+0x0000fa88,0x00000000,0x00000032,0x00001000,
+0x0000073d,0x00001000,0x0007f188,0x000c0000,
+0x00000000,0x00000000,0x0008c01c,0x00001003,
+0x00002705,0x00001008,0x0008b201,0x000c1392,
+0x0000ba01,0x00000000,0x00008731,0x00001400,
+0x0004c108,0x000fe0c4,0x00057488,0x00000000,
+0x000a6388,0x00001001,0x0008b334,0x000bc141,
+0x0003020e,0x00000000,0x000886b0,0x00001008,
+0x00003625,0x000c5dfa,0x000a638a,0x00001001,
+0x0008020e,0x00001002,0x0008a6b0,0x00001008,
+0x0007f301,0x00000000,0x00000000,0x00000000,
+0x00002725,0x000a8c40,0x000000ae,0x00000000,
+0x000d8630,0x00001008,0x00000000,0x000c74e0,
+0x0007d182,0x0002d640,0x000a8630,0x00001008,
+0x000799b8,0x0002d6c0,0x0000748a,0x000c3ec5,
+0x0007420a,0x000c0000,0x00062208,0x000c4117,
+0x00070630,0x00001009,0x00000000,0x000c0000,
+0x0001022e,0x00000000,0x0003a630,0x00001009,
+0x00000000,0x000c0000,0x00000036,0x00001000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x0002a730,0x00001008,0x0007f801,0x000c0000,
+0x00000037,0x00001000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x0002a730,0x00001008,
+0x00000033,0x00001000,0x0002a705,0x00001008,
+0x00007a01,0x000c0000,0x000e6288,0x000d550a,
+0x0006428a,0x00000000,0x00060730,0x0000100a,
+0x00000000,0x000c0000,0x00000000,0x00000000,
+0x0007aab0,0x00034880,0x00078fb0,0x0000100b,
+0x00057488,0x00000000,0x00033b94,0x00081140,
+0x000183ae,0x00000000,0x000786b0,0x0000100b,
+0x00022f05,0x000c3545,0x0000eb8a,0x00000000,
+0x00042731,0x00001003,0x0007aab0,0x00034880,
+0x00048fb0,0x0000100a,0x00057488,0x00000000,
+0x00033b94,0x00081140,0x000183ae,0x00000000,
+0x000806b0,0x0000100b,0x00022f05,0x00000000,
+0x00007401,0x00091140,0x00048f05,0x000951c0,
+0x00042731,0x00001003,0x0000473d,0x00001000,
+0x000f19b0,0x000bbc47,0x00080000,0x000bffc7,
+0x000fe19e,0x00001003,0x00000000,0x00000000,
+0x0008e19c,0x00001003,0x000083c1,0x00093040,
+0x00000f41,0x00097140,0x0000a841,0x0009b240,
+0x0000a0c1,0x0009f040,0x0001c641,0x00093540,
+0x0001cec1,0x0009b5c0,0x00000000,0x000fdc44,
+0x00055208,0x00000000,0x00010705,0x000a2880,
+0x0000a23a,0x00093a00,0x0003fc8a,0x000df6c5,
+0x0004ef0a,0x000c0000,0x00012f05,0x00036880,
+0x00065308,0x000c2997,0x000d86b0,0x0000100a,
+0x0004410a,0x000d40c7,0x00000000,0x00000000,
+0x00080730,0x00001004,0x00056f0a,0x000ea105,
+0x00000000,0x00000000,0x0000473d,0x00001000,
+0x000f19b0,0x000bbc47,0x00080000,0x000bffc7,
+0x0000273d,0x00001000,0x00000000,0x000eba44,
+0x00048f05,0x0000f440,0x00007401,0x0000f7c0,
+0x00000734,0x00001000,0x00010705,0x000a6880,
+0x00006a88,0x000c75c4,0x00000000,0x000e5084,
+0x00000000,0x000eba44,0x00087401,0x000e4782,
+0x00000734,0x00001000,0x00010705,0x000a6880,
+0x00006a88,0x000c75c4,0x0007c108,0x000c0000,
+0x0007e721,0x000bed40,0x00005f25,0x000badc0,
+0x0003ba97,0x000beb80,0x00065590,0x000b2e00,
+0x00033217,0x00003ec0,0x00065590,0x000b8e40,
+0x0003ed80,0x000491c0,0x00073fb0,0x00074c80,
+0x000283a0,0x0000100c,0x000ee388,0x00042970,
+0x00008301,0x00021ef2,0x000b8f14,0x0000000f,
+0x000c4d8d,0x0000001b,0x000d6dc2,0x000e06c6,
+0x000032ac,0x000c3916,0x0004edc2,0x00074c80,
+0x00078898,0x00001000,0x00038894,0x00000032,
+0x000c4d8d,0x00092e1b,0x000d6dc2,0x000e06c6,
+0x0004edc2,0x000c1956,0x0000722c,0x00034a00,
+0x00041705,0x0009ed40,0x00058730,0x00001400,
+0x000d7488,0x000c3a00,0x00048f05,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000,
+0x00000000,0x00000000,0x00000000,0x00000000}
+ };
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/cs46xx/cs46xx_lib.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/cs46xx/cs46xx_lib.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/cs46xx/cs46xx_lib.c	(revision 91)
@@ -0,0 +1,3896 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *                   Abramo Bagnara <abramo@alsa-project.org>
+ *                   Cirrus Logic, Inc.
+ *  Routines for control of Cirrus Logic CS461x chips
+ *
+ *  KNOWN BUGS:
+ *    - Sometimes the SPDIF input DSP tasks get's unsynchronized
+ *      and the SPDIF get somewhat "distorcionated", or/and left right channel
+ *      are swapped. To get around this problem when it happens, mute and unmute 
+ *      the SPDIF input mixer controll.
+ *    - On the Hercules Game Theater XP the amplifier are sometimes turned
+ *      off on inadecuate moments which causes distorcions on sound.
+ *
+ *  TODO:
+ *    - Secondary CODEC on some soundcards
+ *    - SPDIF input support for other sample rates then 48khz
+ *    - Posibility to mix the SPDIF output with analog sources.
+ *    - PCM channels for Center and LFE on secondary codec
+ *
+ *  NOTE: with CONFIG_SND_CS46XX_NEW_DSP unset uses old DSP image (which
+ *        is default configuration), no SPDIF, no secondary codec, no
+ *        multi channel PCM.  But known to work.
+ *
+ *  FINALLY: A credit to the developers Tom and Jordan 
+ *           at Cirrus for have helping me out with the DSP, however we
+ *           still don't have sufficient documentation and technical
+ *           references to be able to implement all fancy feutures
+ *           supported by the cs46xx DSP's. 
+ *           Benny <benny@hostmobility.com>
+ *                
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/pm.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+//#include <linux/gameport.h>
+#include <linux/mutex.h>
+
+
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/info.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/cs46xx.h>
+
+#include <asm/io.h>
+
+#include "cs46xx_lib.h"
+#include "dsp_spos.h"
+
+static void amp_voyetra(struct snd_cs46xx *chip, int change);
+
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+static struct snd_pcm_ops snd_cs46xx_playback_rear_ops;
+static struct snd_pcm_ops snd_cs46xx_playback_indirect_rear_ops;
+static struct snd_pcm_ops snd_cs46xx_playback_clfe_ops;
+static struct snd_pcm_ops snd_cs46xx_playback_indirect_clfe_ops;
+static struct snd_pcm_ops snd_cs46xx_playback_iec958_ops;
+static struct snd_pcm_ops snd_cs46xx_playback_indirect_iec958_ops;
+#endif
+
+static struct snd_pcm_ops snd_cs46xx_playback_ops;
+static struct snd_pcm_ops snd_cs46xx_playback_indirect_ops;
+static struct snd_pcm_ops snd_cs46xx_capture_ops;
+static struct snd_pcm_ops snd_cs46xx_capture_indirect_ops;
+
+static unsigned short snd_cs46xx_codec_read(struct snd_cs46xx *chip,
+					    unsigned short reg,
+					    int codec_index)
+{
+	int count;
+	unsigned short result,tmp;
+	u32 offset = 0;
+	snd_assert ( (codec_index == CS46XX_PRIMARY_CODEC_INDEX) ||
+		     (codec_index == CS46XX_SECONDARY_CODEC_INDEX),
+		     return -EINVAL);
+
+	chip->active_ctrl(chip, 1);
+
+	if (codec_index == CS46XX_SECONDARY_CODEC_INDEX)
+		offset = CS46XX_SECONDARY_CODEC_OFFSET;
+
+	/*
+	 *  1. Write ACCAD = Command Address Register = 46Ch for AC97 register address
+	 *  2. Write ACCDA = Command Data Register = 470h    for data to write to AC97 
+	 *  3. Write ACCTL = Control Register = 460h for initiating the write7---55
+	 *  4. Read ACCTL = 460h, DCV should be reset by now and 460h = 17h
+	 *  5. if DCV not cleared, break and return error
+	 *  6. Read ACSTS = Status Register = 464h, check VSTS bit
+	 */
+
+	snd_cs46xx_peekBA0(chip, BA0_ACSDA + offset);
+
+	tmp = snd_cs46xx_peekBA0(chip, BA0_ACCTL);
+	if ((tmp & ACCTL_VFRM) == 0) {
+		snd_printk(KERN_WARNING  "cs46xx: ACCTL_VFRM not set 0x%x\n",tmp);
+		snd_cs46xx_pokeBA0(chip, BA0_ACCTL, (tmp & (~ACCTL_ESYN)) | ACCTL_VFRM );
+		msleep(50);
+		tmp = snd_cs46xx_peekBA0(chip, BA0_ACCTL + offset);
+		snd_cs46xx_pokeBA0(chip, BA0_ACCTL, tmp | ACCTL_ESYN | ACCTL_VFRM );
+
+	}
+
+	/*
+	 *  Setup the AC97 control registers on the CS461x to send the
+	 *  appropriate command to the AC97 to perform the read.
+	 *  ACCAD = Command Address Register = 46Ch
+	 *  ACCDA = Command Data Register = 470h
+	 *  ACCTL = Control Register = 460h
+	 *  set DCV - will clear when process completed
+	 *  set CRW - Read command
+	 *  set VFRM - valid frame enabled
+	 *  set ESYN - ASYNC generation enabled
+	 *  set RSTN - ARST# inactive, AC97 codec not reset
+	 */
+
+	snd_cs46xx_pokeBA0(chip, BA0_ACCAD, reg);
+	snd_cs46xx_pokeBA0(chip, BA0_ACCDA, 0);
+	if (codec_index == CS46XX_PRIMARY_CODEC_INDEX) {
+		snd_cs46xx_pokeBA0(chip, BA0_ACCTL,/* clear ACCTL_DCV */ ACCTL_CRW | 
+				   ACCTL_VFRM | ACCTL_ESYN |
+				   ACCTL_RSTN);
+		snd_cs46xx_pokeBA0(chip, BA0_ACCTL, ACCTL_DCV | ACCTL_CRW |
+				   ACCTL_VFRM | ACCTL_ESYN |
+				   ACCTL_RSTN);
+	} else {
+		snd_cs46xx_pokeBA0(chip, BA0_ACCTL, ACCTL_DCV | ACCTL_TC |
+				   ACCTL_CRW | ACCTL_VFRM | ACCTL_ESYN |
+				   ACCTL_RSTN);
+	}
+
+	/*
+	 *  Wait for the read to occur.
+	 */
+	for (count = 0; count < 1000; count++) {
+		/*
+		 *  First, we want to wait for a short time.
+	 	 */
+		udelay(10);
+		/*
+		 *  Now, check to see if the read has completed.
+		 *  ACCTL = 460h, DCV should be reset by now and 460h = 17h
+		 */
+		if (!(snd_cs46xx_peekBA0(chip, BA0_ACCTL) & ACCTL_DCV))
+			goto ok1;
+	}
+
+	snd_printk(KERN_ERR "AC'97 read problem (ACCTL_DCV), reg = 0x%x\n", reg);
+	result = 0xffff;
+	goto end;
+	
+ ok1:
+	/*
+	 *  Wait for the valid status bit to go active.
+	 */
+	for (count = 0; count < 100; count++) {
+		/*
+		 *  Read the AC97 status register.
+		 *  ACSTS = Status Register = 464h
+		 *  VSTS - Valid Status
+		 */
+		if (snd_cs46xx_peekBA0(chip, BA0_ACSTS + offset) & ACSTS_VSTS)
+			goto ok2;
+		udelay(10);
+	}
+	
+	snd_printk(KERN_ERR "AC'97 read problem (ACSTS_VSTS), codec_index %d, reg = 0x%x\n", codec_index, reg);
+	result = 0xffff;
+	goto end;
+
+ ok2:
+	/*
+	 *  Read the data returned from the AC97 register.
+	 *  ACSDA = Status Data Register = 474h
+	 */
+#if 0
+	printk("e) reg = 0x%x, val = 0x%x, BA0_ACCAD = 0x%x\n", reg,
+			snd_cs46xx_peekBA0(chip, BA0_ACSDA),
+			snd_cs46xx_peekBA0(chip, BA0_ACCAD));
+#endif
+
+	//snd_cs46xx_peekBA0(chip, BA0_ACCAD);
+	result = snd_cs46xx_peekBA0(chip, BA0_ACSDA + offset);
+ end:
+	chip->active_ctrl(chip, -1);
+	return result;
+}
+
+static unsigned short snd_cs46xx_ac97_read(struct snd_ac97 * ac97,
+					    unsigned short reg)
+{
+	struct snd_cs46xx *chip = ac97->private_data;
+	unsigned short val;
+	int codec_index = ac97->num;
+
+	snd_assert(codec_index == CS46XX_PRIMARY_CODEC_INDEX ||
+		   codec_index == CS46XX_SECONDARY_CODEC_INDEX,
+		   return 0xffff);
+
+	val = snd_cs46xx_codec_read(chip, reg, codec_index);
+
+	return val;
+}
+
+
+static void snd_cs46xx_codec_write(struct snd_cs46xx *chip,
+				   unsigned short reg,
+				   unsigned short val,
+				   int codec_index)
+{
+	int count;
+
+	snd_assert ((codec_index == CS46XX_PRIMARY_CODEC_INDEX) ||
+		    (codec_index == CS46XX_SECONDARY_CODEC_INDEX),
+		    return);
+
+	chip->active_ctrl(chip, 1);
+
+	/*
+	 *  1. Write ACCAD = Command Address Register = 46Ch for AC97 register address
+	 *  2. Write ACCDA = Command Data Register = 470h    for data to write to AC97
+	 *  3. Write ACCTL = Control Register = 460h for initiating the write
+	 *  4. Read ACCTL = 460h, DCV should be reset by now and 460h = 07h
+	 *  5. if DCV not cleared, break and return error
+	 */
+
+	/*
+	 *  Setup the AC97 control registers on the CS461x to send the
+	 *  appropriate command to the AC97 to perform the read.
+	 *  ACCAD = Command Address Register = 46Ch
+	 *  ACCDA = Command Data Register = 470h
+	 *  ACCTL = Control Register = 460h
+	 *  set DCV - will clear when process completed
+	 *  reset CRW - Write command
+	 *  set VFRM - valid frame enabled
+	 *  set ESYN - ASYNC generation enabled
+	 *  set RSTN - ARST# inactive, AC97 codec not reset
+         */
+	snd_cs46xx_pokeBA0(chip, BA0_ACCAD , reg);
+	snd_cs46xx_pokeBA0(chip, BA0_ACCDA , val);
+	snd_cs46xx_peekBA0(chip, BA0_ACCTL);
+
+	if (codec_index == CS46XX_PRIMARY_CODEC_INDEX) {
+		snd_cs46xx_pokeBA0(chip, BA0_ACCTL, /* clear ACCTL_DCV */ ACCTL_VFRM |
+				   ACCTL_ESYN | ACCTL_RSTN);
+		snd_cs46xx_pokeBA0(chip, BA0_ACCTL, ACCTL_DCV | ACCTL_VFRM |
+				   ACCTL_ESYN | ACCTL_RSTN);
+	} else {
+		snd_cs46xx_pokeBA0(chip, BA0_ACCTL, ACCTL_DCV | ACCTL_TC |
+				   ACCTL_VFRM | ACCTL_ESYN | ACCTL_RSTN);
+	}
+
+	for (count = 0; count < 4000; count++) {
+		/*
+		 *  First, we want to wait for a short time.
+		 */
+		udelay(10);
+		/*
+		 *  Now, check to see if the write has completed.
+		 *  ACCTL = 460h, DCV should be reset by now and 460h = 07h
+		 */
+		if (!(snd_cs46xx_peekBA0(chip, BA0_ACCTL) & ACCTL_DCV)) {
+			goto end;
+		}
+	}
+	snd_printk(KERN_ERR "AC'97 write problem, codec_index = %d, reg = 0x%x, val = 0x%x\n", codec_index, reg, val);
+ end:
+	chip->active_ctrl(chip, -1);
+}
+
+static void snd_cs46xx_ac97_write(struct snd_ac97 *ac97,
+				   unsigned short reg,
+				   unsigned short val)
+{
+	struct snd_cs46xx *chip = ac97->private_data;
+	int codec_index = ac97->num;
+
+	snd_assert(codec_index == CS46XX_PRIMARY_CODEC_INDEX ||
+		   codec_index == CS46XX_SECONDARY_CODEC_INDEX,
+		   return);
+
+	snd_cs46xx_codec_write(chip, reg, val, codec_index);
+}
+
+
+/*
+ *  Chip initialization
+ */
+
+int snd_cs46xx_download(struct snd_cs46xx *chip,
+			u32 *src,
+                        unsigned long offset,
+                        unsigned long len)
+{
+	void __iomem *dst;
+	unsigned int bank = offset >> 16;
+	offset = offset & 0xffff;
+
+	snd_assert(!(offset & 3) && !(len & 3), return -EINVAL);
+	dst = chip->region.idx[bank+1].remap_addr + offset;
+	len /= sizeof(u32);
+
+	/* writel already converts 32-bit value to right endianess */
+	while (len-- > 0) {
+		writel(*src++, dst);
+		dst += sizeof(u32);
+	}
+	return 0;
+}
+
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+
+#include "imgs/cwc4630.h"
+#include "imgs/cwcasync.h"
+#include "imgs/cwcsnoop.h"
+#include "imgs/cwcbinhack.h"
+#include "imgs/cwcdma.h"
+
+int snd_cs46xx_clear_BA1(struct snd_cs46xx *chip,
+                         unsigned long offset,
+                         unsigned long len) 
+{
+	void __iomem *dst;
+	unsigned int bank = offset >> 16;
+	offset = offset & 0xffff;
+
+	snd_assert(!(offset & 3) && !(len & 3), return -EINVAL);
+	dst = chip->region.idx[bank+1].remap_addr + offset;
+	len /= sizeof(u32);
+
+	/* writel already converts 32-bit value to right endianess */
+	while (len-- > 0) {
+		writel(0, dst);
+		dst += sizeof(u32);
+	}
+	return 0;
+}
+
+#else /* old DSP image */
+
+#include "cs46xx_image.h"
+
+int snd_cs46xx_download_image(struct snd_cs46xx *chip)
+{
+	int idx, err;
+	unsigned long offset = 0;
+
+	for (idx = 0; idx < BA1_MEMORY_COUNT; idx++) {
+		if ((err = snd_cs46xx_download(chip,
+					       &BA1Struct.map[offset],
+					       BA1Struct.memory[idx].offset,
+					       BA1Struct.memory[idx].size)) < 0)
+			return err;
+		offset += BA1Struct.memory[idx].size >> 2;
+	}	
+	return 0;
+}
+#endif /* CONFIG_SND_CS46XX_NEW_DSP */
+
+/*
+ *  Chip reset
+ */
+
+static void snd_cs46xx_reset(struct snd_cs46xx *chip)
+{
+	int idx;
+
+	/*
+	 *  Write the reset bit of the SP control register.
+	 */
+	snd_cs46xx_poke(chip, BA1_SPCR, SPCR_RSTSP);
+
+	/*
+	 *  Write the control register.
+	 */
+	snd_cs46xx_poke(chip, BA1_SPCR, SPCR_DRQEN);
+
+	/*
+	 *  Clear the trap registers.
+	 */
+	for (idx = 0; idx < 8; idx++) {
+		snd_cs46xx_poke(chip, BA1_DREG, DREG_REGID_TRAP_SELECT + idx);
+		snd_cs46xx_poke(chip, BA1_TWPR, 0xFFFF);
+	}
+	snd_cs46xx_poke(chip, BA1_DREG, 0);
+
+	/*
+	 *  Set the frame timer to reflect the number of cycles per frame.
+	 */
+	snd_cs46xx_poke(chip, BA1_FRMT, 0xadf);
+}
+
+static int cs46xx_wait_for_fifo(struct snd_cs46xx * chip,int retry_timeout) 
+{
+	u32 i, status = 0;
+	/*
+	 * Make sure the previous FIFO write operation has completed.
+	 */
+	for(i = 0; i < 50; i++){
+		status = snd_cs46xx_peekBA0(chip, BA0_SERBST);
+    
+		if( !(status & SERBST_WBSY) )
+			break;
+
+		mdelay(retry_timeout);
+	}
+  
+	if(status & SERBST_WBSY) {
+		snd_printk( KERN_ERR "cs46xx: failure waiting for FIFO command to complete\n");
+
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void snd_cs46xx_clear_serial_FIFOs(struct snd_cs46xx *chip)
+{
+	int idx, powerdown = 0;
+	unsigned int tmp;
+
+	/*
+	 *  See if the devices are powered down.  If so, we must power them up first
+	 *  or they will not respond.
+	 */
+	tmp = snd_cs46xx_peekBA0(chip, BA0_CLKCR1);
+	if (!(tmp & CLKCR1_SWCE)) {
+		snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, tmp | CLKCR1_SWCE);
+		powerdown = 1;
+	}
+
+	/*
+	 *  We want to clear out the serial port FIFOs so we don't end up playing
+	 *  whatever random garbage happens to be in them.  We fill the sample FIFOS
+	 *  with zero (silence).
+	 */
+	snd_cs46xx_pokeBA0(chip, BA0_SERBWP, 0);
+
+	/*
+	 *  Fill all 256 sample FIFO locations.
+	 */
+	for (idx = 0; idx < 0xFF; idx++) {
+		/*
+		 *  Make sure the previous FIFO write operation has completed.
+		 */
+		if (cs46xx_wait_for_fifo(chip,1)) {
+			snd_printdd ("failed waiting for FIFO at addr (%02X)\n",idx);
+
+			if (powerdown)
+				snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, tmp);
+          
+			break;
+		}
+		/*
+		 *  Write the serial port FIFO index.
+		 */
+		snd_cs46xx_pokeBA0(chip, BA0_SERBAD, idx);
+		/*
+		 *  Tell the serial port to load the new value into the FIFO location.
+		 */
+		snd_cs46xx_pokeBA0(chip, BA0_SERBCM, SERBCM_WRC);
+	}
+	/*
+	 *  Now, if we powered up the devices, then power them back down again.
+	 *  This is kinda ugly, but should never happen.
+	 */
+	if (powerdown)
+		snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, tmp);
+}
+
+static void snd_cs46xx_proc_start(struct snd_cs46xx *chip)
+{
+	int cnt;
+
+	/*
+	 *  Set the frame timer to reflect the number of cycles per frame.
+	 */
+	snd_cs46xx_poke(chip, BA1_FRMT, 0xadf);
+	/*
+	 *  Turn on the run, run at frame, and DMA enable bits in the local copy of
+	 *  the SP control register.
+	 */
+	snd_cs46xx_poke(chip, BA1_SPCR, SPCR_RUN | SPCR_RUNFR | SPCR_DRQEN);
+	/*
+	 *  Wait until the run at frame bit resets itself in the SP control
+	 *  register.
+	 */
+	for (cnt = 0; cnt < 25; cnt++) {
+		udelay(50);
+		if (!(snd_cs46xx_peek(chip, BA1_SPCR) & SPCR_RUNFR))
+			break;
+	}
+
+	if (snd_cs46xx_peek(chip, BA1_SPCR) & SPCR_RUNFR)
+		snd_printk(KERN_ERR "SPCR_RUNFR never reset\n");
+}
+
+static void snd_cs46xx_proc_stop(struct snd_cs46xx *chip)
+{
+	/*
+	 *  Turn off the run, run at frame, and DMA enable bits in the local copy of
+	 *  the SP control register.
+	 */
+	snd_cs46xx_poke(chip, BA1_SPCR, 0);
+}
+
+/*
+ *  Sample rate routines
+ */
+
+#define GOF_PER_SEC 200
+
+static void snd_cs46xx_set_play_sample_rate(struct snd_cs46xx *chip, unsigned int rate)
+{
+	unsigned long flags;
+	unsigned int tmp1, tmp2;
+	unsigned int phiIncr;
+	unsigned int correctionPerGOF, correctionPerSec;
+
+	/*
+	 *  Compute the values used to drive the actual sample rate conversion.
+	 *  The following formulas are being computed, using inline assembly
+	 *  since we need to use 64 bit arithmetic to compute the values:
+	 *
+	 *  phiIncr = floor((Fs,in * 2^26) / Fs,out)
+	 *  correctionPerGOF = floor((Fs,in * 2^26 - Fs,out * phiIncr) /
+         *                                   GOF_PER_SEC)
+         *  ulCorrectionPerSec = Fs,in * 2^26 - Fs,out * phiIncr -M
+         *                       GOF_PER_SEC * correctionPerGOF
+	 *
+	 *  i.e.
+	 *
+	 *  phiIncr:other = dividend:remainder((Fs,in * 2^26) / Fs,out)
+	 *  correctionPerGOF:correctionPerSec =
+	 *      dividend:remainder(ulOther / GOF_PER_SEC)
+	 */
+	tmp1 = rate << 16;
+	phiIncr = tmp1 / 48000;
+	tmp1 -= phiIncr * 48000;
+	tmp1 <<= 10;
+	phiIncr <<= 10;
+	tmp2 = tmp1 / 48000;
+	phiIncr += tmp2;
+	tmp1 -= tmp2 * 48000;
+	correctionPerGOF = tmp1 / GOF_PER_SEC;
+	tmp1 -= correctionPerGOF * GOF_PER_SEC;
+	correctionPerSec = tmp1;
+
+	/*
+	 *  Fill in the SampleRateConverter control block.
+	 */
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_cs46xx_poke(chip, BA1_PSRC,
+	  ((correctionPerSec << 16) & 0xFFFF0000) | (correctionPerGOF & 0xFFFF));
+	snd_cs46xx_poke(chip, BA1_PPI, phiIncr);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+static void snd_cs46xx_set_capture_sample_rate(struct snd_cs46xx *chip, unsigned int rate)
+{
+	unsigned long flags;
+	unsigned int phiIncr, coeffIncr, tmp1, tmp2;
+	unsigned int correctionPerGOF, correctionPerSec, initialDelay;
+	unsigned int frameGroupLength, cnt;
+
+	/*
+	 *  We can only decimate by up to a factor of 1/9th the hardware rate.
+	 *  Correct the value if an attempt is made to stray outside that limit.
+	 */
+	if ((rate * 9) < 48000)
+		rate = 48000 / 9;
+
+	/*
+	 *  We can not capture at at rate greater than the Input Rate (48000).
+	 *  Return an error if an attempt is made to stray outside that limit.
+	 */
+	if (rate > 48000)
+		rate = 48000;
+
+	/*
+	 *  Compute the values used to drive the actual sample rate conversion.
+	 *  The following formulas are being computed, using inline assembly
+	 *  since we need to use 64 bit arithmetic to compute the values:
+	 *
+	 *     coeffIncr = -floor((Fs,out * 2^23) / Fs,in)
+	 *     phiIncr = floor((Fs,in * 2^26) / Fs,out)
+	 *     correctionPerGOF = floor((Fs,in * 2^26 - Fs,out * phiIncr) /
+	 *                                GOF_PER_SEC)
+	 *     correctionPerSec = Fs,in * 2^26 - Fs,out * phiIncr -
+	 *                          GOF_PER_SEC * correctionPerGOF
+	 *     initialDelay = ceil((24 * Fs,in) / Fs,out)
+	 *
+	 * i.e.
+	 *
+	 *     coeffIncr = neg(dividend((Fs,out * 2^23) / Fs,in))
+	 *     phiIncr:ulOther = dividend:remainder((Fs,in * 2^26) / Fs,out)
+	 *     correctionPerGOF:correctionPerSec =
+	 * 	    dividend:remainder(ulOther / GOF_PER_SEC)
+	 *     initialDelay = dividend(((24 * Fs,in) + Fs,out - 1) / Fs,out)
+	 */
+
+	tmp1 = rate << 16;
+	coeffIncr = tmp1 / 48000;
+	tmp1 -= coeffIncr * 48000;
+	tmp1 <<= 7;
+	coeffIncr <<= 7;
+	coeffIncr += tmp1 / 48000;
+	coeffIncr ^= 0xFFFFFFFF;
+	coeffIncr++;
+	tmp1 = 48000 << 16;
+	phiIncr = tmp1 / rate;
+	tmp1 -= phiIncr * rate;
+	tmp1 <<= 10;
+	phiIncr <<= 10;
+	tmp2 = tmp1 / rate;
+	phiIncr += tmp2;
+	tmp1 -= tmp2 * rate;
+	correctionPerGOF = tmp1 / GOF_PER_SEC;
+	tmp1 -= correctionPerGOF * GOF_PER_SEC;
+	correctionPerSec = tmp1;
+	initialDelay = ((48000 * 24) + rate - 1) / rate;
+
+	/*
+	 *  Fill in the VariDecimate control block.
+	 */
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_cs46xx_poke(chip, BA1_CSRC,
+		((correctionPerSec << 16) & 0xFFFF0000) | (correctionPerGOF & 0xFFFF));
+	snd_cs46xx_poke(chip, BA1_CCI, coeffIncr);
+	snd_cs46xx_poke(chip, BA1_CD,
+		(((BA1_VARIDEC_BUF_1 + (initialDelay << 2)) << 16) & 0xFFFF0000) | 0x80);
+	snd_cs46xx_poke(chip, BA1_CPI, phiIncr);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+	/*
+	 *  Figure out the frame group length for the write back task.  Basically,
+	 *  this is just the factors of 24000 (2^6*3*5^3) that are not present in
+	 *  the output sample rate.
+	 */
+	frameGroupLength = 1;
+	for (cnt = 2; cnt <= 64; cnt *= 2) {
+		if (((rate / cnt) * cnt) != rate)
+			frameGroupLength *= 2;
+	}
+	if (((rate / 3) * 3) != rate) {
+		frameGroupLength *= 3;
+	}
+	for (cnt = 5; cnt <= 125; cnt *= 5) {
+		if (((rate / cnt) * cnt) != rate) 
+			frameGroupLength *= 5;
+        }
+
+	/*
+	 * Fill in the WriteBack control block.
+	 */
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_cs46xx_poke(chip, BA1_CFG1, frameGroupLength);
+	snd_cs46xx_poke(chip, BA1_CFG2, (0x00800000 | frameGroupLength));
+	snd_cs46xx_poke(chip, BA1_CCST, 0x0000FFFF);
+	snd_cs46xx_poke(chip, BA1_CSPB, ((65536 * rate) / 24000));
+	snd_cs46xx_poke(chip, (BA1_CSPB + 4), 0x0000FFFF);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+/*
+ *  PCM part
+ */
+
+static void snd_cs46xx_pb_trans_copy(struct snd_pcm_substream *substream,
+				     struct snd_pcm_indirect *rec, size_t bytes)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_cs46xx_pcm * cpcm = runtime->private_data;
+	memcpy(cpcm->hw_buf.area + rec->hw_data, runtime->dma_area + rec->sw_data, bytes);
+}
+
+static int snd_cs46xx_playback_transfer(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_cs46xx_pcm * cpcm = runtime->private_data;
+	snd_pcm_indirect_playback_transfer(substream, &cpcm->pcm_rec, snd_cs46xx_pb_trans_copy);
+	return 0;
+}
+
+static void snd_cs46xx_cp_trans_copy(struct snd_pcm_substream *substream,
+				     struct snd_pcm_indirect *rec, size_t bytes)
+{
+	struct snd_cs46xx *chip = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	memcpy(runtime->dma_area + rec->sw_data,
+	       chip->capt.hw_buf.area + rec->hw_data, bytes);
+}
+
+static int snd_cs46xx_capture_transfer(struct snd_pcm_substream *substream)
+{
+	struct snd_cs46xx *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_indirect_capture_transfer(substream, &chip->capt.pcm_rec, snd_cs46xx_cp_trans_copy);
+	return 0;
+}
+
+static snd_pcm_uframes_t snd_cs46xx_playback_direct_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_cs46xx *chip = snd_pcm_substream_chip(substream);
+	size_t ptr;
+	struct snd_cs46xx_pcm *cpcm = substream->runtime->private_data;
+	snd_assert (cpcm->pcm_channel,return -ENXIO);
+
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+	ptr = snd_cs46xx_peek(chip, (cpcm->pcm_channel->pcm_reader_scb->address + 2) << 2);
+#else
+	ptr = snd_cs46xx_peek(chip, BA1_PBA);
+#endif
+	ptr -= cpcm->hw_buf.addr;
+	return ptr >> cpcm->shift;
+}
+
+static snd_pcm_uframes_t snd_cs46xx_playback_indirect_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_cs46xx *chip = snd_pcm_substream_chip(substream);
+	size_t ptr;
+	struct snd_cs46xx_pcm *cpcm = substream->runtime->private_data;
+
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+	snd_assert (cpcm->pcm_channel,return -ENXIO);
+	ptr = snd_cs46xx_peek(chip, (cpcm->pcm_channel->pcm_reader_scb->address + 2) << 2);
+#else
+	ptr = snd_cs46xx_peek(chip, BA1_PBA);
+#endif
+	ptr -= cpcm->hw_buf.addr;
+	return snd_pcm_indirect_playback_pointer(substream, &cpcm->pcm_rec, ptr);
+}
+
+static snd_pcm_uframes_t snd_cs46xx_capture_direct_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_cs46xx *chip = snd_pcm_substream_chip(substream);
+	size_t ptr = snd_cs46xx_peek(chip, BA1_CBA) - chip->capt.hw_buf.addr;
+	return ptr >> chip->capt.shift;
+}
+
+static snd_pcm_uframes_t snd_cs46xx_capture_indirect_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_cs46xx *chip = snd_pcm_substream_chip(substream);
+	size_t ptr = snd_cs46xx_peek(chip, BA1_CBA) - chip->capt.hw_buf.addr;
+	return snd_pcm_indirect_capture_pointer(substream, &chip->capt.pcm_rec, ptr);
+}
+
+static int snd_cs46xx_playback_trigger(struct snd_pcm_substream *substream,
+				       int cmd)
+{
+	struct snd_cs46xx *chip = snd_pcm_substream_chip(substream);
+	/*struct snd_pcm_runtime *runtime = substream->runtime;*/
+	int result = 0;
+
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+	struct snd_cs46xx_pcm *cpcm = substream->runtime->private_data;
+	if (! cpcm->pcm_channel) {
+		return -ENXIO;
+	}
+#endif
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+		/* magic value to unmute PCM stream  playback volume */
+		snd_cs46xx_poke(chip, (cpcm->pcm_channel->pcm_reader_scb->address + 
+				       SCBVolumeCtrl) << 2, 0x80008000);
+
+		if (cpcm->pcm_channel->unlinked)
+			cs46xx_dsp_pcm_link(chip,cpcm->pcm_channel);
+
+		if (substream->runtime->periods != CS46XX_FRAGS)
+			snd_cs46xx_playback_transfer(substream);
+#else
+		spin_lock(&chip->reg_lock);
+		if (substream->runtime->periods != CS46XX_FRAGS)
+			snd_cs46xx_playback_transfer(substream);
+		{ unsigned int tmp;
+		tmp = snd_cs46xx_peek(chip, BA1_PCTL);
+		tmp &= 0x0000ffff;
+		snd_cs46xx_poke(chip, BA1_PCTL, chip->play_ctl | tmp);
+		}
+		spin_unlock(&chip->reg_lock);
+#endif
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+		/* magic mute channel */
+		snd_cs46xx_poke(chip, (cpcm->pcm_channel->pcm_reader_scb->address + 
+				       SCBVolumeCtrl) << 2, 0xffffffff);
+
+		if (!cpcm->pcm_channel->unlinked)
+			cs46xx_dsp_pcm_unlink(chip,cpcm->pcm_channel);
+#else
+		spin_lock(&chip->reg_lock);
+		{ unsigned int tmp;
+		tmp = snd_cs46xx_peek(chip, BA1_PCTL);
+		tmp &= 0x0000ffff;
+		snd_cs46xx_poke(chip, BA1_PCTL, tmp);
+		}
+		spin_unlock(&chip->reg_lock);
+#endif
+		break;
+	default:
+		result = -EINVAL;
+		break;
+	}
+
+	return result;
+}
+
+static int snd_cs46xx_capture_trigger(struct snd_pcm_substream *substream,
+				      int cmd)
+{
+	struct snd_cs46xx *chip = snd_pcm_substream_chip(substream);
+	unsigned int tmp;
+	int result = 0;
+
+	spin_lock(&chip->reg_lock);
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		tmp = snd_cs46xx_peek(chip, BA1_CCTL);
+		tmp &= 0xffff0000;
+		snd_cs46xx_poke(chip, BA1_CCTL, chip->capt.ctl | tmp);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		tmp = snd_cs46xx_peek(chip, BA1_CCTL);
+		tmp &= 0xffff0000;
+		snd_cs46xx_poke(chip, BA1_CCTL, tmp);
+		break;
+	default:
+		result = -EINVAL;
+		break;
+	}
+	spin_unlock(&chip->reg_lock);
+
+	return result;
+}
+
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+static int _cs46xx_adjust_sample_rate (struct snd_cs46xx *chip, struct snd_cs46xx_pcm *cpcm,
+				       int sample_rate) 
+{
+
+	/* If PCMReaderSCB and SrcTaskSCB not created yet ... */
+	if ( cpcm->pcm_channel == NULL) {
+		cpcm->pcm_channel = cs46xx_dsp_create_pcm_channel (chip, sample_rate, 
+								   cpcm, cpcm->hw_buf.addr,cpcm->pcm_channel_id);
+		if (cpcm->pcm_channel == NULL) {
+			snd_printk(KERN_ERR "cs46xx: failed to create virtual PCM channel\n");
+			return -ENOMEM;
+		}
+		cpcm->pcm_channel->sample_rate = sample_rate;
+	} else
+	/* if sample rate is changed */
+	if ((int)cpcm->pcm_channel->sample_rate != sample_rate) {
+		int unlinked = cpcm->pcm_channel->unlinked;
+		cs46xx_dsp_destroy_pcm_channel (chip,cpcm->pcm_channel);
+
+		if ( (cpcm->pcm_channel = cs46xx_dsp_create_pcm_channel (chip, sample_rate, cpcm, 
+									 cpcm->hw_buf.addr,
+									 cpcm->pcm_channel_id)) == NULL) {
+			snd_printk(KERN_ERR "cs46xx: failed to re-create virtual PCM channel\n");
+			return -ENOMEM;
+		}
+
+		if (!unlinked) cs46xx_dsp_pcm_link (chip,cpcm->pcm_channel);
+		cpcm->pcm_channel->sample_rate = sample_rate;
+	}
+
+	return 0;
+}
+#endif
+
+
+static int snd_cs46xx_playback_hw_params(struct snd_pcm_substream *substream,
+					 struct snd_pcm_hw_params *hw_params)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_cs46xx_pcm *cpcm;
+	int err;
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+	struct snd_cs46xx *chip = snd_pcm_substream_chip(substream);
+	int sample_rate = params_rate(hw_params);
+	int period_size = params_period_bytes(hw_params);
+#endif
+	cpcm = runtime->private_data;
+
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+	snd_assert (sample_rate != 0, return -ENXIO);
+
+	mutex_lock(&chip->spos_mutex);
+
+	if (_cs46xx_adjust_sample_rate (chip,cpcm,sample_rate)) {
+		mutex_unlock(&chip->spos_mutex);
+		return -ENXIO;
+	}
+
+	snd_assert (cpcm->pcm_channel != NULL, return -ENXIO);
+	if (!cpcm->pcm_channel) {
+		mutex_unlock(&chip->spos_mutex);
+		return -ENXIO;
+	}
+
+
+	if (cs46xx_dsp_pcm_channel_set_period (chip,cpcm->pcm_channel,period_size)) {
+		 mutex_unlock(&chip->spos_mutex);
+		 return -EINVAL;
+	 }
+
+	snd_printdd ("period_size (%d), periods (%d) buffer_size(%d)\n",
+		     period_size, params_periods(hw_params),
+		     params_buffer_bytes(hw_params));
+#endif
+
+	if (params_periods(hw_params) == CS46XX_FRAGS) {
+		if (runtime->dma_area != cpcm->hw_buf.area)
+			snd_pcm_lib_free_pages(substream);
+		runtime->dma_area = cpcm->hw_buf.area;
+		runtime->dma_addr = cpcm->hw_buf.addr;
+		runtime->dma_bytes = cpcm->hw_buf.bytes;
+
+
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+		if (cpcm->pcm_channel_id == DSP_PCM_MAIN_CHANNEL) {
+			substream->ops = &snd_cs46xx_playback_ops;
+		} else if (cpcm->pcm_channel_id == DSP_PCM_REAR_CHANNEL) {
+			substream->ops = &snd_cs46xx_playback_rear_ops;
+		} else if (cpcm->pcm_channel_id == DSP_PCM_CENTER_LFE_CHANNEL) {
+			substream->ops = &snd_cs46xx_playback_clfe_ops;
+		} else if (cpcm->pcm_channel_id == DSP_IEC958_CHANNEL) {
+			substream->ops = &snd_cs46xx_playback_iec958_ops;
+		} else {
+			snd_assert(0, return -ENXIO);
+		}
+#else
+		substream->ops = &snd_cs46xx_playback_ops;
+#endif
+
+	} else {
+		if (runtime->dma_area == cpcm->hw_buf.area) {
+			runtime->dma_area = NULL;
+			runtime->dma_addr = 0;
+			runtime->dma_bytes = 0;
+		}
+		if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) {
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+			mutex_unlock(&chip->spos_mutex);
+#endif
+			return err;
+		}
+
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+		if (cpcm->pcm_channel_id == DSP_PCM_MAIN_CHANNEL) {
+			substream->ops = &snd_cs46xx_playback_indirect_ops;
+		} else if (cpcm->pcm_channel_id == DSP_PCM_REAR_CHANNEL) {
+			substream->ops = &snd_cs46xx_playback_indirect_rear_ops;
+		} else if (cpcm->pcm_channel_id == DSP_PCM_CENTER_LFE_CHANNEL) {
+			substream->ops = &snd_cs46xx_playback_indirect_clfe_ops;
+		} else if (cpcm->pcm_channel_id == DSP_IEC958_CHANNEL) {
+			substream->ops = &snd_cs46xx_playback_indirect_iec958_ops;
+		} else {
+			snd_assert(0, return -ENXIO);
+		}
+#else
+		substream->ops = &snd_cs46xx_playback_indirect_ops;
+#endif
+
+	}
+
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+	mutex_unlock(&chip->spos_mutex);
+#endif
+
+	return 0;
+}
+
+static int snd_cs46xx_playback_hw_free(struct snd_pcm_substream *substream)
+{
+	/*struct snd_cs46xx *chip = snd_pcm_substream_chip(substream);*/
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_cs46xx_pcm *cpcm;
+
+	cpcm = runtime->private_data;
+
+	/* if play_back open fails, then this function
+	   is called and cpcm can actually be NULL here */
+	if (!cpcm) return -ENXIO;
+
+	if (runtime->dma_area != cpcm->hw_buf.area)
+		snd_pcm_lib_free_pages(substream);
+    
+	runtime->dma_area = NULL;
+	runtime->dma_addr = 0;
+	runtime->dma_bytes = 0;
+
+	return 0;
+}
+
+static int snd_cs46xx_playback_prepare(struct snd_pcm_substream *substream)
+{
+	unsigned int tmp;
+	unsigned int pfie;
+	struct snd_cs46xx *chip = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_cs46xx_pcm *cpcm;
+
+	cpcm = runtime->private_data;
+
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+    snd_assert (cpcm->pcm_channel != NULL, return -ENXIO);
+
+	pfie = snd_cs46xx_peek(chip, (cpcm->pcm_channel->pcm_reader_scb->address + 1) << 2 );
+	pfie &= ~0x0000f03f;
+#else
+	/* old dsp */
+	pfie = snd_cs46xx_peek(chip, BA1_PFIE);
+ 	pfie &= ~0x0000f03f;
+#endif
+
+	cpcm->shift = 2;
+	/* if to convert from stereo to mono */
+	if (runtime->channels == 1) {
+		cpcm->shift--;
+		pfie |= 0x00002000;
+	}
+	/* if to convert from 8 bit to 16 bit */
+	if (snd_pcm_format_width(runtime->format) == 8) {
+		cpcm->shift--;
+		pfie |= 0x00001000;
+	}
+	/* if to convert to unsigned */
+	if (snd_pcm_format_unsigned(runtime->format))
+		pfie |= 0x00008000;
+
+	/* Never convert byte order when sample stream is 8 bit */
+	if (snd_pcm_format_width(runtime->format) != 8) {
+		/* convert from big endian to little endian */
+		if (snd_pcm_format_big_endian(runtime->format))
+			pfie |= 0x00004000;
+	}
+	
+	memset(&cpcm->pcm_rec, 0, sizeof(cpcm->pcm_rec));
+	cpcm->pcm_rec.sw_buffer_size = snd_pcm_lib_buffer_bytes(substream);
+	cpcm->pcm_rec.hw_buffer_size = runtime->period_size * CS46XX_FRAGS << cpcm->shift;
+
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+
+	tmp = snd_cs46xx_peek(chip, (cpcm->pcm_channel->pcm_reader_scb->address) << 2);
+	tmp &= ~0x000003ff;
+	tmp |= (4 << cpcm->shift) - 1;
+	/* playback transaction count register */
+	snd_cs46xx_poke(chip, (cpcm->pcm_channel->pcm_reader_scb->address) << 2, tmp);
+
+	/* playback format && interrupt enable */
+	snd_cs46xx_poke(chip, (cpcm->pcm_channel->pcm_reader_scb->address + 1) << 2, pfie | cpcm->pcm_channel->pcm_slot);
+#else
+	snd_cs46xx_poke(chip, BA1_PBA, cpcm->hw_buf.addr);
+	tmp = snd_cs46xx_peek(chip, BA1_PDTC);
+	tmp &= ~0x000003ff;
+	tmp |= (4 << cpcm->shift) - 1;
+	snd_cs46xx_poke(chip, BA1_PDTC, tmp);
+	snd_cs46xx_poke(chip, BA1_PFIE, pfie);
+	snd_cs46xx_set_play_sample_rate(chip, runtime->rate);
+#endif
+
+	return 0;
+}
+
+static int snd_cs46xx_capture_hw_params(struct snd_pcm_substream *substream,
+					struct snd_pcm_hw_params *hw_params)
+{
+	struct snd_cs46xx *chip = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	int err;
+
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+	cs46xx_dsp_pcm_ostream_set_period (chip, params_period_bytes(hw_params));
+#endif
+	if (runtime->periods == CS46XX_FRAGS) {
+		if (runtime->dma_area != chip->capt.hw_buf.area)
+			snd_pcm_lib_free_pages(substream);
+		runtime->dma_area = chip->capt.hw_buf.area;
+		runtime->dma_addr = chip->capt.hw_buf.addr;
+		runtime->dma_bytes = chip->capt.hw_buf.bytes;
+		substream->ops = &snd_cs46xx_capture_ops;
+	} else {
+		if (runtime->dma_area == chip->capt.hw_buf.area) {
+			runtime->dma_area = NULL;
+			runtime->dma_addr = 0;
+			runtime->dma_bytes = 0;
+		}
+		if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
+			return err;
+		substream->ops = &snd_cs46xx_capture_indirect_ops;
+	}
+
+	return 0;
+}
+
+static int snd_cs46xx_capture_hw_free(struct snd_pcm_substream *substream)
+{
+	struct snd_cs46xx *chip = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+
+	if (runtime->dma_area != chip->capt.hw_buf.area)
+		snd_pcm_lib_free_pages(substream);
+	runtime->dma_area = NULL;
+	runtime->dma_addr = 0;
+	runtime->dma_bytes = 0;
+
+	return 0;
+}
+
+static int snd_cs46xx_capture_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_cs46xx *chip = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+
+	snd_cs46xx_poke(chip, BA1_CBA, chip->capt.hw_buf.addr);
+	chip->capt.shift = 2;
+	memset(&chip->capt.pcm_rec, 0, sizeof(chip->capt.pcm_rec));
+	chip->capt.pcm_rec.sw_buffer_size = snd_pcm_lib_buffer_bytes(substream);
+	chip->capt.pcm_rec.hw_buffer_size = runtime->period_size * CS46XX_FRAGS << 2;
+	snd_cs46xx_set_capture_sample_rate(chip, runtime->rate);
+
+	return 0;
+}
+
+static irqreturn_t snd_cs46xx_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct snd_cs46xx *chip = dev_id;
+	u32 status1;
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
+	u32 status2;
+	int i;
+	struct snd_cs46xx_pcm *cpcm = NULL;
+#endif
+
+	/*
+	 *  Read the Interrupt Status Register to clear the interrupt
+	 */
+	status1 = snd_cs46xx_peekBA0(chip, BA0_HISR);
+	if ((status1 & 0x7fffffff) == 0) {
+		snd_cs46xx_pokeBA0(chip, BA0_HICR, HICR_CHGM | HICR_IEV);
+		return IRQ_NONE;
+	}
+
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+	status2 = snd_cs46xx_peekBA0(chip, BA0_HSR0);
+
+	for (i = 0; i < DSP_MAX_PCM_CHANNELS; ++i) {
+		if (i <= 15) {
+			if ( status1 & (1 << i) ) {
+				if (i == CS46XX_DSP_CAPTURE_CHANNEL) {
+					if (chip->capt.substream)
+						snd_pcm_period_elapsed(chip->capt.substream);
+				} else {
+					if (ins->pcm_channels[i].active &&
+					    ins->pcm_channels[i].private_data &&
+					    !ins->pcm_channels[i].unlinked) {
+						cpcm = ins->pcm_channels[i].private_data;
+						snd_pcm_period_elapsed(cpcm->substream);
+					}
+				}
+			}
+		} else {
+			if ( status2 & (1 << (i - 16))) {
+				if (ins->pcm_channels[i].active && 
+				    ins->pcm_channels[i].private_data &&
+				    !ins->pcm_channels[i].unlinked) {
+					cpcm = ins->pcm_channels[i].private_data;
+					snd_pcm_period_elapsed(cpcm->substream);
+				}
+			}
+		}
+	}
+
+#else
+	/* old dsp */
+	if ((status1 & HISR_VC0) && chip->playback_pcm) {
+		if (chip->playback_pcm->substream)
+			snd_pcm_period_elapsed(chip->playback_pcm->substream);
+	}
+	if ((status1 & HISR_VC1) && chip->pcm) {
+		if (chip->capt.substream)
+			snd_pcm_period_elapsed(chip->capt.substream);
+	}
+#endif
+
+	if ((status1 & HISR_MIDI) && chip->rmidi) {
+		unsigned char c;
+		
+		spin_lock(&chip->reg_lock);
+		while ((snd_cs46xx_peekBA0(chip, BA0_MIDSR) & MIDSR_RBE) == 0) {
+			c = snd_cs46xx_peekBA0(chip, BA0_MIDRP);
+			if ((chip->midcr & MIDCR_RIE) == 0)
+				continue;
+			snd_rawmidi_receive(chip->midi_input, &c, 1);
+		}
+		while ((snd_cs46xx_peekBA0(chip, BA0_MIDSR) & MIDSR_TBF) == 0) {
+			if ((chip->midcr & MIDCR_TIE) == 0)
+				break;
+			if (snd_rawmidi_transmit(chip->midi_output, &c, 1) != 1) {
+				chip->midcr &= ~MIDCR_TIE;
+				snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr);
+				break;
+			}
+			snd_cs46xx_pokeBA0(chip, BA0_MIDWP, c);
+		}
+		spin_unlock(&chip->reg_lock);
+	}
+	/*
+	 *  EOI to the PCI part....reenables interrupts
+	 */
+	snd_cs46xx_pokeBA0(chip, BA0_HICR, HICR_CHGM | HICR_IEV);
+
+	return IRQ_HANDLED;
+}
+
+static struct snd_pcm_hardware snd_cs46xx_playback =
+{
+	.info =			(SNDRV_PCM_INFO_MMAP |
+				 SNDRV_PCM_INFO_INTERLEAVED | 
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER /*|*/
+				 /*SNDRV_PCM_INFO_RESUME*/),
+	.formats =		(SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 |
+				 SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |
+				 SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_U16_BE),
+	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	.rate_min =		5500,
+	.rate_max =		48000,
+	.channels_min =		1,
+	.channels_max =		2,
+	.buffer_bytes_max =	(256 * 1024),
+	.period_bytes_min =	CS46XX_MIN_PERIOD_SIZE,
+	.period_bytes_max =	CS46XX_MAX_PERIOD_SIZE,
+	.periods_min =		CS46XX_FRAGS,
+	.periods_max =		1024,
+	.fifo_size =		0,
+};
+
+static struct snd_pcm_hardware snd_cs46xx_capture =
+{
+	.info =			(SNDRV_PCM_INFO_MMAP |
+				 SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER /*|*/
+				 /*SNDRV_PCM_INFO_RESUME*/),
+	.formats =		SNDRV_PCM_FMTBIT_S16_LE,
+	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	.rate_min =		5500,
+	.rate_max =		48000,
+	.channels_min =		2,
+	.channels_max =		2,
+	.buffer_bytes_max =	(256 * 1024),
+	.period_bytes_min =	CS46XX_MIN_PERIOD_SIZE,
+	.period_bytes_max =	CS46XX_MAX_PERIOD_SIZE,
+	.periods_min =		CS46XX_FRAGS,
+	.periods_max =		1024,
+	.fifo_size =		0,
+};
+
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+
+static unsigned int period_sizes[] = { 32, 64, 128, 256, 512, 1024, 2048 };
+
+static struct snd_pcm_hw_constraint_list hw_constraints_period_sizes = {
+	.count = ARRAY_SIZE(period_sizes),
+	.list = period_sizes,
+	.mask = 0
+};
+
+#endif
+
+static void snd_cs46xx_pcm_free_substream(struct snd_pcm_runtime *runtime)
+{
+	kfree(runtime->private_data);
+}
+
+static int _cs46xx_playback_open_channel (struct snd_pcm_substream *substream,int pcm_channel_id)
+{
+	struct snd_cs46xx *chip = snd_pcm_substream_chip(substream);
+	struct snd_cs46xx_pcm * cpcm;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+
+	cpcm = kzalloc(sizeof(*cpcm), GFP_KERNEL);
+	if (cpcm == NULL)
+		return -ENOMEM;
+	if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci),
+				PAGE_SIZE, &cpcm->hw_buf) < 0) {
+		kfree(cpcm);
+		return -ENOMEM;
+	}
+
+	runtime->hw = snd_cs46xx_playback;
+	runtime->private_data = cpcm;
+	runtime->private_free = snd_cs46xx_pcm_free_substream;
+
+	cpcm->substream = substream;
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+	mutex_lock(&chip->spos_mutex);
+	cpcm->pcm_channel = NULL; 
+	cpcm->pcm_channel_id = pcm_channel_id;
+
+
+	snd_pcm_hw_constraint_list(runtime, 0,
+				   SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 
+				   &hw_constraints_period_sizes);
+
+	mutex_unlock(&chip->spos_mutex);
+#else
+	chip->playback_pcm = cpcm; /* HACK */
+#endif
+
+	if (chip->accept_valid)
+		substream->runtime->hw.info |= SNDRV_PCM_INFO_MMAP_VALID;
+	chip->active_ctrl(chip, 1);
+
+	return 0;
+}
+
+static int snd_cs46xx_playback_open(struct snd_pcm_substream *substream)
+{
+	snd_printdd("open front channel\n");
+	return _cs46xx_playback_open_channel(substream,DSP_PCM_MAIN_CHANNEL);
+}
+
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+static int snd_cs46xx_playback_open_rear(struct snd_pcm_substream *substream)
+{
+	snd_printdd("open rear channel\n");
+
+	return _cs46xx_playback_open_channel(substream,DSP_PCM_REAR_CHANNEL);
+}
+
+static int snd_cs46xx_playback_open_clfe(struct snd_pcm_substream *substream)
+{
+	snd_printdd("open center - LFE channel\n");
+
+	return _cs46xx_playback_open_channel(substream,DSP_PCM_CENTER_LFE_CHANNEL);
+}
+
+static int snd_cs46xx_playback_open_iec958(struct snd_pcm_substream *substream)
+{
+	struct snd_cs46xx *chip = snd_pcm_substream_chip(substream);
+
+	snd_printdd("open raw iec958 channel\n");
+
+	mutex_lock(&chip->spos_mutex);
+	cs46xx_iec958_pre_open (chip);
+	mutex_unlock(&chip->spos_mutex);
+
+	return _cs46xx_playback_open_channel(substream,DSP_IEC958_CHANNEL);
+}
+
+static int snd_cs46xx_playback_close(struct snd_pcm_substream *substream);
+
+static int snd_cs46xx_playback_close_iec958(struct snd_pcm_substream *substream)
+{
+	int err;
+	struct snd_cs46xx *chip = snd_pcm_substream_chip(substream);
+  
+	snd_printdd("close raw iec958 channel\n");
+
+	err = snd_cs46xx_playback_close(substream);
+
+	mutex_lock(&chip->spos_mutex);
+	cs46xx_iec958_post_close (chip);
+	mutex_unlock(&chip->spos_mutex);
+
+	return err;
+}
+#endif
+
+static int snd_cs46xx_capture_open(struct snd_pcm_substream *substream)
+{
+	struct snd_cs46xx *chip = snd_pcm_substream_chip(substream);
+
+	if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci),
+				PAGE_SIZE, &chip->capt.hw_buf) < 0)
+		return -ENOMEM;
+	chip->capt.substream = substream;
+	substream->runtime->hw = snd_cs46xx_capture;
+
+	if (chip->accept_valid)
+		substream->runtime->hw.info |= SNDRV_PCM_INFO_MMAP_VALID;
+
+	chip->active_ctrl(chip, 1);
+
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+	snd_pcm_hw_constraint_list(substream->runtime, 0,
+				   SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 
+				   &hw_constraints_period_sizes);
+#endif
+	return 0;
+}
+
+static int snd_cs46xx_playback_close(struct snd_pcm_substream *substream)
+{
+	struct snd_cs46xx *chip = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_cs46xx_pcm * cpcm;
+
+	cpcm = runtime->private_data;
+
+	/* when playback_open fails, then cpcm can be NULL */
+	if (!cpcm) return -ENXIO;
+
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+	mutex_lock(&chip->spos_mutex);
+	if (cpcm->pcm_channel) {
+		cs46xx_dsp_destroy_pcm_channel(chip,cpcm->pcm_channel);
+		cpcm->pcm_channel = NULL;
+	}
+	mutex_unlock(&chip->spos_mutex);
+#else
+	chip->playback_pcm = NULL;
+#endif
+
+	cpcm->substream = NULL;
+	snd_dma_free_pages(&cpcm->hw_buf);
+	chip->active_ctrl(chip, -1);
+
+	return 0;
+}
+
+static int snd_cs46xx_capture_close(struct snd_pcm_substream *substream)
+{
+	struct snd_cs46xx *chip = snd_pcm_substream_chip(substream);
+
+	chip->capt.substream = NULL;
+	snd_dma_free_pages(&chip->capt.hw_buf);
+	chip->active_ctrl(chip, -1);
+
+	return 0;
+}
+
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+static struct snd_pcm_ops snd_cs46xx_playback_rear_ops = {
+	.open =			snd_cs46xx_playback_open_rear,
+	.close =		snd_cs46xx_playback_close,
+	.ioctl =		snd_pcm_lib_ioctl,
+	.hw_params =		snd_cs46xx_playback_hw_params,
+	.hw_free =		snd_cs46xx_playback_hw_free,
+	.prepare =		snd_cs46xx_playback_prepare,
+	.trigger =		snd_cs46xx_playback_trigger,
+	.pointer =		snd_cs46xx_playback_direct_pointer,
+};
+
+static struct snd_pcm_ops snd_cs46xx_playback_indirect_rear_ops = {
+	.open =			snd_cs46xx_playback_open_rear,
+	.close =		snd_cs46xx_playback_close,
+	.ioctl =		snd_pcm_lib_ioctl,
+	.hw_params =		snd_cs46xx_playback_hw_params,
+	.hw_free =		snd_cs46xx_playback_hw_free,
+	.prepare =		snd_cs46xx_playback_prepare,
+	.trigger =		snd_cs46xx_playback_trigger,
+	.pointer =		snd_cs46xx_playback_indirect_pointer,
+	.ack =			snd_cs46xx_playback_transfer,
+};
+
+static struct snd_pcm_ops snd_cs46xx_playback_clfe_ops = {
+	.open =			snd_cs46xx_playback_open_clfe,
+	.close =		snd_cs46xx_playback_close,
+	.ioctl =		snd_pcm_lib_ioctl,
+	.hw_params =		snd_cs46xx_playback_hw_params,
+	.hw_free =		snd_cs46xx_playback_hw_free,
+	.prepare =		snd_cs46xx_playback_prepare,
+	.trigger =		snd_cs46xx_playback_trigger,
+	.pointer =		snd_cs46xx_playback_direct_pointer,
+};
+
+static struct snd_pcm_ops snd_cs46xx_playback_indirect_clfe_ops = {
+	.open =			snd_cs46xx_playback_open_clfe,
+	.close =		snd_cs46xx_playback_close,
+	.ioctl =		snd_pcm_lib_ioctl,
+	.hw_params =		snd_cs46xx_playback_hw_params,
+	.hw_free =		snd_cs46xx_playback_hw_free,
+	.prepare =		snd_cs46xx_playback_prepare,
+	.trigger =		snd_cs46xx_playback_trigger,
+	.pointer =		snd_cs46xx_playback_indirect_pointer,
+	.ack =			snd_cs46xx_playback_transfer,
+};
+
+static struct snd_pcm_ops snd_cs46xx_playback_iec958_ops = {
+	.open =			snd_cs46xx_playback_open_iec958,
+	.close =		snd_cs46xx_playback_close_iec958,
+	.ioctl =		snd_pcm_lib_ioctl,
+	.hw_params =		snd_cs46xx_playback_hw_params,
+	.hw_free =		snd_cs46xx_playback_hw_free,
+	.prepare =		snd_cs46xx_playback_prepare,
+	.trigger =		snd_cs46xx_playback_trigger,
+	.pointer =		snd_cs46xx_playback_direct_pointer,
+};
+
+static struct snd_pcm_ops snd_cs46xx_playback_indirect_iec958_ops = {
+	.open =			snd_cs46xx_playback_open_iec958,
+	.close =		snd_cs46xx_playback_close_iec958,
+	.ioctl =		snd_pcm_lib_ioctl,
+	.hw_params =		snd_cs46xx_playback_hw_params,
+	.hw_free =		snd_cs46xx_playback_hw_free,
+	.prepare =		snd_cs46xx_playback_prepare,
+	.trigger =		snd_cs46xx_playback_trigger,
+	.pointer =		snd_cs46xx_playback_indirect_pointer,
+	.ack =			snd_cs46xx_playback_transfer,
+};
+
+#endif
+
+static struct snd_pcm_ops snd_cs46xx_playback_ops = {
+	.open =			snd_cs46xx_playback_open,
+	.close =		snd_cs46xx_playback_close,
+	.ioctl =		snd_pcm_lib_ioctl,
+	.hw_params =		snd_cs46xx_playback_hw_params,
+	.hw_free =		snd_cs46xx_playback_hw_free,
+	.prepare =		snd_cs46xx_playback_prepare,
+	.trigger =		snd_cs46xx_playback_trigger,
+	.pointer =		snd_cs46xx_playback_direct_pointer,
+};
+
+static struct snd_pcm_ops snd_cs46xx_playback_indirect_ops = {
+	.open =			snd_cs46xx_playback_open,
+	.close =		snd_cs46xx_playback_close,
+	.ioctl =		snd_pcm_lib_ioctl,
+	.hw_params =		snd_cs46xx_playback_hw_params,
+	.hw_free =		snd_cs46xx_playback_hw_free,
+	.prepare =		snd_cs46xx_playback_prepare,
+	.trigger =		snd_cs46xx_playback_trigger,
+	.pointer =		snd_cs46xx_playback_indirect_pointer,
+	.ack =			snd_cs46xx_playback_transfer,
+};
+
+static struct snd_pcm_ops snd_cs46xx_capture_ops = {
+	.open =			snd_cs46xx_capture_open,
+	.close =		snd_cs46xx_capture_close,
+	.ioctl =		snd_pcm_lib_ioctl,
+	.hw_params =		snd_cs46xx_capture_hw_params,
+	.hw_free =		snd_cs46xx_capture_hw_free,
+	.prepare =		snd_cs46xx_capture_prepare,
+	.trigger =		snd_cs46xx_capture_trigger,
+	.pointer =		snd_cs46xx_capture_direct_pointer,
+};
+
+static struct snd_pcm_ops snd_cs46xx_capture_indirect_ops = {
+	.open =			snd_cs46xx_capture_open,
+	.close =		snd_cs46xx_capture_close,
+	.ioctl =		snd_pcm_lib_ioctl,
+	.hw_params =		snd_cs46xx_capture_hw_params,
+	.hw_free =		snd_cs46xx_capture_hw_free,
+	.prepare =		snd_cs46xx_capture_prepare,
+	.trigger =		snd_cs46xx_capture_trigger,
+	.pointer =		snd_cs46xx_capture_indirect_pointer,
+	.ack =			snd_cs46xx_capture_transfer,
+};
+
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+#define MAX_PLAYBACK_CHANNELS	(DSP_MAX_PCM_CHANNELS - 1)
+#else
+#define MAX_PLAYBACK_CHANNELS	1
+#endif
+
+int __devinit snd_cs46xx_pcm(struct snd_cs46xx *chip, int device, struct snd_pcm ** rpcm)
+{
+	struct snd_pcm *pcm;
+	int err;
+
+	if (rpcm)
+		*rpcm = NULL;
+	if ((err = snd_pcm_new(chip->card, "CS46xx", device, MAX_PLAYBACK_CHANNELS, 1, &pcm)) < 0)
+		return err;
+
+	pcm->private_data = chip;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_cs46xx_playback_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_cs46xx_capture_ops);
+
+	/* global setup */
+	pcm->info_flags = 0;
+	strcpy(pcm->name, "CS46xx");
+	chip->pcm = pcm;
+
+	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+					      snd_dma_pci_data(chip->pci), 64*1024, 256*1024);
+
+	if (rpcm)
+		*rpcm = pcm;
+
+	return 0;
+}
+
+
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+int __devinit snd_cs46xx_pcm_rear(struct snd_cs46xx *chip, int device, struct snd_pcm ** rpcm)
+{
+	struct snd_pcm *pcm;
+	int err;
+
+	if (rpcm)
+		*rpcm = NULL;
+
+	if ((err = snd_pcm_new(chip->card, "CS46xx - Rear", device, MAX_PLAYBACK_CHANNELS, 0, &pcm)) < 0)
+		return err;
+
+	pcm->private_data = chip;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_cs46xx_playback_rear_ops);
+
+	/* global setup */
+	pcm->info_flags = 0;
+	strcpy(pcm->name, "CS46xx - Rear");
+	chip->pcm_rear = pcm;
+
+	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+					      snd_dma_pci_data(chip->pci), 64*1024, 256*1024);
+
+	if (rpcm)
+		*rpcm = pcm;
+
+	return 0;
+}
+
+int __devinit snd_cs46xx_pcm_center_lfe(struct snd_cs46xx *chip, int device, struct snd_pcm ** rpcm)
+{
+	struct snd_pcm *pcm;
+	int err;
+
+	if (rpcm)
+		*rpcm = NULL;
+
+	if ((err = snd_pcm_new(chip->card, "CS46xx - Center LFE", device, MAX_PLAYBACK_CHANNELS, 0, &pcm)) < 0)
+		return err;
+
+	pcm->private_data = chip;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_cs46xx_playback_clfe_ops);
+
+	/* global setup */
+	pcm->info_flags = 0;
+	strcpy(pcm->name, "CS46xx - Center LFE");
+	chip->pcm_center_lfe = pcm;
+
+	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+					      snd_dma_pci_data(chip->pci), 64*1024, 256*1024);
+
+	if (rpcm)
+		*rpcm = pcm;
+
+	return 0;
+}
+
+int __devinit snd_cs46xx_pcm_iec958(struct snd_cs46xx *chip, int device, struct snd_pcm ** rpcm)
+{
+	struct snd_pcm *pcm;
+	int err;
+
+	if (rpcm)
+		*rpcm = NULL;
+
+	if ((err = snd_pcm_new(chip->card, "CS46xx - IEC958", device, 1, 0, &pcm)) < 0)
+		return err;
+
+	pcm->private_data = chip;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_cs46xx_playback_iec958_ops);
+
+	/* global setup */
+	pcm->info_flags = 0;
+	strcpy(pcm->name, "CS46xx - IEC958");
+	chip->pcm_rear = pcm;
+
+	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+					      snd_dma_pci_data(chip->pci), 64*1024, 256*1024);
+
+	if (rpcm)
+		*rpcm = pcm;
+
+	return 0;
+}
+#endif
+
+/*
+ *  Mixer routines
+ */
+static void snd_cs46xx_mixer_free_ac97_bus(struct snd_ac97_bus *bus)
+{
+	struct snd_cs46xx *chip = bus->private_data;
+
+	chip->ac97_bus = NULL;
+}
+
+static void snd_cs46xx_mixer_free_ac97(struct snd_ac97 *ac97)
+{
+	struct snd_cs46xx *chip = ac97->private_data;
+
+	snd_assert ((ac97 == chip->ac97[CS46XX_PRIMARY_CODEC_INDEX]) ||
+		    (ac97 == chip->ac97[CS46XX_SECONDARY_CODEC_INDEX]),
+		    return);
+
+	if (ac97 == chip->ac97[CS46XX_PRIMARY_CODEC_INDEX]) {
+		chip->ac97[CS46XX_PRIMARY_CODEC_INDEX] = NULL;
+		chip->eapd_switch = NULL;
+	}
+	else
+		chip->ac97[CS46XX_SECONDARY_CODEC_INDEX] = NULL;
+}
+
+static int snd_cs46xx_vol_info(struct snd_kcontrol *kcontrol, 
+			       struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 0x7fff;
+	return 0;
+}
+
+static int snd_cs46xx_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_cs46xx *chip = snd_kcontrol_chip(kcontrol);
+	int reg = kcontrol->private_value;
+	unsigned int val = snd_cs46xx_peek(chip, reg);
+	ucontrol->value.integer.value[0] = 0xffff - (val >> 16);
+	ucontrol->value.integer.value[1] = 0xffff - (val & 0xffff);
+	return 0;
+}
+
+static int snd_cs46xx_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_cs46xx *chip = snd_kcontrol_chip(kcontrol);
+	int reg = kcontrol->private_value;
+	unsigned int val = ((0xffff - ucontrol->value.integer.value[0]) << 16 | 
+			    (0xffff - ucontrol->value.integer.value[1]));
+	unsigned int old = snd_cs46xx_peek(chip, reg);
+	int change = (old != val);
+
+	if (change) {
+		snd_cs46xx_poke(chip, reg, val);
+	}
+
+	return change;
+}
+
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+
+static int snd_cs46xx_vol_dac_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_cs46xx *chip = snd_kcontrol_chip(kcontrol);
+
+	ucontrol->value.integer.value[0] = chip->dsp_spos_instance->dac_volume_left;
+	ucontrol->value.integer.value[1] = chip->dsp_spos_instance->dac_volume_right;
+
+	return 0;
+}
+
+static int snd_cs46xx_vol_dac_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_cs46xx *chip = snd_kcontrol_chip(kcontrol);
+	int change = 0;
+
+	if (chip->dsp_spos_instance->dac_volume_right != ucontrol->value.integer.value[0] ||
+	    chip->dsp_spos_instance->dac_volume_left != ucontrol->value.integer.value[1]) {
+		cs46xx_dsp_set_dac_volume(chip,
+					  ucontrol->value.integer.value[0],
+					  ucontrol->value.integer.value[1]);
+		change = 1;
+	}
+
+	return change;
+}
+
+#if 0
+static int snd_cs46xx_vol_iec958_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_cs46xx *chip = snd_kcontrol_chip(kcontrol);
+
+	ucontrol->value.integer.value[0] = chip->dsp_spos_instance->spdif_input_volume_left;
+	ucontrol->value.integer.value[1] = chip->dsp_spos_instance->spdif_input_volume_right;
+	return 0;
+}
+
+static int snd_cs46xx_vol_iec958_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_cs46xx *chip = snd_kcontrol_chip(kcontrol);
+	int change = 0;
+
+	if (chip->dsp_spos_instance->spdif_input_volume_left  != ucontrol->value.integer.value[0] ||
+	    chip->dsp_spos_instance->spdif_input_volume_right!= ucontrol->value.integer.value[1]) {
+		cs46xx_dsp_set_iec958_volume (chip,
+					      ucontrol->value.integer.value[0],
+					      ucontrol->value.integer.value[1]);
+		change = 1;
+	}
+
+	return change;
+}
+#endif
+
+static int snd_mixer_boolean_info(struct snd_kcontrol *kcontrol, 
+				  struct snd_ctl_elem_info *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_cs46xx_iec958_get(struct snd_kcontrol *kcontrol, 
+                                 struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_cs46xx *chip = snd_kcontrol_chip(kcontrol);
+	int reg = kcontrol->private_value;
+
+	if (reg == CS46XX_MIXER_SPDIF_OUTPUT_ELEMENT)
+		ucontrol->value.integer.value[0] = (chip->dsp_spos_instance->spdif_status_out & DSP_SPDIF_STATUS_OUTPUT_ENABLED);
+	else
+		ucontrol->value.integer.value[0] = chip->dsp_spos_instance->spdif_status_in;
+
+	return 0;
+}
+
+static int snd_cs46xx_iec958_put(struct snd_kcontrol *kcontrol, 
+                                  struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_cs46xx *chip = snd_kcontrol_chip(kcontrol);
+	int change, res;
+
+	switch (kcontrol->private_value) {
+	case CS46XX_MIXER_SPDIF_OUTPUT_ELEMENT:
+		mutex_lock(&chip->spos_mutex);
+		change = (chip->dsp_spos_instance->spdif_status_out & DSP_SPDIF_STATUS_OUTPUT_ENABLED);
+		if (ucontrol->value.integer.value[0] && !change) 
+			cs46xx_dsp_enable_spdif_out(chip);
+		else if (change && !ucontrol->value.integer.value[0])
+			cs46xx_dsp_disable_spdif_out(chip);
+
+		res = (change != (chip->dsp_spos_instance->spdif_status_out & DSP_SPDIF_STATUS_OUTPUT_ENABLED));
+		mutex_unlock(&chip->spos_mutex);
+		break;
+	case CS46XX_MIXER_SPDIF_INPUT_ELEMENT:
+		change = chip->dsp_spos_instance->spdif_status_in;
+		if (ucontrol->value.integer.value[0] && !change) {
+			cs46xx_dsp_enable_spdif_in(chip);
+			/* restore volume */
+		}
+		else if (change && !ucontrol->value.integer.value[0])
+			cs46xx_dsp_disable_spdif_in(chip);
+		
+		res = (change != chip->dsp_spos_instance->spdif_status_in);
+		break;
+	default:
+		res = -EINVAL;
+		snd_assert(0, (void)0);
+	}
+
+	return res;
+}
+
+static int snd_cs46xx_adc_capture_get(struct snd_kcontrol *kcontrol, 
+                                      struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_cs46xx *chip = snd_kcontrol_chip(kcontrol);
+	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
+
+	if (ins->adc_input != NULL) 
+		ucontrol->value.integer.value[0] = 1;
+	else 
+		ucontrol->value.integer.value[0] = 0;
+	
+	return 0;
+}
+
+static int snd_cs46xx_adc_capture_put(struct snd_kcontrol *kcontrol, 
+                                      struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_cs46xx *chip = snd_kcontrol_chip(kcontrol);
+	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
+	int change = 0;
+
+	if (ucontrol->value.integer.value[0] && !ins->adc_input) {
+		cs46xx_dsp_enable_adc_capture(chip);
+		change = 1;
+	} else  if (!ucontrol->value.integer.value[0] && ins->adc_input) {
+		cs46xx_dsp_disable_adc_capture(chip);
+		change = 1;
+	}
+	return change;
+}
+
+static int snd_cs46xx_pcm_capture_get(struct snd_kcontrol *kcontrol, 
+                                      struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_cs46xx *chip = snd_kcontrol_chip(kcontrol);
+	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
+
+	if (ins->pcm_input != NULL) 
+		ucontrol->value.integer.value[0] = 1;
+	else 
+		ucontrol->value.integer.value[0] = 0;
+
+	return 0;
+}
+
+
+static int snd_cs46xx_pcm_capture_put(struct snd_kcontrol *kcontrol, 
+                                      struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_cs46xx *chip = snd_kcontrol_chip(kcontrol);
+	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
+	int change = 0;
+
+	if (ucontrol->value.integer.value[0] && !ins->pcm_input) {
+		cs46xx_dsp_enable_pcm_capture(chip);
+		change = 1;
+	} else  if (!ucontrol->value.integer.value[0] && ins->pcm_input) {
+		cs46xx_dsp_disable_pcm_capture(chip);
+		change = 1;
+	}
+
+	return change;
+}
+
+static int snd_herc_spdif_select_get(struct snd_kcontrol *kcontrol, 
+                                     struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_cs46xx *chip = snd_kcontrol_chip(kcontrol);
+
+	int val1 = snd_cs46xx_peekBA0(chip, BA0_EGPIODR);
+
+	if (val1 & EGPIODR_GPOE0)
+		ucontrol->value.integer.value[0] = 1;
+	else
+		ucontrol->value.integer.value[0] = 0;
+
+	return 0;
+}
+
+/*
+ *	Game Theatre XP card - EGPIO[0] is used to select SPDIF input optical or coaxial.
+ */ 
+static int snd_herc_spdif_select_put(struct snd_kcontrol *kcontrol, 
+                                       struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_cs46xx *chip = snd_kcontrol_chip(kcontrol);
+	int val1 = snd_cs46xx_peekBA0(chip, BA0_EGPIODR);
+	int val2 = snd_cs46xx_peekBA0(chip, BA0_EGPIOPTR);
+
+	if (ucontrol->value.integer.value[0]) {
+		/* optical is default */
+		snd_cs46xx_pokeBA0(chip, BA0_EGPIODR, 
+				   EGPIODR_GPOE0 | val1);  /* enable EGPIO0 output */
+		snd_cs46xx_pokeBA0(chip, BA0_EGPIOPTR, 
+				   EGPIOPTR_GPPT0 | val2); /* open-drain on output */
+	} else {
+		/* coaxial */
+		snd_cs46xx_pokeBA0(chip, BA0_EGPIODR,  val1 & ~EGPIODR_GPOE0); /* disable */
+		snd_cs46xx_pokeBA0(chip, BA0_EGPIOPTR, val2 & ~EGPIOPTR_GPPT0); /* disable */
+	}
+
+	/* checking diff from the EGPIO direction register 
+	   should be enough */
+	return (val1 != (int)snd_cs46xx_peekBA0(chip, BA0_EGPIODR));
+}
+
+
+static int snd_cs46xx_spdif_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+	uinfo->count = 1;
+	return 0;
+}
+
+static int snd_cs46xx_spdif_default_get(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_cs46xx *chip = snd_kcontrol_chip(kcontrol);
+	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
+
+	mutex_lock(&chip->spos_mutex);
+	ucontrol->value.iec958.status[0] = _wrap_all_bits((ins->spdif_csuv_default >> 24) & 0xff);
+	ucontrol->value.iec958.status[1] = _wrap_all_bits((ins->spdif_csuv_default >> 16) & 0xff);
+	ucontrol->value.iec958.status[2] = 0;
+	ucontrol->value.iec958.status[3] = _wrap_all_bits((ins->spdif_csuv_default) & 0xff);
+	mutex_unlock(&chip->spos_mutex);
+
+	return 0;
+}
+
+static int snd_cs46xx_spdif_default_put(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_cs46xx * chip = snd_kcontrol_chip(kcontrol);
+	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
+	unsigned int val;
+	int change;
+
+	mutex_lock(&chip->spos_mutex);
+	val = ((unsigned int)_wrap_all_bits(ucontrol->value.iec958.status[0]) << 24) |
+		((unsigned int)_wrap_all_bits(ucontrol->value.iec958.status[2]) << 16) |
+		((unsigned int)_wrap_all_bits(ucontrol->value.iec958.status[3]))  |
+		/* left and right validity bit */
+		(1 << 13) | (1 << 12);
+
+
+	change = (unsigned int)ins->spdif_csuv_default != val;
+	ins->spdif_csuv_default = val;
+
+	if ( !(ins->spdif_status_out & DSP_SPDIF_STATUS_PLAYBACK_OPEN) )
+		cs46xx_poke_via_dsp (chip,SP_SPDOUT_CSUV,val);
+
+	mutex_unlock(&chip->spos_mutex);
+
+	return change;
+}
+
+static int snd_cs46xx_spdif_mask_get(struct snd_kcontrol *kcontrol,
+				     struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.iec958.status[0] = 0xff;
+	ucontrol->value.iec958.status[1] = 0xff;
+	ucontrol->value.iec958.status[2] = 0x00;
+	ucontrol->value.iec958.status[3] = 0xff;
+	return 0;
+}
+
+static int snd_cs46xx_spdif_stream_get(struct snd_kcontrol *kcontrol,
+                                         struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_cs46xx *chip = snd_kcontrol_chip(kcontrol);
+	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
+
+	mutex_lock(&chip->spos_mutex);
+	ucontrol->value.iec958.status[0] = _wrap_all_bits((ins->spdif_csuv_stream >> 24) & 0xff);
+	ucontrol->value.iec958.status[1] = _wrap_all_bits((ins->spdif_csuv_stream >> 16) & 0xff);
+	ucontrol->value.iec958.status[2] = 0;
+	ucontrol->value.iec958.status[3] = _wrap_all_bits((ins->spdif_csuv_stream) & 0xff);
+	mutex_unlock(&chip->spos_mutex);
+
+	return 0;
+}
+
+static int snd_cs46xx_spdif_stream_put(struct snd_kcontrol *kcontrol,
+                                        struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_cs46xx * chip = snd_kcontrol_chip(kcontrol);
+	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
+	unsigned int val;
+	int change;
+
+	mutex_lock(&chip->spos_mutex);
+	val = ((unsigned int)_wrap_all_bits(ucontrol->value.iec958.status[0]) << 24) |
+		((unsigned int)_wrap_all_bits(ucontrol->value.iec958.status[1]) << 16) |
+		((unsigned int)_wrap_all_bits(ucontrol->value.iec958.status[3])) |
+		/* left and right validity bit */
+		(1 << 13) | (1 << 12);
+
+
+	change = ins->spdif_csuv_stream != val;
+	ins->spdif_csuv_stream = val;
+
+	if ( ins->spdif_status_out & DSP_SPDIF_STATUS_PLAYBACK_OPEN )
+		cs46xx_poke_via_dsp (chip,SP_SPDOUT_CSUV,val);
+
+	mutex_unlock(&chip->spos_mutex);
+
+	return change;
+}
+
+#endif /* CONFIG_SND_CS46XX_NEW_DSP */
+
+
+#ifdef CONFIG_SND_CS46XX_DEBUG_GPIO
+static int snd_cs46xx_egpio_select_info(struct snd_kcontrol *kcontrol, 
+                                        struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 8;
+	return 0;
+}
+
+static int snd_cs46xx_egpio_select_get(struct snd_kcontrol *kcontrol, 
+                                       struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_cs46xx *chip = snd_kcontrol_chip(kcontrol);
+	ucontrol->value.integer.value[0] = chip->current_gpio;
+
+	return 0;
+}
+
+static int snd_cs46xx_egpio_select_put(struct snd_kcontrol *kcontrol, 
+                                       struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_cs46xx *chip = snd_kcontrol_chip(kcontrol);
+	int change = (chip->current_gpio != ucontrol->value.integer.value[0]);
+	chip->current_gpio = ucontrol->value.integer.value[0];
+
+	return change;
+}
+
+
+static int snd_cs46xx_egpio_get(struct snd_kcontrol *kcontrol, 
+                                       struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_cs46xx *chip = snd_kcontrol_chip(kcontrol);
+	int reg = kcontrol->private_value;
+
+	snd_printdd ("put: reg = %04x, gpio %02x\n",reg,chip->current_gpio);
+	ucontrol->value.integer.value[0] = 
+		(snd_cs46xx_peekBA0(chip, reg) & (1 << chip->current_gpio)) ? 1 : 0;
+  
+	return 0;
+}
+
+static int snd_cs46xx_egpio_put(struct snd_kcontrol *kcontrol, 
+                                       struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_cs46xx *chip = snd_kcontrol_chip(kcontrol);
+	int reg = kcontrol->private_value;
+	int val = snd_cs46xx_peekBA0(chip, reg);
+	int oldval = val;
+	snd_printdd ("put: reg = %04x, gpio %02x\n",reg,chip->current_gpio);
+
+	if (ucontrol->value.integer.value[0])
+		val |= (1 << chip->current_gpio);
+	else
+		val &= ~(1 << chip->current_gpio);
+
+	snd_cs46xx_pokeBA0(chip, reg,val);
+	snd_printdd ("put: val %08x oldval %08x\n",val,oldval);
+
+	return (oldval != val);
+}
+#endif /* CONFIG_SND_CS46XX_DEBUG_GPIO */
+
+static struct snd_kcontrol_new snd_cs46xx_controls[] __devinitdata = {
+{
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "DAC Volume",
+	.info = snd_cs46xx_vol_info,
+#ifndef CONFIG_SND_CS46XX_NEW_DSP
+	.get = snd_cs46xx_vol_get,
+	.put = snd_cs46xx_vol_put,
+	.private_value = BA1_PVOL,
+#else
+	.get = snd_cs46xx_vol_dac_get,
+	.put = snd_cs46xx_vol_dac_put,
+#endif
+},
+
+{
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "ADC Volume",
+	.info = snd_cs46xx_vol_info,
+	.get = snd_cs46xx_vol_get,
+	.put = snd_cs46xx_vol_put,
+#ifndef CONFIG_SND_CS46XX_NEW_DSP
+	.private_value = BA1_CVOL,
+#else
+	.private_value = (VARIDECIMATE_SCB_ADDR + 0xE) << 2,
+#endif
+},
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+{
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "ADC Capture Switch",
+	.info = snd_mixer_boolean_info,
+	.get = snd_cs46xx_adc_capture_get,
+	.put = snd_cs46xx_adc_capture_put
+},
+{
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "DAC Capture Switch",
+	.info = snd_mixer_boolean_info,
+	.get = snd_cs46xx_pcm_capture_get,
+	.put = snd_cs46xx_pcm_capture_put
+},
+{
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = SNDRV_CTL_NAME_IEC958("Output ",NONE,SWITCH),
+	.info = snd_mixer_boolean_info,
+	.get = snd_cs46xx_iec958_get,
+	.put = snd_cs46xx_iec958_put,
+	.private_value = CS46XX_MIXER_SPDIF_OUTPUT_ELEMENT,
+},
+{
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = SNDRV_CTL_NAME_IEC958("Input ",NONE,SWITCH),
+	.info = snd_mixer_boolean_info,
+	.get = snd_cs46xx_iec958_get,
+	.put = snd_cs46xx_iec958_put,
+	.private_value = CS46XX_MIXER_SPDIF_INPUT_ELEMENT,
+},
+#if 0
+/* Input IEC958 volume does not work for the moment. (Benny) */
+{
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = SNDRV_CTL_NAME_IEC958("Input ",NONE,VOLUME),
+	.info = snd_cs46xx_vol_info,
+	.get = snd_cs46xx_vol_iec958_get,
+	.put = snd_cs46xx_vol_iec958_put,
+	.private_value = (ASYNCRX_SCB_ADDR + 0xE) << 2,
+},
+#endif
+{
+	.iface = SNDRV_CTL_ELEM_IFACE_PCM,
+	.name =  SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
+	.info =	 snd_cs46xx_spdif_info,
+	.get =	 snd_cs46xx_spdif_default_get,
+	.put =   snd_cs46xx_spdif_default_put,
+},
+{
+	.iface = SNDRV_CTL_ELEM_IFACE_PCM,
+	.name =	 SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK),
+	.info =	 snd_cs46xx_spdif_info,
+        .get =	 snd_cs46xx_spdif_mask_get,
+	.access = SNDRV_CTL_ELEM_ACCESS_READ
+},
+{
+	.iface = SNDRV_CTL_ELEM_IFACE_PCM,
+	.name =	 SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM),
+	.info =	 snd_cs46xx_spdif_info,
+	.get =	 snd_cs46xx_spdif_stream_get,
+	.put =	 snd_cs46xx_spdif_stream_put
+},
+
+#endif
+#ifdef CONFIG_SND_CS46XX_DEBUG_GPIO
+{
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "EGPIO select",
+	.info = snd_cs46xx_egpio_select_info,
+	.get = snd_cs46xx_egpio_select_get,
+	.put = snd_cs46xx_egpio_select_put,
+	.private_value = 0,
+},
+{
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "EGPIO Input/Output",
+	.info = snd_mixer_boolean_info,
+	.get = snd_cs46xx_egpio_get,
+	.put = snd_cs46xx_egpio_put,
+	.private_value = BA0_EGPIODR,
+},
+{
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "EGPIO CMOS/Open drain",
+	.info = snd_mixer_boolean_info,
+	.get = snd_cs46xx_egpio_get,
+	.put = snd_cs46xx_egpio_put,
+	.private_value = BA0_EGPIOPTR,
+},
+{
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "EGPIO On/Off",
+	.info = snd_mixer_boolean_info,
+	.get = snd_cs46xx_egpio_get,
+	.put = snd_cs46xx_egpio_put,
+	.private_value = BA0_EGPIOSR,
+},
+#endif
+};
+
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+/* set primary cs4294 codec into Extended Audio Mode */
+static int snd_cs46xx_front_dup_get(struct snd_kcontrol *kcontrol, 
+				    struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_cs46xx *chip = snd_kcontrol_chip(kcontrol);
+	unsigned short val;
+	val = snd_ac97_read(chip->ac97[CS46XX_PRIMARY_CODEC_INDEX], AC97_CSR_ACMODE);
+	ucontrol->value.integer.value[0] = (val & 0x200) ? 0 : 1;
+	return 0;
+}
+
+static int snd_cs46xx_front_dup_put(struct snd_kcontrol *kcontrol, 
+				    struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_cs46xx *chip = snd_kcontrol_chip(kcontrol);
+	return snd_ac97_update_bits(chip->ac97[CS46XX_PRIMARY_CODEC_INDEX],
+				    AC97_CSR_ACMODE, 0x200,
+				    ucontrol->value.integer.value[0] ? 0 : 0x200);
+}
+
+static struct snd_kcontrol_new snd_cs46xx_front_dup_ctl = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "Duplicate Front",
+	.info = snd_mixer_boolean_info,
+	.get = snd_cs46xx_front_dup_get,
+	.put = snd_cs46xx_front_dup_put,
+};
+#endif
+
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+/* Only available on the Hercules Game Theater XP soundcard */
+static struct snd_kcontrol_new snd_hercules_controls[] __devinitdata = {
+{
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "Optical/Coaxial SPDIF Input Switch",
+	.info = snd_mixer_boolean_info,
+	.get = snd_herc_spdif_select_get,
+	.put = snd_herc_spdif_select_put,
+},
+};
+
+
+static void snd_cs46xx_codec_reset (struct snd_ac97 * ac97)
+{
+	unsigned long end_time;
+	int err;
+
+	/* reset to defaults */
+	snd_ac97_write(ac97, AC97_RESET, 0);	
+
+	/* set the desired CODEC mode */
+	if (ac97->num == CS46XX_PRIMARY_CODEC_INDEX) {
+		snd_printdd("cs46xx: CODOEC1 mode %04x\n",0x0);
+		snd_cs46xx_ac97_write(ac97,AC97_CSR_ACMODE,0x0);
+	} else if (ac97->num == CS46XX_SECONDARY_CODEC_INDEX) {
+		snd_printdd("cs46xx: CODOEC2 mode %04x\n",0x3);
+		snd_cs46xx_ac97_write(ac97,AC97_CSR_ACMODE,0x3);
+	} else {
+		snd_assert(0, return); /* should never happen ... */
+	}
+
+	udelay(50);
+
+	/* it's necessary to wait awhile until registers are accessible after RESET */
+	/* because the PCM or MASTER volume registers can be modified, */
+	/* the REC_GAIN register is used for tests */
+	end_time = jiffies + HZ;
+	do {
+		unsigned short ext_mid;
+    
+		/* use preliminary reads to settle the communication */
+		snd_ac97_read(ac97, AC97_RESET);
+		snd_ac97_read(ac97, AC97_VENDOR_ID1);
+		snd_ac97_read(ac97, AC97_VENDOR_ID2);
+		/* modem? */
+		ext_mid = snd_ac97_read(ac97, AC97_EXTENDED_MID);
+		if (ext_mid != 0xffff && (ext_mid & 1) != 0)
+			return;
+
+		/* test if we can write to the record gain volume register */
+		snd_ac97_write_cache(ac97, AC97_REC_GAIN, 0x8a05);
+		if ((err = snd_ac97_read(ac97, AC97_REC_GAIN)) == 0x8a05)
+			return;
+
+		msleep(10);
+	} while (time_after_eq(end_time, jiffies));
+
+	snd_printk(KERN_ERR "CS46xx secondary codec doesn't respond!\n");  
+}
+#endif
+
+static int __devinit cs46xx_detect_codec(struct snd_cs46xx *chip, int codec)
+{
+	int idx, err;
+	struct snd_ac97_template ac97;
+
+	memset(&ac97, 0, sizeof(ac97));
+	ac97.private_data = chip;
+	ac97.private_free = snd_cs46xx_mixer_free_ac97;
+	ac97.num = codec;
+	if (chip->amplifier_ctrl == amp_voyetra)
+		ac97.scaps = AC97_SCAP_INV_EAPD;
+
+	if (codec == CS46XX_SECONDARY_CODEC_INDEX) {
+		snd_cs46xx_codec_write(chip, AC97_RESET, 0, codec);
+		udelay(10);
+		if (snd_cs46xx_codec_read(chip, AC97_RESET, codec) & 0x8000) {
+			snd_printdd("snd_cs46xx: seconadry codec not present\n");
+			return -ENXIO;
+		}
+	}
+
+	snd_cs46xx_codec_write(chip, AC97_MASTER, 0x8000, codec);
+	for (idx = 0; idx < 100; ++idx) {
+		if (snd_cs46xx_codec_read(chip, AC97_MASTER, codec) == 0x8000) {
+			err = snd_ac97_mixer(chip->ac97_bus, &ac97, &chip->ac97[codec]);
+			return err;
+		}
+		msleep(10);
+	}
+	snd_printdd("snd_cs46xx: codec %d detection timeout\n", codec);
+	return -ENXIO;
+}
+
+int __devinit snd_cs46xx_mixer(struct snd_cs46xx *chip, int spdif_device)
+{
+	struct snd_card *card = chip->card;
+	struct snd_ctl_elem_id id;
+	int err;
+	unsigned int idx;
+	static struct snd_ac97_bus_ops ops = {
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+		.reset = snd_cs46xx_codec_reset,
+#endif
+		.write = snd_cs46xx_ac97_write,
+		.read = snd_cs46xx_ac97_read,
+	};
+
+	/* detect primary codec */
+	chip->nr_ac97_codecs = 0;
+	snd_printdd("snd_cs46xx: detecting primary codec\n");
+	if ((err = snd_ac97_bus(card, 0, &ops, chip, &chip->ac97_bus)) < 0)
+		return err;
+	chip->ac97_bus->private_free = snd_cs46xx_mixer_free_ac97_bus;
+
+	if (cs46xx_detect_codec(chip, CS46XX_PRIMARY_CODEC_INDEX) < 0)
+		return -ENXIO;
+	chip->nr_ac97_codecs = 1;
+
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+	snd_printdd("snd_cs46xx: detecting seconadry codec\n");
+	/* try detect a secondary codec */
+	if (! cs46xx_detect_codec(chip, CS46XX_SECONDARY_CODEC_INDEX))
+		chip->nr_ac97_codecs = 2;
+#endif /* CONFIG_SND_CS46XX_NEW_DSP */
+
+	/* add cs4630 mixer controls */
+	for (idx = 0; idx < ARRAY_SIZE(snd_cs46xx_controls); idx++) {
+		struct snd_kcontrol *kctl;
+		kctl = snd_ctl_new1(&snd_cs46xx_controls[idx], chip);
+		if (kctl && kctl->id.iface == SNDRV_CTL_ELEM_IFACE_PCM)
+			kctl->id.device = spdif_device;
+		if ((err = snd_ctl_add(card, kctl)) < 0)
+			return err;
+	}
+
+	/* get EAPD mixer switch (for voyetra hack) */
+	memset(&id, 0, sizeof(id));
+	id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+	strcpy(id.name, "External Amplifier");
+	chip->eapd_switch = snd_ctl_find_id(chip->card, &id);
+    
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+	if (chip->nr_ac97_codecs == 1) {
+		unsigned int id2 = chip->ac97[CS46XX_PRIMARY_CODEC_INDEX]->id & 0xffff;
+		if (id2 == 0x592b || id2 == 0x592d) {
+			err = snd_ctl_add(card, snd_ctl_new1(&snd_cs46xx_front_dup_ctl, chip));
+			if (err < 0)
+				return err;
+			snd_ac97_write_cache(chip->ac97[CS46XX_PRIMARY_CODEC_INDEX],
+					     AC97_CSR_ACMODE, 0x200);
+		}
+	}
+	/* do soundcard specific mixer setup */
+	if (chip->mixer_init) {
+		snd_printdd ("calling chip->mixer_init(chip);\n");
+		chip->mixer_init(chip);
+	}
+#endif
+
+ 	/* turn on amplifier */
+	chip->amplifier_ctrl(chip, 1);
+    
+	return 0;
+}
+
+/*
+ *  RawMIDI interface
+ */
+
+static void snd_cs46xx_midi_reset(struct snd_cs46xx *chip)
+{
+	snd_cs46xx_pokeBA0(chip, BA0_MIDCR, MIDCR_MRST);
+	udelay(100);
+	snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr);
+}
+
+static int snd_cs46xx_midi_input_open(struct snd_rawmidi_substream *substream)
+{
+	struct snd_cs46xx *chip = substream->rmidi->private_data;
+
+	chip->active_ctrl(chip, 1);
+	spin_lock_irq(&chip->reg_lock);
+	chip->uartm |= CS46XX_MODE_INPUT;
+	chip->midcr |= MIDCR_RXE;
+	chip->midi_input = substream;
+	if (!(chip->uartm & CS46XX_MODE_OUTPUT)) {
+		snd_cs46xx_midi_reset(chip);
+	} else {
+		snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr);
+	}
+	spin_unlock_irq(&chip->reg_lock);
+	return 0;
+}
+
+static int snd_cs46xx_midi_input_close(struct snd_rawmidi_substream *substream)
+{
+	struct snd_cs46xx *chip = substream->rmidi->private_data;
+
+	spin_lock_irq(&chip->reg_lock);
+	chip->midcr &= ~(MIDCR_RXE | MIDCR_RIE);
+	chip->midi_input = NULL;
+	if (!(chip->uartm & CS46XX_MODE_OUTPUT)) {
+		snd_cs46xx_midi_reset(chip);
+	} else {
+		snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr);
+	}
+	chip->uartm &= ~CS46XX_MODE_INPUT;
+	spin_unlock_irq(&chip->reg_lock);
+	chip->active_ctrl(chip, -1);
+	return 0;
+}
+
+static int snd_cs46xx_midi_output_open(struct snd_rawmidi_substream *substream)
+{
+	struct snd_cs46xx *chip = substream->rmidi->private_data;
+
+	chip->active_ctrl(chip, 1);
+
+	spin_lock_irq(&chip->reg_lock);
+	chip->uartm |= CS46XX_MODE_OUTPUT;
+	chip->midcr |= MIDCR_TXE;
+	chip->midi_output = substream;
+	if (!(chip->uartm & CS46XX_MODE_INPUT)) {
+		snd_cs46xx_midi_reset(chip);
+	} else {
+		snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr);
+	}
+	spin_unlock_irq(&chip->reg_lock);
+	return 0;
+}
+
+static int snd_cs46xx_midi_output_close(struct snd_rawmidi_substream *substream)
+{
+	struct snd_cs46xx *chip = substream->rmidi->private_data;
+
+	spin_lock_irq(&chip->reg_lock);
+	chip->midcr &= ~(MIDCR_TXE | MIDCR_TIE);
+	chip->midi_output = NULL;
+	if (!(chip->uartm & CS46XX_MODE_INPUT)) {
+		snd_cs46xx_midi_reset(chip);
+	} else {
+		snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr);
+	}
+	chip->uartm &= ~CS46XX_MODE_OUTPUT;
+	spin_unlock_irq(&chip->reg_lock);
+	chip->active_ctrl(chip, -1);
+	return 0;
+}
+
+static void snd_cs46xx_midi_input_trigger(struct snd_rawmidi_substream *substream, int up)
+{
+	unsigned long flags;
+	struct snd_cs46xx *chip = substream->rmidi->private_data;
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	if (up) {
+		if ((chip->midcr & MIDCR_RIE) == 0) {
+			chip->midcr |= MIDCR_RIE;
+			snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr);
+		}
+	} else {
+		if (chip->midcr & MIDCR_RIE) {
+			chip->midcr &= ~MIDCR_RIE;
+			snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr);
+		}
+	}
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+static void snd_cs46xx_midi_output_trigger(struct snd_rawmidi_substream *substream, int up)
+{
+	unsigned long flags;
+	struct snd_cs46xx *chip = substream->rmidi->private_data;
+	unsigned char byte;
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	if (up) {
+		if ((chip->midcr & MIDCR_TIE) == 0) {
+			chip->midcr |= MIDCR_TIE;
+			/* fill UART FIFO buffer at first, and turn Tx interrupts only if necessary */
+			while ((chip->midcr & MIDCR_TIE) &&
+			       (snd_cs46xx_peekBA0(chip, BA0_MIDSR) & MIDSR_TBF) == 0) {
+				if (snd_rawmidi_transmit(substream, &byte, 1) != 1) {
+					chip->midcr &= ~MIDCR_TIE;
+				} else {
+					snd_cs46xx_pokeBA0(chip, BA0_MIDWP, byte);
+				}
+			}
+			snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr);
+		}
+	} else {
+		if (chip->midcr & MIDCR_TIE) {
+			chip->midcr &= ~MIDCR_TIE;
+			snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr);
+		}
+	}
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+static struct snd_rawmidi_ops snd_cs46xx_midi_output =
+{
+	.open =		snd_cs46xx_midi_output_open,
+	.close =	snd_cs46xx_midi_output_close,
+	.trigger =	snd_cs46xx_midi_output_trigger,
+};
+
+static struct snd_rawmidi_ops snd_cs46xx_midi_input =
+{
+	.open =		snd_cs46xx_midi_input_open,
+	.close =	snd_cs46xx_midi_input_close,
+	.trigger =	snd_cs46xx_midi_input_trigger,
+};
+
+int __devinit snd_cs46xx_midi(struct snd_cs46xx *chip, int device, struct snd_rawmidi **rrawmidi)
+{
+	struct snd_rawmidi *rmidi;
+	int err;
+
+	if (rrawmidi)
+		*rrawmidi = NULL;
+	if ((err = snd_rawmidi_new(chip->card, "CS46XX", device, 1, 1, &rmidi)) < 0)
+		return err;
+	strcpy(rmidi->name, "CS46XX");
+	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_cs46xx_midi_output);
+	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_cs46xx_midi_input);
+	rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX;
+	rmidi->private_data = chip;
+	chip->rmidi = rmidi;
+	if (rrawmidi)
+		*rrawmidi = NULL;
+	return 0;
+}
+
+
+/*
+ * gameport interface
+ */
+
+#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
+
+static void snd_cs46xx_gameport_trigger(struct gameport *gameport)
+{
+	struct snd_cs46xx *chip = gameport_get_port_data(gameport);
+
+	snd_assert(chip, return);
+	snd_cs46xx_pokeBA0(chip, BA0_JSPT, 0xFF);  //outb(gameport->io, 0xFF);
+}
+
+static unsigned char snd_cs46xx_gameport_read(struct gameport *gameport)
+{
+	struct snd_cs46xx *chip = gameport_get_port_data(gameport);
+
+	snd_assert(chip, return 0);
+	return snd_cs46xx_peekBA0(chip, BA0_JSPT); //inb(gameport->io);
+}
+
+static int snd_cs46xx_gameport_cooked_read(struct gameport *gameport, int *axes, int *buttons)
+{
+	struct snd_cs46xx *chip = gameport_get_port_data(gameport);
+	unsigned js1, js2, jst;
+
+	snd_assert(chip, return 0);
+
+	js1 = snd_cs46xx_peekBA0(chip, BA0_JSC1);
+	js2 = snd_cs46xx_peekBA0(chip, BA0_JSC2);
+	jst = snd_cs46xx_peekBA0(chip, BA0_JSPT);
+	
+	*buttons = (~jst >> 4) & 0x0F; 
+	
+	axes[0] = ((js1 & JSC1_Y1V_MASK) >> JSC1_Y1V_SHIFT) & 0xFFFF;
+	axes[1] = ((js1 & JSC1_X1V_MASK) >> JSC1_X1V_SHIFT) & 0xFFFF;
+	axes[2] = ((js2 & JSC2_Y2V_MASK) >> JSC2_Y2V_SHIFT) & 0xFFFF;
+	axes[3] = ((js2 & JSC2_X2V_MASK) >> JSC2_X2V_SHIFT) & 0xFFFF;
+
+	for(jst=0;jst<4;++jst)
+		if(axes[jst]==0xFFFF) axes[jst] = -1;
+	return 0;
+}
+
+static int snd_cs46xx_gameport_open(struct gameport *gameport, int mode)
+{
+	switch (mode) {
+	case GAMEPORT_MODE_COOKED:
+		return 0;
+	case GAMEPORT_MODE_RAW:
+		return 0;
+	default:
+		return -1;
+	}
+	return 0;
+}
+
+int __devinit snd_cs46xx_gameport(struct snd_cs46xx *chip)
+{
+	struct gameport *gp;
+
+	chip->gameport = gp = gameport_allocate_port();
+	if (!gp) {
+		printk(KERN_ERR "cs46xx: cannot allocate memory for gameport\n");
+		return -ENOMEM;
+	}
+
+	gameport_set_name(gp, "CS46xx Gameport");
+	gameport_set_phys(gp, "pci%s/gameport0", pci_name(chip->pci));
+	gameport_set_dev_parent(gp, &chip->pci->dev);
+	gameport_set_port_data(gp, chip);
+
+	gp->open = snd_cs46xx_gameport_open;
+	gp->read = snd_cs46xx_gameport_read;
+	gp->trigger = snd_cs46xx_gameport_trigger;
+	gp->cooked_read = snd_cs46xx_gameport_cooked_read;
+
+	snd_cs46xx_pokeBA0(chip, BA0_JSIO, 0xFF); // ?
+	snd_cs46xx_pokeBA0(chip, BA0_JSCTL, JSCTL_SP_MEDIUM_SLOW);
+
+	gameport_register_port(gp);
+
+	return 0;
+}
+
+static inline void snd_cs46xx_remove_gameport(struct snd_cs46xx *chip)
+{
+	if (chip->gameport) {
+		gameport_unregister_port(chip->gameport);
+		chip->gameport = NULL;
+	}
+}
+#else
+int __devinit snd_cs46xx_gameport(struct snd_cs46xx *chip) { return -ENOSYS; }
+static inline void snd_cs46xx_remove_gameport(struct snd_cs46xx *chip) { }
+#endif /* CONFIG_GAMEPORT */
+
+#ifdef CONFIG_PROC_FS
+/*
+ *  proc interface
+ */
+
+static long snd_cs46xx_io_read(struct snd_info_entry *entry, void *file_private_data,
+			       struct file *file, char __user *buf,
+			       unsigned long count, unsigned long pos)
+{
+	long size;
+	struct snd_cs46xx_region *region = entry->private_data;
+	
+	size = count;
+	if (pos + (size_t)size > region->size)
+		size = region->size - pos;
+	if (size > 0) {
+		if (copy_to_user_fromio(buf, region->remap_addr + pos, size))
+			return -EFAULT;
+	}
+	return size;
+}
+
+static struct snd_info_entry_ops snd_cs46xx_proc_io_ops = {
+	.read = snd_cs46xx_io_read,
+};
+
+static int __devinit snd_cs46xx_proc_init(struct snd_card *card, struct snd_cs46xx *chip)
+{
+	struct snd_info_entry *entry;
+	int idx;
+	
+	for (idx = 0; idx < 5; idx++) {
+		struct snd_cs46xx_region *region = &chip->region.idx[idx];
+		if (! snd_card_proc_new(card, region->name, &entry)) {
+			entry->content = SNDRV_INFO_CONTENT_DATA;
+			entry->private_data = chip;
+			entry->c.ops = &snd_cs46xx_proc_io_ops;
+			entry->size = region->size;
+			entry->mode = S_IFREG | S_IRUSR;
+		}
+	}
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+	cs46xx_dsp_proc_init(card, chip);
+#endif
+	return 0;
+}
+
+static int snd_cs46xx_proc_done(struct snd_cs46xx *chip)
+{
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+	cs46xx_dsp_proc_done(chip);
+#endif
+	return 0;
+}
+#else /* !CONFIG_PROC_FS */
+#define snd_cs46xx_proc_init(card, chip)
+#define snd_cs46xx_proc_done(chip)
+#endif
+
+/*
+ * stop the h/w
+ */
+static void snd_cs46xx_hw_stop(struct snd_cs46xx *chip)
+{
+	unsigned int tmp;
+
+	tmp = snd_cs46xx_peek(chip, BA1_PFIE);
+	tmp &= ~0x0000f03f;
+	tmp |=  0x00000010;
+	snd_cs46xx_poke(chip, BA1_PFIE, tmp);	/* playback interrupt disable */
+
+	tmp = snd_cs46xx_peek(chip, BA1_CIE);
+	tmp &= ~0x0000003f;
+	tmp |=  0x00000011;
+	snd_cs46xx_poke(chip, BA1_CIE, tmp);	/* capture interrupt disable */
+
+	/*
+         *  Stop playback DMA.
+	 */
+	tmp = snd_cs46xx_peek(chip, BA1_PCTL);
+	snd_cs46xx_poke(chip, BA1_PCTL, tmp & 0x0000ffff);
+
+	/*
+         *  Stop capture DMA.
+	 */
+	tmp = snd_cs46xx_peek(chip, BA1_CCTL);
+	snd_cs46xx_poke(chip, BA1_CCTL, tmp & 0xffff0000);
+
+	/*
+         *  Reset the processor.
+         */
+	snd_cs46xx_reset(chip);
+
+	snd_cs46xx_proc_stop(chip);
+
+	/*
+	 *  Power down the PLL.
+	 */
+	snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, 0);
+
+	/*
+	 *  Turn off the Processor by turning off the software clock enable flag in 
+	 *  the clock control register.
+	 */
+	tmp = snd_cs46xx_peekBA0(chip, BA0_CLKCR1) & ~CLKCR1_SWCE;
+	snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, tmp);
+}
+
+
+static int snd_cs46xx_free(struct snd_cs46xx *chip)
+{
+	int idx;
+
+	snd_assert(chip != NULL, return -EINVAL);
+
+	if (chip->active_ctrl)
+		chip->active_ctrl(chip, 1);
+
+	snd_cs46xx_remove_gameport(chip);
+
+	if (chip->amplifier_ctrl)
+		chip->amplifier_ctrl(chip, -chip->amplifier); /* force to off */
+	
+	snd_cs46xx_proc_done(chip);
+
+	if (chip->region.idx[0].resource)
+		snd_cs46xx_hw_stop(chip);
+
+        if (chip->irq >= 0)
+            free_irq(chip->irq, chip);
+
+	for (idx = 0; idx < 5; idx++) {
+		struct snd_cs46xx_region *region = &chip->region.idx[idx];
+		if (region->remap_addr)
+			iounmap(region->remap_addr);
+		release_and_free_resource(region->resource);
+	}
+
+	if (chip->active_ctrl)
+		chip->active_ctrl(chip, -chip->amplifier);
+	
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+	if (chip->dsp_spos_instance) {
+		cs46xx_dsp_spos_destroy(chip);
+		chip->dsp_spos_instance = NULL;
+	}
+#endif
+	
+	pci_disable_device(chip->pci);
+	kfree(chip);
+	return 0;
+}
+
+static int snd_cs46xx_dev_free(struct snd_device *device)
+{
+	struct snd_cs46xx *chip = device->device_data;
+	return snd_cs46xx_free(chip);
+}
+
+/*
+ *  initialize chip
+ */
+static int snd_cs46xx_chip_init(struct snd_cs46xx *chip)
+{
+	int timeout;
+
+	/* 
+	 *  First, blast the clock control register to zero so that the PLL starts
+         *  out in a known state, and blast the master serial port control register
+         *  to zero so that the serial ports also start out in a known state.
+         */
+        snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, 0);
+        snd_cs46xx_pokeBA0(chip, BA0_SERMC1, 0);
+
+	/*
+	 *  If we are in AC97 mode, then we must set the part to a host controlled
+         *  AC-link.  Otherwise, we won't be able to bring up the link.
+         */        
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+	snd_cs46xx_pokeBA0(chip, BA0_SERACC, SERACC_HSP | SERACC_CHIP_TYPE_2_0 | 
+			   SERACC_TWO_CODECS);	/* 2.00 dual codecs */
+	/* snd_cs46xx_pokeBA0(chip, BA0_SERACC, SERACC_HSP | SERACC_CHIP_TYPE_2_0); */ /* 2.00 codec */
+#else
+	snd_cs46xx_pokeBA0(chip, BA0_SERACC, SERACC_HSP | SERACC_CHIP_TYPE_1_03); /* 1.03 codec */
+#endif
+
+        /*
+         *  Drive the ARST# pin low for a minimum of 1uS (as defined in the AC97
+         *  spec) and then drive it high.  This is done for non AC97 modes since
+         *  there might be logic external to the CS461x that uses the ARST# line
+         *  for a reset.
+         */
+	snd_cs46xx_pokeBA0(chip, BA0_ACCTL, 0);
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+	snd_cs46xx_pokeBA0(chip, BA0_ACCTL2, 0);
+#endif
+	udelay(50);
+	snd_cs46xx_pokeBA0(chip, BA0_ACCTL, ACCTL_RSTN);
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+	snd_cs46xx_pokeBA0(chip, BA0_ACCTL2, ACCTL_RSTN);
+#endif
+    
+	/*
+	 *  The first thing we do here is to enable sync generation.  As soon
+	 *  as we start receiving bit clock, we'll start producing the SYNC
+	 *  signal.
+	 */
+	snd_cs46xx_pokeBA0(chip, BA0_ACCTL, ACCTL_ESYN | ACCTL_RSTN);
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+	snd_cs46xx_pokeBA0(chip, BA0_ACCTL2, ACCTL_ESYN | ACCTL_RSTN);
+#endif
+
+	/*
+	 *  Now wait for a short while to allow the AC97 part to start
+	 *  generating bit clock (so we don't try to start the PLL without an
+	 *  input clock).
+	 */
+	mdelay(10);
+
+	/*
+	 *  Set the serial port timing configuration, so that
+	 *  the clock control circuit gets its clock from the correct place.
+	 */
+	snd_cs46xx_pokeBA0(chip, BA0_SERMC1, SERMC1_PTC_AC97);
+
+	/*
+	 *  Write the selected clock control setup to the hardware.  Do not turn on
+	 *  SWCE yet (if requested), so that the devices clocked by the output of
+	 *  PLL are not clocked until the PLL is stable.
+	 */
+	snd_cs46xx_pokeBA0(chip, BA0_PLLCC, PLLCC_LPF_1050_2780_KHZ | PLLCC_CDR_73_104_MHZ);
+	snd_cs46xx_pokeBA0(chip, BA0_PLLM, 0x3a);
+	snd_cs46xx_pokeBA0(chip, BA0_CLKCR2, CLKCR2_PDIVS_8);
+
+	/*
+	 *  Power up the PLL.
+	 */
+	snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, CLKCR1_PLLP);
+
+	/*
+         *  Wait until the PLL has stabilized.
+	 */
+	msleep(100);
+
+	/*
+	 *  Turn on clocking of the core so that we can setup the serial ports.
+	 */
+	snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, CLKCR1_PLLP | CLKCR1_SWCE);
+
+	/*
+	 * Enable FIFO  Host Bypass
+	 */
+	snd_cs46xx_pokeBA0(chip, BA0_SERBCF, SERBCF_HBP);
+
+	/*
+	 *  Fill the serial port FIFOs with silence.
+	 */
+	snd_cs46xx_clear_serial_FIFOs(chip);
+
+	/*
+	 *  Set the serial port FIFO pointer to the first sample in the FIFO.
+	 */
+	/* snd_cs46xx_pokeBA0(chip, BA0_SERBSP, 0); */
+
+	/*
+	 *  Write the serial port configuration to the part.  The master
+	 *  enable bit is not set until all other values have been written.
+	 */
+	snd_cs46xx_pokeBA0(chip, BA0_SERC1, SERC1_SO1F_AC97 | SERC1_SO1EN);
+	snd_cs46xx_pokeBA0(chip, BA0_SERC2, SERC2_SI1F_AC97 | SERC1_SO1EN);
+	snd_cs46xx_pokeBA0(chip, BA0_SERMC1, SERMC1_PTC_AC97 | SERMC1_MSPE);
+
+
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+	snd_cs46xx_pokeBA0(chip, BA0_SERC7, SERC7_ASDI2EN);
+	snd_cs46xx_pokeBA0(chip, BA0_SERC3, 0);
+	snd_cs46xx_pokeBA0(chip, BA0_SERC4, 0);
+	snd_cs46xx_pokeBA0(chip, BA0_SERC5, 0);
+	snd_cs46xx_pokeBA0(chip, BA0_SERC6, 1);
+#endif
+
+	mdelay(5);
+
+
+	/*
+	 * Wait for the codec ready signal from the AC97 codec.
+	 */
+	timeout = 150;
+	while (timeout-- > 0) {
+		/*
+		 *  Read the AC97 status register to see if we've seen a CODEC READY
+		 *  signal from the AC97 codec.
+		 */
+		if (snd_cs46xx_peekBA0(chip, BA0_ACSTS) & ACSTS_CRDY)
+			goto ok1;
+                //msleep(10);
+                set_current_state(TASK_UNINTERRUPTIBLE);
+                schedule_timeout((HZ+99)/100);
+	}
+
+
+	snd_printk(KERN_ERR "create - never read codec ready from AC'97\n");
+	snd_printk(KERN_ERR "it is not probably bug, try to use CS4236 driver\n");
+	return -EIO;
+ ok1:
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+	{
+		int count;
+		for (count = 0; count < 150; count++) {
+			/* First, we want to wait for a short time. */
+			udelay(25);
+        
+			if (snd_cs46xx_peekBA0(chip, BA0_ACSTS2) & ACSTS_CRDY)
+				break;
+		}
+
+		/*
+		 *  Make sure CODEC is READY.
+		 */
+		if (!(snd_cs46xx_peekBA0(chip, BA0_ACSTS2) & ACSTS_CRDY))
+			snd_printdd("cs46xx: never read card ready from secondary AC'97\n");
+	}
+#endif
+
+	/*
+	 *  Assert the vaid frame signal so that we can start sending commands
+	 *  to the AC97 codec.
+	 */
+	snd_cs46xx_pokeBA0(chip, BA0_ACCTL, ACCTL_VFRM | ACCTL_ESYN | ACCTL_RSTN);
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+	snd_cs46xx_pokeBA0(chip, BA0_ACCTL2, ACCTL_VFRM | ACCTL_ESYN | ACCTL_RSTN);
+#endif
+
+
+	/*
+	 *  Wait until we've sampled input slots 3 and 4 as valid, meaning that
+	 *  the codec is pumping ADC data across the AC-link.
+	 */
+	timeout = 150;
+	while (timeout-- > 0) {
+		/*
+		 *  Read the input slot valid register and see if input slots 3 and
+		 *  4 are valid yet.
+		 */
+		if ((snd_cs46xx_peekBA0(chip, BA0_ACISV) & (ACISV_ISV3 | ACISV_ISV4)) == (ACISV_ISV3 | ACISV_ISV4))
+			goto ok2;
+                //msleep(10);
+                set_current_state(TASK_UNINTERRUPTIBLE);
+                schedule_timeout((HZ+99)/100);
+	}
+
+#ifndef CONFIG_SND_CS46XX_NEW_DSP
+	snd_printk(KERN_ERR "create - never read ISV3 & ISV4 from AC'97\n");
+	return -EIO;
+#else
+	/* This may happen on a cold boot with a Terratec SiXPack 5.1.
+	   Reloading the driver may help, if there's other soundcards 
+	   with the same problem I would like to know. (Benny) */
+
+	snd_printk(KERN_ERR "ERROR: snd-cs46xx: never read ISV3 & ISV4 from AC'97\n");
+	snd_printk(KERN_ERR "       Try reloading the ALSA driver, if you find something\n");
+        snd_printk(KERN_ERR "       broken or not working on your soundcard upon\n");
+	snd_printk(KERN_ERR "       this message please report to alsa-devel@lists.sourceforge.net\n");
+
+	return -EIO;
+#endif
+ ok2:
+
+	/*
+	 *  Now, assert valid frame and the slot 3 and 4 valid bits.  This will
+	 *  commense the transfer of digital audio data to the AC97 codec.
+	 */
+
+	snd_cs46xx_pokeBA0(chip, BA0_ACOSV, ACOSV_SLV3 | ACOSV_SLV4);
+
+
+	/*
+	 *  Power down the DAC and ADC.  We will power them up (if) when we need
+	 *  them.
+	 */
+	/* snd_cs46xx_pokeBA0(chip, BA0_AC97_POWERDOWN, 0x300); */
+
+	/*
+	 *  Turn off the Processor by turning off the software clock enable flag in 
+	 *  the clock control register.
+	 */
+	/* tmp = snd_cs46xx_peekBA0(chip, BA0_CLKCR1) & ~CLKCR1_SWCE; */
+	/* snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, tmp); */
+
+	return 0;
+}
+
+/*
+ *  start and load DSP 
+ */
+int __devinit snd_cs46xx_start_dsp(struct snd_cs46xx *chip)
+{	
+	unsigned int tmp;
+	/*
+	 *  Reset the processor.
+	 */
+	snd_cs46xx_reset(chip);
+	/*
+	 *  Download the image to the processor.
+	 */
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+#if 0
+	if (cs46xx_dsp_load_module(chip, &cwcemb80_module) < 0) {
+		snd_printk(KERN_ERR "image download error\n");
+		return -EIO;
+	}
+#endif
+
+	if (cs46xx_dsp_load_module(chip, &cwc4630_module) < 0) {
+		snd_printk(KERN_ERR "image download error [cwc4630]\n");
+		return -EIO;
+	}
+
+	if (cs46xx_dsp_load_module(chip, &cwcasync_module) < 0) {
+		snd_printk(KERN_ERR "image download error [cwcasync]\n");
+		return -EIO;
+	}
+
+	if (cs46xx_dsp_load_module(chip, &cwcsnoop_module) < 0) {
+		snd_printk(KERN_ERR "image download error [cwcsnoop]\n");
+		return -EIO;
+	}
+
+	if (cs46xx_dsp_load_module(chip, &cwcbinhack_module) < 0) {
+		snd_printk(KERN_ERR "image download error [cwcbinhack]\n");
+		return -EIO;
+	}
+
+	if (cs46xx_dsp_load_module(chip, &cwcdma_module) < 0) {
+		snd_printk(KERN_ERR "image download error [cwcdma]\n");
+		return -EIO;
+	}
+
+	if (cs46xx_dsp_scb_and_task_init(chip) < 0)
+		return -EIO;
+#else
+	/* old image */
+	if (snd_cs46xx_download_image(chip) < 0) {
+		snd_printk(KERN_ERR "image download error\n");
+		return -EIO;
+	}
+
+	/*
+         *  Stop playback DMA.
+	 */
+	tmp = snd_cs46xx_peek(chip, BA1_PCTL);
+	chip->play_ctl = tmp & 0xffff0000;
+	snd_cs46xx_poke(chip, BA1_PCTL, tmp & 0x0000ffff);
+#endif
+
+	/*
+         *  Stop capture DMA.
+	 */
+	tmp = snd_cs46xx_peek(chip, BA1_CCTL);
+	chip->capt.ctl = tmp & 0x0000ffff;
+	snd_cs46xx_poke(chip, BA1_CCTL, tmp & 0xffff0000);
+
+	mdelay(5);
+
+	snd_cs46xx_set_play_sample_rate(chip, 8000);
+	snd_cs46xx_set_capture_sample_rate(chip, 8000);
+
+	snd_cs46xx_proc_start(chip);
+
+	/*
+	 *  Enable interrupts on the part.
+	 */
+	snd_cs46xx_pokeBA0(chip, BA0_HICR, HICR_IEV | HICR_CHGM);
+        
+	tmp = snd_cs46xx_peek(chip, BA1_PFIE);
+	tmp &= ~0x0000f03f;
+	snd_cs46xx_poke(chip, BA1_PFIE, tmp);	/* playback interrupt enable */
+
+	tmp = snd_cs46xx_peek(chip, BA1_CIE);
+	tmp &= ~0x0000003f;
+	tmp |=  0x00000001;
+	snd_cs46xx_poke(chip, BA1_CIE, tmp);	/* capture interrupt enable */
+	
+#ifndef CONFIG_SND_CS46XX_NEW_DSP
+	/* set the attenuation to 0dB */ 
+	snd_cs46xx_poke(chip, BA1_PVOL, 0x80008000);
+	snd_cs46xx_poke(chip, BA1_CVOL, 0x80008000);
+#endif
+
+	return 0;
+}
+
+
+/*
+ *	AMP control - null AMP
+ */
+ 
+static void amp_none(struct snd_cs46xx *chip, int change)
+{	
+}
+
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+static int voyetra_setup_eapd_slot(struct snd_cs46xx *chip)
+{
+	
+	u32 idx, valid_slots,tmp,powerdown = 0;
+	u16 modem_power,pin_config,logic_type;
+
+	snd_printdd ("cs46xx: cs46xx_setup_eapd_slot()+\n");
+
+	/*
+	 *  See if the devices are powered down.  If so, we must power them up first
+	 *  or they will not respond.
+	 */
+	tmp = snd_cs46xx_peekBA0(chip, BA0_CLKCR1);
+
+	if (!(tmp & CLKCR1_SWCE)) {
+		snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, tmp | CLKCR1_SWCE);
+		powerdown = 1;
+	}
+
+	/*
+	 * Clear PRA.  The Bonzo chip will be used for GPIO not for modem
+	 * stuff.
+	 */
+	if(chip->nr_ac97_codecs != 2) {
+		snd_printk (KERN_ERR "cs46xx: cs46xx_setup_eapd_slot() - no secondary codec configured\n");
+		return -EINVAL;
+	}
+
+	modem_power = snd_cs46xx_codec_read (chip, 
+					     AC97_EXTENDED_MSTATUS,
+					     CS46XX_SECONDARY_CODEC_INDEX);
+	modem_power &=0xFEFF;
+
+	snd_cs46xx_codec_write(chip, 
+			       AC97_EXTENDED_MSTATUS, modem_power,
+			       CS46XX_SECONDARY_CODEC_INDEX);
+
+	/*
+	 * Set GPIO pin's 7 and 8 so that they are configured for output.
+	 */
+	pin_config = snd_cs46xx_codec_read (chip, 
+					    AC97_GPIO_CFG,
+					    CS46XX_SECONDARY_CODEC_INDEX);
+	pin_config &=0x27F;
+
+	snd_cs46xx_codec_write(chip, 
+			       AC97_GPIO_CFG, pin_config,
+			       CS46XX_SECONDARY_CODEC_INDEX);
+    
+	/*
+	 * Set GPIO pin's 7 and 8 so that they are compatible with CMOS logic.
+	 */
+
+	logic_type = snd_cs46xx_codec_read(chip, AC97_GPIO_POLARITY,
+					   CS46XX_SECONDARY_CODEC_INDEX);
+	logic_type &=0x27F; 
+
+	snd_cs46xx_codec_write (chip, AC97_GPIO_POLARITY, logic_type,
+				CS46XX_SECONDARY_CODEC_INDEX);
+
+	valid_slots = snd_cs46xx_peekBA0(chip, BA0_ACOSV);
+	valid_slots |= 0x200;
+	snd_cs46xx_pokeBA0(chip, BA0_ACOSV, valid_slots);
+
+	if ( cs46xx_wait_for_fifo(chip,1) ) {
+	  snd_printdd("FIFO is busy\n");
+	  
+	  return -EINVAL;
+	}
+
+	/*
+	 * Fill slots 12 with the correct value for the GPIO pins. 
+	 */
+	for(idx = 0x90; idx <= 0x9F; idx++) {
+		/*
+		 * Initialize the fifo so that bits 7 and 8 are on.
+		 *
+		 * Remember that the GPIO pins in bonzo are shifted by 4 bits to
+		 * the left.  0x1800 corresponds to bits 7 and 8.
+		 */
+		snd_cs46xx_pokeBA0(chip, BA0_SERBWP, 0x1800);
+
+		/*
+		 * Wait for command to complete
+		 */
+		if ( cs46xx_wait_for_fifo(chip,200) ) {
+			snd_printdd("failed waiting for FIFO at addr (%02X)\n",idx);
+
+			return -EINVAL;
+		}
+            
+		/*
+		 * Write the serial port FIFO index.
+		 */
+		snd_cs46xx_pokeBA0(chip, BA0_SERBAD, idx);
+      
+		/*
+		 * Tell the serial port to load the new value into the FIFO location.
+		 */
+		snd_cs46xx_pokeBA0(chip, BA0_SERBCM, SERBCM_WRC);
+	}
+
+	/* wait for last command to complete */
+	cs46xx_wait_for_fifo(chip,200);
+
+	/*
+	 *  Now, if we powered up the devices, then power them back down again.
+	 *  This is kinda ugly, but should never happen.
+	 */
+	if (powerdown)
+		snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, tmp);
+
+	return 0;
+}
+#endif
+
+/*
+ *	Crystal EAPD mode
+ */
+ 
+static void amp_voyetra(struct snd_cs46xx *chip, int change)
+{
+	/* Manage the EAPD bit on the Crystal 4297 
+	   and the Analog AD1885 */
+	   
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+	int old = chip->amplifier;
+#endif
+	int oval, val;
+	
+	chip->amplifier += change;
+	oval = snd_cs46xx_codec_read(chip, AC97_POWERDOWN,
+				     CS46XX_PRIMARY_CODEC_INDEX);
+	val = oval;
+	if (chip->amplifier) {
+		/* Turn the EAPD amp on */
+		val |= 0x8000;
+	} else {
+		/* Turn the EAPD amp off */
+		val &= ~0x8000;
+	}
+	if (val != oval) {
+		snd_cs46xx_codec_write(chip, AC97_POWERDOWN, val,
+				       CS46XX_PRIMARY_CODEC_INDEX);
+		if (chip->eapd_switch)
+			snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
+				       &chip->eapd_switch->id);
+	}
+
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+	if (chip->amplifier && !old) {
+		voyetra_setup_eapd_slot(chip);
+	}
+#endif
+}
+
+static void hercules_init(struct snd_cs46xx *chip) 
+{
+	/* default: AMP off, and SPDIF input optical */
+	snd_cs46xx_pokeBA0(chip, BA0_EGPIODR, EGPIODR_GPOE0);
+	snd_cs46xx_pokeBA0(chip, BA0_EGPIOPTR, EGPIODR_GPOE0);
+}
+
+
+/*
+ *	Game Theatre XP card - EGPIO[2] is used to enable the external amp.
+ */ 
+static void amp_hercules(struct snd_cs46xx *chip, int change)
+{
+	int old = chip->amplifier;
+	int val1 = snd_cs46xx_peekBA0(chip, BA0_EGPIODR);
+	int val2 = snd_cs46xx_peekBA0(chip, BA0_EGPIOPTR);
+
+	chip->amplifier += change;
+	if (chip->amplifier && !old) {
+		snd_printdd ("Hercules amplifier ON\n");
+
+		snd_cs46xx_pokeBA0(chip, BA0_EGPIODR, 
+				   EGPIODR_GPOE2 | val1);     /* enable EGPIO2 output */
+		snd_cs46xx_pokeBA0(chip, BA0_EGPIOPTR, 
+				   EGPIOPTR_GPPT2 | val2);   /* open-drain on output */
+	} else if (old && !chip->amplifier) {
+		snd_printdd ("Hercules amplifier OFF\n");
+		snd_cs46xx_pokeBA0(chip, BA0_EGPIODR,  val1 & ~EGPIODR_GPOE2); /* disable */
+		snd_cs46xx_pokeBA0(chip, BA0_EGPIOPTR, val2 & ~EGPIOPTR_GPPT2); /* disable */
+	}
+}
+
+static void voyetra_mixer_init (struct snd_cs46xx *chip)
+{
+	snd_printdd ("initializing Voyetra mixer\n");
+
+	/* Enable SPDIF out */
+	snd_cs46xx_pokeBA0(chip, BA0_EGPIODR, EGPIODR_GPOE0);
+	snd_cs46xx_pokeBA0(chip, BA0_EGPIOPTR, EGPIODR_GPOE0);
+}
+
+static void hercules_mixer_init (struct snd_cs46xx *chip)
+{
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+	unsigned int idx;
+	int err;
+	struct snd_card *card = chip->card;
+#endif
+
+	/* set EGPIO to default */
+	hercules_init(chip);
+
+	snd_printdd ("initializing Hercules mixer\n");
+
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+	for (idx = 0 ; idx < ARRAY_SIZE(snd_hercules_controls); idx++) {
+		struct snd_kcontrol *kctl;
+
+		kctl = snd_ctl_new1(&snd_hercules_controls[idx], chip);
+		if ((err = snd_ctl_add(card, kctl)) < 0) {
+			printk (KERN_ERR "cs46xx: failed to initialize Hercules mixer (%d)\n",err);
+			break;
+		}
+	}
+#endif
+}
+
+
+#if 0
+/*
+ *	Untested
+ */
+ 
+static void amp_voyetra_4294(struct snd_cs46xx *chip, int change)
+{
+	chip->amplifier += change;
+
+	if (chip->amplifier) {
+		/* Switch the GPIO pins 7 and 8 to open drain */
+		snd_cs46xx_codec_write(chip, 0x4C,
+				       snd_cs46xx_codec_read(chip, 0x4C) & 0xFE7F);
+		snd_cs46xx_codec_write(chip, 0x4E,
+				       snd_cs46xx_codec_read(chip, 0x4E) | 0x0180);
+		/* Now wake the AMP (this might be backwards) */
+		snd_cs46xx_codec_write(chip, 0x54,
+				       snd_cs46xx_codec_read(chip, 0x54) & ~0x0180);
+	} else {
+		snd_cs46xx_codec_write(chip, 0x54,
+				       snd_cs46xx_codec_read(chip, 0x54) | 0x0180);
+	}
+}
+#endif
+
+
+/*
+ *	Handle the CLKRUN on a thinkpad. We must disable CLKRUN support
+ *	whenever we need to beat on the chip.
+ *
+ *	The original idea and code for this hack comes from David Kaiser at
+ *	Linuxcare. Perhaps one day Crystal will document their chips well
+ *	enough to make them useful.
+ */
+ 
+static void clkrun_hack(struct snd_cs46xx *chip, int change)
+{
+	u16 control, nval;
+	
+	if (!chip->acpi_port)
+		return;
+
+	chip->amplifier += change;
+	
+	/* Read ACPI port */	
+	nval = control = inw(chip->acpi_port + 0x10);
+
+	/* Flip CLKRUN off while running */
+	if (! chip->amplifier)
+		nval |= 0x2000;
+	else
+		nval &= ~0x2000;
+	if (nval != control)
+		outw(nval, chip->acpi_port + 0x10);
+}
+
+	
+/*
+ * detect intel piix4
+ */
+static void clkrun_init(struct snd_cs46xx *chip)
+{
+	struct pci_dev *pdev;
+	u8 pp;
+
+	chip->acpi_port = 0;
+	
+	pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
+		PCI_DEVICE_ID_INTEL_82371AB_3, NULL);
+	if (pdev == NULL)
+		return;		/* Not a thinkpad thats for sure */
+
+	/* Find the control port */		
+	pci_read_config_byte(pdev, 0x41, &pp);
+	chip->acpi_port = pp << 8;
+	pci_dev_put(pdev);
+}
+
+
+/*
+ * Card subid table
+ */
+ 
+struct cs_card_type
+{
+	u16 vendor;
+	u16 id;
+	char *name;
+	void (*init)(struct snd_cs46xx *);
+	void (*amp)(struct snd_cs46xx *, int);
+	void (*active)(struct snd_cs46xx *, int);
+	void (*mixer_init)(struct snd_cs46xx *);
+};
+
+static struct cs_card_type __devinitdata cards[] = {
+	{
+		.vendor = 0x1489,
+		.id = 0x7001,
+		.name = "Genius Soundmaker 128 value",
+		/* nothing special */
+	},
+	{
+		.vendor = 0x5053,
+		.id = 0x3357,
+		.name = "Voyetra",
+		.amp = amp_voyetra,
+		.mixer_init = voyetra_mixer_init,
+	},
+	{
+		.vendor = 0x1071,
+		.id = 0x6003,
+		.name = "Mitac MI6020/21",
+		.amp = amp_voyetra,
+	},
+	{
+		.vendor = 0x14AF,
+		.id = 0x0050,
+		.name = "Hercules Game Theatre XP",
+		.amp = amp_hercules,
+		.mixer_init = hercules_mixer_init,
+	},
+	{
+		.vendor = 0x1681,
+		.id = 0x0050,
+		.name = "Hercules Game Theatre XP",
+		.amp = amp_hercules,
+		.mixer_init = hercules_mixer_init,
+	},
+	{
+		.vendor = 0x1681,
+		.id = 0x0051,
+		.name = "Hercules Game Theatre XP",
+		.amp = amp_hercules,
+		.mixer_init = hercules_mixer_init,
+
+	},
+	{
+		.vendor = 0x1681,
+		.id = 0x0052,
+		.name = "Hercules Game Theatre XP",
+		.amp = amp_hercules,
+		.mixer_init = hercules_mixer_init,
+	},
+	{
+		.vendor = 0x1681,
+		.id = 0x0053,
+		.name = "Hercules Game Theatre XP",
+		.amp = amp_hercules,
+		.mixer_init = hercules_mixer_init,
+	},
+	{
+		.vendor = 0x1681,
+		.id = 0x0054,
+		.name = "Hercules Game Theatre XP",
+		.amp = amp_hercules,
+		.mixer_init = hercules_mixer_init,
+	},
+	/* Teratec */
+	{
+		.vendor = 0x153b,
+		.id = 0x1136,
+		.name = "Terratec SiXPack 5.1",
+	},
+	/* Not sure if the 570 needs the clkrun hack */
+	{
+		.vendor = PCI_VENDOR_ID_IBM,
+		.id = 0x0132,
+		.name = "Thinkpad 570",
+		.init = clkrun_init,
+		.active = clkrun_hack,
+	},
+	{
+		.vendor = PCI_VENDOR_ID_IBM,
+		.id = 0x0153,
+		.name = "Thinkpad 600X/A20/T20",
+		.init = clkrun_init,
+		.active = clkrun_hack,
+	},
+	{
+		.vendor = PCI_VENDOR_ID_IBM,
+		.id = 0x1010,
+		.name = "Thinkpad 600E (unsupported)",
+	},
+	{0} /* terminator */
+};
+
+
+/*
+ * APM support
+ */
+#ifdef CONFIG_PM
+int snd_cs46xx_suspend(struct pci_dev *pci, pm_message_t state)
+{
+	struct snd_card *card = pci_get_drvdata(pci);
+	struct snd_cs46xx *chip = card->private_data;
+	int amp_saved;
+
+	snd_pcm_suspend_all(chip->pcm);
+	// chip->ac97_powerdown = snd_cs46xx_codec_read(chip, AC97_POWER_CONTROL);
+	// chip->ac97_general_purpose = snd_cs46xx_codec_read(chip, BA0_AC97_GENERAL_PURPOSE);
+
+	snd_ac97_suspend(chip->ac97[CS46XX_PRIMARY_CODEC_INDEX]);
+	snd_ac97_suspend(chip->ac97[CS46XX_SECONDARY_CODEC_INDEX]);
+
+	amp_saved = chip->amplifier;
+	/* turn off amp */
+	chip->amplifier_ctrl(chip, -chip->amplifier);
+	snd_cs46xx_hw_stop(chip);
+	/* disable CLKRUN */
+	chip->active_ctrl(chip, -chip->amplifier);
+	chip->amplifier = amp_saved; /* restore the status */
+	pci_disable_device(pci);
+	pci_save_state(pci);
+	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+	return 0;
+}
+
+int snd_cs46xx_resume(struct pci_dev *pci)
+{
+	struct snd_card *card = pci_get_drvdata(pci);
+	struct snd_cs46xx *chip = card->private_data;
+	int amp_saved;
+
+	pci_restore_state(pci);
+	pci_enable_device(pci);
+	pci_set_master(pci);
+	amp_saved = chip->amplifier;
+	chip->amplifier = 0;
+	chip->active_ctrl(chip, 1); /* force to on */
+
+	snd_cs46xx_chip_init(chip);
+
+#if 0
+	snd_cs46xx_codec_write(chip, BA0_AC97_GENERAL_PURPOSE, 
+			       chip->ac97_general_purpose);
+	snd_cs46xx_codec_write(chip, AC97_POWER_CONTROL, 
+			       chip->ac97_powerdown);
+	mdelay(10);
+	snd_cs46xx_codec_write(chip, BA0_AC97_POWERDOWN,
+			       chip->ac97_powerdown);
+	mdelay(5);
+#endif
+
+	snd_ac97_resume(chip->ac97[CS46XX_PRIMARY_CODEC_INDEX]);
+	snd_ac97_resume(chip->ac97[CS46XX_SECONDARY_CODEC_INDEX]);
+
+	if (amp_saved)
+		chip->amplifier_ctrl(chip, 1); /* turn amp on */
+	else
+		chip->active_ctrl(chip, -1); /* disable CLKRUN */
+	chip->amplifier = amp_saved;
+        snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+        printk("CS46XX: resume\n");
+	return 0;
+}
+#endif /* CONFIG_PM */
+
+
+/*
+ */
+
+int __devinit snd_cs46xx_create(struct snd_card *card,
+		      struct pci_dev * pci,
+		      int external_amp, int thinkpad,
+		      struct snd_cs46xx ** rchip)
+{
+	struct snd_cs46xx *chip;
+	int err, idx;
+	struct snd_cs46xx_region *region;
+	struct cs_card_type *cp;
+	u16 ss_card, ss_vendor;
+	static struct snd_device_ops ops = {
+		.dev_free =	snd_cs46xx_dev_free,
+	};
+	
+	*rchip = NULL;
+
+	/* enable PCI device */
+	if ((err = pci_enable_device(pci)) < 0)
+		return err;
+
+	chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+	if (chip == NULL) {
+		pci_disable_device(pci);
+		return -ENOMEM;
+	}
+	spin_lock_init(&chip->reg_lock);
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+	mutex_init(&chip->spos_mutex);
+#endif
+	chip->card = card;
+	chip->pci = pci;
+	chip->irq = -1;
+	chip->ba0_addr = pci_resource_start(pci, 0);
+	chip->ba1_addr = pci_resource_start(pci, 1);
+	if (chip->ba0_addr == 0 || chip->ba0_addr == (unsigned long)~0 ||
+	    chip->ba1_addr == 0 || chip->ba1_addr == (unsigned long)~0) {
+	    	snd_printk(KERN_ERR "wrong address(es) - ba0 = 0x%lx, ba1 = 0x%lx\n",
+			   chip->ba0_addr, chip->ba1_addr);
+	    	snd_cs46xx_free(chip);
+	    	return -ENOMEM;
+	}
+
+	region = &chip->region.name.ba0;
+	strcpy(region->name, "CS46xx_BA0");
+	region->base = chip->ba0_addr;
+	region->size = CS46XX_BA0_SIZE;
+
+	region = &chip->region.name.data0;
+	strcpy(region->name, "CS46xx_BA1_data0");
+	region->base = chip->ba1_addr + BA1_SP_DMEM0;
+	region->size = CS46XX_BA1_DATA0_SIZE;
+
+	region = &chip->region.name.data1;
+	strcpy(region->name, "CS46xx_BA1_data1");
+	region->base = chip->ba1_addr + BA1_SP_DMEM1;
+	region->size = CS46XX_BA1_DATA1_SIZE;
+
+	region = &chip->region.name.pmem;
+	strcpy(region->name, "CS46xx_BA1_pmem");
+	region->base = chip->ba1_addr + BA1_SP_PMEM;
+	region->size = CS46XX_BA1_PRG_SIZE;
+
+	region = &chip->region.name.reg;
+	strcpy(region->name, "CS46xx_BA1_reg");
+	region->base = chip->ba1_addr + BA1_SP_REG;
+	region->size = CS46XX_BA1_REG_SIZE;
+
+	/* set up amp and clkrun hack */
+	pci_read_config_word(pci, PCI_SUBSYSTEM_VENDOR_ID, &ss_vendor);
+	pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &ss_card);
+
+	for (cp = &cards[0]; cp->name; cp++) {
+		if (cp->vendor == ss_vendor && cp->id == ss_card) {
+			snd_printdd ("hack for %s enabled\n", cp->name);
+
+			chip->amplifier_ctrl = cp->amp;
+			chip->active_ctrl = cp->active;
+			chip->mixer_init = cp->mixer_init;
+
+			if (cp->init)
+				cp->init(chip);
+			break;
+		}
+	}
+
+	if (external_amp) {
+		snd_printk(KERN_INFO "Crystal EAPD support forced on.\n");
+		chip->amplifier_ctrl = amp_voyetra;
+	}
+
+	if (thinkpad) {
+		snd_printk(KERN_INFO "Activating CLKRUN hack for Thinkpad.\n");
+		chip->active_ctrl = clkrun_hack;
+		clkrun_init(chip);
+	}
+	
+	if (chip->amplifier_ctrl == NULL)
+		chip->amplifier_ctrl = amp_none;
+	if (chip->active_ctrl == NULL)
+		chip->active_ctrl = amp_none;
+
+	chip->active_ctrl(chip, 1); /* enable CLKRUN */
+
+	pci_set_master(pci);
+
+	for (idx = 0; idx < 5; idx++) {
+		region = &chip->region.idx[idx];
+		if ((region->resource = request_mem_region(region->base, region->size,
+							   region->name)) == NULL) {
+			snd_printk(KERN_ERR "unable to request memory region 0x%lx-0x%lx\n",
+				   region->base, region->base + region->size - 1);
+			snd_cs46xx_free(chip);
+			return -EBUSY;
+		}
+		region->remap_addr = ioremap_nocache(region->base, region->size);
+		if (region->remap_addr == NULL) {
+			snd_printk(KERN_ERR "%s ioremap problem\n", region->name);
+			snd_cs46xx_free(chip);
+			return -ENOMEM;
+		}
+	}
+
+	if (request_irq(pci->irq, snd_cs46xx_interrupt, SA_INTERRUPT|SA_SHIRQ,
+			"CS46XX", chip)) {
+		snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
+		snd_cs46xx_free(chip);
+		return -EBUSY;
+	}
+	chip->irq = pci->irq;
+
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+	chip->dsp_spos_instance = cs46xx_dsp_spos_create(chip);
+	if (chip->dsp_spos_instance == NULL) {
+		snd_cs46xx_free(chip);
+		return -ENOMEM;
+	}
+#endif
+
+	err = snd_cs46xx_chip_init(chip);
+	if (err < 0) {
+		snd_cs46xx_free(chip);
+		return err;
+	}
+
+	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+		snd_cs46xx_free(chip);
+		return err;
+	}
+	
+	snd_cs46xx_proc_init(card, chip);
+
+	chip->active_ctrl(chip, -1); /* disable CLKRUN */
+
+	snd_card_set_dev(card, &pci->dev);
+
+	*rchip = chip;
+	return 0;
+}
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/cs46xx/cs46xx_lib.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/cs46xx/cs46xx_lib.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/cs46xx/cs46xx_lib.h	(revision 91)
@@ -0,0 +1,203 @@
+/*
+ *  The driver for the Cirrus Logic's Sound Fusion CS46XX based soundcards
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#ifndef __CS46XX_LIB_H__
+#define __CS46XX_LIB_H__
+
+/*
+ *  constants
+ */
+
+#define CS46XX_BA0_SIZE		  0x1000
+#define CS46XX_BA1_DATA0_SIZE 0x3000
+#define CS46XX_BA1_DATA1_SIZE 0x3800
+#define CS46XX_BA1_PRG_SIZE	  0x7000
+#define CS46XX_BA1_REG_SIZE	  0x0100
+
+
+
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+#define CS46XX_MIN_PERIOD_SIZE 1
+#define CS46XX_MAX_PERIOD_SIZE 1024*1024
+#else
+#define CS46XX_MIN_PERIOD_SIZE 2048
+#define CS46XX_MAX_PERIOD_SIZE 2048
+#endif
+
+#define CS46XX_FRAGS 2
+/* #define CS46XX_BUFFER_SIZE CS46XX_MAX_PERIOD_SIZE * CS46XX_FRAGS */
+
+#define SCB_NO_PARENT             0
+#define SCB_ON_PARENT_NEXT_SCB    1
+#define SCB_ON_PARENT_SUBLIST_SCB 2
+
+/* 3*1024 parameter, 3.5*1024 sample, 2*3.5*1024 code */
+#define BA1_DWORD_SIZE		(13 * 1024 + 512)
+#define BA1_MEMORY_COUNT	3
+
+/*
+ *  common I/O routines
+ */
+
+static inline void snd_cs46xx_poke(struct snd_cs46xx *chip, unsigned long reg, unsigned int val)
+{
+	unsigned int bank = reg >> 16;
+	unsigned int offset = reg & 0xffff;
+
+	/*if (bank == 0) printk("snd_cs46xx_poke: %04X - %08X\n",reg >> 2,val); */
+	writel(val, chip->region.idx[bank+1].remap_addr + offset);
+}
+
+static inline unsigned int snd_cs46xx_peek(struct snd_cs46xx *chip, unsigned long reg)
+{
+	unsigned int bank = reg >> 16;
+	unsigned int offset = reg & 0xffff;
+	return readl(chip->region.idx[bank+1].remap_addr + offset);
+}
+
+static inline void snd_cs46xx_pokeBA0(struct snd_cs46xx *chip, unsigned long offset, unsigned int val)
+{
+	writel(val, chip->region.name.ba0.remap_addr + offset);
+}
+
+static inline unsigned int snd_cs46xx_peekBA0(struct snd_cs46xx *chip, unsigned long offset)
+{
+	return readl(chip->region.name.ba0.remap_addr + offset);
+}
+
+struct dsp_spos_instance *cs46xx_dsp_spos_create (struct snd_cs46xx * chip);
+void cs46xx_dsp_spos_destroy (struct snd_cs46xx * chip);
+int cs46xx_dsp_load_module (struct snd_cs46xx * chip, struct dsp_module_desc * module);
+struct dsp_symbol_entry *cs46xx_dsp_lookup_symbol (struct snd_cs46xx * chip, char * symbol_name,
+						   int symbol_type);
+#ifdef CONFIG_PROC_FS
+int cs46xx_dsp_proc_init (struct snd_card *card, struct snd_cs46xx *chip);
+int cs46xx_dsp_proc_done (struct snd_cs46xx *chip);
+#else
+#define cs46xx_dsp_proc_init(card, chip)
+#define cs46xx_dsp_proc_done(chip)
+#endif
+int cs46xx_dsp_scb_and_task_init (struct snd_cs46xx *chip);
+int snd_cs46xx_download (struct snd_cs46xx *chip, u32 *src, unsigned long offset,
+			 unsigned long len);
+int snd_cs46xx_clear_BA1(struct snd_cs46xx *chip, unsigned long offset, unsigned long len);
+int cs46xx_dsp_enable_spdif_out (struct snd_cs46xx *chip);
+int cs46xx_dsp_enable_spdif_hw (struct snd_cs46xx *chip);
+int cs46xx_dsp_disable_spdif_out (struct snd_cs46xx *chip);
+int cs46xx_dsp_enable_spdif_in (struct snd_cs46xx *chip);
+int cs46xx_dsp_disable_spdif_in (struct snd_cs46xx *chip);
+int cs46xx_dsp_enable_pcm_capture (struct snd_cs46xx *chip);
+int cs46xx_dsp_disable_pcm_capture (struct snd_cs46xx *chip);
+int cs46xx_dsp_enable_adc_capture (struct snd_cs46xx *chip);
+int cs46xx_dsp_disable_adc_capture (struct snd_cs46xx *chip);
+int cs46xx_poke_via_dsp (struct snd_cs46xx *chip, u32 address, u32 data);
+struct dsp_scb_descriptor * cs46xx_dsp_create_scb (struct snd_cs46xx *chip, char * name,
+						   u32 * scb_data, u32 dest);
+#ifdef CONFIG_PROC_FS
+void cs46xx_dsp_proc_free_scb_desc (struct dsp_scb_descriptor * scb);
+void cs46xx_dsp_proc_register_scb_desc (struct snd_cs46xx *chip,
+					struct dsp_scb_descriptor * scb);
+#else
+#define cs46xx_dsp_proc_free_scb_desc(scb)
+#define cs46xx_dsp_proc_register_scb_desc(chip, scb)
+#endif
+struct dsp_scb_descriptor * cs46xx_dsp_create_timing_master_scb (struct snd_cs46xx *chip);
+struct dsp_scb_descriptor *
+cs46xx_dsp_create_codec_out_scb(struct snd_cs46xx * chip,
+				char * codec_name, u16 channel_disp, u16 fifo_addr,
+				u16 child_scb_addr, u32 dest,
+				struct dsp_scb_descriptor * parent_scb,
+				int scb_child_type);
+struct dsp_scb_descriptor *
+cs46xx_dsp_create_codec_in_scb(struct snd_cs46xx * chip, char * codec_name,
+			       u16 channel_disp, u16 fifo_addr,
+			       u16 sample_buffer_addr, u32 dest,
+			       struct dsp_scb_descriptor * parent_scb,
+			       int scb_child_type);
+void cs46xx_dsp_remove_scb (struct snd_cs46xx *chip,
+			    struct dsp_scb_descriptor * scb);
+struct dsp_scb_descriptor *
+cs46xx_dsp_create_codec_in_scb(struct snd_cs46xx * chip, char * codec_name,
+			       u16 channel_disp, u16 fifo_addr,
+			       u16 sample_buffer_addr, u32 dest,
+			       struct dsp_scb_descriptor * parent_scb,
+			       int scb_child_type);
+struct dsp_scb_descriptor *
+cs46xx_dsp_create_src_task_scb(struct snd_cs46xx * chip, char * scb_name,
+			       int sample_rate, u16 src_buffer_addr,
+			       u16 src_delay_buffer_addr, u32 dest,
+			       struct dsp_scb_descriptor * parent_scb,
+			       int scb_child_type, int pass_through);
+struct dsp_scb_descriptor *
+cs46xx_dsp_create_mix_only_scb(struct snd_cs46xx * chip, char * scb_name,
+			       u16 mix_buffer_addr, u32 dest,
+			       struct dsp_scb_descriptor * parent_scb,
+			       int scb_child_type);
+
+struct dsp_scb_descriptor *
+cs46xx_dsp_create_vari_decimate_scb(struct snd_cs46xx * chip, char * scb_name,
+				    u16 vari_buffer_addr0, u16 vari_buffer_addr1, u32 dest,
+				    struct dsp_scb_descriptor * parent_scb,
+				    int scb_child_type);
+struct dsp_scb_descriptor *
+cs46xx_dsp_create_asynch_fg_rx_scb(struct snd_cs46xx * chip, char * scb_name,
+				   u32 dest, u16 hfg_scb_address, u16 asynch_buffer_address,
+				   struct dsp_scb_descriptor * parent_scb,
+				   int scb_child_type);
+struct dsp_scb_descriptor *
+cs46xx_dsp_create_spio_write_scb(struct snd_cs46xx * chip, char * scb_name, u32 dest,
+				 struct dsp_scb_descriptor * parent_scb,
+				 int scb_child_type);
+struct dsp_scb_descriptor *
+cs46xx_dsp_create_mix_to_ostream_scb(struct snd_cs46xx * chip, char * scb_name,
+				     u16 mix_buffer_addr, u16 writeback_spb, u32 dest,
+				     struct dsp_scb_descriptor * parent_scb,
+				     int scb_child_type);
+struct dsp_scb_descriptor *
+cs46xx_dsp_create_magic_snoop_scb(struct snd_cs46xx * chip, char * scb_name,
+				  u32 dest, u16 snoop_buffer_address,
+				  struct dsp_scb_descriptor * snoop_scb,
+				  struct dsp_scb_descriptor * parent_scb,
+				  int scb_child_type);
+struct dsp_pcm_channel_descriptor *
+cs46xx_dsp_create_pcm_channel (struct snd_cs46xx * chip, u32 sample_rate,
+			       void * private_data, u32 hw_dma_addr,
+			       int pcm_channel_id);
+void cs46xx_dsp_destroy_pcm_channel (struct snd_cs46xx * chip,
+				     struct dsp_pcm_channel_descriptor * pcm_channel);
+int cs46xx_dsp_pcm_unlink (struct snd_cs46xx * chip,
+			   struct dsp_pcm_channel_descriptor * pcm_channel);
+int cs46xx_dsp_pcm_link (struct snd_cs46xx * chip,
+			 struct dsp_pcm_channel_descriptor * pcm_channel);
+struct dsp_scb_descriptor *
+cs46xx_add_record_source (struct snd_cs46xx *chip, struct dsp_scb_descriptor * source,
+			  u16 addr, char * scb_name);
+int cs46xx_src_unlink(struct snd_cs46xx *chip, struct dsp_scb_descriptor * src);
+int cs46xx_src_link(struct snd_cs46xx *chip, struct dsp_scb_descriptor * src);
+int cs46xx_iec958_pre_open (struct snd_cs46xx *chip);
+int cs46xx_iec958_post_close (struct snd_cs46xx *chip);
+int cs46xx_dsp_pcm_channel_set_period (struct snd_cs46xx * chip,
+				       struct dsp_pcm_channel_descriptor * pcm_channel,
+				       int period_size);
+int cs46xx_dsp_pcm_ostream_set_period (struct snd_cs46xx * chip, int period_size);
+int cs46xx_dsp_set_dac_volume (struct snd_cs46xx * chip, u16 left, u16 right);
+int cs46xx_dsp_set_iec958_volume (struct snd_cs46xx * chip, u16 left, u16 right);
+#endif /* __CS46XX_LIB_H__ */
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/cs46xx/diff.cmd
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/cs46xx/diff.cmd	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/cs46xx/diff.cmd	(revision 91)
@@ -0,0 +1,1 @@
+diff.exe -Nuwr e:\dev\uss\from_z\ua2\GPL\alsa-kernel\pci\cs46xx\%1 E:\DEV\alsa\alsa-kernel\pci\cs46xx\%1 >%1.diff
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/cs46xx/diff1.cmd
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/cs46xx/diff1.cmd	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/cs46xx/diff1.cmd	(revision 91)
@@ -0,0 +1,1 @@
+diff.exe -Nuwr e:\dev\uss\from_z\ua2\GPL\alsa-kernel\pci\cs46xx\%1 E:\DEV\uss\ua2\gpl\alsa-kernel\pci\cs46xx\%1 >%1.diff
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/cs46xx/dsp_spos.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/cs46xx/dsp_spos.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/cs46xx/dsp_spos.c	(revision 91)
@@ -0,0 +1,1921 @@
+/*
+ *   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
+ *
+ */
+
+/*
+ * 2002-07 Benny Sjostrand benny@hostmobility.com
+ */
+
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/pm.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/mutex.h>
+
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/info.h>
+#include <sound/asoundef.h>
+#include <sound/cs46xx.h>
+
+#include "cs46xx_lib.h"
+#include "dsp_spos.h"
+
+static int cs46xx_dsp_async_init (struct snd_cs46xx *chip,
+				  struct dsp_scb_descriptor * fg_entry);
+
+static enum wide_opcode wide_opcodes[] = { 
+	WIDE_FOR_BEGIN_LOOP,
+	WIDE_FOR_BEGIN_LOOP2,
+	WIDE_COND_GOTO_ADDR,
+	WIDE_COND_GOTO_CALL,
+	WIDE_TBEQ_COND_GOTO_ADDR,
+	WIDE_TBEQ_COND_CALL_ADDR,
+	WIDE_TBEQ_NCOND_GOTO_ADDR,
+	WIDE_TBEQ_NCOND_CALL_ADDR,
+	WIDE_TBEQ_COND_GOTO1_ADDR,
+	WIDE_TBEQ_COND_CALL1_ADDR,
+	WIDE_TBEQ_NCOND_GOTOI_ADDR,
+	WIDE_TBEQ_NCOND_CALL1_ADDR
+};
+
+static int shadow_and_reallocate_code (struct snd_cs46xx * chip, u32 * data, u32 size,
+				       u32 overlay_begin_address)
+{
+	unsigned int i = 0, j, nreallocated = 0;
+	u32 hival,loval,address;
+	u32 mop_operands,mop_type,wide_op;
+	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
+
+	snd_assert( ((size % 2) == 0), return -EINVAL);
+  
+	while (i < size) {
+		loval = data[i++];
+		hival = data[i++];
+
+		if (ins->code.offset > 0) {
+			mop_operands = (hival >> 6) & 0x03fff;
+			mop_type = mop_operands >> 10;
+      
+			/* check for wide type instruction */
+			if (mop_type == 0 &&
+			    (mop_operands & WIDE_LADD_INSTR_MASK) == 0 &&
+			    (mop_operands & WIDE_INSTR_MASK) != 0) {
+				wide_op = loval & 0x7f;
+				for (j = 0;j < ARRAY_SIZE(wide_opcodes); ++j) {
+					if (wide_opcodes[j] == wide_op) {
+						/* need to reallocate instruction */
+						address  = (hival & 0x00FFF) << 5;
+						address |=  loval >> 15;
+            
+						snd_printdd("handle_wideop[1]: %05x:%05x addr %04x\n",hival,loval,address);
+            
+						if ( !(address & 0x8000) ) {
+							address += (ins->code.offset / 2) - overlay_begin_address;
+						} else {
+							snd_printdd("handle_wideop[1]: ROM symbol not reallocated\n");
+						}
+            
+						hival &= 0xFF000;
+						loval &= 0x07FFF;
+            
+						hival |= ( (address >> 5)  & 0x00FFF);
+						loval |= ( (address << 15) & 0xF8000);
+            
+						address  = (hival & 0x00FFF) << 5;
+						address |=  loval >> 15;
+            
+						snd_printdd("handle_wideop:[2] %05x:%05x addr %04x\n",hival,loval,address);            
+						nreallocated ++;
+					} /* wide_opcodes[j] == wide_op */
+				} /* for */
+			} /* mod_type == 0 ... */
+		} /* ins->code.offset > 0 */
+
+		ins->code.data[ins->code.size++] = loval;
+		ins->code.data[ins->code.size++] = hival;
+	}
+
+	snd_printdd("dsp_spos: %d instructions reallocated\n",nreallocated);
+	return nreallocated;
+}
+
+static struct dsp_segment_desc * get_segment_desc (struct dsp_module_desc * module, int seg_type)
+{
+	int i;
+	for (i = 0;i < module->nsegments; ++i) {
+		if (module->segments[i].segment_type == seg_type) {
+			return (module->segments + i);
+		}
+	}
+
+	return NULL;
+};
+
+static int find_free_symbol_index (struct dsp_spos_instance * ins)
+{
+	int index = ins->symbol_table.nsymbols,i;
+
+	for (i = ins->symbol_table.highest_frag_index; i < ins->symbol_table.nsymbols; ++i) {
+		if (ins->symbol_table.symbols[i].deleted) {
+			index = i;
+			break;
+		}
+	}
+
+	return index;
+}
+
+static int add_symbols (struct snd_cs46xx * chip, struct dsp_module_desc * module)
+{
+	int i;
+	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
+
+	if (module->symbol_table.nsymbols > 0) {
+		if (!strcmp(module->symbol_table.symbols[0].symbol_name, "OVERLAYBEGINADDRESS") &&
+		    module->symbol_table.symbols[0].symbol_type == SYMBOL_CONSTANT ) {
+			module->overlay_begin_address = module->symbol_table.symbols[0].address;
+		}
+	}
+
+	for (i = 0;i < module->symbol_table.nsymbols; ++i) {
+		if (ins->symbol_table.nsymbols == (DSP_MAX_SYMBOLS - 1)) {
+			snd_printk(KERN_ERR "dsp_spos: symbol table is full\n");
+			return -ENOMEM;
+		}
+
+
+		if (cs46xx_dsp_lookup_symbol(chip,
+					     module->symbol_table.symbols[i].symbol_name,
+					     module->symbol_table.symbols[i].symbol_type) == NULL) {
+
+			ins->symbol_table.symbols[ins->symbol_table.nsymbols] = module->symbol_table.symbols[i];
+			ins->symbol_table.symbols[ins->symbol_table.nsymbols].address += ((ins->code.offset / 2) - module->overlay_begin_address);
+			ins->symbol_table.symbols[ins->symbol_table.nsymbols].module = module;
+			ins->symbol_table.symbols[ins->symbol_table.nsymbols].deleted = 0;
+
+			if (ins->symbol_table.nsymbols > ins->symbol_table.highest_frag_index) 
+				ins->symbol_table.highest_frag_index = ins->symbol_table.nsymbols;
+
+			ins->symbol_table.nsymbols++;
+		} else {
+          /* if (0) printk ("dsp_spos: symbol <%s> duplicated, probably nothing wrong with that (Cirrus?)\n",
+                             module->symbol_table.symbols[i].symbol_name); */
+		}
+	}
+
+	return 0;
+}
+
+static struct dsp_symbol_entry *
+add_symbol (struct snd_cs46xx * chip, char * symbol_name, u32 address, int type)
+{
+	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
+	struct dsp_symbol_entry * symbol = NULL;
+	int index;
+
+	if (ins->symbol_table.nsymbols == (DSP_MAX_SYMBOLS - 1)) {
+		snd_printk(KERN_ERR "dsp_spos: symbol table is full\n");
+		return NULL;
+	}
+  
+	if (cs46xx_dsp_lookup_symbol(chip,
+				     symbol_name,
+				     type) != NULL) {
+		snd_printk(KERN_ERR "dsp_spos: symbol <%s> duplicated\n", symbol_name);
+		return NULL;
+	}
+
+	index = find_free_symbol_index (ins);
+
+	strcpy (ins->symbol_table.symbols[index].symbol_name, symbol_name);
+	ins->symbol_table.symbols[index].address = address;
+	ins->symbol_table.symbols[index].symbol_type = type;
+	ins->symbol_table.symbols[index].module = NULL;
+	ins->symbol_table.symbols[index].deleted = 0;
+	symbol = (ins->symbol_table.symbols + index);
+
+	if (index > ins->symbol_table.highest_frag_index) 
+		ins->symbol_table.highest_frag_index = index;
+
+	if (index == ins->symbol_table.nsymbols)
+		ins->symbol_table.nsymbols++; /* no frag. in list */
+
+	return symbol;
+}
+
+struct dsp_spos_instance *cs46xx_dsp_spos_create (struct snd_cs46xx * chip)
+{
+	struct dsp_spos_instance * ins = kzalloc(sizeof(struct dsp_spos_instance), GFP_KERNEL);
+
+	if (ins == NULL) 
+		return NULL;
+
+	/* better to use vmalloc for this big table */
+	ins->symbol_table.nsymbols = 0;
+	ins->symbol_table.symbols = vmalloc(sizeof(struct dsp_symbol_entry) *
+					    DSP_MAX_SYMBOLS);
+	ins->symbol_table.highest_frag_index = 0;
+
+        if (ins->symbol_table.symbols == NULL) {
+            cs46xx_dsp_spos_destroy(chip);
+            goto error;
+	}
+
+	ins->code.offset = 0;
+	ins->code.size = 0;
+	ins->code.data = kmalloc(DSP_CODE_BYTE_SIZE, GFP_KERNEL);
+
+	if (ins->code.data == NULL) {
+            cs46xx_dsp_spos_destroy(chip);
+            goto error;
+	}
+
+	ins->nscb = 0;
+	ins->ntask = 0;
+
+	ins->nmodules = 0;
+	ins->modules = kmalloc(sizeof(struct dsp_module_desc) * DSP_MAX_MODULES, GFP_KERNEL);
+
+	if (ins->modules == NULL) {
+            cs46xx_dsp_spos_destroy(chip);
+            goto error;
+	}
+
+	/* default SPDIF input sample rate
+	   to 48000 khz */
+	ins->spdif_in_sample_rate = 48000;
+
+	/* maximize volume */
+	ins->dac_volume_right = 0x8000;
+	ins->dac_volume_left = 0x8000;
+	ins->spdif_input_volume_right = 0x8000;
+	ins->spdif_input_volume_left = 0x8000;
+
+	/* set left and right validity bits and
+	   default channel status */
+	ins->spdif_csuv_default = 
+		ins->spdif_csuv_stream =  
+	 /* byte 0 */  ((unsigned int)_wrap_all_bits(  (SNDRV_PCM_DEFAULT_CON_SPDIF        & 0xff)) << 24) |
+	 /* byte 1 */  ((unsigned int)_wrap_all_bits( ((SNDRV_PCM_DEFAULT_CON_SPDIF >> 8) & 0xff)) << 16) |
+	 /* byte 3 */   (unsigned int)_wrap_all_bits(  (SNDRV_PCM_DEFAULT_CON_SPDIF >> 24) & 0xff) |
+	 /* left and right validity bits */ (1 << 13) | (1 << 12);
+        return ins;
+    error:
+        kfree(ins);
+        return NULL;
+}
+
+void  cs46xx_dsp_spos_destroy (struct snd_cs46xx * chip)
+{
+	int i;
+	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
+
+	snd_assert(ins != NULL, return);
+
+	mutex_lock(&chip->spos_mutex);
+	for (i = 0; i < ins->nscb; ++i) {
+		if (ins->scbs[i].deleted) continue;
+
+		cs46xx_dsp_proc_free_scb_desc ( (ins->scbs + i) );
+	}
+
+	kfree(ins->code.data);
+	vfree(ins->symbol_table.symbols);
+	kfree(ins->modules);
+	kfree(ins);
+	mutex_unlock(&chip->spos_mutex);
+}
+
+int cs46xx_dsp_load_module (struct snd_cs46xx * chip, struct dsp_module_desc * module)
+{
+	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
+	struct dsp_segment_desc * code = get_segment_desc (module,SEGTYPE_SP_PROGRAM);
+	struct dsp_segment_desc * parameter = get_segment_desc (module,SEGTYPE_SP_PARAMETER);
+	struct dsp_segment_desc * sample = get_segment_desc (module,SEGTYPE_SP_SAMPLE);
+	u32 doffset, dsize;
+
+	if (ins->nmodules == DSP_MAX_MODULES - 1) {
+		snd_printk(KERN_ERR "dsp_spos: to many modules loaded into DSP\n");
+		return -ENOMEM;
+	}
+
+	snd_printdd("dsp_spos: loading module %s into DSP\n", module->module_name);
+  
+	if (ins->nmodules == 0) {
+		snd_printdd("dsp_spos: clearing parameter area\n");
+		snd_cs46xx_clear_BA1(chip, DSP_PARAMETER_BYTE_OFFSET, DSP_PARAMETER_BYTE_SIZE);
+	}
+  
+	if (parameter == NULL) {
+		snd_printdd("dsp_spos: module got no parameter segment\n");
+	} else {
+		if (ins->nmodules > 0) {
+			snd_printk(KERN_WARNING "dsp_spos: WARNING current parameter data may be overwriten!\n");
+		}
+
+		doffset = (parameter->offset * 4 + DSP_PARAMETER_BYTE_OFFSET);
+		dsize   = parameter->size * 4;
+
+		snd_printdd("dsp_spos: downloading parameter data to chip (%08x-%08x)\n",
+			    doffset,doffset + dsize);
+
+		if (snd_cs46xx_download (chip, parameter->data, doffset, dsize)) {
+			snd_printk(KERN_ERR "dsp_spos: failed to download parameter data to DSP\n");
+			return -EINVAL;
+		}
+	}
+
+	if (ins->nmodules == 0) {
+		snd_printdd("dsp_spos: clearing sample area\n");
+		snd_cs46xx_clear_BA1(chip, DSP_SAMPLE_BYTE_OFFSET, DSP_SAMPLE_BYTE_SIZE);
+	}
+
+	if (sample == NULL) {
+		snd_printdd("dsp_spos: module got no sample segment\n");
+	} else {
+		if (ins->nmodules > 0) {
+			snd_printk(KERN_WARNING "dsp_spos: WARNING current sample data may be overwriten\n");
+		}
+
+		doffset = (sample->offset * 4  + DSP_SAMPLE_BYTE_OFFSET);
+		dsize   =  sample->size * 4;
+
+		snd_printdd("dsp_spos: downloading sample data to chip (%08x-%08x)\n",
+			    doffset,doffset + dsize);
+
+		if (snd_cs46xx_download (chip,sample->data,doffset,dsize)) {
+			snd_printk(KERN_ERR "dsp_spos: failed to sample data to DSP\n");
+			return -EINVAL;
+		}
+	}
+
+
+	if (ins->nmodules == 0) {
+		snd_printdd("dsp_spos: clearing code area\n");
+		snd_cs46xx_clear_BA1(chip, DSP_CODE_BYTE_OFFSET, DSP_CODE_BYTE_SIZE);
+	}
+
+	if (code == NULL) {
+		snd_printdd("dsp_spos: module got no code segment\n");
+	} else {
+		if (ins->code.offset + code->size > DSP_CODE_BYTE_SIZE) {
+			snd_printk(KERN_ERR "dsp_spos: no space available in DSP\n");
+			return -ENOMEM;
+		}
+
+		module->load_address = ins->code.offset;
+		module->overlay_begin_address = 0x000;
+
+		/* if module has a code segment it must have
+		   symbol table */
+		snd_assert(module->symbol_table.symbols != NULL ,return -ENOMEM);
+		if (add_symbols(chip,module)) {
+			snd_printk(KERN_ERR "dsp_spos: failed to load symbol table\n");
+			return -ENOMEM;
+		}
+    
+		doffset = (code->offset * 4 + ins->code.offset * 4 + DSP_CODE_BYTE_OFFSET);
+		dsize   = code->size * 4;
+		snd_printdd("dsp_spos: downloading code to chip (%08x-%08x)\n",
+			    doffset,doffset + dsize);   
+
+		module->nfixups = shadow_and_reallocate_code(chip,code->data,code->size,module->overlay_begin_address);
+
+		if (snd_cs46xx_download (chip,(ins->code.data + ins->code.offset),doffset,dsize)) {
+			snd_printk(KERN_ERR "dsp_spos: failed to download code to DSP\n");
+			return -EINVAL;
+		}
+
+		ins->code.offset += code->size;
+	}
+
+	/* NOTE: module segments and symbol table must be
+	   statically allocated. Case that module data is
+	   not generated by the ospparser */
+	ins->modules[ins->nmodules] = *module;
+	ins->nmodules++;
+
+	return 0;
+}
+
+struct dsp_symbol_entry *
+cs46xx_dsp_lookup_symbol (struct snd_cs46xx * chip, char * symbol_name, int symbol_type)
+{
+	int i;
+	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
+
+	for ( i = 0; i < ins->symbol_table.nsymbols; ++i ) {
+
+		if (ins->symbol_table.symbols[i].deleted)
+			continue;
+
+		if (!strcmp(ins->symbol_table.symbols[i].symbol_name,symbol_name) &&
+		    ins->symbol_table.symbols[i].symbol_type == symbol_type) {
+			return (ins->symbol_table.symbols + i);
+		}
+	}
+
+#if 0
+	printk ("dsp_spos: symbol <%s> type %02x not found\n",
+		symbol_name,symbol_type);
+#endif
+
+	return NULL;
+}
+
+
+#ifdef CONFIG_PROC_FS
+static struct dsp_symbol_entry *
+cs46xx_dsp_lookup_symbol_addr (struct snd_cs46xx * chip, u32 address, int symbol_type)
+{
+	int i;
+	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
+
+	for ( i = 0; i < ins->symbol_table.nsymbols; ++i ) {
+
+		if (ins->symbol_table.symbols[i].deleted)
+			continue;
+
+		if (ins->symbol_table.symbols[i].address == address &&
+		    ins->symbol_table.symbols[i].symbol_type == symbol_type) {
+			return (ins->symbol_table.symbols + i);
+		}
+	}
+
+
+	return NULL;
+}
+
+
+static void cs46xx_dsp_proc_symbol_table_read (struct snd_info_entry *entry,
+					       struct snd_info_buffer *buffer)
+{
+	struct snd_cs46xx *chip = entry->private_data;
+	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
+	int i;
+
+	snd_iprintf(buffer, "SYMBOLS:\n");
+	for ( i = 0; i < ins->symbol_table.nsymbols; ++i ) {
+		char *module_str = "system";
+
+		if (ins->symbol_table.symbols[i].deleted)
+			continue;
+
+		if (ins->symbol_table.symbols[i].module != NULL) {
+			module_str = ins->symbol_table.symbols[i].module->module_name;
+		}
+
+    
+		snd_iprintf(buffer, "%04X <%02X> %s [%s]\n",
+			    ins->symbol_table.symbols[i].address,
+			    ins->symbol_table.symbols[i].symbol_type,
+			    ins->symbol_table.symbols[i].symbol_name,
+			    module_str);    
+	}
+}
+
+
+static void cs46xx_dsp_proc_modules_read (struct snd_info_entry *entry,
+					  struct snd_info_buffer *buffer)
+{
+	struct snd_cs46xx *chip = entry->private_data;
+	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
+	int i,j;
+
+	mutex_lock(&chip->spos_mutex);
+	snd_iprintf(buffer, "MODULES:\n");
+	for ( i = 0; i < ins->nmodules; ++i ) {
+		snd_iprintf(buffer, "\n%s:\n", ins->modules[i].module_name);
+		snd_iprintf(buffer, "   %d symbols\n", ins->modules[i].symbol_table.nsymbols);
+		snd_iprintf(buffer, "   %d fixups\n", ins->modules[i].nfixups);
+
+		for (j = 0; j < ins->modules[i].nsegments; ++ j) {
+			struct dsp_segment_desc * desc = (ins->modules[i].segments + j);
+			snd_iprintf(buffer, "   segment %02x offset %08x size %08x\n",
+				    desc->segment_type,desc->offset, desc->size);
+		}
+	}
+	mutex_unlock(&chip->spos_mutex);
+}
+
+static void cs46xx_dsp_proc_task_tree_read (struct snd_info_entry *entry,
+					    struct snd_info_buffer *buffer)
+{
+	struct snd_cs46xx *chip = entry->private_data;
+	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
+	int i, j, col;
+	void __iomem *dst = chip->region.idx[1].remap_addr + DSP_PARAMETER_BYTE_OFFSET;
+
+	mutex_lock(&chip->spos_mutex);
+	snd_iprintf(buffer, "TASK TREES:\n");
+	for ( i = 0; i < ins->ntask; ++i) {
+		snd_iprintf(buffer,"\n%04x %s:\n",ins->tasks[i].address,ins->tasks[i].task_name);
+
+		for (col = 0,j = 0;j < ins->tasks[i].size; j++,col++) {
+			u32 val;
+			if (col == 4) {
+				snd_iprintf(buffer,"\n");
+				col = 0;
+			}
+			val = readl(dst + (ins->tasks[i].address + j) * sizeof(u32));
+			snd_iprintf(buffer,"%08x ",val);
+		}
+	}
+
+	snd_iprintf(buffer,"\n");  
+	mutex_unlock(&chip->spos_mutex);
+}
+
+static void cs46xx_dsp_proc_scb_read (struct snd_info_entry *entry,
+				      struct snd_info_buffer *buffer)
+{
+	struct snd_cs46xx *chip = entry->private_data;
+	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
+	int i;
+
+	mutex_lock(&chip->spos_mutex);
+	snd_iprintf(buffer, "SCB's:\n");
+	for ( i = 0; i < ins->nscb; ++i) {
+		if (ins->scbs[i].deleted)
+			continue;
+		snd_iprintf(buffer,"\n%04x %s:\n\n",ins->scbs[i].address,ins->scbs[i].scb_name);
+
+		if (ins->scbs[i].parent_scb_ptr != NULL) {
+			snd_iprintf(buffer,"parent [%s:%04x] ", 
+				    ins->scbs[i].parent_scb_ptr->scb_name,
+				    ins->scbs[i].parent_scb_ptr->address);
+		} else snd_iprintf(buffer,"parent [none] ");
+
+		snd_iprintf(buffer,"sub_list_ptr [%s:%04x]\nnext_scb_ptr [%s:%04x]  task_entry [%s:%04x]\n",
+			    ins->scbs[i].sub_list_ptr->scb_name,
+			    ins->scbs[i].sub_list_ptr->address,
+			    ins->scbs[i].next_scb_ptr->scb_name,
+			    ins->scbs[i].next_scb_ptr->address,
+			    ins->scbs[i].task_entry->symbol_name,
+			    ins->scbs[i].task_entry->address);
+	}
+
+	snd_iprintf(buffer,"\n");
+	mutex_unlock(&chip->spos_mutex);
+}
+
+static void cs46xx_dsp_proc_parameter_dump_read (struct snd_info_entry *entry,
+						 struct snd_info_buffer *buffer)
+{
+	struct snd_cs46xx *chip = entry->private_data;
+	/*struct dsp_spos_instance * ins = chip->dsp_spos_instance; */
+	unsigned int i, col = 0;
+	void __iomem *dst = chip->region.idx[1].remap_addr + DSP_PARAMETER_BYTE_OFFSET;
+	struct dsp_symbol_entry * symbol; 
+
+	for (i = 0;i < DSP_PARAMETER_BYTE_SIZE; i += sizeof(u32),col ++) {
+		if (col == 4) {
+			snd_iprintf(buffer,"\n");
+			col = 0;
+		}
+
+		if ( (symbol = cs46xx_dsp_lookup_symbol_addr (chip,i / sizeof(u32), SYMBOL_PARAMETER)) != NULL) {
+			col = 0;
+			snd_iprintf (buffer,"\n%s:\n",symbol->symbol_name);
+		}
+
+		if (col == 0) {
+			snd_iprintf(buffer, "%04X ", i / (unsigned int)sizeof(u32));
+		}
+
+		snd_iprintf(buffer,"%08X ",readl(dst + i));
+	}
+}
+
+static void cs46xx_dsp_proc_sample_dump_read (struct snd_info_entry *entry,
+					      struct snd_info_buffer *buffer)
+{
+	struct snd_cs46xx *chip = entry->private_data;
+	int i,col = 0;
+	void __iomem *dst = chip->region.idx[2].remap_addr;
+
+	snd_iprintf(buffer,"PCMREADER:\n");
+	for (i = PCM_READER_BUF1;i < PCM_READER_BUF1 + 0x30; i += sizeof(u32),col ++) {
+		if (col == 4) {
+			snd_iprintf(buffer,"\n");
+			col = 0;
+		}
+
+		if (col == 0) {
+			snd_iprintf(buffer, "%04X ",i);
+		}
+
+		snd_iprintf(buffer,"%08X ",readl(dst + i));
+	}
+
+	snd_iprintf(buffer,"\nMIX_SAMPLE_BUF1:\n");
+
+	col = 0;
+	for (i = MIX_SAMPLE_BUF1;i < MIX_SAMPLE_BUF1 + 0x40; i += sizeof(u32),col ++) {
+		if (col == 4) {
+			snd_iprintf(buffer,"\n");
+			col = 0;
+		}
+
+		if (col == 0) {
+			snd_iprintf(buffer, "%04X ",i);
+		}
+
+		snd_iprintf(buffer,"%08X ",readl(dst + i));
+	}
+
+	snd_iprintf(buffer,"\nSRC_TASK_SCB1:\n");
+	col = 0;
+	for (i = 0x2480 ; i < 0x2480 + 0x40 ; i += sizeof(u32),col ++) {
+		if (col == 4) {
+			snd_iprintf(buffer,"\n");
+			col = 0;
+		}
+		
+		if (col == 0) {
+			snd_iprintf(buffer, "%04X ",i);
+		}
+
+		snd_iprintf(buffer,"%08X ",readl(dst + i));
+	}
+
+
+	snd_iprintf(buffer,"\nSPDIFO_BUFFER:\n");
+	col = 0;
+	for (i = SPDIFO_IP_OUTPUT_BUFFER1;i < SPDIFO_IP_OUTPUT_BUFFER1 + 0x30; i += sizeof(u32),col ++) {
+		if (col == 4) {
+			snd_iprintf(buffer,"\n");
+			col = 0;
+		}
+
+		if (col == 0) {
+			snd_iprintf(buffer, "%04X ",i);
+		}
+
+		snd_iprintf(buffer,"%08X ",readl(dst + i));
+	}
+
+	snd_iprintf(buffer,"\n...\n");
+	col = 0;
+
+	for (i = SPDIFO_IP_OUTPUT_BUFFER1+0xD0;i < SPDIFO_IP_OUTPUT_BUFFER1 + 0x110; i += sizeof(u32),col ++) {
+		if (col == 4) {
+			snd_iprintf(buffer,"\n");
+			col = 0;
+		}
+
+		if (col == 0) {
+			snd_iprintf(buffer, "%04X ",i);
+		}
+
+		snd_iprintf(buffer,"%08X ",readl(dst + i));
+	}
+
+
+	snd_iprintf(buffer,"\nOUTPUT_SNOOP:\n");
+	col = 0;
+	for (i = OUTPUT_SNOOP_BUFFER;i < OUTPUT_SNOOP_BUFFER + 0x40; i += sizeof(u32),col ++) {
+		if (col == 4) {
+			snd_iprintf(buffer,"\n");
+			col = 0;
+		}
+
+		if (col == 0) {
+			snd_iprintf(buffer, "%04X ",i);
+		}
+
+		snd_iprintf(buffer,"%08X ",readl(dst + i));
+	}
+
+	snd_iprintf(buffer,"\nCODEC_INPUT_BUF1: \n");
+	col = 0;
+	for (i = CODEC_INPUT_BUF1;i < CODEC_INPUT_BUF1 + 0x40; i += sizeof(u32),col ++) {
+		if (col == 4) {
+			snd_iprintf(buffer,"\n");
+			col = 0;
+		}
+
+		if (col == 0) {
+			snd_iprintf(buffer, "%04X ",i);
+		}
+
+		snd_iprintf(buffer,"%08X ",readl(dst + i));
+	}
+#if 0
+	snd_iprintf(buffer,"\nWRITE_BACK_BUF1: \n");
+	col = 0;
+	for (i = WRITE_BACK_BUF1;i < WRITE_BACK_BUF1 + 0x40; i += sizeof(u32),col ++) {
+		if (col == 4) {
+			snd_iprintf(buffer,"\n");
+			col = 0;
+		}
+
+		if (col == 0) {
+			snd_iprintf(buffer, "%04X ",i);
+		}
+
+		snd_iprintf(buffer,"%08X ",readl(dst + i));
+	}
+#endif
+
+	snd_iprintf(buffer,"\nSPDIFI_IP_OUTPUT_BUFFER1: \n");
+	col = 0;
+	for (i = SPDIFI_IP_OUTPUT_BUFFER1;i < SPDIFI_IP_OUTPUT_BUFFER1 + 0x80; i += sizeof(u32),col ++) {
+		if (col == 4) {
+			snd_iprintf(buffer,"\n");
+			col = 0;
+		}
+
+		if (col == 0) {
+			snd_iprintf(buffer, "%04X ",i);
+		}
+		
+		snd_iprintf(buffer,"%08X ",readl(dst + i));
+	}
+	snd_iprintf(buffer,"\n");
+}
+
+int cs46xx_dsp_proc_init (struct snd_card *card, struct snd_cs46xx *chip)
+{
+	struct snd_info_entry *entry;
+	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
+	int i;
+
+	ins->snd_card = card;
+
+	if ((entry = snd_info_create_card_entry(card, "dsp", card->proc_root)) != NULL) {
+		entry->content = SNDRV_INFO_CONTENT_TEXT;
+		entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
+		entry->c.text.read_size = 512;
+      
+		if (snd_info_register(entry) < 0) {
+			snd_info_free_entry(entry);
+			entry = NULL;
+		}
+	}
+
+	ins->proc_dsp_dir = entry;
+
+	if (!ins->proc_dsp_dir)
+		return -ENOMEM;
+
+	if ((entry = snd_info_create_card_entry(card, "spos_symbols", ins->proc_dsp_dir)) != NULL) {
+		entry->content = SNDRV_INFO_CONTENT_TEXT;
+		entry->private_data = chip;
+		entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
+		entry->c.text.read_size = 512;
+		entry->c.text.read = cs46xx_dsp_proc_symbol_table_read;
+		if (snd_info_register(entry) < 0) {
+			snd_info_free_entry(entry);
+			entry = NULL;
+		}
+	}
+	ins->proc_sym_info_entry = entry;
+    
+	if ((entry = snd_info_create_card_entry(card, "spos_modules", ins->proc_dsp_dir)) != NULL) {
+		entry->content = SNDRV_INFO_CONTENT_TEXT;
+		entry->private_data = chip;
+		entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
+		entry->c.text.read_size = 512;
+		entry->c.text.read = cs46xx_dsp_proc_modules_read;
+		if (snd_info_register(entry) < 0) {
+			snd_info_free_entry(entry);
+			entry = NULL;
+		}
+	}
+	ins->proc_modules_info_entry = entry;
+
+	if ((entry = snd_info_create_card_entry(card, "parameter", ins->proc_dsp_dir)) != NULL) {
+		entry->content = SNDRV_INFO_CONTENT_TEXT;
+		entry->private_data = chip;
+		entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
+		entry->c.text.read_size = 512;
+		entry->c.text.read = cs46xx_dsp_proc_parameter_dump_read;
+		if (snd_info_register(entry) < 0) {
+			snd_info_free_entry(entry);
+			entry = NULL;
+		}
+	}
+	ins->proc_parameter_dump_info_entry = entry;
+
+	if ((entry = snd_info_create_card_entry(card, "sample", ins->proc_dsp_dir)) != NULL) {
+		entry->content = SNDRV_INFO_CONTENT_TEXT;
+		entry->private_data = chip;
+		entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
+		entry->c.text.read_size = 512;
+		entry->c.text.read = cs46xx_dsp_proc_sample_dump_read;
+		if (snd_info_register(entry) < 0) {
+			snd_info_free_entry(entry);
+			entry = NULL;
+		}
+	}
+	ins->proc_sample_dump_info_entry = entry;
+
+	if ((entry = snd_info_create_card_entry(card, "task_tree", ins->proc_dsp_dir)) != NULL) {
+		entry->content = SNDRV_INFO_CONTENT_TEXT;
+		entry->private_data = chip;
+		entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
+		entry->c.text.read_size = 512;
+		entry->c.text.read = cs46xx_dsp_proc_task_tree_read;
+		if (snd_info_register(entry) < 0) {
+			snd_info_free_entry(entry);
+			entry = NULL;
+		}
+	}
+	ins->proc_task_info_entry = entry;
+
+	if ((entry = snd_info_create_card_entry(card, "scb_info", ins->proc_dsp_dir)) != NULL) {
+		entry->content = SNDRV_INFO_CONTENT_TEXT;
+		entry->private_data = chip;
+		entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
+		entry->c.text.read_size = 1024;
+		entry->c.text.read = cs46xx_dsp_proc_scb_read;
+		if (snd_info_register(entry) < 0) {
+			snd_info_free_entry(entry);
+			entry = NULL;
+		}
+	}
+	ins->proc_scb_info_entry = entry;
+
+	mutex_lock(&chip->spos_mutex);
+	/* register/update SCB's entries on proc */
+	for (i = 0; i < ins->nscb; ++i) {
+		if (ins->scbs[i].deleted) continue;
+
+		cs46xx_dsp_proc_register_scb_desc (chip, (ins->scbs + i));
+	}
+	mutex_unlock(&chip->spos_mutex);
+
+	return 0;
+}
+
+int cs46xx_dsp_proc_done (struct snd_cs46xx *chip)
+{
+	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
+	int i;
+
+	if (ins->proc_sym_info_entry) {
+		snd_info_unregister(ins->proc_sym_info_entry);
+		ins->proc_sym_info_entry = NULL;
+	}
+  
+	if (ins->proc_modules_info_entry) {
+		snd_info_unregister(ins->proc_modules_info_entry);
+		ins->proc_modules_info_entry = NULL;
+	}
+ 
+	if (ins->proc_parameter_dump_info_entry) {
+		snd_info_unregister(ins->proc_parameter_dump_info_entry);
+		ins->proc_parameter_dump_info_entry = NULL;
+	}
+  
+	if (ins->proc_sample_dump_info_entry) {
+		snd_info_unregister(ins->proc_sample_dump_info_entry);
+		ins->proc_sample_dump_info_entry = NULL;
+	}
+  
+	if (ins->proc_scb_info_entry) {
+		snd_info_unregister(ins->proc_scb_info_entry);
+		ins->proc_scb_info_entry = NULL;
+	}
+  
+	if (ins->proc_task_info_entry) {
+		snd_info_unregister(ins->proc_task_info_entry);
+		ins->proc_task_info_entry = NULL;
+	}
+
+	mutex_lock(&chip->spos_mutex);
+	for (i = 0; i < ins->nscb; ++i) {
+		if (ins->scbs[i].deleted) continue;
+		cs46xx_dsp_proc_free_scb_desc ( (ins->scbs + i) );
+	}
+	mutex_unlock(&chip->spos_mutex);
+
+	if (ins->proc_dsp_dir) {
+		snd_info_unregister (ins->proc_dsp_dir);
+		ins->proc_dsp_dir = NULL;
+	}
+
+	return 0;
+}
+#endif /* CONFIG_PROC_FS */
+
+static int debug_tree;
+static void _dsp_create_task_tree (struct snd_cs46xx *chip, u32 * task_data,
+				   u32  dest, int size)
+{
+	void __iomem *spdst = chip->region.idx[1].remap_addr + 
+		DSP_PARAMETER_BYTE_OFFSET + dest * sizeof(u32);
+	int i;
+
+	for (i = 0; i < size; ++i) {
+		if (debug_tree) printk ("addr %p, val %08x\n",spdst,task_data[i]);
+		writel(task_data[i],spdst);
+		spdst += sizeof(u32);
+	}
+}
+
+static int debug_scb;
+static void _dsp_create_scb (struct snd_cs46xx *chip, u32 * scb_data, u32 dest)
+{
+	void __iomem *spdst = chip->region.idx[1].remap_addr + 
+		DSP_PARAMETER_BYTE_OFFSET + dest * sizeof(u32);
+	int i;
+
+	for (i = 0; i < 0x10; ++i) {
+		if (debug_scb) printk ("addr %p, val %08x\n",spdst,scb_data[i]);
+		writel(scb_data[i],spdst);
+		spdst += sizeof(u32);
+	}
+}
+
+static int find_free_scb_index (struct dsp_spos_instance * ins)
+{
+	int index = ins->nscb, i;
+
+	for (i = ins->scb_highest_frag_index; i < ins->nscb; ++i) {
+		if (ins->scbs[i].deleted) {
+			index = i;
+			break;
+		}
+	}
+
+	return index;
+}
+
+static struct dsp_scb_descriptor * _map_scb (struct snd_cs46xx *chip, char * name, u32 dest)
+{
+	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
+	struct dsp_scb_descriptor * desc = NULL;
+	int index;
+
+	if (ins->nscb == DSP_MAX_SCB_DESC - 1) {
+		snd_printk(KERN_ERR "dsp_spos: got no place for other SCB\n");
+		return NULL;
+	}
+
+	index = find_free_scb_index (ins);
+
+	strcpy(ins->scbs[index].scb_name, name);
+	ins->scbs[index].address = dest;
+	ins->scbs[index].index = index;
+	ins->scbs[index].proc_info = NULL;
+	ins->scbs[index].ref_count = 1;
+	ins->scbs[index].deleted = 0;
+	spin_lock_init(&ins->scbs[index].lock);
+
+	desc = (ins->scbs + index);
+	ins->scbs[index].scb_symbol = add_symbol (chip, name, dest, SYMBOL_PARAMETER);
+
+	if (index > ins->scb_highest_frag_index)
+		ins->scb_highest_frag_index = index;
+
+	if (index == ins->nscb)
+		ins->nscb++;
+
+	return desc;
+}
+
+static struct dsp_task_descriptor *
+_map_task_tree (struct snd_cs46xx *chip, char * name, u32 dest, u32 size)
+{
+	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
+	struct dsp_task_descriptor * desc = NULL;
+
+	if (ins->ntask == DSP_MAX_TASK_DESC - 1) {
+		snd_printk(KERN_ERR "dsp_spos: got no place for other TASK\n");
+		return NULL;
+	}
+
+	strcpy(ins->tasks[ins->ntask].task_name,name);
+	ins->tasks[ins->ntask].address = dest;
+	ins->tasks[ins->ntask].size = size;
+
+	/* quick find in list */
+	ins->tasks[ins->ntask].index = ins->ntask;
+	desc = (ins->tasks + ins->ntask);
+	ins->ntask++;
+
+	add_symbol (chip,name,dest,SYMBOL_PARAMETER);
+	return desc;
+}
+
+struct dsp_scb_descriptor *
+cs46xx_dsp_create_scb (struct snd_cs46xx *chip, char * name, u32 * scb_data, u32 dest)
+{
+	struct dsp_scb_descriptor * desc;
+
+	desc = _map_scb (chip,name,dest);
+	if (desc) {
+		_dsp_create_scb(chip,scb_data,dest);
+	} else {
+		snd_printk(KERN_ERR "dsp_spos: failed to map SCB\n");
+	}
+
+	return desc;
+}
+
+
+static struct dsp_task_descriptor *
+cs46xx_dsp_create_task_tree (struct snd_cs46xx *chip, char * name, u32 * task_data,
+			     u32 dest, int size)
+{
+	struct dsp_task_descriptor * desc;
+
+	desc = _map_task_tree (chip,name,dest,size);
+	if (desc) {
+		_dsp_create_task_tree(chip,task_data,dest,size);
+	} else {
+		snd_printk(KERN_ERR "dsp_spos: failed to map TASK\n");
+	}
+
+	return desc;
+}
+
+int cs46xx_dsp_scb_and_task_init (struct snd_cs46xx *chip)
+{
+	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
+	struct dsp_symbol_entry * fg_task_tree_header_code;
+	struct dsp_symbol_entry * task_tree_header_code;
+	struct dsp_symbol_entry * task_tree_thread;
+	struct dsp_symbol_entry * null_algorithm;
+	struct dsp_symbol_entry * magic_snoop_task;
+
+	struct dsp_scb_descriptor * timing_master_scb;
+	struct dsp_scb_descriptor * codec_out_scb;
+	struct dsp_scb_descriptor * codec_in_scb;
+	struct dsp_scb_descriptor * src_task_scb;
+	struct dsp_scb_descriptor * master_mix_scb;
+	struct dsp_scb_descriptor * rear_mix_scb;
+	struct dsp_scb_descriptor * record_mix_scb;
+	struct dsp_scb_descriptor * write_back_scb;
+	struct dsp_scb_descriptor * vari_decimate_scb;
+	struct dsp_scb_descriptor * rear_codec_out_scb;
+	struct dsp_scb_descriptor * clfe_codec_out_scb;
+	struct dsp_scb_descriptor * magic_snoop_scb;
+	
+	int fifo_addr, fifo_span, valid_slots;
+
+	static struct dsp_spos_control_block sposcb = {
+		/* 0 */ HFG_TREE_SCB,HFG_STACK,
+		/* 1 */ SPOSCB_ADDR,BG_TREE_SCB_ADDR,
+		/* 2 */ DSP_SPOS_DC,0,
+		/* 3 */ DSP_SPOS_DC,DSP_SPOS_DC,
+		/* 4 */ 0,0,
+		/* 5 */ DSP_SPOS_UU,0,
+		/* 6 */ FG_TASK_HEADER_ADDR,0,
+		/* 7 */ 0,0,
+		/* 8 */ DSP_SPOS_UU,DSP_SPOS_DC,
+		/* 9 */ 0,
+		/* A */ 0,HFG_FIRST_EXECUTE_MODE,
+		/* B */ DSP_SPOS_UU,DSP_SPOS_UU,
+		/* C */ DSP_SPOS_DC_DC,
+		/* D */ DSP_SPOS_DC_DC,
+		/* E */ DSP_SPOS_DC_DC,
+		/* F */ DSP_SPOS_DC_DC
+	};
+
+	cs46xx_dsp_create_task_tree(chip, "sposCB", (u32 *)&sposcb, SPOSCB_ADDR, 0x10);
+
+	null_algorithm  = cs46xx_dsp_lookup_symbol(chip, "NULLALGORITHM", SYMBOL_CODE);
+	if (null_algorithm == NULL) {
+		snd_printk(KERN_ERR "dsp_spos: symbol NULLALGORITHM not found\n");
+		return -EIO;
+	}
+
+	fg_task_tree_header_code = cs46xx_dsp_lookup_symbol(chip, "FGTASKTREEHEADERCODE", SYMBOL_CODE);  
+	if (fg_task_tree_header_code == NULL) {
+		snd_printk(KERN_ERR "dsp_spos: symbol FGTASKTREEHEADERCODE not found\n");
+		return -EIO;
+	}
+
+	task_tree_header_code = cs46xx_dsp_lookup_symbol(chip, "TASKTREEHEADERCODE", SYMBOL_CODE);  
+	if (task_tree_header_code == NULL) {
+		snd_printk(KERN_ERR "dsp_spos: symbol TASKTREEHEADERCODE not found\n");
+		return -EIO;
+	}
+  
+	task_tree_thread = cs46xx_dsp_lookup_symbol(chip, "TASKTREETHREAD", SYMBOL_CODE);
+	if (task_tree_thread == NULL) {
+		snd_printk(KERN_ERR "dsp_spos: symbol TASKTREETHREAD not found\n");
+		return -EIO;
+	}
+
+	magic_snoop_task = cs46xx_dsp_lookup_symbol(chip, "MAGICSNOOPTASK", SYMBOL_CODE);
+	if (magic_snoop_task == NULL) {
+		snd_printk(KERN_ERR "dsp_spos: symbol MAGICSNOOPTASK not found\n");
+		return -EIO;
+	}
+  
+	{
+		/* create the null SCB */
+		static struct dsp_generic_scb null_scb = {
+			{ 0, 0, 0, 0 },
+			{ 0, 0, 0, 0, 0 },
+			NULL_SCB_ADDR, NULL_SCB_ADDR,
+			0, 0, 0, 0, 0,
+			{
+				0,0,
+				0,0,
+			}
+		};
+
+		null_scb.entry_point = null_algorithm->address;
+		ins->the_null_scb = cs46xx_dsp_create_scb(chip, "nullSCB", (u32 *)&null_scb, NULL_SCB_ADDR);
+		ins->the_null_scb->task_entry = null_algorithm;
+		ins->the_null_scb->sub_list_ptr = ins->the_null_scb;
+		ins->the_null_scb->next_scb_ptr = ins->the_null_scb;
+		ins->the_null_scb->parent_scb_ptr = NULL;
+		cs46xx_dsp_proc_register_scb_desc (chip,ins->the_null_scb);
+	}
+
+	{
+		/* setup foreground task tree */
+		static struct dsp_task_tree_control_block fg_task_tree_hdr =  {
+			{ FG_TASK_HEADER_ADDR | (DSP_SPOS_DC << 0x10),
+			  DSP_SPOS_DC_DC,
+			  DSP_SPOS_DC_DC,
+			  0x0000,DSP_SPOS_DC,
+			  DSP_SPOS_DC, DSP_SPOS_DC,
+			  DSP_SPOS_DC_DC,
+			  DSP_SPOS_DC_DC,
+			  DSP_SPOS_DC_DC,
+			  DSP_SPOS_DC,DSP_SPOS_DC },
+    
+			{
+				BG_TREE_SCB_ADDR,TIMINGMASTER_SCB_ADDR, 
+				0,
+				FG_TASK_HEADER_ADDR + TCBData,                  
+			},
+
+			{    
+				4,0,
+				1,0,
+				2,SPOSCB_ADDR + HFGFlags,
+				0,0,
+				FG_TASK_HEADER_ADDR + TCBContextBlk,FG_STACK
+			},
+
+			{
+				DSP_SPOS_DC,0,
+				DSP_SPOS_DC,DSP_SPOS_DC,
+				DSP_SPOS_DC,DSP_SPOS_DC,
+				DSP_SPOS_DC,DSP_SPOS_DC,
+				DSP_SPOS_DC,DSP_SPOS_DC,
+				DSP_SPOS_DCDC,
+				DSP_SPOS_UU,1,
+				DSP_SPOS_DCDC,
+				DSP_SPOS_DCDC,
+				DSP_SPOS_DCDC,
+				DSP_SPOS_DCDC,
+				DSP_SPOS_DCDC,
+				DSP_SPOS_DCDC,
+				DSP_SPOS_DCDC,
+				DSP_SPOS_DCDC,
+				DSP_SPOS_DCDC,
+				DSP_SPOS_DCDC,
+				DSP_SPOS_DCDC,
+				DSP_SPOS_DCDC,
+				DSP_SPOS_DCDC,
+				DSP_SPOS_DCDC,
+				DSP_SPOS_DCDC,
+				DSP_SPOS_DCDC,
+				DSP_SPOS_DCDC,
+				DSP_SPOS_DCDC,
+				DSP_SPOS_DCDC,
+				DSP_SPOS_DCDC,
+				DSP_SPOS_DCDC,
+				DSP_SPOS_DCDC,
+				DSP_SPOS_DCDC,
+				DSP_SPOS_DCDC,
+				DSP_SPOS_DCDC,
+				DSP_SPOS_DCDC,
+				DSP_SPOS_DCDC,
+				DSP_SPOS_DCDC 
+			},                                               
+			{ 
+				FG_INTERVAL_TIMER_PERIOD,DSP_SPOS_UU,
+				0,0
+			}
+		};
+
+		fg_task_tree_hdr.links.entry_point = fg_task_tree_header_code->address;
+		fg_task_tree_hdr.context_blk.stack0 = task_tree_thread->address;
+		cs46xx_dsp_create_task_tree(chip,"FGtaskTreeHdr",(u32 *)&fg_task_tree_hdr,FG_TASK_HEADER_ADDR,0x35);
+	}
+
+
+	{
+		/* setup foreground task tree */
+		static struct dsp_task_tree_control_block bg_task_tree_hdr =  {
+			{ DSP_SPOS_DC_DC,
+			  DSP_SPOS_DC_DC,
+			  DSP_SPOS_DC_DC,
+			  DSP_SPOS_DC, DSP_SPOS_DC,
+			  DSP_SPOS_DC, DSP_SPOS_DC,
+			  DSP_SPOS_DC_DC,
+			  DSP_SPOS_DC_DC,
+			  DSP_SPOS_DC_DC,
+			  DSP_SPOS_DC,DSP_SPOS_DC },
+    
+			{
+				NULL_SCB_ADDR,NULL_SCB_ADDR,  /* Set up the background to do nothing */
+				0,
+				BG_TREE_SCB_ADDR + TCBData,
+			},
+
+			{    
+				9999,0,
+				0,1,
+				0,SPOSCB_ADDR + HFGFlags,
+				0,0,
+				BG_TREE_SCB_ADDR + TCBContextBlk,BG_STACK
+			},
+
+			{
+				DSP_SPOS_DC,0,
+				DSP_SPOS_DC,DSP_SPOS_DC,
+				DSP_SPOS_DC,DSP_SPOS_DC,
+				DSP_SPOS_DC,DSP_SPOS_DC,
+				DSP_SPOS_DC,DSP_SPOS_DC,
+				DSP_SPOS_DCDC,
+				DSP_SPOS_UU,1,
+				DSP_SPOS_DCDC,
+				DSP_SPOS_DCDC,
+				DSP_SPOS_DCDC,
+				DSP_SPOS_DCDC,
+				DSP_SPOS_DCDC,
+				DSP_SPOS_DCDC,
+				DSP_SPOS_DCDC,
+				DSP_SPOS_DCDC,
+				DSP_SPOS_DCDC,
+				DSP_SPOS_DCDC,
+				DSP_SPOS_DCDC,
+				DSP_SPOS_DCDC,
+				DSP_SPOS_DCDC,
+				DSP_SPOS_DCDC,
+				DSP_SPOS_DCDC,
+				DSP_SPOS_DCDC,
+				DSP_SPOS_DCDC,
+				DSP_SPOS_DCDC,
+				DSP_SPOS_DCDC,
+				DSP_SPOS_DCDC,
+				DSP_SPOS_DCDC,
+				DSP_SPOS_DCDC,
+				DSP_SPOS_DCDC,
+				DSP_SPOS_DCDC,
+				DSP_SPOS_DCDC,
+				DSP_SPOS_DCDC,
+				DSP_SPOS_DCDC,
+				DSP_SPOS_DCDC 
+			},                                               
+			{ 
+				BG_INTERVAL_TIMER_PERIOD,DSP_SPOS_UU,
+				0,0
+			}
+		};
+
+		bg_task_tree_hdr.links.entry_point = task_tree_header_code->address;
+		bg_task_tree_hdr.context_blk.stack0 = task_tree_thread->address;
+		cs46xx_dsp_create_task_tree(chip,"BGtaskTreeHdr",(u32 *)&bg_task_tree_hdr,BG_TREE_SCB_ADDR,0x35);
+	}
+
+	/* create timing master SCB */
+	timing_master_scb = cs46xx_dsp_create_timing_master_scb(chip);
+
+	/* create the CODEC output task */
+	codec_out_scb = cs46xx_dsp_create_codec_out_scb(chip,"CodecOutSCB_I",0x0010,0x0000,
+							MASTERMIX_SCB_ADDR,
+							CODECOUT_SCB_ADDR,timing_master_scb,
+							SCB_ON_PARENT_SUBLIST_SCB);
+
+	if (!codec_out_scb) goto _fail_end;
+	/* create the master mix SCB */
+	master_mix_scb = cs46xx_dsp_create_mix_only_scb(chip,"MasterMixSCB",
+							MIX_SAMPLE_BUF1,MASTERMIX_SCB_ADDR,
+							codec_out_scb,
+							SCB_ON_PARENT_SUBLIST_SCB);
+	ins->master_mix_scb = master_mix_scb;
+
+	if (!master_mix_scb) goto _fail_end;
+
+	/* create codec in */
+	codec_in_scb = cs46xx_dsp_create_codec_in_scb(chip,"CodecInSCB",0x0010,0x00A0,
+						      CODEC_INPUT_BUF1,
+						      CODECIN_SCB_ADDR,codec_out_scb,
+						      SCB_ON_PARENT_NEXT_SCB);
+	if (!codec_in_scb) goto _fail_end;
+	ins->codec_in_scb = codec_in_scb;
+
+	/* create write back scb */
+	write_back_scb = cs46xx_dsp_create_mix_to_ostream_scb(chip,"WriteBackSCB",
+							      WRITE_BACK_BUF1,WRITE_BACK_SPB,
+							      WRITEBACK_SCB_ADDR,
+							      timing_master_scb,
+							      SCB_ON_PARENT_NEXT_SCB);
+	if (!write_back_scb) goto _fail_end;
+
+	{
+		static struct dsp_mix2_ostream_spb mix2_ostream_spb = {
+			0x00020000,
+			0x0000ffff
+		};
+    
+		/* dirty hack ... */
+		_dsp_create_task_tree (chip,(u32 *)&mix2_ostream_spb,WRITE_BACK_SPB,2);
+	}
+
+	/* input sample converter */
+	vari_decimate_scb = cs46xx_dsp_create_vari_decimate_scb(chip,"VariDecimateSCB",
+								VARI_DECIMATE_BUF0,
+								VARI_DECIMATE_BUF1,
+								VARIDECIMATE_SCB_ADDR,
+								write_back_scb,
+								SCB_ON_PARENT_SUBLIST_SCB);
+	if (!vari_decimate_scb) goto _fail_end;
+
+	/* create the record mixer SCB */
+	record_mix_scb = cs46xx_dsp_create_mix_only_scb(chip,"RecordMixerSCB",
+							MIX_SAMPLE_BUF2,
+							RECORD_MIXER_SCB_ADDR,
+							vari_decimate_scb,
+							SCB_ON_PARENT_SUBLIST_SCB);
+	ins->record_mixer_scb = record_mix_scb;
+
+	if (!record_mix_scb) goto _fail_end;
+
+	valid_slots = snd_cs46xx_peekBA0(chip, BA0_ACOSV);
+
+	snd_assert (chip->nr_ac97_codecs == 1 || chip->nr_ac97_codecs == 2, return -ENXIO);
+
+	if (chip->nr_ac97_codecs == 1) {
+		/* output on slot 5 and 11 
+		   on primary CODEC */
+		fifo_addr = 0x20;
+		fifo_span = 0x60;
+
+		/* enable slot 5 and 11 */
+		valid_slots |= ACOSV_SLV5 | ACOSV_SLV11;
+	} else {
+		/* output on slot 7 and 8 
+		   on secondary CODEC */
+		fifo_addr = 0x40;
+		fifo_span = 0x10;
+
+		/* enable slot 7 and 8 */
+		valid_slots |= ACOSV_SLV7 | ACOSV_SLV8;
+	}
+	/* create CODEC tasklet for rear speakers output*/
+	rear_codec_out_scb = cs46xx_dsp_create_codec_out_scb(chip,"CodecOutSCB_Rear",fifo_span,fifo_addr,
+							     REAR_MIXER_SCB_ADDR,
+							     REAR_CODECOUT_SCB_ADDR,codec_in_scb,
+							     SCB_ON_PARENT_NEXT_SCB);
+	if (!rear_codec_out_scb) goto _fail_end;
+	
+	
+	/* create the rear PCM channel  mixer SCB */
+	rear_mix_scb = cs46xx_dsp_create_mix_only_scb(chip,"RearMixerSCB",
+						      MIX_SAMPLE_BUF3,
+						      REAR_MIXER_SCB_ADDR,
+						      rear_codec_out_scb,
+						      SCB_ON_PARENT_SUBLIST_SCB);
+	ins->rear_mix_scb = rear_mix_scb;
+	if (!rear_mix_scb) goto _fail_end;
+	
+	if (chip->nr_ac97_codecs == 2) {
+		/* create CODEC tasklet for rear Center/LFE output 
+		   slot 6 and 9 on seconadry CODEC */
+		clfe_codec_out_scb = cs46xx_dsp_create_codec_out_scb(chip,"CodecOutSCB_CLFE",0x0030,0x0030,
+								     CLFE_MIXER_SCB_ADDR,
+								     CLFE_CODEC_SCB_ADDR,
+								     rear_codec_out_scb,
+								     SCB_ON_PARENT_NEXT_SCB);
+		if (!clfe_codec_out_scb) goto _fail_end;
+		
+		
+		/* create the rear PCM channel  mixer SCB */
+		ins->center_lfe_mix_scb = cs46xx_dsp_create_mix_only_scb(chip,"CLFEMixerSCB",
+									 MIX_SAMPLE_BUF4,
+									 CLFE_MIXER_SCB_ADDR,
+									 clfe_codec_out_scb,
+									 SCB_ON_PARENT_SUBLIST_SCB);
+		if (!ins->center_lfe_mix_scb) goto _fail_end;
+
+		/* enable slot 6 and 9 */
+		valid_slots |= ACOSV_SLV6 | ACOSV_SLV9;
+	} else {
+		clfe_codec_out_scb = rear_codec_out_scb;
+		ins->center_lfe_mix_scb = rear_mix_scb;
+	}
+
+	/* enable slots depending on CODEC configuration */
+	snd_cs46xx_pokeBA0(chip, BA0_ACOSV, valid_slots);
+
+	/* the magic snooper */
+	magic_snoop_scb = cs46xx_dsp_create_magic_snoop_scb (chip,"MagicSnoopSCB_I",OUTPUTSNOOP_SCB_ADDR,
+							     OUTPUT_SNOOP_BUFFER,
+							     codec_out_scb,
+							     clfe_codec_out_scb,
+							     SCB_ON_PARENT_NEXT_SCB);
+
+    
+	if (!magic_snoop_scb) goto _fail_end;
+	ins->ref_snoop_scb = magic_snoop_scb;
+
+	/* SP IO access */
+	if (!cs46xx_dsp_create_spio_write_scb(chip,"SPIOWriteSCB",SPIOWRITE_SCB_ADDR,
+					      magic_snoop_scb,
+					      SCB_ON_PARENT_NEXT_SCB))
+		goto _fail_end;
+
+	/* SPDIF input sampel rate converter */
+	src_task_scb = cs46xx_dsp_create_src_task_scb(chip,"SrcTaskSCB_SPDIFI",
+						      ins->spdif_in_sample_rate,
+						      SRC_OUTPUT_BUF1,
+						      SRC_DELAY_BUF1,SRCTASK_SCB_ADDR,
+						      master_mix_scb,
+						      SCB_ON_PARENT_SUBLIST_SCB,1);
+
+	if (!src_task_scb) goto _fail_end;
+	cs46xx_src_unlink(chip,src_task_scb);
+
+	/* NOTE: when we now how to detect the SPDIF input
+	   sample rate we will use this SRC to adjust it */
+	ins->spdif_in_src = src_task_scb;
+
+	cs46xx_dsp_async_init(chip,timing_master_scb);
+	return 0;
+
+ _fail_end:
+	snd_printk(KERN_ERR "dsp_spos: failed to setup SCB's in DSP\n");
+	return -EINVAL;
+}
+
+static int cs46xx_dsp_async_init (struct snd_cs46xx *chip,
+				  struct dsp_scb_descriptor * fg_entry)
+{
+	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
+	struct dsp_symbol_entry * s16_async_codec_input_task;
+	struct dsp_symbol_entry * spdifo_task;
+	struct dsp_symbol_entry * spdifi_task;
+	struct dsp_scb_descriptor * spdifi_scb_desc, * spdifo_scb_desc, * async_codec_scb_desc;
+
+	s16_async_codec_input_task = cs46xx_dsp_lookup_symbol(chip, "S16_ASYNCCODECINPUTTASK", SYMBOL_CODE);
+	if (s16_async_codec_input_task == NULL) {
+		snd_printk(KERN_ERR "dsp_spos: symbol S16_ASYNCCODECINPUTTASK not found\n");
+		return -EIO;
+	}
+	spdifo_task = cs46xx_dsp_lookup_symbol(chip, "SPDIFOTASK", SYMBOL_CODE);
+	if (spdifo_task == NULL) {
+		snd_printk(KERN_ERR "dsp_spos: symbol SPDIFOTASK not found\n");
+		return -EIO;
+	}
+
+	spdifi_task = cs46xx_dsp_lookup_symbol(chip, "SPDIFITASK", SYMBOL_CODE);
+	if (spdifi_task == NULL) {
+		snd_printk(KERN_ERR "dsp_spos: symbol SPDIFITASK not found\n");
+		return -EIO;
+	}
+
+	{
+		/* 0xBC0 */
+		struct dsp_spdifoscb spdifo_scb = {
+			/* 0 */ DSP_SPOS_UUUU,
+			{
+				/* 1 */ 0xb0, 
+				/* 2 */ 0, 
+				/* 3 */ 0, 
+				/* 4 */ 0, 
+			},
+			/* NOTE: the SPDIF output task read samples in mono
+			   format, the AsynchFGTxSCB task writes to buffer
+			   in stereo format
+			*/
+			/* 5 */ RSCONFIG_SAMPLE_16MONO + RSCONFIG_MODULO_256,
+			/* 6 */ ( SPDIFO_IP_OUTPUT_BUFFER1 << 0x10 )  |  0xFFFC,
+			/* 7 */ 0,0, 
+			/* 8 */ 0, 
+			/* 9 */ FG_TASK_HEADER_ADDR, NULL_SCB_ADDR, 
+			/* A */ /*spdifo_task->address*/0,
+			SPDIFO_SCB_INST + SPDIFOFIFOPointer,
+			{
+				/* B */ 0x0040, /*DSP_SPOS_UUUU,*/
+				/* C */ 0x20ff, /*DSP_SPOS_UUUU,*/
+			},
+			/* D */ 0x804c,0,							  /* SPDIFOFIFOPointer:SPDIFOStatRegAddr; */
+			/* E */ 0x0108,0x0001,					  /* SPDIFOStMoFormat:SPDIFOFIFOBaseAddr; */
+			/* F */ DSP_SPOS_UUUU	  			          /* SPDIFOFree; */
+                };
+
+		/* 0xBB0 */
+		struct dsp_spdifiscb spdifi_scb = {
+			/* 0 */ DSP_SPOS_UULO,DSP_SPOS_UUHI,
+			/* 1 */ 0,
+			/* 2 */ 0,
+			/* 3 */ 1,4000,        /* SPDIFICountLimit SPDIFICount */ 
+			/* 4 */ DSP_SPOS_UUUU, /* SPDIFIStatusData */
+			/* 5 */ 0,DSP_SPOS_UUHI, /* StatusData, Free4 */
+			/* 6 */ DSP_SPOS_UUUU,  /* Free3 */
+			/* 7 */ DSP_SPOS_UU,DSP_SPOS_DC,  /* Free2 BitCount*/
+			/* 8 */ DSP_SPOS_UUUU,	/* TempStatus */
+			/* 9 */ SPDIFO_SCB_INST, NULL_SCB_ADDR,
+			/* A */ /*spdifi_task->address*/0,
+			SPDIFI_SCB_INST + SPDIFIFIFOPointer,
+			/* NOTE: The SPDIF input task write the sample in mono
+			   format from the HW FIFO, the AsynchFGRxSCB task  reads 
+			   them in stereo 
+			*/
+			/* B */ RSCONFIG_SAMPLE_16MONO + RSCONFIG_MODULO_128,
+			/* C */ (SPDIFI_IP_OUTPUT_BUFFER1 << 0x10) | 0xFFFC,
+			/* D */ 0x8048,0,
+			/* E */ 0x01f0,0x0001,
+			/* F */ DSP_SPOS_UUUU /* SPDIN_STATUS monitor */
+                };
+
+		/* 0xBA0 */
+		struct dsp_async_codec_input_scb async_codec_input_scb = {
+			/* 0 */ DSP_SPOS_UUUU,
+			/* 1 */ 0,
+			/* 2 */ 0,
+			/* 3 */ 1,4000,
+			/* 4 */ 0x0118,0x0001,
+			/* 5 */ RSCONFIG_SAMPLE_16MONO + RSCONFIG_MODULO_64,
+			/* 6 */ (ASYNC_IP_OUTPUT_BUFFER1 << 0x10) | 0xFFFC,
+			/* 7 */ DSP_SPOS_UU,0x3,
+			/* 8 */ DSP_SPOS_UUUU,
+			/* 9 */ SPDIFI_SCB_INST,NULL_SCB_ADDR,
+			/* A */ /*s16_async_codec_input_task->address*/0,
+			HFG_TREE_SCB + AsyncCIOFIFOPointer,
+              
+			/* B */ RSCONFIG_SAMPLE_16STEREO + RSCONFIG_MODULO_64,
+			/* C */ (ASYNC_IP_OUTPUT_BUFFER1 << 0x10),  /*(ASYNC_IP_OUTPUT_BUFFER1 << 0x10) | 0xFFFC,*/
+      
+#ifdef UseASER1Input
+			/* short AsyncCIFIFOPointer:AsyncCIStatRegAddr;	       
+			   Init. 0000:8042: for ASER1
+			   0000:8044: for ASER2 */
+			/* D */ 0x8042,0,
+      
+			/* short AsyncCIStMoFormat:AsyncCIFIFOBaseAddr;
+			   Init 1 stero:8050 ASER1
+			   Init 0  mono:8070 ASER2
+			   Init 1 Stereo : 0100 ASER1 (Set by script) */
+			/* E */ 0x0100,0x0001,
+      
+#endif
+      
+#ifdef UseASER2Input
+			/* short AsyncCIFIFOPointer:AsyncCIStatRegAddr;
+			   Init. 0000:8042: for ASER1
+			   0000:8044: for ASER2 */
+			/* D */ 0x8044,0,
+      
+			/* short AsyncCIStMoFormat:AsyncCIFIFOBaseAddr;
+			   Init 1 stero:8050 ASER1
+			   Init 0  mono:8070 ASER2
+			   Init 1 Stereo : 0100 ASER1 (Set by script) */
+			/* E */ 0x0110,0x0001,
+      
+#endif
+      
+			/* short AsyncCIOutputBufModulo:AsyncCIFree;
+			   AsyncCIOutputBufModulo: The modulo size for   
+			   the output buffer of this task */
+			/* F */ 0, /* DSP_SPOS_UUUU */
+                };
+
+                spdifo_scb.entry_point = spdifo_task->address;
+                spdifi_scb.entry_point = spdifi_task->address;
+                async_codec_input_scb.io_entry_point =
+                    s16_async_codec_input_task->address;
+
+		spdifo_scb_desc = cs46xx_dsp_create_scb(chip,"SPDIFOSCB",(u32 *)&spdifo_scb,SPDIFO_SCB_INST);
+
+		snd_assert(spdifo_scb_desc, return -EIO);
+		spdifi_scb_desc = cs46xx_dsp_create_scb(chip,"SPDIFISCB",(u32 *)&spdifi_scb,SPDIFI_SCB_INST);
+		snd_assert(spdifi_scb_desc, return -EIO);
+		async_codec_scb_desc = cs46xx_dsp_create_scb(chip,"AsynCodecInputSCB",(u32 *)&async_codec_input_scb, HFG_TREE_SCB);
+		snd_assert(async_codec_scb_desc, return -EIO);
+
+		async_codec_scb_desc->parent_scb_ptr = NULL;
+		async_codec_scb_desc->next_scb_ptr = spdifi_scb_desc;
+		async_codec_scb_desc->sub_list_ptr = ins->the_null_scb;
+		async_codec_scb_desc->task_entry = s16_async_codec_input_task;
+
+		spdifi_scb_desc->parent_scb_ptr = async_codec_scb_desc;
+		spdifi_scb_desc->next_scb_ptr = spdifo_scb_desc;
+		spdifi_scb_desc->sub_list_ptr = ins->the_null_scb;
+		spdifi_scb_desc->task_entry = spdifi_task;
+
+		spdifo_scb_desc->parent_scb_ptr = spdifi_scb_desc;
+		spdifo_scb_desc->next_scb_ptr = fg_entry;
+		spdifo_scb_desc->sub_list_ptr = ins->the_null_scb;
+		spdifo_scb_desc->task_entry = spdifo_task;
+
+		/* this one is faked, as the parnet of SPDIFO task
+		   is the FG task tree */
+		fg_entry->parent_scb_ptr = spdifo_scb_desc;
+
+		/* for proc fs */
+		cs46xx_dsp_proc_register_scb_desc (chip,spdifo_scb_desc);
+		cs46xx_dsp_proc_register_scb_desc (chip,spdifi_scb_desc);
+		cs46xx_dsp_proc_register_scb_desc (chip,async_codec_scb_desc);
+
+		/* Async MASTER ENABLE, affects both SPDIF input and output */
+		snd_cs46xx_pokeBA0(chip, BA0_ASER_MASTER, 0x1 );
+	}
+
+	return 0;
+}
+
+
+static void cs46xx_dsp_disable_spdif_hw (struct snd_cs46xx *chip)
+{
+	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
+
+	/* set SPDIF output FIFO slot */
+	snd_cs46xx_pokeBA0(chip, BA0_ASER_FADDR, 0);
+
+	/* SPDIF output MASTER ENABLE */
+	cs46xx_poke_via_dsp (chip,SP_SPDOUT_CONTROL, 0);
+
+	/* right and left validate bit */
+	/*cs46xx_poke_via_dsp (chip,SP_SPDOUT_CSUV, ins->spdif_csuv_default);*/
+	cs46xx_poke_via_dsp (chip,SP_SPDOUT_CSUV, 0x0);
+
+	/* clear fifo pointer */
+	cs46xx_poke_via_dsp (chip,SP_SPDIN_FIFOPTR, 0x0);
+
+	/* monitor state */
+	ins->spdif_status_out &= ~DSP_SPDIF_STATUS_HW_ENABLED;
+}
+
+int cs46xx_dsp_enable_spdif_hw (struct snd_cs46xx *chip)
+{
+	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
+
+	/* if hw-ctrl already enabled, turn off to reset logic ... */
+	cs46xx_dsp_disable_spdif_hw (chip);
+	udelay(50);
+
+	/* set SPDIF output FIFO slot */
+	snd_cs46xx_pokeBA0(chip, BA0_ASER_FADDR, ( 0x8000 | ((SP_SPDOUT_FIFO >> 4) << 4) ));
+
+	/* SPDIF output MASTER ENABLE */
+	cs46xx_poke_via_dsp (chip,SP_SPDOUT_CONTROL, 0x80000000);
+
+	/* right and left validate bit */
+	cs46xx_poke_via_dsp (chip,SP_SPDOUT_CSUV, ins->spdif_csuv_default);
+
+	/* monitor state */
+	ins->spdif_status_out |= DSP_SPDIF_STATUS_HW_ENABLED;
+
+	return 0;
+}
+
+int cs46xx_dsp_enable_spdif_in (struct snd_cs46xx *chip)
+{
+	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
+
+	/* turn on amplifier */
+	chip->active_ctrl(chip, 1);
+	chip->amplifier_ctrl(chip, 1);
+
+	snd_assert (ins->asynch_rx_scb == NULL,return -EINVAL);
+	snd_assert (ins->spdif_in_src != NULL,return -EINVAL);
+
+	mutex_lock(&chip->spos_mutex);
+
+	if ( ! (ins->spdif_status_out & DSP_SPDIF_STATUS_INPUT_CTRL_ENABLED) ) {
+		/* time countdown enable */
+		cs46xx_poke_via_dsp (chip,SP_ASER_COUNTDOWN, 0x80000005);
+		/* NOTE: 80000005 value is just magic. With all values
+		   that I've tested this one seem to give the best result.
+		   Got no explication why. (Benny) */
+
+		/* SPDIF input MASTER ENABLE */
+		cs46xx_poke_via_dsp (chip,SP_SPDIN_CONTROL, 0x800003ff);
+
+		ins->spdif_status_out |= DSP_SPDIF_STATUS_INPUT_CTRL_ENABLED;
+	}
+
+	/* create and start the asynchronous receiver SCB */
+	ins->asynch_rx_scb = cs46xx_dsp_create_asynch_fg_rx_scb(chip,"AsynchFGRxSCB",
+								ASYNCRX_SCB_ADDR,
+								SPDIFI_SCB_INST,
+								SPDIFI_IP_OUTPUT_BUFFER1,
+								ins->spdif_in_src,
+								SCB_ON_PARENT_SUBLIST_SCB);
+
+	spin_lock_irq(&chip->reg_lock);
+
+	/* reset SPDIF input sample buffer pointer */
+	/*snd_cs46xx_poke (chip, (SPDIFI_SCB_INST + 0x0c) << 2,
+	  (SPDIFI_IP_OUTPUT_BUFFER1 << 0x10) | 0xFFFC);*/
+
+	/* reset FIFO ptr */
+	/*cs46xx_poke_via_dsp (chip,SP_SPDIN_FIFOPTR, 0x0);*/
+	cs46xx_src_link(chip,ins->spdif_in_src);
+
+	/* unmute SRC volume */
+	cs46xx_dsp_scb_set_volume (chip,ins->spdif_in_src,0x7fff,0x7fff);
+
+	spin_unlock_irq(&chip->reg_lock);
+
+	/* set SPDIF input sample rate and unmute
+	   NOTE: only 48khz support for SPDIF input this time */
+	/* cs46xx_dsp_set_src_sample_rate(chip,ins->spdif_in_src,48000); */
+
+	/* monitor state */
+	ins->spdif_status_in = 1;
+	mutex_unlock(&chip->spos_mutex);
+
+	return 0;
+}
+
+int cs46xx_dsp_disable_spdif_in (struct snd_cs46xx *chip)
+{
+	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
+
+	snd_assert (ins->asynch_rx_scb != NULL, return -EINVAL);
+	snd_assert (ins->spdif_in_src != NULL,return -EINVAL);	
+
+	mutex_lock(&chip->spos_mutex);
+
+	/* Remove the asynchronous receiver SCB */
+	cs46xx_dsp_remove_scb (chip,ins->asynch_rx_scb);
+	ins->asynch_rx_scb = NULL;
+
+	cs46xx_src_unlink(chip,ins->spdif_in_src);
+
+	/* monitor state */
+	ins->spdif_status_in = 0;
+	mutex_unlock(&chip->spos_mutex);
+
+	/* restore amplifier */
+	chip->active_ctrl(chip, -1);
+	chip->amplifier_ctrl(chip, -1);
+
+	return 0;
+}
+
+int cs46xx_dsp_enable_pcm_capture (struct snd_cs46xx *chip)
+{
+	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
+
+	snd_assert (ins->pcm_input == NULL,return -EINVAL);
+	snd_assert (ins->ref_snoop_scb != NULL,return -EINVAL);
+
+	mutex_lock(&chip->spos_mutex);
+	ins->pcm_input = cs46xx_add_record_source(chip,ins->ref_snoop_scb,PCMSERIALIN_PCM_SCB_ADDR,
+                                                  "PCMSerialInput_Wave");
+	mutex_unlock(&chip->spos_mutex);
+
+	return 0;
+}
+
+int cs46xx_dsp_disable_pcm_capture (struct snd_cs46xx *chip)
+{
+	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
+
+	snd_assert (ins->pcm_input != NULL,return -EINVAL);
+
+	mutex_lock(&chip->spos_mutex);
+	cs46xx_dsp_remove_scb (chip,ins->pcm_input);
+	ins->pcm_input = NULL;
+	mutex_unlock(&chip->spos_mutex);
+
+	return 0;
+}
+
+int cs46xx_dsp_enable_adc_capture (struct snd_cs46xx *chip)
+{
+	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
+
+	snd_assert (ins->adc_input == NULL,return -EINVAL);
+	snd_assert (ins->codec_in_scb != NULL,return -EINVAL);
+
+	mutex_lock(&chip->spos_mutex);
+	ins->adc_input = cs46xx_add_record_source(chip,ins->codec_in_scb,PCMSERIALIN_SCB_ADDR,
+						  "PCMSerialInput_ADC");
+	mutex_unlock(&chip->spos_mutex);
+
+	return 0;
+}
+
+int cs46xx_dsp_disable_adc_capture (struct snd_cs46xx *chip)
+{
+	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
+
+	snd_assert (ins->adc_input != NULL,return -EINVAL);
+
+	mutex_lock(&chip->spos_mutex);
+	cs46xx_dsp_remove_scb (chip,ins->adc_input);
+	ins->adc_input = NULL;
+	mutex_unlock(&chip->spos_mutex);
+
+	return 0;
+}
+
+int cs46xx_poke_via_dsp (struct snd_cs46xx *chip, u32 address, u32 data)
+{
+	u32 temp;
+	int  i;
+
+	/* santiy check the parameters.  (These numbers are not 100% correct.  They are
+	   a rough guess from looking at the controller spec.) */
+	if (address < 0x8000 || address >= 0x9000)
+		return -EINVAL;
+        
+	/* initialize the SP_IO_WRITE SCB with the data. */
+	temp = ( address << 16 ) | ( address & 0x0000FFFF);   /* offset 0 <-- address2 : address1 */
+
+	snd_cs46xx_poke(chip,( SPIOWRITE_SCB_ADDR      << 2), temp);
+	snd_cs46xx_poke(chip,((SPIOWRITE_SCB_ADDR + 1) << 2), data); /* offset 1 <-- data1 */
+	snd_cs46xx_poke(chip,((SPIOWRITE_SCB_ADDR + 2) << 2), data); /* offset 1 <-- data2 */
+    
+	/* Poke this location to tell the task to start */
+	snd_cs46xx_poke(chip,((SPIOWRITE_SCB_ADDR + 6) << 2), SPIOWRITE_SCB_ADDR << 0x10);
+
+	/* Verify that the task ran */
+	for (i=0; i<25; i++) {
+		udelay(125);
+
+		temp =  snd_cs46xx_peek(chip,((SPIOWRITE_SCB_ADDR + 6) << 2));
+		if (temp == 0x00000000)
+			break;
+	}
+
+	if (i == 25) {
+		snd_printk(KERN_ERR "dsp_spos: SPIOWriteTask not responding\n");
+		return -EBUSY;
+	}
+
+	return 0;
+}
+
+int cs46xx_dsp_set_dac_volume (struct snd_cs46xx * chip, u16 left, u16 right)
+{
+	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
+	struct dsp_scb_descriptor * scb; 
+
+	mutex_lock(&chip->spos_mutex);
+	
+	/* main output */
+	scb = ins->master_mix_scb->sub_list_ptr;
+	while (scb != ins->the_null_scb) {
+		cs46xx_dsp_scb_set_volume (chip,scb,left,right);
+		scb = scb->next_scb_ptr;
+	}
+
+	/* rear output */
+	scb = ins->rear_mix_scb->sub_list_ptr;
+	while (scb != ins->the_null_scb) {
+		cs46xx_dsp_scb_set_volume (chip,scb,left,right);
+		scb = scb->next_scb_ptr;
+	}
+
+	ins->dac_volume_left = left;
+	ins->dac_volume_right = right;
+
+	mutex_unlock(&chip->spos_mutex);
+
+	return 0;
+}
+
+int cs46xx_dsp_set_iec958_volume (struct snd_cs46xx * chip, u16 left, u16 right)
+{
+	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
+
+	mutex_lock(&chip->spos_mutex);
+
+	if (ins->asynch_rx_scb != NULL)
+		cs46xx_dsp_scb_set_volume (chip,ins->asynch_rx_scb,
+					   left,right);
+
+	ins->spdif_input_volume_left = left;
+	ins->spdif_input_volume_right = right;
+
+	mutex_unlock(&chip->spos_mutex);
+
+	return 0;
+}
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/cs46xx/dsp_spos.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/cs46xx/dsp_spos.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/cs46xx/dsp_spos.h	(revision 91)
@@ -0,0 +1,227 @@
+/*
+ *  The driver for the Cirrus Logic's Sound Fusion CS46XX based soundcards
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+/*
+ * 2002-07 Benny Sjostrand benny@hostmobility.com
+ */
+
+#ifdef  CONFIG_SND_CS46XX_NEW_DSP /* hack ... */
+#ifndef __DSP_SPOS_H__
+#define __DSP_SPOS_H__
+
+#define DSP_MAX_SYMBOLS 1024
+#define DSP_MAX_MODULES 64
+
+#define DSP_CODE_BYTE_SIZE             0x00007000UL
+#define DSP_PARAMETER_BYTE_SIZE        0x00003000UL
+#define DSP_SAMPLE_BYTE_SIZE           0x00003800UL
+#define DSP_PARAMETER_BYTE_OFFSET      0x00000000UL
+#define DSP_SAMPLE_BYTE_OFFSET         0x00010000UL
+#define DSP_CODE_BYTE_OFFSET           0x00020000UL
+
+#define WIDE_INSTR_MASK       0x0040
+#define WIDE_LADD_INSTR_MASK  0x0380
+
+/* this instruction types
+   needs to be reallocated when load
+   code into DSP */
+enum wide_opcode {
+	WIDE_FOR_BEGIN_LOOP = 0x20,
+	WIDE_FOR_BEGIN_LOOP2,
+
+	WIDE_COND_GOTO_ADDR = 0x30,
+	WIDE_COND_GOTO_CALL,
+
+	WIDE_TBEQ_COND_GOTO_ADDR = 0x70,
+	WIDE_TBEQ_COND_CALL_ADDR,
+	WIDE_TBEQ_NCOND_GOTO_ADDR,
+	WIDE_TBEQ_NCOND_CALL_ADDR,
+	WIDE_TBEQ_COND_GOTO1_ADDR,
+	WIDE_TBEQ_COND_CALL1_ADDR,
+	WIDE_TBEQ_NCOND_GOTOI_ADDR,
+	WIDE_TBEQ_NCOND_CALL1_ADDR,
+};
+
+/* SAMPLE segment */
+#define VARI_DECIMATE_BUF1       0x0000
+#define WRITE_BACK_BUF1          0x0400
+#define CODEC_INPUT_BUF1         0x0500
+#define PCM_READER_BUF1          0x0600
+#define SRC_DELAY_BUF1           0x0680
+#define VARI_DECIMATE_BUF0       0x0780
+#define SRC_OUTPUT_BUF1          0x07A0
+#define ASYNC_IP_OUTPUT_BUFFER1  0x0A00
+#define OUTPUT_SNOOP_BUFFER      0x0B00
+#define SPDIFI_IP_OUTPUT_BUFFER1 0x0E00
+#define SPDIFO_IP_OUTPUT_BUFFER1 0x1000
+#define MIX_SAMPLE_BUF1          0x1400
+#define MIX_SAMPLE_BUF2          0x2E80
+#define MIX_SAMPLE_BUF3          0x2F00
+#define MIX_SAMPLE_BUF4          0x2F80
+#define MIX_SAMPLE_BUF5          0x3000
+
+/* Task stack address */
+#define HFG_STACK                0x066A
+#define FG_STACK                 0x066E
+#define BG_STACK                 0x068E
+
+/* SCB's addresses */
+#define SPOSCB_ADDR              0x070
+#define BG_TREE_SCB_ADDR         0x635
+#define NULL_SCB_ADDR            0x000
+#define TIMINGMASTER_SCB_ADDR    0x010
+#define CODECOUT_SCB_ADDR        0x020
+#define PCMREADER_SCB_ADDR       0x030
+#define WRITEBACK_SCB_ADDR       0x040
+#define CODECIN_SCB_ADDR         0x080
+#define MASTERMIX_SCB_ADDR       0x090
+#define SRCTASK_SCB_ADDR         0x0A0
+#define VARIDECIMATE_SCB_ADDR    0x0B0
+#define PCMSERIALIN_SCB_ADDR     0x0C0
+#define FG_TASK_HEADER_ADDR      0x600
+#define ASYNCTX_SCB_ADDR         0x0E0
+#define ASYNCRX_SCB_ADDR         0x0F0
+#define SRCTASKII_SCB_ADDR       0x100
+#define OUTPUTSNOOP_SCB_ADDR     0x110
+#define PCMSERIALINII_SCB_ADDR   0x120
+#define SPIOWRITE_SCB_ADDR       0x130
+#define REAR_CODECOUT_SCB_ADDR   0x140
+#define OUTPUTSNOOPII_SCB_ADDR   0x150
+#define PCMSERIALIN_PCM_SCB_ADDR 0x160
+#define RECORD_MIXER_SCB_ADDR    0x170
+#define REAR_MIXER_SCB_ADDR      0x180
+#define CLFE_MIXER_SCB_ADDR      0x190
+#define CLFE_CODEC_SCB_ADDR      0x1A0
+
+/* hyperforground SCB's*/
+#define HFG_TREE_SCB             0xBA0
+#define SPDIFI_SCB_INST          0xBB0
+#define SPDIFO_SCB_INST          0xBC0
+#define WRITE_BACK_SPB           0x0D0
+
+/* offsets */
+#define AsyncCIOFIFOPointer  0xd
+#define SPDIFOFIFOPointer    0xd
+#define SPDIFIFIFOPointer    0xd
+#define TCBData              0xb
+#define HFGFlags             0xa
+#define TCBContextBlk        0x10
+#define AFGTxAccumPhi        0x4
+#define SCBsubListPtr        0x9
+#define SCBfuncEntryPtr      0xA
+#define SRCCorPerGof         0x2
+#define SRCPhiIncr6Int26Frac 0xd
+#define SCBVolumeCtrl        0xe
+
+/* conf */
+#define UseASER1Input 1
+
+
+
+/*
+ * The following defines are for the flags in the rsConfig01/23 registers of
+ * the SP.
+ */
+
+#define RSCONFIG_MODULO_SIZE_MASK               0x0000000FL
+#define RSCONFIG_MODULO_16                      0x00000001L
+#define RSCONFIG_MODULO_32                      0x00000002L
+#define RSCONFIG_MODULO_64                      0x00000003L
+#define RSCONFIG_MODULO_128                     0x00000004L
+#define RSCONFIG_MODULO_256                     0x00000005L
+#define RSCONFIG_MODULO_512                     0x00000006L
+#define RSCONFIG_MODULO_1024                    0x00000007L
+#define RSCONFIG_MODULO_4                       0x00000008L
+#define RSCONFIG_MODULO_8                       0x00000009L
+#define RSCONFIG_SAMPLE_SIZE_MASK               0x000000C0L
+#define RSCONFIG_SAMPLE_8MONO                   0x00000000L
+#define RSCONFIG_SAMPLE_8STEREO                 0x00000040L
+#define RSCONFIG_SAMPLE_16MONO                  0x00000080L
+#define RSCONFIG_SAMPLE_16STEREO                0x000000C0L
+#define RSCONFIG_UNDERRUN_ZERO                  0x00004000L
+#define RSCONFIG_DMA_TO_HOST                    0x00008000L
+#define RSCONFIG_STREAM_NUM_MASK                0x00FF0000L
+#define RSCONFIG_MAX_DMA_SIZE_MASK              0x1F000000L
+#define RSCONFIG_DMA_ENABLE                     0x20000000L
+#define RSCONFIG_PRIORITY_MASK                  0xC0000000L
+#define RSCONFIG_PRIORITY_HIGH                  0x00000000L
+#define RSCONFIG_PRIORITY_MEDIUM_HIGH           0x40000000L
+#define RSCONFIG_PRIORITY_MEDIUM_LOW            0x80000000L
+#define RSCONFIG_PRIORITY_LOW                   0xC0000000L
+#define RSCONFIG_STREAM_NUM_SHIFT               16L
+#define RSCONFIG_MAX_DMA_SIZE_SHIFT             24L
+
+/* SP constants */
+#define FG_INTERVAL_TIMER_PERIOD                0x0051
+#define BG_INTERVAL_TIMER_PERIOD                0x0100
+
+
+/* Only SP accessible registers */
+#define SP_ASER_COUNTDOWN 0x8040
+#define SP_SPDOUT_FIFO    0x0108
+#define SP_SPDIN_MI_FIFO  0x01E0
+#define SP_SPDIN_D_FIFO   0x01F0
+#define SP_SPDIN_STATUS   0x8048
+#define SP_SPDIN_CONTROL  0x8049
+#define SP_SPDIN_FIFOPTR  0x804A
+#define SP_SPDOUT_STATUS  0x804C
+#define SP_SPDOUT_CONTROL 0x804D
+#define SP_SPDOUT_CSUV    0x808E
+
+static inline u8 _wrap_all_bits (u8 val)
+{
+	u8 wrapped;
+	
+	/* wrap all 8 bits */
+	wrapped = 
+		((val & 0x1 ) << 7) |
+		((val & 0x2 ) << 5) |
+		((val & 0x4 ) << 3) |
+		((val & 0x8 ) << 1) |
+		((val & 0x10) >> 1) |
+		((val & 0x20) >> 3) |
+		((val & 0x40) >> 5) |
+		((val & 0x80) >> 7);
+
+	return wrapped;
+}
+
+static inline void cs46xx_dsp_spos_update_scb (struct snd_cs46xx * chip,
+					       struct dsp_scb_descriptor * scb) 
+{
+	/* update nextSCB and subListPtr in SCB */
+	snd_cs46xx_poke(chip,
+			(scb->address + SCBsubListPtr) << 2,
+			(scb->sub_list_ptr->address << 0x10) |
+			(scb->next_scb_ptr->address));	
+}
+
+static inline void cs46xx_dsp_scb_set_volume (struct snd_cs46xx * chip,
+					      struct dsp_scb_descriptor * scb,
+					      u16 left, u16 right)
+{
+	unsigned int val = ((0xffff - left) << 16 | (0xffff - right));
+
+	snd_cs46xx_poke(chip, (scb->address + SCBVolumeCtrl) << 2, val);
+	snd_cs46xx_poke(chip, (scb->address + SCBVolumeCtrl + 1) << 2, val);
+}
+#endif /* __DSP_SPOS_H__ */
+#endif /* CONFIG_SND_CS46XX_NEW_DSP  */
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/cs46xx/dsp_spos_scb_lib.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/cs46xx/dsp_spos_scb_lib.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/cs46xx/dsp_spos_scb_lib.c	(revision 91)
@@ -0,0 +1,1782 @@
+/*
+ *
+ *   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
+ *
+ */
+
+/*
+ * 2002-07 Benny Sjostrand benny@hostmobility.com
+ */
+
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/pm.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/info.h>
+#include <sound/cs46xx.h>
+
+#include "cs46xx_lib.h"
+#include "dsp_spos.h"
+
+struct proc_scb_info {
+	struct dsp_scb_descriptor * scb_desc;
+	struct snd_cs46xx *chip;
+};
+
+static void remove_symbol (struct snd_cs46xx * chip, struct dsp_symbol_entry * symbol)
+{
+	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
+	int symbol_index = (int)(symbol - ins->symbol_table.symbols);
+
+	snd_assert(ins->symbol_table.nsymbols > 0,return);
+	snd_assert(symbol_index >= 0 && symbol_index < ins->symbol_table.nsymbols, return);
+
+	ins->symbol_table.symbols[symbol_index].deleted = 1;
+
+	if (symbol_index < ins->symbol_table.highest_frag_index) {
+		ins->symbol_table.highest_frag_index = symbol_index;
+	}
+  
+	if (symbol_index == ins->symbol_table.nsymbols - 1)
+		ins->symbol_table.nsymbols --;
+
+	if (ins->symbol_table.highest_frag_index > ins->symbol_table.nsymbols) {
+		ins->symbol_table.highest_frag_index = ins->symbol_table.nsymbols;
+	}
+
+}
+
+#ifdef CONFIG_PROC_FS
+static void cs46xx_dsp_proc_scb_info_read (struct snd_info_entry *entry,
+					   struct snd_info_buffer *buffer)
+{
+	struct proc_scb_info * scb_info  = entry->private_data;
+	struct dsp_scb_descriptor * scb = scb_info->scb_desc;
+	struct dsp_spos_instance * ins;
+	struct snd_cs46xx *chip = scb_info->chip;
+	int j,col;
+	void __iomem *dst = chip->region.idx[1].remap_addr + DSP_PARAMETER_BYTE_OFFSET;
+
+	ins = chip->dsp_spos_instance;
+
+	mutex_lock(&chip->spos_mutex);
+	snd_iprintf(buffer,"%04x %s:\n",scb->address,scb->scb_name);
+
+	for (col = 0,j = 0;j < 0x10; j++,col++) {
+		if (col == 4) {
+			snd_iprintf(buffer,"\n");
+			col = 0;
+		}
+		snd_iprintf(buffer,"%08x ",readl(dst + (scb->address + j) * sizeof(u32)));
+	}
+  
+	snd_iprintf(buffer,"\n");
+
+	if (scb->parent_scb_ptr != NULL) {
+		snd_iprintf(buffer,"parent [%s:%04x] ", 
+			    scb->parent_scb_ptr->scb_name,
+			    scb->parent_scb_ptr->address);
+	} else snd_iprintf(buffer,"parent [none] ");
+  
+	snd_iprintf(buffer,"sub_list_ptr [%s:%04x]\nnext_scb_ptr [%s:%04x]  task_entry [%s:%04x]\n",
+		    scb->sub_list_ptr->scb_name,
+		    scb->sub_list_ptr->address,
+		    scb->next_scb_ptr->scb_name,
+		    scb->next_scb_ptr->address,
+		    scb->task_entry->symbol_name,
+		    scb->task_entry->address);
+
+	snd_iprintf(buffer,"index [%d] ref_count [%d]\n",scb->index,scb->ref_count);  
+	mutex_unlock(&chip->spos_mutex);
+}
+#endif
+
+static void _dsp_unlink_scb (struct snd_cs46xx *chip, struct dsp_scb_descriptor * scb)
+{
+	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
+	unsigned long flags;
+
+	if ( scb->parent_scb_ptr ) {
+		/* unlink parent SCB */
+		snd_assert ((scb->parent_scb_ptr->sub_list_ptr == scb ||
+			     scb->parent_scb_ptr->next_scb_ptr == scb),return);
+  
+		if (scb->parent_scb_ptr->sub_list_ptr == scb) {
+
+			if (scb->next_scb_ptr == ins->the_null_scb) {
+				/* last and only node in parent sublist */
+				scb->parent_scb_ptr->sub_list_ptr = scb->sub_list_ptr;
+
+				if (scb->sub_list_ptr != ins->the_null_scb) {
+					scb->sub_list_ptr->parent_scb_ptr = scb->parent_scb_ptr;
+				}
+				scb->sub_list_ptr = ins->the_null_scb;
+			} else {
+				/* first node in parent sublist */
+				scb->parent_scb_ptr->sub_list_ptr = scb->next_scb_ptr;
+
+				if (scb->next_scb_ptr != ins->the_null_scb) {
+					/* update next node parent ptr. */
+					scb->next_scb_ptr->parent_scb_ptr = scb->parent_scb_ptr;
+				}
+				scb->next_scb_ptr = ins->the_null_scb;
+			}
+		} else {
+			/* snd_assert ( (scb->sub_list_ptr == ins->the_null_scb), return); */
+			scb->parent_scb_ptr->next_scb_ptr = scb->next_scb_ptr;
+
+			if (scb->next_scb_ptr != ins->the_null_scb) {
+				/* update next node parent ptr. */
+				scb->next_scb_ptr->parent_scb_ptr = scb->parent_scb_ptr;
+			}
+			scb->next_scb_ptr = ins->the_null_scb;
+		}
+
+		spin_lock_irqsave(&chip->reg_lock, flags);    
+
+		/* update parent first entry in DSP RAM */
+		cs46xx_dsp_spos_update_scb(chip,scb->parent_scb_ptr);
+
+		/* then update entry in DSP RAM */
+		cs46xx_dsp_spos_update_scb(chip,scb);
+
+		scb->parent_scb_ptr = NULL;
+		spin_unlock_irqrestore(&chip->reg_lock, flags);
+	}
+}
+
+static void _dsp_clear_sample_buffer (struct snd_cs46xx *chip, u32 sample_buffer_addr,
+				      int dword_count) 
+{
+	void __iomem *dst = chip->region.idx[2].remap_addr + sample_buffer_addr;
+	int i;
+  
+	for (i = 0; i < dword_count ; ++i ) {
+		writel(0, dst);
+		dst += 4;
+	}  
+}
+
+void cs46xx_dsp_remove_scb (struct snd_cs46xx *chip, struct dsp_scb_descriptor * scb)
+{
+	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
+
+	/* check integrety */
+	snd_assert ( (scb->index >= 0 && 
+		      scb->index < ins->nscb && 
+		      (ins->scbs + scb->index) == scb), return );
+
+#if 0
+	/* can't remove a SCB with childs before 
+	   removing childs first  */
+	snd_assert ( (scb->sub_list_ptr == ins->the_null_scb &&
+		      scb->next_scb_ptr == ins->the_null_scb),
+		     goto _end);
+#endif
+
+	spin_lock(&scb->lock);
+	_dsp_unlink_scb (chip,scb);
+	spin_unlock(&scb->lock);
+
+	cs46xx_dsp_proc_free_scb_desc(scb);
+	snd_assert (scb->scb_symbol != NULL, return );
+	remove_symbol (chip,scb->scb_symbol);
+
+	ins->scbs[scb->index].deleted = 1;
+
+	if (scb->index < ins->scb_highest_frag_index)
+		ins->scb_highest_frag_index = scb->index;
+
+	if (scb->index == ins->nscb - 1) {
+		ins->nscb --;
+	}
+
+	if (ins->scb_highest_frag_index > ins->nscb) {
+		ins->scb_highest_frag_index = ins->nscb;
+	}
+
+#if 0
+	/* !!!! THIS IS A PIECE OF SHIT MADE BY ME !!! */
+	for(i = scb->index + 1;i < ins->nscb; ++i) {
+		ins->scbs[i - 1].index = i - 1;
+	}
+#endif
+}
+
+
+#ifdef CONFIG_PROC_FS
+void cs46xx_dsp_proc_free_scb_desc (struct dsp_scb_descriptor * scb)
+{
+	if (scb->proc_info) {
+		struct proc_scb_info * scb_info = scb->proc_info->private_data;
+
+		snd_printdd("cs46xx_dsp_proc_free_scb_desc: freeing %s\n",scb->scb_name);
+
+		snd_info_unregister(scb->proc_info);
+		scb->proc_info = NULL;
+
+		snd_assert (scb_info != NULL, return);
+		kfree (scb_info);
+	}
+}
+
+void cs46xx_dsp_proc_register_scb_desc (struct snd_cs46xx *chip,
+					struct dsp_scb_descriptor * scb)
+{
+	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
+	struct snd_info_entry * entry;
+	struct proc_scb_info * scb_info;
+
+	/* register to proc */
+	if (ins->snd_card != NULL && ins->proc_dsp_dir != NULL &&
+	    scb->proc_info == NULL) {
+  
+		if ((entry = snd_info_create_card_entry(ins->snd_card, scb->scb_name, 
+							ins->proc_dsp_dir)) != NULL) {
+			scb_info = kmalloc(sizeof(struct proc_scb_info), GFP_KERNEL);
+			if (!scb_info) {
+				snd_info_free_entry(entry);
+				entry = NULL;
+				goto out;
+			}
+
+			scb_info->chip = chip;
+			scb_info->scb_desc = scb;
+      
+			entry->content = SNDRV_INFO_CONTENT_TEXT;
+			entry->private_data = scb_info;
+			entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
+      
+			entry->c.text.read_size = 512;
+			entry->c.text.read = cs46xx_dsp_proc_scb_info_read;
+      
+			if (snd_info_register(entry) < 0) {
+				snd_info_free_entry(entry);
+				kfree (scb_info);
+				entry = NULL;
+			}
+		}
+out:
+		scb->proc_info = entry;
+	}
+}
+#endif /* CONFIG_PROC_FS */
+
+static struct dsp_scb_descriptor * 
+_dsp_create_generic_scb (struct snd_cs46xx *chip, char * name, u32 * scb_data, u32 dest,
+                         struct dsp_symbol_entry * task_entry,
+                         struct dsp_scb_descriptor * parent_scb,
+                         int scb_child_type)
+{
+	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
+	struct dsp_scb_descriptor * scb;
+  
+	unsigned long flags;
+
+	snd_assert (ins->the_null_scb != NULL,return NULL);
+
+	/* fill the data that will be wroten to DSP */
+	scb_data[SCBsubListPtr] = 
+		(ins->the_null_scb->address << 0x10) | ins->the_null_scb->address;
+
+	scb_data[SCBfuncEntryPtr] &= 0xFFFF0000;
+	scb_data[SCBfuncEntryPtr] |= task_entry->address;
+
+	snd_printdd("dsp_spos: creating SCB <%s>\n",name);
+
+	scb = cs46xx_dsp_create_scb(chip,name,scb_data,dest);
+
+
+	scb->sub_list_ptr = ins->the_null_scb;
+	scb->next_scb_ptr = ins->the_null_scb;
+
+	scb->parent_scb_ptr = parent_scb;
+	scb->task_entry = task_entry;
+
+  
+	/* update parent SCB */
+	if (scb->parent_scb_ptr) {
+#if 0
+		printk ("scb->parent_scb_ptr = %s\n",scb->parent_scb_ptr->scb_name);
+		printk ("scb->parent_scb_ptr->next_scb_ptr = %s\n",scb->parent_scb_ptr->next_scb_ptr->scb_name);
+		printk ("scb->parent_scb_ptr->sub_list_ptr = %s\n",scb->parent_scb_ptr->sub_list_ptr->scb_name);
+#endif
+		/* link to  parent SCB */
+		if (scb_child_type == SCB_ON_PARENT_NEXT_SCB) {
+			snd_assert ( (scb->parent_scb_ptr->next_scb_ptr == ins->the_null_scb),
+				     return NULL);
+
+			scb->parent_scb_ptr->next_scb_ptr = scb;
+
+		} else if (scb_child_type == SCB_ON_PARENT_SUBLIST_SCB) {
+			snd_assert ( (scb->parent_scb_ptr->sub_list_ptr == ins->the_null_scb),
+				     return NULL);
+
+			scb->parent_scb_ptr->sub_list_ptr = scb;
+		} else {
+			snd_assert (0,return NULL);
+		}
+
+		spin_lock_irqsave(&chip->reg_lock, flags);
+
+		/* update entry in DSP RAM */
+		cs46xx_dsp_spos_update_scb(chip,scb->parent_scb_ptr);
+
+		spin_unlock_irqrestore(&chip->reg_lock, flags);
+	}
+
+
+	cs46xx_dsp_proc_register_scb_desc (chip,scb);
+
+	return scb;
+}
+
+static struct dsp_scb_descriptor * 
+cs46xx_dsp_create_generic_scb (struct snd_cs46xx *chip, char * name, u32 * scb_data,
+			       u32 dest, char * task_entry_name,
+                               struct dsp_scb_descriptor * parent_scb,
+                               int scb_child_type)
+{
+	struct dsp_symbol_entry * task_entry;
+
+	task_entry = cs46xx_dsp_lookup_symbol (chip,task_entry_name,
+					       SYMBOL_CODE);
+  
+	if (task_entry == NULL) {
+		snd_printk (KERN_ERR "dsp_spos: symbol %s not found\n",task_entry_name);
+		return NULL;
+	}
+  
+	return _dsp_create_generic_scb (chip,name,scb_data,dest,task_entry,
+					parent_scb,scb_child_type);
+}
+
+struct dsp_scb_descriptor * 
+cs46xx_dsp_create_timing_master_scb (struct snd_cs46xx *chip)
+{
+	struct dsp_scb_descriptor * scb;
+  
+	struct dsp_timing_master_scb timing_master_scb = {
+		{ 0,
+		  0,
+		  0,
+		  0
+		},
+		{ 0,
+		  0,
+		  0,
+		  0,
+		  0
+		},
+		0,0,
+		0,NULL_SCB_ADDR,
+		0,0,             /* extraSampleAccum:TMreserved */
+		0,0,             /* codecFIFOptr:codecFIFOsyncd */
+		0x0001,0x8000,   /* fracSampAccumQm1:TMfrmsLeftInGroup */
+		0x0001,0x0000,   /* fracSampCorrectionQm1:TMfrmGroupLength */
+		0x00060000       /* nSampPerFrmQ15 */
+	};    
+  
+	scb = cs46xx_dsp_create_generic_scb(chip,"TimingMasterSCBInst",(u32 *)&timing_master_scb,
+					    TIMINGMASTER_SCB_ADDR,
+					    "TIMINGMASTER",NULL,SCB_NO_PARENT);
+
+	return scb;
+}
+
+
+struct dsp_scb_descriptor * 
+cs46xx_dsp_create_codec_out_scb(struct snd_cs46xx * chip, char * codec_name,
+                                u16 channel_disp, u16 fifo_addr, u16 child_scb_addr,
+                                u32 dest, struct dsp_scb_descriptor * parent_scb,
+                                int scb_child_type)
+{
+	struct dsp_scb_descriptor * scb;
+  
+	struct dsp_codec_output_scb codec_out_scb = {
+		{ 0,
+		  0,
+		  0,
+		  0
+		},
+		{
+			0,
+			0,
+			0,
+			0,
+			0
+		},
+		0,0,
+		0,NULL_SCB_ADDR,
+		0,                      /* COstrmRsConfig */
+		0,                      /* COstrmBufPtr */
+		/*channel_disp*/0,/*fifo_addr*/0, /* leftChanBaseIOaddr:rightChanIOdisp */
+		0x0000,0x0080,          /* (!AC97!) COexpVolChangeRate:COscaleShiftCount */
+		0,/*child_scb_addr*/0        /* COreserved - need child scb to work with rom code */
+        };
+
+        codec_out_scb.left_chan_base_IO_addr = channel_disp;
+        codec_out_scb.right_chan_IO_disp = fifo_addr;
+        codec_out_scb.last_sub_ptr = child_scb_addr;
+
+	scb = cs46xx_dsp_create_generic_scb(chip,codec_name,(u32 *)&codec_out_scb,
+					    dest,"S16_CODECOUTPUTTASK",parent_scb,
+					    scb_child_type);
+  
+	return scb;
+}
+
+struct dsp_scb_descriptor * 
+cs46xx_dsp_create_codec_in_scb(struct snd_cs46xx * chip, char * codec_name,
+			       u16 channel_disp, u16 fifo_addr, u16 sample_buffer_addr,
+			       u32 dest, struct dsp_scb_descriptor * parent_scb,
+			       int scb_child_type)
+{
+
+	struct dsp_scb_descriptor * scb;
+	struct dsp_codec_input_scb codec_input_scb = {
+		{ 0,
+		  0,
+		  0,
+		  0
+		},
+		{
+			0,
+			0,
+			0,
+			0,
+			0
+		},
+    
+#if 0  /* cs4620 */
+		SyncIOSCB,NULL_SCB_ADDR
+#else
+		0 , 0,
+#endif
+		0,0,
+
+		RSCONFIG_SAMPLE_16STEREO + RSCONFIG_MODULO_64,  /* strmRsConfig */
+		/*sample_buffer_addr << 0x10*/0,       /* strmBufPtr; defined as a dword ptr, used as a byte ptr */
+		/*channel_disp*/0,/*fifo_addr*/0,           /* (!AC97!) leftChanBaseINaddr=AC97primary
+						     link input slot 3 :rightChanINdisp=""slot 4 */
+		0x0000,0x0000,                    /* (!AC97!) ????:scaleShiftCount; no shift needed 
+						     because AC97 is already 20 bits */
+		0x80008000                        /* ??clw cwcgame.scb has 0 */
+	};
+
+        codec_input_scb.strm_buf_ptr = sample_buffer_addr << 0x10;
+        codec_input_scb.rightChanINdisp = channel_disp;
+        codec_input_scb.left_chan_base_IN_addr = fifo_addr;
+
+	scb = cs46xx_dsp_create_generic_scb(chip,codec_name,(u32 *)&codec_input_scb,
+					    dest,"S16_CODECINPUTTASK",parent_scb,
+					    scb_child_type);
+	return scb;
+}
+
+
+static struct dsp_scb_descriptor * 
+cs46xx_dsp_create_pcm_reader_scb(struct snd_cs46xx * chip, char * scb_name,
+                                 u16 sample_buffer_addr, u32 dest,
+                                 int virtual_channel, u32 playback_hw_addr,
+                                 struct dsp_scb_descriptor * parent_scb,
+                                 int scb_child_type)
+{
+	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
+	struct dsp_scb_descriptor * scb;
+  
+	struct dsp_generic_scb pcm_reader_scb = {
+    
+		/*
+		  Play DMA Task xfers data from host buffer to SP buffer
+		  init/runtime variables:
+		  PlayAC: Play Audio Data Conversion - SCB loc: 2nd dword, mask: 0x0000F000L
+		  DATA_FMT_16BIT_ST_LTLEND(0x00000000L)   from 16-bit stereo, little-endian
+		  DATA_FMT_8_BIT_ST_SIGNED(0x00001000L)   from 8-bit stereo, signed
+		  DATA_FMT_16BIT_MN_LTLEND(0x00002000L)   from 16-bit mono, little-endian
+		  DATA_FMT_8_BIT_MN_SIGNED(0x00003000L)   from 8-bit mono, signed
+		  DATA_FMT_16BIT_ST_BIGEND(0x00004000L)   from 16-bit stereo, big-endian
+		  DATA_FMT_16BIT_MN_BIGEND(0x00006000L)   from 16-bit mono, big-endian
+		  DATA_FMT_8_BIT_ST_UNSIGNED(0x00009000L) from 8-bit stereo, unsigned
+		  DATA_FMT_8_BIT_MN_UNSIGNED(0x0000b000L) from 8-bit mono, unsigned
+		  ? Other combinations possible from:
+		  DMA_RQ_C2_AUDIO_CONVERT_MASK    0x0000F000L
+		  DMA_RQ_C2_AC_NONE               0x00000000L
+		  DMA_RQ_C2_AC_8_TO_16_BIT        0x00001000L
+		  DMA_RQ_C2_AC_MONO_TO_STEREO     0x00002000L
+		  DMA_RQ_C2_AC_ENDIAN_CONVERT     0x00004000L
+		  DMA_RQ_C2_AC_SIGNED_CONVERT     0x00008000L
+        
+		  HostBuffAddr: Host Buffer Physical Byte Address - SCB loc:3rd dword, Mask: 0xFFFFFFFFL
+		  aligned to dword boundary
+		*/
+		/* Basic (non scatter/gather) DMA requestor (4 ints) */
+		{ DMA_RQ_C1_SOURCE_ON_HOST +        /* source buffer is on the host */
+		  DMA_RQ_C1_SOURCE_MOD1024 +        /* source buffer is 1024 dwords (4096 bytes) */
+		  DMA_RQ_C1_DEST_MOD32 +            /* dest buffer(PCMreaderBuf) is 32 dwords*/
+		  DMA_RQ_C1_WRITEBACK_SRC_FLAG +    /* ?? */
+		  DMA_RQ_C1_WRITEBACK_DEST_FLAG +   /* ?? */
+		  15,                             /* DwordCount-1: picked 16 for DwordCount because Jim */
+		  /*        Barnette said that is what we should use since */
+		  /*        we are not running in optimized mode? */
+		  DMA_RQ_C2_AC_NONE +
+		  DMA_RQ_C2_SIGNAL_SOURCE_PINGPONG + /* set play interrupt (bit0) in HISR when source */
+		  /*   buffer (on host) crosses half-way point */
+		  virtual_channel,                   /* Play DMA channel arbitrarily set to 0 */
+		  playback_hw_addr,                  /* HostBuffAddr (source) */
+		  DMA_RQ_SD_SP_SAMPLE_ADDR +         /* destination buffer is in SP Sample Memory */
+		  sample_buffer_addr                 /* SP Buffer Address (destination) */
+		},
+		/* Scatter/gather DMA requestor extension   (5 ints) */
+		{
+			0,
+			0,
+			0,
+			0,
+			0 
+		},
+		/* Sublist pointer & next stream control block (SCB) link. */
+		NULL_SCB_ADDR,NULL_SCB_ADDR,
+		/* Pointer to this tasks parameter block & stream function pointer */
+                0,NULL_SCB_ADDR,
+#ifndef TARGET_OS2
+		/* rsConfig register for stream buffer (rsDMA reg. is loaded from basicReq.daw */
+		/*   for incoming streams, or basicReq.saw, for outgoing streams) */
+		RSCONFIG_DMA_ENABLE +                 /* enable DMA */
+		(19 << RSCONFIG_MAX_DMA_SIZE_SHIFT) + /* MAX_DMA_SIZE picked to be 19 since SPUD  */
+		/*  uses it for some reason */
+		((dest >> 4) << RSCONFIG_STREAM_NUM_SHIFT) + /* stream number = SCBaddr/16 */
+		RSCONFIG_SAMPLE_16STEREO +
+                RSCONFIG_MODULO_32,             /* dest buffer(PCMreaderBuf) is 32 dwords (256 bytes) */
+#else
+                0,
+#endif
+		/* Stream sample pointer & MAC-unit mode for this stream */
+		/*(sample_buffer_addr << 0x10)*/0,
+		/* Fractional increment per output sample in the input sample buffer */
+		0, 
+		{
+			/* Standard stereo volume control
+			   default muted */
+			0xffff,0xffff,
+			0xffff,0xffff
+		}
+        };
+
+        pcm_reader_scb.strm_rs_config = RSCONFIG_DMA_ENABLE +
+            (19 << RSCONFIG_MAX_DMA_SIZE_SHIFT) +
+            ((dest >> 4) << RSCONFIG_STREAM_NUM_SHIFT) +
+            RSCONFIG_SAMPLE_16STEREO +
+            RSCONFIG_MODULO_32;
+        pcm_reader_scb.strm_buf_ptr = sample_buffer_addr << 0x10;
+
+	if (ins->null_algorithm == NULL) {
+		ins->null_algorithm =  cs46xx_dsp_lookup_symbol (chip,"NULLALGORITHM",
+								 SYMBOL_CODE);
+    
+		if (ins->null_algorithm == NULL) {
+			snd_printk (KERN_ERR "dsp_spos: symbol NULLALGORITHM not found\n");
+			return NULL;
+		}    
+	}
+
+	scb = _dsp_create_generic_scb(chip,scb_name,(u32 *)&pcm_reader_scb,
+				      dest,ins->null_algorithm,parent_scb,
+				      scb_child_type);
+  
+	return scb;
+}
+
+#define GOF_PER_SEC 200
+
+struct dsp_scb_descriptor * 
+cs46xx_dsp_create_src_task_scb(struct snd_cs46xx * chip, char * scb_name,
+			       int rate,
+                               u16 src_buffer_addr,
+                               u16 src_delay_buffer_addr, u32 dest,
+                               struct dsp_scb_descriptor * parent_scb,
+                               int scb_child_type,
+	                       int pass_through)
+{
+
+	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
+	struct dsp_scb_descriptor * scb;
+	unsigned int tmp1, tmp2;
+	unsigned int phiIncr;
+	unsigned int correctionPerGOF, correctionPerSec;
+
+	snd_printdd( "dsp_spos: setting %s rate to %u\n",scb_name,rate);
+
+	/*
+	 *  Compute the values used to drive the actual sample rate conversion.
+	 *  The following formulas are being computed, using inline assembly
+	 *  since we need to use 64 bit arithmetic to compute the values:
+	 *
+	 *  phiIncr = floor((Fs,in * 2^26) / Fs,out)
+	 *  correctionPerGOF = floor((Fs,in * 2^26 - Fs,out * phiIncr) /
+	 *                                   GOF_PER_SEC)
+	 *  ulCorrectionPerSec = Fs,in * 2^26 - Fs,out * phiIncr -M
+	 *                       GOF_PER_SEC * correctionPerGOF
+	 *
+	 *  i.e.
+	 *
+	 *  phiIncr:other = dividend:remainder((Fs,in * 2^26) / Fs,out)
+	 *  correctionPerGOF:correctionPerSec =
+	 *      dividend:remainder(ulOther / GOF_PER_SEC)
+	 */
+	tmp1 = rate << 16;
+	phiIncr = tmp1 / 48000;
+	tmp1 -= phiIncr * 48000;
+	tmp1 <<= 10;
+	phiIncr <<= 10;
+	tmp2 = tmp1 / 48000;
+	phiIncr += tmp2;
+	tmp1 -= tmp2 * 48000;
+	correctionPerGOF = tmp1 / GOF_PER_SEC;
+	tmp1 -= correctionPerGOF * GOF_PER_SEC;
+	correctionPerSec = tmp1;
+
+	{
+		struct dsp_src_task_scb src_task_scb = {
+			0x0028,0x00c8,
+			0x5555,0x0000,
+			0x0000,0x0000,
+			src_buffer_addr,1,
+			correctionPerGOF,correctionPerSec,
+			RSCONFIG_SAMPLE_16STEREO + RSCONFIG_MODULO_32,  
+			0x0000,src_delay_buffer_addr,                  
+			0x0,                                            
+			0x080,(src_delay_buffer_addr + (24 * 4)),
+			0,0, /* next_scb, sub_list_ptr */
+			0,0, /* entry, this_spb */
+			RSCONFIG_SAMPLE_16STEREO + RSCONFIG_MODULO_8,
+			src_buffer_addr << 0x10,
+			phiIncr,
+			{ 
+				0xffff - ins->dac_volume_right,0xffff - ins->dac_volume_left,
+				0xffff - ins->dac_volume_right,0xffff - ins->dac_volume_left
+			}
+		};
+		
+		if (ins->s16_up == NULL) {
+			ins->s16_up =  cs46xx_dsp_lookup_symbol (chip,"S16_UPSRC",
+								 SYMBOL_CODE);
+			
+			if (ins->s16_up == NULL) {
+				snd_printk (KERN_ERR "dsp_spos: symbol S16_UPSRC not found\n");
+				return NULL;
+			}    
+		}
+		
+		/* clear buffers */
+		_dsp_clear_sample_buffer (chip,src_buffer_addr,8);
+		_dsp_clear_sample_buffer (chip,src_delay_buffer_addr,32);
+				
+		if (pass_through) {
+			/* wont work with any other rate than
+			   the native DSP rate */
+			snd_assert (rate == 48000, return NULL);
+
+			scb = cs46xx_dsp_create_generic_scb(chip,scb_name,(u32 *)&src_task_scb,
+							    dest,"DMAREADER",parent_scb,
+							    scb_child_type);
+		} else {
+			scb = _dsp_create_generic_scb(chip,scb_name,(u32 *)&src_task_scb,
+						      dest,ins->s16_up,parent_scb,
+						      scb_child_type);
+		}
+
+
+	}
+
+	return scb;
+}
+
+#if 0 /* not used */
+struct dsp_scb_descriptor * 
+cs46xx_dsp_create_filter_scb(struct snd_cs46xx * chip, char * scb_name,
+			     u16 buffer_addr, u32 dest,
+			     struct dsp_scb_descriptor * parent_scb,
+			     int scb_child_type) {
+	struct dsp_scb_descriptor * scb;
+	
+	struct dsp_filter_scb filter_scb = {
+		.a0_right            = 0x41a9,
+		.a0_left             = 0x41a9,
+		.a1_right            = 0xb8e4,
+		.a1_left             = 0xb8e4,
+		.a2_right            = 0x3e55,
+		.a2_left             = 0x3e55,
+		
+		.filter_unused3      = 0x0000,
+		.filter_unused2      = 0x0000,
+
+		.output_buf_ptr      = buffer_addr,
+		.init                = 0x000,
+
+		.prev_sample_output1 = 0x00000000,
+		.prev_sample_output2 = 0x00000000,
+
+		.prev_sample_input1  = 0x00000000,
+		.prev_sample_input2  = 0x00000000,
+
+		.next_scb_ptr        = 0x0000,
+		.sub_list_ptr        = 0x0000,
+
+		.entry_point         = 0x0000,
+		.spb_ptr             = 0x0000,
+
+		.b0_right            = 0x0e38,
+		.b0_left             = 0x0e38,
+		.b1_right            = 0x1c71,
+		.b1_left             = 0x1c71,
+		.b2_right            = 0x0e38,
+		.b2_left             = 0x0e38,
+	};
+
+
+	scb = cs46xx_dsp_create_generic_scb(chip,scb_name,(u32 *)&filter_scb,
+					    dest,"FILTERTASK",parent_scb,
+					    scb_child_type);
+
+ 	return scb;
+}
+#endif /* not used */
+
+struct dsp_scb_descriptor * 
+cs46xx_dsp_create_mix_only_scb(struct snd_cs46xx * chip, char * scb_name,
+                               u16 mix_buffer_addr, u32 dest,
+                               struct dsp_scb_descriptor * parent_scb,
+                               int scb_child_type)
+{
+	struct dsp_scb_descriptor * scb;
+  
+	struct dsp_mix_only_scb master_mix_scb = {
+		/* 0 */ { 0,
+			  /* 1 */   0,
+			  /* 2 */  mix_buffer_addr,
+			  /* 3 */  0
+			  /*   */ },
+		{
+			/* 4 */  0,
+			/* 5 */  0,
+			/* 6 */  0,
+			/* 7 */  0,
+			/* 8 */  0x00000080
+		},
+		/* 9 */ 0,0,
+		/* A */ 0,0,
+		/* B */ RSCONFIG_SAMPLE_16STEREO + RSCONFIG_MODULO_32,
+		/* C */ (mix_buffer_addr  + (16 * 4)) << 0x10, 
+		/* D */ 0,
+		{
+			/* E */ 0x8000,0x8000,
+			/* F */ 0x8000,0x8000
+		}
+	};
+
+
+	scb = cs46xx_dsp_create_generic_scb(chip,scb_name,(u32 *)&master_mix_scb,
+					    dest,"S16_MIX",parent_scb,
+					    scb_child_type);
+	return scb;
+}
+
+
+struct dsp_scb_descriptor * 
+cs46xx_dsp_create_mix_to_ostream_scb(struct snd_cs46xx * chip, char * scb_name,
+                                     u16 mix_buffer_addr, u16 writeback_spb, u32 dest,
+                                     struct dsp_scb_descriptor * parent_scb,
+                                     int scb_child_type)
+{
+	struct dsp_scb_descriptor * scb;
+
+	struct dsp_mix2_ostream_scb mix2_ostream_scb = {
+		/* Basic (non scatter/gather) DMA requestor (4 ints) */
+		{ 
+			DMA_RQ_C1_SOURCE_MOD64 +
+			DMA_RQ_C1_DEST_ON_HOST +
+			DMA_RQ_C1_DEST_MOD1024 +
+			DMA_RQ_C1_WRITEBACK_SRC_FLAG + 
+			DMA_RQ_C1_WRITEBACK_DEST_FLAG +
+			15,                            
+      
+			DMA_RQ_C2_AC_NONE +
+			DMA_RQ_C2_SIGNAL_DEST_PINGPONG + 
+      
+			CS46XX_DSP_CAPTURE_CHANNEL,                                 
+			DMA_RQ_SD_SP_SAMPLE_ADDR + 
+			mix_buffer_addr, 
+			0x0                   
+		},
+    
+		{ 0, 0, 0, 0, 0, },
+		0,0,
+		0,writeback_spb,
+    
+		RSCONFIG_DMA_ENABLE + 
+		(19 << RSCONFIG_MAX_DMA_SIZE_SHIFT) + 
+    
+		((dest >> 4) << RSCONFIG_STREAM_NUM_SHIFT) +
+		RSCONFIG_DMA_TO_HOST + 
+		RSCONFIG_SAMPLE_16STEREO +
+		RSCONFIG_MODULO_64,    
+		(mix_buffer_addr + (32 * 4)) << 0x10,
+		1,0,            
+		0x0001,0x0080,
+		0xFFFF,0
+	};
+
+
+	scb = cs46xx_dsp_create_generic_scb(chip,scb_name,(u32 *)&mix2_ostream_scb,
+				
+	    dest,"S16_MIX_TO_OSTREAM",parent_scb,
+					    scb_child_type);
+  
+	return scb;
+}
+
+
+struct dsp_scb_descriptor * 
+cs46xx_dsp_create_vari_decimate_scb(struct snd_cs46xx * chip,char * scb_name,
+                                    u16 vari_buffer_addr0,
+                                    u16 vari_buffer_addr1,
+                                    u32 dest,
+                                    struct dsp_scb_descriptor * parent_scb,
+                                    int scb_child_type)
+{
+
+	struct dsp_scb_descriptor * scb;
+  
+	struct dsp_vari_decimate_scb vari_decimate_scb = {
+		0x0028,0x00c8,
+		0x5555,0x0000,
+		0x0000,0x0000,
+		vari_buffer_addr0,vari_buffer_addr1,
+    
+		0x0028,0x00c8,
+		RSCONFIG_SAMPLE_16STEREO + RSCONFIG_MODULO_256, 
+    
+		0xFF800000,   
+		0,
+		0x0080,vari_buffer_addr1 + (25 * 4), 
+    
+		0,0, 
+		0,0,
+
+		RSCONFIG_SAMPLE_16STEREO + RSCONFIG_MODULO_8,
+		vari_buffer_addr0 << 0x10,   
+		0x04000000,                   
+		{
+			0x8000,0x8000, 
+			0xFFFF,0xFFFF
+		}
+	};
+
+	scb = cs46xx_dsp_create_generic_scb(chip,scb_name,(u32 *)&vari_decimate_scb,
+					    dest,"VARIDECIMATE",parent_scb,
+					    scb_child_type);
+  
+	return scb;
+}
+
+
+static struct dsp_scb_descriptor * 
+cs46xx_dsp_create_pcm_serial_input_scb(struct snd_cs46xx * chip, char * scb_name, u32 dest,
+                                       struct dsp_scb_descriptor * input_scb,
+                                       struct dsp_scb_descriptor * parent_scb,
+                                       int scb_child_type)
+{
+
+	struct dsp_scb_descriptor * scb;
+
+
+	struct dsp_pcm_serial_input_scb pcm_serial_input_scb = {
+		{ 0,
+		  0,
+		  0,
+		  0
+		},
+		{
+			0,
+			0,
+			0,
+			0,
+			0
+		},
+
+		0,0,
+		0,0,
+
+		RSCONFIG_SAMPLE_16STEREO + RSCONFIG_MODULO_16,
+		0,
+      /* 0xD */ 0,input_scb->address,
+		{
+      /* 0xE */   0x8000,0x8000,
+      /* 0xF */	  0x8000,0x8000
+		}
+	};
+
+	scb = cs46xx_dsp_create_generic_scb(chip,scb_name,(u32 *)&pcm_serial_input_scb,
+					    dest,"PCMSERIALINPUTTASK",parent_scb,
+					    scb_child_type);
+	return scb;
+}
+
+
+static struct dsp_scb_descriptor * 
+cs46xx_dsp_create_asynch_fg_tx_scb(struct snd_cs46xx * chip, char * scb_name, u32 dest,
+                                   u16 hfg_scb_address,
+                                   u16 asynch_buffer_address,
+                                   struct dsp_scb_descriptor * parent_scb,
+                                   int scb_child_type)
+{
+
+	struct dsp_scb_descriptor * scb;
+
+	struct dsp_asynch_fg_tx_scb asynch_fg_tx_scb = {
+		0xfc00,0x03ff,      /*  Prototype sample buffer size of 256 dwords */
+		0x0058,0x0028,      /* Min Delta 7 dwords == 28 bytes */
+		/* : Max delta 25 dwords == 100 bytes */
+		0,hfg_scb_address,  /* Point to HFG task SCB */
+		0,0,		    /* Initialize current Delta and Consumer ptr adjustment count */
+		0,                  /* Initialize accumulated Phi to 0 */
+		0,0x2aab,           /* Const 1/3 */
+    
+		{
+			0,         /* Define the unused elements */
+			0,
+			0
+		},
+    
+		0,0,
+		0,dest + AFGTxAccumPhi,
+    
+		RSCONFIG_SAMPLE_16STEREO + RSCONFIG_MODULO_256, /* Stereo, 256 dword */
+		(asynch_buffer_address) << 0x10,  /* This should be automagically synchronized
+                                                     to the producer pointer */
+    
+		/* There is no correct initial value, it will depend upon the detected
+		   rate etc  */
+		0x18000000,                     /* Phi increment for approx 32k operation */
+		0x8000,0x8000,                  /* Volume controls are unused at this time */
+		0x8000,0x8000
+	};
+  
+	scb = cs46xx_dsp_create_generic_scb(chip,scb_name,(u32 *)&asynch_fg_tx_scb,
+					    dest,"ASYNCHFGTXCODE",parent_scb,
+					    scb_child_type);
+
+	return scb;
+}
+
+
+struct dsp_scb_descriptor * 
+cs46xx_dsp_create_asynch_fg_rx_scb(struct snd_cs46xx * chip, char * scb_name, u32 dest,
+                                   u16 hfg_scb_address,
+                                   u16 asynch_buffer_address,
+                                   struct dsp_scb_descriptor * parent_scb,
+                                   int scb_child_type)
+{
+	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
+	struct dsp_scb_descriptor * scb;
+
+	struct dsp_asynch_fg_rx_scb asynch_fg_rx_scb = {
+		0xfe00,0x01ff,      /*  Prototype sample buffer size of 128 dwords */
+		0x0064,0x001c,      /* Min Delta 7 dwords == 28 bytes */
+		                    /* : Max delta 25 dwords == 100 bytes */
+		0,hfg_scb_address,  /* Point to HFG task SCB */
+		0,0,				/* Initialize current Delta and Consumer ptr adjustment count */
+		{
+			0,                /* Define the unused elements */
+			0,
+			0,
+			0,
+			0
+		},
+      
+		0,0,
+		0,dest,
+    
+		RSCONFIG_MODULO_128 |
+        RSCONFIG_SAMPLE_16STEREO,                         /* Stereo, 128 dword */
+		( (asynch_buffer_address + (16 * 4))  << 0x10),   /* This should be automagically 
+							                                  synchrinized to the producer pointer */
+    
+		/* There is no correct initial value, it will depend upon the detected
+		   rate etc  */
+		0x18000000,         
+
+		/* Set IEC958 input volume */
+		0xffff - ins->spdif_input_volume_right,0xffff - ins->spdif_input_volume_left,
+		0xffff - ins->spdif_input_volume_right,0xffff - ins->spdif_input_volume_left,
+	};
+
+	scb = cs46xx_dsp_create_generic_scb(chip,scb_name,(u32 *)&asynch_fg_rx_scb,
+					    dest,"ASYNCHFGRXCODE",parent_scb,
+					    scb_child_type);
+
+	return scb;
+}
+
+
+#if 0 /* not used */
+struct dsp_scb_descriptor * 
+cs46xx_dsp_create_output_snoop_scb(struct snd_cs46xx * chip, char * scb_name, u32 dest,
+                                   u16 snoop_buffer_address,
+                                   struct dsp_scb_descriptor * snoop_scb,
+                                   struct dsp_scb_descriptor * parent_scb,
+                                   int scb_child_type)
+{
+
+	struct dsp_scb_descriptor * scb;
+  
+	struct dsp_output_snoop_scb output_snoop_scb = {
+		{ 0,	/*  not used.  Zero */
+		  0,
+		  0,
+		  0,
+		},
+		{
+			0, /* not used.  Zero */
+			0,
+			0,
+			0,
+			0
+		},
+    
+		0,0,
+		0,0,
+    
+		RSCONFIG_SAMPLE_16STEREO + RSCONFIG_MODULO_64,
+		snoop_buffer_address << 0x10,  
+		0,0,
+		0,
+		0,snoop_scb->address
+	};
+  
+	scb = cs46xx_dsp_create_generic_scb(chip,scb_name,(u32 *)&output_snoop_scb,
+					    dest,"OUTPUTSNOOP",parent_scb,
+					    scb_child_type);
+	return scb;
+}
+#endif /* not used */
+
+
+struct dsp_scb_descriptor * 
+cs46xx_dsp_create_spio_write_scb(struct snd_cs46xx * chip, char * scb_name, u32 dest,
+                                 struct dsp_scb_descriptor * parent_scb,
+                                 int scb_child_type)
+{
+	struct dsp_scb_descriptor * scb;
+  
+	struct dsp_spio_write_scb spio_write_scb = {
+		0,0,         /*   SPIOWAddress2:SPIOWAddress1; */
+		0,           /*   SPIOWData1; */
+		0,           /*   SPIOWData2; */
+		0,0,         /*   SPIOWAddress4:SPIOWAddress3; */
+		0,           /*   SPIOWData3; */
+		0,           /*   SPIOWData4; */
+		0,0,         /*   SPIOWDataPtr:Unused1; */
+		{ 0,0 },     /*   Unused2[2]; */
+    
+		0,0,	     /*   SPIOWChildPtr:SPIOWSiblingPtr; */
+		0,0,         /*   SPIOWThisPtr:SPIOWEntryPoint; */
+    
+		{ 
+			0,
+			0,
+			0,
+			0,
+			0          /*   Unused3[5];  */
+		}
+	};
+
+	scb = cs46xx_dsp_create_generic_scb(chip,scb_name,(u32 *)&spio_write_scb,
+					    dest,"SPIOWRITE",parent_scb,
+					    scb_child_type);
+
+	return scb;
+}
+
+struct dsp_scb_descriptor *
+cs46xx_dsp_create_magic_snoop_scb(struct snd_cs46xx * chip, char * scb_name, u32 dest,
+				  u16 snoop_buffer_address,
+				  struct dsp_scb_descriptor * snoop_scb,
+				  struct dsp_scb_descriptor * parent_scb,
+				  int scb_child_type)
+{
+	struct dsp_scb_descriptor * scb;
+  
+	struct dsp_magic_snoop_task magic_snoop_scb = {
+		/* 0 */ 0, /* i0 */
+		/* 1 */ 0, /* i1 */
+		/* 2 */ snoop_buffer_address << 0x10,
+		/* 3 */ 0,snoop_scb->address,
+		/* 4 */ 0, /* i3 */
+		/* 5 */ 0, /* i4 */
+		/* 6 */ 0, /* i5 */
+		/* 7 */ 0, /* i6 */
+		/* 8 */ 0, /* i7 */
+		/* 9 */ 0,0, /* next_scb, sub_list_ptr */
+		/* A */ 0,0, /* entry_point, this_ptr */
+		/* B */ RSCONFIG_SAMPLE_16STEREO + RSCONFIG_MODULO_64,
+		/* C */ snoop_buffer_address  << 0x10,
+		/* D */ 0,
+		/* E */ { 0x8000,0x8000,
+	        /* F */   0xffff,0xffff
+		}
+	};
+
+	scb = cs46xx_dsp_create_generic_scb(chip,scb_name,(u32 *)&magic_snoop_scb,
+					    dest,"MAGICSNOOPTASK",parent_scb,
+					    scb_child_type);
+
+	return scb;
+}
+
+static struct dsp_scb_descriptor *
+find_next_free_scb (struct snd_cs46xx * chip, struct dsp_scb_descriptor * from)
+{
+	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
+	struct dsp_scb_descriptor * scb = from;
+
+	while (scb->next_scb_ptr != ins->the_null_scb) {
+		snd_assert (scb->next_scb_ptr != NULL, return NULL);
+
+		scb = scb->next_scb_ptr;
+	}
+
+	return scb;
+}
+
+static u32 pcm_reader_buffer_addr[DSP_MAX_PCM_CHANNELS] = {
+	0x0600, /* 1 */
+	0x1500, /* 2 */
+	0x1580, /* 3 */
+	0x1600, /* 4 */
+	0x1680, /* 5 */
+	0x1700, /* 6 */
+	0x1780, /* 7 */
+	0x1800, /* 8 */
+	0x1880, /* 9 */
+	0x1900, /* 10 */
+	0x1980, /* 11 */
+	0x1A00, /* 12 */
+	0x1A80, /* 13 */
+	0x1B00, /* 14 */
+	0x1B80, /* 15 */
+	0x1C00, /* 16 */
+	0x1C80, /* 17 */
+	0x1D00, /* 18 */
+	0x1D80, /* 19 */
+	0x1E00, /* 20 */
+	0x1E80, /* 21 */
+	0x1F00, /* 22 */
+	0x1F80, /* 23 */
+	0x2000, /* 24 */
+	0x2080, /* 25 */
+	0x2100, /* 26 */
+	0x2180, /* 27 */
+	0x2200, /* 28 */
+	0x2280, /* 29 */
+	0x2300, /* 30 */
+	0x2380, /* 31 */
+	0x2400, /* 32 */
+};
+
+static u32 src_output_buffer_addr[DSP_MAX_SRC_NR] = {
+	0x2B80,
+	0x2BA0,
+	0x2BC0,
+	0x2BE0,
+	0x2D00,  
+	0x2D20,  
+	0x2D40,  
+	0x2D60,
+	0x2D80,
+	0x2DA0,
+	0x2DC0,
+	0x2DE0,
+	0x2E00,
+	0x2E20
+};
+
+static u32 src_delay_buffer_addr[DSP_MAX_SRC_NR] = {
+	0x2480,
+	0x2500,
+	0x2580,
+	0x2600,
+	0x2680,
+	0x2700,
+	0x2780,
+	0x2800,
+	0x2880,
+	0x2900,
+	0x2980,
+	0x2A00,
+	0x2A80,
+	0x2B00
+};
+
+struct dsp_pcm_channel_descriptor *
+cs46xx_dsp_create_pcm_channel (struct snd_cs46xx * chip,
+			       u32 sample_rate, void * private_data, 
+			       u32 hw_dma_addr,
+			       int pcm_channel_id)
+{
+	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
+	struct dsp_scb_descriptor * src_scb = NULL, * pcm_scb, * mixer_scb = NULL;
+	struct dsp_scb_descriptor * src_parent_scb = NULL;
+
+	/* struct dsp_scb_descriptor * pcm_parent_scb; */
+	char scb_name[DSP_MAX_SCB_NAME];
+	int i, pcm_index = -1, insert_point, src_index = -1, pass_through = 0;
+	unsigned long flags;
+
+	switch (pcm_channel_id) {
+	case DSP_PCM_MAIN_CHANNEL:
+		mixer_scb = ins->master_mix_scb;
+		break;
+	case DSP_PCM_REAR_CHANNEL:
+		mixer_scb = ins->rear_mix_scb;
+		break;
+	case DSP_PCM_CENTER_LFE_CHANNEL:
+		mixer_scb = ins->center_lfe_mix_scb;
+		break;
+	case DSP_PCM_S71_CHANNEL:
+		/* TODO */
+		snd_assert(0, return NULL);
+		break;
+	case DSP_IEC958_CHANNEL:
+		snd_assert (ins->asynch_tx_scb != NULL, return NULL);
+		mixer_scb = ins->asynch_tx_scb;
+
+		/* if sample rate is set to 48khz we pass
+		   the Sample Rate Converted (which could
+		   alter the raw data stream ...) */
+		if (sample_rate == 48000) {
+			snd_printdd ("IEC958 pass through\n");
+			/* Hack to bypass creating a new SRC */
+			pass_through = 1;
+		}
+		break;
+	default:
+		snd_assert (0, return NULL);
+		return NULL;
+	}
+	/* default sample rate is 44100 */
+	if (!sample_rate) sample_rate = 44100;
+
+	/* search for a already created SRC SCB with the same sample rate */
+	for (i = 0; i < DSP_MAX_PCM_CHANNELS && 
+		     (pcm_index == -1 || src_scb == NULL); ++i) {
+
+		/* virtual channel reserved 
+		   for capture */
+		if (i == CS46XX_DSP_CAPTURE_CHANNEL) continue;
+
+		if (ins->pcm_channels[i].active) {
+			if (!src_scb && 
+			    ins->pcm_channels[i].sample_rate == sample_rate &&
+			    ins->pcm_channels[i].mixer_scb == mixer_scb) {
+				src_scb = ins->pcm_channels[i].src_scb;
+				ins->pcm_channels[i].src_scb->ref_count ++;
+				src_index = ins->pcm_channels[i].src_slot;
+			}
+		} else if (pcm_index == -1) {
+			pcm_index = i;
+		}
+	}
+
+	if (pcm_index == -1) {
+		snd_printk (KERN_ERR "dsp_spos: no free PCM channel\n");
+		return NULL;
+	}
+
+	if (src_scb == NULL) {
+		if (ins->nsrc_scb >= DSP_MAX_SRC_NR) {
+			snd_printk(KERN_ERR "dsp_spos: to many SRC instances\n!");
+			return NULL;
+		}
+
+		/* find a free slot */
+		for (i = 0; i < DSP_MAX_SRC_NR; ++i) {
+			if (ins->src_scb_slots[i] == 0) {
+				src_index = i;
+				ins->src_scb_slots[i] = 1;
+				break;
+			}
+		}
+		snd_assert (src_index != -1,return NULL);
+
+		/* we need to create a new SRC SCB */
+		if (mixer_scb->sub_list_ptr == ins->the_null_scb) {
+			src_parent_scb = mixer_scb;
+			insert_point = SCB_ON_PARENT_SUBLIST_SCB;
+		} else {
+			src_parent_scb = find_next_free_scb(chip,mixer_scb->sub_list_ptr);
+			insert_point = SCB_ON_PARENT_NEXT_SCB;
+		}
+
+		snprintf (scb_name,DSP_MAX_SCB_NAME,"SrcTask_SCB%d",src_index);
+		
+		snd_printdd( "dsp_spos: creating SRC \"%s\"\n",scb_name);
+		src_scb = cs46xx_dsp_create_src_task_scb(chip,scb_name,
+							 sample_rate,
+							 src_output_buffer_addr[src_index],
+							 src_delay_buffer_addr[src_index],
+							 /* 0x400 - 0x600 source SCBs */
+							 0x400 + (src_index * 0x10) ,
+							 src_parent_scb,
+							 insert_point,
+							 pass_through);
+
+		if (!src_scb) {
+			snd_printk (KERN_ERR "dsp_spos: failed to create SRCtaskSCB\n");
+			return NULL;
+		}
+
+		/* cs46xx_dsp_set_src_sample_rate(chip,src_scb,sample_rate); */
+
+		ins->nsrc_scb ++;
+	} 
+  
+  
+	snprintf (scb_name,DSP_MAX_SCB_NAME,"PCMReader_SCB%d",pcm_index);
+
+	snd_printdd( "dsp_spos: creating PCM \"%s\" (%d)\n",scb_name,
+                 pcm_channel_id);
+
+	pcm_scb = cs46xx_dsp_create_pcm_reader_scb(chip,scb_name,
+						   pcm_reader_buffer_addr[pcm_index],
+						   /* 0x200 - 400 PCMreader SCBs */
+						   (pcm_index * 0x10) + 0x200,
+						   pcm_index,    /* virtual channel 0-31 */
+						   hw_dma_addr,  /* pcm hw addr */
+                           NULL,         /* parent SCB ptr */
+                           0             /* insert point */ 
+                           );
+
+	if (!pcm_scb) {
+		snd_printk (KERN_ERR "dsp_spos: failed to create PCMreaderSCB\n");
+		return NULL;
+	}
+	
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	ins->pcm_channels[pcm_index].sample_rate = sample_rate;
+	ins->pcm_channels[pcm_index].pcm_reader_scb = pcm_scb;
+	ins->pcm_channels[pcm_index].src_scb = src_scb;
+	ins->pcm_channels[pcm_index].unlinked = 1;
+	ins->pcm_channels[pcm_index].private_data = private_data;
+	ins->pcm_channels[pcm_index].src_slot = src_index;
+	ins->pcm_channels[pcm_index].active = 1;
+	ins->pcm_channels[pcm_index].pcm_slot = pcm_index;
+	ins->pcm_channels[pcm_index].mixer_scb = mixer_scb;
+	ins->npcm_channels ++;
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+	return (ins->pcm_channels + pcm_index);
+}
+
+int cs46xx_dsp_pcm_channel_set_period (struct snd_cs46xx * chip,
+				       struct dsp_pcm_channel_descriptor * pcm_channel,
+				       int period_size)
+{
+	u32 temp = snd_cs46xx_peek (chip,pcm_channel->pcm_reader_scb->address << 2);
+	temp &= ~DMA_RQ_C1_SOURCE_SIZE_MASK;
+
+	switch (period_size) {
+	case 2048:
+		temp |= DMA_RQ_C1_SOURCE_MOD1024;
+		break;
+	case 1024:
+		temp |= DMA_RQ_C1_SOURCE_MOD512;
+		break;
+	case 512:
+		temp |= DMA_RQ_C1_SOURCE_MOD256;
+		break;
+	case 256:
+		temp |= DMA_RQ_C1_SOURCE_MOD128;
+		break;
+	case 128:
+		temp |= DMA_RQ_C1_SOURCE_MOD64;
+		break;
+	case 64:
+		temp |= DMA_RQ_C1_SOURCE_MOD32;
+		break;		      
+	case 32:
+		temp |= DMA_RQ_C1_SOURCE_MOD16;
+		break; 
+	default:
+		snd_printdd ("period size (%d) not supported by HW\n", period_size);
+		return -EINVAL;
+	}
+
+	snd_cs46xx_poke (chip,pcm_channel->pcm_reader_scb->address << 2,temp);
+
+	return 0;
+}
+
+int cs46xx_dsp_pcm_ostream_set_period (struct snd_cs46xx * chip,
+				       int period_size)
+{
+	u32 temp = snd_cs46xx_peek (chip,WRITEBACK_SCB_ADDR << 2);
+	temp &= ~DMA_RQ_C1_DEST_SIZE_MASK;
+
+	switch (period_size) {
+	case 2048:
+		temp |= DMA_RQ_C1_DEST_MOD1024;
+		break;
+	case 1024:
+		temp |= DMA_RQ_C1_DEST_MOD512;
+		break;
+	case 512:
+		temp |= DMA_RQ_C1_DEST_MOD256;
+		break;
+	case 256:
+		temp |= DMA_RQ_C1_DEST_MOD128;
+		break;
+	case 128:
+		temp |= DMA_RQ_C1_DEST_MOD64;
+		break;
+	case 64:
+		temp |= DMA_RQ_C1_DEST_MOD32;
+		break;		      
+	case 32:
+		temp |= DMA_RQ_C1_DEST_MOD16;
+		break; 
+	default:
+		snd_printdd ("period size (%d) not supported by HW\n", period_size);
+		return -EINVAL;
+	}
+
+	snd_cs46xx_poke (chip,WRITEBACK_SCB_ADDR << 2,temp);
+
+	return 0;
+}
+
+void cs46xx_dsp_destroy_pcm_channel (struct snd_cs46xx * chip,
+				     struct dsp_pcm_channel_descriptor * pcm_channel)
+{
+	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
+	unsigned long flags;
+
+	snd_assert(pcm_channel->active, return );
+	snd_assert(ins->npcm_channels > 0, return );
+	snd_assert(pcm_channel->src_scb->ref_count > 0, return );
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	pcm_channel->unlinked = 1;
+	pcm_channel->active = 0;
+	pcm_channel->private_data = NULL;
+	pcm_channel->src_scb->ref_count --;
+	ins->npcm_channels --;
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+	cs46xx_dsp_remove_scb(chip,pcm_channel->pcm_reader_scb);
+
+	if (!pcm_channel->src_scb->ref_count) {
+		cs46xx_dsp_remove_scb(chip,pcm_channel->src_scb);
+
+		snd_assert (pcm_channel->src_slot >= 0 && pcm_channel->src_slot <= DSP_MAX_SRC_NR,
+			    return );
+
+		ins->src_scb_slots[pcm_channel->src_slot] = 0;
+		ins->nsrc_scb --;
+	}
+}
+
+int cs46xx_dsp_pcm_unlink (struct snd_cs46xx * chip,
+			   struct dsp_pcm_channel_descriptor * pcm_channel)
+{
+	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
+	unsigned long flags;
+
+	snd_assert(pcm_channel->active,return -EIO);
+	snd_assert(ins->npcm_channels > 0,return -EIO);
+
+	spin_lock(&pcm_channel->src_scb->lock);
+
+	if (pcm_channel->unlinked) {
+		spin_unlock(&pcm_channel->src_scb->lock);
+		return -EIO;
+	}
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	pcm_channel->unlinked = 1;
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+	_dsp_unlink_scb (chip,pcm_channel->pcm_reader_scb);
+
+	spin_unlock(&pcm_channel->src_scb->lock);
+	return 0;
+}
+
+int cs46xx_dsp_pcm_link (struct snd_cs46xx * chip,
+			 struct dsp_pcm_channel_descriptor * pcm_channel)
+{
+	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
+	struct dsp_scb_descriptor * parent_scb;
+	struct dsp_scb_descriptor * src_scb = pcm_channel->src_scb;
+	unsigned long flags;
+
+	spin_lock(&pcm_channel->src_scb->lock);
+
+	if (pcm_channel->unlinked == 0) {
+		spin_unlock(&pcm_channel->src_scb->lock);
+		return -EIO;
+	}
+
+	parent_scb = src_scb;
+
+	if (src_scb->sub_list_ptr != ins->the_null_scb) {
+		src_scb->sub_list_ptr->parent_scb_ptr = pcm_channel->pcm_reader_scb;
+		pcm_channel->pcm_reader_scb->next_scb_ptr = src_scb->sub_list_ptr;
+	}
+
+	src_scb->sub_list_ptr = pcm_channel->pcm_reader_scb;
+
+	snd_assert (pcm_channel->pcm_reader_scb->parent_scb_ptr == NULL, ; );
+	pcm_channel->pcm_reader_scb->parent_scb_ptr = parent_scb;
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+
+	/* update SCB entry in DSP RAM */
+	cs46xx_dsp_spos_update_scb(chip,pcm_channel->pcm_reader_scb);
+
+	/* update parent SCB entry */
+	cs46xx_dsp_spos_update_scb(chip,parent_scb);
+
+	pcm_channel->unlinked = 0;
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+	spin_unlock(&pcm_channel->src_scb->lock);
+	return 0;
+}
+
+struct dsp_scb_descriptor *
+cs46xx_add_record_source (struct snd_cs46xx *chip, struct dsp_scb_descriptor * source,
+			  u16 addr, char * scb_name)
+{
+  	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
+	struct dsp_scb_descriptor * parent;
+	struct dsp_scb_descriptor * pcm_input;
+	int insert_point;
+
+	snd_assert (ins->record_mixer_scb != NULL,return NULL);
+
+	if (ins->record_mixer_scb->sub_list_ptr != ins->the_null_scb) {
+		parent = find_next_free_scb (chip,ins->record_mixer_scb->sub_list_ptr);
+		insert_point = SCB_ON_PARENT_NEXT_SCB;
+	} else {
+		parent = ins->record_mixer_scb;
+		insert_point = SCB_ON_PARENT_SUBLIST_SCB;
+	}
+
+	pcm_input = cs46xx_dsp_create_pcm_serial_input_scb(chip,scb_name,addr,
+							   source, parent,
+							   insert_point);
+
+	return pcm_input;
+}
+
+int cs46xx_src_unlink(struct snd_cs46xx *chip, struct dsp_scb_descriptor * src)
+{
+	snd_assert (src->parent_scb_ptr != NULL,  return -EINVAL );
+
+	/* mute SCB */
+	cs46xx_dsp_scb_set_volume (chip,src,0,0);
+
+	_dsp_unlink_scb (chip,src);
+
+	return 0;
+}
+
+int cs46xx_src_link(struct snd_cs46xx *chip, struct dsp_scb_descriptor * src)
+{
+	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
+	struct dsp_scb_descriptor * parent_scb;
+
+	snd_assert (src->parent_scb_ptr == NULL,   return -EINVAL );
+	snd_assert(ins->master_mix_scb !=NULL,   return -EINVAL );
+
+	if (ins->master_mix_scb->sub_list_ptr != ins->the_null_scb) {
+		parent_scb = find_next_free_scb (chip,ins->master_mix_scb->sub_list_ptr);
+		parent_scb->next_scb_ptr = src;
+	} else {
+		parent_scb = ins->master_mix_scb;
+		parent_scb->sub_list_ptr = src;
+	}
+
+	src->parent_scb_ptr = parent_scb;
+
+	/* update entry in DSP RAM */
+	cs46xx_dsp_spos_update_scb(chip,parent_scb);
+  
+	return 0;
+}
+
+int cs46xx_dsp_enable_spdif_out (struct snd_cs46xx *chip)
+{
+	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
+
+	if ( ! (ins->spdif_status_out & DSP_SPDIF_STATUS_HW_ENABLED) ) {
+		cs46xx_dsp_enable_spdif_hw (chip);
+	}
+
+	/* dont touch anything if SPDIF is open */
+	if ( ins->spdif_status_out & DSP_SPDIF_STATUS_PLAYBACK_OPEN) {
+		/* when cs46xx_iec958_post_close(...) is called it
+		   will call this function if necessary depending on
+		   this bit */
+		ins->spdif_status_out |= DSP_SPDIF_STATUS_OUTPUT_ENABLED;
+
+		return -EBUSY;
+	}
+
+	snd_assert (ins->asynch_tx_scb == NULL, return -EINVAL);
+	snd_assert (ins->master_mix_scb->next_scb_ptr == ins->the_null_scb, return -EINVAL);
+
+	/* reset output snooper sample buffer pointer */
+	snd_cs46xx_poke (chip, (ins->ref_snoop_scb->address + 2) << 2,
+			 (OUTPUT_SNOOP_BUFFER + 0x10) << 0x10 );
+	
+	/* The asynch. transfer task */
+	ins->asynch_tx_scb = cs46xx_dsp_create_asynch_fg_tx_scb(chip,"AsynchFGTxSCB",ASYNCTX_SCB_ADDR,
+								SPDIFO_SCB_INST,
+								SPDIFO_IP_OUTPUT_BUFFER1,
+								ins->master_mix_scb,
+								SCB_ON_PARENT_NEXT_SCB);
+	if (!ins->asynch_tx_scb) return -ENOMEM;
+
+	ins->spdif_pcm_input_scb = cs46xx_dsp_create_pcm_serial_input_scb(chip,"PCMSerialInput_II",
+									  PCMSERIALINII_SCB_ADDR,
+									  ins->ref_snoop_scb,
+									  ins->asynch_tx_scb,
+									  SCB_ON_PARENT_SUBLIST_SCB);
+  
+	
+	if (!ins->spdif_pcm_input_scb) return -ENOMEM;
+
+	/* monitor state */
+	ins->spdif_status_out |= DSP_SPDIF_STATUS_OUTPUT_ENABLED;
+
+	return 0;
+}
+
+int  cs46xx_dsp_disable_spdif_out (struct snd_cs46xx *chip)
+{
+	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
+
+	/* dont touch anything if SPDIF is open */
+	if ( ins->spdif_status_out & DSP_SPDIF_STATUS_PLAYBACK_OPEN) {
+		ins->spdif_status_out &= ~DSP_SPDIF_STATUS_OUTPUT_ENABLED;
+		return -EBUSY;
+	}
+
+	/* check integrety */
+	snd_assert (ins->asynch_tx_scb != NULL, return -EINVAL);
+	snd_assert (ins->spdif_pcm_input_scb != NULL,return -EINVAL);
+	snd_assert (ins->master_mix_scb->next_scb_ptr == ins->asynch_tx_scb, return -EINVAL);
+	snd_assert (ins->asynch_tx_scb->parent_scb_ptr == ins->master_mix_scb, return -EINVAL);
+
+	cs46xx_dsp_remove_scb (chip,ins->spdif_pcm_input_scb);
+	cs46xx_dsp_remove_scb (chip,ins->asynch_tx_scb);
+
+	ins->spdif_pcm_input_scb = NULL;
+	ins->asynch_tx_scb = NULL;
+
+	/* clear buffer to prevent any undesired noise */
+	_dsp_clear_sample_buffer(chip,SPDIFO_IP_OUTPUT_BUFFER1,256);
+
+	/* monitor state */
+	ins->spdif_status_out  &= ~DSP_SPDIF_STATUS_OUTPUT_ENABLED;
+
+
+	return 0;
+}
+
+int cs46xx_iec958_pre_open (struct snd_cs46xx *chip)
+{
+	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
+
+	if ( ins->spdif_status_out & DSP_SPDIF_STATUS_OUTPUT_ENABLED ) {
+		/* remove AsynchFGTxSCB and and PCMSerialInput_II */
+		cs46xx_dsp_disable_spdif_out (chip);
+
+		/* save state */
+		ins->spdif_status_out |= DSP_SPDIF_STATUS_OUTPUT_ENABLED;
+	}
+	
+	/* if not enabled already */
+	if ( !(ins->spdif_status_out & DSP_SPDIF_STATUS_HW_ENABLED) ) {
+		cs46xx_dsp_enable_spdif_hw (chip);
+	}
+
+	/* Create the asynch. transfer task  for playback */
+	ins->asynch_tx_scb = cs46xx_dsp_create_asynch_fg_tx_scb(chip,"AsynchFGTxSCB",ASYNCTX_SCB_ADDR,
+								SPDIFO_SCB_INST,
+								SPDIFO_IP_OUTPUT_BUFFER1,
+								ins->master_mix_scb,
+								SCB_ON_PARENT_NEXT_SCB);
+
+
+	/* set spdif channel status value for streaming */
+	cs46xx_poke_via_dsp (chip,SP_SPDOUT_CSUV, ins->spdif_csuv_stream);
+
+	ins->spdif_status_out  |= DSP_SPDIF_STATUS_PLAYBACK_OPEN;
+
+	return 0;
+}
+
+int cs46xx_iec958_post_close (struct snd_cs46xx *chip)
+{
+	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
+
+	snd_assert (ins->asynch_tx_scb != NULL, return -EINVAL);
+
+	ins->spdif_status_out  &= ~DSP_SPDIF_STATUS_PLAYBACK_OPEN;
+
+	/* restore settings */
+	cs46xx_poke_via_dsp (chip,SP_SPDOUT_CSUV, ins->spdif_csuv_default);
+	
+	/* deallocate stuff */
+	if (ins->spdif_pcm_input_scb != NULL) {
+		cs46xx_dsp_remove_scb (chip,ins->spdif_pcm_input_scb);
+		ins->spdif_pcm_input_scb = NULL;
+	}
+
+	cs46xx_dsp_remove_scb (chip,ins->asynch_tx_scb);
+	ins->asynch_tx_scb = NULL;
+
+	/* clear buffer to prevent any undesired noise */
+	_dsp_clear_sample_buffer(chip,SPDIFO_IP_OUTPUT_BUFFER1,256);
+
+	/* restore state */
+	if ( ins->spdif_status_out & DSP_SPDIF_STATUS_OUTPUT_ENABLED ) {
+		cs46xx_dsp_enable_spdif_out (chip);
+	}
+	
+	return 0;
+}
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/cs46xx/imgs/CVS/Entries
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/cs46xx/imgs/CVS/Entries	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/cs46xx/imgs/CVS/Entries	(revision 91)
@@ -0,0 +1,8 @@
+/cwc4630.h/1.1/Thu Aug  1 09:26:32 2002/-ko/
+/cwcasync.h/1.1/Thu Aug  1 09:26:32 2002/-ko/
+/cwcbinhack.h/1.1/Thu Aug  1 09:26:32 2002/-ko/
+/cwcemb80.h/1.1/Thu Aug  1 09:26:32 2002/-ko/
+/cwcsnoop.h/1.1/Thu Aug  1 09:26:32 2002/-ko/
+/cwcdma.asp/1.1/Mon Mar  3 12:07:18 2003/-ko/
+/cwcdma.h/1.1/Mon Mar  3 12:07:18 2003/-ko/
+D
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/cs46xx/imgs/CVS/Repository
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/cs46xx/imgs/CVS/Repository	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/cs46xx/imgs/CVS/Repository	(revision 91)
@@ -0,0 +1,1 @@
+alsa-kernel/pci/cs46xx/imgs
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/cs46xx/imgs/CVS/Root
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/cs46xx/imgs/CVS/Root	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/cs46xx/imgs/CVS/Root	(revision 91)
@@ -0,0 +1,1 @@
+:pserver:anonymous@cvs.alsa-project.org:/cvsroot/alsa
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/cs46xx/imgs/cwc4630.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/cs46xx/imgs/cwc4630.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/cs46xx/imgs/cwc4630.h	(revision 91)
@@ -0,0 +1,320 @@
+/* generated from cwc4630.osp DO NOT MODIFY */
+
+#ifndef __HEADER_cwc4630_H__
+#define __HEADER_cwc4630_H__
+
+static struct dsp_symbol_entry cwc4630_symbols[] = {
+  { 0x0000, "BEGINADDRESS",0x00 },
+  { 0x8000, "EXECCHILD",0x03 },
+  { 0x8001, "EXECCHILD_98",0x03 },
+  { 0x8003, "EXECCHILD_PUSH1IND",0x03 },
+  { 0x8008, "EXECSIBLING",0x03 },
+  { 0x800a, "EXECSIBLING_298",0x03 },
+  { 0x800b, "EXECSIBLING_2IND1",0x03 },
+  { 0x8010, "TIMINGMASTER",0x03 },
+  { 0x804f, "S16_CODECINPUTTASK",0x03 },
+  { 0x805e, "PCMSERIALINPUTTASK",0x03 },
+  { 0x806d, "S16_MIX_TO_OSTREAM",0x03 },
+  { 0x809a, "S16_MIX",0x03 },
+  { 0x80bb, "S16_UPSRC",0x03 },
+  { 0x813b, "MIX3_EXP",0x03 },
+  { 0x8164, "DECIMATEBYPOW2",0x03 },
+  { 0x8197, "VARIDECIMATE",0x03 },
+  { 0x81f2, "_3DINPUTTASK",0x03 },
+  { 0x820a, "_3DPRLGCINPTASK",0x03 },
+  { 0x8227, "_3DSTEREOINPUTTASK",0x03 },
+  { 0x8242, "_3DOUTPUTTASK",0x03 },
+  { 0x82c4, "HRTF_MORPH_TASK",0x03 },
+  { 0x82c6, "WAIT4DATA",0x03 },
+  { 0x82fa, "PROLOGIC",0x03 },
+  { 0x8496, "DECORRELATOR",0x03 },
+  { 0x84a4, "STEREO2MONO",0x03 },
+  { 0x0070, "SPOSCB",0x02 },
+  { 0x0107, "TASKTREETHREAD",0x03 },
+  { 0x013c, "TASKTREEHEADERCODE",0x03 },
+  { 0x0145, "FGTASKTREEHEADERCODE",0x03 },
+  { 0x0169, "NULLALGORITHM",0x03 },
+  { 0x016d, "HFGEXECCHILD",0x03 },
+  { 0x016e, "HFGEXECCHILD_98",0x03 },
+  { 0x0170, "HFGEXECCHILD_PUSH1IND",0x03 },
+  { 0x0173, "HFGEXECSIBLING",0x03 },
+  { 0x0175, "HFGEXECSIBLING_298",0x03 },
+  { 0x0176, "HFGEXECSIBLING_2IND1",0x03 },
+  { 0x0179, "S16_CODECOUTPUTTASK",0x03 },
+  { 0x0194, "#CODE_END",0x00 },
+}; /* cwc4630 symbols */
+
+static u32 cwc4630_code[] = {
+/* BEGINADDRESS */
+/* 0000 */ 0x00040730,0x00001002,0x000f619e,0x00001003,
+/* 0002 */ 0x00001705,0x00001400,0x000a411e,0x00001003,
+/* 0004 */ 0x00040730,0x00001002,0x000f619e,0x00001003,
+/* 0006 */ 0x00009705,0x00001400,0x000a411e,0x00001003,
+/* 0008 */ 0x00040730,0x00001002,0x000f619e,0x00001003,
+/* 000A */ 0x00011705,0x00001400,0x000a411e,0x00001003,
+/* 000C */ 0x00040730,0x00001002,0x000f619e,0x00001003,
+/* 000E */ 0x00019705,0x00001400,0x000a411e,0x00001003,
+/* 0010 */ 0x00040730,0x00001002,0x000f619e,0x00001003,
+/* 0012 */ 0x00021705,0x00001400,0x000a411e,0x00001003,
+/* 0014 */ 0x00040730,0x00001002,0x000f619e,0x00001003,
+/* 0016 */ 0x00029705,0x00001400,0x000a411e,0x00001003,
+/* 0018 */ 0x00040730,0x00001002,0x000f619e,0x00001003,
+/* 001A */ 0x00031705,0x00001400,0x000a411e,0x00001003,
+/* 001C */ 0x00040730,0x00001002,0x000f619e,0x00001003,
+/* 001E */ 0x00039705,0x00001400,0x000a411e,0x00001003,
+/* 0020 */ 0x000fe19e,0x00001003,0x0009c730,0x00001003,
+/* 0022 */ 0x0008e19c,0x00001003,0x000083c1,0x00093040,
+/* 0024 */ 0x00098730,0x00001002,0x000ee19e,0x00001003,
+/* 0026 */ 0x00009705,0x00001400,0x000a211e,0x00001003,
+/* 0028 */ 0x00098730,0x00001002,0x000ee19e,0x00001003,
+/* 002A */ 0x00011705,0x00001400,0x000a211e,0x00001003,
+/* 002C */ 0x00098730,0x00001002,0x000ee19e,0x00001003,
+/* 002E */ 0x00019705,0x00001400,0x000a211e,0x00001003,
+/* 0030 */ 0x00098730,0x00001002,0x000ee19e,0x00001003,
+/* 0032 */ 0x00021705,0x00001400,0x000a211e,0x00001003,
+/* 0034 */ 0x00098730,0x00001002,0x000ee19e,0x00001003,
+/* 0036 */ 0x00029705,0x00001400,0x000a211e,0x00001003,
+/* 0038 */ 0x00098730,0x00001002,0x000ee19e,0x00001003,
+/* 003A */ 0x00031705,0x00001400,0x000a211e,0x00001003,
+/* 003C */ 0x00098730,0x00001002,0x000ee19e,0x00001003,
+/* 003E */ 0x00039705,0x00001400,0x000a211e,0x00001003,
+/* 0040 */ 0x0001a730,0x00001008,0x000e2730,0x00001002,
+/* 0042 */ 0x0000a731,0x00001002,0x0000a731,0x00001002,
+/* 0044 */ 0x0000a731,0x00001002,0x0000a731,0x00001002,
+/* 0046 */ 0x0000a731,0x00001002,0x0000a731,0x00001002,
+/* 0048 */ 0x00000000,0x00000000,0x000f619c,0x00001003,
+/* 004A */ 0x0007f801,0x000c0000,0x00000037,0x00001000,
+/* 004C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 004E */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0050 */ 0x00000000,0x000c0000,0x00000000,0x00000000,
+/* 0052 */ 0x0000373c,0x00001000,0x00000000,0x00000000,
+/* 0054 */ 0x000ee19c,0x00001003,0x0007f801,0x000c0000,
+/* 0056 */ 0x00000037,0x00001000,0x00000000,0x00000000,
+/* 0058 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 005A */ 0x00000000,0x00000000,0x0000273c,0x00001000,
+/* 005C */ 0x00000033,0x00001000,0x000e679e,0x00001003,
+/* 005E */ 0x00007705,0x00001400,0x000ac71e,0x00001003,
+/* 0060 */ 0x00087fc1,0x000c3be0,0x0007f801,0x000c0000,
+/* 0062 */ 0x00000037,0x00001000,0x00000000,0x00000000,
+/* 0064 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0066 */ 0x00000000,0x00000000,0x0000a730,0x00001003,
+/* 0068 */ 0x00000033,0x00001000,0x0007f801,0x000c0000,
+/* 006A */ 0x00000037,0x00001000,0x00000000,0x00000000,
+/* 006C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 006E */ 0x00000000,0x00000000,0x00000000,0x000c0000,
+/* 0070 */ 0x00000032,0x00001000,0x0000273d,0x00001000,
+/* 0072 */ 0x0004a730,0x00001003,0x00000f41,0x00097140,
+/* 0074 */ 0x0000a841,0x0009b240,0x0000a0c1,0x0009f040,
+/* 0076 */ 0x0001c641,0x00093540,0x0001cec1,0x0009b5c0,
+/* 0078 */ 0x00000000,0x00000000,0x0001bf05,0x0003fc40,
+/* 007A */ 0x00002725,0x000aa400,0x00013705,0x00093a00,
+/* 007C */ 0x0000002e,0x0009d6c0,0x0002ef8a,0x00000000,
+/* 007E */ 0x00040630,0x00001004,0x0004ef0a,0x000eb785,
+/* 0080 */ 0x0003fc8a,0x00000000,0x00000000,0x000c70e0,
+/* 0082 */ 0x0007d182,0x0002c640,0x00008630,0x00001004,
+/* 0084 */ 0x000799b8,0x0002c6c0,0x00031705,0x00092240,
+/* 0086 */ 0x00039f05,0x000932c0,0x0003520a,0x00000000,
+/* 0088 */ 0x00070731,0x0000100b,0x00010705,0x000b20c0,
+/* 008A */ 0x00000000,0x000eba44,0x00032108,0x000c60c4,
+/* 008C */ 0x00065208,0x000c2917,0x000486b0,0x00001007,
+/* 008E */ 0x00012f05,0x00036880,0x0002818e,0x000c0000,
+/* 0090 */ 0x0004410a,0x00000000,0x00048630,0x00001007,
+/* 0092 */ 0x00029705,0x000c0000,0x00000000,0x00000000,
+/* 0094 */ 0x00003fc1,0x0003fc40,0x000037c1,0x00091b40,
+/* 0096 */ 0x00003fc1,0x000911c0,0x000037c1,0x000957c0,
+/* 0098 */ 0x00003fc1,0x000951c0,0x000037c1,0x00000000,
+/* 009A */ 0x00003fc1,0x000991c0,0x000037c1,0x00000000,
+/* 009C */ 0x00003fc1,0x0009d1c0,0x000037c1,0x00000000,
+/* 009E */ 0x0001ccc1,0x000915c0,0x0001c441,0x0009d800,
+/* 00A0 */ 0x0009cdc1,0x00091240,0x0001c541,0x00091d00,
+/* 00A2 */ 0x0009cfc1,0x00095240,0x0001c741,0x00095c80,
+/* 00A4 */ 0x000e8ca9,0x00099240,0x000e85ad,0x00095640,
+/* 00A6 */ 0x00069ca9,0x00099d80,0x000e952d,0x00099640,
+/* 00A8 */ 0x000eaca9,0x0009d6c0,0x000ea5ad,0x00091a40,
+/* 00AA */ 0x0006bca9,0x0009de80,0x000eb52d,0x00095a40,
+/* 00AC */ 0x000ecca9,0x00099ac0,0x000ec5ad,0x0009da40,
+/* 00AE */ 0x000edca9,0x0009d300,0x000a6e0a,0x00001000,
+/* 00B0 */ 0x000ed52d,0x00091e40,0x000eeca9,0x00095ec0,
+/* 00B2 */ 0x000ee5ad,0x00099e40,0x0006fca9,0x00002500,
+/* 00B4 */ 0x000fb208,0x000c59a0,0x000ef52d,0x0009de40,
+/* 00B6 */ 0x00068ca9,0x000912c1,0x000683ad,0x00095241,
+/* 00B8 */ 0x00020f05,0x000991c1,0x00000000,0x00000000,
+/* 00BA */ 0x00086f88,0x00001000,0x0009cf81,0x000b5340,
+/* 00BC */ 0x0009c701,0x000b92c0,0x0009de81,0x000bd300,
+/* 00BE */ 0x0009d601,0x000b1700,0x0001fd81,0x000b9d80,
+/* 00C0 */ 0x0009f501,0x000b57c0,0x000a0f81,0x000bd740,
+/* 00C2 */ 0x00020701,0x000b5c80,0x000a1681,0x000b97c0,
+/* 00C4 */ 0x00021601,0x00002500,0x000a0701,0x000b9b40,
+/* 00C6 */ 0x000a0f81,0x000b1bc0,0x00021681,0x00002d00,
+/* 00C8 */ 0x00020f81,0x000bd800,0x000a0701,0x000b5bc0,
+/* 00CA */ 0x00021601,0x00003500,0x000a0f81,0x000b5f40,
+/* 00CC */ 0x000a0701,0x000bdbc0,0x00021681,0x00003d00,
+/* 00CE */ 0x00020f81,0x000b1d00,0x000a0701,0x000b1fc0,
+/* 00D0 */ 0x00021601,0x00020500,0x00020f81,0x000b1341,
+/* 00D2 */ 0x000a0701,0x000b9fc0,0x00021681,0x00020d00,
+/* 00D4 */ 0x00020f81,0x000bde80,0x000a0701,0x000bdfc0,
+/* 00D6 */ 0x00021601,0x00021500,0x00020f81,0x000b9341,
+/* 00D8 */ 0x00020701,0x000b53c1,0x00021681,0x00021d00,
+/* 00DA */ 0x000a0f81,0x000d0380,0x0000b601,0x000b15c0,
+/* 00DC */ 0x00007b01,0x00000000,0x00007b81,0x000bd1c0,
+/* 00DE */ 0x00007b01,0x00000000,0x00007b81,0x000b91c0,
+/* 00E0 */ 0x00007b01,0x000b57c0,0x00007b81,0x000b51c0,
+/* 00E2 */ 0x00007b01,0x000b1b40,0x00007b81,0x000b11c0,
+/* 00E4 */ 0x00087b01,0x000c3dc0,0x0007e488,0x000d7e45,
+/* 00E6 */ 0x00000000,0x000d7a44,0x0007e48a,0x00000000,
+/* 00E8 */ 0x00011f05,0x00084080,0x00000000,0x00000000,
+/* 00EA */ 0x00001705,0x000b3540,0x00008a01,0x000bf040,
+/* 00EC */ 0x00007081,0x000bb5c0,0x00055488,0x00000000,
+/* 00EE */ 0x0000d482,0x0003fc40,0x0003fc88,0x00000000,
+/* 00F0 */ 0x0001e401,0x000b3a00,0x0001ec81,0x000bd6c0,
+/* 00F2 */ 0x0002ef88,0x000e7784,0x00056f08,0x00000000,
+/* 00F4 */ 0x000d86b0,0x00001007,0x00008281,0x000bb240,
+/* 00F6 */ 0x0000b801,0x000b7140,0x00007888,0x00000000,
+/* 00F8 */ 0x0000073c,0x00001000,0x0007f188,0x000c0000,
+/* 00FA */ 0x00000000,0x00000000,0x00055288,0x000c555c,
+/* 00FC */ 0x0005528a,0x000c0000,0x0009fa88,0x000c5d00,
+/* 00FE */ 0x0000fa88,0x00000000,0x00000032,0x00001000,
+/* 0100 */ 0x0000073d,0x00001000,0x0007f188,0x000c0000,
+/* 0102 */ 0x00000000,0x00000000,0x0008c01c,0x00001003,
+/* 0104 */ 0x00002705,0x00001008,0x0008b201,0x000c1392,
+/* 0106 */ 0x0000ba01,0x00000000,
+/* TASKTREETHREAD */
+/* 0107 */ 0x00008731,0x00001400,0x0004c108,0x000fe0c4,
+/* 0109 */ 0x00057488,0x00000000,0x000a6388,0x00001001,
+/* 010B */ 0x0008b334,0x000bc141,0x0003020e,0x00000000,
+/* 010D */ 0x000986b0,0x00001008,0x00003625,0x000c5dfa,
+/* 010F */ 0x000a638a,0x00001001,0x0008020e,0x00001002,
+/* 0111 */ 0x0009a6b0,0x00001008,0x0007f301,0x00000000,
+/* 0113 */ 0x00000000,0x00000000,0x00002725,0x000a8c40,
+/* 0115 */ 0x000000ae,0x00000000,0x000e8630,0x00001008,
+/* 0117 */ 0x00000000,0x000c74e0,0x0007d182,0x0002d640,
+/* 0119 */ 0x000b8630,0x00001008,0x000799b8,0x0002d6c0,
+/* 011B */ 0x0000748a,0x000c3ec5,0x0007420a,0x000c0000,
+/* 011D */ 0x00062208,0x000c4117,0x000a0630,0x00001009,
+/* 011F */ 0x00000000,0x000c0000,0x0001022e,0x00000000,
+/* 0121 */ 0x0006a630,0x00001009,0x00000032,0x00001000,
+/* 0123 */ 0x000ca21c,0x00001003,0x00005a02,0x00000000,
+/* 0125 */ 0x0001a630,0x00001009,0x00000000,0x000c0000,
+/* 0127 */ 0x00000036,0x00001000,0x00000000,0x00000000,
+/* 0129 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 012B */ 0x00000000,0x00000000,0x0003a730,0x00001008,
+/* 012D */ 0x0007f801,0x000c0000,0x00000037,0x00001000,
+/* 012F */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0131 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0133 */ 0x0003a730,0x00001008,0x00000033,0x00001000,
+/* 0135 */ 0x0003a705,0x00001008,0x00007a01,0x000c0000,
+/* 0137 */ 0x000e6288,0x000d550a,0x0006428a,0x00000000,
+/* 0139 */ 0x00090730,0x0000100a,0x00000000,0x000c0000,
+/* 013B */ 0x00000000,0x00000000,
+/* TASKTREEHEADERCODE */
+/* 013C */ 0x0007aab0,0x00034880,0x000a8fb0,0x0000100b,
+/* 013E */ 0x00057488,0x00000000,0x00033b94,0x00081140,
+/* 0140 */ 0x000183ae,0x00000000,0x000a86b0,0x0000100b,
+/* 0142 */ 0x00022f05,0x000c3545,0x0000eb8a,0x00000000,
+/* 0144 */ 0x00042731,0x00001003,
+/* FGTASKTREEHEADERCODE */
+/* 0145 */ 0x0007aab0,0x00034880,0x00078fb0,0x0000100a,
+/* 0147 */ 0x00057488,0x00000000,0x00033b94,0x00081140,
+/* 0149 */ 0x000183ae,0x00000000,0x000b06b0,0x0000100b,
+/* 014B */ 0x00022f05,0x00000000,0x00007401,0x00091140,
+/* 014D */ 0x00048f05,0x000951c0,0x00042731,0x00001003,
+/* 014F */ 0x0000473d,0x00001000,0x000f19b0,0x000bbc47,
+/* 0151 */ 0x00080000,0x000bffc7,0x000fe19e,0x00001003,
+/* 0153 */ 0x00000000,0x00000000,0x0008e19c,0x00001003,
+/* 0155 */ 0x000083c1,0x00093040,0x00000f41,0x00097140,
+/* 0157 */ 0x0000a841,0x0009b240,0x0000a0c1,0x0009f040,
+/* 0159 */ 0x0001c641,0x00093540,0x0001cec1,0x0009b5c0,
+/* 015B */ 0x00000000,0x000fdc44,0x00055208,0x00000000,
+/* 015D */ 0x00010705,0x000a2880,0x0000a23a,0x00093a00,
+/* 015F */ 0x0003fc8a,0x000df6c5,0x0004ef0a,0x000c0000,
+/* 0161 */ 0x00012f05,0x00036880,0x00065308,0x000c2997,
+/* 0163 */ 0x000086b0,0x0000100b,0x0004410a,0x000d40c7,
+/* 0165 */ 0x00000000,0x00000000,0x00088730,0x00001004,
+/* 0167 */ 0x00056f0a,0x000ea105,0x00000000,0x00000000,
+/* NULLALGORITHM */
+/* 0169 */ 0x0000473d,0x00001000,0x000f19b0,0x000bbc47,
+/* 016B */ 0x00080000,0x000bffc7,0x0000273d,0x00001000,
+/* HFGEXECCHILD */
+/* 016D */ 0x00000000,0x000eba44,
+/* HFGEXECCHILD_98 */
+/* 016E */ 0x00048f05,0x0000f440,0x00007401,0x0000f7c0,
+/* HFGEXECCHILD_PUSH1IND */
+/* 0170 */ 0x00000734,0x00001000,0x00010705,0x000a6880,
+/* 0172 */ 0x00006a88,0x000c75c4,
+/* HFGEXECSIBLING */
+/* 0173 */ 0x00000000,0x000e5084,0x00000000,0x000eba44,
+/* HFGEXECSIBLING_298 */
+/* 0175 */ 0x00087401,0x000e4782,
+/* HFGEXECSIBLING_2IND1 */
+/* 0176 */ 0x00000734,0x00001000,0x00010705,0x000a6880,
+/* 0178 */ 0x00006a88,0x000c75c4,
+/* S16_CODECOUTPUTTASK */
+/* 0179 */ 0x0007c108,0x000c0000,0x0007e721,0x000bed40,
+/* 017B */ 0x00005f25,0x000badc0,0x0003ba97,0x000beb80,
+/* 017D */ 0x00065590,0x000b2e00,0x00033217,0x00003ec0,
+/* 017F */ 0x00065590,0x000b8e40,0x0003ed80,0x000491c0,
+/* 0181 */ 0x00073fb0,0x00074c80,0x000583a0,0x0000100c,
+/* 0183 */ 0x000ee388,0x00042970,0x00008301,0x00021ef2,
+/* 0185 */ 0x000b8f14,0x0000000f,0x000c4d8d,0x0000001b,
+/* 0187 */ 0x000d6dc2,0x000e06c6,0x000032ac,0x000c3916,
+/* 0189 */ 0x0004edc2,0x00074c80,0x00078898,0x00001000,
+/* 018B */ 0x00038894,0x00000032,0x000c4d8d,0x00092e1b,
+/* 018D */ 0x000d6dc2,0x000e06c6,0x0004edc2,0x000c1956,
+/* 018F */ 0x0000722c,0x00034a00,0x00041705,0x0009ed40,
+/* 0191 */ 0x00058730,0x00001400,0x000d7488,0x000c3a00,
+/* 0193 */ 0x00048f05,0x00000000
+};
+/* #CODE_END */
+
+static u32 cwc4630_parameter[] = {
+/* 0000 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0004 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0008 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 000C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0010 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0014 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0018 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 001C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0020 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0024 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0028 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 002C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0030 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0034 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0038 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 003C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0040 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0044 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0048 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 004C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0050 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0054 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0058 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 005C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0060 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0064 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0068 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 006C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0070 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0074 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0078 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 007C */ 0x00000000,0x00000000,0x00000000,0x00000000
+}; /* #PARAMETER_END */
+
+
+static struct dsp_segment_desc cwc4630_segments[] = {
+  { SEGTYPE_SP_PROGRAM, 0x00000000, 0x00000328, cwc4630_code },
+  { SEGTYPE_SP_PARAMETER, 0x00000000, 0x00000080, cwc4630_parameter },
+};
+
+static struct dsp_module_desc cwc4630_module = {
+  "cwc4630",
+  {
+    38,
+    cwc4630_symbols
+  },
+  2,
+  cwc4630_segments,
+};
+
+#endif /* __HEADER_cwc4630_H__ */
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/cs46xx/imgs/cwcasync.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/cs46xx/imgs/cwcasync.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/cs46xx/imgs/cwcasync.h	(revision 91)
@@ -0,0 +1,176 @@
+/* generated from cwcasync.osp DO NOT MODIFY */
+
+#ifndef __HEADER_cwcasync_H__
+#define __HEADER_cwcasync_H__
+
+static struct dsp_symbol_entry cwcasync_symbols[] = {
+  { 0x8000, "EXECCHILD",0x03 },
+  { 0x8001, "EXECCHILD_98",0x03 },
+  { 0x8003, "EXECCHILD_PUSH1IND",0x03 },
+  { 0x8008, "EXECSIBLING",0x03 },
+  { 0x800a, "EXECSIBLING_298",0x03 },
+  { 0x800b, "EXECSIBLING_2IND1",0x03 },
+  { 0x8010, "TIMINGMASTER",0x03 },
+  { 0x804f, "S16_CODECINPUTTASK",0x03 },
+  { 0x805e, "PCMSERIALINPUTTASK",0x03 },
+  { 0x806d, "S16_MIX_TO_OSTREAM",0x03 },
+  { 0x809a, "S16_MIX",0x03 },
+  { 0x80bb, "S16_UPSRC",0x03 },
+  { 0x813b, "MIX3_EXP",0x03 },
+  { 0x8164, "DECIMATEBYPOW2",0x03 },
+  { 0x8197, "VARIDECIMATE",0x03 },
+  { 0x81f2, "_3DINPUTTASK",0x03 },
+  { 0x820a, "_3DPRLGCINPTASK",0x03 },
+  { 0x8227, "_3DSTEREOINPUTTASK",0x03 },
+  { 0x8242, "_3DOUTPUTTASK",0x03 },
+  { 0x82c4, "HRTF_MORPH_TASK",0x03 },
+  { 0x82c6, "WAIT4DATA",0x03 },
+  { 0x82fa, "PROLOGIC",0x03 },
+  { 0x8496, "DECORRELATOR",0x03 },
+  { 0x84a4, "STEREO2MONO",0x03 },
+  { 0x0000, "OVERLAYBEGINADDRESS",0x00 },
+  { 0x0000, "SPIOWRITE",0x03 },
+  { 0x000d, "S16_ASYNCCODECINPUTTASK",0x03 },
+  { 0x0043, "SPDIFITASK",0x03 },
+  { 0x007b, "SPDIFOTASK",0x03 },
+  { 0x0097, "ASYNCHFGTXCODE",0x03 },
+  { 0x00be, "ASYNCHFGRXCODE",0x03 },
+  { 0x00db, "#CODE_END",0x00 },
+}; /* cwcasync symbols */
+
+static u32 cwcasync_code[] = {
+/* OVERLAYBEGINADDRESS */
+/* 0000 */ 0x00002731,0x00001400,0x00003725,0x000a8440,
+/* 0002 */ 0x000000ae,0x00000000,0x00060630,0x00001000,
+/* 0004 */ 0x00000000,0x000c7560,0x00075282,0x0002d640,
+/* 0006 */ 0x00021705,0x00000000,0x00072ab8,0x0002d6c0,
+/* 0008 */ 0x00020630,0x00001000,0x000c74c2,0x000d4b82,
+/* 000A */ 0x000475c2,0x00000000,0x0003430a,0x000c0000,
+/* 000C */ 0x00042730,0x00001400,
+/* S16_ASYNCCODECINPUTTASK */
+/* 000D */ 0x0006a108,0x000cf2c4,0x0004f4c0,0x00000000,
+/* 000F */ 0x000fa418,0x0000101f,0x0005d402,0x0001c500,
+/* 0011 */ 0x000f0630,0x00001000,0x00004418,0x00001380,
+/* 0013 */ 0x000e243d,0x000d394a,0x00049705,0x00000000,
+/* 0015 */ 0x0007d530,0x000b4240,0x000e00f2,0x00001000,
+/* 0017 */ 0x00009134,0x000ca20a,0x00004c90,0x00001000,
+/* 0019 */ 0x0005d705,0x00000000,0x00004f25,0x00098240,
+/* 001B */ 0x00004725,0x00000000,0x0000e48a,0x00000000,
+/* 001D */ 0x00027295,0x0009c2c0,0x0003df25,0x00000000,
+/* 001F */ 0x000e8030,0x00001001,0x0005f718,0x000ac600,
+/* 0021 */ 0x0007cf30,0x000c2a01,0x00082630,0x00001001,
+/* 0023 */ 0x000504a0,0x00001001,0x00029314,0x000bcb80,
+/* 0025 */ 0x0003cf25,0x000b0e00,0x0004f5c0,0x00000000,
+/* 0027 */ 0x00049118,0x000d888a,0x0007dd02,0x000c6efa,
+/* 0029 */ 0x00000000,0x00000000,0x0004f5c0,0x00069c80,
+/* 002B */ 0x0000d402,0x00000000,0x000e8630,0x00001001,
+/* 002D */ 0x00079130,0x00000000,0x00049118,0x00090e00,
+/* 002F */ 0x0006c10a,0x00000000,0x00000000,0x000c0000,
+/* 0031 */ 0x0007cf30,0x00030580,0x00005725,0x00000000,
+/* 0033 */ 0x000d84a0,0x00001001,0x00029314,0x000b4780,
+/* 0035 */ 0x0003cf25,0x000b8600,0x00000000,0x00000000,
+/* 0037 */ 0x00000000,0x000c0000,0x00000000,0x00042c80,
+/* 0039 */ 0x0001dec1,0x000e488c,0x00031114,0x00000000,
+/* 003B */ 0x0004f5c2,0x00000000,0x0003640a,0x00000000,
+/* 003D */ 0x00000000,0x000e5084,0x00000000,0x000eb844,
+/* 003F */ 0x00007001,0x00000000,0x00000734,0x00001000,
+/* 0041 */ 0x00010705,0x000a6880,0x00006a88,0x000c75c4,
+/* SPDIFITASK */
+/* 0043 */ 0x0006a108,0x000cf2c4,0x0004f4c0,0x000d5384,
+/* 0045 */ 0x0007e48a,0x00000000,0x00067718,0x00001000,
+/* 0047 */ 0x0007a418,0x00001000,0x0007221a,0x00000000,
+/* 0049 */ 0x0005d402,0x00014500,0x000b8630,0x00001002,
+/* 004B */ 0x00004418,0x00001780,0x000e243d,0x000d394a,
+/* 004D */ 0x00049705,0x00000000,0x0007d530,0x000b4240,
+/* 004F */ 0x000ac0f2,0x00001002,0x00014414,0x00000000,
+/* 0051 */ 0x00004c90,0x00001000,0x0005d705,0x00000000,
+/* 0053 */ 0x00004f25,0x00098240,0x00004725,0x00000000,
+/* 0055 */ 0x0000e48a,0x00000000,0x00027295,0x0009c2c0,
+/* 0057 */ 0x0007df25,0x00000000,0x000ac030,0x00001003,
+/* 0059 */ 0x0005f718,0x000fe798,0x00029314,0x000bcb80,
+/* 005B */ 0x00000930,0x000b0e00,0x0004f5c0,0x000de204,
+/* 005D */ 0x000884a0,0x00001003,0x0007cf25,0x000e3560,
+/* 005F */ 0x00049118,0x00000000,0x00049118,0x000d888a,
+/* 0061 */ 0x0007dd02,0x000c6efa,0x0000c434,0x00030040,
+/* 0063 */ 0x000fda82,0x000c2312,0x000fdc0e,0x00001001,
+/* 0065 */ 0x00083402,0x000c2b92,0x000706b0,0x00001003,
+/* 0067 */ 0x00075a82,0x00000000,0x0000d625,0x000b0940,
+/* 0069 */ 0x0000840e,0x00001002,0x0000aabc,0x000c511e,
+/* 006B */ 0x00078730,0x00001003,0x0000aaf4,0x000e910a,
+/* 006D */ 0x0004628a,0x00000000,0x00006aca,0x00000000,
+/* 006F */ 0x00000930,0x00000000,0x0004f5c0,0x00069c80,
+/* 0071 */ 0x00046ac0,0x00000000,0x0003c40a,0x000fc898,
+/* 0073 */ 0x00049118,0x00090e00,0x0006c10a,0x00000000,
+/* 0075 */ 0x00000000,0x000e5084,0x00000000,0x000eb844,
+/* 0077 */ 0x00007001,0x00000000,0x00000734,0x00001000,
+/* 0079 */ 0x00010705,0x000a6880,0x00006a88,0x000c75c4,
+/* SPDIFOTASK */
+/* 007B */ 0x0006a108,0x000c0000,0x0004f4c0,0x000c3245,
+/* 007D */ 0x0000a418,0x00001000,0x0003a20a,0x00000000,
+/* 007F */ 0x00004418,0x00001380,0x000e243d,0x000d394a,
+/* 0081 */ 0x000c9705,0x000def92,0x0008c030,0x00001004,
+/* 0083 */ 0x0005f718,0x000fe798,0x00000000,0x000c0000,
+/* 0085 */ 0x00005725,0x00000000,0x000704a0,0x00001004,
+/* 0087 */ 0x00029314,0x000b4780,0x0003cf25,0x000b8600,
+/* 0089 */ 0x00000000,0x00000000,0x00000000,0x000c0000,
+/* 008B */ 0x00000000,0x00042c80,0x0001dec1,0x000e488c,
+/* 008D */ 0x00031114,0x00000000,0x0004f5c2,0x00000000,
+/* 008F */ 0x0004a918,0x00098600,0x0006c28a,0x00000000,
+/* 0091 */ 0x00000000,0x000e5084,0x00000000,0x000eb844,
+/* 0093 */ 0x00007001,0x00000000,0x00000734,0x00001000,
+/* 0095 */ 0x00010705,0x000a6880,0x00006a88,0x000c75c4,
+/* ASYNCHFGTXCODE */
+/* 0097 */ 0x0002a880,0x000b4e40,0x00042214,0x000e5548,
+/* 0099 */ 0x000542bf,0x00000000,0x00000000,0x000481c0,
+/* 009B */ 0x00000000,0x00000000,0x00000000,0x00000030,
+/* 009D */ 0x0000072d,0x000fbf8a,0x00077f94,0x000ea7df,
+/* 009F */ 0x0002ac95,0x000d3145,0x00002731,0x00001400,
+/* 00A1 */ 0x00006288,0x000c71c4,0x00014108,0x000e6044,
+/* 00A3 */ 0x00035408,0x00000000,0x00025418,0x000a0ec0,
+/* 00A5 */ 0x0001443d,0x000ca21e,0x00046595,0x000d730c,
+/* 00A7 */ 0x0006538e,0x00000000,0x00064630,0x00001005,
+/* 00A9 */ 0x000e7b0e,0x000df782,0x000746b0,0x00001005,
+/* 00AB */ 0x00036f05,0x000c0000,0x00043695,0x000d598c,
+/* 00AD */ 0x0005331a,0x000f2185,0x00000000,0x00000000,
+/* 00AF */ 0x000007ae,0x000bdb00,0x00040630,0x00001400,
+/* 00B1 */ 0x0005e708,0x000c0000,0x0007ef30,0x000b1c00,
+/* 00B3 */ 0x000d86a0,0x00001005,0x00066408,0x000c0000,
+/* 00B5 */ 0x00000000,0x00000000,0x00021843,0x00000000,
+/* 00B7 */ 0x00000cac,0x00062c00,0x00001dac,0x00063400,
+/* 00B9 */ 0x00002cac,0x0006cc80,0x000db943,0x000e5ca1,
+/* 00BB */ 0x00000000,0x00000000,0x0006680a,0x000f3205,
+/* 00BD */ 0x00042730,0x00001400,
+/* ASYNCHFGRXCODE */
+/* 00BE */ 0x00014108,0x000f2204,0x00025418,0x000a2ec0,
+/* 00C0 */ 0x00015dbd,0x00038100,0x00015dbc,0x00000000,
+/* 00C2 */ 0x0005e415,0x00034880,0x0001258a,0x000d730c,
+/* 00C4 */ 0x0006538e,0x000baa40,0x00060630,0x00001006,
+/* 00C6 */ 0x00067b0e,0x000ac380,0x0003ef05,0x00000000,
+/* 00C8 */ 0x0000f734,0x0001c300,0x000586b0,0x00001400,
+/* 00CA */ 0x000b6f05,0x000c3a00,0x00048f05,0x00000000,
+/* 00CC */ 0x0005b695,0x0008c380,0x0002058e,0x00000000,
+/* 00CE */ 0x000500b0,0x00001400,0x0002b318,0x000e998d,
+/* 00D0 */ 0x0006430a,0x00000000,0x00000000,0x000ef384,
+/* 00D2 */ 0x00004725,0x000c0000,0x00000000,0x000f3204,
+/* 00D4 */ 0x00004f25,0x000c0000,0x00080000,0x000e5ca1,
+/* 00D6 */ 0x000cb943,0x000e5ca1,0x0004b943,0x00000000,
+/* 00D8 */ 0x00040730,0x00001400,0x000cb943,0x000e5ca1,
+/* 00DA */ 0x0004b943,0x00000000
+};
+/* #CODE_END */
+
+static struct dsp_segment_desc cwcasync_segments[] = {
+  { SEGTYPE_SP_PROGRAM, 0x00000000, 0x000001b6, cwcasync_code },
+};
+
+static struct dsp_module_desc cwcasync_module = {
+  "cwcasync",
+  {
+    32,
+    cwcasync_symbols
+  },
+  1,
+  cwcasync_segments,
+};
+
+#endif /* __HEADER_cwcasync_H__ */
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/cs46xx/imgs/cwcbinhack.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/cs46xx/imgs/cwcbinhack.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/cs46xx/imgs/cwcbinhack.h	(revision 91)
@@ -0,0 +1,48 @@
+/* generated by Benny 
+   MODIFY ON YOUR OWN RISK */
+
+#ifndef __HEADER_cwcbinhack_H__
+#define __HEADER_cwcbinhack_H__
+
+static struct dsp_symbol_entry cwcbinhack_symbols[] = {
+  { 0x02c8, "OVERLAYBEGINADDRESS",0x00 },
+  { 0x02c8, "MAGICSNOOPTASK",0x03 },
+  { 0x0308, "#CODE_END",0x00 },
+}; /* cwcbinhack symbols */
+
+static u32 cwcbinhack_code[] = {
+  /* 0x02c8 */
+  0x0007bfb0,0x000bc240,0x00000c2e,0x000c6084, /* 1 */
+  0x000b8630,0x00001016,0x00006408,0x000efb84, /* 2 */
+  0x00016008,0x00000000,0x0001c088,0x000c0000, /* 3 */
+  0x000fc908,0x000e3392,0x0005f488,0x000efb84, /* 4 */
+  0x0001d402,0x000b2e00,0x0003d418,0x00001000, /* 5 */
+  0x0008d574,0x000c4293,0x00065625,0x000ea30e, /* 6 */
+  0x00096c01,0x000c6f92,0x0001a58a,0x000c6085, /* 7 */
+  0x00002f43,0x00000000,0x000e03a0,0x00001016, /* 8 */
+  0x0005e608,0x000c0000,0x00000000,0x00000000, /* 9 */
+  0x000ca108,0x000dcca1,0x00003bac,0x000c3205, /* 10 */
+  0x00073843,0x00000000,0x00010730,0x00001017, /* 11 */
+  0x0001600a,0x000c0000,0x00057488,0x00000000, /* 12 */
+  0x00000000,0x000e5084,0x00000000,0x000eba44, /* 13 */
+  0x00087401,0x000e4782,0x00000734,0x00001000, /* 14 */
+  0x00010705,0x000a6880,0x00006a88,0x000c75c4, /* 15 */
+  0x00000000,0x00000000,0x00000000,0x00000000, /* 16 */
+};
+/* #CODE_END */
+
+static struct dsp_segment_desc cwcbinhack_segments[] = {
+  { SEGTYPE_SP_PROGRAM, 0x00000000, 64, cwcbinhack_code },
+};
+
+static struct dsp_module_desc cwcbinhack_module = {
+  "cwcbinhack",
+  {
+    3,
+    cwcbinhack_symbols
+  },
+  1,
+  cwcbinhack_segments,
+};
+
+#endif /* __HEADER_cwcbinhack_H__ */
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/cs46xx/imgs/cwcdma.asp
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/cs46xx/imgs/cwcdma.asp	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/cs46xx/imgs/cwcdma.asp	(revision 91)
@@ -0,0 +1,169 @@
+// 
+//  Copyright(c) by Benny Sjostrand (benny@hostmobility.com)
+//
+//  This program is free software; you can redistribute it and/or modify
+//  it under the terms of the GNU General Public License as published by
+//  the Free Software Foundation; either version 2 of the License, or
+//  (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+
+
+//
+// This code runs inside the DSP (cs4610, cs4612, cs4624, or cs4630),
+// to compile it you need a tool named SPASM 3.0 and DSP code owned by 
+// Cirrus Logic(R). The SPASM program will generate a object file (cwcdma.osp),
+// the "ospparser"  tool will genereate the cwcdma.h file it's included from
+// the cs46xx_lib.c file.
+//
+//
+// The purpose of this code is very simple: make it possible to tranfser
+// the samples 'as they are' with no alteration from a PCMreader SCB (DMA from host)
+// to any other SCB. This is useful for AC3 throug SPDIF. SRC (source rate converters) 
+// task always alters the samples in some how, however it's from 48khz -> 48khz. The
+// alterations are not audible, but AC3 wont work. 
+//
+//        ...
+//         |
+// +---------------+
+// | AsynchFGTxSCB |
+// +---------------+
+//        |
+//    subListPtr
+//        |
+// +--------------+
+// |   DMAReader  |
+// +--------------+
+//        |
+//    subListPtr
+//        |
+// +-------------+
+// | PCMReader   |
+// +-------------+
+// (DMA from host)
+//
+
+struct dmaSCB
+  {
+    long  dma_reserved1[3];
+
+    short dma_reserved2:dma_outBufPtr;
+
+    short dma_unused1:dma_unused2;
+
+    long  dma_reserved3[4];
+
+    short dma_subListPtr:dma_nextSCB;
+    short dma_SPBptr:dma_entryPoint;
+
+    long  dma_strmRsConfig;
+    long  dma_strmBufPtr;
+
+    long  dma_reserved4;
+
+    VolumeControl s2m_volume;
+  };
+
+#export DMAReader
+void DMAReader()
+{
+  execChild();
+  r2 = r0->dma_subListPtr;
+  r1 = r0->nextSCB;
+	
+  rsConfig01 = r2->strmRsConfig;
+  // Load rsConfig for input buffer
+
+  rsDMA01 = r2->basicReq.daw,       ,                   tb = Z(0 - rf);
+  // Load rsDMA in case input buffer is a DMA buffer    Test to see if there is any data to transfer
+
+  if (tb) goto execSibling_2ind1 after {
+      r5 = rf + (-1);
+      r6 = r1->dma_entryPoint;           // r6 = entry point of sibling task
+      r1 = r1->dma_SPBptr,               // r1 = pointer to sibling task's SPB
+          ,   ind = r6;                  // Load entry point of sibling task
+  }
+
+  rsConfig23 = r0->dma_strmRsConfig;
+  // Load rsConfig for output buffer (never a DMA buffer)
+
+  r4 = r0->dma_outBufPtr;
+
+  rsa0 = r2->strmBufPtr;
+  // rsa0 = input buffer pointer                        
+
+  for (i = r5; i >= 0; --i)
+    after {
+      rsa2 = r4;
+      // rsa2 = output buffer pointer
+
+      nop;
+      nop;
+    }
+  //*****************************
+  // TODO: cycles to this point *
+  //*****************************
+    {
+      acc0 =  (rsd0 = *rsa0++1);
+      // get sample
+
+      nop;  // Those "nop"'s are really uggly, but there's
+      nop;  // something with DSP's pipelines which I don't
+      nop;  // understand, resulting this code to fail without
+            // having those "nop"'s (Benny)
+
+      rsa0?reqDMA = r2;
+      // Trigger DMA transfer on input stream, 
+      // if needed to replenish input buffer
+
+      nop;
+      // Yet another magic "nop" to make stuff work
+
+      ,,r98 = acc0 $+>> 0;
+      // store sample in ALU
+
+      nop;
+      // latency on load register.
+      // (this one is understandable)
+
+      *rsa2++1 = r98;
+      // store sample in output buffer
+
+      nop; // The same story
+      nop; // as above again ...
+      nop;
+    }
+  // TODO: cycles per loop iteration
+
+  r2->strmBufPtr = rsa0,,   ;
+  // Update the modified buffer pointers
+
+  r4 = rsa2;
+  // Load output pointer position into r4
+
+  r2 = r0->nextSCB;
+  // Sibling task
+
+  goto execSibling_2ind1 // takes 6 cycles
+    after {
+      r98 = r2->thisSPB:entryPoint;
+      // Load child routine entry and data address 
+
+      r1 = r9;
+      // r9 is r2->thisSPB
+
+      r0->dma_outBufPtr = r4,,
+      // Store updated output buffer pointer
+
+      ind = r8;
+      // r8 is r2->entryPoint
+    }
+}
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/cs46xx/imgs/cwcdma.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/cs46xx/imgs/cwcdma.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/cs46xx/imgs/cwcdma.h	(revision 91)
@@ -0,0 +1,68 @@
+/* generated from cwcdma.osp DO NOT MODIFY */
+
+#ifndef __HEADER_cwcdma_H__
+#define __HEADER_cwcdma_H__
+
+static struct dsp_symbol_entry cwcdma_symbols[] = {
+  { 0x8000, "EXECCHILD",0x03 },
+  { 0x8001, "EXECCHILD_98",0x03 },
+  { 0x8003, "EXECCHILD_PUSH1IND",0x03 },
+  { 0x8008, "EXECSIBLING",0x03 },
+  { 0x800a, "EXECSIBLING_298",0x03 },
+  { 0x800b, "EXECSIBLING_2IND1",0x03 },
+  { 0x8010, "TIMINGMASTER",0x03 },
+  { 0x804f, "S16_CODECINPUTTASK",0x03 },
+  { 0x805e, "PCMSERIALINPUTTASK",0x03 },
+  { 0x806d, "S16_MIX_TO_OSTREAM",0x03 },
+  { 0x809a, "S16_MIX",0x03 },
+  { 0x80bb, "S16_UPSRC",0x03 },
+  { 0x813b, "MIX3_EXP",0x03 },
+  { 0x8164, "DECIMATEBYPOW2",0x03 },
+  { 0x8197, "VARIDECIMATE",0x03 },
+  { 0x81f2, "_3DINPUTTASK",0x03 },
+  { 0x820a, "_3DPRLGCINPTASK",0x03 },
+  { 0x8227, "_3DSTEREOINPUTTASK",0x03 },
+  { 0x8242, "_3DOUTPUTTASK",0x03 },
+  { 0x82c4, "HRTF_MORPH_TASK",0x03 },
+  { 0x82c6, "WAIT4DATA",0x03 },
+  { 0x82fa, "PROLOGIC",0x03 },
+  { 0x8496, "DECORRELATOR",0x03 },
+  { 0x84a4, "STEREO2MONO",0x03 },
+  { 0x0000, "OVERLAYBEGINADDRESS",0x00 },
+  { 0x0000, "DMAREADER",0x03 },
+  { 0x0018, "#CODE_END",0x00 },
+}; /* cwcdma symbols */
+
+static u32 cwcdma_code[] = {
+/* OVERLAYBEGINADDRESS */
+/* 0000 */ 0x00002731,0x00001400,0x0004c108,0x000e5044,
+/* 0002 */ 0x0005f608,0x00000000,0x000007ae,0x000be300,
+/* 0004 */ 0x00058630,0x00001400,0x0007afb0,0x000e9584,
+/* 0006 */ 0x00007301,0x000a9840,0x0005e708,0x000cd104,
+/* 0008 */ 0x00067008,0x00000000,0x000902a0,0x00001000,
+/* 000A */ 0x00012a01,0x000c0000,0x00000000,0x00000000,
+/* 000C */ 0x00021843,0x000c0000,0x00000000,0x000c0000,
+/* 000E */ 0x0000e101,0x000c0000,0x00000cac,0x00000000,
+/* 0010 */ 0x00080000,0x000e5ca1,0x00000000,0x000c0000,
+/* 0012 */ 0x00000000,0x00000000,0x00000000,0x00092c00,
+/* 0014 */ 0x000122c1,0x000e5084,0x00058730,0x00001400,
+/* 0016 */ 0x000d7488,0x000e4782,0x00007401,0x0001c100
+};
+
+/* #CODE_END */
+
+static struct dsp_segment_desc cwcdma_segments[] = {
+  { SEGTYPE_SP_PROGRAM, 0x00000000, 0x00000030, cwcdma_code },
+};
+
+static struct dsp_module_desc cwcdma_module = {
+  "cwcdma",
+  {
+    27,
+    cwcdma_symbols
+  },
+  1,
+  cwcdma_segments,
+};
+
+#endif /* __HEADER_cwcdma_H__ */
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/cs46xx/imgs/cwcemb80.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/cs46xx/imgs/cwcemb80.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/cs46xx/imgs/cwcemb80.h	(revision 91)
@@ -0,0 +1,1607 @@
+/* generated from cwcemb80.osp DO NOT MODIFY */
+
+#ifndef __HEADER_cwcemb80_H__
+#define __HEADER_cwcemb80_H__
+
+static struct dsp_symbol_entry cwcemb80_symbols[] = {
+  { 0x0000, "BEGINADDRESS",0x00 },
+  { 0x8000, "EXECCHILD",0x03 },
+  { 0x8001, "EXECCHILD_98",0x03 },
+  { 0x8003, "EXECCHILD_PUSH1IND",0x03 },
+  { 0x8008, "EXECSIBLING",0x03 },
+  { 0x800a, "EXECSIBLING_298",0x03 },
+  { 0x800b, "EXECSIBLING_2IND1",0x03 },
+  { 0x8010, "TIMINGMASTER",0x03 },
+  { 0x804f, "S16_CODECINPUTTASK",0x03 },
+  { 0x805e, "PCMSERIALINPUTTASK",0x03 },
+  { 0x806d, "S16_MIX_TO_OSTREAM",0x03 },
+  { 0x809a, "S16_MIX",0x03 },
+  { 0x80bb, "S16_UPSRC",0x03 },
+  { 0x813b, "MIX3_EXP",0x03 },
+  { 0x8164, "DECIMATEBYPOW2",0x03 },
+  { 0x8197, "VARIDECIMATE",0x03 },
+  { 0x81f2, "_3DINPUTTASK",0x03 },
+  { 0x820a, "_3DPRLGCINPTASK",0x03 },
+  { 0x8227, "_3DSTEREOINPUTTASK",0x03 },
+  { 0x8242, "_3DOUTPUTTASK",0x03 },
+  { 0x82c4, "HRTF_MORPH_TASK",0x03 },
+  { 0x82c6, "WAIT4DATA",0x03 },
+  { 0x82fa, "PROLOGIC",0x03 },
+  { 0x8496, "DECORRELATOR",0x03 },
+  { 0x84a4, "STEREO2MONO",0x03 },
+  { 0x0070, "SPOSCB",0x02 },
+  { 0x0105, "TASKTREETHREAD",0x03 },
+  { 0x0136, "TASKTREEHEADERCODE",0x03 },
+  { 0x013f, "FGTASKTREEHEADERCODE",0x03 },
+  { 0x0163, "NULLALGORITHM",0x03 },
+  { 0x0167, "HFGEXECCHILD",0x03 },
+  { 0x0168, "HFGEXECCHILD_98",0x03 },
+  { 0x016a, "HFGEXECCHILD_PUSH1IND",0x03 },
+  { 0x016d, "HFGEXECSIBLING",0x03 },
+  { 0x016f, "HFGEXECSIBLING_298",0x03 },
+  { 0x0170, "HFGEXECSIBLING_2IND1",0x03 },
+  { 0x0173, "S16_CODECOUTPUTTASK",0x03 },
+  { 0x018e, "#CODE_END",0x00 },
+}; /* cwcemb80 symbols */
+
+static u32 cwcemb80_code[] = {
+/* BEGINADDRESS */
+/* 0000 */ 0x00040730,0x00001002,0x000f619e,0x00001003,
+/* 0002 */ 0x00001705,0x00001400,0x000a411e,0x00001003,
+/* 0004 */ 0x00040730,0x00001002,0x000f619e,0x00001003,
+/* 0006 */ 0x00009705,0x00001400,0x000a411e,0x00001003,
+/* 0008 */ 0x00040730,0x00001002,0x000f619e,0x00001003,
+/* 000A */ 0x00011705,0x00001400,0x000a411e,0x00001003,
+/* 000C */ 0x00040730,0x00001002,0x000f619e,0x00001003,
+/* 000E */ 0x00019705,0x00001400,0x000a411e,0x00001003,
+/* 0010 */ 0x00040730,0x00001002,0x000f619e,0x00001003,
+/* 0012 */ 0x00021705,0x00001400,0x000a411e,0x00001003,
+/* 0014 */ 0x00040730,0x00001002,0x000f619e,0x00001003,
+/* 0016 */ 0x00029705,0x00001400,0x000a411e,0x00001003,
+/* 0018 */ 0x00040730,0x00001002,0x000f619e,0x00001003,
+/* 001A */ 0x00031705,0x00001400,0x000a411e,0x00001003,
+/* 001C */ 0x00040730,0x00001002,0x000f619e,0x00001003,
+/* 001E */ 0x00039705,0x00001400,0x000a411e,0x00001003,
+/* 0020 */ 0x000fe19e,0x00001003,0x0009c730,0x00001003,
+/* 0022 */ 0x0008e19c,0x00001003,0x000083c1,0x00093040,
+/* 0024 */ 0x00098730,0x00001002,0x000ee19e,0x00001003,
+/* 0026 */ 0x00009705,0x00001400,0x000a211e,0x00001003,
+/* 0028 */ 0x00098730,0x00001002,0x000ee19e,0x00001003,
+/* 002A */ 0x00011705,0x00001400,0x000a211e,0x00001003,
+/* 002C */ 0x00098730,0x00001002,0x000ee19e,0x00001003,
+/* 002E */ 0x00019705,0x00001400,0x000a211e,0x00001003,
+/* 0030 */ 0x00098730,0x00001002,0x000ee19e,0x00001003,
+/* 0032 */ 0x00021705,0x00001400,0x000a211e,0x00001003,
+/* 0034 */ 0x00098730,0x00001002,0x000ee19e,0x00001003,
+/* 0036 */ 0x00029705,0x00001400,0x000a211e,0x00001003,
+/* 0038 */ 0x00098730,0x00001002,0x000ee19e,0x00001003,
+/* 003A */ 0x00031705,0x00001400,0x000a211e,0x00001003,
+/* 003C */ 0x00098730,0x00001002,0x000ee19e,0x00001003,
+/* 003E */ 0x00039705,0x00001400,0x000a211e,0x00001003,
+/* 0040 */ 0x0000a730,0x00001008,0x000e2730,0x00001002,
+/* 0042 */ 0x0000a731,0x00001002,0x0000a731,0x00001002,
+/* 0044 */ 0x0000a731,0x00001002,0x0000a731,0x00001002,
+/* 0046 */ 0x0000a731,0x00001002,0x0000a731,0x00001002,
+/* 0048 */ 0x00000000,0x00000000,0x000f619c,0x00001003,
+/* 004A */ 0x0007f801,0x000c0000,0x00000037,0x00001000,
+/* 004C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 004E */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0050 */ 0x00000000,0x000c0000,0x00000000,0x00000000,
+/* 0052 */ 0x0000373c,0x00001000,0x00000000,0x00000000,
+/* 0054 */ 0x000ee19c,0x00001003,0x0007f801,0x000c0000,
+/* 0056 */ 0x00000037,0x00001000,0x00000000,0x00000000,
+/* 0058 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 005A */ 0x00000000,0x00000000,0x0000273c,0x00001000,
+/* 005C */ 0x00000033,0x00001000,0x000e679e,0x00001003,
+/* 005E */ 0x00007705,0x00001400,0x000ac71e,0x00001003,
+/* 0060 */ 0x00087fc1,0x000c3be0,0x0007f801,0x000c0000,
+/* 0062 */ 0x00000037,0x00001000,0x00000000,0x00000000,
+/* 0064 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0066 */ 0x00000000,0x00000000,0x0000a730,0x00001003,
+/* 0068 */ 0x00000033,0x00001000,0x0007f801,0x000c0000,
+/* 006A */ 0x00000037,0x00001000,0x00000000,0x00000000,
+/* 006C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 006E */ 0x00000000,0x00000000,0x00000000,0x000c0000,
+/* 0070 */ 0x00000032,0x00001000,0x0000273d,0x00001000,
+/* 0072 */ 0x0004a730,0x00001003,0x00000f41,0x00097140,
+/* 0074 */ 0x0000a841,0x0009b240,0x0000a0c1,0x0009f040,
+/* 0076 */ 0x0001c641,0x00093540,0x0001cec1,0x0009b5c0,
+/* 0078 */ 0x00000000,0x00000000,0x0001bf05,0x0003fc40,
+/* 007A */ 0x00002725,0x000aa400,0x00013705,0x00093a00,
+/* 007C */ 0x0000002e,0x0009d6c0,0x00038630,0x00001004,
+/* 007E */ 0x0004ef0a,0x000eb785,0x0003fc8a,0x00000000,
+/* 0080 */ 0x00000000,0x000c70e0,0x0007d182,0x0002c640,
+/* 0082 */ 0x00000630,0x00001004,0x000799b8,0x0002c6c0,
+/* 0084 */ 0x00031705,0x00092240,0x00039f05,0x000932c0,
+/* 0086 */ 0x0003520a,0x00000000,0x00040731,0x0000100b,
+/* 0088 */ 0x00010705,0x000b20c0,0x00000000,0x000eba44,
+/* 008A */ 0x00032108,0x000c60c4,0x00065208,0x000c2917,
+/* 008C */ 0x000406b0,0x00001007,0x00012f05,0x00036880,
+/* 008E */ 0x0002818e,0x000c0000,0x0004410a,0x00000000,
+/* 0090 */ 0x00040630,0x00001007,0x00029705,0x000c0000,
+/* 0092 */ 0x00000000,0x00000000,0x00003fc1,0x0003fc40,
+/* 0094 */ 0x000037c1,0x00091b40,0x00003fc1,0x000911c0,
+/* 0096 */ 0x000037c1,0x000957c0,0x00003fc1,0x000951c0,
+/* 0098 */ 0x000037c1,0x00000000,0x00003fc1,0x000991c0,
+/* 009A */ 0x000037c1,0x00000000,0x00003fc1,0x0009d1c0,
+/* 009C */ 0x000037c1,0x00000000,0x0001ccc1,0x000915c0,
+/* 009E */ 0x0001c441,0x0009d800,0x0009cdc1,0x00091240,
+/* 00A0 */ 0x0001c541,0x00091d00,0x0009cfc1,0x00095240,
+/* 00A2 */ 0x0001c741,0x00095c80,0x000e8ca9,0x00099240,
+/* 00A4 */ 0x000e85ad,0x00095640,0x00069ca9,0x00099d80,
+/* 00A6 */ 0x000e952d,0x00099640,0x000eaca9,0x0009d6c0,
+/* 00A8 */ 0x000ea5ad,0x00091a40,0x0006bca9,0x0009de80,
+/* 00AA */ 0x000eb52d,0x00095a40,0x000ecca9,0x00099ac0,
+/* 00AC */ 0x000ec5ad,0x0009da40,0x000edca9,0x0009d300,
+/* 00AE */ 0x000a6e0a,0x00001000,0x000ed52d,0x00091e40,
+/* 00B0 */ 0x000eeca9,0x00095ec0,0x000ee5ad,0x00099e40,
+/* 00B2 */ 0x0006fca9,0x00002500,0x000fb208,0x000c59a0,
+/* 00B4 */ 0x000ef52d,0x0009de40,0x00068ca9,0x000912c1,
+/* 00B6 */ 0x000683ad,0x00095241,0x00020f05,0x000991c1,
+/* 00B8 */ 0x00000000,0x00000000,0x00086f88,0x00001000,
+/* 00BA */ 0x0009cf81,0x000b5340,0x0009c701,0x000b92c0,
+/* 00BC */ 0x0009de81,0x000bd300,0x0009d601,0x000b1700,
+/* 00BE */ 0x0001fd81,0x000b9d80,0x0009f501,0x000b57c0,
+/* 00C0 */ 0x000a0f81,0x000bd740,0x00020701,0x000b5c80,
+/* 00C2 */ 0x000a1681,0x000b97c0,0x00021601,0x00002500,
+/* 00C4 */ 0x000a0701,0x000b9b40,0x000a0f81,0x000b1bc0,
+/* 00C6 */ 0x00021681,0x00002d00,0x00020f81,0x000bd800,
+/* 00C8 */ 0x000a0701,0x000b5bc0,0x00021601,0x00003500,
+/* 00CA */ 0x000a0f81,0x000b5f40,0x000a0701,0x000bdbc0,
+/* 00CC */ 0x00021681,0x00003d00,0x00020f81,0x000b1d00,
+/* 00CE */ 0x000a0701,0x000b1fc0,0x00021601,0x00020500,
+/* 00D0 */ 0x00020f81,0x000b1341,0x000a0701,0x000b9fc0,
+/* 00D2 */ 0x00021681,0x00020d00,0x00020f81,0x000bde80,
+/* 00D4 */ 0x000a0701,0x000bdfc0,0x00021601,0x00021500,
+/* 00D6 */ 0x00020f81,0x000b9341,0x00020701,0x000b53c1,
+/* 00D8 */ 0x00021681,0x00021d00,0x000a0f81,0x000d0380,
+/* 00DA */ 0x0000b601,0x000b15c0,0x00007b01,0x00000000,
+/* 00DC */ 0x00007b81,0x000bd1c0,0x00007b01,0x00000000,
+/* 00DE */ 0x00007b81,0x000b91c0,0x00007b01,0x000b57c0,
+/* 00E0 */ 0x00007b81,0x000b51c0,0x00007b01,0x000b1b40,
+/* 00E2 */ 0x00007b81,0x000b11c0,0x00087b01,0x000c3dc0,
+/* 00E4 */ 0x0007e488,0x000d7e45,0x00000000,0x000d7a44,
+/* 00E6 */ 0x0007e48a,0x00000000,0x00011f05,0x00084080,
+/* 00E8 */ 0x00000000,0x00000000,0x00001705,0x000b3540,
+/* 00EA */ 0x00008a01,0x000bf040,0x00007081,0x000bb5c0,
+/* 00EC */ 0x00055488,0x00000000,0x0000d482,0x0003fc40,
+/* 00EE */ 0x0003fc88,0x00000000,0x0001e401,0x000b3a00,
+/* 00F0 */ 0x0001ec81,0x000bd6c0,0x0004ef08,0x000eb784,
+/* 00F2 */ 0x000c86b0,0x00001007,0x00008281,0x000bb240,
+/* 00F4 */ 0x0000b801,0x000b7140,0x00007888,0x00000000,
+/* 00F6 */ 0x0000073c,0x00001000,0x0007f188,0x000c0000,
+/* 00F8 */ 0x00000000,0x00000000,0x00055288,0x000c555c,
+/* 00FA */ 0x0005528a,0x000c0000,0x0009fa88,0x000c5d00,
+/* 00FC */ 0x0000fa88,0x00000000,0x00000032,0x00001000,
+/* 00FE */ 0x0000073d,0x00001000,0x0007f188,0x000c0000,
+/* 0100 */ 0x00000000,0x00000000,0x0008c01c,0x00001003,
+/* 0102 */ 0x00002705,0x00001008,0x0008b201,0x000c1392,
+/* 0104 */ 0x0000ba01,0x00000000,
+/* TASKTREETHREAD */
+/* 0105 */ 0x00008731,0x00001400,0x0004c108,0x000fe0c4,
+/* 0107 */ 0x00057488,0x00000000,0x000a6388,0x00001001,
+/* 0109 */ 0x0008b334,0x000bc141,0x0003020e,0x00000000,
+/* 010B */ 0x000886b0,0x00001008,0x00003625,0x000c5dfa,
+/* 010D */ 0x000a638a,0x00001001,0x0008020e,0x00001002,
+/* 010F */ 0x0008a6b0,0x00001008,0x0007f301,0x00000000,
+/* 0111 */ 0x00000000,0x00000000,0x00002725,0x000a8c40,
+/* 0113 */ 0x000000ae,0x00000000,0x000d8630,0x00001008,
+/* 0115 */ 0x00000000,0x000c74e0,0x0007d182,0x0002d640,
+/* 0117 */ 0x000a8630,0x00001008,0x000799b8,0x0002d6c0,
+/* 0119 */ 0x0000748a,0x000c3ec5,0x0007420a,0x000c0000,
+/* 011B */ 0x00062208,0x000c4117,0x00070630,0x00001009,
+/* 011D */ 0x00000000,0x000c0000,0x0001022e,0x00000000,
+/* 011F */ 0x0003a630,0x00001009,0x00000000,0x000c0000,
+/* 0121 */ 0x00000036,0x00001000,0x00000000,0x00000000,
+/* 0123 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0125 */ 0x00000000,0x00000000,0x0002a730,0x00001008,
+/* 0127 */ 0x0007f801,0x000c0000,0x00000037,0x00001000,
+/* 0129 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 012B */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 012D */ 0x0002a730,0x00001008,0x00000033,0x00001000,
+/* 012F */ 0x0002a705,0x00001008,0x00007a01,0x000c0000,
+/* 0131 */ 0x000e6288,0x000d550a,0x0006428a,0x00000000,
+/* 0133 */ 0x00060730,0x0000100a,0x00000000,0x000c0000,
+/* 0135 */ 0x00000000,0x00000000,
+/* TASKTREEHEADERCODE */
+/* 0136 */ 0x0007aab0,0x00034880,0x00078fb0,0x0000100b,
+/* 0138 */ 0x00057488,0x00000000,0x00033b94,0x00081140,
+/* 013A */ 0x000183ae,0x00000000,0x000786b0,0x0000100b,
+/* 013C */ 0x00022f05,0x000c3545,0x0000eb8a,0x00000000,
+/* 013E */ 0x00042731,0x00001003,
+/* FGTASKTREEHEADERCODE */
+/* 013F */ 0x0007aab0,0x00034880,0x00048fb0,0x0000100a,
+/* 0141 */ 0x00057488,0x00000000,0x00033b94,0x00081140,
+/* 0143 */ 0x000183ae,0x00000000,0x000806b0,0x0000100b,
+/* 0145 */ 0x00022f05,0x00000000,0x00007401,0x00091140,
+/* 0147 */ 0x00048f05,0x000951c0,0x00042731,0x00001003,
+/* 0149 */ 0x0000473d,0x00001000,0x000f19b0,0x000bbc47,
+/* 014B */ 0x00080000,0x000bffc7,0x000fe19e,0x00001003,
+/* 014D */ 0x00000000,0x00000000,0x0008e19c,0x00001003,
+/* 014F */ 0x000083c1,0x00093040,0x00000f41,0x00097140,
+/* 0151 */ 0x0000a841,0x0009b240,0x0000a0c1,0x0009f040,
+/* 0153 */ 0x0001c641,0x00093540,0x0001cec1,0x0009b5c0,
+/* 0155 */ 0x00000000,0x000fdc44,0x00055208,0x00000000,
+/* 0157 */ 0x00010705,0x000a2880,0x0000a23a,0x00093a00,
+/* 0159 */ 0x0003fc8a,0x000df6c5,0x0004ef0a,0x000c0000,
+/* 015B */ 0x00012f05,0x00036880,0x00065308,0x000c2997,
+/* 015D */ 0x000d86b0,0x0000100a,0x0004410a,0x000d40c7,
+/* 015F */ 0x00000000,0x00000000,0x00080730,0x00001004,
+/* 0161 */ 0x00056f0a,0x000ea105,0x00000000,0x00000000,
+/* NULLALGORITHM */
+/* 0163 */ 0x0000473d,0x00001000,0x000f19b0,0x000bbc47,
+/* 0165 */ 0x00080000,0x000bffc7,0x0000273d,0x00001000,
+/* HFGEXECCHILD */
+/* 0167 */ 0x00000000,0x000eba44,
+/* HFGEXECCHILD_98 */
+/* 0168 */ 0x00048f05,0x0000f440,0x00007401,0x0000f7c0,
+/* HFGEXECCHILD_PUSH1IND */
+/* 016A */ 0x00000734,0x00001000,0x00010705,0x000a6880,
+/* 016C */ 0x00006a88,0x000c75c4,
+/* HFGEXECSIBLING */
+/* 016D */ 0x00000000,0x000e5084,0x00000000,0x000eba44,
+/* HFGEXECSIBLING_298 */
+/* 016F */ 0x00087401,0x000e4782,
+/* HFGEXECSIBLING_2IND1 */
+/* 0170 */ 0x00000734,0x00001000,0x00010705,0x000a6880,
+/* 0172 */ 0x00006a88,0x000c75c4,
+/* S16_CODECOUTPUTTASK */
+/* 0173 */ 0x0007c108,0x000c0000,0x0007e721,0x000bed40,
+/* 0175 */ 0x00005f25,0x000badc0,0x0003ba97,0x000beb80,
+/* 0177 */ 0x00065590,0x000b2e00,0x00033217,0x00003ec0,
+/* 0179 */ 0x00065590,0x000b8e40,0x0003ed80,0x000491c0,
+/* 017B */ 0x00073fb0,0x00074c80,0x000283a0,0x0000100c,
+/* 017D */ 0x000ee388,0x00042970,0x00008301,0x00021ef2,
+/* 017F */ 0x000b8f14,0x0000000f,0x000c4d8d,0x0000001b,
+/* 0181 */ 0x000d6dc2,0x000e06c6,0x000032ac,0x000c3916,
+/* 0183 */ 0x0004edc2,0x00074c80,0x00078898,0x00001000,
+/* 0185 */ 0x00038894,0x00000032,0x000c4d8d,0x00092e1b,
+/* 0187 */ 0x000d6dc2,0x000e06c6,0x0004edc2,0x000c1956,
+/* 0189 */ 0x0000722c,0x00034a00,0x00041705,0x0009ed40,
+/* 018B */ 0x00058730,0x00001400,0x000d7488,0x000c3a00,
+/* 018D */ 0x00048f05,0x00000000
+};
+/* #CODE_END */
+
+static u32 cwcemb80_parameter[] = {
+/* 0000 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0004 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0008 */ 0x00000000,0x00000000,0x00000163,0x00000000,
+/* 000C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0010 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0014 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0018 */ 0x00000000,0x00200040,0x00008010,0x00000000,
+/* 001C */ 0x00000000,0x80000001,0x00000001,0x00060000,
+/* 0020 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0024 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0028 */ 0x00000000,0x00900080,0x00000173,0x00000000,
+/* 002C */ 0x00000000,0x00000010,0x00800000,0x00900000,
+/* 0030 */ 0xf2c0000f,0x00000200,0x00000000,0x00010600,
+/* 0034 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0038 */ 0x00000000,0x00000000,0x00000163,0x330300c2,
+/* 003C */ 0x06000000,0x00000000,0x80008000,0x80008000,
+/* 0040 */ 0x3fc0000f,0x00000301,0x00010400,0x00000000,
+/* 0044 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0048 */ 0x00000000,0x00b00000,0x00d0806d,0x330480c3,
+/* 004C */ 0x04800000,0x00000001,0x00800001,0x0000ffff,
+/* 0050 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0054 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0058 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 005C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0060 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0064 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0068 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 006C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0070 */ 0x066a0600,0x06350070,0x0000929d,0x929d929d,
+/* 0074 */ 0x00000000,0x0000735a,0x00000600,0x00000000,
+/* 0078 */ 0x929d735a,0x00000000,0x00010000,0x735a735a,
+/* 007C */ 0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+/* 0080 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0084 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0088 */ 0x00000000,0x00000000,0x0000804f,0x000000c3,
+/* 008C */ 0x05000000,0x00a00010,0x00000000,0x80008000,
+/* 0090 */ 0x00000000,0x00000000,0x00000700,0x00000000,
+/* 0094 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0098 */ 0x00000080,0x00a00000,0x0000809a,0x000000c2,
+/* 009C */ 0x07400000,0x00000000,0x80008000,0xffffffff,
+/* 00A0 */ 0x00c80028,0x00005555,0x00000000,0x000107a0,
+/* 00A4 */ 0x00c80028,0x000000c2,0x06800000,0x00000000,
+/* 00A8 */ 0x06e00080,0x00300000,0x000080bb,0x000000c9,
+/* 00AC */ 0x07a00000,0x04000000,0x80008000,0xffffffff,
+/* 00B0 */ 0x00c80028,0x00005555,0x00000000,0x00000780,
+/* 00B4 */ 0x00c80028,0x000000c5,0xff800000,0x00000000,
+/* 00B8 */ 0x00640080,0x00c00000,0x00008197,0x000000c9,
+/* 00BC */ 0x07800000,0x04000000,0x80008000,0xffffffff,
+/* 00C0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00C4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00C8 */ 0x00000000,0x00000000,0x0000805e,0x000000c1,
+/* 00CC */ 0x00000000,0x00800000,0x80008000,0x80008000,
+/* 00D0 */ 0x00020000,0x0000ffff,0x00000000,0x00000000,
+/* 00D4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00D8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00DC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00E0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00E4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00E8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00EC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00F0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00F4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00F8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00FC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0100 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0104 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0108 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 010C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0110 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0114 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0118 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 011C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0120 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0124 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0128 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 012C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0130 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0134 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0138 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 013C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0140 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0144 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0148 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 014C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0150 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0154 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0158 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 015C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0160 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0164 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0168 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 016C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0170 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0174 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0178 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 017C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0180 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0184 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0188 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 018C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0190 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0194 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0198 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 019C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01A0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01A4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01A8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01AC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01B0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01B4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01B8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01BC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01C0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01C4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01C8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01CC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01D0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01D4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01D8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01DC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01E0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01E4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01E8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01EC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01F0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01F4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01F8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01FC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0200 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0204 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0208 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 020C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0210 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0214 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0218 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 021C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0220 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0224 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0228 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 022C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0230 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0234 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0238 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 023C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0240 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0244 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0248 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 024C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0250 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0254 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0258 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 025C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0260 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0264 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0268 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 026C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0270 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0274 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0278 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 027C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0280 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0284 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0288 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 028C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0290 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0294 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0298 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 029C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02A0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02A4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02A8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02AC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02B0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02B4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02B8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02BC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02C0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02C4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02C8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02CC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02D0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02D4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02D8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02DC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02E0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02E4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02E8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02EC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02F0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02F4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02F8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02FC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0300 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0304 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0308 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 030C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0310 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0314 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0318 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 031C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0320 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0324 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0328 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 032C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0330 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0334 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0338 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 033C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0340 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0344 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0348 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 034C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0350 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0354 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0358 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 035C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0360 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0364 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0368 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 036C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0370 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0374 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0378 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 037C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0380 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0384 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0388 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 038C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0390 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0394 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0398 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 039C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03A0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03A4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03A8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03AC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03B0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03B4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03B8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03BC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03C0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03C4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03C8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03CC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03D0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03D4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03D8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03DC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03E0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03E4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03E8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03EC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03F0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03F4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03F8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03FC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0400 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0404 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0408 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 040C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0410 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0414 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0418 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 041C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0420 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0424 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0428 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 042C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0430 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0434 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0438 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 043C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0440 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0444 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0448 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 044C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0450 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0454 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0458 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 045C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0460 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0464 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0468 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 046C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0470 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0474 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0478 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 047C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0480 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0484 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0488 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 048C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0490 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0494 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0498 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 049C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04A0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04A4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04A8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04AC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04B0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04B4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04B8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04BC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04C0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04C4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04C8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04CC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04D0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04D4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04D8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04DC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04E0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04E4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04E8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04EC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04F0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04F4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04F8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04FC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0500 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0504 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0508 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 050C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0510 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0514 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0518 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 051C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0520 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0524 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0528 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 052C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0530 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0534 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0538 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 053C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0540 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0544 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0548 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 054C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0550 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0554 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0558 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 055C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0560 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0564 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0568 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 056C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0570 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0574 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0578 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 057C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0580 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0584 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0588 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 058C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0590 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0594 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0598 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 059C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05A0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05A4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05A8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05AC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05B0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05B4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05B8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05BC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05C0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05C4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05C8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05CC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05D0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05D4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05D8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05DC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05E0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05E4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05E8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05EC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05F0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05F4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05F8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05FC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0600 */ 0x929d0600,0x929d929d,0x929d929d,0x929d0000,
+/* 0604 */ 0x929d929d,0x929d929d,0x929d929d,0x929d929d,
+/* 0608 */ 0x929d929d,0x00100635,0x060b013f,0x00000004,
+/* 060C */ 0x00000001,0x007a0002,0x00000000,0x066e0610,
+/* 0610 */ 0x0105929d,0x929d929d,0x929d929d,0x929d929d,
+/* 0614 */ 0x929d929d,0xa431ac75,0x0001735a,0xa431ac75,
+/* 0618 */ 0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+/* 061C */ 0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+/* 0620 */ 0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+/* 0624 */ 0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+/* 0628 */ 0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+/* 062C */ 0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+/* 0630 */ 0xa431ac75,0xa431ac75,0xa431ac75,0x735a0051,
+/* 0634 */ 0x00000000,0x929d929d,0x929d929d,0x929d929d,
+/* 0638 */ 0x929d929d,0x929d929d,0x929d929d,0x929d929d,
+/* 063C */ 0x929d929d,0x929d929d,0x00000000,0x06400136,
+/* 0640 */ 0x0000270f,0x00010000,0x007a0000,0x00000000,
+/* 0644 */ 0x068e0645,0x0105929d,0x929d929d,0x929d929d,
+/* 0648 */ 0x929d929d,0x929d929d,0xa431ac75,0x0001735a,
+/* 064C */ 0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+/* 0650 */ 0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+/* 0654 */ 0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+/* 0658 */ 0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+/* 065C */ 0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+/* 0660 */ 0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+/* 0664 */ 0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
+/* 0668 */ 0x735a0100,0x00000000,0x00000000,0x00000000,
+/* 066C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0670 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0674 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0678 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 067C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0680 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0684 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0688 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 068C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0690 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0694 */ 0x00000000,0x00000000,0x00000000
+}; /* #PARAMETER_END */
+
+static u32 cwcemb80_sample[] = {
+/* 0000 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0004 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0008 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 000C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0010 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0014 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0018 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 001C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0020 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0024 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0028 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 002C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0030 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0034 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0038 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 003C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0040 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0044 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0048 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 004C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0050 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0054 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0058 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 005C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0060 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0064 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0068 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 006C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0070 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0074 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0078 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 007C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0080 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0084 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0088 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 008C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0090 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0094 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0098 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 009C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00A0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00A4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00A8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00AC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00B0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00B4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00B8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00BC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00C0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00C4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00C8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00CC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00D0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00D4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00D8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00DC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00E0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00E4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00E8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00EC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00F0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00F4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00F8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 00FC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0100 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0104 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0108 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 010C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0110 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0114 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0118 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 011C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0120 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0124 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0128 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 012C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0130 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0134 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0138 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 013C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0140 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0144 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0148 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 014C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0150 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0154 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0158 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 015C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0160 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0164 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0168 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 016C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0170 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0174 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0178 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 017C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0180 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0184 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0188 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 018C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0190 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0194 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0198 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 019C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01A0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01A4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01A8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01AC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01B0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01B4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01B8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01BC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01C0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01C4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01C8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01CC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01D0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01D4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01D8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01DC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01E0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01E4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01E8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01EC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01F0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01F4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01F8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 01FC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0200 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0204 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0208 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 020C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0210 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0214 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0218 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 021C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0220 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0224 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0228 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 022C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0230 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0234 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0238 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 023C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0240 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0244 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0248 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 024C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0250 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0254 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0258 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 025C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0260 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0264 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0268 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 026C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0270 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0274 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0278 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 027C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0280 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0284 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0288 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 028C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0290 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0294 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0298 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 029C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02A0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02A4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02A8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02AC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02B0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02B4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02B8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02BC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02C0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02C4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02C8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02CC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02D0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02D4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02D8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02DC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02E0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02E4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02E8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02EC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02F0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02F4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02F8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 02FC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0300 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0304 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0308 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 030C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0310 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0314 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0318 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 031C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0320 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0324 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0328 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 032C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0330 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0334 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0338 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 033C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0340 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0344 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0348 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 034C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0350 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0354 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0358 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 035C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0360 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0364 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0368 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 036C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0370 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0374 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0378 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 037C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0380 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0384 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0388 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 038C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0390 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0394 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0398 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 039C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03A0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03A4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03A8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03AC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03B0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03B4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03B8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03BC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03C0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03C4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03C8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03CC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03D0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03D4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03D8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03DC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03E0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03E4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03E8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03EC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03F0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03F4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03F8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 03FC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0400 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0404 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0408 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 040C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0410 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0414 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0418 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 041C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0420 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0424 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0428 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 042C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0430 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0434 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0438 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 043C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0440 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0444 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0448 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 044C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0450 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0454 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0458 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 045C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0460 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0464 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0468 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 046C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0470 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0474 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0478 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 047C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0480 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0484 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0488 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 048C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0490 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0494 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0498 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 049C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04A0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04A4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04A8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04AC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04B0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04B4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04B8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04BC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04C0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04C4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04C8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04CC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04D0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04D4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04D8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04DC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04E0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04E4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04E8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04EC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04F0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04F4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04F8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 04FC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0500 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0504 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0508 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 050C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0510 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0514 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0518 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 051C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0520 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0524 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0528 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 052C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0530 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0534 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0538 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 053C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0540 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0544 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0548 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 054C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0550 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0554 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0558 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 055C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0560 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0564 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0568 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 056C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0570 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0574 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0578 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 057C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0580 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0584 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0588 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 058C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0590 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0594 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0598 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 059C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05A0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05A4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05A8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05AC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05B0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05B4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05B8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05BC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05C0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05C4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05C8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05CC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05D0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05D4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05D8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05DC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05E0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05E4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05E8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05EC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05F0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05F4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05F8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 05FC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0600 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0604 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0608 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 060C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0610 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0614 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0618 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 061C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0620 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0624 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0628 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 062C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0630 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0634 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0638 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 063C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0640 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0644 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0648 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 064C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0650 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0654 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0658 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 065C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0660 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0664 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0668 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 066C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0670 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0674 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0678 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 067C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0680 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0684 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0688 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 068C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0690 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0694 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0698 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 069C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 06A0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 06A4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 06A8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 06AC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 06B0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 06B4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 06B8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 06BC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 06C0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 06C4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 06C8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 06CC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 06D0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 06D4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 06D8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 06DC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 06E0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 06E4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 06E8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 06EC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 06F0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 06F4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 06F8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 06FC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0700 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0704 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0708 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 070C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0710 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0714 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0718 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 071C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0720 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0724 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0728 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 072C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0730 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0734 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0738 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 073C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0740 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0744 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0748 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 074C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0750 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0754 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0758 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 075C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0760 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0764 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0768 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 076C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0770 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0774 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0778 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 077C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0780 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0784 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0788 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 078C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0790 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0794 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0798 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 079C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 07A0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 07A4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 07A8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 07AC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 07B0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 07B4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 07B8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 07BC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 07C0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 07C4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 07C8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 07CC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 07D0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 07D4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 07D8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 07DC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 07E0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 07E4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 07E8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 07EC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 07F0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 07F4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 07F8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 07FC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0800 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0804 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0808 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 080C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0810 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0814 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0818 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 081C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0820 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0824 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0828 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 082C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0830 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0834 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0838 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 083C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0840 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0844 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0848 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 084C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0850 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0854 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0858 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 085C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0860 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0864 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0868 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 086C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0870 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0874 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0878 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 087C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0880 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0884 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0888 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 088C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0890 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0894 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0898 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 089C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 08A0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 08A4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 08A8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 08AC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 08B0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 08B4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 08B8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 08BC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 08C0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 08C4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 08C8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 08CC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 08D0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 08D4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 08D8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 08DC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 08E0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 08E4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 08E8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 08EC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 08F0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 08F4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 08F8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 08FC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0900 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0904 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0908 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 090C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0910 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0914 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0918 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 091C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0920 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0924 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0928 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 092C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0930 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0934 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0938 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 093C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0940 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0944 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0948 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 094C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0950 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0954 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0958 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 095C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0960 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0964 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0968 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 096C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0970 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0974 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0978 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 097C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0980 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0984 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0988 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 098C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0990 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0994 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0998 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 099C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 09A0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 09A4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 09A8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 09AC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 09B0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 09B4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 09B8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 09BC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 09C0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 09C4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 09C8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 09CC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 09D0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 09D4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 09D8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 09DC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 09E0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 09E4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 09E8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 09EC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 09F0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 09F4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 09F8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 09FC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A00 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A04 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A08 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A0C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A10 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A14 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A18 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A1C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A20 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A24 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A28 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A2C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A30 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A34 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A38 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A3C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A40 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A44 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A48 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A4C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A50 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A54 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A58 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A5C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A60 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A64 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A68 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A6C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A70 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A74 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A78 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A7C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A80 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A84 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A88 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A8C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A90 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A94 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A98 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0A9C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0AA0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0AA4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0AA8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0AAC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0AB0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0AB4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0AB8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0ABC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0AC0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0AC4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0AC8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0ACC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0AD0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0AD4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0AD8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0ADC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0AE0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0AE4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0AE8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0AEC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0AF0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0AF4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0AF8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0AFC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B00 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B04 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B08 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B0C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B10 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B14 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B18 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B1C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B20 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B24 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B28 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B2C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B30 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B34 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B38 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B3C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B40 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B44 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B48 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B4C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B50 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B54 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B58 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B5C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B60 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B64 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B68 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B6C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B70 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B74 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B78 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B7C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B80 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B84 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B88 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B8C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B90 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B94 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B98 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0B9C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0BA0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0BA4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0BA8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0BAC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0BB0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0BB4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0BB8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0BBC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0BC0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0BC4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0BC8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0BCC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0BD0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0BD4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0BD8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0BDC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0BE0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0BE4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0BE8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0BEC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0BF0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0BF4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0BF8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0BFC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C00 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C04 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C08 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C0C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C10 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C14 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C18 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C1C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C20 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C24 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C28 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C2C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C30 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C34 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C38 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C3C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C40 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C44 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C48 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C4C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C50 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C54 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C58 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C5C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C60 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C64 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C68 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C6C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C70 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C74 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C78 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C7C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C80 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C84 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C88 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C8C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C90 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C94 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C98 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0C9C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0CA0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0CA4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0CA8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0CAC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0CB0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0CB4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0CB8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0CBC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0CC0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0CC4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0CC8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0CCC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0CD0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0CD4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0CD8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0CDC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0CE0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0CE4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0CE8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0CEC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0CF0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0CF4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0CF8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0CFC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D00 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D04 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D08 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D0C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D10 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D14 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D18 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D1C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D20 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D24 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D28 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D2C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D30 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D34 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D38 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D3C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D40 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D44 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D48 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D4C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D50 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D54 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D58 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D5C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D60 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D64 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D68 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D6C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D70 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D74 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D78 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D7C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D80 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D84 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D88 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D8C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D90 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D94 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D98 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0D9C */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0DA0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0DA4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0DA8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0DAC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0DB0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0DB4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0DB8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0DBC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0DC0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0DC4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0DC8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0DCC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0DD0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0DD4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0DD8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0DDC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0DE0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0DE4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0DE8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0DEC */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0DF0 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0DF4 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0DF8 */ 0x00000000,0x00000000,0x00000000,0x00000000,
+/* 0DFC */ 0x00000000,0x00000000,0x00000000,0x00010004
+}; /* #SAMPLE_END */
+
+
+static struct dsp_segment_desc cwcemb80_segments[] = {
+  { SEGTYPE_SP_PROGRAM, 0x00000000, 0x0000031c, cwcemb80_code },
+  { SEGTYPE_SP_PARAMETER, 0x00000000, 0x00000697, cwcemb80_parameter },
+  { SEGTYPE_SP_SAMPLE, 0x00000000, 0x00000e00, cwcemb80_sample },
+};
+
+static struct dsp_module_desc cwcemb80_module = {
+  "cwcemb80",
+  {
+    38,
+    cwcemb80_symbols
+  },
+  3,
+  cwcemb80_segments,
+};
+
+#endif /* __HEADER_cwcemb80_H__ */
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/cs46xx/imgs/cwcsnoop.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/cs46xx/imgs/cwcsnoop.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/cs46xx/imgs/cwcsnoop.h	(revision 91)
@@ -0,0 +1,46 @@
+/* generated from cwcsnoop.osp DO NOT MODIFY */
+
+#ifndef __HEADER_cwcsnoop_H__
+#define __HEADER_cwcsnoop_H__
+
+static struct dsp_symbol_entry cwcsnoop_symbols[] = {
+  { 0x0500, "OVERLAYBEGINADDRESS",0x00 },
+  { 0x0500, "OUTPUTSNOOP",0x03 },
+  { 0x051f, "#CODE_END",0x00 },
+}; /* cwcsnoop symbols */
+
+static u32 cwcsnoop_code[] = {
+/* 0000 */ 0x0007bfb0,0x000b4e40,0x0007c088,0x000c0617,
+/* 0002 */ 0x00049705,0x00000000,0x00080630,0x00001028,
+/* 0004 */ 0x00076408,0x000efb84,0x00066008,0x00000000,
+/* 0006 */ 0x0007c908,0x000c0000,0x00046725,0x000efa44,
+/* 0008 */ 0x0005f708,0x00000000,0x0001d402,0x000b2e00,
+/* 000A */ 0x0003d418,0x00001000,0x0008d574,0x000c4293,
+/* 000C */ 0x00065625,0x000ea30e,0x00096c01,0x000c6f92,
+/* 000E */ 0x0006a58a,0x000f6085,0x00002f43,0x00000000,
+/* 0010 */ 0x000a83a0,0x00001028,0x0005e608,0x000c0000,
+/* 0012 */ 0x00000000,0x00000000,0x000ca108,0x000dcca1,
+/* 0014 */ 0x00003bac,0x000fb205,0x00073843,0x00000000,
+/* 0016 */ 0x000d8730,0x00001028,0x0006600a,0x000c0000,
+/* 0018 */ 0x00057488,0x00000000,0x00000000,0x000e5084,
+/* 001A */ 0x00000000,0x000eba44,0x00087401,0x000e4782,
+/* 001C */ 0x00000734,0x00001000,0x00010705,0x000a6880,
+/* 001E */ 0x00006a88,0x000c75c4
+};
+/* #CODE_END */
+
+static struct dsp_segment_desc cwcsnoop_segments[] = {
+  { SEGTYPE_SP_PROGRAM, 0x00000000, 0x0000003e, cwcsnoop_code },
+};
+
+static struct dsp_module_desc cwcsnoop_module = {
+  "cwcsnoop",
+  {
+    3,
+    cwcsnoop_symbols
+  },
+  1,
+  cwcsnoop_segments,
+};
+
+#endif /* __HEADER_cwcsnoop_H__ */
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/cs46xx/makefile.os2
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/cs46xx/makefile.os2	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/cs46xx/makefile.os2	(revision 91)
@@ -0,0 +1,52 @@
+CINCLUDES = ..\..\..\include;..\..\include;$(%WATCOM)\H
+AINCLUDES = $(DDK_BASE)\H
+
+CDEFINES = -D__KERNEL__ -DMODULE -dTARGET_OS2 -dALSA_BUILD -D__ISAPNP__ -DCONFIG_ISAPNP -D__i386_ 
+#-DCONFIG_SND_CS46XX_NEW_DSP
+
+32BIT=1
+!include ..\..\..\..\makefile.inc
+
+
+#===================================================================
+#
+#   List of source files
+#
+#===================================================================
+FILE1    = cs46xx.obj cs46xx_lib.obj 
+#FILE2    = dsp_spos.obj dsp_spos_scb_lib.obj
+FILE3    = 
+FILE4    = 
+FILE5    = 
+FILE6    = 
+FILE7    = 
+FILE8    = 
+FILELAST =
+FILES    = $(FILE0) $(FILE1) $(FILE2) $(FILE3) $(FILE4) $(FILE5) $(FILE6) $(FILE7) $(FILE8) $(FILE9) $(FILE10) $(FILE11) $(FILE12)
+
+TARGET   = cs46xx
+
+LNKFILE  = $(OBJDIR)\$(TARGET).lnk
+
+
+#===================================================================
+#
+#   Specific dependencies
+#
+#===================================================================
+all: $(OBJDIR)\$(TARGET).lib 
+
+
+$(LNKFILE): makefile.os2
+    @%create $(LNKFILE)
+    @for %f in ($(FILES)) do @%append $(LNKFILE) -+$(OBJDIR)\%f
+
+$(OBJDIR)\$(TARGET).lib: $(LNKFILE) $(FILES)
+    $(LIB) $(OBJDIR)\$(TARGET).lib @$(LNKFILE)
+    copy $(OBJDIR)\$(TARGET).lib $(ALSA_LIB) 
+
+clean: .SYMBOLIC
+    -@rm  *.err 2>nul
+    @cd $(OBJDIR)
+    -rm *.obj *.lnk *.sys *.sym *.lst *.map *.wmap *.lib 2>nul
+    @cd ..\..
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/diff.cmd
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/diff.cmd	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/diff.cmd	(revision 91)
@@ -0,0 +1,1 @@
+diff.exe -Nuwr e:\dev\uss\from_z\ua2\GPL\alsa-kernel\pci\%1 E:\DEV\alsa\alsa-kernel\pci\%1 >%1.diff
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/diff1.cmd
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/diff1.cmd	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/diff1.cmd	(revision 91)
@@ -0,0 +1,1 @@
+diff.exe -Nuwr e:\dev\uss\from_z\ua2\GPL\alsa-kernel\pci\%1 e:\dev\uss\ua2\gpl\alsa-kernel\pci\%1 >%1.diff
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/diff810.cmd
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/diff810.cmd	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/diff810.cmd	(revision 91)
@@ -0,0 +1,1 @@
+diff.exe -Nuwr E:\DEV\USS\ua2\GPL\alsa-kernel\pci\intel8x0.c E:\DEV\alsa\alsa-kernel\pci\intel8x0.c >i810.diff
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/emu10k1/diff.cmd
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/emu10k1/diff.cmd	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/emu10k1/diff.cmd	(revision 91)
@@ -0,0 +1,1 @@
+diff.exe -Nuwr E:\DEV\USS\from_z\ua2\GPL\alsa-kernel\pci\emu10k1\%1 E:\DEV\alsa\alsa-kernel\pci\emu10k1\%1 >%1.diff
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/emu10k1/emu10k1.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/emu10k1/emu10k1.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/emu10k1/emu10k1.c	(revision 91)
@@ -0,0 +1,306 @@
+/*
+ *  The driver for the EMU10K1 (SB Live!) based soundcards
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *  Copyright (c) by James Courtier-Dutton <James@superbug.demon.co.uk>
+ *      Added support for Audigy 2 Value.
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/emu10k1.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("EMU10K1");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{Creative Labs,SB Live!/PCI512/E-mu APS},"
+	       "{Creative Labs,SB Audigy}}");
+
+#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE))
+#define ENABLE_SYNTH
+#include <sound/emu10k1_synth.h>
+#endif
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;	/* Enable this card */
+
+#ifdef TARGET_OS2
+static int extin[SNDRV_CARDS] = {0,0,0,0,0,0,0,0};
+static int extout[SNDRV_CARDS] = {0,0,0,0,0,0,0,0};
+static int seq_ports[SNDRV_CARDS] = {4,4,4,4,4,4,4,4};
+static int max_synth_voices[SNDRV_CARDS] = {64,64,64,64,64,64,64,64};
+static int max_buffer_size[SNDRV_CARDS] = {128,128,128,128,128,128,128,128};
+static int enable_ir[SNDRV_CARDS] = {0,0,0,0,0,0,0,0};
+static uint subsystem[SNDRV_CARDS] = {0,0,0,0,0,0,0,0}; /* Force card subsystem model */
+#else
+static int extin[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0};
+static int extout[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0};
+static int seq_ports[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 4};
+static int max_synth_voices[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 64};
+static int max_buffer_size[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 128};
+static int enable_ir[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0};
+#endif
+
+MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(index, "Index value for the EMU10K1 soundcard.");
+MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC);
+MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+MODULE_PARM_DESC(id, "ID string for the EMU10K1 soundcard.");
+MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC);
+MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(enable, "Enable the EMU10K1 soundcard.");
+MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(extin, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(extin, "Available external inputs for FX8010. Zero=default.");
+MODULE_PARM_SYNTAX(extin, SNDRV_ENABLED "allows:{{0,0x0ffff}},base:16");
+MODULE_PARM(extout, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(extout, "Available external outputs for FX8010. Zero=default.");
+MODULE_PARM_SYNTAX(extout, SNDRV_ENABLED "allows:{{0,0x0ffff}},base:16");
+MODULE_PARM(seq_ports, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(seq_ports, "Allocated sequencer ports for internal synthesizer.");
+MODULE_PARM_SYNTAX(seq_ports, SNDRV_ENABLED "allows:{{0,32}}");
+MODULE_PARM(max_synth_voices, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(max_synth_voices, "Maximum number of voices for WaveTable.");
+MODULE_PARM_SYNTAX(max_synth_voices, SNDRV_ENABLED);
+MODULE_PARM(max_buffer_size, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(max_buffer_size, "Maximum sample buffer size in MB.");
+MODULE_PARM_SYNTAX(max_buffer_size, SNDRV_ENABLED);
+MODULE_PARM(enable_ir, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(enable_ir, "Enable IR.");
+MODULE_PARM_SYNTAX(enable_ir, SNDRV_ENABLE_DESC);
+//module_param_array(subsystem, uint, NULL, 0444);
+MODULE_PARM_DESC(subsystem, "Force card subsystem model.");
+/*
+ * Class 0401: 1102:0008 (rev 00) Subsystem: 1102:1001 -> Audigy2 Value  Model:SB0400
+ */
+
+static struct pci_device_id snd_emu10k1_ids[] = {
+    { 0x1102, 0x0002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },	/* EMU10K1 */
+    { 0x1102, 0x0004, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1 },	/* Audigy */
+    { 0x1102, 0x0008, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1 },    /* Audigy 2 Value SB0400 */
+    { 0, }
+};
+/*
+ * Audigy 2 Value notes:
+ * A_IOCFG Input (GPIO)
+ * 0x400  = Front analog jack plugged in. (Green socket)
+ * 0x1000 = Read analog jack plugged in. (Black socket)
+ * 0x2000 = Center/LFE analog jack plugged in. (Orange socket)
+ * A_IOCFG Output (GPIO)
+ * 0x60 = Sound out of front Left.
+ * Win sets it to 0xXX61
+ */
+
+MODULE_DEVICE_TABLE(pci, snd_emu10k1_ids);
+
+static int __devinit snd_card_emu10k1_probe(struct pci_dev *pci,
+					    const struct pci_device_id *pci_id)
+{
+#ifdef TARGET_OS2
+	static int dev = 0;
+#else
+	static int dev;
+#endif
+        struct snd_card *card;
+	struct snd_emu10k1 *emu;
+#ifdef ENABLE_SYNTH
+        struct snd_seq_device *wave = NULL;
+#endif
+	int err;
+
+	if (dev >= SNDRV_CARDS)
+        	return -ENODEV;
+	if (!enable[dev]) {
+		dev++;
+		return -ENOENT;
+	}
+
+	card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+	if (card == NULL)
+		return -ENOMEM;
+	if (max_buffer_size[dev] < 32)
+		max_buffer_size[dev] = 32;
+	else if (max_buffer_size[dev] > 1024)
+		max_buffer_size[dev] = 1024;
+	if ((err = snd_emu10k1_create(card, pci, extin[dev], extout[dev],
+                                      (long)max_buffer_size[dev] * 1024 * 1024,
+                                      enable_ir[dev], subsystem[dev],
+                                      &emu)) < 0)
+            goto error;
+        card->private_data = emu;
+	if ((err = snd_emu10k1_pcm(emu, 0, NULL)) < 0)
+		goto error;
+	if ((err = snd_emu10k1_pcm_mic(emu, 1, NULL)) < 0)
+		goto error;
+	if ((err = snd_emu10k1_pcm_efx(emu, 2, NULL)) < 0)
+		goto error;
+        /* This stores the periods table. */
+        if (emu->card_capabilities->ca0151_chip) { /* P16V */
+            if ((err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
+                                           1024, &emu->p16v_buffer)) < 0)
+                goto error;
+        }
+
+	if ((err = snd_emu10k1_mixer(emu, 0, 3)) < 0)
+		goto error;
+
+	if ((err = snd_emu10k1_timer(emu, 0)) < 0)
+		goto error;
+
+	if ((err = snd_emu10k1_pcm_multi(emu, 3, NULL)) < 0)
+		goto error;
+        if (emu->card_capabilities->ca0151_chip) { /* P16V */
+            if ((err = snd_p16v_pcm(emu, 4, NULL)) < 0)
+                goto error;
+        }
+        if (emu->audigy) {
+            if ((err = snd_emu10k1_audigy_midi(emu)) < 0)
+                goto error;
+        } else {
+            if ((err = snd_emu10k1_midi(emu)) < 0)
+                goto error;
+        }
+	if ((err = snd_emu10k1_fx8010_new(emu, 0, NULL)) < 0)
+		goto error;
+#ifdef ENABLE_SYNTH
+	if (snd_seq_device_new(card, 1, SNDRV_SEQ_DEV_ID_EMU10K1_SYNTH,
+			       sizeof(struct snd_emu10k1_synth_arg), &wave) < 0 ||
+	    wave == NULL) {
+		snd_printk("can't initialize Emu10k1 wavetable synth\n");
+	} else {
+		struct snd_emu10k1_synth_arg *arg;
+		arg = SNDRV_SEQ_DEVICE_ARGPTR(wave);
+		strcpy(wave->name, "Emu-10k1 Synth");
+		arg->hwptr = emu;
+		arg->index = 1;
+		arg->seq_ports = seq_ports[dev];
+		arg->max_voices = max_synth_voices[dev];
+	}
+#endif
+
+        strcpy(card->driver, emu->card_capabilities->driver);
+        strcpy(card->shortname, emu->card_capabilities->name);
+        sprintf(card->longname,
+                "%s (rev.%d, serial:0x%x) at 0x%lx, irq %i",
+                card->shortname, emu->revision, emu->serial, emu->port, emu->irq);
+
+	if ((err = snd_card_register(card)) < 0)
+		goto error;
+
+	pci_set_drvdata(pci, card);
+	dev++;
+        return 0;
+
+ error:
+	snd_card_free(card);
+	return err;
+}
+
+static void __devexit snd_card_emu10k1_remove(struct pci_dev *pci)
+{
+	snd_card_free(pci_get_drvdata(pci));
+	pci_set_drvdata(pci, NULL);
+}
+
+#ifdef CONFIG_PM
+static int snd_emu10k1_suspend(struct pci_dev *pci, pm_message_t state)
+{
+    struct snd_card *card = pci_get_drvdata(pci);
+    struct snd_emu10k1 *emu = card->private_data;
+    snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+
+    snd_pcm_suspend_all(emu->pcm);
+    snd_pcm_suspend_all(emu->pcm_mic);
+    snd_pcm_suspend_all(emu->pcm_efx);
+    snd_pcm_suspend_all(emu->pcm_multi);
+    snd_pcm_suspend_all(emu->pcm_p16v);
+
+    snd_ac97_suspend(emu->ac97);
+
+    snd_emu10k1_efx_suspend(emu);
+    snd_emu10k1_suspend_regs(emu);
+    if (emu->card_capabilities->ca0151_chip)
+        snd_p16v_suspend(emu);
+
+    snd_emu10k1_done(emu);
+
+    pci_disable_device(pci);
+    pci_save_state(pci);
+    pci_set_power_state(pci, PCI_D3hot);
+    return 0;
+}
+
+static int snd_emu10k1_resume(struct pci_dev *pci)
+{
+    struct snd_card *card = pci_get_drvdata(pci);
+    struct snd_emu10k1 *emu = card->private_data;
+
+    pci_set_power_state(pci, PCI_D0);
+    pci_restore_state(pci);
+    if (pci_enable_device(pci) < 0) {
+        printk(KERN_ERR "emu10k1: pci_enable_device failed, "
+               "disabling device\n");
+        snd_card_disconnect(card);
+        return -EIO;
+    }
+
+    pci_set_master(pci);
+
+    snd_emu10k1_resume_init(emu);
+    snd_emu10k1_efx_resume(emu);
+    snd_ac97_resume(emu->ac97);
+    snd_emu10k1_resume_regs(emu);
+
+    if (emu->card_capabilities->ca0151_chip)
+        snd_p16v_resume(emu);
+    snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+    return 0;
+}
+#endif
+
+static struct pci_driver driver = {
+	.name = "EMU10K1_Audigy",
+	.id_table = snd_emu10k1_ids,
+	.probe = snd_card_emu10k1_probe,
+	.remove = snd_card_emu10k1_remove,
+#ifdef CONFIG_PM
+	.suspend = snd_emu10k1_suspend,
+	.resume = snd_emu10k1_resume,
+#endif
+};
+
+static int __init alsa_card_emu10k1_init(void)
+{
+    return pci_register_driver(&driver);
+}
+
+static void __exit alsa_card_emu10k1_exit(void)
+{
+    pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_emu10k1_init)
+module_exit(alsa_card_emu10k1_exit)
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/emu10k1/emu10k1_callback.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/emu10k1/emu10k1_callback.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/emu10k1/emu10k1_callback.c	(revision 91)
@@ -0,0 +1,541 @@
+/*
+ *  synth callback routines for Emu10k1
+ *
+ *  Copyright (C) 2000 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 "emu10k1_synth_local.h"
+#include <sound/asoundef.h>
+
+/* voice status */
+enum {
+	V_FREE=0, V_OFF, V_RELEASED, V_PLAYING, V_END
+};
+
+/* Keeps track of what we are finding */
+struct best_voice {
+	unsigned int time;
+	int voice;
+};
+
+/*
+ * prototypes
+ */
+static void lookup_voices(struct snd_emux *emu, struct snd_emu10k1 *hw, struct best_voice *best, int active_only);
+static struct snd_emux_voice *get_voice(struct snd_emux *emu, struct snd_emux_port *port);
+static int start_voice(struct snd_emux_voice *vp);
+static void trigger_voice(struct snd_emux_voice *vp);
+static void release_voice(struct snd_emux_voice *vp);
+static void update_voice(struct snd_emux_voice *vp, int update);
+static void terminate_voice(struct snd_emux_voice *vp);
+static void free_voice(struct snd_emux_voice *vp);
+
+static void set_fmmod(struct snd_emu10k1 *hw, struct snd_emux_voice *vp);
+static void set_fm2frq2(struct snd_emu10k1 *hw, struct snd_emux_voice *vp);
+static void set_filterQ(struct snd_emu10k1 *hw, struct snd_emux_voice *vp);
+
+/*
+ * Ensure a value is between two points
+ * macro evaluates its args more than once, so changed to upper-case.
+ */
+#define LIMITVALUE(x, a, b) do { if ((x) < (a)) (x) = (a); else if ((x) > (b)) (x) = (b); } while (0)
+#define LIMITMAX(x, a) do {if ((x) > (a)) (x) = (a); } while (0)
+
+
+/*
+ * set up operators
+ */
+static struct snd_emux_operators emu10k1_ops = {
+	.owner =	THIS_MODULE,
+	.get_voice =	get_voice,
+	.prepare =	start_voice,
+	.trigger =	trigger_voice,
+	.release =	release_voice,
+	.update =	update_voice,
+	.terminate =	terminate_voice,
+	.free_voice =	free_voice,
+	.sample_new =	snd_emu10k1_sample_new,
+	.sample_free =	snd_emu10k1_sample_free,
+};
+
+void snd_emu10k1_ops_setup(struct snd_emux *emu)
+{
+	emu->ops = emu10k1_ops;
+}
+
+
+/*
+ * get more voice for pcm
+ *
+ * terminate most inactive voice and give it as a pcm voice.
+ */
+int
+snd_emu10k1_synth_get_voice(struct snd_emu10k1 *hw)
+{
+	struct snd_emux *emu;
+        struct snd_emux_voice *vp;
+        struct best_voice best[V_END];
+	unsigned long flags;
+	int i;
+
+        emu = hw->synth;
+
+	spin_lock_irqsave(&emu->voice_lock, flags);
+	lookup_voices(emu, hw, best, 1); /* no OFF voices */
+	for (i = 0; i < V_END; i++) {
+		if (best[i].voice >= 0) {
+			int ch;
+			vp = &emu->voices[best[i].voice];
+			if ((ch = vp->ch) < 0) {
+				//printk("synth_get_voice: ch < 0 (%d) ??", i);
+				continue;
+			}
+			vp->emu->num_voices--;
+			vp->ch = -1;
+			vp->state = SNDRV_EMUX_ST_OFF;
+			spin_unlock_irqrestore(&emu->voice_lock, flags);
+			return ch;
+		}
+	}
+	spin_unlock_irqrestore(&emu->voice_lock, flags);
+
+	/* not found */
+	return -ENOMEM;
+}
+
+
+/*
+ * turn off the voice (not terminated)
+ */
+static void
+release_voice(struct snd_emux_voice *vp)
+{
+	int dcysusv;
+	struct snd_emu10k1 *hw;
+
+        hw = vp->hw;
+	dcysusv = 0x8000 | (unsigned char)vp->reg.parm.modrelease;
+	snd_emu10k1_ptr_write(hw, DCYSUSM, vp->ch, dcysusv);
+	dcysusv = 0x8000 | (unsigned char)vp->reg.parm.volrelease | DCYSUSV_CHANNELENABLE_MASK;
+	snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, dcysusv);
+}
+
+
+/*
+ * terminate the voice
+ */
+static void
+terminate_voice(struct snd_emux_voice *vp)
+{
+	struct snd_emu10k1 *hw;
+	
+        snd_assert(vp, return);
+        hw = vp->hw;
+	snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, 0x807f | DCYSUSV_CHANNELENABLE_MASK);
+	if (vp->block) {
+		struct snd_emu10k1_memblk *emem;
+		emem = (struct snd_emu10k1_memblk *)vp->block;
+		if (emem->map_locked > 0)
+			emem->map_locked--;
+	}
+}
+
+/*
+ * release the voice to system
+ */
+static void
+free_voice(struct snd_emux_voice *vp)
+{
+	struct snd_emu10k1 *hw;
+
+        hw = vp->hw;
+	if (vp->ch >= 0) {
+		snd_emu10k1_ptr_write(hw, IFATN, vp->ch, 0xff00);
+		snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, 0x807f | DCYSUSV_CHANNELENABLE_MASK);
+		// snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, 0);
+		snd_emu10k1_ptr_write(hw, VTFT, vp->ch, 0xffff);
+		snd_emu10k1_ptr_write(hw, CVCF, vp->ch, 0xffff);
+		snd_emu10k1_voice_free(hw, &hw->voices[vp->ch]);
+		vp->emu->num_voices--;
+		vp->ch = -1;
+	}
+}
+
+
+/*
+ * update registers
+ */
+static void
+update_voice(struct snd_emux_voice *vp, int update)
+{
+	struct snd_emu10k1 *hw;
+
+        hw = vp->hw;
+	if (update & SNDRV_EMUX_UPDATE_VOLUME)
+		snd_emu10k1_ptr_write(hw, IFATN_ATTENUATION, vp->ch, vp->avol);
+	if (update & SNDRV_EMUX_UPDATE_PITCH)
+		snd_emu10k1_ptr_write(hw, IP, vp->ch, vp->apitch);
+	if (update & SNDRV_EMUX_UPDATE_PAN) {
+		snd_emu10k1_ptr_write(hw, PTRX_FXSENDAMOUNT_A, vp->ch, vp->apan);
+		snd_emu10k1_ptr_write(hw, PTRX_FXSENDAMOUNT_B, vp->ch, vp->aaux);
+	}
+	if (update & SNDRV_EMUX_UPDATE_FMMOD)
+		set_fmmod(hw, vp);
+	if (update & SNDRV_EMUX_UPDATE_TREMFREQ)
+		snd_emu10k1_ptr_write(hw, TREMFRQ, vp->ch, vp->reg.parm.tremfrq);
+	if (update & SNDRV_EMUX_UPDATE_FM2FRQ2)
+		set_fm2frq2(hw, vp);
+	if (update & SNDRV_EMUX_UPDATE_Q)
+		set_filterQ(hw, vp);
+}
+
+
+/*
+ * look up voice table - get the best voice in order of preference
+ */
+/* spinlock held! */
+static void
+lookup_voices(struct snd_emux *emu, struct snd_emu10k1 *hw,
+	      struct best_voice *best, int active_only)
+{
+    struct snd_emux_voice *vp;
+    struct best_voice *bp;
+	int  i;
+
+	for (i = 0; i < V_END; i++) {
+		best[i].time = (unsigned int)-1; /* XXX MAX_?INT really */;
+		best[i].voice = -1;
+	}
+
+	/*
+	 * Go through them all and get a best one to use.
+	 * NOTE: could also look at volume and pick the quietest one.
+	 */
+	for (i = 0; i < emu->max_voices; i++) {
+		int state, val;
+
+		vp = &emu->voices[i];
+		state = vp->state;
+		if (state == SNDRV_EMUX_ST_OFF) {
+			if (vp->ch < 0) {
+				if (active_only)
+					continue;
+				bp = best + V_FREE;
+			} else
+				bp = best + V_OFF;
+		}
+		else if (state == SNDRV_EMUX_ST_RELEASED ||
+			 state == SNDRV_EMUX_ST_PENDING) {
+			bp = best + V_RELEASED;
+#if 1
+			val = snd_emu10k1_ptr_read(hw, CVCF_CURRENTVOL, vp->ch);
+			if (! val)
+				bp = best + V_OFF;
+#endif
+		}
+		else if (state == SNDRV_EMUX_ST_STANDBY)
+			continue;
+		else if (state & SNDRV_EMUX_ST_ON)
+			bp = best + V_PLAYING;
+		else
+			continue;
+
+		/* check if sample is finished playing (non-looping only) */
+		if (bp != best + V_OFF && bp != best + V_FREE &&
+		    (vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_SINGLESHOT)) {
+			val = snd_emu10k1_ptr_read(hw, CCCA_CURRADDR, vp->ch);
+			if (val >= vp->reg.loopstart)
+				bp = best + V_OFF;
+		}
+
+		if (vp->time < bp->time) {
+			bp->time = vp->time;
+			bp->voice = i;
+		}
+	}
+}
+
+/*
+ * get an empty voice
+ *
+ * emu->voice_lock is already held.
+ */
+static struct snd_emux_voice *
+get_voice(struct snd_emux *emu, struct snd_emux_port *port)
+{
+	struct snd_emu10k1 *hw;
+        struct snd_emux_voice *vp;
+        struct best_voice best[V_END];
+	int i;
+
+        hw = emu->hw;
+
+	lookup_voices(emu, hw, best, 0);
+	for (i = 0; i < V_END; i++) {
+		if (best[i].voice >= 0) {
+			vp = &emu->voices[best[i].voice];
+			if (vp->ch < 0) {
+				/* allocate a voice */
+                            struct snd_emu10k1_voice *hwvoice;
+                            if (snd_emu10k1_voice_alloc(hw, EMU10K1_SYNTH, 1, &hwvoice) < 0 || hwvoice == NULL)
+					continue;
+				vp->ch = hwvoice->number;
+				emu->num_voices++;
+			}
+			return vp;
+		}
+	}
+
+	/* not found */
+	return NULL;
+}
+
+/*
+ * prepare envelopes and LFOs
+ */
+static int
+start_voice(struct snd_emux_voice *vp)
+{
+	unsigned int temp;
+	int ch;
+        unsigned int addr, mapped_offset;
+        struct snd_midi_channel *chan;
+	struct snd_emu10k1 *hw;
+        struct snd_emu10k1_memblk *emem;
+
+        hw = vp->hw;
+	ch = vp->ch;
+	snd_assert(ch >= 0, return -EINVAL);
+	chan = vp->chan;
+
+	emem = (struct snd_emu10k1_memblk *)vp->block;
+	if (emem == NULL)
+		return -EINVAL;
+	emem->map_locked++;
+	if (snd_emu10k1_memblk_map(hw, emem) < 0) {
+		// printk("emu: cannot map!\n");
+		return -ENOMEM;
+	}
+	mapped_offset = snd_emu10k1_memblk_offset(emem) >> 1;
+	vp->reg.start += mapped_offset;
+	vp->reg.end += mapped_offset;
+	vp->reg.loopstart += mapped_offset;
+	vp->reg.loopend += mapped_offset;
+
+	/* set channel routing */
+	/* A = left(0), B = right(1), C = reverb(c), D = chorus(d) */
+	if (hw->audigy) {
+		temp = FXBUS_MIDI_LEFT | (FXBUS_MIDI_RIGHT << 8) | 
+			(FXBUS_MIDI_REVERB << 16) | (FXBUS_MIDI_CHORUS << 24);
+		snd_emu10k1_ptr_write(hw, A_FXRT1, ch, temp);
+	} else {
+		temp = (FXBUS_MIDI_LEFT << 16) | (FXBUS_MIDI_RIGHT << 20) | 
+			(FXBUS_MIDI_REVERB << 24) | (FXBUS_MIDI_CHORUS << 28);
+		snd_emu10k1_ptr_write(hw, FXRT, ch, temp);
+	}
+
+        /* channel to be silent and idle */
+        snd_emu10k1_ptr_write(hw, DCYSUSV, ch, 0x0000);
+	snd_emu10k1_ptr_write(hw, VTFT, ch, 0x0000FFFF);
+	snd_emu10k1_ptr_write(hw, CVCF, ch, 0x0000FFFF);
+	snd_emu10k1_ptr_write(hw, PTRX, ch, 0);
+	snd_emu10k1_ptr_write(hw, CPF, ch, 0);
+
+	/* set pitch offset */
+	snd_emu10k1_ptr_write(hw, IP, vp->ch, vp->apitch);
+
+	/* set envelope parameters */
+	snd_emu10k1_ptr_write(hw, ENVVAL, ch, vp->reg.parm.moddelay);
+	snd_emu10k1_ptr_write(hw, ATKHLDM, ch, vp->reg.parm.modatkhld);
+	snd_emu10k1_ptr_write(hw, DCYSUSM, ch, vp->reg.parm.moddcysus);
+	snd_emu10k1_ptr_write(hw, ENVVOL, ch, vp->reg.parm.voldelay);
+	snd_emu10k1_ptr_write(hw, ATKHLDV, ch, vp->reg.parm.volatkhld);
+	/* decay/sustain parameter for volume envelope is used
+	   for triggerg the voice */
+
+	/* cutoff and volume */
+	temp = (unsigned int)vp->acutoff << 8 | (unsigned char)vp->avol;
+	snd_emu10k1_ptr_write(hw, IFATN, vp->ch, temp);
+
+	/* modulation envelope heights */
+	snd_emu10k1_ptr_write(hw, PEFE, ch, vp->reg.parm.pefe);
+
+	/* lfo1/2 delay */
+	snd_emu10k1_ptr_write(hw, LFOVAL1, ch, vp->reg.parm.lfo1delay);
+	snd_emu10k1_ptr_write(hw, LFOVAL2, ch, vp->reg.parm.lfo2delay);
+
+	/* lfo1 pitch & cutoff shift */
+	set_fmmod(hw, vp);
+	/* lfo1 volume & freq */
+	snd_emu10k1_ptr_write(hw, TREMFRQ, vp->ch, vp->reg.parm.tremfrq);
+	/* lfo2 pitch & freq */
+	set_fm2frq2(hw, vp);
+
+	/* reverb and loop start (reverb 8bit, MSB) */
+	temp = vp->reg.parm.reverb;
+	temp += (int)vp->chan->control[MIDI_CTL_E1_REVERB_DEPTH] * 9 / 10;
+	LIMITMAX(temp, 255);
+	addr = vp->reg.loopstart;
+	snd_emu10k1_ptr_write(hw, PSST, vp->ch, (temp << 24) | addr);
+
+	/* chorus & loop end (chorus 8bit, MSB) */
+	addr = vp->reg.loopend;
+	temp = vp->reg.parm.chorus;
+	temp += (int)chan->control[MIDI_CTL_E3_CHORUS_DEPTH] * 9 / 10;
+	LIMITMAX(temp, 255);
+	temp = (temp <<24) | addr;
+	snd_emu10k1_ptr_write(hw, DSL, ch, temp);
+
+	/* clear filter delay memory */
+	snd_emu10k1_ptr_write(hw, Z1, ch, 0);
+	snd_emu10k1_ptr_write(hw, Z2, ch, 0);
+
+        /* invalidate maps */
+        temp = (hw->silent_page.addr << 1) | MAP_PTI_MASK;
+	snd_emu10k1_ptr_write(hw, MAPA, ch, temp);
+	snd_emu10k1_ptr_write(hw, MAPB, ch, temp);
+#if 0
+	/* cache */
+	{
+		unsigned int val, sample;
+		val = 32;
+		if (vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_8BITS)
+			sample = 0x80808080;
+		else {
+			sample = 0;
+			val *= 2;
+		}
+
+		/* cache */
+		snd_emu10k1_ptr_write(hw, CCR, ch, 0x1c << 16);
+		snd_emu10k1_ptr_write(hw, CDE, ch, sample);
+		snd_emu10k1_ptr_write(hw, CDF, ch, sample);
+
+                /* invalidate maps */
+                temp = ((unsigned int)hw->silent_page.addr << 1) | MAP_PTI_MASK;
+//		temp = ((unsigned int)hw->silent_page_dmaaddr << 1) | MAP_PTI_MASK;
+		snd_emu10k1_ptr_write(hw, MAPA, ch, temp);
+		snd_emu10k1_ptr_write(hw, MAPB, ch, temp);
+		
+		/* fill cache */
+		val -= 4;
+		val <<= 25;
+		val |= 0x1c << 16;
+		snd_emu10k1_ptr_write(hw, CCR, ch, val);
+	}
+#endif
+
+	/* Q & current address (Q 4bit value, MSB) */
+	addr = vp->reg.start;
+	temp = vp->reg.parm.filterQ;
+	temp = (temp<<28) | addr;
+	if (vp->apitch < 0xe400)
+		temp |= CCCA_INTERPROM_0;
+	else {
+		unsigned int shift = (vp->apitch - 0xe000) >> 10;
+		temp |= shift << 25;
+	}
+	if (vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_8BITS)
+		temp |= CCCA_8BITSELECT;
+	snd_emu10k1_ptr_write(hw, CCCA, ch, temp);
+
+	/* reset volume */
+	temp = (unsigned int)vp->vtarget << 16;
+	snd_emu10k1_ptr_write(hw, VTFT, ch, temp | vp->ftarget);
+	snd_emu10k1_ptr_write(hw, CVCF, ch, temp | 0xff00);
+	return 0;
+}
+
+/*
+ * Start envelope
+ */
+static void
+trigger_voice(struct snd_emux_voice *vp)
+{
+	unsigned int temp, ptarget;
+	struct snd_emu10k1 *hw;
+	struct snd_emu10k1_memblk *emem;
+
+        hw = vp->hw;
+
+	emem = (struct snd_emu10k1_memblk *)vp->block;
+	if (! emem || emem->mapped_page < 0)
+		return; /* not mapped */
+
+#if 0
+	ptarget = (unsigned int)vp->ptarget << 16;
+#else
+	ptarget = IP_TO_CP(vp->apitch);
+#endif
+	/* set pitch target and pan (volume) */
+	temp = ptarget | (vp->apan << 8) | vp->aaux;
+	snd_emu10k1_ptr_write(hw, PTRX, vp->ch, temp);
+
+	/* pitch target */
+	snd_emu10k1_ptr_write(hw, CPF, vp->ch, ptarget);
+
+	/* trigger voice */
+	snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, vp->reg.parm.voldcysus|DCYSUSV_CHANNELENABLE_MASK);
+}
+
+#define MOD_SENSE 18
+
+/* set lfo1 modulation height and cutoff */
+static void
+set_fmmod(struct snd_emu10k1 *hw, struct snd_emux_voice *vp)
+{
+	unsigned short fmmod;
+	short pitch;
+	unsigned char cutoff;
+	int modulation;
+
+	pitch = (char)(vp->reg.parm.fmmod>>8);
+	cutoff = (vp->reg.parm.fmmod & 0xff);
+	modulation = vp->chan->gm_modulation + vp->chan->midi_pressure;
+	pitch += (MOD_SENSE * modulation) / 1200;
+	LIMITVALUE(pitch, -128, 127);
+	fmmod = ((unsigned char)pitch<<8) | cutoff;
+	snd_emu10k1_ptr_write(hw, FMMOD, vp->ch, fmmod);
+}
+
+/* set lfo2 pitch & frequency */
+static void
+set_fm2frq2(struct snd_emu10k1 *hw, struct snd_emux_voice *vp)
+{
+	unsigned short fm2frq2;
+	short pitch;
+	unsigned char freq;
+	int modulation;
+
+	pitch = (char)(vp->reg.parm.fm2frq2>>8);
+	freq = vp->reg.parm.fm2frq2 & 0xff;
+	modulation = vp->chan->gm_modulation + vp->chan->midi_pressure;
+	pitch += (MOD_SENSE * modulation) / 1200;
+	LIMITVALUE(pitch, -128, 127);
+	fm2frq2 = ((unsigned char)pitch<<8) | freq;
+	snd_emu10k1_ptr_write(hw, FM2FRQ2, vp->ch, fm2frq2);
+}
+
+/* set filterQ */
+static void
+set_filterQ(struct snd_emu10k1 *hw, struct snd_emux_voice *vp)
+{
+	unsigned int val;
+	val = snd_emu10k1_ptr_read(hw, CCCA, vp->ch) & ~CCCA_RESONANCE;
+	val |= (vp->reg.parm.filterQ << 28);
+	snd_emu10k1_ptr_write(hw, CCCA, vp->ch, val);
+}
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/emu10k1/emu10k1_main.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/emu10k1/emu10k1_main.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/emu10k1/emu10k1_main.c	(revision 91)
@@ -0,0 +1,1773 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *                   Creative Labs, Inc.
+ *  Routines for control of EMU10K1 chips
+ *
+ *  Copyright (c) by James Courtier-Dutton <James@superbug.co.uk>
+ *      Added support for Audigy 2 Value.
+ *  	Added EMU 1010 support.
+ *  	General bug fixes and enhancements.
+ *
+ *
+ *  BUGS:
+ *    --
+ *
+ *  TODO:
+ *    --
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <sound/core.h>
+#include <sound/emu10k1.h>
+#include <sound/firmware.h>
+#include "p16v.h"
+#include "tina2.h"
+
+/*************************************************************************
+ * EMU10K1 init / done
+ *************************************************************************/
+
+void snd_emu10k1_voice_init(struct snd_emu10k1 * emu, int ch)
+{
+    snd_emu10k1_ptr_write(emu, DCYSUSV, ch, 0);
+    snd_emu10k1_ptr_write(emu, IP, ch, 0);
+    snd_emu10k1_ptr_write(emu, VTFT, ch, 0xffff);
+    snd_emu10k1_ptr_write(emu, CVCF, ch, 0xffff);
+    snd_emu10k1_ptr_write(emu, PTRX, ch, 0);
+    snd_emu10k1_ptr_write(emu, CPF, ch, 0);
+    snd_emu10k1_ptr_write(emu, CCR, ch, 0);
+
+    snd_emu10k1_ptr_write(emu, PSST, ch, 0);
+    snd_emu10k1_ptr_write(emu, DSL, ch, 0x10);
+    snd_emu10k1_ptr_write(emu, CCCA, ch, 0);
+    snd_emu10k1_ptr_write(emu, Z1, ch, 0);
+    snd_emu10k1_ptr_write(emu, Z2, ch, 0);
+    snd_emu10k1_ptr_write(emu, FXRT, ch, 0x32100000);
+
+    snd_emu10k1_ptr_write(emu, ATKHLDM, ch, 0);
+    snd_emu10k1_ptr_write(emu, DCYSUSM, ch, 0);
+    snd_emu10k1_ptr_write(emu, IFATN, ch, 0xffff);
+    snd_emu10k1_ptr_write(emu, PEFE, ch, 0);
+    snd_emu10k1_ptr_write(emu, FMMOD, ch, 0);
+    snd_emu10k1_ptr_write(emu, TREMFRQ, ch, 24);	/* 1 Hz */
+    snd_emu10k1_ptr_write(emu, FM2FRQ2, ch, 24);	/* 1 Hz */
+    snd_emu10k1_ptr_write(emu, TEMPENV, ch, 0);
+
+    /*** these are last so OFF prevents writing ***/
+    snd_emu10k1_ptr_write(emu, LFOVAL2, ch, 0);
+    snd_emu10k1_ptr_write(emu, LFOVAL1, ch, 0);
+    snd_emu10k1_ptr_write(emu, ATKHLDV, ch, 0);
+    snd_emu10k1_ptr_write(emu, ENVVOL, ch, 0);
+    snd_emu10k1_ptr_write(emu, ENVVAL, ch, 0);
+
+    /* Audigy extra stuffs */
+    if (emu->audigy) {
+        snd_emu10k1_ptr_write(emu, 0x4c, ch, 0); /* ?? */
+        snd_emu10k1_ptr_write(emu, 0x4d, ch, 0); /* ?? */
+        snd_emu10k1_ptr_write(emu, 0x4e, ch, 0); /* ?? */
+        snd_emu10k1_ptr_write(emu, 0x4f, ch, 0); /* ?? */
+        snd_emu10k1_ptr_write(emu, A_FXRT1, ch, 0x03020100);
+        snd_emu10k1_ptr_write(emu, A_FXRT2, ch, 0x3f3f3f3f);
+        snd_emu10k1_ptr_write(emu, A_SENDAMOUNTS, ch, 0);
+    }
+}
+
+static unsigned int spi_dac_init[] = {
+    0x00ff,
+    0x02ff,
+    0x0400,
+    0x0520,
+    0x0600,
+    0x08ff,
+    0x0aff,
+    0x0cff,
+    0x0eff,
+    0x10ff,
+    0x1200,
+    0x1400,
+    0x1480,
+    0x1800,
+    0x1aff,
+    0x1cff,
+    0x1e00,
+    0x0530,
+    0x0602,
+    0x0622,
+    0x1400,
+};
+
+static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
+{
+    unsigned int silent_page;
+    int ch;
+
+    /* disable audio and lock cache */
+    outl(HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE_MASK | HCFG_MUTEBUTTONENABLE,
+         emu->port + HCFG);
+
+    /* reset recording buffers */
+    snd_emu10k1_ptr_write(emu, MICBS, 0, ADCBS_BUFSIZE_NONE);
+    snd_emu10k1_ptr_write(emu, MICBA, 0, 0);
+    snd_emu10k1_ptr_write(emu, FXBS, 0, ADCBS_BUFSIZE_NONE);
+    snd_emu10k1_ptr_write(emu, FXBA, 0, 0);
+    snd_emu10k1_ptr_write(emu, ADCBS, 0, ADCBS_BUFSIZE_NONE);
+    snd_emu10k1_ptr_write(emu, ADCBA, 0, 0);
+
+    /* disable channel interrupt */
+    outl(0, emu->port + INTE);
+    snd_emu10k1_ptr_write(emu, CLIEL, 0, 0);
+    snd_emu10k1_ptr_write(emu, CLIEH, 0, 0);
+    snd_emu10k1_ptr_write(emu, SOLEL, 0, 0);
+    snd_emu10k1_ptr_write(emu, SOLEH, 0, 0);
+
+    if (emu->audigy){
+        /* set SPDIF bypass mode */
+        snd_emu10k1_ptr_write(emu, SPBYPASS, 0, SPBYPASS_FORMAT);
+        /* enable rear left + rear right AC97 slots */
+        snd_emu10k1_ptr_write(emu, AC97SLOT, 0, AC97SLOT_REAR_RIGHT |
+                              AC97SLOT_REAR_LEFT);
+    }
+
+    /* init envelope engine */
+    for (ch = 0; ch < NUM_G; ch++)
+        snd_emu10k1_voice_init(emu, ch);
+
+    snd_emu10k1_ptr_write(emu, SPCS0, 0, emu->spdif_bits[0]);
+    snd_emu10k1_ptr_write(emu, SPCS1, 0, emu->spdif_bits[1]);
+    snd_emu10k1_ptr_write(emu, SPCS2, 0, emu->spdif_bits[2]);
+
+    if (emu->card_capabilities->ca0151_chip) { /* audigy2 */
+        /* Hacks for Alice3 to work independent of haP16V driver */
+        u32 tmp;
+
+        //Setup SRCMulti_I2S SamplingRate
+        tmp = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, 0);
+        tmp &= 0xfffff1ff;
+        tmp |= (0x2<<9);
+        snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, 0, tmp);
+
+        /* Setup SRCSel (Enable Spdif,I2S SRCMulti) */
+        snd_emu10k1_ptr20_write(emu, SRCSel, 0, 0x14);
+        /* Setup SRCMulti Input Audio Enable */
+        /* Use 0xFFFFFFFF to enable P16V sounds. */
+        snd_emu10k1_ptr20_write(emu, SRCMULTI_ENABLE, 0, 0xFFFFFFFF);
+
+        /* Enabled Phased (8-channel) P16V playback */
+        outl(0x0201, emu->port + HCFG2);
+        /* Set playback routing. */
+        snd_emu10k1_ptr20_write(emu, CAPTURE_P16V_SOURCE, 0, 0x78e4);
+    }
+    if (emu->card_capabilities->ca0108_chip) { /* audigy2 Value */
+        /* Hacks for Alice3 to work independent of haP16V driver */
+        u32 tmp;
+
+        snd_printk(KERN_INFO "Audigy2 value: Special config.\n");
+        //Setup SRCMulti_I2S SamplingRate
+        tmp = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, 0);
+        tmp &= 0xfffff1ff;
+        tmp |= (0x2<<9);
+        snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, 0, tmp);
+
+        /* Setup SRCSel (Enable Spdif,I2S SRCMulti) */
+        outl(0x600000, emu->port + 0x20);
+        outl(0x14, emu->port + 0x24);
+
+        /* Setup SRCMulti Input Audio Enable */
+        outl(0x7b0000, emu->port + 0x20);
+        outl(0xFF000000, emu->port + 0x24);
+
+        /* Setup SPDIF Out Audio Enable */
+        /* The Audigy 2 Value has a separate SPDIF out,
+         * so no need for a mixer switch
+         */
+        outl(0x7a0000, emu->port + 0x20);
+        outl(0xFF000000, emu->port + 0x24);
+        tmp = inl(emu->port + A_IOCFG) & ~0x8; /* Clear bit 3 */
+        outl(tmp, emu->port + A_IOCFG);
+    }
+    if (emu->card_capabilities->spi_dac) { /* Audigy 2 ZS Notebook with DAC Wolfson WM8768/WM8568 */
+        int size, n;
+        size = ARRAY_SIZE(spi_dac_init);
+        for (n=0; n < size; n++)
+            snd_emu10k1_spi_write(emu, spi_dac_init[n]);
+
+        snd_emu10k1_ptr20_write(emu, 0x60, 0, 0x10);
+        /* Enable GPIOs
+         * GPIO0: Unknown
+         * GPIO1: Speakers-enabled.
+         * GPIO2: Unknown
+         * GPIO3: Unknown
+         * GPIO4: IEC958 Output on.
+         * GPIO5: Unknown
+         * GPIO6: Unknown
+         * GPIO7: Unknown
+         */
+        outl(0x76, emu->port + A_IOCFG); /* Windows uses 0x3f76 */
+
+    }
+
+    snd_emu10k1_ptr_write(emu, PTB, 0, emu->ptb_pages.addr);
+    snd_emu10k1_ptr_write(emu, TCB, 0, 0);	/* taken from original driver */
+    snd_emu10k1_ptr_write(emu, TCBS, 0, 4);	/* taken from original driver */
+
+    silent_page = (emu->silent_page.addr << 1) | MAP_PTI_MASK;
+    for (ch = 0; ch < NUM_G; ch++) {
+        snd_emu10k1_ptr_write(emu, MAPA, ch, silent_page);
+        snd_emu10k1_ptr_write(emu, MAPB, ch, silent_page);
+    }
+
+    if (emu->card_capabilities->emu1010) {
+        outl(HCFG_AUTOMUTE_ASYNC |
+             HCFG_EMU32_SLAVE |
+             HCFG_AUDIOENABLE, emu->port + HCFG);
+
+        /*
+         *  Hokay, setup HCFG
+         *   Mute Disable Audio = 0
+         *   Lock Tank Memory = 1
+         *   Lock Sound Memory = 0
+         *   Auto Mute = 1
+         */
+    } else if (emu->audigy) {
+        if (emu->revision == 4) /* audigy2 */
+            outl(HCFG_AUDIOENABLE |
+                 HCFG_AC3ENABLE_CDSPDIF |
+                 HCFG_AC3ENABLE_GPSPDIF |
+                 HCFG_AUTOMUTE | HCFG_JOYENABLE, emu->port + HCFG);
+        else
+            outl(HCFG_AUTOMUTE | HCFG_JOYENABLE, emu->port + HCFG);
+        /* FIXME: Remove all these emu->model and replace it with a card recognition parameter,
+         * e.g. card_capabilities->joystick */
+    } else if (emu->model == 0x20 ||
+               emu->model == 0xc400 ||
+               (emu->model == 0x21 && emu->revision < 6))
+        outl(HCFG_LOCKTANKCACHE_MASK | HCFG_AUTOMUTE, emu->port + HCFG);
+    else
+        // With on-chip joystick
+        outl(HCFG_LOCKTANKCACHE_MASK | HCFG_AUTOMUTE | HCFG_JOYENABLE, emu->port + HCFG);
+
+    if (enable_ir) {	/* enable IR for SB Live */
+        if (emu->card_capabilities->emu1010) {
+            ;  /* Disable all access to A_IOCFG for the emu1010 */
+        } else if (emu->audigy) {
+            unsigned int reg = inl(emu->port + A_IOCFG);
+            outl(reg | A_IOCFG_GPOUT2, emu->port + A_IOCFG);
+            udelay(500);
+            outl(reg | A_IOCFG_GPOUT1 | A_IOCFG_GPOUT2, emu->port + A_IOCFG);
+            udelay(100);
+            outl(reg, emu->port + A_IOCFG);
+        } else {
+            unsigned int reg = inl(emu->port + HCFG);
+            outl(reg | HCFG_GPOUT2, emu->port + HCFG);
+            udelay(500);
+            outl(reg | HCFG_GPOUT1 | HCFG_GPOUT2, emu->port + HCFG);
+            udelay(100);
+            outl(reg, emu->port + HCFG);
+        }
+    }
+
+    if (emu->card_capabilities->emu1010) {
+        ;  /* Disable all access to A_IOCFG for the emu1010 */
+    } else if (emu->audigy) {	/* enable analog output */
+        unsigned int reg = inl(emu->port + A_IOCFG);
+        outl(reg | A_IOCFG_GPOUT0, emu->port + A_IOCFG);
+    }
+
+    return 0;
+}
+
+static void snd_emu10k1_audio_enable(struct snd_emu10k1 *emu)
+{
+    /*
+     *  Enable the audio bit
+     */
+    outl(inl(emu->port + HCFG) | HCFG_AUDIOENABLE, emu->port + HCFG);
+
+    /* Enable analog/digital outs on audigy */
+    if (emu->card_capabilities->emu1010) {
+        ;  /* Disable all access to A_IOCFG for the emu1010 */
+    } else if (emu->audigy) {
+        outl(inl(emu->port + A_IOCFG) & ~0x44, emu->port + A_IOCFG);
+
+        if (emu->card_capabilities->ca0151_chip) { /* audigy2 */
+            /* Unmute Analog now.  Set GPO6 to 1 for Apollo.
+             * This has to be done after init ALice3 I2SOut beyond 48KHz.
+             * So, sequence is important. */
+            outl(inl(emu->port + A_IOCFG) | 0x0040, emu->port + A_IOCFG);
+        } else if (emu->card_capabilities->ca0108_chip) { /* audigy2 value */
+            /* Unmute Analog now. */
+            outl(inl(emu->port + A_IOCFG) | 0x0060, emu->port + A_IOCFG);
+        } else {
+            /* Disable routing from AC97 line out to Front speakers */
+            outl(inl(emu->port + A_IOCFG) | 0x0080, emu->port + A_IOCFG);
+        }
+    }
+
+#if 0
+    {
+        unsigned int tmp;
+        /* FIXME: the following routine disables LiveDrive-II !! */
+        // TOSLink detection
+        emu->tos_link = 0;
+        tmp = inl(emu->port + HCFG);
+        if (tmp & (HCFG_GPINPUT0 | HCFG_GPINPUT1)) {
+            outl(tmp|0x800, emu->port + HCFG);
+            udelay(50);
+            if (tmp != (inl(emu->port + HCFG) & ~0x800)) {
+                emu->tos_link = 1;
+                outl(tmp, emu->port + HCFG);
+            }
+        }
+    }
+#endif
+
+    snd_emu10k1_intr_enable(emu, INTE_PCIERRORENABLE);
+}
+
+int snd_emu10k1_done(struct snd_emu10k1 * emu)
+{
+    int ch;
+
+    outl(0, emu->port + INTE);
+
+    /*
+     *  Shutdown the chip
+     */
+    for (ch = 0; ch < NUM_G; ch++)
+        snd_emu10k1_ptr_write(emu, DCYSUSV, ch, 0);
+    for (ch = 0; ch < NUM_G; ch++) {
+        snd_emu10k1_ptr_write(emu, VTFT, ch, 0);
+        snd_emu10k1_ptr_write(emu, CVCF, ch, 0);
+        snd_emu10k1_ptr_write(emu, PTRX, ch, 0);
+        snd_emu10k1_ptr_write(emu, CPF, ch, 0);
+    }
+
+    /* reset recording buffers */
+    snd_emu10k1_ptr_write(emu, MICBS, 0, 0);
+    snd_emu10k1_ptr_write(emu, MICBA, 0, 0);
+    snd_emu10k1_ptr_write(emu, FXBS, 0, 0);
+    snd_emu10k1_ptr_write(emu, FXBA, 0, 0);
+    snd_emu10k1_ptr_write(emu, FXWC, 0, 0);
+    snd_emu10k1_ptr_write(emu, ADCBS, 0, ADCBS_BUFSIZE_NONE);
+    snd_emu10k1_ptr_write(emu, ADCBA, 0, 0);
+    snd_emu10k1_ptr_write(emu, TCBS, 0, TCBS_BUFFSIZE_16K);
+    snd_emu10k1_ptr_write(emu, TCB, 0, 0);
+    if (emu->audigy)
+        snd_emu10k1_ptr_write(emu, A_DBG, 0, A_DBG_SINGLE_STEP);
+    else
+        snd_emu10k1_ptr_write(emu, DBG, 0, EMU10K1_DBG_SINGLE_STEP);
+
+    /* disable channel interrupt */
+    snd_emu10k1_ptr_write(emu, CLIEL, 0, 0);
+    snd_emu10k1_ptr_write(emu, CLIEH, 0, 0);
+    snd_emu10k1_ptr_write(emu, SOLEL, 0, 0);
+    snd_emu10k1_ptr_write(emu, SOLEH, 0, 0);
+
+    /* disable audio and lock cache */
+    outl(HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE_MASK | HCFG_MUTEBUTTONENABLE, emu->port + HCFG);
+    snd_emu10k1_ptr_write(emu, PTB, 0, 0);
+
+    return 0;
+}
+
+/*************************************************************************
+ * ECARD functional implementation
+ *************************************************************************/
+
+/* In A1 Silicon, these bits are in the HC register */
+#define HOOKN_BIT		(1L << 12)
+#define HANDN_BIT		(1L << 11)
+#define PULSEN_BIT		(1L << 10)
+
+#define EC_GDI1			(1 << 13)
+#define EC_GDI0			(1 << 14)
+
+#define EC_NUM_CONTROL_BITS	20
+
+#define EC_AC3_DATA_SELN	0x0001L
+#define EC_EE_DATA_SEL		0x0002L
+#define EC_EE_CNTRL_SELN	0x0004L
+#define EC_EECLK		0x0008L
+#define EC_EECS			0x0010L
+#define EC_EESDO		0x0020L
+#define EC_TRIM_CSN		0x0040L
+#define EC_TRIM_SCLK		0x0080L
+#define EC_TRIM_SDATA		0x0100L
+#define EC_TRIM_MUTEN		0x0200L
+#define EC_ADCCAL		0x0400L
+#define EC_ADCRSTN		0x0800L
+#define EC_DACCAL		0x1000L
+#define EC_DACMUTEN		0x2000L
+#define EC_LEDN			0x4000L
+
+#define EC_SPDIF0_SEL_SHIFT	15
+#define EC_SPDIF1_SEL_SHIFT	17
+#define EC_SPDIF0_SEL_MASK	(0x3L << EC_SPDIF0_SEL_SHIFT)
+#define EC_SPDIF1_SEL_MASK	(0x7L << EC_SPDIF1_SEL_SHIFT)
+#define EC_SPDIF0_SELECT(_x)	(((_x) << EC_SPDIF0_SEL_SHIFT) & EC_SPDIF0_SEL_MASK)
+#define EC_SPDIF1_SELECT(_x)	(((_x) << EC_SPDIF1_SEL_SHIFT) & EC_SPDIF1_SEL_MASK)
+#define EC_CURRENT_PROM_VERSION 0x01	/* Self-explanatory.  This should
+* be incremented any time the EEPROM's
+* format is changed.  */
+
+#define EC_EEPROM_SIZE		0x40	/* ECARD EEPROM has 64 16-bit words */
+
+/* Addresses for special values stored in to EEPROM */
+#define EC_PROM_VERSION_ADDR	0x20	/* Address of the current prom version */
+#define EC_BOARDREV0_ADDR	0x21	/* LSW of board rev */
+#define EC_BOARDREV1_ADDR	0x22	/* MSW of board rev */
+
+#define EC_LAST_PROMFILE_ADDR	0x2f
+
+#define EC_SERIALNUM_ADDR	0x30	/* First word of serial number.  The
+* can be up to 30 characters in length
+* and is stored as a NULL-terminated
+* ASCII string.  Any unused bytes must be
+* filled with zeros */
+#define EC_CHECKSUM_ADDR	0x3f	/* Location at which checksum is stored */
+
+
+/* Most of this stuff is pretty self-evident.  According to the hardware
+ * dudes, we need to leave the ADCCAL bit low in order to avoid a DC
+ * offset problem.  Weird.
+ */
+#define EC_RAW_RUN_MODE		(EC_DACMUTEN | EC_ADCRSTN | EC_TRIM_MUTEN | \
+    EC_TRIM_CSN)
+
+
+#define EC_DEFAULT_ADC_GAIN	0xC4C4
+#define EC_DEFAULT_SPDIF0_SEL	0x0
+#define EC_DEFAULT_SPDIF1_SEL	0x4
+
+/**************************************************************************
+ * @func Clock bits into the Ecard's control latch.  The Ecard uses a
+ *  control latch will is loaded bit-serially by toggling the Modem control
+ *  lines from function 2 on the E8010.  This function hides these details
+ *  and presents the illusion that we are actually writing to a distinct
+ *  register.
+ */
+
+static void snd_emu10k1_ecard_write(struct snd_emu10k1 * emu, unsigned int value)
+{
+    unsigned short count;
+    unsigned int data;
+    unsigned long hc_port;
+    unsigned int hc_value;
+
+    hc_port = emu->port + HCFG;
+    hc_value = inl(hc_port) & ~(HOOKN_BIT | HANDN_BIT | PULSEN_BIT);
+    outl(hc_value, hc_port);
+
+    for (count = 0; count < EC_NUM_CONTROL_BITS; count++) {
+
+        /* Set up the value */
+        data = ((value & 0x1) ? PULSEN_BIT : 0);
+        value >>= 1;
+
+        outl(hc_value | data, hc_port);
+
+        /* Clock the shift register */
+        outl(hc_value | data | HANDN_BIT, hc_port);
+        outl(hc_value | data, hc_port);
+    }
+
+    /* Latch the bits */
+    outl(hc_value | HOOKN_BIT, hc_port);
+    outl(hc_value, hc_port);
+}
+
+/**************************************************************************
+ * @func Set the gain of the ECARD's CS3310 Trim/gain controller.  The
+ * trim value consists of a 16bit value which is composed of two
+ * 8 bit gain/trim values, one for the left channel and one for the
+ * right channel.  The following table maps from the Gain/Attenuation
+ * value in decibels into the corresponding bit pattern for a single
+ * channel.
+ */
+
+static void snd_emu10k1_ecard_setadcgain(struct snd_emu10k1 * emu,
+                                         unsigned short gain)
+{
+    unsigned int bit;
+
+    /* Enable writing to the TRIM registers */
+    snd_emu10k1_ecard_write(emu, emu->ecard_ctrl & ~EC_TRIM_CSN);
+
+    /* Do it again to insure that we meet hold time requirements */
+    snd_emu10k1_ecard_write(emu, emu->ecard_ctrl & ~EC_TRIM_CSN);
+
+    for (bit = (1 << 15); bit; bit >>= 1) {
+        unsigned int value;
+
+        value = emu->ecard_ctrl & ~(EC_TRIM_CSN | EC_TRIM_SDATA);
+
+        if (gain & bit)
+            value |= EC_TRIM_SDATA;
+
+        /* Clock the bit */
+        snd_emu10k1_ecard_write(emu, value);
+        snd_emu10k1_ecard_write(emu, value | EC_TRIM_SCLK);
+        snd_emu10k1_ecard_write(emu, value);
+    }
+
+    snd_emu10k1_ecard_write(emu, emu->ecard_ctrl);
+}
+
+static int snd_emu10k1_ecard_init(struct snd_emu10k1 * emu)
+{
+    unsigned int hc_value;
+
+    /* Set up the initial settings */
+    emu->ecard_ctrl = EC_RAW_RUN_MODE |
+        EC_SPDIF0_SELECT(EC_DEFAULT_SPDIF0_SEL) |
+        EC_SPDIF1_SELECT(EC_DEFAULT_SPDIF1_SEL);
+
+    /* Step 0: Set the codec type in the hardware control register
+     * and enable audio output */
+    hc_value = inl(emu->port + HCFG);
+    outl(hc_value | HCFG_AUDIOENABLE | HCFG_CODECFORMAT_I2S, emu->port + HCFG);
+    inl(emu->port + HCFG);
+
+    /* Step 1: Turn off the led and deassert TRIM_CS */
+    snd_emu10k1_ecard_write(emu, EC_ADCCAL | EC_LEDN | EC_TRIM_CSN);
+
+    /* Step 2: Calibrate the ADC and DAC */
+    snd_emu10k1_ecard_write(emu, EC_DACCAL | EC_LEDN | EC_TRIM_CSN);
+
+    /* Step 3: Wait for awhile;   XXX We can't get away with this
+     * under a real operating system; we'll need to block and wait that
+     * way. */
+    snd_emu10k1_wait(emu, 48000);
+
+    /* Step 4: Switch off the DAC and ADC calibration.  Note
+     * That ADC_CAL is actually an inverted signal, so we assert
+     * it here to stop calibration.  */
+    snd_emu10k1_ecard_write(emu, EC_ADCCAL | EC_LEDN | EC_TRIM_CSN);
+
+    /* Step 4: Switch into run mode */
+    snd_emu10k1_ecard_write(emu, emu->ecard_ctrl);
+
+    /* Step 5: Set the analog input gain */
+    snd_emu10k1_ecard_setadcgain(emu, EC_DEFAULT_ADC_GAIN);
+
+    return 0;
+}
+
+static int snd_emu10k1_cardbus_init(struct snd_emu10k1 * emu)
+{
+    unsigned long special_port;
+    unsigned int value;
+
+    /* Special initialisation routine
+     * before the rest of the IO-Ports become active.
+     */
+    special_port = emu->port + 0x38;
+    value = inl(special_port);
+    outl(0x00d00000, special_port);
+    value = inl(special_port);
+    outl(0x00d00001, special_port);
+    value = inl(special_port);
+    outl(0x00d0005f, special_port);
+    value = inl(special_port);
+    outl(0x00d0007f, special_port);
+    value = inl(special_port);
+    outl(0x0090007f, special_port);
+    value = inl(special_port);
+
+    snd_emu10k1_ptr20_write(emu, TINA2_VOLUME, 0, 0xfefefefe); /* Defaults to 0x30303030 */
+    return 0;
+}
+
+static int snd_emu1010_load_firmware(struct snd_emu10k1 * emu, const char * filename)
+{
+    int err;
+    int n, i;
+    int reg;
+    int value;
+    const struct firmware *fw_entry;
+
+    if ((err = request_firmware(&fw_entry, filename, &emu->pci->dev)) != 0) {
+        snd_printk(KERN_ERR "firmware: %s not found. Err=%d\n",filename, err);
+        return err;
+    }
+
+    snd_printk(KERN_INFO "firmware size=0x%zx\n", fw_entry->size);
+    if (fw_entry->size != 0x133a4) {
+        snd_printk(KERN_ERR "firmware: %s wrong size.\n",filename);
+        return -EINVAL;
+    }
+
+    /* The FPGA is a Xilinx Spartan IIE XC2S50E */
+    /* GPIO7 -> FPGA PGMN
+     * GPIO6 -> FPGA CCLK
+     * GPIO5 -> FPGA DIN
+     * FPGA CONFIG OFF -> FPGA PGMN
+     */
+    outl(0x00, emu->port + A_IOCFG); /* Set PGMN low for 1uS. */
+    udelay(1);
+    outl(0x80, emu->port + A_IOCFG); /* Leave bit 7 set during netlist setup. */
+    udelay(100); /* Allow FPGA memory to clean */
+    for(n = 0; n < fw_entry->size; n++) {
+        value=fw_entry->data[n];
+        for(i = 0; i < 8; i++) {
+            reg = 0x80;
+            if (value & 0x1)
+                reg = reg | 0x20;
+            value = value >> 1;
+            outl(reg, emu->port + A_IOCFG);
+            outl(reg | 0x40, emu->port + A_IOCFG);
+        }
+    }
+    /* After programming, set GPIO bit 4 high again. */
+    outl(0x10, emu->port + A_IOCFG);
+
+
+    release_firmware(fw_entry);
+    return 0;
+}
+
+static int snd_emu10k1_emu1010_init(struct snd_emu10k1 * emu)
+{
+    unsigned int i;
+    int tmp,tmp2;
+    int reg;
+    int err;
+    const char *hana_filename = "emu/hana.fw";
+    const char *dock_filename = "emu/audio_dock.fw";
+
+    snd_printk(KERN_INFO "emu1010: Special config.\n");
+    /* AC97 2.1, Any 16Meg of 4Gig address, Auto-Mute, EMU32 Slave,
+     * Lock Sound Memory Cache, Lock Tank Memory Cache,
+     * Mute all codecs.
+     */
+
+    outl(0x0005a00c, emu->port + HCFG);
+    /* AC97 2.1, Any 16Meg of 4Gig address, Auto-Mute, EMU32 Slave,
+     * Lock Tank Memory Cache,
+     * Mute all codecs.
+     */
+    outl(0x0005a004, emu->port + HCFG);
+    /* AC97 2.1, Any 16Meg of 4Gig address, Auto-Mute, EMU32 Slave,
+     * Mute all codecs.
+     */
+    outl(0x0005a000, emu->port + HCFG);
+    /* AC97 2.1, Any 16Meg of 4Gig address, Auto-Mute, EMU32 Slave,
+     * Mute all codecs.
+     */
+    outl(0x0005a000, emu->port + HCFG);
+
+    /* Disable 48Volt power to Audio Dock */
+    snd_emu1010_fpga_write(emu,  EMU_HANA_DOCK_PWR,  0 );
+
+    /* ID, should read & 0x7f = 0x55. (Bit 7 is the IRQ bit) */
+    snd_emu1010_fpga_read(emu, EMU_HANA_ID, &reg );
+    snd_printdd("reg1=0x%x\n",reg);
+    if (reg == 0x55) {
+        /* FPGA netlist already present so clear it */
+        /* Return to programming mode */
+
+        snd_emu1010_fpga_write(emu,  EMU_HANA_FPGA_CONFIG, 0x02 );
+    }
+    snd_emu1010_fpga_read(emu, EMU_HANA_ID, &reg );
+    snd_printdd("reg2=0x%x\n",reg);
+    if (reg == 0x55) {
+        /* FPGA failed to return to programming mode */
+        return -ENODEV;
+    }
+    snd_printk(KERN_INFO "emu1010: EMU_HANA_ID=0x%x\n",reg);
+    if ((err = snd_emu1010_load_firmware(emu, hana_filename)) != 0) {
+        snd_printk(KERN_INFO "emu1010: Loading Hana Firmware file %s failed\n", hana_filename);
+        return err;
+    }
+
+    /* ID, should read & 0x7f = 0x55 when FPGA programmed. */
+    snd_emu1010_fpga_read(emu, EMU_HANA_ID, &reg );
+    if (reg != 0x55) {
+        /* FPGA failed to be programmed */
+        snd_printk(KERN_INFO "emu1010: Loading Hana Firmware file failed, reg=0x%x\n", reg);
+        return -ENODEV;
+    }
+
+    snd_printk(KERN_INFO "emu1010: Hana Firmware loaded\n");
+    snd_emu1010_fpga_read(emu, EMU_HANA_MAJOR_REV, &tmp );
+    snd_emu1010_fpga_read(emu, EMU_HANA_MINOR_REV, &tmp2 );
+    snd_printk("Hana ver:%d.%d\n",tmp ,tmp2);
+    /* Enable 48Volt power to Audio Dock */
+    snd_emu1010_fpga_write(emu,  EMU_HANA_DOCK_PWR,  EMU_HANA_DOCK_PWR_ON );
+
+    snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &reg );
+    snd_printk(KERN_INFO "emu1010: Card options=0x%x\n",reg);
+    snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &reg );
+    snd_printk(KERN_INFO "emu1010: Card options=0x%x\n",reg);
+    snd_emu1010_fpga_read(emu, EMU_HANA_OPTICAL_TYPE, &tmp );
+    /* ADAT input. */
+    snd_emu1010_fpga_write(emu, EMU_HANA_OPTICAL_TYPE, 0x01 );
+    snd_emu1010_fpga_read(emu, EMU_HANA_ADC_PADS, &tmp );
+    /* Set no attenuation on Audio Dock pads. */
+    snd_emu1010_fpga_write(emu, EMU_HANA_ADC_PADS, 0x00 );
+    emu->emu1010.adc_pads = 0x00;
+    snd_emu1010_fpga_read(emu, EMU_HANA_DOCK_MISC, &tmp );
+    /* Unmute Audio dock DACs, Headphone source DAC-4. */
+    snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_MISC, 0x30 );
+    snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2, 0x12 );
+    snd_emu1010_fpga_read(emu, EMU_HANA_DAC_PADS, &tmp );
+    /* DAC PADs. */
+    snd_emu1010_fpga_write(emu, EMU_HANA_DAC_PADS, 0x0f );
+    emu->emu1010.dac_pads = 0x0f;
+    snd_emu1010_fpga_read(emu, EMU_HANA_DOCK_MISC, &tmp );
+    snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_MISC, 0x30 );
+    snd_emu1010_fpga_read(emu, EMU_HANA_SPDIF_MODE, &tmp );
+    /* SPDIF Format. Set Consumer mode, 24bit, copy enable */
+    snd_emu1010_fpga_write(emu, EMU_HANA_SPDIF_MODE, 0x10 );
+    /* MIDI routing */
+    snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_IN, 0x19 );
+    /* Unknown. */
+    snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_OUT, 0x0c );
+    /* snd_emu1010_fpga_write(emu, 0x09, 0x0f ); // IRQ Enable: All on */
+    /* IRQ Enable: All off */
+    snd_emu1010_fpga_write(emu, EMU_HANA_IRQ_ENABLE, 0x00 );
+
+    snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &reg );
+    snd_printk(KERN_INFO "emu1010: Card options3=0x%x\n",reg);
+    /* Default WCLK set to 48kHz. */
+    snd_emu1010_fpga_write(emu, EMU_HANA_DEFCLOCK, 0x00 );
+    /* Word Clock source, Internal 48kHz x1 */
+    snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, EMU_HANA_WCLOCK_INT_48K );
+    //snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, EMU_HANA_WCLOCK_INT_48K | EMU_HANA_WCLOCK_4X );
+    /* Audio Dock LEDs. */
+    snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2, 0x12 );
+
+#if 0
+    /* For 96kHz */
+    snd_emu1010_fpga_link_dst_src_write(emu,
+                                        EMU_DST_ALICE2_EMU32_0, EMU_SRC_HAMOA_ADC_LEFT1);
+    snd_emu1010_fpga_link_dst_src_write(emu,
+                                        EMU_DST_ALICE2_EMU32_1, EMU_SRC_HAMOA_ADC_RIGHT1);
+    snd_emu1010_fpga_link_dst_src_write(emu,
+                                        EMU_DST_ALICE2_EMU32_4, EMU_SRC_HAMOA_ADC_LEFT2);
+    snd_emu1010_fpga_link_dst_src_write(emu,
+                                        EMU_DST_ALICE2_EMU32_5, EMU_SRC_HAMOA_ADC_RIGHT2);
+#endif
+#if 0
+    /* For 192kHz */
+    snd_emu1010_fpga_link_dst_src_write(emu,
+                                        EMU_DST_ALICE2_EMU32_0, EMU_SRC_HAMOA_ADC_LEFT1);
+    snd_emu1010_fpga_link_dst_src_write(emu,
+                                        EMU_DST_ALICE2_EMU32_1, EMU_SRC_HAMOA_ADC_RIGHT1);
+    snd_emu1010_fpga_link_dst_src_write(emu,
+                                        EMU_DST_ALICE2_EMU32_2, EMU_SRC_HAMOA_ADC_LEFT2);
+    snd_emu1010_fpga_link_dst_src_write(emu,
+                                        EMU_DST_ALICE2_EMU32_3, EMU_SRC_HAMOA_ADC_RIGHT2);
+    snd_emu1010_fpga_link_dst_src_write(emu,
+                                        EMU_DST_ALICE2_EMU32_4, EMU_SRC_HAMOA_ADC_LEFT3);
+    snd_emu1010_fpga_link_dst_src_write(emu,
+                                        EMU_DST_ALICE2_EMU32_5, EMU_SRC_HAMOA_ADC_RIGHT3);
+    snd_emu1010_fpga_link_dst_src_write(emu,
+                                        EMU_DST_ALICE2_EMU32_6, EMU_SRC_HAMOA_ADC_LEFT4);
+    snd_emu1010_fpga_link_dst_src_write(emu,
+                                        EMU_DST_ALICE2_EMU32_7, EMU_SRC_HAMOA_ADC_RIGHT4);
+#endif
+#if 1
+    /* For 48kHz */
+    snd_emu1010_fpga_link_dst_src_write(emu,
+                                        EMU_DST_ALICE2_EMU32_0, EMU_SRC_DOCK_MIC_A1);
+    snd_emu1010_fpga_link_dst_src_write(emu,
+                                        EMU_DST_ALICE2_EMU32_1, EMU_SRC_DOCK_MIC_B1);
+    snd_emu1010_fpga_link_dst_src_write(emu,
+                                        EMU_DST_ALICE2_EMU32_2, EMU_SRC_HAMOA_ADC_LEFT2);
+    snd_emu1010_fpga_link_dst_src_write(emu,
+                                        EMU_DST_ALICE2_EMU32_3, EMU_SRC_HAMOA_ADC_LEFT2);
+    snd_emu1010_fpga_link_dst_src_write(emu,
+                                        EMU_DST_ALICE2_EMU32_4, EMU_SRC_DOCK_ADC1_LEFT1);
+    snd_emu1010_fpga_link_dst_src_write(emu,
+                                        EMU_DST_ALICE2_EMU32_5, EMU_SRC_DOCK_ADC1_RIGHT1);
+    snd_emu1010_fpga_link_dst_src_write(emu,
+                                        EMU_DST_ALICE2_EMU32_6, EMU_SRC_DOCK_ADC2_LEFT1);
+    snd_emu1010_fpga_link_dst_src_write(emu,
+                                        EMU_DST_ALICE2_EMU32_7, EMU_SRC_DOCK_ADC2_RIGHT1);
+#endif
+#if 0
+    /* Original */
+    snd_emu1010_fpga_link_dst_src_write(emu,
+                                        EMU_DST_ALICE2_EMU32_4, EMU_SRC_HANA_ADAT);
+    snd_emu1010_fpga_link_dst_src_write(emu,
+                                        EMU_DST_ALICE2_EMU32_5, EMU_SRC_HANA_ADAT + 1);
+    snd_emu1010_fpga_link_dst_src_write(emu,
+                                        EMU_DST_ALICE2_EMU32_6, EMU_SRC_HANA_ADAT + 2);
+    snd_emu1010_fpga_link_dst_src_write(emu,
+                                        EMU_DST_ALICE2_EMU32_7, EMU_SRC_HANA_ADAT + 3);
+    snd_emu1010_fpga_link_dst_src_write(emu,
+                                        EMU_DST_ALICE2_EMU32_8, EMU_SRC_HANA_ADAT + 4);
+    snd_emu1010_fpga_link_dst_src_write(emu,
+                                        EMU_DST_ALICE2_EMU32_9, EMU_SRC_HANA_ADAT + 5);
+    snd_emu1010_fpga_link_dst_src_write(emu,
+                                        EMU_DST_ALICE2_EMU32_A, EMU_SRC_HANA_ADAT + 6);
+    snd_emu1010_fpga_link_dst_src_write(emu,
+                                        EMU_DST_ALICE2_EMU32_B, EMU_SRC_HANA_ADAT + 7);
+    snd_emu1010_fpga_link_dst_src_write(emu,
+                                        EMU_DST_ALICE2_EMU32_C, EMU_SRC_DOCK_MIC_A1);
+    snd_emu1010_fpga_link_dst_src_write(emu,
+                                        EMU_DST_ALICE2_EMU32_D, EMU_SRC_DOCK_MIC_B1);
+    snd_emu1010_fpga_link_dst_src_write(emu,
+                                        EMU_DST_ALICE2_EMU32_E, EMU_SRC_HAMOA_ADC_LEFT2);
+    snd_emu1010_fpga_link_dst_src_write(emu,
+                                        EMU_DST_ALICE2_EMU32_F, EMU_SRC_HAMOA_ADC_LEFT2);
+#endif
+    for (i=0;i < 0x20;i++) {
+        /* AudioDock Elink <-  Silence */
+        snd_emu1010_fpga_link_dst_src_write(emu, 0x0100+i, EMU_SRC_SILENCE);
+    }
+    for (i=0;i < 4;i++) {
+        /* Hana SPDIF Out <- Silence */
+        snd_emu1010_fpga_link_dst_src_write(emu, 0x0200+i, EMU_SRC_SILENCE);
+    }
+    for (i=0;i < 7;i++) {
+        /* Hamoa DAC <- Silence */
+        snd_emu1010_fpga_link_dst_src_write(emu, 0x0300+i, EMU_SRC_SILENCE);
+    }
+    for (i=0;i < 7;i++) {
+        /* Hana ADAT Out <- Silence */
+        snd_emu1010_fpga_link_dst_src_write(emu, EMU_DST_HANA_ADAT + i, EMU_SRC_SILENCE);
+    }
+    snd_emu1010_fpga_link_dst_src_write(emu,
+                                        EMU_DST_ALICE_I2S0_LEFT, EMU_SRC_DOCK_ADC1_LEFT1);
+    snd_emu1010_fpga_link_dst_src_write(emu,
+                                        EMU_DST_ALICE_I2S0_RIGHT, EMU_SRC_DOCK_ADC1_RIGHT1);
+    snd_emu1010_fpga_link_dst_src_write(emu,
+                                        EMU_DST_ALICE_I2S1_LEFT, EMU_SRC_DOCK_ADC2_LEFT1);
+    snd_emu1010_fpga_link_dst_src_write(emu,
+                                        EMU_DST_ALICE_I2S1_RIGHT, EMU_SRC_DOCK_ADC2_RIGHT1);
+    snd_emu1010_fpga_link_dst_src_write(emu,
+                                        EMU_DST_ALICE_I2S2_LEFT, EMU_SRC_DOCK_ADC3_LEFT1);
+    snd_emu1010_fpga_link_dst_src_write(emu,
+                                        EMU_DST_ALICE_I2S2_RIGHT, EMU_SRC_DOCK_ADC3_RIGHT1);
+    snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, 0x01 ); // Unmute all
+
+    snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &tmp );
+
+    /* AC97 1.03, Any 32Meg of 2Gig address, Auto-Mute, EMU32 Slave,
+     * Lock Sound Memory Cache, Lock Tank Memory Cache,
+     * Mute all codecs.
+     */
+    outl(0x0000a000, emu->port + HCFG);
+    /* AC97 1.03, Any 32Meg of 2Gig address, Auto-Mute, EMU32 Slave,
+     * Lock Sound Memory Cache, Lock Tank Memory Cache,
+     * Un-Mute all codecs.
+     */
+    outl(0x0000a001, emu->port + HCFG);
+
+    /* Initial boot complete. Now patches */
+
+    snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &tmp );
+    snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_IN, 0x19 ); /* MIDI Route */
+    snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_OUT, 0x0c ); /* Unknown */
+    snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_IN, 0x19 ); /* MIDI Route */
+    snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_OUT, 0x0c ); /* Unknown */
+    snd_emu1010_fpga_read(emu, EMU_HANA_SPDIF_MODE, &tmp );
+    snd_emu1010_fpga_write(emu, EMU_HANA_SPDIF_MODE, 0x10 ); /* SPDIF Format spdif  (or 0x11 for aes/ebu) */
+
+    /* Delay to allow Audio Dock to settle */
+    msleep(100);
+    snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, &tmp ); /* IRQ Status */
+    snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &reg ); /* OPTIONS: Which cards are attached to the EMU */
+    /* FIXME: The loading of this should be able to happen any time,
+     * as the user can plug/unplug it at any time
+     */
+    if (reg & (EMU_HANA_OPTION_DOCK_ONLINE | EMU_HANA_OPTION_DOCK_OFFLINE) ) {
+        /* Audio Dock attached */
+        /* Return to Audio Dock programming mode */
+        snd_printk(KERN_INFO "emu1010: Loading Audio Dock Firmware\n");
+        snd_emu1010_fpga_write(emu,  EMU_HANA_FPGA_CONFIG, EMU_HANA_FPGA_CONFIG_AUDIODOCK );
+        if ((err = snd_emu1010_load_firmware(emu, dock_filename)) != 0) {
+            return err;
+        }
+        snd_emu1010_fpga_write(emu,  EMU_HANA_FPGA_CONFIG, 0 );
+        snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, &reg );
+        snd_printk(KERN_INFO "emu1010: EMU_HANA+DOCK_IRQ_STATUS=0x%x\n",reg);
+        /* ID, should read & 0x7f = 0x55 when FPGA programmed. */
+        snd_emu1010_fpga_read(emu, EMU_HANA_ID, &reg );
+        snd_printk(KERN_INFO "emu1010: EMU_HANA+DOCK_ID=0x%x\n",reg);
+        if (reg != 0x55) {
+            /* FPGA failed to be programmed */
+            snd_printk(KERN_INFO "emu1010: Loading Audio Dock Firmware file failed, reg=0x%x\n", reg);
+            return 0;
+            return -ENODEV;
+        }
+        snd_printk(KERN_INFO "emu1010: Audio Dock Firmware loaded\n");
+        snd_emu1010_fpga_read(emu, EMU_DOCK_MAJOR_REV, &tmp );
+        snd_emu1010_fpga_read(emu, EMU_DOCK_MINOR_REV, &tmp2 );
+        snd_printk("Audio Dock ver:%d.%d\n",tmp ,tmp2);
+    }
+#if 0
+    snd_emu1010_fpga_link_dst_src_write(emu,
+                                        EMU_DST_HAMOA_DAC_LEFT1, EMU_SRC_ALICE_EMU32B + 2); /* ALICE2 bus 0xa2 */
+    snd_emu1010_fpga_link_dst_src_write(emu,
+                                        EMU_DST_HAMOA_DAC_RIGHT1, EMU_SRC_ALICE_EMU32B + 3); /* ALICE2 bus 0xa3 */
+    snd_emu1010_fpga_link_dst_src_write(emu,
+                                        EMU_DST_HANA_SPDIF_LEFT1, EMU_SRC_ALICE_EMU32A + 2); /* ALICE2 bus 0xb2 */
+    snd_emu1010_fpga_link_dst_src_write(emu,
+                                        EMU_DST_HANA_SPDIF_RIGHT1, EMU_SRC_ALICE_EMU32A + 3); /* ALICE2 bus 0xb3 */
+#endif
+    /* Default outputs */
+    snd_emu1010_fpga_link_dst_src_write(emu,
+                                        EMU_DST_DOCK_DAC1_LEFT1, EMU_SRC_ALICE_EMU32A + 0); /* ALICE2 bus 0xa0 */
+    emu->emu1010.output_source[0] = 21;
+    snd_emu1010_fpga_link_dst_src_write(emu,
+                                        EMU_DST_DOCK_DAC1_RIGHT1, EMU_SRC_ALICE_EMU32A + 1);
+    emu->emu1010.output_source[1] = 22;
+    snd_emu1010_fpga_link_dst_src_write(emu,
+                                        EMU_DST_DOCK_DAC2_LEFT1, EMU_SRC_ALICE_EMU32A + 2);
+    emu->emu1010.output_source[2] = 23;
+    snd_emu1010_fpga_link_dst_src_write(emu,
+                                        EMU_DST_DOCK_DAC2_RIGHT1, EMU_SRC_ALICE_EMU32A + 3);
+    emu->emu1010.output_source[3] = 24;
+    snd_emu1010_fpga_link_dst_src_write(emu,
+                                        EMU_DST_DOCK_DAC3_LEFT1, EMU_SRC_ALICE_EMU32A + 4);
+    emu->emu1010.output_source[4] = 25;
+    snd_emu1010_fpga_link_dst_src_write(emu,
+                                        EMU_DST_DOCK_DAC3_RIGHT1, EMU_SRC_ALICE_EMU32A + 5);
+    emu->emu1010.output_source[5] = 26;
+    snd_emu1010_fpga_link_dst_src_write(emu,
+                                        EMU_DST_DOCK_DAC4_LEFT1, EMU_SRC_ALICE_EMU32A + 6);
+    emu->emu1010.output_source[6] = 27;
+    snd_emu1010_fpga_link_dst_src_write(emu,
+                                        EMU_DST_DOCK_DAC4_RIGHT1, EMU_SRC_ALICE_EMU32A + 7);
+    emu->emu1010.output_source[7] = 28;
+    snd_emu1010_fpga_link_dst_src_write(emu,
+                                        EMU_DST_DOCK_PHONES_LEFT1, EMU_SRC_ALICE_EMU32A + 0); /* ALICE2 bus 0xa0 */
+    emu->emu1010.output_source[8] = 21;
+    snd_emu1010_fpga_link_dst_src_write(emu,
+                                        EMU_DST_DOCK_PHONES_RIGHT1, EMU_SRC_ALICE_EMU32A + 1);
+    emu->emu1010.output_source[9] = 22;
+    snd_emu1010_fpga_link_dst_src_write(emu,
+                                        EMU_DST_DOCK_SPDIF_LEFT1, EMU_SRC_ALICE_EMU32A + 0); /* ALICE2 bus 0xa0 */
+    emu->emu1010.output_source[10] = 21;
+    snd_emu1010_fpga_link_dst_src_write(emu,
+                                        EMU_DST_DOCK_SPDIF_RIGHT1, EMU_SRC_ALICE_EMU32A + 1);
+    emu->emu1010.output_source[11] = 22;
+    snd_emu1010_fpga_link_dst_src_write(emu,
+                                        EMU_DST_HANA_SPDIF_LEFT1, EMU_SRC_ALICE_EMU32A + 0); /* ALICE2 bus 0xa0 */
+    emu->emu1010.output_source[12] = 21;
+    snd_emu1010_fpga_link_dst_src_write(emu,
+                                        EMU_DST_HANA_SPDIF_RIGHT1, EMU_SRC_ALICE_EMU32A + 1);
+    emu->emu1010.output_source[13] = 22;
+    snd_emu1010_fpga_link_dst_src_write(emu,
+                                        EMU_DST_HAMOA_DAC_LEFT1, EMU_SRC_ALICE_EMU32A + 0); /* ALICE2 bus 0xa0 */
+    emu->emu1010.output_source[14] = 21;
+    snd_emu1010_fpga_link_dst_src_write(emu,
+                                        EMU_DST_HAMOA_DAC_RIGHT1, EMU_SRC_ALICE_EMU32A + 1);
+    emu->emu1010.output_source[15] = 22;
+    snd_emu1010_fpga_link_dst_src_write(emu,
+                                        EMU_DST_HANA_ADAT, EMU_SRC_ALICE_EMU32A + 0); /* ALICE2 bus 0xa0 */
+    emu->emu1010.output_source[16] = 21;
+    snd_emu1010_fpga_link_dst_src_write(emu,
+                                        EMU_DST_HANA_ADAT + 1, EMU_SRC_ALICE_EMU32A + 1);
+    emu->emu1010.output_source[17] = 22;
+    snd_emu1010_fpga_link_dst_src_write(emu,
+                                        EMU_DST_HANA_ADAT + 2, EMU_SRC_ALICE_EMU32A + 2);
+    emu->emu1010.output_source[18] = 23;
+    snd_emu1010_fpga_link_dst_src_write(emu,
+                                        EMU_DST_HANA_ADAT + 3, EMU_SRC_ALICE_EMU32A + 3);
+    emu->emu1010.output_source[19] = 24;
+    snd_emu1010_fpga_link_dst_src_write(emu,
+                                        EMU_DST_HANA_ADAT + 4, EMU_SRC_ALICE_EMU32A + 4);
+    emu->emu1010.output_source[20] = 25;
+    snd_emu1010_fpga_link_dst_src_write(emu,
+                                        EMU_DST_HANA_ADAT + 5, EMU_SRC_ALICE_EMU32A + 5);
+    emu->emu1010.output_source[21] = 26;
+    snd_emu1010_fpga_link_dst_src_write(emu,
+                                        EMU_DST_HANA_ADAT + 6, EMU_SRC_ALICE_EMU32A + 6);
+    emu->emu1010.output_source[22] = 27;
+    snd_emu1010_fpga_link_dst_src_write(emu,
+                                        EMU_DST_HANA_ADAT + 7, EMU_SRC_ALICE_EMU32A + 7);
+    emu->emu1010.output_source[23] = 28;
+
+    /* TEMP: Select SPDIF in/out */
+    snd_emu1010_fpga_write(emu, EMU_HANA_OPTICAL_TYPE, 0x0); /* Output spdif */
+
+    /* TEMP: Select 48kHz SPDIF out */
+    snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, 0x0); /* Mute all */
+    snd_emu1010_fpga_write(emu, EMU_HANA_DEFCLOCK, 0x0); /* Default fallback clock 48kHz */
+    /* Word Clock source, Internal 48kHz x1 */
+    snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, EMU_HANA_WCLOCK_INT_48K );
+    //snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, EMU_HANA_WCLOCK_INT_48K | EMU_HANA_WCLOCK_4X );
+    emu->emu1010.internal_clock = 1; /* 48000 */
+    snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2, 0x12);/* Set LEDs on Audio Dock */
+    snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, 0x1); /* Unmute all */
+    //snd_emu1010_fpga_write(emu, 0x7, 0x0); /* Mute all */
+    //snd_emu1010_fpga_write(emu, 0x7, 0x1); /* Unmute all */
+    //snd_emu1010_fpga_write(emu, 0xe, 0x12); /* Set LEDs on Audio Dock */
+
+    return 0;
+}
+
+
+/*
+ *  Create the EMU10K1 instance
+ */
+
+#ifdef CONFIG_PM
+static int alloc_pm_buffer(struct snd_emu10k1 *emu);
+static void free_pm_buffer(struct snd_emu10k1 *emu);
+#endif
+
+static int snd_emu10k1_free(struct snd_emu10k1 *emu)
+{
+    if (emu->port) {        /* avoid access to already used hardware */
+        snd_emu10k1_fx8010_tram_setup(emu, 0);
+        snd_emu10k1_done(emu);
+        /* remove reserved page */
+        if (emu->reserved_page) {
+            snd_emu10k1_synth_free(emu, (struct snd_util_memblk *)emu->reserved_page);
+            emu->reserved_page = NULL;
+        }
+        snd_emu10k1_free_efx(emu);
+    }
+    if (emu->card_capabilities->emu1010) {
+        /* Disable 48Volt power to Audio Dock */
+        snd_emu1010_fpga_write(emu,  EMU_HANA_DOCK_PWR,  0 );
+    }
+
+    if (emu->memhdr)
+        snd_util_memhdr_free(emu->memhdr);
+    if (emu->silent_page.area)
+        snd_dma_free_pages(&emu->silent_page);
+    if (emu->ptb_pages.area)
+        snd_dma_free_pages(&emu->ptb_pages);
+    vfree(emu->page_ptr_table);
+    vfree(emu->page_addr_table);
+#ifdef CONFIG_PM
+    free_pm_buffer(emu);
+#endif
+    if (emu->irq >= 0)
+        free_irq(emu->irq, (void *)emu);
+    if (emu->port)
+        pci_release_regions(emu->pci);
+    if (emu->card_capabilities->ca0151_chip) /* P16V */
+        snd_p16v_free(emu);
+    pci_disable_device(emu->pci);
+    kfree(emu);
+    return 0;
+}
+
+static int snd_emu10k1_dev_free(struct snd_device *device)
+{
+    struct snd_emu10k1 *emu = device->device_data;
+    return snd_emu10k1_free(emu);
+}
+
+static struct snd_emu_chip_details emu_chip_details[] = {
+    /* Audigy 2 Value AC3 out does not work yet. Need to find out how to turn off interpolators.*/
+    /* Tested by James@superbug.co.uk 3rd July 2005 */
+    /* DSP: CA0108-IAT
+     * DAC: CS4382-KQ
+     * ADC: Philips 1361T
+     * AC97: STAC9750
+     * CA0151: None
+     */
+    {.vendor = 0x1102, .device = 0x0008, .subsystem = 0x10011102,
+    .driver = "Audigy2", .name = "Audigy 2 Value [SB0400]",
+    .id = "Audigy2",
+    .emu10k2_chip = 1,
+    .ca0108_chip = 1,
+    .spk71 = 1,
+    .ac97_chip = 1} ,
+    /* Audigy4 (Not PRO) SB0610 */
+    /* Tested by James@superbug.co.uk 4th April 2006 */
+    /* A_IOCFG bits
+     * Output
+     * 0: ?
+     * 1: ?
+     * 2: ?
+     * 3: 0 - Digital Out, 1 - Line in
+     * 4: ?
+     * 5: ?
+     * 6: ?
+     * 7: ?
+     * Input
+     * 8: ?
+     * 9: ?
+     * A: Green jack sense (Front)
+     * B: ?
+     * C: Black jack sense (Rear/Side Right)
+     * D: Yellow jack sense (Center/LFE/Side Left)
+     * E: ?
+     * F: ?
+     *
+     * Digital Out/Line in switch using A_IOCFG bit 3 (0x08)
+     * 0 - Digital Out
+     * 1 - Line in
+     */
+    /* Mic input not tested.
+     * Analog CD input not tested
+     * Digital Out not tested.
+     * Line in working.
+     * Audio output 5.1 working. Side outputs not working.
+     */
+    /* DSP: CA10300-IAT LF
+     * DAC: Cirrus Logic CS4382-KQZ
+     * ADC: Philips 1361T
+     * AC97: Sigmatel STAC9750
+     * CA0151: None
+     */
+    {.vendor = 0x1102, .device = 0x0008, .subsystem = 0x10211102,
+    .driver = "Audigy2", .name = "Audigy 4 [SB0610]",
+    .id = "Audigy2",
+    .emu10k2_chip = 1,
+    .ca0108_chip = 1,
+    .spk71 = 1,
+    .adc_1361t = 1,  /* 24 bit capture instead of 16bit */
+    .ac97_chip = 1} ,
+    /* Audigy 2 ZS Notebook Cardbus card.*/
+    /* Tested by James@superbug.co.uk 22th December 2005 */
+    /* Audio output 7.1/Headphones working.
+     * Digital output working. (AC3 not checked, only PCM)
+     * Audio inputs not tested.
+     */
+    /* DSP: Tina2
+     * DAC: Wolfson WM8768/WM8568
+     * ADC: Wolfson WM8775
+     * AC97: None
+     * CA0151: None
+     */
+    {.vendor = 0x1102, .device = 0x0008, .subsystem = 0x20011102,
+    .driver = "Audigy2", .name = "Audigy 2 ZS Notebook [SB0530]",
+    .id = "Audigy2",
+    .emu10k2_chip = 1,
+    .ca0108_chip = 1,
+    .ca_cardbus_chip = 1,
+    .spi_dac = 1,
+    .spk71 = 1} ,
+    {.vendor = 0x1102, .device = 0x0008,
+    .driver = "Audigy2", .name = "Audigy 2 Value [Unknown]",
+    .id = "Audigy2",
+    .emu10k2_chip = 1,
+    .ca0108_chip = 1,
+    .ac97_chip = 1} ,
+    /* Tested by James@superbug.co.uk 8th July 2005. No sound available yet. */
+    {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x40011102,
+    .driver = "Audigy2", .name = "E-mu 1010 [4001]",
+    .id = "EMU1010",
+    .emu10k2_chip = 1,
+    .ca0102_chip = 1,
+    .spk71 = 1,
+    .emu1010 = 1} ,
+    /* Tested by James@superbug.co.uk 3rd July 2005 */
+    {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x20071102,
+    .driver = "Audigy2", .name = "Audigy 4 PRO [SB0380]",
+    .id = "Audigy2",
+    .emu10k2_chip = 1,
+    .ca0102_chip = 1,
+    .ca0151_chip = 1,
+    .spk71 = 1,
+    .spdif_bug = 1,
+    .ac97_chip = 1} ,
+    /* Tested by shane-alsa@cm.nu 5th Nov 2005 */
+    /* The 0x20061102 does have SB0350 written on it
+     * Just like 0x20021102
+     */
+    {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x20061102,
+    .driver = "Audigy2", .name = "Audigy 2 [SB0350b]",
+    .id = "Audigy2",
+    .emu10k2_chip = 1,
+    .ca0102_chip = 1,
+    .ca0151_chip = 1,
+    .spk71 = 1,
+    .spdif_bug = 1,
+    .ac97_chip = 1} ,
+    {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x20021102,
+    .driver = "Audigy2", .name = "Audigy 2 ZS [SB0350]",
+    .id = "Audigy2",
+    .emu10k2_chip = 1,
+    .ca0102_chip = 1,
+    .ca0151_chip = 1,
+    .spk71 = 1,
+    .spdif_bug = 1,
+    .ac97_chip = 1} ,
+    {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x20011102,
+    .driver = "Audigy2", .name = "Audigy 2 ZS [2001]",
+    .id = "Audigy2",
+    .emu10k2_chip = 1,
+    .ca0102_chip = 1,
+    .ca0151_chip = 1,
+    .spk71 = 1,
+    .spdif_bug = 1,
+    .ac97_chip = 1} ,
+    /* Audigy 2 */
+    /* Tested by James@superbug.co.uk 3rd July 2005 */
+    /* DSP: CA0102-IAT
+     * DAC: CS4382-KQ
+     * ADC: Philips 1361T
+     * AC97: STAC9721
+     * CA0151: Yes
+     */
+    {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x10071102,
+    .driver = "Audigy2", .name = "Audigy 2 [SB0240]",
+    .id = "Audigy2",
+    .emu10k2_chip = 1,
+    .ca0102_chip = 1,
+    .ca0151_chip = 1,
+    .spk71 = 1,
+    .spdif_bug = 1,
+    .adc_1361t = 1,  /* 24 bit capture instead of 16bit */
+    .ac97_chip = 1} ,
+    {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x10051102,
+    .driver = "Audigy2", .name = "Audigy 2 EX [1005]",
+    .id = "Audigy2",
+    .emu10k2_chip = 1,
+    .ca0102_chip = 1,
+    .ca0151_chip = 1,
+    .spk71 = 1,
+    .spdif_bug = 1} ,
+    /* Dell OEM/Creative Labs Audigy 2 ZS */
+    /* See ALSA bug#1365 */
+    {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x10031102,
+    .driver = "Audigy2", .name = "Audigy 2 ZS [SB0353]",
+    .id = "Audigy2",
+    .emu10k2_chip = 1,
+    .ca0102_chip = 1,
+    .ca0151_chip = 1,
+    .spk71 = 1,
+    .spdif_bug = 1,
+    .ac97_chip = 1} ,
+    {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x10021102,
+    .driver = "Audigy2", .name = "Audigy 2 Platinum [SB0240P]",
+    .id = "Audigy2",
+    .emu10k2_chip = 1,
+    .ca0102_chip = 1,
+    .ca0151_chip = 1,
+    .spk71 = 1,
+    .spdif_bug = 1,
+    .ac97_chip = 1} ,
+    {.vendor = 0x1102, .device = 0x0004, .revision = 0x04,
+    .driver = "Audigy2", .name = "Audigy 2 [Unknown]",
+    .id = "Audigy2",
+    .emu10k2_chip = 1,
+    .ca0102_chip = 1,
+    .ca0151_chip = 1,
+    .spdif_bug = 1,
+    .ac97_chip = 1} ,
+    {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x00531102,
+    .driver = "Audigy", .name = "Audigy 1 [SB0090]",
+    .id = "Audigy",
+    .emu10k2_chip = 1,
+    .ca0102_chip = 1,
+    .ac97_chip = 1} ,
+    {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x00521102,
+    .driver = "Audigy", .name = "Audigy 1 ES [SB0160]",
+    .id = "Audigy",
+    .emu10k2_chip = 1,
+    .ca0102_chip = 1,
+    .spdif_bug = 1,
+    .ac97_chip = 1} ,
+    {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x00511102,
+    .driver = "Audigy", .name = "Audigy 1 [SB0090]",
+    .id = "Audigy",
+    .emu10k2_chip = 1,
+    .ca0102_chip = 1,
+    .ac97_chip = 1} ,
+    {.vendor = 0x1102, .device = 0x0004,
+    .driver = "Audigy", .name = "Audigy 1 [Unknown]",
+    .id = "Audigy",
+    .emu10k2_chip = 1,
+    .ca0102_chip = 1,
+    .ac97_chip = 1} ,
+    {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x806B1102,
+    .driver = "EMU10K1", .name = "SBLive! [SB0105]",
+    .id = "Live",
+    .emu10k1_chip = 1,
+    .ac97_chip = 1,
+    .sblive51 = 1} ,
+    {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x806A1102,
+    .driver = "EMU10K1", .name = "SBLive! Value [SB0103]",
+    .id = "Live",
+    .emu10k1_chip = 1,
+    .ac97_chip = 1,
+    .sblive51 = 1} ,
+    {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80691102,
+    .driver = "EMU10K1", .name = "SBLive! Value [SB0101]",
+    .id = "Live",
+    .emu10k1_chip = 1,
+    .ac97_chip = 1,
+    .sblive51 = 1},
+    /* Tested by ALSA bug#1680 26th December 2005 */
+    /* note: It really has SB0220 written on the card. */
+    {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80661102,
+    .driver = "EMU10K1", .name = "SB Live 5.1 Dell OEM [SB0220]",
+    .id = "Live",
+    .emu10k1_chip = 1,
+    .ac97_chip = 1,
+    .sblive51 = 1} ,
+    /* Tested by Thomas Zehetbauer 27th Aug 2005 */
+    {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80651102,
+    .driver = "EMU10K1", .name = "SB Live 5.1 [SB0220]",
+    .id = "Live",
+    .emu10k1_chip = 1,
+    .ac97_chip = 1,
+    .sblive51 = 1} ,
+    {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x100a1102,
+    .driver = "EMU10K1", .name = "SB Live 5.1 [SB0220]",
+    .id = "Live",
+    .emu10k1_chip = 1,
+    .ac97_chip = 1,
+    .sblive51 = 1} ,
+    {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80641102,
+    .driver = "EMU10K1", .name = "SB Live 5.1",
+    .id = "Live",
+    .emu10k1_chip = 1,
+    .ac97_chip = 1,
+    .sblive51 = 1} ,
+    /* Tested by alsa bugtrack user "hus" bug #1297 12th Aug 2005 */
+    {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80611102,
+    .driver = "EMU10K1", .name = "SBLive 5.1 [SB0060]",
+    .id = "Live",
+    .emu10k1_chip = 1,
+    .ac97_chip = 2, /* ac97 is optional; both SBLive 5.1 and platinum
+    * share the same IDs!
+    */
+    .sblive51 = 1} ,
+    {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80511102,
+    .driver = "EMU10K1", .name = "SBLive! Value [CT4850]",
+    .id = "Live",
+    .emu10k1_chip = 1,
+    .ac97_chip = 1,
+    .sblive51 = 1} ,
+    {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80401102,
+    .driver = "EMU10K1", .name = "SBLive! Platinum [CT4760P]",
+    .id = "Live",
+    .emu10k1_chip = 1,
+    .ac97_chip = 1} ,
+    {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80321102,
+    .driver = "EMU10K1", .name = "SBLive! Value [CT4871]",
+    .id = "Live",
+    .emu10k1_chip = 1,
+    .ac97_chip = 1,
+    .sblive51 = 1} ,
+    {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80311102,
+    .driver = "EMU10K1", .name = "SBLive! Value [CT4831]",
+    .id = "Live",
+    .emu10k1_chip = 1,
+    .ac97_chip = 1,
+    .sblive51 = 1} ,
+    {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80281102,
+    .driver = "EMU10K1", .name = "SBLive! Value [CT4870]",
+    .id = "Live",
+    .emu10k1_chip = 1,
+    .ac97_chip = 1,
+    .sblive51 = 1} ,
+    /* Tested by James@superbug.co.uk 3rd July 2005 */
+    {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80271102,
+    .driver = "EMU10K1", .name = "SBLive! Value [CT4832]",
+    .id = "Live",
+    .emu10k1_chip = 1,
+    .ac97_chip = 1,
+    .sblive51 = 1} ,
+    {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80261102,
+    .driver = "EMU10K1", .name = "SBLive! Value [CT4830]",
+    .id = "Live",
+    .emu10k1_chip = 1,
+    .ac97_chip = 1,
+    .sblive51 = 1} ,
+    {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80231102,
+    .driver = "EMU10K1", .name = "SB PCI512 [CT4790]",
+    .id = "Live",
+    .emu10k1_chip = 1,
+    .ac97_chip = 1,
+    .sblive51 = 1} ,
+    {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80221102,
+    .driver = "EMU10K1", .name = "SBLive! Value [CT4780]",
+    .id = "Live",
+    .emu10k1_chip = 1,
+    .ac97_chip = 1,
+    .sblive51 = 1} ,
+    {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x40011102,
+    .driver = "EMU10K1", .name = "E-mu APS [4001]",
+    .id = "APS",
+    .emu10k1_chip = 1,
+    .ecard = 1} ,
+    {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x00211102,
+    .driver = "EMU10K1", .name = "SBLive! [CT4620]",
+    .id = "Live",
+    .emu10k1_chip = 1,
+    .ac97_chip = 1,
+    .sblive51 = 1} ,
+    {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x00201102,
+    .driver = "EMU10K1", .name = "SBLive! Value [CT4670]",
+    .id = "Live",
+    .emu10k1_chip = 1,
+    .ac97_chip = 1,
+    .sblive51 = 1} ,
+    {.vendor = 0x1102, .device = 0x0002,
+    .driver = "EMU10K1", .name = "SB Live [Unknown]",
+    .id = "Live",
+    .emu10k1_chip = 1,
+    .ac97_chip = 1,
+    .sblive51 = 1} ,
+    {0 } /* terminator */
+};
+
+int __devinit snd_emu10k1_create(struct snd_card *card,
+                                 struct pci_dev * pci,
+                                 unsigned short extin_mask,
+                                 unsigned short extout_mask,
+                                 long max_cache_bytes,
+                                 int enable_ir,
+                                 uint subsystem,
+                                 struct snd_emu10k1 ** remu)
+{
+    struct snd_emu10k1 *emu;
+    int idx, err;
+    int is_audigy;
+    unsigned char revision;
+    unsigned int silent_page;
+    const struct snd_emu_chip_details *c;
+    static struct snd_device_ops ops = {
+        .dev_free =	snd_emu10k1_dev_free,
+    };
+
+    *remu = NULL;
+
+    /* enable PCI device */
+    if ((err = pci_enable_device(pci)) < 0)
+        return err;
+
+    emu = kzalloc(sizeof(*emu), GFP_KERNEL);
+    if (emu == NULL) {
+        pci_disable_device(pci);
+        return -ENOMEM;
+    }
+    emu->card = card;
+    spin_lock_init(&emu->reg_lock);
+    spin_lock_init(&emu->emu_lock);
+    spin_lock_init(&emu->voice_lock);
+    spin_lock_init(&emu->synth_lock);
+    spin_lock_init(&emu->memblk_lock);
+    init_MUTEX(&emu->fx8010.lock);
+    INIT_LIST_HEAD(&emu->mapped_link_head);
+    INIT_LIST_HEAD(&emu->mapped_order_link_head);
+    emu->pci = pci;
+    emu->irq = -1;
+    emu->synth = NULL;
+    emu->get_synth_voice = NULL;
+    /* read revision & serial */
+    pci_read_config_byte(pci, PCI_REVISION_ID, &revision);
+    emu->revision = revision;
+    pci_read_config_dword(pci, PCI_SUBSYSTEM_VENDOR_ID, &emu->serial);
+    pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &emu->model);
+    snd_printk("vendor=0x%x, device=0x%x, subsystem_vendor_id=0x%x, subsystem_id=0x%x\n",pci->vendor, pci->device, emu->serial, emu->model);
+
+    for (c=emu_chip_details; c->vendor; c++) {
+        if (c->vendor == pci->vendor && c->device == pci->device) {
+            if (subsystem) {
+                if (c->subsystem && (c->subsystem == subsystem) ) {
+                    break;
+                } else continue;
+            } else {
+                if (c->subsystem && (c->subsystem != emu->serial) )
+                    continue;
+                if (c->revision && c->revision != emu->revision)
+                    continue;
+            }
+            break;
+        }
+    }
+    if (c->vendor == 0) {
+        snd_printk(KERN_ERR "emu10k1: Card not recognised\n");
+        kfree(emu);
+        pci_disable_device(pci);
+        return -ENOENT;
+    }
+    emu->card_capabilities = c;
+    if (c->subsystem && !subsystem)
+        snd_printdd("Sound card name=%s\n", c->name);
+    else if (subsystem)
+        snd_printdd("Sound card name=%s, vendor=0x%x, device=0x%x, subsystem=0x%x. Forced to subsytem=0x%x\n",
+                    c->name, pci->vendor, pci->device, emu->serial, c->subsystem);
+    else
+        snd_printdd("Sound card name=%s, vendor=0x%x, device=0x%x, subsystem=0x%x.\n",
+                    c->name, pci->vendor, pci->device, emu->serial);
+
+    if (!*card->id && c->id) {
+        int i, n = 0;
+        strlcpy(card->id, c->id, sizeof(card->id));
+        for (;;) {
+            for (i = 0; i < snd_ecards_limit; i++) {
+                if (snd_cards[i] && !strcmp(snd_cards[i]->id, card->id))
+                    break;
+            }
+            if (i >= snd_ecards_limit)
+                break;
+            n++;
+            if (n >= SNDRV_CARDS)
+                break;
+            sprintf(card->id, "%s_%d", c->id, n);
+        }
+    }
+
+    is_audigy = emu->audigy = c->emu10k2_chip;
+
+    /* set the DMA transfer mask */
+    emu->dma_mask = is_audigy ? AUDIGY_DMA_MASK : EMU10K1_DMA_MASK;
+    pci_set_dma_mask(pci, emu->dma_mask);
+    if (is_audigy)
+        emu->gpr_base = A_FXGPREGBASE;
+    else
+        emu->gpr_base = FXGPREGBASE;
+
+    if ((err = pci_request_regions(pci, "EMU10K1")) < 0) {
+        kfree(emu);
+        pci_disable_device(pci);
+        return err;
+    }
+    emu->port = pci_resource_start(pci, 0);
+
+    if (request_irq(pci->irq, snd_emu10k1_interrupt, SA_INTERRUPT|SA_SHIRQ, "EMU10K1", (void *)emu)) {
+        snd_emu10k1_free(emu);
+        return -EBUSY;
+    }
+    emu->irq = pci->irq;
+
+    emu->max_cache_pages = max_cache_bytes >> PAGE_SHIFT;
+    if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
+                            32 * 1024, &emu->ptb_pages) < 0) {
+        err = -ENOMEM;
+        goto error;
+    }
+
+    emu->page_ptr_table = (void **)vmalloc(emu->max_cache_pages * sizeof(void*));
+    emu->page_addr_table = (unsigned long*)vmalloc(emu->max_cache_pages * sizeof(unsigned long));
+    if (emu->page_ptr_table == NULL || emu->page_addr_table == NULL) {
+        err = -ENOMEM;
+        goto error;
+    }
+
+    if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
+                            EMUPAGESIZE, &emu->silent_page) < 0) {
+        err = -ENOMEM;
+        goto error;
+    }
+    emu->memhdr = snd_util_memhdr_new(emu->max_cache_pages * PAGE_SIZE);
+    if (emu->memhdr == NULL) {
+        err = -ENOMEM;
+        goto error;
+    }
+    emu->memhdr->block_extra_size = sizeof(struct snd_emu10k1_memblk) -
+        sizeof(struct snd_util_memblk);
+
+    pci_set_master(pci);
+
+    emu->fx8010.fxbus_mask = 0x303f;
+    if (extin_mask == 0)
+        extin_mask = 0x3fcf;
+    if (extout_mask == 0)
+        extout_mask = 0x7fff;
+    emu->fx8010.extin_mask = extin_mask;
+    emu->fx8010.extout_mask = extout_mask;
+    emu->enable_ir = enable_ir;
+
+    if (emu->card_capabilities->ecard) {
+        if ((err = snd_emu10k1_ecard_init(emu)) < 0)
+            goto error;
+    } else if (emu->card_capabilities->ca_cardbus_chip) {
+        if ((err = snd_emu10k1_cardbus_init(emu)) < 0)
+            goto error;
+    } else if (emu->card_capabilities->emu1010) {
+        if ((err = snd_emu10k1_emu1010_init(emu)) < 0) {
+            snd_emu10k1_free(emu);
+            return err;
+        }
+    } else {
+        /* 5.1: Enable the additional AC97 Slots. If the emu10k1 version
+         does not support this, it shouldn't do any harm */
+        snd_emu10k1_ptr_write(emu, AC97SLOT, 0, AC97SLOT_CNTR|AC97SLOT_LFE);
+    }
+
+    /* initialize TRAM setup */
+    emu->fx8010.itram_size = (16 * 1024)/2;
+    emu->fx8010.etram_pages.area = NULL;
+    emu->fx8010.etram_pages.bytes = 0;
+
+    /*
+     *  Init to 0x02109204 :
+     *  Clock accuracy    = 0     (1000ppm)
+     *  Sample Rate       = 2     (48kHz)
+     *  Audio Channel     = 1     (Left of 2)
+     *  Source Number     = 0     (Unspecified)
+     *  Generation Status = 1     (Original for Cat Code 12)
+     *  Cat Code          = 12    (Digital Signal Mixer)
+     *  Mode              = 0     (Mode 0)
+     *  Emphasis          = 0     (None)
+     *  CP                = 1     (Copyright unasserted)
+     *  AN                = 0     (Audio data)
+     *  P                 = 0     (Consumer)
+     */
+    emu->spdif_bits[0] = emu->spdif_bits[1] =
+        emu->spdif_bits[2] = SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
+        SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
+        SPCS_GENERATIONSTATUS | 0x00001200 |
+        0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT;
+
+    emu->reserved_page = (struct snd_emu10k1_memblk *)
+        snd_emu10k1_synth_alloc(emu, 4096);
+    if (emu->reserved_page)
+        emu->reserved_page->map_locked = 1;
+
+    /* Clear silent pages and set up pointers */
+    memset(emu->silent_page.area, 0, PAGE_SIZE);
+    silent_page = emu->silent_page.addr << 1;
+    for (idx = 0; idx < MAXPAGES; idx++)
+        ((u32 *)emu->ptb_pages.area)[idx] = cpu_to_le32(silent_page | idx);
+
+    /* set up voice indices */
+    for (idx = 0; idx < NUM_G; idx++) {
+        emu->voices[idx].emu = emu;
+        emu->voices[idx].number = idx;
+    }
+
+    if ((err = snd_emu10k1_init(emu, enable_ir, 0)) < 0)
+        goto error;
+#ifdef CONFIG_PM
+    if ((err = alloc_pm_buffer(emu)) < 0)
+        goto error;
+#endif
+
+    /*  Initialize the effect engine */
+    if ((err = snd_emu10k1_init_efx(emu)) < 0)
+        goto error;
+    snd_emu10k1_audio_enable(emu);
+
+    if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, emu, &ops)) < 0)
+        goto error;
+
+#ifdef CONFIG_PROC_FS
+    snd_emu10k1_proc_init(emu);
+#endif
+
+    snd_card_set_dev(card, &pci->dev);
+    *remu = emu;
+    return 0;
+
+error:
+    snd_emu10k1_free(emu);
+    return err;
+}
+
+#ifdef CONFIG_PM
+static unsigned char saved_regs[] = {
+    CPF, PTRX, CVCF, VTFT, Z1, Z2, PSST, DSL, CCCA, CCR, CLP,
+    FXRT, MAPA, MAPB, ENVVOL, ATKHLDV, DCYSUSV, LFOVAL1, ENVVAL,
+    ATKHLDM, DCYSUSM, LFOVAL2, IP, IFATN, PEFE, FMMOD, TREMFRQ, FM2FRQ2,
+    TEMPENV, ADCCR, FXWC, MICBA, ADCBA, FXBA,
+    MICBS, ADCBS, FXBS, CDCS, GPSCS, SPCS0, SPCS1, SPCS2,
+    SPBYPASS, AC97SLOT, CDSRCS, GPSRCS, ZVSRCS, MICIDX, ADCIDX, FXIDX,
+    0xff /* end */
+};
+static unsigned char saved_regs_audigy[] = {
+    A_ADCIDX, A_MICIDX, A_FXWC1, A_FXWC2, A_SAMPLE_RATE,
+    A_FXRT2, A_SENDAMOUNTS, A_FXRT1,
+    0xff /* end */
+};
+
+static int __devinit alloc_pm_buffer(struct snd_emu10k1 *emu)
+{
+    int size;
+
+    size = ARRAY_SIZE(saved_regs);
+    if (emu->audigy)
+        size += ARRAY_SIZE(saved_regs_audigy);
+    emu->saved_ptr = vmalloc(4 * NUM_G * size);
+    if (! emu->saved_ptr)
+        return -ENOMEM;
+    if (snd_emu10k1_efx_alloc_pm_buffer(emu) < 0)
+        return -ENOMEM;
+    if (emu->card_capabilities->ca0151_chip &&
+        snd_p16v_alloc_pm_buffer(emu) < 0)
+        return -ENOMEM;
+
+    return 0;
+}
+
+static void free_pm_buffer(struct snd_emu10k1 *emu)
+{
+    vfree(emu->saved_ptr);
+    snd_emu10k1_efx_free_pm_buffer(emu);
+    if (emu->card_capabilities->ca0151_chip)
+        snd_p16v_free_pm_buffer(emu);
+}
+
+void snd_emu10k1_suspend_regs(struct snd_emu10k1 *emu)
+{
+    int i;
+    unsigned char *reg;
+    unsigned int *val;
+
+    val = emu->saved_ptr;
+    for (reg = saved_regs; *reg != 0xff; reg++)
+        for (i = 0; i < NUM_G; i++, val++)
+            *val = snd_emu10k1_ptr_read(emu, *reg, i);
+    if (emu->audigy) {
+        for (reg = saved_regs_audigy; *reg != 0xff; reg++)
+            for (i = 0; i < NUM_G; i++, val++)
+                *val = snd_emu10k1_ptr_read(emu, *reg, i);
+    }
+    if (emu->audigy)
+        emu->saved_a_iocfg = inl(emu->port + A_IOCFG);
+    emu->saved_hcfg = inl(emu->port + HCFG);
+}
+
+void snd_emu10k1_resume_init(struct snd_emu10k1 *emu)
+{
+    if (emu->card_capabilities->ecard)
+        snd_emu10k1_ecard_init(emu);
+    else if (emu->card_capabilities->ca_cardbus_chip)
+        snd_emu10k1_cardbus_init(emu);
+    else if (emu->card_capabilities->emu1010)
+        snd_emu10k1_emu1010_init(emu);
+    else
+        snd_emu10k1_ptr_write(emu, AC97SLOT, 0, AC97SLOT_CNTR|AC97SLOT_LFE);
+    snd_emu10k1_init(emu, emu->enable_ir, 1);
+}
+
+void snd_emu10k1_resume_regs(struct snd_emu10k1 *emu)
+{
+    int i;
+    unsigned char *reg;
+    unsigned int *val;
+
+    snd_emu10k1_audio_enable(emu);
+
+    /* resore for spdif */
+    if (emu->audigy)
+        outl(emu->saved_a_iocfg, emu->port + A_IOCFG);
+    outl(emu->saved_hcfg, emu->port + HCFG);
+
+    val = emu->saved_ptr;
+    for (reg = saved_regs; *reg != 0xff; reg++)
+        for (i = 0; i < NUM_G; i++, val++)
+            snd_emu10k1_ptr_write(emu, *reg, i, *val);
+    if (emu->audigy) {
+        for (reg = saved_regs_audigy; *reg != 0xff; reg++)
+            for (i = 0; i < NUM_G; i++, val++)
+                snd_emu10k1_ptr_write(emu, *reg, i, *val);
+    }
+}
+#endif
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/emu10k1/emu10k1_patch.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/emu10k1/emu10k1_patch.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/emu10k1/emu10k1_patch.c	(revision 91)
@@ -0,0 +1,227 @@
+/*
+ *  Patch transfer callback for Emu10k1
+ *
+ *  Copyright (C) 2000 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
+ */
+/*
+ * All the code for loading in a patch.  There is very little that is
+ * chip specific here.  Just the actual writing to the board.
+ */
+
+#include "emu10k1_synth_local.h"
+
+/*
+ */
+#define BLANK_LOOP_START	4
+#define BLANK_LOOP_END		8
+#define BLANK_LOOP_SIZE		12
+#define BLANK_HEAD_SIZE		32
+
+/*
+ * allocate a sample block and copy data from userspace
+ */
+int
+snd_emu10k1_sample_new(struct snd_emux *rec, struct snd_sf_sample *sp,
+		       struct snd_util_memhdr *hdr,
+		       const void __user *data, long count)
+{
+	int offset;
+	int truesize, size, loopsize, blocksize;
+	int loopend, sampleend;
+	unsigned int start_addr;
+	struct snd_emu10k1 *emu;
+
+        emu = rec->hw;
+	snd_assert(sp != NULL, return -EINVAL);
+	snd_assert(hdr != NULL, return -EINVAL);
+
+	if (sp->v.size == 0) {
+		snd_printd("emu: rom font for sample %d\n", sp->v.sample);
+		return 0;
+	}
+
+	/* recalculate address offset */
+	sp->v.end -= sp->v.start;
+	sp->v.loopstart -= sp->v.start;
+	sp->v.loopend -= sp->v.start;
+	sp->v.start = 0;
+
+	/* some samples have invalid data.  the addresses are corrected in voice info */
+	sampleend = sp->v.end;
+	if (sampleend > sp->v.size)
+		sampleend = sp->v.size;
+	loopend = sp->v.loopend;
+	if (loopend > sampleend)
+		loopend = sampleend;
+
+	/* be sure loop points start < end */
+	if (sp->v.loopstart >= sp->v.loopend) {
+		int tmp = sp->v.loopstart;
+		sp->v.loopstart = sp->v.loopend;
+		sp->v.loopend = tmp;
+	}
+
+	/* compute true data size to be loaded */
+	truesize = sp->v.size + BLANK_HEAD_SIZE;
+	loopsize = 0;
+#if 0 /* not supported */
+	if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP))
+		loopsize = sp->v.loopend - sp->v.loopstart;
+	truesize += loopsize;
+#endif
+	if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK)
+		truesize += BLANK_LOOP_SIZE;
+
+	/* try to allocate a memory block */
+	blocksize = truesize;
+	if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
+		blocksize *= 2;
+	sp->block = snd_emu10k1_synth_alloc(emu, blocksize);
+	if (sp->block == NULL) {
+		snd_printd("emu10k1: synth malloc failed (size=%d)\n", blocksize);
+		/* not ENOMEM (for compatibility with OSS) */
+		return -ENOSPC;
+	}
+	/* set the total size */
+	sp->v.truesize = blocksize;
+
+	/* write blank samples at head */
+	offset = 0;
+	size = BLANK_HEAD_SIZE;
+	if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
+		size *= 2;
+	snd_assert(offset + size <= blocksize, return -EINVAL);
+	snd_emu10k1_synth_bzero(emu, sp->block, offset, size);
+	offset += size;
+
+	/* copy start->loopend */
+	size = loopend;
+	if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
+		size *= 2;
+	snd_assert(offset + size <= blocksize, return -EINVAL);
+	if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size)) {
+		snd_emu10k1_synth_free(emu, sp->block);
+		sp->block = NULL;
+		return -EFAULT;
+	}
+	offset += size;
+#ifdef TARGET_OS2
+	data = (char *)data + size;
+#else
+	data += size;
+#endif
+#if 0 /* not suppported yet */
+	/* handle reverse (or bidirectional) loop */
+	if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP)) {
+		/* copy loop in reverse */
+		if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) {
+			int woffset;
+			unsigned short *wblock = (unsigned short*)block;
+			woffset = offset / 2;
+			snd_assert(offset + loopsize*2 <= blocksize, return -EINVAL);
+			for (i = 0; i < loopsize; i++)
+				wblock[woffset + i] = wblock[woffset - i -1];
+			offset += loopsize * 2;
+		} else {
+			snd_assert(offset + loopsize <= blocksize, return -EINVAL);
+			for (i = 0; i < loopsize; i++)
+				block[offset + i] = block[offset - i -1];
+			offset += loopsize;
+		}
+
+		/* modify loop pointers */
+		if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_BIDIR_LOOP) {
+			sp->v.loopend += loopsize;
+		} else {
+			sp->v.loopstart += loopsize;
+			sp->v.loopend += loopsize;
+		}
+		/* add sample pointer */
+		sp->v.end += loopsize;
+	}
+#endif
+
+	/* loopend -> sample end */
+	size = sp->v.size - loopend;
+	snd_assert(size >= 0, return -EINVAL);
+	if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
+		size *= 2;
+	if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size)) {
+		snd_emu10k1_synth_free(emu, sp->block);
+		sp->block = NULL;
+		return -EFAULT;
+	}
+	offset += size;
+
+	/* clear rest of samples (if any) */
+	if (offset < blocksize)
+		snd_emu10k1_synth_bzero(emu, sp->block, offset, blocksize - offset);
+
+	if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK) {
+		/* if no blank loop is attached in the sample, add it */
+		if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_SINGLESHOT) {
+			sp->v.loopstart = sp->v.end + BLANK_LOOP_START;
+			sp->v.loopend = sp->v.end + BLANK_LOOP_END;
+		}
+	}
+
+#if 0 /* not supported yet */
+	if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_UNSIGNED) {
+		/* unsigned -> signed */
+		if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) {
+			unsigned short *wblock = (unsigned short*)block;
+			for (i = 0; i < truesize; i++)
+				wblock[i] ^= 0x8000;
+		} else {
+			for (i = 0; i < truesize; i++)
+				block[i] ^= 0x80;
+		}
+	}
+#endif
+
+	/* recalculate offset */
+	start_addr = BLANK_HEAD_SIZE * 2;
+	if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
+		start_addr >>= 1;
+	sp->v.start += start_addr;
+	sp->v.end += start_addr;
+	sp->v.loopstart += start_addr;
+	sp->v.loopend += start_addr;
+
+	return 0;
+}
+
+/*
+ * free a sample block
+ */
+int
+snd_emu10k1_sample_free(struct snd_emux *rec, struct snd_sf_sample *sp,
+                        struct snd_util_memhdr *hdr)
+{
+	struct snd_emu10k1 *emu;
+
+        emu = rec->hw;
+	snd_assert(sp != NULL, return -EINVAL);
+	snd_assert(hdr != NULL, return -EINVAL);
+
+	if (sp->block) {
+		snd_emu10k1_synth_free(emu, sp->block);
+		sp->block = NULL;
+	}
+	return 0;
+}
+
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/emu10k1/emu10k1_synth.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/emu10k1/emu10k1_synth.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/emu10k1/emu10k1_synth.c	(revision 91)
@@ -0,0 +1,120 @@
+/*
+ *  Copyright (C) 2000 Takashi Iwai <tiwai@suse.de>
+ *
+ *  Routines for control of EMU10K1 WaveTable synth
+ *
+ *   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 "emu10k1_synth_local.h"
+#include <linux/init.h>
+
+MODULE_AUTHOR("Takashi Iwai");
+MODULE_DESCRIPTION("Routines for control of EMU10K1 WaveTable synth");
+MODULE_LICENSE("GPL");
+
+/*
+ * create a new hardware dependent device for Emu10k1
+ */
+static int snd_emu10k1_synth_new_device(struct snd_seq_device *dev)
+{
+	struct snd_emux *emu;
+	struct snd_emu10k1 *hw;
+	struct snd_emu10k1_synth_arg *arg;
+	unsigned long flags;
+
+	arg = SNDRV_SEQ_DEVICE_ARGPTR(dev);
+	if (arg == NULL)
+		return -EINVAL;
+
+	if (arg->seq_ports <= 0)
+		return 0; /* nothing */
+	if (arg->max_voices < 1)
+		arg->max_voices = 1;
+	else if (arg->max_voices > 64)
+		arg->max_voices = 64;
+
+	if (snd_emux_new(&emu) < 0)
+		return -ENOMEM;
+
+	snd_emu10k1_ops_setup(emu);
+	emu->hw = hw = arg->hwptr;
+	emu->max_voices = arg->max_voices;
+	emu->num_ports = arg->seq_ports;
+	emu->pitch_shift = -501;
+	emu->memhdr = hw->memhdr;
+	emu->midi_ports = arg->seq_ports < 2 ? arg->seq_ports : 2; /* maximum two ports */
+	emu->midi_devidx = hw->audigy ? 2 : 1; /* audigy has two external midis */
+	emu->linear_panning = 0;
+	emu->hwdep_idx = 2; /* FIXED */
+
+	if (snd_emux_register(emu, dev->card, arg->index, "Emu10k1") < 0) {
+		snd_emux_free(emu);
+		return -ENOMEM;
+	}
+
+	spin_lock_irqsave(&hw->voice_lock, flags);
+	hw->synth = emu;
+	hw->get_synth_voice = snd_emu10k1_synth_get_voice;
+	spin_unlock_irqrestore(&hw->voice_lock, flags);
+
+	dev->driver_data = emu;
+
+	return 0;
+}
+
+static int snd_emu10k1_synth_delete_device(struct snd_seq_device *dev)
+{
+	struct snd_emux *emu;
+	struct snd_emu10k1 *hw;
+	unsigned long flags;
+
+	if (dev->driver_data == NULL)
+		return 0; /* not registered actually */
+
+        emu = dev->driver_data;
+
+        hw = emu->hw;
+	spin_lock_irqsave(&hw->voice_lock, flags);
+	hw->synth = NULL;
+	hw->get_synth_voice = NULL;
+	spin_unlock_irqrestore(&hw->voice_lock, flags);
+
+	snd_emux_free(emu);
+	return 0;
+}
+
+
+/*
+ *  INIT part
+ */
+
+static int __init alsa_emu10k1_synth_init(void)
+{
+    static struct snd_seq_dev_ops ops = {
+		snd_emu10k1_synth_new_device,
+		snd_emu10k1_synth_delete_device,
+	};
+	return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_EMU10K1_SYNTH, &ops,
+					      sizeof(struct snd_emu10k1_synth_arg));
+}
+
+static void __exit alsa_emu10k1_synth_exit(void)
+{
+	snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_EMU10K1_SYNTH);
+}
+
+module_init(alsa_emu10k1_synth_init)
+module_exit(alsa_emu10k1_synth_exit)
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/emu10k1/emu10k1_synth_local.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/emu10k1/emu10k1_synth_local.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/emu10k1/emu10k1_synth_local.h	(revision 91)
@@ -0,0 +1,43 @@
+#ifndef __EMU10K1_SYNTH_LOCAL_H
+#define __EMU10K1_SYNTH_LOCAL_H
+/*
+ *  Local defininitons for Emu10k1 wavetable
+ *
+ *  Copyright (C) 2000 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 <linux/time.h>
+#include <sound/core.h>
+#include <sound/emu10k1_synth.h>
+
+/* emu10k1_patch.c */
+int snd_emu10k1_sample_new(struct snd_emux *private_data,
+			   struct snd_sf_sample *sp,
+			   struct snd_util_memhdr *hdr,
+			   const void __user *_data, long count);
+int snd_emu10k1_sample_free(struct snd_emux *private_data,
+			    struct snd_sf_sample *sp,
+			    struct snd_util_memhdr *hdr);
+int snd_emu10k1_memhdr_init(struct snd_emux *emu);
+
+/* emu10k1_callback.c */
+void snd_emu10k1_ops_setup(struct snd_emux *emu);
+int snd_emu10k1_synth_get_voice(struct snd_emu10k1 *hw);
+
+
+#endif	/* __EMU10K1_SYNTH_LOCAL_H */
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/emu10k1/emu10k1x.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/emu10k1/emu10k1x.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/emu10k1/emu10k1x.c	(revision 91)
@@ -0,0 +1,1639 @@
+/*
+ *  Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com>
+ *  Driver EMU10K1X chips
+ *
+ *  Parts of this code were adapted from audigyls.c driver which is
+ *  Copyright (c) by James Courtier-Dutton <James@superbug.demon.co.uk>
+ *
+ *  BUGS:
+ *    --
+ *
+ *  TODO:
+ *
+ *  Chips (SB0200 model):
+ *    - EMU10K1X-DBQ
+ *    - STAC 9708T
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include <sound/info.h>
+#include <sound/rawmidi.h>
+
+MODULE_AUTHOR("Francisco Moraes <fmoraes@nc.rr.com>");
+MODULE_DESCRIPTION("EMU10K1X");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{Dell Creative Labs,SB Live!}");
+
+// module parameters (see "Module Parameters")
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+
+//module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for the EMU10K1X soundcard.");
+//module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for the EMU10K1X soundcard.");
+//module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable the EMU10K1X soundcard.");
+
+
+// some definitions were borrowed from emu10k1 driver as they seem to be the same
+/************************************************************************************************/
+/* PCI function 0 registers, address = <val> + PCIBASE0						*/
+/************************************************************************************************/
+
+#define PTR			0x00		/* Indexed register set pointer register	*/
+/* NOTE: The CHANNELNUM and ADDRESS words can	*/
+/* be modified independently of each other.	*/
+
+#define DATA			0x04		/* Indexed register set data register		*/
+
+#define IPR			0x08		/* Global interrupt pending register		*/
+/* Clear pending interrupts by writing a 1 to	*/
+/* the relevant bits and zero to the other bits	*/
+#define IPR_MIDITRANSBUFEMPTY   0x00000001	/* MIDI UART transmit buffer empty		*/
+#define IPR_MIDIRECVBUFEMPTY    0x00000002	/* MIDI UART receive buffer empty		*/
+#define IPR_CH_0_LOOP           0x00000800      /* Channel 0 loop                               */
+#define IPR_CH_0_HALF_LOOP      0x00000100      /* Channel 0 half loop                          */
+#define IPR_CAP_0_LOOP          0x00080000      /* Channel capture loop                         */
+#define IPR_CAP_0_HALF_LOOP     0x00010000      /* Channel capture half loop                    */
+
+#define INTE			0x0c		/* Interrupt enable register			*/
+#define INTE_MIDITXENABLE       0x00000001	/* Enable MIDI transmit-buffer-empty interrupts	*/
+#define INTE_MIDIRXENABLE       0x00000002	/* Enable MIDI receive-buffer-empty interrupts	*/
+#define INTE_CH_0_LOOP          0x00000800      /* Channel 0 loop                               */
+#define INTE_CH_0_HALF_LOOP     0x00000100      /* Channel 0 half loop                          */
+#define INTE_CAP_0_LOOP         0x00080000      /* Channel capture loop                         */
+#define INTE_CAP_0_HALF_LOOP    0x00010000      /* Channel capture half loop                    */
+
+#define HCFG			0x14		/* Hardware config register			*/
+
+#define HCFG_LOCKSOUNDCACHE	0x00000008	/* 1 = Cancel bustmaster accesses to soundcache */
+/* NOTE: This should generally never be used.  	*/
+#define HCFG_AUDIOENABLE	0x00000001	/* 0 = CODECs transmit zero-valued samples	*/
+/* Should be set to 1 when the EMU10K1 is	*/
+/* completely initialized.			*/
+#define GPIO			0x18		/* Defaults: 00001080-Analog, 00001000-SPDIF.   */
+
+
+#define AC97DATA		0x1c		/* AC97 register set data register (16 bit)	*/
+
+#define AC97ADDRESS		0x1e		/* AC97 register set address register (8 bit)	*/
+
+/********************************************************************************************************/
+/* Emu10k1x pointer-offset register set, accessed through the PTR and DATA registers			*/
+/********************************************************************************************************/
+#define PLAYBACK_LIST_ADDR	0x00		/* Base DMA address of a list of pointers to each period/size */
+/* One list entry: 4 bytes for DMA address,
+ * 4 bytes for period_size << 16.
+ * One list entry is 8 bytes long.
+ * One list entry for each period in the buffer.
+ */
+#define PLAYBACK_LIST_SIZE	0x01		/* Size of list in bytes << 16. E.g. 8 periods -> 0x00380000  */
+#define PLAYBACK_LIST_PTR	0x02		/* Pointer to the current period being played */
+#define PLAYBACK_DMA_ADDR	0x04		/* Playback DMA addresss */
+#define PLAYBACK_PERIOD_SIZE	0x05		/* Playback period size */
+#define PLAYBACK_POINTER	0x06		/* Playback period pointer. Sample currently in DAC */
+#define PLAYBACK_UNKNOWN1       0x07
+#define PLAYBACK_UNKNOWN2       0x08
+
+/* Only one capture channel supported */
+#define CAPTURE_DMA_ADDR	0x10		/* Capture DMA address */
+#define CAPTURE_BUFFER_SIZE	0x11		/* Capture buffer size */
+#define CAPTURE_POINTER		0x12		/* Capture buffer pointer. Sample currently in ADC */
+#define CAPTURE_UNKNOWN         0x13
+
+/* From 0x20 - 0x3f, last samples played on each channel */
+
+#define TRIGGER_CHANNEL         0x40            /* Trigger channel playback                     */
+#define TRIGGER_CHANNEL_0       0x00000001      /* Trigger channel 0                            */
+#define TRIGGER_CHANNEL_1       0x00000002      /* Trigger channel 1                            */
+#define TRIGGER_CHANNEL_2       0x00000004      /* Trigger channel 2                            */
+#define TRIGGER_CAPTURE         0x00000100      /* Trigger capture channel                      */
+
+#define ROUTING                 0x41            /* Setup sound routing ?                        */
+#define ROUTING_FRONT_LEFT      0x00000001
+#define ROUTING_FRONT_RIGHT     0x00000002
+#define ROUTING_REAR_LEFT       0x00000004
+#define ROUTING_REAR_RIGHT      0x00000008
+#define ROUTING_CENTER_LFE      0x00010000
+
+#define SPCS0			0x42		/* SPDIF output Channel Status 0 register	*/
+
+#define SPCS1			0x43		/* SPDIF output Channel Status 1 register	*/
+
+#define SPCS2			0x44		/* SPDIF output Channel Status 2 register	*/
+
+#define SPCS_CLKACCYMASK	0x30000000	/* Clock accuracy				*/
+#define SPCS_CLKACCY_1000PPM	0x00000000	/* 1000 parts per million			*/
+#define SPCS_CLKACCY_50PPM	0x10000000	/* 50 parts per million				*/
+#define SPCS_CLKACCY_VARIABLE	0x20000000	/* Variable accuracy				*/
+#define SPCS_SAMPLERATEMASK	0x0f000000	/* Sample rate					*/
+#define SPCS_SAMPLERATE_44	0x00000000	/* 44.1kHz sample rate				*/
+#define SPCS_SAMPLERATE_48	0x02000000	/* 48kHz sample rate				*/
+#define SPCS_SAMPLERATE_32	0x03000000	/* 32kHz sample rate				*/
+#define SPCS_CHANNELNUMMASK	0x00f00000	/* Channel number				*/
+#define SPCS_CHANNELNUM_UNSPEC	0x00000000	/* Unspecified channel number			*/
+#define SPCS_CHANNELNUM_LEFT	0x00100000	/* Left channel					*/
+#define SPCS_CHANNELNUM_RIGHT	0x00200000	/* Right channel				*/
+#define SPCS_SOURCENUMMASK	0x000f0000	/* Source number				*/
+#define SPCS_SOURCENUM_UNSPEC	0x00000000	/* Unspecified source number			*/
+#define SPCS_GENERATIONSTATUS	0x00008000	/* Originality flag (see IEC-958 spec)		*/
+#define SPCS_CATEGORYCODEMASK	0x00007f00	/* Category code (see IEC-958 spec)		*/
+#define SPCS_MODEMASK		0x000000c0	/* Mode (see IEC-958 spec)			*/
+#define SPCS_EMPHASISMASK	0x00000038	/* Emphasis					*/
+#define SPCS_EMPHASIS_NONE	0x00000000	/* No emphasis					*/
+#define SPCS_EMPHASIS_50_15	0x00000008	/* 50/15 usec 2 channel				*/
+#define SPCS_COPYRIGHT		0x00000004	/* Copyright asserted flag -- do not modify	*/
+#define SPCS_NOTAUDIODATA	0x00000002	/* 0 = Digital audio, 1 = not audio		*/
+#define SPCS_PROFESSIONAL	0x00000001	/* 0 = Consumer (IEC-958), 1 = pro (AES3-1992)	*/
+
+#define SPDIF_SELECT		0x45		/* Enables SPDIF or Analogue outputs 0-Analogue, 0x700-SPDIF */
+
+/* This is the MPU port on the card                      					*/
+#define MUDATA		0x47
+#define MUCMD		0x48
+#define MUSTAT		MUCMD
+
+/* From 0x50 - 0x5f, last samples captured */
+
+/**
+ * The hardware has 3 channels for playback and 1 for capture.
+ *  - channel 0 is the front channel
+ *  - channel 1 is the rear channel
+ *  - channel 2 is the center/lfe chanel
+ * Volume is controlled by the AC97 for the front and rear channels by
+ * the PCM Playback Volume, Sigmatel Surround Playback Volume and
+ * Surround Playback Volume. The Sigmatel 4-Speaker Stereo switch affects
+ * the front/rear channel mixing in the REAR OUT jack. When using the
+ * 4-Speaker Stereo, both front and rear channels will be mixed in the
+ * REAR OUT.
+ * The center/lfe channel has no volume control and cannot be muted during
+ * playback.
+ */
+
+struct emu10k1x_voice {
+    struct emu10k1x *emu;
+    int number;
+    int use;
+
+    struct emu10k1x_pcm *epcm;
+};
+
+struct emu10k1x_pcm {
+    struct emu10k1x *emu;
+    struct snd_pcm_substream *substream;
+    struct emu10k1x_voice *voice;
+    unsigned short running;
+};
+
+struct emu10k1x_midi {
+    struct emu10k1x *emu;
+    struct snd_rawmidi *rmidi;
+    struct snd_rawmidi_substream *substream_input;
+    struct snd_rawmidi_substream *substream_output;
+    unsigned int midi_mode;
+    spinlock_t input_lock;
+    spinlock_t output_lock;
+    spinlock_t open_lock;
+    int tx_enable, rx_enable;
+    int port;
+    int ipr_tx, ipr_rx;
+    void (*interrupt)(struct emu10k1x *emu, unsigned int status);
+};
+
+// definition of the chip-specific record
+struct emu10k1x {
+    struct snd_card *card;
+    struct pci_dev *pci;
+
+    unsigned long port;
+    struct resource *res_port;
+    int irq;
+
+    unsigned int revision;		/* chip revision */
+    unsigned int serial;            /* serial number */
+    unsigned short model;		/* subsystem id */
+
+    spinlock_t emu_lock;
+    spinlock_t voice_lock;
+
+    struct snd_ac97 *ac97;
+    struct snd_pcm *pcm;
+
+    struct emu10k1x_voice voices[3];
+    struct emu10k1x_voice capture_voice;
+    u32 spdif_bits[3]; // SPDIF out setup
+
+    struct snd_dma_buffer dma_buffer;
+
+    struct emu10k1x_midi midi;
+};
+
+/* hardware definition */
+static struct snd_pcm_hardware snd_emu10k1x_playback_hw = {
+    .info =			(SNDRV_PCM_INFO_MMAP |
+                                 SNDRV_PCM_INFO_INTERLEAVED |
+                                 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+                                 SNDRV_PCM_INFO_MMAP_VALID),
+                                 .formats =		SNDRV_PCM_FMTBIT_S16_LE,
+                                 .rates =		SNDRV_PCM_RATE_48000,
+                                 .rate_min =		48000,
+                                 .rate_max =		48000,
+                                 .channels_min =		2,
+                                 .channels_max =		2,
+                                 .buffer_bytes_max =	(32*1024),
+                                 .period_bytes_min =	64,
+                                 .period_bytes_max =	(16*1024),
+                                 .periods_min =		2,
+                                 .periods_max =		8,
+                                 .fifo_size =		0,
+};
+
+static struct snd_pcm_hardware snd_emu10k1x_capture_hw = {
+    .info =			(SNDRV_PCM_INFO_MMAP |
+                                 SNDRV_PCM_INFO_INTERLEAVED |
+                                 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+                                 SNDRV_PCM_INFO_MMAP_VALID),
+                                 .formats =		SNDRV_PCM_FMTBIT_S16_LE,
+                                 .rates =		SNDRV_PCM_RATE_48000,
+                                 .rate_min =		48000,
+                                 .rate_max =		48000,
+                                 .channels_min =		2,
+                                 .channels_max =		2,
+                                 .buffer_bytes_max =	(32*1024),
+                                 .period_bytes_min =	64,
+                                 .period_bytes_max =	(16*1024),
+                                 .periods_min =		2,
+                                 .periods_max =		2,
+                                 .fifo_size =		0,
+};
+
+static unsigned int snd_emu10k1x_ptr_read(struct emu10k1x * emu,
+                                          unsigned int reg,
+                                          unsigned int chn)
+{
+    unsigned long flags;
+    unsigned int regptr, val;
+
+    regptr = (reg << 16) | chn;
+
+    spin_lock_irqsave(&emu->emu_lock, flags);
+    outl(regptr, emu->port + PTR);
+    val = inl(emu->port + DATA);
+    spin_unlock_irqrestore(&emu->emu_lock, flags);
+    return val;
+}
+
+static void snd_emu10k1x_ptr_write(struct emu10k1x *emu,
+                                   unsigned int reg,
+                                   unsigned int chn,
+                                   unsigned int data)
+{
+    unsigned int regptr;
+    unsigned long flags;
+
+    regptr = (reg << 16) | chn;
+
+    spin_lock_irqsave(&emu->emu_lock, flags);
+    outl(regptr, emu->port + PTR);
+    outl(data, emu->port + DATA);
+    spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+static void snd_emu10k1x_intr_enable(struct emu10k1x *emu, unsigned int intrenb)
+{
+    unsigned long flags;
+    unsigned int enable;
+
+    spin_lock_irqsave(&emu->emu_lock, flags);
+    enable = inl(emu->port + INTE) | intrenb;
+    outl(enable, emu->port + INTE);
+    spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+static void snd_emu10k1x_intr_disable(struct emu10k1x *emu, unsigned int intrenb)
+{
+    unsigned long flags;
+    unsigned int enable;
+
+    spin_lock_irqsave(&emu->emu_lock, flags);
+    enable = inl(emu->port + INTE) & ~intrenb;
+    outl(enable, emu->port + INTE);
+    spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+static void snd_emu10k1x_gpio_write(struct emu10k1x *emu, unsigned int value)
+{
+    unsigned long flags;
+
+    spin_lock_irqsave(&emu->emu_lock, flags);
+    outl(value, emu->port + GPIO);
+    spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+static void snd_emu10k1x_pcm_free_substream(struct snd_pcm_runtime *runtime)
+{
+    kfree(runtime->private_data);
+}
+
+static void snd_emu10k1x_pcm_interrupt(struct emu10k1x *emu, struct emu10k1x_voice *voice)
+{
+    struct emu10k1x_pcm *epcm;
+
+    if ((epcm = voice->epcm) == NULL)
+        return;
+    if (epcm->substream == NULL)
+        return;
+#if 0
+    snd_printk(KERN_INFO "IRQ: position = 0x%x, period = 0x%x, size = 0x%x\n",
+               epcm->substream->ops->pointer(epcm->substream),
+               snd_pcm_lib_period_bytes(epcm->substream),
+               snd_pcm_lib_buffer_bytes(epcm->substream));
+#endif
+    snd_pcm_period_elapsed(epcm->substream);
+}
+
+/* open callback */
+static int snd_emu10k1x_playback_open(struct snd_pcm_substream *substream)
+{
+    struct emu10k1x *chip = snd_pcm_substream_chip(substream);
+    struct emu10k1x_pcm *epcm;
+    struct snd_pcm_runtime *runtime = substream->runtime;
+    int err;
+
+    if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) {
+        return err;
+    }
+    if ((err = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 64)) < 0)
+        return err;
+
+    epcm = (struct emu10k1x_pcm *)kzalloc(sizeof(*epcm), GFP_KERNEL);
+    if (epcm == NULL)
+        return -ENOMEM;
+    epcm->emu = chip;
+    epcm->substream = substream;
+
+    runtime->private_data = epcm;
+    runtime->private_free = snd_emu10k1x_pcm_free_substream;
+
+    runtime->hw = snd_emu10k1x_playback_hw;
+
+    return 0;
+}
+
+/* close callback */
+static int snd_emu10k1x_playback_close(struct snd_pcm_substream *substream)
+{
+    return 0;
+}
+
+/* hw_params callback */
+static int snd_emu10k1x_pcm_hw_params(struct snd_pcm_substream *substream,
+                                      struct snd_pcm_hw_params *hw_params)
+{
+    struct snd_pcm_runtime *runtime = substream->runtime;
+    struct emu10k1x_pcm *epcm = runtime->private_data;
+
+    if (! epcm->voice) {
+        epcm->voice = &epcm->emu->voices[substream->pcm->device];
+        epcm->voice->use = 1;
+        epcm->voice->epcm = epcm;
+    }
+
+    return snd_pcm_lib_malloc_pages(substream,
+                                    params_buffer_bytes(hw_params));
+}
+
+/* hw_free callback */
+static int snd_emu10k1x_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+    struct snd_pcm_runtime *runtime = substream->runtime;
+    struct emu10k1x_pcm *epcm;
+
+    if (runtime->private_data == NULL)
+        return 0;
+
+    epcm = runtime->private_data;
+
+    if (epcm->voice) {
+        epcm->voice->use = 0;
+        epcm->voice->epcm = NULL;
+        epcm->voice = NULL;
+    }
+
+    return snd_pcm_lib_free_pages(substream);
+}
+
+/* prepare callback */
+static int snd_emu10k1x_pcm_prepare(struct snd_pcm_substream *substream)
+{
+    struct emu10k1x *emu = snd_pcm_substream_chip(substream);
+    struct snd_pcm_runtime *runtime = substream->runtime;
+    struct emu10k1x_pcm *epcm = runtime->private_data;
+    int voice = epcm->voice->number;
+    u32 *table_base = (u32 *)(emu->dma_buffer.area+1024*voice);
+    u32 period_size_bytes = frames_to_bytes(runtime, runtime->period_size);
+    int i;
+
+    for(i=0; i < runtime->periods; i++) {
+        *table_base++=runtime->dma_addr+(i*period_size_bytes);
+        *table_base++=period_size_bytes<<16;
+    }
+
+    snd_emu10k1x_ptr_write(emu, PLAYBACK_LIST_ADDR, voice, emu->dma_buffer.addr+1024*voice);
+    snd_emu10k1x_ptr_write(emu, PLAYBACK_LIST_SIZE, voice, (runtime->periods - 1) << 19);
+    snd_emu10k1x_ptr_write(emu, PLAYBACK_LIST_PTR, voice, 0);
+    snd_emu10k1x_ptr_write(emu, PLAYBACK_POINTER, voice, 0);
+    snd_emu10k1x_ptr_write(emu, PLAYBACK_UNKNOWN1, voice, 0);
+    snd_emu10k1x_ptr_write(emu, PLAYBACK_UNKNOWN2, voice, 0);
+    snd_emu10k1x_ptr_write(emu, PLAYBACK_DMA_ADDR, voice, runtime->dma_addr);
+
+    snd_emu10k1x_ptr_write(emu, PLAYBACK_PERIOD_SIZE, voice, frames_to_bytes(runtime, runtime->period_size)<<16);
+
+    return 0;
+}
+
+/* trigger callback */
+static int snd_emu10k1x_pcm_trigger(struct snd_pcm_substream *substream,
+                                    int cmd)
+{
+    struct emu10k1x *emu = snd_pcm_substream_chip(substream);
+    struct snd_pcm_runtime *runtime = substream->runtime;
+    struct emu10k1x_pcm *epcm = runtime->private_data;
+    int channel = epcm->voice->number;
+    int result = 0;
+
+    //	snd_printk(KERN_INFO "trigger - emu10k1x = 0x%x, cmd = %i, pointer = %d\n", (int)emu, cmd, (int)substream->ops->pointer(substream));
+
+    switch (cmd) {
+    case SNDRV_PCM_TRIGGER_START:
+        if(runtime->periods == 2)
+            snd_emu10k1x_intr_enable(emu, (INTE_CH_0_LOOP | INTE_CH_0_HALF_LOOP) << channel);
+        else
+            snd_emu10k1x_intr_enable(emu, INTE_CH_0_LOOP << channel);
+        epcm->running = 1;
+        snd_emu10k1x_ptr_write(emu, TRIGGER_CHANNEL, 0, snd_emu10k1x_ptr_read(emu, TRIGGER_CHANNEL, 0)|(TRIGGER_CHANNEL_0<<channel));
+        break;
+    case SNDRV_PCM_TRIGGER_STOP:
+        epcm->running = 0;
+        snd_emu10k1x_intr_disable(emu, (INTE_CH_0_LOOP | INTE_CH_0_HALF_LOOP) << channel);
+        snd_emu10k1x_ptr_write(emu, TRIGGER_CHANNEL, 0, snd_emu10k1x_ptr_read(emu, TRIGGER_CHANNEL, 0) & ~(TRIGGER_CHANNEL_0<<channel));
+        break;
+    default:
+        result = -EINVAL;
+        break;
+    }
+    return result;
+}
+
+/* pointer callback */
+static snd_pcm_uframes_t
+snd_emu10k1x_pcm_pointer(struct snd_pcm_substream *substream)
+{
+    struct emu10k1x *emu = snd_pcm_substream_chip(substream);
+    struct snd_pcm_runtime *runtime = substream->runtime;
+    struct emu10k1x_pcm *epcm = runtime->private_data;
+    int channel = epcm->voice->number;
+    snd_pcm_uframes_t ptr = 0, ptr1 = 0, ptr2= 0,ptr3 = 0,ptr4 = 0;
+
+    if (!epcm->running)
+        return 0;
+
+    ptr3 = snd_emu10k1x_ptr_read(emu, PLAYBACK_LIST_PTR, channel);
+    ptr1 = snd_emu10k1x_ptr_read(emu, PLAYBACK_POINTER, channel);
+    ptr4 = snd_emu10k1x_ptr_read(emu, PLAYBACK_LIST_PTR, channel);
+
+    if(ptr4 == 0 && ptr1 == frames_to_bytes(runtime, runtime->buffer_size))
+        return 0;
+
+    if (ptr3 != ptr4)
+        ptr1 = snd_emu10k1x_ptr_read(emu, PLAYBACK_POINTER, channel);
+    ptr2 = bytes_to_frames(runtime, ptr1);
+    ptr2 += (ptr4 >> 3) * runtime->period_size;
+    ptr = ptr2;
+
+    if (ptr >= runtime->buffer_size)
+        ptr -= runtime->buffer_size;
+
+    return ptr;
+}
+
+/* operators */
+static struct snd_pcm_ops snd_emu10k1x_playback_ops = {
+    .open =        snd_emu10k1x_playback_open,
+    .close =       snd_emu10k1x_playback_close,
+    .ioctl =       snd_pcm_lib_ioctl,
+    .hw_params =   snd_emu10k1x_pcm_hw_params,
+    .hw_free =     snd_emu10k1x_pcm_hw_free,
+    .prepare =     snd_emu10k1x_pcm_prepare,
+    .trigger =     snd_emu10k1x_pcm_trigger,
+    .pointer =     snd_emu10k1x_pcm_pointer,
+};
+
+/* open_capture callback */
+static int snd_emu10k1x_pcm_open_capture(struct snd_pcm_substream *substream)
+{
+    struct emu10k1x *chip = snd_pcm_substream_chip(substream);
+    struct emu10k1x_pcm *epcm;
+    struct snd_pcm_runtime *runtime = substream->runtime;
+    int err;
+
+    if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+        return err;
+    if ((err = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 64)) < 0)
+        return err;
+
+    epcm = (struct emu10k1x_pcm *)kzalloc(sizeof(*epcm), GFP_KERNEL);
+    if (epcm == NULL)
+        return -ENOMEM;
+
+    epcm->emu = chip;
+    epcm->substream = substream;
+
+    runtime->private_data = epcm;
+    runtime->private_free = snd_emu10k1x_pcm_free_substream;
+
+    runtime->hw = snd_emu10k1x_capture_hw;
+
+    return 0;
+}
+
+/* close callback */
+static int snd_emu10k1x_pcm_close_capture(struct snd_pcm_substream *substream)
+{
+    return 0;
+}
+
+/* hw_params callback */
+static int snd_emu10k1x_pcm_hw_params_capture(struct snd_pcm_substream *substream,
+                                              struct snd_pcm_hw_params *hw_params)
+{
+    struct snd_pcm_runtime *runtime = substream->runtime;
+    struct emu10k1x_pcm *epcm = runtime->private_data;
+
+    if (! epcm->voice) {
+        if (epcm->emu->capture_voice.use)
+            return -EBUSY;
+        epcm->voice = &epcm->emu->capture_voice;
+        epcm->voice->epcm = epcm;
+        epcm->voice->use = 1;
+    }
+
+    return snd_pcm_lib_malloc_pages(substream,
+                                    params_buffer_bytes(hw_params));
+}
+
+/* hw_free callback */
+static int snd_emu10k1x_pcm_hw_free_capture(struct snd_pcm_substream *substream)
+{
+    struct snd_pcm_runtime *runtime = substream->runtime;
+
+    struct emu10k1x_pcm *epcm;
+
+    if (runtime->private_data == NULL)
+        return 0;
+    epcm = runtime->private_data;
+
+    if (epcm->voice) {
+        epcm->voice->use = 0;
+        epcm->voice->epcm = NULL;
+        epcm->voice = NULL;
+    }
+
+    return snd_pcm_lib_free_pages(substream);
+}
+
+/* prepare capture callback */
+static int snd_emu10k1x_pcm_prepare_capture(struct snd_pcm_substream *substream)
+{
+    struct emu10k1x *emu = snd_pcm_substream_chip(substream);
+    struct snd_pcm_runtime *runtime = substream->runtime;
+
+    snd_emu10k1x_ptr_write(emu, CAPTURE_DMA_ADDR, 0, runtime->dma_addr);
+    snd_emu10k1x_ptr_write(emu, CAPTURE_BUFFER_SIZE, 0, frames_to_bytes(runtime, runtime->buffer_size)<<16); // buffer size in bytes
+    snd_emu10k1x_ptr_write(emu, CAPTURE_POINTER, 0, 0);
+    snd_emu10k1x_ptr_write(emu, CAPTURE_UNKNOWN, 0, 0);
+
+    return 0;
+}
+
+/* trigger_capture callback */
+static int snd_emu10k1x_pcm_trigger_capture(struct snd_pcm_substream *substream,
+                                            int cmd)
+{
+    struct emu10k1x *emu = snd_pcm_substream_chip(substream);
+    struct snd_pcm_runtime *runtime = substream->runtime;
+    struct emu10k1x_pcm *epcm = runtime->private_data;
+    int result = 0;
+
+    switch (cmd) {
+    case SNDRV_PCM_TRIGGER_START:
+        snd_emu10k1x_intr_enable(emu, INTE_CAP_0_LOOP |
+                                 INTE_CAP_0_HALF_LOOP);
+        snd_emu10k1x_ptr_write(emu, TRIGGER_CHANNEL, 0, snd_emu10k1x_ptr_read(emu, TRIGGER_CHANNEL, 0)|TRIGGER_CAPTURE);
+        epcm->running = 1;
+        break;
+    case SNDRV_PCM_TRIGGER_STOP:
+        epcm->running = 0;
+        snd_emu10k1x_intr_disable(emu, INTE_CAP_0_LOOP |
+                                  INTE_CAP_0_HALF_LOOP);
+        snd_emu10k1x_ptr_write(emu, TRIGGER_CHANNEL, 0, snd_emu10k1x_ptr_read(emu, TRIGGER_CHANNEL, 0) & ~(TRIGGER_CAPTURE));
+        break;
+    default:
+        result = -EINVAL;
+        break;
+    }
+    return result;
+}
+
+/* pointer_capture callback */
+static snd_pcm_uframes_t
+snd_emu10k1x_pcm_pointer_capture(struct snd_pcm_substream *substream)
+{
+    struct emu10k1x *emu = snd_pcm_substream_chip(substream);
+    struct snd_pcm_runtime *runtime = substream->runtime;
+    struct emu10k1x_pcm *epcm = runtime->private_data;
+    snd_pcm_uframes_t ptr;
+
+    if (!epcm->running)
+        return 0;
+
+    ptr = bytes_to_frames(runtime, snd_emu10k1x_ptr_read(emu, CAPTURE_POINTER, 0));
+    if (ptr >= runtime->buffer_size)
+        ptr -= runtime->buffer_size;
+
+    return ptr;
+}
+
+static struct snd_pcm_ops snd_emu10k1x_capture_ops = {
+    .open =        snd_emu10k1x_pcm_open_capture,
+    .close =       snd_emu10k1x_pcm_close_capture,
+    .ioctl =       snd_pcm_lib_ioctl,
+    .hw_params =   snd_emu10k1x_pcm_hw_params_capture,
+    .hw_free =     snd_emu10k1x_pcm_hw_free_capture,
+    .prepare =     snd_emu10k1x_pcm_prepare_capture,
+    .trigger =     snd_emu10k1x_pcm_trigger_capture,
+    .pointer =     snd_emu10k1x_pcm_pointer_capture,
+};
+
+static unsigned short snd_emu10k1x_ac97_read(struct snd_ac97 *ac97,
+                                             unsigned short reg)
+{
+    struct emu10k1x *emu = ac97->private_data;
+    unsigned long flags;
+    unsigned short val;
+
+    spin_lock_irqsave(&emu->emu_lock, flags);
+    outb(reg, emu->port + AC97ADDRESS);
+    val = inw(emu->port + AC97DATA);
+    spin_unlock_irqrestore(&emu->emu_lock, flags);
+    return val;
+}
+
+static void snd_emu10k1x_ac97_write(struct snd_ac97 *ac97,
+                                    unsigned short reg, unsigned short val)
+{
+    struct emu10k1x *emu = ac97->private_data;
+    unsigned long flags;
+
+    spin_lock_irqsave(&emu->emu_lock, flags);
+    outb(reg, emu->port + AC97ADDRESS);
+    outw(val, emu->port + AC97DATA);
+    spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+static int snd_emu10k1x_ac97(struct emu10k1x *chip)
+{
+    struct snd_ac97_bus *pbus;
+    struct snd_ac97_template ac97;
+    int err;
+    static struct snd_ac97_bus_ops ops = {
+        .write = snd_emu10k1x_ac97_write,
+        .read = snd_emu10k1x_ac97_read,
+    };
+
+    if ((err = snd_ac97_bus(chip->card, 0, &ops, NULL, &pbus)) < 0)
+        return err;
+    pbus->no_vra = 1; /* we don't need VRA */
+
+    memset(&ac97, 0, sizeof(ac97));
+    ac97.private_data = chip;
+    ac97.scaps = AC97_SCAP_NO_SPDIF;
+    return snd_ac97_mixer(pbus, &ac97, &chip->ac97);
+}
+
+static int snd_emu10k1x_free(struct emu10k1x *chip)
+{
+    snd_emu10k1x_ptr_write(chip, TRIGGER_CHANNEL, 0, 0);
+    // disable interrupts
+    outl(0, chip->port + INTE);
+    // disable audio
+    outl(HCFG_LOCKSOUNDCACHE, chip->port + HCFG);
+
+    // release the i/o port
+    release_and_free_resource(chip->res_port);
+
+    // release the irq
+    if (chip->irq >= 0)
+        free_irq(chip->irq, (void *)chip);
+
+    // release the DMA
+    if (chip->dma_buffer.area) {
+        snd_dma_free_pages(&chip->dma_buffer);
+    }
+
+    pci_disable_device(chip->pci);
+
+    // release the data
+    kfree(chip);
+    return 0;
+}
+
+static int snd_emu10k1x_dev_free(struct snd_device *device)
+{
+    struct emu10k1x *chip = device->device_data;
+    return snd_emu10k1x_free(chip);
+}
+
+static irqreturn_t snd_emu10k1x_interrupt(int irq, void *dev_id)
+{
+    unsigned int status;
+
+    struct emu10k1x *chip = dev_id;
+    struct emu10k1x_voice *pvoice = chip->voices;
+    int i;
+    int mask;
+
+    status = inl(chip->port + IPR);
+
+    if (! status)
+        return IRQ_NONE;
+
+    // capture interrupt
+    if (status & (IPR_CAP_0_LOOP | IPR_CAP_0_HALF_LOOP)) {
+        struct emu10k1x_voice *pvoice = &chip->capture_voice;
+        if (pvoice->use)
+            snd_emu10k1x_pcm_interrupt(chip, pvoice);
+        else
+            snd_emu10k1x_intr_disable(chip,
+                                      INTE_CAP_0_LOOP |
+                                      INTE_CAP_0_HALF_LOOP);
+    }
+
+    mask = IPR_CH_0_LOOP|IPR_CH_0_HALF_LOOP;
+    for (i = 0; i < 3; i++) {
+        if (status & mask) {
+            if (pvoice->use)
+                snd_emu10k1x_pcm_interrupt(chip, pvoice);
+            else
+                snd_emu10k1x_intr_disable(chip, mask);
+        }
+        pvoice++;
+        mask <<= 1;
+    }
+
+    if (status & (IPR_MIDITRANSBUFEMPTY|IPR_MIDIRECVBUFEMPTY)) {
+        if (chip->midi.interrupt)
+            chip->midi.interrupt(chip, status);
+        else
+            snd_emu10k1x_intr_disable(chip, INTE_MIDITXENABLE|INTE_MIDIRXENABLE);
+    }
+
+    // acknowledge the interrupt if necessary
+    outl(status, chip->port + IPR);
+
+    // snd_printk(KERN_INFO "interrupt %08x\n", status);
+    return IRQ_HANDLED;
+}
+
+static int __devinit snd_emu10k1x_pcm(struct emu10k1x *emu, int device, struct snd_pcm **rpcm)
+{
+    struct snd_pcm *pcm;
+    int err;
+    int capture = 0;
+
+    if (rpcm)
+        *rpcm = NULL;
+    if (device == 0)
+        capture = 1;
+
+    if ((err = snd_pcm_new(emu->card, "emu10k1x", device, 1, capture, &pcm)) < 0)
+        return err;
+
+    pcm->private_data = emu;
+
+    switch(device) {
+    case 0:
+        snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_emu10k1x_playback_ops);
+        snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_emu10k1x_capture_ops);
+        break;
+    case 1:
+    case 2:
+        snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_emu10k1x_playback_ops);
+        break;
+    }
+
+    pcm->info_flags = 0;
+    pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
+    switch(device) {
+    case 0:
+        strcpy(pcm->name, "EMU10K1X Front");
+        break;
+    case 1:
+        strcpy(pcm->name, "EMU10K1X Rear");
+        break;
+    case 2:
+        strcpy(pcm->name, "EMU10K1X Center/LFE");
+        break;
+    }
+    emu->pcm = pcm;
+
+    snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+                                          snd_dma_pci_data(emu->pci),
+                                          32*1024, 32*1024);
+
+    if (rpcm)
+        *rpcm = pcm;
+
+    return 0;
+}
+
+static int __devinit snd_emu10k1x_create(struct snd_card *card,
+                                         struct pci_dev *pci,
+                                         struct emu10k1x **rchip)
+{
+    struct emu10k1x *chip;
+    int err;
+    int ch;
+    static struct snd_device_ops ops = {
+        .dev_free = snd_emu10k1x_dev_free,
+    };
+
+    *rchip = NULL;
+
+    if ((err = pci_enable_device(pci)) < 0)
+        return err;
+    pci_set_dma_mask(pci, 0x0fffffff);
+
+    chip = (struct emu10k1x *)kzalloc(sizeof(*chip), GFP_KERNEL);
+    if (chip == NULL) {
+        pci_disable_device(pci);
+        return -ENOMEM;
+    }
+
+    chip->card = card;
+    chip->pci = pci;
+    chip->irq = -1;
+
+    spin_lock_init(&chip->emu_lock);
+    spin_lock_init(&chip->voice_lock);
+
+    chip->port = pci_resource_start(pci, 0);
+    if ((chip->res_port = request_region(chip->port, 8,
+                                         "EMU10K1X")) == NULL) {
+        snd_printk(KERN_ERR "emu10k1x: cannot allocate the port 0x%lx\n", chip->port);
+        snd_emu10k1x_free(chip);
+        return -EBUSY;
+    }
+
+    if (request_irq(pci->irq, snd_emu10k1x_interrupt,
+                    SA_INTERRUPT|SA_SHIRQ, "EMU10K1X",
+                    (void *)chip)) {
+        snd_printk(KERN_ERR "emu10k1x: cannot grab irq %d\n", pci->irq);
+        snd_emu10k1x_free(chip);
+        return -EBUSY;
+    }
+    chip->irq = pci->irq;
+
+    if(snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
+                           4 * 1024, &chip->dma_buffer) < 0) {
+        snd_emu10k1x_free(chip);
+        return -ENOMEM;
+    }
+
+    pci_set_master(pci);
+    /* read revision & serial */
+    pci_read_config_byte(pci, PCI_REVISION_ID, (char *)&chip->revision);
+    pci_read_config_dword(pci, PCI_SUBSYSTEM_VENDOR_ID, &chip->serial);
+    pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &chip->model);
+    snd_printk(KERN_INFO "Model %04x Rev %08x Serial %08x\n", chip->model,
+               chip->revision, chip->serial);
+
+    outl(0, chip->port + INTE);
+
+    for(ch = 0; ch < 3; ch++) {
+        chip->voices[ch].emu = chip;
+        chip->voices[ch].number = ch;
+    }
+
+    /*
+     *  Init to 0x02109204 :
+     *  Clock accuracy    = 0     (1000ppm)
+     *  Sample Rate       = 2     (48kHz)
+     *  Audio Channel     = 1     (Left of 2)
+     *  Source Number     = 0     (Unspecified)
+     *  Generation Status = 1     (Original for Cat Code 12)
+     *  Cat Code          = 12    (Digital Signal Mixer)
+     *  Mode              = 0     (Mode 0)
+     *  Emphasis          = 0     (None)
+     *  CP                = 1     (Copyright unasserted)
+     *  AN                = 0     (Audio data)
+     *  P                 = 0     (Consumer)
+     */
+    snd_emu10k1x_ptr_write(chip, SPCS0, 0,
+                           chip->spdif_bits[0] =
+                           SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
+                           SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
+                           SPCS_GENERATIONSTATUS | 0x00001200 |
+                           0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT);
+    snd_emu10k1x_ptr_write(chip, SPCS1, 0,
+                           chip->spdif_bits[1] =
+                           SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
+                           SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
+                           SPCS_GENERATIONSTATUS | 0x00001200 |
+                           0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT);
+    snd_emu10k1x_ptr_write(chip, SPCS2, 0,
+                           chip->spdif_bits[2] =
+                           SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
+                           SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
+                           SPCS_GENERATIONSTATUS | 0x00001200 |
+                           0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT);
+
+    snd_emu10k1x_ptr_write(chip, SPDIF_SELECT, 0, 0x700); // disable SPDIF
+    snd_emu10k1x_ptr_write(chip, ROUTING, 0, 0x1003F); // routing
+    snd_emu10k1x_gpio_write(chip, 0x1080); // analog mode
+
+    outl(HCFG_LOCKSOUNDCACHE|HCFG_AUDIOENABLE, chip->port+HCFG);
+
+    if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL,
+                              chip, &ops)) < 0) {
+        snd_emu10k1x_free(chip);
+        return err;
+    }
+    *rchip = chip;
+    return 0;
+}
+
+static void snd_emu10k1x_proc_reg_read(struct snd_info_entry *entry,
+                                       struct snd_info_buffer *buffer)
+{
+    struct emu10k1x *emu = entry->private_data;
+    unsigned long value,value1,value2;
+    unsigned long flags;
+    int i;
+
+    snd_iprintf(buffer, "Registers:\n\n");
+    for(i = 0; i < 0x20; i+=4) {
+        spin_lock_irqsave(&emu->emu_lock, flags);
+        value = inl(emu->port + i);
+        spin_unlock_irqrestore(&emu->emu_lock, flags);
+        snd_iprintf(buffer, "Register %02X: %08lX\n", i, value);
+    }
+    snd_iprintf(buffer, "\nRegisters\n\n");
+    for(i = 0; i <= 0x48; i++) {
+        value = snd_emu10k1x_ptr_read(emu, i, 0);
+        if(i < 0x10 || (i >= 0x20 && i < 0x40)) {
+            value1 = snd_emu10k1x_ptr_read(emu, i, 1);
+            value2 = snd_emu10k1x_ptr_read(emu, i, 2);
+            snd_iprintf(buffer, "%02X: %08lX %08lX %08lX\n", i, value, value1, value2);
+        } else {
+            snd_iprintf(buffer, "%02X: %08lX\n", i, value);
+        }
+    }
+}
+
+static void snd_emu10k1x_proc_reg_write(struct snd_info_entry *entry,
+                                        struct snd_info_buffer *buffer)
+{
+    struct emu10k1x *emu = entry->private_data;
+    char line[64];
+    unsigned int reg, channel_id , val;
+#if 0
+    while (!snd_info_get_line(buffer, line, sizeof(line))) {
+        if (sscanf(line, "%x %x %x", &reg, &channel_id, &val) != 3)
+            continue;
+
+        if ((reg < 0x49) && (reg >=0) && (val <= 0xffffffff)
+            && (channel_id >=0) && (channel_id <= 2) )
+            snd_emu10k1x_ptr_write(emu, reg, channel_id, val);
+    }
+#endif
+}
+
+static int __devinit snd_emu10k1x_proc_init(struct emu10k1x * emu)
+{
+    struct snd_info_entry *entry;
+
+    if(! snd_card_proc_new(emu->card, "emu10k1x_regs", &entry)) {
+        snd_info_set_text_ops(entry, emu, 1024, snd_emu10k1x_proc_reg_read);
+        entry->c.text.write_size = 64;
+        entry->c.text.write = snd_emu10k1x_proc_reg_write;
+        entry->mode |= S_IWUSR;
+        entry->private_data = emu;
+    }
+
+    return 0;
+}
+
+static int snd_emu10k1x_shared_spdif_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *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_emu10k1x_shared_spdif_get(struct snd_kcontrol *kcontrol,
+                                         struct snd_ctl_elem_value *ucontrol)
+{
+    struct emu10k1x *emu = snd_kcontrol_chip(kcontrol);
+
+    ucontrol->value.integer.value[0] = (snd_emu10k1x_ptr_read(emu, SPDIF_SELECT, 0) == 0x700) ? 0 : 1;
+
+    return 0;
+}
+
+static int snd_emu10k1x_shared_spdif_put(struct snd_kcontrol *kcontrol,
+                                         struct snd_ctl_elem_value *ucontrol)
+{
+    struct emu10k1x *emu = snd_kcontrol_chip(kcontrol);
+    unsigned int val;
+    int change = 0;
+
+    val = ucontrol->value.integer.value[0] ;
+
+    if (val) {
+        // enable spdif output
+        snd_emu10k1x_ptr_write(emu, SPDIF_SELECT, 0, 0x000);
+        snd_emu10k1x_ptr_write(emu, ROUTING, 0, 0x700);
+        snd_emu10k1x_gpio_write(emu, 0x1000);
+    } else {
+        // disable spdif output
+        snd_emu10k1x_ptr_write(emu, SPDIF_SELECT, 0, 0x700);
+        snd_emu10k1x_ptr_write(emu, ROUTING, 0, 0x1003F);
+        snd_emu10k1x_gpio_write(emu, 0x1080);
+    }
+    return change;
+}
+
+static struct snd_kcontrol_new snd_emu10k1x_shared_spdif __devinitdata =
+{
+    .iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
+    .name =		"Analog/Digital Output Jack",
+    .info =		snd_emu10k1x_shared_spdif_info,
+    .get =		snd_emu10k1x_shared_spdif_get,
+    .put =		snd_emu10k1x_shared_spdif_put
+};
+
+static int snd_emu10k1x_spdif_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+    uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+    uinfo->count = 1;
+    return 0;
+}
+
+static int snd_emu10k1x_spdif_get(struct snd_kcontrol *kcontrol,
+                                  struct snd_ctl_elem_value *ucontrol)
+{
+    struct emu10k1x *emu = snd_kcontrol_chip(kcontrol);
+    unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+
+    ucontrol->value.iec958.status[0] = (emu->spdif_bits[idx] >> 0) & 0xff;
+    ucontrol->value.iec958.status[1] = (emu->spdif_bits[idx] >> 8) & 0xff;
+    ucontrol->value.iec958.status[2] = (emu->spdif_bits[idx] >> 16) & 0xff;
+    ucontrol->value.iec958.status[3] = (emu->spdif_bits[idx] >> 24) & 0xff;
+    return 0;
+}
+
+static int snd_emu10k1x_spdif_get_mask(struct snd_kcontrol *kcontrol,
+                                       struct snd_ctl_elem_value *ucontrol)
+{
+    ucontrol->value.iec958.status[0] = 0xff;
+    ucontrol->value.iec958.status[1] = 0xff;
+    ucontrol->value.iec958.status[2] = 0xff;
+    ucontrol->value.iec958.status[3] = 0xff;
+    return 0;
+}
+
+static int snd_emu10k1x_spdif_put(struct snd_kcontrol *kcontrol,
+                                  struct snd_ctl_elem_value *ucontrol)
+{
+    struct emu10k1x *emu = snd_kcontrol_chip(kcontrol);
+    unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+    int change;
+    unsigned int val;
+
+    val = (ucontrol->value.iec958.status[0] << 0) |
+        (ucontrol->value.iec958.status[1] << 8) |
+        (ucontrol->value.iec958.status[2] << 16) |
+        (ucontrol->value.iec958.status[3] << 24);
+    change = val != emu->spdif_bits[idx];
+    if (change) {
+        snd_emu10k1x_ptr_write(emu, SPCS0 + idx, 0, val);
+        emu->spdif_bits[idx] = val;
+    }
+    return change;
+}
+
+static struct snd_kcontrol_new snd_emu10k1x_spdif_mask_control =
+{
+    .access =	SNDRV_CTL_ELEM_ACCESS_READ,
+    .iface =        SNDRV_CTL_ELEM_IFACE_PCM,
+    .name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK),
+    .count =	3,
+    .info =         snd_emu10k1x_spdif_info,
+    .get =          snd_emu10k1x_spdif_get_mask
+};
+
+static struct snd_kcontrol_new snd_emu10k1x_spdif_control =
+{
+    .iface =	SNDRV_CTL_ELEM_IFACE_PCM,
+    .name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
+    .count =	3,
+    .info =         snd_emu10k1x_spdif_info,
+    .get =          snd_emu10k1x_spdif_get,
+    .put =          snd_emu10k1x_spdif_put
+};
+
+static int __devinit snd_emu10k1x_mixer(struct emu10k1x *emu)
+{
+    int err;
+    struct snd_kcontrol *kctl;
+    struct snd_card *card = emu->card;
+
+    if ((kctl = snd_ctl_new1(&snd_emu10k1x_spdif_mask_control, emu)) == NULL)
+        return -ENOMEM;
+    if ((err = snd_ctl_add(card, kctl)))
+        return err;
+    if ((kctl = snd_ctl_new1(&snd_emu10k1x_shared_spdif, emu)) == NULL)
+        return -ENOMEM;
+    if ((err = snd_ctl_add(card, kctl)))
+        return err;
+    if ((kctl = snd_ctl_new1(&snd_emu10k1x_spdif_control, emu)) == NULL)
+        return -ENOMEM;
+    if ((err = snd_ctl_add(card, kctl)))
+        return err;
+
+    return 0;
+}
+
+#define EMU10K1X_MIDI_MODE_INPUT	(1<<0)
+#define EMU10K1X_MIDI_MODE_OUTPUT	(1<<1)
+
+static inline unsigned char mpu401_read(struct emu10k1x *emu, struct emu10k1x_midi *mpu, int idx)
+{
+    return (unsigned char)snd_emu10k1x_ptr_read(emu, mpu->port + idx, 0);
+}
+
+static inline void mpu401_write(struct emu10k1x *emu, struct emu10k1x_midi *mpu, int data, int idx)
+{
+    snd_emu10k1x_ptr_write(emu, mpu->port + idx, 0, data);
+}
+
+#define mpu401_write_data(emu, mpu, data)	mpu401_write(emu, mpu, data, 0)
+#define mpu401_write_cmd(emu, mpu, data)	mpu401_write(emu, mpu, data, 1)
+#define mpu401_read_data(emu, mpu)		mpu401_read(emu, mpu, 0)
+#define mpu401_read_stat(emu, mpu)		mpu401_read(emu, mpu, 1)
+
+#define mpu401_input_avail(emu,mpu)	(!(mpu401_read_stat(emu,mpu) & 0x80))
+#define mpu401_output_ready(emu,mpu)	(!(mpu401_read_stat(emu,mpu) & 0x40))
+
+#define MPU401_RESET		0xff
+#define MPU401_ENTER_UART	0x3f
+#define MPU401_ACK		0xfe
+
+static void mpu401_clear_rx(struct emu10k1x *emu, struct emu10k1x_midi *mpu)
+{
+    int timeout = 100000;
+    for (; timeout > 0 && mpu401_input_avail(emu, mpu); timeout--)
+        mpu401_read_data(emu, mpu);
+#ifdef CONFIG_SND_DEBUG
+    if (timeout <= 0)
+        snd_printk(KERN_ERR "cmd: clear rx timeout (status = 0x%x)\n", mpu401_read_stat(emu, mpu));
+#endif
+}
+
+/*
+
+*/
+
+static void do_emu10k1x_midi_interrupt(struct emu10k1x *emu,
+                                       struct emu10k1x_midi *midi, unsigned int status)
+{
+    unsigned char byte;
+
+    if (midi->rmidi == NULL) {
+        snd_emu10k1x_intr_disable(emu, midi->tx_enable | midi->rx_enable);
+        return;
+    }
+
+    spin_lock(&midi->input_lock);
+    if ((status & midi->ipr_rx) && mpu401_input_avail(emu, midi)) {
+        if (!(midi->midi_mode & EMU10K1X_MIDI_MODE_INPUT)) {
+            mpu401_clear_rx(emu, midi);
+        } else {
+            byte = mpu401_read_data(emu, midi);
+            if (midi->substream_input)
+                snd_rawmidi_receive(midi->substream_input, &byte, 1);
+        }
+    }
+    spin_unlock(&midi->input_lock);
+
+    spin_lock(&midi->output_lock);
+    if ((status & midi->ipr_tx) && mpu401_output_ready(emu, midi)) {
+        if (midi->substream_output &&
+            snd_rawmidi_transmit(midi->substream_output, &byte, 1) == 1) {
+            mpu401_write_data(emu, midi, byte);
+        } else {
+            snd_emu10k1x_intr_disable(emu, midi->tx_enable);
+        }
+    }
+    spin_unlock(&midi->output_lock);
+}
+
+static void snd_emu10k1x_midi_interrupt(struct emu10k1x *emu, unsigned int status)
+{
+    do_emu10k1x_midi_interrupt(emu, &emu->midi, status);
+}
+
+static int snd_emu10k1x_midi_cmd(struct emu10k1x * emu,
+                                 struct emu10k1x_midi *midi, unsigned char cmd, int ack)
+{
+    unsigned long flags;
+    int timeout, ok;
+
+    spin_lock_irqsave(&midi->input_lock, flags);
+    mpu401_write_data(emu, midi, 0x00);
+    /* mpu401_clear_rx(emu, midi); */
+
+    mpu401_write_cmd(emu, midi, cmd);
+    if (ack) {
+        ok = 0;
+        timeout = 10000;
+        while (!ok && timeout-- > 0) {
+            if (mpu401_input_avail(emu, midi)) {
+                if (mpu401_read_data(emu, midi) == MPU401_ACK)
+                    ok = 1;
+            }
+        }
+        if (!ok && mpu401_read_data(emu, midi) == MPU401_ACK)
+            ok = 1;
+    } else {
+        ok = 1;
+    }
+    spin_unlock_irqrestore(&midi->input_lock, flags);
+    if (!ok) {
+        snd_printk(KERN_ERR "midi_cmd: 0x%x failed at 0x%lx (status = 0x%x, data = 0x%x)!!!\n",
+                   cmd, emu->port,
+                   mpu401_read_stat(emu, midi),
+                   mpu401_read_data(emu, midi));
+        return 1;
+    }
+    return 0;
+}
+
+static int snd_emu10k1x_midi_input_open(struct snd_rawmidi_substream *substream)
+{
+    struct emu10k1x *emu;
+    struct emu10k1x_midi *midi = substream->rmidi->private_data;
+    unsigned long flags;
+
+    emu = midi->emu;
+    snd_assert(emu, return -ENXIO);
+    spin_lock_irqsave(&midi->open_lock, flags);
+    midi->midi_mode |= EMU10K1X_MIDI_MODE_INPUT;
+    midi->substream_input = substream;
+    if (!(midi->midi_mode & EMU10K1X_MIDI_MODE_OUTPUT)) {
+        spin_unlock_irqrestore(&midi->open_lock, flags);
+        if (snd_emu10k1x_midi_cmd(emu, midi, MPU401_RESET, 1))
+            goto error_out;
+        if (snd_emu10k1x_midi_cmd(emu, midi, MPU401_ENTER_UART, 1))
+            goto error_out;
+    } else {
+        spin_unlock_irqrestore(&midi->open_lock, flags);
+    }
+    return 0;
+
+    error_out:
+        return -EIO;
+}
+
+static int snd_emu10k1x_midi_output_open(struct snd_rawmidi_substream *substream)
+{
+    struct emu10k1x *emu;
+    struct emu10k1x_midi *midi = substream->rmidi->private_data;
+    unsigned long flags;
+
+    emu = midi->emu;
+    snd_assert(emu, return -ENXIO);
+    spin_lock_irqsave(&midi->open_lock, flags);
+    midi->midi_mode |= EMU10K1X_MIDI_MODE_OUTPUT;
+    midi->substream_output = substream;
+    if (!(midi->midi_mode & EMU10K1X_MIDI_MODE_INPUT)) {
+        spin_unlock_irqrestore(&midi->open_lock, flags);
+        if (snd_emu10k1x_midi_cmd(emu, midi, MPU401_RESET, 1))
+            goto error_out;
+        if (snd_emu10k1x_midi_cmd(emu, midi, MPU401_ENTER_UART, 1))
+            goto error_out;
+    } else {
+        spin_unlock_irqrestore(&midi->open_lock, flags);
+    }
+    return 0;
+
+    error_out:
+        return -EIO;
+}
+
+static int snd_emu10k1x_midi_input_close(struct snd_rawmidi_substream *substream)
+{
+    struct emu10k1x *emu;
+    struct emu10k1x_midi *midi = substream->rmidi->private_data;
+    unsigned long flags;
+    int err = 0;
+
+    emu = midi->emu;
+    snd_assert(emu, return -ENXIO);
+    spin_lock_irqsave(&midi->open_lock, flags);
+    snd_emu10k1x_intr_disable(emu, midi->rx_enable);
+    midi->midi_mode &= ~EMU10K1X_MIDI_MODE_INPUT;
+    midi->substream_input = NULL;
+    if (!(midi->midi_mode & EMU10K1X_MIDI_MODE_OUTPUT)) {
+        spin_unlock_irqrestore(&midi->open_lock, flags);
+        err = snd_emu10k1x_midi_cmd(emu, midi, MPU401_RESET, 0);
+    } else {
+        spin_unlock_irqrestore(&midi->open_lock, flags);
+    }
+    return err;
+}
+
+static int snd_emu10k1x_midi_output_close(struct snd_rawmidi_substream *substream)
+{
+    struct emu10k1x *emu;
+    struct emu10k1x_midi *midi = substream->rmidi->private_data;
+    unsigned long flags;
+    int err = 0;
+
+    emu = midi->emu;
+    snd_assert(emu, return -ENXIO);
+    spin_lock_irqsave(&midi->open_lock, flags);
+    snd_emu10k1x_intr_disable(emu, midi->tx_enable);
+    midi->midi_mode &= ~EMU10K1X_MIDI_MODE_OUTPUT;
+    midi->substream_output = NULL;
+    if (!(midi->midi_mode & EMU10K1X_MIDI_MODE_INPUT)) {
+        spin_unlock_irqrestore(&midi->open_lock, flags);
+        err = snd_emu10k1x_midi_cmd(emu, midi, MPU401_RESET, 0);
+    } else {
+        spin_unlock_irqrestore(&midi->open_lock, flags);
+    }
+    return err;
+}
+
+static void snd_emu10k1x_midi_input_trigger(struct snd_rawmidi_substream *substream, int up)
+{
+    struct emu10k1x *emu;
+    struct emu10k1x_midi *midi = substream->rmidi->private_data;
+    emu = midi->emu;
+    snd_assert(emu, return);
+
+    if (up)
+        snd_emu10k1x_intr_enable(emu, midi->rx_enable);
+    else
+        snd_emu10k1x_intr_disable(emu, midi->rx_enable);
+}
+
+static void snd_emu10k1x_midi_output_trigger(struct snd_rawmidi_substream *substream, int up)
+{
+    struct emu10k1x *emu;
+    struct emu10k1x_midi *midi = substream->rmidi->private_data;
+    unsigned long flags;
+
+    emu = midi->emu;
+    snd_assert(emu, return);
+
+    if (up) {
+        int max = 4;
+        unsigned char byte;
+
+        /* try to send some amount of bytes here before interrupts */
+        spin_lock_irqsave(&midi->output_lock, flags);
+        while (max > 0) {
+            if (mpu401_output_ready(emu, midi)) {
+                if (!(midi->midi_mode & EMU10K1X_MIDI_MODE_OUTPUT) ||
+                    snd_rawmidi_transmit(substream, &byte, 1) != 1) {
+                    /* no more data */
+                    spin_unlock_irqrestore(&midi->output_lock, flags);
+                    return;
+                }
+                mpu401_write_data(emu, midi, byte);
+                max--;
+            } else {
+                break;
+            }
+        }
+        spin_unlock_irqrestore(&midi->output_lock, flags);
+        snd_emu10k1x_intr_enable(emu, midi->tx_enable);
+    } else {
+        snd_emu10k1x_intr_disable(emu, midi->tx_enable);
+    }
+}
+
+/*
+
+*/
+
+static struct snd_rawmidi_ops snd_emu10k1x_midi_output =
+{
+    .open =		snd_emu10k1x_midi_output_open,
+    .close =	snd_emu10k1x_midi_output_close,
+    .trigger =	snd_emu10k1x_midi_output_trigger,
+};
+
+static struct snd_rawmidi_ops snd_emu10k1x_midi_input =
+{
+    .open =		snd_emu10k1x_midi_input_open,
+    .close =	snd_emu10k1x_midi_input_close,
+    .trigger =	snd_emu10k1x_midi_input_trigger,
+};
+
+static void snd_emu10k1x_midi_free(struct snd_rawmidi *rmidi)
+{
+    struct emu10k1x_midi *midi = rmidi->private_data;
+    midi->interrupt = NULL;
+    midi->rmidi = NULL;
+}
+
+static int __devinit emu10k1x_midi_init(struct emu10k1x *emu,
+                                        struct emu10k1x_midi *midi, int device, char *name)
+{
+    struct snd_rawmidi *rmidi;
+    int err;
+
+    if ((err = snd_rawmidi_new(emu->card, name, device, 1, 1, &rmidi)) < 0)
+        return err;
+    midi->emu = emu;
+    spin_lock_init(&midi->open_lock);
+    spin_lock_init(&midi->input_lock);
+    spin_lock_init(&midi->output_lock);
+    strcpy(rmidi->name, name);
+    snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_emu10k1x_midi_output);
+    snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_emu10k1x_midi_input);
+    rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT |
+        SNDRV_RAWMIDI_INFO_INPUT |
+        SNDRV_RAWMIDI_INFO_DUPLEX;
+    rmidi->private_data = midi;
+    rmidi->private_free = snd_emu10k1x_midi_free;
+    midi->rmidi = rmidi;
+    return 0;
+}
+
+static int __devinit snd_emu10k1x_midi(struct emu10k1x *emu)
+{
+    struct emu10k1x_midi *midi = &emu->midi;
+    int err;
+
+    if ((err = emu10k1x_midi_init(emu, midi, 0, "EMU10K1X MPU-401 (UART)")) < 0)
+        return err;
+
+    midi->tx_enable = INTE_MIDITXENABLE;
+    midi->rx_enable = INTE_MIDIRXENABLE;
+    midi->port = MUDATA;
+    midi->ipr_tx = IPR_MIDITRANSBUFEMPTY;
+    midi->ipr_rx = IPR_MIDIRECVBUFEMPTY;
+    midi->interrupt = snd_emu10k1x_midi_interrupt;
+    return 0;
+}
+
+static int __devinit snd_emu10k1x_probe(struct pci_dev *pci,
+                                        const struct pci_device_id *pci_id)
+{
+    static int dev;
+    struct snd_card *card;
+    struct emu10k1x *chip;
+    int err;
+
+    if (dev >= SNDRV_CARDS)
+        return -ENODEV;
+    if (!enable[dev]) {
+        dev++;
+        return -ENOENT;
+    }
+
+    card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+    if (card == NULL)
+        return -ENOMEM;
+
+    if ((err = snd_emu10k1x_create(card, pci, &chip)) < 0) {
+        snd_card_free(card);
+        return err;
+    }
+
+    if ((err = snd_emu10k1x_pcm(chip, 0, NULL)) < 0) {
+        snd_card_free(card);
+        return err;
+    }
+    if ((err = snd_emu10k1x_pcm(chip, 1, NULL)) < 0) {
+        snd_card_free(card);
+        return err;
+    }
+    if ((err = snd_emu10k1x_pcm(chip, 2, NULL)) < 0) {
+        snd_card_free(card);
+        return err;
+    }
+
+    if ((err = snd_emu10k1x_ac97(chip)) < 0) {
+        snd_card_free(card);
+        return err;
+    }
+
+    if ((err = snd_emu10k1x_mixer(chip)) < 0) {
+        snd_card_free(card);
+        return err;
+    }
+
+    if ((err = snd_emu10k1x_midi(chip)) < 0) {
+        snd_card_free(card);
+        return err;
+    }
+
+    snd_emu10k1x_proc_init(chip);
+
+    strcpy(card->driver, "EMU10K1X");
+    strcpy(card->shortname, "Dell Sound Blaster Live!");
+    sprintf(card->longname, "%s at 0x%lx irq %i",
+            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_emu10k1x_remove(struct pci_dev *pci)
+{
+    snd_card_free(pci_get_drvdata(pci));
+    pci_set_drvdata(pci, NULL);
+}
+
+// PCI IDs
+static struct pci_device_id snd_emu10k1x_ids[] = {
+    { 0x1102, 0x0006, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },	/* Dell OEM version (EMU10K1) */
+    { 0, }
+};
+MODULE_DEVICE_TABLE(pci, snd_emu10k1x_ids);
+
+// pci_driver definition
+static struct pci_driver driver = {
+    .name = "EMU10K1X",
+    .id_table = snd_emu10k1x_ids,
+    .probe = snd_emu10k1x_probe,
+    .remove = snd_emu10k1x_remove,
+};
+
+// initialization of the module
+static int __init alsa_card_emu10k1x_init(void)
+{
+    int err;
+
+    if ((err = pci_register_driver(&driver)) > 0)
+        return err;
+
+    return 0;
+}
+
+// clean up the module
+static void __exit alsa_card_emu10k1x_exit(void)
+{
+    pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_emu10k1x_init)
+module_exit(alsa_card_emu10k1x_exit)
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/emu10k1/emufx.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/emu10k1/emufx.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/emu10k1/emufx.c	(revision 91)
@@ -0,0 +1,2594 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *                   Creative Labs, Inc.
+ *  Routines for effect processor FX8010
+ *
+ *  Copyright (c) by James Courtier-Dutton <James@superbug.co.uk>
+ *  	Added EMU 1010 support.
+ *
+ *  BUGS:
+ *    --
+ *
+ *  TODO:
+ *    --
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+
+#include <sound/core.h>
+#include <sound/tlv.h>
+#include <sound/emu10k1.h>
+
+#if 0		/* for testing purposes - digital out -> capture */
+#define EMU10K1_CAPTURE_DIGITAL_OUT
+#endif
+#if 0		/* for testing purposes - set S/PDIF to AC3 output */
+#define EMU10K1_SET_AC3_IEC958
+#endif
+#if 0		/* for testing purposes - feed the front signal to Center/LFE outputs */
+#define EMU10K1_CENTER_LFE_FROM_FRONT
+#endif
+
+/*
+ *  Tables
+ */
+
+static char *fxbuses[16] = {
+    /* 0x00 */ "PCM Left",
+    /* 0x01 */ "PCM Right",
+    /* 0x02 */ "PCM Surround Left",
+    /* 0x03 */ "PCM Surround Right",
+    /* 0x04 */ "MIDI Left",
+    /* 0x05 */ "MIDI Right",
+    /* 0x06 */ "Center",
+    /* 0x07 */ "LFE",
+    /* 0x08 */ NULL,
+    /* 0x09 */ NULL,
+    /* 0x0a */ NULL,
+    /* 0x0b */ NULL,
+    /* 0x0c */ "MIDI Reverb",
+    /* 0x0d */ "MIDI Chorus",
+    /* 0x0e */ NULL,
+    /* 0x0f */ NULL
+};
+
+static char *creative_ins[16] = {
+    /* 0x00 */ "AC97 Left",
+    /* 0x01 */ "AC97 Right",
+    /* 0x02 */ "TTL IEC958 Left",
+    /* 0x03 */ "TTL IEC958 Right",
+    /* 0x04 */ "Zoom Video Left",
+    /* 0x05 */ "Zoom Video Right",
+    /* 0x06 */ "Optical IEC958 Left",
+    /* 0x07 */ "Optical IEC958 Right",
+    /* 0x08 */ "Line/Mic 1 Left",
+    /* 0x09 */ "Line/Mic 1 Right",
+    /* 0x0a */ "Coaxial IEC958 Left",
+    /* 0x0b */ "Coaxial IEC958 Right",
+    /* 0x0c */ "Line/Mic 2 Left",
+    /* 0x0d */ "Line/Mic 2 Right",
+    /* 0x0e */ NULL,
+    /* 0x0f */ NULL
+};
+
+static char *audigy_ins[16] = {
+    /* 0x00 */ "AC97 Left",
+    /* 0x01 */ "AC97 Right",
+    /* 0x02 */ "Audigy CD Left",
+    /* 0x03 */ "Audigy CD Right",
+    /* 0x04 */ "Optical IEC958 Left",
+    /* 0x05 */ "Optical IEC958 Right",
+    /* 0x06 */ NULL,
+    /* 0x07 */ NULL,
+    /* 0x08 */ "Line/Mic 2 Left",
+    /* 0x09 */ "Line/Mic 2 Right",
+    /* 0x0a */ "SPDIF Left",
+    /* 0x0b */ "SPDIF Right",
+    /* 0x0c */ "Aux2 Left",
+    /* 0x0d */ "Aux2 Right",
+    /* 0x0e */ NULL,
+    /* 0x0f */ NULL
+};
+
+static char *creative_outs[32] = {
+    /* 0x00 */ "AC97 Left",
+    /* 0x01 */ "AC97 Right",
+    /* 0x02 */ "Optical IEC958 Left",
+    /* 0x03 */ "Optical IEC958 Right",
+    /* 0x04 */ "Center",
+    /* 0x05 */ "LFE",
+    /* 0x06 */ "Headphone Left",
+    /* 0x07 */ "Headphone Right",
+    /* 0x08 */ "Surround Left",
+    /* 0x09 */ "Surround Right",
+    /* 0x0a */ "PCM Capture Left",
+    /* 0x0b */ "PCM Capture Right",
+    /* 0x0c */ "MIC Capture",
+    /* 0x0d */ "AC97 Surround Left",
+    /* 0x0e */ "AC97 Surround Right",
+    /* 0x0f */ NULL,
+    /* 0x10 */ NULL,
+    /* 0x11 */ "Analog Center",
+    /* 0x12 */ "Analog LFE",
+    /* 0x13 */ NULL,
+    /* 0x14 */ NULL,
+    /* 0x15 */ NULL,
+    /* 0x16 */ NULL,
+    /* 0x17 */ NULL,
+    /* 0x18 */ NULL,
+    /* 0x19 */ NULL,
+    /* 0x1a */ NULL,
+    /* 0x1b */ NULL,
+    /* 0x1c */ NULL,
+    /* 0x1d */ NULL,
+    /* 0x1e */ NULL,
+    /* 0x1f */ NULL,
+};
+
+static char *audigy_outs[32] = {
+    /* 0x00 */ "Digital Front Left",
+    /* 0x01 */ "Digital Front Right",
+    /* 0x02 */ "Digital Center",
+    /* 0x03 */ "Digital LEF",
+    /* 0x04 */ "Headphone Left",
+    /* 0x05 */ "Headphone Right",
+    /* 0x06 */ "Digital Rear Left",
+    /* 0x07 */ "Digital Rear Right",
+    /* 0x08 */ "Front Left",
+    /* 0x09 */ "Front Right",
+    /* 0x0a */ "Center",
+    /* 0x0b */ "LFE",
+    /* 0x0c */ NULL,
+    /* 0x0d */ NULL,
+    /* 0x0e */ "Rear Left",
+    /* 0x0f */ "Rear Right",
+    /* 0x10 */ "AC97 Front Left",
+    /* 0x11 */ "AC97 Front Right",
+    /* 0x12 */ "ADC Caputre Left",
+    /* 0x13 */ "ADC Capture Right",
+    /* 0x14 */ NULL,
+    /* 0x15 */ NULL,
+    /* 0x16 */ NULL,
+    /* 0x17 */ NULL,
+    /* 0x18 */ NULL,
+    /* 0x19 */ NULL,
+    /* 0x1a */ NULL,
+    /* 0x1b */ NULL,
+    /* 0x1c */ NULL,
+    /* 0x1d */ NULL,
+    /* 0x1e */ NULL,
+    /* 0x1f */ NULL,
+};
+
+static const u32 bass_table[41][5] = {
+    { 0x3e4f844f, 0x84ed4cc3, 0x3cc69927, 0x7b03553a, 0xc4da8486 },
+    { 0x3e69a17a, 0x84c280fb, 0x3cd77cd4, 0x7b2f2a6f, 0xc4b08d1d },
+    { 0x3e82ff42, 0x849991d5, 0x3ce7466b, 0x7b5917c6, 0xc48863ee },
+    { 0x3e9bab3c, 0x847267f0, 0x3cf5ffe8, 0x7b813560, 0xc461f22c },
+    { 0x3eb3b275, 0x844ced29, 0x3d03b295, 0x7ba79a1c, 0xc43d223b },
+    { 0x3ecb2174, 0x84290c8b, 0x3d106714, 0x7bcc5ba3, 0xc419dfa5 },
+    { 0x3ee2044b, 0x8406b244, 0x3d1c2561, 0x7bef8e77, 0xc3f8170f },
+    { 0x3ef86698, 0x83e5cb96, 0x3d26f4d8, 0x7c114600, 0xc3d7b625 },
+    { 0x3f0e5390, 0x83c646c9, 0x3d30dc39, 0x7c319498, 0xc3b8ab97 },
+    { 0x3f23d60b, 0x83a81321, 0x3d39e1af, 0x7c508b9c, 0xc39ae704 },
+    { 0x3f38f884, 0x838b20d2, 0x3d420ad2, 0x7c6e3b75, 0xc37e58f1 },
+    { 0x3f4dc52c, 0x836f60ef, 0x3d495cab, 0x7c8ab3a6, 0xc362f2be },
+    { 0x3f6245e8, 0x8354c565, 0x3d4fdbb8, 0x7ca602d6, 0xc348a69b },
+    { 0x3f76845f, 0x833b40ec, 0x3d558bf0, 0x7cc036df, 0xc32f677c },
+    { 0x3f8a8a03, 0x8322c6fb, 0x3d5a70c4, 0x7cd95cd7, 0xc317290b },
+    { 0x3f9e6014, 0x830b4bc3, 0x3d5e8d25, 0x7cf1811a, 0xc2ffdfa5 },
+    { 0x3fb20fae, 0x82f4c420, 0x3d61e37f, 0x7d08af56, 0xc2e9804a },
+    { 0x3fc5a1cc, 0x82df2592, 0x3d6475c3, 0x7d1ef294, 0xc2d40096 },
+    { 0x3fd91f55, 0x82ca6632, 0x3d664564, 0x7d345541, 0xc2bf56b9 },
+    { 0x3fec9120, 0x82b67cac, 0x3d675356, 0x7d48e138, 0xc2ab796e },
+    { 0x40000000, 0x82a36037, 0x3d67a012, 0x7d5c9fc9, 0xc2985fee },
+    { 0x401374c7, 0x8291088a, 0x3d672b93, 0x7d6f99c3, 0xc28601f2 },
+    { 0x4026f857, 0x827f6dd7, 0x3d65f559, 0x7d81d77c, 0xc27457a3 },
+    { 0x403a939f, 0x826e88c5, 0x3d63fc63, 0x7d9360d4, 0xc2635996 },
+    { 0x404e4faf, 0x825e5266, 0x3d613f32, 0x7da43d42, 0xc25300c6 },
+    { 0x406235ba, 0x824ec434, 0x3d5dbbc3, 0x7db473d7, 0xc243468e },
+    { 0x40764f1f, 0x823fd80c, 0x3d596f8f, 0x7dc40b44, 0xc23424a2 },
+    { 0x408aa576, 0x82318824, 0x3d545787, 0x7dd309e2, 0xc2259509 },
+    { 0x409f4296, 0x8223cf0b, 0x3d4e7012, 0x7de175b5, 0xc2179218 },
+    { 0x40b430a0, 0x8216a7a1, 0x3d47b505, 0x7def5475, 0xc20a1670 },
+    { 0x40c97a0a, 0x820a0d12, 0x3d4021a1, 0x7dfcab8d, 0xc1fd1cf5 },
+    { 0x40df29a6, 0x81fdfad6, 0x3d37b08d, 0x7e098028, 0xc1f0a0ca },
+    { 0x40f54ab1, 0x81f26ca9, 0x3d2e5bd1, 0x7e15d72b, 0xc1e49d52 },
+    { 0x410be8da, 0x81e75e89, 0x3d241cce, 0x7e21b544, 0xc1d90e24 },
+    { 0x41231051, 0x81dcccb3, 0x3d18ec37, 0x7e2d1ee6, 0xc1cdef10 },
+    { 0x413acdd0, 0x81d2b39e, 0x3d0cc20a, 0x7e38184e, 0xc1c33c13 },
+    { 0x41532ea7, 0x81c90ffb, 0x3cff9585, 0x7e42a58b, 0xc1b8f15a },
+    { 0x416c40cd, 0x81bfdeb2, 0x3cf15d21, 0x7e4cca7c, 0xc1af0b3f },
+    { 0x418612ea, 0x81b71cdc, 0x3ce20e85, 0x7e568ad3, 0xc1a58640 },
+    { 0x41a0b465, 0x81aec7c5, 0x3cd19e7c, 0x7e5fea1e, 0xc19c5f03 },
+    { 0x41bc3573, 0x81a6dcea, 0x3cc000e9, 0x7e68ebc2, 0xc1939250 }
+};
+
+static const u32 treble_table[41][5] = {
+    { 0x0125cba9, 0xfed5debd, 0x00599b6c, 0x0d2506da, 0xfa85b354 },
+    { 0x0142f67e, 0xfeb03163, 0x0066cd0f, 0x0d14c69d, 0xfa914473 },
+    { 0x016328bd, 0xfe860158, 0x0075b7f2, 0x0d03eb27, 0xfa9d32d2 },
+    { 0x0186b438, 0xfe56c982, 0x00869234, 0x0cf27048, 0xfaa97fca },
+    { 0x01adf358, 0xfe21f5fe, 0x00999842, 0x0ce051c2, 0xfab62ca5 },
+    { 0x01d949fa, 0xfde6e287, 0x00af0d8d, 0x0ccd8b4a, 0xfac33aa7 },
+    { 0x02092669, 0xfda4d8bf, 0x00c73d4c, 0x0cba1884, 0xfad0ab07 },
+    { 0x023e0268, 0xfd5b0e4a, 0x00e27b54, 0x0ca5f509, 0xfade7ef2 },
+    { 0x0278645c, 0xfd08a2b0, 0x01012509, 0x0c911c63, 0xfaecb788 },
+    { 0x02b8e091, 0xfcac9d1a, 0x0123a262, 0x0c7b8a14, 0xfafb55df },
+    { 0x03001a9a, 0xfc45e9ce, 0x014a6709, 0x0c65398f, 0xfb0a5aff },
+    { 0x034ec6d7, 0xfbd3576b, 0x0175f397, 0x0c4e2643, 0xfb19c7e4 },
+    { 0x03a5ac15, 0xfb5393ee, 0x01a6d6ed, 0x0c364b94, 0xfb299d7c },
+    { 0x0405a562, 0xfac52968, 0x01ddafae, 0x0c1da4e2, 0xfb39dca5 },
+    { 0x046fa3fe, 0xfa267a66, 0x021b2ddd, 0x0c042d8d, 0xfb4a8631 },
+    { 0x04e4b17f, 0xf975be0f, 0x0260149f, 0x0be9e0f2, 0xfb5b9ae0 },
+    { 0x0565f220, 0xf8b0fbe5, 0x02ad3c29, 0x0bceba73, 0xfb6d1b60 },
+    { 0x05f4a745, 0xf7d60722, 0x030393d4, 0x0bb2b578, 0xfb7f084d },
+    { 0x06923236, 0xf6e279bd, 0x03642465, 0x0b95cd75, 0xfb916233 },
+    { 0x07401713, 0xf5d3aef9, 0x03d01283, 0x0b77fded, 0xfba42984 },
+    { 0x08000000, 0xf4a6bd88, 0x0448a161, 0x0b594278, 0xfbb75e9f },
+    { 0x08d3c097, 0xf3587131, 0x04cf35a4, 0x0b3996c9, 0xfbcb01cb },
+    { 0x09bd59a2, 0xf1e543f9, 0x05655880, 0x0b18f6b2, 0xfbdf1333 },
+    { 0x0abefd0f, 0xf04956ca, 0x060cbb12, 0x0af75e2c, 0xfbf392e8 },
+    { 0x0bdb123e, 0xee806984, 0x06c739fe, 0x0ad4c962, 0xfc0880dd },
+    { 0x0d143a94, 0xec85d287, 0x0796e150, 0x0ab134b0, 0xfc1ddce5 },
+    { 0x0e6d5664, 0xea547598, 0x087df0a0, 0x0a8c9cb6, 0xfc33a6ad },
+    { 0x0fe98a2a, 0xe7e6ba35, 0x097edf83, 0x0a66fe5b, 0xfc49ddc2 },
+    { 0x118c4421, 0xe536813a, 0x0a9c6248, 0x0a4056d7, 0xfc608185 },
+    { 0x1359422e, 0xe23d19eb, 0x0bd96efb, 0x0a18a3bf, 0xfc77912c },
+    { 0x1554982b, 0xdef33645, 0x0d3942bd, 0x09efe312, 0xfc8f0bc1 },
+    { 0x1782b68a, 0xdb50deb1, 0x0ebf676d, 0x09c6133f, 0xfca6f019 },
+    { 0x19e8715d, 0xd74d64fd, 0x106fb999, 0x099b3337, 0xfcbf3cd6 },
+    { 0x1c8b07b8, 0xd2df56ab, 0x124e6ec8, 0x096f4274, 0xfcd7f060 },
+    { 0x1f702b6d, 0xcdfc6e92, 0x14601c10, 0x0942410b, 0xfcf108e5 },
+    { 0x229e0933, 0xc89985cd, 0x16a9bcfa, 0x09142fb5, 0xfd0a8451 },
+    { 0x261b5118, 0xc2aa8409, 0x1930bab6, 0x08e50fdc, 0xfd24604d },
+    { 0x29ef3f5d, 0xbc224f28, 0x1bfaf396, 0x08b4e3aa, 0xfd3e9a3b },
+    { 0x2e21a59b, 0xb4f2ba46, 0x1f0ec2d6, 0x0883ae15, 0xfd592f33 },
+    { 0x32baf44b, 0xad0c7429, 0x227308a3, 0x085172eb, 0xfd741bfd },
+    { 0x37c4448b, 0xa45ef51d, 0x262f3267, 0x081e36dc, 0xfd8f5d14 }
+};
+
+/* dB gain = (float) 20 * log10( float(db_table_value) / 0x8000000 ) */
+static const u32 db_table[101] = {
+    0x00000000, 0x01571f82, 0x01674b41, 0x01783a1b, 0x0189f540,
+    0x019c8651, 0x01aff763, 0x01c45306, 0x01d9a446, 0x01eff6b8,
+    0x0207567a, 0x021fd03d, 0x0239714c, 0x02544792, 0x027061a1,
+    0x028dcebb, 0x02ac9edc, 0x02cce2bf, 0x02eeabe8, 0x03120cb0,
+    0x0337184e, 0x035de2df, 0x03868173, 0x03b10a18, 0x03dd93e9,
+    0x040c3713, 0x043d0cea, 0x04702ff3, 0x04a5bbf2, 0x04ddcdfb,
+    0x0518847f, 0x0555ff62, 0x05966005, 0x05d9c95d, 0x06206005,
+    0x066a4a52, 0x06b7b067, 0x0708bc4c, 0x075d9a01, 0x07b6779d,
+    0x08138561, 0x0874f5d5, 0x08dafde1, 0x0945d4ed, 0x09b5b4fd,
+    0x0a2adad1, 0x0aa58605, 0x0b25f936, 0x0bac7a24, 0x0c3951d8,
+    0x0ccccccc, 0x0d673b17, 0x0e08f093, 0x0eb24510, 0x0f639481,
+    0x101d3f2d, 0x10dfa9e6, 0x11ab3e3f, 0x12806ac3, 0x135fa333,
+    0x144960c5, 0x153e2266, 0x163e6cfe, 0x174acbb7, 0x1863d04d,
+    0x198a1357, 0x1abe349f, 0x1c00db77, 0x1d52b712, 0x1eb47ee6,
+    0x2026f30f, 0x21aadcb6, 0x23410e7e, 0x24ea64f9, 0x26a7c71d,
+    0x287a26c4, 0x2a62812c, 0x2c61df84, 0x2e795779, 0x30aa0bcf,
+    0x32f52cfe, 0x355bf9d8, 0x37dfc033, 0x3a81dda4, 0x3d43c038,
+    0x4026e73c, 0x432ce40f, 0x46575af8, 0x49a8040f, 0x4d20ac2a,
+    0x50c335d3, 0x54919a57, 0x588dead1, 0x5cba514a, 0x611911ea,
+    0x65ac8c2f, 0x6a773c39, 0x6f7bbc23, 0x74bcc56c, 0x7a3d3272,
+    0x7fffffff,
+};
+
+/* EMU10k1/EMU10k2 DSP control db gain */
+static DECLARE_TLV_DB_SCALE(snd_emu10k1_db_scale1, -4000, 40, 1);
+
+static const u32 onoff_table[2] = {
+    0x00000000, 0x00000001
+};
+
+/*
+ */
+
+#ifndef TARGET_OS2
+static inline mm_segment_t snd_enter_user(void)
+{
+    mm_segment_t fs = get_fs();
+    set_fs(get_ds());
+    return fs;
+}
+
+static inline void snd_leave_user(mm_segment_t fs)
+{
+    set_fs(fs);
+}
+#endif
+/*
+ *   controls
+ */
+
+static int snd_emu10k1_gpr_ctl_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+    struct snd_emu10k1_fx8010_ctl *ctl =
+        (struct snd_emu10k1_fx8010_ctl *) kcontrol->private_value;
+
+    if (ctl->min == 0 && ctl->max == 1)
+        uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+    else
+        uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+    uinfo->count = ctl->vcount;
+    uinfo->value.integer.min = ctl->min;
+    uinfo->value.integer.max = ctl->max;
+    return 0;
+}
+
+static int snd_emu10k1_gpr_ctl_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+    struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+    struct snd_emu10k1_fx8010_ctl *ctl =
+        (struct snd_emu10k1_fx8010_ctl *) kcontrol->private_value;
+    unsigned long flags;
+    unsigned int i;
+
+    spin_lock_irqsave(&emu->reg_lock, flags);
+    for (i = 0; i < ctl->vcount; i++)
+        ucontrol->value.integer.value[i] = ctl->value[i];
+    spin_unlock_irqrestore(&emu->reg_lock, flags);
+    return 0;
+}
+
+static int snd_emu10k1_gpr_ctl_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+    struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+    struct snd_emu10k1_fx8010_ctl *ctl =
+        (struct snd_emu10k1_fx8010_ctl *) kcontrol->private_value;
+    unsigned long flags;
+    unsigned int nval, val;
+    unsigned int i, j;
+    int change = 0;
+
+    spin_lock_irqsave(&emu->reg_lock, flags);
+    for (i = 0; i < ctl->vcount; i++) {
+        nval = ucontrol->value.integer.value[i];
+        if (nval < ctl->min)
+            nval = ctl->min;
+        if (nval > ctl->max)
+            nval = ctl->max;
+        if (nval != ctl->value[i])
+            change = 1;
+        val = ctl->value[i] = nval;
+        switch (ctl->translation) {
+        case EMU10K1_GPR_TRANSLATION_NONE:
+            snd_emu10k1_ptr_write(emu, emu->gpr_base + ctl->gpr[i], 0, val);
+            break;
+        case EMU10K1_GPR_TRANSLATION_TABLE100:
+            snd_emu10k1_ptr_write(emu, emu->gpr_base + ctl->gpr[i], 0, db_table[val]);
+            break;
+        case EMU10K1_GPR_TRANSLATION_BASS:
+            if ((ctl->count % 5) != 0 || (ctl->count / 5) != ctl->vcount) {
+                change = -EIO;
+                goto __error;
+            }
+            for (j = 0; j < 5; j++)
+                snd_emu10k1_ptr_write(emu, emu->gpr_base + ctl->gpr[j * ctl->vcount + i], 0, bass_table[val][j]);
+            break;
+        case EMU10K1_GPR_TRANSLATION_TREBLE:
+            if ((ctl->count % 5) != 0 || (ctl->count / 5) != ctl->vcount) {
+                change = -EIO;
+                goto __error;
+            }
+            for (j = 0; j < 5; j++)
+                snd_emu10k1_ptr_write(emu, emu->gpr_base + ctl->gpr[j * ctl->vcount + i], 0, treble_table[val][j]);
+            break;
+        case EMU10K1_GPR_TRANSLATION_ONOFF:
+            snd_emu10k1_ptr_write(emu, emu->gpr_base + ctl->gpr[i], 0, onoff_table[val]);
+            break;
+        }
+    }
+__error:
+    spin_unlock_irqrestore(&emu->reg_lock, flags);
+    return change;
+}
+
+/*
+ *   Interrupt handler
+ */
+
+static void snd_emu10k1_fx8010_interrupt(struct snd_emu10k1 *emu)
+{
+    struct snd_emu10k1_fx8010_irq *irq, *nirq;
+
+    irq = emu->fx8010.irq_handlers;
+    while (irq) {
+        nirq = irq->next;	/* irq ptr can be removed from list */
+        if (snd_emu10k1_ptr_read(emu, emu->gpr_base + irq->gpr_running, 0) & 0xffff0000) {
+            if (irq->handler)
+                irq->handler(emu, irq->private_data);
+            snd_emu10k1_ptr_write(emu, emu->gpr_base + irq->gpr_running, 0, 1);
+        }
+        irq = nirq;
+    }
+}
+
+int snd_emu10k1_fx8010_register_irq_handler(struct snd_emu10k1 *emu,
+                                            snd_fx8010_irq_handler_t *handler,
+                                            unsigned char gpr_running,
+                                            void *private_data,
+                                            struct snd_emu10k1_fx8010_irq **r_irq)
+{
+    struct snd_emu10k1_fx8010_irq *irq;
+    unsigned long flags;
+
+    irq = kmalloc(sizeof(*irq), GFP_ATOMIC);
+    if (irq == NULL)
+        return -ENOMEM;
+    irq->handler = handler;
+    irq->gpr_running = gpr_running;
+    irq->private_data = private_data;
+    irq->next = NULL;
+    spin_lock_irqsave(&emu->fx8010.irq_lock, flags);
+    if (emu->fx8010.irq_handlers == NULL) {
+        emu->fx8010.irq_handlers = irq;
+        emu->dsp_interrupt = snd_emu10k1_fx8010_interrupt;
+        snd_emu10k1_intr_enable(emu, INTE_FXDSPENABLE);
+    } else {
+        irq->next = emu->fx8010.irq_handlers;
+        emu->fx8010.irq_handlers = irq;
+    }
+    spin_unlock_irqrestore(&emu->fx8010.irq_lock, flags);
+    if (r_irq)
+        *r_irq = irq;
+    return 0;
+}
+
+int snd_emu10k1_fx8010_unregister_irq_handler(struct snd_emu10k1 *emu,
+                                              struct snd_emu10k1_fx8010_irq *irq)
+{
+    struct snd_emu10k1_fx8010_irq *tmp;
+    unsigned long flags;
+
+    spin_lock_irqsave(&emu->fx8010.irq_lock, flags);
+    if ((tmp = emu->fx8010.irq_handlers) == irq) {
+        emu->fx8010.irq_handlers = tmp->next;
+        if (emu->fx8010.irq_handlers == NULL) {
+            snd_emu10k1_intr_disable(emu, INTE_FXDSPENABLE);
+            emu->dsp_interrupt = NULL;
+        }
+    } else {
+        while (tmp && tmp->next != irq)
+            tmp = tmp->next;
+        if (tmp)
+            tmp->next = tmp->next->next;
+    }
+    spin_unlock_irqrestore(&emu->fx8010.irq_lock, flags);
+    kfree(irq);
+    return 0;
+}
+
+/*************************************************************************
+ * EMU10K1 effect manager
+ *************************************************************************/
+
+static void snd_emu10k1_write_op(struct snd_emu10k1_fx8010_code *icode,
+                                 unsigned int *ptr,
+                                 u32 op, u32 r, u32 a, u32 x, u32 y)
+{
+    u_int32_t *code;
+    snd_assert(*ptr < 512, return);
+    code = (u_int32_t __force *)icode->code + (*ptr) * 2;
+    set_bit(*ptr, icode->code_valid);
+    code[0] = ((x & 0x3ff) << 10) | (y & 0x3ff);
+    code[1] = ((op & 0x0f) << 20) | ((r & 0x3ff) << 10) | (a & 0x3ff);
+    (*ptr)++;
+}
+
+#define OP(icode, ptr, op, r, a, x, y) \
+    snd_emu10k1_write_op(icode, ptr, op, r, a, x, y)
+
+static void snd_emu10k1_audigy_write_op(struct snd_emu10k1_fx8010_code *icode,
+                                        unsigned int *ptr,
+                                        u32 op, u32 r, u32 a, u32 x, u32 y)
+{
+    u_int32_t *code;
+    snd_assert(*ptr < 1024, return);
+    code = (u_int32_t __force *)icode->code + (*ptr) * 2;
+    set_bit(*ptr, icode->code_valid);
+    code[0] = ((x & 0x7ff) << 12) | (y & 0x7ff);
+    code[1] = ((op & 0x0f) << 24) | ((r & 0x7ff) << 12) | (a & 0x7ff);
+    (*ptr)++;
+}
+
+#define A_OP(icode, ptr, op, r, a, x, y) \
+    snd_emu10k1_audigy_write_op(icode, ptr, op, r, a, x, y)
+
+static void snd_emu10k1_efx_write(struct snd_emu10k1 *emu, unsigned int pc, unsigned int data)
+{
+    pc += emu->audigy ? A_MICROCODEBASE : MICROCODEBASE;
+    snd_emu10k1_ptr_write(emu, pc, 0, data);
+}
+
+unsigned int snd_emu10k1_efx_read(struct snd_emu10k1 *emu, unsigned int pc)
+{
+    pc += emu->audigy ? A_MICROCODEBASE : MICROCODEBASE;
+    return snd_emu10k1_ptr_read(emu, pc, 0);
+}
+
+static int snd_emu10k1_gpr_poke(struct snd_emu10k1 *emu,
+                                struct snd_emu10k1_fx8010_code *icode)
+{
+    int gpr;
+    u32 val;
+
+    for (gpr = 0; gpr < (emu->audigy ? 0x200 : 0x100); gpr++) {
+        if (!test_bit(gpr, icode->gpr_valid))
+            continue;
+        if (get_user(val, &icode->gpr_map[gpr]))
+            return -EFAULT;
+        snd_emu10k1_ptr_write(emu, emu->gpr_base + gpr, 0, val);
+    }
+    return 0;
+}
+
+static int snd_emu10k1_gpr_peek(struct snd_emu10k1 *emu,
+                                struct snd_emu10k1_fx8010_code *icode)
+{
+    int gpr;
+    u32 val;
+
+    for (gpr = 0; gpr < (emu->audigy ? 0x200 : 0x100); gpr++) {
+        set_bit(gpr, icode->gpr_valid);
+        val = snd_emu10k1_ptr_read(emu, emu->gpr_base + gpr, 0);
+        if (put_user(val, &icode->gpr_map[gpr]))
+            return -EFAULT;
+    }
+    return 0;
+}
+
+static int snd_emu10k1_tram_poke(struct snd_emu10k1 *emu,
+                                 struct snd_emu10k1_fx8010_code *icode)
+{
+    int tram;
+    u32 addr, val;
+
+    for (tram = 0; tram < (emu->audigy ? 0x100 : 0xa0); tram++) {
+        if (!test_bit(tram, icode->tram_valid))
+            continue;
+        if (get_user(val, &icode->tram_data_map[tram]) ||
+            get_user(addr, &icode->tram_addr_map[tram]))
+            return -EFAULT;
+        snd_emu10k1_ptr_write(emu, TANKMEMDATAREGBASE + tram, 0, val);
+        if (!emu->audigy) {
+            snd_emu10k1_ptr_write(emu, TANKMEMADDRREGBASE + tram, 0, addr);
+        } else {
+            snd_emu10k1_ptr_write(emu, TANKMEMADDRREGBASE + tram, 0, addr << 12);
+            snd_emu10k1_ptr_write(emu, A_TANKMEMCTLREGBASE + tram, 0, addr >> 20);
+        }
+    }
+    return 0;
+}
+
+static int snd_emu10k1_tram_peek(struct snd_emu10k1 *emu,
+                                 struct snd_emu10k1_fx8010_code *icode)
+{
+    int tram;
+    u32 val, addr;
+
+    memset(icode->tram_valid, 0, sizeof(icode->tram_valid));
+    for (tram = 0; tram < (emu->audigy ? 0x100 : 0xa0); tram++) {
+        set_bit(tram, icode->tram_valid);
+        val = snd_emu10k1_ptr_read(emu, TANKMEMDATAREGBASE + tram, 0);
+        if (!emu->audigy) {
+            addr = snd_emu10k1_ptr_read(emu, TANKMEMADDRREGBASE + tram, 0);
+        } else {
+            addr = snd_emu10k1_ptr_read(emu, TANKMEMADDRREGBASE + tram, 0) >> 12;
+            addr |= snd_emu10k1_ptr_read(emu, A_TANKMEMCTLREGBASE + tram, 0) << 20;
+        }
+        if (put_user(val, &icode->tram_data_map[tram]) ||
+            put_user(addr, &icode->tram_addr_map[tram]))
+            return -EFAULT;
+    }
+    return 0;
+}
+
+static int snd_emu10k1_code_poke(struct snd_emu10k1 *emu,
+                                 struct snd_emu10k1_fx8010_code *icode)
+{
+    u32 pc, lo, hi;
+
+    for (pc = 0; pc < (emu->audigy ? 2*1024 : 2*512); pc += 2) {
+        if (!test_bit(pc / 2, icode->code_valid))
+            continue;
+        if (get_user(lo, &icode->code[pc + 0]) ||
+            get_user(hi, &icode->code[pc + 1]))
+            return -EFAULT;
+        snd_emu10k1_efx_write(emu, pc + 0, lo);
+        snd_emu10k1_efx_write(emu, pc + 1, hi);
+    }
+    return 0;
+}
+
+static int snd_emu10k1_code_peek(struct snd_emu10k1 *emu,
+                                 struct snd_emu10k1_fx8010_code *icode)
+{
+    u32 pc;
+
+    memset(icode->code_valid, 0, sizeof(icode->code_valid));
+    for (pc = 0; pc < (emu->audigy ? 2*1024 : 2*512); pc += 2) {
+        set_bit(pc / 2, icode->code_valid);
+        if (put_user(snd_emu10k1_efx_read(emu, pc + 0), &icode->code[pc + 0]))
+            return -EFAULT;
+        if (put_user(snd_emu10k1_efx_read(emu, pc + 1), &icode->code[pc + 1]))
+            return -EFAULT;
+    }
+    return 0;
+}
+
+static struct snd_emu10k1_fx8010_ctl *
+snd_emu10k1_look_for_ctl(struct snd_emu10k1 *emu, struct snd_ctl_elem_id *id)
+{
+    struct snd_emu10k1_fx8010_ctl *ctl;
+    struct snd_kcontrol *kcontrol;
+    struct list_head *list;
+
+    list_for_each(list, &emu->fx8010.gpr_ctl) {
+        ctl = emu10k1_gpr_ctl(list);
+        kcontrol = ctl->kcontrol;
+        if (kcontrol->id.iface == id->iface &&
+            !strcmp(kcontrol->id.name, id->name) &&
+            kcontrol->id.index == id->index)
+            return ctl;
+    }
+    return NULL;
+}
+
+static int snd_emu10k1_verify_controls(struct snd_emu10k1 *emu,
+                                       struct snd_emu10k1_fx8010_code *icode)
+{
+    unsigned int i;
+    struct snd_ctl_elem_id __user *_id;
+    struct snd_ctl_elem_id id;
+    struct snd_emu10k1_fx8010_control_gpr __user *_gctl;
+    struct snd_emu10k1_fx8010_control_gpr *gctl;
+    int err;
+
+    for (i = 0, _id = icode->gpr_del_controls;
+         i < icode->gpr_del_control_count; i++, _id++) {
+        if (copy_from_user(&id, _id, sizeof(id)))
+            return -EFAULT;
+        if (snd_emu10k1_look_for_ctl(emu, &id) == NULL)
+            return -ENOENT;
+    }
+    gctl = kmalloc(sizeof(*gctl), GFP_KERNEL);
+    if (! gctl)
+        return -ENOMEM;
+    err = 0;
+    for (i = 0, _gctl = icode->gpr_add_controls;
+         i < icode->gpr_add_control_count; i++, _gctl++) {
+        if (copy_from_user(gctl, _gctl, sizeof(*gctl))) {
+            err = -EFAULT;
+            goto __error;
+        }
+        if (snd_emu10k1_look_for_ctl(emu, &gctl->id))
+            continue;
+        down_read(&emu->card->controls_rwsem);
+        if (snd_ctl_find_id(emu->card, &gctl->id) != NULL) {
+            up_read(&emu->card->controls_rwsem);
+            err = -EEXIST;
+            goto __error;
+        }
+        up_read(&emu->card->controls_rwsem);
+        if (gctl->id.iface != SNDRV_CTL_ELEM_IFACE_MIXER &&
+            gctl->id.iface != SNDRV_CTL_ELEM_IFACE_PCM) {
+            err = -EINVAL;
+            goto __error;
+        }
+    }
+    for (i = 0, _gctl = icode->gpr_list_controls;
+         i < icode->gpr_list_control_count; i++, _gctl++) {
+        /* FIXME: we need to check the WRITE access */
+        if (copy_from_user(gctl, _gctl, sizeof(*gctl))) {
+            err = -EFAULT;
+            goto __error;
+        }
+    }
+__error:
+    kfree(gctl);
+    return err;
+}
+
+static void snd_emu10k1_ctl_private_free(struct snd_kcontrol *kctl)
+{
+    struct snd_emu10k1_fx8010_ctl *ctl;
+
+    ctl = (struct snd_emu10k1_fx8010_ctl *)kctl->private_value;
+    kctl->private_value = 0;
+    list_del(&ctl->list);
+    kfree(ctl);
+}
+
+static int snd_emu10k1_add_controls(struct snd_emu10k1 *emu,
+                                    struct snd_emu10k1_fx8010_code *icode)
+{
+    unsigned int i, j;
+    struct snd_emu10k1_fx8010_control_gpr __user *_gctl;
+    struct snd_emu10k1_fx8010_control_gpr *gctl;
+    struct snd_emu10k1_fx8010_ctl *ctl, *nctl;
+    struct snd_kcontrol_new knew;
+    struct snd_kcontrol *kctl;
+    struct snd_ctl_elem_value *val;
+    int err = 0;
+
+    val = kmalloc(sizeof(*val), GFP_KERNEL);
+    gctl = kmalloc(sizeof(*gctl), GFP_KERNEL);
+    nctl = kmalloc(sizeof(*nctl), GFP_KERNEL);
+    if (!val || !gctl || !nctl) {
+        err = -ENOMEM;
+        goto __error;
+    }
+
+    for (i = 0, _gctl = icode->gpr_add_controls;
+         i < icode->gpr_add_control_count; i++, _gctl++) {
+        if (copy_from_user(gctl, _gctl, sizeof(*gctl))) {
+            err = -EFAULT;
+            goto __error;
+        }
+        if (gctl->id.iface != SNDRV_CTL_ELEM_IFACE_MIXER &&
+            gctl->id.iface != SNDRV_CTL_ELEM_IFACE_PCM) {
+            err = -EINVAL;
+            goto __error;
+        }
+        if (! gctl->id.name[0]) {
+            err = -EINVAL;
+            goto __error;
+        }
+        ctl = snd_emu10k1_look_for_ctl(emu, &gctl->id);
+        memset(&knew, 0, sizeof(knew));
+        knew.iface = gctl->id.iface;
+        knew.name = gctl->id.name;
+        knew.index = gctl->id.index;
+        knew.device = gctl->id.device;
+        knew.subdevice = gctl->id.subdevice;
+        knew.info = snd_emu10k1_gpr_ctl_info;
+        if (gctl->tlv.p) {
+            knew.tlv.p = gctl->tlv.p;
+            knew.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+                SNDRV_CTL_ELEM_ACCESS_TLV_READ;
+        }
+        knew.get = snd_emu10k1_gpr_ctl_get;
+        knew.put = snd_emu10k1_gpr_ctl_put;
+        memset(nctl, 0, sizeof(*nctl));
+        nctl->vcount = gctl->vcount;
+        nctl->count = gctl->count;
+        for (j = 0; j < 32; j++) {
+            nctl->gpr[j] = gctl->gpr[j];
+            nctl->value[j] = ~gctl->value[j];       /* inverted, we want to write new value in gpr_ctl_put() */
+            val->value.integer.value[j] = gctl->value[j];
+        }
+        nctl->min = gctl->min;
+        nctl->max = gctl->max;
+        nctl->translation = gctl->translation;
+        if (ctl == NULL) {
+            ctl = kmalloc(sizeof(*ctl), GFP_KERNEL);
+            if (ctl == NULL) {
+                err = -ENOMEM;
+                goto __error;
+            }
+            knew.private_value = (unsigned long)ctl;
+            *ctl = *nctl;
+            if ((err = snd_ctl_add(emu->card, kctl = snd_ctl_new1(&knew, emu))) < 0) {
+                kfree(ctl);
+                goto __error;
+            }
+            kctl->private_free = snd_emu10k1_ctl_private_free;
+            ctl->kcontrol = kctl;
+            list_add_tail(&ctl->list, &emu->fx8010.gpr_ctl);
+        } else {
+            /* overwrite */
+            nctl->list = ctl->list;
+            nctl->kcontrol = ctl->kcontrol;
+            *ctl = *nctl;
+            snd_ctl_notify(emu->card, SNDRV_CTL_EVENT_MASK_VALUE |
+                           SNDRV_CTL_EVENT_MASK_INFO, &ctl->kcontrol->id);
+        }
+        snd_emu10k1_gpr_ctl_put(ctl->kcontrol, val);
+    }
+__error:
+    kfree(nctl);
+    kfree(gctl);
+    kfree(val);
+    return err;
+}
+
+static int snd_emu10k1_del_controls(struct snd_emu10k1 *emu,
+                                    struct snd_emu10k1_fx8010_code *icode)
+{
+    unsigned int i;
+    struct snd_ctl_elem_id id;
+    struct snd_ctl_elem_id __user *_id;
+    struct snd_emu10k1_fx8010_ctl *ctl;
+    struct snd_card *card = emu->card;
+
+    for (i = 0, _id = icode->gpr_del_controls;
+         i < icode->gpr_del_control_count; i++, _id++) {
+        if (copy_from_user(&id, _id, sizeof(id)))
+            return -EFAULT;
+        down_write(&card->controls_rwsem);
+        ctl = snd_emu10k1_look_for_ctl(emu, &id);
+        if (ctl)
+            snd_ctl_remove(card, ctl->kcontrol);
+        up_write(&card->controls_rwsem);
+    }
+    return 0;
+}
+
+static int snd_emu10k1_list_controls(struct snd_emu10k1 *emu,
+                                     struct snd_emu10k1_fx8010_code *icode)
+{
+    unsigned int i = 0, j;
+    unsigned int total = 0;
+    struct snd_emu10k1_fx8010_control_gpr *gctl;
+    struct snd_emu10k1_fx8010_control_gpr __user *_gctl;
+    struct snd_emu10k1_fx8010_ctl *ctl;
+    struct snd_ctl_elem_id *id;
+    struct list_head *list;
+
+    gctl = kmalloc(sizeof(*gctl), GFP_KERNEL);
+    if (! gctl)
+        return -ENOMEM;
+
+    _gctl = icode->gpr_list_controls;
+    list_for_each(list, &emu->fx8010.gpr_ctl) {
+        ctl = emu10k1_gpr_ctl(list);
+        total++;
+        if (_gctl && i < icode->gpr_list_control_count) {
+            memset(gctl, 0, sizeof(*gctl));
+            id = &ctl->kcontrol->id;
+            gctl->id.iface = id->iface;
+            strlcpy(gctl->id.name, id->name, sizeof(gctl->id.name));
+            gctl->id.index = id->index;
+            gctl->id.device = id->device;
+            gctl->id.subdevice = id->subdevice;
+            gctl->vcount = ctl->vcount;
+            gctl->count = ctl->count;
+            for (j = 0; j < 32; j++) {
+                gctl->gpr[j] = ctl->gpr[j];
+                gctl->value[j] = ctl->value[j];
+            }
+            gctl->min = ctl->min;
+            gctl->max = ctl->max;
+            gctl->translation = ctl->translation;
+            if (copy_to_user(_gctl, gctl, sizeof(*gctl))) {
+                kfree(gctl);
+                return -EFAULT;
+            }
+            _gctl++;
+            i++;
+        }
+    }
+    icode->gpr_list_control_total = total;
+    kfree(gctl);
+    return 0;
+}
+
+static int snd_emu10k1_icode_poke(struct snd_emu10k1 *emu,
+                                  struct snd_emu10k1_fx8010_code *icode)
+{
+    int err = 0;
+
+    down(&emu->fx8010.lock);
+    if ((err = snd_emu10k1_verify_controls(emu, icode)) < 0)
+        goto __error;
+    strlcpy(emu->fx8010.name, icode->name, sizeof(emu->fx8010.name));
+    /* stop FX processor - this may be dangerous, but it's better to miss
+     some samples than generate wrong ones - [jk] */
+    if (emu->audigy)
+        snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg | A_DBG_SINGLE_STEP);
+    else
+        snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg | EMU10K1_DBG_SINGLE_STEP);
+    /* ok, do the main job */
+    if ((err = snd_emu10k1_del_controls(emu, icode)) < 0 ||
+        (err = snd_emu10k1_gpr_poke(emu, icode)) < 0 ||
+        (err = snd_emu10k1_tram_poke(emu, icode)) < 0 ||
+        (err = snd_emu10k1_code_poke(emu, icode)) < 0 ||
+        (err = snd_emu10k1_add_controls(emu, icode)) < 0)
+        goto __error;
+    /* start FX processor when the DSP code is updated */
+    if (emu->audigy)
+        snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg);
+    else
+        snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg);
+__error:
+    up(&emu->fx8010.lock);
+    return err;
+}
+
+static int snd_emu10k1_icode_peek(struct snd_emu10k1 *emu,
+                                  struct snd_emu10k1_fx8010_code *icode)
+{
+    int err;
+
+    down(&emu->fx8010.lock);
+    strlcpy(icode->name, emu->fx8010.name, sizeof(icode->name));
+    /* ok, do the main job */
+    err = snd_emu10k1_gpr_peek(emu, icode);
+    if (err >= 0)
+        err = snd_emu10k1_tram_peek(emu, icode);
+    if (err >= 0)
+        err = snd_emu10k1_code_peek(emu, icode);
+    if (err >= 0)
+        err = snd_emu10k1_list_controls(emu, icode);
+    up(&emu->fx8010.lock);
+    return err;
+}
+
+static int snd_emu10k1_ipcm_poke(struct snd_emu10k1 *emu,
+                                 struct snd_emu10k1_fx8010_pcm_rec *ipcm)
+{
+    unsigned int i;
+    int err = 0;
+    struct snd_emu10k1_fx8010_pcm *pcm;
+
+    if (ipcm->substream >= EMU10K1_FX8010_PCM_COUNT)
+        return -EINVAL;
+    if (ipcm->channels > 32)
+        return -EINVAL;
+    pcm = &emu->fx8010.pcm[ipcm->substream];
+    down(&emu->fx8010.lock);
+    spin_lock_irq(&emu->reg_lock);
+    if (pcm->opened) {
+        err = -EBUSY;
+        goto __error;
+    }
+    if (ipcm->channels == 0) {	/* remove */
+        pcm->valid = 0;
+    } else {
+        /* FIXME: we need to add universal code to the PCM transfer routine */
+        if (ipcm->channels != 2) {
+            err = -EINVAL;
+            goto __error;
+        }
+        pcm->valid = 1;
+        pcm->opened = 0;
+        pcm->channels = ipcm->channels;
+        pcm->tram_start = ipcm->tram_start;
+        pcm->buffer_size = ipcm->buffer_size;
+        pcm->gpr_size = ipcm->gpr_size;
+        pcm->gpr_count = ipcm->gpr_count;
+        pcm->gpr_tmpcount = ipcm->gpr_tmpcount;
+        pcm->gpr_ptr = ipcm->gpr_ptr;
+        pcm->gpr_trigger = ipcm->gpr_trigger;
+        pcm->gpr_running = ipcm->gpr_running;
+        for (i = 0; i < pcm->channels; i++)
+            pcm->etram[i] = ipcm->etram[i];
+    }
+__error:
+    spin_unlock_irq(&emu->reg_lock);
+    up(&emu->fx8010.lock);
+    return err;
+}
+
+static int snd_emu10k1_ipcm_peek(struct snd_emu10k1 *emu,
+                                 struct snd_emu10k1_fx8010_pcm_rec *ipcm)
+{
+    unsigned int i;
+    int err = 0;
+    struct snd_emu10k1_fx8010_pcm *pcm;
+
+    if (ipcm->substream >= EMU10K1_FX8010_PCM_COUNT)
+        return -EINVAL;
+    pcm = &emu->fx8010.pcm[ipcm->substream];
+    down(&emu->fx8010.lock);
+    spin_lock_irq(&emu->reg_lock);
+    ipcm->channels = pcm->channels;
+    ipcm->tram_start = pcm->tram_start;
+    ipcm->buffer_size = pcm->buffer_size;
+    ipcm->gpr_size = pcm->gpr_size;
+    ipcm->gpr_ptr = pcm->gpr_ptr;
+    ipcm->gpr_count = pcm->gpr_count;
+    ipcm->gpr_tmpcount = pcm->gpr_tmpcount;
+    ipcm->gpr_trigger = pcm->gpr_trigger;
+    ipcm->gpr_running = pcm->gpr_running;
+    for (i = 0; i < pcm->channels; i++)
+        ipcm->etram[i] = pcm->etram[i];
+    ipcm->res1 = ipcm->res2 = 0;
+    ipcm->pad = 0;
+    spin_unlock_irq(&emu->reg_lock);
+    up(&emu->fx8010.lock);
+    return err;
+}
+
+#define SND_EMU10K1_GPR_CONTROLS        44
+#define SND_EMU10K1_INPUTS              12
+#define SND_EMU10K1_PLAYBACK_CHANNELS   8
+#define SND_EMU10K1_CAPTURE_CHANNELS	4
+
+static void __devinit
+snd_emu10k1_init_mono_control(struct snd_emu10k1_fx8010_control_gpr *ctl,
+                              const char *name, int gpr, int defval)
+{
+    ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+    strcpy(ctl->id.name, name);
+    ctl->vcount = ctl->count = 1;
+    ctl->gpr[0] = gpr + 0; ctl->value[0] = defval;
+    ctl->min = 0;
+    ctl->max = 100;
+    ctl->tlv.p = snd_emu10k1_db_scale1;
+    ctl->translation = EMU10K1_GPR_TRANSLATION_TABLE100;
+}
+
+static void __devinit
+snd_emu10k1_init_stereo_control(struct snd_emu10k1_fx8010_control_gpr *ctl,
+                                const char *name, int gpr, int defval)
+{
+    ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+    strcpy(ctl->id.name, name);
+    ctl->vcount = ctl->count = 2;
+    ctl->gpr[0] = gpr + 0; ctl->value[0] = defval;
+    ctl->gpr[1] = gpr + 1; ctl->value[1] = defval;
+    ctl->min = 0;
+    ctl->max = 100;
+    ctl->tlv.p = snd_emu10k1_db_scale1;
+    ctl->translation = EMU10K1_GPR_TRANSLATION_TABLE100;
+}
+
+static void __devinit
+snd_emu10k1_init_mono_onoff_control(struct snd_emu10k1_fx8010_control_gpr *ctl,
+                                    const char *name, int gpr, int defval)
+{
+    ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+    strcpy(ctl->id.name, name);
+    ctl->vcount = ctl->count = 1;
+    ctl->gpr[0] = gpr + 0; ctl->value[0] = defval;
+    ctl->min = 0;
+    ctl->max = 1;
+    ctl->translation = EMU10K1_GPR_TRANSLATION_ONOFF;
+}
+
+static void __devinit
+snd_emu10k1_init_stereo_onoff_control(struct snd_emu10k1_fx8010_control_gpr *ctl,
+                                      const char *name, int gpr, int defval)
+{
+    ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+    strcpy(ctl->id.name, name);
+    ctl->vcount = ctl->count = 2;
+    ctl->gpr[0] = gpr + 0; ctl->value[0] = defval;
+    ctl->gpr[1] = gpr + 1; ctl->value[1] = defval;
+    ctl->min = 0;
+    ctl->max = 1;
+    ctl->translation = EMU10K1_GPR_TRANSLATION_ONOFF;
+}
+
+static int snd_emu10k1_audigy_dsp_convert_32_to_2x16(
+                                                     struct snd_emu10k1_fx8010_code *icode,
+                                                     u32 *ptr, int tmp, int bit_shifter16,
+                                                     int reg_in, int reg_out)
+{
+    A_OP(icode, ptr, iACC3, A_GPR(tmp + 1), reg_in, A_C_00000000, A_C_00000000);
+    A_OP(icode, ptr, iANDXOR, A_GPR(tmp), A_GPR(tmp + 1), A_GPR(bit_shifter16 - 1), A_C_00000000);
+    A_OP(icode, ptr, iTSTNEG, A_GPR(tmp + 2), A_GPR(tmp), A_C_80000000, A_GPR(bit_shifter16 - 2));
+    A_OP(icode, ptr, iANDXOR, A_GPR(tmp + 2), A_GPR(tmp + 2), A_C_80000000, A_C_00000000);
+    A_OP(icode, ptr, iANDXOR, A_GPR(tmp), A_GPR(tmp), A_GPR(bit_shifter16 - 3), A_C_00000000);
+    A_OP(icode, ptr, iMACINT0, A_GPR(tmp), A_C_00000000, A_GPR(tmp), A_C_00010000);
+    A_OP(icode, ptr, iANDXOR, reg_out, A_GPR(tmp), A_C_ffffffff, A_GPR(tmp + 2));
+    A_OP(icode, ptr, iACC3, reg_out + 1, A_GPR(tmp + 1), A_C_00000000, A_C_00000000);
+    return 1;
+}
+
+/*
+ * initial DSP configuration for Audigy
+ */
+
+static int __devinit _snd_emu10k1_audigy_init_efx(struct snd_emu10k1 *emu)
+{
+    int err, i, z, gpr, nctl;
+    int bit_shifter16;
+    const int playback = 10;
+    const int capture = playback + (SND_EMU10K1_PLAYBACK_CHANNELS * 2); /* we reserve 10 voices */
+    const int stereo_mix = capture + 2;
+    const int tmp = 0x88;
+    u32 ptr;
+    struct snd_emu10k1_fx8010_code *icode = NULL;
+    struct snd_emu10k1_fx8010_control_gpr *controls = NULL, *ctl;
+    u32 *gpr_map;
+    mm_segment_t seg;
+
+    if ((icode = kzalloc(sizeof(*icode), GFP_KERNEL)) == NULL ||
+        (icode->gpr_map = (u_int32_t __user *)
+         kcalloc(512 + 256 + 256 + 2 * 1024, sizeof(u_int32_t),
+                 GFP_KERNEL)) == NULL ||
+        (controls = kcalloc(SND_EMU10K1_GPR_CONTROLS,
+                            sizeof(*controls), GFP_KERNEL)) == NULL) {
+        err = -ENOMEM;
+        goto __err;
+    }
+    gpr_map = (u32 __force *)icode->gpr_map;
+
+    icode->tram_data_map = icode->gpr_map + 512;
+    icode->tram_addr_map = icode->tram_data_map + 256;
+    icode->code = icode->tram_addr_map + 256;
+
+    /* clear free GPRs */
+    for (i = 0; i < 512; i++)
+        set_bit(i, icode->gpr_valid);
+
+    /* clear TRAM data & address lines */
+    for (i = 0; i < 256; i++)
+        set_bit(i, icode->tram_valid);
+
+    strcpy(icode->name, "Audigy DSP code for ALSA");
+    ptr = 0;
+    nctl = 0;
+    gpr = stereo_mix + 10;
+    gpr_map[gpr++] = 0x00007fff;
+    gpr_map[gpr++] = 0x00008000;
+    gpr_map[gpr++] = 0x0000ffff;
+    bit_shifter16 = gpr;
+
+    /* stop FX processor */
+    snd_emu10k1_ptr_write(emu, A_DBG, 0, (emu->fx8010.dbg = 0) | A_DBG_SINGLE_STEP);
+
+#if 1
+    /* PCM front Playback Volume (independent from stereo mix) */
+    A_OP(icode, &ptr, iMAC0, A_GPR(playback), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT_FRONT));
+    A_OP(icode, &ptr, iMAC0, A_GPR(playback+1), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT_FRONT));
+    snd_emu10k1_init_stereo_control(&controls[nctl++], "PCM Front Playback Volume", gpr, 100);
+    gpr += 2;
+
+    /* PCM Surround Playback (independent from stereo mix) */
+    A_OP(icode, &ptr, iMAC0, A_GPR(playback+2), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT_REAR));
+    A_OP(icode, &ptr, iMAC0, A_GPR(playback+3), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT_REAR));
+    snd_emu10k1_init_stereo_control(&controls[nctl++], "PCM Surround Playback Volume", gpr, 100);
+    gpr += 2;
+
+    /* PCM Side Playback (independent from stereo mix) */
+    if (emu->card_capabilities->spk71) {
+        A_OP(icode, &ptr, iMAC0, A_GPR(playback+6), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT_SIDE));
+        A_OP(icode, &ptr, iMAC0, A_GPR(playback+7), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT_SIDE));
+        snd_emu10k1_init_stereo_control(&controls[nctl++], "PCM Side Playback Volume", gpr, 100);
+        gpr += 2;
+    }
+
+    /* PCM Center Playback (independent from stereo mix) */
+    A_OP(icode, &ptr, iMAC0, A_GPR(playback+4), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_CENTER));
+    snd_emu10k1_init_mono_control(&controls[nctl++], "PCM Center Playback Volume", gpr, 100);
+    gpr++;
+
+    /* PCM LFE Playback (independent from stereo mix) */
+    A_OP(icode, &ptr, iMAC0, A_GPR(playback+5), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LFE));
+    snd_emu10k1_init_mono_control(&controls[nctl++], "PCM LFE Playback Volume", gpr, 100);
+    gpr++;
+
+    /*
+     * Stereo Mix
+     */
+    /* Wave (PCM) Playback Volume (will be renamed later) */
+    A_OP(icode, &ptr, iMAC0, A_GPR(stereo_mix), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT));
+    A_OP(icode, &ptr, iMAC0, A_GPR(stereo_mix+1), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT));
+    snd_emu10k1_init_stereo_control(&controls[nctl++], "Wave Playback Volume", gpr, 100);
+    gpr += 2;
+
+    /* Synth Playback */
+    A_OP(icode, &ptr, iMAC0, A_GPR(stereo_mix+0), A_GPR(stereo_mix+0), A_GPR(gpr), A_FXBUS(FXBUS_MIDI_LEFT));
+    A_OP(icode, &ptr, iMAC0, A_GPR(stereo_mix+1), A_GPR(stereo_mix+1), A_GPR(gpr+1), A_FXBUS(FXBUS_MIDI_RIGHT));
+    snd_emu10k1_init_stereo_control(&controls[nctl++], "Synth Playback Volume", gpr, 100);
+    gpr += 2;
+
+    /* Wave (PCM) Capture */
+    A_OP(icode, &ptr, iMAC0, A_GPR(capture+0), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT));
+    A_OP(icode, &ptr, iMAC0, A_GPR(capture+1), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT));
+    snd_emu10k1_init_stereo_control(&controls[nctl++], "PCM Capture Volume", gpr, 0);
+    gpr += 2;
+
+    /* Synth Capture */
+    A_OP(icode, &ptr, iMAC0, A_GPR(capture+0), A_GPR(capture+0), A_GPR(gpr), A_FXBUS(FXBUS_MIDI_LEFT));
+    A_OP(icode, &ptr, iMAC0, A_GPR(capture+1), A_GPR(capture+1), A_GPR(gpr+1), A_FXBUS(FXBUS_MIDI_RIGHT));
+    snd_emu10k1_init_stereo_control(&controls[nctl++], "Synth Capture Volume", gpr, 0);
+    gpr += 2;
+
+    /*
+     * inputs
+     */
+#define A_ADD_VOLUME_IN(var,vol,input) \
+    A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input))
+
+    /* emu1212 DSP 0 and DSP 1 Capture */
+    if (emu->card_capabilities->emu1010) {
+        A_OP(icode, &ptr, iMAC0, A_GPR(capture+0), A_GPR(capture+0), A_GPR(gpr), A_P16VIN(0x0));
+        A_OP(icode, &ptr, iMAC0, A_GPR(capture+1), A_GPR(capture+1), A_GPR(gpr+1), A_P16VIN(0x1));
+        snd_emu10k1_init_stereo_control(&controls[nctl++], "EMU Capture Volume", gpr, 0);
+        gpr += 2;
+    }
+    /* AC'97 Playback Volume - used only for mic (renamed later) */
+    A_ADD_VOLUME_IN(stereo_mix, gpr, A_EXTIN_AC97_L);
+    A_ADD_VOLUME_IN(stereo_mix+1, gpr+1, A_EXTIN_AC97_R);
+    snd_emu10k1_init_stereo_control(&controls[nctl++], "AMic Playback Volume", gpr, 0);
+    gpr += 2;
+    /* AC'97 Capture Volume - used only for mic */
+    A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_AC97_L);
+    A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_AC97_R);
+    snd_emu10k1_init_stereo_control(&controls[nctl++], "Mic Capture Volume", gpr, 0);
+    gpr += 2;
+
+    /* mic capture buffer */
+    A_OP(icode, &ptr, iINTERP, A_EXTOUT(A_EXTOUT_MIC_CAP), A_EXTIN(A_EXTIN_AC97_L), 0xcd, A_EXTIN(A_EXTIN_AC97_R));
+
+    /* Audigy CD Playback Volume */
+    A_ADD_VOLUME_IN(stereo_mix, gpr, A_EXTIN_SPDIF_CD_L);
+    A_ADD_VOLUME_IN(stereo_mix+1, gpr+1, A_EXTIN_SPDIF_CD_R);
+    snd_emu10k1_init_stereo_control(&controls[nctl++],
+                                    emu->card_capabilities->ac97_chip ? "Audigy CD Playback Volume" : "CD Playback Volume",
+                                    gpr, 0);
+    gpr += 2;
+    /* Audigy CD Capture Volume */
+    A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_SPDIF_CD_L);
+    A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_SPDIF_CD_R);
+    snd_emu10k1_init_stereo_control(&controls[nctl++],
+                                    emu->card_capabilities->ac97_chip ? "Audigy CD Capture Volume" : "CD Capture Volume",
+                                    gpr, 0);
+    gpr += 2;
+
+    /* Optical SPDIF Playback Volume */
+    A_ADD_VOLUME_IN(stereo_mix, gpr, A_EXTIN_OPT_SPDIF_L);
+    A_ADD_VOLUME_IN(stereo_mix+1, gpr+1, A_EXTIN_OPT_SPDIF_R);
+    snd_emu10k1_init_stereo_control(&controls[nctl++], SNDRV_CTL_NAME_IEC958("Optical ",PLAYBACK,VOLUME), gpr, 0);
+    gpr += 2;
+    /* Optical SPDIF Capture Volume */
+    A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_OPT_SPDIF_L);
+    A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_OPT_SPDIF_R);
+    snd_emu10k1_init_stereo_control(&controls[nctl++], SNDRV_CTL_NAME_IEC958("Optical ",CAPTURE,VOLUME), gpr, 0);
+    gpr += 2;
+
+    /* Line2 Playback Volume */
+    A_ADD_VOLUME_IN(stereo_mix, gpr, A_EXTIN_LINE2_L);
+    A_ADD_VOLUME_IN(stereo_mix+1, gpr+1, A_EXTIN_LINE2_R);
+    snd_emu10k1_init_stereo_control(&controls[nctl++],
+                                    emu->card_capabilities->ac97_chip ? "Line2 Playback Volume" : "Line Playback Volume",
+                                    gpr, 0);
+    gpr += 2;
+    /* Line2 Capture Volume */
+    A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_LINE2_L);
+    A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_LINE2_R);
+    snd_emu10k1_init_stereo_control(&controls[nctl++],
+                                    emu->card_capabilities->ac97_chip ? "Line2 Capture Volume" : "Line Capture Volume",
+                                    gpr, 0);
+    gpr += 2;
+
+    /* Philips ADC Playback Volume */
+    A_ADD_VOLUME_IN(stereo_mix, gpr, A_EXTIN_ADC_L);
+    A_ADD_VOLUME_IN(stereo_mix+1, gpr+1, A_EXTIN_ADC_R);
+    snd_emu10k1_init_stereo_control(&controls[nctl++], "Analog Mix Playback Volume", gpr, 0);
+    gpr += 2;
+    /* Philips ADC Capture Volume */
+    A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_ADC_L);
+    A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_ADC_R);
+    snd_emu10k1_init_stereo_control(&controls[nctl++], "Analog Mix Capture Volume", gpr, 0);
+    gpr += 2;
+
+    /* Aux2 Playback Volume */
+    A_ADD_VOLUME_IN(stereo_mix, gpr, A_EXTIN_AUX2_L);
+    A_ADD_VOLUME_IN(stereo_mix+1, gpr+1, A_EXTIN_AUX2_R);
+    snd_emu10k1_init_stereo_control(&controls[nctl++],
+                                    emu->card_capabilities->ac97_chip ? "Aux2 Playback Volume" : "Aux Playback Volume",
+                                    gpr, 0);
+    gpr += 2;
+    /* Aux2 Capture Volume */
+    A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_AUX2_L);
+    A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_AUX2_R);
+    snd_emu10k1_init_stereo_control(&controls[nctl++],
+                                    emu->card_capabilities->ac97_chip ? "Aux2 Capture Volume" : "Aux Capture Volume",
+                                    gpr, 0);
+    gpr += 2;
+
+    /* Stereo Mix Front Playback Volume */
+    A_OP(icode, &ptr, iMAC0, A_GPR(playback), A_GPR(playback), A_GPR(gpr), A_GPR(stereo_mix));
+    A_OP(icode, &ptr, iMAC0, A_GPR(playback+1), A_GPR(playback+1), A_GPR(gpr+1), A_GPR(stereo_mix+1));
+    snd_emu10k1_init_stereo_control(&controls[nctl++], "Front Playback Volume", gpr, 100);
+    gpr += 2;
+
+    /* Stereo Mix Surround Playback */
+    A_OP(icode, &ptr, iMAC0, A_GPR(playback+2), A_GPR(playback+2), A_GPR(gpr), A_GPR(stereo_mix));
+    A_OP(icode, &ptr, iMAC0, A_GPR(playback+3), A_GPR(playback+3), A_GPR(gpr+1), A_GPR(stereo_mix+1));
+    snd_emu10k1_init_stereo_control(&controls[nctl++], "Surround Playback Volume", gpr, 0);
+    gpr += 2;
+
+    /* Stereo Mix Center Playback */
+    /* Center = sub = Left/2 + Right/2 */
+    A_OP(icode, &ptr, iINTERP, A_GPR(tmp), A_GPR(stereo_mix), 0xcd, A_GPR(stereo_mix+1));
+    A_OP(icode, &ptr, iMAC0, A_GPR(playback+4), A_GPR(playback+4), A_GPR(gpr), A_GPR(tmp));
+    snd_emu10k1_init_mono_control(&controls[nctl++], "Center Playback Volume", gpr, 0);
+    gpr++;
+
+    /* Stereo Mix LFE Playback */
+    A_OP(icode, &ptr, iMAC0, A_GPR(playback+5), A_GPR(playback+5), A_GPR(gpr), A_GPR(tmp));
+    snd_emu10k1_init_mono_control(&controls[nctl++], "LFE Playback Volume", gpr, 0);
+    gpr++;
+
+    if (emu->card_capabilities->spk71) {
+        /* Stereo Mix Side Playback */
+        A_OP(icode, &ptr, iMAC0, A_GPR(playback+6), A_GPR(playback+6), A_GPR(gpr), A_GPR(stereo_mix));
+        A_OP(icode, &ptr, iMAC0, A_GPR(playback+7), A_GPR(playback+7), A_GPR(gpr+1), A_GPR(stereo_mix+1));
+        snd_emu10k1_init_stereo_control(&controls[nctl++], "Side Playback Volume", gpr, 0);
+        gpr += 2;
+    }
+
+    /*
+     * outputs
+     */
+#define A_PUT_OUTPUT(out,src) A_OP(icode, &ptr, iACC3, A_EXTOUT(out), A_C_00000000, A_C_00000000, A_GPR(src))
+#define A_PUT_STEREO_OUTPUT(out1,out2,src) \
+    {A_PUT_OUTPUT(out1,src); A_PUT_OUTPUT(out2,src+1);}
+
+#define _A_SWITCH(icode, ptr, dst, src, sw) \
+    A_OP((icode), ptr, iMACINT0, dst, A_C_00000000, src, sw);
+#define A_SWITCH(icode, ptr, dst, src, sw) \
+    _A_SWITCH(icode, ptr, A_GPR(dst), A_GPR(src), A_GPR(sw))
+#define _A_SWITCH_NEG(icode, ptr, dst, src) \
+    A_OP((icode), ptr, iANDXOR, dst, src, A_C_00000001, A_C_00000001);
+#define A_SWITCH_NEG(icode, ptr, dst, src) \
+    _A_SWITCH_NEG(icode, ptr, A_GPR(dst), A_GPR(src))
+
+
+    /*
+     *  Process tone control
+     */
+    A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 0), A_GPR(playback + 0), A_C_00000000, A_C_00000000); /* left */
+    A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 1), A_GPR(playback + 1), A_C_00000000, A_C_00000000); /* right */
+    A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 2), A_GPR(playback + 2), A_C_00000000, A_C_00000000); /* rear left */
+    A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 3), A_GPR(playback + 3), A_C_00000000, A_C_00000000); /* rear right */
+    A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 4), A_GPR(playback + 4), A_C_00000000, A_C_00000000); /* center */
+    A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 5), A_GPR(playback + 5), A_C_00000000, A_C_00000000); /* LFE */
+    if (emu->card_capabilities->spk71) {
+        A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 6), A_GPR(playback + 6), A_C_00000000, A_C_00000000); /* side left */
+        A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 7), A_GPR(playback + 7), A_C_00000000, A_C_00000000); /* side right */
+    }
+
+
+    ctl = &controls[nctl + 0];
+    ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+    strcpy(ctl->id.name, "Tone Control - Bass");
+    ctl->vcount = 2;
+    ctl->count = 10;
+    ctl->min = 0;
+    ctl->max = 40;
+    ctl->value[0] = ctl->value[1] = 20;
+    ctl->translation = EMU10K1_GPR_TRANSLATION_BASS;
+    ctl = &controls[nctl + 1];
+    ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+    strcpy(ctl->id.name, "Tone Control - Treble");
+    ctl->vcount = 2;
+    ctl->count = 10;
+    ctl->min = 0;
+    ctl->max = 40;
+    ctl->value[0] = ctl->value[1] = 20;
+    ctl->translation = EMU10K1_GPR_TRANSLATION_TREBLE;
+
+#define BASS_GPR	0x8c
+#define TREBLE_GPR	0x96
+
+    for (z = 0; z < 5; z++) {
+        int j;
+        for (j = 0; j < 2; j++) {
+            controls[nctl + 0].gpr[z * 2 + j] = BASS_GPR + z * 2 + j;
+            controls[nctl + 1].gpr[z * 2 + j] = TREBLE_GPR + z * 2 + j;
+        }
+    }
+    for (z = 0; z < 4; z++) {               /* front/rear/center-lfe/side */
+        int j, k, l, d;
+        for (j = 0; j < 2; j++) {	/* left/right */
+            k = 0xb0 + (z * 8) + (j * 4);
+            l = 0xe0 + (z * 8) + (j * 4);
+            d = playback + SND_EMU10K1_PLAYBACK_CHANNELS + z * 2 + j;
+
+            A_OP(icode, &ptr, iMAC0, A_C_00000000, A_C_00000000, A_GPR(d), A_GPR(BASS_GPR + 0 + j));
+            A_OP(icode, &ptr, iMACMV, A_GPR(k+1), A_GPR(k), A_GPR(k+1), A_GPR(BASS_GPR + 4 + j));
+            A_OP(icode, &ptr, iMACMV, A_GPR(k), A_GPR(d), A_GPR(k), A_GPR(BASS_GPR + 2 + j));
+            A_OP(icode, &ptr, iMACMV, A_GPR(k+3), A_GPR(k+2), A_GPR(k+3), A_GPR(BASS_GPR + 8 + j));
+            A_OP(icode, &ptr, iMAC0, A_GPR(k+2), A_GPR_ACCU, A_GPR(k+2), A_GPR(BASS_GPR + 6 + j));
+            A_OP(icode, &ptr, iACC3, A_GPR(k+2), A_GPR(k+2), A_GPR(k+2), A_C_00000000);
+
+            A_OP(icode, &ptr, iMAC0, A_C_00000000, A_C_00000000, A_GPR(k+2), A_GPR(TREBLE_GPR + 0 + j));
+            A_OP(icode, &ptr, iMACMV, A_GPR(l+1), A_GPR(l), A_GPR(l+1), A_GPR(TREBLE_GPR + 4 + j));
+            A_OP(icode, &ptr, iMACMV, A_GPR(l), A_GPR(k+2), A_GPR(l), A_GPR(TREBLE_GPR + 2 + j));
+            A_OP(icode, &ptr, iMACMV, A_GPR(l+3), A_GPR(l+2), A_GPR(l+3), A_GPR(TREBLE_GPR + 8 + j));
+            A_OP(icode, &ptr, iMAC0, A_GPR(l+2), A_GPR_ACCU, A_GPR(l+2), A_GPR(TREBLE_GPR + 6 + j));
+            A_OP(icode, &ptr, iMACINT0, A_GPR(l+2), A_C_00000000, A_GPR(l+2), A_C_00000010);
+
+            A_OP(icode, &ptr, iACC3, A_GPR(d), A_GPR(l+2), A_C_00000000, A_C_00000000);
+
+            if (z == 2)	/* center */
+                break;
+        }
+    }
+    nctl += 2;
+
+#undef BASS_GPR
+#undef TREBLE_GPR
+
+    for (z = 0; z < 8; z++) {
+        A_SWITCH(icode, &ptr, tmp + 0, playback + SND_EMU10K1_PLAYBACK_CHANNELS + z, gpr + 0);
+        A_SWITCH_NEG(icode, &ptr, tmp + 1, gpr + 0);
+        A_SWITCH(icode, &ptr, tmp + 1, playback + z, tmp + 1);
+        A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + z), A_GPR(tmp + 0), A_GPR(tmp + 1), A_C_00000000);
+    }
+    snd_emu10k1_init_stereo_onoff_control(controls + nctl++, "Tone Control - Switch", gpr, 0);
+    gpr += 2;
+
+    /* Master volume (will be renamed later) */
+    A_OP(icode, &ptr, iMAC0, A_GPR(playback+0+SND_EMU10K1_PLAYBACK_CHANNELS), A_C_00000000, A_GPR(gpr), A_GPR(playback+0+SND_EMU10K1_PLAYBACK_CHANNELS));
+    A_OP(icode, &ptr, iMAC0, A_GPR(playback+1+SND_EMU10K1_PLAYBACK_CHANNELS), A_C_00000000, A_GPR(gpr), A_GPR(playback+1+SND_EMU10K1_PLAYBACK_CHANNELS));
+    A_OP(icode, &ptr, iMAC0, A_GPR(playback+2+SND_EMU10K1_PLAYBACK_CHANNELS), A_C_00000000, A_GPR(gpr), A_GPR(playback+2+SND_EMU10K1_PLAYBACK_CHANNELS));
+    A_OP(icode, &ptr, iMAC0, A_GPR(playback+3+SND_EMU10K1_PLAYBACK_CHANNELS), A_C_00000000, A_GPR(gpr), A_GPR(playback+3+SND_EMU10K1_PLAYBACK_CHANNELS));
+    A_OP(icode, &ptr, iMAC0, A_GPR(playback+4+SND_EMU10K1_PLAYBACK_CHANNELS), A_C_00000000, A_GPR(gpr), A_GPR(playback+4+SND_EMU10K1_PLAYBACK_CHANNELS));
+    A_OP(icode, &ptr, iMAC0, A_GPR(playback+5+SND_EMU10K1_PLAYBACK_CHANNELS), A_C_00000000, A_GPR(gpr), A_GPR(playback+5+SND_EMU10K1_PLAYBACK_CHANNELS));
+    A_OP(icode, &ptr, iMAC0, A_GPR(playback+6+SND_EMU10K1_PLAYBACK_CHANNELS), A_C_00000000, A_GPR(gpr), A_GPR(playback+6+SND_EMU10K1_PLAYBACK_CHANNELS));
+    A_OP(icode, &ptr, iMAC0, A_GPR(playback+7+SND_EMU10K1_PLAYBACK_CHANNELS), A_C_00000000, A_GPR(gpr), A_GPR(playback+7+SND_EMU10K1_PLAYBACK_CHANNELS));
+    snd_emu10k1_init_mono_control(&controls[nctl++], "Wave Master Playback Volume", gpr, 0);
+    gpr += 2;
+
+    /* analog speakers */
+    A_PUT_STEREO_OUTPUT(A_EXTOUT_AFRONT_L, A_EXTOUT_AFRONT_R, playback + SND_EMU10K1_PLAYBACK_CHANNELS);
+    A_PUT_STEREO_OUTPUT(A_EXTOUT_AREAR_L, A_EXTOUT_AREAR_R, playback+2 + SND_EMU10K1_PLAYBACK_CHANNELS);
+    A_PUT_OUTPUT(A_EXTOUT_ACENTER, playback+4 + SND_EMU10K1_PLAYBACK_CHANNELS);
+    A_PUT_OUTPUT(A_EXTOUT_ALFE, playback+5 + SND_EMU10K1_PLAYBACK_CHANNELS);
+    if (emu->card_capabilities->spk71)
+        A_PUT_STEREO_OUTPUT(A_EXTOUT_ASIDE_L, A_EXTOUT_ASIDE_R, playback+6 + SND_EMU10K1_PLAYBACK_CHANNELS);
+
+    /* headphone */
+    A_PUT_STEREO_OUTPUT(A_EXTOUT_HEADPHONE_L, A_EXTOUT_HEADPHONE_R, playback + SND_EMU10K1_PLAYBACK_CHANNELS);
+
+    /* digital outputs */
+    /* A_PUT_STEREO_OUTPUT(A_EXTOUT_FRONT_L, A_EXTOUT_FRONT_R, playback + SND_EMU10K1_PLAYBACK_CHANNELS); */
+    if (emu->card_capabilities->emu1010) {
+        /* EMU1010 Outputs from PCM Front, Rear, Center, LFE, Side */
+        snd_printk("EMU outputs on\n");
+        for (z = 0; z < 8; z++) {
+            A_OP(icode, &ptr, iACC3, A_EMU32OUTL(z), A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + z), A_C_00000000, A_C_00000000);
+        }
+    }
+
+    /* IEC958 Optical Raw Playback Switch */
+    gpr_map[gpr++] = 0;
+    gpr_map[gpr++] = 0x1008;
+    gpr_map[gpr++] = 0xffff0000;
+    for (z = 0; z < 2; z++) {
+        A_OP(icode, &ptr, iMAC0, A_GPR(tmp + 2), A_FXBUS(FXBUS_PT_LEFT + z), A_C_00000000, A_C_00000000);
+        A_OP(icode, &ptr, iSKIP, A_GPR_COND, A_GPR_COND, A_GPR(gpr - 2), A_C_00000001);
+        A_OP(icode, &ptr, iACC3, A_GPR(tmp + 2), A_C_00000000, A_C_00010000, A_GPR(tmp + 2));
+        A_OP(icode, &ptr, iANDXOR, A_GPR(tmp + 2), A_GPR(tmp + 2), A_GPR(gpr - 1), A_C_00000000);
+        A_SWITCH(icode, &ptr, tmp + 0, tmp + 2, gpr + z);
+        A_SWITCH_NEG(icode, &ptr, tmp + 1, gpr + z);
+        A_SWITCH(icode, &ptr, tmp + 1, playback + SND_EMU10K1_PLAYBACK_CHANNELS + z, tmp + 1);
+        if ((z==1) && (emu->card_capabilities->spdif_bug)) {
+            /* Due to a SPDIF output bug on some Audigy cards, this code delays the Right channel by 1 sample */
+            snd_printk("Installing spdif_bug patch: %s\n", emu->card_capabilities->name);
+            A_OP(icode, &ptr, iACC3, A_EXTOUT(A_EXTOUT_FRONT_L + z), A_GPR(gpr - 3), A_C_00000000, A_C_00000000);
+            A_OP(icode, &ptr, iACC3, A_GPR(gpr - 3), A_GPR(tmp + 0), A_GPR(tmp + 1), A_C_00000000);
+        } else {
+            A_OP(icode, &ptr, iACC3, A_EXTOUT(A_EXTOUT_FRONT_L + z), A_GPR(tmp + 0), A_GPR(tmp + 1), A_C_00000000);
+        }
+    }
+    snd_emu10k1_init_stereo_onoff_control(controls + nctl++, SNDRV_CTL_NAME_IEC958("Optical Raw ",PLAYBACK,SWITCH), gpr, 0);
+    gpr += 2;
+
+    A_PUT_STEREO_OUTPUT(A_EXTOUT_REAR_L, A_EXTOUT_REAR_R, playback+2 + SND_EMU10K1_PLAYBACK_CHANNELS);
+    A_PUT_OUTPUT(A_EXTOUT_CENTER, playback+4 + SND_EMU10K1_PLAYBACK_CHANNELS);
+    A_PUT_OUTPUT(A_EXTOUT_LFE, playback+5 + SND_EMU10K1_PLAYBACK_CHANNELS);
+
+    /* ADC buffer */
+#ifdef EMU10K1_CAPTURE_DIGITAL_OUT
+    A_PUT_STEREO_OUTPUT(A_EXTOUT_ADC_CAP_L, A_EXTOUT_ADC_CAP_R, playback + SND_EMU10K1_PLAYBACK_CHANNELS);
+#else
+    A_PUT_OUTPUT(A_EXTOUT_ADC_CAP_L, capture);
+    A_PUT_OUTPUT(A_EXTOUT_ADC_CAP_R, capture+1);
+#endif
+
+    if (emu->card_capabilities->emu1010) {
+        snd_printk("EMU inputs on\n");
+        /* Capture 8 channels of S32_LE sound */
+
+        /* printk("emufx.c: gpr=0x%x, tmp=0x%x\n",gpr, tmp); */
+        /* For the EMU1010: How to get 32bit values from the DSP. High 16bits into L, low 16bits into R. */
+        /* A_P16VIN(0) is delayed by one sample,
+         * so all other A_P16VIN channels will need to also be delayed
+         */
+        /* Left ADC in. 1 of 2 */
+        snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_P16VIN(0x0), A_FXBUS2(0) );
+        /* Right ADC in 1 of 2 */
+        gpr_map[gpr++] = 0x00000000;
+        snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(2) );
+        A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x1), A_C_00000000, A_C_00000000);
+        gpr_map[gpr++] = 0x00000000;
+        snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(4) );
+        A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x2), A_C_00000000, A_C_00000000);
+        gpr_map[gpr++] = 0x00000000;
+        snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(6) );
+        A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x3), A_C_00000000, A_C_00000000);
+        /* For 96kHz mode */
+        /* Left ADC in. 2 of 2 */
+        gpr_map[gpr++] = 0x00000000;
+        snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(0x8) );
+        A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x4), A_C_00000000, A_C_00000000);
+        /* Right ADC in 2 of 2 */
+        gpr_map[gpr++] = 0x00000000;
+        snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(0xa) );
+        A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x5), A_C_00000000, A_C_00000000);
+        gpr_map[gpr++] = 0x00000000;
+        snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(0xc) );
+        A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x6), A_C_00000000, A_C_00000000);
+        gpr_map[gpr++] = 0x00000000;
+        snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(0xe) );
+        A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x7), A_C_00000000, A_C_00000000);
+
+#if 0
+        for (z = 4; z < 8; z++) {
+            A_OP(icode, &ptr, iACC3, A_FXBUS2(z), A_C_00000000, A_C_00000000, A_C_00000000);
+        }
+        for (z = 0xc; z < 0x10; z++) {
+            A_OP(icode, &ptr, iACC3, A_FXBUS2(z), A_C_00000000, A_C_00000000, A_C_00000000);
+        }
+#endif
+    } else {
+
+        /* EFX capture - capture the 16 EXTINs */
+        /* Capture 16 channels of S16_LE sound */
+        for (z = 0; z < 16; z++) {
+            A_OP(icode, &ptr, iACC3, A_FXBUS2(z), A_C_00000000, A_C_00000000, A_EXTIN(z));
+        }
+    }
+
+#endif /* JCD test */
+    /*
+     * ok, set up done..
+     */
+
+    if (gpr > tmp) {
+        snd_BUG();
+        err = -EIO;
+        goto __err;
+    }
+    /* clear remaining instruction memory */
+    while (ptr < 0x400)
+        A_OP(icode, &ptr, 0x0f, 0xc0, 0xc0, 0xcf, 0xc0);
+
+    seg = snd_enter_user();
+    icode->gpr_add_control_count = nctl;
+    icode->gpr_add_controls = (struct snd_emu10k1_fx8010_control_gpr __user *)controls;
+    err = snd_emu10k1_icode_poke(emu, icode);
+    snd_leave_user(seg);
+
+__err:
+    kfree(controls);
+    if (icode != NULL) {
+        kfree((void __force *)icode->gpr_map);
+        kfree(icode);
+    }
+    return err;
+}
+
+
+/*
+ * initial DSP configuration for Emu10k1
+ */
+
+/* when volume = max, then copy only to avoid volume modification */
+/* with iMAC0 (negative values) */
+static void __devinit _volume(struct snd_emu10k1_fx8010_code *icode, u32 *ptr, u32 dst, u32 src, u32 vol)
+{
+    OP(icode, ptr, iMAC0, dst, C_00000000, src, vol);
+    OP(icode, ptr, iANDXOR, C_00000000, vol, C_ffffffff, C_7fffffff);
+    OP(icode, ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_NONZERO, C_00000001);
+    OP(icode, ptr, iACC3, dst, src, C_00000000, C_00000000);
+}
+static void __devinit _volume_add(struct snd_emu10k1_fx8010_code *icode, u32 *ptr, u32 dst, u32 src, u32 vol)
+{
+    OP(icode, ptr, iANDXOR, C_00000000, vol, C_ffffffff, C_7fffffff);
+    OP(icode, ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_NONZERO, C_00000002);
+    OP(icode, ptr, iMACINT0, dst, dst, src, C_00000001);
+    OP(icode, ptr, iSKIP, C_00000000, C_7fffffff, C_7fffffff, C_00000001);
+    OP(icode, ptr, iMAC0, dst, dst, src, vol);
+}
+static void __devinit _volume_out(struct snd_emu10k1_fx8010_code *icode, u32 *ptr, u32 dst, u32 src, u32 vol)
+{
+    OP(icode, ptr, iANDXOR, C_00000000, vol, C_ffffffff, C_7fffffff);
+    OP(icode, ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_NONZERO, C_00000002);
+    OP(icode, ptr, iACC3, dst, src, C_00000000, C_00000000);
+    OP(icode, ptr, iSKIP, C_00000000, C_7fffffff, C_7fffffff, C_00000001);
+    OP(icode, ptr, iMAC0, dst, C_00000000, src, vol);
+}
+
+#define VOLUME(icode, ptr, dst, src, vol) \
+    _volume(icode, ptr, GPR(dst), GPR(src), GPR(vol))
+#define VOLUME_IN(icode, ptr, dst, src, vol) \
+    _volume(icode, ptr, GPR(dst), EXTIN(src), GPR(vol))
+#define VOLUME_ADD(icode, ptr, dst, src, vol) \
+    _volume_add(icode, ptr, GPR(dst), GPR(src), GPR(vol))
+#define VOLUME_ADDIN(icode, ptr, dst, src, vol) \
+    _volume_add(icode, ptr, GPR(dst), EXTIN(src), GPR(vol))
+#define VOLUME_OUT(icode, ptr, dst, src, vol) \
+    _volume_out(icode, ptr, EXTOUT(dst), GPR(src), GPR(vol))
+#define _SWITCH(icode, ptr, dst, src, sw) \
+    OP((icode), ptr, iMACINT0, dst, C_00000000, src, sw);
+#define SWITCH(icode, ptr, dst, src, sw) \
+    _SWITCH(icode, ptr, GPR(dst), GPR(src), GPR(sw))
+#define SWITCH_IN(icode, ptr, dst, src, sw) \
+    _SWITCH(icode, ptr, GPR(dst), EXTIN(src), GPR(sw))
+#define _SWITCH_NEG(icode, ptr, dst, src) \
+    OP((icode), ptr, iANDXOR, dst, src, C_00000001, C_00000001);
+#define SWITCH_NEG(icode, ptr, dst, src) \
+    _SWITCH_NEG(icode, ptr, GPR(dst), GPR(src))
+
+
+static int __devinit _snd_emu10k1_init_efx(struct snd_emu10k1 *emu)
+{
+    int err, i, z, gpr, tmp, playback, capture;
+    u32 ptr;
+    struct snd_emu10k1_fx8010_code *icode;
+    struct snd_emu10k1_fx8010_pcm_rec *ipcm = NULL;
+    struct snd_emu10k1_fx8010_control_gpr *controls = NULL, *ctl;
+    u32 *gpr_map;
+    mm_segment_t seg;
+
+    if ((icode = kzalloc(sizeof(*icode), GFP_KERNEL)) == NULL)
+        return -ENOMEM;
+    if ((icode->gpr_map = (u_int32_t __user *)
+         kcalloc(256 + 160 + 160 + 2 * 512, sizeof(u_int32_t),
+                 GFP_KERNEL)) == NULL ||
+        (controls = kcalloc(SND_EMU10K1_GPR_CONTROLS,
+                            sizeof(struct snd_emu10k1_fx8010_control_gpr),
+                            GFP_KERNEL)) == NULL ||
+        (ipcm = kzalloc(sizeof(*ipcm), GFP_KERNEL)) == NULL) {
+        err = -ENOMEM;
+        goto __err;
+    }
+    gpr_map = (u32 __force *)icode->gpr_map;
+
+    icode->tram_data_map = icode->gpr_map + 256;
+    icode->tram_addr_map = icode->tram_data_map + 160;
+    icode->code = icode->tram_addr_map + 160;
+
+    /* clear free GPRs */
+    for (i = 0; i < 256; i++)
+        set_bit(i, icode->gpr_valid);
+
+    /* clear TRAM data & address lines */
+    for (i = 0; i < 160; i++)
+        set_bit(i, icode->tram_valid);
+
+    strcpy(icode->name, "SB Live! FX8010 code for ALSA v1.2 by Jaroslav Kysela");
+    ptr = 0; i = 0;
+    /* we have 12 inputs */
+    playback = SND_EMU10K1_INPUTS;
+    /* we have 6 playback channels and tone control doubles */
+    capture = playback + (SND_EMU10K1_PLAYBACK_CHANNELS * 2);
+    gpr = capture + SND_EMU10K1_CAPTURE_CHANNELS;
+    tmp = 0x88;	/* we need 4 temporary GPR */
+    /* from 0x8c to 0xff is the area for tone control */
+
+    /* stop FX processor */
+    snd_emu10k1_ptr_write(emu, DBG, 0, (emu->fx8010.dbg = 0) | EMU10K1_DBG_SINGLE_STEP);
+
+    /*
+     *  Process FX Buses
+     */
+    OP(icode, &ptr, iMACINT0, GPR(0), C_00000000, FXBUS(FXBUS_PCM_LEFT), C_00000004);
+    OP(icode, &ptr, iMACINT0, GPR(1), C_00000000, FXBUS(FXBUS_PCM_RIGHT), C_00000004);
+    OP(icode, &ptr, iMACINT0, GPR(2), C_00000000, FXBUS(FXBUS_MIDI_LEFT), C_00000004);
+    OP(icode, &ptr, iMACINT0, GPR(3), C_00000000, FXBUS(FXBUS_MIDI_RIGHT), C_00000004);
+    OP(icode, &ptr, iMACINT0, GPR(4), C_00000000, FXBUS(FXBUS_PCM_LEFT_REAR), C_00000004);
+    OP(icode, &ptr, iMACINT0, GPR(5), C_00000000, FXBUS(FXBUS_PCM_RIGHT_REAR), C_00000004);
+    OP(icode, &ptr, iMACINT0, GPR(6), C_00000000, FXBUS(FXBUS_PCM_CENTER), C_00000004);
+    OP(icode, &ptr, iMACINT0, GPR(7), C_00000000, FXBUS(FXBUS_PCM_LFE), C_00000004);
+    OP(icode, &ptr, iMACINT0, GPR(8), C_00000000, C_00000000, C_00000000);	/* S/PDIF left */
+    OP(icode, &ptr, iMACINT0, GPR(9), C_00000000, C_00000000, C_00000000);	/* S/PDIF right */
+    OP(icode, &ptr, iMACINT0, GPR(10), C_00000000, FXBUS(FXBUS_PCM_LEFT_FRONT), C_00000004);
+    OP(icode, &ptr, iMACINT0, GPR(11), C_00000000, FXBUS(FXBUS_PCM_RIGHT_FRONT), C_00000004);
+
+    /* Raw S/PDIF PCM */
+    ipcm->substream = 0;
+    ipcm->channels = 2;
+    ipcm->tram_start = 0;
+    ipcm->buffer_size = (64 * 1024) / 2;
+    ipcm->gpr_size = gpr++;
+    ipcm->gpr_ptr = gpr++;
+    ipcm->gpr_count = gpr++;
+    ipcm->gpr_tmpcount = gpr++;
+    ipcm->gpr_trigger = gpr++;
+    ipcm->gpr_running = gpr++;
+    ipcm->etram[0] = 0;
+    ipcm->etram[1] = 1;
+
+    gpr_map[gpr + 0] = 0xfffff000;
+    gpr_map[gpr + 1] = 0xffff0000;
+    gpr_map[gpr + 2] = 0x70000000;
+    gpr_map[gpr + 3] = 0x00000007;
+    gpr_map[gpr + 4] = 0x001f << 11;
+    gpr_map[gpr + 5] = 0x001c << 11;
+    gpr_map[gpr + 6] = (0x22  - 0x01) - 1;	/* skip at 01 to 22 */
+    gpr_map[gpr + 7] = (0x22  - 0x06) - 1;	/* skip at 06 to 22 */
+    gpr_map[gpr + 8] = 0x2000000 + (2<<11);
+    gpr_map[gpr + 9] = 0x4000000 + (2<<11);
+    gpr_map[gpr + 10] = 1<<11;
+    gpr_map[gpr + 11] = (0x24 - 0x0a) - 1;	/* skip at 0a to 24 */
+    gpr_map[gpr + 12] = 0;
+
+    /* if the trigger flag is not set, skip */
+    /* 00: */ OP(icode, &ptr, iMAC0, C_00000000, GPR(ipcm->gpr_trigger), C_00000000, C_00000000);
+    /* 01: */ OP(icode, &ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_ZERO, GPR(gpr + 6));
+    /* if the running flag is set, we're running */
+    /* 02: */ OP(icode, &ptr, iMAC0, C_00000000, GPR(ipcm->gpr_running), C_00000000, C_00000000);
+    /* 03: */ OP(icode, &ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_NONZERO, C_00000004);
+    /* wait until ((GPR_DBAC>>11) & 0x1f) == 0x1c) */
+    /* 04: */ OP(icode, &ptr, iANDXOR, GPR(tmp + 0), GPR_DBAC, GPR(gpr + 4), C_00000000);
+    /* 05: */ OP(icode, &ptr, iMACINT0, C_00000000, GPR(tmp + 0), C_ffffffff, GPR(gpr + 5));
+    /* 06: */ OP(icode, &ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_NONZERO, GPR(gpr + 7));
+    /* 07: */ OP(icode, &ptr, iACC3, GPR(gpr + 12), C_00000010, C_00000001, C_00000000);
+
+    /* 08: */ OP(icode, &ptr, iANDXOR, GPR(ipcm->gpr_running), GPR(ipcm->gpr_running), C_00000000, C_00000001);
+    /* 09: */ OP(icode, &ptr, iACC3, GPR(gpr + 12), GPR(gpr + 12), C_ffffffff, C_00000000);
+    /* 0a: */ OP(icode, &ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_NONZERO, GPR(gpr + 11));
+    /* 0b: */ OP(icode, &ptr, iACC3, GPR(gpr + 12), C_00000001, C_00000000, C_00000000);
+
+    /* 0c: */ OP(icode, &ptr, iANDXOR, GPR(tmp + 0), ETRAM_DATA(ipcm->etram[0]), GPR(gpr + 0), C_00000000);
+    /* 0d: */ OP(icode, &ptr, iLOG, GPR(tmp + 0), GPR(tmp + 0), GPR(gpr + 3), C_00000000);
+    /* 0e: */ OP(icode, &ptr, iANDXOR, GPR(8), GPR(tmp + 0), GPR(gpr + 1), GPR(gpr + 2));
+    /* 0f: */ OP(icode, &ptr, iSKIP, C_00000000, GPR_COND, CC_REG_MINUS, C_00000001);
+    /* 10: */ OP(icode, &ptr, iANDXOR, GPR(8), GPR(8), GPR(gpr + 1), GPR(gpr + 2));
+
+    /* 11: */ OP(icode, &ptr, iANDXOR, GPR(tmp + 0), ETRAM_DATA(ipcm->etram[1]), GPR(gpr + 0), C_00000000);
+    /* 12: */ OP(icode, &ptr, iLOG, GPR(tmp + 0), GPR(tmp + 0), GPR(gpr + 3), C_00000000);
+    /* 13: */ OP(icode, &ptr, iANDXOR, GPR(9), GPR(tmp + 0), GPR(gpr + 1), GPR(gpr + 2));
+    /* 14: */ OP(icode, &ptr, iSKIP, C_00000000, GPR_COND, CC_REG_MINUS, C_00000001);
+    /* 15: */ OP(icode, &ptr, iANDXOR, GPR(9), GPR(9), GPR(gpr + 1), GPR(gpr + 2));
+
+    /* 16: */ OP(icode, &ptr, iACC3, GPR(tmp + 0), GPR(ipcm->gpr_ptr), C_00000001, C_00000000);
+    /* 17: */ OP(icode, &ptr, iMACINT0, C_00000000, GPR(tmp + 0), C_ffffffff, GPR(ipcm->gpr_size));
+    /* 18: */ OP(icode, &ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_MINUS, C_00000001);
+    /* 19: */ OP(icode, &ptr, iACC3, GPR(tmp + 0), C_00000000, C_00000000, C_00000000);
+    /* 1a: */ OP(icode, &ptr, iACC3, GPR(ipcm->gpr_ptr), GPR(tmp + 0), C_00000000, C_00000000);
+
+    /* 1b: */ OP(icode, &ptr, iACC3, GPR(ipcm->gpr_tmpcount), GPR(ipcm->gpr_tmpcount), C_ffffffff, C_00000000);
+    /* 1c: */ OP(icode, &ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_NONZERO, C_00000002);
+    /* 1d: */ OP(icode, &ptr, iACC3, GPR(ipcm->gpr_tmpcount), GPR(ipcm->gpr_count), C_00000000, C_00000000);
+    /* 1e: */ OP(icode, &ptr, iACC3, GPR_IRQ, C_80000000, C_00000000, C_00000000);
+    /* 1f: */ OP(icode, &ptr, iANDXOR, GPR(ipcm->gpr_running), GPR(ipcm->gpr_running), C_00000001, C_00010000);
+
+    /* 20: */ OP(icode, &ptr, iANDXOR, GPR(ipcm->gpr_running), GPR(ipcm->gpr_running), C_00010000, C_00000001);
+    /* 21: */ OP(icode, &ptr, iSKIP, C_00000000, C_7fffffff, C_7fffffff, C_00000002);
+
+    /* 22: */ OP(icode, &ptr, iMACINT1, ETRAM_ADDR(ipcm->etram[0]), GPR(gpr + 8), GPR_DBAC, C_ffffffff);
+    /* 23: */ OP(icode, &ptr, iMACINT1, ETRAM_ADDR(ipcm->etram[1]), GPR(gpr + 9), GPR_DBAC, C_ffffffff);
+
+    /* 24: */
+    gpr += 13;
+
+    /* Wave Playback Volume */
+    for (z = 0; z < 2; z++)
+        VOLUME(icode, &ptr, playback + z, z, gpr + z);
+    snd_emu10k1_init_stereo_control(controls + i++, "Wave Playback Volume", gpr, 100);
+    gpr += 2;
+
+    /* Wave Surround Playback Volume */
+    for (z = 0; z < 2; z++)
+        VOLUME(icode, &ptr, playback + 2 + z, z, gpr + z);
+    snd_emu10k1_init_stereo_control(controls + i++, "Wave Surround Playback Volume", gpr, 0);
+    gpr += 2;
+
+    /* Wave Center/LFE Playback Volume */
+    OP(icode, &ptr, iACC3, GPR(tmp + 0), FXBUS(FXBUS_PCM_LEFT), FXBUS(FXBUS_PCM_RIGHT), C_00000000);
+    OP(icode, &ptr, iMACINT0, GPR(tmp + 0), C_00000000, GPR(tmp + 0), C_00000002);
+    VOLUME(icode, &ptr, playback + 4, tmp + 0, gpr);
+    snd_emu10k1_init_mono_control(controls + i++, "Wave Center Playback Volume", gpr++, 0);
+    VOLUME(icode, &ptr, playback + 5, tmp + 0, gpr);
+    snd_emu10k1_init_mono_control(controls + i++, "Wave LFE Playback Volume", gpr++, 0);
+
+    /* Wave Capture Volume + Switch */
+    for (z = 0; z < 2; z++) {
+        SWITCH(icode, &ptr, tmp + 0, z, gpr + 2 + z);
+        VOLUME(icode, &ptr, capture + z, tmp + 0, gpr + z);
+    }
+    snd_emu10k1_init_stereo_control(controls + i++, "Wave Capture Volume", gpr, 0);
+    snd_emu10k1_init_stereo_onoff_control(controls + i++, "Wave Capture Switch", gpr + 2, 0);
+    gpr += 4;
+
+    /* Synth Playback Volume */
+    for (z = 0; z < 2; z++)
+        VOLUME_ADD(icode, &ptr, playback + z, 2 + z, gpr + z);
+    snd_emu10k1_init_stereo_control(controls + i++, "Synth Playback Volume", gpr, 100);
+    gpr += 2;
+
+    /* Synth Capture Volume + Switch */
+    for (z = 0; z < 2; z++) {
+        SWITCH(icode, &ptr, tmp + 0, 2 + z, gpr + 2 + z);
+        VOLUME_ADD(icode, &ptr, capture + z, tmp + 0, gpr + z);
+    }
+    snd_emu10k1_init_stereo_control(controls + i++, "Synth Capture Volume", gpr, 0);
+    snd_emu10k1_init_stereo_onoff_control(controls + i++, "Synth Capture Switch", gpr + 2, 0);
+    gpr += 4;
+
+    /* Surround Digital Playback Volume (renamed later without Digital) */
+    for (z = 0; z < 2; z++)
+        VOLUME_ADD(icode, &ptr, playback + 2 + z, 4 + z, gpr + z);
+    snd_emu10k1_init_stereo_control(controls + i++, "Surround Digital Playback Volume", gpr, 100);
+    gpr += 2;
+
+    /* Surround Capture Volume + Switch */
+    for (z = 0; z < 2; z++) {
+        SWITCH(icode, &ptr, tmp + 0, 4 + z, gpr + 2 + z);
+        VOLUME_ADD(icode, &ptr, capture + z, tmp + 0, gpr + z);
+    }
+    snd_emu10k1_init_stereo_control(controls + i++, "Surround Capture Volume", gpr, 0);
+    snd_emu10k1_init_stereo_onoff_control(controls + i++, "Surround Capture Switch", gpr + 2, 0);
+    gpr += 4;
+
+    /* Center Playback Volume (renamed later without Digital) */
+    VOLUME_ADD(icode, &ptr, playback + 4, 6, gpr);
+    snd_emu10k1_init_mono_control(controls + i++, "Center Digital Playback Volume", gpr++, 100);
+
+    /* LFE Playback Volume + Switch (renamed later without Digital) */
+    VOLUME_ADD(icode, &ptr, playback + 5, 7, gpr);
+    snd_emu10k1_init_mono_control(controls + i++, "LFE Digital Playback Volume", gpr++, 100);
+
+    /* Front Playback Volume */
+    for (z = 0; z < 2; z++)
+        VOLUME_ADD(icode, &ptr, playback + z, 10 + z, gpr + z);
+    snd_emu10k1_init_stereo_control(controls + i++, "Front Playback Volume", gpr, 100);
+    gpr += 2;
+
+    /* Front Capture Volume + Switch */
+    for (z = 0; z < 2; z++) {
+        SWITCH(icode, &ptr, tmp + 0, 10 + z, gpr + 2);
+        VOLUME_ADD(icode, &ptr, capture + z, tmp + 0, gpr + z);
+    }
+    snd_emu10k1_init_stereo_control(controls + i++, "Front Capture Volume", gpr, 0);
+    snd_emu10k1_init_mono_onoff_control(controls + i++, "Front Capture Switch", gpr + 2, 0);
+    gpr += 3;
+
+    /*
+     *  Process inputs
+     */
+
+    if (emu->fx8010.extin_mask & ((1<<EXTIN_AC97_L)|(1<<EXTIN_AC97_R))) {
+        /* AC'97 Playback Volume */
+        VOLUME_ADDIN(icode, &ptr, playback + 0, EXTIN_AC97_L, gpr); gpr++;
+        VOLUME_ADDIN(icode, &ptr, playback + 1, EXTIN_AC97_R, gpr); gpr++;
+        snd_emu10k1_init_stereo_control(controls + i++, "AC97 Playback Volume", gpr-2, 0);
+        /* AC'97 Capture Volume */
+        VOLUME_ADDIN(icode, &ptr, capture + 0, EXTIN_AC97_L, gpr); gpr++;
+        VOLUME_ADDIN(icode, &ptr, capture + 1, EXTIN_AC97_R, gpr); gpr++;
+        snd_emu10k1_init_stereo_control(controls + i++, "AC97 Capture Volume", gpr-2, 100);
+    }
+
+    if (emu->fx8010.extin_mask & ((1<<EXTIN_SPDIF_CD_L)|(1<<EXTIN_SPDIF_CD_R))) {
+        /* IEC958 TTL Playback Volume */
+        for (z = 0; z < 2; z++)
+            VOLUME_ADDIN(icode, &ptr, playback + z, EXTIN_SPDIF_CD_L + z, gpr + z);
+        snd_emu10k1_init_stereo_control(controls + i++, SNDRV_CTL_NAME_IEC958("TTL ",PLAYBACK,VOLUME), gpr, 0);
+        gpr += 2;
+
+        /* IEC958 TTL Capture Volume + Switch */
+        for (z = 0; z < 2; z++) {
+            SWITCH_IN(icode, &ptr, tmp + 0, EXTIN_SPDIF_CD_L + z, gpr + 2 + z);
+            VOLUME_ADD(icode, &ptr, capture + z, tmp + 0, gpr + z);
+        }
+        snd_emu10k1_init_stereo_control(controls + i++, SNDRV_CTL_NAME_IEC958("TTL ",CAPTURE,VOLUME), gpr, 0);
+        snd_emu10k1_init_stereo_onoff_control(controls + i++, SNDRV_CTL_NAME_IEC958("TTL ",CAPTURE,SWITCH), gpr + 2, 0);
+        gpr += 4;
+    }
+
+    if (emu->fx8010.extin_mask & ((1<<EXTIN_ZOOM_L)|(1<<EXTIN_ZOOM_R))) {
+        /* Zoom Video Playback Volume */
+        for (z = 0; z < 2; z++)
+            VOLUME_ADDIN(icode, &ptr, playback + z, EXTIN_ZOOM_L + z, gpr + z);
+        snd_emu10k1_init_stereo_control(controls + i++, "Zoom Video Playback Volume", gpr, 0);
+        gpr += 2;
+
+        /* Zoom Video Capture Volume + Switch */
+        for (z = 0; z < 2; z++) {
+            SWITCH_IN(icode, &ptr, tmp + 0, EXTIN_ZOOM_L + z, gpr + 2 + z);
+            VOLUME_ADD(icode, &ptr, capture + z, tmp + 0, gpr + z);
+        }
+        snd_emu10k1_init_stereo_control(controls + i++, "Zoom Video Capture Volume", gpr, 0);
+        snd_emu10k1_init_stereo_onoff_control(controls + i++, "Zoom Video Capture Switch", gpr + 2, 0);
+        gpr += 4;
+    }
+
+    if (emu->fx8010.extin_mask & ((1<<EXTIN_TOSLINK_L)|(1<<EXTIN_TOSLINK_R))) {
+        /* IEC958 Optical Playback Volume */
+        for (z = 0; z < 2; z++)
+            VOLUME_ADDIN(icode, &ptr, playback + z, EXTIN_TOSLINK_L + z, gpr + z);
+        snd_emu10k1_init_stereo_control(controls + i++, SNDRV_CTL_NAME_IEC958("LiveDrive ",PLAYBACK,VOLUME), gpr, 0);
+        gpr += 2;
+
+        /* IEC958 Optical Capture Volume */
+        for (z = 0; z < 2; z++) {
+            SWITCH_IN(icode, &ptr, tmp + 0, EXTIN_TOSLINK_L + z, gpr + 2 + z);
+            VOLUME_ADD(icode, &ptr, capture + z, tmp + 0, gpr + z);
+        }
+        snd_emu10k1_init_stereo_control(controls + i++, SNDRV_CTL_NAME_IEC958("LiveDrive ",CAPTURE,VOLUME), gpr, 0);
+        snd_emu10k1_init_stereo_onoff_control(controls + i++, SNDRV_CTL_NAME_IEC958("LiveDrive ",CAPTURE,SWITCH), gpr + 2, 0);
+        gpr += 4;
+    }
+
+    if (emu->fx8010.extin_mask & ((1<<EXTIN_LINE1_L)|(1<<EXTIN_LINE1_R))) {
+        /* Line LiveDrive Playback Volume */
+        for (z = 0; z < 2; z++)
+            VOLUME_ADDIN(icode, &ptr, playback + z, EXTIN_LINE1_L + z, gpr + z);
+        snd_emu10k1_init_stereo_control(controls + i++, "Line LiveDrive Playback Volume", gpr, 0);
+        gpr += 2;
+
+        /* Line LiveDrive Capture Volume + Switch */
+        for (z = 0; z < 2; z++) {
+            SWITCH_IN(icode, &ptr, tmp + 0, EXTIN_LINE1_L + z, gpr + 2 + z);
+            VOLUME_ADD(icode, &ptr, capture + z, tmp + 0, gpr + z);
+        }
+        snd_emu10k1_init_stereo_control(controls + i++, "Line LiveDrive Capture Volume", gpr, 0);
+        snd_emu10k1_init_stereo_onoff_control(controls + i++, "Line LiveDrive Capture Switch", gpr + 2, 0);
+        gpr += 4;
+    }
+
+    if (emu->fx8010.extin_mask & ((1<<EXTIN_COAX_SPDIF_L)|(1<<EXTIN_COAX_SPDIF_R))) {
+        /* IEC958 Coax Playback Volume */
+        for (z = 0; z < 2; z++)
+            VOLUME_ADDIN(icode, &ptr, playback + z, EXTIN_COAX_SPDIF_L + z, gpr + z);
+        snd_emu10k1_init_stereo_control(controls + i++, SNDRV_CTL_NAME_IEC958("Coaxial ",PLAYBACK,VOLUME), gpr, 0);
+        gpr += 2;
+
+        /* IEC958 Coax Capture Volume + Switch */
+        for (z = 0; z < 2; z++) {
+            SWITCH_IN(icode, &ptr, tmp + 0, EXTIN_COAX_SPDIF_L + z, gpr + 2 + z);
+            VOLUME_ADD(icode, &ptr, capture + z, tmp + 0, gpr + z);
+        }
+        snd_emu10k1_init_stereo_control(controls + i++, SNDRV_CTL_NAME_IEC958("Coaxial ",CAPTURE,VOLUME), gpr, 0);
+        snd_emu10k1_init_stereo_onoff_control(controls + i++, SNDRV_CTL_NAME_IEC958("Coaxial ",CAPTURE,SWITCH), gpr + 2, 0);
+        gpr += 4;
+    }
+
+    if (emu->fx8010.extin_mask & ((1<<EXTIN_LINE2_L)|(1<<EXTIN_LINE2_R))) {
+        /* Line LiveDrive Playback Volume */
+        for (z = 0; z < 2; z++)
+            VOLUME_ADDIN(icode, &ptr, playback + z, EXTIN_LINE2_L + z, gpr + z);
+        snd_emu10k1_init_stereo_control(controls + i++, "Line2 LiveDrive Playback Volume", gpr, 0);
+        controls[i-1].id.index = 1;
+        gpr += 2;
+
+        /* Line LiveDrive Capture Volume */
+        for (z = 0; z < 2; z++) {
+            SWITCH_IN(icode, &ptr, tmp + 0, EXTIN_LINE2_L + z, gpr + 2 + z);
+            VOLUME_ADD(icode, &ptr, capture + z, tmp + 0, gpr + z);
+        }
+        snd_emu10k1_init_stereo_control(controls + i++, "Line2 LiveDrive Capture Volume", gpr, 0);
+        controls[i-1].id.index = 1;
+        snd_emu10k1_init_stereo_onoff_control(controls + i++, "Line2 LiveDrive Capture Switch", gpr + 2, 0);
+        controls[i-1].id.index = 1;
+        gpr += 4;
+    }
+
+    /*
+     *  Process tone control
+     */
+    OP(icode, &ptr, iACC3, GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 0), GPR(playback + 0), C_00000000, C_00000000); /* left */
+    OP(icode, &ptr, iACC3, GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 1), GPR(playback + 1), C_00000000, C_00000000); /* right */
+    OP(icode, &ptr, iACC3, GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 2), GPR(playback + 2), C_00000000, C_00000000); /* rear left */
+    OP(icode, &ptr, iACC3, GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 3), GPR(playback + 3), C_00000000, C_00000000); /* rear right */
+    OP(icode, &ptr, iACC3, GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 4), GPR(playback + 4), C_00000000, C_00000000); /* center */
+    OP(icode, &ptr, iACC3, GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 5), GPR(playback + 5), C_00000000, C_00000000); /* LFE */
+
+    ctl = &controls[i + 0];
+    ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+    strcpy(ctl->id.name, "Tone Control - Bass");
+    ctl->vcount = 2;
+    ctl->count = 10;
+    ctl->min = 0;
+    ctl->max = 40;
+    ctl->value[0] = ctl->value[1] = 20;
+    ctl->translation = EMU10K1_GPR_TRANSLATION_BASS;
+    ctl = &controls[i + 1];
+    ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+    strcpy(ctl->id.name, "Tone Control - Treble");
+    ctl->vcount = 2;
+    ctl->count = 10;
+    ctl->min = 0;
+    ctl->max = 40;
+    ctl->value[0] = ctl->value[1] = 20;
+    ctl->translation = EMU10K1_GPR_TRANSLATION_TREBLE;
+
+#define BASS_GPR	0x8c
+#define TREBLE_GPR	0x96
+
+    for (z = 0; z < 5; z++) {
+        int j;
+        for (j = 0; j < 2; j++) {
+            controls[i + 0].gpr[z * 2 + j] = BASS_GPR + z * 2 + j;
+            controls[i + 1].gpr[z * 2 + j] = TREBLE_GPR + z * 2 + j;
+        }
+    }
+    for (z = 0; z < 3; z++) {		/* front/rear/center-lfe */
+        int j, k, l, d;
+        for (j = 0; j < 2; j++) {	/* left/right */
+            k = 0xa0 + (z * 8) + (j * 4);
+            l = 0xd0 + (z * 8) + (j * 4);
+            d = playback + SND_EMU10K1_PLAYBACK_CHANNELS + z * 2 + j;
+
+            OP(icode, &ptr, iMAC0, C_00000000, C_00000000, GPR(d), GPR(BASS_GPR + 0 + j));
+            OP(icode, &ptr, iMACMV, GPR(k+1), GPR(k), GPR(k+1), GPR(BASS_GPR + 4 + j));
+            OP(icode, &ptr, iMACMV, GPR(k), GPR(d), GPR(k), GPR(BASS_GPR + 2 + j));
+            OP(icode, &ptr, iMACMV, GPR(k+3), GPR(k+2), GPR(k+3), GPR(BASS_GPR + 8 + j));
+            OP(icode, &ptr, iMAC0, GPR(k+2), GPR_ACCU, GPR(k+2), GPR(BASS_GPR + 6 + j));
+            OP(icode, &ptr, iACC3, GPR(k+2), GPR(k+2), GPR(k+2), C_00000000);
+
+            OP(icode, &ptr, iMAC0, C_00000000, C_00000000, GPR(k+2), GPR(TREBLE_GPR + 0 + j));
+            OP(icode, &ptr, iMACMV, GPR(l+1), GPR(l), GPR(l+1), GPR(TREBLE_GPR + 4 + j));
+            OP(icode, &ptr, iMACMV, GPR(l), GPR(k+2), GPR(l), GPR(TREBLE_GPR + 2 + j));
+            OP(icode, &ptr, iMACMV, GPR(l+3), GPR(l+2), GPR(l+3), GPR(TREBLE_GPR + 8 + j));
+            OP(icode, &ptr, iMAC0, GPR(l+2), GPR_ACCU, GPR(l+2), GPR(TREBLE_GPR + 6 + j));
+            OP(icode, &ptr, iMACINT0, GPR(l+2), C_00000000, GPR(l+2), C_00000010);
+
+            OP(icode, &ptr, iACC3, GPR(d), GPR(l+2), C_00000000, C_00000000);
+
+            if (z == 2)	/* center */
+                break;
+        }
+    }
+    i += 2;
+
+#undef BASS_GPR
+#undef TREBLE_GPR
+
+    for (z = 0; z < 6; z++) {
+        SWITCH(icode, &ptr, tmp + 0, playback + SND_EMU10K1_PLAYBACK_CHANNELS + z, gpr + 0);
+        SWITCH_NEG(icode, &ptr, tmp + 1, gpr + 0);
+        SWITCH(icode, &ptr, tmp + 1, playback + z, tmp + 1);
+        OP(icode, &ptr, iACC3, GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + z), GPR(tmp + 0), GPR(tmp + 1), C_00000000);
+    }
+    snd_emu10k1_init_stereo_onoff_control(controls + i++, "Tone Control - Switch", gpr, 0);
+    gpr += 2;
+
+    /*
+     *  Process outputs
+     */
+    if (emu->fx8010.extout_mask & ((1<<EXTOUT_AC97_L)|(1<<EXTOUT_AC97_R))) {
+        /* AC'97 Playback Volume */
+
+        for (z = 0; z < 2; z++)
+            OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_AC97_L + z), GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + z), C_00000000, C_00000000);
+    }
+
+    if (emu->fx8010.extout_mask & ((1<<EXTOUT_TOSLINK_L)|(1<<EXTOUT_TOSLINK_R))) {
+        /* IEC958 Optical Raw Playback Switch */
+
+        for (z = 0; z < 2; z++) {
+            SWITCH(icode, &ptr, tmp + 0, 8 + z, gpr + z);
+            SWITCH_NEG(icode, &ptr, tmp + 1, gpr + z);
+            SWITCH(icode, &ptr, tmp + 1, playback + SND_EMU10K1_PLAYBACK_CHANNELS + z, tmp + 1);
+            OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_TOSLINK_L + z), GPR(tmp + 0), GPR(tmp + 1), C_00000000);
+#ifdef EMU10K1_CAPTURE_DIGITAL_OUT
+            OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_ADC_CAP_L + z), GPR(tmp + 0), GPR(tmp + 1), C_00000000);
+#endif
+        }
+
+        snd_emu10k1_init_stereo_onoff_control(controls + i++, SNDRV_CTL_NAME_IEC958("Optical Raw ",PLAYBACK,SWITCH), gpr, 0);
+        gpr += 2;
+    }
+
+    if (emu->fx8010.extout_mask & ((1<<EXTOUT_HEADPHONE_L)|(1<<EXTOUT_HEADPHONE_R))) {
+        /* Headphone Playback Volume */
+
+        for (z = 0; z < 2; z++) {
+            SWITCH(icode, &ptr, tmp + 0, playback + SND_EMU10K1_PLAYBACK_CHANNELS + 4 + z, gpr + 2 + z);
+            SWITCH_NEG(icode, &ptr, tmp + 1, gpr + 2 + z);
+            SWITCH(icode, &ptr, tmp + 1, playback + SND_EMU10K1_PLAYBACK_CHANNELS + z, tmp + 1);
+            OP(icode, &ptr, iACC3, GPR(tmp + 0), GPR(tmp + 0), GPR(tmp + 1), C_00000000);
+            VOLUME_OUT(icode, &ptr, EXTOUT_HEADPHONE_L + z, tmp + 0, gpr + z);
+        }
+
+        snd_emu10k1_init_stereo_control(controls + i++, "Headphone Playback Volume", gpr + 0, 0);
+        controls[i-1].id.index = 1;	/* AC'97 can have also Headphone control */
+        snd_emu10k1_init_mono_onoff_control(controls + i++, "Headphone Center Playback Switch", gpr + 2, 0);
+        controls[i-1].id.index = 1;
+        snd_emu10k1_init_mono_onoff_control(controls + i++, "Headphone LFE Playback Switch", gpr + 3, 0);
+        controls[i-1].id.index = 1;
+
+        gpr += 4;
+    }
+
+    if (emu->fx8010.extout_mask & ((1<<EXTOUT_REAR_L)|(1<<EXTOUT_REAR_R)))
+        for (z = 0; z < 2; z++)
+            OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_REAR_L + z), GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 2 + z), C_00000000, C_00000000);
+
+    if (emu->fx8010.extout_mask & ((1<<EXTOUT_AC97_REAR_L)|(1<<EXTOUT_AC97_REAR_R)))
+        for (z = 0; z < 2; z++)
+            OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_AC97_REAR_L + z), GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 2 + z), C_00000000, C_00000000);
+    if (emu->fx8010.extout_mask & (1<<EXTOUT_AC97_CENTER)) {
+#ifndef EMU10K1_CENTER_LFE_FROM_FRONT
+        OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_AC97_CENTER), GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 4), C_00000000, C_00000000);
+        OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_ACENTER), GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 4), C_00000000, C_00000000);
+#else
+        OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_AC97_CENTER), GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 0), C_00000000, C_00000000);
+        OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_ACENTER), GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 0), C_00000000, C_00000000);
+#endif
+    }
+
+    if (emu->fx8010.extout_mask & (1<<EXTOUT_AC97_LFE)) {
+#ifndef EMU10K1_CENTER_LFE_FROM_FRONT
+        OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_AC97_LFE), GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 5), C_00000000, C_00000000);
+        OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_ALFE), GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 5), C_00000000, C_00000000);
+#else
+        OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_AC97_LFE), GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 1), C_00000000, C_00000000);
+        OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_ALFE), GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 1), C_00000000, C_00000000);
+#endif
+    }
+
+#ifndef EMU10K1_CAPTURE_DIGITAL_OUT
+    for (z = 0; z < 2; z++)
+        OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_ADC_CAP_L + z), GPR(capture + z), C_00000000, C_00000000);
+#endif
+
+    if (emu->fx8010.extout_mask & (1<<EXTOUT_MIC_CAP))
+        OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_MIC_CAP), GPR(capture + 2), C_00000000, C_00000000);
+
+    /* EFX capture - capture the 16 EXTINS */
+    if (emu->card_capabilities->sblive51) {
+        /* On the Live! 5.1, FXBUS2(1) and FXBUS(2) are shared with EXTOUT_ACENTER
+         * and EXTOUT_ALFE, so we can't connect inputs to them for multitrack recording.
+         *
+         * Since only 14 of the 16 EXTINs are used, this is not a big problem.
+         * We route AC97L and R to FX capture 14 and 15, SPDIF CD in to FX capture
+         * 0 and 3, then the rest of the EXTINs to the corresponding FX capture
+         * channel.  Multitrack recorders will still see the center/lfe output signal
+         * on the second and third channels.
+         */
+        OP(icode, &ptr, iACC3, FXBUS2(14), C_00000000, C_00000000, EXTIN(0));
+        OP(icode, &ptr, iACC3, FXBUS2(15), C_00000000, C_00000000, EXTIN(1));
+        OP(icode, &ptr, iACC3, FXBUS2(0), C_00000000, C_00000000, EXTIN(2));
+        OP(icode, &ptr, iACC3, FXBUS2(3), C_00000000, C_00000000, EXTIN(3));
+        for (z = 4; z < 14; z++)
+            OP(icode, &ptr, iACC3, FXBUS2(z), C_00000000, C_00000000, EXTIN(z));
+    } else {
+        for (z = 0; z < 16; z++)
+            OP(icode, &ptr, iACC3, FXBUS2(z), C_00000000, C_00000000, EXTIN(z));
+    }
+
+    if (gpr > tmp) {
+        snd_BUG();
+        err = -EIO;
+        goto __err;
+    }
+    if (i > SND_EMU10K1_GPR_CONTROLS) {
+        snd_BUG();
+        err = -EIO;
+        goto __err;
+    }
+
+    /* clear remaining instruction memory */
+    while (ptr < 0x200)
+        OP(icode, &ptr, iACC3, C_00000000, C_00000000, C_00000000, C_00000000);
+
+    if ((err = snd_emu10k1_fx8010_tram_setup(emu, ipcm->buffer_size)) < 0)
+        goto __err;
+    seg = snd_enter_user();
+    icode->gpr_add_control_count = i;
+    icode->gpr_add_controls = (struct snd_emu10k1_fx8010_control_gpr __user *)controls;
+    err = snd_emu10k1_icode_poke(emu, icode);
+    snd_leave_user(seg);
+    if (err >= 0)
+        err = snd_emu10k1_ipcm_poke(emu, ipcm);
+__err:
+    kfree(ipcm);
+    kfree(controls);
+    if (icode != NULL) {
+        kfree((void __force *)icode->gpr_map);
+        kfree(icode);
+    }
+    return err;
+}
+
+int __devinit snd_emu10k1_init_efx(struct snd_emu10k1 *emu)
+{
+    spin_lock_init(&emu->fx8010.irq_lock);
+    INIT_LIST_HEAD(&emu->fx8010.gpr_ctl);
+    if (emu->audigy)
+        return _snd_emu10k1_audigy_init_efx(emu);
+    else
+        return _snd_emu10k1_init_efx(emu);
+}
+
+void snd_emu10k1_free_efx(struct snd_emu10k1 *emu)
+{
+    /* stop processor */
+    if (emu->audigy)
+        snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg = A_DBG_SINGLE_STEP);
+    else
+        snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg = EMU10K1_DBG_SINGLE_STEP);
+}
+
+#if 0 /* FIXME: who use them? */
+int snd_emu10k1_fx8010_tone_control_activate(struct snd_emu10k1 *emu, int output)
+{
+    if (output < 0 || output >= 6)
+        return -EINVAL;
+    snd_emu10k1_ptr_write(emu, emu->gpr_base + 0x94 + output, 0, 1);
+    return 0;
+}
+
+int snd_emu10k1_fx8010_tone_control_deactivate(struct snd_emu10k1 *emu, int output)
+{
+    if (output < 0 || output >= 6)
+        return -EINVAL;
+    snd_emu10k1_ptr_write(emu, emu->gpr_base + 0x94 + output, 0, 0);
+    return 0;
+}
+#endif
+
+int snd_emu10k1_fx8010_tram_setup(struct snd_emu10k1 *emu, u32 size)
+{
+    u8 size_reg = 0;
+
+    /* size is in samples */
+    if (size != 0) {
+        size = (size - 1) >> 13;
+
+        while (size) {
+            size >>= 1;
+            size_reg++;
+        }
+        size = 0x2000 << size_reg;
+    }
+    if ((emu->fx8010.etram_pages.bytes / 2) == size)
+        return 0;
+    spin_lock_irq(&emu->emu_lock);
+    outl(HCFG_LOCKTANKCACHE_MASK | inl(emu->port + HCFG), emu->port + HCFG);
+    spin_unlock_irq(&emu->emu_lock);
+    snd_emu10k1_ptr_write(emu, TCB, 0, 0);
+    snd_emu10k1_ptr_write(emu, TCBS, 0, 0);
+    if (emu->fx8010.etram_pages.area != NULL) {
+        snd_dma_free_pages(&emu->fx8010.etram_pages);
+        emu->fx8010.etram_pages.area = NULL;
+        emu->fx8010.etram_pages.bytes = 0;
+    }
+
+    if (size > 0) {
+        if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(emu->pci),
+                                size * 2, &emu->fx8010.etram_pages) < 0)
+            return -ENOMEM;
+        memset(emu->fx8010.etram_pages.area, 0, size * 2);
+        snd_emu10k1_ptr_write(emu, TCB, 0, emu->fx8010.etram_pages.addr);
+        snd_emu10k1_ptr_write(emu, TCBS, 0, size_reg);
+        spin_lock_irq(&emu->emu_lock);
+        outl(inl(emu->port + HCFG) & ~HCFG_LOCKTANKCACHE_MASK, emu->port + HCFG);
+        spin_unlock_irq(&emu->emu_lock);
+    }
+
+    return 0;
+}
+
+static int snd_emu10k1_fx8010_open(struct snd_hwdep * hw, struct file *file)
+{
+    return 0;
+}
+
+static void copy_string(char *dst, char *src, char *null, int idx)
+{
+    if (src == NULL)
+        sprintf(dst, "%s %02X", null, idx);
+    else
+        strcpy(dst, src);
+}
+
+static int snd_emu10k1_fx8010_info(struct snd_emu10k1 *emu,
+                                   struct snd_emu10k1_fx8010_info *info)
+{
+    char **fxbus, **extin, **extout;
+    unsigned short fxbus_mask, extin_mask, extout_mask;
+    int res;
+
+    memset(info, 0, sizeof(info));
+    info->internal_tram_size = emu->fx8010.itram_size;
+    info->external_tram_size = emu->fx8010.etram_pages.bytes / 2;
+    fxbus = fxbuses;
+    extin = emu->audigy ? audigy_ins : creative_ins;
+    extout = emu->audigy ? audigy_outs : creative_outs;
+    fxbus_mask = emu->fx8010.fxbus_mask;
+    extin_mask = emu->fx8010.extin_mask;
+    extout_mask = emu->fx8010.extout_mask;
+    for (res = 0; res < 16; res++, fxbus++, extin++, extout++) {
+        copy_string(info->fxbus_names[res], fxbus_mask & (1 << res) ? *fxbus : NULL, "FXBUS", res);
+        copy_string(info->extin_names[res], extin_mask & (1 << res) ? *extin : NULL, "Unused", res);
+        copy_string(info->extout_names[res], extout_mask & (1 << res) ? *extout : NULL, "Unused", res);
+    }
+    for (res = 16; res < 32; res++, extout++)
+        copy_string(info->extout_names[res], extout_mask & (1 << res) ? *extout : NULL, "Unused", res);
+    info->gpr_controls = emu->fx8010.gpr_count;
+    return 0;
+}
+
+static int snd_emu10k1_fx8010_ioctl(struct snd_hwdep * hw, struct file *file, unsigned int cmd, unsigned long arg)
+{
+    struct snd_emu10k1 *emu = hw->private_data;
+    struct snd_emu10k1_fx8010_info *info;
+    struct snd_emu10k1_fx8010_code *icode;
+    struct snd_emu10k1_fx8010_pcm_rec *ipcm;
+    unsigned int addr;
+    void __user *argp = (void __user *)arg;
+    int res;
+
+    switch (cmd) {
+    case SNDRV_EMU10K1_IOCTL_INFO:
+        info = kmalloc(sizeof(*info), GFP_KERNEL);
+        if (!info)
+            return -ENOMEM;
+        if ((res = snd_emu10k1_fx8010_info(emu, info)) < 0) {
+            kfree(info);
+            return res;
+        }
+        if (copy_to_user(argp, info, sizeof(*info))) {
+            kfree(info);
+            return -EFAULT;
+        }
+        kfree(info);
+        return 0;
+    case SNDRV_EMU10K1_IOCTL_CODE_POKE:
+        if (!capable(CAP_SYS_ADMIN))
+            return -EPERM;
+        icode = kmalloc(sizeof(*icode), GFP_KERNEL);
+        if (icode == NULL)
+            return -ENOMEM;
+        if (copy_from_user(icode, argp, sizeof(*icode))) {
+            kfree(icode);
+            return -EFAULT;
+        }
+        res = snd_emu10k1_icode_poke(emu, icode);
+        kfree(icode);
+        return res;
+    case SNDRV_EMU10K1_IOCTL_CODE_PEEK:
+        icode = kmalloc(sizeof(*icode), GFP_KERNEL);
+        if (icode == NULL)
+            return -ENOMEM;
+        if (copy_from_user(icode, argp, sizeof(*icode))) {
+            kfree(icode);
+            return -EFAULT;
+        }
+        res = snd_emu10k1_icode_peek(emu, icode);
+        if (res == 0 && copy_to_user(argp, icode, sizeof(*icode))) {
+            kfree(icode);
+            return -EFAULT;
+        }
+        kfree(icode);
+        return res;
+    case SNDRV_EMU10K1_IOCTL_PCM_POKE:
+        ipcm = kmalloc(sizeof(*ipcm), GFP_KERNEL);
+        if (ipcm == NULL)
+            return -ENOMEM;
+        if (copy_from_user(ipcm, argp, sizeof(*ipcm))) {
+            kfree(ipcm);
+            return -EFAULT;
+        }
+        res = snd_emu10k1_ipcm_poke(emu, ipcm);
+        kfree(ipcm);
+        return res;
+    case SNDRV_EMU10K1_IOCTL_PCM_PEEK:
+        ipcm = kzalloc(sizeof(*ipcm), GFP_KERNEL);
+        if (ipcm == NULL)
+            return -ENOMEM;
+        if (copy_from_user(ipcm, argp, sizeof(*ipcm))) {
+            kfree(ipcm);
+            return -EFAULT;
+        }
+        res = snd_emu10k1_ipcm_peek(emu, ipcm);
+        if (res == 0 && copy_to_user(argp, ipcm, sizeof(*ipcm))) {
+            kfree(ipcm);
+            return -EFAULT;
+        }
+        kfree(ipcm);
+        return res;
+    case SNDRV_EMU10K1_IOCTL_TRAM_SETUP:
+        if (!capable(CAP_SYS_ADMIN))
+            return -EPERM;
+        if (get_user(addr, (unsigned int __user *)argp))
+            return -EFAULT;
+        down(&emu->fx8010.lock);
+        res = snd_emu10k1_fx8010_tram_setup(emu, addr);
+        up(&emu->fx8010.lock);
+        return res;
+    case SNDRV_EMU10K1_IOCTL_STOP:
+        if (!capable(CAP_SYS_ADMIN))
+            return -EPERM;
+        if (emu->audigy)
+            snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg |= A_DBG_SINGLE_STEP);
+        else
+            snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg |= EMU10K1_DBG_SINGLE_STEP);
+        return 0;
+    case SNDRV_EMU10K1_IOCTL_CONTINUE:
+        if (!capable(CAP_SYS_ADMIN))
+            return -EPERM;
+        if (emu->audigy)
+            snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg = 0);
+        else
+            snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg = 0);
+        return 0;
+    case SNDRV_EMU10K1_IOCTL_ZERO_TRAM_COUNTER:
+        if (!capable(CAP_SYS_ADMIN))
+            return -EPERM;
+        if (emu->audigy)
+            snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg | A_DBG_ZC);
+        else
+            snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg | EMU10K1_DBG_ZC);
+        udelay(10);
+        if (emu->audigy)
+            snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg);
+        else
+            snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg);
+        return 0;
+    case SNDRV_EMU10K1_IOCTL_SINGLE_STEP:
+        if (!capable(CAP_SYS_ADMIN))
+            return -EPERM;
+        if (get_user(addr, (unsigned int __user *)argp))
+            return -EFAULT;
+        if (addr > 0x1ff)
+            return -EINVAL;
+        if (emu->audigy)
+            snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg |= A_DBG_SINGLE_STEP | addr);
+        else
+            snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg |= EMU10K1_DBG_SINGLE_STEP | addr);
+        udelay(10);
+        if (emu->audigy)
+            snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg |= A_DBG_SINGLE_STEP | A_DBG_STEP_ADDR | addr);
+        else
+            snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg |= EMU10K1_DBG_SINGLE_STEP | EMU10K1_DBG_STEP | addr);
+        return 0;
+    case SNDRV_EMU10K1_IOCTL_DBG_READ:
+        if (emu->audigy)
+            addr = snd_emu10k1_ptr_read(emu, A_DBG, 0);
+        else
+            addr = snd_emu10k1_ptr_read(emu, DBG, 0);
+        if (put_user(addr, (unsigned int __user *)argp))
+            return -EFAULT;
+        return 0;
+    }
+    return -ENOTTY;
+}
+
+static int snd_emu10k1_fx8010_release(struct snd_hwdep * hw, struct file *file)
+{
+    return 0;
+}
+
+int __devinit snd_emu10k1_fx8010_new(struct snd_emu10k1 *emu, int device, struct snd_hwdep ** rhwdep)
+{
+    struct snd_hwdep *hw;
+    int err;
+
+    if (rhwdep)
+        *rhwdep = NULL;
+    if ((err = snd_hwdep_new(emu->card, "FX8010", device, &hw)) < 0)
+        return err;
+    strcpy(hw->name, "EMU10K1 (FX8010)");
+    hw->iface = SNDRV_HWDEP_IFACE_EMU10K1;
+    hw->ops.open = snd_emu10k1_fx8010_open;
+    hw->ops.ioctl = snd_emu10k1_fx8010_ioctl;
+    hw->ops.release = snd_emu10k1_fx8010_release;
+    hw->private_data = emu;
+    if (rhwdep)
+        *rhwdep = hw;
+    return 0;
+}
+
+#ifdef CONFIG_PM
+int __devinit snd_emu10k1_efx_alloc_pm_buffer(struct snd_emu10k1 *emu)
+{
+    int len;
+
+    len = emu->audigy ? 0x200 : 0x100;
+    emu->saved_gpr = kmalloc(len * 4, GFP_KERNEL);
+    if (! emu->saved_gpr)
+        return -ENOMEM;
+    len = emu->audigy ? 0x100 : 0xa0;
+    emu->tram_val_saved = kmalloc(len * 4, GFP_KERNEL);
+    emu->tram_addr_saved = kmalloc(len * 4, GFP_KERNEL);
+    if (! emu->tram_val_saved || ! emu->tram_addr_saved)
+        return -ENOMEM;
+    len = emu->audigy ? 2 * 1024 : 2 * 512;
+    emu->saved_icode = vmalloc(len * 4);
+    if (! emu->saved_icode)
+        return -ENOMEM;
+    return 0;
+}
+
+void snd_emu10k1_efx_free_pm_buffer(struct snd_emu10k1 *emu)
+{
+    kfree(emu->saved_gpr);
+    kfree(emu->tram_val_saved);
+    kfree(emu->tram_addr_saved);
+    vfree(emu->saved_icode);
+}
+
+/*
+ * save/restore GPR, TRAM and codes
+ */
+void snd_emu10k1_efx_suspend(struct snd_emu10k1 *emu)
+{
+    int i, len;
+
+    len = emu->audigy ? 0x200 : 0x100;
+    for (i = 0; i < len; i++)
+        emu->saved_gpr[i] = snd_emu10k1_ptr_read(emu, emu->gpr_base + i, 0);
+
+    len = emu->audigy ? 0x100 : 0xa0;
+    for (i = 0; i < len; i++) {
+        emu->tram_val_saved[i] = snd_emu10k1_ptr_read(emu, TANKMEMDATAREGBASE + i, 0);
+        emu->tram_addr_saved[i] = snd_emu10k1_ptr_read(emu, TANKMEMADDRREGBASE + i, 0);
+        if (emu->audigy) {
+            emu->tram_addr_saved[i] >>= 12;
+            emu->tram_addr_saved[i] |=
+                snd_emu10k1_ptr_read(emu, A_TANKMEMCTLREGBASE + i, 0) << 20;
+        }
+    }
+
+    len = emu->audigy ? 2 * 1024 : 2 * 512;
+    for (i = 0; i < len; i++)
+        emu->saved_icode[i] = snd_emu10k1_efx_read(emu, i);
+}
+
+void snd_emu10k1_efx_resume(struct snd_emu10k1 *emu)
+{
+    int i, len;
+
+    /* set up TRAM */
+    if (emu->fx8010.etram_pages.bytes > 0) {
+        unsigned size, size_reg = 0;
+        size = emu->fx8010.etram_pages.bytes / 2;
+        size = (size - 1) >> 13;
+        while (size) {
+            size >>= 1;
+            size_reg++;
+        }
+        outl(HCFG_LOCKTANKCACHE_MASK | inl(emu->port + HCFG), emu->port + HCFG);
+        snd_emu10k1_ptr_write(emu, TCB, 0, emu->fx8010.etram_pages.addr);
+        snd_emu10k1_ptr_write(emu, TCBS, 0, size_reg);
+        outl(inl(emu->port + HCFG) & ~HCFG_LOCKTANKCACHE_MASK, emu->port + HCFG);
+    }
+
+    if (emu->audigy)
+        snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg | A_DBG_SINGLE_STEP);
+    else
+        snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg | EMU10K1_DBG_SINGLE_STEP);
+
+    len = emu->audigy ? 0x200 : 0x100;
+    for (i = 0; i < len; i++)
+        snd_emu10k1_ptr_write(emu, emu->gpr_base + i, 0, emu->saved_gpr[i]);
+
+    len = emu->audigy ? 0x100 : 0xa0;
+    for (i = 0; i < len; i++) {
+        snd_emu10k1_ptr_write(emu, TANKMEMDATAREGBASE + i, 0,
+                              emu->tram_val_saved[i]);
+        if (! emu->audigy)
+            snd_emu10k1_ptr_write(emu, TANKMEMADDRREGBASE + i, 0,
+                                  emu->tram_addr_saved[i]);
+        else {
+            snd_emu10k1_ptr_write(emu, TANKMEMADDRREGBASE + i, 0,
+                                  emu->tram_addr_saved[i] << 12);
+            snd_emu10k1_ptr_write(emu, TANKMEMADDRREGBASE + i, 0,
+                                  emu->tram_addr_saved[i] >> 20);
+        }
+    }
+
+    len = emu->audigy ? 2 * 1024 : 2 * 512;
+    for (i = 0; i < len; i++)
+        snd_emu10k1_efx_write(emu, i, emu->saved_icode[i]);
+
+    /* start FX processor when the DSP code is updated */
+    if (emu->audigy)
+        snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg);
+    else
+        snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg);
+}
+#endif
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/emu10k1/emumixer.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/emu10k1/emumixer.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/emu10k1/emumixer.c	(revision 91)
@@ -0,0 +1,1589 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>,
+ *                   Takashi Iwai <tiwai@suse.de>
+ *                   Creative Labs, Inc.
+ *  Routines for control of EMU10K1 chips / mixer routines
+ *  Multichannel PCM support Copyright (c) Lee Revell <rlrevell@joe-job.com>
+ *
+ *  Copyright (c) by James Courtier-Dutton <James@superbug.co.uk>
+ *  	Added EMU 1010 support.
+ *
+ *  BUGS:
+ *    --
+ *
+ *  TODO:
+ *    --
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/emu10k1.h>
+
+#define AC97_ID_STAC9758        0x83847658
+
+static int snd_emu10k1_spdif_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+    uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+    uinfo->count = 1;
+    return 0;
+}
+
+static int snd_emu10k1_spdif_get(struct snd_kcontrol *kcontrol,
+                                 struct snd_ctl_elem_value *ucontrol)
+{
+    struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+    unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+    unsigned long flags;
+
+    spin_lock_irqsave(&emu->reg_lock, flags);
+    ucontrol->value.iec958.status[0] = (emu->spdif_bits[idx] >> 0) & 0xff;
+    ucontrol->value.iec958.status[1] = (emu->spdif_bits[idx] >> 8) & 0xff;
+    ucontrol->value.iec958.status[2] = (emu->spdif_bits[idx] >> 16) & 0xff;
+    ucontrol->value.iec958.status[3] = (emu->spdif_bits[idx] >> 24) & 0xff;
+    spin_unlock_irqrestore(&emu->reg_lock, flags);
+    return 0;
+}
+
+static int snd_emu10k1_spdif_get_mask(struct snd_kcontrol *kcontrol,
+                                      struct snd_ctl_elem_value *ucontrol)
+{
+    ucontrol->value.iec958.status[0] = 0xff;
+    ucontrol->value.iec958.status[1] = 0xff;
+    ucontrol->value.iec958.status[2] = 0xff;
+    ucontrol->value.iec958.status[3] = 0xff;
+    return 0;
+}
+
+static char *emu1010_src_texts[] = {
+    "Silence",
+    "Dock Mic A",
+    "Dock Mic B",
+    "Dock ADC1 Left",
+    "Dock ADC1 Right",
+    "Dock ADC2 Left",
+    "Dock ADC2 Right",
+    "Dock ADC3 Left",
+    "Dock ADC3 Right",
+    "0202 ADC Left",
+    "0202 ADC Right",
+    "0202 SPDIF Left",
+    "0202 SPDIF Right",
+    "ADAT 0",
+    "ADAT 1",
+    "ADAT 2",
+    "ADAT 3",
+    "ADAT 4",
+    "ADAT 5",
+    "ADAT 6",
+    "ADAT 7",
+    "DSP 0",
+    "DSP 1",
+    "DSP 2",
+    "DSP 3",
+    "DSP 4",
+    "DSP 5",
+    "DSP 6",
+    "DSP 7",
+    "DSP 8",
+    "DSP 9",
+    "DSP 10",
+    "DSP 11",
+    "DSP 12",
+    "DSP 13",
+    "DSP 14",
+    "DSP 15",
+    "DSP 16",
+    "DSP 17",
+    "DSP 18",
+    "DSP 19",
+    "DSP 20",
+    "DSP 21",
+    "DSP 22",
+    "DSP 23",
+    "DSP 24",
+    "DSP 25",
+    "DSP 26",
+    "DSP 27",
+    "DSP 28",
+    "DSP 29",
+    "DSP 30",
+    "DSP 31",
+};
+
+static unsigned int emu1010_src_regs[] = {
+    EMU_SRC_SILENCE,/* 0 */
+    EMU_SRC_DOCK_MIC_A1, /* 1 */
+    EMU_SRC_DOCK_MIC_B1, /* 2 */
+    EMU_SRC_DOCK_ADC1_LEFT1, /* 3 */
+    EMU_SRC_DOCK_ADC1_RIGHT1, /* 4 */
+    EMU_SRC_DOCK_ADC2_LEFT1, /* 5 */
+    EMU_SRC_DOCK_ADC2_RIGHT1, /* 6 */
+    EMU_SRC_DOCK_ADC3_LEFT1, /* 7 */
+    EMU_SRC_DOCK_ADC3_RIGHT1, /* 8 */
+    EMU_SRC_HAMOA_ADC_LEFT1, /* 9 */
+    EMU_SRC_HAMOA_ADC_RIGHT1, /* 10 */
+    EMU_SRC_HANA_SPDIF_LEFT1, /* 11 */
+    EMU_SRC_HANA_SPDIF_RIGHT1, /* 12 */
+    EMU_SRC_HANA_ADAT, /* 13 */
+    EMU_SRC_HANA_ADAT+1, /* 14 */
+    EMU_SRC_HANA_ADAT+2, /* 15 */
+    EMU_SRC_HANA_ADAT+3, /* 16 */
+    EMU_SRC_HANA_ADAT+4, /* 17 */
+    EMU_SRC_HANA_ADAT+5, /* 18 */
+    EMU_SRC_HANA_ADAT+6, /* 19 */
+    EMU_SRC_HANA_ADAT+7, /* 20 */
+    EMU_SRC_ALICE_EMU32A, /* 21 */
+    EMU_SRC_ALICE_EMU32A+1, /* 22 */
+    EMU_SRC_ALICE_EMU32A+2, /* 23 */
+    EMU_SRC_ALICE_EMU32A+3, /* 24 */
+    EMU_SRC_ALICE_EMU32A+4, /* 25 */
+    EMU_SRC_ALICE_EMU32A+5, /* 26 */
+    EMU_SRC_ALICE_EMU32A+6, /* 27 */
+    EMU_SRC_ALICE_EMU32A+7, /* 28 */
+    EMU_SRC_ALICE_EMU32A+8, /* 29 */
+    EMU_SRC_ALICE_EMU32A+9, /* 30 */
+    EMU_SRC_ALICE_EMU32A+0xa, /* 31 */
+    EMU_SRC_ALICE_EMU32A+0xb, /* 32 */
+    EMU_SRC_ALICE_EMU32A+0xc, /* 33 */
+    EMU_SRC_ALICE_EMU32A+0xd, /* 34 */
+    EMU_SRC_ALICE_EMU32A+0xe, /* 35 */
+    EMU_SRC_ALICE_EMU32A+0xf, /* 36 */
+    EMU_SRC_ALICE_EMU32B, /* 37 */
+    EMU_SRC_ALICE_EMU32B+1, /* 38 */
+    EMU_SRC_ALICE_EMU32B+2, /* 39 */
+    EMU_SRC_ALICE_EMU32B+3, /* 40 */
+    EMU_SRC_ALICE_EMU32B+4, /* 41 */
+    EMU_SRC_ALICE_EMU32B+5, /* 42 */
+    EMU_SRC_ALICE_EMU32B+6, /* 43 */
+    EMU_SRC_ALICE_EMU32B+7, /* 44 */
+    EMU_SRC_ALICE_EMU32B+8, /* 45 */
+    EMU_SRC_ALICE_EMU32B+9, /* 46 */
+    EMU_SRC_ALICE_EMU32B+0xa, /* 47 */
+    EMU_SRC_ALICE_EMU32B+0xb, /* 48 */
+    EMU_SRC_ALICE_EMU32B+0xc, /* 49 */
+    EMU_SRC_ALICE_EMU32B+0xd, /* 50 */
+    EMU_SRC_ALICE_EMU32B+0xe, /* 51 */
+    EMU_SRC_ALICE_EMU32B+0xf, /* 52 */
+};
+
+static unsigned int emu1010_output_dst[] = {
+    EMU_DST_DOCK_DAC1_LEFT1, /* 0 */
+    EMU_DST_DOCK_DAC1_RIGHT1, /* 1 */
+    EMU_DST_DOCK_DAC2_LEFT1, /* 2 */
+    EMU_DST_DOCK_DAC2_RIGHT1, /* 3 */
+    EMU_DST_DOCK_DAC3_LEFT1, /* 4 */
+    EMU_DST_DOCK_DAC3_RIGHT1, /* 5 */
+    EMU_DST_DOCK_DAC4_LEFT1, /* 6 */
+    EMU_DST_DOCK_DAC4_RIGHT1, /* 7 */
+    EMU_DST_DOCK_PHONES_LEFT1, /* 8 */
+    EMU_DST_DOCK_PHONES_RIGHT1, /* 9 */
+    EMU_DST_DOCK_SPDIF_LEFT1, /* 10 */
+    EMU_DST_DOCK_SPDIF_RIGHT1, /* 11 */
+    EMU_DST_HANA_SPDIF_LEFT1, /* 12 */
+    EMU_DST_HANA_SPDIF_RIGHT1, /* 13 */
+    EMU_DST_HAMOA_DAC_LEFT1, /* 14 */
+    EMU_DST_HAMOA_DAC_RIGHT1, /* 15 */
+    EMU_DST_HANA_ADAT, /* 16 */
+    EMU_DST_HANA_ADAT+1, /* 17 */
+    EMU_DST_HANA_ADAT+2, /* 18 */
+    EMU_DST_HANA_ADAT+3, /* 19 */
+    EMU_DST_HANA_ADAT+4, /* 20 */
+    EMU_DST_HANA_ADAT+5, /* 21 */
+    EMU_DST_HANA_ADAT+6, /* 22 */
+    EMU_DST_HANA_ADAT+7, /* 23 */
+};
+
+static unsigned int emu1010_input_dst[] = {
+    EMU_DST_ALICE2_EMU32_0,
+    EMU_DST_ALICE2_EMU32_1,
+    EMU_DST_ALICE2_EMU32_2,
+    EMU_DST_ALICE2_EMU32_3,
+    EMU_DST_ALICE2_EMU32_4,
+    EMU_DST_ALICE2_EMU32_5,
+    EMU_DST_ALICE2_EMU32_6,
+    EMU_DST_ALICE2_EMU32_7,
+    EMU_DST_ALICE2_EMU32_8,
+    EMU_DST_ALICE2_EMU32_9,
+    EMU_DST_ALICE2_EMU32_A,
+    EMU_DST_ALICE2_EMU32_B,
+    EMU_DST_ALICE2_EMU32_C,
+    EMU_DST_ALICE2_EMU32_D,
+    EMU_DST_ALICE2_EMU32_E,
+    EMU_DST_ALICE2_EMU32_F,
+    EMU_DST_ALICE_I2S0_LEFT,
+    EMU_DST_ALICE_I2S0_RIGHT,
+    EMU_DST_ALICE_I2S1_LEFT,
+    EMU_DST_ALICE_I2S1_RIGHT,
+    EMU_DST_ALICE_I2S2_LEFT,
+    EMU_DST_ALICE_I2S2_RIGHT,
+};
+
+static int snd_emu1010_input_output_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+    uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+    uinfo->count = 1;
+    uinfo->value.enumerated.items = 53;
+    if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+        uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
+    strcpy(uinfo->value.enumerated.name, emu1010_src_texts[uinfo->value.enumerated.item]);
+    return 0;
+}
+
+static int snd_emu1010_output_source_get(struct snd_kcontrol *kcontrol,
+                                         struct snd_ctl_elem_value *ucontrol)
+{
+    struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+    int channel;
+
+    channel = (kcontrol->private_value) & 0xff;
+    ucontrol->value.enumerated.item[0] = emu->emu1010.output_source[channel];
+    return 0;
+}
+
+static int snd_emu1010_output_source_put(struct snd_kcontrol *kcontrol,
+                                         struct snd_ctl_elem_value *ucontrol)
+{
+    struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+    int change = 0;
+    unsigned int val;
+    int channel;
+
+    channel = (kcontrol->private_value) & 0xff;
+    if (emu->emu1010.output_source[channel] != ucontrol->value.enumerated.item[0]) {
+        val = emu->emu1010.output_source[channel] = ucontrol->value.enumerated.item[0];
+        change = 1;
+        snd_emu1010_fpga_link_dst_src_write(emu,
+                                            emu1010_output_dst[channel], emu1010_src_regs[val]);
+    }
+    return change;
+}
+
+static int snd_emu1010_input_source_get(struct snd_kcontrol *kcontrol,
+                                        struct snd_ctl_elem_value *ucontrol)
+{
+    struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+    int channel;
+
+    channel = (kcontrol->private_value) & 0xff;
+    ucontrol->value.enumerated.item[0] = emu->emu1010.input_source[channel];
+    return 0;
+}
+
+static int snd_emu1010_input_source_put(struct snd_kcontrol *kcontrol,
+                                        struct snd_ctl_elem_value *ucontrol)
+{
+    struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+    int change = 0;
+    unsigned int val;
+    int channel;
+
+    channel = (kcontrol->private_value) & 0xff;
+    if (emu->emu1010.input_source[channel] != ucontrol->value.enumerated.item[0]) {
+        val = emu->emu1010.input_source[channel] = ucontrol->value.enumerated.item[0];
+        change = 1;
+        snd_emu1010_fpga_link_dst_src_write(emu,
+                                            emu1010_input_dst[channel], emu1010_src_regs[val]);
+    }
+    return change;
+}
+
+#define EMU1010_SOURCE_OUTPUT(xname,chid) \
+    {								\
+    .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,	\
+    .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,		\
+    .info =  snd_emu1010_input_output_source_info,		\
+    .get =   snd_emu1010_output_source_get,			\
+    .put =   snd_emu1010_output_source_put,			\
+    .private_value = chid					\
+    }
+
+static struct snd_kcontrol_new snd_emu1010_output_enum_ctls[] __devinitdata = {
+    EMU1010_SOURCE_OUTPUT("Dock DAC1 Left Playback Switch", 0),
+    EMU1010_SOURCE_OUTPUT("Dock DAC1 Right Playback Switch", 1),
+    EMU1010_SOURCE_OUTPUT("Dock DAC2 Left Playback Switch", 2),
+    EMU1010_SOURCE_OUTPUT("Dock DAC2 Right Playback Switch", 3),
+    EMU1010_SOURCE_OUTPUT("Dock DAC3 Left Playback Switch", 4),
+    EMU1010_SOURCE_OUTPUT("Dock DAC3 Right Playback Switch", 5),
+    EMU1010_SOURCE_OUTPUT("Dock DAC4 Left Playback Switch", 6),
+    EMU1010_SOURCE_OUTPUT("Dock DAC4 Right Playback Switch", 7),
+    EMU1010_SOURCE_OUTPUT("Dock Phones Left Playback Switch", 8),
+    EMU1010_SOURCE_OUTPUT("Dock Phones Right Playback Switch", 9),
+    EMU1010_SOURCE_OUTPUT("Dock SPDIF Left Playback Switch", 0xa),
+    EMU1010_SOURCE_OUTPUT("Dock SPDIF Right Playback Switch", 0xb),
+    EMU1010_SOURCE_OUTPUT("1010 SPDIF Left Playback Switch", 0xc),
+    EMU1010_SOURCE_OUTPUT("1010 SPDIF Right Playback Switch", 0xd),
+    EMU1010_SOURCE_OUTPUT("0202 DAC Left Playback Switch", 0xe),
+    EMU1010_SOURCE_OUTPUT("0202 DAC Right Playback Switch", 0xf),
+    EMU1010_SOURCE_OUTPUT("1010 ADAT 0 Playback Switch", 0x10),
+    EMU1010_SOURCE_OUTPUT("1010 ADAT 1 Playback Switch", 0x11),
+    EMU1010_SOURCE_OUTPUT("1010 ADAT 2 Playback Switch", 0x12),
+    EMU1010_SOURCE_OUTPUT("1010 ADAT 3 Playback Switch", 0x13),
+    EMU1010_SOURCE_OUTPUT("1010 ADAT 4 Playback Switch", 0x14),
+    EMU1010_SOURCE_OUTPUT("1010 ADAT 5 Playback Switch", 0x15),
+    EMU1010_SOURCE_OUTPUT("1010 ADAT 6 Playback Switch", 0x16),
+    EMU1010_SOURCE_OUTPUT("1010 ADAT 7 Playback Switch", 0x17),
+};
+
+#define EMU1010_SOURCE_INPUT(xname,chid) \
+    {								\
+    .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,	\
+    .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,		\
+    .info =  snd_emu1010_input_output_source_info,		\
+    .get =   snd_emu1010_input_source_get,			\
+    .put =   snd_emu1010_input_source_put,			\
+    .private_value = chid					\
+    }
+
+static struct snd_kcontrol_new snd_emu1010_input_enum_ctls[] __devinitdata = {
+    EMU1010_SOURCE_INPUT("DSP 0 Capture Switch", 0),
+    EMU1010_SOURCE_INPUT("DSP 1 Capture Switch", 1),
+    EMU1010_SOURCE_INPUT("DSP 2 Capture Switch", 2),
+    EMU1010_SOURCE_INPUT("DSP 3 Capture Switch", 3),
+    EMU1010_SOURCE_INPUT("DSP 4 Capture Switch", 4),
+    EMU1010_SOURCE_INPUT("DSP 5 Capture Switch", 5),
+    EMU1010_SOURCE_INPUT("DSP 6 Capture Switch", 6),
+    EMU1010_SOURCE_INPUT("DSP 7 Capture Switch", 7),
+    EMU1010_SOURCE_INPUT("DSP 8 Capture Switch", 8),
+    EMU1010_SOURCE_INPUT("DSP 9 Capture Switch", 9),
+    EMU1010_SOURCE_INPUT("DSP A Capture Switch", 0xa),
+    EMU1010_SOURCE_INPUT("DSP B Capture Switch", 0xb),
+    EMU1010_SOURCE_INPUT("DSP C Capture Switch", 0xc),
+    EMU1010_SOURCE_INPUT("DSP D Capture Switch", 0xd),
+    EMU1010_SOURCE_INPUT("DSP E Capture Switch", 0xe),
+    EMU1010_SOURCE_INPUT("DSP F Capture Switch", 0xf),
+    EMU1010_SOURCE_INPUT("DSP 10 Capture Switch", 0x10),
+    EMU1010_SOURCE_INPUT("DSP 11 Capture Switch", 0x11),
+    EMU1010_SOURCE_INPUT("DSP 12 Capture Switch", 0x12),
+    EMU1010_SOURCE_INPUT("DSP 13 Capture Switch", 0x13),
+    EMU1010_SOURCE_INPUT("DSP 14 Capture Switch", 0x14),
+    EMU1010_SOURCE_INPUT("DSP 15 Capture Switch", 0x15),
+};
+
+
+
+
+static int snd_emu1010_adc_pads_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *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_emu1010_adc_pads_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+    struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+    unsigned int mask = kcontrol->private_value & 0xff;
+    ucontrol->value.integer.value[0] = (emu->emu1010.adc_pads & mask) ? 1 : 0;
+    return 0;
+}
+
+static int snd_emu1010_adc_pads_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+    struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+    unsigned int mask = kcontrol->private_value & 0xff;
+    unsigned int val, cache;
+    val = ucontrol->value.integer.value[0];
+    cache = emu->emu1010.adc_pads;
+    if (val == 1)
+        cache = cache | mask;
+    else
+        cache = cache & ~mask;
+    if (cache != emu->emu1010.adc_pads) {
+        snd_emu1010_fpga_write(emu, EMU_HANA_ADC_PADS, cache );
+        emu->emu1010.adc_pads = cache;
+    }
+
+    return 0;
+}
+
+
+
+#define EMU1010_ADC_PADS(xname,chid) \
+    {								\
+    .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,	\
+    .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,		\
+    .info =  snd_emu1010_adc_pads_info,			\
+    .get =   snd_emu1010_adc_pads_get,			\
+    .put =   snd_emu1010_adc_pads_put,			\
+    .private_value = chid					\
+    }
+
+static struct snd_kcontrol_new snd_emu1010_adc_pads[] __devinitdata = {
+    EMU1010_ADC_PADS("ADC1 14dB PAD Audio Dock Capture Switch", EMU_HANA_DOCK_ADC_PAD1),
+    EMU1010_ADC_PADS("ADC2 14dB PAD Audio Dock Capture Switch", EMU_HANA_DOCK_ADC_PAD2),
+    EMU1010_ADC_PADS("ADC3 14dB PAD Audio Dock Capture Switch", EMU_HANA_DOCK_ADC_PAD3),
+    EMU1010_ADC_PADS("ADC1 14dB PAD 0202 Capture Switch", EMU_HANA_0202_ADC_PAD1),
+};
+
+static int snd_emu1010_dac_pads_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *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_emu1010_dac_pads_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+    struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+    unsigned int mask = kcontrol->private_value & 0xff;
+    ucontrol->value.integer.value[0] = (emu->emu1010.dac_pads & mask) ? 1 : 0;
+    return 0;
+}
+
+static int snd_emu1010_dac_pads_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+    struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+    unsigned int mask = kcontrol->private_value & 0xff;
+    unsigned int val, cache;
+    val = ucontrol->value.integer.value[0];
+    cache = emu->emu1010.dac_pads;
+    if (val == 1)
+        cache = cache | mask;
+    else
+        cache = cache & ~mask;
+    if (cache != emu->emu1010.dac_pads) {
+        snd_emu1010_fpga_write(emu, EMU_HANA_DAC_PADS, cache );
+        emu->emu1010.dac_pads = cache;
+    }
+
+    return 0;
+}
+
+
+
+#define EMU1010_DAC_PADS(xname,chid) \
+    {								\
+    .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,	\
+    .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,		\
+    .info =  snd_emu1010_dac_pads_info,			\
+    .get =   snd_emu1010_dac_pads_get,			\
+    .put =   snd_emu1010_dac_pads_put,			\
+    .private_value = chid					\
+    }
+
+static struct snd_kcontrol_new snd_emu1010_dac_pads[] __devinitdata = {
+    EMU1010_DAC_PADS("DAC1 Audio Dock 14dB PAD Playback Switch", EMU_HANA_DOCK_DAC_PAD1),
+    EMU1010_DAC_PADS("DAC2 Audio Dock 14dB PAD Playback Switch", EMU_HANA_DOCK_DAC_PAD2),
+    EMU1010_DAC_PADS("DAC3 Audio Dock 14dB PAD Playback Switch", EMU_HANA_DOCK_DAC_PAD3),
+    EMU1010_DAC_PADS("DAC4 Audio Dock 14dB PAD Playback Switch", EMU_HANA_DOCK_DAC_PAD4),
+    EMU1010_DAC_PADS("DAC1 0202 14dB PAD Playback Switch", EMU_HANA_0202_DAC_PAD1),
+};
+
+
+static int snd_emu1010_internal_clock_info(struct snd_kcontrol *kcontrol,
+                                           struct snd_ctl_elem_info *uinfo)
+{
+    static char *texts[2] = {
+        "44100", "48000"
+    };
+
+    uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+    uinfo->count = 1;
+    uinfo->value.enumerated.items = 2;
+    if (uinfo->value.enumerated.item > 1)
+        uinfo->value.enumerated.item = 1;
+    strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+    return 0;
+}
+
+static int snd_emu1010_internal_clock_get(struct snd_kcontrol *kcontrol,
+                                          struct snd_ctl_elem_value *ucontrol)
+{
+    struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+
+    ucontrol->value.enumerated.item[0] = emu->emu1010.internal_clock;
+    return 0;
+}
+
+static int snd_emu1010_internal_clock_put(struct snd_kcontrol *kcontrol,
+                                          struct snd_ctl_elem_value *ucontrol)
+{
+    struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+    unsigned int val;
+    int change = 0;
+
+    val = ucontrol->value.enumerated.item[0] ;
+    change = (emu->emu1010.internal_clock != val);
+    if (change) {
+        emu->emu1010.internal_clock = val;
+        switch (val) {
+        case 0:
+            /* 44100 */
+            /* Mute all */
+            snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_MUTE );
+            /* Default fallback clock 48kHz */
+            snd_emu1010_fpga_write(emu, EMU_HANA_DEFCLOCK, EMU_HANA_DEFCLOCK_44_1K );
+            /* Word Clock source, Internal 44.1kHz x1 */
+            snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK,
+                                   EMU_HANA_WCLOCK_INT_44_1K | EMU_HANA_WCLOCK_1X );
+            /* Set LEDs on Audio Dock */
+            snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2,
+                                   EMU_HANA_DOCK_LEDS_2_44K | EMU_HANA_DOCK_LEDS_2_LOCK );
+            /* Allow DLL to settle */
+            msleep(10);
+            /* Unmute all */
+            snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE );
+            break;
+        case 1:
+            /* 48000 */
+            /* Mute all */
+            snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_MUTE );
+            /* Default fallback clock 48kHz */
+            snd_emu1010_fpga_write(emu, EMU_HANA_DEFCLOCK, EMU_HANA_DEFCLOCK_48K );
+            /* Word Clock source, Internal 48kHz x1 */
+            snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK,
+                                   EMU_HANA_WCLOCK_INT_48K | EMU_HANA_WCLOCK_1X );
+            /* Set LEDs on Audio Dock */
+            snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2,
+                                   EMU_HANA_DOCK_LEDS_2_48K | EMU_HANA_DOCK_LEDS_2_LOCK );
+            /* Allow DLL to settle */
+            msleep(10);
+            /* Unmute all */
+            snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE );
+            break;
+        }
+    }
+    return change;
+}
+
+static struct snd_kcontrol_new snd_emu1010_internal_clock =
+{
+    .access =	SNDRV_CTL_ELEM_ACCESS_READWRITE,
+    .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
+    .name =         "Clock Internal Rate",
+    .count =	1,
+    .info =         snd_emu1010_internal_clock_info,
+    .get =          snd_emu1010_internal_clock_get,
+    .put =          snd_emu1010_internal_clock_put
+};
+
+
+#if 0
+static int snd_audigy_spdif_output_rate_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+    static char *texts[] = {"44100", "48000", "96000"};
+
+    uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+    uinfo->count = 1;
+    uinfo->value.enumerated.items = 3;
+    if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+        uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
+    strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+    return 0;
+}
+
+static int snd_audigy_spdif_output_rate_get(struct snd_kcontrol *kcontrol,
+                                            struct snd_ctl_elem_value *ucontrol)
+{
+    struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+    unsigned int tmp;
+    unsigned long flags;
+
+
+    spin_lock_irqsave(&emu->reg_lock, flags);
+    tmp = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, 0);
+    switch (tmp & A_SPDIF_RATE_MASK) {
+    case A_SPDIF_44100:
+        ucontrol->value.enumerated.item[0] = 0;
+        break;
+    case A_SPDIF_48000:
+        ucontrol->value.enumerated.item[0] = 1;
+        break;
+    case A_SPDIF_96000:
+        ucontrol->value.enumerated.item[0] = 2;
+        break;
+    default:
+        ucontrol->value.enumerated.item[0] = 1;
+    }
+    spin_unlock_irqrestore(&emu->reg_lock, flags);
+    return 0;
+}
+
+static int snd_audigy_spdif_output_rate_put(struct snd_kcontrol *kcontrol,
+                                            struct snd_ctl_elem_value *ucontrol)
+{
+    struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+    int change;
+    unsigned int reg, val, tmp;
+    unsigned long flags;
+
+    switch(ucontrol->value.enumerated.item[0]) {
+    case 0:
+        val = A_SPDIF_44100;
+        break;
+    case 1:
+        val = A_SPDIF_48000;
+        break;
+    case 2:
+        val = A_SPDIF_96000;
+        break;
+    default:
+        val = A_SPDIF_48000;
+        break;
+    }
+
+
+    spin_lock_irqsave(&emu->reg_lock, flags);
+    reg = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, 0);
+    tmp = reg & ~A_SPDIF_RATE_MASK;
+    tmp |= val;
+    if ((change = (tmp != reg)))
+        snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, 0, tmp);
+    spin_unlock_irqrestore(&emu->reg_lock, flags);
+    return change;
+}
+
+static struct snd_kcontrol_new snd_audigy_spdif_output_rate =
+{
+    .access =	SNDRV_CTL_ELEM_ACCESS_READWRITE,
+    .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
+    .name =         "Audigy SPDIF Output Sample Rate",
+    .count =	1,
+    .info =         snd_audigy_spdif_output_rate_info,
+    .get =          snd_audigy_spdif_output_rate_get,
+    .put =          snd_audigy_spdif_output_rate_put
+};
+#endif
+
+static int snd_emu10k1_spdif_put(struct snd_kcontrol *kcontrol,
+                                 struct snd_ctl_elem_value *ucontrol)
+{
+    struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+    unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+    int change;
+    unsigned int val;
+    unsigned long flags;
+
+    val = (ucontrol->value.iec958.status[0] << 0) |
+        (ucontrol->value.iec958.status[1] << 8) |
+        (ucontrol->value.iec958.status[2] << 16) |
+        (ucontrol->value.iec958.status[3] << 24);
+    spin_lock_irqsave(&emu->reg_lock, flags);
+    change = val != emu->spdif_bits[idx];
+    if (change) {
+        snd_emu10k1_ptr_write(emu, SPCS0 + idx, 0, val);
+        emu->spdif_bits[idx] = val;
+    }
+    spin_unlock_irqrestore(&emu->reg_lock, flags);
+    return change;
+}
+
+static struct snd_kcontrol_new snd_emu10k1_spdif_mask_control =
+{
+    .access =	SNDRV_CTL_ELEM_ACCESS_READ,
+    .iface =        SNDRV_CTL_ELEM_IFACE_PCM,
+    .name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK),
+    .count =	4,
+    .info =         snd_emu10k1_spdif_info,
+    .get =          snd_emu10k1_spdif_get_mask
+};
+
+static struct snd_kcontrol_new snd_emu10k1_spdif_control =
+{
+    .iface =	SNDRV_CTL_ELEM_IFACE_PCM,
+    .name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
+    .count =	4,
+    .info =         snd_emu10k1_spdif_info,
+    .get =          snd_emu10k1_spdif_get,
+    .put =          snd_emu10k1_spdif_put
+};
+
+
+static void update_emu10k1_fxrt(struct snd_emu10k1 *emu, int voice, unsigned char *route)
+{
+    if (emu->audigy) {
+        snd_emu10k1_ptr_write(emu, A_FXRT1, voice,
+                              snd_emu10k1_compose_audigy_fxrt1(route));
+        snd_emu10k1_ptr_write(emu, A_FXRT2, voice,
+                              snd_emu10k1_compose_audigy_fxrt2(route));
+    } else {
+        snd_emu10k1_ptr_write(emu, FXRT, voice,
+                              snd_emu10k1_compose_send_routing(route));
+    }
+}
+
+static void update_emu10k1_send_volume(struct snd_emu10k1 *emu, int voice, unsigned char *volume)
+{
+    snd_emu10k1_ptr_write(emu, PTRX_FXSENDAMOUNT_A, voice, volume[0]);
+    snd_emu10k1_ptr_write(emu, PTRX_FXSENDAMOUNT_B, voice, volume[1]);
+    snd_emu10k1_ptr_write(emu, PSST_FXSENDAMOUNT_C, voice, volume[2]);
+    snd_emu10k1_ptr_write(emu, DSL_FXSENDAMOUNT_D, voice, volume[3]);
+    if (emu->audigy) {
+        unsigned int val = ((unsigned int)volume[4] << 24) |
+            ((unsigned int)volume[5] << 16) |
+            ((unsigned int)volume[6] << 8) |
+            (unsigned int)volume[7];
+        snd_emu10k1_ptr_write(emu, A_SENDAMOUNTS, voice, val);
+    }
+}
+
+/* PCM stream controls */
+
+static int snd_emu10k1_send_routing_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+    struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+    uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+    uinfo->count = emu->audigy ? 3*8 : 3*4;
+    uinfo->value.integer.min = 0;
+    uinfo->value.integer.max = emu->audigy ? 0x3f : 0x0f;
+    return 0;
+}
+
+static int snd_emu10k1_send_routing_get(struct snd_kcontrol *kcontrol,
+                                        struct snd_ctl_elem_value *ucontrol)
+{
+    unsigned long flags;
+    struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+    struct snd_emu10k1_pcm_mixer *mix =
+        &emu->pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
+    int voice, idx;
+    int num_efx = emu->audigy ? 8 : 4;
+    int mask = emu->audigy ? 0x3f : 0x0f;
+
+    spin_lock_irqsave(&emu->reg_lock, flags);
+    for (voice = 0; voice < 3; voice++)
+        for (idx = 0; idx < num_efx; idx++)
+            ucontrol->value.integer.value[(voice * num_efx) + idx] =
+                mix->send_routing[voice][idx] & mask;
+    spin_unlock_irqrestore(&emu->reg_lock, flags);
+    return 0;
+}
+
+static int snd_emu10k1_send_routing_put(struct snd_kcontrol *kcontrol,
+                                        struct snd_ctl_elem_value *ucontrol)
+{
+    unsigned long flags;
+    struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+    struct snd_emu10k1_pcm_mixer *mix =
+        &emu->pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
+    int change = 0, voice, idx, val;
+    int num_efx = emu->audigy ? 8 : 4;
+    int mask = emu->audigy ? 0x3f : 0x0f;
+
+    spin_lock_irqsave(&emu->reg_lock, flags);
+    for (voice = 0; voice < 3; voice++)
+        for (idx = 0; idx < num_efx; idx++) {
+            val = ucontrol->value.integer.value[(voice * num_efx) + idx] & mask;
+            if (mix->send_routing[voice][idx] != val) {
+                mix->send_routing[voice][idx] = val;
+                change = 1;
+            }
+        }
+    if (change && mix->epcm) {
+        if (mix->epcm->voices[0] && mix->epcm->voices[1]) {
+            update_emu10k1_fxrt(emu, mix->epcm->voices[0]->number,
+                                &mix->send_routing[1][0]);
+            update_emu10k1_fxrt(emu, mix->epcm->voices[1]->number,
+                                &mix->send_routing[2][0]);
+        } else if (mix->epcm->voices[0]) {
+            update_emu10k1_fxrt(emu, mix->epcm->voices[0]->number,
+                                &mix->send_routing[0][0]);
+        }
+    }
+    spin_unlock_irqrestore(&emu->reg_lock, flags);
+    return change;
+}
+
+static struct snd_kcontrol_new snd_emu10k1_send_routing_control =
+{
+    .access =	SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+    .iface =        SNDRV_CTL_ELEM_IFACE_PCM,
+    .name =         "EMU10K1 PCM Send Routing",
+    .count =	32,
+    .info =         snd_emu10k1_send_routing_info,
+    .get =          snd_emu10k1_send_routing_get,
+    .put =          snd_emu10k1_send_routing_put
+};
+
+static int snd_emu10k1_send_volume_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+    struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+    uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+    uinfo->count = emu->audigy ? 3*8 : 3*4;
+    uinfo->value.integer.min = 0;
+    uinfo->value.integer.max = 255;
+    return 0;
+}
+
+static int snd_emu10k1_send_volume_get(struct snd_kcontrol *kcontrol,
+                                       struct snd_ctl_elem_value *ucontrol)
+{
+    unsigned long flags;
+    struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+    struct snd_emu10k1_pcm_mixer *mix =
+        &emu->pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
+    int idx;
+    int num_efx = emu->audigy ? 8 : 4;
+
+    spin_lock_irqsave(&emu->reg_lock, flags);
+    for (idx = 0; idx < 3*num_efx; idx++)
+        ucontrol->value.integer.value[idx] = mix->send_volume[idx/num_efx][idx%num_efx];
+    spin_unlock_irqrestore(&emu->reg_lock, flags);
+    return 0;
+}
+
+static int snd_emu10k1_send_volume_put(struct snd_kcontrol *kcontrol,
+                                       struct snd_ctl_elem_value *ucontrol)
+{
+    unsigned long flags;
+    struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+    struct snd_emu10k1_pcm_mixer *mix =
+        &emu->pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
+    int change = 0, idx, val;
+    int num_efx = emu->audigy ? 8 : 4;
+
+    spin_lock_irqsave(&emu->reg_lock, flags);
+    for (idx = 0; idx < 3*num_efx; idx++) {
+        val = ucontrol->value.integer.value[idx] & 255;
+        if (mix->send_volume[idx/num_efx][idx%num_efx] != val) {
+            mix->send_volume[idx/num_efx][idx%num_efx] = val;
+            change = 1;
+        }
+    }
+    if (change && mix->epcm) {
+        if (mix->epcm->voices[0] && mix->epcm->voices[1]) {
+            update_emu10k1_send_volume(emu, mix->epcm->voices[0]->number,
+                                       &mix->send_volume[1][0]);
+            update_emu10k1_send_volume(emu, mix->epcm->voices[1]->number,
+                                       &mix->send_volume[2][0]);
+        } else if (mix->epcm->voices[0]) {
+            update_emu10k1_send_volume(emu, mix->epcm->voices[0]->number,
+                                       &mix->send_volume[0][0]);
+        }
+    }
+    spin_unlock_irqrestore(&emu->reg_lock, flags);
+    return change;
+}
+
+static struct snd_kcontrol_new snd_emu10k1_send_volume_control =
+{
+    .access =	SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+    .iface =        SNDRV_CTL_ELEM_IFACE_PCM,
+    .name =         "EMU10K1 PCM Send Volume",
+    .count =	32,
+    .info =         snd_emu10k1_send_volume_info,
+    .get =          snd_emu10k1_send_volume_get,
+    .put =          snd_emu10k1_send_volume_put
+};
+
+static int snd_emu10k1_attn_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+    uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+    uinfo->count = 3;
+    uinfo->value.integer.min = 0;
+    uinfo->value.integer.max = 0xffff;
+    return 0;
+}
+
+static int snd_emu10k1_attn_get(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+    struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+    struct snd_emu10k1_pcm_mixer *mix =
+        &emu->pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
+    unsigned long flags;
+    int idx;
+
+    spin_lock_irqsave(&emu->reg_lock, flags);
+    for (idx = 0; idx < 3; idx++)
+        ucontrol->value.integer.value[idx] = mix->attn[idx];
+    spin_unlock_irqrestore(&emu->reg_lock, flags);
+    return 0;
+}
+
+static int snd_emu10k1_attn_put(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+    unsigned long flags;
+    struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+    struct snd_emu10k1_pcm_mixer *mix =
+        &emu->pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
+    int change = 0, idx, val;
+
+    spin_lock_irqsave(&emu->reg_lock, flags);
+    for (idx = 0; idx < 3; idx++) {
+        val = ucontrol->value.integer.value[idx] & 0xffff;
+        if (mix->attn[idx] != val) {
+            mix->attn[idx] = val;
+            change = 1;
+        }
+    }
+    if (change && mix->epcm) {
+        if (mix->epcm->voices[0] && mix->epcm->voices[1]) {
+            snd_emu10k1_ptr_write(emu, VTFT_VOLUMETARGET, mix->epcm->voices[0]->number, mix->attn[1]);
+            snd_emu10k1_ptr_write(emu, VTFT_VOLUMETARGET, mix->epcm->voices[1]->number, mix->attn[2]);
+        } else if (mix->epcm->voices[0]) {
+            snd_emu10k1_ptr_write(emu, VTFT_VOLUMETARGET, mix->epcm->voices[0]->number, mix->attn[0]);
+        }
+    }
+    spin_unlock_irqrestore(&emu->reg_lock, flags);
+    return change;
+}
+
+static struct snd_kcontrol_new snd_emu10k1_attn_control =
+{
+    .access =	SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+    .iface =        SNDRV_CTL_ELEM_IFACE_PCM,
+    .name =         "EMU10K1 PCM Volume",
+    .count =	32,
+    .info =         snd_emu10k1_attn_info,
+    .get =          snd_emu10k1_attn_get,
+    .put =          snd_emu10k1_attn_put
+};
+
+/* Mutichannel PCM stream controls */
+
+static int snd_emu10k1_efx_send_routing_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+    struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+    uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+    uinfo->count = emu->audigy ? 8 : 4;
+    uinfo->value.integer.min = 0;
+    uinfo->value.integer.max = emu->audigy ? 0x3f : 0x0f;
+    return 0;
+}
+
+static int snd_emu10k1_efx_send_routing_get(struct snd_kcontrol *kcontrol,
+                                            struct snd_ctl_elem_value *ucontrol)
+{
+    unsigned long flags;
+    struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+    struct snd_emu10k1_pcm_mixer *mix =
+        &emu->efx_pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
+    int idx;
+    int num_efx = emu->audigy ? 8 : 4;
+    int mask = emu->audigy ? 0x3f : 0x0f;
+
+    spin_lock_irqsave(&emu->reg_lock, flags);
+    for (idx = 0; idx < num_efx; idx++)
+        ucontrol->value.integer.value[idx] =
+            mix->send_routing[0][idx] & mask;
+    spin_unlock_irqrestore(&emu->reg_lock, flags);
+    return 0;
+}
+
+static int snd_emu10k1_efx_send_routing_put(struct snd_kcontrol *kcontrol,
+                                            struct snd_ctl_elem_value *ucontrol)
+{
+    unsigned long flags;
+    struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+    int ch = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+    struct snd_emu10k1_pcm_mixer *mix = &emu->efx_pcm_mixer[ch];
+    int change = 0, idx, val;
+    int num_efx = emu->audigy ? 8 : 4;
+    int mask = emu->audigy ? 0x3f : 0x0f;
+
+    spin_lock_irqsave(&emu->reg_lock, flags);
+    for (idx = 0; idx < num_efx; idx++) {
+        val = ucontrol->value.integer.value[idx] & mask;
+        if (mix->send_routing[0][idx] != val) {
+            mix->send_routing[0][idx] = val;
+            change = 1;
+        }
+    }
+
+    if (change && mix->epcm) {
+        if (mix->epcm->voices[ch]) {
+            update_emu10k1_fxrt(emu, mix->epcm->voices[ch]->number,
+                                &mix->send_routing[0][0]);
+        }
+    }
+    spin_unlock_irqrestore(&emu->reg_lock, flags);
+    return change;
+}
+
+static struct snd_kcontrol_new snd_emu10k1_efx_send_routing_control =
+{
+    .access =	SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+    .iface =        SNDRV_CTL_ELEM_IFACE_PCM,
+    .name =         "Multichannel PCM Send Routing",
+    .count =	16,
+    .info =         snd_emu10k1_efx_send_routing_info,
+    .get =          snd_emu10k1_efx_send_routing_get,
+    .put =          snd_emu10k1_efx_send_routing_put
+};
+
+static int snd_emu10k1_efx_send_volume_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+    struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+    uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+    uinfo->count = emu->audigy ? 8 : 4;
+    uinfo->value.integer.min = 0;
+    uinfo->value.integer.max = 255;
+    return 0;
+}
+
+static int snd_emu10k1_efx_send_volume_get(struct snd_kcontrol *kcontrol,
+                                           struct snd_ctl_elem_value *ucontrol)
+{
+    unsigned long flags;
+    struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+    struct snd_emu10k1_pcm_mixer *mix =
+        &emu->efx_pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
+    int idx;
+    int num_efx = emu->audigy ? 8 : 4;
+
+    spin_lock_irqsave(&emu->reg_lock, flags);
+    for (idx = 0; idx < num_efx; idx++)
+        ucontrol->value.integer.value[idx] = mix->send_volume[0][idx];
+    spin_unlock_irqrestore(&emu->reg_lock, flags);
+    return 0;
+}
+
+static int snd_emu10k1_efx_send_volume_put(struct snd_kcontrol *kcontrol,
+                                           struct snd_ctl_elem_value *ucontrol)
+{
+    unsigned long flags;
+    struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+    int ch = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+    struct snd_emu10k1_pcm_mixer *mix = &emu->efx_pcm_mixer[ch];
+    int change = 0, idx, val;
+    int num_efx = emu->audigy ? 8 : 4;
+
+    spin_lock_irqsave(&emu->reg_lock, flags);
+    for (idx = 0; idx < num_efx; idx++) {
+        val = ucontrol->value.integer.value[idx] & 255;
+        if (mix->send_volume[0][idx] != val) {
+            mix->send_volume[0][idx] = val;
+            change = 1;
+        }
+    }
+    if (change && mix->epcm) {
+        if (mix->epcm->voices[ch]) {
+            update_emu10k1_send_volume(emu, mix->epcm->voices[ch]->number,
+                                       &mix->send_volume[0][0]);
+        }
+    }
+    spin_unlock_irqrestore(&emu->reg_lock, flags);
+    return change;
+}
+
+
+static struct snd_kcontrol_new snd_emu10k1_efx_send_volume_control =
+{
+    .access =	SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+    .iface =        SNDRV_CTL_ELEM_IFACE_PCM,
+    .name =         "Multichannel PCM Send Volume",
+    .count =	16,
+    .info =         snd_emu10k1_efx_send_volume_info,
+    .get =          snd_emu10k1_efx_send_volume_get,
+    .put =          snd_emu10k1_efx_send_volume_put
+};
+
+static int snd_emu10k1_efx_attn_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+    uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+    uinfo->count = 1;
+    uinfo->value.integer.min = 0;
+    uinfo->value.integer.max = 0xffff;
+    return 0;
+}
+
+static int snd_emu10k1_efx_attn_get(struct snd_kcontrol *kcontrol,
+                                    struct snd_ctl_elem_value *ucontrol)
+{
+    struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+    struct snd_emu10k1_pcm_mixer *mix =
+        &emu->efx_pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
+    unsigned long flags;
+
+    spin_lock_irqsave(&emu->reg_lock, flags);
+    ucontrol->value.integer.value[0] = mix->attn[0];
+    spin_unlock_irqrestore(&emu->reg_lock, flags);
+    return 0;
+}
+
+static int snd_emu10k1_efx_attn_put(struct snd_kcontrol *kcontrol,
+                                    struct snd_ctl_elem_value *ucontrol)
+{
+    unsigned long flags;
+    struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+    int ch = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+    struct snd_emu10k1_pcm_mixer *mix = &emu->efx_pcm_mixer[ch];
+    int change = 0, val;
+
+    spin_lock_irqsave(&emu->reg_lock, flags);
+    val = ucontrol->value.integer.value[0] & 0xffff;
+    if (mix->attn[0] != val) {
+        mix->attn[0] = val;
+        change = 1;
+    }
+    if (change && mix->epcm) {
+        if (mix->epcm->voices[ch]) {
+            snd_emu10k1_ptr_write(emu, VTFT_VOLUMETARGET, mix->epcm->voices[ch]->number, mix->attn[0]);
+        }
+    }
+    spin_unlock_irqrestore(&emu->reg_lock, flags);
+    return change;
+}
+
+static struct snd_kcontrol_new snd_emu10k1_efx_attn_control =
+{
+    .access =	SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+    .iface =        SNDRV_CTL_ELEM_IFACE_PCM,
+    .name =         "Multichannel PCM Volume",
+    .count =	16,
+    .info =         snd_emu10k1_efx_attn_info,
+    .get =          snd_emu10k1_efx_attn_get,
+    .put =          snd_emu10k1_efx_attn_put
+};
+
+static int snd_emu10k1_shared_spdif_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *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_emu10k1_shared_spdif_get(struct snd_kcontrol *kcontrol,
+                                        struct snd_ctl_elem_value *ucontrol)
+{
+    struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+
+    if (emu->audigy)
+        ucontrol->value.integer.value[0] = inl(emu->port + A_IOCFG) & A_IOCFG_GPOUT0 ? 1 : 0;
+    else
+        ucontrol->value.integer.value[0] = inl(emu->port + HCFG) & HCFG_GPOUT0 ? 1 : 0;
+    return 0;
+}
+
+static int snd_emu10k1_shared_spdif_put(struct snd_kcontrol *kcontrol,
+                                        struct snd_ctl_elem_value *ucontrol)
+{
+    unsigned long flags;
+    struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+    unsigned int reg, val;
+    int change = 0;
+
+    spin_lock_irqsave(&emu->reg_lock, flags);
+    if (emu->audigy) {
+        reg = inl(emu->port + A_IOCFG);
+        val = ucontrol->value.integer.value[0] ? A_IOCFG_GPOUT0 : 0;
+        change = (reg & A_IOCFG_GPOUT0) != val;
+        if (change) {
+            reg &= ~A_IOCFG_GPOUT0;
+            reg |= val;
+            outl(reg | val, emu->port + A_IOCFG);
+        }
+    }
+    reg = inl(emu->port + HCFG);
+    val = ucontrol->value.integer.value[0] ? HCFG_GPOUT0 : 0;
+    change |= (reg & HCFG_GPOUT0) != val;
+    if (change) {
+        reg &= ~HCFG_GPOUT0;
+        reg |= val;
+        outl(reg | val, emu->port + HCFG);
+    }
+    spin_unlock_irqrestore(&emu->reg_lock, flags);
+    return change;
+}
+
+static struct snd_kcontrol_new snd_emu10k1_shared_spdif __devinitdata =
+{
+    .iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
+    .name =		"SB Live Analog/Digital Output Jack",
+    .info =		snd_emu10k1_shared_spdif_info,
+    .get =		snd_emu10k1_shared_spdif_get,
+    .put =		snd_emu10k1_shared_spdif_put
+};
+
+static struct snd_kcontrol_new snd_audigy_shared_spdif __devinitdata =
+{
+    .iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
+    .name =		"Audigy Analog/Digital Output Jack",
+    .info =		snd_emu10k1_shared_spdif_info,
+    .get =		snd_emu10k1_shared_spdif_get,
+    .put =		snd_emu10k1_shared_spdif_put
+};
+
+/*
+ */
+static void snd_emu10k1_mixer_free_ac97(struct snd_ac97 *ac97)
+{
+    struct snd_emu10k1 *emu = ac97->private_data;
+    emu->ac97 = NULL;
+}
+
+/*
+ */
+static int remove_ctl(struct snd_card *card, const char *name)
+{
+    struct snd_ctl_elem_id id;
+    memset(&id, 0, sizeof(id));
+    strcpy(id.name, name);
+    id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+    return snd_ctl_remove_id(card, &id);
+}
+
+static struct snd_kcontrol *ctl_find(struct snd_card *card, const char *name)
+{
+    struct snd_ctl_elem_id sid;
+    memset(&sid, 0, sizeof(sid));
+    strcpy(sid.name, name);
+    sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+    return snd_ctl_find_id(card, &sid);
+}
+
+static int rename_ctl(struct snd_card *card, const char *src, const char *dst)
+{
+    struct snd_kcontrol *kctl = ctl_find(card, src);
+    if (kctl) {
+        strcpy(kctl->id.name, dst);
+        return 0;
+    }
+    return -ENOENT;
+}
+
+int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu,
+                                int pcm_device, int multi_device)
+{
+    int err, pcm;
+    struct snd_kcontrol *kctl;
+    struct snd_card *card = emu->card;
+    char **c;
+    static char *emu10k1_remove_ctls[] = {
+        /* no AC97 mono, surround, center/lfe */
+        "Master Mono Playback Switch",
+        "Master Mono Playback Volume",
+        "PCM Out Path & Mute",
+        "Mono Output Select",
+        "Front Playback Switch",
+        "Front Playback Volume",
+        "Surround Playback Switch",
+        "Surround Playback Volume",
+        "Center Playback Switch",
+        "Center Playback Volume",
+        "LFE Playback Switch",
+        "LFE Playback Volume",
+        NULL
+    };
+    static char *emu10k1_rename_ctls[] = {
+        "Surround Digital Playback Volume", "Surround Playback Volume",
+        "Center Digital Playback Volume", "Center Playback Volume",
+        "LFE Digital Playback Volume", "LFE Playback Volume",
+        NULL
+    };
+    static char *audigy_remove_ctls[] = {
+        /* Master/PCM controls on ac97 of Audigy has no effect */
+        /* On the Audigy2 the AC97 playback is piped into
+         * the Philips ADC for 24bit capture */
+        "PCM Playback Switch",
+        "PCM Playback Volume",
+        "Master Mono Playback Switch",
+        "Master Mono Playback Volume",
+        "Master Playback Switch",
+        "Master Playback Volume",
+        "PCM Out Path & Mute",
+        "Mono Output Select",
+        /* remove unused AC97 capture controls */
+        "Capture Source",
+        "Capture Switch",
+        "Capture Volume",
+        "Mic Select",
+        "Video Playback Switch",
+        "Video Playback Volume",
+        "Mic Playback Switch",
+        "Mic Playback Volume",
+        NULL
+    };
+    static char *audigy_rename_ctls[] = {
+        /* use conventional names */
+        "Wave Playback Volume", "PCM Playback Volume",
+        /* "Wave Capture Volume", "PCM Capture Volume", */
+        "Wave Master Playback Volume", "Master Playback Volume",
+        "AMic Playback Volume", "Mic Playback Volume",
+        NULL
+    };
+
+    static char *audigy_remove_ctls_1361t_adc[] = {
+        /* On the Audigy2 the AC97 playback is piped into
+         * the Philips ADC for 24bit capture */
+        "PCM Playback Switch",
+        "PCM Playback Volume",
+        "Master Mono Playback Switch",
+        "Master Mono Playback Volume",
+        "Capture Source",
+        "Capture Switch",
+        "Capture Volume",
+        "Mic Capture Volume",
+        "Headphone Playback Switch",
+        "Headphone Playback Volume",
+        "3D Control - Center",
+        "3D Control - Depth",
+        "3D Control - Switch",
+        "Line2 Playback Volume",
+        "Line2 Capture Volume",
+        NULL
+    };
+    static char *audigy_rename_ctls_1361t_adc[] = {
+        "Master Playback Switch", "Master Capture Switch",
+        "Master Playback Volume", "Master Capture Volume",
+        "Wave Master Playback Volume", "Master Playback Volume",
+        "PC Speaker Playback Switch", "PC Speaker Capture Switch",
+        "PC Speaker Playback Volume", "PC Speaker Capture Volume",
+        "Phone Playback Switch", "Phone Capture Switch",
+        "Phone Playback Volume", "Phone Capture Volume",
+        "Mic Playback Switch", "Mic Capture Switch",
+        "Mic Playback Volume", "Mic Capture Volume",
+        "Line Playback Switch", "Line Capture Switch",
+        "Line Playback Volume", "Line Capture Volume",
+        "CD Playback Switch", "CD Capture Switch",
+        "CD Playback Volume", "CD Capture Volume",
+        "Aux Playback Switch", "Aux Capture Switch",
+        "Aux Playback Volume", "Aux Capture Volume",
+        "Video Playback Switch", "Video Capture Switch",
+        "Video Playback Volume", "Video Capture Volume",
+
+        NULL
+    };
+
+    if (emu->card_capabilities->ac97_chip) {
+        struct snd_ac97_bus *pbus;
+        struct snd_ac97_template ac97;
+        static struct snd_ac97_bus_ops ops = {
+            .write = snd_emu10k1_ac97_write,
+            .read = snd_emu10k1_ac97_read,
+        };
+
+        if ((err = snd_ac97_bus(emu->card, 0, &ops, NULL, &pbus)) < 0)
+            return err;
+        pbus->no_vra = 1; /* we don't need VRA */
+
+        memset(&ac97, 0, sizeof(ac97));
+        ac97.private_data = emu;
+        ac97.private_free = snd_emu10k1_mixer_free_ac97;
+        ac97.scaps = AC97_SCAP_NO_SPDIF;
+        if ((err = snd_ac97_mixer(pbus, &ac97, &emu->ac97)) < 0) {
+            if (emu->card_capabilities->ac97_chip == 1)
+                return err;
+            snd_printd(KERN_INFO "emu10k1: AC97 is optional on this board\n");
+            snd_printd(KERN_INFO"          Proceeding without ac97 mixers...\n");
+            snd_device_free(emu->card, pbus);
+            goto no_ac97; /* FIXME: get rid of ugly gotos.. */
+        }
+        if (emu->audigy) {
+            /* set master volume to 0 dB */
+            snd_ac97_write_cache(emu->ac97, AC97_MASTER, 0x0000);
+            /* set capture source to mic */
+            snd_ac97_write_cache(emu->ac97, AC97_REC_SEL, 0x0000);
+            if (emu->card_capabilities->adc_1361t)
+                c = audigy_remove_ctls_1361t_adc;
+            else
+                c = audigy_remove_ctls;
+        } else {
+            /*
+             * Credits for cards based on STAC9758:
+             *   James Courtier-Dutton <James@superbug.demon.co.uk>
+             *   Voluspa <voluspa@comhem.se>
+             */
+            if (emu->ac97->id == AC97_ID_STAC9758) {
+                emu->rear_ac97 = 1;
+                snd_emu10k1_ptr_write(emu, AC97SLOT, 0, AC97SLOT_CNTR|AC97SLOT_LFE|AC97SLOT_REAR_LEFT|AC97SLOT_REAR_RIGHT);
+            }
+            /* remove unused AC97 controls */
+            snd_ac97_write_cache(emu->ac97, AC97_SURROUND_MASTER, 0x0202);
+            snd_ac97_write_cache(emu->ac97, AC97_CENTER_LFE_MASTER, 0x0202);
+            c = emu10k1_remove_ctls;
+        }
+        for (; *c; c++)
+            remove_ctl(card, *c);
+    } else {
+    no_ac97:
+        if (emu->card_capabilities->ecard)
+            strcpy(emu->card->mixername, "EMU APS");
+        else if (emu->audigy)
+            strcpy(emu->card->mixername, "SB Audigy");
+        else
+            strcpy(emu->card->mixername, "Emu10k1");
+    }
+
+    if (emu->audigy)
+        if (emu->card_capabilities->adc_1361t)
+            c = audigy_rename_ctls_1361t_adc;
+        else
+            c = audigy_rename_ctls;
+    else
+        c = emu10k1_rename_ctls;
+    for (; *c; c += 2)
+        rename_ctl(card, c[0], c[1]);
+
+    if (emu->card_capabilities->subsystem == 0x20071102) {  /* Audigy 4 Pro */
+        rename_ctl(card, "Line2 Capture Volume", "Line1/Mic Capture Volume");
+        rename_ctl(card, "Analog Mix Capture Volume", "Line2 Capture Volume");
+        rename_ctl(card, "Aux2 Capture Volume", "Line3 Capture Volume");
+        rename_ctl(card, "Mic Capture Volume", "Unknown1 Capture Volume");
+        remove_ctl(card, "Headphone Playback Switch");
+        remove_ctl(card, "Headphone Playback Volume");
+        remove_ctl(card, "3D Control - Center");
+        remove_ctl(card, "3D Control - Depth");
+        remove_ctl(card, "3D Control - Switch");
+    }
+    if ((kctl = emu->ctl_send_routing = snd_ctl_new1(&snd_emu10k1_send_routing_control, emu)) == NULL)
+        return -ENOMEM;
+    kctl->id.device = pcm_device;
+    if ((err = snd_ctl_add(card, kctl)))
+        return err;
+    if ((kctl = emu->ctl_send_volume = snd_ctl_new1(&snd_emu10k1_send_volume_control, emu)) == NULL)
+        return -ENOMEM;
+    kctl->id.device = pcm_device;
+    if ((err = snd_ctl_add(card, kctl)))
+        return err;
+    if ((kctl = emu->ctl_attn = snd_ctl_new1(&snd_emu10k1_attn_control, emu)) == NULL)
+        return -ENOMEM;
+    kctl->id.device = pcm_device;
+    if ((err = snd_ctl_add(card, kctl)))
+        return err;
+
+    if ((kctl = emu->ctl_efx_send_routing = snd_ctl_new1(&snd_emu10k1_efx_send_routing_control, emu)) == NULL)
+        return -ENOMEM;
+    kctl->id.device = multi_device;
+    if ((err = snd_ctl_add(card, kctl)))
+        return err;
+
+    if ((kctl = emu->ctl_efx_send_volume = snd_ctl_new1(&snd_emu10k1_efx_send_volume_control, emu)) == NULL)
+        return -ENOMEM;
+    kctl->id.device = multi_device;
+    if ((err = snd_ctl_add(card, kctl)))
+        return err;
+
+    if ((kctl = emu->ctl_efx_attn = snd_ctl_new1(&snd_emu10k1_efx_attn_control, emu)) == NULL)
+        return -ENOMEM;
+    kctl->id.device = multi_device;
+    if ((err = snd_ctl_add(card, kctl)))
+        return err;
+
+    /* initialize the routing and volume table for each pcm playback stream */
+    for (pcm = 0; pcm < 32; pcm++) {
+        struct snd_emu10k1_pcm_mixer *mix;
+        int v;
+
+        mix = &emu->pcm_mixer[pcm];
+        mix->epcm = NULL;
+
+        for (v = 0; v < 4; v++)
+            mix->send_routing[0][v] =
+                mix->send_routing[1][v] =
+                mix->send_routing[2][v] = v;
+
+        memset(&mix->send_volume, 0, sizeof(mix->send_volume));
+        mix->send_volume[0][0] = mix->send_volume[0][1] =
+            mix->send_volume[1][0] = mix->send_volume[2][1] = 255;
+
+        mix->attn[0] = mix->attn[1] = mix->attn[2] = 0xffff;
+    }
+
+    /* initialize the routing and volume table for the multichannel playback stream */
+    for (pcm = 0; pcm < NUM_EFX_PLAYBACK; pcm++) {
+        struct snd_emu10k1_pcm_mixer *mix;
+        int v;
+
+        mix = &emu->efx_pcm_mixer[pcm];
+        mix->epcm = NULL;
+
+        mix->send_routing[0][0] = pcm;
+        mix->send_routing[0][1] = (pcm == 0) ? 1 : 0;
+        for (v = 0; v < 2; v++)
+            mix->send_routing[0][2+v] = 13+v;
+        if (emu->audigy)
+            for (v = 0; v < 4; v++)
+                mix->send_routing[0][4+v] = 60+v;
+
+        memset(&mix->send_volume, 0, sizeof(mix->send_volume));
+        mix->send_volume[0][0]  = 255;
+
+        mix->attn[0] = 0xffff;
+    }
+
+    if (! emu->card_capabilities->ecard) { /* FIXME: APS has these controls? */
+        /* sb live! and audigy */
+        if ((kctl = snd_ctl_new1(&snd_emu10k1_spdif_mask_control, emu)) == NULL)
+            return -ENOMEM;
+        if (!emu->audigy)
+            kctl->id.device = emu->pcm_efx->device;
+        if ((err = snd_ctl_add(card, kctl)))
+            return err;
+        if ((kctl = snd_ctl_new1(&snd_emu10k1_spdif_control, emu)) == NULL)
+            return -ENOMEM;
+        if (!emu->audigy)
+            kctl->id.device = emu->pcm_efx->device;
+        if ((err = snd_ctl_add(card, kctl)))
+            return err;
+    }
+
+    if ( emu->card_capabilities->emu1010) {
+        ;  /* Disable the snd_audigy_spdif_shared_spdif */
+    } else if (emu->audigy) {
+        if ((kctl = snd_ctl_new1(&snd_audigy_shared_spdif, emu)) == NULL)
+            return -ENOMEM;
+        if ((err = snd_ctl_add(card, kctl)))
+            return err;
+#if 0
+        if ((kctl = snd_ctl_new1(&snd_audigy_spdif_output_rate, emu)) == NULL)
+            return -ENOMEM;
+        if ((err = snd_ctl_add(card, kctl)))
+            return err;
+#endif
+    } else if (! emu->card_capabilities->ecard) {
+        /* sb live! */
+        if ((kctl = snd_ctl_new1(&snd_emu10k1_shared_spdif, emu)) == NULL)
+            return -ENOMEM;
+        if ((err = snd_ctl_add(card, kctl)))
+            return err;
+    }
+    if (emu->card_capabilities->ca0151_chip) { /* P16V */
+        if ((err = snd_p16v_mixer(emu)))
+            return err;
+    }
+    if ( emu->card_capabilities->emu1010) {
+        int i;
+
+        for (i = 0; i < ARRAY_SIZE(snd_emu1010_output_enum_ctls); i++) {
+            err = snd_ctl_add(card, snd_ctl_new1(&snd_emu1010_output_enum_ctls[i], emu));
+            if (err < 0)
+                return err;
+        }
+        for (i = 0; i < ARRAY_SIZE(snd_emu1010_input_enum_ctls); i++) {
+            err = snd_ctl_add(card, snd_ctl_new1(&snd_emu1010_input_enum_ctls[i], emu));
+            if (err < 0)
+                return err;
+        }
+        for (i = 0; i < ARRAY_SIZE(snd_emu1010_adc_pads); i++) {
+            err = snd_ctl_add(card, snd_ctl_new1(&snd_emu1010_adc_pads[i], emu));
+            if (err < 0)
+                return err;
+        }
+        for (i = 0; i < ARRAY_SIZE(snd_emu1010_dac_pads); i++) {
+            err = snd_ctl_add(card, snd_ctl_new1(&snd_emu1010_dac_pads[i], emu));
+            if (err < 0)
+                return err;
+        }
+        err = snd_ctl_add(card, snd_ctl_new1(&snd_emu1010_internal_clock, emu));
+        if (err < 0)
+            return err;
+    }
+
+    return 0;
+}
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/emu10k1/emumpu401.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/emu10k1/emumpu401.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/emu10k1/emumpu401.c	(revision 91)
@@ -0,0 +1,389 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  Routines for control of EMU10K1 MPU-401 in UART mode
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/emu10k1.h>
+
+#define EMU10K1_MIDI_MODE_INPUT		(1<<0)
+#define EMU10K1_MIDI_MODE_OUTPUT	(1<<1)
+
+static inline unsigned char mpu401_read(struct snd_emu10k1 *emu, struct snd_emu10k1_midi *mpu, int idx)
+{
+    if (emu->audigy)
+        return (unsigned char)snd_emu10k1_ptr_read(emu, mpu->port + idx, 0);
+    else
+        return inb(emu->port + mpu->port + idx);
+}
+
+static inline void mpu401_write(struct snd_emu10k1 *emu, struct snd_emu10k1_midi *mpu, int data, int idx)
+{
+    if (emu->audigy)
+        snd_emu10k1_ptr_write(emu, mpu->port + idx, 0, data);
+    else
+        outb(data, emu->port + mpu->port + idx);
+}
+
+#define mpu401_write_data(emu, mpu, data)	mpu401_write(emu, mpu, data, 0)
+#define mpu401_write_cmd(emu, mpu, data)	mpu401_write(emu, mpu, data, 1)
+#define mpu401_read_data(emu, mpu)		mpu401_read(emu, mpu, 0)
+#define mpu401_read_stat(emu, mpu)		mpu401_read(emu, mpu, 1)
+
+#define mpu401_input_avail(emu,mpu)	(!(mpu401_read_stat(emu,mpu) & 0x80))
+#define mpu401_output_ready(emu,mpu)	(!(mpu401_read_stat(emu,mpu) & 0x40))
+
+#define MPU401_RESET		0xff
+#define MPU401_ENTER_UART	0x3f
+#define MPU401_ACK		0xfe
+
+static void mpu401_clear_rx(struct snd_emu10k1 *emu, struct snd_emu10k1_midi *mpu)
+{
+    int timeout = 100000;
+    for (; timeout > 0 && mpu401_input_avail(emu, mpu); timeout--)
+        mpu401_read_data(emu, mpu);
+#ifdef CONFIG_SND_DEBUG
+    if (timeout <= 0)
+        snd_printk(KERN_ERR "cmd: clear rx timeout (status = 0x%x)\n", mpu401_read_stat(emu, mpu));
+#endif
+}
+
+/*
+
+*/
+
+static void do_emu10k1_midi_interrupt(struct snd_emu10k1 *emu, struct snd_emu10k1_midi *midi, unsigned int status)
+{
+    unsigned char byte;
+
+    if (midi->rmidi == NULL) {
+        snd_emu10k1_intr_disable(emu, midi->tx_enable | midi->rx_enable);
+        return;
+    }
+
+    spin_lock(&midi->input_lock);
+    if ((status & midi->ipr_rx) && mpu401_input_avail(emu, midi)) {
+        if (!(midi->midi_mode & EMU10K1_MIDI_MODE_INPUT)) {
+            mpu401_clear_rx(emu, midi);
+        } else {
+            byte = mpu401_read_data(emu, midi);
+            if (midi->substream_input)
+                snd_rawmidi_receive(midi->substream_input, &byte, 1);
+        }
+    }
+    spin_unlock(&midi->input_lock);
+
+    spin_lock(&midi->output_lock);
+    if ((status & midi->ipr_tx) && mpu401_output_ready(emu, midi)) {
+        if (midi->substream_output &&
+            snd_rawmidi_transmit(midi->substream_output, &byte, 1) == 1) {
+            mpu401_write_data(emu, midi, byte);
+        } else {
+            snd_emu10k1_intr_disable(emu, midi->tx_enable);
+        }
+    }
+    spin_unlock(&midi->output_lock);
+}
+
+static void snd_emu10k1_midi_interrupt(struct snd_emu10k1 *emu, unsigned int status)
+{
+    do_emu10k1_midi_interrupt(emu, &emu->midi, status);
+}
+
+static void snd_emu10k1_midi_interrupt2(struct snd_emu10k1 *emu, unsigned int status)
+{
+    do_emu10k1_midi_interrupt(emu, &emu->midi2, status);
+}
+
+static int snd_emu10k1_midi_cmd(struct snd_emu10k1 * emu, struct snd_emu10k1_midi *midi, unsigned char cmd, int ack)
+{
+    unsigned long flags;
+    int timeout, ok;
+
+    spin_lock_irqsave(&midi->input_lock, flags);
+    mpu401_write_data(emu, midi, 0x00);
+    /* mpu401_clear_rx(emu, midi); */
+
+    mpu401_write_cmd(emu, midi, cmd);
+    if (ack) {
+        ok = 0;
+        timeout = 10000;
+        while (!ok && timeout-- > 0) {
+            if (mpu401_input_avail(emu, midi)) {
+                if (mpu401_read_data(emu, midi) == MPU401_ACK)
+                    ok = 1;
+            }
+        }
+        if (!ok && mpu401_read_data(emu, midi) == MPU401_ACK)
+            ok = 1;
+    } else {
+        ok = 1;
+    }
+    spin_unlock_irqrestore(&midi->input_lock, flags);
+    if (!ok) {
+        snd_printk(KERN_ERR "midi_cmd: 0x%x failed at 0x%lx (status = 0x%x, data = 0x%x)!!!\n",
+                   cmd, emu->port,
+                   mpu401_read_stat(emu, midi),
+                   mpu401_read_data(emu, midi));
+        return 1;
+    }
+    return 0;
+}
+
+static int snd_emu10k1_midi_input_open(struct snd_rawmidi_substream *substream)
+{
+    struct snd_emu10k1 *emu;
+    struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data;
+    unsigned long flags;
+
+    emu = midi->emu;
+    snd_assert(emu, return -ENXIO);
+    spin_lock_irqsave(&midi->open_lock, flags);
+    midi->midi_mode |= EMU10K1_MIDI_MODE_INPUT;
+    midi->substream_input = substream;
+    if (!(midi->midi_mode & EMU10K1_MIDI_MODE_OUTPUT)) {
+        spin_unlock_irqrestore(&midi->open_lock, flags);
+        if (snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 1))
+            goto error_out;
+        if (snd_emu10k1_midi_cmd(emu, midi, MPU401_ENTER_UART, 1))
+            goto error_out;
+    } else {
+        spin_unlock_irqrestore(&midi->open_lock, flags);
+    }
+    return 0;
+
+    error_out:
+        return -EIO;
+}
+
+static int snd_emu10k1_midi_output_open(struct snd_rawmidi_substream *substream)
+{
+    struct snd_emu10k1 *emu;
+    struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data;
+    unsigned long flags;
+
+    emu = midi->emu;
+    snd_assert(emu, return -ENXIO);
+    spin_lock_irqsave(&midi->open_lock, flags);
+    midi->midi_mode |= EMU10K1_MIDI_MODE_OUTPUT;
+    midi->substream_output = substream;
+    if (!(midi->midi_mode & EMU10K1_MIDI_MODE_INPUT)) {
+        spin_unlock_irqrestore(&midi->open_lock, flags);
+        if (snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 1))
+            goto error_out;
+        if (snd_emu10k1_midi_cmd(emu, midi, MPU401_ENTER_UART, 1))
+            goto error_out;
+    } else {
+        spin_unlock_irqrestore(&midi->open_lock, flags);
+    }
+    return 0;
+
+    error_out:
+        return -EIO;
+}
+
+static int snd_emu10k1_midi_input_close(struct snd_rawmidi_substream *substream)
+{
+    struct snd_emu10k1 *emu;
+    struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data;
+    unsigned long flags;
+    int err = 0;
+
+    emu = midi->emu;
+    snd_assert(emu, return -ENXIO);
+    spin_lock_irqsave(&midi->open_lock, flags);
+    snd_emu10k1_intr_disable(emu, midi->rx_enable);
+    midi->midi_mode &= ~EMU10K1_MIDI_MODE_INPUT;
+    midi->substream_input = NULL;
+    if (!(midi->midi_mode & EMU10K1_MIDI_MODE_OUTPUT)) {
+        spin_unlock_irqrestore(&midi->open_lock, flags);
+        err = snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 0);
+    } else {
+        spin_unlock_irqrestore(&midi->open_lock, flags);
+    }
+    return err;
+}
+
+static int snd_emu10k1_midi_output_close(struct snd_rawmidi_substream *substream)
+{
+    struct snd_emu10k1 *emu;
+    struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data;
+    unsigned long flags;
+    int err = 0;
+
+    emu = midi->emu;
+    snd_assert(emu, return -ENXIO);
+    spin_lock_irqsave(&midi->open_lock, flags);
+    snd_emu10k1_intr_disable(emu, midi->tx_enable);
+    midi->midi_mode &= ~EMU10K1_MIDI_MODE_OUTPUT;
+    midi->substream_output = NULL;
+    if (!(midi->midi_mode & EMU10K1_MIDI_MODE_INPUT)) {
+        spin_unlock_irqrestore(&midi->open_lock, flags);
+        err = snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 0);
+    } else {
+        spin_unlock_irqrestore(&midi->open_lock, flags);
+    }
+    return err;
+}
+
+static void snd_emu10k1_midi_input_trigger(struct snd_rawmidi_substream *substream, int up)
+{
+    struct snd_emu10k1 *emu;
+    struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data;
+    emu = midi->emu;
+    snd_assert(emu, return);
+
+    if (up)
+        snd_emu10k1_intr_enable(emu, midi->rx_enable);
+    else
+        snd_emu10k1_intr_disable(emu, midi->rx_enable);
+}
+
+static void snd_emu10k1_midi_output_trigger(struct snd_rawmidi_substream *substream, int up)
+{
+    struct snd_emu10k1 *emu;
+    struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data;
+    unsigned long flags;
+
+    emu = midi->emu;
+    snd_assert(emu, return);
+
+    if (up) {
+        int max = 4;
+        unsigned char byte;
+
+        /* try to send some amount of bytes here before interrupts */
+        spin_lock_irqsave(&midi->output_lock, flags);
+        while (max > 0) {
+            if (mpu401_output_ready(emu, midi)) {
+                if (!(midi->midi_mode & EMU10K1_MIDI_MODE_OUTPUT) ||
+                    snd_rawmidi_transmit(substream, &byte, 1) != 1) {
+                    /* no more data */
+                    spin_unlock_irqrestore(&midi->output_lock, flags);
+                    return;
+                }
+                mpu401_write_data(emu, midi, byte);
+                max--;
+            } else {
+                break;
+            }
+        }
+        spin_unlock_irqrestore(&midi->output_lock, flags);
+        snd_emu10k1_intr_enable(emu, midi->tx_enable);
+    } else {
+        snd_emu10k1_intr_disable(emu, midi->tx_enable);
+    }
+}
+
+/*
+
+*/
+
+static struct snd_rawmidi_ops snd_emu10k1_midi_output =
+{
+    .open =		snd_emu10k1_midi_output_open,
+    .close =	snd_emu10k1_midi_output_close,
+    .trigger =	snd_emu10k1_midi_output_trigger,
+};
+
+static struct snd_rawmidi_ops snd_emu10k1_midi_input =
+{
+    .open =		snd_emu10k1_midi_input_open,
+    .close =	snd_emu10k1_midi_input_close,
+    .trigger =	snd_emu10k1_midi_input_trigger,
+};
+
+static void snd_emu10k1_midi_free(struct snd_rawmidi *rmidi)
+{
+    struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)rmidi->private_data;
+    midi->interrupt = NULL;
+    midi->rmidi = NULL;
+}
+
+static int __devinit emu10k1_midi_init(struct snd_emu10k1 *emu, struct snd_emu10k1_midi *midi, int device, char *name)
+{
+    struct snd_rawmidi *rmidi;
+    int err;
+
+    if ((err = snd_rawmidi_new(emu->card, name, device, 1, 1, &rmidi)) < 0)
+        return err;
+    midi->emu = emu;
+    spin_lock_init(&midi->open_lock);
+    spin_lock_init(&midi->input_lock);
+    spin_lock_init(&midi->output_lock);
+    strcpy(rmidi->name, name);
+    snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_emu10k1_midi_output);
+    snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_emu10k1_midi_input);
+    rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT |
+        SNDRV_RAWMIDI_INFO_INPUT |
+        SNDRV_RAWMIDI_INFO_DUPLEX;
+    rmidi->private_data = midi;
+    rmidi->private_free = snd_emu10k1_midi_free;
+    midi->rmidi = rmidi;
+    return 0;
+}
+
+int __devinit snd_emu10k1_midi(struct snd_emu10k1 *emu)
+{
+    struct snd_emu10k1_midi *midi = &emu->midi;
+    int err;
+
+    if ((err = emu10k1_midi_init(emu, midi, 0, "EMU10K1 MPU-401 (UART)")) < 0)
+        return err;
+
+    midi->tx_enable = INTE_MIDITXENABLE;
+    midi->rx_enable = INTE_MIDIRXENABLE;
+    midi->port = MUDATA;
+    midi->ipr_tx = IPR_MIDITRANSBUFEMPTY;
+    midi->ipr_rx = IPR_MIDIRECVBUFEMPTY;
+    midi->interrupt = snd_emu10k1_midi_interrupt;
+    return 0;
+}
+
+int __devinit snd_emu10k1_audigy_midi(struct snd_emu10k1 *emu)
+{
+    struct snd_emu10k1_midi *midi;
+    int err;
+
+    midi = &emu->midi;
+    if ((err = emu10k1_midi_init(emu, midi, 0, "Audigy MPU-401 (UART)")) < 0)
+        return err;
+
+    midi->tx_enable = INTE_MIDITXENABLE;
+    midi->rx_enable = INTE_MIDIRXENABLE;
+    midi->port = A_MUDATA1;
+    midi->ipr_tx = IPR_MIDITRANSBUFEMPTY;
+    midi->ipr_rx = IPR_MIDIRECVBUFEMPTY;
+    midi->interrupt = snd_emu10k1_midi_interrupt;
+
+    midi = &emu->midi2;
+    if ((err = emu10k1_midi_init(emu, midi, 1, "Audigy MPU-401 #2")) < 0)
+        return err;
+
+    midi->tx_enable = INTE_A_MIDITXENABLE2;
+    midi->rx_enable = INTE_A_MIDIRXENABLE2;
+    midi->port = A_MUDATA2;
+    midi->ipr_tx = IPR_A_MIDITRANSBUFEMPTY2;
+    midi->ipr_rx = IPR_A_MIDIRECVBUFEMPTY2;
+    midi->interrupt = snd_emu10k1_midi_interrupt2;
+    return 0;
+}
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/emu10k1/emupcm.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/emu10k1/emupcm.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/emu10k1/emupcm.c	(revision 91)
@@ -0,0 +1,1804 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *                   Creative Labs, Inc.
+ *  Routines for control of EMU10K1 chips / PCM routines
+ *  Multichannel PCM support Copyright (c) Lee Revell <rlrevell@joe-job.com>
+ *
+ *  BUGS:
+ *    --
+ *
+ *  TODO:
+ *    --
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <linux/init.h>
+#include <sound/core.h>
+#include <sound/emu10k1.h>
+
+static void snd_emu10k1_pcm_interrupt(struct snd_emu10k1 *emu,
+				      struct snd_emu10k1_voice *voice)
+{
+	struct snd_emu10k1_pcm *epcm;
+
+	if ((epcm = voice->epcm) == NULL)
+		return;
+	if (epcm->substream == NULL)
+		return;
+#if 0
+	printk("IRQ: position = 0x%x, period = 0x%x, size = 0x%x\n",
+			epcm->substream->runtime->hw->pointer(emu, epcm->substream),
+			snd_pcm_lib_period_bytes(epcm->substream),
+			snd_pcm_lib_buffer_bytes(epcm->substream));
+#endif
+	snd_pcm_period_elapsed(epcm->substream);
+}
+
+static void snd_emu10k1_pcm_ac97adc_interrupt(struct snd_emu10k1 *emu,
+					      unsigned int status)
+{
+#if 0
+	if (status & IPR_ADCBUFHALFFULL) {
+		if (emu->pcm_capture_substream->runtime->mode == SNDRV_PCM_MODE_FRAME)
+			return;
+	}
+#endif
+	snd_pcm_period_elapsed(emu->pcm_capture_substream);
+}
+
+static void snd_emu10k1_pcm_ac97mic_interrupt(struct snd_emu10k1 *emu,
+					      unsigned int status)
+{
+#if 0
+	if (status & IPR_MICBUFHALFFULL) {
+		if (emu->pcm_capture_mic_substream->runtime->mode == SNDRV_PCM_MODE_FRAME)
+			return;
+	}
+#endif
+	snd_pcm_period_elapsed(emu->pcm_capture_mic_substream);
+}
+
+static void snd_emu10k1_pcm_efx_interrupt(struct snd_emu10k1 *emu,
+					  unsigned int status)
+{
+#if 0
+	if (status & IPR_EFXBUFHALFFULL) {
+		if (emu->pcm_capture_efx_substream->runtime->mode == SNDRV_PCM_MODE_FRAME)
+			return;
+	}
+#endif
+	snd_pcm_period_elapsed(emu->pcm_capture_efx_substream);
+}	 
+
+static snd_pcm_uframes_t snd_emu10k1_efx_playback_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_emu10k1_pcm *epcm = runtime->private_data;
+	unsigned int ptr;
+
+	if (!epcm->running)
+		return 0;
+	ptr = snd_emu10k1_ptr_read(emu, CCCA, epcm->voices[0]->number) & 0x00ffffff;
+	ptr += runtime->buffer_size;
+	ptr -= epcm->ccca_start_addr;
+	ptr %= runtime->buffer_size;
+
+	return ptr;
+}
+
+static int snd_emu10k1_pcm_channel_alloc(struct snd_emu10k1_pcm * epcm, int voices)
+{
+	int err, i;
+
+	if (epcm->voices[1] != NULL && voices < 2) {
+		snd_emu10k1_voice_free(epcm->emu, epcm->voices[1]);
+		epcm->voices[1] = NULL;
+	}
+	for (i = 0; i < voices; i++) {
+		if (epcm->voices[i] == NULL)
+			break;
+	}
+	if (i == voices)
+		return 0; /* already allocated */
+
+	for (i = 0; i < ARRAY_SIZE(epcm->voices); i++) {
+		if (epcm->voices[i]) {
+			snd_emu10k1_voice_free(epcm->emu, epcm->voices[i]);
+			epcm->voices[i] = NULL;
+		}
+	}
+	err = snd_emu10k1_voice_alloc(epcm->emu,
+				      epcm->type == PLAYBACK_EMUVOICE ? EMU10K1_PCM : EMU10K1_EFX,
+				      voices,
+				      &epcm->voices[0]);
+	
+	if (err < 0)
+		return err;
+	epcm->voices[0]->epcm = epcm;
+	if (voices > 1) {
+		for (i = 1; i < voices; i++) {
+			epcm->voices[i] = &epcm->emu->voices[epcm->voices[0]->number + i];
+			epcm->voices[i]->epcm = epcm;
+		}
+	}
+	if (epcm->extra == NULL) {
+		err = snd_emu10k1_voice_alloc(epcm->emu,
+					      epcm->type == PLAYBACK_EMUVOICE ? EMU10K1_PCM : EMU10K1_EFX,
+					      1,
+					      &epcm->extra);
+		if (err < 0) {
+			/* printk("pcm_channel_alloc: failed extra: voices=%d, frame=%d\n", voices, frame); */
+			for (i = 0; i < voices; i++) {
+				snd_emu10k1_voice_free(epcm->emu, epcm->voices[i]);
+				epcm->voices[i] = NULL;
+			}
+			return err;
+		}
+		epcm->extra->epcm = epcm;
+		epcm->extra->interrupt = snd_emu10k1_pcm_interrupt;
+	}
+	return 0;
+}
+
+static unsigned int capture_period_sizes[31] = {
+	384,	448,	512,	640,
+	384*2,	448*2,	512*2,	640*2,
+	384*4,	448*4,	512*4,	640*4,
+	384*8,	448*8,	512*8,	640*8,
+	384*16,	448*16,	512*16,	640*16,
+	384*32,	448*32,	512*32,	640*32,
+	384*64,	448*64,	512*64,	640*64,
+	384*128,448*128,512*128
+};
+
+static struct snd_pcm_hw_constraint_list hw_constraints_capture_period_sizes = {
+	.count = 31,
+	.list = capture_period_sizes,
+	.mask = 0
+};
+
+static unsigned int capture_rates[8] = {
+	8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000
+};
+
+static struct snd_pcm_hw_constraint_list hw_constraints_capture_rates = {
+	.count = 8,
+	.list = capture_rates,
+	.mask = 0
+};
+
+static unsigned int snd_emu10k1_capture_rate_reg(unsigned int rate)
+{
+	switch (rate) {
+	case 8000:	return ADCCR_SAMPLERATE_8;
+	case 11025:	return ADCCR_SAMPLERATE_11;
+	case 16000:	return ADCCR_SAMPLERATE_16;
+	case 22050:	return ADCCR_SAMPLERATE_22;
+	case 24000:	return ADCCR_SAMPLERATE_24;
+	case 32000:	return ADCCR_SAMPLERATE_32;
+	case 44100:	return ADCCR_SAMPLERATE_44;
+	case 48000:	return ADCCR_SAMPLERATE_48;
+	default:
+			snd_BUG();
+			return ADCCR_SAMPLERATE_8;
+	}
+}
+
+static unsigned int snd_emu10k1_audigy_capture_rate_reg(unsigned int rate)
+{
+	switch (rate) {
+	case 8000:	return A_ADCCR_SAMPLERATE_8;
+	case 11025:	return A_ADCCR_SAMPLERATE_11;
+	case 12000:	return A_ADCCR_SAMPLERATE_12; /* really supported? */
+	case 16000:	return ADCCR_SAMPLERATE_16;
+	case 22050:	return ADCCR_SAMPLERATE_22;
+	case 24000:	return ADCCR_SAMPLERATE_24;
+	case 32000:	return ADCCR_SAMPLERATE_32;
+	case 44100:	return ADCCR_SAMPLERATE_44;
+	case 48000:	return ADCCR_SAMPLERATE_48;
+	default:
+			snd_BUG();
+			return A_ADCCR_SAMPLERATE_8;
+	}
+}
+
+static unsigned int emu10k1_calc_pitch_target(unsigned int rate)
+{
+	unsigned int pitch_target;
+
+	pitch_target = (rate << 8) / 375;
+	pitch_target = (pitch_target >> 1) + (pitch_target & 1);
+	return pitch_target;
+}
+
+#define PITCH_48000 0x00004000
+#define PITCH_96000 0x00008000
+#define PITCH_85000 0x00007155
+#define PITCH_80726 0x00006ba2
+#define PITCH_67882 0x00005a82
+#define PITCH_57081 0x00004c1c
+
+static unsigned int emu10k1_select_interprom(unsigned int pitch_target)
+{
+	if (pitch_target == PITCH_48000)
+		return CCCA_INTERPROM_0;
+	else if (pitch_target < PITCH_48000)
+		return CCCA_INTERPROM_1;
+	else if (pitch_target >= PITCH_96000)
+		return CCCA_INTERPROM_0;
+	else if (pitch_target >= PITCH_85000)
+		return CCCA_INTERPROM_6;
+	else if (pitch_target >= PITCH_80726)
+		return CCCA_INTERPROM_5;
+	else if (pitch_target >= PITCH_67882)
+		return CCCA_INTERPROM_4;
+	else if (pitch_target >= PITCH_57081)
+		return CCCA_INTERPROM_3;
+	else  
+		return CCCA_INTERPROM_2;
+}
+
+/*
+ * calculate cache invalidate size 
+ *
+ * stereo: channel is stereo
+ * w_16: using 16bit samples
+ *
+ * returns: cache invalidate size in samples
+ */
+static inline int emu10k1_ccis(int stereo, int w_16)
+{
+	if (w_16) {
+		return stereo ? 24 : 26;
+	} else {
+		return stereo ? 24*2 : 26*2;
+	}
+}
+
+static void snd_emu10k1_pcm_init_voice(struct snd_emu10k1 *emu,
+				       int master, int extra,
+				       struct snd_emu10k1_voice *evoice,
+				       unsigned int start_addr,
+				       unsigned int end_addr,
+				       struct snd_emu10k1_pcm_mixer *mix)
+{
+	struct snd_pcm_substream *substream = evoice->epcm->substream;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	unsigned int silent_page, tmp;
+	int voice, stereo, w_16;
+	unsigned char attn, send_amount[8];
+	unsigned char send_routing[8];
+	unsigned long flags;
+	unsigned int pitch_target;
+	unsigned int ccis;
+
+	voice = evoice->number;
+	stereo = runtime->channels == 2;
+	w_16 = snd_pcm_format_width(runtime->format) == 16;
+
+	if (!extra && stereo) {
+		start_addr >>= 1;
+		end_addr >>= 1;
+	}
+	if (w_16) {
+		start_addr >>= 1;
+		end_addr >>= 1;
+	}
+
+	spin_lock_irqsave(&emu->reg_lock, flags);
+
+	/* volume parameters */
+	if (extra) {
+		attn = 0;
+		memset(send_routing, 0, sizeof(send_routing));
+		send_routing[0] = 0;
+		send_routing[1] = 1;
+		send_routing[2] = 2;
+		send_routing[3] = 3;
+		memset(send_amount, 0, sizeof(send_amount));
+	} else {
+		/* mono, left, right (master voice = left) */
+		tmp = stereo ? (master ? 1 : 2) : 0;
+		memcpy(send_routing, &mix->send_routing[tmp][0], 8);
+		memcpy(send_amount, &mix->send_volume[tmp][0], 8);
+	}
+
+	ccis = emu10k1_ccis(stereo, w_16);
+	
+	if (master) {
+		evoice->epcm->ccca_start_addr = start_addr + ccis;
+		if (extra) {
+			start_addr += ccis;
+			end_addr += ccis;
+		}
+		if (stereo && !extra) {
+			snd_emu10k1_ptr_write(emu, CPF, voice, CPF_STEREO_MASK);
+			snd_emu10k1_ptr_write(emu, CPF, (voice + 1), CPF_STEREO_MASK);
+		} else {
+			snd_emu10k1_ptr_write(emu, CPF, voice, 0);
+		}
+	}
+
+	/* setup routing */
+	if (emu->audigy) {
+		snd_emu10k1_ptr_write(emu, A_FXRT1, voice,
+				      snd_emu10k1_compose_audigy_fxrt1(send_routing));
+		snd_emu10k1_ptr_write(emu, A_FXRT2, voice,
+				      snd_emu10k1_compose_audigy_fxrt2(send_routing));
+		snd_emu10k1_ptr_write(emu, A_SENDAMOUNTS, voice,
+				      ((unsigned int)send_amount[4] << 24) |
+				      ((unsigned int)send_amount[5] << 16) |
+				      ((unsigned int)send_amount[6] << 8) |
+				      (unsigned int)send_amount[7]);
+	} else
+		snd_emu10k1_ptr_write(emu, FXRT, voice,
+				      snd_emu10k1_compose_send_routing(send_routing));
+	/* Stop CA */
+	/* Assumption that PT is already 0 so no harm overwriting */
+	snd_emu10k1_ptr_write(emu, PTRX, voice, (send_amount[0] << 8) | send_amount[1]);
+	snd_emu10k1_ptr_write(emu, DSL, voice, end_addr | (send_amount[3] << 24));
+	snd_emu10k1_ptr_write(emu, PSST, voice, start_addr | (send_amount[2] << 24));
+	if (emu->card_capabilities->emu1010)
+		pitch_target = PITCH_48000; /* Disable interpolators on emu1010 card */
+	else 
+		pitch_target = emu10k1_calc_pitch_target(runtime->rate);
+	if (extra)
+		snd_emu10k1_ptr_write(emu, CCCA, voice, start_addr |
+			      emu10k1_select_interprom(pitch_target) |
+			      (w_16 ? 0 : CCCA_8BITSELECT));
+	else
+		snd_emu10k1_ptr_write(emu, CCCA, voice, (start_addr + ccis) |
+			      emu10k1_select_interprom(pitch_target) |
+			      (w_16 ? 0 : CCCA_8BITSELECT));
+	/* Clear filter delay memory */
+	snd_emu10k1_ptr_write(emu, Z1, voice, 0);
+	snd_emu10k1_ptr_write(emu, Z2, voice, 0);
+	/* invalidate maps */
+	silent_page = ((unsigned int)emu->silent_page.addr << 1) | MAP_PTI_MASK;
+	snd_emu10k1_ptr_write(emu, MAPA, voice, silent_page);
+	snd_emu10k1_ptr_write(emu, MAPB, voice, silent_page);
+	/* modulation envelope */
+	snd_emu10k1_ptr_write(emu, CVCF, voice, 0xffff);
+	snd_emu10k1_ptr_write(emu, VTFT, voice, 0xffff);
+	snd_emu10k1_ptr_write(emu, ATKHLDM, voice, 0);
+	snd_emu10k1_ptr_write(emu, DCYSUSM, voice, 0x007f);
+	snd_emu10k1_ptr_write(emu, LFOVAL1, voice, 0x8000);
+	snd_emu10k1_ptr_write(emu, LFOVAL2, voice, 0x8000);
+	snd_emu10k1_ptr_write(emu, FMMOD, voice, 0);
+	snd_emu10k1_ptr_write(emu, TREMFRQ, voice, 0);
+	snd_emu10k1_ptr_write(emu, FM2FRQ2, voice, 0);
+	snd_emu10k1_ptr_write(emu, ENVVAL, voice, 0x8000);
+	/* volume envelope */
+	snd_emu10k1_ptr_write(emu, ATKHLDV, voice, 0x7f7f);
+	snd_emu10k1_ptr_write(emu, ENVVOL, voice, 0x0000);
+	/* filter envelope */
+	snd_emu10k1_ptr_write(emu, PEFE_FILTERAMOUNT, voice, 0x7f);
+	/* pitch envelope */
+	snd_emu10k1_ptr_write(emu, PEFE_PITCHAMOUNT, voice, 0);
+
+	spin_unlock_irqrestore(&emu->reg_lock, flags);
+}
+
+static int snd_emu10k1_playback_hw_params(struct snd_pcm_substream *substream,
+					  struct snd_pcm_hw_params *hw_params)
+{
+	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_emu10k1_pcm *epcm = runtime->private_data;
+	int err;
+
+	if ((err = snd_emu10k1_pcm_channel_alloc(epcm, params_channels(hw_params))) < 0)
+		return err;
+	if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
+		return err;
+	if (err > 0) {	/* change */
+		int mapped;
+		if (epcm->memblk != NULL)
+			snd_emu10k1_free_pages(emu, epcm->memblk);
+		epcm->memblk = snd_emu10k1_alloc_pages(emu, substream);
+		epcm->start_addr = 0;
+		if (! epcm->memblk)
+			return -ENOMEM;
+		mapped = ((struct snd_emu10k1_memblk *)epcm->memblk)->mapped_page;
+		if (mapped < 0)
+			return -ENOMEM;
+		epcm->start_addr = mapped << PAGE_SHIFT;
+	}
+	return 0;
+}
+
+static int snd_emu10k1_playback_hw_free(struct snd_pcm_substream *substream)
+{
+	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_emu10k1_pcm *epcm;
+
+	if (runtime->private_data == NULL)
+		return 0;
+	epcm = runtime->private_data;
+	if (epcm->extra) {
+		snd_emu10k1_voice_free(epcm->emu, epcm->extra);
+		epcm->extra = NULL;
+	}
+	if (epcm->voices[1]) {
+		snd_emu10k1_voice_free(epcm->emu, epcm->voices[1]);
+		epcm->voices[1] = NULL;
+	}
+	if (epcm->voices[0]) {
+		snd_emu10k1_voice_free(epcm->emu, epcm->voices[0]);
+		epcm->voices[0] = NULL;
+	}
+	if (epcm->memblk) {
+		snd_emu10k1_free_pages(emu, epcm->memblk);
+		epcm->memblk = NULL;
+		epcm->start_addr = 0;
+	}
+	snd_pcm_lib_free_pages(substream);
+	return 0;
+}
+
+static int snd_emu10k1_efx_playback_hw_free(struct snd_pcm_substream *substream)
+{
+	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_emu10k1_pcm *epcm;
+	int i;
+
+	if (runtime->private_data == NULL)
+		return 0;
+	epcm = runtime->private_data;
+	if (epcm->extra) {
+		snd_emu10k1_voice_free(epcm->emu, epcm->extra);
+		epcm->extra = NULL;
+	}
+	for (i = 0; i < NUM_EFX_PLAYBACK; i++) {
+		if (epcm->voices[i]) {
+			snd_emu10k1_voice_free(epcm->emu, epcm->voices[i]);
+			epcm->voices[i] = NULL;
+		}
+	}
+	if (epcm->memblk) {
+		snd_emu10k1_free_pages(emu, epcm->memblk);
+		epcm->memblk = NULL;
+		epcm->start_addr = 0;
+	}
+	snd_pcm_lib_free_pages(substream);
+	return 0;
+}
+
+static int snd_emu10k1_playback_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_emu10k1_pcm *epcm = runtime->private_data;
+	unsigned int start_addr, end_addr;
+
+	start_addr = epcm->start_addr;
+	end_addr = snd_pcm_lib_period_bytes(substream);
+	if (runtime->channels == 2) {
+		start_addr >>= 1;
+		end_addr >>= 1;
+	}
+	end_addr += start_addr;
+	snd_emu10k1_pcm_init_voice(emu, 1, 1, epcm->extra,
+				   start_addr, end_addr, NULL);
+	start_addr = epcm->start_addr;
+	end_addr = epcm->start_addr + snd_pcm_lib_buffer_bytes(substream);
+	snd_emu10k1_pcm_init_voice(emu, 1, 0, epcm->voices[0],
+				   start_addr, end_addr,
+				   &emu->pcm_mixer[substream->number]);
+	if (epcm->voices[1])
+		snd_emu10k1_pcm_init_voice(emu, 0, 0, epcm->voices[1],
+					   start_addr, end_addr,
+					   &emu->pcm_mixer[substream->number]);
+	return 0;
+}
+
+static int snd_emu10k1_efx_playback_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_emu10k1_pcm *epcm = runtime->private_data;
+	unsigned int start_addr, end_addr;
+	unsigned int channel_size;
+	int i;
+
+	start_addr = epcm->start_addr;
+	end_addr = epcm->start_addr + snd_pcm_lib_buffer_bytes(substream);
+
+	/*
+	 * the kX driver leaves some space between voices
+	 */
+	channel_size = ( end_addr - start_addr ) / NUM_EFX_PLAYBACK;
+
+	snd_emu10k1_pcm_init_voice(emu, 1, 1, epcm->extra,
+				   start_addr, start_addr + (channel_size / 2), NULL);
+
+	/* only difference with the master voice is we use it for the pointer */
+	snd_emu10k1_pcm_init_voice(emu, 1, 0, epcm->voices[0],
+				   start_addr, start_addr + channel_size,
+				   &emu->efx_pcm_mixer[0]);
+
+	start_addr += channel_size;
+	for (i = 1; i < NUM_EFX_PLAYBACK; i++) {
+		snd_emu10k1_pcm_init_voice(emu, 0, 0, epcm->voices[i],
+					   start_addr, start_addr + channel_size,
+					   &emu->efx_pcm_mixer[i]);
+		start_addr += channel_size;
+	}
+
+	return 0;
+}
+
+static struct snd_pcm_hardware snd_emu10k1_efx_playback =
+{
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_NONINTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_RESUME |
+				 SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE),
+	.formats =		SNDRV_PCM_FMTBIT_S16_LE,
+	.rates =		SNDRV_PCM_RATE_48000,
+	.rate_min =		48000,
+	.rate_max =		48000,
+	.channels_min =		NUM_EFX_PLAYBACK,
+	.channels_max =		NUM_EFX_PLAYBACK,
+	.buffer_bytes_max =	(64*1024),
+	.period_bytes_min =	64,
+	.period_bytes_max =	(64*1024),
+	.periods_min =		2,
+	.periods_max =		2,
+	.fifo_size =		0,
+};
+
+static int snd_emu10k1_capture_hw_params(struct snd_pcm_substream *substream,
+					 struct snd_pcm_hw_params *hw_params)
+{
+	return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+static int snd_emu10k1_capture_hw_free(struct snd_pcm_substream *substream)
+{
+	return snd_pcm_lib_free_pages(substream);
+}
+
+static int snd_emu10k1_capture_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_emu10k1_pcm *epcm = runtime->private_data;
+	int idx;
+
+	/* zeroing the buffer size will stop capture */
+	snd_emu10k1_ptr_write(emu, epcm->capture_bs_reg, 0, 0);
+	switch (epcm->type) {
+	case CAPTURE_AC97ADC:
+		snd_emu10k1_ptr_write(emu, ADCCR, 0, 0);
+		break;
+	case CAPTURE_EFX:
+		if (emu->audigy) {
+			snd_emu10k1_ptr_write(emu, A_FXWC1, 0, 0);
+			snd_emu10k1_ptr_write(emu, A_FXWC2, 0, 0);
+		} else
+			snd_emu10k1_ptr_write(emu, FXWC, 0, 0);
+		break;
+	default:
+		break;
+	}	
+	snd_emu10k1_ptr_write(emu, epcm->capture_ba_reg, 0, runtime->dma_addr);
+	epcm->capture_bufsize = snd_pcm_lib_buffer_bytes(substream);
+	epcm->capture_bs_val = 0;
+	for (idx = 0; idx < 31; idx++) {
+		if (capture_period_sizes[idx] == epcm->capture_bufsize) {
+			epcm->capture_bs_val = idx + 1;
+			break;
+		}
+	}
+	if (epcm->capture_bs_val == 0) {
+		snd_BUG();
+		epcm->capture_bs_val++;
+	}
+	if (epcm->type == CAPTURE_AC97ADC) {
+		epcm->capture_cr_val = emu->audigy ? A_ADCCR_LCHANENABLE : ADCCR_LCHANENABLE;
+		if (runtime->channels > 1)
+			epcm->capture_cr_val |= emu->audigy ? A_ADCCR_RCHANENABLE : ADCCR_RCHANENABLE;
+		epcm->capture_cr_val |= emu->audigy ?
+			snd_emu10k1_audigy_capture_rate_reg(runtime->rate) :
+			snd_emu10k1_capture_rate_reg(runtime->rate);
+	}
+	return 0;
+}
+
+static void snd_emu10k1_playback_invalidate_cache(struct snd_emu10k1 *emu, int extra, struct snd_emu10k1_voice *evoice)
+{
+	struct snd_pcm_runtime *runtime;
+	unsigned int voice, stereo, i, ccis, cra = 64, cs, sample;
+
+	if (evoice == NULL)
+		return;
+	runtime = evoice->epcm->substream->runtime;
+	voice = evoice->number;
+	stereo = (!extra && runtime->channels == 2);
+	sample = snd_pcm_format_width(runtime->format) == 16 ? 0 : 0x80808080;
+	ccis = emu10k1_ccis(stereo, sample == 0);
+	/* set cs to 2 * number of cache registers beside the invalidated */
+	cs = (sample == 0) ? (32-ccis) : (64-ccis+1) >> 1;
+	if (cs > 16) cs = 16;
+	for (i = 0; i < cs; i++) {
+		snd_emu10k1_ptr_write(emu, CD0 + i, voice, sample);
+		if (stereo) {
+			snd_emu10k1_ptr_write(emu, CD0 + i, voice + 1, sample);
+		}
+	}
+	/* reset cache */
+	snd_emu10k1_ptr_write(emu, CCR_CACHEINVALIDSIZE, voice, 0);
+	snd_emu10k1_ptr_write(emu, CCR_READADDRESS, voice, cra);
+	if (stereo) {
+		snd_emu10k1_ptr_write(emu, CCR_CACHEINVALIDSIZE, voice + 1, 0);
+		snd_emu10k1_ptr_write(emu, CCR_READADDRESS, voice + 1, cra);
+	}
+	/* fill cache */
+	snd_emu10k1_ptr_write(emu, CCR_CACHEINVALIDSIZE, voice, ccis);
+	if (stereo) {
+		snd_emu10k1_ptr_write(emu, CCR_CACHEINVALIDSIZE, voice+1, ccis);
+	}
+}
+
+static void snd_emu10k1_playback_prepare_voice(struct snd_emu10k1 *emu, struct snd_emu10k1_voice *evoice,
+					       int master, int extra,
+					       struct snd_emu10k1_pcm_mixer *mix)
+{
+	struct snd_pcm_substream *substream;
+	struct snd_pcm_runtime *runtime;
+	unsigned int attn, vattn;
+	unsigned int voice, tmp;
+
+	if (evoice == NULL)	/* skip second voice for mono */
+		return;
+	substream = evoice->epcm->substream;
+	runtime = substream->runtime;
+	voice = evoice->number;
+
+	attn = extra ? 0 : 0x00ff;
+	tmp = runtime->channels == 2 ? (master ? 1 : 2) : 0;
+	vattn = mix != NULL ? (mix->attn[tmp] << 16) : 0;
+	snd_emu10k1_ptr_write(emu, IFATN, voice, attn);
+	snd_emu10k1_ptr_write(emu, VTFT, voice, vattn | 0xffff);
+	snd_emu10k1_ptr_write(emu, CVCF, voice, vattn | 0xffff);
+	snd_emu10k1_ptr_write(emu, DCYSUSV, voice, 0x7f7f);
+	snd_emu10k1_voice_clear_loop_stop(emu, voice);
+}	
+
+static void snd_emu10k1_playback_trigger_voice(struct snd_emu10k1 *emu, struct snd_emu10k1_voice *evoice, int master, int extra)
+{
+	struct snd_pcm_substream *substream;
+	struct snd_pcm_runtime *runtime;
+	unsigned int voice, pitch, pitch_target;
+
+	if (evoice == NULL)	/* skip second voice for mono */
+		return;
+	substream = evoice->epcm->substream;
+	runtime = substream->runtime;
+	voice = evoice->number;
+
+	pitch = snd_emu10k1_rate_to_pitch(runtime->rate) >> 8;
+	if (emu->card_capabilities->emu1010)
+		pitch_target = PITCH_48000; /* Disable interpolators on emu1010 card */
+	else 
+		pitch_target = emu10k1_calc_pitch_target(runtime->rate);
+	snd_emu10k1_ptr_write(emu, PTRX_PITCHTARGET, voice, pitch_target);
+	if (master || evoice->epcm->type == PLAYBACK_EFX)
+		snd_emu10k1_ptr_write(emu, CPF_CURRENTPITCH, voice, pitch_target);
+	snd_emu10k1_ptr_write(emu, IP, voice, pitch);
+	if (extra)
+		snd_emu10k1_voice_intr_enable(emu, voice);
+}
+
+static void snd_emu10k1_playback_stop_voice(struct snd_emu10k1 *emu, struct snd_emu10k1_voice *evoice)
+{
+	unsigned int voice;
+
+	if (evoice == NULL)
+		return;
+	voice = evoice->number;
+	snd_emu10k1_voice_intr_disable(emu, voice);
+	snd_emu10k1_ptr_write(emu, PTRX_PITCHTARGET, voice, 0);
+	snd_emu10k1_ptr_write(emu, CPF_CURRENTPITCH, voice, 0);
+	snd_emu10k1_ptr_write(emu, IFATN, voice, 0xffff);
+	snd_emu10k1_ptr_write(emu, VTFT, voice, 0xffff);
+	snd_emu10k1_ptr_write(emu, CVCF, voice, 0xffff);
+	snd_emu10k1_ptr_write(emu, IP, voice, 0);
+}
+
+static int snd_emu10k1_playback_trigger(struct snd_pcm_substream *substream,
+				        int cmd)
+{
+	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_emu10k1_pcm *epcm = runtime->private_data;
+	struct snd_emu10k1_pcm_mixer *mix;
+	int result = 0;
+
+	/* printk("trigger - emu10k1 = 0x%x, cmd = %i, pointer = %i\n", (int)emu, cmd, substream->ops->pointer(substream)); */
+	spin_lock(&emu->reg_lock);
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		snd_emu10k1_playback_invalidate_cache(emu, 1, epcm->extra);	/* do we need this? */
+		snd_emu10k1_playback_invalidate_cache(emu, 0, epcm->voices[0]);
+		/* follow thru */
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		mix = &emu->pcm_mixer[substream->number];
+		snd_emu10k1_playback_prepare_voice(emu, epcm->voices[0], 1, 0, mix);
+		snd_emu10k1_playback_prepare_voice(emu, epcm->voices[1], 0, 0, mix);
+		snd_emu10k1_playback_prepare_voice(emu, epcm->extra, 1, 1, NULL);
+		snd_emu10k1_playback_trigger_voice(emu, epcm->voices[0], 1, 0);
+		snd_emu10k1_playback_trigger_voice(emu, epcm->voices[1], 0, 0);
+		snd_emu10k1_playback_trigger_voice(emu, epcm->extra, 1, 1);
+		epcm->running = 1;
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		epcm->running = 0;
+		snd_emu10k1_playback_stop_voice(emu, epcm->voices[0]);
+		snd_emu10k1_playback_stop_voice(emu, epcm->voices[1]);
+		snd_emu10k1_playback_stop_voice(emu, epcm->extra);
+		break;
+	default:
+		result = -EINVAL;
+		break;
+	}
+	spin_unlock(&emu->reg_lock);
+	return result;
+}
+
+static int snd_emu10k1_capture_trigger(struct snd_pcm_substream *substream,
+				       int cmd)
+{
+	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_emu10k1_pcm *epcm = runtime->private_data;
+	int result = 0;
+
+	spin_lock(&emu->reg_lock);
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		/* hmm this should cause full and half full interrupt to be raised? */
+		outl(epcm->capture_ipr, emu->port + IPR);
+		snd_emu10k1_intr_enable(emu, epcm->capture_inte);
+		/* printk("adccr = 0x%x, adcbs = 0x%x\n", epcm->adccr, epcm->adcbs); */
+		switch (epcm->type) {
+		case CAPTURE_AC97ADC:
+			snd_emu10k1_ptr_write(emu, ADCCR, 0, epcm->capture_cr_val);
+			break;
+		case CAPTURE_EFX:
+			if (emu->audigy) {
+				snd_emu10k1_ptr_write(emu, A_FXWC1, 0, epcm->capture_cr_val);
+				snd_emu10k1_ptr_write(emu, A_FXWC2, 0, epcm->capture_cr_val2);
+				snd_printdd("cr_val=0x%x, cr_val2=0x%x\n", epcm->capture_cr_val, epcm->capture_cr_val2);
+			} else
+				snd_emu10k1_ptr_write(emu, FXWC, 0, epcm->capture_cr_val);
+			break;
+		default:	
+			break;
+		}
+		snd_emu10k1_ptr_write(emu, epcm->capture_bs_reg, 0, epcm->capture_bs_val);
+		epcm->running = 1;
+		epcm->first_ptr = 1;
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		epcm->running = 0;
+		snd_emu10k1_intr_disable(emu, epcm->capture_inte);
+		outl(epcm->capture_ipr, emu->port + IPR);
+		snd_emu10k1_ptr_write(emu, epcm->capture_bs_reg, 0, 0);
+		switch (epcm->type) {
+		case CAPTURE_AC97ADC:
+			snd_emu10k1_ptr_write(emu, ADCCR, 0, 0);
+			break;
+		case CAPTURE_EFX:
+			if (emu->audigy) {
+				snd_emu10k1_ptr_write(emu, A_FXWC1, 0, 0);
+				snd_emu10k1_ptr_write(emu, A_FXWC2, 0, 0);
+			} else
+				snd_emu10k1_ptr_write(emu, FXWC, 0, 0);
+			break;
+		default:
+			break;
+		}
+		break;
+	default:
+		result = -EINVAL;
+	}
+	spin_unlock(&emu->reg_lock);
+	return result;
+}
+
+static snd_pcm_uframes_t snd_emu10k1_playback_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_emu10k1_pcm *epcm = runtime->private_data;
+	unsigned int ptr;
+
+	if (!epcm->running)
+		return 0;
+	ptr = snd_emu10k1_ptr_read(emu, CCCA, epcm->voices[0]->number) & 0x00ffffff;
+#if 0	/* Perex's code */
+	ptr += runtime->buffer_size;
+	ptr -= epcm->ccca_start_addr;
+	ptr %= runtime->buffer_size;
+#else	/* EMU10K1 Open Source code from Creative */
+	if (ptr < epcm->ccca_start_addr)
+		ptr += runtime->buffer_size - epcm->ccca_start_addr;
+	else {
+		ptr -= epcm->ccca_start_addr;
+		if (ptr >= runtime->buffer_size)
+			ptr -= runtime->buffer_size;
+	}
+#endif
+	/* printk("ptr = 0x%x, buffer_size = 0x%x, period_size = 0x%x\n", ptr, runtime->buffer_size, runtime->period_size); */
+	return ptr;
+}
+
+
+static int snd_emu10k1_efx_playback_trigger(struct snd_pcm_substream *substream,
+				        int cmd)
+{
+	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_emu10k1_pcm *epcm = runtime->private_data;
+	int i;
+	int result = 0;
+
+	spin_lock(&emu->reg_lock);
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		/* prepare voices */
+		for (i = 0; i < NUM_EFX_PLAYBACK; i++) {	
+			snd_emu10k1_playback_invalidate_cache(emu, 0, epcm->voices[i]);
+		}
+		snd_emu10k1_playback_invalidate_cache(emu, 1, epcm->extra);
+
+		/* follow thru */
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		snd_emu10k1_playback_prepare_voice(emu, epcm->extra, 1, 1, NULL);
+		snd_emu10k1_playback_prepare_voice(emu, epcm->voices[0], 0, 0,
+						   &emu->efx_pcm_mixer[0]);
+		for (i = 1; i < NUM_EFX_PLAYBACK; i++)
+			snd_emu10k1_playback_prepare_voice(emu, epcm->voices[i], 0, 0,
+							   &emu->efx_pcm_mixer[i]);
+		snd_emu10k1_playback_trigger_voice(emu, epcm->voices[0], 0, 0);
+		snd_emu10k1_playback_trigger_voice(emu, epcm->extra, 1, 1);
+		for (i = 1; i < NUM_EFX_PLAYBACK; i++)
+			snd_emu10k1_playback_trigger_voice(emu, epcm->voices[i], 0, 0);
+		epcm->running = 1;
+		break;
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		epcm->running = 0;
+		for (i = 0; i < NUM_EFX_PLAYBACK; i++) {	
+			snd_emu10k1_playback_stop_voice(emu, epcm->voices[i]);
+		}
+		snd_emu10k1_playback_stop_voice(emu, epcm->extra);
+		break;
+	default:
+		result = -EINVAL;
+		break;
+	}
+	spin_unlock(&emu->reg_lock);
+	return result;
+}
+
+
+static snd_pcm_uframes_t snd_emu10k1_capture_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_emu10k1_pcm *epcm = runtime->private_data;
+	unsigned int ptr;
+
+	if (!epcm->running)
+		return 0;
+	if (epcm->first_ptr) {
+		udelay(50);	/* hack, it takes awhile until capture is started */
+		epcm->first_ptr = 0;
+	}
+	ptr = snd_emu10k1_ptr_read(emu, epcm->capture_idx_reg, 0) & 0x0000ffff;
+	return bytes_to_frames(runtime, ptr);
+}
+
+/*
+ *  Playback support device description
+ */
+
+static struct snd_pcm_hardware snd_emu10k1_playback =
+{
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_RESUME |
+				 SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE),
+	.formats =		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_96000,
+	.rate_min =		4000,
+	.rate_max =		96000,
+	.channels_min =		1,
+	.channels_max =		2,
+	.buffer_bytes_max =	(128*1024),
+	.period_bytes_min =	64,
+	.period_bytes_max =	(128*1024),
+	.periods_min =		1,
+	.periods_max =		1024,
+	.fifo_size =		0,
+};
+
+/*
+ *  Capture support device description
+ */
+
+static struct snd_pcm_hardware snd_emu10k1_capture =
+{
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_RESUME |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+	.formats =		SNDRV_PCM_FMTBIT_S16_LE,
+	.rates =		SNDRV_PCM_RATE_8000_48000,
+	.rate_min =		8000,
+	.rate_max =		48000,
+	.channels_min =		1,
+	.channels_max =		2,
+	.buffer_bytes_max =	(64*1024),
+	.period_bytes_min =	384,
+	.period_bytes_max =	(64*1024),
+	.periods_min =		2,
+	.periods_max =		2,
+	.fifo_size =		0,
+};
+
+static struct snd_pcm_hardware snd_emu10k1_capture_efx =
+{
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_RESUME |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+	.formats =		SNDRV_PCM_FMTBIT_S16_LE,
+	.rates =		SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | 
+				 SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | 
+				 SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000,
+	.rate_min =		44100,
+	.rate_max =		192000,
+	.channels_min =		8,
+	.channels_max =		8,
+	.buffer_bytes_max =	(64*1024),
+	.period_bytes_min =	384,
+	.period_bytes_max =	(64*1024),
+	.periods_min =		2,
+	.periods_max =		2,
+	.fifo_size =		0,
+};
+
+/*
+ *
+ */
+
+static void snd_emu10k1_pcm_mixer_notify1(struct snd_emu10k1 *emu, struct snd_kcontrol *kctl, int idx, int activate)
+{
+	struct snd_ctl_elem_id id;
+
+	if (! kctl)
+		return;
+	if (activate)
+		kctl->vd[idx].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+	else
+		kctl->vd[idx].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+	snd_ctl_notify(emu->card, SNDRV_CTL_EVENT_MASK_VALUE |
+		       SNDRV_CTL_EVENT_MASK_INFO,
+		       snd_ctl_build_ioff(&id, kctl, idx));
+}
+
+static void snd_emu10k1_pcm_mixer_notify(struct snd_emu10k1 *emu, int idx, int activate)
+{
+	snd_emu10k1_pcm_mixer_notify1(emu, emu->ctl_send_routing, idx, activate);
+	snd_emu10k1_pcm_mixer_notify1(emu, emu->ctl_send_volume, idx, activate);
+	snd_emu10k1_pcm_mixer_notify1(emu, emu->ctl_attn, idx, activate);
+}
+
+static void snd_emu10k1_pcm_efx_mixer_notify(struct snd_emu10k1 *emu, int idx, int activate)
+{
+	snd_emu10k1_pcm_mixer_notify1(emu, emu->ctl_efx_send_routing, idx, activate);
+	snd_emu10k1_pcm_mixer_notify1(emu, emu->ctl_efx_send_volume, idx, activate);
+	snd_emu10k1_pcm_mixer_notify1(emu, emu->ctl_efx_attn, idx, activate);
+}
+
+static void snd_emu10k1_pcm_free_substream(struct snd_pcm_runtime *runtime)
+{
+	kfree(runtime->private_data);
+}
+
+static int snd_emu10k1_efx_playback_close(struct snd_pcm_substream *substream)
+{
+	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+	struct snd_emu10k1_pcm_mixer *mix;
+	int i;
+
+	for (i = 0; i < NUM_EFX_PLAYBACK; i++) {
+		mix = &emu->efx_pcm_mixer[i];
+		mix->epcm = NULL;
+		snd_emu10k1_pcm_efx_mixer_notify(emu, i, 0);
+	}
+	return 0;
+}
+
+static int snd_emu10k1_efx_playback_open(struct snd_pcm_substream *substream)
+{
+	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+	struct snd_emu10k1_pcm *epcm;
+	struct snd_emu10k1_pcm_mixer *mix;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	int i;
+
+	epcm = kzalloc(sizeof(*epcm), GFP_KERNEL);
+	if (epcm == NULL)
+		return -ENOMEM;
+	epcm->emu = emu;
+	epcm->type = PLAYBACK_EFX;
+	epcm->substream = substream;
+	
+	emu->pcm_playback_efx_substream = substream;
+
+	runtime->private_data = epcm;
+	runtime->private_free = snd_emu10k1_pcm_free_substream;
+	runtime->hw = snd_emu10k1_efx_playback;
+	
+	for (i = 0; i < NUM_EFX_PLAYBACK; i++) {
+		mix = &emu->efx_pcm_mixer[i];
+		mix->send_routing[0][0] = i;
+		memset(&mix->send_volume, 0, sizeof(mix->send_volume));
+		mix->send_volume[0][0] = 255;
+		mix->attn[0] = 0xffff;
+		mix->epcm = epcm;
+		snd_emu10k1_pcm_efx_mixer_notify(emu, i, 1);
+	}
+	return 0;
+}
+
+static int snd_emu10k1_playback_open(struct snd_pcm_substream *substream)
+{
+	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+	struct snd_emu10k1_pcm *epcm;
+	struct snd_emu10k1_pcm_mixer *mix;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	int i, err;
+
+	epcm = kzalloc(sizeof(*epcm), GFP_KERNEL);
+	if (epcm == NULL)
+		return -ENOMEM;
+	epcm->emu = emu;
+	epcm->type = PLAYBACK_EMUVOICE;
+	epcm->substream = substream;
+	runtime->private_data = epcm;
+	runtime->private_free = snd_emu10k1_pcm_free_substream;
+	runtime->hw = snd_emu10k1_playback;
+	if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) {
+		kfree(epcm);
+		return err;
+	}
+	if ((err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 256, UINT_MAX)) < 0) {
+		kfree(epcm);
+		return err;
+	}
+	mix = &emu->pcm_mixer[substream->number];
+	for (i = 0; i < 4; i++)
+		mix->send_routing[0][i] = mix->send_routing[1][i] = mix->send_routing[2][i] = i;
+	memset(&mix->send_volume, 0, sizeof(mix->send_volume));
+	mix->send_volume[0][0] = mix->send_volume[0][1] =
+	mix->send_volume[1][0] = mix->send_volume[2][1] = 255;
+	mix->attn[0] = mix->attn[1] = mix->attn[2] = 0xffff;
+	mix->epcm = epcm;
+	snd_emu10k1_pcm_mixer_notify(emu, substream->number, 1);
+	return 0;
+}
+
+static int snd_emu10k1_playback_close(struct snd_pcm_substream *substream)
+{
+	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+	struct snd_emu10k1_pcm_mixer *mix = &emu->pcm_mixer[substream->number];
+
+	mix->epcm = NULL;
+	snd_emu10k1_pcm_mixer_notify(emu, substream->number, 0);
+	return 0;
+}
+
+static int snd_emu10k1_capture_open(struct snd_pcm_substream *substream)
+{
+	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_emu10k1_pcm *epcm;
+
+	epcm = kzalloc(sizeof(*epcm), GFP_KERNEL);
+	if (epcm == NULL)
+		return -ENOMEM;
+	epcm->emu = emu;
+	epcm->type = CAPTURE_AC97ADC;
+	epcm->substream = substream;
+	epcm->capture_ipr = IPR_ADCBUFFULL|IPR_ADCBUFHALFFULL;
+	epcm->capture_inte = INTE_ADCBUFENABLE;
+	epcm->capture_ba_reg = ADCBA;
+	epcm->capture_bs_reg = ADCBS;
+	epcm->capture_idx_reg = emu->audigy ? A_ADCIDX : ADCIDX;
+	runtime->private_data = epcm;
+	runtime->private_free = snd_emu10k1_pcm_free_substream;
+	runtime->hw = snd_emu10k1_capture;
+	emu->capture_interrupt = snd_emu10k1_pcm_ac97adc_interrupt;
+	emu->pcm_capture_substream = substream;
+	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_capture_period_sizes);
+	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_capture_rates);
+	return 0;
+}
+
+static int snd_emu10k1_capture_close(struct snd_pcm_substream *substream)
+{
+	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+
+	emu->capture_interrupt = NULL;
+	emu->pcm_capture_substream = NULL;
+	return 0;
+}
+
+static int snd_emu10k1_capture_mic_open(struct snd_pcm_substream *substream)
+{
+	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+	struct snd_emu10k1_pcm *epcm;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+
+	epcm = kzalloc(sizeof(*epcm), GFP_KERNEL);
+	if (epcm == NULL)
+		return -ENOMEM;
+	epcm->emu = emu;
+	epcm->type = CAPTURE_AC97MIC;
+	epcm->substream = substream;
+	epcm->capture_ipr = IPR_MICBUFFULL|IPR_MICBUFHALFFULL;
+	epcm->capture_inte = INTE_MICBUFENABLE;
+	epcm->capture_ba_reg = MICBA;
+	epcm->capture_bs_reg = MICBS;
+	epcm->capture_idx_reg = emu->audigy ? A_MICIDX : MICIDX;
+	substream->runtime->private_data = epcm;
+	substream->runtime->private_free = snd_emu10k1_pcm_free_substream;
+	runtime->hw = snd_emu10k1_capture;
+	runtime->hw.rates = SNDRV_PCM_RATE_8000;
+	runtime->hw.rate_min = runtime->hw.rate_max = 8000;
+	runtime->hw.channels_min = 1;
+	emu->capture_mic_interrupt = snd_emu10k1_pcm_ac97mic_interrupt;
+	emu->pcm_capture_mic_substream = substream;
+	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_capture_period_sizes);
+	return 0;
+}
+
+static int snd_emu10k1_capture_mic_close(struct snd_pcm_substream *substream)
+{
+	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+
+	emu->capture_interrupt = NULL;
+	emu->pcm_capture_mic_substream = NULL;
+	return 0;
+}
+
+static int snd_emu10k1_capture_efx_open(struct snd_pcm_substream *substream)
+{
+	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+	struct snd_emu10k1_pcm *epcm;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	int nefx = emu->audigy ? 64 : 32;
+	int idx;
+
+	epcm = kzalloc(sizeof(*epcm), GFP_KERNEL);
+	if (epcm == NULL)
+		return -ENOMEM;
+	epcm->emu = emu;
+	epcm->type = CAPTURE_EFX;
+	epcm->substream = substream;
+	epcm->capture_ipr = IPR_EFXBUFFULL|IPR_EFXBUFHALFFULL;
+	epcm->capture_inte = INTE_EFXBUFENABLE;
+	epcm->capture_ba_reg = FXBA;
+	epcm->capture_bs_reg = FXBS;
+	epcm->capture_idx_reg = FXIDX;
+	substream->runtime->private_data = epcm;
+	substream->runtime->private_free = snd_emu10k1_pcm_free_substream;
+	runtime->hw = snd_emu10k1_capture_efx;
+	runtime->hw.rates = SNDRV_PCM_RATE_48000;
+	runtime->hw.rate_min = runtime->hw.rate_max = 48000;
+	spin_lock_irq(&emu->reg_lock);
+	if (emu->card_capabilities->emu1010) {
+		/* TODO 
+		 * SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE
+		 * SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
+		 * SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
+		 * SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000
+		 * rate_min = 44100,
+		 * rate_max = 192000,
+		 * channels_min = 8,
+		 * channels_max = 8,
+		 * Need to add mixer control to fix sample rate
+		 *                 
+		 * There are 16 mono channels of 16bits each.
+		 * 24bit Audio uses 2x channels over 16bit
+		 * 96kHz uses 2x channels over 48kHz
+		 * 192kHz uses 4x channels over 48kHz
+		 * So, for 48kHz 24bit, one has 8 channels
+		 * for 96kHz 24bit, one has 4 channels
+		 * for 192kHz 24bit, one has 2 channels
+		 */
+#if 1
+		switch (emu->emu1010.internal_clock) {
+		case 0:
+			/* For 44.1kHz */
+			runtime->hw.rates = SNDRV_PCM_RATE_44100;
+			runtime->hw.rate_min = runtime->hw.rate_max = 44100;
+			runtime->hw.channels_min = runtime->hw.channels_max = 8;
+			break;
+		case 1:
+			/* For 48kHz */
+			runtime->hw.rates = SNDRV_PCM_RATE_48000;
+			runtime->hw.rate_min = runtime->hw.rate_max = 48000;
+			runtime->hw.channels_min = runtime->hw.channels_max = 8;
+			break;
+		};
+#endif
+#if 0
+		/* For 96kHz */
+		runtime->hw.rates = SNDRV_PCM_RATE_96000;
+		runtime->hw.rate_min = runtime->hw.rate_max = 96000;
+		runtime->hw.channels_min = runtime->hw.channels_max = 4;
+#endif
+#if 0
+		/* For 192kHz */
+		runtime->hw.rates = SNDRV_PCM_RATE_192000;
+		runtime->hw.rate_min = runtime->hw.rate_max = 192000;
+		runtime->hw.channels_min = runtime->hw.channels_max = 2;
+#endif
+		runtime->hw.formats = SNDRV_PCM_FMTBIT_S32_LE;
+		/* efx_voices_mask[0] is expected to be zero
+ 		 * efx_voices_mask[1] is expected to have 16bits set
+		 */
+	} else {
+		runtime->hw.channels_min = runtime->hw.channels_max = 0;
+		for (idx = 0; idx < nefx; idx++) {
+			if (emu->efx_voices_mask[idx/32] & (1 << (idx%32))) {
+				runtime->hw.channels_min++;
+				runtime->hw.channels_max++;
+			}
+		}
+	}
+	epcm->capture_cr_val = emu->efx_voices_mask[0];
+	epcm->capture_cr_val2 = emu->efx_voices_mask[1];
+	spin_unlock_irq(&emu->reg_lock);
+	emu->capture_efx_interrupt = snd_emu10k1_pcm_efx_interrupt;
+	emu->pcm_capture_efx_substream = substream;
+	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_capture_period_sizes);
+	return 0;
+}
+
+static int snd_emu10k1_capture_efx_close(struct snd_pcm_substream *substream)
+{
+	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+
+	emu->capture_interrupt = NULL;
+	emu->pcm_capture_efx_substream = NULL;
+	return 0;
+}
+
+static struct snd_pcm_ops snd_emu10k1_playback_ops = {
+	.open =			snd_emu10k1_playback_open,
+	.close =		snd_emu10k1_playback_close,
+	.ioctl =		snd_pcm_lib_ioctl,
+	.hw_params =		snd_emu10k1_playback_hw_params,
+	.hw_free =		snd_emu10k1_playback_hw_free,
+	.prepare =		snd_emu10k1_playback_prepare,
+	.trigger =		snd_emu10k1_playback_trigger,
+	.pointer =		snd_emu10k1_playback_pointer,
+	.page =			snd_pcm_sgbuf_ops_page,
+};
+
+static struct snd_pcm_ops snd_emu10k1_capture_ops = {
+	.open =			snd_emu10k1_capture_open,
+	.close =		snd_emu10k1_capture_close,
+	.ioctl =		snd_pcm_lib_ioctl,
+	.hw_params =		snd_emu10k1_capture_hw_params,
+	.hw_free =		snd_emu10k1_capture_hw_free,
+	.prepare =		snd_emu10k1_capture_prepare,
+	.trigger =		snd_emu10k1_capture_trigger,
+	.pointer =		snd_emu10k1_capture_pointer,
+};
+
+/* EFX playback */
+static struct snd_pcm_ops snd_emu10k1_efx_playback_ops = {
+	.open =			snd_emu10k1_efx_playback_open,
+	.close =		snd_emu10k1_efx_playback_close,
+	.ioctl =		snd_pcm_lib_ioctl,
+	.hw_params =		snd_emu10k1_playback_hw_params,
+	.hw_free =		snd_emu10k1_efx_playback_hw_free,
+	.prepare =		snd_emu10k1_efx_playback_prepare,
+	.trigger =		snd_emu10k1_efx_playback_trigger,
+	.pointer =		snd_emu10k1_efx_playback_pointer,
+	.page =			snd_pcm_sgbuf_ops_page,
+};
+
+int __devinit snd_emu10k1_pcm(struct snd_emu10k1 * emu, int device, struct snd_pcm ** rpcm)
+{
+	struct snd_pcm *pcm;
+	struct snd_pcm_substream *substream;
+	int err;
+
+	if (rpcm)
+		*rpcm = NULL;
+
+	if ((err = snd_pcm_new(emu->card, "emu10k1", device, 32, 1, &pcm)) < 0)
+		return err;
+
+	pcm->private_data = emu;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_emu10k1_playback_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_emu10k1_capture_ops);
+
+	pcm->info_flags = 0;
+	pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
+	strcpy(pcm->name, "ADC Capture/Standard PCM Playback");
+	emu->pcm = pcm;
+
+	for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next)
+		if ((err = snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV_SG, snd_dma_pci_data(emu->pci), 64*1024, 64*1024)) < 0)
+			return err;
+
+	for (substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; substream; substream = substream->next)
+		snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(emu->pci), 64*1024, 64*1024);
+
+	if (rpcm)
+		*rpcm = pcm;
+
+	return 0;
+}
+
+int __devinit snd_emu10k1_pcm_multi(struct snd_emu10k1 * emu, int device, struct snd_pcm ** rpcm)
+{
+	struct snd_pcm *pcm;
+	struct snd_pcm_substream *substream;
+	int err;
+
+	if (rpcm)
+		*rpcm = NULL;
+
+	if ((err = snd_pcm_new(emu->card, "emu10k1", device, 1, 0, &pcm)) < 0)
+		return err;
+
+	pcm->private_data = emu;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_emu10k1_efx_playback_ops);
+
+	pcm->info_flags = 0;
+	pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
+	strcpy(pcm->name, "Multichannel Playback");
+	emu->pcm_multi = pcm;
+
+	for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next)
+		if ((err = snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV_SG, snd_dma_pci_data(emu->pci), 64*1024, 64*1024)) < 0)
+			return err;
+
+	if (rpcm)
+		*rpcm = pcm;
+
+	return 0;
+}
+
+
+static struct snd_pcm_ops snd_emu10k1_capture_mic_ops = {
+	.open =			snd_emu10k1_capture_mic_open,
+	.close =		snd_emu10k1_capture_mic_close,
+	.ioctl =		snd_pcm_lib_ioctl,
+	.hw_params =		snd_emu10k1_capture_hw_params,
+	.hw_free =		snd_emu10k1_capture_hw_free,
+	.prepare =		snd_emu10k1_capture_prepare,
+	.trigger =		snd_emu10k1_capture_trigger,
+	.pointer =		snd_emu10k1_capture_pointer,
+};
+
+int __devinit snd_emu10k1_pcm_mic(struct snd_emu10k1 * emu, int device, struct snd_pcm ** rpcm)
+{
+	struct snd_pcm *pcm;
+	int err;
+
+	if (rpcm)
+		*rpcm = NULL;
+
+	if ((err = snd_pcm_new(emu->card, "emu10k1 mic", device, 0, 1, &pcm)) < 0)
+		return err;
+
+	pcm->private_data = emu;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_emu10k1_capture_mic_ops);
+
+	pcm->info_flags = 0;
+	strcpy(pcm->name, "Mic Capture");
+	emu->pcm_mic = pcm;
+
+	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(emu->pci), 64*1024, 64*1024);
+
+	if (rpcm)
+		*rpcm = pcm;
+	return 0;
+}
+
+static int snd_emu10k1_pcm_efx_voices_mask_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+	int nefx = emu->audigy ? 64 : 32;
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = nefx;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+	return 0;
+}
+
+static int snd_emu10k1_pcm_efx_voices_mask_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+	int nefx = emu->audigy ? 64 : 32;
+	int idx;
+	
+	spin_lock_irq(&emu->reg_lock);
+	for (idx = 0; idx < nefx; idx++)
+		ucontrol->value.integer.value[idx] = (emu->efx_voices_mask[idx / 32] & (1 << (idx % 32))) ? 1 : 0;
+	spin_unlock_irq(&emu->reg_lock);
+	return 0;
+}
+
+static int snd_emu10k1_pcm_efx_voices_mask_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+	unsigned int nval[2], bits;
+	int nefx = emu->audigy ? 64 : 32;
+	int nefxb = emu->audigy ? 7 : 6;
+	int change, idx;
+	
+	nval[0] = nval[1] = 0;
+	for (idx = 0, bits = 0; idx < nefx; idx++)
+		if (ucontrol->value.integer.value[idx]) {
+			nval[idx / 32] |= 1 << (idx % 32);
+			bits++;
+		}
+		
+	for (idx = 0; idx < nefxb; idx++)
+		if (1 << idx == bits)
+			break;
+	
+	if (idx >= nefxb)
+		return -EINVAL;
+
+	spin_lock_irq(&emu->reg_lock);
+	change = (nval[0] != emu->efx_voices_mask[0]) ||
+		(nval[1] != emu->efx_voices_mask[1]);
+	emu->efx_voices_mask[0] = nval[0];
+	emu->efx_voices_mask[1] = nval[1];
+	spin_unlock_irq(&emu->reg_lock);
+	return change;
+}
+
+static struct snd_kcontrol_new snd_emu10k1_pcm_efx_voices_mask = {
+	.iface = SNDRV_CTL_ELEM_IFACE_PCM,
+	.name = "Captured FX8010 Outputs",
+	.info = snd_emu10k1_pcm_efx_voices_mask_info,
+	.get = snd_emu10k1_pcm_efx_voices_mask_get,
+	.put = snd_emu10k1_pcm_efx_voices_mask_put
+};
+
+static struct snd_pcm_ops snd_emu10k1_capture_efx_ops = {
+	.open =			snd_emu10k1_capture_efx_open,
+	.close =		snd_emu10k1_capture_efx_close,
+	.ioctl =		snd_pcm_lib_ioctl,
+	.hw_params =		snd_emu10k1_capture_hw_params,
+	.hw_free =		snd_emu10k1_capture_hw_free,
+	.prepare =		snd_emu10k1_capture_prepare,
+	.trigger =		snd_emu10k1_capture_trigger,
+	.pointer =		snd_emu10k1_capture_pointer,
+};
+
+
+/* EFX playback */
+
+#define INITIAL_TRAM_SHIFT     14
+#define INITIAL_TRAM_POS(size) ((((size) / 2) - INITIAL_TRAM_SHIFT) - 1)
+
+static void snd_emu10k1_fx8010_playback_irq(struct snd_emu10k1 *emu, void *private_data)
+{
+	struct snd_pcm_substream *substream = private_data;
+	snd_pcm_period_elapsed(substream);
+}
+
+static void snd_emu10k1_fx8010_playback_tram_poke1(unsigned short *dst_left,
+						   unsigned short *dst_right,
+						   unsigned short *src,
+						   unsigned int count,
+						   unsigned int tram_shift)
+{
+	/* printk("tram_poke1: dst_left = 0x%p, dst_right = 0x%p, src = 0x%p, count = 0x%x\n", dst_left, dst_right, src, count); */
+	if ((tram_shift & 1) == 0) {
+		while (count--) {
+			*dst_left-- = *src++;
+			*dst_right-- = *src++;
+		}
+	} else {
+		while (count--) {
+			*dst_right-- = *src++;
+			*dst_left-- = *src++;
+		}
+	}
+}
+
+static void fx8010_pb_trans_copy(struct snd_pcm_substream *substream,
+				 struct snd_pcm_indirect *rec, size_t bytes)
+{
+	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+	struct snd_emu10k1_fx8010_pcm *pcm = &emu->fx8010.pcm[substream->number];
+	unsigned int tram_size = pcm->buffer_size;
+	unsigned short *src = (unsigned short *)(substream->runtime->dma_area + rec->sw_data);
+	unsigned int frames = bytes >> 2, count;
+	unsigned int tram_pos = pcm->tram_pos;
+	unsigned int tram_shift = pcm->tram_shift;
+
+	while (frames > tram_pos) {
+		count = tram_pos + 1;
+		snd_emu10k1_fx8010_playback_tram_poke1((unsigned short *)emu->fx8010.etram_pages.area + tram_pos,
+						       (unsigned short *)emu->fx8010.etram_pages.area + tram_pos + tram_size / 2,
+						       src, count, tram_shift);
+		src += count * 2;
+		frames -= count;
+		tram_pos = (tram_size / 2) - 1;
+		tram_shift++;
+	}
+	snd_emu10k1_fx8010_playback_tram_poke1((unsigned short *)emu->fx8010.etram_pages.area + tram_pos,
+					       (unsigned short *)emu->fx8010.etram_pages.area + tram_pos + tram_size / 2,
+					       src, frames, tram_shift);
+	tram_pos -= frames;
+	pcm->tram_pos = tram_pos;
+	pcm->tram_shift = tram_shift;
+}
+
+static int snd_emu10k1_fx8010_playback_transfer(struct snd_pcm_substream *substream)
+{
+	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+	struct snd_emu10k1_fx8010_pcm *pcm = &emu->fx8010.pcm[substream->number];
+
+	snd_pcm_indirect_playback_transfer(substream, &pcm->pcm_rec, fx8010_pb_trans_copy);
+	return 0;
+}
+
+static int snd_emu10k1_fx8010_playback_hw_params(struct snd_pcm_substream *substream,
+						 struct snd_pcm_hw_params *hw_params)
+{
+	return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+static int snd_emu10k1_fx8010_playback_hw_free(struct snd_pcm_substream *substream)
+{
+	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+	struct snd_emu10k1_fx8010_pcm *pcm = &emu->fx8010.pcm[substream->number];
+	unsigned int i;
+
+	for (i = 0; i < pcm->channels; i++)
+		snd_emu10k1_ptr_write(emu, TANKMEMADDRREGBASE + 0x80 + pcm->etram[i], 0, 0);
+	snd_pcm_lib_free_pages(substream);
+	return 0;
+}
+
+static int snd_emu10k1_fx8010_playback_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_emu10k1_fx8010_pcm *pcm = &emu->fx8010.pcm[substream->number];
+	unsigned int i;
+	
+	/* printk("prepare: etram_pages = 0x%p, dma_area = 0x%x, buffer_size = 0x%x (0x%x)\n", emu->fx8010.etram_pages, runtime->dma_area, runtime->buffer_size, runtime->buffer_size << 2); */
+	memset(&pcm->pcm_rec, 0, sizeof(pcm->pcm_rec));
+	pcm->pcm_rec.hw_buffer_size = pcm->buffer_size * 2; /* byte size */
+	pcm->pcm_rec.sw_buffer_size = snd_pcm_lib_buffer_bytes(substream);
+	pcm->tram_pos = INITIAL_TRAM_POS(pcm->buffer_size);
+	pcm->tram_shift = 0;
+	snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_running, 0, 0);	/* reset */
+	snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_trigger, 0, 0);	/* reset */
+	snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_size, 0, runtime->buffer_size);
+	snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_ptr, 0, 0);		/* reset ptr number */
+	snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_count, 0, runtime->period_size);
+	snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_tmpcount, 0, runtime->period_size);
+	for (i = 0; i < pcm->channels; i++)
+		snd_emu10k1_ptr_write(emu, TANKMEMADDRREGBASE + 0x80 + pcm->etram[i], 0, (TANKMEMADDRREG_READ|TANKMEMADDRREG_ALIGN) + i * (runtime->buffer_size / pcm->channels));
+	return 0;
+}
+
+static int snd_emu10k1_fx8010_playback_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+	struct snd_emu10k1_fx8010_pcm *pcm = &emu->fx8010.pcm[substream->number];
+	int result = 0;
+
+	spin_lock(&emu->reg_lock);
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		/* follow thru */
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+	case SNDRV_PCM_TRIGGER_RESUME:
+#ifdef EMU10K1_SET_AC3_IEC958
+	{
+		int i;
+		for (i = 0; i < 3; i++) {
+			unsigned int bits;
+			bits = SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
+			       SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC | SPCS_GENERATIONSTATUS |
+			       0x00001200 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT | SPCS_NOTAUDIODATA;
+			snd_emu10k1_ptr_write(emu, SPCS0 + i, 0, bits);
+		}
+	}
+#endif
+		result = snd_emu10k1_fx8010_register_irq_handler(emu, snd_emu10k1_fx8010_playback_irq, pcm->gpr_running, substream, &pcm->irq);
+		if (result < 0)
+			goto __err;
+		snd_emu10k1_fx8010_playback_transfer(substream);	/* roll the ball */
+		snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_trigger, 0, 1);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		snd_emu10k1_fx8010_unregister_irq_handler(emu, pcm->irq); pcm->irq = NULL;
+		snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_trigger, 0, 0);
+		pcm->tram_pos = INITIAL_TRAM_POS(pcm->buffer_size);
+		pcm->tram_shift = 0;
+		break;
+	default:
+		result = -EINVAL;
+		break;
+	}
+      __err:
+	spin_unlock(&emu->reg_lock);
+	return result;
+}
+
+static snd_pcm_uframes_t snd_emu10k1_fx8010_playback_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+	struct snd_emu10k1_fx8010_pcm *pcm = &emu->fx8010.pcm[substream->number];
+	size_t ptr; /* byte pointer */
+
+	if (!snd_emu10k1_ptr_read(emu, emu->gpr_base + pcm->gpr_trigger, 0))
+		return 0;
+	ptr = snd_emu10k1_ptr_read(emu, emu->gpr_base + pcm->gpr_ptr, 0) << 2;
+	return snd_pcm_indirect_playback_pointer(substream, &pcm->pcm_rec, ptr);
+}
+
+static struct snd_pcm_hardware snd_emu10k1_fx8010_playback =
+{
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_RESUME |
+				 /* SNDRV_PCM_INFO_MMAP_VALID | */ SNDRV_PCM_INFO_PAUSE),
+	.formats =		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+	.rates =		SNDRV_PCM_RATE_48000,
+	.rate_min =		48000,
+	.rate_max =		48000,
+	.channels_min =		1,
+	.channels_max =		1,
+	.buffer_bytes_max =	(128*1024),
+	.period_bytes_min =	1024,
+	.period_bytes_max =	(128*1024),
+	.periods_min =		1,
+	.periods_max =		1024,
+	.fifo_size =		0,
+};
+
+static int snd_emu10k1_fx8010_playback_open(struct snd_pcm_substream *substream)
+{
+	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_emu10k1_fx8010_pcm *pcm = &emu->fx8010.pcm[substream->number];
+
+	runtime->hw = snd_emu10k1_fx8010_playback;
+	runtime->hw.channels_min = runtime->hw.channels_max = pcm->channels;
+	runtime->hw.period_bytes_max = (pcm->buffer_size * 2) / 2;
+	spin_lock_irq(&emu->reg_lock);
+	if (pcm->valid == 0) {
+		spin_unlock_irq(&emu->reg_lock);
+		return -ENODEV;
+	}
+	pcm->opened = 1;
+	spin_unlock_irq(&emu->reg_lock);
+	return 0;
+}
+
+static int snd_emu10k1_fx8010_playback_close(struct snd_pcm_substream *substream)
+{
+	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+	struct snd_emu10k1_fx8010_pcm *pcm = &emu->fx8010.pcm[substream->number];
+
+	spin_lock_irq(&emu->reg_lock);
+	pcm->opened = 0;
+	spin_unlock_irq(&emu->reg_lock);
+	return 0;
+}
+
+static struct snd_pcm_ops snd_emu10k1_fx8010_playback_ops = {
+	.open =			snd_emu10k1_fx8010_playback_open,
+	.close =		snd_emu10k1_fx8010_playback_close,
+	.ioctl =		snd_pcm_lib_ioctl,
+	.hw_params =		snd_emu10k1_fx8010_playback_hw_params,
+	.hw_free =		snd_emu10k1_fx8010_playback_hw_free,
+	.prepare =		snd_emu10k1_fx8010_playback_prepare,
+	.trigger =		snd_emu10k1_fx8010_playback_trigger,
+	.pointer =		snd_emu10k1_fx8010_playback_pointer,
+	.ack =			snd_emu10k1_fx8010_playback_transfer,
+};
+
+int __devinit snd_emu10k1_pcm_efx(struct snd_emu10k1 * emu, int device, struct snd_pcm ** rpcm)
+{
+	struct snd_pcm *pcm;
+	struct snd_kcontrol *kctl;
+	int err;
+
+	if (rpcm)
+		*rpcm = NULL;
+
+	if ((err = snd_pcm_new(emu->card, "emu10k1 efx", device, 8, 1, &pcm)) < 0)
+		return err;
+
+	pcm->private_data = emu;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_emu10k1_fx8010_playback_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_emu10k1_capture_efx_ops);
+
+	pcm->info_flags = 0;
+	strcpy(pcm->name, "Multichannel Capture/PT Playback");
+	emu->pcm_efx = pcm;
+	if (rpcm)
+		*rpcm = pcm;
+
+	/* EFX capture - record the "FXBUS2" channels, by default we connect the EXTINs 
+	 * to these
+	 */	
+	
+	/* emu->efx_voices_mask[0] = FXWC_DEFAULTROUTE_C | FXWC_DEFAULTROUTE_A; */
+	if (emu->audigy) {
+		emu->efx_voices_mask[0] = 0;
+		emu->efx_voices_mask[1] = 0xffff;
+	} else {
+		emu->efx_voices_mask[0] = 0xffff0000;
+		emu->efx_voices_mask[1] = 0;
+	}
+	kctl = snd_ctl_new1(&snd_emu10k1_pcm_efx_voices_mask, emu);
+	if (!kctl)
+		return -ENOMEM;
+	kctl->id.device = device;
+	snd_ctl_add(emu->card, kctl);
+
+	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(emu->pci), 64*1024, 64*1024);
+
+	return 0;
+}
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/emu10k1/emuproc.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/emu10k1/emuproc.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/emu10k1/emuproc.c	(revision 91)
@@ -0,0 +1,563 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *                   Creative Labs, Inc.
+ *  Routines for control of EMU10K1 chips / proc interface routines
+ *
+ *  BUGS:
+ *    --
+ *
+ *  TODO:
+ *    --
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/emu10k1.h>
+
+static void snd_emu10k1_proc_spdif_status(struct snd_emu10k1 * emu,
+                                          snd_info_buffer_t * buffer,
+                                          char *title,
+                                          int status_reg,
+                                          int rate_reg)
+{
+    static char *clkaccy[4] = { "1000ppm", "50ppm", "variable", "unknown" };
+    static int samplerate[16] = { 44100, 1, 48000, 32000, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
+    static char *channel[16] = { "unspec", "left", "right", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15" };
+    static char *emphasis[8] = { "none", "50/15 usec 2 channel", "2", "3", "4", "5", "6", "7" };
+    unsigned int status, rate = 0;
+
+    status = snd_emu10k1_ptr_read(emu, status_reg, 0);
+    if (rate_reg > 0)
+        rate = snd_emu10k1_ptr_read(emu, rate_reg, 0);
+
+    snd_iprintf(buffer, "\n%s\n", title);
+
+    snd_iprintf(buffer, "Professional Mode     : %s\n", (status & SPCS_PROFESSIONAL) ? "yes" : "no");
+    snd_iprintf(buffer, "Not Audio Data        : %s\n", (status & SPCS_NOTAUDIODATA) ? "yes" : "no");
+    snd_iprintf(buffer, "Copyright             : %s\n", (status & SPCS_COPYRIGHT) ? "yes" : "no");
+    snd_iprintf(buffer, "Emphasis              : %s\n", emphasis[(status & SPCS_EMPHASISMASK) >> 3]);
+    snd_iprintf(buffer, "Mode                  : %i\n", (status & SPCS_MODEMASK) >> 6);
+    snd_iprintf(buffer, "Category Code         : 0x%x\n", (status & SPCS_CATEGORYCODEMASK) >> 8);
+    snd_iprintf(buffer, "Generation Status     : %s\n", status & SPCS_GENERATIONSTATUS ? "original" : "copy");
+    snd_iprintf(buffer, "Source Mask           : %i\n", (status & SPCS_SOURCENUMMASK) >> 16);
+    snd_iprintf(buffer, "Channel Number        : %s\n", channel[(status & SPCS_CHANNELNUMMASK) >> 20]);
+    snd_iprintf(buffer, "Sample Rate           : %iHz\n", samplerate[(status & SPCS_SAMPLERATEMASK) >> 24]);
+    snd_iprintf(buffer, "Clock Accuracy        : %s\n", clkaccy[(status & SPCS_CLKACCYMASK) >> 28]);
+
+    if (rate_reg > 0) {
+        snd_iprintf(buffer, "S/PDIF Locked         : %s\n", rate & SRCS_SPDIFLOCKED ? "on" : "off");
+        snd_iprintf(buffer, "Rate Locked           : %s\n", rate & SRCS_RATELOCKED ? "on" : "off");
+        snd_iprintf(buffer, "Estimated Sample Rate : 0x%x\n", rate & SRCS_ESTSAMPLERATE);
+    }
+}
+
+static void snd_emu10k1_proc_read(snd_info_entry_t *entry,
+                                  snd_info_buffer_t * buffer)
+{
+    /* FIXME - output names are in emufx.c too */
+    static char *creative_outs[32] = {
+        /* 00 */ "AC97 Left",
+        /* 01 */ "AC97 Right",
+        /* 02 */ "Optical IEC958 Left",
+        /* 03 */ "Optical IEC958 Right",
+        /* 04 */ "Center",
+        /* 05 */ "LFE",
+        /* 06 */ "Headphone Left",
+        /* 07 */ "Headphone Right",
+        /* 08 */ "Surround Left",
+        /* 09 */ "Surround Right",
+        /* 10 */ "PCM Capture Left",
+        /* 11 */ "PCM Capture Right",
+        /* 12 */ "MIC Capture",
+        /* 13 */ "AC97 Surround Left",
+        /* 14 */ "AC97 Surround Right",
+        /* 15 */ "???",
+        /* 16 */ "???",
+        /* 17 */ "Analog Center",
+        /* 18 */ "Analog LFE",
+        /* 19 */ "???",
+        /* 20 */ "???",
+        /* 21 */ "???",
+        /* 22 */ "???",
+        /* 23 */ "???",
+        /* 24 */ "???",
+        /* 25 */ "???",
+        /* 26 */ "???",
+        /* 27 */ "???",
+        /* 28 */ "???",
+        /* 29 */ "???",
+        /* 30 */ "???",
+        /* 31 */ "???"
+    };
+
+    static char *audigy_outs[64] = {
+        /* 00 */ "Digital Front Left",
+        /* 01 */ "Digital Front Right",
+        /* 02 */ "Digital Center",
+        /* 03 */ "Digital LEF",
+        /* 04 */ "Headphone Left",
+        /* 05 */ "Headphone Right",
+        /* 06 */ "Digital Rear Left",
+        /* 07 */ "Digital Rear Right",
+        /* 08 */ "Front Left",
+        /* 09 */ "Front Right",
+        /* 10 */ "Center",
+        /* 11 */ "LFE",
+        /* 12 */ "???",
+        /* 13 */ "???",
+        /* 14 */ "Rear Left",
+        /* 15 */ "Rear Right",
+        /* 16 */ "AC97 Front Left",
+        /* 17 */ "AC97 Front Right",
+        /* 18 */ "ADC Caputre Left",
+        /* 19 */ "ADC Capture Right",
+        /* 20 */ "???",
+        /* 21 */ "???",
+        /* 22 */ "???",
+        /* 23 */ "???",
+        /* 24 */ "???",
+        /* 25 */ "???",
+        /* 26 */ "???",
+        /* 27 */ "???",
+        /* 28 */ "???",
+        /* 29 */ "???",
+        /* 30 */ "???",
+        /* 31 */ "???",
+        /* 32 */ "FXBUS2_0",
+        /* 33 */ "FXBUS2_1",
+        /* 34 */ "FXBUS2_2",
+        /* 35 */ "FXBUS2_3",
+        /* 36 */ "FXBUS2_4",
+        /* 37 */ "FXBUS2_5",
+        /* 38 */ "FXBUS2_6",
+        /* 39 */ "FXBUS2_7",
+        /* 40 */ "FXBUS2_8",
+        /* 41 */ "FXBUS2_9",
+        /* 42 */ "FXBUS2_10",
+        /* 43 */ "FXBUS2_11",
+        /* 44 */ "FXBUS2_12",
+        /* 45 */ "FXBUS2_13",
+        /* 46 */ "FXBUS2_14",
+        /* 47 */ "FXBUS2_15",
+        /* 48 */ "FXBUS2_16",
+        /* 49 */ "FXBUS2_17",
+        /* 50 */ "FXBUS2_18",
+        /* 51 */ "FXBUS2_19",
+        /* 52 */ "FXBUS2_20",
+        /* 53 */ "FXBUS2_21",
+        /* 54 */ "FXBUS2_22",
+        /* 55 */ "FXBUS2_23",
+        /* 56 */ "FXBUS2_24",
+        /* 57 */ "FXBUS2_25",
+        /* 58 */ "FXBUS2_26",
+        /* 59 */ "FXBUS2_27",
+        /* 60 */ "FXBUS2_28",
+        /* 61 */ "FXBUS2_29",
+        /* 62 */ "FXBUS2_30",
+        /* 63 */ "FXBUS2_31"
+    };
+
+    struct snd_emu10k1 *emu = entry->private_data;
+    unsigned int val;
+    int nefx = emu->audigy ? 64 : 32;
+    char **outputs = emu->audigy ? audigy_outs : creative_outs;
+    int idx;
+
+    snd_iprintf(buffer, "EMU10K1\n\n");
+    snd_iprintf(buffer, "Card                  : %s\n",
+                emu->audigy ? "Audigy" : (emu->card_capabilities->ecard ? "EMU APS" : "Creative"));
+    snd_iprintf(buffer, "Internal TRAM (words) : 0x%x\n", emu->fx8010.itram_size);
+    snd_iprintf(buffer, "External TRAM (words) : 0x%x\n", emu->fx8010.etram_pages.bytes);
+    snd_iprintf(buffer, "\n");
+    if (emu->audigy) {
+        snd_iprintf(buffer, "Effect Send Routing   : A=%i, B=%i, C=%i, D=%i\n",
+                    val & 0x3f,
+                    (val >> 8) & 0x3f,
+                    (val >> 16) & 0x3f,
+                    (val >> 24) & 0x3f);
+    } else {
+        snd_iprintf(buffer, "Effect Send Routing   : A=%i, B=%i, C=%i, D=%i\n",
+                    (val >> 16) & 0x0f,
+                    (val >> 20) & 0x0f,
+                    (val >> 24) & 0x0f,
+                    (val >> 28) & 0x0f);
+    }
+    snd_iprintf(buffer, "\nCaptured FX Outputs   :\n");
+    for (idx = 0; idx < nefx; idx++) {
+        if (emu->efx_voices_mask[idx/32] & (1 << (idx%32)))
+            snd_iprintf(buffer, "  Output %02i [%s]\n", idx, outputs[idx]);
+    }
+    snd_iprintf(buffer, "\nAll FX Outputs        :\n");
+    for (idx = 0; idx < (emu->audigy ? 64 : 32); idx++)
+        snd_iprintf(buffer, "  Output %02i [%s]\n", idx, outputs[idx]);
+    snd_emu10k1_proc_spdif_status(emu, buffer, "S/PDIF Output 0", SPCS0, -1);
+    snd_emu10k1_proc_spdif_status(emu, buffer, "S/PDIF Output 1", SPCS1, -1);
+    snd_emu10k1_proc_spdif_status(emu, buffer, "S/PDIF Output 2/3", SPCS2, -1);
+    snd_emu10k1_proc_spdif_status(emu, buffer, "CD-ROM S/PDIF", CDCS, CDSRCS);
+    snd_emu10k1_proc_spdif_status(emu, buffer, "General purpose S/PDIF", GPSCS, GPSRCS);
+    val = snd_emu10k1_ptr_read(emu, ZVSRCS, 0);
+    snd_iprintf(buffer, "\nZoomed Video\n");
+    snd_iprintf(buffer, "Rate Locked           : %s\n", val & SRCS_RATELOCKED ? "on" : "off");
+    snd_iprintf(buffer, "Estimated Sample Rate : 0x%x\n", val & SRCS_ESTSAMPLERATE);
+}
+
+static void snd_emu10k1_proc_acode_read(snd_info_entry_t *entry,
+                                        snd_info_buffer_t * buffer)
+{
+    u32 pc;
+    struct snd_emu10k1 *emu = entry->private_data;
+
+    snd_iprintf(buffer, "FX8010 Instruction List '%s'\n", emu->fx8010.name);
+    snd_iprintf(buffer, "  Code dump      :\n");
+    for (pc = 0; pc < (emu->audigy ? 1024 : 512); pc++) {
+        u32 low, high;
+
+        low = snd_emu10k1_efx_read(emu, pc * 2);
+        high = snd_emu10k1_efx_read(emu, pc * 2 + 1);
+        if (emu->audigy)
+            snd_iprintf(buffer, "    OP(0x%02x, 0x%03x, 0x%03x, 0x%03x, 0x%03x) /* 0x%04x: 0x%08x%08x */\n",
+                        (high >> 24) & 0x0f,
+                        (high >> 12) & 0x7ff,
+                        (high >> 0) & 0x7ff,
+                        (low >> 12) & 0x7ff,
+                        (low >> 0) & 0x7ff,
+                        pc,
+                        high, low);
+        else
+            snd_iprintf(buffer, "    OP(0x%02x, 0x%03x, 0x%03x, 0x%03x, 0x%03x) /* 0x%04x: 0x%08x%08x */\n",
+                        (high >> 20) & 0x0f,
+                        (high >> 10) & 0x3ff,
+                        (high >> 0) & 0x3ff,
+                        (low >> 10) & 0x3ff,
+                        (low >> 0) & 0x3ff,
+                        pc,
+                        high, low);
+    }
+}
+
+#define TOTAL_SIZE_GPR		(0x100*4)
+#define A_TOTAL_SIZE_GPR        (0x200*4)
+#define TOTAL_SIZE_TANKMEM_DATA	(0xa0*4)
+#define TOTAL_SIZE_TANKMEM_ADDR (0xa0*4)
+#define A_TOTAL_SIZE_TANKMEM_DATA (0x100*4)
+#define A_TOTAL_SIZE_TANKMEM_ADDR (0x100*4)
+#define TOTAL_SIZE_CODE		(0x200*8)
+#define A_TOTAL_SIZE_CODE       (0x400*8)
+
+static long snd_emu10k1_fx8010_read(snd_info_entry_t *entry, void *file_private_data,
+                                    struct file *file, char __user *buf,
+                                    unsigned long count, unsigned long pos)
+{
+    long size;
+    struct snd_emu10k1 *emu = entry->private_data;
+    unsigned int offset;
+    int tram_addr = 0;
+
+    if (!strcmp(entry->name, "fx8010_tram_addr")) {
+        offset = TANKMEMADDRREGBASE;
+        tram_addr = 1;
+    } else if (!strcmp(entry->name, "fx8010_tram_data")) {
+        offset = TANKMEMDATAREGBASE;
+    } else if (!strcmp(entry->name, "fx8010_code")) {
+        offset = emu->audigy ? A_MICROCODEBASE : MICROCODEBASE;
+    } else {
+        offset = emu->audigy ? A_FXGPREGBASE : FXGPREGBASE;
+    }
+    size = count;
+    if (pos + size > entry->size)
+        size = (long)entry->size - pos;
+    if (size > 0) {
+        unsigned int *tmp;
+        long res;
+        unsigned int idx;
+        if ((tmp = kmalloc(size + 8, GFP_KERNEL)) == NULL)
+            return -ENOMEM;
+        for (idx = 0; idx < ((pos & 3) + size + 3) >> 2; idx++)
+            if (tram_addr && emu->audigy) {
+                tmp[idx] = snd_emu10k1_ptr_read(emu, offset + idx + (pos >> 2), 0) >> 11;
+                tmp[idx] |= snd_emu10k1_ptr_read(emu, 0x100 + idx + (pos >> 2), 0) << 20;
+            } else
+                tmp[idx] = snd_emu10k1_ptr_read(emu, offset + idx + (pos >> 2), 0);
+        if (copy_to_user(buf, ((char *)tmp) + (pos & 3), size))
+            res = -EFAULT;
+        else {
+            res = size;
+        }
+        kfree(tmp);
+        return res;
+    }
+    return 0;
+}
+
+static void snd_emu10k1_proc_voices_read(snd_info_entry_t *entry,
+                                         snd_info_buffer_t * buffer)
+{
+    struct snd_emu10k1 *emu = entry->private_data;
+    struct snd_emu10k1_voice *voice;
+    int idx;
+
+    snd_iprintf(buffer, "ch\tuse\tpcm\tefx\tsynth\tmidi\n");
+    for (idx = 0; idx < NUM_G; idx++) {
+        voice = &emu->voices[idx];
+        snd_iprintf(buffer, "%i\t%i\t%i\t%i\t%i\t%i\n",
+                    idx,
+                    voice->use,
+                    voice->pcm,
+                    voice->efx,
+                    voice->synth,
+                    voice->midi);
+    }
+}
+
+#ifdef CONFIG_SND_DEBUG
+static void snd_emu_proc_io_reg_read(snd_info_entry_t *entry,
+                                     snd_info_buffer_t * buffer)
+{
+    struct snd_emu10k1 *emu = entry->private_data;
+    unsigned long value;
+    unsigned long flags;
+    int i;
+    snd_iprintf(buffer, "IO Registers:\n\n");
+    for(i = 0; i < 0x40; i+=4) {
+        spin_lock_irqsave(&emu->emu_lock, flags);
+        value = inl(emu->port + i);
+        spin_unlock_irqrestore(&emu->emu_lock, flags);
+        snd_iprintf(buffer, "%02X: %08lX\n", i, value);
+    }
+}
+
+static void snd_emu_proc_io_reg_write(snd_info_entry_t *entry,
+                                      snd_info_buffer_t * buffer)
+{
+    struct snd_emu10k1 *emu = entry->private_data;
+    unsigned long flags;
+    char line[64];
+    u32 reg, val;
+#if 0
+    while (!snd_info_get_line(buffer, line, sizeof(line))) {
+        if (sscanf(line, "%x %x", &reg, &val) != 2)
+            continue;
+        if ((reg < 0x40) && (reg >=0) && (val <= 0xffffffff) ) {
+            spin_lock_irqsave(&emu->emu_lock, flags);
+            outl(val, emu->port + (reg & 0xfffffffc));
+            spin_unlock_irqrestore(&emu->emu_lock, flags);
+        }
+    }
+#endif
+}
+
+static unsigned int snd_ptr_read(struct snd_emu10k1 * emu,
+                                 unsigned int iobase,
+                                 unsigned int reg,
+                                 unsigned int chn)
+{
+    unsigned long flags;
+    unsigned int regptr, val;
+
+    regptr = (reg << 16) | chn;
+
+    spin_lock_irqsave(&emu->emu_lock, flags);
+    outl(regptr, emu->port + iobase + PTR);
+    val = inl(emu->port + iobase + DATA);
+    spin_unlock_irqrestore(&emu->emu_lock, flags);
+    return val;
+}
+
+static void snd_ptr_write(struct snd_emu10k1 *emu,
+                          unsigned int iobase,
+                          unsigned int reg,
+                          unsigned int chn,
+                          unsigned int data)
+{
+    unsigned int regptr;
+    unsigned long flags;
+
+    regptr = (reg << 16) | chn;
+
+    spin_lock_irqsave(&emu->emu_lock, flags);
+    outl(regptr, emu->port + iobase + PTR);
+    outl(data, emu->port + iobase + DATA);
+    spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+
+static void snd_emu_proc_ptr_reg_read(snd_info_entry_t *entry,
+                                      snd_info_buffer_t * buffer, int iobase, int offset, int length)
+{
+    struct snd_emu10k1 *emu = entry->private_data;
+    unsigned long value;
+    int i,j;
+    if (offset+length > 0x80) {
+        snd_iprintf(buffer, "Input values out of range\n");
+        return;
+    }
+    snd_iprintf(buffer, "Registers 0x%x\n", iobase);
+    for(i = offset; i < offset+length; i++) {
+        snd_iprintf(buffer, "%02X: ",i);
+        for (j = 0; j < 64; j++) {
+            if(iobase == 0)
+                value = snd_ptr_read(emu, 0, i, j);
+            else
+                value = snd_ptr_read(emu, 0x20, i, j);
+            snd_iprintf(buffer, "%08lX ", value);
+        }
+        snd_iprintf(buffer, "\n");
+    }
+}
+
+static void snd_emu_proc_ptr_reg_write(snd_info_entry_t *entry,
+                                       snd_info_buffer_t * buffer, int iobase)
+{
+    struct snd_emu10k1 *emu = entry->private_data;
+    char line[64];
+    unsigned int reg, channel_id , val;
+#if 0
+    while (!snd_info_get_line(buffer, line, sizeof(line))) {
+        if (sscanf(line, "%x %x %x", &reg, &channel_id, &val) != 3)
+            continue;
+        if ((reg < 0x80) && (reg >=0) && (val <= 0xffffffff) && (channel_id >=0) && (channel_id <= 3) )
+            snd_ptr_write(emu, iobase, reg, channel_id, val);
+    }
+#endif
+}
+
+static void snd_emu_proc_ptr_reg_write00(snd_info_entry_t *entry,
+                                         snd_info_buffer_t * buffer)
+{
+    snd_emu_proc_ptr_reg_write(entry, buffer, 0);
+}
+
+static void snd_emu_proc_ptr_reg_write20(snd_info_entry_t *entry,
+                                         snd_info_buffer_t * buffer)
+{
+    snd_emu_proc_ptr_reg_write(entry, buffer, 0x20);
+}
+
+
+static void snd_emu_proc_ptr_reg_read00a(snd_info_entry_t *entry,
+                                         snd_info_buffer_t * buffer)
+{
+    snd_emu_proc_ptr_reg_read(entry, buffer, 0, 0, 0x40);
+}
+
+static void snd_emu_proc_ptr_reg_read00b(snd_info_entry_t *entry,
+                                         snd_info_buffer_t * buffer)
+{
+    snd_emu_proc_ptr_reg_read(entry, buffer, 0, 0x40, 0x40);
+}
+
+static void snd_emu_proc_ptr_reg_read20a(snd_info_entry_t *entry,
+                                         snd_info_buffer_t * buffer)
+{
+    snd_emu_proc_ptr_reg_read(entry, buffer, 0x20, 0, 0x40);
+}
+
+static void snd_emu_proc_ptr_reg_read20b(snd_info_entry_t *entry,
+                                         snd_info_buffer_t * buffer)
+{
+    snd_emu_proc_ptr_reg_read(entry, buffer, 0x20, 0x40, 0x40);
+}
+#endif
+
+
+#ifdef TARGET_OS2
+static struct snd_info_entry_ops snd_emu10k1_proc_ops_fx8010 = {
+    0, 0, snd_emu10k1_fx8010_read, 0, 0,0,0,0
+};
+#else
+static struct snd_info_entry_ops snd_emu10k1_proc_ops_fx8010 = {
+read: snd_emu10k1_fx8010_read,
+};
+#endif
+int __devinit snd_emu10k1_proc_init(struct snd_emu10k1 * emu)
+{
+    snd_info_entry_t *entry;
+#ifdef CONFIG_SND_DEBUG
+    if (! snd_card_proc_new(emu->card, "io_regs", &entry)) {
+        snd_info_set_text_ops(entry, emu, 1024, snd_emu_proc_io_reg_read);
+        entry->c.text.write_size = 64;
+        entry->c.text.write = snd_emu_proc_io_reg_write;
+    }
+    if (! snd_card_proc_new(emu->card, "ptr_regs00a", &entry)) {
+        snd_info_set_text_ops(entry, emu, 65536, snd_emu_proc_ptr_reg_read00a);
+        entry->c.text.write_size = 64;
+        entry->c.text.write = snd_emu_proc_ptr_reg_write00;
+    }
+    if (! snd_card_proc_new(emu->card, "ptr_regs00b", &entry)) {
+        snd_info_set_text_ops(entry, emu, 65536, snd_emu_proc_ptr_reg_read00b);
+        entry->c.text.write_size = 64;
+        entry->c.text.write = snd_emu_proc_ptr_reg_write00;
+    }
+    if (! snd_card_proc_new(emu->card, "ptr_regs20a", &entry)) {
+        snd_info_set_text_ops(entry, emu, 65536, snd_emu_proc_ptr_reg_read20a);
+        entry->c.text.write_size = 64;
+        entry->c.text.write = snd_emu_proc_ptr_reg_write20;
+    }
+    if (! snd_card_proc_new(emu->card, "ptr_regs20b", &entry)) {
+        snd_info_set_text_ops(entry, emu, 65536, snd_emu_proc_ptr_reg_read20b);
+        entry->c.text.write_size = 64;
+        entry->c.text.write = snd_emu_proc_ptr_reg_write20;
+    }
+#endif
+
+    if (! snd_card_proc_new(emu->card, "emu10k1", &entry))
+        snd_info_set_text_ops(entry, emu, 1024, snd_emu10k1_proc_read);
+
+    if (! snd_card_proc_new(emu->card, "voices", &entry))
+        snd_info_set_text_ops(entry, emu, 2048, snd_emu10k1_proc_voices_read);
+
+    if (! snd_card_proc_new(emu->card, "fx8010_gpr", &entry)) {
+        entry->content = SNDRV_INFO_CONTENT_DATA;
+        entry->private_data = emu;
+        entry->mode = S_IFREG | S_IRUGO /*| S_IWUSR*/;
+        entry->size = emu->audigy ? A_TOTAL_SIZE_GPR : TOTAL_SIZE_GPR;
+        entry->c.ops = &snd_emu10k1_proc_ops_fx8010;
+    }
+    if (! snd_card_proc_new(emu->card, "fx8010_tram_data", &entry)) {
+        entry->content = SNDRV_INFO_CONTENT_DATA;
+        entry->private_data = emu;
+        entry->mode = S_IFREG | S_IRUGO /*| S_IWUSR*/;
+        entry->size = emu->audigy ? A_TOTAL_SIZE_TANKMEM_DATA : TOTAL_SIZE_TANKMEM_DATA ;
+        entry->c.ops = &snd_emu10k1_proc_ops_fx8010;
+    }
+    if (! snd_card_proc_new(emu->card, "fx8010_tram_addr", &entry)) {
+        entry->content = SNDRV_INFO_CONTENT_DATA;
+        entry->private_data = emu;
+        entry->mode = S_IFREG | S_IRUGO /*| S_IWUSR*/;
+        entry->size = emu->audigy ? A_TOTAL_SIZE_TANKMEM_ADDR : TOTAL_SIZE_TANKMEM_ADDR ;
+        entry->c.ops = &snd_emu10k1_proc_ops_fx8010;
+    }
+    if (! snd_card_proc_new(emu->card, "fx8010_code", &entry)) {
+        entry->content = SNDRV_INFO_CONTENT_DATA;
+        entry->private_data = emu;
+        entry->mode = S_IFREG | S_IRUGO /*| S_IWUSR*/;
+        entry->size = emu->audigy ? A_TOTAL_SIZE_CODE : TOTAL_SIZE_CODE;
+        entry->c.ops = &snd_emu10k1_proc_ops_fx8010;
+    }
+    if (! snd_card_proc_new(emu->card, "fx8010_acode", &entry)) {
+        entry->content = SNDRV_INFO_CONTENT_TEXT;
+        entry->private_data = emu;
+        entry->mode = S_IFREG | S_IRUGO /*| S_IWUSR*/;
+        entry->c.text.read_size = 128*1024;
+        entry->c.text.read = snd_emu10k1_proc_acode_read;
+    }
+    return 0;
+}
+
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/emu10k1/io.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/emu10k1/io.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/emu10k1/io.c	(revision 91)
@@ -0,0 +1,491 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *                   Creative Labs, Inc.
+ *  Routines for control of EMU10K1 chips
+ *
+ *  BUGS:
+ *    --
+ *
+ *  TODO:
+ *    --
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/emu10k1.h>
+
+unsigned int snd_emu10k1_ptr_read(struct snd_emu10k1 * emu, unsigned int reg, unsigned int chn)
+{
+	unsigned long flags;
+	unsigned int regptr, val;
+	unsigned int mask;
+
+	mask = emu->audigy ? A_PTR_ADDRESS_MASK : PTR_ADDRESS_MASK;
+	regptr = ((reg << 16) & mask) | (chn & PTR_CHANNELNUM_MASK);
+
+	if (reg & 0xff000000) {
+		unsigned char size, offset;
+
+		size = (reg >> 24) & 0x3f;
+		offset = (reg >> 16) & 0x1f;
+		mask = ((1 << size) - 1) << offset;
+
+		spin_lock_irqsave(&emu->emu_lock, flags);
+		outl(regptr, emu->port + PTR);
+		val = inl(emu->port + DATA);
+		spin_unlock_irqrestore(&emu->emu_lock, flags);
+
+		return (val & mask) >> offset;
+	} else {
+		spin_lock_irqsave(&emu->emu_lock, flags);
+		outl(regptr, emu->port + PTR);
+		val = inl(emu->port + DATA);
+		spin_unlock_irqrestore(&emu->emu_lock, flags);
+		return val;
+	}
+}
+
+EXPORT_SYMBOL(snd_emu10k1_ptr_read);
+
+void snd_emu10k1_ptr_write(struct snd_emu10k1 *emu, unsigned int reg, unsigned int chn, unsigned int data)
+{
+	unsigned int regptr;
+	unsigned long flags;
+	unsigned int mask;
+
+	mask = emu->audigy ? A_PTR_ADDRESS_MASK : PTR_ADDRESS_MASK;
+	regptr = ((reg << 16) & mask) | (chn & PTR_CHANNELNUM_MASK);
+
+	if (reg & 0xff000000) {
+		unsigned char size, offset;
+
+		size = (reg >> 24) & 0x3f;
+		offset = (reg >> 16) & 0x1f;
+		mask = ((1 << size) - 1) << offset;
+		data = (data << offset) & mask;
+
+		spin_lock_irqsave(&emu->emu_lock, flags);
+		outl(regptr, emu->port + PTR);
+		data |= inl(emu->port + DATA) & ~mask;
+		outl(data, emu->port + DATA);
+		spin_unlock_irqrestore(&emu->emu_lock, flags);
+	} else {
+		spin_lock_irqsave(&emu->emu_lock, flags);
+		outl(regptr, emu->port + PTR);
+		outl(data, emu->port + DATA);
+		spin_unlock_irqrestore(&emu->emu_lock, flags);
+	}
+}
+
+EXPORT_SYMBOL(snd_emu10k1_ptr_write);
+
+unsigned int snd_emu10k1_ptr20_read(struct snd_emu10k1 * emu,
+                                    unsigned int reg,
+                                    unsigned int chn)
+{
+    unsigned long flags;
+    unsigned int regptr, val;
+
+    regptr = (reg << 16) | chn;
+
+    spin_lock_irqsave(&emu->emu_lock, flags);
+    outl(regptr, emu->port + 0x20 + PTR);
+    val = inl(emu->port + 0x20 + DATA);
+    spin_unlock_irqrestore(&emu->emu_lock, flags);
+    return val;
+}
+
+void snd_emu10k1_ptr20_write(struct snd_emu10k1 *emu,
+                             unsigned int reg,
+                             unsigned int chn,
+                             unsigned int data)
+{
+    unsigned int regptr;
+    unsigned long flags;
+
+    regptr = (reg << 16) | chn;
+
+    spin_lock_irqsave(&emu->emu_lock, flags);
+    outl(regptr, emu->port + 0x20 + PTR);
+    outl(data, emu->port + 0x20 + DATA);
+    spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+int snd_emu10k1_spi_write(struct snd_emu10k1 * emu,
+				   unsigned int data)
+{
+	unsigned int reset, set;
+	unsigned int reg, tmp;
+	int n, result;
+	if (emu->card_capabilities->ca0108_chip)
+		reg = 0x3c; /* PTR20, reg 0x3c */
+	else {
+		/* For other chip types the SPI register
+		 * is currently unknown. */
+		return 1;
+	}
+	if (data > 0xffff) /* Only 16bit values allowed */
+		return 1;
+
+	tmp = snd_emu10k1_ptr20_read(emu, reg, 0);
+	reset = (tmp & ~0x3ffff) | 0x20000; /* Set xxx20000 */
+	set = reset | 0x10000; /* Set xxx1xxxx */
+	snd_emu10k1_ptr20_write(emu, reg, 0, reset | data);
+	tmp = snd_emu10k1_ptr20_read(emu, reg, 0); /* write post */
+	snd_emu10k1_ptr20_write(emu, reg, 0, set | data);
+	result = 1;
+	/* Wait for status bit to return to 0 */
+	for (n = 0; n < 100; n++) {
+		udelay(10);
+		tmp = snd_emu10k1_ptr20_read(emu, reg, 0);
+		if (!(tmp & 0x10000)) {
+			result = 0;
+			break;
+		}
+	}
+	if (result) /* Timed out */
+		return 1;
+	snd_emu10k1_ptr20_write(emu, reg, 0, reset | data);
+	tmp = snd_emu10k1_ptr20_read(emu, reg, 0); /* Write post */
+	return 0;
+}
+
+int snd_emu1010_fpga_write(struct snd_emu10k1 * emu, int reg, int value)
+{
+	if (reg < 0 || reg > 0x3f)
+		return 1;
+	reg += 0x40; /* 0x40 upwards are registers. */
+	if (value < 0 || value > 0x3f) /* 0 to 0x3f are values */
+		return 1;
+	outl(reg, emu->port + A_IOCFG);
+	udelay(10);
+	outl(reg | 0x80, emu->port + A_IOCFG);  /* High bit clocks the value into the fpga. */
+	udelay(10);
+	outl(value, emu->port + A_IOCFG);
+	udelay(10);
+	outl(value | 0x80 , emu->port + A_IOCFG);  /* High bit clocks the value into the fpga. */
+
+	return 0;
+}
+
+int snd_emu1010_fpga_read(struct snd_emu10k1 * emu, int reg, int *value)
+{
+	if (reg < 0 || reg > 0x3f)
+		return 1;
+	reg += 0x40; /* 0x40 upwards are registers. */
+	outl(reg, emu->port + A_IOCFG);
+	udelay(10);
+	outl(reg | 0x80, emu->port + A_IOCFG);  /* High bit clocks the value into the fpga. */
+	udelay(10);
+	*value = ((inl(emu->port + A_IOCFG) >> 8) & 0x7f);
+
+	return 0;
+}
+
+/* Each Destination has one and only one Source,
+ * but one Source can feed any number of Destinations simultaneously.
+ */
+int snd_emu1010_fpga_link_dst_src_write(struct snd_emu10k1 * emu, int dst, int src)
+{
+	snd_emu1010_fpga_write(emu, 0x00, ((dst >> 8) & 0x3f) );
+	snd_emu1010_fpga_write(emu, 0x01, (dst & 0x3f) );
+	snd_emu1010_fpga_write(emu, 0x02, ((src >> 8) & 0x3f) );
+	snd_emu1010_fpga_write(emu, 0x03, (src & 0x3f) );
+
+	return 0;
+}
+
+void snd_emu10k1_intr_enable(struct snd_emu10k1 *emu, unsigned int intrenb)
+{
+	unsigned long flags;
+	unsigned int enable;
+
+	spin_lock_irqsave(&emu->emu_lock, flags);
+	enable = inl(emu->port + INTE) | intrenb;
+	outl(enable, emu->port + INTE);
+	spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+void snd_emu10k1_intr_disable(struct snd_emu10k1 *emu, unsigned int intrenb)
+{
+	unsigned long flags;
+	unsigned int enable;
+
+	spin_lock_irqsave(&emu->emu_lock, flags);
+	enable = inl(emu->port + INTE) & ~intrenb;
+	outl(enable, emu->port + INTE);
+	spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+void snd_emu10k1_voice_intr_enable(struct snd_emu10k1 *emu, unsigned int voicenum)
+{
+	unsigned long flags;
+	unsigned int val;
+
+	spin_lock_irqsave(&emu->emu_lock, flags);
+	/* voice interrupt */
+	if (voicenum >= 32) {
+		outl(CLIEH << 16, emu->port + PTR);
+		val = inl(emu->port + DATA);
+		val |= 1 << (voicenum - 32);
+	} else {
+		outl(CLIEL << 16, emu->port + PTR);
+		val = inl(emu->port + DATA);
+		val |= 1 << voicenum;
+	}
+	outl(val, emu->port + DATA);
+	spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+void snd_emu10k1_voice_intr_disable(struct snd_emu10k1 *emu, unsigned int voicenum)
+{
+	unsigned long flags;
+	unsigned int val;
+
+	spin_lock_irqsave(&emu->emu_lock, flags);
+	/* voice interrupt */
+	if (voicenum >= 32) {
+		outl(CLIEH << 16, emu->port + PTR);
+		val = inl(emu->port + DATA);
+		val &= ~(1 << (voicenum - 32));
+	} else {
+		outl(CLIEL << 16, emu->port + PTR);
+		val = inl(emu->port + DATA);
+		val &= ~(1 << voicenum);
+	}
+	outl(val, emu->port + DATA);
+	spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+void snd_emu10k1_voice_intr_ack(struct snd_emu10k1 *emu, unsigned int voicenum)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&emu->emu_lock, flags);
+	/* voice interrupt */
+	if (voicenum >= 32) {
+		outl(CLIPH << 16, emu->port + PTR);
+		voicenum = 1 << (voicenum - 32);
+	} else {
+		outl(CLIPL << 16, emu->port + PTR);
+		voicenum = 1 << voicenum;
+	}
+	outl(voicenum, emu->port + DATA);
+	spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+void snd_emu10k1_voice_half_loop_intr_enable(struct snd_emu10k1 *emu, unsigned int voicenum)
+{
+    unsigned long flags;
+    unsigned int val;
+
+    spin_lock_irqsave(&emu->emu_lock, flags);
+    /* voice interrupt */
+    if (voicenum >= 32) {
+        outl(HLIEH << 16, emu->port + PTR);
+        val = inl(emu->port + DATA);
+        val |= 1 << (voicenum - 32);
+    } else {
+        outl(HLIEL << 16, emu->port + PTR);
+        val = inl(emu->port + DATA);
+        val |= 1 << voicenum;
+    }
+    outl(val, emu->port + DATA);
+    spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+void snd_emu10k1_voice_half_loop_intr_disable(struct snd_emu10k1 *emu, unsigned int voicenum)
+{
+    unsigned long flags;
+    unsigned int val;
+
+    spin_lock_irqsave(&emu->emu_lock, flags);
+    /* voice interrupt */
+    if (voicenum >= 32) {
+        outl(HLIEH << 16, emu->port + PTR);
+        val = inl(emu->port + DATA);
+        val &= ~(1 << (voicenum - 32));
+    } else {
+        outl(HLIEL << 16, emu->port + PTR);
+        val = inl(emu->port + DATA);
+        val &= ~(1 << voicenum);
+    }
+    outl(val, emu->port + DATA);
+    spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+void snd_emu10k1_voice_half_loop_intr_ack(struct snd_emu10k1 *emu, unsigned int voicenum)
+{
+    unsigned long flags;
+
+    spin_lock_irqsave(&emu->emu_lock, flags);
+    /* voice interrupt */
+    if (voicenum >= 32) {
+        outl(HLIPH << 16, emu->port + PTR);
+        voicenum = 1 << (voicenum - 32);
+    } else {
+        outl(HLIPL << 16, emu->port + PTR);
+        voicenum = 1 << voicenum;
+    }
+    outl(voicenum, emu->port + DATA);
+    spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+void snd_emu10k1_voice_set_loop_stop(struct snd_emu10k1 *emu, unsigned int voicenum)
+{
+	unsigned long flags;
+	unsigned int sol;
+
+	spin_lock_irqsave(&emu->emu_lock, flags);
+	/* voice interrupt */
+	if (voicenum >= 32) {
+		outl(SOLEH << 16, emu->port + PTR);
+		sol = inl(emu->port + DATA);
+		sol |= 1 << (voicenum - 32);
+	} else {
+		outl(SOLEL << 16, emu->port + PTR);
+		sol = inl(emu->port + DATA);
+		sol |= 1 << voicenum;
+	}
+	outl(sol, emu->port + DATA);
+	spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+void snd_emu10k1_voice_clear_loop_stop(struct snd_emu10k1 *emu, unsigned int voicenum)
+{
+	unsigned long flags;
+	unsigned int sol;
+
+	spin_lock_irqsave(&emu->emu_lock, flags);
+	/* voice interrupt */
+	if (voicenum >= 32) {
+		outl(SOLEH << 16, emu->port + PTR);
+		sol = inl(emu->port + DATA);
+		sol &= ~(1 << (voicenum - 32));
+	} else {
+		outl(SOLEL << 16, emu->port + PTR);
+		sol = inl(emu->port + DATA);
+		sol &= ~(1 << voicenum);
+	}
+	outl(sol, emu->port + DATA);
+	spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+void snd_emu10k1_wait(struct snd_emu10k1 *emu, unsigned int wait)
+{
+	volatile unsigned count;
+	unsigned int newtime = 0, curtime;
+
+	curtime = inl(emu->port + WC) >> 6;
+	while (wait-- > 0) {
+		count = 0;
+		while (count++ < 16384) {
+			newtime = inl(emu->port + WC) >> 6;
+			if (newtime != curtime)
+				break;
+		}
+		if (count >= 16384)
+			break;
+		curtime = newtime;
+	}
+}
+
+unsigned short snd_emu10k1_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
+{
+    struct snd_emu10k1 *emu = ac97->private_data;
+	unsigned long flags;
+	unsigned short val;
+
+	spin_lock_irqsave(&emu->emu_lock, flags);
+	outb(reg, emu->port + AC97ADDRESS);
+	val = inw(emu->port + AC97DATA);
+	spin_unlock_irqrestore(&emu->emu_lock, flags);
+	return val;
+}
+
+void snd_emu10k1_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short data)
+{
+    struct snd_emu10k1 *emu = ac97->private_data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&emu->emu_lock, flags);
+	outb(reg, emu->port + AC97ADDRESS);
+	outw(data, emu->port + AC97DATA);
+	spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+/*
+ *  convert rate to pitch
+ */
+
+unsigned int snd_emu10k1_rate_to_pitch(unsigned int rate)
+{
+	static u32 logMagTable[128] = {
+		0x00000, 0x02dfc, 0x05b9e, 0x088e6, 0x0b5d6, 0x0e26f, 0x10eb3, 0x13aa2,
+		0x1663f, 0x1918a, 0x1bc84, 0x1e72e, 0x2118b, 0x23b9a, 0x2655d, 0x28ed5,
+		0x2b803, 0x2e0e8, 0x30985, 0x331db, 0x359eb, 0x381b6, 0x3a93d, 0x3d081,
+		0x3f782, 0x41e42, 0x444c1, 0x46b01, 0x49101, 0x4b6c4, 0x4dc49, 0x50191,
+		0x5269e, 0x54b6f, 0x57006, 0x59463, 0x5b888, 0x5dc74, 0x60029, 0x623a7,
+		0x646ee, 0x66a00, 0x68cdd, 0x6af86, 0x6d1fa, 0x6f43c, 0x7164b, 0x73829,
+		0x759d4, 0x77b4f, 0x79c9a, 0x7bdb5, 0x7dea1, 0x7ff5e, 0x81fed, 0x8404e,
+		0x86082, 0x88089, 0x8a064, 0x8c014, 0x8df98, 0x8fef1, 0x91e20, 0x93d26,
+		0x95c01, 0x97ab4, 0x9993e, 0x9b79f, 0x9d5d9, 0x9f3ec, 0xa11d8, 0xa2f9d,
+		0xa4d3c, 0xa6ab5, 0xa8808, 0xaa537, 0xac241, 0xadf26, 0xafbe7, 0xb1885,
+		0xb3500, 0xb5157, 0xb6d8c, 0xb899f, 0xba58f, 0xbc15e, 0xbdd0c, 0xbf899,
+		0xc1404, 0xc2f50, 0xc4a7b, 0xc6587, 0xc8073, 0xc9b3f, 0xcb5ed, 0xcd07c,
+		0xceaec, 0xd053f, 0xd1f73, 0xd398a, 0xd5384, 0xd6d60, 0xd8720, 0xda0c3,
+		0xdba4a, 0xdd3b4, 0xded03, 0xe0636, 0xe1f4e, 0xe384a, 0xe512c, 0xe69f3,
+		0xe829f, 0xe9b31, 0xeb3a9, 0xecc08, 0xee44c, 0xefc78, 0xf148a, 0xf2c83,
+		0xf4463, 0xf5c2a, 0xf73da, 0xf8b71, 0xfa2f0, 0xfba57, 0xfd1a7, 0xfe8df
+	};
+	static char logSlopeTable[128] = {
+		0x5c, 0x5c, 0x5b, 0x5a, 0x5a, 0x59, 0x58, 0x58,
+		0x57, 0x56, 0x56, 0x55, 0x55, 0x54, 0x53, 0x53,
+		0x52, 0x52, 0x51, 0x51, 0x50, 0x50, 0x4f, 0x4f,
+		0x4e, 0x4d, 0x4d, 0x4d, 0x4c, 0x4c, 0x4b, 0x4b,
+		0x4a, 0x4a, 0x49, 0x49, 0x48, 0x48, 0x47, 0x47,
+		0x47, 0x46, 0x46, 0x45, 0x45, 0x45, 0x44, 0x44,
+		0x43, 0x43, 0x43, 0x42, 0x42, 0x42, 0x41, 0x41,
+		0x41, 0x40, 0x40, 0x40, 0x3f, 0x3f, 0x3f, 0x3e,
+		0x3e, 0x3e, 0x3d, 0x3d, 0x3d, 0x3c, 0x3c, 0x3c,
+		0x3b, 0x3b, 0x3b, 0x3b, 0x3a, 0x3a, 0x3a, 0x39,
+		0x39, 0x39, 0x39, 0x38, 0x38, 0x38, 0x38, 0x37,
+		0x37, 0x37, 0x37, 0x36, 0x36, 0x36, 0x36, 0x35,
+		0x35, 0x35, 0x35, 0x34, 0x34, 0x34, 0x34, 0x34,
+		0x33, 0x33, 0x33, 0x33, 0x32, 0x32, 0x32, 0x32,
+		0x32, 0x31, 0x31, 0x31, 0x31, 0x31, 0x30, 0x30,
+		0x30, 0x30, 0x30, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f
+	};
+	int i;
+
+	if (rate == 0)
+		return 0;	/* Bail out if no leading "1" */
+	rate *= 11185;		/* Scale 48000 to 0x20002380 */
+	for (i = 31; i > 0; i--) {
+		if (rate & 0x80000000) {	/* Detect leading "1" */
+			return (((unsigned int) (i - 15) << 20) +
+			       logMagTable[0x7f & (rate >> 24)] +
+					(0x7f & (rate >> 17)) *
+					logSlopeTable[0x7f & (rate >> 24)]);
+		}
+		rate <<= 1;
+	}
+
+	return 0;		/* Should never reach this point */
+}
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/emu10k1/irq.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/emu10k1/irq.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/emu10k1/irq.c	(revision 91)
@@ -0,0 +1,208 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *                   Creative Labs, Inc.
+ *  Routines for IRQ control of EMU10K1 chips
+ *
+ *  BUGS:
+ *    --
+ *
+ *  TODO:
+ *    --
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/emu10k1.h>
+
+irqreturn_t snd_emu10k1_interrupt(int irq, void *dev_id)
+{
+    struct snd_emu10k1 *emu = dev_id;
+    unsigned int status, status2, orig_status, orig_status2;
+    int handled = 0;
+
+    while ((status = inl(emu->port + IPR)) != 0) {
+        //snd_printk(KERN_INFO "emu10k1 irq - status = 0x%x\n", status);
+        orig_status = status;
+        handled = 1;
+        if ((status & 0xffffffff) == 0xffffffff) {
+            snd_printk(KERN_INFO "snd-emu10k1: Suspected sound card removal\n");
+            break;
+        }
+        if (status & IPR_PCIERROR) {
+            snd_printk("interrupt: PCI error\n");
+            snd_emu10k1_intr_disable(emu, INTE_PCIERRORENABLE);
+            status &= ~IPR_PCIERROR;
+        }
+        if (status & (IPR_VOLINCR|IPR_VOLDECR|IPR_MUTE)) {
+            if (emu->hwvol_interrupt)
+                emu->hwvol_interrupt(emu, status);
+            else
+                snd_emu10k1_intr_disable(emu, INTE_VOLINCRENABLE|INTE_VOLDECRENABLE|INTE_MUTEENABLE);
+            status &= ~(IPR_VOLINCR|IPR_VOLDECR|IPR_MUTE);
+        }
+        if (status & IPR_CHANNELLOOP) {
+            int voice;
+            int voice_max = status & IPR_CHANNELNUMBERMASK;
+            u32 val;
+            struct snd_emu10k1_voice *pvoice = emu->voices;
+
+            val = snd_emu10k1_ptr_read(emu, CLIPL, 0);
+            for (voice = 0; voice <= voice_max; voice++) {
+                if (voice == 0x20)
+                    val = snd_emu10k1_ptr_read(emu, CLIPH, 0);
+                if(val & 1) {
+                    if (pvoice->use && pvoice->interrupt != NULL) {
+                        pvoice->interrupt(emu, pvoice);
+                        snd_emu10k1_voice_intr_ack(emu, voice);
+                    } else {
+                        snd_emu10k1_voice_intr_disable(emu, voice);
+                    }
+                }
+                val >>= 1;
+                pvoice++;
+            }
+            val = snd_emu10k1_ptr_read(emu, HLIPL, 0);
+            for (voice = 0; voice <= voice_max; voice++) {
+                if (voice == 0x20)
+                    val = snd_emu10k1_ptr_read(emu, HLIPH, 0);
+                if (val & 1) {
+                    if (pvoice->use && pvoice->interrupt != NULL) {
+                        pvoice->interrupt(emu, pvoice);
+                        snd_emu10k1_voice_half_loop_intr_ack(emu, voice);
+                    } else {
+                        snd_emu10k1_voice_half_loop_intr_disable(emu, voice);
+                    }
+                }
+                val >>= 1;
+                pvoice++;
+            }
+            status &= ~IPR_CHANNELLOOP;
+        }
+        status &= ~IPR_CHANNELNUMBERMASK;
+        if (status & (IPR_ADCBUFFULL|IPR_ADCBUFHALFFULL)) {
+            if (emu->capture_interrupt)
+                emu->capture_interrupt(emu, status);
+            else
+                snd_emu10k1_intr_disable(emu, INTE_ADCBUFENABLE);
+            status &= ~(IPR_ADCBUFFULL|IPR_ADCBUFHALFFULL);
+        }
+        if (status & (IPR_MICBUFFULL|IPR_MICBUFHALFFULL)) {
+            if (emu->capture_mic_interrupt)
+                emu->capture_mic_interrupt(emu, status);
+            else
+                snd_emu10k1_intr_disable(emu, INTE_MICBUFENABLE);
+            status &= ~(IPR_MICBUFFULL|IPR_MICBUFHALFFULL);
+        }
+        if (status & (IPR_EFXBUFFULL|IPR_EFXBUFHALFFULL)) {
+            if (emu->capture_efx_interrupt)
+                emu->capture_efx_interrupt(emu, status);
+            else
+                snd_emu10k1_intr_disable(emu, INTE_EFXBUFENABLE);
+            status &= ~(IPR_EFXBUFFULL|IPR_EFXBUFHALFFULL);
+        }
+        if (status & (IPR_MIDITRANSBUFEMPTY|IPR_MIDIRECVBUFEMPTY)) {
+            if (emu->midi.interrupt)
+                emu->midi.interrupt(emu, status);
+            else
+                snd_emu10k1_intr_disable(emu, INTE_MIDITXENABLE|INTE_MIDIRXENABLE);
+            status &= ~(IPR_MIDITRANSBUFEMPTY|IPR_MIDIRECVBUFEMPTY);
+        }
+        if (status & (IPR_A_MIDITRANSBUFEMPTY2|IPR_A_MIDIRECVBUFEMPTY2)) {
+            if (emu->midi2.interrupt)
+                emu->midi2.interrupt(emu, status);
+            else
+                snd_emu10k1_intr_disable(emu, INTE_A_MIDITXENABLE2|INTE_A_MIDIRXENABLE2);
+            status &= ~(IPR_A_MIDITRANSBUFEMPTY2|IPR_A_MIDIRECVBUFEMPTY2);
+        }
+        if (status & IPR_INTERVALTIMER) {
+            if (emu->timer)
+                snd_timer_interrupt(emu->timer, emu->timer->sticks);
+            else
+                snd_emu10k1_intr_disable(emu, INTE_INTERVALTIMERENB);
+            status &= ~IPR_INTERVALTIMER;
+        }
+        if (status & (IPR_GPSPDIFSTATUSCHANGE|IPR_CDROMSTATUSCHANGE)) {
+            if (emu->spdif_interrupt)
+                emu->spdif_interrupt(emu, status);
+            else
+                snd_emu10k1_intr_disable(emu, INTE_GPSPDIFENABLE|INTE_CDSPDIFENABLE);
+            status &= ~(IPR_GPSPDIFSTATUSCHANGE|IPR_CDROMSTATUSCHANGE);
+        }
+        if (status & IPR_FXDSP) {
+            if (emu->dsp_interrupt)
+                emu->dsp_interrupt(emu);
+            else
+                snd_emu10k1_intr_disable(emu, INTE_FXDSPENABLE);
+            status &= ~IPR_FXDSP;
+        }
+        if (status & IPR_P16V) {
+            while ((status2 = inl(emu->port + IPR2)) != 0) {
+                u32 mask = INTE2_PLAYBACK_CH_0_LOOP;  /* Full Loop */
+                struct snd_emu10k1_voice *pvoice = &(emu->p16v_voices[0]);
+                struct snd_emu10k1_voice *cvoice = &(emu->p16v_capture_voice);
+
+                //printk(KERN_INFO "status2=0x%x\n", status2);
+                orig_status2 = status2;
+                if(status2 & mask) {
+                    if(pvoice->use) {
+                        snd_pcm_period_elapsed(pvoice->epcm->substream);
+                    } else {
+                        snd_printk(KERN_ERR "p16v: status: 0x%08x, mask=0x%08x, pvoice=%p, use=%d\n", status2, mask, pvoice, pvoice->use);
+                    }
+                }
+                if(status2 & 0x110000) {
+                    //printk(KERN_INFO "capture int found\n");
+                    if(cvoice->use) {
+                        //printk(KERN_INFO "capture period_elapsed\n");
+                        snd_pcm_period_elapsed(cvoice->epcm->substream);
+                    }
+                }
+                outl(orig_status2, emu->port + IPR2); /* ack all */
+            }
+            status &= ~IPR_P16V;
+        }
+
+        if (status) {
+            unsigned int bits;
+            //			snd_printk(KERN_ERR "emu10k1: unhandled interrupt: 0x%08x\n", status);
+            //make sure any interrupts we don't handle are disabled:
+            bits = INTE_FXDSPENABLE |
+                INTE_PCIERRORENABLE |
+                INTE_VOLINCRENABLE |
+                INTE_VOLDECRENABLE |
+                INTE_MUTEENABLE |
+                INTE_MICBUFENABLE |
+                INTE_ADCBUFENABLE |
+                INTE_EFXBUFENABLE |
+                INTE_GPSPDIFENABLE |
+                INTE_CDSPDIFENABLE |
+                INTE_INTERVALTIMERENB |
+                INTE_MIDITXENABLE |
+                INTE_MIDIRXENABLE;
+            if (emu->audigy)
+                bits |= INTE_A_MIDITXENABLE2 | INTE_A_MIDIRXENABLE2;
+            snd_emu10k1_intr_disable(emu, bits);
+        }
+
+        outl(orig_status, emu->port + IPR); /* ack all */
+
+    }
+    return IRQ_RETVAL(handled);
+}
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/emu10k1/makefile.os2
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/emu10k1/makefile.os2	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/emu10k1/makefile.os2	(revision 91)
@@ -0,0 +1,51 @@
+CINCLUDES = ..\..\..\include;..\..\include;$(%WATCOM)\H
+AINCLUDES = $(DDK_BASE)\H
+
+CDEFINES = -D__KERNEL__ -DMODULE -dTARGET_OS2 -dALSA_BUILD -D__ISAPNP__ -DCONFIG_ISAPNP -D__i386_
+
+32BIT=1
+!include ..\..\..\..\makefile.inc
+
+
+#===================================================================
+#
+#   List of source files
+#
+#===================================================================
+FILE1    = emu10k1.obj emu10k1_main.obj 
+FILE2    = irq.obj memory.obj voice.obj emumpu401.obj emupcm.obj io.obj
+FILE3    = emuproc.obj emumixer.obj emufx.obj
+FILE4    = emu10k1_synth.obj emu10k1_callback.obj emu10k1_patch.obj
+FILE5    = timer.obj emu10k1x.obj p16v.obj
+FILE6    = 
+FILE7    = 
+FILE8    = 
+FILELAST =
+FILES    = $(FILE0) $(FILE1) $(FILE2) $(FILE3) $(FILE4) $(FILE5) $(FILE6) $(FILE7) $(FILE8) $(FILE9) $(FILE10) $(FILE11) $(FILE12)
+
+TARGET   = emu10k1
+
+LNKFILE  = $(OBJDIR)\$(TARGET).lnk
+
+
+#===================================================================
+#
+#   Specific dependencies
+#
+#===================================================================
+all: $(OBJDIR)\$(TARGET).lib 
+
+
+$(LNKFILE): makefile.os2
+    @%create $(LNKFILE)
+    @for %f in ($(FILES)) do @%append $(LNKFILE) -+$(OBJDIR)\%f
+
+$(OBJDIR)\$(TARGET).lib: $(LNKFILE) $(FILES)
+    $(LIB) $(OBJDIR)\$(TARGET).lib @$(LNKFILE)
+    copy $(OBJDIR)\$(TARGET).lib $(ALSA_LIB) 
+
+clean: .SYMBOLIC
+    -@rm  *.err 2>nul
+    @cd $(OBJDIR)
+    -rm *.obj *.lnk *.sys *.sym *.lst *.map *.wmap *.lib 2>nul
+    @cd ..\..
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/emu10k1/memory.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/emu10k1/memory.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/emu10k1/memory.c	(revision 91)
@@ -0,0 +1,564 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  Copyright (c) by Takashi Iwai <tiwai@suse.de>
+ *
+ *  EMU10K1 memory page allocation (PTB area)
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/pci.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/emu10k1.h>
+
+/* page arguments of these two macros are Emu page (4096 bytes), not like
+ * aligned pages in others
+ */
+#define __set_ptb_entry(emu,page,addr) \
+    (((u32 *)(emu)->ptb_pages.area)[page] = cpu_to_le32(((addr) << 1) | (page)))
+
+#define UNIT_PAGES		(PAGE_SIZE / EMUPAGESIZE)
+#define MAX_ALIGN_PAGES		(MAXPAGES / UNIT_PAGES)
+/* get aligned page from offset address */
+#define get_aligned_page(offset)	((offset) >> PAGE_SHIFT)
+/* get offset address from aligned page */
+#define aligned_page_offset(page)	((page) << PAGE_SHIFT)
+
+#if PAGE_SIZE == 4096
+/* page size == EMUPAGESIZE */
+/* fill PTB entrie(s) corresponding to page with addr */
+#define set_ptb_entry(emu,page,addr)	__set_ptb_entry(emu,page,addr)
+/* fill PTB entrie(s) corresponding to page with silence pointer */
+#define set_silent_ptb(emu,page)        __set_ptb_entry(emu,page,emu->silent_page.addr)
+#else
+/* fill PTB entries -- we need to fill UNIT_PAGES entries */
+static inline void set_ptb_entry(struct snd_emu10k1 *emu, int page, dma_addr_t addr)
+{
+	int i;
+	page *= UNIT_PAGES;
+	for (i = 0; i < UNIT_PAGES; i++, page++) {
+		__set_ptb_entry(emu, page, addr);
+		addr += EMUPAGESIZE;
+	}
+}
+static inline void set_silent_ptb(struct snd_emu10k1 *emu, int page)
+{
+	int i;
+	page *= UNIT_PAGES;
+	for (i = 0; i < UNIT_PAGES; i++, page++)
+            /* do not increment ptr */
+            __set_ptb_entry(emu, page, emu->silent_page.addr);
+}
+#endif /* PAGE_SIZE */
+
+
+/*
+ */
+static int synth_alloc_pages(struct snd_emu10k1 *hw, struct snd_emu10k1_memblk *blk);
+static int synth_free_pages(struct snd_emu10k1 *hw, struct snd_emu10k1_memblk *blk);
+
+#define get_emu10k1_memblk(l,member)	list_entry(l, struct snd_emu10k1_memblk, member)
+
+
+/* initialize emu10k1 part */
+static void emu10k1_memblk_init(struct snd_emu10k1_memblk *blk)
+{
+	blk->mapped_page = -1;
+	INIT_LIST_HEAD(&blk->mapped_link);
+	INIT_LIST_HEAD(&blk->mapped_order_link);
+	blk->map_locked = 0;
+
+	blk->first_page = get_aligned_page(blk->mem.offset);
+	blk->last_page = get_aligned_page(blk->mem.offset + blk->mem.size - 1);
+	blk->pages = blk->last_page - blk->first_page + 1;
+}
+
+/*
+ * search empty region on PTB with the given size
+ *
+ * if an empty region is found, return the page and store the next mapped block
+ * in nextp
+ * if not found, return a negative error code.
+ */
+static int search_empty_map_area(struct snd_emu10k1 *emu, int npages, struct list_head **nextp)
+{
+	int page = 0, found_page = -ENOMEM;
+	int max_size = npages;
+	int size;
+	struct list_head *candidate = &emu->mapped_link_head;
+	struct list_head *pos;
+
+	list_for_each (pos, &emu->mapped_link_head) {
+		struct snd_emu10k1_memblk *blk = get_emu10k1_memblk(pos, mapped_link);
+		snd_assert(blk->mapped_page >= 0, continue);
+		size = blk->mapped_page - page;
+		if (size == npages) {
+			*nextp = pos;
+			return page;
+		}
+		else if (size > max_size) {
+			/* we look for the maximum empty hole */
+			max_size = size;
+			candidate = pos;
+			found_page = page;
+		}
+		page = blk->mapped_page + blk->pages;
+	}
+	size = MAX_ALIGN_PAGES - page;
+	if (size >= max_size) {
+		*nextp = pos;
+		return page;
+	}
+	*nextp = candidate;
+	return found_page;
+}
+
+/*
+ * map a memory block onto emu10k1's PTB
+ *
+ * call with memblk_lock held
+ */
+static int map_memblk(struct snd_emu10k1 *emu, struct snd_emu10k1_memblk *blk)
+{
+	int page, pg;
+	struct list_head *next;
+
+	page = search_empty_map_area(emu, blk->pages, &next);
+	if (page < 0) /* not found */
+		return page;
+	/* insert this block in the proper position of mapped list */
+	list_add_tail(&blk->mapped_link, next);
+	/* append this as a newest block in order list */
+	list_add_tail(&blk->mapped_order_link, &emu->mapped_order_link_head);
+	blk->mapped_page = page;
+	/* fill PTB */
+	for (pg = blk->first_page; pg <= blk->last_page; pg++) {
+		set_ptb_entry(emu, page, emu->page_addr_table[pg]);
+		page++;
+	}
+	return 0;
+}
+
+/*
+ * unmap the block
+ * return the size of resultant empty pages
+ *
+ * call with memblk_lock held
+ */
+static int unmap_memblk(struct snd_emu10k1 *emu, struct snd_emu10k1_memblk *blk)
+{
+	int start_page, end_page, mpage, pg;
+	struct list_head *p;
+	struct snd_emu10k1_memblk *q;
+
+	/* calculate the expected size of empty region */
+	if ((p = blk->mapped_link.prev) != &emu->mapped_link_head) {
+		q = get_emu10k1_memblk(p, mapped_link);
+		start_page = q->mapped_page + q->pages;
+	} else
+		start_page = 0;
+	if ((p = blk->mapped_link.next) != &emu->mapped_link_head) {
+		q = get_emu10k1_memblk(p, mapped_link);
+		end_page = q->mapped_page;
+	} else
+		end_page = MAX_ALIGN_PAGES;
+
+	/* remove links */
+	list_del(&blk->mapped_link);
+	list_del(&blk->mapped_order_link);
+	/* clear PTB */
+	mpage = blk->mapped_page;
+	for (pg = blk->first_page; pg <= blk->last_page; pg++) {
+		set_silent_ptb(emu, mpage);
+		mpage++;
+	}
+	blk->mapped_page = -1;
+	return end_page - start_page; /* return the new empty size */
+}
+
+/*
+ * search empty pages with the given size, and create a memory block
+ *
+ * unlike synth_alloc the memory block is aligned to the page start
+ */
+static struct snd_emu10k1_memblk *
+search_empty(struct snd_emu10k1 *emu, int size)
+{
+	struct list_head *p;
+	struct snd_emu10k1_memblk *blk;
+	int page, psize;
+
+	psize = get_aligned_page(size + PAGE_SIZE -1);
+	page = 0;
+	list_for_each(p, &emu->memhdr->block) {
+		blk = get_emu10k1_memblk(p, mem.list);
+		if (page + psize <= blk->first_page)
+			goto __found_pages;
+		page = blk->last_page + 1;
+	}
+	if (page + psize > emu->max_cache_pages)
+		return NULL;
+
+__found_pages:
+	/* create a new memory block */
+	blk = (struct snd_emu10k1_memblk *)__snd_util_memblk_new(emu->memhdr, psize << PAGE_SHIFT, p->prev);
+	if (blk == NULL)
+		return NULL;
+	blk->mem.offset = aligned_page_offset(page); /* set aligned offset */
+	emu10k1_memblk_init(blk);
+	return blk;
+}
+
+
+/*
+ * check if the given pointer is valid for pages
+ */
+static int is_valid_page(struct snd_emu10k1 *emu, dma_addr_t addr)
+{
+	if (addr & ~emu->dma_mask) {
+		snd_printk("max memory size is 0x%lx (addr = 0x%lx)!!\n", emu->dma_mask, (unsigned long)addr);
+		return 0;
+	}
+	if (addr & (EMUPAGESIZE-1)) {
+		snd_printk("page is not aligned\n");
+		return 0;
+	}
+	return 1;
+}
+
+/*
+ * map the given memory block on PTB.
+ * if the block is already mapped, update the link order.
+ * if no empty pages are found, tries to release unsed memory blocks
+ * and retry the mapping.
+ */
+int snd_emu10k1_memblk_map(struct snd_emu10k1 *emu, struct snd_emu10k1_memblk *blk)
+{
+	int err;
+	int size;
+	struct list_head *p, *nextp;
+	struct snd_emu10k1_memblk *deleted;
+	unsigned long flags;
+
+	spin_lock_irqsave(&emu->memblk_lock, flags);
+	if (blk->mapped_page >= 0) {
+		/* update order link */
+		list_del(&blk->mapped_order_link);
+		list_add_tail(&blk->mapped_order_link, &emu->mapped_order_link_head);
+		spin_unlock_irqrestore(&emu->memblk_lock, flags);
+		return 0;
+	}
+	if ((err = map_memblk(emu, blk)) < 0) {
+		/* no enough page - try to unmap some blocks */
+		/* starting from the oldest block */
+		p = emu->mapped_order_link_head.next;
+		for (; p != &emu->mapped_order_link_head; p = nextp) {
+			nextp = p->next;
+			deleted = get_emu10k1_memblk(p, mapped_order_link);
+			if (deleted->map_locked)
+				continue;
+			size = unmap_memblk(emu, deleted);
+			if (size >= blk->pages) {
+				/* ok the empty region is enough large */
+				err = map_memblk(emu, blk);
+				break;
+			}
+		}
+	}
+	spin_unlock_irqrestore(&emu->memblk_lock, flags);
+	return err;
+}
+
+/*
+ * page allocation for DMA
+ */
+struct snd_util_memblk *
+snd_emu10k1_alloc_pages(struct snd_emu10k1 *emu, struct snd_pcm_substream *substream)
+{
+    struct snd_pcm_runtime *runtime = substream->runtime;
+    struct snd_sg_buf *sgbuf = snd_pcm_substream_sgbuf(substream);
+    struct snd_util_memhdr *hdr;
+    struct snd_emu10k1_memblk *blk;
+    int page, err, idx;
+
+    snd_assert(emu, return NULL);
+    snd_assert(runtime->dma_bytes > 0 && runtime->dma_bytes < MAXPAGES * EMUPAGESIZE, return NULL);
+    hdr = emu->memhdr;
+    snd_assert(hdr, return NULL);
+
+    down(&hdr->block_mutex);
+    blk = search_empty(emu, runtime->dma_bytes);
+    if (blk == NULL) {
+        up(&hdr->block_mutex);
+        return NULL;
+    }
+    /* fill buffer addresses but pointers are not stored so that
+     * snd_free_pci_page() is not called in in synth_free()
+     */
+    idx = 0;
+    for (page = blk->first_page; page <= blk->last_page; page++, idx++) {
+        dma_addr_t addr;
+#ifdef CONFIG_SND_DEBUG
+        if (idx >= sgbuf->pages) {
+            printk(KERN_ERR "emu: pages overflow! (%d-%d) for %d\n",
+                   blk->first_page, blk->last_page, sgbuf->pages);
+            up(&hdr->block_mutex);
+            return NULL;
+        }
+#endif
+        addr = sgbuf->table[idx].addr;
+        if (! is_valid_page(emu, addr)) {
+            printk(KERN_ERR "emu: failure page = %d\n", idx);
+            up(&hdr->block_mutex);
+            return NULL;
+        }
+        emu->page_addr_table[page] = addr;
+        emu->page_ptr_table[page] = NULL;
+    }
+
+    /* set PTB entries */
+    blk->map_locked = 1; /* do not unmap this block! */
+    err = snd_emu10k1_memblk_map(emu, blk);
+    if (err < 0) {
+        __snd_util_mem_free(hdr, (struct snd_util_memblk *)blk);
+        up(&hdr->block_mutex);
+        return NULL;
+    }
+    up(&hdr->block_mutex);
+    return (struct snd_util_memblk *)blk;
+}
+
+
+/*
+ * release DMA buffer from page table
+ */
+int snd_emu10k1_free_pages(struct snd_emu10k1 *emu, struct snd_util_memblk *blk)
+{
+	snd_assert(emu && blk, return -EINVAL);
+	return snd_emu10k1_synth_free(emu, blk);
+}
+
+
+/*
+ * memory allocation using multiple pages (for synth)
+ * Unlike the DMA allocation above, non-contiguous pages are assined.
+ */
+
+/*
+ * allocate a synth sample area
+ */
+struct snd_util_memblk *
+snd_emu10k1_synth_alloc(struct snd_emu10k1 *hw, unsigned int size)
+{
+    struct snd_emu10k1_memblk *blk;
+    struct snd_util_memhdr *hdr = hw->memhdr; 
+
+	down(&hdr->block_mutex);
+	blk = (struct snd_emu10k1_memblk *)__snd_util_mem_alloc(hdr, size);
+	if (blk == NULL) {
+		up(&hdr->block_mutex);
+		return NULL;
+	}
+        if (synth_alloc_pages(hw, blk)) {
+            __snd_util_mem_free(hdr, (struct snd_util_memblk *)blk);
+		up(&hdr->block_mutex);
+		return NULL;
+	}
+	snd_emu10k1_memblk_map(hw, blk);
+	up(&hdr->block_mutex);
+	return (struct snd_util_memblk *)blk;
+}
+
+
+/*
+ * free a synth sample area
+ */
+int
+snd_emu10k1_synth_free(struct snd_emu10k1 *emu, snd_util_memblk_t *memblk)
+{
+	snd_util_memhdr_t *hdr = emu->memhdr;
+	struct snd_emu10k1_memblk *blk = (struct snd_emu10k1_memblk *)memblk;
+	unsigned long flags;
+
+	down(&hdr->block_mutex);
+	spin_lock_irqsave(&emu->memblk_lock, flags);
+	if (blk->mapped_page >= 0)
+		unmap_memblk(emu, blk);
+	spin_unlock_irqrestore(&emu->memblk_lock, flags);
+	synth_free_pages(emu, blk);
+	 __snd_util_mem_free(hdr, memblk);
+	up(&hdr->block_mutex);
+	return 0;
+}
+
+
+/* check new allocation range */
+static void get_single_page_range(snd_util_memhdr_t *hdr, struct snd_emu10k1_memblk *blk, int *first_page_ret, int *last_page_ret)
+{
+	struct list_head *p;
+	struct snd_emu10k1_memblk *q;
+	int first_page, last_page;
+	first_page = blk->first_page;
+	if ((p = blk->mem.list.prev) != &hdr->block) {
+		q = get_emu10k1_memblk(p, mem.list);
+		if (q->last_page == first_page)
+			first_page++;  /* first page was already allocated */
+	}
+	last_page = blk->last_page;
+	if ((p = blk->mem.list.next) != &hdr->block) {
+		q = get_emu10k1_memblk(p, mem.list);
+		if (q->first_page == last_page)
+			last_page--; /* last page was already allocated */
+	}
+	*first_page_ret = first_page;
+	*last_page_ret = last_page;
+}
+
+/*
+ * allocate kernel pages
+ */
+static int synth_alloc_pages(struct snd_emu10k1 *emu, struct snd_emu10k1_memblk *blk)
+{
+    int page, first_page, last_page;
+    struct snd_dma_buffer dmab;
+
+    emu10k1_memblk_init(blk);
+    get_single_page_range(emu->memhdr, blk, &first_page, &last_page);
+    /* allocate kernel pages */
+    for (page = first_page; page <= last_page; page++) {
+        if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(emu->pci),
+                                PAGE_SIZE, &dmab) < 0)
+            goto __fail;
+        if (! is_valid_page(emu, dmab.addr)) {
+            snd_dma_free_pages(&dmab);
+            goto __fail;
+        }
+        emu->page_addr_table[page] = dmab.addr;
+        emu->page_ptr_table[page] = dmab.area;
+    }
+    return 0;
+
+__fail:
+    /* release allocated pages */
+    last_page = page - 1;
+    for (page = first_page; page <= last_page; page++) {
+        dmab.area = emu->page_ptr_table[page];
+        dmab.addr = emu->page_addr_table[page];
+        dmab.bytes = PAGE_SIZE;
+        snd_dma_free_pages(&dmab);
+        emu->page_addr_table[page] = 0;
+        emu->page_ptr_table[page] = NULL;
+    }
+
+    return -ENOMEM;
+}
+
+/*
+ * free pages
+ */
+static int synth_free_pages(struct snd_emu10k1 *emu, struct snd_emu10k1_memblk *blk)
+{
+    int page, first_page, last_page;
+    struct snd_dma_buffer dmab;
+
+    get_single_page_range(emu->memhdr, blk, &first_page, &last_page);
+    dmab.dev.type = SNDRV_DMA_TYPE_DEV;
+    dmab.dev.dev = snd_dma_pci_data(emu->pci);
+        for (page = first_page; page <= last_page; page++) {
+            if (emu->page_ptr_table[page] == NULL)
+                continue;
+            dmab.area = emu->page_ptr_table[page];
+            dmab.addr = emu->page_addr_table[page];
+            dmab.bytes = PAGE_SIZE;
+            snd_dma_free_pages(&dmab);
+            emu->page_addr_table[page] = 0;
+            emu->page_ptr_table[page] = NULL;
+	}
+
+	return 0;
+}
+
+/* calculate buffer pointer from offset address */
+inline static void *offset_ptr(struct snd_emu10k1 *emu, int page, int offset)
+{
+	char *ptr;
+	snd_assert(page >= 0 && page < emu->max_cache_pages, return NULL);
+	ptr = emu->page_ptr_table[page];
+	if (! ptr) {
+		printk("emu10k1: access to NULL ptr: page = %d\n", page);
+		return NULL;
+	}
+	ptr += offset & (PAGE_SIZE - 1);
+	return (void*)ptr;
+}
+
+/*
+ * bzero(blk + offset, size)
+ */
+int snd_emu10k1_synth_bzero(struct snd_emu10k1 *emu, snd_util_memblk_t *blk, int offset, int size)
+{
+	int page, nextofs, end_offset, temp, temp1;
+	void *ptr;
+	struct snd_emu10k1_memblk *p = (struct snd_emu10k1_memblk *)blk;
+
+	offset += blk->offset & (PAGE_SIZE - 1);
+	end_offset = offset + size;
+	page = get_aligned_page(offset);
+	do {
+		nextofs = aligned_page_offset(page + 1);
+		temp = nextofs - offset;
+		temp1 = end_offset - offset;
+		if (temp1 < temp)
+			temp = temp1;
+		ptr = offset_ptr(emu, page + p->first_page, offset);
+		if (ptr)
+			memset(ptr, 0, temp);
+		offset = nextofs;
+		page++;
+	} while (offset < end_offset);
+	return 0;
+}
+
+/*
+ * copy_from_user(blk + offset, data, size)
+ */
+int snd_emu10k1_synth_copy_from_user(struct snd_emu10k1 *emu, snd_util_memblk_t *blk, int offset, const char *data, int size)
+{
+	int page, nextofs, end_offset, temp, temp1;
+	void *ptr;
+	struct snd_emu10k1_memblk *p = (struct snd_emu10k1_memblk *)blk;
+
+	offset += blk->offset & (PAGE_SIZE - 1);
+	end_offset = offset + size;
+	page = get_aligned_page(offset);
+	do {
+		nextofs = aligned_page_offset(page + 1);
+		temp = nextofs - offset;
+		temp1 = end_offset - offset;
+		if (temp1 < temp)
+			temp = temp1;
+		ptr = offset_ptr(emu, page + p->first_page, offset);
+		if (ptr && copy_from_user(ptr, data, temp))
+			return -EFAULT;
+		offset = nextofs;
+		data += temp;
+		page++;
+	} while (offset < end_offset);
+	return 0;
+}
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/emu10k1/p16v.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/emu10k1/p16v.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/emu10k1/p16v.c	(revision 91)
@@ -0,0 +1,878 @@
+/*
+ *  Copyright (c) by James Courtier-Dutton <James@superbug.demon.co.uk>
+ *  Driver p16v chips
+ *  Version: 0.25
+ *
+ *  FEATURES currently supported:
+ *    Output fixed at S32_LE, 2 channel to hw:0,0
+ *    Rates: 44.1, 48, 96, 192.
+ *
+ *  Changelog:
+ *  0.8
+ *    Use separate card based buffer for periods table.
+ *  0.9
+ *    Use 2 channel output streams instead of 8 channel.
+ *       (8 channel output streams might be good for ASIO type output)
+ *    Corrected speaker output, so Front -> Front etc.
+ *  0.10
+ *    Fixed missed interrupts.
+ *  0.11
+ *    Add Sound card model number and names.
+ *    Add Analog volume controls.
+ *  0.12
+ *    Corrected playback interrupts. Now interrupt per period, instead of half period.
+ *  0.13
+ *    Use single trigger for multichannel.
+ *  0.14
+ *    Mic capture now works at fixed: S32_LE, 96000Hz, Stereo.
+ *  0.15
+ *    Force buffer_size / period_size == INTEGER.
+ *  0.16
+ *    Update p16v.c to work with changed alsa api.
+ *  0.17
+ *    Update p16v.c to work with changed alsa api. Removed boot_devs.
+ *  0.18
+ *    Merging with snd-emu10k1 driver.
+ *  0.19
+ *    One stereo channel at 24bit now works.
+ *  0.20
+ *    Added better register defines.
+ *  0.21
+ *    Integrated with snd-emu10k1 driver.
+ *  0.22
+ *    Removed #if 0 ... #endif
+ *  0.23
+ *    Implement different capture rates.
+ *  0.24
+ *    Implement different capture source channels.
+ *    e.g. When HD Capture source is set to SPDIF,
+ *    setting HD Capture channel to 0 captures from CDROM digital input.
+ *    setting HD Capture channel to 1 captures from SPDIF in.
+ *  0.25
+ *    Include capture buffer sizes.
+ *
+ *  BUGS:
+ *    Some stability problems when unloading the snd-p16v kernel module.
+ *    --
+ *
+ *  TODO:
+ *    SPDIF out.
+ *    Find out how to change capture sample rates. E.g. To record SPDIF at 48000Hz.
+ *    Currently capture fixed at 48000Hz.
+ *
+ *    --
+ *  GENERAL INFO:
+ *    Model: SB0240
+ *    P16V Chip: CA0151-DBS
+ *    Audigy 2 Chip: CA0102-IAT
+ *    AC97 Codec: STAC 9721
+ *    ADC: Philips 1361T (Stereo 24bit)
+ *    DAC: CS4382-K (8-channel, 24bit, 192Khz)
+ *
+ *  This code was initally based on code from ALSA's emu10k1x.c which is:
+ *  Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include <sound/info.h>
+#include <sound/tlv.h>
+#include <sound/emu10k1.h>
+#include "p16v.h"
+
+#define SET_CHANNEL 0  /* Testing channel outputs 0=Front, 1=Center/LFE, 2=Unknown, 3=Rear */
+#define PCM_FRONT_CHANNEL 0
+#define PCM_REAR_CHANNEL 1
+#define PCM_CENTER_LFE_CHANNEL 2
+#define PCM_SIDE_CHANNEL 3
+#define CONTROL_FRONT_CHANNEL 0
+#define CONTROL_REAR_CHANNEL 3
+#define CONTROL_CENTER_LFE_CHANNEL 1
+#define CONTROL_SIDE_CHANNEL 2
+
+/* Card IDs:
+ * Class 0401: 1102:0004 (rev 04) Subsystem: 1102:2002 -> Audigy2 ZS 7.1 Model:SB0350
+ * Class 0401: 1102:0004 (rev 04) Subsystem: 1102:1007 -> Audigy2 6.1    Model:SB0240
+ * Class 0401: 1102:0004 (rev 04) Subsystem: 1102:1002 -> Audigy2 Platinum  Model:SB msb0240230009266
+ * Class 0401: 1102:0004 (rev 04) Subsystem: 1102:2007 -> Audigy4 Pro Model:SB0380 M1SB0380472001901E
+ *
+ */
+
+/* hardware definition */
+static struct snd_pcm_hardware snd_p16v_playback_hw = {
+	.info =			(SNDRV_PCM_INFO_MMAP | 
+				 SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_RESUME |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+	.formats =		SNDRV_PCM_FMTBIT_S32_LE, /* Only supports 24-bit samples padded to 32 bits. */
+	.rates =		SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100, 
+	.rate_min =		44100,
+	.rate_max =		192000,
+	.channels_min =		8, 
+	.channels_max =		8,
+	.buffer_bytes_max =	((65536 - 64) * 8),
+	.period_bytes_min =	64,
+	.period_bytes_max =	(65536 - 64),
+	.periods_min =		2,
+	.periods_max =		8,
+	.fifo_size =		0,
+};
+
+static struct snd_pcm_hardware snd_p16v_capture_hw = {
+	.info =			(SNDRV_PCM_INFO_MMAP |
+				 SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_RESUME |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+	.formats =		SNDRV_PCM_FMTBIT_S32_LE,
+	.rates =		SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100, 
+	.rate_min =		44100,
+	.rate_max =		192000,
+	.channels_min =		2,
+	.channels_max =		2,
+	.buffer_bytes_max =	(65536 - 64),
+	.period_bytes_min =	64,
+	.period_bytes_max =	(65536 - 128) >> 1,  /* size has to be N*64 bytes */
+	.periods_min =		2,
+	.periods_max =		2,
+	.fifo_size =		0,
+};
+
+static void snd_p16v_pcm_free_substream(struct snd_pcm_runtime *runtime)
+{
+    struct snd_emu10k1_pcm *epcm = runtime->private_data;
+
+    if (epcm) {
+        //snd_printk("epcm free: %p\n", epcm);
+        kfree(epcm);
+    }
+}
+
+/* open_playback callback */
+static int snd_p16v_pcm_open_playback_channel(struct snd_pcm_substream *substream, int channel_id)
+{
+    struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+    struct snd_emu10k1_voice *channel = &(emu->p16v_voices[channel_id]);
+    struct snd_emu10k1_pcm *epcm;
+    struct snd_pcm_runtime *runtime = substream->runtime;
+    int err;
+
+    epcm = kzalloc(sizeof(*epcm), GFP_KERNEL);
+    //snd_printk("epcm kcalloc: %p\n", epcm);
+
+    if (epcm == NULL)
+        return -ENOMEM;
+    epcm->emu = emu;
+    epcm->substream = substream;
+    //snd_printk("epcm device=%d, channel_id=%d\n", substream->pcm->device, channel_id);
+
+    runtime->private_data = epcm;
+    runtime->private_free = snd_p16v_pcm_free_substream;
+
+    runtime->hw = snd_p16v_playback_hw;
+
+    channel->emu = emu;
+    channel->number = channel_id;
+
+    channel->use=1;
+    //snd_printk("p16v: open channel_id=%d, channel=%p, use=0x%x\n", channel_id, channel, channel->use);
+    //printk("open:channel_id=%d, chip=%p, channel=%p\n",channel_id, chip, channel);
+    //channel->interrupt = snd_p16v_pcm_channel_interrupt;
+    channel->epcm=epcm;
+    if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+        return err;
+
+    return 0;
+}
+
+/* open_capture callback */
+static int snd_p16v_pcm_open_capture_channel(struct snd_pcm_substream *substream, int channel_id)
+{
+    struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+    struct snd_emu10k1_voice *channel = &(emu->p16v_capture_voice);
+    struct snd_emu10k1_pcm *epcm;
+    struct snd_pcm_runtime *runtime = substream->runtime;
+    int err;
+
+    epcm = kzalloc(sizeof(*epcm), GFP_KERNEL);
+    //snd_printk("epcm kcalloc: %p\n", epcm);
+
+    if (epcm == NULL)
+        return -ENOMEM;
+    epcm->emu = emu;
+    epcm->substream = substream;
+    //snd_printk("epcm device=%d, channel_id=%d\n", substream->pcm->device, channel_id);
+
+    runtime->private_data = epcm;
+    runtime->private_free = snd_p16v_pcm_free_substream;
+
+    runtime->hw = snd_p16v_capture_hw;
+
+    channel->emu = emu;
+    channel->number = channel_id;
+
+    channel->use=1;
+    //snd_printk("p16v: open channel_id=%d, channel=%p, use=0x%x\n", channel_id, channel, channel->use);
+    //printk("open:channel_id=%d, chip=%p, channel=%p\n",channel_id, chip, channel);
+    //channel->interrupt = snd_p16v_pcm_channel_interrupt;
+    channel->epcm=epcm;
+    if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+        return err;
+
+    return 0;
+}
+
+
+/* close callback */
+static int snd_p16v_pcm_close_playback(struct snd_pcm_substream *substream)
+{
+    struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+    //struct snd_pcm_runtime *runtime = substream->runtime;
+    //struct snd_emu10k1_pcm *epcm = runtime->private_data;
+    emu->p16v_voices[substream->pcm->device - emu->p16v_device_offset].use=0;
+    /* FIXME: maybe zero others */
+    return 0;
+}
+
+/* close callback */
+static int snd_p16v_pcm_close_capture(struct snd_pcm_substream *substream)
+{
+    struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+    //struct snd_pcm_runtime *runtime = substream->runtime;
+    //struct snd_emu10k1_pcm *epcm = runtime->private_data;
+    emu->p16v_capture_voice.use=0;
+    /* FIXME: maybe zero others */
+    return 0;
+}
+
+static int snd_p16v_pcm_open_playback_front(struct snd_pcm_substream *substream)
+{
+    return snd_p16v_pcm_open_playback_channel(substream, PCM_FRONT_CHANNEL);
+}
+
+static int snd_p16v_pcm_open_capture(struct snd_pcm_substream *substream)
+{
+    // Only using channel 0 for now, but the card has 2 channels.
+    return snd_p16v_pcm_open_capture_channel(substream, 0);
+}
+
+/* hw_params callback */
+static int snd_p16v_pcm_hw_params_playback(struct snd_pcm_substream *substream,
+				      struct snd_pcm_hw_params *hw_params)
+{
+    int result;
+    result = snd_pcm_lib_malloc_pages(substream,
+                                      params_buffer_bytes(hw_params));
+    return result;
+}
+
+/* hw_params callback */
+static int snd_p16v_pcm_hw_params_capture(struct snd_pcm_substream *substream,
+				      struct snd_pcm_hw_params *hw_params)
+{
+    int result;
+    result = snd_pcm_lib_malloc_pages(substream,
+                                      params_buffer_bytes(hw_params));
+    return result;
+}
+
+
+/* hw_free callback */
+static int snd_p16v_pcm_hw_free_playback(struct snd_pcm_substream *substream)
+{
+    int result;
+    result = snd_pcm_lib_free_pages(substream);
+    return result;
+}
+
+/* hw_free callback */
+static int snd_p16v_pcm_hw_free_capture(struct snd_pcm_substream *substream)
+{
+    int result;
+    result = snd_pcm_lib_free_pages(substream);
+    return result;
+}
+
+
+/* prepare playback callback */
+static int snd_p16v_pcm_prepare_playback(struct snd_pcm_substream *substream)
+{
+    struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+    struct snd_pcm_runtime *runtime = substream->runtime;
+    int channel = substream->pcm->device - emu->p16v_device_offset;
+    u32 *table_base = (u32 *)(emu->p16v_buffer.area+(8*16*channel));
+    u32 period_size_bytes = frames_to_bytes(runtime, runtime->period_size);
+    int i;
+    u32 tmp;
+
+    //snd_printk("prepare:channel_number=%d, rate=%d, format=0x%x, channels=%d, buffer_size=%ld, period_size=%ld, periods=%u, frames_to_bytes=%d\n",channel, runtime->rate, runtime->format, runtime->channels, runtime->buffer_size, runtime->period_size, runtime->periods, frames_to_bytes(runtime, 1));
+    //snd_printk("dma_addr=%x, dma_area=%p, table_base=%p\n",runtime->dma_addr, runtime->dma_area, table_base);
+    //snd_printk("dma_addr=%x, dma_area=%p, dma_bytes(size)=%x\n",emu->p16v_buffer.addr, emu->p16v_buffer.area, emu->p16v_buffer.bytes);
+    tmp = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, channel);
+    switch (runtime->rate) {
+    case 44100:
+        snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0xe0e0) | 0x8080);
+        break;
+    case 96000:
+        snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0xe0e0) | 0x4040);
+        break;
+    case 192000:
+        snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0xe0e0) | 0x2020);
+        break;
+    case 48000:
+    default:
+        snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0xe0e0) | 0x0000);
+        break;
+    }
+    /* FIXME: Check emu->buffer.size before actually writing to it. */
+    for(i=0; i < runtime->periods; i++) {
+        table_base[i*2]=runtime->dma_addr+(i*period_size_bytes);
+        table_base[(i*2)+1]=period_size_bytes<<16;
+    }
+
+    snd_emu10k1_ptr20_write(emu, PLAYBACK_LIST_ADDR, channel, emu->p16v_buffer.addr+(8*16*channel));
+    snd_emu10k1_ptr20_write(emu, PLAYBACK_LIST_SIZE, channel, (runtime->periods - 1) << 19);
+    snd_emu10k1_ptr20_write(emu, PLAYBACK_LIST_PTR, channel, 0);
+    snd_emu10k1_ptr20_write(emu, PLAYBACK_DMA_ADDR, channel, runtime->dma_addr);
+    //snd_emu10k1_ptr20_write(emu, PLAYBACK_PERIOD_SIZE, channel, frames_to_bytes(runtime, runtime->period_size)<<16); // buffer size in bytes
+    snd_emu10k1_ptr20_write(emu, PLAYBACK_PERIOD_SIZE, channel, 0); // buffer size in bytes
+    snd_emu10k1_ptr20_write(emu, PLAYBACK_POINTER, channel, 0);
+    snd_emu10k1_ptr20_write(emu, 0x07, channel, 0x0);
+    snd_emu10k1_ptr20_write(emu, 0x08, channel, 0);
+
+    return 0;
+}
+
+/* prepare capture callback */
+static int snd_p16v_pcm_prepare_capture(struct snd_pcm_substream *substream)
+{
+    struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+    struct snd_pcm_runtime *runtime = substream->runtime;
+    int channel = substream->pcm->device - emu->p16v_device_offset;
+    u32 tmp;
+    //printk("prepare capture:channel_number=%d, rate=%d, format=0x%x, channels=%d, buffer_size=%ld, period_size=%ld, frames_to_bytes=%d\n",channel, runtime->rate, runtime->format, runtime->channels, runtime->buffer_size, runtime->period_size,  frames_to_bytes(runtime, 1));
+    tmp = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, channel);
+    switch (runtime->rate) {
+    case 44100:
+        snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0x0e00) | 0x0800);
+        break;
+    case 96000:
+        snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0x0e00) | 0x0400);
+        break;
+    case 192000:
+        snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0x0e00) | 0x0200);
+        break;
+    case 48000:
+    default:
+        snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0x0e00) | 0x0000);
+        break;
+    }
+    /* FIXME: Check emu->buffer.size before actually writing to it. */
+    snd_emu10k1_ptr20_write(emu, 0x13, channel, 0);
+    snd_emu10k1_ptr20_write(emu, CAPTURE_DMA_ADDR, channel, runtime->dma_addr);
+    snd_emu10k1_ptr20_write(emu, CAPTURE_BUFFER_SIZE, channel, frames_to_bytes(runtime, runtime->buffer_size)<<16); // buffer size in bytes
+    snd_emu10k1_ptr20_write(emu, CAPTURE_POINTER, channel, 0);
+    //snd_emu10k1_ptr20_write(emu, CAPTURE_SOURCE, 0x0, 0x333300e4); /* Select MIC or Line in */
+    //snd_emu10k1_ptr20_write(emu, EXTENDED_INT_MASK, 0, snd_emu10k1_ptr20_read(emu, EXTENDED_INT_MASK, 0) | (0x110000<<channel));
+
+    return 0;
+}
+
+static void snd_p16v_intr_enable(struct snd_emu10k1 *emu, unsigned int intrenb)
+{
+    unsigned long flags;
+    unsigned int enable;
+
+    spin_lock_irqsave(&emu->emu_lock, flags);
+    enable = inl(emu->port + INTE2) | intrenb;
+    outl(enable, emu->port + INTE2);
+    spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+static void snd_p16v_intr_disable(struct snd_emu10k1 *emu, unsigned int intrenb)
+{
+    unsigned long flags;
+    unsigned int disable;
+
+    spin_lock_irqsave(&emu->emu_lock, flags);
+    disable = inl(emu->port + INTE2) & (~intrenb);
+    outl(disable, emu->port + INTE2);
+    spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+/* trigger_playback callback */
+static int snd_p16v_pcm_trigger_playback(struct snd_pcm_substream *substream,
+                                         int cmd)
+{
+    struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+    struct snd_pcm_runtime *runtime;
+    struct snd_emu10k1_pcm *epcm;
+    int channel;
+    int result = 0;
+    struct list_head *pos;
+    struct snd_pcm_substream *s;
+    u32 basic = 0;
+    u32 inte = 0;
+    int running=0;
+
+    switch (cmd) {
+    case SNDRV_PCM_TRIGGER_START:
+        running=1;
+        break;
+    case SNDRV_PCM_TRIGGER_STOP:
+    default:
+        running=0;
+        break;
+    }
+    snd_pcm_group_for_each(pos, substream) {
+        s = snd_pcm_group_substream_entry(pos);
+        runtime = s->runtime;
+        epcm = runtime->private_data;
+        channel = substream->pcm->device-emu->p16v_device_offset;
+        //snd_printk("p16v channel=%d\n",channel);
+        epcm->running = running;
+        basic |= (0x1<<channel);
+        inte |= (INTE2_PLAYBACK_CH_0_LOOP<<channel);
+        snd_pcm_trigger_done(s, substream);
+    }
+    //snd_printk("basic=0x%x, inte=0x%x\n",basic, inte);
+
+    switch (cmd) {
+    case SNDRV_PCM_TRIGGER_START:
+        snd_p16v_intr_enable(emu, inte);
+        snd_emu10k1_ptr20_write(emu, BASIC_INTERRUPT, 0, snd_emu10k1_ptr20_read(emu, BASIC_INTERRUPT, 0)| (basic));
+        break;
+    case SNDRV_PCM_TRIGGER_STOP:
+        snd_emu10k1_ptr20_write(emu, BASIC_INTERRUPT, 0, snd_emu10k1_ptr20_read(emu, BASIC_INTERRUPT, 0) & ~(basic));
+        snd_p16v_intr_disable(emu, inte);
+        break;
+    default:
+        result = -EINVAL;
+        break;
+    }
+    return result;
+}
+
+/* trigger_capture callback */
+static int snd_p16v_pcm_trigger_capture(struct snd_pcm_substream *substream,
+                                        int cmd)
+{
+    struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+    struct snd_pcm_runtime *runtime = substream->runtime;
+    struct snd_emu10k1_pcm *epcm = runtime->private_data;
+    int channel = 0;
+    int result = 0;
+    u32 inte = INTE2_CAPTURE_CH_0_LOOP | INTE2_CAPTURE_CH_0_HALF_LOOP;
+
+    switch (cmd) {
+    case SNDRV_PCM_TRIGGER_START:
+        snd_p16v_intr_enable(emu, inte);
+        snd_emu10k1_ptr20_write(emu, BASIC_INTERRUPT, 0, snd_emu10k1_ptr20_read(emu, BASIC_INTERRUPT, 0)|(0x100<<channel));
+        epcm->running = 1;
+        break;
+    case SNDRV_PCM_TRIGGER_STOP:
+        snd_emu10k1_ptr20_write(emu, BASIC_INTERRUPT, 0, snd_emu10k1_ptr20_read(emu, BASIC_INTERRUPT, 0) & ~(0x100<<channel));
+        snd_p16v_intr_disable(emu, inte);
+        //snd_emu10k1_ptr20_write(emu, EXTENDED_INT_MASK, 0, snd_emu10k1_ptr20_read(emu, EXTENDED_INT_MASK, 0) & ~(0x110000<<channel));
+        epcm->running = 0;
+        break;
+    default:
+        result = -EINVAL;
+        break;
+    }
+    return result;
+}
+
+/* pointer_playback callback */
+static snd_pcm_uframes_t
+snd_p16v_pcm_pointer_playback(struct snd_pcm_substream *substream)
+{
+    struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+    struct snd_pcm_runtime *runtime = substream->runtime;
+    struct snd_emu10k1_pcm *epcm = runtime->private_data;
+    snd_pcm_uframes_t ptr, ptr1, ptr2,ptr3,ptr4 = 0;
+    int channel = substream->pcm->device - emu->p16v_device_offset;
+    if (!epcm->running)
+        return 0;
+
+    ptr3 = snd_emu10k1_ptr20_read(emu, PLAYBACK_LIST_PTR, channel);
+    ptr1 = snd_emu10k1_ptr20_read(emu, PLAYBACK_POINTER, channel);
+    ptr4 = snd_emu10k1_ptr20_read(emu, PLAYBACK_LIST_PTR, channel);
+    if (ptr3 != ptr4) ptr1 = snd_emu10k1_ptr20_read(emu, PLAYBACK_POINTER, channel);
+    ptr2 = bytes_to_frames(runtime, ptr1);
+    ptr2+= (ptr4 >> 3) * runtime->period_size;
+    ptr=ptr2;
+    if (ptr >= runtime->buffer_size)
+        ptr -= runtime->buffer_size;
+
+    return ptr;
+}
+
+/* pointer_capture callback */
+static snd_pcm_uframes_t
+snd_p16v_pcm_pointer_capture(struct snd_pcm_substream *substream)
+{
+    struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+    struct snd_pcm_runtime *runtime = substream->runtime;
+    struct snd_emu10k1_pcm *epcm = runtime->private_data;
+    snd_pcm_uframes_t ptr, ptr1, ptr2 = 0;
+    int channel = 0;
+
+    if (!epcm->running)
+        return 0;
+
+    ptr1 = snd_emu10k1_ptr20_read(emu, CAPTURE_POINTER, channel);
+    ptr2 = bytes_to_frames(runtime, ptr1);
+    ptr=ptr2;
+    if (ptr >= runtime->buffer_size) {
+        ptr -= runtime->buffer_size;
+        printk("buffer capture limited!\n");
+    }
+    //printk("ptr1 = 0x%lx, ptr2=0x%lx, ptr=0x%lx, buffer_size = 0x%x, period_size = 0x%x, bits=%d, rate=%d\n", ptr1, ptr2, ptr, (int)runtime->buffer_size, (int)runtime->period_size, (int)runtime->frame_bits, (int)runtime->rate);
+
+    return ptr;
+}
+
+/* operators */
+static struct snd_pcm_ops snd_p16v_playback_front_ops = {
+	.open =        snd_p16v_pcm_open_playback_front,
+	.close =       snd_p16v_pcm_close_playback,
+	.ioctl =       snd_pcm_lib_ioctl,
+	.hw_params =   snd_p16v_pcm_hw_params_playback,
+	.hw_free =     snd_p16v_pcm_hw_free_playback,
+	.prepare =     snd_p16v_pcm_prepare_playback,
+	.trigger =     snd_p16v_pcm_trigger_playback,
+	.pointer =     snd_p16v_pcm_pointer_playback,
+};
+
+static struct snd_pcm_ops snd_p16v_capture_ops = {
+	.open =        snd_p16v_pcm_open_capture,
+	.close =       snd_p16v_pcm_close_capture,
+	.ioctl =       snd_pcm_lib_ioctl,
+	.hw_params =   snd_p16v_pcm_hw_params_capture,
+	.hw_free =     snd_p16v_pcm_hw_free_capture,
+	.prepare =     snd_p16v_pcm_prepare_capture,
+	.trigger =     snd_p16v_pcm_trigger_capture,
+	.pointer =     snd_p16v_pcm_pointer_capture,
+};
+
+int snd_p16v_free(struct snd_emu10k1 *chip)
+{
+    // release the data
+    if (chip->p16v_buffer.area) {
+        snd_dma_free_pages(&chip->p16v_buffer);
+        //snd_printk("period lables free: %p\n", &chip->p16v_buffer);
+    }
+    return 0;
+}
+
+int __devinit snd_p16v_pcm(struct snd_emu10k1 *emu, int device, struct snd_pcm **rpcm)
+{
+    struct snd_pcm *pcm;
+    struct snd_pcm_substream *substream;
+    int err;
+    int capture=1;
+
+    //snd_printk("snd_p16v_pcm called. device=%d\n", device);
+    emu->p16v_device_offset = device;
+    if (rpcm)
+        *rpcm = NULL;
+    if ((err = snd_pcm_new(emu->card, "p16v", device, 1, capture, &pcm)) < 0)
+        return err;
+
+    pcm->private_data = emu;
+    // Single playback 8 channel device.
+    // Single capture 2 channel device.
+    snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_p16v_playback_front_ops);
+    snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_p16v_capture_ops);
+
+    pcm->info_flags = 0;
+    pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
+    strcpy(pcm->name, "p16v");
+    emu->pcm_p16v = pcm;
+
+    for(substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+        substream;
+        substream = substream->next) {
+        if ((err = snd_pcm_lib_preallocate_pages(substream,
+                                                 SNDRV_DMA_TYPE_DEV,
+                                                 snd_dma_pci_data(emu->pci),
+                                                 ((65536 - 64) * 8), ((65536 - 64) * 8))) < 0)
+            return err;
+        //snd_printk("preallocate playback substream: err=%d\n", err);
+    }
+
+    for (substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
+         substream;
+         substream = substream->next) {
+        if ((err = snd_pcm_lib_preallocate_pages(substream,
+                                                 SNDRV_DMA_TYPE_DEV,
+                                                 snd_dma_pci_data(emu->pci),
+                                                 65536 - 64, 65536 - 64)) < 0)
+            return err;
+        //snd_printk("preallocate capture substream: err=%d\n", err);
+    }
+
+    if (rpcm)
+        *rpcm = pcm;
+
+    return 0;
+}
+
+static int snd_p16v_volume_info(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_info *uinfo)
+{
+        uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+        uinfo->count = 2;
+        uinfo->value.integer.min = 0;
+        uinfo->value.integer.max = 255;
+        return 0;
+}
+
+static int snd_p16v_volume_get(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+        struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+	int high_low = (kcontrol->private_value >> 8) & 0xff;
+	int reg = kcontrol->private_value & 0xff;
+	u32 value;
+
+	value = snd_emu10k1_ptr20_read(emu, reg, high_low);
+	if (high_low) {
+		ucontrol->value.integer.value[0] = 0xff - ((value >> 24) & 0xff); /* Left */
+		ucontrol->value.integer.value[1] = 0xff - ((value >> 16) & 0xff); /* Right */
+	} else {
+		ucontrol->value.integer.value[0] = 0xff - ((value >> 8) & 0xff); /* Left */
+		ucontrol->value.integer.value[1] = 0xff - ((value >> 0) & 0xff); /* Right */
+	}
+	return 0;
+}
+
+static int snd_p16v_volume_put(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+        struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+	int high_low = (kcontrol->private_value >> 8) & 0xff;
+	int reg = kcontrol->private_value & 0xff;
+        u32 value, oval;
+
+	oval = value = snd_emu10k1_ptr20_read(emu, reg, 0);
+	if (high_low == 1) {
+		value &= 0xffff;
+		value |= ((0xff - ucontrol->value.integer.value[0]) << 24) |
+			((0xff - ucontrol->value.integer.value[1]) << 16);
+	} else {
+		value &= 0xffff0000;
+		value |= ((0xff - ucontrol->value.integer.value[0]) << 8) |
+			((0xff - ucontrol->value.integer.value[1]) );
+	}
+	if (value != oval) {
+		snd_emu10k1_ptr20_write(emu, reg, 0, value);
+		return 1;
+	}
+	return 0;
+}
+
+
+static int snd_p16v_capture_source_info(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_info *uinfo)
+{
+    static char *texts[8] = {
+        "SPDIF", "I2S", "SRC48", "SRCMulti_SPDIF", "SRCMulti_I2S",
+        "CDIF", "FX", "AC97"
+    };
+
+    uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+    uinfo->count = 1;
+    uinfo->value.enumerated.items = 8;
+    if (uinfo->value.enumerated.item > 7)
+        uinfo->value.enumerated.item = 7;
+    strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+    return 0;
+}
+
+static int snd_p16v_capture_source_get(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+    struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+
+    ucontrol->value.enumerated.item[0] = emu->p16v_capture_source;
+    return 0;
+}
+
+static int snd_p16v_capture_source_put(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+    struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+    unsigned int val;
+    int change = 0;
+    u32 mask;
+    u32 source;
+
+    val = ucontrol->value.enumerated.item[0] ;
+    change = (emu->p16v_capture_source != val);
+    if (change) {
+        emu->p16v_capture_source = val;
+        source = (val << 28) | (val << 24) | (val << 20) | (val << 16);
+        mask = snd_emu10k1_ptr20_read(emu, BASIC_INTERRUPT, 0) & 0xffff;
+        snd_emu10k1_ptr20_write(emu, BASIC_INTERRUPT, 0, source | mask);
+    }
+    return change;
+}
+
+static int snd_p16v_capture_channel_info(struct snd_kcontrol *kcontrol,
+					 struct snd_ctl_elem_info *uinfo)
+{
+    static char *texts[4] = { "0", "1", "2", "3",  };
+
+    uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+    uinfo->count = 1;
+    uinfo->value.enumerated.items = 4;
+    if (uinfo->value.enumerated.item > 3)
+        uinfo->value.enumerated.item = 3;
+    strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+    return 0;
+}
+
+static int snd_p16v_capture_channel_get(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+    struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+
+    ucontrol->value.enumerated.item[0] = emu->p16v_capture_channel;
+    return 0;
+}
+
+static int snd_p16v_capture_channel_put(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+    struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+    unsigned int val;
+    int change = 0;
+    u32 tmp;
+
+    val = ucontrol->value.enumerated.item[0] ;
+    change = (emu->p16v_capture_channel != val);
+    if (change) {
+        emu->p16v_capture_channel = val;
+        tmp = snd_emu10k1_ptr20_read(emu, CAPTURE_P16V_SOURCE, 0) & 0xfffc;
+        snd_emu10k1_ptr20_write(emu, CAPTURE_P16V_SOURCE, 0, tmp | val);
+    }
+    return change;
+}
+
+static DECLARE_TLV_DB_SCALE(snd_p16v_db_scale1, -5175, 25, 1);
+
+#define P16V_VOL(xname,xreg,xhl) { \
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+        .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |             \
+                  SNDRV_CTL_ELEM_ACCESS_TLV_READ,               \
+	.info = snd_p16v_volume_info, \
+	.get = snd_p16v_volume_get, \
+	.put = snd_p16v_volume_put, \
+	.tlv = { .p = snd_p16v_db_scale1 },	\
+	.private_value = ((xreg) | ((xhl) << 8)) \
+}
+
+static struct snd_kcontrol_new p16v_mixer_controls[] __devinitdata = {
+	P16V_VOL("HD Analog Front Playback Volume", PLAYBACK_VOLUME_MIXER9, 0),
+	P16V_VOL("HD Analog Rear Playback Volume", PLAYBACK_VOLUME_MIXER10, 1),
+	P16V_VOL("HD Analog Center/LFE Playback Volume", PLAYBACK_VOLUME_MIXER9, 1),
+	P16V_VOL("HD Analog Side Playback Volume", PLAYBACK_VOLUME_MIXER10, 0),
+	P16V_VOL("HD SPDIF Front Playback Volume", PLAYBACK_VOLUME_MIXER7, 0),
+	P16V_VOL("HD SPDIF Rear Playback Volume", PLAYBACK_VOLUME_MIXER8, 1),
+	P16V_VOL("HD SPDIF Center/LFE Playback Volume", PLAYBACK_VOLUME_MIXER7, 1),
+	P16V_VOL("HD SPDIF Side Playback Volume", PLAYBACK_VOLUME_MIXER8, 0),
+	{
+		.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name =		"HD source Capture",
+		.info =		snd_p16v_capture_source_info,
+		.get =		snd_p16v_capture_source_get,
+		.put =		snd_p16v_capture_source_put
+	},
+	{
+		.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name =		"HD channel Capture",
+		.info =		snd_p16v_capture_channel_info,
+		.get =		snd_p16v_capture_channel_get,
+		.put =		snd_p16v_capture_channel_put
+	},
+};
+
+int __devinit snd_p16v_mixer(struct snd_emu10k1 *emu)
+{
+	int i, err;
+        struct snd_card *card = emu->card;
+
+	for (i = 0; i < ARRAY_SIZE(p16v_mixer_controls); i++) {
+		if ((err = snd_ctl_add(card, snd_ctl_new1(&p16v_mixer_controls[i],
+							  emu))) < 0)
+			return err;
+	}
+        return 0;
+}
+
+#ifdef CONFIG_PM
+
+#define NUM_CHS	1	/* up to 4, but only first channel is used */
+
+int __devinit snd_p16v_alloc_pm_buffer(struct snd_emu10k1 *emu)
+{
+	emu->p16v_saved = vmalloc(NUM_CHS * 4 * 0x80);
+	if (! emu->p16v_saved)
+		return -ENOMEM;
+	return 0;
+}
+
+void snd_p16v_free_pm_buffer(struct snd_emu10k1 *emu)
+{
+	vfree(emu->p16v_saved);
+}
+
+void snd_p16v_suspend(struct snd_emu10k1 *emu)
+{
+	int i, ch;
+	unsigned int *val;
+
+	val = emu->p16v_saved;
+	for (ch = 0; ch < NUM_CHS; ch++)
+		for (i = 0; i < 0x80; i++, val++)
+			*val = snd_emu10k1_ptr20_read(emu, i, ch);
+}
+
+void snd_p16v_resume(struct snd_emu10k1 *emu)
+{
+	int i, ch;
+	unsigned int *val;
+
+	val = emu->p16v_saved;
+	for (ch = 0; ch < NUM_CHS; ch++)
+		for (i = 0; i < 0x80; i++, val++)
+			snd_emu10k1_ptr20_write(emu, i, ch, *val);
+}
+#endif
+
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/emu10k1/p16v.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/emu10k1/p16v.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/emu10k1/p16v.h	(revision 91)
@@ -0,0 +1,299 @@
+/*
+ *  Copyright (c) by James Courtier-Dutton <James@superbug.demon.co.uk>
+ *  Driver p16v chips
+ *  Version: 0.21
+ *
+ *  FEATURES currently supported:
+ *    Output fixed at S32_LE, 2 channel to hw:0,0
+ *    Rates: 44.1, 48, 96, 192.
+ *
+ *  Changelog:
+ *  0.8
+ *    Use separate card based buffer for periods table.
+ *  0.9
+ *    Use 2 channel output streams instead of 8 channel.
+ *       (8 channel output streams might be good for ASIO type output)
+ *    Corrected speaker output, so Front -> Front etc.
+ *  0.10
+ *    Fixed missed interrupts.
+ *  0.11
+ *    Add Sound card model number and names.
+ *    Add Analog volume controls.
+ *  0.12
+ *    Corrected playback interrupts. Now interrupt per period, instead of half period.
+ *  0.13
+ *    Use single trigger for multichannel.
+ *  0.14
+ *    Mic capture now works at fixed: S32_LE, 96000Hz, Stereo.
+ *  0.15
+ *    Force buffer_size / period_size == INTEGER.
+ *  0.16
+ *    Update p16v.c to work with changed alsa api.
+ *  0.17
+ *    Update p16v.c to work with changed alsa api. Removed boot_devs.
+ *  0.18
+ *    Merging with snd-emu10k1 driver.
+ *  0.19
+ *    One stereo channel at 24bit now works.
+ *  0.20
+ *    Added better register defines.
+ *  0.21
+ *    Split from p16v.c
+ *
+ *
+ *  BUGS:
+ *    Some stability problems when unloading the snd-p16v kernel module.
+ *    --
+ *
+ *  TODO:
+ *    SPDIF out.
+ *    Find out how to change capture sample rates. E.g. To record SPDIF at 48000Hz.
+ *    Currently capture fixed at 48000Hz.
+ *
+ *    --
+ *  GENERAL INFO:
+ *    Model: SB0240
+ *    P16V Chip: CA0151-DBS
+ *    Audigy 2 Chip: CA0102-IAT
+ *    AC97 Codec: STAC 9721
+ *    ADC: Philips 1361T (Stereo 24bit)
+ *    DAC: CS4382-K (8-channel, 24bit, 192Khz)
+ *
+ *  This code was initally based on code from ALSA's emu10k1x.c which is:
+ *  Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+/********************************************************************************************************/
+/* Audigy2 P16V pointer-offset register set, accessed through the PTR2 and DATA2 registers                     */
+/********************************************************************************************************/
+                                                                                                                           
+/* The sample rate of the SPDIF outputs is set by modifying a register in the EMU10K2 PTR register A_SPDIF_SAMPLERATE.
+ * The sample rate is also controlled by the same registers that control the rate of the EMU10K2 sample rate converters.
+ */
+
+/* Initally all registers from 0x00 to 0x3f have zero contents. */
+#define PLAYBACK_LIST_ADDR	0x00		/* Base DMA address of a list of pointers to each period/size */
+						/* One list entry: 4 bytes for DMA address, 
+						 * 4 bytes for period_size << 16.
+						 * One list entry is 8 bytes long.
+						 * One list entry for each period in the buffer.
+						 */
+#define PLAYBACK_LIST_SIZE	0x01		/* Size of list in bytes << 16. E.g. 8 periods -> 0x00380000  */
+#define PLAYBACK_LIST_PTR	0x02		/* Pointer to the current period being played */
+#define PLAYBACK_UNKNOWN3	0x03		/* Not used */
+#define PLAYBACK_DMA_ADDR	0x04		/* Playback DMA addresss */
+#define PLAYBACK_PERIOD_SIZE	0x05		/* Playback period size. win2000 uses 0x04000000 */
+#define PLAYBACK_POINTER	0x06		/* Playback period pointer. Used with PLAYBACK_LIST_PTR to determine buffer position currently in DAC */
+#define PLAYBACK_FIFO_END_ADDRESS	0x07		/* Playback FIFO end address */
+#define PLAYBACK_FIFO_POINTER	0x08		/* Playback FIFO pointer and number of valid sound samples in cache */
+#define PLAYBACK_UNKNOWN9	0x09		/* Not used */
+#define CAPTURE_DMA_ADDR	0x10		/* Capture DMA address */
+#define CAPTURE_BUFFER_SIZE	0x11		/* Capture buffer size */
+#define CAPTURE_POINTER		0x12		/* Capture buffer pointer. Sample currently in ADC */
+#define CAPTURE_FIFO_POINTER	0x13		/* Capture FIFO pointer and number of valid sound samples in cache */
+#define CAPTURE_P16V_VOLUME1	0x14		/* Low: Capture volume 0xXXXX3030 */
+#define CAPTURE_P16V_VOLUME2	0x15		/* High:Has no effect on capture volume */
+#define CAPTURE_P16V_SOURCE     0x16            /* P16V source select. Set to 0x0700E4E5 for AC97 CAPTURE */
+						/* [0:1] Capture input 0 channel select. 0 = Capture output 0.
+						 *                                       1 = Capture output 1.
+						 *                                       2 = Capture output 2.
+						 *                                       3 = Capture output 3.
+						 * [3:2] Capture input 1 channel select. 0 = Capture output 0.
+						 *                                       1 = Capture output 1.
+						 *                                       2 = Capture output 2.
+						 *                                       3 = Capture output 3.
+						 * [5:4] Capture input 2 channel select. 0 = Capture output 0.
+						 *                                       1 = Capture output 1.
+						 *                                       2 = Capture output 2.
+						 *                                       3 = Capture output 3.
+						 * [7:6] Capture input 3 channel select. 0 = Capture output 0.
+						 *                                       1 = Capture output 1.
+						 *                                       2 = Capture output 2.
+						 *                                       3 = Capture output 3.
+						 * [9:8] Playback input 0 channel select. 0 = Play output 0.
+						 *                                        1 = Play output 1.
+						 *                                        2 = Play output 2.
+						 *                                        3 = Play output 3.
+						 * [11:10] Playback input 1 channel select. 0 = Play output 0.
+						 *                                          1 = Play output 1.
+						 *                                          2 = Play output 2.
+						 *                                          3 = Play output 3.
+						 * [13:12] Playback input 2 channel select. 0 = Play output 0.
+						 *                                          1 = Play output 1.
+						 *                                          2 = Play output 2.
+						 *                                          3 = Play output 3.
+						 * [15:14] Playback input 3 channel select. 0 = Play output 0.
+						 *                                          1 = Play output 1.
+						 *                                          2 = Play output 2.
+						 *                                          3 = Play output 3.
+						 * [19:16] Playback mixer output enable. 1 bit per channel.
+						 * [23:20] Capture mixer output enable. 1 bit per channel.
+						 * [26:24] FX engine channel capture 0 = 0x60-0x67.
+						 *                                   1 = 0x68-0x6f.
+						 *                                   2 = 0x70-0x77.
+						 *                                   3 = 0x78-0x7f.
+						 *                                   4 = 0x80-0x87.
+						 *                                   5 = 0x88-0x8f.
+						 *                                   6 = 0x90-0x97.
+						 *                                   7 = 0x98-0x9f.
+						 * [31:27] Not used.
+						 */
+
+						/* 0x1 = capture on.
+						 * 0x100 = capture off.
+						 * 0x200 = capture off.
+						 * 0x1000 = capture off.
+						 */
+#define CAPTURE_RATE_STATUS		0x17		/* Capture sample rate. Read only */
+						/* [15:0] Not used.
+						 * [18:16] Channel 0 Detected sample rate. 0 - 44.1khz
+						 *                               1 - 48 khz
+						 *                               2 - 96 khz
+						 *                               3 - 192 khz
+						 *                               7 - undefined rate.
+						 * [19] Channel 0. 1 - Valid, 0 - Not Valid.
+						 * [22:20] Channel 1 Detected sample rate. 
+						 * [23] Channel 1. 1 - Valid, 0 - Not Valid.
+						 * [26:24] Channel 2 Detected sample rate. 
+						 * [27] Channel 2. 1 - Valid, 0 - Not Valid.
+						 * [30:28] Channel 3 Detected sample rate. 
+						 * [31] Channel 3. 1 - Valid, 0 - Not Valid.
+						 */
+/* 0x18 - 0x1f unused */
+#define PLAYBACK_LAST_SAMPLE    0x20		/* The sample currently being played. Read only */
+/* 0x21 - 0x3f unused */
+#define BASIC_INTERRUPT         0x40		/* Used by both playback and capture interrupt handler */
+						/* Playback (0x1<<channel_id) Don't touch high 16bits. */
+						/* Capture  (0x100<<channel_id). not tested */
+						/* Start Playback [3:0] (one bit per channel)
+						 * Start Capture [11:8] (one bit per channel)
+						 * Record source select for channel 0 [18:16]
+						 * Record source select for channel 1 [22:20]
+						 * Record source select for channel 2 [26:24]
+						 * Record source select for channel 3 [30:28]
+						 * 0 - SPDIF channel.
+						 * 1 - I2S channel.
+						 * 2 - SRC48 channel.
+						 * 3 - SRCMulti_SPDIF channel.
+						 * 4 - SRCMulti_I2S channel.
+						 * 5 - SPDIF channel.
+						 * 6 - fxengine capture.
+						 * 7 - AC97 capture.
+						 */
+						/* Default 41110000.
+						 * Writing 0xffffffff hangs the PC.
+						 * Writing 0xffff0000 -> 77770000 so it must be some sort of route.
+						 * bit 0x1 starts DMA playback on channel_id 0
+						 */
+/* 0x41,42 take values from 0 - 0xffffffff, but have no effect on playback */
+/* 0x43,0x48 do not remember settings */
+/* 0x41-45 unused */
+#define WATERMARK            0x46		/* Test bit to indicate cache level usage */
+						/* Values it can have while playing on channel 0. 
+						 * 0000f000, 0000f004, 0000f008, 0000f00c.
+						 * Readonly.
+						 */
+/* 0x47-0x4f unused */
+/* 0x50-0x5f Capture cache data */
+#define SRCSel			0x60            /* SRCSel. Default 0x4. Bypass P16V 0x14 */
+						/* [0] 0 = 10K2 audio, 1 = SRC48 mixer output.
+						 * [2] 0 = 10K2 audio, 1 = SRCMulti SPDIF mixer output.
+						 * [4] 0 = 10K2 audio, 1 = SRCMulti I2S mixer output.
+						 */
+						/* SRC48 converts samples rates 44.1, 48, 96, 192 to 48 khz. */
+						/* SRCMulti converts 48khz samples rates to 44.1, 48, 96, 192 to 48. */
+						/* SRC48 and SRCMULTI sample rate select and output select. */
+						/* 0xffffffff -> 0xC0000015
+						 * 0xXXXXXXX4 = Enable Front Left/Right
+						 * Enable PCMs
+						 */
+
+/* 0x61 -> 0x6c are Volume controls */
+#define PLAYBACK_VOLUME_MIXER1  0x61		/* SRC48 Low to mixer input volume control. */
+#define PLAYBACK_VOLUME_MIXER2  0x62		/* SRC48 High to mixer input volume control. */
+#define PLAYBACK_VOLUME_MIXER3  0x63		/* SRCMULTI SPDIF Low to mixer input volume control. */
+#define PLAYBACK_VOLUME_MIXER4  0x64		/* SRCMULTI SPDIF High to mixer input volume control. */
+#define PLAYBACK_VOLUME_MIXER5  0x65		/* SRCMULTI I2S Low to mixer input volume control. */
+#define PLAYBACK_VOLUME_MIXER6  0x66		/* SRCMULTI I2S High to mixer input volume control. */
+#define PLAYBACK_VOLUME_MIXER7  0x67		/* P16V Low to SRCMULTI SPDIF mixer input volume control. */
+#define PLAYBACK_VOLUME_MIXER8  0x68		/* P16V High to SRCMULTI SPDIF mixer input volume control. */
+#define PLAYBACK_VOLUME_MIXER9  0x69		/* P16V Low to SRCMULTI I2S mixer input volume control. */
+						/* 0xXXXX3030 = PCM0 Volume (Front).
+						 * 0x3030XXXX = PCM1 Volume (Center)
+						 */
+#define PLAYBACK_VOLUME_MIXER10  0x6a		/* P16V High to SRCMULTI I2S mixer input volume control. */
+						/* 0x3030XXXX = PCM3 Volume (Rear). */
+#define PLAYBACK_VOLUME_MIXER11  0x6b		/* E10K2 Low to SRC48 mixer input volume control. */
+#define PLAYBACK_VOLUME_MIXER12 0x6c		/* E10K2 High to SRC48 mixer input volume control. */
+
+#define SRC48_ENABLE            0x6d            /* SRC48 input audio enable */
+						/* SRC48 converts samples rates 44.1, 48, 96, 192 to 48 khz. */
+						/* [23:16] The corresponding P16V channel to SRC48 enabled if == 1.
+						 * [31:24] The corresponding E10K2 channel to SRC48 enabled.
+						 */
+#define SRCMULTI_ENABLE         0x6e            /* SRCMulti input audio enable. Default 0xffffffff */
+						/* SRCMulti converts 48khz samples rates to 44.1, 48, 96, 192 to 48. */
+						/* [7:0] The corresponding P16V channel to SRCMulti_I2S enabled if == 1.
+						 * [15:8] The corresponding E10K2 channel to SRCMulti I2S enabled.
+						 * [23:16] The corresponding P16V channel to SRCMulti SPDIF enabled.
+						 * [31:24] The corresponding E10K2 channel to SRCMulti SPDIF enabled.
+						 */
+						/* Bypass P16V 0xff00ff00 
+						 * Bitmap. 0 = Off, 1 = On.
+						 * P16V playback outputs:
+						 * 0xXXXXXXX1 = PCM0 Left. (Front)
+						 * 0xXXXXXXX2 = PCM0 Right.
+						 * 0xXXXXXXX4 = PCM1 Left. (Center/LFE)
+						 * 0xXXXXXXX8 = PCM1 Right.
+						 * 0xXXXXXX1X = PCM2 Left. (Unknown)
+						 * 0xXXXXXX2X = PCM2 Right.
+						 * 0xXXXXXX4X = PCM3 Left. (Rear)
+						 * 0xXXXXXX8X = PCM3 Right.
+						 */
+#define AUDIO_OUT_ENABLE        0x6f            /* Default: 000100FF */
+						/* [3:0] Does something, but not documented. Probably capture enable.
+						 * [7:4] Playback channels enable. not documented.
+						 * [16] AC97 output enable if == 1
+						 * [30] 0 = SRCMulti_I2S input from fxengine 0x68-0x6f.
+						 *      1 = SRCMulti_I2S input from SRC48 output.
+						 * [31] 0 = SRCMulti_SPDIF input from fxengine 0x60-0x67.
+						 *      1 = SRCMulti_SPDIF input from SRC48 output.
+						 */
+						/* 0xffffffff -> C00100FF */
+						/* 0 -> Not playback sound, irq still running */
+						/* 0xXXXXXX10 = PCM0 Left/Right On. (Front)
+						 * 0xXXXXXX20 = PCM1 Left/Right On. (Center/LFE)
+						 * 0xXXXXXX40 = PCM2 Left/Right On. (Unknown)
+						 * 0xXXXXXX80 = PCM3 Left/Right On. (Rear)
+						 */
+#define PLAYBACK_SPDIF_SELECT     0x70          /* Default: 12030F00 */
+						/* 0xffffffff -> 3FF30FFF */
+						/* 0x00000001 pauses stream/irq fail. */
+						/* All other bits do not effect playback */
+#define PLAYBACK_SPDIF_SRC_SELECT 0x71          /* Default: 0000E4E4 */
+						/* 0xffffffff -> F33FFFFF */
+						/* All bits do not effect playback */
+#define PLAYBACK_SPDIF_USER_DATA0 0x72		/* SPDIF out user data 0 */
+#define PLAYBACK_SPDIF_USER_DATA1 0x73		/* SPDIF out user data 1 */
+/* 0x74-0x75 unknown */
+#define CAPTURE_SPDIF_CONTROL	0x76		/* SPDIF in control setting */
+#define CAPTURE_SPDIF_STATUS	0x77		/* SPDIF in status */
+#define CAPURE_SPDIF_USER_DATA0 0x78		/* SPDIF in user data 0 */
+#define CAPURE_SPDIF_USER_DATA1 0x79		/* SPDIF in user data 1 */
+#define CAPURE_SPDIF_USER_DATA2 0x7a		/* SPDIF in user data 2 */
+
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/emu10k1/timer.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/emu10k1/timer.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/emu10k1/timer.c	(revision 91)
@@ -0,0 +1,99 @@
+/*
+ *  Copyright (c) by Lee Revell <rlrevell@joe-job.com>
+ *
+ *  Routines for control of EMU10K1 chips
+ *
+ *  Copied from similar code by Clemens Ladisch in the ymfpci driver
+ *
+ *  BUGS:
+ *    --
+ *
+ *  TODO:
+ *    --
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/emu10k1.h>
+
+static int snd_emu10k1_timer_start(snd_timer_t *timer)
+{
+	struct snd_emu10k1 *emu;
+	unsigned long flags;
+	unsigned int delay;
+
+	emu = snd_timer_chip(timer);
+	delay = timer->sticks - 1;
+	if (delay < 5 ) /* minimum time is 5 ticks */
+		delay = 5;
+	spin_lock_irqsave(&emu->reg_lock, flags);
+	snd_emu10k1_intr_enable(emu, INTE_INTERVALTIMERENB);
+	outw(delay & TIMER_RATE_MASK, emu->port + TIMER);
+	spin_unlock_irqrestore(&emu->reg_lock, flags);
+	return 0;
+}
+
+static int snd_emu10k1_timer_stop(snd_timer_t *timer)
+{
+	struct snd_emu10k1 *emu;
+	unsigned long flags;
+
+	emu = snd_timer_chip(timer);
+	spin_lock_irqsave(&emu->reg_lock, flags);
+	snd_emu10k1_intr_disable(emu, INTE_INTERVALTIMERENB);
+	spin_unlock_irqrestore(&emu->reg_lock, flags);
+	return 0;
+}
+
+static int snd_emu10k1_timer_precise_resolution(snd_timer_t *timer,
+					       unsigned long *num, unsigned long *den)
+{
+	*num = 1;
+	*den = 48000;
+	return 0;
+}
+
+static struct snd_timer_hardware snd_emu10k1_timer_hw = {
+    .flags = SNDRV_TIMER_HW_AUTO,
+    .resolution = 20833, /* 1 sample @ 48KHZ = 20.833...us */
+    .ticks = 1024,
+    .start = snd_emu10k1_timer_start,
+    .stop = snd_emu10k1_timer_stop,
+    .precise_resolution = snd_emu10k1_timer_precise_resolution,
+};
+
+int __devinit snd_emu10k1_timer(struct snd_emu10k1 *emu, int device)
+{
+	snd_timer_t *timer = NULL;
+	snd_timer_id_t tid;
+	int err;
+
+	tid.dev_class = SNDRV_TIMER_CLASS_CARD;
+	tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE;
+	tid.card = emu->card->number;
+	tid.device = device;
+	tid.subdevice = 0;
+	if ((err = snd_timer_new(emu->card, "EMU10K1", &tid, &timer)) >= 0) {
+		strcpy(timer->name, "EMU10K1 timer");
+		timer->private_data = emu;
+		timer->hw = snd_emu10k1_timer_hw;
+	}
+	emu->timer = timer;
+	return err;
+}
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/emu10k1/voice.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/emu10k1/voice.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/emu10k1/voice.c	(revision 91)
@@ -0,0 +1,153 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *                   Creative Labs, Inc.
+ *                   Lee Revell <rlrevell@joe-job.com>
+ *  Routines for control of EMU10K1 chips - voice manager
+ *
+ *  Rewrote voice allocator for multichannel support - rlrevell 12/2004
+ *
+ *  BUGS:
+ *    --
+ *
+ *  TODO:
+ *    --
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#define __NO_VERSION__
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/emu10k1.h>
+
+/* Previously the voice allocator started at 0 every time.  The new voice
+ * allocator uses a round robin scheme.  The next free voice is tracked in
+ * the card record and each allocation begins where the last left off.  The
+ * hardware requires stereo interleaved voices be aligned to an even/odd
+ * boundary.  For multichannel voice allocation we ensure than the block of
+ * voices does not cross the 32 voice boundary.  This simplifies the
+ * multichannel support and ensures we can use a single write to the
+ * (set|clear)_loop_stop registers.  Otherwise (for example) the voices would
+ * get out of sync when pausing/resuming a stream.
+ *                                                      --rlrevell
+ */
+
+static int voice_alloc(struct snd_emu10k1 *emu, int type, int number,
+                       struct snd_emu10k1_voice **rvoice)
+{
+    struct snd_emu10k1_voice *voice;
+    int i, j, k, first_voice, last_voice, skip;
+
+    *rvoice = NULL;
+    first_voice = last_voice = 0;
+    for (i = emu->next_free_voice, j = 0; j < NUM_G ; i += number, j += number) {
+        // printk("i %d j %d next free %d!\n", i, j, emu->next_free_voice);
+        i %= NUM_G;
+        /* stereo voices must be even/odd */
+        if ((number == 2) && (i % 2)) {
+            i++;
+            continue;
+        }
+
+        skip = 0;
+        for (k = 0; k < number; k++) {
+            voice = &emu->voices[(i+k) % NUM_G];
+            if (voice->use) {
+                skip = 1;
+                break;
+            }
+        }
+        if (!skip) {
+            // printk("allocated voice %d\n", i);
+            first_voice = i;
+            last_voice = (i + number) % NUM_G;
+            emu->next_free_voice = last_voice;
+            break;
+        }
+    }
+
+    if (first_voice == last_voice)
+        return -ENOMEM;
+
+    for (i=0; i < number; i++) {
+        voice = &emu->voices[(first_voice + i) % NUM_G];
+        // printk("voice alloc - %i, %i of %i\n", voice->number, idx-first_voice+1, number);
+        voice->use = 1;
+        switch (type) {
+        case EMU10K1_PCM:
+            voice->pcm = 1;
+            break;
+        case EMU10K1_SYNTH:
+            voice->synth = 1;
+            break;
+        case EMU10K1_MIDI:
+            voice->midi = 1;
+            break;
+        case EMU10K1_EFX:
+            voice->efx = 1;
+            break;
+        }
+    }
+    *rvoice = &emu->voices[first_voice];
+    return 0;
+}
+
+int snd_emu10k1_voice_alloc(struct snd_emu10k1 *emu, int type, int number, struct snd_emu10k1_voice **rvoice)
+{
+    unsigned long flags;
+    int result;
+
+    snd_assert(rvoice != NULL, return -EINVAL);
+    snd_assert(number, return -EINVAL);
+
+    spin_lock_irqsave(&emu->voice_lock, flags);
+    for (;;) {
+        result = voice_alloc(emu, type, number, rvoice);
+        if (result == 0 || type == EMU10K1_SYNTH || type == EMU10K1_MIDI)
+            break;
+
+        /* free a voice from synth */
+        if (emu->get_synth_voice) {
+            result = emu->get_synth_voice(emu);
+            if (result >= 0) {
+                struct snd_emu10k1_voice *pvoice = &emu->voices[result];
+                pvoice->interrupt = NULL;
+                pvoice->use = pvoice->pcm = pvoice->synth = pvoice->midi = pvoice->efx = 0;
+                pvoice->epcm = NULL;
+            }
+        }
+        if (result < 0)
+            break;
+    }
+    spin_unlock_irqrestore(&emu->voice_lock, flags);
+
+    return result;
+}
+
+int snd_emu10k1_voice_free(struct snd_emu10k1 *emu, struct snd_emu10k1_voice *pvoice)
+{
+    unsigned long flags;
+
+    snd_assert(pvoice != NULL, return -EINVAL);
+    spin_lock_irqsave(&emu->voice_lock, flags);
+    pvoice->interrupt = NULL;
+    pvoice->use = pvoice->pcm = pvoice->synth = pvoice->midi = pvoice->efx = 0;
+    pvoice->epcm = NULL;
+    snd_emu10k1_voice_init(emu, pvoice->number);
+    spin_unlock_irqrestore(&emu->voice_lock, flags);
+    return 0;
+}
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/ens1370.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/ens1370.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/ens1370.c	(revision 91)
@@ -0,0 +1,2544 @@
+/*
+ *  Driver for Ensoniq ES1370/ES1371 AudioPCI soundcard
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>,
+ *		     Thomas Sailer <sailer@ife.ee.ethz.ch>
+ *
+ *   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
+ *
+ */
+
+/* Power-Management-Code ( CONFIG_PM )
+ * for ens1371 only ( FIXME )
+ * derived from cs4281.c, atiixp.c and via82xx.c
+ * using http://www.alsa-project.org/~iwai/writing-an-alsa-driver/c1540.htm
+ * by Kurt J. Bosch
+ */
+
+#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 <linux/gameport.h>
+#include <linux/moduleparam.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/rawmidi.h>
+#ifdef CHIP1371
+#include <sound/ac97_codec.h>
+#else
+#include <sound/ak4531_codec.h>
+#endif
+#include <sound/initval.h>
+#include <sound/asoundef.h>
+
+#ifndef PCI_DEVICE_ID_ENSONIQ_CT5880
+#define PCI_DEVICE_ID_ENSONIQ_CT5880    0x5880
+#endif
+
+#ifndef CHIP1371
+#undef CHIP1370
+#define CHIP1370
+#endif
+
+#ifdef CHIP1370
+#define DRIVER_NAME "ENS1370"
+#else
+#define DRIVER_NAME "ENS1371"
+#endif
+
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>, Thomas Sailer <sailer@ife.ee.ethz.ch>");
+MODULE_LICENSE("GPL");
+#ifdef CHIP1370
+MODULE_DESCRIPTION("Ensoniq AudioPCI ES1370");
+MODULE_SUPPORTED_DEVICE("{{Ensoniq,AudioPCI-97 ES1370},"
+	        "{Creative Labs,SB PCI64/128 (ES1370)}}");
+#endif
+#ifdef CHIP1371
+MODULE_DESCRIPTION("Ensoniq/Creative AudioPCI ES1371+");
+MODULE_SUPPORTED_DEVICE("{{Ensoniq,AudioPCI ES1371/73},"
+		"{Ensoniq,AudioPCI ES1373},"
+		"{Creative Labs,Ectiva EV1938},"
+		"{Creative Labs,SB PCI64/128 (ES1371/73)},"
+		"{Creative Labs,Vibra PCI128},"
+		"{Ectiva,EV1938}}");
+#endif
+
+#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
+#define SUPPORT_JOYSTICK
+#endif
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;	/* Enable switches */
+#ifdef SUPPORT_JOYSTICK
+#ifdef CHIP1371
+static int joystick_port[SNDRV_CARDS];
+#else
+static int joystick[SNDRV_CARDS];
+#endif
+#endif
+#ifdef CHIP1371
+static int spdif[SNDRV_CARDS];
+static int lineio[SNDRV_CARDS];
+#endif
+
+//module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for Ensoniq AudioPCI soundcard.");
+//module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for Ensoniq AudioPCI soundcard.");
+//module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable Ensoniq AudioPCI soundcard.");
+#ifdef SUPPORT_JOYSTICK
+#ifdef CHIP1371
+//module_param_array(joystick_port, int, NULL, 0444);
+MODULE_PARM_DESC(joystick_port, "Joystick port address.");
+#else
+//module_param_array(joystick, bool, NULL, 0444);
+MODULE_PARM_DESC(joystick, "Enable joystick.");
+#endif
+#endif /* SUPPORT_JOYSTICK */
+#ifdef CHIP1371
+//module_param_array(spdif, int, NULL, 0444);
+MODULE_PARM_DESC(spdif, "S/PDIF output (-1 = none, 0 = auto, 1 = force).");
+//module_param_array(lineio, int, NULL, 0444);
+MODULE_PARM_DESC(lineio, "Line In to Rear Out (0 = auto, 1 = force).");
+#endif
+
+/* ES1371 chip ID */
+/* This is a little confusing because all ES1371 compatible chips have the
+   same DEVICE_ID, the only thing differentiating them is the REV_ID field.
+   This is only significant if you want to enable features on the later parts.
+   Yes, I know it's stupid and why didn't we use the sub IDs?
+*/
+#define ES1371REV_ES1373_A  0x04
+#define ES1371REV_ES1373_B  0x06
+#define ES1371REV_CT5880_A  0x07
+#define CT5880REV_CT5880_C  0x02
+#define CT5880REV_CT5880_D  0x03	/* ??? -jk */
+#define CT5880REV_CT5880_E  0x04	/* mw */
+#define ES1371REV_ES1371_B  0x09
+#define EV1938REV_EV1938_A  0x00
+#define ES1371REV_ES1373_8  0x08
+
+/*
+ * Direct registers
+ */
+
+#define ES_REG(ensoniq, x) ((ensoniq)->port + ES_REG_##x)
+
+#define ES_REG_CONTROL	0x00	/* R/W: Interrupt/Chip select control register */
+#define   ES_1370_ADC_STOP	(1<<31)		/* disable capture buffer transfers */
+#define   ES_1370_XCTL1 	(1<<30)		/* general purpose output bit */
+#define   ES_1373_BYPASS_P1	(1<<31)		/* bypass SRC for PB1 */
+#define   ES_1373_BYPASS_P2	(1<<30)		/* bypass SRC for PB2 */
+#define   ES_1373_BYPASS_R	(1<<29)		/* bypass SRC for REC */
+#define   ES_1373_TEST_BIT	(1<<28)		/* should be set to 0 for normal operation */
+#define   ES_1373_RECEN_B	(1<<27)		/* mix record with playback for I2S/SPDIF out */
+#define   ES_1373_SPDIF_THRU	(1<<26)		/* 0 = SPDIF thru mode, 1 = SPDIF == dig out */
+#define   ES_1371_JOY_ASEL(o)	(((o)&0x03)<<24)/* joystick port mapping */
+#define   ES_1371_JOY_ASELM	(0x03<<24)	/* mask for above */
+#define   ES_1371_JOY_ASELI(i)  (((i)>>24)&0x03)
+#define   ES_1371_GPIO_IN(i)	(((i)>>20)&0x0f)/* GPIO in [3:0] pins - R/O */
+#define   ES_1370_PCLKDIVO(o)	(((o)&0x1fff)<<16)/* clock divide ratio for DAC2 */
+#define   ES_1370_PCLKDIVM	((0x1fff)<<16)	/* mask for above */
+#define   ES_1370_PCLKDIVI(i)	(((i)>>16)&0x1fff)/* clock divide ratio for DAC2 */
+#define   ES_1371_GPIO_OUT(o)	(((o)&0x0f)<<16)/* GPIO out [3:0] pins - W/R */
+#define   ES_1371_GPIO_OUTM     (0x0f<<16)	/* mask for above */
+#define   ES_MSFMTSEL		(1<<15)		/* MPEG serial data format; 0 = SONY, 1 = I2S */
+#define   ES_1370_M_SBB		(1<<14)		/* clock source for DAC - 0 = clock generator; 1 = MPEG clocks */
+#define   ES_1371_SYNC_RES	(1<<14)		/* Warm AC97 reset */
+#define   ES_1370_WTSRSEL(o)	(((o)&0x03)<<12)/* fixed frequency clock for DAC1 */
+#define   ES_1370_WTSRSELM	(0x03<<12)	/* mask for above */
+#define   ES_1371_ADC_STOP	(1<<13)		/* disable CCB transfer capture information */
+#define   ES_1371_PWR_INTRM	(1<<12)		/* power level change interrupts enable */
+#define   ES_1370_DAC_SYNC	(1<<11)		/* DAC's are synchronous */
+#define   ES_1371_M_CB		(1<<11)		/* capture clock source; 0 = AC'97 ADC; 1 = I2S */
+#define   ES_CCB_INTRM		(1<<10)		/* CCB voice interrupts enable */
+#define   ES_1370_M_CB		(1<<9)		/* capture clock source; 0 = ADC; 1 = MPEG */
+#define   ES_1370_XCTL0		(1<<8)		/* generap purpose output bit */
+#define   ES_1371_PDLEV(o)	(((o)&0x03)<<8)	/* current power down level */
+#define   ES_1371_PDLEVM	(0x03<<8)	/* mask for above */
+#define   ES_BREQ		(1<<7)		/* memory bus request enable */
+#define   ES_DAC1_EN		(1<<6)		/* DAC1 playback channel enable */
+#define   ES_DAC2_EN		(1<<5)		/* DAC2 playback channel enable */
+#define   ES_ADC_EN		(1<<4)		/* ADC capture channel enable */
+#define   ES_UART_EN		(1<<3)		/* UART enable */
+#define   ES_JYSTK_EN		(1<<2)		/* Joystick module enable */
+#define   ES_1370_CDC_EN	(1<<1)		/* Codec interface enable */
+#define   ES_1371_XTALCKDIS	(1<<1)		/* Xtal clock disable */
+#define   ES_1370_SERR_DISABLE	(1<<0)		/* PCI serr signal disable */
+#define   ES_1371_PCICLKDIS     (1<<0)		/* PCI clock disable */
+#define ES_REG_STATUS	0x04	/* R/O: Interrupt/Chip select status register */
+#define   ES_INTR               (1<<31)		/* Interrupt is pending */
+#define   ES_1371_ST_AC97_RST	(1<<29)		/* CT5880 AC'97 Reset bit */
+#define   ES_1373_REAR_BIT27	(1<<27)		/* rear bits: 000 - front, 010 - mirror, 101 - separate */
+#define   ES_1373_REAR_BIT26	(1<<26)
+#define   ES_1373_REAR_BIT24	(1<<24)
+#define   ES_1373_GPIO_INT_EN(o)(((o)&0x0f)<<20)/* GPIO [3:0] pins - interrupt enable */
+#define   ES_1373_SPDIF_EN	(1<<18)		/* SPDIF enable */
+#define   ES_1373_SPDIF_TEST	(1<<17)		/* SPDIF test */
+#define   ES_1371_TEST          (1<<16)		/* test ASIC */
+#define   ES_1373_GPIO_INT(i)	(((i)&0x0f)>>12)/* GPIO [3:0] pins - interrupt pending */
+#define   ES_1370_CSTAT		(1<<10)		/* CODEC is busy or register write in progress */
+#define   ES_1370_CBUSY         (1<<9)		/* CODEC is busy */
+#define   ES_1370_CWRIP		(1<<8)		/* CODEC register write in progress */
+#define   ES_1371_SYNC_ERR	(1<<8)		/* CODEC synchronization error occurred */
+#define   ES_1371_VC(i)         (((i)>>6)&0x03)	/* voice code from CCB module */
+#define   ES_1370_VC(i)		(((i)>>5)&0x03)	/* voice code from CCB module */
+#define   ES_1371_MPWR          (1<<5)		/* power level interrupt pending */
+#define   ES_MCCB		(1<<4)		/* CCB interrupt pending */
+#define   ES_UART		(1<<3)		/* UART interrupt pending */
+#define   ES_DAC1		(1<<2)		/* DAC1 channel interrupt pending */
+#define   ES_DAC2		(1<<1)		/* DAC2 channel interrupt pending */
+#define   ES_ADC		(1<<0)		/* ADC channel interrupt pending */
+#define ES_REG_UART_DATA 0x08	/* R/W: UART data register */
+#define ES_REG_UART_STATUS 0x09	/* R/O: UART status register */
+#define   ES_RXINT		(1<<7)		/* RX interrupt occurred */
+#define   ES_TXINT		(1<<2)		/* TX interrupt occurred */
+#define   ES_TXRDY		(1<<1)		/* transmitter ready */
+#define   ES_RXRDY		(1<<0)		/* receiver ready */
+#define ES_REG_UART_CONTROL 0x09	/* W/O: UART control register */
+#define   ES_RXINTEN		(1<<7)		/* RX interrupt enable */
+#define   ES_TXINTENO(o)	(((o)&0x03)<<5)	/* TX interrupt enable */
+#define   ES_TXINTENM		(0x03<<5)	/* mask for above */
+#define   ES_TXINTENI(i)	(((i)>>5)&0x03)
+#define   ES_CNTRL(o)		(((o)&0x03)<<0)	/* control */
+#define   ES_CNTRLM		(0x03<<0)	/* mask for above */
+#define ES_REG_UART_RES	0x0a	/* R/W: UART reserver register */
+#define   ES_TEST_MODE		(1<<0)		/* test mode enabled */
+#define ES_REG_MEM_PAGE	0x0c	/* R/W: Memory page register */
+#define   ES_MEM_PAGEO(o)	(((o)&0x0f)<<0)	/* memory page select - out */
+#define   ES_MEM_PAGEM		(0x0f<<0)	/* mask for above */
+#define   ES_MEM_PAGEI(i)	(((i)>>0)&0x0f) /* memory page select - in */
+#define ES_REG_1370_CODEC 0x10	/* W/O: Codec write register address */
+#define   ES_1370_CODEC_WRITE(a,d) ((((a)&0xff)<<8)|(((d)&0xff)<<0))
+#define ES_REG_1371_CODEC 0x14	/* W/R: Codec Read/Write register address */
+#define   ES_1371_CODEC_RDY	   (1<<31)	/* codec ready */
+#define   ES_1371_CODEC_WIP	   (1<<30)	/* codec register access in progress */
+#define   ES_1371_CODEC_PIRD	   (1<<23)	/* codec read/write select register */
+#define   ES_1371_CODEC_WRITE(a,d) ((((a)&0x7f)<<16)|(((d)&0xffff)<<0))
+#define   ES_1371_CODEC_READS(a)   ((((a)&0x7f)<<16)|ES_1371_CODEC_PIRD)
+#define   ES_1371_CODEC_READ(i)    (((i)>>0)&0xffff)
+
+#define ES_REG_1371_SMPRATE 0x10	/* W/R: Codec rate converter interface register */
+#define   ES_1371_SRC_RAM_ADDRO(o) (((o)&0x7f)<<25)/* address of the sample rate converter */
+#define   ES_1371_SRC_RAM_ADDRM	   (0x7f<<25)	/* mask for above */
+#define   ES_1371_SRC_RAM_ADDRI(i) (((i)>>25)&0x7f)/* address of the sample rate converter */
+#define   ES_1371_SRC_RAM_WE	   (1<<24)	/* R/W: read/write control for sample rate converter */
+#define   ES_1371_SRC_RAM_BUSY     (1<<23)	/* R/O: sample rate memory is busy */
+#define   ES_1371_SRC_DISABLE      (1<<22)	/* sample rate converter disable */
+#define   ES_1371_DIS_P1	   (1<<21)	/* playback channel 1 accumulator update disable */
+#define   ES_1371_DIS_P2	   (1<<20)	/* playback channel 1 accumulator update disable */
+#define   ES_1371_DIS_R1	   (1<<19)	/* capture channel accumulator update disable */
+#define   ES_1371_SRC_RAM_DATAO(o) (((o)&0xffff)<<0)/* current value of the sample rate converter */
+#define   ES_1371_SRC_RAM_DATAM	   (0xffff<<0)	/* mask for above */
+#define   ES_1371_SRC_RAM_DATAI(i) (((i)>>0)&0xffff)/* current value of the sample rate converter */
+
+#define ES_REG_1371_LEGACY 0x18	/* W/R: Legacy control/status register */
+#define   ES_1371_JFAST		(1<<31)		/* fast joystick timing */
+#define   ES_1371_HIB		(1<<30)		/* host interrupt blocking enable */
+#define   ES_1371_VSB		(1<<29)		/* SB; 0 = addr 0x220xH, 1 = 0x22FxH */
+#define   ES_1371_VMPUO(o)	(((o)&0x03)<<27)/* base register address; 0 = 0x320xH; 1 = 0x330xH; 2 = 0x340xH; 3 = 0x350xH */
+#define   ES_1371_VMPUM		(0x03<<27)	/* mask for above */
+#define   ES_1371_VMPUI(i)	(((i)>>27)&0x03)/* base register address */
+#define   ES_1371_VCDCO(o)	(((o)&0x03)<<25)/* CODEC; 0 = 0x530xH; 1 = undefined; 2 = 0xe80xH; 3 = 0xF40xH */
+#define   ES_1371_VCDCM		(0x03<<25)	/* mask for above */
+#define   ES_1371_VCDCI(i)	(((i)>>25)&0x03)/* CODEC address */
+#define   ES_1371_FIRQ		(1<<24)		/* force an interrupt */
+#define   ES_1371_SDMACAP	(1<<23)		/* enable event capture for slave DMA controller */
+#define   ES_1371_SPICAP	(1<<22)		/* enable event capture for slave IRQ controller */
+#define   ES_1371_MDMACAP	(1<<21)		/* enable event capture for master DMA controller */
+#define   ES_1371_MPICAP	(1<<20)		/* enable event capture for master IRQ controller */
+#define   ES_1371_ADCAP		(1<<19)		/* enable event capture for ADLIB register; 0x388xH */
+#define   ES_1371_SVCAP		(1<<18)		/* enable event capture for SB registers */
+#define   ES_1371_CDCCAP	(1<<17)		/* enable event capture for CODEC registers */
+#define   ES_1371_BACAP		(1<<16)		/* enable event capture for SoundScape base address */
+#define   ES_1371_EXI(i)	(((i)>>8)&0x07)	/* event number */
+#define   ES_1371_AI(i)		(((i)>>3)&0x1f)	/* event significant I/O address */
+#define   ES_1371_WR		(1<<2)	/* event capture; 0 = read; 1 = write */
+#define   ES_1371_LEGINT	(1<<0)	/* interrupt for legacy events; 0 = interrupt did occur */
+
+#define ES_REG_CHANNEL_STATUS 0x1c /* R/W: first 32-bits from S/PDIF channel status block, es1373 */
+
+#define ES_REG_SERIAL	0x20	/* R/W: Serial interface control register */
+#define   ES_1371_DAC_TEST	(1<<22)		/* DAC test mode enable */
+#define   ES_P2_END_INCO(o)	(((o)&0x07)<<19)/* binary offset value to increment / loop end */
+#define   ES_P2_END_INCM	(0x07<<19)	/* mask for above */
+#define   ES_P2_END_INCI(i)	(((i)>>16)&0x07)/* binary offset value to increment / loop end */
+#define   ES_P2_ST_INCO(o)	(((o)&0x07)<<16)/* binary offset value to increment / start */
+#define   ES_P2_ST_INCM		(0x07<<16)	/* mask for above */
+#define   ES_P2_ST_INCI(i)	(((i)<<16)&0x07)/* binary offset value to increment / start */
+#define   ES_R1_LOOP_SEL	(1<<15)		/* ADC; 0 - loop mode; 1 = stop mode */
+#define   ES_P2_LOOP_SEL	(1<<14)		/* DAC2; 0 - loop mode; 1 = stop mode */
+#define   ES_P1_LOOP_SEL	(1<<13)		/* DAC1; 0 - loop mode; 1 = stop mode */
+#define   ES_P2_PAUSE		(1<<12)		/* DAC2; 0 - play mode; 1 = pause mode */
+#define   ES_P1_PAUSE		(1<<11)		/* DAC1; 0 - play mode; 1 = pause mode */
+#define   ES_R1_INT_EN		(1<<10)		/* ADC interrupt enable */
+#define   ES_P2_INT_EN		(1<<9)		/* DAC2 interrupt enable */
+#define   ES_P1_INT_EN		(1<<8)		/* DAC1 interrupt enable */
+#define   ES_P1_SCT_RLD		(1<<7)		/* force sample counter reload for DAC1 */
+#define   ES_P2_DAC_SEN		(1<<6)		/* when stop mode: 0 - DAC2 play back zeros; 1 = DAC2 play back last sample */
+#define   ES_R1_MODEO(o)	(((o)&0x03)<<4)	/* ADC mode; 0 = 8-bit mono; 1 = 8-bit stereo; 2 = 16-bit mono; 3 = 16-bit stereo */
+#define   ES_R1_MODEM		(0x03<<4)	/* mask for above */
+#define   ES_R1_MODEI(i)	(((i)>>4)&0x03)
+#define   ES_P2_MODEO(o)	(((o)&0x03)<<2)	/* DAC2 mode; -- '' -- */
+#define   ES_P2_MODEM		(0x03<<2)	/* mask for above */
+#define   ES_P2_MODEI(i)	(((i)>>2)&0x03)
+#define   ES_P1_MODEO(o)	(((o)&0x03)<<0)	/* DAC1 mode; -- '' -- */
+#define   ES_P1_MODEM		(0x03<<0)	/* mask for above */
+#define   ES_P1_MODEI(i)	(((i)>>0)&0x03)
+
+#define ES_REG_DAC1_COUNT 0x24	/* R/W: DAC1 sample count register */
+#define ES_REG_DAC2_COUNT 0x28	/* R/W: DAC2 sample count register */
+#define ES_REG_ADC_COUNT  0x2c	/* R/W: ADC sample count register */
+#define   ES_REG_CURR_COUNT(i)  (((i)>>16)&0xffff)
+#define   ES_REG_COUNTO(o)	(((o)&0xffff)<<0)
+#define   ES_REG_COUNTM		(0xffff<<0)
+#define   ES_REG_COUNTI(i)	(((i)>>0)&0xffff)
+
+#define ES_REG_DAC1_FRAME 0x30	/* R/W: PAGE 0x0c; DAC1 frame address */
+#define ES_REG_DAC1_SIZE  0x34	/* R/W: PAGE 0x0c; DAC1 frame size */
+#define ES_REG_DAC2_FRAME 0x38	/* R/W: PAGE 0x0c; DAC2 frame address */
+#define ES_REG_DAC2_SIZE  0x3c	/* R/W: PAGE 0x0c; DAC2 frame size */
+#define ES_REG_ADC_FRAME  0x30	/* R/W: PAGE 0x0d; ADC frame address */
+#define ES_REG_ADC_SIZE	  0x34	/* R/W: PAGE 0x0d; ADC frame size */
+#define   ES_REG_FCURR_COUNTO(o) (((o)&0xffff)<<16)
+#define   ES_REG_FCURR_COUNTM    (0xffff<<16)
+#define   ES_REG_FCURR_COUNTI(i) (((i)>>14)&0x3fffc)
+#define   ES_REG_FSIZEO(o)	 (((o)&0xffff)<<0)
+#define   ES_REG_FSIZEM		 (0xffff<<0)
+#define   ES_REG_FSIZEI(i)	 (((i)>>0)&0xffff)
+#define ES_REG_PHANTOM_FRAME 0x38 /* R/W: PAGE 0x0d: phantom frame address */
+#define ES_REG_PHANTOM_COUNT 0x3c /* R/W: PAGE 0x0d: phantom frame count */
+
+#define ES_REG_UART_FIFO  0x30	/* R/W: PAGE 0x0e; UART FIFO register */
+#define   ES_REG_UF_VALID	 (1<<8)
+#define   ES_REG_UF_BYTEO(o)	 (((o)&0xff)<<0)
+#define   ES_REG_UF_BYTEM	 (0xff<<0)
+#define   ES_REG_UF_BYTEI(i)	 (((i)>>0)&0xff)
+
+
+/*
+ *  Pages
+ */
+
+#define ES_PAGE_DAC	0x0c
+#define ES_PAGE_ADC	0x0d
+#define ES_PAGE_UART	0x0e
+#define ES_PAGE_UART1	0x0f
+
+/*
+ *  Sample rate converter addresses
+ */
+
+#define ES_SMPREG_DAC1		0x70
+#define ES_SMPREG_DAC2		0x74
+#define ES_SMPREG_ADC		0x78
+#define ES_SMPREG_VOL_ADC	0x6c
+#define ES_SMPREG_VOL_DAC1	0x7c
+#define ES_SMPREG_VOL_DAC2	0x7e
+#define ES_SMPREG_TRUNC_N	0x00
+#define ES_SMPREG_INT_REGS	0x01
+#define ES_SMPREG_ACCUM_FRAC	0x02
+#define ES_SMPREG_VFREQ_FRAC	0x03
+
+/*
+ *  Some contants
+ */
+
+#define ES_1370_SRCLOCK	   1411200
+#define ES_1370_SRTODIV(x) (ES_1370_SRCLOCK/(x)-2)
+
+/*
+ *  Open modes
+ */
+
+#define ES_MODE_PLAY1	0x0001
+#define ES_MODE_PLAY2	0x0002
+#define ES_MODE_CAPTURE	0x0004
+
+#define ES_MODE_OUTPUT	0x0001	/* for MIDI */
+#define ES_MODE_INPUT	0x0002	/* for MIDI */
+
+/*
+
+ */
+
+struct ensoniq {
+	spinlock_t reg_lock;
+	struct semaphore src_mutex;
+
+	int irq;
+
+	unsigned long playback1size;
+	unsigned long playback2size;
+	unsigned long capture3size;
+
+	unsigned long port;
+	unsigned int mode;
+	unsigned int uartm;	/* UART mode */
+
+	unsigned int ctrl;	/* control register */
+	unsigned int sctrl;	/* serial control register */
+	unsigned int cssr;	/* control status register */
+	unsigned int uartc;	/* uart control register */
+	unsigned int rev;	/* chip revision */
+
+	union {
+#ifdef CHIP1371
+		struct {
+			struct snd_ac97 *ac97;
+		} es1371;
+#else
+		struct {
+			int pclkdiv_lock;
+			struct snd_ak4531 *ak4531;
+		} es1370;
+#endif
+	} u;
+
+	struct pci_dev *pci;
+	unsigned short subsystem_vendor_id;
+	unsigned short subsystem_device_id;
+	struct snd_card *card;
+	struct snd_pcm *pcm1;	/* DAC1/ADC PCM */
+	struct snd_pcm *pcm2;	/* DAC2 PCM */
+	struct snd_pcm_substream *playback1_substream;
+	struct snd_pcm_substream *playback2_substream;
+	struct snd_pcm_substream *capture_substream;
+	unsigned int p1_dma_size;
+	unsigned int p2_dma_size;
+	unsigned int c_dma_size;
+	unsigned int p1_period_size;
+	unsigned int p2_period_size;
+	unsigned int c_period_size;
+	struct snd_rawmidi *rmidi;
+	struct snd_rawmidi_substream *midi_input;
+	struct snd_rawmidi_substream *midi_output;
+
+	unsigned int spdif;
+	unsigned int spdif_default;
+	unsigned int spdif_stream;
+
+#ifdef CHIP1370
+	struct snd_dma_buffer dma_bug;
+#endif
+
+#ifdef SUPPORT_JOYSTICK
+	struct gameport *gameport;
+#endif
+};
+
+static irqreturn_t snd_audiopci_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+
+static struct pci_device_id snd_audiopci_ids[] = {
+#ifdef CHIP1370
+	{ 0x1274, 0x5000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },	/* ES1370 */
+#endif
+#ifdef CHIP1371
+	{ 0x1274, 0x1371, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },	/* ES1371 */
+	{ 0x1274, 0x5880, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },	/* ES1373 - CT5880 */
+	{ 0x1102, 0x8938, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },	/* Ectiva EV1938 */
+#endif
+	{ 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, snd_audiopci_ids);
+
+/*
+ *  constants
+ */
+
+#define POLL_COUNT	0xa000
+
+#ifdef CHIP1370
+static unsigned int snd_es1370_fixed_rates[] =
+	{5512, 11025, 22050, 44100};
+static struct snd_pcm_hw_constraint_list snd_es1370_hw_constraints_rates = {
+	.count = 4, 
+	.list = snd_es1370_fixed_rates,
+	.mask = 0,
+};
+static struct snd_ratnum es1370_clock = {
+	.num = ES_1370_SRCLOCK,
+	.den_min = 29, 
+	.den_max = 353,
+	.den_step = 1,
+};
+static struct snd_pcm_hw_constraint_ratnums snd_es1370_hw_constraints_clock = {
+	.nrats = 1,
+	.rats = &es1370_clock,
+};
+#else
+static struct snd_ratden es1371_dac_clock = {
+	.num_min = 3000 * (1 << 15),
+	.num_max = 48000 * (1 << 15),
+	.num_step = 3000,
+	.den = 1 << 15,
+};
+static struct snd_pcm_hw_constraint_ratdens snd_es1371_hw_constraints_dac_clock = {
+	.nrats = 1,
+	.rats = &es1371_dac_clock,
+};
+static struct snd_ratnum es1371_adc_clock = {
+	.num = 48000 << 15,
+	.den_min = 32768, 
+	.den_max = 393216,
+	.den_step = 1,
+};
+static struct snd_pcm_hw_constraint_ratnums snd_es1371_hw_constraints_adc_clock = {
+	.nrats = 1,
+	.rats = &es1371_adc_clock,
+};
+#endif
+static const unsigned int snd_ensoniq_sample_shift[] =
+	{0, 1, 1, 2};
+
+/*
+ *  common I/O routines
+ */
+
+#ifdef CHIP1371
+
+static unsigned int snd_es1371_wait_src_ready(struct ensoniq * ensoniq)
+{
+	unsigned int t, r = 0;
+
+	for (t = 0; t < POLL_COUNT; t++) {
+		r = inl(ES_REG(ensoniq, 1371_SMPRATE));
+		if ((r & ES_1371_SRC_RAM_BUSY) == 0)
+			return r;
+		cond_resched();
+	}
+	snd_printk(KERN_ERR "wait source ready timeout 0x%lx [0x%x]\n",
+		   ES_REG(ensoniq, 1371_SMPRATE), r);
+	return 0;
+}
+
+static unsigned int snd_es1371_src_read(struct ensoniq * ensoniq, unsigned short reg)
+{
+	unsigned int temp, i, orig, r;
+
+	/* wait for ready */
+	temp = orig = snd_es1371_wait_src_ready(ensoniq);
+
+	/* expose the SRC state bits */
+	r = temp & (ES_1371_SRC_DISABLE | ES_1371_DIS_P1 |
+		    ES_1371_DIS_P2 | ES_1371_DIS_R1);
+	r |= ES_1371_SRC_RAM_ADDRO(reg) | 0x10000;
+	outl(r, ES_REG(ensoniq, 1371_SMPRATE));
+
+	/* now, wait for busy and the correct time to read */
+	temp = snd_es1371_wait_src_ready(ensoniq);
+	
+	if ((temp & 0x00870000) != 0x00010000) {
+		/* wait for the right state */
+		for (i = 0; i < POLL_COUNT; i++) {
+			temp = inl(ES_REG(ensoniq, 1371_SMPRATE));
+			if ((temp & 0x00870000) == 0x00010000)
+				break;
+		}
+	}
+
+	/* hide the state bits */	
+	r = orig & (ES_1371_SRC_DISABLE | ES_1371_DIS_P1 |
+		   ES_1371_DIS_P2 | ES_1371_DIS_R1);
+	r |= ES_1371_SRC_RAM_ADDRO(reg);
+	outl(r, ES_REG(ensoniq, 1371_SMPRATE));
+	
+	return temp;
+}
+
+static void snd_es1371_src_write(struct ensoniq * ensoniq,
+				 unsigned short reg, unsigned short data)
+{
+	unsigned int r;
+
+	r = snd_es1371_wait_src_ready(ensoniq) &
+	    (ES_1371_SRC_DISABLE | ES_1371_DIS_P1 |
+	     ES_1371_DIS_P2 | ES_1371_DIS_R1);
+	r |= ES_1371_SRC_RAM_ADDRO(reg) | ES_1371_SRC_RAM_DATAO(data);
+	outl(r | ES_1371_SRC_RAM_WE, ES_REG(ensoniq, 1371_SMPRATE));
+}
+
+#endif /* CHIP1371 */
+
+#ifdef CHIP1370
+
+static void snd_es1370_codec_write(struct snd_ak4531 *ak4531,
+				   unsigned short reg, unsigned short val)
+{
+	struct ensoniq *ensoniq = ak4531->private_data;
+	unsigned long end_time = jiffies + HZ / 10;
+
+#if 0
+	printk("CODEC WRITE: reg = 0x%x, val = 0x%x (0x%x), creg = 0x%x\n",
+	       reg, val, ES_1370_CODEC_WRITE(reg, val), ES_REG(ensoniq, 1370_CODEC));
+#endif
+	do {
+		if (!(inl(ES_REG(ensoniq, STATUS)) & ES_1370_CSTAT)) {
+			outw(ES_1370_CODEC_WRITE(reg, val), ES_REG(ensoniq, 1370_CODEC));
+			return;
+		}
+		schedule_timeout_uninterruptible(1);
+	} while (time_after(end_time, jiffies));
+	snd_printk(KERN_ERR "codec write timeout, status = 0x%x\n",
+		   inl(ES_REG(ensoniq, STATUS)));
+}
+
+#endif /* CHIP1370 */
+
+#ifdef CHIP1371
+
+static void snd_es1371_codec_write(struct snd_ac97 *ac97,
+				   unsigned short reg, unsigned short val)
+{
+	struct ensoniq *ensoniq = ac97->private_data;
+	unsigned int t, x;
+
+	down(&ensoniq->src_mutex);
+	for (t = 0; t < POLL_COUNT; t++) {
+		if (!(inl(ES_REG(ensoniq, 1371_CODEC)) & ES_1371_CODEC_WIP)) {
+			/* save the current state for latter */
+			x = snd_es1371_wait_src_ready(ensoniq);
+			outl((x & (ES_1371_SRC_DISABLE | ES_1371_DIS_P1 |
+			           ES_1371_DIS_P2 | ES_1371_DIS_R1)) | 0x00010000,
+			     ES_REG(ensoniq, 1371_SMPRATE));
+			/* wait for not busy (state 0) first to avoid
+			   transition states */
+			for (t = 0; t < POLL_COUNT; t++) {
+				if ((inl(ES_REG(ensoniq, 1371_SMPRATE)) & 0x00870000) ==
+				    0x00000000)
+					break;
+			}
+			/* wait for a SAFE time to write addr/data and then do it, dammit */
+			for (t = 0; t < POLL_COUNT; t++) {
+				if ((inl(ES_REG(ensoniq, 1371_SMPRATE)) & 0x00870000) ==
+				    0x00010000)
+					break;
+			}
+			outl(ES_1371_CODEC_WRITE(reg, val), ES_REG(ensoniq, 1371_CODEC));
+			/* restore SRC reg */
+			snd_es1371_wait_src_ready(ensoniq);
+			outl(x, ES_REG(ensoniq, 1371_SMPRATE));
+			up(&ensoniq->src_mutex);
+			return;
+		}
+	}
+	up(&ensoniq->src_mutex);
+	snd_printk(KERN_ERR "codec write timeout at 0x%lx [0x%x]\n",
+		   ES_REG(ensoniq, 1371_CODEC), inl(ES_REG(ensoniq, 1371_CODEC)));
+}
+
+static unsigned short snd_es1371_codec_read(struct snd_ac97 *ac97,
+					    unsigned short reg)
+{
+	struct ensoniq *ensoniq = ac97->private_data;
+	unsigned int t, x, fail = 0;
+
+      __again:
+	down(&ensoniq->src_mutex);
+	for (t = 0; t < POLL_COUNT; t++) {
+		if (!(inl(ES_REG(ensoniq, 1371_CODEC)) & ES_1371_CODEC_WIP)) {
+			/* save the current state for latter */
+			x = snd_es1371_wait_src_ready(ensoniq);
+			outl((x & (ES_1371_SRC_DISABLE | ES_1371_DIS_P1 |
+			           ES_1371_DIS_P2 | ES_1371_DIS_R1)) | 0x00010000,
+			     ES_REG(ensoniq, 1371_SMPRATE));
+			/* wait for not busy (state 0) first to avoid
+			   transition states */
+			for (t = 0; t < POLL_COUNT; t++) {
+				if ((inl(ES_REG(ensoniq, 1371_SMPRATE)) & 0x00870000) ==
+				    0x00000000)
+					break;
+			}
+			/* wait for a SAFE time to write addr/data and then do it, dammit */
+			for (t = 0; t < POLL_COUNT; t++) {
+				if ((inl(ES_REG(ensoniq, 1371_SMPRATE)) & 0x00870000) ==
+				    0x00010000)
+					break;
+			}
+			outl(ES_1371_CODEC_READS(reg), ES_REG(ensoniq, 1371_CODEC));
+			/* restore SRC reg */
+			snd_es1371_wait_src_ready(ensoniq);
+			outl(x, ES_REG(ensoniq, 1371_SMPRATE));
+			/* wait for WIP again */
+			for (t = 0; t < POLL_COUNT; t++) {
+				if (!(inl(ES_REG(ensoniq, 1371_CODEC)) & ES_1371_CODEC_WIP))
+					break;		
+			}
+			/* now wait for the stinkin' data (RDY) */
+			for (t = 0; t < POLL_COUNT; t++) {
+				if ((x = inl(ES_REG(ensoniq, 1371_CODEC))) & ES_1371_CODEC_RDY) {
+					up(&ensoniq->src_mutex);
+					return ES_1371_CODEC_READ(x);
+				}
+			}
+			up(&ensoniq->src_mutex);
+			if (++fail > 10) {
+				snd_printk(KERN_ERR "codec read timeout (final) "
+					   "at 0x%lx, reg = 0x%x [0x%x]\n",
+					   ES_REG(ensoniq, 1371_CODEC), reg,
+					   inl(ES_REG(ensoniq, 1371_CODEC)));
+				return 0;
+			}
+			goto __again;
+		}
+	}
+	up(&ensoniq->src_mutex);
+	snd_printk(KERN_ERR "es1371: codec read timeout at 0x%lx [0x%x]\n",
+		   ES_REG(ensoniq, 1371_CODEC), inl(ES_REG(ensoniq, 1371_CODEC)));
+	return 0;
+}
+
+static void snd_es1371_codec_wait(struct snd_ac97 *ac97)
+{
+	msleep(750);
+	snd_es1371_codec_read(ac97, AC97_RESET);
+	snd_es1371_codec_read(ac97, AC97_VENDOR_ID1);
+	snd_es1371_codec_read(ac97, AC97_VENDOR_ID2);
+	msleep(50);
+}
+
+static void snd_es1371_adc_rate(struct ensoniq * ensoniq, unsigned int rate)
+{
+	unsigned int n, truncm, freq, result;
+
+	down(&ensoniq->src_mutex);
+	n = rate / 3000;
+	if ((1 << n) & ((1 << 15) | (1 << 13) | (1 << 11) | (1 << 9)))
+		n--;
+	truncm = (21 * n - 1) | 1;
+	freq = ((48000UL << 15) / rate) * n;
+	result = (48000UL << 15) / (freq / n);
+	if (rate >= 24000) {
+		if (truncm > 239)
+			truncm = 239;
+		snd_es1371_src_write(ensoniq, ES_SMPREG_ADC + ES_SMPREG_TRUNC_N,
+				(((239 - truncm) >> 1) << 9) | (n << 4));
+	} else {
+		if (truncm > 119)
+			truncm = 119;
+		snd_es1371_src_write(ensoniq, ES_SMPREG_ADC + ES_SMPREG_TRUNC_N,
+				0x8000 | (((119 - truncm) >> 1) << 9) | (n << 4));
+	}
+	snd_es1371_src_write(ensoniq, ES_SMPREG_ADC + ES_SMPREG_INT_REGS,
+			     (snd_es1371_src_read(ensoniq, ES_SMPREG_ADC +
+						  ES_SMPREG_INT_REGS) & 0x00ff) |
+			     ((freq >> 5) & 0xfc00));
+	snd_es1371_src_write(ensoniq, ES_SMPREG_ADC + ES_SMPREG_VFREQ_FRAC, freq & 0x7fff);
+	snd_es1371_src_write(ensoniq, ES_SMPREG_VOL_ADC, n << 8);
+	snd_es1371_src_write(ensoniq, ES_SMPREG_VOL_ADC + 1, n << 8);
+	up(&ensoniq->src_mutex);
+}
+
+static void snd_es1371_dac1_rate(struct ensoniq * ensoniq, unsigned int rate)
+{
+	unsigned int freq, r;
+
+	down(&ensoniq->src_mutex);
+	freq = ((rate << 15) + 1500) / 3000;
+	r = (snd_es1371_wait_src_ready(ensoniq) & (ES_1371_SRC_DISABLE |
+						   ES_1371_DIS_P2 | ES_1371_DIS_R1)) |
+		ES_1371_DIS_P1;
+	outl(r, ES_REG(ensoniq, 1371_SMPRATE));
+	snd_es1371_src_write(ensoniq, ES_SMPREG_DAC1 + ES_SMPREG_INT_REGS,
+			     (snd_es1371_src_read(ensoniq, ES_SMPREG_DAC1 +
+						  ES_SMPREG_INT_REGS) & 0x00ff) |
+			     ((freq >> 5) & 0xfc00));
+	snd_es1371_src_write(ensoniq, ES_SMPREG_DAC1 + ES_SMPREG_VFREQ_FRAC, freq & 0x7fff);
+	r = (snd_es1371_wait_src_ready(ensoniq) & (ES_1371_SRC_DISABLE |
+						   ES_1371_DIS_P2 | ES_1371_DIS_R1));
+	outl(r, ES_REG(ensoniq, 1371_SMPRATE));
+	up(&ensoniq->src_mutex);
+}
+
+static void snd_es1371_dac2_rate(struct ensoniq * ensoniq, unsigned int rate)
+{
+	unsigned int freq, r;
+
+	down(&ensoniq->src_mutex);
+	freq = ((rate << 15) + 1500) / 3000;
+	r = (snd_es1371_wait_src_ready(ensoniq) & (ES_1371_SRC_DISABLE |
+						   ES_1371_DIS_P1 | ES_1371_DIS_R1)) |
+		ES_1371_DIS_P2;
+	outl(r, ES_REG(ensoniq, 1371_SMPRATE));
+	snd_es1371_src_write(ensoniq, ES_SMPREG_DAC2 + ES_SMPREG_INT_REGS,
+			     (snd_es1371_src_read(ensoniq, ES_SMPREG_DAC2 +
+						  ES_SMPREG_INT_REGS) & 0x00ff) |
+			     ((freq >> 5) & 0xfc00));
+	snd_es1371_src_write(ensoniq, ES_SMPREG_DAC2 + ES_SMPREG_VFREQ_FRAC,
+			     freq & 0x7fff);
+	r = (snd_es1371_wait_src_ready(ensoniq) & (ES_1371_SRC_DISABLE |
+						   ES_1371_DIS_P1 | ES_1371_DIS_R1));
+	outl(r, ES_REG(ensoniq, 1371_SMPRATE));
+	up(&ensoniq->src_mutex);
+}
+
+#endif /* CHIP1371 */
+
+static int snd_ensoniq_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct ensoniq *ensoniq = snd_pcm_substream_chip(substream);
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+	{
+		unsigned int what = 0;
+		struct list_head *pos;
+		struct snd_pcm_substream *s;
+		snd_pcm_group_for_each(pos, substream) {
+			s = snd_pcm_group_substream_entry(pos);
+			if (s == ensoniq->playback1_substream) {
+				what |= ES_P1_PAUSE;
+				snd_pcm_trigger_done(s, substream);
+			} else if (s == ensoniq->playback2_substream) {
+				what |= ES_P2_PAUSE;
+				snd_pcm_trigger_done(s, substream);
+			} else if (s == ensoniq->capture_substream)
+				return -EINVAL;
+		}
+		spin_lock(&ensoniq->reg_lock);
+		if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH)
+			ensoniq->sctrl |= what;
+		else
+			ensoniq->sctrl &= ~what;
+		outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL));
+		spin_unlock(&ensoniq->reg_lock);
+		break;
+	}
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_STOP:
+	{
+		unsigned int what = 0;
+		struct list_head *pos;
+		struct snd_pcm_substream *s;
+		snd_pcm_group_for_each(pos, substream) {
+			s = snd_pcm_group_substream_entry(pos);
+			if (s == ensoniq->playback1_substream) {
+				what |= ES_DAC1_EN;
+				snd_pcm_trigger_done(s, substream);
+			} else if (s == ensoniq->playback2_substream) {
+				what |= ES_DAC2_EN;
+				snd_pcm_trigger_done(s, substream);
+			} else if (s == ensoniq->capture_substream) {
+				what |= ES_ADC_EN;
+				snd_pcm_trigger_done(s, substream);
+			}
+		}
+		spin_lock(&ensoniq->reg_lock);
+		if (cmd == SNDRV_PCM_TRIGGER_START)
+			ensoniq->ctrl |= what;
+		else
+			ensoniq->ctrl &= ~what;
+		outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
+		spin_unlock(&ensoniq->reg_lock);
+		break;
+	}
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+/*
+ *  PCM part
+ */
+
+static int snd_ensoniq_hw_params(struct snd_pcm_substream *substream,
+				 struct snd_pcm_hw_params *hw_params)
+{
+	return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+static int snd_ensoniq_hw_free(struct snd_pcm_substream *substream)
+{
+	return snd_pcm_lib_free_pages(substream);
+}
+
+static int snd_ensoniq_playback1_prepare(struct snd_pcm_substream *substream)
+{
+	struct ensoniq *ensoniq = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	unsigned int mode = 0;
+
+	ensoniq->p1_dma_size = snd_pcm_lib_buffer_bytes(substream);
+	ensoniq->p1_period_size = snd_pcm_lib_period_bytes(substream);
+	if (snd_pcm_format_width(runtime->format) == 16)
+		mode |= 0x02;
+	if (runtime->channels > 1)
+		mode |= 0x01;
+	spin_lock_irq(&ensoniq->reg_lock);
+	ensoniq->ctrl &= ~ES_DAC1_EN;
+#ifdef CHIP1371
+	/* 48k doesn't need SRC (it breaks AC3-passthru) */
+	if (runtime->rate == 48000)
+		ensoniq->ctrl |= ES_1373_BYPASS_P1;
+	else
+		ensoniq->ctrl &= ~ES_1373_BYPASS_P1;
+#endif
+	outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
+	outl(ES_MEM_PAGEO(ES_PAGE_DAC), ES_REG(ensoniq, MEM_PAGE));
+	outl(runtime->dma_addr, ES_REG(ensoniq, DAC1_FRAME));
+	outl((ensoniq->p1_dma_size >> 2) - 1, ES_REG(ensoniq, DAC1_SIZE));
+	ensoniq->sctrl &= ~(ES_P1_LOOP_SEL | ES_P1_PAUSE | ES_P1_SCT_RLD | ES_P1_MODEM);
+	ensoniq->sctrl |= ES_P1_INT_EN | ES_P1_MODEO(mode);
+	outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL));
+	outl((ensoniq->p1_period_size >> snd_ensoniq_sample_shift[mode]) - 1,
+	     ES_REG(ensoniq, DAC1_COUNT));
+#ifdef CHIP1370
+	ensoniq->ctrl &= ~ES_1370_WTSRSELM;
+	switch (runtime->rate) {
+	case 5512: ensoniq->ctrl |= ES_1370_WTSRSEL(0); break;
+	case 11025: ensoniq->ctrl |= ES_1370_WTSRSEL(1); break;
+	case 22050: ensoniq->ctrl |= ES_1370_WTSRSEL(2); break;
+	case 44100: ensoniq->ctrl |= ES_1370_WTSRSEL(3); break;
+	default: snd_BUG();
+	}
+#endif
+	outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
+	spin_unlock_irq(&ensoniq->reg_lock);
+#ifndef CHIP1370
+	snd_es1371_dac1_rate(ensoniq, runtime->rate);
+#endif
+	return 0;
+}
+
+static int snd_ensoniq_playback2_prepare(struct snd_pcm_substream *substream)
+{
+	struct ensoniq *ensoniq = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	unsigned int mode = 0;
+
+	ensoniq->p2_dma_size = snd_pcm_lib_buffer_bytes(substream);
+	ensoniq->p2_period_size = snd_pcm_lib_period_bytes(substream);
+	if (snd_pcm_format_width(runtime->format) == 16)
+		mode |= 0x02;
+	if (runtime->channels > 1)
+		mode |= 0x01;
+	spin_lock_irq(&ensoniq->reg_lock);
+	ensoniq->ctrl &= ~ES_DAC2_EN;
+	outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
+	outl(ES_MEM_PAGEO(ES_PAGE_DAC), ES_REG(ensoniq, MEM_PAGE));
+	outl(runtime->dma_addr, ES_REG(ensoniq, DAC2_FRAME));
+	outl((ensoniq->p2_dma_size >> 2) - 1, ES_REG(ensoniq, DAC2_SIZE));
+	ensoniq->sctrl &= ~(ES_P2_LOOP_SEL | ES_P2_PAUSE | ES_P2_DAC_SEN |
+			    ES_P2_END_INCM | ES_P2_ST_INCM | ES_P2_MODEM);
+	ensoniq->sctrl |= ES_P2_INT_EN | ES_P2_MODEO(mode) |
+			  ES_P2_END_INCO(mode & 2 ? 2 : 1) | ES_P2_ST_INCO(0);
+	outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL));
+	outl((ensoniq->p2_period_size >> snd_ensoniq_sample_shift[mode]) - 1,
+	     ES_REG(ensoniq, DAC2_COUNT));
+#ifdef CHIP1370
+	if (!(ensoniq->u.es1370.pclkdiv_lock & ES_MODE_CAPTURE)) {
+		ensoniq->ctrl &= ~ES_1370_PCLKDIVM;
+		ensoniq->ctrl |= ES_1370_PCLKDIVO(ES_1370_SRTODIV(runtime->rate));
+		ensoniq->u.es1370.pclkdiv_lock |= ES_MODE_PLAY2;
+	}
+#endif
+	outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
+	spin_unlock_irq(&ensoniq->reg_lock);
+#ifndef CHIP1370
+	snd_es1371_dac2_rate(ensoniq, runtime->rate);
+#endif
+	return 0;
+}
+
+static int snd_ensoniq_capture_prepare(struct snd_pcm_substream *substream)
+{
+	struct ensoniq *ensoniq = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	unsigned int mode = 0;
+
+	ensoniq->c_dma_size = snd_pcm_lib_buffer_bytes(substream);
+	ensoniq->c_period_size = snd_pcm_lib_period_bytes(substream);
+	if (snd_pcm_format_width(runtime->format) == 16)
+		mode |= 0x02;
+	if (runtime->channels > 1)
+		mode |= 0x01;
+	spin_lock_irq(&ensoniq->reg_lock);
+	ensoniq->ctrl &= ~ES_ADC_EN;
+	outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
+	outl(ES_MEM_PAGEO(ES_PAGE_ADC), ES_REG(ensoniq, MEM_PAGE));
+	outl(runtime->dma_addr, ES_REG(ensoniq, ADC_FRAME));
+	outl((ensoniq->c_dma_size >> 2) - 1, ES_REG(ensoniq, ADC_SIZE));
+	ensoniq->sctrl &= ~(ES_R1_LOOP_SEL | ES_R1_MODEM);
+	ensoniq->sctrl |= ES_R1_INT_EN | ES_R1_MODEO(mode);
+	outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL));
+	outl((ensoniq->c_period_size >> snd_ensoniq_sample_shift[mode]) - 1,
+	     ES_REG(ensoniq, ADC_COUNT));
+#ifdef CHIP1370
+	if (!(ensoniq->u.es1370.pclkdiv_lock & ES_MODE_PLAY2)) {
+		ensoniq->ctrl &= ~ES_1370_PCLKDIVM;
+		ensoniq->ctrl |= ES_1370_PCLKDIVO(ES_1370_SRTODIV(runtime->rate));
+		ensoniq->u.es1370.pclkdiv_lock |= ES_MODE_CAPTURE;
+	}
+#endif
+	outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
+	spin_unlock_irq(&ensoniq->reg_lock);
+#ifndef CHIP1370
+	snd_es1371_adc_rate(ensoniq, runtime->rate);
+#endif
+	return 0;
+}
+
+static snd_pcm_uframes_t snd_ensoniq_playback1_pointer(struct snd_pcm_substream *substream)
+{
+	struct ensoniq *ensoniq = snd_pcm_substream_chip(substream);
+	size_t ptr;
+
+	spin_lock(&ensoniq->reg_lock);
+	if (inl(ES_REG(ensoniq, CONTROL)) & ES_DAC1_EN) {
+		outl(ES_MEM_PAGEO(ES_PAGE_DAC), ES_REG(ensoniq, MEM_PAGE));
+		ptr = ES_REG_FCURR_COUNTI(inl(ES_REG(ensoniq, DAC1_SIZE)));
+		ptr = bytes_to_frames(substream->runtime, ptr);
+	} else {
+		ptr = 0;
+	}
+	spin_unlock(&ensoniq->reg_lock);
+	return ptr;
+}
+
+static snd_pcm_uframes_t snd_ensoniq_playback2_pointer(struct snd_pcm_substream *substream)
+{
+	struct ensoniq *ensoniq = snd_pcm_substream_chip(substream);
+	size_t ptr;
+
+	spin_lock(&ensoniq->reg_lock);
+	if (inl(ES_REG(ensoniq, CONTROL)) & ES_DAC2_EN) {
+		outl(ES_MEM_PAGEO(ES_PAGE_DAC), ES_REG(ensoniq, MEM_PAGE));
+		ptr = ES_REG_FCURR_COUNTI(inl(ES_REG(ensoniq, DAC2_SIZE)));
+		ptr = bytes_to_frames(substream->runtime, ptr);
+	} else {
+		ptr = 0;
+	}
+	spin_unlock(&ensoniq->reg_lock);
+	return ptr;
+}
+
+static snd_pcm_uframes_t snd_ensoniq_capture_pointer(struct snd_pcm_substream *substream)
+{
+	struct ensoniq *ensoniq = snd_pcm_substream_chip(substream);
+	size_t ptr;
+
+	spin_lock(&ensoniq->reg_lock);
+	if (inl(ES_REG(ensoniq, CONTROL)) & ES_ADC_EN) {
+		outl(ES_MEM_PAGEO(ES_PAGE_ADC), ES_REG(ensoniq, MEM_PAGE));
+		ptr = ES_REG_FCURR_COUNTI(inl(ES_REG(ensoniq, ADC_SIZE)));
+		ptr = bytes_to_frames(substream->runtime, ptr);
+	} else {
+		ptr = 0;
+	}
+	spin_unlock(&ensoniq->reg_lock);
+	return ptr;
+}
+
+static struct snd_pcm_hardware snd_ensoniq_playback1 =
+{
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_MMAP_VALID |
+				 SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_SYNC_START),
+	.formats =		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+	.rates =
+#ifndef CHIP1370
+				SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+#else
+				(SNDRV_PCM_RATE_KNOT | 	/* 5512Hz rate */
+				 SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_22050 | 
+				 SNDRV_PCM_RATE_44100),
+#endif
+	.rate_min =		4000,
+	.rate_max =		48000,
+	.channels_min =		1,
+	.channels_max =		2,
+	.buffer_bytes_max =	(128*1024),
+	.period_bytes_min =	64,
+	.period_bytes_max =	(128*1024),
+	.periods_min =		1,
+	.periods_max =		1024,
+	.fifo_size =		0,
+};
+
+static struct snd_pcm_hardware snd_ensoniq_playback2 =
+{
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE | 
+				 SNDRV_PCM_INFO_SYNC_START),
+	.formats =		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	.rate_min =		4000,
+	.rate_max =		48000,
+	.channels_min =		1,
+	.channels_max =		2,
+	.buffer_bytes_max =	(128*1024),
+	.period_bytes_min =	64,
+	.period_bytes_max =	(128*1024),
+	.periods_min =		1,
+	.periods_max =		1024,
+	.fifo_size =		0,
+};
+
+static struct snd_pcm_hardware snd_ensoniq_capture =
+{
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START),
+	.formats =		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	.rate_min =		4000,
+	.rate_max =		48000,
+	.channels_min =		1,
+	.channels_max =		2,
+	.buffer_bytes_max =	(128*1024),
+	.period_bytes_min =	64,
+	.period_bytes_max =	(128*1024),
+	.periods_min =		1,
+	.periods_max =		1024,
+	.fifo_size =		0,
+};
+
+static int snd_ensoniq_playback1_open(struct snd_pcm_substream *substream)
+{
+	struct ensoniq *ensoniq = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+
+	ensoniq->mode |= ES_MODE_PLAY1;
+	ensoniq->playback1_substream = substream;
+	runtime->hw = snd_ensoniq_playback1;
+	snd_pcm_set_sync(substream);
+	spin_lock_irq(&ensoniq->reg_lock);
+	if (ensoniq->spdif && ensoniq->playback2_substream == NULL)
+		ensoniq->spdif_stream = ensoniq->spdif_default;
+	spin_unlock_irq(&ensoniq->reg_lock);
+#ifdef CHIP1370
+	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+				   &snd_es1370_hw_constraints_rates);
+#else
+//	snd_pcm_hw_constraint_ratdens(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+//				      &snd_es1371_hw_constraints_dac_clock);
+#endif
+	return 0;
+}
+
+static int snd_ensoniq_playback2_open(struct snd_pcm_substream *substream)
+{
+	struct ensoniq *ensoniq = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+
+	ensoniq->mode |= ES_MODE_PLAY2;
+	ensoniq->playback2_substream = substream;
+	runtime->hw = snd_ensoniq_playback2;
+	snd_pcm_set_sync(substream);
+	spin_lock_irq(&ensoniq->reg_lock);
+	if (ensoniq->spdif && ensoniq->playback1_substream == NULL)
+		ensoniq->spdif_stream = ensoniq->spdif_default;
+	spin_unlock_irq(&ensoniq->reg_lock);
+#ifdef CHIP1370
+	snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+				      &snd_es1370_hw_constraints_clock);
+#else
+//	snd_pcm_hw_constraint_ratdens(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+//				      &snd_es1371_hw_constraints_dac_clock);
+#endif
+	return 0;
+}
+
+static int snd_ensoniq_capture_open(struct snd_pcm_substream *substream)
+{
+	struct ensoniq *ensoniq = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+
+	ensoniq->mode |= ES_MODE_CAPTURE;
+	ensoniq->capture_substream = substream;
+	runtime->hw = snd_ensoniq_capture;
+	snd_pcm_set_sync(substream);
+#ifdef CHIP1370
+	snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+				      &snd_es1370_hw_constraints_clock);
+#else
+	snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+				      &snd_es1371_hw_constraints_adc_clock);
+#endif
+	return 0;
+}
+
+static int snd_ensoniq_playback1_close(struct snd_pcm_substream *substream)
+{
+	struct ensoniq *ensoniq = snd_pcm_substream_chip(substream);
+
+	ensoniq->playback1_substream = NULL;
+	ensoniq->mode &= ~ES_MODE_PLAY1;
+	return 0;
+}
+
+static int snd_ensoniq_playback2_close(struct snd_pcm_substream *substream)
+{
+	struct ensoniq *ensoniq = snd_pcm_substream_chip(substream);
+
+	ensoniq->playback2_substream = NULL;
+	spin_lock_irq(&ensoniq->reg_lock);
+#ifdef CHIP1370
+	ensoniq->u.es1370.pclkdiv_lock &= ~ES_MODE_PLAY2;
+#endif
+	ensoniq->mode &= ~ES_MODE_PLAY2;
+	spin_unlock_irq(&ensoniq->reg_lock);
+	return 0;
+}
+
+static int snd_ensoniq_capture_close(struct snd_pcm_substream *substream)
+{
+	struct ensoniq *ensoniq = snd_pcm_substream_chip(substream);
+
+	ensoniq->capture_substream = NULL;
+	spin_lock_irq(&ensoniq->reg_lock);
+#ifdef CHIP1370
+	ensoniq->u.es1370.pclkdiv_lock &= ~ES_MODE_CAPTURE;
+#endif
+	ensoniq->mode &= ~ES_MODE_CAPTURE;
+	spin_unlock_irq(&ensoniq->reg_lock);
+	return 0;
+}
+
+static struct snd_pcm_ops snd_ensoniq_playback1_ops = {
+	.open =		snd_ensoniq_playback1_open,
+	.close =	snd_ensoniq_playback1_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_ensoniq_hw_params,
+	.hw_free =	snd_ensoniq_hw_free,
+	.prepare =	snd_ensoniq_playback1_prepare,
+	.trigger =	snd_ensoniq_trigger,
+	.pointer =	snd_ensoniq_playback1_pointer,
+};
+
+static struct snd_pcm_ops snd_ensoniq_playback2_ops = {
+	.open =		snd_ensoniq_playback2_open,
+	.close =	snd_ensoniq_playback2_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_ensoniq_hw_params,
+	.hw_free =	snd_ensoniq_hw_free,
+	.prepare =	snd_ensoniq_playback2_prepare,
+	.trigger =	snd_ensoniq_trigger,
+	.pointer =	snd_ensoniq_playback2_pointer,
+};
+
+static struct snd_pcm_ops snd_ensoniq_capture_ops = {
+	.open =		snd_ensoniq_capture_open,
+	.close =	snd_ensoniq_capture_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_ensoniq_hw_params,
+	.hw_free =	snd_ensoniq_hw_free,
+	.prepare =	snd_ensoniq_capture_prepare,
+	.trigger =	snd_ensoniq_trigger,
+	.pointer =	snd_ensoniq_capture_pointer,
+};
+
+static int __devinit snd_ensoniq_pcm(struct ensoniq * ensoniq, int device,
+				     struct snd_pcm ** rpcm)
+{
+	struct snd_pcm *pcm;
+	int err;
+
+	if (rpcm)
+		*rpcm = NULL;
+#ifdef CHIP1370
+	err = snd_pcm_new(ensoniq->card, "ES1370/1", device, 1, 1, &pcm);
+#else
+	err = snd_pcm_new(ensoniq->card, "ES1371/1", device, 1, 1, &pcm);
+#endif
+	if (err < 0)
+		return err;
+
+#ifdef CHIP1370
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ensoniq_playback2_ops);
+#else
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ensoniq_playback1_ops);
+#endif
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ensoniq_capture_ops);
+
+	pcm->private_data = ensoniq;
+	pcm->info_flags = 0;
+#ifdef CHIP1370
+	strcpy(pcm->name, "ES1370 DAC2/ADC");
+#else
+	strcpy(pcm->name, "ES1371 DAC2/ADC");
+#endif
+	ensoniq->pcm1 = pcm;
+
+	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+					      snd_dma_pci_data(ensoniq->pci), 64*1024, 128*1024);
+
+	if (rpcm)
+		*rpcm = pcm;
+	return 0;
+}
+
+static int __devinit snd_ensoniq_pcm2(struct ensoniq * ensoniq, int device,
+				      struct snd_pcm ** rpcm)
+{
+	struct snd_pcm *pcm;
+	int err;
+
+	if (rpcm)
+		*rpcm = NULL;
+#ifdef CHIP1370
+	err = snd_pcm_new(ensoniq->card, "ES1370/2", device, 1, 0, &pcm);
+#else
+	err = snd_pcm_new(ensoniq->card, "ES1371/2", device, 1, 0, &pcm);
+#endif
+	if (err < 0)
+		return err;
+
+#ifdef CHIP1370
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ensoniq_playback1_ops);
+#else
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ensoniq_playback2_ops);
+#endif
+	pcm->private_data = ensoniq;
+	pcm->info_flags = 0;
+#ifdef CHIP1370
+	strcpy(pcm->name, "ES1370 DAC1");
+#else
+	strcpy(pcm->name, "ES1371 DAC1");
+#endif
+	ensoniq->pcm2 = pcm;
+
+	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+					      snd_dma_pci_data(ensoniq->pci), 64*1024, 128*1024);
+
+	if (rpcm)
+		*rpcm = pcm;
+	return 0;
+}
+
+/*
+ *  Mixer section
+ */
+
+/*
+ * ENS1371 mixer (including SPDIF interface)
+ */
+#ifdef CHIP1371
+static int snd_ens1373_spdif_info(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+	uinfo->count = 1;
+	return 0;
+}
+
+static int snd_ens1373_spdif_default_get(struct snd_kcontrol *kcontrol,
+                                         struct snd_ctl_elem_value *ucontrol)
+{
+	struct ensoniq *ensoniq = snd_kcontrol_chip(kcontrol);
+	spin_lock_irq(&ensoniq->reg_lock);
+	ucontrol->value.iec958.status[0] = (ensoniq->spdif_default >> 0) & 0xff;
+	ucontrol->value.iec958.status[1] = (ensoniq->spdif_default >> 8) & 0xff;
+	ucontrol->value.iec958.status[2] = (ensoniq->spdif_default >> 16) & 0xff;
+	ucontrol->value.iec958.status[3] = (ensoniq->spdif_default >> 24) & 0xff;
+	spin_unlock_irq(&ensoniq->reg_lock);
+	return 0;
+}
+
+static int snd_ens1373_spdif_default_put(struct snd_kcontrol *kcontrol,
+                                         struct snd_ctl_elem_value *ucontrol)
+{
+	struct ensoniq *ensoniq = snd_kcontrol_chip(kcontrol);
+	unsigned int val;
+	int change;
+
+	val = ((u32)ucontrol->value.iec958.status[0] << 0) |
+	      ((u32)ucontrol->value.iec958.status[1] << 8) |
+	      ((u32)ucontrol->value.iec958.status[2] << 16) |
+	      ((u32)ucontrol->value.iec958.status[3] << 24);
+	spin_lock_irq(&ensoniq->reg_lock);
+	change = ensoniq->spdif_default != val;
+	ensoniq->spdif_default = val;
+	if (change && ensoniq->playback1_substream == NULL &&
+	    ensoniq->playback2_substream == NULL)
+		outl(val, ES_REG(ensoniq, CHANNEL_STATUS));
+	spin_unlock_irq(&ensoniq->reg_lock);
+	return change;
+}
+
+static int snd_ens1373_spdif_mask_get(struct snd_kcontrol *kcontrol,
+				      struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.iec958.status[0] = 0xff;
+	ucontrol->value.iec958.status[1] = 0xff;
+	ucontrol->value.iec958.status[2] = 0xff;
+	ucontrol->value.iec958.status[3] = 0xff;
+	return 0;
+}
+
+static int snd_ens1373_spdif_stream_get(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	struct ensoniq *ensoniq = snd_kcontrol_chip(kcontrol);
+	spin_lock_irq(&ensoniq->reg_lock);
+	ucontrol->value.iec958.status[0] = (ensoniq->spdif_stream >> 0) & 0xff;
+	ucontrol->value.iec958.status[1] = (ensoniq->spdif_stream >> 8) & 0xff;
+	ucontrol->value.iec958.status[2] = (ensoniq->spdif_stream >> 16) & 0xff;
+	ucontrol->value.iec958.status[3] = (ensoniq->spdif_stream >> 24) & 0xff;
+	spin_unlock_irq(&ensoniq->reg_lock);
+	return 0;
+}
+
+static int snd_ens1373_spdif_stream_put(struct snd_kcontrol *kcontrol,
+                                        struct snd_ctl_elem_value *ucontrol)
+{
+	struct ensoniq *ensoniq = snd_kcontrol_chip(kcontrol);
+	unsigned int val;
+	int change;
+
+	val = ((u32)ucontrol->value.iec958.status[0] << 0) |
+	      ((u32)ucontrol->value.iec958.status[1] << 8) |
+	      ((u32)ucontrol->value.iec958.status[2] << 16) |
+	      ((u32)ucontrol->value.iec958.status[3] << 24);
+	spin_lock_irq(&ensoniq->reg_lock);
+	change = ensoniq->spdif_stream != val;
+	ensoniq->spdif_stream = val;
+	if (change && (ensoniq->playback1_substream != NULL ||
+		       ensoniq->playback2_substream != NULL))
+		outl(val, ES_REG(ensoniq, CHANNEL_STATUS));
+	spin_unlock_irq(&ensoniq->reg_lock);
+	return change;
+}
+
+#define ES1371_SPDIF(xname) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_es1371_spdif_info, \
+  .get = snd_es1371_spdif_get, .put = snd_es1371_spdif_put }
+
+static int snd_es1371_spdif_info(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_info *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_es1371_spdif_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct ensoniq *ensoniq = snd_kcontrol_chip(kcontrol);
+	
+	spin_lock_irq(&ensoniq->reg_lock);
+	ucontrol->value.integer.value[0] = ensoniq->ctrl & ES_1373_SPDIF_THRU ? 1 : 0;
+	spin_unlock_irq(&ensoniq->reg_lock);
+	return 0;
+}
+
+static int snd_es1371_spdif_put(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct ensoniq *ensoniq = snd_kcontrol_chip(kcontrol);
+	unsigned int nval1, nval2;
+	int change;
+	
+	nval1 = ucontrol->value.integer.value[0] ? ES_1373_SPDIF_THRU : 0;
+	nval2 = ucontrol->value.integer.value[0] ? ES_1373_SPDIF_EN : 0;
+	spin_lock_irq(&ensoniq->reg_lock);
+	change = (ensoniq->ctrl & ES_1373_SPDIF_THRU) != nval1;
+	ensoniq->ctrl &= ~ES_1373_SPDIF_THRU;
+	ensoniq->ctrl |= nval1;
+	ensoniq->cssr &= ~ES_1373_SPDIF_EN;
+	ensoniq->cssr |= nval2;
+	outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
+	outl(ensoniq->cssr, ES_REG(ensoniq, STATUS));
+	spin_unlock_irq(&ensoniq->reg_lock);
+	return change;
+}
+
+
+/* spdif controls */
+static struct snd_kcontrol_new snd_es1371_mixer_spdif[] __devinitdata = {
+	ES1371_SPDIF(SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH)),
+	{
+		.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
+		.name =		SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
+		.info =		snd_ens1373_spdif_info,
+		.get =		snd_ens1373_spdif_default_get,
+		.put =		snd_ens1373_spdif_default_put,
+	},
+	{
+		.access =	SNDRV_CTL_ELEM_ACCESS_READ,
+		.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
+		.name =		SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK),
+		.info =		snd_ens1373_spdif_info,
+		.get =		snd_ens1373_spdif_mask_get
+	},
+	{
+		.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
+		.name =		SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM),
+		.info =		snd_ens1373_spdif_info,
+		.get =		snd_ens1373_spdif_stream_get,
+		.put =		snd_ens1373_spdif_stream_put
+	},
+};
+
+
+static int snd_es1373_rear_info(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_info *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_es1373_rear_get(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	struct ensoniq *ensoniq = snd_kcontrol_chip(kcontrol);
+	int val = 0;
+	
+	spin_lock_irq(&ensoniq->reg_lock);
+	if ((ensoniq->cssr & (ES_1373_REAR_BIT27|ES_1373_REAR_BIT26|
+			      ES_1373_REAR_BIT24)) == ES_1373_REAR_BIT26)
+	    	val = 1;
+	ucontrol->value.integer.value[0] = val;
+	spin_unlock_irq(&ensoniq->reg_lock);
+	return 0;
+}
+
+static int snd_es1373_rear_put(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	struct ensoniq *ensoniq = snd_kcontrol_chip(kcontrol);
+	unsigned int nval1;
+	int change;
+	
+	nval1 = ucontrol->value.integer.value[0] ?
+		ES_1373_REAR_BIT26 : (ES_1373_REAR_BIT27|ES_1373_REAR_BIT24);
+	spin_lock_irq(&ensoniq->reg_lock);
+	change = (ensoniq->cssr & (ES_1373_REAR_BIT27|
+				   ES_1373_REAR_BIT26|ES_1373_REAR_BIT24)) != nval1;
+	ensoniq->cssr &= ~(ES_1373_REAR_BIT27|ES_1373_REAR_BIT26|ES_1373_REAR_BIT24);
+	ensoniq->cssr |= nval1;
+	outl(ensoniq->cssr, ES_REG(ensoniq, STATUS));
+	spin_unlock_irq(&ensoniq->reg_lock);
+	return change;
+}
+
+static struct snd_kcontrol_new snd_ens1373_rear __devinitdata =
+{
+	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name =		"AC97 2ch->4ch Copy Switch",
+	.info =		snd_es1373_rear_info,
+	.get =		snd_es1373_rear_get,
+	.put =		snd_es1373_rear_put,
+};
+
+static int snd_es1373_line_info(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_info *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_es1373_line_get(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	struct ensoniq *ensoniq = snd_kcontrol_chip(kcontrol);
+	int val = 0;
+	
+	spin_lock_irq(&ensoniq->reg_lock);
+	if ((ensoniq->ctrl & ES_1371_GPIO_OUTM) >= 4)
+	    	val = 1;
+	ucontrol->value.integer.value[0] = val;
+	spin_unlock_irq(&ensoniq->reg_lock);
+	return 0;
+}
+
+static int snd_es1373_line_put(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	struct ensoniq *ensoniq = snd_kcontrol_chip(kcontrol);
+	int changed;
+	unsigned int ctrl;
+	
+	spin_lock_irq(&ensoniq->reg_lock);
+	ctrl = ensoniq->ctrl;
+	if (ucontrol->value.integer.value[0])
+		ensoniq->ctrl |= ES_1371_GPIO_OUT(4);	/* switch line-in -> rear out */
+	else
+		ensoniq->ctrl &= ~ES_1371_GPIO_OUT(4);
+	changed = (ctrl != ensoniq->ctrl);
+	if (changed)
+		outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
+	spin_unlock_irq(&ensoniq->reg_lock);
+	return changed;
+}
+
+static struct snd_kcontrol_new snd_ens1373_line __devinitdata =
+{
+	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name =		"Line In->Rear Out Switch",
+	.info =		snd_es1373_line_info,
+	.get =		snd_es1373_line_get,
+	.put =		snd_es1373_line_put,
+};
+
+static void snd_ensoniq_mixer_free_ac97(struct snd_ac97 *ac97)
+{
+	struct ensoniq *ensoniq = ac97->private_data;
+	ensoniq->u.es1371.ac97 = NULL;
+}
+
+static struct {
+	unsigned short vid;		/* vendor ID */
+	unsigned short did;		/* device ID */
+	unsigned char rev;		/* revision */
+} es1371_spdif_present[] __devinitdata = {
+	{ .vid = PCI_VENDOR_ID_ENSONIQ, .did = PCI_DEVICE_ID_ENSONIQ_CT5880, .rev = CT5880REV_CT5880_C },
+	{ .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 }
+};
+
+static int snd_ensoniq_1371_mixer(struct ensoniq * ensoniq, int has_spdif, int has_line)
+{
+	struct snd_card *card = ensoniq->card;
+	struct snd_ac97_bus *pbus;
+	struct snd_ac97_template ac97;
+	int err, idx;
+	static struct snd_ac97_bus_ops ops = {
+		.write = snd_es1371_codec_write,
+		.read = snd_es1371_codec_read,
+		.wait = snd_es1371_codec_wait,
+	};
+
+	if ((err = snd_ac97_bus(card, 0, &ops, NULL, &pbus)) < 0)
+		return err;
+
+	memset(&ac97, 0, sizeof(ac97));
+	ac97.private_data = ensoniq;
+	ac97.private_free = snd_ensoniq_mixer_free_ac97;
+	ac97.scaps = AC97_SCAP_AUDIO;
+	if ((err = snd_ac97_mixer(pbus, &ac97, &ensoniq->u.es1371.ac97)) < 0)
+		return err;
+	for (idx = 0; es1371_spdif_present[idx].vid != (unsigned short)PCI_ANY_ID; idx++)
+		if ((ensoniq->pci->vendor == es1371_spdif_present[idx].vid &&
+		     ensoniq->pci->device == es1371_spdif_present[idx].did &&
+		     ensoniq->rev == es1371_spdif_present[idx].rev) || has_spdif > 0) {
+		    	struct snd_kcontrol *kctl;
+			int i, index = 0; 
+
+                        if (has_spdif < 0)
+                                break;
+
+			ensoniq->spdif_default = ensoniq->spdif_stream =
+				SNDRV_PCM_DEFAULT_CON_SPDIF;
+			outl(ensoniq->spdif_default, ES_REG(ensoniq, CHANNEL_STATUS));
+
+		    	if (ensoniq->u.es1371.ac97->ext_id & AC97_EI_SPDIF)
+			    	index++;
+
+			for (i = 0; i < (int)ARRAY_SIZE(snd_es1371_mixer_spdif); i++) {
+				kctl = snd_ctl_new1(&snd_es1371_mixer_spdif[i], ensoniq);
+				if (! kctl)
+					return -ENOMEM;
+				kctl->id.index = index;
+				if ((err = snd_ctl_add(card, kctl)) < 0)
+					return err;
+			}
+			break;
+		}
+	if (ensoniq->u.es1371.ac97->ext_id & AC97_EI_SDAC) {
+		/* mirror rear to front speakers */
+		ensoniq->cssr &= ~(ES_1373_REAR_BIT27|ES_1373_REAR_BIT24);
+		ensoniq->cssr |= ES_1373_REAR_BIT26;
+		err = snd_ctl_add(card, snd_ctl_new1(&snd_ens1373_rear, ensoniq));
+		if (err < 0)
+			return err;
+	}
+	if (((ensoniq->subsystem_vendor_id == 0x1274) &&
+	    (ensoniq->subsystem_device_id == 0x2000)) || /* GA-7DXR */
+	    ((ensoniq->subsystem_vendor_id == 0x1458) &&
+	    (ensoniq->subsystem_device_id == 0xa000)) || /* GA-8IEXP */
+	    has_line > 0) {
+		 err = snd_ctl_add(card, snd_ctl_new1(&snd_ens1373_line, ensoniq));
+		 if (err < 0)
+			 return err;
+	}
+
+	return 0;
+}
+
+#endif /* CHIP1371 */
+
+/* generic control callbacks for ens1370 */
+#ifdef CHIP1370
+#define ENSONIQ_CONTROL(xname, mask) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_CARD, .name = xname, .info = snd_ensoniq_control_info, \
+  .get = snd_ensoniq_control_get, .put = snd_ensoniq_control_put, \
+  .private_value = mask }
+
+static int snd_ensoniq_control_info(struct snd_kcontrol *kcontrol,
+				    struct snd_ctl_elem_info *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_ensoniq_control_get(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	struct ensoniq *ensoniq = snd_kcontrol_chip(kcontrol);
+	int mask = kcontrol->private_value;
+	
+	spin_lock_irq(&ensoniq->reg_lock);
+	ucontrol->value.integer.value[0] = ensoniq->ctrl & mask ? 1 : 0;
+	spin_unlock_irq(&ensoniq->reg_lock);
+	return 0;
+}
+
+static int snd_ensoniq_control_put(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	struct ensoniq *ensoniq = snd_kcontrol_chip(kcontrol);
+	int mask = kcontrol->private_value;
+	unsigned int nval;
+	int change;
+	
+	nval = ucontrol->value.integer.value[0] ? mask : 0;
+	spin_lock_irq(&ensoniq->reg_lock);
+	change = (ensoniq->ctrl & mask) != nval;
+	ensoniq->ctrl &= ~mask;
+	ensoniq->ctrl |= nval;
+	outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
+	spin_unlock_irq(&ensoniq->reg_lock);
+	return change;
+}
+
+/*
+ * ENS1370 mixer
+ */
+
+static struct snd_kcontrol_new snd_es1370_controls[2] __devinitdata = {
+ENSONIQ_CONTROL("PCM 0 Output also on Line-In Jack", ES_1370_XCTL0),
+ENSONIQ_CONTROL("Mic +5V bias", ES_1370_XCTL1)
+};
+
+#define ES1370_CONTROLS ARRAY_SIZE(snd_es1370_controls)
+
+static void snd_ensoniq_mixer_free_ak4531(struct snd_ak4531 *ak4531)
+{
+	struct ensoniq *ensoniq = ak4531->private_data;
+	ensoniq->u.es1370.ak4531 = NULL;
+}
+
+static int __devinit snd_ensoniq_1370_mixer(struct ensoniq * ensoniq)
+{
+	struct snd_card *card = ensoniq->card;
+	struct snd_ak4531 ak4531;
+	unsigned int idx;
+	int err;
+
+	/* try reset AK4531 */
+	outw(ES_1370_CODEC_WRITE(AK4531_RESET, 0x02), ES_REG(ensoniq, 1370_CODEC));
+	inw(ES_REG(ensoniq, 1370_CODEC));
+	udelay(100);
+	outw(ES_1370_CODEC_WRITE(AK4531_RESET, 0x03), ES_REG(ensoniq, 1370_CODEC));
+	inw(ES_REG(ensoniq, 1370_CODEC));
+	udelay(100);
+
+	memset(&ak4531, 0, sizeof(ak4531));
+	ak4531.write = snd_es1370_codec_write;
+	ak4531.private_data = ensoniq;
+	ak4531.private_free = snd_ensoniq_mixer_free_ak4531;
+	if ((err = snd_ak4531_mixer(card, &ak4531, &ensoniq->u.es1370.ak4531)) < 0)
+		return err;
+	for (idx = 0; idx < ES1370_CONTROLS; idx++) {
+		err = snd_ctl_add(card, snd_ctl_new1(&snd_es1370_controls[idx], ensoniq));
+		if (err < 0)
+			return err;
+	}
+	return 0;
+}
+
+#endif /* CHIP1370 */
+
+#ifdef SUPPORT_JOYSTICK
+
+#ifdef CHIP1371
+static int __devinit snd_ensoniq_get_joystick_port(int dev)
+{
+	switch (joystick_port[dev]) {
+	case 0: /* disabled */
+	case 1: /* auto-detect */
+	case 0x200:
+	case 0x208:
+	case 0x210:
+	case 0x218:
+		return joystick_port[dev];
+
+	default:
+		printk(KERN_ERR "ens1371: invalid joystick port %#x", joystick_port[dev]);
+		return 0;
+	}
+}
+#else
+static inline int snd_ensoniq_get_joystick_port(int dev)
+{
+	return joystick[dev] ? 0x200 : 0;
+}
+#endif
+
+static int __devinit snd_ensoniq_create_gameport(struct ensoniq *ensoniq, int dev)
+{
+	struct gameport *gp;
+	int io_port;
+
+	io_port = snd_ensoniq_get_joystick_port(dev);
+
+	switch (io_port) {
+	case 0:
+		return -ENOSYS;
+
+	case 1: /* auto_detect */
+		for (io_port = 0x200; io_port <= 0x218; io_port += 8)
+			if (request_region(io_port, 8, "ens137x: gameport"))
+				break;
+		if (io_port > 0x218) {
+			printk(KERN_WARNING "ens137x: no gameport ports available\n");
+			return -EBUSY;
+		}
+		break;
+
+	default:
+		if (!request_region(io_port, 8, "ens137x: gameport")) {
+			printk(KERN_WARNING "ens137x: gameport io port 0x%#x in use\n",
+			       io_port);
+			return -EBUSY;
+		}
+		break;
+	}
+
+	ensoniq->gameport = gp = gameport_allocate_port();
+	if (!gp) {
+		printk(KERN_ERR "ens137x: cannot allocate memory for gameport\n");
+		release_region(io_port, 8);
+		return -ENOMEM;
+	}
+
+	gameport_set_name(gp, "ES137x");
+	gameport_set_phys(gp, "pci%s/gameport0", pci_name(ensoniq->pci));
+	gameport_set_dev_parent(gp, &ensoniq->pci->dev);
+	gp->io = io_port;
+
+	ensoniq->ctrl |= ES_JYSTK_EN;
+#ifdef CHIP1371
+	ensoniq->ctrl &= ~ES_1371_JOY_ASELM;
+	ensoniq->ctrl |= ES_1371_JOY_ASEL((io_port - 0x200) / 8);
+#endif
+	outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
+
+	gameport_register_port(ensoniq->gameport);
+
+	return 0;
+}
+
+static void snd_ensoniq_free_gameport(struct ensoniq *ensoniq)
+{
+	if (ensoniq->gameport) {
+		int port = ensoniq->gameport->io;
+
+		gameport_unregister_port(ensoniq->gameport);
+		ensoniq->gameport = NULL;
+		ensoniq->ctrl &= ~ES_JYSTK_EN;
+		outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
+		release_region(port, 8);
+	}
+}
+#else
+static inline int snd_ensoniq_create_gameport(struct ensoniq *ensoniq, long port) { return -ENOSYS; }
+static inline void snd_ensoniq_free_gameport(struct ensoniq *ensoniq) { }
+#endif /* SUPPORT_JOYSTICK */
+
+/*
+
+ */
+
+static void snd_ensoniq_proc_read(struct snd_info_entry *entry, 
+				  struct snd_info_buffer *buffer)
+{
+	struct ensoniq *ensoniq = entry->private_data;
+
+#ifdef CHIP1370
+	snd_iprintf(buffer, "Ensoniq AudioPCI ES1370\n\n");
+#else
+	snd_iprintf(buffer, "Ensoniq AudioPCI ES1371\n\n");
+#endif
+	snd_iprintf(buffer, "Joystick enable  : %s\n",
+		    ensoniq->ctrl & ES_JYSTK_EN ? "on" : "off");
+#ifdef CHIP1370
+	snd_iprintf(buffer, "MIC +5V bias     : %s\n",
+		    ensoniq->ctrl & ES_1370_XCTL1 ? "on" : "off");
+	snd_iprintf(buffer, "Line In to AOUT  : %s\n",
+		    ensoniq->ctrl & ES_1370_XCTL0 ? "on" : "off");
+#else
+	snd_iprintf(buffer, "Joystick port    : 0x%x\n",
+		    (ES_1371_JOY_ASELI(ensoniq->ctrl) * 8) + 0x200);
+#endif
+}
+
+static void __devinit snd_ensoniq_proc_init(struct ensoniq * ensoniq)
+{
+	struct snd_info_entry *entry;
+
+	if (! snd_card_proc_new(ensoniq->card, "audiopci", &entry))
+		snd_info_set_text_ops(entry, ensoniq, 1024, snd_ensoniq_proc_read);
+}
+
+/*
+
+ */
+
+static int snd_ensoniq_free(struct ensoniq *ensoniq)
+{
+	snd_ensoniq_free_gameport(ensoniq);
+	if (ensoniq->irq < 0)
+		goto __hw_end;
+#ifdef CHIP1370
+	outl(ES_1370_SERR_DISABLE, ES_REG(ensoniq, CONTROL));	/* switch everything off */
+	outl(0, ES_REG(ensoniq, SERIAL));	/* clear serial interface */
+#else
+	outl(0, ES_REG(ensoniq, CONTROL));	/* switch everything off */
+	outl(0, ES_REG(ensoniq, SERIAL));	/* clear serial interface */
+#endif
+	synchronize_irq(ensoniq->irq);
+	pci_set_power_state(ensoniq->pci, 3);
+      __hw_end:
+#ifdef CHIP1370
+	if (ensoniq->dma_bug.area)
+		snd_dma_free_pages(&ensoniq->dma_bug);
+#endif
+	if (ensoniq->irq >= 0)
+		free_irq(ensoniq->irq, ensoniq);
+	pci_release_regions(ensoniq->pci);
+	pci_disable_device(ensoniq->pci);
+	kfree(ensoniq);
+	return 0;
+}
+
+static int snd_ensoniq_dev_free(struct snd_device *device)
+{
+	struct ensoniq *ensoniq = device->device_data;
+	return snd_ensoniq_free(ensoniq);
+}
+
+#ifdef CHIP1371
+static struct {
+	unsigned short svid;		/* subsystem vendor ID */
+	unsigned short sdid;		/* subsystem device ID */
+} es1371_amplifier_hack[] = {
+	{ .svid = 0x107b, .sdid = 0x2150 },	/* Gateway Solo 2150 */
+	{ .svid = 0x13bd, .sdid = 0x100c },	/* EV1938 on Mebius PC-MJ100V */
+	{ .svid = 0x1102, .sdid = 0x5938 },	/* Targa Xtender300 */
+	{ .svid = 0x1102, .sdid = 0x8938 },	/* IPC Topnote G notebook */
+	{ .svid = PCI_ANY_ID, .sdid = PCI_ANY_ID }
+};
+static struct {
+	unsigned short vid;		/* vendor ID */
+	unsigned short did;		/* device ID */
+	unsigned char rev;		/* revision */
+} es1371_ac97_reset_hack[] = {
+	{ .vid = PCI_VENDOR_ID_ENSONIQ, .did = PCI_DEVICE_ID_ENSONIQ_CT5880, .rev = CT5880REV_CT5880_C },
+	{ .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 }
+};
+#endif
+
+static void snd_ensoniq_chip_init(struct ensoniq *ensoniq)
+{
+#ifdef CHIP1371
+	int idx;
+	struct pci_dev *pci = ensoniq->pci;
+#endif
+	/* this code was part of snd_ensoniq_create before intruduction
+	  * of suspend/resume
+	  */
+#ifdef CHIP1370
+	outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
+	outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL));
+	outl(ES_MEM_PAGEO(ES_PAGE_ADC), ES_REG(ensoniq, MEM_PAGE));
+	outl(ensoniq->dma_bug.addr, ES_REG(ensoniq, PHANTOM_FRAME));
+	outl(0, ES_REG(ensoniq, PHANTOM_COUNT));
+#else
+	outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
+	outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL));
+	outl(0, ES_REG(ensoniq, 1371_LEGACY));
+	for (idx = 0; es1371_ac97_reset_hack[idx].vid != (unsigned short)PCI_ANY_ID; idx++)
+		if (pci->vendor == es1371_ac97_reset_hack[idx].vid &&
+		    pci->device == es1371_ac97_reset_hack[idx].did &&
+		    ensoniq->rev == es1371_ac97_reset_hack[idx].rev) {
+			outl(ensoniq->cssr, ES_REG(ensoniq, STATUS));
+			/* need to delay around 20ms(bleech) to give
+			some CODECs enough time to wakeup */
+			msleep(20);
+			break;
+		}
+	/* AC'97 warm reset to start the bitclk */
+	outl(ensoniq->ctrl | ES_1371_SYNC_RES, ES_REG(ensoniq, CONTROL));
+	inl(ES_REG(ensoniq, CONTROL));
+	udelay(20);
+	outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
+	/* Init the sample rate converter */
+	snd_es1371_wait_src_ready(ensoniq);	
+	outl(ES_1371_SRC_DISABLE, ES_REG(ensoniq, 1371_SMPRATE));
+	for (idx = 0; idx < 0x80; idx++)
+		snd_es1371_src_write(ensoniq, idx, 0);
+	snd_es1371_src_write(ensoniq, ES_SMPREG_DAC1 + ES_SMPREG_TRUNC_N, 16 << 4);
+	snd_es1371_src_write(ensoniq, ES_SMPREG_DAC1 + ES_SMPREG_INT_REGS, 16 << 10);
+	snd_es1371_src_write(ensoniq, ES_SMPREG_DAC2 + ES_SMPREG_TRUNC_N, 16 << 4);
+	snd_es1371_src_write(ensoniq, ES_SMPREG_DAC2 + ES_SMPREG_INT_REGS, 16 << 10);
+	snd_es1371_src_write(ensoniq, ES_SMPREG_VOL_ADC, 1 << 12);
+	snd_es1371_src_write(ensoniq, ES_SMPREG_VOL_ADC + 1, 1 << 12);
+	snd_es1371_src_write(ensoniq, ES_SMPREG_VOL_DAC1, 1 << 12);
+	snd_es1371_src_write(ensoniq, ES_SMPREG_VOL_DAC1 + 1, 1 << 12);
+	snd_es1371_src_write(ensoniq, ES_SMPREG_VOL_DAC2, 1 << 12);
+	snd_es1371_src_write(ensoniq, ES_SMPREG_VOL_DAC2 + 1, 1 << 12);
+	snd_es1371_adc_rate(ensoniq, 22050);
+	snd_es1371_dac1_rate(ensoniq, 22050);
+	snd_es1371_dac2_rate(ensoniq, 22050);
+	/* WARNING:
+	 * enabling the sample rate converter without properly programming
+	 * its parameters causes the chip to lock up (the SRC busy bit will
+	 * be stuck high, and I've found no way to rectify this other than
+	 * power cycle) - Thomas Sailer
+	 */
+	snd_es1371_wait_src_ready(ensoniq);
+	outl(0, ES_REG(ensoniq, 1371_SMPRATE));
+	/* try reset codec directly */
+	outl(ES_1371_CODEC_WRITE(0, 0), ES_REG(ensoniq, 1371_CODEC));
+#endif
+	outb(ensoniq->uartc = 0x00, ES_REG(ensoniq, UART_CONTROL));
+	outb(0x00, ES_REG(ensoniq, UART_RES));
+	outl(ensoniq->cssr, ES_REG(ensoniq, STATUS));
+	synchronize_irq(ensoniq->irq);
+}
+
+#ifdef CONFIG_PM
+static int snd_ensoniq_suspend(struct pci_dev *pci, pm_message_t state)
+{
+	struct snd_card *card = pci_get_drvdata(pci);
+	struct ensoniq *ensoniq = card->private_data;
+	
+	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+
+	snd_pcm_suspend_all(ensoniq->pcm1);
+	snd_pcm_suspend_all(ensoniq->pcm2);
+	
+#ifdef CHIP1371	
+	snd_ac97_suspend(ensoniq->u.es1371.ac97);
+#else
+	/* try to reset AK4531 */
+	outw(ES_1370_CODEC_WRITE(AK4531_RESET, 0x02), ES_REG(ensoniq, 1370_CODEC));
+	inw(ES_REG(ensoniq, 1370_CODEC));
+	udelay(100);
+	outw(ES_1370_CODEC_WRITE(AK4531_RESET, 0x03), ES_REG(ensoniq, 1370_CODEC));
+	inw(ES_REG(ensoniq, 1370_CODEC));
+	udelay(100);
+	snd_ak4531_suspend(ensoniq->u.es1370.ak4531);
+#endif	
+	pci_set_power_state(pci, PCI_D3hot);
+	pci_disable_device(pci);
+	pci_save_state(pci);
+	return 0;
+}
+
+static int snd_ensoniq_resume(struct pci_dev *pci)
+{
+	struct snd_card *card = pci_get_drvdata(pci);
+	struct ensoniq *ensoniq = card->private_data;
+
+	pci_restore_state(pci);
+	pci_enable_device(pci);
+	pci_set_power_state(pci, PCI_D0);
+	pci_set_master(pci);
+
+	snd_ensoniq_chip_init(ensoniq);
+
+#ifdef CHIP1371	
+	snd_ac97_resume(ensoniq->u.es1371.ac97);
+#else
+	snd_ak4531_resume(ensoniq->u.es1370.ak4531);
+#endif	
+	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+	return 0;
+}
+#endif /* CONFIG_PM */
+
+
+static int __devinit snd_ensoniq_create(struct snd_card *card,
+				     struct pci_dev *pci,
+				     struct ensoniq ** rensoniq)
+{
+	struct ensoniq *ensoniq;
+	unsigned short cmdw;
+	unsigned char cmdb;
+#ifdef CHIP1371
+	int idx;
+#endif
+	int err;
+	static struct snd_device_ops ops = {
+		.dev_free =	snd_ensoniq_dev_free,
+	};
+
+	*rensoniq = NULL;
+	if ((err = pci_enable_device(pci)) < 0)
+		return err;
+	ensoniq = kzalloc(sizeof(*ensoniq), GFP_KERNEL);
+	if (ensoniq == NULL) {
+		pci_disable_device(pci);
+		return -ENOMEM;
+	}
+	spin_lock_init(&ensoniq->reg_lock);
+	init_MUTEX(&ensoniq->src_mutex);
+	ensoniq->card = card;
+	ensoniq->pci = pci;
+	ensoniq->irq = -1;
+	if ((err = pci_request_regions(pci, "Ensoniq AudioPCI")) < 0) {
+		kfree(ensoniq);
+		pci_disable_device(pci);
+		return err;
+	}
+	ensoniq->port = pci_resource_start(pci, 0);
+	if (request_irq(pci->irq, snd_audiopci_interrupt, SA_INTERRUPT|SA_SHIRQ,
+			"Ensoniq AudioPCI", ensoniq)) {
+		snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
+		snd_ensoniq_free(ensoniq);
+		return -EBUSY;
+	}
+	ensoniq->irq = pci->irq;
+#ifdef CHIP1370
+	if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
+				16, &ensoniq->dma_bug) < 0) {
+		snd_printk(KERN_ERR "unable to allocate space for phantom area - dma_bug\n");
+		snd_ensoniq_free(ensoniq);
+		return -EBUSY;
+	}
+#endif
+	pci_set_master(pci);
+	pci_read_config_byte(pci, PCI_REVISION_ID, &cmdb);
+	ensoniq->rev = cmdb;
+	pci_read_config_word(pci, PCI_SUBSYSTEM_VENDOR_ID, &cmdw);
+	ensoniq->subsystem_vendor_id = cmdw;
+	pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &cmdw);
+	ensoniq->subsystem_device_id = cmdw;
+#ifdef CHIP1370
+#if 0
+	ensoniq->ctrl = ES_1370_CDC_EN | ES_1370_SERR_DISABLE |
+		ES_1370_PCLKDIVO(ES_1370_SRTODIV(8000));
+#else	/* get microphone working */
+	ensoniq->ctrl = ES_1370_CDC_EN | ES_1370_PCLKDIVO(ES_1370_SRTODIV(8000));
+#endif
+	ensoniq->sctrl = 0;
+#else
+	ensoniq->ctrl = 0;
+	ensoniq->sctrl = 0;
+	ensoniq->cssr = 0;
+	for (idx = 0; es1371_amplifier_hack[idx].svid != (unsigned short)PCI_ANY_ID; idx++)
+		if (ensoniq->subsystem_vendor_id == es1371_amplifier_hack[idx].svid &&
+		    ensoniq->subsystem_device_id == es1371_amplifier_hack[idx].sdid) {
+			ensoniq->ctrl |= ES_1371_GPIO_OUT(1);	/* turn amplifier on */
+			break;
+		}
+	for (idx = 0; es1371_ac97_reset_hack[idx].vid != (unsigned short)PCI_ANY_ID; idx++)
+		if (pci->vendor == es1371_ac97_reset_hack[idx].vid &&
+		    pci->device == es1371_ac97_reset_hack[idx].did &&
+		    ensoniq->rev == es1371_ac97_reset_hack[idx].rev) {
+			ensoniq->cssr |= ES_1371_ST_AC97_RST;
+			break;
+		}
+#endif
+
+	snd_ensoniq_chip_init(ensoniq);
+
+	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, ensoniq, &ops)) < 0) {
+		snd_ensoniq_free(ensoniq);
+		return err;
+	}
+
+	snd_ensoniq_proc_init(ensoniq);
+
+	snd_card_set_dev(card, &pci->dev);
+
+	*rensoniq = ensoniq;
+	return 0;
+}
+
+/*
+ *  MIDI section
+ */
+
+static void snd_ensoniq_midi_interrupt(struct ensoniq * ensoniq)
+{
+	struct snd_rawmidi *rmidi = ensoniq->rmidi;
+	unsigned char status, mask, byte;
+
+	if (rmidi == NULL)
+		return;
+	/* do Rx at first */
+	spin_lock(&ensoniq->reg_lock);
+	mask = ensoniq->uartm & ES_MODE_INPUT ? ES_RXRDY : 0;
+	while (mask) {
+		status = inb(ES_REG(ensoniq, UART_STATUS));
+		if ((status & mask) == 0)
+			break;
+		byte = inb(ES_REG(ensoniq, UART_DATA));
+		snd_rawmidi_receive(ensoniq->midi_input, &byte, 1);
+	}
+	spin_unlock(&ensoniq->reg_lock);
+
+	/* do Tx at second */
+	spin_lock(&ensoniq->reg_lock);
+	mask = ensoniq->uartm & ES_MODE_OUTPUT ? ES_TXRDY : 0;
+	while (mask) {
+		status = inb(ES_REG(ensoniq, UART_STATUS));
+		if ((status & mask) == 0)
+			break;
+		if (snd_rawmidi_transmit(ensoniq->midi_output, &byte, 1) != 1) {
+			ensoniq->uartc &= ~ES_TXINTENM;
+			outb(ensoniq->uartc, ES_REG(ensoniq, UART_CONTROL));
+			mask &= ~ES_TXRDY;
+		} else {
+			outb(byte, ES_REG(ensoniq, UART_DATA));
+		}
+	}
+	spin_unlock(&ensoniq->reg_lock);
+}
+
+static int snd_ensoniq_midi_input_open(struct snd_rawmidi_substream *substream)
+{
+	struct ensoniq *ensoniq = substream->rmidi->private_data;
+
+	spin_lock_irq(&ensoniq->reg_lock);
+	ensoniq->uartm |= ES_MODE_INPUT;
+	ensoniq->midi_input = substream;
+	if (!(ensoniq->uartm & ES_MODE_OUTPUT)) {
+		outb(ES_CNTRL(3), ES_REG(ensoniq, UART_CONTROL));
+		outb(ensoniq->uartc = 0, ES_REG(ensoniq, UART_CONTROL));
+		outl(ensoniq->ctrl |= ES_UART_EN, ES_REG(ensoniq, CONTROL));
+	}
+	spin_unlock_irq(&ensoniq->reg_lock);
+	return 0;
+}
+
+static int snd_ensoniq_midi_input_close(struct snd_rawmidi_substream *substream)
+{
+	struct ensoniq *ensoniq = substream->rmidi->private_data;
+
+	spin_lock_irq(&ensoniq->reg_lock);
+	if (!(ensoniq->uartm & ES_MODE_OUTPUT)) {
+		outb(ensoniq->uartc = 0, ES_REG(ensoniq, UART_CONTROL));
+		outl(ensoniq->ctrl &= ~ES_UART_EN, ES_REG(ensoniq, CONTROL));
+	} else {
+		outb(ensoniq->uartc &= ~ES_RXINTEN, ES_REG(ensoniq, UART_CONTROL));
+	}
+	ensoniq->midi_input = NULL;
+	ensoniq->uartm &= ~ES_MODE_INPUT;
+	spin_unlock_irq(&ensoniq->reg_lock);
+	return 0;
+}
+
+static int snd_ensoniq_midi_output_open(struct snd_rawmidi_substream *substream)
+{
+	struct ensoniq *ensoniq = substream->rmidi->private_data;
+
+	spin_lock_irq(&ensoniq->reg_lock);
+	ensoniq->uartm |= ES_MODE_OUTPUT;
+	ensoniq->midi_output = substream;
+	if (!(ensoniq->uartm & ES_MODE_INPUT)) {
+		outb(ES_CNTRL(3), ES_REG(ensoniq, UART_CONTROL));
+		outb(ensoniq->uartc = 0, ES_REG(ensoniq, UART_CONTROL));
+		outl(ensoniq->ctrl |= ES_UART_EN, ES_REG(ensoniq, CONTROL));
+	}
+	spin_unlock_irq(&ensoniq->reg_lock);
+	return 0;
+}
+
+static int snd_ensoniq_midi_output_close(struct snd_rawmidi_substream *substream)
+{
+	struct ensoniq *ensoniq = substream->rmidi->private_data;
+
+	spin_lock_irq(&ensoniq->reg_lock);
+	if (!(ensoniq->uartm & ES_MODE_INPUT)) {
+		outb(ensoniq->uartc = 0, ES_REG(ensoniq, UART_CONTROL));
+		outl(ensoniq->ctrl &= ~ES_UART_EN, ES_REG(ensoniq, CONTROL));
+	} else {
+		outb(ensoniq->uartc &= ~ES_TXINTENM, ES_REG(ensoniq, UART_CONTROL));
+	}
+	ensoniq->midi_output = NULL;
+	ensoniq->uartm &= ~ES_MODE_OUTPUT;
+	spin_unlock_irq(&ensoniq->reg_lock);
+	return 0;
+}
+
+static void snd_ensoniq_midi_input_trigger(struct snd_rawmidi_substream *substream, int up)
+{
+	unsigned long flags;
+	struct ensoniq *ensoniq = substream->rmidi->private_data;
+	int idx;
+
+	spin_lock_irqsave(&ensoniq->reg_lock, flags);
+	if (up) {
+		if ((ensoniq->uartc & ES_RXINTEN) == 0) {
+			/* empty input FIFO */
+			for (idx = 0; idx < 32; idx++)
+				inb(ES_REG(ensoniq, UART_DATA));
+			ensoniq->uartc |= ES_RXINTEN;
+			outb(ensoniq->uartc, ES_REG(ensoniq, UART_CONTROL));
+		}
+	} else {
+		if (ensoniq->uartc & ES_RXINTEN) {
+			ensoniq->uartc &= ~ES_RXINTEN;
+			outb(ensoniq->uartc, ES_REG(ensoniq, UART_CONTROL));
+		}
+	}
+	spin_unlock_irqrestore(&ensoniq->reg_lock, flags);
+}
+
+static void snd_ensoniq_midi_output_trigger(struct snd_rawmidi_substream *substream, int up)
+{
+	unsigned long flags;
+	struct ensoniq *ensoniq = substream->rmidi->private_data;
+	unsigned char byte;
+
+	spin_lock_irqsave(&ensoniq->reg_lock, flags);
+	if (up) {
+		if (ES_TXINTENI(ensoniq->uartc) == 0) {
+			ensoniq->uartc |= ES_TXINTENO(1);
+			/* fill UART FIFO buffer at first, and turn Tx interrupts only if necessary */
+			while (ES_TXINTENI(ensoniq->uartc) == 1 &&
+			       (inb(ES_REG(ensoniq, UART_STATUS)) & ES_TXRDY)) {
+				if (snd_rawmidi_transmit(substream, &byte, 1) != 1) {
+					ensoniq->uartc &= ~ES_TXINTENM;
+				} else {
+					outb(byte, ES_REG(ensoniq, UART_DATA));
+				}
+			}
+			outb(ensoniq->uartc, ES_REG(ensoniq, UART_CONTROL));
+		}
+	} else {
+		if (ES_TXINTENI(ensoniq->uartc) == 1) {
+			ensoniq->uartc &= ~ES_TXINTENM;
+			outb(ensoniq->uartc, ES_REG(ensoniq, UART_CONTROL));
+		}
+	}
+	spin_unlock_irqrestore(&ensoniq->reg_lock, flags);
+}
+
+static struct snd_rawmidi_ops snd_ensoniq_midi_output =
+{
+	.open =		snd_ensoniq_midi_output_open,
+	.close =	snd_ensoniq_midi_output_close,
+	.trigger =	snd_ensoniq_midi_output_trigger,
+};
+
+static struct snd_rawmidi_ops snd_ensoniq_midi_input =
+{
+	.open =		snd_ensoniq_midi_input_open,
+	.close =	snd_ensoniq_midi_input_close,
+	.trigger =	snd_ensoniq_midi_input_trigger,
+};
+
+static int __devinit snd_ensoniq_midi(struct ensoniq * ensoniq, int device,
+				      struct snd_rawmidi **rrawmidi)
+{
+	struct snd_rawmidi *rmidi;
+	int err;
+
+	if (rrawmidi)
+		*rrawmidi = NULL;
+	if ((err = snd_rawmidi_new(ensoniq->card, "ES1370/1", device, 1, 1, &rmidi)) < 0)
+		return err;
+#ifdef CHIP1370
+	strcpy(rmidi->name, "ES1370");
+#else
+	strcpy(rmidi->name, "ES1371");
+#endif
+	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_ensoniq_midi_output);
+	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_ensoniq_midi_input);
+	rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT |
+		SNDRV_RAWMIDI_INFO_DUPLEX;
+	rmidi->private_data = ensoniq;
+	ensoniq->rmidi = rmidi;
+	if (rrawmidi)
+		*rrawmidi = rmidi;
+	return 0;
+}
+
+/*
+ *  Interrupt handler
+ */
+
+static irqreturn_t snd_audiopci_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct ensoniq *ensoniq = dev_id;
+	unsigned int status, sctrl;
+
+	if (ensoniq == NULL)
+		return IRQ_NONE;
+
+	status = inl(ES_REG(ensoniq, STATUS));
+	if (!(status & ES_INTR))
+		return IRQ_NONE;
+
+	spin_lock(&ensoniq->reg_lock);
+	sctrl = ensoniq->sctrl;
+	if (status & ES_DAC1)
+		sctrl &= ~ES_P1_INT_EN;
+	if (status & ES_DAC2)
+		sctrl &= ~ES_P2_INT_EN;
+	if (status & ES_ADC)
+		sctrl &= ~ES_R1_INT_EN;
+	outl(sctrl, ES_REG(ensoniq, SERIAL));
+	outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL));
+	spin_unlock(&ensoniq->reg_lock);
+
+	if (status & ES_UART)
+		snd_ensoniq_midi_interrupt(ensoniq);
+	if ((status & ES_DAC2) && ensoniq->playback2_substream)
+		snd_pcm_period_elapsed(ensoniq->playback2_substream);
+	if ((status & ES_ADC) && ensoniq->capture_substream)
+		snd_pcm_period_elapsed(ensoniq->capture_substream);
+	if ((status & ES_DAC1) && ensoniq->playback1_substream)
+		snd_pcm_period_elapsed(ensoniq->playback1_substream);
+	return IRQ_HANDLED;
+}
+
+static int __devinit snd_audiopci_probe(struct pci_dev *pci,
+					const struct pci_device_id *pci_id)
+{
+	static int dev;
+	struct snd_card *card;
+	struct ensoniq *ensoniq;
+	int err, pcm_devs[2];
+
+	if (dev >= SNDRV_CARDS)
+		return -ENODEV;
+	if (!enable[dev]) {
+		dev++;
+		return -ENOENT;
+	}
+
+	card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+	if (card == NULL)
+		return -ENOMEM;
+
+	if ((err = snd_ensoniq_create(card, pci, &ensoniq)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	card->private_data = ensoniq;
+
+	pcm_devs[0] = 0; pcm_devs[1] = 1;
+#ifdef CHIP1370
+	if ((err = snd_ensoniq_1370_mixer(ensoniq)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+#endif
+#ifdef CHIP1371
+	if ((err = snd_ensoniq_1371_mixer(ensoniq, spdif[dev], lineio[dev])) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+#endif
+	if ((err = snd_ensoniq_pcm(ensoniq, 0, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_ensoniq_pcm2(ensoniq, 1, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_ensoniq_midi(ensoniq, 0, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	snd_ensoniq_create_gameport(ensoniq, dev);
+
+	strcpy(card->driver, DRIVER_NAME);
+
+	strcpy(card->shortname, "Ensoniq AudioPCI");
+	sprintf(card->longname, "%s %s at 0x%lx, irq %i",
+		card->shortname,
+		card->driver,
+		ensoniq->port,
+		ensoniq->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_audiopci_remove(struct pci_dev *pci)
+{
+	snd_card_free(pci_get_drvdata(pci));
+	pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver driver = {
+	.name = DRIVER_NAME,
+	.id_table = snd_audiopci_ids,
+	.probe = snd_audiopci_probe,
+	.remove = snd_audiopci_remove,
+#ifdef CONFIG_PM
+	.suspend = snd_ensoniq_suspend,
+	.resume = snd_ensoniq_resume,
+#endif
+};
+	
+static int __init alsa_card_ens137x_init(void)
+{
+	return pci_register_driver(&driver);
+}
+
+static void __exit alsa_card_ens137x_exit(void)
+{
+	pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_ens137x_init)
+module_exit(alsa_card_ens137x_exit)
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/ens1371.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/ens1371.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/ens1371.c	(revision 91)
@@ -0,0 +1,2 @@
+#define CHIP1371
+#include "ens1370.c"
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/es1938.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/es1938.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/es1938.c	(revision 91)
@@ -0,0 +1,1802 @@
+/*
+ *  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>,
+ *                   Abramo Bagnara <abramo@alsa-project.org>,
+ *                   Markus Gruber <gruber@eikon.tum.de>
+ * 
+ * Rewritten from sonicvibes.c source.
+ *
+ *  TODO:
+ *    Rewrite better spinlocks
+ *
+ *
+ *   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
+ *
+ */
+
+/*
+  NOTES:
+  - Capture data is written unaligned starting from dma_base + 1 so I need to
+    disable mmap and to add a copy callback.
+  - After several cycle of the following:
+    while : ; do arecord -d1 -f cd -t raw | aplay -f cd ; done
+    a "playback write error (DMA or IRQ trouble?)" may happen.
+    This is due to playback interrupts not generated.
+    I suspect a timing issue.
+  - Sometimes the interrupt handler is invoked wrongly during playback.
+    This generates some harmless "Unexpected hw_pointer: wrong interrupt
+    acknowledge".
+    I've seen that using small period sizes.
+    Reproducible with:
+    mpg123 test.mp3 &
+    hdparm -t -T /dev/hda
+*/
+
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+//#include <linux/gameport.h>
+#include <linux/moduleparam.h>
+#include <linux/delay.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/opl3.h>
+#include <sound/mpu401.h>
+#include <sound/initval.h>
+
+#include <asm/io.h>
+
+MODULE_AUTHOR("Jaromir Koutek <miri@punknet.cz>");
+MODULE_DESCRIPTION("ESS Solo-1");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{ESS,ES1938},"
+                "{ESS,ES1946},"
+                "{ESS,ES1969},"
+		"{TerraTec,128i PCI}}");
+
+#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
+#define SUPPORT_JOYSTICK 1
+#endif
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;	/* Enable this card */
+
+//module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for ESS Solo-1 soundcard.");
+//module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for ESS Solo-1 soundcard.");
+//module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable ESS Solo-1 soundcard.");
+
+#define SLIO_REG(chip, x) ((chip)->io_port + ESSIO_REG_##x)
+
+#define SLDM_REG(chip, x) ((chip)->ddma_port + ESSDM_REG_##x)
+
+#define SLSB_REG(chip, x) ((chip)->sb_port + ESSSB_REG_##x)
+
+#define SL_PCI_LEGACYCONTROL		0x40
+#define SL_PCI_CONFIG			0x50
+#define SL_PCI_DDMACONTROL		0x60
+
+#define ESSIO_REG_AUDIO2DMAADDR		0
+#define ESSIO_REG_AUDIO2DMACOUNT	4
+#define ESSIO_REG_AUDIO2MODE		6
+#define ESSIO_REG_IRQCONTROL		7
+
+#define ESSDM_REG_DMAADDR		0x00
+#define ESSDM_REG_DMACOUNT		0x04
+#define ESSDM_REG_DMACOMMAND		0x08
+#define ESSDM_REG_DMASTATUS		0x08
+#define ESSDM_REG_DMAMODE		0x0b
+#define ESSDM_REG_DMACLEAR		0x0d
+#define ESSDM_REG_DMAMASK		0x0f
+
+#define ESSSB_REG_FMLOWADDR		0x00
+#define ESSSB_REG_FMHIGHADDR		0x02
+#define ESSSB_REG_MIXERADDR		0x04
+#define ESSSB_REG_MIXERDATA		0x05
+
+#define ESSSB_IREG_AUDIO1		0x14
+#define ESSSB_IREG_MICMIX		0x1a
+#define ESSSB_IREG_RECSRC		0x1c
+#define ESSSB_IREG_MASTER		0x32
+#define ESSSB_IREG_FM			0x36
+#define ESSSB_IREG_AUXACD		0x38
+#define ESSSB_IREG_AUXB			0x3a
+#define ESSSB_IREG_PCSPEAKER		0x3c
+#define ESSSB_IREG_LINE			0x3e
+#define ESSSB_IREG_SPATCONTROL		0x50
+#define ESSSB_IREG_SPATLEVEL		0x52
+#define ESSSB_IREG_MASTER_LEFT		0x60
+#define ESSSB_IREG_MASTER_RIGHT		0x62
+#define ESSSB_IREG_MPU401CONTROL	0x64
+#define ESSSB_IREG_MICMIXRECORD		0x68
+#define ESSSB_IREG_AUDIO2RECORD		0x69
+#define ESSSB_IREG_AUXACDRECORD		0x6a
+#define ESSSB_IREG_FMRECORD		0x6b
+#define ESSSB_IREG_AUXBRECORD		0x6c
+#define ESSSB_IREG_MONO			0x6d
+#define ESSSB_IREG_LINERECORD		0x6e
+#define ESSSB_IREG_MONORECORD		0x6f
+#define ESSSB_IREG_AUDIO2SAMPLE		0x70
+#define ESSSB_IREG_AUDIO2MODE		0x71
+#define ESSSB_IREG_AUDIO2FILTER		0x72
+#define ESSSB_IREG_AUDIO2TCOUNTL	0x74
+#define ESSSB_IREG_AUDIO2TCOUNTH	0x76
+#define ESSSB_IREG_AUDIO2CONTROL1	0x78
+#define ESSSB_IREG_AUDIO2CONTROL2	0x7a
+#define ESSSB_IREG_AUDIO2		0x7c
+
+#define ESSSB_REG_RESET			0x06
+
+#define ESSSB_REG_READDATA		0x0a
+#define ESSSB_REG_WRITEDATA		0x0c
+#define ESSSB_REG_READSTATUS		0x0c
+
+#define ESSSB_REG_STATUS		0x0e
+
+#define ESS_CMD_EXTSAMPLERATE		0xa1
+#define ESS_CMD_FILTERDIV		0xa2
+#define ESS_CMD_DMACNTRELOADL		0xa4
+#define ESS_CMD_DMACNTRELOADH		0xa5
+#define ESS_CMD_ANALOGCONTROL		0xa8
+#define ESS_CMD_IRQCONTROL		0xb1
+#define ESS_CMD_DRQCONTROL		0xb2
+#define ESS_CMD_RECLEVEL		0xb4
+#define ESS_CMD_SETFORMAT		0xb6
+#define ESS_CMD_SETFORMAT2		0xb7
+#define ESS_CMD_DMACONTROL		0xb8
+#define ESS_CMD_DMATYPE			0xb9
+#define ESS_CMD_OFFSETLEFT		0xba	
+#define ESS_CMD_OFFSETRIGHT		0xbb
+#define ESS_CMD_READREG			0xc0
+#define ESS_CMD_ENABLEEXT		0xc6
+#define ESS_CMD_PAUSEDMA		0xd0
+#define ESS_CMD_ENABLEAUDIO1		0xd1
+#define ESS_CMD_STOPAUDIO1		0xd3
+#define ESS_CMD_AUDIO1STATUS		0xd8
+#define ESS_CMD_CONTDMA			0xd4
+#define ESS_CMD_TESTIRQ			0xf2
+
+#define ESS_RECSRC_MIC		0
+#define ESS_RECSRC_AUXACD	2
+#define ESS_RECSRC_AUXB		5
+#define ESS_RECSRC_LINE		6
+#define ESS_RECSRC_NONE		7
+
+#define DAC1 0x01
+#define ADC1 0x02
+#define DAC2 0x04
+
+/*
+
+ */
+
+#define SAVED_REG_SIZE	32 /* max. number of registers to save */
+
+struct es1938 {
+	int irq;
+
+	unsigned long io_port;
+	unsigned long sb_port;
+	unsigned long vc_port;
+	unsigned long mpu_port;
+	unsigned long game_port;
+	unsigned long ddma_port;
+
+	unsigned char irqmask;
+	unsigned char revision;
+
+	struct snd_kcontrol *hw_volume;
+	struct snd_kcontrol *hw_switch;
+	struct snd_kcontrol *master_volume;
+	struct snd_kcontrol *master_switch;
+
+	struct pci_dev *pci;
+	struct snd_card *card;
+	struct snd_pcm *pcm;
+	struct snd_pcm_substream *capture_substream;
+	struct snd_pcm_substream *playback1_substream;
+	struct snd_pcm_substream *playback2_substream;
+	struct snd_rawmidi *rmidi;
+
+	unsigned int dma1_size;
+	unsigned int dma2_size;
+	unsigned int dma1_start;
+	unsigned int dma2_start;
+	unsigned int dma1_shift;
+	unsigned int dma2_shift;
+	unsigned int active;
+
+	spinlock_t reg_lock;
+	spinlock_t mixer_lock;
+        struct snd_info_entry *proc_entry;
+
+#ifdef SUPPORT_JOYSTICK
+	struct gameport *gameport;
+#endif
+#ifdef CONFIG_PM
+	unsigned char saved_regs[SAVED_REG_SIZE];
+#endif
+};
+
+static irqreturn_t snd_es1938_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+
+static struct pci_device_id snd_es1938_ids[] = {
+        { 0x125d, 0x1969, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },   /* Solo-1 */
+	{ 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, snd_es1938_ids);
+
+#define RESET_LOOP_TIMEOUT	0x10000
+#define WRITE_LOOP_TIMEOUT	0x10000
+#define GET_LOOP_TIMEOUT	0x01000
+
+#undef REG_DEBUG
+/* -----------------------------------------------------------------
+ * Write to a mixer register
+ * -----------------------------------------------------------------*/
+static void snd_es1938_mixer_write(struct es1938 *chip, unsigned char reg, unsigned char val)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&chip->mixer_lock, flags);
+	outb(reg, SLSB_REG(chip, MIXERADDR));
+	outb(val, SLSB_REG(chip, MIXERDATA));
+	spin_unlock_irqrestore(&chip->mixer_lock, flags);
+#ifdef REG_DEBUG
+	snd_printk(KERN_DEBUG "Mixer reg %02x set to %02x\n", reg, val);
+#endif
+}
+
+/* -----------------------------------------------------------------
+ * Read from a mixer register
+ * -----------------------------------------------------------------*/
+static int snd_es1938_mixer_read(struct es1938 *chip, unsigned char reg)
+{
+	int data;
+	unsigned long flags;
+	spin_lock_irqsave(&chip->mixer_lock, flags);
+	outb(reg, SLSB_REG(chip, MIXERADDR));
+	data = inb(SLSB_REG(chip, MIXERDATA));
+	spin_unlock_irqrestore(&chip->mixer_lock, flags);
+#ifdef REG_DEBUG
+	snd_printk(KERN_DEBUG "Mixer reg %02x now is %02x\n", reg, data);
+#endif
+	return data;
+}
+
+/* -----------------------------------------------------------------
+ * Write to some bits of a mixer register (return old value)
+ * -----------------------------------------------------------------*/
+static int snd_es1938_mixer_bits(struct es1938 *chip, unsigned char reg,
+				 unsigned char mask, unsigned char val)
+{
+	unsigned long flags;
+	unsigned char old, new, oval;
+	spin_lock_irqsave(&chip->mixer_lock, flags);
+	outb(reg, SLSB_REG(chip, MIXERADDR));
+	old = inb(SLSB_REG(chip, MIXERDATA));
+	oval = old & mask;
+	if (val != oval) {
+		new = (old & ~mask) | (val & mask);
+		outb(new, SLSB_REG(chip, MIXERDATA));
+#ifdef REG_DEBUG
+		snd_printk(KERN_DEBUG "Mixer reg %02x was %02x, set to %02x\n",
+			   reg, old, new);
+#endif
+	}
+	spin_unlock_irqrestore(&chip->mixer_lock, flags);
+	return oval;
+}
+
+/* -----------------------------------------------------------------
+ * Write command to Controller Registers
+ * -----------------------------------------------------------------*/
+static void snd_es1938_write_cmd(struct es1938 *chip, unsigned char cmd)
+{
+	int i;
+	unsigned char v;
+	for (i = 0; i < WRITE_LOOP_TIMEOUT; i++) {
+		if (!(v = inb(SLSB_REG(chip, READSTATUS)) & 0x80)) {
+			outb(cmd, SLSB_REG(chip, WRITEDATA));
+			return;
+		}
+	}
+	printk(KERN_ERR "snd_es1938_write_cmd timeout (0x02%x/0x02%x)\n", cmd, v);
+}
+
+/* -----------------------------------------------------------------
+ * Read the Read Data Buffer
+ * -----------------------------------------------------------------*/
+static int snd_es1938_get_byte(struct es1938 *chip)
+{
+	int i;
+	unsigned char v;
+	for (i = GET_LOOP_TIMEOUT; i; i--)
+		if ((v = inb(SLSB_REG(chip, STATUS))) & 0x80)
+			return inb(SLSB_REG(chip, READDATA));
+	snd_printk(KERN_ERR "get_byte timeout: status 0x02%x\n", v);
+	return -ENODEV;
+}
+
+/* -----------------------------------------------------------------
+ * Write value cmd register
+ * -----------------------------------------------------------------*/
+static void snd_es1938_write(struct es1938 *chip, unsigned char reg, unsigned char val)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_es1938_write_cmd(chip, reg);
+	snd_es1938_write_cmd(chip, val);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+#ifdef REG_DEBUG
+	snd_printk(KERN_DEBUG "Reg %02x set to %02x\n", reg, val);
+#endif
+}
+
+/* -----------------------------------------------------------------
+ * Read data from cmd register and return it
+ * -----------------------------------------------------------------*/
+static unsigned char snd_es1938_read(struct es1938 *chip, unsigned char reg)
+{
+	unsigned char val;
+	unsigned long flags;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_es1938_write_cmd(chip, ESS_CMD_READREG);
+	snd_es1938_write_cmd(chip, reg);
+	val = snd_es1938_get_byte(chip);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+#ifdef REG_DEBUG
+	snd_printk(KERN_DEBUG "Reg %02x now is %02x\n", reg, val);
+#endif
+	return val;
+}
+
+/* -----------------------------------------------------------------
+ * Write data to cmd register and return old value
+ * -----------------------------------------------------------------*/
+static int snd_es1938_bits(struct es1938 *chip, unsigned char reg, unsigned char mask,
+			   unsigned char val)
+{
+	unsigned long flags;
+	unsigned char old, new, oval;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_es1938_write_cmd(chip, ESS_CMD_READREG);
+	snd_es1938_write_cmd(chip, reg);
+	old = snd_es1938_get_byte(chip);
+	oval = old & mask;
+	if (val != oval) {
+		snd_es1938_write_cmd(chip, reg);
+		new = (old & ~mask) | (val & mask);
+		snd_es1938_write_cmd(chip, new);
+#ifdef REG_DEBUG
+		snd_printk(KERN_DEBUG "Reg %02x was %02x, set to %02x\n",
+			   reg, old, new);
+#endif
+	}
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return oval;
+}
+
+/* --------------------------------------------------------------------
+ * Reset the chip
+ * --------------------------------------------------------------------*/
+static void snd_es1938_reset(struct es1938 *chip)
+{
+	int i;
+
+	outb(3, SLSB_REG(chip, RESET));
+	inb(SLSB_REG(chip, RESET));
+	outb(0, SLSB_REG(chip, RESET));
+	for (i = 0; i < RESET_LOOP_TIMEOUT; i++) {
+		if (inb(SLSB_REG(chip, STATUS)) & 0x80) {
+			if (inb(SLSB_REG(chip, READDATA)) == 0xaa)
+				goto __next;
+		}
+	}
+	snd_printk(KERN_ERR "ESS Solo-1 reset failed\n");
+
+     __next:
+	snd_es1938_write_cmd(chip, ESS_CMD_ENABLEEXT);
+
+	/* Demand transfer DMA: 4 bytes per DMA request */
+	snd_es1938_write(chip, ESS_CMD_DMATYPE, 2);
+
+	/* Change behaviour of register A1
+	   4x oversampling
+	   2nd channel DAC asynchronous */                                                      
+	snd_es1938_mixer_write(chip, ESSSB_IREG_AUDIO2MODE, 0x32);
+	/* enable/select DMA channel and IRQ channel */
+	snd_es1938_bits(chip, ESS_CMD_IRQCONTROL, 0xf0, 0x50);
+	snd_es1938_bits(chip, ESS_CMD_DRQCONTROL, 0xf0, 0x50);
+	snd_es1938_write_cmd(chip, ESS_CMD_ENABLEAUDIO1);
+	/* Set spatializer parameters to recommended values */
+	snd_es1938_mixer_write(chip, 0x54, 0x8f);
+	snd_es1938_mixer_write(chip, 0x56, 0x95);
+	snd_es1938_mixer_write(chip, 0x58, 0x94);
+	snd_es1938_mixer_write(chip, 0x5a, 0x80);
+}
+
+/* --------------------------------------------------------------------
+ * Reset the FIFOs
+ * --------------------------------------------------------------------*/
+static void snd_es1938_reset_fifo(struct es1938 *chip)
+{
+	outb(2, SLSB_REG(chip, RESET));
+	outb(0, SLSB_REG(chip, RESET));
+}
+
+static struct snd_ratnum clocks[2] = {
+	{
+		.num = 793800,
+		.den_min = 1,
+		.den_max = 128,
+		.den_step = 1,
+	},
+	{
+		.num = 768000,
+		.den_min = 1,
+		.den_max = 128,
+		.den_step = 1,
+	}
+};
+
+static struct snd_pcm_hw_constraint_ratnums hw_constraints_clocks = {
+	.nrats = 2,
+	.rats = clocks,
+};
+
+
+static void snd_es1938_rate_set(struct es1938 *chip, 
+				struct snd_pcm_substream *substream,
+				int mode)
+{
+	unsigned int bits, div0;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	if (runtime->rate_num == clocks[0].num)
+		bits = 128 - runtime->rate_den;
+	else
+		bits = 256 - runtime->rate_den;
+
+	/* set filter register */
+	div0 = 256 - 7160000*20/(8*82*runtime->rate);
+		
+	if (mode == DAC2) {
+		snd_es1938_mixer_write(chip, 0x70, bits);
+		snd_es1938_mixer_write(chip, 0x72, div0);
+	} else {
+		snd_es1938_write(chip, 0xA1, bits);
+		snd_es1938_write(chip, 0xA2, div0);
+	}
+}
+
+/* --------------------------------------------------------------------
+ * Configure Solo1 builtin DMA Controller
+ * --------------------------------------------------------------------*/
+
+static void snd_es1938_playback1_setdma(struct es1938 *chip)
+{
+	outb(0x00, SLIO_REG(chip, AUDIO2MODE));
+	outl(chip->dma2_start, SLIO_REG(chip, AUDIO2DMAADDR));
+	outw(0, SLIO_REG(chip, AUDIO2DMACOUNT));
+	outw(chip->dma2_size, SLIO_REG(chip, AUDIO2DMACOUNT));
+}
+
+static void snd_es1938_playback2_setdma(struct es1938 *chip)
+{
+	/* Enable DMA controller */
+	outb(0xc4, SLDM_REG(chip, DMACOMMAND));
+	/* 1. Master reset */
+	outb(0, SLDM_REG(chip, DMACLEAR));
+	/* 2. Mask DMA */
+	outb(1, SLDM_REG(chip, DMAMASK));
+	outb(0x18, SLDM_REG(chip, DMAMODE));
+	outl(chip->dma1_start, SLDM_REG(chip, DMAADDR));
+	outw(chip->dma1_size - 1, SLDM_REG(chip, DMACOUNT));
+	/* 3. Unmask DMA */
+	outb(0, SLDM_REG(chip, DMAMASK));
+}
+
+static void snd_es1938_capture_setdma(struct es1938 *chip)
+{
+	/* Enable DMA controller */
+	outb(0xc4, SLDM_REG(chip, DMACOMMAND));
+	/* 1. Master reset */
+	outb(0, SLDM_REG(chip, DMACLEAR));
+	/* 2. Mask DMA */
+	outb(1, SLDM_REG(chip, DMAMASK));
+	outb(0x14, SLDM_REG(chip, DMAMODE));
+	outl(chip->dma1_start, SLDM_REG(chip, DMAADDR));
+	outw(chip->dma1_size - 1, SLDM_REG(chip, DMACOUNT));
+	/* 3. Unmask DMA */
+	outb(0, SLDM_REG(chip, DMAMASK));
+}
+
+/* ----------------------------------------------------------------------
+ *
+ *                           *** PCM part ***
+ */
+
+static int snd_es1938_capture_trigger(struct snd_pcm_substream *substream,
+				      int cmd)
+{
+	struct es1938 *chip = snd_pcm_substream_chip(substream);
+	int val;
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		val = 0x0f;
+		chip->active |= ADC1;
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		val = 0x00;
+		chip->active &= ~ADC1;
+		break;
+	default:
+		return -EINVAL;
+	}
+	snd_es1938_write(chip, ESS_CMD_DMACONTROL, val);
+	return 0;
+}
+
+static int snd_es1938_playback1_trigger(struct snd_pcm_substream *substream,
+					int cmd)
+{
+	struct es1938 *chip = snd_pcm_substream_chip(substream);
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		/* According to the documentation this should be:
+		   0x13 but that value may randomly swap stereo channels */
+                snd_es1938_mixer_write(chip, ESSSB_IREG_AUDIO2CONTROL1, 0x92);
+                udelay(10);
+		snd_es1938_mixer_write(chip, ESSSB_IREG_AUDIO2CONTROL1, 0x93);
+                /* This two stage init gives the FIFO -> DAC connection time to
+                 * settle before first data from DMA flows in.  This should ensure
+                 * no swapping of stereo channels.  Report a bug if otherwise :-) */
+		outb(0x0a, SLIO_REG(chip, AUDIO2MODE));
+		chip->active |= DAC2;
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		outb(0, SLIO_REG(chip, AUDIO2MODE));
+		snd_es1938_mixer_write(chip, ESSSB_IREG_AUDIO2CONTROL1, 0);
+		chip->active &= ~DAC2;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int snd_es1938_playback2_trigger(struct snd_pcm_substream *substream,
+					int cmd)
+{
+	struct es1938 *chip = snd_pcm_substream_chip(substream);
+	int val;
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		val = 5;
+		chip->active |= DAC1;
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		val = 0;
+		chip->active &= ~DAC1;
+		break;
+	default:
+		return -EINVAL;
+	}
+	snd_es1938_write(chip, ESS_CMD_DMACONTROL, val);
+	return 0;
+}
+
+static int snd_es1938_playback_trigger(struct snd_pcm_substream *substream,
+				       int cmd)
+{
+	switch (substream->number) {
+	case 0:
+		return snd_es1938_playback1_trigger(substream, cmd);
+	case 1:
+		return snd_es1938_playback2_trigger(substream, cmd);
+	}
+	snd_BUG();
+	return -EINVAL;
+}
+
+/* --------------------------------------------------------------------
+ * First channel for Extended Mode Audio 1 ADC Operation
+ * --------------------------------------------------------------------*/
+static int snd_es1938_capture_prepare(struct snd_pcm_substream *substream)
+{
+	struct es1938 *chip = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	int u, is8, mono;
+	unsigned int size = snd_pcm_lib_buffer_bytes(substream);
+	unsigned int count = snd_pcm_lib_period_bytes(substream);
+
+	chip->dma1_size = size;
+	chip->dma1_start = runtime->dma_addr;
+
+	mono = (runtime->channels > 1) ? 0 : 1;
+	is8 = snd_pcm_format_width(runtime->format) == 16 ? 0 : 1;
+	u = snd_pcm_format_unsigned(runtime->format);
+
+	chip->dma1_shift = 2 - mono - is8;
+
+	snd_es1938_reset_fifo(chip);
+	
+	/* program type */
+	snd_es1938_bits(chip, ESS_CMD_ANALOGCONTROL, 0x03, (mono ? 2 : 1));
+
+	/* set clock and counters */
+        snd_es1938_rate_set(chip, substream, ADC1);
+
+	count = 0x10000 - count;
+	snd_es1938_write(chip, ESS_CMD_DMACNTRELOADL, count & 0xff);
+	snd_es1938_write(chip, ESS_CMD_DMACNTRELOADH, count >> 8);
+
+	/* initialize and configure ADC */
+	snd_es1938_write(chip, ESS_CMD_SETFORMAT2, u ? 0x51 : 0x71);
+	snd_es1938_write(chip, ESS_CMD_SETFORMAT2, 0x90 | 
+		       (u ? 0x00 : 0x20) | 
+		       (is8 ? 0x00 : 0x04) | 
+		       (mono ? 0x40 : 0x08));
+
+	//	snd_es1938_reset_fifo(chip);	
+
+	/* 11. configure system interrupt controller and DMA controller */
+	snd_es1938_capture_setdma(chip);
+
+	return 0;
+}
+
+
+/* ------------------------------------------------------------------------------
+ * Second Audio channel DAC Operation
+ * ------------------------------------------------------------------------------*/
+static int snd_es1938_playback1_prepare(struct snd_pcm_substream *substream)
+{
+	struct es1938 *chip = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	int u, is8, mono;
+	unsigned int size = snd_pcm_lib_buffer_bytes(substream);
+	unsigned int count = snd_pcm_lib_period_bytes(substream);
+
+	chip->dma2_size = size;
+	chip->dma2_start = runtime->dma_addr;
+
+	mono = (runtime->channels > 1) ? 0 : 1;
+	is8 = snd_pcm_format_width(runtime->format) == 16 ? 0 : 1;
+	u = snd_pcm_format_unsigned(runtime->format);
+
+	chip->dma2_shift = 2 - mono - is8;
+
+        snd_es1938_reset_fifo(chip);
+
+	/* set clock and counters */
+        snd_es1938_rate_set(chip, substream, DAC2);
+
+	count >>= 1;
+	count = 0x10000 - count;
+	snd_es1938_mixer_write(chip, ESSSB_IREG_AUDIO2TCOUNTL, count & 0xff);
+	snd_es1938_mixer_write(chip, ESSSB_IREG_AUDIO2TCOUNTH, count >> 8);
+
+	/* initialize and configure Audio 2 DAC */
+	snd_es1938_mixer_write(chip, ESSSB_IREG_AUDIO2CONTROL2, 0x40 | (u ? 0 : 4) |
+			       (mono ? 0 : 2) | (is8 ? 0 : 1));
+
+	/* program DMA */
+	snd_es1938_playback1_setdma(chip);
+	
+	return 0;
+}
+
+static int snd_es1938_playback2_prepare(struct snd_pcm_substream *substream)
+{
+	struct es1938 *chip = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	int u, is8, mono;
+	unsigned int size = snd_pcm_lib_buffer_bytes(substream);
+	unsigned int count = snd_pcm_lib_period_bytes(substream);
+
+	chip->dma1_size = size;
+	chip->dma1_start = runtime->dma_addr;
+
+	mono = (runtime->channels > 1) ? 0 : 1;
+	is8 = snd_pcm_format_width(runtime->format) == 16 ? 0 : 1;
+	u = snd_pcm_format_unsigned(runtime->format);
+
+	chip->dma1_shift = 2 - mono - is8;
+
+	count = 0x10000 - count;
+ 
+	/* reset */
+	snd_es1938_reset_fifo(chip);
+	
+	snd_es1938_bits(chip, ESS_CMD_ANALOGCONTROL, 0x03, (mono ? 2 : 1));
+
+	/* set clock and counters */
+        snd_es1938_rate_set(chip, substream, DAC1);
+	snd_es1938_write(chip, ESS_CMD_DMACNTRELOADL, count & 0xff);
+	snd_es1938_write(chip, ESS_CMD_DMACNTRELOADH, count >> 8);
+
+	/* initialized and configure DAC */
+        snd_es1938_write(chip, ESS_CMD_SETFORMAT, u ? 0x80 : 0x00);
+        snd_es1938_write(chip, ESS_CMD_SETFORMAT, u ? 0x51 : 0x71);
+        snd_es1938_write(chip, ESS_CMD_SETFORMAT2, 
+			 0x90 | (mono ? 0x40 : 0x08) |
+			 (is8 ? 0x00 : 0x04) | (u ? 0x00 : 0x20));
+
+	/* program DMA */
+	snd_es1938_playback2_setdma(chip);
+	
+	return 0;
+}
+
+static int snd_es1938_playback_prepare(struct snd_pcm_substream *substream)
+{
+	switch (substream->number) {
+	case 0:
+		return snd_es1938_playback1_prepare(substream);
+	case 1:
+		return snd_es1938_playback2_prepare(substream);
+	}
+	snd_BUG();
+	return -EINVAL;
+}
+
+static snd_pcm_uframes_t snd_es1938_capture_pointer(struct snd_pcm_substream *substream)
+{
+	struct es1938 *chip = snd_pcm_substream_chip(substream);
+	size_t ptr;
+	size_t old, new;
+#if 1
+	/* This stuff is *needed*, don't ask why - AB */
+	old = inw(SLDM_REG(chip, DMACOUNT));
+	while ((new = inw(SLDM_REG(chip, DMACOUNT))) != old)
+		old = new;
+	ptr = chip->dma1_size - 1 - new;
+#else
+	ptr = inl(SLDM_REG(chip, DMAADDR)) - chip->dma1_start;
+#endif
+	return ptr >> chip->dma1_shift;
+}
+
+static snd_pcm_uframes_t snd_es1938_playback1_pointer(struct snd_pcm_substream *substream)
+{
+	struct es1938 *chip = snd_pcm_substream_chip(substream);
+	size_t ptr;
+#if 1
+	ptr = chip->dma2_size - inw(SLIO_REG(chip, AUDIO2DMACOUNT));
+#else
+	ptr = inl(SLIO_REG(chip, AUDIO2DMAADDR)) - chip->dma2_start;
+#endif
+	return ptr >> chip->dma2_shift;
+}
+
+static snd_pcm_uframes_t snd_es1938_playback2_pointer(struct snd_pcm_substream *substream)
+{
+	struct es1938 *chip = snd_pcm_substream_chip(substream);
+	size_t ptr;
+	size_t old, new;
+#if 1
+	/* This stuff is *needed*, don't ask why - AB */
+	old = inw(SLDM_REG(chip, DMACOUNT));
+	while ((new = inw(SLDM_REG(chip, DMACOUNT))) != old)
+		old = new;
+	ptr = chip->dma1_size - 1 - new;
+#else
+	ptr = inl(SLDM_REG(chip, DMAADDR)) - chip->dma1_start;
+#endif
+	return ptr >> chip->dma1_shift;
+}
+
+static snd_pcm_uframes_t snd_es1938_playback_pointer(struct snd_pcm_substream *substream)
+{
+	switch (substream->number) {
+	case 0:
+		return snd_es1938_playback1_pointer(substream);
+	case 1:
+		return snd_es1938_playback2_pointer(substream);
+	}
+	snd_BUG();
+	return -EINVAL;
+}
+
+static int snd_es1938_capture_copy(struct snd_pcm_substream *substream,
+				   int channel,
+				   snd_pcm_uframes_t pos,
+				   void __user *dst,
+				   snd_pcm_uframes_t count)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct es1938 *chip = snd_pcm_substream_chip(substream);
+	pos <<= chip->dma1_shift;
+	count <<= chip->dma1_shift;
+	snd_assert(pos + count <= chip->dma1_size, return -EINVAL);
+	if (pos + count < chip->dma1_size) {
+		if (copy_to_user(dst, runtime->dma_area + pos + 1, count))
+			return -EFAULT;
+	} else {
+		if (copy_to_user(dst, runtime->dma_area + pos + 1, count - 1))
+			return -EFAULT;
+		if (put_user(runtime->dma_area[0], ((unsigned char __user *)dst) + count - 1))
+			return -EFAULT;
+	}
+	return 0;
+}
+
+/*
+ * buffer management
+ */
+static int snd_es1938_pcm_hw_params(struct snd_pcm_substream *substream,
+				    struct snd_pcm_hw_params *hw_params)
+
+{
+	int err;
+
+	if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
+		return err;
+	return 0;
+}
+
+static int snd_es1938_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+	return snd_pcm_lib_free_pages(substream);
+}
+
+/* ----------------------------------------------------------------------
+ * Audio1 Capture (ADC)
+ * ----------------------------------------------------------------------*/
+static struct snd_pcm_hardware snd_es1938_capture =
+{
+	.info =			(SNDRV_PCM_INFO_INTERLEAVED |
+				SNDRV_PCM_INFO_BLOCK_TRANSFER),
+	.formats =		(SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE |
+				 SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE),
+	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	.rate_min =		6000,
+	.rate_max =		48000,
+	.channels_min =		1,
+	.channels_max =		2,
+        .buffer_bytes_max =	0x8000,       /* DMA controller screws on higher values */
+	.period_bytes_min =	64,
+	.period_bytes_max =	0x8000,
+	.periods_min =		1,
+	.periods_max =		1024,
+	.fifo_size =		256,
+};
+
+/* -----------------------------------------------------------------------
+ * Audio2 Playback (DAC)
+ * -----------------------------------------------------------------------*/
+static struct snd_pcm_hardware snd_es1938_playback =
+{
+	.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 |
+				 SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE),
+	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	.rate_min =		6000,
+	.rate_max =		48000,
+	.channels_min =		1,
+	.channels_max =		2,
+        .buffer_bytes_max =	0x8000,       /* DMA controller screws on higher values */
+	.period_bytes_min =	64,
+	.period_bytes_max =	0x8000,
+	.periods_min =		1,
+	.periods_max =		1024,
+	.fifo_size =		256,
+};
+
+static int snd_es1938_capture_open(struct snd_pcm_substream *substream)
+{
+	struct es1938 *chip = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+
+	if (chip->playback2_substream)
+		return -EAGAIN;
+	chip->capture_substream = substream;
+	runtime->hw = snd_es1938_capture;
+	snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+				      &hw_constraints_clocks);
+	snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 0, 0xff00);
+	return 0;
+}
+
+static int snd_es1938_playback_open(struct snd_pcm_substream *substream)
+{
+	struct es1938 *chip = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+
+	switch (substream->number) {
+	case 0:
+		chip->playback1_substream = substream;
+		break;
+	case 1:
+		if (chip->capture_substream)
+			return -EAGAIN;
+		chip->playback2_substream = substream;
+		break;
+	default:
+		snd_BUG();
+		return -EINVAL;
+	}
+	runtime->hw = snd_es1938_playback;
+	snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+				      &hw_constraints_clocks);
+	snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 0, 0xff00);
+	return 0;
+}
+
+static int snd_es1938_capture_close(struct snd_pcm_substream *substream)
+{
+	struct es1938 *chip = snd_pcm_substream_chip(substream);
+
+	chip->capture_substream = NULL;
+	return 0;
+}
+
+static int snd_es1938_playback_close(struct snd_pcm_substream *substream)
+{
+	struct es1938 *chip = snd_pcm_substream_chip(substream);
+
+	switch (substream->number) {
+	case 0:
+		chip->playback1_substream = NULL;
+		break;
+	case 1:
+		chip->playback2_substream = NULL;
+		break;
+	default:
+		snd_BUG();
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static struct snd_pcm_ops snd_es1938_playback_ops = {
+	.open =		snd_es1938_playback_open,
+	.close =	snd_es1938_playback_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_es1938_pcm_hw_params,
+	.hw_free =	snd_es1938_pcm_hw_free,
+	.prepare =	snd_es1938_playback_prepare,
+	.trigger =	snd_es1938_playback_trigger,
+	.pointer =	snd_es1938_playback_pointer,
+};
+
+static struct snd_pcm_ops snd_es1938_capture_ops = {
+	.open =		snd_es1938_capture_open,
+	.close =	snd_es1938_capture_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_es1938_pcm_hw_params,
+	.hw_free =	snd_es1938_pcm_hw_free,
+	.prepare =	snd_es1938_capture_prepare,
+	.trigger =	snd_es1938_capture_trigger,
+	.pointer =	snd_es1938_capture_pointer,
+	.copy =		snd_es1938_capture_copy,
+};
+
+static int __devinit snd_es1938_new_pcm(struct es1938 *chip, int device)
+{
+	struct snd_pcm *pcm;
+	int err;
+
+	if ((err = snd_pcm_new(chip->card, "es-1938-1946", device, 2, 1, &pcm)) < 0)
+		return err;
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_es1938_playback_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_es1938_capture_ops);
+	
+	pcm->private_data = chip;
+	pcm->info_flags = 0;
+	strcpy(pcm->name, "ESS Solo-1");
+
+	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+					      snd_dma_pci_data(chip->pci), 64*1024, 64*1024);
+
+	chip->pcm = pcm;
+	return 0;
+}
+
+/* -------------------------------------------------------------------
+ * 
+ *                       *** Mixer part ***
+ */
+
+static int snd_es1938_info_mux(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_info *uinfo)
+{
+	static char *texts[8] = {
+		"Mic", "Mic Master", "CD", "AOUT",
+		"Mic1", "Mix", "Line", "Master"
+	};
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 8;
+	if (uinfo->value.enumerated.item > 7)
+		uinfo->value.enumerated.item = 7;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int snd_es1938_get_mux(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	struct es1938 *chip = snd_kcontrol_chip(kcontrol);
+	ucontrol->value.enumerated.item[0] = snd_es1938_mixer_read(chip, 0x1c) & 0x07;
+	return 0;
+}
+
+static int snd_es1938_put_mux(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	struct es1938 *chip = snd_kcontrol_chip(kcontrol);
+	unsigned char val = ucontrol->value.enumerated.item[0];
+	
+	if (val > 7)
+		return -EINVAL;
+	return snd_es1938_mixer_bits(chip, 0x1c, 0x07, val) != val;
+}
+
+static int snd_es1938_info_spatializer_enable(struct snd_kcontrol *kcontrol,
+					      struct snd_ctl_elem_info *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_es1938_get_spatializer_enable(struct snd_kcontrol *kcontrol,
+					     struct snd_ctl_elem_value *ucontrol)
+{
+	struct es1938 *chip = snd_kcontrol_chip(kcontrol);
+	unsigned char val = snd_es1938_mixer_read(chip, 0x50);
+	ucontrol->value.integer.value[0] = !!(val & 8);
+	return 0;
+}
+
+static int snd_es1938_put_spatializer_enable(struct snd_kcontrol *kcontrol,
+					     struct snd_ctl_elem_value *ucontrol)
+{
+	struct es1938 *chip = snd_kcontrol_chip(kcontrol);
+	unsigned char oval, nval;
+	int change;
+	nval = ucontrol->value.integer.value[0] ? 0x0c : 0x04;
+	oval = snd_es1938_mixer_read(chip, 0x50) & 0x0c;
+	change = nval != oval;
+	if (change) {
+		snd_es1938_mixer_write(chip, 0x50, nval & ~0x04);
+		snd_es1938_mixer_write(chip, 0x50, nval);
+	}
+	return change;
+}
+
+static int snd_es1938_info_hw_volume(struct snd_kcontrol *kcontrol,
+				     struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 63;
+	return 0;
+}
+
+static int snd_es1938_get_hw_volume(struct snd_kcontrol *kcontrol,
+				    struct snd_ctl_elem_value *ucontrol)
+{
+	struct es1938 *chip = snd_kcontrol_chip(kcontrol);
+	ucontrol->value.integer.value[0] = snd_es1938_mixer_read(chip, 0x61) & 0x3f;
+	ucontrol->value.integer.value[1] = snd_es1938_mixer_read(chip, 0x63) & 0x3f;
+	return 0;
+}
+
+static int snd_es1938_info_hw_switch(struct snd_kcontrol *kcontrol,
+				     struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+	return 0;
+}
+
+static int snd_es1938_get_hw_switch(struct snd_kcontrol *kcontrol,
+				    struct snd_ctl_elem_value *ucontrol)
+{
+	struct es1938 *chip = snd_kcontrol_chip(kcontrol);
+	ucontrol->value.integer.value[0] = !(snd_es1938_mixer_read(chip, 0x61) & 0x40);
+	ucontrol->value.integer.value[1] = !(snd_es1938_mixer_read(chip, 0x63) & 0x40);
+	return 0;
+}
+
+static void snd_es1938_hwv_free(struct snd_kcontrol *kcontrol)
+{
+	struct es1938 *chip = snd_kcontrol_chip(kcontrol);
+	chip->master_volume = NULL;
+	chip->master_switch = NULL;
+	chip->hw_volume = NULL;
+	chip->hw_switch = NULL;
+}
+
+static int snd_es1938_reg_bits(struct es1938 *chip, unsigned char reg,
+			       unsigned char mask, unsigned char val)
+{
+	if (reg < 0xa0)
+		return snd_es1938_mixer_bits(chip, reg, mask, val);
+	else
+		return snd_es1938_bits(chip, reg, mask, val);
+}
+
+static int snd_es1938_reg_read(struct es1938 *chip, unsigned char reg)
+{
+	if (reg < 0xa0)
+		return snd_es1938_mixer_read(chip, reg);
+	else
+		return snd_es1938_read(chip, reg);
+}
+
+#define ES1938_SINGLE(xname, xindex, reg, shift, mask, invert) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+  .info = snd_es1938_info_single, \
+  .get = snd_es1938_get_single, .put = snd_es1938_put_single, \
+  .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) }
+
+static int snd_es1938_info_single(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_info *uinfo)
+{
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+
+	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	return 0;
+}
+
+static int snd_es1938_get_single(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol)
+{
+	struct es1938 *chip = snd_kcontrol_chip(kcontrol);
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 8) & 0xff;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0xff;
+	int val;
+	
+	val = snd_es1938_reg_read(chip, reg);
+	ucontrol->value.integer.value[0] = (val >> shift) & mask;
+	if (invert)
+		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+	return 0;
+}
+
+static int snd_es1938_put_single(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol)
+{
+	struct es1938 *chip = snd_kcontrol_chip(kcontrol);
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 8) & 0xff;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0xff;
+	unsigned char val;
+	
+	val = (ucontrol->value.integer.value[0] & mask);
+	if (invert)
+		val = mask - val;
+	mask <<= shift;
+	val <<= shift;
+	return snd_es1938_reg_bits(chip, reg, mask, val) != val;
+}
+
+#define ES1938_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+  .info = snd_es1938_info_double, \
+  .get = snd_es1938_get_double, .put = snd_es1938_put_double, \
+  .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
+
+static int snd_es1938_info_double(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_info *uinfo)
+{
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+
+	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	return 0;
+}
+
+static int snd_es1938_get_double(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol)
+{
+	struct es1938 *chip = snd_kcontrol_chip(kcontrol);
+	int left_reg = kcontrol->private_value & 0xff;
+	int right_reg = (kcontrol->private_value >> 8) & 0xff;
+	int shift_left = (kcontrol->private_value >> 16) & 0x07;
+	int shift_right = (kcontrol->private_value >> 19) & 0x07;
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+	int invert = (kcontrol->private_value >> 22) & 1;
+	unsigned char left, right;
+	
+	left = snd_es1938_reg_read(chip, left_reg);
+	if (left_reg != right_reg)
+		right = snd_es1938_reg_read(chip, right_reg);
+	else
+		right = left;
+	ucontrol->value.integer.value[0] = (left >> shift_left) & mask;
+	ucontrol->value.integer.value[1] = (right >> shift_right) & mask;
+	if (invert) {
+		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+		ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1];
+	}
+	return 0;
+}
+
+static int snd_es1938_put_double(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol)
+{
+	struct es1938 *chip = snd_kcontrol_chip(kcontrol);
+	int left_reg = kcontrol->private_value & 0xff;
+	int right_reg = (kcontrol->private_value >> 8) & 0xff;
+	int shift_left = (kcontrol->private_value >> 16) & 0x07;
+	int shift_right = (kcontrol->private_value >> 19) & 0x07;
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+	int invert = (kcontrol->private_value >> 22) & 1;
+	int change;
+	unsigned char val1, val2, mask1, mask2;
+	
+	val1 = ucontrol->value.integer.value[0] & mask;
+	val2 = ucontrol->value.integer.value[1] & mask;
+	if (invert) {
+		val1 = mask - val1;
+		val2 = mask - val2;
+	}
+	val1 <<= shift_left;
+	val2 <<= shift_right;
+	mask1 = mask << shift_left;
+	mask2 = mask << shift_right;
+	if (left_reg != right_reg) {
+		change = 0;
+		if (snd_es1938_reg_bits(chip, left_reg, mask1, val1) != val1)
+			change = 1;
+		if (snd_es1938_reg_bits(chip, right_reg, mask2, val2) != val2)
+			change = 1;
+	} else {
+		change = (snd_es1938_reg_bits(chip, left_reg, mask1 | mask2, 
+					      val1 | val2) != (val1 | val2));
+	}
+	return change;
+}
+
+static struct snd_kcontrol_new snd_es1938_controls[] = {
+ES1938_DOUBLE("Master Playback Volume", 0, 0x60, 0x62, 0, 0, 63, 0),
+ES1938_DOUBLE("Master Playback Switch", 0, 0x60, 0x62, 6, 6, 1, 1),
+{
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "Hardware Master Playback Volume",
+	.access = SNDRV_CTL_ELEM_ACCESS_READ,
+	.info = snd_es1938_info_hw_volume,
+	.get = snd_es1938_get_hw_volume,
+},
+{
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "Hardware Master Playback Switch",
+	.access = SNDRV_CTL_ELEM_ACCESS_READ,
+	.info = snd_es1938_info_hw_switch,
+	.get = snd_es1938_get_hw_switch,
+},
+ES1938_SINGLE("Hardware Volume Split", 0, 0x64, 7, 1, 0),
+ES1938_DOUBLE("Line Playback Volume", 0, 0x3e, 0x3e, 4, 0, 15, 0),
+ES1938_DOUBLE("CD Playback Volume", 0, 0x38, 0x38, 4, 0, 15, 0),
+ES1938_DOUBLE("FM Playback Volume", 0, 0x36, 0x36, 4, 0, 15, 0),
+ES1938_DOUBLE("Mono Playback Volume", 0, 0x6d, 0x6d, 4, 0, 15, 0),
+ES1938_DOUBLE("Mic Playback Volume", 0, 0x1a, 0x1a, 4, 0, 15, 0),
+ES1938_DOUBLE("Aux Playback Volume", 0, 0x3a, 0x3a, 4, 0, 15, 0),
+ES1938_DOUBLE("Capture Volume", 0, 0xb4, 0xb4, 4, 0, 15, 0),
+ES1938_SINGLE("PC Speaker Volume", 0, 0x3c, 0, 7, 0),
+ES1938_SINGLE("Record Monitor", 0, 0xa8, 3, 1, 0),
+ES1938_SINGLE("Capture Switch", 0, 0x1c, 4, 1, 1),
+{
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "Capture Source",
+	.info = snd_es1938_info_mux,
+	.get = snd_es1938_get_mux,
+	.put = snd_es1938_put_mux,
+},
+ES1938_DOUBLE("Mono Input Playback Volume", 0, 0x6d, 0x6d, 4, 0, 15, 0),
+ES1938_DOUBLE("PCM Capture Volume", 0, 0x69, 0x69, 4, 0, 15, 0),
+ES1938_DOUBLE("Mic Capture Volume", 0, 0x68, 0x68, 4, 0, 15, 0),
+ES1938_DOUBLE("Line Capture Volume", 0, 0x6e, 0x6e, 4, 0, 15, 0),
+ES1938_DOUBLE("FM Capture Volume", 0, 0x6b, 0x6b, 4, 0, 15, 0),
+ES1938_DOUBLE("Mono Capture Volume", 0, 0x6f, 0x6f, 4, 0, 15, 0),
+ES1938_DOUBLE("CD Capture Volume", 0, 0x6a, 0x6a, 4, 0, 15, 0),
+ES1938_DOUBLE("Aux Capture Volume", 0, 0x6c, 0x6c, 4, 0, 15, 0),
+ES1938_DOUBLE("PCM Playback Volume", 0, 0x7c, 0x7c, 4, 0, 15, 0),
+ES1938_DOUBLE("PCM Playback Volume", 1, 0x14, 0x14, 4, 0, 15, 0),
+ES1938_SINGLE("3D Control - Level", 0, 0x52, 0, 63, 0),
+{
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "3D Control - Switch",
+	.info = snd_es1938_info_spatializer_enable,
+	.get = snd_es1938_get_spatializer_enable,
+	.put = snd_es1938_put_spatializer_enable,
+},
+ES1938_SINGLE("Mic Boost (+26dB)", 0, 0x7d, 3, 1, 0)
+};
+
+
+/* ---------------------------------------------------------------------------- */
+/* ---------------------------------------------------------------------------- */
+
+/*
+ * initialize the chip - used by resume callback, too
+ */
+static void snd_es1938_chip_init(struct es1938 *chip)
+{
+	/* reset chip */
+	snd_es1938_reset(chip);
+
+	/* configure native mode */
+
+	/* enable bus master */
+	pci_set_master(chip->pci);
+
+	/* disable legacy audio */
+	pci_write_config_word(chip->pci, SL_PCI_LEGACYCONTROL, 0x805f);
+
+	/* set DDMA base */
+	pci_write_config_word(chip->pci, SL_PCI_DDMACONTROL, chip->ddma_port | 1);
+
+	/* set DMA/IRQ policy */
+	pci_write_config_dword(chip->pci, SL_PCI_CONFIG, 0);
+
+	/* enable Audio 1, Audio 2, MPU401 IRQ and HW volume IRQ*/
+	outb(0xf0, SLIO_REG(chip, IRQCONTROL));
+
+	/* reset DMA */
+	outb(0, SLDM_REG(chip, DMACLEAR));
+}
+
+#ifdef CONFIG_PM
+/*
+ * PM support
+ */
+
+static unsigned char saved_regs[SAVED_REG_SIZE+1] = {
+	0x14, 0x1a, 0x1c, 0x3a, 0x3c, 0x3e, 0x36, 0x38,
+	0x50, 0x52, 0x60, 0x61, 0x62, 0x63, 0x64, 0x68,
+	0x69, 0x6a, 0x6b, 0x6d, 0x6e, 0x6f, 0x7c, 0x7d,
+	0xa8, 0xb4,
+};
+
+
+static int es1938_suspend(struct pci_dev *pci, pm_message_t state)
+{
+	struct snd_card *card = pci_get_drvdata(pci);
+	struct es1938 *chip = card->private_data;
+	unsigned char *s, *d;
+
+	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+	snd_pcm_suspend_all(chip->pcm);
+
+	/* save mixer-related registers */
+	for (s = saved_regs, d = chip->saved_regs; *s; s++, d++)
+		*d = snd_es1938_reg_read(chip, *s);
+
+	outb(0x00, SLIO_REG(chip, IRQCONTROL)); /* disable irqs */
+	if (chip->irq >= 0)
+		free_irq(chip->irq, chip);
+	pci_disable_device(pci);
+	pci_save_state(pci);
+	return 0;
+}
+
+static int es1938_resume(struct pci_dev *pci)
+{
+	struct snd_card *card = pci_get_drvdata(pci);
+	struct es1938 *chip = card->private_data;
+	unsigned char *s, *d;
+
+	pci_restore_state(pci);
+	pci_enable_device(pci);
+	request_irq(pci->irq, snd_es1938_interrupt,
+		    SA_INTERRUPT|SA_SHIRQ, "ES1938", chip);
+	chip->irq = pci->irq;
+	snd_es1938_chip_init(chip);
+
+	/* restore mixer-related registers */
+	for (s = saved_regs, d = chip->saved_regs; *s; s++, d++) {
+		if (*s < 0xa0)
+			snd_es1938_mixer_write(chip, *s, *d);
+		else
+			snd_es1938_write(chip, *s, *d);
+	}
+
+	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+	return 0;
+}
+#endif /* CONFIG_PM */
+
+#ifdef SUPPORT_JOYSTICK
+static int __devinit snd_es1938_create_gameport(struct es1938 *chip)
+{
+	struct gameport *gp;
+
+	chip->gameport = gp = gameport_allocate_port();
+	if (!gp) {
+		printk(KERN_ERR "es1938: cannot allocate memory for gameport\n");
+		return -ENOMEM;
+	}
+
+	gameport_set_name(gp, "ES1938");
+	gameport_set_phys(gp, "pci%s/gameport0", pci_name(chip->pci));
+	gameport_set_dev_parent(gp, &chip->pci->dev);
+	gp->io = chip->game_port;
+
+	gameport_register_port(gp);
+
+	return 0;
+}
+
+static void snd_es1938_free_gameport(struct es1938 *chip)
+{
+	if (chip->gameport) {
+		gameport_unregister_port(chip->gameport);
+		chip->gameport = NULL;
+	}
+}
+#else
+static inline int snd_es1938_create_gameport(struct es1938 *chip) { return -ENOSYS; }
+static inline void snd_es1938_free_gameport(struct es1938 *chip) { }
+#endif /* SUPPORT_JOYSTICK */
+
+static int snd_es1938_free(struct es1938 *chip)
+{
+	/* disable irqs */
+	outb(0x00, SLIO_REG(chip, IRQCONTROL));
+	if (chip->rmidi)
+		snd_es1938_mixer_bits(chip, ESSSB_IREG_MPU401CONTROL, 0x40, 0);
+
+	snd_es1938_free_gameport(chip);
+
+	if (chip->irq >= 0)
+		free_irq(chip->irq, chip);
+	pci_release_regions(chip->pci);
+	pci_disable_device(chip->pci);
+	kfree(chip);
+	return 0;
+}
+
+static int snd_es1938_dev_free(struct snd_device *device)
+{
+	struct es1938 *chip = device->device_data;
+	return snd_es1938_free(chip);
+}
+
+static int __devinit snd_es1938_create(struct snd_card *card,
+				    struct pci_dev * pci,
+				    struct es1938 ** rchip)
+{
+	struct es1938 *chip;
+	int err;
+	static struct snd_device_ops ops = {
+		.dev_free =	snd_es1938_dev_free,
+	};
+
+	*rchip = NULL;
+
+	/* enable PCI device */
+	if ((err = pci_enable_device(pci)) < 0)
+		return err;
+        /* check, if we can restrict PCI DMA transfers to 24 bits */
+        pci_set_dma_mask(pci, 0x00ffffff);
+
+	chip = (struct es1938 *)kzalloc(sizeof(*chip), GFP_KERNEL);
+	if (chip == NULL) {
+		pci_disable_device(pci);
+		return -ENOMEM;
+	}
+	spin_lock_init(&chip->reg_lock);
+	spin_lock_init(&chip->mixer_lock);
+	chip->card = card;
+	chip->pci = pci;
+	if ((err = pci_request_regions(pci, "ESS Solo-1")) < 0) {
+		kfree(chip);
+		pci_disable_device(pci);
+		return err;
+	}
+	chip->io_port = pci_resource_start(pci, 0);
+	chip->sb_port = pci_resource_start(pci, 1);
+	chip->vc_port = pci_resource_start(pci, 2);
+	chip->mpu_port = pci_resource_start(pci, 3);
+	chip->game_port = pci_resource_start(pci, 4);
+	if (request_irq(pci->irq, snd_es1938_interrupt, SA_INTERRUPT|SA_SHIRQ,
+			"ES1938", chip)) {
+		snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
+		snd_es1938_free(chip);
+		return -EBUSY;
+	}
+	chip->irq = pci->irq;
+#ifdef ES1938_DDEBUG
+	snd_printk(KERN_DEBUG "create: io: 0x%lx, sb: 0x%lx, vc: 0x%lx, mpu: 0x%lx, game: 0x%lx\n",
+		   chip->io_port, chip->sb_port, chip->vc_port, chip->mpu_port, chip->game_port);
+#endif
+
+	chip->ddma_port = chip->vc_port + 0x00;		/* fix from Thomas Sailer */
+
+	snd_es1938_chip_init(chip);
+
+	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+		snd_es1938_free(chip);
+		return err;
+	}
+
+	snd_card_set_dev(card, &pci->dev);
+
+	*rchip = chip;
+	return 0;
+}
+
+/* --------------------------------------------------------------------
+ * Interrupt handler
+ * -------------------------------------------------------------------- */
+static irqreturn_t snd_es1938_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct es1938 *chip = dev_id;
+	unsigned char status, audiostatus;
+	int handled = 0;
+
+	status = inb(SLIO_REG(chip, IRQCONTROL));
+#if 0
+	printk("Es1938debug - interrupt status: =0x%x\n", status);
+#endif
+	
+	/* AUDIO 1 */
+	if (status & 0x10) {
+#if 0
+                printk("Es1938debug - AUDIO channel 1 interrupt\n");
+		printk("Es1938debug - AUDIO channel 1 DMAC DMA count: %u\n",
+		       inw(SLDM_REG(chip, DMACOUNT)));
+		printk("Es1938debug - AUDIO channel 1 DMAC DMA base: %u\n",
+		       inl(SLDM_REG(chip, DMAADDR)));
+		printk("Es1938debug - AUDIO channel 1 DMAC DMA status: 0x%x\n",
+		       inl(SLDM_REG(chip, DMASTATUS)));
+#endif
+		/* clear irq */
+		handled = 1;
+		audiostatus = inb(SLSB_REG(chip, STATUS));
+		if (chip->active & ADC1)
+			snd_pcm_period_elapsed(chip->capture_substream);
+		else if (chip->active & DAC1)
+			snd_pcm_period_elapsed(chip->playback2_substream);
+	}
+	
+	/* AUDIO 2 */
+	if (status & 0x20) {
+#if 0
+                printk("Es1938debug - AUDIO channel 2 interrupt\n");
+		printk("Es1938debug - AUDIO channel 2 DMAC DMA count: %u\n",
+		       inw(SLIO_REG(chip, AUDIO2DMACOUNT)));
+		printk("Es1938debug - AUDIO channel 2 DMAC DMA base: %u\n",
+		       inl(SLIO_REG(chip, AUDIO2DMAADDR)));
+
+#endif
+		/* clear irq */
+		handled = 1;
+		snd_es1938_mixer_bits(chip, ESSSB_IREG_AUDIO2CONTROL2, 0x80, 0);
+		if (chip->active & DAC2)
+			snd_pcm_period_elapsed(chip->playback1_substream);
+	}
+
+	/* Hardware volume */
+	if (status & 0x40) {
+		int split = snd_es1938_mixer_read(chip, 0x64) & 0x80;
+		handled = 1;
+		snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->hw_switch->id);
+		snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->hw_volume->id);
+		if (!split) {
+			snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
+				       &chip->master_switch->id);
+			snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
+				       &chip->master_volume->id);
+		}
+		/* ack interrupt */
+		snd_es1938_mixer_write(chip, 0x66, 0x00);
+	}
+
+	/* MPU401 */
+	if (status & 0x80) {
+		// the following line is evil! It switches off MIDI interrupt handling after the first interrupt received.
+		// replacing the last 0 by 0x40 works for ESS-Solo1, but just doing nothing works as well!
+		// andreas@flying-snail.de
+		// snd_es1938_mixer_bits(chip, ESSSB_IREG_MPU401CONTROL, 0x40, 0); /* ack? */
+		if (chip->rmidi) {
+			handled = 1;
+			snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data, regs);
+		}
+	}
+	return IRQ_RETVAL(handled);
+}
+
+#define ES1938_DMA_SIZE 64
+
+static int __devinit snd_es1938_mixer(struct es1938 *chip)
+{
+	struct snd_card *card;
+	unsigned int idx;
+	int err;
+
+	card = chip->card;
+
+	strcpy(card->mixername, "ESS Solo-1");
+
+	for (idx = 0; idx < ARRAY_SIZE(snd_es1938_controls); idx++) {
+		struct snd_kcontrol *kctl;
+		kctl = snd_ctl_new1(&snd_es1938_controls[idx], chip);
+		switch (idx) {
+			case 0:
+				chip->master_volume = kctl;
+				kctl->private_free = snd_es1938_hwv_free;
+				break;
+			case 1:
+				chip->master_switch = kctl;
+				kctl->private_free = snd_es1938_hwv_free;
+				break;
+			case 2:
+				chip->hw_volume = kctl;
+				kctl->private_free = snd_es1938_hwv_free;
+				break;
+			case 3:
+				chip->hw_switch = kctl;
+				kctl->private_free = snd_es1938_hwv_free;
+				break;
+			}
+		if ((err = snd_ctl_add(card, kctl)) < 0)
+			return err;
+	}
+	return 0;
+}
+       
+
+static int __devinit snd_es1938_probe(struct pci_dev *pci,
+				      const struct pci_device_id *pci_id)
+{
+	static int dev;
+	struct snd_card *card;
+	struct es1938 *chip;
+	struct snd_opl3 *opl3;
+	int idx, err;
+
+	if (dev >= SNDRV_CARDS)
+		return -ENODEV;
+	if (!enable[dev]) {
+		dev++;
+		return -ENOENT;
+	}
+
+	card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+	if (card == NULL)
+		return -ENOMEM;
+	for (idx = 0; idx < 5; idx++) {
+		if (pci_resource_start(pci, idx) == 0 ||
+		    !(pci_resource_flags(pci, idx) & IORESOURCE_IO)) {
+		    	snd_card_free(card);
+		    	return -ENODEV;
+		}
+	}
+	if ((err = snd_es1938_create(card, pci, &chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	card->private_data = chip;
+
+	strcpy(card->driver, "ES1938");
+	strcpy(card->shortname, "ESS ES1938 (Solo-1)");
+	sprintf(card->longname, "%s rev %i, irq %i",
+		card->shortname,
+		chip->revision,
+		chip->irq);
+
+	if ((err = snd_es1938_new_pcm(chip, 0)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_es1938_mixer(chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if (snd_opl3_create(card,
+			    SLSB_REG(chip, FMLOWADDR),
+			    SLSB_REG(chip, FMHIGHADDR),
+			    OPL3_HW_OPL3, 1, &opl3) < 0) {
+		printk(KERN_ERR "es1938: OPL3 not detected at 0x%lx\n",
+			   SLSB_REG(chip, FMLOWADDR));
+	} else {
+	        if ((err = snd_opl3_timer_new(opl3, 0, 1)) < 0) {
+	                snd_card_free(card);
+	                return err;
+		}
+	        if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
+	                snd_card_free(card);
+	                return err;
+		}
+	}
+        if (snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
+                                chip->mpu_port, MPU401_INFO_INTEGRATED,
+				chip->irq, 0, &chip->rmidi) < 0) {
+		printk(KERN_ERR "es1938: unable to initialize MPU-401\n");
+	} else {
+		// this line is vital for MIDI interrupt handling on ess-solo1
+		// andreas@flying-snail.de
+		snd_es1938_mixer_bits(chip, ESSSB_IREG_MPU401CONTROL, 0x40, 0x40);
+	}
+
+	snd_es1938_create_gameport(chip);
+
+	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_es1938_remove(struct pci_dev *pci)
+{
+	snd_card_free(pci_get_drvdata(pci));
+	pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver driver = {
+	.name = "ESS ES1938 (Solo-1)",
+	.id_table = snd_es1938_ids,
+	.probe = snd_es1938_probe,
+	.remove = snd_es1938_remove,
+#ifdef CONFIG_PM
+	.suspend = es1938_suspend,
+	.resume = es1938_resume,
+#endif
+};
+
+static int __init alsa_card_es1938_init(void)
+{
+	return pci_register_driver(&driver);
+}
+
+static void __exit alsa_card_es1938_exit(void)
+{
+	pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_es1938_init)
+module_exit(alsa_card_es1938_exit)
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/es1938.old
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/es1938.old	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/es1938.old	(revision 91)
@@ -0,0 +1,1866 @@
+/*
+ *  Driver for ESS Solo-1 (ES1938, ES1946) soundcard
+ *  Copyright (c) by Jaromir Koutek <miri@punknet.cz>,
+ *                   Jaroslav Kysela <perex@suse.cz>,
+ *                   Thomas Sailer <sailer@ife.ee.ethz.ch>,
+ *                   Abramo Bagnara <abramo@alsa-project.org>,
+ *                   Markus Gruber <gruber@eikon.tum.de>
+ * 
+ * Rewritted from sonicvibes.c source.
+ *
+ *  TODO:
+ *    MPU401
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/*
+  NOTES:
+  - Capture data is written unaligned starting from dma_base + 1 so I need to
+    disable mmap and to add a copy callback.
+  - After several cycle of the following:
+    while : ; do arecord -d1 -f cd -t raw | aplay -f cd ; done
+    a "playback write error (DMA or IRQ trouble?)" may happen.
+    This is due to playback interrupts not generated.
+    I suspect a timing issue.
+  - Sometimes the interrupt handler is invoked wrongly during playback.
+    This generate some harmless "Unexpected hw_pointer: wrong interrupt
+    acknowledge".
+    I've seen that using small period sizes.
+    Reproducible with:
+    mpg123 test.mp3 &
+    hdparm -t -T /dev/hda
+*/
+
+
+#define SNDRV_MAIN_OBJECT_FILE
+#include <sound/driver.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/opl3.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+#define chip_t es1938_t
+
+EXPORT_NO_SYMBOLS;
+MODULE_DESCRIPTION("ESS Solo-1");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{ESS,ES1938},"
+                "{ESS,ES1946},"
+                "{ESS,ES1969},"
+		"{TerraTec,128i PCI}}");
+
+#ifndef PCI_VENDOR_ID_ESS
+#define PCI_VENDOR_ID_ESS		0x125d
+#endif
+#ifndef PCI_DEVICE_ID_ESS_ES1938
+#define PCI_DEVICE_ID_ESS_ES1938	0x1969
+#endif
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE;	/* Enable this card */
+
+MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(index, "Index value for ESS Solo-1 soundcard.");
+MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC);
+MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+MODULE_PARM_DESC(id, "ID string for ESS Solo-1 soundcard.");
+MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC);
+MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(enable, "Enable ESS Solo-1 soundcard.");
+MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC);
+
+#define SLIO_REG(chip, x) ((chip)->io_port + ESSIO_REG_##x)
+
+#define SLDM_REG(chip, x) ((chip)->ddma_port + ESSDM_REG_##x)
+
+#define SLSB_REG(chip, x) ((chip)->sb_port + ESSSB_REG_##x)
+
+#define SL_PCI_LEGACYCONTROL		0x40
+#define SL_PCI_CONFIG			0x50
+#define SL_PCI_DDMACONTROL		0x60
+
+#define ESSIO_REG_AUDIO2DMAADDR		0
+#define ESSIO_REG_AUDIO2DMACOUNT	4
+#define ESSIO_REG_AUDIO2MODE		6
+#define ESSIO_REG_IRQCONTROL		7
+
+#define ESSDM_REG_DMAADDR		0x00
+#define ESSDM_REG_DMACOUNT		0x04
+#define ESSDM_REG_DMACOMMAND		0x08
+#define ESSDM_REG_DMASTATUS		0x08
+#define ESSDM_REG_DMAMODE		0x0b
+#define ESSDM_REG_DMACLEAR		0x0d
+#define ESSDM_REG_DMAMASK		0x0f
+
+#define ESSSB_REG_FMLOWADDR		0x00
+#define ESSSB_REG_FMHIGHADDR		0x02
+#define ESSSB_REG_MIXERADDR		0x04
+#define ESSSB_REG_MIXERDATA		0x05
+
+#define ESSSB_IREG_AUDIO1		0x14
+#define ESSSB_IREG_MICMIX		0x1a
+#define ESSSB_IREG_RECSRC		0x1c
+#define ESSSB_IREG_MASTER		0x32
+#define ESSSB_IREG_FM			0x36
+#define ESSSB_IREG_AUXACD		0x38
+#define ESSSB_IREG_AUXB			0x3a
+#define ESSSB_IREG_PCSPEAKER		0x3c
+#define ESSSB_IREG_LINE			0x3e
+#define ESSSB_IREG_SPATCONTROL		0x50
+#define ESSSB_IREG_SPATLEVEL		0x52
+#define ESSSB_IREG_MASTER_LEFT		0x60
+#define ESSSB_IREG_MASTER_RIGHT		0x62
+#define ESSSB_IREG_MPU401CONTROL	0x64
+#define ESSSB_IREG_MICMIXRECORD		0x68
+#define ESSSB_IREG_AUDIO2RECORD		0x69
+#define ESSSB_IREG_AUXACDRECORD		0x6a
+#define ESSSB_IREG_FMRECORD		0x6b
+#define ESSSB_IREG_AUXBRECORD		0x6c
+#define ESSSB_IREG_MONO			0x6d
+#define ESSSB_IREG_LINERECORD		0x6e
+#define ESSSB_IREG_MONORECORD		0x6f
+#define ESSSB_IREG_AUDIO2SAMPLE		0x70
+#define ESSSB_IREG_AUDIO2MODE		0x71
+#define ESSSB_IREG_AUDIO2FILTER		0x72
+#define ESSSB_IREG_AUDIO2TCOUNTL	0x74
+#define ESSSB_IREG_AUDIO2TCOUNTH	0x76
+#define ESSSB_IREG_AUDIO2CONTROL1	0x78
+#define ESSSB_IREG_AUDIO2CONTROL2	0x7a
+#define ESSSB_IREG_AUDIO2		0x7c
+
+#define ESSSB_REG_RESET			0x06
+
+#define ESSSB_REG_READDATA		0x0a
+#define ESSSB_REG_WRITEDATA		0x0c
+#define ESSSB_REG_READSTATUS		0x0c
+
+#define ESSSB_REG_STATUS		0x0e
+
+#define ESS_CMD_EXTSAMPLERATE		0xa1
+#define ESS_CMD_FILTERDIV		0xa2
+#define ESS_CMD_DMACNTRELOADL		0xa4
+#define ESS_CMD_DMACNTRELOADH		0xa5
+#define ESS_CMD_ANALOGCONTROL		0xa8
+#define ESS_CMD_IRQCONTROL		0xb1
+#define ESS_CMD_DRQCONTROL		0xb2
+#define ESS_CMD_RECLEVEL		0xb4
+#define ESS_CMD_SETFORMAT		0xb6
+#define ESS_CMD_SETFORMAT2		0xb7
+#define ESS_CMD_DMACONTROL		0xb8
+#define ESS_CMD_DMATYPE			0xb9
+#define ESS_CMD_OFFSETLEFT		0xba	
+#define ESS_CMD_OFFSETRIGHT		0xbb
+#define ESS_CMD_READREG			0xc0
+#define ESS_CMD_ENABLEEXT		0xc6
+#define ESS_CMD_PAUSEDMA		0xd0
+#define ESS_CMD_ENABLEAUDIO1		0xd1
+#define ESS_CMD_STOPAUDIO1		0xd3
+#define ESS_CMD_AUDIO1STATUS		0xd8
+#define ESS_CMD_CONTDMA			0xd4
+#define ESS_CMD_TESTIRQ			0xf2
+
+#define ESS_RECSRC_MIC		0
+#define ESS_RECSRC_AUXACD	2
+#define ESS_RECSRC_AUXB		5
+#define ESS_RECSRC_LINE		6
+#define ESS_RECSRC_NONE		7
+
+#define DAC1 0x01
+#define ADC1 0x02
+#define DAC2 0x04
+
+/*
+
+ */
+
+typedef struct _snd_es1938 es1938_t;
+
+struct _snd_es1938 {
+	int irq;
+
+	unsigned long io_port;
+	struct resource *res_io_port;
+	unsigned long sb_port;
+	struct resource *res_sb_port;
+	unsigned long vc_port;
+	struct resource *res_vc_port;
+	unsigned long mpu_port;
+	struct resource *res_mpu_port;
+	unsigned long game_port;
+	struct resource *res_game_port;
+	unsigned long ddma_port;
+
+	unsigned char irqmask;
+	unsigned char revision;
+
+	snd_kcontrol_t *hw_volume;
+	snd_kcontrol_t *hw_switch;
+	snd_kcontrol_t *master_volume;
+	snd_kcontrol_t *master_switch;
+
+	struct pci_dev *pci;
+	snd_card_t *card;
+	snd_pcm_substream_t *capture_substream;
+	snd_pcm_substream_t *playback1_substream;
+	snd_pcm_substream_t *playback2_substream;
+	snd_kmixer_t *mixer;
+	snd_rawmidi_t *rmidi;
+
+	unsigned int dma1_size;
+	unsigned int dma2_size;
+	unsigned int dma1_start;
+	unsigned int dma2_start;
+	unsigned int dma1_shift;
+	unsigned int dma2_shift;
+	unsigned int active;
+
+	spinlock_t reg_lock;
+	spinlock_t mixer_lock;
+        snd_info_entry_t *proc_entry;
+
+#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
+	struct gameport gameport;
+#endif
+};
+
+static irqreturn_t snd_es1938_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+
+static struct pci_device_id snd_es1938_ids[] __devinitdata = {
+        { 0x125d, 0x1969, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },   /* Solo-1 */
+	{ 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, snd_es1938_ids);
+
+#define RESET_LOOP_TIMEOUT	0x10000
+#define WRITE_LOOP_TIMEOUT	0x10000
+#define GET_LOOP_TIMEOUT	0x01000
+
+#undef REG_DEBUG
+/* -----------------------------------------------------------------
+ * Write to a mixer register
+ * -----------------------------------------------------------------*/
+static void snd_es1938_mixer_write(es1938_t *chip, unsigned char reg, unsigned char val)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&chip->mixer_lock, flags);
+	outb(reg, SLSB_REG(chip, MIXERADDR));
+	outb(val, SLSB_REG(chip, MIXERDATA));
+	spin_unlock_irqrestore(&chip->mixer_lock, flags);
+#ifdef REG_DEBUG
+	snd_printk("Mixer reg %02x set to %02x\n", reg, val);
+#endif
+}
+
+/* -----------------------------------------------------------------
+ * Read from a mixer register
+ * -----------------------------------------------------------------*/
+static int snd_es1938_mixer_read(es1938_t *chip, unsigned char reg)
+{
+	int data;
+	unsigned long flags;
+	spin_lock_irqsave(&chip->mixer_lock, flags);
+	outb(reg, SLSB_REG(chip, MIXERADDR));
+	data = inb(SLSB_REG(chip, MIXERDATA));
+	spin_unlock_irqrestore(&chip->mixer_lock, flags);
+#ifdef REG_DEBUG
+	snd_printk("Mixer reg %02x now is %02x\n", reg, data);
+#endif
+	return data;
+}
+
+/* -----------------------------------------------------------------
+ * Write to some bits of a mixer register (return old value)
+ * -----------------------------------------------------------------*/
+static int snd_es1938_mixer_bits(es1938_t *chip, unsigned char reg, unsigned char mask, unsigned char val)
+{
+	unsigned long flags;
+	unsigned char old, new, oval;
+	spin_lock_irqsave(&chip->mixer_lock, flags);
+	outb(reg, SLSB_REG(chip, MIXERADDR));
+	old = inb(SLSB_REG(chip, MIXERDATA));
+	oval = old & mask;
+	if (val != oval) {
+		new = (old & ~mask) | (val & mask);
+		outb(new, SLSB_REG(chip, MIXERDATA));
+#ifdef REG_DEBUG
+		snd_printk("Mixer reg %02x was %02x, set to %02x\n", reg, old, new);
+#endif
+	}
+	spin_unlock_irqrestore(&chip->mixer_lock, flags);
+	return oval;
+}
+
+/* -----------------------------------------------------------------
+ * Write command to Controller Registers
+ * -----------------------------------------------------------------*/
+static void snd_es1938_write_cmd(es1938_t *chip, unsigned char cmd)
+{
+	int i;
+	unsigned char v;
+	for (i = 0; i < WRITE_LOOP_TIMEOUT; i++) {
+		if (!(v = inb(SLSB_REG(chip, READSTATUS)) & 0x80)) {
+			outb(cmd, SLSB_REG(chip, WRITEDATA));
+			return;
+		}
+	}
+	printk("snd_es1938_write_cmd timeout (0x02%x/0x02%x)\n", cmd, v);
+}
+
+/* -----------------------------------------------------------------
+ * Read the Read Data Buffer
+ * -----------------------------------------------------------------*/
+static int snd_es1938_get_byte(es1938_t *chip)
+{
+	int i;
+	unsigned char v;
+	for (i = GET_LOOP_TIMEOUT; i; i--)
+		if ((v = inb(SLSB_REG(chip, STATUS))) & 0x80)
+			return inb(SLSB_REG(chip, READDATA));
+	snd_printk("get_byte timeout: status 0x02%x\n", v);
+	return -ENODEV;
+}
+
+/* -----------------------------------------------------------------
+ * Write value cmd register
+ * -----------------------------------------------------------------*/
+static void snd_es1938_write(es1938_t *chip, unsigned char reg, unsigned char val)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_es1938_write_cmd(chip, reg);
+	snd_es1938_write_cmd(chip, val);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+#ifdef REG_DEBUG
+	snd_printk("Reg %02x set to %02x\n", reg, val);
+#endif
+}
+
+/* -----------------------------------------------------------------
+ * Read data from cmd register and return it
+ * -----------------------------------------------------------------*/
+static unsigned char snd_es1938_read(es1938_t *chip, unsigned char reg)
+{
+	unsigned char val;
+	unsigned long flags;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_es1938_write_cmd(chip, ESS_CMD_READREG);
+	snd_es1938_write_cmd(chip, reg);
+	val = snd_es1938_get_byte(chip);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+#ifdef REG_DEBUG
+	snd_printk("Reg %02x now is %02x\n", reg, val);
+#endif
+	return val;
+}
+
+/* -----------------------------------------------------------------
+ * Write data to cmd register and return old value
+ * -----------------------------------------------------------------*/
+static int snd_es1938_bits(es1938_t *chip, unsigned char reg, unsigned char mask, unsigned char val)
+{
+	unsigned long flags;
+	unsigned char old, new, oval;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_es1938_write_cmd(chip, ESS_CMD_READREG);
+	snd_es1938_write_cmd(chip, reg);
+	old = snd_es1938_get_byte(chip);
+	oval = old & mask;
+	if (val != oval) {
+		snd_es1938_write_cmd(chip, reg);
+		new = (old & ~mask) | (val & mask);
+		snd_es1938_write_cmd(chip, new);
+#ifdef REG_DEBUG
+		snd_printk("Reg %02x was %02x, set to %02x\n", reg, old, new);
+#endif
+	}
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return oval;
+}
+
+/* --------------------------------------------------------------------
+ * Reset the chip
+ * --------------------------------------------------------------------*/
+static void snd_es1938_reset(es1938_t *chip)
+{
+	int i;
+
+	outb(3, SLSB_REG(chip, RESET));
+	inb(SLSB_REG(chip, RESET));
+	outb(0, SLSB_REG(chip, RESET));
+	for (i = 0; i < RESET_LOOP_TIMEOUT; i++) {
+		if (inb(SLSB_REG(chip, STATUS)) & 0x80) {
+			if (inb(SLSB_REG(chip, READDATA)) == 0xaa)
+				goto __next;
+		}
+	}
+	snd_printk("ESS Solo-1 reset failed\n");
+
+     __next:
+	snd_es1938_write_cmd(chip, ESS_CMD_ENABLEEXT);
+
+	/* Demand transfer DMA: 4 bytes per DMA request */
+	snd_es1938_write(chip, ESS_CMD_DMATYPE, 2);
+
+	/* Change behaviour of register A1
+	   4x oversampling
+	   2nd channel DAC asynchronous */                                                      
+	snd_es1938_mixer_write(chip, ESSSB_IREG_AUDIO2MODE, 0x32);
+	/* enable/select DMA channel and IRQ channel */
+	snd_es1938_bits(chip, ESS_CMD_IRQCONTROL, 0xf0, 0x50);
+	snd_es1938_bits(chip, ESS_CMD_DRQCONTROL, 0xf0, 0x50);
+	snd_es1938_write_cmd(chip, ESS_CMD_ENABLEAUDIO1);
+	/* Set spatializer parameters to recommended values */
+	snd_es1938_mixer_write(chip, 0x54, 0x8f);
+	snd_es1938_mixer_write(chip, 0x56, 0x95);
+	snd_es1938_mixer_write(chip, 0x58, 0x94);
+	snd_es1938_mixer_write(chip, 0x5a, 0x80);
+}
+
+/* --------------------------------------------------------------------
+ * Reset the FIFOs
+ * --------------------------------------------------------------------*/
+static void snd_es1938_reset_fifo(es1938_t *chip)
+{
+	outb(2, SLSB_REG(chip, RESET));
+	outb(0, SLSB_REG(chip, RESET));
+}
+
+#ifdef TARGET_OS2
+static ratnum_t clocks[2] = {
+	{
+		793800,
+		1,
+		128,
+		1,
+	},
+	{
+		768000,
+		1,
+		128,
+		1,
+	}
+};
+
+static snd_pcm_hw_constraint_ratnums_t hw_constraints_clocks = {
+	2,
+	clocks,
+};
+#else
+static ratnum_t clocks[2] = {
+	{
+		num: 793800,
+		den_min: 1,
+		den_max: 128,
+		den_step: 1,
+	},
+	{
+		num: 768000,
+		den_min: 1,
+		den_max: 128,
+		den_step: 1,
+	}
+};
+
+static snd_pcm_hw_constraint_ratnums_t hw_constraints_clocks = {
+	nrats: 2,
+	rats: clocks,
+};
+#endif
+
+static void snd_es1938_rate_set(es1938_t *chip, 
+				snd_pcm_substream_t *substream,
+				int mode)
+{
+	unsigned int bits, div0;
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	if (runtime->rate_num == clocks[0].num)
+		bits = 128 - runtime->rate_den;
+	else
+		bits = 256 - runtime->rate_den;
+
+	/* set filter register */
+	div0 = 256 - 7160000*20/(8*82*runtime->rate);
+		
+	if (mode == DAC2) {
+		snd_es1938_mixer_write(chip, 0x70, bits);
+		snd_es1938_mixer_write(chip, 0x72, div0);
+	} else {
+		snd_es1938_write(chip, 0xA1, bits);
+		snd_es1938_write(chip, 0xA2, div0);
+	}
+}
+
+/* --------------------------------------------------------------------
+ * Configure Solo1 builtin DMA Controller
+ * --------------------------------------------------------------------*/
+
+static void snd_es1938_playback1_setdma(es1938_t *chip)
+{
+	outb(0x00, SLIO_REG(chip, AUDIO2MODE));
+	outl(chip->dma2_start, SLIO_REG(chip, AUDIO2DMAADDR));
+	outw(0, SLIO_REG(chip, AUDIO2DMACOUNT));
+	outw(chip->dma2_size, SLIO_REG(chip, AUDIO2DMACOUNT));
+}
+
+static void snd_es1938_playback2_setdma(es1938_t *chip)
+{
+	/* Enable DMA controller */
+	outb(0xc4, SLDM_REG(chip, DMACOMMAND));
+	/* 1. Master reset */
+	outb(0, SLDM_REG(chip, DMACLEAR));
+	/* 2. Mask DMA */
+	outb(1, SLDM_REG(chip, DMAMASK));
+	outb(0x18, SLDM_REG(chip, DMAMODE));
+	outl(chip->dma1_start, SLDM_REG(chip, DMAADDR));
+	outw(chip->dma1_size - 1, SLDM_REG(chip, DMACOUNT));
+	/* 3. Unmask DMA */
+	outb(0, SLDM_REG(chip, DMAMASK));
+}
+
+static void snd_es1938_capture_setdma(es1938_t *chip)
+{
+	/* Enable DMA controller */
+	outb(0xc4, SLDM_REG(chip, DMACOMMAND));
+	/* 1. Master reset */
+	outb(0, SLDM_REG(chip, DMACLEAR));
+	/* 2. Mask DMA */
+	outb(1, SLDM_REG(chip, DMAMASK));
+	outb(0x14, SLDM_REG(chip, DMAMODE));
+	outl(chip->dma1_start, SLDM_REG(chip, DMAADDR));
+	outw(chip->dma1_size - 1, SLDM_REG(chip, DMACOUNT));
+	/* 3. Unmask DMA */
+	outb(0, SLDM_REG(chip, DMAMASK));
+}
+
+/* ----------------------------------------------------------------------
+ *
+ *                           *** PCM part ***
+ */
+
+static int snd_es1938_capture_trigger(snd_pcm_substream_t * substream,
+				      int cmd)
+{
+	es1938_t *chip = snd_pcm_substream_chip(substream);
+	int val;
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		val = 0x0f;
+		chip->active |= ADC1;
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		val = 0x00;
+		chip->active &= ~ADC1;
+		break;
+	default:
+		return -EINVAL;
+	}
+	snd_es1938_write(chip, ESS_CMD_DMACONTROL, val);
+	return 0;
+}
+
+static int snd_es1938_playback1_trigger(snd_pcm_substream_t * substream,
+					int cmd)
+{
+	es1938_t *chip = snd_pcm_substream_chip(substream);
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+            /* According to the documentation this should be:
+             0x13 but that value may randomly swap stereo channels */
+		snd_es1938_mixer_write(chip, ESSSB_IREG_AUDIO2CONTROL1, 0x93);
+		outb(0x0a, SLIO_REG(chip, AUDIO2MODE));
+		chip->active |= DAC2;
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		outb(0, SLIO_REG(chip, AUDIO2MODE));
+		snd_es1938_mixer_write(chip, ESSSB_IREG_AUDIO2CONTROL1, 0);
+		chip->active &= ~DAC2;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int snd_es1938_playback2_trigger(snd_pcm_substream_t * substream,
+					int cmd)
+{
+	es1938_t *chip = snd_pcm_substream_chip(substream);
+	int val;
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		val = 5;
+		chip->active |= DAC1;
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		val = 0;
+		chip->active &= ~DAC1;
+		break;
+	default:
+		return -EINVAL;
+	}
+	snd_es1938_write(chip, ESS_CMD_DMACONTROL, val);
+	return 0;
+}
+
+static int snd_es1938_playback_trigger(snd_pcm_substream_t *substream,
+				       int cmd)
+{
+	switch (substream->number) {
+	case 0:
+		return snd_es1938_playback1_trigger(substream, cmd);
+	case 1:
+		return snd_es1938_playback2_trigger(substream, cmd);
+	}
+	snd_BUG();
+	return -EINVAL;
+}
+
+/* --------------------------------------------------------------------
+ * First channel for Extended Mode Audio 1 ADC Operation
+ * --------------------------------------------------------------------*/
+static int snd_es1938_capture_prepare(snd_pcm_substream_t * substream)
+{
+	es1938_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int u, is8, mono;
+	unsigned int size = snd_pcm_lib_buffer_bytes(substream);
+	unsigned int count = snd_pcm_lib_period_bytes(substream);
+
+	chip->dma1_size = size;
+	chip->dma1_start = runtime->dma_addr;
+
+	mono = (runtime->channels > 1) ? 0 : 1;
+	is8 = snd_pcm_format_width(runtime->format) == 16 ? 0 : 1;
+	u = snd_pcm_format_unsigned(runtime->format);
+
+	chip->dma1_shift = 2 - mono - is8;
+
+	snd_es1938_reset_fifo(chip);
+	
+	/* program type */
+	snd_es1938_bits(chip, ESS_CMD_ANALOGCONTROL, 0x03, (mono ? 2 : 1));
+
+	/* set clock and counters */
+        snd_es1938_rate_set(chip, substream, ADC1);
+
+	count = 0x10000 - count;
+	snd_es1938_write(chip, ESS_CMD_DMACNTRELOADL, count & 0xff);
+	snd_es1938_write(chip, ESS_CMD_DMACNTRELOADH, count >> 8);
+
+	/* initialize and configure ADC */
+	snd_es1938_write(chip, ESS_CMD_SETFORMAT2, u ? 0x51 : 0x71);
+	snd_es1938_write(chip, ESS_CMD_SETFORMAT2, 0x90 | 
+		       (u ? 0x00 : 0x20) | 
+		       (is8 ? 0x00 : 0x04) | 
+		       (mono ? 0x40 : 0x08));
+
+	//	snd_es1938_reset_fifo(chip);	
+
+	/* 11. configure system interrupt controller and DMA controller */
+	snd_es1938_capture_setdma(chip);
+
+	return 0;
+}
+
+
+/* ------------------------------------------------------------------------------
+ * Second Audio channel DAC Operation
+ * ------------------------------------------------------------------------------*/
+static int snd_es1938_playback1_prepare(snd_pcm_substream_t * substream)
+{
+	es1938_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int u, is8, mono;
+	unsigned int size = snd_pcm_lib_buffer_bytes(substream);
+	unsigned int count = snd_pcm_lib_period_bytes(substream);
+
+	chip->dma2_size = size;
+	chip->dma2_start = runtime->dma_addr;
+
+	mono = (runtime->channels > 1) ? 0 : 1;
+	is8 = snd_pcm_format_width(runtime->format) == 16 ? 0 : 1;
+	u = snd_pcm_format_unsigned(runtime->format);
+
+	chip->dma2_shift = 2 - mono - is8;
+
+	/* set clock and counters */
+        snd_es1938_rate_set(chip, substream, DAC2);
+
+	count >>= 1;
+	count = 0x10000 - count;
+	snd_es1938_mixer_write(chip, ESSSB_IREG_AUDIO2TCOUNTL, count & 0xff);
+	snd_es1938_mixer_write(chip, ESSSB_IREG_AUDIO2TCOUNTH, count >> 8);
+
+	/* initialize and configure Audio 2 DAC */
+	snd_es1938_mixer_write(chip, ESSSB_IREG_AUDIO2CONTROL2, 0x40 | (u ? 0 : 4) | (mono ? 0 : 2) | (is8 ? 0 : 1));
+
+	/* program DMA */
+	snd_es1938_playback1_setdma(chip);
+	
+	return 0;
+}
+
+static int snd_es1938_playback2_prepare(snd_pcm_substream_t * substream)
+{
+	es1938_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int u, is8, mono;
+	unsigned int size = snd_pcm_lib_buffer_bytes(substream);
+	unsigned int count = snd_pcm_lib_period_bytes(substream);
+
+	chip->dma1_size = size;
+	chip->dma1_start = runtime->dma_addr;
+
+	mono = (runtime->channels > 1) ? 0 : 1;
+	is8 = snd_pcm_format_width(runtime->format) == 16 ? 0 : 1;
+	u = snd_pcm_format_unsigned(runtime->format);
+
+	chip->dma1_shift = 2 - mono - is8;
+
+	count = 0x10000 - count;
+ 
+	/* reset */
+	snd_es1938_reset_fifo(chip);
+	
+	snd_es1938_bits(chip, ESS_CMD_ANALOGCONTROL, 0x03, (mono ? 2 : 1));
+
+	/* set clock and counters */
+        snd_es1938_rate_set(chip, substream, DAC1);
+	snd_es1938_write(chip, ESS_CMD_DMACNTRELOADL, count & 0xff);
+	snd_es1938_write(chip, ESS_CMD_DMACNTRELOADH, count >> 8);
+
+	/* initialized and configure DAC */
+        snd_es1938_write(chip, ESS_CMD_SETFORMAT, u ? 0x80 : 0x00);
+        snd_es1938_write(chip, ESS_CMD_SETFORMAT, u ? 0x51 : 0x71);
+        snd_es1938_write(chip, ESS_CMD_SETFORMAT2, 
+			 0x90 | (mono ? 0x40 : 0x08) |
+			 (is8 ? 0x00 : 0x04) | (u ? 0x00 : 0x20));
+
+	/* program DMA */
+	snd_es1938_playback2_setdma(chip);
+	
+	return 0;
+}
+
+static int snd_es1938_playback_prepare(snd_pcm_substream_t *substream)
+{
+	switch (substream->number) {
+	case 0:
+		return snd_es1938_playback1_prepare(substream);
+	case 1:
+		return snd_es1938_playback2_prepare(substream);
+	}
+	snd_BUG();
+	return -EINVAL;
+}
+
+static snd_pcm_uframes_t snd_es1938_capture_pointer(snd_pcm_substream_t * substream)
+{
+	es1938_t *chip = snd_pcm_substream_chip(substream);
+	size_t ptr;
+	size_t old, new;
+#if 1
+	/* This stuff is *needed*, don't ask why - AB */
+	old = inw(SLDM_REG(chip, DMACOUNT));
+	while ((new = inw(SLDM_REG(chip, DMACOUNT))) != old)
+		old = new;
+	ptr = chip->dma1_size - 1 - new;
+#else
+	ptr = inl(SLDM_REG(chip, DMAADDR)) - chip->dma1_start;
+#endif
+	return ptr >> chip->dma1_shift;
+}
+
+static snd_pcm_uframes_t snd_es1938_playback1_pointer(snd_pcm_substream_t * substream)
+{
+	es1938_t *chip = snd_pcm_substream_chip(substream);
+	size_t ptr;
+#if 1
+	ptr = chip->dma2_size - inw(SLIO_REG(chip, AUDIO2DMACOUNT));
+#else
+	ptr = inl(SLIO_REG(chip, AUDIO2DMAADDR)) - chip->dma2_start;
+#endif
+	return ptr >> chip->dma2_shift;
+}
+
+static snd_pcm_uframes_t snd_es1938_playback2_pointer(snd_pcm_substream_t * substream)
+{
+	es1938_t *chip = snd_pcm_substream_chip(substream);
+	size_t ptr;
+	size_t old, new;
+#if 1
+	/* This stuff is *needed*, don't ask why - AB */
+	old = inw(SLDM_REG(chip, DMACOUNT));
+	while ((new = inw(SLDM_REG(chip, DMACOUNT))) != old)
+		old = new;
+	ptr = chip->dma1_size - 1 - new;
+#else
+	ptr = inl(SLDM_REG(chip, DMAADDR)) - chip->dma1_start;
+#endif
+	return ptr >> chip->dma1_shift;
+}
+
+static snd_pcm_uframes_t snd_es1938_playback_pointer(snd_pcm_substream_t *substream)
+{
+	switch (substream->number) {
+	case 0:
+		return snd_es1938_playback1_pointer(substream);
+	case 1:
+		return snd_es1938_playback2_pointer(substream);
+	}
+	snd_BUG();
+	return -EINVAL;
+}
+
+static int snd_es1938_capture_copy(snd_pcm_substream_t *substream,
+				   int channel,
+				   snd_pcm_uframes_t pos,
+				   void *dst,
+				   snd_pcm_uframes_t count)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	es1938_t *chip = snd_pcm_substream_chip(substream);
+	pos <<= chip->dma1_shift;
+	count <<= chip->dma1_shift;
+	snd_assert(pos + count <= chip->dma1_size, return -EINVAL);
+	if (pos + count < chip->dma1_size) {
+		if (copy_to_user(dst, runtime->dma_area + pos + 1, count))
+			return -EFAULT;
+	} else {
+		if (copy_to_user(dst, runtime->dma_area + pos + 1, count - 1))
+			return -EFAULT;
+		if (put_user(runtime->dma_area[0], ((unsigned char *)dst) + count - 1))
+			return -EFAULT;
+	}
+	return 0;
+}
+
+#ifdef TARGET_OS2
+/* ----------------------------------------------------------------------
+ * Audio1 Capture (ADC)
+ * ----------------------------------------------------------------------*/
+static snd_pcm_hardware_t snd_es1938_capture =
+{
+/*	info:		  */	(SNDRV_PCM_INFO_INTERLEAVED |
+			  	 SNDRV_PCM_INFO_BLOCK_TRANSFER),
+/*	formats:	  */	SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE,
+/*	rates:		  */	SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+/*	rate_min:	  */	6000,
+/*	rate_max:	  */	48000,
+/*	channels_min:	  */	1,
+/*	channels_max:	  */	2,
+/*	buffer_bytes_max: */	65536,
+/*	period_bytes_min: */	64,
+/*	period_bytes_max: */	65536,
+/*	periods_min:	  */	1,
+/*	periods_max:	  */	1024,
+/*	fifo_size:	  */	256,
+};
+
+/* -----------------------------------------------------------------------
+ * Audio2 Playback (DAC)
+ * -----------------------------------------------------------------------*/
+static snd_pcm_hardware_t snd_es1938_playback =
+{
+/*	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 | SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE,
+/*	rates:		  */	SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+/*	rate_min:	  */	6000,
+/*	rate_max:	  */	48000,
+/*	channels_min:	  */	1,
+/*	channels_max:	  */	2,
+/*	buffer_bytes_max: */	65536,
+/*	period_bytes_min: */	64,
+/*	period_bytes_max: */	65536,
+/*	periods_min:	  */	1,
+/*	periods_max:	  */	1024,
+/*	fifo_size:	  */	256,
+};
+#else
+/* ----------------------------------------------------------------------
+ * Audio1 Capture (ADC)
+ * ----------------------------------------------------------------------*/
+static snd_pcm_hardware_t snd_es1938_capture =
+{
+	info:			(SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER),
+	formats:		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE,
+	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	rate_min:		6000,
+	rate_max:		48000,
+	channels_min:		1,
+	channels_max:		2,
+	buffer_bytes_max:	65536,
+	period_bytes_min:	64,
+	period_bytes_max:	65536,
+	periods_min:		1,
+	periods_max:		1024,
+	fifo_size:		256,
+};
+
+/* -----------------------------------------------------------------------
+ * Audio2 Playback (DAC)
+ * -----------------------------------------------------------------------*/
+static snd_pcm_hardware_t snd_es1938_playback =
+{
+	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 | SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE,
+	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	rate_min:		6000,
+	rate_max:		48000,
+	channels_min:		1,
+	channels_max:		2,
+	buffer_bytes_max:	65536,
+	period_bytes_min:	64,
+	period_bytes_max:	65536,
+	periods_min:		1,
+	periods_max:		1024,
+	fifo_size:		256,
+};
+#endif
+
+static int snd_es1938_capture_open(snd_pcm_substream_t * substream)
+{
+	es1938_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	if (chip->playback2_substream)
+		return -EAGAIN;
+	if ((runtime->dma_area = snd_malloc_pci_pages_fallback(chip->pci, chip->dma2size, &runtime->dma_addr, &runtime->dma_bytes)) == NULL)
+		return -ENOMEM;
+	chip->capture_substream = substream;
+	runtime->hw = snd_es1938_capture;
+	snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+				      &hw_constraints_clocks);
+	snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 0, 0xff00);
+	return 0;
+}
+
+static int snd_es1938_playback_open(snd_pcm_substream_t * substream)
+{
+	es1938_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	switch (substream->number) {
+	case 0:
+		if ((runtime->dma_area = snd_malloc_pci_pages_fallback(chip->pci, chip->dma1size, &runtime->dma_addr, &runtime->dma_bytes)) == NULL)
+			return -ENOMEM;
+		chip->playback1_substream = substream;
+		break;
+	case 1:
+		if (chip->capture_substream)
+			return -EAGAIN;
+		if ((runtime->dma_area = snd_malloc_pci_pages_fallback(chip->pci, chip->dma1size, &runtime->dma_addr, &runtime->dma_bytes)) == NULL)
+			return -ENOMEM;
+		chip->playback2_substream = substream;
+		break;
+	default:
+		snd_BUG();
+		return -EINVAL;
+	}
+	runtime->hw = snd_es1938_playback;
+	snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+				      &hw_constraints_clocks);
+	snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 0, 0xff00);
+	return 0;
+}
+
+static int snd_es1938_capture_close(snd_pcm_substream_t * substream)
+{
+	es1938_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	chip->capture_substream = NULL;
+	snd_free_pci_pages(chip->pci, runtime->dma_bytes, runtime->dma_area, runtime->dma_addr);
+	return 0;
+}
+
+static int snd_es1938_playback_close(snd_pcm_substream_t * substream)
+{
+	es1938_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	switch (substream->number) {
+	case 0:
+		chip->playback1_substream = NULL;
+		break;
+	case 1:
+		chip->playback2_substream = NULL;
+		break;
+	default:
+		snd_BUG();
+		return -EINVAL;
+	}
+	snd_free_pci_pages(chip->pci, runtime->dma_bytes, runtime->dma_area, runtime->dma_addr);
+	return 0;
+}
+
+#ifdef TARGET_OS2
+static snd_pcm_ops_t snd_es1938_playback_ops = {
+/*	open:	  */	snd_es1938_playback_open,
+/*	close:	  */	snd_es1938_playback_close,
+/*	ioctl:	  */	snd_pcm_lib_ioctl,
+        0,0,
+/*	prepare:  */	snd_es1938_playback_prepare,
+/*	trigger:  */	snd_es1938_playback_trigger,
+/*	pointer:  */	snd_es1938_playback_pointer,
+        0,0
+};
+
+static snd_pcm_ops_t snd_es1938_capture_ops = {
+/*	open:	  */	snd_es1938_capture_open,
+/*	close:	  */	snd_es1938_capture_close,
+/*	ioctl:	  */	snd_pcm_lib_ioctl,
+        0,0,
+/*	prepare:  */	snd_es1938_capture_prepare,
+/*	trigger:  */	snd_es1938_capture_trigger,
+/*	pointer:  */	snd_es1938_capture_pointer,
+/*	copy:	  */	snd_es1938_capture_copy,
+        0
+};
+#else
+static snd_pcm_ops_t snd_es1938_playback_ops = {
+	open:		snd_es1938_playback_open,
+	close:		snd_es1938_playback_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	prepare:	snd_es1938_playback_prepare,
+	trigger:	snd_es1938_playback_trigger,
+	pointer:	snd_es1938_playback_pointer,
+};
+
+static snd_pcm_ops_t snd_es1938_capture_ops = {
+	open:		snd_es1938_capture_open,
+	close:		snd_es1938_capture_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	prepare:	snd_es1938_capture_prepare,
+	trigger:	snd_es1938_capture_trigger,
+	pointer:	snd_es1938_capture_pointer,
+	copy:		snd_es1938_capture_copy,
+};
+#endif
+
+static void snd_es1938_free_pcm(snd_pcm_t *pcm)
+{
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static int __init snd_es1938_new_pcm(es1938_t *chip, int device, snd_pcm_t ** rpcm)
+{
+	snd_pcm_t *pcm;
+	int err;
+
+	if (rpcm)
+		*rpcm = NULL;
+	if ((err = snd_pcm_new(chip->card, "es-1938-1946", device, 2, 1, &pcm)) < 0)
+		return err;
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_es1938_playback_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_es1938_capture_ops);
+	
+	pcm->private_data = chip;
+	pcm->private_free = snd_es1938_free_pcm;
+	pcm->info_flags = 0;
+	strcpy(pcm->name, "ESS Solo-1");
+
+	snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 64*1024);
+
+	if (rpcm)
+		*rpcm = pcm;
+	return 0;
+}
+
+/* -------------------------------------------------------------------
+ * 
+ *                       *** Mixer part ***
+ */
+
+static int snd_es1938_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	static char *texts[8] = {
+		"Mic", "Mic Master", "CD", "AOUT",
+		"Mic1", "Mix", "Line", "Master"
+	};
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 8;
+	if (uinfo->value.enumerated.item > 7)
+		uinfo->value.enumerated.item = 7;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int snd_es1938_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	es1938_t *chip = snd_kcontrol_chip(kcontrol);
+	ucontrol->value.enumerated.item[0] = snd_es1938_mixer_read(chip, 0x1c) & 0x07;
+	return 0;
+}
+
+static int snd_es1938_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	es1938_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned char val = ucontrol->value.enumerated.item[0];
+	
+	if (val > 7)
+		return -EINVAL;
+	return snd_es1938_mixer_bits(chip, 0x1c, 0x07, val) != val;
+}
+
+static int snd_es1938_info_spatializer_enable(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_es1938_get_spatializer_enable(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	es1938_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned char val = snd_es1938_mixer_read(chip, 0x50);
+	ucontrol->value.integer.value[0] = !!(val & 8);
+	return 0;
+}
+
+static int snd_es1938_put_spatializer_enable(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	es1938_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned char oval, nval;
+	int change;
+	nval = ucontrol->value.integer.value[0] ? 0x0c : 0x04;
+	oval = snd_es1938_mixer_read(chip, 0x50) & 0x0c;
+	change = nval != oval;
+	if (change) {
+		snd_es1938_mixer_write(chip, 0x50, nval & ~0x04);
+		snd_es1938_mixer_write(chip, 0x50, nval);
+	}
+	return change;
+}
+
+static int snd_es1938_info_hw_volume(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 = 63;
+	return 0;
+}
+
+static int snd_es1938_get_hw_volume(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	es1938_t *chip = snd_kcontrol_chip(kcontrol);
+	ucontrol->value.integer.value[0] = snd_es1938_mixer_read(chip, 0x61) & 0x3f;
+	ucontrol->value.integer.value[1] = snd_es1938_mixer_read(chip, 0x63) & 0x3f;
+	return 0;
+}
+
+static int snd_es1938_info_hw_switch(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+	return 0;
+}
+
+static int snd_es1938_get_hw_switch(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	es1938_t *chip = snd_kcontrol_chip(kcontrol);
+	ucontrol->value.integer.value[0] = !(snd_es1938_mixer_read(chip, 0x61) & 0x40);
+	ucontrol->value.integer.value[1] = !(snd_es1938_mixer_read(chip, 0x63) & 0x40);
+	return 0;
+}
+
+static void snd_es1938_hwv_free(snd_kcontrol_t *kcontrol)
+{
+	es1938_t *chip = snd_magic_cast(es1938_t, _snd_kcontrol_chip(kcontrol), return);
+	chip->master_volume = NULL;
+	chip->master_switch = NULL;
+	chip->hw_volume = NULL;
+	chip->hw_switch = NULL;
+}
+
+static int snd_es1938_reg_bits(es1938_t *chip, unsigned char reg,
+			       unsigned char mask, unsigned char val)
+{
+	if (reg < 0xa0)
+		return snd_es1938_mixer_bits(chip, reg, mask, val);
+	else
+		return snd_es1938_bits(chip, reg, mask, val);
+}
+
+static int snd_es1938_reg_read(es1938_t *chip, unsigned char reg)
+{
+	if (reg < 0xa0)
+		return snd_es1938_mixer_read(chip, reg);
+	else
+		return snd_es1938_read(chip, reg);
+}
+
+#ifdef TARGET_OS2
+#define ES1938_SINGLE(xname, xindex, reg, shift, mask, invert) \
+{ SNDRV_CTL_ELEM_IFACE_MIXER, 0, 0, xname, xindex, \
+  0, 0, snd_es1938_info_single, \
+  snd_es1938_get_single, snd_es1938_put_single, \
+  reg | (shift << 8) | (mask << 16) | (invert << 24) }
+#else
+#define ES1938_SINGLE(xname, xindex, reg, shift, mask, invert) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
+  info: snd_es1938_info_single, \
+  get: snd_es1938_get_single, put: snd_es1938_put_single, \
+  private_value: reg | (shift << 8) | (mask << 16) | (invert << 24) }
+#endif
+
+static int snd_es1938_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+
+	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	return 0;
+}
+
+static int snd_es1938_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	es1938_t *chip = snd_kcontrol_chip(kcontrol);
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 8) & 0xff;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0xff;
+	int val;
+	
+	val = snd_es1938_reg_read(chip, reg);
+	ucontrol->value.integer.value[0] = (val >> shift) & mask;
+	if (invert)
+		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+	return 0;
+}
+
+static int snd_es1938_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	es1938_t *chip = snd_kcontrol_chip(kcontrol);
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 8) & 0xff;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0xff;
+	unsigned char val;
+	
+	val = (ucontrol->value.integer.value[0] & mask);
+	if (invert)
+		val = mask - val;
+	mask <<= shift;
+	val <<= shift;
+	return snd_es1938_reg_bits(chip, reg, mask, val) != val;
+}
+
+#ifdef TARGET_OS2
+#define ES1938_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \
+{ SNDRV_CTL_ELEM_IFACE_MIXER, 0, 0, xname, xindex, \
+  0, 0, snd_es1938_info_double, \
+  snd_es1938_get_double, snd_es1938_put_double, \
+  left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
+#else
+#define ES1938_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
+  info: snd_es1938_info_double, \
+  get: snd_es1938_get_double, put: snd_es1938_put_double, \
+  private_value: left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
+#endif
+
+static int snd_es1938_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+
+	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	return 0;
+}
+
+static int snd_es1938_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	es1938_t *chip = snd_kcontrol_chip(kcontrol);
+	int left_reg = kcontrol->private_value & 0xff;
+	int right_reg = (kcontrol->private_value >> 8) & 0xff;
+	int shift_left = (kcontrol->private_value >> 16) & 0x07;
+	int shift_right = (kcontrol->private_value >> 19) & 0x07;
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+	int invert = (kcontrol->private_value >> 22) & 1;
+	unsigned char left, right;
+	
+	left = snd_es1938_reg_read(chip, left_reg);
+	if (left_reg != right_reg)
+		right = snd_es1938_reg_read(chip, right_reg);
+	else
+		right = left;
+	ucontrol->value.integer.value[0] = (left >> shift_left) & mask;
+	ucontrol->value.integer.value[1] = (right >> shift_right) & mask;
+	if (invert) {
+		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+		ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1];
+	}
+	return 0;
+}
+
+static int snd_es1938_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	es1938_t *chip = snd_kcontrol_chip(kcontrol);
+	int left_reg = kcontrol->private_value & 0xff;
+	int right_reg = (kcontrol->private_value >> 8) & 0xff;
+	int shift_left = (kcontrol->private_value >> 16) & 0x07;
+	int shift_right = (kcontrol->private_value >> 19) & 0x07;
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+	int invert = (kcontrol->private_value >> 22) & 1;
+	int change;
+	unsigned char val1, val2, mask1, mask2;
+	
+	val1 = ucontrol->value.integer.value[0] & mask;
+	val2 = ucontrol->value.integer.value[1] & mask;
+	if (invert) {
+		val1 = mask - val1;
+		val2 = mask - val2;
+	}
+	val1 <<= shift_left;
+	val2 <<= shift_right;
+	mask1 = mask << shift_left;
+	mask2 = mask << shift_right;
+	if (left_reg != right_reg) {
+		change = 0;
+		if (snd_es1938_reg_bits(chip, left_reg, mask1, val1) != val1)
+			change = 1;
+		if (snd_es1938_reg_bits(chip, right_reg, mask2, val2) != val2)
+			change = 1;
+	} else {
+		change = (snd_es1938_reg_bits(chip, left_reg, mask1 | mask2, 
+					      val1 | val2) != (val1 | val2));
+	}
+	return change;
+}
+
+static snd_kcontrol_new_t snd_es1938_controls[] = {
+ES1938_DOUBLE("Master Playback Volume", 0, 0x60, 0x62, 0, 0, 63, 0),
+ES1938_DOUBLE("Master Playback Switch", 0, 0x60, 0x62, 6, 6, 1, 1),
+#ifdef TARGET_OS2
+{
+	SNDRV_CTL_ELEM_IFACE_MIXER,0,0,
+	"Hardware Master Playback Volume",0,
+	SNDRV_CTL_ELEM_ACCESS_READ, 0,
+	snd_es1938_info_hw_volume,
+	snd_es1938_get_hw_volume,0,0
+},
+{
+	SNDRV_CTL_ELEM_IFACE_MIXER,0,0,
+	"Hardware Master Playback Switch",0,
+	SNDRV_CTL_ELEM_ACCESS_READ,0,
+	snd_es1938_info_hw_switch,
+	snd_es1938_get_hw_switch,0,0
+},
+#else
+{
+	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	name: "Hardware Master Playback Volume",
+	access: SNDRV_CTL_ELEM_ACCESS_READ,
+	info: snd_es1938_info_hw_volume,
+	get: snd_es1938_get_hw_volume,
+},
+{
+	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	name: "Hardware Master Playback Switch",
+	access: SNDRV_CTL_ELEM_ACCESS_READ,
+	info: snd_es1938_info_hw_switch,
+	get: snd_es1938_get_hw_switch,
+},
+#endif
+ES1938_SINGLE("Hardware Volume Split", 0, 0x64, 7, 1, 0),
+ES1938_DOUBLE("Line Playback Volume", 0, 0x3e, 0x3e, 4, 0, 15, 0),
+ES1938_DOUBLE("CD Playback Volume", 0, 0x38, 0x38, 4, 0, 15, 0),
+ES1938_DOUBLE("FM Playback Volume", 0, 0x36, 0x36, 4, 0, 15, 0),
+ES1938_DOUBLE("Mono Playback Volume", 0, 0x6d, 0x6d, 4, 0, 15, 0),
+ES1938_DOUBLE("Mic Playback Volume", 0, 0x1a, 0x1a, 4, 0, 15, 0),
+ES1938_DOUBLE("Aux Playback Volume", 0, 0x3a, 0x3a, 4, 0, 15, 0),
+ES1938_DOUBLE("Capture Volume", 0, 0xb4, 0xb4, 4, 0, 15, 0),
+ES1938_SINGLE("PC Speaker Volume", 0, 0x3c, 0, 7, 0),
+ES1938_SINGLE("Record Monitor", 0, 0xa8, 3, 1, 0),
+ES1938_SINGLE("Capture Switch", 0, 0x1c, 4, 1, 1),
+{
+#ifdef TARGET_OS2
+	SNDRV_CTL_ELEM_IFACE_MIXER,0,0,
+	"Capture Source",0,0, 0,
+	snd_es1938_info_mux,
+	snd_es1938_get_mux,
+	snd_es1938_put_mux,0,
+#else
+	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	name: "Capture Source",
+	info: snd_es1938_info_mux,
+	get: snd_es1938_get_mux,
+	put: snd_es1938_put_mux,
+#endif
+},
+ES1938_DOUBLE("Mono Input Playback Volume", 0, 0x6d, 0x6d, 4, 0, 15, 0),
+ES1938_DOUBLE("PCM Capture Volume", 0, 0x69, 0x69, 4, 0, 15, 0),
+ES1938_DOUBLE("Mic Capture Volume", 0, 0x68, 0x68, 4, 0, 15, 0),
+ES1938_DOUBLE("Line Capture Volume", 0, 0x6e, 0x6e, 4, 0, 15, 0),
+ES1938_DOUBLE("FM Capture Volume", 0, 0x6b, 0x6b, 4, 0, 15, 0),
+ES1938_DOUBLE("Mono Capture Volume", 0, 0x6f, 0x6f, 4, 0, 15, 0),
+ES1938_DOUBLE("CD Capture Volume", 0, 0x6a, 0x6a, 4, 0, 15, 0),
+ES1938_DOUBLE("Aux Capture Volume", 0, 0x6c, 0x6c, 4, 0, 15, 0),
+ES1938_DOUBLE("PCM Playback Volume", 0, 0x7c, 0x7c, 4, 0, 15, 0),
+ES1938_DOUBLE("PCM Playback Volume", 1, 0x14, 0x14, 4, 0, 15, 0),
+ES1938_SINGLE("3D Control - Level", 0, 0x52, 0, 63, 0),
+{
+#ifdef TARGET_OS2
+	SNDRV_CTL_ELEM_IFACE_MIXER,0,0,
+	"3D Control - Switch",0,0, 0,
+	snd_es1938_info_spatializer_enable,
+	snd_es1938_get_spatializer_enable,
+	snd_es1938_put_spatializer_enable,0
+#else
+	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	name: "3D Control - Switch",
+	info: snd_es1938_info_spatializer_enable,
+	get: snd_es1938_get_spatializer_enable,
+	put: snd_es1938_put_spatializer_enable,
+#endif
+},
+ES1938_SINGLE("Mic Boost (+26dB)", 0, 0x7d, 3, 1, 0)
+};
+
+
+/* ---------------------------------------------------------------------------- */
+/* ---------------------------------------------------------------------------- */
+
+static int snd_es1938_free(es1938_t *chip)
+{
+	if (chip->res_io_port)
+		release_resource(chip->res_io_port);
+	if (chip->res_sb_port)
+		release_resource(chip->res_sb_port);
+	if (chip->res_vc_port)
+		release_resource(chip->res_vc_port);
+	if (chip->res_mpu_port)
+		release_resource(chip->res_mpu_port);
+	if (chip->res_game_port)
+		release_resource(chip->res_game_port);
+	if (chip->irq >= 0)
+		free_irq(chip->irq, (void *)chip);
+	snd_magic_kfree(chip);
+	return 0;
+}
+
+static int snd_es1938_dev_free(snd_device_t *device)
+{
+	es1938_t *chip = snd_magic_cast(es1938_t, device->device_data, return -ENXIO);
+	return snd_es1938_free(chip);
+}
+
+static int __init snd_es1938_create(snd_card_t * card,
+				    struct pci_dev * pci,
+				    unsigned long dma1size,
+				    unsigned long dma2size,
+				    es1938_t ** rchip)
+{
+	es1938_t *chip;
+	int err;
+#ifdef TARGET_OS2
+	static snd_device_ops_t ops = {
+		snd_es1938_dev_free,0,0
+	};
+#else
+	static snd_device_ops_t ops = {
+		dev_free:	snd_es1938_dev_free,
+	};
+#endif
+	*rchip = NULL;
+
+	/* enable PCI device */
+	if ((err = pci_enable_device(pci)) < 0)
+		return err;
+        /* check, if we can restrict PCI DMA transfers to 24 bits */
+        if (!pci_dma_supported(pci, 0x00ffffff)) {
+                snd_printk("architecture does not support 24bit PCI busmaster DMA\n");
+                return -ENXIO;
+        }
+	pci_set_dma_mask(pci, 0x00ffffff);
+
+	chip = snd_magic_kcalloc(es1938_t, 0, GFP_KERNEL);
+	if (chip == NULL)
+		return -ENOMEM;
+	spin_lock_init(&chip->reg_lock);
+	spin_lock_init(&chip->mixer_lock);
+	chip->card = card;
+	chip->pci = pci;
+	chip->dma1size = dma1size;
+	chip->dma2size = dma2size;
+	chip->io_port = pci_resource_start(pci, 0);
+	if ((chip->res_io_port = request_region(chip->io_port, 8, "ESS Solo-1")) == NULL) {
+		snd_es1938_free(chip);
+		snd_printk("unable to grab region 0x%lx-0x%lx\n", chip->io_port, chip->io_port + 8 - 1);
+		return -EBUSY;
+	}
+	chip->sb_port = pci_resource_start(pci, 1);
+	if ((chip->res_sb_port = request_region(chip->sb_port, 0x10, "ESS Solo-1 SB")) == NULL) {
+		snd_es1938_free(chip);
+		snd_printk("unable to grab SB region 0x%lx-0x%lx\n", chip->sb_port, chip->sb_port + 0x10 - 1);
+		return -EBUSY;
+	}
+	chip->vc_port = pci_resource_start(pci, 2);
+	if ((chip->res_vc_port = request_region(chip->vc_port, 0x10, "ESS Solo-1 VC (DMA)")) == NULL) {
+		snd_es1938_free(chip);
+		snd_printk("unable to grab VC (DMA) region 0x%lx-0x%lx\n", chip->vc_port, chip->vc_port + 0x10 - 1);
+		return -EBUSY;
+	}
+	chip->mpu_port = pci_resource_start(pci, 3);
+	if ((chip->res_mpu_port = request_region(chip->mpu_port, 4, "ESS Solo-1 MIDI")) == NULL) {
+		snd_es1938_free(chip);
+		snd_printk("unable to grab MIDI region 0x%lx-0x%lx\n", chip->mpu_port, chip->mpu_port + 4 - 1);
+		return -EBUSY;
+	}
+	chip->game_port = pci_resource_start(pci, 4);
+	if ((chip->res_game_port = request_region(chip->game_port, 4, "ESS Solo-1 GAME")) == NULL) {
+		snd_es1938_free(chip);
+		snd_printk("unable to grab GAME region 0x%lx-0x%lx\n", chip->game_port, chip->game_port + 4 - 1);
+		return -EBUSY;
+	}
+	if (request_irq(pci->irq, snd_es1938_interrupt, SA_INTERRUPT|SA_SHIRQ, "ES1938", (void *)chip)) {
+		snd_es1938_free(chip);
+		snd_printk("unable to grab IRQ %d\n", pci->irq);
+		return -EBUSY;
+	}
+	chip->irq = pci->irq;
+#ifdef ES1938_DDEBUG
+	snd_printk("create: io: 0x%lx, sb: 0x%lx, vc: 0x%lx, mpu: 0x%lx, game: 0x%lx\n",
+		   chip->io_port, chip->sb_port, chip->vc_port, chip->mpu_port, chip->game_port);
+#endif
+	/* reset chip */
+	snd_es1938_reset(chip);
+
+	/* configure native mode */
+
+	/* enable bus master */
+	pci_set_master(pci);
+
+	/* disable legacy audio */
+	pci_write_config_word(pci, SL_PCI_LEGACYCONTROL, 0x805f);
+
+	/* set DDMA base */
+	chip->ddma_port = chip->vc_port + 0x00;		/* fix from Thomas Sailer */
+	pci_write_config_word(pci, SL_PCI_DDMACONTROL, chip->ddma_port | 1);
+
+	/* set DMA/IRQ policy */
+	pci_write_config_dword(pci, SL_PCI_CONFIG, 0);
+
+	/* enable Audio 1, Audio 2, MPU401 IRQ and HW volume IRQ*/
+	outb(0xf0, SLIO_REG(chip, IRQCONTROL));
+
+	/* reset DMA */
+	outb(0, SLDM_REG(chip, DMACLEAR));
+
+	/* enable bus mastering */
+	pci_set_master(pci);
+
+	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+		snd_es1938_free(chip);
+		return err;
+	}
+
+	*rchip = chip;
+	return 0;
+}
+
+#if 0
+static void __init snd_es1938_midi(es1938_t *chip, mpu401_t * mpu)
+{
+	mpu->private_data = chip;
+	mpu->open_input = NULL;	/* snd_es1938_midi_input_open; */
+	mpu->close_input = NULL;	/* snd_es1938_midi_input_close; */
+	mpu->open_output = NULL;	/* snd_es1938_midi_output_open; */
+}
+#endif
+
+/* --------------------------------------------------------------------
+ * Interrupt handler
+ * -------------------------------------------------------------------- */
+static void snd_es1938_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	es1938_t *chip = snd_magic_cast(es1938_t, dev_id, return);
+	unsigned char status, audiostatus;
+#ifdef TARGET_OS2
+	int fOurIrq = FALSE;
+#endif
+
+	status = inb(SLIO_REG(chip, IRQCONTROL));
+#if 0
+	printk("Es1938debug - interrupt status: =0x%x\n", status);
+#endif
+	
+	/* AUDIO 1 */
+	if (status & 0x10) {
+#if 0
+                printk("Es1938debug - AUDIO channel 1 interrupt\n");
+		printk("Es1938debug - AUDIO channel 1 DMAC DMA count: %u\n", inw(SLDM_REG(chip, DMACOUNT)));
+		printk("Es1938debug - AUDIO channel 1 DMAC DMA base: %u\n", inl(SLDM_REG(chip, DMAADDR)));
+		printk("Es1938debug - AUDIO channel 1 DMAC DMA status: 0x%x\n", inl(SLDM_REG(chip, DMASTATUS)));
+#endif
+		/* clear irq */
+		audiostatus = inb(SLSB_REG(chip, STATUS));
+		if (chip->active & ADC1)
+			snd_pcm_period_elapsed(chip->capture_substream);
+		else if (chip->active & DAC1)
+			snd_pcm_period_elapsed(chip->playback2_substream);
+#ifdef TARGET_OS2
+		fOurIrq = TRUE;
+#endif
+	}
+	
+	/* AUDIO 2 */
+	if (status & 0x20) {
+#if 0
+                printk("Es1938debug - AUDIO channel 2 interrupt\n");
+		printk("Es1938debug - AUDIO channel 2 DMAC DMA count: %u\n", inw(SLIO_REG(chip, AUDIO2DMACOUNT)));
+		printk("Es1938debug - AUDIO channel 2 DMAC DMA base: %u\n", inl(SLIO_REG(chip, AUDIO2DMAADDR)));
+
+#endif
+		/* clear irq */
+		snd_es1938_mixer_bits(chip, ESSSB_IREG_AUDIO2CONTROL2, 0x80, 0);
+		if (chip->active & DAC2)
+			snd_pcm_period_elapsed(chip->playback1_substream);
+#ifdef TARGET_OS2
+		fOurIrq = TRUE;
+#endif
+	}
+
+	/* Hardware volume */
+	if (status & 0x40) {
+		int split = snd_es1938_mixer_read(chip, 0x64) & 0x80;
+		snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->hw_switch->id);
+		snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->hw_volume->id);
+		if (!split) {
+			snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->master_switch->id);
+			snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->master_volume->id);
+		}
+		/* ack interrupt */
+		snd_es1938_mixer_write(chip, 0x66, 0x00);
+#ifdef TARGET_OS2
+		fOurIrq = TRUE;
+#endif
+	}
+#ifdef TARGET_OS2
+	if (fOurIrq) {
+		eoi_irq(irq);
+	}
+#endif //TARGET_OS2
+}
+
+#define ES1938_DMA_SIZE 64
+
+static int __init snd_es1938_mixer(snd_pcm_t *pcm)
+{
+	snd_card_t *card;
+	es1938_t *chip;
+	int err, idx;
+#ifndef TARGET_OS2
+	snd_assert(pcm != NULL && pcm->card != NULL, return -EINVAL);
+#endif
+	card = pcm->card;
+	chip = snd_pcm_chip(pcm);
+
+	strcpy(card->mixername, pcm->name);
+
+	for (idx = 0; idx < sizeof(snd_es1938_controls) / 
+		     sizeof(snd_es1938_controls[0]); idx++) {
+		snd_kcontrol_t *kctl;
+		kctl = snd_ctl_new1(&snd_es1938_controls[idx], chip);
+		switch (idx) {
+			case 0:
+				chip->master_volume = kctl;
+				kctl->private_free = snd_es1938_hwv_free;
+				break;
+			case 1:
+				chip->master_switch = kctl;
+				kctl->private_free = snd_es1938_hwv_free;
+				break;
+			case 2:
+				chip->hw_volume = kctl;
+				kctl->private_free = snd_es1938_hwv_free;
+				break;
+			case 3:
+				chip->hw_switch = kctl;
+				kctl->private_free = snd_es1938_hwv_free;
+				break;
+			}
+		if ((err = snd_ctl_add(card, kctl)) < 0)
+			return err;
+	}
+	return 0;
+}
+       
+
+static int __init snd_es1938_probe(struct pci_dev *pci,
+				   const struct pci_device_id *id)
+{
+	static int dev = 0;
+	snd_card_t *card;
+	es1938_t *chip;
+	snd_pcm_t *pcm;
+	opl3_t *opl3;
+	int idx, err;
+
+	for ( ; dev < SNDRV_CARDS; dev++) {
+		if (!snd_enable[dev]) {
+			dev++;
+			return -ENOENT;
+		}
+		break;
+	}
+	if (dev >= SNDRV_CARDS)
+		return -ENODEV;
+
+	card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0);
+	if (card == NULL)
+		return -ENOMEM;
+	for (idx = 0; idx < 5; idx++) {
+		if (pci_resource_start(pci, idx) == 0 ||
+		    !(pci_resource_flags(pci, idx) & IORESOURCE_IO)) {
+		    	snd_card_free(card);
+		    	return -ENODEV;
+		}
+	}
+	if ((err = snd_es1938_create(card, pci,
+				     64 * 1024,
+				     64 * 1024,
+				     &chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_es1938_new_pcm(chip, 0, &pcm)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_es1938_mixer(pcm)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if (snd_opl3_create(card,
+			    SLSB_REG(chip, FMLOWADDR),
+			    SLSB_REG(chip, FMHIGHADDR),
+			    OPL3_HW_OPL3, 1, &opl3) < 0) {
+	        snd_printk("OPL3 not detected at 0x%lx\n",
+			   SLSB_REG(chip, FMLOWADDR));
+	} else {
+	        if ((err = snd_opl3_timer_new(opl3, 0, 1)) < 0) {
+	                snd_card_free(card);
+	                return err;
+		}
+	        if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
+	                snd_card_free(card);
+	                return err;
+		}
+	}
+
+	strcpy(card->driver, "ES1938");
+	strcpy(card->shortname, "ESS ES1938 (Solo-1)");
+	sprintf(card->longname, "%s rev %i, irq %i",
+		card->shortname,
+		chip->revision,
+		chip->irq);
+
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	PCI_SET_DRIVER_DATA(pci, card);
+	dev++;
+	return 0;
+}
+
+static void __exit snd_es1938_remove(struct pci_dev *pci)
+{
+	snd_card_free(PCI_GET_DRIVER_DATA(pci));
+	PCI_SET_DRIVER_DATA(pci, NULL);
+}
+
+#ifdef TARGET_OS2
+static struct pci_driver driver = {
+	0, 0,"ESS ES1938 (Solo-1)",
+	snd_es1938_ids,
+	snd_es1938_probe,
+	snd_es1938_remove,0,0
+};
+#else
+static struct pci_driver driver = {
+	name: "ESS ES1938 (Solo-1)",
+	id_table: snd_es1938_ids,
+	probe: snd_es1938_probe,
+	remove: snd_es1938_remove,
+};
+#endif
+
+static int __init alsa_card_es1938_init(void)
+{
+	int err;
+
+	if ((err = pci_module_init(&driver)) < 0) {
+#ifdef MODULE
+		snd_printk("ESS Solo-1 soundcard not found or device busy\n");
+#endif
+		return err;
+	}
+	return 0;
+}
+
+static void __exit alsa_card_es1938_exit(void)
+{
+	pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_es1938_init)
+module_exit(alsa_card_es1938_exit)
+
+#ifndef MODULE
+
+/* format is: snd-card-es1938=snd_enable,snd_index,snd_id */
+
+static int __init alsa_card_es1938_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);
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-card-es1938=", alsa_card_es1938_setup);
+
+#endif /* ifndef MODULE */
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/es1968.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/es1968.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/es1968.c	(revision 91)
@@ -0,0 +1,2893 @@
+/*
+ *  Driver for ESS Maestro 1/2/2E Sound Card (started 21.8.99)
+ *  Copyright (c) by Matze Braun <MatzeBraun@gmx.de>.
+ *                   Takashi Iwai <tiwai@suse.de>
+ *
+ *  Most of the driver code comes from Zach Brown(zab@redhat.com)
+ *	Alan Cox OSS Driver
+ *  Rewritted from card-es1938.c source.
+ *
+ *  TODO:
+ *   Perhaps Synth
+ *
+ *   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
+ *
+ *
+ *  Notes from Zach Brown about the driver code
+ *
+ *  Hardware Description
+ *
+ *	A working Maestro setup contains the Maestro chip wired to a
+ *	codec or 2.  In the Maestro we have the APUs, the ASSP, and the
+ *	Wavecache.  The APUs can be though of as virtual audio routing
+ *	channels.  They can take data from a number of sources and perform
+ *	basic encodings of the data.  The wavecache is a storehouse for
+ *	PCM data.  Typically it deals with PCI and interracts with the
+ *	APUs.  The ASSP is a wacky DSP like device that ESS is loth
+ *	to release docs on.  Thankfully it isn't required on the Maestro
+ *	until you start doing insane things like FM emulation and surround
+ *	encoding.  The codecs are almost always AC-97 compliant codecs,
+ *	but it appears that early Maestros may have had PT101 (an ESS
+ *	part?) wired to them.  The only real difference in the Maestro
+ *	families is external goop like docking capability, memory for
+ *	the ASSP, and initialization differences.
+ *
+ *  Driver Operation
+ *
+ *	We only drive the APU/Wavecache as typical DACs and drive the
+ *	mixers in the codecs.  There are 64 APUs.  We assign 6 to each
+ *	/dev/dsp? device.  2 channels for output, and 4 channels for
+ *	input.
+ *
+ *	Each APU can do a number of things, but we only really use
+ *	3 basic functions.  For playback we use them to convert PCM
+ *	data fetched over PCI by the wavecahche into analog data that
+ *	is handed to the codec.  One APU for mono, and a pair for stereo.
+ *	When in stereo, the combination of smarts in the APU and Wavecache
+ *	decide which wavecache gets the left or right channel.
+ *
+ *	For record we still use the old overly mono system.  For each in
+ *	coming channel the data comes in from the codec, through a 'input'
+ *	APU, through another rate converter APU, and then into memory via
+ *	the wavecache and PCI.  If its stereo, we mash it back into LRLR in
+ *	software.  The pass between the 2 APUs is supposedly what requires us
+ *	to have a 512 byte buffer sitting around in wavecache/memory.
+ *
+ *	The wavecache makes our life even more fun.  First off, it can
+ *	only address the first 28 bits of PCI address space, making it
+ *	useless on quite a few architectures.  Secondly, its insane.
+ *	It claims to fetch from 4 regions of PCI space, each 4 meg in length.
+ *	But that doesn't really work.  You can only use 1 region.  So all our
+ *	allocations have to be in 4meg of each other.  Booo.  Hiss.
+ *	So we have a module parameter, dsps_order, that is the order of
+ *	the number of dsps to provide.  All their buffer space is allocated
+ *	on open time.  The sonicvibes OSS routines we inherited really want
+ *	power of 2 buffers, so we have all those next to each other, then
+ *	512 byte regions for the recording wavecaches.  This ends up
+ *	wasting quite a bit of memory.  The only fixes I can see would be
+ *	getting a kernel allocator that could work in zones, or figuring out
+ *	just how to coerce the WP into doing what we want.
+ *
+ *	The indirection of the various registers means we have to spinlock
+ *	nearly all register accesses.  We have the main register indirection
+ *	like the wave cache, maestro registers, etc.  Then we have beasts
+ *	like the APU interface that is indirect registers gotten at through
+ *	the main maestro indirection.  Ouch.  We spinlock around the actual
+ *	ports on a per card basis.  This means spinlock activity at each IO
+ *	operation, but the only IO operation clusters are in non critical
+ *	paths and it makes the code far easier to follow.  Interrupts are
+ *	blocked while holding the locks because the int handler has to
+ *	get at some of them :(.  The mixer interface doesn't, however.
+ *	We also have an OSS state lock that is thrown around in a few
+ *	places.
+ */
+
+#define __SND_OSS_COMPAT__
+#include <sound/driver.h>
+#include <sound/pcm.h>
+#include <sound/mpu401.h>
+#include <sound/ac97_codec.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+#define CARD_NAME "ESS Maestro1/2"
+#define DRIVER_NAME "ES1968"
+
+EXPORT_NO_SYMBOLS;
+MODULE_DESCRIPTION("ESS Maestro");
+MODULE_CLASSES("{sound}");
+MODULE_LICENSE("GPL");
+MODULE_DEVICES("{{ESS,Maestro 2e},"
+               "{ESS,Maestro 2},"
+               "{ESS,Maestro 1},"
+               "{TerraTec,DMX}}");
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 1-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;	/* Enable this card */
+static int total_bufsize[SNDRV_CARDS] = { REPEAT_SNDRV(1024) };
+static int pcm_substreams_p[SNDRV_CARDS] = { REPEAT_SNDRV(4) };
+static int pcm_substreams_c[SNDRV_CARDS] = { REPEAT_SNDRV(1) };
+static int clock[SNDRV_CARDS] = { REPEAT_SNDRV(0) };
+static int use_pm[SNDRV_CARDS] = { REPEAT_SNDRV(2) };
+static int enable_mpu[SNDRV_CARDS] = { REPEAT_SNDRV(1) };
+
+MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard.");
+MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC);
+MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard.");
+MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC);
+MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard.");
+MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(total_bufsize, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(total_bufsize, "Total buffer size in kB.");
+MODULE_PARM_SYNTAX(total_bufsize, SNDRV_ENABLED ",allows:{{1,4096}},skill:advanced");
+MODULE_PARM(pcm_substreams_p, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(pcm_substreams_p, "PCM Playback substreams for " CARD_NAME " soundcard.");
+MODULE_PARM_SYNTAX(pcm_substreams_p, SNDRV_ENABLED ",allows:{{1,8}}");
+MODULE_PARM(pcm_substreams_c, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(pcm_substreams_c, "PCM Capture substreams for " CARD_NAME " soundcard.");
+MODULE_PARM_SYNTAX(pcm_substreams_c, SNDRV_ENABLED ",allows:{{0,8}}");
+MODULE_PARM(clock, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(clock, "Clock on " CARD_NAME " soundcard.  (0 = auto-detect)");
+MODULE_PARM_SYNTAX(clock, SNDRV_ENABLED);
+MODULE_PARM(use_pm, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(use_pm, "Toggle power-management.  (0 = off, 1 = on, 2 = auto)");
+MODULE_PARM_SYNTAX(use_pm, SNDRV_ENABLED ",allows:{{0,1,2}},skill:advanced");
+MODULE_PARM(enable_mpu, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(enable_mpu, "Enable MPU401.  (0 = off, 1 = on, 2 = auto)");
+MODULE_PARM_SYNTAX(enable_mpu, SNDRV_ENABLED "," SNDRV_BOOLEAN_TRUE_DESC);
+
+
+/* PCI Dev ID's */
+
+#ifndef PCI_VENDOR_ID_ESS
+#define PCI_VENDOR_ID_ESS	0x125D
+#endif
+
+#define PCI_VENDOR_ID_ESS_OLD	0x1285	/* Platform Tech, the people the ESS
+was bought form */
+
+#ifndef PCI_DEVICE_ID_ESS_M2E
+#define PCI_DEVICE_ID_ESS_M2E	0x1978
+#endif
+#ifndef PCI_DEVICE_ID_ESS_M2
+#define PCI_DEVICE_ID_ESS_M2	0x1968
+#endif
+#ifndef PCI_DEVICE_ID_ESS_M1
+#define PCI_DEVICE_ID_ESS_M1	0x0100
+#endif
+
+#define NR_APUS			64
+#define NR_APU_REGS		16
+
+/* NEC Versas ? */
+#define NEC_VERSA_SUBID1	0x80581033
+#define NEC_VERSA_SUBID2	0x803c1033
+
+/* Mode Flags */
+#define ESS_FMT_STEREO     	0x01
+#define ESS_FMT_16BIT      	0x02
+
+#define DAC_RUNNING		1
+#define ADC_RUNNING		2
+
+/* Values for the ESM_LEGACY_AUDIO_CONTROL */
+
+#define ESS_ENABLE_AUDIO	0x8000
+#define ESS_ENABLE_SERIAL_IRQ	0x4000
+#define IO_ADRESS_ALIAS		0x0020
+#define MPU401_IRQ_ENABLE	0x0010
+#define MPU401_IO_ENABLE	0x0008
+#define GAME_IO_ENABLE		0x0004
+#define FM_IO_ENABLE		0x0002
+#define SB_IO_ENABLE		0x0001
+
+/* Values for the ESM_CONFIG_A */
+
+#define PIC_SNOOP1		0x4000
+#define PIC_SNOOP2		0x2000
+#define SAFEGUARD		0x0800
+#define DMA_CLEAR		0x0700
+#define DMA_DDMA		0x0000
+#define DMA_TDMA		0x0100
+#define DMA_PCPCI		0x0200
+#define POST_WRITE		0x0080
+#define ISA_TIMING		0x0040
+#define SWAP_LR			0x0020
+#define SUBTR_DECODE		0x0002
+
+/* Values for the ESM_CONFIG_B */
+
+#define SPDIF_CONFB		0x0100
+#define HWV_CONFB		0x0080
+#define DEBOUNCE		0x0040
+#define GPIO_CONFB		0x0020
+#define CHI_CONFB		0x0010
+#define IDMA_CONFB		0x0008	/*undoc */
+#define MIDI_FIX		0x0004	/*undoc */
+#define IRQ_TO_ISA		0x0001	/*undoc */
+
+/* Values for Ring Bus Control B */
+#define	RINGB_2CODEC_ID_MASK	0x0003
+#define RINGB_DIS_VALIDATION	0x0008
+#define RINGB_EN_SPDIF		0x0010
+#define	RINGB_EN_2CODEC		0x0020
+#define RINGB_SING_BIT_DUAL	0x0040
+
+/* ****Port Adresses**** */
+
+/*   Write & Read */
+#define ESM_INDEX		0x02
+#define ESM_DATA		0x00
+
+/*   AC97 + RingBus */
+#define ESM_AC97_INDEX		0x30
+#define	ESM_AC97_DATA		0x32
+#define ESM_RING_BUS_DEST	0x34
+#define ESM_RING_BUS_CONTR_A	0x36
+#define ESM_RING_BUS_CONTR_B	0x38
+#define ESM_RING_BUS_SDO	0x3A
+
+/*   WaveCache*/
+#define WC_INDEX		0x10
+#define WC_DATA			0x12
+#define WC_CONTROL		0x14
+
+/*   ASSP*/
+#define ASSP_INDEX		0x80
+#define ASSP_MEMORY		0x82
+#define ASSP_DATA		0x84
+#define ASSP_CONTROL_A		0xA2
+#define ASSP_CONTROL_B		0xA4
+#define ASSP_CONTROL_C		0xA6
+#define ASSP_HOSTW_INDEX	0xA8
+#define ASSP_HOSTW_DATA		0xAA
+#define ASSP_HOSTW_IRQ		0xAC
+/* Midi */
+#define ESM_MPU401_PORT		0x98
+/* Others */
+#define ESM_PORT_HOST_IRQ	0x18
+
+#define IDR0_DATA_PORT		0x00
+#define IDR1_CRAM_POINTER	0x01
+#define IDR2_CRAM_DATA		0x02
+#define IDR3_WAVE_DATA		0x03
+#define IDR4_WAVE_PTR_LOW	0x04
+#define IDR5_WAVE_PTR_HI	0x05
+#define IDR6_TIMER_CTRL		0x06
+#define IDR7_WAVE_ROMRAM	0x07
+
+#define WRITEABLE_MAP		0xEFFFFF
+#define READABLE_MAP		0x64003F
+
+/* PCI Register */
+
+#define ESM_LEGACY_AUDIO_CONTROL 0x40
+#define ESM_ACPI_COMMAND	0x54
+#define ESM_CONFIG_A		0x50
+#define ESM_CONFIG_B		0x52
+#define ESM_DDMA		0x60
+
+/* Bob Bits */
+#define ESM_BOB_ENABLE		0x0001
+#define ESM_BOB_START		0x0001
+
+/* Host IRQ Control Bits */
+#define ESM_RESET_MAESTRO	0x8000
+#define ESM_RESET_DIRECTSOUND   0x4000
+#define ESM_HIRQ_ClkRun		0x0100
+#define ESM_HIRQ_HW_VOLUME	0x0040
+#define ESM_HIRQ_HARPO		0x0030	/* What's that? */
+#define ESM_HIRQ_ASSP		0x0010
+#define	ESM_HIRQ_DSIE		0x0004
+#define ESM_HIRQ_MPU401		0x0002
+#define ESM_HIRQ_SB		0x0001
+
+/* Host IRQ Status Bits */
+#define ESM_MPU401_IRQ		0x02
+#define ESM_SB_IRQ		0x01
+#define ESM_SOUND_IRQ		0x04
+#define	ESM_ASSP_IRQ		0x10
+#define ESM_HWVOL_IRQ		0x40
+
+#define ESS_SYSCLK		50000000
+#define ESM_BOB_FREQ 		200
+#define ESM_BOB_FREQ_MAX	800
+
+#define ESM_FREQ_ESM1  		(49152000L / 1024L)	/* default rate 48000 */
+#define ESM_FREQ_ESM2  		(50000000L / 1024L)
+
+/* APU Modes: reg 0x00, bit 4-7 */
+#define ESM_APU_MODE_SHIFT	4
+#define ESM_APU_MODE_MASK	(0xf << 4)
+#define	ESM_APU_OFF		0x00
+#define	ESM_APU_16BITLINEAR	0x01	/* 16-Bit Linear Sample Player */
+#define	ESM_APU_16BITSTEREO	0x02	/* 16-Bit Stereo Sample Player */
+#define	ESM_APU_8BITLINEAR	0x03	/* 8-Bit Linear Sample Player */
+#define	ESM_APU_8BITSTEREO	0x04	/* 8-Bit Stereo Sample Player */
+#define	ESM_APU_8BITDIFF	0x05	/* 8-Bit Differential Sample Playrer */
+#define	ESM_APU_DIGITALDELAY	0x06	/* Digital Delay Line */
+#define	ESM_APU_DUALTAP		0x07	/* Dual Tap Reader */
+#define	ESM_APU_CORRELATOR	0x08	/* Correlator */
+#define	ESM_APU_INPUTMIXER	0x09	/* Input Mixer */
+#define	ESM_APU_WAVETABLE	0x0A	/* Wave Table Mode */
+#define	ESM_APU_SRCONVERTOR	0x0B	/* Sample Rate Convertor */
+#define	ESM_APU_16BITPINGPONG	0x0C	/* 16-Bit Ping-Pong Sample Player */
+#define	ESM_APU_RESERVED1	0x0D	/* Reserved 1 */
+#define	ESM_APU_RESERVED2	0x0E	/* Reserved 2 */
+#define	ESM_APU_RESERVED3	0x0F	/* Reserved 3 */
+
+/* reg 0x00 */
+#define ESM_APU_FILTER_Q_SHIFT		0
+#define ESM_APU_FILTER_Q_MASK		(3 << 0)
+/* APU Filtey Q Control */
+#define ESM_APU_FILTER_LESSQ	0x00
+#define ESM_APU_FILTER_MOREQ	0x03
+
+#define ESM_APU_FILTER_TYPE_SHIFT	2
+#define ESM_APU_FILTER_TYPE_MASK	(3 << 2)
+#define ESM_APU_ENV_TYPE_SHIFT		8
+#define ESM_APU_ENV_TYPE_MASK		(3 << 8)
+#define ESM_APU_ENV_STATE_SHIFT		10
+#define ESM_APU_ENV_STATE_MASK		(3 << 10)
+#define ESM_APU_END_CURVE		(1 << 12)
+#define ESM_APU_INT_ON_LOOP		(1 << 13)
+#define ESM_APU_DMA_ENABLE		(1 << 14)
+
+/* reg 0x02 */
+#define ESM_APU_SUBMIX_GROUP_SHIRT	0
+#define ESM_APU_SUBMIX_GROUP_MASK	(7 << 0)
+#define ESM_APU_SUBMIX_MODE		(1 << 3)
+#define ESM_APU_6dB			(1 << 4)
+#define ESM_APU_DUAL_EFFECT		(1 << 5)
+#define ESM_APU_EFFECT_CHANNELS_SHIFT	6
+#define ESM_APU_EFFECT_CHANNELS_MASK	(3 << 6)
+
+/* reg 0x03 */
+#define ESM_APU_STEP_SIZE_MASK		0x0fff
+
+/* reg 0x04 */
+#define ESM_APU_PHASE_SHIFT		0
+#define ESM_APU_PHASE_MASK		(0xff << 0)
+#define ESM_APU_WAVE64K_PAGE_SHIFT	8	/* most 8bit of wave start offset */
+#define ESM_APU_WAVE64K_PAGE_MASK	(0xff << 8)
+
+/* reg 0x05 - wave start offset */
+/* reg 0x06 - wave end offset */
+/* reg 0x07 - wave loop length */
+
+/* reg 0x08 */
+#define ESM_APU_EFFECT_GAIN_SHIFT	0
+#define ESM_APU_EFFECT_GAIN_MASK	(0xff << 0)
+#define ESM_APU_TREMOLO_DEPTH_SHIFT	8
+#define ESM_APU_TREMOLO_DEPTH_MASK	(0xf << 8)
+#define ESM_APU_TREMOLO_RATE_SHIFT	12
+#define ESM_APU_TREMOLO_RATE_MASK	(0xf << 12)
+
+/* reg 0x09 */
+/* bit 0-7 amplitude dest? */
+#define ESM_APU_AMPLITUDE_NOW_SHIFT	8
+#define ESM_APU_AMPLITUDE_NOW_MASK	(0xff << 8)
+
+/* reg 0x0a */
+#define ESM_APU_POLAR_PAN_SHIFT		0
+#define ESM_APU_POLAR_PAN_MASK		(0x3f << 0)
+/* Polar Pan Control */
+#define	ESM_APU_PAN_CENTER_CIRCLE		0x00
+#define	ESM_APU_PAN_MIDDLE_RADIUS		0x01
+#define	ESM_APU_PAN_OUTSIDE_RADIUS		0x02
+
+#define ESM_APU_FILTER_TUNING_SHIFT	8
+#define ESM_APU_FILTER_TUNING_MASK	(0xff << 8)
+
+/* reg 0x0b */
+#define ESM_APU_DATA_SRC_A_SHIFT	0
+#define ESM_APU_DATA_SRC_A_MASK		(0x7f << 0)
+#define ESM_APU_INV_POL_A		(1 << 7)
+#define ESM_APU_DATA_SRC_B_SHIFT	8
+#define ESM_APU_DATA_SRC_B_MASK		(0x7f << 8)
+#define ESM_APU_INV_POL_B		(1 << 15)
+
+#define ESM_APU_VIBRATO_RATE_SHIFT	0
+#define ESM_APU_VIBRATO_RATE_MASK	(0xf << 0)
+#define ESM_APU_VIBRATO_DEPTH_SHIFT	4
+#define ESM_APU_VIBRATO_DEPTH_MASK	(0xf << 4)
+#define ESM_APU_VIBRATO_PHASE_SHIFT	8
+#define ESM_APU_VIBRATO_PHASE_MASK	(0xff << 8)
+
+/* reg 0x0c */
+#define ESM_APU_RADIUS_SELECT		(1 << 6)
+
+/* APU Filter Control */
+#define	ESM_APU_FILTER_2POLE_LOPASS	0x00
+#define	ESM_APU_FILTER_2POLE_BANDPASS	0x01
+#define	ESM_APU_FILTER_2POLE_HIPASS	0x02
+#define	ESM_APU_FILTER_1POLE_LOPASS	0x03
+#define	ESM_APU_FILTER_1POLE_HIPASS	0x04
+#define	ESM_APU_FILTER_OFF		0x05
+
+/* APU ATFP Type */
+#define	ESM_APU_ATFP_AMPLITUDE			0x00
+#define	ESM_APU_ATFP_TREMELO			0x01
+#define	ESM_APU_ATFP_FILTER			0x02
+#define	ESM_APU_ATFP_PAN			0x03
+
+/* APU ATFP Flags */
+#define	ESM_APU_ATFP_FLG_OFF			0x00
+#define	ESM_APU_ATFP_FLG_WAIT			0x01
+#define	ESM_APU_ATFP_FLG_DONE			0x02
+#define	ESM_APU_ATFP_FLG_INPROCESS		0x03
+
+
+/* capture mixing buffer size */
+#define ESM_MEM_ALIGN		0x1000
+#define ESM_MIXBUF_SIZE		0x400
+
+#define ESM_MODE_PLAY		0
+#define ESM_MODE_CAPTURE	1
+
+/* acpi states */
+enum {
+    ACPI_D0=0,
+    ACPI_D1,
+    ACPI_D2,
+    ACPI_D3
+};
+
+/* bits in the acpi masks */
+#define ACPI_12MHZ	( 1 << 15)
+#define ACPI_24MHZ	( 1 << 14)
+#define ACPI_978	( 1 << 13)
+#define ACPI_SPDIF	( 1 << 12)
+#define ACPI_GLUE	( 1 << 11)
+#define ACPI__10	( 1 << 10) /* reserved */
+#define ACPI_PCIINT	( 1 << 9)
+#define ACPI_HV		( 1 << 8) /* hardware volume */
+#define ACPI_GPIO	( 1 << 7)
+#define ACPI_ASSP	( 1 << 6)
+#define ACPI_SB		( 1 << 5) /* sb emul */
+#define ACPI_FM		( 1 << 4) /* fm emul */
+#define ACPI_RB		( 1 << 3) /* ringbus / aclink */
+#define ACPI_MIDI	( 1 << 2)
+#define ACPI_GP		( 1 << 1) /* game port */
+#define ACPI_WP		( 1 << 0) /* wave processor */
+
+#define ACPI_ALL	(0xffff)
+#define ACPI_SLEEP	(~(ACPI_SPDIF|ACPI_ASSP|ACPI_SB|ACPI_FM| \
+    ACPI_MIDI|ACPI_GP|ACPI_WP))
+#define ACPI_NONE	(ACPI__10)
+
+/* these masks indicate which units we care about at
+ which states */
+static u16 acpi_state_mask[] = {
+#ifdef TARGET_OS2
+    ACPI_ALL,
+    ACPI_SLEEP,
+    ACPI_SLEEP,
+    ACPI_NONE
+#else
+    [ACPI_D0] = ACPI_ALL,
+    [ACPI_D1] = ACPI_SLEEP,
+    [ACPI_D2] = ACPI_SLEEP,
+    [ACPI_D3] = ACPI_NONE
+#endif
+};
+
+
+typedef struct snd_es1968 es1968_t;
+typedef struct snd_esschan esschan_t;
+typedef struct snd_esm_memory esm_memory_t;
+
+/* APU use in the driver */
+enum snd_enum_apu_type {
+    ESM_APU_PCM_PLAY,
+    ESM_APU_PCM_CAPTURE,
+    ESM_APU_PCM_RATECONV,
+    ESM_APU_FREE
+};
+
+/* chip type */
+enum {
+    TYPE_MAESTRO, TYPE_MAESTRO2, TYPE_MAESTRO2E
+};
+
+/* DMA Hack! */
+struct snd_esm_memory {
+    struct snd_dma_buffer buf;
+    int empty;	/* status */
+    struct list_head list;
+};
+
+/* Playback Channel */
+struct snd_esschan {
+    int running;
+
+    u8 apu[4];
+    u8 apu_mode[4];
+
+    /* playback/capture pcm buffer */
+    esm_memory_t *memory;
+    /* capture mixer buffer */
+    esm_memory_t *mixbuf;
+
+    unsigned int hwptr;	/* current hw pointer in bytes */
+    unsigned int count;	/* sample counter in bytes */
+    unsigned int dma_size;	/* total buffer size in bytes */
+    unsigned int frag_size;	/* period size in bytes */
+    unsigned int wav_shift;
+    u16 base[4];		/* offset for ptr */
+
+    /* stereo/16bit flag */
+    unsigned char fmt;
+    int mode;	/* playback / capture */
+
+    int bob_freq;	/* required timer frequency */
+
+    snd_pcm_substream_t *substream;
+
+    /* linked list */
+    struct list_head list;
+
+#ifdef CONFIG_PM
+    u16 wc_map[4];
+#endif
+};
+
+struct snd_es1968 {
+    /* Module Config */
+    int total_bufsize;			/* in bytes */
+
+    int playback_streams, capture_streams;
+
+    unsigned int clock;		/* clock */
+    /* for clock measurement */
+    unsigned int in_measurement: 1;
+    unsigned int measure_apu;
+    unsigned int measure_lastpos;
+    unsigned int measure_count;
+
+    /* buffer */
+    struct snd_dma_buffer dma;
+
+    /* Resources... */
+    int irq;
+    unsigned long io_port;
+    int type;
+    struct pci_dev *pci;
+    snd_card_t *card;
+    snd_pcm_t *pcm;
+    int do_pm;		/* power-management enabled */
+
+    /* DMA memory block */
+    struct list_head buf_list;
+
+    /* ALSA Stuff */
+    ac97_t *ac97;
+    snd_kcontrol_t *master_switch; /* for h/w volume control */
+    snd_kcontrol_t *master_volume;
+
+    snd_rawmidi_t *rmidi;
+
+    spinlock_t reg_lock;
+    spinlock_t ac97_lock;
+    struct tasklet_struct hwvol_tq;
+    unsigned int in_suspend;
+
+    /* Maestro Stuff */
+    u16 maestro_map[32];
+    int bobclient;		/* active timer instancs */
+    int bob_freq;		/* timer frequency */
+    struct semaphore memory_mutex;	/* memory lock */
+
+    /* APU states */
+    unsigned char apu[NR_APUS];
+
+    /* active substreams */
+    struct list_head substream_list;
+    spinlock_t substream_lock;
+
+#ifdef CONFIG_PM
+    u16 apu_map[NR_APUS][NR_APU_REGS];
+#endif
+};
+
+static irqreturn_t snd_es1968_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+
+static struct pci_device_id snd_es1968_ids[] = {
+    /* Maestro 1 */
+    { 0x1285, 0x0100, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, TYPE_MAESTRO },
+    /* Maestro 2 */
+    { 0x125d, 0x1968, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, TYPE_MAESTRO2 },
+    /* Maestro 2E */
+    { 0x125d, 0x1978, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, TYPE_MAESTRO2E },
+    { 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, snd_es1968_ids);
+
+/* *********************
+ * Low Level Funcs!  *
+ *********************/
+
+/* no spinlock */
+static void __maestro_write(es1968_t *chip, u16 reg, u16 data)
+{
+    outw(reg, chip->io_port + ESM_INDEX);
+    outw(data, chip->io_port + ESM_DATA);
+    chip->maestro_map[reg] = data;
+}
+
+inline static void maestro_write(es1968_t *chip, u16 reg, u16 data)
+{
+    unsigned long flags;
+    spin_lock_irqsave(&chip->reg_lock, flags);
+    __maestro_write(chip, reg, data);
+    spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+/* no spinlock */
+static u16 __maestro_read(es1968_t *chip, u16 reg)
+{
+    if (READABLE_MAP & (1 << reg)) {
+        outw(reg, chip->io_port + ESM_INDEX);
+        chip->maestro_map[reg] = inw(chip->io_port + ESM_DATA);
+    }
+    return chip->maestro_map[reg];
+}
+
+inline static u16 maestro_read(es1968_t *chip, u16 reg)
+{
+    unsigned long flags;
+    u16 result;
+    spin_lock_irqsave(&chip->reg_lock, flags);
+    result = __maestro_read(chip, reg);
+    spin_unlock_irqrestore(&chip->reg_lock, flags);
+    return result;
+}
+
+/* Wait for the codec bus to be free */
+static int snd_es1968_ac97_wait(es1968_t *chip)
+{
+    int timeout = 100000;
+
+    while (timeout-- > 0) {
+        if (!(inb(chip->io_port + ESM_AC97_INDEX) & 1))
+            return 0;
+        cond_resched();
+    }
+    snd_printd("es1968: ac97 timeout\n");
+    return 1; /* timeout */
+}
+
+static void snd_es1968_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short val)
+{
+    es1968_t *chip = ac97->private_data;
+    unsigned long flags;
+
+    snd_es1968_ac97_wait(chip);
+
+    /* Write the bus */
+    spin_lock_irqsave(&chip->ac97_lock, flags);
+    outw(val, chip->io_port + ESM_AC97_DATA);
+    /*msleep(1);*/
+    outb(reg, chip->io_port + ESM_AC97_INDEX);
+    /*msleep(1);*/
+    spin_unlock_irqrestore(&chip->ac97_lock, flags);
+}
+
+static unsigned short snd_es1968_ac97_read(ac97_t *ac97, unsigned short reg)
+{
+    u16 data = 0;
+    es1968_t *chip = ac97->private_data;
+    unsigned long flags;
+
+    snd_es1968_ac97_wait(chip);
+
+    spin_lock_irqsave(&chip->ac97_lock, flags);
+    outb(reg | 0x80, chip->io_port + ESM_AC97_INDEX);
+    /*msleep(1);*/
+
+    if (! snd_es1968_ac97_wait(chip)) {
+        data = inw(chip->io_port + ESM_AC97_DATA);
+        /*msleep(1);*/
+    }
+    spin_unlock_irqrestore(&chip->ac97_lock, flags);
+
+    return data;
+}
+
+/* no spinlock */
+static void apu_index_set(es1968_t *chip, u16 index)
+{
+    int i;
+    __maestro_write(chip, IDR1_CRAM_POINTER, index);
+    for (i = 0; i < 1000; i++)
+        if (__maestro_read(chip, IDR1_CRAM_POINTER) == index)
+            return;
+    snd_printd("es1968: APU register select failed. (Timeout)\n");
+}
+
+/* no spinlock */
+static void apu_data_set(es1968_t *chip, u16 data)
+{
+    int i;
+    for (i = 0; i < 1000; i++) {
+        if (__maestro_read(chip, IDR0_DATA_PORT) == data)
+            return;
+        __maestro_write(chip, IDR0_DATA_PORT, data);
+    }
+    snd_printd("es1968: APU register set probably failed (Timeout)!\n");
+}
+
+/* no spinlock */
+static void __apu_set_register(es1968_t *chip, u16 channel, u8 reg, u16 data)
+{
+    snd_assert(channel < NR_APUS, return);
+#ifdef CONFIG_PM
+    chip->apu_map[channel][reg] = data;
+#endif
+    reg |= (channel << 4);
+    apu_index_set(chip, reg);
+    apu_data_set(chip, data);
+}
+
+inline static void apu_set_register(es1968_t *chip, u16 channel, u8 reg, u16 data)
+{
+    unsigned long flags;
+    spin_lock_irqsave(&chip->reg_lock, flags);
+    __apu_set_register(chip, channel, reg, data);
+    spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+static u16 __apu_get_register(es1968_t *chip, u16 channel, u8 reg)
+{
+    snd_assert(channel < NR_APUS, return 0);
+    reg |= (channel << 4);
+    apu_index_set(chip, reg);
+    return __maestro_read(chip, IDR0_DATA_PORT);
+}
+
+inline static u16 apu_get_register(es1968_t *chip, u16 channel, u8 reg)
+{
+    unsigned long flags;
+    u16 v;
+    spin_lock_irqsave(&chip->reg_lock, flags);
+    v = __apu_get_register(chip, channel, reg);
+    spin_unlock_irqrestore(&chip->reg_lock, flags);
+    return v;
+}
+
+#if 0 /* ASSP is not supported */
+
+static void assp_set_register(es1968_t *chip, u32 reg, u32 value)
+{
+    unsigned long flags;
+
+    spin_lock_irqsave(&chip->reg_lock, flags);
+    outl(reg, chip->io_port + ASSP_INDEX);
+    outl(value, chip->io_port + ASSP_DATA);
+    spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+static u32 assp_get_register(es1968_t *chip, u32 reg)
+{
+    unsigned long flags;
+    u32 value;
+
+    spin_lock_irqsave(&chip->reg_lock, flags);
+    outl(reg, chip->io_port + ASSP_INDEX);
+    value = inl(chip->io_port + ASSP_DATA);
+    spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+    return value;
+}
+
+#endif
+
+static void wave_set_register(es1968_t *chip, u16 reg, u16 value)
+{
+    unsigned long flags;
+
+    spin_lock_irqsave(&chip->reg_lock, flags);
+    outw(reg, chip->io_port + WC_INDEX);
+    outw(value, chip->io_port + WC_DATA);
+    spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+static u16 wave_get_register(es1968_t *chip, u16 reg)
+{
+    unsigned long flags;
+    u16 value;
+
+    spin_lock_irqsave(&chip->reg_lock, flags);
+    outw(reg, chip->io_port + WC_INDEX);
+    value = inw(chip->io_port + WC_DATA);
+    spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+    return value;
+}
+
+/* *******************
+ * Bob the Timer!  *
+ *******************/
+
+static void snd_es1968_bob_stop(es1968_t *chip)
+{
+    u16 reg;
+
+    reg = __maestro_read(chip, 0x11);
+    reg &= ~ESM_BOB_ENABLE;
+    __maestro_write(chip, 0x11, reg);
+    reg = __maestro_read(chip, 0x17);
+    reg &= ~ESM_BOB_START;
+    __maestro_write(chip, 0x17, reg);
+}
+
+static void snd_es1968_bob_start(es1968_t *chip)
+{
+    int prescale;
+    int divide;
+
+    /* compute ideal interrupt frequency for buffer size & play rate */
+    /* first, find best prescaler value to match freq */
+    for (prescale = 5; prescale < 12; prescale++)
+        if (chip->bob_freq > (ESS_SYSCLK >> (prescale + 9)))
+            break;
+
+    /* next, back off prescaler whilst getting divider into optimum range */
+    divide = 1;
+    while ((prescale > 5) && (divide < 32)) {
+        prescale--;
+        divide <<= 1;
+    }
+    divide >>= 1;
+
+    /* now fine-tune the divider for best match */
+    for (; divide < 31; divide++)
+        if (chip->bob_freq >
+            ((ESS_SYSCLK >> (prescale + 9)) / (divide + 1))) break;
+
+    /* divide = 0 is illegal, but don't let prescale = 4! */
+    if (divide == 0) {
+        divide++;
+        if (prescale > 5)
+            prescale--;
+    } else if (divide > 1)
+        divide--;
+
+    __maestro_write(chip, 6, 0x9000 | (prescale << 5) | divide);	/* set reg */
+
+    /* Now set IDR 11/17 */
+    __maestro_write(chip, 0x11, __maestro_read(chip, 0x11) | 1);
+    __maestro_write(chip, 0x17, __maestro_read(chip, 0x17) | 1);
+}
+
+/* call with substream spinlock */
+static void snd_es1968_bob_inc(es1968_t *chip, int freq)
+{
+    chip->bobclient++;
+    if (chip->bobclient == 1) {
+        chip->bob_freq = freq;
+        snd_es1968_bob_start(chip);
+    } else if (chip->bob_freq < freq) {
+        snd_es1968_bob_stop(chip);
+        chip->bob_freq = freq;
+        snd_es1968_bob_start(chip);
+    }
+}
+
+/* call with substream spinlock */
+static void snd_es1968_bob_dec(es1968_t *chip)
+{
+    chip->bobclient--;
+    if (chip->bobclient <= 0)
+        snd_es1968_bob_stop(chip);
+    else if (chip->bob_freq > ESM_BOB_FREQ) {
+        /* check reduction of timer frequency */
+        struct list_head *p;
+        int max_freq = ESM_BOB_FREQ;
+        list_for_each(p, &chip->substream_list) {
+            esschan_t *es = list_entry(p, esschan_t, list);
+            if (max_freq < es->bob_freq)
+                max_freq = es->bob_freq;
+        }
+        if (max_freq != chip->bob_freq) {
+            snd_es1968_bob_stop(chip);
+            chip->bob_freq = max_freq;
+            snd_es1968_bob_start(chip);
+        }
+    }
+}
+
+static int
+snd_es1968_calc_bob_rate(es1968_t *chip, esschan_t *es,
+                         snd_pcm_runtime_t *runtime)
+{
+    /* we acquire 4 interrupts per period for precise control.. */
+    int freq = runtime->rate * 4;
+    if (es->fmt & ESS_FMT_STEREO)
+        freq <<= 1;
+    if (es->fmt & ESS_FMT_16BIT)
+        freq <<= 1;
+    freq /= es->frag_size;
+    if (freq < ESM_BOB_FREQ)
+        freq = ESM_BOB_FREQ;
+    else if (freq > ESM_BOB_FREQ_MAX)
+        freq = ESM_BOB_FREQ_MAX;
+    return freq;
+}
+
+
+/*************
+ *  PCM Part *
+ *************/
+
+static u32 snd_es1968_compute_rate(es1968_t *chip, u32 freq)
+{
+    u32 rate = (freq << 16) / chip->clock;
+#if 0 /* XXX: do we need this? */
+    if (rate > 0x10000)
+        rate = 0x10000;
+#endif
+    return rate;
+}
+
+/* get current pointer */
+inline static unsigned int
+snd_es1968_get_dma_ptr(es1968_t *chip, esschan_t *es)
+{
+    unsigned int offset;
+
+    offset = apu_get_register(chip, es->apu[0], 5);
+
+    offset -= es->base[0];
+
+    return (offset & 0xFFFE);	/* hardware is in words */
+}
+
+static void snd_es1968_apu_set_freq(es1968_t *chip, int apu, int freq)
+{
+    apu_set_register(chip, apu, 2,
+                     (apu_get_register(chip, apu, 2) & 0x00FF) |
+                     ((freq & 0xff) << 8) | 0x10);
+    apu_set_register(chip, apu, 3, freq >> 8);
+}
+
+/* spin lock held */
+inline static void snd_es1968_trigger_apu(es1968_t *esm, int apu, int mode)
+{
+    /* set the APU mode */
+    __apu_set_register(esm, apu, 0,
+                       (__apu_get_register(esm, apu, 0) & 0xff0f) |
+                       (mode << 4));
+}
+
+static void snd_es1968_pcm_start(es1968_t *chip, esschan_t *es)
+{
+    spin_lock(&chip->reg_lock);
+    __apu_set_register(chip, es->apu[0], 5, es->base[0]);
+    snd_es1968_trigger_apu(chip, es->apu[0], es->apu_mode[0]);
+    if (es->mode == ESM_MODE_CAPTURE) {
+        __apu_set_register(chip, es->apu[2], 5, es->base[2]);
+        snd_es1968_trigger_apu(chip, es->apu[2], es->apu_mode[2]);
+    }
+    if (es->fmt & ESS_FMT_STEREO) {
+        __apu_set_register(chip, es->apu[1], 5, es->base[1]);
+        snd_es1968_trigger_apu(chip, es->apu[1], es->apu_mode[1]);
+        if (es->mode == ESM_MODE_CAPTURE) {
+            __apu_set_register(chip, es->apu[3], 5, es->base[3]);
+            snd_es1968_trigger_apu(chip, es->apu[3], es->apu_mode[3]);
+        }
+    }
+    spin_unlock(&chip->reg_lock);
+}
+
+static void snd_es1968_pcm_stop(es1968_t *chip, esschan_t *es)
+{
+    spin_lock(&chip->reg_lock);
+    snd_es1968_trigger_apu(chip, es->apu[0], 0);
+    snd_es1968_trigger_apu(chip, es->apu[1], 0);
+    if (es->mode == ESM_MODE_CAPTURE) {
+        snd_es1968_trigger_apu(chip, es->apu[2], 0);
+        snd_es1968_trigger_apu(chip, es->apu[3], 0);
+    }
+    spin_unlock(&chip->reg_lock);
+}
+
+/* set the wavecache control reg */
+static void snd_es1968_program_wavecache(es1968_t *chip, esschan_t *es,
+                                         int channel, u32 addr, int capture)
+{
+    u32 tmpval = (addr - 0x10) & 0xFFF8;
+
+    if (! capture) {
+        if (!(es->fmt & ESS_FMT_16BIT))
+            tmpval |= 4;	/* 8bit */
+        if (es->fmt & ESS_FMT_STEREO)
+            tmpval |= 2;	/* stereo */
+    }
+
+    /* set the wavecache control reg */
+    wave_set_register(chip, es->apu[channel] << 3, tmpval);
+
+#ifdef CONFIG_PM
+    es->wc_map[channel] = tmpval;
+#endif
+}
+
+
+static void snd_es1968_playback_setup(es1968_t *chip, esschan_t *es,
+                                      snd_pcm_runtime_t *runtime)
+{
+    u32 pa;
+    int high_apu = 0;
+    int channel, apu;
+    int i, size;
+    unsigned long flags;
+    u32 freq;
+
+    size = es->dma_size >> es->wav_shift;
+
+    if (es->fmt & ESS_FMT_STEREO)
+        high_apu++;
+
+    for (channel = 0; channel <= high_apu; channel++) {
+        apu = es->apu[channel];
+
+        snd_es1968_program_wavecache(chip, es, channel, es->memory->buf.addr, 0);
+
+        /* Offset to PCMBAR */
+        pa = es->memory->buf.addr;
+        pa -= chip->dma.addr;
+        pa >>= 1;	/* words */
+
+        pa |= 0x00400000;	/* System RAM (Bit 22) */
+
+        if (es->fmt & ESS_FMT_STEREO) {
+            /* Enable stereo */
+            if (channel)
+                pa |= 0x00800000;	/* (Bit 23) */
+            if (es->fmt & ESS_FMT_16BIT)
+                pa >>= 1;
+        }
+
+        /* base offset of dma calcs when reading the pointer
+         on this left one */
+        es->base[channel] = pa & 0xFFFF;
+
+        for (i = 0; i < 16; i++)
+            apu_set_register(chip, apu, i, 0x0000);
+
+        /* Load the buffer into the wave engine */
+        apu_set_register(chip, apu, 4, ((pa >> 16) & 0xFF) << 8);
+        apu_set_register(chip, apu, 5, pa & 0xFFFF);
+        apu_set_register(chip, apu, 6, (pa + size) & 0xFFFF);
+        /* setting loop == sample len */
+        apu_set_register(chip, apu, 7, size);
+
+        /* clear effects/env.. */
+        apu_set_register(chip, apu, 8, 0x0000);
+        /* set amp now to 0xd0 (?), low byte is 'amplitude dest'? */
+        apu_set_register(chip, apu, 9, 0xD000);
+
+        /* clear routing stuff */
+        apu_set_register(chip, apu, 11, 0x0000);
+        /* dma on, no envelopes, filter to all 1s) */
+        apu_set_register(chip, apu, 0, 0x400F);
+
+        if (es->fmt & ESS_FMT_16BIT)
+            es->apu_mode[channel] = ESM_APU_16BITLINEAR;
+        else
+            es->apu_mode[channel] = ESM_APU_8BITLINEAR;
+
+        if (es->fmt & ESS_FMT_STEREO) {
+            /* set panning: left or right */
+            /* Check: different panning. On my Canyon 3D Chipset the
+             Channels are swapped. I don't know, about the output
+             to the SPDif Link. Perhaps you have to change this
+             and not the APU Regs 4-5. */
+            apu_set_register(chip, apu, 10,
+                             0x8F00 | (channel ? 0 : 0x10));
+            es->apu_mode[channel] += 1;	/* stereo */
+        } else
+            apu_set_register(chip, apu, 10, 0x8F08);
+    }
+
+    spin_lock_irqsave(&chip->reg_lock, flags);
+    /* clear WP interrupts */
+    outw(1, chip->io_port + 0x04);
+    /* enable WP ints */
+    outw(inw(chip->io_port + ESM_PORT_HOST_IRQ) | ESM_HIRQ_DSIE, chip->io_port + ESM_PORT_HOST_IRQ);
+    spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+    freq = runtime->rate;
+    /* set frequency */
+    if (freq > 48000)
+        freq = 48000;
+    if (freq < 4000)
+        freq = 4000;
+
+    /* hmmm.. */
+    if (!(es->fmt & ESS_FMT_16BIT) && !(es->fmt & ESS_FMT_STEREO))
+        freq >>= 1;
+
+    freq = snd_es1968_compute_rate(chip, freq);
+
+    /* Load the frequency, turn on 6dB */
+    snd_es1968_apu_set_freq(chip, es->apu[0], freq);
+    snd_es1968_apu_set_freq(chip, es->apu[1], freq);
+}
+
+
+static void init_capture_apu(es1968_t *chip, esschan_t *es, int channel,
+                             unsigned int pa, unsigned int bsize,
+                             int mode, int route)
+{
+    int i, apu = es->apu[channel];
+
+    es->apu_mode[channel] = mode;
+
+    /* set the wavecache control reg */
+    snd_es1968_program_wavecache(chip, es, channel, pa, 1);
+
+    /* Offset to PCMBAR */
+    pa -= chip->dma.addr;
+    pa >>= 1;	/* words */
+
+    /* base offset of dma calcs when reading the pointer
+     on this left one */
+    es->base[channel] = pa & 0xFFFF;
+    pa |= 0x00400000;	/* bit 22 -> System RAM */
+
+    /* Begin loading the APU */
+    for (i = 0; i < 16; i++)
+        apu_set_register(chip, apu, i, 0x0000);
+
+    /* need to enable subgroups.. and we should probably
+     have different groups for different /dev/dsps..  */
+    apu_set_register(chip, apu, 2, 0x8);
+
+    /* Load the buffer into the wave engine */
+    apu_set_register(chip, apu, 4, ((pa >> 16) & 0xFF) << 8);
+    apu_set_register(chip, apu, 5, pa & 0xFFFF);
+    apu_set_register(chip, apu, 6, (pa + bsize) & 0xFFFF);
+    apu_set_register(chip, apu, 7, bsize);
+    /* clear effects/env.. */
+    apu_set_register(chip, apu, 8, 0x00F0);
+    /* amplitude now?  sure.  why not.  */
+    apu_set_register(chip, apu, 9, 0x0000);
+    /* set filter tune, radius, polar pan */
+    apu_set_register(chip, apu, 10, 0x8F08);
+    /* route input */
+    apu_set_register(chip, apu, 11, route);
+    /* dma on, no envelopes, filter to all 1s) */
+    apu_set_register(chip, apu, 0, 0x400F);
+}
+
+
+static void snd_es1968_capture_setup(es1968_t *chip, esschan_t *es,
+                                     snd_pcm_runtime_t *runtime)
+{
+    int size;
+    u32 freq;
+    unsigned long flags;
+
+    size = es->dma_size >> es->wav_shift;
+
+    /* APU assignments:
+     0 = mono/left SRC
+     1 = right SRC
+     2 = mono/left Input Mixer
+     3 = right Input Mixer
+     */
+    /* data seems to flow from the codec, through an apu into
+     the 'mixbuf' bit of page, then through the SRC apu
+     and out to the real 'buffer'.  ok.  sure.  */
+
+    /* input mixer (left/mono) */
+    /* parallel in crap, see maestro reg 0xC [8-11] */
+    init_capture_apu(chip, es, 2,
+                     es->mixbuf->buf.addr, ESM_MIXBUF_SIZE/4, /* in words */
+                     ESM_APU_INPUTMIXER, 0x14);
+    /* SRC (left/mono); get input from inputing apu */
+    init_capture_apu(chip, es, 0, es->memory->buf.addr, size,
+                     ESM_APU_SRCONVERTOR, es->apu[2]);
+    if (es->fmt & ESS_FMT_STEREO) {
+        /* input mixer (right) */
+        init_capture_apu(chip, es, 3,
+                         es->mixbuf->buf.addr + ESM_MIXBUF_SIZE/2,
+                         ESM_MIXBUF_SIZE/4, /* in words */
+                         ESM_APU_INPUTMIXER, 0x15);
+        /* SRC (right) */
+        init_capture_apu(chip, es, 1,
+                         es->memory->buf.addr + size*2, size,
+                         ESM_APU_SRCONVERTOR, es->apu[3]);
+    }
+    freq = runtime->rate;
+    /* Sample Rate conversion APUs don't like 0x10000 for their rate */
+    if (freq > 47999)
+        freq = 47999;
+    if (freq < 4000)
+        freq = 4000;
+
+    freq = snd_es1968_compute_rate(chip, freq);
+
+    /* Load the frequency, turn on 6dB */
+    snd_es1968_apu_set_freq(chip, es->apu[0], freq);
+    snd_es1968_apu_set_freq(chip, es->apu[1], freq);
+
+    /* fix mixer rate at 48khz.  and its _must_ be 0x10000. */
+    freq = 0x10000;
+    snd_es1968_apu_set_freq(chip, es->apu[2], freq);
+    snd_es1968_apu_set_freq(chip, es->apu[3], freq);
+
+    spin_lock_irqsave(&chip->reg_lock, flags);
+    /* clear WP interrupts */
+    outw(1, chip->io_port + 0x04);
+    /* enable WP ints */
+    outw(inw(chip->io_port + ESM_PORT_HOST_IRQ) | ESM_HIRQ_DSIE, chip->io_port + ESM_PORT_HOST_IRQ);
+    spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+/*******************
+ *  ALSA Interface *
+ *******************/
+
+static int snd_es1968_pcm_prepare(snd_pcm_substream_t *substream)
+{
+    es1968_t *chip = snd_pcm_substream_chip(substream);
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    esschan_t *es = runtime->private_data;
+
+    es->dma_size = snd_pcm_lib_buffer_bytes(substream);
+    es->frag_size = snd_pcm_lib_period_bytes(substream);
+
+    es->wav_shift = 1; /* maestro handles always 16bit */
+    es->fmt = 0;
+    if (snd_pcm_format_width(runtime->format) == 16)
+        es->fmt |= ESS_FMT_16BIT;
+    if (runtime->channels > 1) {
+        es->fmt |= ESS_FMT_STEREO;
+        if (es->fmt & ESS_FMT_16BIT) /* 8bit is already word shifted */
+            es->wav_shift++;
+    }
+    es->bob_freq = snd_es1968_calc_bob_rate(chip, es, runtime);
+
+    switch (es->mode) {
+    case ESM_MODE_PLAY:
+        snd_es1968_playback_setup(chip, es, runtime);
+        break;
+    case ESM_MODE_CAPTURE:
+        snd_es1968_capture_setup(chip, es, runtime);
+        break;
+    }
+
+    return 0;
+}
+
+static int snd_es1968_pcm_trigger(snd_pcm_substream_t *substream, int cmd)
+{
+    es1968_t *chip = snd_pcm_substream_chip(substream);
+    esschan_t *es = substream->runtime->private_data;
+
+    spin_lock(&chip->substream_lock);
+    switch (cmd) {
+    case SNDRV_PCM_TRIGGER_START:
+    case SNDRV_PCM_TRIGGER_RESUME:
+        if (es->running)
+            break;
+        snd_es1968_bob_inc(chip, es->bob_freq);
+        es->count = 0;
+        es->hwptr = 0;
+        snd_es1968_pcm_start(chip, es);
+        es->running = 1;
+        break;
+    case SNDRV_PCM_TRIGGER_STOP:
+    case SNDRV_PCM_TRIGGER_SUSPEND:
+        if (! es->running)
+            break;
+        snd_es1968_pcm_stop(chip, es);
+        es->running = 0;
+        snd_es1968_bob_dec(chip);
+        break;
+    }
+    spin_unlock(&chip->substream_lock);
+    return 0;
+}
+
+static snd_pcm_uframes_t snd_es1968_pcm_pointer(snd_pcm_substream_t *substream)
+{
+    es1968_t *chip = snd_pcm_substream_chip(substream);
+    esschan_t *es = substream->runtime->private_data;
+    unsigned int ptr;
+
+    ptr = snd_es1968_get_dma_ptr(chip, es) << es->wav_shift;
+
+    return bytes_to_frames(substream->runtime, ptr % es->dma_size);
+}
+
+static snd_pcm_hardware_t snd_es1968_playback = {
+    /*	info:		  */	(SNDRV_PCM_INFO_MMAP |
+                                 SNDRV_PCM_INFO_MMAP_VALID |
+                                 SNDRV_PCM_INFO_INTERLEAVED |
+                                 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+                                 /*SNDRV_PCM_INFO_PAUSE |*/
+                                 SNDRV_PCM_INFO_RESUME),
+                                 /*	formats:	  */	SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+                                 /*	rates:		  */	SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+                                 /*	rate_min:	  */	4000,
+                                 /*	rate_max:	  */	48000,
+                                 /*	channels_min:	  */	1,
+                                 /*	channels_max:	  */	2,
+                                 /*	buffer_bytes_max: */	65536,
+                                 /*	period_bytes_min: */	256,
+                                 /*	period_bytes_max: */	65536,
+                                 /*	periods_min:	  */	1,
+                                 /*	periods_max:	  */	1024,
+                                 /*	fifo_size:	  */	0,
+};
+
+static snd_pcm_hardware_t snd_es1968_capture = {
+    /*	info:		  */	(SNDRV_PCM_INFO_NONINTERLEAVED |
+                                 SNDRV_PCM_INFO_MMAP |
+                                 SNDRV_PCM_INFO_MMAP_VALID |
+                                 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+                                 /*SNDRV_PCM_INFO_PAUSE |*/
+                                 SNDRV_PCM_INFO_RESUME),
+                                 /*	formats:	  */	/*SNDRV_PCM_FMTBIT_U8 |*/ SNDRV_PCM_FMTBIT_S16_LE,
+                                 /*	rates:		  */	SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+                                 /*	rate_min:	  */	4000,
+                                 /*	rate_max:	  */	48000,
+                                 /*	channels_min:	  */	1,
+                                 /*	channels_max:	  */	2,
+                                 /*	buffer_bytes_max: */	65536,
+                                 /*	period_bytes_min: */	256,
+                                 /*	period_bytes_max: */	65536,
+                                 /*	periods_min:	  */	1,
+                                 /*	periods_max:	  */	1024,
+                                 /*	fifo_size:	  */	0,
+};
+
+/* *************************
+ * DMA memory management *
+ *************************/
+
+/* Because the Maestro can only take addresses relative to the PCM base address
+ register :( */
+
+static int calc_available_memory_size(es1968_t *chip)
+{
+    struct list_head *p;
+    int max_size = 0;
+
+    down(&chip->memory_mutex);
+    list_for_each(p, &chip->buf_list) {
+        esm_memory_t *buf = list_entry(p, esm_memory_t, list);
+        if (buf->empty && buf->buf.bytes > max_size)
+            max_size = buf->buf.bytes;
+    }
+    up(&chip->memory_mutex);
+    if (max_size >= 128*1024)
+        max_size = 127*1024;
+    return max_size;
+}
+
+/* allocate a new memory chunk with the specified size */
+static esm_memory_t *snd_es1968_new_memory(es1968_t *chip, int size)
+{
+    esm_memory_t *buf;
+    struct list_head *p;
+
+    size = ((size + ESM_MEM_ALIGN - 1) / ESM_MEM_ALIGN) * ESM_MEM_ALIGN;
+    down(&chip->memory_mutex);
+    list_for_each(p, &chip->buf_list) {
+        buf = list_entry(p, esm_memory_t, list);
+        if (buf->empty && buf->buf.bytes >= size)
+            goto __found;
+    }
+    up(&chip->memory_mutex);
+    return NULL;
+
+__found:
+    if (buf->buf.bytes > size) {
+        esm_memory_t *chunk = kmalloc(sizeof(*chunk), GFP_KERNEL);
+        if (chunk == NULL) {
+            up(&chip->memory_mutex);
+            return NULL;
+        }
+        chunk->buf = buf->buf;
+        chunk->buf.bytes -= size;
+        chunk->buf.area += size;
+        chunk->buf.addr += size;
+        chunk->empty = 1;
+        buf->buf.bytes = size;
+        list_add(&chunk->list, &buf->list);
+    }
+    buf->empty = 0;
+    up(&chip->memory_mutex);
+    return buf;
+}
+
+/* free a memory chunk */
+static void snd_es1968_free_memory(es1968_t *chip, esm_memory_t *buf)
+{
+    esm_memory_t *chunk;
+
+    down(&chip->memory_mutex);
+    buf->empty = 1;
+    if (buf->list.prev != &chip->buf_list) {
+        chunk = list_entry(buf->list.prev, esm_memory_t, list);
+        if (chunk->empty) {
+            chunk->buf.bytes += buf->buf.bytes;
+            list_del(&buf->list);
+            kfree(buf);
+            buf = chunk;
+        }
+    }
+    if (buf->list.next != &chip->buf_list) {
+        chunk = list_entry(buf->list.next, esm_memory_t, list);
+        if (chunk->empty) {
+            buf->buf.bytes += chunk->buf.bytes;
+            list_del(&chunk->list);
+            kfree(chunk);
+        }
+    }
+    up(&chip->memory_mutex);
+}
+
+static void snd_es1968_free_dmabuf(es1968_t *chip)
+{
+    struct list_head *p;
+
+    if (! chip->dma.area)
+        return;
+    snd_dma_reserve_buf(&chip->dma, snd_dma_pci_buf_id(chip->pci));
+    while ((p = chip->buf_list.next) != &chip->buf_list) {
+        esm_memory_t *chunk = list_entry(p, esm_memory_t, list);
+        list_del(p);
+        kfree(chunk);
+    }
+}
+
+static int __devinit
+snd_es1968_init_dmabuf(es1968_t *chip)
+{
+	int err;
+	esm_memory_t *chunk;
+
+	chip->dma.dev.type = SNDRV_DMA_TYPE_DEV;
+	chip->dma.dev.dev = snd_dma_pci_data(chip->pci);
+	if (! snd_dma_get_reserved_buf(&chip->dma, snd_dma_pci_buf_id(chip->pci))) {
+		err = snd_dma_alloc_pages_fallback(SNDRV_DMA_TYPE_DEV,
+						   snd_dma_pci_data(chip->pci),
+						   chip->total_bufsize, &chip->dma);
+		if (err < 0 || ! chip->dma.area) {
+			snd_printk("es1968: can't allocate dma pages for size %d\n",
+				   chip->total_bufsize);
+			return -ENOMEM;
+		}
+		if ((chip->dma.addr + chip->dma.bytes - 1) & ~((1 << 28) - 1)) {
+			snd_dma_free_pages(&chip->dma);
+			snd_printk("es1968: DMA buffer beyond 256MB.\n");
+			return -ENOMEM;
+		}
+	}
+
+	INIT_LIST_HEAD(&chip->buf_list);
+	/* allocate an empty chunk */
+	chunk = kmalloc(sizeof(*chunk), GFP_KERNEL);
+	if (chunk == NULL) {
+		snd_es1968_free_dmabuf(chip);
+		return -ENOMEM;
+	}
+	memset(chip->dma.area, 0, ESM_MEM_ALIGN);
+	chunk->buf = chip->dma;
+	chunk->buf.area += ESM_MEM_ALIGN;
+	chunk->buf.addr += ESM_MEM_ALIGN;
+	chunk->buf.bytes -= ESM_MEM_ALIGN;
+	chunk->empty = 1;
+	list_add(&chunk->list, &chip->buf_list);
+
+	return 0;
+}
+
+/* setup the dma_areas */
+/* buffer is extracted from the pre-allocated memory chunk */
+static int snd_es1968_hw_params(snd_pcm_substream_t *substream,
+                                snd_pcm_hw_params_t *hw_params)
+{
+    es1968_t *chip = snd_pcm_substream_chip(substream);
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    esschan_t *chan = runtime->private_data;
+    int size = params_buffer_bytes(hw_params);
+
+    if (chan->memory) {
+        if (chan->memory->buf.bytes >= size) {
+            runtime->dma_bytes = size;
+            return 0;
+        }
+        snd_es1968_free_memory(chip, chan->memory);
+    }
+    chan->memory = snd_es1968_new_memory(chip, size);
+    if (chan->memory == NULL) {
+        // snd_printd("cannot allocate dma buffer: size = %d\n", size);
+        return -ENOMEM;
+    }
+    snd_pcm_set_runtime_buffer(substream, &chan->memory->buf);
+    return 1; /* area was changed */
+}
+
+/* remove dma areas if allocated */
+static int snd_es1968_hw_free(snd_pcm_substream_t * substream)
+{
+    es1968_t *chip = snd_pcm_substream_chip(substream);
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    esschan_t *chan;
+
+    if (runtime->private_data == NULL)
+        return 0;
+    chan = runtime->private_data;
+    if (chan->memory) {
+        snd_es1968_free_memory(chip, chan->memory);
+        chan->memory = NULL;
+    }
+    return 0;
+}
+
+
+/*
+ * allocate APU pair
+ */
+static int snd_es1968_alloc_apu_pair(es1968_t *chip, int type)
+{
+    int apu;
+
+    for (apu = 0; apu < NR_APUS; apu += 2) {
+        if (chip->apu[apu] == ESM_APU_FREE &&
+            chip->apu[apu + 1] == ESM_APU_FREE) {
+            chip->apu[apu] = chip->apu[apu + 1] = type;
+            return apu;
+        }
+    }
+    return -EBUSY;
+}
+
+/*
+ * release APU pair
+ */
+static void snd_es1968_free_apu_pair(es1968_t *chip, int apu)
+{
+    chip->apu[apu] = chip->apu[apu + 1] = ESM_APU_FREE;
+}
+
+
+/******************
+ * PCM open/close *
+ ******************/
+
+static int snd_es1968_playback_open(snd_pcm_substream_t *substream)
+{
+    es1968_t *chip = snd_pcm_substream_chip(substream);
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    esschan_t *es;
+    int apu1;
+
+    /* search 2 APUs */
+    apu1 = snd_es1968_alloc_apu_pair(chip, ESM_APU_PCM_PLAY);
+    if (apu1 < 0)
+        return apu1;
+
+    es = kcalloc(1, sizeof(*es), GFP_KERNEL);
+    if (!es) {
+        snd_es1968_free_apu_pair(chip, apu1);
+        return -ENOMEM;
+    }
+
+    es->apu[0] = apu1;
+    es->apu[1] = apu1 + 1;
+    es->apu_mode[0] = 0;
+    es->apu_mode[1] = 0;
+    es->running = 0;
+    es->substream = substream;
+    es->mode = ESM_MODE_PLAY;
+
+    runtime->private_data = es;
+    runtime->hw = snd_es1968_playback;
+    runtime->hw.buffer_bytes_max = runtime->hw.period_bytes_max =
+        calc_available_memory_size(chip);
+#if 0
+    snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+                               1024);
+#endif
+    spin_lock_irq(&chip->substream_lock);
+    list_add(&es->list, &chip->substream_list);
+    spin_unlock_irq(&chip->substream_lock);
+
+    return 0;
+}
+
+static int snd_es1968_capture_open(snd_pcm_substream_t *substream)
+{
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    es1968_t *chip = snd_pcm_substream_chip(substream);
+    esschan_t *es;
+    int apu1, apu2;
+
+    apu1 = snd_es1968_alloc_apu_pair(chip, ESM_APU_PCM_CAPTURE);
+    if (apu1 < 0)
+        return apu1;
+    
+    apu2 = snd_es1968_alloc_apu_pair(chip, ESM_APU_PCM_RATECONV);
+    if (apu2 < 0) {
+        snd_es1968_free_apu_pair(chip, apu1);
+        return apu2;
+    }
+
+    es = kcalloc(1, sizeof(*es), GFP_KERNEL);
+    if (!es) {
+        snd_es1968_free_apu_pair(chip, apu1);
+        snd_es1968_free_apu_pair(chip, apu2);
+        return -ENOMEM;
+    }
+
+    es->apu[0] = apu1;
+    es->apu[1] = apu1 + 1;
+    es->apu[2] = apu2;
+    es->apu[3] = apu2 + 1;
+    es->apu_mode[0] = 0;
+    es->apu_mode[1] = 0;
+    es->apu_mode[2] = 0;
+    es->apu_mode[3] = 0;
+    es->running = 0;
+    es->substream = substream;
+    es->mode = ESM_MODE_CAPTURE;
+
+    /* get mixbuffer */
+    if ((es->mixbuf = snd_es1968_new_memory(chip, ESM_MIXBUF_SIZE)) == NULL) {
+        snd_es1968_free_apu_pair(chip, apu1);
+        snd_es1968_free_apu_pair(chip, apu2);
+        kfree(es);
+        return -ENOMEM;
+    }
+    memset(es->mixbuf->buf.area, 0, ESM_MIXBUF_SIZE);
+
+    runtime->private_data = es;
+    runtime->hw = snd_es1968_capture;
+    runtime->hw.buffer_bytes_max = runtime->hw.period_bytes_max =
+        calc_available_memory_size(chip) - 1024; /* keep MIXBUF size */
+#if 0
+    snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+                               1024);
+#endif
+    spin_lock_irq(&chip->substream_lock);
+    list_add(&es->list, &chip->substream_list);
+    spin_unlock_irq(&chip->substream_lock);
+
+    return 0;
+}
+
+static int snd_es1968_playback_close(snd_pcm_substream_t * substream)
+{
+    es1968_t *chip = snd_pcm_substream_chip(substream);
+    esschan_t *es;
+
+    if (substream->runtime->private_data == NULL)
+        return 0;
+    es = substream->runtime->private_data;
+    spin_lock_irq(&chip->substream_lock);
+    list_del(&es->list);
+    spin_unlock_irq(&chip->substream_lock);
+    snd_es1968_free_apu_pair(chip, es->apu[0]);
+    kfree(es);
+
+    return 0;
+}
+
+static int snd_es1968_capture_close(snd_pcm_substream_t * substream)
+{
+    es1968_t *chip = snd_pcm_substream_chip(substream);
+    esschan_t *es;
+
+    if (substream->runtime->private_data == NULL)
+        return 0;
+    es = substream->runtime->private_data;
+    spin_lock_irq(&chip->substream_lock);
+    list_del(&es->list);
+    spin_unlock_irq(&chip->substream_lock);
+    snd_es1968_free_memory(chip, es->mixbuf);
+    snd_es1968_free_apu_pair(chip, es->apu[0]);
+    snd_es1968_free_apu_pair(chip, es->apu[2]);
+    kfree(es);
+
+    return 0;
+}
+static snd_pcm_ops_t snd_es1968_playback_ops = {
+    /*	open:	  */	snd_es1968_playback_open,
+    /*	close:	  */	snd_es1968_playback_close,
+    /*	ioctl:	  */	snd_pcm_lib_ioctl,
+    /*	hw_params:*/	snd_es1968_hw_params,
+    /*	hw_free:  */	snd_es1968_hw_free,
+    /*	prepare:  */	snd_es1968_pcm_prepare,
+    /*	trigger:  */	snd_es1968_pcm_trigger,
+    /*	pointer:  */	snd_es1968_pcm_pointer,
+    0,0
+};
+
+static snd_pcm_ops_t snd_es1968_capture_ops = {
+    /*	open:	  */	snd_es1968_capture_open,
+    /*	close:	  */	snd_es1968_capture_close,
+    /*	ioctl:	  */	snd_pcm_lib_ioctl,
+    /*	hw_params:*/	snd_es1968_hw_params,
+    /*	hw_free:  */	snd_es1968_hw_free,
+    /*	prepare:  */	snd_es1968_pcm_prepare,
+    /*	trigger:  */	snd_es1968_pcm_trigger,
+    /*	pointer:  */	snd_es1968_pcm_pointer,
+    /*	copy:	  */	0,
+    0
+};
+
+
+/*
+ * measure clock
+ */
+#define CLOCK_MEASURE_BUFSIZE	16768	/* enough large for a single shot */
+
+static void __devinit es1968_measure_clock(es1968_t *chip)
+{
+    int i, apu;
+    unsigned int pa, offset, t;
+    esm_memory_t *memory;
+    struct timeval start_time, stop_time;
+
+    if (chip->clock == 0)
+        chip->clock = 48000; /* default clock value */
+
+    /* search 2 APUs (although one apu is enough) */
+    if ((apu = snd_es1968_alloc_apu_pair(chip, ESM_APU_PCM_PLAY)) < 0) {
+        snd_printk("Hmm, cannot find empty APU pair!?\n");
+        return;
+    }
+    if ((memory = snd_es1968_new_memory(chip, CLOCK_MEASURE_BUFSIZE)) == NULL) {
+        snd_printk("cannot allocate dma buffer - using default clock %d\n", chip->clock);
+        snd_es1968_free_apu_pair(chip, apu);
+        return;
+    }
+
+    memset(memory->buf.area, 0, CLOCK_MEASURE_BUFSIZE);
+
+    wave_set_register(chip, apu << 3, (memory->buf.addr - 0x10) & 0xfff8);
+
+    pa = (unsigned int)((memory->buf.addr - chip->dma.addr) >> 1);
+    pa |= 0x00400000;	/* System RAM (Bit 22) */
+
+    /* initialize apu */
+    for (i = 0; i < 16; i++)
+        apu_set_register(chip, apu, i, 0x0000);
+
+    apu_set_register(chip, apu, 0, 0x400f);
+    apu_set_register(chip, apu, 4, ((pa >> 16) & 0xff) << 8);
+    apu_set_register(chip, apu, 5, pa & 0xffff);
+    apu_set_register(chip, apu, 6, (pa + CLOCK_MEASURE_BUFSIZE/2) & 0xffff);
+    apu_set_register(chip, apu, 7, CLOCK_MEASURE_BUFSIZE/2);
+    apu_set_register(chip, apu, 8, 0x0000);
+    apu_set_register(chip, apu, 9, 0xD000);
+    apu_set_register(chip, apu, 10, 0x8F08);
+    apu_set_register(chip, apu, 11, 0x0000);
+    spin_lock_irq(&chip->reg_lock);
+    outw(1, chip->io_port + 0x04); /* clear WP interrupts */
+    outw(inw(chip->io_port + ESM_PORT_HOST_IRQ) | ESM_HIRQ_DSIE, chip->io_port + ESM_PORT_HOST_IRQ); /* enable WP ints */
+    spin_unlock_irq(&chip->reg_lock);
+
+    snd_es1968_apu_set_freq(chip, apu, ((unsigned int)48000 << 16) / chip->clock); /* 48000 Hz */
+
+    chip->in_measurement = 1;
+    chip->measure_apu = apu;
+    spin_lock_irq(&chip->reg_lock);
+    snd_es1968_bob_inc(chip, ESM_BOB_FREQ);
+    __apu_set_register(chip, apu, 5, pa & 0xffff);
+    snd_es1968_trigger_apu(chip, apu, ESM_APU_16BITLINEAR);
+    do_gettimeofday(&start_time);
+    spin_unlock_irq(&chip->reg_lock);
+    msleep(50);
+    spin_lock_irq(&chip->reg_lock);
+    offset = __apu_get_register(chip, apu, 5);
+    do_gettimeofday(&stop_time);
+    snd_es1968_trigger_apu(chip, apu, 0); /* stop */
+    snd_es1968_bob_dec(chip);
+    chip->in_measurement = 0;
+    spin_unlock_irq(&chip->reg_lock);
+
+    /* check the current position */
+    offset -= (pa & 0xffff);
+    offset &= 0xfffe;
+    offset += chip->measure_count * (CLOCK_MEASURE_BUFSIZE/2);
+#if 0 /* vladest patch baseg on r.ihle suggestion */
+    t = stop_time.tv_sec - start_time.tv_sec;
+    t *= 1000000;
+    if (stop_time.tv_usec < start_time.tv_usec)
+        t -= start_time.tv_usec - stop_time.tv_usec;
+    else
+        t += stop_time.tv_usec - start_time.tv_usec;
+#else
+    t = 50000;
+#endif
+    if (t == 0) {
+        snd_printk("?? calculation error..\n");
+    } else {
+        offset *= 1000;
+        offset = (offset / t) * 1000 + ((offset % t) * 1000) / t;
+        if (offset < 47500 || offset > 48500) {
+            if (offset >= 40000 && offset <= 50000)
+                chip->clock = (chip->clock * offset) / 48000;
+        }
+        printk(KERN_INFO "es1968: clocking to %d\n", chip->clock);
+    }
+    snd_es1968_free_memory(chip, memory);
+    snd_es1968_free_apu_pair(chip, apu);
+}
+
+
+/*
+ */
+static void snd_es1968_pcm_free(snd_pcm_t *pcm)
+{
+    es1968_t *esm = pcm->private_data;
+    snd_es1968_free_dmabuf(esm);
+    esm->pcm = NULL;
+}
+
+static int __devinit
+snd_es1968_pcm(es1968_t *chip, int device)
+{
+    snd_pcm_t *pcm;
+    int err;
+
+    /* get DMA buffer */
+    if ((err = snd_es1968_init_dmabuf(chip)) < 0)
+        return err;
+
+    /* set PCMBAR */
+    wave_set_register(chip, 0x01FC, chip->dma.addr >> 12);
+    wave_set_register(chip, 0x01FD, chip->dma.addr >> 12);
+    wave_set_register(chip, 0x01FE, chip->dma.addr >> 12);
+    wave_set_register(chip, 0x01FF, chip->dma.addr >> 12);
+
+    if ((err = snd_pcm_new(chip->card, "ESS Maestro", device,
+                           chip->playback_streams,
+                           chip->capture_streams, &pcm)) < 0)
+        return err;
+
+    pcm->private_data = chip;
+    pcm->private_free = snd_es1968_pcm_free;
+
+    snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_es1968_playback_ops);
+    snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_es1968_capture_ops);
+
+    pcm->info_flags = 0;
+
+    strcpy(pcm->name, "ESS Maestro");
+
+    chip->pcm = pcm;
+
+    return 0;
+}
+
+/*
+ * update pointer
+ */
+static void snd_es1968_update_pcm(es1968_t *chip, esschan_t *es)
+{
+    unsigned int hwptr;
+    unsigned int diff;
+    snd_pcm_substream_t *subs = es->substream;
+
+    if (subs == NULL || !es->running)
+        return;
+
+    hwptr = snd_es1968_get_dma_ptr(chip, es) << es->wav_shift;
+    hwptr %= es->dma_size;
+
+    diff = (es->dma_size + hwptr - es->hwptr) % es->dma_size;
+
+    es->hwptr = hwptr;
+    es->count += diff;
+
+    if (es->count > es->frag_size) {
+        spin_unlock(&chip->substream_lock);
+        snd_pcm_period_elapsed(subs);
+        spin_lock(&chip->substream_lock);
+        es->count %= es->frag_size;
+    }
+}
+
+/*
+ */
+static void es1968_update_hw_volume(unsigned long private_data)
+{
+    es1968_t *chip = (es1968_t *) private_data;
+    int x, val;
+    unsigned long flags;
+
+    /* Figure out which volume control button was pushed,
+     based on differences from the default register
+     values. */
+    x = inb(chip->io_port + 0x1c);
+    /* Reset the volume control registers. */
+    outb(0x88, chip->io_port + 0x1c);
+    outb(0x88, chip->io_port + 0x1d);
+    outb(0x88, chip->io_port + 0x1e);
+    outb(0x88, chip->io_port + 0x1f);
+
+    if (chip->in_suspend)
+        return;
+
+    if (! chip->master_switch || ! chip->master_volume)
+        return;
+
+    /* FIXME: we can't call snd_ac97_* functions since here is in tasklet. */
+    spin_lock_irqsave(&chip->ac97_lock, flags);
+    val = chip->ac97->regs[AC97_MASTER];
+    if (x & 1) {
+        /* mute */
+        val ^= 0x8000;
+        chip->ac97->regs[AC97_MASTER] = val;
+        outw(val, chip->io_port + ESM_AC97_DATA);
+        outb(AC97_MASTER, chip->io_port + ESM_AC97_INDEX);
+        snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
+                       &chip->master_switch->id);
+    } else {
+        val &= 0x7fff;
+        if (((x>>1) & 7) > 4) {
+            /* volume up */
+            if ((val & 0xff) > 0)
+                val--;
+            if ((val & 0xff00) > 0)
+                val -= 0x0100;
+        } else {
+            /* volume down */
+            if ((val & 0xff) < 0x1f)
+                val++;
+            if ((val & 0xff00) < 0x1f00)
+                val += 0x0100;
+        }
+        chip->ac97->regs[AC97_MASTER] = val;
+        outw(val, chip->io_port + ESM_AC97_DATA);
+        outb(AC97_MASTER, chip->io_port + ESM_AC97_INDEX);
+        snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
+                       &chip->master_volume->id);
+    }
+    spin_unlock_irqrestore(&chip->ac97_lock, flags);
+}
+
+/*
+ * interrupt handler
+ */
+static irqreturn_t snd_es1968_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+    es1968_t *chip = dev_id;
+    u32 event;
+#ifdef TARGET_OS2
+    int fOurIrq = FALSE;
+#endif
+
+    if (!(event = inb(chip->io_port + 0x1A)))
+        return IRQ_NONE;
+
+#ifdef TARGET_OS2
+    fOurIrq = TRUE;
+#endif
+    outw(inw(chip->io_port + 4) & 1, chip->io_port + 4);
+
+    if (event & ESM_HWVOL_IRQ)
+        tasklet_hi_schedule(&chip->hwvol_tq); /* we'll do this later */
+
+    /* else ack 'em all, i imagine */
+    outb(0xFF, chip->io_port + 0x1A);
+
+    if ((event & ESM_MPU401_IRQ) && chip->rmidi) {
+        snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data, regs);
+    }
+
+    if (event & ESM_SOUND_IRQ) {
+        struct list_head *p;
+        spin_lock(&chip->substream_lock);
+        list_for_each(p, &chip->substream_list) {
+            esschan_t *es = list_entry(p, esschan_t, list);
+            if (es->running)
+                snd_es1968_update_pcm(chip, es);
+        }
+        spin_unlock(&chip->substream_lock);
+        if (chip->in_measurement) {
+            unsigned int curp = __apu_get_register(chip, chip->measure_apu, 5);
+            if (curp < chip->measure_lastpos)
+                chip->measure_count++;
+            chip->measure_lastpos = curp;
+        }
+    }
+
+#ifdef TARGET_OS2
+    if (fOurIrq) {
+        eoi_irq(irq);
+    }
+#endif //TARGET_OS2
+    return IRQ_HANDLED;
+}
+
+/*
+ *  Mixer stuff
+ */
+
+static int __devinit
+snd_es1968_mixer(es1968_t *chip)
+{
+    ac97_bus_t *pbus;
+    ac97_template_t ac97;
+    snd_ctl_elem_id_t id;
+    int err;
+    static ac97_bus_ops_t ops = {
+        0, snd_es1968_ac97_write,
+        snd_es1968_ac97_read,0,0
+    };
+
+    if ((err = snd_ac97_bus(chip->card, 0, &ops, NULL, &pbus)) < 0)
+        return err;
+    pbus->no_vra = 1; /* ES1968 doesn't need VRA */
+
+    memset(&ac97, 0, sizeof(ac97));
+    ac97.private_data = chip;
+    if ((err = snd_ac97_mixer(pbus, &ac97, &chip->ac97)) < 0)
+        return err;
+    
+    /* attach master switch / volumes for h/w volume control */
+    memset(&id, 0, sizeof(id));
+    id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+    strcpy(id.name, "Master Playback Switch");
+    chip->master_switch = snd_ctl_find_id(chip->card, &id);
+    memset(&id, 0, sizeof(id));
+    id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+    strcpy(id.name, "Master Playback Volume");
+    chip->master_volume = snd_ctl_find_id(chip->card, &id);
+
+    return 0;
+}
+
+/*
+ * reset ac97 codec
+ */
+
+static void snd_es1968_ac97_reset(es1968_t *chip)
+{
+    unsigned long ioaddr = chip->io_port;
+
+    unsigned short save_ringbus_a;
+    unsigned short save_68;
+    unsigned short w;
+    unsigned int vend;
+
+    /* save configuration */
+    save_ringbus_a = inw(ioaddr + 0x36);
+
+    //outw(inw(ioaddr + 0x38) & 0xfffc, ioaddr + 0x38); /* clear second codec id? */
+    /* set command/status address i/o to 1st codec */
+    outw(inw(ioaddr + 0x3a) & 0xfffc, ioaddr + 0x3a);
+    outw(inw(ioaddr + 0x3c) & 0xfffc, ioaddr + 0x3c);
+
+    /* disable ac link */
+    outw(0x0000, ioaddr + 0x36);
+    save_68 = inw(ioaddr + 0x68);
+    pci_read_config_word(chip->pci, 0x58, &w);	/* something magical with gpio and bus arb. */
+    pci_read_config_dword(chip->pci, PCI_SUBSYSTEM_VENDOR_ID, &vend);
+    if (w & 1)
+        save_68 |= 0x10;
+    outw(0xfffe, ioaddr + 0x64);	/* unmask gpio 0 */
+    outw(0x0001, ioaddr + 0x68);	/* gpio write */
+    outw(0x0000, ioaddr + 0x60);	/* write 0 to gpio 0 */
+    udelay(20);
+    outw(0x0001, ioaddr + 0x60);	/* write 1 to gpio 1 */
+    msleep(20);
+
+    outw(save_68 | 0x1, ioaddr + 0x68);	/* now restore .. */
+    outw((inw(ioaddr + 0x38) & 0xfffc) | 0x1, ioaddr + 0x38);
+    outw((inw(ioaddr + 0x3a) & 0xfffc) | 0x1, ioaddr + 0x3a);
+    outw((inw(ioaddr + 0x3c) & 0xfffc) | 0x1, ioaddr + 0x3c);
+
+    /* now the second codec */
+    /* disable ac link */
+    outw(0x0000, ioaddr + 0x36);
+    outw(0xfff7, ioaddr + 0x64);	/* unmask gpio 3 */
+    save_68 = inw(ioaddr + 0x68);
+    outw(0x0009, ioaddr + 0x68);	/* gpio write 0 & 3 ?? */
+    outw(0x0001, ioaddr + 0x60);	/* write 1 to gpio */
+    udelay(20);
+    outw(0x0009, ioaddr + 0x60);	/* write 9 to gpio */
+    msleep(500);
+    //outw(inw(ioaddr + 0x38) & 0xfffc, ioaddr + 0x38);
+    outw(inw(ioaddr + 0x3a) & 0xfffc, ioaddr + 0x3a);
+    outw(inw(ioaddr + 0x3c) & 0xfffc, ioaddr + 0x3c);
+
+#if 0				/* the loop here needs to be much better if we want it.. */
+    snd_printk("trying software reset\n");
+    /* try and do a software reset */
+    outb(0x80 | 0x7c, ioaddr + 0x30);
+    for (w = 0;; w++) {
+        if ((inw(ioaddr + 0x30) & 1) == 0) {
+            if (inb(ioaddr + 0x32) != 0)
+                break;
+
+            outb(0x80 | 0x7d, ioaddr + 0x30);
+            if (((inw(ioaddr + 0x30) & 1) == 0)
+                && (inb(ioaddr + 0x32) != 0))
+                break;
+            outb(0x80 | 0x7f, ioaddr + 0x30);
+            if (((inw(ioaddr + 0x30) & 1) == 0)
+                && (inb(ioaddr + 0x32) != 0))
+                break;
+        }
+
+        if (w > 10000) {
+            outb(inb(ioaddr + 0x37) | 0x08, ioaddr + 0x37);	/* do a software reset */
+            msleep(500);	/* oh my.. */
+            outb(inb(ioaddr + 0x37) & ~0x08,
+                 ioaddr + 0x37);
+            udelay(1);
+            outw(0x80, ioaddr + 0x30);
+            for (w = 0; w < 10000; w++) {
+                if ((inw(ioaddr + 0x30) & 1) == 0)
+                    break;
+            }
+        }
+    }
+#endif
+    if (vend == NEC_VERSA_SUBID1 || vend == NEC_VERSA_SUBID2) {
+        /* turn on external amp? */
+        outw(0xf9ff, ioaddr + 0x64);
+        outw(inw(ioaddr + 0x68) | 0x600, ioaddr + 0x68);
+        outw(0x0209, ioaddr + 0x60);
+    }
+
+    /* restore.. */
+    outw(save_ringbus_a, ioaddr + 0x36);
+
+    /* Turn on the 978 docking chip.
+     First frob the "master output enable" bit,
+     then set most of the playback volume control registers to max. */
+    outb(inb(ioaddr+0xc0)|(1<<5), ioaddr+0xc0);
+    outb(0xff, ioaddr+0xc3);
+    outb(0xff, ioaddr+0xc4);
+    outb(0xff, ioaddr+0xc6);
+    outb(0xff, ioaddr+0xc8);
+    outb(0x3f, ioaddr+0xcf);
+    outb(0x3f, ioaddr+0xd0);
+}
+
+static void snd_es1968_reset(es1968_t *chip)
+{
+    /* Reset */
+    outw(ESM_RESET_MAESTRO | ESM_RESET_DIRECTSOUND,
+         chip->io_port + ESM_PORT_HOST_IRQ);
+    udelay(10);
+    outw(0x0000, chip->io_port + ESM_PORT_HOST_IRQ);
+    udelay(10);
+}
+
+/*
+ * power management
+ */
+static void snd_es1968_set_acpi(es1968_t *chip, int state)
+{
+    u16 active_mask = acpi_state_mask[state];
+
+    pci_set_power_state(chip->pci, state);
+    /* make sure the units we care about are on
+     XXX we might want to do this before state flipping? */
+    pci_write_config_word(chip->pci, 0x54, ~ active_mask);
+    pci_write_config_word(chip->pci, 0x56, ~ active_mask);
+}
+
+
+/*
+ * initialize maestro chip
+ */
+static void snd_es1968_chip_init(es1968_t *chip)
+{
+    struct pci_dev *pci = chip->pci;
+    int i;
+    unsigned long iobase  = chip->io_port;
+    u16 w;
+    u32 n;
+
+    /* We used to muck around with pci config space that
+     * we had no business messing with.  We don't know enough
+     * about the machine to know which DMA mode is appropriate,
+     * etc.  We were guessing wrong on some machines and making
+     * them unhappy.  We now trust in the BIOS to do things right,
+     * which almost certainly means a new host of problems will
+     * arise with broken BIOS implementations.  screw 'em.
+     * We're already intolerant of machines that don't assign
+     * IRQs.
+     */
+
+    /* do config work at full power */
+    snd_es1968_set_acpi(chip, ACPI_D0);
+
+    /* Config Reg A */
+    pci_read_config_word(pci, ESM_CONFIG_A, &w);
+
+    /*      Use TDMA for now. TDMA works on all boards, so while its
+     *      not the most efficient its the simplest. */
+    w &= ~DMA_CLEAR;	/* Clear DMA bits */
+    w |= DMA_TDMA;		/* TDMA on */
+    w &= ~(PIC_SNOOP1 | PIC_SNOOP2);	/* Clear Pic Snoop Mode Bits */
+    w &= ~SAFEGUARD;	/* Safeguard off */
+    w |= POST_WRITE;	/* Posted write */
+    w |= ISA_TIMING;	/* ISA timing on */
+    /* XXX huh?  claims to be reserved.. */
+    w &= ~SWAP_LR;		/* swap left/right
+    seems to only have effect on SB
+    Emulation */
+    w &= ~SUBTR_DECODE;	/* Subtractive decode off */
+
+    pci_write_config_word(pci, ESM_CONFIG_A, w);
+
+    /* Config Reg B */
+
+    pci_read_config_word(pci, ESM_CONFIG_B, &w);
+
+    w &= ~(1 << 15);	/* Turn off internal clock multiplier */
+    /* XXX how do we know which to use? */
+    w &= ~(1 << 14);	/* External clock */
+
+    w &= ~SPDIF_CONFB;	/* disable S/PDIF output */
+    w |= HWV_CONFB;		/* HWV on */
+    w |= DEBOUNCE;		/* Debounce off: easier to push the HW buttons */
+    w &= ~GPIO_CONFB;	/* GPIO 4:5 */
+    w |= CHI_CONFB;		/* Disconnect from the CHI.  Enabling this made a dell 7500 work. */
+    w &= ~IDMA_CONFB;	/* IDMA off (undocumented) */
+    w &= ~MIDI_FIX;		/* MIDI fix off (undoc) */
+    w &= ~(1 << 1);		/* reserved, always write 0 */
+    w &= ~IRQ_TO_ISA;	/* IRQ to ISA off (undoc) */
+
+    pci_write_config_word(pci, ESM_CONFIG_B, w);
+
+    /* DDMA off */
+
+    pci_read_config_word(pci, ESM_DDMA, &w);
+    w &= ~(1 << 0);
+    pci_write_config_word(pci, ESM_DDMA, w);
+
+    /*
+     *	Legacy mode
+     */
+
+    pci_read_config_word(pci, ESM_LEGACY_AUDIO_CONTROL, &w);
+
+    w &= ~ESS_ENABLE_AUDIO;	/* Disable Legacy Audio */
+    w &= ~ESS_ENABLE_SERIAL_IRQ;	/* Disable SIRQ */
+    w &= ~(0x1f);		/* disable mpu irq/io, game port, fm, SB */
+
+    pci_write_config_word(pci, ESM_LEGACY_AUDIO_CONTROL, w);
+
+    /* Set up 978 docking control chip. */
+    pci_read_config_word(pci, 0x58, &w);
+    w|=1<<2;	/* Enable 978. */
+    w|=1<<3;	/* Turn on 978 hardware volume control. */
+    w&=~(1<<11);	/* Turn on 978 mixer volume control. */
+    pci_write_config_word(pci, 0x58, w);
+
+    /* Sound Reset */
+
+    snd_es1968_reset(chip);
+
+    /*
+     *	Ring Bus Setup
+     */
+
+    /* setup usual 0x34 stuff.. 0x36 may be chip specific */
+    outw(0xC090, iobase + ESM_RING_BUS_DEST); /* direct sound, stereo */
+    udelay(20);
+    outw(0x3000, iobase + ESM_RING_BUS_CONTR_A); /* enable ringbus/serial */
+    udelay(20);
+
+    /*
+     *	Reset the CODEC
+     */
+
+    snd_es1968_ac97_reset(chip);
+
+    /* Ring Bus Control B */
+
+    n = inl(iobase + ESM_RING_BUS_CONTR_B);
+    n &= ~RINGB_EN_SPDIF;	/* SPDIF off */
+    //w |= RINGB_EN_2CODEC;	/* enable 2nd codec */
+    outl(n, iobase + ESM_RING_BUS_CONTR_B);
+
+    /* Set hardware volume control registers to midpoints.
+     We can tell which button was pushed based on how they change. */
+    outb(0x88, iobase+0x1c);
+    outb(0x88, iobase+0x1d);
+    outb(0x88, iobase+0x1e);
+    outb(0x88, iobase+0x1f);
+
+    /* it appears some maestros (dell 7500) only work if these are set,
+     regardless of wether we use the assp or not. */
+
+    outb(0, iobase + ASSP_CONTROL_B);
+    outb(3, iobase + ASSP_CONTROL_A);	/* M: Reserved bits... */
+    outb(0, iobase + ASSP_CONTROL_C);	/* M: Disable ASSP, ASSP IRQ's and FM Port */
+
+    /*
+     * set up wavecache
+     */
+    for (i = 0; i < 16; i++) {
+        /* Write 0 into the buffer area 0x1E0->1EF */
+        outw(0x01E0 + i, iobase + WC_INDEX);
+        outw(0x0000, iobase + WC_DATA);
+
+        /* The 1.10 test program seem to write 0 into the buffer area
+         * 0x1D0-0x1DF too.*/
+        outw(0x01D0 + i, iobase + WC_INDEX);
+        outw(0x0000, iobase + WC_DATA);
+    }
+    wave_set_register(chip, IDR7_WAVE_ROMRAM,
+                      (wave_get_register(chip, IDR7_WAVE_ROMRAM) & 0xFF00));
+    wave_set_register(chip, IDR7_WAVE_ROMRAM,
+                      wave_get_register(chip, IDR7_WAVE_ROMRAM) | 0x100);
+    wave_set_register(chip, IDR7_WAVE_ROMRAM,
+                      wave_get_register(chip, IDR7_WAVE_ROMRAM) & ~0x200);
+    wave_set_register(chip, IDR7_WAVE_ROMRAM,
+                      wave_get_register(chip, IDR7_WAVE_ROMRAM) | ~0x400);
+
+
+    maestro_write(chip, IDR2_CRAM_DATA, 0x0000);
+    /* Now back to the DirectSound stuff */
+    /* audio serial configuration.. ? */
+    maestro_write(chip, 0x08, 0xB004);
+    maestro_write(chip, 0x09, 0x001B);
+    maestro_write(chip, 0x0A, 0x8000);
+    maestro_write(chip, 0x0B, 0x3F37);
+    maestro_write(chip, 0x0C, 0x0098);
+
+    /* parallel in, has something to do with recording :) */
+    maestro_write(chip, 0x0C,
+                  (maestro_read(chip, 0x0C) & ~0xF000) | 0x8000);
+    /* parallel out */
+    maestro_write(chip, 0x0C,
+                  (maestro_read(chip, 0x0C) & ~0x0F00) | 0x0500);
+
+    maestro_write(chip, 0x0D, 0x7632);
+
+    /* Wave cache control on - test off, sg off,
+     enable, enable extra chans 1Mb */
+
+    w = inw(iobase + WC_CONTROL);
+
+    w &= ~0xFA00;		/* Seems to be reserved? I don't know */
+    w |= 0xA000;		/* reserved... I don't know */
+    w &= ~0x0200;		/* Channels 56,57,58,59 as Extra Play,Rec Channel enable
+    Seems to crash the Computer if enabled... */
+    w |= 0x0100;		/* Wave Cache Operation Enabled */
+    w |= 0x0080;		/* Channels 60/61 as Placback/Record enabled */
+    w &= ~0x0060;		/* Clear Wavtable Size */
+    w |= 0x0020;		/* Wavetable Size : 1MB */
+    /* Bit 4 is reserved */
+    w &= ~0x000C;		/* DMA Stuff? I don't understand what the datasheet means */
+    /* Bit 1 is reserved */
+    w &= ~0x0001;		/* Test Mode off */
+
+    outw(w, iobase + WC_CONTROL);
+
+    /* Now clear the APU control ram */
+    for (i = 0; i < NR_APUS; i++) {
+        for (w = 0; w < NR_APU_REGS; w++)
+            apu_set_register(chip, i, w, 0);
+
+    }
+}
+
+/* Enable IRQ's */
+static void snd_es1968_start_irq(es1968_t *chip)
+{
+    unsigned short w;
+    w = ESM_HIRQ_DSIE | ESM_HIRQ_HW_VOLUME;
+    if (chip->rmidi)
+        w |= ESM_HIRQ_MPU401;
+    outw(w, chip->io_port + ESM_PORT_HOST_IRQ);
+}
+
+#ifdef CONFIG_PM
+/*
+ * PM support
+ */
+static int es1968_suspend(snd_card_t *card, unsigned int state)
+{
+    es1968_t *chip = card->pm_private_data;
+
+    if (! chip->do_pm)
+        return 0;
+
+    chip->in_suspend = 1;
+    snd_pcm_suspend_all(chip->pcm);
+    snd_ac97_suspend(chip->ac97);
+    snd_es1968_bob_stop(chip);
+    snd_es1968_set_acpi(chip, ACPI_D3);
+    return 0;
+}
+
+static int es1968_resume(snd_card_t *card, unsigned int state)
+{
+    es1968_t *chip = card->pm_private_data;
+    struct list_head *p;
+
+    if (! chip->do_pm)
+        return 0;
+
+    /* restore all our config */
+    pci_enable_device(chip->pci);
+    pci_set_master(chip->pci);
+    snd_es1968_chip_init(chip);
+
+    /* need to restore the base pointers.. */
+    if (chip->dma.addr) {
+        /* set PCMBAR */
+        wave_set_register(chip, 0x01FC, chip->dma.addr >> 12);
+        // by vladest
+        //            wave_set_register(chip, 0x01FD, chip->dma.addr >> 12);
+        //            wave_set_register(chip, 0x01FE, chip->dma.addr >> 12);
+        //            wave_set_register(chip, 0x01FF, chip->dma.addr >> 12);
+        // !by vladest
+
+    }
+
+    snd_es1968_start_irq(chip);
+
+    // by vladest 06.10.2003 16:22
+    //        es1968_measure_clock(chip);
+    //! by vladest
+
+    /* restore ac97 state */
+    snd_ac97_resume(chip->ac97);
+
+    list_for_each(p, &chip->substream_list) {
+        esschan_t *es = list_entry(p, esschan_t, list);
+        switch (es->mode) {
+        case ESM_MODE_PLAY:
+            snd_es1968_playback_setup(chip, es, es->substream->runtime);
+            break;
+        case ESM_MODE_CAPTURE:
+            snd_es1968_capture_setup(chip, es, es->substream->runtime);
+            break;
+        }
+    }
+    /* start timer again */
+    if (chip->bobclient)
+        snd_es1968_bob_start(chip);
+
+    chip->in_suspend = 0;
+    return 0;
+}
+
+#endif /* CONFIG_PM */
+
+static int snd_es1968_free(es1968_t *chip)
+{
+    if (chip->io_port) {
+        synchronize_irq(chip->irq);
+        outw(1, chip->io_port + 0x04); /* clear WP interrupts */
+        outw(0, chip->io_port + ESM_PORT_HOST_IRQ); /* disable IRQ */
+    }
+    if (chip->irq >= 0)
+        free_irq(chip->irq, (void *)chip);
+    snd_es1968_set_acpi(chip, ACPI_D3);
+    chip->master_switch = NULL;
+    chip->master_volume = NULL;
+    pci_release_regions(chip->pci);
+    kfree(chip);
+    return 0;
+}
+
+static int snd_es1968_dev_free(snd_device_t *device)
+{
+    es1968_t *chip = device->device_data;
+    return snd_es1968_free(chip);
+}
+
+struct ess_device_list {
+    unsigned short type;	/* chip type */
+    unsigned short vendor;	/* subsystem vendor id */
+};
+
+static struct ess_device_list pm_whitelist[] __devinitdata = {
+    { TYPE_MAESTRO2E, 0x0e11 },     /* Compaq Armada */
+    { TYPE_MAESTRO2E, 0x1028 },
+    { TYPE_MAESTRO2E, 0x103c },
+    { TYPE_MAESTRO2E, 0x1179 },
+    { TYPE_MAESTRO2E, 0x14c0 },	/* HP omnibook 4150 */
+    { TYPE_MAESTRO2E, 0x1558 },
+};
+
+static struct ess_device_list mpu_blacklist[] __devinitdata = {
+    { TYPE_MAESTRO2, 0x125d },
+};
+
+static int __devinit snd_es1968_create(snd_card_t * card,
+                                       struct pci_dev *pci,
+                                       int total_bufsize,
+                                       int play_streams,
+                                       int capt_streams,
+                                       int chip_type,
+                                       int do_pm,
+                                       es1968_t **chip_ret)
+{
+#ifdef TARGET_OS2
+    static snd_device_ops_t ops = {
+        snd_es1968_dev_free,0,0,0
+    };
+#else
+    static snd_device_ops_t ops = {
+    dev_free:       snd_es1968_dev_free,
+    };
+#endif
+    es1968_t *chip;
+    int i, err;
+
+    *chip_ret = NULL;
+
+    /* enable PCI device */
+    if ((err = pci_enable_device(pci)) < 0)
+        return err;
+    /* check, if we can restrict PCI DMA transfers to 28 bits */
+    if (!pci_dma_supported(pci, 0x0fffffff)) {
+        snd_printk("architecture does not support 28bit PCI busmaster DMA\n");
+        return -ENXIO;
+    }
+    pci_set_consistent_dma_mask(pci, 0x0fffffff);
+
+    chip = kcalloc(1, sizeof(*chip), GFP_KERNEL);
+    if (! chip)
+        return -ENOMEM;
+
+    /* Set Vars */
+    chip->type = chip_type;
+    spin_lock_init(&chip->reg_lock);
+    spin_lock_init(&chip->substream_lock);
+    INIT_LIST_HEAD(&chip->buf_list);
+    INIT_LIST_HEAD(&chip->substream_list);
+    spin_lock_init(&chip->ac97_lock);
+    init_MUTEX(&chip->memory_mutex);
+    tasklet_init(&chip->hwvol_tq, es1968_update_hw_volume, (unsigned long)chip);
+    chip->card = card;
+    chip->pci = pci;
+    chip->irq = -1;
+    chip->total_bufsize = total_bufsize;	/* in bytes */
+    chip->playback_streams = play_streams;
+    chip->capture_streams = capt_streams;
+
+    if ((err = pci_request_regions(pci, "ESS Maestro")) < 0) {
+        kfree(chip);
+        return err;
+    }
+    chip->io_port = pci_resource_start(pci, 0);
+    if (request_irq(pci->irq, snd_es1968_interrupt, SA_INTERRUPT|SA_SHIRQ,
+                    "ESS Maestro", (void*)chip)) {
+        snd_printk("unable to grab IRQ %d\n", pci->irq);
+        snd_es1968_free(chip);
+        return -EBUSY;
+    }
+    chip->irq = pci->irq;
+
+    /* Clear Maestro_map */
+    for (i = 0; i < 32; i++)
+        chip->maestro_map[i] = 0;
+
+    /* Clear Apu Map */
+    for (i = 0; i < NR_APUS; i++)
+        chip->apu[i] = ESM_APU_FREE;
+
+    /* just to be sure */
+    pci_set_master(pci);
+
+    if (do_pm > 1) {
+        /* disable power-management if not on the whitelist */
+        unsigned short vend;
+        pci_read_config_word(chip->pci, PCI_SUBSYSTEM_VENDOR_ID, &vend);
+        for (i = 0; i < (int)ARRAY_SIZE(pm_whitelist); i++) {
+            if (chip->type == pm_whitelist[i].type &&
+                vend == pm_whitelist[i].vendor) {
+                do_pm = 1;
+                break;
+            }
+        }
+        if (do_pm > 1) {
+            /* not matched; disabling pm */
+            printk(KERN_INFO "es1968: not attempting power management.\n");
+            do_pm = 0;
+        }
+    }
+    chip->do_pm = do_pm;
+
+    snd_es1968_chip_init(chip);
+
+    if (chip->do_pm)
+        snd_card_set_pm_callback(card, es1968_suspend, es1968_resume, chip);
+
+    if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+        snd_es1968_free(chip);
+        return err;
+    }
+
+    *chip_ret = chip;
+
+    return 0;
+}
+
+
+/*
+ * joystick
+ */
+
+static int snd_es1968_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_es1968_joystick_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+    es1968_t *chip = snd_kcontrol_chip(kcontrol);
+    u16 val;
+
+    pci_read_config_word(chip->pci, ESM_LEGACY_AUDIO_CONTROL, &val);
+    ucontrol->value.integer.value[0] = (val & 0x04) ? 1 : 0;
+    return 0;
+}
+
+static int snd_es1968_joystick_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+    es1968_t *chip = snd_kcontrol_chip(kcontrol);
+    u16 val, oval;
+
+    pci_read_config_word(chip->pci, ESM_LEGACY_AUDIO_CONTROL, &oval);
+    val = oval & ~0x04;
+    if (ucontrol->value.integer.value[0])
+        val |= 0x04;
+    if (val != oval) {
+        pci_write_config_word(chip->pci, ESM_LEGACY_AUDIO_CONTROL, val);
+        return 1;
+    }
+    return 0;
+}
+
+#define num_controls(ary) (sizeof(ary) / sizeof(snd_kcontrol_new_t))
+
+static snd_kcontrol_new_t snd_es1968_control_switches[] __devinitdata = {
+    {
+        /* .iface = */SNDRV_CTL_ELEM_IFACE_CARD,0,0,
+        /* .name =  */"Joystick",0,0,0,
+        /* .info =  */snd_es1968_joystick_info,
+        /* .get =   */snd_es1968_joystick_get,
+        /* .put =   */snd_es1968_joystick_put,
+    }
+};
+
+/*
+ */
+static int __devinit snd_es1968_probe(struct pci_dev *pci,
+                                      const struct pci_device_id *pci_id)
+{
+    static int dev;
+    snd_card_t *card;
+    es1968_t *chip;
+    unsigned int i;
+    int err;
+
+    printk("snd_es1968_probe cp1\n");
+    if (dev >= SNDRV_CARDS)
+        return -ENODEV;
+    if (!enable[dev]) {
+        dev++;
+        return -ENOENT;
+    }
+
+    printk("snd_es1968_probe cp2\n");
+    card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+    if (!card)
+        return -ENOMEM;
+
+    printk("snd_es1968_probe cp3\n");
+
+    if (total_bufsize[dev] < 128)
+        total_bufsize[dev] = 128;
+    if (total_bufsize[dev] > 4096)
+        total_bufsize[dev] = 4096;
+    if ((err = snd_es1968_create(card, pci,
+                                 total_bufsize[dev] * 1024, /* in bytes */
+                                 pcm_substreams_p[dev],
+                                 pcm_substreams_c[dev],
+                                 pci_id->driver_data,
+                                 use_pm[dev],
+                                 &chip)) < 0) {
+        snd_card_free(card);
+        return err;
+    }
+
+    printk("snd_es1968_probe cp4n");
+
+    switch (chip->type) {
+    case TYPE_MAESTRO2E:
+        strcpy(card->driver, "ES1978");
+        strcpy(card->shortname, "ESS ES1978 (Maestro 2E)");
+        break;
+    case TYPE_MAESTRO2:
+        strcpy(card->driver, "ES1968");
+        strcpy(card->shortname, "ESS ES1968 (Maestro 2)");
+        break;
+    case TYPE_MAESTRO:
+        strcpy(card->driver, "ESM1");
+        strcpy(card->shortname, "ESS Maestro 1");
+        break;
+    }
+
+    if ((err = snd_es1968_pcm(chip, 0)) < 0) {
+        snd_card_free(card);
+        return err;
+    }
+        printk("snd_es1968_probe cp5\n");
+
+
+    if ((err = snd_es1968_mixer(chip)) < 0) {
+        snd_card_free(card);
+        return err;
+    }
+
+        printk("snd_es1968_probe cp6\n");
+
+    if (enable_mpu[dev] == 2) {
+        /* check the black list */
+        unsigned short vend;
+        pci_read_config_word(chip->pci, PCI_SUBSYSTEM_VENDOR_ID, &vend);
+        for (i = 0; i < ARRAY_SIZE(mpu_blacklist); i++) {
+            if (chip->type == mpu_blacklist[i].type &&
+                vend == mpu_blacklist[i].vendor) {
+                enable_mpu[dev] = 0;
+                break;
+            }
+        }
+    }
+        printk("snd_es1968_probe cp7\n");
+
+    if (enable_mpu[dev]) {
+        if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
+                                       chip->io_port + ESM_MPU401_PORT,
+                                       MPU401_INFO_INTEGRATED,
+                                       chip->irq, 0, &chip->rmidi)) < 0) {
+            printk(KERN_WARNING "es1968: skipping MPU-401 MIDI support..\n");
+        }
+    }
+
+        printk("snd_es1968_probe cp8\n");
+
+    /* card switches */
+    for (i = 0; i < num_controls(snd_es1968_control_switches); i++) {
+        err = snd_ctl_add(card, snd_ctl_new1(&snd_es1968_control_switches[i], chip));
+        if (err < 0) {
+            snd_card_free(card);
+            return err;
+        }
+    }
+        printk("snd_es1968_probe cp9\n");
+
+
+    snd_es1968_start_irq(chip);
+
+    chip->clock = clock[dev];
+    if (! chip->clock)
+        es1968_measure_clock(chip);
+
+        printk("snd_es1968_probe cp10\n");
+
+    sprintf(card->longname, "%s at 0x%lx, irq %i",
+            card->shortname, chip->io_port, chip->irq);
+
+    if ((err = snd_card_register(card)) < 0) {
+        snd_card_free(card);
+        return err;
+    }
+        printk("snd_es1968_probe cp11\n");
+
+    pci_set_drvdata(pci, card);
+        printk("snd_es1968_probe cp12\n");
+
+    dev++;
+    return 0;
+}
+
+static void __devexit snd_es1968_remove(struct pci_dev *pci)
+{
+    snd_card_free(pci_get_drvdata(pci));
+    pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver driver = {
+    0, 0, 0,
+    "ES1968 (ESS Maestro)",
+    snd_es1968_ids,
+    snd_es1968_probe,
+    snd_es1968_remove,
+    SND_PCI_PM_CALLBACKS
+};
+
+static int __init alsa_card_es1968_init(void)
+{
+    int err;
+
+    if ((err = pci_module_init(&driver)) < 0) {
+#ifdef MODULE
+        //		snd_printk("ESS Maestro soundcard not found or device busy\n");
+#endif
+        return err;
+    }
+    return 0;
+}
+
+static void __exit alsa_card_es1968_exit(void)
+{
+#if 0 // do we really need this?
+    unregister_reboot_notifier(&snd_es1968_nb);
+#endif
+    pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_es1968_init)
+module_exit(alsa_card_es1968_exit)
+
+#ifndef MODULE
+
+/* format is: snd-es1968=enable,index,id,
+ total_bufsize,
+ pcm_substreams_p,
+ pcm_substreams_c,
+ clock,
+ use_pm,
+ enable_mpu
+ */
+
+static int __init alsa_card_es1968_setup(char *str)
+{
+    static unsigned __initdata nr_dev = 0;
+
+    if (nr_dev >= SNDRV_CARDS)
+        return 0;
+    (void)(get_option(&str,&enable[nr_dev]) == 2 &&
+           get_option(&str,&index[nr_dev]) == 2 &&
+           get_id(&str,&id[nr_dev]) == 2 &&
+           get_option(&str,&total_bufsize[nr_dev]) == 2 &&
+           get_option(&str,&pcm_substreams_p[nr_dev]) == 2 &&
+           get_option(&str,&pcm_substreams_c[nr_dev]) == 2 &&
+           get_option(&str,&clock[nr_dev]) == 2 &&
+           get_option(&str,&use_pm[nr_dev]) == 2 &&
+           get_option(&str,&enable_mpu[nr_dev]) == 2);
+    nr_dev++;
+    return 1;
+}
+
+__setup("snd-es1968=", alsa_card_es1968_setup);
+
+#endif /* ifndef MODULE */
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/fm801.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/fm801.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/fm801.c	(revision 91)
@@ -0,0 +1,1489 @@
+/*
+ *  The driver for the ForteMedia FM801 based soundcards
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+//#include <linux/moduleparam.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include <sound/mpu401.h>
+#include <sound/opl3.h>
+#include <sound/initval.h>
+
+#include <asm/io.h>
+
+#if (defined(CONFIG_SND_FM801_TEA575X) || defined(CONFIG_SND_FM801_TEA575X_MODULE)) && (defined(CONFIG_VIDEO_DEV) || defined(CONFIG_VIDEO_DEV_MODULE))
+#include <sound/tea575x-tuner.h>
+#define TEA575X_RADIO 1
+#endif
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("ForteMedia FM801");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{ForteMedia,FM801},"
+		"{Genius,SoundMaker Live 5.1}}");
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;	/* Enable this card */
+/*
+ *  Enable TEA575x tuner
+ *    1 = MediaForte 256-PCS
+ *    2 = MediaForte 256-PCPR
+ *    3 = MediaForte 64-PCR
+ *  High 16-bits are video (radio) device number + 1
+ */
+static int tea575x_tuner[SNDRV_CARDS] = { 0 };
+
+//module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for the FM801 soundcard.");
+//module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for the FM801 soundcard.");
+//module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable FM801 soundcard.");
+//module_param_array(tea575x_tuner, bool, NULL, 0444);
+MODULE_PARM_DESC(tea575x_tuner, "Enable TEA575x tuner.");
+
+/*
+ *  Direct registers
+ */
+
+#define FM801_REG(chip, reg)	(chip->port + FM801_##reg)
+
+#define FM801_PCM_VOL		0x00	/* PCM Output Volume */
+#define FM801_FM_VOL		0x02	/* FM Output Volume */
+#define FM801_I2S_VOL		0x04	/* I2S Volume */
+#define FM801_REC_SRC		0x06	/* Record Source */
+#define FM801_PLY_CTRL		0x08	/* Playback Control */
+#define FM801_PLY_COUNT		0x0a	/* Playback Count */
+#define FM801_PLY_BUF1		0x0c	/* Playback Bufer I */
+#define FM801_PLY_BUF2		0x10	/* Playback Buffer II */
+#define FM801_CAP_CTRL		0x14	/* Capture Control */
+#define FM801_CAP_COUNT		0x16	/* Capture Count */
+#define FM801_CAP_BUF1		0x18	/* Capture Buffer I */
+#define FM801_CAP_BUF2		0x1c	/* Capture Buffer II */
+#define FM801_CODEC_CTRL	0x22	/* Codec Control */
+#define FM801_I2S_MODE		0x24	/* I2S Mode Control */
+#define FM801_VOLUME		0x26	/* Volume Up/Down/Mute Status */
+#define FM801_I2C_CTRL		0x29	/* I2C Control */
+#define FM801_AC97_CMD		0x2a	/* AC'97 Command */
+#define FM801_AC97_DATA		0x2c	/* AC'97 Data */
+#define FM801_MPU401_DATA	0x30	/* MPU401 Data */
+#define FM801_MPU401_CMD	0x31	/* MPU401 Command */
+#define FM801_GPIO_CTRL		0x52	/* General Purpose I/O Control */
+#define FM801_GEN_CTRL		0x54	/* General Control */
+#define FM801_IRQ_MASK		0x56	/* Interrupt Mask */
+#define FM801_IRQ_STATUS	0x5a	/* Interrupt Status */
+#define FM801_OPL3_BANK0	0x68	/* OPL3 Status Read / Bank 0 Write */
+#define FM801_OPL3_DATA0	0x69	/* OPL3 Data 0 Write */
+#define FM801_OPL3_BANK1	0x6a	/* OPL3 Bank 1 Write */
+#define FM801_OPL3_DATA1	0x6b	/* OPL3 Bank 1 Write */
+#define FM801_POWERDOWN		0x70	/* Blocks Power Down Control */
+
+#define FM801_AC97_ADDR_SHIFT	10
+
+/* playback and record control register bits */
+#define FM801_BUF1_LAST		(1<<1)
+#define FM801_BUF2_LAST		(1<<2)
+#define FM801_START		(1<<5)
+#define FM801_PAUSE		(1<<6)
+#define FM801_IMMED_STOP	(1<<7)
+#define FM801_RATE_SHIFT	8
+#define FM801_RATE_MASK		(15 << FM801_RATE_SHIFT)
+#define FM801_CHANNELS_4	(1<<12)	/* playback only */
+#define FM801_CHANNELS_6	(2<<12)	/* playback only */
+#define FM801_CHANNELS_6MS	(3<<12)	/* playback only */
+#define FM801_CHANNELS_MASK	(3<<12)
+#define FM801_16BIT		(1<<14)
+#define FM801_STEREO		(1<<15)
+
+/* IRQ status bits */
+#define FM801_IRQ_PLAYBACK	(1<<8)
+#define FM801_IRQ_CAPTURE	(1<<9)
+#define FM801_IRQ_VOLUME	(1<<14)
+#define FM801_IRQ_MPU		(1<<15)
+
+/* GPIO control register */
+#define FM801_GPIO_GP0		(1<<0)	/* read/write */
+#define FM801_GPIO_GP1		(1<<1)
+#define FM801_GPIO_GP2		(1<<2)
+#define FM801_GPIO_GP3		(1<<3)
+#define FM801_GPIO_GP(x)	(1<<(0+(x)))
+#define FM801_GPIO_GD0		(1<<8)	/* directions: 1 = input, 0 = output*/
+#define FM801_GPIO_GD1		(1<<9)
+#define FM801_GPIO_GD2		(1<<10)
+#define FM801_GPIO_GD3		(1<<11)
+#define FM801_GPIO_GD(x)	(1<<(8+(x)))
+#define FM801_GPIO_GS0		(1<<12)	/* function select: */
+#define FM801_GPIO_GS1		(1<<13)	/*    1 = GPIO */
+#define FM801_GPIO_GS2		(1<<14)	/*    0 = other (S/PDIF, VOL) */
+#define FM801_GPIO_GS3		(1<<15)
+#define FM801_GPIO_GS(x)	(1<<(12+(x)))
+	
+/*
+
+ */
+
+typedef struct _snd_fm801 fm801_t;
+
+struct _snd_fm801 {
+	int irq;
+
+	unsigned long port;	/* I/O port number */
+	unsigned int multichannel: 1,	/* multichannel support */
+		     secondary: 1;	/* secondary codec */
+	unsigned char secondary_addr;	/* address of the secondary codec */
+
+	unsigned short ply_ctrl; /* playback control */
+	unsigned short cap_ctrl; /* capture control */
+
+	unsigned long ply_buffer;
+	unsigned int ply_buf;
+	unsigned int ply_count;
+	unsigned int ply_size;
+	unsigned int ply_pos;
+
+	unsigned long cap_buffer;
+	unsigned int cap_buf;
+	unsigned int cap_count;
+	unsigned int cap_size;
+	unsigned int cap_pos;
+
+	ac97_bus_t *ac97_bus;
+	ac97_t *ac97;
+	ac97_t *ac97_sec;
+
+	struct pci_dev *pci;
+	snd_card_t *card;
+	snd_pcm_t *pcm;
+	snd_rawmidi_t *rmidi;
+	snd_pcm_substream_t *playback_substream;
+	snd_pcm_substream_t *capture_substream;
+	unsigned int p_dma_size;
+	unsigned int c_dma_size;
+
+	spinlock_t reg_lock;
+	snd_info_entry_t *proc_entry;
+
+#ifdef TEA575X_RADIO
+	tea575x_t tea;
+#endif
+};
+
+static struct pci_device_id snd_fm801_ids[] = {
+	{ 0x1319, 0x0801, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0, },   /* FM801 */
+	{ 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, snd_fm801_ids);
+
+/*
+ *  common I/O routines
+ */
+
+static int snd_fm801_update_bits(fm801_t *chip, unsigned short reg,
+				 unsigned short mask, unsigned short value)
+{
+    int change;
+    unsigned long flags;
+    unsigned short old, new;
+
+    spin_lock_irqsave(&chip->reg_lock, flags);
+	old = inw(chip->port + reg);
+	new = (old & ~mask) | value;
+	change = old != new;
+	if (change)
+            outw(new, chip->port + reg);
+        spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return change;
+}
+
+static void snd_fm801_codec_write(ac97_t *ac97,
+				  unsigned short reg,
+				  unsigned short val)
+{
+	fm801_t *chip = ac97->private_data;
+	int idx;
+
+	/*
+	 *  Wait until the codec interface is not ready..
+	 */
+	for (idx = 0; idx < 100; idx++) {
+		if (!(inw(FM801_REG(chip, AC97_CMD)) & (1<<9)))
+			goto ok1;
+		udelay(10);
+	}
+	snd_printk("AC'97 interface is busy (1)\n");
+	return;
+
+ ok1:
+	/* write data and address */
+	outw(val, FM801_REG(chip, AC97_DATA));
+	outw(reg | (ac97->addr << FM801_AC97_ADDR_SHIFT), FM801_REG(chip, AC97_CMD));
+	/*
+	 *  Wait until the write command is not completed..
+         */
+	for (idx = 0; idx < 1000; idx++) {
+		if (!(inw(FM801_REG(chip, AC97_CMD)) & (1<<9)))
+			return;
+		udelay(10);
+	}
+	snd_printk("AC'97 interface #%d is busy (2)\n", ac97->num);
+}
+
+static unsigned short snd_fm801_codec_read(ac97_t *ac97, unsigned short reg)
+{
+	fm801_t *chip = ac97->private_data;
+	int idx;
+
+	/*
+	 *  Wait until the codec interface is not ready..
+	 */
+	for (idx = 0; idx < 100; idx++) {
+		if (!(inw(FM801_REG(chip, AC97_CMD)) & (1<<9)))
+			goto ok1;
+		udelay(10);
+	}
+	snd_printk("AC'97 interface is busy (1)\n");
+	return 0;
+
+ ok1:
+	/* read command */
+	outw(reg | (ac97->addr << FM801_AC97_ADDR_SHIFT) | (1<<7), FM801_REG(chip, AC97_CMD));
+	for (idx = 0; idx < 100; idx++) {
+		if (!(inw(FM801_REG(chip, AC97_CMD)) & (1<<9)))
+			goto ok2;
+		udelay(10);
+	}
+	snd_printk("AC'97 interface #%d is busy (2)\n", ac97->num);
+	return 0;
+
+ ok2:
+	for (idx = 0; idx < 1000; idx++) {
+		if (inw(FM801_REG(chip, AC97_CMD)) & (1<<8))
+			goto ok3;
+		udelay(10);
+	}
+	snd_printk("AC'97 interface #%d is not valid (2)\n", ac97->num);
+	return 0;
+
+ ok3:
+	return inw(FM801_REG(chip, AC97_DATA));
+}
+
+static unsigned int rates[] = {
+  5500,  8000,  9600, 11025,
+  16000, 19200, 22050, 32000,
+  38400, 44100, 48000
+};
+
+static snd_pcm_hw_constraint_list_t hw_constraints_rates = {
+       /* .count = */ ARRAY_SIZE(rates),
+       /* .list = */rates,
+       /* .mask = */0,
+};
+
+static unsigned int channels[] = {
+  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,
+};
+
+/*
+ *  Sample rate routines
+ */
+
+static unsigned short snd_fm801_rate_bits(unsigned int rate)
+{
+	unsigned int idx;
+
+	for (idx = 0; idx < ARRAY_SIZE(rates); idx++)
+		if (rates[idx] == rate)
+			return idx;
+	snd_BUG();
+	return ARRAY_SIZE(rates) - 1;
+}
+
+/*
+ *  PCM part
+ */
+
+static int snd_fm801_playback_trigger(snd_pcm_substream_t * substream,
+				      int cmd)
+{
+	fm801_t *chip = snd_pcm_substream_chip(substream);
+
+	spin_lock(&chip->reg_lock);
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		chip->ply_ctrl &= ~(FM801_BUF1_LAST |
+				     FM801_BUF2_LAST |
+				     FM801_PAUSE);
+		chip->ply_ctrl |= FM801_START |
+				   FM801_IMMED_STOP;
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		chip->ply_ctrl &= ~(FM801_START | FM801_PAUSE);
+		break;
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		chip->ply_ctrl |= FM801_PAUSE;
+		break;
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		chip->ply_ctrl &= ~FM801_PAUSE;
+		break;
+	default:
+		spin_unlock(&chip->reg_lock);
+		snd_BUG();
+		return -EINVAL;
+	}
+	outw(chip->ply_ctrl, FM801_REG(chip, PLY_CTRL));
+	spin_unlock(&chip->reg_lock);
+	return 0;
+}
+
+static int snd_fm801_capture_trigger(snd_pcm_substream_t * substream,
+				     int cmd)
+{
+	fm801_t *chip = snd_pcm_substream_chip(substream);
+
+	spin_lock(&chip->reg_lock);
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		chip->cap_ctrl &= ~(FM801_BUF1_LAST |
+				     FM801_BUF2_LAST |
+				     FM801_PAUSE);
+		chip->cap_ctrl |= FM801_START |
+				   FM801_IMMED_STOP;
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		chip->cap_ctrl &= ~(FM801_START | FM801_PAUSE);
+		break;
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		chip->cap_ctrl |= FM801_PAUSE;
+		break;
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		chip->cap_ctrl &= ~FM801_PAUSE;
+		break;
+	default:
+		spin_unlock(&chip->reg_lock);
+		snd_BUG();
+		return -EINVAL;
+	}
+	outw(chip->cap_ctrl, FM801_REG(chip, CAP_CTRL));
+	spin_unlock(&chip->reg_lock);
+	return 0;
+}
+
+static int snd_fm801_hw_params(snd_pcm_substream_t * substream,
+			       snd_pcm_hw_params_t * hw_params)
+{
+	return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+static int snd_fm801_hw_free(snd_pcm_substream_t * substream)
+{
+	return snd_pcm_lib_free_pages(substream);
+}
+
+static int snd_fm801_playback_prepare(snd_pcm_substream_t * substream)
+{
+	fm801_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	chip->ply_size = snd_pcm_lib_buffer_bytes(substream);
+        chip->ply_count = snd_pcm_lib_period_bytes(substream);
+        spin_lock_irq(&chip->reg_lock);
+	chip->ply_ctrl &= ~(FM801_START | FM801_16BIT |
+			     FM801_STEREO | FM801_RATE_MASK |
+			     FM801_CHANNELS_MASK);
+	if (snd_pcm_format_width(runtime->format) == 16)
+		chip->ply_ctrl |= FM801_16BIT;
+	if (runtime->channels > 1) {
+		chip->ply_ctrl |= FM801_STEREO;
+		if (runtime->channels == 4)
+			chip->ply_ctrl |= FM801_CHANNELS_4;
+		else if (runtime->channels == 6)
+			chip->ply_ctrl |= FM801_CHANNELS_6;
+	}
+	chip->ply_ctrl |= snd_fm801_rate_bits(runtime->rate) << FM801_RATE_SHIFT;
+	chip->ply_buf = 0;
+	outw(chip->ply_ctrl, FM801_REG(chip, PLY_CTRL));
+	outw(chip->ply_count - 1, FM801_REG(chip, PLY_COUNT));
+	chip->ply_buffer = runtime->dma_addr;
+	chip->ply_pos = 0;
+	outl(chip->ply_buffer, FM801_REG(chip, PLY_BUF1));
+        outl(chip->ply_buffer + (chip->ply_count % chip->ply_size), FM801_REG(chip, PLY_BUF2));
+        spin_unlock_irq(&chip->reg_lock);
+	return 0;
+}
+
+static int snd_fm801_capture_prepare(snd_pcm_substream_t * substream)
+{
+	fm801_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	chip->cap_size = snd_pcm_lib_buffer_bytes(substream);
+        chip->cap_count = snd_pcm_lib_period_bytes(substream);
+        spin_lock_irq(&chip->reg_lock);
+	chip->cap_ctrl &= ~(FM801_START | FM801_16BIT |
+			     FM801_STEREO | FM801_RATE_MASK);
+	if (snd_pcm_format_width(runtime->format) == 16)
+		chip->cap_ctrl |= FM801_16BIT;
+	if (runtime->channels > 1)
+		chip->cap_ctrl |= FM801_STEREO;
+	chip->cap_ctrl |= snd_fm801_rate_bits(runtime->rate) << FM801_RATE_SHIFT;
+	chip->cap_buf = 0;
+	outw(chip->cap_ctrl, FM801_REG(chip, CAP_CTRL));
+	outw(chip->cap_count - 1, FM801_REG(chip, CAP_COUNT));
+	chip->cap_buffer = runtime->dma_addr;
+	chip->cap_pos = 0;
+	outl(chip->cap_buffer, FM801_REG(chip, CAP_BUF1));
+        outl(chip->cap_buffer + (chip->cap_count % chip->cap_size), FM801_REG(chip, CAP_BUF2));
+        spin_unlock_irq(&chip->reg_lock);
+	return 0;
+}
+
+static snd_pcm_uframes_t snd_fm801_playback_pointer(snd_pcm_substream_t * substream)
+{
+	fm801_t *chip = snd_pcm_substream_chip(substream);
+	size_t ptr;
+
+	if (!(chip->ply_ctrl & FM801_START))
+		return 0;
+	spin_lock(&chip->reg_lock);
+	ptr = chip->ply_pos + (chip->ply_count - 1) - inw(FM801_REG(chip, PLY_COUNT));
+	if (inw(FM801_REG(chip, IRQ_STATUS)) & FM801_IRQ_PLAYBACK) {
+		ptr += chip->ply_count;
+		ptr %= chip->ply_size;
+	}
+	spin_unlock(&chip->reg_lock);
+	return bytes_to_frames(substream->runtime, ptr);
+}
+
+static snd_pcm_uframes_t snd_fm801_capture_pointer(snd_pcm_substream_t * substream)
+{
+	fm801_t *chip = snd_pcm_substream_chip(substream);
+	size_t ptr;
+
+	if (!(chip->cap_ctrl & FM801_START))
+		return 0;
+	spin_lock(&chip->reg_lock);
+	ptr = chip->cap_pos + (chip->cap_count - 1) - inw(FM801_REG(chip, CAP_COUNT));
+	if (inw(FM801_REG(chip, IRQ_STATUS)) & FM801_IRQ_CAPTURE) {
+		ptr += chip->cap_count;
+		ptr %= chip->cap_size;
+	}
+	spin_unlock(&chip->reg_lock);
+	return bytes_to_frames(substream->runtime, ptr);
+}
+
+static irqreturn_t snd_fm801_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	fm801_t *chip = dev_id;
+	unsigned short status;
+	unsigned int tmp;
+
+	status = inw(FM801_REG(chip, IRQ_STATUS));
+	status &= FM801_IRQ_PLAYBACK|FM801_IRQ_CAPTURE|FM801_IRQ_MPU|FM801_IRQ_VOLUME;
+	if (! status)
+            return IRQ_NONE;
+
+	/* ack first */
+	outw(status, FM801_REG(chip, IRQ_STATUS));
+	if (chip->pcm && (status & FM801_IRQ_PLAYBACK) && chip->playback_substream) {
+		spin_lock(&chip->reg_lock);
+		chip->ply_buf++;
+		chip->ply_pos += chip->ply_count;
+		chip->ply_pos %= chip->ply_size;
+		tmp = chip->ply_pos + chip->ply_count;
+		tmp %= chip->ply_size;
+		outl(chip->ply_buffer + tmp,
+				(chip->ply_buf & 1) ?
+					FM801_REG(chip, PLY_BUF1) :
+					FM801_REG(chip, PLY_BUF2));
+		spin_unlock(&chip->reg_lock);
+		snd_pcm_period_elapsed(chip->playback_substream);
+	}
+	if (chip->pcm && (status & FM801_IRQ_CAPTURE) && chip->capture_substream) {
+		spin_lock(&chip->reg_lock);
+		chip->cap_buf++;
+		chip->cap_pos += chip->cap_count;
+		chip->cap_pos %= chip->cap_size;
+		tmp = chip->cap_pos + chip->cap_count;
+		tmp %= chip->cap_size;
+		outl(chip->cap_buffer + tmp,
+				(chip->cap_buf & 1) ?
+					FM801_REG(chip, CAP_BUF1) :
+					FM801_REG(chip, CAP_BUF2));
+		spin_unlock(&chip->reg_lock);
+		snd_pcm_period_elapsed(chip->capture_substream);
+	}
+	if (chip->rmidi && (status & FM801_IRQ_MPU))
+		snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data, regs);
+	if (status & FM801_IRQ_VOLUME)
+		;/* TODO */
+#ifdef TARGET_OS2
+        eoi_irq(irq);
+#endif //TARGET_OS2
+
+	return IRQ_HANDLED;
+}
+
+static snd_pcm_hardware_t snd_fm801_playback =
+{
+	/*.info =	      */  	(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_PAUSE |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+	/*.formats =	       */ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+	/*.rates =	       */ SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000,
+	/*.rate_min =	       */ 5500,
+	/*.rate_max =	       */ 48000,
+	/*.channels_min =      */ 	1,
+	/*.channels_max =      */ 	2,
+	/*.buffer_bytes_max =  */ (128*1024),
+	/*.period_bytes_min =  */ 64,
+	/*.period_bytes_max =  */ (128*1024),
+	/*.periods_min =       */ 	1,
+	/*.periods_max =       */ 	1024,
+	/*.fifo_size =	       */ 0,
+};
+
+static snd_pcm_hardware_t snd_fm801_capture =
+{
+	/*.info =	       */ 	(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_PAUSE |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+	/*.formats =	       */ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+	/*.rates =	       */ SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000,
+	/*.rate_min =	       */ 5500,
+	/*.rate_max =	       */ 48000,
+	/*.channels_min =      */ 	1,
+	/*.channels_max =      */ 	2,
+	/*.buffer_bytes_max =  */ (128*1024),
+	/*.period_bytes_min =  */ 64,
+	/*.period_bytes_max =  */ (128*1024),
+	/*.periods_min =       */ 	1,
+	/*.periods_max =       */ 	1024,
+	/*.fifo_size =	       */ 0,
+};
+
+static int snd_fm801_playback_open(snd_pcm_substream_t * substream)
+{
+	fm801_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int err;
+
+	chip->playback_substream = substream;
+	runtime->hw = snd_fm801_playback;
+	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates);
+	if (chip->multichannel) {
+		runtime->hw.channels_max = 6;
+		snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &hw_constraints_channels);
+	}
+	if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+		return err;
+	return 0;
+}
+
+static int snd_fm801_capture_open(snd_pcm_substream_t * substream)
+{
+	fm801_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int err;
+
+	chip->capture_substream = substream;
+	runtime->hw = snd_fm801_capture;
+	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates);
+	if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+		return err;
+	return 0;
+}
+
+static int snd_fm801_playback_close(snd_pcm_substream_t * substream)
+{
+	fm801_t *chip = snd_pcm_substream_chip(substream);
+
+	chip->playback_substream = NULL;
+	return 0;
+}
+
+static int snd_fm801_capture_close(snd_pcm_substream_t * substream)
+{
+	fm801_t *chip = snd_pcm_substream_chip(substream);
+
+	chip->capture_substream = NULL;
+	return 0;
+}
+
+static snd_pcm_ops_t snd_fm801_playback_ops = {
+	/*.open =      */ snd_fm801_playback_open,
+	/*.close =     */ snd_fm801_playback_close,
+	/*.ioctl =     */ snd_pcm_lib_ioctl,
+	/*.hw_params = */ snd_fm801_hw_params,
+	/*.hw_free =   */ snd_fm801_hw_free,
+	/*.prepare =   */ snd_fm801_playback_prepare,
+	/*.trigger =   */ snd_fm801_playback_trigger,
+        /*.pointer =   */ snd_fm801_playback_pointer,
+        0,0,0,0
+};
+
+static snd_pcm_ops_t snd_fm801_capture_ops = {
+	/*.open =      */ snd_fm801_capture_open,
+	/*.close =     */ snd_fm801_capture_close,
+	/*.ioctl =     */ snd_pcm_lib_ioctl,
+	/*.hw_params = */ snd_fm801_hw_params,
+	/*.hw_free =   */ snd_fm801_hw_free,
+	/*.prepare =   */ snd_fm801_capture_prepare,
+	/*.trigger =   */ snd_fm801_capture_trigger,
+        /*.pointer =   */ snd_fm801_capture_pointer,
+        0,0,0,0
+};
+
+static void snd_fm801_pcm_free(snd_pcm_t *pcm)
+{
+	fm801_t *chip = pcm->private_data;
+	chip->pcm = NULL;
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static int __devinit snd_fm801_pcm(fm801_t *chip, int device, snd_pcm_t ** rpcm)
+{
+	snd_pcm_t *pcm;
+	int err;
+
+	if (rpcm)
+		*rpcm = NULL;
+	if ((err = snd_pcm_new(chip->card, "FM801", device, 1, 1, &pcm)) < 0)
+		return err;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_fm801_playback_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_fm801_capture_ops);
+
+	pcm->private_data = chip;
+	pcm->private_free = snd_fm801_pcm_free;
+	pcm->info_flags = 0;
+	strcpy(pcm->name, "FM801");
+	chip->pcm = pcm;
+
+	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+					      snd_dma_pci_data(chip->pci),
+					      chip->multichannel ? 128*1024 : 64*1024, 128*1024);
+
+	if (rpcm)
+		*rpcm = pcm;
+	return 0;
+}
+
+/*
+ *  TEA5757 radio
+ */
+
+#ifdef TEA575X_RADIO
+
+/* 256PCS GPIO numbers */
+#define TEA_256PCS_DATA			1
+#define TEA_256PCS_WRITE_ENABLE		2	/* inverted */
+#define TEA_256PCS_BUS_CLOCK		3
+
+static void snd_fm801_tea575x_256pcs_write(tea575x_t *tea, unsigned int val)
+{
+	fm801_t *chip = tea->private_data;
+	unsigned short reg;
+	int i = 25;
+
+	spin_lock_irq(&chip->reg_lock);
+	reg = inw(FM801_REG(chip, GPIO_CTRL));
+	/* use GPIO lines and set write enable bit */
+	reg |= FM801_GPIO_GS(TEA_256PCS_DATA) |
+	       FM801_GPIO_GS(TEA_256PCS_WRITE_ENABLE) |
+	       FM801_GPIO_GS(TEA_256PCS_BUS_CLOCK);
+	/* all of lines are in the write direction */
+	/* clear data and clock lines */
+	reg &= ~(FM801_GPIO_GD(TEA_256PCS_DATA) |
+	         FM801_GPIO_GD(TEA_256PCS_WRITE_ENABLE) |
+	         FM801_GPIO_GD(TEA_256PCS_BUS_CLOCK) |
+	         FM801_GPIO_GP(TEA_256PCS_DATA) |
+	         FM801_GPIO_GP(TEA_256PCS_BUS_CLOCK) |
+		 FM801_GPIO_GP(TEA_256PCS_WRITE_ENABLE));
+	outw(reg, FM801_REG(chip, GPIO_CTRL));
+	udelay(1);
+
+	while (i--) {
+		if (val & (1 << i))
+			reg |= FM801_GPIO_GP(TEA_256PCS_DATA);
+		else
+			reg &= ~FM801_GPIO_GP(TEA_256PCS_DATA);
+		outw(reg, FM801_REG(chip, GPIO_CTRL));
+		udelay(1);
+		reg |= FM801_GPIO_GP(TEA_256PCS_BUS_CLOCK);
+		outw(reg, FM801_REG(chip, GPIO_CTRL));
+		reg &= ~FM801_GPIO_GP(TEA_256PCS_BUS_CLOCK);
+		outw(reg, FM801_REG(chip, GPIO_CTRL));
+		udelay(1);
+	}
+
+	/* and reset the write enable bit */
+	reg |= FM801_GPIO_GP(TEA_256PCS_WRITE_ENABLE) |
+	       FM801_GPIO_GP(TEA_256PCS_DATA);
+	outw(reg, FM801_REG(chip, GPIO_CTRL));
+	spin_unlock_irq(&chip->reg_lock);
+}
+
+static unsigned int snd_fm801_tea575x_256pcs_read(tea575x_t *tea)
+{
+	fm801_t *chip = tea->private_data;
+	unsigned short reg;
+	unsigned int val = 0;
+	int i;
+	
+	spin_lock_irq(&chip->reg_lock);
+	reg = inw(FM801_REG(chip, GPIO_CTRL));
+	/* use GPIO lines, set data direction to input */
+	reg |= FM801_GPIO_GS(TEA_256PCS_DATA) |
+	       FM801_GPIO_GS(TEA_256PCS_WRITE_ENABLE) |
+	       FM801_GPIO_GS(TEA_256PCS_BUS_CLOCK) |
+	       FM801_GPIO_GD(TEA_256PCS_DATA) |
+	       FM801_GPIO_GP(TEA_256PCS_DATA) |
+	       FM801_GPIO_GP(TEA_256PCS_WRITE_ENABLE);
+	/* all of lines are in the write direction, except data */
+	/* clear data, write enable and clock lines */
+	reg &= ~(FM801_GPIO_GD(TEA_256PCS_WRITE_ENABLE) |
+	         FM801_GPIO_GD(TEA_256PCS_BUS_CLOCK) |
+	         FM801_GPIO_GP(TEA_256PCS_BUS_CLOCK));
+
+	for (i = 0; i < 24; i++) {
+		reg &= ~FM801_GPIO_GP(TEA_256PCS_BUS_CLOCK);
+		outw(reg, FM801_REG(chip, GPIO_CTRL));
+		udelay(1);
+		reg |= FM801_GPIO_GP(TEA_256PCS_BUS_CLOCK);
+		outw(reg, FM801_REG(chip, GPIO_CTRL));
+		udelay(1);
+		val <<= 1;
+		if (inw(FM801_REG(chip, GPIO_CTRL)) & FM801_GPIO_GP(TEA_256PCS_DATA))
+			val |= 1;
+	}
+
+	spin_unlock_irq(&chip->reg_lock);
+
+	return val;
+}
+
+/* 256PCPR GPIO numbers */
+#define TEA_256PCPR_BUS_CLOCK		0
+#define TEA_256PCPR_DATA		1
+#define TEA_256PCPR_WRITE_ENABLE	2	/* inverted */
+
+static void snd_fm801_tea575x_256pcpr_write(tea575x_t *tea, unsigned int val)
+{
+	fm801_t *chip = tea->private_data;
+	unsigned short reg;
+	int i = 25;
+
+	spin_lock_irq(&chip->reg_lock);
+	reg = inw(FM801_REG(chip, GPIO_CTRL));
+	/* use GPIO lines and set write enable bit */
+	reg |= FM801_GPIO_GS(TEA_256PCPR_DATA) |
+	       FM801_GPIO_GS(TEA_256PCPR_WRITE_ENABLE) |
+	       FM801_GPIO_GS(TEA_256PCPR_BUS_CLOCK);
+	/* all of lines are in the write direction */
+	/* clear data and clock lines */
+	reg &= ~(FM801_GPIO_GD(TEA_256PCPR_DATA) |
+	         FM801_GPIO_GD(TEA_256PCPR_WRITE_ENABLE) |
+	         FM801_GPIO_GD(TEA_256PCPR_BUS_CLOCK) |
+	         FM801_GPIO_GP(TEA_256PCPR_DATA) |
+	         FM801_GPIO_GP(TEA_256PCPR_BUS_CLOCK) |
+		 FM801_GPIO_GP(TEA_256PCPR_WRITE_ENABLE));
+	outw(reg, FM801_REG(chip, GPIO_CTRL));
+	udelay(1);
+
+	while (i--) {
+		if (val & (1 << i))
+			reg |= FM801_GPIO_GP(TEA_256PCPR_DATA);
+		else
+			reg &= ~FM801_GPIO_GP(TEA_256PCPR_DATA);
+		outw(reg, FM801_REG(chip, GPIO_CTRL));
+		udelay(1);
+		reg |= FM801_GPIO_GP(TEA_256PCPR_BUS_CLOCK);
+		outw(reg, FM801_REG(chip, GPIO_CTRL));
+		reg &= ~FM801_GPIO_GP(TEA_256PCPR_BUS_CLOCK);
+		outw(reg, FM801_REG(chip, GPIO_CTRL));
+		udelay(1);
+	}
+
+	/* and reset the write enable bit */
+	reg |= FM801_GPIO_GP(TEA_256PCPR_WRITE_ENABLE) |
+	       FM801_GPIO_GP(TEA_256PCPR_DATA);
+	outw(reg, FM801_REG(chip, GPIO_CTRL));
+	spin_unlock_irq(&chip->reg_lock);
+}
+
+static unsigned int snd_fm801_tea575x_256pcpr_read(tea575x_t *tea)
+{
+	fm801_t *chip = tea->private_data;
+	unsigned short reg;
+	unsigned int val = 0;
+	int i;
+	
+	spin_lock_irq(&chip->reg_lock);
+	reg = inw(FM801_REG(chip, GPIO_CTRL));
+	/* use GPIO lines, set data direction to input */
+	reg |= FM801_GPIO_GS(TEA_256PCPR_DATA) |
+	       FM801_GPIO_GS(TEA_256PCPR_WRITE_ENABLE) |
+	       FM801_GPIO_GS(TEA_256PCPR_BUS_CLOCK) |
+	       FM801_GPIO_GD(TEA_256PCPR_DATA) |
+	       FM801_GPIO_GP(TEA_256PCPR_DATA) |
+	       FM801_GPIO_GP(TEA_256PCPR_WRITE_ENABLE);
+	/* all of lines are in the write direction, except data */
+	/* clear data, write enable and clock lines */
+	reg &= ~(FM801_GPIO_GD(TEA_256PCPR_WRITE_ENABLE) |
+	         FM801_GPIO_GD(TEA_256PCPR_BUS_CLOCK) |
+	         FM801_GPIO_GP(TEA_256PCPR_BUS_CLOCK));
+
+	for (i = 0; i < 24; i++) {
+		reg &= ~FM801_GPIO_GP(TEA_256PCPR_BUS_CLOCK);
+		outw(reg, FM801_REG(chip, GPIO_CTRL));
+		udelay(1);
+		reg |= FM801_GPIO_GP(TEA_256PCPR_BUS_CLOCK);
+		outw(reg, FM801_REG(chip, GPIO_CTRL));
+		udelay(1);
+		val <<= 1;
+		if (inw(FM801_REG(chip, GPIO_CTRL)) & FM801_GPIO_GP(TEA_256PCPR_DATA))
+			val |= 1;
+	}
+
+	spin_unlock_irq(&chip->reg_lock);
+
+	return val;
+}
+
+/* 64PCR GPIO numbers */
+#define TEA_64PCR_BUS_CLOCK		0
+#define TEA_64PCR_WRITE_ENABLE		1	/* inverted */
+#define TEA_64PCR_DATA			2
+
+static void snd_fm801_tea575x_64pcr_write(tea575x_t *tea, unsigned int val)
+{
+	fm801_t *chip = tea->private_data;
+	unsigned short reg;
+	int i = 25;
+
+	spin_lock_irq(&chip->reg_lock);
+	reg = inw(FM801_REG(chip, GPIO_CTRL));
+	/* use GPIO lines and set write enable bit */
+	reg |= FM801_GPIO_GS(TEA_64PCR_DATA) |
+	       FM801_GPIO_GS(TEA_64PCR_WRITE_ENABLE) |
+	       FM801_GPIO_GS(TEA_64PCR_BUS_CLOCK);
+	/* all of lines are in the write direction */
+	/* clear data and clock lines */
+	reg &= ~(FM801_GPIO_GD(TEA_64PCR_DATA) |
+	         FM801_GPIO_GD(TEA_64PCR_WRITE_ENABLE) |
+	         FM801_GPIO_GD(TEA_64PCR_BUS_CLOCK) |
+	         FM801_GPIO_GP(TEA_64PCR_DATA) |
+	         FM801_GPIO_GP(TEA_64PCR_BUS_CLOCK) |
+		 FM801_GPIO_GP(TEA_64PCR_WRITE_ENABLE));
+	outw(reg, FM801_REG(chip, GPIO_CTRL));
+	udelay(1);
+
+	while (i--) {
+		if (val & (1 << i))
+			reg |= FM801_GPIO_GP(TEA_64PCR_DATA);
+		else
+			reg &= ~FM801_GPIO_GP(TEA_64PCR_DATA);
+		outw(reg, FM801_REG(chip, GPIO_CTRL));
+		udelay(1);
+		reg |= FM801_GPIO_GP(TEA_64PCR_BUS_CLOCK);
+		outw(reg, FM801_REG(chip, GPIO_CTRL));
+		reg &= ~FM801_GPIO_GP(TEA_64PCR_BUS_CLOCK);
+		outw(reg, FM801_REG(chip, GPIO_CTRL));
+		udelay(1);
+	}
+
+	/* and reset the write enable bit */
+	reg |= FM801_GPIO_GP(TEA_64PCR_WRITE_ENABLE) |
+	       FM801_GPIO_GP(TEA_64PCR_DATA);
+	outw(reg, FM801_REG(chip, GPIO_CTRL));
+	spin_unlock_irq(&chip->reg_lock);
+}
+
+static unsigned int snd_fm801_tea575x_64pcr_read(tea575x_t *tea)
+{
+	fm801_t *chip = tea->private_data;
+	unsigned short reg;
+	unsigned int val = 0;
+	int i;
+	
+	spin_lock_irq(&chip->reg_lock);
+	reg = inw(FM801_REG(chip, GPIO_CTRL));
+	/* use GPIO lines, set data direction to input */
+	reg |= FM801_GPIO_GS(TEA_64PCR_DATA) |
+	       FM801_GPIO_GS(TEA_64PCR_WRITE_ENABLE) |
+	       FM801_GPIO_GS(TEA_64PCR_BUS_CLOCK) |
+	       FM801_GPIO_GD(TEA_64PCR_DATA) |
+	       FM801_GPIO_GP(TEA_64PCR_DATA) |
+	       FM801_GPIO_GP(TEA_64PCR_WRITE_ENABLE);
+	/* all of lines are in the write direction, except data */
+	/* clear data, write enable and clock lines */
+	reg &= ~(FM801_GPIO_GD(TEA_64PCR_WRITE_ENABLE) |
+	         FM801_GPIO_GD(TEA_64PCR_BUS_CLOCK) |
+	         FM801_GPIO_GP(TEA_64PCR_BUS_CLOCK));
+
+	for (i = 0; i < 24; i++) {
+		reg &= ~FM801_GPIO_GP(TEA_64PCR_BUS_CLOCK);
+		outw(reg, FM801_REG(chip, GPIO_CTRL));
+		udelay(1);
+		reg |= FM801_GPIO_GP(TEA_64PCR_BUS_CLOCK);
+		outw(reg, FM801_REG(chip, GPIO_CTRL));
+		udelay(1);
+		val <<= 1;
+		if (inw(FM801_REG(chip, GPIO_CTRL)) & FM801_GPIO_GP(TEA_64PCR_DATA))
+			val |= 1;
+	}
+
+	spin_unlock_irq(&chip->reg_lock);
+
+	return val;
+}
+
+static struct snd_tea575x_ops snd_fm801_tea_ops[3] = {
+	{
+		/* 1 = MediaForte 256-PCS */
+		.write = snd_fm801_tea575x_256pcs_write,
+		.read = snd_fm801_tea575x_256pcs_read,
+	},
+	{
+		/* 2 = MediaForte 256-PCPR */
+		.write = snd_fm801_tea575x_256pcpr_write,
+		.read = snd_fm801_tea575x_256pcpr_read,
+	},
+	{
+		/* 3 = MediaForte 64-PCR */
+		.write = snd_fm801_tea575x_64pcr_write,
+		.read = snd_fm801_tea575x_64pcr_read,
+	}
+};
+#endif
+
+/*
+ *  Mixer routines
+ */
+
+#define FM801_SINGLE(xname, reg, shift, mask, invert) \
+{ SNDRV_CTL_ELEM_IFACE_MIXER, 0,0, xname, 0,0,0, snd_fm801_info_single, \
+  snd_fm801_get_single, snd_fm801_put_single, \
+  reg | (shift << 8) | (mask << 16) | (invert << 24) }
+
+static int snd_fm801_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+
+	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	return 0;
+}
+
+static int snd_fm801_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	fm801_t *chip = snd_kcontrol_chip(kcontrol);
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 8) & 0xff;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0xff;
+
+	ucontrol->value.integer.value[0] = (inw(chip->port + reg) >> shift) & mask;
+	if (invert)
+		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+	return 0;
+}
+
+static int snd_fm801_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	fm801_t *chip = snd_kcontrol_chip(kcontrol);
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 8) & 0xff;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0xff;
+	unsigned short val;
+
+	val = (ucontrol->value.integer.value[0] & mask);
+	if (invert)
+		val = mask - val;
+	return snd_fm801_update_bits(chip, reg, mask << shift, val << shift);
+}
+
+#define FM801_DOUBLE(xname, reg, shift_left, shift_right, mask, invert) \
+{ SNDRV_CTL_ELEM_IFACE_MIXER, 0,0, xname, 0,0,0, snd_fm801_info_double, \
+  snd_fm801_get_double, snd_fm801_put_double, \
+  reg | (shift_left << 8) | (shift_right << 12) | (mask << 16) | (invert << 24) }
+
+static int snd_fm801_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+
+	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	return 0;
+}
+
+static int snd_fm801_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	fm801_t *chip = snd_kcontrol_chip(kcontrol);
+        int reg = kcontrol->private_value & 0xff;
+	int shift_left = (kcontrol->private_value >> 8) & 0x0f;
+	int shift_right = (kcontrol->private_value >> 12) & 0x0f;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0xff;
+
+	spin_lock_irq(&chip->reg_lock);
+	ucontrol->value.integer.value[0] = (inw(chip->port + reg) >> shift_left) & mask;
+	ucontrol->value.integer.value[1] = (inw(chip->port + reg) >> shift_right) & mask;
+	spin_unlock_irq(&chip->reg_lock);
+	if (invert) {
+		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+		ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1];
+	}
+	return 0;
+}
+
+static int snd_fm801_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	fm801_t *chip = snd_kcontrol_chip(kcontrol);
+	int reg = kcontrol->private_value & 0xff;
+	int shift_left = (kcontrol->private_value >> 8) & 0x0f;
+	int shift_right = (kcontrol->private_value >> 12) & 0x0f;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0xff;
+	unsigned short val1, val2;
+ 
+	val1 = ucontrol->value.integer.value[0] & mask;
+	val2 = ucontrol->value.integer.value[1] & mask;
+	if (invert) {
+		val1 = mask - val1;
+		val2 = mask - val2;
+	}
+	return snd_fm801_update_bits(chip, reg,
+				     (mask << shift_left) | (mask << shift_right),
+				     (val1 << shift_left ) | (val2 << shift_right));
+}
+
+static int snd_fm801_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	static char *texts[5] = {
+		"AC97 Primary", "FM", "I2S", "PCM", "AC97 Secondary"
+	};
+ 
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 5;
+	if (uinfo->value.enumerated.item > 4)
+		uinfo->value.enumerated.item = 4;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int snd_fm801_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	fm801_t *chip = snd_kcontrol_chip(kcontrol);
+        unsigned short val;
+ 
+	val = inw(FM801_REG(chip, REC_SRC)) & 7;
+	if (val > 4)
+		val = 4;
+        ucontrol->value.enumerated.item[0] = val;
+        return 0;
+}
+
+static int snd_fm801_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	fm801_t *chip = snd_kcontrol_chip(kcontrol);
+        unsigned short val;
+ 
+        if ((val = ucontrol->value.enumerated.item[0]) > 4)
+                return -EINVAL;
+	return snd_fm801_update_bits(chip, FM801_REC_SRC, 7, val);
+}
+
+#define FM801_CONTROLS (sizeof(snd_fm801_controls)/sizeof(snd_kcontrol_new_t))
+
+static snd_kcontrol_new_t snd_fm801_controls[] __devinitdata = {
+FM801_DOUBLE("Wave Playback Volume", FM801_PCM_VOL, 0, 8, 31, 1),
+FM801_SINGLE("Wave Playback Switch", FM801_PCM_VOL, 15, 1, 1),
+FM801_DOUBLE("I2S Playback Volume", FM801_I2S_VOL, 0, 8, 31, 1),
+FM801_SINGLE("I2S Playback Switch", FM801_I2S_VOL, 15, 1, 1),
+FM801_DOUBLE("FM Playback Volume", FM801_FM_VOL, 0, 8, 31, 1),
+FM801_SINGLE("FM Playback Switch", FM801_FM_VOL, 15, 1, 1),
+{
+	SNDRV_CTL_ELEM_IFACE_MIXER, 0,0,
+	"Digital Capture Source",0,0,0,
+	snd_fm801_info_mux,
+	snd_fm801_get_mux,
+	snd_fm801_put_mux,0
+}
+};
+
+#define FM801_CONTROLS_MULTI (sizeof(snd_fm801_controls_multi)/sizeof(snd_kcontrol_new_t))
+
+static snd_kcontrol_new_t snd_fm801_controls_multi[] __devinitdata = {
+FM801_SINGLE("AC97 2ch->4ch Copy Switch", FM801_CODEC_CTRL, 7, 1, 0),
+FM801_SINGLE("AC97 18-bit Switch", FM801_CODEC_CTRL, 10, 1, 0),
+FM801_SINGLE("IEC958 Capture Switch", FM801_I2S_MODE, 8, 1, 0),
+FM801_SINGLE("IEC958 Raw Data Playback Switch", FM801_I2S_MODE, 9, 1, 0),
+FM801_SINGLE("IEC958 Raw Data Capture Switch", FM801_I2S_MODE, 10, 1, 0),
+FM801_SINGLE("IEC958 Playback Switch", FM801_GEN_CTRL, 2, 1, 0),
+};
+
+static void snd_fm801_mixer_free_ac97_bus(ac97_bus_t *bus)
+{
+	fm801_t *chip = bus->private_data;
+	chip->ac97_bus = NULL;
+}
+
+static void snd_fm801_mixer_free_ac97(ac97_t *ac97)
+{
+	fm801_t *chip = ac97->private_data;
+	if (ac97->num == 0) {
+		chip->ac97 = NULL;
+	} else {
+		chip->ac97_sec = NULL;
+	}
+}
+
+static int __devinit snd_fm801_mixer(fm801_t *chip)
+{
+	ac97_template_t ac97;
+	unsigned int i;
+	int err;
+        static ac97_bus_ops_t ops = {
+            0, snd_fm801_codec_write,
+            snd_fm801_codec_read, 0,0
+	};
+
+	if ((err = snd_ac97_bus(chip->card, 0, &ops, chip, &chip->ac97_bus)) < 0)
+		return err;
+	chip->ac97_bus->private_free = snd_fm801_mixer_free_ac97_bus;
+
+	memset(&ac97, 0, sizeof(ac97));
+	ac97.private_data = chip;
+	ac97.private_free = snd_fm801_mixer_free_ac97;
+	if ((err = snd_ac97_mixer(chip->ac97_bus, &ac97, &chip->ac97)) < 0)
+		return err;
+	if (chip->secondary) {
+		ac97.num = 1;
+		ac97.addr = chip->secondary_addr;
+		if ((err = snd_ac97_mixer(chip->ac97_bus, &ac97, &chip->ac97_sec)) < 0)
+			return err;
+	}
+	for (i = 0; i < FM801_CONTROLS; i++)
+		snd_ctl_add(chip->card, snd_ctl_new1(&snd_fm801_controls[i], chip));
+	if (chip->multichannel) {
+		for (i = 0; i < FM801_CONTROLS_MULTI; i++)
+			snd_ctl_add(chip->card, snd_ctl_new1(&snd_fm801_controls_multi[i], chip));
+	}
+	return 0;
+}
+
+/*
+ *  initialization routines
+ */
+
+static int snd_fm801_free(fm801_t *chip)
+{
+	unsigned short cmdw;
+
+	if (chip->irq < 0)
+		goto __end_hw;
+
+	/* interrupt setup - mask everything */
+	cmdw = inw(FM801_REG(chip, IRQ_MASK));
+	cmdw |= 0x00c3;
+	outw(cmdw, FM801_REG(chip, IRQ_MASK));
+
+      __end_hw:
+#ifdef TEA575X_RADIO
+	snd_tea575x_exit(&chip->tea);
+#endif
+	if (chip->irq >= 0)
+		free_irq(chip->irq, (void *)chip);
+	pci_release_regions(chip->pci);
+	pci_disable_device(chip->pci);
+
+	kfree(chip);
+	return 0;
+}
+
+static int snd_fm801_dev_free(snd_device_t *device)
+{
+	fm801_t *chip = device->device_data;
+	return snd_fm801_free(chip);
+}
+
+static int __devinit snd_fm801_create(snd_card_t * card,
+				      struct pci_dev * pci,
+				      int tea575x_tuner,
+				      fm801_t ** rchip)
+{
+	fm801_t *chip;
+	unsigned char rev, id;
+	unsigned short cmdw;
+	unsigned long timeout;
+	int err;
+	static snd_device_ops_t ops = {
+		snd_fm801_dev_free, 0,0,0
+	};
+
+	*rchip = NULL;
+	if ((err = pci_enable_device(pci)) < 0)
+		return err;
+	chip = kcalloc(1, sizeof(*chip), GFP_KERNEL);
+	if (chip == NULL) {
+		pci_disable_device(pci);
+		return -ENOMEM;
+	}
+	spin_lock_init(&chip->reg_lock);
+	chip->card = card;
+	chip->pci = pci;
+	chip->irq = -1;
+	if ((err = pci_request_regions(pci, "FM801")) < 0) {
+		kfree(chip);
+		pci_disable_device(pci);
+		return err;
+	}
+	chip->port = pci_resource_start(pci, 0);
+	if (request_irq(pci->irq, snd_fm801_interrupt, SA_INTERRUPT|SA_SHIRQ, "FM801", (void *)chip)) {
+		snd_printk("unable to grab IRQ %d\n", chip->irq);
+		snd_fm801_free(chip);
+		return -EBUSY;
+	}
+	chip->irq = pci->irq;
+	pci_set_master(pci);
+
+	pci_read_config_byte(pci, PCI_REVISION_ID, &rev);
+	if (rev >= 0xb1)	/* FM801-AU */
+		chip->multichannel = 1;
+
+	/* codec cold reset + AC'97 warm reset */
+	outw((1<<5)|(1<<6), FM801_REG(chip, CODEC_CTRL));
+	inw(FM801_REG(chip, CODEC_CTRL)); /* flush posting data */
+	udelay(100);
+	outw(0, FM801_REG(chip, CODEC_CTRL));
+
+	timeout = (jiffies + (3 * HZ) / 4) + 1;		/* min 750ms */
+
+	outw((1<<7) | (0 << FM801_AC97_ADDR_SHIFT), FM801_REG(chip, AC97_CMD));
+	udelay(5);
+	do {
+		if ((inw(FM801_REG(chip, AC97_CMD)) & (3<<8)) == (1<<8))
+			goto __ac97_secondary;
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(1);
+	} while (time_after(timeout, jiffies));
+	snd_printk("Primary AC'97 codec not found\n");
+	snd_fm801_free(chip);
+	return -EIO;
+
+      __ac97_secondary:
+      	if (!chip->multichannel)	/* lookup is not required */
+      		goto __ac97_ok;
+	for (id = 3; id > 0; id--) {	/* my card has the secondary codec */
+					/* at address #3, so the loop is inverted */
+
+		timeout = jiffies + HZ / 20;
+
+		outw((1<<7) | (id << FM801_AC97_ADDR_SHIFT) | AC97_VENDOR_ID1, FM801_REG(chip, AC97_CMD));
+		udelay(5);
+		do {
+			if ((inw(FM801_REG(chip, AC97_CMD)) & (3<<8)) == (1<<8)) {
+				cmdw = inw(FM801_REG(chip, AC97_DATA));
+				if (cmdw != 0xffff && cmdw != 0) {
+					chip->secondary = 1;
+					chip->secondary_addr = id;
+					goto __ac97_ok;
+				}
+			}
+			set_current_state(TASK_UNINTERRUPTIBLE);
+			schedule_timeout(1);
+		} while (time_after(timeout, jiffies));
+	}
+
+	/* the recovery phase, it seems that probing for non-existing codec might */
+	/* cause timeout problems */
+	timeout = (jiffies + (3 * HZ) / 4) + 1;		/* min 750ms */
+
+	outw((1<<7) | (0 << FM801_AC97_ADDR_SHIFT), FM801_REG(chip, AC97_CMD));
+	udelay(5);
+	do {
+		if ((inw(FM801_REG(chip, AC97_CMD)) & (3<<8)) == (1<<8))
+			goto __ac97_ok;
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(1);
+	} while (time_after(timeout, jiffies));
+	snd_printk("Primary AC'97 codec not responding\n");
+	snd_fm801_free(chip);
+	return -EIO;
+
+      __ac97_ok:
+
+	/* init volume */
+	outw(0x0808, FM801_REG(chip, PCM_VOL));
+	outw(0x9f1f, FM801_REG(chip, FM_VOL));
+	outw(0x8808, FM801_REG(chip, I2S_VOL));
+
+	/* I2S control - I2S mode */
+	outw(0x0003, FM801_REG(chip, I2S_MODE));
+
+	/* interrupt setup - unmask MPU, PLAYBACK & CAPTURE */
+	cmdw = inw(FM801_REG(chip, IRQ_MASK));
+	cmdw &= ~0x0083;
+	outw(cmdw, FM801_REG(chip, IRQ_MASK));
+
+	/* interrupt clear */
+	outw(FM801_IRQ_PLAYBACK|FM801_IRQ_CAPTURE|FM801_IRQ_MPU, FM801_REG(chip, IRQ_STATUS));
+
+	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+		snd_fm801_free(chip);
+		return err;
+	}
+
+	snd_card_set_dev(card, &pci->dev);
+
+#ifdef TEA575X_RADIO
+	if (tea575x_tuner > 0 && (tea575x_tuner & 0xffff) < 4) {
+		chip->tea.dev_nr = tea575x_tuner >> 16;
+		chip->tea.card = card;
+		chip->tea.freq_fixup = 10700;
+		chip->tea.private_data = chip;
+		chip->tea.ops = &snd_fm801_tea_ops[(tea575x_tuner & 0xffff) - 1];
+		snd_tea575x_init(&chip->tea);
+	}
+#endif
+
+	*rchip = chip;
+	return 0;
+}
+
+static int __devinit snd_card_fm801_probe(struct pci_dev *pci,
+					  const struct pci_device_id *pci_id)
+{
+	static int dev;
+	snd_card_t *card;
+	fm801_t *chip;
+	opl3_t *opl3;
+	int err;
+
+        if (dev >= SNDRV_CARDS)
+                return -ENODEV;
+	if (!enable[dev]) {
+		dev++;
+		return -ENOENT;
+	}
+
+	card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+	if (card == NULL)
+		return -ENOMEM;
+	if ((err = snd_fm801_create(card, pci, tea575x_tuner[dev], &chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	strcpy(card->driver, "FM801");
+	strcpy(card->shortname, "ForteMedia FM801-");
+	strcat(card->shortname, chip->multichannel ? "AU" : "AS");
+	sprintf(card->longname, "%s at 0x%lx, irq %i",
+		card->shortname, chip->port, chip->irq);
+
+	if ((err = snd_fm801_pcm(chip, 0, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_fm801_mixer(chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+        if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_FM801,
+                                       FM801_REG(chip, MPU401_DATA),
+				       MPU401_INFO_INTEGRATED,
+				       chip->irq, 0, &chip->rmidi)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_opl3_create(card, FM801_REG(chip, OPL3_BANK0),
+				   FM801_REG(chip, OPL3_BANK1),
+				   OPL3_HW_OPL3_FM801, 1, &opl3)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	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_card_fm801_remove(struct pci_dev *pci)
+{
+	snd_card_free(pci_get_drvdata(pci));
+	pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver driver = {
+    0,0,0,
+    "FM801",
+    snd_fm801_ids,
+    snd_card_fm801_probe,
+    snd_card_fm801_remove,
+    0,0
+};
+
+static int __init alsa_card_fm801_init(void)
+{
+	return pci_module_init(&driver);
+}
+
+static void __exit alsa_card_fm801_exit(void)
+{
+	pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_fm801_init)
+module_exit(alsa_card_fm801_exit)
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/hda/CVS/Entries
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/hda/CVS/Entries	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/hda/CVS/Entries	(revision 91)
@@ -0,0 +1,11 @@
+/Makefile/1.1/Thu Jan 20 15:02:28 2005/-ko/
+/hda_codec.c/1.1/Thu Jan 20 15:02:28 2005/-ko/
+/hda_codec.h/1.1/Thu Jan 20 15:02:28 2005/-ko/
+/hda_generic.c/1.1/Thu Jan 20 15:02:28 2005/-ko/
+/hda_intel.c/1.2/Fri Jan 21 17:46:00 2005/-ko/
+/hda_local.h/1.1/Thu Jan 20 15:02:28 2005/-ko/
+/hda_patch.h/1.1/Thu Jan 20 15:02:28 2005/-ko/
+/hda_proc.c/1.1/Thu Jan 20 15:02:28 2005/-ko/
+/patch_cmedia.c/1.1/Thu Jan 20 15:02:28 2005/-ko/
+/patch_realtek.c/1.1/Thu Jan 20 15:02:28 2005/-ko/
+D
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/hda/CVS/Repository
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/hda/CVS/Repository	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/hda/CVS/Repository	(revision 91)
@@ -0,0 +1,1 @@
+alsa-kernel/pci/hda
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/hda/CVS/Root
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/hda/CVS/Root	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/hda/CVS/Root	(revision 91)
@@ -0,0 +1,1 @@
+:pserver:anonymous@cvs.alsa-project.org:/cvsroot/alsa
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/hda/hda_codec.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/hda/hda_codec.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/hda/hda_codec.c	(revision 91)
@@ -0,0 +1,2340 @@
+/*
+ * Universal Interface for Intel High Definition Audio Codec
+ *
+ * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
+ *
+ *
+ *  This driver 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 driver is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/moduleparam.h>
+#include <sound/core.h>
+#include "hda_codec.h"
+#include <sound/asoundef.h>
+#include <sound/tlv.h>
+#include <sound/initval.h>
+#include "hda_local.h"
+
+
+MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
+MODULE_DESCRIPTION("Universal interface for High Definition Audio Codec");
+MODULE_LICENSE("GPL");
+
+
+/*
+ * vendor / preset table
+ */
+
+struct hda_vendor_id {
+    unsigned int id;
+    const char *name;
+};
+
+/* codec vendor labels */
+static struct hda_vendor_id hda_vendor_ids[] = {
+    { 0x10ec, "Realtek" },
+    { 0x1057, "Motorola" },
+    { 0x1106, "VIA" },
+    { 0x11d4, "Analog Devices" },
+    { 0x13f6, "C-Media" },
+    { 0x14f1, "Conexant" },
+    { 0x434d, "C-Media" },
+    { 0x8384, "SigmaTel" },
+    {0} /* terminator */
+};
+
+/* codec presets */
+#include "hda_patch.h"
+
+
+/**
+ * snd_hda_codec_read - send a command and get the response
+ * @codec: the HDA codec
+ * @nid: NID to send the command
+ * @direct: direct flag
+ * @verb: the verb to send
+ * @parm: the parameter for the verb
+ *
+ * Send a single command and read the corresponding response.
+ *
+ * Returns the obtained response value, or -1 for an error.
+ */
+unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid, int direct,
+                                unsigned int verb, unsigned int parm)
+{
+    unsigned int res;
+    down(&codec->bus->cmd_mutex);
+    if (! codec->bus->ops.command(codec, nid, direct, verb, parm))
+        res = codec->bus->ops.get_response(codec);
+    else
+        res = (unsigned int)-1;
+    up(&codec->bus->cmd_mutex);
+    return res;
+}
+
+EXPORT_SYMBOL(snd_hda_codec_read);
+
+/**
+ * snd_hda_codec_write - send a single command without waiting for response
+ * @codec: the HDA codec
+ * @nid: NID to send the command
+ * @direct: direct flag
+ * @verb: the verb to send
+ * @parm: the parameter for the verb
+ *
+ * Send a single command without waiting for response.
+ *
+ * Returns 0 if successful, or a negative error code.
+ */
+int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int direct,
+                        unsigned int verb, unsigned int parm)
+{
+    int err;
+    down(&codec->bus->cmd_mutex);
+    err = codec->bus->ops.command(codec, nid, direct, verb, parm);
+    up(&codec->bus->cmd_mutex);
+    return err;
+}
+
+/**
+ * snd_hda_sequence_write - sequence writes
+ * @codec: the HDA codec
+ * @seq: VERB array to send
+ *
+ * Send the commands sequentially from the given array.
+ * The array must be terminated with NID=0.
+ */
+void snd_hda_sequence_write(struct hda_codec *codec, const struct hda_verb *seq)
+{
+    for (; seq->nid; seq++)
+        snd_hda_codec_write(codec, seq->nid, 0, seq->verb, seq->param);
+}
+
+/**
+ * snd_hda_get_sub_nodes - get the range of sub nodes
+ * @codec: the HDA codec
+ * @nid: NID to parse
+ * @start_id: the pointer to store the start NID
+ *
+ * Parse the NID and store the start NID of its sub-nodes.
+ * Returns the number of sub-nodes.
+ */
+int snd_hda_get_sub_nodes(struct hda_codec *codec, hda_nid_t nid, hda_nid_t *start_id)
+{
+    unsigned int parm;
+
+    parm = snd_hda_param_read(codec, nid, AC_PAR_NODE_COUNT);
+    *start_id = (parm >> 16) & 0x7fff;
+    return (int)(parm & 0x7fff);
+}
+
+/**
+ * snd_hda_get_connections - get connection list
+ * @codec: the HDA codec
+ * @nid: NID to parse
+ * @conn_list: connection list array
+ * @max_conns: max. number of connections to store
+ *
+ * Parses the connection list of the given widget and stores the list
+ * of NIDs.
+ *
+ * Returns the number of connections, or a negative error code.
+ */
+int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
+                            hda_nid_t *conn_list, int max_conns)
+{
+    unsigned int parm;
+    int i, conn_len, conns;
+    unsigned int shift, num_elems, mask;
+    hda_nid_t prev_nid;
+
+    snd_assert(conn_list && max_conns > 0, return -EINVAL);
+
+    parm = snd_hda_param_read(codec, nid, AC_PAR_CONNLIST_LEN);
+    if (parm & AC_CLIST_LONG) {
+        /* long form */
+        shift = 16;
+        num_elems = 2;
+    } else {
+        /* short form */
+        shift = 8;
+        num_elems = 4;
+    }
+    conn_len = parm & AC_CLIST_LENGTH;
+    mask = (1 << (shift-1)) - 1;
+
+    if (! conn_len)
+        return 0; /* no connection */
+
+    if (conn_len == 1) {
+        /* single connection */
+        parm = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONNECT_LIST, 0);
+        conn_list[0] = parm & mask;
+        return 1;
+    }
+
+    /* multi connection */
+    conns = 0;
+    prev_nid = 0;
+    for (i = 0; i < conn_len; i++) {
+        int range_val;
+        hda_nid_t val, n;
+
+        if (i % num_elems == 0)
+            parm = snd_hda_codec_read(codec, nid, 0,
+                                      AC_VERB_GET_CONNECT_LIST, i);
+        range_val = !! (parm & (1 << (shift-1))); /* ranges */
+        val = parm & mask;
+        parm >>= shift;
+        if (range_val) {
+            /* ranges between the previous and this one */
+            if (! prev_nid || prev_nid >= val) {
+                snd_printk(KERN_WARNING "hda_codec: invalid dep_range_val %x:%x\n", prev_nid, val);
+                continue;
+            }
+            for (n = prev_nid + 1; n <= val; n++) {
+                if (conns >= max_conns) {
+                    snd_printk(KERN_ERR "Too many connections\n");
+                    return -EINVAL;
+                }
+                conn_list[conns++] = n;
+            }
+        } else {
+            if (conns >= max_conns) {
+                snd_printk(KERN_ERR "Too many connections\n");
+                return -EINVAL;
+            }
+            conn_list[conns++] = val;
+        }
+        prev_nid = val;
+    }
+    return conns;
+}
+
+
+/**
+ * snd_hda_queue_unsol_event - add an unsolicited event to queue
+ * @bus: the BUS
+ * @res: unsolicited event (lower 32bit of RIRB entry)
+ * @res_ex: codec addr and flags (upper 32bit or RIRB entry)
+ *
+ * Adds the given event to the queue.  The events are processed in
+ * the workqueue asynchronously.  Call this function in the interrupt
+ * hanlder when RIRB receives an unsolicited event.
+ *
+ * Returns 0 if successful, or a negative error code.
+ */
+int snd_hda_queue_unsol_event(struct hda_bus *bus, u32 res, u32 res_ex)
+{
+    struct hda_bus_unsolicited *unsol;
+    unsigned int wp;
+
+    if ((unsol = bus->unsol) == NULL)
+        return 0;
+
+    wp = (unsol->wp + 1) % HDA_UNSOL_QUEUE_SIZE;
+    unsol->wp = wp;
+
+    wp <<= 1;
+    unsol->queue[wp] = res;
+    unsol->queue[wp + 1] = res_ex;
+
+    queue_work(unsol->workq, &unsol->work);
+
+    return 0;
+}
+
+/*
+ * process queueud unsolicited events
+ */
+static void process_unsol_events(void *data)
+{
+    struct hda_bus *bus = data;
+    struct hda_bus_unsolicited *unsol = bus->unsol;
+    struct hda_codec *codec;
+    unsigned int rp, caddr, res;
+
+    while (unsol->rp != unsol->wp) {
+        rp = (unsol->rp + 1) % HDA_UNSOL_QUEUE_SIZE;
+        unsol->rp = rp;
+        rp <<= 1;
+        res = unsol->queue[rp];
+        caddr = unsol->queue[rp + 1];
+        if (! (caddr & (1 << 4))) /* no unsolicited event? */
+            continue;
+        codec = bus->caddr_tbl[caddr & 0x0f];
+        if (codec && codec->patch_ops.unsol_event)
+            codec->patch_ops.unsol_event(codec, res);
+    }
+}
+
+/*
+ * initialize unsolicited queue
+ */
+static int init_unsol_queue(struct hda_bus *bus)
+{
+    struct hda_bus_unsolicited *unsol;
+
+    if (bus->unsol) /* already initialized */
+        return 0;
+
+    unsol = kzalloc(sizeof(*unsol), GFP_KERNEL);
+    if (! unsol) {
+        snd_printk(KERN_ERR "hda_codec: can't allocate unsolicited queue\n");
+        return -ENOMEM;
+    }
+    unsol->workq = create_workqueue("hda_codec");
+    if (! unsol->workq) {
+        snd_printk(KERN_ERR "hda_codec: can't create workqueue\n");
+        kfree(unsol);
+        return -ENOMEM;
+    }
+    INIT_WORK(&unsol->work, process_unsol_events, bus);
+    bus->unsol = unsol;
+    return 0;
+}
+
+/*
+ * destructor
+ */
+static void snd_hda_codec_free(struct hda_codec *codec);
+
+static int snd_hda_bus_free(struct hda_bus *bus)
+{
+    struct list_head *p, *n;
+
+    if (! bus)
+        return 0;
+    if (bus->unsol) {
+        destroy_workqueue(bus->unsol->workq);
+        kfree(bus->unsol);
+    }
+    list_for_each_safe(p, n, &bus->codec_list) {
+        struct hda_codec *codec = list_entry(p, struct hda_codec, list);
+        snd_hda_codec_free(codec);
+    }
+    if (bus->ops.private_free)
+        bus->ops.private_free(bus);
+    kfree(bus);
+    return 0;
+}
+
+static int snd_hda_bus_dev_free(struct snd_device *device)
+{
+    struct hda_bus *bus = device->device_data;
+    return snd_hda_bus_free(bus);
+}
+
+/**
+ * snd_hda_bus_new - create a HDA bus
+ * @card: the card entry
+ * @temp: the template for hda_bus information
+ * @busp: the pointer to store the created bus instance
+ *
+ * Returns 0 if successful, or a negative error code.
+ */
+int snd_hda_bus_new(struct snd_card *card, const struct hda_bus_template *temp,
+                    struct hda_bus **busp)
+{
+    struct hda_bus *bus;
+    int err;
+    static struct snd_device_ops dev_ops = {
+        .dev_free = snd_hda_bus_dev_free,
+    };
+
+    snd_assert(temp, return -EINVAL);
+    snd_assert(temp->ops.command && temp->ops.get_response, return -EINVAL);
+
+    if (busp)
+        *busp = NULL;
+
+    bus = kzalloc(sizeof(*bus), GFP_KERNEL);
+    if (bus == NULL) {
+        snd_printk(KERN_ERR "can't allocate struct hda_bus\n");
+        return -ENOMEM;
+    }
+
+    bus->card = card;
+    bus->private_data = temp->private_data;
+    bus->pci = temp->pci;
+    bus->modelname = temp->modelname;
+    bus->ops = temp->ops;
+
+    init_MUTEX(&bus->cmd_mutex);
+    INIT_LIST_HEAD(&bus->codec_list);
+
+    if ((err = snd_device_new(card, SNDRV_DEV_BUS, bus, &dev_ops)) < 0) {
+        snd_hda_bus_free(bus);
+        return err;
+    }
+    if (busp)
+        *busp = bus;
+    return 0;
+}
+
+
+/*
+ * find a matching codec preset
+ */
+static const struct hda_codec_preset *find_codec_preset(struct hda_codec *codec)
+{
+    const struct hda_codec_preset **tbl, *preset;
+
+    for (tbl = hda_preset_tables; *tbl; tbl++) {
+        for (preset = *tbl; preset->id; preset++) {
+            u32 mask = preset->mask;
+            if (! mask)
+                mask = ~0;
+            if (preset->id == (codec->vendor_id & mask) &&
+                (! preset->rev ||
+                 preset->rev == codec->revision_id))
+                return preset;
+        }
+    }
+    return NULL;
+}
+
+/*
+ * snd_hda_get_codec_name - store the codec name
+ */
+void snd_hda_get_codec_name(struct hda_codec *codec,
+                            char *name, int namelen)
+{
+    const struct hda_vendor_id *c;
+    const char *vendor = NULL;
+    u16 vendor_id = codec->vendor_id >> 16;
+    char tmp[16];
+
+    for (c = hda_vendor_ids; c->id; c++) {
+        if (c->id == vendor_id) {
+            vendor = c->name;
+            break;
+        }
+    }
+    if (! vendor) {
+        sprintf(tmp, "Generic %04x", vendor_id);
+        vendor = tmp;
+    }
+    if (codec->preset && codec->preset->name)
+        sprintf(name, "%s %s", vendor, codec->preset->name);
+    else
+        sprintf(name, "%s ID %x", vendor, codec->vendor_id & 0xffff);
+}
+
+/*
+ * look for an AFG and MFG nodes
+ */
+static void setup_fg_nodes(struct hda_codec *codec)
+{
+    int i, total_nodes;
+    hda_nid_t nid;
+
+    total_nodes = snd_hda_get_sub_nodes(codec, AC_NODE_ROOT, &nid);
+    for (i = 0; i < total_nodes; i++, nid++) {
+        switch((snd_hda_param_read(codec, nid, AC_PAR_FUNCTION_TYPE) & 0xff)) {
+        case AC_GRP_AUDIO_FUNCTION:
+            codec->afg = nid;
+            break;
+        case AC_GRP_MODEM_FUNCTION:
+            codec->mfg = nid;
+            break;
+        default:
+            break;
+        }
+    }
+}
+
+/*
+ * read widget caps for each widget and store in cache
+ */
+static int read_widget_caps(struct hda_codec *codec, hda_nid_t fg_node)
+{
+    int i;
+    hda_nid_t nid;
+
+    codec->num_nodes = snd_hda_get_sub_nodes(codec, fg_node,
+                                             &codec->start_nid);
+    codec->wcaps = kmalloc(codec->num_nodes * 4, GFP_KERNEL);
+    if (! codec->wcaps)
+        return -ENOMEM;
+    nid = codec->start_nid;
+    for (i = 0; i < codec->num_nodes; i++, nid++)
+        codec->wcaps[i] = snd_hda_param_read(codec, nid,
+                                             AC_PAR_AUDIO_WIDGET_CAP);
+    return 0;
+}
+
+
+/*
+ * codec destructor
+ */
+static void snd_hda_codec_free(struct hda_codec *codec)
+{
+    if (! codec)
+        return;
+    list_del(&codec->list);
+    codec->bus->caddr_tbl[codec->addr] = NULL;
+    if (codec->patch_ops.free)
+        codec->patch_ops.free(codec);
+    kfree(codec->amp_info);
+    kfree(codec->wcaps);
+    kfree(codec);
+}
+
+static void init_amp_hash(struct hda_codec *codec);
+
+/**
+ * snd_hda_codec_new - create a HDA codec
+ * @bus: the bus to assign
+ * @codec_addr: the codec address
+ * @codecp: the pointer to store the generated codec
+ *
+ * Returns 0 if successful, or a negative error code.
+ */
+int snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr,
+                      struct hda_codec **codecp)
+{
+    struct hda_codec *codec;
+    char component[13];
+    int err;
+
+    snd_assert(bus, return -EINVAL);
+    snd_assert(codec_addr <= HDA_MAX_CODEC_ADDRESS, return -EINVAL);
+
+    if (bus->caddr_tbl[codec_addr]) {
+        snd_printk(KERN_ERR "hda_codec: address 0x%x is already occupied\n", codec_addr);
+        return -EBUSY;
+    }
+
+    codec = kzalloc(sizeof(*codec), GFP_KERNEL);
+    if (codec == NULL) {
+        snd_printk(KERN_ERR "can't allocate struct hda_codec\n");
+        return -ENOMEM;
+    }
+
+    codec->bus = bus;
+    codec->addr = codec_addr;
+    init_MUTEX(&codec->spdif_mutex);
+    init_amp_hash(codec);
+
+    list_add_tail(&codec->list, &bus->codec_list);
+    bus->caddr_tbl[codec_addr] = codec;
+
+    codec->vendor_id = snd_hda_param_read(codec, AC_NODE_ROOT, AC_PAR_VENDOR_ID);
+    if (codec->vendor_id == -1)
+        /* read again, hopefully the access method was corrected
+         * in the last read...
+         */
+        codec->vendor_id = snd_hda_param_read(codec, AC_NODE_ROOT,
+                                              AC_PAR_VENDOR_ID);
+    codec->subsystem_id = snd_hda_param_read(codec, AC_NODE_ROOT, AC_PAR_SUBSYSTEM_ID);
+    codec->revision_id = snd_hda_param_read(codec, AC_NODE_ROOT, AC_PAR_REV_ID);
+
+    setup_fg_nodes(codec);
+    if (! codec->afg && ! codec->mfg) {
+        snd_printdd("hda_codec: no AFG or MFG node found\n");
+        snd_hda_codec_free(codec);
+        return -ENODEV;
+    }
+
+    if (read_widget_caps(codec, codec->afg ? codec->afg : codec->mfg) < 0) {
+        snd_printk(KERN_ERR "hda_codec: cannot malloc\n");
+        snd_hda_codec_free(codec);
+        return -ENOMEM;
+    }
+
+    if (! codec->subsystem_id) {
+        hda_nid_t nid = codec->afg ? codec->afg : codec->mfg;
+        codec->subsystem_id = snd_hda_codec_read(codec, nid, 0,
+                                                 AC_VERB_GET_SUBSYSTEM_ID,
+                                                 0);
+    }
+
+    codec->preset = find_codec_preset(codec);
+    if (! *bus->card->mixername)
+        snd_hda_get_codec_name(codec, bus->card->mixername,
+                               sizeof(bus->card->mixername));
+
+    if (codec->preset && codec->preset->patch)
+        err = codec->preset->patch(codec);
+    else
+        err = snd_hda_parse_generic_codec(codec);
+    if (err < 0) {
+        snd_hda_codec_free(codec);
+        return err;
+    }
+
+    if (codec->patch_ops.unsol_event)
+        init_unsol_queue(bus);
+
+    snd_hda_codec_proc_new(codec);
+
+    sprintf(component, "HDA:%08x", codec->vendor_id);
+    snd_component_add(codec->bus->card, component);
+
+    if (codecp)
+        *codecp = codec;
+    return 0;
+}
+
+/**
+ * snd_hda_codec_setup_stream - set up the codec for streaming
+ * @codec: the CODEC to set up
+ * @nid: the NID to set up
+ * @stream_tag: stream tag to pass, it's between 0x1 and 0xf.
+ * @channel_id: channel id to pass, zero based.
+ * @format: stream format.
+ */
+void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid, u32 stream_tag,
+                                int channel_id, int format)
+{
+    if (! nid)
+        return;
+
+    snd_printdd("hda_codec_setup_stream: NID=0x%x, stream=0x%x, channel=%d, format=0x%x\n",
+                nid, stream_tag, channel_id, format);
+    snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CHANNEL_STREAMID,
+                        (stream_tag << 4) | channel_id);
+    msleep(1);
+    snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, format);
+}
+
+
+/*
+ * amp access functions
+ */
+
+/* FIXME: more better hash key? */
+#define HDA_HASH_KEY(nid,dir,idx) (u32)((nid) + ((idx) << 16) + ((dir) << 24))
+#define INFO_AMP_CAPS	(1<<0)
+#define INFO_AMP_VOL(ch)	(1 << (1 + (ch)))
+
+/* initialize the hash table */
+static void init_amp_hash(struct hda_codec *codec)
+{
+    memset(codec->amp_hash, 0xff, sizeof(codec->amp_hash));
+    codec->num_amp_entries = 0;
+    codec->amp_info_size = 0;
+    codec->amp_info = NULL;
+}
+
+/* query the hash.  allocate an entry if not found. */
+static struct hda_amp_info *get_alloc_amp_hash(struct hda_codec *codec, u32 key)
+{
+    u16 idx = key % (u16)ARRAY_SIZE(codec->amp_hash);
+    u16 cur = codec->amp_hash[idx];
+    struct hda_amp_info *info;
+
+    while (cur != 0xffff) {
+        info = &codec->amp_info[cur];
+        if (info->key == key)
+            return info;
+        cur = info->next;
+    }
+
+    /* add a new hash entry */
+    if (codec->num_amp_entries >= codec->amp_info_size) {
+        /* reallocate the array */
+        int new_size = codec->amp_info_size + 64;
+        struct hda_amp_info *new_info = kcalloc(new_size, sizeof(struct hda_amp_info),
+                                                GFP_KERNEL);
+        if (! new_info) {
+            snd_printk(KERN_ERR "hda_codec: can't malloc amp_info\n");
+            return NULL;
+        }
+        if (codec->amp_info) {
+            memcpy(new_info, codec->amp_info,
+                   codec->amp_info_size * sizeof(struct hda_amp_info));
+            kfree(codec->amp_info);
+        }
+        codec->amp_info_size = new_size;
+        codec->amp_info = new_info;
+    }
+    cur = codec->num_amp_entries++;
+    info = &codec->amp_info[cur];
+    info->key = key;
+    info->status = 0; /* not initialized yet */
+    info->next = codec->amp_hash[idx];
+    codec->amp_hash[idx] = cur;
+
+    return info;
+}
+
+/*
+ * query AMP capabilities for the given widget and direction
+ */
+static u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction)
+{
+    struct hda_amp_info *info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, 0));
+
+    if (! info)
+        return 0;
+    if (! (info->status & INFO_AMP_CAPS)) {
+        if (! (get_wcaps(codec, nid) & AC_WCAP_AMP_OVRD))
+            nid = codec->afg;
+        info->amp_caps = snd_hda_param_read(codec, nid, direction == HDA_OUTPUT ?
+                                            AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP);
+        info->status |= INFO_AMP_CAPS;
+    }
+    return info->amp_caps;
+}
+
+/*
+ * read the current volume to info
+ * if the cache exists, read the cache value.
+ */
+static unsigned int get_vol_mute(struct hda_codec *codec, struct hda_amp_info *info,
+                                 hda_nid_t nid, int ch, int direction, int index)
+{
+    u32 val, parm;
+
+    if (info->status & INFO_AMP_VOL(ch))
+        return info->vol[ch];
+
+    parm = ch ? AC_AMP_GET_RIGHT : AC_AMP_GET_LEFT;
+    parm |= direction == HDA_OUTPUT ? AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT;
+    parm |= index;
+    val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, parm);
+    info->vol[ch] = val & 0xff;
+    info->status |= INFO_AMP_VOL(ch);
+    return info->vol[ch];
+}
+
+/*
+ * write the current volume in info to the h/w and update the cache
+ */
+static void put_vol_mute(struct hda_codec *codec, struct hda_amp_info *info,
+                         hda_nid_t nid, int ch, int direction, int index, int val)
+{
+    u32 parm;
+
+    parm = ch ? AC_AMP_SET_RIGHT : AC_AMP_SET_LEFT;
+    parm |= direction == HDA_OUTPUT ? AC_AMP_SET_OUTPUT : AC_AMP_SET_INPUT;
+    parm |= index << AC_AMP_SET_INDEX_SHIFT;
+    parm |= val;
+    snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, parm);
+    info->vol[ch] = val;
+}
+
+/*
+ * read AMP value.  The volume is between 0 to 0x7f, 0x80 = mute bit.
+ */
+int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch,
+                           int direction, int index)
+{
+    struct hda_amp_info *info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, index));
+    if (! info)
+        return 0;
+    return get_vol_mute(codec, info, nid, ch, direction, index);
+}
+
+/*
+ * update the AMP value, mask = bit mask to set, val = the value
+ */
+int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch,
+                             int direction, int idx, int mask, int val)
+{
+    struct hda_amp_info *info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, idx));
+
+    if (! info)
+        return 0;
+    val &= mask;
+    val |= get_vol_mute(codec, info, nid, ch, direction, idx) & ~mask;
+    if (info->vol[ch] == val && ! codec->in_resume)
+        return 0;
+    put_vol_mute(codec, info, nid, ch, direction, idx, val);
+    return 1;
+}
+
+
+/*
+ * AMP control callbacks
+ */
+/* retrieve parameters from private_value */
+#define get_amp_nid(kc)		((kc)->private_value & 0xffff)
+#define get_amp_channels(kc)	(((kc)->private_value >> 16) & 0x3)
+#define get_amp_direction(kc)	(((kc)->private_value >> 18) & 0x1)
+#define get_amp_index(kc)	(((kc)->private_value >> 19) & 0xf)
+
+/* volume */
+int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+    struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+    u16 nid = get_amp_nid(kcontrol);
+    u8 chs = get_amp_channels(kcontrol);
+    int dir = get_amp_direction(kcontrol);
+    u32 caps;
+
+    caps = query_amp_caps(codec, nid, dir);
+    caps = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT; /* num steps */
+    if (! caps) {
+        printk(KERN_WARNING "hda_codec: num_steps = 0 for NID=0x%x\n", nid);
+        return -EINVAL;
+    }
+    uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+    uinfo->count = chs == 3 ? 2 : 1;
+    uinfo->value.integer.min = 0;
+    uinfo->value.integer.max = caps;
+    return 0;
+}
+
+int snd_hda_mixer_amp_volume_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+    struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+    hda_nid_t nid = get_amp_nid(kcontrol);
+    int chs = get_amp_channels(kcontrol);
+    int dir = get_amp_direction(kcontrol);
+    int idx = get_amp_index(kcontrol);
+    long *valp = ucontrol->value.integer.value;
+
+    if (chs & 1)
+        *valp++ = snd_hda_codec_amp_read(codec, nid, 0, dir, idx) & 0x7f;
+    if (chs & 2)
+        *valp = snd_hda_codec_amp_read(codec, nid, 1, dir, idx) & 0x7f;
+    return 0;
+}
+
+int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+    struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+    hda_nid_t nid = get_amp_nid(kcontrol);
+    int chs = get_amp_channels(kcontrol);
+    int dir = get_amp_direction(kcontrol);
+    int idx = get_amp_index(kcontrol);
+    long *valp = ucontrol->value.integer.value;
+    int change = 0;
+
+    if (chs & 1) {
+        change = snd_hda_codec_amp_update(codec, nid, 0, dir, idx,
+                                          0x7f, *valp);
+        valp++;
+    }
+    if (chs & 2)
+        change |= snd_hda_codec_amp_update(codec, nid, 1, dir, idx,
+                                           0x7f, *valp);
+    return change;
+}
+
+int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag,
+                          unsigned int size, unsigned int __user *_tlv)
+{
+    struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+    hda_nid_t nid = get_amp_nid(kcontrol);
+    int dir = get_amp_direction(kcontrol);
+    u32 caps, val1, val2;
+
+    if (size < 4 * sizeof(unsigned int))
+        return -ENOMEM;
+    caps = query_amp_caps(codec, nid, dir);
+    val2 = (((caps & AC_AMPCAP_STEP_SIZE) >> AC_AMPCAP_STEP_SIZE_SHIFT) + 1) * 25;
+    val1 = -((caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT);
+    val1 = ((int)val1) * ((int)val2);
+    if (put_user(SNDRV_CTL_TLVT_DB_SCALE, _tlv))
+        return -EFAULT;
+    if (put_user(2 * sizeof(unsigned int), _tlv + 1))
+        return -EFAULT;
+    if (put_user(val1, _tlv + 2))
+        return -EFAULT;
+    if (put_user(val2, _tlv + 3))
+        return -EFAULT;
+    return 0;
+}
+
+
+/* switch */
+int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+    int chs = get_amp_channels(kcontrol);
+
+    uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+    uinfo->count = chs == 3 ? 2 : 1;
+    uinfo->value.integer.min = 0;
+    uinfo->value.integer.max = 1;
+    return 0;
+}
+
+int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+    struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+    hda_nid_t nid = get_amp_nid(kcontrol);
+    int chs = get_amp_channels(kcontrol);
+    int dir = get_amp_direction(kcontrol);
+    int idx = get_amp_index(kcontrol);
+    long *valp = ucontrol->value.integer.value;
+
+    if (chs & 1)
+        *valp++ = (snd_hda_codec_amp_read(codec, nid, 0, dir, idx) & 0x80) ? 0 : 1;
+    if (chs & 2)
+        *valp = (snd_hda_codec_amp_read(codec, nid, 1, dir, idx) & 0x80) ? 0 : 1;
+    return 0;
+}
+
+int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+    struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+    hda_nid_t nid = get_amp_nid(kcontrol);
+    int chs = get_amp_channels(kcontrol);
+    int dir = get_amp_direction(kcontrol);
+    int idx = get_amp_index(kcontrol);
+    long *valp = ucontrol->value.integer.value;
+    int change = 0;
+
+    if (chs & 1) {
+        change = snd_hda_codec_amp_update(codec, nid, 0, dir, idx,
+                                          0x80, *valp ? 0 : 0x80);
+        valp++;
+    }
+    if (chs & 2)
+        change |= snd_hda_codec_amp_update(codec, nid, 1, dir, idx,
+                                           0x80, *valp ? 0 : 0x80);
+
+    return change;
+}
+
+/*
+ * bound volume controls
+ *
+ * bind multiple volumes (# indices, from 0)
+ */
+
+#define AMP_VAL_IDX_SHIFT	19
+#define AMP_VAL_IDX_MASK	(0x0f<<19)
+
+int snd_hda_mixer_bind_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+    struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+    unsigned long pval;
+    int err;
+
+    down(&codec->spdif_mutex); /* reuse spdif_mutex */
+    pval = kcontrol->private_value;
+    kcontrol->private_value = pval & ~AMP_VAL_IDX_MASK; /* index 0 */
+    err = snd_hda_mixer_amp_switch_get(kcontrol, ucontrol);
+    kcontrol->private_value = pval;
+    up(&codec->spdif_mutex);
+    return err;
+}
+
+int snd_hda_mixer_bind_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+    struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+    unsigned long pval;
+    int i, indices, err = 0, change = 0;
+
+    down(&codec->spdif_mutex); /* reuse spdif_mutex */
+    pval = kcontrol->private_value;
+    indices = (pval & AMP_VAL_IDX_MASK) >> AMP_VAL_IDX_SHIFT;
+    for (i = 0; i < indices; i++) {
+        kcontrol->private_value = (pval & ~AMP_VAL_IDX_MASK) | (i << AMP_VAL_IDX_SHIFT);
+        err = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
+        if (err < 0)
+            break;
+        change |= err;
+    }
+    kcontrol->private_value = pval;
+    up(&codec->spdif_mutex);
+    return err < 0 ? err : change;
+}
+
+/*
+ * SPDIF out controls
+ */
+
+static int snd_hda_spdif_mask_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+    uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+    uinfo->count = 1;
+    return 0;
+}
+
+static int snd_hda_spdif_cmask_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+    ucontrol->value.iec958.status[0] = IEC958_AES0_PROFESSIONAL |
+        IEC958_AES0_NONAUDIO |
+        IEC958_AES0_CON_EMPHASIS_5015 |
+        IEC958_AES0_CON_NOT_COPYRIGHT;
+    ucontrol->value.iec958.status[1] = IEC958_AES1_CON_CATEGORY |
+        IEC958_AES1_CON_ORIGINAL;
+    return 0;
+}
+
+static int snd_hda_spdif_pmask_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+    ucontrol->value.iec958.status[0] = IEC958_AES0_PROFESSIONAL |
+        IEC958_AES0_NONAUDIO |
+        IEC958_AES0_PRO_EMPHASIS_5015;
+    return 0;
+}
+
+static int snd_hda_spdif_default_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+    struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+
+    ucontrol->value.iec958.status[0] = codec->spdif_status & 0xff;
+    ucontrol->value.iec958.status[1] = (codec->spdif_status >> 8) & 0xff;
+    ucontrol->value.iec958.status[2] = (codec->spdif_status >> 16) & 0xff;
+    ucontrol->value.iec958.status[3] = (codec->spdif_status >> 24) & 0xff;
+
+    return 0;
+}
+
+/* convert from SPDIF status bits to HDA SPDIF bits
+ * bit 0 (DigEn) is always set zero (to be filled later)
+ */
+static unsigned short convert_from_spdif_status(unsigned int sbits)
+{
+    unsigned short val = 0;
+
+    if (sbits & IEC958_AES0_PROFESSIONAL)
+        val |= 1 << 6;
+    if (sbits & IEC958_AES0_NONAUDIO)
+        val |= 1 << 5;
+    if (sbits & IEC958_AES0_PROFESSIONAL) {
+        if ((sbits & IEC958_AES0_PRO_EMPHASIS) == IEC958_AES0_PRO_EMPHASIS_5015)
+            val |= 1 << 3;
+    } else {
+        if ((sbits & IEC958_AES0_CON_EMPHASIS) == IEC958_AES0_CON_EMPHASIS_5015)
+            val |= 1 << 3;
+        if (! (sbits & IEC958_AES0_CON_NOT_COPYRIGHT))
+            val |= 1 << 4;
+        if (sbits & (IEC958_AES1_CON_ORIGINAL << 8))
+            val |= 1 << 7;
+        val |= sbits & (IEC958_AES1_CON_CATEGORY << 8);
+    }
+    return val;
+}
+
+/* convert to SPDIF status bits from HDA SPDIF bits
+ */
+static unsigned int convert_to_spdif_status(unsigned short val)
+{
+    unsigned int sbits = 0;
+
+    if (val & (1 << 5))
+        sbits |= IEC958_AES0_NONAUDIO;
+    if (val & (1 << 6))
+        sbits |= IEC958_AES0_PROFESSIONAL;
+    if (sbits & IEC958_AES0_PROFESSIONAL) {
+        if (sbits & (1 << 3))
+            sbits |= IEC958_AES0_PRO_EMPHASIS_5015;
+    } else {
+        if (val & (1 << 3))
+            sbits |= IEC958_AES0_CON_EMPHASIS_5015;
+        if (! (val & (1 << 4)))
+            sbits |= IEC958_AES0_CON_NOT_COPYRIGHT;
+        if (val & (1 << 7))
+            sbits |= (IEC958_AES1_CON_ORIGINAL << 8);
+        sbits |= val & (0x7f << 8);
+    }
+    return sbits;
+}
+
+static int snd_hda_spdif_default_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+    struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+    hda_nid_t nid = kcontrol->private_value;
+    unsigned short val;
+    int change;
+
+    down(&codec->spdif_mutex);
+    codec->spdif_status = ucontrol->value.iec958.status[0] |
+        ((unsigned int)ucontrol->value.iec958.status[1] << 8) |
+        ((unsigned int)ucontrol->value.iec958.status[2] << 16) |
+        ((unsigned int)ucontrol->value.iec958.status[3] << 24);
+    val = convert_from_spdif_status(codec->spdif_status);
+    val |= codec->spdif_ctls & 1;
+    change = codec->spdif_ctls != val;
+    codec->spdif_ctls = val;
+
+    if (change || codec->in_resume) {
+        snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1, val & 0xff);
+        snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_2, val >> 8);
+    }
+
+    up(&codec->spdif_mutex);
+    return change;
+}
+
+static int snd_hda_spdif_out_switch_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *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_hda_spdif_out_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+    struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+
+    ucontrol->value.integer.value[0] = codec->spdif_ctls & 1;
+    return 0;
+}
+
+static int snd_hda_spdif_out_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+    struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+    hda_nid_t nid = kcontrol->private_value;
+    unsigned short val;
+    int change;
+
+    down(&codec->spdif_mutex);
+    val = codec->spdif_ctls & ~1;
+    if (ucontrol->value.integer.value[0])
+        val |= 1;
+    change = codec->spdif_ctls != val;
+    if (change || codec->in_resume) {
+        codec->spdif_ctls = val;
+        snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1, val & 0xff);
+        snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+                            AC_AMP_SET_RIGHT | AC_AMP_SET_LEFT |
+                            AC_AMP_SET_OUTPUT | ((val & 1) ? 0 : 0x80));
+    }
+    up(&codec->spdif_mutex);
+    return change;
+}
+
+static struct snd_kcontrol_new dig_mixes[] = {
+    {
+        .access = SNDRV_CTL_ELEM_ACCESS_READ,
+        .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+        .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK),
+        .info = snd_hda_spdif_mask_info,
+        .get = snd_hda_spdif_cmask_get,
+    },
+    {
+        .access = SNDRV_CTL_ELEM_ACCESS_READ,
+        .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+        .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PRO_MASK),
+        .info = snd_hda_spdif_mask_info,
+        .get = snd_hda_spdif_pmask_get,
+    },
+    {
+        .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+        .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
+        .info = snd_hda_spdif_mask_info,
+        .get = snd_hda_spdif_default_get,
+        .put = snd_hda_spdif_default_put,
+    },
+    {
+        .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+        .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH),
+        .info = snd_hda_spdif_out_switch_info,
+        .get = snd_hda_spdif_out_switch_get,
+        .put = snd_hda_spdif_out_switch_put,
+    },
+    {0} /* end */
+};
+
+/**
+ * snd_hda_create_spdif_out_ctls - create Output SPDIF-related controls
+ * @codec: the HDA codec
+ * @nid: audio out widget NID
+ *
+ * Creates controls related with the SPDIF output.
+ * Called from each patch supporting the SPDIF out.
+ *
+ * Returns 0 if successful, or a negative error code.
+ */
+int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid)
+{
+    int err;
+    struct snd_kcontrol *kctl;
+    struct snd_kcontrol_new *dig_mix;
+
+    for (dig_mix = dig_mixes; dig_mix->name; dig_mix++) {
+        kctl = snd_ctl_new1(dig_mix, codec);
+        kctl->private_value = nid;
+        if ((err = snd_ctl_add(codec->bus->card, kctl)) < 0)
+            return err;
+    }
+    codec->spdif_ctls = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_DIGI_CONVERT, 0);
+    codec->spdif_status = convert_to_spdif_status(codec->spdif_ctls);
+    return 0;
+}
+
+/*
+ * SPDIF input
+ */
+
+#define snd_hda_spdif_in_switch_info	snd_hda_spdif_out_switch_info
+
+static int snd_hda_spdif_in_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+    struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+
+    ucontrol->value.integer.value[0] = codec->spdif_in_enable;
+    return 0;
+}
+
+static int snd_hda_spdif_in_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+    struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+    hda_nid_t nid = kcontrol->private_value;
+    unsigned int val = !!ucontrol->value.integer.value[0];
+    int change;
+
+    down(&codec->spdif_mutex);
+    change = codec->spdif_in_enable != val;
+    if (change || codec->in_resume) {
+        codec->spdif_in_enable = val;
+        snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1, val);
+    }
+    up(&codec->spdif_mutex);
+    return change;
+}
+
+static int snd_hda_spdif_in_status_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+    struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+    hda_nid_t nid = kcontrol->private_value;
+    unsigned short val;
+    unsigned int sbits;
+
+    val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_DIGI_CONVERT, 0);
+    sbits = convert_to_spdif_status(val);
+    ucontrol->value.iec958.status[0] = sbits;
+    ucontrol->value.iec958.status[1] = sbits >> 8;
+    ucontrol->value.iec958.status[2] = sbits >> 16;
+    ucontrol->value.iec958.status[3] = sbits >> 24;
+    return 0;
+}
+
+static struct snd_kcontrol_new dig_in_ctls[] = {
+    {
+        .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+        .name = SNDRV_CTL_NAME_IEC958("",CAPTURE,SWITCH),
+        .info = snd_hda_spdif_in_switch_info,
+        .get = snd_hda_spdif_in_switch_get,
+        .put = snd_hda_spdif_in_switch_put,
+    },
+    {
+        .access = SNDRV_CTL_ELEM_ACCESS_READ,
+        .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+        .name = SNDRV_CTL_NAME_IEC958("",CAPTURE,DEFAULT),
+        .info = snd_hda_spdif_mask_info,
+        .get = snd_hda_spdif_in_status_get,
+    },
+    {0} /* end */
+};
+
+/**
+ * snd_hda_create_spdif_in_ctls - create Input SPDIF-related controls
+ * @codec: the HDA codec
+ * @nid: audio in widget NID
+ *
+ * Creates controls related with the SPDIF input.
+ * Called from each patch supporting the SPDIF in.
+ *
+ * Returns 0 if successful, or a negative error code.
+ */
+int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid)
+{
+    int err;
+    struct snd_kcontrol *kctl;
+    struct snd_kcontrol_new *dig_mix;
+
+    for (dig_mix = dig_in_ctls; dig_mix->name; dig_mix++) {
+        kctl = snd_ctl_new1(dig_mix, codec);
+        kctl->private_value = nid;
+        if ((err = snd_ctl_add(codec->bus->card, kctl)) < 0)
+            return err;
+    }
+    codec->spdif_in_enable = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_DIGI_CONVERT, 0) & 1;
+    return 0;
+}
+
+
+/*
+ * set power state of the codec
+ */
+static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
+                                unsigned int power_state)
+{
+    hda_nid_t nid, nid_start;
+    int nodes;
+
+    snd_hda_codec_write(codec, fg, 0, AC_VERB_SET_POWER_STATE,
+                        power_state);
+
+    nodes = snd_hda_get_sub_nodes(codec, fg, &nid_start);
+    for (nid = nid_start; nid < nodes + nid_start; nid++) {
+        if (get_wcaps(codec, nid) & AC_WCAP_POWER)
+            snd_hda_codec_write(codec, nid, 0,
+                                AC_VERB_SET_POWER_STATE,
+                                power_state);
+    }
+
+    if (power_state == AC_PWRST_D0)
+        msleep(10);
+}
+
+
+/**
+ * snd_hda_build_controls - build mixer controls
+ * @bus: the BUS
+ *
+ * Creates mixer controls for each codec included in the bus.
+ *
+ * Returns 0 if successful, otherwise a negative error code.
+ */
+int snd_hda_build_controls(struct hda_bus *bus)
+{
+    struct list_head *p;
+
+    /* build controls */
+    list_for_each(p, &bus->codec_list) {
+        struct hda_codec *codec = list_entry(p, struct hda_codec, list);
+        int err;
+        if (! codec->patch_ops.build_controls)
+            continue;
+        err = codec->patch_ops.build_controls(codec);
+        if (err < 0)
+            return err;
+    }
+
+    /* initialize */
+    list_for_each(p, &bus->codec_list) {
+        struct hda_codec *codec = list_entry(p, struct hda_codec, list);
+        int err;
+        hda_set_power_state(codec,
+                            codec->afg ? codec->afg : codec->mfg,
+                            AC_PWRST_D0);
+        if (! codec->patch_ops.init)
+            continue;
+        err = codec->patch_ops.init(codec);
+        if (err < 0)
+            return err;
+    }
+    return 0;
+}
+
+
+/*
+ * stream formats
+ */
+struct hda_rate_tbl {
+    unsigned int hz;
+    unsigned int alsa_bits;
+    unsigned int hda_fmt;
+};
+
+static struct hda_rate_tbl rate_bits[] = {
+    /* rate in Hz, ALSA rate bitmask, HDA format value */
+
+    /* autodetected value used in snd_hda_query_supported_pcm */
+    { 8000, SNDRV_PCM_RATE_8000, 0x0500 }, /* 1/6 x 48 */
+    { 11025, SNDRV_PCM_RATE_11025, 0x4300 }, /* 1/4 x 44 */
+    { 16000, SNDRV_PCM_RATE_16000, 0x0200 }, /* 1/3 x 48 */
+    { 22050, SNDRV_PCM_RATE_22050, 0x4100 }, /* 1/2 x 44 */
+    { 32000, SNDRV_PCM_RATE_32000, 0x0a00 }, /* 2/3 x 48 */
+    { 44100, SNDRV_PCM_RATE_44100, 0x4000 }, /* 44 */
+    { 48000, SNDRV_PCM_RATE_48000, 0x0000 }, /* 48 */
+    { 88200, SNDRV_PCM_RATE_88200, 0x4800 }, /* 2 x 44 */
+    { 96000, SNDRV_PCM_RATE_96000, 0x0800 }, /* 2 x 48 */
+    { 176400, SNDRV_PCM_RATE_176400, 0x5800 },/* 4 x 44 */
+    { 192000, SNDRV_PCM_RATE_192000, 0x1800 }, /* 4 x 48 */
+
+    { 0 } /* terminator */
+};
+
+/**
+ * snd_hda_calc_stream_format - calculate format bitset
+ * @rate: the sample rate
+ * @channels: the number of channels
+ * @format: the PCM format (SNDRV_PCM_FORMAT_XXX)
+ * @maxbps: the max. bps
+ *
+ * Calculate the format bitset from the given rate, channels and th PCM format.
+ *
+ * Return zero if invalid.
+ */
+unsigned int snd_hda_calc_stream_format(unsigned int rate,
+                                        unsigned int channels,
+                                        unsigned int format,
+                                        unsigned int maxbps)
+{
+    int i;
+    unsigned int val = 0;
+
+    for (i = 0; rate_bits[i].hz; i++)
+        if (rate_bits[i].hz == rate) {
+            val = rate_bits[i].hda_fmt;
+            break;
+        }
+    if (! rate_bits[i].hz) {
+        snd_printdd("invalid rate %d\n", rate);
+        return 0;
+    }
+
+    if (channels == 0 || channels > 8) {
+        snd_printdd("invalid channels %d\n", channels);
+        return 0;
+    }
+    val |= channels - 1;
+
+    switch (snd_pcm_format_width(format)) {
+    case 8:  val |= 0x00; break;
+    case 16: val |= 0x10; break;
+    case 20:
+    case 24:
+    case 32:
+        if (maxbps >= 32)
+            val |= 0x40;
+        else if (maxbps >= 24)
+            val |= 0x30;
+        else
+            val |= 0x20;
+        break;
+    default:
+        snd_printdd("invalid format width %d\n", snd_pcm_format_width(format));
+        return 0;
+    }
+
+    return val;
+}
+
+/**
+ * snd_hda_query_supported_pcm - query the supported PCM rates and formats
+ * @codec: the HDA codec
+ * @nid: NID to query
+ * @ratesp: the pointer to store the detected rate bitflags
+ * @formatsp: the pointer to store the detected formats
+ * @bpsp: the pointer to store the detected format widths
+ *
+ * Queries the supported PCM rates and formats.  The NULL @ratesp, @formatsp
+ * or @bsps argument is ignored.
+ *
+ * Returns 0 if successful, otherwise a negative error code.
+ */
+int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
+                                u32 *ratesp, u64 *formatsp, unsigned int *bpsp)
+{
+    int i;
+    unsigned int val, streams;
+
+    val = 0;
+    if (nid != codec->afg &&
+        (get_wcaps(codec, nid) & AC_WCAP_FORMAT_OVRD)) {
+        val = snd_hda_param_read(codec, nid, AC_PAR_PCM);
+        if (val == -1)
+            return -EIO;
+    }
+    if (! val)
+        val = snd_hda_param_read(codec, codec->afg, AC_PAR_PCM);
+
+    if (ratesp) {
+        u32 rates = 0;
+        for (i = 0; rate_bits[i].hz; i++) {
+            if (val & (1 << i))
+                rates |= rate_bits[i].alsa_bits;
+        }
+        *ratesp = rates;
+    }
+
+    if (formatsp || bpsp) {
+        u64 formats = 0;
+        unsigned int bps;
+        unsigned int wcaps;
+
+        wcaps = get_wcaps(codec, nid);
+        streams = snd_hda_param_read(codec, nid, AC_PAR_STREAM);
+        if (streams == -1)
+            return -EIO;
+        if (! streams) {
+            streams = snd_hda_param_read(codec, codec->afg, AC_PAR_STREAM);
+            if (streams == -1)
+                return -EIO;
+        }
+
+        bps = 0;
+        if (streams & AC_SUPFMT_PCM) {
+            if (val & AC_SUPPCM_BITS_8) {
+                formats |= SNDRV_PCM_FMTBIT_U8;
+                bps = 8;
+            }
+            if (val & AC_SUPPCM_BITS_16) {
+                formats |= SNDRV_PCM_FMTBIT_S16_LE;
+                bps = 16;
+            }
+            if (wcaps & AC_WCAP_DIGITAL) {
+                if (val & AC_SUPPCM_BITS_32)
+                    formats |= SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE;
+                if (val & (AC_SUPPCM_BITS_20|AC_SUPPCM_BITS_24))
+                    formats |= SNDRV_PCM_FMTBIT_S32_LE;
+                if (val & AC_SUPPCM_BITS_24)
+                    bps = 24;
+                else if (val & AC_SUPPCM_BITS_20)
+                    bps = 20;
+            } else if (val & (AC_SUPPCM_BITS_20|AC_SUPPCM_BITS_24|AC_SUPPCM_BITS_32)) {
+                formats |= SNDRV_PCM_FMTBIT_S32_LE;
+                if (val & AC_SUPPCM_BITS_32)
+                    bps = 32;
+                else if (val & AC_SUPPCM_BITS_24)
+                    bps = 24;
+                else if (val & AC_SUPPCM_BITS_20)
+                    bps = 20;
+            }
+        }
+        else if (streams == AC_SUPFMT_FLOAT32) { /* should be exclusive */
+            formats |= SNDRV_PCM_FMTBIT_FLOAT_LE;
+            bps = 32;
+        } else if (streams == AC_SUPFMT_AC3) { /* should be exclusive */
+            /* temporary hack: we have still no proper support
+             * for the direct AC3 stream...
+             */
+            formats |= SNDRV_PCM_FMTBIT_U8;
+            bps = 8;
+        }
+        if (formatsp)
+            *formatsp = formats;
+        if (bpsp)
+            *bpsp = bps;
+    }
+
+    return 0;
+}
+
+/**
+ * snd_hda_is_supported_format - check whether the given node supports the format val
+ *
+ * Returns 1 if supported, 0 if not.
+ */
+int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid,
+                                unsigned int format)
+{
+    int i;
+    unsigned int val = 0, rate, stream;
+
+    if (nid != codec->afg &&
+        (get_wcaps(codec, nid) & AC_WCAP_FORMAT_OVRD)) {
+        val = snd_hda_param_read(codec, nid, AC_PAR_PCM);
+        if (val == -1)
+            return 0;
+    }
+    if (! val) {
+        val = snd_hda_param_read(codec, codec->afg, AC_PAR_PCM);
+        if (val == -1)
+            return 0;
+    }
+
+    rate = format & 0xff00;
+    for (i = 0; rate_bits[i].hz; i++)
+        if (rate_bits[i].hda_fmt == rate) {
+            if (val & (1 << i))
+                break;
+            return 0;
+        }
+    if (! rate_bits[i].hz)
+        return 0;
+
+    stream = snd_hda_param_read(codec, nid, AC_PAR_STREAM);
+    if (stream == -1)
+        return 0;
+    if (! stream && nid != codec->afg)
+        stream = snd_hda_param_read(codec, codec->afg, AC_PAR_STREAM);
+    if (! stream || stream == -1)
+        return 0;
+
+    if (stream & AC_SUPFMT_PCM) {
+        switch (format & 0xf0) {
+        case 0x00:
+            if (! (val & AC_SUPPCM_BITS_8))
+                return 0;
+            break;
+        case 0x10:
+            if (! (val & AC_SUPPCM_BITS_16))
+                return 0;
+            break;
+        case 0x20:
+            if (! (val & AC_SUPPCM_BITS_20))
+                return 0;
+            break;
+        case 0x30:
+            if (! (val & AC_SUPPCM_BITS_24))
+                return 0;
+            break;
+        case 0x40:
+            if (! (val & AC_SUPPCM_BITS_32))
+                return 0;
+            break;
+        default:
+            return 0;
+        }
+    } else {
+        /* FIXME: check for float32 and AC3? */
+    }
+
+    return 1;
+}
+
+/*
+ * PCM stuff
+ */
+static int hda_pcm_default_open_close(struct hda_pcm_stream *hinfo,
+                                      struct hda_codec *codec,
+                                      struct snd_pcm_substream *substream)
+{
+    return 0;
+}
+
+static int hda_pcm_default_prepare(struct hda_pcm_stream *hinfo,
+                                   struct hda_codec *codec,
+                                   unsigned int stream_tag,
+                                   unsigned int format,
+                                   struct snd_pcm_substream *substream)
+{
+    snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format);
+    return 0;
+}
+
+static int hda_pcm_default_cleanup(struct hda_pcm_stream *hinfo,
+                                   struct hda_codec *codec,
+                                   struct snd_pcm_substream *substream)
+{
+    snd_hda_codec_setup_stream(codec, hinfo->nid, 0, 0, 0);
+    return 0;
+}
+
+static int set_pcm_default_values(struct hda_codec *codec, struct hda_pcm_stream *info)
+{
+    if (info->nid) {
+        /* query support PCM information from the given NID */
+        if (! info->rates || ! info->formats)
+            snd_hda_query_supported_pcm(codec, info->nid,
+                                        info->rates ? NULL : &info->rates,
+                                        info->formats ? NULL : &info->formats,
+                                        info->maxbps ? NULL : &info->maxbps);
+    }
+    if (info->ops.open == NULL)
+        info->ops.open = hda_pcm_default_open_close;
+    if (info->ops.close == NULL)
+        info->ops.close = hda_pcm_default_open_close;
+    if (info->ops.prepare == NULL) {
+        snd_assert(info->nid, return -EINVAL);
+        info->ops.prepare = hda_pcm_default_prepare;
+    }
+    if (info->ops.cleanup == NULL) {
+        snd_assert(info->nid, return -EINVAL);
+        info->ops.cleanup = hda_pcm_default_cleanup;
+    }
+    return 0;
+}
+
+/**
+ * snd_hda_build_pcms - build PCM information
+ * @bus: the BUS
+ *
+ * Create PCM information for each codec included in the bus.
+ *
+ * The build_pcms codec patch is requested to set up codec->num_pcms and
+ * codec->pcm_info properly.  The array is referred by the top-level driver
+ * to create its PCM instances.
+ * The allocated codec->pcm_info should be released in codec->patch_ops.free
+ * callback.
+ *
+ * At least, substreams, channels_min and channels_max must be filled for
+ * each stream.  substreams = 0 indicates that the stream doesn't exist.
+ * When rates and/or formats are zero, the supported values are queried
+ * from the given nid.  The nid is used also by the default ops.prepare
+ * and ops.cleanup callbacks.
+ *
+ * The driver needs to call ops.open in its open callback.  Similarly,
+ * ops.close is supposed to be called in the close callback.
+ * ops.prepare should be called in the prepare or hw_params callback
+ * with the proper parameters for set up.
+ * ops.cleanup should be called in hw_free for clean up of streams.
+ *
+ * This function returns 0 if successfull, or a negative error code.
+ */
+int snd_hda_build_pcms(struct hda_bus *bus)
+{
+    struct list_head *p;
+
+    list_for_each(p, &bus->codec_list) {
+        struct hda_codec *codec = list_entry(p, struct hda_codec, list);
+        unsigned int pcm, s;
+        int err;
+        if (! codec->patch_ops.build_pcms)
+            continue;
+        err = codec->patch_ops.build_pcms(codec);
+        if (err < 0)
+            return err;
+        for (pcm = 0; pcm < codec->num_pcms; pcm++) {
+            for (s = 0; s < 2; s++) {
+                struct hda_pcm_stream *info;
+                info = &codec->pcm_info[pcm].stream[s];
+                if (! info->substreams)
+                    continue;
+                err = set_pcm_default_values(codec, info);
+                if (err < 0)
+                    return err;
+            }
+        }
+    }
+    return 0;
+}
+
+
+/**
+ * snd_hda_check_board_config - compare the current codec with the config table
+ * @codec: the HDA codec
+ * @num_configs: number of config enums
+ * @models: array of model name strings
+ * @tbl: configuration table, terminated by null entries
+ *
+ * Compares the modelname or PCI subsystem id of the current codec with the
+ * given configuration table.  If a matching entry is found, returns its
+ * config value (supposed to be 0 or positive).
+ *
+ * If no entries are matching, the function returns a negative value.
+ */
+int snd_hda_check_board_config(struct hda_codec *codec,
+                               int num_configs, const char **models,
+                               const struct snd_pci_quirk *tbl)
+{
+    if (codec->bus->modelname && models) {
+        int i;
+        for (i = 0; i < num_configs; i++) {
+            if (models[i] &&
+                !strcmp(codec->bus->modelname, models[i])) {
+                snd_printd(KERN_INFO "hda_codec: model '%s' is "
+                           "selected\n", models[i]);
+                return i;
+            }
+        }
+    }
+
+    if (!codec->bus->pci || !tbl)
+        return -1;
+
+    tbl = snd_pci_quirk_lookup(codec->bus->pci, tbl);
+    if (!tbl)
+        return -1;
+    if (tbl->value >= 0 && tbl->value < num_configs) {
+#ifdef CONFIG_SND_DEBUG_DETECT
+        char tmp[10];
+        const char *model = NULL;
+        if (models)
+            model = models[tbl->value];
+        if (!model) {
+            sprintf(tmp, "#%d", tbl->value);
+            model = tmp;
+        }
+        snd_printdd(KERN_INFO "hda_codec: model '%s' is selected "
+                    "for config %x:%x (%s)\n",
+                    model, tbl->subvendor, tbl->subdevice,
+                    (tbl->name ? tbl->name : "Unknown device"));
+#endif
+        return tbl->value;
+    }
+    return -1;
+}
+
+/**
+ * snd_hda_add_new_ctls - create controls from the array
+ * @codec: the HDA codec
+ * @knew: the array of struct snd_kcontrol_new
+ *
+ * This helper function creates and add new controls in the given array.
+ * The array must be terminated with an empty entry as terminator.
+ *
+ * Returns 0 if successful, or a negative error code.
+ */
+int snd_hda_add_new_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew)
+{
+    int err;
+
+    for (; knew->name; knew++) {
+        struct snd_kcontrol *kctl;
+        kctl = snd_ctl_new1(knew, codec);
+        if (! kctl)
+            return -ENOMEM;
+        err = snd_ctl_add(codec->bus->card, kctl);
+        if (err < 0) {
+            if (! codec->addr)
+                return err;
+            kctl = snd_ctl_new1(knew, codec);
+            if (! kctl)
+                return -ENOMEM;
+            kctl->id.device = codec->addr;
+            if ((err = snd_ctl_add(codec->bus->card, kctl)) < 0)
+                return err;
+        }
+    }
+    return 0;
+}
+
+
+/*
+ * Channel mode helper
+ */
+int snd_hda_ch_mode_info(struct hda_codec *codec, struct snd_ctl_elem_info *uinfo,
+                         const struct hda_channel_mode *chmode, int num_chmodes)
+{
+    uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+    uinfo->count = 1;
+    uinfo->value.enumerated.items = num_chmodes;
+    if (uinfo->value.enumerated.item >= num_chmodes)
+        uinfo->value.enumerated.item = num_chmodes - 1;
+    sprintf(uinfo->value.enumerated.name, "%dch",
+            chmode[uinfo->value.enumerated.item].channels);
+    return 0;
+}
+
+int snd_hda_ch_mode_get(struct hda_codec *codec, struct snd_ctl_elem_value *ucontrol,
+                        const struct hda_channel_mode *chmode, int num_chmodes,
+                        int max_channels)
+{
+    int i;
+
+    for (i = 0; i < num_chmodes; i++) {
+        if (max_channels == chmode[i].channels) {
+            ucontrol->value.enumerated.item[0] = i;
+            break;
+        }
+    }
+    return 0;
+}
+
+int snd_hda_ch_mode_put(struct hda_codec *codec, struct snd_ctl_elem_value *ucontrol,
+                        const struct hda_channel_mode *chmode, int num_chmodes,
+                        int *max_channelsp)
+{
+    unsigned int mode;
+
+    mode = ucontrol->value.enumerated.item[0];
+    snd_assert(mode < num_chmodes, return -EINVAL);
+    if (*max_channelsp == chmode[mode].channels && ! codec->in_resume)
+        return 0;
+    /* change the current channel setting */
+    *max_channelsp = chmode[mode].channels;
+    if (chmode[mode].sequence)
+        snd_hda_sequence_write(codec, chmode[mode].sequence);
+    return 1;
+}
+
+/*
+ * input MUX helper
+ */
+int snd_hda_input_mux_info(const struct hda_input_mux *imux, struct snd_ctl_elem_info *uinfo)
+{
+    unsigned int index;
+
+    uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+    uinfo->count = 1;
+    uinfo->value.enumerated.items = imux->num_items;
+    index = uinfo->value.enumerated.item;
+    if (index >= imux->num_items)
+        index = imux->num_items - 1;
+    strcpy(uinfo->value.enumerated.name, imux->items[index].label);
+    return 0;
+}
+
+int snd_hda_input_mux_put(struct hda_codec *codec, const struct hda_input_mux *imux,
+                          struct snd_ctl_elem_value *ucontrol, hda_nid_t nid,
+                          unsigned int *cur_val)
+{
+    unsigned int idx;
+
+    idx = ucontrol->value.enumerated.item[0];
+    if (idx >= imux->num_items)
+        idx = imux->num_items - 1;
+    if (*cur_val == idx && ! codec->in_resume)
+        return 0;
+    snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL,
+                        imux->items[idx].index);
+    *cur_val = idx;
+    return 1;
+}
+
+
+/*
+ * Multi-channel / digital-out PCM helper functions
+ */
+
+/*
+ * open the digital out in the exclusive mode
+ */
+int snd_hda_multi_out_dig_open(struct hda_codec *codec, struct hda_multi_out *mout)
+{
+    down(&codec->spdif_mutex);
+    if (mout->dig_out_used) {
+        up(&codec->spdif_mutex);
+        return -EBUSY; /* already being used */
+    }
+    mout->dig_out_used = HDA_DIG_EXCLUSIVE;
+    up(&codec->spdif_mutex);
+    return 0;
+}
+
+/*
+ * release the digital out
+ */
+int snd_hda_multi_out_dig_close(struct hda_codec *codec, struct hda_multi_out *mout)
+{
+    down(&codec->spdif_mutex);
+    mout->dig_out_used = 0;
+    up(&codec->spdif_mutex);
+    return 0;
+}
+
+/*
+ * set up more restrictions for analog out
+ */
+int snd_hda_multi_out_analog_open(struct hda_codec *codec, struct hda_multi_out *mout,
+                                  struct snd_pcm_substream *substream)
+{
+    substream->runtime->hw.channels_max = mout->max_channels;
+    return snd_pcm_hw_constraint_step(substream->runtime, 0,
+                                      SNDRV_PCM_HW_PARAM_CHANNELS, 2);
+}
+
+/*
+ * set up the i/o for analog out
+ * when the digital out is available, copy the front out to digital out, too.
+ */
+int snd_hda_multi_out_analog_prepare(struct hda_codec *codec, struct hda_multi_out *mout,
+                                     unsigned int stream_tag,
+                                     unsigned int format,
+                                     struct snd_pcm_substream *substream)
+{
+    hda_nid_t *nids = mout->dac_nids;
+    int chs = substream->runtime->channels;
+    int i;
+
+    down(&codec->spdif_mutex);
+    if (mout->dig_out_nid && mout->dig_out_used != HDA_DIG_EXCLUSIVE) {
+        if (chs == 2 &&
+            snd_hda_is_supported_format(codec, mout->dig_out_nid, format) &&
+            ! (codec->spdif_status & IEC958_AES0_NONAUDIO)) {
+            mout->dig_out_used = HDA_DIG_ANALOG_DUP;
+            /* setup digital receiver */
+            snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
+                                       stream_tag, 0, format);
+        } else {
+            mout->dig_out_used = 0;
+            snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 0, 0, 0);
+        }
+    }
+    up(&codec->spdif_mutex);
+
+    /* front */
+    snd_hda_codec_setup_stream(codec, nids[HDA_FRONT], stream_tag, 0, format);
+    if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT])
+        /* headphone out will just decode front left/right (stereo) */
+        snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag, 0, format);
+    /* extra outputs copied from front */
+    for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++)
+        if (mout->extra_out_nid[i])
+            snd_hda_codec_setup_stream(codec,
+                                       mout->extra_out_nid[i],
+                                       stream_tag, 0, format);
+
+    /* surrounds */
+    for (i = 1; i < mout->num_dacs; i++) {
+        if (chs >= (i + 1) * 2) /* independent out */
+            snd_hda_codec_setup_stream(codec, nids[i], stream_tag, i * 2,
+                                       format);
+        else /* copy front */
+            snd_hda_codec_setup_stream(codec, nids[i], stream_tag, 0,
+                                       format);
+    }
+    return 0;
+}
+
+/*
+ * clean up the setting for analog out
+ */
+int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec, struct hda_multi_out *mout)
+{
+    hda_nid_t *nids = mout->dac_nids;
+    int i;
+
+    for (i = 0; i < mout->num_dacs; i++)
+        snd_hda_codec_setup_stream(codec, nids[i], 0, 0, 0);
+    if (mout->hp_nid)
+        snd_hda_codec_setup_stream(codec, mout->hp_nid, 0, 0, 0);
+    for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++)
+        if (mout->extra_out_nid[i])
+            snd_hda_codec_setup_stream(codec,
+                                       mout->extra_out_nid[i],
+                                       0, 0, 0);
+    down(&codec->spdif_mutex);
+    if (mout->dig_out_nid && mout->dig_out_used == HDA_DIG_ANALOG_DUP) {
+        snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 0, 0, 0);
+        mout->dig_out_used = 0;
+    }
+    up(&codec->spdif_mutex);
+    return 0;
+}
+
+/*
+ * Helper for automatic ping configuration
+ */
+
+static int is_in_nid_list(hda_nid_t nid, hda_nid_t *list)
+{
+    for (; *list; list++)
+        if (*list == nid)
+            return 1;
+    return 0;
+}
+
+/*
+ * Parse all pin widgets and store the useful pin nids to cfg
+ *
+ * The number of line-outs or any primary output is stored in line_outs,
+ * and the corresponding output pins are assigned to line_out_pins[],
+ * in the order of front, rear, CLFE, side, ...
+ *
+ * If more extra outputs (speaker and headphone) are found, the pins are
+ * assisnged to hp_pins[] and speaker_pins[], respectively.  If no line-out jack
+ * is detected, one of speaker of HP pins is assigned as the primary
+ * output, i.e. to line_out_pins[0].  So, line_outs is always positive
+ * if any analog output exists.
+ *
+ * The analog input pins are assigned to input_pins array.
+ * The digital input/output pins are assigned to dig_in_pin and dig_out_pin,
+ * respectively.
+ */
+int snd_hda_parse_pin_def_config(struct hda_codec *codec, struct auto_pin_cfg *cfg,
+                                 hda_nid_t *ignore_nids)
+{
+    hda_nid_t nid, nid_start;
+    int i, j, nodes;
+    short seq, assoc_line_out, sequences[ARRAY_SIZE(cfg->line_out_pins)];
+
+    memset(cfg, 0, sizeof(*cfg));
+
+    memset(sequences, 0, sizeof(sequences));
+    assoc_line_out = 0;
+
+    nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid_start);
+    for (nid = nid_start; nid < nodes + nid_start; nid++) {
+        unsigned int wid_caps = get_wcaps(codec, nid);
+        unsigned int wid_type = (wid_caps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
+        unsigned int def_conf;
+        short assoc, loc;
+
+        /* read all default configuration for pin complex */
+        if (wid_type != AC_WID_PIN)
+            continue;
+        /* ignore the given nids (e.g. pc-beep returns error) */
+        if (ignore_nids && is_in_nid_list(nid, ignore_nids))
+            continue;
+
+        def_conf = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONFIG_DEFAULT, 0);
+        if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE)
+            continue;
+        loc = get_defcfg_location(def_conf);
+        switch (get_defcfg_device(def_conf)) {
+        case AC_JACK_LINE_OUT:
+            seq = get_defcfg_sequence(def_conf);
+            assoc = get_defcfg_association(def_conf);
+            if (! assoc)
+                continue;
+            if (! assoc_line_out)
+                assoc_line_out = assoc;
+            else if (assoc_line_out != assoc)
+                continue;
+            if (cfg->line_outs >= ARRAY_SIZE(cfg->line_out_pins))
+                continue;
+            cfg->line_out_pins[cfg->line_outs] = nid;
+            sequences[cfg->line_outs] = seq;
+            cfg->line_outs++;
+            break;
+        case AC_JACK_SPEAKER:
+            if (cfg->speaker_outs >= ARRAY_SIZE(cfg->speaker_pins))
+                continue;
+            cfg->speaker_pins[cfg->speaker_outs] = nid;
+            cfg->speaker_outs++;
+            break;
+        case AC_JACK_HP_OUT:
+            if (cfg->hp_outs >= ARRAY_SIZE(cfg->hp_pins))
+                continue;
+            cfg->hp_pins[cfg->hp_outs] = nid;
+            cfg->hp_outs++;
+            break;
+        case AC_JACK_MIC_IN: {
+            int preferred, alt;
+            if (loc == AC_JACK_LOC_FRONT) {
+                preferred = AUTO_PIN_FRONT_MIC;
+                alt = AUTO_PIN_MIC;
+            } else {
+                preferred = AUTO_PIN_MIC;
+                alt = AUTO_PIN_FRONT_MIC;
+            }
+            if (!cfg->input_pins[preferred])
+                cfg->input_pins[preferred] = nid;
+            else if (!cfg->input_pins[alt])
+                cfg->input_pins[alt] = nid;
+            break;
+        }
+        case AC_JACK_LINE_IN:
+            if (loc == AC_JACK_LOC_FRONT)
+                cfg->input_pins[AUTO_PIN_FRONT_LINE] = nid;
+            else
+                cfg->input_pins[AUTO_PIN_LINE] = nid;
+            break;
+        case AC_JACK_CD:
+            cfg->input_pins[AUTO_PIN_CD] = nid;
+            break;
+        case AC_JACK_AUX:
+            cfg->input_pins[AUTO_PIN_AUX] = nid;
+            break;
+        case AC_JACK_SPDIF_OUT:
+            cfg->dig_out_pin = nid;
+            break;
+        case AC_JACK_SPDIF_IN:
+            cfg->dig_in_pin = nid;
+            break;
+        }
+    }
+
+    /* sort by sequence */
+    for (i = 0; i < cfg->line_outs; i++)
+        for (j = i + 1; j < cfg->line_outs; j++)
+            if (sequences[i] > sequences[j]) {
+                seq = sequences[i];
+                sequences[i] = sequences[j];
+                sequences[j] = seq;
+                nid = cfg->line_out_pins[i];
+                cfg->line_out_pins[i] = cfg->line_out_pins[j];
+                cfg->line_out_pins[j] = nid;
+            }
+
+    /* Reorder the surround channels
+     * ALSA sequence is front/surr/clfe/side
+     * HDA sequence is:
+     *    4-ch: front/surr  =>  OK as it is
+     *    6-ch: front/clfe/surr
+     *    8-ch: front/clfe/side/surr
+     */
+    switch (cfg->line_outs) {
+    case 3:
+        nid = cfg->line_out_pins[1];
+        cfg->line_out_pins[1] = cfg->line_out_pins[2];
+        cfg->line_out_pins[2] = nid;
+        break;
+    case 4:
+        nid = cfg->line_out_pins[1];
+        cfg->line_out_pins[1] = cfg->line_out_pins[3];
+        cfg->line_out_pins[3] = cfg->line_out_pins[2];
+        cfg->line_out_pins[2] = nid;
+        break;
+    }
+    /*
+     * debug prints of the parsed results
+     */
+    snd_printd("autoconfig: line_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n",
+               cfg->line_outs, cfg->line_out_pins[0], cfg->line_out_pins[1],
+               cfg->line_out_pins[2], cfg->line_out_pins[3],
+               cfg->line_out_pins[4]);
+    snd_printd("   speaker_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n",
+               cfg->speaker_outs, cfg->speaker_pins[0],
+               cfg->speaker_pins[1], cfg->speaker_pins[2],
+               cfg->speaker_pins[3], cfg->speaker_pins[4]);
+    snd_printd("   hp_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n",
+               cfg->hp_outs, cfg->hp_pins[0],
+               cfg->hp_pins[1], cfg->hp_pins[2],
+               cfg->hp_pins[3], cfg->hp_pins[4]);
+    snd_printd("   inputs: mic=0x%x, fmic=0x%x, line=0x%x, fline=0x%x,"
+               " cd=0x%x, aux=0x%x\n",
+               cfg->input_pins[AUTO_PIN_MIC],
+               cfg->input_pins[AUTO_PIN_FRONT_MIC],
+               cfg->input_pins[AUTO_PIN_LINE],
+               cfg->input_pins[AUTO_PIN_FRONT_LINE],
+               cfg->input_pins[AUTO_PIN_CD],
+               cfg->input_pins[AUTO_PIN_AUX]);
+
+    /*
+     * FIX-UP: if no line-outs are detected, try to use speaker or HP pin
+     * as a primary output
+     */
+    if (! cfg->line_outs) {
+        if (cfg->speaker_outs) {
+            cfg->line_outs = cfg->speaker_outs;
+            memcpy(cfg->line_out_pins, cfg->speaker_pins,
+                   sizeof(cfg->speaker_pins));
+            cfg->speaker_outs = 0;
+            memset(cfg->speaker_pins, 0, sizeof(cfg->speaker_pins));
+        } else if (cfg->hp_outs) {
+            cfg->line_outs = cfg->hp_outs;
+            memcpy(cfg->line_out_pins, cfg->hp_pins,
+                   sizeof(cfg->hp_pins));
+            cfg->hp_outs = 0;
+            memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins));
+        }
+    }
+
+    return 0;
+}
+
+/* labels for input pins */
+const char *auto_pin_cfg_labels[AUTO_PIN_LAST] = {
+    "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux"
+};
+
+
+#ifdef CONFIG_PM
+/*
+ * power management
+ */
+
+/**
+ * snd_hda_suspend - suspend the codecs
+ * @bus: the HDA bus
+ * @state: suspsend state
+ *
+ * Returns 0 if successful.
+ */
+int snd_hda_suspend(struct hda_bus *bus, pm_message_t state)
+{
+    struct list_head *p;
+
+    /* FIXME: should handle power widget capabilities */
+    list_for_each(p, &bus->codec_list) {
+        struct hda_codec *codec = list_entry(p, struct hda_codec, list);
+        if (codec->patch_ops.suspend)
+            codec->patch_ops.suspend(codec, state);
+        hda_set_power_state(codec,
+                            codec->afg ? codec->afg : codec->mfg,
+                            AC_PWRST_D3);
+    }
+    return 0;
+}
+
+/**
+ * snd_hda_resume - resume the codecs
+ * @bus: the HDA bus
+ * @state: resume state
+ *
+ * Returns 0 if successful.
+ */
+int snd_hda_resume(struct hda_bus *bus)
+{
+    struct list_head *p;
+
+    list_for_each(p, &bus->codec_list) {
+        struct hda_codec *codec = list_entry(p, struct hda_codec, list);
+        hda_set_power_state(codec,
+                            codec->afg ? codec->afg : codec->mfg,
+                            AC_PWRST_D0);
+        if (codec->patch_ops.resume)
+            codec->patch_ops.resume(codec);
+    }
+    return 0;
+}
+
+/**
+ * snd_hda_resume_ctls - resume controls in the new control list
+ * @codec: the HDA codec
+ * @knew: the array of struct snd_kcontrol_new
+ *
+ * This function resumes the mixer controls in the struct snd_kcontrol_new array,
+ * originally for snd_hda_add_new_ctls().
+ * The array must be terminated with an empty entry as terminator.
+ */
+int snd_hda_resume_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew)
+{
+    struct snd_ctl_elem_value *val;
+
+    val = kmalloc(sizeof(*val), GFP_KERNEL);
+    if (! val)
+        return -ENOMEM;
+    codec->in_resume = 1;
+    for (; knew->name; knew++) {
+        int i, count;
+        count = knew->count ? knew->count : 1;
+        for (i = 0; i < count; i++) {
+            memset(val, 0, sizeof(*val));
+            val->id.iface = knew->iface;
+            val->id.device = knew->device;
+            val->id.subdevice = knew->subdevice;
+            strcpy(val->id.name, knew->name);
+            val->id.index = knew->index ? knew->index : i;
+            /* Assume that get callback reads only from cache,
+             * not accessing to the real hardware
+             */
+            if (snd_ctl_elem_read(codec->bus->card, val) < 0)
+                continue;
+            snd_ctl_elem_write(codec->bus->card, NULL, val);
+        }
+    }
+    codec->in_resume = 0;
+    kfree(val);
+    return 0;
+}
+
+/**
+ * snd_hda_resume_spdif_out - resume the digital out
+ * @codec: the HDA codec
+ */
+int snd_hda_resume_spdif_out(struct hda_codec *codec)
+{
+    return snd_hda_resume_ctls(codec, dig_mixes);
+}
+
+/**
+ * snd_hda_resume_spdif_in - resume the digital in
+ * @codec: the HDA codec
+ */
+int snd_hda_resume_spdif_in(struct hda_codec *codec)
+{
+    return snd_hda_resume_ctls(codec, dig_in_ctls);
+}
+#endif
+
+/*
+ * symbols exported for controller modules
+ */
+EXPORT_SYMBOL(snd_hda_codec_read);
+EXPORT_SYMBOL(snd_hda_codec_write);
+EXPORT_SYMBOL(snd_hda_sequence_write);
+EXPORT_SYMBOL(snd_hda_get_sub_nodes);
+EXPORT_SYMBOL(snd_hda_queue_unsol_event);
+EXPORT_SYMBOL(snd_hda_bus_new);
+EXPORT_SYMBOL(snd_hda_codec_new);
+EXPORT_SYMBOL(snd_hda_codec_setup_stream);
+EXPORT_SYMBOL(snd_hda_calc_stream_format);
+EXPORT_SYMBOL(snd_hda_build_pcms);
+EXPORT_SYMBOL(snd_hda_build_controls);
+#ifdef CONFIG_PM
+EXPORT_SYMBOL(snd_hda_suspend);
+EXPORT_SYMBOL(snd_hda_resume);
+#endif
+
+/*
+ *  INIT part
+ */
+
+static int __init alsa_hda_init(void)
+{
+    return 0;
+}
+
+static void __exit alsa_hda_exit(void)
+{
+}
+
+module_init(alsa_hda_init)
+module_exit(alsa_hda_exit)
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/hda/hda_codec.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/hda/hda_codec.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/hda/hda_codec.h	(revision 91)
@@ -0,0 +1,635 @@
+/*
+ * Universal Interface for Intel High Definition Audio Codec
+ *
+ * Copyright (c) 2004 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.
+ */
+
+#ifndef __SOUND_HDA_CODEC_H
+#define __SOUND_HDA_CODEC_H
+
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+
+/*
+ * nodes
+ */
+#define	AC_NODE_ROOT		0x00
+
+/*
+ * function group types
+ */
+enum {
+	AC_GRP_AUDIO_FUNCTION = 0x01,
+	AC_GRP_MODEM_FUNCTION = 0x02,
+};
+	
+/*
+ * widget types
+ */
+enum {
+	AC_WID_AUD_OUT,		/* Audio Out */
+	AC_WID_AUD_IN,		/* Audio In */
+	AC_WID_AUD_MIX,		/* Audio Mixer */
+	AC_WID_AUD_SEL,		/* Audio Selector */
+	AC_WID_PIN,		/* Pin Complex */
+	AC_WID_POWER,		/* Power */
+	AC_WID_VOL_KNB,		/* Volume Knob */
+	AC_WID_BEEP,		/* Beep Generator */
+	AC_WID_VENDOR = 0x0f	/* Vendor specific */
+};
+
+/*
+ * GET verbs
+ */
+#define AC_VERB_GET_STREAM_FORMAT		0x0a00
+#define AC_VERB_GET_AMP_GAIN_MUTE		0x0b00
+#define AC_VERB_GET_PROC_COEF			0x0c00
+#define AC_VERB_GET_COEF_INDEX			0x0d00
+#define AC_VERB_PARAMETERS			0x0f00
+#define AC_VERB_GET_CONNECT_SEL			0x0f01
+#define AC_VERB_GET_CONNECT_LIST		0x0f02
+#define AC_VERB_GET_PROC_STATE			0x0f03
+#define AC_VERB_GET_SDI_SELECT			0x0f04
+#define AC_VERB_GET_POWER_STATE			0x0f05
+#define AC_VERB_GET_CONV			0x0f06
+#define AC_VERB_GET_PIN_WIDGET_CONTROL		0x0f07
+#define AC_VERB_GET_UNSOLICITED_RESPONSE	0x0f08
+#define AC_VERB_GET_PIN_SENSE			0x0f09
+#define AC_VERB_GET_BEEP_CONTROL		0x0f0a
+#define AC_VERB_GET_EAPD_BTLENABLE		0x0f0c
+#define AC_VERB_GET_DIGI_CONVERT		0x0f0d
+#define AC_VERB_GET_VOLUME_KNOB_CONTROL		0x0f0f
+/* f10-f1a: GPIO */
+#define AC_VERB_GET_GPIO_DATA			0x0f15
+#define AC_VERB_GET_GPIO_MASK			0x0f16
+#define AC_VERB_GET_GPIO_DIRECTION		0x0f17
+#define AC_VERB_GET_CONFIG_DEFAULT		0x0f1c
+/* f20: AFG/MFG */
+#define AC_VERB_GET_SUBSYSTEM_ID		0x0f20
+
+/*
+ * SET verbs
+ */
+#define AC_VERB_SET_STREAM_FORMAT		0x200
+#define AC_VERB_SET_AMP_GAIN_MUTE		0x300
+#define AC_VERB_SET_PROC_COEF			0x400
+#define AC_VERB_SET_COEF_INDEX			0x500
+#define AC_VERB_SET_CONNECT_SEL			0x701
+#define AC_VERB_SET_PROC_STATE			0x703
+#define AC_VERB_SET_SDI_SELECT			0x704
+#define AC_VERB_SET_POWER_STATE			0x705
+#define AC_VERB_SET_CHANNEL_STREAMID		0x706
+#define AC_VERB_SET_PIN_WIDGET_CONTROL		0x707
+#define AC_VERB_SET_UNSOLICITED_ENABLE		0x708
+#define AC_VERB_SET_PIN_SENSE			0x709
+#define AC_VERB_SET_BEEP_CONTROL		0x70a
+#define AC_VERB_SET_EAPD_BTLENABLE		0x70c
+#define AC_VERB_SET_DIGI_CONVERT_1		0x70d
+#define AC_VERB_SET_DIGI_CONVERT_2		0x70e
+#define AC_VERB_SET_VOLUME_KNOB_CONTROL		0x70f
+#define AC_VERB_SET_GPIO_DATA			0x715
+#define AC_VERB_SET_GPIO_MASK			0x716
+#define AC_VERB_SET_GPIO_DIRECTION		0x717
+#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_0	0x71c
+#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_1	0x71d
+#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_2	0x71e
+#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_3	0x71f
+#define AC_VERB_SET_CODEC_RESET			0x7ff
+
+/*
+ * Parameter IDs
+ */
+#define AC_PAR_VENDOR_ID		0x00
+#define AC_PAR_SUBSYSTEM_ID		0x01
+#define AC_PAR_REV_ID			0x02
+#define AC_PAR_NODE_COUNT		0x04
+#define AC_PAR_FUNCTION_TYPE		0x05
+#define AC_PAR_AUDIO_FG_CAP		0x08
+#define AC_PAR_AUDIO_WIDGET_CAP		0x09
+#define AC_PAR_PCM			0x0a
+#define AC_PAR_STREAM			0x0b
+#define AC_PAR_PIN_CAP			0x0c
+#define AC_PAR_AMP_IN_CAP		0x0d
+#define AC_PAR_CONNLIST_LEN		0x0e
+#define AC_PAR_POWER_STATE		0x0f
+#define AC_PAR_PROC_CAP			0x10
+#define AC_PAR_GPIO_CAP			0x11
+#define AC_PAR_AMP_OUT_CAP		0x12
+
+/*
+ * AC_VERB_PARAMETERS results (32bit)
+ */
+
+/* Function Group Type */
+#define AC_FGT_TYPE			(0xff<<0)
+#define AC_FGT_TYPE_SHIFT		0
+#define AC_FGT_UNSOL_CAP		(1<<8)
+
+/* Audio Function Group Capabilities */
+#define AC_AFG_OUT_DELAY		(0xf<<0)
+#define AC_AFG_IN_DELAY			(0xf<<8)
+#define AC_AFG_BEEP_GEN			(1<<16)
+
+/* Audio Widget Capabilities */
+#define AC_WCAP_STEREO			(1<<0)	/* stereo I/O */
+#define AC_WCAP_IN_AMP			(1<<1)	/* AMP-in present */
+#define AC_WCAP_OUT_AMP			(1<<2)	/* AMP-out present */
+#define AC_WCAP_AMP_OVRD		(1<<3)	/* AMP-parameter override */
+#define AC_WCAP_FORMAT_OVRD		(1<<4)	/* format override */
+#define AC_WCAP_STRIPE			(1<<5)	/* stripe */
+#define AC_WCAP_PROC_WID		(1<<6)	/* Proc Widget */
+#define AC_WCAP_UNSOL_CAP		(1<<7)	/* Unsol capable */
+#define AC_WCAP_CONN_LIST		(1<<8)	/* connection list */
+#define AC_WCAP_DIGITAL			(1<<9)	/* digital I/O */
+#define AC_WCAP_POWER			(1<<10)	/* power control */
+#define AC_WCAP_LR_SWAP			(1<<11)	/* L/R swap */
+#define AC_WCAP_DELAY			(0xf<<16)
+#define AC_WCAP_DELAY_SHIFT		16
+#define AC_WCAP_TYPE			(0xf<<20)
+#define AC_WCAP_TYPE_SHIFT		20
+
+/* supported PCM rates and bits */
+#define AC_SUPPCM_RATES			(0xfff << 0)
+#define AC_SUPPCM_BITS_8		(1<<16)
+#define AC_SUPPCM_BITS_16		(1<<17)
+#define AC_SUPPCM_BITS_20		(1<<18)
+#define AC_SUPPCM_BITS_24		(1<<19)
+#define AC_SUPPCM_BITS_32		(1<<20)
+
+/* supported PCM stream format */
+#define AC_SUPFMT_PCM			(1<<0)
+#define AC_SUPFMT_FLOAT32		(1<<1)
+#define AC_SUPFMT_AC3			(1<<2)
+
+/* Pin widget capabilies */
+#define AC_PINCAP_IMP_SENSE		(1<<0)	/* impedance sense capable */
+#define AC_PINCAP_TRIG_REQ		(1<<1)	/* trigger required */
+#define AC_PINCAP_PRES_DETECT		(1<<2)	/* presence detect capable */
+#define AC_PINCAP_HP_DRV		(1<<3)	/* headphone drive capable */
+#define AC_PINCAP_OUT			(1<<4)	/* output capable */
+#define AC_PINCAP_IN			(1<<5)	/* input capable */
+#define AC_PINCAP_BALANCE		(1<<6)	/* balanced I/O capable */
+#define AC_PINCAP_VREF			(0x37<<8)
+#define AC_PINCAP_VREF_SHIFT		8
+#define AC_PINCAP_EAPD			(1<<16)	/* EAPD capable */
+/* Vref status (used in pin cap) */
+#define AC_PINCAP_VREF_HIZ		(1<<0)	/* Hi-Z */
+#define AC_PINCAP_VREF_50		(1<<1)	/* 50% */
+#define AC_PINCAP_VREF_GRD		(1<<2)	/* ground */
+#define AC_PINCAP_VREF_80		(1<<4)	/* 80% */
+#define AC_PINCAP_VREF_100		(1<<5)	/* 100% */
+
+/* Amplifier capabilities */
+#define AC_AMPCAP_OFFSET		(0x7f<<0)  /* 0dB offset */
+#define AC_AMPCAP_OFFSET_SHIFT		0
+#define AC_AMPCAP_NUM_STEPS		(0x7f<<8)  /* number of steps */
+#define AC_AMPCAP_NUM_STEPS_SHIFT	8
+#define AC_AMPCAP_STEP_SIZE		(0x7f<<16) /* step size 0-32dB in 0.25dB */
+#define AC_AMPCAP_STEP_SIZE_SHIFT	16
+#define AC_AMPCAP_MUTE			(1<<31)    /* mute capable */
+#define AC_AMPCAP_MUTE_SHIFT		31
+
+/* Connection list */
+#define AC_CLIST_LENGTH			(0x7f<<0)
+#define AC_CLIST_LONG			(1<<7)
+
+/* Supported power status */
+#define AC_PWRST_D0SUP			(1<<0)
+#define AC_PWRST_D1SUP			(1<<1)
+#define AC_PWRST_D2SUP			(1<<2)
+#define AC_PWRST_D3SUP			(1<<3)
+
+/* Power state values */
+#define AC_PWRST_D0			0x00
+#define AC_PWRST_D1			0x01
+#define AC_PWRST_D2			0x02
+#define AC_PWRST_D3			0x03
+
+/* Processing capabilies */
+#define AC_PCAP_BENIGN			(1<<0)
+#define AC_PCAP_NUM_COEF		(0xff<<8)
+
+/* Volume knobs capabilities */
+#define AC_KNBCAP_NUM_STEPS		(0x7f<<0)
+#define AC_KNBCAP_DELTA			(1<<8)
+
+/*
+ * Control Parameters
+ */
+
+/* Amp gain/mute */
+#define AC_AMP_MUTE			(1<<8)
+#define AC_AMP_GAIN			(0x7f)
+#define AC_AMP_GET_INDEX		(0xf<<0)
+
+#define AC_AMP_GET_LEFT			(1<<13)
+#define AC_AMP_GET_RIGHT		(0<<13)
+#define AC_AMP_GET_OUTPUT		(1<<15)
+#define AC_AMP_GET_INPUT		(0<<15)
+
+#define AC_AMP_SET_INDEX		(0xf<<8)
+#define AC_AMP_SET_INDEX_SHIFT		8
+#define AC_AMP_SET_RIGHT		(1<<12)
+#define AC_AMP_SET_LEFT			(1<<13)
+#define AC_AMP_SET_INPUT		(1<<14)
+#define AC_AMP_SET_OUTPUT		(1<<15)
+
+/* DIGITAL1 bits */
+#define AC_DIG1_ENABLE			(1<<0)
+#define AC_DIG1_V			(1<<1)
+#define AC_DIG1_VCFG			(1<<2)
+#define AC_DIG1_EMPHASIS		(1<<3)
+#define AC_DIG1_COPYRIGHT		(1<<4)
+#define AC_DIG1_NONAUDIO		(1<<5)
+#define AC_DIG1_PROFESSIONAL		(1<<6)
+#define AC_DIG1_LEVEL			(1<<7)
+
+/* Pin widget control - 8bit */
+#define AC_PINCTL_VREFEN		(0x7<<0)
+#define AC_PINCTL_VREF_HIZ		0	/* Hi-Z */
+#define AC_PINCTL_VREF_50		1	/* 50% */
+#define AC_PINCTL_VREF_GRD		2	/* ground */
+#define AC_PINCTL_VREF_80		4	/* 80% */
+#define AC_PINCTL_VREF_100		5	/* 100% */
+#define AC_PINCTL_IN_EN			(1<<5)
+#define AC_PINCTL_OUT_EN		(1<<6)
+#define AC_PINCTL_HP_EN			(1<<7)
+
+/* Unsolicited response - 8bit */
+#define AC_USRSP_EN			(1<<7)
+
+/* configuration default - 32bit */
+#define AC_DEFCFG_SEQUENCE		(0xf<<0)
+#define AC_DEFCFG_DEF_ASSOC		(0xf<<4)
+#define AC_DEFCFG_ASSOC_SHIFT		4
+#define AC_DEFCFG_MISC			(0xf<<8)
+#define AC_DEFCFG_MISC_SHIFT		8
+#define AC_DEFCFG_COLOR			(0xf<<12)
+#define AC_DEFCFG_COLOR_SHIFT		12
+#define AC_DEFCFG_CONN_TYPE		(0xf<<16)
+#define AC_DEFCFG_CONN_TYPE_SHIFT	16
+#define AC_DEFCFG_DEVICE		(0xf<<20)
+#define AC_DEFCFG_DEVICE_SHIFT		20
+#define AC_DEFCFG_LOCATION		(0x3f<<24)
+#define AC_DEFCFG_LOCATION_SHIFT	24
+#define AC_DEFCFG_PORT_CONN		(0x3<<30)
+#define AC_DEFCFG_PORT_CONN_SHIFT	30
+
+/* device device types (0x0-0xf) */
+enum {
+	AC_JACK_LINE_OUT,
+	AC_JACK_SPEAKER,
+	AC_JACK_HP_OUT,
+	AC_JACK_CD,
+	AC_JACK_SPDIF_OUT,
+	AC_JACK_DIG_OTHER_OUT,
+	AC_JACK_MODEM_LINE_SIDE,
+	AC_JACK_MODEM_HAND_SIDE,
+	AC_JACK_LINE_IN,
+	AC_JACK_AUX,
+	AC_JACK_MIC_IN,
+	AC_JACK_TELEPHONY,
+	AC_JACK_SPDIF_IN,
+	AC_JACK_DIG_OTHER_IN,
+	AC_JACK_OTHER = 0xf,
+};
+
+/* jack connection types (0x0-0xf) */
+enum {
+	AC_JACK_CONN_UNKNOWN,
+	AC_JACK_CONN_1_8,
+	AC_JACK_CONN_1_4,
+	AC_JACK_CONN_ATAPI,
+	AC_JACK_CONN_RCA,
+	AC_JACK_CONN_OPTICAL,
+	AC_JACK_CONN_OTHER_DIGITAL,
+	AC_JACK_CONN_OTHER_ANALOG,
+	AC_JACK_CONN_DIN,
+	AC_JACK_CONN_XLR,
+	AC_JACK_CONN_RJ11,
+	AC_JACK_CONN_COMB,
+	AC_JACK_CONN_OTHER = 0xf,
+};
+
+/* jack colors (0x0-0xf) */
+enum {
+	AC_JACK_COLOR_UNKNOWN,
+	AC_JACK_COLOR_BLACK,
+	AC_JACK_COLOR_GREY,
+	AC_JACK_COLOR_BLUE,
+	AC_JACK_COLOR_GREEN,
+	AC_JACK_COLOR_RED,
+	AC_JACK_COLOR_ORANGE,
+	AC_JACK_COLOR_YELLOW,
+	AC_JACK_COLOR_PURPLE,
+	AC_JACK_COLOR_PINK,
+	AC_JACK_COLOR_WHITE = 0xe,
+	AC_JACK_COLOR_OTHER,
+};
+
+/* Jack location (0x0-0x3f) */
+/* common case */
+enum {
+	AC_JACK_LOC_NONE,
+	AC_JACK_LOC_REAR,
+	AC_JACK_LOC_FRONT,
+	AC_JACK_LOC_LEFT,
+	AC_JACK_LOC_RIGHT,
+	AC_JACK_LOC_TOP,
+	AC_JACK_LOC_BOTTOM,
+};
+/* bits 4-5 */
+enum {
+	AC_JACK_LOC_EXTERNAL = 0x00,
+	AC_JACK_LOC_INTERNAL = 0x10,
+	AC_JACK_LOC_SEPARATE = 0x20,
+	AC_JACK_LOC_OTHER    = 0x30,
+};
+enum {
+	/* external on primary chasis */
+	AC_JACK_LOC_REAR_PANEL = 0x07,
+	AC_JACK_LOC_DRIVE_BAY,
+	/* internal */
+	AC_JACK_LOC_RISER = 0x17,
+	AC_JACK_LOC_HDMI,
+	AC_JACK_LOC_ATAPI,
+	/* others */
+	AC_JACK_LOC_MOBILE_IN = 0x37,
+	AC_JACK_LOC_MOBILE_OUT,
+};
+
+/* Port connectivity (0-3) */
+enum {
+	AC_JACK_PORT_COMPLEX,
+	AC_JACK_PORT_NONE,
+	AC_JACK_PORT_FIXED,
+	AC_JACK_PORT_BOTH,
+};
+
+/* max. connections to a widget */
+#define HDA_MAX_CONNECTIONS	32
+
+/* max. codec address */
+#define HDA_MAX_CODEC_ADDRESS	0x0f
+
+/*
+ * Structures
+ */
+
+struct hda_bus;
+struct hda_codec;
+struct hda_pcm;
+struct hda_pcm_stream;
+struct hda_bus_unsolicited;
+
+/* NID type */
+typedef u16 hda_nid_t;
+
+/* bus operators */
+struct hda_bus_ops {
+	/* send a single command */
+	int (*command)(struct hda_codec *codec, hda_nid_t nid, int direct,
+		       unsigned int verb, unsigned int parm);
+	/* get a response from the last command */
+	unsigned int (*get_response)(struct hda_codec *codec);
+	/* free the private data */
+	void (*private_free)(struct hda_bus *);
+};
+
+/* template to pass to the bus constructor */
+struct hda_bus_template {
+	void *private_data;
+	struct pci_dev *pci;
+	const char *modelname;
+	struct hda_bus_ops ops;
+};
+
+/*
+ * codec bus
+ *
+ * each controller needs to creata a hda_bus to assign the accessor.
+ * A hda_bus contains several codecs in the list codec_list.
+ */
+struct hda_bus {
+	struct snd_card *card;
+
+	/* copied from template */
+	void *private_data;
+	struct pci_dev *pci;
+	const char *modelname;
+	struct hda_bus_ops ops;
+
+	/* codec linked list */
+	struct list_head codec_list;
+	struct hda_codec *caddr_tbl[HDA_MAX_CODEC_ADDRESS + 1]; /* caddr -> codec */
+
+	struct semaphore cmd_mutex;
+
+	/* unsolicited event queue */
+	struct hda_bus_unsolicited *unsol;
+
+	struct snd_info_entry *proc;
+};
+
+/*
+ * codec preset
+ *
+ * Known codecs have the patch to build and set up the controls/PCMs
+ * better than the generic parser.
+ */
+struct hda_codec_preset {
+	unsigned int id;
+	unsigned int mask;
+	unsigned int subs;
+	unsigned int subs_mask;
+	unsigned int rev;
+	const char *name;
+	int (*patch)(struct hda_codec *codec);
+};
+	
+/* ops set by the preset patch */
+struct hda_codec_ops {
+	int (*build_controls)(struct hda_codec *codec);
+	int (*build_pcms)(struct hda_codec *codec);
+	int (*init)(struct hda_codec *codec);
+	void (*free)(struct hda_codec *codec);
+	void (*unsol_event)(struct hda_codec *codec, unsigned int res);
+#ifdef CONFIG_PM
+	int (*suspend)(struct hda_codec *codec, pm_message_t state);
+	int (*resume)(struct hda_codec *codec);
+#endif
+};
+
+/* record for amp information cache */
+struct hda_amp_info {
+	u32 key;		/* hash key */
+	u32 amp_caps;		/* amp capabilities */
+	u16 vol[2];		/* current volume & mute*/
+	u16 status;		/* update flag */
+	u16 next;		/* next link */
+};
+
+/* PCM callbacks */
+struct hda_pcm_ops {
+	int (*open)(struct hda_pcm_stream *info, struct hda_codec *codec,
+		    struct snd_pcm_substream *substream);
+	int (*close)(struct hda_pcm_stream *info, struct hda_codec *codec,
+		     struct snd_pcm_substream *substream);
+	int (*prepare)(struct hda_pcm_stream *info, struct hda_codec *codec,
+		       unsigned int stream_tag, unsigned int format,
+		       struct snd_pcm_substream *substream);
+	int (*cleanup)(struct hda_pcm_stream *info, struct hda_codec *codec,
+		       struct snd_pcm_substream *substream);
+};
+
+/* PCM information for each substream */
+struct hda_pcm_stream {
+	unsigned int substreams;	/* number of substreams, 0 = not exist */
+	unsigned int channels_min;	/* min. number of channels */
+	unsigned int channels_max;	/* max. number of channels */
+	hda_nid_t nid;	/* default NID to query rates/formats/bps, or set up */
+	u32 rates;	/* supported rates */
+	u64 formats;	/* supported formats (SNDRV_PCM_FMTBIT_) */
+	unsigned int maxbps;	/* supported max. bit per sample */
+	struct hda_pcm_ops ops;
+};
+
+/* for PCM creation */
+struct hda_pcm {
+	char *name;
+	struct hda_pcm_stream stream[2];
+	unsigned int is_modem;	/* modem codec? */
+};
+
+/* codec information */
+struct hda_codec {
+	struct hda_bus *bus;
+	unsigned int addr;	/* codec addr*/
+	struct list_head list;	/* list point */
+
+	hda_nid_t afg;	/* AFG node id */
+	hda_nid_t mfg;	/* MFG node id */
+
+	/* ids */
+	u32 vendor_id;
+	u32 subsystem_id;
+	u32 revision_id;
+
+	/* detected preset */
+	const struct hda_codec_preset *preset;
+
+	/* set by patch */
+	struct hda_codec_ops patch_ops;
+
+	/* resume phase - all controls should update even if
+	 * the values are not changed
+	 */
+	unsigned int in_resume;
+
+	/* PCM to create, set by patch_ops.build_pcms callback */
+	unsigned int num_pcms;
+	struct hda_pcm *pcm_info;
+
+	/* codec specific info */
+	void *spec;
+
+	/* widget capabilities cache */
+	unsigned int num_nodes;
+	hda_nid_t start_nid;
+	u32 *wcaps;
+
+	/* hash for amp access */
+	u16 amp_hash[32];
+	int num_amp_entries;
+	int amp_info_size;
+	struct hda_amp_info *amp_info;
+
+	struct semaphore spdif_mutex;
+	unsigned int spdif_status;	/* IEC958 status bits */
+	unsigned short spdif_ctls;	/* SPDIF control bits */
+	unsigned int spdif_in_enable;	/* SPDIF input enable? */
+};
+
+/* direction */
+enum {
+	HDA_INPUT, HDA_OUTPUT
+};
+
+
+/*
+ * constructors
+ */
+int snd_hda_bus_new(struct snd_card *card, const struct hda_bus_template *temp,
+		    struct hda_bus **busp);
+int snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr,
+		      struct hda_codec **codecp);
+
+/*
+ * low level functions
+ */
+unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid, int direct,
+				unsigned int verb, unsigned int parm);
+int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int direct,
+			unsigned int verb, unsigned int parm);
+#define snd_hda_param_read(codec, nid, param) snd_hda_codec_read(codec, nid, 0, AC_VERB_PARAMETERS, param)
+int snd_hda_get_sub_nodes(struct hda_codec *codec, hda_nid_t nid, hda_nid_t *start_id);
+int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid, hda_nid_t *conn_list, int max_conns);
+
+struct hda_verb {
+	hda_nid_t nid;
+	u32 verb;
+	u32 param;
+};
+
+void snd_hda_sequence_write(struct hda_codec *codec, const struct hda_verb *seq);
+
+/* unsolicited event */
+int snd_hda_queue_unsol_event(struct hda_bus *bus, u32 res, u32 res_ex);
+
+/*
+ * Mixer
+ */
+int snd_hda_build_controls(struct hda_bus *bus);
+
+/*
+ * PCM
+ */
+int snd_hda_build_pcms(struct hda_bus *bus);
+void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid, u32 stream_tag,
+				int channel_id, int format);
+unsigned int snd_hda_calc_stream_format(unsigned int rate, unsigned int channels,
+					unsigned int format, unsigned int maxbps);
+int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
+				u32 *ratesp, u64 *formatsp, unsigned int *bpsp);
+int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid,
+				unsigned int format);
+
+/*
+ * Misc
+ */
+void snd_hda_get_codec_name(struct hda_codec *codec, char *name, int namelen);
+
+/*
+ * power management
+ */
+#ifdef CONFIG_PM
+int snd_hda_suspend(struct hda_bus *bus, pm_message_t state);
+int snd_hda_resume(struct hda_bus *bus);
+#endif
+
+#endif /* __SOUND_HDA_CODEC_H */
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/hda/hda_generic.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/hda/hda_generic.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/hda/hda_generic.c	(revision 91)
@@ -0,0 +1,1107 @@
+/*
+ * Universal Interface for Intel High Definition Audio Codec
+ *
+ * Generic widget tree parser
+ *
+ * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
+ *
+ *  This driver 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 driver is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <sound/core.h>
+#include "hda_codec.h"
+#include "hda_local.h"
+
+/* widget node for parsing */
+struct hda_gnode {
+	hda_nid_t nid;		/* NID of this widget */
+	unsigned short nconns;	/* number of input connections */
+	hda_nid_t *conn_list;
+	hda_nid_t slist[2];	/* temporay list */
+	unsigned int wid_caps;	/* widget capabilities */
+	unsigned char type;	/* widget type */
+	unsigned char pin_ctl;	/* pin controls */
+	unsigned char checked;	/* the flag indicates that the node is already parsed */
+	unsigned int pin_caps;	/* pin widget capabilities */
+	unsigned int def_cfg;	/* default configuration */
+	unsigned int amp_out_caps;	/* AMP out capabilities */
+	unsigned int amp_in_caps;	/* AMP in capabilities */
+	struct list_head list;
+};
+
+/* patch-specific record */
+
+#define MAX_PCM_VOLS	2
+struct pcm_vol {
+	struct hda_gnode *node;	/* Node for PCM volume */
+	unsigned int index;	/* connection of PCM volume */
+};
+
+struct hda_gspec {
+	struct hda_gnode *dac_node[2];	/* DAC node */
+	struct hda_gnode *out_pin_node[2];	/* Output pin (Line-Out) node */
+	struct pcm_vol pcm_vol[MAX_PCM_VOLS];	/* PCM volumes */
+	unsigned int pcm_vol_nodes;	/* number of PCM volumes */
+
+	struct hda_gnode *adc_node;	/* ADC node */
+	struct hda_gnode *cap_vol_node;	/* Node for capture volume */
+	unsigned int cur_cap_src;	/* current capture source */
+	struct hda_input_mux input_mux;
+	char cap_labels[HDA_MAX_NUM_INPUTS][16];
+
+	unsigned int def_amp_in_caps;
+	unsigned int def_amp_out_caps;
+
+	struct hda_pcm pcm_rec;		/* PCM information */
+
+	struct list_head nid_list;	/* list of widgets */
+};
+
+/*
+ * retrieve the default device type from the default config value
+ */
+#define defcfg_type(node) (((node)->def_cfg & AC_DEFCFG_DEVICE) >> \
+			   AC_DEFCFG_DEVICE_SHIFT)
+#define defcfg_location(node) (((node)->def_cfg & AC_DEFCFG_LOCATION) >> \
+			       AC_DEFCFG_LOCATION_SHIFT)
+#define defcfg_port_conn(node) (((node)->def_cfg & AC_DEFCFG_PORT_CONN) >> \
+				AC_DEFCFG_PORT_CONN_SHIFT)
+
+/*
+ * destructor
+ */
+static void snd_hda_generic_free(struct hda_codec *codec)
+{
+	struct hda_gspec *spec = codec->spec;
+	struct list_head *p, *n;
+
+	if (! spec)
+		return;
+	/* free all widgets */
+	list_for_each_safe(p, n, &spec->nid_list) {
+		struct hda_gnode *node = list_entry(p, struct hda_gnode, list);
+		if (node->conn_list != node->slist)
+			kfree(node->conn_list);
+		kfree(node);
+	}
+	kfree(spec);
+}
+
+
+/*
+ * add a new widget node and read its attributes
+ */
+static int add_new_node(struct hda_codec *codec, struct hda_gspec *spec, hda_nid_t nid)
+{
+	struct hda_gnode *node;
+	int nconns;
+	hda_nid_t conn_list[HDA_MAX_CONNECTIONS];
+
+	node = kzalloc(sizeof(*node), GFP_KERNEL);
+	if (node == NULL)
+		return -ENOMEM;
+	node->nid = nid;
+	nconns = snd_hda_get_connections(codec, nid, conn_list,
+					 HDA_MAX_CONNECTIONS);
+	if (nconns < 0) {
+		kfree(node);
+		return nconns;
+	}
+	if (nconns <= ARRAY_SIZE(node->slist))
+		node->conn_list = node->slist;
+	else {
+		node->conn_list = kmalloc(sizeof(hda_nid_t) * nconns,
+					  GFP_KERNEL);
+		if (! node->conn_list) {
+			snd_printk(KERN_ERR "hda-generic: cannot malloc\n");
+			kfree(node);
+			return -ENOMEM;
+		}
+	}
+	memcpy(node->conn_list, conn_list, nconns);
+	node->nconns = nconns;
+	node->wid_caps = get_wcaps(codec, nid);
+	node->type = (node->wid_caps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
+
+	if (node->type == AC_WID_PIN) {
+		node->pin_caps = snd_hda_param_read(codec, node->nid, AC_PAR_PIN_CAP);
+		node->pin_ctl = snd_hda_codec_read(codec, node->nid, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+		node->def_cfg = snd_hda_codec_read(codec, node->nid, 0, AC_VERB_GET_CONFIG_DEFAULT, 0);
+	}
+
+	if (node->wid_caps & AC_WCAP_OUT_AMP) {
+		if (node->wid_caps & AC_WCAP_AMP_OVRD)
+			node->amp_out_caps = snd_hda_param_read(codec, node->nid, AC_PAR_AMP_OUT_CAP);
+		if (! node->amp_out_caps)
+			node->amp_out_caps = spec->def_amp_out_caps;
+	}
+	if (node->wid_caps & AC_WCAP_IN_AMP) {
+		if (node->wid_caps & AC_WCAP_AMP_OVRD)
+			node->amp_in_caps = snd_hda_param_read(codec, node->nid, AC_PAR_AMP_IN_CAP);
+		if (! node->amp_in_caps)
+			node->amp_in_caps = spec->def_amp_in_caps;
+	}
+	list_add_tail(&node->list, &spec->nid_list);
+	return 0;
+}
+
+/*
+ * build the AFG subtree
+ */
+static int build_afg_tree(struct hda_codec *codec)
+{
+	struct hda_gspec *spec = codec->spec;
+	int i, nodes, err;
+	hda_nid_t nid;
+
+	snd_assert(spec, return -EINVAL);
+
+	spec->def_amp_out_caps = snd_hda_param_read(codec, codec->afg, AC_PAR_AMP_OUT_CAP);
+	spec->def_amp_in_caps = snd_hda_param_read(codec, codec->afg, AC_PAR_AMP_IN_CAP);
+
+	nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid);
+	if (! nid || nodes < 0) {
+		printk(KERN_ERR "Invalid AFG subtree\n");
+		return -EINVAL;
+	}
+
+	/* parse all nodes belonging to the AFG */
+	for (i = 0; i < nodes; i++, nid++) {
+		if ((err = add_new_node(codec, spec, nid)) < 0)
+			return err;
+	}
+
+	return 0;
+}
+
+
+/*
+ * look for the node record for the given NID
+ */
+/* FIXME: should avoid the braindead linear search */
+static struct hda_gnode *hda_get_node(struct hda_gspec *spec, hda_nid_t nid)
+{
+	struct list_head *p;
+	struct hda_gnode *node;
+
+	list_for_each(p, &spec->nid_list) {
+		node = list_entry(p, struct hda_gnode, list);
+		if (node->nid == nid)
+			return node;
+	}
+	return NULL;
+}
+
+/*
+ * unmute (and set max vol) the output amplifier
+ */
+static int unmute_output(struct hda_codec *codec, struct hda_gnode *node)
+{
+	unsigned int val, ofs;
+	snd_printdd("UNMUTE OUT: NID=0x%x\n", node->nid);
+	val = (node->amp_out_caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT;
+	ofs = (node->amp_out_caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
+	if (val >= ofs)
+		val -= ofs;
+	val |= AC_AMP_SET_LEFT | AC_AMP_SET_RIGHT;
+	val |= AC_AMP_SET_OUTPUT;
+	return snd_hda_codec_write(codec, node->nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, val);
+}
+
+/*
+ * unmute (and set max vol) the input amplifier
+ */
+static int unmute_input(struct hda_codec *codec, struct hda_gnode *node, unsigned int index)
+{
+	unsigned int val, ofs;
+	snd_printdd("UNMUTE IN: NID=0x%x IDX=0x%x\n", node->nid, index);
+	val = (node->amp_in_caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT;
+	ofs = (node->amp_in_caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
+	if (val >= ofs)
+		val -= ofs;
+	val |= AC_AMP_SET_LEFT | AC_AMP_SET_RIGHT;
+	val |= AC_AMP_SET_INPUT;
+	// awk added - fixed to allow unmuting of indexed amps
+	val |= index << AC_AMP_SET_INDEX_SHIFT;
+	return snd_hda_codec_write(codec, node->nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, val);
+}
+
+/*
+ * select the input connection of the given node.
+ */
+static int select_input_connection(struct hda_codec *codec, struct hda_gnode *node,
+				   unsigned int index)
+{
+	snd_printdd("CONNECT: NID=0x%x IDX=0x%x\n", node->nid, index);
+	return snd_hda_codec_write(codec, node->nid, 0, AC_VERB_SET_CONNECT_SEL, index);
+}
+
+/*
+ * clear checked flag of each node in the node list
+ */
+static void clear_check_flags(struct hda_gspec *spec)
+{
+	struct list_head *p;
+	struct hda_gnode *node;
+
+	list_for_each(p, &spec->nid_list) {
+		node = list_entry(p, struct hda_gnode, list);
+		node->checked = 0;
+	}
+}
+
+/*
+ * parse the output path recursively until reach to an audio output widget
+ *
+ * returns 0 if not found, 1 if found, or a negative error code.
+ */
+static int parse_output_path(struct hda_codec *codec, struct hda_gspec *spec,
+			     struct hda_gnode *node, int dac_idx)
+{
+	int i, err;
+	struct hda_gnode *child;
+
+	if (node->checked)
+		return 0;
+
+	node->checked = 1;
+	if (node->type == AC_WID_AUD_OUT) {
+		if (node->wid_caps & AC_WCAP_DIGITAL) {
+			snd_printdd("Skip Digital OUT node %x\n", node->nid);
+			return 0;
+		}
+		snd_printdd("AUD_OUT found %x\n", node->nid);
+		if (spec->dac_node[dac_idx]) {
+			/* already DAC node is assigned, just unmute & connect */
+			return node == spec->dac_node[dac_idx];
+		}
+		spec->dac_node[dac_idx] = node;
+		if ((node->wid_caps & AC_WCAP_OUT_AMP) &&
+		    spec->pcm_vol_nodes < MAX_PCM_VOLS) {
+			spec->pcm_vol[spec->pcm_vol_nodes].node = node;
+			spec->pcm_vol[spec->pcm_vol_nodes].index = 0;
+			spec->pcm_vol_nodes++;
+		}
+		return 1; /* found */
+	}
+
+	for (i = 0; i < node->nconns; i++) {
+		child = hda_get_node(spec, node->conn_list[i]);
+		if (! child)
+			continue;
+		err = parse_output_path(codec, spec, child, dac_idx);
+		if (err < 0)
+			return err;
+		else if (err > 0) {
+			/* found one,
+			 * select the path, unmute both input and output
+			 */
+			if (node->nconns > 1)
+				select_input_connection(codec, node, i);
+			unmute_input(codec, node, i);
+			unmute_output(codec, node);
+			if (spec->dac_node[dac_idx] &&
+			    spec->pcm_vol_nodes < MAX_PCM_VOLS &&
+			    !(spec->dac_node[dac_idx]->wid_caps &
+			      AC_WCAP_OUT_AMP)) {
+				if ((node->wid_caps & AC_WCAP_IN_AMP) ||
+				    (node->wid_caps & AC_WCAP_OUT_AMP)) {
+					int n = spec->pcm_vol_nodes;
+					spec->pcm_vol[n].node = node;
+					spec->pcm_vol[n].index = i;
+					spec->pcm_vol_nodes++;
+				}
+			}
+			return 1;
+		}
+	}
+	return 0;
+}
+
+/*
+ * Look for the output PIN widget with the given jack type
+ * and parse the output path to that PIN.
+ *
+ * Returns the PIN node when the path to DAC is established.
+ */
+static struct hda_gnode *parse_output_jack(struct hda_codec *codec,
+					   struct hda_gspec *spec,
+					   int jack_type)
+{
+	struct list_head *p;
+	struct hda_gnode *node;
+	int err;
+
+	list_for_each(p, &spec->nid_list) {
+		node = list_entry(p, struct hda_gnode, list);
+		if (node->type != AC_WID_PIN)
+			continue;
+		/* output capable? */
+		if (! (node->pin_caps & AC_PINCAP_OUT))
+			continue;
+		if (defcfg_port_conn(node) == AC_JACK_PORT_NONE)
+			continue; /* unconnected */
+		if (jack_type >= 0) {
+			if (jack_type != defcfg_type(node))
+				continue;
+			if (node->wid_caps & AC_WCAP_DIGITAL)
+				continue; /* skip SPDIF */
+		} else {
+			/* output as default? */
+			if (! (node->pin_ctl & AC_PINCTL_OUT_EN))
+				continue;
+		}
+		clear_check_flags(spec);
+		err = parse_output_path(codec, spec, node, 0);
+		if (err < 0)
+			return NULL;
+		if (! err && spec->out_pin_node[0]) {
+			err = parse_output_path(codec, spec, node, 1);
+			if (err < 0)
+				return NULL;
+		}
+		if (err > 0) {
+			/* unmute the PIN output */
+			unmute_output(codec, node);
+			/* set PIN-Out enable */
+			snd_hda_codec_write(codec, node->nid, 0,
+					    AC_VERB_SET_PIN_WIDGET_CONTROL,
+					    AC_PINCTL_OUT_EN |
+					    ((node->pin_caps & AC_PINCAP_HP_DRV) ?
+					     AC_PINCTL_HP_EN : 0));
+			return node;
+		}
+	}
+	return NULL;
+}
+
+
+/*
+ * parse outputs
+ */
+static int parse_output(struct hda_codec *codec)
+{
+	struct hda_gspec *spec = codec->spec;
+	struct hda_gnode *node;
+
+	/*
+	 * Look for the output PIN widget
+	 */
+	/* first, look for the line-out pin */
+	node = parse_output_jack(codec, spec, AC_JACK_LINE_OUT);
+	if (node) /* found, remember the PIN node */
+		spec->out_pin_node[0] = node;
+	else {
+		/* if no line-out is found, try speaker out */
+		node = parse_output_jack(codec, spec, AC_JACK_SPEAKER);
+		if (node)
+			spec->out_pin_node[0] = node;
+	}
+	/* look for the HP-out pin */
+	node = parse_output_jack(codec, spec, AC_JACK_HP_OUT);
+	if (node) {
+		if (! spec->out_pin_node[0])
+			spec->out_pin_node[0] = node;
+		else
+			spec->out_pin_node[1] = node;
+	}
+
+	if (! spec->out_pin_node[0]) {
+		/* no line-out or HP pins found,
+		 * then choose for the first output pin
+		 */
+		spec->out_pin_node[0] = parse_output_jack(codec, spec, -1);
+		if (! spec->out_pin_node[0])
+			snd_printd("hda_generic: no proper output path found\n");
+	}
+
+	return 0;
+}
+
+/*
+ * input MUX
+ */
+
+/* control callbacks */
+static int capture_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct hda_gspec *spec = codec->spec;
+	return snd_hda_input_mux_info(&spec->input_mux, uinfo);
+}
+
+static int capture_source_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct hda_gspec *spec = codec->spec;
+
+	ucontrol->value.enumerated.item[0] = spec->cur_cap_src;
+	return 0;
+}
+
+static int capture_source_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct hda_gspec *spec = codec->spec;
+	return snd_hda_input_mux_put(codec, &spec->input_mux, ucontrol,
+				     spec->adc_node->nid, &spec->cur_cap_src);
+}
+
+/*
+ * return the string name of the given input PIN widget
+ */
+static const char *get_input_type(struct hda_gnode *node, unsigned int *pinctl)
+{
+	unsigned int location = defcfg_location(node);
+	switch (defcfg_type(node)) {
+	case AC_JACK_LINE_IN:
+		if ((location & 0x0f) == AC_JACK_LOC_FRONT)
+			return "Front Line";
+		return "Line";
+	case AC_JACK_CD:
+#if 0
+		if (pinctl)
+			*pinctl |= AC_PINCTL_VREF_GRD;
+#endif
+		return "CD";
+	case AC_JACK_AUX:
+		if ((location & 0x0f) == AC_JACK_LOC_FRONT)
+			return "Front Aux";
+		return "Aux";
+	case AC_JACK_MIC_IN:
+            if (pinctl &&
+                (node->pin_caps &
+                 (AC_PINCAP_VREF_80 << AC_PINCAP_VREF_SHIFT)))
+                *pinctl |= AC_PINCTL_VREF_80;
+            if ((location & 0x0f) == AC_JACK_LOC_FRONT)
+                return "Front Mic";
+            return "Mic";
+	case AC_JACK_SPDIF_IN:
+		return "SPDIF";
+	case AC_JACK_DIG_OTHER_IN:
+		return "Digital";
+	}
+	return NULL;
+}
+
+/*
+ * parse the nodes recursively until reach to the input PIN
+ *
+ * returns 0 if not found, 1 if found, or a negative error code.
+ */
+static int parse_adc_sub_nodes(struct hda_codec *codec, struct hda_gspec *spec,
+			       struct hda_gnode *node)
+{
+	int i, err;
+	unsigned int pinctl;
+	char *label;
+	const char *type;
+
+	if (node->checked)
+		return 0;
+
+	node->checked = 1;
+	if (node->type != AC_WID_PIN) {
+		for (i = 0; i < node->nconns; i++) {
+			struct hda_gnode *child;
+			child = hda_get_node(spec, node->conn_list[i]);
+			if (! child)
+				continue;
+			err = parse_adc_sub_nodes(codec, spec, child);
+			if (err < 0)
+				return err;
+			if (err > 0) {
+				/* found one,
+				 * select the path, unmute both input and output
+				 */
+				if (node->nconns > 1)
+					select_input_connection(codec, node, i);
+				unmute_input(codec, node, i);
+				unmute_output(codec, node);
+				return err;
+			}
+		}
+		return 0;
+	}
+
+	/* input capable? */
+	if (! (node->pin_caps & AC_PINCAP_IN))
+		return 0;
+
+	if (defcfg_port_conn(node) == AC_JACK_PORT_NONE)
+		return 0; /* unconnected */
+
+	if (node->wid_caps & AC_WCAP_DIGITAL)
+		return 0; /* skip SPDIF */
+
+	if (spec->input_mux.num_items >= HDA_MAX_NUM_INPUTS) {
+		snd_printk(KERN_ERR "hda_generic: Too many items for capture\n");
+		return -EINVAL;
+	}
+
+	pinctl = AC_PINCTL_IN_EN;
+	/* create a proper capture source label */
+	type = get_input_type(node, &pinctl);
+	if (! type) {
+		/* input as default? */
+		if (! (node->pin_ctl & AC_PINCTL_IN_EN))
+			return 0;
+		type = "Input";
+	}
+	label = spec->cap_labels[spec->input_mux.num_items];
+	strcpy(label, type);
+	spec->input_mux.items[spec->input_mux.num_items].label = label;
+
+	/* unmute the PIN external input */
+	unmute_input(codec, node, 0); /* index = 0? */
+	/* set PIN-In enable */
+	snd_hda_codec_write(codec, node->nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pinctl);
+
+	return 1; /* found */
+}
+
+/* add a capture source element */
+static void add_cap_src(struct hda_gspec *spec, int idx)
+{
+	struct hda_input_mux_item *csrc;
+	char *buf;
+	int num, ocap;
+
+	num = spec->input_mux.num_items;
+	csrc = &spec->input_mux.items[num];
+	buf = spec->cap_labels[num];
+	for (ocap = 0; ocap < num; ocap++) {
+		if (! strcmp(buf, spec->cap_labels[ocap])) {
+			/* same label already exists,
+			 * put the index number to be unique
+			 */
+			sprintf(buf, "%s %d", spec->cap_labels[ocap], num);
+			break;
+		}
+	}
+	csrc->index = idx;
+	spec->input_mux.num_items++;
+}
+
+/*
+ * parse input
+ */
+static int parse_input_path(struct hda_codec *codec, struct hda_gnode *adc_node)
+{
+	struct hda_gspec *spec = codec->spec;
+	struct hda_gnode *node;
+	int i, err;
+
+	snd_printdd("AUD_IN = %x\n", adc_node->nid);
+	clear_check_flags(spec);
+
+	// awk added - fixed no recording due to muted widget
+	unmute_input(codec, adc_node, 0);
+	
+	/*
+	 * check each connection of the ADC
+	 * if it reaches to a proper input PIN, add the path as the
+	 * input path.
+	 */
+	/* first, check the direct connections to PIN widgets */
+	for (i = 0; i < adc_node->nconns; i++) {
+		node = hda_get_node(spec, adc_node->conn_list[i]);
+		if (node && node->type == AC_WID_PIN) {
+			err = parse_adc_sub_nodes(codec, spec, node);
+			if (err < 0)
+				return err;
+			else if (err > 0)
+				add_cap_src(spec, i);
+		}
+	}
+	/* ... then check the rests, more complicated connections */
+	for (i = 0; i < adc_node->nconns; i++) {
+		node = hda_get_node(spec, adc_node->conn_list[i]);
+		if (node && node->type != AC_WID_PIN) {
+			err = parse_adc_sub_nodes(codec, spec, node);
+			if (err < 0)
+				return err;
+			else if (err > 0)
+				add_cap_src(spec, i);
+		}
+	}
+
+	if (! spec->input_mux.num_items)
+		return 0; /* no input path found... */
+
+	snd_printdd("[Capture Source] NID=0x%x, #SRC=%d\n", adc_node->nid, spec->input_mux.num_items);
+	for (i = 0; i < spec->input_mux.num_items; i++)
+		snd_printdd("  [%s] IDX=0x%x\n", spec->input_mux.items[i].label,
+			    spec->input_mux.items[i].index);
+
+	spec->adc_node = adc_node;
+	return 1;
+}
+
+/*
+ * parse input
+ */
+static int parse_input(struct hda_codec *codec)
+{
+	struct hda_gspec *spec = codec->spec;
+	struct list_head *p;
+	struct hda_gnode *node;
+	int err;
+
+	/*
+	 * At first we look for an audio input widget.
+	 * If it reaches to certain input PINs, we take it as the
+	 * input path.
+	 */
+	list_for_each(p, &spec->nid_list) {
+		node = list_entry(p, struct hda_gnode, list);
+		if (node->wid_caps & AC_WCAP_DIGITAL)
+			continue; /* skip SPDIF */
+		if (node->type == AC_WID_AUD_IN) {
+			err = parse_input_path(codec, node);
+			if (err < 0)
+				return err;
+			else if (err > 0)
+				return 0;
+		}
+	}
+	snd_printd("hda_generic: no proper input path found\n");
+	return 0;
+}
+
+
+/*
+ * create mixer controls if possible
+ */
+static int create_mixer(struct hda_codec *codec, struct hda_gnode *node,
+			unsigned int index, const char *type, const char *dir_sfx)
+{
+	char name[32];
+	int err;
+	int created = 0;
+	struct snd_kcontrol_new knew;
+
+	if (type)
+		sprintf(name, "%s %s Switch", type, dir_sfx);
+	else
+		sprintf(name, "%s Switch", dir_sfx);
+	if ((node->wid_caps & AC_WCAP_IN_AMP) &&
+	    (node->amp_in_caps & AC_AMPCAP_MUTE)) {
+            /*knew = (struct snd_kcontrol_new)HDA_CODEC_MUTE(name, node->nid, index, HDA_INPUT);*/
+            knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+            knew.name = name;
+            knew.info = snd_hda_mixer_amp_switch_info;
+            knew.get = snd_hda_mixer_amp_switch_get;
+            knew.put = snd_hda_mixer_amp_switch_put;
+            knew.private_value = (node->nid | (3 << 16) | (HDA_INPUT << 18) | (index << 19));
+
+		snd_printdd("[%s] NID=0x%x, DIR=IN, IDX=0x%x\n", name, node->nid, index);
+		if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&knew, codec))) < 0)
+			return err;
+		created = 1;
+	} else if ((node->wid_caps & AC_WCAP_OUT_AMP) &&
+		   (node->amp_out_caps & AC_AMPCAP_MUTE)) {
+            /*knew = (struct snd_kcontrol_new)HDA_CODEC_MUTE(name, node->nid, 0, HDA_OUTPUT);*/
+        knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+        knew.name = name;
+        knew.info = snd_hda_mixer_amp_switch_info;
+        knew.get = snd_hda_mixer_amp_switch_get;
+        knew.put = snd_hda_mixer_amp_switch_put;
+        knew.private_value = (node->nid | (3 << 16) | (HDA_OUTPUT << 18) | (0 << 19));
+
+		snd_printdd("[%s] NID=0x%x, DIR=OUT\n", name, node->nid);
+		if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&knew, codec))) < 0)
+			return err;
+		created = 1;
+	}
+
+	if (type)
+		sprintf(name, "%s %s Volume", type, dir_sfx);
+	else
+		sprintf(name, "%s Volume", dir_sfx);
+	if ((node->wid_caps & AC_WCAP_IN_AMP) &&
+	    (node->amp_in_caps & AC_AMPCAP_NUM_STEPS)) {
+            /*knew = (struct snd_kcontrol_new)HDA_CODEC_VOLUME(name, node->nid, index, HDA_INPUT);*/
+        knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+        knew.name = name;
+        knew.info = snd_hda_mixer_amp_volume_info;
+        knew.get = snd_hda_mixer_amp_volume_get;
+        knew.put = snd_hda_mixer_amp_volume_put;
+        knew.private_value = (node->nid | (3 << 16) | (HDA_INPUT << 18) | (index << 19));
+
+		snd_printdd("[%s] NID=0x%x, DIR=IN, IDX=0x%x\n", name, node->nid, index);
+		if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&knew, codec))) < 0)
+			return err;
+		created = 1;
+	} else if ((node->wid_caps & AC_WCAP_OUT_AMP) &&
+		   (node->amp_out_caps & AC_AMPCAP_NUM_STEPS)) {
+		/*knew = (struct snd_kcontrol_new)HDA_CODEC_VOLUME(name, node->nid, 0, HDA_OUTPUT);*/
+        knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+        knew.name = name;
+        knew.info = snd_hda_mixer_amp_volume_info;
+        knew.get = snd_hda_mixer_amp_volume_get;
+        knew.put = snd_hda_mixer_amp_volume_put;
+        knew.private_value = (node->nid | (3 << 16) | (HDA_OUTPUT << 18) | (0 << 19));
+		snd_printdd("[%s] NID=0x%x, DIR=OUT\n", name, node->nid);
+		if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&knew, codec))) < 0)
+			return err;
+		created = 1;
+	}
+
+	return created;
+}
+
+/*
+ * check whether the controls with the given name and direction suffix already exist
+ */
+static int check_existing_control(struct hda_codec *codec, const char *type, const char *dir)
+{
+	struct snd_ctl_elem_id id;
+	memset(&id, 0, sizeof(id));
+	sprintf(id.name, "%s %s Volume", type, dir);
+	id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+	if (snd_ctl_find_id(codec->bus->card, &id))
+		return 1;
+	sprintf(id.name, "%s %s Switch", type, dir);
+	id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+	if (snd_ctl_find_id(codec->bus->card, &id))
+		return 1;
+	return 0;
+}
+
+/*
+ * build output mixer controls
+ */
+static int create_output_mixers(struct hda_codec *codec, const char **names)
+{
+	struct hda_gspec *spec = codec->spec;
+	int i, err;
+
+	for (i = 0; i < spec->pcm_vol_nodes; i++) {
+		err = create_mixer(codec, spec->pcm_vol[i].node,
+				   spec->pcm_vol[i].index,
+				   names[i], "Playback");
+		if (err < 0)
+			return err;
+	}
+	return 0;
+}
+
+static int build_output_controls(struct hda_codec *codec)
+{
+	struct hda_gspec *spec = codec->spec;
+	static const char *types_speaker[] = { "Speaker", "Headphone" };
+	static const char *types_line[] = { "Front", "Headphone" };
+
+	switch (spec->pcm_vol_nodes) {
+	case 1:
+		return create_mixer(codec, spec->pcm_vol[0].node,
+				    spec->pcm_vol[0].index,
+				    "Master", "Playback");
+	case 2:
+		if (defcfg_type(spec->out_pin_node[0]) == AC_JACK_SPEAKER)
+			return create_output_mixers(codec, types_speaker);
+		else
+			return create_output_mixers(codec, types_line);
+	}
+	return 0;
+}
+
+/* create capture volume/switch */
+static int build_input_controls(struct hda_codec *codec)
+{
+	struct hda_gspec *spec = codec->spec;
+	struct hda_gnode *adc_node = spec->adc_node;
+	int i, err;
+	static struct snd_kcontrol_new cap_sel = {
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Capture Source",
+		.info = capture_source_info,
+		.get = capture_source_get,
+		.put = capture_source_put,
+	};
+
+	if (! adc_node || ! spec->input_mux.num_items)
+		return 0; /* not found */
+
+	spec->cur_cap_src = 0;
+	select_input_connection(codec, adc_node,
+				spec->input_mux.items[0].index);
+
+	/* create capture volume and switch controls if the ADC has an amp */
+	/* do we have only a single item? */
+	if (spec->input_mux.num_items == 1) {
+		err = create_mixer(codec, adc_node,
+				   spec->input_mux.items[0].index,
+				   NULL, "Capture");
+		if (err < 0)
+			return err;
+		return 0;
+	}
+
+	/* create input MUX if multiple sources are available */
+	if ((err = snd_ctl_add(codec->bus->card,
+			       snd_ctl_new1(&cap_sel, codec))) < 0)
+		return err;
+
+	/* no volume control? */
+	if (! (adc_node->wid_caps & AC_WCAP_IN_AMP) ||
+	    ! (adc_node->amp_in_caps & AC_AMPCAP_NUM_STEPS))
+		return 0;
+
+	for (i = 0; i < spec->input_mux.num_items; i++) {
+		struct snd_kcontrol_new knew;
+		char name[32];
+		sprintf(name, "%s Capture Volume",
+			spec->input_mux.items[i].label);
+		/*knew = (struct snd_kcontrol_new)
+			HDA_CODEC_VOLUME(name, adc_node->nid,
+					 spec->input_mux.items[i].index,
+                                         HDA_INPUT);*/
+                knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+                knew.name = name;
+                knew.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+                    SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+                    SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
+                knew.info = snd_hda_mixer_amp_volume_info;
+                knew.get = snd_hda_mixer_amp_volume_get;
+                knew.put = snd_hda_mixer_amp_volume_put;
+                knew.tlv.c = snd_hda_mixer_amp_tlv,
+                knew.private_value = (adc_node->nid | (3 << 16) | (HDA_INPUT << 18) | (spec->input_mux.items[i].index << 19));
+
+		if ((err = snd_ctl_add(codec->bus->card,
+				       snd_ctl_new1(&knew, codec))) < 0)
+			return err;
+	}
+
+	return 0;
+}
+
+
+/*
+ * parse the nodes recursively until reach to the output PIN.
+ *
+ * returns 0 - if not found,
+ *         1 - if found, but no mixer is created
+ *         2 - if found and mixer was already created, (just skip)
+ *         a negative error code
+ */
+static int parse_loopback_path(struct hda_codec *codec, struct hda_gspec *spec,
+			       struct hda_gnode *node, struct hda_gnode *dest_node,
+			       const char *type)
+{
+	int i, err;
+
+	if (node->checked)
+		return 0;
+
+	node->checked = 1;
+	if (node == dest_node) {
+		/* loopback connection found */
+		return 1;
+	}
+
+	for (i = 0; i < node->nconns; i++) {
+		struct hda_gnode *child = hda_get_node(spec, node->conn_list[i]);
+		if (! child)
+			continue;
+		err = parse_loopback_path(codec, spec, child, dest_node, type);
+		if (err < 0)
+			return err;
+		else if (err >= 1) {
+			if (err == 1) {
+				err = create_mixer(codec, node, i, type, "Playback");
+				if (err < 0)
+					return err;
+				if (err > 0)
+					return 2; /* ok, created */
+				/* not created, maybe in the lower path */
+				err = 1;
+			}
+			/* connect and unmute */
+			if (node->nconns > 1)
+				select_input_connection(codec, node, i);
+			unmute_input(codec, node, i);
+			unmute_output(codec, node);
+			return err;
+		}
+	}
+	return 0;
+}
+
+/*
+ * parse the tree and build the loopback controls
+ */
+static int build_loopback_controls(struct hda_codec *codec)
+{
+	struct hda_gspec *spec = codec->spec;
+	struct list_head *p;
+	struct hda_gnode *node;
+	int err;
+	const char *type;
+
+	if (! spec->out_pin_node[0])
+		return 0;
+
+	list_for_each(p, &spec->nid_list) {
+		node = list_entry(p, struct hda_gnode, list);
+		if (node->type != AC_WID_PIN)
+			continue;
+		/* input capable? */
+		if (! (node->pin_caps & AC_PINCAP_IN))
+			return 0;
+		type = get_input_type(node, NULL);
+		if (type) {
+			if (check_existing_control(codec, type, "Playback"))
+				continue;
+			clear_check_flags(spec);
+			err = parse_loopback_path(codec, spec,
+						  spec->out_pin_node[0],
+						  node, type);
+			if (err < 0)
+				return err;
+			if (! err)
+				continue;
+		}
+	}
+	return 0;
+}
+
+/*
+ * build mixer controls
+ */
+static int build_generic_controls(struct hda_codec *codec)
+{
+	int err;
+
+	if ((err = build_input_controls(codec)) < 0 ||
+	    (err = build_output_controls(codec)) < 0 ||
+	    (err = build_loopback_controls(codec)) < 0)
+		return err;
+
+	return 0;
+}
+
+/*
+ * PCM
+ */
+static struct hda_pcm_stream generic_pcm_playback = {
+	.substreams = 1,
+	.channels_min = 2,
+	.channels_max = 2,
+};
+
+static int generic_pcm2_prepare(struct hda_pcm_stream *hinfo,
+				struct hda_codec *codec,
+				unsigned int stream_tag,
+				unsigned int format,
+				struct snd_pcm_substream *substream)
+{
+	struct hda_gspec *spec = codec->spec;
+
+	snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format);
+	snd_hda_codec_setup_stream(codec, spec->dac_node[1]->nid,
+				   stream_tag, 0, format);
+	return 0;
+}
+
+static int generic_pcm2_cleanup(struct hda_pcm_stream *hinfo,
+				struct hda_codec *codec,
+				struct snd_pcm_substream *substream)
+{
+	struct hda_gspec *spec = codec->spec;
+
+	snd_hda_codec_setup_stream(codec, hinfo->nid, 0, 0, 0);
+	snd_hda_codec_setup_stream(codec, spec->dac_node[1]->nid, 0, 0, 0);
+	return 0;
+}
+
+static int build_generic_pcms(struct hda_codec *codec)
+{
+	struct hda_gspec *spec = codec->spec;
+	struct hda_pcm *info = &spec->pcm_rec;
+
+	if (! spec->dac_node[0] && ! spec->adc_node) {
+		snd_printd("hda_generic: no PCM found\n");
+		return 0;
+	}
+
+	codec->num_pcms = 1;
+	codec->pcm_info = info;
+
+	info->name = "HDA Generic";
+	if (spec->dac_node[0]) {
+		info->stream[0] = generic_pcm_playback;
+		info->stream[0].nid = spec->dac_node[0]->nid;
+		if (spec->dac_node[1]) {
+			info->stream[0].ops.prepare = generic_pcm2_prepare;
+			info->stream[0].ops.cleanup = generic_pcm2_cleanup;
+		}
+	}
+	if (spec->adc_node) {
+		info->stream[1] = generic_pcm_playback;
+		info->stream[1].nid = spec->adc_node->nid;
+	}
+
+	return 0;
+}
+
+
+/*
+ */
+static struct hda_codec_ops generic_patch_ops = {
+	.build_controls = build_generic_controls,
+	.build_pcms = build_generic_pcms,
+	.free = snd_hda_generic_free,
+};
+
+/*
+ * the generic parser
+ */
+int snd_hda_parse_generic_codec(struct hda_codec *codec)
+{
+	struct hda_gspec *spec;
+	int err;
+
+	if(!codec->afg)
+		return 0;
+
+	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+	if (spec == NULL) {
+		printk(KERN_ERR "hda_generic: can't allocate spec\n");
+		return -ENOMEM;
+	}
+	codec->spec = spec;
+	INIT_LIST_HEAD(&spec->nid_list);
+
+	if ((err = build_afg_tree(codec)) < 0)
+		goto error;
+
+	if ((err = parse_input(codec)) < 0 ||
+	    (err = parse_output(codec)) < 0)
+		goto error;
+
+	codec->patch_ops = generic_patch_ops;
+
+	return 0;
+
+ error:
+	snd_hda_generic_free(codec);
+	return err;
+}
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/hda/hda_intel.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/hda/hda_intel.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/hda/hda_intel.c	(revision 91)
@@ -0,0 +1,1760 @@
+/*
+ *
+ *  hda_intel.c - Implementation of primary alsa driver code base for Intel HD Audio.
+ *
+ *  Copyright(c) 2004 Intel Corporation. All rights reserved.
+ *
+ *  Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
+ *                     PeiSen Hou <pshou@realtek.com.tw>
+ *
+ *  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.
+ *
+ *  CONTACTS:
+ *
+ *  Matt Jared		matt.jared@intel.com
+ *  Andy Kopp		andy.kopp@intel.com
+ *  Dan Kogan		dan.d.kogan@intel.com
+ *
+ *  CHANGES:
+ *
+ *  2004.12.01	Major rewrite by tiwai, merged the work of pshou
+ * 
+ */
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/mutex.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include "hda_codec.h"
+
+
+static int index = SNDRV_DEFAULT_IDX1;
+static char *id = SNDRV_DEFAULT_STR1;
+static char *model;
+static int position_fix;
+static int probe_mask = -1;
+static int single_cmd;
+static int enable_msi;
+
+//module_param(index, int, 0444);
+MODULE_PARM_DESC(index, "Index value for Intel HD audio interface.");
+//module_param(id, charp, 0444);
+MODULE_PARM_DESC(id, "ID string for Intel HD audio interface.");
+//module_param(model, charp, 0444);
+MODULE_PARM_DESC(model, "Use the given board model.");
+//module_param(position_fix, int, 0444);
+MODULE_PARM_DESC(position_fix, "Fix DMA pointer (0 = auto, 1 = none, 2 = POSBUF, 3 = FIFO size).");
+//module_param(probe_mask, int, 0444);
+MODULE_PARM_DESC(probe_mask, "Bitmask to probe codecs (default = -1).");
+//module_param(single_cmd, bool, 0444);
+MODULE_PARM_DESC(single_cmd, "Use single command to communicate with codecs (for debugging only).");
+//module_param(enable_msi, int, 0);
+MODULE_PARM_DESC(enable_msi, "Enable Message Signaled Interrupt (MSI)");
+
+
+/* just for backward compatibility */
+static int enable;
+//module_param(enable, bool, 0444);
+
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{Intel, ICH6},"
+			 "{Intel, ICH6M},"
+			 "{Intel, ICH7},"
+			 "{Intel, ESB2},"
+			 "{Intel, ICH8},"
+			 "{Intel, ICH9},"
+			 "{ATI, SB450},"
+			 "{ATI, SB600},"
+			 "{ATI, RS600},"
+			 "{ATI, RS690},"
+			 "{VIA, VT8251},"
+			 "{VIA, VT8237A},"
+			 "{SiS, SIS966},"
+			 "{ULI, M5461}}");
+MODULE_DESCRIPTION("Intel HDA driver");
+
+#define SFX	"hda-intel: "
+
+/*
+ * registers
+ */
+#define ICH6_REG_GCAP			0x00
+#define ICH6_REG_VMIN			0x02
+#define ICH6_REG_VMAJ			0x03
+#define ICH6_REG_OUTPAY			0x04
+#define ICH6_REG_INPAY			0x06
+#define ICH6_REG_GCTL			0x08
+#define ICH6_REG_WAKEEN			0x0c
+#define ICH6_REG_STATESTS		0x0e
+#define ICH6_REG_GSTS			0x10
+#define ICH6_REG_INTCTL			0x20
+#define ICH6_REG_INTSTS			0x24
+#define ICH6_REG_WALCLK			0x30
+#define ICH6_REG_SYNC			0x34	
+#define ICH6_REG_CORBLBASE		0x40
+#define ICH6_REG_CORBUBASE		0x44
+#define ICH6_REG_CORBWP			0x48
+#define ICH6_REG_CORBRP			0x4A
+#define ICH6_REG_CORBCTL		0x4c
+#define ICH6_REG_CORBSTS		0x4d
+#define ICH6_REG_CORBSIZE		0x4e
+
+#define ICH6_REG_RIRBLBASE		0x50
+#define ICH6_REG_RIRBUBASE		0x54
+#define ICH6_REG_RIRBWP			0x58
+#define ICH6_REG_RINTCNT		0x5a
+#define ICH6_REG_RIRBCTL		0x5c
+#define ICH6_REG_RIRBSTS		0x5d
+#define ICH6_REG_RIRBSIZE		0x5e
+
+#define ICH6_REG_IC			0x60
+#define ICH6_REG_IR			0x64
+#define ICH6_REG_IRS			0x68
+#define   ICH6_IRS_VALID	(1<<1)
+#define   ICH6_IRS_BUSY		(1<<0)
+
+#define ICH6_REG_DPLBASE		0x70
+#define ICH6_REG_DPUBASE		0x74
+#define   ICH6_DPLBASE_ENABLE	0x1	/* Enable position buffer */
+
+/* SD offset: SDI0=0x80, SDI1=0xa0, ... SDO3=0x160 */
+enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 };
+
+/* stream register offsets from stream base */
+#define ICH6_REG_SD_CTL			0x00
+#define ICH6_REG_SD_STS			0x03
+#define ICH6_REG_SD_LPIB		0x04
+#define ICH6_REG_SD_CBL			0x08
+#define ICH6_REG_SD_LVI			0x0c
+#define ICH6_REG_SD_FIFOW		0x0e
+#define ICH6_REG_SD_FIFOSIZE		0x10
+#define ICH6_REG_SD_FORMAT		0x12
+#define ICH6_REG_SD_BDLPL		0x18
+#define ICH6_REG_SD_BDLPU		0x1c
+
+/* PCI space */
+#define ICH6_PCIREG_TCSEL	0x44
+
+/*
+ * other constants
+ */
+
+/* max number of SDs */
+/* ICH, ATI and VIA have 4 playback and 4 capture */
+#define ICH6_CAPTURE_INDEX	0
+#define ICH6_NUM_CAPTURE	4
+#define ICH6_PLAYBACK_INDEX	4
+#define ICH6_NUM_PLAYBACK	4
+
+/* ULI has 6 playback and 5 capture */
+#define ULI_CAPTURE_INDEX	0
+#define ULI_NUM_CAPTURE		5
+#define ULI_PLAYBACK_INDEX	5
+#define ULI_NUM_PLAYBACK	6
+
+/* ATI HDMI has 1 playback and 0 capture */
+#define ATIHDMI_CAPTURE_INDEX	0
+#define ATIHDMI_NUM_CAPTURE	0
+#define ATIHDMI_PLAYBACK_INDEX	0
+#define ATIHDMI_NUM_PLAYBACK	1
+
+/* this number is statically defined for simplicity */
+#define MAX_AZX_DEV		16
+
+/* max number of fragments - we may use more if allocating more pages for BDL */
+#define BDL_SIZE		PAGE_ALIGN(8192)
+#define AZX_MAX_FRAG		(BDL_SIZE / (MAX_AZX_DEV * 16))
+/* max buffer size - no h/w limit, you can increase as you like */
+#define AZX_MAX_BUF_SIZE	(1024*1024*1024)
+/* max number of PCM devics per card */
+#define AZX_MAX_AUDIO_PCMS	6
+#define AZX_MAX_MODEM_PCMS	2
+#define AZX_MAX_PCMS		(AZX_MAX_AUDIO_PCMS + AZX_MAX_MODEM_PCMS)
+
+/* RIRB int mask: overrun[2], response[0] */
+#define RIRB_INT_RESPONSE	0x01
+#define RIRB_INT_OVERRUN	0x04
+#define RIRB_INT_MASK		0x05
+
+/* STATESTS int mask: SD2,SD1,SD0 */
+#define STATESTS_INT_MASK	0x07
+#define AZX_MAX_CODECS		4
+
+/* SD_CTL bits */
+#define SD_CTL_STREAM_RESET	0x01	/* stream reset bit */
+#define SD_CTL_DMA_START	0x02	/* stream DMA start bit */
+#define SD_CTL_STREAM_TAG_MASK	(0xf << 20)
+#define SD_CTL_STREAM_TAG_SHIFT	20
+
+/* SD_CTL and SD_STS */
+#define SD_INT_DESC_ERR		0x10	/* descriptor error interrupt */
+#define SD_INT_FIFO_ERR		0x08	/* FIFO error interrupt */
+#define SD_INT_COMPLETE		0x04	/* completion interrupt */
+#define SD_INT_MASK		(SD_INT_DESC_ERR|SD_INT_FIFO_ERR|SD_INT_COMPLETE)
+
+/* SD_STS */
+#define SD_STS_FIFO_READY	0x20	/* FIFO ready */
+
+/* INTCTL and INTSTS */
+#define ICH6_INT_ALL_STREAM	0xff		/* all stream interrupts */
+#define ICH6_INT_CTRL_EN	0x40000000	/* controller interrupt enable bit */
+#define ICH6_INT_GLOBAL_EN	0x80000000	/* global interrupt enable bit */
+
+/* GCTL unsolicited response enable bit */
+#define ICH6_GCTL_UREN		(1<<8)
+
+/* GCTL reset bit */
+#define ICH6_GCTL_RESET		(1<<0)
+
+/* CORB/RIRB control, read/write pointer */
+#define ICH6_RBCTL_DMA_EN	0x02	/* enable DMA */
+#define ICH6_RBCTL_IRQ_EN	0x01	/* enable IRQ */
+#define ICH6_RBRWP_CLR		0x8000	/* read/write pointer clear */
+/* below are so far hardcoded - should read registers in future */
+#define ICH6_MAX_CORB_ENTRIES	256
+#define ICH6_MAX_RIRB_ENTRIES	256
+
+/* position fix mode */
+enum {
+	POS_FIX_AUTO,
+	POS_FIX_NONE,
+	POS_FIX_POSBUF,
+	POS_FIX_FIFO,
+};
+
+/* Defines for ATI HD Audio support in SB450 south bridge */
+#define ATI_SB450_HDAUDIO_MISC_CNTR2_ADDR   0x42
+#define ATI_SB450_HDAUDIO_ENABLE_SNOOP      0x02
+
+/* Defines for Nvidia HDA support */
+#define NVIDIA_HDA_TRANSREG_ADDR      0x4e
+#define NVIDIA_HDA_ENABLE_COHBITS     0x0f
+
+/*
+ */
+
+struct azx_dev {
+	u32 *bdl;			/* virtual address of the BDL */
+	dma_addr_t bdl_addr;		/* physical address of the BDL */
+	u32 *posbuf;			/* position buffer pointer */
+
+	unsigned int bufsize;		/* size of the play buffer in bytes */
+	unsigned int fragsize;		/* size of each period in bytes */
+	unsigned int frags;		/* number for period in the play buffer */
+	unsigned int fifo_size;		/* FIFO size */
+
+	void __iomem *sd_addr;		/* stream descriptor pointer */
+
+	u32 sd_int_sta_mask;		/* stream int status mask */
+
+	/* pcm support */
+	struct snd_pcm_substream *substream;	/* assigned substream, set in PCM open */
+	unsigned int format_val;	/* format value to be set in the controller and the codec */
+	unsigned char stream_tag;	/* assigned stream */
+	unsigned char index;		/* stream index */
+	/* for sanity check of position buffer */
+	unsigned int period_intr;
+
+	unsigned int opened :1;
+	unsigned int running :1;
+};
+
+/* CORB/RIRB */
+struct azx_rb {
+	u32 *buf;		/* CORB/RIRB buffer
+				 * Each CORB entry is 4byte, RIRB is 8byte
+				 */
+	dma_addr_t addr;	/* physical address of CORB/RIRB buffer */
+	/* for RIRB */
+	unsigned short rp, wp;	/* read/write pointers */
+	int cmds;		/* number of pending requests */
+	u32 res;		/* last read value */
+};
+
+struct azx {
+	struct snd_card *card;
+	struct pci_dev *pci;
+
+	/* chip type specific */
+	int driver_type;
+	int playback_streams;
+	int playback_index_offset;
+	int capture_streams;
+	int capture_index_offset;
+	int num_streams;
+
+	/* pci resources */
+	unsigned long addr;
+	void __iomem *remap_addr;
+	int irq;
+
+	/* locks */
+	spinlock_t reg_lock;
+	struct mutex open_mutex;
+
+	/* streams (x num_streams) */
+	struct azx_dev *azx_dev;
+
+	/* PCM */
+	unsigned int pcm_devs;
+	struct snd_pcm *pcm[AZX_MAX_PCMS];
+
+	/* HD codec */
+	unsigned short codec_mask;
+	struct hda_bus *bus;
+
+	/* CORB/RIRB */
+	struct azx_rb corb;
+	struct azx_rb rirb;
+
+	/* BDL, CORB/RIRB and position buffers */
+	struct snd_dma_buffer bdl;
+	struct snd_dma_buffer rb;
+	struct snd_dma_buffer posbuf;
+
+	/* flags */
+	int position_fix;
+	unsigned int initialized :1;
+	unsigned int single_cmd :1;
+	unsigned int polling_mode :1;
+	unsigned int msi :1;
+};
+
+/* driver types */
+enum {
+	AZX_DRIVER_ICH,
+	AZX_DRIVER_ATI,
+	AZX_DRIVER_ATIHDMI,
+	AZX_DRIVER_VIA,
+	AZX_DRIVER_SIS,
+	AZX_DRIVER_ULI,
+	AZX_DRIVER_NVIDIA,
+};
+
+static char *driver_short_names[] __devinitdata = {
+	[AZX_DRIVER_ICH] = "HDA Intel",
+	[AZX_DRIVER_ATI] = "HDA ATI SB",
+	[AZX_DRIVER_ATIHDMI] = "HDA ATI HDMI",
+	[AZX_DRIVER_VIA] = "HDA VIA VT82xx",
+	[AZX_DRIVER_SIS] = "HDA SIS966",
+	[AZX_DRIVER_ULI] = "HDA ULI M5461",
+	[AZX_DRIVER_NVIDIA] = "HDA NVidia",
+};
+
+/*
+ * macros for easy use
+ */
+#define azx_writel(chip,reg,value) \
+	writel(value, (chip)->remap_addr + ICH6_REG_##reg)
+#define azx_readl(chip,reg) \
+	readl((chip)->remap_addr + ICH6_REG_##reg)
+#define azx_writew(chip,reg,value) \
+	writew(value, (chip)->remap_addr + ICH6_REG_##reg)
+#define azx_readw(chip,reg) \
+	readw((chip)->remap_addr + ICH6_REG_##reg)
+#define azx_writeb(chip,reg,value) \
+	writeb(value, (chip)->remap_addr + ICH6_REG_##reg)
+#define azx_readb(chip,reg) \
+	readb((chip)->remap_addr + ICH6_REG_##reg)
+
+#define azx_sd_writel(dev,reg,value) \
+	writel(value, (dev)->sd_addr + ICH6_REG_##reg)
+#define azx_sd_readl(dev,reg) \
+	readl((dev)->sd_addr + ICH6_REG_##reg)
+#define azx_sd_writew(dev,reg,value) \
+	writew(value, (dev)->sd_addr + ICH6_REG_##reg)
+#define azx_sd_readw(dev,reg) \
+	readw((dev)->sd_addr + ICH6_REG_##reg)
+#define azx_sd_writeb(dev,reg,value) \
+	writeb(value, (dev)->sd_addr + ICH6_REG_##reg)
+#define azx_sd_readb(dev,reg) \
+	readb((dev)->sd_addr + ICH6_REG_##reg)
+
+/* for pcm support */
+#define get_azx_dev(substream) (substream->runtime->private_data)
+
+/* Get the upper 32bit of the given dma_addr_t
+ * Compiler should optimize and eliminate the code if dma_addr_t is 32bit
+ */
+#define upper_32bit(addr) (sizeof(addr) > 4 ? (u32)((addr) >> 32) : (u32)0)
+
+static int azx_acquire_irq(struct azx *chip, int do_disconnect);
+
+/*
+ * Interface for HD codec
+ */
+
+/*
+ * CORB / RIRB interface
+ */
+static int azx_alloc_cmd_io(struct azx *chip)
+{
+	int err;
+
+	/* single page (at least 4096 bytes) must suffice for both ringbuffes */
+	err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci),
+				  PAGE_SIZE, &chip->rb);
+	if (err < 0) {
+		snd_printk(KERN_ERR SFX "cannot allocate CORB/RIRB\n");
+		return err;
+	}
+	return 0;
+}
+
+static void azx_init_cmd_io(struct azx *chip)
+{
+	/* CORB set up */
+	chip->corb.addr = chip->rb.addr;
+	chip->corb.buf = (u32 *)chip->rb.area;
+	azx_writel(chip, CORBLBASE, (u32)chip->corb.addr);
+	azx_writel(chip, CORBUBASE, upper_32bit(chip->corb.addr));
+
+	/* set the corb size to 256 entries (ULI requires explicitly) */
+	azx_writeb(chip, CORBSIZE, 0x02);
+	/* set the corb write pointer to 0 */
+	azx_writew(chip, CORBWP, 0);
+	/* reset the corb hw read pointer */
+	azx_writew(chip, CORBRP, ICH6_RBRWP_CLR);
+	/* enable corb dma */
+	azx_writeb(chip, CORBCTL, ICH6_RBCTL_DMA_EN);
+
+	/* RIRB set up */
+	chip->rirb.addr = chip->rb.addr + 2048;
+	chip->rirb.buf = (u32 *)(chip->rb.area + 2048);
+	azx_writel(chip, RIRBLBASE, (u32)chip->rirb.addr);
+	azx_writel(chip, RIRBUBASE, upper_32bit(chip->rirb.addr));
+
+	/* set the rirb size to 256 entries (ULI requires explicitly) */
+	azx_writeb(chip, RIRBSIZE, 0x02);
+	/* reset the rirb hw write pointer */
+	azx_writew(chip, RIRBWP, ICH6_RBRWP_CLR);
+	/* set N=1, get RIRB response interrupt for new entry */
+	azx_writew(chip, RINTCNT, 1);
+	/* enable rirb dma and response irq */
+	azx_writeb(chip, RIRBCTL, ICH6_RBCTL_DMA_EN | ICH6_RBCTL_IRQ_EN);
+	chip->rirb.rp = chip->rirb.cmds = 0;
+}
+
+static void azx_free_cmd_io(struct azx *chip)
+{
+	/* disable ringbuffer DMAs */
+	azx_writeb(chip, RIRBCTL, 0);
+	azx_writeb(chip, CORBCTL, 0);
+}
+
+/* send a command */
+static int azx_corb_send_cmd(struct hda_codec *codec, hda_nid_t nid, int direct,
+			     unsigned int verb, unsigned int para)
+{
+	struct azx *chip = codec->bus->private_data;
+	unsigned int wp;
+	u32 val;
+
+	val = (u32)(codec->addr & 0x0f) << 28;
+	val |= (u32)direct << 27;
+	val |= (u32)nid << 20;
+	val |= verb << 8;
+	val |= para;
+
+	/* add command to corb */
+	wp = azx_readb(chip, CORBWP);
+	wp++;
+	wp %= ICH6_MAX_CORB_ENTRIES;
+
+	spin_lock_irq(&chip->reg_lock);
+	chip->rirb.cmds++;
+	chip->corb.buf[wp] = cpu_to_le32(val);
+	azx_writel(chip, CORBWP, wp);
+	spin_unlock_irq(&chip->reg_lock);
+
+	return 0;
+}
+
+#define ICH6_RIRB_EX_UNSOL_EV	(1<<4)
+
+/* retrieve RIRB entry - called from interrupt handler */
+static void azx_update_rirb(struct azx *chip)
+{
+	unsigned int rp, wp;
+	u32 res, res_ex;
+
+	wp = azx_readb(chip, RIRBWP);
+	if (wp == chip->rirb.wp)
+		return;
+	chip->rirb.wp = wp;
+		
+	while (chip->rirb.rp != wp) {
+		chip->rirb.rp++;
+		chip->rirb.rp %= ICH6_MAX_RIRB_ENTRIES;
+
+		rp = chip->rirb.rp << 1; /* an RIRB entry is 8-bytes */
+		res_ex = le32_to_cpu(chip->rirb.buf[rp + 1]);
+		res = le32_to_cpu(chip->rirb.buf[rp]);
+		if (res_ex & ICH6_RIRB_EX_UNSOL_EV)
+			snd_hda_queue_unsol_event(chip->bus, res, res_ex);
+		else if (chip->rirb.cmds) {
+			chip->rirb.cmds--;
+			chip->rirb.res = res;
+		}
+	}
+}
+
+/* receive a response */
+static unsigned int azx_rirb_get_response(struct hda_codec *codec)
+{
+	struct azx *chip = codec->bus->private_data;
+	unsigned long timeout;
+
+ again:
+	timeout = jiffies + msecs_to_jiffies(1000);
+	do {
+		if (chip->polling_mode) {
+			spin_lock_irq(&chip->reg_lock);
+			azx_update_rirb(chip);
+			spin_unlock_irq(&chip->reg_lock);
+		}
+		if (! chip->rirb.cmds)
+			return chip->rirb.res; /* the last value */
+		schedule_timeout_interruptible(1);
+	} while (time_after_eq(timeout, jiffies));
+
+	if (chip->msi) {
+		snd_printk(KERN_WARNING "hda_intel: No response from codec, "
+			   "disabling MSI...\n");
+		free_irq(chip->irq, chip);
+		chip->irq = -1;
+		pci_disable_msi(chip->pci);
+		chip->msi = 0;
+		if (azx_acquire_irq(chip, 1) < 0)
+			return -1;
+		goto again;
+	}
+
+	if (!chip->polling_mode) {
+		snd_printk(KERN_WARNING "hda_intel: azx_get_response timeout, "
+			   "switching to polling mode...\n");
+		chip->polling_mode = 1;
+		goto again;
+	}
+
+	snd_printk(KERN_ERR "hda_intel: azx_get_response timeout, "
+		   "switching to single_cmd mode...\n");
+	chip->rirb.rp = azx_readb(chip, RIRBWP);
+	chip->rirb.cmds = 0;
+	/* switch to single_cmd mode */
+	chip->single_cmd = 1;
+	azx_free_cmd_io(chip);
+	return -1;
+}
+
+/*
+ * Use the single immediate command instead of CORB/RIRB for simplicity
+ *
+ * Note: according to Intel, this is not preferred use.  The command was
+ *       intended for the BIOS only, and may get confused with unsolicited
+ *       responses.  So, we shouldn't use it for normal operation from the
+ *       driver.
+ *       I left the codes, however, for debugging/testing purposes.
+ */
+
+/* send a command */
+static int azx_single_send_cmd(struct hda_codec *codec, hda_nid_t nid,
+			       int direct, unsigned int verb,
+			       unsigned int para)
+{
+	struct azx *chip = codec->bus->private_data;
+	u32 val;
+	int timeout = 50;
+
+	val = (u32)(codec->addr & 0x0f) << 28;
+	val |= (u32)direct << 27;
+	val |= (u32)nid << 20;
+	val |= verb << 8;
+	val |= para;
+
+	while (timeout--) {
+		/* check ICB busy bit */
+		if (! (azx_readw(chip, IRS) & ICH6_IRS_BUSY)) {
+			/* Clear IRV valid bit */
+			azx_writew(chip, IRS, azx_readw(chip, IRS) | ICH6_IRS_VALID);
+			azx_writel(chip, IC, val);
+			azx_writew(chip, IRS, azx_readw(chip, IRS) | ICH6_IRS_BUSY);
+			return 0;
+		}
+		udelay(1);
+	}
+	snd_printd(SFX "send_cmd timeout: IRS=0x%x, val=0x%x\n", azx_readw(chip, IRS), val);
+	return -EIO;
+}
+
+/* receive a response */
+static unsigned int azx_single_get_response(struct hda_codec *codec)
+{
+	struct azx *chip = codec->bus->private_data;
+	int timeout = 50;
+
+	while (timeout--) {
+		/* check IRV busy bit */
+		if (azx_readw(chip, IRS) & ICH6_IRS_VALID)
+			return azx_readl(chip, IR);
+		udelay(1);
+	}
+	snd_printd(SFX "get_response timeout: IRS=0x%x\n", azx_readw(chip, IRS));
+	return (unsigned int)-1;
+}
+
+/*
+ * The below are the main callbacks from hda_codec.
+ *
+ * They are just the skeleton to call sub-callbacks according to the
+ * current setting of chip->single_cmd.
+ */
+
+/* send a command */
+static int azx_send_cmd(struct hda_codec *codec, hda_nid_t nid,
+			int direct, unsigned int verb,
+			unsigned int para)
+{
+	struct azx *chip = codec->bus->private_data;
+	if (chip->single_cmd)
+		return azx_single_send_cmd(codec, nid, direct, verb, para);
+	else
+		return azx_corb_send_cmd(codec, nid, direct, verb, para);
+}
+
+/* get a response */
+static unsigned int azx_get_response(struct hda_codec *codec)
+{
+	struct azx *chip = codec->bus->private_data;
+	if (chip->single_cmd)
+		return azx_single_get_response(codec);
+	else
+		return azx_rirb_get_response(codec);
+}
+
+
+/* reset codec link */
+static int azx_reset(struct azx *chip)
+{
+	int count;
+
+	/* reset controller */
+	azx_writel(chip, GCTL, azx_readl(chip, GCTL) & ~ICH6_GCTL_RESET);
+
+	count = 50;
+	while (azx_readb(chip, GCTL) && --count)
+		msleep(1);
+
+	/* delay for >= 100us for codec PLL to settle per spec
+	 * Rev 0.9 section 5.5.1
+	 */
+	msleep(1);
+
+	/* Bring controller out of reset */
+	azx_writeb(chip, GCTL, azx_readb(chip, GCTL) | ICH6_GCTL_RESET);
+
+	count = 50;
+	while (!azx_readb(chip, GCTL) && --count)
+		msleep(1);
+
+	/* Brent Chartrand said to wait >= 540us for codecs to initialize */
+	msleep(1);
+
+	/* check to see if controller is ready */
+	if (!azx_readb(chip, GCTL)) {
+		snd_printd("azx_reset: controller not ready!\n");
+		return -EBUSY;
+	}
+
+	/* Accept unsolicited responses */
+	azx_writel(chip, GCTL, azx_readl(chip, GCTL) | ICH6_GCTL_UREN);
+
+	/* detect codecs */
+	if (!chip->codec_mask) {
+		chip->codec_mask = azx_readw(chip, STATESTS);
+		snd_printdd("codec_mask = 0x%x\n", chip->codec_mask);
+	}
+
+	return 0;
+}
+
+
+/*
+ * Lowlevel interface
+ */  
+
+/* enable interrupts */
+static void azx_int_enable(struct azx *chip)
+{
+	/* enable controller CIE and GIE */
+	azx_writel(chip, INTCTL, azx_readl(chip, INTCTL) |
+		   ICH6_INT_CTRL_EN | ICH6_INT_GLOBAL_EN);
+}
+
+/* disable interrupts */
+static void azx_int_disable(struct azx *chip)
+{
+	int i;
+
+	/* disable interrupts in stream descriptor */
+	for (i = 0; i < chip->num_streams; i++) {
+		struct azx_dev *azx_dev = &chip->azx_dev[i];
+		azx_sd_writeb(azx_dev, SD_CTL,
+			      azx_sd_readb(azx_dev, SD_CTL) & ~SD_INT_MASK);
+	}
+
+	/* disable SIE for all streams */
+	azx_writeb(chip, INTCTL, 0);
+
+	/* disable controller CIE and GIE */
+	azx_writel(chip, INTCTL, azx_readl(chip, INTCTL) &
+		   ~(ICH6_INT_CTRL_EN | ICH6_INT_GLOBAL_EN));
+}
+
+/* clear interrupts */
+static void azx_int_clear(struct azx *chip)
+{
+	int i;
+
+	/* clear stream status */
+	for (i = 0; i < chip->num_streams; i++) {
+		struct azx_dev *azx_dev = &chip->azx_dev[i];
+		azx_sd_writeb(azx_dev, SD_STS, SD_INT_MASK);
+	}
+
+	/* clear STATESTS */
+	azx_writeb(chip, STATESTS, STATESTS_INT_MASK);
+
+	/* clear rirb status */
+	azx_writeb(chip, RIRBSTS, RIRB_INT_MASK);
+
+	/* clear int status */
+	azx_writel(chip, INTSTS, ICH6_INT_CTRL_EN | ICH6_INT_ALL_STREAM);
+}
+
+/* start a stream */
+static void azx_stream_start(struct azx *chip, struct azx_dev *azx_dev)
+{
+	/* enable SIE */
+	azx_writeb(chip, INTCTL,
+		   azx_readb(chip, INTCTL) | (1 << azx_dev->index));
+	/* set DMA start and interrupt mask */
+	azx_sd_writeb(azx_dev, SD_CTL, azx_sd_readb(azx_dev, SD_CTL) |
+		      SD_CTL_DMA_START | SD_INT_MASK);
+}
+
+/* stop a stream */
+static void azx_stream_stop(struct azx *chip, struct azx_dev *azx_dev)
+{
+	/* stop DMA */
+	azx_sd_writeb(azx_dev, SD_CTL, azx_sd_readb(azx_dev, SD_CTL) &
+		      ~(SD_CTL_DMA_START | SD_INT_MASK));
+	azx_sd_writeb(azx_dev, SD_STS, SD_INT_MASK); /* to be sure */
+	/* disable SIE */
+	azx_writeb(chip, INTCTL,
+		   azx_readb(chip, INTCTL) & ~(1 << azx_dev->index));
+}
+
+
+/*
+ * initialize the chip
+ */
+static void azx_init_chip(struct azx *chip)
+{
+	unsigned char reg;
+
+	/* Clear bits 0-2 of PCI register TCSEL (at offset 0x44)
+	 * TCSEL == Traffic Class Select Register, which sets PCI express QOS
+	 * Ensuring these bits are 0 clears playback static on some HD Audio codecs
+	 */
+	pci_read_config_byte (chip->pci, ICH6_PCIREG_TCSEL, &reg);
+	pci_write_config_byte(chip->pci, ICH6_PCIREG_TCSEL, reg & 0xf8);
+
+	/* reset controller */
+	azx_reset(chip);
+
+	/* initialize interrupts */
+	azx_int_clear(chip);
+	azx_int_enable(chip);
+
+	/* initialize the codec command I/O */
+	if (!chip->single_cmd)
+		azx_init_cmd_io(chip);
+
+	/* program the position buffer */
+	azx_writel(chip, DPLBASE, (u32)chip->posbuf.addr);
+	azx_writel(chip, DPUBASE, upper_32bit(chip->posbuf.addr));
+
+	switch (chip->driver_type) {
+	case AZX_DRIVER_ATI:
+		/* For ATI SB450 azalia HD audio, we need to enable snoop */
+		pci_read_config_byte(chip->pci, ATI_SB450_HDAUDIO_MISC_CNTR2_ADDR, 
+				     &reg);
+		pci_write_config_byte(chip->pci, ATI_SB450_HDAUDIO_MISC_CNTR2_ADDR, 
+				      (reg & 0xf8) | ATI_SB450_HDAUDIO_ENABLE_SNOOP);
+		break;
+	case AZX_DRIVER_NVIDIA:
+		/* For NVIDIA HDA, enable snoop */
+		pci_read_config_byte(chip->pci,NVIDIA_HDA_TRANSREG_ADDR, &reg);
+		pci_write_config_byte(chip->pci,NVIDIA_HDA_TRANSREG_ADDR,
+				      (reg & 0xf0) | NVIDIA_HDA_ENABLE_COHBITS);
+		break;
+        }
+}
+
+
+/*
+ * interrupt handler
+ */
+static irqreturn_t azx_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct azx *chip = dev_id;
+	struct azx_dev *azx_dev;
+	u32 status;
+	int i;
+
+	spin_lock(&chip->reg_lock);
+
+	status = azx_readl(chip, INTSTS);
+	if (status == 0) {
+		spin_unlock(&chip->reg_lock);
+		return IRQ_NONE;
+	}
+	
+	for (i = 0; i < chip->num_streams; i++) {
+		azx_dev = &chip->azx_dev[i];
+		if (status & azx_dev->sd_int_sta_mask) {
+			azx_sd_writeb(azx_dev, SD_STS, SD_INT_MASK);
+			if (azx_dev->substream && azx_dev->running) {
+				azx_dev->period_intr++;
+				spin_unlock(&chip->reg_lock);
+				snd_pcm_period_elapsed(azx_dev->substream);
+				spin_lock(&chip->reg_lock);
+			}
+		}
+	}
+
+	/* clear rirb int */
+	status = azx_readb(chip, RIRBSTS);
+	if (status & RIRB_INT_MASK) {
+		if (! chip->single_cmd && (status & RIRB_INT_RESPONSE))
+			azx_update_rirb(chip);
+		azx_writeb(chip, RIRBSTS, RIRB_INT_MASK);
+	}
+
+#if 0
+	/* clear state status int */
+	if (azx_readb(chip, STATESTS) & 0x04)
+		azx_writeb(chip, STATESTS, 0x04);
+#endif
+	spin_unlock(&chip->reg_lock);
+	
+	return IRQ_HANDLED;
+}
+
+
+/*
+ * set up BDL entries
+ */
+static void azx_setup_periods(struct azx_dev *azx_dev)
+{
+	u32 *bdl = azx_dev->bdl;
+	dma_addr_t dma_addr = azx_dev->substream->runtime->dma_addr;
+	int idx;
+
+	/* reset BDL address */
+	azx_sd_writel(azx_dev, SD_BDLPL, 0);
+	azx_sd_writel(azx_dev, SD_BDLPU, 0);
+
+	/* program the initial BDL entries */
+	for (idx = 0; idx < azx_dev->frags; idx++) {
+		unsigned int off = idx << 2; /* 4 dword step */
+		dma_addr_t addr = dma_addr + idx * azx_dev->fragsize;
+		/* program the address field of the BDL entry */
+		bdl[off] = cpu_to_le32((u32)addr);
+		bdl[off+1] = cpu_to_le32(upper_32bit(addr));
+
+		/* program the size field of the BDL entry */
+		bdl[off+2] = cpu_to_le32(azx_dev->fragsize);
+
+		/* program the IOC to enable interrupt when buffer completes */
+		bdl[off+3] = cpu_to_le32(0x01);
+	}
+}
+
+/*
+ * set up the SD for streaming
+ */
+static int azx_setup_controller(struct azx *chip, struct azx_dev *azx_dev)
+{
+	unsigned char val;
+	int timeout;
+
+	/* make sure the run bit is zero for SD */
+	azx_sd_writeb(azx_dev, SD_CTL, azx_sd_readb(azx_dev, SD_CTL) & ~SD_CTL_DMA_START);
+	/* reset stream */
+	azx_sd_writeb(azx_dev, SD_CTL, azx_sd_readb(azx_dev, SD_CTL) | SD_CTL_STREAM_RESET);
+	udelay(3);
+	timeout = 300;
+	while (!((val = azx_sd_readb(azx_dev, SD_CTL)) & SD_CTL_STREAM_RESET) &&
+	       --timeout)
+		;
+	val &= ~SD_CTL_STREAM_RESET;
+	azx_sd_writeb(azx_dev, SD_CTL, val);
+	udelay(3);
+
+	timeout = 300;
+	/* waiting for hardware to report that the stream is out of reset */
+	while (((val = azx_sd_readb(azx_dev, SD_CTL)) & SD_CTL_STREAM_RESET) &&
+	       --timeout)
+		;
+
+	/* program the stream_tag */
+	azx_sd_writel(azx_dev, SD_CTL,
+		      (azx_sd_readl(azx_dev, SD_CTL) & ~SD_CTL_STREAM_TAG_MASK) |
+		      (azx_dev->stream_tag << SD_CTL_STREAM_TAG_SHIFT));
+
+	/* program the length of samples in cyclic buffer */
+	azx_sd_writel(azx_dev, SD_CBL, azx_dev->bufsize);
+
+	/* program the stream format */
+	/* this value needs to be the same as the one programmed */
+	azx_sd_writew(azx_dev, SD_FORMAT, azx_dev->format_val);
+
+	/* program the stream LVI (last valid index) of the BDL */
+	azx_sd_writew(azx_dev, SD_LVI, azx_dev->frags - 1);
+
+	/* program the BDL address */
+	/* lower BDL address */
+	azx_sd_writel(azx_dev, SD_BDLPL, (u32)azx_dev->bdl_addr);
+	/* upper BDL address */
+	azx_sd_writel(azx_dev, SD_BDLPU, upper_32bit(azx_dev->bdl_addr));
+
+	/* enable the position buffer */
+	if (! (azx_readl(chip, DPLBASE) & ICH6_DPLBASE_ENABLE))
+		azx_writel(chip, DPLBASE, (u32)chip->posbuf.addr | ICH6_DPLBASE_ENABLE);
+
+	/* set the interrupt enable bits in the descriptor control register */
+	azx_sd_writel(azx_dev, SD_CTL, azx_sd_readl(azx_dev, SD_CTL) | SD_INT_MASK);
+
+	return 0;
+}
+
+
+/*
+ * Codec initialization
+ */
+
+static int __devinit azx_codec_create(struct azx *chip, const char *model)
+{
+	struct hda_bus_template bus_temp;
+	int c, codecs, err;
+
+	memset(&bus_temp, 0, sizeof(bus_temp));
+	bus_temp.private_data = chip;
+	bus_temp.modelname = model;
+	bus_temp.pci = chip->pci;
+	bus_temp.ops.command = azx_send_cmd;
+	bus_temp.ops.get_response = azx_get_response;
+
+	if ((err = snd_hda_bus_new(chip->card, &bus_temp, &chip->bus)) < 0)
+		return err;
+
+	codecs = 0;
+	for (c = 0; c < AZX_MAX_CODECS; c++) {
+		if ((chip->codec_mask & (1 << c)) & probe_mask) {
+			err = snd_hda_codec_new(chip->bus, c, NULL);
+			if (err < 0)
+				continue;
+			codecs++;
+		}
+	}
+	if (! codecs) {
+		snd_printk(KERN_ERR SFX "no codecs initialized\n");
+		return -ENXIO;
+	}
+
+	return 0;
+}
+
+
+/*
+ * PCM support
+ */
+
+/* assign a stream for the PCM */
+static inline struct azx_dev *azx_assign_device(struct azx *chip, int stream)
+{
+	int dev, i, nums;
+	if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		dev = chip->playback_index_offset;
+		nums = chip->playback_streams;
+	} else {
+		dev = chip->capture_index_offset;
+		nums = chip->capture_streams;
+	}
+	for (i = 0; i < nums; i++, dev++)
+		if (! chip->azx_dev[dev].opened) {
+			chip->azx_dev[dev].opened = 1;
+			return &chip->azx_dev[dev];
+		}
+	return NULL;
+}
+
+/* release the assigned stream */
+static inline void azx_release_device(struct azx_dev *azx_dev)
+{
+	azx_dev->opened = 0;
+}
+
+static struct snd_pcm_hardware azx_pcm_hw = {
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_MMAP_VALID |
+				 /* No full-resume yet implemented */
+				 /* SNDRV_PCM_INFO_RESUME |*/
+				 SNDRV_PCM_INFO_PAUSE),
+	.formats =		SNDRV_PCM_FMTBIT_S16_LE,
+	.rates =		SNDRV_PCM_RATE_48000,
+	.rate_min =		48000,
+	.rate_max =		48000,
+	.channels_min =		2,
+	.channels_max =		2,
+	.buffer_bytes_max =	AZX_MAX_BUF_SIZE,
+	.period_bytes_min =	128,
+	.period_bytes_max =	AZX_MAX_BUF_SIZE / 2,
+	.periods_min =		2,
+	.periods_max =		AZX_MAX_FRAG,
+	.fifo_size =		0,
+};
+
+struct azx_pcm {
+	struct azx *chip;
+	struct hda_codec *codec;
+	struct hda_pcm_stream *hinfo[2];
+};
+
+static int azx_pcm_open(struct snd_pcm_substream *substream)
+{
+	struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+	struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream];
+	struct azx *chip = apcm->chip;
+	struct azx_dev *azx_dev;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	unsigned long flags;
+	int err;
+
+	mutex_lock(&chip->open_mutex);
+	azx_dev = azx_assign_device(chip, substream->stream);
+	if (azx_dev == NULL) {
+		mutex_unlock(&chip->open_mutex);
+		return -EBUSY;
+	}
+	runtime->hw = azx_pcm_hw;
+	runtime->hw.channels_min = hinfo->channels_min;
+	runtime->hw.channels_max = hinfo->channels_max;
+	runtime->hw.formats = hinfo->formats;
+	runtime->hw.rates = hinfo->rates;
+	snd_pcm_limit_hw_rates(runtime);
+	snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+	if ((err = hinfo->ops.open(hinfo, apcm->codec, substream)) < 0) {
+		azx_release_device(azx_dev);
+		mutex_unlock(&chip->open_mutex);
+		return err;
+	}
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	azx_dev->substream = substream;
+	azx_dev->running = 0;
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+	runtime->private_data = azx_dev;
+	mutex_unlock(&chip->open_mutex);
+	return 0;
+}
+
+static int azx_pcm_close(struct snd_pcm_substream *substream)
+{
+	struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+	struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream];
+	struct azx *chip = apcm->chip;
+	struct azx_dev *azx_dev = get_azx_dev(substream);
+	unsigned long flags;
+
+	mutex_lock(&chip->open_mutex);
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	azx_dev->substream = NULL;
+	azx_dev->running = 0;
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	azx_release_device(azx_dev);
+	hinfo->ops.close(hinfo, apcm->codec, substream);
+	mutex_unlock(&chip->open_mutex);
+	return 0;
+}
+
+static int azx_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params)
+{
+	return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+static int azx_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+	struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+	struct azx_dev *azx_dev = get_azx_dev(substream);
+	struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream];
+
+	/* reset BDL address */
+	azx_sd_writel(azx_dev, SD_BDLPL, 0);
+	azx_sd_writel(azx_dev, SD_BDLPU, 0);
+	azx_sd_writel(azx_dev, SD_CTL, 0);
+
+	hinfo->ops.cleanup(hinfo, apcm->codec, substream);
+
+	return snd_pcm_lib_free_pages(substream);
+}
+
+static int azx_pcm_prepare(struct snd_pcm_substream *substream)
+{
+	struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+	struct azx *chip = apcm->chip;
+	struct azx_dev *azx_dev = get_azx_dev(substream);
+	struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream];
+	struct snd_pcm_runtime *runtime = substream->runtime;
+
+	azx_dev->bufsize = snd_pcm_lib_buffer_bytes(substream);
+	azx_dev->fragsize = snd_pcm_lib_period_bytes(substream);
+	azx_dev->frags = azx_dev->bufsize / azx_dev->fragsize;
+	azx_dev->format_val = snd_hda_calc_stream_format(runtime->rate,
+							 runtime->channels,
+							 runtime->format,
+							 hinfo->maxbps);
+	if (! azx_dev->format_val) {
+		snd_printk(KERN_ERR SFX "invalid format_val, rate=%d, ch=%d, format=%d\n",
+			   runtime->rate, runtime->channels, runtime->format);
+		return -EINVAL;
+	}
+
+	snd_printdd("azx_pcm_prepare: bufsize=0x%x, fragsize=0x%x, format=0x%x\n",
+		    azx_dev->bufsize, azx_dev->fragsize, azx_dev->format_val);
+	azx_setup_periods(azx_dev);
+	azx_setup_controller(chip, azx_dev);
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		azx_dev->fifo_size = azx_sd_readw(azx_dev, SD_FIFOSIZE) + 1;
+	else
+		azx_dev->fifo_size = 0;
+
+	return hinfo->ops.prepare(hinfo, apcm->codec, azx_dev->stream_tag,
+				  azx_dev->format_val, substream);
+}
+
+static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+	struct azx_dev *azx_dev = get_azx_dev(substream);
+	struct azx *chip = apcm->chip;
+	int err = 0;
+
+	spin_lock(&chip->reg_lock);
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_START:
+		azx_stream_start(chip, azx_dev);
+		azx_dev->running = 1;
+		break;
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_STOP:
+		azx_stream_stop(chip, azx_dev);
+		azx_dev->running = 0;
+		break;
+	default:
+		err = -EINVAL;
+	}
+	spin_unlock(&chip->reg_lock);
+	if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH ||
+	    cmd == SNDRV_PCM_TRIGGER_SUSPEND ||
+	    cmd == SNDRV_PCM_TRIGGER_STOP) {
+		int timeout = 5000;
+		while (azx_sd_readb(azx_dev, SD_CTL) & SD_CTL_DMA_START && --timeout)
+			;
+	}
+	return err;
+}
+
+static snd_pcm_uframes_t azx_pcm_pointer(struct snd_pcm_substream *substream)
+{
+	struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+	struct azx *chip = apcm->chip;
+	struct azx_dev *azx_dev = get_azx_dev(substream);
+	unsigned int pos;
+
+	if (chip->position_fix == POS_FIX_POSBUF ||
+	    chip->position_fix == POS_FIX_AUTO) {
+		/* use the position buffer */
+		pos = le32_to_cpu(*azx_dev->posbuf);
+		if (chip->position_fix == POS_FIX_AUTO &&
+		    azx_dev->period_intr == 1 && ! pos) {
+			printk(KERN_WARNING
+			       "hda-intel: Invalid position buffer, "
+			       "using LPIB read method instead.\n");
+			chip->position_fix = POS_FIX_NONE;
+			goto read_lpib;
+		}
+	} else {
+	read_lpib:
+		/* read LPIB */
+		pos = azx_sd_readl(azx_dev, SD_LPIB);
+		if (chip->position_fix == POS_FIX_FIFO)
+			pos += azx_dev->fifo_size;
+	}
+	if (pos >= azx_dev->bufsize)
+		pos = 0;
+	return bytes_to_frames(substream->runtime, pos);
+}
+
+static struct snd_pcm_ops azx_pcm_ops = {
+	.open = azx_pcm_open,
+	.close = azx_pcm_close,
+	.ioctl = snd_pcm_lib_ioctl,
+	.hw_params = azx_pcm_hw_params,
+	.hw_free = azx_pcm_hw_free,
+	.prepare = azx_pcm_prepare,
+	.trigger = azx_pcm_trigger,
+	.pointer = azx_pcm_pointer,
+};
+
+static void azx_pcm_free(struct snd_pcm *pcm)
+{
+	kfree(pcm->private_data);
+}
+
+static int __devinit create_codec_pcm(struct azx *chip, struct hda_codec *codec,
+				      struct hda_pcm *cpcm, int pcm_dev)
+{
+	int err;
+	struct snd_pcm *pcm;
+	struct azx_pcm *apcm;
+
+	/* if no substreams are defined for both playback and capture,
+	 * it's just a placeholder.  ignore it.
+	 */
+	if (!cpcm->stream[0].substreams && !cpcm->stream[1].substreams)
+		return 0;
+
+	snd_assert(cpcm->name, return -EINVAL);
+
+	err = snd_pcm_new(chip->card, cpcm->name, pcm_dev,
+			  cpcm->stream[0].substreams, cpcm->stream[1].substreams,
+			  &pcm);
+	if (err < 0)
+		return err;
+	strcpy(pcm->name, cpcm->name);
+	apcm = kmalloc(sizeof(*apcm), GFP_KERNEL);
+	if (apcm == NULL)
+		return -ENOMEM;
+	apcm->chip = chip;
+	apcm->codec = codec;
+	apcm->hinfo[0] = &cpcm->stream[0];
+	apcm->hinfo[1] = &cpcm->stream[1];
+	pcm->private_data = apcm;
+	pcm->private_free = azx_pcm_free;
+	if (cpcm->stream[0].substreams)
+		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &azx_pcm_ops);
+	if (cpcm->stream[1].substreams)
+		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &azx_pcm_ops);
+	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+					      snd_dma_pci_data(chip->pci),
+					      1024 * 64, 1024 * 1024);
+	chip->pcm[pcm_dev] = pcm;
+	if (chip->pcm_devs < pcm_dev + 1)
+		chip->pcm_devs = pcm_dev + 1;
+
+	return 0;
+}
+
+static int __devinit azx_pcm_create(struct azx *chip)
+{
+	struct list_head *p;
+	struct hda_codec *codec;
+	int c, err;
+	int pcm_dev;
+
+	if ((err = snd_hda_build_pcms(chip->bus)) < 0)
+		return err;
+
+	/* create audio PCMs */
+	pcm_dev = 0;
+	list_for_each(p, &chip->bus->codec_list) {
+		codec = list_entry(p, struct hda_codec, list);
+		for (c = 0; c < codec->num_pcms; c++) {
+			if (codec->pcm_info[c].is_modem)
+				continue; /* create later */
+			if (pcm_dev >= AZX_MAX_AUDIO_PCMS) {
+				snd_printk(KERN_ERR SFX "Too many audio PCMs\n");
+				return -EINVAL;
+			}
+			err = create_codec_pcm(chip, codec, &codec->pcm_info[c], pcm_dev);
+			if (err < 0)
+				return err;
+			pcm_dev++;
+		}
+	}
+
+	/* create modem PCMs */
+	pcm_dev = AZX_MAX_AUDIO_PCMS;
+	list_for_each(p, &chip->bus->codec_list) {
+		codec = list_entry(p, struct hda_codec, list);
+		for (c = 0; c < codec->num_pcms; c++) {
+			if (! codec->pcm_info[c].is_modem)
+				continue; /* already created */
+			if (pcm_dev >= AZX_MAX_PCMS) {
+				snd_printk(KERN_ERR SFX "Too many modem PCMs\n");
+				return -EINVAL;
+			}
+			err = create_codec_pcm(chip, codec, &codec->pcm_info[c], pcm_dev);
+			if (err < 0)
+				return err;
+			chip->pcm[pcm_dev]->dev_class = SNDRV_PCM_CLASS_MODEM;
+			pcm_dev++;
+		}
+	}
+	return 0;
+}
+
+/*
+ * mixer creation - all stuff is implemented in hda module
+ */
+static int __devinit azx_mixer_create(struct azx *chip)
+{
+	return snd_hda_build_controls(chip->bus);
+}
+
+
+/*
+ * initialize SD streams
+ */
+static int __devinit azx_init_stream(struct azx *chip)
+{
+	int i;
+
+	/* initialize each stream (aka device)
+	 * assign the starting bdl address to each stream (device) and initialize
+	 */
+	for (i = 0; i < chip->num_streams; i++) {
+		unsigned int off = sizeof(u32) * (i * AZX_MAX_FRAG * 4);
+		struct azx_dev *azx_dev = &chip->azx_dev[i];
+		azx_dev->bdl = (u32 *)(chip->bdl.area + off);
+		azx_dev->bdl_addr = chip->bdl.addr + off;
+		azx_dev->posbuf = (u32 __iomem *)(chip->posbuf.area + i * 8);
+		/* offset: SDI0=0x80, SDI1=0xa0, ... SDO3=0x160 */
+		azx_dev->sd_addr = chip->remap_addr + (0x20 * i + 0x80);
+		/* int mask: SDI0=0x01, SDI1=0x02, ... SDO3=0x80 */
+		azx_dev->sd_int_sta_mask = 1 << i;
+		/* stream tag: must be non-zero and unique */
+		azx_dev->index = i;
+		azx_dev->stream_tag = i + 1;
+	}
+
+	return 0;
+}
+
+static int azx_acquire_irq(struct azx *chip, int do_disconnect)
+{
+	if (request_irq(chip->pci->irq, azx_interrupt,
+			chip->msi ? 0 : SA_INTERRUPT|SA_SHIRQ,
+			"HDA Intel", chip)) {
+		printk(KERN_ERR "hda-intel: unable to grab IRQ %d, "
+		       "disabling device\n", chip->pci->irq);
+/*		if (do_disconnect)
+			snd_card_disconnect(chip->card);*/
+		return -1;
+	}
+	chip->irq = chip->pci->irq;
+	pci_intx(chip->pci, !chip->msi);
+	return 0;
+}
+
+
+#ifdef CONFIG_PM
+/*
+ * power management
+ */
+static int azx_suspend(struct pci_dev *pci, pm_message_t state)
+{
+	struct snd_card *card = pci_get_drvdata(pci);
+	struct azx *chip = card->private_data;
+	int i;
+
+	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+	for (i = 0; i < chip->pcm_devs; i++)
+		snd_pcm_suspend_all(chip->pcm[i]);
+	snd_hda_suspend(chip->bus, state);
+	azx_free_cmd_io(chip);
+	if (chip->irq >= 0) {
+		synchronize_irq(chip->irq);
+		free_irq(chip->irq, chip);
+		chip->irq = -1;
+	}
+	if (chip->msi)
+		pci_disable_msi(chip->pci);
+	pci_disable_device(pci);
+	pci_save_state(pci);
+	pci_set_power_state(pci, pci_choose_state(pci, state));
+	return 0;
+}
+
+static int azx_resume(struct pci_dev *pci)
+{
+	struct snd_card *card = pci_get_drvdata(pci);
+	struct azx *chip = card->private_data;
+
+	pci_set_power_state(pci, PCI_D0);
+	pci_restore_state(pci);
+	if (pci_enable_device(pci) < 0) {
+		printk(KERN_ERR "hda-intel: pci_enable_device failed, "
+		       "disabling device\n");
+		snd_card_disconnect(card);
+		return -EIO;
+	}
+	pci_set_master(pci);
+	if (chip->msi)
+		if (pci_enable_msi(pci) < 0)
+			chip->msi = 0;
+	if (azx_acquire_irq(chip, 1) < 0)
+		return -EIO;
+	azx_init_chip(chip);
+	snd_hda_resume(chip->bus);
+	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+	return 0;
+}
+#endif /* CONFIG_PM */
+
+
+/*
+ * destructor
+ */
+static int azx_free(struct azx *chip)
+{
+	if (chip->initialized) {
+		int i;
+
+		for (i = 0; i < chip->num_streams; i++)
+			azx_stream_stop(chip, &chip->azx_dev[i]);
+
+		/* disable interrupts */
+		azx_int_disable(chip);
+		azx_int_clear(chip);
+
+		/* disable CORB/RIRB */
+		azx_free_cmd_io(chip);
+
+		/* disable position buffer */
+		azx_writel(chip, DPLBASE, 0);
+		azx_writel(chip, DPUBASE, 0);
+	}
+
+	if (chip->irq >= 0) {
+		synchronize_irq(chip->irq);
+		free_irq(chip->irq, (void*)chip);
+	}
+	if (chip->msi)
+		pci_disable_msi(chip->pci);
+	if (chip->remap_addr)
+		iounmap(chip->remap_addr);
+
+	if (chip->bdl.area)
+		snd_dma_free_pages(&chip->bdl);
+	if (chip->rb.area)
+		snd_dma_free_pages(&chip->rb);
+	if (chip->posbuf.area)
+		snd_dma_free_pages(&chip->posbuf);
+	pci_release_regions(chip->pci);
+	pci_disable_device(chip->pci);
+	kfree(chip->azx_dev);
+	kfree(chip);
+
+	return 0;
+}
+
+static int azx_dev_free(struct snd_device *device)
+{
+	return azx_free(device->device_data);
+}
+
+/*
+ * constructor
+ */
+static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
+				int driver_type,
+				struct azx **rchip)
+{
+	struct azx *chip;
+	int err;
+	static struct snd_device_ops ops = {
+		.dev_free = azx_dev_free,
+	};
+
+	*rchip = NULL;
+	
+	err = pci_enable_device(pci);
+	if (err < 0)
+		return err;
+
+	chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+	if (!chip) {
+		snd_printk(KERN_ERR SFX "cannot allocate chip\n");
+		pci_disable_device(pci);
+		return -ENOMEM;
+	}
+
+	spin_lock_init(&chip->reg_lock);
+	mutex_init(&chip->open_mutex);
+	chip->card = card;
+	chip->pci = pci;
+	chip->irq = -1;
+	chip->driver_type = driver_type;
+	chip->msi = enable_msi;
+
+	chip->position_fix = position_fix;
+	chip->single_cmd = single_cmd;
+
+#if BITS_PER_LONG != 64
+	/* Fix up base address on ULI M5461 */
+	if (chip->driver_type == AZX_DRIVER_ULI) {
+		u16 tmp3;
+		pci_read_config_word(pci, 0x40, &tmp3);
+		pci_write_config_word(pci, 0x40, tmp3 | 0x10);
+		pci_write_config_dword(pci, PCI_BASE_ADDRESS_1, 0);
+	}
+#endif
+
+	err = pci_request_regions(pci, "ICH HD audio");
+	if (err < 0) {
+		kfree(chip);
+		pci_disable_device(pci);
+		return err;
+	}
+
+	chip->addr = pci_resource_start(pci, 0);
+	chip->remap_addr = ioremap_nocache(chip->addr, pci_resource_len(pci,0));
+	if (chip->remap_addr == NULL) {
+		snd_printk(KERN_ERR SFX "ioremap error\n");
+		err = -ENXIO;
+		goto errout;
+	}
+
+	if (chip->msi)
+		if (pci_enable_msi(pci) < 0)
+			chip->msi = 0;
+
+	if (azx_acquire_irq(chip, 0) < 0) {
+		err = -EBUSY;
+		goto errout;
+	}
+
+	pci_set_master(pci);
+	synchronize_irq(chip->irq);
+
+	switch (chip->driver_type) {
+	case AZX_DRIVER_ULI:
+		chip->playback_streams = ULI_NUM_PLAYBACK;
+		chip->capture_streams = ULI_NUM_CAPTURE;
+		chip->playback_index_offset = ULI_PLAYBACK_INDEX;
+		chip->capture_index_offset = ULI_CAPTURE_INDEX;
+		break;
+	case AZX_DRIVER_ATIHDMI:
+		chip->playback_streams = ATIHDMI_NUM_PLAYBACK;
+		chip->capture_streams = ATIHDMI_NUM_CAPTURE;
+		chip->playback_index_offset = ATIHDMI_PLAYBACK_INDEX;
+		chip->capture_index_offset = ATIHDMI_CAPTURE_INDEX;
+		break;
+	default:
+		chip->playback_streams = ICH6_NUM_PLAYBACK;
+		chip->capture_streams = ICH6_NUM_CAPTURE;
+		chip->playback_index_offset = ICH6_PLAYBACK_INDEX;
+		chip->capture_index_offset = ICH6_CAPTURE_INDEX;
+		break;
+	}
+	chip->num_streams = chip->playback_streams + chip->capture_streams;
+	chip->azx_dev = kcalloc(chip->num_streams, sizeof(*chip->azx_dev), GFP_KERNEL);
+	if (!chip->azx_dev) {
+		snd_printk(KERN_ERR "cannot malloc azx_dev\n");
+		goto errout;
+	}
+
+	/* allocate memory for the BDL for each stream */
+	if ((err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci),
+				       BDL_SIZE, &chip->bdl)) < 0) {
+		snd_printk(KERN_ERR SFX "cannot allocate BDL\n");
+		goto errout;
+	}
+	/* allocate memory for the position buffer */
+	if ((err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci),
+				       chip->num_streams * 8, &chip->posbuf)) < 0) {
+		snd_printk(KERN_ERR SFX "cannot allocate posbuf\n");
+		goto errout;
+	}
+	/* allocate CORB/RIRB */
+	if (! chip->single_cmd)
+		if ((err = azx_alloc_cmd_io(chip)) < 0)
+			goto errout;
+
+	/* initialize streams */
+	azx_init_stream(chip);
+
+	/* initialize chip */
+	azx_init_chip(chip);
+
+	chip->initialized = 1;
+
+	/* codec detection */
+	if (!chip->codec_mask) {
+		snd_printk(KERN_ERR SFX "no codecs found!\n");
+		err = -ENODEV;
+		goto errout;
+	}
+
+	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) <0) {
+		snd_printk(KERN_ERR SFX "Error creating device [card]!\n");
+		goto errout;
+	}
+
+	strcpy(card->driver, "HDA-Intel");
+	strcpy(card->shortname, driver_short_names[chip->driver_type]);
+	sprintf(card->longname, "%s at 0x%lx irq %i", card->shortname, chip->addr, chip->irq);
+
+	*rchip = chip;
+	return 0;
+
+ errout:
+	azx_free(chip);
+	return err;
+}
+
+static int __devinit azx_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
+{
+	struct snd_card *card;
+	struct azx *chip;
+	int err;
+
+	card = snd_card_new(index, id, THIS_MODULE, 0);
+	if (!card) {
+		snd_printk(KERN_ERR SFX "Error creating card!\n");
+		return -ENOMEM;
+	}
+
+	err = azx_create(card, pci, pci_id->driver_data, &chip);
+	if (err < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	card->private_data = chip;
+
+	/* create codec instances */
+	if ((err = azx_codec_create(chip, model)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	/* create PCM streams */
+	if ((err = azx_pcm_create(chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	/* create mixer controls */
+	if ((err = azx_mixer_create(chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	snd_card_set_dev(card, &pci->dev);
+
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	pci_set_drvdata(pci, card);
+
+	return err;
+}
+
+static void __devexit azx_remove(struct pci_dev *pci)
+{
+	snd_card_free(pci_get_drvdata(pci));
+	pci_set_drvdata(pci, NULL);
+}
+
+/* PCI IDs */
+static struct pci_device_id azx_ids[] = {
+	{ 0x8086, 0x2668, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH6 */
+	{ 0x8086, 0x27d8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH7 */
+	{ 0x8086, 0x269a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ESB2 */
+	{ 0x8086, 0x284b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH8 */
+	{ 0x8086, 0x293e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH9 */
+	{ 0x8086, 0x293f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH9 */
+	{ 0x1002, 0x437b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATI }, /* ATI SB450 */
+	{ 0x1002, 0x4383, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATI }, /* ATI SB600 */
+	{ 0x1002, 0x793b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RS600 HDMI */
+	{ 0x1002, 0x7919, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RS690 HDMI */
+	{ 0x1106, 0x3288, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_VIA }, /* VIA VT8251/VT8237A */
+	{ 0x1039, 0x7502, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_SIS }, /* SIS966 */
+	{ 0x10b9, 0x5461, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ULI }, /* ULI M5461 */
+	{ 0x10de, 0x026c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP51 */
+	{ 0x10de, 0x0371, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP55 */
+	{ 0x10de, 0x03e4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP61 */
+	{ 0x10de, 0x03f0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP61 */
+	{ 0x10de, 0x044a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP65 */
+	{ 0x10de, 0x044b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP65 */
+	{ 0x10de, 0x055c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP67 */
+	{ 0x10de, 0x055d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP67 */
+	{ 0, }
+};
+MODULE_DEVICE_TABLE(pci, azx_ids);
+
+/* pci_driver definition */
+static struct pci_driver driver = {
+	.name = "HDA Intel",
+	.id_table = azx_ids,
+	.probe = azx_probe,
+	.remove = __devexit_p(azx_remove),
+#ifdef CONFIG_PM
+	.suspend = azx_suspend,
+	.resume = azx_resume,
+#endif
+};
+
+static int __init alsa_card_azx_init(void)
+{
+	return pci_register_driver(&driver);
+}
+
+static void __exit alsa_card_azx_exit(void)
+{
+	pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_azx_init)
+module_exit(alsa_card_azx_exit)
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/hda/hda_local.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/hda/hda_local.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/hda/hda_local.h	(revision 91)
@@ -0,0 +1,269 @@
+/*
+ * Universal Interface for Intel High Definition Audio Codec
+ *
+ * Local helper functions
+ *
+ * Copyright (c) 2004 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.
+ */
+
+#ifndef __SOUND_HDA_LOCAL_H
+#define __SOUND_HDA_LOCAL_H
+
+/*
+ * for mixer controls
+ */
+#define HDA_COMPOSE_AMP_VAL(nid,chs,idx,dir) ((nid) | ((chs)<<16) | ((dir)<<18) | ((idx)<<19))
+/* mono volume with index (index=0,1,...) (channel=1,2) */
+#define HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \
+	{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx,  \
+	  .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
+	  	    SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
+	  	    SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, \
+	  .info = snd_hda_mixer_amp_volume_info, \
+	  .get = snd_hda_mixer_amp_volume_get, \
+	  .put = snd_hda_mixer_amp_volume_put, \
+	  .tlv = { .c = snd_hda_mixer_amp_tlv },		\
+	  .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, xindex, direction) }
+/* stereo volume with index */
+#define HDA_CODEC_VOLUME_IDX(xname, xcidx, nid, xindex, direction) \
+	HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, 3, xindex, direction)
+/* mono volume */
+#define HDA_CODEC_VOLUME_MONO(xname, nid, channel, xindex, direction) \
+	HDA_CODEC_VOLUME_MONO_IDX(xname, 0, nid, channel, xindex, direction)
+/* stereo volume */
+#define HDA_CODEC_VOLUME(xname, nid, xindex, direction) \
+	HDA_CODEC_VOLUME_MONO(xname, nid, 3, xindex, direction)
+/* mono mute switch with index (index=0,1,...) (channel=1,2) */
+#define HDA_CODEC_MUTE_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \
+	{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \
+	  .info = snd_hda_mixer_amp_switch_info, \
+	  .get = snd_hda_mixer_amp_switch_get, \
+	  .put = snd_hda_mixer_amp_switch_put, \
+	  .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, xindex, direction) }
+/* stereo mute switch with index */
+#define HDA_CODEC_MUTE_IDX(xname, xcidx, nid, xindex, direction) \
+	HDA_CODEC_MUTE_MONO_IDX(xname, xcidx, nid, 3, xindex, direction)
+/* mono mute switch */
+#define HDA_CODEC_MUTE_MONO(xname, nid, channel, xindex, direction) \
+	HDA_CODEC_MUTE_MONO_IDX(xname, 0, nid, channel, xindex, direction)
+/* stereo mute switch */
+#define HDA_CODEC_MUTE(xname, nid, xindex, direction) \
+	HDA_CODEC_MUTE_MONO(xname, nid, 3, xindex, direction)
+
+int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo);
+int snd_hda_mixer_amp_volume_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag, unsigned int size, unsigned int __user *tlv);
+int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo);
+int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+/* lowlevel accessor with caching; use carefully */
+int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch,
+                           int direction, int index);
+int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch,
+                             int direction, int idx, int mask, int val);
+
+/* mono switch binding multiple inputs */
+#define HDA_BIND_MUTE_MONO(xname, nid, channel, indices, direction) \
+	{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0,  \
+	  .info = snd_hda_mixer_amp_switch_info, \
+	  .get = snd_hda_mixer_bind_switch_get, \
+	  .put = snd_hda_mixer_bind_switch_put, \
+	  .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, indices, direction) }
+
+/* stereo switch binding multiple inputs */
+#define HDA_BIND_MUTE(xname,nid,indices,dir) HDA_BIND_MUTE_MONO(xname,nid,3,indices,dir)
+
+int snd_hda_mixer_bind_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+int snd_hda_mixer_bind_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+
+int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid);
+int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid);
+
+/*
+ * input MUX helper
+ */
+#define HDA_MAX_NUM_INPUTS	16
+struct hda_input_mux_item {
+	const char *label;
+	unsigned int index;
+};
+struct hda_input_mux {
+	unsigned int num_items;
+	struct hda_input_mux_item items[HDA_MAX_NUM_INPUTS];
+};
+
+int snd_hda_input_mux_info(const struct hda_input_mux *imux, struct snd_ctl_elem_info *uinfo);
+int snd_hda_input_mux_put(struct hda_codec *codec, const struct hda_input_mux *imux,
+			  struct snd_ctl_elem_value *ucontrol, hda_nid_t nid,
+			  unsigned int *cur_val);
+
+/*
+ * Channel mode helper
+ */
+struct hda_channel_mode {
+	int channels;
+	const struct hda_verb *sequence;
+};
+
+int snd_hda_ch_mode_info(struct hda_codec *codec, struct snd_ctl_elem_info *uinfo,
+			 const struct hda_channel_mode *chmode, int num_chmodes);
+int snd_hda_ch_mode_get(struct hda_codec *codec, struct snd_ctl_elem_value *ucontrol,
+			const struct hda_channel_mode *chmode, int num_chmodes,
+			int max_channels);
+int snd_hda_ch_mode_put(struct hda_codec *codec, struct snd_ctl_elem_value *ucontrol,
+			const struct hda_channel_mode *chmode, int num_chmodes,
+			int *max_channelsp);
+
+/*
+ * Multi-channel / digital-out PCM helper
+ */
+
+enum { HDA_FRONT, HDA_REAR, HDA_CLFE, HDA_SIDE }; /* index for dac_nidx */
+enum { HDA_DIG_NONE, HDA_DIG_EXCLUSIVE, HDA_DIG_ANALOG_DUP }; /* dig_out_used */
+
+struct hda_multi_out {
+	int num_dacs;		/* # of DACs, must be more than 1 */
+	hda_nid_t *dac_nids;	/* DAC list */
+        hda_nid_t hp_nid;	/* optional DAC for HP, 0 when not exists */
+        hda_nid_t extra_out_nid[3];     /* optional DACs, 0 when not exists */
+	hda_nid_t dig_out_nid;	/* digital out audio widget */
+	int max_channels;	/* currently supported analog channels */
+	int dig_out_used;	/* current usage of digital out (HDA_DIG_XXX) */
+};
+
+int snd_hda_multi_out_dig_open(struct hda_codec *codec, struct hda_multi_out *mout);
+int snd_hda_multi_out_dig_close(struct hda_codec *codec, struct hda_multi_out *mout);
+int snd_hda_multi_out_analog_open(struct hda_codec *codec, struct hda_multi_out *mout,
+				  struct snd_pcm_substream *substream);
+int snd_hda_multi_out_analog_prepare(struct hda_codec *codec, struct hda_multi_out *mout,
+				     unsigned int stream_tag,
+				     unsigned int format,
+				     struct snd_pcm_substream *substream);
+int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec, struct hda_multi_out *mout);
+
+/*
+ * generic codec parser
+ */
+int snd_hda_parse_generic_codec(struct hda_codec *codec);
+
+/*
+ * generic proc interface
+ */
+#ifdef CONFIG_PROC_FS
+int snd_hda_codec_proc_new(struct hda_codec *codec);
+#else
+static inline int snd_hda_codec_proc_new(struct hda_codec *codec) { return 0; }
+#endif
+
+/*
+ * Misc
+ */
+int snd_hda_check_board_config(struct hda_codec *codec, int num_configs,
+			       const char **modelnames,
+			       const struct snd_pci_quirk *pci_list);
+int snd_hda_add_new_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew);
+
+/*
+ * power management
+ */
+#ifdef CONFIG_PM
+int snd_hda_resume_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew);
+int snd_hda_resume_spdif_out(struct hda_codec *codec);
+int snd_hda_resume_spdif_in(struct hda_codec *codec);
+#endif
+
+/*
+ * unsolicited event handler
+ */
+
+#define HDA_UNSOL_QUEUE_SIZE	64
+
+struct hda_bus_unsolicited {
+	/* ring buffer */
+	u32 queue[HDA_UNSOL_QUEUE_SIZE * 2];
+	unsigned int rp, wp;
+
+	/* workqueue */
+	struct workqueue_struct *workq;
+	struct work_struct work;
+};
+
+/*
+ * Helper for automatic ping configuration
+ */
+
+enum {
+	AUTO_PIN_MIC,
+	AUTO_PIN_FRONT_MIC,
+	AUTO_PIN_LINE,
+	AUTO_PIN_FRONT_LINE,
+	AUTO_PIN_CD,
+	AUTO_PIN_AUX,
+	AUTO_PIN_LAST
+};
+
+extern const char *auto_pin_cfg_labels[AUTO_PIN_LAST];
+
+struct auto_pin_cfg {
+	int line_outs;
+        hda_nid_t line_out_pins[5]; /* sorted in the order of Front/Surr/CLFE/Side */
+        int speaker_outs;
+        hda_nid_t speaker_pins[5];
+	int hp_outs;
+	hda_nid_t hp_pins[5];
+	hda_nid_t input_pins[AUTO_PIN_LAST];
+	hda_nid_t dig_out_pin;
+	hda_nid_t dig_in_pin;
+};
+
+#define get_defcfg_connect(cfg) ((cfg & AC_DEFCFG_PORT_CONN) >> AC_DEFCFG_PORT_CONN_SHIFT)
+#define get_defcfg_association(cfg) ((cfg & AC_DEFCFG_DEF_ASSOC) >> AC_DEFCFG_ASSOC_SHIFT)
+#define get_defcfg_location(cfg) ((cfg & AC_DEFCFG_LOCATION) >> AC_DEFCFG_LOCATION_SHIFT)
+#define get_defcfg_sequence(cfg) (cfg & AC_DEFCFG_SEQUENCE)
+#define get_defcfg_device(cfg) ((cfg & AC_DEFCFG_DEVICE) >> AC_DEFCFG_DEVICE_SHIFT)
+
+int snd_hda_parse_pin_def_config(struct hda_codec *codec, struct auto_pin_cfg *cfg,
+				 hda_nid_t *ignore_nids);
+
+/* amp values */
+#define AMP_IN_MUTE(idx)	(0x7080 | ((idx)<<8))
+#define AMP_IN_UNMUTE(idx)	(0x7000 | ((idx)<<8))
+#define AMP_OUT_MUTE	0xb080
+#define AMP_OUT_UNMUTE	0xb000
+#define AMP_OUT_ZERO	0xb000
+/* pinctl values */
+#define PIN_IN		0x20
+#define PIN_VREF80	0x24
+#define PIN_VREF50	0x21
+#define PIN_OUT		0x40
+#define PIN_HP		0xc0
+#define PIN_HP_AMP	0x80
+
+/*
+ * get widget capabilities
+ */
+static inline u32 get_wcaps(struct hda_codec *codec, hda_nid_t nid)
+{
+	if (nid < codec->start_nid ||
+	    nid >= codec->start_nid + codec->num_nodes)
+		return snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP);
+	return codec->wcaps[nid - codec->start_nid];
+}
+
+
+#endif /* __SOUND_HDA_LOCAL_H */
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/hda/hda_patch.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/hda/hda_patch.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/hda/hda_patch.h	(revision 91)
@@ -0,0 +1,26 @@
+/*
+ * HDA Patches - included by hda_codec.c
+ */
+
+/* Realtek codecs */
+extern struct hda_codec_preset snd_hda_preset_realtek[];
+/* C-Media codecs */
+extern struct hda_codec_preset snd_hda_preset_cmedia[];
+/* Analog Devices codecs */
+extern struct hda_codec_preset snd_hda_preset_analog[];
+/* SigmaTel codecs */
+extern struct hda_codec_preset snd_hda_preset_sigmatel[];
+/* SiLabs 3054/3055 modem codecs */
+extern struct hda_codec_preset snd_hda_preset_si3054[];
+/* ATI HDMI codecs */
+extern struct hda_codec_preset snd_hda_preset_atihdmi[];
+
+static const struct hda_codec_preset *hda_preset_tables[] = {
+	snd_hda_preset_realtek,
+	snd_hda_preset_cmedia,
+	snd_hda_preset_analog,
+	snd_hda_preset_sigmatel,
+        snd_hda_preset_si3054,
+        snd_hda_preset_atihdmi,
+	NULL
+};
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/hda/hda_proc.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/hda/hda_proc.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/hda/hda_proc.c	(revision 91)
@@ -0,0 +1,324 @@
+/*
+ * Universal Interface for Intel High Definition Audio Codec
+ * 
+ * Generic proc interface
+ *
+ * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
+ *
+ *
+ *  This driver 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 driver is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <sound/core.h>
+#include "hda_codec.h"
+#include "hda_local.h"
+
+static const char *get_wid_type_name(unsigned int wid_value)
+{
+	static char *names[16] = {
+		[AC_WID_AUD_OUT] = "Audio Output",
+		[AC_WID_AUD_IN] = "Audio Input",
+		[AC_WID_AUD_MIX] = "Audio Mixer",
+		[AC_WID_AUD_SEL] = "Audio Selector",
+		[AC_WID_PIN] = "Pin Complex",
+		[AC_WID_POWER] = "Power Widget",
+		[AC_WID_VOL_KNB] = "Volume Knob Widget",
+		[AC_WID_BEEP] = "Beep Generator Widget",
+		[AC_WID_VENDOR] = "Vendor Defined Widget",
+	};
+	wid_value &= 0xf;
+	if (names[wid_value])
+		return names[wid_value];
+	else
+		return "UNKOWN Widget";
+}
+
+static void print_amp_caps(struct snd_info_buffer *buffer,
+			   struct hda_codec *codec, hda_nid_t nid, int dir)
+{
+	unsigned int caps;
+	if (dir == HDA_OUTPUT)
+		caps = snd_hda_param_read(codec, nid, AC_PAR_AMP_OUT_CAP);
+	else
+		caps = snd_hda_param_read(codec, nid, AC_PAR_AMP_IN_CAP);
+	if (caps == -1 || caps == 0) {
+		snd_iprintf(buffer, "N/A\n");
+		return;
+	}
+	snd_iprintf(buffer, "ofs=0x%02x, nsteps=0x%02x, stepsize=0x%02x, mute=%x\n",
+		    caps & AC_AMPCAP_OFFSET,
+		    (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT,
+		    (caps & AC_AMPCAP_STEP_SIZE) >> AC_AMPCAP_STEP_SIZE_SHIFT,
+		    (caps & AC_AMPCAP_MUTE) >> AC_AMPCAP_MUTE_SHIFT);
+}
+
+static void print_amp_vals(struct snd_info_buffer *buffer,
+			   struct hda_codec *codec, hda_nid_t nid,
+			   int dir, int stereo, int indices)
+{
+	unsigned int val;
+	int i;
+
+	if (dir == HDA_OUTPUT)
+		dir = AC_AMP_GET_OUTPUT;
+	else
+		dir = AC_AMP_GET_INPUT;
+	for (i = 0; i < indices; i++) {
+		snd_iprintf(buffer, " [");
+		if (stereo) {
+			val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_AMP_GAIN_MUTE,
+						 AC_AMP_GET_LEFT | dir | i);
+			snd_iprintf(buffer, "0x%02x ", val);
+		}
+		val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_AMP_GAIN_MUTE,
+					 AC_AMP_GET_RIGHT | dir | i);
+		snd_iprintf(buffer, "0x%02x]", val);
+	}
+	snd_iprintf(buffer, "\n");
+}
+
+static void print_pcm_caps(struct snd_info_buffer *buffer,
+			   struct hda_codec *codec, hda_nid_t nid)
+{
+	unsigned int pcm = snd_hda_param_read(codec, nid, AC_PAR_PCM);
+	unsigned int stream = snd_hda_param_read(codec, nid, AC_PAR_STREAM);
+	if (pcm == -1 || stream == -1) {
+		snd_iprintf(buffer, "N/A\n");
+		return;
+	}
+	snd_iprintf(buffer, "rates 0x%03x, bits 0x%02x, types 0x%x\n",
+		    pcm & AC_SUPPCM_RATES, (pcm >> 16) & 0xff, stream & 0xf);
+}
+
+static const char *get_jack_location(u32 cfg)
+{
+	static char *bases[7] = {
+		"N/A", "Rear", "Front", "Left", "Right", "Top", "Bottom",
+	};
+	static unsigned char specials_idx[] = {
+		0x07, 0x08,
+		0x17, 0x18, 0x19,
+		0x37, 0x38
+	};
+	static char *specials[] = {
+		"Rear Panel", "Drive Bar",
+		"Riser", "HDMI", "ATAPI",
+		"Mobile-In", "Mobile-Out"
+	};
+	int i;
+	cfg = (cfg & AC_DEFCFG_LOCATION) >> AC_DEFCFG_LOCATION_SHIFT;
+	if ((cfg & 0x0f) < 7)
+		return bases[cfg & 0x0f];
+	for (i = 0; i < ARRAY_SIZE(specials_idx); i++) {
+		if (cfg == specials_idx[i])
+			return specials[i];
+	}
+	return "UNKNOWN";
+}
+
+static const char *get_jack_connection(u32 cfg)
+{
+	static char *names[16] = {
+		"Unknown", "1/8", "1/4", "ATAPI",
+		"RCA", "Optical","Digital", "Analog",
+		"DIN", "XLR", "RJ11", "Comb",
+		NULL, NULL, NULL, "Other"
+	};
+	cfg = (cfg & AC_DEFCFG_CONN_TYPE) >> AC_DEFCFG_CONN_TYPE_SHIFT;
+	if (names[cfg])
+		return names[cfg];
+	else
+		return "UNKNOWN";
+}
+
+static const char *get_jack_color(u32 cfg)
+{
+	static char *names[16] = {
+		"Unknown", "Black", "Grey", "Blue",
+		"Green", "Red", "Orange", "Yellow",
+		"Purple", "Pink", NULL, NULL,
+		NULL, NULL, "White", "Other",
+	};
+	cfg = (cfg & AC_DEFCFG_COLOR) >> AC_DEFCFG_COLOR_SHIFT;
+	if (names[cfg])
+		return names[cfg];
+	else
+		return "UNKNOWN";
+}
+
+static void print_pin_caps(struct snd_info_buffer *buffer,
+			   struct hda_codec *codec, hda_nid_t nid)
+{
+	static char *jack_conns[4] = { "Jack", "N/A", "Fixed", "Both" };
+	static char *jack_types[16] = {
+		"Line Out", "Speaker", "HP Out", "CD",
+		"SPDIF Out", "Digital Out", "Modem Line", "Modem Hand",
+		"Line In", "Aux", "Mic", "Telephony",
+		"SPDIF In", "Digitial In", "Reserved", "Other"
+	};
+	static char *jack_locations[4] = { "Ext", "Int", "Sep", "Oth" };
+	unsigned int caps;
+
+	caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
+	snd_iprintf(buffer, "  Pincap 0x08%x:", caps);
+	if (caps & AC_PINCAP_IN)
+		snd_iprintf(buffer, " IN");
+	if (caps & AC_PINCAP_OUT)
+		snd_iprintf(buffer, " OUT");
+	if (caps & AC_PINCAP_HP_DRV)
+		snd_iprintf(buffer, " HP");
+	snd_iprintf(buffer, "\n");
+	caps = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONFIG_DEFAULT, 0);
+	snd_iprintf(buffer, "  Pin Default 0x%08x: [%s] %s at %s %s\n", caps,
+		    jack_conns[(caps & AC_DEFCFG_PORT_CONN) >> AC_DEFCFG_PORT_CONN_SHIFT],
+		    jack_types[(caps & AC_DEFCFG_DEVICE) >> AC_DEFCFG_DEVICE_SHIFT],
+		    jack_locations[(caps >> (AC_DEFCFG_LOCATION_SHIFT + 4)) & 3],
+		    get_jack_location(caps));
+	snd_iprintf(buffer, "    Conn = %s, Color = %s\n",
+		    get_jack_connection(caps),
+		    get_jack_color(caps));
+}
+
+
+static void print_codec_info(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
+{
+	struct hda_codec *codec = entry->private_data;
+	char buf[32];
+	hda_nid_t nid;
+	int i, nodes;
+
+	snd_hda_get_codec_name(codec, buf, sizeof(buf));
+	snd_iprintf(buffer, "Codec: %s\n", buf);
+	snd_iprintf(buffer, "Address: %d\n", codec->addr);
+	snd_iprintf(buffer, "Vendor Id: 0x%x\n", codec->vendor_id);
+	snd_iprintf(buffer, "Subsystem Id: 0x%x\n", codec->subsystem_id);
+	snd_iprintf(buffer, "Revision Id: 0x%x\n", codec->revision_id);
+	if (! codec->afg)
+		return;
+	snd_iprintf(buffer, "Default PCM: ");
+	print_pcm_caps(buffer, codec, codec->afg);
+	snd_iprintf(buffer, "Default Amp-In caps: ");
+	print_amp_caps(buffer, codec, codec->afg, HDA_INPUT);
+	snd_iprintf(buffer, "Default Amp-Out caps: ");
+	print_amp_caps(buffer, codec, codec->afg, HDA_OUTPUT);
+
+	nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid);
+	if (! nid || nodes < 0) {
+		snd_iprintf(buffer, "Invalid AFG subtree\n");
+		return;
+	}
+	for (i = 0; i < nodes; i++, nid++) {
+		unsigned int wid_caps = snd_hda_param_read(codec, nid,
+							   AC_PAR_AUDIO_WIDGET_CAP);
+		unsigned int wid_type = (wid_caps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
+		int conn_len = 0; 
+		hda_nid_t conn[HDA_MAX_CONNECTIONS];
+
+		snd_iprintf(buffer, "Node 0x%02x [%s] wcaps 0x%x:", nid,
+			    get_wid_type_name(wid_type), wid_caps);
+		if (wid_caps & AC_WCAP_STEREO)
+			snd_iprintf(buffer, " Stereo");
+		else
+			snd_iprintf(buffer, " Mono");
+		if (wid_caps & AC_WCAP_DIGITAL)
+			snd_iprintf(buffer, " Digital");
+		if (wid_caps & AC_WCAP_IN_AMP)
+			snd_iprintf(buffer, " Amp-In");
+		if (wid_caps & AC_WCAP_OUT_AMP)
+			snd_iprintf(buffer, " Amp-Out");
+		snd_iprintf(buffer, "\n");
+
+		if (wid_caps & AC_WCAP_CONN_LIST)
+			conn_len = snd_hda_get_connections(codec, nid, conn,
+							   HDA_MAX_CONNECTIONS);
+
+		if (wid_caps & AC_WCAP_IN_AMP) {
+			snd_iprintf(buffer, "  Amp-In caps: ");
+			print_amp_caps(buffer, codec, nid, HDA_INPUT);
+			snd_iprintf(buffer, "  Amp-In vals: ");
+			print_amp_vals(buffer, codec, nid, HDA_INPUT,
+				       wid_caps & AC_WCAP_STEREO, conn_len);
+		}
+		if (wid_caps & AC_WCAP_OUT_AMP) {
+			snd_iprintf(buffer, "  Amp-Out caps: ");
+			print_amp_caps(buffer, codec, nid, HDA_OUTPUT);
+			snd_iprintf(buffer, "  Amp-Out vals: ");
+			print_amp_vals(buffer, codec, nid, HDA_OUTPUT,
+				       wid_caps & AC_WCAP_STEREO, 1);
+		}
+
+		if (wid_type == AC_WID_PIN) {
+			unsigned int pinctls;
+			print_pin_caps(buffer, codec, nid);
+			pinctls = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+			snd_iprintf(buffer, "  Pin-ctls: 0x%02x:", pinctls);
+			if (pinctls & AC_PINCTL_IN_EN)
+				snd_iprintf(buffer, " IN");
+			if (pinctls & AC_PINCTL_OUT_EN)
+				snd_iprintf(buffer, " OUT");
+			if (pinctls & AC_PINCTL_HP_EN)
+				snd_iprintf(buffer, " HP");
+			snd_iprintf(buffer, "\n");
+		}
+
+		if ((wid_type == AC_WID_AUD_OUT || wid_type == AC_WID_AUD_IN) &&
+		    (wid_caps & AC_WCAP_FORMAT_OVRD)) {
+			snd_iprintf(buffer, "  PCM: ");
+			print_pcm_caps(buffer, codec, nid);
+		}
+
+		if (wid_caps & AC_WCAP_POWER)
+			snd_iprintf(buffer, "  Power: 0x%x\n",
+				    snd_hda_codec_read(codec, nid, 0,
+						       AC_VERB_GET_POWER_STATE, 0));
+
+		if (wid_caps & AC_WCAP_CONN_LIST) {
+			int c, curr = -1;
+			if (conn_len > 1 && wid_type != AC_WID_AUD_MIX)
+				curr = snd_hda_codec_read(codec, nid, 0,
+					AC_VERB_GET_CONNECT_SEL, 0);
+			snd_iprintf(buffer, "  Connection: %d\n", conn_len);
+			snd_iprintf(buffer, "    ");
+			for (c = 0; c < conn_len; c++) {
+				snd_iprintf(buffer, " 0x%02x", conn[c]);
+				if (c == curr)
+					snd_iprintf(buffer, "*");
+			}
+			snd_iprintf(buffer, "\n");
+		}
+	}
+}
+
+/*
+ * create a proc read
+ */
+int snd_hda_codec_proc_new(struct hda_codec *codec)
+{
+	char name[32];
+	struct snd_info_entry *entry;
+	int err;
+
+	sprintf(name, "codec#%d", codec->addr);
+	err = snd_card_proc_new(codec->bus->card, name, &entry);
+	if (err < 0)
+		return err;
+
+	snd_info_set_text_ops(entry, codec, 32 * 1024, print_codec_info);
+	return 0;
+}
+
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/hda/makefile.os2
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/hda/makefile.os2	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/hda/makefile.os2	(revision 91)
@@ -0,0 +1,51 @@
+CINCLUDES = ..\..\..\include;..\..\include;$(%WATCOM)\H
+AINCLUDES = $(DDK_BASE)\H
+
+CDEFINES = -D__KERNEL__ -DMODULE -dTARGET_OS2 -dALSA_BUILD -D__ISAPNP__ -DCONFIG_ISAPNP -D__i386_
+
+32BIT=1
+!include ..\..\..\..\makefile.inc
+
+
+#===================================================================
+#
+#   List of source files
+#
+#===================================================================
+FILE1    = hda_intel.obj hda_codec.obj hda_generic.obj patch_realtek.obj 
+FILE2    = patch_cmedia.obj hda_proc.obj patch_analog.obj patch_sigmatel.obj
+FILE3    = patch_si3054.obj patch_atihdmi.obj patch_conexant.obj
+FILE4    = patch_via.obj
+FILE5    = 
+FILE6    = 
+FILE7    = 
+FILE8    = 
+FILELAST =
+FILES    = $(FILE0) $(FILE1) $(FILE2) $(FILE3) $(FILE4) $(FILE5) $(FILE6) $(FILE7) $(FILE8) $(FILE9) $(FILE10) $(FILE11) $(FILE12)
+
+TARGET   = hda
+
+LNKFILE  = $(OBJDIR)\$(TARGET).lnk
+
+
+#===================================================================
+#
+#   Specific dependencies
+#
+#===================================================================
+all: $(OBJDIR)\$(TARGET).lib 
+
+
+$(LNKFILE): makefile.os2
+    @%create $(LNKFILE)
+    @for %f in ($(FILES)) do @%append $(LNKFILE) -+$(OBJDIR)\%f
+
+$(OBJDIR)\$(TARGET).lib: $(LNKFILE) $(FILES)
+    $(LIB) $(OBJDIR)\$(TARGET).lib @$(LNKFILE)
+    copy $(OBJDIR)\$(TARGET).lib $(ALSA_LIB) 
+
+clean: .SYMBOLIC
+    -@rm  *.err 2>nul
+    @cd $(OBJDIR)
+    -rm *.obj *.lnk *.sys *.sym *.lst *.map *.wmap *.lib 2>nul
+    @cd ..\..
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/hda/patch_analog.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/hda/patch_analog.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/hda/patch_analog.c	(revision 91)
@@ -0,0 +1,2738 @@
+/*
+ * HD audio interface patch for AD1981HD, AD1983, AD1986A, AD1988
+ *
+ * Copyright (c) 2005 Takashi Iwai <tiwai@suse.de>
+ *
+ *  This driver 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 driver is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/mutex.h>
+
+#include <sound/core.h>
+#include "hda_codec.h"
+#include "hda_local.h"
+
+struct ad198x_spec {
+	struct snd_kcontrol_new *mixers[5];
+	int num_mixers;
+
+	const struct hda_verb *init_verbs[5];	/* initialization verbs
+						 * don't forget NULL termination!
+						 */
+	unsigned int num_init_verbs;
+
+	/* playback */
+	struct hda_multi_out multiout;	/* playback set-up
+					 * max_channels, dacs must be set
+					 * dig_out_nid and hp_nid are optional
+					 */
+	unsigned int cur_eapd;
+	unsigned int need_dac_fix;
+
+	/* capture */
+	unsigned int num_adc_nids;
+	hda_nid_t *adc_nids;
+	hda_nid_t dig_in_nid;		/* digital-in NID; optional */
+
+	/* capture source */
+	const struct hda_input_mux *input_mux;
+	hda_nid_t *capsrc_nids;
+	unsigned int cur_mux[3];
+
+	/* channel model */
+	const struct hda_channel_mode *channel_mode;
+	int num_channel_mode;
+
+	/* PCM information */
+	struct hda_pcm pcm_rec[2];	/* used in alc_build_pcms() */
+
+	struct mutex amp_mutex;	/* PCM volume/mute control mutex */
+	unsigned int spdif_route;
+
+	/* dynamic controls, init_verbs and input_mux */
+	struct auto_pin_cfg autocfg;
+	unsigned int num_kctl_alloc, num_kctl_used;
+	struct snd_kcontrol_new *kctl_alloc;
+	struct hda_input_mux private_imux;
+	hda_nid_t private_dac_nids[4];
+};
+
+/*
+ * input MUX handling (common part)
+ */
+static int ad198x_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct ad198x_spec *spec = codec->spec;
+
+	return snd_hda_input_mux_info(spec->input_mux, uinfo);
+}
+
+static int ad198x_mux_enum_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct ad198x_spec *spec = codec->spec;
+	unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+
+	ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
+	return 0;
+}
+
+static int ad198x_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct ad198x_spec *spec = codec->spec;
+	unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+
+	return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
+				     spec->capsrc_nids[adc_idx],
+				     &spec->cur_mux[adc_idx]);
+}
+
+/*
+ * initialization (common callbacks)
+ */
+static int ad198x_init(struct hda_codec *codec)
+{
+	struct ad198x_spec *spec = codec->spec;
+	int i;
+
+	for (i = 0; i < spec->num_init_verbs; i++)
+		snd_hda_sequence_write(codec, spec->init_verbs[i]);
+	return 0;
+}
+
+static int ad198x_build_controls(struct hda_codec *codec)
+{
+	struct ad198x_spec *spec = codec->spec;
+	unsigned int i;
+	int err;
+
+	for (i = 0; i < spec->num_mixers; i++) {
+		err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
+		if (err < 0)
+			return err;
+	}
+	if (spec->multiout.dig_out_nid) {
+		err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
+		if (err < 0)
+			return err;
+	} 
+	if (spec->dig_in_nid) {
+		err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
+		if (err < 0)
+			return err;
+	}
+	return 0;
+}
+
+/*
+ * Analog playback callbacks
+ */
+static int ad198x_playback_pcm_open(struct hda_pcm_stream *hinfo,
+				    struct hda_codec *codec,
+				    struct snd_pcm_substream *substream)
+{
+	struct ad198x_spec *spec = codec->spec;
+	return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream);
+}
+
+static int ad198x_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+				       struct hda_codec *codec,
+				       unsigned int stream_tag,
+				       unsigned int format,
+				       struct snd_pcm_substream *substream)
+{
+	struct ad198x_spec *spec = codec->spec;
+	return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
+						format, substream);
+}
+
+static int ad198x_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+				       struct hda_codec *codec,
+				       struct snd_pcm_substream *substream)
+{
+	struct ad198x_spec *spec = codec->spec;
+	return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
+}
+
+/*
+ * Digital out
+ */
+static int ad198x_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
+					struct hda_codec *codec,
+					struct snd_pcm_substream *substream)
+{
+	struct ad198x_spec *spec = codec->spec;
+	return snd_hda_multi_out_dig_open(codec, &spec->multiout);
+}
+
+static int ad198x_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
+					 struct hda_codec *codec,
+					 struct snd_pcm_substream *substream)
+{
+	struct ad198x_spec *spec = codec->spec;
+	return snd_hda_multi_out_dig_close(codec, &spec->multiout);
+}
+
+/*
+ * Analog capture
+ */
+static int ad198x_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
+				      struct hda_codec *codec,
+				      unsigned int stream_tag,
+				      unsigned int format,
+				      struct snd_pcm_substream *substream)
+{
+	struct ad198x_spec *spec = codec->spec;
+	snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
+				   stream_tag, 0, format);
+	return 0;
+}
+
+static int ad198x_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+				      struct hda_codec *codec,
+				      struct snd_pcm_substream *substream)
+{
+	struct ad198x_spec *spec = codec->spec;
+	snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
+				   0, 0, 0);
+	return 0;
+}
+
+
+/*
+ */
+static struct hda_pcm_stream ad198x_pcm_analog_playback = {
+	.substreams = 1,
+	.channels_min = 2,
+	.channels_max = 6, /* changed later */
+	.nid = 0, /* fill later */
+	.ops = {
+		.open = ad198x_playback_pcm_open,
+		.prepare = ad198x_playback_pcm_prepare,
+		.cleanup = ad198x_playback_pcm_cleanup
+	},
+};
+
+static struct hda_pcm_stream ad198x_pcm_analog_capture = {
+	.substreams = 1,
+	.channels_min = 2,
+	.channels_max = 2,
+	.nid = 0, /* fill later */
+	.ops = {
+		.prepare = ad198x_capture_pcm_prepare,
+		.cleanup = ad198x_capture_pcm_cleanup
+	},
+};
+
+static struct hda_pcm_stream ad198x_pcm_digital_playback = {
+	.substreams = 1,
+	.channels_min = 2,
+	.channels_max = 2,
+	.nid = 0, /* fill later */
+	.ops = {
+		.open = ad198x_dig_playback_pcm_open,
+		.close = ad198x_dig_playback_pcm_close
+	},
+};
+
+static struct hda_pcm_stream ad198x_pcm_digital_capture = {
+	.substreams = 1,
+	.channels_min = 2,
+	.channels_max = 2,
+	/* NID is set in alc_build_pcms */
+};
+
+static int ad198x_build_pcms(struct hda_codec *codec)
+{
+	struct ad198x_spec *spec = codec->spec;
+	struct hda_pcm *info = spec->pcm_rec;
+
+	codec->num_pcms = 1;
+	codec->pcm_info = info;
+
+	info->name = "AD198x Analog";
+	info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad198x_pcm_analog_playback;
+	info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = spec->multiout.max_channels;
+	info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0];
+	info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad198x_pcm_analog_capture;
+	info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_adc_nids;
+	info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
+
+	if (spec->multiout.dig_out_nid) {
+		info++;
+		codec->num_pcms++;
+		info->name = "AD198x Digital";
+		info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad198x_pcm_digital_playback;
+		info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid;
+		if (spec->dig_in_nid) {
+			info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad198x_pcm_digital_capture;
+			info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid;
+		}
+	}
+
+	return 0;
+}
+
+static void ad198x_free(struct hda_codec *codec)
+{
+	struct ad198x_spec *spec = codec->spec;
+	unsigned int i;
+
+	if (spec->kctl_alloc) {
+		for (i = 0; i < spec->num_kctl_used; i++)
+			kfree(spec->kctl_alloc[i].name);
+		kfree(spec->kctl_alloc);
+	}
+	kfree(codec->spec);
+}
+
+#ifdef CONFIG_PM
+static int ad198x_resume(struct hda_codec *codec)
+{
+	struct ad198x_spec *spec = codec->spec;
+	int i;
+
+	codec->patch_ops.init(codec);
+	for (i = 0; i < spec->num_mixers; i++)
+		snd_hda_resume_ctls(codec, spec->mixers[i]);
+	if (spec->multiout.dig_out_nid)
+		snd_hda_resume_spdif_out(codec);
+	if (spec->dig_in_nid)
+		snd_hda_resume_spdif_in(codec);
+	return 0;
+}
+#endif
+
+static struct hda_codec_ops ad198x_patch_ops = {
+	.build_controls = ad198x_build_controls,
+	.build_pcms = ad198x_build_pcms,
+	.init = ad198x_init,
+	.free = ad198x_free,
+#ifdef CONFIG_PM
+	.resume = ad198x_resume,
+#endif
+};
+
+
+/*
+ * EAPD control
+ * the private value = nid | (invert << 8)
+ */
+static int ad198x_eapd_info(struct snd_kcontrol *kcontrol,
+			    struct snd_ctl_elem_info *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 ad198x_eapd_get(struct snd_kcontrol *kcontrol,
+			   struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct ad198x_spec *spec = codec->spec;
+	int invert = (kcontrol->private_value >> 8) & 1;
+	if (invert)
+		ucontrol->value.integer.value[0] = ! spec->cur_eapd;
+	else
+		ucontrol->value.integer.value[0] = spec->cur_eapd;
+	return 0;
+}
+
+static int ad198x_eapd_put(struct snd_kcontrol *kcontrol,
+			   struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct ad198x_spec *spec = codec->spec;
+	int invert = (kcontrol->private_value >> 8) & 1;
+	hda_nid_t nid = kcontrol->private_value & 0xff;
+	unsigned int eapd;
+	eapd = ucontrol->value.integer.value[0];
+	if (invert)
+		eapd = !eapd;
+	if (eapd == spec->cur_eapd && ! codec->in_resume)
+		return 0;
+	spec->cur_eapd = eapd;
+	snd_hda_codec_write(codec, nid,
+			    0, AC_VERB_SET_EAPD_BTLENABLE,
+			    eapd ? 0x02 : 0x00);
+	return 1;
+}
+
+static int ad198x_ch_mode_info(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_info *uinfo);
+static int ad198x_ch_mode_get(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol);
+static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol);
+
+
+/*
+ * AD1986A specific
+ */
+
+#define AD1986A_SPDIF_OUT	0x02
+#define AD1986A_FRONT_DAC	0x03
+#define AD1986A_SURR_DAC	0x04
+#define AD1986A_CLFE_DAC	0x05
+#define AD1986A_ADC		0x06
+
+static hda_nid_t ad1986a_dac_nids[3] = {
+	AD1986A_FRONT_DAC, AD1986A_SURR_DAC, AD1986A_CLFE_DAC
+};
+static hda_nid_t ad1986a_adc_nids[1] = { AD1986A_ADC };
+static hda_nid_t ad1986a_capsrc_nids[1] = { 0x12 };
+
+static struct hda_input_mux ad1986a_capture_source = {
+	.num_items = 7,
+	.items = {
+		{ "Mic", 0x0 },
+		{ "CD", 0x1 },
+		{ "Aux", 0x3 },
+		{ "Line", 0x4 },
+		{ "Mix", 0x5 },
+		{ "Mono", 0x6 },
+		{ "Phone", 0x7 },
+	},
+};
+
+/*
+ * PCM control
+ *
+ * bind volumes/mutes of 3 DACs as a single PCM control for simplicity
+ */
+
+#define ad1986a_pcm_amp_vol_info	snd_hda_mixer_amp_volume_info
+
+static int ad1986a_pcm_amp_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct ad198x_spec *ad = codec->spec;
+
+	mutex_lock(&ad->amp_mutex);
+	snd_hda_mixer_amp_volume_get(kcontrol, ucontrol);
+	mutex_unlock(&ad->amp_mutex);
+	return 0;
+}
+
+static int ad1986a_pcm_amp_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct ad198x_spec *ad = codec->spec;
+	int i, change = 0;
+
+	mutex_lock(&ad->amp_mutex);
+	for (i = 0; i < ARRAY_SIZE(ad1986a_dac_nids); i++) {
+		kcontrol->private_value = HDA_COMPOSE_AMP_VAL(ad1986a_dac_nids[i], 3, 0, HDA_OUTPUT);
+		change |= snd_hda_mixer_amp_volume_put(kcontrol, ucontrol);
+	}
+	kcontrol->private_value = HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT);
+	mutex_unlock(&ad->amp_mutex);
+	return change;
+}
+
+#define ad1986a_pcm_amp_sw_info		snd_hda_mixer_amp_switch_info
+
+static int ad1986a_pcm_amp_sw_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct ad198x_spec *ad = codec->spec;
+
+	mutex_lock(&ad->amp_mutex);
+	snd_hda_mixer_amp_switch_get(kcontrol, ucontrol);
+	mutex_unlock(&ad->amp_mutex);
+	return 0;
+}
+
+static int ad1986a_pcm_amp_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct ad198x_spec *ad = codec->spec;
+	int i, change = 0;
+
+	mutex_lock(&ad->amp_mutex);
+	for (i = 0; i < ARRAY_SIZE(ad1986a_dac_nids); i++) {
+		kcontrol->private_value = HDA_COMPOSE_AMP_VAL(ad1986a_dac_nids[i], 3, 0, HDA_OUTPUT);
+		change |= snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
+	}
+	kcontrol->private_value = HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT);
+	mutex_unlock(&ad->amp_mutex);
+	return change;
+}
+
+/*
+ * mixers
+ */
+static struct snd_kcontrol_new ad1986a_mixers[] = {
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "PCM Playback Volume",
+		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+			  SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+			  SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK,
+		.info = ad1986a_pcm_amp_vol_info,
+		.get = ad1986a_pcm_amp_vol_get,
+		.put = ad1986a_pcm_amp_vol_put,
+		.tlv = { .c = snd_hda_mixer_amp_tlv },
+		.private_value = HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT)
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "PCM Playback Switch",
+		.info = ad1986a_pcm_amp_sw_info,
+		.get = ad1986a_pcm_amp_sw_get,
+		.put = ad1986a_pcm_amp_sw_put,
+		.private_value = HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT)
+	},
+	HDA_CODEC_VOLUME("Front Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Surround Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Surround Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x1d, 1, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x1d, 2, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x1d, 1, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x1d, 2, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Headphone Playback Volume", 0x1a, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Aux Playback Volume", 0x16, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Aux Playback Switch", 0x16, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x18, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x18, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Capture Source",
+		.info = ad198x_mux_enum_info,
+		.get = ad198x_mux_enum_get,
+		.put = ad198x_mux_enum_put,
+	},
+	HDA_CODEC_MUTE("Stereo Downmix Switch", 0x09, 0x0, HDA_OUTPUT),
+	{0} /* end */
+};
+
+/* additional mixers for 3stack mode */
+static struct snd_kcontrol_new ad1986a_3st_mixers[] = {
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Channel Mode",
+		.info = ad198x_ch_mode_info,
+		.get = ad198x_ch_mode_get,
+		.put = ad198x_ch_mode_put,
+	},
+	{0} /* end */
+};
+
+/* laptop model - 2ch only */
+static hda_nid_t ad1986a_laptop_dac_nids[1] = { AD1986A_FRONT_DAC };
+
+static struct snd_kcontrol_new ad1986a_laptop_mixers[] = {
+	HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Master Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Master Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
+	/* HDA_CODEC_VOLUME("Headphone Playback Volume", 0x1a, 0x0, HDA_OUTPUT),
+	   HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x0, HDA_OUTPUT), */
+	HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Aux Playback Volume", 0x16, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Aux Playback Switch", 0x16, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
+	/* HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x18, 0x0, HDA_OUTPUT),
+	   HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x18, 0x0, HDA_OUTPUT),
+	   HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
+	   HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT), */
+	HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Capture Source",
+		.info = ad198x_mux_enum_info,
+		.get = ad198x_mux_enum_get,
+		.put = ad198x_mux_enum_put,
+	},
+	{0} /* end */
+};
+
+/* laptop-eapd model - 2ch only */
+
+/* master controls both pins 0x1a and 0x1b */
+static int ad1986a_laptop_master_vol_put(struct snd_kcontrol *kcontrol,
+					 struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	long *valp = ucontrol->value.integer.value;
+	int change;
+
+	change = snd_hda_codec_amp_update(codec, 0x1a, 0, HDA_OUTPUT, 0,
+					  0x7f, valp[0] & 0x7f);
+	change |= snd_hda_codec_amp_update(codec, 0x1a, 1, HDA_OUTPUT, 0,
+					   0x7f, valp[1] & 0x7f);
+	snd_hda_codec_amp_update(codec, 0x1b, 0, HDA_OUTPUT, 0,
+				 0x7f, valp[0] & 0x7f);
+	snd_hda_codec_amp_update(codec, 0x1b, 1, HDA_OUTPUT, 0,
+				 0x7f, valp[1] & 0x7f);
+	return change;
+}
+
+static int ad1986a_laptop_master_sw_put(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	long *valp = ucontrol->value.integer.value;
+	int change;
+
+	change = snd_hda_codec_amp_update(codec, 0x1a, 0, HDA_OUTPUT, 0,
+					  0x80, valp[0] ? 0 : 0x80);
+	change |= snd_hda_codec_amp_update(codec, 0x1a, 1, HDA_OUTPUT, 0,
+					   0x80, valp[1] ? 0 : 0x80);
+	snd_hda_codec_amp_update(codec, 0x1b, 0, HDA_OUTPUT, 0,
+				 0x80, valp[0] ? 0 : 0x80);
+	snd_hda_codec_amp_update(codec, 0x1b, 1, HDA_OUTPUT, 0,
+				 0x80, valp[1] ? 0 : 0x80);
+	return change;
+}
+
+static struct hda_input_mux ad1986a_laptop_eapd_capture_source = {
+	.num_items = 3,
+	.items = {
+		{ "Mic", 0x0 },
+		{ "Internal Mic", 0x4 },
+		{ "Mix", 0x5 },
+	},
+};
+
+static struct snd_kcontrol_new ad1986a_laptop_eapd_mixers[] = {
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Master Playback Volume",
+		.info = snd_hda_mixer_amp_volume_info,
+		.get = snd_hda_mixer_amp_volume_get,
+		.put = ad1986a_laptop_master_vol_put,
+		.tlv = { .c = snd_hda_mixer_amp_tlv },
+		.private_value = HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Master Playback Switch",
+		.info = snd_hda_mixer_amp_switch_info,
+		.get = snd_hda_mixer_amp_switch_get,
+		.put = ad1986a_laptop_master_sw_put,
+		.private_value = HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
+	},
+	HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x17, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x17, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Capture Source",
+		.info = ad198x_mux_enum_info,
+		.get = ad198x_mux_enum_get,
+		.put = ad198x_mux_enum_put,
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "External Amplifier",
+		.info = ad198x_eapd_info,
+		.get = ad198x_eapd_get,
+		.put = ad198x_eapd_put,
+		.private_value = 0x1b | (1 << 8), /* port-D, inversed */
+	},
+	{0} /* end */
+};
+
+/*
+ * initialization verbs
+ */
+static struct hda_verb ad1986a_init_verbs[] = {
+	/* Front, Surround, CLFE DAC; mute as default */
+	{0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+	{0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+	{0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+	/* Downmix - off */
+	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+	/* HP, Line-Out, Surround, CLFE selectors */
+	{0x0a, AC_VERB_SET_CONNECT_SEL, 0x0},
+	{0x0b, AC_VERB_SET_CONNECT_SEL, 0x0},
+	{0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
+	{0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
+	/* Mono selector */
+	{0x0e, AC_VERB_SET_CONNECT_SEL, 0x0},
+	/* Mic selector: Mic 1/2 pin */
+	{0x0f, AC_VERB_SET_CONNECT_SEL, 0x0},
+	/* Line-in selector: Line-in */
+	{0x10, AC_VERB_SET_CONNECT_SEL, 0x0},
+	/* Mic 1/2 swap */
+	{0x11, AC_VERB_SET_CONNECT_SEL, 0x0},
+	/* Record selector: mic */
+	{0x12, AC_VERB_SET_CONNECT_SEL, 0x0},
+	/* Mic, Phone, CD, Aux, Line-In amp; mute as default */
+	{0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+	/* PC beep */
+	{0x18, AC_VERB_SET_CONNECT_SEL, 0x0},
+	/* HP, Line-Out, Surround, CLFE, Mono pins; mute as default */
+	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+	{0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+	{0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+	/* HP Pin */
+	{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
+	/* Front, Surround, CLFE Pins */
+	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
+	{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
+	{0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
+	/* Mono Pin */
+	{0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
+	/* Mic Pin */
+	{0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
+	/* Line, Aux, CD, Beep-In Pin */
+	{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
+	{0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
+	{0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
+	{0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
+	{0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
+	{0} /* end */
+};
+
+/* additional verbs for 3-stack model */
+static struct hda_verb ad1986a_3st_init_verbs[] = {
+ 	/* Mic and line-in selectors */
+	{0x0f, AC_VERB_SET_CONNECT_SEL, 0x2},
+	{0x10, AC_VERB_SET_CONNECT_SEL, 0x1},
+ 	{0} /* end */
+};
+
+static struct hda_verb ad1986a_ch2_init[] = {
+	/* Surround out -> Line In */
+	{ 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
+	{ 0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+	/* CLFE -> Mic in */
+	{ 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
+	{ 0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+	{0} /* end */
+};
+
+static struct hda_verb ad1986a_ch4_init[] = {
+	/* Surround out -> Surround */
+	{ 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
+	{ 0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+	/* CLFE -> Mic in */
+	{ 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
+	{ 0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+	{0} /* end */
+};
+
+static struct hda_verb ad1986a_ch6_init[] = {
+	/* Surround out -> Surround out */
+	{ 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
+	{ 0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+	/* CLFE -> CLFE */
+	{ 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
+	{ 0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+	{0} /* end */
+};
+
+static struct hda_channel_mode ad1986a_modes[3] = {
+	{ 2, ad1986a_ch2_init },
+	{ 4, ad1986a_ch4_init },
+	{ 6, ad1986a_ch6_init },
+};
+
+/* eapd initialization */
+static struct hda_verb ad1986a_eapd_init_verbs[] = {
+	{0x1b, AC_VERB_SET_EAPD_BTLENABLE, 0x00},
+	{0}
+};
+
+/* Ultra initialization */
+static struct hda_verb ad1986a_ultra_init[] = {
+	/* eapd initialization */
+	{ 0x1b, AC_VERB_SET_EAPD_BTLENABLE, 0x00 },
+	/* CLFE -> Mic in */
+	{ 0x0f, AC_VERB_SET_CONNECT_SEL, 0x2 },
+	{ 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
+	{ 0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 },
+	{0} /* end */
+};
+
+/* models */
+enum {
+	AD1986A_6STACK,
+	AD1986A_3STACK,
+	AD1986A_LAPTOP,
+        AD1986A_LAPTOP_EAPD,
+        AD1986A_ULTRA,
+	AD1986A_MODELS
+};
+
+static const char *ad1986a_models[AD1986A_MODELS] = {
+	[AD1986A_6STACK]	= "6stack",
+	[AD1986A_3STACK]	= "3stack",
+	[AD1986A_LAPTOP]	= "laptop",
+        [AD1986A_LAPTOP_EAPD]	= "laptop-eapd",
+        [AD1986A_ULTRA]		= "ultra",
+};
+
+static struct snd_pci_quirk ad1986a_cfg_tbl[] = {
+	SND_PCI_QUIRK(0x103c, 0x30af, "HP B2800", AD1986A_LAPTOP_EAPD),
+	SND_PCI_QUIRK(0x10de, 0xcb84, "ASUS A8N-VM", AD1986A_3STACK),
+	SND_PCI_QUIRK(0x1043, 0x1153, "ASUS M9", AD1986A_LAPTOP_EAPD),
+	SND_PCI_QUIRK(0x1043, 0x1213, "ASUS A6J", AD1986A_LAPTOP_EAPD),
+	SND_PCI_QUIRK(0x1043, 0x11f7, "ASUS U5A", AD1986A_LAPTOP_EAPD),
+	SND_PCI_QUIRK(0x1043, 0x1263, "ASUS U5F", AD1986A_LAPTOP_EAPD),
+	SND_PCI_QUIRK(0x1043, 0x1297, "ASUS Z62F", AD1986A_LAPTOP_EAPD),
+	SND_PCI_QUIRK(0x1043, 0x12b3, "ASUS V1j", AD1986A_LAPTOP_EAPD),
+	SND_PCI_QUIRK(0x1043, 0x1302, "ASUS W3j", AD1986A_LAPTOP_EAPD),
+	SND_PCI_QUIRK(0x1043, 0x817f, "ASUS P5", AD1986A_3STACK),
+	SND_PCI_QUIRK(0x1043, 0x818f, "ASUS P5", AD1986A_LAPTOP),
+	SND_PCI_QUIRK(0x1043, 0x81b3, "ASUS P5", AD1986A_3STACK),
+	SND_PCI_QUIRK(0x1043, 0x81cb, "ASUS M2N", AD1986A_3STACK),
+	SND_PCI_QUIRK(0x1043, 0x8234, "ASUS M2N", AD1986A_3STACK),
+	SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_LAPTOP),
+	SND_PCI_QUIRK(0x144d, 0xc023, "Samsung X60", AD1986A_LAPTOP_EAPD),
+	SND_PCI_QUIRK(0x144d, 0xc024, "Samsung R65", AD1986A_LAPTOP_EAPD),
+	SND_PCI_QUIRK(0x144d, 0xc026, "Samsung X11", AD1986A_LAPTOP_EAPD),
+	SND_PCI_QUIRK(0x144d, 0xc504, "Samsung Q35", AD1986A_3STACK),
+	SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_ULTRA),
+	SND_PCI_QUIRK(0x17aa, 0x1017, "Lenovo A60", AD1986A_3STACK),
+	SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo N100", AD1986A_LAPTOP_EAPD),
+	SND_PCI_QUIRK(0x17c0, 0x2017, "Samsung M50", AD1986A_LAPTOP),
+	{0}
+};
+
+static int patch_ad1986a(struct hda_codec *codec)
+{
+	struct ad198x_spec *spec;
+	int board_config;
+
+	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+	if (spec == NULL)
+		return -ENOMEM;
+
+	mutex_init(&spec->amp_mutex);
+	codec->spec = spec;
+
+	spec->multiout.max_channels = 6;
+	spec->multiout.num_dacs = ARRAY_SIZE(ad1986a_dac_nids);
+	spec->multiout.dac_nids = ad1986a_dac_nids;
+	spec->multiout.dig_out_nid = AD1986A_SPDIF_OUT;
+	spec->num_adc_nids = 1;
+	spec->adc_nids = ad1986a_adc_nids;
+	spec->capsrc_nids = ad1986a_capsrc_nids;
+	spec->input_mux = &ad1986a_capture_source;
+	spec->num_mixers = 1;
+	spec->mixers[0] = ad1986a_mixers;
+	spec->num_init_verbs = 1;
+	spec->init_verbs[0] = ad1986a_init_verbs;
+
+	codec->patch_ops = ad198x_patch_ops;
+
+	/* override some parameters */
+	board_config = snd_hda_check_board_config(codec, AD1986A_MODELS,
+						  ad1986a_models,
+						  ad1986a_cfg_tbl);
+	switch (board_config) {
+	case AD1986A_3STACK:
+		spec->num_mixers = 2;
+		spec->mixers[1] = ad1986a_3st_mixers;
+		spec->num_init_verbs = 3;
+		spec->init_verbs[1] = ad1986a_3st_init_verbs;
+		spec->init_verbs[2] = ad1986a_ch2_init;
+		spec->channel_mode = ad1986a_modes;
+		spec->num_channel_mode = ARRAY_SIZE(ad1986a_modes);
+		spec->need_dac_fix = 1;
+		spec->multiout.max_channels = 2;
+		spec->multiout.num_dacs = 1;
+		break;
+	case AD1986A_LAPTOP:
+		spec->mixers[0] = ad1986a_laptop_mixers;
+		spec->multiout.max_channels = 2;
+		spec->multiout.num_dacs = 1;
+		spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
+		break;
+	case AD1986A_LAPTOP_EAPD:
+		spec->mixers[0] = ad1986a_laptop_eapd_mixers;
+		spec->num_init_verbs = 2;
+		spec->init_verbs[1] = ad1986a_eapd_init_verbs;
+		spec->multiout.max_channels = 2;
+		spec->multiout.num_dacs = 1;
+		spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
+		spec->multiout.dig_out_nid = 0;
+		spec->input_mux = &ad1986a_laptop_eapd_capture_source;
+		break;
+	case AD1986A_ULTRA:
+		spec->mixers[0] = ad1986a_laptop_eapd_mixers;
+		spec->num_init_verbs = 2;
+		spec->init_verbs[1] = ad1986a_ultra_init;
+		spec->multiout.max_channels = 2;
+		spec->multiout.num_dacs = 1;
+		spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
+		spec->multiout.dig_out_nid = 0;
+		break;
+	}
+
+        return 0;
+}
+
+/*
+ * AD1983 specific
+ */
+
+#define AD1983_SPDIF_OUT	0x02
+#define AD1983_DAC		0x03
+#define AD1983_ADC		0x04
+
+static hda_nid_t ad1983_dac_nids[1] = { AD1983_DAC };
+static hda_nid_t ad1983_adc_nids[1] = { AD1983_ADC };
+static hda_nid_t ad1983_capsrc_nids[1] = { 0x15 };
+
+static struct hda_input_mux ad1983_capture_source = {
+	.num_items = 4,
+	.items = {
+		{ "Mic", 0x0 },
+		{ "Line", 0x1 },
+		{ "Mix", 0x2 },
+		{ "Mix Mono", 0x3 },
+	},
+};
+
+/*
+ * SPDIF playback route
+ */
+static int ad1983_spdif_route_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+	static char *texts[] = { "PCM", "ADC" };
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 2;
+	if (uinfo->value.enumerated.item > 1)
+		uinfo->value.enumerated.item = 1;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int ad1983_spdif_route_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct ad198x_spec *spec = codec->spec;
+
+	ucontrol->value.enumerated.item[0] = spec->spdif_route;
+	return 0;
+}
+
+static int ad1983_spdif_route_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct ad198x_spec *spec = codec->spec;
+
+	if (spec->spdif_route != ucontrol->value.enumerated.item[0]) {
+		spec->spdif_route = ucontrol->value.enumerated.item[0];
+		snd_hda_codec_write(codec, spec->multiout.dig_out_nid, 0,
+				    AC_VERB_SET_CONNECT_SEL, spec->spdif_route);
+		return 1;
+	}
+	return 0;
+}
+
+static struct snd_kcontrol_new ad1983_mixers[] = {
+	HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME_MONO("PC Speaker Playback Volume", 0x10, 1, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE_MONO("PC Speaker Playback Switch", 0x10, 1, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Mic Boost", 0x0c, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Capture Source",
+		.info = ad198x_mux_enum_info,
+		.get = ad198x_mux_enum_get,
+		.put = ad198x_mux_enum_put,
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
+		.info = ad1983_spdif_route_info,
+		.get = ad1983_spdif_route_get,
+		.put = ad1983_spdif_route_put,
+	},
+	{0} /* end */
+};
+
+static struct hda_verb ad1983_init_verbs[] = {
+	/* Front, HP, Mono; mute as default */
+	{0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+	{0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+	/* Beep, PCM, Mic, Line-In: mute */
+	{0x10, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+	{0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+	{0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+	{0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+	/* Front, HP selectors; from Mix */
+	{0x05, AC_VERB_SET_CONNECT_SEL, 0x01},
+	{0x06, AC_VERB_SET_CONNECT_SEL, 0x01},
+	/* Mono selector; from Mix */
+	{0x0b, AC_VERB_SET_CONNECT_SEL, 0x03},
+	/* Mic selector; Mic */
+	{0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
+	/* Line-in selector: Line-in */
+	{0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
+	/* Mic boost: 0dB */
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+	/* Record selector: mic */
+	{0x15, AC_VERB_SET_CONNECT_SEL, 0x0},
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+	/* SPDIF route: PCM */
+	{0x02, AC_VERB_SET_CONNECT_SEL, 0x0},
+	/* Front Pin */
+	{0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
+	/* HP Pin */
+	{0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
+	/* Mono Pin */
+	{0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
+	/* Mic Pin */
+	{0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
+	/* Line Pin */
+	{0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
+	{0} /* end */
+};
+
+
+static int patch_ad1983(struct hda_codec *codec)
+{
+	struct ad198x_spec *spec;
+
+	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+	if (spec == NULL)
+		return -ENOMEM;
+
+	mutex_init(&spec->amp_mutex);
+	codec->spec = spec;
+
+	spec->multiout.max_channels = 2;
+	spec->multiout.num_dacs = ARRAY_SIZE(ad1983_dac_nids);
+	spec->multiout.dac_nids = ad1983_dac_nids;
+	spec->multiout.dig_out_nid = AD1983_SPDIF_OUT;
+	spec->num_adc_nids = 1;
+	spec->adc_nids = ad1983_adc_nids;
+	spec->capsrc_nids = ad1983_capsrc_nids;
+	spec->input_mux = &ad1983_capture_source;
+	spec->num_mixers = 1;
+	spec->mixers[0] = ad1983_mixers;
+	spec->num_init_verbs = 1;
+	spec->init_verbs[0] = ad1983_init_verbs;
+	spec->spdif_route = 0;
+
+	codec->patch_ops = ad198x_patch_ops;
+
+	return 0;
+}
+
+
+/*
+ * AD1981 HD specific
+ */
+
+#define AD1981_SPDIF_OUT	0x02
+#define AD1981_DAC		0x03
+#define AD1981_ADC		0x04
+
+static hda_nid_t ad1981_dac_nids[1] = { AD1981_DAC };
+static hda_nid_t ad1981_adc_nids[1] = { AD1981_ADC };
+static hda_nid_t ad1981_capsrc_nids[1] = { 0x15 };
+
+/* 0x0c, 0x09, 0x0e, 0x0f, 0x19, 0x05, 0x18, 0x17 */
+static struct hda_input_mux ad1981_capture_source = {
+	.num_items = 7,
+	.items = {
+		{ "Front Mic", 0x0 },
+		{ "Line", 0x1 },
+		{ "Mix", 0x2 },
+		{ "Mix Mono", 0x3 },
+		{ "CD", 0x4 },
+		{ "Mic", 0x6 },
+		{ "Aux", 0x7 },
+	},
+};
+
+static struct snd_kcontrol_new ad1981_mixers[] = {
+	HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Aux Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Aux Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME_MONO("PC Speaker Playback Volume", 0x0d, 1, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE_MONO("PC Speaker Playback Switch", 0x0d, 1, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Front Mic Boost", 0x08, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Boost", 0x18, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Capture Source",
+		.info = ad198x_mux_enum_info,
+		.get = ad198x_mux_enum_get,
+		.put = ad198x_mux_enum_put,
+	},
+	/* identical with AD1983 */
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
+		.info = ad1983_spdif_route_info,
+		.get = ad1983_spdif_route_get,
+		.put = ad1983_spdif_route_put,
+	},
+	{0} /* end */
+};
+
+static struct hda_verb ad1981_init_verbs[] = {
+	/* Front, HP, Mono; mute as default */
+	{0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+	{0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+	/* Beep, PCM, Front Mic, Line, Rear Mic, Aux, CD-In: mute */
+	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+	{0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+	{0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+	{0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+	{0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+	/* Front, HP selectors; from Mix */
+	{0x05, AC_VERB_SET_CONNECT_SEL, 0x01},
+	{0x06, AC_VERB_SET_CONNECT_SEL, 0x01},
+	/* Mono selector; from Mix */
+	{0x0b, AC_VERB_SET_CONNECT_SEL, 0x03},
+	/* Mic Mixer; select Front Mic */
+	{0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+	{0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+	/* Mic boost: 0dB */
+	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+	/* Record selector: Front mic */
+	{0x15, AC_VERB_SET_CONNECT_SEL, 0x0},
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+	/* SPDIF route: PCM */
+	{0x02, AC_VERB_SET_CONNECT_SEL, 0x0},
+	/* Front Pin */
+	{0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
+	/* HP Pin */
+	{0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
+	/* Mono Pin */
+	{0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
+	/* Front & Rear Mic Pins */
+	{0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
+	{0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
+	/* Line Pin */
+	{0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
+	/* Digital Beep */
+	{0x0d, AC_VERB_SET_CONNECT_SEL, 0x00},
+	/* Line-Out as Input: disabled */
+	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+	{0} /* end */
+};
+
+/*
+ * Patch for HP nx6320
+ *
+ * nx6320 uses EAPD in the reserve way - EAPD-on means the internal
+ * speaker output enabled _and_ mute-LED off.
+ */
+
+#define AD1981_HP_EVENT		0x37
+#define AD1981_MIC_EVENT	0x38
+
+static struct hda_verb ad1981_hp_init_verbs[] = {
+	{0x05, AC_VERB_SET_EAPD_BTLENABLE, 0x00 }, /* default off */
+	/* pin sensing on HP and Mic jacks */
+	{0x06, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_HP_EVENT},
+	{0x08, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_MIC_EVENT},
+	{0}
+};
+
+/* turn on/off EAPD (+ mute HP) as a master switch */
+static int ad1981_hp_master_sw_put(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct ad198x_spec *spec = codec->spec;
+
+	if (! ad198x_eapd_put(kcontrol, ucontrol))
+		return 0;
+
+	/* toggle HP mute appropriately */
+	snd_hda_codec_amp_update(codec, 0x06, 0, HDA_OUTPUT, 0,
+				 0x80, spec->cur_eapd ? 0 : 0x80);
+	snd_hda_codec_amp_update(codec, 0x06, 1, HDA_OUTPUT, 0,
+				 0x80, spec->cur_eapd ? 0 : 0x80);
+	return 1;
+}
+
+/* bind volumes of both NID 0x05 and 0x06 */
+static int ad1981_hp_master_vol_put(struct snd_kcontrol *kcontrol,
+				    struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	long *valp = ucontrol->value.integer.value;
+	int change;
+
+	change = snd_hda_codec_amp_update(codec, 0x05, 0, HDA_OUTPUT, 0,
+					  0x7f, valp[0] & 0x7f);
+	change |= snd_hda_codec_amp_update(codec, 0x05, 1, HDA_OUTPUT, 0,
+					   0x7f, valp[1] & 0x7f);
+	snd_hda_codec_amp_update(codec, 0x06, 0, HDA_OUTPUT, 0,
+				 0x7f, valp[0] & 0x7f);
+	snd_hda_codec_amp_update(codec, 0x06, 1, HDA_OUTPUT, 0,
+				 0x7f, valp[1] & 0x7f);
+	return change;
+}
+
+/* mute internal speaker if HP is plugged */
+static void ad1981_hp_automute(struct hda_codec *codec)
+{
+	unsigned int present;
+
+	present = snd_hda_codec_read(codec, 0x06, 0,
+				     AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+	snd_hda_codec_amp_update(codec, 0x05, 0, HDA_OUTPUT, 0,
+				 0x80, present ? 0x80 : 0);
+	snd_hda_codec_amp_update(codec, 0x05, 1, HDA_OUTPUT, 0,
+				 0x80, present ? 0x80 : 0);
+}
+
+/* toggle input of built-in and mic jack appropriately */
+static void ad1981_hp_automic(struct hda_codec *codec)
+{
+	static struct hda_verb mic_jack_on[] = {
+		{0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+		{0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+		{0}
+	};
+	static struct hda_verb mic_jack_off[] = {
+		{0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+		{0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+		{0}
+	};
+	unsigned int present;
+
+	present = snd_hda_codec_read(codec, 0x08, 0,
+			    	 AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+	if (present)
+		snd_hda_sequence_write(codec, mic_jack_on);
+	else
+		snd_hda_sequence_write(codec, mic_jack_off);
+}
+
+/* unsolicited event for HP jack sensing */
+static void ad1981_hp_unsol_event(struct hda_codec *codec,
+				  unsigned int res)
+{
+	res >>= 26;
+	switch (res) {
+	case AD1981_HP_EVENT:
+		ad1981_hp_automute(codec);
+		break;
+	case AD1981_MIC_EVENT:
+		ad1981_hp_automic(codec);
+		break;
+	}
+}
+
+static struct hda_input_mux ad1981_hp_capture_source = {
+	.num_items = 3,
+	.items = {
+		{ "Mic", 0x0 },
+		{ "Docking-Station", 0x1 },
+		{ "Mix", 0x2 },
+	},
+};
+
+static struct snd_kcontrol_new ad1981_hp_mixers[] = {
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Master Playback Volume",
+		.info = snd_hda_mixer_amp_volume_info,
+		.get = snd_hda_mixer_amp_volume_get,
+		.put = ad1981_hp_master_vol_put,
+		.private_value = HDA_COMPOSE_AMP_VAL(0x05, 3, 0, HDA_OUTPUT),
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Master Playback Switch",
+		.info = ad198x_eapd_info,
+		.get = ad198x_eapd_get,
+		.put = ad1981_hp_master_sw_put,
+		.private_value = 0x05,
+	},
+	HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
+#if 0
+	/* FIXME: analog mic/line loopback doesn't work with my tests...
+	 *        (although recording is OK)
+	 */
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Docking-Station Playback Volume", 0x13, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Docking-Station Playback Switch", 0x13, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
+	/* FIXME: does this laptop have analog CD connection? */
+	HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
+#endif
+	HDA_CODEC_VOLUME("Mic Boost", 0x08, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Internal Mic Boost", 0x18, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Capture Source",
+		.info = ad198x_mux_enum_info,
+		.get = ad198x_mux_enum_get,
+		.put = ad198x_mux_enum_put,
+	},
+	{0} /* end */
+};
+
+/* initialize jack-sensing, too */
+static int ad1981_hp_init(struct hda_codec *codec)
+{
+	ad198x_init(codec);
+	ad1981_hp_automute(codec);
+	ad1981_hp_automic(codec);
+	return 0;
+}
+
+/* configuration for Lenovo Thinkpad T60 */
+static struct snd_kcontrol_new ad1981_thinkpad_mixers[] = {
+	HDA_CODEC_VOLUME("Master Playback Volume", 0x05, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Master Playback Switch", 0x05, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Mic Boost", 0x08, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Capture Source",
+		.info = ad198x_mux_enum_info,
+		.get = ad198x_mux_enum_get,
+		.put = ad198x_mux_enum_put,
+	},
+	/* identical with AD1983 */
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
+		.info = ad1983_spdif_route_info,
+		.get = ad1983_spdif_route_get,
+		.put = ad1983_spdif_route_put,
+	},
+	{0} /* end */
+};
+
+static struct hda_input_mux ad1981_thinkpad_capture_source = {
+	.num_items = 3,
+	.items = {
+		{ "Mic", 0x0 },
+		{ "Mix", 0x2 },
+		{ "CD", 0x4 },
+	},
+};
+
+/* models */
+enum {
+	AD1981_BASIC,
+	AD1981_HP,
+	AD1981_THINKPAD,
+	AD1981_MODELS
+};
+
+static const char *ad1981_models[AD1981_MODELS] = {
+	[AD1981_HP]		= "hp",
+	[AD1981_THINKPAD]	= "thinkpad",
+	[AD1981_BASIC]		= "basic",
+};
+
+static struct snd_pci_quirk ad1981_cfg_tbl[] = {
+	/* All HP models */
+	SND_PCI_QUIRK(0x103c, 0, "HP nx", AD1981_HP),
+	/* HP nx6320 (reversed SSID, H/W bug) */
+	SND_PCI_QUIRK(0x30b0, 0x103c, "HP nx6320", AD1981_HP),
+	/* Lenovo Thinkpad T60/X60/Z6xx */
+	SND_PCI_QUIRK(0x17aa, 0, "Lenovo Thinkpad", AD1981_THINKPAD),
+	SND_PCI_QUIRK(0x1014, 0x0597, "Lenovo Z60", AD1981_THINKPAD),
+	{0}
+};
+
+static int patch_ad1981(struct hda_codec *codec)
+{
+	struct ad198x_spec *spec;
+	int board_config;
+
+	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+	if (spec == NULL)
+		return -ENOMEM;
+
+	mutex_init(&spec->amp_mutex);
+	codec->spec = spec;
+
+	spec->multiout.max_channels = 2;
+	spec->multiout.num_dacs = ARRAY_SIZE(ad1981_dac_nids);
+	spec->multiout.dac_nids = ad1981_dac_nids;
+	spec->multiout.dig_out_nid = AD1981_SPDIF_OUT;
+	spec->num_adc_nids = 1;
+	spec->adc_nids = ad1981_adc_nids;
+	spec->capsrc_nids = ad1981_capsrc_nids;
+	spec->input_mux = &ad1981_capture_source;
+	spec->num_mixers = 1;
+	spec->mixers[0] = ad1981_mixers;
+	spec->num_init_verbs = 1;
+	spec->init_verbs[0] = ad1981_init_verbs;
+	spec->spdif_route = 0;
+
+	codec->patch_ops = ad198x_patch_ops;
+
+	/* override some parameters */
+	board_config = snd_hda_check_board_config(codec, AD1981_MODELS,
+						  ad1981_models,
+						  ad1981_cfg_tbl);
+	switch (board_config) {
+	case AD1981_HP:
+		spec->mixers[0] = ad1981_hp_mixers;
+		spec->num_init_verbs = 2;
+		spec->init_verbs[1] = ad1981_hp_init_verbs;
+		spec->multiout.dig_out_nid = 0;
+		spec->input_mux = &ad1981_hp_capture_source;
+
+		codec->patch_ops.init = ad1981_hp_init;
+		codec->patch_ops.unsol_event = ad1981_hp_unsol_event;
+		break;
+	case AD1981_THINKPAD:
+		spec->mixers[0] = ad1981_thinkpad_mixers;
+		spec->input_mux = &ad1981_thinkpad_capture_source;
+		break;
+	}
+
+	return 0;
+}
+
+
+/*
+ * AD1988
+ *
+ * Output pins and routes
+ *
+ *        Pin               Mix     Sel     DAC (*)
+ * port-A 0x11 (mute/hp) <- 0x22 <- 0x37 <- 03/04/06
+ * port-B 0x14 (mute/hp) <- 0x2b <- 0x30 <- 03/04/06
+ * port-C 0x15 (mute)    <- 0x2c <- 0x31 <- 05/0a
+ * port-D 0x12 (mute/hp) <- 0x29         <- 04
+ * port-E 0x17 (mute/hp) <- 0x26 <- 0x32 <- 05/0a
+ * port-F 0x16 (mute)    <- 0x2a         <- 06
+ * port-G 0x24 (mute)    <- 0x27         <- 05
+ * port-H 0x25 (mute)    <- 0x28         <- 0a
+ * mono   0x13 (mute/amp)<- 0x1e <- 0x36 <- 03/04/06
+ *
+ * DAC0 = 03h, DAC1 = 04h, DAC2 = 05h, DAC3 = 06h, DAC4 = 0ah
+ * (*) DAC2/3/4 are swapped to DAC3/4/2 on AD198A rev.2 due to a h/w bug.
+ *
+ * Input pins and routes
+ *
+ *        pin     boost   mix input # / adc input #
+ * port-A 0x11 -> 0x38 -> mix 2, ADC 0
+ * port-B 0x14 -> 0x39 -> mix 0, ADC 1
+ * port-C 0x15 -> 0x3a -> 33:0 - mix 1, ADC 2
+ * port-D 0x12 -> 0x3d -> mix 3, ADC 8
+ * port-E 0x17 -> 0x3c -> 34:0 - mix 4, ADC 4
+ * port-F 0x16 -> 0x3b -> mix 5, ADC 3
+ * port-G 0x24 -> N/A  -> 33:1 - mix 1, 34:1 - mix 4, ADC 6
+ * port-H 0x25 -> N/A  -> 33:2 - mix 1, 34:2 - mix 4, ADC 7
+ *
+ *
+ * DAC assignment
+ *   6stack - front/surr/CLFE/side/opt DACs - 04/06/05/0a/03
+ *   3stack - front/surr/CLFE/opt DACs - 04/05/0a/03
+ *
+ * Inputs of Analog Mix (0x20)
+ *   0:Port-B (front mic)
+ *   1:Port-C/G/H (line-in)
+ *   2:Port-A
+ *   3:Port-D (line-in/2)
+ *   4:Port-E/G/H (mic-in)
+ *   5:Port-F (mic2-in)
+ *   6:CD
+ *   7:Beep
+ *
+ * ADC selection
+ *   0:Port-A
+ *   1:Port-B (front mic-in)
+ *   2:Port-C (line-in)
+ *   3:Port-F (mic2-in)
+ *   4:Port-E (mic-in)
+ *   5:CD
+ *   6:Port-G
+ *   7:Port-H
+ *   8:Port-D (line-in/2)
+ *   9:Mix
+ *
+ * Proposed pin assignments by the datasheet
+ *
+ * 6-stack
+ * Port-A front headphone
+ *      B front mic-in
+ *      C rear line-in
+ *      D rear front-out
+ *      E rear mic-in
+ *      F rear surround
+ *      G rear CLFE
+ *      H rear side
+ *
+ * 3-stack
+ * Port-A front headphone
+ *      B front mic
+ *      C rear line-in/surround
+ *      D rear front-out
+ *      E rear mic-in/CLFE
+ *
+ * laptop
+ * Port-A headphone
+ *      B mic-in
+ *      C docking station
+ *      D internal speaker (with EAPD)
+ *      E/F quad mic array
+ */
+
+
+/* models */
+enum {
+	AD1988_6STACK,
+	AD1988_6STACK_DIG,
+	AD1988_3STACK,
+	AD1988_3STACK_DIG,
+	AD1988_LAPTOP,
+	AD1988_LAPTOP_DIG,
+	AD1988_AUTO,
+	AD1988_MODEL_LAST,
+};
+
+/* reivision id to check workarounds */
+#define AD1988A_REV2		0x100200
+
+#define is_rev2(codec) \
+	((codec)->vendor_id == 0x11d41988 && \
+	 (codec)->revision_id == AD1988A_REV2)
+
+/*
+ * mixers
+ */
+
+static hda_nid_t ad1988_6stack_dac_nids[4] = {
+	0x04, 0x06, 0x05, 0x0a
+};
+
+static hda_nid_t ad1988_3stack_dac_nids[3] = {
+	0x04, 0x05, 0x0a
+};
+
+/* for AD1988A revision-2, DAC2-4 are swapped */
+static hda_nid_t ad1988_6stack_dac_nids_rev2[4] = {
+	0x04, 0x05, 0x0a, 0x06
+};
+
+static hda_nid_t ad1988_3stack_dac_nids_rev2[3] = {
+	0x04, 0x0a, 0x06
+};
+
+static hda_nid_t ad1988_adc_nids[3] = {
+	0x08, 0x09, 0x0f
+};
+
+static hda_nid_t ad1988_capsrc_nids[3] = {
+	0x0c, 0x0d, 0x0e
+};
+
+#define AD1988_SPDIF_OUT	0x02
+#define AD1988_SPDIF_IN		0x07
+
+static struct hda_input_mux ad1988_6stack_capture_source = {
+	.num_items = 5,
+	.items = {
+		{ "Front Mic", 0x0 },
+		{ "Line", 0x1 },
+		{ "Mic", 0x4 },
+		{ "CD", 0x5 },
+		{ "Mix", 0x9 },
+	},
+};
+
+static struct hda_input_mux ad1988_laptop_capture_source = {
+	.num_items = 3,
+	.items = {
+		{ "Mic/Line", 0x0 },
+		{ "CD", 0x5 },
+		{ "Mix", 0x9 },
+	},
+};
+
+/*
+ */
+static int ad198x_ch_mode_info(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_info *uinfo)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct ad198x_spec *spec = codec->spec;
+	return snd_hda_ch_mode_info(codec, uinfo, spec->channel_mode,
+				    spec->num_channel_mode);
+}
+
+static int ad198x_ch_mode_get(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct ad198x_spec *spec = codec->spec;
+	return snd_hda_ch_mode_get(codec, ucontrol, spec->channel_mode,
+				   spec->num_channel_mode, spec->multiout.max_channels);
+}
+
+static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct ad198x_spec *spec = codec->spec;
+	int err = snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode,
+				      spec->num_channel_mode,
+				      &spec->multiout.max_channels);
+	if (err >= 0 && spec->need_dac_fix)
+		spec->multiout.num_dacs = spec->multiout.max_channels / 2;
+	return err;
+}
+
+/* 6-stack mode */
+static struct snd_kcontrol_new ad1988_6stack_mixers1[] = {
+	HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Surround Playback Volume", 0x06, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Side Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
+	{0} /* end */
+};
+
+static struct snd_kcontrol_new ad1988_6stack_mixers1_rev2[] = {
+	HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Surround Playback Volume", 0x05, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0a, 2, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Side Playback Volume", 0x06, 0x0, HDA_OUTPUT),
+	{0} /* end */
+};
+
+static struct snd_kcontrol_new ad1988_6stack_mixers2[] = {
+	HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT),
+	HDA_BIND_MUTE("Surround Playback Switch", 0x2a, 2, HDA_INPUT),
+	HDA_BIND_MUTE_MONO("Center Playback Switch", 0x27, 1, 2, HDA_INPUT),
+	HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x27, 2, 2, HDA_INPUT),
+	HDA_BIND_MUTE("Side Playback Switch", 0x28, 2, HDA_INPUT),
+	HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT),
+	HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
+
+	HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
+	HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
+	HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
+	HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
+
+	HDA_CODEC_VOLUME("Beep Playback Volume", 0x10, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Beep Playback Switch", 0x10, 0x0, HDA_OUTPUT),
+
+	HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
+
+	HDA_CODEC_VOLUME("Front Mic Boost", 0x39, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Mic Boost", 0x3c, 0x0, HDA_OUTPUT),
+
+	{0} /* end */
+};
+
+/* 3-stack mode */
+static struct snd_kcontrol_new ad1988_3stack_mixers1[] = {
+	HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
+	{0} /* end */
+};
+
+static struct snd_kcontrol_new ad1988_3stack_mixers1_rev2[] = {
+	HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x06, 1, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x06, 2, 0x0, HDA_OUTPUT),
+	{0} /* end */
+};
+
+static struct snd_kcontrol_new ad1988_3stack_mixers2[] = {
+	HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT),
+	HDA_BIND_MUTE("Surround Playback Switch", 0x2c, 2, HDA_INPUT),
+	HDA_BIND_MUTE_MONO("Center Playback Switch", 0x26, 1, 2, HDA_INPUT),
+	HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x26, 2, 2, HDA_INPUT),
+	HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT),
+	HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
+
+	HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
+	HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
+	HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
+	HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
+
+	HDA_CODEC_VOLUME("Beep Playback Volume", 0x10, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Beep Playback Switch", 0x10, 0x0, HDA_OUTPUT),
+
+	HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
+
+	HDA_CODEC_VOLUME("Front Mic Boost", 0x39, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Mic Boost", 0x3c, 0x0, HDA_OUTPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Channel Mode",
+		.info = ad198x_ch_mode_info,
+		.get = ad198x_ch_mode_get,
+		.put = ad198x_ch_mode_put,
+	},
+
+	{0} /* end */
+};
+
+/* laptop mode */
+static struct snd_kcontrol_new ad1988_laptop_mixers[] = {
+	HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("PCM Playback Switch", 0x29, 0x0, HDA_INPUT),
+	HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
+
+	HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
+	HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
+	HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
+
+	HDA_CODEC_VOLUME("Beep Playback Volume", 0x10, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Beep Playback Switch", 0x10, 0x0, HDA_OUTPUT),
+
+	HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
+
+	HDA_CODEC_VOLUME("Mic Boost", 0x39, 0x0, HDA_OUTPUT),
+
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "External Amplifier",
+		.info = ad198x_eapd_info,
+		.get = ad198x_eapd_get,
+		.put = ad198x_eapd_put,
+		.private_value = 0x12 | (1 << 8), /* port-D, inversed */
+	},
+
+	{0} /* end */
+};
+
+/* capture */
+static struct snd_kcontrol_new ad1988_capture_mixers[] = {
+	HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x0e, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x0e, 0x0, HDA_OUTPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		/* The multiple "Capture Source" controls confuse alsamixer
+		 * So call somewhat different..
+		 * FIXME: the controls appear in the "playback" view!
+		 */
+		/* .name = "Capture Source", */
+		.name = "Input Source",
+		.count = 3,
+		.info = ad198x_mux_enum_info,
+		.get = ad198x_mux_enum_get,
+		.put = ad198x_mux_enum_put,
+	},
+	{0} /* end */
+};
+
+static int ad1988_spdif_playback_source_info(struct snd_kcontrol *kcontrol,
+					     struct snd_ctl_elem_info *uinfo)
+{
+	static char *texts[] = {
+		"PCM", "ADC1", "ADC2", "ADC3"
+	};
+	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 = 3;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int ad1988_spdif_playback_source_get(struct snd_kcontrol *kcontrol,
+					    struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	unsigned int sel;
+
+	sel = snd_hda_codec_read(codec, 0x02, 0, AC_VERB_GET_CONNECT_SEL, 0);
+	if (sel > 0) {
+		sel = snd_hda_codec_read(codec, 0x0b, 0, AC_VERB_GET_CONNECT_SEL, 0);
+		if (sel <= 3)
+			sel++;
+		else
+			sel = 0;
+	}
+	ucontrol->value.enumerated.item[0] = sel;
+	return 0;
+}
+
+static int ad1988_spdif_playback_source_put(struct snd_kcontrol *kcontrol,
+					    struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	unsigned int sel;
+	int change;
+
+	sel = snd_hda_codec_read(codec, 0x02, 0, AC_VERB_GET_CONNECT_SEL, 0);
+	if (! ucontrol->value.enumerated.item[0]) {
+		change = sel != 0;
+		if (change)
+			snd_hda_codec_write(codec, 0x02, 0, AC_VERB_SET_CONNECT_SEL, 0);
+	} else {
+		change = sel == 0;
+		if (change)
+			snd_hda_codec_write(codec, 0x02, 0, AC_VERB_SET_CONNECT_SEL, 1);
+		sel = snd_hda_codec_read(codec, 0x0b, 0, AC_VERB_GET_CONNECT_SEL, 0) + 1;
+		change |= sel == ucontrol->value.enumerated.item[0];
+		if (change)
+			snd_hda_codec_write(codec, 0x02, 0, AC_VERB_SET_CONNECT_SEL,
+					    ucontrol->value.enumerated.item[0] - 1);
+	}
+	return change;
+}
+
+static struct snd_kcontrol_new ad1988_spdif_out_mixers[] = {
+	HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "IEC958 Playback Source",
+		.info = ad1988_spdif_playback_source_info,
+		.get = ad1988_spdif_playback_source_get,
+		.put = ad1988_spdif_playback_source_put,
+	},
+	{0} /* end */
+};
+
+static struct snd_kcontrol_new ad1988_spdif_in_mixers[] = {
+	HDA_CODEC_VOLUME("IEC958 Capture Volume", 0x1c, 0x0, HDA_INPUT),
+	{0} /* end */
+};
+
+
+/*
+ * initialization verbs
+ */
+
+/*
+ * for 6-stack (+dig)
+ */
+static struct hda_verb ad1988_6stack_init_verbs[] = {
+	/* Front, Surround, CLFE, side DAC; unmute as default */
+	{0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	/* Port-A front headphon path */
+	{0x37, AC_VERB_SET_CONNECT_SEL, 0x01}, /* DAC1:04h */
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	{0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	/* Port-D line-out path */
+	{0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	{0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	/* Port-F surround path */
+	{0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	/* Port-G CLFE path */
+	{0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	/* Port-H side path */
+	{0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	{0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	/* Mono out path */
+	{0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
+	{0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	{0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
+	/* Port-B front mic-in path */
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+	{0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	/* Port-C line-in path */
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+	{0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
+	/* Port-E mic-in path */
+	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+	{0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x34, AC_VERB_SET_CONNECT_SEL, 0x0},
+
+	{0}
+};
+
+static struct hda_verb ad1988_capture_init_verbs[] = {
+	/* mute analog mix */
+	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
+	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
+	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
+	/* select ADCs - front-mic */
+	{0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
+	{0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
+	{0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
+	/* ADCs; muted */
+	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+
+	{0}
+};
+
+static struct hda_verb ad1988_spdif_init_verbs[] = {
+	/* SPDIF out sel */
+	{0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
+	{0x0b, AC_VERB_SET_CONNECT_SEL, 0x0}, /* ADC1 */
+	{0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	/* SPDIF out pin */
+	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
+	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x17}, /* 0dB */
+
+	{0}
+};
+
+/*
+ * verbs for 3stack (+dig)
+ */
+static struct hda_verb ad1988_3stack_ch2_init[] = {
+	/* set port-C to line-in */
+	{ 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+	{ 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
+	/* set port-E to mic-in */
+	{ 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+	{ 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
+	{0} /* end */
+};
+
+static struct hda_verb ad1988_3stack_ch6_init[] = {
+	/* set port-C to surround out */
+	{ 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+	/* set port-E to CLFE out */
+	{ 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+	{0} /* end */
+};
+
+static struct hda_channel_mode ad1988_3stack_modes[2] = {
+	{ 2, ad1988_3stack_ch2_init },
+	{ 6, ad1988_3stack_ch6_init },
+};
+
+static struct hda_verb ad1988_3stack_init_verbs[] = {
+	/* Front, Surround, CLFE, side DAC; unmute as default */
+	{0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	/* Port-A front headphon path */
+	{0x37, AC_VERB_SET_CONNECT_SEL, 0x01}, /* DAC1:04h */
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	{0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	/* Port-D line-out path */
+	{0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	{0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	/* Mono out path */
+	{0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
+	{0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	{0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
+	/* Port-B front mic-in path */
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+	{0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	/* Port-C line-in/surround path - 6ch mode as default */
+	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x31, AC_VERB_SET_CONNECT_SEL, 0x0}, /* output sel: DAC 0x05 */
+	{0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
+	/* Port-E mic-in/CLFE path - 6ch mode as default */
+	{0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x32, AC_VERB_SET_CONNECT_SEL, 0x1}, /* output sel: DAC 0x0a */
+	{0x34, AC_VERB_SET_CONNECT_SEL, 0x0},
+	/* mute analog mix */
+	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
+	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
+	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
+	/* select ADCs - front-mic */
+	{0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
+	{0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
+	{0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
+	/* ADCs; muted */
+	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0}
+};
+
+/*
+ * verbs for laptop mode (+dig)
+ */
+static struct hda_verb ad1988_laptop_hp_on[] = {
+	/* unmute port-A and mute port-D */
+	{ 0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+	{ 0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+	{0} /* end */
+};
+static struct hda_verb ad1988_laptop_hp_off[] = {
+	/* mute port-A and unmute port-D */
+	{ 0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+	{ 0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+	{0} /* end */
+};
+
+#define AD1988_HP_EVENT	0x01
+
+static struct hda_verb ad1988_laptop_init_verbs[] = {
+	/* Front, Surround, CLFE, side DAC; unmute as default */
+	{0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	/* Port-A front headphon path */
+	{0x37, AC_VERB_SET_CONNECT_SEL, 0x01}, /* DAC1:04h */
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	{0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	/* unsolicited event for pin-sense */
+	{0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1988_HP_EVENT },
+	/* Port-D line-out path + EAPD */
+	{0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	{0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x00}, /* EAPD-off */
+	/* Mono out path */
+	{0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
+	{0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	{0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
+	/* Port-B mic-in path */
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+	{0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	/* Port-C docking station - try to output */
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
+	/* mute analog mix */
+	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
+	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
+	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
+	/* select ADCs - mic */
+	{0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
+	{0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
+	{0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
+	/* ADCs; muted */
+	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0}
+};
+
+static void ad1988_laptop_unsol_event(struct hda_codec *codec, unsigned int res)
+{
+	if ((res >> 26) != AD1988_HP_EVENT)
+		return;
+	if (snd_hda_codec_read(codec, 0x11, 0, AC_VERB_GET_PIN_SENSE, 0) & (1 << 31))
+		snd_hda_sequence_write(codec, ad1988_laptop_hp_on);
+	else
+		snd_hda_sequence_write(codec, ad1988_laptop_hp_off);
+} 
+
+
+/*
+ * Automatic parse of I/O pins from the BIOS configuration
+ */
+
+#define NUM_CONTROL_ALLOC	32
+#define NUM_VERB_ALLOC		32
+
+enum {
+	AD_CTL_WIDGET_VOL,
+	AD_CTL_WIDGET_MUTE,
+	AD_CTL_BIND_MUTE,
+};
+static struct snd_kcontrol_new ad1988_control_templates[] = {
+	HDA_CODEC_VOLUME(NULL, 0, 0, 0),
+	HDA_CODEC_MUTE(NULL, 0, 0, 0),
+	HDA_BIND_MUTE(NULL, 0, 0, 0),
+};
+
+/* add dynamic controls */
+static int add_control(struct ad198x_spec *spec, int type, const char *name,
+		       unsigned long val)
+{
+	struct snd_kcontrol_new *knew;
+
+	if (spec->num_kctl_used >= spec->num_kctl_alloc) {
+		int num = spec->num_kctl_alloc + NUM_CONTROL_ALLOC;
+
+		knew = kcalloc(num + 1, sizeof(*knew), GFP_KERNEL); /* array + terminator */
+		if (! knew)
+			return -ENOMEM;
+		if (spec->kctl_alloc) {
+			memcpy(knew, spec->kctl_alloc, sizeof(*knew) * spec->num_kctl_alloc);
+			kfree(spec->kctl_alloc);
+		}
+		spec->kctl_alloc = knew;
+		spec->num_kctl_alloc = num;
+	}
+
+	knew = &spec->kctl_alloc[spec->num_kctl_used];
+	*knew = ad1988_control_templates[type];
+	knew->name = kstrdup(name, GFP_KERNEL);
+	if (! knew->name)
+		return -ENOMEM;
+	knew->private_value = val;
+	spec->num_kctl_used++;
+	return 0;
+}
+
+#define AD1988_PIN_CD_NID		0x18
+#define AD1988_PIN_BEEP_NID		0x10
+
+static hda_nid_t ad1988_mixer_nids[8] = {
+	/* A     B     C     D     E     F     G     H */
+	0x22, 0x2b, 0x2c, 0x29, 0x26, 0x2a, 0x27, 0x28
+};
+
+static inline hda_nid_t ad1988_idx_to_dac(struct hda_codec *codec, int idx)
+{
+	static hda_nid_t idx_to_dac[8] = {
+		/* A     B     C     D     E     F     G     H */
+		0x04, 0x06, 0x05, 0x04, 0x0a, 0x06, 0x05, 0x0a
+	};
+	static hda_nid_t idx_to_dac_rev2[8] = {
+		/* A     B     C     D     E     F     G     H */
+		0x04, 0x05, 0x0a, 0x04, 0x06, 0x05, 0x0a, 0x06
+	};
+	if (is_rev2(codec))
+		return idx_to_dac_rev2[idx];
+	else
+		return idx_to_dac[idx];
+}
+
+static hda_nid_t ad1988_boost_nids[8] = {
+	0x38, 0x39, 0x3a, 0x3d, 0x3c, 0x3b, 0, 0
+};
+
+static int ad1988_pin_idx(hda_nid_t nid)
+{
+	static hda_nid_t ad1988_io_pins[8] = {
+		0x11, 0x14, 0x15, 0x12, 0x17, 0x16, 0x24, 0x25
+	};
+	int i;
+	for (i = 0; i < ARRAY_SIZE(ad1988_io_pins); i++)
+		if (ad1988_io_pins[i] == nid)
+			return i;
+	return 0; /* should be -1 */
+}
+
+static int ad1988_pin_to_loopback_idx(hda_nid_t nid)
+{
+	static int loopback_idx[8] = {
+		2, 0, 1, 3, 4, 5, 1, 4
+	};
+	switch (nid) {
+	case AD1988_PIN_CD_NID:
+		return 6;
+	default:
+		return loopback_idx[ad1988_pin_idx(nid)];
+	}
+}
+
+static int ad1988_pin_to_adc_idx(hda_nid_t nid)
+{
+	static int adc_idx[8] = {
+		0, 1, 2, 8, 4, 3, 6, 7
+	};
+	switch (nid) {
+	case AD1988_PIN_CD_NID:
+		return 5;
+	default:
+		return adc_idx[ad1988_pin_idx(nid)];
+	}
+}
+
+/* fill in the dac_nids table from the parsed pin configuration */
+static int ad1988_auto_fill_dac_nids(struct hda_codec *codec,
+				     const struct auto_pin_cfg *cfg)
+{
+	struct ad198x_spec *spec = codec->spec;
+	int i, idx;
+
+	spec->multiout.dac_nids = spec->private_dac_nids;
+
+	/* check the pins hardwired to audio widget */
+	for (i = 0; i < cfg->line_outs; i++) {
+		idx = ad1988_pin_idx(cfg->line_out_pins[i]);
+		spec->multiout.dac_nids[i] = ad1988_idx_to_dac(codec, idx);
+	}
+	spec->multiout.num_dacs = cfg->line_outs;
+	return 0;
+}
+
+/* add playback controls from the parsed DAC table */
+static int ad1988_auto_create_multi_out_ctls(struct ad198x_spec *spec,
+					     const struct auto_pin_cfg *cfg)
+{
+	char name[32];
+	static const char *chname[4] = { "Front", "Surround", NULL /*CLFE*/, "Side" };
+	hda_nid_t nid;
+	int i, err;
+
+	for (i = 0; i < cfg->line_outs; i++) {
+		hda_nid_t dac = spec->multiout.dac_nids[i];
+		if (! dac)
+			continue;
+		nid = ad1988_mixer_nids[ad1988_pin_idx(cfg->line_out_pins[i])];
+		if (i == 2) {
+			/* Center/LFE */
+			err = add_control(spec, AD_CTL_WIDGET_VOL,
+					  "Center Playback Volume",
+					  HDA_COMPOSE_AMP_VAL(dac, 1, 0, HDA_OUTPUT));
+			if (err < 0)
+				return err;
+			err = add_control(spec, AD_CTL_WIDGET_VOL,
+					  "LFE Playback Volume",
+					  HDA_COMPOSE_AMP_VAL(dac, 2, 0, HDA_OUTPUT));
+			if (err < 0)
+				return err;
+			err = add_control(spec, AD_CTL_BIND_MUTE,
+					  "Center Playback Switch",
+					  HDA_COMPOSE_AMP_VAL(nid, 1, 2, HDA_INPUT));
+			if (err < 0)
+				return err;
+			err = add_control(spec, AD_CTL_BIND_MUTE,
+					  "LFE Playback Switch",
+					  HDA_COMPOSE_AMP_VAL(nid, 2, 2, HDA_INPUT));
+			if (err < 0)
+				return err;
+		} else {
+			sprintf(name, "%s Playback Volume", chname[i]);
+			err = add_control(spec, AD_CTL_WIDGET_VOL, name,
+					  HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT));
+			if (err < 0)
+				return err;
+			sprintf(name, "%s Playback Switch", chname[i]);
+			err = add_control(spec, AD_CTL_BIND_MUTE, name,
+					  HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT));
+			if (err < 0)
+				return err;
+		}
+	}
+	return 0;
+}
+
+/* add playback controls for speaker and HP outputs */
+static int ad1988_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin,
+					const char *pfx)
+{
+	struct ad198x_spec *spec = codec->spec;
+	hda_nid_t nid;
+	int idx, err;
+	char name[32];
+
+	if (! pin)
+		return 0;
+
+	idx = ad1988_pin_idx(pin);
+	nid = ad1988_idx_to_dac(codec, idx);
+	/* specify the DAC as the extra output */
+	if (! spec->multiout.hp_nid)
+		spec->multiout.hp_nid = nid;
+	else
+		spec->multiout.extra_out_nid[0] = nid;
+	/* control HP volume/switch on the output mixer amp */
+	sprintf(name, "%s Playback Volume", pfx);
+	if ((err = add_control(spec, AD_CTL_WIDGET_VOL, name,
+			       HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT))) < 0)
+		return err;
+	nid = ad1988_mixer_nids[idx];
+	sprintf(name, "%s Playback Switch", pfx);
+	if ((err = add_control(spec, AD_CTL_BIND_MUTE, name,
+			       HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT))) < 0)
+		return err;
+	return 0;
+}
+
+/* create input playback/capture controls for the given pin */
+static int new_analog_input(struct ad198x_spec *spec, hda_nid_t pin,
+			    const char *ctlname, int boost)
+{
+	char name[32];
+	int err, idx;
+
+	sprintf(name, "%s Playback Volume", ctlname);
+	idx = ad1988_pin_to_loopback_idx(pin);
+	if ((err = add_control(spec, AD_CTL_WIDGET_VOL, name,
+			       HDA_COMPOSE_AMP_VAL(0x20, 3, idx, HDA_INPUT))) < 0)
+		return err;
+	sprintf(name, "%s Playback Switch", ctlname);
+	if ((err = add_control(spec, AD_CTL_WIDGET_MUTE, name,
+			       HDA_COMPOSE_AMP_VAL(0x20, 3, idx, HDA_INPUT))) < 0)
+		return err;
+	if (boost) {
+		hda_nid_t bnid;
+		idx = ad1988_pin_idx(pin);
+		bnid = ad1988_boost_nids[idx];
+		if (bnid) {
+			sprintf(name, "%s Boost", ctlname);
+			return add_control(spec, AD_CTL_WIDGET_VOL, name,
+					   HDA_COMPOSE_AMP_VAL(bnid, 3, idx, HDA_OUTPUT));
+
+		}
+	}
+	return 0;
+}
+
+/* create playback/capture controls for input pins */
+static int ad1988_auto_create_analog_input_ctls(struct ad198x_spec *spec,
+						const struct auto_pin_cfg *cfg)
+{
+	struct hda_input_mux *imux = &spec->private_imux;
+	int i, err;
+
+	for (i = 0; i < AUTO_PIN_LAST; i++) {
+		err = new_analog_input(spec, cfg->input_pins[i],
+				       auto_pin_cfg_labels[i],
+				       i <= AUTO_PIN_FRONT_MIC);
+		if (err < 0)
+			return err;
+		imux->items[imux->num_items].label = auto_pin_cfg_labels[i];
+		imux->items[imux->num_items].index = ad1988_pin_to_adc_idx(cfg->input_pins[i]);
+		imux->num_items++;
+	}
+	imux->items[imux->num_items].label = "Mix";
+	imux->items[imux->num_items].index = 9;
+	imux->num_items++;
+
+	if ((err = add_control(spec, AD_CTL_WIDGET_VOL,
+			       "Analog Mix Playback Volume",
+			       HDA_COMPOSE_AMP_VAL(0x21, 3, 0x0, HDA_OUTPUT))) < 0)
+		return err;
+	if ((err = add_control(spec, AD_CTL_WIDGET_MUTE,
+			       "Analog Mix Playback Switch",
+			       HDA_COMPOSE_AMP_VAL(0x21, 3, 0x0, HDA_OUTPUT))) < 0)
+		return err;
+
+	return 0;
+}
+
+static void ad1988_auto_set_output_and_unmute(struct hda_codec *codec,
+					      hda_nid_t nid, int pin_type,
+					      int dac_idx)
+{
+	/* set as output */
+	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);
+	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
+	switch (nid) {
+	case 0x11: /* port-A - DAC 04 */
+		snd_hda_codec_write(codec, 0x37, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
+		break;
+	case 0x14: /* port-B - DAC 06 */
+		snd_hda_codec_write(codec, 0x30, 0, AC_VERB_SET_CONNECT_SEL, 0x02);
+		break;
+	case 0x15: /* port-C - DAC 05 */
+		snd_hda_codec_write(codec, 0x31, 0, AC_VERB_SET_CONNECT_SEL, 0x00);
+		break;
+	case 0x17: /* port-E - DAC 0a */
+		snd_hda_codec_write(codec, 0x32, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
+		break;
+	case 0x13: /* mono - DAC 04 */
+		snd_hda_codec_write(codec, 0x36, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
+		break;
+	}
+}
+
+static void ad1988_auto_init_multi_out(struct hda_codec *codec)
+{
+	struct ad198x_spec *spec = codec->spec;
+	int i;
+
+	for (i = 0; i < spec->autocfg.line_outs; i++) {
+		hda_nid_t nid = spec->autocfg.line_out_pins[i];
+		ad1988_auto_set_output_and_unmute(codec, nid, PIN_OUT, i);
+	}
+}
+
+static void ad1988_auto_init_extra_out(struct hda_codec *codec)
+{
+	struct ad198x_spec *spec = codec->spec;
+	hda_nid_t pin;
+
+	pin = spec->autocfg.speaker_pins[0];
+	if (pin) /* connect to front */
+		ad1988_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);
+	pin = spec->autocfg.hp_pins[0];
+	if (pin) /* connect to front */
+		ad1988_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
+}
+
+static void ad1988_auto_init_analog_input(struct hda_codec *codec)
+{
+	struct ad198x_spec *spec = codec->spec;
+	int i, idx;
+
+	for (i = 0; i < AUTO_PIN_LAST; i++) {
+		hda_nid_t nid = spec->autocfg.input_pins[i];
+		if (! nid)
+			continue;
+		switch (nid) {
+		case 0x15: /* port-C */
+			snd_hda_codec_write(codec, 0x33, 0, AC_VERB_SET_CONNECT_SEL, 0x0);
+			break;
+		case 0x17: /* port-E */
+			snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_CONNECT_SEL, 0x0);
+			break;
+		}
+		snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+				    i <= AUTO_PIN_FRONT_MIC ? PIN_VREF80 : PIN_IN);
+		if (nid != AD1988_PIN_CD_NID)
+			snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+					    AMP_OUT_MUTE);
+		idx = ad1988_pin_idx(nid);
+		if (ad1988_boost_nids[idx])
+			snd_hda_codec_write(codec, ad1988_boost_nids[idx], 0,
+					    AC_VERB_SET_AMP_GAIN_MUTE,
+					    AMP_OUT_ZERO);
+	}
+}
+
+/* parse the BIOS configuration and set up the alc_spec */
+/* return 1 if successful, 0 if the proper config is not found, or a negative error code */
+static int ad1988_parse_auto_config(struct hda_codec *codec)
+{
+	struct ad198x_spec *spec = codec->spec;
+	int err;
+
+	if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL)) < 0)
+		return err;
+	if ((err = ad1988_auto_fill_dac_nids(codec, &spec->autocfg)) < 0)
+		return err;
+	if (! spec->autocfg.line_outs)
+		return 0; /* can't find valid BIOS pin config */
+	if ((err = ad1988_auto_create_multi_out_ctls(spec, &spec->autocfg)) < 0 ||
+	    (err = ad1988_auto_create_extra_out(codec,
+						spec->autocfg.speaker_pins[0],
+						"Speaker")) < 0 ||
+	    (err = ad1988_auto_create_extra_out(codec, spec->autocfg.hp_pins[0],
+						"Headphone")) < 0 ||
+	    (err = ad1988_auto_create_analog_input_ctls(spec, &spec->autocfg)) < 0)
+		return err;
+
+	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
+
+	if (spec->autocfg.dig_out_pin)
+		spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
+	if (spec->autocfg.dig_in_pin)
+		spec->dig_in_nid = AD1988_SPDIF_IN;
+
+	if (spec->kctl_alloc)
+		spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+
+	spec->init_verbs[spec->num_init_verbs++] = ad1988_6stack_init_verbs;
+
+	spec->input_mux = &spec->private_imux;
+
+	return 1;
+}
+
+/* init callback for auto-configuration model -- overriding the default init */
+static int ad1988_auto_init(struct hda_codec *codec)
+{
+	ad198x_init(codec);
+	ad1988_auto_init_multi_out(codec);
+	ad1988_auto_init_extra_out(codec);
+	ad1988_auto_init_analog_input(codec);
+	return 0;
+}
+
+
+/*
+ */
+
+static const char *ad1988_models[AD1988_MODEL_LAST] = {
+	[AD1988_6STACK]		= "6stack",
+	[AD1988_6STACK_DIG]	= "6stack-dig",
+	[AD1988_3STACK]		= "3stack",
+	[AD1988_3STACK_DIG]	= "3stack-dig",
+	[AD1988_LAPTOP]		= "laptop",
+	[AD1988_LAPTOP_DIG]	= "laptop-dig",
+	[AD1988_AUTO]		= "auto",
+};
+
+static int patch_ad1988(struct hda_codec *codec)
+{
+	struct ad198x_spec *spec;
+	int board_config;
+
+	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+	if (spec == NULL)
+		return -ENOMEM;
+
+	mutex_init(&spec->amp_mutex);
+	codec->spec = spec;
+
+	if (is_rev2(codec))
+		snd_printk(KERN_INFO "patch_analog: AD1988A rev.2 is detected, enable workarounds\n");
+
+	board_config = snd_hda_check_board_config(codec, AD1988_MODEL_LAST,
+						  ad1988_models, NULL);
+	if (board_config < 0) {
+		printk(KERN_INFO "hda_codec: Unknown model for AD1988, trying auto-probe from BIOS...\n");
+		board_config = AD1988_AUTO;
+	}
+
+	if (board_config == AD1988_AUTO) {
+		/* automatic parse from the BIOS config */
+		int err = ad1988_parse_auto_config(codec);
+		if (err < 0) {
+			ad198x_free(codec);
+			return err;
+		} else if (! err) {
+			printk(KERN_INFO "hda_codec: Cannot set up configuration from BIOS.  Using 6-stack mode...\n");
+			board_config = AD1988_6STACK;
+		}
+	}
+
+	switch (board_config) {
+	case AD1988_6STACK:
+	case AD1988_6STACK_DIG:
+		spec->multiout.max_channels = 8;
+		spec->multiout.num_dacs = 4;
+		if (is_rev2(codec))
+			spec->multiout.dac_nids = ad1988_6stack_dac_nids_rev2;
+		else
+			spec->multiout.dac_nids = ad1988_6stack_dac_nids;
+		spec->input_mux = &ad1988_6stack_capture_source;
+		spec->num_mixers = 2;
+		if (is_rev2(codec))
+			spec->mixers[0] = ad1988_6stack_mixers1_rev2;
+		else
+			spec->mixers[0] = ad1988_6stack_mixers1;
+		spec->mixers[1] = ad1988_6stack_mixers2;
+		spec->num_init_verbs = 1;
+		spec->init_verbs[0] = ad1988_6stack_init_verbs;
+		if (board_config == AD1988_6STACK_DIG) {
+			spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
+			spec->dig_in_nid = AD1988_SPDIF_IN;
+		}
+		break;
+	case AD1988_3STACK:
+	case AD1988_3STACK_DIG:
+		spec->multiout.max_channels = 6;
+		spec->multiout.num_dacs = 3;
+		if (is_rev2(codec))
+			spec->multiout.dac_nids = ad1988_3stack_dac_nids_rev2;
+		else
+			spec->multiout.dac_nids = ad1988_3stack_dac_nids;
+		spec->input_mux = &ad1988_6stack_capture_source;
+		spec->channel_mode = ad1988_3stack_modes;
+		spec->num_channel_mode = ARRAY_SIZE(ad1988_3stack_modes);
+		spec->num_mixers = 2;
+		if (is_rev2(codec))
+			spec->mixers[0] = ad1988_3stack_mixers1_rev2;
+		else
+			spec->mixers[0] = ad1988_3stack_mixers1;
+		spec->mixers[1] = ad1988_3stack_mixers2;
+		spec->num_init_verbs = 1;
+		spec->init_verbs[0] = ad1988_3stack_init_verbs;
+		if (board_config == AD1988_3STACK_DIG)
+			spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
+		break;
+	case AD1988_LAPTOP:
+	case AD1988_LAPTOP_DIG:
+		spec->multiout.max_channels = 2;
+		spec->multiout.num_dacs = 1;
+		spec->multiout.dac_nids = ad1988_3stack_dac_nids;
+		spec->input_mux = &ad1988_laptop_capture_source;
+		spec->num_mixers = 1;
+		spec->mixers[0] = ad1988_laptop_mixers;
+		spec->num_init_verbs = 1;
+		spec->init_verbs[0] = ad1988_laptop_init_verbs;
+		if (board_config == AD1988_LAPTOP_DIG)
+			spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
+		break;
+	}
+
+	spec->num_adc_nids = ARRAY_SIZE(ad1988_adc_nids);
+	spec->adc_nids = ad1988_adc_nids;
+	spec->capsrc_nids = ad1988_capsrc_nids;
+	spec->mixers[spec->num_mixers++] = ad1988_capture_mixers;
+	spec->init_verbs[spec->num_init_verbs++] = ad1988_capture_init_verbs;
+	if (spec->multiout.dig_out_nid) {
+		spec->mixers[spec->num_mixers++] = ad1988_spdif_out_mixers;
+		spec->init_verbs[spec->num_init_verbs++] = ad1988_spdif_init_verbs;
+	}
+	if (spec->dig_in_nid)
+		spec->mixers[spec->num_mixers++] = ad1988_spdif_in_mixers;
+
+	codec->patch_ops = ad198x_patch_ops;
+	switch (board_config) {
+	case AD1988_AUTO:
+		codec->patch_ops.init = ad1988_auto_init;
+		break;
+	case AD1988_LAPTOP:
+	case AD1988_LAPTOP_DIG:
+		codec->patch_ops.unsol_event = ad1988_laptop_unsol_event;
+		break;
+	}
+
+	return 0;
+}
+
+
+/*
+ * patch entries
+ */
+struct hda_codec_preset snd_hda_preset_analog[] = {
+	{ .id = 0x11d41981, .name = "AD1981", .patch = patch_ad1981 },
+	{ .id = 0x11d41983, .name = "AD1983", .patch = patch_ad1983 },
+	{ .id = 0x11d41986, .name = "AD1986A", .patch = patch_ad1986a },
+	{ .id = 0x11d41988, .name = "AD1988", .patch = patch_ad1988 },
+	{ .id = 0x11d4198b, .name = "AD1988B", .patch = patch_ad1988 },
+	{0} /* terminator */
+};
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/hda/patch_atihdmi.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/hda/patch_atihdmi.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/hda/patch_atihdmi.c	(revision 91)
@@ -0,0 +1,165 @@
+/*
+ * Universal Interface for Intel High Definition Audio Codec
+ *
+ * HD audio interface patch for ATI HDMI codecs
+ *
+ * Copyright (c) 2006 ATI Technologies Inc.
+ *
+ *
+ *  This driver 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 driver is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <sound/core.h>
+#include "hda_codec.h"
+#include "hda_local.h"
+
+struct atihdmi_spec {
+    struct hda_multi_out multiout;
+
+    struct hda_pcm pcm_rec;
+};
+
+static struct hda_verb atihdmi_basic_init[] = {
+    /* enable digital output on pin widget */
+    { 0x03, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+    {0} /* terminator */
+};
+
+/*
+ * Controls
+ */
+static int atihdmi_build_controls(struct hda_codec *codec)
+{
+    struct atihdmi_spec *spec = codec->spec;
+    int err;
+
+    err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
+    if (err < 0)
+        return err;
+
+    return 0;
+}
+
+static int atihdmi_init(struct hda_codec *codec)
+{
+    snd_hda_sequence_write(codec, atihdmi_basic_init);
+    return 0;
+}
+
+#ifdef CONFIG_PM
+/*
+ * resume
+ */
+static int atihdmi_resume(struct hda_codec *codec)
+{
+    atihdmi_init(codec);
+    snd_hda_resume_spdif_out(codec);
+
+    return 0;
+}
+#endif
+
+/*
+ * Digital out
+ */
+static int atihdmi_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
+                                         struct hda_codec *codec,
+                                         struct snd_pcm_substream *substream)
+{
+    struct atihdmi_spec *spec = codec->spec;
+    return snd_hda_multi_out_dig_open(codec, &spec->multiout);
+}
+
+static int atihdmi_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
+                                          struct hda_codec *codec,
+                                          struct snd_pcm_substream *substream)
+{
+    struct atihdmi_spec *spec = codec->spec;
+    return snd_hda_multi_out_dig_close(codec, &spec->multiout);
+}
+
+static struct hda_pcm_stream atihdmi_pcm_digital_playback = {
+    .substreams = 1,
+    .channels_min = 2,
+    .channels_max = 2,
+    .nid = 0x2, /* NID to query formats and rates and setup streams */
+    .ops = {
+        .open = atihdmi_dig_playback_pcm_open,
+        .close = atihdmi_dig_playback_pcm_close
+    },
+};
+
+static int atihdmi_build_pcms(struct hda_codec *codec)
+{
+    struct atihdmi_spec *spec = codec->spec;
+    struct hda_pcm *info = &spec->pcm_rec;
+
+    codec->num_pcms = 1;
+    codec->pcm_info = info;
+
+    info->name = "ATI HDMI";
+    info->stream[SNDRV_PCM_STREAM_PLAYBACK] = atihdmi_pcm_digital_playback;
+
+    return 0;
+}
+
+static void atihdmi_free(struct hda_codec *codec)
+{
+    kfree(codec->spec);
+}
+
+static struct hda_codec_ops atihdmi_patch_ops = {
+    .build_controls = atihdmi_build_controls,
+    .build_pcms = atihdmi_build_pcms,
+    .init = atihdmi_init,
+    .free = atihdmi_free,
+#ifdef CONFIG_PM
+    .resume = atihdmi_resume,
+#endif
+};
+
+static int patch_atihdmi(struct hda_codec *codec)
+{
+    struct atihdmi_spec *spec;
+
+    spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+    if (spec == NULL)
+        return -ENOMEM;
+
+    codec->spec = spec;
+
+    spec->multiout.num_dacs = 0;	  /* no analog */
+    spec->multiout.max_channels = 2;
+    spec->multiout.dig_out_nid = 0x2; /* NID for copying analog to digital,
+    * seems to be unused in pure-digital
+    * case. */
+
+    codec->patch_ops = atihdmi_patch_ops;
+
+    return 0;
+}
+
+/*
+ * patch entries
+ */
+struct hda_codec_preset snd_hda_preset_atihdmi[] = {
+    { .id = 0x1002793c, .name = "ATI RS600 HDMI", .patch = patch_atihdmi },
+    {0} /* terminator */
+};
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/hda/patch_cmedia.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/hda/patch_cmedia.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/hda/patch_cmedia.c	(revision 91)
@@ -0,0 +1,748 @@
+/*
+ * Universal Interface for Intel High Definition Audio Codec
+ *
+ * HD audio interface patch for C-Media CMI9880
+ *
+ * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
+ *
+ *
+ *  This driver 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 driver is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <sound/core.h>
+#include "hda_codec.h"
+#include "hda_local.h"
+#define NUM_PINS	11
+
+
+/* board config type */
+enum {
+	CMI_MINIMAL,	/* back 3-jack */
+	CMI_MIN_FP,	/* back 3-jack + front-panel 2-jack */
+	CMI_FULL,	/* back 6-jack + front-panel 2-jack */
+	CMI_FULL_DIG,	/* back 6-jack + front-panel 2-jack + digital I/O */
+	CMI_ALLOUT,	/* back 5-jack + front-panel 2-jack + digital out */
+	CMI_AUTO,	/* let driver guess it */
+	CMI_MODELS
+};
+
+struct cmi_spec {
+	int board_config;
+	unsigned int no_line_in: 1;	/* no line-in (5-jack) */
+	unsigned int front_panel: 1;	/* has front-panel 2-jack */
+
+	/* playback */
+	struct hda_multi_out multiout;
+	hda_nid_t dac_nids[4];		/* NID for each DAC */
+	int num_dacs;
+
+	/* capture */
+	hda_nid_t *adc_nids;
+	hda_nid_t dig_in_nid;
+
+	/* capture source */
+	const struct hda_input_mux *input_mux;
+	unsigned int cur_mux[2];
+
+	/* channel mode */
+	int num_channel_modes;
+	const struct hda_channel_mode *channel_modes;
+
+	struct hda_pcm pcm_rec[2];	/* PCM information */
+
+	/* pin deafault configuration */
+	hda_nid_t pin_nid[NUM_PINS];
+	unsigned int def_conf[NUM_PINS];
+	unsigned int pin_def_confs;
+
+	/* multichannel pins */
+	hda_nid_t multich_pin[4];	/* max 8-channel */
+	struct hda_verb multi_init[9];	/* 2 verbs for each pin + terminator */
+};
+
+/*
+ * input MUX
+ */
+static int cmi_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct cmi_spec *spec = codec->spec;
+	return snd_hda_input_mux_info(spec->input_mux, uinfo);
+}
+
+static int cmi_mux_enum_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct cmi_spec *spec = codec->spec;
+	unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+
+	ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
+	return 0;
+}
+
+static int cmi_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct cmi_spec *spec = codec->spec;
+	unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+
+	return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
+				     spec->adc_nids[adc_idx], &spec->cur_mux[adc_idx]);
+}
+
+/*
+ * shared line-in, mic for surrounds
+ */
+
+/* 3-stack / 2 channel */
+static struct hda_verb cmi9880_ch2_init[] = {
+	/* set line-in PIN for input */
+	{ 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
+	/* set mic PIN for input, also enable vref */
+	{ 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
+	/* route front PCM (DAC1) to HP */
+	{ 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 },
+	{0}
+};
+
+/* 3-stack / 6 channel */
+static struct hda_verb cmi9880_ch6_init[] = {
+	/* set line-in PIN for output */
+	{ 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	/* set mic PIN for output */
+	{ 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	/* route front PCM (DAC1) to HP */
+	{ 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 },
+	{0}
+};
+
+/* 3-stack+front / 8 channel */
+static struct hda_verb cmi9880_ch8_init[] = {
+	/* set line-in PIN for output */
+	{ 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	/* set mic PIN for output */
+	{ 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	/* route rear-surround PCM (DAC4) to HP */
+	{ 0x0f, AC_VERB_SET_CONNECT_SEL, 0x03 },
+	{0}
+};
+
+static struct hda_channel_mode cmi9880_channel_modes[3] = {
+	{ 2, cmi9880_ch2_init },
+	{ 6, cmi9880_ch6_init },
+	{ 8, cmi9880_ch8_init },
+};
+
+static int cmi_ch_mode_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct cmi_spec *spec = codec->spec;
+	return snd_hda_ch_mode_info(codec, uinfo, spec->channel_modes,
+				    spec->num_channel_modes);
+}
+
+static int cmi_ch_mode_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct cmi_spec *spec = codec->spec;
+	return snd_hda_ch_mode_get(codec, ucontrol, spec->channel_modes,
+				   spec->num_channel_modes, spec->multiout.max_channels);
+}
+
+static int cmi_ch_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct cmi_spec *spec = codec->spec;
+	return snd_hda_ch_mode_put(codec, ucontrol, spec->channel_modes,
+				   spec->num_channel_modes, &spec->multiout.max_channels);
+}
+
+/*
+ */
+static struct snd_kcontrol_new cmi9880_basic_mixer[] = {
+	/* CMI9880 has no playback volumes! */
+	HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT), /* front */
+	HDA_CODEC_MUTE("Surround Playback Switch", 0x04, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x05, 1, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x05, 2, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Side Playback Switch", 0x06, 0x0, HDA_OUTPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		/* The multiple "Capture Source" controls confuse alsamixer
+		 * So call somewhat different..
+		 * FIXME: the controls appear in the "playback" view!
+		 */
+		/* .name = "Capture Source", */
+		.name = "Input Source",
+		.count = 2,
+		.info = cmi_mux_enum_info,
+		.get = cmi_mux_enum_get,
+		.put = cmi_mux_enum_put,
+	},
+	HDA_CODEC_VOLUME("Capture Volume", 0x08, 0, HDA_INPUT),
+	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0, HDA_INPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x08, 0, HDA_INPUT),
+	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0, HDA_INPUT),
+	HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x23, 0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x23, 0, HDA_OUTPUT),
+	{0} /* end */
+};
+
+/*
+ * shared I/O pins
+ */
+static struct snd_kcontrol_new cmi9880_ch_mode_mixer[] = {
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Channel Mode",
+		.info = cmi_ch_mode_info,
+		.get = cmi_ch_mode_get,
+		.put = cmi_ch_mode_put,
+	},
+	{0} /* end */
+};
+
+/* AUD-in selections:
+ * 0x0b 0x0c 0x0d 0x0e 0x0f 0x10 0x11 0x1f 0x20
+ */
+static struct hda_input_mux cmi9880_basic_mux = {
+	.num_items = 4,
+	.items = {
+		{ "Front Mic", 0x5 },
+		{ "Rear Mic", 0x2 },
+		{ "Line", 0x1 },
+		{ "CD", 0x7 },
+	}
+};
+
+static struct hda_input_mux cmi9880_no_line_mux = {
+	.num_items = 3,
+	.items = {
+		{ "Front Mic", 0x5 },
+		{ "Rear Mic", 0x2 },
+		{ "CD", 0x7 },
+	}
+};
+
+/* front, rear, clfe, rear_surr */
+static hda_nid_t cmi9880_dac_nids[4] = {
+	0x03, 0x04, 0x05, 0x06
+};
+/* ADC0, ADC1 */
+static hda_nid_t cmi9880_adc_nids[2] = {
+	0x08, 0x09
+};
+
+#define CMI_DIG_OUT_NID	0x07
+#define CMI_DIG_IN_NID	0x0a
+
+/*
+ */
+static struct hda_verb cmi9880_basic_init[] = {
+	/* port-D for line out (rear panel) */
+	{ 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
+	/* port-E for HP out (front panel) */
+	{ 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
+	/* route front PCM to HP */
+	{ 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 },
+	/* port-A for surround (rear panel) */
+	{ 0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
+	/* port-G for CLFE (rear panel) */
+	{ 0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
+	{ 0x1f, AC_VERB_SET_CONNECT_SEL, 0x02 },
+	/* port-H for side (rear panel) */
+	{ 0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
+	{ 0x20, AC_VERB_SET_CONNECT_SEL, 0x01 },
+	/* port-C for line-in (rear panel) */
+	{ 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
+	/* port-B for mic-in (rear panel) with vref */
+	{ 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
+	/* port-F for mic-in (front panel) with vref */
+	{ 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
+	/* CD-in */
+	{ 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
+	/* route front mic to ADC1/2 */
+	{ 0x08, AC_VERB_SET_CONNECT_SEL, 0x05 },
+	{ 0x09, AC_VERB_SET_CONNECT_SEL, 0x05 },
+	{0} /* terminator */
+};
+
+static struct hda_verb cmi9880_allout_init[] = {
+	/* port-D for line out (rear panel) */
+	{ 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
+	/* port-E for HP out (front panel) */
+	{ 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
+	/* route front PCM to HP */
+	{ 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 },
+	/* port-A for side (rear panel) */
+	{ 0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
+	/* port-G for CLFE (rear panel) */
+	{ 0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
+	{ 0x1f, AC_VERB_SET_CONNECT_SEL, 0x02 },
+	/* port-H for side (rear panel) */
+	{ 0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
+	{ 0x20, AC_VERB_SET_CONNECT_SEL, 0x01 },
+	/* port-C for surround (rear panel) */
+	{ 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
+	/* port-B for mic-in (rear panel) with vref */
+	{ 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
+	/* port-F for mic-in (front panel) with vref */
+	{ 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
+	/* CD-in */
+	{ 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
+	/* route front mic to ADC1/2 */
+	{ 0x08, AC_VERB_SET_CONNECT_SEL, 0x05 },
+	{ 0x09, AC_VERB_SET_CONNECT_SEL, 0x05 },
+	{0} /* terminator */
+};
+
+/*
+ */
+static int cmi9880_build_controls(struct hda_codec *codec)
+{
+	struct cmi_spec *spec = codec->spec;
+	int err;
+
+	err = snd_hda_add_new_ctls(codec, cmi9880_basic_mixer);
+	if (err < 0)
+		return err;
+	if (spec->channel_modes) {
+		err = snd_hda_add_new_ctls(codec, cmi9880_ch_mode_mixer);
+		if (err < 0)
+			return err;
+	}
+	if (spec->multiout.dig_out_nid) {
+		err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
+		if (err < 0)
+			return err;
+	}
+	if (spec->dig_in_nid) {
+		err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
+		if (err < 0)
+			return err;
+	}
+	return 0;
+}
+
+/* fill in the multi_dac_nids table, which will decide
+   which audio widget to use for each channel */
+static int cmi9880_fill_multi_dac_nids(struct hda_codec *codec, const struct auto_pin_cfg *cfg)
+{
+	struct cmi_spec *spec = codec->spec;
+	hda_nid_t nid;
+	int assigned[4];
+	int i, j;
+
+	/* clear the table, only one c-media dac assumed here */
+	memset(spec->dac_nids, 0, sizeof(spec->dac_nids));
+	memset(assigned, 0, sizeof(assigned));
+	/* check the pins we found */
+	for (i = 0; i < cfg->line_outs; i++) {
+		nid = cfg->line_out_pins[i];
+		/* nid 0x0b~0x0e is hardwired to audio widget 0x3~0x6 */
+		if (nid >= 0x0b && nid <= 0x0e) {
+			spec->dac_nids[i] = (nid - 0x0b) + 0x03;
+			assigned[nid - 0x0b] = 1;
+		}
+	}
+	/* left pin can be connect to any audio widget */
+	for (i = 0; i < cfg->line_outs; i++) {
+		nid = cfg->line_out_pins[i];
+		if (nid <= 0x0e)
+			continue;
+		/* search for an empty channel */
+		for (j = 0; j < cfg->line_outs; j++) {
+			if (! assigned[j]) {
+				spec->dac_nids[i] = j + 0x03;
+				assigned[j] = 1;
+				break;
+			}
+		}
+	}
+	spec->num_dacs = cfg->line_outs;
+	return 0;
+}
+
+/* create multi_init table, which is used for multichannel initialization */
+static int cmi9880_fill_multi_init(struct hda_codec *codec, const struct auto_pin_cfg *cfg)
+{
+	struct cmi_spec *spec = codec->spec;
+	hda_nid_t nid;
+	int i, j, k, len;
+
+	/* clear the table, only one c-media dac assumed here */
+	memset(spec->multi_init, 0, sizeof(spec->multi_init));
+	for (j = 0, i = 0; i < cfg->line_outs; i++) {
+		hda_nid_t conn[4];
+		nid = cfg->line_out_pins[i];
+		/* set as output */
+		spec->multi_init[j].nid = nid;
+		spec->multi_init[j].verb = AC_VERB_SET_PIN_WIDGET_CONTROL;
+		spec->multi_init[j].param = PIN_OUT;
+		j++;
+		if (nid > 0x0e) {
+			/* set connection */
+			spec->multi_init[j].nid = nid;
+			spec->multi_init[j].verb = AC_VERB_SET_CONNECT_SEL;
+			spec->multi_init[j].param = 0;
+			/* find the index in connect list */
+			len = snd_hda_get_connections(codec, nid, conn, 4);
+			for (k = 0; k < len; k++)
+				if (conn[k] == spec->dac_nids[i]) {
+					spec->multi_init[j].param = k;
+					break;
+				}
+			j++;
+		}
+	}
+	return 0;
+}
+
+static int cmi9880_init(struct hda_codec *codec)
+{
+	struct cmi_spec *spec = codec->spec;
+	if (spec->board_config == CMI_ALLOUT)
+		snd_hda_sequence_write(codec, cmi9880_allout_init);
+	else
+		snd_hda_sequence_write(codec, cmi9880_basic_init);
+	if (spec->board_config == CMI_AUTO)
+		snd_hda_sequence_write(codec, spec->multi_init);
+	return 0;
+}
+
+#ifdef CONFIG_PM
+/*
+ * resume
+ */
+static int cmi9880_resume(struct hda_codec *codec)
+{
+	struct cmi_spec *spec = codec->spec;
+
+	cmi9880_init(codec);
+	snd_hda_resume_ctls(codec, cmi9880_basic_mixer);
+	if (spec->channel_modes)
+		snd_hda_resume_ctls(codec, cmi9880_ch_mode_mixer);
+	if (spec->multiout.dig_out_nid)
+		snd_hda_resume_spdif_out(codec);
+	if (spec->dig_in_nid)
+		snd_hda_resume_spdif_in(codec);
+
+	return 0;
+}
+#endif
+
+/*
+ * Analog playback callbacks
+ */
+static int cmi9880_playback_pcm_open(struct hda_pcm_stream *hinfo,
+				     struct hda_codec *codec,
+				     struct snd_pcm_substream *substream)
+{
+	struct cmi_spec *spec = codec->spec;
+	return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream);
+}
+
+static int cmi9880_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+					struct hda_codec *codec,
+					unsigned int stream_tag,
+					unsigned int format,
+					struct snd_pcm_substream *substream)
+{
+	struct cmi_spec *spec = codec->spec;
+	return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
+						format, substream);
+}
+
+static int cmi9880_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+				       struct hda_codec *codec,
+				       struct snd_pcm_substream *substream)
+{
+	struct cmi_spec *spec = codec->spec;
+	return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
+}
+
+/*
+ * Digital out
+ */
+static int cmi9880_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
+					 struct hda_codec *codec,
+					 struct snd_pcm_substream *substream)
+{
+	struct cmi_spec *spec = codec->spec;
+	return snd_hda_multi_out_dig_open(codec, &spec->multiout);
+}
+
+static int cmi9880_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
+					  struct hda_codec *codec,
+					  struct snd_pcm_substream *substream)
+{
+	struct cmi_spec *spec = codec->spec;
+	return snd_hda_multi_out_dig_close(codec, &spec->multiout);
+}
+
+/*
+ * Analog capture
+ */
+static int cmi9880_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
+				      struct hda_codec *codec,
+				      unsigned int stream_tag,
+				      unsigned int format,
+				      struct snd_pcm_substream *substream)
+{
+	struct cmi_spec *spec = codec->spec;
+
+	snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
+				   stream_tag, 0, format);
+	return 0;
+}
+
+static int cmi9880_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+				      struct hda_codec *codec,
+				      struct snd_pcm_substream *substream)
+{
+	struct cmi_spec *spec = codec->spec;
+
+	snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], 0, 0, 0);
+	return 0;
+}
+
+
+/*
+ */
+static struct hda_pcm_stream cmi9880_pcm_analog_playback = {
+	.substreams = 1,
+	.channels_min = 2,
+	.channels_max = 8,
+	.nid = 0x03, /* NID to query formats and rates */
+	.ops = {
+		.open = cmi9880_playback_pcm_open,
+		.prepare = cmi9880_playback_pcm_prepare,
+		.cleanup = cmi9880_playback_pcm_cleanup
+	},
+};
+
+static struct hda_pcm_stream cmi9880_pcm_analog_capture = {
+	.substreams = 2,
+	.channels_min = 2,
+	.channels_max = 2,
+	.nid = 0x08, /* NID to query formats and rates */
+	.ops = {
+		.prepare = cmi9880_capture_pcm_prepare,
+		.cleanup = cmi9880_capture_pcm_cleanup
+	},
+};
+
+static struct hda_pcm_stream cmi9880_pcm_digital_playback = {
+	.substreams = 1,
+	.channels_min = 2,
+	.channels_max = 2,
+	/* NID is set in cmi9880_build_pcms */
+	.ops = {
+		.open = cmi9880_dig_playback_pcm_open,
+		.close = cmi9880_dig_playback_pcm_close
+	},
+};
+
+static struct hda_pcm_stream cmi9880_pcm_digital_capture = {
+	.substreams = 1,
+	.channels_min = 2,
+	.channels_max = 2,
+	/* NID is set in cmi9880_build_pcms */
+};
+
+static int cmi9880_build_pcms(struct hda_codec *codec)
+{
+	struct cmi_spec *spec = codec->spec;
+	struct hda_pcm *info = spec->pcm_rec;
+
+	codec->num_pcms = 1;
+	codec->pcm_info = info;
+
+	info->name = "CMI9880";
+	info->stream[SNDRV_PCM_STREAM_PLAYBACK] = cmi9880_pcm_analog_playback;
+	info->stream[SNDRV_PCM_STREAM_CAPTURE] = cmi9880_pcm_analog_capture;
+
+	if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
+		codec->num_pcms++;
+		info++;
+		info->name = "CMI9880 Digital";
+		if (spec->multiout.dig_out_nid) {
+			info->stream[SNDRV_PCM_STREAM_PLAYBACK] = cmi9880_pcm_digital_playback;
+			info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid;
+		}
+		if (spec->dig_in_nid) {
+			info->stream[SNDRV_PCM_STREAM_CAPTURE] = cmi9880_pcm_digital_capture;
+			info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid;
+		}
+	}
+
+	return 0;
+}
+
+static void cmi9880_free(struct hda_codec *codec)
+{
+	kfree(codec->spec);
+}
+
+/*
+ */
+
+static const char *cmi9880_models[CMI_MODELS] = {
+	[CMI_MINIMAL]	= "minimal",
+	[CMI_MIN_FP]	= "min_fp",
+	[CMI_FULL]	= "full",
+	[CMI_FULL_DIG]	= "full_dig",
+	[CMI_ALLOUT]	= "allout",
+	[CMI_AUTO]	= "auto",
+};
+
+static struct snd_pci_quirk cmi9880_cfg_tbl[] = {
+	SND_PCI_QUIRK(0x1043, 0x813d, "ASUS P5AD2", CMI_FULL_DIG),
+	{0} /* terminator */
+};
+
+static struct hda_codec_ops cmi9880_patch_ops = {
+	.build_controls = cmi9880_build_controls,
+	.build_pcms = cmi9880_build_pcms,
+	.init = cmi9880_init,
+	.free = cmi9880_free,
+#ifdef CONFIG_PM
+	.resume = cmi9880_resume,
+#endif
+};
+
+static int patch_cmi9880(struct hda_codec *codec)
+{
+	struct cmi_spec *spec;
+
+	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+	if (spec == NULL)
+		return -ENOMEM;
+
+	codec->spec = spec;
+	spec->board_config = snd_hda_check_board_config(codec, CMI_MODELS,
+							cmi9880_models,
+							cmi9880_cfg_tbl);
+	if (spec->board_config < 0) {
+		snd_printdd(KERN_INFO "hda_codec: Unknown model for CMI9880\n");
+		spec->board_config = CMI_AUTO; /* try everything */
+	}
+
+	/* copy default DAC NIDs */
+	memcpy(spec->dac_nids, cmi9880_dac_nids, sizeof(spec->dac_nids));
+	spec->num_dacs = 4;
+
+	switch (spec->board_config) {
+	case CMI_MINIMAL:
+	case CMI_MIN_FP:
+		spec->channel_modes = cmi9880_channel_modes;
+		if (spec->board_config == CMI_MINIMAL)
+			spec->num_channel_modes = 2;
+		else {
+			spec->front_panel = 1;
+			spec->num_channel_modes = 3;
+		}
+		spec->multiout.max_channels = cmi9880_channel_modes[0].channels;
+		spec->input_mux = &cmi9880_basic_mux;
+		break;
+	case CMI_FULL:
+	case CMI_FULL_DIG:
+		spec->front_panel = 1;
+		spec->multiout.max_channels = 8;
+		spec->input_mux = &cmi9880_basic_mux;
+		if (spec->board_config == CMI_FULL_DIG) {
+			spec->multiout.dig_out_nid = CMI_DIG_OUT_NID;
+			spec->dig_in_nid = CMI_DIG_IN_NID;
+		}
+		break;
+	case CMI_ALLOUT:
+		spec->front_panel = 1;
+		spec->multiout.max_channels = 8;
+		spec->no_line_in = 1;
+		spec->input_mux = &cmi9880_no_line_mux;
+		spec->multiout.dig_out_nid = CMI_DIG_OUT_NID;
+		break;
+	case CMI_AUTO:
+		{
+		unsigned int port_e, port_f, port_g, port_h;
+		unsigned int port_spdifi, port_spdifo;
+		struct auto_pin_cfg cfg;
+
+		/* collect pin default configuration */
+		port_e = snd_hda_codec_read(codec, 0x0f, 0, AC_VERB_GET_CONFIG_DEFAULT, 0);
+		port_f = snd_hda_codec_read(codec, 0x10, 0, AC_VERB_GET_CONFIG_DEFAULT, 0);
+		spec->front_panel = 1;
+		if (get_defcfg_connect(port_e) == AC_JACK_PORT_NONE ||
+		    get_defcfg_connect(port_f) == AC_JACK_PORT_NONE) {
+			port_g = snd_hda_codec_read(codec, 0x1f, 0, AC_VERB_GET_CONFIG_DEFAULT, 0);
+			port_h = snd_hda_codec_read(codec, 0x20, 0, AC_VERB_GET_CONFIG_DEFAULT, 0);
+			spec->channel_modes = cmi9880_channel_modes;
+			/* no front panel */
+			if (get_defcfg_connect(port_g) == AC_JACK_PORT_NONE ||
+			    get_defcfg_connect(port_h) == AC_JACK_PORT_NONE) {
+				/* no optional rear panel */
+				spec->board_config = CMI_MINIMAL;
+				spec->front_panel = 0;
+				spec->num_channel_modes = 2;
+			} else {
+				spec->board_config = CMI_MIN_FP;
+				spec->num_channel_modes = 3;
+			}
+			spec->input_mux = &cmi9880_basic_mux;
+			spec->multiout.max_channels = cmi9880_channel_modes[0].channels;
+		} else {
+			spec->input_mux = &cmi9880_basic_mux;
+			port_spdifi = snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONFIG_DEFAULT, 0);
+			port_spdifo = snd_hda_codec_read(codec, 0x12, 0, AC_VERB_GET_CONFIG_DEFAULT, 0);
+			if (get_defcfg_connect(port_spdifo) != AC_JACK_PORT_NONE)
+				spec->multiout.dig_out_nid = CMI_DIG_OUT_NID;
+			if (get_defcfg_connect(port_spdifi) != AC_JACK_PORT_NONE)
+				spec->dig_in_nid = CMI_DIG_IN_NID;
+			spec->multiout.max_channels = 8;
+		}
+		snd_hda_parse_pin_def_config(codec, &cfg, NULL);
+		if (cfg.line_outs) {
+			spec->multiout.max_channels = cfg.line_outs * 2;
+			cmi9880_fill_multi_dac_nids(codec, &cfg);
+			cmi9880_fill_multi_init(codec, &cfg);
+		} else
+			snd_printd("patch_cmedia: cannot detect association in defcfg\n");
+		break;
+		}
+	}
+
+	spec->multiout.num_dacs = spec->num_dacs;
+	spec->multiout.dac_nids = spec->dac_nids;
+
+	spec->adc_nids = cmi9880_adc_nids;
+
+	codec->patch_ops = cmi9880_patch_ops;
+
+	return 0;
+}
+
+/*
+ * patch entries
+ */
+struct hda_codec_preset snd_hda_preset_cmedia[] = {
+	{ .id = 0x13f69880, .name = "CMI9880", .patch = patch_cmi9880 },
+ 	{ .id = 0x434d4980, .name = "CMI9880", .patch = patch_cmi9880 },
+	{0} /* terminator */
+};
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/hda/patch_realtek.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/hda/patch_realtek.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/hda/patch_realtek.c	(revision 91)
@@ -0,0 +1,7784 @@
+/*
+ * Universal Interface for Intel High Definition Audio Codec
+ *
+ * HD audio interface patch for ALC 260/880/882 codecs
+ *
+ * Copyright (c) 2004 Kailang Yang <kailang@realtek.com.tw>
+ *                    PeiSen Hou <pshou@realtek.com.tw>
+ *                    Takashi Iwai <tiwai@suse.de>
+ *                    Jonathan Woithe <jwoithe@physics.adelaide.edu.au>
+ *
+ *  This driver 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 driver is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <sound/core.h>
+#include "hda_codec.h"
+#include "hda_local.h"
+
+#define ALC880_FRONT_EVENT		0x01
+#define ALC880_DCVOL_EVENT		0x02
+#define ALC880_HP_EVENT			0x04
+#define ALC880_MIC_EVENT		0x08
+
+/* ALC880 board config type */
+enum {
+	ALC880_3ST,
+	ALC880_3ST_DIG,
+	ALC880_5ST,
+	ALC880_5ST_DIG,
+	ALC880_W810,
+	ALC880_Z71V,
+	ALC880_6ST,
+	ALC880_6ST_DIG,
+	ALC880_F1734,
+	ALC880_ASUS,
+	ALC880_ASUS_DIG,
+	ALC880_ASUS_W1V,
+	ALC880_ASUS_DIG2,
+	ALC880_UNIWILL_DIG,
+	ALC880_UNIWILL,
+	ALC880_UNIWILL_P53,
+	ALC880_CLEVO,
+	ALC880_TCL_S700,
+	ALC880_LG,
+	ALC880_LG_LW,
+#ifdef CONFIG_SND_DEBUG
+	ALC880_TEST,
+#endif
+	ALC880_AUTO,
+	ALC880_MODEL_LAST /* last tag */
+};
+
+/* ALC260 models */
+enum {
+	ALC260_BASIC,
+	ALC260_HP,
+	ALC260_HP_3013,
+	ALC260_FUJITSU_S702X,
+	ALC260_ACER,
+#ifdef CONFIG_SND_DEBUG
+	ALC260_TEST,
+#endif
+	ALC260_AUTO,
+	ALC260_MODEL_LAST /* last tag */
+};
+
+/* ALC262 models */
+enum {
+	ALC262_BASIC,
+	ALC262_HIPPO,
+	ALC262_HIPPO_1,
+	ALC262_FUJITSU,
+	ALC262_HP_BPC,
+	ALC262_BENQ_ED8,
+	ALC262_AUTO,
+	ALC262_MODEL_LAST /* last tag */
+};
+
+/* ALC861 models */
+enum {
+	ALC861_3ST,
+	ALC660_3ST,
+	ALC861_3ST_DIG,
+	ALC861_6ST_DIG,
+	ALC861_UNIWILL_M31,
+	ALC861_TOSHIBA,
+	ALC861_ASUS,
+	ALC861_ASUS_LAPTOP,
+	ALC861_AUTO,
+	ALC861_MODEL_LAST,
+};
+
+/* ALC882 models */
+enum {
+	ALC882_3ST_DIG,
+	ALC882_6ST_DIG,
+	ALC882_ARIMA,
+        ALC882_AUTO,
+        ALC885_MACPRO,
+	ALC882_MODEL_LAST,
+};
+
+/* ALC883 models */
+enum {
+	ALC883_3ST_2ch_DIG,
+	ALC883_3ST_6ch_DIG,
+	ALC883_3ST_6ch,
+	ALC883_6ST_DIG,
+	ALC883_TARGA_DIG,
+	ALC883_TARGA_2ch_DIG,
+	ALC888_DEMO_BOARD,
+	ALC883_ACER,
+	ALC883_MEDION,
+	ALC883_LAPTOP_EAPD,
+	ALC883_AUTO,
+	ALC883_MODEL_LAST,
+};
+
+/* for GPIO Poll */
+#define GPIO_MASK	0x03
+
+struct alc_spec {
+	/* codec parameterization */
+	struct snd_kcontrol_new *mixers[5];	/* mixer arrays */
+	unsigned int num_mixers;
+
+	const struct hda_verb *init_verbs[5];	/* initialization verbs
+						 * don't forget NULL
+						 * termination!
+						 */
+	unsigned int num_init_verbs;
+
+	char *stream_name_analog;	/* analog PCM stream */
+	struct hda_pcm_stream *stream_analog_playback;
+	struct hda_pcm_stream *stream_analog_capture;
+
+	char *stream_name_digital;	/* digital PCM stream */ 
+	struct hda_pcm_stream *stream_digital_playback;
+	struct hda_pcm_stream *stream_digital_capture;
+
+	/* playback */
+	struct hda_multi_out multiout;	/* playback set-up
+					 * max_channels, dacs must be set
+					 * dig_out_nid and hp_nid are optional
+					 */
+
+	/* capture */
+	unsigned int num_adc_nids;
+	hda_nid_t *adc_nids;
+	hda_nid_t dig_in_nid;		/* digital-in NID; optional */
+
+	/* capture source */
+	unsigned int num_mux_defs;
+	const struct hda_input_mux *input_mux;
+	unsigned int cur_mux[3];
+
+	/* channel model */
+	const struct hda_channel_mode *channel_mode;
+	int num_channel_mode;
+	int need_dac_fix;
+
+	/* PCM information */
+	struct hda_pcm pcm_rec[3];	/* used in alc_build_pcms() */
+
+	/* dynamic controls, init_verbs and input_mux */
+	struct auto_pin_cfg autocfg;
+	unsigned int num_kctl_alloc, num_kctl_used;
+	struct snd_kcontrol_new *kctl_alloc;
+	struct hda_input_mux private_imux;
+	hda_nid_t private_dac_nids[5];
+
+	/* hooks */
+	void (*init_hook)(struct hda_codec *codec);
+	void (*unsol_event)(struct hda_codec *codec, unsigned int res);
+
+	/* for pin sensing */
+	unsigned int sense_updated: 1;
+	unsigned int jack_present: 1;
+};
+
+/*
+ * configuration template - to be copied to the spec instance
+ */
+struct alc_config_preset {
+	struct snd_kcontrol_new *mixers[5]; /* should be identical size
+					     * with spec
+					     */
+	const struct hda_verb *init_verbs[5];
+	unsigned int num_dacs;
+	hda_nid_t *dac_nids;
+	hda_nid_t dig_out_nid;		/* optional */
+	hda_nid_t hp_nid;		/* optional */
+	unsigned int num_adc_nids;
+	hda_nid_t *adc_nids;
+	hda_nid_t dig_in_nid;
+	unsigned int num_channel_mode;
+	const struct hda_channel_mode *channel_mode;
+	int need_dac_fix;
+	unsigned int num_mux_defs;
+	const struct hda_input_mux *input_mux;
+	void (*unsol_event)(struct hda_codec *, unsigned int);
+	void (*init_hook)(struct hda_codec *);
+};
+
+
+/*
+ * input MUX handling
+ */
+static int alc_mux_enum_info(struct snd_kcontrol *kcontrol,
+			     struct snd_ctl_elem_info *uinfo)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct alc_spec *spec = codec->spec;
+	unsigned int mux_idx = snd_ctl_get_ioffidx(kcontrol, &uinfo->id);
+	if (mux_idx >= spec->num_mux_defs)
+		mux_idx = 0;
+	return snd_hda_input_mux_info(&spec->input_mux[mux_idx], uinfo);
+}
+
+static int alc_mux_enum_get(struct snd_kcontrol *kcontrol,
+			    struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct alc_spec *spec = codec->spec;
+	unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+
+	ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
+	return 0;
+}
+
+static int alc_mux_enum_put(struct snd_kcontrol *kcontrol,
+			    struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct alc_spec *spec = codec->spec;
+	unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+	unsigned int mux_idx = adc_idx >= spec->num_mux_defs ? 0 : adc_idx;
+	return snd_hda_input_mux_put(codec, &spec->input_mux[mux_idx], ucontrol,
+				     spec->adc_nids[adc_idx],
+				     &spec->cur_mux[adc_idx]);
+}
+
+
+/*
+ * channel mode setting
+ */
+static int alc_ch_mode_info(struct snd_kcontrol *kcontrol,
+			    struct snd_ctl_elem_info *uinfo)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct alc_spec *spec = codec->spec;
+	return snd_hda_ch_mode_info(codec, uinfo, spec->channel_mode,
+				    spec->num_channel_mode);
+}
+
+static int alc_ch_mode_get(struct snd_kcontrol *kcontrol,
+			   struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct alc_spec *spec = codec->spec;
+	return snd_hda_ch_mode_get(codec, ucontrol, spec->channel_mode,
+				   spec->num_channel_mode,
+				   spec->multiout.max_channels);
+}
+
+static int alc_ch_mode_put(struct snd_kcontrol *kcontrol,
+			   struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct alc_spec *spec = codec->spec;
+	int err = snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode,
+				      spec->num_channel_mode,
+				      &spec->multiout.max_channels);
+	if (err >= 0 && spec->need_dac_fix)
+		spec->multiout.num_dacs = spec->multiout.max_channels / 2;
+	return err;
+}
+
+/*
+ * Control the mode of pin widget settings via the mixer.  "pc" is used
+ * instead of "%" to avoid consequences of accidently treating the % as 
+ * being part of a format specifier.  Maximum allowed length of a value is
+ * 63 characters plus NULL terminator.
+ *
+ * Note: some retasking pin complexes seem to ignore requests for input
+ * states other than HiZ (eg: PIN_VREFxx) and revert to HiZ if any of these
+ * are requested.  Therefore order this list so that this behaviour will not
+ * cause problems when mixer clients move through the enum sequentially.
+ * NIDs 0x0f and 0x10 have been observed to have this behaviour as of
+ * March 2006.
+ */
+static char *alc_pin_mode_names[] = {
+	"Mic 50pc bias", "Mic 80pc bias",
+	"Line in", "Line out", "Headphone out",
+};
+static unsigned char alc_pin_mode_values[] = {
+	PIN_VREF50, PIN_VREF80, PIN_IN, PIN_OUT, PIN_HP,
+};
+/* The control can present all 5 options, or it can limit the options based
+ * in the pin being assumed to be exclusively an input or an output pin.  In
+ * addition, "input" pins may or may not process the mic bias option
+ * depending on actual widget capability (NIDs 0x0f and 0x10 don't seem to
+ * accept requests for bias as of chip versions up to March 2006) and/or
+ * wiring in the computer.
+ */
+#define ALC_PIN_DIR_IN              0x00
+#define ALC_PIN_DIR_OUT             0x01
+#define ALC_PIN_DIR_INOUT           0x02
+#define ALC_PIN_DIR_IN_NOMICBIAS    0x03
+#define ALC_PIN_DIR_INOUT_NOMICBIAS 0x04
+
+/* Info about the pin modes supported by the different pin direction modes. 
+ * For each direction the minimum and maximum values are given.
+ */
+static signed char alc_pin_mode_dir_info[5][2] = {
+	{ 0, 2 },    /* ALC_PIN_DIR_IN */
+	{ 3, 4 },    /* ALC_PIN_DIR_OUT */
+	{ 0, 4 },    /* ALC_PIN_DIR_INOUT */
+	{ 2, 2 },    /* ALC_PIN_DIR_IN_NOMICBIAS */
+	{ 2, 4 },    /* ALC_PIN_DIR_INOUT_NOMICBIAS */
+};
+#define alc_pin_mode_min(_dir) (alc_pin_mode_dir_info[_dir][0])
+#define alc_pin_mode_max(_dir) (alc_pin_mode_dir_info[_dir][1])
+#define alc_pin_mode_n_items(_dir) \
+	(alc_pin_mode_max(_dir)-alc_pin_mode_min(_dir)+1)
+
+static int alc_pin_mode_info(struct snd_kcontrol *kcontrol,
+			     struct snd_ctl_elem_info *uinfo)
+{
+	unsigned int item_num = uinfo->value.enumerated.item;
+	unsigned char dir = (kcontrol->private_value >> 16) & 0xff;
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = alc_pin_mode_n_items(dir);
+
+	if (item_num<alc_pin_mode_min(dir) || item_num>alc_pin_mode_max(dir))
+		item_num = alc_pin_mode_min(dir);
+	strcpy(uinfo->value.enumerated.name, alc_pin_mode_names[item_num]);
+	return 0;
+}
+
+static int alc_pin_mode_get(struct snd_kcontrol *kcontrol,
+			    struct snd_ctl_elem_value *ucontrol)
+{
+	unsigned int i;
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	hda_nid_t nid = kcontrol->private_value & 0xffff;
+	unsigned char dir = (kcontrol->private_value >> 16) & 0xff;
+	long *valp = ucontrol->value.integer.value;
+	unsigned int pinctl = snd_hda_codec_read(codec, nid, 0,
+						 AC_VERB_GET_PIN_WIDGET_CONTROL,
+						 0x00);
+
+	/* Find enumerated value for current pinctl setting */
+	i = alc_pin_mode_min(dir);
+	while (alc_pin_mode_values[i] != pinctl && i <= alc_pin_mode_max(dir))
+		i++;
+	*valp = i <= alc_pin_mode_max(dir) ? i: alc_pin_mode_min(dir);
+	return 0;
+}
+
+static int alc_pin_mode_put(struct snd_kcontrol *kcontrol,
+			    struct snd_ctl_elem_value *ucontrol)
+{
+	signed int change;
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	hda_nid_t nid = kcontrol->private_value & 0xffff;
+	unsigned char dir = (kcontrol->private_value >> 16) & 0xff;
+	long val = *ucontrol->value.integer.value;
+	unsigned int pinctl = snd_hda_codec_read(codec, nid, 0,
+						 AC_VERB_GET_PIN_WIDGET_CONTROL,
+						 0x00);
+
+	if (val < alc_pin_mode_min(dir) || val > alc_pin_mode_max(dir)) 
+		val = alc_pin_mode_min(dir);
+
+	change = pinctl != alc_pin_mode_values[val];
+	if (change) {
+		/* Set pin mode to that requested */
+		snd_hda_codec_write(codec,nid,0,AC_VERB_SET_PIN_WIDGET_CONTROL,
+				    alc_pin_mode_values[val]);
+
+		/* Also enable the retasking pin's input/output as required 
+		 * for the requested pin mode.  Enum values of 2 or less are
+		 * input modes.
+		 *
+		 * Dynamically switching the input/output buffers probably
+		 * reduces noise slightly (particularly on input) so we'll
+		 * do it.  However, having both input and output buffers
+		 * enabled simultaneously doesn't seem to be problematic if
+		 * this turns out to be necessary in the future.
+		 */
+		if (val <= 2) {
+			snd_hda_codec_write(codec, nid, 0,
+					    AC_VERB_SET_AMP_GAIN_MUTE,
+					    AMP_OUT_MUTE);
+			snd_hda_codec_write(codec, nid, 0,
+					    AC_VERB_SET_AMP_GAIN_MUTE,
+					    AMP_IN_UNMUTE(0));
+		} else {
+			snd_hda_codec_write(codec, nid, 0,
+					    AC_VERB_SET_AMP_GAIN_MUTE,
+					    AMP_IN_MUTE(0));
+			snd_hda_codec_write(codec, nid, 0,
+					    AC_VERB_SET_AMP_GAIN_MUTE,
+					    AMP_OUT_UNMUTE);
+		}
+	}
+	return change;
+}
+
+#define ALC_PIN_MODE(xname, nid, dir) \
+	{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0,  \
+	  .info = alc_pin_mode_info, \
+	  .get = alc_pin_mode_get, \
+	  .put = alc_pin_mode_put, \
+	  .private_value = nid | (dir<<16) }
+
+/* A switch control for ALC260 GPIO pins.  Multiple GPIOs can be ganged
+ * together using a mask with more than one bit set.  This control is
+ * currently used only by the ALC260 test model.  At this stage they are not
+ * needed for any "production" models.
+ */
+#ifdef CONFIG_SND_DEBUG
+static int alc_gpio_data_info(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_info *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 alc_gpio_data_get(struct snd_kcontrol *kcontrol,
+			     struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	hda_nid_t nid = kcontrol->private_value & 0xffff;
+	unsigned char mask = (kcontrol->private_value >> 16) & 0xff;
+	long *valp = ucontrol->value.integer.value;
+	unsigned int val = snd_hda_codec_read(codec, nid, 0,
+					      AC_VERB_GET_GPIO_DATA, 0x00);
+
+	*valp = (val & mask) != 0;
+	return 0;
+}
+static int alc_gpio_data_put(struct snd_kcontrol *kcontrol,
+			     struct snd_ctl_elem_value *ucontrol)
+{
+	signed int change;
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	hda_nid_t nid = kcontrol->private_value & 0xffff;
+	unsigned char mask = (kcontrol->private_value >> 16) & 0xff;
+	long val = *ucontrol->value.integer.value;
+	unsigned int gpio_data = snd_hda_codec_read(codec, nid, 0,
+						    AC_VERB_GET_GPIO_DATA,
+						    0x00);
+
+	/* Set/unset the masked GPIO bit(s) as needed */
+	change = (val == 0 ? 0 : mask) != (gpio_data & mask);
+	if (val == 0)
+		gpio_data &= ~mask;
+	else
+		gpio_data |= mask;
+	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_GPIO_DATA, gpio_data);
+
+	return change;
+}
+#define ALC_GPIO_DATA_SWITCH(xname, nid, mask) \
+	{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0,  \
+	  .info = alc_gpio_data_info, \
+	  .get = alc_gpio_data_get, \
+	  .put = alc_gpio_data_put, \
+	  .private_value = nid | (mask<<16) }
+#endif   /* CONFIG_SND_DEBUG */
+
+/* A switch control to allow the enabling of the digital IO pins on the
+ * ALC260.  This is incredibly simplistic; the intention of this control is
+ * to provide something in the test model allowing digital outputs to be
+ * identified if present.  If models are found which can utilise these
+ * outputs a more complete mixer control can be devised for those models if
+ * necessary.
+ */
+#ifdef CONFIG_SND_DEBUG
+static int alc_spdif_ctrl_info(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_info *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 alc_spdif_ctrl_get(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	hda_nid_t nid = kcontrol->private_value & 0xffff;
+	unsigned char mask = (kcontrol->private_value >> 16) & 0xff;
+	long *valp = ucontrol->value.integer.value;
+	unsigned int val = snd_hda_codec_read(codec, nid, 0,
+					      AC_VERB_GET_DIGI_CONVERT, 0x00);
+
+	*valp = (val & mask) != 0;
+	return 0;
+}
+static int alc_spdif_ctrl_put(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	signed int change;
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	hda_nid_t nid = kcontrol->private_value & 0xffff;
+	unsigned char mask = (kcontrol->private_value >> 16) & 0xff;
+	long val = *ucontrol->value.integer.value;
+	unsigned int ctrl_data = snd_hda_codec_read(codec, nid, 0,
+						    AC_VERB_GET_DIGI_CONVERT,
+						    0x00);
+
+	/* Set/unset the masked control bit(s) as needed */
+	change = (val == 0 ? 0 : mask) != (ctrl_data & mask);
+	if (val==0)
+		ctrl_data &= ~mask;
+	else
+		ctrl_data |= mask;
+	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1,
+			    ctrl_data);
+
+	return change;
+}
+#define ALC_SPDIF_CTRL_SWITCH(xname, nid, mask) \
+	{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0,  \
+	  .info = alc_spdif_ctrl_info, \
+	  .get = alc_spdif_ctrl_get, \
+	  .put = alc_spdif_ctrl_put, \
+	  .private_value = nid | (mask<<16) }
+#endif   /* CONFIG_SND_DEBUG */
+
+/*
+ * set up from the preset table
+ */
+static void setup_preset(struct alc_spec *spec,
+			 const struct alc_config_preset *preset)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(preset->mixers) && preset->mixers[i]; i++)
+		spec->mixers[spec->num_mixers++] = preset->mixers[i];
+	for (i = 0; i < ARRAY_SIZE(preset->init_verbs) && preset->init_verbs[i];
+	     i++)
+		spec->init_verbs[spec->num_init_verbs++] =
+			preset->init_verbs[i];
+	
+	spec->channel_mode = preset->channel_mode;
+	spec->num_channel_mode = preset->num_channel_mode;
+	spec->need_dac_fix = preset->need_dac_fix;
+
+	spec->multiout.max_channels = spec->channel_mode[0].channels;
+
+	spec->multiout.num_dacs = preset->num_dacs;
+	spec->multiout.dac_nids = preset->dac_nids;
+	spec->multiout.dig_out_nid = preset->dig_out_nid;
+	spec->multiout.hp_nid = preset->hp_nid;
+	
+	spec->num_mux_defs = preset->num_mux_defs;
+	if (! spec->num_mux_defs)
+		spec->num_mux_defs = 1;
+	spec->input_mux = preset->input_mux;
+
+	spec->num_adc_nids = preset->num_adc_nids;
+	spec->adc_nids = preset->adc_nids;
+	spec->dig_in_nid = preset->dig_in_nid;
+
+	spec->unsol_event = preset->unsol_event;
+	spec->init_hook = preset->init_hook;
+}
+
+/*
+ * ALC880 3-stack model
+ *
+ * DAC: Front = 0x02 (0x0c), Surr = 0x05 (0x0f), CLFE = 0x04 (0x0e)
+ * Pin assignment: Front = 0x14, Line-In/Surr = 0x1a, Mic/CLFE = 0x18,
+ *                 F-Mic = 0x1b, HP = 0x19
+ */
+
+static hda_nid_t alc880_dac_nids[4] = {
+	/* front, rear, clfe, rear_surr */
+	0x02, 0x05, 0x04, 0x03
+};
+
+static hda_nid_t alc880_adc_nids[3] = {
+	/* ADC0-2 */
+	0x07, 0x08, 0x09,
+};
+
+/* The datasheet says the node 0x07 is connected from inputs,
+ * but it shows zero connection in the real implementation on some devices.
+ * Note: this is a 915GAV bug, fixed on 915GLV
+ */
+static hda_nid_t alc880_adc_nids_alt[2] = {
+	/* ADC1-2 */
+	0x08, 0x09,
+};
+
+#define ALC880_DIGOUT_NID	0x06
+#define ALC880_DIGIN_NID	0x0a
+
+static struct hda_input_mux alc880_capture_source = {
+	.num_items = 4,
+	.items = {
+		{ "Mic", 0x0 },
+		{ "Front Mic", 0x3 },
+		{ "Line", 0x2 },
+		{ "CD", 0x4 },
+	},
+};
+
+/* channel source setting (2/6 channel selection for 3-stack) */
+/* 2ch mode */
+static struct hda_verb alc880_threestack_ch2_init[] = {
+	/* set line-in to input, mute it */
+	{ 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
+	{ 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+	/* set mic-in to input vref 80%, mute it */
+	{ 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
+	{ 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+	{0} /* end */
+};
+
+/* 6ch mode */
+static struct hda_verb alc880_threestack_ch6_init[] = {
+	/* set line-in to output, unmute it */
+	{ 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+	/* set mic-in to output, unmute it */
+	{ 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+	{0} /* end */
+};
+
+static struct hda_channel_mode alc880_threestack_modes[2] = {
+	{ 2, alc880_threestack_ch2_init },
+	{ 6, alc880_threestack_ch6_init },
+};
+
+static struct snd_kcontrol_new alc880_three_stack_mixer[] = {
+	HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
+	HDA_CODEC_VOLUME("Surround Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("Surround Playback Switch", 0x0f, 2, HDA_INPUT),
+	HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
+	HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
+	HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
+	HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
+	HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
+	HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x3, HDA_INPUT),
+	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x3, HDA_INPUT),
+	HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
+	HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
+	HDA_CODEC_MUTE("Headphone Playback Switch", 0x19, 0x0, HDA_OUTPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Channel Mode",
+		.info = alc_ch_mode_info,
+		.get = alc_ch_mode_get,
+		.put = alc_ch_mode_put,
+	},
+	{0} /* end */
+};
+
+/* capture mixer elements */
+static struct snd_kcontrol_new alc880_capture_mixer[] = {
+	HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x08, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x08, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x09, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x09, 0x0, HDA_INPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		/* The multiple "Capture Source" controls confuse alsamixer
+		 * So call somewhat different..
+		 * FIXME: the controls appear in the "playback" view!
+		 */
+		/* .name = "Capture Source", */
+		.name = "Input Source",
+		.count = 3,
+		.info = alc_mux_enum_info,
+		.get = alc_mux_enum_get,
+		.put = alc_mux_enum_put,
+	},
+	{0} /* end */
+};
+
+/* capture mixer elements (in case NID 0x07 not available) */
+static struct snd_kcontrol_new alc880_capture_alt_mixer[] = {
+	HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		/* The multiple "Capture Source" controls confuse alsamixer
+		 * So call somewhat different..
+		 * FIXME: the controls appear in the "playback" view!
+		 */
+		/* .name = "Capture Source", */
+		.name = "Input Source",
+		.count = 2,
+		.info = alc_mux_enum_info,
+		.get = alc_mux_enum_get,
+		.put = alc_mux_enum_put,
+	},
+	{0} /* end */
+};
+
+
+
+/*
+ * ALC880 5-stack model
+ *
+ * DAC: Front = 0x02 (0x0c), Surr = 0x05 (0x0f), CLFE = 0x04 (0x0d),
+ *      Side = 0x02 (0xd)
+ * Pin assignment: Front = 0x14, Surr = 0x17, CLFE = 0x16
+ *                 Line-In/Side = 0x1a, Mic = 0x18, F-Mic = 0x1b, HP = 0x19
+ */
+
+/* additional mixers to alc880_three_stack_mixer */
+static struct snd_kcontrol_new alc880_five_stack_mixer[] = {
+	HDA_CODEC_VOLUME("Side Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("Side Playback Switch", 0x0d, 2, HDA_INPUT),
+	{0} /* end */
+};
+
+/* channel source setting (6/8 channel selection for 5-stack) */
+/* 6ch mode */
+static struct hda_verb alc880_fivestack_ch6_init[] = {
+	/* set line-in to input, mute it */
+	{ 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
+	{ 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+	{0} /* end */
+};
+
+/* 8ch mode */
+static struct hda_verb alc880_fivestack_ch8_init[] = {
+	/* set line-in to output, unmute it */
+	{ 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+	{0} /* end */
+};
+
+static struct hda_channel_mode alc880_fivestack_modes[2] = {
+	{ 6, alc880_fivestack_ch6_init },
+	{ 8, alc880_fivestack_ch8_init },
+};
+
+
+/*
+ * ALC880 6-stack model
+ *
+ * DAC: Front = 0x02 (0x0c), Surr = 0x03 (0x0d), CLFE = 0x04 (0x0e),
+ *      Side = 0x05 (0x0f)
+ * Pin assignment: Front = 0x14, Surr = 0x15, CLFE = 0x16, Side = 0x17,
+ *   Mic = 0x18, F-Mic = 0x19, Line = 0x1a, HP = 0x1b
+ */
+
+static hda_nid_t alc880_6st_dac_nids[4] = {
+	/* front, rear, clfe, rear_surr */
+	0x02, 0x03, 0x04, 0x05
+};	
+
+static struct hda_input_mux alc880_6stack_capture_source = {
+	.num_items = 4,
+	.items = {
+		{ "Mic", 0x0 },
+		{ "Front Mic", 0x1 },
+		{ "Line", 0x2 },
+		{ "CD", 0x4 },
+	},
+};
+
+/* fixed 8-channels */
+static struct hda_channel_mode alc880_sixstack_modes[1] = {
+	{ 8, NULL },
+};
+
+static struct snd_kcontrol_new alc880_six_stack_mixer[] = {
+	HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
+	HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
+	HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
+	HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
+	HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT),
+	HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
+	HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
+	HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
+	HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
+	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
+	HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
+	HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Channel Mode",
+		.info = alc_ch_mode_info,
+		.get = alc_ch_mode_get,
+		.put = alc_ch_mode_put,
+	},
+	{0} /* end */
+};
+
+
+/*
+ * ALC880 W810 model
+ *
+ * W810 has rear IO for:
+ * Front (DAC 02)
+ * Surround (DAC 03)
+ * Center/LFE (DAC 04)
+ * Digital out (06)
+ *
+ * The system also has a pair of internal speakers, and a headphone jack.
+ * These are both connected to Line2 on the codec, hence to DAC 02.
+ * 
+ * There is a variable resistor to control the speaker or headphone
+ * volume. This is a hardware-only device without a software API.
+ *
+ * Plugging headphones in will disable the internal speakers. This is
+ * implemented in hardware, not via the driver using jack sense. In
+ * a similar fashion, plugging into the rear socket marked "front" will
+ * disable both the speakers and headphones.
+ *
+ * For input, there's a microphone jack, and an "audio in" jack.
+ * These may not do anything useful with this driver yet, because I
+ * haven't setup any initialization verbs for these yet...
+ */
+
+static hda_nid_t alc880_w810_dac_nids[3] = {
+	/* front, rear/surround, clfe */
+	0x02, 0x03, 0x04
+};
+
+/* fixed 6 channels */
+static struct hda_channel_mode alc880_w810_modes[1] = {
+	{ 6, NULL }
+};
+
+/* Pin assignment: Front = 0x14, Surr = 0x15, CLFE = 0x16, HP = 0x1b */
+static struct snd_kcontrol_new alc880_w810_base_mixer[] = {
+	HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
+	HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
+	HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
+	HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
+	HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
+	{0} /* end */
+};
+
+
+/*
+ * Z710V model
+ *
+ * DAC: Front = 0x02 (0x0c), HP = 0x03 (0x0d)
+ * Pin assignment: Front = 0x14, HP = 0x15, Mic = 0x18, Mic2 = 0x19(?),
+ *                 Line = 0x1a
+ */
+
+static hda_nid_t alc880_z71v_dac_nids[1] = {
+	0x02
+};
+#define ALC880_Z71V_HP_DAC	0x03
+
+/* fixed 2 channels */
+static struct hda_channel_mode alc880_2_jack_modes[1] = {
+	{ 2, NULL }
+};
+
+static struct snd_kcontrol_new alc880_z71v_mixer[] = {
+	HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
+	HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("Headphone Playback Switch", 0x0d, 2, HDA_INPUT),
+	HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
+	HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+	{0} /* end */
+};
+
+
+/* FIXME! */
+/*
+ * ALC880 F1734 model
+ *
+ * DAC: HP = 0x02 (0x0c), Front = 0x03 (0x0d)
+ * Pin assignment: HP = 0x14, Front = 0x15, Mic = 0x18
+ */
+
+static hda_nid_t alc880_f1734_dac_nids[1] = {
+	0x03
+};
+#define ALC880_F1734_HP_DAC	0x02
+
+static struct snd_kcontrol_new alc880_f1734_mixer[] = {
+	HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT),
+	HDA_CODEC_VOLUME("Internal Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("Internal Speaker Playback Switch", 0x0d, 2, HDA_INPUT),
+	HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
+	HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+	{0} /* end */
+};
+
+
+/* FIXME! */
+/*
+ * ALC880 ASUS model
+ *
+ * DAC: HP/Front = 0x02 (0x0c), Surr = 0x03 (0x0d), CLFE = 0x04 (0x0e)
+ * Pin assignment: HP/Front = 0x14, Surr = 0x15, CLFE = 0x16,
+ *  Mic = 0x18, Line = 0x1a
+ */
+
+#define alc880_asus_dac_nids	alc880_w810_dac_nids	/* identical with w810 */
+#define alc880_asus_modes	alc880_threestack_modes	/* 2/6 channel mode */
+
+static struct snd_kcontrol_new alc880_asus_mixer[] = {
+	HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
+	HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
+	HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
+	HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
+	HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
+	HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
+	HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
+	HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Channel Mode",
+		.info = alc_ch_mode_info,
+		.get = alc_ch_mode_get,
+		.put = alc_ch_mode_put,
+	},
+	{0} /* end */
+};
+
+/* FIXME! */
+/*
+ * ALC880 ASUS W1V model
+ *
+ * DAC: HP/Front = 0x02 (0x0c), Surr = 0x03 (0x0d), CLFE = 0x04 (0x0e)
+ * Pin assignment: HP/Front = 0x14, Surr = 0x15, CLFE = 0x16,
+ *  Mic = 0x18, Line = 0x1a, Line2 = 0x1b
+ */
+
+/* additional mixers to alc880_asus_mixer */
+static struct snd_kcontrol_new alc880_asus_w1v_mixer[] = {
+	HDA_CODEC_VOLUME("Line2 Playback Volume", 0x0b, 0x03, HDA_INPUT),
+	HDA_CODEC_MUTE("Line2 Playback Switch", 0x0b, 0x03, HDA_INPUT),
+	{0} /* end */
+};
+
+/* additional mixers to alc880_asus_mixer */
+static struct snd_kcontrol_new alc880_pcbeep_mixer[] = {
+	HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
+	HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
+	{0} /* end */
+};
+
+/* TCL S700 */
+static struct snd_kcontrol_new alc880_tcl_s700_mixer[] = {
+	HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Headphone Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("CD Playback Volume", 0x0B, 0x04, HDA_INPUT),
+	HDA_CODEC_MUTE("CD Playback Switch", 0x0B, 0x04, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0B, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x0B, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		/* The multiple "Capture Source" controls confuse alsamixer
+		 * So call somewhat different..
+		 * FIXME: the controls appear in the "playback" view!
+		 */
+		/* .name = "Capture Source", */
+		.name = "Input Source",
+		.count = 1,
+		.info = alc_mux_enum_info,
+		.get = alc_mux_enum_get,
+		.put = alc_mux_enum_put,
+	},
+	{0} /* end */
+};
+
+/* Uniwill */
+static struct snd_kcontrol_new alc880_uniwill_mixer[] = {
+	HDA_CODEC_VOLUME("HPhone Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("HPhone Playback Switch", 0x0c, 2, HDA_INPUT),
+	HDA_CODEC_VOLUME("iSpeaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("iSpeaker Playback Switch", 0x0d, 2, HDA_INPUT),
+	HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
+	HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
+	HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
+	HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
+	HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
+	HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
+	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
+	HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
+	HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Channel Mode",
+		.info = alc_ch_mode_info,
+		.get = alc_ch_mode_get,
+		.put = alc_ch_mode_put,
+	},
+	{0} /* end */
+};
+
+static struct snd_kcontrol_new alc880_uniwill_p53_mixer[] = {
+	HDA_CODEC_VOLUME("HPhone Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("HPhone Playback Switch", 0x0c, 2, HDA_INPUT),
+	HDA_CODEC_VOLUME("iSpeaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("iSpeaker Playback Switch", 0x0d, 2, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+	{0} /* end */
+};
+
+/*
+ * build control elements
+ */
+static int alc_build_controls(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+	int err;
+	int i;
+
+	for (i = 0; i < spec->num_mixers; i++) {
+		err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
+		if (err < 0)
+			return err;
+	}
+
+	if (spec->multiout.dig_out_nid) {
+		err = snd_hda_create_spdif_out_ctls(codec,
+						    spec->multiout.dig_out_nid);
+		if (err < 0)
+			return err;
+	}
+	if (spec->dig_in_nid) {
+		err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
+		if (err < 0)
+			return err;
+	}
+	return 0;
+}
+
+
+/*
+ * initialize the codec volumes, etc
+ */
+
+/*
+ * generic initialization of ADC, input mixers and output mixers
+ */
+static struct hda_verb alc880_volume_init_verbs[] = {
+	/*
+	 * Unmute ADC0-2 and set the default input to mic-in
+	 */
+	{0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
+	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
+	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
+	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+
+	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
+	 * mixer widget
+	 * Note: PASD motherboards uses the Line In 2 as the input for front
+	 * panel mic (mic 2)
+	 */
+	/* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
+
+	/*
+	 * Set up output mixers (0x0c - 0x0f)
+	 */
+	/* set vol=0 to output mixers */
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	/* set up input amps for analog loopback */
+	/* Amp Indices: DAC = 0, mixer = 1 */
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+
+	{0}
+};
+
+/*
+ * 3-stack pin configuration:
+ * front = 0x14, mic/clfe = 0x18, HP = 0x19, line/surr = 0x1a, f-mic = 0x1b
+ */
+static struct hda_verb alc880_pin_3stack_init_verbs[] = {
+	/*
+	 * preset connection lists of input pins
+	 * 0 = front, 1 = rear_surr, 2 = CLFE, 3 = surround
+	 */
+	{0x10, AC_VERB_SET_CONNECT_SEL, 0x02}, /* mic/clfe */
+	{0x11, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */
+	{0x12, AC_VERB_SET_CONNECT_SEL, 0x03}, /* line/surround */
+
+	/*
+	 * Set pin mode and muting
+	 */
+	/* set front pin widgets 0x14 for output */
+	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	/* Mic1 (rear panel) pin widget for input and vref at 80% */
+	{0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	/* Mic2 (as headphone out) for HP output */
+	{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	/* Line In pin widget for input */
+	{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	/* Line2 (as front mic) pin widget for input and vref at 80% */
+	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	/* CD pin widget for input */
+	{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+
+	{0}
+};
+
+/*
+ * 5-stack pin configuration:
+ * front = 0x14, surround = 0x17, clfe = 0x16, mic = 0x18, HP = 0x19,
+ * line-in/side = 0x1a, f-mic = 0x1b
+ */
+static struct hda_verb alc880_pin_5stack_init_verbs[] = {
+	/*
+	 * preset connection lists of input pins
+	 * 0 = front, 1 = rear_surr, 2 = CLFE, 3 = surround
+	 */
+	{0x11, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */
+	{0x12, AC_VERB_SET_CONNECT_SEL, 0x01}, /* line/side */
+
+	/*
+	 * Set pin mode and muting
+	 */
+	/* set pin widgets 0x14-0x17 for output */
+	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	/* unmute pins for output (no gain on this amp) */
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+
+	/* Mic1 (rear panel) pin widget for input and vref at 80% */
+	{0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	/* Mic2 (as headphone out) for HP output */
+	{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	/* Line In pin widget for input */
+	{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	/* Line2 (as front mic) pin widget for input and vref at 80% */
+	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	/* CD pin widget for input */
+	{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+
+	{0}
+};
+
+/*
+ * W810 pin configuration:
+ * front = 0x14, surround = 0x15, clfe = 0x16, HP = 0x1b
+ */
+static struct hda_verb alc880_pin_w810_init_verbs[] = {
+	/* hphone/speaker input selector: front DAC */
+	{0x13, AC_VERB_SET_CONNECT_SEL, 0x0},
+
+	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+
+	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+
+	{0}
+};
+
+/*
+ * Z71V pin configuration:
+ * Speaker-out = 0x14, HP = 0x15, Mic = 0x18, Line-in = 0x1a, Mic2 = 0x1b (?)
+ */
+static struct hda_verb alc880_pin_z71v_init_verbs[] = {
+	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+
+	{0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+	{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+	{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+
+	{0}
+};
+
+/*
+ * 6-stack pin configuration:
+ * front = 0x14, surr = 0x15, clfe = 0x16, side = 0x17, mic = 0x18,
+ * f-mic = 0x19, line = 0x1a, HP = 0x1b
+ */
+static struct hda_verb alc880_pin_6stack_init_verbs[] = {
+	{0x13, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */
+
+	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+
+	{0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+	
+	{0}
+};
+
+/*
+ * Uniwill pin configuration:
+ * HP = 0x14, InternalSpeaker = 0x15, mic = 0x18, internal mic = 0x19,
+ * line = 0x1a
+ */
+static struct hda_verb alc880_uniwill_init_verbs[] = {
+	{0x13, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */
+
+	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+
+	{0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	/* {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, */
+	/* {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, */
+	{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+
+	{0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+	{0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
+
+	{0}
+};
+
+/*
+* Uniwill P53
+* HP = 0x14, InternalSpeaker = 0x15, mic = 0x19, 
+ */
+static struct hda_verb alc880_uniwill_p53_init_verbs[] = {
+	{0x13, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */
+
+	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+
+	{0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+
+	{0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+	{0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_DCVOL_EVENT},
+
+	{0}
+};
+
+/* toggle speaker-output according to the hp-jack state */
+static void alc880_uniwill_automute(struct hda_codec *codec)
+{
+ 	unsigned int present;
+
+ 	present = snd_hda_codec_read(codec, 0x14, 0,
+				     AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+	snd_hda_codec_amp_update(codec, 0x15, 0, HDA_OUTPUT, 0,
+				 0x80, present ? 0x80 : 0);
+	snd_hda_codec_amp_update(codec, 0x15, 1, HDA_OUTPUT, 0,
+				 0x80, present ? 0x80 : 0);
+	snd_hda_codec_amp_update(codec, 0x16, 0, HDA_OUTPUT, 0,
+				 0x80, present ? 0x80 : 0);
+	snd_hda_codec_amp_update(codec, 0x16, 1, HDA_OUTPUT, 0,
+				 0x80, present ? 0x80 : 0);
+
+	present = snd_hda_codec_read(codec, 0x18, 0,
+				     AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+	snd_hda_codec_write(codec, 0x0b, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+			    0x7000 | (0x01 << 8) | (present ? 0x80 : 0));
+}
+
+static void alc880_uniwill_unsol_event(struct hda_codec *codec,
+				       unsigned int res)
+{
+	/* Looks like the unsol event is incompatible with the standard
+	 * definition.  4bit tag is placed at 28 bit!
+	 */
+	if ((res >> 28) == ALC880_HP_EVENT ||
+	    (res >> 28) == ALC880_MIC_EVENT)
+		alc880_uniwill_automute(codec);
+}
+
+static void alc880_uniwill_p53_hp_automute(struct hda_codec *codec)
+{
+ 	unsigned int present;
+
+ 	present = snd_hda_codec_read(codec, 0x14, 0,
+				     AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+
+	snd_hda_codec_amp_update(codec, 0x15, 0, HDA_INPUT, 0,
+				 0x80, present ? 0x80 : 0);
+	snd_hda_codec_amp_update(codec, 0x15, 1, HDA_INPUT, 0,
+				 0x80, present ? 0x80 : 0);
+}
+
+static void alc880_uniwill_p53_dcvol_automute(struct hda_codec *codec)
+{
+	unsigned int present;
+	
+	present = snd_hda_codec_read(codec, 0x21, 0,
+				     AC_VERB_GET_VOLUME_KNOB_CONTROL, 0) & 0x7f;
+
+	snd_hda_codec_amp_update(codec, 0x0c, 0, HDA_OUTPUT, 0,
+				 0x7f, present);
+	snd_hda_codec_amp_update(codec, 0x0c, 1, HDA_OUTPUT, 0,
+				 0x7f,  present);
+
+	snd_hda_codec_amp_update(codec, 0x0d, 0, HDA_OUTPUT, 0,
+				 0x7f,  present);
+	snd_hda_codec_amp_update(codec, 0x0d, 1, HDA_OUTPUT, 0,
+				 0x7f, present);
+
+}
+static void alc880_uniwill_p53_unsol_event(struct hda_codec *codec,
+					   unsigned int res)
+{
+	/* Looks like the unsol event is incompatible with the standard
+	 * definition.  4bit tag is placed at 28 bit!
+	 */
+	if ((res >> 28) == ALC880_HP_EVENT)
+		alc880_uniwill_p53_hp_automute(codec);
+	if ((res >> 28) == ALC880_DCVOL_EVENT) 
+		alc880_uniwill_p53_dcvol_automute(codec);
+}
+
+/* FIXME! */
+/*
+ * F1734 pin configuration:
+ * HP = 0x14, speaker-out = 0x15, mic = 0x18
+ */
+static struct hda_verb alc880_pin_f1734_init_verbs[] = {
+	{0x10, AC_VERB_SET_CONNECT_SEL, 0x02},
+	{0x11, AC_VERB_SET_CONNECT_SEL, 0x00},
+	{0x12, AC_VERB_SET_CONNECT_SEL, 0x01},
+	{0x13, AC_VERB_SET_CONNECT_SEL, 0x00},
+
+	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+
+	{0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+
+	{0}
+};
+
+/* FIXME! */
+/*
+ * ASUS pin configuration:
+ * HP/front = 0x14, surr = 0x15, clfe = 0x16, mic = 0x18, line = 0x1a
+ */
+static struct hda_verb alc880_pin_asus_init_verbs[] = {
+	{0x10, AC_VERB_SET_CONNECT_SEL, 0x02},
+	{0x11, AC_VERB_SET_CONNECT_SEL, 0x00},
+	{0x12, AC_VERB_SET_CONNECT_SEL, 0x01},
+	{0x13, AC_VERB_SET_CONNECT_SEL, 0x00},
+
+	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+
+	{0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+	
+	{0}
+};
+
+/* Enable GPIO mask and set output */
+static struct hda_verb alc880_gpio1_init_verbs[] = {
+	{0x01, AC_VERB_SET_GPIO_MASK, 0x01},
+	{0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01},
+	{0x01, AC_VERB_SET_GPIO_DATA, 0x01},
+
+	{0}
+};
+
+/* Enable GPIO mask and set output */
+static struct hda_verb alc880_gpio2_init_verbs[] = {
+	{0x01, AC_VERB_SET_GPIO_MASK, 0x02},
+	{0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
+	{0x01, AC_VERB_SET_GPIO_DATA, 0x02},
+
+	{0}
+};
+
+/* Clevo m520g init */
+static struct hda_verb alc880_pin_clevo_init_verbs[] = {
+	/* headphone output */
+	{0x11, AC_VERB_SET_CONNECT_SEL, 0x01},
+	/* line-out */
+	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	/* Line-in */
+	{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	/* CD */
+	{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	/* Mic1 (rear panel) */
+	{0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	/* Mic2 (front panel) */
+	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	/* headphone */
+	{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+        /* change to EAPD mode */
+	{0x20, AC_VERB_SET_COEF_INDEX, 0x07},
+	{0x20, AC_VERB_SET_PROC_COEF,  0x3060},
+
+	{0}
+};
+
+static struct hda_verb alc880_pin_tcl_S700_init_verbs[] = {
+	/* change to EAPD mode */
+	{0x20, AC_VERB_SET_COEF_INDEX, 0x07},
+	{0x20, AC_VERB_SET_PROC_COEF,  0x3060},
+
+	/* Headphone output */
+	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	/* Front output*/
+	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
+
+	/* Line In pin widget for input */
+	{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+	/* CD pin widget for input */
+	{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+	/* Mic1 (rear panel) pin widget for input and vref at 80% */
+	{0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+
+	/* change to EAPD mode */
+	{0x20, AC_VERB_SET_COEF_INDEX, 0x07},
+	{0x20, AC_VERB_SET_PROC_COEF,  0x3070},
+
+	{0}
+};
+
+/*
+ * LG m1 express dual
+ *
+ * Pin assignment:
+ *   Rear Line-In/Out (blue): 0x14
+ *   Build-in Mic-In: 0x15
+ *   Speaker-out: 0x17
+ *   HP-Out (green): 0x1b
+ *   Mic-In/Out (red): 0x19
+ *   SPDIF-Out: 0x1e
+ */
+
+/* To make 5.1 output working (green=Front, blue=Surr, red=CLFE) */
+static hda_nid_t alc880_lg_dac_nids[3] = {
+	0x05, 0x02, 0x03
+};
+
+/* seems analog CD is not working */
+static struct hda_input_mux alc880_lg_capture_source = {
+	.num_items = 3,
+	.items = {
+		{ "Mic", 0x1 },
+		{ "Line", 0x5 },
+		{ "Internal Mic", 0x6 },
+	},
+};
+
+/* 2,4,6 channel modes */
+static struct hda_verb alc880_lg_ch2_init[] = {
+	/* set line-in and mic-in to input */
+	{ 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
+	{ 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
+	{0}
+};
+
+static struct hda_verb alc880_lg_ch4_init[] = {
+	/* set line-in to out and mic-in to input */
+	{ 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
+	{ 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
+	{0}
+};
+
+static struct hda_verb alc880_lg_ch6_init[] = {
+	/* set line-in and mic-in to output */
+	{ 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
+	{ 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
+	{0}
+};
+
+static struct hda_channel_mode alc880_lg_ch_modes[3] = {
+	{ 2, alc880_lg_ch2_init },
+	{ 4, alc880_lg_ch4_init },
+	{ 6, alc880_lg_ch6_init },
+};
+
+static struct snd_kcontrol_new alc880_lg_mixer[] = {
+	/* FIXME: it's not really "master" but front channels */
+	HDA_CODEC_VOLUME("Master Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("Master Playback Switch", 0x0f, 2, HDA_INPUT),
+	HDA_CODEC_VOLUME("Surround Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("Surround Playback Switch", 0x0c, 2, HDA_INPUT),
+	HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0d, 1, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0d, 2, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0d, 1, 2, HDA_INPUT),
+	HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0d, 2, 2, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
+	HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x06, HDA_INPUT),
+	HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x06, HDA_INPUT),
+	HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x07, HDA_INPUT),
+	HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x07, HDA_INPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Channel Mode",
+		.info = alc_ch_mode_info,
+		.get = alc_ch_mode_get,
+		.put = alc_ch_mode_put,
+	},
+	{0} /* end */
+};
+
+static struct hda_verb alc880_lg_init_verbs[] = {
+	/* set capture source to mic-in */
+	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	/* mute all amp mixer inputs */
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(5)},
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(6)},
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(7)},
+	/* line-in to input */
+	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	/* built-in mic */
+	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	/* speaker-out */
+	{0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	/* mic-in to input */
+	{0x11, AC_VERB_SET_CONNECT_SEL, 0x01},
+	{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	/* HP-out */
+	{0x13, AC_VERB_SET_CONNECT_SEL, 0x03},
+	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	/* jack sense */
+	{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | 0x1},
+	{0}
+};
+
+/* toggle speaker-output according to the hp-jack state */
+static void alc880_lg_automute(struct hda_codec *codec)
+{
+	unsigned int present;
+
+	present = snd_hda_codec_read(codec, 0x1b, 0,
+				     AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+	snd_hda_codec_amp_update(codec, 0x17, 0, HDA_OUTPUT, 0,
+				 0x80, present ? 0x80 : 0);
+	snd_hda_codec_amp_update(codec, 0x17, 1, HDA_OUTPUT, 0,
+				 0x80, present ? 0x80 : 0);
+}
+
+static void alc880_lg_unsol_event(struct hda_codec *codec, unsigned int res)
+{
+	/* Looks like the unsol event is incompatible with the standard
+	 * definition.  4bit tag is placed at 28 bit!
+	 */
+	if ((res >> 28) == 0x01)
+		alc880_lg_automute(codec);
+}
+
+/*
+ * LG LW20
+ *
+ * Pin assignment:
+ *   Speaker-out: 0x14
+ *   Mic-In: 0x18
+ *   Built-in Mic-In: 0x19 (?)
+ *   HP-Out: 0x1b
+ *   SPDIF-Out: 0x1e
+ */
+
+/* seems analog CD is not working */
+static struct hda_input_mux alc880_lg_lw_capture_source = {
+	.num_items = 2,
+	.items = {
+		{ "Mic", 0x0 },
+		{ "Internal Mic", 0x1 },
+	},
+};
+
+static struct snd_kcontrol_new alc880_lg_lw_mixer[] = {
+	HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("Master Playback Switch", 0x0c, 2, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
+	HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
+	{0} /* end */
+};
+
+static struct hda_verb alc880_lg_lw_init_verbs[] = {
+	/* set capture source to mic-in */
+	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(7)},
+	/* speaker-out */
+	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	/* HP-out */
+	{0x13, AC_VERB_SET_CONNECT_SEL, 0x00},
+	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	/* mic-in to input */
+	{0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	/* built-in mic */
+	{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	/* jack sense */
+	{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | 0x1},
+	{0}
+};
+
+/* toggle speaker-output according to the hp-jack state */
+static void alc880_lg_lw_automute(struct hda_codec *codec)
+{
+	unsigned int present;
+
+	present = snd_hda_codec_read(codec, 0x1b, 0,
+				     AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+	snd_hda_codec_amp_update(codec, 0x14, 0, HDA_OUTPUT, 0,
+				 0x80, present ? 0x80 : 0);
+	snd_hda_codec_amp_update(codec, 0x14, 1, HDA_OUTPUT, 0,
+				 0x80, present ? 0x80 : 0);
+}
+
+static void alc880_lg_lw_unsol_event(struct hda_codec *codec, unsigned int res)
+{
+	/* Looks like the unsol event is incompatible with the standard
+	 * definition.  4bit tag is placed at 28 bit!
+	 */
+	if ((res >> 28) == 0x01)
+		alc880_lg_lw_automute(codec);
+}
+
+/*
+ * Common callbacks
+ */
+
+static int alc_init(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+	unsigned int i;
+
+	for (i = 0; i < spec->num_init_verbs; i++)
+		snd_hda_sequence_write(codec, spec->init_verbs[i]);
+
+	if (spec->init_hook)
+		spec->init_hook(codec);
+
+	return 0;
+}
+
+static void alc_unsol_event(struct hda_codec *codec, unsigned int res)
+{
+	struct alc_spec *spec = codec->spec;
+
+	if (spec->unsol_event)
+		spec->unsol_event(codec, res);
+}
+
+#ifdef CONFIG_PM
+/*
+ * resume
+ */
+static int alc_resume(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+	int i;
+
+	alc_init(codec);
+	for (i = 0; i < spec->num_mixers; i++)
+		snd_hda_resume_ctls(codec, spec->mixers[i]);
+	if (spec->multiout.dig_out_nid)
+		snd_hda_resume_spdif_out(codec);
+	if (spec->dig_in_nid)
+		snd_hda_resume_spdif_in(codec);
+
+	return 0;
+}
+#endif
+
+/*
+ * Analog playback callbacks
+ */
+static int alc880_playback_pcm_open(struct hda_pcm_stream *hinfo,
+				    struct hda_codec *codec,
+				    struct snd_pcm_substream *substream)
+{
+	struct alc_spec *spec = codec->spec;
+	return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream);
+}
+
+static int alc880_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+				       struct hda_codec *codec,
+				       unsigned int stream_tag,
+				       unsigned int format,
+				       struct snd_pcm_substream *substream)
+{
+	struct alc_spec *spec = codec->spec;
+	return snd_hda_multi_out_analog_prepare(codec, &spec->multiout,
+						stream_tag, format, substream);
+}
+
+static int alc880_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+				       struct hda_codec *codec,
+				       struct snd_pcm_substream *substream)
+{
+	struct alc_spec *spec = codec->spec;
+	return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
+}
+
+/*
+ * Digital out
+ */
+static int alc880_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
+					struct hda_codec *codec,
+					struct snd_pcm_substream *substream)
+{
+	struct alc_spec *spec = codec->spec;
+	return snd_hda_multi_out_dig_open(codec, &spec->multiout);
+}
+
+static int alc880_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
+					 struct hda_codec *codec,
+					 struct snd_pcm_substream *substream)
+{
+	struct alc_spec *spec = codec->spec;
+	return snd_hda_multi_out_dig_close(codec, &spec->multiout);
+}
+
+/*
+ * Analog capture
+ */
+static int alc880_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
+				      struct hda_codec *codec,
+				      unsigned int stream_tag,
+				      unsigned int format,
+				      struct snd_pcm_substream *substream)
+{
+	struct alc_spec *spec = codec->spec;
+
+	snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
+				   stream_tag, 0, format);
+	return 0;
+}
+
+static int alc880_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+				      struct hda_codec *codec,
+				      struct snd_pcm_substream *substream)
+{
+	struct alc_spec *spec = codec->spec;
+
+	snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
+				   0, 0, 0);
+	return 0;
+}
+
+
+/*
+ */
+static struct hda_pcm_stream alc880_pcm_analog_playback = {
+	.substreams = 1,
+	.channels_min = 2,
+	.channels_max = 8,
+	/* NID is set in alc_build_pcms */
+	.ops = {
+		.open = alc880_playback_pcm_open,
+		.prepare = alc880_playback_pcm_prepare,
+		.cleanup = alc880_playback_pcm_cleanup
+	},
+};
+
+static struct hda_pcm_stream alc880_pcm_analog_capture = {
+	.substreams = 2,
+	.channels_min = 2,
+	.channels_max = 2,
+	/* NID is set in alc_build_pcms */
+	.ops = {
+		.prepare = alc880_capture_pcm_prepare,
+		.cleanup = alc880_capture_pcm_cleanup
+	},
+};
+
+static struct hda_pcm_stream alc880_pcm_digital_playback = {
+	.substreams = 1,
+	.channels_min = 2,
+	.channels_max = 2,
+	/* NID is set in alc_build_pcms */
+	.ops = {
+		.open = alc880_dig_playback_pcm_open,
+		.close = alc880_dig_playback_pcm_close
+	},
+};
+
+static struct hda_pcm_stream alc880_pcm_digital_capture = {
+	.substreams = 1,
+	.channels_min = 2,
+	.channels_max = 2,
+	/* NID is set in alc_build_pcms */
+};
+
+/* Used by alc_build_pcms to flag that a PCM has no playback stream */
+static struct hda_pcm_stream alc_pcm_null_playback = {
+	.substreams = 0,
+	.channels_min = 0,
+	.channels_max = 0,
+};
+
+static int alc_build_pcms(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+	struct hda_pcm *info = spec->pcm_rec;
+	int i;
+
+	codec->num_pcms = 1;
+	codec->pcm_info = info;
+
+	info->name = spec->stream_name_analog;
+	if (spec->stream_analog_playback) {
+		snd_assert(spec->multiout.dac_nids, return -EINVAL);
+		info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *(spec->stream_analog_playback);
+		info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0];
+	}
+	if (spec->stream_analog_capture) {
+		snd_assert(spec->adc_nids, return -EINVAL);
+		info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_analog_capture);
+		info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
+	}
+
+	if (spec->channel_mode) {
+		info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = 0;
+		for (i = 0; i < spec->num_channel_mode; i++) {
+			if (spec->channel_mode[i].channels > info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max) {
+				info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = spec->channel_mode[i].channels;
+			}
+		}
+	}
+
+	/* SPDIF for stream index #1 */
+	if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
+		codec->num_pcms = 2;
+		info = spec->pcm_rec + 1;
+		info->name = spec->stream_name_digital;
+		if (spec->multiout.dig_out_nid &&
+		    spec->stream_digital_playback) {
+			info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *(spec->stream_digital_playback);
+			info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid;
+		}
+		if (spec->dig_in_nid &&
+		    spec->stream_digital_capture) {
+			info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_digital_capture);
+			info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid;
+		}
+	}
+
+	/* If the use of more than one ADC is requested for the current
+	 * model, configure a second analog capture-only PCM.
+	 */
+	/* Additional Analaog capture for index #2 */
+	if (spec->num_adc_nids > 1 && spec->stream_analog_capture &&
+	    spec->adc_nids) {
+		codec->num_pcms = 3;
+		info = spec->pcm_rec + 2;
+		info->name = spec->stream_name_analog;
+		/* No playback stream for second PCM */
+		info->stream[SNDRV_PCM_STREAM_PLAYBACK] = alc_pcm_null_playback;
+		info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 0;
+		if (spec->stream_analog_capture) {
+			info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_analog_capture);
+			info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[1];
+		}
+	}
+
+	return 0;
+}
+
+static void alc_free(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+	unsigned int i;
+
+	if (! spec)
+		return;
+
+	if (spec->kctl_alloc) {
+		for (i = 0; i < spec->num_kctl_used; i++)
+			kfree(spec->kctl_alloc[i].name);
+		kfree(spec->kctl_alloc);
+	}
+	kfree(spec);
+}
+
+/*
+ */
+static struct hda_codec_ops alc_patch_ops = {
+	.build_controls = alc_build_controls,
+	.build_pcms = alc_build_pcms,
+	.init = alc_init,
+	.free = alc_free,
+	.unsol_event = alc_unsol_event,
+#ifdef CONFIG_PM
+	.resume = alc_resume,
+#endif
+};
+
+
+/*
+ * Test configuration for debugging
+ *
+ * Almost all inputs/outputs are enabled.  I/O pins can be configured via
+ * enum controls.
+ */
+#ifdef CONFIG_SND_DEBUG
+static hda_nid_t alc880_test_dac_nids[4] = {
+	0x02, 0x03, 0x04, 0x05
+};
+
+static struct hda_input_mux alc880_test_capture_source = {
+	.num_items = 7,
+	.items = {
+		{ "In-1", 0x0 },
+		{ "In-2", 0x1 },
+		{ "In-3", 0x2 },
+		{ "In-4", 0x3 },
+		{ "CD", 0x4 },
+		{ "Front", 0x5 },
+		{ "Surround", 0x6 },
+	},
+};
+
+static struct hda_channel_mode alc880_test_modes[4] = {
+	{ 2, NULL },
+	{ 4, NULL },
+	{ 6, NULL },
+	{ 8, NULL },
+};
+
+static int alc_test_pin_ctl_info(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_info *uinfo)
+{
+	static char *texts[] = {
+		"N/A", "Line Out", "HP Out",
+		"In Hi-Z", "In 50%", "In Grd", "In 80%", "In 100%"
+	};
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 8;
+	if (uinfo->value.enumerated.item >= 8)
+		uinfo->value.enumerated.item = 7;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int alc_test_pin_ctl_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	hda_nid_t nid = (hda_nid_t)kcontrol->private_value;
+	unsigned int pin_ctl, item = 0;
+
+	pin_ctl = snd_hda_codec_read(codec, nid, 0,
+				     AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+	if (pin_ctl & AC_PINCTL_OUT_EN) {
+		if (pin_ctl & AC_PINCTL_HP_EN)
+			item = 2;
+		else
+			item = 1;
+	} else if (pin_ctl & AC_PINCTL_IN_EN) {
+		switch (pin_ctl & AC_PINCTL_VREFEN) {
+		case AC_PINCTL_VREF_HIZ: item = 3; break;
+		case AC_PINCTL_VREF_50:  item = 4; break;
+		case AC_PINCTL_VREF_GRD: item = 5; break;
+		case AC_PINCTL_VREF_80:  item = 6; break;
+		case AC_PINCTL_VREF_100: item = 7; break;
+		}
+	}
+	ucontrol->value.enumerated.item[0] = item;
+	return 0;
+}
+
+static int alc_test_pin_ctl_put(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	hda_nid_t nid = (hda_nid_t)kcontrol->private_value;
+	static unsigned int ctls[] = {
+		0, AC_PINCTL_OUT_EN, AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN,
+		AC_PINCTL_IN_EN | AC_PINCTL_VREF_HIZ,
+		AC_PINCTL_IN_EN | AC_PINCTL_VREF_50,
+		AC_PINCTL_IN_EN | AC_PINCTL_VREF_GRD,
+		AC_PINCTL_IN_EN | AC_PINCTL_VREF_80,
+		AC_PINCTL_IN_EN | AC_PINCTL_VREF_100,
+	};
+	unsigned int old_ctl, new_ctl;
+
+	old_ctl = snd_hda_codec_read(codec, nid, 0,
+				     AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+	new_ctl = ctls[ucontrol->value.enumerated.item[0]];
+	if (old_ctl != new_ctl) {
+		snd_hda_codec_write(codec, nid, 0,
+				    AC_VERB_SET_PIN_WIDGET_CONTROL, new_ctl);
+		snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+				    (ucontrol->value.enumerated.item[0] >= 3 ?
+				     0xb080 : 0xb000));
+		return 1;
+	}
+	return 0;
+}
+
+static int alc_test_pin_src_info(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_info *uinfo)
+{
+	static char *texts[] = {
+		"Front", "Surround", "CLFE", "Side"
+	};
+	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 = 3;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int alc_test_pin_src_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	hda_nid_t nid = (hda_nid_t)kcontrol->private_value;
+	unsigned int sel;
+
+	sel = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONNECT_SEL, 0);
+	ucontrol->value.enumerated.item[0] = sel & 3;
+	return 0;
+}
+
+static int alc_test_pin_src_put(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	hda_nid_t nid = (hda_nid_t)kcontrol->private_value;
+	unsigned int sel;
+
+	sel = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONNECT_SEL, 0) & 3;
+	if (ucontrol->value.enumerated.item[0] != sel) {
+		sel = ucontrol->value.enumerated.item[0] & 3;
+		snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, sel);
+		return 1;
+	}
+	return 0;
+}
+
+#define PIN_CTL_TEST(xname,nid) {			\
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,	\
+			.name = xname,		       \
+			.info = alc_test_pin_ctl_info, \
+			.get = alc_test_pin_ctl_get,   \
+			.put = alc_test_pin_ctl_put,   \
+			.private_value = nid	       \
+			}
+
+#define PIN_SRC_TEST(xname,nid) {			\
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,	\
+			.name = xname,		       \
+			.info = alc_test_pin_src_info, \
+			.get = alc_test_pin_src_get,   \
+			.put = alc_test_pin_src_put,   \
+			.private_value = nid	       \
+			}
+
+static struct snd_kcontrol_new alc880_test_mixer[] = {
+	HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("CLFE Playback Volume", 0x0e, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
+	HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
+	HDA_BIND_MUTE("CLFE Playback Switch", 0x0e, 2, HDA_INPUT),
+	HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT),
+	PIN_CTL_TEST("Front Pin Mode", 0x14),
+	PIN_CTL_TEST("Surround Pin Mode", 0x15),
+	PIN_CTL_TEST("CLFE Pin Mode", 0x16),
+	PIN_CTL_TEST("Side Pin Mode", 0x17),
+	PIN_CTL_TEST("In-1 Pin Mode", 0x18),
+	PIN_CTL_TEST("In-2 Pin Mode", 0x19),
+	PIN_CTL_TEST("In-3 Pin Mode", 0x1a),
+	PIN_CTL_TEST("In-4 Pin Mode", 0x1b),
+	PIN_SRC_TEST("In-1 Pin Source", 0x18),
+	PIN_SRC_TEST("In-2 Pin Source", 0x19),
+	PIN_SRC_TEST("In-3 Pin Source", 0x1a),
+	PIN_SRC_TEST("In-4 Pin Source", 0x1b),
+	HDA_CODEC_VOLUME("In-1 Playback Volume", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("In-1 Playback Switch", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("In-2 Playback Volume", 0x0b, 0x1, HDA_INPUT),
+	HDA_CODEC_MUTE("In-2 Playback Switch", 0x0b, 0x1, HDA_INPUT),
+	HDA_CODEC_VOLUME("In-3 Playback Volume", 0x0b, 0x2, HDA_INPUT),
+	HDA_CODEC_MUTE("In-3 Playback Switch", 0x0b, 0x2, HDA_INPUT),
+	HDA_CODEC_VOLUME("In-4 Playback Volume", 0x0b, 0x3, HDA_INPUT),
+	HDA_CODEC_MUTE("In-4 Playback Switch", 0x0b, 0x3, HDA_INPUT),
+	HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x4, HDA_INPUT),
+	HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x4, HDA_INPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Channel Mode",
+		.info = alc_ch_mode_info,
+		.get = alc_ch_mode_get,
+		.put = alc_ch_mode_put,
+	},
+	{0} /* end */
+};
+
+static struct hda_verb alc880_test_init_verbs[] = {
+	/* Unmute inputs of 0x0c - 0x0f */
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	/* Vol output for 0x0c-0x0f */
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	/* Set output pins 0x14-0x17 */
+	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	/* Unmute output pins 0x14-0x17 */
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	/* Set input pins 0x18-0x1c */
+	{0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+	{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+	{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+	{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+	/* Mute input pins 0x18-0x1b */
+	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	/* ADC set up */
+	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
+	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
+	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
+	/* Analog input/passthru */
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+	{0}
+};
+#endif
+
+/*
+ */
+
+static const char *alc880_models[ALC880_MODEL_LAST] = {
+	[ALC880_3ST]		= "3stack",
+	[ALC880_TCL_S700]	= "tcl",
+	[ALC880_3ST_DIG]	= "3stack-digout",
+	[ALC880_CLEVO]		= "clevo",
+	[ALC880_5ST]		= "5stack",
+	[ALC880_5ST_DIG]	= "5stack-digout",
+	[ALC880_W810]		= "w810",
+	[ALC880_Z71V]		= "z71v",
+	[ALC880_6ST]		= "6stack",
+	[ALC880_6ST_DIG]	= "6stack-digout",
+	[ALC880_ASUS]		= "asus",
+	[ALC880_ASUS_W1V]	= "asus-w1v",
+	[ALC880_ASUS_DIG]	= "asus-dig",
+	[ALC880_ASUS_DIG2]	= "asus-dig2",
+	[ALC880_UNIWILL_DIG]	= "uniwill",
+	[ALC880_F1734]		= "F1734",
+	[ALC880_LG]		= "lg",
+	[ALC880_LG_LW]		= "lg-lw",
+#ifdef CONFIG_SND_DEBUG
+	[ALC880_TEST]		= "test",
+#endif
+	[ALC880_AUTO]		= "auto",
+};
+
+static struct snd_pci_quirk alc880_cfg_tbl[] = {
+	/* Broken BIOS configuration */
+	SND_PCI_QUIRK(0x2668, 0x8086, NULL, ALC880_6ST_DIG),
+	SND_PCI_QUIRK(0x8086, 0x2668, NULL, ALC880_6ST_DIG),
+
+	SND_PCI_QUIRK(0x1019, 0xa880, "ECS", ALC880_5ST_DIG),
+        SND_PCI_QUIRK(0x1019, 0xa884, "Acer APFV", ALC880_6ST),
+        SND_PCI_QUIRK(0x1019, 0x0f69, "Coeus G610P", ALC880_W810),
+	SND_PCI_QUIRK(0x1025, 0x0070, "ULI", ALC880_3ST_DIG),
+	SND_PCI_QUIRK(0x1025, 0x0077, "ULI", ALC880_6ST_DIG),
+	SND_PCI_QUIRK(0x1025, 0x0078, "ULI", ALC880_6ST_DIG),
+	SND_PCI_QUIRK(0x1025, 0x0087, "ULI", ALC880_6ST_DIG),
+	SND_PCI_QUIRK(0x1025, 0xe309, "ULI", ALC880_3ST_DIG),
+	SND_PCI_QUIRK(0x1025, 0xe310, "ULI", ALC880_3ST),
+
+	SND_PCI_QUIRK(0x1039, 0x1234, NULL, ALC880_6ST_DIG),
+	SND_PCI_QUIRK(0x103c, 0x2a09, "HP", ALC880_5ST),
+
+	SND_PCI_QUIRK(0x1043, 0x10b3, "ASUS W1V", ALC880_ASUS_W1V),
+	SND_PCI_QUIRK(0x1043, 0x10c2, "ASUS W6A", ALC880_ASUS_DIG),
+	SND_PCI_QUIRK(0x1043, 0x10c3, "ASUS Wxx", ALC880_ASUS_DIG),
+	SND_PCI_QUIRK(0x1043, 0x1113, "ASUS", ALC880_ASUS_DIG),
+	SND_PCI_QUIRK(0x1043, 0x1123, "ASUS", ALC880_ASUS_DIG),
+	SND_PCI_QUIRK(0x1043, 0x1173, "ASUS", ALC880_ASUS_DIG),
+	SND_PCI_QUIRK(0x1043, 0x1964, "ASUS Z71V", ALC880_Z71V),
+	/* SND_PCI_QUIRK(0x1043, 0x1964, "ASUS", ALC880_ASUS_DIG), */
+	SND_PCI_QUIRK(0x1043, 0x1973, "ASUS", ALC880_ASUS_DIG),
+        SND_PCI_QUIRK(0x1043, 0x19b3, "ASUS", ALC880_ASUS_DIG),
+        SND_PCI_QUIRK(0x1043, 0x814e, "ASUS", ALC880_ASUS),
+	SND_PCI_QUIRK(0x1043, 0x8181, "ASUS P4GPL", ALC880_ASUS_DIG),
+	SND_PCI_QUIRK(0x1043, 0x8196, "ASUS P5GD1", ALC880_6ST),
+	SND_PCI_QUIRK(0x1043, 0x81b4, "ASUS", ALC880_6ST),
+	SND_PCI_QUIRK(0x1043, 0, "ASUS", ALC880_ASUS),
+
+	SND_PCI_QUIRK(0x104d, 0x81d6, "Sony", ALC880_3ST),
+	SND_PCI_QUIRK(0x104d, 0x81a0, "Sony", ALC880_3ST),
+	SND_PCI_QUIRK(0x107b, 0x3033, "Gateway", ALC880_5ST),
+	SND_PCI_QUIRK(0x107b, 0x4039, "Gateway", ALC880_5ST),
+	SND_PCI_QUIRK(0x107b, 0x3032, "Gateway", ALC880_5ST),
+	SND_PCI_QUIRK(0x1558, 0x0520, "Clevo m520G", ALC880_CLEVO),
+	SND_PCI_QUIRK(0x1558, 0x0660, "Clevo m655n", ALC880_CLEVO),
+	SND_PCI_QUIRK(0x1565, 0x8202, "Biostar", ALC880_5ST_DIG),
+	SND_PCI_QUIRK(0x161f, 0x203d, "W810", ALC880_W810),
+	SND_PCI_QUIRK(0x1695, 0x400d, "EPoX", ALC880_5ST_DIG),
+	SND_PCI_QUIRK(0x19db, 0x4188, "TCL S700", ALC880_TCL_S700),
+	SND_PCI_QUIRK(0xa0a0, 0x0560, "AOpen i915GMm-HFS", ALC880_5ST_DIG),
+	SND_PCI_QUIRK(0xe803, 0x1019, NULL, ALC880_6ST_DIG),
+	SND_PCI_QUIRK(0x1297, 0xc790, "Shuttle ST20G5", ALC880_6ST_DIG),
+	SND_PCI_QUIRK(0x1458, 0xa102, "Gigabyte K8", ALC880_6ST_DIG),
+	SND_PCI_QUIRK(0x1462, 0x1150, "MSI", ALC880_6ST_DIG),
+	SND_PCI_QUIRK(0x1509, 0x925d, "FIC P4M", ALC880_6ST_DIG),
+	SND_PCI_QUIRK(0x1558, 0x5401, "ASUS", ALC880_ASUS_DIG2),
+
+	SND_PCI_QUIRK(0x1584, 0x9050, "Uniwill", ALC880_UNIWILL_DIG),
+	SND_PCI_QUIRK(0x1584, 0x9070, "Uniwill", ALC880_UNIWILL),
+	SND_PCI_QUIRK(0x1584, 0x9077, "Uniwill P53", ALC880_UNIWILL_P53),
+	SND_PCI_QUIRK(0x1584, 0x9054, "Uniwlll", ALC880_F1734),
+
+	SND_PCI_QUIRK(0x1695, 0x4012, "EPox EP-5LDA", ALC880_5ST_DIG),
+	SND_PCI_QUIRK(0x1734, 0x10ac, "FSC", ALC880_UNIWILL),
+	SND_PCI_QUIRK(0x1734, 0x107c, "FSC F1734", ALC880_F1734),
+
+	SND_PCI_QUIRK(0x1854, 0x003b, "LG", ALC880_LG),
+	SND_PCI_QUIRK(0x1854, 0x0068, "LG w1", ALC880_LG),
+	SND_PCI_QUIRK(0x1854, 0x0018, "LG LW20", ALC880_LG_LW),
+	SND_PCI_QUIRK(0x1854, 0x0077, "LG LW25", ALC880_LG_LW),
+
+	SND_PCI_QUIRK(0x8086, 0xe308, "Intel mobo", ALC880_3ST_DIG),
+	SND_PCI_QUIRK(0x8086, 0xe305, "Intel mobo", ALC880_3ST_DIG),
+	SND_PCI_QUIRK(0x8086, 0xd402, "Intel mobo", ALC880_3ST_DIG),
+	SND_PCI_QUIRK(0x8086, 0xd400, "Intel mobo", ALC880_5ST_DIG),
+	SND_PCI_QUIRK(0x8086, 0xd401, "Intel mobo", ALC880_5ST_DIG),
+	SND_PCI_QUIRK(0x8086, 0xe224, "Intel mobo", ALC880_5ST_DIG),
+	SND_PCI_QUIRK(0x8086, 0xe400, "Intel mobo", ALC880_5ST_DIG),
+	SND_PCI_QUIRK(0x8086, 0xe401, "Intel mobo", ALC880_5ST_DIG),
+	SND_PCI_QUIRK(0x8086, 0xe402, "Intel mobo", ALC880_5ST_DIG),
+	SND_PCI_QUIRK(0x8086, 0xa100, "Intel mobo", ALC880_5ST_DIG),
+	SND_PCI_QUIRK(0x8086, 0, "Intel mobo", ALC880_3ST),
+
+	{0}
+};
+
+/*
+ * ALC880 codec presets
+ */
+static struct alc_config_preset alc880_presets[] = {
+	[ALC880_3ST] = {
+		.mixers = { alc880_three_stack_mixer },
+		.init_verbs = { alc880_volume_init_verbs, alc880_pin_3stack_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc880_dac_nids),
+		.dac_nids = alc880_dac_nids,
+		.num_channel_mode = ARRAY_SIZE(alc880_threestack_modes),
+		.channel_mode = alc880_threestack_modes,
+		.need_dac_fix = 1,
+		.input_mux = &alc880_capture_source,
+	},
+	[ALC880_3ST_DIG] = {
+		.mixers = { alc880_three_stack_mixer },
+		.init_verbs = { alc880_volume_init_verbs, alc880_pin_3stack_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc880_dac_nids),
+		.dac_nids = alc880_dac_nids,
+		.dig_out_nid = ALC880_DIGOUT_NID,
+		.num_channel_mode = ARRAY_SIZE(alc880_threestack_modes),
+		.channel_mode = alc880_threestack_modes,
+		.need_dac_fix = 1,
+		.input_mux = &alc880_capture_source,
+	},
+	[ALC880_TCL_S700] = {
+		.mixers = { alc880_tcl_s700_mixer },
+		.init_verbs = { alc880_volume_init_verbs,
+				alc880_pin_tcl_S700_init_verbs,
+				alc880_gpio2_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc880_dac_nids),
+		.dac_nids = alc880_dac_nids,
+		.hp_nid = 0x03,
+		.num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes),
+		.channel_mode = alc880_2_jack_modes,
+		.input_mux = &alc880_capture_source,
+	},
+	[ALC880_5ST] = {
+		.mixers = { alc880_three_stack_mixer, alc880_five_stack_mixer},
+		.init_verbs = { alc880_volume_init_verbs, alc880_pin_5stack_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc880_dac_nids),
+		.dac_nids = alc880_dac_nids,
+		.num_channel_mode = ARRAY_SIZE(alc880_fivestack_modes),
+		.channel_mode = alc880_fivestack_modes,
+		.input_mux = &alc880_capture_source,
+	},
+	[ALC880_5ST_DIG] = {
+		.mixers = { alc880_three_stack_mixer, alc880_five_stack_mixer },
+		.init_verbs = { alc880_volume_init_verbs, alc880_pin_5stack_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc880_dac_nids),
+		.dac_nids = alc880_dac_nids,
+		.dig_out_nid = ALC880_DIGOUT_NID,
+		.num_channel_mode = ARRAY_SIZE(alc880_fivestack_modes),
+		.channel_mode = alc880_fivestack_modes,
+		.input_mux = &alc880_capture_source,
+	},
+	[ALC880_6ST] = {
+		.mixers = { alc880_six_stack_mixer },
+		.init_verbs = { alc880_volume_init_verbs, alc880_pin_6stack_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc880_6st_dac_nids),
+		.dac_nids = alc880_6st_dac_nids,
+		.num_channel_mode = ARRAY_SIZE(alc880_sixstack_modes),
+		.channel_mode = alc880_sixstack_modes,
+		.input_mux = &alc880_6stack_capture_source,
+	},
+	[ALC880_6ST_DIG] = {
+		.mixers = { alc880_six_stack_mixer },
+		.init_verbs = { alc880_volume_init_verbs, alc880_pin_6stack_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc880_6st_dac_nids),
+		.dac_nids = alc880_6st_dac_nids,
+		.dig_out_nid = ALC880_DIGOUT_NID,
+		.num_channel_mode = ARRAY_SIZE(alc880_sixstack_modes),
+		.channel_mode = alc880_sixstack_modes,
+		.input_mux = &alc880_6stack_capture_source,
+	},
+	[ALC880_W810] = {
+		.mixers = { alc880_w810_base_mixer },
+		.init_verbs = { alc880_volume_init_verbs, alc880_pin_w810_init_verbs,
+				alc880_gpio2_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc880_w810_dac_nids),
+		.dac_nids = alc880_w810_dac_nids,
+		.dig_out_nid = ALC880_DIGOUT_NID,
+		.num_channel_mode = ARRAY_SIZE(alc880_w810_modes),
+		.channel_mode = alc880_w810_modes,
+		.input_mux = &alc880_capture_source,
+	},
+	[ALC880_Z71V] = {
+		.mixers = { alc880_z71v_mixer },
+		.init_verbs = { alc880_volume_init_verbs, alc880_pin_z71v_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc880_z71v_dac_nids),
+		.dac_nids = alc880_z71v_dac_nids,
+		.dig_out_nid = ALC880_DIGOUT_NID,
+		.hp_nid = 0x03,
+		.num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes),
+		.channel_mode = alc880_2_jack_modes,
+		.input_mux = &alc880_capture_source,
+	},
+	[ALC880_F1734] = {
+		.mixers = { alc880_f1734_mixer },
+		.init_verbs = { alc880_volume_init_verbs, alc880_pin_f1734_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc880_f1734_dac_nids),
+		.dac_nids = alc880_f1734_dac_nids,
+		.hp_nid = 0x02,
+		.num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes),
+		.channel_mode = alc880_2_jack_modes,
+		.input_mux = &alc880_capture_source,
+	},
+	[ALC880_ASUS] = {
+		.mixers = { alc880_asus_mixer },
+		.init_verbs = { alc880_volume_init_verbs, alc880_pin_asus_init_verbs,
+				alc880_gpio1_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc880_asus_dac_nids),
+		.dac_nids = alc880_asus_dac_nids,
+		.num_channel_mode = ARRAY_SIZE(alc880_asus_modes),
+		.channel_mode = alc880_asus_modes,
+		.need_dac_fix = 1,
+		.input_mux = &alc880_capture_source,
+	},
+	[ALC880_ASUS_DIG] = {
+		.mixers = { alc880_asus_mixer },
+		.init_verbs = { alc880_volume_init_verbs, alc880_pin_asus_init_verbs,
+				alc880_gpio1_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc880_asus_dac_nids),
+		.dac_nids = alc880_asus_dac_nids,
+		.dig_out_nid = ALC880_DIGOUT_NID,
+		.num_channel_mode = ARRAY_SIZE(alc880_asus_modes),
+		.channel_mode = alc880_asus_modes,
+		.need_dac_fix = 1,
+		.input_mux = &alc880_capture_source,
+	},
+	[ALC880_ASUS_DIG2] = {
+		.mixers = { alc880_asus_mixer },
+		.init_verbs = { alc880_volume_init_verbs, alc880_pin_asus_init_verbs,
+				alc880_gpio2_init_verbs }, /* use GPIO2 */
+		.num_dacs = ARRAY_SIZE(alc880_asus_dac_nids),
+		.dac_nids = alc880_asus_dac_nids,
+		.dig_out_nid = ALC880_DIGOUT_NID,
+		.num_channel_mode = ARRAY_SIZE(alc880_asus_modes),
+		.channel_mode = alc880_asus_modes,
+		.need_dac_fix = 1,
+		.input_mux = &alc880_capture_source,
+	},
+	[ALC880_ASUS_W1V] = {
+		.mixers = { alc880_asus_mixer, alc880_asus_w1v_mixer },
+		.init_verbs = { alc880_volume_init_verbs, alc880_pin_asus_init_verbs,
+				alc880_gpio1_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc880_asus_dac_nids),
+		.dac_nids = alc880_asus_dac_nids,
+		.dig_out_nid = ALC880_DIGOUT_NID,
+		.num_channel_mode = ARRAY_SIZE(alc880_asus_modes),
+		.channel_mode = alc880_asus_modes,
+		.need_dac_fix = 1,
+		.input_mux = &alc880_capture_source,
+	},
+	[ALC880_UNIWILL_DIG] = {
+		.mixers = { alc880_asus_mixer, alc880_pcbeep_mixer },
+		.init_verbs = { alc880_volume_init_verbs,
+				alc880_pin_asus_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc880_asus_dac_nids),
+		.dac_nids = alc880_asus_dac_nids,
+		.dig_out_nid = ALC880_DIGOUT_NID,
+		.num_channel_mode = ARRAY_SIZE(alc880_asus_modes),
+		.channel_mode = alc880_asus_modes,
+		.need_dac_fix = 1,
+		.input_mux = &alc880_capture_source,
+	},
+	[ALC880_UNIWILL] = {
+		.mixers = { alc880_uniwill_mixer },
+		.init_verbs = { alc880_volume_init_verbs,
+				alc880_uniwill_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc880_asus_dac_nids),
+		.dac_nids = alc880_asus_dac_nids,
+		.dig_out_nid = ALC880_DIGOUT_NID,
+		.num_channel_mode = ARRAY_SIZE(alc880_threestack_modes),
+		.channel_mode = alc880_threestack_modes,
+		.need_dac_fix = 1,
+		.input_mux = &alc880_capture_source,
+		.unsol_event = alc880_uniwill_unsol_event,
+		.init_hook = alc880_uniwill_automute,
+	},
+	[ALC880_UNIWILL_P53] = {
+		.mixers = { alc880_uniwill_p53_mixer },
+		.init_verbs = { alc880_volume_init_verbs,
+				alc880_uniwill_p53_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc880_asus_dac_nids),
+		.dac_nids = alc880_asus_dac_nids,
+		.num_channel_mode = ARRAY_SIZE(alc880_w810_modes),
+		.channel_mode = alc880_w810_modes,
+		.input_mux = &alc880_capture_source,
+		.unsol_event = alc880_uniwill_p53_unsol_event,
+		.init_hook = alc880_uniwill_p53_hp_automute,
+	},
+	[ALC880_CLEVO] = {
+		.mixers = { alc880_three_stack_mixer },
+		.init_verbs = { alc880_volume_init_verbs,
+				alc880_pin_clevo_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc880_dac_nids),
+		.dac_nids = alc880_dac_nids,
+		.hp_nid = 0x03,
+		.num_channel_mode = ARRAY_SIZE(alc880_threestack_modes),
+		.channel_mode = alc880_threestack_modes,
+		.need_dac_fix = 1,
+		.input_mux = &alc880_capture_source,
+	},
+	[ALC880_LG] = {
+		.mixers = { alc880_lg_mixer },
+		.init_verbs = { alc880_volume_init_verbs,
+				alc880_lg_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc880_lg_dac_nids),
+		.dac_nids = alc880_lg_dac_nids,
+		.dig_out_nid = ALC880_DIGOUT_NID,
+		.num_channel_mode = ARRAY_SIZE(alc880_lg_ch_modes),
+		.channel_mode = alc880_lg_ch_modes,
+		.need_dac_fix = 1,
+		.input_mux = &alc880_lg_capture_source,
+		.unsol_event = alc880_lg_unsol_event,
+		.init_hook = alc880_lg_automute,
+	},
+	[ALC880_LG_LW] = {
+		.mixers = { alc880_lg_lw_mixer },
+		.init_verbs = { alc880_volume_init_verbs,
+				alc880_lg_lw_init_verbs },
+		.num_dacs = 1, 
+		.dac_nids = alc880_dac_nids,
+		.dig_out_nid = ALC880_DIGOUT_NID,
+		.num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes),
+		.channel_mode = alc880_2_jack_modes,
+		.input_mux = &alc880_lg_lw_capture_source,
+		.unsol_event = alc880_lg_lw_unsol_event,
+		.init_hook = alc880_lg_lw_automute,
+	},
+#ifdef CONFIG_SND_DEBUG
+	[ALC880_TEST] = {
+		.mixers = { alc880_test_mixer },
+		.init_verbs = { alc880_test_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc880_test_dac_nids),
+		.dac_nids = alc880_test_dac_nids,
+		.dig_out_nid = ALC880_DIGOUT_NID,
+		.num_channel_mode = ARRAY_SIZE(alc880_test_modes),
+		.channel_mode = alc880_test_modes,
+		.input_mux = &alc880_test_capture_source,
+	},
+#endif
+};
+
+/*
+ * Automatic parse of I/O pins from the BIOS configuration
+ */
+
+#define NUM_CONTROL_ALLOC	32
+#define NUM_VERB_ALLOC		32
+
+enum {
+	ALC_CTL_WIDGET_VOL,
+	ALC_CTL_WIDGET_MUTE,
+	ALC_CTL_BIND_MUTE,
+};
+static struct snd_kcontrol_new alc880_control_templates[] = {
+	HDA_CODEC_VOLUME(NULL, 0, 0, 0),
+	HDA_CODEC_MUTE(NULL, 0, 0, 0),
+	HDA_BIND_MUTE(NULL, 0, 0, 0),
+};
+
+/* add dynamic controls */
+static int add_control(struct alc_spec *spec, int type, const char *name, unsigned long val)
+{
+	struct snd_kcontrol_new *knew;
+
+	if (spec->num_kctl_used >= spec->num_kctl_alloc) {
+		int num = spec->num_kctl_alloc + NUM_CONTROL_ALLOC;
+
+		knew = kcalloc(num + 1, sizeof(*knew), GFP_KERNEL); /* array + terminator */
+		if (! knew)
+			return -ENOMEM;
+		if (spec->kctl_alloc) {
+			memcpy(knew, spec->kctl_alloc, sizeof(*knew) * spec->num_kctl_alloc);
+			kfree(spec->kctl_alloc);
+		}
+		spec->kctl_alloc = knew;
+		spec->num_kctl_alloc = num;
+	}
+
+	knew = &spec->kctl_alloc[spec->num_kctl_used];
+	*knew = alc880_control_templates[type];
+	knew->name = kstrdup(name, GFP_KERNEL);
+	if (! knew->name)
+		return -ENOMEM;
+	knew->private_value = val;
+	spec->num_kctl_used++;
+	return 0;
+}
+
+#define alc880_is_fixed_pin(nid)	((nid) >= 0x14 && (nid) <= 0x17)
+#define alc880_fixed_pin_idx(nid)	((nid) - 0x14)
+#define alc880_is_multi_pin(nid)	((nid) >= 0x18)
+#define alc880_multi_pin_idx(nid)	((nid) - 0x18)
+#define alc880_is_input_pin(nid)	((nid) >= 0x18)
+#define alc880_input_pin_idx(nid)	((nid) - 0x18)
+#define alc880_idx_to_dac(nid)		((nid) + 0x02)
+#define alc880_dac_to_idx(nid)		((nid) - 0x02)
+#define alc880_idx_to_mixer(nid)	((nid) + 0x0c)
+#define alc880_idx_to_selector(nid)	((nid) + 0x10)
+#define ALC880_PIN_CD_NID		0x1c
+
+/* fill in the dac_nids table from the parsed pin configuration */
+static int alc880_auto_fill_dac_nids(struct alc_spec *spec, const struct auto_pin_cfg *cfg)
+{
+	hda_nid_t nid;
+	int assigned[4];
+	int i, j;
+
+	memset(assigned, 0, sizeof(assigned));
+	spec->multiout.dac_nids = spec->private_dac_nids;
+
+	/* check the pins hardwired to audio widget */
+	for (i = 0; i < cfg->line_outs; i++) {
+		nid = cfg->line_out_pins[i];
+		if (alc880_is_fixed_pin(nid)) {
+			int idx = alc880_fixed_pin_idx(nid);
+			spec->multiout.dac_nids[i] = alc880_idx_to_dac(idx);
+			assigned[idx] = 1;
+		}
+	}
+	/* left pins can be connect to any audio widget */
+	for (i = 0; i < cfg->line_outs; i++) {
+		nid = cfg->line_out_pins[i];
+		if (alc880_is_fixed_pin(nid))
+			continue;
+		/* search for an empty channel */
+		for (j = 0; j < cfg->line_outs; j++) {
+			if (! assigned[j]) {
+				spec->multiout.dac_nids[i] = alc880_idx_to_dac(j);
+				assigned[j] = 1;
+				break;
+			}
+		}
+	}
+	spec->multiout.num_dacs = cfg->line_outs;
+	return 0;
+}
+
+/* add playback controls from the parsed DAC table */
+static int alc880_auto_create_multi_out_ctls(struct alc_spec *spec,
+					     const struct auto_pin_cfg *cfg)
+{
+	char name[32];
+	static const char *chname[4] = { "Front", "Surround", NULL /*CLFE*/, "Side" };
+	hda_nid_t nid;
+	int i, err;
+
+	for (i = 0; i < cfg->line_outs; i++) {
+		if (! spec->multiout.dac_nids[i])
+			continue;
+		nid = alc880_idx_to_mixer(alc880_dac_to_idx(spec->multiout.dac_nids[i]));
+		if (i == 2) {
+			/* Center/LFE */
+			if ((err = add_control(spec, ALC_CTL_WIDGET_VOL, "Center Playback Volume",
+					       HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT))) < 0)
+				return err;
+			if ((err = add_control(spec, ALC_CTL_WIDGET_VOL, "LFE Playback Volume",
+					       HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT))) < 0)
+				return err;
+			if ((err = add_control(spec, ALC_CTL_BIND_MUTE, "Center Playback Switch",
+					       HDA_COMPOSE_AMP_VAL(nid, 1, 2, HDA_INPUT))) < 0)
+				return err;
+			if ((err = add_control(spec, ALC_CTL_BIND_MUTE, "LFE Playback Switch",
+					       HDA_COMPOSE_AMP_VAL(nid, 2, 2, HDA_INPUT))) < 0)
+				return err;
+		} else {
+			sprintf(name, "%s Playback Volume", chname[i]);
+			if ((err = add_control(spec, ALC_CTL_WIDGET_VOL, name,
+					       HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT))) < 0)
+				return err;
+			sprintf(name, "%s Playback Switch", chname[i]);
+			if ((err = add_control(spec, ALC_CTL_BIND_MUTE, name,
+					       HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT))) < 0)
+				return err;
+		}
+	}
+	return 0;
+}
+
+/* add playback controls for speaker and HP outputs */
+static int alc880_auto_create_extra_out(struct alc_spec *spec, hda_nid_t pin,
+					const char *pfx)
+{
+	hda_nid_t nid;
+	int err;
+	char name[32];
+
+	if (! pin)
+		return 0;
+
+	if (alc880_is_fixed_pin(pin)) {
+		nid = alc880_idx_to_dac(alc880_fixed_pin_idx(pin));
+		/* specify the DAC as the extra output */
+		if (! spec->multiout.hp_nid)
+			spec->multiout.hp_nid = nid;
+		else
+			spec->multiout.extra_out_nid[0] = nid;
+		/* control HP volume/switch on the output mixer amp */
+		nid = alc880_idx_to_mixer(alc880_fixed_pin_idx(pin));
+		sprintf(name, "%s Playback Volume", pfx);
+		if ((err = add_control(spec, ALC_CTL_WIDGET_VOL, name,
+				       HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT))) < 0)
+			return err;
+		sprintf(name, "%s Playback Switch", pfx);
+		if ((err = add_control(spec, ALC_CTL_BIND_MUTE, name,
+				       HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT))) < 0)
+			return err;
+	} else if (alc880_is_multi_pin(pin)) {
+		/* set manual connection */
+		/* we have only a switch on HP-out PIN */
+		sprintf(name, "%s Playback Switch", pfx);
+		if ((err = add_control(spec, ALC_CTL_WIDGET_MUTE, name,
+				       HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT))) < 0)
+			return err;
+	}
+	return 0;
+}
+
+/* create input playback/capture controls for the given pin */
+static int new_analog_input(struct alc_spec *spec, hda_nid_t pin, const char *ctlname,
+			    int idx, hda_nid_t mix_nid)
+{
+	char name[32];
+	int err;
+
+	sprintf(name, "%s Playback Volume", ctlname);
+	if ((err = add_control(spec, ALC_CTL_WIDGET_VOL, name,
+			       HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT))) < 0)
+		return err;
+	sprintf(name, "%s Playback Switch", ctlname);
+	if ((err = add_control(spec, ALC_CTL_WIDGET_MUTE, name,
+			       HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT))) < 0)
+		return err;
+	return 0;
+}
+
+/* create playback/capture controls for input pins */
+static int alc880_auto_create_analog_input_ctls(struct alc_spec *spec,
+						const struct auto_pin_cfg *cfg)
+{
+	struct hda_input_mux *imux = &spec->private_imux;
+	int i, err, idx;
+
+	for (i = 0; i < AUTO_PIN_LAST; i++) {
+		if (alc880_is_input_pin(cfg->input_pins[i])) {
+			idx = alc880_input_pin_idx(cfg->input_pins[i]);
+			err = new_analog_input(spec, cfg->input_pins[i],
+					       auto_pin_cfg_labels[i],
+					       idx, 0x0b);
+			if (err < 0)
+				return err;
+			imux->items[imux->num_items].label = auto_pin_cfg_labels[i];
+			imux->items[imux->num_items].index = alc880_input_pin_idx(cfg->input_pins[i]);
+			imux->num_items++;
+		}
+	}
+	return 0;
+}
+
+static void alc880_auto_set_output_and_unmute(struct hda_codec *codec,
+					      hda_nid_t nid, int pin_type,
+					      int dac_idx)
+{
+	/* set as output */
+	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);
+	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
+	/* need the manual connection? */
+	if (alc880_is_multi_pin(nid)) {
+		struct alc_spec *spec = codec->spec;
+		int idx = alc880_multi_pin_idx(nid);
+		snd_hda_codec_write(codec, alc880_idx_to_selector(idx), 0,
+				    AC_VERB_SET_CONNECT_SEL,
+				    alc880_dac_to_idx(spec->multiout.dac_nids[dac_idx]));
+	}
+}
+
+static void alc880_auto_init_multi_out(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+	int i;
+
+	for (i = 0; i < spec->autocfg.line_outs; i++) {
+		hda_nid_t nid = spec->autocfg.line_out_pins[i];
+		alc880_auto_set_output_and_unmute(codec, nid, PIN_OUT, i);
+	}
+}
+
+static void alc880_auto_init_extra_out(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+	hda_nid_t pin;
+
+	pin = spec->autocfg.speaker_pins[0];
+	if (pin) /* connect to front */
+		alc880_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);
+	pin = spec->autocfg.hp_pins[0];
+	if (pin) /* connect to front */
+		alc880_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
+}
+
+static void alc880_auto_init_analog_input(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+	int i;
+
+	for (i = 0; i < AUTO_PIN_LAST; i++) {
+		hda_nid_t nid = spec->autocfg.input_pins[i];
+		if (alc880_is_input_pin(nid)) {
+			snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+					    i <= AUTO_PIN_FRONT_MIC ? PIN_VREF80 : PIN_IN);
+			if (nid != ALC880_PIN_CD_NID)
+				snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+						    AMP_OUT_MUTE);
+		}
+	}
+}
+
+/* parse the BIOS configuration and set up the alc_spec */
+/* return 1 if successful, 0 if the proper config is not found, or a negative error code */
+static int alc880_parse_auto_config(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+	int err;
+	static hda_nid_t alc880_ignore[] = { 0x1d, 0 };
+
+	if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
+						alc880_ignore)) < 0)
+		return err;
+	if (! spec->autocfg.line_outs)
+		return 0; /* can't find valid BIOS pin config */
+
+	if ((err = alc880_auto_fill_dac_nids(spec, &spec->autocfg)) < 0 ||
+	    (err = alc880_auto_create_multi_out_ctls(spec, &spec->autocfg)) < 0 ||
+	    (err = alc880_auto_create_extra_out(spec,
+						spec->autocfg.speaker_pins[0],
+						"Speaker")) < 0 ||
+	    (err = alc880_auto_create_extra_out(spec, spec->autocfg.hp_pins[0],
+						"Headphone")) < 0 ||
+	    (err = alc880_auto_create_analog_input_ctls(spec, &spec->autocfg)) < 0)
+		return err;
+
+	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
+
+	if (spec->autocfg.dig_out_pin)
+		spec->multiout.dig_out_nid = ALC880_DIGOUT_NID;
+	if (spec->autocfg.dig_in_pin)
+		spec->dig_in_nid = ALC880_DIGIN_NID;
+
+	if (spec->kctl_alloc)
+		spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+
+	spec->init_verbs[spec->num_init_verbs++] = alc880_volume_init_verbs;
+
+	spec->num_mux_defs = 1;
+	spec->input_mux = &spec->private_imux;
+
+	return 1;
+}
+
+/* additional initialization for auto-configuration model */
+static void alc880_auto_init(struct hda_codec *codec)
+{
+	alc880_auto_init_multi_out(codec);
+	alc880_auto_init_extra_out(codec);
+	alc880_auto_init_analog_input(codec);
+}
+
+/*
+ * OK, here we have finally the patch for ALC880
+ */
+
+static int patch_alc880(struct hda_codec *codec)
+{
+	struct alc_spec *spec;
+	int board_config;
+	int err;
+
+	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+	if (spec == NULL)
+		return -ENOMEM;
+
+	codec->spec = spec;
+
+	board_config = snd_hda_check_board_config(codec, ALC880_MODEL_LAST,
+						  alc880_models,
+						  alc880_cfg_tbl);
+	if (board_config < 0) {
+		printk(KERN_INFO "hda_codec: Unknown model for ALC880, "
+		       "trying auto-probe from BIOS...\n");
+		board_config = ALC880_AUTO;
+	}
+
+	if (board_config == ALC880_AUTO) {
+		/* automatic parse from the BIOS config */
+		err = alc880_parse_auto_config(codec);
+		if (err < 0) {
+			alc_free(codec);
+			return err;
+		} else if (! err) {
+			printk(KERN_INFO
+			       "hda_codec: Cannot set up configuration "
+			       "from BIOS.  Using 3-stack mode...\n");
+			board_config = ALC880_3ST;
+		}
+	}
+
+	if (board_config != ALC880_AUTO)
+		setup_preset(spec, &alc880_presets[board_config]);
+
+	spec->stream_name_analog = "ALC880 Analog";
+	spec->stream_analog_playback = &alc880_pcm_analog_playback;
+	spec->stream_analog_capture = &alc880_pcm_analog_capture;
+
+	spec->stream_name_digital = "ALC880 Digital";
+	spec->stream_digital_playback = &alc880_pcm_digital_playback;
+	spec->stream_digital_capture = &alc880_pcm_digital_capture;
+
+	if (! spec->adc_nids && spec->input_mux) {
+		/* check whether NID 0x07 is valid */
+		unsigned int wcap = get_wcaps(codec, alc880_adc_nids[0]);
+		wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; /* get type */
+		if (wcap != AC_WID_AUD_IN) {
+			spec->adc_nids = alc880_adc_nids_alt;
+			spec->num_adc_nids = ARRAY_SIZE(alc880_adc_nids_alt);
+			spec->mixers[spec->num_mixers] = alc880_capture_alt_mixer;
+			spec->num_mixers++;
+		} else {
+			spec->adc_nids = alc880_adc_nids;
+			spec->num_adc_nids = ARRAY_SIZE(alc880_adc_nids);
+			spec->mixers[spec->num_mixers] = alc880_capture_mixer;
+			spec->num_mixers++;
+		}
+	}
+
+	codec->patch_ops = alc_patch_ops;
+	if (board_config == ALC880_AUTO)
+		spec->init_hook = alc880_auto_init;
+
+	return 0;
+}
+
+
+/*
+ * ALC260 support
+ */
+
+static hda_nid_t alc260_dac_nids[1] = {
+	/* front */
+	0x02,
+};
+
+static hda_nid_t alc260_adc_nids[1] = {
+	/* ADC0 */
+	0x04,
+};
+
+static hda_nid_t alc260_adc_nids_alt[1] = {
+	/* ADC1 */
+	0x05,
+};
+
+static hda_nid_t alc260_hp_adc_nids[2] = {
+	/* ADC1, 0 */
+	0x05, 0x04
+};
+
+/* NIDs used when simultaneous access to both ADCs makes sense.  Note that
+ * alc260_capture_mixer assumes ADC0 (nid 0x04) is the first ADC.
+ */
+static hda_nid_t alc260_dual_adc_nids[2] = {
+	/* ADC0, ADC1 */
+	0x04, 0x05
+};
+
+#define ALC260_DIGOUT_NID	0x03
+#define ALC260_DIGIN_NID	0x06
+
+static struct hda_input_mux alc260_capture_source = {
+	.num_items = 4,
+	.items = {
+		{ "Mic", 0x0 },
+		{ "Front Mic", 0x1 },
+		{ "Line", 0x2 },
+		{ "CD", 0x4 },
+	},
+};
+
+/* On Fujitsu S702x laptops capture only makes sense from Mic/LineIn jack,
+ * headphone jack and the internal CD lines since these are the only pins at
+ * which audio can appear.  For flexibility, also allow the option of
+ * recording the mixer output on the second ADC (ADC0 doesn't have a
+ * connection to the mixer output).
+ */
+static struct hda_input_mux alc260_fujitsu_capture_sources[2] = {
+	{
+		.num_items = 3,
+		.items = {
+			{ "Mic/Line", 0x0 },
+			{ "CD", 0x4 },
+			{ "Headphone", 0x2 },
+		},
+	},
+	{
+		.num_items = 4,
+		.items = {
+			{ "Mic/Line", 0x0 },
+			{ "CD", 0x4 },
+			{ "Headphone", 0x2 },
+			{ "Mixer", 0x5 },
+		},
+	},
+
+};
+
+/* Acer TravelMate(/Extensa/Aspire) notebooks have similar configuration to
+ * the Fujitsu S702x, but jacks are marked differently.
+ */
+static struct hda_input_mux alc260_acer_capture_sources[2] = {
+	{
+		.num_items = 4,
+		.items = {
+			{ "Mic", 0x0 },
+			{ "Line", 0x2 },
+			{ "CD", 0x4 },
+			{ "Headphone", 0x5 },
+		},
+	},
+	{
+		.num_items = 5,
+		.items = {
+			{ "Mic", 0x0 },
+			{ "Line", 0x2 },
+			{ "CD", 0x4 },
+			{ "Headphone", 0x6 },
+			{ "Mixer", 0x5 },
+		},
+	},
+};
+/*
+ * This is just place-holder, so there's something for alc_build_pcms to look
+ * at when it calculates the maximum number of channels. ALC260 has no mixer
+ * element which allows changing the channel mode, so the verb list is
+ * never used.
+ */
+static struct hda_channel_mode alc260_modes[1] = {
+	{ 2, NULL },
+};
+
+
+/* Mixer combinations
+ *
+ * basic: base_output + input + pc_beep + capture
+ * HP: base_output + input + capture_alt
+ * HP_3013: hp_3013 + input + capture
+ * fujitsu: fujitsu + capture
+ * acer: acer + capture
+ */
+
+static struct snd_kcontrol_new alc260_base_output_mixer[] = {
+	HDA_CODEC_VOLUME("Front Playback Volume", 0x08, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("Front Playback Switch", 0x08, 2, HDA_INPUT),
+	HDA_CODEC_VOLUME("Headphone Playback Volume", 0x09, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("Headphone Playback Switch", 0x09, 2, HDA_INPUT),
+	HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE_MONO("Mono Playback Switch", 0x0a, 1, 2, HDA_INPUT),
+	{0} /* end */
+};	
+
+static struct snd_kcontrol_new alc260_input_mixer[] = {
+	HDA_CODEC_VOLUME("CD Playback Volume", 0x07, 0x04, HDA_INPUT),
+	HDA_CODEC_MUTE("CD Playback Switch", 0x07, 0x04, HDA_INPUT),
+	HDA_CODEC_VOLUME("Line Playback Volume", 0x07, 0x02, HDA_INPUT),
+	HDA_CODEC_MUTE("Line Playback Switch", 0x07, 0x02, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x07, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x07, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x07, 0x01, HDA_INPUT),
+	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x07, 0x01, HDA_INPUT),
+	{0} /* end */
+};
+
+static struct snd_kcontrol_new alc260_pc_beep_mixer[] = {
+	HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x07, 0x05, HDA_INPUT),
+	HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x07, 0x05, HDA_INPUT),
+	{0} /* end */
+};
+
+static struct snd_kcontrol_new alc260_hp_3013_mixer[] = {
+	HDA_CODEC_VOLUME("Front Playback Volume", 0x09, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Front Playback Switch", 0x10, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Aux-In Playback Volume", 0x07, 0x06, HDA_INPUT),
+	HDA_CODEC_MUTE("Aux-In Playback Switch", 0x07, 0x06, HDA_INPUT),
+	HDA_CODEC_VOLUME("Headphone Playback Volume", 0x08, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME_MONO("iSpeaker Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE_MONO("iSpeaker Playback Switch", 0x11, 1, 0x0, HDA_OUTPUT),
+	{0} /* end */
+};
+
+/* Fujitsu S702x series laptops.  ALC260 pin usage: Mic/Line jack = 0x12, 
+ * HP jack = 0x14, CD audio =  0x16, internal speaker = 0x10.
+ */
+static struct snd_kcontrol_new alc260_fujitsu_mixer[] = {
+	HDA_CODEC_VOLUME("Headphone Playback Volume", 0x08, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("Headphone Playback Switch", 0x08, 2, HDA_INPUT),
+	ALC_PIN_MODE("Headphone Jack Mode", 0x14, ALC_PIN_DIR_INOUT),
+	HDA_CODEC_VOLUME("CD Playback Volume", 0x07, 0x04, HDA_INPUT),
+	HDA_CODEC_MUTE("CD Playback Switch", 0x07, 0x04, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic/Line Playback Volume", 0x07, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic/Line Playback Switch", 0x07, 0x0, HDA_INPUT),
+	ALC_PIN_MODE("Mic/Line Jack Mode", 0x12, ALC_PIN_DIR_IN),
+	HDA_CODEC_VOLUME("Beep Playback Volume", 0x07, 0x05, HDA_INPUT),
+	HDA_CODEC_MUTE("Beep Playback Switch", 0x07, 0x05, HDA_INPUT),
+	HDA_CODEC_VOLUME("Internal Speaker Playback Volume", 0x09, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("Internal Speaker Playback Switch", 0x09, 2, HDA_INPUT),
+	{0} /* end */
+};
+
+/* Mixer for Acer TravelMate(/Extensa/Aspire) notebooks.  Note that current
+ * versions of the ALC260 don't act on requests to enable mic bias from NID
+ * 0x0f (used to drive the headphone jack in these laptops).  The ALC260
+ * datasheet doesn't mention this restriction.  At this stage it's not clear
+ * whether this behaviour is intentional or is a hardware bug in chip
+ * revisions available in early 2006.  Therefore for now allow the
+ * "Headphone Jack Mode" control to span all choices, but if it turns out
+ * that the lack of mic bias for this NID is intentional we could change the
+ * mode from ALC_PIN_DIR_INOUT to ALC_PIN_DIR_INOUT_NOMICBIAS.
+ *
+ * In addition, Acer TravelMate(/Extensa/Aspire) notebooks in early 2006
+ * don't appear to make the mic bias available from the "line" jack, even
+ * though the NID used for this jack (0x14) can supply it.  The theory is
+ * that perhaps Acer have included blocking capacitors between the ALC260
+ * and the output jack.  If this turns out to be the case for all such
+ * models the "Line Jack Mode" mode could be changed from ALC_PIN_DIR_INOUT
+ * to ALC_PIN_DIR_INOUT_NOMICBIAS.
+ *
+ * The C20x Tablet series have a mono internal speaker which is controlled
+ * via the chip's Mono sum widget and pin complex, so include the necessary
+ * controls for such models.  On models without a "mono speaker" the control
+ * won't do anything.
+ */
+static struct snd_kcontrol_new alc260_acer_mixer[] = {
+	HDA_CODEC_VOLUME("Master Playback Volume", 0x08, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("Master Playback Switch", 0x08, 2, HDA_INPUT),
+	ALC_PIN_MODE("Headphone Jack Mode", 0x0f, ALC_PIN_DIR_INOUT),
+	HDA_CODEC_VOLUME_MONO("Mono Speaker Playback Volume", 0x0a, 1, 0x0,
+			      HDA_OUTPUT),
+	HDA_BIND_MUTE_MONO("Mono Speaker Playback Switch", 0x0a, 1, 2,
+			   HDA_INPUT),
+	HDA_CODEC_VOLUME("CD Playback Volume", 0x07, 0x04, HDA_INPUT),
+	HDA_CODEC_MUTE("CD Playback Switch", 0x07, 0x04, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x07, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x07, 0x0, HDA_INPUT),
+	ALC_PIN_MODE("Mic Jack Mode", 0x12, ALC_PIN_DIR_IN),
+	HDA_CODEC_VOLUME("Line Playback Volume", 0x07, 0x02, HDA_INPUT),
+	HDA_CODEC_MUTE("Line Playback Switch", 0x07, 0x02, HDA_INPUT),
+	ALC_PIN_MODE("Line Jack Mode", 0x14, ALC_PIN_DIR_INOUT),
+	HDA_CODEC_VOLUME("Beep Playback Volume", 0x07, 0x05, HDA_INPUT),
+	HDA_CODEC_MUTE("Beep Playback Switch", 0x07, 0x05, HDA_INPUT),
+	{0} /* end */
+};
+
+/* capture mixer elements */
+static struct snd_kcontrol_new alc260_capture_mixer[] = {
+	HDA_CODEC_VOLUME("Capture Volume", 0x04, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x04, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x05, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x05, 0x0, HDA_INPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		/* The multiple "Capture Source" controls confuse alsamixer
+		 * So call somewhat different..
+		 * FIXME: the controls appear in the "playback" view!
+		 */
+		/* .name = "Capture Source", */
+		.name = "Input Source",
+		.count = 2,
+		.info = alc_mux_enum_info,
+		.get = alc_mux_enum_get,
+		.put = alc_mux_enum_put,
+	},
+	{0} /* end */
+};
+
+static struct snd_kcontrol_new alc260_capture_alt_mixer[] = {
+	HDA_CODEC_VOLUME("Capture Volume", 0x05, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x05, 0x0, HDA_INPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		/* The multiple "Capture Source" controls confuse alsamixer
+		 * So call somewhat different..
+		 * FIXME: the controls appear in the "playback" view!
+		 */
+		/* .name = "Capture Source", */
+		.name = "Input Source",
+		.count = 1,
+		.info = alc_mux_enum_info,
+		.get = alc_mux_enum_get,
+		.put = alc_mux_enum_put,
+	},
+	{0} /* end */
+};
+
+/*
+ * initialization verbs
+ */
+static struct hda_verb alc260_init_verbs[] = {
+	/* Line In pin widget for input */
+	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+	/* CD pin widget for input */
+	{0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+	/* Mic1 (rear panel) pin widget for input and vref at 80% */
+	{0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+	/* Mic2 (front panel) pin widget for input and vref at 80% */
+	{0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+	/* LINE-2 is used for line-out in rear */
+	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	/* select line-out */
+	{0x0e, AC_VERB_SET_CONNECT_SEL, 0x00},
+	/* LINE-OUT pin */
+	{0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	/* enable HP */
+	{0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	/* enable Mono */
+	{0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	/* mute capture amp left and right */
+	{0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	/* set connection select to line in (default select for this ADC) */
+	{0x04, AC_VERB_SET_CONNECT_SEL, 0x02},
+	/* mute capture amp left and right */
+	{0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	/* set connection select to line in (default select for this ADC) */
+	{0x05, AC_VERB_SET_CONNECT_SEL, 0x02},
+	/* set vol=0 Line-Out mixer amp left and right */
+	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	/* unmute pin widget amp left and right (no gain on this amp) */
+	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	/* set vol=0 HP mixer amp left and right */
+	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	/* unmute pin widget amp left and right (no gain on this amp) */
+	{0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	/* set vol=0 Mono mixer amp left and right */
+	{0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	/* unmute pin widget amp left and right (no gain on this amp) */
+	{0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	/* unmute LINE-2 out pin */
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	/* Amp Indexes: CD = 0x04, Line In 1 = 0x02, Mic 1 = 0x00 & Line In 2 = 0x03 */
+	/* mute CD */
+	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
+	/* mute Line In */
+	{0x07,  AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+	/* mute Mic */
+	{0x07,  AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	/* Amp Indexes: DAC = 0x01 & mixer = 0x00 */
+	/* mute Front out path */
+	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	/* mute Headphone out path */
+	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	/* mute Mono out path */
+	{0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	{0}
+};
+
+#if 0 /* should be identical with alc260_init_verbs? */
+static struct hda_verb alc260_hp_init_verbs[] = {
+	/* Headphone and output */
+	{0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0},
+	/* mono output */
+	{0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+	/* Mic1 (rear panel) pin widget for input and vref at 80% */
+	{0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
+	/* Mic2 (front panel) pin widget for input and vref at 80% */
+	{0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
+	/* Line In pin widget for input */
+	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
+	/* Line-2 pin widget for output */
+	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+	/* CD pin widget for input */
+	{0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
+	/* unmute amp left and right */
+	{0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000},
+	/* set connection select to line in (default select for this ADC) */
+	{0x04, AC_VERB_SET_CONNECT_SEL, 0x02},
+	/* unmute Line-Out mixer amp left and right (volume = 0) */
+	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+	/* mute pin widget amp left and right (no gain on this amp) */
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
+	/* unmute HP mixer amp left and right (volume = 0) */
+	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+	/* mute pin widget amp left and right (no gain on this amp) */
+	{0x10, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
+	/* Amp Indexes: CD = 0x04, Line In 1 = 0x02, Mic 1 = 0x00 & Line In 2 = 0x03 */
+	/* unmute CD */
+	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))},
+	/* unmute Line In */
+	{0x07,  AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))},
+	/* unmute Mic */
+	{0x07,  AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+	/* Amp Indexes: DAC = 0x01 & mixer = 0x00 */
+	/* Unmute Front out path */
+	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+	/* Unmute Headphone out path */
+	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+	/* Unmute Mono out path */
+	{0x0a, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+	{0x0a, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+	{0}
+};
+#endif
+
+static struct hda_verb alc260_hp_3013_init_verbs[] = {
+	/* Line out and output */
+	{0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+	/* mono output */
+	{0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+	/* Mic1 (rear panel) pin widget for input and vref at 80% */
+	{0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
+	/* Mic2 (front panel) pin widget for input and vref at 80% */
+	{0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
+	/* Line In pin widget for input */
+	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
+	/* Headphone pin widget for output */
+	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0},
+	/* CD pin widget for input */
+	{0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
+	/* unmute amp left and right */
+	{0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000},
+	/* set connection select to line in (default select for this ADC) */
+	{0x04, AC_VERB_SET_CONNECT_SEL, 0x02},
+	/* unmute Line-Out mixer amp left and right (volume = 0) */
+	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+	/* mute pin widget amp left and right (no gain on this amp) */
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
+	/* unmute HP mixer amp left and right (volume = 0) */
+	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+	/* mute pin widget amp left and right (no gain on this amp) */
+	{0x10, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
+	/* Amp Indexes: CD = 0x04, Line In 1 = 0x02, Mic 1 = 0x00 & Line In 2 = 0x03 */
+	/* unmute CD */
+	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))},
+	/* unmute Line In */
+	{0x07,  AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))},
+	/* unmute Mic */
+	{0x07,  AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+	/* Amp Indexes: DAC = 0x01 & mixer = 0x00 */
+	/* Unmute Front out path */
+	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+	/* Unmute Headphone out path */
+	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+	/* Unmute Mono out path */
+	{0x0a, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+	{0x0a, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+	{0}
+};
+
+/* Initialisation sequence for ALC260 as configured in Fujitsu S702x
+ * laptops.  ALC260 pin usage: Mic/Line jack = 0x12, HP jack = 0x14, CD
+ * audio = 0x16, internal speaker = 0x10.
+ */
+static struct hda_verb alc260_fujitsu_init_verbs[] = {
+	/* Disable all GPIOs */
+	{0x01, AC_VERB_SET_GPIO_MASK, 0},
+	/* Internal speaker is connected to headphone pin */
+	{0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	/* Headphone/Line-out jack connects to Line1 pin; make it an output */
+	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	/* Mic/Line-in jack is connected to mic1 pin, so make it an input */
+	{0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+	/* Ensure all other unused pins are disabled and muted. */
+	{0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+	{0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+	{0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+
+	/* Disable digital (SPDIF) pins */
+	{0x03, AC_VERB_SET_DIGI_CONVERT_1, 0},
+	{0x06, AC_VERB_SET_DIGI_CONVERT_1, 0},
+
+	/* Ensure Line1 pin widget takes its input from the OUT1 sum bus 
+	 * when acting as an output.
+	 */
+	{0x0d, AC_VERB_SET_CONNECT_SEL, 0},
+
+	/* Start with output sum widgets muted and their output gains at min */
+	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	{0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+
+	/* Unmute HP pin widget amp left and right (no equiv mixer ctrl) */
+	{0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	/* Unmute Line1 pin widget output buffer since it starts as an output.
+	 * If the pin mode is changed by the user the pin mode control will
+	 * take care of enabling the pin's input/output buffers as needed.
+	 * Therefore there's no need to enable the input buffer at this
+	 * stage.
+	 */
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	/* Unmute input buffer of pin widget used for Line-in (no equiv 
+	 * mixer ctrl)
+	 */
+	{0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+
+	/* Mute capture amp left and right */
+	{0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	/* Set ADC connection select to match default mixer setting - line 
+	 * in (on mic1 pin)
+	 */
+	{0x04, AC_VERB_SET_CONNECT_SEL, 0x00},
+
+	/* Do the same for the second ADC: mute capture input amp and
+	 * set ADC connection to line in (on mic1 pin)
+	 */
+	{0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x05, AC_VERB_SET_CONNECT_SEL, 0x00},
+
+	/* Mute all inputs to mixer widget (even unconnected ones) */
+	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* mic1 pin */
+	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, /* mic2 pin */
+	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, /* line1 pin */
+	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, /* line2 pin */
+	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, /* CD pin */
+	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, /* Beep-gen pin */
+	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, /* Line-out pin */
+	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, /* HP-pin pin */
+
+	{0}
+};
+
+/* Initialisation sequence for ALC260 as configured in Acer TravelMate and
+ * similar laptops (adapted from Fujitsu init verbs).
+ */
+static struct hda_verb alc260_acer_init_verbs[] = {
+	/* On TravelMate laptops, GPIO 0 enables the internal speaker and
+	 * the headphone jack.  Turn this on and rely on the standard mute
+	 * methods whenever the user wants to turn these outputs off.
+	 */
+	{0x01, AC_VERB_SET_GPIO_MASK, 0x01},
+	{0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01},
+	{0x01, AC_VERB_SET_GPIO_DATA, 0x01},
+	/* Internal speaker/Headphone jack is connected to Line-out pin */
+	{0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	/* Internal microphone/Mic jack is connected to Mic1 pin */
+	{0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50},
+	/* Line In jack is connected to Line1 pin */
+	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+	/* Some Acers (eg: C20x Tablets) use Mono pin for internal speaker */
+	{0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	/* Ensure all other unused pins are disabled and muted. */
+	{0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+	{0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+	{0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	/* Disable digital (SPDIF) pins */
+	{0x03, AC_VERB_SET_DIGI_CONVERT_1, 0},
+	{0x06, AC_VERB_SET_DIGI_CONVERT_1, 0},
+
+	/* Ensure Mic1 and Line1 pin widgets take input from the OUT1 sum 
+	 * bus when acting as outputs.
+	 */
+	{0x0b, AC_VERB_SET_CONNECT_SEL, 0},
+	{0x0d, AC_VERB_SET_CONNECT_SEL, 0},
+
+	/* Start with output sum widgets muted and their output gains at min */
+	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	{0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+
+	/* Unmute Line-out pin widget amp left and right (no equiv mixer ctrl) */
+	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	/* Unmute mono pin widget amp output (no equiv mixer ctrl) */
+	{0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	/* Unmute Mic1 and Line1 pin widget input buffers since they start as
+	 * inputs. If the pin mode is changed by the user the pin mode control
+	 * will take care of enabling the pin's input/output buffers as needed.
+	 * Therefore there's no need to enable the input buffer at this
+	 * stage.
+	 */
+	{0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+
+	/* Mute capture amp left and right */
+	{0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	/* Set ADC connection select to match default mixer setting - mic
+	 * (on mic1 pin)
+	 */
+	{0x04, AC_VERB_SET_CONNECT_SEL, 0x00},
+
+	/* Do similar with the second ADC: mute capture input amp and
+	 * set ADC connection to mic to match ALSA's default state.
+	 */
+	{0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x05, AC_VERB_SET_CONNECT_SEL, 0x00},
+
+	/* Mute all inputs to mixer widget (even unconnected ones) */
+	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* mic1 pin */
+	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, /* mic2 pin */
+	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, /* line1 pin */
+	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, /* line2 pin */
+	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, /* CD pin */
+	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, /* Beep-gen pin */
+	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, /* Line-out pin */
+	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, /* HP-pin pin */
+
+	{0}
+};
+
+/* Test configuration for debugging, modelled after the ALC880 test
+ * configuration.
+ */
+#ifdef CONFIG_SND_DEBUG
+static hda_nid_t alc260_test_dac_nids[1] = {
+	0x02,
+};
+static hda_nid_t alc260_test_adc_nids[2] = {
+	0x04, 0x05,
+};
+/* For testing the ALC260, each input MUX needs its own definition since
+ * the signal assignments are different.  This assumes that the first ADC 
+ * is NID 0x04.
+ */
+static struct hda_input_mux alc260_test_capture_sources[2] = {
+	{
+		.num_items = 7,
+		.items = {
+			{ "MIC1 pin", 0x0 },
+			{ "MIC2 pin", 0x1 },
+			{ "LINE1 pin", 0x2 },
+			{ "LINE2 pin", 0x3 },
+			{ "CD pin", 0x4 },
+			{ "LINE-OUT pin", 0x5 },
+			{ "HP-OUT pin", 0x6 },
+		},
+        },
+	{
+		.num_items = 8,
+		.items = {
+			{ "MIC1 pin", 0x0 },
+			{ "MIC2 pin", 0x1 },
+			{ "LINE1 pin", 0x2 },
+			{ "LINE2 pin", 0x3 },
+			{ "CD pin", 0x4 },
+			{ "Mixer", 0x5 },
+			{ "LINE-OUT pin", 0x6 },
+			{ "HP-OUT pin", 0x7 },
+		},
+        },
+};
+static struct snd_kcontrol_new alc260_test_mixer[] = {
+	/* Output driver widgets */
+	HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE_MONO("Mono Playback Switch", 0x0a, 1, 2, HDA_INPUT),
+	HDA_CODEC_VOLUME("LOUT2 Playback Volume", 0x09, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("LOUT2 Playback Switch", 0x09, 2, HDA_INPUT),
+	HDA_CODEC_VOLUME("LOUT1 Playback Volume", 0x08, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("LOUT1 Playback Switch", 0x08, 2, HDA_INPUT),
+
+	/* Modes for retasking pin widgets
+	 * Note: the ALC260 doesn't seem to act on requests to enable mic
+         * bias from NIDs 0x0f and 0x10.  The ALC260 datasheet doesn't
+         * mention this restriction.  At this stage it's not clear whether
+         * this behaviour is intentional or is a hardware bug in chip
+         * revisions available at least up until early 2006.  Therefore for
+         * now allow the "HP-OUT" and "LINE-OUT" Mode controls to span all
+         * choices, but if it turns out that the lack of mic bias for these
+         * NIDs is intentional we could change their modes from
+         * ALC_PIN_DIR_INOUT to ALC_PIN_DIR_INOUT_NOMICBIAS.
+	 */
+	ALC_PIN_MODE("HP-OUT pin mode", 0x10, ALC_PIN_DIR_INOUT),
+	ALC_PIN_MODE("LINE-OUT pin mode", 0x0f, ALC_PIN_DIR_INOUT),
+	ALC_PIN_MODE("LINE2 pin mode", 0x15, ALC_PIN_DIR_INOUT),
+	ALC_PIN_MODE("LINE1 pin mode", 0x14, ALC_PIN_DIR_INOUT),
+	ALC_PIN_MODE("MIC2 pin mode", 0x13, ALC_PIN_DIR_INOUT),
+	ALC_PIN_MODE("MIC1 pin mode", 0x12, ALC_PIN_DIR_INOUT),
+
+	/* Loopback mixer controls */
+	HDA_CODEC_VOLUME("MIC1 Playback Volume", 0x07, 0x00, HDA_INPUT),
+	HDA_CODEC_MUTE("MIC1 Playback Switch", 0x07, 0x00, HDA_INPUT),
+	HDA_CODEC_VOLUME("MIC2 Playback Volume", 0x07, 0x01, HDA_INPUT),
+	HDA_CODEC_MUTE("MIC2 Playback Switch", 0x07, 0x01, HDA_INPUT),
+	HDA_CODEC_VOLUME("LINE1 Playback Volume", 0x07, 0x02, HDA_INPUT),
+	HDA_CODEC_MUTE("LINE1 Playback Switch", 0x07, 0x02, HDA_INPUT),
+	HDA_CODEC_VOLUME("LINE2 Playback Volume", 0x07, 0x03, HDA_INPUT),
+	HDA_CODEC_MUTE("LINE2 Playback Switch", 0x07, 0x03, HDA_INPUT),
+	HDA_CODEC_VOLUME("CD Playback Volume", 0x07, 0x04, HDA_INPUT),
+	HDA_CODEC_MUTE("CD Playback Switch", 0x07, 0x04, HDA_INPUT),
+	HDA_CODEC_VOLUME("Beep Playback Volume", 0x07, 0x05, HDA_INPUT),
+	HDA_CODEC_MUTE("Beep Playback Switch", 0x07, 0x05, HDA_INPUT),
+	HDA_CODEC_VOLUME("LINE-OUT loopback Playback Volume", 0x07, 0x06, HDA_INPUT),
+	HDA_CODEC_MUTE("LINE-OUT loopback Playback Switch", 0x07, 0x06, HDA_INPUT),
+	HDA_CODEC_VOLUME("HP-OUT loopback Playback Volume", 0x07, 0x7, HDA_INPUT),
+	HDA_CODEC_MUTE("HP-OUT loopback Playback Switch", 0x07, 0x7, HDA_INPUT),
+
+	/* Controls for GPIO pins, assuming they are configured as outputs */
+	ALC_GPIO_DATA_SWITCH("GPIO pin 0", 0x01, 0x01),
+	ALC_GPIO_DATA_SWITCH("GPIO pin 1", 0x01, 0x02),
+	ALC_GPIO_DATA_SWITCH("GPIO pin 2", 0x01, 0x04),
+	ALC_GPIO_DATA_SWITCH("GPIO pin 3", 0x01, 0x08),
+
+	/* Switches to allow the digital IO pins to be enabled.  The datasheet
+	 * is ambigious as to which NID is which; testing on laptops which
+	 * make this output available should provide clarification. 
+	 */
+	ALC_SPDIF_CTRL_SWITCH("SPDIF Playback Switch", 0x03, 0x01),
+	ALC_SPDIF_CTRL_SWITCH("SPDIF Capture Switch", 0x06, 0x01),
+
+	{0} /* end */
+};
+static struct hda_verb alc260_test_init_verbs[] = {
+	/* Enable all GPIOs as outputs with an initial value of 0 */
+	{0x01, AC_VERB_SET_GPIO_DIRECTION, 0x0f},
+	{0x01, AC_VERB_SET_GPIO_DATA, 0x00},
+	{0x01, AC_VERB_SET_GPIO_MASK, 0x0f},
+
+	/* Enable retasking pins as output, initially without power amp */
+	{0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+
+	/* Disable digital (SPDIF) pins initially, but users can enable
+	 * them via a mixer switch.  In the case of SPDIF-out, this initverb
+	 * payload also sets the generation to 0, output to be in "consumer"
+	 * PCM format, copyright asserted, no pre-emphasis and no validity
+	 * control.
+	 */
+	{0x03, AC_VERB_SET_DIGI_CONVERT_1, 0},
+	{0x06, AC_VERB_SET_DIGI_CONVERT_1, 0},
+
+	/* Ensure mic1, mic2, line1 and line2 pin widgets take input from the 
+	 * OUT1 sum bus when acting as an output.
+	 */
+	{0x0b, AC_VERB_SET_CONNECT_SEL, 0},
+	{0x0c, AC_VERB_SET_CONNECT_SEL, 0},
+	{0x0d, AC_VERB_SET_CONNECT_SEL, 0},
+	{0x0e, AC_VERB_SET_CONNECT_SEL, 0},
+
+	/* Start with output sum widgets muted and their output gains at min */
+	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	{0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+
+	/* Unmute retasking pin widget output buffers since the default
+	 * state appears to be output.  As the pin mode is changed by the
+	 * user the pin mode control will take care of enabling the pin's
+	 * input/output buffers as needed.
+	 */
+	{0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	/* Also unmute the mono-out pin widget */
+	{0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+
+	/* Mute capture amp left and right */
+	{0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	/* Set ADC connection select to match default mixer setting (mic1
+	 * pin)
+	 */
+	{0x04, AC_VERB_SET_CONNECT_SEL, 0x00},
+
+	/* Do the same for the second ADC: mute capture input amp and
+	 * set ADC connection to mic1 pin
+	 */
+	{0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x05, AC_VERB_SET_CONNECT_SEL, 0x00},
+
+	/* Mute all inputs to mixer widget (even unconnected ones) */
+	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* mic1 pin */
+	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, /* mic2 pin */
+	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, /* line1 pin */
+	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, /* line2 pin */
+	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, /* CD pin */
+	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, /* Beep-gen pin */
+	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, /* Line-out pin */
+	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, /* HP-pin pin */
+
+	{0}
+};
+#endif
+
+static struct hda_pcm_stream alc260_pcm_analog_playback = {
+	.substreams = 1,
+	.channels_min = 2,
+	.channels_max = 2,
+};
+
+static struct hda_pcm_stream alc260_pcm_analog_capture = {
+	.substreams = 1,
+	.channels_min = 2,
+	.channels_max = 2,
+};
+
+#define alc260_pcm_digital_playback	alc880_pcm_digital_playback
+#define alc260_pcm_digital_capture	alc880_pcm_digital_capture
+
+/*
+ * for BIOS auto-configuration
+ */
+
+static int alc260_add_playback_controls(struct alc_spec *spec, hda_nid_t nid,
+					const char *pfx)
+{
+	hda_nid_t nid_vol;
+	unsigned long vol_val, sw_val;
+	char name[32];
+	int err;
+
+	if (nid >= 0x0f && nid < 0x11) {
+		nid_vol = nid - 0x7;
+		vol_val = HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT);
+		sw_val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
+	} else if (nid == 0x11) {
+		nid_vol = nid - 0x7;
+		vol_val = HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, HDA_OUTPUT);
+		sw_val = HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT);
+	} else if (nid >= 0x12 && nid <= 0x15) {
+		nid_vol = 0x08;
+		vol_val = HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT);
+		sw_val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
+	} else
+		return 0; /* N/A */
+	
+	sprintf(name, "%s Playback Volume", pfx);
+	if ((err = add_control(spec, ALC_CTL_WIDGET_VOL, name, vol_val)) < 0)
+		return err;
+	sprintf(name, "%s Playback Switch", pfx);
+	if ((err = add_control(spec, ALC_CTL_WIDGET_MUTE, name, sw_val)) < 0)
+		return err;
+	return 1;
+}
+
+/* add playback controls from the parsed DAC table */
+static int alc260_auto_create_multi_out_ctls(struct alc_spec *spec,
+					     const struct auto_pin_cfg *cfg)
+{
+	hda_nid_t nid;
+	int err;
+
+	spec->multiout.num_dacs = 1;
+	spec->multiout.dac_nids = spec->private_dac_nids;
+	spec->multiout.dac_nids[0] = 0x02;
+
+	nid = cfg->line_out_pins[0];
+	if (nid) {
+		err = alc260_add_playback_controls(spec, nid, "Front");
+		if (err < 0)
+			return err;
+	}
+
+	nid = cfg->speaker_pins[0];
+	if (nid) {
+		err = alc260_add_playback_controls(spec, nid, "Speaker");
+		if (err < 0)
+			return err;
+	}
+
+	nid = cfg->hp_pins[0];
+	if (nid) {
+		err = alc260_add_playback_controls(spec, nid, "Headphone");
+		if (err < 0)
+			return err;
+	}
+	return 0;	
+}
+
+/* create playback/capture controls for input pins */
+static int alc260_auto_create_analog_input_ctls(struct alc_spec *spec,
+						const struct auto_pin_cfg *cfg)
+{
+	struct hda_input_mux *imux = &spec->private_imux;
+	int i, err, idx;
+
+	for (i = 0; i < AUTO_PIN_LAST; i++) {
+		if (cfg->input_pins[i] >= 0x12) {
+			idx = cfg->input_pins[i] - 0x12;
+			err = new_analog_input(spec, cfg->input_pins[i],
+					       auto_pin_cfg_labels[i], idx, 0x07);
+			if (err < 0)
+				return err;
+			imux->items[imux->num_items].label = auto_pin_cfg_labels[i];
+			imux->items[imux->num_items].index = idx;
+			imux->num_items++;
+		}
+		if ((cfg->input_pins[i] >= 0x0f) && (cfg->input_pins[i] <= 0x10)){
+			idx = cfg->input_pins[i] - 0x09;
+			err = new_analog_input(spec, cfg->input_pins[i],
+					       auto_pin_cfg_labels[i], idx, 0x07);
+			if (err < 0)
+				return err;
+			imux->items[imux->num_items].label = auto_pin_cfg_labels[i];
+			imux->items[imux->num_items].index = idx;
+			imux->num_items++;
+		}
+	}
+	return 0;
+}
+
+static void alc260_auto_set_output_and_unmute(struct hda_codec *codec,
+					      hda_nid_t nid, int pin_type,
+					      int sel_idx)
+{
+	/* set as output */
+	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);
+	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
+	/* need the manual connection? */
+	if (nid >= 0x12) {
+		int idx = nid - 0x12;
+		snd_hda_codec_write(codec, idx + 0x0b, 0,
+				    AC_VERB_SET_CONNECT_SEL, sel_idx);
+				    
+	}
+}
+
+static void alc260_auto_init_multi_out(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+	hda_nid_t nid;
+
+	nid = spec->autocfg.line_out_pins[0];	
+	if (nid)
+		alc260_auto_set_output_and_unmute(codec, nid, PIN_OUT, 0);
+	
+	nid = spec->autocfg.speaker_pins[0];
+	if (nid)
+		alc260_auto_set_output_and_unmute(codec, nid, PIN_OUT, 0);
+
+	nid = spec->autocfg.hp_pins[0];
+	if (nid)
+		alc260_auto_set_output_and_unmute(codec, nid, PIN_OUT, 0);
+}	
+
+#define ALC260_PIN_CD_NID		0x16
+static void alc260_auto_init_analog_input(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+	int i;
+
+	for (i = 0; i < AUTO_PIN_LAST; i++) {
+		hda_nid_t nid = spec->autocfg.input_pins[i];
+		if (nid >= 0x12) {
+			snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+					    i <= AUTO_PIN_FRONT_MIC ? PIN_VREF80 : PIN_IN);
+			if (nid != ALC260_PIN_CD_NID)
+				snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+						    AMP_OUT_MUTE);
+		}
+	}
+}
+
+/*
+ * generic initialization of ADC, input mixers and output mixers
+ */
+static struct hda_verb alc260_volume_init_verbs[] = {
+	/*
+	 * Unmute ADC0-1 and set the default input to mic-in
+	 */
+	{0x04, AC_VERB_SET_CONNECT_SEL, 0x00},
+	{0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x05, AC_VERB_SET_CONNECT_SEL, 0x00},
+	{0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	
+	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
+	 * mixer widget
+	 * Note: PASD motherboards uses the Line In 2 as the input for front panel
+	 * mic (mic 2)
+	 */
+	/* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
+	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
+	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
+
+	/*
+	 * Set up output mixers (0x08 - 0x0a)
+	 */
+	/* set vol=0 to output mixers */
+	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	/* set up input amps for analog loopback */
+	/* Amp Indices: DAC = 0, mixer = 1 */
+	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	
+	{0}
+};
+
+static int alc260_parse_auto_config(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+	unsigned int wcap;
+	int err;
+	static hda_nid_t alc260_ignore[] = { 0x17, 0 };
+
+	if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
+						alc260_ignore)) < 0)
+		return err;
+	if ((err = alc260_auto_create_multi_out_ctls(spec, &spec->autocfg)) < 0)
+		return err;
+	if (! spec->kctl_alloc)
+		return 0; /* can't find valid BIOS pin config */
+	if ((err = alc260_auto_create_analog_input_ctls(spec, &spec->autocfg)) < 0)
+		return err;
+
+	spec->multiout.max_channels = 2;
+
+	if (spec->autocfg.dig_out_pin)
+		spec->multiout.dig_out_nid = ALC260_DIGOUT_NID;
+	if (spec->kctl_alloc)
+		spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+
+	spec->init_verbs[spec->num_init_verbs++] = alc260_volume_init_verbs;
+
+	spec->num_mux_defs = 1;
+	spec->input_mux = &spec->private_imux;
+
+	/* check whether NID 0x04 is valid */
+	wcap = get_wcaps(codec, 0x04);
+	wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; /* get type */
+	if (wcap != AC_WID_AUD_IN) {
+		spec->adc_nids = alc260_adc_nids_alt;
+		spec->num_adc_nids = ARRAY_SIZE(alc260_adc_nids_alt);
+		spec->mixers[spec->num_mixers] = alc260_capture_alt_mixer;
+	} else {
+		spec->adc_nids = alc260_adc_nids;
+		spec->num_adc_nids = ARRAY_SIZE(alc260_adc_nids);
+		spec->mixers[spec->num_mixers] = alc260_capture_mixer;
+	}
+	spec->num_mixers++;
+
+	return 1;
+}
+
+/* additional initialization for auto-configuration model */
+static void alc260_auto_init(struct hda_codec *codec)
+{
+	alc260_auto_init_multi_out(codec);
+	alc260_auto_init_analog_input(codec);
+}
+
+/*
+ * ALC260 configurations
+ */
+static const char *alc260_models[ALC260_MODEL_LAST] = {
+	[ALC260_BASIC]		= "basic",
+	[ALC260_HP]		= "hp",
+	[ALC260_HP_3013]	= "hp-3013",
+	[ALC260_FUJITSU_S702X]	= "fujitsu",
+	[ALC260_ACER]		= "acer",
+#ifdef CONFIG_SND_DEBUG
+	[ALC260_TEST]		= "test",
+#endif
+	[ALC260_AUTO]		= "auto",
+};
+
+static struct snd_pci_quirk alc260_cfg_tbl[] = {
+	SND_PCI_QUIRK(0x1025, 0x007b, "Acer C20x", ALC260_ACER),
+	SND_PCI_QUIRK(0x1025, 0x008f, "Acer", ALC260_ACER),
+	SND_PCI_QUIRK(0x103c, 0x3010, "HP", ALC260_HP_3013),
+	SND_PCI_QUIRK(0x103c, 0x3011, "HP", ALC260_HP),
+	SND_PCI_QUIRK(0x103c, 0x3012, "HP", ALC260_HP_3013),
+	SND_PCI_QUIRK(0x103c, 0x3013, "HP", ALC260_HP_3013),
+	SND_PCI_QUIRK(0x103c, 0x3014, "HP", ALC260_HP),
+	SND_PCI_QUIRK(0x103c, 0x3015, "HP", ALC260_HP),
+	SND_PCI_QUIRK(0x103c, 0x3016, "HP", ALC260_HP),
+	SND_PCI_QUIRK(0x104d, 0x81bb, "Sony VAIO", ALC260_BASIC),
+	SND_PCI_QUIRK(0x104d, 0x81cc, "Sony VAIO", ALC260_BASIC),
+	SND_PCI_QUIRK(0x104d, 0x81cd, "Sony VAIO", ALC260_BASIC),
+	SND_PCI_QUIRK(0x10cf, 0x1326, "Fujitsu S702X", ALC260_FUJITSU_S702X),
+	SND_PCI_QUIRK(0x152d, 0x0729, "CTL U553W", ALC260_BASIC),
+	{0}
+};
+
+static struct alc_config_preset alc260_presets[] = {
+	[ALC260_BASIC] = {
+		.mixers = { alc260_base_output_mixer,
+			    alc260_input_mixer,
+			    alc260_pc_beep_mixer,
+			    alc260_capture_mixer },
+		.init_verbs = { alc260_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc260_dac_nids),
+		.dac_nids = alc260_dac_nids,
+		.num_adc_nids = ARRAY_SIZE(alc260_adc_nids),
+		.adc_nids = alc260_adc_nids,
+		.num_channel_mode = ARRAY_SIZE(alc260_modes),
+		.channel_mode = alc260_modes,
+		.input_mux = &alc260_capture_source,
+	},
+	[ALC260_HP] = {
+		.mixers = { alc260_base_output_mixer,
+			    alc260_input_mixer,
+			    alc260_capture_alt_mixer },
+		.init_verbs = { alc260_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc260_dac_nids),
+		.dac_nids = alc260_dac_nids,
+		.num_adc_nids = ARRAY_SIZE(alc260_hp_adc_nids),
+		.adc_nids = alc260_hp_adc_nids,
+		.num_channel_mode = ARRAY_SIZE(alc260_modes),
+		.channel_mode = alc260_modes,
+		.input_mux = &alc260_capture_source,
+	},
+	[ALC260_HP_3013] = {
+		.mixers = { alc260_hp_3013_mixer,
+			    alc260_input_mixer,
+			    alc260_capture_alt_mixer },
+		.init_verbs = { alc260_hp_3013_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc260_dac_nids),
+		.dac_nids = alc260_dac_nids,
+		.num_adc_nids = ARRAY_SIZE(alc260_hp_adc_nids),
+		.adc_nids = alc260_hp_adc_nids,
+		.num_channel_mode = ARRAY_SIZE(alc260_modes),
+		.channel_mode = alc260_modes,
+		.input_mux = &alc260_capture_source,
+	},
+	[ALC260_FUJITSU_S702X] = {
+		.mixers = { alc260_fujitsu_mixer,
+			    alc260_capture_mixer },
+		.init_verbs = { alc260_fujitsu_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc260_dac_nids),
+		.dac_nids = alc260_dac_nids,
+		.num_adc_nids = ARRAY_SIZE(alc260_dual_adc_nids),
+		.adc_nids = alc260_dual_adc_nids,
+		.num_channel_mode = ARRAY_SIZE(alc260_modes),
+		.channel_mode = alc260_modes,
+		.num_mux_defs = ARRAY_SIZE(alc260_fujitsu_capture_sources),
+		.input_mux = alc260_fujitsu_capture_sources,
+	},
+	[ALC260_ACER] = {
+		.mixers = { alc260_acer_mixer,
+			    alc260_capture_mixer },
+		.init_verbs = { alc260_acer_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc260_dac_nids),
+		.dac_nids = alc260_dac_nids,
+		.num_adc_nids = ARRAY_SIZE(alc260_dual_adc_nids),
+		.adc_nids = alc260_dual_adc_nids,
+		.num_channel_mode = ARRAY_SIZE(alc260_modes),
+		.channel_mode = alc260_modes,
+		.num_mux_defs = ARRAY_SIZE(alc260_acer_capture_sources),
+		.input_mux = alc260_acer_capture_sources,
+	},
+#ifdef CONFIG_SND_DEBUG
+	[ALC260_TEST] = {
+		.mixers = { alc260_test_mixer,
+			    alc260_capture_mixer },
+		.init_verbs = { alc260_test_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc260_test_dac_nids),
+		.dac_nids = alc260_test_dac_nids,
+		.num_adc_nids = ARRAY_SIZE(alc260_test_adc_nids),
+		.adc_nids = alc260_test_adc_nids,
+		.num_channel_mode = ARRAY_SIZE(alc260_modes),
+		.channel_mode = alc260_modes,
+		.num_mux_defs = ARRAY_SIZE(alc260_test_capture_sources),
+		.input_mux = alc260_test_capture_sources,
+	},
+#endif
+};
+
+static int patch_alc260(struct hda_codec *codec)
+{
+	struct alc_spec *spec;
+	int err, board_config;
+
+	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+	if (spec == NULL)
+		return -ENOMEM;
+
+	codec->spec = spec;
+
+	board_config = snd_hda_check_board_config(codec, ALC260_MODEL_LAST,
+						  alc260_models,
+						  alc260_cfg_tbl);
+	if (board_config < 0) {
+		snd_printd(KERN_INFO "hda_codec: Unknown model for ALC260, "
+			   "trying auto-probe from BIOS...\n");
+		board_config = ALC260_AUTO;
+	}
+
+	if (board_config == ALC260_AUTO) {
+		/* automatic parse from the BIOS config */
+		err = alc260_parse_auto_config(codec);
+		if (err < 0) {
+			alc_free(codec);
+			return err;
+		} else if (! err) {
+			printk(KERN_INFO
+			       "hda_codec: Cannot set up configuration "
+			       "from BIOS.  Using base mode...\n");
+			board_config = ALC260_BASIC;
+		}
+	}
+
+	if (board_config != ALC260_AUTO)
+		setup_preset(spec, &alc260_presets[board_config]);
+
+	spec->stream_name_analog = "ALC260 Analog";
+	spec->stream_analog_playback = &alc260_pcm_analog_playback;
+	spec->stream_analog_capture = &alc260_pcm_analog_capture;
+
+	spec->stream_name_digital = "ALC260 Digital";
+	spec->stream_digital_playback = &alc260_pcm_digital_playback;
+	spec->stream_digital_capture = &alc260_pcm_digital_capture;
+
+	codec->patch_ops = alc_patch_ops;
+	if (board_config == ALC260_AUTO)
+		spec->init_hook = alc260_auto_init;
+
+	return 0;
+}
+
+
+/*
+ * ALC882 support
+ *
+ * ALC882 is almost identical with ALC880 but has cleaner and more flexible
+ * configuration.  Each pin widget can choose any input DACs and a mixer.
+ * Each ADC is connected from a mixer of all inputs.  This makes possible
+ * 6-channel independent captures.
+ *
+ * In addition, an independent DAC for the multi-playback (not used in this
+ * driver yet).
+ */
+#define ALC882_DIGOUT_NID	0x06
+#define ALC882_DIGIN_NID	0x0a
+
+static struct hda_channel_mode alc882_ch_modes[1] = {
+	{ 8, NULL }
+};
+
+static hda_nid_t alc882_dac_nids[4] = {
+	/* front, rear, clfe, rear_surr */
+	0x02, 0x03, 0x04, 0x05
+};
+
+/* identical with ALC880 */
+#define alc882_adc_nids		alc880_adc_nids
+#define alc882_adc_nids_alt	alc880_adc_nids_alt
+
+/* input MUX */
+/* FIXME: should be a matrix-type input source selection */
+
+static struct hda_input_mux alc882_capture_source = {
+	.num_items = 4,
+	.items = {
+		{ "Mic", 0x0 },
+		{ "Front Mic", 0x1 },
+		{ "Line", 0x2 },
+		{ "CD", 0x4 },
+	},
+};
+#define alc882_mux_enum_info alc_mux_enum_info
+#define alc882_mux_enum_get alc_mux_enum_get
+
+static int alc882_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct alc_spec *spec = codec->spec;
+	const struct hda_input_mux *imux = spec->input_mux;
+	unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+	static hda_nid_t capture_mixers[3] = { 0x24, 0x23, 0x22 };
+	hda_nid_t nid = capture_mixers[adc_idx];
+	unsigned int *cur_val = &spec->cur_mux[adc_idx];
+	unsigned int i, idx;
+
+	idx = ucontrol->value.enumerated.item[0];
+	if (idx >= imux->num_items)
+		idx = imux->num_items - 1;
+	if (*cur_val == idx && ! codec->in_resume)
+		return 0;
+	for (i = 0; i < imux->num_items; i++) {
+		unsigned int v = (i == idx) ? 0x7000 : 0x7080;
+		snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+				    v | (imux->items[i].index << 8));
+	}
+	*cur_val = idx;
+	return 1;
+}
+
+/*
+ * 6ch mode
+ */
+static struct hda_verb alc882_sixstack_ch6_init[] = {
+	{ 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 },
+	{ 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{0} /* end */
+};
+
+/*
+ * 8ch mode
+ */
+static struct hda_verb alc882_sixstack_ch8_init[] = {
+	{ 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{0} /* end */
+};
+
+static struct hda_channel_mode alc882_sixstack_modes[2] = {
+	{ 6, alc882_sixstack_ch6_init },
+	{ 8, alc882_sixstack_ch8_init },
+};
+
+/* Pin assignment: Front=0x14, Rear=0x15, CLFE=0x16, Side=0x17
+ *                 Mic=0x18, Front Mic=0x19, Line-In=0x1a, HP=0x1b
+ */
+static struct snd_kcontrol_new alc882_base_mixer[] = {
+	HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
+	HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
+	HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
+	HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
+	HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT),
+	HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
+	HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
+	HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
+	HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
+	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
+	HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
+	HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
+	{0} /* end */
+};
+
+static struct snd_kcontrol_new alc882_chmode_mixer[] = {
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Channel Mode",
+		.info = alc_ch_mode_info,
+		.get = alc_ch_mode_get,
+		.put = alc_ch_mode_put,
+	},
+	{0} /* end */
+};
+
+static struct hda_verb alc882_init_verbs[] = {
+	/* Front mixer: unmute input/output amp left and right (volume = 0) */
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	/* Rear mixer */
+	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	/* CLFE mixer */
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	/* Side mixer */
+	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+
+	/* Front Pin: output 0 (0x0c) */
+	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
+	/* Rear Pin: output 1 (0x0d) */
+	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
+	/* CLFE Pin: output 2 (0x0e) */
+	{0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x16, AC_VERB_SET_CONNECT_SEL, 0x02},
+	/* Side Pin: output 3 (0x0f) */
+	{0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x17, AC_VERB_SET_CONNECT_SEL, 0x03},
+	/* Mic (rear) pin: input vref at 80% */
+	{0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	/* Front Mic pin: input vref at 80% */
+	{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	/* Line In pin: input */
+	{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	/* Line-2 In: Headphone output (output 0 - 0x0c) */
+	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
+	/* CD pin widget for input */
+	{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+
+	/* FIXME: use matrix-type input source selection */
+	/* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
+	/* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+	/* Input mixer2 */
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+	/* Input mixer3 */
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+	/* ADC1: mute amp left and right */
+	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
+	/* ADC2: mute amp left and right */
+	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
+	/* ADC3: mute amp left and right */
+	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
+
+	{0}
+};
+
+static struct hda_verb alc882_eapd_verbs[] = {
+	/* change to EAPD mode */
+	{0x20, AC_VERB_SET_COEF_INDEX, 0x07},
+	{0x20, AC_VERB_SET_PROC_COEF, 0x3060},
+	{0} 
+};
+
+
+/* Mac Pro test */
+static struct snd_kcontrol_new alc882_macpro_mixer[] = {
+	HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
+	HDA_CODEC_MUTE("Headphone Playback Switch", 0x18, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x01, HDA_INPUT),
+	HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x01, HDA_INPUT),
+	HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x02, HDA_INPUT),
+	HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x02, HDA_INPUT),
+	{0} /* end */
+};
+ 
+static struct hda_verb alc882_macpro_init_verbs[] = {
+	/* Front mixer: unmute input/output amp left and right (volume = 0) */
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	/* Front Pin: output 0 (0x0c) */
+	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
+	/* Front Mic pin: input vref at 80% */
+	{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	/* Speaker:  output */
+	{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x1a, AC_VERB_SET_CONNECT_SEL, 0x04},
+	/* Headphone output (output 0 - 0x0c) */
+	{0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x18, AC_VERB_SET_CONNECT_SEL, 0x00},
+
+	/* FIXME: use matrix-type input source selection */
+	/* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
+	/* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+	/* Input mixer2 */
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+	/* Input mixer3 */
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+	/* ADC1: mute amp left and right */
+	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
+	/* ADC2: mute amp left and right */
+	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
+	/* ADC3: mute amp left and right */
+	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
+
+	{0}
+};
+static void alc882_gpio_mute(struct hda_codec *codec, int pin, int muted)
+{
+	unsigned int gpiostate, gpiomask, gpiodir;
+
+	gpiostate = snd_hda_codec_read(codec, codec->afg, 0,
+				       AC_VERB_GET_GPIO_DATA, 0);
+
+	if (!muted)
+		gpiostate |= (1 << pin);
+	else
+		gpiostate &= ~(1 << pin);
+
+	gpiomask = snd_hda_codec_read(codec, codec->afg, 0,
+				      AC_VERB_GET_GPIO_MASK, 0);
+	gpiomask |= (1 << pin);
+
+	gpiodir = snd_hda_codec_read(codec, codec->afg, 0,
+				     AC_VERB_GET_GPIO_DIRECTION, 0);
+	gpiodir |= (1 << pin);
+
+
+	snd_hda_codec_write(codec, codec->afg, 0,
+			    AC_VERB_SET_GPIO_MASK, gpiomask);
+	snd_hda_codec_write(codec, codec->afg, 0,
+			    AC_VERB_SET_GPIO_DIRECTION, gpiodir);
+
+	msleep(1);
+
+	snd_hda_codec_write(codec, codec->afg, 0,
+			    AC_VERB_SET_GPIO_DATA, gpiostate);
+}
+
+
+/*
+ * generic initialization of ADC, input mixers and output mixers
+ */
+static struct hda_verb alc882_auto_init_verbs[] = {
+	/*
+	 * Unmute ADC0-2 and set the default input to mic-in
+	 */
+	{0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
+	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
+	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
+	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+
+	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
+	 * mixer widget
+	 * Note: PASD motherboards uses the Line In 2 as the input for front panel
+	 * mic (mic 2)
+	 */
+	/* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
+
+	/*
+	 * Set up output mixers (0x0c - 0x0f)
+	 */
+	/* set vol=0 to output mixers */
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	/* set up input amps for analog loopback */
+	/* Amp Indices: DAC = 0, mixer = 1 */
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+
+	/* FIXME: use matrix-type input source selection */
+	/* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
+	/* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
+	/* Input mixer2 */
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
+	/* Input mixer3 */
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
+
+	{0}
+};
+
+/* capture mixer elements */
+static struct snd_kcontrol_new alc882_capture_alt_mixer[] = {
+	HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		/* The multiple "Capture Source" controls confuse alsamixer
+		 * So call somewhat different..
+		 * FIXME: the controls appear in the "playback" view!
+		 */
+		/* .name = "Capture Source", */
+		.name = "Input Source",
+		.count = 2,
+		.info = alc882_mux_enum_info,
+		.get = alc882_mux_enum_get,
+		.put = alc882_mux_enum_put,
+	},
+	{0} /* end */
+};
+
+static struct snd_kcontrol_new alc882_capture_mixer[] = {
+	HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x08, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x08, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x09, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x09, 0x0, HDA_INPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		/* The multiple "Capture Source" controls confuse alsamixer
+		 * So call somewhat different..
+		 * FIXME: the controls appear in the "playback" view!
+		 */
+		/* .name = "Capture Source", */
+		.name = "Input Source",
+		.count = 3,
+		.info = alc882_mux_enum_info,
+		.get = alc882_mux_enum_get,
+		.put = alc882_mux_enum_put,
+	},
+	{0} /* end */
+};
+
+/* pcm configuration: identiacal with ALC880 */
+#define alc882_pcm_analog_playback	alc880_pcm_analog_playback
+#define alc882_pcm_analog_capture	alc880_pcm_analog_capture
+#define alc882_pcm_digital_playback	alc880_pcm_digital_playback
+#define alc882_pcm_digital_capture	alc880_pcm_digital_capture
+
+/*
+ * configuration and preset
+ */
+static const char *alc882_models[ALC882_MODEL_LAST] = {
+	[ALC882_3ST_DIG]	= "3stack-dig",
+	[ALC882_6ST_DIG]	= "6stack-dig",
+        [ALC882_ARIMA]		= "arima",
+        [ALC885_MACPRO]		= "macpro",
+	[ALC882_AUTO]		= "auto",
+};
+
+static struct snd_pci_quirk alc882_cfg_tbl[] = {
+	SND_PCI_QUIRK(0x1019, 0x6668, "ECS", ALC882_6ST_DIG),
+	SND_PCI_QUIRK(0x105b, 0x6668, "Foxconn", ALC882_6ST_DIG),
+	SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC882_6ST_DIG),
+        SND_PCI_QUIRK(0x161f, 0x2054, "Arima W820", ALC882_ARIMA),
+        SND_PCI_QUIRK(0x1043, 0x81d8, "Asus P5WD", ALC882_6ST_DIG),
+	{0}
+};
+
+static struct alc_config_preset alc882_presets[] = {
+	[ALC882_3ST_DIG] = {
+		.mixers = { alc882_base_mixer },
+		.init_verbs = { alc882_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc882_dac_nids),
+		.dac_nids = alc882_dac_nids,
+		.dig_out_nid = ALC882_DIGOUT_NID,
+		.dig_in_nid = ALC882_DIGIN_NID,
+		.num_channel_mode = ARRAY_SIZE(alc882_ch_modes),
+		.channel_mode = alc882_ch_modes,
+		.need_dac_fix = 1,
+		.input_mux = &alc882_capture_source,
+	},
+	[ALC882_6ST_DIG] = {
+		.mixers = { alc882_base_mixer, alc882_chmode_mixer },
+		.init_verbs = { alc882_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc882_dac_nids),
+		.dac_nids = alc882_dac_nids,
+		.dig_out_nid = ALC882_DIGOUT_NID,
+		.dig_in_nid = ALC882_DIGIN_NID,
+		.num_channel_mode = ARRAY_SIZE(alc882_sixstack_modes),
+		.channel_mode = alc882_sixstack_modes,
+		.input_mux = &alc882_capture_source,
+	},
+	[ALC882_ARIMA] = {
+		.mixers = { alc882_base_mixer, alc882_chmode_mixer },
+		.init_verbs = { alc882_init_verbs, alc882_eapd_verbs },
+		.num_dacs = ARRAY_SIZE(alc882_dac_nids),
+		.dac_nids = alc882_dac_nids,
+		.num_channel_mode = ARRAY_SIZE(alc882_sixstack_modes),
+		.channel_mode = alc882_sixstack_modes,
+		.input_mux = &alc882_capture_source,
+        },
+	[ALC885_MACPRO] = {
+		.mixers = { alc882_macpro_mixer },
+		.init_verbs = { alc882_macpro_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc882_dac_nids),
+		.dac_nids = alc882_dac_nids,
+		.dig_out_nid = ALC882_DIGOUT_NID,
+		.dig_in_nid = ALC882_DIGIN_NID,
+		.num_channel_mode = ARRAY_SIZE(alc882_ch_modes),
+		.channel_mode = alc882_ch_modes,
+		.input_mux = &alc882_capture_source,
+	},
+};
+
+
+/*
+ * BIOS auto configuration
+ */
+static void alc882_auto_set_output_and_unmute(struct hda_codec *codec,
+					      hda_nid_t nid, int pin_type,
+					      int dac_idx)
+{
+	/* set as output */
+	struct alc_spec *spec = codec->spec;
+	int idx; 
+	
+	if (spec->multiout.dac_nids[dac_idx] == 0x25)
+		idx = 4;
+	else
+		idx = spec->multiout.dac_nids[dac_idx] - 2;
+
+	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);
+	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
+	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, idx);
+
+}
+
+static void alc882_auto_init_multi_out(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+	int i;
+
+	for (i = 0; i <= HDA_SIDE; i++) {
+		hda_nid_t nid = spec->autocfg.line_out_pins[i];	
+		if (nid)
+			alc882_auto_set_output_and_unmute(codec, nid, PIN_OUT, i);
+	}
+}
+
+static void alc882_auto_init_hp_out(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+	hda_nid_t pin;
+
+	pin = spec->autocfg.hp_pins[0];
+	if (pin) /* connect to front */
+		alc882_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); /* use dac 0 */
+}
+
+#define alc882_is_input_pin(nid)	alc880_is_input_pin(nid)
+#define ALC882_PIN_CD_NID		ALC880_PIN_CD_NID
+
+static void alc882_auto_init_analog_input(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+	int i;
+
+	for (i = 0; i < AUTO_PIN_LAST; i++) {
+		hda_nid_t nid = spec->autocfg.input_pins[i];
+		if (alc882_is_input_pin(nid)) {
+			snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+					    i <= AUTO_PIN_FRONT_MIC ? PIN_VREF80 : PIN_IN);
+			if (nid != ALC882_PIN_CD_NID)
+				snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+						    AMP_OUT_MUTE);
+		}
+	}
+}
+
+/* almost identical with ALC880 parser... */
+static int alc882_parse_auto_config(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+	int err = alc880_parse_auto_config(codec);
+
+	if (err < 0)
+		return err;
+	else if (err > 0)
+		/* hack - override the init verbs */
+		spec->init_verbs[0] = alc882_auto_init_verbs;
+	return err;
+}
+
+/* additional initialization for auto-configuration model */
+static void alc882_auto_init(struct hda_codec *codec)
+{
+	alc882_auto_init_multi_out(codec);
+	alc882_auto_init_hp_out(codec);
+	alc882_auto_init_analog_input(codec);
+}
+
+static int patch_alc882(struct hda_codec *codec)
+{
+	struct alc_spec *spec;
+	int err, board_config;
+
+	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+	if (spec == NULL)
+		return -ENOMEM;
+
+	codec->spec = spec;
+
+	board_config = snd_hda_check_board_config(codec, ALC882_MODEL_LAST,
+						  alc882_models,
+						  alc882_cfg_tbl);
+
+	if (board_config < 0 || board_config >= ALC882_MODEL_LAST) {
+		printk(KERN_INFO "hda_codec: Unknown model for ALC882, "
+		       "trying auto-probe from BIOS...\n");
+		board_config = ALC882_AUTO;
+	}
+
+	if (board_config == ALC882_AUTO) {
+		/* automatic parse from the BIOS config */
+		err = alc882_parse_auto_config(codec);
+		if (err < 0) {
+			alc_free(codec);
+			return err;
+		} else if (! err) {
+			printk(KERN_INFO
+			       "hda_codec: Cannot set up configuration "
+			       "from BIOS.  Using base mode...\n");
+			board_config = ALC882_3ST_DIG;
+		}
+	}
+
+	if (board_config != ALC882_AUTO)
+		setup_preset(spec, &alc882_presets[board_config]);
+
+	if (board_config == ALC885_MACPRO) {
+		alc882_gpio_mute(codec, 0, 0);
+		alc882_gpio_mute(codec, 1, 0);
+	}
+
+	spec->stream_name_analog = "ALC882 Analog";
+	spec->stream_analog_playback = &alc882_pcm_analog_playback;
+	spec->stream_analog_capture = &alc882_pcm_analog_capture;
+
+	spec->stream_name_digital = "ALC882 Digital";
+	spec->stream_digital_playback = &alc882_pcm_digital_playback;
+	spec->stream_digital_capture = &alc882_pcm_digital_capture;
+
+	if (! spec->adc_nids && spec->input_mux) {
+		/* check whether NID 0x07 is valid */
+		unsigned int wcap = get_wcaps(codec, 0x07);
+		wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; /* get type */
+		if (wcap != AC_WID_AUD_IN) {
+			spec->adc_nids = alc882_adc_nids_alt;
+			spec->num_adc_nids = ARRAY_SIZE(alc882_adc_nids_alt);
+			spec->mixers[spec->num_mixers] = alc882_capture_alt_mixer;
+			spec->num_mixers++;
+		} else {
+			spec->adc_nids = alc882_adc_nids;
+			spec->num_adc_nids = ARRAY_SIZE(alc882_adc_nids);
+			spec->mixers[spec->num_mixers] = alc882_capture_mixer;
+			spec->num_mixers++;
+		}
+	}
+
+	codec->patch_ops = alc_patch_ops;
+	if (board_config == ALC882_AUTO)
+		spec->init_hook = alc882_auto_init;
+
+	return 0;
+}
+
+/*
+ * ALC883 support
+ *
+ * ALC883 is almost identical with ALC880 but has cleaner and more flexible
+ * configuration.  Each pin widget can choose any input DACs and a mixer.
+ * Each ADC is connected from a mixer of all inputs.  This makes possible
+ * 6-channel independent captures.
+ *
+ * In addition, an independent DAC for the multi-playback (not used in this
+ * driver yet).
+ */
+#define ALC883_DIGOUT_NID	0x06
+#define ALC883_DIGIN_NID	0x0a
+
+static hda_nid_t alc883_dac_nids[4] = {
+	/* front, rear, clfe, rear_surr */
+	0x02, 0x04, 0x03, 0x05
+};
+
+static hda_nid_t alc883_adc_nids[2] = {
+	/* ADC1-2 */
+	0x08, 0x09,
+};
+/* input MUX */
+/* FIXME: should be a matrix-type input source selection */
+
+static struct hda_input_mux alc883_capture_source = {
+	.num_items = 4,
+	.items = {
+		{ "Mic", 0x0 },
+		{ "Front Mic", 0x1 },
+		{ "Line", 0x2 },
+		{ "CD", 0x4 },
+	},
+};
+#define alc883_mux_enum_info alc_mux_enum_info
+#define alc883_mux_enum_get alc_mux_enum_get
+
+static int alc883_mux_enum_put(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct alc_spec *spec = codec->spec;
+	const struct hda_input_mux *imux = spec->input_mux;
+	unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+	static hda_nid_t capture_mixers[3] = { 0x24, 0x23, 0x22 };
+	hda_nid_t nid = capture_mixers[adc_idx];
+	unsigned int *cur_val = &spec->cur_mux[adc_idx];
+	unsigned int i, idx;
+
+	idx = ucontrol->value.enumerated.item[0];
+	if (idx >= imux->num_items)
+		idx = imux->num_items - 1;
+	if (*cur_val == idx && ! codec->in_resume)
+		return 0;
+	for (i = 0; i < imux->num_items; i++) {
+		unsigned int v = (i == idx) ? 0x7000 : 0x7080;
+		snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+				    v | (imux->items[i].index << 8));
+	}
+	*cur_val = idx;
+	return 1;
+}
+/*
+ * 2ch mode
+ */
+static struct hda_channel_mode alc883_3ST_2ch_modes[1] = {
+	{ 2, NULL }
+};
+
+/*
+ * 2ch mode
+ */
+static struct hda_verb alc883_3ST_ch2_init[] = {
+	{ 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
+	{ 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+	{ 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
+	{ 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+	{0} /* end */
+};
+
+/*
+ * 6ch mode
+ */
+static struct hda_verb alc883_3ST_ch6_init[] = {
+	{ 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+	{ 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 },
+	{ 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+	{ 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
+	{0} /* end */
+};
+
+static struct hda_channel_mode alc883_3ST_6ch_modes[2] = {
+	{ 2, alc883_3ST_ch2_init },
+	{ 6, alc883_3ST_ch6_init },
+};
+
+/*
+ * 6ch mode
+ */
+static struct hda_verb alc883_sixstack_ch6_init[] = {
+	{ 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 },
+	{ 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{0} /* end */
+};
+
+/*
+ * 8ch mode
+ */
+static struct hda_verb alc883_sixstack_ch8_init[] = {
+	{ 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{0} /* end */
+};
+
+static struct hda_channel_mode alc883_sixstack_modes[2] = {
+	{ 6, alc883_sixstack_ch6_init },
+	{ 8, alc883_sixstack_ch8_init },
+};
+
+static struct hda_verb alc883_medion_eapd_verbs[] = {
+        /* eanable EAPD on medion laptop */
+	{0x20, AC_VERB_SET_COEF_INDEX, 0x07},
+	{0x20, AC_VERB_SET_PROC_COEF, 0x3070},
+	{0}
+};
+
+/* Pin assignment: Front=0x14, Rear=0x15, CLFE=0x16, Side=0x17
+ *                 Mic=0x18, Front Mic=0x19, Line-In=0x1a, HP=0x1b
+ */
+
+static struct snd_kcontrol_new alc883_base_mixer[] = {
+	HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
+	HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
+	HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
+	HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
+	HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT),
+	HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
+	HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
+	HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
+	HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
+	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
+	HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
+	HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
+	HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		/* .name = "Capture Source", */
+		.name = "Input Source",
+		.count = 2,
+		.info = alc883_mux_enum_info,
+		.get = alc883_mux_enum_get,
+		.put = alc883_mux_enum_put,
+	},
+	{0} /* end */
+};
+
+static struct snd_kcontrol_new alc883_3ST_2ch_mixer[] = {
+	HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
+	HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
+	HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
+	HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
+	HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
+	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
+	HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
+	HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
+	HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		/* .name = "Capture Source", */
+		.name = "Input Source",
+		.count = 2,
+		.info = alc883_mux_enum_info,
+		.get = alc883_mux_enum_get,
+		.put = alc883_mux_enum_put,
+	},
+	{0} /* end */
+};
+
+static struct snd_kcontrol_new alc883_3ST_6ch_mixer[] = {
+	HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
+	HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
+	HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
+	HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
+	HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
+	HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
+	HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
+	HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
+	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
+	HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
+	HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
+	HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		/* .name = "Capture Source", */
+		.name = "Input Source",
+		.count = 2,
+		.info = alc883_mux_enum_info,
+		.get = alc883_mux_enum_get,
+		.put = alc883_mux_enum_put,
+	},
+	{0} /* end */
+};
+
+static struct snd_kcontrol_new alc883_fivestack_mixer[] = {
+	HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Surround Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x16, 1, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x16, 2, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
+	HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
+	HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
+	HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
+	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
+	HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
+	HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
+	HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
+
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		/* .name = "Capture Source", */
+		.name = "Input Source",
+		.count = 1,
+		.info = alc883_mux_enum_info,
+		.get = alc883_mux_enum_get,
+		.put = alc883_mux_enum_put,
+	},
+	{0} /* end */
+};
+
+static struct snd_kcontrol_new alc883_tagra_mixer[] = {
+	HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Headphone Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
+	HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
+	HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
+	HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
+	HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
+	HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
+	HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		/* .name = "Capture Source", */
+		.name = "Input Source",
+		.count = 2,
+		.info = alc883_mux_enum_info,
+		.get = alc883_mux_enum_get,
+		.put = alc883_mux_enum_put,
+	},
+	{0} /* end */
+};	
+
+static struct snd_kcontrol_new alc883_tagra_2ch_mixer[] = {
+	HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Headphone Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
+	HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		/* .name = "Capture Source", */
+		.name = "Input Source",
+		.count = 2,
+		.info = alc883_mux_enum_info,
+		.get = alc883_mux_enum_get,
+		.put = alc883_mux_enum_put,
+	},
+	{0} /* end */
+};	
+
+static struct snd_kcontrol_new alc883_chmode_mixer[] = {
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Channel Mode",
+		.info = alc_ch_mode_info,
+		.get = alc_ch_mode_get,
+		.put = alc_ch_mode_put,
+	},
+	{0} /* end */
+};
+
+static struct hda_verb alc883_init_verbs[] = {
+	/* ADC1: mute amp left and right */
+	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
+	/* ADC2: mute amp left and right */
+	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
+	/* Front mixer: unmute input/output amp left and right (volume = 0) */
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	/* Rear mixer */
+	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	/* CLFE mixer */
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	/* Side mixer */
+	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
+
+	/* Front Pin: output 0 (0x0c) */
+	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
+	/* Rear Pin: output 1 (0x0d) */
+	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
+	/* CLFE Pin: output 2 (0x0e) */
+	{0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x16, AC_VERB_SET_CONNECT_SEL, 0x02},
+	/* Side Pin: output 3 (0x0f) */
+	{0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x17, AC_VERB_SET_CONNECT_SEL, 0x03},
+	/* Mic (rear) pin: input vref at 80% */
+	{0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	/* Front Mic pin: input vref at 80% */
+	{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	/* Line In pin: input */
+	{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	/* Line-2 In: Headphone output (output 0 - 0x0c) */
+	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
+	/* CD pin widget for input */
+	{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+
+	/* FIXME: use matrix-type input source selection */
+	/* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
+	/* Input mixer2 */
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
+	/* Input mixer3 */
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
+	{0}
+};
+
+static struct hda_verb alc883_tagra_verbs[] = {
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+
+	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	
+	{0x18, AC_VERB_SET_CONNECT_SEL, 0x02}, /* mic/clfe */
+	{0x1a, AC_VERB_SET_CONNECT_SEL, 0x01}, /* line/surround */
+	{0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */
+
+	{0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+	{0x01, AC_VERB_SET_GPIO_MASK, 0x03}, 
+	{0x01, AC_VERB_SET_GPIO_DIRECTION, 0x03}, 
+	{0x01, AC_VERB_SET_GPIO_DATA, 0x03}, 
+
+	{0} /* end */
+};
+
+/* toggle speaker-output according to the hp-jack state */
+static void alc883_tagra_automute(struct hda_codec *codec)
+{
+ 	unsigned int present;
+
+ 	present = snd_hda_codec_read(codec, 0x14, 0,
+				     AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+	snd_hda_codec_amp_update(codec, 0x1b, 0, HDA_OUTPUT, 0,
+				 0x80, present ? 0x80 : 0);
+	snd_hda_codec_amp_update(codec, 0x1b, 1, HDA_OUTPUT, 0,
+				 0x80, present ? 0x80 : 0);
+	snd_hda_codec_write(codec, 1, 0, AC_VERB_SET_GPIO_DATA, present ? 1 : 3);
+}
+
+static void alc883_tagra_unsol_event(struct hda_codec *codec, unsigned int res)
+{
+	if ((res >> 26) == ALC880_HP_EVENT)
+		alc883_tagra_automute(codec);
+}
+
+/*
+ * generic initialization of ADC, input mixers and output mixers
+ */
+static struct hda_verb alc883_auto_init_verbs[] = {
+	/*
+	 * Unmute ADC0-2 and set the default input to mic-in
+	 */
+	{0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
+	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
+	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+
+	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
+	 * mixer widget
+	 * Note: PASD motherboards uses the Line In 2 as the input for front panel
+	 * mic (mic 2)
+	 */
+	/* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
+
+	/*
+	 * Set up output mixers (0x0c - 0x0f)
+	 */
+	/* set vol=0 to output mixers */
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	/* set up input amps for analog loopback */
+	/* Amp Indices: DAC = 0, mixer = 1 */
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+
+	/* FIXME: use matrix-type input source selection */
+	/* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
+	/* Input mixer1 */
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+	//{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
+	/* Input mixer2 */
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+	//{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
+
+	{0}
+};
+
+/* capture mixer elements */
+static struct snd_kcontrol_new alc883_capture_mixer[] = {
+	HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		/* The multiple "Capture Source" controls confuse alsamixer
+		 * So call somewhat different..
+		 * FIXME: the controls appear in the "playback" view!
+		 */
+		/* .name = "Capture Source", */
+		.name = "Input Source",
+		.count = 2,
+		.info = alc882_mux_enum_info,
+		.get = alc882_mux_enum_get,
+		.put = alc882_mux_enum_put,
+	},
+	{0} /* end */
+};
+
+/* pcm configuration: identiacal with ALC880 */
+#define alc883_pcm_analog_playback	alc880_pcm_analog_playback
+#define alc883_pcm_analog_capture	alc880_pcm_analog_capture
+#define alc883_pcm_digital_playback	alc880_pcm_digital_playback
+#define alc883_pcm_digital_capture	alc880_pcm_digital_capture
+
+/*
+ * configuration and preset
+ */
+static const char *alc883_models[ALC883_MODEL_LAST] = {
+	[ALC883_3ST_2ch_DIG]	= "3stack-dig",
+	[ALC883_3ST_6ch_DIG]	= "3stack-6ch-dig",
+	[ALC883_3ST_6ch]	= "3stack-6ch",
+	[ALC883_6ST_DIG]	= "6stack-dig",
+	[ALC883_TARGA_DIG]	= "targa-dig",
+	[ALC883_TARGA_2ch_DIG]	= "targa-2ch-dig",
+	[ALC888_DEMO_BOARD]	= "6stack-dig-demo",
+	[ALC883_ACER]		= "acer",
+	[ALC883_MEDION]		= "medion",
+	[ALC883_LAPTOP_EAPD]	= "laptop-eapd",
+	[ALC883_AUTO]		= "auto",
+};
+
+static struct snd_pci_quirk alc883_cfg_tbl[] = {
+	SND_PCI_QUIRK(0x1019, 0x6668, "ECS", ALC883_3ST_6ch_DIG),
+	SND_PCI_QUIRK(0x108e, 0x534d, NULL, ALC883_3ST_6ch),
+	SND_PCI_QUIRK(0x1558, 0, "Clevo laptop", ALC883_LAPTOP_EAPD),
+	SND_PCI_QUIRK(0x105b, 0x6668, "Foxconn", ALC883_6ST_DIG),
+	SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC883_6ST_DIG),
+        SND_PCI_QUIRK(0x1462, 0x7187, "MSI", ALC883_6ST_DIG),
+        SND_PCI_QUIRK(0x1462, 0x7280, "MSI", ALC883_6ST_DIG),
+	SND_PCI_QUIRK(0x1462, 0x0579, "MSI", ALC883_TARGA_2ch_DIG),
+	SND_PCI_QUIRK(0x1462, 0x3ef9, "MSI", ALC883_TARGA_DIG),
+	SND_PCI_QUIRK(0x1462, 0x3b7f, "MSI", ALC883_TARGA_2ch_DIG),
+	SND_PCI_QUIRK(0x1462, 0x3fcc, "MSI", ALC883_TARGA_DIG),
+	SND_PCI_QUIRK(0x1462, 0x3fc1, "MSI", ALC883_TARGA_DIG),
+	SND_PCI_QUIRK(0x1462, 0x3fc3, "MSI", ALC883_TARGA_DIG),
+	SND_PCI_QUIRK(0x1462, 0x4314, "MSI", ALC883_TARGA_DIG),
+	SND_PCI_QUIRK(0x1462, 0x4319, "MSI", ALC883_TARGA_DIG),
+	SND_PCI_QUIRK(0x1462, 0x4324, "MSI", ALC883_TARGA_DIG),
+	SND_PCI_QUIRK(0x1462, 0xa422, "MSI", ALC883_TARGA_2ch_DIG),
+	SND_PCI_QUIRK(0x1025, 0, "Acer laptop", ALC883_ACER),
+	SND_PCI_QUIRK(0x161f, 0x2054, "Medion laptop", ALC883_MEDION),
+	SND_PCI_QUIRK(0x1071, 0x8258, "Evesham Voyaeger", ALC883_LAPTOP_EAPD),
+	SND_PCI_QUIRK(0x8086, 0xd601, "D102GGC", ALC883_3ST_6ch),
+	{0}
+};
+
+static struct alc_config_preset alc883_presets[] = {
+	[ALC883_3ST_2ch_DIG] = {
+		.mixers = { alc883_3ST_2ch_mixer },
+		.init_verbs = { alc883_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc883_dac_nids),
+		.dac_nids = alc883_dac_nids,
+		.dig_out_nid = ALC883_DIGOUT_NID,
+		.num_adc_nids = ARRAY_SIZE(alc883_adc_nids),
+		.adc_nids = alc883_adc_nids,
+		.dig_in_nid = ALC883_DIGIN_NID,
+		.num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
+		.channel_mode = alc883_3ST_2ch_modes,
+		.input_mux = &alc883_capture_source,
+	},
+	[ALC883_3ST_6ch_DIG] = {
+		.mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer },
+		.init_verbs = { alc883_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc883_dac_nids),
+		.dac_nids = alc883_dac_nids,
+		.dig_out_nid = ALC883_DIGOUT_NID,
+		.num_adc_nids = ARRAY_SIZE(alc883_adc_nids),
+		.adc_nids = alc883_adc_nids,
+		.dig_in_nid = ALC883_DIGIN_NID,
+		.num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes),
+		.channel_mode = alc883_3ST_6ch_modes,
+		.need_dac_fix = 1,
+		.input_mux = &alc883_capture_source,
+	},	
+	[ALC883_3ST_6ch] = {
+		.mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer },
+		.init_verbs = { alc883_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc883_dac_nids),
+		.dac_nids = alc883_dac_nids,
+		.num_adc_nids = ARRAY_SIZE(alc883_adc_nids),
+		.adc_nids = alc883_adc_nids,
+		.num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes),
+		.channel_mode = alc883_3ST_6ch_modes,
+		.need_dac_fix = 1,
+		.input_mux = &alc883_capture_source,
+	},	
+	[ALC883_6ST_DIG] = {
+		.mixers = { alc883_base_mixer, alc883_chmode_mixer },
+		.init_verbs = { alc883_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc883_dac_nids),
+		.dac_nids = alc883_dac_nids,
+		.dig_out_nid = ALC883_DIGOUT_NID,
+		.num_adc_nids = ARRAY_SIZE(alc883_adc_nids),
+		.adc_nids = alc883_adc_nids,
+		.dig_in_nid = ALC883_DIGIN_NID,
+		.num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes),
+		.channel_mode = alc883_sixstack_modes,
+		.input_mux = &alc883_capture_source,
+	},
+	[ALC883_TARGA_DIG] = {
+		.mixers = { alc883_tagra_mixer, alc883_chmode_mixer },
+		.init_verbs = { alc883_init_verbs, alc883_tagra_verbs},
+		.num_dacs = ARRAY_SIZE(alc883_dac_nids),
+		.dac_nids = alc883_dac_nids,
+		.dig_out_nid = ALC883_DIGOUT_NID,
+		.num_adc_nids = ARRAY_SIZE(alc883_adc_nids),
+		.adc_nids = alc883_adc_nids,
+		.num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes),
+		.channel_mode = alc883_3ST_6ch_modes,
+		.need_dac_fix = 1,
+		.input_mux = &alc883_capture_source,
+		.unsol_event = alc883_tagra_unsol_event,
+		.init_hook = alc883_tagra_automute,
+	},
+	[ALC883_TARGA_2ch_DIG] = {
+		.mixers = { alc883_tagra_2ch_mixer},
+		.init_verbs = { alc883_init_verbs, alc883_tagra_verbs},
+		.num_dacs = ARRAY_SIZE(alc883_dac_nids),
+		.dac_nids = alc883_dac_nids,
+		.dig_out_nid = ALC883_DIGOUT_NID,
+		.num_adc_nids = ARRAY_SIZE(alc883_adc_nids),
+		.adc_nids = alc883_adc_nids,
+		.num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
+		.channel_mode = alc883_3ST_2ch_modes,
+		.input_mux = &alc883_capture_source,
+		.unsol_event = alc883_tagra_unsol_event,
+		.init_hook = alc883_tagra_automute,
+	},
+	[ALC888_DEMO_BOARD] = {
+		.mixers = { alc883_base_mixer, alc883_chmode_mixer },
+		.init_verbs = { alc883_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc883_dac_nids),
+		.dac_nids = alc883_dac_nids,
+		.dig_out_nid = ALC883_DIGOUT_NID,
+		.num_adc_nids = ARRAY_SIZE(alc883_adc_nids),
+		.adc_nids = alc883_adc_nids,
+		.dig_in_nid = ALC883_DIGIN_NID,
+		.num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes),
+		.channel_mode = alc883_sixstack_modes,
+		.input_mux = &alc883_capture_source,
+	},
+	[ALC883_ACER] = {
+		.mixers = { alc883_base_mixer,
+			    alc883_chmode_mixer },
+		/* On TravelMate laptops, GPIO 0 enables the internal speaker
+		 * and the headphone jack.  Turn this on and rely on the
+		 * standard mute methods whenever the user wants to turn
+		 * these outputs off.
+		 */
+		.init_verbs = { alc883_init_verbs, alc880_gpio1_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc883_dac_nids),
+		.dac_nids = alc883_dac_nids,
+		.num_adc_nids = ARRAY_SIZE(alc883_adc_nids),
+		.adc_nids = alc883_adc_nids,
+		.num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
+		.channel_mode = alc883_3ST_2ch_modes,
+		.input_mux = &alc883_capture_source,
+	},
+	[ALC883_MEDION] = {
+		.mixers = { alc883_fivestack_mixer,
+			    alc883_chmode_mixer },
+		.init_verbs = { alc883_init_verbs,
+				alc883_medion_eapd_verbs },
+		.num_dacs = ARRAY_SIZE(alc883_dac_nids),
+		.dac_nids = alc883_dac_nids,
+		.num_adc_nids = ARRAY_SIZE(alc883_adc_nids),
+		.adc_nids = alc883_adc_nids,
+		.num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes),
+		.channel_mode = alc883_sixstack_modes,
+		.input_mux = &alc883_capture_source,
+	},
+	[ALC883_LAPTOP_EAPD] = {
+		.mixers = { alc883_base_mixer,
+			    alc883_chmode_mixer },
+		.init_verbs = { alc883_init_verbs, alc882_eapd_verbs },
+		.num_dacs = ARRAY_SIZE(alc883_dac_nids),
+		.dac_nids = alc883_dac_nids,
+		.num_adc_nids = ARRAY_SIZE(alc883_adc_nids),
+		.adc_nids = alc883_adc_nids,
+		.num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
+		.channel_mode = alc883_3ST_2ch_modes,
+		.input_mux = &alc883_capture_source,
+	},
+};
+
+
+/*
+ * BIOS auto configuration
+ */
+static void alc883_auto_set_output_and_unmute(struct hda_codec *codec,
+					      hda_nid_t nid, int pin_type,
+					      int dac_idx)
+{
+	/* set as output */
+	struct alc_spec *spec = codec->spec;
+	int idx; 
+	
+	if (spec->multiout.dac_nids[dac_idx] == 0x25)
+		idx = 4;
+	else
+		idx = spec->multiout.dac_nids[dac_idx] - 2;
+
+	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+			    pin_type);
+	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+			    AMP_OUT_UNMUTE);
+	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, idx);
+
+}
+
+static void alc883_auto_init_multi_out(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+	int i;
+
+	for (i = 0; i <= HDA_SIDE; i++) {
+		hda_nid_t nid = spec->autocfg.line_out_pins[i];	
+		if (nid)
+			alc883_auto_set_output_and_unmute(codec, nid, PIN_OUT, i);
+	}
+}
+
+static void alc883_auto_init_hp_out(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+	hda_nid_t pin;
+
+	pin = spec->autocfg.hp_pins[0];
+	if (pin) /* connect to front */
+		/* use dac 0 */
+		alc883_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
+}
+
+#define alc883_is_input_pin(nid)	alc880_is_input_pin(nid)
+#define ALC883_PIN_CD_NID		ALC880_PIN_CD_NID
+
+static void alc883_auto_init_analog_input(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+	int i;
+
+	for (i = 0; i < AUTO_PIN_LAST; i++) {
+		hda_nid_t nid = spec->autocfg.input_pins[i];
+		if (alc883_is_input_pin(nid)) {
+			snd_hda_codec_write(codec, nid, 0,
+					    AC_VERB_SET_PIN_WIDGET_CONTROL,
+					    (i <= AUTO_PIN_FRONT_MIC ?
+					     PIN_VREF80 : PIN_IN));
+			if (nid != ALC883_PIN_CD_NID)
+				snd_hda_codec_write(codec, nid, 0,
+						    AC_VERB_SET_AMP_GAIN_MUTE,
+						    AMP_OUT_MUTE);
+		}
+	}
+}
+
+/* almost identical with ALC880 parser... */
+static int alc883_parse_auto_config(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+	int err = alc880_parse_auto_config(codec);
+
+	if (err < 0)
+		return err;
+	else if (err > 0)
+		/* hack - override the init verbs */
+		spec->init_verbs[0] = alc883_auto_init_verbs;
+                spec->mixers[spec->num_mixers] = alc883_capture_mixer;
+		spec->num_mixers++;
+	return err;
+}
+
+/* additional initialization for auto-configuration model */
+static void alc883_auto_init(struct hda_codec *codec)
+{
+	alc883_auto_init_multi_out(codec);
+	alc883_auto_init_hp_out(codec);
+	alc883_auto_init_analog_input(codec);
+}
+
+static int patch_alc883(struct hda_codec *codec)
+{
+	struct alc_spec *spec;
+	int err, board_config;
+
+	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+	if (spec == NULL)
+		return -ENOMEM;
+
+	codec->spec = spec;
+
+	board_config = snd_hda_check_board_config(codec, ALC883_MODEL_LAST,
+						  alc883_models,
+						  alc883_cfg_tbl);
+	if (board_config < 0) {
+		printk(KERN_INFO "hda_codec: Unknown model for ALC883, "
+		       "trying auto-probe from BIOS...\n");
+		board_config = ALC883_AUTO;
+	}
+
+	if (board_config == ALC883_AUTO) {
+		/* automatic parse from the BIOS config */
+		err = alc883_parse_auto_config(codec);
+		if (err < 0) {
+			alc_free(codec);
+			return err;
+		} else if (! err) {
+			printk(KERN_INFO
+			       "hda_codec: Cannot set up configuration "
+			       "from BIOS.  Using base mode...\n");
+			board_config = ALC883_3ST_2ch_DIG;
+		}
+	}
+
+	if (board_config != ALC883_AUTO)
+		setup_preset(spec, &alc883_presets[board_config]);
+
+	spec->stream_name_analog = "ALC883 Analog";
+	spec->stream_analog_playback = &alc883_pcm_analog_playback;
+	spec->stream_analog_capture = &alc883_pcm_analog_capture;
+
+	spec->stream_name_digital = "ALC883 Digital";
+	spec->stream_digital_playback = &alc883_pcm_digital_playback;
+	spec->stream_digital_capture = &alc883_pcm_digital_capture;
+
+	if (! spec->adc_nids && spec->input_mux) {
+		spec->adc_nids = alc883_adc_nids;
+		spec->num_adc_nids = ARRAY_SIZE(alc883_adc_nids);
+	}
+
+	codec->patch_ops = alc_patch_ops;
+	if (board_config == ALC883_AUTO)
+		spec->init_hook = alc883_auto_init;
+
+	return 0;
+}
+
+/*
+ * ALC262 support
+ */
+
+#define ALC262_DIGOUT_NID	ALC880_DIGOUT_NID
+#define ALC262_DIGIN_NID	ALC880_DIGIN_NID
+
+#define alc262_dac_nids		alc260_dac_nids
+#define alc262_adc_nids		alc882_adc_nids
+#define alc262_adc_nids_alt	alc882_adc_nids_alt
+
+#define alc262_modes		alc260_modes
+#define alc262_capture_source	alc882_capture_source
+
+static struct snd_kcontrol_new alc262_base_mixer[] = {
+	HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
+	HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
+	HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
+	HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
+	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
+	/* HDA_CODEC_VOLUME("PC Beep Playback Volume", 0x0b, 0x05, HDA_INPUT),
+	   HDA_CODEC_MUTE("PC Beelp Playback Switch", 0x0b, 0x05, HDA_INPUT), */
+	HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0D, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x16, 2, 0x0, HDA_OUTPUT),
+	{0} /* end */
+};
+
+static struct snd_kcontrol_new alc262_hippo1_mixer[] = {
+	HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
+	HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
+	HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
+	HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
+	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
+	/* HDA_CODEC_VOLUME("PC Beep Playback Volume", 0x0b, 0x05, HDA_INPUT),
+	   HDA_CODEC_MUTE("PC Beelp Playback Switch", 0x0b, 0x05, HDA_INPUT), */
+	/*HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0D, 0x0, HDA_OUTPUT),*/
+	HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
+	{0} /* end */
+};
+
+static struct snd_kcontrol_new alc262_HP_BPC_mixer[] = {
+	HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Front Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x16, 2, 0x0, HDA_OUTPUT),
+
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
+	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
+	HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
+	HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
+	HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
+	HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
+	HDA_CODEC_VOLUME("PC Beep Playback Volume", 0x0b, 0x05, HDA_INPUT),
+	HDA_CODEC_MUTE("PC Beep Playback Switch", 0x0b, 0x05, HDA_INPUT),
+	HDA_CODEC_VOLUME("AUX IN Playback Volume", 0x0b, 0x06, HDA_INPUT),
+	HDA_CODEC_MUTE("AUX IN Playback Switch", 0x0b, 0x06, HDA_INPUT),
+	{0} /* end */
+};
+
+#define alc262_capture_mixer		alc882_capture_mixer
+#define alc262_capture_alt_mixer	alc882_capture_alt_mixer
+
+/*
+ * generic initialization of ADC, input mixers and output mixers
+ */
+static struct hda_verb alc262_init_verbs[] = {
+	/*
+	 * Unmute ADC0-2 and set the default input to mic-in
+	 */
+	{0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
+	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
+	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
+	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+
+	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
+	 * mixer widget
+	 * Note: PASD motherboards uses the Line In 2 as the input for front panel
+	 * mic (mic 2)
+	 */
+	/* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
+
+	/*
+	 * Set up output mixers (0x0c - 0x0e)
+	 */
+	/* set vol=0 to output mixers */
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	/* set up input amps for analog loopback */
+	/* Amp Indices: DAC = 0, mixer = 1 */
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+
+	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0},
+	{0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+	{0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
+	{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
+	{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
+
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
+	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
+	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
+	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
+	
+	{0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
+	{0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
+	
+	/* FIXME: use matrix-type input source selection */
+	/* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
+	/* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
+	/* Input mixer2 */
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
+	/* Input mixer3 */
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},	
+
+	{0}
+};
+
+static struct hda_verb alc262_hippo_unsol_verbs[] = {
+	{0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0}
+};
+
+static struct hda_verb alc262_hippo1_unsol_verbs[] = {
+	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0},
+	{0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
+	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
+
+	{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0}
+};
+
+/* mute/unmute internal speaker according to the hp jack and mute state */
+static void alc262_hippo_automute(struct hda_codec *codec, int force)
+{
+	struct alc_spec *spec = codec->spec;
+	unsigned int mute;
+
+	if (force || ! spec->sense_updated) {
+		unsigned int present;
+		/* need to execute and sync at first */
+		snd_hda_codec_read(codec, 0x15, 0, AC_VERB_SET_PIN_SENSE, 0);
+		present = snd_hda_codec_read(codec, 0x15, 0,
+				    	 AC_VERB_GET_PIN_SENSE, 0);
+		spec->jack_present = (present & 0x80000000) != 0;
+		spec->sense_updated = 1;
+	}
+	if (spec->jack_present) {
+		/* mute internal speaker */
+		snd_hda_codec_amp_update(codec, 0x14, 0, HDA_OUTPUT, 0,
+					 0x80, 0x80);
+		snd_hda_codec_amp_update(codec, 0x14, 1, HDA_OUTPUT, 0,
+					 0x80, 0x80);
+	} else {
+		/* unmute internal speaker if necessary */
+		mute = snd_hda_codec_amp_read(codec, 0x15, 0, HDA_OUTPUT, 0);
+		snd_hda_codec_amp_update(codec, 0x14, 0, HDA_OUTPUT, 0,
+					 0x80, mute & 0x80);
+		mute = snd_hda_codec_amp_read(codec, 0x15, 1, HDA_OUTPUT, 0);
+		snd_hda_codec_amp_update(codec, 0x14, 1, HDA_OUTPUT, 0,
+					 0x80, mute & 0x80);
+	}
+}
+
+/* unsolicited event for HP jack sensing */
+static void alc262_hippo_unsol_event(struct hda_codec *codec,
+				       unsigned int res)
+{
+	if ((res >> 26) != ALC880_HP_EVENT)
+		return;
+	alc262_hippo_automute(codec, 1);
+}
+
+static void alc262_hippo1_automute(struct hda_codec *codec, int force)
+{
+	struct alc_spec *spec = codec->spec;
+	unsigned int mute;
+
+	if (force || ! spec->sense_updated) {
+		unsigned int present;
+		/* need to execute and sync at first */
+		snd_hda_codec_read(codec, 0x1b, 0, AC_VERB_SET_PIN_SENSE, 0);
+		present = snd_hda_codec_read(codec, 0x1b, 0,
+				    	 AC_VERB_GET_PIN_SENSE, 0);
+		spec->jack_present = (present & 0x80000000) != 0;
+		spec->sense_updated = 1;
+	}
+	if (spec->jack_present) {
+		/* mute internal speaker */
+		snd_hda_codec_amp_update(codec, 0x14, 0, HDA_OUTPUT, 0,
+					 0x80, 0x80);
+		snd_hda_codec_amp_update(codec, 0x14, 1, HDA_OUTPUT, 0,
+					 0x80, 0x80);
+	} else {
+		/* unmute internal speaker if necessary */
+		mute = snd_hda_codec_amp_read(codec, 0x1b, 0, HDA_OUTPUT, 0);
+		snd_hda_codec_amp_update(codec, 0x14, 0, HDA_OUTPUT, 0,
+					 0x80, mute & 0x80);
+		mute = snd_hda_codec_amp_read(codec, 0x1b, 1, HDA_OUTPUT, 0);
+		snd_hda_codec_amp_update(codec, 0x14, 1, HDA_OUTPUT, 0,
+					 0x80, mute & 0x80);
+	}
+}
+
+/* unsolicited event for HP jack sensing */
+static void alc262_hippo1_unsol_event(struct hda_codec *codec,
+				       unsigned int res)
+{
+	if ((res >> 26) != ALC880_HP_EVENT)
+		return;
+	alc262_hippo1_automute(codec, 1);
+}
+
+/*
+ * fujitsu model
+ *  0x14 = headphone/spdif-out, 0x15 = internal speaker
+ */
+
+#define ALC_HP_EVENT	0x37
+
+static struct hda_verb alc262_fujitsu_unsol_verbs[] = {
+	{0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT},
+	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0}
+};
+
+static struct hda_input_mux alc262_fujitsu_capture_source = {
+	.num_items = 2,
+	.items = {
+		{ "Mic", 0x0 },
+		{ "CD", 0x4 },
+	},
+};
+
+static struct hda_input_mux alc262_HP_capture_source = {
+	.num_items = 5,
+	.items = {
+		{ "Mic", 0x0 },
+		{ "Front Mic", 0x3 },
+		{ "Line", 0x2 },
+		{ "CD", 0x4 },
+		{ "AUX IN", 0x6 },
+	},
+};
+
+/* mute/unmute internal speaker according to the hp jack and mute state */
+static void alc262_fujitsu_automute(struct hda_codec *codec, int force)
+{
+	struct alc_spec *spec = codec->spec;
+	unsigned int mute;
+
+	if (force || ! spec->sense_updated) {
+		unsigned int present;
+		/* need to execute and sync at first */
+		snd_hda_codec_read(codec, 0x14, 0, AC_VERB_SET_PIN_SENSE, 0);
+		present = snd_hda_codec_read(codec, 0x14, 0,
+				    	 AC_VERB_GET_PIN_SENSE, 0);
+		spec->jack_present = (present & 0x80000000) != 0;
+		spec->sense_updated = 1;
+	}
+	if (spec->jack_present) {
+		/* mute internal speaker */
+		snd_hda_codec_amp_update(codec, 0x15, 0, HDA_OUTPUT, 0,
+					 0x80, 0x80);
+		snd_hda_codec_amp_update(codec, 0x15, 1, HDA_OUTPUT, 0,
+					 0x80, 0x80);
+	} else {
+		/* unmute internal speaker if necessary */
+		mute = snd_hda_codec_amp_read(codec, 0x14, 0, HDA_OUTPUT, 0);
+		snd_hda_codec_amp_update(codec, 0x15, 0, HDA_OUTPUT, 0,
+					 0x80, mute & 0x80);
+		mute = snd_hda_codec_amp_read(codec, 0x14, 1, HDA_OUTPUT, 0);
+		snd_hda_codec_amp_update(codec, 0x15, 1, HDA_OUTPUT, 0,
+					 0x80, mute & 0x80);
+	}
+}
+
+/* unsolicited event for HP jack sensing */
+static void alc262_fujitsu_unsol_event(struct hda_codec *codec,
+				       unsigned int res)
+{
+	if ((res >> 26) != ALC_HP_EVENT)
+		return;
+	alc262_fujitsu_automute(codec, 1);
+}
+
+/* bind volumes of both NID 0x0c and 0x0d */
+static int alc262_fujitsu_master_vol_put(struct snd_kcontrol *kcontrol,
+					 struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	long *valp = ucontrol->value.integer.value;
+	int change;
+
+	change = snd_hda_codec_amp_update(codec, 0x0c, 0, HDA_OUTPUT, 0,
+					  0x7f, valp[0] & 0x7f);
+	change |= snd_hda_codec_amp_update(codec, 0x0c, 1, HDA_OUTPUT, 0,
+					   0x7f, valp[1] & 0x7f);
+	snd_hda_codec_amp_update(codec, 0x0d, 0, HDA_OUTPUT, 0,
+				 0x7f, valp[0] & 0x7f);
+	snd_hda_codec_amp_update(codec, 0x0d, 1, HDA_OUTPUT, 0,
+				 0x7f, valp[1] & 0x7f);
+	return change;
+}
+
+/* bind hp and internal speaker mute (with plug check) */
+static int alc262_fujitsu_master_sw_put(struct snd_kcontrol *kcontrol,
+					 struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	long *valp = ucontrol->value.integer.value;
+	int change;
+
+	change = snd_hda_codec_amp_update(codec, 0x14, 0, HDA_OUTPUT, 0,
+					  0x80, valp[0] ? 0 : 0x80);
+	change |= snd_hda_codec_amp_update(codec, 0x14, 1, HDA_OUTPUT, 0,
+					   0x80, valp[1] ? 0 : 0x80);
+	if (change || codec->in_resume)
+		alc262_fujitsu_automute(codec, codec->in_resume);
+	return change;
+}
+
+static struct snd_kcontrol_new alc262_fujitsu_mixer[] = {
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Master Playback Volume",
+		.info = snd_hda_mixer_amp_volume_info,
+		.get = snd_hda_mixer_amp_volume_get,
+		.put = alc262_fujitsu_master_vol_put,
+		.tlv = { .c = snd_hda_mixer_amp_tlv },
+		.private_value = HDA_COMPOSE_AMP_VAL(0x0c, 3, 0, HDA_OUTPUT),
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Master Playback Switch",
+		.info = snd_hda_mixer_amp_switch_info,
+		.get = snd_hda_mixer_amp_switch_get,
+		.put = alc262_fujitsu_master_sw_put,
+		.private_value = HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT),
+	},
+	HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
+	HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+	{0} /* end */
+};
+
+/* additional init verbs for Benq laptops */
+static struct hda_verb alc262_EAPD_verbs[] = {
+	{0x20, AC_VERB_SET_COEF_INDEX, 0x07},
+	{0x20, AC_VERB_SET_PROC_COEF,  0x3070},
+	{0}
+};
+
+/* add playback controls from the parsed DAC table */
+static int alc262_auto_create_multi_out_ctls(struct alc_spec *spec, const struct auto_pin_cfg *cfg)
+{
+	hda_nid_t nid;
+	int err;
+
+	spec->multiout.num_dacs = 1;	/* only use one dac */
+	spec->multiout.dac_nids = spec->private_dac_nids;
+	spec->multiout.dac_nids[0] = 2;
+
+	nid = cfg->line_out_pins[0];
+	if (nid) {
+		if ((err = add_control(spec, ALC_CTL_WIDGET_VOL, "Front Playback Volume",
+				       HDA_COMPOSE_AMP_VAL(0x0c, 3, 0, HDA_OUTPUT))) < 0)
+			return err;
+		if ((err = add_control(spec, ALC_CTL_WIDGET_MUTE, "Front Playback Switch",
+				       HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT))) < 0)
+			return err;
+	}
+
+	nid = cfg->speaker_pins[0];
+	if (nid) {
+		if (nid == 0x16) {
+			if ((err = add_control(spec, ALC_CTL_WIDGET_VOL, "Speaker Playback Volume",
+					       HDA_COMPOSE_AMP_VAL(0x0e, 2, 0, HDA_OUTPUT))) < 0)
+				return err;
+			if ((err = add_control(spec, ALC_CTL_WIDGET_MUTE, "Speaker Playback Switch",
+					       HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT))) < 0)
+				return err;
+		} else {
+			if ((err = add_control(spec, ALC_CTL_WIDGET_MUTE, "Speaker Playback Switch",
+					       HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT))) < 0)
+				return err;
+		}
+	}
+	nid = cfg->hp_pins[0];
+	if (nid) {
+		/* spec->multiout.hp_nid = 2; */
+		if (nid == 0x16) {
+			if ((err = add_control(spec, ALC_CTL_WIDGET_VOL, "Headphone Playback Volume",
+					       HDA_COMPOSE_AMP_VAL(0x0e, 2, 0, HDA_OUTPUT))) < 0)
+				return err;
+			if ((err = add_control(spec, ALC_CTL_WIDGET_MUTE, "Headphone Playback Switch",
+					       HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT))) < 0)
+				return err;
+		} else {
+			if ((err = add_control(spec, ALC_CTL_WIDGET_MUTE, "Headphone Playback Switch",
+					       HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT))) < 0)
+				return err;
+		}
+	}
+	return 0;	
+}
+
+/* identical with ALC880 */
+#define alc262_auto_create_analog_input_ctls alc880_auto_create_analog_input_ctls
+
+/*
+ * generic initialization of ADC, input mixers and output mixers
+ */
+static struct hda_verb alc262_volume_init_verbs[] = {
+	/*
+	 * Unmute ADC0-2 and set the default input to mic-in
+	 */
+	{0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
+	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
+	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
+	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+
+	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
+	 * mixer widget
+	 * Note: PASD motherboards uses the Line In 2 as the input for front panel
+	 * mic (mic 2)
+	 */
+	/* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
+
+	/*
+	 * Set up output mixers (0x0c - 0x0f)
+	 */
+	/* set vol=0 to output mixers */
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	
+	/* set up input amps for analog loopback */
+	/* Amp Indices: DAC = 0, mixer = 1 */
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+
+	/* FIXME: use matrix-type input source selection */
+	/* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
+	/* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
+	/* Input mixer2 */
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
+	/* Input mixer3 */
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
+
+	{0}
+};
+
+static struct hda_verb alc262_HP_BPC_init_verbs[] = {
+	/*
+	 * Unmute ADC0-2 and set the default input to mic-in
+	 */
+	{0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
+	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
+	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
+	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+
+	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
+	 * mixer widget
+	 * Note: PASD motherboards uses the Line In 2 as the input for front panel
+	 * mic (mic 2)
+	 */
+	/* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(5)},
+        {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(6)},
+	
+	/*
+	 * Set up output mixers (0x0c - 0x0e)
+	 */
+	/* set vol=0 to output mixers */
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+
+	/* set up input amps for analog loopback */
+	/* Amp Indices: DAC = 0, mixer = 1 */
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+
+	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0},
+	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+
+	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+
+	{0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
+	{0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
+
+	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
+	{0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
+        {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
+	{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
+	{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
+
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7023 },
+	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 },
+        {0x19, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 },
+	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0x7023 },
+	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 },
+	{0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 },
+
+
+	/* FIXME: use matrix-type input source selection */
+	/* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
+	/* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))},
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))},
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))},
+	/* Input mixer2 */
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))},
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))},
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))},
+	/* Input mixer3 */
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))},
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))},
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))},
+
+	{0}
+};
+
+/* pcm configuration: identiacal with ALC880 */
+#define alc262_pcm_analog_playback	alc880_pcm_analog_playback
+#define alc262_pcm_analog_capture	alc880_pcm_analog_capture
+#define alc262_pcm_digital_playback	alc880_pcm_digital_playback
+#define alc262_pcm_digital_capture	alc880_pcm_digital_capture
+
+/*
+ * BIOS auto configuration
+ */
+static int alc262_parse_auto_config(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+	int err;
+	static hda_nid_t alc262_ignore[] = { 0x1d, 0 };
+
+	if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
+						alc262_ignore)) < 0)
+		return err;
+	if (! spec->autocfg.line_outs)
+		return 0; /* can't find valid BIOS pin config */
+	if ((err = alc262_auto_create_multi_out_ctls(spec, &spec->autocfg)) < 0 ||
+	    (err = alc262_auto_create_analog_input_ctls(spec, &spec->autocfg)) < 0)
+		return err;
+
+	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
+
+	if (spec->autocfg.dig_out_pin)
+		spec->multiout.dig_out_nid = ALC262_DIGOUT_NID;
+	if (spec->autocfg.dig_in_pin)
+		spec->dig_in_nid = ALC262_DIGIN_NID;
+
+	if (spec->kctl_alloc)
+		spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+
+	spec->init_verbs[spec->num_init_verbs++] = alc262_volume_init_verbs;
+	spec->num_mux_defs = 1;
+	spec->input_mux = &spec->private_imux;
+
+	return 1;
+}
+
+#define alc262_auto_init_multi_out	alc882_auto_init_multi_out
+#define alc262_auto_init_hp_out		alc882_auto_init_hp_out
+#define alc262_auto_init_analog_input	alc882_auto_init_analog_input
+
+
+/* init callback for auto-configuration model -- overriding the default init */
+static void alc262_auto_init(struct hda_codec *codec)
+{
+	alc262_auto_init_multi_out(codec);
+	alc262_auto_init_hp_out(codec);
+	alc262_auto_init_analog_input(codec);
+}
+
+/*
+ * configuration and preset
+ */
+static const char *alc262_models[ALC262_MODEL_LAST] = {
+	[ALC262_BASIC]		= "basic",
+	[ALC262_HIPPO]		= "hippo",
+	[ALC262_HIPPO_1]	= "hippo_1",
+	[ALC262_FUJITSU]	= "fujitsu",
+	[ALC262_HP_BPC]		= "hp-bpc",
+	[ALC262_BENQ_ED8]	= "benq",
+	[ALC262_AUTO]		= "auto",
+};
+
+static struct snd_pci_quirk alc262_cfg_tbl[] = {
+	SND_PCI_QUIRK(0x1002, 0x437b, "Hippo", ALC262_HIPPO),
+	SND_PCI_QUIRK(0x103c, 0x12fe, "HP xw9400", ALC262_HP_BPC),
+	SND_PCI_QUIRK(0x103c, 0x280c, "HP xw4400", ALC262_HP_BPC),
+	SND_PCI_QUIRK(0x103c, 0x2801, "HP q954", ALC262_HP_BPC),
+	SND_PCI_QUIRK(0x103c, 0x3014, "HP xw6400", ALC262_HP_BPC),
+	SND_PCI_QUIRK(0x103c, 0x3015, "HP xw8400", ALC262_HP_BPC),
+	SND_PCI_QUIRK(0x104d, 0x8203, "Sony UX-90", ALC262_HIPPO),
+	SND_PCI_QUIRK(0x10cf, 0x1397, "Fujitsu", ALC262_FUJITSU),
+	SND_PCI_QUIRK(0x17ff, 0x058f, "Benq Hippo", ALC262_HIPPO_1),
+	SND_PCI_QUIRK(0x17ff, 0x0560, "Benq ED8", ALC262_BENQ_ED8),
+	{0}
+};
+
+static struct alc_config_preset alc262_presets[] = {
+	[ALC262_BASIC] = {
+		.mixers = { alc262_base_mixer },
+		.init_verbs = { alc262_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc262_dac_nids),
+		.dac_nids = alc262_dac_nids,
+		.hp_nid = 0x03,
+		.num_channel_mode = ARRAY_SIZE(alc262_modes),
+		.channel_mode = alc262_modes,
+		.input_mux = &alc262_capture_source,
+	},
+	[ALC262_HIPPO] = {
+		.mixers = { alc262_base_mixer },
+		.init_verbs = { alc262_init_verbs, alc262_hippo_unsol_verbs},
+		.num_dacs = ARRAY_SIZE(alc262_dac_nids),
+		.dac_nids = alc262_dac_nids,
+		.hp_nid = 0x03,
+		.dig_out_nid = ALC262_DIGOUT_NID,
+		.num_channel_mode = ARRAY_SIZE(alc262_modes),
+		.channel_mode = alc262_modes,
+		.input_mux = &alc262_capture_source,
+		.unsol_event = alc262_hippo_unsol_event,
+	},
+	[ALC262_HIPPO_1] = {
+		.mixers = { alc262_hippo1_mixer },
+		.init_verbs = { alc262_init_verbs, alc262_hippo1_unsol_verbs},
+		.num_dacs = ARRAY_SIZE(alc262_dac_nids),
+		.dac_nids = alc262_dac_nids,
+		.hp_nid = 0x02,
+		.dig_out_nid = ALC262_DIGOUT_NID,
+		.num_channel_mode = ARRAY_SIZE(alc262_modes),
+		.channel_mode = alc262_modes,
+		.input_mux = &alc262_capture_source,
+		.unsol_event = alc262_hippo1_unsol_event,
+	},
+	[ALC262_FUJITSU] = {
+		.mixers = { alc262_fujitsu_mixer },
+		.init_verbs = { alc262_init_verbs, alc262_fujitsu_unsol_verbs },
+		.num_dacs = ARRAY_SIZE(alc262_dac_nids),
+		.dac_nids = alc262_dac_nids,
+		.hp_nid = 0x03,
+		.dig_out_nid = ALC262_DIGOUT_NID,
+		.num_channel_mode = ARRAY_SIZE(alc262_modes),
+		.channel_mode = alc262_modes,
+		.input_mux = &alc262_fujitsu_capture_source,
+		.unsol_event = alc262_fujitsu_unsol_event,
+	},
+	[ALC262_HP_BPC] = {
+		.mixers = { alc262_HP_BPC_mixer },
+		.init_verbs = { alc262_HP_BPC_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc262_dac_nids),
+		.dac_nids = alc262_dac_nids,
+		.hp_nid = 0x03,
+		.num_channel_mode = ARRAY_SIZE(alc262_modes),
+		.channel_mode = alc262_modes,
+		.input_mux = &alc262_HP_capture_source,
+	},	
+	[ALC262_BENQ_ED8] = {
+		.mixers = { alc262_base_mixer },
+		.init_verbs = { alc262_init_verbs, alc262_EAPD_verbs },
+		.num_dacs = ARRAY_SIZE(alc262_dac_nids),
+		.dac_nids = alc262_dac_nids,
+		.hp_nid = 0x03,
+		.num_channel_mode = ARRAY_SIZE(alc262_modes),
+		.channel_mode = alc262_modes,
+		.input_mux = &alc262_capture_source,
+	},		
+};
+
+static int patch_alc262(struct hda_codec *codec)
+{
+	struct alc_spec *spec;
+	int board_config;
+	int err;
+
+	spec = kcalloc(1, sizeof(*spec), GFP_KERNEL);
+	if (spec == NULL)
+		return -ENOMEM;
+
+	codec->spec = spec;
+#if 0
+	/* pshou 07/11/05  set a zero PCM sample to DAC when FIFO is under-run */
+	{
+	int tmp;
+	snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_COEF_INDEX, 7);
+	tmp = snd_hda_codec_read(codec, 0x20, 0, AC_VERB_GET_PROC_COEF, 0);
+	snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_COEF_INDEX, 7);
+	snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PROC_COEF, tmp | 0x80);
+	}
+#endif
+
+	board_config = snd_hda_check_board_config(codec, ALC262_MODEL_LAST,
+						  alc262_models,
+						  alc262_cfg_tbl);
+	
+	if (board_config < 0) {
+		printk(KERN_INFO "hda_codec: Unknown model for ALC262, "
+		       "trying auto-probe from BIOS...\n");
+		board_config = ALC262_AUTO;
+	}
+
+	if (board_config == ALC262_AUTO) {
+		/* automatic parse from the BIOS config */
+		err = alc262_parse_auto_config(codec);
+		if (err < 0) {
+			alc_free(codec);
+			return err;
+		} else if (! err) {
+			printk(KERN_INFO
+			       "hda_codec: Cannot set up configuration "
+			       "from BIOS.  Using base mode...\n");
+			board_config = ALC262_BASIC;
+		}
+	}
+
+	if (board_config != ALC262_AUTO)
+		setup_preset(spec, &alc262_presets[board_config]);
+
+	spec->stream_name_analog = "ALC262 Analog";
+	spec->stream_analog_playback = &alc262_pcm_analog_playback;
+	spec->stream_analog_capture = &alc262_pcm_analog_capture;
+		
+	spec->stream_name_digital = "ALC262 Digital";
+	spec->stream_digital_playback = &alc262_pcm_digital_playback;
+	spec->stream_digital_capture = &alc262_pcm_digital_capture;
+
+	if (! spec->adc_nids && spec->input_mux) {
+		/* check whether NID 0x07 is valid */
+		unsigned int wcap = get_wcaps(codec, 0x07);
+
+		wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; /* get type */
+		if (wcap != AC_WID_AUD_IN) {
+			spec->adc_nids = alc262_adc_nids_alt;
+			spec->num_adc_nids = ARRAY_SIZE(alc262_adc_nids_alt);
+			spec->mixers[spec->num_mixers] = alc262_capture_alt_mixer;
+			spec->num_mixers++;
+		} else {
+			spec->adc_nids = alc262_adc_nids;
+			spec->num_adc_nids = ARRAY_SIZE(alc262_adc_nids);
+			spec->mixers[spec->num_mixers] = alc262_capture_mixer;
+			spec->num_mixers++;
+		}
+	}
+
+	codec->patch_ops = alc_patch_ops;
+	if (board_config == ALC262_AUTO)
+		spec->init_hook = alc262_auto_init;
+		
+	return 0;
+}
+
+/*
+ *  ALC861 channel source setting (2/6 channel selection for 3-stack)
+ */
+
+/*
+ * set the path ways for 2 channel output
+ * need to set the codec line out and mic 1 pin widgets to inputs
+ */
+static struct hda_verb alc861_threestack_ch2_init[] = {
+	/* set pin widget 1Ah (line in) for input */
+	{ 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
+	/* set pin widget 18h (mic1/2) for input, for mic also enable the vref */
+	{ 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
+
+	{ 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c },
+#if 0
+	{ 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8)) }, /*mic*/
+	{ 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8)) }, /*line-in*/
+#endif
+	{0} /* end */
+};
+/*
+ * 6ch mode
+ * need to set the codec line out and mic 1 pin widgets to outputs
+ */
+static struct hda_verb alc861_threestack_ch6_init[] = {
+	/* set pin widget 1Ah (line in) for output (Back Surround)*/
+	{ 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
+	/* set pin widget 18h (mic1) for output (CLFE)*/
+	{ 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
+
+	{ 0x0c, AC_VERB_SET_CONNECT_SEL, 0x00 },
+	{ 0x0d, AC_VERB_SET_CONNECT_SEL, 0x00 },
+
+	{ 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 },
+#if 0
+	{ 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8)) }, /*mic*/
+	{ 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8)) }, /*line in*/
+#endif
+	{0} /* end */
+};
+
+static struct hda_channel_mode alc861_threestack_modes[2] = {
+	{ 2, alc861_threestack_ch2_init },
+	{ 6, alc861_threestack_ch6_init },
+};
+/* Set mic1 as input and unmute the mixer */
+static struct hda_verb alc861_uniwill_m31_ch2_init[] = {
+	{ 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
+	{ 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8)) }, /*mic*/
+	{0} /* end */
+};
+/* Set mic1 as output and mute mixer */
+static struct hda_verb alc861_uniwill_m31_ch4_init[] = {
+	{ 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
+	{ 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8)) }, /*mic*/
+	{0} /* end */
+};
+
+static struct hda_channel_mode alc861_uniwill_m31_modes[2] = {
+	{ 2, alc861_uniwill_m31_ch2_init },
+	{ 4, alc861_uniwill_m31_ch4_init },
+};
+
+/* Set mic1 and line-in as input and unmute the mixer */
+static struct hda_verb alc861_asus_ch2_init[] = {
+	/* set pin widget 1Ah (line in) for input */
+	{ 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
+	/* set pin widget 18h (mic1/2) for input, for mic also enable the vref */
+	{ 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
+
+	{ 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c },
+#if 0
+	{ 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8)) }, /*mic*/
+	{ 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8)) }, /*line-in*/
+#endif
+	{0} /* end */
+};
+/* Set mic1 nad line-in as output and mute mixer */
+static struct hda_verb alc861_asus_ch6_init[] = {
+	/* set pin widget 1Ah (line in) for output (Back Surround)*/
+	{ 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
+	/* { 0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, */
+	/* set pin widget 18h (mic1) for output (CLFE)*/
+	{ 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
+	/* { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, */
+	{ 0x0c, AC_VERB_SET_CONNECT_SEL, 0x00 },
+	{ 0x0d, AC_VERB_SET_CONNECT_SEL, 0x00 },
+
+	{ 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 },
+#if 0
+	{ 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8)) }, /*mic*/
+	{ 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8)) }, /*line in*/
+#endif
+	{0} /* end */
+};
+
+static struct hda_channel_mode alc861_asus_modes[2] = {
+	{ 2, alc861_asus_ch2_init },
+	{ 6, alc861_asus_ch6_init },
+};
+
+/* patch-ALC861 */
+
+static struct snd_kcontrol_new alc861_base_mixer[] = {
+        /* output mixer control */
+	HDA_CODEC_MUTE("Front Playback Switch", 0x03, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Surround Playback Switch", 0x06, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x05, 1, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x05, 2, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Side Playback Switch", 0x04, 0x0, HDA_OUTPUT),
+
+        /*Input mixer control */
+	/* HDA_CODEC_VOLUME("Input Playback Volume", 0x15, 0x0, HDA_OUTPUT),
+	   HDA_CODEC_MUTE("Input Playback Switch", 0x15, 0x0, HDA_OUTPUT), */
+	HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Line Playback Volume", 0x15, 0x02, HDA_INPUT),
+	HDA_CODEC_MUTE("Line Playback Switch", 0x15, 0x02, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x15, 0x01, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x15, 0x01, HDA_INPUT),
+	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, HDA_INPUT),
+ 
+        /* Capture mixer control */
+	HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Capture Source",
+		.count = 1,
+		.info = alc_mux_enum_info,
+		.get = alc_mux_enum_get,
+		.put = alc_mux_enum_put,
+	},
+	{0} /* end */
+};
+
+static struct snd_kcontrol_new alc861_3ST_mixer[] = {
+        /* output mixer control */
+	HDA_CODEC_MUTE("Front Playback Switch", 0x03, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Surround Playback Switch", 0x06, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x05, 1, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x05, 2, 0x0, HDA_OUTPUT),
+	/*HDA_CODEC_MUTE("Side Playback Switch", 0x04, 0x0, HDA_OUTPUT), */
+
+	/* Input mixer control */
+	/* HDA_CODEC_VOLUME("Input Playback Volume", 0x15, 0x0, HDA_OUTPUT),
+	   HDA_CODEC_MUTE("Input Playback Switch", 0x15, 0x0, HDA_OUTPUT), */
+	HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Line Playback Volume", 0x15, 0x02, HDA_INPUT),
+	HDA_CODEC_MUTE("Line Playback Switch", 0x15, 0x02, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x15, 0x01, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x15, 0x01, HDA_INPUT),
+	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, HDA_INPUT),
+ 
+	/* Capture mixer control */
+	HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Capture Source",
+		.count = 1,
+		.info = alc_mux_enum_info,
+		.get = alc_mux_enum_get,
+		.put = alc_mux_enum_put,
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Channel Mode",
+		.info = alc_ch_mode_info,
+		.get = alc_ch_mode_get,
+		.put = alc_ch_mode_put,
+                .private_value = ARRAY_SIZE(alc861_threestack_modes),
+	},
+	{0} /* end */
+};
+
+static struct snd_kcontrol_new alc861_toshiba_mixer[] = {
+        /* output mixer control */
+	HDA_CODEC_MUTE("Master Playback Switch", 0x03, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x15, 0x01, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x15, 0x01, HDA_INPUT),
+	
+        /*Capture mixer control */
+	HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Capture Source",
+		.count = 1,
+		.info = alc_mux_enum_info,
+		.get = alc_mux_enum_get,
+		.put = alc_mux_enum_put,
+	},
+
+	{0} /* end */
+};	
+
+static struct snd_kcontrol_new alc861_uniwill_m31_mixer[] = {
+        /* output mixer control */
+	HDA_CODEC_MUTE("Front Playback Switch", 0x03, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Surround Playback Switch", 0x06, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x05, 1, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x05, 2, 0x0, HDA_OUTPUT),
+	/*HDA_CODEC_MUTE("Side Playback Switch", 0x04, 0x0, HDA_OUTPUT), */
+
+	/* Input mixer control */
+	/* HDA_CODEC_VOLUME("Input Playback Volume", 0x15, 0x0, HDA_OUTPUT),
+	   HDA_CODEC_MUTE("Input Playback Switch", 0x15, 0x0, HDA_OUTPUT), */
+	HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Line Playback Volume", 0x15, 0x02, HDA_INPUT),
+	HDA_CODEC_MUTE("Line Playback Switch", 0x15, 0x02, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x15, 0x01, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x15, 0x01, HDA_INPUT),
+	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, HDA_INPUT),
+ 
+	/* Capture mixer control */
+	HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Capture Source",
+		.count = 1,
+		.info = alc_mux_enum_info,
+		.get = alc_mux_enum_get,
+		.put = alc_mux_enum_put,
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Channel Mode",
+		.info = alc_ch_mode_info,
+		.get = alc_ch_mode_get,
+		.put = alc_ch_mode_put,
+                .private_value = ARRAY_SIZE(alc861_uniwill_m31_modes),
+	},
+	{0} /* end */
+};			
+
+static struct snd_kcontrol_new alc861_asus_mixer[] = {
+        /* output mixer control */
+	HDA_CODEC_MUTE("Front Playback Switch", 0x03, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Surround Playback Switch", 0x06, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x05, 1, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x05, 2, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Side Playback Switch", 0x04, 0x0, HDA_OUTPUT),
+
+	/* Input mixer control */
+	HDA_CODEC_VOLUME("Input Playback Volume", 0x15, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Input Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Line Playback Volume", 0x15, 0x02, HDA_INPUT),
+	HDA_CODEC_MUTE("Line Playback Switch", 0x15, 0x02, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x15, 0x01, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x15, 0x01, HDA_INPUT),
+	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, HDA_OUTPUT), /* was HDA_INPUT (why?) */
+ 
+	/* Capture mixer control */
+	HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Capture Source",
+		.count = 1,
+		.info = alc_mux_enum_info,
+		.get = alc_mux_enum_get,
+		.put = alc_mux_enum_put,
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Channel Mode",
+		.info = alc_ch_mode_info,
+		.get = alc_ch_mode_get,
+		.put = alc_ch_mode_put,
+                .private_value = ARRAY_SIZE(alc861_asus_modes),
+	},
+	{0}
+};
+
+/* additional mixer */
+static struct snd_kcontrol_new alc861_asus_laptop_mixer[] = {
+	HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("PC Beep Playback Volume", 0x23, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("PC Beep Playback Switch", 0x23, 0x0, HDA_OUTPUT),
+	{0}
+};
+
+/*
+ * generic initialization of ADC, input mixers and output mixers
+ */
+static struct hda_verb alc861_base_init_verbs[] = {
+	/*
+	 * Unmute ADC0 and set the default input to mic-in
+	 */
+	/* port-A for surround (rear panel) */
+	{ 0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
+	{ 0x0e, AC_VERB_SET_CONNECT_SEL, 0x00 },
+	/* port-B for mic-in (rear panel) with vref */
+	{ 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
+	/* port-C for line-in (rear panel) */
+	{ 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
+	/* port-D for Front */
+	{ 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
+	{ 0x0b, AC_VERB_SET_CONNECT_SEL, 0x00 },
+	/* port-E for HP out (front panel) */
+	{ 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
+	/* route front PCM to HP */
+	{ 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 },
+	/* port-F for mic-in (front panel) with vref */
+	{ 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
+	/* port-G for CLFE (rear panel) */
+	{ 0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
+	{ 0x1f, AC_VERB_SET_CONNECT_SEL, 0x00 },
+	/* port-H for side (rear panel) */
+	{ 0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
+	{ 0x20, AC_VERB_SET_CONNECT_SEL, 0x00 },
+	/* CD-in */
+	{ 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
+	/* route front mic to ADC1*/
+	{0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
+	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	
+	/* Unmute DAC0~3 & spdif out*/
+	{0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	
+	/* Unmute Mixer 14 (mic) 1c (Line in)*/
+	{0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+        {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+        {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	
+	/* Unmute Stereo Mixer 15 */
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c          }, //Output 0~12 step
+
+	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, // hp used DAC 3 (Front)
+        {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+
+	{0}
+};
+
+static struct hda_verb alc861_threestack_init_verbs[] = {
+	/*
+	 * Unmute ADC0 and set the default input to mic-in
+	 */
+	/* port-A for surround (rear panel) */
+	{ 0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 },
+	/* port-B for mic-in (rear panel) with vref */
+	{ 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
+	/* port-C for line-in (rear panel) */
+	{ 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
+	/* port-D for Front */
+	{ 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
+	{ 0x0b, AC_VERB_SET_CONNECT_SEL, 0x00 },
+	/* port-E for HP out (front panel) */
+	{ 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
+	/* route front PCM to HP */
+	{ 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 },
+	/* port-F for mic-in (front panel) with vref */
+	{ 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
+	/* port-G for CLFE (rear panel) */
+	{ 0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 },
+	/* port-H for side (rear panel) */
+	{ 0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 },
+	/* CD-in */
+	{ 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
+	/* route front mic to ADC1*/
+	{0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
+	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	/* Unmute DAC0~3 & spdif out*/
+	{0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	
+	/* Unmute Mixer 14 (mic) 1c (Line in)*/
+	{0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+        {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+        {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	
+	/* Unmute Stereo Mixer 15 */
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c          }, //Output 0~12 step
+
+	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, // hp used DAC 3 (Front)
+        {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+	{0}
+};
+
+static struct hda_verb alc861_uniwill_m31_init_verbs[] = {
+	/*
+	 * Unmute ADC0 and set the default input to mic-in
+	 */
+	/* port-A for surround (rear panel) */
+	{ 0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 },
+	/* port-B for mic-in (rear panel) with vref */
+	{ 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
+	/* port-C for line-in (rear panel) */
+	{ 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
+	/* port-D for Front */
+	{ 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
+	{ 0x0b, AC_VERB_SET_CONNECT_SEL, 0x00 },
+	/* port-E for HP out (front panel) */
+	{ 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, // this has to be set to VREF80
+	/* route front PCM to HP */
+	{ 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 },
+	/* port-F for mic-in (front panel) with vref */
+	{ 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
+	/* port-G for CLFE (rear panel) */
+	{ 0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 },
+	/* port-H for side (rear panel) */
+	{ 0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 },
+	/* CD-in */
+	{ 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
+	/* route front mic to ADC1*/
+	{0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
+	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	/* Unmute DAC0~3 & spdif out*/
+	{0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	
+	/* Unmute Mixer 14 (mic) 1c (Line in)*/
+	{0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+        {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+        {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	
+	/* Unmute Stereo Mixer 15 */
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c          }, //Output 0~12 step
+
+	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, // hp used DAC 3 (Front)
+        {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+	{0}
+};
+
+static struct hda_verb alc861_asus_init_verbs[] = {
+	/*
+	 * Unmute ADC0 and set the default input to mic-in
+	 */
+	/* port-A for surround (rear panel) | according to codec#0 this is the HP jack*/
+	{ 0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 }, /* was 0x00 */
+	/* route front PCM to HP */
+	{ 0x0e, AC_VERB_SET_CONNECT_SEL, 0x01 },
+	/* port-B for mic-in (rear panel) with vref */
+	{ 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
+	/* port-C for line-in (rear panel) */
+	{ 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
+	/* port-D for Front */
+	{ 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
+	{ 0x0b, AC_VERB_SET_CONNECT_SEL, 0x00 },
+	/* port-E for HP out (front panel) */
+	{ 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, /* this has to be set to VREF80 */
+	/* route front PCM to HP */
+	{ 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 },
+	/* port-F for mic-in (front panel) with vref */
+	{ 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
+	/* port-G for CLFE (rear panel) */
+	{ 0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
+	/* port-H for side (rear panel) */
+	{ 0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
+	/* CD-in */
+	{ 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
+	/* route front mic to ADC1*/
+	{0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
+	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	/* Unmute DAC0~3 & spdif out*/
+	{0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	/* Unmute Mixer 14 (mic) 1c (Line in)*/
+	{0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+        {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+        {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	
+	/* Unmute Stereo Mixer 15 */
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c          }, /* Output 0~12 step */
+
+	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, /* hp used DAC 3 (Front) */
+	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+	{0}
+};
+
+/* additional init verbs for ASUS laptops */
+static struct hda_verb alc861_asus_laptop_init_verbs[] = {
+	{ 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x45 }, /* HP-out */
+	{ 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2) }, /* mute line-in */
+	{0}
+};
+
+/*
+ * generic initialization of ADC, input mixers and output mixers
+ */
+static struct hda_verb alc861_auto_init_verbs[] = {
+	/*
+	 * Unmute ADC0 and set the default input to mic-in
+	 */
+//	{0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
+	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	
+	/* Unmute DAC0~3 & spdif out*/
+	{0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	
+	/* Unmute Mixer 14 (mic) 1c (Line in)*/
+	{0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	
+	/* Unmute Stereo Mixer 15 */
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c},
+
+	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+
+	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},	
+	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},		
+	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},	
+	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},	
+
+	{0x08, AC_VERB_SET_CONNECT_SEL, 0x00},	// set Mic 1
+
+	{0}
+};
+
+static struct hda_verb alc861_toshiba_init_verbs[] = {
+	{0x0f, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+        
+	{0}
+};
+
+/* toggle speaker-output according to the hp-jack state */
+static void alc861_toshiba_automute(struct hda_codec *codec)
+{
+	unsigned int present;
+
+	present = snd_hda_codec_read(codec, 0x0f, 0,
+				     AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+	snd_hda_codec_amp_update(codec, 0x16, 0, HDA_INPUT, 0,
+				 0x80, present ? 0x80 : 0);
+	snd_hda_codec_amp_update(codec, 0x16, 1, HDA_INPUT, 0,
+				 0x80, present ? 0x80 : 0);
+	snd_hda_codec_amp_update(codec, 0x1a, 0, HDA_INPUT, 3,
+				 0x80, present ? 0 : 0x80);
+	snd_hda_codec_amp_update(codec, 0x1a, 1, HDA_INPUT, 3,
+				 0x80, present ? 0 : 0x80);
+}
+
+static void alc861_toshiba_unsol_event(struct hda_codec *codec,
+				       unsigned int res)
+{
+	/* Looks like the unsol event is incompatible with the standard
+	 * definition.  6bit tag is placed at 26 bit!
+	 */
+	if ((res >> 26) == ALC880_HP_EVENT)
+		alc861_toshiba_automute(codec);
+}
+
+/* pcm configuration: identiacal with ALC880 */
+#define alc861_pcm_analog_playback	alc880_pcm_analog_playback
+#define alc861_pcm_analog_capture	alc880_pcm_analog_capture
+#define alc861_pcm_digital_playback	alc880_pcm_digital_playback
+#define alc861_pcm_digital_capture	alc880_pcm_digital_capture
+
+
+#define ALC861_DIGOUT_NID	0x07
+
+static struct hda_channel_mode alc861_8ch_modes[1] = {
+	{ 8, NULL }
+};
+
+static hda_nid_t alc861_dac_nids[4] = {
+	/* front, surround, clfe, side */
+	0x03, 0x06, 0x05, 0x04
+};
+
+static hda_nid_t alc660_dac_nids[3] = {
+	/* front, clfe, surround */
+	0x03, 0x05, 0x06
+};
+
+static hda_nid_t alc861_adc_nids[1] = {
+	/* ADC0-2 */
+	0x08,
+};
+
+static struct hda_input_mux alc861_capture_source = {
+	.num_items = 5,
+	.items = {
+		{ "Mic", 0x0 },
+		{ "Front Mic", 0x3 },
+		{ "Line", 0x1 },
+		{ "CD", 0x4 },
+		{ "Mixer", 0x5 },
+	},
+};
+
+/* fill in the dac_nids table from the parsed pin configuration */
+static int alc861_auto_fill_dac_nids(struct alc_spec *spec, const struct auto_pin_cfg *cfg)
+{
+	int i;
+	hda_nid_t nid;
+
+	spec->multiout.dac_nids = spec->private_dac_nids;
+	for (i = 0; i < cfg->line_outs; i++) {
+		nid = cfg->line_out_pins[i];
+		if (nid) {
+			if (i >= ARRAY_SIZE(alc861_dac_nids))
+				continue;
+			spec->multiout.dac_nids[i] = alc861_dac_nids[i];
+		}
+	}
+	spec->multiout.num_dacs = cfg->line_outs;
+	return 0;
+}
+
+/* add playback controls from the parsed DAC table */
+static int alc861_auto_create_multi_out_ctls(struct alc_spec *spec,
+					     const struct auto_pin_cfg *cfg)
+{
+	char name[32];
+	static const char *chname[4] = { "Front", "Surround", NULL /*CLFE*/, "Side" };
+	hda_nid_t nid;
+	int i, idx, err;
+
+	for (i = 0; i < cfg->line_outs; i++) {
+		nid = spec->multiout.dac_nids[i];
+		if (! nid)
+			continue;
+		if (nid == 0x05) {
+			/* Center/LFE */
+			if ((err = add_control(spec, ALC_CTL_BIND_MUTE, "Center Playback Switch",
+					       HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT))) < 0)
+				return err;
+			if ((err = add_control(spec, ALC_CTL_BIND_MUTE, "LFE Playback Switch",
+					       HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT))) < 0)
+				return err;
+		} else {
+			for (idx = 0; idx < ARRAY_SIZE(alc861_dac_nids) - 1; idx++)
+				if (nid == alc861_dac_nids[idx])
+					break;
+			sprintf(name, "%s Playback Switch", chname[idx]);
+			if ((err = add_control(spec, ALC_CTL_BIND_MUTE, name,
+					       HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT))) < 0)
+				return err;
+		}
+	}
+	return 0;
+}
+
+static int alc861_auto_create_hp_ctls(struct alc_spec *spec, hda_nid_t pin)
+{
+	int err;
+	hda_nid_t nid;
+
+	if (! pin)
+		return 0;
+
+	if ((pin >= 0x0b && pin <= 0x10) || pin == 0x1f || pin == 0x20) {
+		nid = 0x03;
+		if ((err = add_control(spec, ALC_CTL_WIDGET_MUTE, "Headphone Playback Switch",
+				       HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT))) < 0)
+			return err;
+		spec->multiout.hp_nid = nid;
+	}
+	return 0;
+}
+
+/* create playback/capture controls for input pins */
+static int alc861_auto_create_analog_input_ctls(struct alc_spec *spec, const struct auto_pin_cfg *cfg)
+{
+	struct hda_input_mux *imux = &spec->private_imux;
+	int i, err, idx, idx1;
+
+	for (i = 0; i < AUTO_PIN_LAST; i++) {
+		switch(cfg->input_pins[i]) {
+		case 0x0c:
+			idx1 = 1;
+			idx = 2;	// Line In
+			break;
+		case 0x0f:
+			idx1 = 2;
+			idx = 2;	// Line In
+			break;
+		case 0x0d:
+			idx1 = 0;
+			idx = 1;	// Mic In 
+			break;
+		case 0x10:	
+			idx1 = 3;
+			idx = 1;	// Mic In 
+			break;
+		case 0x11:
+			idx1 = 4;
+			idx = 0;	// CD
+			break;
+		default:
+			continue;
+		}
+
+		err = new_analog_input(spec, cfg->input_pins[i],
+				       auto_pin_cfg_labels[i], idx, 0x15);
+		if (err < 0)
+			return err;
+
+		imux->items[imux->num_items].label = auto_pin_cfg_labels[i];
+		imux->items[imux->num_items].index = idx1;
+		imux->num_items++;	
+	}
+	return 0;
+}
+
+static struct snd_kcontrol_new alc861_capture_mixer[] = {
+	HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
+
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		/* The multiple "Capture Source" controls confuse alsamixer
+		 * So call somewhat different..
+		 *FIXME: the controls appear in the "playback" view!
+		 */
+		/* .name = "Capture Source", */
+		.name = "Input Source",
+		.count = 1,
+		.info = alc_mux_enum_info,
+		.get = alc_mux_enum_get,
+		.put = alc_mux_enum_put,
+	},
+	{0} /* end */
+};
+
+static void alc861_auto_set_output_and_unmute(struct hda_codec *codec, hda_nid_t nid,
+					      int pin_type, int dac_idx)
+{
+	/* set as output */
+
+	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);
+	snd_hda_codec_write(codec, dac_idx, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
+
+}
+
+static void alc861_auto_init_multi_out(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+	int i;
+
+	for (i = 0; i < spec->autocfg.line_outs; i++) {
+		hda_nid_t nid = spec->autocfg.line_out_pins[i];
+		if (nid)
+			alc861_auto_set_output_and_unmute(codec, nid, PIN_OUT, spec->multiout.dac_nids[i]);
+	}
+}
+
+static void alc861_auto_init_hp_out(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+	hda_nid_t pin;
+
+	pin = spec->autocfg.hp_pins[0];
+	if (pin) /* connect to front */
+		alc861_auto_set_output_and_unmute(codec, pin, PIN_HP, spec->multiout.dac_nids[0]);
+}
+
+static void alc861_auto_init_analog_input(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+	int i;
+
+	for (i = 0; i < AUTO_PIN_LAST; i++) {
+		hda_nid_t nid = spec->autocfg.input_pins[i];
+		if ((nid>=0x0c) && (nid <=0x11)) {
+			snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+					    i <= AUTO_PIN_FRONT_MIC ? PIN_VREF80 : PIN_IN);
+		}
+	}
+}
+
+/* parse the BIOS configuration and set up the alc_spec */
+/* return 1 if successful, 0 if the proper config is not found, or a negative error code */
+static int alc861_parse_auto_config(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+	int err;
+	static hda_nid_t alc861_ignore[] = { 0x1d, 0 };
+
+	if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
+						alc861_ignore)) < 0)
+		return err;
+	if (! spec->autocfg.line_outs)
+		return 0; /* can't find valid BIOS pin config */
+
+	if ((err = alc861_auto_fill_dac_nids(spec, &spec->autocfg)) < 0 ||
+	    (err = alc861_auto_create_multi_out_ctls(spec, &spec->autocfg)) < 0 ||
+	    (err = alc861_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0])) < 0 ||
+	    (err = alc861_auto_create_analog_input_ctls(spec, &spec->autocfg)) < 0)
+		return err;
+
+	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
+
+	if (spec->autocfg.dig_out_pin)
+		spec->multiout.dig_out_nid = ALC861_DIGOUT_NID;
+
+	if (spec->kctl_alloc)
+		spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+
+	spec->init_verbs[spec->num_init_verbs++] = alc861_auto_init_verbs;
+
+	spec->num_mux_defs = 1;
+	spec->input_mux = &spec->private_imux;
+
+	spec->adc_nids = alc861_adc_nids;
+	spec->num_adc_nids = ARRAY_SIZE(alc861_adc_nids);
+	spec->mixers[spec->num_mixers] = alc861_capture_mixer;
+	spec->num_mixers++;
+
+	return 1;
+}
+
+/* additional initialization for auto-configuration model */
+static void alc861_auto_init(struct hda_codec *codec)
+{
+	alc861_auto_init_multi_out(codec);
+	alc861_auto_init_hp_out(codec);
+	alc861_auto_init_analog_input(codec);
+}
+
+
+/*
+ * configuration and preset
+ */
+static const char *alc861_models[ALC861_MODEL_LAST] = {
+	[ALC861_3ST]		= "3stack",
+	[ALC660_3ST]		= "3stack-660",
+	[ALC861_3ST_DIG]	= "3stack-dig",
+	[ALC861_6ST_DIG]	= "6stack-dig",
+	[ALC861_UNIWILL_M31]	= "uniwill-m31",
+	[ALC861_TOSHIBA]	= "toshiba",
+	[ALC861_ASUS]		= "asus",
+	[ALC861_ASUS_LAPTOP]	= "asus-laptop",
+	[ALC861_AUTO]		= "auto",
+};
+
+static struct snd_pci_quirk alc861_cfg_tbl[] = {
+    SND_PCI_QUIRK(0x1043, 0x1205, "ASUS W7J", ALC861_3ST),
+	SND_PCI_QUIRK(0x1043, 0x1335, "ASUS F2/3", ALC861_ASUS_LAPTOP),
+	SND_PCI_QUIRK(0x1043, 0x1338, "ASUS F2/3", ALC861_ASUS_LAPTOP),
+	SND_PCI_QUIRK(0x1043, 0x1393, "ASUS", ALC861_ASUS),
+        SND_PCI_QUIRK(0x1043, 0x81e7, "ASUS", ALC660_3ST),
+        SND_PCI_QUIRK(0x1179, 0xff00, "Toshiba", ALC861_TOSHIBA),
+	SND_PCI_QUIRK(0x1179, 0xff10, "Toshiba", ALC861_TOSHIBA),
+	SND_PCI_QUIRK(0x1584, 0x9072, "Uniwill m31", ALC861_UNIWILL_M31),
+	SND_PCI_QUIRK(0x8086, 0xd600, "Intel", ALC861_3ST),
+	{0}
+};
+
+static struct alc_config_preset alc861_presets[] = {
+	[ALC861_3ST] = {
+		.mixers = { alc861_3ST_mixer },
+		.init_verbs = { alc861_threestack_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc861_dac_nids),
+		.dac_nids = alc861_dac_nids,
+		.num_channel_mode = ARRAY_SIZE(alc861_threestack_modes),
+		.channel_mode = alc861_threestack_modes,
+		.need_dac_fix = 1,
+		.num_adc_nids = ARRAY_SIZE(alc861_adc_nids),
+		.adc_nids = alc861_adc_nids,
+		.input_mux = &alc861_capture_source,
+	},
+	[ALC861_3ST_DIG] = {
+		.mixers = { alc861_base_mixer },
+		.init_verbs = { alc861_threestack_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc861_dac_nids),
+		.dac_nids = alc861_dac_nids,
+		.dig_out_nid = ALC861_DIGOUT_NID,
+		.num_channel_mode = ARRAY_SIZE(alc861_threestack_modes),
+		.channel_mode = alc861_threestack_modes,
+		.need_dac_fix = 1,
+		.num_adc_nids = ARRAY_SIZE(alc861_adc_nids),
+		.adc_nids = alc861_adc_nids,
+		.input_mux = &alc861_capture_source,
+	},
+	[ALC861_6ST_DIG] = {
+		.mixers = { alc861_base_mixer },
+		.init_verbs = { alc861_base_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc861_dac_nids),
+		.dac_nids = alc861_dac_nids,
+		.dig_out_nid = ALC861_DIGOUT_NID,
+		.num_channel_mode = ARRAY_SIZE(alc861_8ch_modes),
+		.channel_mode = alc861_8ch_modes,
+		.num_adc_nids = ARRAY_SIZE(alc861_adc_nids),
+		.adc_nids = alc861_adc_nids,
+		.input_mux = &alc861_capture_source,
+	},
+	[ALC660_3ST] = {
+		.mixers = { alc861_3ST_mixer },
+		.init_verbs = { alc861_threestack_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc660_dac_nids),
+		.dac_nids = alc660_dac_nids,
+		.num_channel_mode = ARRAY_SIZE(alc861_threestack_modes),
+		.channel_mode = alc861_threestack_modes,
+		.need_dac_fix = 1,
+		.num_adc_nids = ARRAY_SIZE(alc861_adc_nids),
+		.adc_nids = alc861_adc_nids,
+		.input_mux = &alc861_capture_source,
+	},
+	[ALC861_UNIWILL_M31] = {
+		.mixers = { alc861_uniwill_m31_mixer },
+		.init_verbs = { alc861_uniwill_m31_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc861_dac_nids),
+		.dac_nids = alc861_dac_nids,
+		.dig_out_nid = ALC861_DIGOUT_NID,
+		.num_channel_mode = ARRAY_SIZE(alc861_uniwill_m31_modes),
+		.channel_mode = alc861_uniwill_m31_modes,
+		.need_dac_fix = 1,
+		.num_adc_nids = ARRAY_SIZE(alc861_adc_nids),
+		.adc_nids = alc861_adc_nids,
+		.input_mux = &alc861_capture_source,
+	},
+	[ALC861_TOSHIBA] = {
+		.mixers = { alc861_toshiba_mixer },
+		.init_verbs = { alc861_base_init_verbs, alc861_toshiba_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc861_dac_nids),
+		.dac_nids = alc861_dac_nids,
+		.num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
+		.channel_mode = alc883_3ST_2ch_modes,
+		.num_adc_nids = ARRAY_SIZE(alc861_adc_nids),
+		.adc_nids = alc861_adc_nids,
+		.input_mux = &alc861_capture_source,
+		.unsol_event = alc861_toshiba_unsol_event,
+		.init_hook = alc861_toshiba_automute,
+	},
+	[ALC861_ASUS] = {
+		.mixers = { alc861_asus_mixer },
+		.init_verbs = { alc861_asus_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc861_dac_nids),
+		.dac_nids = alc861_dac_nids,
+		.dig_out_nid = ALC861_DIGOUT_NID,
+		.num_channel_mode = ARRAY_SIZE(alc861_asus_modes),
+		.channel_mode = alc861_asus_modes,
+		.need_dac_fix = 1,
+		.hp_nid = 0x06,
+		.num_adc_nids = ARRAY_SIZE(alc861_adc_nids),
+		.adc_nids = alc861_adc_nids,
+		.input_mux = &alc861_capture_source,
+	},
+	[ALC861_ASUS_LAPTOP] = {
+		.mixers = { alc861_toshiba_mixer, alc861_asus_laptop_mixer },
+		.init_verbs = { alc861_asus_init_verbs,
+				alc861_asus_laptop_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc861_dac_nids),
+		.dac_nids = alc861_dac_nids,
+		.dig_out_nid = ALC861_DIGOUT_NID,
+		.num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
+		.channel_mode = alc883_3ST_2ch_modes,
+		.need_dac_fix = 1,
+		.num_adc_nids = ARRAY_SIZE(alc861_adc_nids),
+		.adc_nids = alc861_adc_nids,
+		.input_mux = &alc861_capture_source,
+	},
+};
+
+
+static int patch_alc861(struct hda_codec *codec)
+{
+	struct alc_spec *spec;
+	int board_config;
+	int err;
+
+	spec = kcalloc(1, sizeof(*spec), GFP_KERNEL);
+	if (spec == NULL)
+		return -ENOMEM;
+
+	codec->spec = spec;	
+
+        board_config = snd_hda_check_board_config(codec, ALC861_MODEL_LAST,
+						  alc861_models,
+						  alc861_cfg_tbl);
+
+	if (board_config < 0) {
+		printk(KERN_INFO "hda_codec: Unknown model for ALC861, "
+		       "trying auto-probe from BIOS...\n");
+		board_config = ALC861_AUTO;
+	}
+
+	if (board_config == ALC861_AUTO) {
+		/* automatic parse from the BIOS config */
+		err = alc861_parse_auto_config(codec);
+		if (err < 0) {
+			alc_free(codec);
+			return err;
+		} else if (! err) {
+			printk(KERN_INFO
+			       "hda_codec: Cannot set up configuration "
+			       "from BIOS.  Using base mode...\n");
+		   board_config = ALC861_3ST_DIG;
+		}
+	}
+
+	if (board_config != ALC861_AUTO)
+		setup_preset(spec, &alc861_presets[board_config]);
+
+	spec->stream_name_analog = "ALC861 Analog";
+	spec->stream_analog_playback = &alc861_pcm_analog_playback;
+	spec->stream_analog_capture = &alc861_pcm_analog_capture;
+
+	spec->stream_name_digital = "ALC861 Digital";
+	spec->stream_digital_playback = &alc861_pcm_digital_playback;
+	spec->stream_digital_capture = &alc861_pcm_digital_capture;
+
+	codec->patch_ops = alc_patch_ops;
+	if (board_config == ALC861_AUTO)
+		spec->init_hook = alc861_auto_init;
+		
+	return 0;
+}
+
+/*
+ * patch entries
+ */
+struct hda_codec_preset snd_hda_preset_realtek[] = {
+	{ .id = 0x10ec0260, .name = "ALC260", .patch = patch_alc260 },
+	{ .id = 0x10ec0262, .name = "ALC262", .patch = patch_alc262 },
+ 	{ .id = 0x10ec0880, .name = "ALC880", .patch = patch_alc880 },
+	{ .id = 0x10ec0882, .name = "ALC882", .patch = patch_alc882 },
+	{ .id = 0x10ec0883, .name = "ALC883", .patch = patch_alc883 },
+	{ .id = 0x10ec0885, .name = "ALC885", .patch = patch_alc882 },
+	{ .id = 0x10ec0888, .name = "ALC888", .patch = patch_alc883 },
+	{ .id = 0x10ec0861, .rev = 0x100300, .name = "ALC861",
+	  .patch = patch_alc861 },
+	{ .id = 0x10ec0861, .rev = 0x100340, .name = "ALC660",
+	  .patch = patch_alc861 },
+	{ .id = 0x10ec0660, .name = "ALC660", .patch = patch_alc861 },
+	{0} /* terminator */
+};
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/hda/patch_si3054.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/hda/patch_si3054.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/hda/patch_si3054.c	(revision 91)
@@ -0,0 +1,303 @@
+/*
+ * Universal Interface for Intel High Definition Audio Codec
+ *
+ * HD audio interface patch for Silicon Labs 3054/5 modem codec
+ *
+ * Copyright (c) 2005 Sasha Khapyorsky <sashak@alsa-project.org>
+ *                    Takashi Iwai <tiwai@suse.de>
+ *
+ *
+ *  This driver 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 driver is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <sound/core.h>
+#include "hda_codec.h"
+#include "hda_local.h"
+
+
+/* si3054 verbs */
+#define SI3054_VERB_READ_NODE  0x900
+#define SI3054_VERB_WRITE_NODE 0x100
+
+/* si3054 nodes (registers) */
+#define SI3054_EXTENDED_MID    2
+#define SI3054_LINE_RATE       3
+#define SI3054_LINE_LEVEL      4
+#define SI3054_GPIO_CFG        5
+#define SI3054_GPIO_POLARITY   6
+#define SI3054_GPIO_STICKY     7
+#define SI3054_GPIO_WAKEUP     8
+#define SI3054_GPIO_STATUS     9
+#define SI3054_GPIO_CONTROL   10
+#define SI3054_MISC_AFE       11
+#define SI3054_CHIPID         12
+#define SI3054_LINE_CFG1      13
+#define SI3054_LINE_STATUS    14
+#define SI3054_DC_TERMINATION 15
+#define SI3054_LINE_CONFIG    16
+#define SI3054_CALLPROG_ATT   17
+#define SI3054_SQ_CONTROL     18
+#define SI3054_MISC_CONTROL   19
+#define SI3054_RING_CTRL1     20
+#define SI3054_RING_CTRL2     21
+
+/* extended MID */
+#define SI3054_MEI_READY 0xf
+
+/* line level */
+#define SI3054_ATAG_MASK 0x00f0
+#define SI3054_DTAG_MASK 0xf000
+
+/* GPIO bits */
+#define SI3054_GPIO_OH    0x0001
+#define SI3054_GPIO_CID   0x0002
+
+/* chipid and revisions */
+#define SI3054_CHIPID_CODEC_REV_MASK 0x000f
+#define SI3054_CHIPID_DAA_REV_MASK   0x00f0
+#define SI3054_CHIPID_INTERNATIONAL  0x0100
+#define SI3054_CHIPID_DAA_ID         0x0f00
+#define SI3054_CHIPID_CODEC_ID      (1<<12)
+
+/* si3054 codec registers (nodes) access macros */
+#define GET_REG(codec,reg) (snd_hda_codec_read(codec,reg,0,SI3054_VERB_READ_NODE,0))
+#define SET_REG(codec,reg,val) (snd_hda_codec_write(codec,reg,0,SI3054_VERB_WRITE_NODE,val))
+
+
+struct si3054_spec {
+	unsigned international;
+	struct hda_pcm pcm;
+};
+
+
+/*
+ * Modem mixer
+ */
+
+#define PRIVATE_VALUE(reg,mask) ((reg<<16)|(mask&0xffff))
+#define PRIVATE_REG(val) ((val>>16)&0xffff)
+#define PRIVATE_MASK(val) (val&0xffff)
+
+static int si3054_switch_info(struct snd_kcontrol *kcontrol,
+		               struct snd_ctl_elem_info *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 si3054_switch_get(struct snd_kcontrol *kcontrol,
+		               struct snd_ctl_elem_value *uvalue)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	u16 reg  = PRIVATE_REG(kcontrol->private_value);
+	u16 mask = PRIVATE_MASK(kcontrol->private_value);
+	uvalue->value.integer.value[0] = (GET_REG(codec, reg)) & mask ? 1 : 0 ;
+	return 0;
+}
+
+static int si3054_switch_put(struct snd_kcontrol *kcontrol,
+		               struct snd_ctl_elem_value *uvalue)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	u16 reg  = PRIVATE_REG(kcontrol->private_value);
+	u16 mask = PRIVATE_MASK(kcontrol->private_value);
+	if (uvalue->value.integer.value[0])
+		SET_REG(codec, reg, (GET_REG(codec, reg)) | mask);
+	else
+		SET_REG(codec, reg, (GET_REG(codec, reg)) & ~mask);
+	return 0;
+}
+
+#define SI3054_KCONTROL(kname,reg,mask) { \
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+	.name = kname, \
+	.info = si3054_switch_info, \
+	.get  = si3054_switch_get, \
+	.put  = si3054_switch_put, \
+	.private_value = PRIVATE_VALUE(reg,mask), \
+}
+		
+
+static struct snd_kcontrol_new si3054_modem_mixer[] = {
+	SI3054_KCONTROL("Off-hook Switch", SI3054_GPIO_CONTROL, SI3054_GPIO_OH),
+	SI3054_KCONTROL("Caller ID Switch", SI3054_GPIO_CONTROL, SI3054_GPIO_CID),
+	{0}
+};
+
+static int si3054_build_controls(struct hda_codec *codec)
+{
+	return snd_hda_add_new_ctls(codec, si3054_modem_mixer);
+}
+
+
+/*
+ * PCM callbacks
+ */
+
+static int si3054_pcm_prepare(struct hda_pcm_stream *hinfo,
+			      struct hda_codec *codec,
+			      unsigned int stream_tag,
+			      unsigned int format,
+			      struct snd_pcm_substream *substream)
+{
+	u16 val;
+
+	SET_REG(codec, SI3054_LINE_RATE, substream->runtime->rate);
+	val = GET_REG(codec, SI3054_LINE_LEVEL);
+	val &= 0xff << (8 * (substream->stream != SNDRV_PCM_STREAM_PLAYBACK));
+	val |= ((stream_tag & 0xf) << 4) << (8 * (substream->stream == SNDRV_PCM_STREAM_PLAYBACK));
+	SET_REG(codec, SI3054_LINE_LEVEL, val);
+
+	snd_hda_codec_setup_stream(codec, hinfo->nid,
+				   stream_tag, 0, format);
+	return 0;
+}
+
+static int si3054_pcm_open(struct hda_pcm_stream *hinfo,
+			   struct hda_codec *codec,
+			    struct snd_pcm_substream *substream)
+{
+	static unsigned int rates[] = { 8000, 9600, 16000 };
+	static struct snd_pcm_hw_constraint_list hw_constraints_rates = {
+		.count = ARRAY_SIZE(rates),
+		.list = rates,
+		.mask = 0,
+	};
+	substream->runtime->hw.period_bytes_min = 80;
+	return snd_pcm_hw_constraint_list(substream->runtime, 0,
+			SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates);
+}
+
+
+static struct hda_pcm_stream si3054_pcm = {
+	.substreams = 1,
+	.channels_min = 1,
+	.channels_max = 1,
+	.nid = 0x1,
+	.rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|SNDRV_PCM_RATE_KNOT,
+	.formats = SNDRV_PCM_FMTBIT_S16_LE,
+	.maxbps = 16,
+	.ops = {
+		.open = si3054_pcm_open,
+		.prepare = si3054_pcm_prepare,
+	},
+};
+
+
+static int si3054_build_pcms(struct hda_codec *codec)
+{
+	struct si3054_spec *spec = codec->spec;
+	struct hda_pcm *info = &spec->pcm;
+	si3054_pcm.nid = codec->mfg;
+	codec->num_pcms = 1;
+	codec->pcm_info = info;
+	info->name = "Si3054 Modem";
+	info->stream[SNDRV_PCM_STREAM_PLAYBACK] = si3054_pcm;
+	info->stream[SNDRV_PCM_STREAM_CAPTURE]  = si3054_pcm;
+	info->is_modem = 1;
+	return 0;
+}
+
+
+/*
+ * Init part
+ */
+
+static int si3054_init(struct hda_codec *codec)
+{
+	struct si3054_spec *spec = codec->spec;
+	unsigned wait_count;
+	u16 val;
+
+	snd_hda_codec_write(codec, AC_NODE_ROOT, 0, AC_VERB_SET_CODEC_RESET, 0);
+	snd_hda_codec_write(codec, codec->mfg, 0, AC_VERB_SET_STREAM_FORMAT, 0);
+	SET_REG(codec, SI3054_LINE_RATE, 9600);
+	SET_REG(codec, SI3054_LINE_LEVEL, SI3054_DTAG_MASK|SI3054_ATAG_MASK);
+	SET_REG(codec, SI3054_EXTENDED_MID, 0);
+
+	wait_count = 10;
+	do {
+		msleep(2);
+		val = GET_REG(codec, SI3054_EXTENDED_MID);
+	} while ((val & SI3054_MEI_READY) != SI3054_MEI_READY && wait_count--);
+
+	if((val&SI3054_MEI_READY) != SI3054_MEI_READY) {
+		snd_printk(KERN_ERR "si3054: cannot initialize. EXT MID = %04x\n", val);
+		return -EACCES;
+	}
+
+	SET_REG(codec, SI3054_GPIO_POLARITY, 0xffff);
+	SET_REG(codec, SI3054_GPIO_CFG, 0x0);
+	SET_REG(codec, SI3054_MISC_AFE, 0);
+	SET_REG(codec, SI3054_LINE_CFG1,0x200);
+
+	if((GET_REG(codec,SI3054_LINE_STATUS) & (1<<6)) == 0) {
+		snd_printd("Link Frame Detect(FDT) is not ready (line status: %04x)\n",
+				GET_REG(codec,SI3054_LINE_STATUS));
+	}
+
+	spec->international = GET_REG(codec, SI3054_CHIPID) & SI3054_CHIPID_INTERNATIONAL;
+
+	return 0;
+}
+
+static void si3054_free(struct hda_codec *codec)
+{
+	kfree(codec->spec);
+}
+
+
+/*
+ */
+
+static struct hda_codec_ops si3054_patch_ops = {
+	.build_controls = si3054_build_controls,
+	.build_pcms = si3054_build_pcms,
+	.init = si3054_init,
+	.free = si3054_free,
+#ifdef CONFIG_PM
+	//.suspend = si3054_suspend,
+	.resume = si3054_init,
+#endif
+};
+
+static int patch_si3054(struct hda_codec *codec)
+{
+	struct si3054_spec *spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+	if (spec == NULL)
+		return -ENOMEM;
+	codec->spec = spec;
+	codec->patch_ops = si3054_patch_ops;
+	return 0;
+}
+
+/*
+ * patch entries
+ */
+struct hda_codec_preset snd_hda_preset_si3054[] = {
+ 	{ .id = 0x163c3055, .name = "Si3054", .patch = patch_si3054 },
+        { .id = 0x163c3155, .name = "Si3054", .patch = patch_si3054 },
+        { .id = 0x11c13026, .name = "Si3054", .patch = patch_si3054 },
+	{0}
+};
+
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/hda/patch_sigmatel.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/hda/patch_sigmatel.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/hda/patch_sigmatel.c	(revision 91)
@@ -0,0 +1,2310 @@
+/*
+ * Universal Interface for Intel High Definition Audio Codec
+ *
+ * HD audio interface patch for SigmaTel STAC92xx
+ *
+ * Copyright (c) 2005 Embedded Alley Solutions, Inc.
+ * Matt Porter <mporter@embeddedalley.com>
+ *
+ * Based on patch_cmedia.c and patch_realtek.c
+ * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
+ *
+ *  This driver 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 driver is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <sound/core.h>
+#include <sound/asoundef.h>
+#include "hda_codec.h"
+#include "hda_local.h"
+
+#define NUM_CONTROL_ALLOC	32
+#define STAC_HP_EVENT		0x37
+
+#define PCI_VENDOR_ID_DELL              0x1028
+
+enum {
+	STAC_REF,
+	STAC_9200_MODELS
+};
+
+enum {
+	STAC_9205_REF,
+	STAC_9205_MODELS
+};
+
+enum {
+	STAC_925x_REF,
+	STAC_M2_2,
+	STAC_MA6,
+	STAC_925x_MODELS
+};
+
+enum {
+	STAC_D945_REF,
+	STAC_D945GTP3,
+	STAC_D945GTP5,
+	STAC_MACMINI,
+	STAC_922X_MODELS
+};
+
+enum {
+	STAC_D965_REF,
+	STAC_D965_3ST,
+	STAC_D965_5ST,
+	STAC_927X_MODELS
+};
+
+struct sigmatel_spec {
+	struct snd_kcontrol_new *mixers[4];
+	unsigned int num_mixers;
+
+	int board_config;
+	unsigned int surr_switch: 1;
+	unsigned int line_switch: 1;
+	unsigned int mic_switch: 1;
+	unsigned int alt_switch: 1;
+	unsigned int hp_detect: 1;
+	unsigned int gpio_mute: 1;
+
+	/* playback */
+	struct hda_multi_out multiout;
+	hda_nid_t dac_nids[5];
+
+	/* capture */
+	hda_nid_t *adc_nids;
+	unsigned int num_adcs;
+	hda_nid_t *mux_nids;
+	unsigned int num_muxes;
+	hda_nid_t *dmic_nids;
+	unsigned int num_dmics;
+	hda_nid_t dmux_nid;
+	hda_nid_t dig_in_nid;
+
+	/* pin widgets */
+	hda_nid_t *pin_nids;
+	unsigned int num_pins;
+	unsigned int *pin_configs;
+	unsigned int *bios_pin_configs;
+
+	/* codec specific stuff */
+	struct hda_verb *init;
+	struct snd_kcontrol_new *mixer;
+
+	/* capture source */
+	struct hda_input_mux *dinput_mux;
+	unsigned int cur_dmux;
+	struct hda_input_mux *input_mux;
+	unsigned int cur_mux[3];
+
+	/* i/o switches */
+	unsigned int io_switch[2];
+
+	struct hda_pcm pcm_rec[2];	/* PCM information */
+
+	/* dynamic controls and input_mux */
+	struct auto_pin_cfg autocfg;
+	unsigned int num_kctl_alloc, num_kctl_used;
+	struct snd_kcontrol_new *kctl_alloc;
+	struct hda_input_mux private_dimux;
+	struct hda_input_mux private_imux;
+};
+
+static hda_nid_t stac9200_adc_nids[1] = {
+        0x03,
+};
+
+static hda_nid_t stac9200_mux_nids[1] = {
+        0x0c,
+};
+
+static hda_nid_t stac9200_dac_nids[1] = {
+        0x02,
+};
+
+static hda_nid_t stac925x_adc_nids[1] = {
+        0x03,
+};
+
+static hda_nid_t stac925x_mux_nids[1] = {
+        0x0f,
+};
+
+static hda_nid_t stac925x_dac_nids[1] = {
+        0x02,
+};
+
+static hda_nid_t stac922x_adc_nids[2] = {
+        0x06, 0x07,
+};
+
+static hda_nid_t stac922x_mux_nids[2] = {
+        0x12, 0x13,
+};
+
+static hda_nid_t stac927x_adc_nids[3] = {
+        0x07, 0x08, 0x09
+};
+
+static hda_nid_t stac927x_mux_nids[3] = {
+        0x15, 0x16, 0x17
+};
+
+static hda_nid_t stac9205_adc_nids[2] = {
+        0x12, 0x13
+};
+
+static hda_nid_t stac9205_mux_nids[2] = {
+        0x19, 0x1a
+};
+
+static hda_nid_t stac9205_dmic_nids[3] = {
+        0x17, 0x18, 0
+};
+
+static hda_nid_t stac9200_pin_nids[8] = {
+	0x08, 0x09, 0x0d, 0x0e, 
+	0x0f, 0x10, 0x11, 0x12,
+};
+
+static hda_nid_t stac925x_pin_nids[8] = {
+	0x07, 0x08, 0x0a, 0x0b,
+	0x0c, 0x0d, 0x10, 0x11,
+};
+
+static hda_nid_t stac922x_pin_nids[10] = {
+	0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
+	0x0f, 0x10, 0x11, 0x15, 0x1b,
+};
+
+static hda_nid_t stac927x_pin_nids[14] = {
+	0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
+	0x0f, 0x10, 0x11, 0x12, 0x13,
+	0x14, 0x21, 0x22, 0x23,
+};
+
+static hda_nid_t stac9205_pin_nids[12] = {
+	0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
+	0x0f, 0x14, 0x16, 0x17, 0x18,
+	0x21, 0x22,
+	
+};
+
+static int stac92xx_dmux_enum_info(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_info *uinfo)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct sigmatel_spec *spec = codec->spec;
+	return snd_hda_input_mux_info(spec->dinput_mux, uinfo);
+}
+
+static int stac92xx_dmux_enum_get(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct sigmatel_spec *spec = codec->spec;
+
+	ucontrol->value.enumerated.item[0] = spec->cur_dmux;
+	return 0;
+}
+
+static int stac92xx_dmux_enum_put(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct sigmatel_spec *spec = codec->spec;
+
+	return snd_hda_input_mux_put(codec, spec->dinput_mux, ucontrol,
+				     spec->dmux_nid, &spec->cur_dmux);
+}
+
+static int stac92xx_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct sigmatel_spec *spec = codec->spec;
+	return snd_hda_input_mux_info(spec->input_mux, uinfo);
+}
+
+static int stac92xx_mux_enum_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct sigmatel_spec *spec = codec->spec;
+	unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+
+	ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
+	return 0;
+}
+
+static int stac92xx_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct sigmatel_spec *spec = codec->spec;
+	unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+
+	return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
+				     spec->mux_nids[adc_idx], &spec->cur_mux[adc_idx]);
+}
+
+static struct hda_verb stac9200_core_init[] = {
+	/* set dac0mux for dac converter */
+	{ 0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
+	{0}
+};
+
+static struct hda_verb stac925x_core_init[] = {
+	/* set dac0mux for dac converter */
+	{ 0x06, AC_VERB_SET_CONNECT_SEL, 0x00},
+	{0}
+};
+
+static struct hda_verb stac922x_core_init[] = {
+	/* set master volume and direct control */	
+	{ 0x16, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
+	{0}
+};
+
+static struct hda_verb d965_core_init[] = {
+	/* set master volume and direct control */	
+	{ 0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
+	/* unmute node 0x1b */
+	{ 0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+	/* select node 0x03 as DAC */	
+	{ 0x0b, AC_VERB_SET_CONNECT_SEL, 0x01},
+	{0}
+};
+
+static struct hda_verb stac927x_core_init[] = {
+	/* set master volume and direct control */	
+	{ 0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
+	{0}
+};
+
+static struct hda_verb stac9205_core_init[] = {
+	/* set master volume and direct control */	
+	{ 0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
+	{0}
+};
+
+static struct snd_kcontrol_new stac9200_mixer[] = {
+	HDA_CODEC_VOLUME("Master Playback Volume", 0xb, 0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Master Playback Switch", 0xb, 0, HDA_OUTPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Input Source",
+		.count = 1,
+		.info = stac92xx_mux_enum_info,
+		.get = stac92xx_mux_enum_get,
+		.put = stac92xx_mux_enum_put,
+	},
+	HDA_CODEC_VOLUME("Capture Volume", 0x0a, 0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x0a, 0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Capture Mux Volume", 0x0c, 0, HDA_OUTPUT),
+	{0} /* end */
+};
+
+static struct snd_kcontrol_new stac925x_mixer[] = {
+	HDA_CODEC_VOLUME("Master Playback Volume", 0xe, 0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Master Playback Switch", 0xe, 0, HDA_OUTPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Input Source",
+		.count = 1,
+		.info = stac92xx_mux_enum_info,
+		.get = stac92xx_mux_enum_get,
+		.put = stac92xx_mux_enum_put,
+	},
+	HDA_CODEC_VOLUME("Capture Volume", 0x09, 0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x09, 0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Capture Mux Volume", 0x0f, 0, HDA_OUTPUT),
+	{0} /* end */
+};
+
+/* This needs to be generated dynamically based on sequence */
+static struct snd_kcontrol_new stac922x_mixer[] = {
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Input Source",
+		.count = 1,
+		.info = stac92xx_mux_enum_info,
+		.get = stac92xx_mux_enum_get,
+		.put = stac92xx_mux_enum_put,
+	},
+	HDA_CODEC_VOLUME("Capture Volume", 0x17, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x17, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mux Capture Volume", 0x12, 0x0, HDA_OUTPUT),
+	{0} /* end */
+};
+
+/* This needs to be generated dynamically based on sequence */
+static struct snd_kcontrol_new stac9227_mixer[] = {
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Input Source",
+		.count = 1,
+		.info = stac92xx_mux_enum_info,
+		.get = stac92xx_mux_enum_get,
+		.put = stac92xx_mux_enum_put,
+	},
+	HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x1b, 0x0, HDA_OUTPUT),
+	{0} /* end */
+};
+
+static struct snd_kcontrol_new stac927x_mixer[] = {
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Input Source",
+		.count = 1,
+		.info = stac92xx_mux_enum_info,
+		.get = stac92xx_mux_enum_get,
+		.put = stac92xx_mux_enum_put,
+	},
+	HDA_CODEC_VOLUME("InMux Capture Volume", 0x15, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("InVol Capture Volume", 0x18, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("ADCMux Capture Switch", 0x1b, 0x0, HDA_OUTPUT),
+	{0} /* end */
+};
+
+static struct snd_kcontrol_new stac9205_mixer[] = {
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Digital Input Source",
+		.count = 1,
+		.info = stac92xx_dmux_enum_info,
+		.get = stac92xx_dmux_enum_get,
+		.put = stac92xx_dmux_enum_put,
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Input Source",
+		.count = 1,
+		.info = stac92xx_mux_enum_info,
+		.get = stac92xx_mux_enum_get,
+		.put = stac92xx_mux_enum_put,
+	},
+	HDA_CODEC_VOLUME("InMux Capture Volume", 0x19, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("InVol Capture Volume", 0x1b, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("ADCMux Capture Switch", 0x1d, 0x0, HDA_OUTPUT),
+	{0} /* end */
+};
+
+static int stac92xx_build_controls(struct hda_codec *codec)
+{
+	struct sigmatel_spec *spec = codec->spec;
+	int err;
+	int i;
+
+	err = snd_hda_add_new_ctls(codec, spec->mixer);
+	if (err < 0)
+		return err;
+
+	for (i = 0; i < spec->num_mixers; i++) {
+		err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
+		if (err < 0)
+			return err;
+	}
+
+	if (spec->multiout.dig_out_nid) {
+		err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
+		if (err < 0)
+			return err;
+	}
+	if (spec->dig_in_nid) {
+		err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
+		if (err < 0)
+			return err;
+	}
+	return 0;	
+}
+
+static unsigned int ref9200_pin_configs[8] = {
+	0x01c47010, 0x01447010, 0x0221401f, 0x01114010,
+	0x02a19020, 0x01a19021, 0x90100140, 0x01813122,
+};
+
+static unsigned int *stac9200_brd_tbl[STAC_9200_MODELS] = {
+	[STAC_REF] = ref9200_pin_configs,
+};
+
+static const char *stac9200_models[STAC_9200_MODELS] = {
+	[STAC_REF] = "ref",
+};
+
+static struct snd_pci_quirk stac9200_cfg_tbl[] = {
+	/* SigmaTel reference board */
+	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
+		      "DFI LanParty", STAC_REF),
+	/* Dell laptops have BIOS problem */
+	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01b5,
+		      "Dell Inspiron 630m", STAC_REF),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01c2,
+		      "Dell Latitude D620", STAC_REF),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01cb,
+		      "Dell Latitude 120L", STAC_REF),
+	{0} /* terminator */
+};
+
+static unsigned int ref925x_pin_configs[8] = {
+	0x40c003f0, 0x424503f2, 0x01813022, 0x02a19021,
+	0x90a70320, 0x02214210, 0x400003f1, 0x9033032e,
+};
+
+static unsigned int stac925x_MA6_pin_configs[8] = {
+	0x40c003f0, 0x424503f2, 0x01813022, 0x02a19021,
+	0x90a70320, 0x90100211, 0x400003f1, 0x9033032e,
+};
+
+static unsigned int stac925xM2_2_pin_configs[8] = {
+	0x40c003f3, 0x424503f2, 0x041800f4, 0x02a19020,
+	0x50a103F0, 0x90100210, 0x400003f1, 0x9033032e,
+};
+
+static unsigned int *stac925x_brd_tbl[STAC_925x_MODELS] = {
+	[STAC_REF] = ref925x_pin_configs,
+	[STAC_M2_2] = stac925xM2_2_pin_configs,
+	[STAC_MA6] = stac925x_MA6_pin_configs,
+};
+
+static const char *stac925x_models[STAC_925x_MODELS] = {
+	[STAC_REF] = "ref",
+	[STAC_M2_2] = "m2-2",
+	[STAC_MA6] = "m6",
+};
+
+static struct snd_pci_quirk stac925x_cfg_tbl[] = {
+	/* SigmaTel reference board */
+	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, "DFI LanParty", STAC_REF),
+	SND_PCI_QUIRK(0x107b, 0x0316, "Gateway M255", STAC_REF),
+	SND_PCI_QUIRK(0x107b, 0x0366, "Gateway MP6954", STAC_REF),
+	SND_PCI_QUIRK(0x107b, 0x0461, "Gateway NX560XL", STAC_MA6),
+	SND_PCI_QUIRK(0x1002, 0x437b, "Gateway MX6453", STAC_M2_2),
+	{0} /* terminator */
+};
+
+static unsigned int ref922x_pin_configs[10] = {
+	0x01014010, 0x01016011, 0x01012012, 0x0221401f,
+	0x01813122, 0x01011014, 0x01441030, 0x01c41030,
+	0x40000100, 0x40000100,
+};
+
+static unsigned int d945gtp3_pin_configs[10] = {
+	0x0221401f, 0x01a19022, 0x01813021, 0x01014010,
+	0x40000100, 0x40000100, 0x40000100, 0x40000100,
+	0x02a19120, 0x40000100,
+};
+
+static unsigned int d945gtp5_pin_configs[10] = {
+	0x0221401f, 0x01011012, 0x01813024, 0x01014010,
+	0x01a19021, 0x01016011, 0x01452130, 0x40000100,
+	0x02a19320, 0x40000100,
+};
+
+static unsigned int *stac922x_brd_tbl[STAC_922X_MODELS] = {
+	[STAC_D945_REF] = ref922x_pin_configs,
+	[STAC_D945GTP3] = d945gtp3_pin_configs,
+	[STAC_D945GTP5] = d945gtp5_pin_configs,
+	[STAC_MACMINI] = d945gtp5_pin_configs,
+};
+
+static const char *stac922x_models[STAC_922X_MODELS] = {
+	[STAC_D945_REF]	= "ref",
+	[STAC_D945GTP5]	= "5stack",
+	[STAC_D945GTP3]	= "3stack",
+	[STAC_MACMINI]	= "macmini",
+};
+
+static struct snd_pci_quirk stac922x_cfg_tbl[] = {
+	/* SigmaTel reference board */
+	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
+		      "DFI LanParty", STAC_D945_REF),
+	/* Intel 945G based systems */
+	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0101,
+		      "Intel D945G", STAC_D945GTP3),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0202,
+		      "Intel D945G", STAC_D945GTP3),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0606,
+		      "Intel D945G", STAC_D945GTP3),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0601,
+		      "Intel D945G", STAC_D945GTP3),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0111,
+		      "Intel D945G", STAC_D945GTP3),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x1115,
+		      "Intel D945G", STAC_D945GTP3),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x1116,
+		      "Intel D945G", STAC_D945GTP3),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x1117,
+		      "Intel D945G", STAC_D945GTP3),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x1118,
+		      "Intel D945G", STAC_D945GTP3),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x1119,
+		      "Intel D945G", STAC_D945GTP3),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x8826,
+		      "Intel D945G", STAC_D945GTP3),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x5049,
+		      "Intel D945G", STAC_D945GTP3),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x5055,
+		      "Intel D945G", STAC_D945GTP3),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x5048,
+		      "Intel D945G", STAC_D945GTP3),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0110,
+		      "Intel D945G", STAC_D945GTP3),
+	/* Intel D945G 5-stack systems */
+	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0404,
+		      "Intel D945G", STAC_D945GTP5),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0303,
+		      "Intel D945G", STAC_D945GTP5),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0013,
+		      "Intel D945G", STAC_D945GTP5),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0417,
+		      "Intel D945G", STAC_D945GTP5),
+	/* Intel 945P based systems */
+	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0b0b,
+		      "Intel D945P", STAC_D945GTP3),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0112,
+		      "Intel D945P", STAC_D945GTP3),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0d0d,
+		      "Intel D945P", STAC_D945GTP3),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0909,
+		      "Intel D945P", STAC_D945GTP3),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0505,
+		      "Intel D945P", STAC_D945GTP3),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0707,
+		      "Intel D945P", STAC_D945GTP5),
+	/* other systems  */
+	/* Apple Mac Mini (early 2006) */
+	SND_PCI_QUIRK(0x8384, 0x7680,
+		      "Mac Mini", STAC_MACMINI),
+	{0} /* terminator */
+};
+
+static unsigned int ref927x_pin_configs[14] = {
+	0x02214020, 0x02a19080, 0x0181304e, 0x01014010,
+	0x01a19040, 0x01011012, 0x01016011, 0x0101201f, 
+	0x183301f0, 0x18a001f0, 0x18a001f0, 0x01442070,
+	0x01c42190, 0x40000100,
+};
+
+static unsigned int d965_3st_pin_configs[14] = {
+	0x0221401f, 0x02a19120, 0x40000100, 0x01014011,
+	0x01a19021, 0x01813024, 0x40000100, 0x40000100,
+	0x40000100, 0x40000100, 0x40000100, 0x40000100,
+	0x40000100, 0x40000100
+};
+
+static unsigned int d965_5st_pin_configs[14] = {
+	0x02214020, 0x02a19080, 0x0181304e, 0x01014010,
+	0x01a19040, 0x01011012, 0x01016011, 0x40000100,
+	0x40000100, 0x40000100, 0x40000100, 0x01442070,
+	0x40000100, 0x40000100
+};
+
+static unsigned int *stac927x_brd_tbl[STAC_927X_MODELS] = {
+	[STAC_D965_REF] = ref927x_pin_configs,
+	[STAC_D965_3ST] = d965_3st_pin_configs,
+	[STAC_D965_5ST] = d965_5st_pin_configs,
+};
+
+static const char *stac927x_models[STAC_927X_MODELS] = {
+	[STAC_D965_REF]	= "ref",
+	[STAC_D965_3ST]	= "3stack",
+	[STAC_D965_5ST] = "5stack",
+};
+
+static struct snd_pci_quirk stac927x_cfg_tbl[] = {
+	/* SigmaTel reference board */
+	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
+		      "DFI LanParty", STAC_D965_REF),
+	 /* Intel 946 based systems */
+	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x3d01, "Intel D946", STAC_D965_3ST),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0xa301, "Intel D946", STAC_D965_3ST),
+	/* 965 based 3 stack systems */
+	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2116, "Intel D965", STAC_D965_3ST),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2115, "Intel D965", STAC_D965_3ST),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2114, "Intel D965", STAC_D965_3ST),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2113, "Intel D965", STAC_D965_3ST),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2112, "Intel D965", STAC_D965_3ST),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2111, "Intel D965", STAC_D965_3ST),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2110, "Intel D965", STAC_D965_3ST),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2009, "Intel D965", STAC_D965_3ST),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2008, "Intel D965", STAC_D965_3ST),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2007, "Intel D965", STAC_D965_3ST),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2006, "Intel D965", STAC_D965_3ST),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2005, "Intel D965", STAC_D965_3ST),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2004, "Intel D965", STAC_D965_3ST),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2003, "Intel D965", STAC_D965_3ST),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2002, "Intel D965", STAC_D965_3ST),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2001, "Intel D965", STAC_D965_3ST),
+	/* 965 based 5 stack systems */
+	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2301, "Intel D965", STAC_D965_5ST),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2302, "Intel D965", STAC_D965_5ST),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2303, "Intel D965", STAC_D965_5ST),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2304, "Intel D965", STAC_D965_5ST),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2305, "Intel D965", STAC_D965_5ST),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2501, "Intel D965", STAC_D965_5ST),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2502, "Intel D965", STAC_D965_5ST),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2503, "Intel D965", STAC_D965_5ST),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2504, "Intel D965", STAC_D965_5ST),
+	{0} /* terminator */
+};
+
+static unsigned int ref9205_pin_configs[12] = {
+	0x40000100, 0x40000100, 0x01016011, 0x01014010,
+	0x01813122, 0x01a19021, 0x40000100, 0x40000100,
+	0x90a000f0, 0x90a000f0, 0x01441030, 0x01c41030
+};
+
+static unsigned int *stac9205_brd_tbl[STAC_9205_MODELS] = {
+	ref9205_pin_configs,
+};
+
+static const char *stac9205_models[STAC_9205_MODELS] = {
+	[STAC_9205_REF] = "ref",
+};
+
+static struct snd_pci_quirk stac9205_cfg_tbl[] = {
+	/* SigmaTel reference board */
+	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
+		      "DFI LanParty", STAC_9205_REF),
+	{0} /* terminator */
+};
+
+static int stac92xx_save_bios_config_regs(struct hda_codec *codec)
+{
+	int i;
+	struct sigmatel_spec *spec = codec->spec;
+	
+	if (! spec->bios_pin_configs) {
+		spec->bios_pin_configs = kcalloc(spec->num_pins,
+		                                 sizeof(*spec->bios_pin_configs), GFP_KERNEL);
+		if (! spec->bios_pin_configs)
+			return -ENOMEM;
+	}
+	
+	for (i = 0; i < spec->num_pins; i++) {
+		hda_nid_t nid = spec->pin_nids[i];
+		unsigned int pin_cfg;
+		
+		pin_cfg = snd_hda_codec_read(codec, nid, 0, 
+			AC_VERB_GET_CONFIG_DEFAULT, 0x00);	
+		snd_printdd(KERN_INFO "hda_codec: pin nid %2.2x bios pin config %8.8x\n",
+					nid, pin_cfg);
+		spec->bios_pin_configs[i] = pin_cfg;
+	}
+	
+	return 0;
+}
+
+static void stac92xx_set_config_regs(struct hda_codec *codec)
+{
+	int i;
+	struct sigmatel_spec *spec = codec->spec;
+	unsigned int pin_cfg;
+
+	if (! spec->pin_nids || ! spec->pin_configs)
+		return;
+
+	for (i = 0; i < spec->num_pins; i++) {
+		snd_hda_codec_write(codec, spec->pin_nids[i], 0,
+				    AC_VERB_SET_CONFIG_DEFAULT_BYTES_0,
+				    spec->pin_configs[i] & 0x000000ff);
+		snd_hda_codec_write(codec, spec->pin_nids[i], 0,
+				    AC_VERB_SET_CONFIG_DEFAULT_BYTES_1,
+				    (spec->pin_configs[i] & 0x0000ff00) >> 8);
+		snd_hda_codec_write(codec, spec->pin_nids[i], 0,
+				    AC_VERB_SET_CONFIG_DEFAULT_BYTES_2,
+				    (spec->pin_configs[i] & 0x00ff0000) >> 16);
+		snd_hda_codec_write(codec, spec->pin_nids[i], 0,
+				    AC_VERB_SET_CONFIG_DEFAULT_BYTES_3,
+				    spec->pin_configs[i] >> 24);
+		pin_cfg = snd_hda_codec_read(codec, spec->pin_nids[i], 0,
+					     AC_VERB_GET_CONFIG_DEFAULT,
+					     0x00);	
+		snd_printdd(KERN_INFO "hda_codec: pin nid %2.2x pin config %8.8x\n", spec->pin_nids[i], pin_cfg);
+	}
+}
+
+/*
+ * Analog playback callbacks
+ */
+static int stac92xx_playback_pcm_open(struct hda_pcm_stream *hinfo,
+				      struct hda_codec *codec,
+				      struct snd_pcm_substream *substream)
+{
+	struct sigmatel_spec *spec = codec->spec;
+	return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream);
+}
+
+static int stac92xx_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+					 struct hda_codec *codec,
+					 unsigned int stream_tag,
+					 unsigned int format,
+					 struct snd_pcm_substream *substream)
+{
+	struct sigmatel_spec *spec = codec->spec;
+	return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag, format, substream);
+}
+
+static int stac92xx_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+					struct hda_codec *codec,
+					struct snd_pcm_substream *substream)
+{
+	struct sigmatel_spec *spec = codec->spec;
+	return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
+}
+
+/*
+ * Digital playback callbacks
+ */
+static int stac92xx_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
+					  struct hda_codec *codec,
+					  struct snd_pcm_substream *substream)
+{
+	struct sigmatel_spec *spec = codec->spec;
+	return snd_hda_multi_out_dig_open(codec, &spec->multiout);
+}
+
+static int stac92xx_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
+					   struct hda_codec *codec,
+					   struct snd_pcm_substream *substream)
+{
+	struct sigmatel_spec *spec = codec->spec;
+	return snd_hda_multi_out_dig_close(codec, &spec->multiout);
+}
+
+
+/*
+ * Analog capture callbacks
+ */
+static int stac92xx_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
+					struct hda_codec *codec,
+					unsigned int stream_tag,
+					unsigned int format,
+					struct snd_pcm_substream *substream)
+{
+	struct sigmatel_spec *spec = codec->spec;
+
+	snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
+                                   stream_tag, 0, format);
+	return 0;
+}
+
+static int stac92xx_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+					struct hda_codec *codec,
+					struct snd_pcm_substream *substream)
+{
+	struct sigmatel_spec *spec = codec->spec;
+
+	snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], 0, 0, 0);
+	return 0;
+}
+
+static struct hda_pcm_stream stac92xx_pcm_digital_playback = {
+	.substreams = 1,
+	.channels_min = 2,
+	.channels_max = 2,
+	/* NID is set in stac92xx_build_pcms */
+	.ops = {
+		.open = stac92xx_dig_playback_pcm_open,
+		.close = stac92xx_dig_playback_pcm_close
+	},
+};
+
+static struct hda_pcm_stream stac92xx_pcm_digital_capture = {
+	.substreams = 1,
+	.channels_min = 2,
+	.channels_max = 2,
+	/* NID is set in stac92xx_build_pcms */
+};
+
+static struct hda_pcm_stream stac92xx_pcm_analog_playback = {
+	.substreams = 1,
+	.channels_min = 2,
+	.channels_max = 8,
+	.nid = 0x02, /* NID to query formats and rates */
+	.ops = {
+		.open = stac92xx_playback_pcm_open,
+		.prepare = stac92xx_playback_pcm_prepare,
+		.cleanup = stac92xx_playback_pcm_cleanup
+	},
+};
+
+static struct hda_pcm_stream stac92xx_pcm_analog_alt_playback = {
+	.substreams = 1,
+	.channels_min = 2,
+	.channels_max = 2,
+	.nid = 0x06, /* NID to query formats and rates */
+	.ops = {
+		.open = stac92xx_playback_pcm_open,
+		.prepare = stac92xx_playback_pcm_prepare,
+		.cleanup = stac92xx_playback_pcm_cleanup
+	},
+};
+
+static struct hda_pcm_stream stac92xx_pcm_analog_capture = {
+	.substreams = 2,
+	.channels_min = 2,
+	.channels_max = 2,
+	/* NID is set in stac92xx_build_pcms */
+	.ops = {
+		.prepare = stac92xx_capture_pcm_prepare,
+		.cleanup = stac92xx_capture_pcm_cleanup
+	},
+};
+
+static int stac92xx_build_pcms(struct hda_codec *codec)
+{
+	struct sigmatel_spec *spec = codec->spec;
+	struct hda_pcm *info = spec->pcm_rec;
+
+	codec->num_pcms = 1;
+	codec->pcm_info = info;
+
+	info->name = "STAC92xx Analog";
+	info->stream[SNDRV_PCM_STREAM_PLAYBACK] = stac92xx_pcm_analog_playback;
+	info->stream[SNDRV_PCM_STREAM_CAPTURE] = stac92xx_pcm_analog_capture;
+	info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
+
+	if (spec->alt_switch) {
+		codec->num_pcms++;
+		info++;
+		info->name = "STAC92xx Analog Alt";
+		info->stream[SNDRV_PCM_STREAM_PLAYBACK] = stac92xx_pcm_analog_alt_playback;
+	}
+
+	if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
+		codec->num_pcms++;
+		info++;
+		info->name = "STAC92xx Digital";
+		if (spec->multiout.dig_out_nid) {
+			info->stream[SNDRV_PCM_STREAM_PLAYBACK] = stac92xx_pcm_digital_playback;
+			info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid;
+		}
+		if (spec->dig_in_nid) {
+			info->stream[SNDRV_PCM_STREAM_CAPTURE] = stac92xx_pcm_digital_capture;
+			info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid;
+		}
+	}
+
+	return 0;
+}
+
+static unsigned int stac92xx_get_vref(struct hda_codec *codec, hda_nid_t nid)
+{
+	unsigned int pincap = snd_hda_param_read(codec, nid,
+						 AC_PAR_PIN_CAP);
+	pincap = (pincap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT;
+	if (pincap & AC_PINCAP_VREF_100)
+		return AC_PINCTL_VREF_100;
+	if (pincap & AC_PINCAP_VREF_80)
+		return AC_PINCTL_VREF_80;
+	if (pincap & AC_PINCAP_VREF_50)
+		return AC_PINCTL_VREF_50;
+	if (pincap & AC_PINCAP_VREF_GRD)
+		return AC_PINCTL_VREF_GRD;
+	return 0;
+}
+
+static void stac92xx_auto_set_pinctl(struct hda_codec *codec, hda_nid_t nid, int pin_type)
+
+{
+	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);
+}
+
+static int stac92xx_io_switch_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *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 stac92xx_io_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct sigmatel_spec *spec = codec->spec;
+	int io_idx = kcontrol-> private_value & 0xff;
+
+	ucontrol->value.integer.value[0] = spec->io_switch[io_idx];
+	return 0;
+}
+
+static int stac92xx_io_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct sigmatel_spec *spec = codec->spec;
+        hda_nid_t nid = kcontrol->private_value >> 8;
+	int io_idx = kcontrol-> private_value & 0xff;
+        unsigned short val = ucontrol->value.integer.value[0];
+
+	spec->io_switch[io_idx] = val;
+
+	if (val)
+		stac92xx_auto_set_pinctl(codec, nid, AC_PINCTL_OUT_EN);
+	else {
+		unsigned int pinctl = AC_PINCTL_IN_EN;
+		if (io_idx) /* set VREF for mic */
+			pinctl |= stac92xx_get_vref(codec, nid);
+		stac92xx_auto_set_pinctl(codec, nid, pinctl);
+	}
+        return 1;
+}
+
+#define STAC_CODEC_IO_SWITCH(xname, xpval) \
+	{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+	  .name = xname, \
+	  .index = 0, \
+          .info = stac92xx_io_switch_info, \
+          .get = stac92xx_io_switch_get, \
+          .put = stac92xx_io_switch_put, \
+          .private_value = xpval, \
+	}
+
+
+enum {
+	STAC_CTL_WIDGET_VOL,
+	STAC_CTL_WIDGET_MUTE,
+	STAC_CTL_WIDGET_IO_SWITCH,
+};
+
+static struct snd_kcontrol_new stac92xx_control_templates[] = {
+	HDA_CODEC_VOLUME(NULL, 0, 0, 0),
+	HDA_CODEC_MUTE(NULL, 0, 0, 0),
+	STAC_CODEC_IO_SWITCH(NULL, 0),
+};
+
+/* add dynamic controls */
+static int stac92xx_add_control(struct sigmatel_spec *spec, int type, const char *name, unsigned long val)
+{
+	struct snd_kcontrol_new *knew;
+
+	if (spec->num_kctl_used >= spec->num_kctl_alloc) {
+		int num = spec->num_kctl_alloc + NUM_CONTROL_ALLOC;
+
+		knew = kcalloc(num + 1, sizeof(*knew), GFP_KERNEL); /* array + terminator */
+		if (! knew)
+			return -ENOMEM;
+		if (spec->kctl_alloc) {
+			memcpy(knew, spec->kctl_alloc, sizeof(*knew) * spec->num_kctl_alloc);
+			kfree(spec->kctl_alloc);
+		}
+		spec->kctl_alloc = knew;
+		spec->num_kctl_alloc = num;
+	}
+
+	knew = &spec->kctl_alloc[spec->num_kctl_used];
+	*knew = stac92xx_control_templates[type];
+	knew->name = kstrdup(name, GFP_KERNEL);
+	if (! knew->name)
+		return -ENOMEM;
+	knew->private_value = val;
+	spec->num_kctl_used++;
+	return 0;
+}
+
+/* flag inputs as additional dynamic lineouts */
+static int stac92xx_add_dyn_out_pins(struct hda_codec *codec, struct auto_pin_cfg *cfg)
+{
+	struct sigmatel_spec *spec = codec->spec;
+
+	switch (cfg->line_outs) {
+	case 3:
+		/* add line-in as side */
+		if (cfg->input_pins[AUTO_PIN_LINE]) {
+			cfg->line_out_pins[3] = cfg->input_pins[AUTO_PIN_LINE];
+			spec->line_switch = 1;
+			cfg->line_outs++;
+		}
+		break;
+	case 2:
+		/* add line-in as clfe and mic as side */
+		if (cfg->input_pins[AUTO_PIN_LINE]) {
+			cfg->line_out_pins[2] = cfg->input_pins[AUTO_PIN_LINE];
+			spec->line_switch = 1;
+			cfg->line_outs++;
+		}
+		if (cfg->input_pins[AUTO_PIN_MIC]) {
+			cfg->line_out_pins[3] = cfg->input_pins[AUTO_PIN_MIC];
+			spec->mic_switch = 1;
+			cfg->line_outs++;
+		}
+		break;
+	case 1:
+		/* add line-in as surr and mic as clfe */
+		if (cfg->input_pins[AUTO_PIN_LINE]) {
+			cfg->line_out_pins[1] = cfg->input_pins[AUTO_PIN_LINE];
+			spec->line_switch = 1;
+			cfg->line_outs++;
+		}
+		if (cfg->input_pins[AUTO_PIN_MIC]) {
+			cfg->line_out_pins[2] = cfg->input_pins[AUTO_PIN_MIC];
+			spec->mic_switch = 1;
+			cfg->line_outs++;
+		}
+		break;
+	}
+
+	return 0;
+}
+
+/*
+ * XXX The line_out pin widget connection list may not be set to the
+ * desired DAC nid. This is the case on 927x where ports A and B can
+ * be routed to several DACs.
+ *
+ * This requires an analysis of the line-out/hp pin configuration
+ * to provide a best fit for pin/DAC configurations that are routable.
+ * For now, 927x DAC4 is not supported and 927x DAC1 output to ports
+ * A and B is not supported.
+ */
+/* fill in the dac_nids table from the parsed pin configuration */
+static int stac92xx_auto_fill_dac_nids(struct hda_codec *codec,
+				       const struct auto_pin_cfg *cfg)
+{
+	struct sigmatel_spec *spec = codec->spec;
+	hda_nid_t nid;
+	int i;
+
+	/* check the pins hardwired to audio widget */
+	for (i = 0; i < cfg->line_outs; i++) {
+		nid = cfg->line_out_pins[i];
+		spec->multiout.dac_nids[i] = snd_hda_codec_read(codec, nid, 0,
+					AC_VERB_GET_CONNECT_LIST, 0) & 0xff;
+	}
+
+	spec->multiout.num_dacs = cfg->line_outs;
+
+	return 0;
+}
+
+/* create volume control/switch for the given prefx type */
+static int create_controls(struct sigmatel_spec *spec, const char *pfx, hda_nid_t nid, int chs)
+{
+	char name[32];
+	int err;
+
+	sprintf(name, "%s Playback Volume", pfx);
+	err = stac92xx_add_control(spec, STAC_CTL_WIDGET_VOL, name,
+				   HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
+	if (err < 0)
+		return err;
+	sprintf(name, "%s Playback Switch", pfx);
+	err = stac92xx_add_control(spec, STAC_CTL_WIDGET_MUTE, name,
+				   HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
+	if (err < 0)
+		return err;
+	return 0;
+}
+
+/* add playback controls from the parsed DAC table */
+static int stac92xx_auto_create_multi_out_ctls(struct sigmatel_spec *spec,
+					       const struct auto_pin_cfg *cfg)
+{
+	static const char *chname[4] = {
+		"Front", "Surround", NULL /*CLFE*/, "Side"
+	};
+	hda_nid_t nid;
+	int i, err;
+
+	for (i = 0; i < cfg->line_outs; i++) {
+		if (!spec->multiout.dac_nids[i])
+			continue;
+
+		nid = spec->multiout.dac_nids[i];
+
+		if (i == 2) {
+			/* Center/LFE */
+			err = create_controls(spec, "Center", nid, 1);
+			if (err < 0)
+				return err;
+			err = create_controls(spec, "LFE", nid, 2);
+			if (err < 0)
+				return err;
+		} else {
+			err = create_controls(spec, chname[i], nid, 3);
+			if (err < 0)
+				return err;
+		}
+	}
+
+	if (spec->line_switch)
+		if ((err = stac92xx_add_control(spec, STAC_CTL_WIDGET_IO_SWITCH, "Line In as Output Switch", cfg->input_pins[AUTO_PIN_LINE] << 8)) < 0)
+			return err;
+
+	if (spec->mic_switch)
+		if ((err = stac92xx_add_control(spec, STAC_CTL_WIDGET_IO_SWITCH, "Mic as Output Switch", (cfg->input_pins[AUTO_PIN_MIC] << 8) | 1)) < 0)
+			return err;
+
+	return 0;
+}
+
+static int check_in_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid)
+{
+	int i;
+
+	for (i = 0; i < spec->multiout.num_dacs; i++) {
+		if (spec->multiout.dac_nids[i] == nid)
+			return 1;
+	}
+	if (spec->multiout.hp_nid == nid)
+		return 1;
+	return 0;
+}
+
+static int add_spec_dacs(struct sigmatel_spec *spec, hda_nid_t nid)
+{
+	if (!spec->multiout.hp_nid)
+		spec->multiout.hp_nid = nid;
+	else if (spec->multiout.num_dacs > 4) {
+		printk(KERN_WARNING "stac92xx: No space for DAC 0x%x\n", nid);
+		return 1;
+	} else {
+		spec->multiout.dac_nids[spec->multiout.num_dacs] = nid;
+		spec->multiout.num_dacs++;
+	}
+	return 0;
+}
+
+/* add playback controls for Speaker and HP outputs */
+static int stac92xx_auto_create_hp_ctls(struct hda_codec *codec,
+					struct auto_pin_cfg *cfg)
+{
+	struct sigmatel_spec *spec = codec->spec;
+	hda_nid_t nid;
+	int i, old_num_dacs, err;
+
+	old_num_dacs = spec->multiout.num_dacs;
+	for (i = 0; i < cfg->hp_outs; i++) {
+		unsigned int wid_caps = get_wcaps(codec, cfg->hp_pins[i]);
+		if (wid_caps & AC_WCAP_UNSOL_CAP)
+			spec->hp_detect = 1;
+		nid = snd_hda_codec_read(codec, cfg->hp_pins[i], 0,
+					 AC_VERB_GET_CONNECT_LIST, 0) & 0xff;
+		if (check_in_dac_nids(spec, nid))
+			nid = 0;
+		if (! nid)
+			continue;
+		add_spec_dacs(spec, nid);
+	}
+	for (i = 0; i < cfg->speaker_outs; i++) {
+		nid = snd_hda_codec_read(codec, cfg->speaker_pins[0], 0,
+					 AC_VERB_GET_CONNECT_LIST, 0) & 0xff;
+		if (check_in_dac_nids(spec, nid))
+			nid = 0;
+		if (check_in_dac_nids(spec, nid))
+			nid = 0;
+		if (! nid)
+			continue;
+		add_spec_dacs(spec, nid);
+	}
+
+	for (i = old_num_dacs; i < spec->multiout.num_dacs; i++) {
+		static const char *pfxs[] = {
+			"Speaker", "External Speaker", "Speaker2",
+		};
+		err = create_controls(spec, pfxs[i - old_num_dacs],
+				      spec->multiout.dac_nids[i], 3);
+		if (err < 0)
+			return err;
+	}
+	if (spec->multiout.hp_nid) {
+		const char *pfx;
+		if (old_num_dacs == spec->multiout.num_dacs)
+			pfx = "Master";
+		else
+			pfx = "Headphone";
+		err = create_controls(spec, pfx, spec->multiout.hp_nid, 3);
+		if (err < 0)
+			return err;
+	}
+
+	return 0;
+}
+
+/* labels for dmic mux inputs */
+static const char *stac92xx_dmic_labels[5] = {
+	"Analog Inputs", "Digital Mic 1", "Digital Mic 2",
+	"Digital Mic 3", "Digital Mic 4"
+};
+
+/* create playback/capture controls for input pins on dmic capable codecs */
+static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec,
+						const struct auto_pin_cfg *cfg)
+{
+	struct sigmatel_spec *spec = codec->spec;
+	struct hda_input_mux *dimux = &spec->private_dimux;
+	hda_nid_t con_lst[HDA_MAX_NUM_INPUTS];
+	int i, j;
+
+	dimux->items[dimux->num_items].label = stac92xx_dmic_labels[0];
+	dimux->items[dimux->num_items].index = 0;
+	dimux->num_items++;
+
+	for (i = 0; i < spec->num_dmics; i++) {
+		int index;
+		int num_cons;
+		unsigned int def_conf;
+
+		def_conf = snd_hda_codec_read(codec,
+					      spec->dmic_nids[i],
+					      0,
+					      AC_VERB_GET_CONFIG_DEFAULT,
+					      0);
+		if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE)
+			continue;
+
+		num_cons = snd_hda_get_connections(codec,
+				spec->dmux_nid,
+				con_lst,
+				HDA_MAX_NUM_INPUTS);
+		for (j = 0; j < num_cons; j++)
+			if (con_lst[j] == spec->dmic_nids[i]) {
+				index = j;
+				goto found;
+			}
+		continue;
+found:
+		dimux->items[dimux->num_items].label =
+			stac92xx_dmic_labels[dimux->num_items];
+		dimux->items[dimux->num_items].index = index;
+		dimux->num_items++;
+	}
+
+	return 0;
+}
+
+/* create playback/capture controls for input pins */
+static int stac92xx_auto_create_analog_input_ctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg)
+{
+	struct sigmatel_spec *spec = codec->spec;
+	struct hda_input_mux *imux = &spec->private_imux;
+	hda_nid_t con_lst[HDA_MAX_NUM_INPUTS];
+	int i, j, k;
+
+	for (i = 0; i < AUTO_PIN_LAST; i++) {
+		int index;
+
+		if (!cfg->input_pins[i])
+			continue;
+		index = -1;
+		for (j = 0; j < spec->num_muxes; j++) {
+			int num_cons;
+			num_cons = snd_hda_get_connections(codec,
+							   spec->mux_nids[j],
+							   con_lst,
+							   HDA_MAX_NUM_INPUTS);
+			for (k = 0; k < num_cons; k++)
+				if (con_lst[k] == cfg->input_pins[i]) {
+					index = k;
+					goto found;
+				}
+		}
+		continue;
+	found:
+		imux->items[imux->num_items].label = auto_pin_cfg_labels[i];
+		imux->items[imux->num_items].index = index;
+		imux->num_items++;
+	}
+
+	if (imux->num_items == 1) {
+		/*
+		 * Set the current input for the muxes.
+		 * The STAC9221 has two input muxes with identical source
+		 * NID lists.  Hopefully this won't get confused.
+		 */
+		for (i = 0; i < spec->num_muxes; i++) {
+			snd_hda_codec_write(codec, spec->mux_nids[i], 0,
+					    AC_VERB_SET_CONNECT_SEL,
+					    imux->items[0].index);
+		}
+	}
+
+	return 0;
+}
+
+static void stac92xx_auto_init_multi_out(struct hda_codec *codec)
+{
+	struct sigmatel_spec *spec = codec->spec;
+	int i;
+
+	for (i = 0; i < spec->autocfg.line_outs; i++) {
+		hda_nid_t nid = spec->autocfg.line_out_pins[i];
+		stac92xx_auto_set_pinctl(codec, nid, AC_PINCTL_OUT_EN);
+	}
+}
+
+static void stac92xx_auto_init_hp_out(struct hda_codec *codec)
+{
+	struct sigmatel_spec *spec = codec->spec;
+	int i;
+
+	for (i = 0; i < spec->autocfg.hp_outs; i++) {
+		hda_nid_t pin;
+		pin = spec->autocfg.hp_pins[i];
+		if (pin) /* connect to front */
+			stac92xx_auto_set_pinctl(codec, pin, AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN);
+	}
+	for (i = 0; i < spec->autocfg.speaker_outs; i++) {
+		hda_nid_t pin;
+		pin = spec->autocfg.speaker_pins[i];
+		if (pin) /* connect to front */
+			stac92xx_auto_set_pinctl(codec, pin, AC_PINCTL_OUT_EN);
+	}
+}
+
+static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out, hda_nid_t dig_in)
+{
+	struct sigmatel_spec *spec = codec->spec;
+	int err;
+
+	if ((err = snd_hda_parse_pin_def_config(codec,
+						&spec->autocfg,
+						spec->dmic_nids)) < 0)
+		return err;
+	if (! spec->autocfg.line_outs)
+		return 0; /* can't find valid pin config */
+
+	if ((err = stac92xx_add_dyn_out_pins(codec, &spec->autocfg)) < 0)
+		return err;
+	if (spec->multiout.num_dacs == 0)
+		if ((err = stac92xx_auto_fill_dac_nids(codec, &spec->autocfg)) < 0)
+			return err;
+
+	if ((err = stac92xx_auto_create_multi_out_ctls(spec, &spec->autocfg)) < 0 ||
+	    (err = stac92xx_auto_create_hp_ctls(codec, &spec->autocfg)) < 0 ||
+	    (err = stac92xx_auto_create_analog_input_ctls(codec, &spec->autocfg)) < 0)
+		return err;
+
+	if (spec->num_dmics > 0)
+		if ((err = stac92xx_auto_create_dmic_input_ctls(codec,
+						&spec->autocfg)) < 0)
+			return err;
+
+	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
+	if (spec->multiout.max_channels > 2)
+		spec->surr_switch = 1;
+
+	if (spec->autocfg.dig_out_pin)
+		spec->multiout.dig_out_nid = dig_out;
+	if (spec->autocfg.dig_in_pin)
+		spec->dig_in_nid = dig_in;
+
+	if (spec->kctl_alloc)
+		spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+
+	spec->input_mux = &spec->private_imux;
+	spec->dinput_mux = &spec->private_dimux;
+
+	return 1;
+}
+
+/* add playback controls for HP output */
+static int stac9200_auto_create_hp_ctls(struct hda_codec *codec,
+					struct auto_pin_cfg *cfg)
+{
+	struct sigmatel_spec *spec = codec->spec;
+	hda_nid_t pin = cfg->hp_pins[0];
+	unsigned int wid_caps;
+
+	if (! pin)
+		return 0;
+
+	wid_caps = get_wcaps(codec, pin);
+	if (wid_caps & AC_WCAP_UNSOL_CAP)
+		spec->hp_detect = 1;
+
+	return 0;
+}
+
+/* add playback controls for LFE output */
+static int stac9200_auto_create_lfe_ctls(struct hda_codec *codec,
+					struct auto_pin_cfg *cfg)
+{
+	struct sigmatel_spec *spec = codec->spec;
+	int err;
+	hda_nid_t lfe_pin = 0x0;
+	int i;
+
+	/*
+	 * search speaker outs and line outs for a mono speaker pin
+	 * with an amp.  If one is found, add LFE controls
+	 * for it.
+	 */
+	for (i = 0; i < spec->autocfg.speaker_outs && lfe_pin == 0x0; i++) {
+		hda_nid_t pin = spec->autocfg.speaker_pins[i];
+		unsigned long wcaps = get_wcaps(codec, pin);
+		wcaps &= (AC_WCAP_STEREO | AC_WCAP_OUT_AMP);
+		if (wcaps == AC_WCAP_OUT_AMP)
+			/* found a mono speaker with an amp, must be lfe */
+			lfe_pin = pin;
+	}
+
+	/* if speaker_outs is 0, then speakers may be in line_outs */
+	if (lfe_pin == 0 && spec->autocfg.speaker_outs == 0) {
+		for (i = 0; i < spec->autocfg.line_outs && lfe_pin == 0x0; i++) {
+			hda_nid_t pin = spec->autocfg.line_out_pins[i];
+			unsigned long cfg;
+			cfg = snd_hda_codec_read(codec, pin, 0,
+						 AC_VERB_GET_CONFIG_DEFAULT,
+						 0x00);
+			if (get_defcfg_device(cfg) == AC_JACK_SPEAKER) {
+				unsigned long wcaps = get_wcaps(codec, pin);
+				wcaps &= (AC_WCAP_STEREO | AC_WCAP_OUT_AMP);
+				if (wcaps == AC_WCAP_OUT_AMP)
+					/* found a mono speaker with an amp,
+					   must be lfe */
+					lfe_pin = pin;
+			}
+		}
+	}
+
+	if (lfe_pin) {
+		err = create_controls(spec, "LFE", lfe_pin, 1);
+		if (err < 0)
+			return err;
+	}
+
+	return 0;
+}
+
+static int stac9200_parse_auto_config(struct hda_codec *codec)
+{
+	struct sigmatel_spec *spec = codec->spec;
+	int err;
+
+	if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL)) < 0)
+		return err;
+
+	if ((err = stac92xx_auto_create_analog_input_ctls(codec, &spec->autocfg)) < 0)
+		return err;
+
+	if ((err = stac9200_auto_create_hp_ctls(codec, &spec->autocfg)) < 0)
+		return err;
+
+	if ((err = stac9200_auto_create_lfe_ctls(codec, &spec->autocfg)) < 0)
+		return err;
+
+	if (spec->autocfg.dig_out_pin)
+		spec->multiout.dig_out_nid = 0x05;
+	if (spec->autocfg.dig_in_pin)
+		spec->dig_in_nid = 0x04;
+
+	if (spec->kctl_alloc)
+		spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+
+	spec->input_mux = &spec->private_imux;
+	spec->dinput_mux = &spec->private_dimux;
+
+	return 1;
+}
+
+/*
+ * Early 2006 Intel Macintoshes with STAC9220X5 codecs seem to have a
+ * funky external mute control using GPIO pins.
+ */
+
+static void stac922x_gpio_mute(struct hda_codec *codec, int pin, int muted)
+{
+	unsigned int gpiostate, gpiomask, gpiodir;
+
+	gpiostate = snd_hda_codec_read(codec, codec->afg, 0,
+				       AC_VERB_GET_GPIO_DATA, 0);
+
+	if (!muted)
+		gpiostate |= (1 << pin);
+	else
+		gpiostate &= ~(1 << pin);
+
+	gpiomask = snd_hda_codec_read(codec, codec->afg, 0,
+				      AC_VERB_GET_GPIO_MASK, 0);
+	gpiomask |= (1 << pin);
+
+	gpiodir = snd_hda_codec_read(codec, codec->afg, 0,
+				     AC_VERB_GET_GPIO_DIRECTION, 0);
+	gpiodir |= (1 << pin);
+
+	/* AppleHDA seems to do this -- WTF is this verb?? */
+	snd_hda_codec_write(codec, codec->afg, 0, 0x7e7, 0);
+
+	snd_hda_codec_write(codec, codec->afg, 0,
+			    AC_VERB_SET_GPIO_MASK, gpiomask);
+	snd_hda_codec_write(codec, codec->afg, 0,
+			    AC_VERB_SET_GPIO_DIRECTION, gpiodir);
+
+	msleep(1);
+
+	snd_hda_codec_write(codec, codec->afg, 0,
+			    AC_VERB_SET_GPIO_DATA, gpiostate);
+}
+
+static void enable_pin_detect(struct hda_codec *codec, hda_nid_t nid,
+			      unsigned int event)
+{
+	if (get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP)
+		snd_hda_codec_write(codec, nid, 0,
+				    AC_VERB_SET_UNSOLICITED_ENABLE,
+				    (AC_USRSP_EN | event));
+}
+
+static int stac92xx_init(struct hda_codec *codec)
+{
+	struct sigmatel_spec *spec = codec->spec;
+	struct auto_pin_cfg *cfg = &spec->autocfg;
+	int i;
+
+	snd_hda_sequence_write(codec, spec->init);
+
+	/* set up pins */
+	if (spec->hp_detect) {
+		/* Enable unsolicited responses on the HP widget */
+		for (i = 0; i < cfg->hp_outs; i++)
+			enable_pin_detect(codec, cfg->hp_pins[i],
+					  STAC_HP_EVENT);
+		stac92xx_auto_init_hp_out(codec);
+		/* fake event to set up pins */
+		codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26);
+	} else {
+		stac92xx_auto_init_multi_out(codec);
+		stac92xx_auto_init_hp_out(codec);
+	}
+	for (i = 0; i < AUTO_PIN_LAST; i++) {
+		hda_nid_t nid = cfg->input_pins[i];
+		if (nid) {
+			unsigned int pinctl = AC_PINCTL_IN_EN;
+			if (i == AUTO_PIN_MIC || i == AUTO_PIN_FRONT_MIC)
+				pinctl |= stac92xx_get_vref(codec, nid);
+			stac92xx_auto_set_pinctl(codec, nid, pinctl);
+		}
+	}
+	if (spec->num_dmics > 0)
+		for (i = 0; i < spec->num_dmics; i++)
+			stac92xx_auto_set_pinctl(codec, spec->dmic_nids[i],
+						 AC_PINCTL_IN_EN);
+
+	if (cfg->dig_out_pin)
+		stac92xx_auto_set_pinctl(codec, cfg->dig_out_pin,
+					 AC_PINCTL_OUT_EN);
+	if (cfg->dig_in_pin)
+		stac92xx_auto_set_pinctl(codec, cfg->dig_in_pin,
+					 AC_PINCTL_IN_EN);
+
+	if (spec->gpio_mute) {
+		stac922x_gpio_mute(codec, 0, 0);
+		stac922x_gpio_mute(codec, 1, 0);
+	}
+
+	return 0;
+}
+
+static void stac92xx_free(struct hda_codec *codec)
+{
+	struct sigmatel_spec *spec = codec->spec;
+	int i;
+
+	if (! spec)
+		return;
+
+	if (spec->kctl_alloc) {
+		for (i = 0; i < spec->num_kctl_used; i++)
+			kfree(spec->kctl_alloc[i].name);
+		kfree(spec->kctl_alloc);
+	}
+
+	if (spec->bios_pin_configs)
+		kfree(spec->bios_pin_configs);
+
+	kfree(spec);
+}
+
+static void stac92xx_set_pinctl(struct hda_codec *codec, hda_nid_t nid,
+				unsigned int flag)
+{
+	unsigned int pin_ctl = snd_hda_codec_read(codec, nid,
+			0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00);
+	if (flag == AC_PINCTL_OUT_EN && (pin_ctl & AC_PINCTL_IN_EN))
+		return;
+	snd_hda_codec_write(codec, nid, 0,
+			AC_VERB_SET_PIN_WIDGET_CONTROL,
+			pin_ctl | flag);
+}
+
+static void stac92xx_reset_pinctl(struct hda_codec *codec, hda_nid_t nid,
+				  unsigned int flag)
+{
+	unsigned int pin_ctl = snd_hda_codec_read(codec, nid,
+			0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00);
+	snd_hda_codec_write(codec, nid, 0,
+			AC_VERB_SET_PIN_WIDGET_CONTROL,
+			pin_ctl & ~flag);
+}
+
+static int get_pin_presence(struct hda_codec *codec, hda_nid_t nid)
+{
+	if (!nid)
+		return 0;
+	if (snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_SENSE, 0x00)
+	    & (1 << 31))
+		return 1;
+	return 0;
+}
+
+static void stac92xx_hp_detect(struct hda_codec *codec, unsigned int res)
+{
+	struct sigmatel_spec *spec = codec->spec;
+	struct auto_pin_cfg *cfg = &spec->autocfg;
+	int i, presence;
+
+	presence = 0;
+	for (i = 0; i < cfg->hp_outs; i++) {
+		presence = get_pin_presence(codec, cfg->hp_pins[i]);
+		if (presence)
+			break;
+	}
+
+	if (presence) {
+		/* disable lineouts, enable hp */
+		for (i = 0; i < cfg->line_outs; i++)
+			stac92xx_reset_pinctl(codec, cfg->line_out_pins[i],
+						AC_PINCTL_OUT_EN);
+		for (i = 0; i < cfg->speaker_outs; i++)
+			stac92xx_reset_pinctl(codec, cfg->speaker_pins[i],
+						AC_PINCTL_OUT_EN);
+	} else {
+		/* enable lineouts, disable hp */
+		for (i = 0; i < cfg->line_outs; i++)
+			stac92xx_set_pinctl(codec, cfg->line_out_pins[i],
+						AC_PINCTL_OUT_EN);
+		for (i = 0; i < cfg->speaker_outs; i++)
+			stac92xx_set_pinctl(codec, cfg->speaker_pins[i],
+						AC_PINCTL_OUT_EN);
+	}
+} 
+
+static void stac92xx_unsol_event(struct hda_codec *codec, unsigned int res)
+{
+	switch (res >> 26) {
+	case STAC_HP_EVENT:
+		stac92xx_hp_detect(codec, res);
+		break;
+	}
+}
+
+#ifdef CONFIG_PM
+static int stac92xx_resume(struct hda_codec *codec)
+{
+	struct sigmatel_spec *spec = codec->spec;
+	int i;
+
+	stac92xx_init(codec);
+	stac92xx_set_config_regs(codec);
+	for (i = 0; i < spec->num_mixers; i++)
+		snd_hda_resume_ctls(codec, spec->mixers[i]);
+	if (spec->multiout.dig_out_nid)
+		snd_hda_resume_spdif_out(codec);
+	if (spec->dig_in_nid)
+		snd_hda_resume_spdif_in(codec);
+
+	return 0;
+}
+#endif
+
+static struct hda_codec_ops stac92xx_patch_ops = {
+	.build_controls = stac92xx_build_controls,
+	.build_pcms = stac92xx_build_pcms,
+	.init = stac92xx_init,
+	.free = stac92xx_free,
+	.unsol_event = stac92xx_unsol_event,
+#ifdef CONFIG_PM
+	.resume = stac92xx_resume,
+#endif
+};
+
+static int patch_stac9200(struct hda_codec *codec)
+{
+	struct sigmatel_spec *spec;
+	int err;
+
+	spec  = kzalloc(sizeof(*spec), GFP_KERNEL);
+	if (spec == NULL)
+		return -ENOMEM;
+
+	codec->spec = spec;
+	spec->num_pins = 8;
+	spec->pin_nids = stac9200_pin_nids;
+	spec->board_config = snd_hda_check_board_config(codec, STAC_9200_MODELS,
+							stac9200_models,
+							stac9200_cfg_tbl);
+	if (spec->board_config < 0) {
+		snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC9200, using BIOS defaults\n");
+		err = stac92xx_save_bios_config_regs(codec);
+		if (err < 0) {
+			stac92xx_free(codec);
+			return err;
+		}
+		spec->pin_configs = spec->bios_pin_configs;
+	} else {
+		spec->pin_configs = stac9200_brd_tbl[spec->board_config];
+		stac92xx_set_config_regs(codec);
+	}
+
+	spec->multiout.max_channels = 2;
+	spec->multiout.num_dacs = 1;
+	spec->multiout.dac_nids = stac9200_dac_nids;
+	spec->adc_nids = stac9200_adc_nids;
+	spec->mux_nids = stac9200_mux_nids;
+	spec->num_muxes = 1;
+	spec->num_dmics = 0;
+
+	spec->init = stac9200_core_init;
+	spec->mixer = stac9200_mixer;
+
+	err = stac9200_parse_auto_config(codec);
+	if (err < 0) {
+		stac92xx_free(codec);
+		return err;
+	}
+
+	codec->patch_ops = stac92xx_patch_ops;
+
+	return 0;
+}
+
+static int patch_stac925x(struct hda_codec *codec)
+{
+	struct sigmatel_spec *spec;
+	int err;
+
+	spec  = kzalloc(sizeof(*spec), GFP_KERNEL);
+	if (spec == NULL)
+		return -ENOMEM;
+
+	codec->spec = spec;
+	spec->num_pins = 8;
+	spec->pin_nids = stac925x_pin_nids;
+	spec->board_config = snd_hda_check_board_config(codec, STAC_925x_MODELS,
+							stac925x_models,
+							stac925x_cfg_tbl);
+	if (spec->board_config < 0) {
+		snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC925x, using BIOS defaults\n");
+		err = stac92xx_save_bios_config_regs(codec);
+		if (err < 0) {
+			stac92xx_free(codec);
+			return err;
+		}
+		spec->pin_configs = spec->bios_pin_configs;
+	} else if (stac925x_brd_tbl[spec->board_config] != NULL){
+		spec->pin_configs = stac925x_brd_tbl[spec->board_config];
+		stac92xx_set_config_regs(codec);
+	}
+
+	spec->multiout.max_channels = 2;
+	spec->multiout.num_dacs = 1;
+	spec->multiout.dac_nids = stac925x_dac_nids;
+	spec->adc_nids = stac925x_adc_nids;
+	spec->mux_nids = stac925x_mux_nids;
+	spec->num_muxes = 1;
+	spec->num_dmics = 0;
+
+	spec->init = stac925x_core_init;
+	spec->mixer = stac925x_mixer;
+
+	err = stac92xx_parse_auto_config(codec, 0x8, 0x7);
+	if (err < 0) {
+		stac92xx_free(codec);
+		return err;
+	}
+
+	codec->patch_ops = stac92xx_patch_ops;
+
+	return 0;
+}
+
+static int patch_stac922x(struct hda_codec *codec)
+{
+	struct sigmatel_spec *spec;
+	int err;
+
+	spec  = kzalloc(sizeof(*spec), GFP_KERNEL);
+	if (spec == NULL)
+		return -ENOMEM;
+
+	codec->spec = spec;
+	spec->num_pins = 10;
+	spec->pin_nids = stac922x_pin_nids;
+	spec->board_config = snd_hda_check_board_config(codec, STAC_922X_MODELS,
+							stac922x_models,
+							stac922x_cfg_tbl);
+	if (spec->board_config < 0) {
+		snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC922x, "
+			"using BIOS defaults\n");
+		err = stac92xx_save_bios_config_regs(codec);
+		if (err < 0) {
+			stac92xx_free(codec);
+			return err;
+		}
+		spec->pin_configs = spec->bios_pin_configs;
+	} else if (stac922x_brd_tbl[spec->board_config] != NULL) {
+		spec->pin_configs = stac922x_brd_tbl[spec->board_config];
+		stac92xx_set_config_regs(codec);
+	}
+
+	spec->adc_nids = stac922x_adc_nids;
+	spec->mux_nids = stac922x_mux_nids;
+	spec->num_muxes = 2;
+	spec->num_dmics = 0;
+
+	spec->init = stac922x_core_init;
+	spec->mixer = stac922x_mixer;
+
+	spec->multiout.dac_nids = spec->dac_nids;
+	
+	err = stac92xx_parse_auto_config(codec, 0x08, 0x09);
+	if (err < 0) {
+		stac92xx_free(codec);
+		return err;
+	}
+
+	if (spec->board_config == STAC_MACMINI)
+		spec->gpio_mute = 1;
+
+	codec->patch_ops = stac92xx_patch_ops;
+
+	return 0;
+}
+
+static int patch_stac927x(struct hda_codec *codec)
+{
+	struct sigmatel_spec *spec;
+	int err;
+
+	spec  = kzalloc(sizeof(*spec), GFP_KERNEL);
+	if (spec == NULL)
+		return -ENOMEM;
+
+	codec->spec = spec;
+	spec->num_pins = 14;
+	spec->pin_nids = stac927x_pin_nids;
+	spec->board_config = snd_hda_check_board_config(codec, STAC_927X_MODELS,
+							stac927x_models,
+							stac927x_cfg_tbl);
+	if (spec->board_config < 0) {
+                snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC927x, using BIOS defaults\n");
+		err = stac92xx_save_bios_config_regs(codec);
+		if (err < 0) {
+			stac92xx_free(codec);
+			return err;
+		}
+		spec->pin_configs = spec->bios_pin_configs;
+	} else if (stac927x_brd_tbl[spec->board_config] != NULL) {
+		spec->pin_configs = stac927x_brd_tbl[spec->board_config];
+		stac92xx_set_config_regs(codec);
+	}
+
+	switch (spec->board_config) {
+	case STAC_D965_3ST:
+		spec->adc_nids = stac927x_adc_nids;
+		spec->mux_nids = stac927x_mux_nids;
+		spec->num_muxes = 3;
+		spec->num_dmics = 0;
+		spec->init = d965_core_init;
+		spec->mixer = stac9227_mixer;
+		break;
+	case STAC_D965_5ST:
+		spec->adc_nids = stac927x_adc_nids;
+		spec->mux_nids = stac927x_mux_nids;
+		spec->num_muxes = 3;
+		spec->num_dmics = 0;
+		spec->init = d965_core_init;
+		spec->mixer = stac9227_mixer;
+		break;
+	default:
+		spec->adc_nids = stac927x_adc_nids;
+		spec->mux_nids = stac927x_mux_nids;
+		spec->num_muxes = 3;
+		spec->num_dmics = 0;
+		spec->init = stac927x_core_init;
+		spec->mixer = stac927x_mixer;
+	}
+
+	spec->multiout.dac_nids = spec->dac_nids;
+
+	err = stac92xx_parse_auto_config(codec, 0x1e, 0x20);
+	if (err < 0) {
+		stac92xx_free(codec);
+		return err;
+	}
+
+	codec->patch_ops = stac92xx_patch_ops;
+
+	return 0;
+}
+
+static int patch_stac9205(struct hda_codec *codec)
+{
+	struct sigmatel_spec *spec;
+	int err;
+
+	spec  = kzalloc(sizeof(*spec), GFP_KERNEL);
+	if (spec == NULL)
+		return -ENOMEM;
+
+	codec->spec = spec;
+	spec->num_pins = 14;
+	spec->pin_nids = stac9205_pin_nids;
+	spec->board_config = snd_hda_check_board_config(codec, STAC_9205_MODELS,
+							stac9205_models,
+							stac9205_cfg_tbl);
+	if (spec->board_config < 0) {
+		snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC9205, using BIOS defaults\n");
+		err = stac92xx_save_bios_config_regs(codec);
+		if (err < 0) {
+			stac92xx_free(codec);
+			return err;
+		}
+		spec->pin_configs = spec->bios_pin_configs;
+	} else {
+		spec->pin_configs = stac9205_brd_tbl[spec->board_config];
+		stac92xx_set_config_regs(codec);
+	}
+
+	spec->adc_nids = stac9205_adc_nids;
+	spec->mux_nids = stac9205_mux_nids;
+	spec->num_muxes = 2;
+	spec->dmic_nids = stac9205_dmic_nids;
+	spec->num_dmics = 2;
+	spec->dmux_nid = 0x1d;
+
+	spec->init = stac9205_core_init;
+	spec->mixer = stac9205_mixer;
+
+	spec->multiout.dac_nids = spec->dac_nids;
+
+	/* Configure GPIO0 as EAPD output */
+	snd_hda_codec_write(codec, codec->afg, 0,
+			    AC_VERB_SET_GPIO_DIRECTION, 0x00000001);
+	/* Configure GPIO0 as CMOS */
+	snd_hda_codec_write(codec, codec->afg, 0, 0x7e7, 0x00000000);
+	/* Assert GPIO0 high */
+	snd_hda_codec_write(codec, codec->afg, 0,
+			    AC_VERB_SET_GPIO_DATA, 0x00000001);
+	/* Enable GPIO0 */
+	snd_hda_codec_write(codec, codec->afg, 0,
+			    AC_VERB_SET_GPIO_MASK, 0x00000001);
+
+	err = stac92xx_parse_auto_config(codec, 0x1f, 0x20);
+	if (err < 0) {
+		stac92xx_free(codec);
+		return err;
+	}
+
+	codec->patch_ops = stac92xx_patch_ops;
+
+	return 0;
+}
+
+/*
+ * STAC9872 hack
+ */
+
+/* static config for Sony VAIO FE550G and Sony VAIO AR */
+static hda_nid_t vaio_dacs[] = { 0x2 };
+#define VAIO_HP_DAC	0x5
+static hda_nid_t vaio_adcs[] = { 0x8 /*,0x6*/ };
+static hda_nid_t vaio_mux_nids[] = { 0x15 };
+
+static struct hda_input_mux vaio_mux = {
+	.num_items = 2,
+	.items = {
+		/* { "HP", 0x0 }, */
+		{ "Line", 0x1 },
+		{ "Mic", 0x2 },
+		{ "PCM", 0x3 },
+	}
+};
+
+static struct hda_verb vaio_init[] = {
+	{0x0a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, /* HP <- 0x2 */
+	{0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, /* Speaker <- 0x5 */
+	{0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Mic? (<- 0x2) */
+	{0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, /* CD */
+	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Mic? */
+	{0x15, AC_VERB_SET_CONNECT_SEL, 0x2}, /* mic-sel: 0a,0d,14,02 */
+	{0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* HP */
+	{0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* Speaker */
+	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* capture sw/vol -> 0x8 */
+	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, /* CD-in -> 0x6 */
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Mic-in -> 0x9 */
+	{0}
+};
+
+static struct hda_verb vaio_ar_init[] = {
+	{0x0a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, /* HP <- 0x2 */
+	{0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, /* Speaker <- 0x5 */
+	{0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Mic? (<- 0x2) */
+	{0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, /* CD */
+/*	{0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },*/ /* Optical Out */
+	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Mic? */
+	{0x15, AC_VERB_SET_CONNECT_SEL, 0x2}, /* mic-sel: 0a,0d,14,02 */
+	{0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* HP */
+	{0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* Speaker */
+/*	{0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},*/ /* Optical Out */
+	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* capture sw/vol -> 0x8 */
+	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, /* CD-in -> 0x6 */
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Mic-in -> 0x9 */
+	{0}
+};
+
+/* bind volumes of both NID 0x02 and 0x05 */
+static int vaio_master_vol_put(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	long *valp = ucontrol->value.integer.value;
+	int change;
+
+	change = snd_hda_codec_amp_update(codec, 0x02, 0, HDA_OUTPUT, 0,
+					  0x7f, valp[0] & 0x7f);
+	change |= snd_hda_codec_amp_update(codec, 0x02, 1, HDA_OUTPUT, 0,
+					   0x7f, valp[1] & 0x7f);
+	snd_hda_codec_amp_update(codec, 0x05, 0, HDA_OUTPUT, 0,
+				 0x7f, valp[0] & 0x7f);
+	snd_hda_codec_amp_update(codec, 0x05, 1, HDA_OUTPUT, 0,
+				 0x7f, valp[1] & 0x7f);
+	return change;
+}
+
+/* bind volumes of both NID 0x02 and 0x05 */
+static int vaio_master_sw_put(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	long *valp = ucontrol->value.integer.value;
+	int change;
+
+	change = snd_hda_codec_amp_update(codec, 0x02, 0, HDA_OUTPUT, 0,
+					  0x80, (valp[0] ? 0 : 0x80));
+	change |= snd_hda_codec_amp_update(codec, 0x02, 1, HDA_OUTPUT, 0,
+					   0x80, (valp[1] ? 0 : 0x80));
+	snd_hda_codec_amp_update(codec, 0x05, 0, HDA_OUTPUT, 0,
+				 0x80, (valp[0] ? 0 : 0x80));
+	snd_hda_codec_amp_update(codec, 0x05, 1, HDA_OUTPUT, 0,
+				 0x80, (valp[1] ? 0 : 0x80));
+	return change;
+}
+
+static struct snd_kcontrol_new vaio_mixer[] = {
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Master Playback Volume",
+		.info = snd_hda_mixer_amp_volume_info,
+		.get = snd_hda_mixer_amp_volume_get,
+		.put = vaio_master_vol_put,
+		.tlv = { .c = snd_hda_mixer_amp_tlv },
+		.private_value = HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT),
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Master Playback Switch",
+		.info = snd_hda_mixer_amp_switch_info,
+		.get = snd_hda_mixer_amp_switch_get,
+		.put = vaio_master_sw_put,
+		.private_value = HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT),
+	},
+	/* HDA_CODEC_VOLUME("CD Capture Volume", 0x07, 0, HDA_INPUT), */
+	HDA_CODEC_VOLUME("Capture Volume", 0x09, 0, HDA_INPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x09, 0, HDA_INPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Capture Source",
+		.count = 1,
+		.info = stac92xx_mux_enum_info,
+		.get = stac92xx_mux_enum_get,
+		.put = stac92xx_mux_enum_put,
+	},
+	{0}
+};
+
+static struct snd_kcontrol_new vaio_ar_mixer[] = {
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Master Playback Volume",
+		.info = snd_hda_mixer_amp_volume_info,
+		.get = snd_hda_mixer_amp_volume_get,
+		.put = vaio_master_vol_put,
+		.private_value = HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT),
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Master Playback Switch",
+		.info = snd_hda_mixer_amp_switch_info,
+		.get = snd_hda_mixer_amp_switch_get,
+		.put = vaio_master_sw_put,
+		.private_value = HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT),
+	},
+	/* HDA_CODEC_VOLUME("CD Capture Volume", 0x07, 0, HDA_INPUT), */
+	HDA_CODEC_VOLUME("Capture Volume", 0x09, 0, HDA_INPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x09, 0, HDA_INPUT),
+	/*HDA_CODEC_MUTE("Optical Out Switch", 0x10, 0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Optical Out Volume", 0x10, 0, HDA_OUTPUT),*/
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Capture Source",
+		.count = 1,
+		.info = stac92xx_mux_enum_info,
+		.get = stac92xx_mux_enum_get,
+		.put = stac92xx_mux_enum_put,
+	},
+	{0}
+};
+
+static struct hda_codec_ops stac9872_patch_ops = {
+	.build_controls = stac92xx_build_controls,
+	.build_pcms = stac92xx_build_pcms,
+	.init = stac92xx_init,
+	.free = stac92xx_free,
+#ifdef CONFIG_PM
+	.resume = stac92xx_resume,
+#endif
+};
+
+enum { /* FE and SZ series. id=0x83847661 and subsys=0x104D0700 or 104D1000. */
+       CXD9872RD_VAIO,
+       /* Unknown. id=0x83847662 and subsys=0x104D1200 or 104D1000. */
+       STAC9872AK_VAIO, 
+       /* Unknown. id=0x83847661 and subsys=0x104D1200. */
+       STAC9872K_VAIO,
+       /* AR Series. id=0x83847664 and subsys=104D1300 */
+       CXD9872AKD_VAIO,
+       STAC_9872_MODELS,
+};
+
+static const char *stac9872_models[STAC_9872_MODELS] = {
+	[CXD9872RD_VAIO]	= "vaio",
+	[CXD9872AKD_VAIO]	= "vaio-ar",
+};
+
+static struct snd_pci_quirk stac9872_cfg_tbl[] = {
+	SND_PCI_QUIRK(0x104d, 0x81e6, "Sony VAIO F/S", CXD9872RD_VAIO),
+	SND_PCI_QUIRK(0x104d, 0x81ef, "Sony VAIO F/S", CXD9872RD_VAIO),
+	SND_PCI_QUIRK(0x104d, 0x81fd, "Sony VAIO AR", CXD9872AKD_VAIO),
+	{0}
+};
+
+static int patch_stac9872(struct hda_codec *codec)
+{
+	struct sigmatel_spec *spec;
+	int board_config;
+
+	board_config = snd_hda_check_board_config(codec, STAC_9872_MODELS,
+						  stac9872_models,
+						  stac9872_cfg_tbl);
+	if (board_config < 0)
+		/* unknown config, let generic-parser do its job... */
+		return snd_hda_parse_generic_codec(codec);
+	
+	spec  = kzalloc(sizeof(*spec), GFP_KERNEL);
+	if (spec == NULL)
+		return -ENOMEM;
+
+	codec->spec = spec;
+	switch (board_config) {
+	case CXD9872RD_VAIO:
+	case STAC9872AK_VAIO:
+	case STAC9872K_VAIO:
+		spec->mixer = vaio_mixer;
+		spec->init = vaio_init;
+		spec->multiout.max_channels = 2;
+		spec->multiout.num_dacs = ARRAY_SIZE(vaio_dacs);
+		spec->multiout.dac_nids = vaio_dacs;
+		spec->multiout.hp_nid = VAIO_HP_DAC;
+		spec->num_adcs = ARRAY_SIZE(vaio_adcs);
+		spec->adc_nids = vaio_adcs;
+		spec->input_mux = &vaio_mux;
+		spec->mux_nids = vaio_mux_nids;
+		break;
+	
+	case CXD9872AKD_VAIO:
+		spec->mixer = vaio_ar_mixer;
+		spec->init = vaio_ar_init;
+		spec->multiout.max_channels = 2;
+		spec->multiout.num_dacs = ARRAY_SIZE(vaio_dacs);
+		spec->multiout.dac_nids = vaio_dacs;
+		spec->multiout.hp_nid = VAIO_HP_DAC;
+		spec->num_adcs = ARRAY_SIZE(vaio_adcs);
+		spec->adc_nids = vaio_adcs;
+		spec->input_mux = &vaio_mux;
+		spec->mux_nids = vaio_mux_nids;
+		break;
+	}
+
+	codec->patch_ops = stac9872_patch_ops;
+	return 0;
+}
+
+
+/*
+ * patch entries
+ */
+struct hda_codec_preset snd_hda_preset_sigmatel[] = {
+ 	{ .id = 0x83847690, .name = "STAC9200", .patch = patch_stac9200 },
+ 	{ .id = 0x83847882, .name = "STAC9220 A1", .patch = patch_stac922x },
+ 	{ .id = 0x83847680, .name = "STAC9221 A1", .patch = patch_stac922x },
+ 	{ .id = 0x83847880, .name = "STAC9220 A2", .patch = patch_stac922x },
+ 	{ .id = 0x83847681, .name = "STAC9220D/9223D A2", .patch = patch_stac922x },
+ 	{ .id = 0x83847682, .name = "STAC9221 A2", .patch = patch_stac922x },
+ 	{ .id = 0x83847683, .name = "STAC9221D A2", .patch = patch_stac922x },
+ 	{ .id = 0x83847618, .name = "STAC9227", .patch = patch_stac927x },
+ 	{ .id = 0x83847619, .name = "STAC9227", .patch = patch_stac927x },
+ 	{ .id = 0x83847616, .name = "STAC9228", .patch = patch_stac927x },
+ 	{ .id = 0x83847617, .name = "STAC9228", .patch = patch_stac927x },
+ 	{ .id = 0x83847614, .name = "STAC9229", .patch = patch_stac927x },
+ 	{ .id = 0x83847615, .name = "STAC9229", .patch = patch_stac927x },
+ 	{ .id = 0x83847620, .name = "STAC9274", .patch = patch_stac927x },
+ 	{ .id = 0x83847621, .name = "STAC9274D", .patch = patch_stac927x },
+ 	{ .id = 0x83847622, .name = "STAC9273X", .patch = patch_stac927x },
+ 	{ .id = 0x83847623, .name = "STAC9273D", .patch = patch_stac927x },
+ 	{ .id = 0x83847624, .name = "STAC9272X", .patch = patch_stac927x },
+ 	{ .id = 0x83847625, .name = "STAC9272D", .patch = patch_stac927x },
+ 	{ .id = 0x83847626, .name = "STAC9271X", .patch = patch_stac927x },
+ 	{ .id = 0x83847627, .name = "STAC9271D", .patch = patch_stac927x },
+ 	{ .id = 0x83847628, .name = "STAC9274X5NH", .patch = patch_stac927x },
+        { .id = 0x83847629, .name = "STAC9274D5NH", .patch = patch_stac927x },
+	{ .id = 0x83847632, .name = "STAC9202",  .patch = patch_stac925x },
+	{ .id = 0x83847633, .name = "STAC9202D", .patch = patch_stac925x },
+	{ .id = 0x83847634, .name = "STAC9250", .patch = patch_stac925x },
+	{ .id = 0x83847635, .name = "STAC9250D", .patch = patch_stac925x },
+	{ .id = 0x83847636, .name = "STAC9251", .patch = patch_stac925x },
+	{ .id = 0x83847637, .name = "STAC9250D", .patch = patch_stac925x },
+ 	/* The following does not take into account .id=0x83847661 when subsys =
+ 	 * 104D0C00 which is STAC9225s. Because of this, some SZ Notebooks are
+ 	 * currently not fully supported.
+ 	 */
+ 	{ .id = 0x83847661, .name = "CXD9872RD/K", .patch = patch_stac9872 },
+ 	{ .id = 0x83847662, .name = "STAC9872AK", .patch = patch_stac9872 },
+ 	{ .id = 0x83847664, .name = "CXD9872AKD", .patch = patch_stac9872 },
+ 	{ .id = 0x838476a0, .name = "STAC9205", .patch = patch_stac9205 },
+ 	{ .id = 0x838476a1, .name = "STAC9205D", .patch = patch_stac9205 },
+ 	{ .id = 0x838476a2, .name = "STAC9204", .patch = patch_stac9205 },
+ 	{ .id = 0x838476a3, .name = "STAC9204D", .patch = patch_stac9205 },
+ 	{ .id = 0x838476a4, .name = "STAC9255", .patch = patch_stac9205 },
+ 	{ .id = 0x838476a5, .name = "STAC9255D", .patch = patch_stac9205 },
+ 	{ .id = 0x838476a6, .name = "STAC9254", .patch = patch_stac9205 },
+ 	{ .id = 0x838476a7, .name = "STAC9254D", .patch = patch_stac9205 },
+	{0} /* terminator */
+};
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/ice1712.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/ice1712.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/ice1712.c	(revision 91)
@@ -0,0 +1,4976 @@
+/*
+ *   ALSA driver for ICEnsemble ICE1712 (Envy24)
+ *
+ *	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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */      
+
+/*
+  NOTES:
+  - spdif nonaudio consumer mode does not work (at least with my 
+    Sony STR-DB830)
+*/
+
+#define SNDRV_MAIN_OBJECT_FILE
+
+#include <sound/driver.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include <sound/mpu401.h>
+#include <sound/info.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+EXPORT_NO_SYMBOLS;
+MODULE_DESCRIPTION("ICEnsemble ICE1712 (Envy24)");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{Hoontech SoundTrack DSP 24},"
+		"{MidiMan M Audio,Delta 1010},"
+		"{MidiMan M Audio,Delta DiO 2496},"
+		"{MidiMan M Audio,Delta 66},"
+		"{MidiMan M Audio,Delta 44},"
+		"{MidiMan M Audio,Audiophile 24/96},"
+		"{TerraTec,EWX 24/96},"
+		"{TerraTec,EWS 88MT},"
+		"{TerraTec,EWS 88D},"
+		"{ICEnsemble,Generic ICE1712},"
+		"{ICEnsemble,Generic Envy24}}");
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+
+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;	/* Enable this card */
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for ICE1712 soundcard.");
+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 ICE1712 soundcard.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable ICE1712 soundcard.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+
+#ifndef PCI_VENDOR_ID_ICE
+#define PCI_VENDOR_ID_ICE		0x1412
+#endif
+#ifndef PCI_DEVICE_ID_ICE_1712
+#define PCI_DEVICE_ID_ICE_1712		0x1712
+#endif
+
+#define ICE1712_SUBDEVICE_STDSP24	0x12141217	/* Hoontech SoundTrack Audio DSP 24 */
+#define ICE1712_SUBDEVICE_DELTA1010	0x121430d6
+#define ICE1712_SUBDEVICE_DELTADIO2496	0x121431d6
+#define ICE1712_SUBDEVICE_DELTA66	0x121432d6
+#define ICE1712_SUBDEVICE_DELTA44	0x121433d6
+#define ICE1712_SUBDEVICE_AUDIOPHILE	0x121434d6
+#define ICE1712_SUBDEVICE_EWX2496	0x3b153011
+#define ICE1712_SUBDEVICE_EWS88MT	0x3b151511
+#define ICE1712_SUBDEVICE_EWS88D	0x3b152b11
+
+/*
+ *  Direct registers
+ */
+
+#define ICEREG(ice, x) ((ice)->port + ICE1712_REG_##x)
+
+#define ICE1712_REG_CONTROL		0x00	/* byte */
+#define   ICE1712_RESET			0x80	/* reset whole chip */
+#define   ICE1712_SERR_LEVEL		0x04	/* SERR# level otherwise edge */
+#define   ICE1712_NATIVE		0x01	/* native mode otherwise SB */
+#define ICE1712_REG_IRQMASK		0x01	/* byte */
+#define   ICE1712_IRQ_MPU1		0x80
+#define   ICE1712_IRQ_TIMER		0x40
+#define   ICE1712_IRQ_MPU2		0x20
+#define   ICE1712_IRQ_PROPCM		0x10
+#define   ICE1712_IRQ_FM		0x08	/* FM/MIDI - legacy */
+#define   ICE1712_IRQ_PBKDS		0x04	/* playback DS channels */
+#define   ICE1712_IRQ_CONCAP		0x02	/* consumer capture */
+#define   ICE1712_IRQ_CONPBK		0x01	/* consumer playback */
+#define ICE1712_REG_IRQSTAT		0x02	/* byte */
+/* look to ICE1712_IRQ_* */
+#define ICE1712_REG_INDEX		0x03	/* byte - indirect CCIxx regs */
+#define ICE1712_REG_DATA		0x04	/* byte - indirect CCIxx regs */
+#define ICE1712_REG_NMI_STAT1		0x05	/* byte */
+#define ICE1712_REG_NMI_DATA		0x06	/* byte */
+#define ICE1712_REG_NMI_INDEX		0x07	/* byte */
+#define ICE1712_REG_AC97_INDEX		0x08	/* byte */
+#define ICE1712_REG_AC97_CMD		0x09	/* byte */
+#define   ICE1712_AC97_COLD		0x80	/* cold reset */
+#define   ICE1712_AC97_WARM		0x40	/* warm reset */
+#define   ICE1712_AC97_WRITE		0x20	/* W: write, R: write in progress */
+#define   ICE1712_AC97_READ		0x10	/* W: read, R: read in progress */
+#define   ICE1712_AC97_READY		0x08	/* codec ready status bit */
+#define   ICE1712_AC97_PBK_VSR		0x02	/* playback VSR */
+#define   ICE1712_AC97_CAP_VSR		0x01	/* capture VSR */
+#define ICE1712_REG_AC97_DATA		0x0a	/* word (little endian) */
+#define ICE1712_REG_MPU1_CTRL		0x0c	/* byte */
+#define ICE1712_REG_MPU1_DATA		0x0d	/* byte */
+#define ICE1712_REG_I2C_DEV_ADDR	0x10	/* byte */
+#define   ICE1712_I2C_WRITE		0x01	/* write direction */
+#define ICE1712_REG_I2C_BYTE_ADDR	0x11	/* byte */
+#define ICE1712_REG_I2C_DATA		0x12	/* byte */
+#define ICE1712_REG_I2C_CTRL		0x13	/* byte */
+#define   ICE1712_I2C_EEPROM		0x80	/* EEPROM exists */
+#define   ICE1712_I2C_BUSY		0x01	/* busy bit */
+#define ICE1712_REG_CONCAP_ADDR		0x14	/* dword - consumer capture */
+#define ICE1712_REG_CONCAP_COUNT	0x18	/* word - current/base count */
+#define ICE1712_REG_SERR_SHADOW		0x1b	/* byte */
+#define ICE1712_REG_MPU2_CTRL		0x1c	/* byte */
+#define ICE1712_REG_MPU2_DATA		0x1d	/* byte */
+#define ICE1712_REG_TIMER		0x1e	/* word */
+
+/*
+ *  Indirect registers
+ */
+
+#define ICE1712_IREG_PBK_COUNT_HI	0x00
+#define ICE1712_IREG_PBK_COUNT_LO	0x01
+#define ICE1712_IREG_PBK_CTRL		0x02
+#define ICE1712_IREG_PBK_LEFT		0x03	/* left volume */
+#define ICE1712_IREG_PBK_RIGHT		0x04	/* right volume */
+#define ICE1712_IREG_PBK_SOFT		0x05	/* soft volume */
+#define ICE1712_IREG_PBK_RATE_LO	0x06
+#define ICE1712_IREG_PBK_RATE_MID	0x07
+#define ICE1712_IREG_PBK_RATE_HI	0x08
+#define ICE1712_IREG_CAP_COUNT_HI	0x10
+#define ICE1712_IREG_CAP_COUNT_LO	0x11
+#define ICE1712_IREG_CAP_CTRL		0x12
+#define ICE1712_IREG_GPIO_DATA		0x20
+#define ICE1712_IREG_GPIO_WRITE_MASK	0x21
+#define ICE1712_IREG_GPIO_DIRECTION	0x22
+#define ICE1712_IREG_CONSUMER_POWERDOWN	0x30
+#define ICE1712_IREG_PRO_POWERDOWN	0x31
+
+/*
+ *  Consumer section direct DMA registers
+ */
+
+#define ICEDS(ice, x) ((ice)->dmapath_port + ICE1712_DS_##x)
+ 
+#define ICE1712_DS_INTMASK		0x00	/* word - interrupt mask */
+#define ICE1712_DS_INTSTAT		0x02	/* word - interrupt status */
+#define ICE1712_DS_DATA			0x04	/* dword - channel data */
+#define ICE1712_DS_INDEX		0x08	/* dword - channel index */
+
+/*
+ *  Consumer section channel registers
+ */
+ 
+#define ICE1712_DSC_ADDR0		0x00	/* dword - base address 0 */
+#define ICE1712_DSC_COUNT0		0x01	/* word - count 0 */
+#define ICE1712_DSC_ADDR1		0x02	/* dword - base address 1 */
+#define ICE1712_DSC_COUNT1		0x03	/* word - count 1 */
+#define ICE1712_DSC_CONTROL		0x04	/* byte - control & status */
+#define   ICE1712_BUFFER1		0x80	/* buffer1 is active */
+#define   ICE1712_BUFFER1_AUTO		0x40	/* buffer1 auto init */
+#define   ICE1712_BUFFER0_AUTO		0x20	/* buffer0 auto init */
+#define   ICE1712_FLUSH			0x10	/* flush FIFO */
+#define   ICE1712_STEREO		0x08	/* stereo */
+#define   ICE1712_16BIT			0x04	/* 16-bit data */
+#define   ICE1712_PAUSE			0x02	/* pause */
+#define   ICE1712_START			0x01	/* start */
+#define ICE1712_DSC_RATE		0x05	/* dword - rate */
+#define ICE1712_DSC_VOLUME		0x06	/* word - volume control */
+
+/* 
+ *  Professional multi-track direct control registers
+ */
+
+#define ICEMT(ice, x) ((ice)->profi_port + ICE1712_MT_##x)
+
+#define ICE1712_MT_IRQ			0x00	/* byte - interrupt mask */
+#define   ICE1712_MULTI_CAPTURE		0x80	/* capture IRQ */
+#define   ICE1712_MULTI_PLAYBACK	0x40	/* playback IRQ */
+#define   ICE1712_MULTI_CAPSTATUS	0x02	/* capture IRQ status */
+#define   ICE1712_MULTI_PBKSTATUS	0x01	/* playback IRQ status */
+#define ICE1712_MT_RATE			0x01	/* byte - sampling rate select */
+#define   ICE1712_SPDIF_MASTER		0x10	/* S/PDIF input is master clock */
+#define ICE1712_MT_I2S_FORMAT		0x02	/* byte - I2S data format */
+#define ICE1712_MT_AC97_INDEX		0x04	/* byte - AC'97 index */
+#define ICE1712_MT_AC97_CMD		0x05	/* byte - AC'97 command & status */
+/* look to ICE1712_AC97_* */
+#define ICE1712_MT_AC97_DATA		0x06	/* word - AC'97 data */
+#define ICE1712_MT_PLAYBACK_ADDR	0x10	/* dword - playback address */
+#define ICE1712_MT_PLAYBACK_SIZE	0x14	/* word - playback size */
+#define ICE1712_MT_PLAYBACK_COUNT	0x16	/* word - playback count */
+#define ICE1712_MT_PLAYBACK_CONTROL	0x18	/* byte - control */
+#define   ICE1712_CAPTURE_START_SHADOW	0x04	/* capture start */
+#define   ICE1712_PLAYBACK_PAUSE	0x02	/* playback pause */
+#define   ICE1712_PLAYBACK_START	0x01	/* playback start */
+#define ICE1712_MT_CAPTURE_ADDR		0x20	/* dword - capture address */
+#define ICE1712_MT_CAPTURE_SIZE		0x24	/* word - capture size */
+#define ICE1712_MT_CAPTURE_COUNT	0x26	/* word - capture count */
+#define ICE1712_MT_CAPTURE_CONTROL	0x28	/* byte - control */
+#define   ICE1712_CAPTURE_START		0x01	/* capture start */
+#define ICE1712_MT_ROUTE_PSDOUT03	0x30	/* word */
+#define ICE1712_MT_ROUTE_SPDOUT		0x32	/* word */
+#define ICE1712_MT_ROUTE_CAPTURE	0x34	/* dword */
+#define ICE1712_MT_MONITOR_VOLUME	0x38	/* word */
+#define ICE1712_MT_MONITOR_INDEX	0x3a	/* byte */
+#define ICE1712_MT_MONITOR_RATE		0x3b	/* byte */
+#define ICE1712_MT_MONITOR_ROUTECTRL	0x3c	/* byte */
+#define   ICE1712_ROUTE_AC97		0x01	/* route digital mixer output to AC'97 */
+#define ICE1712_MT_MONITOR_PEAKINDEX	0x3e	/* byte */
+#define ICE1712_MT_MONITOR_PEAKDATA	0x3f	/* byte */
+
+/*
+ *  Codec configuration bits
+ */
+
+/* PCI[60] System Configuration */
+#define ICE1712_CFG_CLOCK	0xc0
+#define   ICE1712_CFG_CLOCK512	0x00	/* 22.5692Mhz, 44.1kHz*512 */
+#define   ICE1712_CFG_CLOCK384  0x40	/* 16.9344Mhz, 44.1kHz*384 */
+#define   ICE1712_CFG_EXT	0x80	/* external clock */
+#define ICE1712_CFG_2xMPU401	0x20	/* two MPU401 UARTs */
+#define ICE1712_CFG_NO_CON_AC97 0x10	/* consumer AC'97 codec is not present */
+#define ICE1712_CFG_ADC_MASK	0x0c	/* one, two, three, four stereo ADCs */
+#define ICE1712_CFG_DAC_MASK	0x03	/* one, two, three, four stereo DACs */
+/* PCI[61] AC-Link Configuration */
+#define ICE1712_CFG_PRO_I2S	0x80	/* multitrack converter: I2S or AC'97 */
+#define ICE1712_CFG_AC97_PACKED	0x01	/* split or packed mode - AC'97 */
+/* PCI[62] I2S Features */
+#define ICE1712_CFG_I2S_VOLUME	0x80	/* volume/mute capability */
+#define ICE1712_CFG_I2S_96KHZ	0x40	/* supports 96kHz sampling */
+#define ICE1712_CFG_I2S_RESMASK	0x30	/* resolution mask, 16,18,20,24-bit */
+#define ICE1712_CFG_I2S_OTHER	0x0f	/* other I2S IDs */
+/* PCI[63] S/PDIF Configuration */
+#define ICE1712_CFG_I2S_CHIPID	0xfc	/* I2S chip ID */
+#define ICE1712_CFG_SPDIF_IN	0x02	/* S/PDIF input is present */
+#define ICE1712_CFG_SPDIF_OUT	0x01	/* S/PDIF output is present */
+
+/* MidiMan Delta GPIO definitions */
+
+#define ICE1712_DELTA_DFS	0x01	/* fast/slow sample rate mode */
+					/* (>48kHz must be 1) */
+					/* all cards */
+#define ICE1712_DELTA_SPDIF_IN_STAT 0x02
+					/* S/PDIF input status */
+					/* 0 = valid signal is present */
+					/* all except Delta44 */
+					/* look to CS8414 datasheet */
+#define ICE1712_DELTA_SPDIF_OUT_STAT_CLOCK 0x04
+					/* S/PDIF output status clock */
+					/* (writting on rising edge - 0->1) */
+					/* all except Delta44 */
+					/* look to CS8404A datasheet */
+#define ICE1712_DELTA_SPDIF_OUT_STAT_DATA 0x08
+					/* S/PDIF output status data */
+					/* all except Delta44 */
+					/* look to CS8404A datasheet */
+#define ICE1712_DELTA_SPDIF_INPUT_SELECT 0x10
+					/* coaxial (0), optical (1) */
+					/* S/PDIF input select*/
+					/* DeltaDiO only */
+#define ICE1712_DELTA_WORD_CLOCK_SELECT 0x10
+					/* 1 - clock are taken from S/PDIF input */
+					/* 0 - clock are taken from Word Clock input */
+					/* Delta1010 only (affected SPMCLKIN pin of Envy24) */
+#define ICE1712_DELTA_CODEC_SERIAL_DATA 0x10
+					/* AKM4524 serial data */
+					/* Delta66 and Delta44 */
+#define ICE1712_DELTA_WORD_CLOCK_STATUS	0x20
+					/* 0 = valid word clock signal is present */
+					/* Delta1010 only */
+#define ICE1712_DELTA_CODEC_SERIAL_CLOCK 0x20
+					/* AKM4524 serial clock */
+					/* (writting on rising edge - 0->1 */
+					/* Delta66 and Delta44 */
+#define ICE1712_DELTA_CODEC_CHIP_A	0x40
+#define ICE1712_DELTA_CODEC_CHIP_B	0x80
+					/* 1 - select chip A or B */
+					/* Delta66 and Delta44 */
+
+/* M-Audio Audiophile definitions */
+/* 0x01 = DFS */
+#define ICE1712_DELTA_AP_CCLK	0x02	/* AudioPhile only */
+					/* SPI clock */
+					/* (clocking on rising edge - 0->1) */
+#define ICE1712_DELTA_AP_DIN	0x04	/* AudioPhile only - data input */
+#define ICE1712_DELTA_AP_DOUT	0x08	/* AudioPhile only - data output */
+#define ICE1712_DELTA_AP_CS_DIGITAL 0x10 /* AudioPhile only - CS8427 chip select */
+					/* low signal = select */
+#define ICE1712_DELTA_AP_CS_CODEC 0x20	/* AudioPhile only - AK4528 chip select */
+					/* low signal = select */
+
+/* Hoontech SoundTrack Audio DSP 24 GPIO definitions */
+
+#define ICE1712_STDSP24_0_BOX(r, x)	r[0] = ((r[0] & ~3) | ((x)&3))
+#define ICE1712_STDSP24_0_DAREAR(r, x)	r[0] = ((r[0] & ~4) | (((x)&1)<<2))
+#define ICE1712_STDSP24_1_CHN1(r, x)	r[1] = ((r[1] & ~1) | ((x)&1))
+#define ICE1712_STDSP24_1_CHN2(r, x)	r[1] = ((r[1] & ~2) | (((x)&1)<<1))
+#define ICE1712_STDSP24_1_CHN3(r, x)	r[1] = ((r[1] & ~4) | (((x)&1)<<2))
+#define ICE1712_STDSP24_2_CHN4(r, x)	r[2] = ((r[2] & ~1) | ((x)&1))
+#define ICE1712_STDSP24_2_MIDIIN(r, x)	r[2] = ((r[2] & ~2) | (((x)&1)<<1))
+#define ICE1712_STDSP24_2_MIDI1(r, x)	r[2] = ((r[2] & ~4) | (((x)&1)<<2))
+#define ICE1712_STDSP24_3_MIDI2(r, x)	r[3] = ((r[3] & ~1) | ((x)&1))
+#define ICE1712_STDSP24_3_MUTE(r, x)	r[3] = ((r[3] & ~2) | (((x)&1)<<1))
+#define ICE1712_STDSP24_3_INSEL(r, x)	r[3] = ((r[3] & ~4) | (((x)&1)<<2))
+#define ICE1712_STDSP24_SET_ADDR(r, a)	r[a&3] = ((r[a&3] & ~0x18) | (((a)&3)<<3))
+#define ICE1712_STDSP24_CLOCK(r, a, c)	r[a&3] = ((r[a&3] & ~0x20) | (((c)&1)<<5))
+#define ICE1712_STDSP24_CLOCK_BIT	(1<<5)
+
+/* Hoontech SoundTrack Audio DSP 24 box configuration definitions */
+
+#define ICE1712_STDSP24_DAREAR		(1<<0)
+#define ICE1712_STDSP24_MUTE		(1<<1)
+#define ICE1712_STDSP24_INSEL		(1<<2)
+
+#define ICE1712_STDSP24_BOX_CHN1	(1<<0)	/* input channel 1 */
+#define ICE1712_STDSP24_BOX_CHN2	(1<<1)	/* input channel 2 */
+#define ICE1712_STDSP24_BOX_CHN3	(1<<2)	/* input channel 3 */
+#define ICE1712_STDSP24_BOX_CHN4	(1<<3)	/* input channel 4 */
+#define ICE1712_STDSP24_BOX_MIDI1	(1<<8)
+#define ICE1712_STDSP24_BOX_MIDI2	(1<<9)
+
+/* TerraTec EWX 24/96 configuration definitions */
+
+#define ICE1712_EWX2496_AK4524_CS	0x01	/* AK4524 chip select; low = active */
+#define ICE1712_EWX2496_AIN_SEL		0x02	/* input sensitivity switch; high = louder */
+#define ICE1712_EWX2496_AOUT_SEL	0x04	/* output sensitivity switch; high = louder */
+#define ICE1712_EWX2496_RW		0x08	/* read/write switch for i2c; high = write  */
+#define ICE1712_EWX2496_SERIAL_DATA	0x10	/* i2c & ak4524 data */
+#define ICE1712_EWX2496_SERIAL_CLOCK	0x20	/* i2c & ak4524 clock */
+#define ICE1712_EWX2496_TX2		0x40	/* MIDI2 (not used) */
+#define ICE1712_EWX2496_RX2		0x80	/* MIDI2 (not used) */
+
+/* TerraTec EWS 88MT/D configuration definitions */
+/* RW, SDA snd SCLK are identical with EWX24/96 */
+
+#define ICE1712_EWS88_CS8414_RATE	0x07	/* CS8414 sample rate: gpio 0-2 */
+#define ICE1712_EWS88_RW		0x08	/* read/write switch for i2c; high = write  */
+#define ICE1712_EWS88_SERIAL_DATA	0x10	/* i2c & ak4524 data */
+#define ICE1712_EWS88_SERIAL_CLOCK	0x20	/* i2c & ak4524 clock */
+#define ICE1712_EWS88_TX2		0x40	/* MIDI2 (only on 88D) */
+#define ICE1712_EWS88_RX2		0x80	/* MIDI2 (only on 88D) */
+
+/* i2c address */
+#define ICE1712_EWS88MT_CS8404_ADDR	0x40
+#define ICE1712_EWS88MT_INPUT_ADDR	0x46
+#define ICE1712_EWS88MT_OUTPUT_ADDR	0x48
+#define ICE1712_EWS88MT_OUTPUT_SENSE	0x40	/* mask */
+#define ICE1712_EWS88D_PCF_ADDR		0x40
+
+/*
+ * DMA mode values
+ * identical with DMA_XXX on i386 architecture.
+ */
+#define ICE1712_DMA_MODE_WRITE		0x48
+#define ICE1712_DMA_AUTOINIT		0x10
+
+
+/*
+ *  
+ */
+
+typedef struct _snd_ice1712 ice1712_t;
+
+typedef struct {
+	void (*write)(ice1712_t *ice, unsigned char reg, unsigned char data);
+	unsigned char (*read)(ice1712_t *ice, unsigned char reg);
+	void (*write_bytes)(ice1712_t *ice, unsigned char reg, int bytes, unsigned char *data);
+	void (*read_bytes)(ice1712_t *ice, unsigned char reg, int bytes, unsigned char *data);
+} ice1712_cs8427_ops_t;
+
+typedef struct {
+	unsigned int subvendor;	/* PCI[2c-2f] */
+	unsigned char size;	/* size of EEPROM image in bytes */
+	unsigned char version;	/* must be 1 */
+	unsigned char codec;	/* codec configuration PCI[60] */
+	unsigned char aclink;	/* ACLink configuration PCI[61] */
+	unsigned char i2sID;	/* PCI[62] */
+	unsigned char spdif;	/* S/PDIF configuration PCI[63] */
+	unsigned char gpiomask;	/* GPIO initial mask, 0 = write, 1 = don't */
+	unsigned char gpiostate; /* GPIO initial state */
+	unsigned char gpiodir;	/* GPIO direction state */
+	unsigned short ac97main;
+	unsigned short ac97pcm;
+	unsigned short ac97rec;
+	unsigned char ac97recsrc;
+	unsigned char dacID[4];	/* I2S IDs for DACs */
+	unsigned char adcID[4];	/* I2S IDs for ADCs */
+	unsigned char extra[4];
+} ice1712_eeprom_t;
+
+struct _snd_ice1712 {
+	unsigned long conp_dma_size;
+	unsigned long conc_dma_size;
+	unsigned long prop_dma_size;
+	unsigned long proc_dma_size;
+	int irq;
+
+	unsigned long port;
+	struct resource *res_port;
+	unsigned long ddma_port;
+	struct resource *res_ddma_port;
+	unsigned long dmapath_port;
+	struct resource *res_dmapath_port;
+	unsigned long profi_port;
+	struct resource *res_profi_port;
+
+	unsigned int config;	/* system configuration */
+
+	struct pci_dev *pci;
+	snd_card_t *card;
+	snd_pcm_t *pcm;
+	snd_pcm_t *pcm_ds;
+	snd_pcm_t *pcm_pro;
+        snd_pcm_substream_t *playback_con_substream;
+        snd_pcm_substream_t *playback_con_substream_ds[6];
+        snd_pcm_substream_t *capture_con_substream;
+        snd_pcm_substream_t *playback_pro_substream;
+        snd_pcm_substream_t *capture_pro_substream;
+	unsigned int playback_pro_size;
+	unsigned int capture_pro_size;
+	unsigned int playback_con_virt_addr[6];
+	unsigned int playback_con_active_buf[6];
+	unsigned int capture_con_virt_addr;
+	unsigned int ac97_ext_id;
+	ac97_t *ac97;
+	snd_rawmidi_t *rmidi[2];
+
+	spinlock_t reg_lock;
+	struct semaphore gpio_mutex;
+	snd_info_entry_t *proc_entry;
+
+	ice1712_eeprom_t eeprom;
+
+	unsigned int pro_volumes[20];
+	int num_adcs;
+	int num_dacs;
+	unsigned char ak4524_images[4][8];
+	unsigned char hoontech_boxbits[4];
+	unsigned int hoontech_config;
+	unsigned short hoontech_boxconfig[4];
+
+	unsigned char cs8403_spdif_bits;
+	unsigned char cs8403_spdif_stream_bits;
+	snd_kcontrol_t *spdif_stream_ctl;
+
+	unsigned char cs8427_spdif_status[5];
+	unsigned char cs8427_spdif_stream_status[5];
+	ice1712_cs8427_ops_t *cs8427_ops;
+
+	unsigned char gpio_direction, gpio_write_mask;
+};
+
+#define chip_t ice1712_t
+
+static struct pci_device_id snd_ice1712_ids[] __devinitdata = {
+	{ PCI_VENDOR_ID_ICE, PCI_DEVICE_ID_ICE_1712, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },   /* ICE1712 */
+	{ 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, snd_ice1712_ids);
+
+static int snd_ice1712_build_pro_mixer(ice1712_t *ice);
+static int snd_ice1712_build_controls(ice1712_t *ice);
+
+/*
+ *  Basic I/O
+ */
+ 
+static inline void snd_ice1712_write(ice1712_t * ice, u8 addr, u8 data)
+{
+	outb(addr, ICEREG(ice, INDEX));
+	outb(data, ICEREG(ice, DATA));
+}
+
+static inline u8 snd_ice1712_read(ice1712_t * ice, u8 addr)
+{
+	outb(addr, ICEREG(ice, INDEX));
+	return inb(ICEREG(ice, DATA));
+}
+
+static inline void snd_ice1712_ds_write(ice1712_t * ice, u8 channel, u8 addr, u32 data)
+{
+	outb((channel << 4) | addr, ICEDS(ice, INDEX));
+	outl(data, ICEDS(ice, DATA));
+}
+
+static inline u32 snd_ice1712_ds_read(ice1712_t * ice, u8 channel, u8 addr)
+{
+	outb((channel << 4) | addr, ICEDS(ice, INDEX));
+	return inl(ICEDS(ice, DATA));
+}
+
+static void snd_ice1712_ac97_write(ac97_t *ac97,
+				   unsigned short reg,
+				   unsigned short val)
+{
+	ice1712_t *ice = (ice1712_t *)ac97->private_data;
+	int tm;
+	unsigned char old_cmd = 0;
+
+	for (tm = 0; tm < 0x10000; tm++) {
+		old_cmd = inb(ICEREG(ice, AC97_CMD));
+		if (old_cmd & (ICE1712_AC97_WRITE | ICE1712_AC97_READ))
+			continue;
+		if (!(old_cmd & ICE1712_AC97_READY))
+			continue;
+		break;
+	}
+	outb(reg, ICEREG(ice, AC97_INDEX));
+	outw(val, ICEREG(ice, AC97_DATA));
+	old_cmd &= ~(ICE1712_AC97_PBK_VSR | ICE1712_AC97_CAP_VSR);
+	outb(old_cmd | ICE1712_AC97_WRITE, ICEREG(ice, AC97_CMD));
+	for (tm = 0; tm < 0x10000; tm++)
+		if ((inb(ICEREG(ice, AC97_CMD)) & ICE1712_AC97_WRITE) == 0)
+			break;
+}
+
+static unsigned short snd_ice1712_ac97_read(ac97_t *ac97,
+					    unsigned short reg)
+{
+	ice1712_t *ice = (ice1712_t *)ac97->private_data;
+	int tm;
+	unsigned char old_cmd = 0;
+
+	for (tm = 0; tm < 0x10000; tm++) {
+		old_cmd = inb(ICEREG(ice, AC97_CMD));
+		if (old_cmd & (ICE1712_AC97_WRITE | ICE1712_AC97_READ))
+			continue;
+		if (!(old_cmd & ICE1712_AC97_READY))
+			continue;
+		break;
+	}
+	outb(reg, ICEREG(ice, AC97_INDEX));
+	outb(old_cmd | ICE1712_AC97_READ, ICEREG(ice, AC97_CMD));
+	for (tm = 0; tm < 0x10000; tm++)
+		if ((inb(ICEREG(ice, AC97_CMD)) & ICE1712_AC97_READ) == 0)
+			break;
+	if (tm >= 0x10000)		/* timeout */
+		return ~0;
+	return inw(ICEREG(ice, AC97_DATA));
+}
+
+/*
+ * pro ac97 section
+ */
+
+static void snd_ice1712_pro_ac97_write(ac97_t *ac97,
+				       unsigned short reg,
+				       unsigned short val)
+{
+	ice1712_t *ice = (ice1712_t *)ac97->private_data;
+	int tm;
+	unsigned char old_cmd = 0;
+
+	for (tm = 0; tm < 0x10000; tm++) {
+		old_cmd = inb(ICEMT(ice, AC97_CMD));
+		if (old_cmd & (ICE1712_AC97_WRITE | ICE1712_AC97_READ))
+			continue;
+		if (!(old_cmd & ICE1712_AC97_READY))
+			continue;
+		break;
+	}
+	outb(reg, ICEMT(ice, AC97_INDEX));
+	outw(val, ICEMT(ice, AC97_DATA));
+	old_cmd &= ~(ICE1712_AC97_PBK_VSR | ICE1712_AC97_CAP_VSR);
+	outb(old_cmd | ICE1712_AC97_WRITE, ICEMT(ice, AC97_CMD));
+	for (tm = 0; tm < 0x10000; tm++)
+		if ((inb(ICEMT(ice, AC97_CMD)) & ICE1712_AC97_WRITE) == 0)
+			break;
+}
+
+
+static unsigned short snd_ice1712_pro_ac97_read(ac97_t *ac97,
+						unsigned short reg)
+{
+	ice1712_t *ice = (ice1712_t *)ac97->private_data;
+	int tm;
+	unsigned char old_cmd = 0;
+
+	for (tm = 0; tm < 0x10000; tm++) {
+		old_cmd = inb(ICEMT(ice, AC97_CMD));
+		if (old_cmd & (ICE1712_AC97_WRITE | ICE1712_AC97_READ))
+			continue;
+		if (!(old_cmd & ICE1712_AC97_READY))
+			continue;
+		break;
+	}
+	outb(reg, ICEMT(ice, AC97_INDEX));
+	outb(old_cmd | ICE1712_AC97_READ, ICEMT(ice, AC97_CMD));
+	for (tm = 0; tm < 0x10000; tm++)
+		if ((inb(ICEMT(ice, AC97_CMD)) & ICE1712_AC97_READ) == 0)
+			break;
+	if (tm >= 0x10000)		/* timeout */
+		return ~0;
+	return inw(ICEMT(ice, AC97_DATA));
+}
+
+static int snd_ice1712_digmix_route_ac97_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_ice1712_digmix_route_ac97_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	
+	spin_lock_irqsave(&ice->reg_lock, flags);
+	ucontrol->value.integer.value[0] = inb(ICEMT(ice, MONITOR_ROUTECTRL)) & ICE1712_ROUTE_AC97 ? 1 : 0;
+	spin_unlock_irqrestore(&ice->reg_lock, flags);
+	return 0;
+}
+
+static int snd_ice1712_digmix_route_ac97_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	unsigned char val, nval;
+	unsigned long flags;
+	
+	spin_lock_irqsave(&ice->reg_lock, flags);
+	val = inb(ICEMT(ice, MONITOR_ROUTECTRL));
+	nval = val & ~ICE1712_ROUTE_AC97;
+	if (ucontrol->value.integer.value[0]) nval |= ICE1712_ROUTE_AC97;
+	outb(nval, ICEMT(ice, MONITOR_ROUTECTRL));
+	spin_unlock_irqrestore(&ice->reg_lock, flags);
+	return val != nval;
+}
+
+static snd_kcontrol_new_t snd_ice1712_mixer_digmix_route_ac97 = {
+#ifdef TARGET_OS2
+	SNDRV_CTL_ELEM_IFACE_MIXER,0,0,
+	"Digital Mixer To AC97",0,0,
+	snd_ice1712_digmix_route_ac97_info,
+	snd_ice1712_digmix_route_ac97_get,
+	snd_ice1712_digmix_route_ac97_put,0
+#else
+	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	name: "Digital Mixer To AC97",
+	info: snd_ice1712_digmix_route_ac97_info,
+	get: snd_ice1712_digmix_route_ac97_get,
+	put: snd_ice1712_digmix_route_ac97_put,
+#endif
+};
+
+
+/*
+ */
+static void snd_ice1712_delta_cs8403_spdif_write(ice1712_t *ice, unsigned char bits)
+{
+	unsigned char tmp, mask1, mask2;
+	int idx;
+	/* send byte to transmitter */
+	mask1 = ICE1712_DELTA_SPDIF_OUT_STAT_CLOCK;
+	mask2 = ICE1712_DELTA_SPDIF_OUT_STAT_DATA;
+	down(&ice->gpio_mutex);
+	tmp = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA);
+	for (idx = 7; idx >= 0; idx--) {
+		tmp &= ~(mask1 | mask2);
+		if (bits & (1 << idx))
+			tmp |= mask2;
+		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
+		udelay(100);
+		tmp |= mask1;
+		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
+		udelay(100);
+	}
+	tmp &= ~mask1;
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
+	up(&ice->gpio_mutex);
+}
+
+static void snd_ice1712_decode_cs8403_spdif_bits(snd_aes_iec958_t *diga,
+						 unsigned char bits)
+{
+	if (bits & 0x01) {	/* consumer */
+		if (!(bits & 0x02))
+			diga->status[0] |= IEC958_AES0_NONAUDIO;
+		if (!(bits & 0x08))
+			diga->status[0] |= IEC958_AES0_CON_NOT_COPYRIGHT;
+		switch (bits & 0x10) {
+		case 0x10: diga->status[0] |= IEC958_AES0_CON_EMPHASIS_NONE; break;
+		case 0x00: diga->status[0] |= IEC958_AES0_CON_EMPHASIS_5015; break;
+		}
+		if (!(bits & 0x80))
+			diga->status[1] |= IEC958_AES1_CON_ORIGINAL;
+		switch (bits & 0x60) {
+		case 0x00: diga->status[1] |= IEC958_AES1_CON_MAGNETIC_ID; break;
+		case 0x20: diga->status[1] |= IEC958_AES1_CON_DIGDIGCONV_ID; break;
+		case 0x40: diga->status[1] |= IEC958_AES1_CON_LASEROPT_ID; break;
+		case 0x60: diga->status[1] |= IEC958_AES1_CON_GENERAL; break;
+		}
+		switch (bits & 0x06) {
+		case 0x00: diga->status[3] |= IEC958_AES3_CON_FS_44100; break;
+		case 0x02: diga->status[3] |= IEC958_AES3_CON_FS_48000; break;
+		case 0x04: diga->status[3] |= IEC958_AES3_CON_FS_32000; break;
+		}
+	} else {
+		diga->status[0] = IEC958_AES0_PROFESSIONAL;
+		switch (bits & 0x18) {
+		case 0x00: diga->status[0] |= IEC958_AES0_PRO_FS_32000; break;
+		case 0x10: diga->status[0] |= IEC958_AES0_PRO_FS_44100; break;
+		case 0x08: diga->status[0] |= IEC958_AES0_PRO_FS_48000; break;
+		case 0x18: diga->status[0] |= IEC958_AES0_PRO_FS_NOTID; break;
+		}
+		switch (bits & 0x60) {
+		case 0x20: diga->status[0] |= IEC958_AES0_PRO_EMPHASIS_NONE; break;
+		case 0x40: diga->status[0] |= IEC958_AES0_PRO_EMPHASIS_5015; break;
+		case 0x00: diga->status[0] |= IEC958_AES0_PRO_EMPHASIS_CCITT; break;
+		case 0x60: diga->status[0] |= IEC958_AES0_PRO_EMPHASIS_NOTID; break;
+		}
+		if (bits & 0x80)
+			diga->status[1] |= IEC958_AES1_PRO_MODE_STEREOPHONIC;
+	}
+}
+
+static unsigned char snd_ice1712_encode_cs8403_spdif_bits(snd_aes_iec958_t *diga)
+{
+	unsigned char bits;
+
+	if (!(diga->status[0] & IEC958_AES0_PROFESSIONAL)) {
+		bits = 0x01;	/* consumer mode */
+		if (diga->status[0] & IEC958_AES0_NONAUDIO)
+			bits &= ~0x02;
+		else
+			bits |= 0x02;
+		if (diga->status[0] & IEC958_AES0_CON_NOT_COPYRIGHT)
+			bits &= ~0x08;
+		else
+			bits |= 0x08;
+		switch (diga->status[0] & IEC958_AES0_CON_EMPHASIS) {
+		default:
+		case IEC958_AES0_CON_EMPHASIS_NONE: bits |= 0x10; break;
+		case IEC958_AES0_CON_EMPHASIS_5015: bits |= 0x00; break;
+		}
+		if (diga->status[1] & IEC958_AES1_CON_ORIGINAL)
+			bits &= ~0x80;
+		else
+			bits |= 0x80;
+		if ((diga->status[1] & IEC958_AES1_CON_CATEGORY) == IEC958_AES1_CON_GENERAL)
+			bits |= 0x60;
+		else {
+			switch(diga->status[1] & IEC958_AES1_CON_MAGNETIC_MASK) {
+			case IEC958_AES1_CON_MAGNETIC_ID:
+				bits |= 0x00; break;
+			case IEC958_AES1_CON_DIGDIGCONV_ID:
+				bits |= 0x20; break;
+			default:
+			case IEC958_AES1_CON_LASEROPT_ID:
+				bits |= 0x40; break;
+			}
+		}
+		switch (diga->status[3] & IEC958_AES3_CON_FS) {
+		default:
+		case IEC958_AES3_CON_FS_44100: bits |= 0x00; break;
+		case IEC958_AES3_CON_FS_48000: bits |= 0x02; break;
+		case IEC958_AES3_CON_FS_32000: bits |= 0x04; break;
+		}
+	} else {
+		bits = 0x00;	/* professional mode */
+		if (diga->status[0] & IEC958_AES0_NONAUDIO)
+			bits &= ~0x02;
+		else
+			bits |= 0x02;
+		/* CHECKME: I'm not sure about the bit order in val here */
+		switch (diga->status[0] & IEC958_AES0_PRO_FS) {
+		case IEC958_AES0_PRO_FS_32000:	bits |= 0x00; break;
+		case IEC958_AES0_PRO_FS_44100:	bits |= 0x10; break;	/* 44.1kHz */
+		case IEC958_AES0_PRO_FS_48000:	bits |= 0x08; break;	/* 48kHz */
+		default:
+		case IEC958_AES0_PRO_FS_NOTID: bits |= 0x18; break;
+		}
+		switch (diga->status[0] & IEC958_AES0_PRO_EMPHASIS) {
+		case IEC958_AES0_PRO_EMPHASIS_NONE: bits |= 0x20; break;
+		case IEC958_AES0_PRO_EMPHASIS_5015: bits |= 0x40; break;
+		case IEC958_AES0_PRO_EMPHASIS_CCITT: bits |= 0x00; break;
+		default:
+		case IEC958_AES0_PRO_EMPHASIS_NOTID: bits |= 0x60; break;
+		}
+		switch (diga->status[1] & IEC958_AES1_PRO_MODE) {
+		case IEC958_AES1_PRO_MODE_TWO:
+		case IEC958_AES1_PRO_MODE_STEREOPHONIC: bits |= 0x00; break;
+		default: bits |= 0x80; break;
+		}
+	}
+	return bits;
+}
+
+
+/*
+ * set gpio direction, write mask and data
+ */
+static void snd_ice1712_gpio_write_bits(ice1712_t *ice, int mask, int bits)
+{
+	ice->gpio_direction |= mask;
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION, ice->gpio_direction);
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, ~mask);
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, mask & bits);
+}
+
+/*
+ */
+static void save_gpio_status(ice1712_t *ice, unsigned char *tmp)
+{
+	down(&ice->gpio_mutex);
+	tmp[0] = ice->gpio_direction;
+	tmp[1] = ice->gpio_write_mask;
+}
+
+static void restore_gpio_status(ice1712_t *ice, unsigned char *tmp)
+{
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION, tmp[0]);
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, tmp[1]);
+	ice->gpio_direction = tmp[0];
+	ice->gpio_write_mask = tmp[1];
+	up(&ice->gpio_mutex);
+}
+
+/*
+ * CS8427 via SPI mode (for Audiophile)
+ */
+
+/* send 8 bits */
+static void ap_cs8427_write_byte(ice1712_t *ice, unsigned char data, unsigned char tmp)
+{
+	int idx;
+
+	for (idx = 7; idx >= 0; idx--) {
+		tmp &= ~(ICE1712_DELTA_AP_DOUT|ICE1712_DELTA_AP_CCLK);
+		if (data & (1 << idx))
+			tmp |= ICE1712_DELTA_AP_DOUT;
+		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
+		udelay(5);
+		tmp |= ICE1712_DELTA_AP_CCLK;
+		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
+		udelay(5);
+	}
+}
+
+/* read 8 bits */
+static unsigned char ap_cs8427_read_byte(ice1712_t *ice, unsigned char tmp)
+{
+	unsigned char data = 0;
+	int idx;
+	
+	for (idx = 7; idx >= 0; idx--) {
+		tmp &= ~ICE1712_DELTA_AP_CCLK;
+		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
+		udelay(5);
+		if (snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA) & ICE1712_DELTA_AP_DIN)
+			data |= 1 << idx;
+		tmp |= ICE1712_DELTA_AP_CCLK;
+		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
+		udelay(5);
+	}
+	return data;
+}
+
+/* assert chip select */
+static unsigned char ap_cs8427_codec_select(ice1712_t *ice)
+{
+	unsigned char tmp;
+	tmp = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA);
+	tmp |= ICE1712_DELTA_AP_CCLK | ICE1712_DELTA_AP_CS_CODEC;
+	tmp &= ~ICE1712_DELTA_AP_CS_DIGITAL;
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
+	udelay(5);
+	return tmp;
+}
+
+/* deassert chip select */
+static void ap_cs8427_codec_deassert(ice1712_t *ice, unsigned char tmp)
+{
+	tmp |= ICE1712_DELTA_AP_CS_DIGITAL;
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
+}
+
+/* write a register */
+static void snd_ice1712_ap_cs8427_write(ice1712_t *ice, unsigned char reg, unsigned char data)
+{
+	unsigned char tmp;
+	down(&ice->gpio_mutex);
+	tmp = ap_cs8427_codec_select(ice);
+	ap_cs8427_write_byte(ice, 0x20, tmp); /* address + write mode */
+	ap_cs8427_write_byte(ice, reg, tmp);
+	ap_cs8427_write_byte(ice, data, tmp);
+	ap_cs8427_codec_deassert(ice, tmp);
+	up(&ice->gpio_mutex);
+}
+
+/* read a register */
+static unsigned char snd_ice1712_ap_cs8427_read(ice1712_t *ice, unsigned char reg)
+{
+	unsigned char tmp, data;
+
+	down(&ice->gpio_mutex);
+	tmp = ap_cs8427_codec_select(ice);
+	ap_cs8427_write_byte(ice, 0x20, tmp); /* address + write mode */
+	ap_cs8427_write_byte(ice, reg, tmp);
+	ap_cs8427_codec_deassert(ice, tmp);
+	udelay(5);
+	tmp = ap_cs8427_codec_select(ice);
+	ap_cs8427_write_byte(ice, 0x21, tmp); /* address + read mode */
+	data = ap_cs8427_read_byte(ice, tmp);
+	ap_cs8427_codec_deassert(ice, tmp);
+	up(&ice->gpio_mutex);
+	return data;
+}
+
+/* sequential write */
+static void snd_ice1712_ap_cs8427_write_bytes(ice1712_t *ice, unsigned char reg, int bytes, unsigned char *data)
+{
+#if 1
+	unsigned char tmp;
+	down(&ice->gpio_mutex);
+	tmp = ap_cs8427_codec_select(ice);
+	ap_cs8427_write_byte(ice, 0x20, tmp); /* address + write mode */
+	ap_cs8427_write_byte(ice, reg | 0x80, tmp); /* incremental mode */
+	while (bytes-- > 0)
+		ap_cs8427_write_byte(ice, *data++, tmp);
+	ap_cs8427_codec_deassert(ice, tmp);
+	up(&ice->gpio_mutex);
+#else
+	while (bytes-- > 0)
+		snd_ice1712_ap_cs8427_write(ice, reg++, *data++);
+#endif
+}
+
+/* sequential read */
+static void snd_ice1712_ap_cs8427_read_bytes(ice1712_t *ice, unsigned char reg, int bytes, unsigned char *data)
+{
+#if 0 // is this working?  -- ti
+	unsigned char tmp;
+	
+	down(&ice->gpio_mutex);
+	tmp = ap_cs8427_codec_select(ice);
+	ap_cs8427_write_byte(ice, 0x20, tmp); /* address + write mode */
+	ap_cs8427_write_byte(ice, reg | 0x80, tmp); /* incremental mode */
+	ap_cs8427_codec_deassert(ice, tmp);
+	udelay(5);
+	tmp = ap_cs8427_codec_select(ice);
+	ap_cs8427_write_byte(ice, 0x21, tmp); /* address + read mode */
+	while (bytes-- > 0)
+		*data++ = ap_cs8427_read_byte(ice, tmp);
+	ap_cs8427_codec_deassert(ice, tmp);
+	up(&ice->gpio_mutex);
+#else
+	while (bytes-- > 0)
+		*data++ = snd_ice1712_ap_cs8427_read(ice, reg++); 
+#endif
+}
+
+#ifdef TARGET_OS2
+static ice1712_cs8427_ops_t snd_ice1712_ap_cs8427_ops = {
+	snd_ice1712_ap_cs8427_write,
+	snd_ice1712_ap_cs8427_read,
+	snd_ice1712_ap_cs8427_write_bytes,
+	snd_ice1712_ap_cs8427_read_bytes
+};
+#else
+static ice1712_cs8427_ops_t snd_ice1712_ap_cs8427_ops = {
+	write: snd_ice1712_ap_cs8427_write,
+	read: snd_ice1712_ap_cs8427_read,
+	write_bytes: snd_ice1712_ap_cs8427_write_bytes,
+	read_bytes: snd_ice1712_ap_cs8427_read_bytes,
+};
+#endif
+
+
+/*
+ * access via i2c mode (for EWX 24/96, EWS 88MT&D)
+ */
+
+/* send SDA and SCL */
+static void ewx_i2c_set(ice1712_t *ice, int clk, int data)
+{
+	unsigned char tmp = 0;
+	if (clk)
+		tmp |= ICE1712_EWX2496_SERIAL_CLOCK;
+	if (data)
+		tmp |= ICE1712_EWX2496_SERIAL_DATA;
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
+	udelay(5);
+}
+
+/* send one bit */
+static void ewx_i2c_send(ice1712_t *ice, int data)
+{
+	ewx_i2c_set(ice, 0, data);
+	ewx_i2c_set(ice, 1, data);
+	ewx_i2c_set(ice, 0, data);
+}
+
+/* start i2c */
+static void ewx_i2c_start(ice1712_t *ice)
+{
+	ewx_i2c_set(ice, 0, 1);
+	ewx_i2c_set(ice, 1, 1);
+	ewx_i2c_set(ice, 1, 0);
+	ewx_i2c_set(ice, 0, 0);
+}
+
+/* stop i2c */
+static void ewx_i2c_stop(ice1712_t *ice)
+{
+	ewx_i2c_set(ice, 0, 0);
+	ewx_i2c_set(ice, 1, 0);
+	ewx_i2c_set(ice, 1, 1);
+}
+
+/* send a byte and get ack */
+static void ewx_i2c_write(ice1712_t *ice, unsigned char val)
+{
+	int i;
+	for (i = 7; i >= 0; i--)
+		ewx_i2c_send(ice, val & (1 << i));
+	ewx_i2c_send(ice, 1); /* ack */
+}
+
+/* prepare for write and send i2c_start */
+static void ewx_i2c_write_prepare(ice1712_t *ice)
+{
+	/* set RW high */
+	unsigned char mask = ICE1712_EWX2496_RW;
+	if (ice->eeprom.subvendor == ICE1712_SUBDEVICE_EWX2496)
+		mask |= ICE1712_EWX2496_AK4524_CS; /* CS high also */
+	snd_ice1712_gpio_write_bits(ice, mask, mask);
+	/* set direction both SDA and SCL write */
+	ice->gpio_direction |= ICE1712_EWX2496_SERIAL_CLOCK|ICE1712_EWX2496_SERIAL_DATA;
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION, ice->gpio_direction);
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, ~(ICE1712_EWX2496_SERIAL_CLOCK|ICE1712_EWX2496_SERIAL_DATA));
+			  
+	ewx_i2c_start(ice);
+}
+
+/* read a bit from serial data;
+   this changes write mask
+   SDA direction must be set to low
+*/
+static int ewx_i2c_read_bit(ice1712_t *ice)
+{
+	int data;
+	/* set RW pin to low */
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, ~ICE1712_EWX2496_RW);
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, 0);
+	data = (snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA) & ICE1712_EWX2496_SERIAL_DATA) ? 1 : 0;
+	/* set RW pin to high */
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, ICE1712_EWX2496_RW);
+	return data;
+}
+
+static void ewx_i2c_read_prepare(ice1712_t *ice)
+{
+	/* set SCL - write, SDA - read */
+	ice->gpio_direction |= ICE1712_EWX2496_SERIAL_CLOCK;
+	ice->gpio_direction &= ~ICE1712_EWX2496_SERIAL_DATA;
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION, ice->gpio_direction);
+	/* set write mask only to SCL */
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, ~ICE1712_EWX2496_SERIAL_CLOCK);
+}
+
+static void ewx_i2c_read_post(ice1712_t *ice)
+{
+	/* reset direction both SDA and SCL to write */
+	ice->gpio_direction |= ICE1712_EWX2496_SERIAL_CLOCK|ICE1712_EWX2496_SERIAL_DATA;
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION, ice->gpio_direction);
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, ~(ICE1712_EWX2496_SERIAL_CLOCK|ICE1712_EWX2496_SERIAL_DATA));
+}
+
+/* read a byte (and ack bit) */
+static unsigned char ewx_i2c_read(ice1712_t *ice)
+{
+	int i;
+	unsigned char data = 0;
+
+	for (i = 7; i >= 0; i--) {
+		ewx_i2c_set(ice, 1, 0);
+		data |= ewx_i2c_read_bit(ice) << i;
+		/* reset write mask */
+		snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK,
+				  ~ICE1712_EWX2496_SERIAL_CLOCK);
+		ewx_i2c_set(ice, 0, 0);
+	}
+	/* reset write mask */
+	ewx_i2c_read_post(ice);
+	ewx_i2c_send(ice, 1); /* ack */
+	return data;
+}
+
+
+/*
+ * CS8427 on EWX 24/96; Address 0x20
+ */
+/* write a register */
+static void snd_ice1712_ewx_cs8427_write(ice1712_t *ice, unsigned char reg, unsigned char data)
+{
+	unsigned char saved[2];
+
+	save_gpio_status(ice, saved);
+	ewx_i2c_write_prepare(ice);
+	ewx_i2c_write(ice, 0x20);  /* address + write */
+	ewx_i2c_write(ice, reg); /* MAP */
+	ewx_i2c_write(ice, data);
+	ewx_i2c_stop(ice);
+	restore_gpio_status(ice, saved);
+}
+
+/* sequential write */
+static void snd_ice1712_ewx_cs8427_write_bytes(ice1712_t *ice, unsigned char reg, int bytes, unsigned char *data)
+{
+	unsigned char saved[2];
+
+	save_gpio_status(ice, saved);
+	ewx_i2c_write_prepare(ice);
+	ewx_i2c_write(ice, 0x20);  /* address + write */
+	ewx_i2c_write(ice, reg | 0x80); /* MAP incremental mode */
+	while (bytes-- > 0)
+		ewx_i2c_write(ice, *data++);
+	ewx_i2c_stop(ice);
+	restore_gpio_status(ice, saved);
+}
+
+/* read a register */
+static unsigned char snd_ice1712_ewx_cs8427_read(ice1712_t *ice, unsigned char reg)
+{
+	unsigned char saved[2];
+	unsigned char data;
+
+	save_gpio_status(ice, saved);
+	ewx_i2c_write_prepare(ice);
+	ewx_i2c_write(ice, 0x20);  /* address + write */
+	ewx_i2c_write(ice, reg); /* MAP */
+	/* we set up the address first but without data */
+	ewx_i2c_stop(ice);
+
+	/* now read */
+	ewx_i2c_start(ice);
+	ewx_i2c_write(ice, 0x21);  /* address + read */
+	ewx_i2c_read_prepare(ice);
+	data = ewx_i2c_read(ice);
+	ewx_i2c_read_post(ice);
+	ewx_i2c_stop(ice);
+
+	restore_gpio_status(ice, saved);
+	return data;
+}
+
+/* sequential read  */
+static void snd_ice1712_ewx_cs8427_read_bytes(ice1712_t *ice, unsigned char reg, int bytes, unsigned char *data)
+{
+#if 0 // the sequential read seems not working (yet)..
+	unsigned char saved[2];
+
+	save_gpio_status(ice, saved);
+	ewx_i2c_write_prepare(ice);
+	ewx_i2c_write(ice, 0x20);  /* address + write */
+	ewx_i2c_write(ice, reg | 0x80); /* MAP incremental mode */
+	/* we set up the address first but without data */
+	ewx_i2c_stop(ice);
+
+	/* now read */
+	ewx_i2c_start(ice);
+	ewx_i2c_write(ice, 0x21);  /* address + read */
+	ewx_i2c_read_prepare(ice);
+	while (bytes-- > 0)
+		*data++ = ewx_i2c_read(ice);
+	ewx_i2c_read_post(ice);
+	ewx_i2c_stop(ice);
+	restore_gpio_status(ice, saved);
+#else
+	while (bytes-- > 0)
+		*data++ = snd_ice1712_ewx_cs8427_read(ice, reg++); 
+#endif
+}
+
+#ifdef TARGET_OS2
+static ice1712_cs8427_ops_t snd_ice1712_ewx_cs8427_ops = {
+	snd_ice1712_ewx_cs8427_write,
+	snd_ice1712_ewx_cs8427_read,
+	snd_ice1712_ewx_cs8427_write_bytes,
+	snd_ice1712_ewx_cs8427_read_bytes,
+};
+#else
+static ice1712_cs8427_ops_t snd_ice1712_ewx_cs8427_ops = {
+	write: snd_ice1712_ewx_cs8427_write,
+	read: snd_ice1712_ewx_cs8427_read,
+	write_bytes: snd_ice1712_ewx_cs8427_write_bytes,
+	read_bytes: snd_ice1712_ewx_cs8427_read_bytes,
+};
+#endif
+
+/*
+ * PCF8574 on EWS88MT
+ */
+static void snd_ice1712_ews88mt_pcf8574_write(ice1712_t *ice, unsigned char addr, unsigned char data)
+{
+	ewx_i2c_write_prepare(ice);
+	ewx_i2c_write(ice, addr); /* address + write */
+	ewx_i2c_write(ice, data);
+	ewx_i2c_stop(ice);
+}
+
+static unsigned char snd_ice1712_ews88mt_pcf8574_read(ice1712_t *ice, unsigned char addr)
+{
+	unsigned char data;
+	ewx_i2c_write_prepare(ice);
+	ewx_i2c_write(ice, addr | 0x01);  /* address + read */
+	ewx_i2c_read_prepare(ice);
+	data = ewx_i2c_read(ice);
+	ewx_i2c_read_post(ice);
+	ewx_i2c_stop(ice);
+	return data;
+}
+
+/* AK4524 chip select; address 0x48 bit 0-3 */
+static void snd_ice1712_ews88mt_chip_select(ice1712_t *ice, int chip_mask)
+{
+	unsigned char data, ndata;
+
+	snd_assert(chip_mask >= 0 && chip_mask <= 0x0f, return);
+	data = snd_ice1712_ews88mt_pcf8574_read(ice, ICE1712_EWS88MT_OUTPUT_ADDR);
+	ndata = (data & 0xf0) | chip_mask;
+	if (ndata != data)
+		snd_ice1712_ews88mt_pcf8574_write(ice, ICE1712_EWS88MT_OUTPUT_ADDR, ndata);
+}
+
+
+/*
+ * PCF8575 on EWS88D (16bit)
+ */
+static void snd_ice1712_ews88d_pcf8575_write(ice1712_t *ice, unsigned char addr, unsigned short data)
+{
+	ewx_i2c_write_prepare(ice);
+	ewx_i2c_write(ice, addr); /* address + write */
+	ewx_i2c_write(ice, data & 0xff);
+	ewx_i2c_write(ice, (data >> 8) & 0xff);
+	ewx_i2c_stop(ice);
+}
+
+static unsigned short snd_ice1712_ews88d_pcf8575_read(ice1712_t *ice, unsigned char addr)
+{
+	unsigned short data;
+	ewx_i2c_write_prepare(ice);
+	ewx_i2c_write(ice, addr | 0x01);  /* address + read */
+	ewx_i2c_read_prepare(ice);
+	data = ewx_i2c_read(ice);
+	data |= (unsigned short)ewx_i2c_read(ice) << 8;
+	//printk("pcf: read = 0x%x\n", data);
+	ewx_i2c_read_post(ice);
+	ewx_i2c_stop(ice);
+	return data;
+}
+
+/*
+ * write AK4524 register
+ */
+static void snd_ice1712_ak4524_write(ice1712_t *ice, int chip,
+				     unsigned char addr, unsigned char data)
+{
+	unsigned char tmp, data_mask, clk_mask, saved[2];
+	unsigned char codecs_mask;
+	int idx, cif;
+	unsigned int addrdata;
+
+	snd_assert(chip >=0 && chip < 4, return);
+	if (ice->eeprom.subvendor == ICE1712_SUBDEVICE_EWS88MT) {
+		/* assert AK4524 CS */
+		snd_ice1712_ews88mt_chip_select(ice, ~(1 << chip) & 0x0f);
+		//snd_ice1712_ews88mt_chip_select(ice, 0x0f);
+	}
+
+	cif = 0; /* the default level of the CIF pin from AK4524 */
+	save_gpio_status(ice, saved);
+	tmp = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA);
+	switch (ice->eeprom.subvendor) {
+	case ICE1712_SUBDEVICE_AUDIOPHILE:
+		data_mask = ICE1712_DELTA_AP_DOUT;
+		clk_mask = ICE1712_DELTA_AP_CCLK;
+		codecs_mask = ICE1712_DELTA_AP_CS_CODEC; /* select AK4528 codec */
+		tmp |= ICE1712_DELTA_AP_CS_DIGITAL; /* assert digital high */
+		break;
+	case ICE1712_SUBDEVICE_EWX2496:
+		data_mask = ICE1712_EWX2496_SERIAL_DATA;
+		clk_mask = ICE1712_EWX2496_SERIAL_CLOCK;
+		codecs_mask = ICE1712_EWX2496_AK4524_CS;
+		tmp |= ICE1712_EWX2496_RW; /* set rw bit high */
+		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION,
+				  ice->gpio_direction | data_mask | clk_mask |
+				  codecs_mask | ICE1712_EWX2496_RW);
+		snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK,
+				  ~(data_mask | clk_mask |
+				    codecs_mask | ICE1712_EWX2496_RW));
+		cif = 1; /* CIF high */
+		break;
+	case ICE1712_SUBDEVICE_EWS88MT:
+		data_mask = ICE1712_EWS88_SERIAL_DATA;
+		clk_mask = ICE1712_EWS88_SERIAL_CLOCK;
+		codecs_mask = 0; /* no chip select on gpio */
+		tmp |= ICE1712_EWS88_RW; /* set rw bit high */
+		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION,
+				  ice->gpio_direction | data_mask | clk_mask |
+				  codecs_mask | ICE1712_EWS88_RW);
+		snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK,
+				  ~(data_mask | clk_mask |
+				    codecs_mask | ICE1712_EWS88_RW));
+		cif = 1; /* CIF high */
+		break;
+	default:
+		data_mask = ICE1712_DELTA_CODEC_SERIAL_DATA;
+		clk_mask = ICE1712_DELTA_CODEC_SERIAL_CLOCK;
+		codecs_mask = chip == 0 ? ICE1712_DELTA_CODEC_CHIP_A : ICE1712_DELTA_CODEC_CHIP_B;
+		break;
+	}
+
+	if (cif) {
+		tmp |= codecs_mask; /* start without chip select */
+	} else {
+		tmp &= ~codecs_mask; /* chip select low */
+		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
+		udelay(1);
+	}
+
+	addr &= 0x07;
+	/* build I2C address + data byte */
+	addrdata = 0xa000 | (addr << 8) | data;
+	for (idx = 15; idx >= 0; idx--) {
+		tmp &= ~(data_mask|clk_mask);
+		if (addrdata & (1 << idx))
+			tmp |= data_mask;
+		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
+		//udelay(200);
+		udelay(1);
+		tmp |= clk_mask;
+		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
+		udelay(1);
+	}
+
+	if (ice->eeprom.subvendor == ICE1712_SUBDEVICE_EWS88MT) {
+		//snd_ice1712_ews88mt_chip_select(ice, ~(1 << chip) & 0x0f);
+		udelay(1);
+		snd_ice1712_ews88mt_chip_select(ice, 0x0f);
+	} else {
+		if (cif) {
+			/* assert a cs pulse to trigger */
+			tmp &= ~codecs_mask;
+			snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
+			udelay(1);
+		}
+		tmp |= codecs_mask; /* chip select high to trigger */
+		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
+		udelay(1);
+	}
+
+	ice->ak4524_images[chip][addr] = data;
+	restore_gpio_status(ice, saved);
+}
+
+
+/*
+ * decode/encode channel status bits from CS8404A on EWS88MT&D
+ */
+static void snd_ice1712_ews_decode_cs8404_spdif_bits(snd_aes_iec958_t *diga,
+						     unsigned char bits)
+{
+	if (bits & 0x10) {	/* consumer */
+		if (!(bits & 0x20))
+			diga->status[0] |= IEC958_AES0_CON_NOT_COPYRIGHT;
+		if (!(bits & 0x40))
+			diga->status[0] |= IEC958_AES0_CON_EMPHASIS_5015;
+		if (!(bits & 0x80))
+			diga->status[1] |= IEC958_AES1_CON_ORIGINAL;
+		switch (bits & 0x03) {
+		case 0x00: diga->status[1] |= IEC958_AES1_CON_DAT; break;
+		case 0x03: diga->status[1] |= IEC958_AES1_CON_GENERAL; break;
+		}
+		switch (bits & 0x06) {
+		case 0x02: diga->status[3] |= IEC958_AES3_CON_FS_32000; break;
+		case 0x04: diga->status[3] |= IEC958_AES3_CON_FS_48000; break;
+		case 0x06: diga->status[3] |= IEC958_AES3_CON_FS_44100; break;
+		}
+	} else {
+		diga->status[0] = IEC958_AES0_PROFESSIONAL;
+		if (!(bits & 0x04))
+			diga->status[0] |= IEC958_AES0_NONAUDIO;
+		switch (bits & 0x60) {
+		case 0x00: diga->status[0] |= IEC958_AES0_PRO_FS_32000; break;
+		case 0x40: diga->status[0] |= IEC958_AES0_PRO_FS_44100; break;
+		case 0x20: diga->status[0] |= IEC958_AES0_PRO_FS_48000; break;
+		case 0x60: diga->status[0] |= IEC958_AES0_PRO_FS_NOTID; break;
+		}
+		switch (bits & 0x03) {
+		case 0x02: diga->status[0] |= IEC958_AES0_PRO_EMPHASIS_NONE; break;
+		case 0x01: diga->status[0] |= IEC958_AES0_PRO_EMPHASIS_5015; break;
+		case 0x00: diga->status[0] |= IEC958_AES0_PRO_EMPHASIS_CCITT; break;
+		case 0x03: diga->status[0] |= IEC958_AES0_PRO_EMPHASIS_NOTID; break;
+		}
+		if (!(bits & 0x80))
+			diga->status[1] |= IEC958_AES1_PRO_MODE_STEREOPHONIC;
+	}
+}
+
+static unsigned char snd_ice1712_ews_encode_cs8404_spdif_bits(snd_aes_iec958_t *diga)
+{
+	unsigned char bits;
+
+	if (!(diga->status[0] & IEC958_AES0_PROFESSIONAL)) {
+		bits = 0x10;	/* consumer mode */
+		if (!(diga->status[0] & IEC958_AES0_CON_NOT_COPYRIGHT))
+			bits |= 0x20;
+		if ((diga->status[0] & IEC958_AES0_CON_EMPHASIS) == IEC958_AES0_CON_EMPHASIS_NONE)
+			bits |= 0x40;
+		if (!(diga->status[1] & IEC958_AES1_CON_ORIGINAL))
+			bits |= 0x80;
+		if ((diga->status[1] & IEC958_AES1_CON_CATEGORY) == IEC958_AES1_CON_GENERAL)
+			bits |= 0x03;
+		switch (diga->status[3] & IEC958_AES3_CON_FS) {
+		default:
+		case IEC958_AES3_CON_FS_44100: bits |= 0x06; break;
+		case IEC958_AES3_CON_FS_48000: bits |= 0x04; break;
+		case IEC958_AES3_CON_FS_32000: bits |= 0x02; break;
+		}
+	} else {
+		bits = 0x00;	/* professional mode */
+		if (!(diga->status[0] & IEC958_AES0_NONAUDIO))
+			bits |= 0x04;
+		switch (diga->status[0] & IEC958_AES0_PRO_FS) {
+		case IEC958_AES0_PRO_FS_32000:	bits |= 0x00; break;
+		case IEC958_AES0_PRO_FS_44100:	bits |= 0x40; break;	/* 44.1kHz */
+		case IEC958_AES0_PRO_FS_48000:	bits |= 0x20; break;	/* 48kHz */
+		default:
+		case IEC958_AES0_PRO_FS_NOTID:	bits |= 0x00; break;
+		}
+		switch (diga->status[0] & IEC958_AES0_PRO_EMPHASIS) {
+		case IEC958_AES0_PRO_EMPHASIS_NONE: bits |= 0x02; break;
+		case IEC958_AES0_PRO_EMPHASIS_5015: bits |= 0x01; break;
+		case IEC958_AES0_PRO_EMPHASIS_CCITT: bits |= 0x00; break;
+		default:
+		case IEC958_AES0_PRO_EMPHASIS_NOTID: bits |= 0x03; break;
+		}
+		switch (diga->status[1] & IEC958_AES1_PRO_MODE) {
+		case IEC958_AES1_PRO_MODE_TWO:
+		case IEC958_AES1_PRO_MODE_STEREOPHONIC: bits |= 0x00; break;
+		default: bits |= 0x80; break;
+		}
+	}
+	return bits;
+}
+
+/* write on i2c address 0x40 */
+static void snd_ice1712_ews_cs8404_spdif_write(ice1712_t *ice, unsigned char bits)
+{
+	unsigned short val, nval;
+
+	switch (ice->eeprom.subvendor) {
+	case ICE1712_SUBDEVICE_EWS88MT:
+		snd_ice1712_ews88mt_pcf8574_write(ice, ICE1712_EWS88MT_CS8404_ADDR, bits);
+		break;
+	case ICE1712_SUBDEVICE_EWS88D:
+		val = snd_ice1712_ews88d_pcf8575_read(ice, ICE1712_EWS88D_PCF_ADDR);
+		nval = val & 0xff;
+		nval |= bits << 8;
+		if (val != nval)
+			snd_ice1712_ews88d_pcf8575_write(ice, ICE1712_EWS88D_PCF_ADDR, nval);
+		break;
+	}
+}
+
+
+/*
+ * change the input clock selection
+ * spdif_clock = 1 - IEC958 input, 0 - Envy24
+ */
+static int snd_ice1712_cs8427_set_input_clock(ice1712_t *ice, int spdif_clock)
+{
+	unsigned char val, nval;
+	val = ice->cs8427_ops->read(ice, 4);
+	nval = val & 0xf0;
+	if (spdif_clock)
+		nval |= 0x01;
+	else
+		nval |= 0x04;
+	if (val != nval) {
+		ice->cs8427_ops->write(ice, 4, nval);
+		return 1;
+	}
+	return 0;
+}
+
+#if 0 // we change clock selection automatically according to the ice1712 clock source
+static int snd_ice1712_cs8427_clock_select_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_ice1712_cs8427_clock_select_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	int val;
+	val = ice->cs8427_ops->read(ice, 4);
+	ucontrol->value.integer.value[0] = (val & 0x01) ? 1 : 0;
+	return 0;
+}
+
+static int snd_ice1712_cs8427_clock_select_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	return snd_ice1712_cs8427_set_input_clock(ice, ucontrol->value.integer.value[0]);
+}
+
+static snd_kcontrol_new_t snd_ice1712_cs8427_clock_select = {
+	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	name: "CS8427 Clock Select",
+	info: snd_ice1712_cs8427_clock_select_info,
+	get: snd_ice1712_cs8427_clock_select_get,
+	put: snd_ice1712_cs8427_clock_select_put,
+};
+#endif
+
+
+/*
+ */
+static int snd_ice1712_cs8427_in_status_info(snd_kcontrol_t *kcontrol,
+					     snd_ctl_elem_info_t *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 255;
+	return 0;
+}
+
+static int snd_ice1712_cs8427_in_status_get(snd_kcontrol_t *kcontrol,
+					    snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	ucontrol->value.integer.value[0] = ice->cs8427_ops->read(ice, 15);
+	return 0;
+}
+
+static snd_kcontrol_new_t snd_ice1712_cs8427_in_status = {
+#ifdef TARGET_OS2
+	SNDRV_CTL_ELEM_IFACE_PCM,0,0,
+	"IEC958 Input Status",0,
+	SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+	snd_ice1712_cs8427_in_status_info,
+	snd_ice1712_cs8427_in_status_get,0,0
+#else
+	iface: SNDRV_CTL_ELEM_IFACE_PCM,
+	info: snd_ice1712_cs8427_in_status_info,
+	name: "IEC958 Input Status",
+	access: SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+	get: snd_ice1712_cs8427_in_status_get,
+#endif
+};
+
+
+/*
+ */
+static void snd_ice1712_set_pro_rate(ice1712_t *ice, snd_pcm_substream_t *substream)
+{
+	unsigned long flags;
+	unsigned int rate;
+	unsigned char val, tmp;
+
+	spin_lock_irqsave(&ice->reg_lock, flags);
+	if (inb(ICEMT(ice, PLAYBACK_CONTROL)) & (ICE1712_CAPTURE_START_SHADOW|
+						 ICE1712_PLAYBACK_PAUSE|
+						 ICE1712_PLAYBACK_START))
+		goto __end;
+	if (inb(ICEMT(ice, RATE)) & ICE1712_SPDIF_MASTER)
+		goto __end;
+	rate = substream->runtime->rate;
+	switch (rate) {
+	case 8000: val = 6; break;
+	case 9600: val = 3; break;
+	case 11025: val = 10; break;
+	case 12000: val = 2; break;
+	case 16000: val = 5; break;
+	case 22050: val = 9; break;
+	case 24000: val = 1; break;
+	case 32000: val = 4; break;
+	case 44100: val = 8; break;
+	case 48000: val = 0; break;
+	case 64000: val = 15; break;
+	case 88200: val = 11; break;
+	case 96000: val = 7; break;
+	default:
+		snd_BUG();
+		val = 0;
+		break;
+	}
+	outb(val, ICEMT(ice, RATE));
+	switch (ice->eeprom.subvendor) {
+	case ICE1712_SUBDEVICE_DELTA1010:
+	case ICE1712_SUBDEVICE_DELTADIO2496:
+	case ICE1712_SUBDEVICE_DELTA66:
+	case ICE1712_SUBDEVICE_DELTA44:
+	case ICE1712_SUBDEVICE_AUDIOPHILE:
+		/* FIXME: we should put AK452x codecs to reset state
+		   when this bit is being changed */
+		spin_unlock_irqrestore(&ice->reg_lock, flags);
+		down(&ice->gpio_mutex);
+		tmp = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA);
+		if (val == 15 || val == 11 || val == 7) {
+			tmp |= ICE1712_DELTA_DFS;
+		} else {
+			tmp &= ~ICE1712_DELTA_DFS;
+		}
+		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
+		up(&ice->gpio_mutex);
+		return;
+	}
+      __end:
+	spin_unlock_irqrestore(&ice->reg_lock, flags);
+}
+
+/*
+ *  Interrupt handler
+ */
+
+static void snd_ice1712_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	ice1712_t *ice = snd_magic_cast(ice1712_t, dev_id, return);
+	unsigned char status;
+
+	while (1) {
+		status = inb(ICEREG(ice, IRQSTAT));
+		if (status == 0)
+			break;
+		if (status & ICE1712_IRQ_MPU1) {
+			if (ice->rmidi[0])
+				snd_mpu401_uart_interrupt(irq, ice->rmidi[0]->private_data, regs);
+			outb(ICE1712_IRQ_MPU1, ICEREG(ice, IRQSTAT));
+			status &= ~ICE1712_IRQ_MPU1;
+		}
+		if (status & ICE1712_IRQ_TIMER)
+			outb(ICE1712_IRQ_TIMER, ICEREG(ice, IRQSTAT));
+		if (status & ICE1712_IRQ_MPU2) {
+			if (ice->rmidi[1])
+				snd_mpu401_uart_interrupt(irq, ice->rmidi[1]->private_data, regs);
+			outb(ICE1712_IRQ_MPU2, ICEREG(ice, IRQSTAT));
+			status &= ~ICE1712_IRQ_MPU2;
+		}
+		if (status & ICE1712_IRQ_PROPCM) {
+			unsigned char mtstat = inb(ICEMT(ice, IRQ));
+			if (mtstat & ICE1712_MULTI_PBKSTATUS) {
+				if (ice->playback_pro_substream)
+					snd_pcm_period_elapsed(ice->playback_pro_substream);
+				outb(ICE1712_MULTI_PBKSTATUS, ICEMT(ice, IRQ));
+			}
+			if (mtstat & ICE1712_MULTI_CAPSTATUS) {
+				if (ice->capture_pro_substream)
+					snd_pcm_period_elapsed(ice->capture_pro_substream);
+				outb(ICE1712_MULTI_CAPSTATUS, ICEMT(ice, IRQ));
+			}
+		}
+		if (status & ICE1712_IRQ_FM)
+			outb(ICE1712_IRQ_FM, ICEREG(ice, IRQSTAT));
+		if (status & ICE1712_IRQ_PBKDS) {
+			u32 idx;
+			u16 pbkstatus;
+			snd_pcm_substream_t *substream;
+			pbkstatus = inw(ICEDS(ice, INTSTAT));
+			//printk("pbkstatus = 0x%x\n", pbkstatus);
+			for (idx = 0; idx < 6; idx++) {
+				if ((pbkstatus & (3 << (idx * 2))) == 0)
+					continue;
+				if ((substream = ice->playback_con_substream_ds[idx]) != NULL)
+					snd_pcm_period_elapsed(substream);
+				outw(3 << (idx * 2), ICEDS(ice, INTSTAT));
+			}
+			outb(ICE1712_IRQ_PBKDS, ICEREG(ice, IRQSTAT));
+		}
+		if (status & ICE1712_IRQ_CONCAP) {
+			if (ice->capture_con_substream)
+				snd_pcm_period_elapsed(ice->capture_con_substream);
+			outb(ICE1712_IRQ_CONCAP, ICEREG(ice, IRQSTAT));
+		}
+		if (status & ICE1712_IRQ_CONPBK) {
+			if (ice->playback_con_substream)
+				snd_pcm_period_elapsed(ice->playback_con_substream);
+			outb(ICE1712_IRQ_CONPBK, ICEREG(ice, IRQSTAT));
+		}
+	}
+}
+
+/*
+ *  PCM part - misc
+ */
+
+static int snd_ice1712_hw_params(snd_pcm_substream_t * substream,
+				 snd_pcm_hw_params_t * hw_params)
+{
+	return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+static int snd_ice1712_hw_free(snd_pcm_substream_t * substream)
+{
+	return snd_pcm_lib_free_pages(substream);
+}
+
+/*
+ *  PCM part - consumer I/O
+ */
+
+static int snd_ice1712_playback_trigger(snd_pcm_substream_t * substream,
+					int cmd)
+{
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+	int result = 0;
+	u32 tmp;
+	
+	spin_lock(&ice->reg_lock);
+	tmp = snd_ice1712_read(ice, ICE1712_IREG_PBK_CTRL);
+	if (cmd == SNDRV_PCM_TRIGGER_START) {
+		tmp |= 1;
+	} else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+		tmp &= ~1;
+	} else if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH) {
+		tmp |= 2;
+	} else if (cmd == SNDRV_PCM_TRIGGER_PAUSE_RELEASE) {
+		tmp &= ~2;
+	} else {
+		result = -EINVAL;
+	}
+	snd_ice1712_write(ice, ICE1712_IREG_PBK_CTRL, tmp);
+	spin_unlock(&ice->reg_lock);
+	return result;
+}
+
+static int snd_ice1712_playback_ds_trigger(snd_pcm_substream_t * substream,
+					   int cmd)
+{
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+	int result = 0;
+	u32 tmp;
+	
+	spin_lock(&ice->reg_lock);
+	tmp = snd_ice1712_ds_read(ice, substream->number * 2, ICE1712_DSC_CONTROL);
+	if (cmd == SNDRV_PCM_TRIGGER_START) {
+		tmp |= 1;
+	} else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+		tmp &= ~1;
+	} else if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH) {
+		tmp |= 2;
+	} else if (cmd == SNDRV_PCM_TRIGGER_PAUSE_RELEASE) {
+		tmp &= ~2;
+	} else {
+		result = -EINVAL;
+	}
+	snd_ice1712_ds_write(ice, substream->number * 2, ICE1712_DSC_CONTROL, tmp);
+	spin_unlock(&ice->reg_lock);
+	return result;
+}
+
+static int snd_ice1712_capture_trigger(snd_pcm_substream_t * substream,
+				       int cmd)
+{
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+	int result = 0;
+	u8 tmp;
+	
+	spin_lock(&ice->reg_lock);
+	tmp = snd_ice1712_read(ice, ICE1712_IREG_CAP_CTRL);
+	if (cmd == SNDRV_PCM_TRIGGER_START) {
+		tmp |= 1;
+	} else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+		tmp &= ~1;
+	} else {
+		result = -EINVAL;
+	}
+	snd_ice1712_write(ice, ICE1712_IREG_CAP_CTRL, tmp);
+	spin_unlock(&ice->reg_lock);
+	return result;
+}
+
+static int snd_ice1712_playback_prepare(snd_pcm_substream_t * substream)
+{
+	unsigned long flags;
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	u32 period_size, buf_size, rate, tmp;
+
+	period_size = (snd_pcm_lib_period_bytes(substream) >> 2) - 1;
+	buf_size = snd_pcm_lib_buffer_bytes(substream) - 1;
+	tmp = 0x0000;
+	if (snd_pcm_format_width(runtime->format) == 16)
+		tmp |= 0x10;
+	if (runtime->channels == 2)
+		tmp |= 0x08;
+	rate = (runtime->rate * 8192) / 375;
+	if (rate > 0x000fffff)
+		rate = 0x000fffff;
+	spin_lock_irqsave(&ice->reg_lock, flags);
+	outb(0, ice->ddma_port + 15);
+	outb(ICE1712_DMA_MODE_WRITE | ICE1712_DMA_AUTOINIT, ice->ddma_port + 0x0b);
+	outl(runtime->dma_addr, ice->ddma_port + 0);
+	outw(buf_size, ice->ddma_port + 4);
+	snd_ice1712_write(ice, ICE1712_IREG_PBK_RATE_LO, rate & 0xff);
+	snd_ice1712_write(ice, ICE1712_IREG_PBK_RATE_MID, (rate >> 8) & 0xff);
+	snd_ice1712_write(ice, ICE1712_IREG_PBK_RATE_HI, (rate >> 16) & 0xff);
+	snd_ice1712_write(ice, ICE1712_IREG_PBK_CTRL, tmp);
+	snd_ice1712_write(ice, ICE1712_IREG_PBK_COUNT_LO, period_size & 0xff);
+	snd_ice1712_write(ice, ICE1712_IREG_PBK_COUNT_HI, period_size >> 8);
+	snd_ice1712_write(ice, ICE1712_IREG_PBK_LEFT, 0);
+	snd_ice1712_write(ice, ICE1712_IREG_PBK_RIGHT, 0);
+	spin_unlock_irqrestore(&ice->reg_lock, flags);
+	return 0;
+}
+
+static int snd_ice1712_playback_ds_prepare(snd_pcm_substream_t * substream)
+{
+	unsigned long flags;
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	u32 period_size, buf_size, rate, tmp, chn;
+
+	period_size = snd_pcm_lib_period_bytes(substream) - 1;
+	buf_size = snd_pcm_lib_buffer_bytes(substream) - 1;
+	tmp = 0x0064;
+	if (snd_pcm_format_width(runtime->format) == 16)
+		tmp &= ~0x04;
+	if (runtime->channels == 2)
+		tmp |= 0x08;
+	rate = (runtime->rate * 8192) / 375;
+	if (rate > 0x000fffff)
+		rate = 0x000fffff;
+	ice->playback_con_active_buf[substream->number] = 0;
+	ice->playback_con_virt_addr[substream->number] = runtime->dma_addr;
+	chn = substream->number * 2;
+	spin_lock_irqsave(&ice->reg_lock, flags);
+	snd_ice1712_ds_write(ice, chn, ICE1712_DSC_ADDR0, runtime->dma_addr);
+	snd_ice1712_ds_write(ice, chn, ICE1712_DSC_COUNT0, period_size);
+	snd_ice1712_ds_write(ice, chn, ICE1712_DSC_ADDR1, runtime->dma_addr + (runtime->periods > 1 ? period_size + 1 : 0));
+	snd_ice1712_ds_write(ice, chn, ICE1712_DSC_COUNT1, period_size);
+	snd_ice1712_ds_write(ice, chn, ICE1712_DSC_RATE, rate);
+	snd_ice1712_ds_write(ice, chn, ICE1712_DSC_VOLUME, 0);
+	snd_ice1712_ds_write(ice, chn, ICE1712_DSC_CONTROL, tmp);
+	if (runtime->channels == 2) {
+		snd_ice1712_ds_write(ice, chn + 1, ICE1712_DSC_RATE, rate);
+		snd_ice1712_ds_write(ice, chn + 1, ICE1712_DSC_VOLUME, 0);
+	}
+	spin_unlock_irqrestore(&ice->reg_lock, flags);
+	return 0;
+}
+
+static int snd_ice1712_capture_prepare(snd_pcm_substream_t * substream)
+{
+	unsigned long flags;
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	u32 period_size, buf_size;
+	u8 tmp;
+
+	period_size = (snd_pcm_lib_period_bytes(substream) >> 2) - 1;
+	buf_size = snd_pcm_lib_buffer_bytes(substream) - 1;
+	tmp = 0x06;
+	if (snd_pcm_format_width(runtime->format) == 16)
+		tmp &= ~0x04;
+	if (runtime->channels == 2)
+		tmp &= ~0x02;
+	spin_lock_irqsave(&ice->reg_lock, flags);
+	outl(ice->capture_con_virt_addr = runtime->dma_addr, ICEREG(ice, CONCAP_ADDR));
+	outw(buf_size, ICEREG(ice, CONCAP_COUNT));
+	snd_ice1712_write(ice, ICE1712_IREG_CAP_COUNT_HI, period_size >> 8);
+	snd_ice1712_write(ice, ICE1712_IREG_CAP_COUNT_LO, period_size & 0xff);
+	snd_ice1712_write(ice, ICE1712_IREG_CAP_CTRL, tmp);
+	spin_unlock_irqrestore(&ice->reg_lock, flags);
+	snd_ac97_set_rate(ice->ac97, AC97_PCM_LR_ADC_RATE, runtime->rate);
+	return 0;
+}
+
+static snd_pcm_uframes_t snd_ice1712_playback_pointer(snd_pcm_substream_t * substream)
+{
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	size_t ptr;
+
+	if (!(snd_ice1712_read(ice, ICE1712_IREG_PBK_CTRL) & 1))
+		return 0;
+	ptr = runtime->buffer_size - inw(ice->ddma_port + 4);
+	return bytes_to_frames(substream->runtime, ptr);
+}
+
+static snd_pcm_uframes_t snd_ice1712_playback_ds_pointer(snd_pcm_substream_t * substream)
+{
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+	u8 addr;
+	size_t ptr;
+
+	if (!(snd_ice1712_ds_read(ice, substream->number * 2, ICE1712_DSC_CONTROL) & 1))
+		return 0;
+	if (ice->playback_con_active_buf[substream->number])
+		addr = ICE1712_DSC_ADDR1;
+	else
+		addr = ICE1712_DSC_ADDR0;
+	ptr = snd_ice1712_ds_read(ice, substream->number * 2, addr) -
+		ice->playback_con_virt_addr[substream->number];
+	return bytes_to_frames(substream->runtime, ptr);
+}
+
+static snd_pcm_uframes_t snd_ice1712_capture_pointer(snd_pcm_substream_t * substream)
+{
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+	size_t ptr;
+
+	if (!(snd_ice1712_read(ice, ICE1712_IREG_CAP_CTRL) & 1))
+		return 0;
+	ptr = inl(ICEREG(ice, CONCAP_ADDR)) - ice->capture_con_virt_addr;
+	return bytes_to_frames(substream->runtime, ptr);
+}
+
+#ifdef TARGET_OS2
+static snd_pcm_hardware_t snd_ice1712_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:		  */	SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+/*	rate_min:	  */	4000,
+/*	rate_max:	  */	48000,
+/*	channels_min:	  */	1,
+/*	channels_max:	  */	2,
+/*	buffer_bytes_max:  */	(64*1024),
+/*	period_bytes_min:  */	64,
+/*	period_bytes_max:  */	(64*1024),
+/*	periods_min:	  */	1,
+/*	periods_max:	  */	1024,
+/*	fifo_size:	  */	0,
+};
+
+static snd_pcm_hardware_t snd_ice1712_playback_ds =
+{
+/*	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:		  */	SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+/*	rate_min:	  */	4000,
+/*	rate_max:	  */	48000,
+/*	channels_min:	  */	1,
+/*	channels_max:	  */	2,
+/*	buffer_bytes_max:  */	(128*1024),
+/*	period_bytes_min:  */	64,
+/*	period_bytes_max:  */	(128*1024),
+/*	periods_min:	  */	2,
+/*	periods_max:	  */	2,
+/*	fifo_size:	  */	0,
+};
+
+static snd_pcm_hardware_t snd_ice1712_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:		  */	SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+/*	rate_min:	  */	4000,
+/*	rate_max:	  */	48000,
+/*	channels_min:	  */	1,
+/*	channels_max:	  */	2,
+/*	buffer_bytes_max:  */	(64*1024),
+/*	period_bytes_min:  */	64,
+/*	period_bytes_max:  */	(64*1024),
+/*	periods_min:	  */	1,
+/*	periods_max:	  */	1024,
+/*	fifo_size:	  */	0,
+};
+#else
+static snd_pcm_hardware_t snd_ice1712_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:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	rate_min:		4000,
+	rate_max:		48000,
+	channels_min:		1,
+	channels_max:		2,
+	buffer_bytes_max:	(64*1024),
+	period_bytes_min:	64,
+	period_bytes_max:	(64*1024),
+	periods_min:		1,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+
+static snd_pcm_hardware_t snd_ice1712_playback_ds =
+{
+	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:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	rate_min:		4000,
+	rate_max:		48000,
+	channels_min:		1,
+	channels_max:		2,
+	buffer_bytes_max:	(128*1024),
+	period_bytes_min:	64,
+	period_bytes_max:	(128*1024),
+	periods_min:		2,
+	periods_max:		2,
+	fifo_size:		0,
+};
+
+static snd_pcm_hardware_t snd_ice1712_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:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	rate_min:		4000,
+	rate_max:		48000,
+	channels_min:		1,
+	channels_max:		2,
+	buffer_bytes_max:	(64*1024),
+	period_bytes_min:	64,
+	period_bytes_max:	(64*1024),
+	periods_min:		1,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+#endif
+
+static int snd_ice1712_playback_open(snd_pcm_substream_t * substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+
+	ice->playback_con_substream = substream;
+	runtime->hw = snd_ice1712_playback;
+	return 0;
+}
+
+static int snd_ice1712_playback_ds_open(snd_pcm_substream_t * substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+	unsigned long flags;
+	u32 tmp;
+
+	ice->playback_con_substream_ds[substream->number] = substream;
+	runtime->hw = snd_ice1712_playback_ds;
+	spin_lock_irqsave(&ice->reg_lock, flags); 
+	tmp = inw(ICEDS(ice, INTMASK)) & ~(1 << (substream->number * 2));
+	outw(tmp, ICEDS(ice, INTMASK));
+	spin_unlock_irqrestore(&ice->reg_lock, flags);
+	return 0;
+}
+
+static int snd_ice1712_capture_open(snd_pcm_substream_t * substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+
+	ice->capture_con_substream = substream;
+	runtime->hw = snd_ice1712_capture;
+	runtime->hw.rates = ice->ac97->rates_adc;
+	if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000))
+		runtime->hw.rate_min = 48000;
+	return 0;
+}
+
+static int snd_ice1712_playback_close(snd_pcm_substream_t * substream)
+{
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+
+	ice->playback_con_substream = NULL;
+	return 0;
+}
+
+static int snd_ice1712_playback_ds_close(snd_pcm_substream_t * substream)
+{
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+	unsigned long flags;
+	u32 tmp;
+
+	spin_lock_irqsave(&ice->reg_lock, flags); 
+	tmp = inw(ICEDS(ice, INTMASK)) | (3 << (substream->number * 2));
+	outw(tmp, ICEDS(ice, INTMASK));
+	spin_unlock_irqrestore(&ice->reg_lock, flags);
+	ice->playback_con_substream_ds[substream->number] = NULL;
+	return 0;
+}
+
+static int snd_ice1712_capture_close(snd_pcm_substream_t * substream)
+{
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+
+	/* disable ADC power */
+	snd_ac97_update_bits(ice->ac97, AC97_POWERDOWN, 0x0100, 0x0100);
+	ice->capture_con_substream = NULL;
+	return 0;
+}
+
+#ifdef TARGET_OS2
+static snd_pcm_ops_t snd_ice1712_playback_ops = {
+	snd_ice1712_playback_open,
+	snd_ice1712_playback_close,
+	snd_pcm_lib_ioctl,
+	snd_ice1712_hw_params,
+	snd_ice1712_hw_free,
+	snd_ice1712_playback_prepare,
+	snd_ice1712_playback_trigger,
+	snd_ice1712_playback_pointer,0,0
+};
+
+static snd_pcm_ops_t snd_ice1712_playback_ds_ops = {
+	snd_ice1712_playback_ds_open,
+	snd_ice1712_playback_ds_close,
+	snd_pcm_lib_ioctl,
+	snd_ice1712_hw_params,
+	snd_ice1712_hw_free,
+	snd_ice1712_playback_ds_prepare,
+	snd_ice1712_playback_ds_trigger,
+	snd_ice1712_playback_ds_pointer,0,0
+};
+
+static snd_pcm_ops_t snd_ice1712_capture_ops = {
+	snd_ice1712_capture_open,
+	snd_ice1712_capture_close,
+	snd_pcm_lib_ioctl,
+	snd_ice1712_hw_params,
+	snd_ice1712_hw_free,
+	snd_ice1712_capture_prepare,
+	snd_ice1712_capture_trigger,
+	snd_ice1712_capture_pointer,0,0
+};
+#else
+static snd_pcm_ops_t snd_ice1712_playback_ops = {
+	open:		snd_ice1712_playback_open,
+	close:		snd_ice1712_playback_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	hw_params:	snd_ice1712_hw_params,
+	hw_free:	snd_ice1712_hw_free,
+	prepare:	snd_ice1712_playback_prepare,
+	trigger:	snd_ice1712_playback_trigger,
+	pointer:	snd_ice1712_playback_pointer,
+};
+
+static snd_pcm_ops_t snd_ice1712_playback_ds_ops = {
+	open:		snd_ice1712_playback_ds_open,
+	close:		snd_ice1712_playback_ds_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	hw_params:	snd_ice1712_hw_params,
+	hw_free:	snd_ice1712_hw_free,
+	prepare:	snd_ice1712_playback_ds_prepare,
+	trigger:	snd_ice1712_playback_ds_trigger,
+	pointer:	snd_ice1712_playback_ds_pointer,
+};
+
+static snd_pcm_ops_t snd_ice1712_capture_ops = {
+	open:		snd_ice1712_capture_open,
+	close:		snd_ice1712_capture_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	hw_params:	snd_ice1712_hw_params,
+	hw_free:	snd_ice1712_hw_free,
+	prepare:	snd_ice1712_capture_prepare,
+	trigger:	snd_ice1712_capture_trigger,
+	pointer:	snd_ice1712_capture_pointer,
+};
+#endif
+
+static void __exit snd_ice1712_pcm_free(snd_pcm_t *pcm)
+{
+	ice1712_t *ice = snd_magic_cast(ice1712_t, pcm->private_data, return);
+	ice->pcm = NULL;
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static int __init snd_ice1712_pcm(ice1712_t * ice, int device, snd_pcm_t ** rpcm)
+{
+	snd_pcm_t *pcm;
+	int err;
+
+	if (rpcm)
+		*rpcm = NULL;
+	err = snd_pcm_new(ice->card, "ICE1712 consumer", device, 1, 1, &pcm);
+	if (err < 0)
+		return err;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ice1712_playback_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ice1712_capture_ops);
+
+	pcm->private_data = ice;
+	pcm->private_free = snd_ice1712_pcm_free;
+	pcm->info_flags = 0;
+	strcpy(pcm->name, "ICE1712 consumer");
+	ice->pcm = pcm;
+
+	snd_pcm_lib_preallocate_pci_pages_for_all(ice->pci, pcm, 64*1024, 64*1024);
+
+	if (rpcm)
+		*rpcm = pcm;
+
+	printk("Consumer PCM code does not work well at the moment --jk\n");
+
+	return 0;
+}
+
+static void __exit snd_ice1712_pcm_free_ds(snd_pcm_t *pcm)
+{
+	ice1712_t *ice = snd_magic_cast(ice1712_t, pcm->private_data, return);
+	ice->pcm_ds = NULL;
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static int __init snd_ice1712_pcm_ds(ice1712_t * ice, int device, snd_pcm_t ** rpcm)
+{
+	snd_pcm_t *pcm;
+	int err;
+
+	if (rpcm)
+		*rpcm = NULL;
+	err = snd_pcm_new(ice->card, "ICE1712 consumer (DS)", device, 6, 0, &pcm);
+	if (err < 0)
+		return err;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ice1712_playback_ds_ops);
+
+	pcm->private_data = ice;
+	pcm->private_free = snd_ice1712_pcm_free_ds;
+	pcm->info_flags = 0;
+	strcpy(pcm->name, "ICE1712 consumer (DS)");
+	ice->pcm_ds = pcm;
+
+	snd_pcm_lib_preallocate_pci_pages_for_all(ice->pci, pcm, 64*1024, 128*1024);
+
+	if (rpcm)
+		*rpcm = pcm;
+
+	return 0;
+}
+
+/*
+ *  PCM code - professional part (multitrack)
+ */
+
+static unsigned int rates[] = { 8000, 9600, 11025, 12000, 16000, 22050, 24000,
+				32000, 44100, 48000, 64000, 88200, 96000 };
+
+#define RATES sizeof(rates) / sizeof(rates[0])
+
+#ifdef TARGET_OS2
+static snd_pcm_hw_constraint_list_t hw_constraints_rates = {
+	RATES,
+	rates,
+	0,
+};
+#else
+static snd_pcm_hw_constraint_list_t hw_constraints_rates = {
+	count: RATES,
+	list: rates,
+	mask: 0,
+};
+#endif
+
+#if 0
+
+static int snd_ice1712_playback_pro_ioctl(snd_pcm_substream_t * substream,
+					  unsigned int cmd,
+					  void *arg)
+{
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+
+	switch (cmd) {
+	case SNDRV_PCM_IOCTL1_DIG_INFO:
+	{
+		snd_pcm_dig_info_t *info = arg;
+		switch (ice->eeprom.subvendor) {
+		case ICE1712_SUBDEVICE_DELTA1010:
+		case ICE1712_SUBDEVICE_DELTADIO2496:
+		case ICE1712_SUBDEVICE_DELTA66:
+			return snd_ice1712_dig_cs8403_info(substream, info);
+		}
+	}
+	case SNDRV_PCM_IOCTL1_DIG_PARAMS:
+	{
+		snd_pcm_dig_params_t *params = arg;
+		switch (ice->eeprom.subvendor) {
+		case ICE1712_SUBDEVICE_DELTA1010:
+		case ICE1712_SUBDEVICE_DELTADIO2496:
+		case ICE1712_SUBDEVICE_DELTA66:
+			return snd_ice1712_dig_cs8403_params(substream, params);
+		}
+	}
+	default:
+		break;
+	}
+	return snd_pcm_lib_ioctl(substream, cmd, arg);
+}
+
+#endif
+
+static int snd_ice1712_pro_trigger(snd_pcm_substream_t *substream,
+				   int cmd)
+{
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+	{
+		unsigned int what;
+		unsigned int old;
+		if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
+			return -EINVAL;
+		what = ICE1712_PLAYBACK_PAUSE;
+		snd_pcm_trigger_done(substream, substream);
+		spin_lock(&ice->reg_lock);
+		old = inl(ICEMT(ice, PLAYBACK_CONTROL));
+		if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH)
+			old |= what;
+		else
+			old &= ~what;
+		outl(old, ICEMT(ice, PLAYBACK_CONTROL));
+		spin_unlock(&ice->reg_lock);
+		break;
+	}
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_STOP:
+	{
+		unsigned int what = 0;
+		unsigned int old;
+		snd_pcm_substream_t *s = substream;
+		do {
+			if (s == ice->playback_pro_substream) {
+				what |= ICE1712_PLAYBACK_START;
+				snd_pcm_trigger_done(s, substream);
+			} else if (s == ice->capture_pro_substream) {
+				what |= ICE1712_CAPTURE_START_SHADOW;
+				snd_pcm_trigger_done(s, substream);
+			}
+			s = s->link_next;
+		} while (s != substream);
+		spin_lock(&ice->reg_lock);
+		old = inl(ICEMT(ice, PLAYBACK_CONTROL));
+		if (cmd == SNDRV_PCM_TRIGGER_START)
+			old |= what;
+		else
+			old &= ~what;
+		outl(old, ICEMT(ice, PLAYBACK_CONTROL));
+		spin_unlock(&ice->reg_lock);
+		break;
+	}
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static void snd_ice1712_cs8427_set_status(ice1712_t *ice, snd_pcm_runtime_t *runtime,
+					  unsigned char *status)
+{
+	if (status[0] & IEC958_AES0_PROFESSIONAL) {
+		status[0] &= ~IEC958_AES0_PRO_FS;
+		switch (runtime->rate) {
+		case 32000: status[0] |= IEC958_AES0_PRO_FS_32000; break;
+		case 44100: status[0] |= IEC958_AES0_PRO_FS_44100; break;
+		case 48000: status[0] |= IEC958_AES0_PRO_FS_48000; break;
+		default: status[0] |= IEC958_AES0_PRO_FS_NOTID; break;
+		}
+	} else {
+		status[3] &= ~IEC958_AES3_CON_FS;
+		switch (runtime->rate) {
+		case 32000: status[3] |= IEC958_AES3_CON_FS_32000; break;
+		case 44100: status[3] |= IEC958_AES3_CON_FS_44100; break;
+		case 48000: status[3] |= IEC958_AES3_CON_FS_48000; break;
+		}
+	}
+}
+
+static int snd_ice1712_playback_pro_prepare(snd_pcm_substream_t * substream)
+{
+	unsigned long flags;
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+	unsigned int tmp;
+	unsigned char tmpst[5];
+	int change;
+
+	ice->playback_pro_size = snd_pcm_lib_buffer_bytes(substream);
+	snd_ice1712_set_pro_rate(ice, substream);
+	spin_lock_irqsave(&ice->reg_lock, flags);
+	outl(substream->runtime->dma_addr, ICEMT(ice, PLAYBACK_ADDR));
+	outw((ice->playback_pro_size >> 2) - 1, ICEMT(ice, PLAYBACK_SIZE));
+	outw((snd_pcm_lib_period_bytes(substream) >> 2) - 1, ICEMT(ice, PLAYBACK_COUNT));
+	switch (ice->eeprom.subvendor) {
+	case ICE1712_SUBDEVICE_DELTA1010:
+	case ICE1712_SUBDEVICE_DELTADIO2496:
+	case ICE1712_SUBDEVICE_DELTA66:
+		/* setup S/PDIF */
+		tmp = ice->cs8403_spdif_stream_bits;
+		if (tmp & 0x01)		/* consumer */
+			tmp &= (tmp & 0x01) ? ~0x06 : ~0x18;
+		switch (substream->runtime->rate) {
+		case 32000: tmp |= (tmp & 0x01) ? 0x04 : 0x00; break;
+		case 44100: tmp |= (tmp & 0x01) ? 0x00 : 0x10; break;
+		case 48000: tmp |= (tmp & 0x01) ? 0x02 : 0x08; break;
+		default: tmp |= (tmp & 0x01) ? 0x00 : 0x18; break;
+		}
+		change = ice->cs8403_spdif_stream_bits != tmp;
+		ice->cs8403_spdif_stream_bits = tmp;
+		spin_unlock_irqrestore(&ice->reg_lock, flags);
+		if (change)
+			snd_ctl_notify(ice->card, SNDRV_CTL_EVENT_MASK_VALUE, &ice->spdif_stream_ctl->id);
+		snd_ice1712_delta_cs8403_spdif_write(ice, tmp);
+		return 0;
+	case ICE1712_SUBDEVICE_EWX2496:
+	case ICE1712_SUBDEVICE_AUDIOPHILE:
+		/* setup S/PDIF */
+		memcpy(tmpst, ice->cs8427_spdif_stream_status, 5);
+		snd_ice1712_cs8427_set_status(ice, substream->runtime, tmpst); 
+		change = memcmp(tmpst, ice->cs8427_spdif_stream_status, 5) != 0;
+		memcpy(ice->cs8427_spdif_stream_status, tmpst, 5);
+		spin_unlock_irqrestore(&ice->reg_lock, flags);
+		if (change) {
+			snd_ctl_notify(ice->card, SNDRV_CTL_EVENT_MASK_VALUE, &ice->spdif_stream_ctl->id);
+			ice->cs8427_ops->write_bytes(ice, 32, 5, tmpst);
+		}
+		return 0;
+	case ICE1712_SUBDEVICE_EWS88MT:
+	case ICE1712_SUBDEVICE_EWS88D:
+		/* setup S/PDIF */
+		tmp = ice->cs8403_spdif_stream_bits;
+		if (tmp & 0x10)		/* consumer */
+			tmp &= (tmp & 0x01) ? ~0x06 : ~0x60;
+		switch (substream->runtime->rate) {
+		case 32000: tmp |= (tmp & 0x01) ? 0x02 : 0x00; break;
+		case 44100: tmp |= (tmp & 0x01) ? 0x06 : 0x40; break;
+		case 48000: tmp |= (tmp & 0x01) ? 0x04 : 0x20; break;
+		default: tmp |= (tmp & 0x01) ? 0x06 : 0x40; break;
+		}
+		change = ice->cs8403_spdif_stream_bits != tmp;
+		ice->cs8403_spdif_stream_bits = tmp;
+		spin_unlock_irqrestore(&ice->reg_lock, flags);
+		if (change)
+			snd_ctl_notify(ice->card, SNDRV_CTL_EVENT_MASK_VALUE, &ice->spdif_stream_ctl->id);
+		snd_ice1712_ews_cs8404_spdif_write(ice, tmp);
+		return 0;
+	}
+
+	spin_unlock_irqrestore(&ice->reg_lock, flags);
+	return 0;
+}
+
+static int snd_ice1712_capture_pro_prepare(snd_pcm_substream_t * substream)
+{
+	unsigned long flags;
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+
+	ice->capture_pro_size = snd_pcm_lib_buffer_bytes(substream);
+	snd_ice1712_set_pro_rate(ice, substream);
+	spin_lock_irqsave(&ice->reg_lock, flags);
+	outl(substream->runtime->dma_addr, ICEMT(ice, CAPTURE_ADDR));
+	outw((ice->capture_pro_size >> 2) - 1, ICEMT(ice, CAPTURE_SIZE));
+	outw((snd_pcm_lib_period_bytes(substream) >> 2) - 1, ICEMT(ice, CAPTURE_COUNT));
+	spin_unlock_irqrestore(&ice->reg_lock, flags);
+	return 0;
+}
+
+static snd_pcm_uframes_t snd_ice1712_playback_pro_pointer(snd_pcm_substream_t * substream)
+{
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+	size_t ptr;
+
+	if (!(inl(ICEMT(ice, PLAYBACK_CONTROL)) & ICE1712_PLAYBACK_START))
+		return 0;
+	ptr = ice->playback_pro_size - (inw(ICEMT(ice, PLAYBACK_SIZE)) << 2);
+	return bytes_to_frames(substream->runtime, ptr);
+}
+
+static snd_pcm_uframes_t snd_ice1712_capture_pro_pointer(snd_pcm_substream_t * substream)
+{
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+	size_t ptr;
+
+	if (!(inl(ICEMT(ice, PLAYBACK_CONTROL)) & ICE1712_CAPTURE_START_SHADOW))
+		return 0;
+	ptr = ice->capture_pro_size - (inw(ICEMT(ice, CAPTURE_SIZE)) << 2);
+	return bytes_to_frames(substream->runtime, ptr);
+}
+
+#ifdef TARGET_OS2
+static snd_pcm_hardware_t snd_ice1712_playback_pro =
+{
+/*	info:		  */	(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_MMAP_VALID |
+				 SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_SYNC_START),
+/*	formats:	  */	SNDRV_PCM_FMTBIT_S32_LE,
+/*	rates:		  */	SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_96000,
+/*	rate_min:	  */	4000,
+/*	rate_max:	  */	96000,
+/*	channels_min:	  */	10,
+/*	channels_max:	  */	10,
+/*	buffer_bytes_max:  */	(256*1024),
+/*	period_bytes_min:  */	10 * 4 * 2,
+/*	period_bytes_max:  */	131040,
+/*	periods_min:	  */	1,
+/*	periods_max:	  */	1024,
+/*	fifo_size:	  */	0,
+};
+
+static snd_pcm_hardware_t snd_ice1712_capture_pro =
+{
+/*	info:		  */	(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+			  	 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_MMAP_VALID |
+				 SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_SYNC_START),
+/*	formats:	  */	SNDRV_PCM_FMTBIT_S32_LE,
+/*	rates:		  */	SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_96000,
+/*	rate_min:	  */	4000,
+/*	rate_max:	  */	96000,
+/*	channels_min:	  */	12,
+/*	channels_max:	  */	12,
+/*	buffer_bytes_max:  */	(256*1024),
+/*	period_bytes_min:  */	12 * 4 * 2,
+/*	period_bytes_max:  */	131040,
+/*	periods_min:	  */	1,
+/*	periods_max:	  */	1024,
+/*	fifo_size:	  */	0,
+};
+#else
+static snd_pcm_hardware_t snd_ice1712_playback_pro =
+{
+	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_MMAP_VALID |
+				 SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_SYNC_START),
+	formats:		SNDRV_PCM_FMTBIT_S32_LE,
+	rates:			SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_96000,
+	rate_min:		4000,
+	rate_max:		96000,
+	channels_min:		10,
+	channels_max:		10,
+	buffer_bytes_max:	(256*1024),
+	period_bytes_min:	10 * 4 * 2,
+	period_bytes_max:	131040,
+	periods_min:		1,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+
+static snd_pcm_hardware_t snd_ice1712_capture_pro =
+{
+	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_MMAP_VALID |
+				 SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_SYNC_START),
+	formats:		SNDRV_PCM_FMTBIT_S32_LE,
+	rates:			SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_96000,
+	rate_min:		4000,
+	rate_max:		96000,
+	channels_min:		12,
+	channels_max:		12,
+	buffer_bytes_max:	(256*1024),
+	period_bytes_min:	12 * 4 * 2,
+	period_bytes_max:	131040,
+	periods_min:		1,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+#endif
+
+static int snd_ice1712_playback_pro_open(snd_pcm_substream_t * substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+
+	ice->playback_pro_substream = substream;
+	runtime->hw = snd_ice1712_playback_pro;
+	snd_pcm_set_sync(substream);
+	snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
+	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates);
+
+	ice->cs8403_spdif_stream_bits = ice->cs8403_spdif_bits;
+	memcpy(ice->cs8427_spdif_stream_status, ice->cs8427_spdif_status, 5);
+	if (ice->spdif_stream_ctl != NULL) {
+		ice->spdif_stream_ctl->access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+		snd_ctl_notify(ice->card, SNDRV_CTL_EVENT_MASK_VALUE |
+			       SNDRV_CTL_EVENT_MASK_INFO, &ice->spdif_stream_ctl->id);
+	}
+
+	return 0;
+}
+
+static int snd_ice1712_capture_pro_open(snd_pcm_substream_t * substream)
+{
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	ice->capture_pro_substream = substream;
+	runtime->hw = snd_ice1712_capture_pro;
+	snd_pcm_set_sync(substream);
+	snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
+	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates);
+	return 0;
+}
+
+static int snd_ice1712_playback_pro_close(snd_pcm_substream_t * substream)
+{
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+
+	ice->playback_pro_substream = NULL;
+
+	if (ice->spdif_stream_ctl != NULL) {
+		ice->spdif_stream_ctl->access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+		snd_ctl_notify(ice->card, SNDRV_CTL_EVENT_MASK_VALUE |
+			       SNDRV_CTL_EVENT_MASK_INFO, &ice->spdif_stream_ctl->id);
+	}
+
+	return 0;
+}
+
+static int snd_ice1712_capture_pro_close(snd_pcm_substream_t * substream)
+{
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+
+	ice->capture_pro_substream = NULL;
+	return 0;
+}
+
+static void __exit snd_ice1712_pcm_profi_free(snd_pcm_t *pcm)
+{
+	ice1712_t *ice = snd_magic_cast(ice1712_t, pcm->private_data, return);
+	ice->pcm_pro = NULL;
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+#ifdef TARGET_OS2
+static snd_pcm_ops_t snd_ice1712_playback_pro_ops = {
+	snd_ice1712_playback_pro_open,
+	snd_ice1712_playback_pro_close,
+	snd_pcm_lib_ioctl,
+	snd_ice1712_hw_params,
+	snd_ice1712_hw_free,
+	snd_ice1712_playback_pro_prepare,
+	snd_ice1712_pro_trigger,
+	snd_ice1712_playback_pro_pointer,0,0
+};
+
+static snd_pcm_ops_t snd_ice1712_capture_pro_ops = {
+	snd_ice1712_capture_pro_open,
+	snd_ice1712_capture_pro_close,
+	snd_pcm_lib_ioctl,
+	snd_ice1712_hw_params,
+	snd_ice1712_hw_free,
+	snd_ice1712_capture_pro_prepare,
+	snd_ice1712_pro_trigger,
+	snd_ice1712_capture_pro_pointer,0,0
+};
+#else
+static snd_pcm_ops_t snd_ice1712_playback_pro_ops = {
+	open:		snd_ice1712_playback_pro_open,
+	close:		snd_ice1712_playback_pro_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	hw_params:	snd_ice1712_hw_params,
+	hw_free:	snd_ice1712_hw_free,
+	prepare:	snd_ice1712_playback_pro_prepare,
+	trigger:	snd_ice1712_pro_trigger,
+	pointer:	snd_ice1712_playback_pro_pointer,
+};
+
+static snd_pcm_ops_t snd_ice1712_capture_pro_ops = {
+	open:		snd_ice1712_capture_pro_open,
+	close:		snd_ice1712_capture_pro_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	hw_params:	snd_ice1712_hw_params,
+	hw_free:	snd_ice1712_hw_free,
+	prepare:	snd_ice1712_capture_pro_prepare,
+	trigger:	snd_ice1712_pro_trigger,
+	pointer:	snd_ice1712_capture_pro_pointer,
+};
+#endif
+
+static int __init snd_ice1712_pcm_profi(ice1712_t * ice, int device, snd_pcm_t ** rpcm)
+{
+	snd_pcm_t *pcm;
+	int err;
+
+	if (rpcm)
+		*rpcm = NULL;
+	err = snd_pcm_new(ice->card, "ICE1712 multi", device, 1, 1, &pcm);
+	if (err < 0)
+		return err;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ice1712_playback_pro_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ice1712_capture_pro_ops);
+
+	pcm->private_data = ice;
+	pcm->private_free = snd_ice1712_pcm_profi_free;
+	pcm->info_flags = 0;
+	strcpy(pcm->name, "ICE1712 multi");
+
+	snd_pcm_lib_preallocate_pci_pages_for_all(ice->pci, pcm, 256*1024, 256*1024);
+
+	ice->pcm_pro = pcm;
+	if (rpcm)
+		*rpcm = pcm;
+	
+	if ((err = snd_ice1712_build_pro_mixer(ice)) < 0)
+		return err;
+	return 0;
+}
+
+/*
+ *  Mixer section
+ */
+
+static void snd_ice1712_update_volume(ice1712_t *ice, int index)
+{
+	unsigned int vol = ice->pro_volumes[index];
+	unsigned short val = 0;
+
+	val |= (vol & 0x8000) == 0 ? (96 - (vol & 0x7f)) : 0x7f;
+	val |= ((vol & 0x80000000) == 0 ? (96 - ((vol >> 16) & 0x7f)) : 0x7f) << 8;
+	outb(index, ICEMT(ice, MONITOR_INDEX));
+	outw(val, ICEMT(ice, MONITOR_VOLUME));
+}
+
+static int snd_ice1712_pro_mixer_switch_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+	return 0;
+}
+
+static int snd_ice1712_pro_mixer_switch_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int index = kcontrol->private_value;
+	
+	spin_lock_irqsave(&ice->reg_lock, flags);
+	ucontrol->value.integer.value[0] = !((ice->pro_volumes[index] >> 15) & 1);
+	ucontrol->value.integer.value[1] = !((ice->pro_volumes[index] >> 31) & 1);
+	spin_unlock_irqrestore(&ice->reg_lock, flags);
+	return 0;
+}
+
+static int snd_ice1712_pro_mixer_switch_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	unsigned long flags;
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	int index = kcontrol->private_value;
+	unsigned int nval, change;
+
+	nval = (ucontrol->value.integer.value[0] ? 0 : 0x00008000) |
+	       (ucontrol->value.integer.value[1] ? 0 : 0x80000000);
+	spin_lock_irqsave(&ice->reg_lock, flags);
+	nval |= ice->pro_volumes[index] & ~0x80008000;
+	change = nval != ice->pro_volumes[index];
+	ice->pro_volumes[index] = nval;
+	snd_ice1712_update_volume(ice, index);
+	spin_unlock_irqrestore(&ice->reg_lock, flags);
+	return change;
+}
+
+static int snd_ice1712_pro_mixer_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 96;
+	return 0;
+}
+
+static int snd_ice1712_pro_mixer_volume_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int index = kcontrol->private_value;
+	
+	spin_lock_irqsave(&ice->reg_lock, flags);
+	ucontrol->value.integer.value[0] = (ice->pro_volumes[index] >> 0) & 127;
+	ucontrol->value.integer.value[1] = (ice->pro_volumes[index] >> 16) & 127;
+	spin_unlock_irqrestore(&ice->reg_lock, flags);
+	return 0;
+}
+
+static int snd_ice1712_pro_mixer_volume_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	unsigned long flags;
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	int index = kcontrol->private_value;
+	unsigned int nval, change;
+
+	nval = (ucontrol->value.integer.value[0] & 127) |
+	       ((ucontrol->value.integer.value[1] & 127) << 16);
+	spin_lock_irqsave(&ice->reg_lock, flags);
+	nval |= ice->pro_volumes[index] & ~0x007f007f;
+	change = nval != ice->pro_volumes[index];
+	ice->pro_volumes[index] = nval;
+	snd_ice1712_update_volume(ice, index);
+	spin_unlock_irqrestore(&ice->reg_lock, flags);
+	return change;
+}
+
+static int snd_ice1712_ak4524_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 127;
+	return 0;
+}
+
+static int snd_ice1712_ak4524_volume_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	int chip = kcontrol->private_value / 8;
+	int addr = kcontrol->private_value % 8;
+	ucontrol->value.integer.value[0] = ice->ak4524_images[chip][addr];
+	return 0;
+}
+
+static int snd_ice1712_ak4524_volume_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	int chip = kcontrol->private_value / 8;
+	int addr = kcontrol->private_value % 8;
+	unsigned char nval = ucontrol->value.integer.value[0];
+	int change = ice->ak4524_images[chip][addr] != nval;
+	if (change)
+		snd_ice1712_ak4524_write(ice, chip, addr, nval);
+	return change;
+}
+
+static int snd_ice1712_ak4524_deemphasis_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+	static char *texts[4] = {
+		"44.1kHz", "Off", "48kHz", "32kHz",
+	};
+	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 = 3;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int snd_ice1712_ak4524_deemphasis_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	int chip = kcontrol->id.index;
+	ucontrol->value.enumerated.item[0] = ice->ak4524_images[chip][3] & 3;
+	return 0;
+}
+
+static int snd_ice1712_ak4524_deemphasis_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	int chip = kcontrol->id.index;
+	unsigned char nval = ucontrol->value.enumerated.item[0];
+	int change;
+	nval |= (nval & 3) | (ice->ak4524_images[chip][3] & ~3);
+	change = ice->ak4524_images[chip][3] != nval;
+	if (change)
+		snd_ice1712_ak4524_write(ice, chip, 3, nval);
+	return change;
+}
+
+static int __init snd_ice1712_build_pro_mixer(ice1712_t *ice)
+{
+	snd_card_t * card = ice->card;
+	snd_kcontrol_t ctl;
+	int idx, err;
+
+	/* PCM playback */
+	for (idx = 0; idx < 10; idx++) {
+		memset(&ctl, 0, sizeof(ctl));
+		strcpy(ctl.id.name, "Multi Playback Switch");
+		ctl.id.index = idx;
+		ctl.id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+		ctl.info = snd_ice1712_pro_mixer_switch_info;
+		ctl.access = SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE;
+		ctl.get = snd_ice1712_pro_mixer_switch_get;
+		ctl.put = snd_ice1712_pro_mixer_switch_put;
+		ctl.private_value = idx;
+		ctl.private_data = ice;
+		if ((err = snd_ctl_add(card, snd_ctl_new(&ctl))) < 0)
+			return err;
+		memset(&ctl, 0, sizeof(ctl));
+		strcpy(ctl.id.name, "Multi Playback Volume");
+		ctl.id.index = idx;
+		ctl.id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+		ctl.info = snd_ice1712_pro_mixer_volume_info;
+		ctl.access = SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE;
+		ctl.get = snd_ice1712_pro_mixer_volume_get;
+		ctl.put = snd_ice1712_pro_mixer_volume_put;
+		ctl.private_value = idx;
+		ctl.private_data = ice;
+		if ((err = snd_ctl_add(card, snd_ctl_new(&ctl))) < 0)
+			return err;
+	}
+
+	/* PCM capture */
+	for (idx = 0; idx < 10; idx++) {
+		memset(&ctl, 0, sizeof(ctl));
+		strcpy(ctl.id.name, "Multi Capture Switch");
+		ctl.id.index = idx;
+		ctl.id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+		ctl.info = snd_ice1712_pro_mixer_switch_info;
+		ctl.access = SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE;
+		ctl.get = snd_ice1712_pro_mixer_switch_get;
+		ctl.put = snd_ice1712_pro_mixer_switch_put;
+		ctl.private_value = idx + 10;
+		ctl.private_data = ice;
+		if ((err = snd_ctl_add(card, snd_ctl_new(&ctl))) < 0)
+			return err;
+		memset(&ctl, 0, sizeof(ctl));
+		strcpy(ctl.id.name, "Multi Capture Volume");
+		ctl.id.index = idx;
+		ctl.id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+		ctl.info = snd_ice1712_pro_mixer_volume_info;
+		ctl.access = SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE;
+		ctl.get = snd_ice1712_pro_mixer_volume_get;
+		ctl.put = snd_ice1712_pro_mixer_volume_put;
+		ctl.private_value = idx + 10;
+		ctl.private_data = ice;
+		if ((err = snd_ctl_add(card, snd_ctl_new(&ctl))) < 0)
+			return err;
+	}
+	
+	return 0;
+}
+
+static void /*__init*/ snd_ice1712_ac97_init(ac97_t *ac97)
+{
+	// ice1712_t *ice = snd_magic_cast(ice1712_t, ac97->private_data, return);
+
+        /* disable center DAC/surround DAC/LFE DAC/MIC ADC */
+        snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, 0xe800, 0xe800);
+}
+
+static void __exit snd_ice1712_mixer_free_ac97(ac97_t *ac97)
+{
+	ice1712_t *ice = snd_magic_cast(ice1712_t, ac97->private_data, return);
+	ice->ac97 = NULL;
+}
+
+static int __init snd_ice1712_ac97_mixer(ice1712_t * ice)
+{
+	int err;
+
+	if (!(ice->eeprom.codec & ICE1712_CFG_NO_CON_AC97)) {
+		ac97_t ac97;
+		memset(&ac97, 0, sizeof(ac97));
+		ac97.write = snd_ice1712_ac97_write;
+		ac97.read = snd_ice1712_ac97_read;
+		ac97.init = snd_ice1712_ac97_init;
+		ac97.private_data = ice;
+		ac97.private_free = snd_ice1712_mixer_free_ac97;
+		if ((err = snd_ac97_mixer(ice->card, &ac97, &ice->ac97)) < 0)
+			return err;
+		return 0;
+	}
+	/* hmm.. can we have both consumer and pro ac97 mixers? */
+	if (! (ice->eeprom.aclink & ICE1712_CFG_PRO_I2S)) {
+		ac97_t ac97;
+		memset(&ac97, 0, sizeof(ac97));
+		ac97.write = snd_ice1712_pro_ac97_write;
+		ac97.read = snd_ice1712_pro_ac97_read;
+		ac97.init = snd_ice1712_ac97_init;
+		ac97.private_data = ice;
+		ac97.private_free = snd_ice1712_mixer_free_ac97;
+		if ((err = snd_ac97_mixer(ice->card, &ac97, &ice->ac97)) < 0)
+			return err;
+		if ((err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_mixer_digmix_route_ac97, ice))) < 0)
+			return err;
+		return 0;
+	}
+	/* I2S mixer only */
+	strcat(ice->card->mixername, "ICE1712 - multitrack");
+	return 0;
+}
+
+/*
+ *
+ */
+
+static void snd_ice1712_proc_read(snd_info_entry_t *entry, 
+				  snd_info_buffer_t * buffer)
+{
+	ice1712_t *ice = snd_magic_cast(ice1712_t, entry->private_data, return);
+	unsigned int idx;
+
+	snd_iprintf(buffer, "ICE1712\n\n");
+	snd_iprintf(buffer, "EEPROM:\n");
+	snd_iprintf(buffer, "  Subvendor        : 0x%x\n", ice->eeprom.subvendor);
+	snd_iprintf(buffer, "  Size             : %i bytes\n", ice->eeprom.size);
+	snd_iprintf(buffer, "  Version          : %i\n", ice->eeprom.version);
+	snd_iprintf(buffer, "  Codec            : 0x%x\n", ice->eeprom.codec);
+	snd_iprintf(buffer, "  ACLink           : 0x%x\n", ice->eeprom.aclink);
+	snd_iprintf(buffer, "  I2S ID           : 0x%x\n", ice->eeprom.i2sID);
+	snd_iprintf(buffer, "  S/PDIF           : 0x%x\n", ice->eeprom.spdif);
+	snd_iprintf(buffer, "  GPIO mask        : 0x%x\n", ice->eeprom.gpiomask);
+	snd_iprintf(buffer, "  GPIO state       : 0x%x\n", ice->eeprom.gpiostate);
+	snd_iprintf(buffer, "  GPIO direction   : 0x%x\n", ice->eeprom.gpiodir);
+	snd_iprintf(buffer, "  AC'97 main       : 0x%x\n", ice->eeprom.ac97main);
+	snd_iprintf(buffer, "  AC'97 pcm        : 0x%x\n", ice->eeprom.ac97pcm);
+	snd_iprintf(buffer, "  AC'97 record     : 0x%x\n", ice->eeprom.ac97rec);
+	snd_iprintf(buffer, "  AC'97 record src : 0x%x\n", ice->eeprom.ac97recsrc);
+	for (idx = 0; idx < 4; idx++)
+		snd_iprintf(buffer, "  DAC ID #%i        : 0x%x\n", idx, ice->eeprom.dacID[idx]);
+	for (idx = 0; idx < 4; idx++)
+		snd_iprintf(buffer, "  ADC ID #%i        : 0x%x\n", idx, ice->eeprom.adcID[idx]);
+	for (idx = 0x1c; idx < ice->eeprom.size && idx < 0x1c + sizeof(ice->eeprom.extra); idx++)
+		snd_iprintf(buffer, "  Extra #%02i        : 0x%x\n", idx, ice->eeprom.extra[idx - 0x1c]);
+}
+
+static void __init snd_ice1712_proc_init(ice1712_t * ice)
+{
+	snd_info_entry_t *entry;
+
+	if ((entry = snd_info_create_card_entry(ice->card, "ice1712", ice->card->proc_root)) != NULL) {
+		entry->content = SNDRV_INFO_CONTENT_TEXT;
+		entry->private_data = ice;
+		entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
+		entry->c.text.read_size = 2048;
+		entry->c.text.read = snd_ice1712_proc_read;
+		if (snd_info_register(entry) < 0) {
+			snd_info_free_entry(entry);
+			entry = NULL;
+		}
+	}
+	ice->proc_entry = entry;
+}
+
+static void __exit snd_ice1712_proc_done(ice1712_t * ice)
+{
+	if (ice->proc_entry) {
+		snd_info_unregister(ice->proc_entry);
+		ice->proc_entry = NULL;
+	}
+}
+
+/*
+ *
+ */
+
+static int snd_ice1712_eeprom_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+	uinfo->count = 32;
+	return 0;
+}
+
+static int snd_ice1712_eeprom_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	
+	memcpy(ucontrol->value.bytes.data, &ice->eeprom, 32);
+	return 0;
+}
+
+static snd_kcontrol_new_t snd_ice1712_eeprom = {
+#ifdef TARGET_OS2
+	SNDRV_CTL_ELEM_IFACE_CARD,0,0,
+	"ICE1712 EEPROM",0,
+	SNDRV_CTL_ELEM_ACCESS_READ,
+	snd_ice1712_eeprom_info,
+	snd_ice1712_eeprom_get,0,0
+#else
+	iface: SNDRV_CTL_ELEM_IFACE_CARD,
+	name: "ICE1712 EEPROM",
+	access: SNDRV_CTL_ELEM_ACCESS_READ,
+	info: snd_ice1712_eeprom_info,
+	get: snd_ice1712_eeprom_get
+#endif
+};
+
+/*
+ */
+static int snd_ice1712_spdif_default_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+	uinfo->count = 1;
+	return 0;
+}
+
+static int snd_ice1712_spdif_default_get(snd_kcontrol_t * kcontrol,
+					 snd_ctl_elem_value_t * ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	
+	switch (ice->eeprom.subvendor) {
+	case ICE1712_SUBDEVICE_DELTA1010:
+	case ICE1712_SUBDEVICE_DELTADIO2496:
+	case ICE1712_SUBDEVICE_DELTA66:
+		snd_ice1712_decode_cs8403_spdif_bits(&ucontrol->value.iec958, ice->cs8403_spdif_bits);
+		break;
+	case ICE1712_SUBDEVICE_AUDIOPHILE:
+	case ICE1712_SUBDEVICE_EWX2496:
+		memcpy(ucontrol->value.iec958.status, ice->cs8427_spdif_status, 5);
+		break;
+	case ICE1712_SUBDEVICE_EWS88MT:
+	case ICE1712_SUBDEVICE_EWS88D:
+		snd_ice1712_ews_decode_cs8404_spdif_bits(&ucontrol->value.iec958, ice->cs8403_spdif_bits);
+		break;
+	}
+	return 0;
+}
+
+static int snd_ice1712_spdif_default_put(snd_kcontrol_t * kcontrol,
+					 snd_ctl_elem_value_t * ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	unsigned int val;
+	int change;
+
+	switch (ice->eeprom.subvendor) {
+	case ICE1712_SUBDEVICE_DELTA1010:
+	case ICE1712_SUBDEVICE_DELTADIO2496:
+	case ICE1712_SUBDEVICE_DELTA66:
+		val = snd_ice1712_encode_cs8403_spdif_bits(&ucontrol->value.iec958);
+		spin_lock_irqsave(&ice->reg_lock, flags);
+		change = ice->cs8403_spdif_bits != val;
+		ice->cs8403_spdif_bits = val;
+		if (change && ice->playback_pro_substream == NULL) {
+			spin_unlock_irqrestore(&ice->reg_lock, flags);
+			snd_ice1712_delta_cs8403_spdif_write(ice, val);
+		} else {
+			spin_unlock_irqrestore(&ice->reg_lock, flags);
+		}
+		break;
+	case ICE1712_SUBDEVICE_AUDIOPHILE:
+	case ICE1712_SUBDEVICE_EWX2496:
+		spin_lock_irqsave(&ice->reg_lock, flags);
+		change = memcmp(ucontrol->value.iec958.status, ice->cs8427_spdif_status, 5) != 0;
+		memcpy(ice->cs8427_spdif_status, ucontrol->value.iec958.status, 5);
+		if (change && ice->playback_pro_substream == NULL) {
+			spin_unlock_irqrestore(&ice->reg_lock, flags);
+			ice->cs8427_ops->write_bytes(ice, 32, 5, ice->cs8427_spdif_status);
+		} else
+			spin_unlock_irqrestore(&ice->reg_lock, flags);
+		break;
+	case ICE1712_SUBDEVICE_EWS88MT:
+	case ICE1712_SUBDEVICE_EWS88D:
+		val = snd_ice1712_ews_encode_cs8404_spdif_bits(&ucontrol->value.iec958);
+		spin_lock_irqsave(&ice->reg_lock, flags);
+		change = ice->cs8403_spdif_bits != val;
+		ice->cs8403_spdif_bits = val;
+		if (change && ice->playback_pro_substream == NULL) {
+			spin_unlock_irqrestore(&ice->reg_lock, flags);
+			snd_ice1712_ews_cs8404_spdif_write(ice, val);
+		} else {
+			spin_unlock_irqrestore(&ice->reg_lock, flags);
+		}
+		break;
+	default:
+		change = 0;
+	}
+	return change;
+}
+
+static snd_kcontrol_new_t snd_ice1712_spdif_default =
+{
+#ifdef TARGET_OS2
+	SNDRV_CTL_ELEM_IFACE_PCM,0,0,
+	SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),0,0,
+	snd_ice1712_spdif_default_info,
+	snd_ice1712_spdif_default_get,
+	snd_ice1712_spdif_default_put,0
+#else
+	iface:		SNDRV_CTL_ELEM_IFACE_PCM,
+	name:           SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
+	info:		snd_ice1712_spdif_default_info,
+	get:		snd_ice1712_spdif_default_get,
+	put:		snd_ice1712_spdif_default_put
+#endif
+};
+
+static int snd_ice1712_spdif_mask_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+	uinfo->count = 1;
+	return 0;
+}
+
+static int snd_ice1712_spdif_maskc_get(snd_kcontrol_t * kcontrol,
+				       snd_ctl_elem_value_t * ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+
+	switch (ice->eeprom.subvendor) {
+	case ICE1712_SUBDEVICE_DELTA1010:
+	case ICE1712_SUBDEVICE_DELTADIO2496:
+	case ICE1712_SUBDEVICE_DELTA66:
+	case ICE1712_SUBDEVICE_EWS88MT:
+	case ICE1712_SUBDEVICE_EWS88D:
+		ucontrol->value.iec958.status[0] = IEC958_AES0_NONAUDIO |
+						     IEC958_AES0_PROFESSIONAL |
+						     IEC958_AES0_CON_NOT_COPYRIGHT |
+						     IEC958_AES0_CON_EMPHASIS;
+		ucontrol->value.iec958.status[1] = IEC958_AES1_CON_ORIGINAL |
+						     IEC958_AES1_CON_CATEGORY;
+		ucontrol->value.iec958.status[3] = IEC958_AES3_CON_FS;
+		break;
+	case ICE1712_SUBDEVICE_AUDIOPHILE:
+	case ICE1712_SUBDEVICE_EWX2496:
+		ucontrol->value.iec958.status[0] = 0xff;
+		ucontrol->value.iec958.status[1] = 0xff;
+		ucontrol->value.iec958.status[2] = 0xff;
+		ucontrol->value.iec958.status[3] = 0xff;
+		ucontrol->value.iec958.status[4] = 0xff;
+		break;
+	}
+	return 0;
+}
+
+static int snd_ice1712_spdif_maskp_get(snd_kcontrol_t * kcontrol,
+				       snd_ctl_elem_value_t * ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+
+	switch (ice->eeprom.subvendor) {
+	case ICE1712_SUBDEVICE_DELTA1010:
+	case ICE1712_SUBDEVICE_DELTADIO2496:
+	case ICE1712_SUBDEVICE_DELTA66:
+	case ICE1712_SUBDEVICE_EWS88MT:
+	case ICE1712_SUBDEVICE_EWS88D:
+		ucontrol->value.iec958.status[0] = IEC958_AES0_NONAUDIO |
+						     IEC958_AES0_PROFESSIONAL |
+						     IEC958_AES0_PRO_FS |
+						     IEC958_AES0_PRO_EMPHASIS;
+		ucontrol->value.iec958.status[1] = IEC958_AES1_PRO_MODE;
+		break;
+	case ICE1712_SUBDEVICE_AUDIOPHILE:
+	case ICE1712_SUBDEVICE_EWX2496:
+		ucontrol->value.iec958.status[0] = 0xff;
+		ucontrol->value.iec958.status[1] = 0xff;
+		ucontrol->value.iec958.status[2] = 0xff;
+		ucontrol->value.iec958.status[3] = 0xff;
+		ucontrol->value.iec958.status[4] = 0xff;
+		break;
+	}
+	return 0;
+}
+
+static snd_kcontrol_new_t snd_ice1712_spdif_maskc =
+{
+#ifdef TARGET_OS2
+	SNDRV_CTL_ELEM_IFACE_MIXER,0,0,
+	SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK),0,
+	SNDRV_CTL_ELEM_ACCESS_READ,
+	snd_ice1712_spdif_mask_info,
+	snd_ice1712_spdif_maskc_get,0,0
+#else
+	access:		SNDRV_CTL_ELEM_ACCESS_READ,
+	iface:		SNDRV_CTL_ELEM_IFACE_MIXER,
+	name:           SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK),
+	info:		snd_ice1712_spdif_mask_info,
+	get:		snd_ice1712_spdif_maskc_get,
+#endif
+};
+
+static snd_kcontrol_new_t snd_ice1712_spdif_maskp =
+{
+#ifdef TARGET_OS2
+	SNDRV_CTL_ELEM_IFACE_MIXER,0,0,
+	SNDRV_CTL_NAME_IEC958("",PLAYBACK,PRO_MASK),0,
+	SNDRV_CTL_ELEM_ACCESS_READ,
+	snd_ice1712_spdif_mask_info,
+	snd_ice1712_spdif_maskp_get,0,0
+#else
+	access:		SNDRV_CTL_ELEM_ACCESS_READ,
+	iface:		SNDRV_CTL_ELEM_IFACE_MIXER,
+	name:           SNDRV_CTL_NAME_IEC958("",PLAYBACK,PRO_MASK),
+	info:		snd_ice1712_spdif_mask_info,
+	get:		snd_ice1712_spdif_maskp_get,
+#endif
+};
+
+static int snd_ice1712_spdif_stream_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+	uinfo->count = 1;
+	return 0;
+}
+
+static int snd_ice1712_spdif_stream_get(snd_kcontrol_t * kcontrol,
+					snd_ctl_elem_value_t * ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+
+	switch (ice->eeprom.subvendor) {
+	case ICE1712_SUBDEVICE_DELTA1010:
+	case ICE1712_SUBDEVICE_DELTADIO2496:
+	case ICE1712_SUBDEVICE_DELTA66:
+		snd_ice1712_decode_cs8403_spdif_bits(&ucontrol->value.iec958, ice->cs8403_spdif_stream_bits);
+		break;
+	case ICE1712_SUBDEVICE_AUDIOPHILE:
+	case ICE1712_SUBDEVICE_EWX2496:
+		//ice->cs8427_ops->read_bytes(ice, 32, 5, ucontrol->value.iec958.status);
+		memcpy(ucontrol->value.iec958.status, ice->cs8427_spdif_stream_status, 5);
+		break;
+	case ICE1712_SUBDEVICE_EWS88MT:
+	case ICE1712_SUBDEVICE_EWS88D:
+		snd_ice1712_ews_decode_cs8404_spdif_bits(&ucontrol->value.iec958, ice->cs8403_spdif_stream_bits);
+		break;
+	}
+	return 0;
+}
+
+static int snd_ice1712_spdif_stream_put(snd_kcontrol_t * kcontrol,
+					snd_ctl_elem_value_t * ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	unsigned int val;
+	int change;
+
+	switch (ice->eeprom.subvendor) {
+	case ICE1712_SUBDEVICE_DELTA1010:
+	case ICE1712_SUBDEVICE_DELTADIO2496:
+	case ICE1712_SUBDEVICE_DELTA66:
+		val = snd_ice1712_encode_cs8403_spdif_bits(&ucontrol->value.iec958);
+		spin_lock_irqsave(&ice->reg_lock, flags);
+		change = ice->cs8403_spdif_stream_bits != val;
+		ice->cs8403_spdif_stream_bits = val;
+		if (change && ice->playback_pro_substream != NULL) {
+			spin_unlock_irqrestore(&ice->reg_lock, flags);
+			snd_ice1712_delta_cs8403_spdif_write(ice, val);
+		} else {
+			spin_unlock_irqrestore(&ice->reg_lock, flags);
+		}
+		break;
+	case ICE1712_SUBDEVICE_AUDIOPHILE:
+	case ICE1712_SUBDEVICE_EWX2496:
+		spin_lock_irqsave(&ice->reg_lock, flags);
+		change = memcmp(ice->cs8427_spdif_stream_status,
+				ucontrol->value.iec958.status, 5) != 0;
+		if (change && ice->playback_pro_substream != NULL) {
+			memcpy(ice->cs8427_spdif_stream_status,
+			       ucontrol->value.iec958.status, 5);
+			spin_unlock_irqrestore(&ice->reg_lock, flags);
+			ice->cs8427_ops->write_bytes(ice, 32, 5, ucontrol->value.iec958.status);
+		} else
+			spin_unlock_irqrestore(&ice->reg_lock, flags);
+		break;
+	case ICE1712_SUBDEVICE_EWS88MT:
+	case ICE1712_SUBDEVICE_EWS88D:
+		val = snd_ice1712_ews_encode_cs8404_spdif_bits(&ucontrol->value.iec958);
+		spin_lock_irqsave(&ice->reg_lock, flags);
+		change = ice->cs8403_spdif_stream_bits != val;
+		ice->cs8403_spdif_stream_bits = val;
+		if (change && ice->playback_pro_substream != NULL) {
+			spin_unlock_irqrestore(&ice->reg_lock, flags);
+			snd_ice1712_ews_cs8404_spdif_write(ice, val);
+		} else {
+			spin_unlock_irqrestore(&ice->reg_lock, flags);
+		}
+		break;
+	default:
+		change = 0;
+	}
+	return change;
+}
+
+static snd_kcontrol_new_t snd_ice1712_spdif_stream =
+{
+#ifdef TARGET_OS2
+	SNDRV_CTL_ELEM_IFACE_PCM,0,0,
+	SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM),0,
+	SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+	snd_ice1712_spdif_stream_info,
+	snd_ice1712_spdif_stream_get,
+	snd_ice1712_spdif_stream_put,0
+#else
+	access:		SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+	iface:		SNDRV_CTL_ELEM_IFACE_PCM,
+	name:           SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM),
+	info:		snd_ice1712_spdif_stream_info,
+	get:		snd_ice1712_spdif_stream_get,
+	put:		snd_ice1712_spdif_stream_put
+#endif
+};
+
+#ifdef TARGET_OS2
+#define ICE1712_GPIO(xiface, xname, xindex, shift, xaccess) \
+{ xiface, 0, 0, xname, 0, xaccess, snd_ice1712_gpio_info, \
+  snd_ice1712_gpio_get, snd_ice1712_gpio_put, \
+  shift }
+#else
+#define ICE1712_GPIO(xiface, xname, xindex, shift, xaccess) \
+{ iface: xiface, name: xname, access: xaccess, info: snd_ice1712_gpio_info, \
+  get: snd_ice1712_gpio_get, put: snd_ice1712_gpio_put, \
+  private_value: shift }
+#endif
+
+static int snd_ice1712_gpio_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_ice1712_gpio_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	unsigned char mask = kcontrol->private_value & 0xff;
+	unsigned char saved[2];
+	
+	save_gpio_status(ice, saved);
+	ucontrol->value.integer.value[0] = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA) & mask ? 1 : 0;
+	restore_gpio_status(ice, saved);
+	return 0;
+}
+
+static int snd_ice1712_gpio_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	unsigned char mask = kcontrol->private_value & 0xff;
+	unsigned char saved[2];
+	int val, nval;
+
+	if (kcontrol->private_value & (1 << 31))
+		return -EPERM;
+	nval = ucontrol->value.integer.value[0] ? mask : 0;
+	save_gpio_status(ice, saved);
+	val = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA);
+	nval |= val & ~mask;
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, nval);
+	restore_gpio_status(ice, saved);
+	return val != nval;
+}
+
+static snd_kcontrol_new_t snd_ice1712_delta1010_wordclock_select =
+ICE1712_GPIO(SNDRV_CTL_ELEM_IFACE_PCM, "Word Clock Sync", 0, ICE1712_DELTA_WORD_CLOCK_SELECT, 0);
+static snd_kcontrol_new_t snd_ice1712_delta1010_wordclock_status =
+ICE1712_GPIO(SNDRV_CTL_ELEM_IFACE_PCM, "Word Clock Status", 0, ICE1712_DELTA_WORD_CLOCK_STATUS, SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE);
+static snd_kcontrol_new_t snd_ice1712_deltadio2496_spdif_in_select =
+ICE1712_GPIO(SNDRV_CTL_ELEM_IFACE_PCM, "IEC958 Input Optical", 0, ICE1712_DELTA_SPDIF_INPUT_SELECT, 0);
+static snd_kcontrol_new_t snd_ice1712_delta_spdif_in_status =
+ICE1712_GPIO(SNDRV_CTL_ELEM_IFACE_PCM, "Delta IEC958 Input Status", 0, ICE1712_DELTA_SPDIF_IN_STAT, SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE);
+
+static int snd_ice1712_pro_spdif_master_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_ice1712_pro_spdif_master_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	
+	spin_lock_irqsave(&ice->reg_lock, flags);
+	ucontrol->value.integer.value[0] = inb(ICEMT(ice, RATE)) & ICE1712_SPDIF_MASTER ? 1 : 0;
+	spin_unlock_irqrestore(&ice->reg_lock, flags);
+	return 0;
+}
+
+static int snd_ice1712_pro_spdif_master_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	unsigned long flags;
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	int nval, change;
+
+	nval = ucontrol->value.integer.value[0] ? ICE1712_SPDIF_MASTER : 0;
+	spin_lock_irqsave(&ice->reg_lock, flags);
+	nval |= inb(ICEMT(ice, RATE)) & ~ICE1712_SPDIF_MASTER;
+	change = inb(ICEMT(ice, RATE)) != nval;
+	outb(nval, ICEMT(ice, RATE));
+	spin_unlock_irqrestore(&ice->reg_lock, flags);
+
+	if (ice->cs8427_ops) {
+		/* change CS8427 clock source too */
+		snd_ice1712_cs8427_set_input_clock(ice, ucontrol->value.integer.value[0]);
+	}
+
+	return change;
+}
+
+static snd_kcontrol_new_t snd_ice1712_pro_spdif_master = {
+#ifdef TARGET_OS2
+	SNDRV_CTL_ELEM_IFACE_MIXER,0,0,
+	"Multi Track IEC958 Master",0,0,
+	snd_ice1712_pro_spdif_master_info,
+	snd_ice1712_pro_spdif_master_get,
+	snd_ice1712_pro_spdif_master_put,0
+#else
+	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	name: "Multi Track IEC958 Master",
+	info: snd_ice1712_pro_spdif_master_info,
+	get: snd_ice1712_pro_spdif_master_get,
+	put: snd_ice1712_pro_spdif_master_put
+#endif
+};
+
+/*
+ * routing
+ */
+static int snd_ice1712_pro_route_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	static char *texts[] = {
+		"PCM Out", /* 0 */
+		"H/W In 0", "H/W In 1", "H/W In 2", "H/W In 3", /* 1-4 */
+		"H/W In 4", "H/W In 5", "H/W In 6", "H/W In 7", /* 5-8 */
+		"IEC958 In L", "IEC958 In R", /* 9-10 */
+		"Digital Mixer", /* 11 - optional */
+	};
+	
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = kcontrol->id.index < 2 ? 12 : 11;
+	if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+		uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int snd_ice1712_pro_route_analog_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	int idx = kcontrol->id.index;
+	unsigned int val, cval;
+	val = inw(ICEMT(ice, ROUTE_PSDOUT03));
+	val >>= ((idx % 2) * 8) + ((idx / 2) * 2);
+	val &= 3;
+	cval = inw(ICEMT(ice, ROUTE_CAPTURE));
+	cval >>= ((idx / 2) * 8) + ((idx % 2) * 4);
+	if (val == 1 && idx < 2)
+		ucontrol->value.enumerated.item[0] = 11;
+	else if (val == 2)
+		ucontrol->value.enumerated.item[0] = (cval & 7) + 1;
+	else if (val == 3)
+		ucontrol->value.enumerated.item[0] = ((cval >> 3) & 1) + 9;
+	else
+		ucontrol->value.enumerated.item[0] = 0;
+	return 0;
+}
+
+static int snd_ice1712_pro_route_analog_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	int change, shift;
+	int idx = kcontrol->id.index;
+	unsigned int val, old_val, nval;
+	
+	/* update PSDOUT */
+	if (ucontrol->value.enumerated.item[0] >= 11)
+		nval = idx < 2 ? 1 : 0;
+	else if (ucontrol->value.enumerated.item[0] >= 9)
+		nval = 3;
+	else if (ucontrol->value.enumerated.item[0] >= 1)
+		nval = 2;
+	else
+		nval = 0;
+	shift = ((idx % 2) * 8) + ((idx / 2) * 2);
+	val = old_val = inw(ICEMT(ice, ROUTE_PSDOUT03));
+	val &= ~(0x03 << shift);
+	val |= nval << shift;
+	change = val != old_val;
+	if (change)
+		outw(val, ICEMT(ice, ROUTE_PSDOUT03));
+	if (nval < 2)
+		return change;
+
+	/* update CAPTURE */
+	val = old_val = inw(ICEMT(ice, ROUTE_CAPTURE));
+	shift = ((idx / 2) * 8) + ((idx % 2) * 4);
+	if (nval == 2) {
+		nval = ucontrol->value.enumerated.item[0] - 1;
+		val &= ~(0x07 << shift);
+		val |= nval << shift;
+	} else {
+		nval = (ucontrol->value.enumerated.item[0] - 9) << 3;
+		val &= ~(0x08 << shift);
+		val |= nval << shift;
+	}
+	if (val != old_val) {
+		change = 1;
+		outw(val, ICEMT(ice, ROUTE_CAPTURE));
+	}
+	return change;
+}
+
+static int snd_ice1712_pro_route_spdif_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	int idx = kcontrol->id.index;
+	unsigned int val, cval;
+	val = inw(ICEMT(ice, ROUTE_SPDOUT));
+	cval = (val >> (idx * 4 + 8)) & 0x0f;
+	val = (val >> (idx * 2)) & 0x03;
+	if (val == 1)
+		ucontrol->value.enumerated.item[0] = 11;
+	else if (val == 2)
+		ucontrol->value.enumerated.item[0] = (cval & 7) + 1;
+	else if (val == 3)
+		ucontrol->value.enumerated.item[0] = ((cval >> 3) & 1) + 9;
+	else
+		ucontrol->value.enumerated.item[0] = 0;
+	return 0;
+}
+
+static int snd_ice1712_pro_route_spdif_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	int change, shift;
+	int idx = kcontrol->id.index;
+	unsigned int val, old_val, nval;
+	
+	/* update SPDOUT */
+	val = old_val = inw(ICEMT(ice, ROUTE_SPDOUT));
+	if (ucontrol->value.enumerated.item[0] >= 11)
+		nval = 1;
+	else if (ucontrol->value.enumerated.item[0] >= 9)
+		nval = 3;
+	else if (ucontrol->value.enumerated.item[0] >= 1)
+		nval = 2;
+	else
+		nval = 0;
+	shift = idx * 2;
+	val &= ~(0x03 << shift);
+	val |= nval << shift;
+	shift = idx * 4 + 8;
+	if (nval == 2) {
+		nval = ucontrol->value.enumerated.item[0] - 1;
+		val &= ~(0x07 << shift);
+		val |= nval << shift;
+	} else if (nval == 3) {
+		nval = (ucontrol->value.enumerated.item[0] - 9) << 3;
+		val &= ~(0x08 << shift);
+		val |= nval << shift;
+	}
+	change = val != old_val;
+	if (change)
+		outw(val, ICEMT(ice, ROUTE_SPDOUT));
+	return change;
+}
+
+static snd_kcontrol_new_t snd_ice1712_mixer_pro_analog_route = {
+#ifdef TARGET_OS2
+	SNDRV_CTL_ELEM_IFACE_MIXER,0,0,
+	"H/W Playback Route",0,0,
+	snd_ice1712_pro_route_info,
+	snd_ice1712_pro_route_analog_get,
+	snd_ice1712_pro_route_analog_put,0
+#else
+	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	name: "H/W Playback Route",
+	info: snd_ice1712_pro_route_info,
+	get: snd_ice1712_pro_route_analog_get,
+	put: snd_ice1712_pro_route_analog_put,
+#endif
+};
+
+static snd_kcontrol_new_t snd_ice1712_mixer_pro_spdif_route = {
+#ifdef TARGET_OS2
+	SNDRV_CTL_ELEM_IFACE_MIXER,0,0,
+	"IEC958 Playback Route",0,0,
+	snd_ice1712_pro_route_info,
+	snd_ice1712_pro_route_spdif_get,
+	snd_ice1712_pro_route_spdif_put,0
+#else
+	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	name: "IEC958 Playback Route",
+	info: snd_ice1712_pro_route_info,
+	get: snd_ice1712_pro_route_spdif_get,
+	put: snd_ice1712_pro_route_spdif_put,
+#endif
+};
+
+
+static int snd_ice1712_pro_volume_rate_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 255;
+	return 0;
+}
+
+static int snd_ice1712_pro_volume_rate_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	
+	spin_lock_irqsave(&ice->reg_lock, flags);
+	ucontrol->value.integer.value[0] = inb(ICEMT(ice, MONITOR_RATE));
+	spin_unlock_irqrestore(&ice->reg_lock, flags);
+	return 0;
+}
+
+static int snd_ice1712_pro_volume_rate_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	unsigned long flags;
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	int change;
+
+	spin_lock_irqsave(&ice->reg_lock, flags);
+	change = inb(ICEMT(ice, MONITOR_RATE)) != ucontrol->value.integer.value[0];
+	outb(ucontrol->value.integer.value[0], ICEMT(ice, MONITOR_RATE));
+	spin_unlock_irqrestore(&ice->reg_lock, flags);
+	return change;
+}
+
+static snd_kcontrol_new_t snd_ice1712_mixer_pro_volume_rate = {
+#ifdef TARGET_OS2
+	SNDRV_CTL_ELEM_IFACE_MIXER,0,0,
+	"Multi Track Volume Rate",0,0,
+	snd_ice1712_pro_volume_rate_info,
+	snd_ice1712_pro_volume_rate_get,
+	snd_ice1712_pro_volume_rate_put,0
+#else
+	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	name: "Multi Track Volume Rate",
+	info: snd_ice1712_pro_volume_rate_info,
+	get: snd_ice1712_pro_volume_rate_get,
+	put: snd_ice1712_pro_volume_rate_put
+#endif
+};
+
+static int snd_ice1712_pro_peak_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 22;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 255;
+	return 0;
+}
+
+static int snd_ice1712_pro_peak_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int idx;
+	
+	spin_lock_irqsave(&ice->reg_lock, flags);
+	for (idx = 0; idx < 22; idx++) {
+		outb(idx, ICEMT(ice, MONITOR_PEAKINDEX));
+		ucontrol->value.integer.value[idx] = inb(ICEMT(ice, MONITOR_PEAKDATA));
+	}
+	spin_unlock_irqrestore(&ice->reg_lock, flags);
+	return 0;
+}
+
+static snd_kcontrol_new_t snd_ice1712_mixer_pro_peak = {
+#ifdef TARGET_OS2
+	SNDRV_CTL_ELEM_IFACE_MIXER,0,0,
+	"Multi Track Peak",0,
+	SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+	snd_ice1712_pro_peak_info,
+	snd_ice1712_pro_peak_get,0,0
+#else
+	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	name: "Multi Track Peak",
+	access: SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+	info: snd_ice1712_pro_peak_info,
+	get: snd_ice1712_pro_peak_get
+#endif
+};
+
+/*
+ * EWX 24/96
+ */
+
+static int snd_ice1712_ewx_io_sense_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo){
+
+	static char *texts[4] = {
+		"+4dBu", "-10dBV",
+	};
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 2;
+	if (uinfo->value.enumerated.item >= 2)
+		uinfo->value.enumerated.item = 1;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int snd_ice1712_ewx_io_sense_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	unsigned char mask = kcontrol->private_value & 0xff;
+	unsigned char saved[2];
+	
+	save_gpio_status(ice, saved);
+	ucontrol->value.enumerated.item[0] = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA) & mask ? 1 : 0;
+	restore_gpio_status(ice, saved);
+	return 0;
+}
+
+static int snd_ice1712_ewx_io_sense_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	unsigned char mask = kcontrol->private_value & 0xff;
+	unsigned char saved[2];
+	int val, nval;
+
+	if (kcontrol->private_value & (1 << 31))
+		return -EPERM;
+	nval = ucontrol->value.enumerated.item[0] ? mask : 0;
+	save_gpio_status(ice, saved);
+	val = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA);
+	nval |= val & ~mask;
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, nval);
+	restore_gpio_status(ice, saved);
+	return val != nval;
+}
+
+static snd_kcontrol_new_t snd_ice1712_ewx_input_sense = {
+#ifdef TARGET_OS2
+	SNDRV_CTL_ELEM_IFACE_MIXER,0,0,
+	"Input Sensitivity Switch",0,0,
+	snd_ice1712_ewx_io_sense_info,
+	snd_ice1712_ewx_io_sense_get,
+	snd_ice1712_ewx_io_sense_put,
+	ICE1712_EWX2496_AIN_SEL,
+#else
+	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	name: "Input Sensitivity Switch",
+	info: snd_ice1712_ewx_io_sense_info,
+	get: snd_ice1712_ewx_io_sense_get,
+	put: snd_ice1712_ewx_io_sense_put,
+	private_value: ICE1712_EWX2496_AIN_SEL,
+#endif
+};
+
+static snd_kcontrol_new_t snd_ice1712_ewx_output_sense = {
+#ifdef TARGET_OS2
+	SNDRV_CTL_ELEM_IFACE_MIXER,0,0,
+	"Output Sensitivity Switch",0,0,
+	snd_ice1712_ewx_io_sense_info,
+	snd_ice1712_ewx_io_sense_get,
+	snd_ice1712_ewx_io_sense_put,
+	ICE1712_EWX2496_AOUT_SEL,
+#else
+	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	name: "Output Sensitivity Switch",
+	info: snd_ice1712_ewx_io_sense_info,
+	get: snd_ice1712_ewx_io_sense_get,
+	put: snd_ice1712_ewx_io_sense_put,
+	private_value: ICE1712_EWX2496_AOUT_SEL,
+#endif
+};
+
+
+/*
+ * EWS88MT
+ */
+/* analog output sensitivity;; address 0x48 bit 6 */
+static int snd_ice1712_ews88mt_output_sense_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	unsigned char saved[2];
+	unsigned char data;
+
+	save_gpio_status(ice, saved);
+	data = snd_ice1712_ews88mt_pcf8574_read(ice, ICE1712_EWS88MT_OUTPUT_ADDR);
+	restore_gpio_status(ice, saved);
+	ucontrol->value.enumerated.item[0] = data & ICE1712_EWS88MT_OUTPUT_SENSE ? 1 : 0; /* high = -10dBV, low = +4dBu */
+	return 0;
+}
+
+/* analog output sensitivity;; address 0x48 bit 6 */
+static int snd_ice1712_ews88mt_output_sense_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	unsigned char saved[2];
+	unsigned char data, ndata;
+
+	save_gpio_status(ice, saved);
+	data = snd_ice1712_ews88mt_pcf8574_read(ice, ICE1712_EWS88MT_OUTPUT_ADDR);
+	ndata = (data & ~ICE1712_EWS88MT_OUTPUT_SENSE) | (ucontrol->value.enumerated.item[0] ? ICE1712_EWS88MT_OUTPUT_SENSE : 0);
+	if (ndata != data)
+		snd_ice1712_ews88mt_pcf8574_write(ice, ICE1712_EWS88MT_OUTPUT_ADDR, ndata);
+	restore_gpio_status(ice, saved);
+	return ndata != data;
+
+}
+
+/* analog input sensitivity; address 0x46 */
+static int snd_ice1712_ews88mt_input_sense_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	int channel = kcontrol->id.index;
+	unsigned char saved[2];
+	unsigned char data;
+
+	snd_assert(channel >= 0 && channel <= 7, return 0);
+	save_gpio_status(ice, saved);
+	data = snd_ice1712_ews88mt_pcf8574_read(ice, ICE1712_EWS88MT_INPUT_ADDR);
+	restore_gpio_status(ice, saved);
+	/* reversed; high = +4dBu, low = -10dBV */
+	ucontrol->value.enumerated.item[0] = data & (1 << channel) ? 0 : 1;
+	return 0;
+}
+
+/* analog output sensitivity; address 0x46 */
+static int snd_ice1712_ews88mt_input_sense_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	int channel = kcontrol->id.index;
+	unsigned char saved[2];
+	unsigned char data, ndata;
+
+	snd_assert(channel >= 0 && channel <= 7, return 0);
+	save_gpio_status(ice, saved);
+	data = snd_ice1712_ews88mt_pcf8574_read(ice, ICE1712_EWS88MT_INPUT_ADDR);
+	ndata = (data & ~(1 << channel)) | (ucontrol->value.enumerated.item[0] ? 0 : (1 << channel));
+	if (ndata != data)
+		snd_ice1712_ews88mt_pcf8574_write(ice, ICE1712_EWS88MT_INPUT_ADDR, ndata);
+	restore_gpio_status(ice, saved);
+	return ndata != data;
+}
+
+static snd_kcontrol_new_t snd_ice1712_ews88mt_input_sense = {
+#ifdef TARGET_OS2
+	SNDRV_CTL_ELEM_IFACE_MIXER,0,0,
+	"Input Sensitivity Switch",0,0,
+	snd_ice1712_ewx_io_sense_info,
+	snd_ice1712_ews88mt_input_sense_get,
+	snd_ice1712_ews88mt_input_sense_put,0
+#else
+	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	name: "Input Sensitivity Switch",
+	info: snd_ice1712_ewx_io_sense_info,
+	get: snd_ice1712_ews88mt_input_sense_get,
+	put: snd_ice1712_ews88mt_input_sense_put,
+#endif
+};
+
+static snd_kcontrol_new_t snd_ice1712_ews88mt_output_sense = {
+#ifdef TARGET_OS2
+	SNDRV_CTL_ELEM_IFACE_MIXER,0,0,
+	"Output Sensitivity Switch",0,0,
+	snd_ice1712_ewx_io_sense_info,
+	snd_ice1712_ews88mt_output_sense_get,
+	snd_ice1712_ews88mt_output_sense_put,0
+#else
+	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	name: "Output Sensitivity Switch",
+	info: snd_ice1712_ewx_io_sense_info,
+	get: snd_ice1712_ews88mt_output_sense_get,
+	put: snd_ice1712_ews88mt_output_sense_put,
+#endif
+};
+
+
+/*
+ * EWS88D controls
+ */
+
+static int snd_ice1712_ews88d_control_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_ice1712_ews88d_control_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	int shift = kcontrol->private_value & 0xff;
+	int invert = (kcontrol->private_value >> 8) & 1;
+	unsigned short data;
+	
+	data = snd_ice1712_ews88d_pcf8575_read(ice, ICE1712_EWS88D_PCF_ADDR);
+	//printk("pcf: read 0x%x\n", data);
+	data = (data >> shift) & 0x01;
+	if (invert)
+		data ^= 0x01;
+	ucontrol->value.integer.value[0] = data;
+	return 0;
+}
+
+static int snd_ice1712_ews88d_control_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	int shift = kcontrol->private_value & 0xff;
+	int invert = (kcontrol->private_value >> 8) & 1;
+	unsigned short data, ndata;
+	int change;
+
+	data = snd_ice1712_ews88d_pcf8575_read(ice, ICE1712_EWS88D_PCF_ADDR);
+	ndata = data & ~(1 << shift);
+	if (invert) {
+		if (! ucontrol->value.integer.value[0])
+			ndata |= (1 << shift);
+	} else {
+		if (ucontrol->value.integer.value[0])
+			ndata |= (1 << shift);
+	}
+	change = (data != ndata);
+	if (change) {
+		ndata &= 0xff;
+		ndata |= (unsigned short)ice->cs8403_spdif_stream_bits << 8;
+		snd_ice1712_ews88d_pcf8575_write(ice, ICE1712_EWS88D_PCF_ADDR, ndata);
+		//printk("pcf: write 0x%x\n", ndata);
+	}
+	return change;
+}
+
+#ifdef TARGET_OS2
+#define EWS88D_CONTROL(xiface, xname, xindex, xshift, xinvert, xaccess) \
+{ xiface,0,0,\
+  xname, 0,\
+  xaccess,\
+  snd_ice1712_ews88d_control_info,\
+  snd_ice1712_ews88d_control_get,\
+  snd_ice1712_ews88d_control_put,\
+  xshift | (xinvert << 8),\
+}
+#else
+#define EWS88D_CONTROL(xiface, xname, xindex, xshift, xinvert, xaccess) \
+{ iface: xiface,\
+  name: xname,\
+  access: xaccess,\
+  info: snd_ice1712_ews88d_control_info,\
+  get: snd_ice1712_ews88d_control_get,\
+  put: snd_ice1712_ews88d_control_put,\
+  private_value: xshift | (xinvert << 8),\
+}
+#endif
+
+static snd_kcontrol_new_t snd_ice1712_ews88d_spdif_in_opt =
+EWS88D_CONTROL(SNDRV_CTL_ELEM_IFACE_MIXER, "IEC958 Input Optical", 0, 0, 1, 0); /* inverted */
+static snd_kcontrol_new_t snd_ice1712_ews88d_opt_out_adat =
+EWS88D_CONTROL(SNDRV_CTL_ELEM_IFACE_MIXER, "ADAT Output Optical", 0, 1, 0, 0);
+static snd_kcontrol_new_t snd_ice1712_ews88d_master_adat =
+EWS88D_CONTROL(SNDRV_CTL_ELEM_IFACE_MIXER, "ADAT External Master Clock", 0, 2, 0, 0);
+static snd_kcontrol_new_t snd_ice1712_ews88d_adat_enable =
+EWS88D_CONTROL(SNDRV_CTL_ELEM_IFACE_MIXER, "Enable ADAT", 0, 3, 0, 0);
+static snd_kcontrol_new_t snd_ice1712_ews88d_adat_through =
+EWS88D_CONTROL(SNDRV_CTL_ELEM_IFACE_MIXER, "ADAT Through", 0, 4, 1, 0);
+
+
+/*
+ *
+ */
+
+static unsigned char __init snd_ice1712_read_i2c(ice1712_t *ice,
+						 unsigned char dev,
+						 unsigned char addr)
+{
+	long t = 0x10000;
+
+	outb(addr, ICEREG(ice, I2C_BYTE_ADDR));
+	outb(dev & ~ICE1712_I2C_WRITE, ICEREG(ice, I2C_DEV_ADDR));
+	while (t-- > 0 && (inb(ICEREG(ice, I2C_CTRL)) & ICE1712_I2C_BUSY)) ;
+	return inb(ICEREG(ice, I2C_DATA));
+}
+
+static int __init snd_ice1712_read_eeprom(ice1712_t *ice)
+{
+	int dev = 0xa0;		/* EEPROM device address */
+	unsigned int idx;
+
+	if ((inb(ICEREG(ice, I2C_CTRL)) & ICE1712_I2C_EEPROM) == 0) {
+		snd_printk("ICE1712 has not detected EEPROM\n");
+		return -EIO;
+	}
+	ice->eeprom.subvendor = (snd_ice1712_read_i2c(ice, dev, 0x00) << 0) |
+				(snd_ice1712_read_i2c(ice, dev, 0x01) << 8) | 
+				(snd_ice1712_read_i2c(ice, dev, 0x02) << 16) | 
+				(snd_ice1712_read_i2c(ice, dev, 0x03) << 24);
+	ice->eeprom.size = snd_ice1712_read_i2c(ice, dev, 0x04);
+	if (ice->eeprom.size < 28) {
+		snd_printk("invalid EEPROM (size = %i)\n", ice->eeprom.size);
+		return -EIO;
+	}
+	ice->eeprom.version = snd_ice1712_read_i2c(ice, dev, 0x05);
+	if (ice->eeprom.version != 1) {
+		snd_printk("invalid EEPROM version %i\n", ice->eeprom.version);
+		return -EIO;
+	}
+	ice->eeprom.codec = snd_ice1712_read_i2c(ice, dev, 0x06);
+	ice->eeprom.aclink = snd_ice1712_read_i2c(ice, dev, 0x07);
+	ice->eeprom.i2sID = snd_ice1712_read_i2c(ice, dev, 0x08);
+	ice->eeprom.spdif = snd_ice1712_read_i2c(ice, dev, 0x09);
+	ice->eeprom.gpiomask = snd_ice1712_read_i2c(ice, dev, 0x0a);
+	ice->eeprom.gpiostate = snd_ice1712_read_i2c(ice, dev, 0x0b);
+	ice->eeprom.gpiodir = snd_ice1712_read_i2c(ice, dev, 0x0c);
+	ice->eeprom.ac97main = (snd_ice1712_read_i2c(ice, dev, 0x0d) << 0) |
+			       (snd_ice1712_read_i2c(ice, dev, 0x0e) << 8);
+	ice->eeprom.ac97pcm = (snd_ice1712_read_i2c(ice, dev, 0x0f) << 0) |
+			      (snd_ice1712_read_i2c(ice, dev, 0x10) << 8);
+	ice->eeprom.ac97rec = (snd_ice1712_read_i2c(ice, dev, 0x11) << 0) |
+			      (snd_ice1712_read_i2c(ice, dev, 0x12) << 8);
+	ice->eeprom.ac97recsrc = snd_ice1712_read_i2c(ice, dev, 0x13) << 0;
+	for (idx = 0; idx < 4; idx++) {
+		ice->eeprom.dacID[idx] = snd_ice1712_read_i2c(ice, dev, 0x14 + idx);
+		ice->eeprom.adcID[idx] = snd_ice1712_read_i2c(ice, dev, 0x18 + idx);
+	}
+	for (idx = 0x1c; idx < ice->eeprom.size && idx < 0x1c + sizeof(ice->eeprom.extra); idx++)
+		ice->eeprom.extra[idx - 0x1c] = snd_ice1712_read_i2c(ice, dev, idx);
+	return 0;
+}
+
+static void __init snd_ice1712_ak4524_init(ice1712_t *ice)
+{
+	static unsigned char inits[8] = {
+		0x07, /* 0: all power up */
+		0x03, /* 1: ADC/DAC reset */
+		0x60, /* 2: 24bit I2S */
+		0x19, /* 3: deemphasis off */
+		0x00, /* 4: ADC left muted */
+		0x00, /* 5: ADC right muted */
+		0x00, /* 6: DAC left muted */
+		0x00, /* 7: DAC right muted */
+	};
+	int chip;
+	unsigned char reg;
+
+	for (chip = 0; chip < ice->num_dacs/2; chip++) {
+		for (reg = 0; reg < 8; ++reg)
+			snd_ice1712_ak4524_write(ice, chip, reg, inits[reg]);
+	}
+}
+
+/* init CS8427 transciever */
+static void __init snd_ice1712_cs8427_init(ice1712_t *ice)
+{
+	static unsigned char initvals[] = {
+		/* RMCK to OMCK, no validity, disable mutes, TCBL=output */
+		0x80,
+		/* hold last valid audio sample, RMCK=256*Fs, normal stereo operation */
+		0x00,
+		/* output drivers normal operation, Tx<=serial, Rx=>serial */
+		0x0c,
+		/* Run off, CMCK=256*Fs, output time base = OMCK, input time base =
+		   covered input clock, recovered input clock source is Envy24 */
+		0x04,
+		/* Serial audio input port data format = I2S */
+		0x05, /* SIDEL=1, SILRPOL=1 */
+		/* Serial audio output port data format = I2S */
+		0x05,	/* SODEL=1, SOLRPOL=1 */
+	};
+	unsigned char buf[32];
+
+	/* verify CS8427 ID */
+	if (ice->cs8427_ops->read(ice, 127) != 0x71) {
+		snd_printk("unable to find CS8427 signature, initialization is not completed\n");
+		return;
+	}
+	/* turn off run bit while making changes to configuration */
+	ice->cs8427_ops->write(ice, 4, 0x00);
+	/* send initial values */
+	ice->cs8427_ops->write_bytes(ice, 1, 6, initvals);
+	/* Turn off CS8427 interrupt stuff that is not used in hardware */
+	memset(buf, 0, sizeof(buf));
+	/* from address 9 to 15 */
+	ice->cs8427_ops->write_bytes(ice, 9, 7, buf);
+	/* unmask the input PLL clock, V, confidence, biphase, parity status bits */
+	//buf[0] = 0x1f;
+	buf[0] = 0;
+	/* Registers 32-55 window to CS buffer
+	   Inhibit D->E transfers from overwriting first 5 bytes of CS data.
+	   Allow D->E transfers (all) of CS data.
+	   Allow E->F transfer of CS data.
+	   One byte mode; both A/B channels get same written CB data.
+	   A channel info is output to chip's EMPH* pin. */
+	buf[1] = 0x10;
+	/* Use internal buffer to transmit User (U) data.
+	   Chip's U pin is an output.
+	   Transmit all O's for user data. */
+	//buf[2] = 0x10;
+	buf[2] = 0x00;
+	ice->cs8427_ops->write_bytes(ice, 17, 3, buf);
+	/* turn on run bit and rock'n'roll */
+	ice->cs8427_ops->write(ice, 4, initvals[3] | 0x40);
+	/* write default channel status bytes */
+	ice->cs8427_ops->write_bytes(ice, 32, 5, ice->cs8427_spdif_status);
+
+	/*
+	 * add controls..
+	 */
+	snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_cs8427_in_status, ice));
+#if 0
+	snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_cs8427_clock_select, ice));
+#endif
+}
+
+static void __init snd_ice1712_stdsp24_gpio_write(ice1712_t *ice, unsigned char byte)
+{
+	byte |= ICE1712_STDSP24_CLOCK_BIT;
+	udelay(100);
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, byte);
+	byte &= ~ICE1712_STDSP24_CLOCK_BIT;
+	udelay(100);
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, byte);
+	byte |= ICE1712_STDSP24_CLOCK_BIT;
+	udelay(100);
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, byte);
+}
+
+static void __init snd_ice1712_stdsp24_darear(ice1712_t *ice, int activate)
+{
+	down(&ice->gpio_mutex);
+	ICE1712_STDSP24_0_DAREAR(ice->hoontech_boxbits, activate);
+	snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[0]);
+	up(&ice->gpio_mutex);
+}
+
+static void __init snd_ice1712_stdsp24_mute(ice1712_t *ice, int activate)
+{
+	down(&ice->gpio_mutex);
+	ICE1712_STDSP24_3_MUTE(ice->hoontech_boxbits, activate);
+	snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[3]);
+	up(&ice->gpio_mutex);
+}
+
+static void __init snd_ice1712_stdsp24_insel(ice1712_t *ice, int activate)
+{
+	down(&ice->gpio_mutex);
+	ICE1712_STDSP24_3_INSEL(ice->hoontech_boxbits, activate);
+	snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[3]);
+	up(&ice->gpio_mutex);
+}
+
+static void __init snd_ice1712_stdsp24_box_channel(ice1712_t *ice, int box, int chn, int activate)
+{
+	down(&ice->gpio_mutex);
+
+	/* select box */
+	ICE1712_STDSP24_0_BOX(ice->hoontech_boxbits, box);
+	snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[0]);
+
+	/* prepare for write */
+	if (chn == 3)
+		ICE1712_STDSP24_2_CHN4(ice->hoontech_boxbits, 0);
+	ICE1712_STDSP24_2_MIDI1(ice->hoontech_boxbits, activate);
+	snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[2]);
+
+	ICE1712_STDSP24_1_CHN1(ice->hoontech_boxbits, 1);
+	ICE1712_STDSP24_1_CHN2(ice->hoontech_boxbits, 1);
+	ICE1712_STDSP24_1_CHN3(ice->hoontech_boxbits, 1);
+	ICE1712_STDSP24_2_CHN4(ice->hoontech_boxbits, 1);
+	snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[1]);
+	snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[2]);
+	udelay(100);
+	if (chn == 3) {
+		ICE1712_STDSP24_2_CHN4(ice->hoontech_boxbits, 0);
+		snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[2]);
+	} else {
+		switch (chn) {
+		case 0:	ICE1712_STDSP24_1_CHN1(ice->hoontech_boxbits, 0); break;
+		case 1:	ICE1712_STDSP24_1_CHN2(ice->hoontech_boxbits, 0); break;
+		case 2:	ICE1712_STDSP24_1_CHN3(ice->hoontech_boxbits, 0); break;
+		}
+		snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[1]);
+	}
+	udelay(100);
+	ICE1712_STDSP24_1_CHN1(ice->hoontech_boxbits, 1);
+	ICE1712_STDSP24_1_CHN2(ice->hoontech_boxbits, 1);
+	ICE1712_STDSP24_1_CHN3(ice->hoontech_boxbits, 1);
+	ICE1712_STDSP24_2_CHN4(ice->hoontech_boxbits, 1);
+	snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[1]);
+	snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[2]);
+	udelay(100);
+
+	ICE1712_STDSP24_2_MIDI1(ice->hoontech_boxbits, 0);
+	snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[2]);
+
+	up(&ice->gpio_mutex);
+}
+
+static void __init snd_ice1712_stdsp24_box_midi(ice1712_t *ice, int box, int master, int slave)
+{
+	down(&ice->gpio_mutex);
+
+	/* select box */
+	ICE1712_STDSP24_0_BOX(ice->hoontech_boxbits, box);
+	snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[0]);
+
+	ICE1712_STDSP24_2_MIDIIN(ice->hoontech_boxbits, 1);
+	ICE1712_STDSP24_2_MIDI1(ice->hoontech_boxbits, master);
+	snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[2]);
+
+	udelay(100);
+	
+	ICE1712_STDSP24_2_MIDIIN(ice->hoontech_boxbits, 0);
+	snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[2]);
+	
+	udelay(100);
+	
+	ICE1712_STDSP24_2_MIDIIN(ice->hoontech_boxbits, 1);
+	snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[2]);
+
+	udelay(100);
+
+	/* MIDI2 is direct */
+	ICE1712_STDSP24_3_MIDI2(ice->hoontech_boxbits, slave);
+	snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[3]);
+
+	up(&ice->gpio_mutex);
+}
+
+static void __init snd_ice1712_stdsp24_init(ice1712_t *ice)
+{
+	int box, chn;
+
+	ice->hoontech_boxbits[0] = 
+	ice->hoontech_boxbits[1] = 
+	ice->hoontech_boxbits[2] = 
+	ice->hoontech_boxbits[3] = 0;	/* should be already */
+
+	ICE1712_STDSP24_SET_ADDR(ice->hoontech_boxbits, 0);
+	ICE1712_STDSP24_CLOCK(ice->hoontech_boxbits, 0, 1);
+	ICE1712_STDSP24_0_BOX(ice->hoontech_boxbits, 0);
+	ICE1712_STDSP24_0_DAREAR(ice->hoontech_boxbits, 0);
+
+	ICE1712_STDSP24_SET_ADDR(ice->hoontech_boxbits, 1);
+	ICE1712_STDSP24_CLOCK(ice->hoontech_boxbits, 1, 1);
+	ICE1712_STDSP24_1_CHN1(ice->hoontech_boxbits, 1);
+	ICE1712_STDSP24_1_CHN2(ice->hoontech_boxbits, 1);
+	ICE1712_STDSP24_1_CHN3(ice->hoontech_boxbits, 1);
+	
+	ICE1712_STDSP24_SET_ADDR(ice->hoontech_boxbits, 2);
+	ICE1712_STDSP24_CLOCK(ice->hoontech_boxbits, 2, 1);
+	ICE1712_STDSP24_2_CHN4(ice->hoontech_boxbits, 1);
+	ICE1712_STDSP24_2_MIDIIN(ice->hoontech_boxbits, 1);
+	ICE1712_STDSP24_2_MIDI1(ice->hoontech_boxbits, 0);
+
+	ICE1712_STDSP24_SET_ADDR(ice->hoontech_boxbits, 3);
+	ICE1712_STDSP24_CLOCK(ice->hoontech_boxbits, 3, 1);
+	ICE1712_STDSP24_3_MIDI2(ice->hoontech_boxbits, 0);
+	ICE1712_STDSP24_3_MUTE(ice->hoontech_boxbits, 1);
+	ICE1712_STDSP24_3_INSEL(ice->hoontech_boxbits, 0);
+
+	/* let's go - activate only functions in first box */
+	ice->hoontech_config = 0;
+			    /* ICE1712_STDSP24_MUTE |
+			       ICE1712_STDSP24_INSEL |
+			       ICE1712_STDSP24_DAREAR; */
+	ice->hoontech_boxconfig[0] = ICE1712_STDSP24_BOX_CHN1 |
+				     ICE1712_STDSP24_BOX_CHN2 |
+				     ICE1712_STDSP24_BOX_CHN3 |
+				     ICE1712_STDSP24_BOX_CHN4 |
+				     ICE1712_STDSP24_BOX_MIDI1 |
+				     ICE1712_STDSP24_BOX_MIDI2;
+	ice->hoontech_boxconfig[1] = 
+	ice->hoontech_boxconfig[2] = 
+	ice->hoontech_boxconfig[3] = 0;
+	snd_ice1712_stdsp24_darear(ice, (ice->hoontech_config & ICE1712_STDSP24_DAREAR) ? 1 : 0);
+	snd_ice1712_stdsp24_mute(ice, (ice->hoontech_config & ICE1712_STDSP24_MUTE) ? 1 : 0);
+	snd_ice1712_stdsp24_insel(ice, (ice->hoontech_config & ICE1712_STDSP24_INSEL) ? 1 : 0);
+	for (box = 0; box < 4; box++) {
+		for (chn = 0; chn < 4; chn++)
+			snd_ice1712_stdsp24_box_channel(ice, box, chn, (ice->hoontech_boxconfig[box] & (1 << chn)) ? 1 : 0);
+		snd_ice1712_stdsp24_box_midi(ice, box,
+				(ice->hoontech_boxconfig[box] & ICE1712_STDSP24_BOX_MIDI1) ? 1 : 0,
+				(ice->hoontech_boxconfig[box] & ICE1712_STDSP24_BOX_MIDI2) ? 1 : 0);
+	}
+}
+
+static int __init snd_ice1712_chip_init(ice1712_t *ice)
+{
+	outb(ICE1712_RESET | ICE1712_NATIVE, ICEREG(ice, CONTROL));
+	udelay(200);
+	outb(ICE1712_NATIVE, ICEREG(ice, CONTROL));
+	udelay(200);
+	pci_write_config_byte(ice->pci, 0x60, ice->eeprom.codec);
+	pci_write_config_byte(ice->pci, 0x61, ice->eeprom.aclink);
+	pci_write_config_byte(ice->pci, 0x62, ice->eeprom.i2sID);
+	pci_write_config_byte(ice->pci, 0x63, ice->eeprom.spdif);
+	if (ice->eeprom.subvendor != ICE1712_SUBDEVICE_STDSP24) {
+		ice->gpio_write_mask = ice->eeprom.gpiomask;
+		ice->gpio_direction = ice->eeprom.gpiodir;
+		snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, ice->eeprom.gpiomask);
+		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION, ice->eeprom.gpiodir);
+		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, ice->eeprom.gpiostate);
+	} else {
+		ice->gpio_write_mask = 0xc0;
+		ice->gpio_direction = 0xff;
+		snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, 0xc0);
+		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION, 0xff);
+		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, ICE1712_STDSP24_CLOCK_BIT);
+	}
+	snd_ice1712_write(ice, ICE1712_IREG_PRO_POWERDOWN, 0);
+	if (!(ice->eeprom.codec & ICE1712_CFG_NO_CON_AC97)) {
+		outb(ICE1712_AC97_WARM, ICEREG(ice, AC97_CMD));
+		udelay(100);
+		outb(0, ICEREG(ice, AC97_CMD));
+		udelay(200);
+		snd_ice1712_write(ice, ICE1712_IREG_CONSUMER_POWERDOWN, 0);
+	}
+
+	switch (ice->eeprom.subvendor) {
+	case ICE1712_SUBDEVICE_AUDIOPHILE:
+	case ICE1712_SUBDEVICE_EWX2496:
+		ice->num_adcs = ice->num_dacs = 2;
+		break;	
+	case ICE1712_SUBDEVICE_DELTA44:
+	case ICE1712_SUBDEVICE_DELTA66:
+		ice->num_adcs = ice->num_dacs = 4;
+		break;
+	case ICE1712_SUBDEVICE_EWS88MT:
+		ice->num_adcs = ice->num_dacs = 8;
+		break;
+	}
+
+	switch (ice->eeprom.subvendor) {
+	case ICE1712_SUBDEVICE_DELTA66:
+	case ICE1712_SUBDEVICE_DELTA44:
+	case ICE1712_SUBDEVICE_AUDIOPHILE:
+	case ICE1712_SUBDEVICE_EWX2496:
+	case ICE1712_SUBDEVICE_EWS88MT:
+		snd_ice1712_ak4524_init(ice);
+		break;
+	case ICE1712_SUBDEVICE_STDSP24:
+		snd_ice1712_stdsp24_init(ice);
+		break;
+	}
+	switch (ice->eeprom.subvendor) {
+	case ICE1712_SUBDEVICE_AUDIOPHILE:
+		ice->cs8427_ops = &snd_ice1712_ap_cs8427_ops;
+		snd_ice1712_cs8427_init(ice);
+		break;
+	case ICE1712_SUBDEVICE_EWX2496:
+		ice->cs8427_ops = &snd_ice1712_ewx_cs8427_ops;
+		snd_ice1712_cs8427_init(ice);
+		break;
+	case ICE1712_SUBDEVICE_DELTA1010:
+	case ICE1712_SUBDEVICE_DELTADIO2496:
+	case ICE1712_SUBDEVICE_DELTA66:
+		/* Set spdif defaults */
+		snd_ice1712_delta_cs8403_spdif_write(ice, ice->cs8403_spdif_bits);
+		break;
+	case ICE1712_SUBDEVICE_EWS88MT:
+	case ICE1712_SUBDEVICE_EWS88D:
+		/* Set spdif defaults */
+		snd_ice1712_ews_cs8404_spdif_write(ice, ice->cs8403_spdif_bits);
+		break;
+	}
+	return 0;
+}
+
+static int __init snd_ice1712_build_controls(ice1712_t *ice)
+{
+	unsigned int idx;
+	snd_kcontrol_t *kctl;
+	int err;
+
+	err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_eeprom, ice));
+	if (err < 0)
+		return err;
+	err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_pro_spdif_master, ice));
+	if (err < 0)
+		return err;
+	for (idx = 0; idx < ice->num_dacs; idx++) {
+		kctl = snd_ctl_new1(&snd_ice1712_mixer_pro_analog_route, ice);
+		if (kctl == NULL)
+			return -ENOMEM;
+		kctl->id.index = idx;
+		err = snd_ctl_add(ice->card, kctl);
+		if (err < 0)
+			return err;
+	}
+	for (idx = 0; idx < 2; idx++) {
+		kctl = snd_ctl_new1(&snd_ice1712_mixer_pro_spdif_route, ice);
+		if (kctl == NULL)
+			return -ENOMEM;
+		kctl->id.index = idx;
+		err = snd_ctl_add(ice->card, kctl);
+		if (err < 0)
+			return err;
+	}
+	err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_mixer_pro_volume_rate, ice));
+	if (err < 0)
+		return err;
+	err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_mixer_pro_peak, ice));
+	if (err < 0)
+		return err;
+	switch (ice->eeprom.subvendor) {
+	case ICE1712_SUBDEVICE_DELTA1010:
+		err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_delta1010_wordclock_select, ice));
+		if (err < 0)
+			return err;
+		err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_delta1010_wordclock_status, ice));
+		if (err < 0)
+			return err;
+		break;
+	case ICE1712_SUBDEVICE_DELTADIO2496:
+		err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_deltadio2496_spdif_in_select, ice));
+		if (err < 0)
+			return err;
+		break;
+	}
+	switch (ice->eeprom.subvendor) {
+	case ICE1712_SUBDEVICE_DELTA1010:
+	case ICE1712_SUBDEVICE_DELTADIO2496:
+	case ICE1712_SUBDEVICE_DELTA66:
+	case ICE1712_SUBDEVICE_AUDIOPHILE:
+	case ICE1712_SUBDEVICE_EWX2496:
+	case ICE1712_SUBDEVICE_EWS88MT:
+	case ICE1712_SUBDEVICE_EWS88D:
+		snd_assert(ice->pcm_pro != NULL, return -EIO);
+		err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_ice1712_spdif_default, ice));
+		if (err < 0)
+			return err;
+		kctl->id.device = ice->pcm_pro->device;
+		err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_ice1712_spdif_maskc, ice));
+		if (err < 0)
+			return err;
+		kctl->id.device = ice->pcm_pro->device;
+		err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_ice1712_spdif_maskp, ice));
+		if (err < 0)
+			return err;
+		kctl->id.device = ice->pcm_pro->device;
+		err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_ice1712_spdif_stream, ice));
+		if (err < 0)
+			return err;
+		kctl->id.device = ice->pcm_pro->device;
+		ice->spdif_stream_ctl = kctl;
+		break;
+	}
+	switch (ice->eeprom.subvendor) {
+	case ICE1712_SUBDEVICE_DELTA1010:
+	case ICE1712_SUBDEVICE_DELTADIO2496:
+	case ICE1712_SUBDEVICE_DELTA66:
+		err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_delta_spdif_in_status, ice));
+		if (err < 0)
+			return err;
+		break;
+	}
+	switch (ice->eeprom.subvendor) {
+	case ICE1712_SUBDEVICE_EWX2496:
+	case ICE1712_SUBDEVICE_AUDIOPHILE:
+	case ICE1712_SUBDEVICE_DELTA44:
+	case ICE1712_SUBDEVICE_DELTA66:
+	case ICE1712_SUBDEVICE_EWS88MT:
+		for (idx = 0; idx < ice->num_dacs; ++idx) {
+			snd_kcontrol_t ctl;
+			memset(&ctl, 0, sizeof(ctl));
+			strcpy(ctl.id.name, "DAC Volume");
+			ctl.id.index = idx;
+			ctl.id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+			ctl.info = snd_ice1712_ak4524_volume_info;
+			ctl.access = SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE;
+			ctl.get = snd_ice1712_ak4524_volume_get;
+			ctl.put = snd_ice1712_ak4524_volume_put;
+			ctl.private_value = (idx / 2) * 8 + (idx % 2) + 6; /* reigster 6 & 7 */
+			ctl.private_data = ice;
+			if ((err = snd_ctl_add(ice->card, snd_ctl_new(&ctl))) < 0)
+				return err;
+		}
+		for (idx = 0; idx < ice->num_adcs; ++idx) {
+			snd_kcontrol_t ctl;
+			memset(&ctl, 0, sizeof(ctl));
+			strcpy(ctl.id.name, "ADC Volume");
+			ctl.id.index = idx;
+			ctl.id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+			ctl.info = snd_ice1712_ak4524_volume_info;
+			ctl.access = SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE;
+			ctl.get = snd_ice1712_ak4524_volume_get;
+			ctl.put = snd_ice1712_ak4524_volume_put;
+			ctl.private_value = (idx / 2) * 8 + (idx % 2) + 4; /* reigster 4 & 5 */
+			ctl.private_data = ice;
+			if ((err = snd_ctl_add(ice->card, snd_ctl_new(&ctl))) < 0)
+				return err;
+		}
+		for (idx = 0; idx < ice->num_dacs/2; idx++) {
+			snd_kcontrol_t ctl;
+			memset(&ctl, 0, sizeof(ctl));
+			strcpy(ctl.id.name, "Deemphasis");
+			ctl.id.index = idx;
+			ctl.id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+			ctl.info = snd_ice1712_ak4524_deemphasis_info;
+			ctl.access = SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE;
+			ctl.get = snd_ice1712_ak4524_deemphasis_get;
+			ctl.put = snd_ice1712_ak4524_deemphasis_put;
+			ctl.private_data = ice;
+			if ((err = snd_ctl_add(ice->card, snd_ctl_new(&ctl))) < 0)
+				return err;
+		}
+		break;
+	}
+	switch (ice->eeprom.subvendor) {
+	case ICE1712_SUBDEVICE_EWX2496:
+		err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_ewx_input_sense, ice));
+		if (err < 0)
+			return err;
+		err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_ewx_output_sense, ice));
+		if (err < 0)
+			return err;
+		break;
+	case ICE1712_SUBDEVICE_EWS88MT:
+		for (idx = 0; idx < 8; idx++) {
+			kctl = snd_ctl_new1(&snd_ice1712_ews88mt_input_sense, ice);
+			kctl->id.index = idx;
+			err = snd_ctl_add(ice->card, kctl);
+			if (err < 0)
+				return err;
+		}
+		err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_ews88mt_output_sense, ice));
+		if (err < 0)
+			return err;
+		break;
+	case ICE1712_SUBDEVICE_EWS88D:
+		err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_ews88d_spdif_in_opt, ice));
+		if (err < 0)
+			return err;
+		err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_ews88d_opt_out_adat, ice));
+		if (err < 0)
+			return err;
+		err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_ews88d_master_adat, ice));
+		if (err < 0)
+			return err;
+		err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_ews88d_adat_enable, ice));
+		if (err < 0)
+			return err;
+		err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_ews88d_adat_through, ice));
+		if (err < 0)
+			return err;
+		break;
+	}
+
+	return 0;
+}
+
+static int __exit snd_ice1712_free(ice1712_t *ice)
+{
+	if (ice->res_port == NULL)
+		goto __hw_end;
+	/* mask all interrupts */
+	outb(0xc0, ICEMT(ice, IRQ));
+	outb(0xff, ICEREG(ice, IRQMASK));
+	/* --- */
+      __hw_end:
+	snd_ice1712_proc_done(ice);
+	synchronize_irq();
+	if (ice->irq)
+		free_irq(ice->irq, (void *) ice);
+	if (ice->res_port)
+		release_resource(ice->res_port);
+	if (ice->res_ddma_port)
+		release_resource(ice->res_ddma_port);
+	if (ice->res_dmapath_port)
+		release_resource(ice->res_dmapath_port);
+	if (ice->res_profi_port)
+		release_resource(ice->res_profi_port);
+	snd_magic_kfree(ice);
+	return 0;
+}
+
+static int __exit snd_ice1712_dev_free(snd_device_t *device)
+{
+	ice1712_t *ice = snd_magic_cast(ice1712_t, device->device_data, return -ENXIO);
+	return snd_ice1712_free(ice);
+}
+
+static int __init snd_ice1712_create(snd_card_t * card,
+				     struct pci_dev *pci,
+				     ice1712_t ** r_ice1712)
+{
+	ice1712_t *ice;
+	int err;
+#ifdef TARGET_OS2
+	static snd_device_ops_t ops = {
+		snd_ice1712_dev_free,0,0,0
+	};
+#else
+	static snd_device_ops_t ops = {
+		dev_free:	snd_ice1712_dev_free,
+	};
+#endif
+	*r_ice1712 = NULL;
+
+        /* enable PCI device */
+	if ((err = pci_enable_device(pci)) < 0)
+		return err;
+	/* check, if we can restrict PCI DMA transfers to 28 bits */
+	if (!pci_dma_supported(pci, 0x0fffffff)) {
+		snd_printk("architecture does not support 28bit PCI busmaster DMA\n");
+		return -ENXIO;
+	}
+	pci_set_dma_mask(pci, 0x0fffffff);
+
+	ice = snd_magic_kcalloc(ice1712_t, 0, GFP_KERNEL);
+	if (ice == NULL)
+		return -ENOMEM;
+	spin_lock_init(&ice->reg_lock);
+	init_MUTEX(&ice->gpio_mutex);
+	ice->cs8403_spdif_bits =
+	ice->cs8403_spdif_stream_bits = (0x01 |	/* consumer format */
+					 0x10 |	/* no emphasis */
+					 0x20);	/* PCM encoder/decoder */
+	ice->cs8427_spdif_status[0] = (SNDRV_PCM_DEFAULT_CON_SPDIF >> 0) & 0xff;
+	ice->cs8427_spdif_status[1] = (SNDRV_PCM_DEFAULT_CON_SPDIF >> 8) & 0xff;
+	ice->cs8427_spdif_status[2] = (SNDRV_PCM_DEFAULT_CON_SPDIF >> 16) & 0xff;
+	ice->cs8427_spdif_status[3] = (SNDRV_PCM_DEFAULT_CON_SPDIF >> 24) & 0xff;
+	memcpy(ice->cs8427_spdif_stream_status, ice->cs8427_spdif_status, 5);
+	ice->card = card;
+	ice->pci = pci;
+	ice->irq = -1;
+	ice->port = pci_resource_start(pci, 0);
+	ice->ddma_port = pci_resource_start(pci, 1);
+	ice->dmapath_port = pci_resource_start(pci, 2);
+	ice->profi_port = pci_resource_start(pci, 3);
+	pci_set_master(pci);
+	pci_write_config_word(ice->pci, 0x40, 0x807f);
+	pci_write_config_word(ice->pci, 0x42, 0x0006);
+	snd_ice1712_proc_init(ice);
+	synchronize_irq();
+
+	if ((ice->res_port = request_region(ice->port, 32, "ICE1712 - Controller")) == NULL) {
+		snd_ice1712_free(ice);
+		snd_printk("unable to grab ports 0x%lx-0x%lx\n", ice->port, ice->port + 32 - 1);
+		return -EIO;
+	}
+	if ((ice->res_ddma_port = request_region(ice->ddma_port, 16, "ICE1712 - DDMA")) == NULL) {
+		snd_ice1712_free(ice);
+		snd_printk("unable to grab ports 0x%lx-0x%lx\n", ice->ddma_port, ice->ddma_port + 16 - 1);
+		return -EIO;
+	}
+	if ((ice->res_dmapath_port = request_region(ice->dmapath_port, 16, "ICE1712 - DMA path")) == NULL) {
+		snd_ice1712_free(ice);
+		snd_printk("unable to grab ports 0x%lx-0x%lx\n", ice->dmapath_port, ice->dmapath_port + 16 - 1);
+		return -EIO;
+	}
+	if ((ice->res_profi_port = request_region(ice->profi_port, 64, "ICE1712 - Professional")) == NULL) {
+		snd_ice1712_free(ice);
+		snd_printk("unable to grab ports 0x%lx-0x%lx\n", ice->profi_port, ice->profi_port + 16 - 1);
+		return -EIO;
+	}
+	if (request_irq(pci->irq, snd_ice1712_interrupt, SA_INTERRUPT|SA_SHIRQ, "ICE1712", (void *) ice)) {
+		snd_ice1712_free(ice);
+		snd_printk("unable to grab IRQ %d\n", pci->irq);
+		return -EIO;
+	}
+	ice->irq = pci->irq;
+
+	if (snd_ice1712_read_eeprom(ice) < 0) {
+		snd_ice1712_free(ice);
+		return -EIO;
+	}
+	if (snd_ice1712_chip_init(ice) < 0) {
+		snd_ice1712_free(ice);
+		return -EIO;
+	}
+
+	/* unmask used interrupts */
+	outb((ice->eeprom.codec & ICE1712_CFG_2xMPU401) == 0 ? ICE1712_IRQ_MPU2 : 0 |
+	     (ice->eeprom.codec & ICE1712_CFG_NO_CON_AC97) ? ICE1712_IRQ_PBKDS | ICE1712_IRQ_CONCAP | ICE1712_IRQ_CONPBK : 0,
+	     ICEREG(ice, IRQMASK));
+	outb(0x00, ICEMT(ice, IRQ));
+
+	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, ice, &ops)) < 0) {
+		snd_ice1712_free(ice);
+		return err;
+	}
+
+	*r_ice1712 = ice;
+	return 0;
+}
+
+static int __init snd_ice1712_probe(struct pci_dev *pci,
+				    const struct pci_device_id *id)
+{
+	static int dev = 0;
+	snd_card_t *card;
+	ice1712_t *ice;
+	int pcm_dev = 0, err;
+
+	for ( ; dev < SNDRV_CARDS; dev++) {
+		if (!snd_enable[dev]) {
+			dev++;
+			return -ENOENT;
+		}
+		break;
+	}
+	if (dev >= SNDRV_CARDS)
+		return -ENODEV;
+
+	card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0);
+	if (card == NULL)
+		return -ENOMEM;
+
+	if ((err = snd_ice1712_create(card, pci, &ice)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	if ((err = snd_ice1712_pcm_profi(ice, pcm_dev++, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	
+	if (!(ice->eeprom.codec & ICE1712_CFG_NO_CON_AC97))
+		if ((err = snd_ice1712_pcm(ice, pcm_dev++, NULL)) < 0) {
+			snd_card_free(card);
+			return err;
+		}
+
+	if ((err = snd_ice1712_ac97_mixer(ice)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	if ((err = snd_ice1712_build_controls(ice)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	if (!(ice->eeprom.codec & ICE1712_CFG_NO_CON_AC97))
+		if ((err = snd_ice1712_pcm_ds(ice, pcm_dev++, NULL)) < 0) {
+			snd_card_free(card);
+			return err;
+		}
+
+	strcpy(card->driver, "ICE1712");
+	strcpy(card->shortname, "ICEnsemble ICE1712");
+	
+	switch (ice->eeprom.subvendor) {
+	case ICE1712_SUBDEVICE_STDSP24:
+		strcpy(card->shortname, "Hoontech SoundTrack Audio DSP24");
+		break;
+	case ICE1712_SUBDEVICE_DELTA1010:
+		strcpy(card->shortname, "M Audio Delta 1010");
+		break;
+	case ICE1712_SUBDEVICE_DELTADIO2496:
+		strcpy(card->shortname, "M Audio Delta DiO 2496");
+		goto __no_mpu401;
+	case ICE1712_SUBDEVICE_DELTA66:
+		strcpy(card->shortname, "M Audio Delta 66");
+		goto __no_mpu401;
+	case ICE1712_SUBDEVICE_DELTA44:
+		strcpy(card->shortname, "M Audio Delta 44");
+		goto __no_mpu401;
+	case ICE1712_SUBDEVICE_AUDIOPHILE:
+		strcpy(card->shortname, "M Audio Audiophile 24/96");
+		break;
+	case ICE1712_SUBDEVICE_EWX2496:
+		strcpy(card->shortname, "TerraTec EWX 24/96");
+		break;
+	case ICE1712_SUBDEVICE_EWS88MT:
+		strcpy(card->shortname, "TerraTec EWS 88MT");
+		break;
+	case ICE1712_SUBDEVICE_EWS88D:
+		strcpy(card->shortname, "TerraTec EWS 88D");
+		break;
+	}
+
+	if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_ICE1712,
+				       ICEREG(ice, MPU1_CTRL), 1,
+				       ice->irq, 0,
+				       &ice->rmidi[0])) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	if (ice->eeprom.codec & ICE1712_CFG_2xMPU401)
+		if ((err = snd_mpu401_uart_new(card, 1, MPU401_HW_ICE1712,
+					       ICEREG(ice, MPU2_CTRL), 1,
+					       ice->irq, 0,
+					       &ice->rmidi[1])) < 0) {
+			snd_card_free(card);
+			return err;
+		}
+
+      __no_mpu401:
+	sprintf(card->longname, "%s at 0x%lx, irq %i",
+		card->shortname, ice->port, ice->irq);
+
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	PCI_SET_DRIVER_DATA(pci, card);
+	dev++;
+	return 0;
+}
+
+static void __exit snd_ice1712_remove(struct pci_dev *pci)
+{
+	snd_card_free(PCI_GET_DRIVER_DATA(pci));
+	PCI_SET_DRIVER_DATA(pci, NULL);
+}
+
+#ifdef TARGET_OS2
+static struct pci_driver driver = {
+	0,0,"ICE1712",
+	snd_ice1712_ids,
+	snd_ice1712_probe,
+	snd_ice1712_remove,0,0
+};
+#else
+static struct pci_driver driver = {
+	name: "ICE1712",
+	id_table: snd_ice1712_ids,
+	probe: snd_ice1712_probe,
+	remove: snd_ice1712_remove,
+};
+#endif
+
+static int __init alsa_card_ice1712_init(void)
+{
+	int err;
+
+	if ((err = pci_module_init(&driver)) < 0) {
+#ifdef MODULE
+		snd_printk("ICE1712 soundcard not found or device busy\n");
+#endif
+		return err;
+	}
+	return 0;
+}
+
+static void __exit alsa_card_ice1712_exit(void)
+{
+	pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_ice1712_init)
+module_exit(alsa_card_ice1712_exit)
+
+#ifndef MODULE
+
+/* format is: snd-card-ice1712=snd_enable,snd_index,snd_id */
+
+static int __init alsa_card_ice1712_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);
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-card-ice1712=", alsa_card_ice1712_setup);
+
+#endif /* ifndef MODULE */
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/intel8x0.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/intel8x0.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/intel8x0.c	(revision 91)
@@ -0,0 +1,3348 @@
+/*
+ *   ALSA driver for Intel ICH (i8x0) chipsets
+ *
+ *	Copyright (c) 2000 Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This code also contains alpha support for SiS 735 chipsets provided
+ *   by Mike Pieper <mptei@users.sourceforge.net>. We have no datasheet
+ *   for SiS735, so the code is not fully functional.
+ *
+ *
+ *   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/ac97_codec.h>
+#include <sound/info.h>
+#include <sound/mpu401.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+#define I810_DEBUG
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Intel 82801AA,82901AB,i810,i820,i830,i840,i845,MX440; SiS 7012; Ali 5455");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{Intel,82801AA-ICH},"
+               "{Intel,82901AB-ICH0},"
+               "{Intel,82801BA-ICH2},"
+               "{Intel,82801CA-ICH3},"
+               "{Intel,82801DB-ICH4},"
+               "{Intel,ICH5},"
+               "{Intel,ICH6},"
+               "{Intel,ICH7},"
+               "{Intel,6300ESB},"
+               "{Intel,ESB2},"
+               "{Intel,MX440},"
+               "{SiS,SI7012},"
+               "{NVidia,nForce Audio},"
+               "{NVidia,nForce2 Audio},"
+               "{AMD,AMD768},"
+               "{AMD,AMD8111},"
+               "{ALI,M5455}}");
+
+#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
+//#define SUPPORT_JOYSTICK 1
+#endif
+#define SUPPORT_MIDI 1
+
+extern int midi_port;
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;	/* Enable this card */
+#ifdef TARGET_OS2
+static int ac97_clock[SNDRV_CARDS] = {REPEAT_SNDRV(0)};
+static char *ac97_quirk[SNDRV_CARDS];
+#else
+static int ac97_clock[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0};
+#endif
+#ifdef SUPPORT_JOYSTICK
+static int joystick[SNDRV_CARDS];
+#endif
+#ifdef SUPPORT_MIDI
+static int mpu_port[SNDRV_CARDS];// = {REPEAT_SNDRV(0x330)};;
+/* disabled */
+#endif
+
+static int buggy_semaphore;
+static int buggy_irq = -1; /* auto-check */
+
+MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(index, "Index value for Intel i8x0 soundcard.");
+MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC);
+MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+MODULE_PARM_DESC(id, "ID string for Intel i8x0 soundcard.");
+MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC);
+MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(enable, "Enable Intel i8x0 soundcard.");
+MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(ac97_clock, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(ac97_clock, "AC'97 codec clock (0 = auto-detect).");
+MODULE_PARM_SYNTAX(ac97_clock, SNDRV_ENABLED ",default:0");
+MODULE_PARM(ac97_quirk, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(ac97_quirk, "AC'97 workaround for strange hardware.");
+MODULE_PARM_SYNTAX(ac97_quirk, SNDRV_ENABLED ",allows:{{-1,3}},dialog:list,default:-1");
+#ifdef SUPPORT_JOYSTICK
+MODULE_PARM(joystick, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(joystick, "Enable joystick for Intel i8x0 soundcard.");
+MODULE_PARM_SYNTAX(joystick, SNDRV_ENABLED "," SNDRV_BOOLEAN_FALSE_DESC);
+#endif
+#ifdef SUPPORT_MIDI
+MODULE_PARM(mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(mpu_port, "MPU401 port # for Intel i8x0 driver.");
+MODULE_PARM_SYNTAX(mpu_port, SNDRV_ENABLED ",allows:{{0},{0x330},{0x300}},dialog:list");
+#endif
+
+/*
+ *  Direct registers
+ */
+
+#ifndef PCI_DEVICE_ID_INTEL_82801
+#define PCI_DEVICE_ID_INTEL_82801       0x2415
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_82901
+#define PCI_DEVICE_ID_INTEL_82901       0x2425
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_82801BA
+#define PCI_DEVICE_ID_INTEL_82801BA     0x2445
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_440MX
+#define PCI_DEVICE_ID_INTEL_440MX       0x7195
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_ICH3
+#define PCI_DEVICE_ID_INTEL_ICH3	0x2485
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_ICH4
+#define PCI_DEVICE_ID_INTEL_ICH4	0x24c5
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_ICH5
+#define PCI_DEVICE_ID_INTEL_ICH5	0x24d5
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_ESB_5
+#define PCI_DEVICE_ID_INTEL_ESB_5       0x25a6
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_ICH6_18
+#define PCI_DEVICE_ID_INTEL_ICH6_18     0x266e
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_ICH7_20
+#define PCI_DEVICE_ID_INTEL_ICH7_20     0x27de
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_ESB2_14
+#define PCI_DEVICE_ID_INTEL_ESB2_14     0x2698
+#endif
+#ifndef PCI_DEVICE_ID_SI_7012
+#define PCI_DEVICE_ID_SI_7012		0x7012
+#endif
+#ifndef PCI_DEVICE_ID_NVIDIA_CK804_AUDIO
+#define PCI_DEVICE_ID_NVIDIA_CK804_AUDIO 0x0059
+#endif
+#ifndef PCI_DEVICE_ID_NVIDIA_MCP_AUDIO
+#define PCI_DEVICE_ID_NVIDIA_MCP_AUDIO	0x01b1
+#endif
+#ifndef PCI_DEVICE_ID_NVIDIA_MCP2_AUDIO
+#define PCI_DEVICE_ID_NVIDIA_MCP2_AUDIO	0x006a
+#endif
+#ifndef PCI_DEVICE_ID_NVIDIA_MCP3_AUDIO
+#define PCI_DEVICE_ID_NVIDIA_MCP3_AUDIO	0x00da
+#endif
+#ifndef PCI_DEVICE_ID_NVIDIA_CK8S_AUDIO
+#define PCI_DEVICE_ID_NVIDIA_CK8S_AUDIO 0x00ea
+#endif
+#ifndef PCI_DEVICE_ID_NVIDIA_CK8_AUDIO
+#define PCI_DEVICE_ID_NVIDIA_CK8_AUDIO  0x008a
+#endif
+
+enum { DEVICE_INTEL, DEVICE_INTEL_ICH4, DEVICE_INTEL_ICH5, DEVICE_SIS, DEVICE_ALI, DEVICE_NFORCE };
+
+#define ICHREG(x) ICH_REG_##x
+
+#define DEFINE_REGSET(name,base) \
+    enum { \
+    ICH_REG_##name##_BDBAR	= base + 0x0,	/* dword - buffer descriptor list base address */ \
+    ICH_REG_##name##_CIV	= base + 0x04,	/* byte - current index value */ \
+    ICH_REG_##name##_LVI	= base + 0x05,	/* byte - last valid index */ \
+    ICH_REG_##name##_SR	= base + 0x06,	/* byte - status register */ \
+    ICH_REG_##name##_PICB	= base + 0x08,	/* word - position in current buffer */ \
+    ICH_REG_##name##_PIV	= base + 0x0a,	/* byte - prefetched index value */ \
+    ICH_REG_##name##_CR	= base + 0x0b,	/* byte - control register */ \
+    };
+
+/* busmaster blocks */
+DEFINE_REGSET(OFF, 0);		/* offset */
+DEFINE_REGSET(PI, 0x00);	/* PCM in */
+DEFINE_REGSET(PO, 0x10);	/* PCM out */
+DEFINE_REGSET(MC, 0x20);	/* Mic in */
+
+/* ICH4 busmaster blocks */
+DEFINE_REGSET(MC2, 0x40);	/* Mic in 2 */
+DEFINE_REGSET(PI2, 0x50);	/* PCM in 2 */
+DEFINE_REGSET(SP, 0x60);	/* SPDIF out */
+
+/* values for each busmaster block */
+
+/* LVI */
+#define ICH_REG_LVI_MASK		0x1f
+
+/* SR */
+#define ICH_FIFOE			0x10	/* FIFO error */
+#define ICH_BCIS			0x08	/* buffer completion interrupt status */
+#define ICH_LVBCI			0x04	/* last valid buffer completion interrupt */
+#define ICH_CELV			0x02	/* current equals last valid */
+#define ICH_DCH				0x01	/* DMA controller halted */
+
+/* PIV */
+#define ICH_REG_PIV_MASK		0x1f	/* mask */
+
+/* CR */
+#define ICH_IOCE			0x10	/* interrupt on completion enable */
+#define ICH_FEIE			0x08	/* fifo error interrupt enable */
+#define ICH_LVBIE			0x04	/* last valid buffer interrupt enable */
+#define ICH_RESETREGS			0x02	/* reset busmaster registers */
+#define ICH_STARTBM			0x01	/* start busmaster operation */
+
+
+/* global block */
+#define ICH_REG_GLOB_CNT		0x2c	/* dword - global control */
+#define   ICH_PCM_SPDIF_MASK    0xc0000000      /* s/pdif pcm slot mask (ICH4) */
+#define   ICH_PCM_SPDIF_NONE    0x00000000      /* reserved - undefined */
+#define   ICH_PCM_SPDIF_78      0x40000000      /* s/pdif pcm on slots 7&8 */
+#define   ICH_PCM_SPDIF_69      0x80000000      /* s/pdif pcm on slots 6&9 */
+#define   ICH_PCM_SPDIF_1011    0xc0000000      /* s/pdif pcm on slots 10&11 */
+#define   ICH_PCM_20BIT		0x00400000	/* 20-bit samples (ICH4) */
+#define   ICH_PCM_246_MASK	0x00300000	/* 6 channels (not all chips) */
+#define   ICH_PCM_6		0x00200000	/* 6 channels (not all chips) */
+#define   ICH_PCM_4		0x00100000	/* 4 channels (not all chips) */
+#define   ICH_PCM_2		0x00000000	/* 2 channels (stereo) */
+#define   ICH_SIS_PCM_246_MASK	0x000000c0	/* 6 channels (SIS7012) */
+#define   ICH_SIS_PCM_6		0x00000080	/* 6 channels (SIS7012) */
+#define   ICH_SIS_PCM_4		0x00000040	/* 4 channels (SIS7012) */
+#define   ICH_SIS_PCM_2		0x00000000	/* 2 channels (SIS7012) */
+#define   ICH_TRIE		0x00000040	/* tertiary resume interrupt enable */
+#define   ICH_SRIE		0x00000020	/* secondary resume interrupt enable */
+#define   ICH_PRIE		0x00000010	/* primary resume interrupt enable */
+#define   ICH_ACLINK		0x00000008	/* AClink shut off */
+#define   ICH_AC97WARM		0x00000004	/* AC'97 warm reset */
+#define   ICH_AC97COLD		0x00000002	/* AC'97 cold reset */
+#define   ICH_GIE		0x00000001	/* GPI interrupt enable */
+#define   ICH_REG_GLOB_STA    	0x30	/* dword - global status */
+#define   ICH_TRI		0x20000000	/* ICH4: tertiary (AC_SDIN2) resume interrupt */
+#define   ICH_TCR		0x10000000	/* ICH4: tertiary (AC_SDIN2) codec ready */
+#define   ICH_BCS		0x08000000	/* ICH4: bit clock stopped */
+#define   ICH_SPINT		0x04000000	/* ICH4: S/PDIF interrupt */
+#define   ICH_P2INT		0x02000000	/* ICH4: PCM2-In interrupt */
+#define   ICH_M2INT		0x01000000	/* ICH4: Mic2-In interrupt */
+#define   ICH_SAMPLE_CAP	0x00c00000	/* ICH4: sample capability bits (RO) */
+#define   ICH_SAMPLE_16_20      0x00400000      /* ICH4: 16- and 20-bit samples */
+#define   ICH_MULTICHAN_CAP	0x00300000	/* ICH4: multi-channel capability bits (RO) */
+#define   ICH_SIS_TRI           0x00080000      /* SIS: tertiary resume irq */
+#define   ICH_SIS_TCR           0x00040000      /* SIS: tertiary codec ready */
+#define   ICH_MD3		0x00020000	/* modem power down semaphore */
+#define   ICH_AD3		0x00010000	/* audio power down semaphore */
+#define   ICH_RCS		0x00008000	/* read completion status */
+#define   ICH_BIT3		0x00004000	/* bit 3 slot 12 */
+#define   ICH_BIT2		0x00002000	/* bit 2 slot 12 */
+#define   ICH_BIT1		0x00001000	/* bit 1 slot 12 */
+#define   ICH_SRI		0x00000800	/* secondary (AC_SDIN1) resume interrupt */
+#define   ICH_PRI		0x00000400	/* primary (AC_SDIN0) resume interrupt */
+#define   ICH_SCR		0x00000200	/* secondary (AC_SDIN1) codec ready */
+#define   ICH_PCR		0x00000100	/* primary (AC_SDIN0) codec ready */
+#define   ICH_MCINT		0x00000080	/* MIC capture interrupt */
+#define   ICH_POINT		0x00000040	/* playback interrupt */
+#define   ICH_PIINT		0x00000020	/* capture interrupt */
+#define   ICH_NVSPINT		0x00000010	/* nforce spdif interrupt */
+#define   ICH_MOINT		0x00000004	/* modem playback interrupt */
+#define   ICH_MIINT		0x00000002	/* modem capture interrupt */
+#define   ICH_GSCI		0x00000001	/* GPI status change interrupt */
+#define   ICH_REG_ACC_SEMA     	0x34	/* byte - codec write semaphore */
+#define   ICH_CAS		0x01		/* codec access semaphore */
+#define ICH_REG_SDM		0x80
+#define   ICH_DI2L_MASK		0x000000c0	/* PCM In 2, Mic In 2 data in line */
+#define   ICH_DI2L_SHIFT	6
+#define   ICH_DI1L_MASK		0x00000030	/* PCM In 1, Mic In 1 data in line */
+#define   ICH_DI1L_SHIFT	4
+#define   ICH_SE		0x00000008	/* steer enable */
+#define   ICH_LDI_MASK		0x00000003	/* last codec read data input */
+
+#define   ICH_MAX_FRAGS		32		/* max hw frags */
+
+
+/*
+ * registers for Ali5455
+ */
+
+/* ALi 5455 busmaster blocks */
+DEFINE_REGSET(AL_PI, 0x40);	/* ALi PCM in */
+DEFINE_REGSET(AL_PO, 0x50);	/* Ali PCM out */
+DEFINE_REGSET(AL_MC, 0x60);	/* Ali Mic in */
+DEFINE_REGSET(AL_CDC_SPO, 0x70);	/* Ali Codec SPDIF out */
+DEFINE_REGSET(AL_CENTER, 0x80);		/* Ali center out */
+DEFINE_REGSET(AL_LFE, 0x90);		/* Ali center out */
+DEFINE_REGSET(AL_CLR_SPI, 0xa0);	/* Ali Controller SPDIF in */
+DEFINE_REGSET(AL_CLR_SPO, 0xb0);	/* Ali Controller SPDIF out */
+DEFINE_REGSET(AL_I2S, 0xc0);	/* Ali I2S in */
+DEFINE_REGSET(AL_PI2, 0xd0);	/* Ali PCM2 in */
+DEFINE_REGSET(AL_MC2, 0xe0);	/* Ali Mic2 in */
+
+enum {
+    ICH_REG_ALI_SCR = 0x00,		/* System Control Register */
+    ICH_REG_ALI_SSR = 0x04,		/* System Status Register  */
+    ICH_REG_ALI_DMACR = 0x08,	/* DMA Control Register    */
+    ICH_REG_ALI_FIFOCR1 = 0x0c,	/* FIFO Control Register 1  */
+    ICH_REG_ALI_INTERFACECR = 0x10,	/* Interface Control Register */
+    ICH_REG_ALI_INTERRUPTCR = 0x14,	/* Interrupt control Register */
+    ICH_REG_ALI_INTERRUPTSR = 0x18,	/* Interrupt  Status Register */
+    ICH_REG_ALI_FIFOCR2 = 0x1c,	/* FIFO Control Register 2   */
+    ICH_REG_ALI_CPR = 0x20,		/* Command Port Register     */
+    ICH_REG_ALI_CPR_ADDR = 0x22,	/* ac97 addr write */
+    ICH_REG_ALI_SPR = 0x24,		/* Status Port Register      */
+    ICH_REG_ALI_SPR_ADDR = 0x26,	/* ac97 addr read */
+    ICH_REG_ALI_FIFOCR3 = 0x2c,	/* FIFO Control Register 3  */
+    ICH_REG_ALI_TTSR = 0x30,	/* Transmit Tag Slot Register */
+    ICH_REG_ALI_RTSR = 0x34,	/* Receive Tag Slot  Register */
+    ICH_REG_ALI_CSPSR = 0x38,	/* Command/Status Port Status Register */
+    ICH_REG_ALI_CAS = 0x3c,		/* Codec Write Semaphore Register */
+    ICH_REG_ALI_HWVOL = 0xf0,	/* hardware volume control/status */
+    ICH_REG_ALI_I2SCR = 0xf4,	/* I2S control/status */
+    ICH_REG_ALI_SPDIFCSR = 0xf8,	/* spdif channel status register  */
+    ICH_REG_ALI_SPDIFICS = 0xfc,	/* spdif interface control/status  */
+};
+
+#define ALI_CAS_SEM_BUSY	0x80000000
+#define ALI_CPR_ADDR_SECONDARY	0x100
+#define ALI_CPR_ADDR_READ	0x80
+#define ALI_CSPSR_CODEC_READY	0x08
+#define ALI_CSPSR_READ_OK	0x02
+#define ALI_CSPSR_WRITE_OK	0x01
+
+/* interrupts for the whole chip by interrupt status register finish */
+
+#define ALI_INT_MICIN2		(1<<26)
+#define ALI_INT_PCMIN2		(1<<25)
+#define ALI_INT_I2SIN		(1<<24)
+#define ALI_INT_SPDIFOUT	(1<<23)	/* controller spdif out INTERRUPT */
+#define ALI_INT_SPDIFIN		(1<<22)
+#define ALI_INT_LFEOUT		(1<<21)
+#define ALI_INT_CENTEROUT	(1<<20)
+#define ALI_INT_CODECSPDIFOUT	(1<<19)
+#define ALI_INT_MICIN		(1<<18)
+#define ALI_INT_PCMOUT		(1<<17)
+#define ALI_INT_PCMIN		(1<<16)
+#define ALI_INT_CPRAIS		(1<<7)	/* command port available */
+#define ALI_INT_SPRAIS		(1<<5)	/* status port available */
+#define ALI_INT_GPIO		(1<<1)
+#define ALI_INT_MASK		(ALI_INT_SPDIFOUT|ALI_INT_CODECSPDIFOUT|ALI_INT_MICIN|ALI_INT_PCMOUT|ALI_INT_PCMIN)
+
+#define ICH_ALI_SC_RESET	(1<<31)	/* master reset */
+#define ICH_ALI_SC_AC97_DBL	(1<<30)
+#define ICH_ALI_SC_CODEC_SPDF	(3<<20)	/* 1=7/8, 2=6/9, 3=10/11 */
+#define ICH_ALI_SC_IN_BITS	(3<<18)
+#define ICH_ALI_SC_OUT_BITS	(3<<16)
+#define ICH_ALI_SC_6CH_CFG	(3<<14)
+#define ICH_ALI_SC_PCM_4	(1<<8)
+#define ICH_ALI_SC_PCM_6	(2<<8)
+#define ICH_ALI_SC_PCM_246_MASK	(3<<8)
+
+#define ICH_ALI_SS_SEC_ID	(3<<5)
+#define ICH_ALI_SS_PRI_ID	(3<<3)
+
+#define ICH_ALI_IF_AC97SP	(1<<21)
+#define ICH_ALI_IF_MC		(1<<20)
+#define ICH_ALI_IF_PI		(1<<19)
+#define ICH_ALI_IF_MC2		(1<<18)
+#define ICH_ALI_IF_PI2		(1<<17)
+#define ICH_ALI_IF_LINE_SRC	(1<<15)	/* 0/1 = slot 3/6 */
+#define ICH_ALI_IF_MIC_SRC	(1<<14)	/* 0/1 = slot 3/6 */
+#define ICH_ALI_IF_SPDF_SRC	(3<<12)	/* 00 = PCM, 01 = AC97-in, 10 = spdif-in, 11 = i2s */
+#define ICH_ALI_IF_AC97_OUT	(3<<8)	/* 00 = PCM, 10 = spdif-in, 11 = i2s */
+#define ICH_ALI_IF_PO_SPDF	(1<<3)
+#define ICH_ALI_IF_PO		(1<<1)
+
+/*
+ *
+ */
+
+enum { ICHD_PCMIN, ICHD_PCMOUT, ICHD_MIC, ICHD_MIC2, ICHD_PCM2IN, ICHD_SPBAR, ICHD_LAST = ICHD_SPBAR };
+enum { NVD_PCMIN, NVD_PCMOUT, NVD_MIC, NVD_SPBAR, NVD_LAST = NVD_SPBAR };
+enum { ALID_PCMIN, ALID_PCMOUT, ALID_MIC, ALID_AC97SPDIFOUT, ALID_SPDIFIN, ALID_SPDIFOUT, ALID_LAST = ALID_SPDIFOUT };
+
+#define get_ichdev(substream) (struct ichdev *)(substream->runtime->private_data)
+
+typedef struct ichdev {
+    unsigned int ichd;			/* ich device number */
+    unsigned long reg_offset;		/* offset to bmaddr */
+    u32 *bdbar;				/* CPU address (32bit) */
+    unsigned int bdbar_addr;		/* PCI bus address (32bit) */
+    snd_pcm_substream_t *substream;
+    unsigned int physbuf;			/* physical address (32bit) */
+    unsigned int size;
+    unsigned int fragsize;
+    unsigned int fragsize1;
+    unsigned int position;
+    unsigned int pos_shift;
+    int frags;
+    int lvi;
+    int lvi_frag;
+    int civ;
+    int ack;
+    int ack_reload;
+    unsigned int ack_bit;
+    unsigned int roff_sr;
+    unsigned int roff_picb;
+    unsigned int int_sta_mask;		/* interrupt status mask */
+    unsigned int ali_slot;			/* ALI DMA slot */
+    struct ac97_pcm *pcm;
+    int pcm_open_flag;
+    unsigned int page_attr_changed: 1;
+    unsigned int suspended: 1;
+};
+
+struct intel8x0 {
+    unsigned int device_type;
+
+    int irq;
+
+    unsigned int mmio;
+    unsigned long addr;
+    unsigned long remap_addr;
+    unsigned int bm_mmio;
+    unsigned long bmaddr;
+    unsigned long remap_bmaddr;
+
+    struct pci_dev *pci;
+    snd_card_t *card;
+
+    int pcm_devs;
+    snd_pcm_t *pcm[6];
+    struct ichdev ichd[6];
+
+    int multi4: 1,
+    multi6: 1,
+    dra: 1,
+    smp20bit: 1;
+    int in_ac97_init: 1,
+    in_sdin_init: 1;
+    unsigned in_measurement: 1;	/* during ac97 clock measurement */
+    unsigned fix_nocache: 1; 	/* workaround for 440MX */
+    unsigned buggy_irq: 1;		/* workaround for buggy mobos */
+    unsigned xbox: 1;		/* workaround for Xbox AC'97 detection */
+    unsigned buggy_semaphore: 1;	/* workaround for buggy codec semaphore */
+
+    int spdif_idx;  /* SPDIF BAR index; *_SPBAR or -1 if use PCMOUT */
+    unsigned int sdm_saved; /* SDM reg value */
+
+    ac97_bus_t *ac97_bus;
+    ac97_t *ac97[3];
+    unsigned int ac97_sdin[3];
+    unsigned int max_codecs, ncodecs;
+    unsigned int *codec_bit;
+    unsigned int codec_isr_bits;
+    unsigned int codec_ready_bits;
+
+    snd_rawmidi_t *rmidi;
+
+    spinlock_t reg_lock;
+
+    u32 bdbars_count;
+    struct snd_dma_buffer bdbars;
+    u32 int_sta_reg;		/* interrupt status register */
+    u32 int_sta_mask;		/* interrupt status mask */
+};
+
+static struct pci_device_id snd_intel8x0_ids[] = {
+    { 0x8086, 0x2415, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL },	/* 82801AA */
+    { 0x8086, 0x2425, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL },	/* 82901AB */
+    { 0x8086, 0x2445, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL },	/* 82801BA */
+    { 0x8086, 0x2485, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL },	/* ICH3 */
+    { 0x8086, 0x24c5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL_ICH4 }, /* ICH4 */
+    { 0x8086, 0x24d5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL_ICH5 }, /* ICH5 */
+    { 0x8086, 0x25a6, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL_ICH5 }, /* ESB */
+    { 0x8086, 0x266e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL_ICH5 }, /* ICH6 */
+    { 0x8086, 0x27de, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL_ICH5 }, /* ICH7 */
+    { 0x8086, 0x2698, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL_ICH4 }, /* ESB2 */
+    { 0x8086, 0x7195, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL },	/* 440MX */
+    { 0x1039, 0x7012, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_SIS },	/* SI7012 */
+    { 0x10de, 0x01b1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE },	/* NFORCE */
+    { 0x10de, 0x003a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE },        /* MCP04 */
+    { 0x10de, 0x006a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE },	/* NFORCE2 */
+    { 0x10de, 0x0059, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE },        /* CK804 */
+    { 0x10de, 0x008a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE },        /* CK8 */
+    { 0x10de, 0x00da, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE },	/* NFORCE3 */
+    { 0x10de, 0x00ea, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE },        /* CK8S */
+    { 0x10de, 0x026b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE },        /* MCP51 */
+    { 0x1022, 0x746d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL },	/* AMD8111 */
+    { 0x1022, 0x7445, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL },	/* AMD768 */
+    { 0x10b9, 0x5455, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_ALI },   /* Ali5455 */
+    { 0 }
+};
+
+MODULE_DEVICE_TABLE(pci, snd_intel8x0_ids);
+
+
+/*
+ *  Lowlevel I/O - busmaster
+ */
+
+static u8 igetbyte(struct intel8x0 *chip, u32 offset)
+{
+    if (chip->bm_mmio)
+        return readb(chip->remap_bmaddr + offset);
+    else
+        return inb(chip->bmaddr + offset);
+}
+
+static u16 igetword(struct intel8x0 *chip, u32 offset)
+{
+    if (chip->bm_mmio)
+        return readw(chip->remap_bmaddr + offset);
+    else
+        return inw(chip->bmaddr + offset);
+}
+
+static u32 igetdword(struct intel8x0 *chip, u32 offset)
+{
+    if (chip->bm_mmio)
+        return readl(chip->remap_bmaddr + offset);
+    else
+        return inl(chip->bmaddr + offset);
+}
+
+static void iputbyte(struct intel8x0 *chip, u32 offset, u8 val)
+{
+    if (chip->bm_mmio)
+        writeb(val, chip->remap_bmaddr + offset);
+    else
+        outb(val, chip->bmaddr + offset);
+}
+
+static void iputword(struct intel8x0 *chip, u32 offset, u16 val)
+{
+    if (chip->bm_mmio)
+        writew(val, chip->remap_bmaddr + offset);
+    else
+        outw(val, chip->bmaddr + offset);
+}
+
+static void iputdword(struct intel8x0 *chip, u32 offset, u32 val)
+{
+    if (chip->bm_mmio)
+        writel(val, chip->remap_bmaddr + offset);
+    else
+        outl(val, chip->bmaddr + offset);
+}
+
+/*
+ *  Lowlevel I/O - AC'97 registers
+ */
+
+static u16 iagetword(struct intel8x0 *chip, u32 offset)
+{
+    if (chip->mmio)
+        return readw(chip->remap_addr + offset);
+    else
+        return inw(chip->addr + offset);
+}
+
+static void iaputword(struct intel8x0 *chip, u32 offset, u16 val)
+{
+    if (chip->mmio)
+        writew(val, chip->remap_addr + offset);
+    else
+        outw(val, chip->addr + offset);
+}
+
+/*
+ *  Basic I/O
+ */
+
+/*
+ * access to AC97 codec via normal i/o (for ICH and SIS7012)
+ */
+
+static int snd_intel8x0_codec_semaphore(struct intel8x0 *chip, unsigned int codec)
+{
+    int time;
+
+    if (codec > 2)
+        return -EIO;
+    if (chip->in_sdin_init) {
+        /* we don't know the ready bit assignment at the moment */
+        /* so we check any */
+        codec = chip->codec_isr_bits;
+    } else {
+        codec = chip->codec_bit[chip->ac97_sdin[codec]];
+    }
+
+    /* codec ready ? */
+    if ((igetdword(chip, ICHREG(GLOB_STA)) & codec) == 0)
+        return -EIO;
+
+    /* Anyone holding a semaphore for 1 msec should be shot... */
+    time = 100;
+    do {
+        if (!(igetbyte(chip, ICHREG(ACC_SEMA)) & ICH_CAS))
+            return 0;
+        udelay(10);
+    } while (time--);
+
+    /* access to some forbidden (non existant) ac97 registers will not
+     * reset the semaphore. So even if you don't get the semaphore, still
+     * continue the access. We don't need the semaphore anyway. */
+    snd_printk("codec_semaphore: semaphore is not ready [0x%x][0x%x]\n",
+               igetbyte(chip, ICHREG(ACC_SEMA)), igetdword(chip, ICHREG(GLOB_STA)));
+    iagetword(chip, 0);	/* clear semaphore flag */
+    /* I don't care about the semaphore */
+    return -EBUSY;
+}
+
+static void snd_intel8x0_codec_write(ac97_t *ac97,
+                                     unsigned short reg,
+                                     unsigned short val)
+{
+    struct intel8x0 *chip = ac97->private_data;
+
+    if (snd_intel8x0_codec_semaphore(chip, ac97->num) < 0) {
+        if (! chip->in_ac97_init)
+            snd_printk("codec_write %d: semaphore is not ready for register 0x%x\n", ac97->num, reg);
+    }
+    iaputword(chip, reg + ac97->num * 0x80, val);
+}
+
+static unsigned short snd_intel8x0_codec_read(ac97_t *ac97,
+                                              unsigned short reg)
+{
+    struct intel8x0 *chip = ac97->private_data;
+    unsigned short res;
+    unsigned int tmp;
+
+    if (snd_intel8x0_codec_semaphore(chip, ac97->num) < 0) {
+        if (! chip->in_ac97_init)
+            snd_printk("codec_read %d: semaphore is not ready for register 0x%x\n", ac97->num, reg);
+        res = 0xffff;
+    } else {
+        res = iagetword(chip, reg + ac97->num * 0x80);
+        if ((tmp = igetdword(chip, ICHREG(GLOB_STA))) & ICH_RCS) {
+            /* reset RCS and preserve other R/WC bits */
+            iputdword(chip, ICHREG(GLOB_STA), tmp & ~(chip->codec_ready_bits | ICH_GSCI));
+            if (! chip->in_ac97_init)
+                snd_printk("codec_read %d: read timeout for register 0x%x\n", ac97->num, reg);
+            res = 0xffff;
+        }
+    }
+    return res;
+}
+
+static void __devinit snd_intel8x0_codec_read_test(struct intel8x0 *chip,
+                                                   unsigned int codec)
+{
+    unsigned int tmp;
+
+    if (snd_intel8x0_codec_semaphore(chip, codec) >= 0) {
+        iagetword(chip, codec * 0x80);
+        if ((tmp = igetdword(chip, ICHREG(GLOB_STA))) & ICH_RCS) {
+            /* reset RCS and preserve other R/WC bits */
+            iputdword(chip, ICHREG(GLOB_STA), tmp & ~(chip->codec_ready_bits | ICH_GSCI));
+        }
+    }
+}
+
+/*
+ * access to AC97 for Ali5455
+ */
+static int snd_intel8x0_ali_codec_ready(struct intel8x0 *chip, int mask)
+{
+    int count = 0;
+    for (count = 0; count < 0x7f; count++) {
+        int val = igetbyte(chip, ICHREG(ALI_CSPSR));
+        if (val & mask)
+            return 0;
+    }
+    if (! chip->in_ac97_init)
+        snd_printd(KERN_WARNING "intel8x0: AC97 codec ready timeout.\n");
+    return -EBUSY;
+}
+
+static int snd_intel8x0_ali_codec_semaphore(struct intel8x0 *chip)
+{
+    int time = 100;
+    if (chip->buggy_semaphore)
+        return 0; /* just ignore ... */
+    while (time-- && (igetdword(chip, ICHREG(ALI_CAS)) & ALI_CAS_SEM_BUSY))
+        udelay(1);
+    if (! time && ! chip->in_ac97_init)
+        snd_printk(KERN_WARNING "ali_codec_semaphore timeout\n");
+    return snd_intel8x0_ali_codec_ready(chip, ALI_CSPSR_CODEC_READY);
+}
+
+static unsigned short snd_intel8x0_ali_codec_read(ac97_t *ac97, unsigned short reg)
+{
+    struct intel8x0 *chip = ac97->private_data;
+    unsigned short data = 0xffff;
+
+    if (snd_intel8x0_ali_codec_semaphore(chip))
+        goto __err;
+    reg |= ALI_CPR_ADDR_READ;
+    if (ac97->num)
+        reg |= ALI_CPR_ADDR_SECONDARY;
+    iputword(chip, ICHREG(ALI_CPR_ADDR), reg);
+    if (snd_intel8x0_ali_codec_ready(chip, ALI_CSPSR_READ_OK))
+        goto __err;
+    data = igetword(chip, ICHREG(ALI_SPR));
+__err:
+    return data;
+}
+
+static void snd_intel8x0_ali_codec_write(ac97_t *ac97, unsigned short reg, unsigned short val)
+{
+    struct intel8x0 *chip = ac97->private_data;
+
+    if (snd_intel8x0_ali_codec_semaphore(chip))
+        return;
+    iputword(chip, ICHREG(ALI_CPR), val);
+    if (ac97->num)
+        reg |= ALI_CPR_ADDR_SECONDARY;
+    iputword(chip, ICHREG(ALI_CPR_ADDR), reg);
+    snd_intel8x0_ali_codec_ready(chip, ALI_CSPSR_WRITE_OK);
+}
+
+
+/*
+ * DMA I/O
+ */
+static void snd_intel8x0_setup_periods(struct intel8x0 *chip, struct ichdev *ichdev)
+{
+    int idx;
+    u32 *bdbar = ichdev->bdbar;
+    unsigned long port = ichdev->reg_offset;
+
+    iputdword(chip, port + ICH_REG_OFF_BDBAR, ichdev->bdbar_addr);
+    if (ichdev->size == ichdev->fragsize) {
+        ichdev->ack_reload = ichdev->ack = 2;
+        ichdev->fragsize1 = ichdev->fragsize >> 1;
+        for (idx = 0; idx < (ICH_REG_LVI_MASK + 1) * 2; idx += 4) {
+            bdbar[idx + 0] = cpu_to_le32(ichdev->physbuf);
+            bdbar[idx + 1] = cpu_to_le32(0x80000000 | /* interrupt on completion */
+                                         ichdev->fragsize1 >> ichdev->pos_shift);
+            bdbar[idx + 2] = cpu_to_le32(ichdev->physbuf + (ichdev->size >> 1));
+            bdbar[idx + 3] = cpu_to_le32(0x80000000 | /* interrupt on completion */
+                                         ichdev->fragsize1 >> ichdev->pos_shift);
+        }
+        ichdev->frags = 2;
+    } else {
+        ichdev->ack_reload = ichdev->ack = 1;
+        ichdev->fragsize1 = ichdev->fragsize;
+        for (idx = 0; idx < (ICH_REG_LVI_MASK + 1) * 2; idx += 2) {
+            bdbar[idx + 0] = cpu_to_le32(ichdev->physbuf + (((idx >> 1) * ichdev->fragsize) % ichdev->size));
+            bdbar[idx + 1] = cpu_to_le32(0x80000000 | /* interrupt on completion */
+                                         ichdev->fragsize >> ichdev->pos_shift);
+            // printk("bdbar[%i] = 0x%x [0x%x]\n", idx + 0, bdbar[idx + 0], bdbar[idx + 1]);
+        }
+        ichdev->frags = ichdev->size / ichdev->fragsize;
+    }
+    iputbyte(chip, port + ICH_REG_OFF_LVI, ichdev->lvi = ICH_REG_LVI_MASK);
+    ichdev->civ = 0;
+    iputbyte(chip, port + ICH_REG_OFF_CIV, 0);
+    ichdev->lvi_frag = ICH_REG_LVI_MASK % ichdev->frags;
+    ichdev->position = 0;
+#if 0
+    printk("lvi_frag = %i, frags = %i, period_size = 0x%x, period_size1 = 0x%x\n",
+           ichdev->lvi_frag, ichdev->frags, ichdev->fragsize, ichdev->fragsize1);
+#endif
+    /* clear interrupts */
+    iputbyte(chip, port + ichdev->roff_sr, ICH_FIFOE | ICH_BCIS | ICH_LVBCI);
+}
+
+/*
+ *  Interrupt handler
+ */
+
+static inline void snd_intel8x0_update(struct intel8x0 *chip, struct ichdev *ichdev)
+{
+    unsigned long port = ichdev->reg_offset;
+    int status, civ, i, step;
+    int ack = 0;
+
+    spin_lock(&chip->reg_lock);
+    status = igetbyte(chip, port + ichdev->roff_sr);
+    civ = igetbyte(chip, port + ICH_REG_OFF_CIV);
+    if (!(status & ICH_BCIS)) {
+        step = 0;
+    } else if (civ == ichdev->civ) {
+        // snd_printd("civ same %d\n", civ);
+        step = 1;
+        ichdev->civ++;
+        ichdev->civ &= ICH_REG_LVI_MASK;
+    } else {
+        step = civ - ichdev->civ;
+        if (step < 0)
+            step += ICH_REG_LVI_MASK + 1;
+        // if (step != 1)
+        //	snd_printd("step = %d, %d -> %d\n", step, ichdev->civ, civ);
+        ichdev->civ = civ;
+    }
+
+    ichdev->position += step * ichdev->fragsize1;
+    ichdev->position %= ichdev->size;
+    ichdev->lvi += step;
+    ichdev->lvi &= ICH_REG_LVI_MASK;
+    iputbyte(chip, port + ICH_REG_OFF_LVI, ichdev->lvi);
+    for (i = 0; i < step; i++) {
+        ichdev->lvi_frag++;
+        ichdev->lvi_frag %= ichdev->frags;
+        ichdev->bdbar[ichdev->lvi * 2] = cpu_to_le32(ichdev->physbuf + ichdev->lvi_frag * ichdev->fragsize1);
+        // printk("new: bdbar[%i] = 0x%x [0x%x], prefetch = %i, all = 0x%x, 0x%x\n", ichdev->lvi * 2, ichdev->bdbar[ichdev->lvi * 2], ichdev->bdbar[ichdev->lvi * 2 + 1], inb(ICH_REG_OFF_PIV + port), inl(port + 4), inb(port + ICH_REG_OFF_CR));
+        if (--ichdev->ack == 0) {
+            ichdev->ack = ichdev->ack_reload;
+            ack = 1;
+        }
+    }
+    spin_unlock(&chip->reg_lock);
+    if (ack && ichdev->substream) {
+        snd_pcm_period_elapsed(ichdev->substream);
+    }
+    iputbyte(chip, port + ichdev->roff_sr,
+             status & (ICH_FIFOE | ICH_BCIS | ICH_LVBCI));
+}
+
+static irqreturn_t snd_intel8x0_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+    struct intel8x0 *chip = dev_id;
+    struct ichdev *ichdev;
+    unsigned int status;
+    unsigned int i;
+#ifdef TARGET_OS2
+    int fOurIrq = FALSE;
+#endif
+
+    status = igetdword(chip, chip->int_sta_reg);
+    if (status == 0xffffffff)       /* we are not yet resumed */
+        return IRQ_NONE;
+
+    if ((status & chip->int_sta_mask) == 0) {
+        static int err_count = 10;
+        if (status) {
+            /* ack */
+            iputdword(chip, chip->int_sta_reg, status);
+            /* FIXME: on some ICH5 board shows the same
+             *        problem.  So we return IRQ_HANDLED
+             *        in any cases.
+             * (or, maybe add a new module param to control this?)
+             */
+#if 0
+            if (chip->device_type != DEVICE_NFORCE)
+                status ^= igetdword(chip, chip->int_sta_reg);
+#endif
+        }
+        return IRQ_NONE/*RETVAL(status)*/;
+    }
+#ifdef TARGET_OS2
+    fOurIrq = TRUE;
+#endif
+
+    for (i = 0; i < chip->bdbars_count; i++) {
+        ichdev = &chip->ichd[i];
+        if (status & ichdev->int_sta_mask)
+            snd_intel8x0_update(chip, ichdev);
+    }
+
+    /* ack them */
+    iputdword(chip, chip->int_sta_reg, status & chip->int_sta_mask);
+#ifdef TARGET_OS2
+    if (fOurIrq) {
+        //eoi_irq(irq);
+    }
+#endif //TARGET_OS2
+
+    return IRQ_HANDLED;
+}
+
+/*
+ *  PCM part
+ */
+
+static int snd_intel8x0_pcm_trigger(snd_pcm_substream_t *substream, int cmd)
+{
+    struct intel8x0 *chip = snd_pcm_substream_chip(substream);
+    struct ichdev *ichdev = get_ichdev(substream);
+    unsigned char val = 0;
+    unsigned long port = ichdev->reg_offset;
+
+    switch (cmd) {
+    case SNDRV_PCM_TRIGGER_RESUME:
+        ichdev->suspended = 0;
+        /* fallthru */
+    case SNDRV_PCM_TRIGGER_START:
+        val = ICH_IOCE | ICH_STARTBM;
+        break;
+    case SNDRV_PCM_TRIGGER_SUSPEND:
+        ichdev->suspended = 1;
+        /* fallthru */
+    case SNDRV_PCM_TRIGGER_STOP:
+        val = 0;
+        break;
+    case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+        val = ICH_IOCE;
+        break;
+    case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+        val = ICH_IOCE | ICH_STARTBM;
+        break;
+    default:
+        return -EINVAL;
+    }
+    iputbyte(chip, port + ICH_REG_OFF_CR, val);
+    if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+        /* wait until DMA stopped */
+        while (!(igetbyte(chip, port + ichdev->roff_sr) & ICH_DCH)) ;
+        /* reset whole DMA things */
+        iputbyte(chip, port + ICH_REG_OFF_CR, ICH_RESETREGS);
+    }
+    return 0;
+}
+
+static int snd_intel8x0_ali_trigger(snd_pcm_substream_t *substream, int cmd)
+{
+    struct intel8x0 *chip = snd_pcm_substream_chip(substream);
+    struct ichdev *ichdev = get_ichdev(substream);
+    unsigned long port = ichdev->reg_offset;
+    static int fiforeg[] = { ICHREG(ALI_FIFOCR1), ICHREG(ALI_FIFOCR2), ICHREG(ALI_FIFOCR3) };
+    unsigned int val, fifo;
+
+    val = igetdword(chip, ICHREG(ALI_DMACR));
+    switch (cmd) {
+    case SNDRV_PCM_TRIGGER_RESUME:
+        ichdev->suspended = 0;
+        /* fallthru */
+    case SNDRV_PCM_TRIGGER_START:
+    case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+            /* clear FIFO for synchronization of channels */
+            fifo = igetdword(chip, fiforeg[ichdev->ali_slot / 4]);
+            fifo &= ~(0xff << (ichdev->ali_slot % 4));
+            fifo |= 0x83 << (ichdev->ali_slot % 4);
+            iputdword(chip, fiforeg[ichdev->ali_slot / 4], fifo);
+        }
+        iputbyte(chip, port + ICH_REG_OFF_CR, ICH_IOCE);
+        val &= ~(1 << (ichdev->ali_slot + 16)); /* clear PAUSE flag */
+        iputdword(chip, ICHREG(ALI_DMACR), val | (1 << ichdev->ali_slot)); /* start DMA */
+        break;
+    case SNDRV_PCM_TRIGGER_SUSPEND:
+        ichdev->suspended = 1;
+        /* fallthru */
+    case SNDRV_PCM_TRIGGER_STOP:
+    case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+        iputdword(chip, ICHREG(ALI_DMACR), val | (1 << (ichdev->ali_slot + 16))); /* pause */
+        iputbyte(chip, port + ICH_REG_OFF_CR, 0);
+        while (igetbyte(chip, port + ICH_REG_OFF_CR))
+            ;
+        if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH)
+            break;
+        /* reset whole DMA things */
+        iputbyte(chip, port + ICH_REG_OFF_CR, ICH_RESETREGS);
+        /* clear interrupts */
+        iputbyte(chip, port + ICH_REG_OFF_SR, igetbyte(chip, port + ICH_REG_OFF_SR) | 0x1e);
+        iputdword(chip, ICHREG(ALI_INTERRUPTSR),
+                  igetdword(chip, ICHREG(ALI_INTERRUPTSR)) & ichdev->int_sta_mask);
+        break;
+    default:
+        return -EINVAL;
+    }
+    return 0;
+}
+
+static int snd_intel8x0_hw_params(snd_pcm_substream_t * substream,
+                                  snd_pcm_hw_params_t * hw_params)
+{
+    struct intel8x0 *chip = snd_pcm_substream_chip(substream);
+    struct ichdev *ichdev = get_ichdev(substream);
+    int dbl = params_rate(hw_params) > 48000;
+    int err;
+
+    err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+    if (err < 0)
+        return err;
+    if (ichdev->pcm_open_flag) {
+        snd_ac97_pcm_close(ichdev->pcm);
+        ichdev->pcm_open_flag = 0;
+    }
+    err = snd_ac97_pcm_open(ichdev->pcm, params_rate(hw_params),
+                            params_channels(hw_params),
+                            ichdev->pcm->r[dbl].slots);
+    if (err >= 0) {
+        ichdev->pcm_open_flag = 1;
+        /* Force SPDIF setting */
+        if (ichdev->ichd == ICHD_PCMOUT && chip->spdif_idx < 0)
+            snd_ac97_set_rate(ichdev->pcm->r[0].codec[0], AC97_SPDIF, params_rate(hw_params));
+    }
+    return err;
+}
+
+static int snd_intel8x0_hw_free(snd_pcm_substream_t * substream)
+{
+    struct ichdev *ichdev = get_ichdev(substream);
+
+    if (ichdev->pcm_open_flag) {
+        snd_ac97_pcm_close(ichdev->pcm);
+        ichdev->pcm_open_flag = 0;
+    }
+    return snd_pcm_lib_free_pages(substream);
+}
+
+static void snd_intel8x0_setup_pcm_out(struct intel8x0 *chip,
+                                       snd_pcm_runtime_t *runtime)
+{
+    unsigned int cnt;
+    int dbl = runtime->rate > 48000;
+
+    spin_lock_irq(&chip->reg_lock);
+    switch (chip->device_type) {
+    case DEVICE_ALI:
+        cnt = igetdword(chip, ICHREG(ALI_SCR));
+        cnt &= ~ICH_ALI_SC_PCM_246_MASK;
+        if (runtime->channels == 4 || dbl)
+            cnt |= ICH_ALI_SC_PCM_4;
+        else if (runtime->channels == 6)
+            cnt |= ICH_ALI_SC_PCM_6;
+        iputdword(chip, ICHREG(ALI_SCR), cnt);
+        break;
+    case DEVICE_SIS:
+        cnt = igetdword(chip, ICHREG(GLOB_CNT));
+        cnt &= ~ICH_SIS_PCM_246_MASK;
+        if (runtime->channels == 4 || dbl)
+            cnt |= ICH_SIS_PCM_4;
+        else if (runtime->channels == 6)
+            cnt |= ICH_SIS_PCM_6;
+        iputdword(chip, ICHREG(GLOB_CNT), cnt);
+        break;
+    default:
+        cnt = igetdword(chip, ICHREG(GLOB_CNT));
+        cnt &= ~(ICH_PCM_246_MASK | ICH_PCM_20BIT);
+        if (runtime->channels == 4 || dbl)
+            cnt |= ICH_PCM_4;
+        else if (runtime->channels == 6)
+            cnt |= ICH_PCM_6;
+        if (chip->device_type == DEVICE_NFORCE) {
+            /* reset to 2ch once to keep the 6 channel data in alignment,
+             * to start from Front Left always
+             */
+            if (cnt & ICH_PCM_246_MASK) {
+                iputdword(chip, ICHREG(GLOB_CNT), cnt & ~ICH_PCM_246_MASK);
+                spin_unlock_irq(&chip->reg_lock);
+                msleep(50); /* grrr... */
+                spin_lock_irq(&chip->reg_lock);
+            }
+        } else if (chip->device_type == DEVICE_INTEL_ICH4 ||
+                   chip->device_type == DEVICE_INTEL_ICH5) {
+            if (runtime->sample_bits > 16)
+                cnt |= ICH_PCM_20BIT;
+        }
+        iputdword(chip, ICHREG(GLOB_CNT), cnt);
+        break;
+    }
+    spin_unlock_irq(&chip->reg_lock);
+}
+
+static int snd_intel8x0_pcm_prepare(snd_pcm_substream_t * substream)
+{
+    struct intel8x0 *chip = snd_pcm_substream_chip(substream);
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    struct ichdev *ichdev = get_ichdev(substream);
+
+    ichdev->physbuf = runtime->dma_addr;
+    ichdev->size = snd_pcm_lib_buffer_bytes(substream);
+    ichdev->fragsize = snd_pcm_lib_period_bytes(substream);
+    if (ichdev->ichd == ICHD_PCMOUT) {
+        snd_intel8x0_setup_pcm_out(chip, runtime);
+        if (chip->device_type == DEVICE_INTEL_ICH4 ||
+            chip->device_type == DEVICE_INTEL_ICH5)
+            ichdev->pos_shift = (runtime->sample_bits > 16) ? 2 : 1;
+    }
+    snd_intel8x0_setup_periods(chip, ichdev);
+    return 0;
+}
+
+static snd_pcm_uframes_t snd_intel8x0_pcm_pointer(snd_pcm_substream_t * substream)
+{
+    struct intel8x0 *chip = snd_pcm_substream_chip(substream);
+    struct ichdev *ichdev = get_ichdev(substream);
+    size_t ptr1, ptr;
+    int civ, timeout = 100;
+    unsigned int position;
+
+    spin_lock(&chip->reg_lock);
+    do {
+        civ = igetbyte(chip, ichdev->reg_offset + ICH_REG_OFF_CIV);
+        ptr1 = igetword(chip, ichdev->reg_offset + ichdev->roff_picb);
+        position = ichdev->position;
+        if (ptr1 == 0) {
+            udelay(10);
+            continue;
+        }
+        if (civ == igetbyte(chip, ichdev->reg_offset + ICH_REG_OFF_CIV) &&
+            ptr1 == igetword(chip, ichdev->reg_offset + ichdev->roff_picb))
+            break;
+    } while (timeout--);
+    ptr1 <<= ichdev->pos_shift;
+    ptr = ichdev->fragsize1 - ptr1;
+    ptr += position;
+    spin_unlock(&chip->reg_lock);
+    if (ptr >= ichdev->size)
+        return 0;
+    return bytes_to_frames(substream->runtime, ptr);
+}
+
+static snd_pcm_hardware_t snd_intel8x0_stream =
+{
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_MMAP_VALID |
+				 SNDRV_PCM_INFO_PAUSE |
+				 SNDRV_PCM_INFO_RESUME),
+	.formats =		SNDRV_PCM_FMTBIT_S16_LE,
+	.rates =		SNDRV_PCM_RATE_48000,
+	.rate_min =		48000,
+	.rate_max =		48000,
+	.channels_min =		2,
+	.channels_max =		2,
+	.buffer_bytes_max =	128 * 1024,
+	.period_bytes_min =	32,
+	.period_bytes_max =	128 * 1024,
+	.periods_min =		1,
+	.periods_max =		1024,
+	.fifo_size =		0,
+};
+
+static unsigned int channels4[] = {
+    2, 4,
+};
+
+static snd_pcm_hw_constraint_list_t hw_constraints_channels4 = {
+	.count = ARRAY_SIZE(channels4),
+	.list = channels4,
+	.mask = 0,
+};
+
+static unsigned int channels6[] = {
+    2, 4, 6,
+};
+
+static snd_pcm_hw_constraint_list_t hw_constraints_channels6 = {
+	.count = ARRAY_SIZE(channels6),
+	.list = channels6,
+	.mask = 0,
+};
+
+static int snd_intel8x0_pcm_open(snd_pcm_substream_t * substream, struct ichdev *ichdev)
+{
+    struct intel8x0 *chip = snd_pcm_substream_chip(substream);
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    int err;
+
+    ichdev->substream = substream;
+    runtime->hw = snd_intel8x0_stream;
+    runtime->hw.rates = ichdev->pcm->rates;
+    snd_pcm_limit_hw_rates(runtime);
+    if (chip->device_type == DEVICE_SIS) {
+        // vladest workaround
+        if (!runtime->hw.rates)
+            runtime->hw.rates = SNDRV_PCM_RATE_48000;
+        runtime->hw.buffer_bytes_max = 64*1024;
+        runtime->hw.period_bytes_max = 64*1024;
+    }
+#if 1 /* vladest */
+    if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+        return err;
+#endif
+    runtime->private_data = ichdev;
+    return 0;
+}
+
+static int snd_intel8x0_playback_open(snd_pcm_substream_t * substream)
+{
+    struct intel8x0 *chip = snd_pcm_substream_chip(substream);
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    int err;
+
+    err = snd_intel8x0_pcm_open(substream, &chip->ichd[ICHD_PCMOUT]);
+    if (err < 0)
+    {
+        printk("snd_intel8x0_pcm_open open error: %i\n", err);
+        return err;
+    }
+    if (chip->multi6) {
+        runtime->hw.channels_max = 6;
+        snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &hw_constraints_channels6);
+    } else if (chip->multi4) {
+        runtime->hw.channels_max = 4;
+        snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &hw_constraints_channels4);
+    }
+    if (chip->dra) {
+        snd_ac97_pcm_double_rate_rules(runtime);
+    }
+    if (chip->smp20bit)
+        runtime->hw.formats |= SNDRV_PCM_FMTBIT_S32_LE;
+    return 0;
+}
+
+static int snd_intel8x0_playback_close(snd_pcm_substream_t * substream)
+{
+    struct intel8x0 *chip = snd_pcm_substream_chip(substream);
+
+    chip->ichd[ICHD_PCMOUT].substream = NULL;
+    return 0;
+}
+
+static int snd_intel8x0_capture_open(snd_pcm_substream_t * substream)
+{
+    struct intel8x0 *chip = snd_pcm_substream_chip(substream);
+
+    return snd_intel8x0_pcm_open(substream, &chip->ichd[ICHD_PCMIN]);
+}
+
+static int snd_intel8x0_capture_close(snd_pcm_substream_t * substream)
+{
+    struct intel8x0 *chip = snd_pcm_substream_chip(substream);
+
+    chip->ichd[ICHD_PCMIN].substream = NULL;
+    return 0;
+}
+
+static int snd_intel8x0_mic_open(snd_pcm_substream_t * substream)
+{
+    struct intel8x0 *chip = snd_pcm_substream_chip(substream);
+
+    return snd_intel8x0_pcm_open(substream, &chip->ichd[ICHD_MIC]);
+}
+
+static int snd_intel8x0_mic_close(snd_pcm_substream_t * substream)
+{
+    struct intel8x0 *chip = snd_pcm_substream_chip(substream);
+
+    chip->ichd[ICHD_MIC].substream = NULL;
+    return 0;
+}
+
+static int snd_intel8x0_mic2_open(snd_pcm_substream_t * substream)
+{
+    struct intel8x0 *chip = snd_pcm_substream_chip(substream);
+
+    return snd_intel8x0_pcm_open(substream, &chip->ichd[ICHD_MIC2]);
+}
+
+static int snd_intel8x0_mic2_close(snd_pcm_substream_t * substream)
+{
+    struct intel8x0 *chip = snd_pcm_substream_chip(substream);
+
+    chip->ichd[ICHD_MIC2].substream = NULL;
+    return 0;
+}
+
+static int snd_intel8x0_capture2_open(snd_pcm_substream_t * substream)
+{
+    struct intel8x0 *chip = snd_pcm_substream_chip(substream);
+
+    return snd_intel8x0_pcm_open(substream, &chip->ichd[ICHD_PCM2IN]);
+}
+
+static int snd_intel8x0_capture2_close(snd_pcm_substream_t * substream)
+{
+    struct intel8x0 *chip = snd_pcm_substream_chip(substream);
+
+    chip->ichd[ICHD_PCM2IN].substream = NULL;
+    return 0;
+}
+
+static int snd_intel8x0_spdif_open(snd_pcm_substream_t * substream)
+{
+    struct intel8x0 *chip = snd_pcm_substream_chip(substream);
+    int idx = chip->device_type == DEVICE_NFORCE ? NVD_SPBAR : ICHD_SPBAR;
+
+    return snd_intel8x0_pcm_open(substream, &chip->ichd[idx]);
+}
+
+static int snd_intel8x0_spdif_close(snd_pcm_substream_t * substream)
+{
+    struct intel8x0 *chip = snd_pcm_substream_chip(substream);
+    int idx = chip->device_type == DEVICE_NFORCE ? NVD_SPBAR : ICHD_SPBAR;
+
+    chip->ichd[idx].substream = NULL;
+    return 0;
+}
+
+static int snd_intel8x0_ali_ac97spdifout_open(snd_pcm_substream_t * substream)
+{
+    struct intel8x0 *chip = snd_pcm_substream_chip(substream);
+    unsigned int val;
+
+    spin_lock_irq(&chip->reg_lock);
+    val = igetdword(chip, ICHREG(ALI_INTERFACECR));
+    val |= ICH_ALI_IF_AC97SP;
+    iputdword(chip, ICHREG(ALI_INTERFACECR), val);
+    /* also needs to set ALI_SC_CODEC_SPDF correctly */
+    spin_unlock_irq(&chip->reg_lock);
+
+    return snd_intel8x0_pcm_open(substream, &chip->ichd[ALID_AC97SPDIFOUT]);
+}
+
+static int snd_intel8x0_ali_ac97spdifout_close(snd_pcm_substream_t * substream)
+{
+    struct intel8x0 *chip = snd_pcm_substream_chip(substream);
+    unsigned int val;
+
+    chip->ichd[ALID_AC97SPDIFOUT].substream = NULL;
+    spin_lock_irq(&chip->reg_lock);
+    val = igetdword(chip, ICHREG(ALI_INTERFACECR));
+    val &= ~ICH_ALI_IF_AC97SP;
+    iputdword(chip, ICHREG(ALI_INTERFACECR), val);
+    spin_unlock_irq(&chip->reg_lock);
+
+    return 0;
+}
+
+static int snd_intel8x0_ali_spdifin_open(snd_pcm_substream_t * substream)
+{
+    struct intel8x0 *chip = snd_pcm_substream_chip(substream);
+
+    return snd_intel8x0_pcm_open(substream, &chip->ichd[ALID_SPDIFIN]);
+}
+
+static int snd_intel8x0_ali_spdifin_close(snd_pcm_substream_t * substream)
+{
+    struct intel8x0 *chip = snd_pcm_substream_chip(substream);
+
+    chip->ichd[ALID_SPDIFIN].substream = NULL;
+    return 0;
+}
+
+#if 0 // NYI
+static int snd_intel8x0_ali_spdifout_open(snd_pcm_substream_t * substream)
+{
+    struct intel8x0 *chip = snd_pcm_substream_chip(substream);
+
+    return snd_intel8x0_pcm_open(substream, &chip->ichd[ALID_SPDIFOUT]);
+}
+
+static int snd_intel8x0_ali_spdifout_close(snd_pcm_substream_t * substream)
+{
+    struct intel8x0 *chip = snd_pcm_substream_chip(substream);
+
+    chip->ichd[ALID_SPDIFOUT].substream = NULL;
+    return 0;
+}
+#endif
+
+static snd_pcm_ops_t snd_intel8x0_playback_ops = {
+	.open =		snd_intel8x0_playback_open,
+	.close =	snd_intel8x0_playback_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_intel8x0_hw_params,
+	.hw_free =	snd_intel8x0_hw_free,
+	.prepare =	snd_intel8x0_pcm_prepare,
+	.trigger =	snd_intel8x0_pcm_trigger,
+	.pointer =	snd_intel8x0_pcm_pointer,
+};
+
+static snd_pcm_ops_t snd_intel8x0_capture_ops = {
+	.open =		snd_intel8x0_capture_open,
+	.close =	snd_intel8x0_capture_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_intel8x0_hw_params,
+	.hw_free =	snd_intel8x0_hw_free,
+	.prepare =	snd_intel8x0_pcm_prepare,
+	.trigger =	snd_intel8x0_pcm_trigger,
+	.pointer =	snd_intel8x0_pcm_pointer,
+};
+
+static snd_pcm_ops_t snd_intel8x0_capture_mic_ops = {
+	.open =		snd_intel8x0_mic_open,
+	.close =	snd_intel8x0_mic_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_intel8x0_hw_params,
+	.hw_free =	snd_intel8x0_hw_free,
+	.prepare =	snd_intel8x0_pcm_prepare,
+	.trigger =	snd_intel8x0_pcm_trigger,
+	.pointer =	snd_intel8x0_pcm_pointer,
+};
+
+static snd_pcm_ops_t snd_intel8x0_capture_mic2_ops = {
+	.open =		snd_intel8x0_mic2_open,
+	.close =	snd_intel8x0_mic2_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_intel8x0_hw_params,
+	.hw_free =	snd_intel8x0_hw_free,
+	.prepare =	snd_intel8x0_pcm_prepare,
+	.trigger =	snd_intel8x0_pcm_trigger,
+	.pointer =	snd_intel8x0_pcm_pointer,
+};
+
+static snd_pcm_ops_t snd_intel8x0_capture2_ops = {
+	.open =		snd_intel8x0_capture2_open,
+	.close =	snd_intel8x0_capture2_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_intel8x0_hw_params,
+	.hw_free =	snd_intel8x0_hw_free,
+	.prepare =	snd_intel8x0_pcm_prepare,
+	.trigger =	snd_intel8x0_pcm_trigger,
+	.pointer =	snd_intel8x0_pcm_pointer,
+};
+
+static snd_pcm_ops_t snd_intel8x0_spdif_ops = {
+	.open =		snd_intel8x0_spdif_open,
+	.close =	snd_intel8x0_spdif_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_intel8x0_hw_params,
+	.hw_free =	snd_intel8x0_hw_free,
+	.prepare =	snd_intel8x0_pcm_prepare,
+	.trigger =	snd_intel8x0_pcm_trigger,
+	.pointer =	snd_intel8x0_pcm_pointer,
+};
+
+static snd_pcm_ops_t snd_intel8x0_ali_playback_ops = {
+	.open =		snd_intel8x0_playback_open,
+	.close =	snd_intel8x0_playback_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_intel8x0_hw_params,
+	.hw_free =	snd_intel8x0_hw_free,
+	.prepare =	snd_intel8x0_pcm_prepare,
+	.trigger =	snd_intel8x0_ali_trigger,
+	.pointer =	snd_intel8x0_pcm_pointer,
+};
+
+static snd_pcm_ops_t snd_intel8x0_ali_capture_ops = {
+	.open =		snd_intel8x0_capture_open,
+	.close =	snd_intel8x0_capture_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_intel8x0_hw_params,
+	.hw_free =	snd_intel8x0_hw_free,
+	.prepare =	snd_intel8x0_pcm_prepare,
+	.trigger =	snd_intel8x0_ali_trigger,
+	.pointer =	snd_intel8x0_pcm_pointer,
+};
+
+static snd_pcm_ops_t snd_intel8x0_ali_capture_mic_ops = {
+	.open =		snd_intel8x0_mic_open,
+	.close =	snd_intel8x0_mic_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_intel8x0_hw_params,
+	.hw_free =	snd_intel8x0_hw_free,
+	.prepare =	snd_intel8x0_pcm_prepare,
+	.trigger =	snd_intel8x0_ali_trigger,
+	.pointer =	snd_intel8x0_pcm_pointer,
+};
+
+static snd_pcm_ops_t snd_intel8x0_ali_ac97spdifout_ops = {
+	.open =		snd_intel8x0_ali_ac97spdifout_open,
+	.close =	snd_intel8x0_ali_ac97spdifout_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_intel8x0_hw_params,
+	.hw_free =	snd_intel8x0_hw_free,
+	.prepare =	snd_intel8x0_pcm_prepare,
+	.trigger =	snd_intel8x0_ali_trigger,
+	.pointer =	snd_intel8x0_pcm_pointer,
+};
+
+static snd_pcm_ops_t snd_intel8x0_ali_spdifin_ops = {
+	.open =		snd_intel8x0_ali_spdifin_open,
+	.close =	snd_intel8x0_ali_spdifin_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_intel8x0_hw_params,
+	.hw_free =	snd_intel8x0_hw_free,
+	.prepare =	snd_intel8x0_pcm_prepare,
+	.trigger =	snd_intel8x0_pcm_trigger,
+	.pointer =	snd_intel8x0_pcm_pointer,
+};
+
+#if 0 // NYI
+static snd_pcm_ops_t snd_intel8x0_ali_spdifout_ops = {
+	.open =		snd_intel8x0_ali_spdifout_open,
+	.close =	snd_intel8x0_ali_spdifout_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_intel8x0_hw_params,
+	.hw_free =	snd_intel8x0_hw_free,
+	.prepare =	snd_intel8x0_pcm_prepare,
+	.trigger =	snd_intel8x0_pcm_trigger,
+	.pointer =	snd_intel8x0_pcm_pointer,
+};
+#endif // NYI
+
+
+struct ich_pcm_table {
+    char *suffix;
+    snd_pcm_ops_t *playback_ops;
+    snd_pcm_ops_t *capture_ops;
+    size_t prealloc_size;
+    size_t prealloc_max_size;
+    int ac97_idx;
+};
+
+static int __devinit snd_intel8x0_pcm1(struct intel8x0 *chip, int device, struct ich_pcm_table *rec)
+{
+	snd_pcm_t *pcm;
+	int err;
+	char name[32];
+
+	if (rec->suffix)
+		sprintf(name, "Intel ICH - %s", rec->suffix);
+	else
+		strcpy(name, "Intel ICH");
+	err = snd_pcm_new(chip->card, name, device,
+			  rec->playback_ops ? 1 : 0,
+			  rec->capture_ops ? 1 : 0, &pcm);
+	if (err < 0)
+		return err;
+
+	if (rec->playback_ops)
+		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, rec->playback_ops);
+	if (rec->capture_ops)
+		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, rec->capture_ops);
+
+	pcm->private_data = chip;
+	pcm->info_flags = 0;
+	if (rec->suffix)
+		sprintf(pcm->name, "%s - %s", chip->card->shortname, rec->suffix);
+	else
+		strcpy(pcm->name, chip->card->shortname);
+	chip->pcm[device] = pcm;
+
+	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci),
+					      rec->prealloc_size, rec->prealloc_max_size);
+
+	return 0;
+}
+
+static struct ich_pcm_table intel_pcms[] __devinitdata = {
+	{
+		.playback_ops = &snd_intel8x0_playback_ops,
+		.capture_ops = &snd_intel8x0_capture_ops,
+		.prealloc_size = 64 * 1024,
+		.prealloc_max_size = 128 * 1024,
+	},
+	{
+		.suffix = "MIC ADC",
+		.capture_ops = &snd_intel8x0_capture_mic_ops,
+		.prealloc_size = 0,
+		.prealloc_max_size = 128 * 1024,
+		.ac97_idx = ICHD_MIC,
+	},
+	{
+		.suffix = "MIC2 ADC",
+		.capture_ops = &snd_intel8x0_capture_mic2_ops,
+		.prealloc_size = 0,
+		.prealloc_max_size = 128 * 1024,
+		.ac97_idx = ICHD_MIC2,
+	},
+	{
+		.suffix = "ADC2",
+		.capture_ops = &snd_intel8x0_capture2_ops,
+		.prealloc_size = 0,
+		.prealloc_max_size = 128 * 1024,
+		.ac97_idx = ICHD_PCM2IN,
+	},
+	{
+		.suffix = "IEC958",
+		.playback_ops = &snd_intel8x0_spdif_ops,
+		.prealloc_size = 64 * 1024,
+		.prealloc_max_size = 128 * 1024,
+		.ac97_idx = ICHD_SPBAR,
+	},
+};
+
+static struct ich_pcm_table nforce_pcms[] __devinitdata = {
+	{
+		.playback_ops = &snd_intel8x0_playback_ops,
+		.capture_ops = &snd_intel8x0_capture_ops,
+		.prealloc_size = 64 * 1024,
+		.prealloc_max_size = 128 * 1024,
+	},
+	{
+		.suffix = "MIC ADC",
+		.capture_ops = &snd_intel8x0_capture_mic_ops,
+		.prealloc_size = 0,
+		.prealloc_max_size = 128 * 1024,
+		.ac97_idx = NVD_MIC,
+	},
+	{
+		.suffix = "IEC958",
+		.playback_ops = &snd_intel8x0_spdif_ops,
+		.prealloc_size = 64 * 1024,
+		.prealloc_max_size = 128 * 1024,
+		.ac97_idx = NVD_SPBAR,
+	},
+};
+
+static struct ich_pcm_table ali_pcms[] __devinitdata = {
+	{
+		.playback_ops = &snd_intel8x0_ali_playback_ops,
+		.capture_ops = &snd_intel8x0_ali_capture_ops,
+		.prealloc_size = 64 * 1024,
+		.prealloc_max_size = 128 * 1024,
+	},
+	{
+		.suffix = "MIC ADC",
+		.capture_ops = &snd_intel8x0_ali_capture_mic_ops,
+		.prealloc_size = 0,
+		.prealloc_max_size = 128 * 1024,
+		.ac97_idx = ALID_MIC,
+	},
+	{
+		.suffix = "IEC958",
+		.playback_ops = &snd_intel8x0_ali_ac97spdifout_ops,
+		.capture_ops = &snd_intel8x0_ali_spdifin_ops,
+		.prealloc_size = 64 * 1024,
+		.prealloc_max_size = 128 * 1024,
+		.ac97_idx = ALID_AC97SPDIFOUT,
+	},
+#if 0 // NYI
+	{
+		.suffix = "HW IEC958",
+		.playback_ops = &snd_intel8x0_ali_spdifout_ops,
+		.prealloc_size = 64 * 1024,
+		.prealloc_max_size = 128 * 1024,
+	},
+#endif
+};
+
+
+static int __devinit snd_intel8x0_pcm(struct intel8x0 *chip)
+{
+    int i, tblsize, device, err;
+    struct ich_pcm_table *tbl, *rec;
+
+    switch (chip->device_type) {
+    case DEVICE_INTEL_ICH4:
+    case DEVICE_INTEL_ICH5:
+        tbl = intel_pcms;
+        tblsize = ARRAY_SIZE(intel_pcms);
+        break;
+    case DEVICE_NFORCE:
+        tbl = nforce_pcms;
+        tblsize = ARRAY_SIZE(nforce_pcms);
+        break;
+    case DEVICE_ALI:
+        tbl = ali_pcms;
+        tblsize = ARRAY_SIZE(ali_pcms);
+        break;
+    default:
+        tbl = intel_pcms;
+        tblsize = 2;
+        break;
+    }
+
+    device = 0;
+    for (i = 0; i < tblsize; i++) {
+        rec = tbl + i;
+        if (i > 0 && rec->ac97_idx) {
+            /* activate PCM only when associated AC'97 codec */
+            if (! chip->ichd[rec->ac97_idx].pcm)
+                continue;
+        }
+        err = snd_intel8x0_pcm1(chip, device, rec);
+        if (err < 0)
+            return err;
+        device++;
+    }
+
+    chip->pcm_devs = device;
+    return 0;
+}
+
+
+/*
+ *  Mixer part
+ */
+
+static void snd_intel8x0_mixer_free_ac97_bus(ac97_bus_t *bus)
+{
+    struct intel8x0 *chip = bus->private_data;
+    chip->ac97_bus = NULL;
+}
+
+static void snd_intel8x0_mixer_free_ac97(ac97_t *ac97)
+{
+    struct intel8x0 *chip = ac97->private_data;
+    chip->ac97[ac97->num] = NULL;
+}
+
+static struct ac97_pcm ac97_pcm_defs[] __devinitdata = {
+    /* front PCM */
+    {
+        .exclusive = 1,
+        .r =
+        {
+            {
+                .slots = (1 << AC97_SLOT_PCM_LEFT) |
+                    (1 << AC97_SLOT_PCM_RIGHT) |
+                    (1 << AC97_SLOT_PCM_CENTER) |
+                    (1 << AC97_SLOT_PCM_SLEFT) |
+                    (1 << AC97_SLOT_PCM_SRIGHT) |
+                    (1 << AC97_SLOT_LFE)
+            },
+            {
+                .slots = (1 << AC97_SLOT_PCM_LEFT) |
+                    (1 << AC97_SLOT_PCM_RIGHT) |
+                    (1 << AC97_SLOT_PCM_LEFT_0) |
+                    (1 << AC97_SLOT_PCM_RIGHT_0)
+            }
+        }
+    },
+    /* PCM IN #1 */
+    {
+        .stream = 1,
+        .exclusive = 1,
+        .r = {	{
+            .slots = (1 << AC97_SLOT_PCM_LEFT) |
+                (1 << AC97_SLOT_PCM_RIGHT)
+        }
+        }
+    },
+    /* MIC IN #1 */
+    {
+        .stream = 1,
+        .exclusive = 1,
+        .r = {	{
+            .slots = (1 << AC97_SLOT_MIC)
+        }
+        }
+    },
+    /* S/PDIF PCM */
+    {
+        .exclusive = 1,
+        .spdif = 1,
+        .r = {	{
+            .slots = (1 << AC97_SLOT_SPDIF_LEFT2) |
+                (1 << AC97_SLOT_SPDIF_RIGHT2)
+        }
+        }
+    },
+    /* PCM IN #2 */
+    {
+        .stream = 1,
+        .exclusive = 1,
+        .r = {	{
+            .slots = (1 << AC97_SLOT_PCM_LEFT) |
+                (1 << AC97_SLOT_PCM_RIGHT)
+        }
+        }
+    },
+    /* MIC IN #2 */
+    {
+        .stream = 1,
+        .exclusive = 1,
+        .r = {	{
+            .slots = (1 << AC97_SLOT_MIC)
+        }
+        }
+    },
+};
+
+static struct ac97_quirk ac97_quirks[] __devinitdata = {
+	{
+		.subvendor = 0x0e11,
+		.subdevice = 0x008a,
+		.name = "Compaq Evo W4000",	/* AD1885 */
+		.type = AC97_TUNE_HP_ONLY
+	},
+	{
+		.subvendor = 0x0e11,
+		.subdevice = 0x00b8,
+		.name = "Compaq Evo D510C",
+		.type = AC97_TUNE_HP_ONLY
+	},
+        {
+		.subvendor = 0x0e11,
+		.subdevice = 0x0860,
+		.name = "HP/Compaq nx7010",
+		.type = AC97_TUNE_MUTE_LED
+        },
+	{
+		.subvendor = 0x1014,
+		.subdevice = 0x1f00,
+		.name = "MS-9128",
+		.type = AC97_TUNE_ALC_JACK
+	},
+        {
+            .subvendor = 0x1014,
+            .subdevice = 0x0267,
+            .name = "IBM NetVista A30p",    /* AD1981B */
+            .type = AC97_TUNE_HP_ONLY
+        },
+        {
+            .subvendor = 0x1025,
+            .subdevice = 0x0083,
+            .name = "Acer Aspire 3003LCi",
+            .type = AC97_TUNE_HP_ONLY
+        },
+        {
+		.subvendor = 0x1028,
+		.subdevice = 0x00d8,
+		.name = "Dell Precision 530",	/* AD1885 */
+		.type = AC97_TUNE_HP_ONLY
+	},
+	{
+		.subvendor = 0x1028,
+		.subdevice = 0x010d,
+		.name = "Dell",	/* which model?  AD1885 */
+		.type = AC97_TUNE_HP_ONLY
+	},
+	{
+		.subvendor = 0x1028,
+		.subdevice = 0x0126,
+		.name = "Dell Optiplex GX260",	/* AD1981A */
+		.type = AC97_TUNE_HP_ONLY
+	},
+	{
+		.subvendor = 0x1028,
+		.subdevice = 0x012c,
+		.name = "Dell Precision 650",	/* AD1981A */
+		.type = AC97_TUNE_HP_ONLY
+	},
+	{
+		.subvendor = 0x1028,
+		.subdevice = 0x012d,
+		.name = "Dell Precision 450",	/* AD1981B*/
+		.type = AC97_TUNE_HP_ONLY
+	},
+	{
+		.subvendor = 0x1028,
+		.subdevice = 0x0147,
+		.name = "Dell",	/* which model?  AD1981B*/
+		.type = AC97_TUNE_HP_ONLY
+	},
+	{
+            .subvendor = 0x1028,
+            .subdevice = 0x0151,
+            .name = "Dell Optiplex GX270",  /* AD1981B */
+            .type = AC97_TUNE_HP_ONLY
+        },
+        {
+            .subvendor = 0x1028,
+            .subdevice = 0x014e,
+            .name = "Dell D800", /* STAC9750/51 */
+            .type = AC97_TUNE_HP_ONLY
+	},
+	{
+		.subvendor = 0x1028,
+		.subdevice = 0x0163,
+		.name = "Dell Unknown",	/* STAC9750/51 */
+		.type = AC97_TUNE_HP_ONLY
+        },
+        {
+            .subvendor = 0x1028,
+            .subdevice = 0x0191,
+            .name = "Dell Inspiron 8600",
+            .type = AC97_TUNE_HP_ONLY
+        },
+	{
+		.subvendor = 0x103c,
+		.subdevice = 0x006d,
+		.name = "HP zv5000",
+		.type = AC97_TUNE_MUTE_LED	/*AD1981B*/
+	},
+	{	/* FIXME: which codec? */
+		.subvendor = 0x103c,
+		.subdevice = 0x00c3,
+		.name = "HP xw6000",
+		.type = AC97_TUNE_HP_ONLY
+	},
+	{
+		.subvendor = 0x103c,
+		.subdevice = 0x088c,
+		.name = "HP nc8000",
+		.type = AC97_TUNE_MUTE_LED
+	},
+	{
+		.subvendor = 0x103c,
+		.subdevice = 0x0890,
+		.name = "HP nc6000",
+		.type = AC97_TUNE_MUTE_LED
+	},
+	{
+            .subvendor = 0x103c,
+            .subdevice = 0x0934,
+            .name = "HP nx8220",
+            .type = AC97_TUNE_MUTE_LED
+        },
+        {
+            .subvendor = 0x103c,
+		.subdevice = 0x129d,
+		.name = "HP xw8000",
+		.type = AC97_TUNE_HP_ONLY
+	},
+	{
+            .subvendor = 0x103c,
+            .subdevice = 0x0938,
+            .name = "HP nc4200",
+            .type = AC97_TUNE_HP_MUTE_LED
+        },
+        {
+            .subvendor = 0x103c,
+            .subdevice = 0x099c,
+            .name = "HP nc6120",
+            .type = AC97_TUNE_HP_MUTE_LED
+        },
+        {
+            .subvendor = 0x103c,
+            .subdevice = 0x0944,
+            .name = "HP nx6110/nc6120",
+            .type = AC97_TUNE_HP_MUTE_LED
+        },
+        {
+            .subvendor = 0x103c,
+            .subdevice = 0x0934,
+            .name = "HP nc8220",
+            .type = AC97_TUNE_HP_MUTE_LED
+        },
+        {
+            .subvendor = 0x103c,
+		.subdevice = 0x12f1,
+		.name = "HP xw8200",	/* AD1981B*/
+		.type = AC97_TUNE_HP_ONLY
+	},
+	{
+		.subvendor = 0x103c,
+		.subdevice = 0x12f2,
+		.name = "HP xw6200",
+		.type = AC97_TUNE_HP_ONLY
+	},
+	{
+		.subvendor = 0x103c,
+		.subdevice = 0x3008,
+		.name = "HP xw4200",	/* AD1981B*/
+		.type = AC97_TUNE_HP_ONLY
+	},
+	{
+		.subvendor = 0x104d,
+		.subdevice = 0x8197,
+		.name = "Sony S1XP",
+		.type = AC97_TUNE_INV_EAPD
+	},
+ 	{
+		.subvendor = 0x1043,
+		.subdevice = 0x80f3,
+		.name = "ASUS ICH5/AD1985",
+		.type = AC97_TUNE_AD_SHARING
+	},
+	{
+		.subvendor = 0x10cf,
+		.subdevice = 0x11c3,
+		.name = "Fujitsu-Siemens E4010",
+		.type = AC97_TUNE_HP_ONLY
+	},
+	{
+		.subvendor = 0x10cf,
+		.subdevice = 0x1225,
+		.name = "Fujitsu-Siemens T3010",
+		.type = AC97_TUNE_HP_ONLY
+	},
+	{
+		.subvendor = 0x10cf,
+		.subdevice = 0x1253,
+		.name = "Fujitsu S6210",	/* STAC9750/51 */
+		.type = AC97_TUNE_HP_ONLY
+	},
+        {
+            .subvendor = 0x10cf,
+            .subdevice = 0x12ec,
+            .name = "Fujitsu-Siemens 4010",
+            .type = AC97_TUNE_HP_ONLY
+        },
+        {
+            .subvendor = 0x10cf,
+            .subdevice = 0x12f2,
+            .name = "Fujitsu-Siemens Celsius H320",
+            .type = AC97_TUNE_SWAP_HP
+        },
+        {
+		.subvendor = 0x10f1,
+		.subdevice = 0x2665,
+		.name = "Fujitsu-Siemens Celsius",	/* AD1981? */
+		.type = AC97_TUNE_HP_ONLY
+	},
+	{
+		.subvendor = 0x10f1,
+		.subdevice = 0x2885,
+		.name = "AMD64 Mobo",	/* ALC650 */
+		.type = AC97_TUNE_HP_ONLY
+	},
+        {
+            .subvendor = 0x10f1,
+            .subdevice = 0x2895,
+            .name = "Tyan Thunder K8WE",
+            .type = AC97_TUNE_HP_ONLY
+	},
+	{
+		.subvendor = 0x110a,
+		.subdevice = 0x0056,
+		.name = "Fujitsu-Siemens Scenic",	/* AD1981? */
+		.type = AC97_TUNE_HP_ONLY
+	},
+	{
+		.subvendor = 0x11d4,
+		.subdevice = 0x5375,
+		.name = "ADI AD1985 (discrete)",
+		.type = AC97_TUNE_HP_ONLY
+	},
+	{
+		.subvendor = 0x1462,
+		.subdevice = 0x5470,
+		.name = "MSI P4 ATX 645 Ultra",
+		.type = AC97_TUNE_HP_ONLY
+	},
+	{
+		.subvendor = 0x1734,
+		.subdevice = 0x0088,
+		.name = "Fujitsu-Siemens D1522",	/* AD1981 */
+		.type = AC97_TUNE_HP_ONLY
+        },
+        {
+            .subvendor = 0x8086,
+            .subdevice = 0x0104,
+            .name = "Intel D845GEBV2",              /* AD1981B */
+            .type = AC97_TUNE_HP_ONLY
+        },
+	{
+		.subvendor = 0x8086,
+		.subdevice = 0x2000,
+		.mask = 0xfff0,
+		.name = "Intel ICH5/AD1985",
+		.type = AC97_TUNE_AD_SHARING
+	},
+	{
+		.subvendor = 0x8086,
+		.subdevice = 0x4000,
+		.mask = 0xfff0,
+		.name = "Intel ICH5/AD1985",
+		.type = AC97_TUNE_AD_SHARING
+	},
+	{
+		.subvendor = 0x8086,
+		.subdevice = 0x4856,
+		.name = "Intel D845WN (82801BA)",
+		.type = AC97_TUNE_SWAP_HP
+	},
+	{
+		.subvendor = 0x8086,
+		.subdevice = 0x4d44,
+		.name = "Intel D850EMV2",	/* AD1885 */
+		.type = AC97_TUNE_HP_ONLY
+	},
+	{
+		.subvendor = 0x8086,
+		.subdevice = 0x4d56,
+		.name = "Intel ICH/AD1885",
+		.type = AC97_TUNE_HP_ONLY
+	},
+	{
+		.subvendor = 0x8086,
+		.subdevice = 0x6000,
+		.mask = 0xfff0,
+		.name = "Intel ICH5/AD1985",
+		.type = AC97_TUNE_AD_SHARING
+	},
+	{
+		.subvendor = 0x8086,
+		.subdevice = 0xe000,
+		.mask = 0xfff0,
+		.name = "Intel ICH5/AD1985",
+		.type = AC97_TUNE_AD_SHARING
+	},
+#if 0 /* FIXME: this seems wrong on most boards */
+	{
+		.subvendor = 0x8086,
+		.subdevice = 0xa000,
+		.mask = 0xfff0,
+		.name = "Intel ICH5/AD1985",
+		.type = AC97_TUNE_HP_ONLY
+	},
+#endif
+	{0} /* terminator */
+};
+
+static int __devinit snd_intel8x0_mixer(struct intel8x0 *chip, int ac97_clock, char *quirk_override)
+{
+    ac97_bus_t *pbus;
+    ac97_template_t ac97;
+    int err;
+    unsigned int i, codecs;
+    unsigned int glob_sta = 0;
+    ac97_bus_ops_t *ops;
+    static struct snd_ac97_bus_ops standard_bus_ops = {
+        .write = snd_intel8x0_codec_write,
+        .read = snd_intel8x0_codec_read,
+    };
+    static struct snd_ac97_bus_ops ali_bus_ops = {
+        .write = snd_intel8x0_ali_codec_write,
+        .read = snd_intel8x0_ali_codec_read,
+    };
+
+    chip->spdif_idx = -1; /* use PCMOUT (or disabled) */
+    switch (chip->device_type) {
+    case DEVICE_NFORCE:
+        chip->spdif_idx = NVD_SPBAR;
+        break;
+    case DEVICE_ALI:
+        chip->spdif_idx = ALID_AC97SPDIFOUT;
+        break;
+    case DEVICE_INTEL_ICH4:
+    case DEVICE_INTEL_ICH5:
+        chip->spdif_idx = ICHD_SPBAR;
+        break;
+    };
+
+    chip->in_ac97_init = 1;
+
+    memset(&ac97, 0, sizeof(ac97));
+    ac97.private_data = chip;
+    ac97.private_free = snd_intel8x0_mixer_free_ac97;
+    ac97.scaps = AC97_SCAP_SKIP_MODEM;
+    if (chip->device_type != DEVICE_ALI) {
+        glob_sta = igetdword(chip, ICHREG(GLOB_STA));
+        ops = &standard_bus_ops;
+        chip->in_sdin_init = 1;
+        codecs = 0;
+        for (i = 0; i < chip->max_codecs; i++) {
+            if (! (glob_sta & chip->codec_bit[i]))
+                continue;
+            if (chip->device_type == DEVICE_INTEL_ICH4 ||
+                chip->device_type == DEVICE_INTEL_ICH5) {
+                snd_intel8x0_codec_read_test(chip, codecs);
+                chip->ac97_sdin[codecs] =
+                    igetbyte(chip, ICHREG(SDM)) & ICH_LDI_MASK;
+                snd_assert(chip->ac97_sdin[codecs] < 3,
+                           chip->ac97_sdin[codecs] = 0);
+            } else
+                chip->ac97_sdin[codecs] = i;
+            codecs++;
+            chip->in_sdin_init = 1;
+        }
+        chip->in_sdin_init = 0;
+        if (! codecs)
+            codecs = 1;
+    } else {
+        ops = &ali_bus_ops;
+        codecs = 1;
+        /* detect the secondary codec */
+        for (i = 0; i < 100; i++) {
+            unsigned int reg = igetdword(chip, ICHREG(ALI_RTSR));
+            if (reg & 0x40) {
+                codecs = 2;
+                break;
+            }
+            iputdword(chip, ICHREG(ALI_RTSR), reg | 0x40);
+            udelay(1);
+        }
+    }
+    if ((err = snd_ac97_bus(chip->card, 0, ops, chip, &pbus)) < 0)
+        goto __err;
+    pbus->private_free = snd_intel8x0_mixer_free_ac97_bus;
+    if (ac97_clock >= 8000 && ac97_clock <= 48000)
+        pbus->clock = ac97_clock;
+    /* FIXME: my test board doesn't work well with VRA... */
+    if (chip->device_type == DEVICE_ALI)
+        pbus->no_vra = 1;
+    else
+        pbus->dra = 1;
+    chip->ac97_bus = pbus;
+    chip->ncodecs = codecs;
+
+    ac97.pci = chip->pci;
+    for (i = 0; i < codecs; i++) {
+        ac97.num = i;
+        if ((err = snd_ac97_mixer(pbus, &ac97, &chip->ac97[i])) < 0) {
+            if (err != -EACCES)
+                snd_printk(KERN_ERR "Unable to initialize codec #%d\n", i);
+            if (i == 0)
+                goto __err;
+            continue;
+        }
+    }
+    /* tune up the primary codec */
+    snd_ac97_tune_hardware(chip->ac97[0], ac97_quirks, quirk_override);
+    /* enable separate SDINs for ICH4 */
+    if (chip->device_type == DEVICE_INTEL_ICH4 ||
+        chip->device_type == DEVICE_INTEL_ICH5)
+        pbus->isdin = 1;
+    /* find the available PCM streams */
+    i = ARRAY_SIZE(ac97_pcm_defs);
+    if (chip->device_type != DEVICE_INTEL_ICH4 &&
+        chip->device_type != DEVICE_INTEL_ICH5)
+        i -= 2;		/* do not allocate PCM2IN and MIC2 */
+    if (chip->spdif_idx < 0)
+        i--;		/* do not allocate S/PDIF */
+    err = snd_ac97_pcm_assign(pbus, i, ac97_pcm_defs);
+    if (err < 0)
+        goto __err;
+    chip->ichd[ICHD_PCMOUT].pcm = &pbus->pcms[0];
+    chip->ichd[ICHD_PCMIN].pcm = &pbus->pcms[1];
+    chip->ichd[ICHD_MIC].pcm = &pbus->pcms[2];
+    if (chip->spdif_idx >= 0)
+        chip->ichd[chip->spdif_idx].pcm = &pbus->pcms[3];
+    if (chip->device_type == DEVICE_INTEL_ICH4 ||
+        chip->device_type == DEVICE_INTEL_ICH5) {
+        chip->ichd[ICHD_PCM2IN].pcm = &pbus->pcms[4];
+        chip->ichd[ICHD_MIC2].pcm = &pbus->pcms[5];
+    }
+    /* enable separate SDINs for ICH4 */
+    if (chip->device_type == DEVICE_INTEL_ICH4 ||
+        chip->device_type == DEVICE_INTEL_ICH5) {
+        struct ac97_pcm *pcm = chip->ichd[ICHD_PCM2IN].pcm;
+        u8 tmp = igetbyte(chip, ICHREG(SDM));
+        tmp &= ~(ICH_DI2L_MASK|ICH_DI1L_MASK);
+        if (pcm) {
+            tmp |= ICH_SE;	/* steer enable for multiple SDINs */
+            tmp |= chip->ac97_sdin[0] << ICH_DI1L_SHIFT;
+            for (i = 1; i < 4; i++) {
+                if (pcm->r[0].codec[i]) {
+                    tmp |= chip->ac97_sdin[pcm->r[0].codec[1]->num] << ICH_DI2L_SHIFT;
+                    break;
+                }
+            }
+        } else {
+            tmp &= ~ICH_SE; /* steer disable */
+        }
+        iputbyte(chip, ICHREG(SDM), tmp);
+    }
+    if (pbus->pcms[0].r[0].slots & (1 << AC97_SLOT_PCM_SLEFT)) {
+        chip->multi4 = 1;
+        if (pbus->pcms[0].r[0].slots & (1 << AC97_SLOT_LFE))
+            chip->multi6 = 1;
+    }
+    if (pbus->pcms[0].r[1].rslots[0]) {
+        chip->dra = 1;
+    }
+    if (chip->device_type == DEVICE_INTEL_ICH4 ||
+        chip->device_type == DEVICE_INTEL_ICH5) {
+        if ((igetdword(chip, ICHREG(GLOB_STA)) & ICH_SAMPLE_CAP) == ICH_SAMPLE_16_20)
+            chip->smp20bit = 1;
+    }
+    if (chip->device_type == DEVICE_NFORCE) {
+        /* 48kHz only */
+        chip->ichd[chip->spdif_idx].pcm->rates = SNDRV_PCM_RATE_48000;
+    }
+
+    if (chip->device_type == DEVICE_INTEL_ICH4 ||
+        chip->device_type == DEVICE_INTEL_ICH5) {
+        /* use slot 10/11 for SPDIF */
+        u32 val;
+        val = igetdword(chip, ICHREG(GLOB_CNT)) & ~ICH_PCM_SPDIF_MASK;
+        val |= ICH_PCM_SPDIF_1011;
+        iputdword(chip, ICHREG(GLOB_CNT), val);
+        snd_ac97_update_bits(chip->ac97[0], AC97_EXTENDED_STATUS, 0x03 << 4, 0x03 << 4);
+    }
+    chip->in_ac97_init = 0;
+    return 0;
+
+    __err:
+        /* clear the cold-reset bit for the next chance */
+        if (chip->device_type != DEVICE_ALI)
+            iputdword(chip, ICHREG(GLOB_CNT), igetdword(chip, ICHREG(GLOB_CNT)) & ~ICH_AC97COLD);
+        return err;
+}
+
+
+/*
+ *
+ */
+
+static void do_ali_reset(struct intel8x0 *chip)
+{
+    iputdword(chip, ICHREG(ALI_SCR), ICH_ALI_SC_RESET);
+    iputdword(chip, ICHREG(ALI_FIFOCR1), 0x83838383);
+    iputdword(chip, ICHREG(ALI_FIFOCR2), 0x83838383);
+    iputdword(chip, ICHREG(ALI_FIFOCR3), 0x83838383);
+    iputdword(chip, ICHREG(ALI_INTERFACECR),
+              ICH_ALI_IF_PI|ICH_ALI_IF_PO);
+    iputdword(chip, ICHREG(ALI_INTERRUPTCR), 0x00000000);
+    iputdword(chip, ICHREG(ALI_INTERRUPTSR), 0x00000000);
+}
+
+#define do_delay(chip) do {\
+    set_current_state(TASK_UNINTERRUPTIBLE);\
+    schedule_timeout(1);\
+    } while (0)
+
+static int snd_intel8x0_ich_chip_init(struct intel8x0 *chip, int probing)
+{
+    unsigned long end_time;
+    unsigned int cnt, status, nstatus,i;
+
+#ifdef DEBUG
+    dprintf(("ICH chip init begins"));
+#endif
+    /* put logic to right state */
+    /* first clear status bits */
+    status = ICH_RCS | ICH_MCINT | ICH_POINT | ICH_PIINT;
+    if (chip->device_type == DEVICE_NFORCE)
+        status |= ICH_NVSPINT;
+    cnt = igetdword(chip, ICHREG(GLOB_STA));
+    iputdword(chip, ICHREG(GLOB_STA), cnt & status);
+    /* ACLink on, 2 channels */
+    cnt = igetdword(chip, ICHREG(GLOB_CNT));
+    //    cnt &= ~ICH_ACLINK;
+    cnt &= ~(ICH_ACLINK | ICH_PCM_246_MASK);
+#if 0
+    //def CONFIG_SND_AC97_POWER_SAVE
+    /* do cold reset - the full ac97 powerdown may leave the controller
+     * in a warm state but actually it cannot communicate with the codec.
+     */
+    iputdword(chip, ICHREG(GLOB_CNT), cnt & ~ICH_AC97COLD);
+    cnt = igetdword(chip, ICHREG(GLOB_CNT));
+    udelay(10);
+    iputdword(chip, ICHREG(GLOB_CNT), cnt | ICH_AC97COLD);
+    msleep(1);
+#else
+    cnt |= (cnt & ICH_AC97COLD) == 0 ? ICH_AC97COLD : ICH_AC97WARM;
+    /* finish cold or do warm reset */
+#ifdef DEBUG
+    dprintf(("ICH chip init try to wake up ACLink with %x",cnt));
+#endif
+    iputdword(chip, ICHREG(GLOB_CNT), cnt);
+    mdelay(500); //vladest 06.10.2003 15:08
+    end_time = (jiffies + (HZ / 4)) + 1;
+    i = 0;
+    do {
+        cnt=igetdword(chip, ICHREG(GLOB_CNT));
+        if ((cnt & ICH_AC97WARM) == 0)
+            goto __ok;
+#ifdef DEBUG
+        dprintf(("ICH chip init ACLink ON. try no %d got %x",i,cnt));
+#endif
+        do_delay(chip);
+        i++;
+    } while (i<100); //(time_after_eq(end_time, jiffies));
+    snd_printk("AC'97 warm reset still in progress? [0x%x]\n", igetdword(chip, ICHREG(GLOB_CNT)));
+    return -EIO;
+__ok:
+#endif /* CONFIG_SND_AC97_POWER_SAVE */
+#ifdef DEBUG
+    dprintf(("ICH chip init ACLink ON"));
+#endif
+
+//    mdelay(50);
+
+    if (probing) {
+        /* wait for any codec ready status.
+         * Once it becomes ready it should remain ready
+         * as long as we do not disable the ac97 link.
+         */
+        end_time = jiffies + HZ;
+        do {
+            status = igetdword(chip, ICHREG(GLOB_STA)) &
+                chip->codec_isr_bits;
+            if (status)
+                break;
+            mdelay(1);
+            //do_delay(chip);
+        } while (time_after_eq(end_time, jiffies));
+
+        if (! status) {
+            /* no codec is found */
+            snd_printk(KERN_ERR "codec_ready: codec is not ready [0x%x]\n", igetdword(chip, ICHREG(GLOB_STA)));
+            return -EIO;
+        }
+#ifdef DEBUG
+        dprintf(("ICH chip init codec ready"));
+#endif
+//        mdelay(50);
+
+        /* wait for other codecs ready status. */
+        end_time = jiffies + HZ / 4;
+        while (status != chip->codec_isr_bits &&
+               time_after_eq(end_time, jiffies)) {
+            do_delay(chip);
+            status |= igetdword(chip, ICHREG(GLOB_STA)) &
+                chip->codec_isr_bits;
+        }
+    } else {
+        /* resume phase */
+        int i;
+        status = 0;
+        for (i = 0; i < chip->ncodecs; i++)
+            if (chip->ac97[i])
+                status |= chip->codec_bit[chip->ac97_sdin[i]];
+        /* wait until all the probed codecs are ready */
+        end_time = jiffies + HZ;
+        do {
+            nstatus = igetdword(chip, ICHREG(GLOB_STA)) & chip->codec_isr_bits;;
+            if (status == nstatus)
+                break;
+            do_delay(chip);
+        } while (time_after_eq(end_time, jiffies));
+    }
+
+    if (chip->device_type == DEVICE_SIS) {
+        /* unmute the output on SIS7012 */
+        iputword(chip, 0x4c, igetword(chip, 0x4c) | 1);
+    }
+    if (chip->device_type == DEVICE_NFORCE) {
+        /* enable SPDIF interrupt */
+        unsigned int val;
+        pci_read_config_dword(chip->pci, 0x4c, &val);
+        val |= 0x1000000;
+        pci_write_config_dword(chip->pci, 0x4c, val);
+    }
+#ifdef DEBUG
+    dprintf(("ICH chip init finished"));
+#endif
+
+    return 0;
+}
+
+static int snd_intel8x0_ali_chip_init(struct intel8x0 *chip, int probing)
+{
+    u32 reg;
+    int i = 0;
+
+    reg = igetdword(chip, ICHREG(ALI_SCR));
+    if ((reg & 2) == 0)	/* Cold required */
+        reg |= 2;
+    else
+        reg |= 1;	/* Warm */
+    reg &= ~0x80000000;	/* ACLink on */
+    iputdword(chip, ICHREG(ALI_SCR), reg);
+
+    for (i = 0; i < HZ / 2; i++) {
+        if (! (igetdword(chip, ICHREG(ALI_INTERRUPTSR)) & ALI_INT_GPIO))
+            goto __ok;
+        do_delay(chip);
+    }
+    snd_printk(KERN_ERR "AC'97 reset failed.\n");
+    if (probing)
+        return -EIO;
+
+    __ok:
+        for (i = 0; i < HZ / 2; i++) {
+            reg = igetdword(chip, ICHREG(ALI_RTSR));
+            if (reg & 0x80) /* primary codec */
+                break;
+            iputdword(chip, ICHREG(ALI_RTSR), reg | 0x80);
+            do_delay(chip);
+        }
+
+        do_ali_reset(chip);
+        return 0;
+}
+
+static int snd_intel8x0_chip_init(struct intel8x0 *chip, int probing)
+{
+    unsigned int i, timeout;
+    int err;
+
+    if (chip->device_type != DEVICE_ALI) {
+        if ((err = snd_intel8x0_ich_chip_init(chip, probing)) < 0)
+            return err;
+        iagetword(chip, 0);	/* clear semaphore flag */
+    } else {
+        if ((err = snd_intel8x0_ali_chip_init(chip, probing)) < 0)
+            return err;
+    }
+
+    /* disable interrupts */
+    for (i = 0; i < chip->bdbars_count; i++)
+        iputbyte(chip, ICH_REG_OFF_CR + chip->ichd[i].reg_offset, 0x00);
+    /* reset channels */
+    for (i = 0; i < chip->bdbars_count; i++)
+        iputbyte(chip, ICH_REG_OFF_CR + chip->ichd[i].reg_offset, ICH_RESETREGS);
+    for (i = 0; i < chip->bdbars_count; i++) {
+        timeout = 100000;
+        while (--timeout != 0) {
+            if ((igetbyte(chip, ICH_REG_OFF_CR + chip->ichd[i].reg_offset) & ICH_RESETREGS) == 0)
+                break;
+        }
+        if (timeout == 0)
+            printk(KERN_ERR "intel8x0: reset of registers failed?\n");
+    }
+    /* initialize Buffer Descriptor Lists */
+    for (i = 0; i < chip->bdbars_count; i++)
+        iputdword(chip, ICH_REG_OFF_BDBAR + chip->ichd[i].reg_offset, chip->ichd[i].bdbar_addr);
+    return 0;
+}
+
+static int snd_intel8x0_free(struct intel8x0 *chip)
+{
+    unsigned int i;
+
+    if (chip->irq < 0)
+        goto __hw_end;
+    /* disable interrupts */
+    for (i = 0; i < chip->bdbars_count; i++)
+        iputbyte(chip, ICH_REG_OFF_CR + chip->ichd[i].reg_offset, 0x00);
+    /* reset channels */
+    for (i = 0; i < chip->bdbars_count; i++)
+        iputbyte(chip, ICH_REG_OFF_CR + chip->ichd[i].reg_offset, ICH_RESETREGS);
+    if (chip->device_type == DEVICE_NFORCE) {
+        /* stop the spdif interrupt */
+        unsigned int val;
+        pci_read_config_dword(chip->pci, 0x4c, &val);
+        val &= ~0x1000000;
+        pci_write_config_dword(chip->pci, 0x4c, val);
+    }
+    /* --- */
+    synchronize_irq(chip->irq);
+__hw_end:
+    if (chip->irq >= 0)
+        free_irq(chip->irq, (void *)chip);
+    if (chip->bdbars.area)
+        snd_dma_free_pages(&chip->bdbars);
+    if (chip->remap_addr)
+        iounmap((void *) chip->remap_addr);
+    if (chip->remap_bmaddr)
+        iounmap((void *) chip->remap_bmaddr);
+    pci_release_regions(chip->pci);
+    kfree(chip);
+    return 0;
+}
+
+#ifdef CONFIG_PM
+/*
+ * power management
+ */
+static int intel8x0_suspend(struct pci_dev *pci, pm_message_t state)
+{
+    struct snd_card *card = pci_get_drvdata(pci);
+    struct intel8x0 *chip = card->private_data;
+    int i;
+
+    snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+
+    for (i = 0; i < chip->pcm_devs; i++)
+        snd_pcm_suspend_all(chip->pcm[i]);
+
+    for (i = 0; i < chip->ncodecs; i++)
+        if (chip->ac97[i])	//Rudi: check, if codec present !!!
+            snd_ac97_suspend(chip->ac97[i]);
+    if (chip->device_type == DEVICE_INTEL_ICH4 ||
+        chip->device_type == DEVICE_INTEL_ICH5)
+        chip->sdm_saved = igetbyte(chip, ICHREG(SDM));
+
+    if (chip->irq >= 0)
+        free_irq(chip->irq, (void *)chip);
+    pci_disable_device(pci);
+    pci_save_state(pci);
+    return 0;
+}
+
+static int intel8x0_resume(struct pci_dev *pci)
+{
+    struct snd_card *card = pci_get_drvdata(pci);
+    struct intel8x0 *chip = card->private_data;
+    int i;
+
+    pci_restore_state(pci);
+    pci_enable_device(pci);
+    pci_set_master(pci);
+    request_irq(pci->irq, snd_intel8x0_interrupt, SA_INTERRUPT|SA_SHIRQ,
+                card->shortname, (void *)chip);
+    chip->irq = pci->irq;
+    synchronize_irq(chip->irq);
+    snd_intel8x0_chip_init(chip, 0);
+    /* re-initialize mixer stuff */
+    if (chip->device_type == DEVICE_INTEL_ICH4 ||
+        chip->device_type == DEVICE_INTEL_ICH5) {
+        /* enable separate SDINs for ICH4 */
+        iputbyte(chip, ICHREG(SDM), chip->sdm_saved);
+        /* use slot 10/11 for SPDIF */
+        iputdword(chip, ICHREG(GLOB_CNT),
+                  (igetdword(chip, ICHREG(GLOB_CNT)) & ~ICH_PCM_SPDIF_MASK) |
+                  ICH_PCM_SPDIF_1011);
+    }
+
+    for (i = 0; i < chip->ncodecs; i++)
+        if (chip->ac97[i])		//Rudi: check, if codec present !!!
+            snd_ac97_resume(chip->ac97[i]);
+    /* resume status */
+    for (i = 0; i < chip->bdbars_count; i++) {
+        struct ichdev *ichdev = &chip->ichd[i];
+        unsigned long port = ichdev->reg_offset;
+        if (! ichdev->substream || ! ichdev->suspended)
+            continue;
+        if (ichdev->ichd == ICHD_PCMOUT)
+            snd_intel8x0_setup_pcm_out(chip, ichdev->substream->runtime);
+        iputdword(chip, port + ICH_REG_OFF_BDBAR, ichdev->bdbar_addr);
+        iputbyte(chip, port + ICH_REG_OFF_LVI, ichdev->lvi);
+        iputbyte(chip, port + ICH_REG_OFF_CIV, ichdev->civ);
+        iputbyte(chip, port + ichdev->roff_sr, ICH_FIFOE | ICH_BCIS | ICH_LVBCI);
+    }
+    snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+    return 0;
+}
+
+#endif /* CONFIG_PM */
+
+#define INTEL8X0_TESTBUF_SIZE	32768	/* enough large for one shot */
+
+static void __devinit intel8x0_measure_ac97_clock(struct intel8x0 *chip)
+{
+    snd_pcm_substream_t *subs;
+    struct ichdev *ichdev;
+    unsigned long port;
+    unsigned long pos, t;
+    struct timeval start_time, stop_time;
+
+    if (chip->ac97_bus->clock != 48000)
+        return; /* specified in module option */
+
+    subs = chip->pcm[0]->streams[0].substream;
+    if (! subs || subs->dma_buffer.bytes < INTEL8X0_TESTBUF_SIZE) {
+        snd_printk("no playback buffer allocated - aborting measure ac97 clock\n");
+        return;
+    }
+    ichdev = &chip->ichd[ICHD_PCMOUT];
+    ichdev->physbuf = subs->dma_buffer.addr;
+    ichdev->size = chip->ichd[ICHD_PCMOUT].fragsize = INTEL8X0_TESTBUF_SIZE;
+    ichdev->substream = NULL; /* don't process interrupts */
+
+    /* set rate */
+    if (snd_ac97_set_rate(chip->ac97[0], AC97_PCM_FRONT_DAC_RATE, 48000) < 0) {
+        snd_printk(KERN_ERR "cannot set ac97 rate: clock = %d\n", chip->ac97_bus->clock);
+        return;
+    }
+    snd_intel8x0_setup_periods(chip, ichdev);
+    port = ichdev->reg_offset;
+    spin_lock_irq(&chip->reg_lock);
+    /* 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);
+        iputdword(chip, ICHREG(ALI_DMACR), 1 << ichdev->ali_slot);
+    }
+    do_gettimeofday(&start_time);
+    spin_unlock_irq(&chip->reg_lock);
+    mdelay(50);
+
+    spin_lock_irq(&chip->reg_lock);
+    /* check the position */
+    pos = ichdev->fragsize1;
+    pos -= igetword(chip, ichdev->reg_offset + ichdev->roff_picb) << ichdev->pos_shift;
+    pos += ichdev->position;
+    do_gettimeofday(&stop_time);
+    /* stop */
+    if (chip->device_type == DEVICE_ALI) {
+        iputdword(chip, ICHREG(ALI_DMACR), 1 << (ichdev->ali_slot + 16));
+        iputbyte(chip, port + ICH_REG_OFF_CR, 0);
+        while (igetbyte(chip, port + ICH_REG_OFF_CR))
+            ;
+    } else {
+        iputbyte(chip, port + ICH_REG_OFF_CR, 0);
+        while (!(igetbyte(chip, port + ichdev->roff_sr) & ICH_DCH))
+            ;
+    }
+    iputbyte(chip, port + ICH_REG_OFF_CR, ICH_RESETREGS);
+    spin_unlock_irq(&chip->reg_lock);
+#if 0
+    t = stop_time.tv_sec - start_time.tv_sec;
+    t *= 1000000;
+    t += stop_time.tv_usec - start_time.tv_usec;
+#else
+    t = 50000; /* patch, suggested by r.ihle */
+#endif
+    printk(KERN_INFO "%s: measured %lu usecs\n", __FUNCTION__, t);
+    if (t == 0) {
+        snd_printk(KERN_ERR "?? calculation error..\n");
+        return;
+    }
+    pos = (pos / 4) * 1000;
+    pos = (pos / t) * 1000 + ((pos % t) * 1000) / t;
+    if (pos < 40000 || pos >= 60000)
+        /* abnormal value. hw problem? */
+        printk(KERN_INFO "intel8x0: measured clock %ld rejected\n", pos);
+    else if (pos < 47500 || pos > 48500)
+        /* not 48000Hz, tuning the clock.. */
+        chip->ac97_bus->clock = (chip->ac97_bus->clock * 48000) / pos;
+    printk(KERN_INFO "intel8x0: clocking to %d\n", chip->ac97_bus->clock);
+    snd_ac97_update_power(chip->ac97[0], AC97_PCM_FRONT_DAC_RATE, 0);
+}
+
+#ifdef CONFIG_PROC_FS
+static void snd_intel8x0_proc_read(snd_info_entry_t * entry,
+                                   snd_info_buffer_t * buffer)
+{
+    struct intel8x0 *chip = entry->private_data;
+    unsigned int tmp;
+
+    snd_iprintf(buffer, "Intel8x0\n\n");
+    if (chip->device_type == DEVICE_ALI)
+        return;
+    tmp = igetdword(chip, ICHREG(GLOB_STA));
+    snd_iprintf(buffer, "Global control        : 0x%08x\n", igetdword(chip, ICHREG(GLOB_CNT)));
+    snd_iprintf(buffer, "Global status         : 0x%08x\n", tmp);
+    if (chip->device_type == DEVICE_INTEL_ICH4 ||
+        chip->device_type == DEVICE_INTEL_ICH5)
+        snd_iprintf(buffer, "SDM                   : 0x%08x\n", igetdword(chip, ICHREG(SDM)));
+    snd_iprintf(buffer, "AC'97 codecs ready    :");
+    if (tmp & chip->codec_isr_bits) {
+        int i;
+        static const char *codecs[3] = {
+            "primary", "secondary", "tertiary"
+        };
+        for (i = 0; i < chip->max_codecs; i++)
+            if (tmp & chip->codec_bit[i])
+                snd_iprintf(buffer, " %s", codecs[i]);
+    } else
+        snd_iprintf(buffer, " none");
+    snd_iprintf(buffer, "\n");
+    if (chip->device_type == DEVICE_INTEL_ICH4 ||
+  	             chip->device_type == DEVICE_SIS)
+        snd_iprintf(buffer, "AC'97 codecs SDIN     : %i %i %i\n",
+                    chip->ac97_sdin[0],
+                    chip->ac97_sdin[1],
+                    chip->ac97_sdin[2]);
+}
+
+static void __devinit snd_intel8x0_proc_init(struct intel8x0 * chip)
+{
+    snd_info_entry_t *entry;
+
+    if (! snd_card_proc_new(chip->card, "intel8x0", &entry))
+        snd_info_set_text_ops(entry, chip, 1024, snd_intel8x0_proc_read);
+}
+#else
+#define snd_intel8x0_proc_init(x)
+#endif
+
+static int snd_intel8x0_dev_free(snd_device_t *device)
+{
+    struct intel8x0 *chip = device->device_data;
+    return snd_intel8x0_free(chip);
+}
+
+struct ich_reg_info {
+    unsigned int int_sta_mask;
+    unsigned int offset;
+};
+
+static unsigned int ich_codec_bits[3] = {
+    ICH_PCR, ICH_SCR, ICH_TCR
+};
+static unsigned int sis_codec_bits[3] = {
+    ICH_PCR, ICH_SCR, ICH_SIS_TCR
+};
+
+static int __devinit snd_intel8x0_create(snd_card_t * card,
+                                         struct pci_dev *pci,
+                                         unsigned long device_type,
+                                         struct intel8x0 ** r_intel8x0)
+{
+    struct intel8x0 *chip;
+    int err;
+    unsigned int i,pci_dword;
+    unsigned int int_sta_masks;
+    unsigned short pci_word;
+    unsigned char pci_byte;
+
+    struct ichdev *ichdev;
+#ifdef TARGET_OS2
+    static snd_device_ops_t ops = {
+        snd_intel8x0_dev_free,0,0,0
+    };
+#else
+    static snd_device_ops_t ops = {
+        .dev_free =	snd_intel8x0_dev_free,
+    };
+#endif
+    static unsigned int bdbars[] = {
+        3, /* DEVICE_INTEL */
+        6, /* DEVICE_INTEL_ICH4 */
+        6, /* DEVICE_INTEL_ICH5 */
+        3, /* DEVICE_SIS */
+        6, /* DEVICE_ALI */
+        4, /* DEVICE_NFORCE */
+    };
+    static struct ich_reg_info intel_regs[6] = {
+        { ICH_PIINT, 0 },
+        { ICH_POINT, 0x10 },
+        { ICH_MCINT, 0x20 },
+        { ICH_M2INT, 0x40 },
+        { ICH_P2INT, 0x50 },
+        { ICH_SPINT, 0x60 },
+    };
+    static struct ich_reg_info nforce_regs[4] = {
+        { ICH_PIINT, 0 },
+        { ICH_POINT, 0x10 },
+        { ICH_MCINT, 0x20 },
+        { ICH_NVSPINT, 0x70 },
+    };
+    static struct ich_reg_info ali_regs[6] = {
+        { ALI_INT_PCMIN, 0x40 },
+        { ALI_INT_PCMOUT, 0x50 },
+        { ALI_INT_MICIN, 0x60 },
+        { ALI_INT_CODECSPDIFOUT, 0x70 },
+        { ALI_INT_SPDIFIN, 0xa0 },
+        { ALI_INT_SPDIFOUT, 0xb0 },
+    };
+    struct ich_reg_info *tbl;
+
+    *r_intel8x0 = NULL;
+
+    if ((err = pci_enable_device(pci)) < 0)
+        return err;
+    pci_set_master(pci);
+
+//    pci_write_config_byte(pci, 0x40, 0xff); // added by vladest
+
+    chip = kcalloc(1, sizeof(*chip), GFP_KERNEL);
+    if (chip == NULL)
+        return -ENOMEM;
+    spin_lock_init(&chip->reg_lock);
+    chip->device_type = device_type;
+    chip->card = card;
+    chip->pci = pci;
+    chip->irq = -1;
+    if ((err = pci_request_regions(pci, card->shortname)) < 0) {
+        kfree(chip);
+        return err;
+    }
+    if (device_type == DEVICE_ALI) {
+        /* ALI5455 has no ac97 region */
+        chip->bmaddr = pci_resource_start(pci, 0);
+        goto port_inited;
+    }
+    /*
+     ¥áâì €¢  ­î ­á .  €® á¬®âà¥âì. ¥à¢®¥: pci_cfg_space 54h,
+     €¢  ¬« €èšå ¡šâ  ãª §ë¢ îâ á®áâ®ï­š¥ çš¯ . ¥á«š â ¬ 11, â®
+     ªà®¬¥ ª®­äš£á¯¥©á  ¡®«ìè¥ ­š åà¥­  ­¥ à ¡®â ¥â.
+     ã€  ­ €® § ¯šá âì 00.
+     â®à®¥: à¥£šáâà PCICMD, íâ® 16-¡šâ­ë© à¥£šáâà ¯® á¬¥é¥­šî 04h
+     ¢ cfg_space. šâ 1 ¢ëáâ ¢šâì ¢ 1
+     */
+#if 1
+    if (chip->device_type == DEVICE_INTEL_ICH5 ||
+        chip->device_type == DEVICE_INTEL_ICH4)
+    {
+        pci_read_config_dword(pci, 0x40, &pci_dword);
+        printk("acpi_base %x\n", pci_dword);
+        pci_read_config_byte(pci, 0x44, &pci_byte);
+        printk("acpi_cntl %i\n", pci_byte);
+//        pci_write_config_byte(pci, 0x44, pci_byte & ~(0x10));
+        pci_read_config_word(pci, 0x54, &pci_word);
+//        pci_write_config_word(pci, 0x54, pci_word & ~(0x3));
+
+        pci_read_config_word(pci, PCI_COMMAND, &pci_word);
+//        pci_write_config_word(pci, PCI_COMMAND, pci_word | (PCI_COMMAND_IO | PCI_COMMAND_MEMORY));
+
+        pci_read_config_byte(pci, 0x55, &pci_byte);
+        printk("pci config at 0x55 %i\n", pci_byte);
+
+        pci_write_config_byte(pci, 0x55, 0x01);
+    }
+#endif
+    // make sure that pci allow mmio operations
+
+    if (pci_resource_flags(pci, /*2*/0) & IORESOURCE_MEM) {	/* ICH4 and Nforce */
+        chip->mmio = 1;
+        chip->addr = pci_resource_start(pci, /*2*/0);
+        chip->remap_addr = (unsigned long)
+            ioremap_nocache(chip->addr, pci_resource_len(pci, /*2*/0));
+        if (chip->remap_addr == 0) {
+            snd_printk("AC'97 space ioremap problem\n");
+            snd_intel8x0_free(chip);
+            return -EIO;
+        }
+        printk("ioremap %x len %i remap addr: %x\n",
+               chip->addr, pci_resource_len(pci, /*2*/0), chip->remap_addr);
+    }
+    else if (pci_resource_flags(pci, 2) & IORESOURCE_MEM) {	/* ICH4 and Nforce */
+        chip->mmio = 1;
+        chip->addr = pci_resource_start(pci, 2);
+        chip->remap_addr = (unsigned long)
+            ioremap_nocache(chip->addr, pci_resource_len(pci, 2));
+        if (chip->remap_addr == 0) {
+            snd_printk("AC'97 space ioremap problem\n");
+            snd_intel8x0_free(chip);
+            return -EIO;
+        }
+        printk("ioremap %x len %i remap addr: %x\n",
+               chip->addr, pci_resource_len(pci, 2), chip->remap_addr);
+    } else {
+        chip->addr = pci_resource_start(pci, 0);
+    }
+
+    if (pci_resource_flags(pci, /*3*/1) & IORESOURCE_MEM) {	/* ICH4 */
+        chip->bm_mmio = 1;
+        chip->bmaddr = pci_resource_start(pci, /*3*/1);
+        chip->remap_bmaddr = (unsigned long)
+            ioremap_nocache(chip->bmaddr, pci_resource_len(pci, /*3*/1));
+        if (chip->remap_bmaddr == 0) {
+            snd_printk("Controller space ioremap problem\n");
+            snd_intel8x0_free(chip);
+            return -EIO;
+        }
+        printk("ioremap bm %x len %i remap addr: %x\n",
+               chip->bmaddr, pci_resource_len(pci, /*3*/1), chip->remap_bmaddr);
+    }
+    else if (pci_resource_flags(pci, 3) & IORESOURCE_MEM) {	/* ICH4 */
+        chip->bm_mmio = 1;
+        chip->bmaddr = pci_resource_start(pci, 3);
+        chip->remap_bmaddr = (unsigned long)
+            ioremap_nocache(chip->bmaddr, pci_resource_len(pci, 3));
+        if (chip->remap_bmaddr == 0) {
+            snd_printk("Controller space ioremap problem\n");
+            snd_intel8x0_free(chip);
+            return -EIO;
+        }
+        printk("ioremap bm %x len %i remap addr: %x\n",
+               chip->bmaddr, pci_resource_len(pci, 3), chip->remap_bmaddr);
+    } else {
+        chip->bmaddr = pci_resource_start(pci, 1);
+    }
+
+port_inited:
+    chip->bdbars_count = bdbars[device_type];
+
+    /* initialize offsets */
+    switch (device_type) {
+    case DEVICE_NFORCE:
+        tbl = nforce_regs;
+        break;
+    case DEVICE_ALI:
+        tbl = ali_regs;
+        break;
+    default:
+        tbl = intel_regs;
+        break;
+    }
+    for (i = 0; i < chip->bdbars_count; i++) {
+        ichdev = &chip->ichd[i];
+        ichdev->ichd = i;
+        ichdev->reg_offset = tbl[i].offset;
+        ichdev->int_sta_mask = tbl[i].int_sta_mask;
+        if (device_type == DEVICE_SIS) {
+            /* SiS 7012 swaps the registers */
+            ichdev->roff_sr = ICH_REG_OFF_PICB;
+            ichdev->roff_picb = ICH_REG_OFF_SR;
+        } else {
+            ichdev->roff_sr = ICH_REG_OFF_SR;
+            ichdev->roff_picb = ICH_REG_OFF_PICB;
+        }
+        if (device_type == DEVICE_ALI)
+            ichdev->ali_slot = (ichdev->reg_offset - 0x40) / 0x10;
+        /* SIS7012 handles the pcm data in bytes, others are in samples */
+        ichdev->pos_shift = (device_type == DEVICE_SIS) ? 0 : 1;
+    }
+
+    /* allocate buffer descriptor lists */
+    /* the start of each lists must be aligned to 8 bytes */
+    if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
+                            chip->bdbars_count * sizeof(u32) * ICH_MAX_FRAGS * 2,
+                            &chip->bdbars) < 0) {
+        snd_intel8x0_free(chip);
+        snd_printk(KERN_ERR "intel8x0: cannot allocate buffer descriptors\n");
+        return -ENOMEM;
+    }
+    /* tables must be aligned to 8 bytes here, but the kernel pages
+     are much bigger, so we don't care (on i386) */
+
+    int_sta_masks = 0;
+    for (i = 0; i < chip->bdbars_count; i++) {
+        ichdev = &chip->ichd[i];
+        ichdev->bdbar = ((u32 *)chip->bdbars.area) +
+            (i * ICH_MAX_FRAGS * 2);
+        ichdev->bdbar_addr = chip->bdbars.addr +
+            (i * sizeof(u32) * ICH_MAX_FRAGS * 2);
+        int_sta_masks |= ichdev->int_sta_mask;
+    }
+    chip->int_sta_reg = device_type == DEVICE_ALI ?
+        ICH_REG_ALI_INTERRUPTSR : ICH_REG_GLOB_STA;
+    chip->int_sta_mask = int_sta_masks;
+
+    /* request irq after initializaing int_sta_mask, etc */
+    if (request_irq(pci->irq, snd_intel8x0_interrupt,
+                    SA_INTERRUPT|SA_SHIRQ, card->shortname, (void *)chip)) {
+        snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
+        snd_intel8x0_free(chip);
+        return -EBUSY;
+    }
+    chip->irq = pci->irq;
+    pci_set_master(pci);
+    synchronize_irq(chip->irq);
+
+    switch(chip->device_type) {
+    case DEVICE_INTEL_ICH4:
+    case DEVICE_INTEL_ICH5:
+        /* ICH4/5 can have three codecs */
+        chip->max_codecs = 3;
+        chip->codec_bit = ich_codec_bits;
+        chip->codec_ready_bits = ICH_PRI | ICH_SRI | ICH_TRI;
+        break;
+    case DEVICE_SIS:
+        /* recent SIS7012 can have three codecs */
+        chip->max_codecs = 3;
+        chip->codec_bit = sis_codec_bits;
+        chip->codec_ready_bits = ICH_PRI | ICH_SRI | ICH_SIS_TRI;
+        break;
+    default:
+        /* others up to two codecs */
+        chip->max_codecs = 2;
+        chip->codec_bit = ich_codec_bits;
+        chip->codec_ready_bits = ICH_PRI | ICH_SRI;
+        break;
+    }
+    for (i = 0; i < chip->max_codecs; i++)
+        chip->codec_isr_bits |= chip->codec_bit[i];
+
+    if ((err = snd_intel8x0_chip_init(chip, 1)) < 0) {
+        snd_intel8x0_free(chip);
+        return err;
+    }
+#ifdef DEBUG
+    dprintf(("snd_intel8x0_create. cp1"));
+#endif
+    if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+        snd_intel8x0_free(chip);
+        return err;
+    }
+
+    *r_intel8x0 = chip;
+#ifdef DEBUG
+    dprintf(("snd_intel8x0_create. cp2"));
+#endif
+    return 0;
+}
+
+static struct shortname_table {
+    unsigned int id;
+    const char *s;
+} shortnames[] __devinitdata = {
+    { PCI_DEVICE_ID_INTEL_82801, "Intel 82801AA-ICH" },
+    { PCI_DEVICE_ID_INTEL_82901, "Intel 82901AB-ICH0" },
+    { PCI_DEVICE_ID_INTEL_82801BA, "Intel 82801BA-ICH2" },
+    { PCI_DEVICE_ID_INTEL_440MX, "Intel 440MX" },
+    { PCI_DEVICE_ID_INTEL_ICH3, "Intel 82801CA-ICH3" },
+    { PCI_DEVICE_ID_INTEL_ICH4, "Intel 82801DB-ICH4" },
+    { PCI_DEVICE_ID_INTEL_ICH5, "Intel ICH5" },
+    { PCI_DEVICE_ID_INTEL_ESB_5, "Intel ICH 6300ESB" },
+    { PCI_DEVICE_ID_INTEL_ICH6_18, "Intel ICH6" },
+    { PCI_DEVICE_ID_INTEL_ICH7_20, "Intel ICH7" },
+    { PCI_DEVICE_ID_INTEL_ESB2_14, "Intel ESB2" },
+    { PCI_DEVICE_ID_SI_7012, "SiS SI7012" },
+    { PCI_DEVICE_ID_NVIDIA_MCP_AUDIO, "NVidia nForce" },
+    { PCI_DEVICE_ID_NVIDIA_MCP2_AUDIO, "NVidia nForce2" },
+    { PCI_DEVICE_ID_NVIDIA_MCP3_AUDIO, "NVidia nForce3" },
+    { PCI_DEVICE_ID_NVIDIA_CK8S_AUDIO, "NVidia CK8S" },
+    { PCI_DEVICE_ID_NVIDIA_CK804_AUDIO, "NVidia CK804" },
+    { PCI_DEVICE_ID_NVIDIA_CK8_AUDIO, "NVidia CK8" },
+    { 0x003a, "NVidia MCP04" },
+    { 0x746d, "AMD AMD8111" },
+    { 0x7445, "AMD AMD768" },
+    { 0x5455, "ALi M5455" },
+    { 0, 0 },
+};
+
+static int __devinit snd_intel8x0_probe(struct pci_dev *pci,
+                                        const struct pci_device_id *pci_id)
+{
+    static int dev;
+    snd_card_t *card;
+    struct intel8x0 *chip;
+    int err;
+    struct shortname_table *name;
+
+    if (dev >= SNDRV_CARDS)
+        return -ENODEV;
+    if (!enable[dev]) {
+        dev++;
+        return -ENOENT;
+    }
+
+    card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+    if (card == NULL)
+        return -ENOMEM;
+    switch (pci_id->driver_data) {
+    case DEVICE_NFORCE:
+        strcpy(card->driver, "NFORCE");
+        break;
+    case DEVICE_INTEL_ICH4:
+        strcpy(card->driver, "ICH4");
+        break;
+    case DEVICE_INTEL_ICH5:
+        strcpy(card->driver, "ICH5");
+        break;
+    default:
+        strcpy(card->driver, "ICH");
+        break;
+    }
+
+    strcpy(card->shortname, "Intel ICH");
+    for (name = shortnames; name->id; name++) {
+        if (pci->device == name->id) {
+            strcpy(card->shortname, name->s);
+            break;
+        }
+    }
+    //	card->dev = &pci->dev;
+    if (buggy_irq < 0) {
+        /* some Nforce[2] and ICH boards have problems with IRQ handling.
+         * Needs to return IRQ_HANDLED for unknown irqs.
+         */
+        if (pci_id->driver_data == DEVICE_NFORCE)
+            buggy_irq = 1;
+        else
+            buggy_irq = 0;
+    }
+
+    if ((err = snd_intel8x0_create(card, pci, pci_id->driver_data, &chip)) < 0) {
+        snd_card_free(card);
+        printk(KERN_ERR "ICH: create error. err = %x\n",err);
+        return err;
+    }
+
+    card->private_data = chip;
+
+    if ((err = snd_intel8x0_mixer(chip, ac97_clock[dev], ac97_quirk[dev])) < 0){
+        snd_card_free(card);
+        //            printk(KERN_ERR "ICH: mixer error. err = %x\n",err);
+        return err;
+    }
+#ifdef DEBUG
+    dprintf(("snd_intel8x0_mixer passed"));
+#endif
+    if ((err = snd_intel8x0_pcm(chip)) < 0) {
+        snd_card_free(card);
+        printk(KERN_ERR "ICH: pcm error. err = %x\n",err);
+        return err;
+    }
+
+#ifdef DEBUG
+    dprintf(("snd_intel8x0_pcm passed"));
+#endif
+    if (mpu_port[dev] == 0x300 || mpu_port[dev] == 0x330) {
+        if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_INTEL8X0,
+                                       mpu_port[dev], 0,
+                                       -1, 0, &chip->rmidi)) < 0) {
+            printk(KERN_ERR "intel8x0: no UART401 device at 0x%x, skipping.\n", mpu_port[dev]);
+            mpu_port[dev] = 0;
+        }
+    } else
+        mpu_port[dev] = 0;
+
+    snd_intel8x0_proc_init(chip);
+#ifdef DEBUG
+    dprintf(("snd_intel8x0_proc_init passed"));
+#endif
+
+    sprintf(card->longname, "%s at 0x%lx, irq %i",
+            card->shortname, chip->addr, chip->irq);
+
+    if (! ac97_clock[dev])
+        intel8x0_measure_ac97_clock(chip);
+#ifdef DEBUG
+    dprintf(("intel8x0_measure_ac97_clock passed"));
+#endif
+
+    if ((err = snd_card_register(card)) < 0) {
+        snd_card_free(card);
+        printk(KERN_ERR "ICH: card register error. err = %x\n",err);
+        return err;
+    }
+#ifdef DEBUG
+    dprintf(("snd_card_register passed"));
+#endif
+    pci_set_drvdata(pci, card);
+    dev++;
+    return 0;
+}
+
+static void __devexit snd_intel8x0_remove(struct pci_dev *pci)
+{
+    snd_card_free(pci_get_drvdata(pci));
+    pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver driver = {
+	.name = "Intel ICH",
+	.id_table = snd_intel8x0_ids,
+	.probe = snd_intel8x0_probe,
+        .remove = snd_intel8x0_remove,
+#ifdef CONFIG_PM
+        .suspend = intel8x0_suspend,
+        .resume = intel8x0_resume,
+#endif
+};
+
+#if defined(SUPPORT_JOYSTICK) || defined(SUPPORT_MIDI)
+/*
+ * initialize joystick/midi addresses
+ */
+#ifdef SUPPORT_JOYSTICK
+/* there is only one available device, so we keep it here */
+static struct pci_dev *ich_gameport_pci;
+static struct gameport ich_gameport = { .io = 0x200 };
+#endif
+
+
+static int __devinit snd_intel8x0_joystick_probe(struct pci_dev *pci,
+                                                 const struct pci_device_id *id)
+{
+    u16 val;
+    static int dev;
+    if (dev >= SNDRV_CARDS)
+        return -ENODEV;
+    if (!enable[dev]) {
+        dev++;
+        return -ENOENT;
+    }
+
+    pci_read_config_word(pci, 0xe6, &val);
+#ifdef SUPPORT_JOYSTICK
+    if (joystick[dev]) {
+        if (! request_region(ich_gameport.io, 8, "ICH gameport")) {
+            printk(KERN_WARNING "intel8x0: cannot grab gameport 0x%x\n",  ich_gameport.io);
+            joystick[dev] = 0;
+        } else {
+            ich_gameport_pci = pci;
+            gameport_register_port(&ich_gameport);
+            val |= 0x100;
+        }
+    }
+#endif
+#ifdef SUPPORT_MIDI
+    if (mpu_port[dev] > 0) {
+        if (mpu_port[dev] == 0x300 || mpu_port[dev] == 0x330) {
+            u8 b;
+            val |= 0x20;
+            pci_read_config_byte(pci, 0xe2, &b);
+            if (mpu_port[dev] == 0x300)
+                b |= 0x08;
+            else
+                b &= ~0x08;
+            pci_write_config_byte(pci, 0xe2, b);
+        }
+    }
+#endif
+    pci_write_config_word(pci, 0xe6, val);
+    return 0;
+}
+
+static void __devexit snd_intel8x0_joystick_remove(struct pci_dev *pci)
+{
+    u16 val;
+#ifdef SUPPORT_JOYSTICK
+    if (ich_gameport_pci == pci) {
+        gameport_unregister_port(&ich_gameport);
+        release_region(ich_gameport.io, 8);
+        ich_gameport_pci = NULL;
+    }
+#endif
+    /* disable joystick and MIDI */
+    pci_read_config_word(pci, 0xe6, &val);
+    val &= ~0x120;
+    pci_write_config_word(pci, 0xe6, val);
+}
+
+static struct pci_device_id snd_intel8x0_joystick_ids[] = {
+    { 0x8086, 0x2410, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },	/* 82801AA */
+    { 0x8086, 0x2420, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },	/* 82901AB */
+    { 0x8086, 0x2440, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* ICH2 */
+    { 0x8086, 0x244c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* ICH2M */
+    { 0x8086, 0x248c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },	/* ICH3 */
+    // { 0x8086, 0x7195, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },	/* 440MX */
+    // { 0x1039, 0x7012, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },	/* SI7012 */
+    //Rudi: this is not a joystick !!!!
+    //  { 0x10de, 0x01b2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },	/* NFORCE */
+    //	{ 0x10de, 0x006b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },	/* NFORCE2 */
+    { 0x10de, 0x00db, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },	/* NFORCE3 */
+    { 0, }
+};
+
+static struct pci_driver joystick_driver = {
+    0, 0, 0,
+    /*	name:     */ "Intel ICH Joystick",
+    /*	id_table: */ snd_intel8x0_joystick_ids,
+    /*	probe:    */ snd_intel8x0_joystick_probe,
+    0,0,0
+};
+static int have_joystick;
+#endif
+
+static int __init alsa_card_intel8x0_init(void)
+{
+    int err = 0;
+
+    //    printk(KERN_ERR "alsa_card_intel8x0_init\n");
+#ifdef TARGET_OS2
+    if (midi_port > 0) mpu_port[0] = midi_port;
+#endif
+
+    if ((err = pci_module_init(&driver)) < 0) {
+#ifdef MODULE
+        //     printk(KERN_ERR "Intel ICH soundcard not found or device busy. err = %x\n",err);
+#endif
+        return err;
+    }
+#if defined(SUPPORT_JOYSTICK) || defined(SUPPORT_MIDI)
+    if (pci_module_init(&joystick_driver) < 0) {
+        snd_printdd(KERN_INFO "no joystick found\n");
+        have_joystick = 0;
+    } else {
+        snd_printdd(KERN_INFO "joystick(s) found\n");
+        have_joystick = 1;
+    }
+#endif
+    return 0;
+}
+
+static void __exit alsa_card_intel8x0_exit(void)
+{
+    pci_unregister_driver(&driver);
+#if defined(SUPPORT_JOYSTICK) || defined(SUPPORT_MIDI)
+    if (have_joystick)
+        pci_unregister_driver(&joystick_driver);
+#endif
+}
+
+module_init(alsa_card_intel8x0_init)
+module_exit(alsa_card_intel8x0_exit)
+
+#ifndef MODULE
+
+/* format is: snd-intel8x0=enable,index,id,ac97_clock,mpu_port,joystick */
+
+static int __init alsa_card_intel8x0_setup(char *str)
+{
+    static unsigned __initdata nr_dev = 0;
+
+    if (nr_dev >= SNDRV_CARDS)
+        return 0;
+    (void)(get_option(&str,&enable[nr_dev]) == 2 &&
+           get_option(&str,&index[nr_dev]) == 2 &&
+           get_id(&str,&id[nr_dev]) == 2 &&
+           get_option(&str,&ac97_clock[nr_dev]) == 2 &&
+           get_option(&str,&ac97_quirk[nr_dev]) == 2
+#ifdef SUPPORT_MIDI
+           && get_option(&str,&mpu_port[nr_dev]) == 2
+#endif
+#ifdef SUPPORT_JOYSTICK
+           && get_option(&str,&joystick[nr_dev]) == 2
+#endif
+          );
+    nr_dev++;
+    return 1;
+}
+
+__setup("snd-intel8x0=", alsa_card_intel8x0_setup);
+
+#endif /* ifndef MODULE */
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/korg1212/korg1212-firmware.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/korg1212/korg1212-firmware.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/korg1212/korg1212-firmware.h	(revision 91)
@@ -0,0 +1,987 @@
+static char dspCode [] = {
+0x01,0xff,0x18,0xff,0xf5,0xff,0xcf,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x26,0xff,0x18,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x38,0xff,0x18,0xff,0xff,0xff,0xdf,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x03,0xff,0x3c,0xff,0xff,0xff,0xfc,0xff,0x67,0xff,0x40,0xff,0xff,0xff,0xc0,0xff,
+0xff,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,0x00,0xff,0x0c,0xff,0xff,0xff,0x0c,0xff,
+0x0c,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x30,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,
+0x0f,0xff,0x40,0xff,0xff,0xff,0xf4,0xff,0x47,0xff,0x80,0xff,0xff,0xff,0x0a,0xff,
+0x82,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0x7a,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
+0x72,0xff,0x1c,0xff,0xff,0xff,0x5f,0xff,0x02,0xff,0x40,0xff,0xff,0xff,0x40,0xff,
+0x11,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,
+0x8b,0xff,0x93,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x86,0xff,0x93,0xff,0xff,0xff,0x70,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x30,0xff,
+0x8d,0xff,0x93,0xff,0xff,0xff,0x40,0xff,0x02,0xff,0x91,0xff,0xff,0xff,0x80,0xff,
+0x02,0xff,0x91,0xff,0xff,0xff,0x90,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0xc0,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0xd0,0xff,
+0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x40,0xff,
+0xff,0xff,0x47,0xff,0xff,0xff,0xf0,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,
+0x00,0xff,0x34,0xff,0xff,0xff,0x17,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x17,0xff,
+0x80,0xff,0x37,0xff,0xff,0xff,0x02,0xff,0x84,0xff,0x3b,0xff,0xff,0xff,0x02,0xff,
+0x02,0xff,0x34,0xff,0xff,0xff,0x4a,0xff,0x02,0xff,0x38,0xff,0xff,0xff,0x4a,0xff,
+0x01,0xff,0x34,0xff,0xff,0xff,0x2b,0xff,0x01,0xff,0x38,0xff,0xff,0xff,0x2b,0xff,
+0x80,0xff,0x43,0xff,0xff,0xff,0x00,0xff,0x82,0xff,0x93,0xff,0xff,0xff,0x50,0xff,
+0x81,0xff,0x43,0xff,0xff,0xff,0x20,0xff,0x82,0xff,0x93,0xff,0xff,0xff,0x60,0xff,
+0x84,0xff,0x43,0xff,0xff,0xff,0x00,0xff,0x82,0xff,0x93,0xff,0xff,0xff,0x70,0xff,
+0x85,0xff,0x43,0xff,0xff,0xff,0x20,0xff,0x83,0xff,0x93,0xff,0xff,0xff,0xc0,0xff,
+0x82,0xff,0x37,0xff,0xff,0xff,0x81,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x89,0xff,
+0x88,0xff,0x43,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x07,0xff,
+0x82,0xff,0x83,0xff,0xff,0xff,0x60,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x07,0xff,
+0x8c,0xff,0x43,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x07,0xff,
+0x83,0xff,0x83,0xff,0xff,0xff,0xc0,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x07,0xff,
+0x8a,0xff,0x43,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x07,0xff,
+0x82,0xff,0x83,0xff,0xff,0xff,0x50,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x07,0xff,
+0x8e,0xff,0x43,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x07,0xff,
+0x82,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x07,0xff,
+0x83,0xff,0x37,0xff,0xff,0xff,0x01,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x89,0xff,
+0x00,0xff,0x34,0xff,0xff,0xff,0x26,0xff,0x30,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x26,0xff,0x20,0xff,0x40,0xff,0xff,0xff,0x04,0xff,
+0x80,0xff,0x41,0xff,0xff,0xff,0x02,0xff,0xe0,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0xb6,0xff,0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x62,0xff,0x6a,0xff,0xff,0xff,0xa6,0xff,0x62,0xff,0x6a,0xff,0xff,0xff,0xa6,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0xa6,0xff,0x00,0xff,0x09,0xff,0xff,0xff,0x07,0xff,
+0x40,0xff,0x41,0xff,0xff,0xff,0x02,0xff,0xe0,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0xb6,0xff,0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x62,0xff,0x6a,0xff,0xff,0xff,0xa6,0xff,0x62,0xff,0x6a,0xff,0xff,0xff,0xa6,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0xa6,0xff,0x05,0xff,0x41,0xff,0xff,0xff,0x02,0xff,
+0xe0,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0xbb,0xff,
+0x02,0xff,0x41,0xff,0xff,0xff,0x82,0xff,0xe0,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,
+0x8b,0xff,0x93,0xff,0xff,0xff,0xcb,0xff,0x05,0xff,0x41,0xff,0xff,0xff,0xe2,0xff,
+0xe0,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0xdb,0xff,
+0x20,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x30,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x26,0xff,0x00,0xff,0x41,0xff,0xff,0xff,0x02,0xff,
+0xe0,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,0x83,0xff,0x93,0xff,0xff,0xff,0x82,0xff,
+0x83,0xff,0x93,0xff,0xff,0xff,0x9b,0xff,0x03,0xff,0x41,0xff,0xff,0xff,0x02,0xff,
+0xe0,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,0x83,0xff,0x93,0xff,0xff,0xff,0xa2,0xff,
+0x83,0xff,0x93,0xff,0xff,0xff,0xbb,0xff,0x20,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,0x44,0xff,0x90,0xff,0xff,0xff,0x60,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x21,0xff,0x40,0xff,0xff,0xff,0x60,0xff,0x40,0xff,0x90,0xff,0xff,0xff,0x20,0xff,
+0x02,0xff,0x35,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,
+0x00,0xff,0x3c,0xff,0xff,0xff,0x85,0xff,0x0a,0xff,0x14,0xff,0xff,0xff,0xae,0xff,
+0x00,0xff,0xa0,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x35,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,0x02,0xff,0x3c,0xff,0xff,0xff,0x05,0xff,
+0x0a,0xff,0x14,0xff,0xff,0xff,0xfe,0xff,0x00,0xff,0xa0,0xff,0xff,0xff,0x03,0xff,
+0x03,0xff,0x35,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,
+0x02,0xff,0x3c,0xff,0xff,0xff,0x05,0xff,0x0b,0xff,0x14,0xff,0xff,0xff,0x4e,0xff,
+0x00,0xff,0xa0,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x35,0xff,0xff,0xff,0x01,0xff,
+0x78,0xff,0x1c,0xff,0xff,0xff,0x5f,0xff,0x03,0xff,0x35,0xff,0xff,0xff,0x01,0xff,
+0x78,0xff,0x1c,0xff,0xff,0xff,0x5f,0xff,0x5b,0xff,0x40,0xff,0xff,0xff,0xf0,0xff,
+0xff,0xff,0x93,0xff,0xff,0xff,0x30,0xff,0x80,0xff,0x42,0xff,0xff,0xff,0x70,0xff,
+0xff,0xff,0x93,0xff,0xff,0xff,0x60,0xff,0xdf,0xff,0x40,0xff,0xff,0xff,0xf0,0xff,
+0xfe,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,0x80,0xff,0x42,0xff,0xff,0xff,0x70,0xff,
+0xff,0xff,0x93,0xff,0xff,0xff,0x20,0xff,0xc1,0xff,0x41,0xff,0xff,0xff,0x80,0xff,
+0xff,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,0x03,0xff,0x3c,0xff,0xff,0xff,0xfc,0xff,
+0x00,0xff,0x3c,0xff,0xff,0xff,0x04,0xff,0x02,0xff,0x3c,0xff,0xff,0xff,0x23,0xff,
+0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x20,0xff,
+0x59,0xff,0x18,0xff,0xff,0xff,0xdf,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,
+0x8b,0xff,0x93,0xff,0xff,0xff,0x20,0xff,0x18,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x0c,0xff,0x14,0xff,0xff,0xff,0xe4,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0x24,0xff,
+0x00,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x0d,0xff,0x18,0xff,0xff,0xff,0x0f,0xff,
+0x8b,0xff,0x83,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x14,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x10,0xff,0x18,0xff,0xff,0xff,0xd0,0xff,
+0x8b,0xff,0x83,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x24,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x10,0xff,0x18,0xff,0xff,0xff,0x30,0xff,
+0x8b,0xff,0x83,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x44,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x22,0xff,0x18,0xff,0xff,0xff,0x90,0xff,
+0x8b,0xff,0x83,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x84,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x22,0xff,0x18,0xff,0xff,0xff,0x90,0xff,
+0x0c,0xff,0x18,0xff,0xff,0xff,0x6f,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x86,0xff,0x93,0xff,0xff,0xff,0x70,0xff,0x76,0xff,0x1c,0xff,0xff,0xff,0x9f,0xff,
+0x86,0xff,0x83,0xff,0xff,0xff,0x50,0xff,0x86,0xff,0x83,0xff,0xff,0xff,0x64,0xff,
+0x60,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x74,0xff,0x18,0xff,0xff,0xff,0x81,0xff,
+0x00,0xff,0x35,0xff,0xff,0xff,0x00,0xff,0x60,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff,
+0x61,0xff,0x1c,0xff,0xff,0xff,0xaf,0xff,0x77,0xff,0x1c,0xff,0xff,0xff,0xaf,0xff,
+0x63,0xff,0x1c,0xff,0xff,0xff,0x4f,0xff,0x05,0xff,0x35,0xff,0xff,0xff,0x00,0xff,
+0x92,0xff,0x3b,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,
+0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x3c,0xff,0xff,0xff,0x65,0xff,
+0x0f,0xff,0x14,0xff,0xff,0xff,0x6e,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x78,0xff,0xff,0xff,0x13,0xff,
+0x00,0xff,0x78,0xff,0xff,0xff,0x03,0xff,0x05,0xff,0x35,0xff,0xff,0xff,0xe0,0xff,
+0x7f,0xff,0x38,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,
+0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x3c,0xff,0xff,0xff,0x65,0xff,
+0x10,0xff,0x14,0xff,0xff,0xff,0x0e,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0x13,0xff,
+0x00,0xff,0x58,0xff,0xff,0xff,0x03,0xff,0x79,0xff,0x1c,0xff,0xff,0xff,0xff,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x0e,0xff,0x1c,0xff,0xff,0xff,0x1f,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0xe0,0xff,0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x15,0xff,0x1c,0xff,0xff,0xff,0x85,0xff,0x75,0xff,0x1c,0xff,0xff,0xff,0x8f,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x40,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x80,0xff,
+0x02,0xff,0x40,0xff,0xff,0xff,0x60,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0x20,0xff,
+0x16,0xff,0x18,0xff,0xff,0xff,0x1f,0xff,0x0e,0xff,0x1c,0xff,0xff,0xff,0x1f,0xff,
+0x75,0xff,0x1c,0xff,0xff,0xff,0x8f,0xff,0x80,0xff,0x35,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x40,0xff,0x3c,0xff,0xff,0xff,0x05,0xff,0x11,0xff,0x14,0xff,0xff,0xff,0x4e,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0x03,0xff,0x87,0xff,0x83,0xff,0xff,0xff,0xf0,0xff,
+0x86,0xff,0x93,0xff,0xff,0xff,0x80,0xff,0x90,0xff,0x37,0xff,0xff,0xff,0x00,0xff,
+0x02,0xff,0x34,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,
+0x89,0xff,0x93,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,
+0x89,0xff,0x93,0xff,0xff,0xff,0x30,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,
+0x89,0xff,0x93,0xff,0xff,0xff,0x40,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,
+0x89,0xff,0x93,0xff,0xff,0xff,0x50,0xff,0x86,0xff,0x97,0xff,0xff,0xff,0x90,0xff,
+0x03,0xff,0x35,0xff,0xff,0xff,0x00,0xff,0x60,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff,
+0x63,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x8d,0xff,0x93,0xff,0xff,0xff,0x60,0xff,0x82,0xff,0x93,0xff,0xff,0xff,0x40,0xff,
+0x86,0xff,0x93,0xff,0xff,0xff,0xa0,0xff,0x83,0xff,0x37,0xff,0xff,0xff,0x80,0xff,
+0x75,0xff,0x1c,0xff,0xff,0xff,0x1f,0xff,0x83,0xff,0x43,0xff,0xff,0xff,0x00,0xff,
+0x87,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,0x6a,0xff,0x1c,0xff,0xff,0xff,0x0f,0xff,
+0x40,0xff,0x41,0xff,0xff,0xff,0x00,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x90,0xff,
+0x80,0xff,0x41,0xff,0xff,0xff,0x00,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0xa0,0xff,
+0x8b,0xff,0x87,0xff,0xff,0xff,0x90,0xff,0x7e,0xff,0x38,0xff,0xff,0xff,0x00,0xff,
+0x40,0xff,0x34,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff,
+0x00,0xff,0x3c,0xff,0xff,0xff,0x55,0xff,0x13,0xff,0x14,0xff,0xff,0xff,0xbe,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff,
+0x00,0xff,0x58,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0x03,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff,
+0x00,0xff,0x58,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0x03,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff,
+0x00,0xff,0x58,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0x03,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,
+0x8b,0xff,0x97,0xff,0xff,0xff,0x90,0xff,0x05,0xff,0x41,0xff,0xff,0xff,0x00,0xff,
+0x92,0xff,0x43,0xff,0xff,0xff,0x01,0xff,0x86,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,
+0x86,0xff,0x93,0xff,0xff,0xff,0xe1,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0xe0,0xff,
+0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x15,0xff,0x1c,0xff,0xff,0xff,0x85,0xff,
+0x75,0xff,0x1c,0xff,0xff,0xff,0x8f,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x40,0xff,
+0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x53,0xff,0x18,0xff,0xff,0xff,0xb4,0xff,
+0x72,0xff,0x1c,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x8b,0xff,0x93,0xff,0xff,0xff,0x30,0xff,0x02,0xff,0x40,0xff,0xff,0xff,0x60,0xff,
+0x11,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x16,0xff,0x18,0xff,0xff,0xff,0x4f,0xff,
+0x38,0xff,0x42,0xff,0xff,0xff,0x50,0xff,0x48,0xff,0x90,0xff,0xff,0xff,0xa0,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x30,0xff,0x40,0xff,0xff,0xff,0x00,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x50,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x1e,0xff,0x1c,0xff,0xff,0xff,0x0f,0xff,
+0x20,0xff,0x1c,0xff,0xff,0xff,0xcf,0xff,0x16,0xff,0x18,0xff,0xff,0xff,0x1f,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x70,0xff,
+0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x6a,0xff,0x1c,0xff,0xff,0xff,0xbf,0xff,
+0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff,0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,
+0x67,0xff,0x1c,0xff,0xff,0xff,0x3f,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff,
+0x08,0xff,0x40,0xff,0xff,0xff,0x00,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x70,0xff,
+0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x69,0xff,0x1c,0xff,0xff,0xff,0x0f,0xff,
+0x5d,0xff,0x1c,0xff,0xff,0xff,0x2f,0xff,0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,
+0x79,0xff,0x1c,0xff,0xff,0xff,0x1f,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff,
+0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff,
+0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff,
+0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5d,0xff,0x1c,0xff,0xff,0xff,0x2f,0xff,
+0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff,
+0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff,
+0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff,
+0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5d,0xff,0x1c,0xff,0xff,0xff,0x2f,0xff,
+0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff,
+0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff,
+0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff,
+0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x5d,0xff,0x1c,0xff,0xff,0xff,0x2f,0xff,
+0x18,0xff,0x1c,0xff,0xff,0xff,0xef,0xff,0x66,0xff,0x1c,0xff,0xff,0xff,0x1f,0xff,
+0x5c,0xff,0x1c,0xff,0xff,0xff,0x7f,0xff,0x16,0xff,0x18,0xff,0xff,0xff,0x4f,0xff,
+0x8b,0xff,0x87,0xff,0xff,0xff,0x61,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x89,0xff,
+0x00,0xff,0x34,0xff,0xff,0xff,0x26,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x06,0xff,
+0x83,0xff,0x93,0xff,0xff,0xff,0xd0,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x06,0xff,
+0x83,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,0x38,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x19,0xff,0x14,0xff,0xff,0xff,0x85,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0x50,0xff,
+0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x07,0xff,
+0x04,0xff,0x0d,0xff,0xff,0xff,0x30,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x07,0xff,
+0x83,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x07,0xff,
+0x08,0xff,0x0d,0xff,0xff,0xff,0x30,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x07,0xff,
+0x86,0xff,0x93,0xff,0xff,0xff,0x40,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x01,0xff,
+0x8b,0xff,0x93,0xff,0xff,0xff,0x51,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x46,0xff,
+0x00,0xff,0x09,0xff,0xff,0xff,0x06,0xff,0x8b,0xff,0x97,0xff,0xff,0xff,0x61,0xff,
+0x83,0xff,0x8b,0xff,0xff,0xff,0xd0,0xff,0x83,0xff,0x8b,0xff,0xff,0xff,0xe1,0xff,
+0x87,0xff,0x37,0xff,0xff,0xff,0x01,0xff,0x6e,0xff,0x1c,0xff,0xff,0xff,0xbf,0xff,
+0x87,0xff,0x37,0xff,0xff,0xff,0x00,0xff,0x92,0xff,0x37,0xff,0xff,0xff,0x01,0xff,
+0x7f,0xff,0x38,0xff,0xff,0xff,0x00,0xff,0x7e,0xff,0x38,0xff,0xff,0xff,0x01,0xff,
+0x23,0xff,0x1c,0xff,0xff,0xff,0xff,0xff,0x7e,0xff,0x38,0xff,0xff,0xff,0x00,0xff,
+0x83,0xff,0x87,0xff,0xff,0xff,0xf1,0xff,0x86,0xff,0x8b,0xff,0xff,0xff,0x41,0xff,
+0x6c,0xff,0x1c,0xff,0xff,0xff,0x2f,0xff,0x87,0xff,0x37,0xff,0xff,0xff,0x00,0xff,
+0x8b,0xff,0x8b,0xff,0xff,0xff,0xa0,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,
+0x40,0xff,0x38,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x3c,0xff,0xff,0xff,0x55,0xff,
+0x1b,0xff,0x14,0xff,0xff,0xff,0xce,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x78,0xff,0xff,0xff,0x13,0xff,
+0x00,0xff,0x78,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x78,0xff,0xff,0xff,0x13,0xff,
+0x00,0xff,0x78,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x78,0xff,0xff,0xff,0x13,0xff,
+0x00,0xff,0x78,0xff,0xff,0xff,0x03,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0xe1,0xff,
+0x8b,0xff,0x83,0xff,0xff,0xff,0xf0,0xff,0x00,0xff,0x78,0xff,0xff,0xff,0x13,0xff,
+0x00,0xff,0x78,0xff,0xff,0xff,0x03,0xff,0x8b,0xff,0x9b,0xff,0xff,0xff,0xa0,0xff,
+0x8b,0xff,0x87,0xff,0xff,0xff,0x90,0xff,0x7e,0xff,0x38,0xff,0xff,0xff,0x00,0xff,
+0x40,0xff,0x34,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff,
+0x00,0xff,0x3c,0xff,0xff,0xff,0x55,0xff,0x1d,0xff,0x14,0xff,0xff,0xff,0x3e,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff,
+0x00,0xff,0x58,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0x03,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff,
+0x00,0xff,0x58,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0x03,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff,
+0x00,0xff,0x58,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0x03,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,
+0x8b,0xff,0x97,0xff,0xff,0xff,0x90,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
+0x8b,0xff,0x87,0xff,0xff,0xff,0x61,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x89,0xff,
+0x00,0xff,0x34,0xff,0xff,0xff,0x26,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x06,0xff,
+0x83,0xff,0x93,0xff,0xff,0xff,0xd0,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x06,0xff,
+0x83,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0x51,0xff,
+0x79,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x74,0xff,0x18,0xff,0xff,0xff,0xb4,0xff,
+0x38,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x1e,0xff,0x14,0xff,0xff,0xff,0xd5,0xff,
+0x8b,0xff,0x83,0xff,0xff,0xff,0x50,0xff,0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x07,0xff,0x04,0xff,0x0d,0xff,0xff,0xff,0x30,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x07,0xff,0x83,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x07,0xff,0x08,0xff,0x0d,0xff,0xff,0xff,0x30,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x07,0xff,0x86,0xff,0x93,0xff,0xff,0xff,0x40,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x01,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x51,0xff,
+0x00,0xff,0x34,0xff,0xff,0xff,0x46,0xff,0x00,0xff,0x09,0xff,0xff,0xff,0x06,0xff,
+0x8b,0xff,0x97,0xff,0xff,0xff,0x61,0xff,0x83,0xff,0x8b,0xff,0xff,0xff,0xd0,0xff,
+0x83,0xff,0x8b,0xff,0xff,0xff,0xe1,0xff,0x87,0xff,0x37,0xff,0xff,0xff,0x01,0xff,
+0x6e,0xff,0x1c,0xff,0xff,0xff,0xbf,0xff,0x87,0xff,0x37,0xff,0xff,0xff,0x00,0xff,
+0x92,0xff,0x37,0xff,0xff,0xff,0x01,0xff,0x7f,0xff,0x38,0xff,0xff,0xff,0x00,0xff,
+0x23,0xff,0x1c,0xff,0xff,0xff,0xff,0xff,0x7e,0xff,0x38,0xff,0xff,0xff,0x00,0xff,
+0x83,0xff,0x87,0xff,0xff,0xff,0xf1,0xff,0x86,0xff,0x8b,0xff,0xff,0xff,0x41,0xff,
+0x6c,0xff,0x1c,0xff,0xff,0xff,0x2f,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
+0x8d,0xff,0x8f,0xff,0xff,0xff,0xc5,0xff,0x20,0xff,0x14,0xff,0xff,0xff,0xae,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
+0x8b,0xff,0x83,0xff,0xff,0xff,0x84,0xff,0x00,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x8b,0xff,0x93,0xff,0xff,0xff,0x8a,0xff,0x64,0xff,0x1c,0xff,0xff,0xff,0xe0,0xff,
+0x7e,0xff,0x38,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff,
+0x00,0xff,0x3c,0xff,0xff,0xff,0xe5,0xff,0x21,0xff,0x14,0xff,0xff,0xff,0x5e,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0x03,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x08,0xff,0x40,0xff,0xff,0xff,0x10,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x02,0xff,0x40,0xff,0xff,0xff,0x40,0xff,
+0x11,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x78,0xff,0x42,0xff,0xff,0xff,0x50,0xff,
+0x48,0xff,0x90,0xff,0xff,0xff,0xa0,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0xb0,0xff,0x40,0xff,0xff,0xff,0x00,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x50,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0x40,0xff,
+0x8d,0xff,0x93,0xff,0xff,0xff,0x50,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x01,0xff,
+0x8b,0xff,0x93,0xff,0xff,0xff,0x51,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x70,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0xd0,0xff,
+0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x0c,0xff,0x18,0xff,0xff,0xff,0x90,0xff,
+0x0c,0xff,0x18,0xff,0xff,0xff,0x6f,0xff,0x20,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x34,0xff,0xff,0xff,0x09,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,
+0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x09,0xff,
+0x00,0xff,0x38,0xff,0xff,0xff,0x06,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x26,0xff,
+0x98,0xff,0xcc,0xff,0xff,0xff,0x37,0xff,0x00,0xff,0x3c,0xff,0xff,0xff,0xa5,0xff,
+0x24,0xff,0x14,0xff,0xff,0xff,0xfe,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x73,0xff,
+0x08,0xff,0x0d,0xff,0xff,0xff,0x14,0xff,0x98,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x50,0xff,0xff,0xff,0xc6,0xff,0x69,0xff,0xcc,0xff,0xff,0xff,0x37,0xff,
+0x00,0xff,0x05,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0xc6,0xff,
+0x98,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x72,0xff,
+0x08,0xff,0x0d,0xff,0xff,0xff,0x14,0xff,0x00,0xff,0x50,0xff,0xff,0xff,0xc6,0xff,
+0x69,0xff,0xcc,0xff,0xff,0xff,0x37,0xff,0x00,0xff,0x05,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x58,0xff,0xff,0xff,0xc6,0xff,0x98,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x73,0xff,0x08,0xff,0x0d,0xff,0xff,0xff,0x14,0xff,
+0x00,0xff,0x50,0xff,0xff,0xff,0xc6,0xff,0x69,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x05,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0xc6,0xff,
+0x30,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x0c,0xff,0xff,0xff,0x30,0xff,0x47,0xff,0x80,0xff,0xff,0xff,0x58,0xff,
+0x10,0xff,0x0f,0xff,0xff,0xff,0x01,0xff,0x66,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x26,0xff,0x18,0xff,0xff,0xff,0x94,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,
+0x8b,0xff,0x93,0xff,0xff,0xff,0x40,0xff,0x80,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x49,0xff,0x90,0xff,0xff,0xff,0x40,0xff,0x16,0xff,0x0f,0xff,0xff,0xff,0x02,0xff,
+0x66,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x38,0xff,0x18,0xff,0xff,0xff,0xb4,0xff,
+0x0f,0xff,0x40,0xff,0xff,0xff,0xf4,0xff,0x47,0xff,0x80,0xff,0xff,0xff,0x0a,0xff,
+0x82,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0x7a,0xff,
+0x7a,0xff,0x26,0xff,0xff,0xff,0x0f,0xff,0x10,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,
+0x38,0xff,0x18,0xff,0xff,0xff,0xb4,0xff,0x27,0xff,0x18,0xff,0xff,0xff,0xd2,0xff,
+0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x30,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x10,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,
+0x29,0xff,0x18,0xff,0xff,0xff,0x92,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x00,0xff,
+0x8b,0xff,0x93,0xff,0xff,0xff,0x20,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
+0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
+0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x1c,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,
+0x00,0xff,0x04,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x04,0xff,0xff,0xff,0x03,0xff,
+0x0d,0xff,0x18,0xff,0xff,0xff,0x0f,0xff,0x10,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,
+0x31,0xff,0x18,0xff,0xff,0xff,0x12,0xff,0x30,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,
+0x08,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x4f,0xff,0xff,0xff,0x89,0xff,
+0x90,0xff,0x37,0xff,0xff,0xff,0x00,0xff,0x02,0xff,0x34,0xff,0xff,0xff,0x08,0xff,
+0x00,0xff,0x34,0xff,0xff,0xff,0x34,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x55,0xff,
+0x46,0xff,0x80,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x0e,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x4e,0xff,0xa7,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0xa3,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,
+0x46,0xff,0x80,0xff,0xff,0xff,0x18,0xff,0x00,0xff,0x0e,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x5e,0xff,0xaf,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,
+0x46,0xff,0x80,0xff,0xff,0xff,0x48,0xff,0x10,0xff,0x0f,0xff,0xff,0xff,0xfe,0xff,
+0x87,0xff,0x93,0xff,0xff,0xff,0xfe,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x2e,0xff,
+0x02,0xff,0x40,0xff,0xff,0xff,0x06,0xff,0xe0,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x01,0xff,0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,
+0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,
+0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,
+0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,
+0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,
+0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,
+0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0xa1,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x28,0xff,
+0x00,0xff,0x0e,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4e,0xff,
+0xa7,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa3,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x38,0xff,
+0x00,0xff,0x0e,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x5e,0xff,
+0xaf,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,
+0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,
+0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,
+0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,
+0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,
+0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,
+0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0xa0,0xff,0x63,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x4a,0xff,0x49,0xff,0x6a,0xff,0xff,0xff,0xa3,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa3,0xff,
+0xff,0xff,0x4f,0xff,0xff,0xff,0xf0,0xff,0x86,0xff,0x93,0xff,0xff,0xff,0x50,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x10,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,
+0x32,0xff,0x18,0xff,0xff,0xff,0x42,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0xe4,0xff,
+0x8b,0xff,0x83,0xff,0xff,0xff,0xf5,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x44,0xff,
+0x08,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0xff,0xff,0x4f,0xff,0xff,0xff,0x89,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x8a,0xff,0x00,0xff,0x0e,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x4e,0xff,0xa7,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x5a,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
+0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
+0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,
+0x10,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,0x35,0xff,0x18,0xff,0xff,0xff,0xd2,0xff,
+0x00,0xff,0x4c,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x93,0xff,0xff,0xff,0x00,0xff,
+0x0b,0xff,0x40,0xff,0xff,0xff,0x80,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0xf0,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x10,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0xe0,0xff,
+0x46,0xff,0x80,0xff,0xff,0xff,0x0a,0xff,0x7a,0xff,0x26,0xff,0xff,0xff,0x0f,0xff,
+0x35,0xff,0x1c,0xff,0xff,0xff,0x24,0xff,0x10,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,
+0x33,0xff,0x18,0xff,0xff,0xff,0xc5,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0xc0,0xff,
+0x11,0xff,0x90,0xff,0xff,0xff,0x60,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0xd0,0xff,
+0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
+0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
+0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,
+0x10,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,0x34,0xff,0x18,0xff,0xff,0xff,0x85,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x40,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0x60,0xff,
+0x8d,0xff,0x93,0xff,0xff,0xff,0xd0,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
+0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
+0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0x60,0xff,
+0x8d,0xff,0x93,0xff,0xff,0xff,0xd0,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
+0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
+0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,
+0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0x80,0xff,
+0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x93,0xff,0xff,0xff,0x00,0xff,
+0x0d,0xff,0x40,0xff,0xff,0xff,0xf0,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0xf0,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x10,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0xe0,0xff,
+0xff,0xff,0x40,0xff,0xff,0xff,0xf0,0xff,0x90,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x10,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,
+0x37,0xff,0x18,0xff,0xff,0xff,0x42,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x00,0xff,
+0x89,0xff,0x93,0xff,0xff,0xff,0xa0,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x10,0xff,
+0x89,0xff,0x93,0xff,0xff,0xff,0xb0,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x20,0xff,
+0x89,0xff,0x93,0xff,0xff,0xff,0xc0,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x30,0xff,
+0x89,0xff,0x93,0xff,0xff,0xff,0xd0,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x40,0xff,
+0x89,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x50,0xff,
+0x89,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x10,0xff,
+0x86,0xff,0x93,0xff,0xff,0xff,0x60,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
+0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
+0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,
+0x10,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,0x39,0xff,0x18,0xff,0xff,0xff,0x22,0xff,
+0x46,0xff,0x80,0xff,0xff,0xff,0x00,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0x20,0xff,
+0x46,0xff,0x80,0xff,0xff,0xff,0x10,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0x30,0xff,
+0x46,0xff,0x80,0xff,0xff,0xff,0x20,0xff,0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x38,0xff,0x1c,0xff,0xff,0xff,0x84,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,
+0x8d,0xff,0x93,0xff,0xff,0xff,0x40,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
+0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
+0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,
+0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0x50,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x00,0xff,0x0c,0xff,0xff,0xff,0x30,0xff,
+0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x50,0xff,
+0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0xf4,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x41,0xff,0x18,0xff,0xff,0xff,0x30,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0xe4,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x42,0xff,0x18,0xff,0xff,0xff,0x40,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0xd4,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x47,0xff,0x18,0xff,0xff,0xff,0xa0,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0xc4,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x18,0xff,0xff,0xff,0xd0,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0xb4,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x48,0xff,0x18,0xff,0xff,0xff,0xe0,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0xa4,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x4a,0xff,0x18,0xff,0xff,0xff,0x60,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0x94,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x4c,0xff,0x18,0xff,0xff,0xff,0x00,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0x84,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x4d,0xff,0x18,0xff,0xff,0xff,0xe0,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0x74,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x4f,0xff,0x18,0xff,0xff,0xff,0x20,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0x64,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x4f,0xff,0x18,0xff,0xff,0xff,0xf0,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0e,0xff,0x40,0xff,0xff,0xff,0xf4,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x44,0xff,0x18,0xff,0xff,0xff,0x40,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0e,0xff,0x40,0xff,0xff,0xff,0xe4,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x45,0xff,0x18,0xff,0xff,0xff,0x50,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0a,0xff,0x40,0xff,0xff,0xff,0x04,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x3d,0xff,0x18,0xff,0xff,0xff,0xd0,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0a,0xff,0x40,0xff,0xff,0xff,0x14,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x3f,0xff,0x18,0xff,0xff,0xff,0x10,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0a,0xff,0x40,0xff,0xff,0xff,0x24,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x3f,0xff,0x18,0xff,0xff,0xff,0x80,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0a,0xff,0x40,0xff,0xff,0xff,0x34,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x3f,0xff,0x18,0xff,0xff,0xff,0xf0,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x0a,0xff,0x40,0xff,0xff,0xff,0x44,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x40,0xff,0x18,0xff,0xff,0xff,0x60,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
+0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
+0x00,0xff,0x0c,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0xff,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x44,0xff,0x90,0xff,0xff,0xff,0x60,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x21,0xff,0x40,0xff,0xff,0xff,0x80,0xff,
+0xff,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
+0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
+0x25,0xff,0x40,0xff,0xff,0xff,0x80,0xff,0xff,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0xe9,0xff,0x41,0xff,0xff,0xff,0x80,0xff,
+0xff,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
+0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
+0xed,0xff,0x41,0xff,0xff,0xff,0x80,0xff,0xff,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x44,0xff,0x90,0xff,0xff,0xff,0x60,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xf1,0xff,0x41,0xff,0xff,0xff,0x80,0xff,
+0xff,0xff,0x93,0xff,0xff,0xff,0xf0,0xff,0x46,0xff,0x84,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x60,0xff,0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
+0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
+0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,
+0x46,0xff,0x84,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,
+0x00,0xff,0x34,0xff,0xff,0xff,0x06,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x10,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0x02,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x22,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x62,0xff,0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
+0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
+0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,
+0x46,0xff,0x88,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff,
+0x00,0xff,0x50,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x60,0xff,
+0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x46,0xff,0x88,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x04,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x46,0xff,0x80,0xff,0xff,0xff,0x10,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0x03,0xff,
+0x00,0xff,0x50,0xff,0xff,0xff,0x23,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x62,0xff,
+0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x00,0xff,
+0xff,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,0xff,0xff,0x83,0xff,0xff,0xff,0xe2,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x62,0xff,0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
+0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
+0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,
+0x03,0xff,0x0d,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x60,0xff,
+0x0c,0xff,0x0d,0xff,0xff,0xff,0xf0,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
+0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
+0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,
+0x46,0xff,0x80,0xff,0xff,0xff,0x02,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x13,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0x60,0xff,
+0x8d,0xff,0x93,0xff,0xff,0xff,0xd0,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0xf2,0xff,
+0x11,0xff,0x90,0xff,0xff,0xff,0xe3,0xff,0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
+0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
+0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,
+0x46,0xff,0x84,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,
+0x00,0xff,0x34,0xff,0xff,0xff,0x06,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x10,0xff,
+0xff,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x22,0xff,
+0x67,0xff,0x40,0xff,0xff,0xff,0x40,0xff,0xff,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x62,0xff,0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
+0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
+0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,
+0x46,0xff,0x84,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,
+0x00,0xff,0x34,0xff,0xff,0xff,0x06,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x10,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x46,0xff,0x80,0xff,0xff,0xff,0x23,0xff,0xff,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0x32,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x72,0xff,
+0x67,0xff,0x40,0xff,0xff,0xff,0x40,0xff,0xff,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x67,0xff,0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
+0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
+0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,
+0x46,0xff,0x80,0xff,0xff,0xff,0x05,0xff,0x03,0xff,0x0d,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x0c,0xff,0x0d,0xff,0xff,0xff,0xf5,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
+0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
+0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,
+0x46,0xff,0x80,0xff,0xff,0xff,0x00,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0xc0,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0xc7,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x67,0xff,
+0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,0x46,0xff,0x80,0xff,0xff,0xff,0x00,0xff,
+0x8d,0xff,0x93,0xff,0xff,0xff,0xe0,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0xe7,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x67,0xff,0xf7,0xff,0x4f,0xff,0xff,0xff,0xf4,0xff,
+0x46,0xff,0x90,0xff,0xff,0xff,0x74,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x70,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x04,0xff,
+0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x46,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
+0x00,0xff,0x0c,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x1f,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0xd0,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x24,0xff,
+0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x14,0xff,0x18,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x51,0xff,0x14,0xff,0xff,0xff,0x81,0xff,0x90,0xff,0x80,0xff,0xff,0xff,0x60,0xff,
+0x80,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0xd0,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x14,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x11,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0x30,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x16,0xff,0x10,0xff,0x40,0xff,0xff,0xff,0x07,0xff,
+0x90,0xff,0x34,0xff,0xff,0xff,0x71,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x09,0xff,
+0x0f,0xff,0x40,0xff,0xff,0xff,0xf5,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x07,0xff,
+0x88,0xff,0x63,0xff,0xff,0xff,0x27,0xff,0xe8,0xff,0x60,0xff,0xff,0xff,0x07,0xff,
+0x62,0xff,0x61,0xff,0xff,0xff,0x27,0xff,0x88,0xff,0x2b,0xff,0xff,0xff,0x8b,0xff,
+0xe8,0xff,0x60,0xff,0xff,0xff,0x07,0xff,0x62,0xff,0x61,0xff,0xff,0xff,0x17,0xff,
+0x88,0xff,0x2f,0xff,0xff,0xff,0xfb,0xff,0x89,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x98,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,0xea,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0xab,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0xb8,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0xcf,0xff,0x62,0xff,0x21,0xff,0xff,0xff,0x0f,0xff,
+0x10,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x62,0xff,0x21,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x24,0xff,0x90,0xff,0x80,0xff,0xff,0xff,0x60,0xff,
+0x80,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x51,0xff,0x18,0xff,0xff,0xff,0x00,0xff,
+0x8b,0xff,0x93,0xff,0xff,0xff,0xeb,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0xfc,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x51,0xff,0x1c,0xff,0xff,0xff,0x0f,0xff,
+0x8d,0xff,0x93,0xff,0xff,0xff,0xac,0xff,0x82,0xff,0x3c,0xff,0xff,0xff,0x45,0xff,
+0x54,0xff,0x14,0xff,0xff,0xff,0x2e,0xff,0xff,0xff,0x3f,0xff,0xff,0xff,0xf5,0xff,
+0x54,0xff,0x14,0xff,0xff,0xff,0x1e,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x51,0xff,0x1c,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x0c,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0xa4,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x53,0xff,0x18,0xff,0xff,0xff,0xb3,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0xd0,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x24,0xff,
+0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0xba,0xff,
+0x8b,0xff,0x83,0xff,0xff,0xff,0xf5,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0x6b,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x14,0xff,0x18,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x55,0xff,0x14,0xff,0xff,0xff,0x21,0xff,0x90,0xff,0x80,0xff,0xff,0xff,0x60,0xff,
+0x80,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x20,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x01,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0xe4,0xff,
+0x8b,0xff,0x83,0xff,0xff,0xff,0xf5,0xff,0x08,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,
+0x60,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x49,0xff,0x2a,0xff,0xff,0xff,0xea,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x4e,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0xee,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0xfa,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x20,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x31,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0xc9,0xff,0x2a,0xff,0xff,0xff,0xea,0xff,
+0x54,0xff,0x18,0xff,0xff,0xff,0xd5,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4e,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x5a,0xff,0x82,0xff,0x4f,0xff,0xff,0xff,0xf0,0xff,
+0xff,0xff,0x4f,0xff,0xff,0xff,0xf1,0xff,0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0xc9,0xff,0x2a,0xff,0xff,0xff,0xea,0xff,0x56,0xff,0x18,0xff,0xff,0xff,0xb4,0xff,
+0x54,0xff,0x18,0xff,0xff,0xff,0xdf,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x20,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x0c,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,
+0x02,0xff,0x40,0xff,0xff,0xff,0x60,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0x20,0xff,
+0x16,0xff,0x18,0xff,0xff,0xff,0x4f,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0xd0,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x24,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x11,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x14,0xff,
+0x18,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x57,0xff,0x14,0xff,0xff,0xff,0x91,0xff,
+0x90,0xff,0x80,0xff,0xff,0xff,0x60,0xff,0x80,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0xd0,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x14,0xff,
+0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0x6a,0xff,
+0x30,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x16,0xff,
+0x10,0xff,0x40,0xff,0xff,0xff,0x07,0xff,0x90,0xff,0x34,0xff,0xff,0xff,0x71,0xff,
+0x00,0xff,0x34,0xff,0xff,0xff,0x09,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0xf5,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x07,0xff,0x88,0xff,0x63,0xff,0xff,0xff,0x27,0xff,
+0xe8,0xff,0x60,0xff,0xff,0xff,0x07,0xff,0x62,0xff,0x61,0xff,0xff,0xff,0x27,0xff,
+0x88,0xff,0x2b,0xff,0xff,0xff,0x8b,0xff,0xe8,0xff,0x60,0xff,0xff,0xff,0x07,0xff,
+0x62,0xff,0x61,0xff,0xff,0xff,0x17,0xff,0x88,0xff,0x2f,0xff,0xff,0xff,0xfb,0xff,
+0x89,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x98,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,
+0xea,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0xab,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0xb8,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0xcf,0xff,
+0x62,0xff,0x21,0xff,0xff,0xff,0x0f,0xff,0x10,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x62,0xff,0x21,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x24,0xff,
+0x90,0xff,0x80,0xff,0xff,0xff,0x60,0xff,0x80,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x57,0xff,0x18,0xff,0xff,0xff,0x10,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0xeb,0xff,
+0x8b,0xff,0x93,0xff,0xff,0xff,0xfc,0xff,0x5c,0xff,0x1c,0xff,0xff,0xff,0x3f,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x57,0xff,0x1c,0xff,0xff,0xff,0x1f,0xff,
+0x8d,0xff,0x93,0xff,0xff,0xff,0xac,0xff,0x82,0xff,0x3c,0xff,0xff,0xff,0x45,0xff,
+0x5a,0xff,0x14,0xff,0xff,0xff,0x4e,0xff,0xff,0xff,0x3f,0xff,0xff,0xff,0xf5,0xff,
+0x5a,0xff,0x14,0xff,0xff,0xff,0x3e,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x57,0xff,0x1c,0xff,0xff,0xff,0x1f,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x0c,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0xa4,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x59,0xff,0x18,0xff,0xff,0xff,0xd3,0xff,
+0x5c,0xff,0x1c,0xff,0xff,0xff,0x3f,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0xd0,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x24,0xff,0xa0,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0xba,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0xf5,0xff,
+0x5c,0xff,0x1c,0xff,0xff,0xff,0x3f,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0x6b,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x14,0xff,0x18,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x5b,0xff,0x14,0xff,0xff,0xff,0x61,0xff,0x90,0xff,0x80,0xff,0xff,0xff,0x60,0xff,
+0x80,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x20,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x01,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0xe4,0xff,
+0x8b,0xff,0x83,0xff,0xff,0xff,0xf5,0xff,0x08,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,
+0x60,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x49,0xff,0x2a,0xff,0xff,0xff,0xea,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x4e,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0xee,0xff,
+0x8b,0xff,0x93,0xff,0xff,0xff,0xfa,0xff,0x5e,0xff,0x1c,0xff,0xff,0xff,0x0f,0xff,
+0x5b,0xff,0x18,0xff,0xff,0xff,0x0f,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0x20,0xff,
+0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x0d,0xff,0x18,0xff,0xff,0xff,0x05,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x08,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x14,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x05,0xff,
+0x8b,0xff,0x83,0xff,0xff,0xff,0xe0,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0xf1,0xff,
+0x60,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x49,0xff,0x2a,0xff,0xff,0xff,0xea,0xff,
+0x8b,0xff,0x93,0xff,0xff,0xff,0xfa,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0xee,0xff,
+0x0c,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
+0x08,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x14,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x05,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0xe0,0xff,
+0x8b,0xff,0x83,0xff,0xff,0xff,0xf1,0xff,0x60,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x49,0xff,0x2a,0xff,0xff,0xff,0xea,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0xfa,0xff,
+0x8b,0xff,0x93,0xff,0xff,0xff,0xee,0xff,0x0c,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x40,0xff,0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x5e,0xff,0x1c,0xff,0xff,0xff,0x04,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
+0x08,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x50,0xff,
+0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x5f,0xff,0x1c,0xff,0xff,0xff,0x54,0xff,
+0x0f,0xff,0x40,0xff,0xff,0xff,0xf5,0xff,0x90,0xff,0x80,0xff,0xff,0xff,0xa8,0xff,
+0x10,0xff,0x0f,0xff,0xff,0xff,0x08,0xff,0x90,0xff,0x80,0xff,0xff,0xff,0x90,0xff,
+0x88,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,0xb6,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,
+0x8b,0xff,0x83,0xff,0xff,0xff,0xfa,0xff,0xf2,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x0a,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x14,0xff,
+0xe2,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x74,0xff,0x18,0xff,0xff,0xff,0xe2,0xff,
+0x38,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x24,0xff,
+0xe2,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x74,0xff,0x18,0xff,0xff,0xff,0xe2,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x0f,0xff,0x40,0xff,0xff,0xff,0xf5,0xff,
+0x90,0xff,0x80,0xff,0xff,0xff,0x80,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0xe2,0xff,
+0x88,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,0x98,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,
+0x10,0xff,0x40,0xff,0xff,0xff,0x07,0xff,0xe8,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0xac,0xff,0xf2,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x0a,0xff,0x01,0xff,0x40,0xff,0xff,0xff,0x04,0xff,
+0xe2,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x74,0xff,0x18,0xff,0xff,0xff,0xe2,0xff,
+0x38,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x02,0xff,0x40,0xff,0xff,0xff,0x04,0xff,
+0xe2,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x74,0xff,0x18,0xff,0xff,0xff,0xe2,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,
+0x00,0xff,0x34,0xff,0xff,0xff,0xd4,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x85,0xff,
+0xff,0xff,0x4f,0xff,0xff,0xff,0x89,0xff,0x00,0xff,0x09,0xff,0xff,0xff,0x01,0xff,
+0x89,0xff,0x83,0xff,0xff,0xff,0xb8,0xff,0x00,0xff,0x0e,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x4e,0xff,0xa7,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0xa3,0xff,0x89,0xff,0x83,0xff,0xff,0xff,0xa8,0xff,
+0x00,0xff,0x0e,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4e,0xff,
+0xa7,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa3,0xff,
+0x00,0xff,0x09,0xff,0xff,0xff,0x03,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0xb0,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
+0x02,0xff,0x35,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,
+0x00,0xff,0x34,0xff,0xff,0xff,0x26,0xff,0x89,0xff,0x83,0xff,0xff,0xff,0xd8,0xff,
+0x00,0xff,0x0e,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x4e,0xff,
+0xa7,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa3,0xff,
+0x89,0xff,0x83,0xff,0xff,0xff,0xc8,0xff,0x00,0xff,0x0e,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x4e,0xff,0xa7,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0xa3,0xff,0x00,0xff,0x09,0xff,0xff,0xff,0x03,0xff,
+0x8b,0xff,0x83,0xff,0xff,0xff,0xd0,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x02,0xff,
+0x01,0xff,0x40,0xff,0xff,0xff,0x80,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x02,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x03,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x10,0xff,
+0x8b,0xff,0x93,0xff,0xff,0xff,0x40,0xff,0x30,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x49,0xff,0x90,0xff,0xff,0xff,0x40,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x60,0xff,0x00,0xff,0x91,0xff,0xff,0xff,0xf0,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x01,0xff,0x42,0xff,0xff,0xff,0x80,0xff,
+0x00,0xff,0x91,0xff,0xff,0xff,0x70,0xff,0x07,0xff,0x42,0xff,0xff,0xff,0x80,0xff,
+0x03,0xff,0x91,0xff,0xff,0xff,0x70,0xff,0x02,0xff,0x42,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x91,0xff,0xff,0xff,0xf0,0xff,0x08,0xff,0x42,0xff,0xff,0xff,0x00,0xff,
+0x03,0xff,0x91,0xff,0xff,0xff,0xf0,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x20,0xff,
+0x01,0xff,0x91,0xff,0xff,0xff,0x70,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x20,0xff,
+0x04,0xff,0x91,0xff,0xff,0xff,0x70,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
+0x04,0xff,0x42,0xff,0xff,0xff,0x00,0xff,0x49,0xff,0x90,0xff,0xff,0xff,0x20,0xff,
+0x62,0xff,0x1c,0xff,0xff,0xff,0xff,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x91,0xff,0xff,0xff,0xf0,0xff,
+0x01,0xff,0x42,0xff,0xff,0xff,0x00,0xff,0x49,0xff,0x90,0xff,0xff,0xff,0x20,0xff,
+0x62,0xff,0x1c,0xff,0xff,0xff,0xff,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x40,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x80,0xff,
+0x05,0xff,0x35,0xff,0xff,0xff,0x00,0xff,0x92,0xff,0x3b,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff,
+0x00,0xff,0x3c,0xff,0xff,0xff,0x65,0xff,0x65,0xff,0x14,0xff,0xff,0xff,0x9e,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff,
+0x00,0xff,0x78,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x78,0xff,0xff,0xff,0x03,0xff,
+0x01,0xff,0x42,0xff,0xff,0xff,0x00,0xff,0x49,0xff,0x90,0xff,0xff,0xff,0x20,0xff,
+0x62,0xff,0x1c,0xff,0xff,0xff,0xff,0xff,0x05,0xff,0x81,0xff,0xff,0xff,0xc0,0xff,
+0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x21,0xff,0x18,0xff,0xff,0xff,0x71,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x49,0xff,0x80,0xff,0xff,0xff,0x48,0xff,
+0x10,0xff,0x0f,0xff,0xff,0xff,0x03,0xff,0x66,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0x74,0xff,0x18,0xff,0xff,0xff,0x54,0xff,0x86,0xff,0x83,0xff,0xff,0xff,0xc0,0xff,
+0x49,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x62,0xff,0x1c,0xff,0xff,0xff,0xff,0xff,
+0x86,0xff,0x83,0xff,0xff,0xff,0x80,0xff,0x01,0xff,0x40,0xff,0xff,0xff,0x04,0xff,
+0xe0,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x86,0xff,0x93,0xff,0xff,0xff,0x8a,0xff,
+0x67,0xff,0x1c,0xff,0xff,0xff,0x00,0xff,0x86,0xff,0x87,0xff,0xff,0xff,0xd0,0xff,
+0x75,0xff,0x1c,0xff,0xff,0xff,0x1f,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,0x82,0xff,0x93,0xff,0xff,0xff,0x40,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x08,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,
+0x86,0xff,0x8b,0xff,0xff,0xff,0xb0,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff,
+0x00,0xff,0x38,0xff,0xff,0xff,0xf4,0xff,0xff,0xff,0x4f,0xff,0xff,0xff,0x89,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x60,0xff,0x89,0xff,0x83,0xff,0xff,0xff,0x44,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x01,0xff,0x89,0xff,0x83,0xff,0xff,0xff,0x55,0xff,
+0x60,0xff,0x26,0xff,0xff,0xff,0x0f,0xff,0x49,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x78,0xff,0xff,0xff,0xa3,0xff,0x10,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x78,0xff,0xff,0xff,0xa0,0xff,0x8d,0xff,0x83,0xff,0xff,0xff,0x60,0xff,
+0x89,0xff,0x83,0xff,0xff,0xff,0x24,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x01,0xff,
+0x89,0xff,0x83,0xff,0xff,0xff,0x35,0xff,0x60,0xff,0x26,0xff,0xff,0xff,0x0f,0xff,
+0x49,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x78,0xff,0xff,0xff,0xa3,0xff,
+0x10,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x78,0xff,0xff,0xff,0xa3,0xff,
+0x8d,0xff,0x83,0xff,0xff,0xff,0x60,0xff,0x20,0xff,0x40,0xff,0xff,0xff,0x04,0xff,
+0x60,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0x6a,0xff,
+0x0c,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
+0x08,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x86,0xff,0x87,0xff,0xff,0xff,0xb0,0xff,
+0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,0x01,0xff,0x34,0xff,0xff,0xff,0x04,0xff,
+0x00,0xff,0x34,0xff,0xff,0xff,0x35,0xff,0xff,0xff,0x4f,0xff,0xff,0xff,0x89,0xff,
+0x00,0xff,0x09,0xff,0xff,0xff,0x01,0xff,0x87,0xff,0x8b,0xff,0xff,0xff,0xe0,0xff,
+0x00,0xff,0x38,0xff,0xff,0xff,0x88,0xff,0x00,0xff,0x70,0xff,0xff,0xff,0x03,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x70,0xff,0xff,0xff,0x03,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0x03,0xff,0x87,0xff,0x9b,0xff,0xff,0xff,0xe0,0xff,
+0x0c,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
+0x01,0xff,0x3c,0xff,0xff,0xff,0x05,0xff,0x6a,0xff,0x14,0xff,0xff,0xff,0x9e,0xff,
+0x67,0xff,0x1c,0xff,0xff,0xff,0x3f,0xff,0x69,0xff,0x1c,0xff,0xff,0xff,0x0f,0xff,
+0x66,0xff,0x1c,0xff,0xff,0xff,0x1f,0xff,0x38,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x6a,0xff,0x14,0xff,0xff,0xff,0x85,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0x40,0xff,
+0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x82,0xff,0x83,0xff,0xff,0xff,0x40,0xff,
+0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x6a,0xff,0x1c,0xff,0xff,0xff,0xf4,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x10,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x87,0xff,0x83,0xff,0xff,0xff,0xf0,0xff,
+0x86,0xff,0x93,0xff,0xff,0xff,0x80,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x8d,0xff,0x93,0xff,0xff,0xff,0x60,0xff,0x82,0xff,0x93,0xff,0xff,0xff,0x40,0xff,
+0x86,0xff,0x87,0xff,0xff,0xff,0x90,0xff,0x02,0xff,0x34,0xff,0xff,0xff,0x08,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x89,0xff,0x93,0xff,0xff,0xff,0x20,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x89,0xff,0x93,0xff,0xff,0xff,0x30,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x89,0xff,0x93,0xff,0xff,0xff,0x40,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x89,0xff,0x93,0xff,0xff,0xff,0x50,0xff,
+0x86,0xff,0x97,0xff,0xff,0xff,0x90,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x34,0xff,0xff,0xff,0x64,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x35,0xff,
+0x01,0xff,0x34,0xff,0xff,0xff,0x96,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x34,0xff,
+0x00,0xff,0x38,0xff,0xff,0xff,0x65,0xff,0x01,0xff,0x38,0xff,0xff,0xff,0x96,0xff,
+0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff,0x02,0xff,0x34,0xff,0xff,0xff,0x49,0xff,
+0x02,0xff,0x38,0xff,0xff,0xff,0x49,0xff,0x10,0xff,0x40,0xff,0xff,0xff,0x06,0xff,
+0x10,0xff,0x40,0xff,0xff,0xff,0x02,0xff,0x30,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x3c,0xff,0xff,0xff,0x25,0xff,0x6d,0xff,0x14,0xff,0xff,0xff,0xbe,0xff,
+0x98,0xff,0x50,0xff,0xff,0xff,0x73,0xff,0x88,0xff,0x50,0xff,0xff,0xff,0x73,0xff,
+0x83,0xff,0x68,0xff,0xff,0xff,0xc5,0xff,0x88,0xff,0x68,0xff,0xff,0xff,0xc4,0xff,
+0x83,0xff,0x68,0xff,0xff,0xff,0xc5,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xc6,0xff,
+0x98,0xff,0x50,0xff,0xff,0xff,0x73,0xff,0x88,0xff,0x50,0xff,0xff,0xff,0x73,0xff,
+0x83,0xff,0x78,0xff,0xff,0xff,0xc4,0xff,0x88,0xff,0x78,0xff,0xff,0xff,0xc5,0xff,
+0x83,0xff,0x78,0xff,0xff,0xff,0xc4,0xff,0x00,0xff,0x78,0xff,0xff,0xff,0xc6,0xff,
+0x98,0xff,0x50,0xff,0xff,0xff,0x73,0xff,0x88,0xff,0x50,0xff,0xff,0xff,0x73,0xff,
+0x83,0xff,0x68,0xff,0xff,0xff,0xc5,0xff,0x88,0xff,0x68,0xff,0xff,0xff,0xc4,0xff,
+0x83,0xff,0x68,0xff,0xff,0xff,0xc5,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xc6,0xff,
+0x00,0xff,0x3c,0xff,0xff,0xff,0x25,0xff,0x6e,0xff,0x14,0xff,0xff,0xff,0x8e,0xff,
+0x98,0xff,0x50,0xff,0xff,0xff,0x73,0xff,0x88,0xff,0x50,0xff,0xff,0xff,0x73,0xff,
+0x83,0xff,0x78,0xff,0xff,0xff,0xc4,0xff,0x88,0xff,0x78,0xff,0xff,0xff,0xc4,0xff,
+0x00,0xff,0x78,0xff,0xff,0xff,0xc4,0xff,0x20,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0xe9,0xff,
+0x01,0xff,0x38,0xff,0xff,0xff,0x28,0xff,0x01,0xff,0x38,0xff,0xff,0xff,0x29,0xff,
+0x00,0xff,0x38,0xff,0xff,0xff,0x66,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x34,0xff,
+0x00,0xff,0x38,0xff,0xff,0xff,0x75,0xff,0x10,0xff,0x40,0xff,0xff,0xff,0x06,0xff,
+0x00,0xff,0x41,0xff,0xff,0xff,0x07,0xff,0x30,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,
+0x98,0xff,0x70,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x3c,0xff,0xff,0xff,0x25,0xff,
+0x70,0xff,0x14,0xff,0xff,0xff,0x2e,0xff,0x80,0xff,0x70,0xff,0xff,0xff,0x42,0xff,
+0x63,0xff,0x72,0xff,0xff,0xff,0x20,0xff,0x80,0xff,0x68,0xff,0xff,0xff,0xa7,0xff,
+0x00,0xff,0x70,0xff,0xff,0xff,0x41,0xff,0x63,0xff,0x72,0xff,0xff,0xff,0x24,0xff,
+0x80,0xff,0x68,0xff,0xff,0xff,0xa7,0xff,0x00,0xff,0x70,0xff,0xff,0xff,0x46,0xff,
+0x63,0xff,0x72,0xff,0xff,0xff,0x24,0xff,0x80,0xff,0x68,0xff,0xff,0xff,0xa7,0xff,
+0x00,0xff,0x70,0xff,0xff,0xff,0x45,0xff,0x63,0xff,0x72,0xff,0xff,0xff,0x20,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0xa7,0xff,0x80,0xff,0x70,0xff,0xff,0xff,0x42,0xff,
+0x63,0xff,0x72,0xff,0xff,0xff,0x20,0xff,0x80,0xff,0x70,0xff,0xff,0xff,0x41,0xff,
+0x63,0xff,0x6a,0xff,0xff,0xff,0xa7,0xff,0x00,0xff,0x70,0xff,0xff,0xff,0x24,0xff,
+0x80,0xff,0x68,0xff,0xff,0xff,0xa7,0xff,0x00,0xff,0x70,0xff,0xff,0xff,0x44,0xff,
+0x63,0xff,0x72,0xff,0xff,0xff,0x24,0xff,0x80,0xff,0x68,0xff,0xff,0xff,0xa7,0xff,
+0xf0,0xff,0x40,0xff,0xff,0xff,0x05,0xff,0x00,0xff,0x4f,0xff,0xff,0xff,0x04,0xff,
+0x00,0xff,0x0d,0xff,0xff,0xff,0x0b,0xff,0x80,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,
+0x88,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,0xea,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,
+0x74,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0xa7,0xff,
+0x00,0xff,0x70,0xff,0xff,0xff,0x24,0xff,0x80,0xff,0x70,0xff,0xff,0xff,0x44,0xff,
+0x63,0xff,0x72,0xff,0xff,0xff,0x24,0xff,0x80,0xff,0x68,0xff,0xff,0xff,0xa7,0xff,
+0x00,0xff,0x4f,0xff,0xff,0xff,0x04,0xff,0x00,0xff,0x0d,0xff,0xff,0xff,0x0b,0xff,
+0x80,0xff,0x27,0xff,0xff,0xff,0x0f,0xff,0x88,0xff,0x23,0xff,0xff,0xff,0x0f,0xff,
+0xea,0xff,0x20,0xff,0xff,0xff,0x0f,0xff,0x74,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0xa7,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
+0x38,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x72,0xff,0x14,0xff,0xff,0xff,0x35,0xff,
+0x8b,0xff,0x83,0xff,0xff,0xff,0x30,0xff,0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x0e,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x10,0xff,0x90,0xff,0xff,0xff,0x10,0xff,0x08,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x10,0xff,0x90,0xff,0xff,0xff,0x90,0xff,0x02,0xff,0x40,0xff,0xff,0xff,0x40,0xff,
+0x11,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x10,0xff,0x90,0xff,0xff,0xff,0xb0,0xff,0xb0,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x50,0xff,0x30,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x40,0xff,0x78,0xff,0x42,0xff,0xff,0xff,0x50,0xff,
+0x48,0xff,0x90,0xff,0xff,0xff,0xa0,0xff,0x40,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x49,0xff,0x90,0xff,0xff,0xff,0x70,0xff,0x18,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x70,0xff,0x18,0xff,0x40,0xff,0xff,0xff,0x10,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x70,0xff,0x18,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x70,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x11,0xff,0x90,0xff,0xff,0xff,0x60,0xff,0x8d,0xff,0x93,0xff,0xff,0xff,0xd0,0xff,
+0x0b,0xff,0x40,0xff,0xff,0xff,0x80,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0xf0,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x10,0xff,0x11,0xff,0x90,0xff,0xff,0xff,0xe0,0xff,
+0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x93,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x08,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x47,0xff,0x90,0xff,0xff,0xff,0x20,0xff,0x22,0xff,0x18,0xff,0xff,0xff,0x9f,0xff,
+0x00,0xff,0x48,0xff,0xff,0xff,0x10,0xff,0x86,0xff,0x93,0xff,0xff,0xff,0x70,0xff,
+0x22,0xff,0x18,0xff,0xff,0xff,0x9f,0xff,0x00,0xff,0x48,0xff,0xff,0xff,0x00,0xff,
+0x86,0xff,0x93,0xff,0xff,0xff,0x70,0xff,0x22,0xff,0x18,0xff,0xff,0xff,0x9f,0xff,
+0x00,0xff,0x48,0xff,0xff,0xff,0x20,0xff,0x86,0xff,0x93,0xff,0xff,0xff,0x70,0xff,
+0x22,0xff,0x18,0xff,0xff,0xff,0x9f,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x48,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x86,0xff,0x93,0xff,0xff,0xff,0xb0,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,0x86,0xff,0x93,0xff,0xff,0xff,0xc0,0xff,
+0x86,0xff,0x97,0xff,0xff,0xff,0xd0,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
+0x80,0xff,0x37,0xff,0xff,0xff,0x02,0xff,0x84,0xff,0x3b,0xff,0xff,0xff,0x02,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x0b,0xff,0x0c,0xff,0x0d,0xff,0xff,0xff,0x90,0xff,
+0x00,0xff,0x70,0xff,0xff,0xff,0x0b,0xff,0x0c,0xff,0x0d,0xff,0xff,0xff,0xb0,0xff,
+0x88,0xff,0x37,0xff,0xff,0xff,0x03,0xff,0x8c,0xff,0x3b,0xff,0xff,0xff,0x03,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0x01,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x51,0xff,
+0x82,0xff,0x43,0xff,0xff,0xff,0x80,0xff,0x8b,0xff,0x93,0xff,0xff,0xff,0x60,0xff,
+0x00,0xff,0x34,0xff,0xff,0xff,0x89,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x0c,0xff,0x0d,0xff,0xff,0xff,0x80,0xff,0x0c,0xff,0x0d,0xff,0xff,0xff,0xa0,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x80,0xff,0x37,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,0x02,0xff,0x3c,0xff,0xff,0xff,0x45,0xff,
+0x76,0xff,0x14,0xff,0xff,0xff,0xde,0xff,0x00,0xff,0xa0,0xff,0xff,0xff,0x03,0xff,
+0x84,0xff,0x37,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x08,0xff,
+0x02,0xff,0x3c,0xff,0xff,0xff,0x45,0xff,0x77,0xff,0x14,0xff,0xff,0xff,0x2e,0xff,
+0x00,0xff,0xa0,0xff,0xff,0xff,0x03,0xff,0x7e,0xff,0x38,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x38,0xff,0xff,0xff,0x08,0xff,0x00,0xff,0x3c,0xff,0xff,0xff,0xe5,0xff,
+0x77,0xff,0x14,0xff,0xff,0xff,0x8e,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x00,0xff,0x58,0xff,0xff,0xff,0x03,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
+0x64,0xff,0x1c,0xff,0xff,0xff,0x4f,0xff,0x38,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x77,0xff,0x14,0xff,0xff,0xff,0xe5,0xff,0x8b,0xff,0x83,0xff,0xff,0xff,0x40,0xff,
+0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x64,0xff,0x1c,0xff,0xff,0xff,0x8f,0xff,
+0x38,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x78,0xff,0x14,0xff,0xff,0xff,0x35,0xff,
+0x8b,0xff,0x83,0xff,0xff,0xff,0x40,0xff,0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x09,0xff,
+0x00,0xff,0x34,0xff,0xff,0xff,0x85,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0x56,0xff,
+0x00,0xff,0x09,0xff,0xff,0xff,0x06,0xff,0x20,0xff,0x40,0xff,0xff,0xff,0x00,0xff,
+0x01,0xff,0x40,0xff,0xff,0xff,0xc1,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0x44,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0x05,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x15,0xff,
+0x00,0xff,0x68,0xff,0xff,0xff,0x05,0xff,0x00,0xff,0x68,0xff,0xff,0xff,0x45,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x86,0xff,0x87,0xff,0xff,0xff,0xf0,0xff,
+0x86,0xff,0x8b,0xff,0xff,0xff,0xe0,0xff,0x00,0xff,0x34,0xff,0xff,0xff,0xc8,0xff,
+0x00,0xff,0x38,0xff,0xff,0xff,0xc8,0xff,0x00,0xff,0x60,0xff,0xff,0xff,0x03,0xff,
+0x00,0xff,0x60,0xff,0xff,0xff,0x13,0xff,0x00,0xff,0x78,0xff,0xff,0xff,0x13,0xff,
+0x00,0xff,0x78,0xff,0xff,0xff,0x03,0xff,0x86,0xff,0x97,0xff,0xff,0xff,0xf0,0xff,
+0x86,0xff,0x9b,0xff,0xff,0xff,0xe0,0xff,0x05,0xff,0x81,0xff,0xff,0xff,0xc0,0xff,
+0x78,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x21,0xff,0x18,0xff,0xff,0xff,0x71,0xff,
+0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,0x7f,0xff,0x38,0xff,0xff,0xff,0x01,0xff,
+0x00,0xff,0x38,0xff,0xff,0xff,0x09,0xff,0x00,0xff,0x38,0xff,0xff,0xff,0x06,0xff,
+0x7e,0xff,0x40,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x40,0xff,0xff,0xff,0xb1,0xff,
+0x08,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x3c,0xff,0xff,0xff,0xc5,0xff,
+0x7a,0xff,0x14,0xff,0xff,0xff,0xbe,0xff,0x00,0xff,0x50,0xff,0xff,0xff,0x46,0xff,
+0xe1,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x7a,0xff,0x1c,0xff,0xff,0xff,0xe0,0xff,
+0x60,0xff,0x22,0xff,0xff,0xff,0x0f,0xff,0x00,0xff,0x58,0xff,0xff,0xff,0xa7,0xff,
+0x0c,0xff,0x0c,0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
+0x00,0xff,0x40,0xff,0xff,0xff,0xc4,0xff,0x00,0xff,0x0a,0xff,0xff,0xff,0x0f,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff };
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/korg1212/korg1212.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/korg1212/korg1212.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/korg1212/korg1212.c	(revision 91)
@@ -0,0 +1,2350 @@
+/*
+ *   Driver for the Korg 1212 IO PCI card
+ *
+ *	Copyright (c) 2001 Haroldo Gamal <gamal@alternex.com.br>
+ *
+ *   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/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+#define DEBUG                    1
+//#define LARGEALLOC               1
+#define PRINTK	printk
+
+// ----------------------------------------------------------------------------
+// the following enum defines the valid states of the Korg 1212 I/O card.
+// ----------------------------------------------------------------------------
+typedef enum {
+   K1212_STATE_NONEXISTENT,		// there is no card here
+   K1212_STATE_UNINITIALIZED,		// the card is awaiting DSP download
+   K1212_STATE_DSP_IN_PROCESS,		// the card is currently downloading its DSP code
+   K1212_STATE_DSP_COMPLETE,		// the card has finished the DSP download
+   K1212_STATE_READY,			// the card can be opened by an application.  Any application
+					//    requests prior to this state should fail.  Only an open
+					//    request can be made at this state.
+   K1212_STATE_OPEN,			// an application has opened the card
+   K1212_STATE_SETUP,			// the card has been setup for play
+   K1212_STATE_PLAYING,			// the card is playing
+   K1212_STATE_MONITOR,			// the card is in the monitor mode
+   K1212_STATE_CALIBRATING,		// the card is currently calibrating
+   K1212_STATE_ERRORSTOP,		// the card has stopped itself because of an error and we
+					//    are in the process of cleaning things up.
+   K1212_STATE_MAX_STATE		// state values of this and beyond are invalid
+} CardState;
+
+// ----------------------------------------------------------------------------
+// The following enumeration defines the constants written to the card's
+// host-to-card doorbell to initiate a command.
+// ----------------------------------------------------------------------------
+typedef enum {
+   K1212_DB_RequestForData        = 0,    // sent by the card to request a buffer fill.
+   K1212_DB_TriggerPlay           = 1,    // starts playback/record on the card.
+   K1212_DB_SelectPlayMode        = 2,    // select monitor, playback setup, or stop.
+   K1212_DB_ConfigureBufferMemory = 3,    // tells card where the host audio buffers are.
+   K1212_DB_RequestAdatTimecode   = 4,    // asks the card for the latest ADAT timecode value.
+   K1212_DB_SetClockSourceRate    = 5,    // sets the clock source and rate for the card.
+   K1212_DB_ConfigureMiscMemory   = 6,    // tells card where other buffers are.
+   K1212_DB_TriggerFromAdat       = 7,    // tells card to trigger from Adat at a specific
+                                          //    timecode value.
+   K1212_DB_RebootCard            = 0xA0, // instructs the card to reboot.
+   K1212_DB_BootFromDSPPage4      = 0xA4, // instructs the card to boot from the DSP microcode
+                                          //    on page 4 (local page to card).
+   K1212_DB_DSPDownloadDone       = 0xAE, // sent by the card to indicate the download has
+                                          //    completed.
+   K1212_DB_StartDSPDownload      = 0xAF  // tells the card to download its DSP firmware.
+} korg1212_dbcnst_t;
+
+#define K1212_ISRCODE_DMAERROR      0x80
+#define K1212_ISRCODE_CARDSTOPPED   0x81
+
+// ----------------------------------------------------------------------------
+// The following enumeration defines return codes for DeviceIoControl() calls
+// to the Korg 1212 I/O driver.
+// ----------------------------------------------------------------------------
+typedef enum {
+   K1212_CMDRET_Success         = 0,   // command was successfully placed
+   K1212_CMDRET_DIOCFailure,           // the DeviceIoControl call failed
+   K1212_CMDRET_PMFailure,             // the protected mode call failed
+   K1212_CMDRET_FailUnspecified,       // unspecified failure
+   K1212_CMDRET_FailBadState,          // the specified command can not be given in
+                                       //    the card's current state. (or the wave device's
+                                       //    state)
+   K1212_CMDRET_CardUninitialized,     // the card is uninitialized and cannot be used
+   K1212_CMDRET_BadIndex,              // an out of range card index was specified
+   K1212_CMDRET_BadHandle,             // an invalid card handle was specified
+   K1212_CMDRET_NoFillRoutine,         // a play request has been made before a fill routine set
+   K1212_CMDRET_FillRoutineInUse,      // can't set a new fill routine while one is in use
+   K1212_CMDRET_NoAckFromCard,         // the card never acknowledged a command
+   K1212_CMDRET_BadParams,             // bad parameters were provided by the caller
+
+   // --------------------------------------------------------------
+   // the following return errors are specific to the wave device
+   // driver interface.  These will not be encountered by users of
+   // the 32 bit DIOC interface (a.k.a. custom or native API).
+   // --------------------------------------------------------------
+   K1212_CMDRET_BadDevice,             // the specified wave device was out of range
+   K1212_CMDRET_BadFormat              // the specified wave format is unsupported
+} snd_korg1212rc;
+
+// ----------------------------------------------------------------------------
+// The following enumeration defines the constants used to select the play
+// mode for the card in the SelectPlayMode command.
+// ----------------------------------------------------------------------------
+typedef enum {
+   K1212_MODE_SetupPlay  = 0x00000001,     // provides card with pre-play information
+   K1212_MODE_MonitorOn  = 0x00000002,     // tells card to turn on monitor mode
+   K1212_MODE_MonitorOff = 0x00000004,     // tells card to turn off monitor mode
+   K1212_MODE_StopPlay   = 0x00000008      // stops playback on the card
+} PlayModeSelector;
+
+// ----------------------------------------------------------------------------
+// The following enumeration defines the constants used to select the monitor
+// mode for the card in the SetMonitorMode command.
+// ----------------------------------------------------------------------------
+typedef enum {
+   K1212_MONMODE_Off  = 0,     // tells card to turn off monitor mode
+   K1212_MONMODE_On            // tells card to turn on monitor mode
+} MonitorModeSelector;
+
+#define MAILBOX0_OFFSET      0x40	// location of mailbox 0 relative to base address
+#define MAILBOX1_OFFSET      0x44	// location of mailbox 1 relative to base address
+#define MAILBOX2_OFFSET      0x48	// location of mailbox 2 relative to base address
+#define MAILBOX3_OFFSET      0x4c	// location of mailbox 3 relative to base address
+#define OUT_DOORBELL_OFFSET  0x60	// location of PCI to local doorbell "
+#define IN_DOORBELL_OFFSET   0x64	// location of local to PCI doorbell "
+#define STATUS_REG_OFFSET    0x68	// location of interrupt control/status register "
+#define PCI_CONTROL_OFFSET   0x6c	// location of the EEPROM, PCI, User I/O, init control
+					//    register
+#define SENS_CONTROL_OFFSET  0x6e	// location of the input sensitivity setting register.
+					//    this is the upper word of the PCI control reg.
+#define DEV_VEND_ID_OFFSET   0x70	// location of the device and vendor ID register
+
+#define COMMAND_ACK_DELAY    13        // number of RTC ticks to wait for an acknowledgement
+                                        //    from the card after sending a command.
+#define INTERCOMMAND_DELAY   40
+#define MAX_COMMAND_RETRIES  5         // maximum number of times the driver will attempt
+                                       //    to send a command before giving up.
+#define COMMAND_ACK_MASK     0x8000    // the MSB is set in the command acknowledgment from
+                                        //    the card.
+#define DOORBELL_VAL_MASK    0x00FF    // the doorbell value is one byte
+
+#define CARD_BOOT_DELAY_IN_MS  10
+
+#define DSP_BOOT_DELAY_IN_MS   200
+
+#define kNumBuffers		8
+#define k1212MaxCards		4
+#define k1212NumWaveDevices	6
+#define k16BitChannels		10
+#define k32BitChannels		2
+#define kAudioChannels		(k16BitChannels + k32BitChannels)
+#define kPlayBufferFrames	1024
+
+#define K1212_CHANNELS     	16
+#define K1212_FRAME_SIZE        (sizeof(KorgAudioFrame))
+#define K1212_MAX_SAMPLES	(kPlayBufferFrames*kNumBuffers)
+#define K1212_PERIODS		(K1212_BUF_SIZE/K1212_BLOCK_SIZE)
+#define K1212_PERIOD_BYTES	(K1212_BLOCK_SIZE)
+#define K1212_BLOCK_SIZE        (K1212_FRAME_SIZE*kPlayBufferFrames)
+#define K1212_BUF_SIZE          (K1212_BLOCK_SIZE*kNumBuffers)
+
+#define k1212MinADCSens     0x7f
+#define k1212MaxADCSens     0x00
+#define k1212MaxVolume      0x7fff
+#define k1212MaxWaveVolume  0xffff
+#define k1212MinVolume      0x0000
+#define k1212MaxVolInverted 0x8000
+
+// -----------------------------------------------------------------
+// the following bits are used for controlling interrupts in the
+// interrupt control/status reg
+// -----------------------------------------------------------------
+#define  PCI_INT_ENABLE_BIT               0x00000100
+#define  PCI_DOORBELL_INT_ENABLE_BIT      0x00000200
+#define  LOCAL_INT_ENABLE_BIT             0x00010000
+#define  LOCAL_DOORBELL_INT_ENABLE_BIT    0x00020000
+#define  LOCAL_DMA1_INT_ENABLE_BIT        0x00080000
+
+// -----------------------------------------------------------------
+// the following bits are defined for the PCI command register
+// -----------------------------------------------------------------
+#define  PCI_CMD_MEM_SPACE_ENABLE_BIT     0x0002
+#define  PCI_CMD_IO_SPACE_ENABLE_BIT      0x0001
+#define  PCI_CMD_BUS_MASTER_ENABLE_BIT    0x0004
+
+// -----------------------------------------------------------------
+// the following bits are defined for the PCI status register
+// -----------------------------------------------------------------
+#define  PCI_STAT_PARITY_ERROR_BIT        0x8000
+#define  PCI_STAT_SYSTEM_ERROR_BIT        0x4000
+#define  PCI_STAT_MASTER_ABORT_RCVD_BIT   0x2000
+#define  PCI_STAT_TARGET_ABORT_RCVD_BIT   0x1000
+#define  PCI_STAT_TARGET_ABORT_SENT_BIT   0x0800
+
+// ------------------------------------------------------------------------
+// the following constants are used in setting the 1212 I/O card's input
+// sensitivity.
+// ------------------------------------------------------------------------
+#define  SET_SENS_LOCALINIT_BITPOS        15
+#define  SET_SENS_DATA_BITPOS             10
+#define  SET_SENS_CLOCK_BITPOS            8
+#define  SET_SENS_LOADSHIFT_BITPOS        0
+
+#define  SET_SENS_LEFTCHANID              0x00
+#define  SET_SENS_RIGHTCHANID             0x01
+
+#define  K1212SENSUPDATE_DELAY_IN_MS      50
+
+// --------------------------------------------------------------------------
+// WaitRTCTicks
+//
+//    This function waits the specified number of real time clock ticks.
+//    According to the DDK, each tick is ~0.8 microseconds.
+//    The defines following the function declaration can be used for the
+//    numTicksToWait parameter.
+// --------------------------------------------------------------------------
+#define ONE_RTC_TICK         1
+#define SENSCLKPULSE_WIDTH   4
+#define LOADSHIFT_DELAY      4
+#define INTERCOMMAND_DELAY  40
+#define STOPCARD_DELAY      300        // max # RTC ticks for the card to stop once we write
+                                       //    the command register.  (could be up to 180 us)
+#define COMMAND_ACK_DELAY   13         // number of RTC ticks to wait for an acknowledgement
+                                       //    from the card after sending a command.
+
+#include "korg1212-firmware.h"
+
+typedef struct _snd_korg1212 korg1212_t;
+
+typedef u16 K1212Sample;          // channels 0-9 use 16 bit samples
+typedef u32 K1212SpdifSample;     // channels 10-11 use 32 bits - only 20 are sent
+                                  //  across S/PDIF.
+typedef u32 K1212TimeCodeSample;  // holds the ADAT timecode value
+
+typedef enum {
+   K1212_CLKIDX_AdatAt44_1K = 0,    // selects source as ADAT at 44.1 kHz
+   K1212_CLKIDX_AdatAt48K,          // selects source as ADAT at 48 kHz
+   K1212_CLKIDX_WordAt44_1K,        // selects source as S/PDIF at 44.1 kHz
+   K1212_CLKIDX_WordAt48K,          // selects source as S/PDIF at 48 kHz
+   K1212_CLKIDX_LocalAt44_1K,       // selects source as local clock at 44.1 kHz
+   K1212_CLKIDX_LocalAt48K,         // selects source as local clock at 48 kHz
+   K1212_CLKIDX_Invalid             // used to check validity of the index
+} ClockSourceIndex;
+
+typedef enum {
+   K1212_CLKIDX_Adat = 0,    // selects source as ADAT
+   K1212_CLKIDX_Word,        // selects source as S/PDIF
+   K1212_CLKIDX_Local        // selects source as local clock
+} ClockSourceType;
+
+typedef struct KorgAudioFrame {
+   K1212Sample          frameData16[k16BitChannels];
+   K1212SpdifSample     frameData32[k32BitChannels];
+   K1212TimeCodeSample  timeCodeVal;
+} KorgAudioFrame;
+
+typedef struct KorgAudioBuffer {
+   KorgAudioFrame  bufferData[kPlayBufferFrames];     /* buffer definition */
+} KorgAudioBuffer;
+
+typedef struct KorgSharedBuffer {
+#ifdef LARGEALLOC
+   KorgAudioBuffer   playDataBufs[kNumBuffers];
+   KorgAudioBuffer   recordDataBufs[kNumBuffers];
+#endif
+   short             volumeData[kAudioChannels];
+   u32               cardCommand;
+   u16               routeData [kAudioChannels];
+   u32               AdatTimeCode;                 // ADAT timecode value
+} KorgSharedBuffer;
+
+typedef struct SensBits {
+   union {
+      struct {
+         unsigned int leftChanVal:8;
+         unsigned int leftChanId:8;
+      } v;
+      u16  leftSensBits;
+   } l;
+   union {
+      struct {
+         unsigned int rightChanVal:8;
+         unsigned int rightChanId:8;
+      } v;
+      u16  rightSensBits;
+   } r;
+} SensBits;
+
+struct _snd_korg1212 {
+        struct pci_dev *pci;
+        snd_card_t *card;
+        snd_pcm_t *pcm16;
+        int irq;
+
+        spinlock_t    lock;
+
+        wait_queue_head_t wait;
+
+        unsigned long iomem;
+        unsigned long ioport;
+	unsigned long iomem2;
+        unsigned long irqcount;
+        unsigned long inIRQ;
+        unsigned long iobase;
+
+	struct resource *res_iomem;
+	struct resource *res_ioport;
+	struct resource *res_iomem2;
+
+        u32 dspCodeSize;
+        u32 dspMemPhy;          // DSP memory block handle (Physical Address)
+        void * dspMemPtr;       //            block memory (Virtual Address)
+
+	u32 DataBufsSize;
+
+        KorgAudioBuffer  * playDataBufsPtr;
+        KorgAudioBuffer  * recordDataBufsPtr;
+
+	KorgSharedBuffer * sharedBufferPtr;
+	u32 RecDataPhy;
+	u32 PlayDataPhy;
+	unsigned long sharedBufferPhy;
+	u32 VolumeTablePhy;
+	u32 RoutingTablePhy;
+	u32 AdatTimeCodePhy;
+
+        u32 * statusRegPtr;	     // address of the interrupt status/control register
+        u32 * outDoorbellPtr;	     // address of the host->card doorbell register
+        u32 * inDoorbellPtr;	     // address of the card->host doorbell register
+        u32 * mailbox0Ptr;	     // address of mailbox 0 on the card
+        u32 * mailbox1Ptr;	     // address of mailbox 1 on the card
+        u32 * mailbox2Ptr;	     // address of mailbox 2 on the card
+        u32 * mailbox3Ptr;	     // address of mailbox 3 on the card
+        u32 * controlRegPtr;	     // address of the EEPROM, PCI, I/O, Init ctrl reg
+        u16 * sensRegPtr;	     // address of the sensitivity setting register
+        u32 * idRegPtr;		     // address of the device and vendor ID registers
+
+
+        size_t periodsize;
+        size_t currentBuffer;
+
+        snd_pcm_substream_t *playback_substream;
+        snd_pcm_substream_t *capture_substream;
+        snd_info_entry_t * proc_entry;
+
+ 	CardState cardState;
+        int running;
+        int idleMonitorOn;           // indicates whether the card is in idle monitor mode.
+        u32 cmdRetryCount;           // tracks how many times we have retried sending to the card.
+
+        ClockSourceIndex clkSrcRate; // sample rate and clock source
+
+        ClockSourceType clkSource;   // clock source
+        int clkRate;                 // clock rate
+
+        int volumePhase[kAudioChannels];
+
+        u16 leftADCInSens;           // ADC left channel input sensitivity
+        u16 rightADCInSens;          // ADC right channel input sensitivity
+};
+
+EXPORT_NO_SYMBOLS;
+MODULE_DESCRIPTION("korg1212");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{KORG,korg1212}}");
+
+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; /* Enable this card */
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for Korg 1212 soundcard.");
+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 Korg 1212 soundcard.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable Korg 1212 soundcard.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+MODULE_AUTHOR("Haroldo Gamal <gamal@alternex.com.br>");
+
+static struct pci_device_id snd_korg1212_ids[] __devinitdata = {
+	{ 0x10b5, 0x906d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },
+	{ 0, }
+};
+
+static char* stateName[] = {
+		        "Non-existent",
+                        "Uninitialized",
+                        "DSP download in process",
+                        "DSP download complete",
+                        "Ready",
+                        "Open",
+                        "Setup for play",
+                        "Playing",
+                        "Monitor mode on",
+                        "Calibrating"
+                        "Invalid"
+};
+
+static char* clockSourceTypeName[] = { "ADAT", "S/PDIF", "local" };
+
+static char* clockSourceName[] = {
+                        "ADAT at 44.1 kHz",
+                        "ADAT at 48 kHz",
+                        "S/PDIF at 44.1 kHz",
+                        "S/PDIF at 48 kHz",
+                        "local clock at 44.1 kHz",
+                        "local clock at 48 kHz"
+};
+
+static char* channelName[] = {
+                        "ADAT-1",
+                        "ADAT-2",
+                        "ADAT-3",
+                        "ADAT-4",
+                        "ADAT-5",
+                        "ADAT-6",
+                        "ADAT-7",
+                        "ADAT-8",
+                        "Analog-L",
+                        "Analog-R",
+                        "SPDIF-L",
+                        "SPDIF-R",
+};
+
+u16 ClockSourceSelector[] = {0x8000,   // selects source as ADAT at 44.1 kHz
+                             0x0000,   // selects source as ADAT at 48 kHz
+                             0x8001,   // selects source as S/PDIF at 44.1 kHz
+                             0x0001,   // selects source as S/PDIF at 48 kHz
+                             0x8002,   // selects source as local clock at 44.1 kHz
+                             0x0002    // selects source as local clock at 48 kHz
+                            };
+
+snd_korg1212rc rc;
+
+
+MODULE_DEVICE_TABLE(pci, snd_korg1212_ids);
+
+typedef union swap_u32 { unsigned char c[4]; u32 i; } swap_u32;
+
+#ifdef SNDRV_BIG_ENDIAN
+static u32 LowerWordSwap(u32 swappee)
+#else
+static u32 UpperWordSwap(u32 swappee)
+#endif
+{
+   swap_u32 retVal, swapper;
+
+   swapper.i = swappee;
+   retVal.c[2] = swapper.c[3];
+   retVal.c[3] = swapper.c[2];
+   retVal.c[1] = swapper.c[1];
+   retVal.c[0] = swapper.c[0];
+
+   return retVal.i;
+}
+
+#ifdef SNDRV_BIG_ENDIAN
+static u32 UpperWordSwap(u32 swappee)
+#else
+static u32 LowerWordSwap(u32 swappee)
+#endif
+{
+   swap_u32 retVal, swapper;
+
+   swapper.i = swappee;
+   retVal.c[2] = swapper.c[2];
+   retVal.c[3] = swapper.c[3];
+   retVal.c[1] = swapper.c[0];
+   retVal.c[0] = swapper.c[1];
+
+   return retVal.i;
+}
+
+#if 0 /* not used */
+
+static u32 EndianSwap(u32 swappee)
+{
+   swap_u32 retVal, swapper;
+
+   swapper.i = swappee;
+   retVal.c[0] = swapper.c[3];
+   retVal.c[1] = swapper.c[2];
+   retVal.c[2] = swapper.c[1];
+   retVal.c[3] = swapper.c[0];
+
+   return retVal.i;
+}
+
+#endif /* not used */
+
+void TickDelay(int time)
+{
+        udelay(time);
+}
+
+#define SetBitInWord(theWord,bitPosition)       (*theWord) |= (0x0001 << bitPosition)
+#define SetBitInDWord(theWord,bitPosition)      (*theWord) |= (0x00000001 << bitPosition)
+#define ClearBitInWord(theWord,bitPosition)     (*theWord) &= ~(0x0001 << bitPosition)
+#define ClearBitInDWord(theWord,bitPosition)    (*theWord) &= ~(0x00000001 << bitPosition)
+
+static snd_korg1212rc snd_korg1212_Send1212Command(korg1212_t *korg1212, korg1212_dbcnst_t doorbellVal,
+                            u32 mailBox0Val, u32 mailBox1Val, u32 mailBox2Val, u32 mailBox3Val)
+{
+        u32 retryCount;
+        u16 mailBox3Lo;
+
+        if (korg1212->outDoorbellPtr) {
+#ifdef DEBUG
+		PRINTK("DEBUG: Card <- 0x%08x 0x%08x [%s]\n", doorbellVal, mailBox0Val, stateName[korg1212->cardState]);
+#endif
+                for (retryCount = 0; retryCount < MAX_COMMAND_RETRIES; retryCount++) {
+
+                        writel(mailBox3Val, korg1212->mailbox3Ptr);
+                        writel(mailBox2Val, korg1212->mailbox2Ptr);
+                        writel(mailBox1Val, korg1212->mailbox1Ptr);
+                        writel(mailBox0Val, korg1212->mailbox0Ptr);
+                        writel(doorbellVal, korg1212->outDoorbellPtr);  // interrupt the card
+
+                        // --------------------------------------------------------------
+                        // the reboot command will not give an acknowledgement.
+                        // --------------------------------------------------------------
+                        switch (doorbellVal) {
+                                case K1212_DB_RebootCard:
+                                case K1212_DB_BootFromDSPPage4:
+                                case K1212_DB_StartDSPDownload:
+                                        return K1212_CMDRET_Success;
+                                default:
+                                        break;
+                        }
+
+                        // --------------------------------------------------------------
+                        // See if the card acknowledged the command.  Wait a bit, then
+                        // read in the low word of mailbox3.  If the MSB is set and the
+                        // low byte is equal to the doorbell value, then it ack'd.
+                        // --------------------------------------------------------------
+                        TickDelay(COMMAND_ACK_DELAY);
+                        mailBox3Lo = readl(korg1212->mailbox3Ptr);
+                        if (mailBox3Lo & COMMAND_ACK_MASK) {
+                                if ((mailBox3Lo & DOORBELL_VAL_MASK) == (doorbellVal & DOORBELL_VAL_MASK)) {
+                                        korg1212->cmdRetryCount += retryCount;
+                                        return K1212_CMDRET_Success;
+                                }
+                        }
+                }
+                korg1212->cmdRetryCount += retryCount;
+                return K1212_CMDRET_NoAckFromCard;
+        } else {
+                return K1212_CMDRET_CardUninitialized;
+        }
+}
+
+static void snd_korg1212_WaitForCardStopAck(korg1212_t *korg1212)
+{
+        unsigned long endtime = jiffies + 20 * HZ;
+
+#ifdef DEBUG
+        PRINTK("DEBUG: WaitForCardStopAck [%s]\n", stateName[korg1212->cardState]);
+#endif // DEBUG
+
+        if (korg1212->inIRQ)
+                return;
+
+        do {
+                if (readl(&korg1212->sharedBufferPtr->cardCommand) == 0)
+                        return;
+                if (!korg1212->inIRQ)
+                        schedule();
+        } while (jiffies < endtime);
+
+        writel(0, &korg1212->sharedBufferPtr->cardCommand);
+}
+
+static void snd_korg1212_TurnOnIdleMonitor(korg1212_t *korg1212)
+{
+        TickDelay(INTERCOMMAND_DELAY);
+        korg1212->idleMonitorOn = 1;
+        rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_SelectPlayMode,
+                        K1212_MODE_MonitorOn, 0, 0, 0);
+}
+
+static void snd_korg1212_TurnOffIdleMonitor(korg1212_t *korg1212)
+{
+        if (korg1212->idleMonitorOn) {
+                writel(0xffffffff, &korg1212->sharedBufferPtr->cardCommand);
+                snd_korg1212_WaitForCardStopAck(korg1212);
+                korg1212->idleMonitorOn = 0;
+        }
+}
+
+static void snd_korg1212_setCardState(korg1212_t * korg1212, CardState csState)
+{
+        switch (csState) {
+                case K1212_STATE_READY:
+                        snd_korg1212_TurnOnIdleMonitor(korg1212);
+                        break;
+
+                case K1212_STATE_OPEN:
+                        snd_korg1212_TurnOffIdleMonitor(korg1212);
+                        break;
+
+                default:
+                        break;
+        }
+
+        korg1212->cardState = csState;
+}
+
+static int snd_korg1212_OpenCard(korg1212_t * korg1212)
+{
+#ifdef DEBUG
+	PRINTK("DEBUG: OpenCard [%s]\n", stateName[korg1212->cardState]);
+#endif
+        snd_korg1212_setCardState(korg1212, K1212_STATE_OPEN);
+        return 1;
+}
+
+static int snd_korg1212_CloseCard(korg1212_t * korg1212)
+{
+#ifdef DEBUG
+	PRINTK("DEBUG: CloseCard [%s]\n", stateName[korg1212->cardState]);
+#endif
+
+        if (korg1212->cardState == K1212_STATE_SETUP) {
+                rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_SelectPlayMode,
+                                K1212_MODE_StopPlay, 0, 0, 0);
+#ifdef DEBUG
+	if (rc) PRINTK("DEBUG: CloseCard - RC = %d [%s]\n", rc, stateName[korg1212->cardState]);
+#endif
+
+                if (rc != K1212_CMDRET_Success)
+                        return 0;
+        } else if (korg1212->cardState > K1212_STATE_SETUP) {
+                writel(0xffffffff, &korg1212->sharedBufferPtr->cardCommand);
+                snd_korg1212_WaitForCardStopAck(korg1212);
+        }
+
+        if (korg1212->cardState > K1212_STATE_READY)
+                snd_korg1212_setCardState(korg1212, K1212_STATE_READY);
+
+        return 0;
+}
+
+static int snd_korg1212_SetupForPlay(korg1212_t * korg1212)
+{
+#ifdef DEBUG
+	PRINTK("DEBUG: SetupForPlay [%s]\n", stateName[korg1212->cardState]);
+#endif
+
+        snd_korg1212_setCardState(korg1212, K1212_STATE_SETUP);
+        rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_SelectPlayMode,
+                                        K1212_MODE_SetupPlay, 0, 0, 0);
+
+#ifdef DEBUG
+	if (rc) PRINTK("DEBUG: SetupForPlay - RC = %d [%s]\n", rc, stateName[korg1212->cardState]);
+#endif
+        if (rc != K1212_CMDRET_Success) {
+                return 0;
+        }
+        return 1;
+}
+
+static int snd_korg1212_TriggerPlay(korg1212_t * korg1212)
+{
+#ifdef DEBUG
+	PRINTK("DEBUG: TriggerPlay [%s]\n", stateName[korg1212->cardState]);
+#endif
+
+        snd_korg1212_setCardState(korg1212, K1212_STATE_PLAYING);
+        rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_TriggerPlay, 0, 0, 0, 0);
+
+#ifdef DEBUG
+	if (rc) PRINTK("DEBUG: TriggerPlay - RC = %d [%s]\n", rc, stateName[korg1212->cardState]);
+#endif
+
+        if (rc != K1212_CMDRET_Success) {
+                return 0;
+        }
+        return 1;
+}
+
+static int snd_korg1212_StopPlay(korg1212_t * korg1212)
+{
+#ifdef DEBUG
+	PRINTK("DEBUG: StopPlay [%s]\n", stateName[korg1212->cardState]);
+#endif
+
+        if (korg1212->cardState != K1212_STATE_ERRORSTOP) {
+                writel(0xffffffff, &korg1212->sharedBufferPtr->cardCommand);
+                snd_korg1212_WaitForCardStopAck(korg1212);
+        }
+        snd_korg1212_setCardState(korg1212, K1212_STATE_OPEN);
+        return 1;
+}
+
+static void snd_korg1212_EnableCardInterrupts(korg1212_t * korg1212)
+{
+	* korg1212->statusRegPtr = PCI_INT_ENABLE_BIT            |
+                                   PCI_DOORBELL_INT_ENABLE_BIT   |
+                                   LOCAL_INT_ENABLE_BIT          |
+                                   LOCAL_DOORBELL_INT_ENABLE_BIT |
+                                   LOCAL_DMA1_INT_ENABLE_BIT;
+}
+
+#if 0 /* not used */
+
+static int snd_korg1212_SetMonitorMode(korg1212_t *korg1212, MonitorModeSelector mode)
+{
+#ifdef DEBUG
+	PRINTK("DEBUG: SetMonitorMode [%s]\n", stateName[korg1212->cardState]);
+#endif
+
+        switch (mode) {
+                case K1212_MONMODE_Off:
+                        if (korg1212->cardState != K1212_STATE_MONITOR) {
+                                return 0;
+                        } else {
+                                writel(0xffffffff, &korg1212->sharedBufferPtr->cardCommand);
+                                snd_korg1212_WaitForCardStopAck(korg1212);
+                                snd_korg1212_setCardState(korg1212, K1212_STATE_OPEN);
+                        }
+                        break;
+
+                case K1212_MONMODE_On:
+                        if (korg1212->cardState != K1212_STATE_OPEN) {
+                                return 0;
+                        } else {
+                                snd_korg1212_setCardState(korg1212, K1212_STATE_MONITOR);
+                                rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_SelectPlayMode,
+                                                        K1212_MODE_MonitorOn, 0, 0, 0);
+                                if (rc != K1212_CMDRET_Success) {
+                                        return 0;
+                                }
+                        }
+                        break;
+
+                default:
+                        return 0;
+        }
+
+        return 1;
+}
+
+#endif /* not used */
+
+static int snd_korg1212_SetRate(korg1212_t *korg1212, int rate)
+{
+        static ClockSourceIndex s44[] = { K1212_CLKIDX_AdatAt44_1K,
+                                          K1212_CLKIDX_WordAt44_1K,
+                                          K1212_CLKIDX_LocalAt44_1K };
+        static ClockSourceIndex s48[] = {
+                                          K1212_CLKIDX_AdatAt48K,
+                                          K1212_CLKIDX_WordAt48K,
+                                          K1212_CLKIDX_LocalAt48K };
+        int parm;
+
+        switch(rate) {
+                case 44100:
+                parm = s44[korg1212->clkSource];
+                break;
+
+                case 48000:
+                parm = s48[korg1212->clkSource];
+                break;
+
+                default:
+                return -EINVAL;
+        }
+
+        korg1212->clkSrcRate = parm;
+        korg1212->clkRate = rate;
+
+	TickDelay(INTERCOMMAND_DELAY);
+	rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_SetClockSourceRate,
+					  ClockSourceSelector[korg1212->clkSrcRate],
+					  0, 0, 0);
+
+#ifdef DEBUG
+	if (rc) PRINTK("DEBUG: Set Clock Source Selector - RC = %d [%s]\n", rc, stateName[korg1212->cardState]);
+#endif
+
+        return 0;
+}
+
+static int snd_korg1212_SetClockSource(korg1212_t *korg1212, int source)
+{
+
+        if (source<0 || source >2)
+           return -EINVAL;
+
+        korg1212->clkSource = source;
+
+        snd_korg1212_SetRate(korg1212, korg1212->clkRate);
+
+        return 0;
+}
+
+static void snd_korg1212_DisableCardInterrupts(korg1212_t *korg1212)
+{
+	* korg1212->statusRegPtr = 0;
+}
+
+static int snd_korg1212_WriteADCSensitivity(korg1212_t *korg1212)
+{
+        SensBits  sensVals;
+        int       bitPosition;
+        int       channel;
+        int       clkIs48K;
+        int       monModeSet;
+        u16       controlValue;    // this keeps the current value to be written to
+                                   //  the card's eeprom control register.
+        u16       count;
+
+#ifdef DEBUG
+	PRINTK("DEBUG: WriteADCSensivity [%s]\n", stateName[korg1212->cardState]);
+#endif
+
+        // ----------------------------------------------------------------------------
+        // initialize things.  The local init bit is always set when writing to the
+        // card's control register.
+        // ----------------------------------------------------------------------------
+        controlValue = 0;
+        SetBitInWord(&controlValue, SET_SENS_LOCALINIT_BITPOS);    // init the control value
+
+        // ----------------------------------------------------------------------------
+        // make sure the card is not in monitor mode when we do this update.
+        // ----------------------------------------------------------------------------
+        if (korg1212->cardState == K1212_STATE_MONITOR || korg1212->idleMonitorOn) {
+                writel(0xffffffff, &korg1212->sharedBufferPtr->cardCommand);
+                monModeSet = 1;
+                snd_korg1212_WaitForCardStopAck(korg1212);
+        } else
+                monModeSet = 0;
+
+        // ----------------------------------------------------------------------------
+        // we are about to send new values to the card, so clear the new values queued
+        // flag.  Also, clear out mailbox 3, so we don't lockup.
+        // ----------------------------------------------------------------------------
+        writel(0, korg1212->mailbox3Ptr);
+        TickDelay(LOADSHIFT_DELAY);
+
+        // ----------------------------------------------------------------------------
+        // determine whether we are running a 48K or 44.1K clock.  This info is used
+        // later when setting the SPDIF FF after the volume has been shifted in.
+        // ----------------------------------------------------------------------------
+        switch (korg1212->clkSrcRate) {
+                case K1212_CLKIDX_AdatAt44_1K:
+                case K1212_CLKIDX_WordAt44_1K:
+                case K1212_CLKIDX_LocalAt44_1K:
+                        clkIs48K = 0;
+                        break;
+
+                case K1212_CLKIDX_WordAt48K:
+                case K1212_CLKIDX_AdatAt48K:
+                case K1212_CLKIDX_LocalAt48K:
+                default:
+                        clkIs48K = 1;
+                        break;
+        }
+
+        // ----------------------------------------------------------------------------
+        // start the update.  Setup the bit structure and then shift the bits.
+        // ----------------------------------------------------------------------------
+        sensVals.l.v.leftChanId   = SET_SENS_LEFTCHANID;
+        sensVals.r.v.rightChanId  = SET_SENS_RIGHTCHANID;
+        sensVals.l.v.leftChanVal  = korg1212->leftADCInSens;
+        sensVals.r.v.rightChanVal = korg1212->rightADCInSens;
+
+        // ----------------------------------------------------------------------------
+        // now start shifting the bits in.  Start with the left channel then the right.
+        // ----------------------------------------------------------------------------
+        for (channel = 0; channel < 2; channel++) {
+
+                // ----------------------------------------------------------------------------
+                // Bring the load/shift line low, then wait - the spec says >150ns from load/
+                // shift low to the first rising edge of the clock.
+                // ----------------------------------------------------------------------------
+                ClearBitInWord(&controlValue, SET_SENS_LOADSHIFT_BITPOS);
+                ClearBitInWord(&controlValue, SET_SENS_DATA_BITPOS);
+                writew(controlValue, korg1212->sensRegPtr);                          // load/shift goes low
+                TickDelay(LOADSHIFT_DELAY);
+
+                for (bitPosition = 15; bitPosition >= 0; bitPosition--) {       // for all the bits
+                        if (channel == 0) {
+                                if (sensVals.l.leftSensBits & (0x0001 << bitPosition)) {
+                                        SetBitInWord(&controlValue, SET_SENS_DATA_BITPOS);     // data bit set high
+                                } else {
+                                        ClearBitInWord(&controlValue, SET_SENS_DATA_BITPOS);   // data bit set low
+                                }
+                        } else {
+                                if (sensVals.r.rightSensBits & (0x0001 << bitPosition)) {
+                                SetBitInWord(&controlValue, SET_SENS_DATA_BITPOS);     // data bit set high
+                                } else {
+                                ClearBitInWord(&controlValue, SET_SENS_DATA_BITPOS);   // data bit set low
+                                }
+                        }
+
+                        ClearBitInWord(&controlValue, SET_SENS_CLOCK_BITPOS);
+                        writew(controlValue, korg1212->sensRegPtr);                       // clock goes low
+                        TickDelay(SENSCLKPULSE_WIDTH);
+                        SetBitInWord(&controlValue, SET_SENS_CLOCK_BITPOS);
+                        writew(controlValue, korg1212->sensRegPtr);                       // clock goes high
+                        TickDelay(SENSCLKPULSE_WIDTH);
+                }
+
+                // ----------------------------------------------------------------------------
+                // finish up SPDIF for left.  Bring the load/shift line high, then write a one
+                // bit if the clock rate is 48K otherwise write 0.
+                // ----------------------------------------------------------------------------
+                ClearBitInWord(&controlValue, SET_SENS_DATA_BITPOS);
+                ClearBitInWord(&controlValue, SET_SENS_CLOCK_BITPOS);
+                SetBitInWord(&controlValue, SET_SENS_LOADSHIFT_BITPOS);
+                writew(controlValue, korg1212->sensRegPtr);                   // load shift goes high - clk low
+                TickDelay(SENSCLKPULSE_WIDTH);
+
+                if (clkIs48K)
+                        SetBitInWord(&controlValue, SET_SENS_DATA_BITPOS);
+
+                writew(controlValue, korg1212->sensRegPtr);                   // set/clear data bit
+                TickDelay(ONE_RTC_TICK);
+                SetBitInWord(&controlValue, SET_SENS_CLOCK_BITPOS);
+                writew(controlValue, korg1212->sensRegPtr);                   // clock goes high
+                TickDelay(SENSCLKPULSE_WIDTH);
+                ClearBitInWord(&controlValue, SET_SENS_CLOCK_BITPOS);
+                writew(controlValue, korg1212->sensRegPtr);                   // clock goes low
+                TickDelay(SENSCLKPULSE_WIDTH);
+        }
+
+        // ----------------------------------------------------------------------------
+        // The update is complete.  Set a timeout.  This is the inter-update delay.
+        // Also, if the card was in monitor mode, restore it.
+        // ----------------------------------------------------------------------------
+        for (count = 0; count < 10; count++)
+                TickDelay(SENSCLKPULSE_WIDTH);
+
+        if (monModeSet) {
+                rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_SelectPlayMode,
+                                K1212_MODE_MonitorOn, 0, 0, 0);
+#ifdef DEBUG
+	        if (rc) PRINTK("DEBUG: WriteADCSensivity - RC = %d [%s]\n", rc, stateName[korg1212->cardState]);
+#endif
+
+        }
+
+        return 1;
+}
+
+static void snd_korg1212_OnDSPDownloadComplete(korg1212_t *korg1212)
+{
+        int channel;
+
+#ifdef DEBUG
+        PRINTK("DEBUG: DSP download is complete. [%s]\n", stateName[korg1212->cardState]);
+#endif
+
+        // ----------------------------------------------------
+        // tell the card to boot
+        // ----------------------------------------------------
+        rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_BootFromDSPPage4, 0, 0, 0, 0);
+
+#ifdef DEBUG
+	if (rc) PRINTK("DEBUG: Boot from Page 4 - RC = %d [%s]\n", rc, stateName[korg1212->cardState]);
+#endif
+	mdelay(DSP_BOOT_DELAY_IN_MS);
+
+        // --------------------------------------------------------------------------------
+        // Let the card know where all the buffers are.
+        // --------------------------------------------------------------------------------
+        rc = snd_korg1212_Send1212Command(korg1212,
+                        K1212_DB_ConfigureBufferMemory,
+                        LowerWordSwap(korg1212->PlayDataPhy),
+                        LowerWordSwap(korg1212->RecDataPhy),
+                        ((kNumBuffers * kPlayBufferFrames) / 2),   // size given to the card
+                                                                   // is based on 2 buffers
+                        0
+        );
+
+#ifdef DEBUG
+	if (rc) PRINTK("DEBUG: Configure Buffer Memory - RC = %d [%s]\n", rc, stateName[korg1212->cardState]);
+#endif
+
+        TickDelay(INTERCOMMAND_DELAY);
+
+        rc = snd_korg1212_Send1212Command(korg1212,
+                        K1212_DB_ConfigureMiscMemory,
+                        LowerWordSwap(korg1212->VolumeTablePhy),
+                        LowerWordSwap(korg1212->RoutingTablePhy),
+                        LowerWordSwap(korg1212->AdatTimeCodePhy),
+                        0
+        );
+
+#ifdef DEBUG
+	if (rc) PRINTK("DEBUG: Configure Misc Memory - RC = %d [%s]\n", rc, stateName[korg1212->cardState]);
+#endif
+
+
+        // --------------------------------------------------------------------------------
+        // Initialize the routing and volume tables, then update the card's state.
+        // --------------------------------------------------------------------------------
+        TickDelay(INTERCOMMAND_DELAY);
+
+        for (channel = 0; channel < kAudioChannels; channel++) {
+                korg1212->sharedBufferPtr->volumeData[channel] = k1212MaxVolume;
+                //korg1212->sharedBufferPtr->routeData[channel] = channel;
+                korg1212->sharedBufferPtr->routeData[channel] = 8 + (channel & 1);
+        }
+
+        snd_korg1212_WriteADCSensitivity(korg1212);
+
+	TickDelay(INTERCOMMAND_DELAY);
+	rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_SetClockSourceRate,
+					  ClockSourceSelector[korg1212->clkSrcRate],
+					  0, 0, 0);
+#ifdef DEBUG
+	if (rc) PRINTK("DEBUG: Set Clock Source Selector - RC = %d [%s]\n", rc, stateName[korg1212->cardState]);
+#endif
+
+	snd_korg1212_setCardState(korg1212, K1212_STATE_READY);
+
+#ifdef DEBUG
+	if (rc) PRINTK("DEBUG: Set Monitor On - RC = %d [%s]\n", rc, stateName[korg1212->cardState]);
+#endif
+
+        wake_up_interruptible(&korg1212->wait);
+}
+
+
+static void snd_korg1212_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+        u32 doorbellValue;
+        korg1212_t *korg1212 = snd_magic_cast(korg1212_t, dev_id, return);
+
+	if(irq != korg1212->irq)
+		return;
+
+        doorbellValue = readl(korg1212->inDoorbellPtr);
+
+        if (!doorbellValue)
+		return;
+
+	writel(doorbellValue, korg1212->inDoorbellPtr);
+
+        korg1212->irqcount++;
+
+	korg1212->inIRQ++;
+
+
+        switch (doorbellValue) {
+                case K1212_DB_DSPDownloadDone:
+#ifdef DEBUG
+                        PRINTK("DEBUG: IRQ DNLD count - %ld, %x, [%s].\n", korg1212->irqcount, doorbellValue, stateName[korg1212->cardState]);
+#endif
+                        if (korg1212->cardState == K1212_STATE_DSP_IN_PROCESS) {
+                                        snd_korg1212_setCardState(korg1212, K1212_STATE_DSP_COMPLETE);
+                                        snd_korg1212_OnDSPDownloadComplete(korg1212);
+                        }
+                        break;
+
+                // ------------------------------------------------------------------------
+                // an error occurred - stop the card
+                // ------------------------------------------------------------------------
+                case K1212_ISRCODE_DMAERROR:
+#ifdef DEBUG
+                        PRINTK("DEBUG: IRQ DMAE count - %ld, %x, [%s].\n", korg1212->irqcount, doorbellValue, stateName[korg1212->cardState]);
+#endif
+                        writel(0, &korg1212->sharedBufferPtr->cardCommand);
+                        break;
+
+                // ------------------------------------------------------------------------
+                // the card has stopped by our request.  Clear the command word and signal
+                // the semaphore in case someone is waiting for this.
+                // ------------------------------------------------------------------------
+                case K1212_ISRCODE_CARDSTOPPED:
+#ifdef DEBUG
+                        PRINTK("DEBUG: IRQ CSTP count - %ld, %x, [%s].\n", korg1212->irqcount, doorbellValue, stateName[korg1212->cardState]);
+#endif
+                        writel(0, &korg1212->sharedBufferPtr->cardCommand);
+                        break;
+
+                default:
+#ifdef XDEBUG
+                        PRINTK("DEBUG: IRQ DFLT count - %ld, %x, cpos=%d [%s].\n", korg1212->irqcount, doorbellValue, 
+				korg1212->currentBuffer, stateName[korg1212->cardState]);
+#endif
+                        if ((korg1212->cardState > K1212_STATE_SETUP) || korg1212->idleMonitorOn) {
+                                korg1212->currentBuffer++;
+
+                                if (korg1212->currentBuffer >= kNumBuffers)
+                                        korg1212->currentBuffer = 0;
+
+                                if (!korg1212->running)
+                                        break;
+
+                                if (korg1212->capture_substream) {
+                                        snd_pcm_period_elapsed(korg1212->capture_substream);
+                                }
+
+                                if (korg1212->playback_substream) {
+                                        snd_pcm_period_elapsed(korg1212->playback_substream);
+                                }
+                        }
+                        break;
+        }
+
+	korg1212->inIRQ--;
+}
+
+static int snd_korg1212_downloadDSPCode(korg1212_t *korg1212)
+{
+
+#ifdef DEBUG
+        PRINTK("DEBUG: DSP download is starting... [%s]\n", stateName[korg1212->cardState]);
+#endif
+
+        // ---------------------------------------------------------------
+        // verify the state of the card before proceeding.
+        // ---------------------------------------------------------------
+        if (korg1212->cardState >= K1212_STATE_DSP_IN_PROCESS) {
+                return 1;
+        }
+
+        snd_korg1212_setCardState(korg1212, K1212_STATE_DSP_IN_PROCESS);
+
+        memcpy(korg1212->dspMemPtr, dspCode, korg1212->dspCodeSize);
+
+        rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_StartDSPDownload,
+                                     UpperWordSwap(korg1212->dspMemPhy),
+                                     0, 0, 0);
+
+#ifdef DEBUG
+	if (rc) PRINTK("DEBUG: Start DSP Download RC = %d [%s]\n", rc, stateName[korg1212->cardState]);
+#endif
+
+        interruptible_sleep_on_timeout(&korg1212->wait, HZ * 4);
+
+        return 0;
+}
+
+static snd_pcm_hardware_t snd_korg1212_playback_info =
+{
+        info:                (SNDRV_PCM_INFO_MMAP |
+                              SNDRV_PCM_INFO_MMAP_VALID |
+                              SNDRV_PCM_INFO_INTERLEAVED),
+        formats:             SNDRV_PCM_FMTBIT_S16_LE,
+        rates:               (SNDRV_PCM_RATE_44100 |
+                              SNDRV_PCM_RATE_48000),
+        rate_min:            44100,
+        rate_max:            48000,
+        channels_min:        K1212_CHANNELS,
+        channels_max:        K1212_CHANNELS,
+        buffer_bytes_max:    K1212_BUF_SIZE,
+        period_bytes_min:    K1212_PERIOD_BYTES,
+        period_bytes_max:    K1212_PERIOD_BYTES,
+        periods_min:         K1212_PERIODS,
+        periods_max:         K1212_PERIODS,
+        fifo_size:           0,
+};
+
+static snd_pcm_hardware_t snd_korg1212_capture_info =
+{
+        info:                (SNDRV_PCM_INFO_MMAP |
+                              SNDRV_PCM_INFO_MMAP_VALID |
+                              SNDRV_PCM_INFO_INTERLEAVED),
+        formats:             SNDRV_PCM_FMTBIT_S16_LE,
+        rates:               (SNDRV_PCM_RATE_44100 |
+                              SNDRV_PCM_RATE_48000),
+        rate_min:            44100,
+        rate_max:            48000,
+        channels_min:        K1212_CHANNELS,
+        channels_max:        K1212_CHANNELS,
+        buffer_bytes_max:    K1212_BUF_SIZE,
+        period_bytes_min:    K1212_PERIOD_BYTES,
+        period_bytes_max:    K1212_PERIOD_BYTES,
+        periods_min:         K1212_PERIODS,
+        periods_max:         K1212_PERIODS,
+        fifo_size:           0,
+};
+
+static void snd_korg1212_free_pcm(snd_pcm_t *pcm)
+{
+        korg1212_t *korg1212 = (korg1212_t *) pcm->private_data;
+
+#ifdef DEBUG
+		PRINTK("DEBUG: snd_korg1212_free_pcm [%s]\n", stateName[korg1212->cardState]);
+#endif
+
+        korg1212->pcm16 = NULL;
+}
+
+static unsigned int period_bytes[] = { K1212_PERIOD_BYTES };
+
+#define PERIOD_BYTES sizeof(period_bytes) / sizeof(period_bytes[0])
+
+static snd_pcm_hw_constraint_list_t hw_constraints_period_bytes = {
+        count: PERIOD_BYTES,
+        list: period_bytes,
+        mask: 0
+};
+
+static int snd_korg1212_playback_open(snd_pcm_substream_t *substream)
+{
+        unsigned long flags;
+        korg1212_t *korg1212 = _snd_pcm_substream_chip(substream);
+        snd_pcm_runtime_t *runtime = substream->runtime;
+
+#ifdef DEBUG
+		PRINTK("DEBUG: snd_korg1212_playback_open [%s]\n", stateName[korg1212->cardState]);
+#endif
+
+        spin_lock_irqsave(&korg1212->lock, flags);
+
+        snd_korg1212_OpenCard(korg1212);
+
+        snd_pcm_set_sync(substream);    // ???
+
+        runtime->hw = snd_korg1212_playback_info;
+	runtime->dma_area = (char *) korg1212->playDataBufsPtr;
+	runtime->dma_bytes = K1212_BUF_SIZE;
+
+        korg1212->playback_substream = substream;
+        korg1212->periodsize = K1212_PERIODS;
+
+        spin_unlock_irqrestore(&korg1212->lock, flags);
+
+        snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, K1212_BUF_SIZE, K1212_BUF_SIZE);
+        snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_period_bytes);
+        return 0;
+}
+
+static int snd_korg1212_capture_open(snd_pcm_substream_t *substream)
+{
+        unsigned long flags;
+        korg1212_t *korg1212 = _snd_pcm_substream_chip(substream);
+        snd_pcm_runtime_t *runtime = substream->runtime;
+
+#ifdef DEBUG
+		PRINTK("DEBUG: snd_korg1212_capture_open [%s]\n", stateName[korg1212->cardState]);
+#endif
+
+        spin_lock_irqsave(&korg1212->lock, flags);
+
+        snd_korg1212_OpenCard(korg1212);
+
+        snd_pcm_set_sync(substream);    // ???
+
+        runtime->hw = snd_korg1212_capture_info;
+	runtime->dma_area = (char *) korg1212->recordDataBufsPtr;
+	runtime->dma_bytes = K1212_BUF_SIZE;
+
+        korg1212->capture_substream = substream;
+        korg1212->periodsize = K1212_PERIODS;
+
+        spin_unlock_irqrestore(&korg1212->lock, flags);
+
+        snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, K1212_BUF_SIZE, K1212_BUF_SIZE);
+        snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_period_bytes);
+        return 0;
+}
+
+static int snd_korg1212_playback_close(snd_pcm_substream_t *substream)
+{
+        unsigned long flags;
+        korg1212_t *korg1212 = _snd_pcm_substream_chip(substream);
+
+#ifdef DEBUG
+		PRINTK("DEBUG: snd_korg1212_playback_close [%s]\n", stateName[korg1212->cardState]);
+#endif
+
+        spin_lock_irqsave(&korg1212->lock, flags);
+
+        korg1212->playback_substream = NULL;
+        korg1212->periodsize = 0;
+
+        snd_korg1212_CloseCard(korg1212);
+
+        spin_unlock_irqrestore(&korg1212->lock, flags);
+        return 0;
+}
+
+static int snd_korg1212_capture_close(snd_pcm_substream_t *substream)
+{
+        unsigned long flags;
+        korg1212_t *korg1212 = _snd_pcm_substream_chip(substream);
+
+#ifdef DEBUG
+		PRINTK("DEBUG: snd_korg1212_capture_close [%s]\n", stateName[korg1212->cardState]);
+#endif
+
+        spin_lock_irqsave(&korg1212->lock, flags);
+
+        korg1212->capture_substream = NULL;
+        korg1212->periodsize = 0;
+
+        snd_korg1212_CloseCard(korg1212);
+
+        spin_unlock_irqrestore(&korg1212->lock, flags);
+        return 0;
+}
+
+static int snd_korg1212_channel_info(snd_pcm_substream_t *substream,
+				    snd_pcm_channel_info_t *info)
+{
+	int chn = info->channel;
+
+	// snd_assert(info->channel < kAudioChannels + 1, return -EINVAL);
+
+        info->offset = 0;
+        // if (chn < k16BitChannels) {
+                info->first = chn * 16;
+        // } else {
+        //         info->first = k16BitChannels * 16 + (chn - k16BitChannels - 1) * 32;
+        // }
+        info->step = sizeof(KorgAudioFrame) * 8;
+
+#ifdef DEBUG
+		PRINTK("DEBUG: snd_korg1212_channel_info %d:, offset=%ld, first=%d, step=%d\n", chn, info->offset, info->first, info->step);
+#endif
+
+	return 0;
+}
+
+static int snd_korg1212_ioctl(snd_pcm_substream_t *substream,
+			     unsigned int cmd, void *arg)
+{
+#ifdef DEBUG
+		PRINTK("DEBUG: snd_korg1212_ioctl: cmd=%d\n", cmd);
+#endif
+	if (cmd == SNDRV_PCM_IOCTL1_CHANNEL_INFO ) {
+		snd_pcm_channel_info_t *info = arg;
+		return snd_korg1212_channel_info(substream, info);
+	}
+
+        return snd_pcm_lib_ioctl(substream, cmd, arg);
+}
+
+static int snd_korg1212_hw_params(snd_pcm_substream_t *substream,
+                             snd_pcm_hw_params_t *params)
+{
+        unsigned long flags;
+        korg1212_t *korg1212 = _snd_pcm_substream_chip(substream);
+        int err;
+
+#ifdef DEBUG
+		PRINTK("DEBUG: snd_korg1212_hw_params [%s]\n", stateName[korg1212->cardState]);
+#endif
+
+        spin_lock_irqsave(&korg1212->lock, flags);
+        if ((err = snd_korg1212_SetRate(korg1212, params_rate(params))) < 0) {
+                spin_unlock_irqrestore(&korg1212->lock, flags);
+                return err;
+        }
+
+        if (params_format(params) != SNDRV_PCM_FORMAT_S16_LE) {
+                spin_unlock_irqrestore(&korg1212->lock, flags);
+                return -EINVAL;
+        }
+
+        korg1212->periodsize = K1212_BLOCK_SIZE;
+
+        spin_unlock_irqrestore(&korg1212->lock, flags);
+
+        return 0;
+}
+
+static int snd_korg1212_prepare(snd_pcm_substream_t *substream)
+{
+        korg1212_t *korg1212 = _snd_pcm_substream_chip(substream);
+        unsigned long flags;
+
+#ifdef DEBUG
+		PRINTK("DEBUG: snd_korg1212_prepare [%s]\n", stateName[korg1212->cardState]);
+#endif
+
+        spin_lock_irqsave(&korg1212->lock, flags);
+
+        snd_korg1212_SetupForPlay(korg1212);
+
+        korg1212->currentBuffer = -1;
+
+        spin_unlock_irqrestore(&korg1212->lock, flags);
+        return 0;
+}
+
+static int snd_korg1212_trigger(snd_pcm_substream_t *substream,
+                           int cmd)
+{
+        korg1212_t *korg1212 = _snd_pcm_substream_chip(substream);
+
+#ifdef DEBUG
+		PRINTK("DEBUG: snd_korg1212_trigger [%s] cmd=%d\n", stateName[korg1212->cardState], cmd);
+#endif
+
+        switch (cmd) {
+                case SNDRV_PCM_TRIGGER_START:
+                        korg1212->running = 1;
+                        snd_korg1212_TriggerPlay(korg1212);
+                        break;
+
+                case SNDRV_PCM_TRIGGER_STOP:
+                        korg1212->running = 0;
+                        snd_korg1212_StopPlay(korg1212);
+                        break;
+
+                default:
+                        return -EINVAL;
+        }
+        return 0;
+}
+
+static snd_pcm_uframes_t snd_korg1212_pointer(snd_pcm_substream_t *substream)
+{
+        korg1212_t *korg1212 = _snd_pcm_substream_chip(substream);
+        snd_pcm_uframes_t pos;
+
+	if (korg1212->currentBuffer < 0)
+		return 0;
+
+	pos = korg1212->currentBuffer * kPlayBufferFrames;
+
+#ifdef XDEBUG
+		PRINTK("DEBUG: snd_korg1212_pointer [%s] %ld\n", stateName[korg1212->cardState], pos);
+#endif
+
+        return pos;
+}
+
+static int snd_korg1212_playback_copy(snd_pcm_substream_t *substream,
+                        int channel, /* not used (interleaved data) */
+                        snd_pcm_uframes_t pos,
+                        void *src,
+                        snd_pcm_uframes_t count)
+{
+        korg1212_t *korg1212 = _snd_pcm_substream_chip(substream);
+	KorgAudioFrame * dst = korg1212->playDataBufsPtr[0].bufferData + pos;
+
+#ifdef DEBUG
+		PRINTK("DEBUG: snd_korg1212_playback_copy [%s] %ld %ld\n", stateName[korg1212->cardState], pos, count);
+#endif
+ 
+	snd_assert(pos + count <= K1212_MAX_SAMPLES, return -EINVAL);
+
+        copy_from_user(dst, src, count * K1212_FRAME_SIZE);
+
+        return 0;
+}
+
+static int snd_korg1212_capture_copy(snd_pcm_substream_t *substream,
+                        int channel, /* not used (interleaved data) */
+                        snd_pcm_uframes_t pos,
+                        void *dst,
+                        snd_pcm_uframes_t count)
+{
+        korg1212_t *korg1212 = _snd_pcm_substream_chip(substream);
+	KorgAudioFrame * src = korg1212->recordDataBufsPtr[0].bufferData + pos;
+
+#ifdef DEBUG
+		PRINTK("DEBUG: snd_korg1212_capture_copy [%s] %ld %ld\n", stateName[korg1212->cardState], pos, count);
+#endif
+
+	snd_assert(pos + count <= K1212_MAX_SAMPLES, return -EINVAL);
+
+        copy_to_user(dst, src, count * K1212_FRAME_SIZE);
+
+        return 0;
+}
+
+static int snd_korg1212_playback_silence(snd_pcm_substream_t *substream,
+                           int channel, /* not used (interleaved data) */
+                           snd_pcm_uframes_t pos,
+                           snd_pcm_uframes_t count)
+{
+        korg1212_t *korg1212 = _snd_pcm_substream_chip(substream);
+	KorgAudioFrame * dst = korg1212->playDataBufsPtr[0].bufferData + pos;
+
+#ifdef DEBUG
+		PRINTK("DEBUG: snd_korg1212_playback_silence [%s]\n", stateName[korg1212->cardState]);
+#endif
+
+	snd_assert(pos + count <= K1212_MAX_SAMPLES, return -EINVAL);
+
+        memset(dst, 0, count * K1212_FRAME_SIZE);
+
+        return 0;
+}
+
+static snd_pcm_ops_t snd_korg1212_playback_ops = {
+        open:           snd_korg1212_playback_open,
+        close:          snd_korg1212_playback_close,
+        ioctl:          snd_korg1212_ioctl,
+        hw_params:      snd_korg1212_hw_params,
+        prepare:        snd_korg1212_prepare,
+        trigger:        snd_korg1212_trigger,
+        pointer:        snd_korg1212_pointer,
+        copy:           snd_korg1212_playback_copy,
+        silence:        snd_korg1212_playback_silence,
+};
+
+static snd_pcm_ops_t snd_korg1212_capture_ops = {
+	open:		snd_korg1212_capture_open,
+	close:		snd_korg1212_capture_close,
+	ioctl:		snd_korg1212_ioctl,
+	hw_params:	snd_korg1212_hw_params,
+	prepare:	snd_korg1212_prepare,
+	trigger:	snd_korg1212_trigger,
+	pointer:	snd_korg1212_pointer,
+	copy:		snd_korg1212_capture_copy,
+};
+
+/*
+ * Control Interface
+ */
+
+static int snd_korg1212_control_phase_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = (kcontrol->private_value >= 8) ? 2 : 1;
+	return 0;
+}
+
+static int snd_korg1212_control_phase_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u)
+{
+	korg1212_t *korg1212 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int i = kcontrol->private_value;
+
+	spin_lock_irqsave(&korg1212->lock, flags);
+
+        u->value.integer.value[0] = korg1212->volumePhase[i];
+
+	if (i >= 8) 
+        	u->value.integer.value[1] = korg1212->volumePhase[i+1];
+
+        spin_unlock_irqrestore(&korg1212->lock, flags);
+
+        return 0;
+}
+
+static int snd_korg1212_control_phase_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u)
+{
+	korg1212_t *korg1212 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+        int change = 0;
+        int i, val;
+
+	spin_lock_irqsave(&korg1212->lock, flags);
+
+	i = kcontrol->private_value;
+
+	korg1212->volumePhase[i] = u->value.integer.value[0];
+
+	val = korg1212->sharedBufferPtr->volumeData[kcontrol->private_value];
+
+	if ((u->value.integer.value[0] > 0) != (val < 0)) {
+		val = abs(val) * (korg1212->volumePhase[i] > 0 ? -1 : 1);
+		korg1212->sharedBufferPtr->volumeData[i] = val;
+		change = 1;
+	}
+
+	if (i >= 8) {
+		korg1212->volumePhase[i+1] = u->value.integer.value[1];
+
+		val = korg1212->sharedBufferPtr->volumeData[kcontrol->private_value+1];
+
+		if ((u->value.integer.value[1] > 0) != (val < 0)) {
+			val = abs(val) * (korg1212->volumePhase[i+1] > 0 ? -1 : 1);
+			korg1212->sharedBufferPtr->volumeData[i+1] = val;
+			change = 1;
+		}
+	}
+
+	spin_unlock_irqrestore(&korg1212->lock, flags);
+
+        return change;
+}
+
+static int snd_korg1212_control_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+        uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = (kcontrol->private_value >= 8) ? 2 : 1;
+        uinfo->value.integer.min = k1212MinVolume;
+	uinfo->value.integer.max = k1212MaxVolume;
+        return 0;
+}
+
+static int snd_korg1212_control_volume_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u)
+{
+	korg1212_t *korg1212 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+        int i;
+
+	spin_lock_irqsave(&korg1212->lock, flags);
+
+	i = kcontrol->private_value;
+        u->value.integer.value[0] = abs(korg1212->sharedBufferPtr->volumeData[i]);
+
+	if (i >= 8) 
+                u->value.integer.value[1] = abs(korg1212->sharedBufferPtr->volumeData[i+1]);
+
+        spin_unlock_irqrestore(&korg1212->lock, flags);
+
+        return 0;
+}
+
+static int snd_korg1212_control_volume_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u)
+{
+	korg1212_t *korg1212 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+        int change = 0;
+        int i;
+	int val;
+
+	spin_lock_irqsave(&korg1212->lock, flags);
+
+	i = kcontrol->private_value;
+
+	if (u->value.integer.value[0] != abs(korg1212->sharedBufferPtr->volumeData[i])) {
+		val = korg1212->volumePhase[i] > 0 ? -1 : 1;
+		val *= u->value.integer.value[0];
+		korg1212->sharedBufferPtr->volumeData[i] = val;
+		change = 1;
+	}
+
+	if (i >= 8) {
+		if (u->value.integer.value[1] != abs(korg1212->sharedBufferPtr->volumeData[i+1])) {
+			val = korg1212->volumePhase[i+1] > 0 ? -1 : 1;
+			val *= u->value.integer.value[1];
+			korg1212->sharedBufferPtr->volumeData[i+1] = val;
+			change = 1;
+		}
+	}
+
+	spin_unlock_irqrestore(&korg1212->lock, flags);
+
+        return change;
+}
+
+static int snd_korg1212_control_route_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = (kcontrol->private_value >= 8) ? 2 : 1;
+	uinfo->value.enumerated.items = kAudioChannels;
+	if (uinfo->value.enumerated.item > kAudioChannels-1) {
+		uinfo->value.enumerated.item = kAudioChannels-1;
+	}
+	strcpy(uinfo->value.enumerated.name, channelName[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int snd_korg1212_control_route_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u)
+{
+	korg1212_t *korg1212 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+        int i;
+
+	spin_lock_irqsave(&korg1212->lock, flags);
+
+	i = kcontrol->private_value;
+	u->value.enumerated.item[0] = korg1212->sharedBufferPtr->routeData[i];
+
+	if (i >= 8) 
+		u->value.enumerated.item[1] = korg1212->sharedBufferPtr->routeData[i+1];
+
+        spin_unlock_irqrestore(&korg1212->lock, flags);
+
+        return 0;
+}
+
+static int snd_korg1212_control_route_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u)
+{
+	korg1212_t *korg1212 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+        int change = 0, i;
+
+	spin_lock_irqsave(&korg1212->lock, flags);
+
+	i = kcontrol->private_value;
+
+	if (u->value.enumerated.item[0] != korg1212->sharedBufferPtr->volumeData[i]) {
+		korg1212->sharedBufferPtr->routeData[i] = u->value.enumerated.item[0];
+		change = 1;
+	}
+
+	if (i >= 8) {
+		if (u->value.enumerated.item[1] != korg1212->sharedBufferPtr->volumeData[i+1]) {
+			korg1212->sharedBufferPtr->routeData[i+1] = u->value.enumerated.item[1];
+			change = 1;
+		}
+	}
+
+	spin_unlock_irqrestore(&korg1212->lock, flags);
+
+        return change;
+}
+
+static int snd_korg1212_control_analog_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+        uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+        uinfo->count = 2;
+        uinfo->value.integer.min = k1212MaxADCSens;
+	uinfo->value.integer.max = k1212MinADCSens;
+        return 0;
+}
+
+static int snd_korg1212_control_analog_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u)
+{
+	korg1212_t *korg1212 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+
+	spin_lock_irqsave(&korg1212->lock, flags);
+
+        u->value.integer.value[0] = korg1212->leftADCInSens;
+        u->value.integer.value[1] = korg1212->rightADCInSens;
+
+        spin_unlock_irqrestore(&korg1212->lock, flags);
+
+        return 0;
+}
+
+static int snd_korg1212_control_analog_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u)
+{
+	korg1212_t *korg1212 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+        int change = 0;
+
+	spin_lock_irqsave(&korg1212->lock, flags);
+
+        if (u->value.integer.value[0] != korg1212->leftADCInSens) {
+                korg1212->leftADCInSens = u->value.integer.value[0];
+                change = 1;
+        }
+        if (u->value.integer.value[1] != korg1212->rightADCInSens) {
+                korg1212->rightADCInSens = u->value.integer.value[1];
+                change = 1;
+        }
+
+        if (change)
+                snd_korg1212_WriteADCSensitivity(korg1212);
+
+	spin_unlock_irqrestore(&korg1212->lock, flags);
+
+        return change;
+}
+
+static int snd_korg1212_control_sync_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 3;
+	if (uinfo->value.enumerated.item > 2) {
+		uinfo->value.enumerated.item = 2;
+	}
+	strcpy(uinfo->value.enumerated.name, clockSourceTypeName[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int snd_korg1212_control_sync_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	korg1212_t *korg1212 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+
+	spin_lock_irqsave(&korg1212->lock, flags);
+
+	ucontrol->value.enumerated.item[0] = korg1212->clkSource;
+
+	spin_unlock_irqrestore(&korg1212->lock, flags);
+	return 0;
+}
+
+static int snd_korg1212_control_sync_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	korg1212_t *korg1212 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	unsigned int val;
+	int change;
+
+	val = ucontrol->value.enumerated.item[0] % 3;
+	spin_lock_irqsave(&korg1212->lock, flags);
+	change = val != korg1212->clkSource;
+        snd_korg1212_SetClockSource(korg1212, val);
+	spin_unlock_irqrestore(&korg1212->lock, flags);
+	return change;
+}
+
+#define MON_MIXER(ord,c_name)									\
+        {											\
+                access:		SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_WRITE,	\
+                iface:          SNDRV_CTL_ELEM_IFACE_MIXER,					\
+                name:		c_name " Monitor Volume",						\
+                info:		snd_korg1212_control_volume_info,				\
+                get:		snd_korg1212_control_volume_get,				\
+                put:		snd_korg1212_control_volume_put,				\
+		private_value:	ord,								\
+        },                                                                                      \
+        {											\
+                access:		SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_WRITE,	\
+                iface:          SNDRV_CTL_ELEM_IFACE_MIXER,					\
+                name:		c_name " Monitor Route",						\
+                info:		snd_korg1212_control_route_info,				\
+                get:		snd_korg1212_control_route_get,					\
+                put:		snd_korg1212_control_route_put,					\
+		private_value:	ord,								\
+        },                                                                                      \
+        {											\
+                access:		SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_WRITE,	\
+                iface:          SNDRV_CTL_ELEM_IFACE_PCM,					\
+                name:		c_name " Monitor Phase Invert",						\
+                info:		snd_korg1212_control_phase_info,				\
+                get:		snd_korg1212_control_phase_get,					\
+                put:		snd_korg1212_control_phase_put,					\
+		private_value:	ord,								\
+        }
+
+static snd_kcontrol_new_t snd_korg1212_controls[] = {
+        MON_MIXER(8, "Analog"),
+	MON_MIXER(10, "SPDIF"), 
+        MON_MIXER(0, "ADAT-1"), MON_MIXER(1, "ADAT-2"), MON_MIXER(2, "ADAT-3"), MON_MIXER(3, "ADAT-4"),
+        MON_MIXER(4, "ADAT-5"), MON_MIXER(5, "ADAT-6"), MON_MIXER(6, "ADAT-7"), MON_MIXER(7, "ADAT-8"),
+	{
+                access:		SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_WRITE,
+                iface:          SNDRV_CTL_ELEM_IFACE_PCM,
+                name:		"Sync Source",
+                info:		snd_korg1212_control_sync_info,
+                get:		snd_korg1212_control_sync_get,
+                put:		snd_korg1212_control_sync_put,
+        },
+        {
+                access:		SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_WRITE,
+                iface:          SNDRV_CTL_ELEM_IFACE_MIXER,
+                name:		"ADC Attenuation",
+                info:		snd_korg1212_control_analog_info,
+                get:		snd_korg1212_control_analog_get,
+                put:		snd_korg1212_control_analog_put,
+        }
+};
+
+#define K1212_CONTROL_ELEMENTS (sizeof(snd_korg1212_controls) / sizeof(snd_korg1212_controls[0]))
+
+/*
+ * proc interface
+ */
+
+static void snd_korg1212_proc_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer)
+{
+	int n;
+	korg1212_t *korg1212 = (korg1212_t *)entry->private_data;
+
+	snd_iprintf(buffer, korg1212->card->longname);
+	snd_iprintf(buffer, " (index #%d)\n", korg1212->card->number + 1);
+	snd_iprintf(buffer, "\nGeneral settings\n");
+	snd_iprintf(buffer, "    period size: %d bytes\n", K1212_BLOCK_SIZE);
+	snd_iprintf(buffer, "     clock mode: %s\n", clockSourceName[korg1212->clkSrcRate] );
+	snd_iprintf(buffer, "  left ADC Sens: %d\n", korg1212->leftADCInSens );
+	snd_iprintf(buffer, " right ADC Sens: %d\n", korg1212->rightADCInSens );
+        snd_iprintf(buffer, "    Volume Info:\n");
+        for (n=0; n<kAudioChannels; n++)
+                snd_iprintf(buffer, " Channel %d: %s -> %s [%d]\n", n,
+                                    channelName[n],
+                                    channelName[korg1212->sharedBufferPtr->routeData[n]],
+                                    korg1212->sharedBufferPtr->volumeData[n]);
+	snd_iprintf(buffer, "\nGeneral status\n");
+        snd_iprintf(buffer, " ADAT Time Code: %d\n", korg1212->sharedBufferPtr->AdatTimeCode);
+        snd_iprintf(buffer, "     Card State: %s\n", stateName[korg1212->cardState]);
+        snd_iprintf(buffer, "Idle mon. State: %d\n", korg1212->idleMonitorOn);
+        snd_iprintf(buffer, "Cmd retry count: %d\n", korg1212->cmdRetryCount);
+        snd_iprintf(buffer, "      Irq count: %ld\n", korg1212->irqcount);
+}
+
+static void __init snd_korg1212_proc_init(korg1212_t *korg1212)
+{
+	snd_info_entry_t *entry;
+
+	if ((entry = snd_info_create_card_entry(korg1212->card, "korg1212", korg1212->card->proc_root)) != NULL) {
+		entry->content = SNDRV_INFO_CONTENT_TEXT;
+		entry->private_data = korg1212;
+		entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
+		entry->c.text.read_size = 256;
+		entry->c.text.read = snd_korg1212_proc_read;
+		if (snd_info_register(entry) < 0) {
+			snd_info_free_entry(entry);
+			entry = NULL;
+		}
+	}
+	korg1212->proc_entry = entry;
+}
+
+static void snd_korg1212_proc_done(korg1212_t * korg1212)
+{
+	if (korg1212->proc_entry) {
+		snd_info_unregister(korg1212->proc_entry);
+		korg1212->proc_entry = NULL;
+	}
+}
+
+static int __init snd_korg1212_create(korg1212_t *korg1212)
+{
+        struct pci_dev *pci = korg1212->pci;
+        int err;
+        int i;
+	unsigned ioport_size, iomem_size, iomem2_size;
+	dma_addr_t phys_addr;
+
+        korg1212->irq = -1;
+        korg1212->clkSource = K1212_CLKIDX_Local;
+        korg1212->clkRate = 44100;
+        korg1212->inIRQ = 0;
+        korg1212->running = 0;
+        snd_korg1212_setCardState(korg1212, K1212_STATE_UNINITIALIZED);
+        korg1212->idleMonitorOn = 0;
+        korg1212->clkSrcRate = K1212_CLKIDX_LocalAt44_1K;
+        korg1212->leftADCInSens = k1212MaxADCSens;
+        korg1212->rightADCInSens = k1212MaxADCSens;
+
+        for (i=0; i<kAudioChannels; i++)
+                korg1212->volumePhase[i] = 0;
+
+        if ((err = pci_enable_device(pci)) < 0)
+                return err;
+
+        korg1212->iomem = pci_resource_start(korg1212->pci, 0);
+        korg1212->ioport = pci_resource_start(korg1212->pci, 1);
+        korg1212->iomem2 = pci_resource_start(korg1212->pci, 2);
+
+	iomem_size = pci_resource_len(korg1212->pci, 0);
+	ioport_size = pci_resource_len(korg1212->pci, 1);
+	iomem2_size = pci_resource_len(korg1212->pci, 2);
+
+#ifdef DEBUG
+        PRINTK("DEBUG: resources:\n"
+                   "    iomem = 0x%lx (%d)\n"
+		   "    ioport  = 0x%lx (%d)\n"
+                   "    iomem = 0x%lx (%d)\n"
+		   "    [%s]\n",
+		   korg1212->iomem, iomem_size,
+		   korg1212->ioport, ioport_size,
+		   korg1212->iomem2, iomem2_size,
+		   stateName[korg1212->cardState]);
+#endif
+
+        korg1212->res_iomem = request_mem_region(korg1212->iomem, iomem_size, "korg1212");
+        if (korg1212->res_iomem == NULL) {
+		snd_printk("unable to grab region 0x%lx-0x%lx\n",
+                           korg1212->iomem, korg1212->iomem + iomem_size - 1);
+                return -EBUSY;
+        }
+
+        korg1212->res_ioport = request_region(korg1212->ioport, ioport_size, "korg1212");
+        if (korg1212->res_ioport == NULL) {
+		snd_printk("unable to grab region 0x%lx-0x%lx\n",
+                           korg1212->ioport, korg1212->ioport + ioport_size - 1);
+                return -EBUSY;
+        }
+
+        korg1212->res_iomem2 = request_mem_region(korg1212->iomem2, iomem2_size, "korg1212");
+        if (korg1212->res_iomem2 == NULL) {
+		snd_printk("unable to grab region 0x%lx-0x%lx\n",
+                           korg1212->iomem2, korg1212->iomem2 + iomem2_size - 1);
+                return -EBUSY;
+        }
+
+        if ((korg1212->iobase = (unsigned long) ioremap(korg1212->iomem, iomem_size)) == 0) {
+		snd_printk("unable to remap memory region 0x%lx-0x%lx\n", korg1212->iobase,
+                           korg1212->iobase + iomem_size - 1);
+                return -EBUSY;
+        }
+
+        err = request_irq(pci->irq, snd_korg1212_interrupt,
+                          SA_INTERRUPT|SA_SHIRQ,
+                          "korg1212", (void *) korg1212);
+
+        if (err) {
+		snd_printk("unable to grab IRQ %d\n", pci->irq);
+                return -EBUSY;
+        }
+
+        korg1212->irq = pci->irq;
+
+        init_waitqueue_head(&korg1212->wait);
+        spin_lock_init(&korg1212->lock);
+	pci_set_master(korg1212->pci);
+
+        korg1212->statusRegPtr = (u32 *) (korg1212->iobase + STATUS_REG_OFFSET);
+        korg1212->outDoorbellPtr = (u32 *) (korg1212->iobase + OUT_DOORBELL_OFFSET);
+        korg1212->inDoorbellPtr = (u32 *) (korg1212->iobase + IN_DOORBELL_OFFSET);
+        korg1212->mailbox0Ptr = (u32 *) (korg1212->iobase + MAILBOX0_OFFSET);
+        korg1212->mailbox1Ptr = (u32 *) (korg1212->iobase + MAILBOX1_OFFSET);
+        korg1212->mailbox2Ptr = (u32 *) (korg1212->iobase + MAILBOX2_OFFSET);
+        korg1212->mailbox3Ptr = (u32 *) (korg1212->iobase + MAILBOX3_OFFSET);
+        korg1212->controlRegPtr = (u32 *) (korg1212->iobase + PCI_CONTROL_OFFSET);
+        korg1212->sensRegPtr = (u16 *) (korg1212->iobase + SENS_CONTROL_OFFSET);
+        korg1212->idRegPtr = (u32 *) (korg1212->iobase + DEV_VEND_ID_OFFSET);
+
+#ifdef DEBUG
+        PRINTK("DEBUG: card registers:\n"
+                   "    Status register = 0x%p\n"
+                   "    OutDoorbell     = 0x%p\n"
+                   "    InDoorbell      = 0x%p\n"
+                   "    Mailbox0        = 0x%p\n"
+                   "    Mailbox1        = 0x%p\n"
+                   "    Mailbox2        = 0x%p\n"
+                   "    Mailbox3        = 0x%p\n"
+                   "    ControlReg      = 0x%p\n"
+                   "    SensReg         = 0x%p\n"
+                   "    IDReg           = 0x%p\n"
+		   "    [%s]\n",
+                   korg1212->statusRegPtr,
+		   korg1212->outDoorbellPtr,
+		   korg1212->inDoorbellPtr,
+                   korg1212->mailbox0Ptr,
+                   korg1212->mailbox1Ptr,
+                   korg1212->mailbox2Ptr,
+                   korg1212->mailbox3Ptr,
+                   korg1212->controlRegPtr,
+                   korg1212->sensRegPtr,
+                   korg1212->idRegPtr,
+		   stateName[korg1212->cardState]);
+#endif
+
+	korg1212->sharedBufferPtr = (KorgSharedBuffer *) snd_malloc_pci_pages(korg1212->pci, sizeof(KorgSharedBuffer), &phys_addr);
+	korg1212->sharedBufferPhy = (unsigned long)phys_addr;
+
+        if (korg1212->sharedBufferPtr == NULL) {
+		snd_printk("can not allocate shared buffer memory (%d bytes)\n", sizeof(KorgSharedBuffer));
+                return -ENOMEM;
+        }
+
+#ifdef DEBUG
+        PRINTK("DEBUG: Shared Buffer Area = 0x%p (0x%08lx), %d bytes\n", korg1212->sharedBufferPtr, korg1212->sharedBufferPhy, sizeof(KorgSharedBuffer));
+#endif
+
+#ifndef LARGEALLOC
+
+        korg1212->DataBufsSize = sizeof(KorgAudioBuffer) * kNumBuffers;
+
+	korg1212->playDataBufsPtr = (KorgAudioBuffer *) snd_malloc_pci_pages(korg1212->pci, korg1212->DataBufsSize, &phys_addr);
+	korg1212->PlayDataPhy = (u32)phys_addr;
+
+        if (korg1212->playDataBufsPtr == NULL) {
+		snd_printk("can not allocate play data buffer memory (%d bytes)\n", korg1212->DataBufsSize);
+                return -ENOMEM;
+        }
+
+#ifdef DEBUG
+        PRINTK("DEBUG: Play Data Area = 0x%p (0x%08x), %d bytes\n",
+		korg1212->playDataBufsPtr, korg1212->PlayDataPhy, korg1212->DataBufsSize);
+#endif
+
+	korg1212->recordDataBufsPtr = (KorgAudioBuffer *) snd_malloc_pci_pages(korg1212->pci, korg1212->DataBufsSize, &phys_addr);
+	korg1212->RecDataPhy = (u32)phys_addr;
+
+        if (korg1212->recordDataBufsPtr == NULL) {
+		snd_printk("can not allocate record data buffer memory (%d bytes)\n", korg1212->DataBufsSize);
+                return -ENOMEM;
+        }
+
+#ifdef DEBUG
+        PRINTK("DEBUG: Record Data Area = 0x%p (0x%08x), %d bytes\n",
+		korg1212->recordDataBufsPtr, korg1212->RecDataPhy, korg1212->DataBufsSize);
+#endif
+
+#else // LARGEALLOC
+
+        korg1212->recordDataBufsPtr = korg1212->sharedBufferPtr->recordDataBufs;
+        korg1212->playDataBufsPtr = korg1212->sharedBufferPtr->playDataBufs;
+        korg1212->PlayDataPhy = (u32) &((KorgSharedBuffer *) korg1212->sharedBufferPhy)->playDataBufs;
+        korg1212->RecDataPhy  = (u32) &((KorgSharedBuffer *) korg1212->sharedBufferPhy)->recordDataBufs;
+
+#endif // LARGEALLOC
+
+        korg1212->dspCodeSize = sizeof (dspCode);
+
+        korg1212->VolumeTablePhy = (u32) &((KorgSharedBuffer *) korg1212->sharedBufferPhy)->volumeData;
+        korg1212->RoutingTablePhy = (u32) &((KorgSharedBuffer *) korg1212->sharedBufferPhy)->routeData;
+        korg1212->AdatTimeCodePhy = (u32) &((KorgSharedBuffer *) korg1212->sharedBufferPhy)->AdatTimeCode;
+
+        korg1212->dspMemPtr = snd_malloc_pci_pages(korg1212->pci, korg1212->dspCodeSize, &phys_addr);
+	korg1212->dspMemPhy = (u32)phys_addr;
+
+        if (korg1212->dspMemPtr == NULL) {
+		snd_printk("can not allocate dsp code memory (%d bytes)\n", korg1212->dspCodeSize);
+                return -ENOMEM;
+        }
+
+#ifdef DEBUG
+        PRINTK("DEBUG: DSP Code area = 0x%p (0x%08x) %d bytes [%s]\n",
+		   korg1212->dspMemPtr, korg1212->dspMemPhy, korg1212->dspCodeSize,
+		   stateName[korg1212->cardState]);
+#endif
+
+	rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_RebootCard, 0, 0, 0, 0);
+
+#ifdef DEBUG
+	if (rc) PRINTK("DEBUG: Reboot Card - RC = %d [%s]\n", rc, stateName[korg1212->cardState]);
+#endif
+
+	snd_korg1212_EnableCardInterrupts(korg1212);
+
+	mdelay(CARD_BOOT_DELAY_IN_MS);
+
+        if (snd_korg1212_downloadDSPCode(korg1212))
+        	return -EBUSY;
+
+	printk(KERN_INFO "dspMemPhy       = %08x U[%08x]\n"
+               "PlayDataPhy     = %08x L[%08x]\n"
+               "RecDataPhy      = %08x L[%08x]\n"
+               "VolumeTablePhy  = %08x L[%08x]\n"
+               "RoutingTablePhy = %08x L[%08x]\n"
+               "AdatTimeCodePhy = %08x L[%08x]\n",
+	       korg1212->dspMemPhy,       UpperWordSwap(korg1212->dspMemPhy),
+               korg1212->PlayDataPhy,     LowerWordSwap(korg1212->PlayDataPhy),
+               korg1212->RecDataPhy,      LowerWordSwap(korg1212->RecDataPhy),
+               korg1212->VolumeTablePhy,  LowerWordSwap(korg1212->VolumeTablePhy),
+               korg1212->RoutingTablePhy, LowerWordSwap(korg1212->RoutingTablePhy),
+               korg1212->AdatTimeCodePhy, LowerWordSwap(korg1212->AdatTimeCodePhy));
+
+        if ((err = snd_pcm_new(korg1212->card, "korg1212", 0, 1, 1, &korg1212->pcm16)) < 0)
+                return err;
+
+        korg1212->pcm16->private_data = korg1212;
+        korg1212->pcm16->private_free = snd_korg1212_free_pcm;
+        strcpy(korg1212->pcm16->name, "korg1212");
+
+        snd_pcm_set_ops(korg1212->pcm16, SNDRV_PCM_STREAM_PLAYBACK, &snd_korg1212_playback_ops);
+        snd_pcm_set_ops(korg1212->pcm16, SNDRV_PCM_STREAM_CAPTURE, &snd_korg1212_capture_ops);
+
+	korg1212->pcm16->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX;
+
+        for (i = 0; i < K1212_CONTROL_ELEMENTS; i++) {
+                err = snd_ctl_add(korg1212->card, snd_ctl_new1(&snd_korg1212_controls[i], korg1212));
+                if (err < 0)
+                        return err;
+        }
+
+        snd_korg1212_proc_init(korg1212);
+
+	return 0;
+
+}
+
+static void
+snd_korg1212_free(void *private_data)
+{
+        korg1212_t *korg1212 = (korg1212_t *)private_data;
+
+        if (korg1212 == NULL) {
+                return;
+        }
+
+        snd_korg1212_proc_done(korg1212);
+
+        snd_korg1212_TurnOffIdleMonitor(korg1212);
+
+        snd_korg1212_DisableCardInterrupts(korg1212);
+
+        if (korg1212->irq >= 0) {
+                free_irq(korg1212->irq, (void *)korg1212);
+                korg1212->irq = -1;
+        }
+        if (korg1212->iobase != 0) {
+                iounmap((void *)korg1212->iobase);
+                korg1212->iobase = 0;
+        }
+        if (korg1212->res_iomem != NULL) {
+                release_resource(korg1212->res_iomem);
+                kfree_nocheck(korg1212->res_iomem);
+                korg1212->res_iomem = NULL;
+        }
+        if (korg1212->res_ioport != NULL) {
+                release_resource(korg1212->res_ioport);
+                kfree_nocheck(korg1212->res_ioport);
+                korg1212->res_ioport = NULL;
+        }
+        if (korg1212->res_iomem2 != NULL) {
+                release_resource(korg1212->res_iomem2);
+                kfree_nocheck(korg1212->res_iomem2);
+                korg1212->res_iomem2 = NULL;
+        }
+
+        // ----------------------------------------------------
+        // free up memory resources used for the DSP download.
+        // ----------------------------------------------------
+        if (korg1212->dspMemPtr) {
+                snd_free_pci_pages(korg1212->pci, korg1212->dspCodeSize,
+                                   korg1212->dspMemPtr, (dma_addr_t)korg1212->dspMemPhy);
+                korg1212->dspMemPhy = 0;
+                korg1212->dspMemPtr = 0;
+                korg1212->dspCodeSize = 0;
+        }
+
+#ifndef LARGEALLOC
+
+        // ------------------------------------------------------
+        // free up memory resources used for the Play/Rec Buffers
+        // ------------------------------------------------------
+	if (korg1212->playDataBufsPtr) {
+                snd_free_pci_pages(korg1212->pci, korg1212->DataBufsSize,
+                                   korg1212->playDataBufsPtr, (dma_addr_t)korg1212->PlayDataPhy);
+		korg1212->PlayDataPhy = 0;
+		korg1212->playDataBufsPtr = NULL;
+        }
+
+	if (korg1212->recordDataBufsPtr) {
+                snd_free_pci_pages(korg1212->pci, korg1212->DataBufsSize,
+                                   korg1212->recordDataBufsPtr, (dma_addr_t)korg1212->RecDataPhy);
+		korg1212->RecDataPhy = 0;
+		korg1212->recordDataBufsPtr = NULL;
+        }
+
+#endif
+
+        // ----------------------------------------------------
+        // free up memory resources used for the Shared Buffers
+        // ----------------------------------------------------
+	if (korg1212->sharedBufferPtr) {
+                snd_free_pci_pages(korg1212->pci, (u32) sizeof(KorgSharedBuffer),
+                                   korg1212->sharedBufferPtr, (dma_addr_t)korg1212->sharedBufferPhy);
+		korg1212->sharedBufferPhy = 0;
+		korg1212->sharedBufferPtr = NULL;
+        }
+}
+
+/*
+ * Card initialisation
+ */
+
+static void snd_korg1212_card_free(snd_card_t *card)
+{
+#ifdef DEBUG
+        PRINTK("DEBUG: Freeing card\n");
+#endif
+	snd_korg1212_free(card->private_data);
+}
+
+static int __devinit
+snd_korg1212_probe(struct pci_dev *pci,
+		const struct pci_device_id *id)
+{
+	static int dev = 0;
+	korg1212_t *korg1212;
+	snd_card_t *card;
+	int err;
+
+	if (dev >= SNDRV_CARDS) {
+		return -ENODEV;
+	}
+	if (!snd_enable[dev]) {
+		dev++;
+		return -ENOENT;
+	}
+	if ((card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE,
+				 sizeof(korg1212_t))) == NULL)
+		return -ENOMEM;
+
+	card->private_free = snd_korg1212_card_free;
+	korg1212 = (korg1212_t *)card->private_data;
+	korg1212->card = card;
+	korg1212->pci = pci;
+
+	if ((err = snd_korg1212_create(korg1212)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	strcpy(card->driver, "korg1212");
+	strcpy(card->shortname, "korg1212");
+	sprintf(card->longname, "%s at 0x%lx, irq %d", card->shortname,
+		korg1212->iomem, korg1212->irq);
+
+#ifdef DEBUG
+        PRINTK("DEBUG: %s\n", card->longname);
+#endif
+
+	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_korg1212_remove(struct pci_dev *pci)
+{
+	snd_card_free(pci_get_drvdata(pci));
+	pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver driver = {
+	name: "korg1212",
+	id_table: snd_korg1212_ids,
+	probe: snd_korg1212_probe,
+	remove: __devexit_p(snd_korg1212_remove),
+};
+
+static int __init alsa_card_korg1212_init(void)
+{
+	int err;
+
+	if ((err = pci_module_init(&driver)) < 0) {
+#ifdef MODULE
+		printk(KERN_ERR "No Korg 1212IO cards found\n");
+#endif
+		return err;
+	}
+	return 0;
+}
+
+static void __exit alsa_card_korg1212_exit(void)
+{
+	pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_korg1212_init)
+module_exit(alsa_card_korg1212_exit)
+
+#ifndef MODULE
+
+/* format is: snd-korg1212=snd_enable,snd_index,snd_id */
+
+static int __init alsa_card_korg1212_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);
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-korg1212=", alsa_card_korg1212_setup);
+
+#endif /* ifndef MODULE */
+
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/korg1212/makefile.os2
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/korg1212/makefile.os2	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/korg1212/makefile.os2	(revision 91)
@@ -0,0 +1,51 @@
+CINCLUDES = ..\..\..\include;..\..\include;$(%WATCOM)\H
+AINCLUDES = $(DDK_BASE)\H
+
+CDEFINES = -D__KERNEL__ -DMODULE -dTARGET_OS2 -dALSA_BUILD -D__ISAPNP__ -DCONFIG_ISAPNP -D__i386_
+
+32BIT=1
+!include ..\..\..\..\makefile.inc
+
+
+#===================================================================
+#
+#   List of source files
+#
+#===================================================================
+FILE1    = korg1212.obj
+FILE2    = 
+FILE3    = 
+FILE4    = 
+FILE5    = 
+FILE6    = 
+FILE7    = 
+FILE8    = 
+FILELAST =
+FILES    = $(FILE0) $(FILE1) $(FILE2) $(FILE3) $(FILE4) $(FILE5) $(FILE6) $(FILE7) $(FILE8) $(FILE9) $(FILE10) $(FILE11) $(FILE12)
+
+TARGET   = korg1212
+
+LNKFILE  = $(OBJDIR)\$(TARGET).lnk
+
+
+#===================================================================
+#
+#   Specific dependencies
+#
+#===================================================================
+all: $(OBJDIR)\$(TARGET).lib 
+
+
+$(LNKFILE): makefile.os2
+    @%create $(LNKFILE)
+    @for %f in ($(FILES)) do @%append $(LNKFILE) -+$(OBJDIR)\%f
+
+$(OBJDIR)\$(TARGET).lib: $(LNKFILE) $(FILES)
+    $(LIB) $(OBJDIR)\$(TARGET).lib @$(LNKFILE)
+    copy $(OBJDIR)\$(TARGET).lib $(ALSA_LIB) 
+
+clean: .SYMBOLIC
+    -@rm  *.err 2>nul
+    @cd $(OBJDIR)
+    -rm *.obj *.lnk *.sys *.sym *.lst *.map *.wmap *.lib 2>nul
+    @cd ..\..
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/maestro3.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/maestro3.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/maestro3.c	(revision 91)
@@ -0,0 +1,2962 @@
+/*
+ * Driver for ESS Maestro3/Allegro (ES1988) soundcards.
+ * Copyright (c) 2000 by Zach Brown <zab@zabbo.net>
+ *                       Takashi Iwai <tiwai@suse.de>
+ *
+ * Most of the hardware init stuffs are based on maestro3 driver for
+ * OSS/Free by Zach Brown.  Many thanks to Zach!
+ *
+ *   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
+ *
+ *
+ * ChangeLog:
+ * Aug. 27, 2001
+ *     - Fixed deadlock on capture
+ *     - Added Canyon3D-2 support by Rob Riggs <rob@pangalactic.org>
+ *
+ */
+
+#define CARD_NAME "ESS Maestro3/Allegro/Canyon3D-2"
+#define DRIVER_NAME "Maestro3"
+
+#include <sound/driver.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+MODULE_AUTHOR("Zach Brown <zab@zabbo.net>, Takashi Iwai <tiwai@suse.de>");
+MODULE_DESCRIPTION("ESS Maestro3 PCI");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{ESS,Maestro3 PCI},"
+               "{ESS,ES1988},"
+               "{ESS,Allegro PCI},"
+               "{ESS,Allegro-1 PCI},"
+               "{ESS,Canyon3D-2/LE PCI}}");
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* all enabled */
+static int external_amp[SNDRV_CARDS] = {REPEAT_SNDRV(1)};
+static int amp_gpio[SNDRV_CARDS] = {REPEAT_SNDRV(-1)};
+
+MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard.");
+MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC);
+MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard.");
+MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC);
+MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(enable, "Enable this soundcard.");
+MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(external_amp, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(external_amp, "Enable external amp for " CARD_NAME " soundcard.");
+MODULE_PARM_SYNTAX(external_amp, SNDRV_ENABLED "," SNDRV_BOOLEAN_TRUE_DESC);
+MODULE_PARM(amp_gpio, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(amp_gpio, "GPIO pin number for external amp. (default = -1)");
+MODULE_PARM_SYNTAX(amp_gpio, SNDRV_ENABLED);
+
+#define MAX_PLAYBACKS	2
+#define MAX_CAPTURES	1
+#define NR_DSPS		(MAX_PLAYBACKS + MAX_CAPTURES)
+
+
+/*
+ * maestro3 registers
+ */
+
+/* Allegro PCI configuration registers */
+#define PCI_LEGACY_AUDIO_CTRL   0x40
+#define SOUND_BLASTER_ENABLE    0x00000001
+#define FM_SYNTHESIS_ENABLE     0x00000002
+#define GAME_PORT_ENABLE        0x00000004
+#define MPU401_IO_ENABLE        0x00000008
+#define MPU401_IRQ_ENABLE       0x00000010
+#define ALIAS_10BIT_IO          0x00000020
+#define SB_DMA_MASK             0x000000C0
+#define SB_DMA_0                0x00000040
+#define SB_DMA_1                0x00000040
+#define SB_DMA_R                0x00000080
+#define SB_DMA_3                0x000000C0
+#define SB_IRQ_MASK             0x00000700
+#define SB_IRQ_5                0x00000000
+#define SB_IRQ_7                0x00000100
+#define SB_IRQ_9                0x00000200
+#define SB_IRQ_10               0x00000300
+#define MIDI_IRQ_MASK           0x00003800
+#define SERIAL_IRQ_ENABLE       0x00004000
+#define DISABLE_LEGACY          0x00008000
+
+#define PCI_ALLEGRO_CONFIG      0x50
+#define SB_ADDR_240             0x00000004
+#define MPU_ADDR_MASK           0x00000018
+#define MPU_ADDR_330            0x00000000
+#define MPU_ADDR_300            0x00000008
+#define MPU_ADDR_320            0x00000010
+#define MPU_ADDR_340            0x00000018
+#define USE_PCI_TIMING          0x00000040
+#define POSTED_WRITE_ENABLE     0x00000080
+#define DMA_POLICY_MASK         0x00000700
+#define DMA_DDMA                0x00000000
+#define DMA_TDMA                0x00000100
+#define DMA_PCPCI               0x00000200
+#define DMA_WBDMA16             0x00000400
+#define DMA_WBDMA4              0x00000500
+#define DMA_WBDMA2              0x00000600
+#define DMA_WBDMA1              0x00000700
+#define DMA_SAFE_GUARD          0x00000800
+#define HI_PERF_GP_ENABLE       0x00001000
+#define PIC_SNOOP_MODE_0        0x00002000
+#define PIC_SNOOP_MODE_1        0x00004000
+#define SOUNDBLASTER_IRQ_MASK   0x00008000
+#define RING_IN_ENABLE          0x00010000
+#define SPDIF_TEST_MODE         0x00020000
+#define CLK_MULT_MODE_SELECT_2  0x00040000
+#define EEPROM_WRITE_ENABLE     0x00080000
+#define CODEC_DIR_IN            0x00100000
+#define HV_BUTTON_FROM_GD       0x00200000
+#define REDUCED_DEBOUNCE        0x00400000
+#define HV_CTRL_ENABLE          0x00800000
+#define SPDIF_ENABLE            0x01000000
+#define CLK_DIV_SELECT          0x06000000
+#define CLK_DIV_BY_48           0x00000000
+#define CLK_DIV_BY_49           0x02000000
+#define CLK_DIV_BY_50           0x04000000
+#define CLK_DIV_RESERVED        0x06000000
+#define PM_CTRL_ENABLE          0x08000000
+#define CLK_MULT_MODE_SELECT    0x30000000
+#define CLK_MULT_MODE_SHIFT     28
+#define CLK_MULT_MODE_0         0x00000000
+#define CLK_MULT_MODE_1         0x10000000
+#define CLK_MULT_MODE_2         0x20000000
+#define CLK_MULT_MODE_3         0x30000000
+#define INT_CLK_SELECT          0x40000000
+#define INT_CLK_MULT_RESET      0x80000000
+
+/* M3 */
+#define INT_CLK_SRC_NOT_PCI     0x00100000
+#define INT_CLK_MULT_ENABLE     0x80000000
+
+#define PCI_ACPI_CONTROL        0x54
+#define PCI_ACPI_D0             0x00000000
+#define PCI_ACPI_D1             0xB4F70000
+#define PCI_ACPI_D2             0xB4F7B4F7
+
+#define PCI_USER_CONFIG         0x58
+#define EXT_PCI_MASTER_ENABLE   0x00000001
+#define SPDIF_OUT_SELECT        0x00000002
+#define TEST_PIN_DIR_CTRL       0x00000004
+#define AC97_CODEC_TEST         0x00000020
+#define TRI_STATE_BUFFER        0x00000080
+#define IN_CLK_12MHZ_SELECT     0x00000100
+#define MULTI_FUNC_DISABLE      0x00000200
+#define EXT_MASTER_PAIR_SEL     0x00000400
+#define PCI_MASTER_SUPPORT      0x00000800
+#define STOP_CLOCK_ENABLE       0x00001000
+#define EAPD_DRIVE_ENABLE       0x00002000
+#define REQ_TRI_STATE_ENABLE    0x00004000
+#define REQ_LOW_ENABLE          0x00008000
+#define MIDI_1_ENABLE           0x00010000
+#define MIDI_2_ENABLE           0x00020000
+#define SB_AUDIO_SYNC           0x00040000
+#define HV_CTRL_TEST            0x00100000
+#define SOUNDBLASTER_TEST       0x00400000
+
+#define PCI_USER_CONFIG_C       0x5C
+
+#define PCI_DDMA_CTRL           0x60
+#define DDMA_ENABLE             0x00000001
+
+
+/* Allegro registers */
+#define HOST_INT_CTRL           0x18
+#define SB_INT_ENABLE           0x0001
+#define MPU401_INT_ENABLE       0x0002
+#define ASSP_INT_ENABLE         0x0010
+#define RING_INT_ENABLE         0x0020
+#define HV_INT_ENABLE           0x0040
+#define CLKRUN_GEN_ENABLE       0x0100
+#define HV_CTRL_TO_PME          0x0400
+#define SOFTWARE_RESET_ENABLE   0x8000
+
+/*
+ * should be using the above defines, probably.
+ */
+#define REGB_ENABLE_RESET               0x01
+#define REGB_STOP_CLOCK                 0x10
+
+#define HOST_INT_STATUS         0x1A
+#define SB_INT_PENDING          0x01
+#define MPU401_INT_PENDING      0x02
+#define ASSP_INT_PENDING        0x10
+#define RING_INT_PENDING        0x20
+#define HV_INT_PENDING          0x40
+
+#define HARDWARE_VOL_CTRL       0x1B
+#define SHADOW_MIX_REG_VOICE    0x1C
+#define HW_VOL_COUNTER_VOICE    0x1D
+#define SHADOW_MIX_REG_MASTER   0x1E
+#define HW_VOL_COUNTER_MASTER   0x1F
+
+#define CODEC_COMMAND           0x30
+#define CODEC_READ_B            0x80
+
+#define CODEC_STATUS            0x30
+#define CODEC_BUSY_B            0x01
+
+#define CODEC_DATA              0x32
+
+#define RING_BUS_CTRL_A         0x36
+#define RAC_PME_ENABLE          0x0100
+#define RAC_SDFS_ENABLE         0x0200
+#define LAC_PME_ENABLE          0x0400
+#define LAC_SDFS_ENABLE         0x0800
+#define SERIAL_AC_LINK_ENABLE   0x1000
+#define IO_SRAM_ENABLE          0x2000
+#define IIS_INPUT_ENABLE        0x8000
+
+#define RING_BUS_CTRL_B         0x38
+#define SECOND_CODEC_ID_MASK    0x0003
+#define SPDIF_FUNC_ENABLE       0x0010
+#define SECOND_AC_ENABLE        0x0020
+#define SB_MODULE_INTF_ENABLE   0x0040
+#define SSPE_ENABLE             0x0040
+#define M3I_DOCK_ENABLE         0x0080
+
+#define SDO_OUT_DEST_CTRL       0x3A
+#define COMMAND_ADDR_OUT        0x0003
+#define PCM_LR_OUT_LOCAL        0x0000
+#define PCM_LR_OUT_REMOTE       0x0004
+#define PCM_LR_OUT_MUTE         0x0008
+#define PCM_LR_OUT_BOTH         0x000C
+#define LINE1_DAC_OUT_LOCAL     0x0000
+#define LINE1_DAC_OUT_REMOTE    0x0010
+#define LINE1_DAC_OUT_MUTE      0x0020
+#define LINE1_DAC_OUT_BOTH      0x0030
+#define PCM_CLS_OUT_LOCAL       0x0000
+#define PCM_CLS_OUT_REMOTE      0x0040
+#define PCM_CLS_OUT_MUTE        0x0080
+#define PCM_CLS_OUT_BOTH        0x00C0
+#define PCM_RLF_OUT_LOCAL       0x0000
+#define PCM_RLF_OUT_REMOTE      0x0100
+#define PCM_RLF_OUT_MUTE        0x0200
+#define PCM_RLF_OUT_BOTH        0x0300
+#define LINE2_DAC_OUT_LOCAL     0x0000
+#define LINE2_DAC_OUT_REMOTE    0x0400
+#define LINE2_DAC_OUT_MUTE      0x0800
+#define LINE2_DAC_OUT_BOTH      0x0C00
+#define HANDSET_OUT_LOCAL       0x0000
+#define HANDSET_OUT_REMOTE      0x1000
+#define HANDSET_OUT_MUTE        0x2000
+#define HANDSET_OUT_BOTH        0x3000
+#define IO_CTRL_OUT_LOCAL       0x0000
+#define IO_CTRL_OUT_REMOTE      0x4000
+#define IO_CTRL_OUT_MUTE        0x8000
+#define IO_CTRL_OUT_BOTH        0xC000
+
+#define SDO_IN_DEST_CTRL        0x3C
+#define STATUS_ADDR_IN          0x0003
+#define PCM_LR_IN_LOCAL         0x0000
+#define PCM_LR_IN_REMOTE        0x0004
+#define PCM_LR_RESERVED         0x0008
+#define PCM_LR_IN_BOTH          0x000C
+#define LINE1_ADC_IN_LOCAL      0x0000
+#define LINE1_ADC_IN_REMOTE     0x0010
+#define LINE1_ADC_IN_MUTE       0x0020
+#define MIC_ADC_IN_LOCAL        0x0000
+#define MIC_ADC_IN_REMOTE       0x0040
+#define MIC_ADC_IN_MUTE         0x0080
+#define LINE2_DAC_IN_LOCAL      0x0000
+#define LINE2_DAC_IN_REMOTE     0x0400
+#define LINE2_DAC_IN_MUTE       0x0800
+#define HANDSET_IN_LOCAL        0x0000
+#define HANDSET_IN_REMOTE       0x1000
+#define HANDSET_IN_MUTE         0x2000
+#define IO_STATUS_IN_LOCAL      0x0000
+#define IO_STATUS_IN_REMOTE     0x4000
+
+#define SPDIF_IN_CTRL           0x3E
+#define SPDIF_IN_ENABLE         0x0001
+
+#define GPIO_DATA               0x60
+#define GPIO_DATA_MASK          0x0FFF
+#define GPIO_HV_STATUS          0x3000
+#define GPIO_PME_STATUS         0x4000
+
+#define GPIO_MASK               0x64
+#define GPIO_DIRECTION          0x68
+#define GPO_PRIMARY_AC97        0x0001
+#define GPI_LINEOUT_SENSE       0x0004
+#define GPO_SECONDARY_AC97      0x0008
+#define GPI_VOL_DOWN            0x0010
+#define GPI_VOL_UP              0x0020
+#define GPI_IIS_CLK             0x0040
+#define GPI_IIS_LRCLK           0x0080
+#define GPI_IIS_DATA            0x0100
+#define GPI_DOCKING_STATUS      0x0100
+#define GPI_HEADPHONE_SENSE     0x0200
+#define GPO_EXT_AMP_SHUTDOWN    0x1000
+
+#define GPO_EXT_AMP_M3		1	/* default m3 amp */
+#define GPO_EXT_AMP_ALLEGRO	8	/* default allegro amp */
+
+/* M3 */
+#define GPO_M3_EXT_AMP_SHUTDN   0x0002
+
+#define ASSP_INDEX_PORT         0x80
+#define ASSP_MEMORY_PORT        0x82
+#define ASSP_DATA_PORT          0x84
+
+#define MPU401_DATA_PORT        0x98
+#define MPU401_STATUS_PORT      0x99
+
+#define CLK_MULT_DATA_PORT      0x9C
+
+#define ASSP_CONTROL_A          0xA2
+#define ASSP_0_WS_ENABLE        0x01
+#define ASSP_CTRL_A_RESERVED1   0x02
+#define ASSP_CTRL_A_RESERVED2   0x04
+#define ASSP_CLK_49MHZ_SELECT   0x08
+#define FAST_PLU_ENABLE         0x10
+#define ASSP_CTRL_A_RESERVED3   0x20
+#define DSP_CLK_36MHZ_SELECT    0x40
+
+#define ASSP_CONTROL_B          0xA4
+#define RESET_ASSP              0x00
+#define RUN_ASSP                0x01
+#define ENABLE_ASSP_CLOCK       0x00
+#define STOP_ASSP_CLOCK         0x10
+#define RESET_TOGGLE            0x40
+
+#define ASSP_CONTROL_C          0xA6
+#define ASSP_HOST_INT_ENABLE    0x01
+#define FM_ADDR_REMAP_DISABLE   0x02
+#define HOST_WRITE_PORT_ENABLE  0x08
+
+#define ASSP_HOST_INT_STATUS    0xAC
+#define DSP2HOST_REQ_PIORECORD  0x01
+#define DSP2HOST_REQ_I2SRATE    0x02
+#define DSP2HOST_REQ_TIMER      0x04
+
+/* AC97 registers */
+/* XXX fix this crap up */
+/*#define AC97_RESET              0x00*/
+
+#define AC97_VOL_MUTE_B         0x8000
+#define AC97_VOL_M              0x1F
+#define AC97_LEFT_VOL_S         8
+
+#define AC97_MASTER_VOL         0x02
+#define AC97_LINE_LEVEL_VOL     0x04
+#define AC97_MASTER_MONO_VOL    0x06
+#define AC97_PC_BEEP_VOL        0x0A
+#define AC97_PC_BEEP_VOL_M      0x0F
+#define AC97_SROUND_MASTER_VOL  0x38
+#define AC97_PC_BEEP_VOL_S      1
+
+/*#define AC97_PHONE_VOL          0x0C
+ #define AC97_MIC_VOL            0x0E*/
+#define AC97_MIC_20DB_ENABLE    0x40
+
+/*#define AC97_LINEIN_VOL         0x10
+ #define AC97_CD_VOL             0x12
+ #define AC97_VIDEO_VOL          0x14
+ #define AC97_AUX_VOL            0x16*/
+#define AC97_PCM_OUT_VOL        0x18
+/*#define AC97_RECORD_SELECT      0x1A*/
+#define AC97_RECORD_MIC         0x00
+#define AC97_RECORD_CD          0x01
+#define AC97_RECORD_VIDEO       0x02
+#define AC97_RECORD_AUX         0x03
+#define AC97_RECORD_MONO_MUX    0x02
+#define AC97_RECORD_DIGITAL     0x03
+#define AC97_RECORD_LINE        0x04
+#define AC97_RECORD_STEREO      0x05
+#define AC97_RECORD_MONO        0x06
+#define AC97_RECORD_PHONE       0x07
+
+/*#define AC97_RECORD_GAIN        0x1C*/
+#define AC97_RECORD_VOL_M       0x0F
+
+/*#define AC97_GENERAL_PURPOSE    0x20*/
+#define AC97_POWER_DOWN_CTRL    0x26
+#define AC97_ADC_READY          0x0001
+#define AC97_DAC_READY          0x0002
+#define AC97_ANALOG_READY       0x0004
+#define AC97_VREF_ON            0x0008
+#define AC97_PR0                0x0100
+#define AC97_PR1                0x0200
+#define AC97_PR2                0x0400
+#define AC97_PR3                0x0800
+#define AC97_PR4                0x1000
+
+#define AC97_RESERVED1          0x28
+
+#define AC97_VENDOR_TEST        0x5A
+
+#define AC97_CLOCK_DELAY        0x5C
+#define AC97_LINEOUT_MUX_SEL    0x0001
+#define AC97_MONO_MUX_SEL       0x0002
+#define AC97_CLOCK_DELAY_SEL    0x1F
+#define AC97_DAC_CDS_SHIFT      6
+#define AC97_ADC_CDS_SHIFT      11
+
+#define AC97_MULTI_CHANNEL_SEL  0x74
+
+/*#define AC97_VENDOR_ID1         0x7C
+ #define AC97_VENDOR_ID2         0x7E*/
+
+/*
+ * ASSP control regs
+ */
+#define DSP_PORT_TIMER_COUNT    0x06
+
+#define DSP_PORT_MEMORY_INDEX   0x80
+
+#define DSP_PORT_MEMORY_TYPE    0x82
+#define MEMTYPE_INTERNAL_CODE   0x0002
+#define MEMTYPE_INTERNAL_DATA   0x0003
+#define MEMTYPE_MASK            0x0003
+
+#define DSP_PORT_MEMORY_DATA    0x84
+
+#define DSP_PORT_CONTROL_REG_A  0xA2
+#define DSP_PORT_CONTROL_REG_B  0xA4
+#define DSP_PORT_CONTROL_REG_C  0xA6
+
+#define REV_A_CODE_MEMORY_BEGIN         0x0000
+#define REV_A_CODE_MEMORY_END           0x0FFF
+#define REV_A_CODE_MEMORY_UNIT_LENGTH   0x0040
+#define REV_A_CODE_MEMORY_LENGTH        (REV_A_CODE_MEMORY_END - REV_A_CODE_MEMORY_BEGIN + 1)
+
+#define REV_B_CODE_MEMORY_BEGIN         0x0000
+#define REV_B_CODE_MEMORY_END           0x0BFF
+#define REV_B_CODE_MEMORY_UNIT_LENGTH   0x0040
+#define REV_B_CODE_MEMORY_LENGTH        (REV_B_CODE_MEMORY_END - REV_B_CODE_MEMORY_BEGIN + 1)
+
+#define REV_A_DATA_MEMORY_BEGIN         0x1000
+#define REV_A_DATA_MEMORY_END           0x2FFF
+#define REV_A_DATA_MEMORY_UNIT_LENGTH   0x0080
+#define REV_A_DATA_MEMORY_LENGTH        (REV_A_DATA_MEMORY_END - REV_A_DATA_MEMORY_BEGIN + 1)
+
+#define REV_B_DATA_MEMORY_BEGIN         0x1000
+#define REV_B_DATA_MEMORY_END           0x2BFF
+#define REV_B_DATA_MEMORY_UNIT_LENGTH   0x0080
+#define REV_B_DATA_MEMORY_LENGTH        (REV_B_DATA_MEMORY_END - REV_B_DATA_MEMORY_BEGIN + 1)
+
+
+#define NUM_UNITS_KERNEL_CODE          16
+#define NUM_UNITS_KERNEL_DATA           2
+
+#define NUM_UNITS_KERNEL_CODE_WITH_HSP 16
+#define NUM_UNITS_KERNEL_DATA_WITH_HSP  5
+
+/*
+ * Kernel data layout
+ */
+
+#define DP_SHIFT_COUNT                  7
+
+#define KDATA_BASE_ADDR                 0x1000
+#define KDATA_BASE_ADDR2                0x1080
+
+#define KDATA_TASK0                     (KDATA_BASE_ADDR + 0x0000)
+#define KDATA_TASK1                     (KDATA_BASE_ADDR + 0x0001)
+#define KDATA_TASK2                     (KDATA_BASE_ADDR + 0x0002)
+#define KDATA_TASK3                     (KDATA_BASE_ADDR + 0x0003)
+#define KDATA_TASK4                     (KDATA_BASE_ADDR + 0x0004)
+#define KDATA_TASK5                     (KDATA_BASE_ADDR + 0x0005)
+#define KDATA_TASK6                     (KDATA_BASE_ADDR + 0x0006)
+#define KDATA_TASK7                     (KDATA_BASE_ADDR + 0x0007)
+#define KDATA_TASK_ENDMARK              (KDATA_BASE_ADDR + 0x0008)
+
+#define KDATA_CURRENT_TASK              (KDATA_BASE_ADDR + 0x0009)
+#define KDATA_TASK_SWITCH               (KDATA_BASE_ADDR + 0x000A)
+
+#define KDATA_INSTANCE0_POS3D           (KDATA_BASE_ADDR + 0x000B)
+#define KDATA_INSTANCE1_POS3D           (KDATA_BASE_ADDR + 0x000C)
+#define KDATA_INSTANCE2_POS3D           (KDATA_BASE_ADDR + 0x000D)
+#define KDATA_INSTANCE3_POS3D           (KDATA_BASE_ADDR + 0x000E)
+#define KDATA_INSTANCE4_POS3D           (KDATA_BASE_ADDR + 0x000F)
+#define KDATA_INSTANCE5_POS3D           (KDATA_BASE_ADDR + 0x0010)
+#define KDATA_INSTANCE6_POS3D           (KDATA_BASE_ADDR + 0x0011)
+#define KDATA_INSTANCE7_POS3D           (KDATA_BASE_ADDR + 0x0012)
+#define KDATA_INSTANCE8_POS3D           (KDATA_BASE_ADDR + 0x0013)
+#define KDATA_INSTANCE_POS3D_ENDMARK    (KDATA_BASE_ADDR + 0x0014)
+
+#define KDATA_INSTANCE0_SPKVIRT         (KDATA_BASE_ADDR + 0x0015)
+#define KDATA_INSTANCE_SPKVIRT_ENDMARK  (KDATA_BASE_ADDR + 0x0016)
+
+#define KDATA_INSTANCE0_SPDIF           (KDATA_BASE_ADDR + 0x0017)
+#define KDATA_INSTANCE_SPDIF_ENDMARK    (KDATA_BASE_ADDR + 0x0018)
+
+#define KDATA_INSTANCE0_MODEM           (KDATA_BASE_ADDR + 0x0019)
+#define KDATA_INSTANCE_MODEM_ENDMARK    (KDATA_BASE_ADDR + 0x001A)
+
+#define KDATA_INSTANCE0_SRC             (KDATA_BASE_ADDR + 0x001B)
+#define KDATA_INSTANCE1_SRC             (KDATA_BASE_ADDR + 0x001C)
+#define KDATA_INSTANCE_SRC_ENDMARK      (KDATA_BASE_ADDR + 0x001D)
+
+#define KDATA_INSTANCE0_MINISRC         (KDATA_BASE_ADDR + 0x001E)
+#define KDATA_INSTANCE1_MINISRC         (KDATA_BASE_ADDR + 0x001F)
+#define KDATA_INSTANCE2_MINISRC         (KDATA_BASE_ADDR + 0x0020)
+#define KDATA_INSTANCE3_MINISRC         (KDATA_BASE_ADDR + 0x0021)
+#define KDATA_INSTANCE_MINISRC_ENDMARK  (KDATA_BASE_ADDR + 0x0022)
+
+#define KDATA_INSTANCE0_CPYTHRU         (KDATA_BASE_ADDR + 0x0023)
+#define KDATA_INSTANCE1_CPYTHRU         (KDATA_BASE_ADDR + 0x0024)
+#define KDATA_INSTANCE_CPYTHRU_ENDMARK  (KDATA_BASE_ADDR + 0x0025)
+
+#define KDATA_CURRENT_DMA               (KDATA_BASE_ADDR + 0x0026)
+#define KDATA_DMA_SWITCH                (KDATA_BASE_ADDR + 0x0027)
+#define KDATA_DMA_ACTIVE                (KDATA_BASE_ADDR + 0x0028)
+
+#define KDATA_DMA_XFER0                 (KDATA_BASE_ADDR + 0x0029)
+#define KDATA_DMA_XFER1                 (KDATA_BASE_ADDR + 0x002A)
+#define KDATA_DMA_XFER2                 (KDATA_BASE_ADDR + 0x002B)
+#define KDATA_DMA_XFER3                 (KDATA_BASE_ADDR + 0x002C)
+#define KDATA_DMA_XFER4                 (KDATA_BASE_ADDR + 0x002D)
+#define KDATA_DMA_XFER5                 (KDATA_BASE_ADDR + 0x002E)
+#define KDATA_DMA_XFER6                 (KDATA_BASE_ADDR + 0x002F)
+#define KDATA_DMA_XFER7                 (KDATA_BASE_ADDR + 0x0030)
+#define KDATA_DMA_XFER8                 (KDATA_BASE_ADDR + 0x0031)
+#define KDATA_DMA_XFER_ENDMARK          (KDATA_BASE_ADDR + 0x0032)
+
+#define KDATA_I2S_SAMPLE_COUNT          (KDATA_BASE_ADDR + 0x0033)
+#define KDATA_I2S_INT_METER             (KDATA_BASE_ADDR + 0x0034)
+#define KDATA_I2S_ACTIVE                (KDATA_BASE_ADDR + 0x0035)
+
+#define KDATA_TIMER_COUNT_RELOAD        (KDATA_BASE_ADDR + 0x0036)
+#define KDATA_TIMER_COUNT_CURRENT       (KDATA_BASE_ADDR + 0x0037)
+
+#define KDATA_HALT_SYNCH_CLIENT         (KDATA_BASE_ADDR + 0x0038)
+#define KDATA_HALT_SYNCH_DMA            (KDATA_BASE_ADDR + 0x0039)
+#define KDATA_HALT_ACKNOWLEDGE          (KDATA_BASE_ADDR + 0x003A)
+
+#define KDATA_ADC1_XFER0                (KDATA_BASE_ADDR + 0x003B)
+#define KDATA_ADC1_XFER_ENDMARK         (KDATA_BASE_ADDR + 0x003C)
+#define KDATA_ADC1_LEFT_VOLUME			(KDATA_BASE_ADDR + 0x003D)
+#define KDATA_ADC1_RIGHT_VOLUME  		(KDATA_BASE_ADDR + 0x003E)
+#define KDATA_ADC1_LEFT_SUR_VOL			(KDATA_BASE_ADDR + 0x003F)
+#define KDATA_ADC1_RIGHT_SUR_VOL		(KDATA_BASE_ADDR + 0x0040)
+
+#define KDATA_ADC2_XFER0                (KDATA_BASE_ADDR + 0x0041)
+#define KDATA_ADC2_XFER_ENDMARK         (KDATA_BASE_ADDR + 0x0042)
+#define KDATA_ADC2_LEFT_VOLUME			(KDATA_BASE_ADDR + 0x0043)
+#define KDATA_ADC2_RIGHT_VOLUME			(KDATA_BASE_ADDR + 0x0044)
+#define KDATA_ADC2_LEFT_SUR_VOL			(KDATA_BASE_ADDR + 0x0045)
+#define KDATA_ADC2_RIGHT_SUR_VOL		(KDATA_BASE_ADDR + 0x0046)
+
+#define KDATA_CD_XFER0					(KDATA_BASE_ADDR + 0x0047)
+#define KDATA_CD_XFER_ENDMARK			(KDATA_BASE_ADDR + 0x0048)
+#define KDATA_CD_LEFT_VOLUME			(KDATA_BASE_ADDR + 0x0049)
+#define KDATA_CD_RIGHT_VOLUME			(KDATA_BASE_ADDR + 0x004A)
+#define KDATA_CD_LEFT_SUR_VOL			(KDATA_BASE_ADDR + 0x004B)
+#define KDATA_CD_RIGHT_SUR_VOL			(KDATA_BASE_ADDR + 0x004C)
+
+#define KDATA_MIC_XFER0					(KDATA_BASE_ADDR + 0x004D)
+#define KDATA_MIC_XFER_ENDMARK			(KDATA_BASE_ADDR + 0x004E)
+#define KDATA_MIC_VOLUME				(KDATA_BASE_ADDR + 0x004F)
+#define KDATA_MIC_SUR_VOL				(KDATA_BASE_ADDR + 0x0050)
+
+#define KDATA_I2S_XFER0                 (KDATA_BASE_ADDR + 0x0051)
+#define KDATA_I2S_XFER_ENDMARK          (KDATA_BASE_ADDR + 0x0052)
+
+#define KDATA_CHI_XFER0                 (KDATA_BASE_ADDR + 0x0053)
+#define KDATA_CHI_XFER_ENDMARK          (KDATA_BASE_ADDR + 0x0054)
+
+#define KDATA_SPDIF_XFER                (KDATA_BASE_ADDR + 0x0055)
+#define KDATA_SPDIF_CURRENT_FRAME       (KDATA_BASE_ADDR + 0x0056)
+#define KDATA_SPDIF_FRAME0              (KDATA_BASE_ADDR + 0x0057)
+#define KDATA_SPDIF_FRAME1              (KDATA_BASE_ADDR + 0x0058)
+#define KDATA_SPDIF_FRAME2              (KDATA_BASE_ADDR + 0x0059)
+
+#define KDATA_SPDIF_REQUEST             (KDATA_BASE_ADDR + 0x005A)
+#define KDATA_SPDIF_TEMP                (KDATA_BASE_ADDR + 0x005B)
+
+#define KDATA_SPDIFIN_XFER0             (KDATA_BASE_ADDR + 0x005C)
+#define KDATA_SPDIFIN_XFER_ENDMARK      (KDATA_BASE_ADDR + 0x005D)
+#define KDATA_SPDIFIN_INT_METER         (KDATA_BASE_ADDR + 0x005E)
+
+#define KDATA_DSP_RESET_COUNT           (KDATA_BASE_ADDR + 0x005F)
+#define KDATA_DEBUG_OUTPUT              (KDATA_BASE_ADDR + 0x0060)
+
+#define KDATA_KERNEL_ISR_LIST           (KDATA_BASE_ADDR + 0x0061)
+
+#define KDATA_KERNEL_ISR_CBSR1          (KDATA_BASE_ADDR + 0x0062)
+#define KDATA_KERNEL_ISR_CBER1          (KDATA_BASE_ADDR + 0x0063)
+#define KDATA_KERNEL_ISR_CBCR           (KDATA_BASE_ADDR + 0x0064)
+#define KDATA_KERNEL_ISR_AR0            (KDATA_BASE_ADDR + 0x0065)
+#define KDATA_KERNEL_ISR_AR1            (KDATA_BASE_ADDR + 0x0066)
+#define KDATA_KERNEL_ISR_AR2            (KDATA_BASE_ADDR + 0x0067)
+#define KDATA_KERNEL_ISR_AR3            (KDATA_BASE_ADDR + 0x0068)
+#define KDATA_KERNEL_ISR_AR4            (KDATA_BASE_ADDR + 0x0069)
+#define KDATA_KERNEL_ISR_AR5            (KDATA_BASE_ADDR + 0x006A)
+#define KDATA_KERNEL_ISR_BRCR           (KDATA_BASE_ADDR + 0x006B)
+#define KDATA_KERNEL_ISR_PASR           (KDATA_BASE_ADDR + 0x006C)
+#define KDATA_KERNEL_ISR_PAER           (KDATA_BASE_ADDR + 0x006D)
+
+#define KDATA_CLIENT_SCRATCH0           (KDATA_BASE_ADDR + 0x006E)
+#define KDATA_CLIENT_SCRATCH1           (KDATA_BASE_ADDR + 0x006F)
+#define KDATA_KERNEL_SCRATCH            (KDATA_BASE_ADDR + 0x0070)
+#define KDATA_KERNEL_ISR_SCRATCH        (KDATA_BASE_ADDR + 0x0071)
+
+#define KDATA_OUEUE_LEFT                (KDATA_BASE_ADDR + 0x0072)
+#define KDATA_QUEUE_RIGHT               (KDATA_BASE_ADDR + 0x0073)
+
+#define KDATA_ADC1_REQUEST              (KDATA_BASE_ADDR + 0x0074)
+#define KDATA_ADC2_REQUEST              (KDATA_BASE_ADDR + 0x0075)
+#define KDATA_CD_REQUEST				(KDATA_BASE_ADDR + 0x0076)
+#define KDATA_MIC_REQUEST				(KDATA_BASE_ADDR + 0x0077)
+
+#define KDATA_ADC1_MIXER_REQUEST        (KDATA_BASE_ADDR + 0x0078)
+#define KDATA_ADC2_MIXER_REQUEST        (KDATA_BASE_ADDR + 0x0079)
+#define KDATA_CD_MIXER_REQUEST			(KDATA_BASE_ADDR + 0x007A)
+#define KDATA_MIC_MIXER_REQUEST			(KDATA_BASE_ADDR + 0x007B)
+#define KDATA_MIC_SYNC_COUNTER			(KDATA_BASE_ADDR + 0x007C)
+
+/*
+ * second 'segment' (?) reserved for mixer
+ * buffers..
+ */
+
+#define KDATA_MIXER_WORD0               (KDATA_BASE_ADDR2 + 0x0000)
+#define KDATA_MIXER_WORD1               (KDATA_BASE_ADDR2 + 0x0001)
+#define KDATA_MIXER_WORD2               (KDATA_BASE_ADDR2 + 0x0002)
+#define KDATA_MIXER_WORD3               (KDATA_BASE_ADDR2 + 0x0003)
+#define KDATA_MIXER_WORD4               (KDATA_BASE_ADDR2 + 0x0004)
+#define KDATA_MIXER_WORD5               (KDATA_BASE_ADDR2 + 0x0005)
+#define KDATA_MIXER_WORD6               (KDATA_BASE_ADDR2 + 0x0006)
+#define KDATA_MIXER_WORD7               (KDATA_BASE_ADDR2 + 0x0007)
+#define KDATA_MIXER_WORD8               (KDATA_BASE_ADDR2 + 0x0008)
+#define KDATA_MIXER_WORD9               (KDATA_BASE_ADDR2 + 0x0009)
+#define KDATA_MIXER_WORDA               (KDATA_BASE_ADDR2 + 0x000A)
+#define KDATA_MIXER_WORDB               (KDATA_BASE_ADDR2 + 0x000B)
+#define KDATA_MIXER_WORDC               (KDATA_BASE_ADDR2 + 0x000C)
+#define KDATA_MIXER_WORDD               (KDATA_BASE_ADDR2 + 0x000D)
+#define KDATA_MIXER_WORDE               (KDATA_BASE_ADDR2 + 0x000E)
+#define KDATA_MIXER_WORDF               (KDATA_BASE_ADDR2 + 0x000F)
+
+#define KDATA_MIXER_XFER0               (KDATA_BASE_ADDR2 + 0x0010)
+#define KDATA_MIXER_XFER1               (KDATA_BASE_ADDR2 + 0x0011)
+#define KDATA_MIXER_XFER2               (KDATA_BASE_ADDR2 + 0x0012)
+#define KDATA_MIXER_XFER3               (KDATA_BASE_ADDR2 + 0x0013)
+#define KDATA_MIXER_XFER4               (KDATA_BASE_ADDR2 + 0x0014)
+#define KDATA_MIXER_XFER5               (KDATA_BASE_ADDR2 + 0x0015)
+#define KDATA_MIXER_XFER6               (KDATA_BASE_ADDR2 + 0x0016)
+#define KDATA_MIXER_XFER7               (KDATA_BASE_ADDR2 + 0x0017)
+#define KDATA_MIXER_XFER8               (KDATA_BASE_ADDR2 + 0x0018)
+#define KDATA_MIXER_XFER9               (KDATA_BASE_ADDR2 + 0x0019)
+#define KDATA_MIXER_XFER_ENDMARK        (KDATA_BASE_ADDR2 + 0x001A)
+
+#define KDATA_MIXER_TASK_NUMBER         (KDATA_BASE_ADDR2 + 0x001B)
+#define KDATA_CURRENT_MIXER             (KDATA_BASE_ADDR2 + 0x001C)
+#define KDATA_MIXER_ACTIVE              (KDATA_BASE_ADDR2 + 0x001D)
+#define KDATA_MIXER_BANK_STATUS         (KDATA_BASE_ADDR2 + 0x001E)
+#define KDATA_DAC_LEFT_VOLUME	        (KDATA_BASE_ADDR2 + 0x001F)
+#define KDATA_DAC_RIGHT_VOLUME          (KDATA_BASE_ADDR2 + 0x0020)
+
+#define MAX_INSTANCE_MINISRC            (KDATA_INSTANCE_MINISRC_ENDMARK - KDATA_INSTANCE0_MINISRC)
+#define MAX_VIRTUAL_DMA_CHANNELS        (KDATA_DMA_XFER_ENDMARK - KDATA_DMA_XFER0)
+#define MAX_VIRTUAL_MIXER_CHANNELS      (KDATA_MIXER_XFER_ENDMARK - KDATA_MIXER_XFER0)
+#define MAX_VIRTUAL_ADC1_CHANNELS       (KDATA_ADC1_XFER_ENDMARK - KDATA_ADC1_XFER0)
+
+/*
+ * client data area offsets
+ */
+#define CDATA_INSTANCE_READY            0x00
+
+#define CDATA_HOST_SRC_ADDRL            0x01
+#define CDATA_HOST_SRC_ADDRH            0x02
+#define CDATA_HOST_SRC_END_PLUS_1L      0x03
+#define CDATA_HOST_SRC_END_PLUS_1H      0x04
+#define CDATA_HOST_SRC_CURRENTL         0x05
+#define CDATA_HOST_SRC_CURRENTH         0x06
+
+#define CDATA_IN_BUF_CONNECT            0x07
+#define CDATA_OUT_BUF_CONNECT           0x08
+
+#define CDATA_IN_BUF_BEGIN              0x09
+#define CDATA_IN_BUF_END_PLUS_1         0x0A
+#define CDATA_IN_BUF_HEAD               0x0B
+#define CDATA_IN_BUF_TAIL               0x0C
+#define CDATA_OUT_BUF_BEGIN             0x0D
+#define CDATA_OUT_BUF_END_PLUS_1        0x0E
+#define CDATA_OUT_BUF_HEAD              0x0F
+#define CDATA_OUT_BUF_TAIL              0x10
+
+#define CDATA_DMA_CONTROL               0x11
+#define CDATA_RESERVED                  0x12
+
+#define CDATA_FREQUENCY                 0x13
+#define CDATA_LEFT_VOLUME               0x14
+#define CDATA_RIGHT_VOLUME              0x15
+#define CDATA_LEFT_SUR_VOL              0x16
+#define CDATA_RIGHT_SUR_VOL             0x17
+
+#define CDATA_HEADER_LEN                0x18
+
+#define SRC3_DIRECTION_OFFSET           CDATA_HEADER_LEN
+#define SRC3_MODE_OFFSET                (CDATA_HEADER_LEN + 1)
+#define SRC3_WORD_LENGTH_OFFSET         (CDATA_HEADER_LEN + 2)
+#define SRC3_PARAMETER_OFFSET           (CDATA_HEADER_LEN + 3)
+#define SRC3_COEFF_ADDR_OFFSET          (CDATA_HEADER_LEN + 8)
+#define SRC3_FILTAP_ADDR_OFFSET         (CDATA_HEADER_LEN + 10)
+#define SRC3_TEMP_INBUF_ADDR_OFFSET     (CDATA_HEADER_LEN + 16)
+#define SRC3_TEMP_OUTBUF_ADDR_OFFSET    (CDATA_HEADER_LEN + 17)
+
+#define MINISRC_IN_BUFFER_SIZE   ( 0x50 * 2 )
+#define MINISRC_OUT_BUFFER_SIZE  ( 0x50 * 2 * 2)
+#define MINISRC_OUT_BUFFER_SIZE  ( 0x50 * 2 * 2)
+#define MINISRC_TMP_BUFFER_SIZE  ( 112 + ( MINISRC_BIQUAD_STAGE * 3 + 4 ) * 2 * 2 )
+#define MINISRC_BIQUAD_STAGE    2
+#define MINISRC_COEF_LOC          0x175
+
+#define DMACONTROL_BLOCK_MASK           0x000F
+#define  DMAC_BLOCK0_SELECTOR           0x0000
+#define  DMAC_BLOCK1_SELECTOR           0x0001
+#define  DMAC_BLOCK2_SELECTOR           0x0002
+#define  DMAC_BLOCK3_SELECTOR           0x0003
+#define  DMAC_BLOCK4_SELECTOR           0x0004
+#define  DMAC_BLOCK5_SELECTOR           0x0005
+#define  DMAC_BLOCK6_SELECTOR           0x0006
+#define  DMAC_BLOCK7_SELECTOR           0x0007
+#define  DMAC_BLOCK8_SELECTOR           0x0008
+#define  DMAC_BLOCK9_SELECTOR           0x0009
+#define  DMAC_BLOCKA_SELECTOR           0x000A
+#define  DMAC_BLOCKB_SELECTOR           0x000B
+#define  DMAC_BLOCKC_SELECTOR           0x000C
+#define  DMAC_BLOCKD_SELECTOR           0x000D
+#define  DMAC_BLOCKE_SELECTOR           0x000E
+#define  DMAC_BLOCKF_SELECTOR           0x000F
+#define DMACONTROL_PAGE_MASK            0x00F0
+#define  DMAC_PAGE0_SELECTOR            0x0030
+#define  DMAC_PAGE1_SELECTOR            0x0020
+#define  DMAC_PAGE2_SELECTOR            0x0010
+#define  DMAC_PAGE3_SELECTOR            0x0000
+#define DMACONTROL_AUTOREPEAT           0x1000
+#define DMACONTROL_STOPPED              0x2000
+#define DMACONTROL_DIRECTION            0x0100
+
+/*
+ * an arbitrary volume we set the internal
+ * volume settings to so that the ac97 volume
+ * range is a little less insane.  0x7fff is
+ * max.
+ */
+#define ARB_VOLUME ( 0x6800 )
+
+/*
+ */
+
+typedef struct snd_m3_dma m3_dma_t;
+typedef struct snd_m3 m3_t;
+
+/* quirk lists */
+struct m3_quirk {
+    const char *name;	/* device name */
+    u16 vendor, device;	/* subsystem ids */
+    int amp_gpio;		/* gpio pin #  for external amp, -1 = default */
+    int irda_workaround;	/* non-zero if avoid to touch 0x10 on GPIO_DIRECTION
+    (e.g. for IrDA on Dell Inspirons) */
+};
+
+struct m3_hv_quirk {
+    u16 vendor, device, subsystem_vendor, subsystem_device;
+    u32 config;             /* ALLEGRO_CONFIG hardware volume bits */
+    int is_omnibook;        /* Do HP OmniBook GPIO magic? */
+};
+
+struct m3_list {
+    int curlen;
+    int mem_addr;
+    int max;
+};
+
+struct snd_m3_dma {
+
+    int number;
+    m3_t *chip;
+    snd_pcm_substream_t *substream;
+
+    struct assp_instance {
+        unsigned short code, data;
+    } inst;
+
+    int running;
+    int opened;
+
+    unsigned long buffer_addr;
+    int dma_size;
+    int period_size;
+    unsigned int hwptr;
+    int count;
+
+    int index[3];
+    struct m3_list *index_list[3];
+
+    int in_lists;
+
+    struct list_head list;
+
+};
+
+struct snd_m3 {
+
+    snd_card_t *card;
+
+    unsigned long iobase;
+
+    int irq;
+    unsigned int allegro_flag: 1;
+
+    ac97_t *ac97;
+
+    snd_pcm_t *pcm;
+
+    struct pci_dev *pci;
+    const struct m3_quirk *quirk;
+    const struct m3_hv_quirk *hv_quirk;
+
+    int dacs_active;
+    int timer_users;
+
+    struct m3_list  msrc_list;
+    struct m3_list  mixer_list;
+    struct m3_list  adc1_list;
+    struct m3_list  dma_list;
+
+    /* for storing reset state..*/
+    u8 reset_state;
+
+    int external_amp;
+    int amp_gpio;
+
+    /* midi */
+    snd_rawmidi_t *rmidi;
+
+    /* pcm streams */
+    int num_substreams;
+    m3_dma_t *substreams;
+
+    spinlock_t reg_lock;
+    spinlock_t ac97_lock;
+
+    snd_kcontrol_t *master_switch;
+    snd_kcontrol_t *master_volume;
+    struct tasklet_struct hwvol_tq;
+#ifdef CONFIG_PM
+    u16 *suspend_mem;
+#endif
+};
+
+/*
+ * pci ids
+ */
+
+#ifndef PCI_VENDOR_ID_ESS
+#define PCI_VENDOR_ID_ESS         0x125D
+#endif
+#ifndef PCI_DEVICE_ID_ESS_ALLEGRO_1
+#define PCI_DEVICE_ID_ESS_ALLEGRO_1	0x1988
+#endif
+#ifndef PCI_DEVICE_ID_ESS_ALLEGRO
+#define PCI_DEVICE_ID_ESS_ALLEGRO	0x1989
+#endif
+#ifndef PCI_DEVICE_ID_ESS_CANYON3D_2LE
+#define PCI_DEVICE_ID_ESS_CANYON3D_2LE	0x1990
+#endif
+#ifndef PCI_DEVICE_ID_ESS_CANYON3D_2
+#define PCI_DEVICE_ID_ESS_CANYON3D_2	0x1992
+#endif
+#ifndef PCI_DEVICE_ID_ESS_MAESTRO3
+#define PCI_DEVICE_ID_ESS_MAESTRO3	0x1998
+#endif
+#ifndef PCI_DEVICE_ID_ESS_MAESTRO3_1
+#define PCI_DEVICE_ID_ESS_MAESTRO3_1	0x1999
+#endif
+#ifndef PCI_DEVICE_ID_ESS_MAESTRO3_HW
+#define PCI_DEVICE_ID_ESS_MAESTRO3_HW	0x199a
+#endif
+#ifndef PCI_DEVICE_ID_ESS_MAESTRO3_2
+#define PCI_DEVICE_ID_ESS_MAESTRO3_2	0x199b
+#endif
+
+static struct pci_device_id snd_m3_ids[] = {
+    {PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_ALLEGRO_1, PCI_ANY_ID, PCI_ANY_ID,
+    PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0},
+    //	{PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_ALLEGRO, PCI_ANY_ID, PCI_ANY_ID,
+    //	 PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0},
+    {PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_CANYON3D_2LE, PCI_ANY_ID, PCI_ANY_ID,
+    PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0},
+    {PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_CANYON3D_2, PCI_ANY_ID, PCI_ANY_ID,
+    PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0},
+    {PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_MAESTRO3, PCI_ANY_ID, PCI_ANY_ID,
+    PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0},
+    {PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_MAESTRO3_1, PCI_ANY_ID, PCI_ANY_ID,
+    PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0},
+    {PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_MAESTRO3_HW, PCI_ANY_ID, PCI_ANY_ID,
+    PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0},
+    {PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_MAESTRO3_2, PCI_ANY_ID, PCI_ANY_ID,
+    PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0},
+    {0,},
+};
+
+MODULE_DEVICE_TABLE(pci, snd_m3_ids);
+
+static const struct m3_quirk m3_quirk_list[] = {
+    /* panasonic CF-28 "toughbook" */
+    {
+        .name = "Panasonic CF-28",
+        .vendor = 0x10f7,
+        .device = 0x833e,
+        .amp_gpio = 0x0d,
+    },
+    /* panasonic CF-72 "toughbook" */
+    {
+        .name = "Panasonic CF-72",
+        .vendor = 0x10f7,
+        .device = 0x833d,
+        .amp_gpio = 0x0d,
+    },
+    /* Dell Inspiron 4000 */
+    {
+        .name = "Dell Inspiron 4000",
+        .vendor = 0x1028,
+        .device = 0x00b0,
+        .amp_gpio = -1,
+        .irda_workaround = 1,
+    },
+    /* Dell Inspiron 8000 */
+    {
+        .name = "Dell Inspiron 8000",
+        .vendor = 0x1028,
+        .device = 0x00a4,
+        .amp_gpio = -1,
+        .irda_workaround = 1,
+    },
+    /* Dell Inspiron 8100 */
+    {
+        .name = "Dell Inspiron 8100",
+        .vendor = 0x1028,
+        .device = 0x00e6,
+        .amp_gpio = -1,
+        .irda_workaround = 1,
+    },
+    /* NEC LM800J/7 */
+    {
+        .name = "NEC LM800J/7",
+        .vendor = 0x1033,
+        .device = 0x80f1,
+        .amp_gpio = 0x03,
+    },
+    /* LEGEND ZhaoYang 3100CF */
+    {
+        .name = "LEGEND ZhaoYang 3100CF",
+        .vendor = 0x1509,
+        .device = 0x1740,
+        .amp_gpio = 0x03,
+    },
+    /* END */
+    { NULL }
+};
+
+/* These values came from the Windows driver. */
+static const struct m3_hv_quirk m3_hv_quirk_list[] = {
+    /* Allegro chips */
+    { 0x125D, 0x1988, 0x0E11, 0x002E, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+    { 0x125D, 0x1988, 0x0E11, 0x0094, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+    { 0x125D, 0x1988, 0x0E11, 0xB112, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+    { 0x125D, 0x1988, 0x0E11, 0xB114, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+    { 0x125D, 0x1988, 0x103C, 0x0012, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+    { 0x125D, 0x1988, 0x103C, 0x0018, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+    { 0x125D, 0x1988, 0x103C, 0x001C, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+    { 0x125D, 0x1988, 0x103C, 0x001D, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+    { 0x125D, 0x1988, 0x103C, 0x001E, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+    { 0x125D, 0x1988, 0x107B, 0x3350, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+    { 0x125D, 0x1988, 0x10F7, 0x8338, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+    { 0x125D, 0x1988, 0x10F7, 0x833C, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+    { 0x125D, 0x1988, 0x10F7, 0x833D, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+    { 0x125D, 0x1988, 0x10F7, 0x833E, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+    { 0x125D, 0x1988, 0x10F7, 0x833F, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+    { 0x125D, 0x1988, 0x13BD, 0x1018, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+    { 0x125D, 0x1988, 0x13BD, 0x1019, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+    { 0x125D, 0x1988, 0x13BD, 0x101A, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+    { 0x125D, 0x1988, 0x14FF, 0x0F03, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+    { 0x125D, 0x1988, 0x14FF, 0x0F04, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+    { 0x125D, 0x1988, 0x14FF, 0x0F05, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+    { 0x125D, 0x1988, 0x156D, 0xB400, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+    { 0x125D, 0x1988, 0x156D, 0xB795, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+    { 0x125D, 0x1988, 0x156D, 0xB797, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+    { 0x125D, 0x1988, 0x156D, 0xC700, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+    { 0x125D, 0x1988, 0x1033, 0x80F1, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 },
+    { 0x125D, 0x1988, 0x103C, 0x001A, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 }, /* HP OmniBook 6100 */
+    { 0x125D, 0x1988, 0x107B, 0x340A, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 },
+    { 0x125D, 0x1988, 0x107B, 0x3450, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 },
+    { 0x125D, 0x1988, 0x109F, 0x3134, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 },
+    { 0x125D, 0x1988, 0x109F, 0x3161, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 },
+    { 0x125D, 0x1988, 0x144D, 0x3280, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 },
+    { 0x125D, 0x1988, 0x144D, 0x3281, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 },
+    { 0x125D, 0x1988, 0x144D, 0xC002, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 },
+    { 0x125D, 0x1988, 0x144D, 0xC003, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 },
+    { 0x125D, 0x1988, 0x1509, 0x1740, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 },
+    { 0x125D, 0x1988, 0x1610, 0x0010, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 },
+    { 0x125D, 0x1988, 0x1042, 0x1042, HV_CTRL_ENABLE, 0 },
+    { 0x125D, 0x1988, 0x107B, 0x9500, HV_CTRL_ENABLE, 0 },
+    { 0x125D, 0x1988, 0x14FF, 0x0F06, HV_CTRL_ENABLE, 0 },
+    { 0x125D, 0x1988, 0x1558, 0x8586, HV_CTRL_ENABLE, 0 },
+    { 0x125D, 0x1988, 0x161F, 0x2011, HV_CTRL_ENABLE, 0 },
+    /* Maestro3 chips */
+    { 0x125D, 0x1998, 0x103C, 0x000E, HV_CTRL_ENABLE, 0 },
+    { 0x125D, 0x1998, 0x103C, 0x0010, HV_CTRL_ENABLE, 1 }, /* HP OmniBook 6000 */
+    { 0x125D, 0x1998, 0x103C, 0x0011, HV_CTRL_ENABLE, 1 }, /* HP OmniBook 500 */
+    { 0x125D, 0x1998, 0x103C, 0x001B, HV_CTRL_ENABLE, 0 },
+    { 0x125D, 0x1998, 0x104D, 0x80A6, HV_CTRL_ENABLE, 0 },
+    { 0x125D, 0x1998, 0x104D, 0x80AA, HV_CTRL_ENABLE, 0 },
+    { 0x125D, 0x1998, 0x107B, 0x5300, HV_CTRL_ENABLE, 0 },
+    { 0x125D, 0x1998, 0x110A, 0x1998, HV_CTRL_ENABLE, 0 },
+    { 0x125D, 0x1998, 0x13BD, 0x1015, HV_CTRL_ENABLE, 0 },
+    { 0x125D, 0x1998, 0x13BD, 0x101C, HV_CTRL_ENABLE, 0 },
+    { 0x125D, 0x1998, 0x13BD, 0x1802, HV_CTRL_ENABLE, 0 },
+    { 0x125D, 0x1998, 0x1599, 0x0715, HV_CTRL_ENABLE, 0 },
+    { 0x125D, 0x1998, 0x5643, 0x5643, HV_CTRL_ENABLE, 0 },
+    { 0x125D, 0x199A, 0x144D, 0x3260, HV_CTRL_ENABLE | REDUCED_DEBOUNCE, 0 },
+    { 0x125D, 0x199A, 0x144D, 0x3261, HV_CTRL_ENABLE | REDUCED_DEBOUNCE, 0 },
+    { 0x125D, 0x199A, 0x144D, 0xC000, HV_CTRL_ENABLE | REDUCED_DEBOUNCE, 0 },
+    { 0x125D, 0x199A, 0x144D, 0xC001, HV_CTRL_ENABLE | REDUCED_DEBOUNCE, 0 },
+    { 0 }
+};
+
+/*
+ * lowlevel functions
+ */
+
+inline static void snd_m3_outw(m3_t *chip, u16 value, unsigned long reg)
+{
+    outw(value, chip->iobase + reg);
+}
+
+inline static u16 snd_m3_inw(m3_t *chip, unsigned long reg)
+{
+    return inw(chip->iobase + reg);
+}
+
+inline static void snd_m3_outb(m3_t *chip, u8 value, unsigned long reg)
+{
+    outb(value, chip->iobase + reg);
+}
+
+inline static u8 snd_m3_inb(m3_t *chip, unsigned long reg)
+{
+    return inb(chip->iobase + reg);
+}
+
+/*
+ * access 16bit words to the code or data regions of the dsp's memory.
+ * index addresses 16bit words.
+ */
+static u16 snd_m3_assp_read(m3_t *chip, u16 region, u16 index)
+{
+    snd_m3_outw(chip, region & MEMTYPE_MASK, DSP_PORT_MEMORY_TYPE);
+    snd_m3_outw(chip, index, DSP_PORT_MEMORY_INDEX);
+    return snd_m3_inw(chip, DSP_PORT_MEMORY_DATA);
+}
+
+static void snd_m3_assp_write(m3_t *chip, u16 region, u16 index, u16 data)
+{
+    snd_m3_outw(chip, region & MEMTYPE_MASK, DSP_PORT_MEMORY_TYPE);
+    snd_m3_outw(chip, index, DSP_PORT_MEMORY_INDEX);
+    snd_m3_outw(chip, data, DSP_PORT_MEMORY_DATA);
+}
+
+static void snd_m3_assp_halt(m3_t *chip)
+{
+    chip->reset_state = snd_m3_inb(chip, DSP_PORT_CONTROL_REG_B) & ~REGB_STOP_CLOCK;
+    msleep(10);
+    snd_m3_outb(chip, chip->reset_state & ~REGB_ENABLE_RESET, DSP_PORT_CONTROL_REG_B);
+}
+
+static void snd_m3_assp_continue(m3_t *chip)
+{
+    snd_m3_outb(chip, chip->reset_state | REGB_ENABLE_RESET, DSP_PORT_CONTROL_REG_B);
+}
+
+
+/*
+ * This makes me sad. the maestro3 has lists
+ * internally that must be packed.. 0 terminates,
+ * apparently, or maybe all unused entries have
+ * to be 0, the lists have static lengths set
+ * by the binary code images.
+ */
+
+static int snd_m3_add_list(m3_t *chip, struct m3_list *list, u16 val)
+{
+    snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+                      list->mem_addr + list->curlen,
+                      val);
+    return list->curlen++;
+}
+
+static void snd_m3_remove_list(m3_t *chip, struct m3_list *list, int index)
+{
+    u16  val;
+    int lastindex = list->curlen - 1;
+
+    if (index != lastindex) {
+        val = snd_m3_assp_read(chip, MEMTYPE_INTERNAL_DATA,
+                               list->mem_addr + lastindex);
+        snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+                          list->mem_addr + index,
+                          val);
+    }
+
+    snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+                      list->mem_addr + lastindex,
+                      0);
+
+    list->curlen--;
+}
+
+static void snd_m3_inc_timer_users(m3_t *chip)
+{
+    chip->timer_users++;
+    if (chip->timer_users != 1)
+        return;
+
+    snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+                      KDATA_TIMER_COUNT_RELOAD,
+                      240);
+
+    snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+                      KDATA_TIMER_COUNT_CURRENT,
+                      240);
+
+    snd_m3_outw(chip,
+                snd_m3_inw(chip, HOST_INT_CTRL) | CLKRUN_GEN_ENABLE,
+                HOST_INT_CTRL);
+}
+
+static void snd_m3_dec_timer_users(m3_t *chip)
+{
+    chip->timer_users--;
+    if (chip->timer_users > 0)
+        return;
+
+    snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+                      KDATA_TIMER_COUNT_RELOAD,
+                      0);
+
+    snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+                      KDATA_TIMER_COUNT_CURRENT,
+                      0);
+
+    snd_m3_outw(chip,
+                snd_m3_inw(chip, HOST_INT_CTRL) & ~CLKRUN_GEN_ENABLE,
+                HOST_INT_CTRL);
+}
+
+/*
+ * start/stop
+ */
+
+/* spinlock held! */
+static int snd_m3_pcm_start(m3_t *chip, m3_dma_t *s, snd_pcm_substream_t *subs)
+{
+    if (! s || ! subs)
+        return -EINVAL;
+
+    snd_m3_inc_timer_users(chip);
+    switch (subs->stream) {
+    case SNDRV_PCM_STREAM_PLAYBACK:
+        chip->dacs_active++;
+        snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+                          s->inst.data + CDATA_INSTANCE_READY, 1);
+        snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+                          KDATA_MIXER_TASK_NUMBER,
+                          chip->dacs_active);
+        break;
+    case SNDRV_PCM_STREAM_CAPTURE:
+        snd_m3_assp_write(s->chip, MEMTYPE_INTERNAL_DATA,
+                          KDATA_ADC1_REQUEST, 1);
+        snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+                          s->inst.data + CDATA_INSTANCE_READY, 1);
+        break;
+    }
+    return 0;
+}
+
+/* spinlock held! */
+static int snd_m3_pcm_stop(m3_t *chip, m3_dma_t *s, snd_pcm_substream_t *subs)
+{
+    if (! s || ! subs)
+        return -EINVAL;
+
+    snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+                      s->inst.data + CDATA_INSTANCE_READY, 0);
+    snd_m3_dec_timer_users(chip);
+    switch (subs->stream) {
+    case SNDRV_PCM_STREAM_PLAYBACK:
+        chip->dacs_active--;
+        snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+                          KDATA_MIXER_TASK_NUMBER,
+                          chip->dacs_active);
+        break;
+    case SNDRV_PCM_STREAM_CAPTURE:
+        snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+                          KDATA_ADC1_REQUEST, 0);
+        break;
+    }
+    return 0;
+}
+
+static int
+snd_m3_pcm_trigger(snd_pcm_substream_t *subs, int cmd)
+{
+    m3_t *chip = snd_pcm_substream_chip(subs);
+    m3_dma_t *s = (m3_dma_t*)subs->runtime->private_data;
+    int err = -EINVAL;
+
+    snd_assert(s != NULL, return -ENXIO);
+
+    spin_lock(&chip->reg_lock);
+    switch (cmd) {
+    case SNDRV_PCM_TRIGGER_START:
+    case SNDRV_PCM_TRIGGER_RESUME:
+        if (s->running)
+            err = -EBUSY;
+        else {
+            s->running = 1;
+            err = snd_m3_pcm_start(chip, s, subs);
+        }
+        break;
+    case SNDRV_PCM_TRIGGER_STOP:
+    case SNDRV_PCM_TRIGGER_SUSPEND:
+        if (! s->running)
+            err = 0; /* should return error? */
+        else {
+            s->running = 0;
+            err = snd_m3_pcm_stop(chip, s, subs);
+        }
+        break;
+    }
+    spin_unlock(&chip->reg_lock);
+    return err;
+}
+
+/*
+ * setup
+ */
+static void
+snd_m3_pcm_setup1(m3_t *chip, m3_dma_t *s, snd_pcm_substream_t *subs)
+{
+    int dsp_in_size, dsp_out_size, dsp_in_buffer, dsp_out_buffer;
+    snd_pcm_runtime_t *runtime = subs->runtime;
+
+    if (subs->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+        dsp_in_size = MINISRC_IN_BUFFER_SIZE - (0x20 * 2);
+        dsp_out_size = MINISRC_OUT_BUFFER_SIZE - (0x20 * 2);
+    } else {
+        dsp_in_size = MINISRC_IN_BUFFER_SIZE - (0x10 * 2);
+        dsp_out_size = MINISRC_OUT_BUFFER_SIZE - (0x10 * 2);
+    }
+    dsp_in_buffer = s->inst.data + (MINISRC_TMP_BUFFER_SIZE / 2);
+    dsp_out_buffer = dsp_in_buffer + (dsp_in_size / 2) + 1;
+
+    s->dma_size = frames_to_bytes(runtime, runtime->buffer_size);
+    s->period_size = frames_to_bytes(runtime, runtime->period_size);
+    s->hwptr = 0;
+    s->count = 0;
+
+#define LO(x) ((x) & 0xffff)
+#define HI(x) LO((x) >> 16)
+
+    /* host dma buffer pointers */
+    snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+                      s->inst.data + CDATA_HOST_SRC_ADDRL,
+                      LO(s->buffer_addr));
+
+    snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+                      s->inst.data + CDATA_HOST_SRC_ADDRH,
+                      HI(s->buffer_addr));
+
+    snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+                      s->inst.data + CDATA_HOST_SRC_END_PLUS_1L,
+                      LO(s->buffer_addr + s->dma_size));
+
+    snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+                      s->inst.data + CDATA_HOST_SRC_END_PLUS_1H,
+                      HI(s->buffer_addr + s->dma_size));
+
+    snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+                      s->inst.data + CDATA_HOST_SRC_CURRENTL,
+                      LO(s->buffer_addr));
+
+    snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+                      s->inst.data + CDATA_HOST_SRC_CURRENTH,
+                      HI(s->buffer_addr));
+#undef LO
+#undef HI
+
+    /* dsp buffers */
+
+    snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+                      s->inst.data + CDATA_IN_BUF_BEGIN,
+                      dsp_in_buffer);
+
+    snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+                      s->inst.data + CDATA_IN_BUF_END_PLUS_1,
+                      dsp_in_buffer + (dsp_in_size / 2));
+
+    snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+                      s->inst.data + CDATA_IN_BUF_HEAD,
+                      dsp_in_buffer);
+
+    snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+                      s->inst.data + CDATA_IN_BUF_TAIL,
+                      dsp_in_buffer);
+
+    snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+                      s->inst.data + CDATA_OUT_BUF_BEGIN,
+                      dsp_out_buffer);
+
+    snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+                      s->inst.data + CDATA_OUT_BUF_END_PLUS_1,
+                      dsp_out_buffer + (dsp_out_size / 2));
+
+    snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+                      s->inst.data + CDATA_OUT_BUF_HEAD,
+                      dsp_out_buffer);
+
+    snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+                      s->inst.data + CDATA_OUT_BUF_TAIL,
+                      dsp_out_buffer);
+}
+
+static void snd_m3_pcm_setup2(m3_t *chip, m3_dma_t *s, snd_pcm_runtime_t *runtime)
+{
+    u32 freq;
+
+    /*
+     * put us in the lists if we're not already there
+     */
+    if (! s->in_lists) {
+        s->index[0] = snd_m3_add_list(chip, s->index_list[0],
+                                      s->inst.data >> DP_SHIFT_COUNT);
+        s->index[1] = snd_m3_add_list(chip, s->index_list[1],
+                                      s->inst.data >> DP_SHIFT_COUNT);
+        s->index[2] = snd_m3_add_list(chip, s->index_list[2],
+                                      s->inst.data >> DP_SHIFT_COUNT);
+        s->in_lists = 1;
+    }
+
+    /* write to 'mono' word */
+    snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+                      s->inst.data + SRC3_DIRECTION_OFFSET + 1,
+                      runtime->channels == 2 ? 0 : 1);
+    /* write to '8bit' word */
+    snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+                      s->inst.data + SRC3_DIRECTION_OFFSET + 2,
+                      snd_pcm_format_width(runtime->format) == 16 ? 0 : 1);
+
+    /* set up dac/adc rate */
+    freq = ((runtime->rate << 15) + 24000 ) / 48000;
+    if (freq)
+        freq--;
+    printk("set rate: %i, freq: %i\n",runtime->rate,freq);
+    snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+                      s->inst.data + CDATA_FREQUENCY,
+                      freq);
+}
+
+static const struct play_vals {
+    u16 addr, val;
+} pv[] = {
+    {CDATA_LEFT_VOLUME, ARB_VOLUME},
+    {CDATA_RIGHT_VOLUME, ARB_VOLUME},
+    {SRC3_DIRECTION_OFFSET, 0} ,
+    /* +1, +2 are stereo/16 bit */
+    {SRC3_DIRECTION_OFFSET + 3, 0x0000}, /* fraction? */
+    {SRC3_DIRECTION_OFFSET + 4, 0}, /* first l */
+    {SRC3_DIRECTION_OFFSET + 5, 0}, /* first r */
+    {SRC3_DIRECTION_OFFSET + 6, 0}, /* second l */
+    {SRC3_DIRECTION_OFFSET + 7, 0}, /* second r */
+    {SRC3_DIRECTION_OFFSET + 8, 0}, /* delta l */
+    {SRC3_DIRECTION_OFFSET + 9, 0}, /* delta r */
+    {SRC3_DIRECTION_OFFSET + 10, 0x8000}, /* round */
+    {SRC3_DIRECTION_OFFSET + 11, 0xFF00}, /* higher bute mark */
+    {SRC3_DIRECTION_OFFSET + 13, 0}, /* temp0 */
+    {SRC3_DIRECTION_OFFSET + 14, 0}, /* c fraction */
+    {SRC3_DIRECTION_OFFSET + 15, 0}, /* counter */
+    {SRC3_DIRECTION_OFFSET + 16, 8}, /* numin */
+    {SRC3_DIRECTION_OFFSET + 17, 50*2}, /* numout */
+    {SRC3_DIRECTION_OFFSET + 18, MINISRC_BIQUAD_STAGE - 1}, /* numstage */
+    {SRC3_DIRECTION_OFFSET + 20, 0}, /* filtertap */
+    {SRC3_DIRECTION_OFFSET + 21, 0} /* booster */
+};
+
+
+/* the mode passed should be already shifted and masked */
+static void
+snd_m3_playback_setup(m3_t *chip, m3_dma_t *s, snd_pcm_substream_t *subs)
+{
+    unsigned int i;
+
+    /*
+     * some per client initializers
+     */
+
+    snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+                      s->inst.data + SRC3_DIRECTION_OFFSET + 12,
+                      s->inst.data + 40 + 8);
+
+    snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+                      s->inst.data + SRC3_DIRECTION_OFFSET + 19,
+                      s->inst.code + MINISRC_COEF_LOC);
+
+    /* enable or disable low pass filter? */
+    snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+                      s->inst.data + SRC3_DIRECTION_OFFSET + 22,
+                      subs->runtime->rate > 45000 ? 0xff : 0);
+
+    /* tell it which way dma is going? */
+    snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+                      s->inst.data + CDATA_DMA_CONTROL,
+                      DMACONTROL_AUTOREPEAT + DMAC_PAGE3_SELECTOR + DMAC_BLOCKF_SELECTOR);
+
+    /*
+     * set an armload of static initializers
+     */
+    for (i = 0; i < ARRAY_SIZE(pv); i++)
+        snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+                          s->inst.data + pv[i].addr, pv[i].val);
+}
+
+/*
+ *    Native record driver
+ */
+static const struct rec_vals {
+    u16 addr, val;
+} rv[] = {
+    {CDATA_LEFT_VOLUME, ARB_VOLUME},
+    {CDATA_RIGHT_VOLUME, ARB_VOLUME},
+    {SRC3_DIRECTION_OFFSET, 1} ,
+    /* +1, +2 are stereo/16 bit */
+    {SRC3_DIRECTION_OFFSET + 3, 0x0000}, /* fraction? */
+    {SRC3_DIRECTION_OFFSET + 4, 0}, /* first l */
+    {SRC3_DIRECTION_OFFSET + 5, 0}, /* first r */
+    {SRC3_DIRECTION_OFFSET + 6, 0}, /* second l */
+    {SRC3_DIRECTION_OFFSET + 7, 0}, /* second r */
+    {SRC3_DIRECTION_OFFSET + 8, 0}, /* delta l */
+    {SRC3_DIRECTION_OFFSET + 9, 0}, /* delta r */
+    {SRC3_DIRECTION_OFFSET + 10, 0x8000}, /* round */
+    {SRC3_DIRECTION_OFFSET + 11, 0xFF00}, /* higher bute mark */
+    {SRC3_DIRECTION_OFFSET + 13, 0}, /* temp0 */
+    {SRC3_DIRECTION_OFFSET + 14, 0}, /* c fraction */
+    {SRC3_DIRECTION_OFFSET + 15, 0}, /* counter */
+    {SRC3_DIRECTION_OFFSET + 16, 50},/* numin */
+    {SRC3_DIRECTION_OFFSET + 17, 8}, /* numout */
+    {SRC3_DIRECTION_OFFSET + 18, 0}, /* numstage */
+    {SRC3_DIRECTION_OFFSET + 19, 0}, /* coef */
+    {SRC3_DIRECTION_OFFSET + 20, 0}, /* filtertap */
+    {SRC3_DIRECTION_OFFSET + 21, 0}, /* booster */
+    {SRC3_DIRECTION_OFFSET + 22, 0xff} /* skip lpf */
+};
+
+static void
+snd_m3_capture_setup(m3_t *chip, m3_dma_t *s, snd_pcm_substream_t *subs)
+{
+    unsigned int i;
+
+    /*
+     * some per client initializers
+     */
+
+    snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+                      s->inst.data + SRC3_DIRECTION_OFFSET + 12,
+                      s->inst.data + 40 + 8);
+
+    /* tell it which way dma is going? */
+    snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+                      s->inst.data + CDATA_DMA_CONTROL,
+                      DMACONTROL_DIRECTION + DMACONTROL_AUTOREPEAT +
+                      DMAC_PAGE3_SELECTOR + DMAC_BLOCKF_SELECTOR);
+
+    /*
+     * set an armload of static initializers
+     */
+    for (i = 0; i < ARRAY_SIZE(rv); i++)
+        snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+                          s->inst.data + rv[i].addr, rv[i].val);
+}
+
+static int snd_m3_pcm_hw_params(snd_pcm_substream_t * substream,
+                                snd_pcm_hw_params_t * hw_params)
+{
+    m3_dma_t *s = (m3_dma_t*) substream->runtime->private_data;
+    int err;
+
+    if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
+        return err;
+    /* set buffer address */
+    s->buffer_addr = substream->runtime->dma_addr;
+    if (s->buffer_addr & 0x3) {
+        snd_printk("oh my, not aligned\n");
+        s->buffer_addr = s->buffer_addr & ~0x3;
+    }
+    return 0;
+}
+
+static int snd_m3_pcm_hw_free(snd_pcm_substream_t * substream)
+{
+    m3_dma_t *s;
+
+    if (substream->runtime->private_data == NULL)
+        return 0;
+    s = (m3_dma_t*) substream->runtime->private_data;
+    snd_pcm_lib_free_pages(substream);
+    s->buffer_addr = 0;
+    return 0;
+}
+
+static int
+snd_m3_pcm_prepare(snd_pcm_substream_t *subs)
+{
+    m3_t *chip = snd_pcm_substream_chip(subs);
+    snd_pcm_runtime_t *runtime = subs->runtime;
+    m3_dma_t *s = (m3_dma_t*)runtime->private_data;
+
+    snd_assert(s != NULL, return -ENXIO);
+
+    if (runtime->format != SNDRV_PCM_FORMAT_U8 &&
+        runtime->format != SNDRV_PCM_FORMAT_S16_LE)
+        return -EINVAL;
+    if (runtime->rate > 48000 ||
+        runtime->rate < 8000)
+        return -EINVAL;
+
+    spin_lock_irq(&chip->reg_lock);
+
+    snd_m3_pcm_setup1(chip, s, subs);
+
+    if (subs->stream == SNDRV_PCM_STREAM_PLAYBACK)
+        snd_m3_playback_setup(chip, s, subs);
+    else
+        snd_m3_capture_setup(chip, s, subs);
+
+    snd_m3_pcm_setup2(chip, s, runtime);
+
+    spin_unlock_irq(&chip->reg_lock);
+
+    return 0;
+}
+
+/*
+ * get current pointer
+ */
+static unsigned int
+snd_m3_get_pointer(m3_t *chip, m3_dma_t *s, snd_pcm_substream_t *subs)
+{
+    u16 hi = 0, lo = 0;
+    int retry = 10;
+    u32 addr;
+
+    /*
+     * try and get a valid answer
+     */
+    while (retry--) {
+        hi =  snd_m3_assp_read(chip, MEMTYPE_INTERNAL_DATA,
+                               s->inst.data + CDATA_HOST_SRC_CURRENTH);
+
+        lo = snd_m3_assp_read(chip, MEMTYPE_INTERNAL_DATA,
+                              s->inst.data + CDATA_HOST_SRC_CURRENTL);
+
+        if (hi == snd_m3_assp_read(chip, MEMTYPE_INTERNAL_DATA,
+                                   s->inst.data + CDATA_HOST_SRC_CURRENTH))
+            break;
+    }
+    addr = lo | ((u32)hi<<16);
+    return (unsigned int)(addr - s->buffer_addr);
+}
+
+static snd_pcm_uframes_t
+snd_m3_pcm_pointer(snd_pcm_substream_t * subs)
+{
+    m3_t *chip = snd_pcm_substream_chip(subs);
+    unsigned int ptr;
+    m3_dma_t *s = (m3_dma_t*)subs->runtime->private_data;
+    snd_assert(s != NULL, return 0);
+
+    spin_lock(&chip->reg_lock);
+    ptr = snd_m3_get_pointer(chip, s, subs);
+    spin_unlock(&chip->reg_lock);
+    return bytes_to_frames(subs->runtime, ptr);
+}
+
+
+/* update pointer */
+/* spinlock held! */
+static void snd_m3_update_ptr(m3_t *chip, m3_dma_t *s)
+{
+    snd_pcm_substream_t *subs = s->substream;
+    unsigned int hwptr;
+    int diff;
+
+    if (! s->running)
+        return;
+
+    hwptr = snd_m3_get_pointer(chip, s, subs);
+    /* try to avoid expensive modulo divisions */
+    if (hwptr >= s->dma_size)
+        hwptr %= s->dma_size;
+
+    diff = s->dma_size + hwptr - s->hwptr;
+    if (diff >= s->dma_size)
+        diff %= s->dma_size;
+
+    s->hwptr = hwptr;
+    s->count += diff;
+
+    if (s->count >= (signed)s->period_size) {
+        if (s->count < 2 * (signed)s->period_size)
+            s->count -= (signed)s->period_size;
+        else
+            s->count %= s->period_size;
+
+        spin_unlock(&chip->reg_lock);
+        snd_pcm_period_elapsed(subs);
+        spin_lock(&chip->reg_lock);
+    }
+}
+
+static void snd_m3_update_hw_volume(unsigned long private_data)
+{
+    m3_t *chip = (m3_t *) private_data;
+    int x, val;
+    unsigned long flags;
+
+    /* Figure out which volume control button was pushed,
+     based on differences from the default register
+     values. */
+    x = inb(chip->iobase + SHADOW_MIX_REG_VOICE) & 0xee;
+
+    /* Reset the volume control registers. */
+    outb(0x88, chip->iobase + SHADOW_MIX_REG_VOICE);
+    outb(0x88, chip->iobase + HW_VOL_COUNTER_VOICE);
+    outb(0x88, chip->iobase + SHADOW_MIX_REG_MASTER);
+    outb(0x88, chip->iobase + HW_VOL_COUNTER_MASTER);
+
+    if (!chip->master_switch || !chip->master_volume)
+        return;
+
+    /* FIXME: we can't call snd_ac97_* functions since here is in tasklet. */
+    spin_lock_irqsave(&chip->ac97_lock, flags);
+
+    val = chip->ac97->regs[AC97_MASTER_VOL];
+    switch (x) {
+    case 0x88:
+        /* mute */
+        val ^= 0x8000;
+        chip->ac97->regs[AC97_MASTER_VOL] = val;
+        outw(val, chip->iobase + CODEC_DATA);
+        outb(AC97_MASTER_VOL, chip->iobase + CODEC_COMMAND);
+        snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
+                       &chip->master_switch->id);
+        break;
+    case 0xaa:
+        /* volume up */
+        if ((val & 0x7f) > 0)
+            val--;
+        if ((val & 0x7f00) > 0)
+            val -= 0x0100;
+        chip->ac97->regs[AC97_MASTER_VOL] = val;
+        outw(val, chip->iobase + CODEC_DATA);
+        outb(AC97_MASTER_VOL, chip->iobase + CODEC_COMMAND);
+        snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
+                       &chip->master_volume->id);
+        break;
+    case 0x66:
+        /* volume down */
+        if ((val & 0x7f) < 0x1f)
+            val++;
+        if ((val & 0x7f00) < 0x1f00)
+            val += 0x0100;
+        chip->ac97->regs[AC97_MASTER_VOL] = val;
+        outw(val, chip->iobase + CODEC_DATA);
+        outb(AC97_MASTER_VOL, chip->iobase + CODEC_COMMAND);
+        snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
+                       &chip->master_volume->id);
+        break;
+    }
+    spin_unlock_irqrestore(&chip->ac97_lock, flags);
+}
+
+static irqreturn_t
+snd_m3_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+    m3_t *chip = dev_id;
+    u8 status;
+    int i;
+
+    status = inb(chip->iobase + HOST_INT_STATUS);
+
+    if (status == 0xff)
+        return IRQ_NONE;
+
+    if (status & HV_INT_PENDING)
+        tasklet_hi_schedule(&chip->hwvol_tq);
+    /*
+     * ack an assp int if its running
+     * and has an int pending
+     */
+    if (status & ASSP_INT_PENDING) {
+        u8 ctl;
+        ctl = inb(chip->iobase + ASSP_CONTROL_B);
+        if (!(ctl & STOP_ASSP_CLOCK)) {
+            ctl = inb(chip->iobase + ASSP_HOST_INT_STATUS);
+            if (ctl & DSP2HOST_REQ_TIMER) {
+                outb(DSP2HOST_REQ_TIMER, chip->iobase + ASSP_HOST_INT_STATUS);
+                /* update adc/dac info if it was a timer int */
+                spin_lock(&chip->reg_lock);
+                for (i = 0; i < chip->num_substreams; i++) {
+                    m3_dma_t *s = &chip->substreams[i];
+                    if (s->running)
+                        snd_m3_update_ptr(chip, s);
+                }
+                spin_unlock(&chip->reg_lock);
+            }
+        }
+    }
+
+#if 0 /* TODO: not supported yet */
+    if ((status & MPU401_INT_PENDING) && chip->rmidi)
+        snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data, regs);
+#endif
+
+    /* ack ints */
+    outb(status, chip->iobase + HOST_INT_STATUS);
+    return IRQ_HANDLED;
+}
+
+
+/*
+ */
+
+static snd_pcm_hardware_t snd_m3_playback =
+{
+    /*	info:		  */	(SNDRV_PCM_INFO_MMAP |
+                                 SNDRV_PCM_INFO_INTERLEAVED |
+                                 SNDRV_PCM_INFO_MMAP_VALID |
+                                 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+                                 /*SNDRV_PCM_INFO_PAUSE |*/
+                                 SNDRV_PCM_INFO_RESUME),
+                                 /*	formats:	  */	SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+                                 /*	rates:		  */	SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+                                 /*	rate_min:	  */	8000,
+                                 /*	rate_max:	  */	48000,
+                                 /*	channels_min:	  */	1,
+                                 /*	channels_max:	  */	2,
+                                 /*	buffer_bytes_max:  */	(512*1024),
+                                 /*	period_bytes_min:  */	64,
+                                 /*	period_bytes_max:  */	(512*1024),
+                                 /*	periods_min:	  */	1,
+                                 /*	periods_max:	  */	1024,
+                                 0
+};
+
+static snd_pcm_hardware_t snd_m3_capture =
+{
+    /*	info:		  */	(SNDRV_PCM_INFO_MMAP |
+                                 SNDRV_PCM_INFO_INTERLEAVED |
+                                 SNDRV_PCM_INFO_MMAP_VALID |
+                                 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+                                 /*SNDRV_PCM_INFO_PAUSE |*/
+                                 SNDRV_PCM_INFO_RESUME),
+                                 /*	formats:	  */	SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+                                 /*	rates:		  */	SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+                                 /*	rate_min:	  */	8000,
+                                 /*	rate_max:	  */	48000,
+                                 /*	channels_min:	  */	1,
+                                 /*	channels_max:	  */	2,
+                                 /*	buffer_bytes_max:  */	(512*1024),
+                                 /*	period_bytes_min:  */	64,
+                                 /*	period_bytes_max:  */	(512*1024),
+                                 /*	periods_min:	  */	1,
+                                 /*	periods_max:	  */	1024,
+                                 0
+};
+
+/*
+ */
+
+static int
+snd_m3_substream_open(m3_t *chip, snd_pcm_substream_t *subs)
+{
+    int i;
+    m3_dma_t *s;
+
+    spin_lock_irq(&chip->reg_lock);
+    for (i = 0; i < chip->num_substreams; i++) {
+        s = &chip->substreams[i];
+        if (! s->opened)
+            goto __found;
+    }
+    spin_unlock_irq(&chip->reg_lock);
+    return -ENOMEM;
+__found:
+    s->opened = 1;
+    s->running = 0;
+    spin_unlock_irq(&chip->reg_lock);
+
+    subs->runtime->private_data = s;
+    s->substream = subs;
+
+    /* set list owners */
+    if (subs->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+        s->index_list[0] = &chip->mixer_list;
+    } else
+        s->index_list[0] = &chip->adc1_list;
+    s->index_list[1] = &chip->msrc_list;
+    s->index_list[2] = &chip->dma_list;
+
+    return 0;
+}
+
+static void
+snd_m3_substream_close(m3_t *chip, snd_pcm_substream_t *subs)
+{
+    m3_dma_t *s = (m3_dma_t*) subs->runtime->private_data;
+
+    if (s == NULL)
+        return; /* not opened properly */
+
+    spin_lock_irq(&chip->reg_lock);
+    if (s->substream && s->running)
+        snd_m3_pcm_stop(chip, s, s->substream); /* does this happen? */
+    if (s->in_lists) {
+        snd_m3_remove_list(chip, s->index_list[0], s->index[0]);
+        snd_m3_remove_list(chip, s->index_list[1], s->index[1]);
+        snd_m3_remove_list(chip, s->index_list[2], s->index[2]);
+        s->in_lists = 0;
+    }
+    s->running = 0;
+    s->opened = 0;
+    spin_unlock_irq(&chip->reg_lock);
+}
+
+static int
+snd_m3_playback_open(snd_pcm_substream_t *subs)
+{
+    m3_t *chip = snd_pcm_substream_chip(subs);
+    snd_pcm_runtime_t *runtime = subs->runtime;
+    int err;
+
+    if ((err = snd_m3_substream_open(chip, subs)) < 0)
+        return err;
+
+    runtime->hw = snd_m3_playback;
+    snd_pcm_set_sync(subs);
+
+    return 0;
+}
+
+static int
+snd_m3_playback_close(snd_pcm_substream_t *subs)
+{
+    m3_t *chip = snd_pcm_substream_chip(subs);
+
+    snd_m3_substream_close(chip, subs);
+    return 0;
+}
+
+static int
+snd_m3_capture_open(snd_pcm_substream_t *subs)
+{
+    m3_t *chip = snd_pcm_substream_chip(subs);
+    snd_pcm_runtime_t *runtime = subs->runtime;
+    int err;
+
+    if ((err = snd_m3_substream_open(chip, subs)) < 0)
+        return err;
+
+    runtime->hw = snd_m3_capture;
+    snd_pcm_set_sync(subs);
+
+    return 0;
+}
+
+static int
+snd_m3_capture_close(snd_pcm_substream_t *subs)
+{
+    m3_t *chip = snd_pcm_substream_chip(subs);
+
+    snd_m3_substream_close(chip, subs);
+    return 0;
+}
+
+/*
+ * create pcm instance
+ */
+
+static snd_pcm_ops_t snd_m3_playback_ops = {
+    snd_m3_playback_open,
+    snd_m3_playback_close,
+    snd_pcm_lib_ioctl,
+    snd_m3_pcm_hw_params,
+    snd_m3_pcm_hw_free,
+    snd_m3_pcm_prepare,
+    snd_m3_pcm_trigger,
+    snd_m3_pcm_pointer,0,0
+};
+
+static snd_pcm_ops_t snd_m3_capture_ops = {
+    snd_m3_capture_open,
+    snd_m3_capture_close,
+    snd_pcm_lib_ioctl,
+    snd_m3_pcm_hw_params,
+    snd_m3_pcm_hw_free,
+    snd_m3_pcm_prepare,
+    snd_m3_pcm_trigger,
+    snd_m3_pcm_pointer,0,0
+};
+
+static int __devinit
+snd_m3_pcm(m3_t * chip, int device)
+{
+    snd_pcm_t *pcm;
+    int err;
+
+    err = snd_pcm_new(chip->card, chip->card->driver, device,
+                      MAX_PLAYBACKS, MAX_CAPTURES, &pcm);
+    if (err < 0)
+        return err;
+
+    snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_m3_playback_ops);
+    snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_m3_capture_ops);
+
+    pcm->private_data = chip;
+    pcm->info_flags = 0;
+    strcpy(pcm->name, chip->card->driver);
+    chip->pcm = pcm;
+
+    snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+                                          snd_dma_pci_data(chip->pci), 64*1024, 64*1024);
+
+    return 0;
+}
+
+
+/*
+ * ac97 interface
+ */
+
+/*
+ * Wait for the ac97 serial bus to be free.
+ * return nonzero if the bus is still busy.
+ */
+static int snd_m3_ac97_wait(m3_t *chip)
+{
+    int i = 10000;
+
+    do {
+        if (! (snd_m3_inb(chip, 0x30) & 1))
+            return 0;
+        cpu_relax();
+    } while (i-- > 0);
+
+    snd_printk("ac97 serial bus busy\n");
+    return 1;
+}
+
+static unsigned short
+snd_m3_ac97_read(ac97_t *ac97, unsigned short reg)
+{
+    m3_t *chip = ac97->private_data;
+    unsigned long flags;
+    unsigned short data = 0xffff;
+
+    if (snd_m3_ac97_wait(chip))
+        goto fail;
+    spin_lock_irqsave(&chip->ac97_lock, flags);
+    snd_m3_outb(chip, 0x80 | (reg & 0x7f), CODEC_COMMAND);
+    if (snd_m3_ac97_wait(chip))
+        goto fail_unlock;
+    data = snd_m3_inw(chip, CODEC_DATA);
+fail_unlock:
+    spin_unlock_irqrestore(&chip->ac97_lock, flags);
+fail:
+    return data;
+}
+
+static void
+snd_m3_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short val)
+{
+    m3_t *chip = ac97->private_data;
+    unsigned long flags;
+
+    if (snd_m3_ac97_wait(chip))
+        return;
+    spin_lock_irqsave(&chip->ac97_lock, flags);
+    snd_m3_outw(chip, val, CODEC_DATA);
+    snd_m3_outb(chip, reg & 0x7f, CODEC_COMMAND);
+    spin_unlock_irqrestore(&chip->ac97_lock, flags);
+}
+
+
+static void snd_m3_remote_codec_config(int io, int isremote)
+{
+    isremote = isremote ? 1 : 0;
+
+    outw((inw(io + RING_BUS_CTRL_B) & ~SECOND_CODEC_ID_MASK) | isremote,
+         io + RING_BUS_CTRL_B);
+    outw((inw(io + SDO_OUT_DEST_CTRL) & ~COMMAND_ADDR_OUT) | isremote,
+         io + SDO_OUT_DEST_CTRL);
+    outw((inw(io + SDO_IN_DEST_CTRL) & ~STATUS_ADDR_IN) | isremote,
+         io + SDO_IN_DEST_CTRL);
+}
+
+/*
+ * hack, returns non zero on err
+ */
+static int snd_m3_try_read_vendor(m3_t *chip)
+{
+    u16 ret;
+
+    if (snd_m3_ac97_wait(chip))
+        return 1;
+
+    snd_m3_outb(chip, 0x80 | (AC97_VENDOR_ID1 & 0x7f), 0x30);
+
+    if (snd_m3_ac97_wait(chip))
+        return 1;
+
+    ret = snd_m3_inw(chip, 0x32);
+
+    return (ret == 0) || (ret == 0xffff);
+}
+
+static void snd_m3_ac97_reset(m3_t *chip)
+{
+    u16 dir;
+    int delay1 = 0, delay2 = 0, i;
+    int io = chip->iobase;
+
+    if (chip->allegro_flag) {
+        /*
+         * the onboard codec on the allegro seems
+         * to want to wait a very long time before
+         * coming back to life
+         */
+        delay1 = 50;
+        delay2 = 800;
+    } else {
+        /* maestro3 */
+        delay1 = 20;
+        delay2 = 500;
+    }
+
+    for (i = 0; i < 5; i++) {
+        dir = inw(io + GPIO_DIRECTION);
+        if (! chip->quirk || ! chip->quirk->irda_workaround)
+            dir |= 0x10; /* assuming pci bus master? */
+
+        snd_m3_remote_codec_config(io, 0);
+
+        outw(IO_SRAM_ENABLE, io + RING_BUS_CTRL_A);
+        udelay(20);
+
+        outw(dir & ~GPO_PRIMARY_AC97 , io + GPIO_DIRECTION);
+        outw(~GPO_PRIMARY_AC97 , io + GPIO_MASK);
+        outw(0, io + GPIO_DATA);
+        outw(dir | GPO_PRIMARY_AC97, io + GPIO_DIRECTION);
+
+        set_current_state(TASK_UNINTERRUPTIBLE);
+        schedule_timeout((delay1 * HZ) / 1000);
+
+        outw(GPO_PRIMARY_AC97, io + GPIO_DATA);
+        udelay(5);
+        /* ok, bring back the ac-link */
+        outw(IO_SRAM_ENABLE | SERIAL_AC_LINK_ENABLE, io + RING_BUS_CTRL_A);
+        outw(~0, io + GPIO_MASK);
+
+        set_current_state(TASK_UNINTERRUPTIBLE);
+        schedule_timeout((delay2 * HZ) / 1000);
+
+        if (! snd_m3_try_read_vendor(chip))
+            break;
+
+        delay1 += 10;
+        delay2 += 100;
+
+        snd_printd("maestro3: retrying codec reset with delays of %d and %d ms\n",
+                   delay1, delay2);
+    }
+
+#if 0
+    /* more gung-ho reset that doesn't
+     * seem to work anywhere :)
+     */
+    tmp = inw(io + RING_BUS_CTRL_A);
+    outw(RAC_SDFS_ENABLE|LAC_SDFS_ENABLE, io + RING_BUS_CTRL_A);
+    msleep(20);
+    outw(tmp, io + RING_BUS_CTRL_A);
+    msleep(50);
+#endif
+}
+
+static int __devinit snd_m3_mixer(m3_t *chip)
+{
+    ac97_bus_t *pbus;
+    ac97_template_t ac97;
+    snd_ctl_elem_id_t id;
+    int err;
+    static ac97_bus_ops_t ops = {
+        0,snd_m3_ac97_write,
+        snd_m3_ac97_read,0,0
+    };
+
+    if ((err = snd_ac97_bus(chip->card, 0, &ops, NULL, &pbus)) < 0)
+        return err;
+
+    memset(&ac97, 0, sizeof(ac97));
+    ac97.private_data = chip;
+    if ((err = snd_ac97_mixer(pbus, &ac97, &chip->ac97)) < 0)
+        return err;
+
+    /* seems ac97 PCM needs initialization.. hack hack.. */
+    snd_ac97_write(chip->ac97, AC97_PCM, 0x8000 | (15 << 8) | 15);
+    set_current_state(TASK_UNINTERRUPTIBLE);
+    schedule_timeout(HZ / 10);
+    snd_ac97_write(chip->ac97, AC97_PCM, 0);
+
+    memset(&id, 0, sizeof(id));
+    id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+    strcpy(id.name, "Master Playback Switch");
+    chip->master_switch = snd_ctl_find_id(chip->card, &id);
+    memset(&id, 0, sizeof(id));
+    id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+    strcpy(id.name, "Master Playback Volume");
+    chip->master_volume = snd_ctl_find_id(chip->card, &id);
+
+    return 0;
+}
+
+
+/*
+ * DSP Code images
+ */
+
+static const u16 assp_kernel_image[] __devinitdata = {
+    0x7980, 0x0030, 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x00FB, 0x7980, 0x00DD, 0x7980, 0x03B4,
+    0x7980, 0x0332, 0x7980, 0x0287, 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x03B4,
+    0x7980, 0x031A, 0x7980, 0x03B4, 0x7980, 0x022F, 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x03B4,
+    0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x0063, 0x7980, 0x006B, 0x7980, 0x03B4, 0x7980, 0x03B4,
+    0xBF80, 0x2C7C, 0x8806, 0x8804, 0xBE40, 0xBC20, 0xAE09, 0x1000, 0xAE0A, 0x0001, 0x6938, 0xEB08,
+    0x0053, 0x695A, 0xEB08, 0x00D6, 0x0009, 0x8B88, 0x6980, 0xE388, 0x0036, 0xBE30, 0xBC20, 0x6909,
+    0xB801, 0x9009, 0xBE41, 0xBE41, 0x6928, 0xEB88, 0x0078, 0xBE41, 0xBE40, 0x7980, 0x0038, 0xBE41,
+    0xBE41, 0x903A, 0x6938, 0xE308, 0x0056, 0x903A, 0xBE41, 0xBE40, 0xEF00, 0x903A, 0x6939, 0xE308,
+    0x005E, 0x903A, 0xEF00, 0x690B, 0x660C, 0xEF8C, 0x690A, 0x660C, 0x620B, 0x6609, 0xEF00, 0x6910,
+    0x660F, 0xEF04, 0xE388, 0x0075, 0x690E, 0x660F, 0x6210, 0x660D, 0xEF00, 0x690E, 0x660D, 0xEF00,
+    0xAE70, 0x0001, 0xBC20, 0xAE27, 0x0001, 0x6939, 0xEB08, 0x005D, 0x6926, 0xB801, 0x9026, 0x0026,
+    0x8B88, 0x6980, 0xE388, 0x00CB, 0x9028, 0x0D28, 0x4211, 0xE100, 0x007A, 0x4711, 0xE100, 0x00A0,
+    0x7A80, 0x0063, 0xB811, 0x660A, 0x6209, 0xE304, 0x007A, 0x0C0B, 0x4005, 0x100A, 0xBA01, 0x9012,
+    0x0C12, 0x4002, 0x7980, 0x00AF, 0x7A80, 0x006B, 0xBE02, 0x620E, 0x660D, 0xBA10, 0xE344, 0x007A,
+    0x0C10, 0x4005, 0x100E, 0xBA01, 0x9012, 0x0C12, 0x4002, 0x1003, 0xBA02, 0x9012, 0x0C12, 0x4000,
+    0x1003, 0xE388, 0x00BA, 0x1004, 0x7980, 0x00BC, 0x1004, 0xBA01, 0x9012, 0x0C12, 0x4001, 0x0C05,
+    0x4003, 0x0C06, 0x4004, 0x1011, 0xBFB0, 0x01FF, 0x9012, 0x0C12, 0x4006, 0xBC20, 0xEF00, 0xAE26,
+    0x1028, 0x6970, 0xBFD0, 0x0001, 0x9070, 0xE388, 0x007A, 0xAE28, 0x0000, 0xEF00, 0xAE70, 0x0300,
+    0x0C70, 0xB00C, 0xAE5A, 0x0000, 0xEF00, 0x7A80, 0x038A, 0x697F, 0xB801, 0x907F, 0x0056, 0x8B88,
+    0x0CA0, 0xB008, 0xAF71, 0xB000, 0x4E71, 0xE200, 0x00F3, 0xAE56, 0x1057, 0x0056, 0x0CA0, 0xB008,
+    0x8056, 0x7980, 0x03A1, 0x0810, 0xBFA0, 0x1059, 0xE304, 0x03A1, 0x8056, 0x7980, 0x03A1, 0x7A80,
+    0x038A, 0xBF01, 0xBE43, 0xBE59, 0x907C, 0x6937, 0xE388, 0x010D, 0xBA01, 0xE308, 0x010C, 0xAE71,
+    0x0004, 0x0C71, 0x5000, 0x6936, 0x9037, 0xBF0A, 0x109E, 0x8B8A, 0xAF80, 0x8014, 0x4C80, 0xBF0A,
+    0x0560, 0xF500, 0xBF0A, 0x0520, 0xB900, 0xBB17, 0x90A0, 0x6917, 0xE388, 0x0148, 0x0D17, 0xE100,
+    0x0127, 0xBF0C, 0x0578, 0xBF0D, 0x057C, 0x7980, 0x012B, 0xBF0C, 0x0538, 0xBF0D, 0x053C, 0x6900,
+    0xE308, 0x0135, 0x8B8C, 0xBE59, 0xBB07, 0x90A0, 0xBC20, 0x7980, 0x0157, 0x030C, 0x8B8B, 0xB903,
+    0x8809, 0xBEC6, 0x013E, 0x69AC, 0x90AB, 0x69AD, 0x90AB, 0x0813, 0x660A, 0xE344, 0x0144, 0x0309,
+    0x830C, 0xBC20, 0x7980, 0x0157, 0x6955, 0xE388, 0x0157, 0x7C38, 0xBF0B, 0x0578, 0xF500, 0xBF0B,
+    0x0538, 0xB907, 0x8809, 0xBEC6, 0x0156, 0x10AB, 0x90AA, 0x6974, 0xE388, 0x0163, 0xAE72, 0x0540,
+    0xF500, 0xAE72, 0x0500, 0xAE61, 0x103B, 0x7A80, 0x02F6, 0x6978, 0xE388, 0x0182, 0x8B8C, 0xBF0C,
+    0x0560, 0xE500, 0x7C40, 0x0814, 0xBA20, 0x8812, 0x733D, 0x7A80, 0x0380, 0x733E, 0x7A80, 0x0380,
+    0x8B8C, 0xBF0C, 0x056C, 0xE500, 0x7C40, 0x0814, 0xBA2C, 0x8812, 0x733F, 0x7A80, 0x0380, 0x7340,
+    0x7A80, 0x0380, 0x6975, 0xE388, 0x018E, 0xAE72, 0x0548, 0xF500, 0xAE72, 0x0508, 0xAE61, 0x1041,
+    0x7A80, 0x02F6, 0x6979, 0xE388, 0x01AD, 0x8B8C, 0xBF0C, 0x0560, 0xE500, 0x7C40, 0x0814, 0xBA18,
+    0x8812, 0x7343, 0x7A80, 0x0380, 0x7344, 0x7A80, 0x0380, 0x8B8C, 0xBF0C, 0x056C, 0xE500, 0x7C40,
+    0x0814, 0xBA24, 0x8812, 0x7345, 0x7A80, 0x0380, 0x7346, 0x7A80, 0x0380, 0x6976, 0xE388, 0x01B9,
+    0xAE72, 0x0558, 0xF500, 0xAE72, 0x0518, 0xAE61, 0x1047, 0x7A80, 0x02F6, 0x697A, 0xE388, 0x01D8,
+    0x8B8C, 0xBF0C, 0x0560, 0xE500, 0x7C40, 0x0814, 0xBA08, 0x8812, 0x7349, 0x7A80, 0x0380, 0x734A,
+    0x7A80, 0x0380, 0x8B8C, 0xBF0C, 0x056C, 0xE500, 0x7C40, 0x0814, 0xBA14, 0x8812, 0x734B, 0x7A80,
+    0x0380, 0x734C, 0x7A80, 0x0380, 0xBC21, 0xAE1C, 0x1090, 0x8B8A, 0xBF0A, 0x0560, 0xE500, 0x7C40,
+    0x0812, 0xB804, 0x8813, 0x8B8D, 0xBF0D, 0x056C, 0xE500, 0x7C40, 0x0815, 0xB804, 0x8811, 0x7A80,
+    0x034A, 0x8B8A, 0xBF0A, 0x0560, 0xE500, 0x7C40, 0x731F, 0xB903, 0x8809, 0xBEC6, 0x01F9, 0x548A,
+    0xBE03, 0x98A0, 0x7320, 0xB903, 0x8809, 0xBEC6, 0x0201, 0x548A, 0xBE03, 0x98A0, 0x1F20, 0x2F1F,
+    0x9826, 0xBC20, 0x6935, 0xE388, 0x03A1, 0x6933, 0xB801, 0x9033, 0xBFA0, 0x02EE, 0xE308, 0x03A1,
+    0x9033, 0xBF00, 0x6951, 0xE388, 0x021F, 0x7334, 0xBE80, 0x5760, 0xBE03, 0x9F7E, 0xBE59, 0x9034,
+    0x697E, 0x0D51, 0x9013, 0xBC20, 0x695C, 0xE388, 0x03A1, 0x735E, 0xBE80, 0x5760, 0xBE03, 0x9F7E,
+    0xBE59, 0x905E, 0x697E, 0x0D5C, 0x9013, 0x7980, 0x03A1, 0x7A80, 0x038A, 0xBF01, 0xBE43, 0x6977,
+    0xE388, 0x024E, 0xAE61, 0x104D, 0x0061, 0x8B88, 0x6980, 0xE388, 0x024E, 0x9071, 0x0D71, 0x000B,
+    0xAFA0, 0x8010, 0xAFA0, 0x8010, 0x0810, 0x660A, 0xE308, 0x0249, 0x0009, 0x0810, 0x660C, 0xE388,
+    0x024E, 0x800B, 0xBC20, 0x697B, 0xE388, 0x03A1, 0xBF0A, 0x109E, 0x8B8A, 0xAF80, 0x8014, 0x4C80,
+    0xE100, 0x0266, 0x697C, 0xBF90, 0x0560, 0x9072, 0x0372, 0x697C, 0xBF90, 0x0564, 0x9073, 0x0473,
+    0x7980, 0x0270, 0x697C, 0xBF90, 0x0520, 0x9072, 0x0372, 0x697C, 0xBF90, 0x0524, 0x9073, 0x0473,
+    0x697C, 0xB801, 0x907C, 0xBF0A, 0x10FD, 0x8B8A, 0xAF80, 0x8010, 0x734F, 0x548A, 0xBE03, 0x9880,
+    0xBC21, 0x7326, 0x548B, 0xBE03, 0x618B, 0x988C, 0xBE03, 0x6180, 0x9880, 0x7980, 0x03A1, 0x7A80,
+    0x038A, 0x0D28, 0x4711, 0xE100, 0x02BE, 0xAF12, 0x4006, 0x6912, 0xBFB0, 0x0C00, 0xE388, 0x02B6,
+    0xBFA0, 0x0800, 0xE388, 0x02B2, 0x6912, 0xBFB0, 0x0C00, 0xBFA0, 0x0400, 0xE388, 0x02A3, 0x6909,
+    0x900B, 0x7980, 0x02A5, 0xAF0B, 0x4005, 0x6901, 0x9005, 0x6902, 0x9006, 0x4311, 0xE100, 0x02ED,
+    0x6911, 0xBFC0, 0x2000, 0x9011, 0x7980, 0x02ED, 0x6909, 0x900B, 0x7980, 0x02B8, 0xAF0B, 0x4005,
+    0xAF05, 0x4003, 0xAF06, 0x4004, 0x7980, 0x02ED, 0xAF12, 0x4006, 0x6912, 0xBFB0, 0x0C00, 0xE388,
+    0x02E7, 0xBFA0, 0x0800, 0xE388, 0x02E3, 0x6912, 0xBFB0, 0x0C00, 0xBFA0, 0x0400, 0xE388, 0x02D4,
+    0x690D, 0x9010, 0x7980, 0x02D6, 0xAF10, 0x4005, 0x6901, 0x9005, 0x6902, 0x9006, 0x4311, 0xE100,
+    0x02ED, 0x6911, 0xBFC0, 0x2000, 0x9011, 0x7980, 0x02ED, 0x690D, 0x9010, 0x7980, 0x02E9, 0xAF10,
+    0x4005, 0xAF05, 0x4003, 0xAF06, 0x4004, 0xBC20, 0x6970, 0x9071, 0x7A80, 0x0078, 0x6971, 0x9070,
+    0x7980, 0x03A1, 0xBC20, 0x0361, 0x8B8B, 0x6980, 0xEF88, 0x0272, 0x0372, 0x7804, 0x9071, 0x0D71,
+    0x8B8A, 0x000B, 0xB903, 0x8809, 0xBEC6, 0x0309, 0x69A8, 0x90AB, 0x69A8, 0x90AA, 0x0810, 0x660A,
+    0xE344, 0x030F, 0x0009, 0x0810, 0x660C, 0xE388, 0x0314, 0x800B, 0xBC20, 0x6961, 0xB801, 0x9061,
+    0x7980, 0x02F7, 0x7A80, 0x038A, 0x5D35, 0x0001, 0x6934, 0xB801, 0x9034, 0xBF0A, 0x109E, 0x8B8A,
+    0xAF80, 0x8014, 0x4880, 0xAE72, 0x0550, 0xF500, 0xAE72, 0x0510, 0xAE61, 0x1051, 0x7A80, 0x02F6,
+    0x7980, 0x03A1, 0x7A80, 0x038A, 0x5D35, 0x0002, 0x695E, 0xB801, 0x905E, 0xBF0A, 0x109E, 0x8B8A,
+    0xAF80, 0x8014, 0x4780, 0xAE72, 0x0558, 0xF500, 0xAE72, 0x0518, 0xAE61, 0x105C, 0x7A80, 0x02F6,
+    0x7980, 0x03A1, 0x001C, 0x8B88, 0x6980, 0xEF88, 0x901D, 0x0D1D, 0x100F, 0x6610, 0xE38C, 0x0358,
+    0x690E, 0x6610, 0x620F, 0x660D, 0xBA0F, 0xE301, 0x037A, 0x0410, 0x8B8A, 0xB903, 0x8809, 0xBEC6,
+    0x036C, 0x6A8C, 0x61AA, 0x98AB, 0x6A8C, 0x61AB, 0x98AD, 0x6A8C, 0x61AD, 0x98A9, 0x6A8C, 0x61A9,
+    0x98AA, 0x7C04, 0x8B8B, 0x7C04, 0x8B8D, 0x7C04, 0x8B89, 0x7C04, 0x0814, 0x660E, 0xE308, 0x0379,
+    0x040D, 0x8410, 0xBC21, 0x691C, 0xB801, 0x901C, 0x7980, 0x034A, 0xB903, 0x8809, 0x8B8A, 0xBEC6,
+    0x0388, 0x54AC, 0xBE03, 0x618C, 0x98AA, 0xEF00, 0xBC20, 0xBE46, 0x0809, 0x906B, 0x080A, 0x906C,
+    0x080B, 0x906D, 0x081A, 0x9062, 0x081B, 0x9063, 0x081E, 0x9064, 0xBE59, 0x881E, 0x8065, 0x8166,
+    0x8267, 0x8368, 0x8469, 0x856A, 0xEF00, 0xBC20, 0x696B, 0x8809, 0x696C, 0x880A, 0x696D, 0x880B,
+    0x6962, 0x881A, 0x6963, 0x881B, 0x6964, 0x881E, 0x0065, 0x0166, 0x0267, 0x0368, 0x0469, 0x056A,
+    0xBE3A,
+};
+
+/*
+ * Mini sample rate converter code image
+ * that is to be loaded at 0x400 on the DSP.
+ */
+static const u16 assp_minisrc_image[] __devinitdata = {
+
+    0xBF80, 0x101E, 0x906E, 0x006E, 0x8B88, 0x6980, 0xEF88, 0x906F, 0x0D6F, 0x6900, 0xEB08, 0x0412,
+    0xBC20, 0x696E, 0xB801, 0x906E, 0x7980, 0x0403, 0xB90E, 0x8807, 0xBE43, 0xBF01, 0xBE47, 0xBE41,
+    0x7A80, 0x002A, 0xBE40, 0x3029, 0xEFCC, 0xBE41, 0x7A80, 0x0028, 0xBE40, 0x3028, 0xEFCC, 0x6907,
+    0xE308, 0x042A, 0x6909, 0x902C, 0x7980, 0x042C, 0x690D, 0x902C, 0x1009, 0x881A, 0x100A, 0xBA01,
+    0x881B, 0x100D, 0x881C, 0x100E, 0xBA01, 0x881D, 0xBF80, 0x00ED, 0x881E, 0x050C, 0x0124, 0xB904,
+    0x9027, 0x6918, 0xE308, 0x04B3, 0x902D, 0x6913, 0xBFA0, 0x7598, 0xF704, 0xAE2D, 0x00FF, 0x8B8D,
+    0x6919, 0xE308, 0x0463, 0x691A, 0xE308, 0x0456, 0xB907, 0x8809, 0xBEC6, 0x0453, 0x10A9, 0x90AD,
+    0x7980, 0x047C, 0xB903, 0x8809, 0xBEC6, 0x0460, 0x1889, 0x6C22, 0x90AD, 0x10A9, 0x6E23, 0x6C22,
+    0x90AD, 0x7980, 0x047C, 0x101A, 0xE308, 0x046F, 0xB903, 0x8809, 0xBEC6, 0x046C, 0x10A9, 0x90A0,
+    0x90AD, 0x7980, 0x047C, 0xB901, 0x8809, 0xBEC6, 0x047B, 0x1889, 0x6C22, 0x90A0, 0x90AD, 0x10A9,
+    0x6E23, 0x6C22, 0x90A0, 0x90AD, 0x692D, 0xE308, 0x049C, 0x0124, 0xB703, 0xB902, 0x8818, 0x8B89,
+    0x022C, 0x108A, 0x7C04, 0x90A0, 0x692B, 0x881F, 0x7E80, 0x055B, 0x692A, 0x8809, 0x8B89, 0x99A0,
+    0x108A, 0x90A0, 0x692B, 0x881F, 0x7E80, 0x055B, 0x692A, 0x8809, 0x8B89, 0x99AF, 0x7B99, 0x0484,
+    0x0124, 0x060F, 0x101B, 0x2013, 0x901B, 0xBFA0, 0x7FFF, 0xE344, 0x04AC, 0x901B, 0x8B89, 0x7A80,
+    0x051A, 0x6927, 0xBA01, 0x9027, 0x7A80, 0x0523, 0x6927, 0xE308, 0x049E, 0x7980, 0x050F, 0x0624,
+    0x1026, 0x2013, 0x9026, 0xBFA0, 0x7FFF, 0xE304, 0x04C0, 0x8B8D, 0x7A80, 0x051A, 0x7980, 0x04B4,
+    0x9026, 0x1013, 0x3026, 0x901B, 0x8B8D, 0x7A80, 0x051A, 0x7A80, 0x0523, 0x1027, 0xBA01, 0x9027,
+    0xE308, 0x04B4, 0x0124, 0x060F, 0x8B89, 0x691A, 0xE308, 0x04EA, 0x6919, 0xE388, 0x04E0, 0xB903,
+    0x8809, 0xBEC6, 0x04DD, 0x1FA0, 0x2FAE, 0x98A9, 0x7980, 0x050F, 0xB901, 0x8818, 0xB907, 0x8809,
+    0xBEC6, 0x04E7, 0x10EE, 0x90A9, 0x7980, 0x050F, 0x6919, 0xE308, 0x04FE, 0xB903, 0x8809, 0xBE46,
+    0xBEC6, 0x04FA, 0x17A0, 0xBE1E, 0x1FAE, 0xBFBF, 0xFF00, 0xBE13, 0xBFDF, 0x8080, 0x99A9, 0xBE47,
+    0x7980, 0x050F, 0xB901, 0x8809, 0xBEC6, 0x050E, 0x16A0, 0x26A0, 0xBFB7, 0xFF00, 0xBE1E, 0x1EA0,
+    0x2EAE, 0xBFBF, 0xFF00, 0xBE13, 0xBFDF, 0x8080, 0x99A9, 0x850C, 0x860F, 0x6907, 0xE388, 0x0516,
+    0x0D07, 0x8510, 0xBE59, 0x881E, 0xBE4A, 0xEF00, 0x101E, 0x901C, 0x101F, 0x901D, 0x10A0, 0x901E,
+    0x10A0, 0x901F, 0xEF00, 0x101E, 0x301C, 0x9020, 0x731B, 0x5420, 0xBE03, 0x9825, 0x1025, 0x201C,
+    0x9025, 0x7325, 0x5414, 0xBE03, 0x8B8E, 0x9880, 0x692F, 0xE388, 0x0539, 0xBE59, 0xBB07, 0x6180,
+    0x9880, 0x8BA0, 0x101F, 0x301D, 0x9021, 0x731B, 0x5421, 0xBE03, 0x982E, 0x102E, 0x201D, 0x902E,
+    0x732E, 0x5415, 0xBE03, 0x9880, 0x692F, 0xE388, 0x054F, 0xBE59, 0xBB07, 0x6180, 0x9880, 0x8BA0,
+    0x6918, 0xEF08, 0x7325, 0x5416, 0xBE03, 0x98A0, 0x732E, 0x5417, 0xBE03, 0x98A0, 0xEF00, 0x8BA0,
+    0xBEC6, 0x056B, 0xBE59, 0xBB04, 0xAA90, 0xBE04, 0xBE1E, 0x99E0, 0x8BE0, 0x69A0, 0x90D0, 0x69A0,
+    0x90D0, 0x081F, 0xB805, 0x881F, 0x8B90, 0x69A0, 0x90D0, 0x69A0, 0x9090, 0x8BD0, 0x8BD8, 0xBE1F,
+    0xEF00, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+};
+
+
+/*
+ * initialize ASSP
+ */
+
+#define MINISRC_LPF_LEN 10
+static const u16 minisrc_lpf[MINISRC_LPF_LEN] __devinitdata = {
+    0X0743, 0X1104, 0X0A4C, 0XF88D, 0X242C,
+    0X1023, 0X1AA9, 0X0B60, 0XEFDD, 0X186F
+};
+
+static void __devinit snd_m3_assp_init(m3_t *chip)
+{
+    unsigned int i;
+
+    /* zero kernel data */
+    for (i = 0; i < (REV_B_DATA_MEMORY_UNIT_LENGTH * NUM_UNITS_KERNEL_DATA) / 2; i++)
+        snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+                          KDATA_BASE_ADDR + i, 0);
+
+    /* zero mixer data? */
+    for (i = 0; i < (REV_B_DATA_MEMORY_UNIT_LENGTH * NUM_UNITS_KERNEL_DATA) / 2; i++)
+        snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+                          KDATA_BASE_ADDR2 + i, 0);
+
+    /* init dma pointer */
+    snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+                      KDATA_CURRENT_DMA,
+                      KDATA_DMA_XFER0);
+
+    /* write kernel into code memory.. */
+    for (i = 0 ; i < sizeof(assp_kernel_image) / 2; i++) {
+        snd_m3_assp_write(chip, MEMTYPE_INTERNAL_CODE,
+                          REV_B_CODE_MEMORY_BEGIN + i,
+                          assp_kernel_image[i]);
+    }
+
+    /*
+     * We only have this one client and we know that 0x400
+     * is free in our kernel's mem map, so lets just
+     * drop it there.  It seems that the minisrc doesn't
+     * need vectors, so we won't bother with them..
+     */
+    for (i = 0; i < sizeof(assp_minisrc_image) / 2; i++) {
+        snd_m3_assp_write(chip, MEMTYPE_INTERNAL_CODE,
+                          0x400 + i,
+                          assp_minisrc_image[i]);
+    }
+
+    /*
+     * write the coefficients for the low pass filter?
+     */
+    for (i = 0; i < MINISRC_LPF_LEN ; i++) {
+        snd_m3_assp_write(chip, MEMTYPE_INTERNAL_CODE,
+                          0x400 + MINISRC_COEF_LOC + i,
+                          minisrc_lpf[i]);
+    }
+
+    snd_m3_assp_write(chip, MEMTYPE_INTERNAL_CODE,
+                      0x400 + MINISRC_COEF_LOC + MINISRC_LPF_LEN,
+                      0x8000);
+
+    /*
+     * the minisrc is the only thing on
+     * our task list..
+     */
+    snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+                      KDATA_TASK0,
+                      0x400);
+
+    /*
+     * init the mixer number..
+     */
+
+    snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+                      KDATA_MIXER_TASK_NUMBER,0);
+
+    /*
+     * EXTREME KERNEL MASTER VOLUME
+     */
+    snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+                      KDATA_DAC_LEFT_VOLUME, ARB_VOLUME);
+    snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+                      KDATA_DAC_RIGHT_VOLUME, ARB_VOLUME);
+
+    chip->mixer_list.curlen = 0;
+    chip->mixer_list.mem_addr = KDATA_MIXER_XFER0;
+    chip->mixer_list.max = MAX_VIRTUAL_MIXER_CHANNELS;
+    chip->adc1_list.curlen = 0;
+    chip->adc1_list.mem_addr = KDATA_ADC1_XFER0;
+    chip->adc1_list.max = MAX_VIRTUAL_ADC1_CHANNELS;
+    chip->dma_list.curlen = 0;
+    chip->dma_list.mem_addr = KDATA_DMA_XFER0;
+    chip->dma_list.max = MAX_VIRTUAL_DMA_CHANNELS;
+    chip->msrc_list.curlen = 0;
+    chip->msrc_list.mem_addr = KDATA_INSTANCE0_MINISRC;
+    chip->msrc_list.max = MAX_INSTANCE_MINISRC;
+}
+
+
+static int __devinit snd_m3_assp_client_init(m3_t *chip, m3_dma_t *s, int index)
+{
+    int data_bytes = 2 * ( MINISRC_TMP_BUFFER_SIZE / 2 +
+                          MINISRC_IN_BUFFER_SIZE / 2 +
+                          1 + MINISRC_OUT_BUFFER_SIZE / 2 + 1 );
+    int address, i;
+
+    /*
+     * the revb memory map has 0x1100 through 0x1c00
+     * free.
+     */
+
+    /*
+     * align instance address to 256 bytes so that its
+     * shifted list address is aligned.
+     * list address = (mem address >> 1) >> 7;
+     */
+    data_bytes = (data_bytes + 255) & ~255;
+    address = 0x1100 + ((data_bytes/2) * index);
+
+    if ((address + (data_bytes/2)) >= 0x1c00) {
+        snd_printk("no memory for %d bytes at ind %d (addr 0x%x)\n",
+                   data_bytes, index, address);
+        return -ENOMEM;
+    }
+
+    s->number = index;
+    s->inst.code = 0x400;
+    s->inst.data = address;
+
+    for (i = data_bytes / 2; i > 0; address++, i--) {
+        snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+                          address, 0);
+    }
+
+    return 0;
+}
+
+
+/*
+ * this works for the reference board, have to find
+ * out about others
+ *
+ * this needs more magic for 4 speaker, but..
+ */
+static void
+snd_m3_amp_enable(m3_t *chip, int enable)
+{
+    int io = chip->iobase;
+    u16 gpo, polarity;
+
+    if (! chip->external_amp)
+        return;
+
+    polarity = enable ? 0 : 1;
+    polarity = polarity << chip->amp_gpio;
+    gpo = 1 << chip->amp_gpio;
+
+    outw(~gpo, io + GPIO_MASK);
+
+    outw(inw(io + GPIO_DIRECTION) | gpo,
+         io + GPIO_DIRECTION);
+
+    outw((GPO_SECONDARY_AC97 | GPO_PRIMARY_AC97 | polarity),
+         io + GPIO_DATA);
+
+    outw(0xffff, io + GPIO_MASK);
+}
+
+static int
+snd_m3_chip_init(m3_t *chip)
+{
+    struct pci_dev *pcidev = chip->pci;
+    unsigned long io = chip->iobase;
+    u32 n;
+    u16 w;
+    u8 t; /* makes as much sense as 'n', no? */
+
+    pci_read_config_word(pcidev, PCI_LEGACY_AUDIO_CTRL, &w);
+    w &= ~(SOUND_BLASTER_ENABLE|FM_SYNTHESIS_ENABLE|
+           MPU401_IO_ENABLE|MPU401_IRQ_ENABLE|ALIAS_10BIT_IO|
+           DISABLE_LEGACY);
+    pci_write_config_word(pcidev, PCI_LEGACY_AUDIO_CTRL, w);
+
+    if (chip->hv_quirk && chip->hv_quirk->is_omnibook) {
+        /*
+         * Volume buttons on some HP OmniBook laptops don't work
+         * correctly. This makes them work for the most part.
+         *
+         * Volume up and down buttons on the laptop side work.
+         * Fn+cursor_up (volme up) works.
+         * Fn+cursor_down (volume down) doesn't work.
+         * Fn+F7 (mute) works acts as volume up.
+         */
+        outw(~(GPI_VOL_DOWN|GPI_VOL_UP), io + GPIO_MASK);
+        outw(inw(io + GPIO_DIRECTION) & ~(GPI_VOL_DOWN|GPI_VOL_UP), io + GPIO_DIRECTION);
+        outw((GPI_VOL_DOWN|GPI_VOL_UP), io + GPIO_DATA);
+        outw(0xffff, io + GPIO_MASK);
+    }
+    pci_read_config_dword(pcidev, PCI_ALLEGRO_CONFIG, &n);
+    n &= ~(HV_CTRL_ENABLE | REDUCED_DEBOUNCE | HV_BUTTON_FROM_GD);
+    if (chip->hv_quirk)
+        n |= chip->hv_quirk->config;
+    /* For some reason we must always use reduced debounce. */
+    n |= REDUCED_DEBOUNCE;
+    n |= PM_CTRL_ENABLE | CLK_DIV_BY_49 | USE_PCI_TIMING;
+    pci_write_config_dword(pcidev, PCI_ALLEGRO_CONFIG, n);
+
+    outb(RESET_ASSP, chip->iobase + ASSP_CONTROL_B);
+    pci_read_config_dword(pcidev, PCI_ALLEGRO_CONFIG, &n);
+    n &= ~INT_CLK_SELECT;
+    if (!chip->allegro_flag) {
+        n &= ~INT_CLK_MULT_ENABLE;
+        n |= INT_CLK_SRC_NOT_PCI;
+    }
+    n &=  ~( CLK_MULT_MODE_SELECT | CLK_MULT_MODE_SELECT_2 );
+    pci_write_config_dword(pcidev, PCI_ALLEGRO_CONFIG, n);
+
+    if (chip->allegro_flag) {
+        pci_read_config_dword(pcidev, PCI_USER_CONFIG, &n);
+        n |= IN_CLK_12MHZ_SELECT;
+        pci_write_config_dword(pcidev, PCI_USER_CONFIG, n);
+    }
+
+    t = inb(chip->iobase + ASSP_CONTROL_A);
+    t &= ~( DSP_CLK_36MHZ_SELECT  | ASSP_CLK_49MHZ_SELECT);
+    t |= ASSP_CLK_49MHZ_SELECT;
+    t |= ASSP_0_WS_ENABLE;
+    outb(t, chip->iobase + ASSP_CONTROL_A);
+
+    snd_m3_assp_init(chip); /* download DSP code before starting ASSP below */
+    outb(RUN_ASSP, chip->iobase + ASSP_CONTROL_B);
+    outb(0x00, io + HARDWARE_VOL_CTRL);
+    outb(0x88, io + SHADOW_MIX_REG_VOICE);
+    outb(0x88, io + HW_VOL_COUNTER_VOICE);
+    outb(0x88, io + SHADOW_MIX_REG_MASTER);
+    outb(0x88, io + HW_VOL_COUNTER_MASTER);
+    return 0;
+}
+
+static void
+snd_m3_enable_ints(m3_t *chip)
+{
+    unsigned long io = chip->iobase;
+    unsigned short val;
+
+    /* TODO: MPU401 not supported yet */
+    val = ASSP_INT_ENABLE /*| MPU401_INT_ENABLE*/;
+    if (chip->hv_quirk && (chip->hv_quirk->config & HV_CTRL_ENABLE))
+        val |= HV_INT_ENABLE;
+    outw(val, io + HOST_INT_CTRL);
+    outb(inb(io + ASSP_CONTROL_C) | ASSP_HOST_INT_ENABLE,
+         io + ASSP_CONTROL_C);
+}
+
+
+/*
+ */
+
+static int snd_m3_free(m3_t *chip)
+{
+    m3_dma_t *s;
+    int i;
+
+    if (chip->substreams) {
+        spin_lock_irq(&chip->reg_lock);
+        for (i = 0; i < chip->num_substreams; i++) {
+            s = &chip->substreams[i];
+            /* check surviving pcms; this should not happen though.. */
+            if (s->substream && s->running)
+                snd_m3_pcm_stop(chip, s, s->substream);
+        }
+        spin_unlock_irq(&chip->reg_lock);
+        kfree(chip->substreams);
+    }
+    if (chip->iobase) {
+        outw(0, chip->iobase + HOST_INT_CTRL); /* disable ints */
+    }
+
+#ifdef CONFIG_PM
+    if (chip->suspend_mem)
+        vfree(chip->suspend_mem);
+#endif
+
+    if (chip->irq >= 0) {
+        synchronize_irq(chip->irq);
+        free_irq(chip->irq, (void *)chip);
+    }
+    if (chip->iobase)
+        pci_release_regions(chip->pci);
+    kfree(chip);
+    return 0;
+}
+
+
+/*
+ * APM support
+ */
+#ifdef CONFIG_PM
+
+static int m3_suspend(snd_card_t *card, unsigned int state)
+{
+    m3_t *chip = card->pm_private_data;
+    int i, index;
+
+    if (chip->suspend_mem == NULL)
+        return 0;
+
+    snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+    snd_pcm_suspend_all(chip->pcm);
+    snd_ac97_suspend(chip->ac97);
+
+    msleep(10); /* give the assp a chance to idle.. */
+
+    snd_m3_assp_halt(chip);
+
+    /* save dsp image */
+    index = 0;
+    for (i = REV_B_CODE_MEMORY_BEGIN; i <= REV_B_CODE_MEMORY_END; i++)
+        chip->suspend_mem[index++] =
+            snd_m3_assp_read(chip, MEMTYPE_INTERNAL_CODE, i);
+    for (i = REV_B_DATA_MEMORY_BEGIN ; i <= REV_B_DATA_MEMORY_END; i++)
+        chip->suspend_mem[index++] =
+            snd_m3_assp_read(chip, MEMTYPE_INTERNAL_DATA, i);
+
+    /* power down apci registers */
+    snd_m3_outw(chip, 0xffff, 0x54);
+    snd_m3_outw(chip, 0xffff, 0x56);
+    pci_disable_device(chip->pci);
+    pci_save_state(chip->pci);
+    return 0;
+}
+
+static int m3_resume(snd_card_t *card, unsigned int state)
+{
+    m3_t *chip = card->pm_private_data;
+    int i, index;
+
+    if (chip->suspend_mem == NULL)
+        return 0;
+
+    pci_restore_state(chip->pci);
+    pci_enable_device(chip->pci);
+    pci_set_master(chip->pci);
+
+    /* first lets just bring everything back. .*/
+    snd_m3_outw(chip, 0, 0x54);
+    snd_m3_outw(chip, 0, 0x56);
+
+    snd_m3_chip_init(chip);
+    snd_m3_assp_halt(chip);
+    snd_m3_ac97_reset(chip);
+
+    /* restore dsp image */
+    index = 0;
+    for (i = REV_B_CODE_MEMORY_BEGIN; i <= REV_B_CODE_MEMORY_END; i++)
+        snd_m3_assp_write(chip, MEMTYPE_INTERNAL_CODE, i,
+                          chip->suspend_mem[index++]);
+    for (i = REV_B_DATA_MEMORY_BEGIN ; i <= REV_B_DATA_MEMORY_END; i++)
+        snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, i,
+                          chip->suspend_mem[index++]);
+
+    /* tell the dma engine to restart itself */
+    snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA,
+                      KDATA_DMA_ACTIVE, 0);
+
+    /* restore ac97 registers */
+    snd_ac97_resume(chip->ac97);
+
+    snd_m3_assp_continue(chip);
+    snd_m3_enable_ints(chip);
+    snd_m3_amp_enable(chip, 1);
+
+    snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+    return 0;
+}
+#endif /* CONFIG_PM */
+
+
+/*
+ */
+
+static int snd_m3_dev_free(snd_device_t *device)
+{
+    m3_t *chip = device->device_data;
+    return snd_m3_free(chip);
+}
+
+static int __devinit
+snd_m3_create(snd_card_t *card, struct pci_dev *pci,
+              int enable_amp,
+              int amp_gpio,
+              m3_t **chip_ret)
+{
+    m3_t *chip;
+    int i, err;
+    const struct m3_quirk *quirk;
+    const struct m3_hv_quirk *hv_quirk;
+    static struct snd_device_ops ops = {
+        .dev_free =	snd_m3_dev_free,
+    };
+
+    *chip_ret = NULL;
+
+    if (pci_enable_device(pci))
+        return -EIO;
+
+    /* check, if we can restrict PCI DMA transfers to 28 bits */
+    if (!pci_dma_supported(pci, 0x0fffffff)) {
+        snd_printk("architecture does not support 28bit PCI busmaster DMA\n");
+        return -ENXIO;
+    }
+    pci_set_consistent_dma_mask(pci, 0x0fffffff);
+
+    chip = kcalloc(1, sizeof(*chip), GFP_KERNEL);
+    if (chip == NULL)
+        return -ENOMEM;
+
+    spin_lock_init(&chip->reg_lock);
+    spin_lock_init(&chip->ac97_lock);
+
+    switch (pci->device) {
+    case PCI_DEVICE_ID_ESS_ALLEGRO:
+    case PCI_DEVICE_ID_ESS_ALLEGRO_1:
+    case PCI_DEVICE_ID_ESS_CANYON3D_2LE:
+    case PCI_DEVICE_ID_ESS_CANYON3D_2:
+        chip->allegro_flag = 1;
+        break;
+    }
+
+    chip->card = card;
+    chip->pci = pci;
+    chip->irq = -1;
+
+    for (quirk = m3_quirk_list; quirk->vendor; quirk++) {
+        if (pci->subsystem_vendor == quirk->vendor &&
+            pci->subsystem_device == quirk->device) {
+            snd_printk("maestro3: enabled hack for '%s'\n", quirk->name);
+            chip->quirk = quirk;
+            break;
+        }
+    }
+
+    for (hv_quirk = m3_hv_quirk_list; hv_quirk->vendor; hv_quirk++) {
+        if (pci->vendor == hv_quirk->vendor &&
+            pci->device == hv_quirk->device &&
+            pci->subsystem_vendor == hv_quirk->subsystem_vendor &&
+            pci->subsystem_device == hv_quirk->subsystem_device) {
+            chip->hv_quirk = hv_quirk;
+            break;
+        }
+    }
+
+    chip->external_amp = enable_amp;
+    if (amp_gpio >= 0 && amp_gpio <= 0x0f)
+        chip->amp_gpio = amp_gpio;
+    else if (chip->quirk && chip->quirk->amp_gpio >= 0)
+        chip->amp_gpio = chip->quirk->amp_gpio;
+    else if (chip->allegro_flag)
+        chip->amp_gpio = GPO_EXT_AMP_ALLEGRO;
+    else /* presumably this is for all 'maestro3's.. */
+        chip->amp_gpio = GPO_EXT_AMP_M3;
+
+    chip->num_substreams = NR_DSPS;
+    chip->substreams = kmalloc(sizeof(m3_dma_t) * chip->num_substreams, GFP_KERNEL);
+    if (chip->substreams == NULL) {
+        kfree(chip);
+        return -ENOMEM;
+    }
+    memset(chip->substreams, 0, sizeof(m3_dma_t) * chip->num_substreams);
+
+    if ((err = pci_request_regions(pci, card->driver)) < 0) {
+        snd_m3_free(chip);
+        return err;
+    }
+    chip->iobase = pci_resource_start(pci, 0);
+
+    /* just to be sure */
+    pci_set_master(pci);
+
+    snd_m3_chip_init(chip);
+    snd_m3_assp_halt(chip);
+
+    snd_m3_ac97_reset(chip);
+
+    snd_m3_amp_enable(chip, 1);
+
+    if ((err = snd_m3_mixer(chip)) < 0) {
+        snd_m3_free(chip);
+        return err;
+    }
+
+    for (i = 0; i < chip->num_substreams; i++) {
+        m3_dma_t *s = &chip->substreams[i];
+        s->chip = chip;
+        if ((err = snd_m3_assp_client_init(chip, s, i)) < 0) {
+            snd_m3_free(chip);
+            return err;
+        }
+    }
+
+    if ((err = snd_m3_pcm(chip, 0)) < 0) {
+        snd_m3_free(chip);
+        return err;
+    }
+
+    tasklet_init(&chip->hwvol_tq, snd_m3_update_hw_volume, (unsigned long)chip);
+
+    if (request_irq(pci->irq, snd_m3_interrupt, SA_INTERRUPT|SA_SHIRQ,
+                    card->driver, (void *)chip)) {
+        snd_printk("unable to grab IRQ %d\n", pci->irq);
+        snd_m3_free(chip);
+        return -ENOMEM;
+    }
+    chip->irq = pci->irq;
+
+#ifdef CONFIG_PM
+    chip->suspend_mem = vmalloc(sizeof(u16) * (REV_B_CODE_MEMORY_LENGTH + REV_B_DATA_MEMORY_LENGTH));
+    if (chip->suspend_mem == NULL)
+        snd_printk("can't allocate apm buffer\n");
+#endif
+
+    if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+        snd_m3_free(chip);
+        return err;
+    }
+
+    snd_m3_enable_ints(chip);
+    snd_m3_assp_continue(chip);
+
+    *chip_ret = chip;
+
+    return 0;
+}
+
+/*
+ */
+static int __devinit
+snd_m3_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
+{
+    static int dev;
+    snd_card_t *card;
+    m3_t *chip;
+    int err;
+
+    pci_read_config_dword(pci, PCI_CLASS_REVISION, &pci->_class);
+
+#if 0   // os/2 doesnt pickup classes
+    /* don't pick up modems */
+#ifdef TARGET_OS2
+    if (((pci->_class >> 8) & 0xffff) != PCI_CLASS_MULTIMEDIA_AUDIO)
+#else
+        if (((pci->class >> 8) & 0xffff) != PCI_CLASS_MULTIMEDIA_AUDIO)
+#endif
+            return -ENODEV;
+#endif // 0
+
+    if (dev >= SNDRV_CARDS)
+        return -ENODEV;
+    if (!enable[dev]) {
+        dev++;
+        return -ENOENT;
+    }
+    card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+    if (card == NULL)
+        return -ENOMEM;
+
+    switch (pci->device) {
+    case PCI_DEVICE_ID_ESS_ALLEGRO:
+    case PCI_DEVICE_ID_ESS_ALLEGRO_1:
+        strcpy(card->driver, "Allegro");
+        break;
+    case PCI_DEVICE_ID_ESS_CANYON3D_2LE:
+    case PCI_DEVICE_ID_ESS_CANYON3D_2:
+        strcpy(card->driver, "Canyon3D-2");
+        break;
+    default:
+        strcpy(card->driver, "Maestro3");
+        break;
+    }
+
+    if ((err = snd_m3_create(card, pci,
+                             external_amp[dev],
+                             amp_gpio[dev],
+                             &chip)) < 0) {
+        snd_card_free(card);
+        return err;
+    }
+#ifdef DEBUG
+    dprintf(("m3_probe: create passed"));
+#endif
+
+    sprintf(card->shortname, "ESS %s PCI", card->driver);
+    sprintf(card->longname, "%s at 0x%lx, irq %d",
+            card->shortname, chip->iobase, chip->irq);
+
+    if ((err = snd_card_register(card)) < 0) {
+        snd_card_free(card);
+        return err;
+    }
+
+#if 0 /* TODO: not supported yet */
+    /* TODO enable MIDI IRQ and I/O */
+    err = snd_mpu401_uart_new(chip->card, 0, MPU401_HW_MPU401,
+                              chip->iobase + MPU401_DATA_PORT,
+                              MPU401_INFO_INTEGRATED,
+                              chip->irq, 0, &chip->rmidi);
+    if (err < 0)
+        printk(KERN_WARNING "maestro3: no MIDI support.\n");
+#endif
+
+    pci_set_drvdata(pci, card);
+    dev++;
+    return 0;
+}
+
+static void __devexit snd_m3_remove(struct pci_dev *pci)
+{
+    snd_card_free(pci_get_drvdata(pci));
+    pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver driver = {
+	.name = "Maestro3",
+	.id_table = snd_m3_ids,
+	.probe = snd_m3_probe,
+	.remove = __devexit_p(snd_m3_remove),
+#ifdef CONFIG_PM
+	.suspend = m3_suspend,
+	.resume = m3_resume,
+#endif
+};
+
+static int __init alsa_card_m3_init(void)
+{
+    int err;
+
+    if ((err = pci_module_init(&driver)) < 0) {
+#ifdef MODULE
+        //		snd_printk("Maestro3/Allegro soundcard not found or device busy\n");
+#endif
+        return err;
+    }
+    return 0;
+}
+
+static void __exit alsa_card_m3_exit(void)
+{
+    pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_m3_init)
+module_exit(alsa_card_m3_exit)
+
+#ifndef MODULE
+
+/* format is: snd-maestro3=enable,index,id,external_amp,amp_gpio */
+
+static int __init alsa_card_maestro3_setup(char *str)
+{
+    static unsigned __initdata nr_dev = 0;
+
+    if (nr_dev >= SNDRV_CARDS)
+        return 0;
+    (void)(get_option(&str,&enable[nr_dev]) == 2 &&
+           get_option(&str,&index[nr_dev]) == 2 &&
+           get_id(&str,&id[nr_dev]) == 2 &&
+           get_option(&str,&external_amp[nr_dev]) == 2 &&
+           get_option(&str,&amp_gpio[nr_dev]) == 2);
+    nr_dev++;
+    return 1;
+}
+
+__setup("snd-maestro3=", alsa_card_maestro3_setup);
+
+#endif /* ifndef MODULE */
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/makefile.os2
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/makefile.os2	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/makefile.os2	(revision 91)
@@ -0,0 +1,177 @@
+CINCLUDES = ..\..\include;..\include;$(%WATCOM)\H
+AINCLUDES = $(DDK_BASE)\H
+
+CDEFINES = -D__KERNEL__ -DMODULE -dTARGET_OS2 -dALSA_BUILD -D__ISAPNP__ -DCONFIG_ISAPNP -D__i386_
+
+32BIT=1
+!include ..\..\..\makefile.inc
+
+NMAKEOPTS=-f makefile.os2 $(LOGO)
+
+!if "$(DEBUG)" == "1"
+NMAKEOPTS += DEBUG=1
+!endif
+
+!if "$(KEE)" == "0"
+NMAKEOPTS += KEE=0
+KEE=0
+!else
+NMAKEOPTS += KEE=1
+KEE=1
+!endif
+
+!if "$(DEBUG)" == "1"
+!Message BUILDING DEBUG VERSION
+!else
+!Message BUILDING RELEASE VERSION
+!endif
+
+#===================================================================
+#
+#   List of source files
+#
+#===================================================================
+FILE1    = cmipci.obj ens1370.obj ens1371.obj cs4281.obj  es1938.obj
+FILE2    = es1968.obj intel8x0.obj maestro3.obj via82xx.obj als4000.obj
+FILE3    = fm801.obj atiixp.obj bt87x.obj
+#ice1712.obj fm801.obj  rme96.obj sonicvibes.obj 
+FILE4    = 
+FILE5    = 
+FILE6    = 
+FILE7    = 
+FILE8    = 
+FILELAST =
+FILES    = $(FILE0) $(FILE1) $(FILE2) $(FILE3) $(FILE4) $(FILE5) $(FILE6) $(FILE7) $(FILE8) $(FILE9) $(FILE10) $(FILE11) $(FILE12)
+
+TARGET   = pci
+
+LNKFILE  = $(OBJDIR)\$(TARGET).lnk
+
+
+#===================================================================
+#
+#   Specific dependencies
+#
+#===================================================================
+all: $(OBJDIR)\$(TARGET).lib subdirs
+
+
+$(LNKFILE): makefile.os2
+    @%create $(LNKFILE)
+    @for %f in ($(FILES)) do @%append $(LNKFILE) -+$(OBJDIR)\%f
+
+$(OBJDIR)\$(TARGET).lib: $(LNKFILE) $(FILES)
+    $(LIB) $(OBJDIR)\$(TARGET).lib @$(LNKFILE)
+    copy $(OBJDIR)\$(TARGET).lib $(ALSA_LIB)
+
+
+subdirs: .SYMBOLIC
+    @echo BUILDING hda
+    @cd hda
+    @wmake $(NMAKEOPTS)
+    @cd ..
+    @echo BUILDING ac97
+    @cd ac97
+    @wmake $(NMAKEOPTS)
+    @cd ..
+    @echo BUILDING ali5451
+    @cd ali5451
+    @wmake $(NMAKEOPTS)
+    @cd ..
+    @echo BUILDING cs46xx
+    @cd cs46xx
+    @wmake $(NMAKEOPTS)
+    @cd ..
+    @echo BUILDING emu10k1
+    @cd emu10k1
+    @wmake $(NMAKEOPTS)
+    @cd ..
+    @echo BUILDING ca0106
+    @cd ca0106
+    @wmake $(NMAKEOPTS)
+    @cd ..
+##    @echo BUILDING korg1212
+##    @cd korg1212
+##    @wmake $(NMAKEOPTS)
+##    @cd ..
+    @echo BUILDING nm256
+    @cd nm256
+    @wmake $(NMAKEOPTS)
+    @cd ..
+#    @echo BUILDING rme9652
+#    @cd rme9652
+#    @wmake $(NMAKEOPTS)
+#    @cd ..
+    @echo BUILDING trident
+    @cd trident
+    @wmake $(NMAKEOPTS)
+    @cd ..
+    @echo BUILDING ymfpci
+    @cd ymfpci
+    @wmake $(NMAKEOPTS)
+    @cd ..
+#    @echo BUILDING vortex
+#    @cd au88x0
+#    @wmake $(NMAKEOPTS)
+#    @cd ..
+
+
+##    @echo BUILDING wavefront
+##    @cd wavefront
+##    @nmake $(NMAKEOPTS)
+##    @cd ..
+    
+
+clean: .SYMBOLIC
+    @echo CLEANING ac97
+    @cd ac97
+    @wmake $(NMAKEOPTS) clean
+    @cd ..
+    @echo CLEANING ali5451
+    @cd ali5451
+    @wmake $(NMAKEOPTS) clean
+    @cd ..
+    @echo CLEANING cs46xx
+    @cd cs46xx
+    @wmake $(NMAKEOPTS) clean
+    @cd ..
+    @echo CLEANING emu10k1
+    @cd emu10k1
+    @wmake $(NMAKEOPTS) clean
+    @cd ..
+    @echo CLEANING ca0106
+    @cd ca0106
+    @wmake $(NMAKEOPTS) clean
+    @cd ..
+    @echo CLEANING korg1212
+    @cd korg1212
+    @wmake $(NMAKEOPTS) clean
+    @cd ..
+    @echo CLEANING nm256
+    @cd nm256
+    @wmake $(NMAKEOPTS) clean
+    @cd ..
+    @echo CLEANING rme9652
+    @cd rme9652
+    @wmake $(NMAKEOPTS) clean
+    @cd ..
+    @echo CLEANING trident
+    @cd trident
+    @wmake $(NMAKEOPTS) clean
+    @cd ..
+    @echo CLEANING ymfpci
+    @cd ymfpci
+    @wmake $(NMAKEOPTS) clean
+    @cd ..
+    @echo CLEANING vortex
+    @cd au88x0
+    @wmake $(NMAKEOPTS) clean
+    @cd ..
+#    @echo CLEANING wavefront
+#    @cd wavefront
+#    @nmake $(NMAKEOPTS) clean
+#    @cd ..
+    -@rm  *.err 2>nul
+    @cd $(OBJDIR)
+    -rm *.obj *.lnk *.sys *.sym *.lst *.map *.wmap *.lib 2>nul
+    @cd ../..
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/nm256/diff.cmd
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/nm256/diff.cmd	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/nm256/diff.cmd	(revision 91)
@@ -0,0 +1,1 @@
+diff.exe -Nuwr e:\dev\uss\from_z\ua2\GPL\alsa-kernel\pci\nm256\%1 E:\DEV\alsa\alsa-kernel\pci\nm256\%1 >%1.diff
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/nm256/makefile.os2
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/nm256/makefile.os2	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/nm256/makefile.os2	(revision 91)
@@ -0,0 +1,51 @@
+CINCLUDES = ..\..\..\include;..\..\include;$(%WATCOM)\H
+AINCLUDES = $(DDK_BASE)\H
+
+CDEFINES = -D__KERNEL__ -DMODULE -dTARGET_OS2 -dALSA_BUILD -D__ISAPNP__ -DCONFIG_ISAPNP -D__i386_
+
+32BIT=1
+!include ..\..\..\..\makefile.inc
+
+
+#===================================================================
+#
+#   List of source files
+#
+#===================================================================
+FILE1    = nm256.obj
+FILE2    = 
+FILE3    = 
+FILE4    = 
+FILE5    = 
+FILE6    = 
+FILE7    = 
+FILE8    = 
+FILELAST =
+FILES    = $(FILE0) $(FILE1) $(FILE2) $(FILE3) $(FILE4) $(FILE5) $(FILE6) $(FILE7) $(FILE8) $(FILE9) $(FILE10) $(FILE11) $(FILE12)
+
+TARGET   = nm256
+
+LNKFILE  = $(OBJDIR)\$(TARGET).lnk
+
+
+#===================================================================
+#
+#   Specific dependencies
+#
+#===================================================================
+all: $(OBJDIR)\$(TARGET).lib 
+
+
+$(LNKFILE): makefile.os2
+    @%create $(LNKFILE)
+    @for %f in ($(FILES)) do @%append $(LNKFILE) -+$(OBJDIR)\%f
+
+$(OBJDIR)\$(TARGET).lib: $(LNKFILE) $(FILES)
+    $(LIB) $(OBJDIR)\$(TARGET).lib @$(LNKFILE)
+    copy $(OBJDIR)\$(TARGET).lib $(ALSA_LIB) 
+
+clean: .SYMBOLIC
+    -@rm  *.err 2>nul
+    @cd $(OBJDIR)
+    -rm *.obj *.lnk *.sys *.sym *.lst *.map *.wmap *.lib 2>nul
+    @cd ..\..
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/nm256/nm256.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/nm256/nm256.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/nm256/nm256.c	(revision 91)
@@ -0,0 +1,1781 @@
+/* 
+ * Driver for NeoMagic 256AV and 256ZX chipsets.
+ * Copyright (c) 2000 by Takashi Iwai <tiwai@suse.de>
+ *
+ * Based on nm256_audio.c OSS driver in linux kernel.
+ * The original author of OSS nm256 driver wishes to remain anonymous,
+ * so I just put my acknoledgment to him/her here.
+ * The original author's web page is found at
+ *	http://www.uglx.org/sony.html
+ *
+ *
+ *   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 <linux/moduleparam.h>
+#include <linux/mutex.h>
+
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include <sound/initval.h>
+
+#define CARD_NAME "NeoMagic 256AV/ZX"
+#define DRIVER_NAME "NM256"
+
+MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
+MODULE_DESCRIPTION("NeoMagic NM256AV/ZX");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{NeoMagic,NM256AV},"
+		"{NeoMagic,NM256ZX}}");
+
+/*
+ * some compile conditions.
+ */
+
+static int index = SNDRV_DEFAULT_IDX1;	/* Index */
+static char *id = SNDRV_DEFAULT_STR1;	/* ID for this card */
+static int playback_bufsize = 16;
+static int capture_bufsize = 16;
+static int force_ac97;			/* disabled as default */
+static int buffer_top;			/* not specified */
+static int use_cache;			/* disabled */
+static int vaio_hack;			/* disabled */
+static int reset_workaround;
+static int reset_workaround_2;
+
+//module_param(index, int, 0444);
+MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard.");
+//module_param(id, charp, 0444);
+MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard.");
+//module_param(playback_bufsize, int, 0444);
+MODULE_PARM_DESC(playback_bufsize, "DAC frame size in kB for " CARD_NAME " soundcard.");
+//module_param(capture_bufsize, int, 0444);
+MODULE_PARM_DESC(capture_bufsize, "ADC frame size in kB for " CARD_NAME " soundcard.");
+//module_param(force_ac97, bool, 0444);
+MODULE_PARM_DESC(force_ac97, "Force to use AC97 codec for " CARD_NAME " soundcard.");
+//module_param(buffer_top, int, 0444);
+MODULE_PARM_DESC(buffer_top, "Set the top address of audio buffer for " CARD_NAME " soundcard.");
+//module_param(use_cache, bool, 0444);
+MODULE_PARM_DESC(use_cache, "Enable the cache for coefficient table access.");
+//module_param(vaio_hack, bool, 0444);
+MODULE_PARM_DESC(vaio_hack, "Enable workaround for Sony VAIO notebooks.");
+//module_param(reset_workaround, bool, 0444);
+MODULE_PARM_DESC(reset_workaround, "Enable AC97 RESET workaround for some laptops.");
+//module_param(reset_workaround_2, bool, 0444);
+MODULE_PARM_DESC(reset_workaround_2, "Enable extended AC97 RESET workaround for some other laptops.");
+
+/* just for backward compatibility */
+static int enable;
+//module_param(enable, bool, 0444);
+
+
+/* nm256 */
+#ifndef PCI_VENDOR_ID_NEOMAGIC
+#define PCI_VENDOR_ID_NEOMEGIC          0x10c8
+#endif
+#ifndef PCI_DEVICE_ID_NEOMAGIC_NM256AV_AUDIO
+#define PCI_DEVICE_ID_NEOMAGIC_NM256AV_AUDIO 0x8005
+#endif
+#ifndef PCI_DEVICE_ID_NEOMAGIC_NM256ZX_AUDIO
+#define PCI_DEVICE_ID_NEOMAGIC_NM256ZX_AUDIO 0x8006
+#endif
+#ifndef PCI_DEVICE_ID_NEOMAGIC_NM256XL_PLUS_AUDIO
+#define PCI_DEVICE_ID_NEOMAGIC_NM256XL_PLUS_AUDIO 0x8016
+#endif
+
+/*
+ * hw definitions
+ */
+
+/* The BIOS signature. */
+#define NM_SIGNATURE 0x4e4d0000
+/* Signature mask. */
+#define NM_SIG_MASK 0xffff0000
+
+/* Size of the second memory area. */
+#define NM_PORT2_SIZE 4096
+
+/* The base offset of the mixer in the second memory area. */
+#define NM_MIXER_OFFSET 0x600
+
+/* The maximum size of a coefficient entry. */
+#define NM_MAX_PLAYBACK_COEF_SIZE	0x5000
+#define NM_MAX_RECORD_COEF_SIZE		0x1260
+
+/* The interrupt register. */
+#define NM_INT_REG 0xa04
+/* And its bits. */
+#define NM_PLAYBACK_INT 0x40
+#define NM_RECORD_INT 0x100
+#define NM_MISC_INT_1 0x4000
+#define NM_MISC_INT_2 0x1
+#define NM_ACK_INT(chip, X) snd_nm256_writew(chip, NM_INT_REG, (X) << 1)
+
+/* The AV's "mixer ready" status bit and location. */
+#define NM_MIXER_STATUS_OFFSET 0xa04
+#define NM_MIXER_READY_MASK 0x0800
+#define NM_MIXER_PRESENCE 0xa06
+#define NM_PRESENCE_MASK 0x0050
+#define NM_PRESENCE_VALUE 0x0040
+
+/*
+ * For the ZX.  It uses the same interrupt register, but it holds 32
+ * bits instead of 16.
+ */
+#define NM2_PLAYBACK_INT 0x10000
+#define NM2_RECORD_INT 0x80000
+#define NM2_MISC_INT_1 0x8
+#define NM2_MISC_INT_2 0x2
+#define NM2_ACK_INT(chip, X) snd_nm256_writel(chip, NM_INT_REG, (X))
+
+/* The ZX's "mixer ready" status bit and location. */
+#define NM2_MIXER_STATUS_OFFSET 0xa06
+#define NM2_MIXER_READY_MASK 0x0800
+
+/* The playback registers start from here. */
+#define NM_PLAYBACK_REG_OFFSET 0x0
+/* The record registers start from here. */
+#define NM_RECORD_REG_OFFSET 0x200
+
+/* The rate register is located 2 bytes from the start of the register area. */
+#define NM_RATE_REG_OFFSET 2
+
+/* Mono/stereo flag, number of bits on playback, and rate mask. */
+#define NM_RATE_STEREO 1
+#define NM_RATE_BITS_16 2
+#define NM_RATE_MASK 0xf0
+
+/* Playback enable register. */
+#define NM_PLAYBACK_ENABLE_REG (NM_PLAYBACK_REG_OFFSET + 0x1)
+#define NM_PLAYBACK_ENABLE_FLAG 1
+#define NM_PLAYBACK_ONESHOT 2
+#define NM_PLAYBACK_FREERUN 4
+
+/* Mutes the audio output. */
+#define NM_AUDIO_MUTE_REG (NM_PLAYBACK_REG_OFFSET + 0x18)
+#define NM_AUDIO_MUTE_LEFT 0x8000
+#define NM_AUDIO_MUTE_RIGHT 0x0080
+
+/* Recording enable register. */
+#define NM_RECORD_ENABLE_REG (NM_RECORD_REG_OFFSET + 0)
+#define NM_RECORD_ENABLE_FLAG 1
+#define NM_RECORD_FREERUN 2
+
+/* coefficient buffer pointer */
+#define NM_COEFF_START_OFFSET	0x1c
+#define NM_COEFF_END_OFFSET	0x20
+
+/* DMA buffer offsets */
+#define NM_RBUFFER_START (NM_RECORD_REG_OFFSET + 0x4)
+#define NM_RBUFFER_END   (NM_RECORD_REG_OFFSET + 0x10)
+#define NM_RBUFFER_WMARK (NM_RECORD_REG_OFFSET + 0xc)
+#define NM_RBUFFER_CURRP (NM_RECORD_REG_OFFSET + 0x8)
+
+#define NM_PBUFFER_START (NM_PLAYBACK_REG_OFFSET + 0x4)
+#define NM_PBUFFER_END   (NM_PLAYBACK_REG_OFFSET + 0x14)
+#define NM_PBUFFER_WMARK (NM_PLAYBACK_REG_OFFSET + 0xc)
+#define NM_PBUFFER_CURRP (NM_PLAYBACK_REG_OFFSET + 0x8)
+
+struct nm256_stream {
+
+	struct nm256 *chip;
+	struct snd_pcm_substream *substream;
+	int running;
+	int suspended;
+	
+	u32 buf;	/* offset from chip->buffer */
+	int bufsize;	/* buffer size in bytes */
+	void __iomem *bufptr;		/* mapped pointer */
+	unsigned long bufptr_addr;	/* physical address of the mapped pointer */
+
+	int dma_size;		/* buffer size of the substream in bytes */
+	int period_size;	/* period size in bytes */
+	int periods;		/* # of periods */
+	int shift;		/* bit shifts */
+	int cur_period;		/* current period # */
+
+};
+
+struct nm256 {
+	
+	struct snd_card *card;
+
+	void __iomem *cport;		/* control port */
+	struct resource *res_cport;	/* its resource */
+	unsigned long cport_addr;	/* physical address */
+
+	void __iomem *buffer;		/* buffer */
+	struct resource *res_buffer;	/* its resource */
+	unsigned long buffer_addr;	/* buffer phyiscal address */
+
+	u32 buffer_start;		/* start offset from pci resource 0 */
+	u32 buffer_end;			/* end offset */
+	u32 buffer_size;		/* total buffer size */
+
+	u32 all_coeff_buf;		/* coefficient buffer */
+	u32 coeff_buf[2];		/* coefficient buffer for each stream */
+
+	unsigned int coeffs_current: 1;	/* coeff. table is loaded? */
+	unsigned int use_cache: 1;	/* use one big coef. table */
+	unsigned int reset_workaround: 1; /* Workaround for some laptops to avoid freeze */
+	unsigned int reset_workaround_2: 1; /* Extended workaround for some other laptops to avoid freeze */
+	unsigned int in_resume: 1;
+
+	int mixer_base;			/* register offset of ac97 mixer */
+	int mixer_status_offset;	/* offset of mixer status reg. */
+	int mixer_status_mask;		/* bit mask to test the mixer status */
+
+	int irq;
+	int irq_acks;
+	irqreturn_t (*interrupt)(int, void *, struct pt_regs *);
+	int badintrcount;		/* counter to check bogus interrupts */
+	struct mutex irq_mutex;
+
+	struct nm256_stream streams[2];
+
+	struct snd_ac97 *ac97;
+	unsigned short *ac97_regs; /* register caches, only for valid regs */
+
+	struct snd_pcm *pcm;
+
+	struct pci_dev *pci;
+
+	spinlock_t reg_lock;
+
+};
+
+
+/*
+ * include coefficient table
+ */
+#include "nm256_coef.c"
+
+
+/*
+ * PCI ids
+ */
+static struct pci_device_id snd_nm256_ids[] = {
+	{PCI_VENDOR_ID_NEOMAGIC, PCI_DEVICE_ID_NEOMAGIC_NM256AV_AUDIO, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{PCI_VENDOR_ID_NEOMAGIC, PCI_DEVICE_ID_NEOMAGIC_NM256ZX_AUDIO, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{PCI_VENDOR_ID_NEOMAGIC, PCI_DEVICE_ID_NEOMAGIC_NM256XL_PLUS_AUDIO, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{0,},
+};
+
+MODULE_DEVICE_TABLE(pci, snd_nm256_ids);
+
+
+/*
+ * lowlvel stuffs
+ */
+
+static inline u8
+snd_nm256_readb(struct nm256 *chip, int offset)
+{
+	return readb(chip->cport + offset);
+}
+
+static inline u16
+snd_nm256_readw(struct nm256 *chip, int offset)
+{
+	return readw(chip->cport + offset);
+}
+
+static inline u32
+snd_nm256_readl(struct nm256 *chip, int offset)
+{
+	return readl(chip->cport + offset);
+}
+
+static inline void
+snd_nm256_writeb(struct nm256 *chip, int offset, u8 val)
+{
+	writeb(val, chip->cport + offset);
+}
+
+static inline void
+snd_nm256_writew(struct nm256 *chip, int offset, u16 val)
+{
+	writew(val, chip->cport + offset);
+}
+
+static inline void
+snd_nm256_writel(struct nm256 *chip, int offset, u32 val)
+{
+	writel(val, chip->cport + offset);
+}
+
+static inline void
+snd_nm256_write_buffer(struct nm256 *chip, void *src, int offset, int size)
+{
+	offset -= chip->buffer_start;
+#ifdef CONFIG_SND_DEBUG
+	if (offset < 0 || offset >= chip->buffer_size) {
+		snd_printk(KERN_ERR "write_buffer invalid offset = %d size = %d\n",
+			   offset, size);
+		return;
+	}
+#endif
+	memcpy_toio(chip->buffer + offset, src, size);
+}
+
+/*
+ * coefficient handlers -- what a magic!
+ */
+
+static u16
+snd_nm256_get_start_offset(int which)
+{
+	u16 offset = 0;
+	while (which-- > 0)
+		offset += coefficient_sizes[which];
+	return offset;
+}
+
+static void
+snd_nm256_load_one_coefficient(struct nm256 *chip, int stream, u32 port, int which)
+{
+	u32 coeff_buf = chip->coeff_buf[stream];
+	u16 offset = snd_nm256_get_start_offset(which);
+	u16 size = coefficient_sizes[which];
+
+	snd_nm256_write_buffer(chip, coefficients + offset, coeff_buf, size);
+	snd_nm256_writel(chip, port, coeff_buf);
+	/* ???  Record seems to behave differently than playback.  */
+	if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+		size--;
+	snd_nm256_writel(chip, port + 4, coeff_buf + size);
+}
+
+static void
+snd_nm256_load_coefficient(struct nm256 *chip, int stream, int number)
+{
+	/* The enable register for the specified engine.  */
+	u32 poffset = (stream == SNDRV_PCM_STREAM_CAPTURE ?
+		       NM_RECORD_ENABLE_REG : NM_PLAYBACK_ENABLE_REG);
+	u32 addr = NM_COEFF_START_OFFSET;
+
+	addr += (stream == SNDRV_PCM_STREAM_CAPTURE ?
+		 NM_RECORD_REG_OFFSET : NM_PLAYBACK_REG_OFFSET);
+
+	if (snd_nm256_readb(chip, poffset) & 1) {
+		snd_printd("NM256: Engine was enabled while loading coefficients!\n");
+		return;
+	}
+
+	/* The recording engine uses coefficient values 8-15.  */
+	number &= 7;
+	if (stream == SNDRV_PCM_STREAM_CAPTURE)
+		number += 8;
+
+	if (! chip->use_cache) {
+		snd_nm256_load_one_coefficient(chip, stream, addr, number);
+		return;
+	}
+	if (! chip->coeffs_current) {
+		snd_nm256_write_buffer(chip, coefficients, chip->all_coeff_buf,
+				       NM_TOTAL_COEFF_COUNT * 4);
+		chip->coeffs_current = 1;
+	} else {
+		u32 base = chip->all_coeff_buf;
+		u32 offset = snd_nm256_get_start_offset(number);
+		u32 end_offset = offset + coefficient_sizes[number];
+		snd_nm256_writel(chip, addr, base + offset);
+		if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+			end_offset--;
+		snd_nm256_writel(chip, addr + 4, base + end_offset);
+	}
+}
+
+
+/* The actual rates supported by the card. */
+static unsigned int samplerates[8] = {
+	8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000,
+};
+static struct snd_pcm_hw_constraint_list constraints_rates = {
+	.count = ARRAY_SIZE(samplerates), 
+	.list = samplerates,
+	.mask = 0,
+};
+
+/*
+ * return the index of the target rate
+ */
+static int
+snd_nm256_fixed_rate(unsigned int rate)
+{
+	unsigned int i;
+	for (i = 0; i < ARRAY_SIZE(samplerates); i++) {
+		if (rate == samplerates[i])
+			return i;
+	}
+	snd_BUG();
+	return 0;
+}
+
+/*
+ * set sample rate and format
+ */
+static void
+snd_nm256_set_format(struct nm256 *chip, struct nm256_stream *s,
+		     struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	int rate_index = snd_nm256_fixed_rate(runtime->rate);
+	unsigned char ratebits = (rate_index << 4) & NM_RATE_MASK;
+
+	s->shift = 0;
+	if (snd_pcm_format_width(runtime->format) == 16) {
+		ratebits |= NM_RATE_BITS_16;
+		s->shift++;
+	}
+	if (runtime->channels > 1) {
+		ratebits |= NM_RATE_STEREO;
+		s->shift++;
+	}
+
+	runtime->rate = samplerates[rate_index];
+
+	switch (substream->stream) {
+	case SNDRV_PCM_STREAM_PLAYBACK:
+		snd_nm256_load_coefficient(chip, 0, rate_index); /* 0 = playback */
+		snd_nm256_writeb(chip,
+				 NM_PLAYBACK_REG_OFFSET + NM_RATE_REG_OFFSET,
+				 ratebits);
+		break;
+	case SNDRV_PCM_STREAM_CAPTURE:
+		snd_nm256_load_coefficient(chip, 1, rate_index); /* 1 = record */
+		snd_nm256_writeb(chip,
+				 NM_RECORD_REG_OFFSET + NM_RATE_REG_OFFSET,
+				 ratebits);
+		break;
+	}
+}
+
+/* acquire interrupt */
+static int snd_nm256_acquire_irq(struct nm256 *chip)
+{
+	mutex_lock(&chip->irq_mutex);
+	if (chip->irq < 0) {
+		if (request_irq(chip->pci->irq, chip->interrupt, SA_INTERRUPT|SA_SHIRQ,
+				chip->card->driver, chip)) {
+			snd_printk(KERN_ERR "unable to grab IRQ %d\n", chip->pci->irq);
+			mutex_unlock(&chip->irq_mutex);
+			return -EBUSY;
+		}
+		chip->irq = chip->pci->irq;
+	}
+	chip->irq_acks++;
+	mutex_unlock(&chip->irq_mutex);
+	return 0;
+}
+
+/* release interrupt */
+static void snd_nm256_release_irq(struct nm256 *chip)
+{
+	mutex_lock(&chip->irq_mutex);
+	if (chip->irq_acks > 0)
+		chip->irq_acks--;
+	if (chip->irq_acks == 0 && chip->irq >= 0) {
+		free_irq(chip->irq, chip);
+		chip->irq = -1;
+	}
+	mutex_unlock(&chip->irq_mutex);
+}
+
+/*
+ * start / stop
+ */
+
+/* update the watermark (current period) */
+static void snd_nm256_pcm_mark(struct nm256 *chip, struct nm256_stream *s, int reg)
+{
+	s->cur_period++;
+	s->cur_period %= s->periods;
+	snd_nm256_writel(chip, reg, s->buf + s->cur_period * s->period_size);
+}
+
+#define snd_nm256_playback_mark(chip, s) snd_nm256_pcm_mark(chip, s, NM_PBUFFER_WMARK)
+#define snd_nm256_capture_mark(chip, s)  snd_nm256_pcm_mark(chip, s, NM_RBUFFER_WMARK)
+
+static void
+snd_nm256_playback_start(struct nm256 *chip, struct nm256_stream *s,
+			 struct snd_pcm_substream *substream)
+{
+	/* program buffer pointers */
+	snd_nm256_writel(chip, NM_PBUFFER_START, s->buf);
+	snd_nm256_writel(chip, NM_PBUFFER_END, s->buf + s->dma_size - (1 << s->shift));
+	snd_nm256_writel(chip, NM_PBUFFER_CURRP, s->buf);
+	snd_nm256_playback_mark(chip, s);
+
+	/* Enable playback engine and interrupts. */
+	snd_nm256_writeb(chip, NM_PLAYBACK_ENABLE_REG,
+			 NM_PLAYBACK_ENABLE_FLAG | NM_PLAYBACK_FREERUN);
+	/* Enable both channels. */
+	snd_nm256_writew(chip, NM_AUDIO_MUTE_REG, 0x0);
+}
+
+static void
+snd_nm256_capture_start(struct nm256 *chip, struct nm256_stream *s,
+			struct snd_pcm_substream *substream)
+{
+	/* program buffer pointers */
+	snd_nm256_writel(chip, NM_RBUFFER_START, s->buf);
+	snd_nm256_writel(chip, NM_RBUFFER_END, s->buf + s->dma_size);
+	snd_nm256_writel(chip, NM_RBUFFER_CURRP, s->buf);
+	snd_nm256_capture_mark(chip, s);
+
+	/* Enable playback engine and interrupts. */
+	snd_nm256_writeb(chip, NM_RECORD_ENABLE_REG,
+			 NM_RECORD_ENABLE_FLAG | NM_RECORD_FREERUN);
+}
+
+/* Stop the play engine. */
+static void
+snd_nm256_playback_stop(struct nm256 *chip)
+{
+	/* Shut off sound from both channels. */
+	snd_nm256_writew(chip, NM_AUDIO_MUTE_REG,
+			 NM_AUDIO_MUTE_LEFT | NM_AUDIO_MUTE_RIGHT);
+	/* Disable play engine. */
+	snd_nm256_writeb(chip, NM_PLAYBACK_ENABLE_REG, 0);
+}
+
+static void
+snd_nm256_capture_stop(struct nm256 *chip)
+{
+	/* Disable recording engine. */
+	snd_nm256_writeb(chip, NM_RECORD_ENABLE_REG, 0);
+}
+
+static int
+snd_nm256_playback_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct nm256 *chip = snd_pcm_substream_chip(substream);
+	struct nm256_stream *s = substream->runtime->private_data;
+	int err = 0;
+
+	snd_assert(s != NULL, return -ENXIO);
+
+	spin_lock(&chip->reg_lock);
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_RESUME:
+		s->suspended = 0;
+		/* fallthru */
+	case SNDRV_PCM_TRIGGER_START:
+		if (! s->running) {
+			snd_nm256_playback_start(chip, s, substream);
+			s->running = 1;
+		}
+		break;
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		s->suspended = 1;
+		/* fallthru */
+	case SNDRV_PCM_TRIGGER_STOP:
+		if (s->running) {
+			snd_nm256_playback_stop(chip);
+			s->running = 0;
+		}
+		break;
+	default:
+		err = -EINVAL;
+		break;
+	}
+	spin_unlock(&chip->reg_lock);
+	return err;
+}
+
+static int
+snd_nm256_capture_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct nm256 *chip = snd_pcm_substream_chip(substream);
+	struct nm256_stream *s = substream->runtime->private_data;
+	int err = 0;
+
+	snd_assert(s != NULL, return -ENXIO);
+
+	spin_lock(&chip->reg_lock);
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		if (! s->running) {
+			snd_nm256_capture_start(chip, s, substream);
+			s->running = 1;
+		}
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		if (s->running) {
+			snd_nm256_capture_stop(chip);
+			s->running = 0;
+		}
+		break;
+	default:
+		err = -EINVAL;
+		break;
+	}
+	spin_unlock(&chip->reg_lock);
+	return err;
+}
+
+
+/*
+ * prepare playback/capture channel
+ */
+static int snd_nm256_pcm_prepare(struct snd_pcm_substream *substream)
+{
+	struct nm256 *chip = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct nm256_stream *s = runtime->private_data;
+
+	snd_assert(s, return -ENXIO);
+	s->dma_size = frames_to_bytes(runtime, substream->runtime->buffer_size);
+	s->period_size = frames_to_bytes(runtime, substream->runtime->period_size);
+	s->periods = substream->runtime->periods;
+	s->cur_period = 0;
+
+	spin_lock_irq(&chip->reg_lock);
+	s->running = 0;
+	snd_nm256_set_format(chip, s, substream);
+	spin_unlock_irq(&chip->reg_lock);
+
+	return 0;
+}
+
+
+/*
+ * get the current pointer
+ */
+static snd_pcm_uframes_t
+snd_nm256_playback_pointer(struct snd_pcm_substream *substream)
+{
+	struct nm256 *chip = snd_pcm_substream_chip(substream);
+	struct nm256_stream *s = substream->runtime->private_data;
+	unsigned long curp;
+
+	snd_assert(s, return 0);
+	curp = snd_nm256_readl(chip, NM_PBUFFER_CURRP) - (unsigned long)s->buf;
+	curp %= s->dma_size;
+	return bytes_to_frames(substream->runtime, curp);
+}
+
+static snd_pcm_uframes_t
+snd_nm256_capture_pointer(struct snd_pcm_substream *substream)
+{
+	struct nm256 *chip = snd_pcm_substream_chip(substream);
+	struct nm256_stream *s = substream->runtime->private_data;
+	unsigned long curp;
+
+	snd_assert(s != NULL, return 0);
+	curp = snd_nm256_readl(chip, NM_RBUFFER_CURRP) - (unsigned long)s->buf;
+	curp %= s->dma_size;	
+	return bytes_to_frames(substream->runtime, curp);
+}
+
+/* Remapped I/O space can be accessible as pointer on i386 */
+/* This might be changed in the future */
+#ifndef __i386__
+/*
+ * silence / copy for playback
+ */
+static int
+snd_nm256_playback_silence(struct snd_pcm_substream *substream,
+			   int channel, /* not used (interleaved data) */
+			   snd_pcm_uframes_t pos,
+			   snd_pcm_uframes_t count)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct nm256_stream *s = runtime->private_data;
+	count = frames_to_bytes(runtime, count);
+	pos = frames_to_bytes(runtime, pos);
+	memset_io(s->bufptr + pos, 0, count);
+	return 0;
+}
+
+static int
+snd_nm256_playback_copy(struct snd_pcm_substream *substream,
+			int channel, /* not used (interleaved data) */
+			snd_pcm_uframes_t pos,
+			void __user *src,
+			snd_pcm_uframes_t count)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct nm256_stream *s = runtime->private_data;
+	count = frames_to_bytes(runtime, count);
+	pos = frames_to_bytes(runtime, pos);
+	if (copy_from_user_toio(s->bufptr + pos, src, count))
+		return -EFAULT;
+	return 0;
+}
+
+/*
+ * copy to user
+ */
+static int
+snd_nm256_capture_copy(struct snd_pcm_substream *substream,
+		       int channel, /* not used (interleaved data) */
+		       snd_pcm_uframes_t pos,
+		       void __user *dst,
+		       snd_pcm_uframes_t count)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct nm256_stream *s = runtime->private_data;
+	count = frames_to_bytes(runtime, count);
+	pos = frames_to_bytes(runtime, pos);
+	if (copy_to_user_fromio(dst, s->bufptr + pos, count))
+		return -EFAULT;
+	return 0;
+}
+
+#endif /* !__i386__ */
+
+
+/*
+ * update playback/capture watermarks
+ */
+
+/* spinlock held! */
+static void
+snd_nm256_playback_update(struct nm256 *chip)
+{
+	struct nm256_stream *s;
+
+	s = &chip->streams[SNDRV_PCM_STREAM_PLAYBACK];
+	if (s->running && s->substream) {
+		spin_unlock(&chip->reg_lock);
+		snd_pcm_period_elapsed(s->substream);
+		spin_lock(&chip->reg_lock);
+		snd_nm256_playback_mark(chip, s);
+	}
+}
+
+/* spinlock held! */
+static void
+snd_nm256_capture_update(struct nm256 *chip)
+{
+	struct nm256_stream *s;
+
+	s = &chip->streams[SNDRV_PCM_STREAM_CAPTURE];
+	if (s->running && s->substream) {
+		spin_unlock(&chip->reg_lock);
+		snd_pcm_period_elapsed(s->substream);
+		spin_lock(&chip->reg_lock);
+		snd_nm256_capture_mark(chip, s);
+	}
+}
+
+/*
+ * hardware info
+ */
+static struct snd_pcm_hardware snd_nm256_playback =
+{
+	.info =			SNDRV_PCM_INFO_MMAP_IOMEM |SNDRV_PCM_INFO_MMAP_VALID |
+				SNDRV_PCM_INFO_INTERLEAVED |
+				/*SNDRV_PCM_INFO_PAUSE |*/
+				SNDRV_PCM_INFO_RESUME,
+	.formats =		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+	.rates =		SNDRV_PCM_RATE_KNOT/*24k*/ | SNDRV_PCM_RATE_8000_48000,
+	.rate_min =		8000,
+	.rate_max =		48000,
+	.channels_min =		1,
+	.channels_max =		2,
+	.periods_min =		2,
+	.periods_max =		1024,
+	.buffer_bytes_max =	128 * 1024,
+	.period_bytes_min =	256,
+	.period_bytes_max =	128 * 1024,
+};
+
+static struct snd_pcm_hardware snd_nm256_capture =
+{
+	.info =			SNDRV_PCM_INFO_MMAP_IOMEM | SNDRV_PCM_INFO_MMAP_VALID |
+				SNDRV_PCM_INFO_INTERLEAVED |
+				/*SNDRV_PCM_INFO_PAUSE |*/
+				SNDRV_PCM_INFO_RESUME,
+	.formats =		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+	.rates =		SNDRV_PCM_RATE_KNOT/*24k*/ | SNDRV_PCM_RATE_8000_48000,
+	.rate_min =		8000,
+	.rate_max =		48000,
+	.channels_min =		1,
+	.channels_max =		2,
+	.periods_min =		2,
+	.periods_max =		1024,
+	.buffer_bytes_max =	128 * 1024,
+	.period_bytes_min =	256,
+	.period_bytes_max =	128 * 1024,
+};
+
+
+/* set dma transfer size */
+static int snd_nm256_pcm_hw_params(struct snd_pcm_substream *substream,
+				   struct snd_pcm_hw_params *hw_params)
+{
+	/* area and addr are already set and unchanged */
+	substream->runtime->dma_bytes = params_buffer_bytes(hw_params);
+	return 0;
+}
+
+/*
+ * open
+ */
+static void snd_nm256_setup_stream(struct nm256 *chip, struct nm256_stream *s,
+				   struct snd_pcm_substream *substream,
+				   struct snd_pcm_hardware *hw_ptr)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+
+	s->running = 0;
+	runtime->hw = *hw_ptr;
+	runtime->hw.buffer_bytes_max = s->bufsize;
+	runtime->hw.period_bytes_max = s->bufsize / 2;
+	runtime->dma_area = (void __force *) s->bufptr;
+	runtime->dma_addr = s->bufptr_addr;
+	runtime->dma_bytes = s->bufsize;
+	runtime->private_data = s;
+	s->substream = substream;
+
+	snd_pcm_set_sync(substream);
+	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+				   &constraints_rates);
+}
+
+static int
+snd_nm256_playback_open(struct snd_pcm_substream *substream)
+{
+	struct nm256 *chip = snd_pcm_substream_chip(substream);
+
+	if (snd_nm256_acquire_irq(chip) < 0)
+		return -EBUSY;
+	snd_nm256_setup_stream(chip, &chip->streams[SNDRV_PCM_STREAM_PLAYBACK],
+			       substream, &snd_nm256_playback);
+	return 0;
+}
+
+static int
+snd_nm256_capture_open(struct snd_pcm_substream *substream)
+{
+	struct nm256 *chip = snd_pcm_substream_chip(substream);
+
+	if (snd_nm256_acquire_irq(chip) < 0)
+		return -EBUSY;
+	snd_nm256_setup_stream(chip, &chip->streams[SNDRV_PCM_STREAM_CAPTURE],
+			       substream, &snd_nm256_capture);
+	return 0;
+}
+
+/*
+ * close - we don't have to do special..
+ */
+static int
+snd_nm256_playback_close(struct snd_pcm_substream *substream)
+{
+	struct nm256 *chip = snd_pcm_substream_chip(substream);
+
+	snd_nm256_release_irq(chip);
+	return 0;
+}
+
+
+static int
+snd_nm256_capture_close(struct snd_pcm_substream *substream)
+{
+	struct nm256 *chip = snd_pcm_substream_chip(substream);
+
+	snd_nm256_release_irq(chip);
+	return 0;
+}
+
+/*
+ * create a pcm instance
+ */
+static struct snd_pcm_ops snd_nm256_playback_ops = {
+	.open =		snd_nm256_playback_open,
+	.close =	snd_nm256_playback_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_nm256_pcm_hw_params,
+	.prepare =	snd_nm256_pcm_prepare,
+	.trigger =	snd_nm256_playback_trigger,
+	.pointer =	snd_nm256_playback_pointer,
+#ifndef __i386__
+	.copy =		snd_nm256_playback_copy,
+	.silence =	snd_nm256_playback_silence,
+#endif
+	.mmap =		snd_pcm_lib_mmap_iomem,
+};
+
+static struct snd_pcm_ops snd_nm256_capture_ops = {
+	.open =		snd_nm256_capture_open,
+	.close =	snd_nm256_capture_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_nm256_pcm_hw_params,
+	.prepare =	snd_nm256_pcm_prepare,
+	.trigger =	snd_nm256_capture_trigger,
+	.pointer =	snd_nm256_capture_pointer,
+#ifndef __i386__
+	.copy =		snd_nm256_capture_copy,
+#endif
+	.mmap =		snd_pcm_lib_mmap_iomem,
+};
+
+static int __devinit
+snd_nm256_pcm(struct nm256 *chip, int device)
+{
+	struct snd_pcm *pcm;
+	int i, err;
+
+	for (i = 0; i < 2; i++) {
+		struct nm256_stream *s = &chip->streams[i];
+		s->bufptr = chip->buffer + (s->buf - chip->buffer_start);
+		s->bufptr_addr = chip->buffer_addr + (s->buf - chip->buffer_start);
+	}
+
+	err = snd_pcm_new(chip->card, chip->card->driver, device,
+			  1, 1, &pcm);
+	if (err < 0)
+		return err;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_nm256_playback_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_nm256_capture_ops);
+
+	pcm->private_data = chip;
+	pcm->info_flags = 0;
+	chip->pcm = pcm;
+
+	return 0;
+}
+
+
+/* 
+ * Initialize the hardware. 
+ */
+static void
+snd_nm256_init_chip(struct nm256 *chip)
+{
+	/* Reset everything. */
+	snd_nm256_writeb(chip, 0x0, 0x11);
+	snd_nm256_writew(chip, 0x214, 0);
+	/* stop sounds.. */
+	//snd_nm256_playback_stop(chip);
+	//snd_nm256_capture_stop(chip);
+}
+
+
+static irqreturn_t
+snd_nm256_intr_check(struct nm256 *chip)
+{
+	if (chip->badintrcount++ > 1000) {
+		/*
+		 * I'm not sure if the best thing is to stop the card from
+		 * playing or just release the interrupt (after all, we're in
+		 * a bad situation, so doing fancy stuff may not be such a good
+		 * idea).
+		 *
+		 * I worry about the card engine continuing to play noise
+		 * over and over, however--that could become a very
+		 * obnoxious problem.  And we know that when this usually
+		 * happens things are fairly safe, it just means the user's
+		 * inserted a PCMCIA card and someone's spamming us with IRQ 9s.
+		 */
+		if (chip->streams[SNDRV_PCM_STREAM_PLAYBACK].running)
+			snd_nm256_playback_stop(chip);
+		if (chip->streams[SNDRV_PCM_STREAM_CAPTURE].running)
+			snd_nm256_capture_stop(chip);
+		chip->badintrcount = 0;
+		return IRQ_HANDLED;
+	}
+	return IRQ_NONE;
+}
+
+/* 
+ * Handle a potential interrupt for the device referred to by DEV_ID. 
+ *
+ * I don't like the cut-n-paste job here either between the two routines,
+ * but there are sufficient differences between the two interrupt handlers
+ * that parameterizing it isn't all that great either.  (Could use a macro,
+ * I suppose...yucky bleah.)
+ */
+
+static irqreturn_t
+snd_nm256_interrupt(int irq, void *dev_id, struct pt_regs *dummy)
+{
+	struct nm256 *chip = dev_id;
+	u16 status;
+	u8 cbyte;
+
+	status = snd_nm256_readw(chip, NM_INT_REG);
+
+	/* Not ours. */
+	if (status == 0)
+		return snd_nm256_intr_check(chip);
+
+	chip->badintrcount = 0;
+
+	/* Rather boring; check for individual interrupts and process them. */
+
+	spin_lock(&chip->reg_lock);
+	if (status & NM_PLAYBACK_INT) {
+		status &= ~NM_PLAYBACK_INT;
+		NM_ACK_INT(chip, NM_PLAYBACK_INT);
+		snd_nm256_playback_update(chip);
+	}
+
+	if (status & NM_RECORD_INT) {
+		status &= ~NM_RECORD_INT;
+		NM_ACK_INT(chip, NM_RECORD_INT);
+		snd_nm256_capture_update(chip);
+	}
+
+	if (status & NM_MISC_INT_1) {
+		status &= ~NM_MISC_INT_1;
+		NM_ACK_INT(chip, NM_MISC_INT_1);
+		snd_printd("NM256: Got misc interrupt #1\n");
+		snd_nm256_writew(chip, NM_INT_REG, 0x8000);
+		cbyte = snd_nm256_readb(chip, 0x400);
+		snd_nm256_writeb(chip, 0x400, cbyte | 2);
+	}
+
+	if (status & NM_MISC_INT_2) {
+		status &= ~NM_MISC_INT_2;
+		NM_ACK_INT(chip, NM_MISC_INT_2);
+		snd_printd("NM256: Got misc interrupt #2\n");
+		cbyte = snd_nm256_readb(chip, 0x400);
+		snd_nm256_writeb(chip, 0x400, cbyte & ~2);
+	}
+
+	/* Unknown interrupt. */
+	if (status) {
+		snd_printd("NM256: Fire in the hole! Unknown status 0x%x\n",
+			   status);
+		/* Pray. */
+		NM_ACK_INT(chip, status);
+	}
+
+	spin_unlock(&chip->reg_lock);
+	return IRQ_HANDLED;
+}
+
+/*
+ * Handle a potential interrupt for the device referred to by DEV_ID.
+ * This handler is for the 256ZX, and is very similar to the non-ZX
+ * routine.
+ */
+
+static irqreturn_t
+snd_nm256_interrupt_zx(int irq, void *dev_id, struct pt_regs *dummy)
+{
+	struct nm256 *chip = dev_id;
+	u32 status;
+	u8 cbyte;
+
+	status = snd_nm256_readl(chip, NM_INT_REG);
+
+	/* Not ours. */
+	if (status == 0)
+		return snd_nm256_intr_check(chip);
+
+	chip->badintrcount = 0;
+
+	/* Rather boring; check for individual interrupts and process them. */
+
+	spin_lock(&chip->reg_lock);
+	if (status & NM2_PLAYBACK_INT) {
+		status &= ~NM2_PLAYBACK_INT;
+		NM2_ACK_INT(chip, NM2_PLAYBACK_INT);
+		snd_nm256_playback_update(chip);
+	}
+
+	if (status & NM2_RECORD_INT) {
+		status &= ~NM2_RECORD_INT;
+		NM2_ACK_INT(chip, NM2_RECORD_INT);
+		snd_nm256_capture_update(chip);
+	}
+
+	if (status & NM2_MISC_INT_1) {
+		status &= ~NM2_MISC_INT_1;
+		NM2_ACK_INT(chip, NM2_MISC_INT_1);
+		snd_printd("NM256: Got misc interrupt #1\n");
+		cbyte = snd_nm256_readb(chip, 0x400);
+		snd_nm256_writeb(chip, 0x400, cbyte | 2);
+	}
+
+	if (status & NM2_MISC_INT_2) {
+		status &= ~NM2_MISC_INT_2;
+		NM2_ACK_INT(chip, NM2_MISC_INT_2);
+		snd_printd("NM256: Got misc interrupt #2\n");
+		cbyte = snd_nm256_readb(chip, 0x400);
+		snd_nm256_writeb(chip, 0x400, cbyte & ~2);
+	}
+
+	/* Unknown interrupt. */
+	if (status) {
+		snd_printd("NM256: Fire in the hole! Unknown status 0x%x\n",
+			   status);
+		/* Pray. */
+		NM2_ACK_INT(chip, status);
+	}
+
+	spin_unlock(&chip->reg_lock);
+	return IRQ_HANDLED;
+}
+
+/*
+ * AC97 interface
+ */
+
+/*
+ * Waits for the mixer to become ready to be written; returns a zero value
+ * if it timed out.
+ */
+static int
+snd_nm256_ac97_ready(struct nm256 *chip)
+{
+	int timeout = 10;
+	u32 testaddr;
+	u16 testb;
+
+	testaddr = chip->mixer_status_offset;
+	testb = chip->mixer_status_mask;
+
+	/* 
+	 * Loop around waiting for the mixer to become ready. 
+	 */
+	while (timeout-- > 0) {
+		if ((snd_nm256_readw(chip, testaddr) & testb) == 0)
+			return 1;
+		udelay(100);
+	}
+	return 0;
+}
+
+/* 
+ * Initial register values to be written to the AC97 mixer.
+ * While most of these are identical to the reset values, we do this
+ * so that we have most of the register contents cached--this avoids
+ * reading from the mixer directly (which seems to be problematic,
+ * probably due to ignorance).
+ */
+
+struct initialValues {
+	unsigned short reg;
+	unsigned short value;
+};
+
+static struct initialValues nm256_ac97_init_val[] =
+{
+	{ AC97_MASTER, 		0x8000 },
+	{ AC97_HEADPHONE,	0x8000 },
+	{ AC97_MASTER_MONO,	0x8000 },
+	{ AC97_PC_BEEP,		0x8000 },
+	{ AC97_PHONE,		0x8008 },
+	{ AC97_MIC,		0x8000 },
+	{ AC97_LINE,		0x8808 },
+	{ AC97_CD,		0x8808 },
+	{ AC97_VIDEO,		0x8808 },
+	{ AC97_AUX,		0x8808 },
+	{ AC97_PCM,		0x8808 },
+	{ AC97_REC_SEL,		0x0000 },
+	{ AC97_REC_GAIN,	0x0B0B },
+	{ AC97_GENERAL_PURPOSE,	0x0000 },
+	{ AC97_3D_CONTROL,	0x8000 }, 
+	{ AC97_VENDOR_ID1, 	0x8384 },
+	{ AC97_VENDOR_ID2,	0x7609 },
+};
+
+static int nm256_ac97_idx(unsigned short reg)
+{
+	int i;
+	for (i = 0; i < ARRAY_SIZE(nm256_ac97_init_val); i++)
+		if (nm256_ac97_init_val[i].reg == reg)
+			return i;
+	return -1;
+}
+
+/*
+ * some nm256 easily crash when reading from mixer registers
+ * thus we're treating it as a write-only mixer and cache the
+ * written values
+ */
+static unsigned short
+snd_nm256_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
+{
+	struct nm256 *chip = ac97->private_data;
+	int idx = nm256_ac97_idx(reg);
+
+	if (idx < 0)
+		return 0;
+	return chip->ac97_regs[idx];
+}
+
+/* 
+ */
+static void
+snd_nm256_ac97_write(struct snd_ac97 *ac97,
+		     unsigned short reg, unsigned short val)
+{
+	struct nm256 *chip = ac97->private_data;
+	int tries = 2;
+	int idx = nm256_ac97_idx(reg);
+	u32 base;
+
+	if (idx < 0)
+		return;
+
+	base = chip->mixer_base;
+
+	snd_nm256_ac97_ready(chip);
+
+	/* Wait for the write to take, too. */
+	while (tries-- > 0) {
+		snd_nm256_writew(chip, base + reg, val);
+		msleep(1);  /* a little delay here seems better.. */
+		if (snd_nm256_ac97_ready(chip)) {
+			/* successful write: set cache */
+			chip->ac97_regs[idx] = val;
+			return;
+		}
+	}
+	snd_printd("nm256: ac97 codec not ready..\n");
+}
+
+/* static resolution table */
+static struct snd_ac97_res_table nm256_res_table[] = {
+	{ AC97_MASTER, 0x1f1f },
+	{ AC97_HEADPHONE, 0x1f1f },
+	{ AC97_MASTER_MONO, 0x001f },
+	{ AC97_PC_BEEP, 0x001f },
+	{ AC97_PHONE, 0x001f },
+	{ AC97_MIC, 0x001f },
+	{ AC97_LINE, 0x1f1f },
+	{ AC97_CD, 0x1f1f },
+	{ AC97_VIDEO, 0x1f1f },
+	{ AC97_AUX, 0x1f1f },
+	{ AC97_PCM, 0x1f1f },
+	{ AC97_REC_GAIN, 0x0f0f },
+	{0} /* terminator */
+};
+
+/* initialize the ac97 into a known state */
+static void
+snd_nm256_ac97_reset(struct snd_ac97 *ac97)
+{
+	struct nm256 *chip = ac97->private_data;
+
+	/* Reset the mixer.  'Tis magic!  */
+	snd_nm256_writeb(chip, 0x6c0, 1);
+	if (! chip->reset_workaround) {
+		/* Dell latitude LS will lock up by this */
+		snd_nm256_writeb(chip, 0x6cc, 0x87);
+	}
+	if (! chip->reset_workaround_2) {
+		/* Dell latitude CSx will lock up by this */
+		snd_nm256_writeb(chip, 0x6cc, 0x80);
+		snd_nm256_writeb(chip, 0x6cc, 0x0);
+	}
+	if (! chip->in_resume) {
+		int i;
+		for (i = 0; i < ARRAY_SIZE(nm256_ac97_init_val); i++) {
+			/* preload the cache, so as to avoid even a single
+			 * read of the mixer regs
+			 */
+			snd_nm256_ac97_write(ac97, nm256_ac97_init_val[i].reg,
+					     nm256_ac97_init_val[i].value);
+		}
+	}
+}
+
+/* create an ac97 mixer interface */
+static int __devinit
+snd_nm256_mixer(struct nm256 *chip)
+{
+	struct snd_ac97_bus *pbus;
+	struct snd_ac97_template ac97;
+	int err;
+	static struct snd_ac97_bus_ops ops = {
+		.reset = snd_nm256_ac97_reset,
+		.write = snd_nm256_ac97_write,
+		.read = snd_nm256_ac97_read,
+	};
+
+	chip->ac97_regs = kcalloc(sizeof(short),
+				  ARRAY_SIZE(nm256_ac97_init_val), GFP_KERNEL);
+	if (! chip->ac97_regs)
+		return -ENOMEM;
+
+	if ((err = snd_ac97_bus(chip->card, 0, &ops, NULL, &pbus)) < 0)
+		return err;
+
+	memset(&ac97, 0, sizeof(ac97));
+	ac97.scaps = AC97_SCAP_AUDIO; /* we support audio! */
+	ac97.private_data = chip;
+	ac97.res_table = nm256_res_table;
+	pbus->no_vra = 1;
+	err = snd_ac97_mixer(pbus, &ac97, &chip->ac97);
+	if (err < 0)
+		return err;
+	if (! (chip->ac97->id & (0xf0000000))) {
+		/* looks like an invalid id */
+		sprintf(chip->card->mixername, "%s AC97", chip->card->driver);
+	}
+	return 0;
+}
+
+/* 
+ * See if the signature left by the NM256 BIOS is intact; if so, we use
+ * the associated address as the end of our audio buffer in the video
+ * RAM.
+ */
+
+static int __devinit
+snd_nm256_peek_for_sig(struct nm256 *chip)
+{
+	/* The signature is located 1K below the end of video RAM.  */
+	void __iomem *temp;
+	/* Default buffer end is 5120 bytes below the top of RAM.  */
+	unsigned long pointer_found = chip->buffer_end - 0x1400;
+	u32 sig;
+
+	temp = ioremap_nocache(chip->buffer_addr + chip->buffer_end - 0x400, 16);
+	if (temp == NULL) {
+		snd_printk(KERN_ERR "Unable to scan for card signature in video RAM\n");
+		return -EBUSY;
+	}
+
+	sig = readl(temp);
+	if ((sig & NM_SIG_MASK) == NM_SIGNATURE) {
+		u32 pointer = readl(temp + 4);
+
+		/*
+		 * If it's obviously invalid, don't use it
+		 */
+		if (pointer == 0xffffffff ||
+		    pointer < chip->buffer_size ||
+		    pointer > chip->buffer_end) {
+			snd_printk(KERN_ERR "invalid signature found: 0x%x\n", pointer);
+			iounmap(temp);
+			return -ENODEV;
+		} else {
+			pointer_found = pointer;
+			printk(KERN_INFO "nm256: found card signature in video RAM: 0x%x\n",
+			       pointer);
+		}
+	}
+
+	iounmap(temp);
+	chip->buffer_end = pointer_found;
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+/*
+ * APM event handler, so the card is properly reinitialized after a power
+ * event.
+ */
+static int nm256_suspend(struct pci_dev *pci, pm_message_t state)
+{
+	struct snd_card *card = pci_get_drvdata(pci);
+	struct nm256 *chip = card->private_data;
+
+	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+	snd_pcm_suspend_all(chip->pcm);
+	snd_ac97_suspend(chip->ac97);
+	chip->coeffs_current = 0;
+	pci_disable_device(pci);
+	pci_save_state(pci);
+	return 0;
+}
+
+static int nm256_resume(struct pci_dev *pci)
+{
+	struct snd_card *card = pci_get_drvdata(pci);
+	struct nm256 *chip = card->private_data;
+	int i;
+
+	/* Perform a full reset on the hardware */
+	chip->in_resume = 1;
+	pci_restore_state(pci);
+	pci_enable_device(pci);
+	snd_nm256_init_chip(chip);
+
+	/* restore ac97 */
+	snd_ac97_resume(chip->ac97);
+
+	for (i = 0; i < 2; i++) {
+		struct nm256_stream *s = &chip->streams[i];
+		if (s->substream && s->suspended) {
+			spin_lock_irq(&chip->reg_lock);
+			snd_nm256_set_format(chip, s, s->substream);
+			spin_unlock_irq(&chip->reg_lock);
+		}
+	}
+
+	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+	chip->in_resume = 0;
+	return 0;
+}
+#endif /* CONFIG_PM */
+
+static int snd_nm256_free(struct nm256 *chip)
+{
+	if (chip->streams[SNDRV_PCM_STREAM_PLAYBACK].running)
+		snd_nm256_playback_stop(chip);
+	if (chip->streams[SNDRV_PCM_STREAM_CAPTURE].running)
+		snd_nm256_capture_stop(chip);
+
+	if (chip->irq >= 0)
+		synchronize_irq(chip->irq);
+
+	if (chip->cport)
+		iounmap(chip->cport);
+	if (chip->buffer)
+		iounmap(chip->buffer);
+	release_and_free_resource(chip->res_cport);
+	release_and_free_resource(chip->res_buffer);
+	if (chip->irq >= 0)
+		free_irq(chip->irq, chip);
+
+	pci_disable_device(chip->pci);
+	kfree(chip->ac97_regs);
+	kfree(chip);
+	return 0;
+}
+
+static int snd_nm256_dev_free(struct snd_device *device)
+{
+	struct nm256 *chip = device->device_data;
+	return snd_nm256_free(chip);
+}
+
+static int __devinit
+snd_nm256_create(struct snd_card *card, struct pci_dev *pci,
+		 struct nm256 **chip_ret)
+{
+	struct nm256 *chip;
+	int err, pval;
+	static struct snd_device_ops ops = {
+		.dev_free =	snd_nm256_dev_free,
+	};
+	u32 addr;
+
+	*chip_ret = NULL;
+
+	if ((err = pci_enable_device(pci)) < 0)
+		return err;
+
+	chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+	if (chip == NULL) {
+		pci_disable_device(pci);
+		return -ENOMEM;
+	}
+
+	chip->card = card;
+	chip->pci = pci;
+	chip->use_cache = use_cache;
+	spin_lock_init(&chip->reg_lock);
+	chip->irq = -1;
+	mutex_init(&chip->irq_mutex);
+
+	/* store buffer sizes in bytes */
+	chip->streams[SNDRV_PCM_STREAM_PLAYBACK].bufsize = playback_bufsize * 1024;
+	chip->streams[SNDRV_PCM_STREAM_CAPTURE].bufsize = capture_bufsize * 1024;
+
+	/* 
+	 * The NM256 has two memory ports.  The first port is nothing
+	 * more than a chunk of video RAM, which is used as the I/O ring
+	 * buffer.  The second port has the actual juicy stuff (like the
+	 * mixer and the playback engine control registers).
+	 */
+
+	chip->buffer_addr = pci_resource_start(pci, 0);
+	chip->cport_addr = pci_resource_start(pci, 1);
+
+	/* Init the memory port info.  */
+	/* remap control port (#2) */
+	chip->res_cport = request_mem_region(chip->cport_addr, NM_PORT2_SIZE,
+					     card->driver);
+	if (chip->res_cport == NULL) {
+		snd_printk(KERN_ERR "memory region 0x%lx (size 0x%x) busy\n",
+			   chip->cport_addr, NM_PORT2_SIZE);
+		err = -EBUSY;
+		goto __error;
+	}
+	chip->cport = ioremap_nocache(chip->cport_addr, NM_PORT2_SIZE);
+	if (chip->cport == NULL) {
+		snd_printk(KERN_ERR "unable to map control port %lx\n", chip->cport_addr);
+		err = -ENOMEM;
+		goto __error;
+	}
+
+	if (!strcmp(card->driver, "NM256AV")) {
+		/* Ok, try to see if this is a non-AC97 version of the hardware. */
+		pval = snd_nm256_readw(chip, NM_MIXER_PRESENCE);
+		if ((pval & NM_PRESENCE_MASK) != NM_PRESENCE_VALUE) {
+			if (! force_ac97) {
+				printk(KERN_ERR "nm256: no ac97 is found!\n");
+				printk(KERN_ERR "  force the driver to load by "
+				       "passing in the module parameter\n");
+				printk(KERN_ERR "    force_ac97=1\n");
+				printk(KERN_ERR "  or try sb16 or cs423x drivers instead.\n");
+				err = -ENXIO;
+				goto __error;
+			}
+		}
+		chip->buffer_end = 2560 * 1024;
+		chip->interrupt = snd_nm256_interrupt;
+		chip->mixer_status_offset = NM_MIXER_STATUS_OFFSET;
+		chip->mixer_status_mask = NM_MIXER_READY_MASK;
+	} else {
+		/* Not sure if there is any relevant detect for the ZX or not.  */
+		if (snd_nm256_readb(chip, 0xa0b) != 0)
+			chip->buffer_end = 6144 * 1024;
+		else
+			chip->buffer_end = 4096 * 1024;
+
+		chip->interrupt = snd_nm256_interrupt_zx;
+		chip->mixer_status_offset = NM2_MIXER_STATUS_OFFSET;
+		chip->mixer_status_mask = NM2_MIXER_READY_MASK;
+	}
+	
+	chip->buffer_size = chip->streams[SNDRV_PCM_STREAM_PLAYBACK].bufsize +
+		chip->streams[SNDRV_PCM_STREAM_CAPTURE].bufsize;
+	if (chip->use_cache)
+		chip->buffer_size += NM_TOTAL_COEFF_COUNT * 4;
+	else
+		chip->buffer_size += NM_MAX_PLAYBACK_COEF_SIZE + NM_MAX_RECORD_COEF_SIZE;
+
+	if (buffer_top >= chip->buffer_size && buffer_top < chip->buffer_end)
+		chip->buffer_end = buffer_top;
+	else {
+		/* get buffer end pointer from signature */
+		if ((err = snd_nm256_peek_for_sig(chip)) < 0)
+			goto __error;
+	}
+
+	chip->buffer_start = chip->buffer_end - chip->buffer_size;
+	chip->buffer_addr += chip->buffer_start;
+
+	printk(KERN_INFO "nm256: Mapping port 1 from 0x%x - 0x%x\n",
+	       chip->buffer_start, chip->buffer_end);
+
+	chip->res_buffer = request_mem_region(chip->buffer_addr,
+					      chip->buffer_size,
+					      card->driver);
+	if (chip->res_buffer == NULL) {
+		snd_printk(KERN_ERR "nm256: buffer 0x%lx (size 0x%x) busy\n",
+			   chip->buffer_addr, chip->buffer_size);
+		err = -EBUSY;
+		goto __error;
+	}
+	chip->buffer = ioremap_nocache(chip->buffer_addr, chip->buffer_size);
+	if (chip->buffer == NULL) {
+		err = -ENOMEM;
+		snd_printk(KERN_ERR "unable to map ring buffer at %lx\n", chip->buffer_addr);
+		goto __error;
+	}
+
+	/* set offsets */
+	addr = chip->buffer_start;
+	chip->streams[SNDRV_PCM_STREAM_PLAYBACK].buf = addr;
+	addr += chip->streams[SNDRV_PCM_STREAM_PLAYBACK].bufsize;
+	chip->streams[SNDRV_PCM_STREAM_CAPTURE].buf = addr;
+	addr += chip->streams[SNDRV_PCM_STREAM_CAPTURE].bufsize;
+	if (chip->use_cache) {
+		chip->all_coeff_buf = addr;
+	} else {
+		chip->coeff_buf[SNDRV_PCM_STREAM_PLAYBACK] = addr;
+		addr += NM_MAX_PLAYBACK_COEF_SIZE;
+		chip->coeff_buf[SNDRV_PCM_STREAM_CAPTURE] = addr;
+	}
+
+	/* Fixed setting. */
+	chip->mixer_base = NM_MIXER_OFFSET;
+
+	chip->coeffs_current = 0;
+
+	snd_nm256_init_chip(chip);
+
+	// pci_set_master(pci); /* needed? */
+	
+	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0)
+		goto __error;
+
+	snd_card_set_dev(card, &pci->dev);
+
+	*chip_ret = chip;
+	return 0;
+
+__error:
+	snd_nm256_free(chip);
+	return err;
+}
+
+
+struct nm256_quirk {
+	unsigned short vendor;
+	unsigned short device;
+	int type;
+};
+
+enum { NM_BLACKLISTED, NM_RESET_WORKAROUND, NM_RESET_WORKAROUND_2 };
+
+static struct nm256_quirk nm256_quirks[] __devinitdata = {
+	/* HP omnibook 4150 has cs4232 codec internally */
+	{ .vendor = 0x103c, .device = 0x0007, .type = NM_BLACKLISTED },
+	/* Sony PCG-F305 */
+	{ .vendor = 0x104d, .device = 0x8041, .type = NM_RESET_WORKAROUND },
+	/* Dell Latitude LS */
+	{ .vendor = 0x1028, .device = 0x0080, .type = NM_RESET_WORKAROUND },
+	/* Dell Latitude CSx */
+	{ .vendor = 0x1028, .device = 0x0091, .type = NM_RESET_WORKAROUND_2 },
+	{0} /* terminator */
+};
+
+
+static int __devinit snd_nm256_probe(struct pci_dev *pci,
+				     const struct pci_device_id *pci_id)
+{
+	struct snd_card *card;
+	struct nm256 *chip;
+	int err;
+	struct nm256_quirk *q;
+	u16 subsystem_vendor, subsystem_device;
+
+	pci_read_config_word(pci, PCI_SUBSYSTEM_VENDOR_ID, &subsystem_vendor);
+	pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &subsystem_device);
+
+	for (q = nm256_quirks; q->vendor; q++) {
+		if (q->vendor == subsystem_vendor && q->device == subsystem_device) {
+			switch (q->type) {
+			case NM_BLACKLISTED:
+				printk(KERN_INFO "nm256: The device is blacklisted. "
+				       "Loading stopped\n");
+				return -ENODEV;
+			case NM_RESET_WORKAROUND_2:
+				reset_workaround_2 = 1;
+				/* Fall-through */
+			case NM_RESET_WORKAROUND:
+				reset_workaround = 1;
+				break;
+			}
+		}
+	}
+
+	card = snd_card_new(index, id, THIS_MODULE, 0);
+	if (card == NULL)
+		return -ENOMEM;
+
+	switch (pci->device) {
+	case PCI_DEVICE_ID_NEOMAGIC_NM256AV_AUDIO:
+		strcpy(card->driver, "NM256AV");
+		break;
+	case PCI_DEVICE_ID_NEOMAGIC_NM256ZX_AUDIO:
+		strcpy(card->driver, "NM256ZX");
+		break;
+	case PCI_DEVICE_ID_NEOMAGIC_NM256XL_PLUS_AUDIO:
+		strcpy(card->driver, "NM256XL+");
+		break;
+	default:
+		snd_printk(KERN_ERR "invalid device id 0x%x\n", pci->device);
+		snd_card_free(card);
+		return -EINVAL;
+	}
+
+	if (vaio_hack)
+		buffer_top = 0x25a800;	/* this avoids conflicts with XFree86 server */
+
+	if (playback_bufsize < 4)
+		playback_bufsize = 4;
+	if (playback_bufsize > 128)
+		playback_bufsize = 128;
+	if (capture_bufsize < 4)
+		capture_bufsize = 4;
+	if (capture_bufsize > 128)
+		capture_bufsize = 128;
+	if ((err = snd_nm256_create(card, pci, &chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	card->private_data = chip;
+
+	if (reset_workaround) {
+		snd_printdd(KERN_INFO "nm256: reset_workaround activated\n");
+		chip->reset_workaround = 1;
+	}
+
+	if (reset_workaround_2) {
+		snd_printdd(KERN_INFO "nm256: reset_workaround_2 activated\n");
+		chip->reset_workaround_2 = 1;
+	}
+
+	if ((err = snd_nm256_pcm(chip, 0)) < 0 ||
+	    (err = snd_nm256_mixer(chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	sprintf(card->shortname, "NeoMagic %s", card->driver);
+	sprintf(card->longname, "%s at 0x%lx & 0x%lx, irq %d",
+		card->shortname,
+		chip->buffer_addr, chip->cport_addr, chip->irq);
+
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	pci_set_drvdata(pci, card);
+	return 0;
+}
+
+static void __devexit snd_nm256_remove(struct pci_dev *pci)
+{
+	snd_card_free(pci_get_drvdata(pci));
+	pci_set_drvdata(pci, NULL);
+}
+
+
+static struct pci_driver driver = {
+	.name = "NeoMagic 256",
+	.id_table = snd_nm256_ids,
+	.probe = snd_nm256_probe,
+	.remove = snd_nm256_remove,
+#ifdef CONFIG_PM
+	.suspend = nm256_suspend,
+	.resume = nm256_resume,
+#endif
+};
+
+
+static int __init alsa_card_nm256_init(void)
+{
+	return pci_register_driver(&driver);
+}
+
+static void __exit alsa_card_nm256_exit(void)
+{
+	pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_nm256_init)
+module_exit(alsa_card_nm256_exit)
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/nm256/nm256_coef.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/nm256/nm256_coef.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/nm256/nm256_coef.c	(revision 91)
@@ -0,0 +1,4607 @@
+#define NM_TOTAL_COEFF_COUNT 0x3158
+
+static char coefficients[NM_TOTAL_COEFF_COUNT * 4] = { 
+	0xFF, 0xFF, 0x2F, 0x00, 0x4B, 0xFF, 0xA5, 0x01, 0xEF, 0xFC, 0x21,
+	0x05, 0x87, 0xF7, 0x62, 0x11, 0xE9, 0x45, 0x5E, 0xF9, 0xB5, 0x01,
+	0xDE, 0xFF, 0xA4, 0xFF, 0x60, 0x00, 0xCA, 0xFF, 0x0D, 0x00, 0xFD,
+	0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3D, 0xFC, 0xD6, 0x06,
+	0x4C, 0xF3, 0xED, 0x20, 0x3D, 0x3D, 0x4A, 0xF3, 0x4E, 0x05, 0xB1,
+	0xFD, 0xE1, 0x00, 0xC3, 0xFF, 0x05, 0x00, 0x02, 0x00, 0xFD, 0xFF,
+	0x2A, 0x00, 0x5C, 0xFF, 0xAA, 0x01, 0x71, 0xFC, 0x07, 0x07, 0x7E,
+	0xF1, 0x44, 0x30, 0x44, 0x30, 0x7E, 0xF1, 0x07, 0x07, 0x71, 0xFC,
+	0xAA, 0x01, 0x5C, 0xFF, 0x2A, 0x00, 0xFD, 0xFF, 0x02, 0x00, 0x05,
+	0x00, 0xC3, 0xFF, 0xE1, 0x00, 0xB1, 0xFD, 0x4E, 0x05, 0x4A, 0xF3,
+	0x3D, 0x3D, 0xED, 0x20, 0x4C, 0xF3, 0xD6, 0x06, 0x3D, 0xFC, 0xE6,
+	0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x0D, 0x00, 0xCA, 0xFF,
+	0x60, 0x00, 0xA4, 0xFF, 0xDE, 0xFF, 0xB5, 0x01, 0x5E, 0xF9, 0xE9,
+	0x45, 0x62, 0x11, 0x87, 0xF7, 0x21, 0x05, 0xEF, 0xFC, 0xA5, 0x01,
+	0x4B, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x1E, 0x00, 0x84,
+	0xFF, 0x11, 0x01, 0x34, 0xFE, 0x8F, 0x02, 0xC7, 0xFC, 0xAE, 0x03,
+	0xF7, 0x48, 0xAE, 0x03, 0xC7, 0xFC, 0x8F, 0x02, 0x34, 0xFE, 0x11,
+	0x01, 0x84, 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3D, 0xFF,
+	0xCA, 0x01, 0x95, 0xFC, 0xEA, 0x05, 0xBB, 0xF5, 0x25, 0x17, 0x3C,
+	0x43, 0x8D, 0xF6, 0x43, 0x03, 0xF5, 0xFE, 0x26, 0x00, 0x20, 0x00,
+	0xE2, 0xFF, 0x08, 0x00, 0xFD, 0xFF, 0x30, 0x00, 0x4D, 0xFF, 0xC5,
+	0x01, 0x4C, 0xFC, 0x26, 0x07, 0xA3, 0xF1, 0xAB, 0x2C, 0xBB, 0x33,
+	0x8F, 0xF1, 0xCA, 0x06, 0xA6, 0xFC, 0x85, 0x01, 0x6F, 0xFF, 0x24,
+	0x00, 0xFD, 0xFF, 0x03, 0x00, 0xFE, 0xFF, 0xD5, 0xFF, 0xBC, 0x00,
+	0xF0, 0xFD, 0xEC, 0x04, 0xD9, 0xF3, 0xB1, 0x3E, 0xCD, 0x1E, 0xC1,
+	0xF3, 0xAF, 0x06, 0x49, 0xFC, 0xE4, 0x01, 0x36, 0xFF, 0x36, 0x00,
+	0xFE, 0xFF, 0x16, 0x00, 0xA6, 0xFF, 0xBB, 0x00, 0xE9, 0xFE, 0x38,
+	0x01, 0x4B, 0xFF, 0x28, 0xFE, 0x3A, 0x48, 0x04, 0x0A, 0x2E, 0xFA,
+	0xDF, 0x03, 0x8A, 0xFD, 0x60, 0x01, 0x65, 0xFF, 0x27, 0x00, 0x00,
+	0x00, 0xFF, 0xFF, 0x2E, 0x00, 0x50, 0xFF, 0x98, 0x01, 0x0D, 0xFD,
+	0xE0, 0x04, 0x14, 0xF8, 0xC3, 0x0F, 0x89, 0x46, 0x4C, 0xFA, 0x38,
+	0x01, 0x25, 0x00, 0x7D, 0xFF, 0x73, 0x00, 0xC2, 0xFF, 0x0F, 0x00,
+	0xFD, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xE3, 0x01, 0x31, 0xFC, 0x0F,
+	0x07, 0x84, 0xF2, 0x29, 0x25, 0x1A, 0x3A, 0x67, 0xF2, 0xF6, 0x05,
+	0x41, 0xFD, 0x24, 0x01, 0xA1, 0xFF, 0x12, 0x00, 0x00, 0x00, 0xFF,
+	0xFF, 0x15, 0x00, 0x97, 0xFF, 0x37, 0x01, 0x22, 0xFD, 0x23, 0x06,
+	0x2F, 0xF2, 0x11, 0x39, 0x7B, 0x26, 0x50, 0xF2, 0x1B, 0x07, 0x32,
+	0xFC, 0xE1, 0x01, 0x3C, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x0E, 0x00,
+	0xC8, 0xFF, 0x64, 0x00, 0x9B, 0xFF, 0xEE, 0xFF, 0x98, 0x01, 0x93,
+	0xF9, 0x10, 0x46, 0x03, 0x11, 0xA7, 0xF7, 0x12, 0x05, 0xF6, 0xFC,
+	0xA2, 0x01, 0x4C, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x26,
+	0x00, 0x6A, 0xFF, 0x53, 0x01, 0xA6, 0xFD, 0xA6, 0x03, 0xA1, 0xFA,
+	0xDE, 0x08, 0x76, 0x48, 0x0C, 0xFF, 0xDE, 0xFE, 0x73, 0x01, 0xC9,
+	0xFE, 0xCA, 0x00, 0xA0, 0xFF, 0x17, 0x00, 0xFE, 0xFF, 0x36, 0x00,
+	0x36, 0xFF, 0xE1, 0x01, 0x52, 0xFC, 0x93, 0x06, 0x10, 0xF4, 0x78,
+	0x1D, 0x90, 0x3F, 0x3E, 0xF4, 0xAA, 0x04, 0x19, 0xFE, 0xA4, 0x00,
+	0xE2, 0xFF, 0xFA, 0xFF, 0x03, 0x00, 0xFD, 0xFF, 0x26, 0x00, 0x68,
+	0xFF, 0x93, 0x01, 0x92, 0xFC, 0xE2, 0x06, 0x83, 0xF1, 0x8C, 0x32,
+	0xED, 0x2D, 0x90, 0xF1, 0x1E, 0x07, 0x57, 0xFC, 0xBD, 0x01, 0x51,
+	0xFF, 0x2E, 0x00, 0xFD, 0xFF, 0x07, 0x00, 0xE8, 0xFF, 0x12, 0x00,
+	0x42, 0x00, 0xC4, 0xFE, 0x94, 0x03, 0x02, 0xF6, 0x89, 0x42, 0x76,
+	0x18, 0x5C, 0xF5, 0x12, 0x06, 0x84, 0xFC, 0xD1, 0x01, 0x3B, 0xFF,
+	0x34, 0x00, 0xFE, 0xFF, 0x1D, 0x00, 0x8A, 0xFF, 0x03, 0x01, 0x53,
+	0xFE, 0x53, 0x02, 0x39, 0xFD, 0xA9, 0x02, 0xF2, 0x48, 0xB9, 0x04,
+	0x54, 0xFC, 0xCA, 0x02, 0x16, 0xFE, 0x20, 0x01, 0x7F, 0xFF, 0x20,
+	0x00, 0x00, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x40, 0xFF, 0xC3, 0x01,
+	0xA7, 0xFC, 0xC0, 0x05, 0x1E, 0xF6, 0xD8, 0x15, 0xE7, 0x43, 0x20,
+	0xF7, 0xEF, 0x02, 0x27, 0xFF, 0x0A, 0x00, 0x2E, 0x00, 0xDD, 0xFF,
+	0x09, 0x00, 0xFD, 0xFF, 0x31, 0x00, 0x48, 0xFF, 0xCD, 0x01, 0x43,
+	0xFC, 0x2A, 0x07, 0xBC, 0xF1, 0x64, 0x2B, 0xE3, 0x34, 0xA3, 0xF1,
+	0xAE, 0x06, 0xBD, 0xFC, 0x77, 0x01, 0x77, 0xFF, 0x21, 0x00, 0xFE,
+	0xFF, 0x02, 0x00, 0x03, 0x00, 0xCA, 0xFF, 0xD4, 0x00, 0xC8, 0xFD,
+	0x2A, 0x05, 0x7D, 0xF3, 0xCA, 0x3D, 0x22, 0x20, 0x76, 0xF3, 0xC8,
+	0x06, 0x41, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF,
+	0x14, 0x00, 0xAC, 0xFF, 0xAC, 0x00, 0x08, 0xFF, 0xFD, 0x00, 0xB5,
+	0xFF, 0x4B, 0xFD, 0xF4, 0x47, 0x30, 0x0B, 0xBC, 0xF9, 0x17, 0x04,
+	0x6E, 0xFD, 0x6D, 0x01, 0x60, 0xFF, 0x29, 0x00, 0x00, 0x00, 0xFF,
+	0xFF, 0x2C, 0x00, 0x54, 0xFF, 0x8D, 0x01, 0x26, 0xFD, 0xAD, 0x04,
+	0x82, 0xF8, 0x87, 0x0E, 0xF9, 0x46, 0x0C, 0xFB, 0xD4, 0x00, 0x5D,
+	0x00, 0x5E, 0xFF, 0x82, 0x00, 0xBD, 0xFF, 0x10, 0x00, 0xFD, 0xFF,
+	0x36, 0x00, 0x38, 0xFF, 0xE5, 0x01, 0x33, 0xFC, 0x01, 0x07, 0xBE,
+	0xF2, 0xD6, 0x23, 0x1F, 0x3B, 0xA5, 0xF2, 0xC5, 0x05, 0x62, 0xFD,
+	0x10, 0x01, 0xAB, 0xFF, 0x0E, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x19,
+	0x00, 0x8E, 0xFF, 0x49, 0x01, 0x04, 0xFD, 0x4D, 0x06, 0x00, 0xF2,
+	0xFE, 0x37, 0xCB, 0x27, 0x21, 0xF2, 0x23, 0x07, 0x34, 0xFC, 0xDD,
+	0x01, 0x3F, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x0C, 0x00, 0xCE, 0xFF,
+	0x56, 0x00, 0xB9, 0xFF, 0xB8, 0xFF, 0xF7, 0x01, 0xE2, 0xF8, 0x8D,
+	0x45, 0x46, 0x12, 0x3C, 0xF7, 0x43, 0x05, 0xDF, 0xFC, 0xAC, 0x01,
+	0x48, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x24, 0x00, 0x70,
+	0xFF, 0x46, 0x01, 0xC3, 0xFD, 0x6D, 0x03, 0x14, 0xFB, 0xBE, 0x07,
+	0xA6, 0x48, 0xF8, 0xFF, 0x70, 0xFE, 0xAE, 0x01, 0xAA, 0xFE, 0xD9,
+	0x00, 0x9A, 0xFF, 0x19, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF,
+	0xDE, 0x01, 0x5D, 0xFC, 0x74, 0x06, 0x63, 0xF4, 0x23, 0x1C, 0x66,
+	0x40, 0xAA, 0xF4, 0x65, 0x04, 0x44, 0xFE, 0x8B, 0x00, 0xEE, 0xFF,
+	0xF5, 0xFF, 0x04, 0x00, 0xFD, 0xFF, 0x29, 0x00, 0x61, 0xFF, 0x9F,
+	0x01, 0x80, 0xFC, 0xF7, 0x06, 0x7D, 0xF1, 0x5A, 0x31, 0x2C, 0x2F,
+	0x83, 0xF1, 0x13, 0x07, 0x64, 0xFC, 0xB3, 0x01, 0x57, 0xFF, 0x2C,
+	0x00, 0xFD, 0xFF, 0x06, 0x00, 0xED, 0xFF, 0x05, 0x00, 0x5D, 0x00,
+	0x95, 0xFE, 0xE2, 0x03, 0x7F, 0xF5, 0xCC, 0x41, 0xC7, 0x19, 0xFF,
+	0xF4, 0x37, 0x06, 0x75, 0xFC, 0xD6, 0x01, 0x39, 0xFF, 0x35, 0x00,
+	0xFE, 0xFF, 0x1B, 0x00, 0x90, 0xFF, 0xF4, 0x00, 0x72, 0xFE, 0x18,
+	0x02, 0xAA, 0xFD, 0xAB, 0x01, 0xDF, 0x48, 0xCA, 0x05, 0xE1, 0xFB,
+	0x05, 0x03, 0xF7, 0xFD, 0x2E, 0x01, 0x79, 0xFF, 0x21, 0x00, 0x00,
+	0x00, 0xFF, 0xFF, 0x32, 0x00, 0x43, 0xFF, 0xBB, 0x01, 0xBA, 0xFC,
+	0x95, 0x05, 0x83, 0xF6, 0x8C, 0x14, 0x87, 0x44, 0xBB, 0xF7, 0x98,
+	0x02, 0x5A, 0xFF, 0xEE, 0xFF, 0x3C, 0x00, 0xD8, 0xFF, 0x0A, 0x00,
+	0xFD, 0xFF, 0x32, 0x00, 0x44, 0xFF, 0xD3, 0x01, 0x3C, 0xFC, 0x2A,
+	0x07, 0xDC, 0xF1, 0x1A, 0x2A, 0x06, 0x36, 0xBE, 0xF1, 0x8E, 0x06,
+	0xD5, 0xFC, 0x67, 0x01, 0x7F, 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0x01,
+	0x00, 0x07, 0x00, 0xBE, 0xFF, 0xEA, 0x00, 0xA2, 0xFD, 0x65, 0x05,
+	0x28, 0xF3, 0xDB, 0x3C, 0x78, 0x21, 0x30, 0xF3, 0xDF, 0x06, 0x3A,
+	0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x13, 0x00,
+	0xB2, 0xFF, 0x9D, 0x00, 0x27, 0xFF, 0xC3, 0x00, 0x1F, 0x00, 0x76,
+	0xFC, 0xA3, 0x47, 0x60, 0x0C, 0x4A, 0xF9, 0x4E, 0x04, 0x53, 0xFD,
+	0x79, 0x01, 0x5C, 0xFF, 0x2A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B,
+	0x00, 0x58, 0xFF, 0x82, 0x01, 0x3F, 0xFD, 0x78, 0x04, 0xF2, 0xF8,
+	0x50, 0x0D, 0x5E, 0x47, 0xD5, 0xFB, 0x6F, 0x00, 0x96, 0x00, 0x40,
+	0xFF, 0x91, 0x00, 0xB7, 0xFF, 0x12, 0x00, 0xFD, 0xFF, 0x36, 0x00,
+	0x37, 0xFF, 0xE6, 0x01, 0x36, 0xFC, 0xEF, 0x06, 0xFC, 0xF2, 0x81,
+	0x22, 0x1C, 0x3C, 0xEC, 0xF2, 0x90, 0x05, 0x85, 0xFD, 0xFB, 0x00,
+	0xB6, 0xFF, 0x0A, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x1C, 0x00, 0x85,
+	0xFF, 0x5B, 0x01, 0xE9, 0xFC, 0x73, 0x06, 0xD8, 0xF1, 0xE5, 0x36,
+	0x19, 0x29, 0xF8, 0xF1, 0x29, 0x07, 0x37, 0xFC, 0xD8, 0x01, 0x42,
+	0xFF, 0x33, 0x00, 0xFD, 0xFF, 0x0B, 0x00, 0xD3, 0xFF, 0x47, 0x00,
+	0xD7, 0xFF, 0x82, 0xFF, 0x53, 0x02, 0x39, 0xF8, 0xFD, 0x44, 0x8D,
+	0x13, 0xD3, 0xF6, 0x72, 0x05, 0xCA, 0xFC, 0xB5, 0x01, 0x45, 0xFF,
+	0x31, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x23, 0x00, 0x75, 0xFF, 0x39,
+	0x01, 0xE0, 0xFD, 0x33, 0x03, 0x87, 0xFB, 0xA2, 0x06, 0xCB, 0x48,
+	0xEA, 0x00, 0x01, 0xFE, 0xE9, 0x01, 0x8A, 0xFE, 0xE8, 0x00, 0x95,
+	0xFF, 0x1A, 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x38, 0xFF, 0xDA, 0x01,
+	0x6A, 0xFC, 0x53, 0x06, 0xBA, 0xF4, 0xCE, 0x1A, 0x32, 0x41, 0x1F,
+	0xF5, 0x1D, 0x04, 0x71, 0xFE, 0x71, 0x00, 0xFB, 0xFF, 0xF0, 0xFF,
+	0x05, 0x00, 0xFD, 0xFF, 0x2B, 0x00, 0x5B, 0xFF, 0xAB, 0x01, 0x6F,
+	0xFC, 0x08, 0x07, 0x7E, 0xF1, 0x21, 0x30, 0x67, 0x30, 0x7D, 0xF1,
+	0x05, 0x07, 0x73, 0xFC, 0xA8, 0x01, 0x5C, 0xFF, 0x2A, 0x00, 0xFD,
+	0xFF, 0x05, 0x00, 0xF2, 0xFF, 0xF8, 0xFF, 0x77, 0x00, 0x67, 0xFE,
+	0x2D, 0x04, 0x04, 0xF5, 0x07, 0x41, 0x1B, 0x1B, 0xA6, 0xF4, 0x5A,
+	0x06, 0x67, 0xFC, 0xDB, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFE, 0xFF,
+	0x1A, 0x00, 0x96, 0xFF, 0xE5, 0x00, 0x91, 0xFE, 0xDC, 0x01, 0x1A,
+	0xFE, 0xB3, 0x00, 0xC3, 0x48, 0xE1, 0x06, 0x6E, 0xFB, 0x40, 0x03,
+	0xDA, 0xFD, 0x3C, 0x01, 0x74, 0xFF, 0x23, 0x00, 0x00, 0x00, 0xFF,
+	0xFF, 0x31, 0x00, 0x46, 0xFF, 0xB3, 0x01, 0xCF, 0xFC, 0x67, 0x05,
+	0xEA, 0xF6, 0x44, 0x13, 0x1E, 0x45, 0x5E, 0xF8, 0x3F, 0x02, 0x8E,
+	0xFF, 0xD0, 0xFF, 0x4A, 0x00, 0xD2, 0xFF, 0x0B, 0x00, 0xFD, 0xFF,
+	0x33, 0x00, 0x41, 0xFF, 0xD9, 0x01, 0x36, 0xFC, 0x28, 0x07, 0x01,
+	0xF2, 0xCE, 0x28, 0x23, 0x37, 0xE0, 0xF1, 0x6B, 0x06, 0xEF, 0xFC,
+	0x57, 0x01, 0x87, 0xFF, 0x1B, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x0B,
+	0x00, 0xB4, 0xFF, 0x00, 0x01, 0x7E, 0xFD, 0x9C, 0x05, 0xDC, 0xF2,
+	0xE4, 0x3B, 0xCD, 0x22, 0xEE, 0xF2, 0xF3, 0x06, 0x35, 0xFC, 0xE6,
+	0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x11, 0x00, 0xB8, 0xFF,
+	0x8E, 0x00, 0x46, 0xFF, 0x8A, 0x00, 0x86, 0x00, 0xA7, 0xFB, 0x48,
+	0x47, 0x95, 0x0D, 0xD9, 0xF8, 0x84, 0x04, 0x39, 0xFD, 0x85, 0x01,
+	0x57, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x5D,
+	0xFF, 0x76, 0x01, 0x59, 0xFD, 0x42, 0x04, 0x63, 0xF9, 0x1C, 0x0C,
+	0xB6, 0x47, 0xA4, 0xFC, 0x07, 0x00, 0xD0, 0x00, 0x20, 0xFF, 0xA0,
+	0x00, 0xB1, 0xFF, 0x13, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF,
+	0xE6, 0x01, 0x3B, 0xFC, 0xDA, 0x06, 0x3F, 0xF3, 0x2C, 0x21, 0x11,
+	0x3D, 0x3A, 0xF3, 0x58, 0x05, 0xAA, 0xFD, 0xE5, 0x00, 0xC1, 0xFF,
+	0x06, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x1F, 0x00, 0x7D, 0xFF, 0x6B,
+	0x01, 0xCF, 0xFC, 0x96, 0x06, 0xB7, 0xF1, 0xC6, 0x35, 0x64, 0x2A,
+	0xD4, 0xF1, 0x2B, 0x07, 0x3D, 0xFC, 0xD2, 0x01, 0x45, 0xFF, 0x32,
+	0x00, 0xFD, 0xFF, 0x0A, 0x00, 0xD9, 0xFF, 0x39, 0x00, 0xF4, 0xFF,
+	0x4E, 0xFF, 0xAC, 0x02, 0x98, 0xF7, 0x65, 0x44, 0xD6, 0x14, 0x6C,
+	0xF6, 0x9F, 0x05, 0xB6, 0xFC, 0xBD, 0x01, 0x42, 0xFF, 0x32, 0x00,
+	0xFF, 0xFF, 0x00, 0x00, 0x21, 0x00, 0x7A, 0xFF, 0x2B, 0x01, 0xFE,
+	0xFD, 0xF8, 0x02, 0xFB, 0xFB, 0x8D, 0x05, 0xE5, 0x48, 0xE3, 0x01,
+	0x91, 0xFD, 0x25, 0x02, 0x6B, 0xFE, 0xF7, 0x00, 0x8F, 0xFF, 0x1C,
+	0x00, 0xFE, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xD5, 0x01, 0x78, 0xFC,
+	0x2F, 0x06, 0x13, 0xF5, 0x7C, 0x19, 0xF7, 0x41, 0x9B, 0xF5, 0xD1,
+	0x03, 0x9F, 0xFE, 0x57, 0x00, 0x08, 0x00, 0xEC, 0xFF, 0x06, 0x00,
+	0xFD, 0xFF, 0x2D, 0x00, 0x55, 0xFF, 0xB5, 0x01, 0x61, 0xFC, 0x16,
+	0x07, 0x85, 0xF1, 0xE6, 0x2E, 0x9E, 0x31, 0x7D, 0xF1, 0xF3, 0x06,
+	0x84, 0xFC, 0x9D, 0x01, 0x63, 0xFF, 0x28, 0x00, 0xFD, 0xFF, 0x04,
+	0x00, 0xF6, 0xFF, 0xEB, 0xFF, 0x91, 0x00, 0x3B, 0xFE, 0x75, 0x04,
+	0x92, 0xF4, 0x36, 0x40, 0x6E, 0x1C, 0x50, 0xF4, 0x7B, 0x06, 0x5B,
+	0xFC, 0xDF, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x18, 0x00,
+	0x9C, 0xFF, 0xD6, 0x00, 0xB1, 0xFE, 0xA1, 0x01, 0x89, 0xFE, 0xC3,
+	0xFF, 0x9C, 0x48, 0xFD, 0x07, 0xFA, 0xFA, 0x7A, 0x03, 0xBC, 0xFD,
+	0x49, 0x01, 0x6E, 0xFF, 0x24, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x30,
+	0x00, 0x49, 0xFF, 0xAA, 0x01, 0xE4, 0xFC, 0x38, 0x05, 0x54, 0xF7,
+	0xFE, 0x11, 0xAA, 0x45, 0x09, 0xF9, 0xE2, 0x01, 0xC4, 0xFF, 0xB3,
+	0xFF, 0x59, 0x00, 0xCD, 0xFF, 0x0D, 0x00, 0xFD, 0xFF, 0x34, 0x00,
+	0x3E, 0xFF, 0xDE, 0x01, 0x33, 0xFC, 0x22, 0x07, 0x2B, 0xF2, 0x80,
+	0x27, 0x3B, 0x38, 0x0A, 0xF2, 0x44, 0x06, 0x0B, 0xFD, 0x45, 0x01,
+	0x90, 0xFF, 0x18, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x0F, 0x00, 0xA9,
+	0xFF, 0x15, 0x01, 0x5B, 0xFD, 0xD0, 0x05, 0x97, 0xF2, 0xE6, 0x3A,
+	0x21, 0x24, 0xB1, 0xF2, 0x04, 0x07, 0x33, 0xFC, 0xE5, 0x01, 0x39,
+	0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x10, 0x00, 0xBE, 0xFF, 0x7F, 0x00,
+	0x65, 0xFF, 0x51, 0x00, 0xEB, 0x00, 0xE1, 0xFA, 0xE1, 0x46, 0xCD,
+	0x0E, 0x6A, 0xF8, 0xB8, 0x04, 0x20, 0xFD, 0x90, 0x01, 0x53, 0xFF,
+	0x2D, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x28, 0x00, 0x62, 0xFF, 0x6A,
+	0x01, 0x74, 0xFD, 0x0A, 0x04, 0xD5, 0xF9, 0xED, 0x0A, 0x03, 0x48,
+	0x7C, 0xFD, 0x9E, 0xFF, 0x0A, 0x01, 0x01, 0xFF, 0xAF, 0x00, 0xAB,
+	0xFF, 0x14, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE5, 0x01,
+	0x42, 0xFC, 0xC3, 0x06, 0x87, 0xF3, 0xD7, 0x1F, 0xFE, 0x3D, 0x91,
+	0xF3, 0x1D, 0x05, 0xD1, 0xFD, 0xCE, 0x00, 0xCC, 0xFF, 0x02, 0x00,
+	0x02, 0x00, 0xFE, 0xFF, 0x22, 0x00, 0x75, 0xFF, 0x7A, 0x01, 0xB8,
+	0xFC, 0xB4, 0x06, 0x9E, 0xF1, 0xA2, 0x34, 0xAD, 0x2B, 0xB6, 0xF1,
+	0x29, 0x07, 0x45, 0xFC, 0xCB, 0x01, 0x49, 0xFF, 0x31, 0x00, 0xFD,
+	0xFF, 0x09, 0x00, 0xDE, 0xFF, 0x2B, 0x00, 0x11, 0x00, 0x1B, 0xFF,
+	0x02, 0x03, 0xFE, 0xF6, 0xC3, 0x43, 0x22, 0x16, 0x07, 0xF6, 0xCA,
+	0x05, 0xA3, 0xFC, 0xC5, 0x01, 0x3F, 0xFF, 0x33, 0x00, 0xFF, 0xFF,
+	0x00, 0x00, 0x20, 0x00, 0x80, 0xFF, 0x1C, 0x01, 0x1C, 0xFE, 0xBD,
+	0x02, 0x6E, 0xFC, 0x7D, 0x04, 0xF3, 0x48, 0xE2, 0x02, 0x1F, 0xFD,
+	0x60, 0x02, 0x4C, 0xFE, 0x06, 0x01, 0x89, 0xFF, 0x1D, 0x00, 0xFE,
+	0xFF, 0x34, 0x00, 0x3C, 0xFF, 0xCF, 0x01, 0x88, 0xFC, 0x09, 0x06,
+	0x71, 0xF5, 0x2B, 0x18, 0xB2, 0x42, 0x20, 0xF6, 0x83, 0x03, 0xCF,
+	0xFE, 0x3C, 0x00, 0x15, 0x00, 0xE6, 0xFF, 0x07, 0x00, 0xFD, 0xFF,
+	0x2E, 0x00, 0x50, 0xFF, 0xBF, 0x01, 0x54, 0xFC, 0x20, 0x07, 0x94,
+	0xF1, 0xA6, 0x2D, 0xD0, 0x32, 0x85, 0xF1, 0xDD, 0x06, 0x96, 0xFC,
+	0x90, 0x01, 0x69, 0xFF, 0x26, 0x00, 0xFD, 0xFF, 0x03, 0x00, 0xFB,
+	0xFF, 0xDF, 0xFF, 0xA9, 0x00, 0x10, 0xFE, 0xB9, 0x04, 0x27, 0xF4,
+	0x5E, 0x3F, 0xC3, 0x1D, 0xFE, 0xF3, 0x99, 0x06, 0x50, 0xFC, 0xE2,
+	0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x17, 0x00, 0xA2, 0xFF,
+	0xC7, 0x00, 0xD0, 0xFE, 0x65, 0x01, 0xF6, 0xFE, 0xD9, 0xFE, 0x6A,
+	0x48, 0x1F, 0x09, 0x87, 0xFA, 0xB3, 0x03, 0xA0, 0xFD, 0x56, 0x01,
+	0x69, 0xFF, 0x26, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2F, 0x00, 0x4D,
+	0xFF, 0xA0, 0x01, 0xFB, 0xFC, 0x07, 0x05, 0xBF, 0xF7, 0xBB, 0x10,
+	0x2B, 0x46, 0xBB, 0xF9, 0x83, 0x01, 0xFA, 0xFF, 0x95, 0xFF, 0x68,
+	0x00, 0xC7, 0xFF, 0x0E, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3C, 0xFF,
+	0xE1, 0x01, 0x31, 0xFC, 0x19, 0x07, 0x5B, 0xF2, 0x30, 0x26, 0x4B,
+	0x39, 0x3B, 0xF2, 0x1A, 0x06, 0x29, 0xFD, 0x33, 0x01, 0x99, 0xFF,
+	0x15, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x13, 0x00, 0x9F, 0xFF, 0x28,
+	0x01, 0x3A, 0xFD, 0x00, 0x06, 0x5A, 0xF2, 0xDF, 0x39, 0x73, 0x25,
+	0x79, 0xF2, 0x12, 0x07, 0x31, 0xFC, 0xE3, 0x01, 0x3B, 0xFF, 0x35,
+	0x00, 0xFD, 0xFF, 0x0F, 0x00, 0xC4, 0xFF, 0x70, 0x00, 0x84, 0xFF,
+	0x19, 0x00, 0x4D, 0x01, 0x22, 0xFA, 0x70, 0x46, 0x0A, 0x10, 0xFC,
+	0xF7, 0xEB, 0x04, 0x08, 0xFD, 0x9A, 0x01, 0x4F, 0xFF, 0x2E, 0x00,
+	0xFF, 0xFF, 0x00, 0x00, 0x27, 0x00, 0x66, 0xFF, 0x5E, 0x01, 0x90,
+	0xFD, 0xD2, 0x03, 0x47, 0xFA, 0xC3, 0x09, 0x48, 0x48, 0x5A, 0xFE,
+	0x33, 0xFF, 0x45, 0x01, 0xE2, 0xFE, 0xBE, 0x00, 0xA5, 0xFF, 0x16,
+	0x00, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE3, 0x01, 0x4B, 0xFC,
+	0xA9, 0x06, 0xD2, 0xF3, 0x81, 0x1E, 0xE4, 0x3E, 0xEF, 0xF3, 0xDE,
+	0x04, 0xF9, 0xFD, 0xB7, 0x00, 0xD8, 0xFF, 0xFD, 0xFF, 0x03, 0x00,
+	0xFD, 0xFF, 0x24, 0x00, 0x6D, 0xFF, 0x88, 0x01, 0xA2, 0xFC, 0xD0,
+	0x06, 0x8C, 0xF1, 0x78, 0x33, 0xF2, 0x2C, 0x9E, 0xF1, 0x24, 0x07,
+	0x4E, 0xFC, 0xC3, 0x01, 0x4E, 0xFF, 0x2F, 0x00, 0xFD, 0xFF, 0x08,
+	0x00, 0xE4, 0xFF, 0x1D, 0x00, 0x2D, 0x00, 0xEA, 0xFE, 0x56, 0x03,
+	0x6D, 0xF6, 0x17, 0x43, 0x70, 0x17, 0xA6, 0xF5, 0xF3, 0x05, 0x91,
+	0xFC, 0xCC, 0x01, 0x3D, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0x1E, 0x00,
+	0x86, 0xFF, 0x0E, 0x01, 0x3B, 0xFE, 0x82, 0x02, 0xE0, 0xFC, 0x73,
+	0x03, 0xF6, 0x48, 0xE9, 0x03, 0xAD, 0xFC, 0x9C, 0x02, 0x2D, 0xFE,
+	0x14, 0x01, 0x83, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x33,
+	0x00, 0x3E, 0xFF, 0xC9, 0x01, 0x99, 0xFC, 0xE1, 0x05, 0xD1, 0xF5,
+	0xDC, 0x16, 0x65, 0x43, 0xAD, 0xF6, 0x31, 0x03, 0x00, 0xFF, 0x20,
+	0x00, 0x23, 0x00, 0xE1, 0xFF, 0x08, 0x00, 0xFD, 0xFF, 0x30, 0x00,
+	0x4C, 0xFF, 0xC7, 0x01, 0x4A, 0xFC, 0x27, 0x07, 0xA8, 0xF1, 0x62,
+	0x2C, 0xFD, 0x33, 0x93, 0xF1, 0xC4, 0x06, 0xAB, 0xFC, 0x82, 0x01,
+	0x71, 0xFF, 0x23, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0xFF, 0xFF, 0xD3,
+	0xFF, 0xC1, 0x00, 0xE7, 0xFD, 0xFA, 0x04, 0xC4, 0xF3, 0x7E, 0x3E,
+	0x19, 0x1F, 0xB0, 0xF3, 0xB5, 0x06, 0x47, 0xFC, 0xE4, 0x01, 0x36,
+	0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x15, 0x00, 0xA8, 0xFF, 0xB8, 0x00,
+	0xF0, 0xFE, 0x2B, 0x01, 0x63, 0xFF, 0xF6, 0xFD, 0x2C, 0x48, 0x47,
+	0x0A, 0x14, 0xFA, 0xEB, 0x03, 0x84, 0xFD, 0x63, 0x01, 0x64, 0xFF,
+	0x27, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2D, 0x00, 0x51, 0xFF, 0x96,
+	0x01, 0x13, 0xFD, 0xD5, 0x04, 0x2C, 0xF8, 0x7D, 0x0F, 0xA3, 0x46,
+	0x76, 0xFA, 0x22, 0x01, 0x32, 0x00, 0x76, 0xFF, 0x76, 0x00, 0xC1,
+	0xFF, 0x0F, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x3A, 0xFF, 0xE4, 0x01,
+	0x32, 0xFC, 0x0C, 0x07, 0x91, 0xF2, 0xDD, 0x24, 0x54, 0x3A, 0x74,
+	0xF2, 0xEB, 0x05, 0x49, 0xFD, 0x20, 0x01, 0xA3, 0xFF, 0x11, 0x00,
+	0x00, 0x00, 0xFF, 0xFF, 0x16, 0x00, 0x95, 0xFF, 0x3B, 0x01, 0x1B,
+	0xFD, 0x2D, 0x06, 0x24, 0xF2, 0xD3, 0x38, 0xC6, 0x26, 0x45, 0xF2,
+	0x1D, 0x07, 0x32, 0xFC, 0xE0, 0x01, 0x3D, 0xFF, 0x35, 0x00, 0xFD,
+	0xFF, 0x0D, 0x00, 0xC9, 0xFF, 0x61, 0x00, 0xA2, 0xFF, 0xE2, 0xFF,
+	0xAE, 0x01, 0x6B, 0xF9, 0xF2, 0x45, 0x4A, 0x11, 0x8F, 0xF7, 0x1D,
+	0x05, 0xF1, 0xFC, 0xA4, 0x01, 0x4B, 0xFF, 0x2F, 0x00, 0xFF, 0xFF,
+	0x00, 0x00, 0x25, 0x00, 0x6C, 0xFF, 0x51, 0x01, 0xAC, 0xFD, 0x9A,
+	0x03, 0xBA, 0xFA, 0x9E, 0x08, 0x81, 0x48, 0x40, 0xFF, 0xC6, 0xFE,
+	0x80, 0x01, 0xC2, 0xFE, 0xCE, 0x00, 0x9F, 0xFF, 0x17, 0x00, 0xFE,
+	0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE1, 0x01, 0x55, 0xFC, 0x8C, 0x06,
+	0x22, 0xF4, 0x2C, 0x1D, 0xC0, 0x3F, 0x55, 0xF4, 0x9B, 0x04, 0x23,
+	0xFE, 0x9F, 0x00, 0xE4, 0xFF, 0xF9, 0xFF, 0x04, 0x00, 0xFD, 0xFF,
+	0x27, 0x00, 0x66, 0xFF, 0x96, 0x01, 0x8E, 0xFC, 0xE7, 0x06, 0x81,
+	0xF1, 0x48, 0x32, 0x34, 0x2E, 0x8D, 0xF1, 0x1C, 0x07, 0x5A, 0xFC,
+	0xBB, 0x01, 0x53, 0xFF, 0x2E, 0x00, 0xFD, 0xFF, 0x07, 0x00, 0xE9,
+	0xFF, 0x0F, 0x00, 0x48, 0x00, 0xB9, 0xFE, 0xA6, 0x03, 0xE4, 0xF5,
+	0x60, 0x42, 0xC1, 0x18, 0x47, 0xF5, 0x1A, 0x06, 0x81, 0xFC, 0xD2,
+	0x01, 0x3B, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1C, 0x00, 0x8B, 0xFF,
+	0xFF, 0x00, 0x5A, 0xFE, 0x46, 0x02, 0x52, 0xFD, 0x70, 0x02, 0xED,
+	0x48, 0xF5, 0x04, 0x3B, 0xFC, 0xD7, 0x02, 0x0F, 0xFE, 0x23, 0x01,
+	0x7E, 0xFF, 0x20, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x40,
+	0xFF, 0xC1, 0x01, 0xAB, 0xFC, 0xB7, 0x05, 0x34, 0xF6, 0x8E, 0x15,
+	0x0B, 0x44, 0x42, 0xF7, 0xDC, 0x02, 0x32, 0xFF, 0x04, 0x00, 0x31,
+	0x00, 0xDC, 0xFF, 0x09, 0x00, 0xFD, 0xFF, 0x31, 0x00, 0x47, 0xFF,
+	0xCE, 0x01, 0x41, 0xFC, 0x2A, 0x07, 0xC2, 0xF1, 0x1B, 0x2B, 0x25,
+	0x35, 0xA8, 0xF1, 0xA7, 0x06, 0xC2, 0xFC, 0x74, 0x01, 0x78, 0xFF,
+	0x20, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0x04, 0x00, 0xC7, 0xFF, 0xD9,
+	0x00, 0xBF, 0xFD, 0x38, 0x05, 0x69, 0xF3, 0x96, 0x3D, 0x6F, 0x20,
+	0x66, 0xF3, 0xCE, 0x06, 0x3F, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36,
+	0x00, 0xFD, 0xFF, 0x14, 0x00, 0xAE, 0xFF, 0xA9, 0x00, 0x0F, 0xFF,
+	0xF0, 0x00, 0xCD, 0xFF, 0x1B, 0xFD, 0xE4, 0x47, 0x73, 0x0B, 0xA2,
+	0xF9, 0x23, 0x04, 0x68, 0xFD, 0x70, 0x01, 0x5F, 0xFF, 0x29, 0x00,
+	0x00, 0x00, 0xFF, 0xFF, 0x2C, 0x00, 0x55, 0xFF, 0x8B, 0x01, 0x2B,
+	0xFD, 0xA1, 0x04, 0x9B, 0xF8, 0x42, 0x0E, 0x0F, 0x47, 0x38, 0xFB,
+	0xBE, 0x00, 0x6A, 0x00, 0x58, 0xFF, 0x85, 0x00, 0xBB, 0xFF, 0x10,
+	0x00, 0xFD, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xE6, 0x01, 0x34, 0xFC,
+	0xFD, 0x06, 0xCB, 0xF2, 0x8A, 0x23, 0x58, 0x3B, 0xB4, 0xF2, 0xBA,
+	0x05, 0x6A, 0xFD, 0x0B, 0x01, 0xAE, 0xFF, 0x0D, 0x00, 0x00, 0x00,
+	0xFF, 0xFF, 0x19, 0x00, 0x8C, 0xFF, 0x4D, 0x01, 0xFE, 0xFC, 0x56,
+	0x06, 0xF7, 0xF1, 0xBF, 0x37, 0x15, 0x28, 0x18, 0xF2, 0x25, 0x07,
+	0x34, 0xFC, 0xDC, 0x01, 0x3F, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x0C,
+	0x00, 0xCF, 0xFF, 0x52, 0x00, 0xC0, 0xFF, 0xAC, 0xFF, 0x0C, 0x02,
+	0xBC, 0xF8, 0x6D, 0x45, 0x8E, 0x12, 0x24, 0xF7, 0x4D, 0x05, 0xDB,
+	0xFC, 0xAE, 0x01, 0x48, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0x00, 0x00,
+	0x24, 0x00, 0x71, 0xFF, 0x43, 0x01, 0xC9, 0xFD, 0x60, 0x03, 0x2E,
+	0xFB, 0x7E, 0x07, 0xAF, 0x48, 0x2D, 0x00, 0x58, 0xFE, 0xBB, 0x01,
+	0xA3, 0xFE, 0xDD, 0x00, 0x99, 0xFF, 0x19, 0x00, 0xFE, 0xFF, 0x36,
+	0x00, 0x37, 0xFF, 0xDD, 0x01, 0x60, 0xFC, 0x6D, 0x06, 0x76, 0xF4,
+	0xD8, 0x1B, 0x95, 0x40, 0xC3, 0xF4, 0x56, 0x04, 0x4E, 0xFE, 0x85,
+	0x00, 0xF1, 0xFF, 0xF4, 0xFF, 0x04, 0x00, 0xFD, 0xFF, 0x29, 0x00,
+	0x60, 0xFF, 0xA2, 0x01, 0x7C, 0xFC, 0xFB, 0x06, 0x7C, 0xF1, 0x15,
+	0x31, 0x73, 0x2F, 0x81, 0xF1, 0x10, 0x07, 0x67, 0xFC, 0xB1, 0x01,
+	0x58, 0xFF, 0x2C, 0x00, 0xFD, 0xFF, 0x06, 0x00, 0xEE, 0xFF, 0x02,
+	0x00, 0x63, 0x00, 0x8A, 0xFE, 0xF3, 0x03, 0x63, 0xF5, 0xA1, 0x41,
+	0x12, 0x1A, 0xEB, 0xF4, 0x3F, 0x06, 0x72, 0xFC, 0xD7, 0x01, 0x39,
+	0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1B, 0x00, 0x91, 0xFF, 0xF1, 0x00,
+	0x79, 0xFE, 0x0A, 0x02, 0xC3, 0xFD, 0x73, 0x01, 0xDB, 0x48, 0x07,
+	0x06, 0xC7, 0xFB, 0x12, 0x03, 0xF1, 0xFD, 0x31, 0x01, 0x78, 0xFF,
+	0x22, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x32, 0x00, 0x43, 0xFF, 0xBA,
+	0x01, 0xBF, 0xFC, 0x8B, 0x05, 0x99, 0xF6, 0x43, 0x14, 0xA9, 0x44,
+	0xDE, 0xF7, 0x85, 0x02, 0x65, 0xFF, 0xE7, 0xFF, 0x3F, 0x00, 0xD6,
+	0xFF, 0x0A, 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x44, 0xFF, 0xD5, 0x01,
+	0x3A, 0xFC, 0x2A, 0x07, 0xE3, 0xF1, 0xD1, 0x29, 0x46, 0x36, 0xC5,
+	0xF1, 0x87, 0x06, 0xDA, 0xFC, 0x64, 0x01, 0x80, 0xFF, 0x1E, 0x00,
+	0xFE, 0xFF, 0x01, 0x00, 0x08, 0x00, 0xBC, 0xFF, 0xEF, 0x00, 0x9A,
+	0xFD, 0x72, 0x05, 0x16, 0xF3, 0xA5, 0x3C, 0xC4, 0x21, 0x21, 0xF3,
+	0xE4, 0x06, 0x39, 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD,
+	0xFF, 0x12, 0x00, 0xB3, 0xFF, 0x99, 0x00, 0x2E, 0xFF, 0xB6, 0x00,
+	0x36, 0x00, 0x47, 0xFC, 0x90, 0x47, 0xA4, 0x0C, 0x31, 0xF9, 0x5A,
+	0x04, 0x4E, 0xFD, 0x7C, 0x01, 0x5B, 0xFF, 0x2A, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x2B, 0x00, 0x59, 0xFF, 0x80, 0x01, 0x45, 0xFD, 0x6C,
+	0x04, 0x0B, 0xF9, 0x0B, 0x0D, 0x73, 0x47, 0x02, 0xFC, 0x58, 0x00,
+	0xA3, 0x00, 0x39, 0xFF, 0x94, 0x00, 0xB5, 0xFF, 0x12, 0x00, 0xFD,
+	0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x37, 0xFC, 0xEB, 0x06,
+	0x0B, 0xF3, 0x35, 0x22, 0x52, 0x3C, 0xFD, 0xF2, 0x84, 0x05, 0x8D,
+	0xFD, 0xF6, 0x00, 0xB8, 0xFF, 0x09, 0x00, 0x01, 0x00, 0xFE, 0xFF,
+	0x1D, 0x00, 0x83, 0xFF, 0x5E, 0x01, 0xE3, 0xFC, 0x7B, 0x06, 0xD0,
+	0xF1, 0xA5, 0x36, 0x62, 0x29, 0xEF, 0xF1, 0x29, 0x07, 0x39, 0xFC,
+	0xD7, 0x01, 0x42, 0xFF, 0x33, 0x00, 0xFD, 0xFF, 0x0B, 0x00, 0xD5,
+	0xFF, 0x44, 0x00, 0xDD, 0xFF, 0x77, 0xFF, 0x67, 0x02, 0x14, 0xF8,
+	0xDC, 0x44, 0xD5, 0x13, 0xBC, 0xF6, 0x7C, 0x05, 0xC5, 0xFC, 0xB7,
+	0x01, 0x44, 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x22, 0x00,
+	0x76, 0xFF, 0x35, 0x01, 0xE7, 0xFD, 0x26, 0x03, 0xA1, 0xFB, 0x64,
+	0x06, 0xD2, 0x48, 0x21, 0x01, 0xE8, 0xFD, 0xF7, 0x01, 0x83, 0xFE,
+	0xEC, 0x00, 0x93, 0xFF, 0x1A, 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x39,
+	0xFF, 0xD9, 0x01, 0x6D, 0xFC, 0x4B, 0x06, 0xCD, 0xF4, 0x83, 0x1A,
+	0x5F, 0x41, 0x3A, 0xF5, 0x0C, 0x04, 0x7B, 0xFE, 0x6C, 0x00, 0xFE,
+	0xFF, 0xEF, 0xFF, 0x05, 0x00, 0xFD, 0xFF, 0x2B, 0x00, 0x5A, 0xFF,
+	0xAD, 0x01, 0x6C, 0xFC, 0x0C, 0x07, 0x7F, 0xF1, 0xDC, 0x2F, 0xAD,
+	0x30, 0x7D, 0xF1, 0x01, 0x07, 0x76, 0xFC, 0xA6, 0x01, 0x5E, 0xFF,
+	0x2A, 0x00, 0xFD, 0xFF, 0x05, 0x00, 0xF3, 0xFF, 0xF5, 0xFF, 0x7D,
+	0x00, 0x5D, 0xFE, 0x3E, 0x04, 0xEA, 0xF4, 0xD9, 0x40, 0x66, 0x1B,
+	0x93, 0xF4, 0x62, 0x06, 0x64, 0xFC, 0xDC, 0x01, 0x38, 0xFF, 0x36,
+	0x00, 0xFE, 0xFF, 0x19, 0x00, 0x97, 0xFF, 0xE2, 0x00, 0x98, 0xFE,
+	0xCF, 0x01, 0x33, 0xFE, 0x7D, 0x00, 0xBB, 0x48, 0x1F, 0x07, 0x54,
+	0xFB, 0x4C, 0x03, 0xD3, 0xFD, 0x3F, 0x01, 0x73, 0xFF, 0x23, 0x00,
+	0x00, 0x00, 0xFF, 0xFF, 0x31, 0x00, 0x46, 0xFF, 0xB1, 0x01, 0xD3,
+	0xFC, 0x5D, 0x05, 0x01, 0xF7, 0xFB, 0x12, 0x3F, 0x45, 0x83, 0xF8,
+	0x2A, 0x02, 0x9A, 0xFF, 0xCA, 0xFF, 0x4E, 0x00, 0xD1, 0xFF, 0x0C,
+	0x00, 0xFD, 0xFF, 0x34, 0x00, 0x40, 0xFF, 0xDA, 0x01, 0x35, 0xFC,
+	0x27, 0x07, 0x09, 0xF2, 0x85, 0x28, 0x63, 0x37, 0xE9, 0xF1, 0x63,
+	0x06, 0xF5, 0xFC, 0x53, 0x01, 0x89, 0xFF, 0x1A, 0x00, 0xFE, 0xFF,
+	0x00, 0x00, 0x0C, 0x00, 0xB1, 0xFF, 0x04, 0x01, 0x76, 0xFD, 0xA8,
+	0x05, 0xCC, 0xF2, 0xAB, 0x3B, 0x18, 0x23, 0xE0, 0xF2, 0xF7, 0x06,
+	0x35, 0xFC, 0xE6, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x11,
+	0x00, 0xB9, 0xFF, 0x8A, 0x00, 0x4D, 0xFF, 0x7D, 0x00, 0x9C, 0x00,
+	0x7B, 0xFB, 0x31, 0x47, 0xD9, 0x0D, 0xC0, 0xF8, 0x8F, 0x04, 0x34,
+	0xFD, 0x87, 0x01, 0x56, 0xFF, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x29, 0x00, 0x5E, 0xFF, 0x74, 0x01, 0x5F, 0xFD, 0x35, 0x04, 0x7C,
+	0xF9, 0xD8, 0x0B, 0xC9, 0x47, 0xD4, 0xFC, 0xF0, 0xFF, 0xDD, 0x00,
+	0x19, 0xFF, 0xA4, 0x00, 0xAF, 0xFF, 0x13, 0x00, 0xFD, 0xFF, 0x36,
+	0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3D, 0xFC, 0xD5, 0x06, 0x4F, 0xF3,
+	0xE0, 0x20, 0x45, 0x3D, 0x4D, 0xF3, 0x4B, 0x05, 0xB3, 0xFD, 0xE0,
+	0x00, 0xC3, 0xFF, 0x05, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x20, 0x00,
+	0x7B, 0xFF, 0x6E, 0x01, 0xCA, 0xFC, 0x9D, 0x06, 0xB1, 0xF1, 0x86,
+	0x35, 0xAE, 0x2A, 0xCD, 0xF1, 0x2B, 0x07, 0x3F, 0xFC, 0xD1, 0x01,
+	0x46, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x0A, 0x00, 0xDA, 0xFF, 0x36,
+	0x00, 0xFA, 0xFF, 0x43, 0xFF, 0xBF, 0x02, 0x75, 0xF7, 0x42, 0x44,
+	0x20, 0x15, 0x55, 0xF6, 0xA9, 0x05, 0xB2, 0xFC, 0xBF, 0x01, 0x41,
+	0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x21, 0x00, 0x7C, 0xFF,
+	0x27, 0x01, 0x05, 0xFE, 0xEB, 0x02, 0x14, 0xFC, 0x50, 0x05, 0xEA,
+	0x48, 0x1B, 0x02, 0x78, 0xFD, 0x32, 0x02, 0x64, 0xFE, 0xFA, 0x00,
+	0x8D, 0xFF, 0x1C, 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xD4,
+	0x01, 0x7C, 0xFC, 0x27, 0x06, 0x28, 0xF5, 0x31, 0x19, 0x21, 0x42,
+	0xB8, 0xF5, 0xC0, 0x03, 0xAA, 0xFE, 0x51, 0x00, 0x0B, 0x00, 0xEA,
+	0xFF, 0x06, 0x00, 0xFD, 0xFF, 0x2D, 0x00, 0x54, 0xFF, 0xB7, 0x01,
+	0x5E, 0xFC, 0x19, 0x07, 0x88, 0xF1, 0x9F, 0x2E, 0xE3, 0x31, 0x7E,
+	0xF1, 0xEE, 0x06, 0x88, 0xFC, 0x9A, 0x01, 0x64, 0xFF, 0x28, 0x00,
+	0xFD, 0xFF, 0x04, 0x00, 0xF7, 0xFF, 0xE8, 0xFF, 0x96, 0x00, 0x31,
+	0xFE, 0x84, 0x04, 0x79, 0xF4, 0x07, 0x40, 0xBA, 0x1C, 0x3E, 0xF4,
+	0x82, 0x06, 0x58, 0xFC, 0xE0, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE,
+	0xFF, 0x18, 0x00, 0x9D, 0xFF, 0xD3, 0x00, 0xB8, 0xFE, 0x93, 0x01,
+	0xA1, 0xFE, 0x8E, 0xFF, 0x92, 0x48, 0x3D, 0x08, 0xE1, 0xFA, 0x86,
+	0x03, 0xB6, 0xFD, 0x4C, 0x01, 0x6D, 0xFF, 0x25, 0x00, 0x00, 0x00,
+	0xFF, 0xFF, 0x30, 0x00, 0x4A, 0xFF, 0xA8, 0x01, 0xE9, 0xFC, 0x2D,
+	0x05, 0x6B, 0xF7, 0xB6, 0x11, 0xC8, 0x45, 0x30, 0xF9, 0xCD, 0x01,
+	0xD0, 0xFF, 0xAC, 0xFF, 0x5C, 0x00, 0xCB, 0xFF, 0x0D, 0x00, 0xFD,
+	0xFF, 0x34, 0x00, 0x3E, 0xFF, 0xDF, 0x01, 0x33, 0xFC, 0x20, 0x07,
+	0x35, 0xF2, 0x36, 0x27, 0x78, 0x38, 0x14, 0xF2, 0x3B, 0x06, 0x11,
+	0xFD, 0x41, 0x01, 0x92, 0xFF, 0x17, 0x00, 0xFF, 0xFF, 0x00, 0x00,
+	0x10, 0x00, 0xA7, 0xFF, 0x19, 0x01, 0x53, 0xFD, 0xDB, 0x05, 0x88,
+	0xF2, 0xAD, 0x3A, 0x6D, 0x24, 0xA4, 0xF2, 0x08, 0x07, 0x32, 0xFC,
+	0xE5, 0x01, 0x39, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x10, 0x00, 0xBF,
+	0xFF, 0x7B, 0x00, 0x6C, 0xFF, 0x44, 0x00, 0x01, 0x01, 0xB6, 0xFA,
+	0xC8, 0x46, 0x13, 0x0F, 0x51, 0xF8, 0xC4, 0x04, 0x1B, 0xFD, 0x92,
+	0x01, 0x52, 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x28, 0x00,
+	0x63, 0xFF, 0x67, 0x01, 0x7A, 0xFD, 0xFE, 0x03, 0xEE, 0xF9, 0xAA,
+	0x0A, 0x16, 0x48, 0xAC, 0xFD, 0x86, 0xFF, 0x17, 0x01, 0xFA, 0xFE,
+	0xB3, 0x00, 0xAA, 0xFF, 0x15, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36,
+	0xFF, 0xE5, 0x01, 0x44, 0xFC, 0xBD, 0x06, 0x97, 0xF3, 0x8A, 0x1F,
+	0x31, 0x3E, 0xA5, 0xF3, 0x0F, 0x05, 0xDA, 0xFD, 0xC9, 0x00, 0xCF,
+	0xFF, 0x01, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x22, 0x00, 0x73, 0xFF,
+	0x7D, 0x01, 0xB3, 0xFC, 0xBB, 0x06, 0x9A, 0xF1, 0x60, 0x34, 0xF5,
+	0x2B, 0xB0, 0xF1, 0x28, 0x07, 0x47, 0xFC, 0xCA, 0x01, 0x4A, 0xFF,
+	0x30, 0x00, 0xFD, 0xFF, 0x09, 0x00, 0xDF, 0xFF, 0x28, 0x00, 0x17,
+	0x00, 0x10, 0xFF, 0x15, 0x03, 0xDD, 0xF6, 0x9E, 0x43, 0x6C, 0x16,
+	0xF1, 0xF5, 0xD3, 0x05, 0x9F, 0xFC, 0xC6, 0x01, 0x3F, 0xFF, 0x33,
+	0x00, 0xFF, 0xFF, 0x00, 0x00, 0x1F, 0x00, 0x81, 0xFF, 0x19, 0x01,
+	0x23, 0xFE, 0xB0, 0x02, 0x87, 0xFC, 0x41, 0x04, 0xF4, 0x48, 0x1C,
+	0x03, 0x06, 0xFD, 0x6E, 0x02, 0x45, 0xFE, 0x09, 0x01, 0x88, 0xFF,
+	0x1D, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3C, 0xFF, 0xCE, 0x01, 0x8C,
+	0xFC, 0x00, 0x06, 0x86, 0xF5, 0xE0, 0x17, 0xDB, 0x42, 0x3F, 0xF6,
+	0x71, 0x03, 0xD9, 0xFE, 0x36, 0x00, 0x18, 0x00, 0xE5, 0xFF, 0x07,
+	0x00, 0xFD, 0xFF, 0x2F, 0x00, 0x4F, 0xFF, 0xC1, 0x01, 0x52, 0xFC,
+	0x22, 0x07, 0x98, 0xF1, 0x5E, 0x2D, 0x13, 0x33, 0x87, 0xF1, 0xD8,
+	0x06, 0x9B, 0xFC, 0x8D, 0x01, 0x6B, 0xFF, 0x25, 0x00, 0xFD, 0xFF,
+	0x03, 0x00, 0xFC, 0xFF, 0xDC, 0xFF, 0xAF, 0x00, 0x07, 0xFE, 0xC8,
+	0x04, 0x10, 0xF4, 0x2D, 0x3F, 0x0F, 0x1E, 0xED, 0xF3, 0xA0, 0x06,
+	0x4E, 0xFC, 0xE3, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x16,
+	0x00, 0xA3, 0xFF, 0xC3, 0x00, 0xD7, 0xFE, 0x58, 0x01, 0x0F, 0xFF,
+	0xA6, 0xFE, 0x5D, 0x48, 0x61, 0x09, 0x6E, 0xFA, 0xC0, 0x03, 0x99,
+	0xFD, 0x59, 0x01, 0x68, 0xFF, 0x26, 0x00, 0x00, 0x00, 0xFF, 0xFF,
+	0x2E, 0x00, 0x4E, 0xFF, 0x9E, 0x01, 0x00, 0xFD, 0xFC, 0x04, 0xD7,
+	0xF7, 0x75, 0x10, 0x48, 0x46, 0xE4, 0xF9, 0x6E, 0x01, 0x06, 0x00,
+	0x8E, 0xFF, 0x6B, 0x00, 0xC6, 0xFF, 0x0E, 0x00, 0xFD, 0xFF, 0x35,
+	0x00, 0x3B, 0xFF, 0xE2, 0x01, 0x31, 0xFC, 0x16, 0x07, 0x67, 0xF2,
+	0xE5, 0x25, 0x87, 0x39, 0x47, 0xF2, 0x10, 0x06, 0x30, 0xFD, 0x2F,
+	0x01, 0x9C, 0xFF, 0x14, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x13, 0x00,
+	0x9D, 0xFF, 0x2D, 0x01, 0x33, 0xFD, 0x0B, 0x06, 0x4D, 0xF2, 0xA5,
+	0x39, 0xBF, 0x25, 0x6D, 0xF2, 0x15, 0x07, 0x31, 0xFC, 0xE2, 0x01,
+	0x3B, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x0E, 0x00, 0xC5, 0xFF, 0x6D,
+	0x00, 0x8B, 0xFF, 0x0D, 0x00, 0x63, 0x01, 0xF9, 0xF9, 0x55, 0x46,
+	0x51, 0x10, 0xE3, 0xF7, 0xF7, 0x04, 0x03, 0xFD, 0x9D, 0x01, 0x4E,
+	0xFF, 0x2E, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x26, 0x00, 0x68, 0xFF,
+	0x5B, 0x01, 0x96, 0xFD, 0xC6, 0x03, 0x61, 0xFA, 0x81, 0x09, 0x57,
+	0x48, 0x8D, 0xFE, 0x1B, 0xFF, 0x52, 0x01, 0xDB, 0xFE, 0xC2, 0x00,
+	0xA4, 0xFF, 0x16, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE3,
+	0x01, 0x4D, 0xFC, 0xA3, 0x06, 0xE4, 0xF3, 0x36, 0x1E, 0x16, 0x3F,
+	0x05, 0xF4, 0xCF, 0x04, 0x02, 0xFE, 0xB2, 0x00, 0xDB, 0xFF, 0xFC,
+	0xFF, 0x03, 0x00, 0xFD, 0xFF, 0x25, 0x00, 0x6C, 0xFF, 0x8B, 0x01,
+	0x9D, 0xFC, 0xD5, 0x06, 0x89, 0xF1, 0x35, 0x33, 0x3A, 0x2D, 0x9A,
+	0xF1, 0x23, 0x07, 0x51, 0xFC, 0xC2, 0x01, 0x4F, 0xFF, 0x2F, 0x00,
+	0xFD, 0xFF, 0x07, 0x00, 0xE5, 0xFF, 0x1A, 0x00, 0x33, 0x00, 0xDF,
+	0xFE, 0x68, 0x03, 0x4E, 0xF6, 0xEE, 0x42, 0xBB, 0x17, 0x90, 0xF5,
+	0xFC, 0x05, 0x8E, 0xFC, 0xCD, 0x01, 0x3C, 0xFF, 0x34, 0x00, 0xFE,
+	0xFF, 0x1E, 0x00, 0x87, 0xFF, 0x0B, 0x01, 0x42, 0xFE, 0x74, 0x02,
+	0xF9, 0xFC, 0x39, 0x03, 0xF5, 0x48, 0x24, 0x04, 0x94, 0xFC, 0xA9,
+	0x02, 0x27, 0xFE, 0x18, 0x01, 0x82, 0xFF, 0x1F, 0x00, 0x00, 0x00,
+	0xFF, 0xFF, 0x33, 0x00, 0x3E, 0xFF, 0xC7, 0x01, 0x9D, 0xFC, 0xD8,
+	0x05, 0xE7, 0xF5, 0x91, 0x16, 0x89, 0x43, 0xCD, 0xF6, 0x1E, 0x03,
+	0x0B, 0xFF, 0x1A, 0x00, 0x26, 0x00, 0xE0, 0xFF, 0x08, 0x00, 0xFD,
+	0xFF, 0x30, 0x00, 0x4B, 0xFF, 0xC9, 0x01, 0x48, 0xFC, 0x28, 0x07,
+	0xAD, 0xF1, 0x19, 0x2C, 0x3F, 0x34, 0x97, 0xF1, 0xBE, 0x06, 0xB0,
+	0xFC, 0x7F, 0x01, 0x72, 0xFF, 0x23, 0x00, 0xFE, 0xFF, 0x02, 0x00,
+	0x00, 0x00, 0xD0, 0xFF, 0xC7, 0x00, 0xDE, 0xFD, 0x08, 0x05, 0xB0,
+	0xF3, 0x4A, 0x3E, 0x64, 0x1F, 0xA0, 0xF3, 0xBB, 0x06, 0x45, 0xFC,
+	0xE5, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x15, 0x00, 0xA9,
+	0xFF, 0xB4, 0x00, 0xF7, 0xFE, 0x1D, 0x01, 0x7A, 0xFF, 0xC5, 0xFD,
+	0x1D, 0x48, 0x89, 0x0A, 0xFB, 0xF9, 0xF8, 0x03, 0x7D, 0xFD, 0x66,
+	0x01, 0x63, 0xFF, 0x28, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2D, 0x00,
+	0x52, 0xFF, 0x93, 0x01, 0x18, 0xFD, 0xC9, 0x04, 0x45, 0xF8, 0x36,
+	0x0F, 0xBB, 0x46, 0xA1, 0xFA, 0x0C, 0x01, 0x3E, 0x00, 0x70, 0xFF,
+	0x7A, 0x00, 0xC0, 0xFF, 0x0F, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x39,
+	0xFF, 0xE4, 0x01, 0x32, 0xFC, 0x09, 0x07, 0x9D, 0xF2, 0x92, 0x24,
+	0x8F, 0x3A, 0x82, 0xF2, 0xE1, 0x05, 0x50, 0xFD, 0x1B, 0x01, 0xA6,
+	0xFF, 0x10, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x17, 0x00, 0x93, 0xFF,
+	0x3F, 0x01, 0x15, 0xFD, 0x36, 0x06, 0x19, 0xF2, 0x97, 0x38, 0x11,
+	0x27, 0x3B, 0xF2, 0x1F, 0x07, 0x32, 0xFC, 0xDF, 0x01, 0x3D, 0xFF,
+	0x34, 0x00, 0xFD, 0xFF, 0x0D, 0x00, 0xCB, 0xFF, 0x5E, 0x00, 0xA9,
+	0xFF, 0xD6, 0xFF, 0xC3, 0x01, 0x43, 0xF9, 0xD7, 0x45, 0x92, 0x11,
+	0x77, 0xF7, 0x28, 0x05, 0xEC, 0xFC, 0xA7, 0x01, 0x4A, 0xFF, 0x2F,
+	0x00, 0xFF, 0xFF, 0x00, 0x00, 0x25, 0x00, 0x6D, 0xFF, 0x4E, 0x01,
+	0xB3, 0xFD, 0x8D, 0x03, 0xD4, 0xFA, 0x5D, 0x08, 0x8D, 0x48, 0x74,
+	0xFF, 0xAE, 0xFE, 0x8D, 0x01, 0xBB, 0xFE, 0xD1, 0x00, 0x9E, 0xFF,
+	0x18, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE0, 0x01, 0x57,
+	0xFC, 0x85, 0x06, 0x34, 0xF4, 0xE0, 0x1C, 0xF0, 0x3F, 0x6D, 0xF4,
+	0x8C, 0x04, 0x2C, 0xFE, 0x99, 0x00, 0xE7, 0xFF, 0xF8, 0xFF, 0x04,
+	0x00, 0xFD, 0xFF, 0x27, 0x00, 0x65, 0xFF, 0x98, 0x01, 0x8A, 0xFC,
+	0xEC, 0x06, 0x7F, 0xF1, 0x04, 0x32, 0x7B, 0x2E, 0x8A, 0xF1, 0x1A,
+	0x07, 0x5D, 0xFC, 0xB8, 0x01, 0x54, 0xFF, 0x2D, 0x00, 0xFD, 0xFF,
+	0x06, 0x00, 0xEA, 0xFF, 0x0C, 0x00, 0x4E, 0x00, 0xAF, 0xFE, 0xB8,
+	0x03, 0xC7, 0xF5, 0x38, 0x42, 0x0C, 0x19, 0x32, 0xF5, 0x23, 0x06,
+	0x7D, 0xFC, 0xD3, 0x01, 0x3A, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1C,
+	0x00, 0x8D, 0xFF, 0xFC, 0x00, 0x61, 0xFE, 0x39, 0x02, 0x6B, 0xFD,
+	0x37, 0x02, 0xEB, 0x48, 0x31, 0x05, 0x21, 0xFC, 0xE4, 0x02, 0x08,
+	0xFE, 0x26, 0x01, 0x7C, 0xFF, 0x21, 0x00, 0x00, 0x00, 0xFF, 0xFF,
+	0x32, 0x00, 0x41, 0xFF, 0xC0, 0x01, 0xAF, 0xFC, 0xAD, 0x05, 0x4A,
+	0xF6, 0x44, 0x15, 0x2F, 0x44, 0x64, 0xF7, 0xC9, 0x02, 0x3D, 0xFF,
+	0xFE, 0xFF, 0x34, 0x00, 0xDB, 0xFF, 0x09, 0x00, 0xFD, 0xFF, 0x32,
+	0x00, 0x47, 0xFF, 0xD0, 0x01, 0x40, 0xFC, 0x2A, 0x07, 0xCA, 0xF1,
+	0xD1, 0x2A, 0x65, 0x35, 0xAE, 0xF1, 0xA0, 0x06, 0xC7, 0xFC, 0x70,
+	0x01, 0x7A, 0xFF, 0x20, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0x05, 0x00,
+	0xC5, 0xFF, 0xDE, 0x00, 0xB7, 0xFD, 0x45, 0x05, 0x56, 0xF3, 0x61,
+	0x3D, 0xBA, 0x20, 0x56, 0xF3, 0xD3, 0x06, 0x3E, 0xFC, 0xE6, 0x01,
+	0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x13, 0x00, 0xAF, 0xFF, 0xA5,
+	0x00, 0x16, 0xFF, 0xE3, 0x00, 0xE4, 0xFF, 0xEB, 0xFC, 0xD2, 0x47,
+	0xB6, 0x0B, 0x89, 0xF9, 0x2F, 0x04, 0x62, 0xFD, 0x72, 0x01, 0x5E,
+	0xFF, 0x29, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2C, 0x00, 0x56, 0xFF,
+	0x88, 0x01, 0x31, 0xFD, 0x95, 0x04, 0xB4, 0xF8, 0xFC, 0x0D, 0x26,
+	0x47, 0x64, 0xFB, 0xA7, 0x00, 0x77, 0x00, 0x51, 0xFF, 0x89, 0x00,
+	0xBA, 0xFF, 0x11, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xE6,
+	0x01, 0x34, 0xFC, 0xF9, 0x06, 0xD9, 0xF2, 0x3F, 0x23, 0x90, 0x3B,
+	0xC4, 0xF2, 0xAE, 0x05, 0x72, 0xFD, 0x07, 0x01, 0xB0, 0xFF, 0x0C,
+	0x00, 0x00, 0x00, 0xFF, 0xFF, 0x1A, 0x00, 0x8A, 0xFF, 0x51, 0x01,
+	0xF8, 0xFC, 0x5E, 0x06, 0xED, 0xF1, 0x82, 0x37, 0x60, 0x28, 0x0E,
+	0xF2, 0x26, 0x07, 0x35, 0xFC, 0xDB, 0x01, 0x40, 0xFF, 0x34, 0x00,
+	0xFD, 0xFF, 0x0C, 0x00, 0xD0, 0xFF, 0x4F, 0x00, 0xC7, 0xFF, 0xA0,
+	0xFF, 0x20, 0x02, 0x96, 0xF8, 0x4E, 0x45, 0xD7, 0x12, 0x0D, 0xF7,
+	0x58, 0x05, 0xD6, 0xFC, 0xB0, 0x01, 0x47, 0xFF, 0x30, 0x00, 0xFF,
+	0xFF, 0x00, 0x00, 0x23, 0x00, 0x72, 0xFF, 0x40, 0x01, 0xD0, 0xFD,
+	0x53, 0x03, 0x47, 0xFB, 0x3F, 0x07, 0xB8, 0x48, 0x62, 0x00, 0x3F,
+	0xFE, 0xC8, 0x01, 0x9C, 0xFE, 0xE0, 0x00, 0x98, 0xFF, 0x19, 0x00,
+	0xFE, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xDC, 0x01, 0x63, 0xFC, 0x66,
+	0x06, 0x89, 0xF4, 0x8C, 0x1B, 0xC3, 0x40, 0xDD, 0xF4, 0x46, 0x04,
+	0x58, 0xFE, 0x80, 0x00, 0xF4, 0xFF, 0xF3, 0xFF, 0x05, 0x00, 0xFD,
+	0xFF, 0x29, 0x00, 0x5F, 0xFF, 0xA5, 0x01, 0x78, 0xFC, 0xFF, 0x06,
+	0x7D, 0xF1, 0xCF, 0x30, 0xB8, 0x2F, 0x80, 0xF1, 0x0D, 0x07, 0x6A,
+	0xFC, 0xAE, 0x01, 0x59, 0xFF, 0x2B, 0x00, 0xFD, 0xFF, 0x05, 0x00,
+	0xEF, 0xFF, 0xFF, 0xFF, 0x69, 0x00, 0x80, 0xFE, 0x04, 0x04, 0x48,
+	0xF5, 0x74, 0x41, 0x5D, 0x1A, 0xD7, 0xF4, 0x47, 0x06, 0x6F, 0xFC,
+	0xD8, 0x01, 0x39, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1B, 0x00, 0x93,
+	0xFF, 0xED, 0x00, 0x80, 0xFE, 0xFD, 0x01, 0xDC, 0xFD, 0x3C, 0x01,
+	0xD5, 0x48, 0x45, 0x06, 0xAE, 0xFB, 0x1F, 0x03, 0xEA, 0xFD, 0x34,
+	0x01, 0x77, 0xFF, 0x22, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x31, 0x00,
+	0x44, 0xFF, 0xB8, 0x01, 0xC3, 0xFC, 0x81, 0x05, 0xB0, 0xF6, 0xFA,
+	0x13, 0xCC, 0x44, 0x02, 0xF8, 0x71, 0x02, 0x71, 0xFF, 0xE1, 0xFF,
+	0x42, 0x00, 0xD5, 0xFF, 0x0B, 0x00, 0xFD, 0xFF, 0x33, 0x00, 0x43,
+	0xFF, 0xD6, 0x01, 0x39, 0xFC, 0x2A, 0x07, 0xEB, 0xF1, 0x87, 0x29,
+	0x85, 0x36, 0xCC, 0xF1, 0x7F, 0x06, 0xE0, 0xFC, 0x60, 0x01, 0x82,
+	0xFF, 0x1D, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x09, 0x00, 0xBA, 0xFF,
+	0xF4, 0x00, 0x91, 0xFD, 0x7E, 0x05, 0x05, 0xF3, 0x6E, 0x3C, 0x10,
+	0x22, 0x12, 0xF3, 0xE9, 0x06, 0x38, 0xFC, 0xE6, 0x01, 0x37, 0xFF,
+	0x36, 0x00, 0xFD, 0xFF, 0x12, 0x00, 0xB5, 0xFF, 0x96, 0x00, 0x35,
+	0xFF, 0xA9, 0x00, 0x4D, 0x00, 0x19, 0xFC, 0x7C, 0x47, 0xE8, 0x0C,
+	0x18, 0xF9, 0x66, 0x04, 0x48, 0xFD, 0x7E, 0x01, 0x5A, 0xFF, 0x2B,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x5A, 0xFF, 0x7D, 0x01,
+	0x4B, 0xFD, 0x60, 0x04, 0x24, 0xF9, 0xC6, 0x0C, 0x86, 0x47, 0x30,
+	0xFC, 0x41, 0x00, 0xB0, 0x00, 0x32, 0xFF, 0x98, 0x00, 0xB4, 0xFF,
+	0x12, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x38,
+	0xFC, 0xE6, 0x06, 0x19, 0xF3, 0xEA, 0x21, 0x8A, 0x3C, 0x0E, 0xF3,
+	0x78, 0x05, 0x96, 0xFD, 0xF1, 0x00, 0xBB, 0xFF, 0x08, 0x00, 0x01,
+	0x00, 0xFE, 0xFF, 0x1D, 0x00, 0x81, 0xFF, 0x62, 0x01, 0xDD, 0xFC,
+	0x83, 0x06, 0xC9, 0xF1, 0x66, 0x36, 0xAC, 0x29, 0xE7, 0xF1, 0x2A,
+	0x07, 0x3A, 0xFC, 0xD5, 0x01, 0x43, 0xFF, 0x33, 0x00, 0xFD, 0xFF,
+	0x0B, 0x00, 0xD6, 0xFF, 0x41, 0x00, 0xE4, 0xFF, 0x6B, 0xFF, 0x7B,
+	0x02, 0xF0, 0xF7, 0xBA, 0x44, 0x1E, 0x14, 0xA5, 0xF6, 0x86, 0x05,
+	0xC1, 0xFC, 0xB9, 0x01, 0x44, 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x00,
+	0x00, 0x22, 0x00, 0x77, 0xFF, 0x32, 0x01, 0xED, 0xFD, 0x19, 0x03,
+	0xBB, 0xFB, 0x26, 0x06, 0xD7, 0x48, 0x58, 0x01, 0xCF, 0xFD, 0x04,
+	0x02, 0x7D, 0xFE, 0xEF, 0x00, 0x92, 0xFF, 0x1B, 0x00, 0xFE, 0xFF,
+	0x35, 0x00, 0x39, 0xFF, 0xD8, 0x01, 0x70, 0xFC, 0x43, 0x06, 0xE1,
+	0xF4, 0x38, 0x1A, 0x8C, 0x41, 0x55, 0xF5, 0xFC, 0x03, 0x85, 0xFE,
+	0x66, 0x00, 0x01, 0x00, 0xEE, 0xFF, 0x06, 0x00, 0xFD, 0xFF, 0x2B,
+	0x00, 0x59, 0xFF, 0xB0, 0x01, 0x69, 0xFC, 0x0F, 0x07, 0x80, 0xF1,
+	0x96, 0x2F, 0xF2, 0x30, 0x7C, 0xF1, 0xFD, 0x06, 0x7A, 0xFC, 0xA3,
+	0x01, 0x5F, 0xFF, 0x29, 0x00, 0xFD, 0xFF, 0x05, 0x00, 0xF4, 0xFF,
+	0xF2, 0xFF, 0x83, 0x00, 0x53, 0xFE, 0x4E, 0x04, 0xD0, 0xF4, 0xAB,
+	0x40, 0xB2, 0x1B, 0x7F, 0xF4, 0x69, 0x06, 0x62, 0xFC, 0xDD, 0x01,
+	0x38, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x19, 0x00, 0x98, 0xFF, 0xDE,
+	0x00, 0x9F, 0xFE, 0xC2, 0x01, 0x4B, 0xFE, 0x48, 0x00, 0xB3, 0x48,
+	0x5E, 0x07, 0x3B, 0xFB, 0x59, 0x03, 0xCD, 0xFD, 0x42, 0x01, 0x71,
+	0xFF, 0x24, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x30, 0x00, 0x47, 0xFF,
+	0xAF, 0x01, 0xD8, 0xFC, 0x52, 0x05, 0x19, 0xF7, 0xB2, 0x12, 0x5C,
+	0x45, 0xA9, 0xF8, 0x16, 0x02, 0xA6, 0xFF, 0xC3, 0xFF, 0x51, 0x00,
+	0xD0, 0xFF, 0x0C, 0x00, 0xFD, 0xFF, 0x34, 0x00, 0x40, 0xFF, 0xDB,
+	0x01, 0x35, 0xFC, 0x25, 0x07, 0x13, 0xF2, 0x3A, 0x28, 0xA0, 0x37,
+	0xF2, 0xF1, 0x5A, 0x06, 0xFB, 0xFC, 0x4F, 0x01, 0x8B, 0xFF, 0x1A,
+	0x00, 0xFF, 0xFF, 0x00, 0x00, 0x0D, 0x00, 0xAF, 0xFF, 0x09, 0x01,
+	0x6E, 0xFD, 0xB4, 0x05, 0xBC, 0xF2, 0x73, 0x3B, 0x64, 0x23, 0xD2,
+	0xF2, 0xFB, 0x06, 0x34, 0xFC, 0xE6, 0x01, 0x38, 0xFF, 0x36, 0x00,
+	0xFD, 0xFF, 0x11, 0x00, 0xBB, 0xFF, 0x87, 0x00, 0x54, 0xFF, 0x70,
+	0x00, 0xB3, 0x00, 0x4E, 0xFB, 0x1A, 0x47, 0x1F, 0x0E, 0xA8, 0xF8,
+	0x9B, 0x04, 0x2E, 0xFD, 0x8A, 0x01, 0x55, 0xFF, 0x2C, 0x00, 0xFF,
+	0xFF, 0x00, 0x00, 0x29, 0x00, 0x5F, 0xFF, 0x71, 0x01, 0x65, 0xFD,
+	0x29, 0x04, 0x96, 0xF9, 0x95, 0x0B, 0xDC, 0x47, 0x03, 0xFD, 0xD9,
+	0xFF, 0xEA, 0x00, 0x12, 0xFF, 0xA7, 0x00, 0xAE, 0xFF, 0x14, 0x00,
+	0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3E, 0xFC, 0xD0,
+	0x06, 0x5E, 0xF3, 0x94, 0x20, 0x7B, 0x3D, 0x60, 0xF3, 0x3E, 0x05,
+	0xBB, 0xFD, 0xDB, 0x00, 0xC6, 0xFF, 0x04, 0x00, 0x02, 0x00, 0xFE,
+	0xFF, 0x20, 0x00, 0x79, 0xFF, 0x72, 0x01, 0xC4, 0xFC, 0xA4, 0x06,
+	0xAB, 0xF1, 0x46, 0x35, 0xF7, 0x2A, 0xC6, 0xF1, 0x2A, 0x07, 0x40,
+	0xFC, 0xCF, 0x01, 0x47, 0xFF, 0x31, 0x00, 0xFD, 0xFF, 0x09, 0x00,
+	0xDB, 0xFF, 0x33, 0x00, 0x01, 0x00, 0x38, 0xFF, 0xD3, 0x02, 0x53,
+	0xF7, 0x1F, 0x44, 0x69, 0x15, 0x3F, 0xF6, 0xB2, 0x05, 0xAD, 0xFC,
+	0xC1, 0x01, 0x41, 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x20,
+	0x00, 0x7D, 0xFF, 0x24, 0x01, 0x0C, 0xFE, 0xDE, 0x02, 0x2E, 0xFC,
+	0x13, 0x05, 0xEC, 0x48, 0x54, 0x02, 0x5E, 0xFD, 0x3F, 0x02, 0x5D,
+	0xFE, 0xFE, 0x00, 0x8C, 0xFF, 0x1C, 0x00, 0xFE, 0xFF, 0x35, 0x00,
+	0x3B, 0xFF, 0xD3, 0x01, 0x7F, 0xFC, 0x1F, 0x06, 0x3C, 0xF5, 0xE6,
+	0x18, 0x4D, 0x42, 0xD5, 0xF5, 0xAF, 0x03, 0xB4, 0xFE, 0x4B, 0x00,
+	0x0E, 0x00, 0xE9, 0xFF, 0x07, 0x00, 0xFD, 0xFF, 0x2D, 0x00, 0x53,
+	0xFF, 0xBA, 0x01, 0x5B, 0xFC, 0x1B, 0x07, 0x8B, 0xF1, 0x58, 0x2E,
+	0x26, 0x32, 0x80, 0xF1, 0xEA, 0x06, 0x8C, 0xFC, 0x97, 0x01, 0x66,
+	0xFF, 0x27, 0x00, 0xFD, 0xFF, 0x04, 0x00, 0xF8, 0xFF, 0xE6, 0xFF,
+	0x9C, 0x00, 0x27, 0xFE, 0x94, 0x04, 0x61, 0xF4, 0xD7, 0x3F, 0x06,
+	0x1D, 0x2B, 0xF4, 0x89, 0x06, 0x56, 0xFC, 0xE0, 0x01, 0x37, 0xFF,
+	0x36, 0x00, 0xFE, 0xFF, 0x17, 0x00, 0x9E, 0xFF, 0xCF, 0x00, 0xBF,
+	0xFE, 0x86, 0x01, 0xBA, 0xFE, 0x5A, 0xFF, 0x86, 0x48, 0x7D, 0x08,
+	0xC7, 0xFA, 0x93, 0x03, 0xB0, 0xFD, 0x4F, 0x01, 0x6C, 0xFF, 0x25,
+	0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2F, 0x00, 0x4B, 0xFF, 0xA6, 0x01,
+	0xEE, 0xFC, 0x23, 0x05, 0x83, 0xF7, 0x6E, 0x11, 0xE5, 0x45, 0x57,
+	0xF9, 0xB8, 0x01, 0xDC, 0xFF, 0xA5, 0xFF, 0x5F, 0x00, 0xCA, 0xFF,
+	0x0D, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3D, 0xFF, 0xDF, 0x01, 0x32,
+	0xFC, 0x1E, 0x07, 0x40, 0xF2, 0xEB, 0x26, 0xB5, 0x38, 0x1F, 0xF2,
+	0x32, 0x06, 0x18, 0xFD, 0x3D, 0x01, 0x94, 0xFF, 0x16, 0x00, 0xFF,
+	0xFF, 0x00, 0x00, 0x11, 0x00, 0xA4, 0xFF, 0x1D, 0x01, 0x4C, 0xFD,
+	0xE6, 0x05, 0x7B, 0xF2, 0x71, 0x3A, 0xB8, 0x24, 0x97, 0xF2, 0x0B,
+	0x07, 0x32, 0xFC, 0xE4, 0x01, 0x39, 0xFF, 0x36, 0x00, 0xFD, 0xFF,
+	0x0F, 0x00, 0xC0, 0xFF, 0x78, 0x00, 0x73, 0xFF, 0x38, 0x00, 0x17,
+	0x01, 0x8B, 0xFA, 0xAF, 0x46, 0x59, 0x0F, 0x39, 0xF8, 0xCF, 0x04,
+	0x15, 0xFD, 0x95, 0x01, 0x51, 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0x00,
+	0x00, 0x28, 0x00, 0x64, 0xFF, 0x65, 0x01, 0x81, 0xFD, 0xF2, 0x03,
+	0x08, 0xFA, 0x68, 0x0A, 0x25, 0x48, 0xDE, 0xFD, 0x6E, 0xFF, 0x24,
+	0x01, 0xF3, 0xFE, 0xB6, 0x00, 0xA8, 0xFF, 0x15, 0x00, 0xFD, 0xFF,
+	0x36, 0x00, 0x36, 0xFF, 0xE5, 0x01, 0x46, 0xFC, 0xB8, 0x06, 0xA8,
+	0xF3, 0x3F, 0x1F, 0x64, 0x3E, 0xBA, 0xF3, 0x01, 0x05, 0xE2, 0xFD,
+	0xC4, 0x00, 0xD2, 0xFF, 0x00, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x23,
+	0x00, 0x71, 0xFF, 0x81, 0x01, 0xAE, 0xFC, 0xC1, 0x06, 0x95, 0xF1,
+	0x1E, 0x34, 0x3E, 0x2C, 0xAB, 0xF1, 0x27, 0x07, 0x49, 0xFC, 0xC8,
+	0x01, 0x4B, 0xFF, 0x30, 0x00, 0xFD, 0xFF, 0x08, 0x00, 0xE1, 0xFF,
+	0x25, 0x00, 0x1D, 0x00, 0x05, 0xFF, 0x28, 0x03, 0xBD, 0xF6, 0x77,
+	0x43, 0xB6, 0x16, 0xDC, 0xF5, 0xDD, 0x05, 0x9B, 0xFC, 0xC8, 0x01,
+	0x3E, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x1F, 0x00, 0x83,
+	0xFF, 0x16, 0x01, 0x2A, 0xFE, 0xA3, 0x02, 0xA1, 0xFC, 0x06, 0x04,
+	0xF5, 0x48, 0x56, 0x03, 0xED, 0xFC, 0x7B, 0x02, 0x3E, 0xFE, 0x0C,
+	0x01, 0x86, 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3D, 0xFF,
+	0xCC, 0x01, 0x8F, 0xFC, 0xF8, 0x05, 0x9B, 0xF5, 0x96, 0x17, 0x02,
+	0x43, 0x5E, 0xF6, 0x5F, 0x03, 0xE4, 0xFE, 0x30, 0x00, 0x1B, 0x00,
+	0xE4, 0xFF, 0x08, 0x00, 0xFD, 0xFF, 0x2F, 0x00, 0x4E, 0xFF, 0xC3,
+	0x01, 0x4F, 0xFC, 0x24, 0x07, 0x9C, 0xF1, 0x17, 0x2D, 0x57, 0x33,
+	0x8A, 0xF1, 0xD3, 0x06, 0x9F, 0xFC, 0x8A, 0x01, 0x6D, 0xFF, 0x25,
+	0x00, 0xFD, 0xFF, 0x03, 0x00, 0xFD, 0xFF, 0xD9, 0xFF, 0xB4, 0x00,
+	0xFD, 0xFD, 0xD7, 0x04, 0xFA, 0xF3, 0xFC, 0x3E, 0x5B, 0x1E, 0xDB,
+	0xF3, 0xA6, 0x06, 0x4C, 0xFC, 0xE3, 0x01, 0x36, 0xFF, 0x36, 0x00,
+	0xFE, 0xFF, 0x16, 0x00, 0xA4, 0xFF, 0xC0, 0x00, 0xDE, 0xFE, 0x4B,
+	0x01, 0x27, 0xFF, 0x73, 0xFE, 0x4F, 0x48, 0xA2, 0x09, 0x54, 0xFA,
+	0xCC, 0x03, 0x93, 0xFD, 0x5C, 0x01, 0x67, 0xFF, 0x27, 0x00, 0x00,
+	0x00, 0xFF, 0xFF, 0x2E, 0x00, 0x4E, 0xFF, 0x9C, 0x01, 0x05, 0xFD,
+	0xF1, 0x04, 0xF0, 0xF7, 0x2D, 0x10, 0x61, 0x46, 0x0D, 0xFA, 0x58,
+	0x01, 0x13, 0x00, 0x87, 0xFF, 0x6E, 0x00, 0xC4, 0xFF, 0x0E, 0x00,
+	0xFD, 0xFF, 0x35, 0x00, 0x3B, 0xFF, 0xE3, 0x01, 0x31, 0xFC, 0x14,
+	0x07, 0x73, 0xF2, 0x99, 0x25, 0xC2, 0x39, 0x54, 0xF2, 0x05, 0x06,
+	0x37, 0xFD, 0x2B, 0x01, 0x9E, 0xFF, 0x13, 0x00, 0xFF, 0xFF, 0xFF,
+	0xFF, 0x14, 0x00, 0x9B, 0xFF, 0x31, 0x01, 0x2C, 0xFD, 0x15, 0x06,
+	0x41, 0xF2, 0x6A, 0x39, 0x0A, 0x26, 0x61, 0xF2, 0x17, 0x07, 0x31,
+	0xFC, 0xE2, 0x01, 0x3B, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x0E, 0x00,
+	0xC6, 0xFF, 0x69, 0x00, 0x91, 0xFF, 0x00, 0x00, 0x78, 0x01, 0xD0,
+	0xF9, 0x39, 0x46, 0x98, 0x10, 0xCB, 0xF7, 0x02, 0x05, 0xFE, 0xFC,
+	0x9F, 0x01, 0x4D, 0xFF, 0x2E, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x26,
+	0x00, 0x69, 0xFF, 0x58, 0x01, 0x9D, 0xFD, 0xB9, 0x03, 0x7B, 0xFA,
+	0x40, 0x09, 0x63, 0x48, 0xBF, 0xFE, 0x03, 0xFF, 0x5F, 0x01, 0xD4,
+	0xFE, 0xC5, 0x00, 0xA2, 0xFF, 0x16, 0x00, 0xFE, 0xFF, 0x36, 0x00,
+	0x36, 0xFF, 0xE2, 0x01, 0x4F, 0xFC, 0x9C, 0x06, 0xF5, 0xF3, 0xEA,
+	0x1D, 0x47, 0x3F, 0x1B, 0xF4, 0xC1, 0x04, 0x0B, 0xFE, 0xAC, 0x00,
+	0xDE, 0xFF, 0xFB, 0xFF, 0x03, 0x00, 0xFD, 0xFF, 0x25, 0x00, 0x6A,
+	0xFF, 0x8E, 0x01, 0x99, 0xFC, 0xDB, 0x06, 0x86, 0xF1, 0xF2, 0x32,
+	0x82, 0x2D, 0x96, 0xF1, 0x21, 0x07, 0x53, 0xFC, 0xC0, 0x01, 0x50,
+	0xFF, 0x2E, 0x00, 0xFD, 0xFF, 0x07, 0x00, 0xE6, 0xFF, 0x17, 0x00,
+	0x39, 0x00, 0xD4, 0xFE, 0x7A, 0x03, 0x2F, 0xF6, 0xC7, 0x42, 0x06,
+	0x18, 0x7B, 0xF5, 0x05, 0x06, 0x8A, 0xFC, 0xCF, 0x01, 0x3C, 0xFF,
+	0x34, 0x00, 0xFE, 0xFF, 0x1D, 0x00, 0x88, 0xFF, 0x07, 0x01, 0x49,
+	0xFE, 0x67, 0x02, 0x13, 0xFD, 0xFF, 0x02, 0xF4, 0x48, 0x5F, 0x04,
+	0x7A, 0xFC, 0xB6, 0x02, 0x20, 0xFE, 0x1B, 0x01, 0x81, 0xFF, 0x1F,
+	0x00, 0x00, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x3F, 0xFF, 0xC6, 0x01,
+	0xA1, 0xFC, 0xCF, 0x05, 0xFC, 0xF5, 0x47, 0x16, 0xB0, 0x43, 0xEE,
+	0xF6, 0x0C, 0x03, 0x16, 0xFF, 0x14, 0x00, 0x29, 0x00, 0xDF, 0xFF,
+	0x09, 0x00, 0xFD, 0xFF, 0x30, 0x00, 0x4A, 0xFF, 0xCA, 0x01, 0x46,
+	0xFC, 0x29, 0x07, 0xB3, 0xF1, 0xD1, 0x2B, 0x81, 0x34, 0x9C, 0xF1,
+	0xB8, 0x06, 0xB5, 0xFC, 0x7C, 0x01, 0x74, 0xFF, 0x22, 0x00, 0xFE,
+	0xFF, 0x02, 0x00, 0x01, 0x00, 0xCE, 0xFF, 0xCC, 0x00, 0xD5, 0xFD,
+	0x16, 0x05, 0x9B, 0xF3, 0x18, 0x3E, 0xB1, 0x1F, 0x8F, 0xF3, 0xC0,
+	0x06, 0x43, 0xFC, 0xE5, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF,
+	0x15, 0x00, 0xAA, 0xFF, 0xB1, 0x00, 0xFE, 0xFE, 0x10, 0x01, 0x92,
+	0xFF, 0x94, 0xFD, 0x0D, 0x48, 0xCB, 0x0A, 0xE2, 0xF9, 0x04, 0x04,
+	0x77, 0xFD, 0x69, 0x01, 0x62, 0xFF, 0x28, 0x00, 0x00, 0x00, 0xFF,
+	0xFF, 0x2D, 0x00, 0x52, 0xFF, 0x91, 0x01, 0x1E, 0xFD, 0xBE, 0x04,
+	0x5E, 0xF8, 0xF0, 0x0E, 0xD3, 0x46, 0xCB, 0xFA, 0xF6, 0x00, 0x4B,
+	0x00, 0x69, 0xFF, 0x7D, 0x00, 0xBE, 0xFF, 0x10, 0x00, 0xFD, 0xFF,
+	0x36, 0x00, 0x39, 0xFF, 0xE5, 0x01, 0x32, 0xFC, 0x06, 0x07, 0xAA,
+	0xF2, 0x46, 0x24, 0xC8, 0x3A, 0x90, 0xF2, 0xD6, 0x05, 0x57, 0xFD,
+	0x17, 0x01, 0xA8, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x18,
+	0x00, 0x91, 0xFF, 0x43, 0x01, 0x0E, 0xFD, 0x40, 0x06, 0x0F, 0xF2,
+	0x5B, 0x38, 0x5C, 0x27, 0x30, 0xF2, 0x21, 0x07, 0x33, 0xFC, 0xDE,
+	0x01, 0x3E, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x0D, 0x00, 0xCC, 0xFF,
+	0x5A, 0x00, 0xAF, 0xFF, 0xCA, 0xFF, 0xD8, 0x01, 0x1C, 0xF9, 0xB8,
+	0x45, 0xDA, 0x11, 0x60, 0xF7, 0x33, 0x05, 0xE7, 0xFC, 0xA9, 0x01,
+	0x4A, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x25, 0x00, 0x6E,
+	0xFF, 0x4B, 0x01, 0xB9, 0xFD, 0x80, 0x03, 0xEE, 0xFA, 0x1D, 0x08,
+	0x98, 0x48, 0xA8, 0xFF, 0x95, 0xFE, 0x9A, 0x01, 0xB4, 0xFE, 0xD4,
+	0x00, 0x9C, 0xFF, 0x18, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF,
+	0xDF, 0x01, 0x5A, 0xFC, 0x7E, 0x06, 0x47, 0xF4, 0x94, 0x1C, 0x1F,
+	0x40, 0x85, 0xF4, 0x7D, 0x04, 0x36, 0xFE, 0x93, 0x00, 0xEA, 0xFF,
+	0xF7, 0xFF, 0x04, 0x00, 0xFD, 0xFF, 0x28, 0x00, 0x63, 0xFF, 0x9B,
+	0x01, 0x86, 0xFC, 0xF1, 0x06, 0x7E, 0xF1, 0xC0, 0x31, 0xC2, 0x2E,
+	0x87, 0xF1, 0x17, 0x07, 0x5F, 0xFC, 0xB6, 0x01, 0x55, 0xFF, 0x2D,
+	0x00, 0xFD, 0xFF, 0x06, 0x00, 0xEB, 0xFF, 0x09, 0x00, 0x54, 0x00,
+	0xA4, 0xFE, 0xC9, 0x03, 0xAA, 0xF5, 0x0C, 0x42, 0x56, 0x19, 0x1E,
+	0xF5, 0x2B, 0x06, 0x7A, 0xFC, 0xD4, 0x01, 0x3A, 0xFF, 0x35, 0x00,
+	0xFE, 0xFF, 0x1C, 0x00, 0x8E, 0xFF, 0xF9, 0x00, 0x68, 0xFE, 0x2C,
+	0x02, 0x84, 0xFD, 0xFF, 0x01, 0xE6, 0x48, 0x6E, 0x05, 0x07, 0xFC,
+	0xF1, 0x02, 0x01, 0xFE, 0x29, 0x01, 0x7B, 0xFF, 0x21, 0x00, 0x00,
+	0x00, 0xFF, 0xFF, 0x32, 0x00, 0x42, 0xFF, 0xBE, 0x01, 0xB4, 0xFC,
+	0xA4, 0x05, 0x61, 0xF6, 0xFB, 0x14, 0x53, 0x44, 0x86, 0xF7, 0xB6,
+	0x02, 0x49, 0xFF, 0xF7, 0xFF, 0x37, 0x00, 0xD9, 0xFF, 0x0A, 0x00,
+	0xFD, 0xFF, 0x32, 0x00, 0x46, 0xFF, 0xD1, 0x01, 0x3E, 0xFC, 0x2B,
+	0x07, 0xD0, 0xF1, 0x89, 0x2A, 0xA6, 0x35, 0xB4, 0xF1, 0x99, 0x06,
+	0xCD, 0xFC, 0x6D, 0x01, 0x7C, 0xFF, 0x1F, 0x00, 0xFE, 0xFF, 0x01,
+	0x00, 0x06, 0x00, 0xC2, 0xFF, 0xE3, 0x00, 0xAE, 0xFD, 0x52, 0x05,
+	0x44, 0xF3, 0x2A, 0x3D, 0x06, 0x21, 0x47, 0xF3, 0xD8, 0x06, 0x3C,
+	0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x13, 0x00,
+	0xB0, 0xFF, 0xA2, 0x00, 0x1D, 0xFF, 0xD6, 0x00, 0xFC, 0xFF, 0xBC,
+	0xFC, 0xC0, 0x47, 0xFA, 0x0B, 0x70, 0xF9, 0x3C, 0x04, 0x5C, 0xFD,
+	0x75, 0x01, 0x5D, 0xFF, 0x2A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B,
+	0x00, 0x57, 0xFF, 0x86, 0x01, 0x36, 0xFD, 0x89, 0x04, 0xCD, 0xF8,
+	0xB7, 0x0D, 0x3D, 0x47, 0x91, 0xFB, 0x91, 0x00, 0x83, 0x00, 0x4A,
+	0xFF, 0x8C, 0x00, 0xB9, 0xFF, 0x11, 0x00, 0xFD, 0xFF, 0x36, 0x00,
+	0x38, 0xFF, 0xE6, 0x01, 0x35, 0xFC, 0xF5, 0x06, 0xE7, 0xF2, 0xF2,
+	0x22, 0xC7, 0x3B, 0xD4, 0xF2, 0xA2, 0x05, 0x7A, 0xFD, 0x02, 0x01,
+	0xB2, 0xFF, 0x0B, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x1B, 0x00, 0x88,
+	0xFF, 0x55, 0x01, 0xF2, 0xFC, 0x67, 0x06, 0xE4, 0xF1, 0x44, 0x37,
+	0xAA, 0x28, 0x05, 0xF2, 0x27, 0x07, 0x36, 0xFC, 0xDA, 0x01, 0x41,
+	0xFF, 0x33, 0x00, 0xFD, 0xFF, 0x0B, 0x00, 0xD2, 0xFF, 0x4C, 0x00,
+	0xCD, 0xFF, 0x94, 0xFF, 0x34, 0x02, 0x70, 0xF8, 0x2E, 0x45, 0x20,
+	0x13, 0xF6, 0xF6, 0x62, 0x05, 0xD1, 0xFC, 0xB2, 0x01, 0x46, 0xFF,
+	0x31, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x23, 0x00, 0x73, 0xFF, 0x3D,
+	0x01, 0xD6, 0xFD, 0x46, 0x03, 0x61, 0xFB, 0x00, 0x07, 0xBF, 0x48,
+	0x98, 0x00, 0x26, 0xFE, 0xD5, 0x01, 0x95, 0xFE, 0xE3, 0x00, 0x96,
+	0xFF, 0x1A, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xDB, 0x01,
+	0x66, 0xFC, 0x5E, 0x06, 0x9C, 0xF4, 0x40, 0x1B, 0xEF, 0x40, 0xF7,
+	0xF4, 0x35, 0x04, 0x62, 0xFE, 0x7A, 0x00, 0xF7, 0xFF, 0xF2, 0xFF,
+	0x05, 0x00, 0xFD, 0xFF, 0x2A, 0x00, 0x5D, 0xFF, 0xA7, 0x01, 0x75,
+	0xFC, 0x03, 0x07, 0x7D, 0xF1, 0x8A, 0x30, 0xFF, 0x2F, 0x7E, 0xF1,
+	0x0A, 0x07, 0x6E, 0xFC, 0xAC, 0x01, 0x5A, 0xFF, 0x2B, 0x00, 0xFD,
+	0xFF, 0x05, 0x00, 0xF0, 0xFF, 0xFC, 0xFF, 0x6E, 0x00, 0x76, 0xFE,
+	0x15, 0x04, 0x2C, 0xF5, 0x49, 0x41, 0xA9, 0x1A, 0xC3, 0xF4, 0x4F,
+	0x06, 0x6C, 0xFC, 0xD9, 0x01, 0x38, 0xFF, 0x35, 0x00, 0xFE, 0xFF,
+	0x1A, 0x00, 0x94, 0xFF, 0xEA, 0x00, 0x87, 0xFE, 0xF0, 0x01, 0xF5,
+	0xFD, 0x05, 0x01, 0xCE, 0x48, 0x83, 0x06, 0x94, 0xFB, 0x2C, 0x03,
+	0xE4, 0xFD, 0x37, 0x01, 0x76, 0xFF, 0x22, 0x00, 0x00, 0x00, 0xFF,
+	0xFF, 0x31, 0x00, 0x45, 0xFF, 0xB6, 0x01, 0xC8, 0xFC, 0x77, 0x05,
+	0xC7, 0xF6, 0xB1, 0x13, 0xED, 0x44, 0x26, 0xF8, 0x5D, 0x02, 0x7D,
+	0xFF, 0xDA, 0xFF, 0x46, 0x00, 0xD4, 0xFF, 0x0B, 0x00, 0xFD, 0xFF,
+	0x33, 0x00, 0x42, 0xFF, 0xD7, 0x01, 0x38, 0xFC, 0x29, 0x07, 0xF3,
+	0xF1, 0x3E, 0x29, 0xC6, 0x36, 0xD4, 0xF1, 0x77, 0x06, 0xE6, 0xFC,
+	0x5C, 0x01, 0x84, 0xFF, 0x1C, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x0A,
+	0x00, 0xB7, 0xFF, 0xF9, 0x00, 0x89, 0xFD, 0x8A, 0x05, 0xF4, 0xF2,
+	0x37, 0x3C, 0x5B, 0x22, 0x03, 0xF3, 0xED, 0x06, 0x37, 0xFC, 0xE6,
+	0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x12, 0x00, 0xB6, 0xFF,
+	0x93, 0x00, 0x3C, 0xFF, 0x9D, 0x00, 0x63, 0x00, 0xEB, 0xFB, 0x69,
+	0x47, 0x2D, 0x0D, 0xFF, 0xF8, 0x72, 0x04, 0x42, 0xFD, 0x81, 0x01,
+	0x59, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x5B,
+	0xFF, 0x7A, 0x01, 0x50, 0xFD, 0x54, 0x04, 0x3D, 0xF9, 0x82, 0x0C,
+	0x9A, 0x47, 0x5E, 0xFC, 0x2A, 0x00, 0xBD, 0x00, 0x2B, 0xFF, 0x9B,
+	0x00, 0xB3, 0xFF, 0x12, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF,
+	0xE6, 0x01, 0x3A, 0xFC, 0xE2, 0x06, 0x28, 0xF3, 0x9E, 0x21, 0xC0,
+	0x3C, 0x1F, 0xF3, 0x6C, 0x05, 0x9E, 0xFD, 0xED, 0x00, 0xBD, 0xFF,
+	0x07, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x1E, 0x00, 0x80, 0xFF, 0x66,
+	0x01, 0xD8, 0xFC, 0x8B, 0x06, 0xC1, 0xF1, 0x27, 0x36, 0xF6, 0x29,
+	0xDF, 0xF1, 0x2A, 0x07, 0x3B, 0xFC, 0xD4, 0x01, 0x44, 0xFF, 0x32,
+	0x00, 0xFD, 0xFF, 0x0A, 0x00, 0xD7, 0xFF, 0x3E, 0x00, 0xEA, 0xFF,
+	0x60, 0xFF, 0x8F, 0x02, 0xCD, 0xF7, 0x99, 0x44, 0x68, 0x14, 0x8E,
+	0xF6, 0x90, 0x05, 0xBC, 0xFC, 0xBA, 0x01, 0x43, 0xFF, 0x32, 0x00,
+	0xFF, 0xFF, 0x00, 0x00, 0x22, 0x00, 0x79, 0xFF, 0x2F, 0x01, 0xF4,
+	0xFD, 0x0C, 0x03, 0xD4, 0xFB, 0xE9, 0x05, 0xDE, 0x48, 0x8F, 0x01,
+	0xB6, 0xFD, 0x11, 0x02, 0x76, 0xFE, 0xF2, 0x00, 0x91, 0xFF, 0x1B,
+	0x00, 0xFE, 0xFF, 0x35, 0x00, 0x39, 0xFF, 0xD7, 0x01, 0x73, 0xFC,
+	0x3B, 0x06, 0xF5, 0xF4, 0xED, 0x19, 0xB7, 0x41, 0x71, 0xF5, 0xEB,
+	0x03, 0x90, 0xFE, 0x60, 0x00, 0x04, 0x00, 0xED, 0xFF, 0x06, 0x00,
+	0xFD, 0xFF, 0x2C, 0x00, 0x57, 0xFF, 0xB2, 0x01, 0x65, 0xFC, 0x12,
+	0x07, 0x82, 0xF1, 0x50, 0x2F, 0x38, 0x31, 0x7C, 0xF1, 0xF9, 0x06,
+	0x7E, 0xFC, 0xA1, 0x01, 0x61, 0xFF, 0x29, 0x00, 0xFD, 0xFF, 0x04,
+	0x00, 0xF5, 0xFF, 0xEF, 0xFF, 0x88, 0x00, 0x49, 0xFE, 0x5D, 0x04,
+	0xB7, 0xF4, 0x7D, 0x40, 0xFD, 0x1B, 0x6C, 0xF4, 0x70, 0x06, 0x5F,
+	0xFC, 0xDE, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x19, 0x00,
+	0x9A, 0xFF, 0xDB, 0x00, 0xA6, 0xFE, 0xB4, 0x01, 0x64, 0xFE, 0x12,
+	0x00, 0xAA, 0x48, 0x9E, 0x07, 0x21, 0xFB, 0x66, 0x03, 0xC6, 0xFD,
+	0x45, 0x01, 0x70, 0xFF, 0x24, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x30,
+	0x00, 0x48, 0xFF, 0xAD, 0x01, 0xDD, 0xFC, 0x48, 0x05, 0x30, 0xF7,
+	0x6B, 0x12, 0x7D, 0x45, 0xCF, 0xF8, 0x01, 0x02, 0xB2, 0xFF, 0xBD,
+	0xFF, 0x54, 0x00, 0xCE, 0xFF, 0x0C, 0x00, 0xFD, 0xFF, 0x34, 0x00,
+	0x3F, 0xFF, 0xDC, 0x01, 0x34, 0xFC, 0x24, 0x07, 0x1C, 0xF2, 0xF0,
+	0x27, 0xDF, 0x37, 0xFB, 0xF1, 0x51, 0x06, 0x01, 0xFD, 0x4B, 0x01,
+	0x8D, 0xFF, 0x19, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x0E, 0x00, 0xAC,
+	0xFF, 0x0E, 0x01, 0x66, 0xFD, 0xBF, 0x05, 0xAD, 0xF2, 0x3B, 0x3B,
+	0xB0, 0x23, 0xC4, 0xF2, 0xFF, 0x06, 0x33, 0xFC, 0xE5, 0x01, 0x38,
+	0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x10, 0x00, 0xBC, 0xFF, 0x84, 0x00,
+	0x5B, 0xFF, 0x64, 0x00, 0xC9, 0x00, 0x22, 0xFB, 0x02, 0x47, 0x64,
+	0x0E, 0x8F, 0xF8, 0xA7, 0x04, 0x29, 0xFD, 0x8C, 0x01, 0x54, 0xFF,
+	0x2C, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x29, 0x00, 0x60, 0xFF, 0x6E,
+	0x01, 0x6B, 0xFD, 0x1D, 0x04, 0xAF, 0xF9, 0x51, 0x0B, 0xEC, 0x47,
+	0x33, 0xFD, 0xC1, 0xFF, 0xF7, 0x00, 0x0C, 0xFF, 0xAA, 0x00, 0xAD,
+	0xFF, 0x14, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01,
+	0x40, 0xFC, 0xCB, 0x06, 0x6E, 0xF3, 0x49, 0x20, 0xB0, 0x3D, 0x73,
+	0xF3, 0x31, 0x05, 0xC4, 0xFD, 0xD6, 0x00, 0xC8, 0xFF, 0x03, 0x00,
+	0x02, 0x00, 0xFE, 0xFF, 0x21, 0x00, 0x77, 0xFF, 0x75, 0x01, 0xBF,
+	0xFC, 0xAB, 0x06, 0xA6, 0xF1, 0x05, 0x35, 0x40, 0x2B, 0xBF, 0xF1,
+	0x2A, 0x07, 0x42, 0xFC, 0xCE, 0x01, 0x48, 0xFF, 0x31, 0x00, 0xFD,
+	0xFF, 0x09, 0x00, 0xDC, 0xFF, 0x2F, 0x00, 0x07, 0x00, 0x2C, 0xFF,
+	0xE6, 0x02, 0x31, 0xF7, 0xFA, 0x43, 0xB3, 0x15, 0x29, 0xF6, 0xBC,
+	0x05, 0xA9, 0xFC, 0xC2, 0x01, 0x40, 0xFF, 0x33, 0x00, 0xFF, 0xFF,
+	0x00, 0x00, 0x20, 0x00, 0x7E, 0xFF, 0x21, 0x01, 0x12, 0xFE, 0xD1,
+	0x02, 0x47, 0xFC, 0xD7, 0x04, 0xF0, 0x48, 0x8D, 0x02, 0x45, 0xFD,
+	0x4D, 0x02, 0x56, 0xFE, 0x01, 0x01, 0x8B, 0xFF, 0x1D, 0x00, 0xFE,
+	0xFF, 0x34, 0x00, 0x3B, 0xFF, 0xD1, 0x01, 0x83, 0xFC, 0x16, 0x06,
+	0x51, 0xF5, 0x9B, 0x18, 0x75, 0x42, 0xF3, 0xF5, 0x9D, 0x03, 0xBF,
+	0xFE, 0x45, 0x00, 0x11, 0x00, 0xE8, 0xFF, 0x07, 0x00, 0xFD, 0xFF,
+	0x2E, 0x00, 0x52, 0xFF, 0xBC, 0x01, 0x58, 0xFC, 0x1D, 0x07, 0x8E,
+	0xF1, 0x11, 0x2E, 0x6B, 0x32, 0x81, 0xF1, 0xE5, 0x06, 0x90, 0xFC,
+	0x94, 0x01, 0x67, 0xFF, 0x26, 0x00, 0xFD, 0xFF, 0x04, 0x00, 0xF9,
+	0xFF, 0xE3, 0xFF, 0xA1, 0x00, 0x1E, 0xFE, 0xA3, 0x04, 0x49, 0xF4,
+	0xA8, 0x3F, 0x52, 0x1D, 0x19, 0xF4, 0x90, 0x06, 0x53, 0xFC, 0xE1,
+	0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x17, 0x00, 0xA0, 0xFF,
+	0xCC, 0x00, 0xC6, 0xFE, 0x79, 0x01, 0xD2, 0xFE, 0x26, 0xFF, 0x7C,
+	0x48, 0xBE, 0x08, 0xAE, 0xFA, 0xA0, 0x03, 0xA9, 0xFD, 0x52, 0x01,
+	0x6B, 0xFF, 0x25, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2F, 0x00, 0x4C,
+	0xFF, 0xA3, 0x01, 0xF3, 0xFC, 0x18, 0x05, 0x9B, 0xF7, 0x27, 0x11,
+	0x02, 0x46, 0x7F, 0xF9, 0xA3, 0x01, 0xE8, 0xFF, 0x9F, 0xFF, 0x63,
+	0x00, 0xC9, 0xFF, 0x0D, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3C, 0xFF,
+	0xE0, 0x01, 0x32, 0xFC, 0x1C, 0x07, 0x4B, 0xF2, 0xA0, 0x26, 0xF2,
+	0x38, 0x2A, 0xF2, 0x28, 0x06, 0x1F, 0xFD, 0x39, 0x01, 0x96, 0xFF,
+	0x16, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x11, 0x00, 0xA2, 0xFF, 0x22,
+	0x01, 0x45, 0xFD, 0xF1, 0x05, 0x6D, 0xF2, 0x38, 0x3A, 0x03, 0x25,
+	0x8B, 0xF2, 0x0E, 0x07, 0x32, 0xFC, 0xE4, 0x01, 0x3A, 0xFF, 0x36,
+	0x00, 0xFD, 0xFF, 0x0F, 0x00, 0xC2, 0xFF, 0x75, 0x00, 0x7A, 0xFF,
+	0x2B, 0x00, 0x2D, 0x01, 0x61, 0xFA, 0x97, 0x46, 0xA0, 0x0F, 0x20,
+	0xF8, 0xDA, 0x04, 0x10, 0xFD, 0x97, 0x01, 0x50, 0xFF, 0x2E, 0x00,
+	0xFF, 0xFF, 0x00, 0x00, 0x27, 0x00, 0x65, 0xFF, 0x62, 0x01, 0x87,
+	0xFD, 0xE5, 0x03, 0x21, 0xFA, 0x25, 0x0A, 0x33, 0x48, 0x0F, 0xFE,
+	0x57, 0xFF, 0x31, 0x01, 0xEC, 0xFE, 0xB9, 0x00, 0xA7, 0xFF, 0x15,
+	0x00, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE4, 0x01, 0x48, 0xFC,
+	0xB2, 0x06, 0xB9, 0xF3, 0xF3, 0x1E, 0x98, 0x3E, 0xCF, 0xF3, 0xF3,
+	0x04, 0xEB, 0xFD, 0xBF, 0x00, 0xD4, 0xFF, 0xFF, 0xFF, 0x03, 0x00,
+	0xFE, 0xFF, 0x23, 0x00, 0x70, 0xFF, 0x84, 0x01, 0xA9, 0xFC, 0xC7,
+	0x06, 0x91, 0xF1, 0xDC, 0x33, 0x87, 0x2C, 0xA5, 0xF1, 0x26, 0x07,
+	0x4B, 0xFC, 0xC6, 0x01, 0x4C, 0xFF, 0x30, 0x00, 0xFD, 0xFF, 0x08,
+	0x00, 0xE2, 0xFF, 0x21, 0x00, 0x23, 0x00, 0xFA, 0xFE, 0x3A, 0x03,
+	0x9D, 0xF6, 0x50, 0x43, 0x00, 0x17, 0xC6, 0xF5, 0xE6, 0x05, 0x97,
+	0xFC, 0xC9, 0x01, 0x3E, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0x00, 0x00,
+	0x1E, 0x00, 0x84, 0xFF, 0x13, 0x01, 0x31, 0xFE, 0x95, 0x02, 0xBA,
+	0xFC, 0xCB, 0x03, 0xF7, 0x48, 0x91, 0x03, 0xD3, 0xFC, 0x88, 0x02,
+	0x38, 0xFE, 0x10, 0x01, 0x85, 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0x34,
+	0x00, 0x3D, 0xFF, 0xCB, 0x01, 0x93, 0xFC, 0xEF, 0x05, 0xB0, 0xF5,
+	0x4B, 0x17, 0x2A, 0x43, 0x7D, 0xF6, 0x4D, 0x03, 0xEF, 0xFE, 0x2A,
+	0x00, 0x1E, 0x00, 0xE3, 0xFF, 0x08, 0x00, 0xFD, 0xFF, 0x2F, 0x00,
+	0x4D, 0xFF, 0xC4, 0x01, 0x4D, 0xFC, 0x25, 0x07, 0xA1, 0xF1, 0xCE,
+	0x2C, 0x99, 0x33, 0x8E, 0xF1, 0xCD, 0x06, 0xA4, 0xFC, 0x87, 0x01,
+	0x6E, 0xFF, 0x24, 0x00, 0xFD, 0xFF, 0x03, 0x00, 0xFE, 0xFF, 0xD7,
+	0xFF, 0xBA, 0x00, 0xF4, 0xFD, 0xE5, 0x04, 0xE4, 0xF3, 0xCA, 0x3E,
+	0xA7, 0x1E, 0xCA, 0xF3, 0xAC, 0x06, 0x4A, 0xFC, 0xE4, 0x01, 0x36,
+	0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x16, 0x00, 0xA6, 0xFF, 0xBD, 0x00,
+	0xE5, 0xFE, 0x3E, 0x01, 0x3F, 0xFF, 0x41, 0xFE, 0x41, 0x48, 0xE4,
+	0x09, 0x3B, 0xFA, 0xD9, 0x03, 0x8D, 0xFD, 0x5F, 0x01, 0x66, 0xFF,
+	0x27, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2E, 0x00, 0x4F, 0xFF, 0x99,
+	0x01, 0x0B, 0xFD, 0xE6, 0x04, 0x08, 0xF8, 0xE7, 0x0F, 0x7C, 0x46,
+	0x37, 0xFA, 0x42, 0x01, 0x1F, 0x00, 0x81, 0xFF, 0x71, 0x00, 0xC3,
+	0xFF, 0x0F, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xE3, 0x01,
+	0x31, 0xFC, 0x11, 0x07, 0x7F, 0xF2, 0x4E, 0x25, 0xFD, 0x39, 0x60,
+	0xF2, 0xFB, 0x05, 0x3E, 0xFD, 0x26, 0x01, 0xA0, 0xFF, 0x12, 0x00,
+	0x00, 0x00, 0xFF, 0xFF, 0x15, 0x00, 0x98, 0xFF, 0x35, 0x01, 0x25,
+	0xFD, 0x1E, 0x06, 0x35, 0xF2, 0x2E, 0x39, 0x55, 0x26, 0x56, 0xF2,
+	0x1A, 0x07, 0x31, 0xFC, 0xE1, 0x01, 0x3C, 0xFF, 0x35, 0x00, 0xFD,
+	0xFF, 0x0E, 0x00, 0xC7, 0xFF, 0x66, 0x00, 0x98, 0xFF, 0xF4, 0xFF,
+	0x8E, 0x01, 0xA7, 0xF9, 0x1D, 0x46, 0xDF, 0x10, 0xB3, 0xF7, 0x0D,
+	0x05, 0xF8, 0xFC, 0xA1, 0x01, 0x4C, 0xFF, 0x2F, 0x00, 0xFF, 0xFF,
+	0x00, 0x00, 0x26, 0x00, 0x6A, 0xFF, 0x55, 0x01, 0xA3, 0xFD, 0xAD,
+	0x03, 0x94, 0xFA, 0xFF, 0x08, 0x70, 0x48, 0xF3, 0xFE, 0xEA, 0xFE,
+	0x6C, 0x01, 0xCD, 0xFE, 0xC9, 0x00, 0xA1, 0xFF, 0x17, 0x00, 0xFE,
+	0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE2, 0x01, 0x51, 0xFC, 0x96, 0x06,
+	0x07, 0xF4, 0x9E, 0x1D, 0x77, 0x3F, 0x32, 0xF4, 0xB2, 0x04, 0x15,
+	0xFE, 0xA7, 0x00, 0xE0, 0xFF, 0xFA, 0xFF, 0x03, 0x00, 0xFD, 0xFF,
+	0x26, 0x00, 0x69, 0xFF, 0x91, 0x01, 0x94, 0xFC, 0xE0, 0x06, 0x84,
+	0xF1, 0xAF, 0x32, 0xCA, 0x2D, 0x92, 0xF1, 0x1F, 0x07, 0x56, 0xFC,
+	0xBE, 0x01, 0x51, 0xFF, 0x2E, 0x00, 0xFD, 0xFF, 0x07, 0x00, 0xE7,
+	0xFF, 0x14, 0x00, 0x3F, 0x00, 0xC9, 0xFE, 0x8C, 0x03, 0x11, 0xF6,
+	0x9E, 0x42, 0x50, 0x18, 0x66, 0xF5, 0x0D, 0x06, 0x86, 0xFC, 0xD0,
+	0x01, 0x3B, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0x1D, 0x00, 0x8A, 0xFF,
+	0x04, 0x01, 0x50, 0xFE, 0x5A, 0x02, 0x2C, 0xFD, 0xC6, 0x02, 0xF2,
+	0x48, 0x9B, 0x04, 0x61, 0xFC, 0xC3, 0x02, 0x19, 0xFE, 0x1E, 0x01,
+	0x7F, 0xFF, 0x20, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x40,
+	0xFF, 0xC4, 0x01, 0xA5, 0xFC, 0xC5, 0x05, 0x13, 0xF6, 0xFD, 0x15,
+	0xD4, 0x43, 0x0F, 0xF7, 0xF9, 0x02, 0x21, 0xFF, 0x0D, 0x00, 0x2C,
+	0x00, 0xDE, 0xFF, 0x09, 0x00, 0xFD, 0xFF, 0x31, 0x00, 0x49, 0xFF,
+	0xCC, 0x01, 0x44, 0xFC, 0x29, 0x07, 0xB9, 0xF1, 0x89, 0x2B, 0xC3,
+	0x34, 0xA0, 0xF1, 0xB1, 0x06, 0xBA, 0xFC, 0x79, 0x01, 0x76, 0xFF,
+	0x21, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0x02, 0x00, 0xCB, 0xFF, 0xD1,
+	0x00, 0xCC, 0xFD, 0x24, 0x05, 0x87, 0xF3, 0xE4, 0x3D, 0xFD, 0x1F,
+	0x7F, 0xF3, 0xC6, 0x06, 0x41, 0xFC, 0xE5, 0x01, 0x36, 0xFF, 0x36,
+	0x00, 0xFD, 0xFF, 0x14, 0x00, 0xAC, 0xFF, 0xAE, 0x00, 0x05, 0xFF,
+	0x03, 0x01, 0xAA, 0xFF, 0x63, 0xFD, 0xFD, 0x47, 0x0E, 0x0B, 0xC8,
+	0xF9, 0x11, 0x04, 0x71, 0xFD, 0x6C, 0x01, 0x61, 0xFF, 0x28, 0x00,
+	0x00, 0x00, 0xFF, 0xFF, 0x2D, 0x00, 0x53, 0xFF, 0x8F, 0x01, 0x23,
+	0xFD, 0xB2, 0x04, 0x76, 0xF8, 0xAA, 0x0E, 0xED, 0x46, 0xF7, 0xFA,
+	0xDF, 0x00, 0x57, 0x00, 0x62, 0xFF, 0x80, 0x00, 0xBD, 0xFF, 0x10,
+	0x00, 0xFD, 0xFF, 0x36, 0x00, 0x39, 0xFF, 0xE5, 0x01, 0x33, 0xFC,
+	0x03, 0x07, 0xB7, 0xF2, 0xFC, 0x23, 0x03, 0x3B, 0x9E, 0xF2, 0xCB,
+	0x05, 0x5F, 0xFD, 0x12, 0x01, 0xAA, 0xFF, 0x0E, 0x00, 0x00, 0x00,
+	0xFF, 0xFF, 0x18, 0x00, 0x8F, 0xFF, 0x47, 0x01, 0x08, 0xFD, 0x49,
+	0x06, 0x05, 0xF2, 0x1D, 0x38, 0xA6, 0x27, 0x26, 0xF2, 0x23, 0x07,
+	0x33, 0xFC, 0xDD, 0x01, 0x3E, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x0C,
+	0x00, 0xCD, 0xFF, 0x57, 0x00, 0xB6, 0xFF, 0xBE, 0xFF, 0xED, 0x01,
+	0xF5, 0xF8, 0x9B, 0x45, 0x22, 0x12, 0x48, 0xF7, 0x3D, 0x05, 0xE2,
+	0xFC, 0xAB, 0x01, 0x49, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0x00, 0x00,
+	0x24, 0x00, 0x6F, 0xFF, 0x48, 0x01, 0xC0, 0xFD, 0x73, 0x03, 0x07,
+	0xFB, 0xDD, 0x07, 0xA1, 0x48, 0xDD, 0xFF, 0x7D, 0xFE, 0xA7, 0x01,
+	0xAD, 0xFE, 0xD8, 0x00, 0x9B, 0xFF, 0x18, 0x00, 0xFE, 0xFF, 0x36,
+	0x00, 0x37, 0xFF, 0xDF, 0x01, 0x5C, 0xFC, 0x78, 0x06, 0x5A, 0xF4,
+	0x49, 0x1C, 0x4E, 0x40, 0x9E, 0xF4, 0x6D, 0x04, 0x3F, 0xFE, 0x8E,
+	0x00, 0xED, 0xFF, 0xF6, 0xFF, 0x04, 0x00, 0xFD, 0xFF, 0x28, 0x00,
+	0x62, 0xFF, 0x9E, 0x01, 0x82, 0xFC, 0xF5, 0x06, 0x7D, 0xF1, 0x7B,
+	0x31, 0x09, 0x2F, 0x84, 0xF1, 0x15, 0x07, 0x62, 0xFC, 0xB4, 0x01,
+	0x56, 0xFF, 0x2C, 0x00, 0xFD, 0xFF, 0x06, 0x00, 0xEC, 0xFF, 0x06,
+	0x00, 0x5A, 0x00, 0x9A, 0xFE, 0xDA, 0x03, 0x8D, 0xF5, 0xE1, 0x41,
+	0xA1, 0x19, 0x09, 0xF5, 0x33, 0x06, 0x77, 0xFC, 0xD6, 0x01, 0x3A,
+	0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1B, 0x00, 0x8F, 0xFF, 0xF5, 0x00,
+	0x6F, 0xFE, 0x1E, 0x02, 0x9D, 0xFD, 0xC7, 0x01, 0xE1, 0x48, 0xAB,
+	0x05, 0xEE, 0xFB, 0xFE, 0x02, 0xFB, 0xFD, 0x2C, 0x01, 0x7A, 0xFF,
+	0x21, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x32, 0x00, 0x42, 0xFF, 0xBC,
+	0x01, 0xB8, 0xFC, 0x9A, 0x05, 0x77, 0xF6, 0xB1, 0x14, 0x77, 0x44,
+	0xA9, 0xF7, 0xA2, 0x02, 0x54, 0xFF, 0xF1, 0xFF, 0x3A, 0x00, 0xD8,
+	0xFF, 0x0A, 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x45, 0xFF, 0xD3, 0x01,
+	0x3C, 0xFC, 0x2A, 0x07, 0xD8, 0xF1, 0x3F, 0x2A, 0xE6, 0x35, 0xBB,
+	0xF1, 0x92, 0x06, 0xD2, 0xFC, 0x69, 0x01, 0x7E, 0xFF, 0x1F, 0x00,
+	0xFE, 0xFF, 0x01, 0x00, 0x07, 0x00, 0xC0, 0xFF, 0xE8, 0x00, 0xA6,
+	0xFD, 0x5F, 0x05, 0x31, 0xF3, 0xF6, 0x3C, 0x52, 0x21, 0x37, 0xF3,
+	0xDD, 0x06, 0x3B, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD,
+	0xFF, 0x13, 0x00, 0xB1, 0xFF, 0x9F, 0x00, 0x24, 0xFF, 0xC9, 0x00,
+	0x13, 0x00, 0x8D, 0xFC, 0xAE, 0x47, 0x3E, 0x0C, 0x56, 0xF9, 0x48,
+	0x04, 0x56, 0xFD, 0x78, 0x01, 0x5C, 0xFF, 0x2A, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x2B, 0x00, 0x58, 0xFF, 0x83, 0x01, 0x3C, 0xFD, 0x7E,
+	0x04, 0xE6, 0xF8, 0x72, 0x0D, 0x52, 0x47, 0xBE, 0xFB, 0x7A, 0x00,
+	0x90, 0x00, 0x43, 0xFF, 0x8F, 0x00, 0xB7, 0xFF, 0x11, 0x00, 0xFD,
+	0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x36, 0xFC, 0xF1, 0x06,
+	0xF5, 0xF2, 0xA7, 0x22, 0xFF, 0x3B, 0xE4, 0xF2, 0x96, 0x05, 0x81,
+	0xFD, 0xFD, 0x00, 0xB5, 0xFF, 0x0B, 0x00, 0x01, 0x00, 0xFE, 0xFF,
+	0x1C, 0x00, 0x86, 0xFF, 0x59, 0x01, 0xEC, 0xFC, 0x6F, 0x06, 0xDC,
+	0xF1, 0x04, 0x37, 0xF3, 0x28, 0xFC, 0xF1, 0x28, 0x07, 0x37, 0xFC,
+	0xD8, 0x01, 0x41, 0xFF, 0x33, 0x00, 0xFD, 0xFF, 0x0B, 0x00, 0xD3,
+	0xFF, 0x49, 0x00, 0xD4, 0xFF, 0x88, 0xFF, 0x49, 0x02, 0x4B, 0xF8,
+	0x0D, 0x45, 0x68, 0x13, 0xDF, 0xF6, 0x6C, 0x05, 0xCC, 0xFC, 0xB4,
+	0x01, 0x45, 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x23, 0x00,
+	0x74, 0xFF, 0x3A, 0x01, 0xDD, 0xFD, 0x39, 0x03, 0x7B, 0xFB, 0xC1,
+	0x06, 0xC7, 0x48, 0xCF, 0x00, 0x0D, 0xFE, 0xE3, 0x01, 0x8E, 0xFE,
+	0xE7, 0x00, 0x95, 0xFF, 0x1A, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x38,
+	0xFF, 0xDA, 0x01, 0x69, 0xFC, 0x57, 0x06, 0xAF, 0xF4, 0xF5, 0x1A,
+	0x1D, 0x41, 0x11, 0xF5, 0x25, 0x04, 0x6C, 0xFE, 0x74, 0x00, 0xF9,
+	0xFF, 0xF1, 0xFF, 0x05, 0x00, 0xFD, 0xFF, 0x2A, 0x00, 0x5C, 0xFF,
+	0xAA, 0x01, 0x71, 0xFC, 0x07, 0x07, 0x7E, 0xF1, 0x44, 0x30, 0x44,
+	0x30, 0x7E, 0xF1, 0x07, 0x07, 0x71, 0xFC, 0xAA, 0x01, 0x5C, 0xFF,
+	0x2A, 0x00, 0xFD, 0xFF, 0x05, 0x00, 0xF1, 0xFF, 0xF9, 0xFF, 0x74,
+	0x00, 0x6C, 0xFE, 0x25, 0x04, 0x11, 0xF5, 0x1D, 0x41, 0xF5, 0x1A,
+	0xAF, 0xF4, 0x57, 0x06, 0x69, 0xFC, 0xDA, 0x01, 0x38, 0xFF, 0x36,
+	0x00, 0xFE, 0xFF, 0x1A, 0x00, 0x95, 0xFF, 0xE7, 0x00, 0x8E, 0xFE,
+	0xE3, 0x01, 0x0D, 0xFE, 0xCF, 0x00, 0xC7, 0x48, 0xC1, 0x06, 0x7B,
+	0xFB, 0x39, 0x03, 0xDD, 0xFD, 0x3A, 0x01, 0x74, 0xFF, 0x23, 0x00,
+	0x00, 0x00, 0xFF, 0xFF, 0x31, 0x00, 0x45, 0xFF, 0xB4, 0x01, 0xCC,
+	0xFC, 0x6C, 0x05, 0xDF, 0xF6, 0x68, 0x13, 0x0D, 0x45, 0x4B, 0xF8,
+	0x49, 0x02, 0x88, 0xFF, 0xD4, 0xFF, 0x49, 0x00, 0xD3, 0xFF, 0x0B,
+	0x00, 0xFD, 0xFF, 0x33, 0x00, 0x41, 0xFF, 0xD8, 0x01, 0x37, 0xFC,
+	0x28, 0x07, 0xFC, 0xF1, 0xF3, 0x28, 0x04, 0x37, 0xDC, 0xF1, 0x6F,
+	0x06, 0xEC, 0xFC, 0x59, 0x01, 0x86, 0xFF, 0x1C, 0x00, 0xFE, 0xFF,
+	0x01, 0x00, 0x0B, 0x00, 0xB5, 0xFF, 0xFD, 0x00, 0x81, 0xFD, 0x96,
+	0x05, 0xE4, 0xF2, 0xFF, 0x3B, 0xA7, 0x22, 0xF5, 0xF2, 0xF1, 0x06,
+	0x36, 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x11,
+	0x00, 0xB7, 0xFF, 0x8F, 0x00, 0x43, 0xFF, 0x90, 0x00, 0x7A, 0x00,
+	0xBE, 0xFB, 0x52, 0x47, 0x72, 0x0D, 0xE6, 0xF8, 0x7E, 0x04, 0x3C,
+	0xFD, 0x83, 0x01, 0x58, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x2A, 0x00, 0x5C, 0xFF, 0x78, 0x01, 0x56, 0xFD, 0x48, 0x04, 0x56,
+	0xF9, 0x3E, 0x0C, 0xAE, 0x47, 0x8D, 0xFC, 0x13, 0x00, 0xC9, 0x00,
+	0x24, 0xFF, 0x9F, 0x00, 0xB1, 0xFF, 0x13, 0x00, 0xFD, 0xFF, 0x36,
+	0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3B, 0xFC, 0xDD, 0x06, 0x37, 0xF3,
+	0x52, 0x21, 0xF6, 0x3C, 0x31, 0xF3, 0x5F, 0x05, 0xA6, 0xFD, 0xE8,
+	0x00, 0xC0, 0xFF, 0x07, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x1F, 0x00,
+	0x7E, 0xFF, 0x69, 0x01, 0xD2, 0xFC, 0x92, 0x06, 0xBB, 0xF1, 0xE6,
+	0x35, 0x3F, 0x2A, 0xD8, 0xF1, 0x2A, 0x07, 0x3C, 0xFC, 0xD3, 0x01,
+	0x45, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x0A, 0x00, 0xD8, 0xFF, 0x3A,
+	0x00, 0xF1, 0xFF, 0x54, 0xFF, 0xA2, 0x02, 0xA9, 0xF7, 0x77, 0x44,
+	0xB1, 0x14, 0x77, 0xF6, 0x9A, 0x05, 0xB8, 0xFC, 0xBC, 0x01, 0x42,
+	0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x21, 0x00, 0x7A, 0xFF,
+	0x2C, 0x01, 0xFB, 0xFD, 0xFE, 0x02, 0xEE, 0xFB, 0xAB, 0x05, 0xE1,
+	0x48, 0xC7, 0x01, 0x9D, 0xFD, 0x1E, 0x02, 0x6F, 0xFE, 0xF5, 0x00,
+	0x8F, 0xFF, 0x1B, 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xD6,
+	0x01, 0x77, 0xFC, 0x33, 0x06, 0x09, 0xF5, 0xA1, 0x19, 0xE1, 0x41,
+	0x8D, 0xF5, 0xDA, 0x03, 0x9A, 0xFE, 0x5A, 0x00, 0x06, 0x00, 0xEC,
+	0xFF, 0x06, 0x00, 0xFD, 0xFF, 0x2C, 0x00, 0x56, 0xFF, 0xB4, 0x01,
+	0x62, 0xFC, 0x15, 0x07, 0x84, 0xF1, 0x09, 0x2F, 0x7B, 0x31, 0x7D,
+	0xF1, 0xF5, 0x06, 0x82, 0xFC, 0x9E, 0x01, 0x62, 0xFF, 0x28, 0x00,
+	0xFD, 0xFF, 0x04, 0x00, 0xF6, 0xFF, 0xED, 0xFF, 0x8E, 0x00, 0x3F,
+	0xFE, 0x6D, 0x04, 0x9E, 0xF4, 0x4E, 0x40, 0x49, 0x1C, 0x5A, 0xF4,
+	0x78, 0x06, 0x5C, 0xFC, 0xDF, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE,
+	0xFF, 0x18, 0x00, 0x9B, 0xFF, 0xD8, 0x00, 0xAD, 0xFE, 0xA7, 0x01,
+	0x7D, 0xFE, 0xDD, 0xFF, 0xA1, 0x48, 0xDD, 0x07, 0x07, 0xFB, 0x73,
+	0x03, 0xC0, 0xFD, 0x48, 0x01, 0x6F, 0xFF, 0x24, 0x00, 0x00, 0x00,
+	0xFF, 0xFF, 0x30, 0x00, 0x49, 0xFF, 0xAB, 0x01, 0xE2, 0xFC, 0x3D,
+	0x05, 0x48, 0xF7, 0x22, 0x12, 0x9B, 0x45, 0xF5, 0xF8, 0xED, 0x01,
+	0xBE, 0xFF, 0xB6, 0xFF, 0x57, 0x00, 0xCD, 0xFF, 0x0C, 0x00, 0xFD,
+	0xFF, 0x34, 0x00, 0x3E, 0xFF, 0xDD, 0x01, 0x33, 0xFC, 0x23, 0x07,
+	0x26, 0xF2, 0xA6, 0x27, 0x1D, 0x38, 0x05, 0xF2, 0x49, 0x06, 0x08,
+	0xFD, 0x47, 0x01, 0x8F, 0xFF, 0x18, 0x00, 0xFF, 0xFF, 0x00, 0x00,
+	0x0E, 0x00, 0xAA, 0xFF, 0x12, 0x01, 0x5F, 0xFD, 0xCB, 0x05, 0x9E,
+	0xF2, 0x03, 0x3B, 0xFC, 0x23, 0xB7, 0xF2, 0x03, 0x07, 0x33, 0xFC,
+	0xE5, 0x01, 0x39, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x10, 0x00, 0xBD,
+	0xFF, 0x80, 0x00, 0x62, 0xFF, 0x57, 0x00, 0xDF, 0x00, 0xF7, 0xFA,
+	0xED, 0x46, 0xAA, 0x0E, 0x76, 0xF8, 0xB2, 0x04, 0x23, 0xFD, 0x8F,
+	0x01, 0x53, 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x28, 0x00,
+	0x61, 0xFF, 0x6C, 0x01, 0x71, 0xFD, 0x11, 0x04, 0xC8, 0xF9, 0x0E,
+	0x0B, 0xFD, 0x47, 0x63, 0xFD, 0xAA, 0xFF, 0x03, 0x01, 0x05, 0xFF,
+	0xAE, 0x00, 0xAC, 0xFF, 0x14, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36,
+	0xFF, 0xE5, 0x01, 0x41, 0xFC, 0xC6, 0x06, 0x7F, 0xF3, 0xFD, 0x1F,
+	0xE4, 0x3D, 0x87, 0xF3, 0x24, 0x05, 0xCC, 0xFD, 0xD1, 0x00, 0xCB,
+	0xFF, 0x02, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x21, 0x00, 0x76, 0xFF,
+	0x79, 0x01, 0xBA, 0xFC, 0xB1, 0x06, 0xA0, 0xF1, 0xC3, 0x34, 0x89,
+	0x2B, 0xB9, 0xF1, 0x29, 0x07, 0x44, 0xFC, 0xCC, 0x01, 0x49, 0xFF,
+	0x31, 0x00, 0xFD, 0xFF, 0x09, 0x00, 0xDE, 0xFF, 0x2C, 0x00, 0x0D,
+	0x00, 0x21, 0xFF, 0xF9, 0x02, 0x0F, 0xF7, 0xD4, 0x43, 0xFD, 0x15,
+	0x13, 0xF6, 0xC5, 0x05, 0xA5, 0xFC, 0xC4, 0x01, 0x40, 0xFF, 0x33,
+	0x00, 0xFF, 0xFF, 0x00, 0x00, 0x20, 0x00, 0x7F, 0xFF, 0x1E, 0x01,
+	0x19, 0xFE, 0xC3, 0x02, 0x61, 0xFC, 0x9B, 0x04, 0xF2, 0x48, 0xC6,
+	0x02, 0x2C, 0xFD, 0x5A, 0x02, 0x50, 0xFE, 0x04, 0x01, 0x8A, 0xFF,
+	0x1D, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3B, 0xFF, 0xD0, 0x01, 0x86,
+	0xFC, 0x0D, 0x06, 0x66, 0xF5, 0x50, 0x18, 0x9E, 0x42, 0x11, 0xF6,
+	0x8C, 0x03, 0xC9, 0xFE, 0x3F, 0x00, 0x14, 0x00, 0xE7, 0xFF, 0x07,
+	0x00, 0xFD, 0xFF, 0x2E, 0x00, 0x51, 0xFF, 0xBE, 0x01, 0x56, 0xFC,
+	0x1F, 0x07, 0x92, 0xF1, 0xCA, 0x2D, 0xAF, 0x32, 0x84, 0xF1, 0xE0,
+	0x06, 0x94, 0xFC, 0x91, 0x01, 0x69, 0xFF, 0x26, 0x00, 0xFD, 0xFF,
+	0x03, 0x00, 0xFA, 0xFF, 0xE0, 0xFF, 0xA7, 0x00, 0x15, 0xFE, 0xB2,
+	0x04, 0x32, 0xF4, 0x77, 0x3F, 0x9E, 0x1D, 0x07, 0xF4, 0x96, 0x06,
+	0x51, 0xFC, 0xE2, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x17,
+	0x00, 0xA1, 0xFF, 0xC9, 0x00, 0xCD, 0xFE, 0x6C, 0x01, 0xEA, 0xFE,
+	0xF3, 0xFE, 0x70, 0x48, 0xFF, 0x08, 0x94, 0xFA, 0xAD, 0x03, 0xA3,
+	0xFD, 0x55, 0x01, 0x6A, 0xFF, 0x26, 0x00, 0x00, 0x00, 0xFF, 0xFF,
+	0x2F, 0x00, 0x4C, 0xFF, 0xA1, 0x01, 0xF8, 0xFC, 0x0D, 0x05, 0xB3,
+	0xF7, 0xDF, 0x10, 0x1D, 0x46, 0xA7, 0xF9, 0x8E, 0x01, 0xF4, 0xFF,
+	0x98, 0xFF, 0x66, 0x00, 0xC7, 0xFF, 0x0E, 0x00, 0xFD, 0xFF, 0x35,
+	0x00, 0x3C, 0xFF, 0xE1, 0x01, 0x31, 0xFC, 0x1A, 0x07, 0x56, 0xF2,
+	0x55, 0x26, 0x2E, 0x39, 0x35, 0xF2, 0x1E, 0x06, 0x25, 0xFD, 0x35,
+	0x01, 0x98, 0xFF, 0x15, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x12, 0x00,
+	0xA0, 0xFF, 0x26, 0x01, 0x3E, 0xFD, 0xFB, 0x05, 0x60, 0xF2, 0xFD,
+	0x39, 0x4E, 0x25, 0x7F, 0xF2, 0x11, 0x07, 0x31, 0xFC, 0xE3, 0x01,
+	0x3A, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x0F, 0x00, 0xC3, 0xFF, 0x71,
+	0x00, 0x81, 0xFF, 0x1F, 0x00, 0x42, 0x01, 0x37, 0xFA, 0x7C, 0x46,
+	0xE7, 0x0F, 0x08, 0xF8, 0xE6, 0x04, 0x0B, 0xFD, 0x99, 0x01, 0x4F,
+	0xFF, 0x2E, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x27, 0x00, 0x66, 0xFF,
+	0x5F, 0x01, 0x8D, 0xFD, 0xD9, 0x03, 0x3B, 0xFA, 0xE4, 0x09, 0x41,
+	0x48, 0x41, 0xFE, 0x3F, 0xFF, 0x3E, 0x01, 0xE5, 0xFE, 0xBD, 0x00,
+	0xA6, 0xFF, 0x16, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE4,
+	0x01, 0x4A, 0xFC, 0xAC, 0x06, 0xCA, 0xF3, 0xA7, 0x1E, 0xCA, 0x3E,
+	0xE4, 0xF3, 0xE5, 0x04, 0xF4, 0xFD, 0xBA, 0x00, 0xD7, 0xFF, 0xFE,
+	0xFF, 0x03, 0x00, 0xFD, 0xFF, 0x24, 0x00, 0x6E, 0xFF, 0x87, 0x01,
+	0xA4, 0xFC, 0xCD, 0x06, 0x8E, 0xF1, 0x99, 0x33, 0xCE, 0x2C, 0xA1,
+	0xF1, 0x25, 0x07, 0x4D, 0xFC, 0xC4, 0x01, 0x4D, 0xFF, 0x2F, 0x00,
+	0xFD, 0xFF, 0x08, 0x00, 0xE3, 0xFF, 0x1E, 0x00, 0x2A, 0x00, 0xEF,
+	0xFE, 0x4D, 0x03, 0x7D, 0xF6, 0x2A, 0x43, 0x4B, 0x17, 0xB0, 0xF5,
+	0xEF, 0x05, 0x93, 0xFC, 0xCB, 0x01, 0x3D, 0xFF, 0x34, 0x00, 0xFE,
+	0xFF, 0x1E, 0x00, 0x85, 0xFF, 0x10, 0x01, 0x38, 0xFE, 0x88, 0x02,
+	0xD3, 0xFC, 0x91, 0x03, 0xF7, 0x48, 0xCB, 0x03, 0xBA, 0xFC, 0x95,
+	0x02, 0x31, 0xFE, 0x13, 0x01, 0x84, 0xFF, 0x1E, 0x00, 0x00, 0x00,
+	0xFE, 0xFF, 0x34, 0x00, 0x3E, 0xFF, 0xC9, 0x01, 0x97, 0xFC, 0xE6,
+	0x05, 0xC6, 0xF5, 0x00, 0x17, 0x50, 0x43, 0x9D, 0xF6, 0x3A, 0x03,
+	0xFA, 0xFE, 0x23, 0x00, 0x21, 0x00, 0xE2, 0xFF, 0x08, 0x00, 0xFD,
+	0xFF, 0x30, 0x00, 0x4C, 0xFF, 0xC6, 0x01, 0x4B, 0xFC, 0x26, 0x07,
+	0xA5, 0xF1, 0x87, 0x2C, 0xDC, 0x33, 0x91, 0xF1, 0xC7, 0x06, 0xA9,
+	0xFC, 0x84, 0x01, 0x70, 0xFF, 0x23, 0x00, 0xFE, 0xFF, 0x03, 0x00,
+	0xFF, 0xFF, 0xD4, 0xFF, 0xBF, 0x00, 0xEB, 0xFD, 0xF3, 0x04, 0xCF,
+	0xF3, 0x98, 0x3E, 0xF3, 0x1E, 0xB9, 0xF3, 0xB2, 0x06, 0x48, 0xFC,
+	0xE4, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x15, 0x00, 0xA7,
+	0xFF, 0xB9, 0x00, 0xEC, 0xFE, 0x31, 0x01, 0x57, 0xFF, 0x0F, 0xFE,
+	0x33, 0x48, 0x25, 0x0A, 0x21, 0xFA, 0xE5, 0x03, 0x87, 0xFD, 0x62,
+	0x01, 0x65, 0xFF, 0x27, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2E, 0x00,
+	0x50, 0xFF, 0x97, 0x01, 0x10, 0xFD, 0xDA, 0x04, 0x20, 0xF8, 0xA0,
+	0x0F, 0x97, 0x46, 0x61, 0xFA, 0x2D, 0x01, 0x2B, 0x00, 0x7A, 0xFF,
+	0x75, 0x00, 0xC2, 0xFF, 0x0F, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x3A,
+	0xFF, 0xE4, 0x01, 0x32, 0xFC, 0x0E, 0x07, 0x8B, 0xF2, 0x03, 0x25,
+	0x38, 0x3A, 0x6D, 0xF2, 0xF1, 0x05, 0x45, 0xFD, 0x22, 0x01, 0xA2,
+	0xFF, 0x11, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x16, 0x00, 0x96, 0xFF,
+	0x39, 0x01, 0x1F, 0xFD, 0x28, 0x06, 0x2A, 0xF2, 0xF2, 0x38, 0xA0,
+	0x26, 0x4B, 0xF2, 0x1C, 0x07, 0x32, 0xFC, 0xE0, 0x01, 0x3C, 0xFF,
+	0x35, 0x00, 0xFD, 0xFF, 0x0D, 0x00, 0xC9, 0xFF, 0x63, 0x00, 0x9F,
+	0xFF, 0xE8, 0xFF, 0xA3, 0x01, 0x7F, 0xF9, 0x02, 0x46, 0x27, 0x11,
+	0x9B, 0xF7, 0x18, 0x05, 0xF3, 0xFC, 0xA3, 0x01, 0x4C, 0xFF, 0x2F,
+	0x00, 0xFF, 0xFF, 0x00, 0x00, 0x25, 0x00, 0x6B, 0xFF, 0x52, 0x01,
+	0xA9, 0xFD, 0xA0, 0x03, 0xAE, 0xFA, 0xBE, 0x08, 0x7C, 0x48, 0x26,
+	0xFF, 0xD2, 0xFE, 0x79, 0x01, 0xC6, 0xFE, 0xCC, 0x00, 0xA0, 0xFF,
+	0x17, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE1, 0x01, 0x53,
+	0xFC, 0x90, 0x06, 0x19, 0xF4, 0x52, 0x1D, 0xA8, 0x3F, 0x49, 0xF4,
+	0xA3, 0x04, 0x1E, 0xFE, 0xA1, 0x00, 0xE3, 0xFF, 0xF9, 0xFF, 0x04,
+	0x00, 0xFD, 0xFF, 0x26, 0x00, 0x67, 0xFF, 0x94, 0x01, 0x90, 0xFC,
+	0xE5, 0x06, 0x81, 0xF1, 0x6B, 0x32, 0x11, 0x2E, 0x8E, 0xF1, 0x1D,
+	0x07, 0x58, 0xFC, 0xBC, 0x01, 0x52, 0xFF, 0x2E, 0x00, 0xFD, 0xFF,
+	0x07, 0x00, 0xE8, 0xFF, 0x11, 0x00, 0x45, 0x00, 0xBF, 0xFE, 0x9D,
+	0x03, 0xF3, 0xF5, 0x75, 0x42, 0x9B, 0x18, 0x51, 0xF5, 0x16, 0x06,
+	0x83, 0xFC, 0xD1, 0x01, 0x3B, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0x1D,
+	0x00, 0x8B, 0xFF, 0x01, 0x01, 0x56, 0xFE, 0x4D, 0x02, 0x45, 0xFD,
+	0x8D, 0x02, 0xF0, 0x48, 0xD7, 0x04, 0x47, 0xFC, 0xD1, 0x02, 0x12,
+	0xFE, 0x21, 0x01, 0x7E, 0xFF, 0x20, 0x00, 0x00, 0x00, 0xFF, 0xFF,
+	0x33, 0x00, 0x40, 0xFF, 0xC2, 0x01, 0xA9, 0xFC, 0xBC, 0x05, 0x29,
+	0xF6, 0xB3, 0x15, 0xFA, 0x43, 0x31, 0xF7, 0xE6, 0x02, 0x2C, 0xFF,
+	0x07, 0x00, 0x2F, 0x00, 0xDC, 0xFF, 0x09, 0x00, 0xFD, 0xFF, 0x31,
+	0x00, 0x48, 0xFF, 0xCE, 0x01, 0x42, 0xFC, 0x2A, 0x07, 0xBF, 0xF1,
+	0x40, 0x2B, 0x05, 0x35, 0xA6, 0xF1, 0xAB, 0x06, 0xBF, 0xFC, 0x75,
+	0x01, 0x77, 0xFF, 0x21, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0x03, 0x00,
+	0xC8, 0xFF, 0xD6, 0x00, 0xC4, 0xFD, 0x31, 0x05, 0x73, 0xF3, 0xB0,
+	0x3D, 0x49, 0x20, 0x6E, 0xF3, 0xCB, 0x06, 0x40, 0xFC, 0xE6, 0x01,
+	0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x14, 0x00, 0xAD, 0xFF, 0xAA,
+	0x00, 0x0C, 0xFF, 0xF7, 0x00, 0xC1, 0xFF, 0x33, 0xFD, 0xEC, 0x47,
+	0x51, 0x0B, 0xAF, 0xF9, 0x1D, 0x04, 0x6B, 0xFD, 0x6E, 0x01, 0x60,
+	0xFF, 0x29, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2C, 0x00, 0x54, 0xFF,
+	0x8C, 0x01, 0x29, 0xFD, 0xA7, 0x04, 0x8F, 0xF8, 0x64, 0x0E, 0x02,
+	0x47, 0x22, 0xFB, 0xC9, 0x00, 0x64, 0x00, 0x5B, 0xFF, 0x84, 0x00,
+	0xBC, 0xFF, 0x10, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xE5,
+	0x01, 0x33, 0xFC, 0xFF, 0x06, 0xC4, 0xF2, 0xB0, 0x23, 0x3B, 0x3B,
+	0xAD, 0xF2, 0xBF, 0x05, 0x66, 0xFD, 0x0E, 0x01, 0xAC, 0xFF, 0x0E,
+	0x00, 0x00, 0x00, 0xFF, 0xFF, 0x19, 0x00, 0x8D, 0xFF, 0x4B, 0x01,
+	0x01, 0xFD, 0x51, 0x06, 0xFB, 0xF1, 0xDF, 0x37, 0xF0, 0x27, 0x1C,
+	0xF2, 0x24, 0x07, 0x34, 0xFC, 0xDC, 0x01, 0x3F, 0xFF, 0x34, 0x00,
+	0xFD, 0xFF, 0x0C, 0x00, 0xCE, 0xFF, 0x54, 0x00, 0xBD, 0xFF, 0xB2,
+	0xFF, 0x01, 0x02, 0xCF, 0xF8, 0x7D, 0x45, 0x6B, 0x12, 0x30, 0xF7,
+	0x48, 0x05, 0xDD, 0xFC, 0xAD, 0x01, 0x48, 0xFF, 0x30, 0x00, 0xFF,
+	0xFF, 0x00, 0x00, 0x24, 0x00, 0x70, 0xFF, 0x45, 0x01, 0xC6, 0xFD,
+	0x66, 0x03, 0x21, 0xFB, 0x9E, 0x07, 0xAA, 0x48, 0x12, 0x00, 0x64,
+	0xFE, 0xB4, 0x01, 0xA6, 0xFE, 0xDB, 0x00, 0x9A, 0xFF, 0x19, 0x00,
+	0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xDE, 0x01, 0x5F, 0xFC, 0x70,
+	0x06, 0x6C, 0xF4, 0xFD, 0x1B, 0x7D, 0x40, 0xB7, 0xF4, 0x5D, 0x04,
+	0x49, 0xFE, 0x88, 0x00, 0xEF, 0xFF, 0xF5, 0xFF, 0x04, 0x00, 0xFD,
+	0xFF, 0x29, 0x00, 0x61, 0xFF, 0xA1, 0x01, 0x7E, 0xFC, 0xF9, 0x06,
+	0x7C, 0xF1, 0x38, 0x31, 0x50, 0x2F, 0x82, 0xF1, 0x12, 0x07, 0x65,
+	0xFC, 0xB2, 0x01, 0x57, 0xFF, 0x2C, 0x00, 0xFD, 0xFF, 0x06, 0x00,
+	0xED, 0xFF, 0x04, 0x00, 0x60, 0x00, 0x90, 0xFE, 0xEB, 0x03, 0x71,
+	0xF5, 0xB7, 0x41, 0xED, 0x19, 0xF5, 0xF4, 0x3B, 0x06, 0x73, 0xFC,
+	0xD7, 0x01, 0x39, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1B, 0x00, 0x91,
+	0xFF, 0xF2, 0x00, 0x76, 0xFE, 0x11, 0x02, 0xB6, 0xFD, 0x8F, 0x01,
+	0xDE, 0x48, 0xE9, 0x05, 0xD4, 0xFB, 0x0C, 0x03, 0xF4, 0xFD, 0x2F,
+	0x01, 0x79, 0xFF, 0x22, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x32, 0x00,
+	0x43, 0xFF, 0xBA, 0x01, 0xBC, 0xFC, 0x90, 0x05, 0x8E, 0xF6, 0x68,
+	0x14, 0x99, 0x44, 0xCD, 0xF7, 0x8F, 0x02, 0x60, 0xFF, 0xEA, 0xFF,
+	0x3E, 0x00, 0xD7, 0xFF, 0x0A, 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x44,
+	0xFF, 0xD4, 0x01, 0x3B, 0xFC, 0x2A, 0x07, 0xDF, 0xF1, 0xF6, 0x29,
+	0x27, 0x36, 0xC1, 0xF1, 0x8B, 0x06, 0xD8, 0xFC, 0x66, 0x01, 0x80,
+	0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x07, 0x00, 0xBD, 0xFF,
+	0xED, 0x00, 0x9E, 0xFD, 0x6C, 0x05, 0x1F, 0xF3, 0xC0, 0x3C, 0x9E,
+	0x21, 0x28, 0xF3, 0xE2, 0x06, 0x3A, 0xFC, 0xE6, 0x01, 0x37, 0xFF,
+	0x36, 0x00, 0xFD, 0xFF, 0x12, 0x00, 0xB3, 0xFF, 0x9B, 0x00, 0x2B,
+	0xFF, 0xBD, 0x00, 0x2A, 0x00, 0x5E, 0xFC, 0x9A, 0x47, 0x82, 0x0C,
+	0x3D, 0xF9, 0x54, 0x04, 0x50, 0xFD, 0x7A, 0x01, 0x5B, 0xFF, 0x2A,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x59, 0xFF, 0x81, 0x01,
+	0x42, 0xFD, 0x72, 0x04, 0xFF, 0xF8, 0x2D, 0x0D, 0x69, 0x47, 0xEB,
+	0xFB, 0x63, 0x00, 0x9D, 0x00, 0x3C, 0xFF, 0x93, 0x00, 0xB6, 0xFF,
+	0x12, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x37,
+	0xFC, 0xED, 0x06, 0x03, 0xF3, 0x5B, 0x22, 0x37, 0x3C, 0xF4, 0xF2,
+	0x8A, 0x05, 0x89, 0xFD, 0xF9, 0x00, 0xB7, 0xFF, 0x0A, 0x00, 0x01,
+	0x00, 0xFE, 0xFF, 0x1C, 0x00, 0x84, 0xFF, 0x5C, 0x01, 0xE6, 0xFC,
+	0x77, 0x06, 0xD4, 0xF1, 0xC6, 0x36, 0x3E, 0x29, 0xF3, 0xF1, 0x29,
+	0x07, 0x38, 0xFC, 0xD7, 0x01, 0x42, 0xFF, 0x33, 0x00, 0xFD, 0xFF,
+	0x0B, 0x00, 0xD4, 0xFF, 0x46, 0x00, 0xDA, 0xFF, 0x7D, 0xFF, 0x5D,
+	0x02, 0x26, 0xF8, 0xED, 0x44, 0xB1, 0x13, 0xC7, 0xF6, 0x77, 0x05,
+	0xC8, 0xFC, 0xB6, 0x01, 0x45, 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0x00,
+	0x00, 0x22, 0x00, 0x76, 0xFF, 0x37, 0x01, 0xE4, 0xFD, 0x2C, 0x03,
+	0x94, 0xFB, 0x83, 0x06, 0xCE, 0x48, 0x05, 0x01, 0xF5, 0xFD, 0xF0,
+	0x01, 0x87, 0xFE, 0xEA, 0x00, 0x94, 0xFF, 0x1A, 0x00, 0xFE, 0xFF,
+	0x35, 0x00, 0x38, 0xFF, 0xD9, 0x01, 0x6C, 0xFC, 0x4F, 0x06, 0xC3,
+	0xF4, 0xA9, 0x1A, 0x49, 0x41, 0x2C, 0xF5, 0x15, 0x04, 0x76, 0xFE,
+	0x6E, 0x00, 0xFC, 0xFF, 0xF0, 0xFF, 0x05, 0x00, 0xFD, 0xFF, 0x2B,
+	0x00, 0x5A, 0xFF, 0xAC, 0x01, 0x6E, 0xFC, 0x0A, 0x07, 0x7E, 0xF1,
+	0xFF, 0x2F, 0x8A, 0x30, 0x7D, 0xF1, 0x03, 0x07, 0x75, 0xFC, 0xA7,
+	0x01, 0x5D, 0xFF, 0x2A, 0x00, 0xFD, 0xFF, 0x05, 0x00, 0xF2, 0xFF,
+	0xF7, 0xFF, 0x7A, 0x00, 0x62, 0xFE, 0x35, 0x04, 0xF7, 0xF4, 0xEF,
+	0x40, 0x40, 0x1B, 0x9C, 0xF4, 0x5E, 0x06, 0x66, 0xFC, 0xDB, 0x01,
+	0x38, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x1A, 0x00, 0x96, 0xFF, 0xE3,
+	0x00, 0x95, 0xFE, 0xD5, 0x01, 0x26, 0xFE, 0x98, 0x00, 0xBF, 0x48,
+	0x00, 0x07, 0x61, 0xFB, 0x46, 0x03, 0xD6, 0xFD, 0x3D, 0x01, 0x73,
+	0xFF, 0x23, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x31, 0x00, 0x46, 0xFF,
+	0xB2, 0x01, 0xD1, 0xFC, 0x62, 0x05, 0xF6, 0xF6, 0x20, 0x13, 0x2E,
+	0x45, 0x70, 0xF8, 0x34, 0x02, 0x94, 0xFF, 0xCD, 0xFF, 0x4C, 0x00,
+	0xD2, 0xFF, 0x0B, 0x00, 0xFD, 0xFF, 0x33, 0x00, 0x41, 0xFF, 0xDA,
+	0x01, 0x36, 0xFC, 0x27, 0x07, 0x05, 0xF2, 0xAA, 0x28, 0x44, 0x37,
+	0xE4, 0xF1, 0x67, 0x06, 0xF2, 0xFC, 0x55, 0x01, 0x88, 0xFF, 0x1B,
+	0x00, 0xFE, 0xFF, 0x01, 0x00, 0x0B, 0x00, 0xB2, 0xFF, 0x02, 0x01,
+	0x7A, 0xFD, 0xA2, 0x05, 0xD4, 0xF2, 0xC7, 0x3B, 0xF2, 0x22, 0xE7,
+	0xF2, 0xF5, 0x06, 0x35, 0xFC, 0xE6, 0x01, 0x38, 0xFF, 0x36, 0x00,
+	0xFD, 0xFF, 0x11, 0x00, 0xB9, 0xFF, 0x8C, 0x00, 0x4A, 0xFF, 0x83,
+	0x00, 0x91, 0x00, 0x91, 0xFB, 0x3D, 0x47, 0xB7, 0x0D, 0xCD, 0xF8,
+	0x89, 0x04, 0x36, 0xFD, 0x86, 0x01, 0x57, 0xFF, 0x2B, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x2A, 0x00, 0x5D, 0xFF, 0x75, 0x01, 0x5C, 0xFD,
+	0x3C, 0x04, 0x70, 0xF9, 0xFA, 0x0B, 0xC0, 0x47, 0xBC, 0xFC, 0xFC,
+	0xFF, 0xD6, 0x00, 0x1D, 0xFF, 0xA2, 0x00, 0xB0, 0xFF, 0x13, 0x00,
+	0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3C, 0xFC, 0xD8,
+	0x06, 0x47, 0xF3, 0x06, 0x21, 0x2A, 0x3D, 0x44, 0xF3, 0x52, 0x05,
+	0xAE, 0xFD, 0xE3, 0x00, 0xC2, 0xFF, 0x06, 0x00, 0x01, 0x00, 0xFE,
+	0xFF, 0x1F, 0x00, 0x7C, 0xFF, 0x6D, 0x01, 0xCD, 0xFC, 0x99, 0x06,
+	0xB4, 0xF1, 0xA6, 0x35, 0x89, 0x2A, 0xD0, 0xF1, 0x2B, 0x07, 0x3E,
+	0xFC, 0xD1, 0x01, 0x46, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x0A, 0x00,
+	0xD9, 0xFF, 0x37, 0x00, 0xF7, 0xFF, 0x49, 0xFF, 0xB6, 0x02, 0x86,
+	0xF7, 0x53, 0x44, 0xFB, 0x14, 0x61, 0xF6, 0xA4, 0x05, 0xB4, 0xFC,
+	0xBE, 0x01, 0x42, 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x21,
+	0x00, 0x7B, 0xFF, 0x29, 0x01, 0x01, 0xFE, 0xF1, 0x02, 0x07, 0xFC,
+	0x6E, 0x05, 0xE6, 0x48, 0xFF, 0x01, 0x84, 0xFD, 0x2C, 0x02, 0x68,
+	0xFE, 0xF9, 0x00, 0x8E, 0xFF, 0x1C, 0x00, 0xFE, 0xFF, 0x35, 0x00,
+	0x3A, 0xFF, 0xD4, 0x01, 0x7A, 0xFC, 0x2B, 0x06, 0x1E, 0xF5, 0x56,
+	0x19, 0x0C, 0x42, 0xAA, 0xF5, 0xC9, 0x03, 0xA4, 0xFE, 0x54, 0x00,
+	0x09, 0x00, 0xEB, 0xFF, 0x06, 0x00, 0xFD, 0xFF, 0x2D, 0x00, 0x55,
+	0xFF, 0xB6, 0x01, 0x5F, 0xFC, 0x17, 0x07, 0x87, 0xF1, 0xC2, 0x2E,
+	0xC0, 0x31, 0x7E, 0xF1, 0xF1, 0x06, 0x86, 0xFC, 0x9B, 0x01, 0x63,
+	0xFF, 0x28, 0x00, 0xFD, 0xFF, 0x04, 0x00, 0xF7, 0xFF, 0xEA, 0xFF,
+	0x93, 0x00, 0x36, 0xFE, 0x7D, 0x04, 0x85, 0xF4, 0x1F, 0x40, 0x94,
+	0x1C, 0x47, 0xF4, 0x7E, 0x06, 0x5A, 0xFC, 0xDF, 0x01, 0x37, 0xFF,
+	0x36, 0x00, 0xFE, 0xFF, 0x18, 0x00, 0x9C, 0xFF, 0xD4, 0x00, 0xB4,
+	0xFE, 0x9A, 0x01, 0x95, 0xFE, 0xA8, 0xFF, 0x98, 0x48, 0x1D, 0x08,
+	0xEE, 0xFA, 0x80, 0x03, 0xB9, 0xFD, 0x4B, 0x01, 0x6E, 0xFF, 0x25,
+	0x00, 0x00, 0x00, 0xFF, 0xFF, 0x30, 0x00, 0x4A, 0xFF, 0xA9, 0x01,
+	0xE7, 0xFC, 0x33, 0x05, 0x60, 0xF7, 0xDA, 0x11, 0xB8, 0x45, 0x1C,
+	0xF9, 0xD8, 0x01, 0xCA, 0xFF, 0xAF, 0xFF, 0x5A, 0x00, 0xCC, 0xFF,
+	0x0D, 0x00, 0xFD, 0xFF, 0x34, 0x00, 0x3E, 0xFF, 0xDE, 0x01, 0x33,
+	0xFC, 0x21, 0x07, 0x30, 0xF2, 0x5C, 0x27, 0x5B, 0x38, 0x0F, 0xF2,
+	0x40, 0x06, 0x0E, 0xFD, 0x43, 0x01, 0x91, 0xFF, 0x18, 0x00, 0xFF,
+	0xFF, 0x00, 0x00, 0x0F, 0x00, 0xA8, 0xFF, 0x17, 0x01, 0x57, 0xFD,
+	0xD6, 0x05, 0x90, 0xF2, 0xC8, 0x3A, 0x46, 0x24, 0xAA, 0xF2, 0x06,
+	0x07, 0x32, 0xFC, 0xE5, 0x01, 0x39, 0xFF, 0x36, 0x00, 0xFD, 0xFF,
+	0x10, 0x00, 0xBE, 0xFF, 0x7D, 0x00, 0x69, 0xFF, 0x4B, 0x00, 0xF6,
+	0x00, 0xCB, 0xFA, 0xD3, 0x46, 0xF0, 0x0E, 0x5E, 0xF8, 0xBE, 0x04,
+	0x1E, 0xFD, 0x91, 0x01, 0x52, 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0x00,
+	0x00, 0x28, 0x00, 0x62, 0xFF, 0x69, 0x01, 0x77, 0xFD, 0x04, 0x04,
+	0xE2, 0xF9, 0xCB, 0x0A, 0x0D, 0x48, 0x94, 0xFD, 0x92, 0xFF, 0x10,
+	0x01, 0xFE, 0xFE, 0xB1, 0x00, 0xAA, 0xFF, 0x15, 0x00, 0xFD, 0xFF,
+	0x36, 0x00, 0x36, 0xFF, 0xE5, 0x01, 0x43, 0xFC, 0xC0, 0x06, 0x8F,
+	0xF3, 0xB1, 0x1F, 0x18, 0x3E, 0x9B, 0xF3, 0x16, 0x05, 0xD5, 0xFD,
+	0xCC, 0x00, 0xCE, 0xFF, 0x01, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x22,
+	0x00, 0x74, 0xFF, 0x7C, 0x01, 0xB5, 0xFC, 0xB8, 0x06, 0x9C, 0xF1,
+	0x81, 0x34, 0xD1, 0x2B, 0xB3, 0xF1, 0x29, 0x07, 0x46, 0xFC, 0xCA,
+	0x01, 0x4A, 0xFF, 0x30, 0x00, 0xFD, 0xFF, 0x09, 0x00, 0xDF, 0xFF,
+	0x29, 0x00, 0x14, 0x00, 0x16, 0xFF, 0x0C, 0x03, 0xEE, 0xF6, 0xB0,
+	0x43, 0x47, 0x16, 0xFC, 0xF5, 0xCF, 0x05, 0xA1, 0xFC, 0xC6, 0x01,
+	0x3F, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x1F, 0x00, 0x81,
+	0xFF, 0x1B, 0x01, 0x20, 0xFE, 0xB6, 0x02, 0x7A, 0xFC, 0x5F, 0x04,
+	0xF4, 0x48, 0xFF, 0x02, 0x13, 0xFD, 0x67, 0x02, 0x49, 0xFE, 0x07,
+	0x01, 0x88, 0xFF, 0x1D, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3C, 0xFF,
+	0xCF, 0x01, 0x8A, 0xFC, 0x05, 0x06, 0x7B, 0xF5, 0x06, 0x18, 0xC7,
+	0x42, 0x2F, 0xF6, 0x7A, 0x03, 0xD4, 0xFE, 0x39, 0x00, 0x17, 0x00,
+	0xE6, 0xFF, 0x07, 0x00, 0xFD, 0xFF, 0x2E, 0x00, 0x50, 0xFF, 0xC0,
+	0x01, 0x53, 0xFC, 0x21, 0x07, 0x96, 0xF1, 0x82, 0x2D, 0xF2, 0x32,
+	0x86, 0xF1, 0xDB, 0x06, 0x99, 0xFC, 0x8E, 0x01, 0x6A, 0xFF, 0x25,
+	0x00, 0xFD, 0xFF, 0x03, 0x00, 0xFB, 0xFF, 0xDE, 0xFF, 0xAC, 0x00,
+	0x0B, 0xFE, 0xC1, 0x04, 0x1B, 0xF4, 0x47, 0x3F, 0xEA, 0x1D, 0xF5,
+	0xF3, 0x9C, 0x06, 0x4F, 0xFC, 0xE2, 0x01, 0x36, 0xFF, 0x36, 0x00,
+	0xFE, 0xFF, 0x16, 0x00, 0xA2, 0xFF, 0xC5, 0x00, 0xD4, 0xFE, 0x5F,
+	0x01, 0x03, 0xFF, 0xBF, 0xFE, 0x63, 0x48, 0x40, 0x09, 0x7B, 0xFA,
+	0xB9, 0x03, 0x9D, 0xFD, 0x58, 0x01, 0x69, 0xFF, 0x26, 0x00, 0x00,
+	0x00, 0xFF, 0xFF, 0x2E, 0x00, 0x4D, 0xFF, 0x9F, 0x01, 0xFE, 0xFC,
+	0x02, 0x05, 0xCB, 0xF7, 0x98, 0x10, 0x39, 0x46, 0xD0, 0xF9, 0x78,
+	0x01, 0x00, 0x00, 0x91, 0xFF, 0x69, 0x00, 0xC6, 0xFF, 0x0E, 0x00,
+	0xFD, 0xFF, 0x35, 0x00, 0x3B, 0xFF, 0xE2, 0x01, 0x31, 0xFC, 0x17,
+	0x07, 0x61, 0xF2, 0x0A, 0x26, 0x6A, 0x39, 0x41, 0xF2, 0x15, 0x06,
+	0x2C, 0xFD, 0x31, 0x01, 0x9B, 0xFF, 0x14, 0x00, 0xFF, 0xFF, 0xFF,
+	0xFF, 0x13, 0x00, 0x9E, 0xFF, 0x2B, 0x01, 0x37, 0xFD, 0x05, 0x06,
+	0x54, 0xF2, 0xC2, 0x39, 0x99, 0x25, 0x73, 0xF2, 0x14, 0x07, 0x31,
+	0xFC, 0xE3, 0x01, 0x3B, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x0E, 0x00,
+	0xC4, 0xFF, 0x6E, 0x00, 0x87, 0xFF, 0x13, 0x00, 0x58, 0x01, 0x0D,
+	0xFA, 0x61, 0x46, 0x2D, 0x10, 0xF0, 0xF7, 0xF1, 0x04, 0x05, 0xFD,
+	0x9C, 0x01, 0x4E, 0xFF, 0x2E, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x27,
+	0x00, 0x67, 0xFF, 0x5C, 0x01, 0x93, 0xFD, 0xCC, 0x03, 0x54, 0xFA,
+	0xA2, 0x09, 0x4F, 0x48, 0x73, 0xFE, 0x27, 0xFF, 0x4B, 0x01, 0xDE,
+	0xFE, 0xC0, 0x00, 0xA4, 0xFF, 0x16, 0x00, 0xFE, 0xFF, 0x36, 0x00,
+	0x36, 0xFF, 0xE3, 0x01, 0x4C, 0xFC, 0xA6, 0x06, 0xDB, 0xF3, 0x5B,
+	0x1E, 0xFC, 0x3E, 0xFA, 0xF3, 0xD7, 0x04, 0xFD, 0xFD, 0xB4, 0x00,
+	0xD9, 0xFF, 0xFD, 0xFF, 0x03, 0x00, 0xFD, 0xFF, 0x25, 0x00, 0x6D,
+	0xFF, 0x8A, 0x01, 0x9F, 0xFC, 0xD3, 0x06, 0x8A, 0xF1, 0x57, 0x33,
+	0x17, 0x2D, 0x9C, 0xF1, 0x24, 0x07, 0x4F, 0xFC, 0xC3, 0x01, 0x4E,
+	0xFF, 0x2F, 0x00, 0xFD, 0xFF, 0x08, 0x00, 0xE4, 0xFF, 0x1B, 0x00,
+	0x30, 0x00, 0xE4, 0xFE, 0x5F, 0x03, 0x5E, 0xF6, 0x02, 0x43, 0x96,
+	0x17, 0x9B, 0xF5, 0xF8, 0x05, 0x8F, 0xFC, 0xCC, 0x01, 0x3D, 0xFF,
+	0x34, 0x00, 0xFE, 0xFF, 0x1E, 0x00, 0x86, 0xFF, 0x0C, 0x01, 0x3E,
+	0xFE, 0x7B, 0x02, 0xED, 0xFC, 0x56, 0x03, 0xF5, 0x48, 0x06, 0x04,
+	0xA1, 0xFC, 0xA3, 0x02, 0x2A, 0xFE, 0x16, 0x01, 0x83, 0xFF, 0x1F,
+	0x00, 0x00, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x3E, 0xFF, 0xC8, 0x01,
+	0x9B, 0xFC, 0xDD, 0x05, 0xDC, 0xF5, 0xB6, 0x16, 0x77, 0x43, 0xBD,
+	0xF6, 0x28, 0x03, 0x05, 0xFF, 0x1D, 0x00, 0x25, 0x00, 0xE1, 0xFF,
+	0x08, 0x00, 0xFD, 0xFF, 0x30, 0x00, 0x4B, 0xFF, 0xC8, 0x01, 0x49,
+	0xFC, 0x27, 0x07, 0xAB, 0xF1, 0x3E, 0x2C, 0x1E, 0x34, 0x95, 0xF1,
+	0xC1, 0x06, 0xAE, 0xFC, 0x81, 0x01, 0x71, 0xFF, 0x23, 0x00, 0xFE,
+	0xFF, 0x02, 0x00, 0x00, 0x00, 0xD2, 0xFF, 0xC4, 0x00, 0xE2, 0xFD,
+	0x01, 0x05, 0xBA, 0xF3, 0x64, 0x3E, 0x3F, 0x1F, 0xA8, 0xF3, 0xB8,
+	0x06, 0x46, 0xFC, 0xE5, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF,
+	0x15, 0x00, 0xA8, 0xFF, 0xB6, 0x00, 0xF3, 0xFE, 0x24, 0x01, 0x6E,
+	0xFF, 0xDE, 0xFD, 0x25, 0x48, 0x68, 0x0A, 0x08, 0xFA, 0xF2, 0x03,
+	0x81, 0xFD, 0x65, 0x01, 0x64, 0xFF, 0x28, 0x00, 0x00, 0x00, 0xFF,
+	0xFF, 0x2D, 0x00, 0x51, 0xFF, 0x95, 0x01, 0x15, 0xFD, 0xCF, 0x04,
+	0x39, 0xF8, 0x59, 0x0F, 0xAF, 0x46, 0x8B, 0xFA, 0x17, 0x01, 0x38,
+	0x00, 0x73, 0xFF, 0x78, 0x00, 0xC0, 0xFF, 0x0F, 0x00, 0xFD, 0xFF,
+	0x36, 0x00, 0x39, 0xFF, 0xE4, 0x01, 0x32, 0xFC, 0x0B, 0x07, 0x97,
+	0xF2, 0xB8, 0x24, 0x71, 0x3A, 0x7B, 0xF2, 0xE6, 0x05, 0x4C, 0xFD,
+	0x1D, 0x01, 0xA4, 0xFF, 0x11, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x16,
+	0x00, 0x94, 0xFF, 0x3D, 0x01, 0x18, 0xFD, 0x32, 0x06, 0x1F, 0xF2,
+	0xB5, 0x38, 0xEB, 0x26, 0x40, 0xF2, 0x1E, 0x07, 0x32, 0xFC, 0xDF,
+	0x01, 0x3D, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x0D, 0x00, 0xCA, 0xFF,
+	0x5F, 0x00, 0xA5, 0xFF, 0xDC, 0xFF, 0xB8, 0x01, 0x57, 0xF9, 0xE5,
+	0x45, 0x6E, 0x11, 0x83, 0xF7, 0x23, 0x05, 0xEE, 0xFC, 0xA6, 0x01,
+	0x4B, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x25, 0x00, 0x6C,
+	0xFF, 0x4F, 0x01, 0xB0, 0xFD, 0x93, 0x03, 0xC7, 0xFA, 0x7D, 0x08,
+	0x86, 0x48, 0x5A, 0xFF, 0xBA, 0xFE, 0x86, 0x01, 0xBF, 0xFE, 0xCF,
+	0x00, 0x9E, 0xFF, 0x17, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF,
+	0xE0, 0x01, 0x56, 0xFC, 0x89, 0x06, 0x2B, 0xF4, 0x06, 0x1D, 0xD7,
+	0x3F, 0x61, 0xF4, 0x94, 0x04, 0x27, 0xFE, 0x9C, 0x00, 0xE6, 0xFF,
+	0xF8, 0xFF, 0x04, 0x00, 0xFD, 0xFF, 0x27, 0x00, 0x66, 0xFF, 0x97,
+	0x01, 0x8C, 0xFC, 0xEA, 0x06, 0x80, 0xF1, 0x26, 0x32, 0x58, 0x2E,
+	0x8B, 0xF1, 0x1B, 0x07, 0x5B, 0xFC, 0xBA, 0x01, 0x53, 0xFF, 0x2D,
+	0x00, 0xFD, 0xFF, 0x07, 0x00, 0xE9, 0xFF, 0x0E, 0x00, 0x4B, 0x00,
+	0xB4, 0xFE, 0xAF, 0x03, 0xD5, 0xF5, 0x4D, 0x42, 0xE6, 0x18, 0x3C,
+	0xF5, 0x1F, 0x06, 0x7F, 0xFC, 0xD3, 0x01, 0x3B, 0xFF, 0x35, 0x00,
+	0xFE, 0xFF, 0x1C, 0x00, 0x8C, 0xFF, 0xFE, 0x00, 0x5D, 0xFE, 0x3F,
+	0x02, 0x5E, 0xFD, 0x54, 0x02, 0xEC, 0x48, 0x13, 0x05, 0x2E, 0xFC,
+	0xDE, 0x02, 0x0C, 0xFE, 0x24, 0x01, 0x7D, 0xFF, 0x20, 0x00, 0x00,
+	0x00, 0xFF, 0xFF, 0x32, 0x00, 0x41, 0xFF, 0xC1, 0x01, 0xAD, 0xFC,
+	0xB2, 0x05, 0x3F, 0xF6, 0x69, 0x15, 0x1F, 0x44, 0x53, 0xF7, 0xD3,
+	0x02, 0x38, 0xFF, 0x01, 0x00, 0x33, 0x00, 0xDB, 0xFF, 0x09, 0x00,
+	0xFD, 0xFF, 0x31, 0x00, 0x47, 0xFF, 0xCF, 0x01, 0x40, 0xFC, 0x2A,
+	0x07, 0xC6, 0xF1, 0xF7, 0x2A, 0x46, 0x35, 0xAB, 0xF1, 0xA4, 0x06,
+	0xC4, 0xFC, 0x72, 0x01, 0x79, 0xFF, 0x20, 0x00, 0xFE, 0xFF, 0x02,
+	0x00, 0x04, 0x00, 0xC6, 0xFF, 0xDB, 0x00, 0xBB, 0xFD, 0x3E, 0x05,
+	0x60, 0xF3, 0x7B, 0x3D, 0x94, 0x20, 0x5E, 0xF3, 0xD0, 0x06, 0x3E,
+	0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x14, 0x00,
+	0xAE, 0xFF, 0xA7, 0x00, 0x12, 0xFF, 0xEA, 0x00, 0xD9, 0xFF, 0x03,
+	0xFD, 0xDC, 0x47, 0x95, 0x0B, 0x96, 0xF9, 0x29, 0x04, 0x65, 0xFD,
+	0x71, 0x01, 0x5F, 0xFF, 0x29, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2C,
+	0x00, 0x55, 0xFF, 0x8A, 0x01, 0x2E, 0xFD, 0x9B, 0x04, 0xA8, 0xF8,
+	0x1F, 0x0E, 0x1A, 0x47, 0x4E, 0xFB, 0xB3, 0x00, 0x70, 0x00, 0x54,
+	0xFF, 0x87, 0x00, 0xBB, 0xFF, 0x11, 0x00, 0xFD, 0xFF, 0x36, 0x00,
+	0x38, 0xFF, 0xE6, 0x01, 0x34, 0xFC, 0xFB, 0x06, 0xD2, 0xF2, 0x64,
+	0x23, 0x73, 0x3B, 0xBC, 0xF2, 0xB4, 0x05, 0x6E, 0xFD, 0x09, 0x01,
+	0xAF, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x1A, 0x00, 0x8B,
+	0xFF, 0x4F, 0x01, 0xFB, 0xFC, 0x5A, 0x06, 0xF2, 0xF1, 0xA0, 0x37,
+	0x3A, 0x28, 0x13, 0xF2, 0x25, 0x07, 0x35, 0xFC, 0xDB, 0x01, 0x40,
+	0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x0C, 0x00, 0xD0, 0xFF, 0x51, 0x00,
+	0xC3, 0xFF, 0xA6, 0xFF, 0x16, 0x02, 0xA9, 0xF8, 0x5C, 0x45, 0xB2,
+	0x12, 0x19, 0xF7, 0x52, 0x05, 0xD8, 0xFC, 0xAF, 0x01, 0x47, 0xFF,
+	0x30, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x24, 0x00, 0x71, 0xFF, 0x42,
+	0x01, 0xCD, 0xFD, 0x59, 0x03, 0x3B, 0xFB, 0x5E, 0x07, 0xB3, 0x48,
+	0x48, 0x00, 0x4B, 0xFE, 0xC2, 0x01, 0x9F, 0xFE, 0xDE, 0x00, 0x98,
+	0xFF, 0x19, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xDD, 0x01,
+	0x62, 0xFC, 0x69, 0x06, 0x7F, 0xF4, 0xB2, 0x1B, 0xAB, 0x40, 0xD0,
+	0xF4, 0x4E, 0x04, 0x53, 0xFE, 0x83, 0x00, 0xF2, 0xFF, 0xF4, 0xFF,
+	0x05, 0x00, 0xFD, 0xFF, 0x29, 0x00, 0x5F, 0xFF, 0xA3, 0x01, 0x7A,
+	0xFC, 0xFD, 0x06, 0x7C, 0xF1, 0xF2, 0x30, 0x96, 0x2F, 0x80, 0xF1,
+	0x0F, 0x07, 0x69, 0xFC, 0xB0, 0x01, 0x59, 0xFF, 0x2B, 0x00, 0xFD,
+	0xFF, 0x06, 0x00, 0xEE, 0xFF, 0x01, 0x00, 0x66, 0x00, 0x85, 0xFE,
+	0xFC, 0x03, 0x55, 0xF5, 0x8C, 0x41, 0x38, 0x1A, 0xE1, 0xF4, 0x43,
+	0x06, 0x70, 0xFC, 0xD8, 0x01, 0x39, 0xFF, 0x35, 0x00, 0xFE, 0xFF,
+	0x1B, 0x00, 0x92, 0xFF, 0xEF, 0x00, 0x7D, 0xFE, 0x04, 0x02, 0xCF,
+	0xFD, 0x58, 0x01, 0xD7, 0x48, 0x26, 0x06, 0xBB, 0xFB, 0x19, 0x03,
+	0xED, 0xFD, 0x32, 0x01, 0x77, 0xFF, 0x22, 0x00, 0x00, 0x00, 0xFF,
+	0xFF, 0x32, 0x00, 0x44, 0xFF, 0xB9, 0x01, 0xC1, 0xFC, 0x86, 0x05,
+	0xA5, 0xF6, 0x1E, 0x14, 0xBA, 0x44, 0xF0, 0xF7, 0x7B, 0x02, 0x6B,
+	0xFF, 0xE4, 0xFF, 0x41, 0x00, 0xD6, 0xFF, 0x0B, 0x00, 0xFD, 0xFF,
+	0x33, 0x00, 0x43, 0xFF, 0xD5, 0x01, 0x3A, 0xFC, 0x2A, 0x07, 0xE7,
+	0xF1, 0xAC, 0x29, 0x66, 0x36, 0xC9, 0xF1, 0x83, 0x06, 0xDD, 0xFC,
+	0x62, 0x01, 0x81, 0xFF, 0x1D, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x08,
+	0x00, 0xBB, 0xFF, 0xF1, 0x00, 0x96, 0xFD, 0x78, 0x05, 0x0E, 0xF3,
+	0x8A, 0x3C, 0xEA, 0x21, 0x19, 0xF3, 0xE6, 0x06, 0x38, 0xFC, 0xE6,
+	0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x12, 0x00, 0xB4, 0xFF,
+	0x98, 0x00, 0x32, 0xFF, 0xB0, 0x00, 0x41, 0x00, 0x30, 0xFC, 0x86,
+	0x47, 0xC6, 0x0C, 0x24, 0xF9, 0x60, 0x04, 0x4B, 0xFD, 0x7D, 0x01,
+	0x5A, 0xFF, 0x2A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x5A,
+	0xFF, 0x7E, 0x01, 0x48, 0xFD, 0x66, 0x04, 0x18, 0xF9, 0xE8, 0x0C,
+	0x7C, 0x47, 0x19, 0xFC, 0x4D, 0x00, 0xA9, 0x00, 0x35, 0xFF, 0x96,
+	0x00, 0xB5, 0xFF, 0x12, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF,
+	0xE6, 0x01, 0x38, 0xFC, 0xE9, 0x06, 0x12, 0xF3, 0x10, 0x22, 0x6E,
+	0x3C, 0x05, 0xF3, 0x7E, 0x05, 0x91, 0xFD, 0xF4, 0x00, 0xBA, 0xFF,
+	0x09, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x1D, 0x00, 0x82, 0xFF, 0x60,
+	0x01, 0xE0, 0xFC, 0x7F, 0x06, 0xCC, 0xF1, 0x85, 0x36, 0x87, 0x29,
+	0xEB, 0xF1, 0x2A, 0x07, 0x39, 0xFC, 0xD6, 0x01, 0x43, 0xFF, 0x33,
+	0x00, 0xFD, 0xFF, 0x0B, 0x00, 0xD5, 0xFF, 0x42, 0x00, 0xE1, 0xFF,
+	0x71, 0xFF, 0x71, 0x02, 0x02, 0xF8, 0xCC, 0x44, 0xFA, 0x13, 0xB0,
+	0xF6, 0x81, 0x05, 0xC3, 0xFC, 0xB8, 0x01, 0x44, 0xFF, 0x31, 0x00,
+	0xFF, 0xFF, 0x00, 0x00, 0x22, 0x00, 0x77, 0xFF, 0x34, 0x01, 0xEA,
+	0xFD, 0x1F, 0x03, 0xAE, 0xFB, 0x45, 0x06, 0xD5, 0x48, 0x3C, 0x01,
+	0xDC, 0xFD, 0xFD, 0x01, 0x80, 0xFE, 0xED, 0x00, 0x93, 0xFF, 0x1B,
+	0x00, 0xFE, 0xFF, 0x35, 0x00, 0x39, 0xFF, 0xD8, 0x01, 0x6F, 0xFC,
+	0x47, 0x06, 0xD7, 0xF4, 0x5D, 0x1A, 0x74, 0x41, 0x48, 0xF5, 0x04,
+	0x04, 0x80, 0xFE, 0x69, 0x00, 0xFF, 0xFF, 0xEF, 0xFF, 0x05, 0x00,
+	0xFD, 0xFF, 0x2B, 0x00, 0x59, 0xFF, 0xAE, 0x01, 0x6A, 0xFC, 0x0D,
+	0x07, 0x80, 0xF1, 0xB8, 0x2F, 0xCF, 0x30, 0x7D, 0xF1, 0xFF, 0x06,
+	0x78, 0xFC, 0xA5, 0x01, 0x5F, 0xFF, 0x29, 0x00, 0xFD, 0xFF, 0x05,
+	0x00, 0xF3, 0xFF, 0xF4, 0xFF, 0x80, 0x00, 0x58, 0xFE, 0x46, 0x04,
+	0xDD, 0xF4, 0xC3, 0x40, 0x8C, 0x1B, 0x89, 0xF4, 0x66, 0x06, 0x63,
+	0xFC, 0xDC, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x19, 0x00,
+	0x98, 0xFF, 0xE0, 0x00, 0x9C, 0xFE, 0xC8, 0x01, 0x3F, 0xFE, 0x62,
+	0x00, 0xB8, 0x48, 0x3F, 0x07, 0x47, 0xFB, 0x53, 0x03, 0xD0, 0xFD,
+	0x40, 0x01, 0x72, 0xFF, 0x23, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x30,
+	0x00, 0x47, 0xFF, 0xB0, 0x01, 0xD6, 0xFC, 0x58, 0x05, 0x0D, 0xF7,
+	0xD7, 0x12, 0x4E, 0x45, 0x96, 0xF8, 0x20, 0x02, 0xA0, 0xFF, 0xC7,
+	0xFF, 0x4F, 0x00, 0xD0, 0xFF, 0x0C, 0x00, 0xFD, 0xFF, 0x34, 0x00,
+	0x40, 0xFF, 0xDB, 0x01, 0x35, 0xFC, 0x26, 0x07, 0x0E, 0xF2, 0x60,
+	0x28, 0x82, 0x37, 0xED, 0xF1, 0x5E, 0x06, 0xF8, 0xFC, 0x51, 0x01,
+	0x8A, 0xFF, 0x1A, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x0C, 0x00, 0xB0,
+	0xFF, 0x07, 0x01, 0x72, 0xFD, 0xAE, 0x05, 0xC4, 0xF2, 0x90, 0x3B,
+	0x3F, 0x23, 0xD9, 0xF2, 0xF9, 0x06, 0x34, 0xFC, 0xE6, 0x01, 0x38,
+	0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x11, 0x00, 0xBA, 0xFF, 0x89, 0x00,
+	0x51, 0xFF, 0x77, 0x00, 0xA7, 0x00, 0x64, 0xFB, 0x26, 0x47, 0xFC,
+	0x0D, 0xB4, 0xF8, 0x95, 0x04, 0x31, 0xFD, 0x88, 0x01, 0x56, 0xFF,
+	0x2C, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x29, 0x00, 0x5E, 0xFF, 0x72,
+	0x01, 0x62, 0xFD, 0x2F, 0x04, 0x89, 0xF9, 0xB6, 0x0B, 0xD2, 0x47,
+	0xEB, 0xFC, 0xE4, 0xFF, 0xE3, 0x00, 0x16, 0xFF, 0xA5, 0x00, 0xAF,
+	0xFF, 0x13, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01,
+	0x3E, 0xFC, 0xD3, 0x06, 0x56, 0xF3, 0xBA, 0x20, 0x61, 0x3D, 0x56,
+	0xF3, 0x45, 0x05, 0xB7, 0xFD, 0xDE, 0x00, 0xC5, 0xFF, 0x05, 0x00,
+	0x02, 0x00, 0xFE, 0xFF, 0x20, 0x00, 0x7A, 0xFF, 0x70, 0x01, 0xC7,
+	0xFC, 0xA0, 0x06, 0xAE, 0xF1, 0x65, 0x35, 0xD1, 0x2A, 0xCA, 0xF1,
+	0x2A, 0x07, 0x40, 0xFC, 0xD0, 0x01, 0x47, 0xFF, 0x32, 0x00, 0xFD,
+	0xFF, 0x09, 0x00, 0xDB, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0x3D, 0xFF,
+	0xC9, 0x02, 0x64, 0xF7, 0x2F, 0x44, 0x44, 0x15, 0x4A, 0xF6, 0xAD,
+	0x05, 0xAF, 0xFC, 0xC0, 0x01, 0x41, 0xFF, 0x32, 0x00, 0xFF, 0xFF,
+	0x00, 0x00, 0x21, 0x00, 0x7C, 0xFF, 0x26, 0x01, 0x08, 0xFE, 0xE4,
+	0x02, 0x21, 0xFC, 0x31, 0x05, 0xEB, 0x48, 0x37, 0x02, 0x6B, 0xFD,
+	0x39, 0x02, 0x61, 0xFE, 0xFC, 0x00, 0x8D, 0xFF, 0x1C, 0x00, 0xFE,
+	0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xD3, 0x01, 0x7D, 0xFC, 0x23, 0x06,
+	0x32, 0xF5, 0x0C, 0x19, 0x38, 0x42, 0xC7, 0xF5, 0xB8, 0x03, 0xAF,
+	0xFE, 0x4E, 0x00, 0x0C, 0x00, 0xEA, 0xFF, 0x06, 0x00, 0xFD, 0xFF,
+	0x2D, 0x00, 0x54, 0xFF, 0xB8, 0x01, 0x5D, 0xFC, 0x1A, 0x07, 0x8A,
+	0xF1, 0x7B, 0x2E, 0x04, 0x32, 0x7F, 0xF1, 0xEC, 0x06, 0x8A, 0xFC,
+	0x98, 0x01, 0x65, 0xFF, 0x27, 0x00, 0xFD, 0xFF, 0x04, 0x00, 0xF8,
+	0xFF, 0xE7, 0xFF, 0x99, 0x00, 0x2C, 0xFE, 0x8C, 0x04, 0x6D, 0xF4,
+	0xF0, 0x3F, 0xE0, 0x1C, 0x34, 0xF4, 0x85, 0x06, 0x57, 0xFC, 0xE0,
+	0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x18, 0x00, 0x9E, 0xFF,
+	0xD1, 0x00, 0xBB, 0xFE, 0x8D, 0x01, 0xAE, 0xFE, 0x74, 0xFF, 0x8D,
+	0x48, 0x5D, 0x08, 0xD4, 0xFA, 0x8D, 0x03, 0xB3, 0xFD, 0x4E, 0x01,
+	0x6D, 0xFF, 0x25, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2F, 0x00, 0x4A,
+	0xFF, 0xA7, 0x01, 0xEC, 0xFC, 0x28, 0x05, 0x77, 0xF7, 0x92, 0x11,
+	0xD7, 0x45, 0x43, 0xF9, 0xC3, 0x01, 0xD6, 0xFF, 0xA9, 0xFF, 0x5E,
+	0x00, 0xCB, 0xFF, 0x0D, 0x00, 0xFD, 0xFF, 0x34, 0x00, 0x3D, 0xFF,
+	0xDF, 0x01, 0x32, 0xFC, 0x1F, 0x07, 0x3B, 0xF2, 0x11, 0x27, 0x97,
+	0x38, 0x19, 0xF2, 0x36, 0x06, 0x15, 0xFD, 0x3F, 0x01, 0x93, 0xFF,
+	0x17, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x10, 0x00, 0xA6, 0xFF, 0x1B,
+	0x01, 0x50, 0xFD, 0xE1, 0x05, 0x82, 0xF2, 0x8F, 0x3A, 0x92, 0x24,
+	0x9D, 0xF2, 0x09, 0x07, 0x32, 0xFC, 0xE4, 0x01, 0x39, 0xFF, 0x36,
+	0x00, 0xFD, 0xFF, 0x0F, 0x00, 0xC0, 0xFF, 0x7A, 0x00, 0x70, 0xFF,
+	0x3E, 0x00, 0x0C, 0x01, 0xA1, 0xFA, 0xBB, 0x46, 0x36, 0x0F, 0x45,
+	0xF8, 0xC9, 0x04, 0x18, 0xFD, 0x93, 0x01, 0x52, 0xFF, 0x2D, 0x00,
+	0xFF, 0xFF, 0x00, 0x00, 0x28, 0x00, 0x63, 0xFF, 0x66, 0x01, 0x7D,
+	0xFD, 0xF8, 0x03, 0xFB, 0xF9, 0x89, 0x0A, 0x1D, 0x48, 0xC5, 0xFD,
+	0x7A, 0xFF, 0x1D, 0x01, 0xF7, 0xFE, 0xB4, 0x00, 0xA9, 0xFF, 0x15,
+	0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE5, 0x01, 0x45, 0xFC,
+	0xBB, 0x06, 0xA0, 0xF3, 0x64, 0x1F, 0x4A, 0x3E, 0xB0, 0xF3, 0x08,
+	0x05, 0xDE, 0xFD, 0xC7, 0x00, 0xD0, 0xFF, 0x00, 0x00, 0x02, 0x00,
+	0xFE, 0xFF, 0x23, 0x00, 0x72, 0xFF, 0x7F, 0x01, 0xB0, 0xFC, 0xBE,
+	0x06, 0x97, 0xF1, 0x3F, 0x34, 0x19, 0x2C, 0xAD, 0xF1, 0x28, 0x07,
+	0x48, 0xFC, 0xC9, 0x01, 0x4B, 0xFF, 0x30, 0x00, 0xFD, 0xFF, 0x08,
+	0x00, 0xE0, 0xFF, 0x26, 0x00, 0x1A, 0x00, 0x0B, 0xFF, 0x1E, 0x03,
+	0xCD, 0xF6, 0x89, 0x43, 0x91, 0x16, 0xE7, 0xF5, 0xD8, 0x05, 0x9D,
+	0xFC, 0xC7, 0x01, 0x3E, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0x00, 0x00,
+	0x1F, 0x00, 0x82, 0xFF, 0x18, 0x01, 0x27, 0xFE, 0xA9, 0x02, 0x94,
+	0xFC, 0x24, 0x04, 0xF5, 0x48, 0x39, 0x03, 0xF9, 0xFC, 0x74, 0x02,
+	0x42, 0xFE, 0x0B, 0x01, 0x87, 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0x34,
+	0x00, 0x3C, 0xFF, 0xCD, 0x01, 0x8E, 0xFC, 0xFC, 0x05, 0x90, 0xF5,
+	0xBB, 0x17, 0xEE, 0x42, 0x4E, 0xF6, 0x68, 0x03, 0xDF, 0xFE, 0x33,
+	0x00, 0x1A, 0x00, 0xE5, 0xFF, 0x07, 0x00, 0xFD, 0xFF, 0x2F, 0x00,
+	0x4F, 0xFF, 0xC2, 0x01, 0x51, 0xFC, 0x23, 0x07, 0x9A, 0xF1, 0x3A,
+	0x2D, 0x35, 0x33, 0x89, 0xF1, 0xD5, 0x06, 0x9D, 0xFC, 0x8B, 0x01,
+	0x6C, 0xFF, 0x25, 0x00, 0xFD, 0xFF, 0x03, 0x00, 0xFC, 0xFF, 0xDB,
+	0xFF, 0xB2, 0x00, 0x02, 0xFE, 0xCF, 0x04, 0x05, 0xF4, 0x16, 0x3F,
+	0x36, 0x1E, 0xE4, 0xF3, 0xA3, 0x06, 0x4D, 0xFC, 0xE3, 0x01, 0x36,
+	0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x16, 0x00, 0xA4, 0xFF, 0xC2, 0x00,
+	0xDB, 0xFE, 0x52, 0x01, 0x1B, 0xFF, 0x8D, 0xFE, 0x57, 0x48, 0x81,
+	0x09, 0x61, 0xFA, 0xC6, 0x03, 0x96, 0xFD, 0x5B, 0x01, 0x68, 0xFF,
+	0x26, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2E, 0x00, 0x4E, 0xFF, 0x9D,
+	0x01, 0x03, 0xFD, 0xF7, 0x04, 0xE3, 0xF7, 0x51, 0x10, 0x55, 0x46,
+	0xF9, 0xF9, 0x63, 0x01, 0x0D, 0x00, 0x8B, 0xFF, 0x6D, 0x00, 0xC5,
+	0xFF, 0x0E, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3B, 0xFF, 0xE2, 0x01,
+	0x31, 0xFC, 0x15, 0x07, 0x6D, 0xF2, 0xBF, 0x25, 0xA5, 0x39, 0x4D,
+	0xF2, 0x0B, 0x06, 0x33, 0xFD, 0x2D, 0x01, 0x9D, 0xFF, 0x13, 0x00,
+	0xFF, 0xFF, 0xFF, 0xFF, 0x14, 0x00, 0x9C, 0xFF, 0x2F, 0x01, 0x30,
+	0xFD, 0x10, 0x06, 0x47, 0xF2, 0x87, 0x39, 0xE5, 0x25, 0x67, 0xF2,
+	0x16, 0x07, 0x31, 0xFC, 0xE2, 0x01, 0x3B, 0xFF, 0x35, 0x00, 0xFD,
+	0xFF, 0x0E, 0x00, 0xC6, 0xFF, 0x6B, 0x00, 0x8E, 0xFF, 0x06, 0x00,
+	0x6E, 0x01, 0xE4, 0xF9, 0x48, 0x46, 0x75, 0x10, 0xD7, 0xF7, 0xFC,
+	0x04, 0x00, 0xFD, 0x9E, 0x01, 0x4E, 0xFF, 0x2E, 0x00, 0xFF, 0xFF,
+	0x00, 0x00, 0x26, 0x00, 0x68, 0xFF, 0x59, 0x01, 0x99, 0xFD, 0xC0,
+	0x03, 0x6E, 0xFA, 0x61, 0x09, 0x5D, 0x48, 0xA6, 0xFE, 0x0F, 0xFF,
+	0x58, 0x01, 0xD7, 0xFE, 0xC3, 0x00, 0xA3, 0xFF, 0x16, 0x00, 0xFE,
+	0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE3, 0x01, 0x4E, 0xFC, 0xA0, 0x06,
+	0xED, 0xF3, 0x0F, 0x1E, 0x2D, 0x3F, 0x10, 0xF4, 0xC8, 0x04, 0x07,
+	0xFE, 0xAF, 0x00, 0xDC, 0xFF, 0xFC, 0xFF, 0x03, 0x00, 0xFD, 0xFF,
+	0x25, 0x00, 0x6B, 0xFF, 0x8D, 0x01, 0x9B, 0xFC, 0xD8, 0x06, 0x87,
+	0xF1, 0x13, 0x33, 0x5E, 0x2D, 0x98, 0xF1, 0x22, 0x07, 0x52, 0xFC,
+	0xC1, 0x01, 0x4F, 0xFF, 0x2F, 0x00, 0xFD, 0xFF, 0x07, 0x00, 0xE5,
+	0xFF, 0x18, 0x00, 0x36, 0x00, 0xD9, 0xFE, 0x71, 0x03, 0x3F, 0xF6,
+	0xDB, 0x42, 0xE0, 0x17, 0x86, 0xF5, 0x00, 0x06, 0x8C, 0xFC, 0xCE,
+	0x01, 0x3C, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0x1D, 0x00, 0x88, 0xFF,
+	0x09, 0x01, 0x45, 0xFE, 0x6E, 0x02, 0x06, 0xFD, 0x1C, 0x03, 0xF4,
+	0x48, 0x41, 0x04, 0x87, 0xFC, 0xB0, 0x02, 0x23, 0xFE, 0x19, 0x01,
+	0x81, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x3F,
+	0xFF, 0xC6, 0x01, 0x9F, 0xFC, 0xD3, 0x05, 0xF1, 0xF5, 0x6C, 0x16,
+	0x9E, 0x43, 0xDD, 0xF6, 0x15, 0x03, 0x10, 0xFF, 0x17, 0x00, 0x28,
+	0x00, 0xDF, 0xFF, 0x09, 0x00, 0xFD, 0xFF, 0x30, 0x00, 0x4A, 0xFF,
+	0xCA, 0x01, 0x47, 0xFC, 0x28, 0x07, 0xB0, 0xF1, 0xF5, 0x2B, 0x60,
+	0x34, 0x9A, 0xF1, 0xBB, 0x06, 0xB3, 0xFC, 0x7D, 0x01, 0x73, 0xFF,
+	0x22, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0x01, 0x00, 0xCF, 0xFF, 0xC9,
+	0x00, 0xDA, 0xFD, 0x0F, 0x05, 0xA5, 0xF3, 0x31, 0x3E, 0x8A, 0x1F,
+	0x97, 0xF3, 0xBD, 0x06, 0x44, 0xFC, 0xE5, 0x01, 0x36, 0xFF, 0x36,
+	0x00, 0xFD, 0xFF, 0x15, 0x00, 0xAA, 0xFF, 0xB3, 0x00, 0xFA, 0xFE,
+	0x17, 0x01, 0x86, 0xFF, 0xAC, 0xFD, 0x16, 0x48, 0xAA, 0x0A, 0xEE,
+	0xF9, 0xFE, 0x03, 0x7A, 0xFD, 0x67, 0x01, 0x63, 0xFF, 0x28, 0x00,
+	0x00, 0x00, 0xFF, 0xFF, 0x2D, 0x00, 0x52, 0xFF, 0x92, 0x01, 0x1B,
+	0xFD, 0xC4, 0x04, 0x51, 0xF8, 0x13, 0x0F, 0xC8, 0x46, 0xB6, 0xFA,
+	0x01, 0x01, 0x44, 0x00, 0x6C, 0xFF, 0x7B, 0x00, 0xBF, 0xFF, 0x10,
+	0x00, 0xFD, 0xFF, 0x36, 0x00, 0x39, 0xFF, 0xE5, 0x01, 0x32, 0xFC,
+	0x08, 0x07, 0xA4, 0xF2, 0x6D, 0x24, 0xAD, 0x3A, 0x88, 0xF2, 0xDB,
+	0x05, 0x53, 0xFD, 0x19, 0x01, 0xA7, 0xFF, 0x10, 0x00, 0x00, 0x00,
+	0xFF, 0xFF, 0x17, 0x00, 0x92, 0xFF, 0x41, 0x01, 0x11, 0xFD, 0x3B,
+	0x06, 0x14, 0xF2, 0x78, 0x38, 0x36, 0x27, 0x35, 0xF2, 0x20, 0x07,
+	0x33, 0xFC, 0xDF, 0x01, 0x3E, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x0D,
+	0x00, 0xCB, 0xFF, 0x5C, 0x00, 0xAC, 0xFF, 0xD0, 0xFF, 0xCD, 0x01,
+	0x30, 0xF9, 0xC8, 0x45, 0xB6, 0x11, 0x6B, 0xF7, 0x2D, 0x05, 0xE9,
+	0xFC, 0xA8, 0x01, 0x4A, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0x00, 0x00,
+	0x25, 0x00, 0x6D, 0xFF, 0x4C, 0x01, 0xB6, 0xFD, 0x86, 0x03, 0xE1,
+	0xFA, 0x3D, 0x08, 0x92, 0x48, 0x8E, 0xFF, 0xA1, 0xFE, 0x93, 0x01,
+	0xB8, 0xFE, 0xD3, 0x00, 0x9D, 0xFF, 0x18, 0x00, 0xFE, 0xFF, 0x36,
+	0x00, 0x37, 0xFF, 0xE0, 0x01, 0x58, 0xFC, 0x82, 0x06, 0x3E, 0xF4,
+	0xBA, 0x1C, 0x07, 0x40, 0x79, 0xF4, 0x84, 0x04, 0x31, 0xFE, 0x96,
+	0x00, 0xE8, 0xFF, 0xF7, 0xFF, 0x04, 0x00, 0xFD, 0xFF, 0x28, 0x00,
+	0x64, 0xFF, 0x9A, 0x01, 0x88, 0xFC, 0xEE, 0x06, 0x7E, 0xF1, 0xE3,
+	0x31, 0x9F, 0x2E, 0x88, 0xF1, 0x19, 0x07, 0x5E, 0xFC, 0xB7, 0x01,
+	0x54, 0xFF, 0x2D, 0x00, 0xFD, 0xFF, 0x06, 0x00, 0xEA, 0xFF, 0x0B,
+	0x00, 0x51, 0x00, 0xAA, 0xFE, 0xC0, 0x03, 0xB8, 0xF5, 0x21, 0x42,
+	0x31, 0x19, 0x28, 0xF5, 0x27, 0x06, 0x7C, 0xFC, 0xD4, 0x01, 0x3A,
+	0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1C, 0x00, 0x8D, 0xFF, 0xFA, 0x00,
+	0x64, 0xFE, 0x32, 0x02, 0x78, 0xFD, 0x1B, 0x02, 0xEA, 0x48, 0x50,
+	0x05, 0x14, 0xFC, 0xEB, 0x02, 0x05, 0xFE, 0x27, 0x01, 0x7C, 0xFF,
+	0x21, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x32, 0x00, 0x41, 0xFF, 0xBF,
+	0x01, 0xB2, 0xFC, 0xA9, 0x05, 0x55, 0xF6, 0x20, 0x15, 0x42, 0x44,
+	0x75, 0xF7, 0xBF, 0x02, 0x43, 0xFF, 0xFA, 0xFF, 0x36, 0x00, 0xDA,
+	0xFF, 0x0A, 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x46, 0xFF, 0xD1, 0x01,
+	0x3F, 0xFC, 0x2B, 0x07, 0xCD, 0xF1, 0xAE, 0x2A, 0x86, 0x35, 0xB1,
+	0xF1, 0x9D, 0x06, 0xCA, 0xFC, 0x6E, 0x01, 0x7B, 0xFF, 0x20, 0x00,
+	0xFE, 0xFF, 0x02, 0x00, 0x05, 0x00, 0xC3, 0xFF, 0xE0, 0x00, 0xB3,
+	0xFD, 0x4B, 0x05, 0x4D, 0xF3, 0x45, 0x3D, 0xE0, 0x20, 0x4F, 0xF3,
+	0xD5, 0x06, 0x3D, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD,
+	0xFF, 0x13, 0x00, 0xAF, 0xFF, 0xA4, 0x00, 0x19, 0xFF, 0xDD, 0x00,
+	0xF0, 0xFF, 0xD4, 0xFC, 0xC9, 0x47, 0xD8, 0x0B, 0x7C, 0xF9, 0x35,
+	0x04, 0x5F, 0xFD, 0x74, 0x01, 0x5E, 0xFF, 0x29, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x2C, 0x00, 0x56, 0xFF, 0x87, 0x01, 0x34, 0xFD, 0x8F,
+	0x04, 0xC0, 0xF8, 0xD9, 0x0D, 0x31, 0x47, 0x7B, 0xFB, 0x9C, 0x00,
+	0x7D, 0x00, 0x4D, 0xFF, 0x8A, 0x00, 0xB9, 0xFF, 0x11, 0x00, 0xFD,
+	0xFF, 0x36, 0x00, 0x38, 0xFF, 0xE6, 0x01, 0x35, 0xFC, 0xF7, 0x06,
+	0xE0, 0xF2, 0x18, 0x23, 0xAB, 0x3B, 0xCC, 0xF2, 0xA8, 0x05, 0x76,
+	0xFD, 0x04, 0x01, 0xB1, 0xFF, 0x0C, 0x00, 0x00, 0x00, 0xFE, 0xFF,
+	0x1A, 0x00, 0x89, 0xFF, 0x53, 0x01, 0xF5, 0xFC, 0x63, 0x06, 0xE9,
+	0xF1, 0x63, 0x37, 0x85, 0x28, 0x09, 0xF2, 0x27, 0x07, 0x35, 0xFC,
+	0xDA, 0x01, 0x40, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x0C, 0x00, 0xD1,
+	0xFF, 0x4E, 0x00, 0xCA, 0xFF, 0x9A, 0xFF, 0x2A, 0x02, 0x83, 0xF8,
+	0x3F, 0x45, 0xFB, 0x12, 0x01, 0xF7, 0x5D, 0x05, 0xD3, 0xFC, 0xB1,
+	0x01, 0x46, 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x23, 0x00,
+	0x73, 0xFF, 0x3F, 0x01, 0xD3, 0xFD, 0x4C, 0x03, 0x54, 0xFB, 0x1F,
+	0x07, 0xBB, 0x48, 0x7D, 0x00, 0x33, 0xFE, 0xCF, 0x01, 0x98, 0xFE,
+	0xE2, 0x00, 0x97, 0xFF, 0x19, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x38,
+	0xFF, 0xDC, 0x01, 0x64, 0xFC, 0x62, 0x06, 0x93, 0xF4, 0x66, 0x1B,
+	0xD9, 0x40, 0xEA, 0xF4, 0x3E, 0x04, 0x5D, 0xFE, 0x7D, 0x00, 0xF5,
+	0xFF, 0xF3, 0xFF, 0x05, 0x00, 0xFD, 0xFF, 0x2A, 0x00, 0x5E, 0xFF,
+	0xA6, 0x01, 0x76, 0xFC, 0x01, 0x07, 0x7D, 0xF1, 0xAD, 0x30, 0xDC,
+	0x2F, 0x7F, 0xF1, 0x0C, 0x07, 0x6C, 0xFC, 0xAD, 0x01, 0x5A, 0xFF,
+	0x2B, 0x00, 0xFD, 0xFF, 0x05, 0x00, 0xEF, 0xFF, 0xFE, 0xFF, 0x6C,
+	0x00, 0x7B, 0xFE, 0x0C, 0x04, 0x3A, 0xF5, 0x5F, 0x41, 0x83, 0x1A,
+	0xCD, 0xF4, 0x4B, 0x06, 0x6D, 0xFC, 0xD9, 0x01, 0x39, 0xFF, 0x35,
+	0x00, 0xFE, 0xFF, 0x1A, 0x00, 0x93, 0xFF, 0xEC, 0x00, 0x83, 0xFE,
+	0xF7, 0x01, 0xE8, 0xFD, 0x21, 0x01, 0xD2, 0x48, 0x64, 0x06, 0xA1,
+	0xFB, 0x26, 0x03, 0xE7, 0xFD, 0x35, 0x01, 0x76, 0xFF, 0x22, 0x00,
+	0x00, 0x00, 0xFF, 0xFF, 0x31, 0x00, 0x44, 0xFF, 0xB7, 0x01, 0xC5,
+	0xFC, 0x7C, 0x05, 0xBC, 0xF6, 0xD5, 0x13, 0xDC, 0x44, 0x14, 0xF8,
+	0x67, 0x02, 0x77, 0xFF, 0xDD, 0xFF, 0x44, 0x00, 0xD5, 0xFF, 0x0B,
+	0x00, 0xFD, 0xFF, 0x33, 0x00, 0x42, 0xFF, 0xD7, 0x01, 0x39, 0xFC,
+	0x29, 0x07, 0xEF, 0xF1, 0x62, 0x29, 0xA5, 0x36, 0xD0, 0xF1, 0x7B,
+	0x06, 0xE3, 0xFC, 0x5E, 0x01, 0x83, 0xFF, 0x1D, 0x00, 0xFE, 0xFF,
+	0x01, 0x00, 0x09, 0x00, 0xB8, 0xFF, 0xF6, 0x00, 0x8D, 0xFD, 0x84,
+	0x05, 0xFD, 0xF2, 0x52, 0x3C, 0x35, 0x22, 0x0B, 0xF3, 0xEB, 0x06,
+	0x37, 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x12,
+	0x00, 0xB5, 0xFF, 0x94, 0x00, 0x39, 0xFF, 0xA3, 0x00, 0x58, 0x00,
+	0x02, 0xFC, 0x73, 0x47, 0x0B, 0x0D, 0x0B, 0xF9, 0x6C, 0x04, 0x45,
+	0xFD, 0x80, 0x01, 0x59, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x2A, 0x00, 0x5B, 0xFF, 0x7C, 0x01, 0x4E, 0xFD, 0x5A, 0x04, 0x31,
+	0xF9, 0xA4, 0x0C, 0x90, 0x47, 0x47, 0xFC, 0x36, 0x00, 0xB6, 0x00,
+	0x2E, 0xFF, 0x99, 0x00, 0xB3, 0xFF, 0x12, 0x00, 0xFD, 0xFF, 0x36,
+	0x00, 0x37, 0xFF, 0xE6, 0x01, 0x39, 0xFC, 0xE4, 0x06, 0x21, 0xF3,
+	0xC4, 0x21, 0xA5, 0x3C, 0x16, 0xF3, 0x72, 0x05, 0x9A, 0xFD, 0xEF,
+	0x00, 0xBC, 0xFF, 0x08, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x1E, 0x00,
+	0x80, 0xFF, 0x64, 0x01, 0xDA, 0xFC, 0x87, 0x06, 0xC5, 0xF1, 0x46,
+	0x36, 0xD1, 0x29, 0xE3, 0xF1, 0x2A, 0x07, 0x3A, 0xFC, 0xD5, 0x01,
+	0x44, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x0A, 0x00, 0xD6, 0xFF, 0x3F,
+	0x00, 0xE7, 0xFF, 0x65, 0xFF, 0x85, 0x02, 0xDE, 0xF7, 0xA9, 0x44,
+	0x43, 0x14, 0x99, 0xF6, 0x8B, 0x05, 0xBF, 0xFC, 0xBA, 0x01, 0x43,
+	0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x22, 0x00, 0x78, 0xFF,
+	0x31, 0x01, 0xF1, 0xFD, 0x12, 0x03, 0xC7, 0xFB, 0x07, 0x06, 0xDB,
+	0x48, 0x73, 0x01, 0xC3, 0xFD, 0x0A, 0x02, 0x79, 0xFE, 0xF1, 0x00,
+	0x91, 0xFF, 0x1B, 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x39, 0xFF, 0xD7,
+	0x01, 0x72, 0xFC, 0x3F, 0x06, 0xEB, 0xF4, 0x12, 0x1A, 0xA1, 0x41,
+	0x63, 0xF5, 0xF3, 0x03, 0x8A, 0xFE, 0x63, 0x00, 0x02, 0x00, 0xEE,
+	0xFF, 0x06, 0x00, 0xFD, 0xFF, 0x2C, 0x00, 0x58, 0xFF, 0xB1, 0x01,
+	0x67, 0xFC, 0x10, 0x07, 0x81, 0xF1, 0x73, 0x2F, 0x15, 0x31, 0x7C,
+	0xF1, 0xFB, 0x06, 0x7C, 0xFC, 0xA2, 0x01, 0x60, 0xFF, 0x29, 0x00,
+	0xFD, 0xFF, 0x04, 0x00, 0xF4, 0xFF, 0xF1, 0xFF, 0x85, 0x00, 0x4E,
+	0xFE, 0x56, 0x04, 0xC3, 0xF4, 0x95, 0x40, 0xD8, 0x1B, 0x76, 0xF4,
+	0x6D, 0x06, 0x60, 0xFC, 0xDD, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE,
+	0xFF, 0x19, 0x00, 0x99, 0xFF, 0xDD, 0x00, 0xA3, 0xFE, 0xBB, 0x01,
+	0x58, 0xFE, 0x2D, 0x00, 0xAF, 0x48, 0x7E, 0x07, 0x2E, 0xFB, 0x60,
+	0x03, 0xC9, 0xFD, 0x43, 0x01, 0x71, 0xFF, 0x24, 0x00, 0x00, 0x00,
+	0xFF, 0xFF, 0x30, 0x00, 0x48, 0xFF, 0xAE, 0x01, 0xDB, 0xFC, 0x4D,
+	0x05, 0x24, 0xF7, 0x8E, 0x12, 0x6D, 0x45, 0xBC, 0xF8, 0x0C, 0x02,
+	0xAC, 0xFF, 0xC0, 0xFF, 0x52, 0x00, 0xCF, 0xFF, 0x0C, 0x00, 0xFD,
+	0xFF, 0x34, 0x00, 0x3F, 0xFF, 0xDC, 0x01, 0x34, 0xFC, 0x25, 0x07,
+	0x18, 0xF2, 0x15, 0x28, 0xBF, 0x37, 0xF7, 0xF1, 0x56, 0x06, 0xFE,
+	0xFC, 0x4D, 0x01, 0x8C, 0xFF, 0x19, 0x00, 0xFF, 0xFF, 0x00, 0x00,
+	0x0D, 0x00, 0xAE, 0xFF, 0x0B, 0x01, 0x6A, 0xFD, 0xBA, 0x05, 0xB4,
+	0xF2, 0x58, 0x3B, 0x8A, 0x23, 0xCB, 0xF2, 0xFD, 0x06, 0x34, 0xFC,
+	0xE6, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x10, 0x00, 0xBB,
+	0xFF, 0x85, 0x00, 0x58, 0xFF, 0x6A, 0x00, 0xBE, 0x00, 0x38, 0xFB,
+	0x0F, 0x47, 0x42, 0x0E, 0x9B, 0xF8, 0xA1, 0x04, 0x2B, 0xFD, 0x8B,
+	0x01, 0x55, 0xFF, 0x2C, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x29, 0x00,
+	0x5F, 0xFF, 0x70, 0x01, 0x68, 0xFD, 0x23, 0x04, 0xA2, 0xF9, 0x73,
+	0x0B, 0xE4, 0x47, 0x1B, 0xFD, 0xCD, 0xFF, 0xF0, 0x00, 0x0F, 0xFF,
+	0xA9, 0x00, 0xAE, 0xFF, 0x14, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36,
+	0xFF, 0xE6, 0x01, 0x3F, 0xFC, 0xCE, 0x06, 0x66, 0xF3, 0x6F, 0x20,
+	0x96, 0x3D, 0x69, 0xF3, 0x38, 0x05, 0xBF, 0xFD, 0xD9, 0x00, 0xC7,
+	0xFF, 0x04, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x20, 0x00, 0x78, 0xFF,
+	0x74, 0x01, 0xC2, 0xFC, 0xA7, 0x06, 0xA8, 0xF1, 0x25, 0x35, 0x1B,
+	0x2B, 0xC2, 0xF1, 0x2A, 0x07, 0x41, 0xFC, 0xCE, 0x01, 0x47, 0xFF,
+	0x31, 0x00, 0xFD, 0xFF, 0x09, 0x00, 0xDC, 0xFF, 0x31, 0x00, 0x04,
+	0x00, 0x32, 0xFF, 0xDC, 0x02, 0x42, 0xF7, 0x0B, 0x44, 0x8E, 0x15,
+	0x34, 0xF6, 0xB7, 0x05, 0xAB, 0xFC, 0xC1, 0x01, 0x40, 0xFF, 0x33,
+	0x00, 0xFF, 0xFF, 0x00, 0x00, 0x20, 0x00, 0x7E, 0xFF, 0x23, 0x01,
+	0x0F, 0xFE, 0xD7, 0x02, 0x3B, 0xFC, 0xF5, 0x04, 0xED, 0x48, 0x70,
+	0x02, 0x52, 0xFD, 0x46, 0x02, 0x5A, 0xFE, 0xFF, 0x00, 0x8B, 0xFF,
+	0x1C, 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x3B, 0xFF, 0xD2, 0x01, 0x81,
+	0xFC, 0x1A, 0x06, 0x47, 0xF5, 0xC1, 0x18, 0x60, 0x42, 0xE4, 0xF5,
+	0xA6, 0x03, 0xB9, 0xFE, 0x48, 0x00, 0x0F, 0x00, 0xE9, 0xFF, 0x07,
+	0x00, 0xFD, 0xFF, 0x2E, 0x00, 0x53, 0xFF, 0xBB, 0x01, 0x5A, 0xFC,
+	0x1C, 0x07, 0x8D, 0xF1, 0x34, 0x2E, 0x48, 0x32, 0x81, 0xF1, 0xE7,
+	0x06, 0x8E, 0xFC, 0x96, 0x01, 0x66, 0xFF, 0x27, 0x00, 0xFD, 0xFF,
+	0x04, 0x00, 0xF9, 0xFF, 0xE4, 0xFF, 0x9F, 0x00, 0x23, 0xFE, 0x9B,
+	0x04, 0x55, 0xF4, 0xC0, 0x3F, 0x2C, 0x1D, 0x22, 0xF4, 0x8C, 0x06,
+	0x55, 0xFC, 0xE1, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x17,
+	0x00, 0x9F, 0xFF, 0xCE, 0x00, 0xC2, 0xFE, 0x80, 0x01, 0xC6, 0xFE,
+	0x40, 0xFF, 0x81, 0x48, 0x9E, 0x08, 0xBA, 0xFA, 0x9A, 0x03, 0xAC,
+	0xFD, 0x51, 0x01, 0x6C, 0xFF, 0x25, 0x00, 0x00, 0x00, 0xFF, 0xFF,
+	0x2F, 0x00, 0x4B, 0xFF, 0xA4, 0x01, 0xF1, 0xFC, 0x1D, 0x05, 0x8F,
+	0xF7, 0x4A, 0x11, 0xF2, 0x45, 0x6B, 0xF9, 0xAE, 0x01, 0xE2, 0xFF,
+	0xA2, 0xFF, 0x61, 0x00, 0xC9, 0xFF, 0x0D, 0x00, 0xFD, 0xFF, 0x35,
+	0x00, 0x3D, 0xFF, 0xE0, 0x01, 0x32, 0xFC, 0x1D, 0x07, 0x45, 0xF2,
+	0xC6, 0x26, 0xD3, 0x38, 0x24, 0xF2, 0x2D, 0x06, 0x1B, 0xFD, 0x3B,
+	0x01, 0x95, 0xFF, 0x16, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x11, 0x00,
+	0xA3, 0xFF, 0x20, 0x01, 0x49, 0xFD, 0xEB, 0x05, 0x74, 0xF2, 0x54,
+	0x3A, 0xDD, 0x24, 0x91, 0xF2, 0x0C, 0x07, 0x32, 0xFC, 0xE4, 0x01,
+	0x3A, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x0F, 0x00, 0xC1, 0xFF, 0x76,
+	0x00, 0x76, 0xFF, 0x32, 0x00, 0x22, 0x01, 0x76, 0xFA, 0xA3, 0x46,
+	0x7D, 0x0F, 0x2C, 0xF8, 0xD5, 0x04, 0x13, 0xFD, 0x96, 0x01, 0x51,
+	0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x27, 0x00, 0x64, 0xFF,
+	0x63, 0x01, 0x84, 0xFD, 0xEB, 0x03, 0x14, 0xFA, 0x47, 0x0A, 0x2C,
+	0x48, 0xF6, 0xFD, 0x63, 0xFF, 0x2B, 0x01, 0xF0, 0xFE, 0xB8, 0x00,
+	0xA8, 0xFF, 0x15, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE4,
+	0x01, 0x47, 0xFC, 0xB5, 0x06, 0xB0, 0xF3, 0x19, 0x1F, 0x7E, 0x3E,
+	0xC4, 0xF3, 0xFA, 0x04, 0xE7, 0xFD, 0xC1, 0x00, 0xD3, 0xFF, 0xFF,
+	0xFF, 0x02, 0x00, 0xFE, 0xFF, 0x23, 0x00, 0x71, 0xFF, 0x82, 0x01,
+	0xAB, 0xFC, 0xC4, 0x06, 0x93, 0xF1, 0xFD, 0x33, 0x62, 0x2C, 0xA8,
+	0xF1, 0x27, 0x07, 0x4A, 0xFC, 0xC7, 0x01, 0x4C, 0xFF, 0x30, 0x00,
+	0xFD, 0xFF, 0x08, 0x00, 0xE1, 0xFF, 0x23, 0x00, 0x20, 0x00, 0x00,
+	0xFF, 0x31, 0x03, 0xAD, 0xF6, 0x65, 0x43, 0xDC, 0x16, 0xD1, 0xF5,
+	0xE1, 0x05, 0x99, 0xFC, 0xC9, 0x01, 0x3E, 0xFF, 0x33, 0x00, 0xFF,
+	0xFF, 0x00, 0x00, 0x1F, 0x00, 0x83, 0xFF, 0x14, 0x01, 0x2D, 0xFE,
+	0x9C, 0x02, 0xAD, 0xFC, 0xE9, 0x03, 0xF6, 0x48, 0x73, 0x03, 0xE0,
+	0xFC, 0x82, 0x02, 0x3B, 0xFE, 0x0E, 0x01, 0x86, 0xFF, 0x1E, 0x00,
+	0xFE, 0xFF, 0x34, 0x00, 0x3D, 0xFF, 0xCC, 0x01, 0x91, 0xFC, 0xF3,
+	0x05, 0xA6, 0xF5, 0x70, 0x17, 0x17, 0x43, 0x6D, 0xF6, 0x56, 0x03,
+	0xEA, 0xFE, 0x2D, 0x00, 0x1D, 0x00, 0xE4, 0xFF, 0x08, 0x00, 0xFD,
+	0xFF, 0x2F, 0x00, 0x4E, 0xFF, 0xC3, 0x01, 0x4E, 0xFC, 0x24, 0x07,
+	0x9E, 0xF1, 0xF2, 0x2C, 0x78, 0x33, 0x8C, 0xF1, 0xD0, 0x06, 0xA2,
+	0xFC, 0x88, 0x01, 0x6D, 0xFF, 0x24, 0x00, 0xFD, 0xFF, 0x03, 0x00,
+	0xFD, 0xFF, 0xD8, 0xFF, 0xB7, 0x00, 0xF9, 0xFD, 0xDE, 0x04, 0xEF,
+	0xF3, 0xE4, 0x3E, 0x81, 0x1E, 0xD2, 0xF3, 0xA9, 0x06, 0x4B, 0xFC,
+	0xE3, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x16, 0x00, 0xA5,
+	0xFF, 0xBE, 0x00, 0xE2, 0xFE, 0x45, 0x01, 0x33, 0xFF, 0x5A, 0xFE,
+	0x48, 0x48, 0xC3, 0x09, 0x47, 0xFA, 0xD2, 0x03, 0x90, 0xFD, 0x5E,
+	0x01, 0x66, 0xFF, 0x27, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2E, 0x00,
+	0x4F, 0xFF, 0x9A, 0x01, 0x08, 0xFD, 0xEB, 0x04, 0xFC, 0xF7, 0x0A,
+	0x10, 0x70, 0x46, 0x22, 0xFA, 0x4D, 0x01, 0x19, 0x00, 0x84, 0xFF,
+	0x70, 0x00, 0xC4, 0xFF, 0x0F, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3B,
+	0xFF, 0xE3, 0x01, 0x31, 0xFC, 0x12, 0x07, 0x79, 0xF2, 0x73, 0x25,
+	0xDF, 0x39, 0x5A, 0xF2, 0x00, 0x06, 0x3A, 0xFD, 0x28, 0x01, 0x9F,
+	0xFF, 0x13, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x15, 0x00, 0x99, 0xFF,
+	0x33, 0x01, 0x29, 0xFD, 0x1A, 0x06, 0x3B, 0xF2, 0x4B, 0x39, 0x30,
+	0x26, 0x5B, 0xF2, 0x19, 0x07, 0x31, 0xFC, 0xE1, 0x01, 0x3C, 0xFF,
+	0x35, 0x00, 0xFD, 0xFF, 0x0E, 0x00, 0xC7, 0xFF, 0x68, 0x00, 0x95,
+	0xFF, 0xFA, 0xFF, 0x83, 0x01, 0xBB, 0xF9, 0x2B, 0x46, 0xBB, 0x10,
+	0xBF, 0xF7, 0x07, 0x05, 0xFB, 0xFC, 0xA0, 0x01, 0x4D, 0xFF, 0x2F,
+	0x00, 0xFF, 0xFF, 0x00, 0x00, 0x26, 0x00, 0x69, 0xFF, 0x56, 0x01,
+	0xA0, 0xFD, 0xB3, 0x03, 0x87, 0xFA, 0x1F, 0x09, 0x6A, 0x48, 0xD9,
+	0xFE, 0xF6, 0xFE, 0x65, 0x01, 0xD0, 0xFE, 0xC7, 0x00, 0xA2, 0xFF,
+	0x17, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE2, 0x01, 0x50,
+	0xFC, 0x99, 0x06, 0xFE, 0xF3, 0xC3, 0x1D, 0x5E, 0x3F, 0x27, 0xF4,
+	0xB9, 0x04, 0x10, 0xFE, 0xA9, 0x00, 0xDF, 0xFF, 0xFB, 0xFF, 0x03,
+	0x00, 0xFD, 0xFF, 0x26, 0x00, 0x69, 0xFF, 0x90, 0x01, 0x96, 0xFC,
+	0xDD, 0x06, 0x85, 0xF1, 0xD0, 0x32, 0xA6, 0x2D, 0x94, 0xF1, 0x20,
+	0x07, 0x54, 0xFC, 0xBF, 0x01, 0x50, 0xFF, 0x2E, 0x00, 0xFD, 0xFF,
+	0x07, 0x00, 0xE6, 0xFF, 0x15, 0x00, 0x3C, 0x00, 0xCF, 0xFE, 0x83,
+	0x03, 0x20, 0xF6, 0xB2, 0x42, 0x2B, 0x18, 0x71, 0xF5, 0x09, 0x06,
+	0x88, 0xFC, 0xCF, 0x01, 0x3C, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0x1D,
+	0x00, 0x89, 0xFF, 0x06, 0x01, 0x4C, 0xFE, 0x60, 0x02, 0x1F, 0xFD,
+	0xE2, 0x02, 0xF3, 0x48, 0x7D, 0x04, 0x6E, 0xFC, 0xBD, 0x02, 0x1C,
+	0xFE, 0x1C, 0x01, 0x80, 0xFF, 0x20, 0x00, 0x00, 0x00, 0xFF, 0xFF,
+	0x33, 0x00, 0x3F, 0xFF, 0xC5, 0x01, 0xA3, 0xFC, 0xCA, 0x05, 0x07,
+	0xF6, 0x22, 0x16, 0xC3, 0x43, 0xFE, 0xF6, 0x02, 0x03, 0x1B, 0xFF,
+	0x11, 0x00, 0x2B, 0x00, 0xDE, 0xFF, 0x09, 0x00, 0xFD, 0xFF, 0x31,
+	0x00, 0x49, 0xFF, 0xCB, 0x01, 0x45, 0xFC, 0x29, 0x07, 0xB6, 0xF1,
+	0xAD, 0x2B, 0xA2, 0x34, 0x9E, 0xF1, 0xB4, 0x06, 0xB8, 0xFC, 0x7A,
+	0x01, 0x75, 0xFF, 0x22, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0x02, 0x00,
+	0xCC, 0xFF, 0xCE, 0x00, 0xD1, 0xFD, 0x1D, 0x05, 0x91, 0xF3, 0xFE,
+	0x3D, 0xD7, 0x1F, 0x87, 0xF3, 0xC3, 0x06, 0x42, 0xFC, 0xE5, 0x01,
+	0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x14, 0x00, 0xAB, 0xFF, 0xAF,
+	0x00, 0x01, 0xFF, 0x0A, 0x01, 0x9E, 0xFF, 0x7C, 0xFD, 0x03, 0x48,
+	0xED, 0x0A, 0xD5, 0xF9, 0x0A, 0x04, 0x74, 0xFD, 0x6A, 0x01, 0x62,
+	0xFF, 0x28, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x2D, 0x00, 0x53, 0xFF,
+	0x90, 0x01, 0x20, 0xFD, 0xB8, 0x04, 0x6A, 0xF8, 0xCD, 0x0E, 0xE1,
+	0x46, 0xE1, 0xFA, 0xEB, 0x00, 0x51, 0x00, 0x65, 0xFF, 0x7F, 0x00,
+	0xBE, 0xFF, 0x10, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x39, 0xFF, 0xE5,
+	0x01, 0x33, 0xFC, 0x04, 0x07, 0xB1, 0xF2, 0x21, 0x24, 0xE6, 0x3A,
+	0x97, 0xF2, 0xD0, 0x05, 0x5B, 0xFD, 0x15, 0x01, 0xA9, 0xFF, 0x0F,
+	0x00, 0x00, 0x00, 0xFF, 0xFF, 0x18, 0x00, 0x90, 0xFF, 0x45, 0x01,
+	0x0B, 0xFD, 0x44, 0x06, 0x0A, 0xF2, 0x3B, 0x38, 0x80, 0x27, 0x2B,
+	0xF2, 0x22, 0x07, 0x33, 0xFC, 0xDE, 0x01, 0x3E, 0xFF, 0x34, 0x00,
+	0xFD, 0xFF, 0x0D, 0x00, 0xCD, 0xFF, 0x59, 0x00, 0xB3, 0xFF, 0xC4,
+	0xFF, 0xE2, 0x01, 0x09, 0xF9, 0xAA, 0x45, 0xFE, 0x11, 0x54, 0xF7,
+	0x38, 0x05, 0xE4, 0xFC, 0xAA, 0x01, 0x49, 0xFF, 0x30, 0x00, 0xFF,
+	0xFF, 0x00, 0x00, 0x24, 0x00, 0x6E, 0xFF, 0x49, 0x01, 0xBC, 0xFD,
+	0x7A, 0x03, 0xFA, 0xFA, 0xFD, 0x07, 0x9C, 0x48, 0xC3, 0xFF, 0x89,
+	0xFE, 0xA1, 0x01, 0xB1, 0xFE, 0xD6, 0x00, 0x9C, 0xFF, 0x18, 0x00,
+	0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xDF, 0x01, 0x5B, 0xFC, 0x7B,
+	0x06, 0x50, 0xF4, 0x6E, 0x1C, 0x36, 0x40, 0x92, 0xF4, 0x75, 0x04,
+	0x3B, 0xFE, 0x91, 0x00, 0xEB, 0xFF, 0xF6, 0xFF, 0x04, 0x00, 0xFD,
+	0xFF, 0x28, 0x00, 0x63, 0xFF, 0x9D, 0x01, 0x84, 0xFC, 0xF3, 0x06,
+	0x7D, 0xF1, 0x9E, 0x31, 0xE6, 0x2E, 0x85, 0xF1, 0x16, 0x07, 0x61,
+	0xFC, 0xB5, 0x01, 0x55, 0xFF, 0x2D, 0x00, 0xFD, 0xFF, 0x06, 0x00,
+	0xEC, 0xFF, 0x08, 0x00, 0x57, 0x00, 0x9F, 0xFE, 0xD1, 0x03, 0x9B,
+	0xF5, 0xF7, 0x41, 0x7C, 0x19, 0x13, 0xF5, 0x2F, 0x06, 0x78, 0xFC,
+	0xD5, 0x01, 0x3A, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1C, 0x00, 0x8F,
+	0xFF, 0xF7, 0x00, 0x6B, 0xFE, 0x25, 0x02, 0x91, 0xFD, 0xE3, 0x01,
+	0xE5, 0x48, 0x8D, 0x05, 0xFB, 0xFB, 0xF8, 0x02, 0xFE, 0xFD, 0x2B,
+	0x01, 0x7A, 0xFF, 0x21, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x32, 0x00,
+	0x42, 0xFF, 0xBD, 0x01, 0xB6, 0xFC, 0x9F, 0x05, 0x6C, 0xF6, 0xD6,
+	0x14, 0x65, 0x44, 0x98, 0xF7, 0xAC, 0x02, 0x4E, 0xFF, 0xF4, 0xFF,
+	0x39, 0x00, 0xD9, 0xFF, 0x0A, 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x45,
+	0xFF, 0xD2, 0x01, 0x3D, 0xFC, 0x2B, 0x07, 0xD4, 0xF1, 0x64, 0x2A,
+	0xC6, 0x35, 0xB7, 0xF1, 0x96, 0x06, 0xCF, 0xFC, 0x6B, 0x01, 0x7D,
+	0xFF, 0x1F, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x06, 0x00, 0xC1, 0xFF,
+	0xE5, 0x00, 0xAA, 0xFD, 0x58, 0x05, 0x3A, 0xF3, 0x11, 0x3D, 0x2C,
+	0x21, 0x3F, 0xF3, 0xDA, 0x06, 0x3B, 0xFC, 0xE6, 0x01, 0x36, 0xFF,
+	0x36, 0x00, 0xFD, 0xFF, 0x13, 0x00, 0xB1, 0xFF, 0xA0, 0x00, 0x20,
+	0xFF, 0xD0, 0x00, 0x07, 0x00, 0xA4, 0xFC, 0xB6, 0x47, 0x1C, 0x0C,
+	0x63, 0xF9, 0x42, 0x04, 0x59, 0xFD, 0x76, 0x01, 0x5D, 0xFF, 0x2A,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x57, 0xFF, 0x85, 0x01,
+	0x39, 0xFD, 0x84, 0x04, 0xD9, 0xF8, 0x95, 0x0D, 0x48, 0x47, 0xA7,
+	0xFB, 0x86, 0x00, 0x8A, 0x00, 0x46, 0xFF, 0x8E, 0x00, 0xB8, 0xFF,
+	0x11, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x35,
+	0xFC, 0xF3, 0x06, 0xEE, 0xF2, 0xCD, 0x22, 0xE4, 0x3B, 0xDC, 0xF2,
+	0x9C, 0x05, 0x7E, 0xFD, 0x00, 0x01, 0xB4, 0xFF, 0x0B, 0x00, 0x01,
+	0x00, 0xFE, 0xFF, 0x1B, 0x00, 0x87, 0xFF, 0x57, 0x01, 0xEF, 0xFC,
+	0x6B, 0x06, 0xE0, 0xF1, 0x23, 0x37, 0xCE, 0x28, 0x01, 0xF2, 0x28,
+	0x07, 0x36, 0xFC, 0xD9, 0x01, 0x41, 0xFF, 0x33, 0x00, 0xFD, 0xFF,
+	0x0B, 0x00, 0xD2, 0xFF, 0x4A, 0x00, 0xD0, 0xFF, 0x8E, 0xFF, 0x3F,
+	0x02, 0x5E, 0xF8, 0x1E, 0x45, 0x44, 0x13, 0xEA, 0xF6, 0x67, 0x05,
+	0xCF, 0xFC, 0xB3, 0x01, 0x46, 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0x00,
+	0x00, 0x23, 0x00, 0x74, 0xFF, 0x3C, 0x01, 0xDA, 0xFD, 0x40, 0x03,
+	0x6E, 0xFB, 0xE1, 0x06, 0xC3, 0x48, 0xB3, 0x00, 0x1A, 0xFE, 0xDC,
+	0x01, 0x91, 0xFE, 0xE5, 0x00, 0x96, 0xFF, 0x1A, 0x00, 0xFE, 0xFF,
+	0x36, 0x00, 0x38, 0xFF, 0xDB, 0x01, 0x67, 0xFC, 0x5A, 0x06, 0xA6,
+	0xF4, 0x1B, 0x1B, 0x07, 0x41, 0x04, 0xF5, 0x2D, 0x04, 0x67, 0xFE,
+	0x77, 0x00, 0xF8, 0xFF, 0xF2, 0xFF, 0x05, 0x00, 0xFD, 0xFF, 0x2A,
+	0x00, 0x5C, 0xFF, 0xA8, 0x01, 0x73, 0xFC, 0x05, 0x07, 0x7D, 0xF1,
+	0x67, 0x30, 0x21, 0x30, 0x7E, 0xF1, 0x08, 0x07, 0x6F, 0xFC, 0xAB,
+	0x01, 0x5B, 0xFF, 0x2B, 0x00, 0xFD, 0xFF, 0x05, 0x00, 0xF0, 0xFF,
+	0xFB, 0xFF, 0x71, 0x00, 0x71, 0xFE, 0x1D, 0x04, 0x1F, 0xF5, 0x32,
+	0x41, 0xCE, 0x1A, 0xBA, 0xF4, 0x53, 0x06, 0x6A, 0xFC, 0xDA, 0x01,
+	0x38, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x1A, 0x00, 0x95, 0xFF, 0xE8,
+	0x00, 0x8A, 0xFE, 0xE9, 0x01, 0x01, 0xFE, 0xEA, 0x00, 0xCB, 0x48,
+	0xA2, 0x06, 0x87, 0xFB, 0x33, 0x03, 0xE0, 0xFD, 0x39, 0x01, 0x75,
+	0xFF, 0x23, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x31, 0x00, 0x45, 0xFF,
+	0xB5, 0x01, 0xCA, 0xFC, 0x72, 0x05, 0xD3, 0xF6, 0x8D, 0x13, 0xFD,
+	0x44, 0x39, 0xF8, 0x53, 0x02, 0x82, 0xFF, 0xD7, 0xFF, 0x47, 0x00,
+	0xD3, 0xFF, 0x0B, 0x00, 0xFD, 0xFF, 0x33, 0x00, 0x42, 0xFF, 0xD8,
+	0x01, 0x37, 0xFC, 0x29, 0x07, 0xF8, 0xF1, 0x19, 0x29, 0xE5, 0x36,
+	0xD8, 0xF1, 0x73, 0x06, 0xE9, 0xFC, 0x5B, 0x01, 0x85, 0xFF, 0x1C,
+	0x00, 0xFE, 0xFF, 0x01, 0x00, 0x0A, 0x00, 0xB6, 0xFF, 0xFB, 0x00,
+	0x85, 0xFD, 0x90, 0x05, 0xEC, 0xF2, 0x1C, 0x3C, 0x81, 0x22, 0xFC,
+	0xF2, 0xEF, 0x06, 0x36, 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00,
+	0xFD, 0xFF, 0x12, 0x00, 0xB7, 0xFF, 0x91, 0x00, 0x40, 0xFF, 0x96,
+	0x00, 0x6F, 0x00, 0xD5, 0xFB, 0x5E, 0x47, 0x50, 0x0D, 0xF2, 0xF8,
+	0x78, 0x04, 0x3F, 0xFD, 0x82, 0x01, 0x58, 0xFF, 0x2B, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x2A, 0x00, 0x5C, 0xFF, 0x79, 0x01, 0x53, 0xFD,
+	0x4E, 0x04, 0x4A, 0xF9, 0x60, 0x0C, 0xA3, 0x47, 0x76, 0xFC, 0x1F,
+	0x00, 0xC3, 0x00, 0x27, 0xFF, 0x9D, 0x00, 0xB2, 0xFF, 0x13, 0x00,
+	0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x3A, 0xFC, 0xDF,
+	0x06, 0x30, 0xF3, 0x78, 0x21, 0xDB, 0x3C, 0x28, 0xF3, 0x65, 0x05,
+	0xA2, 0xFD, 0xEA, 0x00, 0xBE, 0xFF, 0x07, 0x00, 0x01, 0x00, 0xFE,
+	0xFF, 0x1E, 0x00, 0x7F, 0xFF, 0x67, 0x01, 0xD5, 0xFC, 0x8E, 0x06,
+	0xBE, 0xF1, 0x06, 0x36, 0x1A, 0x2A, 0xDC, 0xF1, 0x2A, 0x07, 0x3C,
+	0xFC, 0xD3, 0x01, 0x44, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x0A, 0x00,
+	0xD8, 0xFF, 0x3C, 0x00, 0xEE, 0xFF, 0x5A, 0xFF, 0x98, 0x02, 0xBB,
+	0xF7, 0x87, 0x44, 0x8C, 0x14, 0x83, 0xF6, 0x95, 0x05, 0xBA, 0xFC,
+	0xBB, 0x01, 0x43, 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x21,
+	0x00, 0x79, 0xFF, 0x2E, 0x01, 0xF7, 0xFD, 0x05, 0x03, 0xE1, 0xFB,
+	0xCA, 0x05, 0xDF, 0x48, 0xAB, 0x01, 0xAA, 0xFD, 0x18, 0x02, 0x72,
+	0xFE, 0xF4, 0x00, 0x90, 0xFF, 0x1B, 0x00, 0xFE, 0xFF, 0x35, 0x00,
+	0x39, 0xFF, 0xD6, 0x01, 0x75, 0xFC, 0x37, 0x06, 0xFF, 0xF4, 0xC7,
+	0x19, 0xCC, 0x41, 0x7F, 0xF5, 0xE2, 0x03, 0x95, 0xFE, 0x5D, 0x00,
+	0x05, 0x00, 0xED, 0xFF, 0x06, 0x00, 0xFD, 0xFF, 0x2C, 0x00, 0x57,
+	0xFF, 0xB3, 0x01, 0x64, 0xFC, 0x13, 0x07, 0x83, 0xF1, 0x2C, 0x2F,
+	0x5A, 0x31, 0x7D, 0xF1, 0xF7, 0x06, 0x80, 0xFC, 0x9F, 0x01, 0x61,
+	0xFF, 0x29, 0x00, 0xFD, 0xFF, 0x04, 0x00, 0xF5, 0xFF, 0xEE, 0xFF,
+	0x8B, 0x00, 0x44, 0xFE, 0x65, 0x04, 0xAA, 0xF4, 0x66, 0x40, 0x23,
+	0x1C, 0x63, 0xF4, 0x74, 0x06, 0x5D, 0xFC, 0xDE, 0x01, 0x37, 0xFF,
+	0x36, 0x00, 0xFE, 0xFF, 0x19, 0x00, 0x9A, 0xFF, 0xD9, 0x00, 0xAA,
+	0xFE, 0xAE, 0x01, 0x70, 0xFE, 0xF8, 0xFF, 0xA6, 0x48, 0xBE, 0x07,
+	0x14, 0xFB, 0x6D, 0x03, 0xC3, 0xFD, 0x46, 0x01, 0x70, 0xFF, 0x24,
+	0x00, 0x00, 0x00, 0xFF, 0xFF, 0x30, 0x00, 0x48, 0xFF, 0xAC, 0x01,
+	0xDF, 0xFC, 0x43, 0x05, 0x3C, 0xF7, 0x46, 0x12, 0x8D, 0x45, 0xE2,
+	0xF8, 0xF7, 0x01, 0xB8, 0xFF, 0xB9, 0xFF, 0x56, 0x00, 0xCE, 0xFF,
+	0x0C, 0x00, 0xFD, 0xFF, 0x34, 0x00, 0x3F, 0xFF, 0xDD, 0x01, 0x34,
+	0xFC, 0x23, 0x07, 0x21, 0xF2, 0xCB, 0x27, 0xFE, 0x37, 0x00, 0xF2,
+	0x4D, 0x06, 0x04, 0xFD, 0x49, 0x01, 0x8E, 0xFF, 0x19, 0x00, 0xFF,
+	0xFF, 0x00, 0x00, 0x0E, 0x00, 0xAB, 0xFF, 0x10, 0x01, 0x62, 0xFD,
+	0xC5, 0x05, 0xA5, 0xF2, 0x1F, 0x3B, 0xD6, 0x23, 0xBE, 0xF2, 0x01,
+	0x07, 0x33, 0xFC, 0xE5, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFD, 0xFF,
+	0x10, 0x00, 0xBD, 0xFF, 0x82, 0x00, 0x5E, 0xFF, 0x5D, 0x00, 0xD4,
+	0x00, 0x0C, 0xFB, 0xF9, 0x46, 0x87, 0x0E, 0x82, 0xF8, 0xAD, 0x04,
+	0x26, 0xFD, 0x8D, 0x01, 0x54, 0xFF, 0x2C, 0x00, 0xFF, 0xFF, 0x00,
+	0x00, 0x29, 0x00, 0x60, 0xFF, 0x6D, 0x01, 0x6E, 0xFD, 0x17, 0x04,
+	0xBC, 0xF9, 0x30, 0x0B, 0xF4, 0x47, 0x4B, 0xFD, 0xB5, 0xFF, 0xFD,
+	0x00, 0x08, 0xFF, 0xAC, 0x00, 0xAC, 0xFF, 0x14, 0x00, 0xFD, 0xFF,
+	0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x41, 0xFC, 0xC8, 0x06, 0x76,
+	0xF3, 0x22, 0x20, 0xCA, 0x3D, 0x7D, 0xF3, 0x2A, 0x05, 0xC8, 0xFD,
+	0xD4, 0x00, 0xCA, 0xFF, 0x03, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x21,
+	0x00, 0x77, 0xFF, 0x77, 0x01, 0xBD, 0xFC, 0xAE, 0x06, 0xA3, 0xF1,
+	0xE3, 0x34, 0x64, 0x2B, 0xBC, 0xF1, 0x2A, 0x07, 0x43, 0xFC, 0xCD,
+	0x01, 0x48, 0xFF, 0x31, 0x00, 0xFD, 0xFF, 0x09, 0x00, 0xDD, 0xFF,
+	0x2E, 0x00, 0x0A, 0x00, 0x27, 0xFF, 0xEF, 0x02, 0x20, 0xF7, 0xE7,
+	0x43, 0xD8, 0x15, 0x1E, 0xF6, 0xC0, 0x05, 0xA7, 0xFC, 0xC3, 0x01,
+	0x40, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x20, 0x00, 0x7F,
+	0xFF, 0x20, 0x01, 0x16, 0xFE, 0xCA, 0x02, 0x54, 0xFC, 0xB9, 0x04,
+	0xF2, 0x48, 0xA9, 0x02, 0x39, 0xFD, 0x53, 0x02, 0x53, 0xFE, 0x03,
+	0x01, 0x8A, 0xFF, 0x1D, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3B, 0xFF,
+	0xD1, 0x01, 0x84, 0xFC, 0x12, 0x06, 0x5C, 0xF5, 0x76, 0x18, 0x89,
+	0x42, 0x02, 0xF6, 0x94, 0x03, 0xC4, 0xFE, 0x42, 0x00, 0x12, 0x00,
+	0xE8, 0xFF, 0x07, 0x00, 0xFD, 0xFF, 0x2E, 0x00, 0x51, 0xFF, 0xBD,
+	0x01, 0x57, 0xFC, 0x1E, 0x07, 0x90, 0xF1, 0xED, 0x2D, 0x8C, 0x32,
+	0x83, 0xF1, 0xE2, 0x06, 0x92, 0xFC, 0x93, 0x01, 0x68, 0xFF, 0x26,
+	0x00, 0xFD, 0xFF, 0x03, 0x00, 0xFA, 0xFF, 0xE2, 0xFF, 0xA4, 0x00,
+	0x19, 0xFE, 0xAA, 0x04, 0x3E, 0xF4, 0x90, 0x3F, 0x78, 0x1D, 0x10,
+	0xF4, 0x93, 0x06, 0x52, 0xFC, 0xE1, 0x01, 0x36, 0xFF, 0x36, 0x00,
+	0xFE, 0xFF, 0x17, 0x00, 0xA0, 0xFF, 0xCA, 0x00, 0xC9, 0xFE, 0x73,
+	0x01, 0xDE, 0xFE, 0x0C, 0xFF, 0x76, 0x48, 0xDE, 0x08, 0xA1, 0xFA,
+	0xA6, 0x03, 0xA6, 0xFD, 0x53, 0x01, 0x6A, 0xFF, 0x26, 0x00, 0x00,
+	0x00, 0xFF, 0xFF, 0x2F, 0x00, 0x4C, 0xFF, 0xA2, 0x01, 0xF6, 0xFC,
+	0x12, 0x05, 0xA7, 0xF7, 0x03, 0x11, 0x10, 0x46, 0x93, 0xF9, 0x98,
+	0x01, 0xEE, 0xFF, 0x9B, 0xFF, 0x64, 0x00, 0xC8, 0xFF, 0x0E, 0x00,
+	0xFD, 0xFF, 0x35, 0x00, 0x3C, 0xFF, 0xE1, 0x01, 0x32, 0xFC, 0x1B,
+	0x07, 0x50, 0xF2, 0x7B, 0x26, 0x11, 0x39, 0x2F, 0xF2, 0x23, 0x06,
+	0x22, 0xFD, 0x37, 0x01, 0x97, 0xFF, 0x15, 0x00, 0xFF, 0xFF, 0x00,
+	0x00, 0x12, 0x00, 0xA1, 0xFF, 0x24, 0x01, 0x41, 0xFD, 0xF6, 0x05,
+	0x67, 0xF2, 0x1A, 0x3A, 0x29, 0x25, 0x84, 0xF2, 0x0F, 0x07, 0x31,
+	0xFC, 0xE3, 0x01, 0x3A, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x0F, 0x00,
+	0xC2, 0xFF, 0x73, 0x00, 0x7D, 0xFF, 0x25, 0x00, 0x38, 0x01, 0x4C,
+	0xFA, 0x89, 0x46, 0xC3, 0x0F, 0x14, 0xF8, 0xE0, 0x04, 0x0D, 0xFD,
+	0x98, 0x01, 0x50, 0xFF, 0x2E, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x27,
+	0x00, 0x65, 0xFF, 0x60, 0x01, 0x8A, 0xFD, 0xDF, 0x03, 0x2E, 0xFA,
+	0x04, 0x0A, 0x3A, 0x48, 0x28, 0xFE, 0x4B, 0xFF, 0x38, 0x01, 0xE9,
+	0xFE, 0xBB, 0x00, 0xA6, 0xFF, 0x16, 0x00, 0xFE, 0xFF, 0x36, 0x00,
+	0x36, 0xFF, 0xE4, 0x01, 0x49, 0xFC, 0xAF, 0x06, 0xC1, 0xF3, 0xCD,
+	0x1E, 0xB1, 0x3E, 0xD9, 0xF3, 0xEC, 0x04, 0xF0, 0xFD, 0xBC, 0x00,
+	0xD5, 0xFF, 0xFE, 0xFF, 0x03, 0x00, 0xFD, 0xFF, 0x24, 0x00, 0x6F,
+	0xFF, 0x85, 0x01, 0xA6, 0xFC, 0xCA, 0x06, 0x8F, 0xF1, 0xBB, 0x33,
+	0xAB, 0x2C, 0xA3, 0xF1, 0x26, 0x07, 0x4C, 0xFC, 0xC5, 0x01, 0x4D,
+	0xFF, 0x30, 0x00, 0xFD, 0xFF, 0x08, 0x00, 0xE2, 0xFF, 0x20, 0x00,
+	0x26, 0x00, 0xF5, 0xFE, 0x43, 0x03, 0x8D, 0xF6, 0x3C, 0x43, 0x25,
+	0x17, 0xBB, 0xF5, 0xEA, 0x05, 0x95, 0xFC, 0xCA, 0x01, 0x3D, 0xFF,
+	0x34, 0x00, 0xFE, 0xFF, 0x00, 0x00, 0x1E, 0x00, 0x84, 0xFF, 0x11,
+	0x01, 0x34, 0xFE, 0x8F, 0x02, 0xC7, 0xFC, 0xAE, 0x03, 0xF7, 0x48,
+	0xAE, 0x03, 0xC7, 0xFC, 0x8F, 0x02, 0x34, 0xFE, 0x11, 0x01, 0x84,
+	0xFF, 0x1E, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01,
+	0x3D, 0xFC, 0xD6, 0x06, 0x4C, 0xF3, 0xED, 0x20, 0x3D, 0x3D, 0x4A,
+	0xF3, 0x4E, 0x05, 0xB1, 0xFD, 0xE1, 0x00, 0xC3, 0xFF, 0x05, 0x00,
+	0x02, 0x00, 0x02, 0x00, 0x05, 0x00, 0xC3, 0xFF, 0xE1, 0x00, 0xB1,
+	0xFD, 0x4E, 0x05, 0x4A, 0xF3, 0x3D, 0x3D, 0xED, 0x20, 0x4C, 0xF3,
+	0xD6, 0x06, 0x3D, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD,
+	0xFF, 0x00, 0x00, 0x1E, 0x00, 0x84, 0xFF, 0x11, 0x01, 0x34, 0xFE,
+	0x8F, 0x02, 0xC7, 0xFC, 0xAE, 0x03, 0xF7, 0x48, 0xAE, 0x03, 0xC7,
+	0xFC, 0x8F, 0x02, 0x34, 0xFE, 0x11, 0x01, 0x84, 0xFF, 0x1E, 0x00,
+	0xFD, 0xFF, 0x30, 0x00, 0x4D, 0xFF, 0xC5, 0x01, 0x4C, 0xFC, 0x26,
+	0x07, 0xA3, 0xF1, 0xAB, 0x2C, 0xBB, 0x33, 0x8F, 0xF1, 0xCA, 0x06,
+	0xA6, 0xFC, 0x85, 0x01, 0x6F, 0xFF, 0x24, 0x00, 0xFD, 0xFF, 0x16,
+	0x00, 0xA6, 0xFF, 0xBB, 0x00, 0xE9, 0xFE, 0x38, 0x01, 0x4B, 0xFF,
+	0x28, 0xFE, 0x3A, 0x48, 0x04, 0x0A, 0x2E, 0xFA, 0xDF, 0x03, 0x8A,
+	0xFD, 0x60, 0x01, 0x65, 0xFF, 0x27, 0x00, 0x00, 0x00, 0xFD, 0xFF,
+	0x35, 0x00, 0x3A, 0xFF, 0xE3, 0x01, 0x31, 0xFC, 0x0F, 0x07, 0x84,
+	0xF2, 0x29, 0x25, 0x1A, 0x3A, 0x67, 0xF2, 0xF6, 0x05, 0x41, 0xFD,
+	0x24, 0x01, 0xA1, 0xFF, 0x12, 0x00, 0x00, 0x00, 0x0E, 0x00, 0xC8,
+	0xFF, 0x64, 0x00, 0x9B, 0xFF, 0xEE, 0xFF, 0x98, 0x01, 0x93, 0xF9,
+	0x10, 0x46, 0x03, 0x11, 0xA7, 0xF7, 0x12, 0x05, 0xF6, 0xFC, 0xA2,
+	0x01, 0x4C, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00,
+	0x36, 0xFF, 0xE1, 0x01, 0x52, 0xFC, 0x93, 0x06, 0x10, 0xF4, 0x78,
+	0x1D, 0x90, 0x3F, 0x3E, 0xF4, 0xAA, 0x04, 0x19, 0xFE, 0xA4, 0x00,
+	0xE2, 0xFF, 0xFA, 0xFF, 0x03, 0x00, 0x07, 0x00, 0xE8, 0xFF, 0x12,
+	0x00, 0x42, 0x00, 0xC4, 0xFE, 0x94, 0x03, 0x02, 0xF6, 0x89, 0x42,
+	0x76, 0x18, 0x5C, 0xF5, 0x12, 0x06, 0x84, 0xFC, 0xD1, 0x01, 0x3B,
+	0xFF, 0x34, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x33, 0x00, 0x40, 0xFF,
+	0xC3, 0x01, 0xA7, 0xFC, 0xC0, 0x05, 0x1E, 0xF6, 0xD8, 0x15, 0xE7,
+	0x43, 0x20, 0xF7, 0xEF, 0x02, 0x27, 0xFF, 0x0A, 0x00, 0x2E, 0x00,
+	0xDD, 0xFF, 0x09, 0x00, 0x02, 0x00, 0x03, 0x00, 0xCA, 0xFF, 0xD4,
+	0x00, 0xC8, 0xFD, 0x2A, 0x05, 0x7D, 0xF3, 0xCA, 0x3D, 0x22, 0x20,
+	0x76, 0xF3, 0xC8, 0x06, 0x41, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36,
+	0x00, 0xFD, 0xFF, 0xFF, 0xFF, 0x2C, 0x00, 0x54, 0xFF, 0x8D, 0x01,
+	0x26, 0xFD, 0xAD, 0x04, 0x82, 0xF8, 0x87, 0x0E, 0xF9, 0x46, 0x0C,
+	0xFB, 0xD4, 0x00, 0x5D, 0x00, 0x5E, 0xFF, 0x82, 0x00, 0xBD, 0xFF,
+	0x10, 0x00, 0xFF, 0xFF, 0x19, 0x00, 0x8E, 0xFF, 0x49, 0x01, 0x04,
+	0xFD, 0x4D, 0x06, 0x00, 0xF2, 0xFE, 0x37, 0xCB, 0x27, 0x21, 0xF2,
+	0x23, 0x07, 0x34, 0xFC, 0xDD, 0x01, 0x3F, 0xFF, 0x34, 0x00, 0xFD,
+	0xFF, 0x00, 0x00, 0x24, 0x00, 0x70, 0xFF, 0x46, 0x01, 0xC3, 0xFD,
+	0x6D, 0x03, 0x14, 0xFB, 0xBE, 0x07, 0xA6, 0x48, 0xF8, 0xFF, 0x70,
+	0xFE, 0xAE, 0x01, 0xAA, 0xFE, 0xD9, 0x00, 0x9A, 0xFF, 0x19, 0x00,
+	0xFD, 0xFF, 0x29, 0x00, 0x61, 0xFF, 0x9F, 0x01, 0x80, 0xFC, 0xF7,
+	0x06, 0x7D, 0xF1, 0x5A, 0x31, 0x2C, 0x2F, 0x83, 0xF1, 0x13, 0x07,
+	0x64, 0xFC, 0xB3, 0x01, 0x57, 0xFF, 0x2C, 0x00, 0xFD, 0xFF, 0x1B,
+	0x00, 0x90, 0xFF, 0xF4, 0x00, 0x72, 0xFE, 0x18, 0x02, 0xAA, 0xFD,
+	0xAB, 0x01, 0xDF, 0x48, 0xCA, 0x05, 0xE1, 0xFB, 0x05, 0x03, 0xF7,
+	0xFD, 0x2E, 0x01, 0x79, 0xFF, 0x21, 0x00, 0x00, 0x00, 0xFD, 0xFF,
+	0x32, 0x00, 0x44, 0xFF, 0xD3, 0x01, 0x3C, 0xFC, 0x2A, 0x07, 0xDC,
+	0xF1, 0x1A, 0x2A, 0x06, 0x36, 0xBE, 0xF1, 0x8E, 0x06, 0xD5, 0xFC,
+	0x67, 0x01, 0x7F, 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0x13, 0x00, 0xB2,
+	0xFF, 0x9D, 0x00, 0x27, 0xFF, 0xC3, 0x00, 0x1F, 0x00, 0x76, 0xFC,
+	0xA3, 0x47, 0x60, 0x0C, 0x4A, 0xF9, 0x4E, 0x04, 0x53, 0xFD, 0x79,
+	0x01, 0x5C, 0xFF, 0x2A, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00,
+	0x37, 0xFF, 0xE6, 0x01, 0x36, 0xFC, 0xEF, 0x06, 0xFC, 0xF2, 0x81,
+	0x22, 0x1C, 0x3C, 0xEC, 0xF2, 0x90, 0x05, 0x85, 0xFD, 0xFB, 0x00,
+	0xB6, 0xFF, 0x0A, 0x00, 0x01, 0x00, 0x0B, 0x00, 0xD3, 0xFF, 0x47,
+	0x00, 0xD7, 0xFF, 0x82, 0xFF, 0x53, 0x02, 0x39, 0xF8, 0xFD, 0x44,
+	0x8D, 0x13, 0xD3, 0xF6, 0x72, 0x05, 0xCA, 0xFC, 0xB5, 0x01, 0x45,
+	0xFF, 0x31, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x35, 0x00, 0x38, 0xFF,
+	0xDA, 0x01, 0x6A, 0xFC, 0x53, 0x06, 0xBA, 0xF4, 0xCE, 0x1A, 0x32,
+	0x41, 0x1F, 0xF5, 0x1D, 0x04, 0x71, 0xFE, 0x71, 0x00, 0xFB, 0xFF,
+	0xF0, 0xFF, 0x05, 0x00, 0x05, 0x00, 0xF2, 0xFF, 0xF8, 0xFF, 0x77,
+	0x00, 0x67, 0xFE, 0x2D, 0x04, 0x04, 0xF5, 0x07, 0x41, 0x1B, 0x1B,
+	0xA6, 0xF4, 0x5A, 0x06, 0x67, 0xFC, 0xDB, 0x01, 0x38, 0xFF, 0x36,
+	0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x31, 0x00, 0x46, 0xFF, 0xB3, 0x01,
+	0xCF, 0xFC, 0x67, 0x05, 0xEA, 0xF6, 0x44, 0x13, 0x1E, 0x45, 0x5E,
+	0xF8, 0x3F, 0x02, 0x8E, 0xFF, 0xD0, 0xFF, 0x4A, 0x00, 0xD2, 0xFF,
+	0x0B, 0x00, 0x01, 0x00, 0x0B, 0x00, 0xB4, 0xFF, 0x00, 0x01, 0x7E,
+	0xFD, 0x9C, 0x05, 0xDC, 0xF2, 0xE4, 0x3B, 0xCD, 0x22, 0xEE, 0xF2,
+	0xF3, 0x06, 0x35, 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD,
+	0xFF, 0x00, 0x00, 0x2A, 0x00, 0x5D, 0xFF, 0x76, 0x01, 0x59, 0xFD,
+	0x42, 0x04, 0x63, 0xF9, 0x1C, 0x0C, 0xB6, 0x47, 0xA4, 0xFC, 0x07,
+	0x00, 0xD0, 0x00, 0x20, 0xFF, 0xA0, 0x00, 0xB1, 0xFF, 0x13, 0x00,
+	0xFE, 0xFF, 0x1F, 0x00, 0x7D, 0xFF, 0x6B, 0x01, 0xCF, 0xFC, 0x96,
+	0x06, 0xB7, 0xF1, 0xC6, 0x35, 0x64, 0x2A, 0xD4, 0xF1, 0x2B, 0x07,
+	0x3D, 0xFC, 0xD2, 0x01, 0x45, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x00,
+	0x00, 0x21, 0x00, 0x7A, 0xFF, 0x2B, 0x01, 0xFE, 0xFD, 0xF8, 0x02,
+	0xFB, 0xFB, 0x8D, 0x05, 0xE5, 0x48, 0xE3, 0x01, 0x91, 0xFD, 0x25,
+	0x02, 0x6B, 0xFE, 0xF7, 0x00, 0x8F, 0xFF, 0x1C, 0x00, 0xFD, 0xFF,
+	0x2D, 0x00, 0x55, 0xFF, 0xB5, 0x01, 0x61, 0xFC, 0x16, 0x07, 0x85,
+	0xF1, 0xE6, 0x2E, 0x9E, 0x31, 0x7D, 0xF1, 0xF3, 0x06, 0x84, 0xFC,
+	0x9D, 0x01, 0x63, 0xFF, 0x28, 0x00, 0xFD, 0xFF, 0x18, 0x00, 0x9C,
+	0xFF, 0xD6, 0x00, 0xB1, 0xFE, 0xA1, 0x01, 0x89, 0xFE, 0xC3, 0xFF,
+	0x9C, 0x48, 0xFD, 0x07, 0xFA, 0xFA, 0x7A, 0x03, 0xBC, 0xFD, 0x49,
+	0x01, 0x6E, 0xFF, 0x24, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x34, 0x00,
+	0x3E, 0xFF, 0xDE, 0x01, 0x33, 0xFC, 0x22, 0x07, 0x2B, 0xF2, 0x80,
+	0x27, 0x3B, 0x38, 0x0A, 0xF2, 0x44, 0x06, 0x0B, 0xFD, 0x45, 0x01,
+	0x90, 0xFF, 0x18, 0x00, 0xFF, 0xFF, 0x10, 0x00, 0xBE, 0xFF, 0x7F,
+	0x00, 0x65, 0xFF, 0x51, 0x00, 0xEB, 0x00, 0xE1, 0xFA, 0xE1, 0x46,
+	0xCD, 0x0E, 0x6A, 0xF8, 0xB8, 0x04, 0x20, 0xFD, 0x90, 0x01, 0x53,
+	0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF,
+	0xE5, 0x01, 0x42, 0xFC, 0xC3, 0x06, 0x87, 0xF3, 0xD7, 0x1F, 0xFE,
+	0x3D, 0x91, 0xF3, 0x1D, 0x05, 0xD1, 0xFD, 0xCE, 0x00, 0xCC, 0xFF,
+	0x02, 0x00, 0x02, 0x00, 0x09, 0x00, 0xDE, 0xFF, 0x2B, 0x00, 0x11,
+	0x00, 0x1B, 0xFF, 0x02, 0x03, 0xFE, 0xF6, 0xC3, 0x43, 0x22, 0x16,
+	0x07, 0xF6, 0xCA, 0x05, 0xA3, 0xFC, 0xC5, 0x01, 0x3F, 0xFF, 0x33,
+	0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x34, 0x00, 0x3C, 0xFF, 0xCF, 0x01,
+	0x88, 0xFC, 0x09, 0x06, 0x71, 0xF5, 0x2B, 0x18, 0xB2, 0x42, 0x20,
+	0xF6, 0x83, 0x03, 0xCF, 0xFE, 0x3C, 0x00, 0x15, 0x00, 0xE6, 0xFF,
+	0x07, 0x00, 0x03, 0x00, 0xFB, 0xFF, 0xDF, 0xFF, 0xA9, 0x00, 0x10,
+	0xFE, 0xB9, 0x04, 0x27, 0xF4, 0x5E, 0x3F, 0xC3, 0x1D, 0xFE, 0xF3,
+	0x99, 0x06, 0x50, 0xFC, 0xE2, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE,
+	0xFF, 0xFF, 0xFF, 0x2F, 0x00, 0x4D, 0xFF, 0xA0, 0x01, 0xFB, 0xFC,
+	0x07, 0x05, 0xBF, 0xF7, 0xBB, 0x10, 0x2B, 0x46, 0xBB, 0xF9, 0x83,
+	0x01, 0xFA, 0xFF, 0x95, 0xFF, 0x68, 0x00, 0xC7, 0xFF, 0x0E, 0x00,
+	0x00, 0x00, 0x13, 0x00, 0x9F, 0xFF, 0x28, 0x01, 0x3A, 0xFD, 0x00,
+	0x06, 0x5A, 0xF2, 0xDF, 0x39, 0x73, 0x25, 0x79, 0xF2, 0x12, 0x07,
+	0x31, 0xFC, 0xE3, 0x01, 0x3B, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x00,
+	0x00, 0x27, 0x00, 0x66, 0xFF, 0x5E, 0x01, 0x90, 0xFD, 0xD2, 0x03,
+	0x47, 0xFA, 0xC3, 0x09, 0x48, 0x48, 0x5A, 0xFE, 0x33, 0xFF, 0x45,
+	0x01, 0xE2, 0xFE, 0xBE, 0x00, 0xA5, 0xFF, 0x16, 0x00, 0xFD, 0xFF,
+	0x24, 0x00, 0x6D, 0xFF, 0x88, 0x01, 0xA2, 0xFC, 0xD0, 0x06, 0x8C,
+	0xF1, 0x78, 0x33, 0xF2, 0x2C, 0x9E, 0xF1, 0x24, 0x07, 0x4E, 0xFC,
+	0xC3, 0x01, 0x4E, 0xFF, 0x2F, 0x00, 0xFD, 0xFF, 0x1E, 0x00, 0x86,
+	0xFF, 0x0E, 0x01, 0x3B, 0xFE, 0x82, 0x02, 0xE0, 0xFC, 0x73, 0x03,
+	0xF6, 0x48, 0xE9, 0x03, 0xAD, 0xFC, 0x9C, 0x02, 0x2D, 0xFE, 0x14,
+	0x01, 0x83, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x30, 0x00,
+	0x4C, 0xFF, 0xC7, 0x01, 0x4A, 0xFC, 0x27, 0x07, 0xA8, 0xF1, 0x62,
+	0x2C, 0xFD, 0x33, 0x93, 0xF1, 0xC4, 0x06, 0xAB, 0xFC, 0x82, 0x01,
+	0x71, 0xFF, 0x23, 0x00, 0xFE, 0xFF, 0x15, 0x00, 0xA8, 0xFF, 0xB8,
+	0x00, 0xF0, 0xFE, 0x2B, 0x01, 0x63, 0xFF, 0xF6, 0xFD, 0x2C, 0x48,
+	0x47, 0x0A, 0x14, 0xFA, 0xEB, 0x03, 0x84, 0xFD, 0x63, 0x01, 0x64,
+	0xFF, 0x27, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x3A, 0xFF,
+	0xE4, 0x01, 0x32, 0xFC, 0x0C, 0x07, 0x91, 0xF2, 0xDD, 0x24, 0x54,
+	0x3A, 0x74, 0xF2, 0xEB, 0x05, 0x49, 0xFD, 0x20, 0x01, 0xA3, 0xFF,
+	0x11, 0x00, 0x00, 0x00, 0x0D, 0x00, 0xC9, 0xFF, 0x61, 0x00, 0xA2,
+	0xFF, 0xE2, 0xFF, 0xAE, 0x01, 0x6B, 0xF9, 0xF2, 0x45, 0x4A, 0x11,
+	0x8F, 0xF7, 0x1D, 0x05, 0xF1, 0xFC, 0xA4, 0x01, 0x4B, 0xFF, 0x2F,
+	0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE1, 0x01,
+	0x55, 0xFC, 0x8C, 0x06, 0x22, 0xF4, 0x2C, 0x1D, 0xC0, 0x3F, 0x55,
+	0xF4, 0x9B, 0x04, 0x23, 0xFE, 0x9F, 0x00, 0xE4, 0xFF, 0xF9, 0xFF,
+	0x04, 0x00, 0x07, 0x00, 0xE9, 0xFF, 0x0F, 0x00, 0x48, 0x00, 0xB9,
+	0xFE, 0xA6, 0x03, 0xE4, 0xF5, 0x60, 0x42, 0xC1, 0x18, 0x47, 0xF5,
+	0x1A, 0x06, 0x81, 0xFC, 0xD2, 0x01, 0x3B, 0xFF, 0x35, 0x00, 0xFE,
+	0xFF, 0xFF, 0xFF, 0x33, 0x00, 0x40, 0xFF, 0xC1, 0x01, 0xAB, 0xFC,
+	0xB7, 0x05, 0x34, 0xF6, 0x8E, 0x15, 0x0B, 0x44, 0x42, 0xF7, 0xDC,
+	0x02, 0x32, 0xFF, 0x04, 0x00, 0x31, 0x00, 0xDC, 0xFF, 0x09, 0x00,
+	0x02, 0x00, 0x04, 0x00, 0xC7, 0xFF, 0xD9, 0x00, 0xBF, 0xFD, 0x38,
+	0x05, 0x69, 0xF3, 0x96, 0x3D, 0x6F, 0x20, 0x66, 0xF3, 0xCE, 0x06,
+	0x3F, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFF,
+	0xFF, 0x2C, 0x00, 0x55, 0xFF, 0x8B, 0x01, 0x2B, 0xFD, 0xA1, 0x04,
+	0x9B, 0xF8, 0x42, 0x0E, 0x0F, 0x47, 0x38, 0xFB, 0xBE, 0x00, 0x6A,
+	0x00, 0x58, 0xFF, 0x85, 0x00, 0xBB, 0xFF, 0x10, 0x00, 0xFF, 0xFF,
+	0x19, 0x00, 0x8C, 0xFF, 0x4D, 0x01, 0xFE, 0xFC, 0x56, 0x06, 0xF7,
+	0xF1, 0xBF, 0x37, 0x15, 0x28, 0x18, 0xF2, 0x25, 0x07, 0x34, 0xFC,
+	0xDC, 0x01, 0x3F, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x24,
+	0x00, 0x71, 0xFF, 0x43, 0x01, 0xC9, 0xFD, 0x60, 0x03, 0x2E, 0xFB,
+	0x7E, 0x07, 0xAF, 0x48, 0x2D, 0x00, 0x58, 0xFE, 0xBB, 0x01, 0xA3,
+	0xFE, 0xDD, 0x00, 0x99, 0xFF, 0x19, 0x00, 0xFD, 0xFF, 0x29, 0x00,
+	0x60, 0xFF, 0xA2, 0x01, 0x7C, 0xFC, 0xFB, 0x06, 0x7C, 0xF1, 0x15,
+	0x31, 0x73, 0x2F, 0x81, 0xF1, 0x10, 0x07, 0x67, 0xFC, 0xB1, 0x01,
+	0x58, 0xFF, 0x2C, 0x00, 0xFD, 0xFF, 0x1B, 0x00, 0x91, 0xFF, 0xF1,
+	0x00, 0x79, 0xFE, 0x0A, 0x02, 0xC3, 0xFD, 0x73, 0x01, 0xDB, 0x48,
+	0x07, 0x06, 0xC7, 0xFB, 0x12, 0x03, 0xF1, 0xFD, 0x31, 0x01, 0x78,
+	0xFF, 0x22, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x44, 0xFF,
+	0xD5, 0x01, 0x3A, 0xFC, 0x2A, 0x07, 0xE3, 0xF1, 0xD1, 0x29, 0x46,
+	0x36, 0xC5, 0xF1, 0x87, 0x06, 0xDA, 0xFC, 0x64, 0x01, 0x80, 0xFF,
+	0x1E, 0x00, 0xFE, 0xFF, 0x12, 0x00, 0xB3, 0xFF, 0x99, 0x00, 0x2E,
+	0xFF, 0xB6, 0x00, 0x36, 0x00, 0x47, 0xFC, 0x90, 0x47, 0xA4, 0x0C,
+	0x31, 0xF9, 0x5A, 0x04, 0x4E, 0xFD, 0x7C, 0x01, 0x5B, 0xFF, 0x2A,
+	0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01,
+	0x37, 0xFC, 0xEB, 0x06, 0x0B, 0xF3, 0x35, 0x22, 0x52, 0x3C, 0xFD,
+	0xF2, 0x84, 0x05, 0x8D, 0xFD, 0xF6, 0x00, 0xB8, 0xFF, 0x09, 0x00,
+	0x01, 0x00, 0x0B, 0x00, 0xD5, 0xFF, 0x44, 0x00, 0xDD, 0xFF, 0x77,
+	0xFF, 0x67, 0x02, 0x14, 0xF8, 0xDC, 0x44, 0xD5, 0x13, 0xBC, 0xF6,
+	0x7C, 0x05, 0xC5, 0xFC, 0xB7, 0x01, 0x44, 0xFF, 0x31, 0x00, 0xFF,
+	0xFF, 0xFE, 0xFF, 0x35, 0x00, 0x39, 0xFF, 0xD9, 0x01, 0x6D, 0xFC,
+	0x4B, 0x06, 0xCD, 0xF4, 0x83, 0x1A, 0x5F, 0x41, 0x3A, 0xF5, 0x0C,
+	0x04, 0x7B, 0xFE, 0x6C, 0x00, 0xFE, 0xFF, 0xEF, 0xFF, 0x05, 0x00,
+	0x05, 0x00, 0xF3, 0xFF, 0xF5, 0xFF, 0x7D, 0x00, 0x5D, 0xFE, 0x3E,
+	0x04, 0xEA, 0xF4, 0xD9, 0x40, 0x66, 0x1B, 0x93, 0xF4, 0x62, 0x06,
+	0x64, 0xFC, 0xDC, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF,
+	0xFF, 0x31, 0x00, 0x46, 0xFF, 0xB1, 0x01, 0xD3, 0xFC, 0x5D, 0x05,
+	0x01, 0xF7, 0xFB, 0x12, 0x3F, 0x45, 0x83, 0xF8, 0x2A, 0x02, 0x9A,
+	0xFF, 0xCA, 0xFF, 0x4E, 0x00, 0xD1, 0xFF, 0x0C, 0x00, 0x00, 0x00,
+	0x0C, 0x00, 0xB1, 0xFF, 0x04, 0x01, 0x76, 0xFD, 0xA8, 0x05, 0xCC,
+	0xF2, 0xAB, 0x3B, 0x18, 0x23, 0xE0, 0xF2, 0xF7, 0x06, 0x35, 0xFC,
+	0xE6, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x29,
+	0x00, 0x5E, 0xFF, 0x74, 0x01, 0x5F, 0xFD, 0x35, 0x04, 0x7C, 0xF9,
+	0xD8, 0x0B, 0xC9, 0x47, 0xD4, 0xFC, 0xF0, 0xFF, 0xDD, 0x00, 0x19,
+	0xFF, 0xA4, 0x00, 0xAF, 0xFF, 0x13, 0x00, 0xFE, 0xFF, 0x20, 0x00,
+	0x7B, 0xFF, 0x6E, 0x01, 0xCA, 0xFC, 0x9D, 0x06, 0xB1, 0xF1, 0x86,
+	0x35, 0xAE, 0x2A, 0xCD, 0xF1, 0x2B, 0x07, 0x3F, 0xFC, 0xD1, 0x01,
+	0x46, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x21, 0x00, 0x7C,
+	0xFF, 0x27, 0x01, 0x05, 0xFE, 0xEB, 0x02, 0x14, 0xFC, 0x50, 0x05,
+	0xEA, 0x48, 0x1B, 0x02, 0x78, 0xFD, 0x32, 0x02, 0x64, 0xFE, 0xFA,
+	0x00, 0x8D, 0xFF, 0x1C, 0x00, 0xFD, 0xFF, 0x2D, 0x00, 0x54, 0xFF,
+	0xB7, 0x01, 0x5E, 0xFC, 0x19, 0x07, 0x88, 0xF1, 0x9F, 0x2E, 0xE3,
+	0x31, 0x7E, 0xF1, 0xEE, 0x06, 0x88, 0xFC, 0x9A, 0x01, 0x64, 0xFF,
+	0x28, 0x00, 0xFD, 0xFF, 0x18, 0x00, 0x9D, 0xFF, 0xD3, 0x00, 0xB8,
+	0xFE, 0x93, 0x01, 0xA1, 0xFE, 0x8E, 0xFF, 0x92, 0x48, 0x3D, 0x08,
+	0xE1, 0xFA, 0x86, 0x03, 0xB6, 0xFD, 0x4C, 0x01, 0x6D, 0xFF, 0x25,
+	0x00, 0x00, 0x00, 0xFD, 0xFF, 0x34, 0x00, 0x3E, 0xFF, 0xDF, 0x01,
+	0x33, 0xFC, 0x20, 0x07, 0x35, 0xF2, 0x36, 0x27, 0x78, 0x38, 0x14,
+	0xF2, 0x3B, 0x06, 0x11, 0xFD, 0x41, 0x01, 0x92, 0xFF, 0x17, 0x00,
+	0xFF, 0xFF, 0x10, 0x00, 0xBF, 0xFF, 0x7B, 0x00, 0x6C, 0xFF, 0x44,
+	0x00, 0x01, 0x01, 0xB6, 0xFA, 0xC8, 0x46, 0x13, 0x0F, 0x51, 0xF8,
+	0xC4, 0x04, 0x1B, 0xFD, 0x92, 0x01, 0x52, 0xFF, 0x2D, 0x00, 0xFF,
+	0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE5, 0x01, 0x44, 0xFC,
+	0xBD, 0x06, 0x97, 0xF3, 0x8A, 0x1F, 0x31, 0x3E, 0xA5, 0xF3, 0x0F,
+	0x05, 0xDA, 0xFD, 0xC9, 0x00, 0xCF, 0xFF, 0x01, 0x00, 0x02, 0x00,
+	0x09, 0x00, 0xDF, 0xFF, 0x28, 0x00, 0x17, 0x00, 0x10, 0xFF, 0x15,
+	0x03, 0xDD, 0xF6, 0x9E, 0x43, 0x6C, 0x16, 0xF1, 0xF5, 0xD3, 0x05,
+	0x9F, 0xFC, 0xC6, 0x01, 0x3F, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0xFE,
+	0xFF, 0x34, 0x00, 0x3C, 0xFF, 0xCE, 0x01, 0x8C, 0xFC, 0x00, 0x06,
+	0x86, 0xF5, 0xE0, 0x17, 0xDB, 0x42, 0x3F, 0xF6, 0x71, 0x03, 0xD9,
+	0xFE, 0x36, 0x00, 0x18, 0x00, 0xE5, 0xFF, 0x07, 0x00, 0x03, 0x00,
+	0xFC, 0xFF, 0xDC, 0xFF, 0xAF, 0x00, 0x07, 0xFE, 0xC8, 0x04, 0x10,
+	0xF4, 0x2D, 0x3F, 0x0F, 0x1E, 0xED, 0xF3, 0xA0, 0x06, 0x4E, 0xFC,
+	0xE3, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x2E,
+	0x00, 0x4E, 0xFF, 0x9E, 0x01, 0x00, 0xFD, 0xFC, 0x04, 0xD7, 0xF7,
+	0x75, 0x10, 0x48, 0x46, 0xE4, 0xF9, 0x6E, 0x01, 0x06, 0x00, 0x8E,
+	0xFF, 0x6B, 0x00, 0xC6, 0xFF, 0x0E, 0x00, 0xFF, 0xFF, 0x13, 0x00,
+	0x9D, 0xFF, 0x2D, 0x01, 0x33, 0xFD, 0x0B, 0x06, 0x4D, 0xF2, 0xA5,
+	0x39, 0xBF, 0x25, 0x6D, 0xF2, 0x15, 0x07, 0x31, 0xFC, 0xE2, 0x01,
+	0x3B, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x26, 0x00, 0x68,
+	0xFF, 0x5B, 0x01, 0x96, 0xFD, 0xC6, 0x03, 0x61, 0xFA, 0x81, 0x09,
+	0x57, 0x48, 0x8D, 0xFE, 0x1B, 0xFF, 0x52, 0x01, 0xDB, 0xFE, 0xC2,
+	0x00, 0xA4, 0xFF, 0x16, 0x00, 0xFD, 0xFF, 0x25, 0x00, 0x6C, 0xFF,
+	0x8B, 0x01, 0x9D, 0xFC, 0xD5, 0x06, 0x89, 0xF1, 0x35, 0x33, 0x3A,
+	0x2D, 0x9A, 0xF1, 0x23, 0x07, 0x51, 0xFC, 0xC2, 0x01, 0x4F, 0xFF,
+	0x2F, 0x00, 0xFD, 0xFF, 0x1E, 0x00, 0x87, 0xFF, 0x0B, 0x01, 0x42,
+	0xFE, 0x74, 0x02, 0xF9, 0xFC, 0x39, 0x03, 0xF5, 0x48, 0x24, 0x04,
+	0x94, 0xFC, 0xA9, 0x02, 0x27, 0xFE, 0x18, 0x01, 0x82, 0xFF, 0x1F,
+	0x00, 0x00, 0x00, 0xFD, 0xFF, 0x30, 0x00, 0x4B, 0xFF, 0xC9, 0x01,
+	0x48, 0xFC, 0x28, 0x07, 0xAD, 0xF1, 0x19, 0x2C, 0x3F, 0x34, 0x97,
+	0xF1, 0xBE, 0x06, 0xB0, 0xFC, 0x7F, 0x01, 0x72, 0xFF, 0x23, 0x00,
+	0xFE, 0xFF, 0x15, 0x00, 0xA9, 0xFF, 0xB4, 0x00, 0xF7, 0xFE, 0x1D,
+	0x01, 0x7A, 0xFF, 0xC5, 0xFD, 0x1D, 0x48, 0x89, 0x0A, 0xFB, 0xF9,
+	0xF8, 0x03, 0x7D, 0xFD, 0x66, 0x01, 0x63, 0xFF, 0x28, 0x00, 0x00,
+	0x00, 0xFD, 0xFF, 0x36, 0x00, 0x39, 0xFF, 0xE4, 0x01, 0x32, 0xFC,
+	0x09, 0x07, 0x9D, 0xF2, 0x92, 0x24, 0x8F, 0x3A, 0x82, 0xF2, 0xE1,
+	0x05, 0x50, 0xFD, 0x1B, 0x01, 0xA6, 0xFF, 0x10, 0x00, 0x00, 0x00,
+	0x0D, 0x00, 0xCB, 0xFF, 0x5E, 0x00, 0xA9, 0xFF, 0xD6, 0xFF, 0xC3,
+	0x01, 0x43, 0xF9, 0xD7, 0x45, 0x92, 0x11, 0x77, 0xF7, 0x28, 0x05,
+	0xEC, 0xFC, 0xA7, 0x01, 0x4A, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, 0xFE,
+	0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE0, 0x01, 0x57, 0xFC, 0x85, 0x06,
+	0x34, 0xF4, 0xE0, 0x1C, 0xF0, 0x3F, 0x6D, 0xF4, 0x8C, 0x04, 0x2C,
+	0xFE, 0x99, 0x00, 0xE7, 0xFF, 0xF8, 0xFF, 0x04, 0x00, 0x06, 0x00,
+	0xEA, 0xFF, 0x0C, 0x00, 0x4E, 0x00, 0xAF, 0xFE, 0xB8, 0x03, 0xC7,
+	0xF5, 0x38, 0x42, 0x0C, 0x19, 0x32, 0xF5, 0x23, 0x06, 0x7D, 0xFC,
+	0xD3, 0x01, 0x3A, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x32,
+	0x00, 0x41, 0xFF, 0xC0, 0x01, 0xAF, 0xFC, 0xAD, 0x05, 0x4A, 0xF6,
+	0x44, 0x15, 0x2F, 0x44, 0x64, 0xF7, 0xC9, 0x02, 0x3D, 0xFF, 0xFE,
+	0xFF, 0x34, 0x00, 0xDB, 0xFF, 0x09, 0x00, 0x02, 0x00, 0x05, 0x00,
+	0xC5, 0xFF, 0xDE, 0x00, 0xB7, 0xFD, 0x45, 0x05, 0x56, 0xF3, 0x61,
+	0x3D, 0xBA, 0x20, 0x56, 0xF3, 0xD3, 0x06, 0x3E, 0xFC, 0xE6, 0x01,
+	0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFF, 0xFF, 0x2C, 0x00, 0x56,
+	0xFF, 0x88, 0x01, 0x31, 0xFD, 0x95, 0x04, 0xB4, 0xF8, 0xFC, 0x0D,
+	0x26, 0x47, 0x64, 0xFB, 0xA7, 0x00, 0x77, 0x00, 0x51, 0xFF, 0x89,
+	0x00, 0xBA, 0xFF, 0x11, 0x00, 0xFF, 0xFF, 0x1A, 0x00, 0x8A, 0xFF,
+	0x51, 0x01, 0xF8, 0xFC, 0x5E, 0x06, 0xED, 0xF1, 0x82, 0x37, 0x60,
+	0x28, 0x0E, 0xF2, 0x26, 0x07, 0x35, 0xFC, 0xDB, 0x01, 0x40, 0xFF,
+	0x34, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x23, 0x00, 0x72, 0xFF, 0x40,
+	0x01, 0xD0, 0xFD, 0x53, 0x03, 0x47, 0xFB, 0x3F, 0x07, 0xB8, 0x48,
+	0x62, 0x00, 0x3F, 0xFE, 0xC8, 0x01, 0x9C, 0xFE, 0xE0, 0x00, 0x98,
+	0xFF, 0x19, 0x00, 0xFD, 0xFF, 0x29, 0x00, 0x5F, 0xFF, 0xA5, 0x01,
+	0x78, 0xFC, 0xFF, 0x06, 0x7D, 0xF1, 0xCF, 0x30, 0xB8, 0x2F, 0x80,
+	0xF1, 0x0D, 0x07, 0x6A, 0xFC, 0xAE, 0x01, 0x59, 0xFF, 0x2B, 0x00,
+	0xFD, 0xFF, 0x1B, 0x00, 0x93, 0xFF, 0xED, 0x00, 0x80, 0xFE, 0xFD,
+	0x01, 0xDC, 0xFD, 0x3C, 0x01, 0xD5, 0x48, 0x45, 0x06, 0xAE, 0xFB,
+	0x1F, 0x03, 0xEA, 0xFD, 0x34, 0x01, 0x77, 0xFF, 0x22, 0x00, 0x00,
+	0x00, 0xFD, 0xFF, 0x33, 0x00, 0x43, 0xFF, 0xD6, 0x01, 0x39, 0xFC,
+	0x2A, 0x07, 0xEB, 0xF1, 0x87, 0x29, 0x85, 0x36, 0xCC, 0xF1, 0x7F,
+	0x06, 0xE0, 0xFC, 0x60, 0x01, 0x82, 0xFF, 0x1D, 0x00, 0xFE, 0xFF,
+	0x12, 0x00, 0xB5, 0xFF, 0x96, 0x00, 0x35, 0xFF, 0xA9, 0x00, 0x4D,
+	0x00, 0x19, 0xFC, 0x7C, 0x47, 0xE8, 0x0C, 0x18, 0xF9, 0x66, 0x04,
+	0x48, 0xFD, 0x7E, 0x01, 0x5A, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0xFD,
+	0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x38, 0xFC, 0xE6, 0x06,
+	0x19, 0xF3, 0xEA, 0x21, 0x8A, 0x3C, 0x0E, 0xF3, 0x78, 0x05, 0x96,
+	0xFD, 0xF1, 0x00, 0xBB, 0xFF, 0x08, 0x00, 0x01, 0x00, 0x0B, 0x00,
+	0xD6, 0xFF, 0x41, 0x00, 0xE4, 0xFF, 0x6B, 0xFF, 0x7B, 0x02, 0xF0,
+	0xF7, 0xBA, 0x44, 0x1E, 0x14, 0xA5, 0xF6, 0x86, 0x05, 0xC1, 0xFC,
+	0xB9, 0x01, 0x44, 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x35,
+	0x00, 0x39, 0xFF, 0xD8, 0x01, 0x70, 0xFC, 0x43, 0x06, 0xE1, 0xF4,
+	0x38, 0x1A, 0x8C, 0x41, 0x55, 0xF5, 0xFC, 0x03, 0x85, 0xFE, 0x66,
+	0x00, 0x01, 0x00, 0xEE, 0xFF, 0x06, 0x00, 0x05, 0x00, 0xF4, 0xFF,
+	0xF2, 0xFF, 0x83, 0x00, 0x53, 0xFE, 0x4E, 0x04, 0xD0, 0xF4, 0xAB,
+	0x40, 0xB2, 0x1B, 0x7F, 0xF4, 0x69, 0x06, 0x62, 0xFC, 0xDD, 0x01,
+	0x38, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x30, 0x00, 0x47,
+	0xFF, 0xAF, 0x01, 0xD8, 0xFC, 0x52, 0x05, 0x19, 0xF7, 0xB2, 0x12,
+	0x5C, 0x45, 0xA9, 0xF8, 0x16, 0x02, 0xA6, 0xFF, 0xC3, 0xFF, 0x51,
+	0x00, 0xD0, 0xFF, 0x0C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0xAF, 0xFF,
+	0x09, 0x01, 0x6E, 0xFD, 0xB4, 0x05, 0xBC, 0xF2, 0x73, 0x3B, 0x64,
+	0x23, 0xD2, 0xF2, 0xFB, 0x06, 0x34, 0xFC, 0xE6, 0x01, 0x38, 0xFF,
+	0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x29, 0x00, 0x5F, 0xFF, 0x71,
+	0x01, 0x65, 0xFD, 0x29, 0x04, 0x96, 0xF9, 0x95, 0x0B, 0xDC, 0x47,
+	0x03, 0xFD, 0xD9, 0xFF, 0xEA, 0x00, 0x12, 0xFF, 0xA7, 0x00, 0xAE,
+	0xFF, 0x14, 0x00, 0xFE, 0xFF, 0x20, 0x00, 0x79, 0xFF, 0x72, 0x01,
+	0xC4, 0xFC, 0xA4, 0x06, 0xAB, 0xF1, 0x46, 0x35, 0xF7, 0x2A, 0xC6,
+	0xF1, 0x2A, 0x07, 0x40, 0xFC, 0xCF, 0x01, 0x47, 0xFF, 0x31, 0x00,
+	0xFD, 0xFF, 0x00, 0x00, 0x20, 0x00, 0x7D, 0xFF, 0x24, 0x01, 0x0C,
+	0xFE, 0xDE, 0x02, 0x2E, 0xFC, 0x13, 0x05, 0xEC, 0x48, 0x54, 0x02,
+	0x5E, 0xFD, 0x3F, 0x02, 0x5D, 0xFE, 0xFE, 0x00, 0x8C, 0xFF, 0x1C,
+	0x00, 0xFD, 0xFF, 0x2D, 0x00, 0x53, 0xFF, 0xBA, 0x01, 0x5B, 0xFC,
+	0x1B, 0x07, 0x8B, 0xF1, 0x58, 0x2E, 0x26, 0x32, 0x80, 0xF1, 0xEA,
+	0x06, 0x8C, 0xFC, 0x97, 0x01, 0x66, 0xFF, 0x27, 0x00, 0xFD, 0xFF,
+	0x17, 0x00, 0x9E, 0xFF, 0xCF, 0x00, 0xBF, 0xFE, 0x86, 0x01, 0xBA,
+	0xFE, 0x5A, 0xFF, 0x86, 0x48, 0x7D, 0x08, 0xC7, 0xFA, 0x93, 0x03,
+	0xB0, 0xFD, 0x4F, 0x01, 0x6C, 0xFF, 0x25, 0x00, 0x00, 0x00, 0xFD,
+	0xFF, 0x35, 0x00, 0x3D, 0xFF, 0xDF, 0x01, 0x32, 0xFC, 0x1E, 0x07,
+	0x40, 0xF2, 0xEB, 0x26, 0xB5, 0x38, 0x1F, 0xF2, 0x32, 0x06, 0x18,
+	0xFD, 0x3D, 0x01, 0x94, 0xFF, 0x16, 0x00, 0xFF, 0xFF, 0x0F, 0x00,
+	0xC0, 0xFF, 0x78, 0x00, 0x73, 0xFF, 0x38, 0x00, 0x17, 0x01, 0x8B,
+	0xFA, 0xAF, 0x46, 0x59, 0x0F, 0x39, 0xF8, 0xCF, 0x04, 0x15, 0xFD,
+	0x95, 0x01, 0x51, 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x36,
+	0x00, 0x36, 0xFF, 0xE5, 0x01, 0x46, 0xFC, 0xB8, 0x06, 0xA8, 0xF3,
+	0x3F, 0x1F, 0x64, 0x3E, 0xBA, 0xF3, 0x01, 0x05, 0xE2, 0xFD, 0xC4,
+	0x00, 0xD2, 0xFF, 0x00, 0x00, 0x02, 0x00, 0x08, 0x00, 0xE1, 0xFF,
+	0x25, 0x00, 0x1D, 0x00, 0x05, 0xFF, 0x28, 0x03, 0xBD, 0xF6, 0x77,
+	0x43, 0xB6, 0x16, 0xDC, 0xF5, 0xDD, 0x05, 0x9B, 0xFC, 0xC8, 0x01,
+	0x3E, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x34, 0x00, 0x3D,
+	0xFF, 0xCC, 0x01, 0x8F, 0xFC, 0xF8, 0x05, 0x9B, 0xF5, 0x96, 0x17,
+	0x02, 0x43, 0x5E, 0xF6, 0x5F, 0x03, 0xE4, 0xFE, 0x30, 0x00, 0x1B,
+	0x00, 0xE4, 0xFF, 0x08, 0x00, 0x03, 0x00, 0xFD, 0xFF, 0xD9, 0xFF,
+	0xB4, 0x00, 0xFD, 0xFD, 0xD7, 0x04, 0xFA, 0xF3, 0xFC, 0x3E, 0x5B,
+	0x1E, 0xDB, 0xF3, 0xA6, 0x06, 0x4C, 0xFC, 0xE3, 0x01, 0x36, 0xFF,
+	0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x2E, 0x00, 0x4E, 0xFF, 0x9C,
+	0x01, 0x05, 0xFD, 0xF1, 0x04, 0xF0, 0xF7, 0x2D, 0x10, 0x61, 0x46,
+	0x0D, 0xFA, 0x58, 0x01, 0x13, 0x00, 0x87, 0xFF, 0x6E, 0x00, 0xC4,
+	0xFF, 0x0E, 0x00, 0xFF, 0xFF, 0x14, 0x00, 0x9B, 0xFF, 0x31, 0x01,
+	0x2C, 0xFD, 0x15, 0x06, 0x41, 0xF2, 0x6A, 0x39, 0x0A, 0x26, 0x61,
+	0xF2, 0x17, 0x07, 0x31, 0xFC, 0xE2, 0x01, 0x3B, 0xFF, 0x35, 0x00,
+	0xFD, 0xFF, 0x00, 0x00, 0x26, 0x00, 0x69, 0xFF, 0x58, 0x01, 0x9D,
+	0xFD, 0xB9, 0x03, 0x7B, 0xFA, 0x40, 0x09, 0x63, 0x48, 0xBF, 0xFE,
+	0x03, 0xFF, 0x5F, 0x01, 0xD4, 0xFE, 0xC5, 0x00, 0xA2, 0xFF, 0x16,
+	0x00, 0xFD, 0xFF, 0x25, 0x00, 0x6A, 0xFF, 0x8E, 0x01, 0x99, 0xFC,
+	0xDB, 0x06, 0x86, 0xF1, 0xF2, 0x32, 0x82, 0x2D, 0x96, 0xF1, 0x21,
+	0x07, 0x53, 0xFC, 0xC0, 0x01, 0x50, 0xFF, 0x2E, 0x00, 0xFD, 0xFF,
+	0x1D, 0x00, 0x88, 0xFF, 0x07, 0x01, 0x49, 0xFE, 0x67, 0x02, 0x13,
+	0xFD, 0xFF, 0x02, 0xF4, 0x48, 0x5F, 0x04, 0x7A, 0xFC, 0xB6, 0x02,
+	0x20, 0xFE, 0x1B, 0x01, 0x81, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0xFD,
+	0xFF, 0x30, 0x00, 0x4A, 0xFF, 0xCA, 0x01, 0x46, 0xFC, 0x29, 0x07,
+	0xB3, 0xF1, 0xD1, 0x2B, 0x81, 0x34, 0x9C, 0xF1, 0xB8, 0x06, 0xB5,
+	0xFC, 0x7C, 0x01, 0x74, 0xFF, 0x22, 0x00, 0xFE, 0xFF, 0x15, 0x00,
+	0xAA, 0xFF, 0xB1, 0x00, 0xFE, 0xFE, 0x10, 0x01, 0x92, 0xFF, 0x94,
+	0xFD, 0x0D, 0x48, 0xCB, 0x0A, 0xE2, 0xF9, 0x04, 0x04, 0x77, 0xFD,
+	0x69, 0x01, 0x62, 0xFF, 0x28, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36,
+	0x00, 0x39, 0xFF, 0xE5, 0x01, 0x32, 0xFC, 0x06, 0x07, 0xAA, 0xF2,
+	0x46, 0x24, 0xC8, 0x3A, 0x90, 0xF2, 0xD6, 0x05, 0x57, 0xFD, 0x17,
+	0x01, 0xA8, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x0D, 0x00, 0xCC, 0xFF,
+	0x5A, 0x00, 0xAF, 0xFF, 0xCA, 0xFF, 0xD8, 0x01, 0x1C, 0xF9, 0xB8,
+	0x45, 0xDA, 0x11, 0x60, 0xF7, 0x33, 0x05, 0xE7, 0xFC, 0xA9, 0x01,
+	0x4A, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x37,
+	0xFF, 0xDF, 0x01, 0x5A, 0xFC, 0x7E, 0x06, 0x47, 0xF4, 0x94, 0x1C,
+	0x1F, 0x40, 0x85, 0xF4, 0x7D, 0x04, 0x36, 0xFE, 0x93, 0x00, 0xEA,
+	0xFF, 0xF7, 0xFF, 0x04, 0x00, 0x06, 0x00, 0xEB, 0xFF, 0x09, 0x00,
+	0x54, 0x00, 0xA4, 0xFE, 0xC9, 0x03, 0xAA, 0xF5, 0x0C, 0x42, 0x56,
+	0x19, 0x1E, 0xF5, 0x2B, 0x06, 0x7A, 0xFC, 0xD4, 0x01, 0x3A, 0xFF,
+	0x35, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x32, 0x00, 0x42, 0xFF, 0xBE,
+	0x01, 0xB4, 0xFC, 0xA4, 0x05, 0x61, 0xF6, 0xFB, 0x14, 0x53, 0x44,
+	0x86, 0xF7, 0xB6, 0x02, 0x49, 0xFF, 0xF7, 0xFF, 0x37, 0x00, 0xD9,
+	0xFF, 0x0A, 0x00, 0x01, 0x00, 0x06, 0x00, 0xC2, 0xFF, 0xE3, 0x00,
+	0xAE, 0xFD, 0x52, 0x05, 0x44, 0xF3, 0x2A, 0x3D, 0x06, 0x21, 0x47,
+	0xF3, 0xD8, 0x06, 0x3C, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00,
+	0xFD, 0xFF, 0x00, 0x00, 0x2B, 0x00, 0x57, 0xFF, 0x86, 0x01, 0x36,
+	0xFD, 0x89, 0x04, 0xCD, 0xF8, 0xB7, 0x0D, 0x3D, 0x47, 0x91, 0xFB,
+	0x91, 0x00, 0x83, 0x00, 0x4A, 0xFF, 0x8C, 0x00, 0xB9, 0xFF, 0x11,
+	0x00, 0xFE, 0xFF, 0x1B, 0x00, 0x88, 0xFF, 0x55, 0x01, 0xF2, 0xFC,
+	0x67, 0x06, 0xE4, 0xF1, 0x44, 0x37, 0xAA, 0x28, 0x05, 0xF2, 0x27,
+	0x07, 0x36, 0xFC, 0xDA, 0x01, 0x41, 0xFF, 0x33, 0x00, 0xFD, 0xFF,
+	0x00, 0x00, 0x23, 0x00, 0x73, 0xFF, 0x3D, 0x01, 0xD6, 0xFD, 0x46,
+	0x03, 0x61, 0xFB, 0x00, 0x07, 0xBF, 0x48, 0x98, 0x00, 0x26, 0xFE,
+	0xD5, 0x01, 0x95, 0xFE, 0xE3, 0x00, 0x96, 0xFF, 0x1A, 0x00, 0xFD,
+	0xFF, 0x2A, 0x00, 0x5D, 0xFF, 0xA7, 0x01, 0x75, 0xFC, 0x03, 0x07,
+	0x7D, 0xF1, 0x8A, 0x30, 0xFF, 0x2F, 0x7E, 0xF1, 0x0A, 0x07, 0x6E,
+	0xFC, 0xAC, 0x01, 0x5A, 0xFF, 0x2B, 0x00, 0xFD, 0xFF, 0x1A, 0x00,
+	0x94, 0xFF, 0xEA, 0x00, 0x87, 0xFE, 0xF0, 0x01, 0xF5, 0xFD, 0x05,
+	0x01, 0xCE, 0x48, 0x83, 0x06, 0x94, 0xFB, 0x2C, 0x03, 0xE4, 0xFD,
+	0x37, 0x01, 0x76, 0xFF, 0x22, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x33,
+	0x00, 0x42, 0xFF, 0xD7, 0x01, 0x38, 0xFC, 0x29, 0x07, 0xF3, 0xF1,
+	0x3E, 0x29, 0xC6, 0x36, 0xD4, 0xF1, 0x77, 0x06, 0xE6, 0xFC, 0x5C,
+	0x01, 0x84, 0xFF, 0x1C, 0x00, 0xFE, 0xFF, 0x12, 0x00, 0xB6, 0xFF,
+	0x93, 0x00, 0x3C, 0xFF, 0x9D, 0x00, 0x63, 0x00, 0xEB, 0xFB, 0x69,
+	0x47, 0x2D, 0x0D, 0xFF, 0xF8, 0x72, 0x04, 0x42, 0xFD, 0x81, 0x01,
+	0x59, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x37,
+	0xFF, 0xE6, 0x01, 0x3A, 0xFC, 0xE2, 0x06, 0x28, 0xF3, 0x9E, 0x21,
+	0xC0, 0x3C, 0x1F, 0xF3, 0x6C, 0x05, 0x9E, 0xFD, 0xED, 0x00, 0xBD,
+	0xFF, 0x07, 0x00, 0x01, 0x00, 0x0A, 0x00, 0xD7, 0xFF, 0x3E, 0x00,
+	0xEA, 0xFF, 0x60, 0xFF, 0x8F, 0x02, 0xCD, 0xF7, 0x99, 0x44, 0x68,
+	0x14, 0x8E, 0xF6, 0x90, 0x05, 0xBC, 0xFC, 0xBA, 0x01, 0x43, 0xFF,
+	0x32, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x35, 0x00, 0x39, 0xFF, 0xD7,
+	0x01, 0x73, 0xFC, 0x3B, 0x06, 0xF5, 0xF4, 0xED, 0x19, 0xB7, 0x41,
+	0x71, 0xF5, 0xEB, 0x03, 0x90, 0xFE, 0x60, 0x00, 0x04, 0x00, 0xED,
+	0xFF, 0x06, 0x00, 0x04, 0x00, 0xF5, 0xFF, 0xEF, 0xFF, 0x88, 0x00,
+	0x49, 0xFE, 0x5D, 0x04, 0xB7, 0xF4, 0x7D, 0x40, 0xFD, 0x1B, 0x6C,
+	0xF4, 0x70, 0x06, 0x5F, 0xFC, 0xDE, 0x01, 0x37, 0xFF, 0x36, 0x00,
+	0xFE, 0xFF, 0xFF, 0xFF, 0x30, 0x00, 0x48, 0xFF, 0xAD, 0x01, 0xDD,
+	0xFC, 0x48, 0x05, 0x30, 0xF7, 0x6B, 0x12, 0x7D, 0x45, 0xCF, 0xF8,
+	0x01, 0x02, 0xB2, 0xFF, 0xBD, 0xFF, 0x54, 0x00, 0xCE, 0xFF, 0x0C,
+	0x00, 0x00, 0x00, 0x0E, 0x00, 0xAC, 0xFF, 0x0E, 0x01, 0x66, 0xFD,
+	0xBF, 0x05, 0xAD, 0xF2, 0x3B, 0x3B, 0xB0, 0x23, 0xC4, 0xF2, 0xFF,
+	0x06, 0x33, 0xFC, 0xE5, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFD, 0xFF,
+	0x00, 0x00, 0x29, 0x00, 0x60, 0xFF, 0x6E, 0x01, 0x6B, 0xFD, 0x1D,
+	0x04, 0xAF, 0xF9, 0x51, 0x0B, 0xEC, 0x47, 0x33, 0xFD, 0xC1, 0xFF,
+	0xF7, 0x00, 0x0C, 0xFF, 0xAA, 0x00, 0xAD, 0xFF, 0x14, 0x00, 0xFE,
+	0xFF, 0x21, 0x00, 0x77, 0xFF, 0x75, 0x01, 0xBF, 0xFC, 0xAB, 0x06,
+	0xA6, 0xF1, 0x05, 0x35, 0x40, 0x2B, 0xBF, 0xF1, 0x2A, 0x07, 0x42,
+	0xFC, 0xCE, 0x01, 0x48, 0xFF, 0x31, 0x00, 0xFD, 0xFF, 0x00, 0x00,
+	0x20, 0x00, 0x7E, 0xFF, 0x21, 0x01, 0x12, 0xFE, 0xD1, 0x02, 0x47,
+	0xFC, 0xD7, 0x04, 0xF0, 0x48, 0x8D, 0x02, 0x45, 0xFD, 0x4D, 0x02,
+	0x56, 0xFE, 0x01, 0x01, 0x8B, 0xFF, 0x1D, 0x00, 0xFD, 0xFF, 0x2E,
+	0x00, 0x52, 0xFF, 0xBC, 0x01, 0x58, 0xFC, 0x1D, 0x07, 0x8E, 0xF1,
+	0x11, 0x2E, 0x6B, 0x32, 0x81, 0xF1, 0xE5, 0x06, 0x90, 0xFC, 0x94,
+	0x01, 0x67, 0xFF, 0x26, 0x00, 0xFD, 0xFF, 0x17, 0x00, 0xA0, 0xFF,
+	0xCC, 0x00, 0xC6, 0xFE, 0x79, 0x01, 0xD2, 0xFE, 0x26, 0xFF, 0x7C,
+	0x48, 0xBE, 0x08, 0xAE, 0xFA, 0xA0, 0x03, 0xA9, 0xFD, 0x52, 0x01,
+	0x6B, 0xFF, 0x25, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3C,
+	0xFF, 0xE0, 0x01, 0x32, 0xFC, 0x1C, 0x07, 0x4B, 0xF2, 0xA0, 0x26,
+	0xF2, 0x38, 0x2A, 0xF2, 0x28, 0x06, 0x1F, 0xFD, 0x39, 0x01, 0x96,
+	0xFF, 0x16, 0x00, 0xFF, 0xFF, 0x0F, 0x00, 0xC2, 0xFF, 0x75, 0x00,
+	0x7A, 0xFF, 0x2B, 0x00, 0x2D, 0x01, 0x61, 0xFA, 0x97, 0x46, 0xA0,
+	0x0F, 0x20, 0xF8, 0xDA, 0x04, 0x10, 0xFD, 0x97, 0x01, 0x50, 0xFF,
+	0x2E, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE4,
+	0x01, 0x48, 0xFC, 0xB2, 0x06, 0xB9, 0xF3, 0xF3, 0x1E, 0x98, 0x3E,
+	0xCF, 0xF3, 0xF3, 0x04, 0xEB, 0xFD, 0xBF, 0x00, 0xD4, 0xFF, 0xFF,
+	0xFF, 0x03, 0x00, 0x08, 0x00, 0xE2, 0xFF, 0x21, 0x00, 0x23, 0x00,
+	0xFA, 0xFE, 0x3A, 0x03, 0x9D, 0xF6, 0x50, 0x43, 0x00, 0x17, 0xC6,
+	0xF5, 0xE6, 0x05, 0x97, 0xFC, 0xC9, 0x01, 0x3E, 0xFF, 0x34, 0x00,
+	0xFE, 0xFF, 0xFE, 0xFF, 0x34, 0x00, 0x3D, 0xFF, 0xCB, 0x01, 0x93,
+	0xFC, 0xEF, 0x05, 0xB0, 0xF5, 0x4B, 0x17, 0x2A, 0x43, 0x7D, 0xF6,
+	0x4D, 0x03, 0xEF, 0xFE, 0x2A, 0x00, 0x1E, 0x00, 0xE3, 0xFF, 0x08,
+	0x00, 0x03, 0x00, 0xFE, 0xFF, 0xD7, 0xFF, 0xBA, 0x00, 0xF4, 0xFD,
+	0xE5, 0x04, 0xE4, 0xF3, 0xCA, 0x3E, 0xA7, 0x1E, 0xCA, 0xF3, 0xAC,
+	0x06, 0x4A, 0xFC, 0xE4, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF,
+	0xFF, 0xFF, 0x2E, 0x00, 0x4F, 0xFF, 0x99, 0x01, 0x0B, 0xFD, 0xE6,
+	0x04, 0x08, 0xF8, 0xE7, 0x0F, 0x7C, 0x46, 0x37, 0xFA, 0x42, 0x01,
+	0x1F, 0x00, 0x81, 0xFF, 0x71, 0x00, 0xC3, 0xFF, 0x0F, 0x00, 0xFF,
+	0xFF, 0x15, 0x00, 0x98, 0xFF, 0x35, 0x01, 0x25, 0xFD, 0x1E, 0x06,
+	0x35, 0xF2, 0x2E, 0x39, 0x55, 0x26, 0x56, 0xF2, 0x1A, 0x07, 0x31,
+	0xFC, 0xE1, 0x01, 0x3C, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x00, 0x00,
+	0x26, 0x00, 0x6A, 0xFF, 0x55, 0x01, 0xA3, 0xFD, 0xAD, 0x03, 0x94,
+	0xFA, 0xFF, 0x08, 0x70, 0x48, 0xF3, 0xFE, 0xEA, 0xFE, 0x6C, 0x01,
+	0xCD, 0xFE, 0xC9, 0x00, 0xA1, 0xFF, 0x17, 0x00, 0xFD, 0xFF, 0x26,
+	0x00, 0x69, 0xFF, 0x91, 0x01, 0x94, 0xFC, 0xE0, 0x06, 0x84, 0xF1,
+	0xAF, 0x32, 0xCA, 0x2D, 0x92, 0xF1, 0x1F, 0x07, 0x56, 0xFC, 0xBE,
+	0x01, 0x51, 0xFF, 0x2E, 0x00, 0xFD, 0xFF, 0x1D, 0x00, 0x8A, 0xFF,
+	0x04, 0x01, 0x50, 0xFE, 0x5A, 0x02, 0x2C, 0xFD, 0xC6, 0x02, 0xF2,
+	0x48, 0x9B, 0x04, 0x61, 0xFC, 0xC3, 0x02, 0x19, 0xFE, 0x1E, 0x01,
+	0x7F, 0xFF, 0x20, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x31, 0x00, 0x49,
+	0xFF, 0xCC, 0x01, 0x44, 0xFC, 0x29, 0x07, 0xB9, 0xF1, 0x89, 0x2B,
+	0xC3, 0x34, 0xA0, 0xF1, 0xB1, 0x06, 0xBA, 0xFC, 0x79, 0x01, 0x76,
+	0xFF, 0x21, 0x00, 0xFE, 0xFF, 0x14, 0x00, 0xAC, 0xFF, 0xAE, 0x00,
+	0x05, 0xFF, 0x03, 0x01, 0xAA, 0xFF, 0x63, 0xFD, 0xFD, 0x47, 0x0E,
+	0x0B, 0xC8, 0xF9, 0x11, 0x04, 0x71, 0xFD, 0x6C, 0x01, 0x61, 0xFF,
+	0x28, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x39, 0xFF, 0xE5,
+	0x01, 0x33, 0xFC, 0x03, 0x07, 0xB7, 0xF2, 0xFC, 0x23, 0x03, 0x3B,
+	0x9E, 0xF2, 0xCB, 0x05, 0x5F, 0xFD, 0x12, 0x01, 0xAA, 0xFF, 0x0E,
+	0x00, 0x00, 0x00, 0x0C, 0x00, 0xCD, 0xFF, 0x57, 0x00, 0xB6, 0xFF,
+	0xBE, 0xFF, 0xED, 0x01, 0xF5, 0xF8, 0x9B, 0x45, 0x22, 0x12, 0x48,
+	0xF7, 0x3D, 0x05, 0xE2, 0xFC, 0xAB, 0x01, 0x49, 0xFF, 0x30, 0x00,
+	0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xDF, 0x01, 0x5C,
+	0xFC, 0x78, 0x06, 0x5A, 0xF4, 0x49, 0x1C, 0x4E, 0x40, 0x9E, 0xF4,
+	0x6D, 0x04, 0x3F, 0xFE, 0x8E, 0x00, 0xED, 0xFF, 0xF6, 0xFF, 0x04,
+	0x00, 0x06, 0x00, 0xEC, 0xFF, 0x06, 0x00, 0x5A, 0x00, 0x9A, 0xFE,
+	0xDA, 0x03, 0x8D, 0xF5, 0xE1, 0x41, 0xA1, 0x19, 0x09, 0xF5, 0x33,
+	0x06, 0x77, 0xFC, 0xD6, 0x01, 0x3A, 0xFF, 0x35, 0x00, 0xFE, 0xFF,
+	0xFF, 0xFF, 0x32, 0x00, 0x42, 0xFF, 0xBC, 0x01, 0xB8, 0xFC, 0x9A,
+	0x05, 0x77, 0xF6, 0xB1, 0x14, 0x77, 0x44, 0xA9, 0xF7, 0xA2, 0x02,
+	0x54, 0xFF, 0xF1, 0xFF, 0x3A, 0x00, 0xD8, 0xFF, 0x0A, 0x00, 0x01,
+	0x00, 0x07, 0x00, 0xC0, 0xFF, 0xE8, 0x00, 0xA6, 0xFD, 0x5F, 0x05,
+	0x31, 0xF3, 0xF6, 0x3C, 0x52, 0x21, 0x37, 0xF3, 0xDD, 0x06, 0x3B,
+	0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00,
+	0x2B, 0x00, 0x58, 0xFF, 0x83, 0x01, 0x3C, 0xFD, 0x7E, 0x04, 0xE6,
+	0xF8, 0x72, 0x0D, 0x52, 0x47, 0xBE, 0xFB, 0x7A, 0x00, 0x90, 0x00,
+	0x43, 0xFF, 0x8F, 0x00, 0xB7, 0xFF, 0x11, 0x00, 0xFE, 0xFF, 0x1C,
+	0x00, 0x86, 0xFF, 0x59, 0x01, 0xEC, 0xFC, 0x6F, 0x06, 0xDC, 0xF1,
+	0x04, 0x37, 0xF3, 0x28, 0xFC, 0xF1, 0x28, 0x07, 0x37, 0xFC, 0xD8,
+	0x01, 0x41, 0xFF, 0x33, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x23, 0x00,
+	0x74, 0xFF, 0x3A, 0x01, 0xDD, 0xFD, 0x39, 0x03, 0x7B, 0xFB, 0xC1,
+	0x06, 0xC7, 0x48, 0xCF, 0x00, 0x0D, 0xFE, 0xE3, 0x01, 0x8E, 0xFE,
+	0xE7, 0x00, 0x95, 0xFF, 0x1A, 0x00, 0xFD, 0xFF, 0x2A, 0x00, 0x5C,
+	0xFF, 0xAA, 0x01, 0x71, 0xFC, 0x07, 0x07, 0x7E, 0xF1, 0x44, 0x30,
+	0x44, 0x30, 0x7E, 0xF1, 0x07, 0x07, 0x71, 0xFC, 0xAA, 0x01, 0x5C,
+	0xFF, 0x2A, 0x00, 0xFD, 0xFF, 0x1A, 0x00, 0x95, 0xFF, 0xE7, 0x00,
+	0x8E, 0xFE, 0xE3, 0x01, 0x0D, 0xFE, 0xCF, 0x00, 0xC7, 0x48, 0xC1,
+	0x06, 0x7B, 0xFB, 0x39, 0x03, 0xDD, 0xFD, 0x3A, 0x01, 0x74, 0xFF,
+	0x23, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x33, 0x00, 0x41, 0xFF, 0xD8,
+	0x01, 0x37, 0xFC, 0x28, 0x07, 0xFC, 0xF1, 0xF3, 0x28, 0x04, 0x37,
+	0xDC, 0xF1, 0x6F, 0x06, 0xEC, 0xFC, 0x59, 0x01, 0x86, 0xFF, 0x1C,
+	0x00, 0xFE, 0xFF, 0x11, 0x00, 0xB7, 0xFF, 0x8F, 0x00, 0x43, 0xFF,
+	0x90, 0x00, 0x7A, 0x00, 0xBE, 0xFB, 0x52, 0x47, 0x72, 0x0D, 0xE6,
+	0xF8, 0x7E, 0x04, 0x3C, 0xFD, 0x83, 0x01, 0x58, 0xFF, 0x2B, 0x00,
+	0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3B,
+	0xFC, 0xDD, 0x06, 0x37, 0xF3, 0x52, 0x21, 0xF6, 0x3C, 0x31, 0xF3,
+	0x5F, 0x05, 0xA6, 0xFD, 0xE8, 0x00, 0xC0, 0xFF, 0x07, 0x00, 0x01,
+	0x00, 0x0A, 0x00, 0xD8, 0xFF, 0x3A, 0x00, 0xF1, 0xFF, 0x54, 0xFF,
+	0xA2, 0x02, 0xA9, 0xF7, 0x77, 0x44, 0xB1, 0x14, 0x77, 0xF6, 0x9A,
+	0x05, 0xB8, 0xFC, 0xBC, 0x01, 0x42, 0xFF, 0x32, 0x00, 0xFF, 0xFF,
+	0xFE, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xD6, 0x01, 0x77, 0xFC, 0x33,
+	0x06, 0x09, 0xF5, 0xA1, 0x19, 0xE1, 0x41, 0x8D, 0xF5, 0xDA, 0x03,
+	0x9A, 0xFE, 0x5A, 0x00, 0x06, 0x00, 0xEC, 0xFF, 0x06, 0x00, 0x04,
+	0x00, 0xF6, 0xFF, 0xED, 0xFF, 0x8E, 0x00, 0x3F, 0xFE, 0x6D, 0x04,
+	0x9E, 0xF4, 0x4E, 0x40, 0x49, 0x1C, 0x5A, 0xF4, 0x78, 0x06, 0x5C,
+	0xFC, 0xDF, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF,
+	0x30, 0x00, 0x49, 0xFF, 0xAB, 0x01, 0xE2, 0xFC, 0x3D, 0x05, 0x48,
+	0xF7, 0x22, 0x12, 0x9B, 0x45, 0xF5, 0xF8, 0xED, 0x01, 0xBE, 0xFF,
+	0xB6, 0xFF, 0x57, 0x00, 0xCD, 0xFF, 0x0C, 0x00, 0x00, 0x00, 0x0E,
+	0x00, 0xAA, 0xFF, 0x12, 0x01, 0x5F, 0xFD, 0xCB, 0x05, 0x9E, 0xF2,
+	0x03, 0x3B, 0xFC, 0x23, 0xB7, 0xF2, 0x03, 0x07, 0x33, 0xFC, 0xE5,
+	0x01, 0x39, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x28, 0x00,
+	0x61, 0xFF, 0x6C, 0x01, 0x71, 0xFD, 0x11, 0x04, 0xC8, 0xF9, 0x0E,
+	0x0B, 0xFD, 0x47, 0x63, 0xFD, 0xAA, 0xFF, 0x03, 0x01, 0x05, 0xFF,
+	0xAE, 0x00, 0xAC, 0xFF, 0x14, 0x00, 0xFE, 0xFF, 0x21, 0x00, 0x76,
+	0xFF, 0x79, 0x01, 0xBA, 0xFC, 0xB1, 0x06, 0xA0, 0xF1, 0xC3, 0x34,
+	0x89, 0x2B, 0xB9, 0xF1, 0x29, 0x07, 0x44, 0xFC, 0xCC, 0x01, 0x49,
+	0xFF, 0x31, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x20, 0x00, 0x7F, 0xFF,
+	0x1E, 0x01, 0x19, 0xFE, 0xC3, 0x02, 0x61, 0xFC, 0x9B, 0x04, 0xF2,
+	0x48, 0xC6, 0x02, 0x2C, 0xFD, 0x5A, 0x02, 0x50, 0xFE, 0x04, 0x01,
+	0x8A, 0xFF, 0x1D, 0x00, 0xFD, 0xFF, 0x2E, 0x00, 0x51, 0xFF, 0xBE,
+	0x01, 0x56, 0xFC, 0x1F, 0x07, 0x92, 0xF1, 0xCA, 0x2D, 0xAF, 0x32,
+	0x84, 0xF1, 0xE0, 0x06, 0x94, 0xFC, 0x91, 0x01, 0x69, 0xFF, 0x26,
+	0x00, 0xFD, 0xFF, 0x17, 0x00, 0xA1, 0xFF, 0xC9, 0x00, 0xCD, 0xFE,
+	0x6C, 0x01, 0xEA, 0xFE, 0xF3, 0xFE, 0x70, 0x48, 0xFF, 0x08, 0x94,
+	0xFA, 0xAD, 0x03, 0xA3, 0xFD, 0x55, 0x01, 0x6A, 0xFF, 0x26, 0x00,
+	0x00, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3C, 0xFF, 0xE1, 0x01, 0x31,
+	0xFC, 0x1A, 0x07, 0x56, 0xF2, 0x55, 0x26, 0x2E, 0x39, 0x35, 0xF2,
+	0x1E, 0x06, 0x25, 0xFD, 0x35, 0x01, 0x98, 0xFF, 0x15, 0x00, 0xFF,
+	0xFF, 0x0F, 0x00, 0xC3, 0xFF, 0x71, 0x00, 0x81, 0xFF, 0x1F, 0x00,
+	0x42, 0x01, 0x37, 0xFA, 0x7C, 0x46, 0xE7, 0x0F, 0x08, 0xF8, 0xE6,
+	0x04, 0x0B, 0xFD, 0x99, 0x01, 0x4F, 0xFF, 0x2E, 0x00, 0xFF, 0xFF,
+	0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE4, 0x01, 0x4A, 0xFC, 0xAC,
+	0x06, 0xCA, 0xF3, 0xA7, 0x1E, 0xCA, 0x3E, 0xE4, 0xF3, 0xE5, 0x04,
+	0xF4, 0xFD, 0xBA, 0x00, 0xD7, 0xFF, 0xFE, 0xFF, 0x03, 0x00, 0x08,
+	0x00, 0xE3, 0xFF, 0x1E, 0x00, 0x2A, 0x00, 0xEF, 0xFE, 0x4D, 0x03,
+	0x7D, 0xF6, 0x2A, 0x43, 0x4B, 0x17, 0xB0, 0xF5, 0xEF, 0x05, 0x93,
+	0xFC, 0xCB, 0x01, 0x3D, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0xFE, 0xFF,
+	0x34, 0x00, 0x3E, 0xFF, 0xC9, 0x01, 0x97, 0xFC, 0xE6, 0x05, 0xC6,
+	0xF5, 0x00, 0x17, 0x50, 0x43, 0x9D, 0xF6, 0x3A, 0x03, 0xFA, 0xFE,
+	0x23, 0x00, 0x21, 0x00, 0xE2, 0xFF, 0x08, 0x00, 0x03, 0x00, 0xFF,
+	0xFF, 0xD4, 0xFF, 0xBF, 0x00, 0xEB, 0xFD, 0xF3, 0x04, 0xCF, 0xF3,
+	0x98, 0x3E, 0xF3, 0x1E, 0xB9, 0xF3, 0xB2, 0x06, 0x48, 0xFC, 0xE4,
+	0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x2E, 0x00,
+	0x50, 0xFF, 0x97, 0x01, 0x10, 0xFD, 0xDA, 0x04, 0x20, 0xF8, 0xA0,
+	0x0F, 0x97, 0x46, 0x61, 0xFA, 0x2D, 0x01, 0x2B, 0x00, 0x7A, 0xFF,
+	0x75, 0x00, 0xC2, 0xFF, 0x0F, 0x00, 0xFF, 0xFF, 0x16, 0x00, 0x96,
+	0xFF, 0x39, 0x01, 0x1F, 0xFD, 0x28, 0x06, 0x2A, 0xF2, 0xF2, 0x38,
+	0xA0, 0x26, 0x4B, 0xF2, 0x1C, 0x07, 0x32, 0xFC, 0xE0, 0x01, 0x3C,
+	0xFF, 0x35, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x25, 0x00, 0x6B, 0xFF,
+	0x52, 0x01, 0xA9, 0xFD, 0xA0, 0x03, 0xAE, 0xFA, 0xBE, 0x08, 0x7C,
+	0x48, 0x26, 0xFF, 0xD2, 0xFE, 0x79, 0x01, 0xC6, 0xFE, 0xCC, 0x00,
+	0xA0, 0xFF, 0x17, 0x00, 0xFD, 0xFF, 0x26, 0x00, 0x67, 0xFF, 0x94,
+	0x01, 0x90, 0xFC, 0xE5, 0x06, 0x81, 0xF1, 0x6B, 0x32, 0x11, 0x2E,
+	0x8E, 0xF1, 0x1D, 0x07, 0x58, 0xFC, 0xBC, 0x01, 0x52, 0xFF, 0x2E,
+	0x00, 0xFD, 0xFF, 0x1D, 0x00, 0x8B, 0xFF, 0x01, 0x01, 0x56, 0xFE,
+	0x4D, 0x02, 0x45, 0xFD, 0x8D, 0x02, 0xF0, 0x48, 0xD7, 0x04, 0x47,
+	0xFC, 0xD1, 0x02, 0x12, 0xFE, 0x21, 0x01, 0x7E, 0xFF, 0x20, 0x00,
+	0x00, 0x00, 0xFD, 0xFF, 0x31, 0x00, 0x48, 0xFF, 0xCE, 0x01, 0x42,
+	0xFC, 0x2A, 0x07, 0xBF, 0xF1, 0x40, 0x2B, 0x05, 0x35, 0xA6, 0xF1,
+	0xAB, 0x06, 0xBF, 0xFC, 0x75, 0x01, 0x77, 0xFF, 0x21, 0x00, 0xFE,
+	0xFF, 0x14, 0x00, 0xAD, 0xFF, 0xAA, 0x00, 0x0C, 0xFF, 0xF7, 0x00,
+	0xC1, 0xFF, 0x33, 0xFD, 0xEC, 0x47, 0x51, 0x0B, 0xAF, 0xF9, 0x1D,
+	0x04, 0x6B, 0xFD, 0x6E, 0x01, 0x60, 0xFF, 0x29, 0x00, 0x00, 0x00,
+	0xFD, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xE5, 0x01, 0x33, 0xFC, 0xFF,
+	0x06, 0xC4, 0xF2, 0xB0, 0x23, 0x3B, 0x3B, 0xAD, 0xF2, 0xBF, 0x05,
+	0x66, 0xFD, 0x0E, 0x01, 0xAC, 0xFF, 0x0E, 0x00, 0x00, 0x00, 0x0C,
+	0x00, 0xCE, 0xFF, 0x54, 0x00, 0xBD, 0xFF, 0xB2, 0xFF, 0x01, 0x02,
+	0xCF, 0xF8, 0x7D, 0x45, 0x6B, 0x12, 0x30, 0xF7, 0x48, 0x05, 0xDD,
+	0xFC, 0xAD, 0x01, 0x48, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0xFE, 0xFF,
+	0x36, 0x00, 0x37, 0xFF, 0xDE, 0x01, 0x5F, 0xFC, 0x70, 0x06, 0x6C,
+	0xF4, 0xFD, 0x1B, 0x7D, 0x40, 0xB7, 0xF4, 0x5D, 0x04, 0x49, 0xFE,
+	0x88, 0x00, 0xEF, 0xFF, 0xF5, 0xFF, 0x04, 0x00, 0x06, 0x00, 0xED,
+	0xFF, 0x04, 0x00, 0x60, 0x00, 0x90, 0xFE, 0xEB, 0x03, 0x71, 0xF5,
+	0xB7, 0x41, 0xED, 0x19, 0xF5, 0xF4, 0x3B, 0x06, 0x73, 0xFC, 0xD7,
+	0x01, 0x39, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x32, 0x00,
+	0x43, 0xFF, 0xBA, 0x01, 0xBC, 0xFC, 0x90, 0x05, 0x8E, 0xF6, 0x68,
+	0x14, 0x99, 0x44, 0xCD, 0xF7, 0x8F, 0x02, 0x60, 0xFF, 0xEA, 0xFF,
+	0x3E, 0x00, 0xD7, 0xFF, 0x0A, 0x00, 0x01, 0x00, 0x07, 0x00, 0xBD,
+	0xFF, 0xED, 0x00, 0x9E, 0xFD, 0x6C, 0x05, 0x1F, 0xF3, 0xC0, 0x3C,
+	0x9E, 0x21, 0x28, 0xF3, 0xE2, 0x06, 0x3A, 0xFC, 0xE6, 0x01, 0x37,
+	0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x2B, 0x00, 0x59, 0xFF,
+	0x81, 0x01, 0x42, 0xFD, 0x72, 0x04, 0xFF, 0xF8, 0x2D, 0x0D, 0x69,
+	0x47, 0xEB, 0xFB, 0x63, 0x00, 0x9D, 0x00, 0x3C, 0xFF, 0x93, 0x00,
+	0xB6, 0xFF, 0x12, 0x00, 0xFE, 0xFF, 0x1C, 0x00, 0x84, 0xFF, 0x5C,
+	0x01, 0xE6, 0xFC, 0x77, 0x06, 0xD4, 0xF1, 0xC6, 0x36, 0x3E, 0x29,
+	0xF3, 0xF1, 0x29, 0x07, 0x38, 0xFC, 0xD7, 0x01, 0x42, 0xFF, 0x33,
+	0x00, 0xFD, 0xFF, 0x00, 0x00, 0x22, 0x00, 0x76, 0xFF, 0x37, 0x01,
+	0xE4, 0xFD, 0x2C, 0x03, 0x94, 0xFB, 0x83, 0x06, 0xCE, 0x48, 0x05,
+	0x01, 0xF5, 0xFD, 0xF0, 0x01, 0x87, 0xFE, 0xEA, 0x00, 0x94, 0xFF,
+	0x1A, 0x00, 0xFD, 0xFF, 0x2B, 0x00, 0x5A, 0xFF, 0xAC, 0x01, 0x6E,
+	0xFC, 0x0A, 0x07, 0x7E, 0xF1, 0xFF, 0x2F, 0x8A, 0x30, 0x7D, 0xF1,
+	0x03, 0x07, 0x75, 0xFC, 0xA7, 0x01, 0x5D, 0xFF, 0x2A, 0x00, 0xFD,
+	0xFF, 0x1A, 0x00, 0x96, 0xFF, 0xE3, 0x00, 0x95, 0xFE, 0xD5, 0x01,
+	0x26, 0xFE, 0x98, 0x00, 0xBF, 0x48, 0x00, 0x07, 0x61, 0xFB, 0x46,
+	0x03, 0xD6, 0xFD, 0x3D, 0x01, 0x73, 0xFF, 0x23, 0x00, 0x00, 0x00,
+	0xFD, 0xFF, 0x33, 0x00, 0x41, 0xFF, 0xDA, 0x01, 0x36, 0xFC, 0x27,
+	0x07, 0x05, 0xF2, 0xAA, 0x28, 0x44, 0x37, 0xE4, 0xF1, 0x67, 0x06,
+	0xF2, 0xFC, 0x55, 0x01, 0x88, 0xFF, 0x1B, 0x00, 0xFE, 0xFF, 0x11,
+	0x00, 0xB9, 0xFF, 0x8C, 0x00, 0x4A, 0xFF, 0x83, 0x00, 0x91, 0x00,
+	0x91, 0xFB, 0x3D, 0x47, 0xB7, 0x0D, 0xCD, 0xF8, 0x89, 0x04, 0x36,
+	0xFD, 0x86, 0x01, 0x57, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0xFD, 0xFF,
+	0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3C, 0xFC, 0xD8, 0x06, 0x47,
+	0xF3, 0x06, 0x21, 0x2A, 0x3D, 0x44, 0xF3, 0x52, 0x05, 0xAE, 0xFD,
+	0xE3, 0x00, 0xC2, 0xFF, 0x06, 0x00, 0x01, 0x00, 0x0A, 0x00, 0xD9,
+	0xFF, 0x37, 0x00, 0xF7, 0xFF, 0x49, 0xFF, 0xB6, 0x02, 0x86, 0xF7,
+	0x53, 0x44, 0xFB, 0x14, 0x61, 0xF6, 0xA4, 0x05, 0xB4, 0xFC, 0xBE,
+	0x01, 0x42, 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x35, 0x00,
+	0x3A, 0xFF, 0xD4, 0x01, 0x7A, 0xFC, 0x2B, 0x06, 0x1E, 0xF5, 0x56,
+	0x19, 0x0C, 0x42, 0xAA, 0xF5, 0xC9, 0x03, 0xA4, 0xFE, 0x54, 0x00,
+	0x09, 0x00, 0xEB, 0xFF, 0x06, 0x00, 0x04, 0x00, 0xF7, 0xFF, 0xEA,
+	0xFF, 0x93, 0x00, 0x36, 0xFE, 0x7D, 0x04, 0x85, 0xF4, 0x1F, 0x40,
+	0x94, 0x1C, 0x47, 0xF4, 0x7E, 0x06, 0x5A, 0xFC, 0xDF, 0x01, 0x37,
+	0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x30, 0x00, 0x4A, 0xFF,
+	0xA9, 0x01, 0xE7, 0xFC, 0x33, 0x05, 0x60, 0xF7, 0xDA, 0x11, 0xB8,
+	0x45, 0x1C, 0xF9, 0xD8, 0x01, 0xCA, 0xFF, 0xAF, 0xFF, 0x5A, 0x00,
+	0xCC, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0x0F, 0x00, 0xA8, 0xFF, 0x17,
+	0x01, 0x57, 0xFD, 0xD6, 0x05, 0x90, 0xF2, 0xC8, 0x3A, 0x46, 0x24,
+	0xAA, 0xF2, 0x06, 0x07, 0x32, 0xFC, 0xE5, 0x01, 0x39, 0xFF, 0x36,
+	0x00, 0xFD, 0xFF, 0x00, 0x00, 0x28, 0x00, 0x62, 0xFF, 0x69, 0x01,
+	0x77, 0xFD, 0x04, 0x04, 0xE2, 0xF9, 0xCB, 0x0A, 0x0D, 0x48, 0x94,
+	0xFD, 0x92, 0xFF, 0x10, 0x01, 0xFE, 0xFE, 0xB1, 0x00, 0xAA, 0xFF,
+	0x15, 0x00, 0xFE, 0xFF, 0x22, 0x00, 0x74, 0xFF, 0x7C, 0x01, 0xB5,
+	0xFC, 0xB8, 0x06, 0x9C, 0xF1, 0x81, 0x34, 0xD1, 0x2B, 0xB3, 0xF1,
+	0x29, 0x07, 0x46, 0xFC, 0xCA, 0x01, 0x4A, 0xFF, 0x30, 0x00, 0xFD,
+	0xFF, 0x00, 0x00, 0x1F, 0x00, 0x81, 0xFF, 0x1B, 0x01, 0x20, 0xFE,
+	0xB6, 0x02, 0x7A, 0xFC, 0x5F, 0x04, 0xF4, 0x48, 0xFF, 0x02, 0x13,
+	0xFD, 0x67, 0x02, 0x49, 0xFE, 0x07, 0x01, 0x88, 0xFF, 0x1D, 0x00,
+	0xFD, 0xFF, 0x2E, 0x00, 0x50, 0xFF, 0xC0, 0x01, 0x53, 0xFC, 0x21,
+	0x07, 0x96, 0xF1, 0x82, 0x2D, 0xF2, 0x32, 0x86, 0xF1, 0xDB, 0x06,
+	0x99, 0xFC, 0x8E, 0x01, 0x6A, 0xFF, 0x25, 0x00, 0xFD, 0xFF, 0x16,
+	0x00, 0xA2, 0xFF, 0xC5, 0x00, 0xD4, 0xFE, 0x5F, 0x01, 0x03, 0xFF,
+	0xBF, 0xFE, 0x63, 0x48, 0x40, 0x09, 0x7B, 0xFA, 0xB9, 0x03, 0x9D,
+	0xFD, 0x58, 0x01, 0x69, 0xFF, 0x26, 0x00, 0x00, 0x00, 0xFD, 0xFF,
+	0x35, 0x00, 0x3B, 0xFF, 0xE2, 0x01, 0x31, 0xFC, 0x17, 0x07, 0x61,
+	0xF2, 0x0A, 0x26, 0x6A, 0x39, 0x41, 0xF2, 0x15, 0x06, 0x2C, 0xFD,
+	0x31, 0x01, 0x9B, 0xFF, 0x14, 0x00, 0xFF, 0xFF, 0x0E, 0x00, 0xC4,
+	0xFF, 0x6E, 0x00, 0x87, 0xFF, 0x13, 0x00, 0x58, 0x01, 0x0D, 0xFA,
+	0x61, 0x46, 0x2D, 0x10, 0xF0, 0xF7, 0xF1, 0x04, 0x05, 0xFD, 0x9C,
+	0x01, 0x4E, 0xFF, 0x2E, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00,
+	0x36, 0xFF, 0xE3, 0x01, 0x4C, 0xFC, 0xA6, 0x06, 0xDB, 0xF3, 0x5B,
+	0x1E, 0xFC, 0x3E, 0xFA, 0xF3, 0xD7, 0x04, 0xFD, 0xFD, 0xB4, 0x00,
+	0xD9, 0xFF, 0xFD, 0xFF, 0x03, 0x00, 0x08, 0x00, 0xE4, 0xFF, 0x1B,
+	0x00, 0x30, 0x00, 0xE4, 0xFE, 0x5F, 0x03, 0x5E, 0xF6, 0x02, 0x43,
+	0x96, 0x17, 0x9B, 0xF5, 0xF8, 0x05, 0x8F, 0xFC, 0xCC, 0x01, 0x3D,
+	0xFF, 0x34, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x33, 0x00, 0x3E, 0xFF,
+	0xC8, 0x01, 0x9B, 0xFC, 0xDD, 0x05, 0xDC, 0xF5, 0xB6, 0x16, 0x77,
+	0x43, 0xBD, 0xF6, 0x28, 0x03, 0x05, 0xFF, 0x1D, 0x00, 0x25, 0x00,
+	0xE1, 0xFF, 0x08, 0x00, 0x02, 0x00, 0x00, 0x00, 0xD2, 0xFF, 0xC4,
+	0x00, 0xE2, 0xFD, 0x01, 0x05, 0xBA, 0xF3, 0x64, 0x3E, 0x3F, 0x1F,
+	0xA8, 0xF3, 0xB8, 0x06, 0x46, 0xFC, 0xE5, 0x01, 0x36, 0xFF, 0x36,
+	0x00, 0xFD, 0xFF, 0xFF, 0xFF, 0x2D, 0x00, 0x51, 0xFF, 0x95, 0x01,
+	0x15, 0xFD, 0xCF, 0x04, 0x39, 0xF8, 0x59, 0x0F, 0xAF, 0x46, 0x8B,
+	0xFA, 0x17, 0x01, 0x38, 0x00, 0x73, 0xFF, 0x78, 0x00, 0xC0, 0xFF,
+	0x0F, 0x00, 0xFF, 0xFF, 0x16, 0x00, 0x94, 0xFF, 0x3D, 0x01, 0x18,
+	0xFD, 0x32, 0x06, 0x1F, 0xF2, 0xB5, 0x38, 0xEB, 0x26, 0x40, 0xF2,
+	0x1E, 0x07, 0x32, 0xFC, 0xDF, 0x01, 0x3D, 0xFF, 0x35, 0x00, 0xFD,
+	0xFF, 0x00, 0x00, 0x25, 0x00, 0x6C, 0xFF, 0x4F, 0x01, 0xB0, 0xFD,
+	0x93, 0x03, 0xC7, 0xFA, 0x7D, 0x08, 0x86, 0x48, 0x5A, 0xFF, 0xBA,
+	0xFE, 0x86, 0x01, 0xBF, 0xFE, 0xCF, 0x00, 0x9E, 0xFF, 0x17, 0x00,
+	0xFD, 0xFF, 0x27, 0x00, 0x66, 0xFF, 0x97, 0x01, 0x8C, 0xFC, 0xEA,
+	0x06, 0x80, 0xF1, 0x26, 0x32, 0x58, 0x2E, 0x8B, 0xF1, 0x1B, 0x07,
+	0x5B, 0xFC, 0xBA, 0x01, 0x53, 0xFF, 0x2D, 0x00, 0xFD, 0xFF, 0x1C,
+	0x00, 0x8C, 0xFF, 0xFE, 0x00, 0x5D, 0xFE, 0x3F, 0x02, 0x5E, 0xFD,
+	0x54, 0x02, 0xEC, 0x48, 0x13, 0x05, 0x2E, 0xFC, 0xDE, 0x02, 0x0C,
+	0xFE, 0x24, 0x01, 0x7D, 0xFF, 0x20, 0x00, 0x00, 0x00, 0xFD, 0xFF,
+	0x31, 0x00, 0x47, 0xFF, 0xCF, 0x01, 0x40, 0xFC, 0x2A, 0x07, 0xC6,
+	0xF1, 0xF7, 0x2A, 0x46, 0x35, 0xAB, 0xF1, 0xA4, 0x06, 0xC4, 0xFC,
+	0x72, 0x01, 0x79, 0xFF, 0x20, 0x00, 0xFE, 0xFF, 0x14, 0x00, 0xAE,
+	0xFF, 0xA7, 0x00, 0x12, 0xFF, 0xEA, 0x00, 0xD9, 0xFF, 0x03, 0xFD,
+	0xDC, 0x47, 0x95, 0x0B, 0x96, 0xF9, 0x29, 0x04, 0x65, 0xFD, 0x71,
+	0x01, 0x5F, 0xFF, 0x29, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00,
+	0x38, 0xFF, 0xE6, 0x01, 0x34, 0xFC, 0xFB, 0x06, 0xD2, 0xF2, 0x64,
+	0x23, 0x73, 0x3B, 0xBC, 0xF2, 0xB4, 0x05, 0x6E, 0xFD, 0x09, 0x01,
+	0xAF, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0x0C, 0x00, 0xD0, 0xFF, 0x51,
+	0x00, 0xC3, 0xFF, 0xA6, 0xFF, 0x16, 0x02, 0xA9, 0xF8, 0x5C, 0x45,
+	0xB2, 0x12, 0x19, 0xF7, 0x52, 0x05, 0xD8, 0xFC, 0xAF, 0x01, 0x47,
+	0xFF, 0x30, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x38, 0xFF,
+	0xDD, 0x01, 0x62, 0xFC, 0x69, 0x06, 0x7F, 0xF4, 0xB2, 0x1B, 0xAB,
+	0x40, 0xD0, 0xF4, 0x4E, 0x04, 0x53, 0xFE, 0x83, 0x00, 0xF2, 0xFF,
+	0xF4, 0xFF, 0x05, 0x00, 0x06, 0x00, 0xEE, 0xFF, 0x01, 0x00, 0x66,
+	0x00, 0x85, 0xFE, 0xFC, 0x03, 0x55, 0xF5, 0x8C, 0x41, 0x38, 0x1A,
+	0xE1, 0xF4, 0x43, 0x06, 0x70, 0xFC, 0xD8, 0x01, 0x39, 0xFF, 0x35,
+	0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x32, 0x00, 0x44, 0xFF, 0xB9, 0x01,
+	0xC1, 0xFC, 0x86, 0x05, 0xA5, 0xF6, 0x1E, 0x14, 0xBA, 0x44, 0xF0,
+	0xF7, 0x7B, 0x02, 0x6B, 0xFF, 0xE4, 0xFF, 0x41, 0x00, 0xD6, 0xFF,
+	0x0B, 0x00, 0x01, 0x00, 0x08, 0x00, 0xBB, 0xFF, 0xF1, 0x00, 0x96,
+	0xFD, 0x78, 0x05, 0x0E, 0xF3, 0x8A, 0x3C, 0xEA, 0x21, 0x19, 0xF3,
+	0xE6, 0x06, 0x38, 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD,
+	0xFF, 0x00, 0x00, 0x2B, 0x00, 0x5A, 0xFF, 0x7E, 0x01, 0x48, 0xFD,
+	0x66, 0x04, 0x18, 0xF9, 0xE8, 0x0C, 0x7C, 0x47, 0x19, 0xFC, 0x4D,
+	0x00, 0xA9, 0x00, 0x35, 0xFF, 0x96, 0x00, 0xB5, 0xFF, 0x12, 0x00,
+	0xFE, 0xFF, 0x1D, 0x00, 0x82, 0xFF, 0x60, 0x01, 0xE0, 0xFC, 0x7F,
+	0x06, 0xCC, 0xF1, 0x85, 0x36, 0x87, 0x29, 0xEB, 0xF1, 0x2A, 0x07,
+	0x39, 0xFC, 0xD6, 0x01, 0x43, 0xFF, 0x33, 0x00, 0xFD, 0xFF, 0x00,
+	0x00, 0x22, 0x00, 0x77, 0xFF, 0x34, 0x01, 0xEA, 0xFD, 0x1F, 0x03,
+	0xAE, 0xFB, 0x45, 0x06, 0xD5, 0x48, 0x3C, 0x01, 0xDC, 0xFD, 0xFD,
+	0x01, 0x80, 0xFE, 0xED, 0x00, 0x93, 0xFF, 0x1B, 0x00, 0xFD, 0xFF,
+	0x2B, 0x00, 0x59, 0xFF, 0xAE, 0x01, 0x6A, 0xFC, 0x0D, 0x07, 0x80,
+	0xF1, 0xB8, 0x2F, 0xCF, 0x30, 0x7D, 0xF1, 0xFF, 0x06, 0x78, 0xFC,
+	0xA5, 0x01, 0x5F, 0xFF, 0x29, 0x00, 0xFD, 0xFF, 0x19, 0x00, 0x98,
+	0xFF, 0xE0, 0x00, 0x9C, 0xFE, 0xC8, 0x01, 0x3F, 0xFE, 0x62, 0x00,
+	0xB8, 0x48, 0x3F, 0x07, 0x47, 0xFB, 0x53, 0x03, 0xD0, 0xFD, 0x40,
+	0x01, 0x72, 0xFF, 0x23, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x34, 0x00,
+	0x40, 0xFF, 0xDB, 0x01, 0x35, 0xFC, 0x26, 0x07, 0x0E, 0xF2, 0x60,
+	0x28, 0x82, 0x37, 0xED, 0xF1, 0x5E, 0x06, 0xF8, 0xFC, 0x51, 0x01,
+	0x8A, 0xFF, 0x1A, 0x00, 0xFF, 0xFF, 0x11, 0x00, 0xBA, 0xFF, 0x89,
+	0x00, 0x51, 0xFF, 0x77, 0x00, 0xA7, 0x00, 0x64, 0xFB, 0x26, 0x47,
+	0xFC, 0x0D, 0xB4, 0xF8, 0x95, 0x04, 0x31, 0xFD, 0x88, 0x01, 0x56,
+	0xFF, 0x2C, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF,
+	0xE6, 0x01, 0x3E, 0xFC, 0xD3, 0x06, 0x56, 0xF3, 0xBA, 0x20, 0x61,
+	0x3D, 0x56, 0xF3, 0x45, 0x05, 0xB7, 0xFD, 0xDE, 0x00, 0xC5, 0xFF,
+	0x05, 0x00, 0x02, 0x00, 0x09, 0x00, 0xDB, 0xFF, 0x34, 0x00, 0xFE,
+	0xFF, 0x3D, 0xFF, 0xC9, 0x02, 0x64, 0xF7, 0x2F, 0x44, 0x44, 0x15,
+	0x4A, 0xF6, 0xAD, 0x05, 0xAF, 0xFC, 0xC0, 0x01, 0x41, 0xFF, 0x32,
+	0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xD3, 0x01,
+	0x7D, 0xFC, 0x23, 0x06, 0x32, 0xF5, 0x0C, 0x19, 0x38, 0x42, 0xC7,
+	0xF5, 0xB8, 0x03, 0xAF, 0xFE, 0x4E, 0x00, 0x0C, 0x00, 0xEA, 0xFF,
+	0x06, 0x00, 0x04, 0x00, 0xF8, 0xFF, 0xE7, 0xFF, 0x99, 0x00, 0x2C,
+	0xFE, 0x8C, 0x04, 0x6D, 0xF4, 0xF0, 0x3F, 0xE0, 0x1C, 0x34, 0xF4,
+	0x85, 0x06, 0x57, 0xFC, 0xE0, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE,
+	0xFF, 0xFF, 0xFF, 0x2F, 0x00, 0x4A, 0xFF, 0xA7, 0x01, 0xEC, 0xFC,
+	0x28, 0x05, 0x77, 0xF7, 0x92, 0x11, 0xD7, 0x45, 0x43, 0xF9, 0xC3,
+	0x01, 0xD6, 0xFF, 0xA9, 0xFF, 0x5E, 0x00, 0xCB, 0xFF, 0x0D, 0x00,
+	0x00, 0x00, 0x10, 0x00, 0xA6, 0xFF, 0x1B, 0x01, 0x50, 0xFD, 0xE1,
+	0x05, 0x82, 0xF2, 0x8F, 0x3A, 0x92, 0x24, 0x9D, 0xF2, 0x09, 0x07,
+	0x32, 0xFC, 0xE4, 0x01, 0x39, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x00,
+	0x00, 0x28, 0x00, 0x63, 0xFF, 0x66, 0x01, 0x7D, 0xFD, 0xF8, 0x03,
+	0xFB, 0xF9, 0x89, 0x0A, 0x1D, 0x48, 0xC5, 0xFD, 0x7A, 0xFF, 0x1D,
+	0x01, 0xF7, 0xFE, 0xB4, 0x00, 0xA9, 0xFF, 0x15, 0x00, 0xFE, 0xFF,
+	0x23, 0x00, 0x72, 0xFF, 0x7F, 0x01, 0xB0, 0xFC, 0xBE, 0x06, 0x97,
+	0xF1, 0x3F, 0x34, 0x19, 0x2C, 0xAD, 0xF1, 0x28, 0x07, 0x48, 0xFC,
+	0xC9, 0x01, 0x4B, 0xFF, 0x30, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x1F,
+	0x00, 0x82, 0xFF, 0x18, 0x01, 0x27, 0xFE, 0xA9, 0x02, 0x94, 0xFC,
+	0x24, 0x04, 0xF5, 0x48, 0x39, 0x03, 0xF9, 0xFC, 0x74, 0x02, 0x42,
+	0xFE, 0x0B, 0x01, 0x87, 0xFF, 0x1E, 0x00, 0xFD, 0xFF, 0x2F, 0x00,
+	0x4F, 0xFF, 0xC2, 0x01, 0x51, 0xFC, 0x23, 0x07, 0x9A, 0xF1, 0x3A,
+	0x2D, 0x35, 0x33, 0x89, 0xF1, 0xD5, 0x06, 0x9D, 0xFC, 0x8B, 0x01,
+	0x6C, 0xFF, 0x25, 0x00, 0xFD, 0xFF, 0x16, 0x00, 0xA4, 0xFF, 0xC2,
+	0x00, 0xDB, 0xFE, 0x52, 0x01, 0x1B, 0xFF, 0x8D, 0xFE, 0x57, 0x48,
+	0x81, 0x09, 0x61, 0xFA, 0xC6, 0x03, 0x96, 0xFD, 0x5B, 0x01, 0x68,
+	0xFF, 0x26, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3B, 0xFF,
+	0xE2, 0x01, 0x31, 0xFC, 0x15, 0x07, 0x6D, 0xF2, 0xBF, 0x25, 0xA5,
+	0x39, 0x4D, 0xF2, 0x0B, 0x06, 0x33, 0xFD, 0x2D, 0x01, 0x9D, 0xFF,
+	0x13, 0x00, 0xFF, 0xFF, 0x0E, 0x00, 0xC6, 0xFF, 0x6B, 0x00, 0x8E,
+	0xFF, 0x06, 0x00, 0x6E, 0x01, 0xE4, 0xF9, 0x48, 0x46, 0x75, 0x10,
+	0xD7, 0xF7, 0xFC, 0x04, 0x00, 0xFD, 0x9E, 0x01, 0x4E, 0xFF, 0x2E,
+	0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE3, 0x01,
+	0x4E, 0xFC, 0xA0, 0x06, 0xED, 0xF3, 0x0F, 0x1E, 0x2D, 0x3F, 0x10,
+	0xF4, 0xC8, 0x04, 0x07, 0xFE, 0xAF, 0x00, 0xDC, 0xFF, 0xFC, 0xFF,
+	0x03, 0x00, 0x07, 0x00, 0xE5, 0xFF, 0x18, 0x00, 0x36, 0x00, 0xD9,
+	0xFE, 0x71, 0x03, 0x3F, 0xF6, 0xDB, 0x42, 0xE0, 0x17, 0x86, 0xF5,
+	0x00, 0x06, 0x8C, 0xFC, 0xCE, 0x01, 0x3C, 0xFF, 0x34, 0x00, 0xFE,
+	0xFF, 0xFF, 0xFF, 0x33, 0x00, 0x3F, 0xFF, 0xC6, 0x01, 0x9F, 0xFC,
+	0xD3, 0x05, 0xF1, 0xF5, 0x6C, 0x16, 0x9E, 0x43, 0xDD, 0xF6, 0x15,
+	0x03, 0x10, 0xFF, 0x17, 0x00, 0x28, 0x00, 0xDF, 0xFF, 0x09, 0x00,
+	0x02, 0x00, 0x01, 0x00, 0xCF, 0xFF, 0xC9, 0x00, 0xDA, 0xFD, 0x0F,
+	0x05, 0xA5, 0xF3, 0x31, 0x3E, 0x8A, 0x1F, 0x97, 0xF3, 0xBD, 0x06,
+	0x44, 0xFC, 0xE5, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFF,
+	0xFF, 0x2D, 0x00, 0x52, 0xFF, 0x92, 0x01, 0x1B, 0xFD, 0xC4, 0x04,
+	0x51, 0xF8, 0x13, 0x0F, 0xC8, 0x46, 0xB6, 0xFA, 0x01, 0x01, 0x44,
+	0x00, 0x6C, 0xFF, 0x7B, 0x00, 0xBF, 0xFF, 0x10, 0x00, 0xFF, 0xFF,
+	0x17, 0x00, 0x92, 0xFF, 0x41, 0x01, 0x11, 0xFD, 0x3B, 0x06, 0x14,
+	0xF2, 0x78, 0x38, 0x36, 0x27, 0x35, 0xF2, 0x20, 0x07, 0x33, 0xFC,
+	0xDF, 0x01, 0x3E, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x25,
+	0x00, 0x6D, 0xFF, 0x4C, 0x01, 0xB6, 0xFD, 0x86, 0x03, 0xE1, 0xFA,
+	0x3D, 0x08, 0x92, 0x48, 0x8E, 0xFF, 0xA1, 0xFE, 0x93, 0x01, 0xB8,
+	0xFE, 0xD3, 0x00, 0x9D, 0xFF, 0x18, 0x00, 0xFD, 0xFF, 0x28, 0x00,
+	0x64, 0xFF, 0x9A, 0x01, 0x88, 0xFC, 0xEE, 0x06, 0x7E, 0xF1, 0xE3,
+	0x31, 0x9F, 0x2E, 0x88, 0xF1, 0x19, 0x07, 0x5E, 0xFC, 0xB7, 0x01,
+	0x54, 0xFF, 0x2D, 0x00, 0xFD, 0xFF, 0x1C, 0x00, 0x8D, 0xFF, 0xFA,
+	0x00, 0x64, 0xFE, 0x32, 0x02, 0x78, 0xFD, 0x1B, 0x02, 0xEA, 0x48,
+	0x50, 0x05, 0x14, 0xFC, 0xEB, 0x02, 0x05, 0xFE, 0x27, 0x01, 0x7C,
+	0xFF, 0x21, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x32, 0x00, 0x46, 0xFF,
+	0xD1, 0x01, 0x3F, 0xFC, 0x2B, 0x07, 0xCD, 0xF1, 0xAE, 0x2A, 0x86,
+	0x35, 0xB1, 0xF1, 0x9D, 0x06, 0xCA, 0xFC, 0x6E, 0x01, 0x7B, 0xFF,
+	0x20, 0x00, 0xFE, 0xFF, 0x13, 0x00, 0xAF, 0xFF, 0xA4, 0x00, 0x19,
+	0xFF, 0xDD, 0x00, 0xF0, 0xFF, 0xD4, 0xFC, 0xC9, 0x47, 0xD8, 0x0B,
+	0x7C, 0xF9, 0x35, 0x04, 0x5F, 0xFD, 0x74, 0x01, 0x5E, 0xFF, 0x29,
+	0x00, 0x00, 0x00, 0xFD, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xE6, 0x01,
+	0x35, 0xFC, 0xF7, 0x06, 0xE0, 0xF2, 0x18, 0x23, 0xAB, 0x3B, 0xCC,
+	0xF2, 0xA8, 0x05, 0x76, 0xFD, 0x04, 0x01, 0xB1, 0xFF, 0x0C, 0x00,
+	0x00, 0x00, 0x0C, 0x00, 0xD1, 0xFF, 0x4E, 0x00, 0xCA, 0xFF, 0x9A,
+	0xFF, 0x2A, 0x02, 0x83, 0xF8, 0x3F, 0x45, 0xFB, 0x12, 0x01, 0xF7,
+	0x5D, 0x05, 0xD3, 0xFC, 0xB1, 0x01, 0x46, 0xFF, 0x31, 0x00, 0xFF,
+	0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xDC, 0x01, 0x64, 0xFC,
+	0x62, 0x06, 0x93, 0xF4, 0x66, 0x1B, 0xD9, 0x40, 0xEA, 0xF4, 0x3E,
+	0x04, 0x5D, 0xFE, 0x7D, 0x00, 0xF5, 0xFF, 0xF3, 0xFF, 0x05, 0x00,
+	0x05, 0x00, 0xEF, 0xFF, 0xFE, 0xFF, 0x6C, 0x00, 0x7B, 0xFE, 0x0C,
+	0x04, 0x3A, 0xF5, 0x5F, 0x41, 0x83, 0x1A, 0xCD, 0xF4, 0x4B, 0x06,
+	0x6D, 0xFC, 0xD9, 0x01, 0x39, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0xFF,
+	0xFF, 0x31, 0x00, 0x44, 0xFF, 0xB7, 0x01, 0xC5, 0xFC, 0x7C, 0x05,
+	0xBC, 0xF6, 0xD5, 0x13, 0xDC, 0x44, 0x14, 0xF8, 0x67, 0x02, 0x77,
+	0xFF, 0xDD, 0xFF, 0x44, 0x00, 0xD5, 0xFF, 0x0B, 0x00, 0x01, 0x00,
+	0x09, 0x00, 0xB8, 0xFF, 0xF6, 0x00, 0x8D, 0xFD, 0x84, 0x05, 0xFD,
+	0xF2, 0x52, 0x3C, 0x35, 0x22, 0x0B, 0xF3, 0xEB, 0x06, 0x37, 0xFC,
+	0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x2A,
+	0x00, 0x5B, 0xFF, 0x7C, 0x01, 0x4E, 0xFD, 0x5A, 0x04, 0x31, 0xF9,
+	0xA4, 0x0C, 0x90, 0x47, 0x47, 0xFC, 0x36, 0x00, 0xB6, 0x00, 0x2E,
+	0xFF, 0x99, 0x00, 0xB3, 0xFF, 0x12, 0x00, 0xFE, 0xFF, 0x1E, 0x00,
+	0x80, 0xFF, 0x64, 0x01, 0xDA, 0xFC, 0x87, 0x06, 0xC5, 0xF1, 0x46,
+	0x36, 0xD1, 0x29, 0xE3, 0xF1, 0x2A, 0x07, 0x3A, 0xFC, 0xD5, 0x01,
+	0x44, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x22, 0x00, 0x78,
+	0xFF, 0x31, 0x01, 0xF1, 0xFD, 0x12, 0x03, 0xC7, 0xFB, 0x07, 0x06,
+	0xDB, 0x48, 0x73, 0x01, 0xC3, 0xFD, 0x0A, 0x02, 0x79, 0xFE, 0xF1,
+	0x00, 0x91, 0xFF, 0x1B, 0x00, 0xFD, 0xFF, 0x2C, 0x00, 0x58, 0xFF,
+	0xB1, 0x01, 0x67, 0xFC, 0x10, 0x07, 0x81, 0xF1, 0x73, 0x2F, 0x15,
+	0x31, 0x7C, 0xF1, 0xFB, 0x06, 0x7C, 0xFC, 0xA2, 0x01, 0x60, 0xFF,
+	0x29, 0x00, 0xFD, 0xFF, 0x19, 0x00, 0x99, 0xFF, 0xDD, 0x00, 0xA3,
+	0xFE, 0xBB, 0x01, 0x58, 0xFE, 0x2D, 0x00, 0xAF, 0x48, 0x7E, 0x07,
+	0x2E, 0xFB, 0x60, 0x03, 0xC9, 0xFD, 0x43, 0x01, 0x71, 0xFF, 0x24,
+	0x00, 0x00, 0x00, 0xFD, 0xFF, 0x34, 0x00, 0x3F, 0xFF, 0xDC, 0x01,
+	0x34, 0xFC, 0x25, 0x07, 0x18, 0xF2, 0x15, 0x28, 0xBF, 0x37, 0xF7,
+	0xF1, 0x56, 0x06, 0xFE, 0xFC, 0x4D, 0x01, 0x8C, 0xFF, 0x19, 0x00,
+	0xFF, 0xFF, 0x10, 0x00, 0xBB, 0xFF, 0x85, 0x00, 0x58, 0xFF, 0x6A,
+	0x00, 0xBE, 0x00, 0x38, 0xFB, 0x0F, 0x47, 0x42, 0x0E, 0x9B, 0xF8,
+	0xA1, 0x04, 0x2B, 0xFD, 0x8B, 0x01, 0x55, 0xFF, 0x2C, 0x00, 0xFF,
+	0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3F, 0xFC,
+	0xCE, 0x06, 0x66, 0xF3, 0x6F, 0x20, 0x96, 0x3D, 0x69, 0xF3, 0x38,
+	0x05, 0xBF, 0xFD, 0xD9, 0x00, 0xC7, 0xFF, 0x04, 0x00, 0x02, 0x00,
+	0x09, 0x00, 0xDC, 0xFF, 0x31, 0x00, 0x04, 0x00, 0x32, 0xFF, 0xDC,
+	0x02, 0x42, 0xF7, 0x0B, 0x44, 0x8E, 0x15, 0x34, 0xF6, 0xB7, 0x05,
+	0xAB, 0xFC, 0xC1, 0x01, 0x40, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0xFE,
+	0xFF, 0x35, 0x00, 0x3B, 0xFF, 0xD2, 0x01, 0x81, 0xFC, 0x1A, 0x06,
+	0x47, 0xF5, 0xC1, 0x18, 0x60, 0x42, 0xE4, 0xF5, 0xA6, 0x03, 0xB9,
+	0xFE, 0x48, 0x00, 0x0F, 0x00, 0xE9, 0xFF, 0x07, 0x00, 0x04, 0x00,
+	0xF9, 0xFF, 0xE4, 0xFF, 0x9F, 0x00, 0x23, 0xFE, 0x9B, 0x04, 0x55,
+	0xF4, 0xC0, 0x3F, 0x2C, 0x1D, 0x22, 0xF4, 0x8C, 0x06, 0x55, 0xFC,
+	0xE1, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x2F,
+	0x00, 0x4B, 0xFF, 0xA4, 0x01, 0xF1, 0xFC, 0x1D, 0x05, 0x8F, 0xF7,
+	0x4A, 0x11, 0xF2, 0x45, 0x6B, 0xF9, 0xAE, 0x01, 0xE2, 0xFF, 0xA2,
+	0xFF, 0x61, 0x00, 0xC9, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0x11, 0x00,
+	0xA3, 0xFF, 0x20, 0x01, 0x49, 0xFD, 0xEB, 0x05, 0x74, 0xF2, 0x54,
+	0x3A, 0xDD, 0x24, 0x91, 0xF2, 0x0C, 0x07, 0x32, 0xFC, 0xE4, 0x01,
+	0x3A, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x27, 0x00, 0x64,
+	0xFF, 0x63, 0x01, 0x84, 0xFD, 0xEB, 0x03, 0x14, 0xFA, 0x47, 0x0A,
+	0x2C, 0x48, 0xF6, 0xFD, 0x63, 0xFF, 0x2B, 0x01, 0xF0, 0xFE, 0xB8,
+	0x00, 0xA8, 0xFF, 0x15, 0x00, 0xFE, 0xFF, 0x23, 0x00, 0x71, 0xFF,
+	0x82, 0x01, 0xAB, 0xFC, 0xC4, 0x06, 0x93, 0xF1, 0xFD, 0x33, 0x62,
+	0x2C, 0xA8, 0xF1, 0x27, 0x07, 0x4A, 0xFC, 0xC7, 0x01, 0x4C, 0xFF,
+	0x30, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x1F, 0x00, 0x83, 0xFF, 0x14,
+	0x01, 0x2D, 0xFE, 0x9C, 0x02, 0xAD, 0xFC, 0xE9, 0x03, 0xF6, 0x48,
+	0x73, 0x03, 0xE0, 0xFC, 0x82, 0x02, 0x3B, 0xFE, 0x0E, 0x01, 0x86,
+	0xFF, 0x1E, 0x00, 0xFD, 0xFF, 0x2F, 0x00, 0x4E, 0xFF, 0xC3, 0x01,
+	0x4E, 0xFC, 0x24, 0x07, 0x9E, 0xF1, 0xF2, 0x2C, 0x78, 0x33, 0x8C,
+	0xF1, 0xD0, 0x06, 0xA2, 0xFC, 0x88, 0x01, 0x6D, 0xFF, 0x24, 0x00,
+	0xFD, 0xFF, 0x16, 0x00, 0xA5, 0xFF, 0xBE, 0x00, 0xE2, 0xFE, 0x45,
+	0x01, 0x33, 0xFF, 0x5A, 0xFE, 0x48, 0x48, 0xC3, 0x09, 0x47, 0xFA,
+	0xD2, 0x03, 0x90, 0xFD, 0x5E, 0x01, 0x66, 0xFF, 0x27, 0x00, 0x00,
+	0x00, 0xFD, 0xFF, 0x35, 0x00, 0x3B, 0xFF, 0xE3, 0x01, 0x31, 0xFC,
+	0x12, 0x07, 0x79, 0xF2, 0x73, 0x25, 0xDF, 0x39, 0x5A, 0xF2, 0x00,
+	0x06, 0x3A, 0xFD, 0x28, 0x01, 0x9F, 0xFF, 0x13, 0x00, 0x00, 0x00,
+	0x0E, 0x00, 0xC7, 0xFF, 0x68, 0x00, 0x95, 0xFF, 0xFA, 0xFF, 0x83,
+	0x01, 0xBB, 0xF9, 0x2B, 0x46, 0xBB, 0x10, 0xBF, 0xF7, 0x07, 0x05,
+	0xFB, 0xFC, 0xA0, 0x01, 0x4D, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, 0xFE,
+	0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE2, 0x01, 0x50, 0xFC, 0x99, 0x06,
+	0xFE, 0xF3, 0xC3, 0x1D, 0x5E, 0x3F, 0x27, 0xF4, 0xB9, 0x04, 0x10,
+	0xFE, 0xA9, 0x00, 0xDF, 0xFF, 0xFB, 0xFF, 0x03, 0x00, 0x07, 0x00,
+	0xE6, 0xFF, 0x15, 0x00, 0x3C, 0x00, 0xCF, 0xFE, 0x83, 0x03, 0x20,
+	0xF6, 0xB2, 0x42, 0x2B, 0x18, 0x71, 0xF5, 0x09, 0x06, 0x88, 0xFC,
+	0xCF, 0x01, 0x3C, 0xFF, 0x34, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x33,
+	0x00, 0x3F, 0xFF, 0xC5, 0x01, 0xA3, 0xFC, 0xCA, 0x05, 0x07, 0xF6,
+	0x22, 0x16, 0xC3, 0x43, 0xFE, 0xF6, 0x02, 0x03, 0x1B, 0xFF, 0x11,
+	0x00, 0x2B, 0x00, 0xDE, 0xFF, 0x09, 0x00, 0x02, 0x00, 0x02, 0x00,
+	0xCC, 0xFF, 0xCE, 0x00, 0xD1, 0xFD, 0x1D, 0x05, 0x91, 0xF3, 0xFE,
+	0x3D, 0xD7, 0x1F, 0x87, 0xF3, 0xC3, 0x06, 0x42, 0xFC, 0xE5, 0x01,
+	0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFF, 0xFF, 0x2D, 0x00, 0x53,
+	0xFF, 0x90, 0x01, 0x20, 0xFD, 0xB8, 0x04, 0x6A, 0xF8, 0xCD, 0x0E,
+	0xE1, 0x46, 0xE1, 0xFA, 0xEB, 0x00, 0x51, 0x00, 0x65, 0xFF, 0x7F,
+	0x00, 0xBE, 0xFF, 0x10, 0x00, 0xFF, 0xFF, 0x18, 0x00, 0x90, 0xFF,
+	0x45, 0x01, 0x0B, 0xFD, 0x44, 0x06, 0x0A, 0xF2, 0x3B, 0x38, 0x80,
+	0x27, 0x2B, 0xF2, 0x22, 0x07, 0x33, 0xFC, 0xDE, 0x01, 0x3E, 0xFF,
+	0x34, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x24, 0x00, 0x6E, 0xFF, 0x49,
+	0x01, 0xBC, 0xFD, 0x7A, 0x03, 0xFA, 0xFA, 0xFD, 0x07, 0x9C, 0x48,
+	0xC3, 0xFF, 0x89, 0xFE, 0xA1, 0x01, 0xB1, 0xFE, 0xD6, 0x00, 0x9C,
+	0xFF, 0x18, 0x00, 0xFD, 0xFF, 0x28, 0x00, 0x63, 0xFF, 0x9D, 0x01,
+	0x84, 0xFC, 0xF3, 0x06, 0x7D, 0xF1, 0x9E, 0x31, 0xE6, 0x2E, 0x85,
+	0xF1, 0x16, 0x07, 0x61, 0xFC, 0xB5, 0x01, 0x55, 0xFF, 0x2D, 0x00,
+	0xFD, 0xFF, 0x1C, 0x00, 0x8F, 0xFF, 0xF7, 0x00, 0x6B, 0xFE, 0x25,
+	0x02, 0x91, 0xFD, 0xE3, 0x01, 0xE5, 0x48, 0x8D, 0x05, 0xFB, 0xFB,
+	0xF8, 0x02, 0xFE, 0xFD, 0x2B, 0x01, 0x7A, 0xFF, 0x21, 0x00, 0x00,
+	0x00, 0xFD, 0xFF, 0x32, 0x00, 0x45, 0xFF, 0xD2, 0x01, 0x3D, 0xFC,
+	0x2B, 0x07, 0xD4, 0xF1, 0x64, 0x2A, 0xC6, 0x35, 0xB7, 0xF1, 0x96,
+	0x06, 0xCF, 0xFC, 0x6B, 0x01, 0x7D, 0xFF, 0x1F, 0x00, 0xFE, 0xFF,
+	0x13, 0x00, 0xB1, 0xFF, 0xA0, 0x00, 0x20, 0xFF, 0xD0, 0x00, 0x07,
+	0x00, 0xA4, 0xFC, 0xB6, 0x47, 0x1C, 0x0C, 0x63, 0xF9, 0x42, 0x04,
+	0x59, 0xFD, 0x76, 0x01, 0x5D, 0xFF, 0x2A, 0x00, 0x00, 0x00, 0xFD,
+	0xFF, 0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x35, 0xFC, 0xF3, 0x06,
+	0xEE, 0xF2, 0xCD, 0x22, 0xE4, 0x3B, 0xDC, 0xF2, 0x9C, 0x05, 0x7E,
+	0xFD, 0x00, 0x01, 0xB4, 0xFF, 0x0B, 0x00, 0x01, 0x00, 0x0B, 0x00,
+	0xD2, 0xFF, 0x4A, 0x00, 0xD0, 0xFF, 0x8E, 0xFF, 0x3F, 0x02, 0x5E,
+	0xF8, 0x1E, 0x45, 0x44, 0x13, 0xEA, 0xF6, 0x67, 0x05, 0xCF, 0xFC,
+	0xB3, 0x01, 0x46, 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x36,
+	0x00, 0x38, 0xFF, 0xDB, 0x01, 0x67, 0xFC, 0x5A, 0x06, 0xA6, 0xF4,
+	0x1B, 0x1B, 0x07, 0x41, 0x04, 0xF5, 0x2D, 0x04, 0x67, 0xFE, 0x77,
+	0x00, 0xF8, 0xFF, 0xF2, 0xFF, 0x05, 0x00, 0x05, 0x00, 0xF0, 0xFF,
+	0xFB, 0xFF, 0x71, 0x00, 0x71, 0xFE, 0x1D, 0x04, 0x1F, 0xF5, 0x32,
+	0x41, 0xCE, 0x1A, 0xBA, 0xF4, 0x53, 0x06, 0x6A, 0xFC, 0xDA, 0x01,
+	0x38, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x31, 0x00, 0x45,
+	0xFF, 0xB5, 0x01, 0xCA, 0xFC, 0x72, 0x05, 0xD3, 0xF6, 0x8D, 0x13,
+	0xFD, 0x44, 0x39, 0xF8, 0x53, 0x02, 0x82, 0xFF, 0xD7, 0xFF, 0x47,
+	0x00, 0xD3, 0xFF, 0x0B, 0x00, 0x01, 0x00, 0x0A, 0x00, 0xB6, 0xFF,
+	0xFB, 0x00, 0x85, 0xFD, 0x90, 0x05, 0xEC, 0xF2, 0x1C, 0x3C, 0x81,
+	0x22, 0xFC, 0xF2, 0xEF, 0x06, 0x36, 0xFC, 0xE6, 0x01, 0x37, 0xFF,
+	0x36, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x2A, 0x00, 0x5C, 0xFF, 0x79,
+	0x01, 0x53, 0xFD, 0x4E, 0x04, 0x4A, 0xF9, 0x60, 0x0C, 0xA3, 0x47,
+	0x76, 0xFC, 0x1F, 0x00, 0xC3, 0x00, 0x27, 0xFF, 0x9D, 0x00, 0xB2,
+	0xFF, 0x13, 0x00, 0xFE, 0xFF, 0x1E, 0x00, 0x7F, 0xFF, 0x67, 0x01,
+	0xD5, 0xFC, 0x8E, 0x06, 0xBE, 0xF1, 0x06, 0x36, 0x1A, 0x2A, 0xDC,
+	0xF1, 0x2A, 0x07, 0x3C, 0xFC, 0xD3, 0x01, 0x44, 0xFF, 0x32, 0x00,
+	0xFD, 0xFF, 0x00, 0x00, 0x21, 0x00, 0x79, 0xFF, 0x2E, 0x01, 0xF7,
+	0xFD, 0x05, 0x03, 0xE1, 0xFB, 0xCA, 0x05, 0xDF, 0x48, 0xAB, 0x01,
+	0xAA, 0xFD, 0x18, 0x02, 0x72, 0xFE, 0xF4, 0x00, 0x90, 0xFF, 0x1B,
+	0x00, 0xFD, 0xFF, 0x2C, 0x00, 0x57, 0xFF, 0xB3, 0x01, 0x64, 0xFC,
+	0x13, 0x07, 0x83, 0xF1, 0x2C, 0x2F, 0x5A, 0x31, 0x7D, 0xF1, 0xF7,
+	0x06, 0x80, 0xFC, 0x9F, 0x01, 0x61, 0xFF, 0x29, 0x00, 0xFD, 0xFF,
+	0x19, 0x00, 0x9A, 0xFF, 0xD9, 0x00, 0xAA, 0xFE, 0xAE, 0x01, 0x70,
+	0xFE, 0xF8, 0xFF, 0xA6, 0x48, 0xBE, 0x07, 0x14, 0xFB, 0x6D, 0x03,
+	0xC3, 0xFD, 0x46, 0x01, 0x70, 0xFF, 0x24, 0x00, 0x00, 0x00, 0xFD,
+	0xFF, 0x34, 0x00, 0x3F, 0xFF, 0xDD, 0x01, 0x34, 0xFC, 0x23, 0x07,
+	0x21, 0xF2, 0xCB, 0x27, 0xFE, 0x37, 0x00, 0xF2, 0x4D, 0x06, 0x04,
+	0xFD, 0x49, 0x01, 0x8E, 0xFF, 0x19, 0x00, 0xFF, 0xFF, 0x10, 0x00,
+	0xBD, 0xFF, 0x82, 0x00, 0x5E, 0xFF, 0x5D, 0x00, 0xD4, 0x00, 0x0C,
+	0xFB, 0xF9, 0x46, 0x87, 0x0E, 0x82, 0xF8, 0xAD, 0x04, 0x26, 0xFD,
+	0x8D, 0x01, 0x54, 0xFF, 0x2C, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x36,
+	0x00, 0x36, 0xFF, 0xE6, 0x01, 0x41, 0xFC, 0xC8, 0x06, 0x76, 0xF3,
+	0x22, 0x20, 0xCA, 0x3D, 0x7D, 0xF3, 0x2A, 0x05, 0xC8, 0xFD, 0xD4,
+	0x00, 0xCA, 0xFF, 0x03, 0x00, 0x02, 0x00, 0x09, 0x00, 0xDD, 0xFF,
+	0x2E, 0x00, 0x0A, 0x00, 0x27, 0xFF, 0xEF, 0x02, 0x20, 0xF7, 0xE7,
+	0x43, 0xD8, 0x15, 0x1E, 0xF6, 0xC0, 0x05, 0xA7, 0xFC, 0xC3, 0x01,
+	0x40, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0xFE, 0xFF, 0x34, 0x00, 0x3B,
+	0xFF, 0xD1, 0x01, 0x84, 0xFC, 0x12, 0x06, 0x5C, 0xF5, 0x76, 0x18,
+	0x89, 0x42, 0x02, 0xF6, 0x94, 0x03, 0xC4, 0xFE, 0x42, 0x00, 0x12,
+	0x00, 0xE8, 0xFF, 0x07, 0x00, 0x03, 0x00, 0xFA, 0xFF, 0xE2, 0xFF,
+	0xA4, 0x00, 0x19, 0xFE, 0xAA, 0x04, 0x3E, 0xF4, 0x90, 0x3F, 0x78,
+	0x1D, 0x10, 0xF4, 0x93, 0x06, 0x52, 0xFC, 0xE1, 0x01, 0x36, 0xFF,
+	0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x2F, 0x00, 0x4C, 0xFF, 0xA2,
+	0x01, 0xF6, 0xFC, 0x12, 0x05, 0xA7, 0xF7, 0x03, 0x11, 0x10, 0x46,
+	0x93, 0xF9, 0x98, 0x01, 0xEE, 0xFF, 0x9B, 0xFF, 0x64, 0x00, 0xC8,
+	0xFF, 0x0E, 0x00, 0x00, 0x00, 0x12, 0x00, 0xA1, 0xFF, 0x24, 0x01,
+	0x41, 0xFD, 0xF6, 0x05, 0x67, 0xF2, 0x1A, 0x3A, 0x29, 0x25, 0x84,
+	0xF2, 0x0F, 0x07, 0x31, 0xFC, 0xE3, 0x01, 0x3A, 0xFF, 0x35, 0x00,
+	0xFD, 0xFF, 0x00, 0x00, 0x27, 0x00, 0x65, 0xFF, 0x60, 0x01, 0x8A,
+	0xFD, 0xDF, 0x03, 0x2E, 0xFA, 0x04, 0x0A, 0x3A, 0x48, 0x28, 0xFE,
+	0x4B, 0xFF, 0x38, 0x01, 0xE9, 0xFE, 0xBB, 0x00, 0xA6, 0xFF, 0x16,
+	0x00, 0xFD, 0xFF, 0x24, 0x00, 0x6F, 0xFF, 0x85, 0x01, 0xA6, 0xFC,
+	0xCA, 0x06, 0x8F, 0xF1, 0xBB, 0x33, 0xAB, 0x2C, 0xA3, 0xF1, 0x26,
+	0x07, 0x4C, 0xFC, 0xC5, 0x01, 0x4D, 0xFF, 0x30, 0x00, 0xFD, 0xFF,
+	0x00, 0x00, 0x1E, 0x00, 0x84, 0xFF, 0x11, 0x01, 0x34, 0xFE, 0x8F,
+	0x02, 0xC7, 0xFC, 0xAE, 0x03, 0xF7, 0x48, 0xAE, 0x03, 0xC7, 0xFC,
+	0x8F, 0x02, 0x34, 0xFE, 0x11, 0x01, 0x84, 0xFF, 0x1E, 0x00, 0xFD,
+	0xFF, 0x2A, 0x00, 0x5C, 0xFF, 0xAA, 0x01, 0x71, 0xFC, 0x07, 0x07,
+	0x7E, 0xF1, 0x44, 0x30, 0x44, 0x30, 0x7E, 0xF1, 0x07, 0x07, 0x71,
+	0xFC, 0xAA, 0x01, 0x5C, 0xFF, 0x2A, 0x00, 0xFD, 0xFF, 0x00, 0x00,
+	0x1E, 0x00, 0x84, 0xFF, 0x11, 0x01, 0x34, 0xFE, 0x8F, 0x02, 0xC7,
+	0xFC, 0xAE, 0x03, 0xF7, 0x48, 0xAE, 0x03, 0xC7, 0xFC, 0x8F, 0x02,
+	0x34, 0xFE, 0x11, 0x01, 0x84, 0xFF, 0x1E, 0x00, 0x02, 0x00, 0x05,
+	0x00, 0xC3, 0xFF, 0xE1, 0x00, 0xB1, 0xFD, 0x4E, 0x05, 0x4A, 0xF3,
+	0x3D, 0x3D, 0xED, 0x20, 0x4C, 0xF3, 0xD6, 0x06, 0x3D, 0xFC, 0xE6,
+	0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x36, 0x00,
+	0x36, 0xFF, 0xE6, 0x01, 0x3D, 0xFC, 0xD6, 0x06, 0x4C, 0xF3, 0xED,
+	0x20, 0x3D, 0x3D, 0x4A, 0xF3, 0x4E, 0x05, 0xB1, 0xFD, 0xE1, 0x00,
+	0xC3, 0xFF, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x84,
+	0xFF, 0x11, 0x01, 0x34, 0xFE, 0x8F, 0x02, 0xC7, 0xFC, 0xAE, 0x03,
+	0xF7, 0x48, 0xAE, 0x03, 0xC7, 0xFC, 0x8F, 0x02, 0x34, 0xFE, 0x11,
+	0x01, 0x84, 0xFF, 0x1E, 0x00, 0x16, 0x00, 0xA6, 0xFF, 0xBB, 0x00,
+	0xE9, 0xFE, 0x38, 0x01, 0x4B, 0xFF, 0x28, 0xFE, 0x3A, 0x48, 0x04,
+	0x0A, 0x2E, 0xFA, 0xDF, 0x03, 0x8A, 0xFD, 0x60, 0x01, 0x65, 0xFF,
+	0x27, 0x00, 0x00, 0x00, 0x0E, 0x00, 0xC8, 0xFF, 0x64, 0x00, 0x9B,
+	0xFF, 0xEE, 0xFF, 0x98, 0x01, 0x93, 0xF9, 0x10, 0x46, 0x03, 0x11,
+	0xA7, 0xF7, 0x12, 0x05, 0xF6, 0xFC, 0xA2, 0x01, 0x4C, 0xFF, 0x2F,
+	0x00, 0xFF, 0xFF, 0x07, 0x00, 0xE8, 0xFF, 0x12, 0x00, 0x42, 0x00,
+	0xC4, 0xFE, 0x94, 0x03, 0x02, 0xF6, 0x89, 0x42, 0x76, 0x18, 0x5C,
+	0xF5, 0x12, 0x06, 0x84, 0xFC, 0xD1, 0x01, 0x3B, 0xFF, 0x34, 0x00,
+	0xFE, 0xFF, 0x02, 0x00, 0x03, 0x00, 0xCA, 0xFF, 0xD4, 0x00, 0xC8,
+	0xFD, 0x2A, 0x05, 0x7D, 0xF3, 0xCA, 0x3D, 0x22, 0x20, 0x76, 0xF3,
+	0xC8, 0x06, 0x41, 0xFC, 0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD,
+	0xFF, 0xFF, 0xFF, 0x19, 0x00, 0x8E, 0xFF, 0x49, 0x01, 0x04, 0xFD,
+	0x4D, 0x06, 0x00, 0xF2, 0xFE, 0x37, 0xCB, 0x27, 0x21, 0xF2, 0x23,
+	0x07, 0x34, 0xFC, 0xDD, 0x01, 0x3F, 0xFF, 0x34, 0x00, 0xFD, 0xFF,
+	0xFD, 0xFF, 0x29, 0x00, 0x61, 0xFF, 0x9F, 0x01, 0x80, 0xFC, 0xF7,
+	0x06, 0x7D, 0xF1, 0x5A, 0x31, 0x2C, 0x2F, 0x83, 0xF1, 0x13, 0x07,
+	0x64, 0xFC, 0xB3, 0x01, 0x57, 0xFF, 0x2C, 0x00, 0xFD, 0xFF, 0xFD,
+	0xFF, 0x32, 0x00, 0x44, 0xFF, 0xD3, 0x01, 0x3C, 0xFC, 0x2A, 0x07,
+	0xDC, 0xF1, 0x1A, 0x2A, 0x06, 0x36, 0xBE, 0xF1, 0x8E, 0x06, 0xD5,
+	0xFC, 0x67, 0x01, 0x7F, 0xFF, 0x1E, 0x00, 0xFE, 0xFF, 0xFD, 0xFF,
+	0x36, 0x00, 0x37, 0xFF, 0xE6, 0x01, 0x36, 0xFC, 0xEF, 0x06, 0xFC,
+	0xF2, 0x81, 0x22, 0x1C, 0x3C, 0xEC, 0xF2, 0x90, 0x05, 0x85, 0xFD,
+	0xFB, 0x00, 0xB6, 0xFF, 0x0A, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x35,
+	0x00, 0x38, 0xFF, 0xDA, 0x01, 0x6A, 0xFC, 0x53, 0x06, 0xBA, 0xF4,
+	0xCE, 0x1A, 0x32, 0x41, 0x1F, 0xF5, 0x1D, 0x04, 0x71, 0xFE, 0x71,
+	0x00, 0xFB, 0xFF, 0xF0, 0xFF, 0x05, 0x00, 0xFF, 0xFF, 0x31, 0x00,
+	0x46, 0xFF, 0xB3, 0x01, 0xCF, 0xFC, 0x67, 0x05, 0xEA, 0xF6, 0x44,
+	0x13, 0x1E, 0x45, 0x5E, 0xF8, 0x3F, 0x02, 0x8E, 0xFF, 0xD0, 0xFF,
+	0x4A, 0x00, 0xD2, 0xFF, 0x0B, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x5D,
+	0xFF, 0x76, 0x01, 0x59, 0xFD, 0x42, 0x04, 0x63, 0xF9, 0x1C, 0x0C,
+	0xB6, 0x47, 0xA4, 0xFC, 0x07, 0x00, 0xD0, 0x00, 0x20, 0xFF, 0xA0,
+	0x00, 0xB1, 0xFF, 0x13, 0x00, 0x00, 0x00, 0x21, 0x00, 0x7A, 0xFF,
+	0x2B, 0x01, 0xFE, 0xFD, 0xF8, 0x02, 0xFB, 0xFB, 0x8D, 0x05, 0xE5,
+	0x48, 0xE3, 0x01, 0x91, 0xFD, 0x25, 0x02, 0x6B, 0xFE, 0xF7, 0x00,
+	0x8F, 0xFF, 0x1C, 0x00, 0x18, 0x00, 0x9C, 0xFF, 0xD6, 0x00, 0xB1,
+	0xFE, 0xA1, 0x01, 0x89, 0xFE, 0xC3, 0xFF, 0x9C, 0x48, 0xFD, 0x07,
+	0xFA, 0xFA, 0x7A, 0x03, 0xBC, 0xFD, 0x49, 0x01, 0x6E, 0xFF, 0x24,
+	0x00, 0x00, 0x00, 0x10, 0x00, 0xBE, 0xFF, 0x7F, 0x00, 0x65, 0xFF,
+	0x51, 0x00, 0xEB, 0x00, 0xE1, 0xFA, 0xE1, 0x46, 0xCD, 0x0E, 0x6A,
+	0xF8, 0xB8, 0x04, 0x20, 0xFD, 0x90, 0x01, 0x53, 0xFF, 0x2D, 0x00,
+	0xFF, 0xFF, 0x09, 0x00, 0xDE, 0xFF, 0x2B, 0x00, 0x11, 0x00, 0x1B,
+	0xFF, 0x02, 0x03, 0xFE, 0xF6, 0xC3, 0x43, 0x22, 0x16, 0x07, 0xF6,
+	0xCA, 0x05, 0xA3, 0xFC, 0xC5, 0x01, 0x3F, 0xFF, 0x33, 0x00, 0xFF,
+	0xFF, 0x03, 0x00, 0xFB, 0xFF, 0xDF, 0xFF, 0xA9, 0x00, 0x10, 0xFE,
+	0xB9, 0x04, 0x27, 0xF4, 0x5E, 0x3F, 0xC3, 0x1D, 0xFE, 0xF3, 0x99,
+	0x06, 0x50, 0xFC, 0xE2, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF,
+	0x00, 0x00, 0x13, 0x00, 0x9F, 0xFF, 0x28, 0x01, 0x3A, 0xFD, 0x00,
+	0x06, 0x5A, 0xF2, 0xDF, 0x39, 0x73, 0x25, 0x79, 0xF2, 0x12, 0x07,
+	0x31, 0xFC, 0xE3, 0x01, 0x3B, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0xFD,
+	0xFF, 0x24, 0x00, 0x6D, 0xFF, 0x88, 0x01, 0xA2, 0xFC, 0xD0, 0x06,
+	0x8C, 0xF1, 0x78, 0x33, 0xF2, 0x2C, 0x9E, 0xF1, 0x24, 0x07, 0x4E,
+	0xFC, 0xC3, 0x01, 0x4E, 0xFF, 0x2F, 0x00, 0xFD, 0xFF, 0xFD, 0xFF,
+	0x30, 0x00, 0x4C, 0xFF, 0xC7, 0x01, 0x4A, 0xFC, 0x27, 0x07, 0xA8,
+	0xF1, 0x62, 0x2C, 0xFD, 0x33, 0x93, 0xF1, 0xC4, 0x06, 0xAB, 0xFC,
+	0x82, 0x01, 0x71, 0xFF, 0x23, 0x00, 0xFE, 0xFF, 0xFD, 0xFF, 0x36,
+	0x00, 0x3A, 0xFF, 0xE4, 0x01, 0x32, 0xFC, 0x0C, 0x07, 0x91, 0xF2,
+	0xDD, 0x24, 0x54, 0x3A, 0x74, 0xF2, 0xEB, 0x05, 0x49, 0xFD, 0x20,
+	0x01, 0xA3, 0xFF, 0x11, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0x36, 0x00,
+	0x37, 0xFF, 0xE1, 0x01, 0x55, 0xFC, 0x8C, 0x06, 0x22, 0xF4, 0x2C,
+	0x1D, 0xC0, 0x3F, 0x55, 0xF4, 0x9B, 0x04, 0x23, 0xFE, 0x9F, 0x00,
+	0xE4, 0xFF, 0xF9, 0xFF, 0x04, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x40,
+	0xFF, 0xC1, 0x01, 0xAB, 0xFC, 0xB7, 0x05, 0x34, 0xF6, 0x8E, 0x15,
+	0x0B, 0x44, 0x42, 0xF7, 0xDC, 0x02, 0x32, 0xFF, 0x04, 0x00, 0x31,
+	0x00, 0xDC, 0xFF, 0x09, 0x00, 0xFF, 0xFF, 0x2C, 0x00, 0x55, 0xFF,
+	0x8B, 0x01, 0x2B, 0xFD, 0xA1, 0x04, 0x9B, 0xF8, 0x42, 0x0E, 0x0F,
+	0x47, 0x38, 0xFB, 0xBE, 0x00, 0x6A, 0x00, 0x58, 0xFF, 0x85, 0x00,
+	0xBB, 0xFF, 0x10, 0x00, 0x00, 0x00, 0x24, 0x00, 0x71, 0xFF, 0x43,
+	0x01, 0xC9, 0xFD, 0x60, 0x03, 0x2E, 0xFB, 0x7E, 0x07, 0xAF, 0x48,
+	0x2D, 0x00, 0x58, 0xFE, 0xBB, 0x01, 0xA3, 0xFE, 0xDD, 0x00, 0x99,
+	0xFF, 0x19, 0x00, 0x1B, 0x00, 0x91, 0xFF, 0xF1, 0x00, 0x79, 0xFE,
+	0x0A, 0x02, 0xC3, 0xFD, 0x73, 0x01, 0xDB, 0x48, 0x07, 0x06, 0xC7,
+	0xFB, 0x12, 0x03, 0xF1, 0xFD, 0x31, 0x01, 0x78, 0xFF, 0x22, 0x00,
+	0x00, 0x00, 0x12, 0x00, 0xB3, 0xFF, 0x99, 0x00, 0x2E, 0xFF, 0xB6,
+	0x00, 0x36, 0x00, 0x47, 0xFC, 0x90, 0x47, 0xA4, 0x0C, 0x31, 0xF9,
+	0x5A, 0x04, 0x4E, 0xFD, 0x7C, 0x01, 0x5B, 0xFF, 0x2A, 0x00, 0x00,
+	0x00, 0x0B, 0x00, 0xD5, 0xFF, 0x44, 0x00, 0xDD, 0xFF, 0x77, 0xFF,
+	0x67, 0x02, 0x14, 0xF8, 0xDC, 0x44, 0xD5, 0x13, 0xBC, 0xF6, 0x7C,
+	0x05, 0xC5, 0xFC, 0xB7, 0x01, 0x44, 0xFF, 0x31, 0x00, 0xFF, 0xFF,
+	0x05, 0x00, 0xF3, 0xFF, 0xF5, 0xFF, 0x7D, 0x00, 0x5D, 0xFE, 0x3E,
+	0x04, 0xEA, 0xF4, 0xD9, 0x40, 0x66, 0x1B, 0x93, 0xF4, 0x62, 0x06,
+	0x64, 0xFC, 0xDC, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x00,
+	0x00, 0x0C, 0x00, 0xB1, 0xFF, 0x04, 0x01, 0x76, 0xFD, 0xA8, 0x05,
+	0xCC, 0xF2, 0xAB, 0x3B, 0x18, 0x23, 0xE0, 0xF2, 0xF7, 0x06, 0x35,
+	0xFC, 0xE6, 0x01, 0x38, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFE, 0xFF,
+	0x20, 0x00, 0x7B, 0xFF, 0x6E, 0x01, 0xCA, 0xFC, 0x9D, 0x06, 0xB1,
+	0xF1, 0x86, 0x35, 0xAE, 0x2A, 0xCD, 0xF1, 0x2B, 0x07, 0x3F, 0xFC,
+	0xD1, 0x01, 0x46, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x2D,
+	0x00, 0x54, 0xFF, 0xB7, 0x01, 0x5E, 0xFC, 0x19, 0x07, 0x88, 0xF1,
+	0x9F, 0x2E, 0xE3, 0x31, 0x7E, 0xF1, 0xEE, 0x06, 0x88, 0xFC, 0x9A,
+	0x01, 0x64, 0xFF, 0x28, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x34, 0x00,
+	0x3E, 0xFF, 0xDF, 0x01, 0x33, 0xFC, 0x20, 0x07, 0x35, 0xF2, 0x36,
+	0x27, 0x78, 0x38, 0x14, 0xF2, 0x3B, 0x06, 0x11, 0xFD, 0x41, 0x01,
+	0x92, 0xFF, 0x17, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x36,
+	0xFF, 0xE5, 0x01, 0x44, 0xFC, 0xBD, 0x06, 0x97, 0xF3, 0x8A, 0x1F,
+	0x31, 0x3E, 0xA5, 0xF3, 0x0F, 0x05, 0xDA, 0xFD, 0xC9, 0x00, 0xCF,
+	0xFF, 0x01, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3C, 0xFF,
+	0xCE, 0x01, 0x8C, 0xFC, 0x00, 0x06, 0x86, 0xF5, 0xE0, 0x17, 0xDB,
+	0x42, 0x3F, 0xF6, 0x71, 0x03, 0xD9, 0xFE, 0x36, 0x00, 0x18, 0x00,
+	0xE5, 0xFF, 0x07, 0x00, 0xFF, 0xFF, 0x2E, 0x00, 0x4E, 0xFF, 0x9E,
+	0x01, 0x00, 0xFD, 0xFC, 0x04, 0xD7, 0xF7, 0x75, 0x10, 0x48, 0x46,
+	0xE4, 0xF9, 0x6E, 0x01, 0x06, 0x00, 0x8E, 0xFF, 0x6B, 0x00, 0xC6,
+	0xFF, 0x0E, 0x00, 0x00, 0x00, 0x26, 0x00, 0x68, 0xFF, 0x5B, 0x01,
+	0x96, 0xFD, 0xC6, 0x03, 0x61, 0xFA, 0x81, 0x09, 0x57, 0x48, 0x8D,
+	0xFE, 0x1B, 0xFF, 0x52, 0x01, 0xDB, 0xFE, 0xC2, 0x00, 0xA4, 0xFF,
+	0x16, 0x00, 0x1E, 0x00, 0x87, 0xFF, 0x0B, 0x01, 0x42, 0xFE, 0x74,
+	0x02, 0xF9, 0xFC, 0x39, 0x03, 0xF5, 0x48, 0x24, 0x04, 0x94, 0xFC,
+	0xA9, 0x02, 0x27, 0xFE, 0x18, 0x01, 0x82, 0xFF, 0x1F, 0x00, 0x00,
+	0x00, 0x15, 0x00, 0xA9, 0xFF, 0xB4, 0x00, 0xF7, 0xFE, 0x1D, 0x01,
+	0x7A, 0xFF, 0xC5, 0xFD, 0x1D, 0x48, 0x89, 0x0A, 0xFB, 0xF9, 0xF8,
+	0x03, 0x7D, 0xFD, 0x66, 0x01, 0x63, 0xFF, 0x28, 0x00, 0x00, 0x00,
+	0x0D, 0x00, 0xCB, 0xFF, 0x5E, 0x00, 0xA9, 0xFF, 0xD6, 0xFF, 0xC3,
+	0x01, 0x43, 0xF9, 0xD7, 0x45, 0x92, 0x11, 0x77, 0xF7, 0x28, 0x05,
+	0xEC, 0xFC, 0xA7, 0x01, 0x4A, 0xFF, 0x2F, 0x00, 0xFF, 0xFF, 0x06,
+	0x00, 0xEA, 0xFF, 0x0C, 0x00, 0x4E, 0x00, 0xAF, 0xFE, 0xB8, 0x03,
+	0xC7, 0xF5, 0x38, 0x42, 0x0C, 0x19, 0x32, 0xF5, 0x23, 0x06, 0x7D,
+	0xFC, 0xD3, 0x01, 0x3A, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x02, 0x00,
+	0x05, 0x00, 0xC5, 0xFF, 0xDE, 0x00, 0xB7, 0xFD, 0x45, 0x05, 0x56,
+	0xF3, 0x61, 0x3D, 0xBA, 0x20, 0x56, 0xF3, 0xD3, 0x06, 0x3E, 0xFC,
+	0xE6, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFF, 0xFF, 0x1A,
+	0x00, 0x8A, 0xFF, 0x51, 0x01, 0xF8, 0xFC, 0x5E, 0x06, 0xED, 0xF1,
+	0x82, 0x37, 0x60, 0x28, 0x0E, 0xF2, 0x26, 0x07, 0x35, 0xFC, 0xDB,
+	0x01, 0x40, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x29, 0x00,
+	0x5F, 0xFF, 0xA5, 0x01, 0x78, 0xFC, 0xFF, 0x06, 0x7D, 0xF1, 0xCF,
+	0x30, 0xB8, 0x2F, 0x80, 0xF1, 0x0D, 0x07, 0x6A, 0xFC, 0xAE, 0x01,
+	0x59, 0xFF, 0x2B, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x33, 0x00, 0x43,
+	0xFF, 0xD6, 0x01, 0x39, 0xFC, 0x2A, 0x07, 0xEB, 0xF1, 0x87, 0x29,
+	0x85, 0x36, 0xCC, 0xF1, 0x7F, 0x06, 0xE0, 0xFC, 0x60, 0x01, 0x82,
+	0xFF, 0x1D, 0x00, 0xFE, 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x37, 0xFF,
+	0xE6, 0x01, 0x38, 0xFC, 0xE6, 0x06, 0x19, 0xF3, 0xEA, 0x21, 0x8A,
+	0x3C, 0x0E, 0xF3, 0x78, 0x05, 0x96, 0xFD, 0xF1, 0x00, 0xBB, 0xFF,
+	0x08, 0x00, 0x01, 0x00, 0xFE, 0xFF, 0x35, 0x00, 0x39, 0xFF, 0xD8,
+	0x01, 0x70, 0xFC, 0x43, 0x06, 0xE1, 0xF4, 0x38, 0x1A, 0x8C, 0x41,
+	0x55, 0xF5, 0xFC, 0x03, 0x85, 0xFE, 0x66, 0x00, 0x01, 0x00, 0xEE,
+	0xFF, 0x06, 0x00, 0xFF, 0xFF, 0x30, 0x00, 0x47, 0xFF, 0xAF, 0x01,
+	0xD8, 0xFC, 0x52, 0x05, 0x19, 0xF7, 0xB2, 0x12, 0x5C, 0x45, 0xA9,
+	0xF8, 0x16, 0x02, 0xA6, 0xFF, 0xC3, 0xFF, 0x51, 0x00, 0xD0, 0xFF,
+	0x0C, 0x00, 0x00, 0x00, 0x29, 0x00, 0x5F, 0xFF, 0x71, 0x01, 0x65,
+	0xFD, 0x29, 0x04, 0x96, 0xF9, 0x95, 0x0B, 0xDC, 0x47, 0x03, 0xFD,
+	0xD9, 0xFF, 0xEA, 0x00, 0x12, 0xFF, 0xA7, 0x00, 0xAE, 0xFF, 0x14,
+	0x00, 0x00, 0x00, 0x20, 0x00, 0x7D, 0xFF, 0x24, 0x01, 0x0C, 0xFE,
+	0xDE, 0x02, 0x2E, 0xFC, 0x13, 0x05, 0xEC, 0x48, 0x54, 0x02, 0x5E,
+	0xFD, 0x3F, 0x02, 0x5D, 0xFE, 0xFE, 0x00, 0x8C, 0xFF, 0x1C, 0x00,
+	0x17, 0x00, 0x9E, 0xFF, 0xCF, 0x00, 0xBF, 0xFE, 0x86, 0x01, 0xBA,
+	0xFE, 0x5A, 0xFF, 0x86, 0x48, 0x7D, 0x08, 0xC7, 0xFA, 0x93, 0x03,
+	0xB0, 0xFD, 0x4F, 0x01, 0x6C, 0xFF, 0x25, 0x00, 0x00, 0x00, 0x0F,
+	0x00, 0xC0, 0xFF, 0x78, 0x00, 0x73, 0xFF, 0x38, 0x00, 0x17, 0x01,
+	0x8B, 0xFA, 0xAF, 0x46, 0x59, 0x0F, 0x39, 0xF8, 0xCF, 0x04, 0x15,
+	0xFD, 0x95, 0x01, 0x51, 0xFF, 0x2D, 0x00, 0xFF, 0xFF, 0x08, 0x00,
+	0xE1, 0xFF, 0x25, 0x00, 0x1D, 0x00, 0x05, 0xFF, 0x28, 0x03, 0xBD,
+	0xF6, 0x77, 0x43, 0xB6, 0x16, 0xDC, 0xF5, 0xDD, 0x05, 0x9B, 0xFC,
+	0xC8, 0x01, 0x3E, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0x03, 0x00, 0xFD,
+	0xFF, 0xD9, 0xFF, 0xB4, 0x00, 0xFD, 0xFD, 0xD7, 0x04, 0xFA, 0xF3,
+	0xFC, 0x3E, 0x5B, 0x1E, 0xDB, 0xF3, 0xA6, 0x06, 0x4C, 0xFC, 0xE3,
+	0x01, 0x36, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x14, 0x00,
+	0x9B, 0xFF, 0x31, 0x01, 0x2C, 0xFD, 0x15, 0x06, 0x41, 0xF2, 0x6A,
+	0x39, 0x0A, 0x26, 0x61, 0xF2, 0x17, 0x07, 0x31, 0xFC, 0xE2, 0x01,
+	0x3B, 0xFF, 0x35, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x25, 0x00, 0x6A,
+	0xFF, 0x8E, 0x01, 0x99, 0xFC, 0xDB, 0x06, 0x86, 0xF1, 0xF2, 0x32,
+	0x82, 0x2D, 0x96, 0xF1, 0x21, 0x07, 0x53, 0xFC, 0xC0, 0x01, 0x50,
+	0xFF, 0x2E, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x30, 0x00, 0x4A, 0xFF,
+	0xCA, 0x01, 0x46, 0xFC, 0x29, 0x07, 0xB3, 0xF1, 0xD1, 0x2B, 0x81,
+	0x34, 0x9C, 0xF1, 0xB8, 0x06, 0xB5, 0xFC, 0x7C, 0x01, 0x74, 0xFF,
+	0x22, 0x00, 0xFE, 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x39, 0xFF, 0xE5,
+	0x01, 0x32, 0xFC, 0x06, 0x07, 0xAA, 0xF2, 0x46, 0x24, 0xC8, 0x3A,
+	0x90, 0xF2, 0xD6, 0x05, 0x57, 0xFD, 0x17, 0x01, 0xA8, 0xFF, 0x0F,
+	0x00, 0x00, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x37, 0xFF, 0xDF, 0x01,
+	0x5A, 0xFC, 0x7E, 0x06, 0x47, 0xF4, 0x94, 0x1C, 0x1F, 0x40, 0x85,
+	0xF4, 0x7D, 0x04, 0x36, 0xFE, 0x93, 0x00, 0xEA, 0xFF, 0xF7, 0xFF,
+	0x04, 0x00, 0xFF, 0xFF, 0x32, 0x00, 0x42, 0xFF, 0xBE, 0x01, 0xB4,
+	0xFC, 0xA4, 0x05, 0x61, 0xF6, 0xFB, 0x14, 0x53, 0x44, 0x86, 0xF7,
+	0xB6, 0x02, 0x49, 0xFF, 0xF7, 0xFF, 0x37, 0x00, 0xD9, 0xFF, 0x0A,
+	0x00, 0x00, 0x00, 0x2B, 0x00, 0x57, 0xFF, 0x86, 0x01, 0x36, 0xFD,
+	0x89, 0x04, 0xCD, 0xF8, 0xB7, 0x0D, 0x3D, 0x47, 0x91, 0xFB, 0x91,
+	0x00, 0x83, 0x00, 0x4A, 0xFF, 0x8C, 0x00, 0xB9, 0xFF, 0x11, 0x00,
+	0x00, 0x00, 0x23, 0x00, 0x73, 0xFF, 0x3D, 0x01, 0xD6, 0xFD, 0x46,
+	0x03, 0x61, 0xFB, 0x00, 0x07, 0xBF, 0x48, 0x98, 0x00, 0x26, 0xFE,
+	0xD5, 0x01, 0x95, 0xFE, 0xE3, 0x00, 0x96, 0xFF, 0x1A, 0x00, 0x1A,
+	0x00, 0x94, 0xFF, 0xEA, 0x00, 0x87, 0xFE, 0xF0, 0x01, 0xF5, 0xFD,
+	0x05, 0x01, 0xCE, 0x48, 0x83, 0x06, 0x94, 0xFB, 0x2C, 0x03, 0xE4,
+	0xFD, 0x37, 0x01, 0x76, 0xFF, 0x22, 0x00, 0x00, 0x00, 0x12, 0x00,
+	0xB6, 0xFF, 0x93, 0x00, 0x3C, 0xFF, 0x9D, 0x00, 0x63, 0x00, 0xEB,
+	0xFB, 0x69, 0x47, 0x2D, 0x0D, 0xFF, 0xF8, 0x72, 0x04, 0x42, 0xFD,
+	0x81, 0x01, 0x59, 0xFF, 0x2B, 0x00, 0x00, 0x00, 0x0A, 0x00, 0xD7,
+	0xFF, 0x3E, 0x00, 0xEA, 0xFF, 0x60, 0xFF, 0x8F, 0x02, 0xCD, 0xF7,
+	0x99, 0x44, 0x68, 0x14, 0x8E, 0xF6, 0x90, 0x05, 0xBC, 0xFC, 0xBA,
+	0x01, 0x43, 0xFF, 0x32, 0x00, 0xFF, 0xFF, 0x04, 0x00, 0xF5, 0xFF,
+	0xEF, 0xFF, 0x88, 0x00, 0x49, 0xFE, 0x5D, 0x04, 0xB7, 0xF4, 0x7D,
+	0x40, 0xFD, 0x1B, 0x6C, 0xF4, 0x70, 0x06, 0x5F, 0xFC, 0xDE, 0x01,
+	0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x00, 0x00, 0x0E, 0x00, 0xAC,
+	0xFF, 0x0E, 0x01, 0x66, 0xFD, 0xBF, 0x05, 0xAD, 0xF2, 0x3B, 0x3B,
+	0xB0, 0x23, 0xC4, 0xF2, 0xFF, 0x06, 0x33, 0xFC, 0xE5, 0x01, 0x38,
+	0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFE, 0xFF, 0x21, 0x00, 0x77, 0xFF,
+	0x75, 0x01, 0xBF, 0xFC, 0xAB, 0x06, 0xA6, 0xF1, 0x05, 0x35, 0x40,
+	0x2B, 0xBF, 0xF1, 0x2A, 0x07, 0x42, 0xFC, 0xCE, 0x01, 0x48, 0xFF,
+	0x31, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x2E, 0x00, 0x52, 0xFF, 0xBC,
+	0x01, 0x58, 0xFC, 0x1D, 0x07, 0x8E, 0xF1, 0x11, 0x2E, 0x6B, 0x32,
+	0x81, 0xF1, 0xE5, 0x06, 0x90, 0xFC, 0x94, 0x01, 0x67, 0xFF, 0x26,
+	0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x35, 0x00, 0x3C, 0xFF, 0xE0, 0x01,
+	0x32, 0xFC, 0x1C, 0x07, 0x4B, 0xF2, 0xA0, 0x26, 0xF2, 0x38, 0x2A,
+	0xF2, 0x28, 0x06, 0x1F, 0xFD, 0x39, 0x01, 0x96, 0xFF, 0x16, 0x00,
+	0xFF, 0xFF, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE4, 0x01, 0x48,
+	0xFC, 0xB2, 0x06, 0xB9, 0xF3, 0xF3, 0x1E, 0x98, 0x3E, 0xCF, 0xF3,
+	0xF3, 0x04, 0xEB, 0xFD, 0xBF, 0x00, 0xD4, 0xFF, 0xFF, 0xFF, 0x03,
+	0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3D, 0xFF, 0xCB, 0x01, 0x93, 0xFC,
+	0xEF, 0x05, 0xB0, 0xF5, 0x4B, 0x17, 0x2A, 0x43, 0x7D, 0xF6, 0x4D,
+	0x03, 0xEF, 0xFE, 0x2A, 0x00, 0x1E, 0x00, 0xE3, 0xFF, 0x08, 0x00,
+	0xFF, 0xFF, 0x2E, 0x00, 0x4F, 0xFF, 0x99, 0x01, 0x0B, 0xFD, 0xE6,
+	0x04, 0x08, 0xF8, 0xE7, 0x0F, 0x7C, 0x46, 0x37, 0xFA, 0x42, 0x01,
+	0x1F, 0x00, 0x81, 0xFF, 0x71, 0x00, 0xC3, 0xFF, 0x0F, 0x00, 0x00,
+	0x00, 0x26, 0x00, 0x6A, 0xFF, 0x55, 0x01, 0xA3, 0xFD, 0xAD, 0x03,
+	0x94, 0xFA, 0xFF, 0x08, 0x70, 0x48, 0xF3, 0xFE, 0xEA, 0xFE, 0x6C,
+	0x01, 0xCD, 0xFE, 0xC9, 0x00, 0xA1, 0xFF, 0x17, 0x00, 0x1D, 0x00,
+	0x8A, 0xFF, 0x04, 0x01, 0x50, 0xFE, 0x5A, 0x02, 0x2C, 0xFD, 0xC6,
+	0x02, 0xF2, 0x48, 0x9B, 0x04, 0x61, 0xFC, 0xC3, 0x02, 0x19, 0xFE,
+	0x1E, 0x01, 0x7F, 0xFF, 0x20, 0x00, 0x00, 0x00, 0x14, 0x00, 0xAC,
+	0xFF, 0xAE, 0x00, 0x05, 0xFF, 0x03, 0x01, 0xAA, 0xFF, 0x63, 0xFD,
+	0xFD, 0x47, 0x0E, 0x0B, 0xC8, 0xF9, 0x11, 0x04, 0x71, 0xFD, 0x6C,
+	0x01, 0x61, 0xFF, 0x28, 0x00, 0x00, 0x00, 0x0C, 0x00, 0xCD, 0xFF,
+	0x57, 0x00, 0xB6, 0xFF, 0xBE, 0xFF, 0xED, 0x01, 0xF5, 0xF8, 0x9B,
+	0x45, 0x22, 0x12, 0x48, 0xF7, 0x3D, 0x05, 0xE2, 0xFC, 0xAB, 0x01,
+	0x49, 0xFF, 0x30, 0x00, 0xFF, 0xFF, 0x06, 0x00, 0xEC, 0xFF, 0x06,
+	0x00, 0x5A, 0x00, 0x9A, 0xFE, 0xDA, 0x03, 0x8D, 0xF5, 0xE1, 0x41,
+	0xA1, 0x19, 0x09, 0xF5, 0x33, 0x06, 0x77, 0xFC, 0xD6, 0x01, 0x3A,
+	0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x07, 0x00, 0xC0, 0xFF,
+	0xE8, 0x00, 0xA6, 0xFD, 0x5F, 0x05, 0x31, 0xF3, 0xF6, 0x3C, 0x52,
+	0x21, 0x37, 0xF3, 0xDD, 0x06, 0x3B, 0xFC, 0xE6, 0x01, 0x36, 0xFF,
+	0x36, 0x00, 0xFD, 0xFF, 0xFE, 0xFF, 0x1C, 0x00, 0x86, 0xFF, 0x59,
+	0x01, 0xEC, 0xFC, 0x6F, 0x06, 0xDC, 0xF1, 0x04, 0x37, 0xF3, 0x28,
+	0xFC, 0xF1, 0x28, 0x07, 0x37, 0xFC, 0xD8, 0x01, 0x41, 0xFF, 0x33,
+	0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x2A, 0x00, 0x5C, 0xFF, 0xAA, 0x01,
+	0x71, 0xFC, 0x07, 0x07, 0x7E, 0xF1, 0x44, 0x30, 0x44, 0x30, 0x7E,
+	0xF1, 0x07, 0x07, 0x71, 0xFC, 0xAA, 0x01, 0x5C, 0xFF, 0x2A, 0x00,
+	0xFD, 0xFF, 0xFD, 0xFF, 0x33, 0x00, 0x41, 0xFF, 0xD8, 0x01, 0x37,
+	0xFC, 0x28, 0x07, 0xFC, 0xF1, 0xF3, 0x28, 0x04, 0x37, 0xDC, 0xF1,
+	0x6F, 0x06, 0xEC, 0xFC, 0x59, 0x01, 0x86, 0xFF, 0x1C, 0x00, 0xFE,
+	0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3B, 0xFC,
+	0xDD, 0x06, 0x37, 0xF3, 0x52, 0x21, 0xF6, 0x3C, 0x31, 0xF3, 0x5F,
+	0x05, 0xA6, 0xFD, 0xE8, 0x00, 0xC0, 0xFF, 0x07, 0x00, 0x01, 0x00,
+	0xFE, 0xFF, 0x35, 0x00, 0x3A, 0xFF, 0xD6, 0x01, 0x77, 0xFC, 0x33,
+	0x06, 0x09, 0xF5, 0xA1, 0x19, 0xE1, 0x41, 0x8D, 0xF5, 0xDA, 0x03,
+	0x9A, 0xFE, 0x5A, 0x00, 0x06, 0x00, 0xEC, 0xFF, 0x06, 0x00, 0xFF,
+	0xFF, 0x30, 0x00, 0x49, 0xFF, 0xAB, 0x01, 0xE2, 0xFC, 0x3D, 0x05,
+	0x48, 0xF7, 0x22, 0x12, 0x9B, 0x45, 0xF5, 0xF8, 0xED, 0x01, 0xBE,
+	0xFF, 0xB6, 0xFF, 0x57, 0x00, 0xCD, 0xFF, 0x0C, 0x00, 0x00, 0x00,
+	0x28, 0x00, 0x61, 0xFF, 0x6C, 0x01, 0x71, 0xFD, 0x11, 0x04, 0xC8,
+	0xF9, 0x0E, 0x0B, 0xFD, 0x47, 0x63, 0xFD, 0xAA, 0xFF, 0x03, 0x01,
+	0x05, 0xFF, 0xAE, 0x00, 0xAC, 0xFF, 0x14, 0x00, 0x00, 0x00, 0x20,
+	0x00, 0x7F, 0xFF, 0x1E, 0x01, 0x19, 0xFE, 0xC3, 0x02, 0x61, 0xFC,
+	0x9B, 0x04, 0xF2, 0x48, 0xC6, 0x02, 0x2C, 0xFD, 0x5A, 0x02, 0x50,
+	0xFE, 0x04, 0x01, 0x8A, 0xFF, 0x1D, 0x00, 0x17, 0x00, 0xA1, 0xFF,
+	0xC9, 0x00, 0xCD, 0xFE, 0x6C, 0x01, 0xEA, 0xFE, 0xF3, 0xFE, 0x70,
+	0x48, 0xFF, 0x08, 0x94, 0xFA, 0xAD, 0x03, 0xA3, 0xFD, 0x55, 0x01,
+	0x6A, 0xFF, 0x26, 0x00, 0x00, 0x00, 0x0F, 0x00, 0xC3, 0xFF, 0x71,
+	0x00, 0x81, 0xFF, 0x1F, 0x00, 0x42, 0x01, 0x37, 0xFA, 0x7C, 0x46,
+	0xE7, 0x0F, 0x08, 0xF8, 0xE6, 0x04, 0x0B, 0xFD, 0x99, 0x01, 0x4F,
+	0xFF, 0x2E, 0x00, 0xFF, 0xFF, 0x08, 0x00, 0xE3, 0xFF, 0x1E, 0x00,
+	0x2A, 0x00, 0xEF, 0xFE, 0x4D, 0x03, 0x7D, 0xF6, 0x2A, 0x43, 0x4B,
+	0x17, 0xB0, 0xF5, 0xEF, 0x05, 0x93, 0xFC, 0xCB, 0x01, 0x3D, 0xFF,
+	0x34, 0x00, 0xFE, 0xFF, 0x03, 0x00, 0xFF, 0xFF, 0xD4, 0xFF, 0xBF,
+	0x00, 0xEB, 0xFD, 0xF3, 0x04, 0xCF, 0xF3, 0x98, 0x3E, 0xF3, 0x1E,
+	0xB9, 0xF3, 0xB2, 0x06, 0x48, 0xFC, 0xE4, 0x01, 0x36, 0xFF, 0x36,
+	0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x16, 0x00, 0x96, 0xFF, 0x39, 0x01,
+	0x1F, 0xFD, 0x28, 0x06, 0x2A, 0xF2, 0xF2, 0x38, 0xA0, 0x26, 0x4B,
+	0xF2, 0x1C, 0x07, 0x32, 0xFC, 0xE0, 0x01, 0x3C, 0xFF, 0x35, 0x00,
+	0xFD, 0xFF, 0xFD, 0xFF, 0x26, 0x00, 0x67, 0xFF, 0x94, 0x01, 0x90,
+	0xFC, 0xE5, 0x06, 0x81, 0xF1, 0x6B, 0x32, 0x11, 0x2E, 0x8E, 0xF1,
+	0x1D, 0x07, 0x58, 0xFC, 0xBC, 0x01, 0x52, 0xFF, 0x2E, 0x00, 0xFD,
+	0xFF, 0xFD, 0xFF, 0x31, 0x00, 0x48, 0xFF, 0xCE, 0x01, 0x42, 0xFC,
+	0x2A, 0x07, 0xBF, 0xF1, 0x40, 0x2B, 0x05, 0x35, 0xA6, 0xF1, 0xAB,
+	0x06, 0xBF, 0xFC, 0x75, 0x01, 0x77, 0xFF, 0x21, 0x00, 0xFE, 0xFF,
+	0xFD, 0xFF, 0x36, 0x00, 0x38, 0xFF, 0xE5, 0x01, 0x33, 0xFC, 0xFF,
+	0x06, 0xC4, 0xF2, 0xB0, 0x23, 0x3B, 0x3B, 0xAD, 0xF2, 0xBF, 0x05,
+	0x66, 0xFD, 0x0E, 0x01, 0xAC, 0xFF, 0x0E, 0x00, 0x00, 0x00, 0xFE,
+	0xFF, 0x36, 0x00, 0x37, 0xFF, 0xDE, 0x01, 0x5F, 0xFC, 0x70, 0x06,
+	0x6C, 0xF4, 0xFD, 0x1B, 0x7D, 0x40, 0xB7, 0xF4, 0x5D, 0x04, 0x49,
+	0xFE, 0x88, 0x00, 0xEF, 0xFF, 0xF5, 0xFF, 0x04, 0x00, 0xFF, 0xFF,
+	0x32, 0x00, 0x43, 0xFF, 0xBA, 0x01, 0xBC, 0xFC, 0x90, 0x05, 0x8E,
+	0xF6, 0x68, 0x14, 0x99, 0x44, 0xCD, 0xF7, 0x8F, 0x02, 0x60, 0xFF,
+	0xEA, 0xFF, 0x3E, 0x00, 0xD7, 0xFF, 0x0A, 0x00, 0x00, 0x00, 0x2B,
+	0x00, 0x59, 0xFF, 0x81, 0x01, 0x42, 0xFD, 0x72, 0x04, 0xFF, 0xF8,
+	0x2D, 0x0D, 0x69, 0x47, 0xEB, 0xFB, 0x63, 0x00, 0x9D, 0x00, 0x3C,
+	0xFF, 0x93, 0x00, 0xB6, 0xFF, 0x12, 0x00, 0x00, 0x00, 0x22, 0x00,
+	0x76, 0xFF, 0x37, 0x01, 0xE4, 0xFD, 0x2C, 0x03, 0x94, 0xFB, 0x83,
+	0x06, 0xCE, 0x48, 0x05, 0x01, 0xF5, 0xFD, 0xF0, 0x01, 0x87, 0xFE,
+	0xEA, 0x00, 0x94, 0xFF, 0x1A, 0x00, 0x1A, 0x00, 0x96, 0xFF, 0xE3,
+	0x00, 0x95, 0xFE, 0xD5, 0x01, 0x26, 0xFE, 0x98, 0x00, 0xBF, 0x48,
+	0x00, 0x07, 0x61, 0xFB, 0x46, 0x03, 0xD6, 0xFD, 0x3D, 0x01, 0x73,
+	0xFF, 0x23, 0x00, 0x00, 0x00, 0x11, 0x00, 0xB9, 0xFF, 0x8C, 0x00,
+	0x4A, 0xFF, 0x83, 0x00, 0x91, 0x00, 0x91, 0xFB, 0x3D, 0x47, 0xB7,
+	0x0D, 0xCD, 0xF8, 0x89, 0x04, 0x36, 0xFD, 0x86, 0x01, 0x57, 0xFF,
+	0x2B, 0x00, 0x00, 0x00, 0x0A, 0x00, 0xD9, 0xFF, 0x37, 0x00, 0xF7,
+	0xFF, 0x49, 0xFF, 0xB6, 0x02, 0x86, 0xF7, 0x53, 0x44, 0xFB, 0x14,
+	0x61, 0xF6, 0xA4, 0x05, 0xB4, 0xFC, 0xBE, 0x01, 0x42, 0xFF, 0x32,
+	0x00, 0xFF, 0xFF, 0x04, 0x00, 0xF7, 0xFF, 0xEA, 0xFF, 0x93, 0x00,
+	0x36, 0xFE, 0x7D, 0x04, 0x85, 0xF4, 0x1F, 0x40, 0x94, 0x1C, 0x47,
+	0xF4, 0x7E, 0x06, 0x5A, 0xFC, 0xDF, 0x01, 0x37, 0xFF, 0x36, 0x00,
+	0xFE, 0xFF, 0x00, 0x00, 0x0F, 0x00, 0xA8, 0xFF, 0x17, 0x01, 0x57,
+	0xFD, 0xD6, 0x05, 0x90, 0xF2, 0xC8, 0x3A, 0x46, 0x24, 0xAA, 0xF2,
+	0x06, 0x07, 0x32, 0xFC, 0xE5, 0x01, 0x39, 0xFF, 0x36, 0x00, 0xFD,
+	0xFF, 0xFE, 0xFF, 0x22, 0x00, 0x74, 0xFF, 0x7C, 0x01, 0xB5, 0xFC,
+	0xB8, 0x06, 0x9C, 0xF1, 0x81, 0x34, 0xD1, 0x2B, 0xB3, 0xF1, 0x29,
+	0x07, 0x46, 0xFC, 0xCA, 0x01, 0x4A, 0xFF, 0x30, 0x00, 0xFD, 0xFF,
+	0xFD, 0xFF, 0x2E, 0x00, 0x50, 0xFF, 0xC0, 0x01, 0x53, 0xFC, 0x21,
+	0x07, 0x96, 0xF1, 0x82, 0x2D, 0xF2, 0x32, 0x86, 0xF1, 0xDB, 0x06,
+	0x99, 0xFC, 0x8E, 0x01, 0x6A, 0xFF, 0x25, 0x00, 0xFD, 0xFF, 0xFD,
+	0xFF, 0x35, 0x00, 0x3B, 0xFF, 0xE2, 0x01, 0x31, 0xFC, 0x17, 0x07,
+	0x61, 0xF2, 0x0A, 0x26, 0x6A, 0x39, 0x41, 0xF2, 0x15, 0x06, 0x2C,
+	0xFD, 0x31, 0x01, 0x9B, 0xFF, 0x14, 0x00, 0xFF, 0xFF, 0xFE, 0xFF,
+	0x36, 0x00, 0x36, 0xFF, 0xE3, 0x01, 0x4C, 0xFC, 0xA6, 0x06, 0xDB,
+	0xF3, 0x5B, 0x1E, 0xFC, 0x3E, 0xFA, 0xF3, 0xD7, 0x04, 0xFD, 0xFD,
+	0xB4, 0x00, 0xD9, 0xFF, 0xFD, 0xFF, 0x03, 0x00, 0xFF, 0xFF, 0x33,
+	0x00, 0x3E, 0xFF, 0xC8, 0x01, 0x9B, 0xFC, 0xDD, 0x05, 0xDC, 0xF5,
+	0xB6, 0x16, 0x77, 0x43, 0xBD, 0xF6, 0x28, 0x03, 0x05, 0xFF, 0x1D,
+	0x00, 0x25, 0x00, 0xE1, 0xFF, 0x08, 0x00, 0xFF, 0xFF, 0x2D, 0x00,
+	0x51, 0xFF, 0x95, 0x01, 0x15, 0xFD, 0xCF, 0x04, 0x39, 0xF8, 0x59,
+	0x0F, 0xAF, 0x46, 0x8B, 0xFA, 0x17, 0x01, 0x38, 0x00, 0x73, 0xFF,
+	0x78, 0x00, 0xC0, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x25, 0x00, 0x6C,
+	0xFF, 0x4F, 0x01, 0xB0, 0xFD, 0x93, 0x03, 0xC7, 0xFA, 0x7D, 0x08,
+	0x86, 0x48, 0x5A, 0xFF, 0xBA, 0xFE, 0x86, 0x01, 0xBF, 0xFE, 0xCF,
+	0x00, 0x9E, 0xFF, 0x17, 0x00, 0x1C, 0x00, 0x8C, 0xFF, 0xFE, 0x00,
+	0x5D, 0xFE, 0x3F, 0x02, 0x5E, 0xFD, 0x54, 0x02, 0xEC, 0x48, 0x13,
+	0x05, 0x2E, 0xFC, 0xDE, 0x02, 0x0C, 0xFE, 0x24, 0x01, 0x7D, 0xFF,
+	0x20, 0x00, 0x00, 0x00, 0x14, 0x00, 0xAE, 0xFF, 0xA7, 0x00, 0x12,
+	0xFF, 0xEA, 0x00, 0xD9, 0xFF, 0x03, 0xFD, 0xDC, 0x47, 0x95, 0x0B,
+	0x96, 0xF9, 0x29, 0x04, 0x65, 0xFD, 0x71, 0x01, 0x5F, 0xFF, 0x29,
+	0x00, 0x00, 0x00, 0x0C, 0x00, 0xD0, 0xFF, 0x51, 0x00, 0xC3, 0xFF,
+	0xA6, 0xFF, 0x16, 0x02, 0xA9, 0xF8, 0x5C, 0x45, 0xB2, 0x12, 0x19,
+	0xF7, 0x52, 0x05, 0xD8, 0xFC, 0xAF, 0x01, 0x47, 0xFF, 0x30, 0x00,
+	0xFF, 0xFF, 0x06, 0x00, 0xEE, 0xFF, 0x01, 0x00, 0x66, 0x00, 0x85,
+	0xFE, 0xFC, 0x03, 0x55, 0xF5, 0x8C, 0x41, 0x38, 0x1A, 0xE1, 0xF4,
+	0x43, 0x06, 0x70, 0xFC, 0xD8, 0x01, 0x39, 0xFF, 0x35, 0x00, 0xFE,
+	0xFF, 0x01, 0x00, 0x08, 0x00, 0xBB, 0xFF, 0xF1, 0x00, 0x96, 0xFD,
+	0x78, 0x05, 0x0E, 0xF3, 0x8A, 0x3C, 0xEA, 0x21, 0x19, 0xF3, 0xE6,
+	0x06, 0x38, 0xFC, 0xE6, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF,
+	0xFE, 0xFF, 0x1D, 0x00, 0x82, 0xFF, 0x60, 0x01, 0xE0, 0xFC, 0x7F,
+	0x06, 0xCC, 0xF1, 0x85, 0x36, 0x87, 0x29, 0xEB, 0xF1, 0x2A, 0x07,
+	0x39, 0xFC, 0xD6, 0x01, 0x43, 0xFF, 0x33, 0x00, 0xFD, 0xFF, 0xFD,
+	0xFF, 0x2B, 0x00, 0x59, 0xFF, 0xAE, 0x01, 0x6A, 0xFC, 0x0D, 0x07,
+	0x80, 0xF1, 0xB8, 0x2F, 0xCF, 0x30, 0x7D, 0xF1, 0xFF, 0x06, 0x78,
+	0xFC, 0xA5, 0x01, 0x5F, 0xFF, 0x29, 0x00, 0xFD, 0xFF, 0xFD, 0xFF,
+	0x34, 0x00, 0x40, 0xFF, 0xDB, 0x01, 0x35, 0xFC, 0x26, 0x07, 0x0E,
+	0xF2, 0x60, 0x28, 0x82, 0x37, 0xED, 0xF1, 0x5E, 0x06, 0xF8, 0xFC,
+	0x51, 0x01, 0x8A, 0xFF, 0x1A, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x36,
+	0x00, 0x36, 0xFF, 0xE6, 0x01, 0x3E, 0xFC, 0xD3, 0x06, 0x56, 0xF3,
+	0xBA, 0x20, 0x61, 0x3D, 0x56, 0xF3, 0x45, 0x05, 0xB7, 0xFD, 0xDE,
+	0x00, 0xC5, 0xFF, 0x05, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0x35, 0x00,
+	0x3A, 0xFF, 0xD3, 0x01, 0x7D, 0xFC, 0x23, 0x06, 0x32, 0xF5, 0x0C,
+	0x19, 0x38, 0x42, 0xC7, 0xF5, 0xB8, 0x03, 0xAF, 0xFE, 0x4E, 0x00,
+	0x0C, 0x00, 0xEA, 0xFF, 0x06, 0x00, 0xFF, 0xFF, 0x2F, 0x00, 0x4A,
+	0xFF, 0xA7, 0x01, 0xEC, 0xFC, 0x28, 0x05, 0x77, 0xF7, 0x92, 0x11,
+	0xD7, 0x45, 0x43, 0xF9, 0xC3, 0x01, 0xD6, 0xFF, 0xA9, 0xFF, 0x5E,
+	0x00, 0xCB, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0x28, 0x00, 0x63, 0xFF,
+	0x66, 0x01, 0x7D, 0xFD, 0xF8, 0x03, 0xFB, 0xF9, 0x89, 0x0A, 0x1D,
+	0x48, 0xC5, 0xFD, 0x7A, 0xFF, 0x1D, 0x01, 0xF7, 0xFE, 0xB4, 0x00,
+	0xA9, 0xFF, 0x15, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x82, 0xFF, 0x18,
+	0x01, 0x27, 0xFE, 0xA9, 0x02, 0x94, 0xFC, 0x24, 0x04, 0xF5, 0x48,
+	0x39, 0x03, 0xF9, 0xFC, 0x74, 0x02, 0x42, 0xFE, 0x0B, 0x01, 0x87,
+	0xFF, 0x1E, 0x00, 0x16, 0x00, 0xA4, 0xFF, 0xC2, 0x00, 0xDB, 0xFE,
+	0x52, 0x01, 0x1B, 0xFF, 0x8D, 0xFE, 0x57, 0x48, 0x81, 0x09, 0x61,
+	0xFA, 0xC6, 0x03, 0x96, 0xFD, 0x5B, 0x01, 0x68, 0xFF, 0x26, 0x00,
+	0x00, 0x00, 0x0E, 0x00, 0xC6, 0xFF, 0x6B, 0x00, 0x8E, 0xFF, 0x06,
+	0x00, 0x6E, 0x01, 0xE4, 0xF9, 0x48, 0x46, 0x75, 0x10, 0xD7, 0xF7,
+	0xFC, 0x04, 0x00, 0xFD, 0x9E, 0x01, 0x4E, 0xFF, 0x2E, 0x00, 0xFF,
+	0xFF, 0x07, 0x00, 0xE5, 0xFF, 0x18, 0x00, 0x36, 0x00, 0xD9, 0xFE,
+	0x71, 0x03, 0x3F, 0xF6, 0xDB, 0x42, 0xE0, 0x17, 0x86, 0xF5, 0x00,
+	0x06, 0x8C, 0xFC, 0xCE, 0x01, 0x3C, 0xFF, 0x34, 0x00, 0xFE, 0xFF,
+	0x02, 0x00, 0x01, 0x00, 0xCF, 0xFF, 0xC9, 0x00, 0xDA, 0xFD, 0x0F,
+	0x05, 0xA5, 0xF3, 0x31, 0x3E, 0x8A, 0x1F, 0x97, 0xF3, 0xBD, 0x06,
+	0x44, 0xFC, 0xE5, 0x01, 0x36, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFF,
+	0xFF, 0x17, 0x00, 0x92, 0xFF, 0x41, 0x01, 0x11, 0xFD, 0x3B, 0x06,
+	0x14, 0xF2, 0x78, 0x38, 0x36, 0x27, 0x35, 0xF2, 0x20, 0x07, 0x33,
+	0xFC, 0xDF, 0x01, 0x3E, 0xFF, 0x34, 0x00, 0xFD, 0xFF, 0xFD, 0xFF,
+	0x28, 0x00, 0x64, 0xFF, 0x9A, 0x01, 0x88, 0xFC, 0xEE, 0x06, 0x7E,
+	0xF1, 0xE3, 0x31, 0x9F, 0x2E, 0x88, 0xF1, 0x19, 0x07, 0x5E, 0xFC,
+	0xB7, 0x01, 0x54, 0xFF, 0x2D, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x32,
+	0x00, 0x46, 0xFF, 0xD1, 0x01, 0x3F, 0xFC, 0x2B, 0x07, 0xCD, 0xF1,
+	0xAE, 0x2A, 0x86, 0x35, 0xB1, 0xF1, 0x9D, 0x06, 0xCA, 0xFC, 0x6E,
+	0x01, 0x7B, 0xFF, 0x20, 0x00, 0xFE, 0xFF, 0xFD, 0xFF, 0x36, 0x00,
+	0x38, 0xFF, 0xE6, 0x01, 0x35, 0xFC, 0xF7, 0x06, 0xE0, 0xF2, 0x18,
+	0x23, 0xAB, 0x3B, 0xCC, 0xF2, 0xA8, 0x05, 0x76, 0xFD, 0x04, 0x01,
+	0xB1, 0xFF, 0x0C, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x38,
+	0xFF, 0xDC, 0x01, 0x64, 0xFC, 0x62, 0x06, 0x93, 0xF4, 0x66, 0x1B,
+	0xD9, 0x40, 0xEA, 0xF4, 0x3E, 0x04, 0x5D, 0xFE, 0x7D, 0x00, 0xF5,
+	0xFF, 0xF3, 0xFF, 0x05, 0x00, 0xFF, 0xFF, 0x31, 0x00, 0x44, 0xFF,
+	0xB7, 0x01, 0xC5, 0xFC, 0x7C, 0x05, 0xBC, 0xF6, 0xD5, 0x13, 0xDC,
+	0x44, 0x14, 0xF8, 0x67, 0x02, 0x77, 0xFF, 0xDD, 0xFF, 0x44, 0x00,
+	0xD5, 0xFF, 0x0B, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x5B, 0xFF, 0x7C,
+	0x01, 0x4E, 0xFD, 0x5A, 0x04, 0x31, 0xF9, 0xA4, 0x0C, 0x90, 0x47,
+	0x47, 0xFC, 0x36, 0x00, 0xB6, 0x00, 0x2E, 0xFF, 0x99, 0x00, 0xB3,
+	0xFF, 0x12, 0x00, 0x00, 0x00, 0x22, 0x00, 0x78, 0xFF, 0x31, 0x01,
+	0xF1, 0xFD, 0x12, 0x03, 0xC7, 0xFB, 0x07, 0x06, 0xDB, 0x48, 0x73,
+	0x01, 0xC3, 0xFD, 0x0A, 0x02, 0x79, 0xFE, 0xF1, 0x00, 0x91, 0xFF,
+	0x1B, 0x00, 0x19, 0x00, 0x99, 0xFF, 0xDD, 0x00, 0xA3, 0xFE, 0xBB,
+	0x01, 0x58, 0xFE, 0x2D, 0x00, 0xAF, 0x48, 0x7E, 0x07, 0x2E, 0xFB,
+	0x60, 0x03, 0xC9, 0xFD, 0x43, 0x01, 0x71, 0xFF, 0x24, 0x00, 0x00,
+	0x00, 0x10, 0x00, 0xBB, 0xFF, 0x85, 0x00, 0x58, 0xFF, 0x6A, 0x00,
+	0xBE, 0x00, 0x38, 0xFB, 0x0F, 0x47, 0x42, 0x0E, 0x9B, 0xF8, 0xA1,
+	0x04, 0x2B, 0xFD, 0x8B, 0x01, 0x55, 0xFF, 0x2C, 0x00, 0xFF, 0xFF,
+	0x09, 0x00, 0xDC, 0xFF, 0x31, 0x00, 0x04, 0x00, 0x32, 0xFF, 0xDC,
+	0x02, 0x42, 0xF7, 0x0B, 0x44, 0x8E, 0x15, 0x34, 0xF6, 0xB7, 0x05,
+	0xAB, 0xFC, 0xC1, 0x01, 0x40, 0xFF, 0x33, 0x00, 0xFF, 0xFF, 0x04,
+	0x00, 0xF9, 0xFF, 0xE4, 0xFF, 0x9F, 0x00, 0x23, 0xFE, 0x9B, 0x04,
+	0x55, 0xF4, 0xC0, 0x3F, 0x2C, 0x1D, 0x22, 0xF4, 0x8C, 0x06, 0x55,
+	0xFC, 0xE1, 0x01, 0x37, 0xFF, 0x36, 0x00, 0xFE, 0xFF, 0x00, 0x00,
+	0x11, 0x00, 0xA3, 0xFF, 0x20, 0x01, 0x49, 0xFD, 0xEB, 0x05, 0x74,
+	0xF2, 0x54, 0x3A, 0xDD, 0x24, 0x91, 0xF2, 0x0C, 0x07, 0x32, 0xFC,
+	0xE4, 0x01, 0x3A, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFE, 0xFF, 0x23,
+	0x00, 0x71, 0xFF, 0x82, 0x01, 0xAB, 0xFC, 0xC4, 0x06, 0x93, 0xF1,
+	0xFD, 0x33, 0x62, 0x2C, 0xA8, 0xF1, 0x27, 0x07, 0x4A, 0xFC, 0xC7,
+	0x01, 0x4C, 0xFF, 0x30, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x2F, 0x00,
+	0x4E, 0xFF, 0xC3, 0x01, 0x4E, 0xFC, 0x24, 0x07, 0x9E, 0xF1, 0xF2,
+	0x2C, 0x78, 0x33, 0x8C, 0xF1, 0xD0, 0x06, 0xA2, 0xFC, 0x88, 0x01,
+	0x6D, 0xFF, 0x24, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x35, 0x00, 0x3B,
+	0xFF, 0xE3, 0x01, 0x31, 0xFC, 0x12, 0x07, 0x79, 0xF2, 0x73, 0x25,
+	0xDF, 0x39, 0x5A, 0xF2, 0x00, 0x06, 0x3A, 0xFD, 0x28, 0x01, 0x9F,
+	0xFF, 0x13, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0x36, 0x00, 0x36, 0xFF,
+	0xE2, 0x01, 0x50, 0xFC, 0x99, 0x06, 0xFE, 0xF3, 0xC3, 0x1D, 0x5E,
+	0x3F, 0x27, 0xF4, 0xB9, 0x04, 0x10, 0xFE, 0xA9, 0x00, 0xDF, 0xFF,
+	0xFB, 0xFF, 0x03, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x3F, 0xFF, 0xC5,
+	0x01, 0xA3, 0xFC, 0xCA, 0x05, 0x07, 0xF6, 0x22, 0x16, 0xC3, 0x43,
+	0xFE, 0xF6, 0x02, 0x03, 0x1B, 0xFF, 0x11, 0x00, 0x2B, 0x00, 0xDE,
+	0xFF, 0x09, 0x00, 0xFF, 0xFF, 0x2D, 0x00, 0x53, 0xFF, 0x90, 0x01,
+	0x20, 0xFD, 0xB8, 0x04, 0x6A, 0xF8, 0xCD, 0x0E, 0xE1, 0x46, 0xE1,
+	0xFA, 0xEB, 0x00, 0x51, 0x00, 0x65, 0xFF, 0x7F, 0x00, 0xBE, 0xFF,
+	0x10, 0x00, 0x00, 0x00, 0x24, 0x00, 0x6E, 0xFF, 0x49, 0x01, 0xBC,
+	0xFD, 0x7A, 0x03, 0xFA, 0xFA, 0xFD, 0x07, 0x9C, 0x48, 0xC3, 0xFF,
+	0x89, 0xFE, 0xA1, 0x01, 0xB1, 0xFE, 0xD6, 0x00, 0x9C, 0xFF, 0x18,
+	0x00, 0x1C, 0x00, 0x8F, 0xFF, 0xF7, 0x00, 0x6B, 0xFE, 0x25, 0x02,
+	0x91, 0xFD, 0xE3, 0x01, 0xE5, 0x48, 0x8D, 0x05, 0xFB, 0xFB, 0xF8,
+	0x02, 0xFE, 0xFD, 0x2B, 0x01, 0x7A, 0xFF, 0x21, 0x00, 0x00, 0x00,
+	0x13, 0x00, 0xB1, 0xFF, 0xA0, 0x00, 0x20, 0xFF, 0xD0, 0x00, 0x07,
+	0x00, 0xA4, 0xFC, 0xB6, 0x47, 0x1C, 0x0C, 0x63, 0xF9, 0x42, 0x04,
+	0x59, 0xFD, 0x76, 0x01, 0x5D, 0xFF, 0x2A, 0x00, 0x00, 0x00, 0x0B,
+	0x00, 0xD2, 0xFF, 0x4A, 0x00, 0xD0, 0xFF, 0x8E, 0xFF, 0x3F, 0x02,
+	0x5E, 0xF8, 0x1E, 0x45, 0x44, 0x13, 0xEA, 0xF6, 0x67, 0x05, 0xCF,
+	0xFC, 0xB3, 0x01, 0x46, 0xFF, 0x31, 0x00, 0xFF, 0xFF, 0x05, 0x00,
+	0xF0, 0xFF, 0xFB, 0xFF, 0x71, 0x00, 0x71, 0xFE, 0x1D, 0x04, 0x1F,
+	0xF5, 0x32, 0x41, 0xCE, 0x1A, 0xBA, 0xF4, 0x53, 0x06, 0x6A, 0xFC,
+	0xDA, 0x01, 0x38, 0xFF, 0x35, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0x0A,
+	0x00, 0xB6, 0xFF, 0xFB, 0x00, 0x85, 0xFD, 0x90, 0x05, 0xEC, 0xF2,
+	0x1C, 0x3C, 0x81, 0x22, 0xFC, 0xF2, 0xEF, 0x06, 0x36, 0xFC, 0xE6,
+	0x01, 0x37, 0xFF, 0x36, 0x00, 0xFD, 0xFF, 0xFE, 0xFF, 0x1E, 0x00,
+	0x7F, 0xFF, 0x67, 0x01, 0xD5, 0xFC, 0x8E, 0x06, 0xBE, 0xF1, 0x06,
+	0x36, 0x1A, 0x2A, 0xDC, 0xF1, 0x2A, 0x07, 0x3C, 0xFC, 0xD3, 0x01,
+	0x44, 0xFF, 0x32, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x2C, 0x00, 0x57,
+	0xFF, 0xB3, 0x01, 0x64, 0xFC, 0x13, 0x07, 0x83, 0xF1, 0x2C, 0x2F,
+	0x5A, 0x31, 0x7D, 0xF1, 0xF7, 0x06, 0x80, 0xFC, 0x9F, 0x01, 0x61,
+	0xFF, 0x29, 0x00, 0xFD, 0xFF, 0xFD, 0xFF, 0x34, 0x00, 0x3F, 0xFF,
+	0xDD, 0x01, 0x34, 0xFC, 0x23, 0x07, 0x21, 0xF2, 0xCB, 0x27, 0xFE,
+	0x37, 0x00, 0xF2, 0x4D, 0x06, 0x04, 0xFD, 0x49, 0x01, 0x8E, 0xFF,
+	0x19, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x36, 0x00, 0x36, 0xFF, 0xE6,
+	0x01, 0x41, 0xFC, 0xC8, 0x06, 0x76, 0xF3, 0x22, 0x20, 0xCA, 0x3D,
+	0x7D, 0xF3, 0x2A, 0x05, 0xC8, 0xFD, 0xD4, 0x00, 0xCA, 0xFF, 0x03,
+	0x00, 0x02, 0x00, 0xFE, 0xFF, 0x34, 0x00, 0x3B, 0xFF, 0xD1, 0x01,
+	0x84, 0xFC, 0x12, 0x06, 0x5C, 0xF5, 0x76, 0x18, 0x89, 0x42, 0x02,
+	0xF6, 0x94, 0x03, 0xC4, 0xFE, 0x42, 0x00, 0x12, 0x00, 0xE8, 0xFF,
+	0x07, 0x00, 0xFF, 0xFF, 0x2F, 0x00, 0x4C, 0xFF, 0xA2, 0x01, 0xF6,
+	0xFC, 0x12, 0x05, 0xA7, 0xF7, 0x03, 0x11, 0x10, 0x46, 0x93, 0xF9,
+	0x98, 0x01, 0xEE, 0xFF, 0x9B, 0xFF, 0x64, 0x00, 0xC8, 0xFF, 0x0E,
+	0x00, 0x00, 0x00, 0x27, 0x00, 0x65, 0xFF, 0x60, 0x01, 0x8A, 0xFD,
+	0xDF, 0x03, 0x2E, 0xFA, 0x04, 0x0A, 0x3A, 0x48, 0x28, 0xFE, 0x4B,
+	0xFF, 0x38, 0x01, 0xE9, 0xFE, 0xBB, 0x00, 0xA6, 0xFF, 0x16, 0x00,
+	0x00, 0x00, 0x1E, 0x00, 0x84, 0xFF, 0x11, 0x01, 0x34, 0xFE, 0x8F,
+	0x02, 0xC7, 0xFC, 0xAE, 0x03, 0xF7, 0x48, 0xAE, 0x03, 0xC7, 0xFC,
+	0x8F, 0x02, 0x34, 0xFE, 0x11, 0x01, 0x84, 0xFF, 0x1E, 0x00, 0x00,
+	0x00, 0xF4, 0xFF, 0x1A, 0x00, 0xFF, 0x00, 0x07, 0x03, 0x16, 0x06,
+	0x7C, 0x09, 0x2A, 0x0C, 0x2E, 0x0D, 0x2A, 0x0C, 0x7C, 0x09, 0x16,
+	0x06, 0x07, 0x03, 0xFF, 0x00, 0x1A, 0x00, 0xF4, 0xFF, 0xF2, 0xFF,
+	0xA0, 0xFF, 0x71, 0xFF, 0x71, 0x00, 0x86, 0x03, 0x73, 0x08, 0x88,
+	0x0D, 0x78, 0x10, 0xC9, 0x0F, 0xD5, 0x0B, 0x8B, 0x06, 0x28, 0x02,
+	0xDF, 0xFF, 0x6F, 0xFF, 0xC3, 0xFF, 0xFD, 0xFF, 0x00, 0x00, 0xDC,
+	0xFF, 0x80, 0xFF, 0x9A, 0xFF, 0x46, 0x01, 0x1E, 0x05, 0x5A, 0x0A,
+	0xED, 0x0E, 0xAA, 0x10, 0xAF, 0x0E, 0xFD, 0x09, 0xCB, 0x04, 0x18,
+	0x01, 0x8E, 0xFF, 0x85, 0xFF, 0xE1, 0xFF, 0xFC, 0xFF, 0xBD, 0xFF,
+	0x6D, 0xFF, 0xF6, 0xFF, 0x65, 0x02, 0xE5, 0x06, 0x2B, 0x0C, 0xF3,
+	0x0F, 0x60, 0x10, 0x3B, 0x0D, 0x16, 0x08, 0x3F, 0x03, 0x50, 0x00,
+	0x6E, 0xFF, 0xA7, 0xFF, 0xF5, 0xFF, 0xEF, 0xFF, 0x9A, 0xFF, 0x75,
+	0xFF, 0x91, 0x00, 0xC9, 0x03, 0xC8, 0x08, 0xCC, 0x0D, 0x89, 0x10,
+	0x9F, 0x0F, 0x85, 0x0B, 0x3B, 0x06, 0xF4, 0x01, 0xCD, 0xFF, 0x72,
+	0xFF, 0xC9, 0xFF, 0xFE, 0xFF, 0x00, 0x00, 0xD7, 0xFF, 0x7B, 0xFF,
+	0xA5, 0xFF, 0x73, 0x01, 0x6A, 0x05, 0xAD, 0x0A, 0x21, 0x0F, 0xA6,
+	0x10, 0x74, 0x0E, 0xA9, 0x09, 0x83, 0x04, 0xF0, 0x00, 0x85, 0xFF,
+	0x8B, 0xFF, 0xE5, 0xFF, 0xFA, 0xFF, 0xB7, 0xFF, 0x6C, 0xFF, 0x0C,
+	0x00, 0x9D, 0x02, 0x37, 0x07, 0x78, 0x0C, 0x15, 0x10, 0x47, 0x10,
+	0xF3, 0x0C, 0xC2, 0x07, 0x01, 0x03, 0x35, 0x00, 0x6D, 0xFF, 0xAD,
+	0xFF, 0xF7, 0xFF, 0xEB, 0xFF, 0x94, 0xFF, 0x7A, 0xFF, 0xB3, 0x00,
+	0x0D, 0x04, 0x1C, 0x09, 0x0D, 0x0E, 0x97, 0x10, 0x73, 0x0F, 0x35,
+	0x0B, 0xEB, 0x05, 0xC1, 0x01, 0xBD, 0xFF, 0x75, 0xFF, 0xCE, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xD2, 0xFF, 0x77, 0xFF, 0xB3, 0xFF, 0xA1,
+	0x01, 0xB7, 0x05, 0xFF, 0x0A, 0x53, 0x0F, 0x9E, 0x10, 0x37, 0x0E,
+	0x55, 0x09, 0x3B, 0x04, 0xCB, 0x00, 0x7E, 0xFF, 0x90, 0xFF, 0xE9,
+	0xFF, 0xF8, 0xFF, 0xB1, 0xFF, 0x6C, 0xFF, 0x24, 0x00, 0xD8, 0x02,
+	0x8A, 0x07, 0xC2, 0x0C, 0x34, 0x10, 0x2A, 0x10, 0xAA, 0x0C, 0x6F,
+	0x07, 0xC4, 0x02, 0x1C, 0x00, 0x6C, 0xFF, 0xB3, 0xFF, 0xF9, 0xFF,
+	0xE8, 0xFF, 0x8E, 0xFF, 0x80, 0xFF, 0xD7, 0x00, 0x53, 0x04, 0x71,
+	0x09, 0x4C, 0x0E, 0xA1, 0x10, 0x43, 0x0F, 0xE3, 0x0A, 0x9D, 0x05,
+	0x91, 0x01, 0xAE, 0xFF, 0x79, 0xFF, 0xD4, 0xFF, 0x00, 0x00, 0xFF,
+	0xFF, 0xCD, 0xFF, 0x74, 0xFF, 0xC2, 0xFF, 0xD2, 0x01, 0x06, 0x06,
+	0x50, 0x0B, 0x82, 0x0F, 0x93, 0x10, 0xF8, 0x0D, 0x00, 0x09, 0xF6,
+	0x03, 0xA7, 0x00, 0x78, 0xFF, 0x96, 0xFF, 0xEC, 0xFF, 0xF6, 0xFF,
+	0xAB, 0xFF, 0x6D, 0xFF, 0x3E, 0x00, 0x15, 0x03, 0xDE, 0x07, 0x0B,
+	0x0D, 0x50, 0x10, 0x0A, 0x10, 0x5E, 0x0C, 0x1C, 0x07, 0x8A, 0x02,
+	0x04, 0x00, 0x6C, 0xFF, 0xB9, 0xFF, 0xFB, 0xFF, 0xE4, 0xFF, 0x89,
+	0xFF, 0x88, 0xFF, 0xFD, 0x00, 0x9B, 0x04, 0xC5, 0x09, 0x88, 0x0E,
+	0xA8, 0x10, 0x10, 0x0F, 0x91, 0x0A, 0x50, 0x05, 0x64, 0x01, 0xA1,
+	0xFF, 0x7D, 0xFF, 0xD9, 0xFF, 0x00, 0x00, 0xFE, 0xFF, 0xC7, 0xFF,
+	0x71, 0xFF, 0xD3, 0xFF, 0x05, 0x02, 0x55, 0x06, 0xA0, 0x0B, 0xAD,
+	0x0F, 0x84, 0x10, 0xB6, 0x0D, 0xAC, 0x08, 0xB3, 0x03, 0x86, 0x00,
+	0x74, 0xFF, 0x9C, 0xFF, 0xF0, 0xFF, 0xF4, 0xFF, 0xA5, 0xFF, 0x6F,
+	0xFF, 0x5A, 0x00, 0x54, 0x03, 0x32, 0x08, 0x52, 0x0D, 0x68, 0x10,
+	0xE6, 0x0F, 0x11, 0x0C, 0xCA, 0x06, 0x52, 0x02, 0xEF, 0xFF, 0x6E,
+	0xFF, 0xBF, 0xFF, 0xFC, 0xFF, 0xDF, 0xFF, 0x84, 0xFF, 0x91, 0xFF,
+	0x25, 0x01, 0xE4, 0x04, 0x19, 0x0A, 0xC2, 0x0E, 0xAA, 0x10, 0xDA,
+	0x0E, 0x3E, 0x0A, 0x05, 0x05, 0x38, 0x01, 0x96, 0xFF, 0x81, 0xFF,
+	0xDD, 0xFF, 0x00, 0x00, 0xFD, 0xFF, 0xC1, 0xFF, 0x6E, 0xFF, 0xE6,
+	0xFF, 0x3A, 0x02, 0xA6, 0x06, 0xEF, 0x0B, 0xD6, 0x0F, 0x71, 0x10,
+	0x71, 0x0D, 0x57, 0x08, 0x71, 0x03, 0x67, 0x00, 0x70, 0xFF, 0xA2,
+	0xFF, 0xF3, 0xFF, 0xF1, 0xFF, 0x9F, 0xFF, 0x72, 0xFF, 0x78, 0x00,
+	0x95, 0x03, 0x86, 0x08, 0x98, 0x0D, 0x7C, 0x10, 0xC0, 0x0F, 0xC3,
+	0x0B, 0x79, 0x06, 0x1C, 0x02, 0xDB, 0xFF, 0x70, 0xFF, 0xC5, 0xFF,
+	0xFE, 0xFF, 0x00, 0x00, 0xDB, 0xFF, 0x7F, 0xFF, 0x9C, 0xFF, 0x50,
+	0x01, 0x2F, 0x05, 0x6C, 0x0A, 0xF9, 0x0E, 0xA9, 0x10, 0xA2, 0x0E,
+	0xEA, 0x09, 0xBB, 0x04, 0x0F, 0x01, 0x8C, 0xFF, 0x87, 0xFF, 0xE2,
+	0xFF, 0xFC, 0xFF, 0xBC, 0xFF, 0x6D, 0xFF, 0xFA, 0xFF, 0x71, 0x02,
+	0xF7, 0x06, 0x3C, 0x0C, 0xFB, 0x0F, 0x5B, 0x10, 0x2B, 0x0D, 0x03,
+	0x08, 0x31, 0x03, 0x4A, 0x00, 0x6E, 0xFF, 0xA8, 0xFF, 0xF5, 0xFF,
+	0xEE, 0xFF, 0x99, 0xFF, 0x76, 0xFF, 0x98, 0x00, 0xD8, 0x03, 0xDB,
+	0x08, 0xDB, 0x0D, 0x8D, 0x10, 0x96, 0x0F, 0x73, 0x0B, 0x29, 0x06,
+	0xE8, 0x01, 0xC9, 0xFF, 0x72, 0xFF, 0xCA, 0xFF, 0xFE, 0xFF, 0x00,
+	0x00, 0xD6, 0xFF, 0x7A, 0xFF, 0xA8, 0xFF, 0x7D, 0x01, 0x7B, 0x05,
+	0xBF, 0x0A, 0x2D, 0x0F, 0xA5, 0x10, 0x67, 0x0E, 0x96, 0x09, 0x73,
+	0x04, 0xE7, 0x00, 0x84, 0xFF, 0x8C, 0xFF, 0xE6, 0xFF, 0xFA, 0xFF,
+	0xB6, 0xFF, 0x6C, 0xFF, 0x11, 0x00, 0xAA, 0x02, 0x4A, 0x07, 0x88,
+	0x0C, 0x1C, 0x10, 0x41, 0x10, 0xE3, 0x0C, 0xAF, 0x07, 0xF3, 0x02,
+	0x2F, 0x00, 0x6C, 0xFF, 0xAE, 0xFF, 0xF7, 0xFF, 0xEA, 0xFF, 0x93,
+	0xFF, 0x7B, 0xFF, 0xBB, 0x00, 0x1C, 0x04, 0x2F, 0x09, 0x1B, 0x0E,
+	0x9A, 0x10, 0x68, 0x0F, 0x23, 0x0B, 0xDA, 0x05, 0xB7, 0x01, 0xB9,
+	0xFF, 0x76, 0xFF, 0xD0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xD1, 0xFF,
+	0x76, 0xFF, 0xB6, 0xFF, 0xAC, 0x01, 0xC8, 0x05, 0x11, 0x0B, 0x5E,
+	0x0F, 0x9C, 0x10, 0x29, 0x0E, 0x42, 0x09, 0x2C, 0x04, 0xC2, 0x00,
+	0x7D, 0xFF, 0x92, 0xFF, 0xEA, 0xFF, 0xF8, 0xFF, 0xB0, 0xFF, 0x6C,
+	0xFF, 0x29, 0x00, 0xE6, 0x02, 0x9D, 0x07, 0xD3, 0x0C, 0x3B, 0x10,
+	0x23, 0x10, 0x99, 0x0C, 0x5C, 0x07, 0xB7, 0x02, 0x16, 0x00, 0x6C,
+	0xFF, 0xB4, 0xFF, 0xF9, 0xFF, 0xE7, 0xFF, 0x8D, 0xFF, 0x82, 0xFF,
+	0xDF, 0x00, 0x63, 0x04, 0x84, 0x09, 0x59, 0x0E, 0xA3, 0x10, 0x38,
+	0x0F, 0xD1, 0x0A, 0x8C, 0x05, 0x87, 0x01, 0xAB, 0xFF, 0x79, 0xFF,
+	0xD5, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xCB, 0xFF, 0x73, 0xFF, 0xC6,
+	0xFF, 0xDD, 0x01, 0x17, 0x06, 0x62, 0x0B, 0x8C, 0x0F, 0x90, 0x10,
+	0xE9, 0x0D, 0xED, 0x08, 0xE7, 0x03, 0xA0, 0x00, 0x77, 0xFF, 0x97,
+	0xFF, 0xED, 0xFF, 0xF6, 0xFF, 0xA9, 0xFF, 0x6D, 0xFF, 0x44, 0x00,
+	0x23, 0x03, 0xF1, 0x07, 0x1B, 0x0D, 0x55, 0x10, 0x02, 0x10, 0x4D,
+	0x0C, 0x0A, 0x07, 0x7E, 0x02, 0xFF, 0xFF, 0x6D, 0xFF, 0xBA, 0xFF,
+	0xFB, 0xFF, 0xE3, 0xFF, 0x88, 0xFF, 0x8A, 0xFF, 0x06, 0x01, 0xAB,
+	0x04, 0xD8, 0x09, 0x95, 0x0E, 0xA9, 0x10, 0x05, 0x0F, 0x7F, 0x0A,
+	0x40, 0x05, 0x5A, 0x01, 0x9F, 0xFF, 0x7E, 0xFF, 0xDA, 0xFF, 0x00,
+	0x00, 0xFE, 0xFF, 0xC6, 0xFF, 0x70, 0xFF, 0xD7, 0xFF, 0x10, 0x02,
+	0x67, 0x06, 0xB1, 0x0B, 0xB7, 0x0F, 0x80, 0x10, 0xA7, 0x0D, 0x99,
+	0x08, 0xA4, 0x03, 0x7F, 0x00, 0x73, 0xFF, 0x9D, 0xFF, 0xF0, 0xFF,
+	0xF3, 0xFF, 0xA3, 0xFF, 0x70, 0xFF, 0x60, 0x00, 0x62, 0x03, 0x45,
+	0x08, 0x62, 0x0D, 0x6C, 0x10, 0xDE, 0x0F, 0x00, 0x0C, 0xB8, 0x06,
+	0x46, 0x02, 0xEA, 0xFF, 0x6E, 0xFF, 0xC0, 0xFF, 0xFD, 0xFF, 0x00,
+	0x00, 0xDE, 0xFF, 0x83, 0xFF, 0x94, 0xFF, 0x2F, 0x01, 0xF4, 0x04,
+	0x2B, 0x0A, 0xCE, 0x0E, 0xAA, 0x10, 0xCE, 0x0E, 0x2B, 0x0A, 0xF4,
+	0x04, 0x2F, 0x01, 0x94, 0xFF, 0x83, 0xFF, 0xDE, 0xFF, 0xFD, 0xFF,
+	0xC0, 0xFF, 0x6E, 0xFF, 0xEA, 0xFF, 0x46, 0x02, 0xB8, 0x06, 0x00,
+	0x0C, 0xDE, 0x0F, 0x6C, 0x10, 0x62, 0x0D, 0x45, 0x08, 0x62, 0x03,
+	0x60, 0x00, 0x70, 0xFF, 0xA3, 0xFF, 0xF3, 0xFF, 0xF0, 0xFF, 0x9D,
+	0xFF, 0x73, 0xFF, 0x7F, 0x00, 0xA4, 0x03, 0x99, 0x08, 0xA7, 0x0D,
+	0x80, 0x10, 0xB7, 0x0F, 0xB1, 0x0B, 0x67, 0x06, 0x10, 0x02, 0xD7,
+	0xFF, 0x70, 0xFF, 0xC6, 0xFF, 0xFE, 0xFF, 0x00, 0x00, 0xDA, 0xFF,
+	0x7E, 0xFF, 0x9F, 0xFF, 0x5A, 0x01, 0x40, 0x05, 0x7F, 0x0A, 0x05,
+	0x0F, 0xA9, 0x10, 0x95, 0x0E, 0xD8, 0x09, 0xAB, 0x04, 0x06, 0x01,
+	0x8A, 0xFF, 0x88, 0xFF, 0xE3, 0xFF, 0xFB, 0xFF, 0xBA, 0xFF, 0x6D,
+	0xFF, 0xFF, 0xFF, 0x7E, 0x02, 0x0A, 0x07, 0x4D, 0x0C, 0x02, 0x10,
+	0x55, 0x10, 0x1B, 0x0D, 0xF1, 0x07, 0x23, 0x03, 0x44, 0x00, 0x6D,
+	0xFF, 0xA9, 0xFF, 0xF6, 0xFF, 0xED, 0xFF, 0x97, 0xFF, 0x77, 0xFF,
+	0xA0, 0x00, 0xE7, 0x03, 0xED, 0x08, 0xE9, 0x0D, 0x90, 0x10, 0x8C,
+	0x0F, 0x62, 0x0B, 0x17, 0x06, 0xDD, 0x01, 0xC6, 0xFF, 0x73, 0xFF,
+	0xCB, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xD5, 0xFF, 0x79, 0xFF, 0xAB,
+	0xFF, 0x87, 0x01, 0x8C, 0x05, 0xD1, 0x0A, 0x38, 0x0F, 0xA3, 0x10,
+	0x59, 0x0E, 0x84, 0x09, 0x63, 0x04, 0xDF, 0x00, 0x82, 0xFF, 0x8D,
+	0xFF, 0xE7, 0xFF, 0xF9, 0xFF, 0xB4, 0xFF, 0x6C, 0xFF, 0x16, 0x00,
+	0xB7, 0x02, 0x5C, 0x07, 0x99, 0x0C, 0x23, 0x10, 0x3B, 0x10, 0xD3,
+	0x0C, 0x9D, 0x07, 0xE6, 0x02, 0x29, 0x00, 0x6C, 0xFF, 0xB0, 0xFF,
+	0xF8, 0xFF, 0xEA, 0xFF, 0x92, 0xFF, 0x7D, 0xFF, 0xC2, 0x00, 0x2C,
+	0x04, 0x42, 0x09, 0x29, 0x0E, 0x9C, 0x10, 0x5E, 0x0F, 0x11, 0x0B,
+	0xC8, 0x05, 0xAC, 0x01, 0xB6, 0xFF, 0x76, 0xFF, 0xD1, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xD0, 0xFF, 0x76, 0xFF, 0xB9, 0xFF, 0xB7, 0x01,
+	0xDA, 0x05, 0x23, 0x0B, 0x68, 0x0F, 0x9A, 0x10, 0x1B, 0x0E, 0x2F,
+	0x09, 0x1C, 0x04, 0xBB, 0x00, 0x7B, 0xFF, 0x93, 0xFF, 0xEA, 0xFF,
+	0xF7, 0xFF, 0xAE, 0xFF, 0x6C, 0xFF, 0x2F, 0x00, 0xF3, 0x02, 0xAF,
+	0x07, 0xE3, 0x0C, 0x41, 0x10, 0x1C, 0x10, 0x88, 0x0C, 0x4A, 0x07,
+	0xAA, 0x02, 0x11, 0x00, 0x6C, 0xFF, 0xB6, 0xFF, 0xFA, 0xFF, 0xE6,
+	0xFF, 0x8C, 0xFF, 0x84, 0xFF, 0xE7, 0x00, 0x73, 0x04, 0x96, 0x09,
+	0x67, 0x0E, 0xA5, 0x10, 0x2D, 0x0F, 0xBF, 0x0A, 0x7B, 0x05, 0x7D,
+	0x01, 0xA8, 0xFF, 0x7A, 0xFF, 0xD6, 0xFF, 0x00, 0x00, 0xFE, 0xFF,
+	0xCA, 0xFF, 0x72, 0xFF, 0xC9, 0xFF, 0xE8, 0x01, 0x29, 0x06, 0x73,
+	0x0B, 0x96, 0x0F, 0x8D, 0x10, 0xDB, 0x0D, 0xDB, 0x08, 0xD8, 0x03,
+	0x98, 0x00, 0x76, 0xFF, 0x99, 0xFF, 0xEE, 0xFF, 0xF5, 0xFF, 0xA8,
+	0xFF, 0x6E, 0xFF, 0x4A, 0x00, 0x31, 0x03, 0x03, 0x08, 0x2B, 0x0D,
+	0x5B, 0x10, 0xFB, 0x0F, 0x3C, 0x0C, 0xF7, 0x06, 0x71, 0x02, 0xFA,
+	0xFF, 0x6D, 0xFF, 0xBC, 0xFF, 0xFC, 0xFF, 0xE2, 0xFF, 0x87, 0xFF,
+	0x8C, 0xFF, 0x0F, 0x01, 0xBB, 0x04, 0xEA, 0x09, 0xA2, 0x0E, 0xA9,
+	0x10, 0xF9, 0x0E, 0x6C, 0x0A, 0x2F, 0x05, 0x50, 0x01, 0x9C, 0xFF,
+	0x7F, 0xFF, 0xDB, 0xFF, 0x00, 0x00, 0xFE, 0xFF, 0xC5, 0xFF, 0x70,
+	0xFF, 0xDB, 0xFF, 0x1C, 0x02, 0x79, 0x06, 0xC3, 0x0B, 0xC0, 0x0F,
+	0x7C, 0x10, 0x98, 0x0D, 0x86, 0x08, 0x95, 0x03, 0x78, 0x00, 0x72,
+	0xFF, 0x9F, 0xFF, 0xF1, 0xFF, 0xF3, 0xFF, 0xA2, 0xFF, 0x70, 0xFF,
+	0x67, 0x00, 0x71, 0x03, 0x57, 0x08, 0x71, 0x0D, 0x71, 0x10, 0xD6,
+	0x0F, 0xEF, 0x0B, 0xA6, 0x06, 0x3A, 0x02, 0xE6, 0xFF, 0x6E, 0xFF,
+	0xC1, 0xFF, 0xFD, 0xFF, 0x00, 0x00, 0xDD, 0xFF, 0x81, 0xFF, 0x96,
+	0xFF, 0x38, 0x01, 0x05, 0x05, 0x3E, 0x0A, 0xDA, 0x0E, 0xAA, 0x10,
+	0xC2, 0x0E, 0x19, 0x0A, 0xE4, 0x04, 0x25, 0x01, 0x91, 0xFF, 0x84,
+	0xFF, 0xDF, 0xFF, 0xFC, 0xFF, 0xBF, 0xFF, 0x6E, 0xFF, 0xEF, 0xFF,
+	0x52, 0x02, 0xCA, 0x06, 0x11, 0x0C, 0xE6, 0x0F, 0x68, 0x10, 0x52,
+	0x0D, 0x32, 0x08, 0x54, 0x03, 0x5A, 0x00, 0x6F, 0xFF, 0xA5, 0xFF,
+	0xF4, 0xFF, 0xF0, 0xFF, 0x9C, 0xFF, 0x74, 0xFF, 0x86, 0x00, 0xB3,
+	0x03, 0xAC, 0x08, 0xB6, 0x0D, 0x84, 0x10, 0xAD, 0x0F, 0xA0, 0x0B,
+	0x55, 0x06, 0x05, 0x02, 0xD3, 0xFF, 0x71, 0xFF, 0xC7, 0xFF, 0xFE,
+	0xFF, 0x00, 0x00, 0xD9, 0xFF, 0x7D, 0xFF, 0xA1, 0xFF, 0x64, 0x01,
+	0x50, 0x05, 0x91, 0x0A, 0x10, 0x0F, 0xA8, 0x10, 0x88, 0x0E, 0xC5,
+	0x09, 0x9B, 0x04, 0xFD, 0x00, 0x88, 0xFF, 0x89, 0xFF, 0xE4, 0xFF,
+	0xFB, 0xFF, 0xB9, 0xFF, 0x6C, 0xFF, 0x04, 0x00, 0x8A, 0x02, 0x1C,
+	0x07, 0x5E, 0x0C, 0x0A, 0x10, 0x50, 0x10, 0x0B, 0x0D, 0xDE, 0x07,
+	0x15, 0x03, 0x3E, 0x00, 0x6D, 0xFF, 0xAB, 0xFF, 0xF6, 0xFF, 0xEC,
+	0xFF, 0x96, 0xFF, 0x78, 0xFF, 0xA7, 0x00, 0xF6, 0x03, 0x00, 0x09,
+	0xF8, 0x0D, 0x93, 0x10, 0x82, 0x0F, 0x50, 0x0B, 0x06, 0x06, 0xD2,
+	0x01, 0xC2, 0xFF, 0x74, 0xFF, 0xCD, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
+	0xD4, 0xFF, 0x79, 0xFF, 0xAE, 0xFF, 0x91, 0x01, 0x9D, 0x05, 0xE3,
+	0x0A, 0x43, 0x0F, 0xA1, 0x10, 0x4C, 0x0E, 0x71, 0x09, 0x53, 0x04,
+	0xD7, 0x00, 0x80, 0xFF, 0x8E, 0xFF, 0xE8, 0xFF, 0xF9, 0xFF, 0xB3,
+	0xFF, 0x6C, 0xFF, 0x1C, 0x00, 0xC4, 0x02, 0x6F, 0x07, 0xAA, 0x0C,
+	0x2A, 0x10, 0x34, 0x10, 0xC2, 0x0C, 0x8A, 0x07, 0xD8, 0x02, 0x24,
+	0x00, 0x6C, 0xFF, 0xB1, 0xFF, 0xF8, 0xFF, 0xE9, 0xFF, 0x90, 0xFF,
+	0x7E, 0xFF, 0xCB, 0x00, 0x3B, 0x04, 0x55, 0x09, 0x37, 0x0E, 0x9E,
+	0x10, 0x53, 0x0F, 0xFF, 0x0A, 0xB7, 0x05, 0xA1, 0x01, 0xB3, 0xFF,
+	0x77, 0xFF, 0xD2, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xCE, 0xFF, 0x75,
+	0xFF, 0xBD, 0xFF, 0xC1, 0x01, 0xEB, 0x05, 0x35, 0x0B, 0x73, 0x0F,
+	0x97, 0x10, 0x0D, 0x0E, 0x1C, 0x09, 0x0D, 0x04, 0xB3, 0x00, 0x7A,
+	0xFF, 0x94, 0xFF, 0xEB, 0xFF, 0xF7, 0xFF, 0xAD, 0xFF, 0x6D, 0xFF,
+	0x35, 0x00, 0x01, 0x03, 0xC2, 0x07, 0xF3, 0x0C, 0x47, 0x10, 0x15,
+	0x10, 0x78, 0x0C, 0x37, 0x07, 0x9D, 0x02, 0x0C, 0x00, 0x6C, 0xFF,
+	0xB7, 0xFF, 0xFA, 0xFF, 0xE5, 0xFF, 0x8B, 0xFF, 0x85, 0xFF, 0xF0,
+	0x00, 0x83, 0x04, 0xA9, 0x09, 0x74, 0x0E, 0xA6, 0x10, 0x21, 0x0F,
+	0xAD, 0x0A, 0x6A, 0x05, 0x73, 0x01, 0xA5, 0xFF, 0x7B, 0xFF, 0xD7,
+	0xFF, 0x00, 0x00, 0xFE, 0xFF, 0xC9, 0xFF, 0x72, 0xFF, 0xCD, 0xFF,
+	0xF4, 0x01, 0x3B, 0x06, 0x85, 0x0B, 0x9F, 0x0F, 0x89, 0x10, 0xCC,
+	0x0D, 0xC8, 0x08, 0xC9, 0x03, 0x91, 0x00, 0x75, 0xFF, 0x9A, 0xFF,
+	0xEF, 0xFF, 0xF5, 0xFF, 0xA7, 0xFF, 0x6E, 0xFF, 0x50, 0x00, 0x3F,
+	0x03, 0x16, 0x08, 0x3B, 0x0D, 0x60, 0x10, 0xF3, 0x0F, 0x2B, 0x0C,
+	0xE5, 0x06, 0x65, 0x02, 0xF6, 0xFF, 0x6D, 0xFF, 0xBD, 0xFF, 0xFC,
+	0xFF, 0xE1, 0xFF, 0x85, 0xFF, 0x8E, 0xFF, 0x18, 0x01, 0xCB, 0x04,
+	0xFD, 0x09, 0xAF, 0x0E, 0xAA, 0x10, 0xED, 0x0E, 0x5A, 0x0A, 0x1E,
+	0x05, 0x46, 0x01, 0x9A, 0xFF, 0x80, 0xFF, 0xDC, 0xFF, 0x00, 0x00,
+	0xFD, 0xFF, 0xC3, 0xFF, 0x6F, 0xFF, 0xDF, 0xFF, 0x28, 0x02, 0x8B,
+	0x06, 0xD5, 0x0B, 0xC9, 0x0F, 0x78, 0x10, 0x88, 0x0D, 0x73, 0x08,
+	0x86, 0x03, 0x71, 0x00, 0x71, 0xFF, 0xA0, 0xFF, 0xF2, 0xFF, 0xF2,
+	0xFF, 0xA1, 0xFF, 0x71, 0xFF, 0x6E, 0x00, 0x7F, 0x03, 0x6A, 0x08,
+	0x81, 0x0D, 0x76, 0x10, 0xCD, 0x0F, 0xDD, 0x0B, 0x94, 0x06, 0x2E,
+	0x02, 0xE1, 0xFF, 0x6F, 0xFF, 0xC3, 0xFF, 0xFD, 0xFF, 0x00, 0x00,
+	0xDC, 0xFF, 0x80, 0xFF, 0x98, 0xFF, 0x42, 0x01, 0x16, 0x05, 0x50,
+	0x0A, 0xE7, 0x0E, 0xAA, 0x10, 0xB5, 0x0E, 0x06, 0x0A, 0xD3, 0x04,
+	0x1C, 0x01, 0x8F, 0xFF, 0x85, 0xFF, 0xE0, 0xFF, 0xFC, 0xFF, 0xBE,
+	0xFF, 0x6D, 0xFF, 0xF3, 0xFF, 0x5E, 0x02, 0xDC, 0x06, 0x23, 0x0C,
+	0xEF, 0x0F, 0x63, 0x10, 0x43, 0x0D, 0x1F, 0x08, 0x46, 0x03, 0x53,
+	0x00, 0x6E, 0xFF, 0xA6, 0xFF, 0xF4, 0xFF, 0xEF, 0xFF, 0x9B, 0xFF,
+	0x75, 0xFF, 0x8D, 0x00, 0xC1, 0x03, 0xBE, 0x08, 0xC4, 0x0D, 0x88,
+	0x10, 0xA4, 0x0F, 0x8E, 0x0B, 0x43, 0x06, 0xF9, 0x01, 0xCF, 0xFF,
+	0x71, 0xFF, 0xC8, 0xFF, 0xFE, 0xFF, 0x00, 0x00, 0xD8, 0xFF, 0x7C,
+	0xFF, 0xA4, 0xFF, 0x6E, 0x01, 0x61, 0x05, 0xA3, 0x0A, 0x1C, 0x0F,
+	0xA7, 0x10, 0x7B, 0x0E, 0xB2, 0x09, 0x8B, 0x04, 0xF4, 0x00, 0x86,
+	0xFF, 0x8A, 0xFF, 0xE4, 0xFF, 0xFA, 0xFF, 0xB8, 0xFF, 0x6C, 0xFF,
+	0x09, 0x00, 0x97, 0x02, 0x2E, 0x07, 0x6F, 0x0C, 0x11, 0x10, 0x4A,
+	0x10, 0xFB, 0x0C, 0xCB, 0x07, 0x07, 0x03, 0x38, 0x00, 0x6D, 0xFF,
+	0xAC, 0xFF, 0xF7, 0xFF, 0xEC, 0xFF, 0x95, 0xFF, 0x79, 0xFF, 0xAF,
+	0x00, 0x05, 0x04, 0x13, 0x09, 0x06, 0x0E, 0x96, 0x10, 0x78, 0x0F,
+	0x3E, 0x0B, 0xF4, 0x05, 0xC7, 0x01, 0xBF, 0xFF, 0x74, 0xFF, 0xCE,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xD2, 0xFF, 0x78, 0xFF, 0xB1, 0xFF,
+	0x9C, 0x01, 0xAE, 0x05, 0xF6, 0x0A, 0x4E, 0x0F, 0x9F, 0x10, 0x3E,
+	0x0E, 0x5E, 0x09, 0x43, 0x04, 0xCF, 0x00, 0x7F, 0xFF, 0x90, 0xFF,
+	0xE8, 0xFF, 0xF9, 0xFF, 0xB2, 0xFF, 0x6C, 0xFF, 0x21, 0x00, 0xD2,
+	0x02, 0x81, 0x07, 0xBA, 0x0C, 0x31, 0x10, 0x2E, 0x10, 0xB2, 0x0C,
+	0x78, 0x07, 0xCB, 0x02, 0x1E, 0x00, 0x6C, 0xFF, 0xB2, 0xFF, 0xF9,
+	0xFF, 0xE8, 0xFF, 0x8F, 0xFF, 0x80, 0xFF, 0xD3, 0x00, 0x4B, 0x04,
+	0x67, 0x09, 0x45, 0x0E, 0xA0, 0x10, 0x48, 0x0F, 0xEC, 0x0A, 0xA6,
+	0x05, 0x97, 0x01, 0xB0, 0xFF, 0x78, 0xFF, 0xD3, 0xFF, 0x00, 0x00,
+	0xFF, 0xFF, 0xCD, 0xFF, 0x74, 0xFF, 0xC0, 0xFF, 0xCC, 0x01, 0xFD,
+	0x05, 0x47, 0x0B, 0x7D, 0x0F, 0x94, 0x10, 0xFF, 0x0D, 0x0A, 0x09,
+	0xFE, 0x03, 0xAB, 0x00, 0x79, 0xFF, 0x95, 0xFF, 0xEC, 0xFF, 0xF7,
+	0xFF, 0xAC, 0xFF, 0x6D, 0xFF, 0x3B, 0x00, 0x0E, 0x03, 0xD5, 0x07,
+	0x03, 0x0D, 0x4D, 0x10, 0x0E, 0x10, 0x67, 0x0C, 0x25, 0x07, 0x91,
+	0x02, 0x07, 0x00, 0x6C, 0xFF, 0xB8, 0xFF, 0xFB, 0xFF, 0xE4, 0xFF,
+	0x89, 0xFF, 0x87, 0xFF, 0xF9, 0x00, 0x93, 0x04, 0xBC, 0x09, 0x82,
+	0x0E, 0xA7, 0x10, 0x16, 0x0F, 0x9A, 0x0A, 0x59, 0x05, 0x69, 0x01,
+	0xA3, 0xFF, 0x7C, 0xFF, 0xD8, 0xFF, 0x00, 0x00, 0xFE, 0xFF, 0xC8,
+	0xFF, 0x71, 0xFF, 0xD1, 0xFF, 0xFF, 0x01, 0x4C, 0x06, 0x97, 0x0B,
+	0xA9, 0x0F, 0x86, 0x10, 0xBD, 0x0D, 0xB5, 0x08, 0xBA, 0x03, 0x8A,
+	0x00, 0x74, 0xFF, 0x9B, 0xFF, 0xEF, 0xFF, 0xF4, 0xFF, 0xA5, 0xFF,
+	0x6F, 0xFF, 0x57, 0x00, 0x4D, 0x03, 0x29, 0x08, 0x4B, 0x0D, 0x65,
+	0x10, 0xEB, 0x0F, 0x1A, 0x0C, 0xD3, 0x06, 0x58, 0x02, 0xF1, 0xFF,
+	0x6D, 0xFF, 0xBE, 0xFF, 0xFC, 0xFF, 0xE0, 0xFF, 0x84, 0xFF, 0x90,
+	0xFF, 0x21, 0x01, 0xDC, 0x04, 0x10, 0x0A, 0xBB, 0x0E, 0xAA, 0x10,
+	0xE1, 0x0E, 0x47, 0x0A, 0x0D, 0x05, 0x3D, 0x01, 0x97, 0xFF, 0x81,
+	0xFF, 0xDD, 0xFF, 0x00, 0x00, 0xFD, 0xFF, 0xC2, 0xFF, 0x6F, 0xFF,
+	0xE4, 0xFF, 0x34, 0x02, 0x9D, 0x06, 0xE6, 0x0B, 0xD1, 0x0F, 0x73,
+	0x10, 0x79, 0x0D, 0x61, 0x08, 0x78, 0x03, 0x6A, 0x00, 0x70, 0xFF,
+	0xA1, 0xFF, 0xF2, 0xFF, 0xF1, 0xFF, 0x9F, 0xFF, 0x72, 0xFF, 0x74,
+	0x00, 0x8E, 0x03, 0x7D, 0x08, 0x90, 0x0D, 0x7A, 0x10, 0xC4, 0x0F,
+	0xCC, 0x0B, 0x82, 0x06, 0x22, 0x02, 0xDD, 0xFF, 0x6F, 0xFF, 0xC4,
+	0xFF, 0xFD, 0xFF, 0x00, 0x00, 0xDB, 0xFF, 0x7F, 0xFF, 0x9B, 0xFF,
+	0x4B, 0x01, 0x26, 0x05, 0x63, 0x0A, 0xF3, 0x0E, 0xAA, 0x10, 0xA8,
+	0x0E, 0xF4, 0x09, 0xC3, 0x04, 0x13, 0x01, 0x8D, 0xFF, 0x86, 0xFF,
+	0xE1, 0xFF, 0xFC, 0xFF, 0xBC, 0xFF, 0x6D, 0xFF, 0xF8, 0xFF, 0x6B,
+	0x02, 0xEE, 0x06, 0x34, 0x0C, 0xF7, 0x0F, 0x5D, 0x10, 0x33, 0x0D,
+	0x0D, 0x08, 0x38, 0x03, 0x4D, 0x00, 0x6E, 0xFF, 0xA7, 0xFF, 0xF5,
+	0xFF, 0xEE, 0xFF, 0x99, 0xFF, 0x76, 0xFF, 0x94, 0x00, 0xD0, 0x03,
+	0xD1, 0x08, 0xD3, 0x0D, 0x8B, 0x10, 0x9A, 0x0F, 0x7C, 0x0B, 0x32,
+	0x06, 0xEE, 0x01, 0xCB, 0xFF, 0x72, 0xFF, 0xCA, 0xFF, 0xFE, 0xFF,
+	0x00, 0x00, 0xD6, 0xFF, 0x7B, 0xFF, 0xA7, 0xFF, 0x78, 0x01, 0x72,
+	0x05, 0xB6, 0x0A, 0x27, 0x0F, 0xA5, 0x10, 0x6E, 0x0E, 0xA0, 0x09,
+	0x7B, 0x04, 0xEC, 0x00, 0x85, 0xFF, 0x8B, 0xFF, 0xE5, 0xFF, 0xFA,
+	0xFF, 0xB6, 0xFF, 0x6C, 0xFF, 0x0E, 0x00, 0xA4, 0x02, 0x41, 0x07,
+	0x80, 0x0C, 0x19, 0x10, 0x44, 0x10, 0xEB, 0x0C, 0xB9, 0x07, 0xFA,
+	0x02, 0x32, 0x00, 0x6D, 0xFF, 0xAE, 0xFF, 0xF7, 0xFF, 0xEB, 0xFF,
+	0x93, 0xFF, 0x7B, 0xFF, 0xB7, 0x00, 0x15, 0x04, 0x26, 0x09, 0x14,
+	0x0E, 0x98, 0x10, 0x6D, 0x0F, 0x2C, 0x0B, 0xE3, 0x05, 0xBC, 0x01,
+	0xBB, 0xFF, 0x75, 0xFF, 0xCF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xD1,
+	0xFF, 0x77, 0xFF, 0xB5, 0xFF, 0xA6, 0x01, 0xC0, 0x05, 0x08, 0x0B,
+	0x58, 0x0F, 0x9D, 0x10, 0x30, 0x0E, 0x4B, 0x09, 0x34, 0x04, 0xC6,
+	0x00, 0x7D, 0xFF, 0x91, 0xFF, 0xE9, 0xFF, 0xF8, 0xFF, 0xB0, 0xFF,
+	0x6C, 0xFF, 0x27, 0x00, 0xDF, 0x02, 0x94, 0x07, 0xCA, 0x0C, 0x37,
+	0x10, 0x27, 0x10, 0xA1, 0x0C, 0x65, 0x07, 0xBE, 0x02, 0x19, 0x00,
+	0x6C, 0xFF, 0xB4, 0xFF, 0xF9, 0xFF, 0xE7, 0xFF, 0x8E, 0xFF, 0x81,
+	0xFF, 0xDB, 0x00, 0x5B, 0x04, 0x7A, 0x09, 0x53, 0x0E, 0xA2, 0x10,
+	0x3D, 0x0F, 0xDA, 0x0A, 0x95, 0x05, 0x8C, 0x01, 0xAD, 0xFF, 0x79,
+	0xFF, 0xD4, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xCC, 0xFF, 0x73, 0xFF,
+	0xC4, 0xFF, 0xD7, 0x01, 0x0E, 0x06, 0x59, 0x0B, 0x87, 0x0F, 0x91,
+	0x10, 0xF0, 0x0D, 0xF7, 0x08, 0xEF, 0x03, 0xA3, 0x00, 0x78, 0xFF,
+	0x97, 0xFF, 0xED, 0xFF, 0xF6, 0xFF, 0xAA, 0xFF, 0x6D, 0xFF, 0x41,
+	0x00, 0x1C, 0x03, 0xE7, 0x07, 0x13, 0x0D, 0x52, 0x10, 0x06, 0x10,
+	0x56, 0x0C, 0x13, 0x07, 0x84, 0x02, 0x02, 0x00, 0x6D, 0xFF, 0xBA,
+	0xFF, 0xFB, 0xFF, 0xE3, 0xFF, 0x88, 0xFF, 0x89, 0xFF, 0x01, 0x01,
+	0xA3, 0x04, 0xCE, 0x09, 0x8F, 0x0E, 0xA8, 0x10, 0x0A, 0x0F, 0x88,
+	0x0A, 0x48, 0x05, 0x5F, 0x01, 0xA0, 0xFF, 0x7D, 0xFF, 0xD9, 0xFF,
+	0x00, 0x00, 0xFE, 0xFF, 0xC7, 0xFF, 0x70, 0xFF, 0xD5, 0xFF, 0x0B,
+	0x02, 0x5E, 0x06, 0xA9, 0x0B, 0xB2, 0x0F, 0x82, 0x10, 0xAE, 0x0D,
+	0xA2, 0x08, 0xAB, 0x03, 0x82, 0x00, 0x73, 0xFF, 0x9D, 0xFF, 0xF0,
+	0xFF, 0xF3, 0xFF, 0xA4, 0xFF, 0x6F, 0xFF, 0x5D, 0x00, 0x5B, 0x03,
+	0x3B, 0x08, 0x5A, 0x0D, 0x6A, 0x10, 0xE2, 0x0F, 0x09, 0x0C, 0xC1,
+	0x06, 0x4C, 0x02, 0xEC, 0xFF, 0x6E, 0xFF, 0xC0, 0xFF, 0xFC, 0xFF,
+	0xDF, 0xFF, 0x83, 0xFF, 0x93, 0xFF, 0x2A, 0x01, 0xEC, 0x04, 0x22,
+	0x0A, 0xC8, 0x0E, 0xAB, 0x10, 0xD4, 0x0E, 0x35, 0x0A, 0xFD, 0x04,
+	0x33, 0x01, 0x95, 0xFF, 0x82, 0xFF, 0xDE, 0xFF, 0x00, 0x00, 0xFD,
+	0xFF, 0xC1, 0xFF, 0x6E, 0xFF, 0xE8, 0xFF, 0x40, 0x02, 0xAF, 0x06,
+	0xF7, 0x0B, 0xDA, 0x0F, 0x6F, 0x10, 0x6A, 0x0D, 0x4E, 0x08, 0x6A,
+	0x03, 0x64, 0x00, 0x70, 0xFF, 0xA3, 0xFF, 0xF3, 0xFF, 0xF1, 0xFF,
+	0x9E, 0xFF, 0x72, 0xFF, 0x7B, 0x00, 0x9C, 0x03, 0x90, 0x08, 0x9F,
+	0x0D, 0x7E, 0x10, 0xBB, 0x0F, 0xBA, 0x0B, 0x70, 0x06, 0x16, 0x02,
+	0xD9, 0xFF, 0x70, 0xFF, 0xC5, 0xFF, 0xFE, 0xFF, 0x00, 0x00, 0xDA,
+	0xFF, 0x7E, 0xFF, 0x9D, 0xFF, 0x55, 0x01, 0x37, 0x05, 0x75, 0x0A,
+	0xFF, 0x0E, 0xA9, 0x10, 0x9C, 0x0E, 0xE1, 0x09, 0xB3, 0x04, 0x0A,
+	0x01, 0x8B, 0xFF, 0x87, 0xFF, 0xE2, 0xFF, 0xFB, 0xFF, 0xBB, 0xFF,
+	0x6D, 0xFF, 0xFD, 0xFF, 0x77, 0x02, 0x01, 0x07, 0x45, 0x0C, 0xFF,
+	0x0F, 0x58, 0x10, 0x23, 0x0D, 0xFA, 0x07, 0x2A, 0x03, 0x47, 0x00,
+	0x6E, 0xFF, 0xA9, 0xFF, 0xF5, 0xFF, 0xED, 0xFF, 0x98, 0xFF, 0x77,
+	0xFF, 0x9C, 0x00, 0xDF, 0x03, 0xE4, 0x08, 0xE2, 0x0D, 0x8E, 0x10,
+	0x91, 0x0F, 0x6B, 0x0B, 0x20, 0x06, 0xE3, 0x01, 0xC8, 0xFF, 0x73,
+	0xFF, 0xCB, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xD5, 0xFF, 0x7A, 0xFF,
+	0xAA, 0xFF, 0x82, 0x01, 0x83, 0x05, 0xC8, 0x0A, 0x32, 0x0F, 0xA4,
+	0x10, 0x60, 0x0E, 0x8D, 0x09, 0x6B, 0x04, 0xE3, 0x00, 0x83, 0xFF,
+	0x8D, 0xFF, 0xE6, 0xFF, 0xFA, 0xFF, 0xB5, 0xFF, 0x6C, 0xFF, 0x14,
+	0x00, 0xB1, 0x02, 0x53, 0x07, 0x91, 0x0C, 0x20, 0x10, 0x3E, 0x10,
+	0xDB, 0x0C, 0xA6, 0x07, 0xEC, 0x02, 0x2C, 0x00, 0x6C, 0xFF, 0xAF,
+	0xFF, 0xF8, 0xFF, 0xEA, 0xFF, 0x92, 0xFF, 0x7C, 0xFF, 0xBE, 0x00,
+	0x24, 0x04, 0x38, 0x09, 0x22, 0x0E, 0x9B, 0x10, 0x63, 0x0F, 0x1A,
+	0x0B, 0xD1, 0x05, 0xB1, 0x01, 0xB8, 0xFF, 0x76, 0xFF, 0xD0, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xD0, 0xFF, 0x76, 0xFF, 0xB8, 0xFF, 0xB1,
+	0x01, 0xD1, 0x05, 0x1A, 0x0B, 0x63, 0x0F, 0x9B, 0x10, 0x22, 0x0E,
+	0x38, 0x09, 0x24, 0x04, 0xBE, 0x00, 0x7C, 0xFF, 0x92, 0xFF, 0xEA,
+	0xFF, 0xF8, 0xFF, 0xAF, 0xFF, 0x6C, 0xFF, 0x2C, 0x00, 0xEC, 0x02,
+	0xA6, 0x07, 0xDB, 0x0C, 0x3E, 0x10, 0x20, 0x10, 0x91, 0x0C, 0x53,
+	0x07, 0xB1, 0x02, 0x14, 0x00, 0x6C, 0xFF, 0xB5, 0xFF, 0xFA, 0xFF,
+	0xE6, 0xFF, 0x8D, 0xFF, 0x83, 0xFF, 0xE3, 0x00, 0x6B, 0x04, 0x8D,
+	0x09, 0x60, 0x0E, 0xA4, 0x10, 0x32, 0x0F, 0xC8, 0x0A, 0x83, 0x05,
+	0x82, 0x01, 0xAA, 0xFF, 0x7A, 0xFF, 0xD5, 0xFF, 0x00, 0x00, 0xFF,
+	0xFF, 0xCB, 0xFF, 0x73, 0xFF, 0xC8, 0xFF, 0xE3, 0x01, 0x20, 0x06,
+	0x6B, 0x0B, 0x91, 0x0F, 0x8E, 0x10, 0xE2, 0x0D, 0xE4, 0x08, 0xDF,
+	0x03, 0x9C, 0x00, 0x77, 0xFF, 0x98, 0xFF, 0xED, 0xFF, 0xF5, 0xFF,
+	0xA9, 0xFF, 0x6E, 0xFF, 0x47, 0x00, 0x2A, 0x03, 0xFA, 0x07, 0x23,
+	0x0D, 0x58, 0x10, 0xFF, 0x0F, 0x45, 0x0C, 0x01, 0x07, 0x77, 0x02,
+	0xFD, 0xFF, 0x6D, 0xFF, 0xBB, 0xFF, 0xFB, 0xFF, 0xE2, 0xFF, 0x87,
+	0xFF, 0x8B, 0xFF, 0x0A, 0x01, 0xB3, 0x04, 0xE1, 0x09, 0x9C, 0x0E,
+	0xA9, 0x10, 0xFF, 0x0E, 0x75, 0x0A, 0x37, 0x05, 0x55, 0x01, 0x9D,
+	0xFF, 0x7E, 0xFF, 0xDA, 0xFF, 0x00, 0x00, 0xFE, 0xFF, 0xC5, 0xFF,
+	0x70, 0xFF, 0xD9, 0xFF, 0x16, 0x02, 0x70, 0x06, 0xBA, 0x0B, 0xBB,
+	0x0F, 0x7E, 0x10, 0x9F, 0x0D, 0x90, 0x08, 0x9C, 0x03, 0x7B, 0x00,
+	0x72, 0xFF, 0x9E, 0xFF, 0xF1, 0xFF, 0xF3, 0xFF, 0xA3, 0xFF, 0x70,
+	0xFF, 0x64, 0x00, 0x6A, 0x03, 0x4E, 0x08, 0x6A, 0x0D, 0x6F, 0x10,
+	0xDA, 0x0F, 0xF7, 0x0B, 0xAF, 0x06, 0x40, 0x02, 0xE8, 0xFF, 0x6E,
+	0xFF, 0xC1, 0xFF, 0xFD, 0xFF, 0x00, 0x00, 0xDE, 0xFF, 0x82, 0xFF,
+	0x95, 0xFF, 0x33, 0x01, 0xFD, 0x04, 0x35, 0x0A, 0xD4, 0x0E, 0xAB,
+	0x10, 0xC8, 0x0E, 0x22, 0x0A, 0xEC, 0x04, 0x2A, 0x01, 0x93, 0xFF,
+	0x83, 0xFF, 0xDF, 0xFF, 0xFC, 0xFF, 0xC0, 0xFF, 0x6E, 0xFF, 0xEC,
+	0xFF, 0x4C, 0x02, 0xC1, 0x06, 0x09, 0x0C, 0xE2, 0x0F, 0x6A, 0x10,
+	0x5A, 0x0D, 0x3B, 0x08, 0x5B, 0x03, 0x5D, 0x00, 0x6F, 0xFF, 0xA4,
+	0xFF, 0xF3, 0xFF, 0xF0, 0xFF, 0x9D, 0xFF, 0x73, 0xFF, 0x82, 0x00,
+	0xAB, 0x03, 0xA2, 0x08, 0xAE, 0x0D, 0x82, 0x10, 0xB2, 0x0F, 0xA9,
+	0x0B, 0x5E, 0x06, 0x0B, 0x02, 0xD5, 0xFF, 0x70, 0xFF, 0xC7, 0xFF,
+	0xFE, 0xFF, 0x00, 0x00, 0xD9, 0xFF, 0x7D, 0xFF, 0xA0, 0xFF, 0x5F,
+	0x01, 0x48, 0x05, 0x88, 0x0A, 0x0A, 0x0F, 0xA8, 0x10, 0x8F, 0x0E,
+	0xCE, 0x09, 0xA3, 0x04, 0x01, 0x01, 0x89, 0xFF, 0x88, 0xFF, 0xE3,
+	0xFF, 0xFB, 0xFF, 0xBA, 0xFF, 0x6D, 0xFF, 0x02, 0x00, 0x84, 0x02,
+	0x13, 0x07, 0x56, 0x0C, 0x06, 0x10, 0x52, 0x10, 0x13, 0x0D, 0xE7,
+	0x07, 0x1C, 0x03, 0x41, 0x00, 0x6D, 0xFF, 0xAA, 0xFF, 0xF6, 0xFF,
+	0xED, 0xFF, 0x97, 0xFF, 0x78, 0xFF, 0xA3, 0x00, 0xEF, 0x03, 0xF7,
+	0x08, 0xF0, 0x0D, 0x91, 0x10, 0x87, 0x0F, 0x59, 0x0B, 0x0E, 0x06,
+	0xD7, 0x01, 0xC4, 0xFF, 0x73, 0xFF, 0xCC, 0xFF, 0xFF, 0xFF, 0x00,
+	0x00, 0xD4, 0xFF, 0x79, 0xFF, 0xAD, 0xFF, 0x8C, 0x01, 0x95, 0x05,
+	0xDA, 0x0A, 0x3D, 0x0F, 0xA2, 0x10, 0x53, 0x0E, 0x7A, 0x09, 0x5B,
+	0x04, 0xDB, 0x00, 0x81, 0xFF, 0x8E, 0xFF, 0xE7, 0xFF, 0xF9, 0xFF,
+	0xB4, 0xFF, 0x6C, 0xFF, 0x19, 0x00, 0xBE, 0x02, 0x65, 0x07, 0xA1,
+	0x0C, 0x27, 0x10, 0x37, 0x10, 0xCA, 0x0C, 0x94, 0x07, 0xDF, 0x02,
+	0x27, 0x00, 0x6C, 0xFF, 0xB0, 0xFF, 0xF8, 0xFF, 0xE9, 0xFF, 0x91,
+	0xFF, 0x7D, 0xFF, 0xC6, 0x00, 0x34, 0x04, 0x4B, 0x09, 0x30, 0x0E,
+	0x9D, 0x10, 0x58, 0x0F, 0x08, 0x0B, 0xC0, 0x05, 0xA6, 0x01, 0xB5,
+	0xFF, 0x77, 0xFF, 0xD1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xCF, 0xFF,
+	0x75, 0xFF, 0xBB, 0xFF, 0xBC, 0x01, 0xE3, 0x05, 0x2C, 0x0B, 0x6D,
+	0x0F, 0x98, 0x10, 0x14, 0x0E, 0x26, 0x09, 0x15, 0x04, 0xB7, 0x00,
+	0x7B, 0xFF, 0x93, 0xFF, 0xEB, 0xFF, 0xF7, 0xFF, 0xAE, 0xFF, 0x6D,
+	0xFF, 0x32, 0x00, 0xFA, 0x02, 0xB9, 0x07, 0xEB, 0x0C, 0x44, 0x10,
+	0x19, 0x10, 0x80, 0x0C, 0x41, 0x07, 0xA4, 0x02, 0x0E, 0x00, 0x6C,
+	0xFF, 0xB6, 0xFF, 0xFA, 0xFF, 0xE5, 0xFF, 0x8B, 0xFF, 0x85, 0xFF,
+	0xEC, 0x00, 0x7B, 0x04, 0xA0, 0x09, 0x6E, 0x0E, 0xA5, 0x10, 0x27,
+	0x0F, 0xB6, 0x0A, 0x72, 0x05, 0x78, 0x01, 0xA7, 0xFF, 0x7B, 0xFF,
+	0xD6, 0xFF, 0x00, 0x00, 0xFE, 0xFF, 0xCA, 0xFF, 0x72, 0xFF, 0xCB,
+	0xFF, 0xEE, 0x01, 0x32, 0x06, 0x7C, 0x0B, 0x9A, 0x0F, 0x8B, 0x10,
+	0xD3, 0x0D, 0xD1, 0x08, 0xD0, 0x03, 0x94, 0x00, 0x76, 0xFF, 0x99,
+	0xFF, 0xEE, 0xFF, 0xF5, 0xFF, 0xA7, 0xFF, 0x6E, 0xFF, 0x4D, 0x00,
+	0x38, 0x03, 0x0D, 0x08, 0x33, 0x0D, 0x5D, 0x10, 0xF7, 0x0F, 0x34,
+	0x0C, 0xEE, 0x06, 0x6B, 0x02, 0xF8, 0xFF, 0x6D, 0xFF, 0xBC, 0xFF,
+	0xFC, 0xFF, 0xE1, 0xFF, 0x86, 0xFF, 0x8D, 0xFF, 0x13, 0x01, 0xC3,
+	0x04, 0xF4, 0x09, 0xA8, 0x0E, 0xAA, 0x10, 0xF3, 0x0E, 0x63, 0x0A,
+	0x26, 0x05, 0x4B, 0x01, 0x9B, 0xFF, 0x7F, 0xFF, 0xDB, 0xFF, 0x00,
+	0x00, 0xFD, 0xFF, 0xC4, 0xFF, 0x6F, 0xFF, 0xDD, 0xFF, 0x22, 0x02,
+	0x82, 0x06, 0xCC, 0x0B, 0xC4, 0x0F, 0x7A, 0x10, 0x90, 0x0D, 0x7D,
+	0x08, 0x8E, 0x03, 0x74, 0x00, 0x72, 0xFF, 0x9F, 0xFF, 0xF1, 0xFF,
+	0xF2, 0xFF, 0xA1, 0xFF, 0x70, 0xFF, 0x6A, 0x00, 0x78, 0x03, 0x61,
+	0x08, 0x79, 0x0D, 0x73, 0x10, 0xD1, 0x0F, 0xE6, 0x0B, 0x9D, 0x06,
+	0x34, 0x02, 0xE4, 0xFF, 0x6F, 0xFF, 0xC2, 0xFF, 0xFD, 0xFF, 0x00,
+	0x00, 0xDD, 0xFF, 0x81, 0xFF, 0x97, 0xFF, 0x3D, 0x01, 0x0D, 0x05,
+	0x47, 0x0A, 0xE1, 0x0E, 0xAA, 0x10, 0xBB, 0x0E, 0x10, 0x0A, 0xDC,
+	0x04, 0x21, 0x01, 0x90, 0xFF, 0x84, 0xFF, 0xE0, 0xFF, 0xFC, 0xFF,
+	0xBE, 0xFF, 0x6D, 0xFF, 0xF1, 0xFF, 0x58, 0x02, 0xD3, 0x06, 0x1A,
+	0x0C, 0xEB, 0x0F, 0x65, 0x10, 0x4B, 0x0D, 0x29, 0x08, 0x4D, 0x03,
+	0x57, 0x00, 0x6F, 0xFF, 0xA5, 0xFF, 0xF4, 0xFF, 0xEF, 0xFF, 0x9B,
+	0xFF, 0x74, 0xFF, 0x8A, 0x00, 0xBA, 0x03, 0xB5, 0x08, 0xBD, 0x0D,
+	0x86, 0x10, 0xA9, 0x0F, 0x97, 0x0B, 0x4C, 0x06, 0xFF, 0x01, 0xD1,
+	0xFF, 0x71, 0xFF, 0xC8, 0xFF, 0xFE, 0xFF, 0x00, 0x00, 0xD8, 0xFF,
+	0x7C, 0xFF, 0xA3, 0xFF, 0x69, 0x01, 0x59, 0x05, 0x9A, 0x0A, 0x16,
+	0x0F, 0xA7, 0x10, 0x82, 0x0E, 0xBC, 0x09, 0x93, 0x04, 0xF9, 0x00,
+	0x87, 0xFF, 0x89, 0xFF, 0xE4, 0xFF, 0xFB, 0xFF, 0xB8, 0xFF, 0x6C,
+	0xFF, 0x07, 0x00, 0x91, 0x02, 0x25, 0x07, 0x67, 0x0C, 0x0E, 0x10,
+	0x4D, 0x10, 0x03, 0x0D, 0xD5, 0x07, 0x0E, 0x03, 0x3B, 0x00, 0x6D,
+	0xFF, 0xAC, 0xFF, 0xF7, 0xFF, 0xEC, 0xFF, 0x95, 0xFF, 0x79, 0xFF,
+	0xAB, 0x00, 0xFE, 0x03, 0x0A, 0x09, 0xFF, 0x0D, 0x94, 0x10, 0x7D,
+	0x0F, 0x47, 0x0B, 0xFD, 0x05, 0xCC, 0x01, 0xC0, 0xFF, 0x74, 0xFF,
+	0xCD, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xD3, 0xFF, 0x78, 0xFF, 0xB0,
+	0xFF, 0x97, 0x01, 0xA6, 0x05, 0xEC, 0x0A, 0x48, 0x0F, 0xA0, 0x10,
+	0x45, 0x0E, 0x67, 0x09, 0x4B, 0x04, 0xD3, 0x00, 0x80, 0xFF, 0x8F,
+	0xFF, 0xE8, 0xFF, 0xF9, 0xFF, 0xB2, 0xFF, 0x6C, 0xFF, 0x1E, 0x00,
+	0xCB, 0x02, 0x78, 0x07, 0xB2, 0x0C, 0x2E, 0x10, 0x31, 0x10, 0xBA,
+	0x0C, 0x81, 0x07, 0xD2, 0x02, 0x21, 0x00, 0x6C, 0xFF, 0xB2, 0xFF,
+	0xF9, 0xFF, 0xE8, 0xFF, 0x90, 0xFF, 0x7F, 0xFF, 0xCF, 0x00, 0x43,
+	0x04, 0x5E, 0x09, 0x3E, 0x0E, 0x9F, 0x10, 0x4E, 0x0F, 0xF6, 0x0A,
+	0xAE, 0x05, 0x9C, 0x01, 0xB1, 0xFF, 0x78, 0xFF, 0xD2, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xCE, 0xFF, 0x74, 0xFF, 0xBF, 0xFF, 0xC7, 0x01,
+	0xF4, 0x05, 0x3E, 0x0B, 0x78, 0x0F, 0x96, 0x10, 0x06, 0x0E, 0x13,
+	0x09, 0x05, 0x04, 0xAF, 0x00, 0x79, 0xFF, 0x95, 0xFF, 0xEC, 0xFF,
+	0xF7, 0xFF, 0xAC, 0xFF, 0x6D, 0xFF, 0x38, 0x00, 0x07, 0x03, 0xCB,
+	0x07, 0xFB, 0x0C, 0x4A, 0x10, 0x11, 0x10, 0x6F, 0x0C, 0x2E, 0x07,
+	0x97, 0x02, 0x09, 0x00, 0x6C, 0xFF, 0xB8, 0xFF, 0xFA, 0xFF, 0xE4,
+	0xFF, 0x8A, 0xFF, 0x86, 0xFF, 0xF4, 0x00, 0x8B, 0x04, 0xB2, 0x09,
+	0x7B, 0x0E, 0xA7, 0x10, 0x1C, 0x0F, 0xA3, 0x0A, 0x61, 0x05, 0x6E,
+	0x01, 0xA4, 0xFF, 0x7C, 0xFF, 0xD8, 0xFF, 0x00, 0x00, 0xFE, 0xFF,
+	0xC8, 0xFF, 0x71, 0xFF, 0xCF, 0xFF, 0xF9, 0x01, 0x43, 0x06, 0x8E,
+	0x0B, 0xA4, 0x0F, 0x88, 0x10, 0xC4, 0x0D, 0xBE, 0x08, 0xC1, 0x03,
+	0x8D, 0x00, 0x75, 0xFF, 0x9B, 0xFF, 0xEF, 0xFF, 0xF4, 0xFF, 0xA6,
+	0xFF, 0x6E, 0xFF, 0x53, 0x00, 0x46, 0x03, 0x1F, 0x08, 0x43, 0x0D,
+	0x63, 0x10, 0xEF, 0x0F, 0x23, 0x0C, 0xDC, 0x06, 0x5E, 0x02, 0xF3,
+	0xFF, 0x6D, 0xFF, 0xBE, 0xFF, 0xFC, 0xFF, 0xE0, 0xFF, 0x85, 0xFF,
+	0x8F, 0xFF, 0x1C, 0x01, 0xD3, 0x04, 0x06, 0x0A, 0xB5, 0x0E, 0xAA,
+	0x10, 0xE7, 0x0E, 0x50, 0x0A, 0x16, 0x05, 0x42, 0x01, 0x98, 0xFF,
+	0x80, 0xFF, 0xDC, 0xFF, 0x00, 0x00, 0xFD, 0xFF, 0xC3, 0xFF, 0x6F,
+	0xFF, 0xE1, 0xFF, 0x2E, 0x02, 0x94, 0x06, 0xDD, 0x0B, 0xCD, 0x0F,
+	0x76, 0x10, 0x81, 0x0D, 0x6A, 0x08, 0x7F, 0x03, 0x6E, 0x00, 0x71,
+	0xFF, 0xA1, 0xFF, 0xF2, 0xFF, 0x00, 0x00, 0x15, 0x00, 0xD1, 0xFF,
+	0x8B, 0xFE, 0xBC, 0xFD, 0xE1, 0x00, 0x84, 0x09, 0xB0, 0x13, 0x47,
+	0x18, 0xB0, 0x13, 0x84, 0x09, 0xE1, 0x00, 0xBC, 0xFD, 0x8B, 0xFE,
+	0xD1, 0xFF, 0x15, 0x00, 0xFD, 0xFF, 0x13, 0x00, 0xDA, 0x00, 0x30,
+	0x00, 0x5D, 0xFC, 0xB3, 0xFC, 0x35, 0x0A, 0xC2, 0x1C, 0x24, 0x20,
+	0x48, 0x10, 0x5D, 0xFF, 0x74, 0xFB, 0x3A, 0xFF, 0xFB, 0x00, 0x42,
+	0x00, 0xF8, 0xFF, 0xFA, 0xFF, 0x2C, 0x00, 0xF3, 0x00, 0xAD, 0xFF,
+	0xC5, 0xFB, 0x11, 0xFE, 0xAF, 0x0D, 0xEF, 0x1E, 0x68, 0x1E, 0xBC,
+	0x0C, 0xA7, 0xFD, 0xEA, 0xFB, 0xD3, 0xFF, 0xEE, 0x00, 0x24, 0x00,
+	0xFA, 0xFF, 0xF7, 0xFF, 0x4C, 0x00, 0xFB, 0x00, 0x0C, 0xFF, 0x5F,
+	0xFB, 0xE8, 0xFF, 0x3D, 0x11, 0x7E, 0x20, 0x13, 0x1C, 0x4C, 0x09,
+	0x6A, 0xFC, 0x8C, 0xFC, 0x4E, 0x00, 0xD1, 0x00, 0x0E, 0x00, 0xFD,
+	0xFF, 0xF7, 0xFF, 0x72, 0x00, 0xEC, 0x00, 0x55, 0xFE, 0x3D, 0xFB,
+	0x37, 0x02, 0xBE, 0x14, 0x5D, 0x21, 0x40, 0x19, 0x18, 0x06, 0xA2,
+	0xFB, 0x47, 0xFD, 0xA7, 0x00, 0xAB, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0xFC, 0xFF, 0x9B, 0x00, 0xC0, 0x00, 0x92, 0xFD, 0x73,
+	0xFB, 0xF2, 0x04, 0x0E, 0x18, 0x81, 0x21, 0x0C, 0x16, 0x37, 0x03,
+	0x47, 0xFB, 0x0B, 0xFE, 0xDF, 0x00, 0x82, 0x00, 0xF9, 0xFF, 0xFE,
+	0xFF, 0x08, 0x00, 0xC3, 0x00, 0x74, 0x00, 0xD2, 0xFC, 0x10, 0xFC,
+	0x08, 0x08, 0x0A, 0x1B, 0xE9, 0x20, 0x9A, 0x12, 0xBE, 0x00, 0x49,
+	0xFB, 0xC8, 0xFE, 0xF9, 0x00, 0x5A, 0x00, 0xF7, 0xFF, 0xFC, 0xFF,
+	0x1B, 0x00, 0xE4, 0x00, 0x06, 0x00, 0x24, 0xFC, 0x1E, 0xFD, 0x65,
+	0x0B, 0x94, 0x1D, 0x9D, 0x1F, 0x0D, 0x0F, 0xB8, 0xFE, 0x96, 0xFB,
+	0x72, 0xFF, 0xF9, 0x00, 0x37, 0x00, 0xF8, 0xFF, 0xF9, 0xFF, 0x36,
+	0x00, 0xF8, 0x00, 0x78, 0xFF, 0x9B, 0xFB, 0xA6, 0xFE, 0xE9, 0x0E,
+	0x8D, 0x1F, 0xAA, 0x1D, 0x87, 0x0B, 0x2B, 0xFD, 0x1E, 0xFC, 0x02,
+	0x00, 0xE5, 0x00, 0x1C, 0x00, 0xFB, 0xFF, 0xF7, 0xFF, 0x58, 0x00,
+	0xF9, 0x00, 0xCF, 0xFE, 0x4A, 0xFB, 0xA7, 0x00, 0x77, 0x12, 0xE0,
+	0x20, 0x26, 0x1B, 0x28, 0x08, 0x18, 0xFC, 0xCB, 0xFC, 0x71, 0x00,
+	0xC5, 0x00, 0x08, 0x00, 0xFE, 0xFF, 0xF8, 0xFF, 0x80, 0x00, 0xE1,
+	0x00, 0x13, 0xFE, 0x45, 0xFB, 0x1D, 0x03, 0xEB, 0x15, 0x7F, 0x21,
+	0x2D, 0x18, 0x0E, 0x05, 0x77, 0xFB, 0x8B, 0xFD, 0xBE, 0x00, 0x9D,
+	0x00, 0xFD, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xA9, 0x00,
+	0xAA, 0x00, 0x4F, 0xFD, 0x9D, 0xFB, 0xFA, 0x05, 0x22, 0x19, 0x62,
+	0x21, 0xE0, 0x14, 0x50, 0x02, 0x3E, 0xFB, 0x4E, 0xFE, 0xEB, 0x00,
+	0x73, 0x00, 0xF7, 0xFF, 0xFE, 0xFF, 0x0D, 0x00, 0xD0, 0x00, 0x52,
+	0x00, 0x93, 0xFC, 0x60, 0xFC, 0x2C, 0x09, 0xFA, 0x1B, 0x8A, 0x20,
+	0x60, 0x11, 0xFD, 0xFF, 0x5C, 0xFB, 0x06, 0xFF, 0xFB, 0x00, 0x4D,
+	0x00, 0xF7, 0xFF, 0xFA, 0xFF, 0x23, 0x00, 0xED, 0x00, 0xD9, 0xFF,
+	0xEF, 0xFB, 0x98, 0xFD, 0x99, 0x0C, 0x54, 0x1E, 0x02, 0x1F, 0xD2,
+	0x0D, 0x20, 0xFE, 0xC0, 0xFB, 0xA7, 0xFF, 0xF4, 0x00, 0x2D, 0x00,
+	0xF9, 0xFF, 0xF8, 0xFF, 0x41, 0x00, 0xFB, 0x00, 0x41, 0xFF, 0x78,
+	0xFB, 0x4A, 0xFF, 0x25, 0x10, 0x16, 0x20, 0xDA, 0x1C, 0x56, 0x0A,
+	0xBE, 0xFC, 0x56, 0xFC, 0x2C, 0x00, 0xDB, 0x00, 0x14, 0x00, 0xFD,
+	0xFF, 0xF7, 0xFF, 0x66, 0x00, 0xF4, 0x00, 0x8F, 0xFE, 0x3F, 0xFB,
+	0x75, 0x01, 0xAE, 0x13, 0x2C, 0x21, 0x2A, 0x1A, 0x0D, 0x07, 0xD4,
+	0xFB, 0x0C, 0xFD, 0x8F, 0x00, 0xB7, 0x00, 0x03, 0x00, 0xFF, 0xFF,
+	0x00, 0x00, 0xFA, 0xFF, 0x8E, 0x00, 0xD1, 0x00, 0xCF, 0xFD, 0x58,
+	0xFB, 0x10, 0x04, 0x10, 0x17, 0x8A, 0x21, 0x10, 0x17, 0x10, 0x04,
+	0x58, 0xFB, 0xCF, 0xFD, 0xD1, 0x00, 0x8E, 0x00, 0xFA, 0xFF, 0xFF,
+	0xFF, 0x03, 0x00, 0xB7, 0x00, 0x8F, 0x00, 0x0C, 0xFD, 0xD4, 0xFB,
+	0x0D, 0x07, 0x2A, 0x1A, 0x2C, 0x21, 0xAE, 0x13, 0x75, 0x01, 0x3F,
+	0xFB, 0x8F, 0xFE, 0xF4, 0x00, 0x66, 0x00, 0xF7, 0xFF, 0xFD, 0xFF,
+	0x14, 0x00, 0xDB, 0x00, 0x2C, 0x00, 0x56, 0xFC, 0xBE, 0xFC, 0x56,
+	0x0A, 0xDA, 0x1C, 0x16, 0x20, 0x25, 0x10, 0x4A, 0xFF, 0x78, 0xFB,
+	0x41, 0xFF, 0xFB, 0x00, 0x41, 0x00, 0xF8, 0xFF, 0xF9, 0xFF, 0x2D,
+	0x00, 0xF4, 0x00, 0xA7, 0xFF, 0xC0, 0xFB, 0x20, 0xFE, 0xD2, 0x0D,
+	0x02, 0x1F, 0x54, 0x1E, 0x99, 0x0C, 0x98, 0xFD, 0xEF, 0xFB, 0xD9,
+	0xFF, 0xED, 0x00, 0x23, 0x00, 0xFA, 0xFF, 0xF7, 0xFF, 0x4D, 0x00,
+	0xFB, 0x00, 0x06, 0xFF, 0x5C, 0xFB, 0xFD, 0xFF, 0x60, 0x11, 0x8A,
+	0x20, 0xFA, 0x1B, 0x2C, 0x09, 0x60, 0xFC, 0x93, 0xFC, 0x52, 0x00,
+	0xD0, 0x00, 0x0D, 0x00, 0xFE, 0xFF, 0xF7, 0xFF, 0x73, 0x00, 0xEB,
+	0x00, 0x4E, 0xFE, 0x3E, 0xFB, 0x50, 0x02, 0xE0, 0x14, 0x62, 0x21,
+	0x22, 0x19, 0xFA, 0x05, 0x9D, 0xFB, 0x4F, 0xFD, 0xAA, 0x00, 0xA9,
+	0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0x9D, 0x00,
+	0xBE, 0x00, 0x8B, 0xFD, 0x77, 0xFB, 0x0E, 0x05, 0x2D, 0x18, 0x7F,
+	0x21, 0xEB, 0x15, 0x1D, 0x03, 0x45, 0xFB, 0x13, 0xFE, 0xE1, 0x00,
+	0x80, 0x00, 0xF8, 0xFF, 0xFE, 0xFF, 0x08, 0x00, 0xC5, 0x00, 0x71,
+	0x00, 0xCB, 0xFC, 0x18, 0xFC, 0x28, 0x08, 0x26, 0x1B, 0xE0, 0x20,
+	0x77, 0x12, 0xA7, 0x00, 0x4A, 0xFB, 0xCF, 0xFE, 0xF9, 0x00, 0x58,
+	0x00, 0xF7, 0xFF, 0xFB, 0xFF, 0x1C, 0x00, 0xE5, 0x00, 0x02, 0x00,
+	0x1E, 0xFC, 0x2B, 0xFD, 0x87, 0x0B, 0xAA, 0x1D, 0x8D, 0x1F, 0xE9,
+	0x0E, 0xA6, 0xFE, 0x9B, 0xFB, 0x78, 0xFF, 0xF8, 0x00, 0x36, 0x00,
+	0xF9, 0xFF, 0xF8, 0xFF, 0x37, 0x00, 0xF9, 0x00, 0x72, 0xFF, 0x96,
+	0xFB, 0xB8, 0xFE, 0x0D, 0x0F, 0x9D, 0x1F, 0x94, 0x1D, 0x65, 0x0B,
+	0x1E, 0xFD, 0x24, 0xFC, 0x06, 0x00, 0xE4, 0x00, 0x1B, 0x00, 0xFC,
+	0xFF, 0xF7, 0xFF, 0x5A, 0x00, 0xF9, 0x00, 0xC8, 0xFE, 0x49, 0xFB,
+	0xBE, 0x00, 0x9A, 0x12, 0xE9, 0x20, 0x0A, 0x1B, 0x08, 0x08, 0x10,
+	0xFC, 0xD2, 0xFC, 0x74, 0x00, 0xC3, 0x00, 0x08, 0x00, 0xFE, 0xFF,
+	0xF9, 0xFF, 0x82, 0x00, 0xDF, 0x00, 0x0B, 0xFE, 0x47, 0xFB, 0x37,
+	0x03, 0x0C, 0x16, 0x81, 0x21, 0x0E, 0x18, 0xF2, 0x04, 0x73, 0xFB,
+	0x92, 0xFD, 0xC0, 0x00, 0x9B, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0xAB, 0x00, 0xA7, 0x00, 0x47, 0xFD, 0xA2, 0xFB,
+	0x18, 0x06, 0x40, 0x19, 0x5D, 0x21, 0xBE, 0x14, 0x37, 0x02, 0x3D,
+	0xFB, 0x55, 0xFE, 0xEC, 0x00, 0x72, 0x00, 0xF7, 0xFF, 0xFD, 0xFF,
+	0x0E, 0x00, 0xD1, 0x00, 0x4E, 0x00, 0x8C, 0xFC, 0x6A, 0xFC, 0x4C,
+	0x09, 0x13, 0x1C, 0x7E, 0x20, 0x3D, 0x11, 0xE8, 0xFF, 0x5F, 0xFB,
+	0x0C, 0xFF, 0xFB, 0x00, 0x4C, 0x00, 0xF7, 0xFF, 0xFA, 0xFF, 0x24,
+	0x00, 0xEE, 0x00, 0xD3, 0xFF, 0xEA, 0xFB, 0xA7, 0xFD, 0xBC, 0x0C,
+	0x68, 0x1E, 0xEF, 0x1E, 0xAF, 0x0D, 0x11, 0xFE, 0xC5, 0xFB, 0xAD,
+	0xFF, 0xF3, 0x00, 0x2C, 0x00, 0xFA, 0xFF, 0xF8, 0xFF, 0x42, 0x00,
+	0xFB, 0x00, 0x3A, 0xFF, 0x74, 0xFB, 0x5D, 0xFF, 0x48, 0x10, 0x24,
+	0x20, 0xC2, 0x1C, 0x35, 0x0A, 0xB3, 0xFC, 0x5D, 0xFC, 0x30, 0x00,
+	0xDA, 0x00, 0x13, 0x00, 0xFD, 0xFF, 0xF7, 0xFF, 0x67, 0x00, 0xF3,
+	0x00, 0x88, 0xFE, 0x3E, 0xFB, 0x8C, 0x01, 0xD0, 0x13, 0x33, 0x21,
+	0x0D, 0x1A, 0xEE, 0x06, 0xCD, 0xFB, 0x13, 0xFD, 0x92, 0x00, 0xB6,
+	0x00, 0x03, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFA, 0xFF, 0x90, 0x00,
+	0xCF, 0x00, 0xC7, 0xFD, 0x5B, 0xFB, 0x2B, 0x04, 0x31, 0x17, 0x8A,
+	0x21, 0xF0, 0x16, 0xF4, 0x03, 0x56, 0xFB, 0xD6, 0xFD, 0xD3, 0x00,
+	0x8D, 0x00, 0xFA, 0xFF, 0xFF, 0xFF, 0x04, 0x00, 0xB9, 0x00, 0x8C,
+	0x00, 0x05, 0xFD, 0xDB, 0xFB, 0x2C, 0x07, 0x47, 0x1A, 0x25, 0x21,
+	0x8B, 0x13, 0x5D, 0x01, 0x40, 0xFB, 0x97, 0xFE, 0xF5, 0x00, 0x64,
+	0x00, 0xF7, 0xFF, 0xFC, 0xFF, 0x15, 0x00, 0xDC, 0x00, 0x27, 0x00,
+	0x50, 0xFC, 0xCA, 0xFC, 0x78, 0x0A, 0xF2, 0x1C, 0x07, 0x20, 0x02,
+	0x10, 0x37, 0xFF, 0x7B, 0xFB, 0x47, 0xFF, 0xFB, 0x00, 0x40, 0x00,
+	0xF8, 0xFF, 0xF9, 0xFF, 0x2E, 0x00, 0xF5, 0x00, 0xA2, 0xFF, 0xBB,
+	0xFB, 0x31, 0xFE, 0xF5, 0x0D, 0x14, 0x1F, 0x3F, 0x1E, 0x77, 0x0C,
+	0x8A, 0xFD, 0xF5, 0xFB, 0xDE, 0xFF, 0xEC, 0x00, 0x22, 0x00, 0xFB,
+	0xFF, 0xF7, 0xFF, 0x4E, 0x00, 0xFB, 0x00, 0xFF, 0xFE, 0x59, 0xFB,
+	0x11, 0x00, 0x83, 0x11, 0x96, 0x20, 0xE0, 0x1B, 0x0B, 0x09, 0x56,
+	0xFC, 0x99, 0xFC, 0x56, 0x00, 0xCE, 0x00, 0x0D, 0x00, 0xFE, 0xFF,
+	0xF8, 0xFF, 0x75, 0x00, 0xEA, 0x00, 0x47, 0xFE, 0x3E, 0xFB, 0x69,
+	0x02, 0x02, 0x15, 0x66, 0x21, 0x04, 0x19, 0xDC, 0x05, 0x98, 0xFB,
+	0x56, 0xFD, 0xAD, 0x00, 0xA8, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00,
+	0x00, 0xFD, 0xFF, 0x9E, 0x00, 0xBC, 0x00, 0x83, 0xFD, 0x7B, 0xFB,
+	0x2B, 0x05, 0x4C, 0x18, 0x7C, 0x21, 0xCA, 0x15, 0x03, 0x03, 0x44,
+	0xFB, 0x1A, 0xFE, 0xE2, 0x00, 0x7E, 0x00, 0xF8, 0xFF, 0xFE, 0xFF,
+	0x09, 0x00, 0xC6, 0x00, 0x6D, 0x00, 0xC3, 0xFC, 0x20, 0xFC, 0x49,
+	0x08, 0x41, 0x1B, 0xD6, 0x20, 0x54, 0x12, 0x92, 0x00, 0x4C, 0xFB,
+	0xD6, 0xFE, 0xFA, 0x00, 0x57, 0x00, 0xF7, 0xFF, 0xFB, 0xFF, 0x1D,
+	0x00, 0xE6, 0x00, 0xFD, 0xFF, 0x18, 0xFC, 0x38, 0xFD, 0xA9, 0x0B,
+	0xC0, 0x1D, 0x7C, 0x1F, 0xC6, 0x0E, 0x95, 0xFE, 0x9F, 0xFB, 0x7E,
+	0xFF, 0xF8, 0x00, 0x35, 0x00, 0xF9, 0xFF, 0xF8, 0xFF, 0x38, 0x00,
+	0xF9, 0x00, 0x6C, 0xFF, 0x92, 0xFB, 0xC9, 0xFE, 0x2F, 0x0F, 0xAD,
+	0x1F, 0x7D, 0x1D, 0x42, 0x0B, 0x12, 0xFD, 0x2A, 0xFC, 0x0B, 0x00,
+	0xE3, 0x00, 0x1A, 0x00, 0xFC, 0xFF, 0xF7, 0xFF, 0x5B, 0x00, 0xF8,
+	0x00, 0xC1, 0xFE, 0x47, 0xFB, 0xD4, 0x00, 0xBC, 0x12, 0xF3, 0x20,
+	0xEF, 0x1A, 0xE9, 0x07, 0x08, 0xFC, 0xD9, 0xFC, 0x78, 0x00, 0xC2,
+	0x00, 0x07, 0x00, 0xFF, 0xFF, 0xF9, 0xFF, 0x83, 0x00, 0xDD, 0x00,
+	0x04, 0xFE, 0x49, 0xFB, 0x52, 0x03, 0x2D, 0x16, 0x83, 0x21, 0xEF,
+	0x17, 0xD5, 0x04, 0x6F, 0xFB, 0x9A, 0xFD, 0xC3, 0x00, 0x9A, 0x00,
+	0xFC, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xAD, 0x00, 0xA4,
+	0x00, 0x40, 0xFD, 0xA8, 0xFB, 0x36, 0x06, 0x5E, 0x19, 0x58, 0x21,
+	0x9C, 0x14, 0x1E, 0x02, 0x3D, 0xFB, 0x5D, 0xFE, 0xED, 0x00, 0x70,
+	0x00, 0xF7, 0xFF, 0xFD, 0xFF, 0x0F, 0x00, 0xD2, 0x00, 0x4A, 0x00,
+	0x85, 0xFC, 0x74, 0xFC, 0x6D, 0x09, 0x2D, 0x1C, 0x72, 0x20, 0x1A,
+	0x11, 0xD4, 0xFF, 0x61, 0xFB, 0x13, 0xFF, 0xFC, 0x00, 0x4A, 0x00,
+	0xF7, 0xFF, 0xFA, 0xFF, 0x25, 0x00, 0xEF, 0x00, 0xCE, 0xFF, 0xE4,
+	0xFB, 0xB5, 0xFD, 0xDE, 0x0C, 0x7C, 0x1E, 0xDD, 0x1E, 0x8C, 0x0D,
+	0x01, 0xFE, 0xCA, 0xFB, 0xB3, 0xFF, 0xF3, 0x00, 0x2B, 0x00, 0xFA,
+	0xFF, 0xF8, 0xFF, 0x44, 0x00, 0xFB, 0x00, 0x34, 0xFF, 0x71, 0xFB,
+	0x71, 0xFF, 0x6B, 0x10, 0x32, 0x20, 0xA9, 0x1C, 0x13, 0x0A, 0xA8,
+	0xFC, 0x63, 0xFC, 0x35, 0x00, 0xD9, 0x00, 0x12, 0x00, 0xFD, 0xFF,
+	0xF7, 0xFF, 0x69, 0x00, 0xF2, 0x00, 0x81, 0xFE, 0x3E, 0xFB, 0xA4,
+	0x01, 0xF2, 0x13, 0x3A, 0x21, 0xF0, 0x19, 0xCF, 0x06, 0xC7, 0xFB,
+	0x1B, 0xFD, 0x96, 0x00, 0xB4, 0x00, 0x02, 0x00, 0xFF, 0xFF, 0x00,
+	0x00, 0xFB, 0xFF, 0x92, 0x00, 0xCD, 0x00, 0xC0, 0xFD, 0x5E, 0xFB,
+	0x47, 0x04, 0x51, 0x17, 0x8A, 0x21, 0xD0, 0x16, 0xD9, 0x03, 0x53,
+	0xFB, 0xDE, 0xFD, 0xD5, 0x00, 0x8B, 0x00, 0xFA, 0xFF, 0xFF, 0xFF,
+	0x04, 0x00, 0xBA, 0x00, 0x89, 0x00, 0xFD, 0xFC, 0xE2, 0xFB, 0x4B,
+	0x07, 0x63, 0x1A, 0x1D, 0x21, 0x69, 0x13, 0x46, 0x01, 0x41, 0xFB,
+	0x9E, 0xFE, 0xF5, 0x00, 0x63, 0x00, 0xF7, 0xFF, 0xFC, 0xFF, 0x16,
+	0x00, 0xDD, 0x00, 0x23, 0x00, 0x49, 0xFC, 0xD5, 0xFC, 0x99, 0x0A,
+	0x09, 0x1D, 0xF9, 0x1F, 0xDF, 0x0F, 0x24, 0xFF, 0x7F, 0xFB, 0x4D,
+	0xFF, 0xFB, 0x00, 0x3F, 0x00, 0xF8, 0xFF, 0xF9, 0xFF, 0x2F, 0x00,
+	0xF5, 0x00, 0x9C, 0xFF, 0xB6, 0xFB, 0x41, 0xFE, 0x17, 0x0E, 0x26,
+	0x1F, 0x2B, 0x1E, 0x54, 0x0C, 0x7C, 0xFD, 0xFA, 0xFB, 0xE3, 0xFF,
+	0xEB, 0x00, 0x21, 0x00, 0xFB, 0xFF, 0xF7, 0xFF, 0x50, 0x00, 0xFB,
+	0x00, 0xF8, 0xFE, 0x57, 0xFB, 0x26, 0x00, 0xA6, 0x11, 0xA1, 0x20,
+	0xC6, 0x1B, 0xEA, 0x08, 0x4D, 0xFC, 0xA0, 0xFC, 0x5A, 0x00, 0xCD,
+	0x00, 0x0C, 0x00, 0xFE, 0xFF, 0xF8, 0xFF, 0x77, 0x00, 0xE9, 0x00,
+	0x3F, 0xFE, 0x3F, 0xFB, 0x82, 0x02, 0x23, 0x15, 0x6B, 0x21, 0xE5,
+	0x18, 0xBE, 0x05, 0x93, 0xFB, 0x5E, 0xFD, 0xAF, 0x00, 0xA6, 0x00,
+	0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0xA0, 0x00, 0xB9,
+	0x00, 0x7C, 0xFD, 0x80, 0xFB, 0x48, 0x05, 0x6B, 0x18, 0x79, 0x21,
+	0xA9, 0x15, 0xE9, 0x02, 0x43, 0xFB, 0x21, 0xFE, 0xE3, 0x00, 0x7D,
+	0x00, 0xF8, 0xFF, 0xFE, 0xFF, 0x09, 0x00, 0xC7, 0x00, 0x69, 0x00,
+	0xBC, 0xFC, 0x29, 0xFC, 0x69, 0x08, 0x5C, 0x1B, 0xCC, 0x20, 0x32,
+	0x12, 0x7C, 0x00, 0x4E, 0xFB, 0xDD, 0xFE, 0xFA, 0x00, 0x56, 0x00,
+	0xF7, 0xFF, 0xFB, 0xFF, 0x1D, 0x00, 0xE7, 0x00, 0xF8, 0xFF, 0x12,
+	0xFC, 0x45, 0xFD, 0xCB, 0x0B, 0xD6, 0x1D, 0x6C, 0x1F, 0xA3, 0x0E,
+	0x84, 0xFE, 0xA4, 0xFB, 0x84, 0xFF, 0xF7, 0x00, 0x34, 0x00, 0xF9,
+	0xFF, 0xF8, 0xFF, 0x3A, 0x00, 0xFA, 0x00, 0x66, 0xFF, 0x8E, 0xFB,
+	0xDB, 0xFE, 0x53, 0x0F, 0xBD, 0x1F, 0x66, 0x1D, 0x21, 0x0B, 0x05,
+	0xFD, 0x30, 0xFC, 0x10, 0x00, 0xE2, 0x00, 0x19, 0x00, 0xFC, 0xFF,
+	0xF7, 0xFF, 0x5D, 0x00, 0xF8, 0x00, 0xBA, 0xFE, 0x46, 0xFB, 0xEA,
+	0x00, 0xDF, 0x12, 0xFC, 0x20, 0xD3, 0x1A, 0xC9, 0x07, 0x00, 0xFC,
+	0xE0, 0xFC, 0x7B, 0x00, 0xC0, 0x00, 0x07, 0x00, 0xFF, 0xFF, 0xF9,
+	0xFF, 0x85, 0x00, 0xDC, 0x00, 0xFC, 0xFD, 0x4A, 0xFB, 0x6C, 0x03,
+	0x4E, 0x16, 0x85, 0x21, 0xCF, 0x17, 0xB8, 0x04, 0x6C, 0xFB, 0xA2,
+	0xFD, 0xC5, 0x00, 0x98, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0xFF, 0xFF,
+	0x01, 0x00, 0xAE, 0x00, 0xA1, 0x00, 0x38, 0xFD, 0xAE, 0xFB, 0x54,
+	0x06, 0x7C, 0x19, 0x53, 0x21, 0x7B, 0x14, 0x05, 0x02, 0x3D, 0xFB,
+	0x64, 0xFE, 0xEE, 0x00, 0x6F, 0x00, 0xF7, 0xFF, 0xFD, 0xFF, 0x0F,
+	0x00, 0xD4, 0x00, 0x46, 0x00, 0x7E, 0xFC, 0x7E, 0xFC, 0x8E, 0x09,
+	0x46, 0x1C, 0x66, 0x20, 0xF7, 0x10, 0xC0, 0xFF, 0x64, 0xFB, 0x1A,
+	0xFF, 0xFC, 0x00, 0x49, 0x00, 0xF7, 0xFF, 0xFA, 0xFF, 0x26, 0x00,
+	0xF0, 0x00, 0xC9, 0xFF, 0xDF, 0xFB, 0xC4, 0xFD, 0x01, 0x0D, 0x90,
+	0x1E, 0xCA, 0x1E, 0x69, 0x0D, 0xF1, 0xFD, 0xCF, 0xFB, 0xB8, 0xFF,
+	0xF2, 0x00, 0x29, 0x00, 0xFA, 0xFF, 0xF7, 0xFF, 0x45, 0x00, 0xFC,
+	0x00, 0x2D, 0xFF, 0x6D, 0xFB, 0x84, 0xFF, 0x8E, 0x10, 0x3F, 0x20,
+	0x91, 0x1C, 0xF2, 0x09, 0x9D, 0xFC, 0x6A, 0xFC, 0x39, 0x00, 0xD7,
+	0x00, 0x12, 0x00, 0xFD, 0xFF, 0xF7, 0xFF, 0x6A, 0x00, 0xF1, 0x00,
+	0x7A, 0xFE, 0x3D, 0xFB, 0xBC, 0x01, 0x14, 0x14, 0x41, 0x21, 0xD4,
+	0x19, 0xB0, 0x06, 0xC0, 0xFB, 0x22, 0xFD, 0x99, 0x00, 0xB3, 0x00,
+	0x02, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFB, 0xFF, 0x93, 0x00, 0xCB,
+	0x00, 0xB8, 0xFD, 0x61, 0xFB, 0x63, 0x04, 0x71, 0x17, 0x89, 0x21,
+	0xB0, 0x16, 0xBD, 0x03, 0x51, 0xFB, 0xE6, 0xFD, 0xD7, 0x00, 0x8A,
+	0x00, 0xFA, 0xFF, 0xFF, 0xFF, 0x05, 0x00, 0xBC, 0x00, 0x86, 0x00,
+	0xF6, 0xFC, 0xE9, 0xFB, 0x6A, 0x07, 0x80, 0x1A, 0x15, 0x21, 0x47,
+	0x13, 0x2F, 0x01, 0x42, 0xFB, 0xA5, 0xFE, 0xF6, 0x00, 0x61, 0x00,
+	0xF7, 0xFF, 0xFC, 0xFF, 0x16, 0x00, 0xDF, 0x00, 0x1E, 0x00, 0x43,
+	0xFC, 0xE1, 0xFC, 0xBB, 0x0A, 0x21, 0x1D, 0xEA, 0x1F, 0xBC, 0x0F,
+	0x12, 0xFF, 0x82, 0xFB, 0x54, 0xFF, 0xFA, 0x00, 0x3D, 0x00, 0xF8,
+	0xFF, 0xF9, 0xFF, 0x30, 0x00, 0xF6, 0x00, 0x96, 0xFF, 0xB1, 0xFB,
+	0x51, 0xFE, 0x3A, 0x0E, 0x38, 0x1F, 0x16, 0x1E, 0x32, 0x0C, 0x6E,
+	0xFD, 0x00, 0xFC, 0xE8, 0xFF, 0xEA, 0x00, 0x20, 0x00, 0xFB, 0xFF,
+	0xF7, 0xFF, 0x51, 0x00, 0xFB, 0x00, 0xF1, 0xFE, 0x54, 0xFB, 0x3B,
+	0x00, 0xC9, 0x11, 0xAD, 0x20, 0xAC, 0x1B, 0xCA, 0x08, 0x44, 0xFC,
+	0xA7, 0xFC, 0x5E, 0x00, 0xCC, 0x00, 0x0B, 0x00, 0xFE, 0xFF, 0xF8,
+	0xFF, 0x78, 0x00, 0xE7, 0x00, 0x38, 0xFE, 0x40, 0xFB, 0x9B, 0x02,
+	0x45, 0x15, 0x6F, 0x21, 0xC7, 0x18, 0xA1, 0x05, 0x8E, 0xFB, 0x65,
+	0xFD, 0xB2, 0x00, 0xA5, 0x00, 0xFE, 0xFF, 0x00, 0x00, 0x00, 0x00,
+	0xFE, 0xFF, 0xA2, 0x00, 0xB7, 0x00, 0x74, 0xFD, 0x84, 0xFB, 0x66,
+	0x05, 0x8A, 0x18, 0x76, 0x21, 0x87, 0x15, 0xCF, 0x02, 0x41, 0xFB,
+	0x29, 0xFE, 0xE5, 0x00, 0x7B, 0x00, 0xF8, 0xFF, 0xFE, 0xFF, 0x0A,
+	0x00, 0xC9, 0x00, 0x66, 0x00, 0xB5, 0xFC, 0x32, 0xFC, 0x89, 0x08,
+	0x77, 0x1B, 0xC2, 0x20, 0x0F, 0x12, 0x66, 0x00, 0x50, 0xFB, 0xE4,
+	0xFE, 0xFA, 0x00, 0x54, 0x00, 0xF7, 0xFF, 0xFB, 0xFF, 0x1E, 0x00,
+	0xE8, 0x00, 0xF3, 0xFF, 0x0C, 0xFC, 0x53, 0xFD, 0xED, 0x0B, 0xEB,
+	0x1D, 0x5A, 0x1F, 0x80, 0x0E, 0x73, 0xFE, 0xA8, 0xFB, 0x8A, 0xFF,
+	0xF7, 0x00, 0x32, 0x00, 0xF9, 0xFF, 0xF8, 0xFF, 0x3B, 0x00, 0xFA,
+	0x00, 0x60, 0xFF, 0x8A, 0xFB, 0xED, 0xFE, 0x76, 0x0F, 0xCC, 0x1F,
+	0x4F, 0x1D, 0xFF, 0x0A, 0xF9, 0xFC, 0x36, 0xFC, 0x15, 0x00, 0xE1,
+	0x00, 0x18, 0x00, 0xFC, 0xFF, 0xF7, 0xFF, 0x5E, 0x00, 0xF7, 0x00,
+	0xB3, 0xFE, 0x44, 0xFB, 0x01, 0x01, 0x02, 0x13, 0x04, 0x21, 0xB8,
+	0x1A, 0xA9, 0x07, 0xF8, 0xFB, 0xE7, 0xFC, 0x7F, 0x00, 0xBF, 0x00,
+	0x06, 0x00, 0xFF, 0xFF, 0xF9, 0xFF, 0x86, 0x00, 0xDA, 0x00, 0xF5,
+	0xFD, 0x4C, 0xFB, 0x87, 0x03, 0x6E, 0x16, 0x86, 0x21, 0xB0, 0x17,
+	0x9C, 0x04, 0x68, 0xFB, 0xA9, 0xFD, 0xC7, 0x00, 0x96, 0x00, 0xFB,
+	0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x01, 0x00, 0xB0, 0x00, 0x9F, 0x00,
+	0x31, 0xFD, 0xB4, 0xFB, 0x73, 0x06, 0x99, 0x19, 0x4D, 0x21, 0x59,
+	0x14, 0xED, 0x01, 0x3D, 0xFB, 0x6B, 0xFE, 0xEF, 0x00, 0x6D, 0x00,
+	0xF7, 0xFF, 0xFD, 0xFF, 0x10, 0x00, 0xD5, 0x00, 0x42, 0x00, 0x77,
+	0xFC, 0x88, 0xFC, 0xAF, 0x09, 0x5F, 0x1C, 0x59, 0x20, 0xD4, 0x10,
+	0xAC, 0xFF, 0x67, 0xFB, 0x20, 0xFF, 0xFC, 0x00, 0x48, 0x00, 0xF7,
+	0xFF, 0xFA, 0xFF, 0x27, 0x00, 0xF0, 0x00, 0xC3, 0xFF, 0xD9, 0xFB,
+	0xD3, 0xFD, 0x24, 0x0D, 0xA3, 0x1E, 0xB7, 0x1E, 0x46, 0x0D, 0xE2,
+	0xFD, 0xD4, 0xFB, 0xBE, 0xFF, 0xF1, 0x00, 0x28, 0x00, 0xFA, 0xFF,
+	0xF7, 0xFF, 0x46, 0x00, 0xFC, 0x00, 0x27, 0xFF, 0x6A, 0xFB, 0x98,
+	0xFF, 0xB1, 0x10, 0x4C, 0x20, 0x78, 0x1C, 0xD1, 0x09, 0x93, 0xFC,
+	0x71, 0xFC, 0x3D, 0x00, 0xD6, 0x00, 0x11, 0x00, 0xFD, 0xFF, 0xF7,
+	0xFF, 0x6C, 0x00, 0xF0, 0x00, 0x72, 0xFE, 0x3D, 0xFB, 0xD4, 0x01,
+	0x36, 0x14, 0x47, 0x21, 0xB6, 0x19, 0x91, 0x06, 0xBA, 0xFB, 0x29,
+	0xFD, 0x9C, 0x00, 0xB1, 0x00, 0x02, 0x00, 0xFF, 0xFF, 0x00, 0x00,
+	0xFB, 0xFF, 0x95, 0x00, 0xC9, 0x00, 0xB1, 0xFD, 0x65, 0xFB, 0x80,
+	0x04, 0x90, 0x17, 0x88, 0x21, 0x8F, 0x16, 0xA2, 0x03, 0x4E, 0xFB,
+	0xED, 0xFD, 0xD9, 0x00, 0x88, 0x00, 0xF9, 0xFF, 0xFF, 0xFF, 0x05,
+	0x00, 0xBD, 0x00, 0x82, 0x00, 0xEF, 0xFC, 0xF0, 0xFB, 0x8A, 0x07,
+	0x9C, 0x1A, 0x0D, 0x21, 0x24, 0x13, 0x18, 0x01, 0x43, 0xFB, 0xAC,
+	0xFE, 0xF7, 0x00, 0x60, 0x00, 0xF7, 0xFF, 0xFC, 0xFF, 0x17, 0x00,
+	0xE0, 0x00, 0x1A, 0x00, 0x3D, 0xFC, 0xED, 0xFC, 0xDD, 0x0A, 0x38,
+	0x1D, 0xDB, 0x1F, 0x99, 0x0F, 0xFF, 0xFE, 0x86, 0xFB, 0x5A, 0xFF,
+	0xFA, 0x00, 0x3C, 0x00, 0xF8, 0xFF, 0xF9, 0xFF, 0x31, 0x00, 0xF6,
+	0x00, 0x90, 0xFF, 0xAD, 0xFB, 0x62, 0xFE, 0x5D, 0x0E, 0x49, 0x1F,
+	0x01, 0x1E, 0x10, 0x0C, 0x60, 0xFD, 0x06, 0xFC, 0xEE, 0xFF, 0xE9,
+	0x00, 0x1F, 0x00, 0xFB, 0xFF, 0xF7, 0xFF, 0x53, 0x00, 0xFB, 0x00,
+	0xEB, 0xFE, 0x52, 0xFB, 0x51, 0x00, 0xEC, 0x11, 0xB7, 0x20, 0x91,
+	0x1B, 0xA9, 0x08, 0x3B, 0xFC, 0xAE, 0xFC, 0x62, 0x00, 0xCA, 0x00,
+	0x0B, 0x00, 0xFE, 0xFF, 0xF8, 0xFF, 0x7A, 0x00, 0xE6, 0x00, 0x30,
+	0xFE, 0x40, 0xFB, 0xB5, 0x02, 0x66, 0x15, 0x73, 0x21, 0xA9, 0x18,
+	0x83, 0x05, 0x89, 0xFB, 0x6D, 0xFD, 0xB4, 0x00, 0xA3, 0x00, 0xFE,
+	0xFF, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xA3, 0x00, 0xB4, 0x00,
+	0x6D, 0xFD, 0x89, 0xFB, 0x83, 0x05, 0xA9, 0x18, 0x73, 0x21, 0x66,
+	0x15, 0xB5, 0x02, 0x40, 0xFB, 0x30, 0xFE, 0xE6, 0x00, 0x7A, 0x00,
+	0xF8, 0xFF, 0xFE, 0xFF, 0x0B, 0x00, 0xCA, 0x00, 0x62, 0x00, 0xAE,
+	0xFC, 0x3B, 0xFC, 0xA9, 0x08, 0x91, 0x1B, 0xB7, 0x20, 0xEC, 0x11,
+	0x51, 0x00, 0x52, 0xFB, 0xEB, 0xFE, 0xFB, 0x00, 0x53, 0x00, 0xF7,
+	0xFF, 0xFB, 0xFF, 0x1F, 0x00, 0xE9, 0x00, 0xEE, 0xFF, 0x06, 0xFC,
+	0x60, 0xFD, 0x10, 0x0C, 0x01, 0x1E, 0x49, 0x1F, 0x5D, 0x0E, 0x62,
+	0xFE, 0xAD, 0xFB, 0x90, 0xFF, 0xF6, 0x00, 0x31, 0x00, 0xF9, 0xFF,
+	0xF8, 0xFF, 0x3C, 0x00, 0xFA, 0x00, 0x5A, 0xFF, 0x86, 0xFB, 0xFF,
+	0xFE, 0x99, 0x0F, 0xDB, 0x1F, 0x38, 0x1D, 0xDD, 0x0A, 0xED, 0xFC,
+	0x3D, 0xFC, 0x1A, 0x00, 0xE0, 0x00, 0x17, 0x00, 0xFC, 0xFF, 0xF7,
+	0xFF, 0x60, 0x00, 0xF7, 0x00, 0xAC, 0xFE, 0x43, 0xFB, 0x18, 0x01,
+	0x24, 0x13, 0x0D, 0x21, 0x9C, 0x1A, 0x8A, 0x07, 0xF0, 0xFB, 0xEF,
+	0xFC, 0x82, 0x00, 0xBD, 0x00, 0x05, 0x00, 0xFF, 0xFF, 0xF9, 0xFF,
+	0x88, 0x00, 0xD9, 0x00, 0xED, 0xFD, 0x4E, 0xFB, 0xA2, 0x03, 0x8F,
+	0x16, 0x88, 0x21, 0x90, 0x17, 0x80, 0x04, 0x65, 0xFB, 0xB1, 0xFD,
+	0xC9, 0x00, 0x95, 0x00, 0xFB, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x02,
+	0x00, 0xB1, 0x00, 0x9C, 0x00, 0x29, 0xFD, 0xBA, 0xFB, 0x91, 0x06,
+	0xB6, 0x19, 0x47, 0x21, 0x36, 0x14, 0xD4, 0x01, 0x3D, 0xFB, 0x72,
+	0xFE, 0xF0, 0x00, 0x6C, 0x00, 0xF7, 0xFF, 0xFD, 0xFF, 0x11, 0x00,
+	0xD6, 0x00, 0x3D, 0x00, 0x71, 0xFC, 0x93, 0xFC, 0xD1, 0x09, 0x78,
+	0x1C, 0x4C, 0x20, 0xB1, 0x10, 0x98, 0xFF, 0x6A, 0xFB, 0x27, 0xFF,
+	0xFC, 0x00, 0x46, 0x00, 0xF7, 0xFF, 0xFA, 0xFF, 0x28, 0x00, 0xF1,
+	0x00, 0xBE, 0xFF, 0xD4, 0xFB, 0xE2, 0xFD, 0x46, 0x0D, 0xB7, 0x1E,
+	0xA3, 0x1E, 0x24, 0x0D, 0xD3, 0xFD, 0xD9, 0xFB, 0xC3, 0xFF, 0xF0,
+	0x00, 0x27, 0x00, 0xFA, 0xFF, 0xF7, 0xFF, 0x48, 0x00, 0xFC, 0x00,
+	0x20, 0xFF, 0x67, 0xFB, 0xAC, 0xFF, 0xD4, 0x10, 0x59, 0x20, 0x5F,
+	0x1C, 0xAF, 0x09, 0x88, 0xFC, 0x77, 0xFC, 0x42, 0x00, 0xD5, 0x00,
+	0x10, 0x00, 0xFD, 0xFF, 0xF7, 0xFF, 0x6D, 0x00, 0xEF, 0x00, 0x6B,
+	0xFE, 0x3D, 0xFB, 0xED, 0x01, 0x59, 0x14, 0x4D, 0x21, 0x99, 0x19,
+	0x73, 0x06, 0xB4, 0xFB, 0x31, 0xFD, 0x9F, 0x00, 0xB0, 0x00, 0x01,
+	0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFB, 0xFF, 0x96, 0x00, 0xC7, 0x00,
+	0xA9, 0xFD, 0x68, 0xFB, 0x9C, 0x04, 0xB0, 0x17, 0x86, 0x21, 0x6E,
+	0x16, 0x87, 0x03, 0x4C, 0xFB, 0xF5, 0xFD, 0xDA, 0x00, 0x86, 0x00,
+	0xF9, 0xFF, 0xFF, 0xFF, 0x06, 0x00, 0xBF, 0x00, 0x7F, 0x00, 0xE7,
+	0xFC, 0xF8, 0xFB, 0xA9, 0x07, 0xB8, 0x1A, 0x04, 0x21, 0x02, 0x13,
+	0x01, 0x01, 0x44, 0xFB, 0xB3, 0xFE, 0xF7, 0x00, 0x5E, 0x00, 0xF7,
+	0xFF, 0xFC, 0xFF, 0x18, 0x00, 0xE1, 0x00, 0x15, 0x00, 0x36, 0xFC,
+	0xF9, 0xFC, 0xFF, 0x0A, 0x4F, 0x1D, 0xCC, 0x1F, 0x76, 0x0F, 0xED,
+	0xFE, 0x8A, 0xFB, 0x60, 0xFF, 0xFA, 0x00, 0x3B, 0x00, 0xF8, 0xFF,
+	0xF9, 0xFF, 0x32, 0x00, 0xF7, 0x00, 0x8A, 0xFF, 0xA8, 0xFB, 0x73,
+	0xFE, 0x80, 0x0E, 0x5A, 0x1F, 0xEB, 0x1D, 0xED, 0x0B, 0x53, 0xFD,
+	0x0C, 0xFC, 0xF3, 0xFF, 0xE8, 0x00, 0x1E, 0x00, 0xFB, 0xFF, 0xF7,
+	0xFF, 0x54, 0x00, 0xFA, 0x00, 0xE4, 0xFE, 0x50, 0xFB, 0x66, 0x00,
+	0x0F, 0x12, 0xC2, 0x20, 0x77, 0x1B, 0x89, 0x08, 0x32, 0xFC, 0xB5,
+	0xFC, 0x66, 0x00, 0xC9, 0x00, 0x0A, 0x00, 0xFE, 0xFF, 0xF8, 0xFF,
+	0x7B, 0x00, 0xE5, 0x00, 0x29, 0xFE, 0x41, 0xFB, 0xCF, 0x02, 0x87,
+	0x15, 0x76, 0x21, 0x8A, 0x18, 0x66, 0x05, 0x84, 0xFB, 0x74, 0xFD,
+	0xB7, 0x00, 0xA2, 0x00, 0xFE, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFE,
+	0xFF, 0xA5, 0x00, 0xB2, 0x00, 0x65, 0xFD, 0x8E, 0xFB, 0xA1, 0x05,
+	0xC7, 0x18, 0x6F, 0x21, 0x45, 0x15, 0x9B, 0x02, 0x40, 0xFB, 0x38,
+	0xFE, 0xE7, 0x00, 0x78, 0x00, 0xF8, 0xFF, 0xFE, 0xFF, 0x0B, 0x00,
+	0xCC, 0x00, 0x5E, 0x00, 0xA7, 0xFC, 0x44, 0xFC, 0xCA, 0x08, 0xAC,
+	0x1B, 0xAD, 0x20, 0xC9, 0x11, 0x3B, 0x00, 0x54, 0xFB, 0xF1, 0xFE,
+	0xFB, 0x00, 0x51, 0x00, 0xF7, 0xFF, 0xFB, 0xFF, 0x20, 0x00, 0xEA,
+	0x00, 0xE8, 0xFF, 0x00, 0xFC, 0x6E, 0xFD, 0x32, 0x0C, 0x16, 0x1E,
+	0x38, 0x1F, 0x3A, 0x0E, 0x51, 0xFE, 0xB1, 0xFB, 0x96, 0xFF, 0xF6,
+	0x00, 0x30, 0x00, 0xF9, 0xFF, 0xF8, 0xFF, 0x3D, 0x00, 0xFA, 0x00,
+	0x54, 0xFF, 0x82, 0xFB, 0x12, 0xFF, 0xBC, 0x0F, 0xEA, 0x1F, 0x21,
+	0x1D, 0xBB, 0x0A, 0xE1, 0xFC, 0x43, 0xFC, 0x1E, 0x00, 0xDF, 0x00,
+	0x16, 0x00, 0xFC, 0xFF, 0xF7, 0xFF, 0x61, 0x00, 0xF6, 0x00, 0xA5,
+	0xFE, 0x42, 0xFB, 0x2F, 0x01, 0x47, 0x13, 0x15, 0x21, 0x80, 0x1A,
+	0x6A, 0x07, 0xE9, 0xFB, 0xF6, 0xFC, 0x86, 0x00, 0xBC, 0x00, 0x05,
+	0x00, 0xFF, 0xFF, 0xFA, 0xFF, 0x8A, 0x00, 0xD7, 0x00, 0xE6, 0xFD,
+	0x51, 0xFB, 0xBD, 0x03, 0xB0, 0x16, 0x89, 0x21, 0x71, 0x17, 0x63,
+	0x04, 0x61, 0xFB, 0xB8, 0xFD, 0xCB, 0x00, 0x93, 0x00, 0xFB, 0xFF,
+	0x00, 0x00, 0xFF, 0xFF, 0x02, 0x00, 0xB3, 0x00, 0x99, 0x00, 0x22,
+	0xFD, 0xC0, 0xFB, 0xB0, 0x06, 0xD4, 0x19, 0x41, 0x21, 0x14, 0x14,
+	0xBC, 0x01, 0x3D, 0xFB, 0x7A, 0xFE, 0xF1, 0x00, 0x6A, 0x00, 0xF7,
+	0xFF, 0xFD, 0xFF, 0x12, 0x00, 0xD7, 0x00, 0x39, 0x00, 0x6A, 0xFC,
+	0x9D, 0xFC, 0xF2, 0x09, 0x91, 0x1C, 0x3F, 0x20, 0x8E, 0x10, 0x84,
+	0xFF, 0x6D, 0xFB, 0x2D, 0xFF, 0xFC, 0x00, 0x45, 0x00, 0xF7, 0xFF,
+	0xFA, 0xFF, 0x29, 0x00, 0xF2, 0x00, 0xB8, 0xFF, 0xCF, 0xFB, 0xF1,
+	0xFD, 0x69, 0x0D, 0xCA, 0x1E, 0x90, 0x1E, 0x01, 0x0D, 0xC4, 0xFD,
+	0xDF, 0xFB, 0xC9, 0xFF, 0xF0, 0x00, 0x26, 0x00, 0xFA, 0xFF, 0xF7,
+	0xFF, 0x49, 0x00, 0xFC, 0x00, 0x1A, 0xFF, 0x64, 0xFB, 0xC0, 0xFF,
+	0xF7, 0x10, 0x66, 0x20, 0x46, 0x1C, 0x8E, 0x09, 0x7E, 0xFC, 0x7E,
+	0xFC, 0x46, 0x00, 0xD4, 0x00, 0x0F, 0x00, 0xFD, 0xFF, 0xF7, 0xFF,
+	0x6F, 0x00, 0xEE, 0x00, 0x64, 0xFE, 0x3D, 0xFB, 0x05, 0x02, 0x7B,
+	0x14, 0x53, 0x21, 0x7C, 0x19, 0x54, 0x06, 0xAE, 0xFB, 0x38, 0xFD,
+	0xA1, 0x00, 0xAE, 0x00, 0x01, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFC,
+	0xFF, 0x98, 0x00, 0xC5, 0x00, 0xA2, 0xFD, 0x6C, 0xFB, 0xB8, 0x04,
+	0xCF, 0x17, 0x85, 0x21, 0x4E, 0x16, 0x6C, 0x03, 0x4A, 0xFB, 0xFC,
+	0xFD, 0xDC, 0x00, 0x85, 0x00, 0xF9, 0xFF, 0xFF, 0xFF, 0x07, 0x00,
+	0xC0, 0x00, 0x7B, 0x00, 0xE0, 0xFC, 0x00, 0xFC, 0xC9, 0x07, 0xD3,
+	0x1A, 0xFC, 0x20, 0xDF, 0x12, 0xEA, 0x00, 0x46, 0xFB, 0xBA, 0xFE,
+	0xF8, 0x00, 0x5D, 0x00, 0xF7, 0xFF, 0xFC, 0xFF, 0x19, 0x00, 0xE2,
+	0x00, 0x10, 0x00, 0x30, 0xFC, 0x05, 0xFD, 0x21, 0x0B, 0x66, 0x1D,
+	0xBD, 0x1F, 0x53, 0x0F, 0xDB, 0xFE, 0x8E, 0xFB, 0x66, 0xFF, 0xFA,
+	0x00, 0x3A, 0x00, 0xF8, 0xFF, 0xF9, 0xFF, 0x34, 0x00, 0xF7, 0x00,
+	0x84, 0xFF, 0xA4, 0xFB, 0x84, 0xFE, 0xA3, 0x0E, 0x6C, 0x1F, 0xD6,
+	0x1D, 0xCB, 0x0B, 0x45, 0xFD, 0x12, 0xFC, 0xF8, 0xFF, 0xE7, 0x00,
+	0x1D, 0x00, 0xFB, 0xFF, 0xF7, 0xFF, 0x56, 0x00, 0xFA, 0x00, 0xDD,
+	0xFE, 0x4E, 0xFB, 0x7C, 0x00, 0x32, 0x12, 0xCC, 0x20, 0x5C, 0x1B,
+	0x69, 0x08, 0x29, 0xFC, 0xBC, 0xFC, 0x69, 0x00, 0xC7, 0x00, 0x09,
+	0x00, 0xFE, 0xFF, 0xF8, 0xFF, 0x7D, 0x00, 0xE3, 0x00, 0x21, 0xFE,
+	0x43, 0xFB, 0xE9, 0x02, 0xA9, 0x15, 0x79, 0x21, 0x6B, 0x18, 0x48,
+	0x05, 0x80, 0xFB, 0x7C, 0xFD, 0xB9, 0x00, 0xA0, 0x00, 0xFD, 0xFF,
+	0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xA6, 0x00, 0xAF, 0x00, 0x5E,
+	0xFD, 0x93, 0xFB, 0xBE, 0x05, 0xE5, 0x18, 0x6B, 0x21, 0x23, 0x15,
+	0x82, 0x02, 0x3F, 0xFB, 0x3F, 0xFE, 0xE9, 0x00, 0x77, 0x00, 0xF8,
+	0xFF, 0xFE, 0xFF, 0x0C, 0x00, 0xCD, 0x00, 0x5A, 0x00, 0xA0, 0xFC,
+	0x4D, 0xFC, 0xEA, 0x08, 0xC6, 0x1B, 0xA1, 0x20, 0xA6, 0x11, 0x26,
+	0x00, 0x57, 0xFB, 0xF8, 0xFE, 0xFB, 0x00, 0x50, 0x00, 0xF7, 0xFF,
+	0xFB, 0xFF, 0x21, 0x00, 0xEB, 0x00, 0xE3, 0xFF, 0xFA, 0xFB, 0x7C,
+	0xFD, 0x54, 0x0C, 0x2B, 0x1E, 0x26, 0x1F, 0x17, 0x0E, 0x41, 0xFE,
+	0xB6, 0xFB, 0x9C, 0xFF, 0xF5, 0x00, 0x2F, 0x00, 0xF9, 0xFF, 0xF8,
+	0xFF, 0x3F, 0x00, 0xFB, 0x00, 0x4D, 0xFF, 0x7F, 0xFB, 0x24, 0xFF,
+	0xDF, 0x0F, 0xF9, 0x1F, 0x09, 0x1D, 0x99, 0x0A, 0xD5, 0xFC, 0x49,
+	0xFC, 0x23, 0x00, 0xDD, 0x00, 0x16, 0x00, 0xFC, 0xFF, 0xF7, 0xFF,
+	0x63, 0x00, 0xF5, 0x00, 0x9E, 0xFE, 0x41, 0xFB, 0x46, 0x01, 0x69,
+	0x13, 0x1D, 0x21, 0x63, 0x1A, 0x4B, 0x07, 0xE2, 0xFB, 0xFD, 0xFC,
+	0x89, 0x00, 0xBA, 0x00, 0x04, 0x00, 0xFF, 0xFF, 0xFA, 0xFF, 0x8B,
+	0x00, 0xD5, 0x00, 0xDE, 0xFD, 0x53, 0xFB, 0xD9, 0x03, 0xD0, 0x16,
+	0x8A, 0x21, 0x51, 0x17, 0x47, 0x04, 0x5E, 0xFB, 0xC0, 0xFD, 0xCD,
+	0x00, 0x92, 0x00, 0xFB, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x02, 0x00,
+	0xB4, 0x00, 0x96, 0x00, 0x1B, 0xFD, 0xC7, 0xFB, 0xCF, 0x06, 0xF0,
+	0x19, 0x3A, 0x21, 0xF2, 0x13, 0xA4, 0x01, 0x3E, 0xFB, 0x81, 0xFE,
+	0xF2, 0x00, 0x69, 0x00, 0xF7, 0xFF, 0xFD, 0xFF, 0x12, 0x00, 0xD9,
+	0x00, 0x35, 0x00, 0x63, 0xFC, 0xA8, 0xFC, 0x13, 0x0A, 0xA9, 0x1C,
+	0x32, 0x20, 0x6B, 0x10, 0x71, 0xFF, 0x71, 0xFB, 0x34, 0xFF, 0xFB,
+	0x00, 0x44, 0x00, 0xF8, 0xFF, 0xFA, 0xFF, 0x2B, 0x00, 0xF3, 0x00,
+	0xB3, 0xFF, 0xCA, 0xFB, 0x01, 0xFE, 0x8C, 0x0D, 0xDD, 0x1E, 0x7C,
+	0x1E, 0xDE, 0x0C, 0xB5, 0xFD, 0xE4, 0xFB, 0xCE, 0xFF, 0xEF, 0x00,
+	0x25, 0x00, 0xFA, 0xFF, 0xF7, 0xFF, 0x4A, 0x00, 0xFC, 0x00, 0x13,
+	0xFF, 0x61, 0xFB, 0xD4, 0xFF, 0x1A, 0x11, 0x72, 0x20, 0x2D, 0x1C,
+	0x6D, 0x09, 0x74, 0xFC, 0x85, 0xFC, 0x4A, 0x00, 0xD2, 0x00, 0x0F,
+	0x00, 0xFD, 0xFF, 0xF7, 0xFF, 0x70, 0x00, 0xED, 0x00, 0x5D, 0xFE,
+	0x3D, 0xFB, 0x1E, 0x02, 0x9C, 0x14, 0x58, 0x21, 0x5E, 0x19, 0x36,
+	0x06, 0xA8, 0xFB, 0x40, 0xFD, 0xA4, 0x00, 0xAD, 0x00, 0x00, 0x00,
+	0xFF, 0xFF, 0x00, 0x00, 0xFC, 0xFF, 0x9A, 0x00, 0xC3, 0x00, 0x9A,
+	0xFD, 0x6F, 0xFB, 0xD5, 0x04, 0xEF, 0x17, 0x83, 0x21, 0x2D, 0x16,
+	0x52, 0x03, 0x49, 0xFB, 0x04, 0xFE, 0xDD, 0x00, 0x83, 0x00, 0xF9,
+	0xFF, 0xFF, 0xFF, 0x07, 0x00, 0xC2, 0x00, 0x78, 0x00, 0xD9, 0xFC,
+	0x08, 0xFC, 0xE9, 0x07, 0xEF, 0x1A, 0xF3, 0x20, 0xBC, 0x12, 0xD4,
+	0x00, 0x47, 0xFB, 0xC1, 0xFE, 0xF8, 0x00, 0x5B, 0x00, 0xF7, 0xFF,
+	0xFC, 0xFF, 0x1A, 0x00, 0xE3, 0x00, 0x0B, 0x00, 0x2A, 0xFC, 0x12,
+	0xFD, 0x42, 0x0B, 0x7D, 0x1D, 0xAD, 0x1F, 0x2F, 0x0F, 0xC9, 0xFE,
+	0x92, 0xFB, 0x6C, 0xFF, 0xF9, 0x00, 0x38, 0x00, 0xF8, 0xFF, 0xF9,
+	0xFF, 0x35, 0x00, 0xF8, 0x00, 0x7E, 0xFF, 0x9F, 0xFB, 0x95, 0xFE,
+	0xC6, 0x0E, 0x7C, 0x1F, 0xC0, 0x1D, 0xA9, 0x0B, 0x38, 0xFD, 0x18,
+	0xFC, 0xFD, 0xFF, 0xE6, 0x00, 0x1D, 0x00, 0xFB, 0xFF, 0xF7, 0xFF,
+	0x57, 0x00, 0xFA, 0x00, 0xD6, 0xFE, 0x4C, 0xFB, 0x92, 0x00, 0x54,
+	0x12, 0xD6, 0x20, 0x41, 0x1B, 0x49, 0x08, 0x20, 0xFC, 0xC3, 0xFC,
+	0x6D, 0x00, 0xC6, 0x00, 0x09, 0x00, 0xFE, 0xFF, 0xF8, 0xFF, 0x7E,
+	0x00, 0xE2, 0x00, 0x1A, 0xFE, 0x44, 0xFB, 0x03, 0x03, 0xCA, 0x15,
+	0x7C, 0x21, 0x4C, 0x18, 0x2B, 0x05, 0x7B, 0xFB, 0x83, 0xFD, 0xBC,
+	0x00, 0x9E, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF,
+	0xA8, 0x00, 0xAD, 0x00, 0x56, 0xFD, 0x98, 0xFB, 0xDC, 0x05, 0x04,
+	0x19, 0x66, 0x21, 0x02, 0x15, 0x69, 0x02, 0x3E, 0xFB, 0x47, 0xFE,
+	0xEA, 0x00, 0x75, 0x00, 0xF8, 0xFF, 0xFE, 0xFF, 0x0D, 0x00, 0xCE,
+	0x00, 0x56, 0x00, 0x99, 0xFC, 0x56, 0xFC, 0x0B, 0x09, 0xE0, 0x1B,
+	0x96, 0x20, 0x83, 0x11, 0x11, 0x00, 0x59, 0xFB, 0xFF, 0xFE, 0xFB,
+	0x00, 0x4E, 0x00, 0xF7, 0xFF, 0xFB, 0xFF, 0x22, 0x00, 0xEC, 0x00,
+	0xDE, 0xFF, 0xF5, 0xFB, 0x8A, 0xFD, 0x77, 0x0C, 0x3F, 0x1E, 0x14,
+	0x1F, 0xF5, 0x0D, 0x31, 0xFE, 0xBB, 0xFB, 0xA2, 0xFF, 0xF5, 0x00,
+	0x2E, 0x00, 0xF9, 0xFF, 0xF8, 0xFF, 0x40, 0x00, 0xFB, 0x00, 0x47,
+	0xFF, 0x7B, 0xFB, 0x37, 0xFF, 0x02, 0x10, 0x07, 0x20, 0xF2, 0x1C,
+	0x78, 0x0A, 0xCA, 0xFC, 0x50, 0xFC, 0x27, 0x00, 0xDC, 0x00, 0x15,
+	0x00, 0xFC, 0xFF, 0xF7, 0xFF, 0x64, 0x00, 0xF5, 0x00, 0x97, 0xFE,
+	0x40, 0xFB, 0x5D, 0x01, 0x8B, 0x13, 0x25, 0x21, 0x47, 0x1A, 0x2C,
+	0x07, 0xDB, 0xFB, 0x05, 0xFD, 0x8C, 0x00, 0xB9, 0x00, 0x04, 0x00,
+	0xFF, 0xFF, 0xFA, 0xFF, 0x8D, 0x00, 0xD3, 0x00, 0xD6, 0xFD, 0x56,
+	0xFB, 0xF4, 0x03, 0xF0, 0x16, 0x8A, 0x21, 0x31, 0x17, 0x2B, 0x04,
+	0x5B, 0xFB, 0xC7, 0xFD, 0xCF, 0x00, 0x90, 0x00, 0xFA, 0xFF, 0x00,
+	0x00, 0xFF, 0xFF, 0x03, 0x00, 0xB6, 0x00, 0x92, 0x00, 0x13, 0xFD,
+	0xCD, 0xFB, 0xEE, 0x06, 0x0D, 0x1A, 0x33, 0x21, 0xD0, 0x13, 0x8C,
+	0x01, 0x3E, 0xFB, 0x88, 0xFE, 0xF3, 0x00, 0x67, 0x00, 0xF7, 0xFF,
+	0x06, 0x00, 0x1D, 0x00, 0x03, 0xFF, 0xFE, 0x00, 0xA1, 0x02, 0xA6,
+	0xF8, 0x56, 0x02, 0xA5, 0x28, 0xA5, 0x28, 0x56, 0x02, 0xA6, 0xF8,
+	0xA1, 0x02, 0xFE, 0x00, 0x03, 0xFF, 0x1D, 0x00, 0x06, 0x00, 0x00,
+	0x00, 0x21, 0x00, 0xA6, 0xFF, 0x3F, 0xFF, 0x0B, 0x03, 0x42, 0xFE,
+	0x3E, 0xF8, 0x7F, 0x15, 0xAC, 0x30, 0x7F, 0x15, 0x3E, 0xF8, 0x42,
+	0xFE, 0x0B, 0x03, 0x3F, 0xFF, 0xA6, 0xFF, 0x21, 0x00, 0x00, 0x00,
+	0xFA, 0xFF, 0xCE, 0xFF, 0x14, 0x01, 0x00, 0xFD, 0x35, 0x06, 0xD5,
+	0xF4, 0xDA, 0x15, 0x92, 0x40, 0xAE, 0xFE, 0xF3, 0xFC, 0x68, 0x03,
+	0x86, 0xFD, 0x51, 0x01, 0x8B, 0xFF, 0x11, 0x00, 0x01, 0x00, 0xEC,
+	0xFF, 0xF9, 0xFF, 0xC6, 0x00, 0x55, 0xFD, 0x35, 0x06, 0x90, 0xF3,
+	0xE5, 0x1C, 0x6B, 0x3D, 0x71, 0xFA, 0x34, 0xFF, 0x46, 0x02, 0xFF,
+	0xFD, 0x2D, 0x01, 0x90, 0xFF, 0x10, 0x00, 0x03, 0x00, 0xDB, 0xFF,
+	0x2D, 0x00, 0x60, 0x00, 0xE1, 0xFD, 0xCE, 0x05, 0xED, 0xF2, 0xF3,
+	0x23, 0x20, 0x39, 0x22, 0xF7, 0x44, 0x01, 0x1F, 0x01, 0x89, 0xFE,
+	0xFB, 0x00, 0x9C, 0xFF, 0x0D, 0x00, 0x06, 0x00, 0xC9, 0xFF, 0x68,
+	0x00, 0xE5, 0xFF, 0xA0, 0xFE, 0xFB, 0x04, 0x0C, 0xF3, 0xC5, 0x2A,
+	0xD8, 0x33, 0xC9, 0xF4, 0x0B, 0x03, 0x05, 0x00, 0x1A, 0xFF, 0xC1,
+	0x00, 0xAD, 0xFF, 0x0A, 0x00, 0x09, 0x00, 0xB5, 0xFF, 0xA5, 0x00,
+	0x5C, 0xFF, 0x8C, 0xFF, 0xBF, 0x03, 0x06, 0xF4, 0x22, 0x31, 0xC8,
+	0x2D, 0x63, 0xF3, 0x76, 0x04, 0x08, 0xFF, 0xA7, 0xFF, 0x84, 0x00,
+	0xC0, 0xFF, 0x07, 0x00, 0x0C, 0x00, 0xA4, 0xFF, 0xE1, 0x00, 0xCB,
+	0xFE, 0x9B, 0x00, 0x21, 0x02, 0xEE, 0xF5, 0xCD, 0x36, 0x24, 0x27,
+	0xE1, 0xF2, 0x7A, 0x05, 0x33, 0xFE, 0x2A, 0x00, 0x47, 0x00, 0xD3,
+	0xFF, 0x04, 0x00, 0x0F, 0x00, 0x95, 0xFF, 0x17, 0x01, 0x3D, 0xFE,
+	0xBD, 0x01, 0x30, 0x00, 0xCC, 0xF8, 0x92, 0x3B, 0x2A, 0x20, 0x2E,
+	0xF3, 0x12, 0x06, 0x8F, 0xFD, 0x9A, 0x00, 0x10, 0x00, 0xE5, 0xFF,
+	0x02, 0x00, 0x10, 0x00, 0x8C, 0xFF, 0x42, 0x01, 0xBB, 0xFD, 0xE4,
+	0x02, 0x01, 0xFE, 0x9C, 0xFC, 0x45, 0x3F, 0x16, 0x19, 0x2D, 0xF4,
+	0x41, 0x06, 0x21, 0xFD, 0xF3, 0x00, 0xE0, 0xFF, 0xF4, 0xFF, 0x01,
+	0x00, 0x10, 0x00, 0x8B, 0xFF, 0x5D, 0x01, 0x4F, 0xFD, 0xFB, 0x03,
+	0xB2, 0xFB, 0x53, 0x01, 0xC2, 0x41, 0x24, 0x12, 0xBA, 0xF5, 0x0F,
+	0x06, 0xE9, 0xFC, 0x33, 0x01, 0xBB, 0xFF, 0x00, 0x00, 0x00, 0x00,
+	0x0D, 0x00, 0x93, 0xFF, 0x63, 0x01, 0x04, 0xFD, 0xEF, 0x04, 0x62,
+	0xF9, 0xD7, 0x06, 0xF2, 0x42, 0x8D, 0x0B, 0xB0, 0xF7, 0x87, 0x05,
+	0xE6, 0xFC, 0x58, 0x01, 0xA0, 0xFF, 0x09, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x07, 0x00, 0xA5, 0xFF, 0x52, 0x01, 0xE2, 0xFC, 0xAD, 0x05,
+	0x35, 0xF7, 0x08, 0x0D, 0xCB, 0x42, 0x81, 0x05, 0xE8, 0xF9, 0xBB,
+	0x04, 0x12, 0xFD, 0x64, 0x01, 0x90, 0xFF, 0x0E, 0x00, 0x00, 0x00,
+	0xFE, 0xFF, 0xC2, 0xFF, 0x27, 0x01, 0xF1, 0xFC, 0x22, 0x06, 0x54,
+	0xF5, 0xB8, 0x13, 0x4A, 0x41, 0x29, 0x00, 0x3C, 0xFC, 0xBD, 0x03,
+	0x66, 0xFD, 0x58, 0x01, 0x8A, 0xFF, 0x11, 0x00, 0x01, 0x00, 0xF1,
+	0xFF, 0xEB, 0xFF, 0xE1, 0x00, 0x35, 0xFD, 0x40, 0x06, 0xE4, 0xF3,
+	0xB7, 0x1A, 0x85, 0x3E, 0xA6, 0xFB, 0x86, 0xFE, 0xA0, 0x02, 0xD7,
+	0xFD, 0x39, 0x01, 0x8E, 0xFF, 0x10, 0x00, 0x03, 0x00, 0xE1, 0xFF,
+	0x1C, 0x00, 0x82, 0x00, 0xB0, 0xFD, 0xF9, 0x05, 0x0C, 0xF3, 0xCB,
+	0x21, 0x8F, 0x3A, 0x0D, 0xF8, 0xA9, 0x00, 0x79, 0x01, 0x5D, 0xFE,
+	0x0B, 0x01, 0x98, 0xFF, 0x0E, 0x00, 0x05, 0x00, 0xCE, 0xFF, 0x55,
+	0x00, 0x0D, 0x00, 0x60, 0xFE, 0x48, 0x05, 0xEC, 0xF2, 0xB6, 0x28,
+	0x91, 0x35, 0x68, 0xF5, 0x88, 0x02, 0x5A, 0x00, 0xED, 0xFE, 0xD4,
+	0x00, 0xA8, 0xFF, 0x0B, 0x00, 0x08, 0x00, 0xBB, 0xFF, 0x92, 0x00,
+	0x87, 0xFF, 0x3F, 0xFF, 0x2B, 0x04, 0xA1, 0xF3, 0x3D, 0x2F, 0xB8,
+	0x2F, 0xB8, 0xF3, 0x11, 0x04, 0x52, 0xFF, 0x7C, 0xFF, 0x97, 0x00,
+	0xBA, 0xFF, 0x08, 0x00, 0x0B, 0x00, 0xA9, 0xFF, 0xCF, 0x00, 0xF8,
+	0xFE, 0x44, 0x00, 0xAA, 0x02, 0x3E, 0xF5, 0x24, 0x35, 0x3B, 0x29,
+	0xF2, 0xF2, 0x35, 0x05, 0x70, 0xFE, 0x03, 0x00, 0x5A, 0x00, 0xCD,
+	0xFF, 0x05, 0x00, 0x0E, 0x00, 0x99, 0xFF, 0x07, 0x01, 0x68, 0xFE,
+	0x63, 0x01, 0xD0, 0x00, 0xD0, 0xF7, 0x35, 0x3A, 0x55, 0x22, 0x02,
+	0xF3, 0xEF, 0x05, 0xBC, 0xFD, 0x7A, 0x00, 0x20, 0x00, 0xDF, 0xFF,
+	0x03, 0x00, 0x10, 0x00, 0x8E, 0xFF, 0x36, 0x01, 0xE1, 0xFD, 0x8A,
+	0x02, 0xB2, 0xFE, 0x56, 0xFB, 0x40, 0x3E, 0x42, 0x1B, 0xCE, 0xF3,
+	0x3E, 0x06, 0x3D, 0xFD, 0xDB, 0x00, 0xEE, 0xFF, 0xF0, 0xFF, 0x01,
+	0x00, 0x11, 0x00, 0x8A, 0xFF, 0x57, 0x01, 0x6D, 0xFD, 0xA8, 0x03,
+	0x69, 0xFC, 0xC8, 0xFF, 0x20, 0x41, 0x40, 0x14, 0x33, 0xF5, 0x28,
+	0x06, 0xF5, 0xFC, 0x22, 0x01, 0xC5, 0xFF, 0xFD, 0xFF, 0x00, 0x00,
+	0x0F, 0x00, 0x8F, 0xFF, 0x64, 0x01, 0x17, 0xFD, 0xA9, 0x04, 0x16,
+	0xFA, 0x10, 0x05, 0xB8, 0x42, 0x87, 0x0D, 0x0D, 0xF7, 0xB9, 0x05,
+	0xE2, 0xFC, 0x50, 0x01, 0xA7, 0xFF, 0x07, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x0A, 0x00, 0x9E, 0xFF, 0x5A, 0x01, 0xE8, 0xFC, 0x7A, 0x05,
+	0xDA, 0xF7, 0x10, 0x0B, 0xFB, 0x42, 0x4B, 0x07, 0x35, 0xF9, 0x00,
+	0x05, 0x00, 0xFD, 0x63, 0x01, 0x94, 0xFF, 0x0D, 0x00, 0x00, 0x00,
+	0x01, 0x00, 0xB8, 0xFF, 0x37, 0x01, 0xE7, 0xFC, 0x07, 0x06, 0xDE,
+	0xF5, 0x9F, 0x11, 0xE4, 0x41, 0xB8, 0x01, 0x84, 0xFB, 0x0F, 0x04,
+	0x48, 0xFD, 0x5E, 0x01, 0x8B, 0xFF, 0x10, 0x00, 0x01, 0x00, 0xF5,
+	0xFF, 0xDD, 0xFF, 0xF9, 0x00, 0x1B, 0xFD, 0x41, 0x06, 0x47, 0xF4,
+	0x8B, 0x18, 0x81, 0x3F, 0xF1, 0xFC, 0xD5, 0xFD, 0xFA, 0x02, 0xB2,
+	0xFD, 0x45, 0x01, 0x8C, 0xFF, 0x11, 0x00, 0x02, 0x00, 0xE6, 0xFF,
+	0x0C, 0x00, 0xA2, 0x00, 0x85, 0xFD, 0x1A, 0x06, 0x3C, 0xF3, 0x9F,
+	0x1F, 0xE6, 0x3B, 0x0E, 0xF9, 0x07, 0x00, 0xD4, 0x01, 0x33, 0xFE,
+	0x1B, 0x01, 0x94, 0xFF, 0x0F, 0x00, 0x04, 0x00, 0xD4, 0xFF, 0x43,
+	0x00, 0x33, 0x00, 0x25, 0xFE, 0x89, 0x05, 0xE0, 0xF2, 0x9C, 0x26,
+	0x33, 0x37, 0x1E, 0xF6, 0xFD, 0x01, 0xB0, 0x00, 0xC0, 0xFE, 0xE6,
+	0x00, 0xA2, 0xFF, 0x0C, 0x00, 0x07, 0x00, 0xC1, 0xFF, 0x7F, 0x00,
+	0xB2, 0xFF, 0xF6, 0xFE, 0x8E, 0x04, 0x51, 0xF3, 0x49, 0x2D, 0x98,
+	0x31, 0x23, 0xF4, 0xA2, 0x03, 0xA0, 0xFF, 0x51, 0xFF, 0xAA, 0x00,
+	0xB4, 0xFF, 0x09, 0x00, 0x0A, 0x00, 0xAE, 0xFF, 0xBD, 0x00, 0x25,
+	0xFF, 0xF1, 0xFF, 0x2B, 0x03, 0xA5, 0xF4, 0x68, 0x33, 0x48, 0x2B,
+	0x17, 0xF3, 0xE7, 0x04, 0xB1, 0xFE, 0xDB, 0xFF, 0x6C, 0x00, 0xC7,
+	0xFF, 0x06, 0x00, 0x0D, 0x00, 0x9E, 0xFF, 0xF7, 0x00, 0x94, 0xFE,
+	0x09, 0x01, 0x6A, 0x01, 0xEB, 0xF6, 0xC1, 0x38, 0x7D, 0x24, 0xE8,
+	0xF2, 0xC1, 0x05, 0xEE, 0xFD, 0x57, 0x00, 0x31, 0x00, 0xDA, 0xFF,
+	0x03, 0x00, 0x10, 0x00, 0x91, 0xFF, 0x29, 0x01, 0x09, 0xFE, 0x2F,
+	0x02, 0x5F, 0xFF, 0x27, 0xFA, 0x20, 0x3D, 0x70, 0x1D, 0x7D, 0xF3,
+	0x31, 0x06, 0x5E, 0xFD, 0xBF, 0x00, 0xFD, 0xFF, 0xEB, 0xFF, 0x02,
+	0x00, 0x11, 0x00, 0x8B, 0xFF, 0x4E, 0x01, 0x8E, 0xFD, 0x52, 0x03,
+	0x20, 0xFD, 0x52, 0xFE, 0x60, 0x40, 0x63, 0x16, 0xB7, 0xF4, 0x39,
+	0x06, 0x05, 0xFD, 0x0F, 0x01, 0xD1, 0xFF, 0xF9, 0xFF, 0x00, 0x00,
+	0x10, 0x00, 0x8D, 0xFF, 0x62, 0x01, 0x2E, 0xFD, 0x5E, 0x04, 0xCC,
+	0xFA, 0x5B, 0x03, 0x5E, 0x42, 0x8E, 0x0F, 0x71, 0xF6, 0xE4, 0x05,
+	0xE2, 0xFC, 0x45, 0x01, 0xAF, 0xFF, 0x04, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x0B, 0x00, 0x99, 0xFF, 0x60, 0x01, 0xF2, 0xFC, 0x40, 0x05,
+	0x85, 0xF8, 0x26, 0x09, 0x0C, 0x43, 0x26, 0x09, 0x85, 0xF8, 0x40,
+	0x05, 0xF2, 0xFC, 0x60, 0x01, 0x99, 0xFF, 0x0B, 0x00, 0x00, 0x00,
+	0x04, 0x00, 0xAF, 0xFF, 0x45, 0x01, 0xE2, 0xFC, 0xE4, 0x05, 0x71,
+	0xF6, 0x8E, 0x0F, 0x5E, 0x42, 0x5B, 0x03, 0xCC, 0xFA, 0x5E, 0x04,
+	0x2E, 0xFD, 0x62, 0x01, 0x8D, 0xFF, 0x10, 0x00, 0x00, 0x00, 0xF9,
+	0xFF, 0xD1, 0xFF, 0x0F, 0x01, 0x05, 0xFD, 0x39, 0x06, 0xB7, 0xF4,
+	0x63, 0x16, 0x60, 0x40, 0x52, 0xFE, 0x20, 0xFD, 0x52, 0x03, 0x8E,
+	0xFD, 0x4E, 0x01, 0x8B, 0xFF, 0x11, 0x00, 0x02, 0x00, 0xEB, 0xFF,
+	0xFD, 0xFF, 0xBF, 0x00, 0x5E, 0xFD, 0x31, 0x06, 0x7D, 0xF3, 0x70,
+	0x1D, 0x20, 0x3D, 0x27, 0xFA, 0x5F, 0xFF, 0x2F, 0x02, 0x09, 0xFE,
+	0x29, 0x01, 0x91, 0xFF, 0x10, 0x00, 0x03, 0x00, 0xDA, 0xFF, 0x31,
+	0x00, 0x57, 0x00, 0xEE, 0xFD, 0xC1, 0x05, 0xE8, 0xF2, 0x7D, 0x24,
+	0xC1, 0x38, 0xEB, 0xF6, 0x6A, 0x01, 0x09, 0x01, 0x94, 0xFE, 0xF7,
+	0x00, 0x9E, 0xFF, 0x0D, 0x00, 0x06, 0x00, 0xC7, 0xFF, 0x6C, 0x00,
+	0xDB, 0xFF, 0xB1, 0xFE, 0xE7, 0x04, 0x17, 0xF3, 0x48, 0x2B, 0x68,
+	0x33, 0xA5, 0xF4, 0x2B, 0x03, 0xF1, 0xFF, 0x25, 0xFF, 0xBD, 0x00,
+	0xAE, 0xFF, 0x0A, 0x00, 0x09, 0x00, 0xB4, 0xFF, 0xAA, 0x00, 0x51,
+	0xFF, 0xA0, 0xFF, 0xA2, 0x03, 0x23, 0xF4, 0x98, 0x31, 0x49, 0x2D,
+	0x51, 0xF3, 0x8E, 0x04, 0xF6, 0xFE, 0xB2, 0xFF, 0x7F, 0x00, 0xC1,
+	0xFF, 0x07, 0x00, 0x0C, 0x00, 0xA2, 0xFF, 0xE6, 0x00, 0xC0, 0xFE,
+	0xB0, 0x00, 0xFD, 0x01, 0x1E, 0xF6, 0x33, 0x37, 0x9C, 0x26, 0xE0,
+	0xF2, 0x89, 0x05, 0x25, 0xFE, 0x33, 0x00, 0x43, 0x00, 0xD4, 0xFF,
+	0x04, 0x00, 0x0F, 0x00, 0x94, 0xFF, 0x1B, 0x01, 0x33, 0xFE, 0xD4,
+	0x01, 0x07, 0x00, 0x0E, 0xF9, 0xE6, 0x3B, 0x9F, 0x1F, 0x3C, 0xF3,
+	0x1A, 0x06, 0x85, 0xFD, 0xA2, 0x00, 0x0C, 0x00, 0xE6, 0xFF, 0x02,
+	0x00, 0x11, 0x00, 0x8C, 0xFF, 0x45, 0x01, 0xB2, 0xFD, 0xFA, 0x02,
+	0xD5, 0xFD, 0xF1, 0xFC, 0x81, 0x3F, 0x8B, 0x18, 0x47, 0xF4, 0x41,
+	0x06, 0x1B, 0xFD, 0xF9, 0x00, 0xDD, 0xFF, 0xF5, 0xFF, 0x01, 0x00,
+	0x10, 0x00, 0x8B, 0xFF, 0x5E, 0x01, 0x48, 0xFD, 0x0F, 0x04, 0x84,
+	0xFB, 0xB8, 0x01, 0xE4, 0x41, 0x9F, 0x11, 0xDE, 0xF5, 0x07, 0x06,
+	0xE7, 0xFC, 0x37, 0x01, 0xB8, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x0D,
+	0x00, 0x94, 0xFF, 0x63, 0x01, 0x00, 0xFD, 0x00, 0x05, 0x35, 0xF9,
+	0x4B, 0x07, 0xFB, 0x42, 0x10, 0x0B, 0xDA, 0xF7, 0x7A, 0x05, 0xE8,
+	0xFC, 0x5A, 0x01, 0x9E, 0xFF, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x07, 0x00, 0xA7, 0xFF, 0x50, 0x01, 0xE2, 0xFC, 0xB9, 0x05, 0x0D,
+	0xF7, 0x87, 0x0D, 0xB8, 0x42, 0x10, 0x05, 0x16, 0xFA, 0xA9, 0x04,
+	0x17, 0xFD, 0x64, 0x01, 0x8F, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0xFD,
+	0xFF, 0xC5, 0xFF, 0x22, 0x01, 0xF5, 0xFC, 0x28, 0x06, 0x33, 0xF5,
+	0x40, 0x14, 0x20, 0x41, 0xC8, 0xFF, 0x69, 0xFC, 0xA8, 0x03, 0x6D,
+	0xFD, 0x57, 0x01, 0x8A, 0xFF, 0x11, 0x00, 0x01, 0x00, 0xF0, 0xFF,
+	0xEE, 0xFF, 0xDB, 0x00, 0x3D, 0xFD, 0x3E, 0x06, 0xCE, 0xF3, 0x42,
+	0x1B, 0x40, 0x3E, 0x56, 0xFB, 0xB2, 0xFE, 0x8A, 0x02, 0xE1, 0xFD,
+	0x36, 0x01, 0x8E, 0xFF, 0x10, 0x00, 0x03, 0x00, 0xDF, 0xFF, 0x20,
+	0x00, 0x7A, 0x00, 0xBC, 0xFD, 0xEF, 0x05, 0x02, 0xF3, 0x55, 0x22,
+	0x35, 0x3A, 0xD0, 0xF7, 0xD0, 0x00, 0x63, 0x01, 0x68, 0xFE, 0x07,
+	0x01, 0x99, 0xFF, 0x0E, 0x00, 0x05, 0x00, 0xCD, 0xFF, 0x5A, 0x00,
+	0x03, 0x00, 0x70, 0xFE, 0x35, 0x05, 0xF2, 0xF2, 0x3B, 0x29, 0x24,
+	0x35, 0x3E, 0xF5, 0xAA, 0x02, 0x44, 0x00, 0xF8, 0xFE, 0xCF, 0x00,
+	0xA9, 0xFF, 0x0B, 0x00, 0x08, 0x00, 0xBA, 0xFF, 0x97, 0x00, 0x7C,
+	0xFF, 0x52, 0xFF, 0x11, 0x04, 0xB8, 0xF3, 0xB8, 0x2F, 0x3D, 0x2F,
+	0xA1, 0xF3, 0x2B, 0x04, 0x3F, 0xFF, 0x87, 0xFF, 0x92, 0x00, 0xBB,
+	0xFF, 0x08, 0x00, 0x0B, 0x00, 0xA8, 0xFF, 0xD4, 0x00, 0xED, 0xFE,
+	0x5A, 0x00, 0x88, 0x02, 0x68, 0xF5, 0x91, 0x35, 0xB6, 0x28, 0xEC,
+	0xF2, 0x48, 0x05, 0x60, 0xFE, 0x0D, 0x00, 0x55, 0x00, 0xCE, 0xFF,
+	0x05, 0x00, 0x0E, 0x00, 0x98, 0xFF, 0x0B, 0x01, 0x5D, 0xFE, 0x79,
+	0x01, 0xA9, 0x00, 0x0D, 0xF8, 0x8F, 0x3A, 0xCB, 0x21, 0x0C, 0xF3,
+	0xF9, 0x05, 0xB0, 0xFD, 0x82, 0x00, 0x1C, 0x00, 0xE1, 0xFF, 0x03,
+	0x00, 0x10, 0x00, 0x8E, 0xFF, 0x39, 0x01, 0xD7, 0xFD, 0xA0, 0x02,
+	0x86, 0xFE, 0xA6, 0xFB, 0x85, 0x3E, 0xB7, 0x1A, 0xE4, 0xF3, 0x40,
+	0x06, 0x35, 0xFD, 0xE1, 0x00, 0xEB, 0xFF, 0xF1, 0xFF, 0x01, 0x00,
+	0x11, 0x00, 0x8A, 0xFF, 0x58, 0x01, 0x66, 0xFD, 0xBD, 0x03, 0x3C,
+	0xFC, 0x29, 0x00, 0x4A, 0x41, 0xB8, 0x13, 0x54, 0xF5, 0x22, 0x06,
+	0xF1, 0xFC, 0x27, 0x01, 0xC2, 0xFF, 0xFE, 0xFF, 0x00, 0x00, 0x0E,
+	0x00, 0x90, 0xFF, 0x64, 0x01, 0x12, 0xFD, 0xBB, 0x04, 0xE8, 0xF9,
+	0x81, 0x05, 0xCB, 0x42, 0x08, 0x0D, 0x35, 0xF7, 0xAD, 0x05, 0xE2,
+	0xFC, 0x52, 0x01, 0xA5, 0xFF, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x09, 0x00, 0xA0, 0xFF, 0x58, 0x01, 0xE6, 0xFC, 0x87, 0x05, 0xB0,
+	0xF7, 0x8D, 0x0B, 0xF2, 0x42, 0xD7, 0x06, 0x62, 0xF9, 0xEF, 0x04,
+	0x04, 0xFD, 0x63, 0x01, 0x93, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0xBB, 0xFF, 0x33, 0x01, 0xE9, 0xFC, 0x0F, 0x06, 0xBA, 0xF5,
+	0x24, 0x12, 0xC2, 0x41, 0x53, 0x01, 0xB2, 0xFB, 0xFB, 0x03, 0x4F,
+	0xFD, 0x5D, 0x01, 0x8B, 0xFF, 0x10, 0x00, 0x01, 0x00, 0xF4, 0xFF,
+	0xE0, 0xFF, 0xF3, 0x00, 0x21, 0xFD, 0x41, 0x06, 0x2D, 0xF4, 0x16,
+	0x19, 0x45, 0x3F, 0x9C, 0xFC, 0x01, 0xFE, 0xE4, 0x02, 0xBB, 0xFD,
+	0x42, 0x01, 0x8C, 0xFF, 0x10, 0x00, 0x02, 0x00, 0xE5, 0xFF, 0x10,
+	0x00, 0x9A, 0x00, 0x8F, 0xFD, 0x12, 0x06, 0x2E, 0xF3, 0x2A, 0x20,
+	0x92, 0x3B, 0xCC, 0xF8, 0x30, 0x00, 0xBD, 0x01, 0x3D, 0xFE, 0x17,
+	0x01, 0x95, 0xFF, 0x0F, 0x00, 0x04, 0x00, 0xD3, 0xFF, 0x47, 0x00,
+	0x2A, 0x00, 0x33, 0xFE, 0x7A, 0x05, 0xE1, 0xF2, 0x24, 0x27, 0xCD,
+	0x36, 0xEE, 0xF5, 0x21, 0x02, 0x9B, 0x00, 0xCB, 0xFE, 0xE1, 0x00,
+	0xA4, 0xFF, 0x0C, 0x00, 0x07, 0x00, 0xC0, 0xFF, 0x84, 0x00, 0xA7,
+	0xFF, 0x08, 0xFF, 0x76, 0x04, 0x63, 0xF3, 0xC8, 0x2D, 0x22, 0x31,
+	0x06, 0xF4, 0xBF, 0x03, 0x8C, 0xFF, 0x5C, 0xFF, 0xA5, 0x00, 0xB5,
+	0xFF, 0x09, 0x00, 0x0A, 0x00, 0xAD, 0xFF, 0xC1, 0x00, 0x1A, 0xFF,
+	0x05, 0x00, 0x0B, 0x03, 0xC9, 0xF4, 0xD8, 0x33, 0xC5, 0x2A, 0x0C,
+	0xF3, 0xFB, 0x04, 0xA0, 0xFE, 0xE5, 0xFF, 0x68, 0x00, 0xC9, 0xFF,
+	0x06, 0x00, 0x0D, 0x00, 0x9C, 0xFF, 0xFB, 0x00, 0x89, 0xFE, 0x1F,
+	0x01, 0x44, 0x01, 0x22, 0xF7, 0x20, 0x39, 0xF3, 0x23, 0xED, 0xF2,
+	0xCE, 0x05, 0xE1, 0xFD, 0x60, 0x00, 0x2D, 0x00, 0xDB, 0xFF, 0x03,
+	0x00, 0x10, 0x00, 0x90, 0xFF, 0x2D, 0x01, 0xFF, 0xFD, 0x46, 0x02,
+	0x34, 0xFF, 0x71, 0xFA, 0x6B, 0x3D, 0xE5, 0x1C, 0x90, 0xF3, 0x35,
+	0x06, 0x55, 0xFD, 0xC6, 0x00, 0xF9, 0xFF, 0xEC, 0xFF, 0x01, 0x00,
+	0x11, 0x00, 0x8B, 0xFF, 0x51, 0x01, 0x86, 0xFD, 0x68, 0x03, 0xF3,
+	0xFC, 0xAE, 0xFE, 0x92, 0x40, 0xDA, 0x15, 0xD5, 0xF4, 0x35, 0x06,
+	0x00, 0xFD, 0x14, 0x01, 0xCE, 0xFF, 0xFA, 0xFF, 0x00, 0x00, 0x0F,
+	0x00, 0x8D, 0xFF, 0x63, 0x01, 0x28, 0xFD, 0x71, 0x04, 0x9E, 0xFA,
+	0xC7, 0x03, 0x79, 0x42, 0x0B, 0x0F, 0x97, 0xF6, 0xDA, 0x05, 0xE2,
+	0xFC, 0x48, 0x01, 0xAD, 0xFF, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x0B, 0x00, 0x9A, 0xFF, 0x5F, 0x01, 0xEF, 0xFC, 0x4F, 0x05, 0x5A,
+	0xF8, 0x9F, 0x09, 0x0A, 0x43, 0xAE, 0x08, 0xB1, 0xF8, 0x30, 0x05,
+	0xF5, 0xFC, 0x61, 0x01, 0x97, 0xFF, 0x0C, 0x00, 0x00, 0x00, 0x03,
+	0x00, 0xB1, 0xFF, 0x41, 0x01, 0xE3, 0xFC, 0xED, 0x05, 0x4C, 0xF6,
+	0x11, 0x10, 0x42, 0x42, 0xF1, 0x02, 0xFA, 0xFA, 0x4B, 0x04, 0x34,
+	0xFD, 0x61, 0x01, 0x8C, 0xFF, 0x10, 0x00, 0x01, 0x00, 0xF8, 0xFF,
+	0xD4, 0xFF, 0x0A, 0x01, 0x0A, 0xFD, 0x3C, 0x06, 0x9A, 0xF4, 0xED,
+	0x16, 0x2A, 0x40, 0xF8, 0xFD, 0x4D, 0xFD, 0x3C, 0x03, 0x97, 0xFD,
+	0x4C, 0x01, 0x8B, 0xFF, 0x11, 0x00, 0x02, 0x00, 0xEA, 0xFF, 0x00,
+	0x00, 0xB8, 0x00, 0x67, 0xFD, 0x2C, 0x06, 0x6B, 0xF3, 0xFC, 0x1D,
+	0xD3, 0x3C, 0xDF, 0xF9, 0x89, 0xFF, 0x18, 0x02, 0x13, 0xFE, 0x26,
+	0x01, 0x92, 0xFF, 0x0F, 0x00, 0x04, 0x00, 0xD9, 0xFF, 0x36, 0x00,
+	0x4E, 0x00, 0xFB, 0xFD, 0xB4, 0x05, 0xE4, 0xF2, 0x04, 0x25, 0x5F,
+	0x38, 0xB6, 0xF6, 0x90, 0x01, 0xF3, 0x00, 0x9F, 0xFE, 0xF3, 0x00,
+	0x9F, 0xFF, 0x0D, 0x00, 0x06, 0x00, 0xC6, 0xFF, 0x71, 0x00, 0xD1,
+	0xFF, 0xC2, 0xFE, 0xD1, 0x04, 0x23, 0xF3, 0xC9, 0x2B, 0xF5, 0x32,
+	0x83, 0xF4, 0x49, 0x03, 0xDC, 0xFF, 0x30, 0xFF, 0xB8, 0x00, 0xB0,
+	0xFF, 0x0A, 0x00, 0x09, 0x00, 0xB3, 0xFF, 0xAE, 0x00, 0x46, 0xFF,
+	0xB4, 0xFF, 0x85, 0x03, 0x42, 0xF4, 0x0E, 0x32, 0xCA, 0x2C, 0x41,
+	0xF3, 0xA5, 0x04, 0xE4, 0xFE, 0xBC, 0xFF, 0x7A, 0x00, 0xC3, 0xFF,
+	0x07, 0x00, 0x0D, 0x00, 0xA1, 0xFF, 0xEA, 0x00, 0xB5, 0xFE, 0xC6,
+	0x00, 0xD9, 0x01, 0x4F, 0xF6, 0x99, 0x37, 0x16, 0x26, 0xE0, 0xF2,
+	0x98, 0x05, 0x16, 0xFE, 0x3C, 0x00, 0x3F, 0x00, 0xD6, 0xFF, 0x04,
+	0x00, 0x0F, 0x00, 0x93, 0xFF, 0x1F, 0x01, 0x28, 0xFE, 0xEB, 0x01,
+	0xDD, 0xFF, 0x52, 0xF9, 0x36, 0x3C, 0x13, 0x1F, 0x4B, 0xF3, 0x20,
+	0x06, 0x7B, 0xFD, 0xA9, 0x00, 0x08, 0x00, 0xE7, 0xFF, 0x02, 0x00,
+	0x11, 0x00, 0x8C, 0xFF, 0x47, 0x01, 0xA9, 0xFD, 0x10, 0x03, 0xA8,
+	0xFD, 0x47, 0xFD, 0xBB, 0x3F, 0x01, 0x18, 0x62, 0xF4, 0x40, 0x06,
+	0x15, 0xFD, 0xFF, 0x00, 0xDA, 0xFF, 0xF6, 0xFF, 0x01, 0x00, 0x10,
+	0x00, 0x8B, 0xFF, 0x5F, 0x01, 0x41, 0xFD, 0x23, 0x04, 0x56, 0xFB,
+	0x1F, 0x02, 0x06, 0x42, 0x19, 0x11, 0x02, 0xF6, 0xFF, 0x05, 0xE5,
+	0xFC, 0x3B, 0x01, 0xB6, 0xFF, 0x02, 0x00, 0x00, 0x00, 0x0D, 0x00,
+	0x95, 0xFF, 0x62, 0x01, 0xFC, 0xFC, 0x10, 0x05, 0x09, 0xF9, 0xC1,
+	0x07, 0x03, 0x43, 0x94, 0x0A, 0x05, 0xF8, 0x6C, 0x05, 0xEA, 0xFC,
+	0x5C, 0x01, 0x9D, 0xFF, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06,
+	0x00, 0xA9, 0xFF, 0x4D, 0x01, 0xE1, 0xFC, 0xC4, 0x05, 0xE6, 0xF6,
+	0x08, 0x0E, 0xA5, 0x42, 0xA1, 0x04, 0x43, 0xFA, 0x97, 0x04, 0x1D,
+	0xFD, 0x64, 0x01, 0x8F, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0xFC, 0xFF,
+	0xC8, 0xFF, 0x1E, 0x01, 0xF8, 0xFC, 0x2D, 0x06, 0x13, 0xF5, 0xC8,
+	0x14, 0xF2, 0x40, 0x69, 0xFF, 0x97, 0xFC, 0x92, 0x03, 0x75, 0xFD,
+	0x55, 0x01, 0x8A, 0xFF, 0x11, 0x00, 0x01, 0x00, 0xEF, 0xFF, 0xF2,
+	0xFF, 0xD4, 0x00, 0x45, 0xFD, 0x3B, 0x06, 0xB8, 0xF3, 0xCE, 0x1B,
+	0xFB, 0x3D, 0x08, 0xFB, 0xDE, 0xFE, 0x73, 0x02, 0xEB, 0xFD, 0x33,
+	0x01, 0x8F, 0xFF, 0x10, 0x00, 0x03, 0x00, 0xDE, 0xFF, 0x25, 0x00,
+	0x71, 0x00, 0xC8, 0xFD, 0xE5, 0x05, 0xFA, 0xF2, 0xDF, 0x22, 0xDB,
+	0x39, 0x94, 0xF7, 0xF7, 0x00, 0x4C, 0x01, 0x73, 0xFE, 0x03, 0x01,
+	0x9A, 0xFF, 0x0E, 0x00, 0x05, 0x00, 0xCC, 0xFF, 0x5E, 0x00, 0xF9,
+	0xFF, 0x80, 0xFE, 0x23, 0x05, 0xF9, 0xF2, 0xC0, 0x29, 0xB8, 0x34,
+	0x16, 0xF5, 0xCB, 0x02, 0x2F, 0x00, 0x03, 0xFF, 0xCA, 0x00, 0xAA,
+	0xFF, 0x0B, 0x00, 0x08, 0x00, 0xB8, 0xFF, 0x9B, 0x00, 0x72, 0xFF,
+	0x65, 0xFF, 0xF6, 0x03, 0xD1, 0xF3, 0x31, 0x30, 0xC1, 0x2E, 0x8B,
+	0xF3, 0x45, 0x04, 0x2D, 0xFF, 0x92, 0xFF, 0x8D, 0x00, 0xBD, 0xFF,
+	0x08, 0x00, 0x0C, 0x00, 0xA6, 0xFF, 0xD8, 0x00, 0xE2, 0xFE, 0x6F,
+	0x00, 0x66, 0x02, 0x93, 0xF5, 0xFB, 0x35, 0x31, 0x28, 0xE7, 0xF2,
+	0x59, 0x05, 0x51, 0xFE, 0x17, 0x00, 0x50, 0x00, 0xD0, 0xFF, 0x05,
+	0x00, 0x0E, 0x00, 0x97, 0xFF, 0x0F, 0x01, 0x53, 0xFE, 0x90, 0x01,
+	0x81, 0x00, 0x4B, 0xF8, 0xE6, 0x3A, 0x3F, 0x21, 0x16, 0xF3, 0x02,
+	0x06, 0xA5, 0xFD, 0x8A, 0x00, 0x18, 0x00, 0xE2, 0xFF, 0x02, 0x00,
+	0x10, 0x00, 0x8D, 0xFF, 0x3C, 0x01, 0xCE, 0xFD, 0xB7, 0x02, 0x5A,
+	0xFE, 0xF7, 0xFB, 0xC6, 0x3E, 0x2C, 0x1A, 0xFC, 0xF3, 0x41, 0x06,
+	0x2E, 0xFD, 0xE7, 0x00, 0xE7, 0xFF, 0xF2, 0xFF, 0x01, 0x00, 0x10,
+	0x00, 0x8B, 0xFF, 0x5A, 0x01, 0x5E, 0xFD, 0xD2, 0x03, 0x0E, 0xFC,
+	0x8B, 0x00, 0x75, 0x41, 0x32, 0x13, 0x75, 0xF5, 0x1C, 0x06, 0xEE,
+	0xFC, 0x2B, 0x01, 0xC0, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x0E, 0x00,
+	0x91, 0xFF, 0x64, 0x01, 0x0D, 0xFD, 0xCD, 0x04, 0xBB, 0xF9, 0xF2,
+	0x05, 0xD9, 0x42, 0x88, 0x0C, 0x5E, 0xF7, 0xA1, 0x05, 0xE3, 0xFC,
+	0x54, 0x01, 0xA3, 0xFF, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09,
+	0x00, 0xA2, 0xFF, 0x56, 0x01, 0xE5, 0xFC, 0x94, 0x05, 0x87, 0xF7,
+	0x0A, 0x0C, 0xE6, 0x42, 0x64, 0x06, 0x8E, 0xF9, 0xDE, 0x04, 0x09,
+	0xFD, 0x64, 0x01, 0x92, 0xFF, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0xBD, 0xFF, 0x2F, 0x01, 0xEC, 0xFC, 0x16, 0x06, 0x98, 0xF5, 0xAB,
+	0x12, 0x9C, 0x41, 0xEE, 0x00, 0xE0, 0xFB, 0xE6, 0x03, 0x57, 0xFD,
+	0x5B, 0x01, 0x8B, 0xFF, 0x10, 0x00, 0x01, 0x00, 0xF3, 0xFF, 0xE4,
+	0xFF, 0xED, 0x00, 0x27, 0xFD, 0x41, 0x06, 0x14, 0xF4, 0xA1, 0x19,
+	0x06, 0x3F, 0x49, 0xFC, 0x2E, 0xFE, 0xCD, 0x02, 0xC4, 0xFD, 0x3F,
+	0x01, 0x8D, 0xFF, 0x10, 0x00, 0x02, 0x00, 0xE3, 0xFF, 0x14, 0x00,
+	0x92, 0x00, 0x9A, 0xFD, 0x0A, 0x06, 0x22, 0xF3, 0xB4, 0x20, 0x3C,
+	0x3B, 0x8B, 0xF8, 0x58, 0x00, 0xA7, 0x01, 0x48, 0xFE, 0x13, 0x01,
+	0x96, 0xFF, 0x0F, 0x00, 0x04, 0x00, 0xD1, 0xFF, 0x4C, 0x00, 0x20,
+	0x00, 0x42, 0xFE, 0x6A, 0x05, 0xE3, 0xF2, 0xAB, 0x27, 0x66, 0x36,
+	0xC0, 0xF5, 0x44, 0x02, 0x85, 0x00, 0xD7, 0xFE, 0xDD, 0x00, 0xA5,
+	0xFF, 0x0C, 0x00, 0x07, 0x00, 0xBE, 0xFF, 0x89, 0x00, 0x9D, 0xFF,
+	0x1A, 0xFF, 0x5E, 0x04, 0x76, 0xF3, 0x45, 0x2E, 0xAA, 0x30, 0xEB,
+	0xF3, 0xDB, 0x03, 0x79, 0xFF, 0x67, 0xFF, 0xA0, 0x00, 0xB7, 0xFF,
+	0x09, 0x00, 0x0B, 0x00, 0xAC, 0xFF, 0xC6, 0x00, 0x0E, 0xFF, 0x1A,
+	0x00, 0xEB, 0x02, 0xEF, 0xF4, 0x49, 0x34, 0x43, 0x2A, 0x02, 0xF3,
+	0x0F, 0x05, 0x90, 0xFE, 0xEF, 0xFF, 0x63, 0x00, 0xCA, 0xFF, 0x06,
+	0x00, 0x0E, 0x00, 0x9B, 0xFF, 0xFF, 0x00, 0x7E, 0xFE, 0x36, 0x01,
+	0x1E, 0x01, 0x5B, 0xF7, 0x7E, 0x39, 0x69, 0x23, 0xF3, 0xF2, 0xD9,
+	0x05, 0xD4, 0xFD, 0x69, 0x00, 0x29, 0x00, 0xDD, 0xFF, 0x03, 0x00,
+	0x10, 0x00, 0x90, 0xFF, 0x30, 0x01, 0xF5, 0xFD, 0x5C, 0x02, 0x09,
+	0xFF, 0xBC, 0xFA, 0xB5, 0x3D, 0x5A, 0x1C, 0xA3, 0xF3, 0x38, 0x06,
+	0x4D, 0xFD, 0xCD, 0x00, 0xF5, 0xFF, 0xED, 0xFF, 0x01, 0x00, 0x11,
+	0x00, 0x8B, 0xFF, 0x53, 0x01, 0x7E, 0xFD, 0x7D, 0x03, 0xC5, 0xFC,
+	0x0B, 0xFF, 0xC3, 0x40, 0x51, 0x15, 0xF4, 0xF4, 0x31, 0x06, 0xFC,
+	0xFC, 0x19, 0x01, 0xCB, 0xFF, 0xFB, 0xFF, 0x00, 0x00, 0x0F, 0x00,
+	0x8E, 0xFF, 0x63, 0x01, 0x22, 0xFD, 0x84, 0x04, 0x71, 0xFA, 0x34,
+	0x04, 0x90, 0x42, 0x89, 0x0E, 0xBE, 0xF6, 0xCF, 0x05, 0xE1, 0xFC,
+	0x4A, 0x01, 0xAB, 0xFF, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B,
+	0x00, 0x9B, 0xFF, 0x5D, 0x01, 0xEC, 0xFC, 0x5D, 0x05, 0x2F, 0xF8,
+	0x19, 0x0A, 0x07, 0x43, 0x37, 0x08, 0xDD, 0xF8, 0x21, 0x05, 0xF8,
+	0xFC, 0x62, 0x01, 0x96, 0xFF, 0x0C, 0x00, 0x00, 0x00, 0x03, 0x00,
+	0xB4, 0xFF, 0x3E, 0x01, 0xE4, 0xFC, 0xF6, 0x05, 0x26, 0xF6, 0x95,
+	0x10, 0x26, 0x42, 0x87, 0x02, 0x28, 0xFB, 0x37, 0x04, 0x3B, 0xFD,
+	0x60, 0x01, 0x8C, 0xFF, 0x10, 0x00, 0x01, 0x00, 0xF7, 0xFF, 0xD7,
+	0xFF, 0x04, 0x01, 0x0F, 0xFD, 0x3E, 0x06, 0x7D, 0xF4, 0x76, 0x17,
+	0xF4, 0x3F, 0x9F, 0xFD, 0x7B, 0xFD, 0x26, 0x03, 0xA0, 0xFD, 0x4A,
+	0x01, 0x8B, 0xFF, 0x11, 0x00, 0x02, 0x00, 0xE9, 0xFF, 0x04, 0x00,
+	0xB1, 0x00, 0x71, 0xFD, 0x26, 0x06, 0x5A, 0xF3, 0x88, 0x1E, 0x87,
+	0x3C, 0x98, 0xF9, 0xB3, 0xFF, 0x02, 0x02, 0x1E, 0xFE, 0x22, 0x01,
+	0x93, 0xFF, 0x0F, 0x00, 0x04, 0x00, 0xD7, 0xFF, 0x3A, 0x00, 0x45,
+	0x00, 0x09, 0xFE, 0xA7, 0x05, 0xE1, 0xF2, 0x8D, 0x25, 0xFD, 0x37,
+	0x82, 0xF6, 0xB5, 0x01, 0xDC, 0x00, 0xAA, 0xFE, 0xEE, 0x00, 0xA0,
+	0xFF, 0x0D, 0x00, 0x06, 0x00, 0xC4, 0xFF, 0x76, 0x00, 0xC7, 0xFF,
+	0xD3, 0xFE, 0xBC, 0x04, 0x31, 0xF3, 0x4A, 0x2C, 0x83, 0x32, 0x61,
+	0xF4, 0x68, 0x03, 0xC8, 0xFF, 0x3B, 0xFF, 0xB3, 0x00, 0xB1, 0xFF,
+	0x0A, 0x00, 0x0A, 0x00, 0xB1, 0xFF, 0xB3, 0x00, 0x3B, 0xFF, 0xC8,
+	0xFF, 0x68, 0x03, 0x61, 0xF4, 0x83, 0x32, 0x4A, 0x2C, 0x31, 0xF3,
+	0xBC, 0x04, 0xD3, 0xFE, 0xC7, 0xFF, 0x76, 0x00, 0xC4, 0xFF, 0x06,
+	0x00, 0x0D, 0x00, 0xA0, 0xFF, 0xEE, 0x00, 0xAA, 0xFE, 0xDC, 0x00,
+	0xB5, 0x01, 0x82, 0xF6, 0xFD, 0x37, 0x8D, 0x25, 0xE1, 0xF2, 0xA7,
+	0x05, 0x09, 0xFE, 0x45, 0x00, 0x3A, 0x00, 0xD7, 0xFF, 0x04, 0x00,
+	0x0F, 0x00, 0x93, 0xFF, 0x22, 0x01, 0x1E, 0xFE, 0x02, 0x02, 0xB3,
+	0xFF, 0x98, 0xF9, 0x87, 0x3C, 0x88, 0x1E, 0x5A, 0xF3, 0x26, 0x06,
+	0x71, 0xFD, 0xB1, 0x00, 0x04, 0x00, 0xE9, 0xFF, 0x02, 0x00, 0x11,
+	0x00, 0x8B, 0xFF, 0x4A, 0x01, 0xA0, 0xFD, 0x26, 0x03, 0x7B, 0xFD,
+	0x9F, 0xFD, 0xF4, 0x3F, 0x76, 0x17, 0x7D, 0xF4, 0x3E, 0x06, 0x0F,
+	0xFD, 0x04, 0x01, 0xD7, 0xFF, 0xF7, 0xFF, 0x01, 0x00, 0x10, 0x00,
+	0x8C, 0xFF, 0x60, 0x01, 0x3B, 0xFD, 0x37, 0x04, 0x28, 0xFB, 0x87,
+	0x02, 0x26, 0x42, 0x95, 0x10, 0x26, 0xF6, 0xF6, 0x05, 0xE4, 0xFC,
+	0x3E, 0x01, 0xB4, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x96,
+	0xFF, 0x62, 0x01, 0xF8, 0xFC, 0x21, 0x05, 0xDD, 0xF8, 0x37, 0x08,
+	0x07, 0x43, 0x19, 0x0A, 0x2F, 0xF8, 0x5D, 0x05, 0xEC, 0xFC, 0x5D,
+	0x01, 0x9B, 0xFF, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00,
+	0xAB, 0xFF, 0x4A, 0x01, 0xE1, 0xFC, 0xCF, 0x05, 0xBE, 0xF6, 0x89,
+	0x0E, 0x90, 0x42, 0x34, 0x04, 0x71, 0xFA, 0x84, 0x04, 0x22, 0xFD,
+	0x63, 0x01, 0x8E, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0xFB, 0xFF, 0xCB,
+	0xFF, 0x19, 0x01, 0xFC, 0xFC, 0x31, 0x06, 0xF4, 0xF4, 0x51, 0x15,
+	0xC3, 0x40, 0x0B, 0xFF, 0xC5, 0xFC, 0x7D, 0x03, 0x7E, 0xFD, 0x53,
+	0x01, 0x8B, 0xFF, 0x11, 0x00, 0x01, 0x00, 0xED, 0xFF, 0xF5, 0xFF,
+	0xCD, 0x00, 0x4D, 0xFD, 0x38, 0x06, 0xA3, 0xF3, 0x5A, 0x1C, 0xB5,
+	0x3D, 0xBC, 0xFA, 0x09, 0xFF, 0x5C, 0x02, 0xF5, 0xFD, 0x30, 0x01,
+	0x90, 0xFF, 0x10, 0x00, 0x03, 0x00, 0xDD, 0xFF, 0x29, 0x00, 0x69,
+	0x00, 0xD4, 0xFD, 0xD9, 0x05, 0xF3, 0xF2, 0x69, 0x23, 0x7E, 0x39,
+	0x5B, 0xF7, 0x1E, 0x01, 0x36, 0x01, 0x7E, 0xFE, 0xFF, 0x00, 0x9B,
+	0xFF, 0x0E, 0x00, 0x06, 0x00, 0xCA, 0xFF, 0x63, 0x00, 0xEF, 0xFF,
+	0x90, 0xFE, 0x0F, 0x05, 0x02, 0xF3, 0x43, 0x2A, 0x49, 0x34, 0xEF,
+	0xF4, 0xEB, 0x02, 0x1A, 0x00, 0x0E, 0xFF, 0xC6, 0x00, 0xAC, 0xFF,
+	0x0B, 0x00, 0x09, 0x00, 0xB7, 0xFF, 0xA0, 0x00, 0x67, 0xFF, 0x79,
+	0xFF, 0xDB, 0x03, 0xEB, 0xF3, 0xAA, 0x30, 0x45, 0x2E, 0x76, 0xF3,
+	0x5E, 0x04, 0x1A, 0xFF, 0x9D, 0xFF, 0x89, 0x00, 0xBE, 0xFF, 0x07,
+	0x00, 0x0C, 0x00, 0xA5, 0xFF, 0xDD, 0x00, 0xD7, 0xFE, 0x85, 0x00,
+	0x44, 0x02, 0xC0, 0xF5, 0x66, 0x36, 0xAB, 0x27, 0xE3, 0xF2, 0x6A,
+	0x05, 0x42, 0xFE, 0x20, 0x00, 0x4C, 0x00, 0xD1, 0xFF, 0x04, 0x00,
+	0x0F, 0x00, 0x96, 0xFF, 0x13, 0x01, 0x48, 0xFE, 0xA7, 0x01, 0x58,
+	0x00, 0x8B, 0xF8, 0x3C, 0x3B, 0xB4, 0x20, 0x22, 0xF3, 0x0A, 0x06,
+	0x9A, 0xFD, 0x92, 0x00, 0x14, 0x00, 0xE3, 0xFF, 0x02, 0x00, 0x10,
+	0x00, 0x8D, 0xFF, 0x3F, 0x01, 0xC4, 0xFD, 0xCD, 0x02, 0x2E, 0xFE,
+	0x49, 0xFC, 0x06, 0x3F, 0xA1, 0x19, 0x14, 0xF4, 0x41, 0x06, 0x27,
+	0xFD, 0xED, 0x00, 0xE4, 0xFF, 0xF3, 0xFF, 0x01, 0x00, 0x10, 0x00,
+	0x8B, 0xFF, 0x5B, 0x01, 0x57, 0xFD, 0xE6, 0x03, 0xE0, 0xFB, 0xEE,
+	0x00, 0x9C, 0x41, 0xAB, 0x12, 0x98, 0xF5, 0x16, 0x06, 0xEC, 0xFC,
+	0x2F, 0x01, 0xBD, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x92,
+	0xFF, 0x64, 0x01, 0x09, 0xFD, 0xDE, 0x04, 0x8E, 0xF9, 0x64, 0x06,
+	0xE6, 0x42, 0x0A, 0x0C, 0x87, 0xF7, 0x94, 0x05, 0xE5, 0xFC, 0x56,
+	0x01, 0xA2, 0xFF, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00,
+	0xA3, 0xFF, 0x54, 0x01, 0xE3, 0xFC, 0xA1, 0x05, 0x5E, 0xF7, 0x88,
+	0x0C, 0xD9, 0x42, 0xF2, 0x05, 0xBB, 0xF9, 0xCD, 0x04, 0x0D, 0xFD,
+	0x64, 0x01, 0x91, 0xFF, 0x0E, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xC0,
+	0xFF, 0x2B, 0x01, 0xEE, 0xFC, 0x1C, 0x06, 0x75, 0xF5, 0x32, 0x13,
+	0x75, 0x41, 0x8B, 0x00, 0x0E, 0xFC, 0xD2, 0x03, 0x5E, 0xFD, 0x5A,
+	0x01, 0x8B, 0xFF, 0x10, 0x00, 0x01, 0x00, 0xF2, 0xFF, 0xE7, 0xFF,
+	0xE7, 0x00, 0x2E, 0xFD, 0x41, 0x06, 0xFC, 0xF3, 0x2C, 0x1A, 0xC6,
+	0x3E, 0xF7, 0xFB, 0x5A, 0xFE, 0xB7, 0x02, 0xCE, 0xFD, 0x3C, 0x01,
+	0x8D, 0xFF, 0x10, 0x00, 0x02, 0x00, 0xE2, 0xFF, 0x18, 0x00, 0x8A,
+	0x00, 0xA5, 0xFD, 0x02, 0x06, 0x16, 0xF3, 0x3F, 0x21, 0xE6, 0x3A,
+	0x4B, 0xF8, 0x81, 0x00, 0x90, 0x01, 0x53, 0xFE, 0x0F, 0x01, 0x97,
+	0xFF, 0x0E, 0x00, 0x05, 0x00, 0xD0, 0xFF, 0x50, 0x00, 0x17, 0x00,
+	0x51, 0xFE, 0x59, 0x05, 0xE7, 0xF2, 0x31, 0x28, 0xFB, 0x35, 0x93,
+	0xF5, 0x66, 0x02, 0x6F, 0x00, 0xE2, 0xFE, 0xD8, 0x00, 0xA6, 0xFF,
+	0x0C, 0x00, 0x08, 0x00, 0xBD, 0xFF, 0x8D, 0x00, 0x92, 0xFF, 0x2D,
+	0xFF, 0x45, 0x04, 0x8B, 0xF3, 0xC1, 0x2E, 0x31, 0x30, 0xD1, 0xF3,
+	0xF6, 0x03, 0x65, 0xFF, 0x72, 0xFF, 0x9B, 0x00, 0xB8, 0xFF, 0x08,
+	0x00, 0x0B, 0x00, 0xAA, 0xFF, 0xCA, 0x00, 0x03, 0xFF, 0x2F, 0x00,
+	0xCB, 0x02, 0x16, 0xF5, 0xB8, 0x34, 0xC0, 0x29, 0xF9, 0xF2, 0x23,
+	0x05, 0x80, 0xFE, 0xF9, 0xFF, 0x5E, 0x00, 0xCC, 0xFF, 0x05, 0x00,
+	0x0E, 0x00, 0x9A, 0xFF, 0x03, 0x01, 0x73, 0xFE, 0x4C, 0x01, 0xF7,
+	0x00, 0x94, 0xF7, 0xDB, 0x39, 0xDF, 0x22, 0xFA, 0xF2, 0xE5, 0x05,
+	0xC8, 0xFD, 0x71, 0x00, 0x25, 0x00, 0xDE, 0xFF, 0x03, 0x00, 0x10,
+	0x00, 0x8F, 0xFF, 0x33, 0x01, 0xEB, 0xFD, 0x73, 0x02, 0xDE, 0xFE,
+	0x08, 0xFB, 0xFB, 0x3D, 0xCE, 0x1B, 0xB8, 0xF3, 0x3B, 0x06, 0x45,
+	0xFD, 0xD4, 0x00, 0xF2, 0xFF, 0xEF, 0xFF, 0x01, 0x00, 0x11, 0x00,
+	0x8A, 0xFF, 0x55, 0x01, 0x75, 0xFD, 0x92, 0x03, 0x97, 0xFC, 0x69,
+	0xFF, 0xF2, 0x40, 0xC8, 0x14, 0x13, 0xF5, 0x2D, 0x06, 0xF8, 0xFC,
+	0x1E, 0x01, 0xC8, 0xFF, 0xFC, 0xFF, 0x00, 0x00, 0x0F, 0x00, 0x8F,
+	0xFF, 0x64, 0x01, 0x1D, 0xFD, 0x97, 0x04, 0x43, 0xFA, 0xA1, 0x04,
+	0xA5, 0x42, 0x08, 0x0E, 0xE6, 0xF6, 0xC4, 0x05, 0xE1, 0xFC, 0x4D,
+	0x01, 0xA9, 0xFF, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00,
+	0x9D, 0xFF, 0x5C, 0x01, 0xEA, 0xFC, 0x6C, 0x05, 0x05, 0xF8, 0x94,
+	0x0A, 0x03, 0x43, 0xC1, 0x07, 0x09, 0xF9, 0x10, 0x05, 0xFC, 0xFC,
+	0x62, 0x01, 0x95, 0xFF, 0x0D, 0x00, 0x00, 0x00, 0x02, 0x00, 0xB6,
+	0xFF, 0x3B, 0x01, 0xE5, 0xFC, 0xFF, 0x05, 0x02, 0xF6, 0x19, 0x11,
+	0x06, 0x42, 0x1F, 0x02, 0x56, 0xFB, 0x23, 0x04, 0x41, 0xFD, 0x5F,
+	0x01, 0x8B, 0xFF, 0x10, 0x00, 0x01, 0x00, 0xF6, 0xFF, 0xDA, 0xFF,
+	0xFF, 0x00, 0x15, 0xFD, 0x40, 0x06, 0x62, 0xF4, 0x01, 0x18, 0xBB,
+	0x3F, 0x47, 0xFD, 0xA8, 0xFD, 0x10, 0x03, 0xA9, 0xFD, 0x47, 0x01,
+	0x8C, 0xFF, 0x11, 0x00, 0x02, 0x00, 0xE7, 0xFF, 0x08, 0x00, 0xA9,
+	0x00, 0x7B, 0xFD, 0x20, 0x06, 0x4B, 0xF3, 0x13, 0x1F, 0x36, 0x3C,
+	0x52, 0xF9, 0xDD, 0xFF, 0xEB, 0x01, 0x28, 0xFE, 0x1F, 0x01, 0x93,
+	0xFF, 0x0F, 0x00, 0x04, 0x00, 0xD6, 0xFF, 0x3F, 0x00, 0x3C, 0x00,
+	0x16, 0xFE, 0x98, 0x05, 0xE0, 0xF2, 0x16, 0x26, 0x99, 0x37, 0x4F,
+	0xF6, 0xD9, 0x01, 0xC6, 0x00, 0xB5, 0xFE, 0xEA, 0x00, 0xA1, 0xFF,
+	0x0D, 0x00, 0x07, 0x00, 0xC3, 0xFF, 0x7A, 0x00, 0xBC, 0xFF, 0xE4,
+	0xFE, 0xA5, 0x04, 0x41, 0xF3, 0xCA, 0x2C, 0x0E, 0x32, 0x42, 0xF4,
+	0x85, 0x03, 0xB4, 0xFF, 0x46, 0xFF, 0xAE, 0x00, 0xB3, 0xFF, 0x09,
+	0x00, 0x0A, 0x00, 0xB0, 0xFF, 0xB8, 0x00, 0x30, 0xFF, 0xDC, 0xFF,
+	0x49, 0x03, 0x83, 0xF4, 0xF5, 0x32, 0xC9, 0x2B, 0x23, 0xF3, 0xD1,
+	0x04, 0xC2, 0xFE, 0xD1, 0xFF, 0x71, 0x00, 0xC6, 0xFF, 0x06, 0x00,
+	0x0D, 0x00, 0x9F, 0xFF, 0xF3, 0x00, 0x9F, 0xFE, 0xF3, 0x00, 0x90,
+	0x01, 0xB6, 0xF6, 0x5F, 0x38, 0x04, 0x25, 0xE4, 0xF2, 0xB4, 0x05,
+	0xFB, 0xFD, 0x4E, 0x00, 0x36, 0x00, 0xD9, 0xFF, 0x04, 0x00, 0x0F,
+	0x00, 0x92, 0xFF, 0x26, 0x01, 0x13, 0xFE, 0x18, 0x02, 0x89, 0xFF,
+	0xDF, 0xF9, 0xD3, 0x3C, 0xFC, 0x1D, 0x6B, 0xF3, 0x2C, 0x06, 0x67,
+	0xFD, 0xB8, 0x00, 0x00, 0x00, 0xEA, 0xFF, 0x02, 0x00, 0x11, 0x00,
+	0x8B, 0xFF, 0x4C, 0x01, 0x97, 0xFD, 0x3C, 0x03, 0x4D, 0xFD, 0xF8,
+	0xFD, 0x2A, 0x40, 0xED, 0x16, 0x9A, 0xF4, 0x3C, 0x06, 0x0A, 0xFD,
+	0x0A, 0x01, 0xD4, 0xFF, 0xF8, 0xFF, 0x01, 0x00, 0x10, 0x00, 0x8C,
+	0xFF, 0x61, 0x01, 0x34, 0xFD, 0x4B, 0x04, 0xFA, 0xFA, 0xF1, 0x02,
+	0x42, 0x42, 0x11, 0x10, 0x4C, 0xF6, 0xED, 0x05, 0xE3, 0xFC, 0x41,
+	0x01, 0xB1, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x97, 0xFF,
+	0x61, 0x01, 0xF5, 0xFC, 0x30, 0x05, 0xB1, 0xF8, 0xAE, 0x08, 0x0A,
+	0x43, 0x9F, 0x09, 0x5A, 0xF8, 0x4F, 0x05, 0xEF, 0xFC, 0x5F, 0x01,
+	0x9A, 0xFF, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0xAD,
+	0xFF, 0x48, 0x01, 0xE2, 0xFC, 0xDA, 0x05, 0x97, 0xF6, 0x0B, 0x0F,
+	0x79, 0x42, 0xC7, 0x03, 0x9E, 0xFA, 0x71, 0x04, 0x28, 0xFD, 0x63,
+	0x01, 0x8D, 0xFF, 0x0F, 0x00 
+};
+
+static u16
+coefficient_sizes[8 * 2] = {
+	/* Playback */
+	0x00C0, 0x5000, 0x0060, 0x2800, 0x0040, 0x0060, 0x1400, 0x0000,
+	/* capture */
+	0x0020, 0x1260, 0x0020, 0x1260, 0x0000, 0x0040, 0x1260, 0x0000,
+};
+
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/rme96.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/rme96.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/rme96.c	(revision 91)
@@ -0,0 +1,2742 @@
+/*
+ *   ALSA driver for RME Digi96, Digi96/8 and Digi96/8 PRO/PAD/PST audio
+ *   interfaces 
+ *
+ *	Copyright (c) 2000, 2001 Anders Torger <torger@ludd.luth.se>
+ *    
+ *      Thanks to Henk Hesselink <henk@anda.nl> for the analog volume control
+ *      code.
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */      
+
+#define SNDRV_MAIN_OBJECT_FILE
+
+#include <sound/driver.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+/* note, two last pcis should be equal, it is not a bug */
+EXPORT_NO_SYMBOLS;
+MODULE_DESCRIPTION("RME Digi96, Digi96/8, Digi96/8 PRO, Digi96/8 PST, "
+		   "Digi96/8 PAD");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{RME,Digi96},"
+		"{RME,Digi96/8},"
+		"{RME,Digi96/8 PRO},"
+		"{RME,Digi96/8 PST},"
+		"{RME,Digi96/8 PAD}}");
+
+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;	/* Enable this card */
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for RME Digi96 soundcard.");
+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 RME Digi96 soundcard.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable RME Digi96 soundcard.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+MODULE_AUTHOR("Anders Torger <torger@ludd.luth.se>");
+
+/*
+ * Defines for RME Digi96 series, from internal RME reference documents
+ * dated 12.01.00
+ */
+
+#define RME96_SPDIF_NCHANNELS 2
+
+/* Playback and capture buffer size */
+#define RME96_BUFFER_SIZE 0x10000
+
+/* IO area size */
+#define RME96_IO_SIZE 0x60000
+
+/* IO area offsets */
+#define RME96_IO_PLAY_BUFFER      0x0
+#define RME96_IO_REC_BUFFER       0x10000
+#define RME96_IO_CONTROL_REGISTER 0x20000
+#define RME96_IO_ADDITIONAL_REG   0x20004
+#define RME96_IO_CONFIRM_PLAY_IRQ 0x20008
+#define RME96_IO_CONFIRM_REC_IRQ  0x2000C
+#define RME96_IO_SET_PLAY_POS     0x40000
+#define RME96_IO_RESET_PLAY_POS   0x4FFFC
+#define RME96_IO_SET_REC_POS      0x50000
+#define RME96_IO_RESET_REC_POS    0x5FFFC
+#define RME96_IO_GET_PLAY_POS     0x20000
+#define RME96_IO_GET_REC_POS      0x30000
+
+/* Write control register bits */
+#define RME96_WCR_START     (1 << 0)
+#define RME96_WCR_START_2   (1 << 1)
+#define RME96_WCR_GAIN_0    (1 << 2)
+#define RME96_WCR_GAIN_1    (1 << 3)
+#define RME96_WCR_MODE24    (1 << 4)
+#define RME96_WCR_MODE24_2  (1 << 5)
+#define RME96_WCR_BM        (1 << 6)
+#define RME96_WCR_BM_2      (1 << 7)
+#define RME96_WCR_ADAT      (1 << 8)
+#define RME96_WCR_FREQ_0    (1 << 9)
+#define RME96_WCR_FREQ_1    (1 << 10)
+#define RME96_WCR_DS        (1 << 11)
+#define RME96_WCR_PRO       (1 << 12)
+#define RME96_WCR_EMP       (1 << 13)
+#define RME96_WCR_SEL       (1 << 14)
+#define RME96_WCR_MASTER    (1 << 15)
+#define RME96_WCR_PD        (1 << 16)
+#define RME96_WCR_INP_0     (1 << 17)
+#define RME96_WCR_INP_1     (1 << 18)
+#define RME96_WCR_THRU_0    (1 << 19)
+#define RME96_WCR_THRU_1    (1 << 20)
+#define RME96_WCR_THRU_2    (1 << 21)
+#define RME96_WCR_THRU_3    (1 << 22)
+#define RME96_WCR_THRU_4    (1 << 23)
+#define RME96_WCR_THRU_5    (1 << 24)
+#define RME96_WCR_THRU_6    (1 << 25)
+#define RME96_WCR_THRU_7    (1 << 26)
+#define RME96_WCR_DOLBY     (1 << 27)
+#define RME96_WCR_MONITOR_0 (1 << 28)
+#define RME96_WCR_MONITOR_1 (1 << 29)
+#define RME96_WCR_ISEL      (1 << 30)
+#define RME96_WCR_IDIS      (1 << 31)
+
+#define RME96_WCR_BITPOS_GAIN_0 2
+#define RME96_WCR_BITPOS_GAIN_1 3
+#define RME96_WCR_BITPOS_FREQ_0 9
+#define RME96_WCR_BITPOS_FREQ_1 10
+#define RME96_WCR_BITPOS_INP_0 17
+#define RME96_WCR_BITPOS_INP_1 18
+#define RME96_WCR_BITPOS_MONITOR_0 28
+#define RME96_WCR_BITPOS_MONITOR_1 29
+
+/* Read control register bits */
+#define RME96_RCR_AUDIO_ADDR_MASK 0xFFFF
+#define RME96_RCR_IRQ_2     (1 << 16)
+#define RME96_RCR_T_OUT     (1 << 17)
+#define RME96_RCR_DEV_ID_0  (1 << 21)
+#define RME96_RCR_DEV_ID_1  (1 << 22)
+#define RME96_RCR_LOCK      (1 << 23)
+#define RME96_RCR_VERF      (1 << 26)
+#define RME96_RCR_F0        (1 << 27)
+#define RME96_RCR_F1        (1 << 28)
+#define RME96_RCR_F2        (1 << 29)
+#define RME96_RCR_AUTOSYNC  (1 << 30)
+#define RME96_RCR_IRQ       (1 << 31)
+
+#define RME96_RCR_BITPOS_F0 27
+#define RME96_RCR_BITPOS_F1 28
+#define RME96_RCR_BITPOS_F2 29
+
+/* Additonal register bits */
+#define RME96_AR_WSEL       (1 << 0)
+#define RME96_AR_ANALOG     (1 << 1)
+#define RME96_AR_FREQPAD_0  (1 << 2)
+#define RME96_AR_FREQPAD_1  (1 << 3)
+#define RME96_AR_FREQPAD_2  (1 << 4)
+#define RME96_AR_PD2        (1 << 5)
+#define RME96_AR_DAC_EN     (1 << 6)
+#define RME96_AR_CLATCH     (1 << 7)
+#define RME96_AR_CCLK       (1 << 8)
+#define RME96_AR_CDATA      (1 << 9)
+
+#define RME96_AR_BITPOS_F0 2
+#define RME96_AR_BITPOS_F1 3
+#define RME96_AR_BITPOS_F2 4
+
+/* Monitor tracks */
+#define RME96_MONITOR_TRACKS_1_2 0
+#define RME96_MONITOR_TRACKS_3_4 1
+#define RME96_MONITOR_TRACKS_5_6 2
+#define RME96_MONITOR_TRACKS_7_8 3
+
+/* Attenuation */
+#define RME96_ATTENUATION_0 0
+#define RME96_ATTENUATION_6 1
+#define RME96_ATTENUATION_12 2
+#define RME96_ATTENUATION_18 3
+
+/* Input types */
+#define RME96_INPUT_OPTICAL 0
+#define RME96_INPUT_COAXIAL 1
+#define RME96_INPUT_INTERNAL 2
+#define RME96_INPUT_XLR 3
+#define RME96_INPUT_ANALOG 4
+
+/* Clock modes */
+#define RME96_CLOCKMODE_SLAVE 0
+#define RME96_CLOCKMODE_MASTER 1
+#define RME96_CLOCKMODE_WORDCLOCK 2
+
+/* Block sizes in bytes */
+#define RME96_SMALL_BLOCK_SIZE 2048
+#define RME96_LARGE_BLOCK_SIZE 8192
+
+/* Volume control */
+#define RME96_AD1852_VOL_BITS 14
+#define RME96_AD1855_VOL_BITS 10
+
+/*
+ * PCI vendor/device ids, could in the future be defined in <linux/pci.h>,
+ * therefore #ifndef is used.
+ */
+#ifndef PCI_VENDOR_ID_XILINX
+#define PCI_VENDOR_ID_XILINX 0x10ee
+#endif
+#ifndef PCI_DEVICE_ID_DIGI96
+#define PCI_DEVICE_ID_DIGI96 0x3fc0
+#endif
+#ifndef PCI_DEVICE_ID_DIGI96_8
+#define PCI_DEVICE_ID_DIGI96_8 0x3fc1
+#endif
+#ifndef PCI_DEVICE_ID_DIGI96_8_PRO
+#define PCI_DEVICE_ID_DIGI96_8_PRO 0x3fc2
+#endif
+#ifndef PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST
+#define PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST 0x3fc3
+#endif
+
+typedef struct snd_rme96 {
+	spinlock_t    lock;
+	int irq;
+	unsigned long port;
+	struct resource *res_port;
+	unsigned long iobase;
+	
+	u32 wcreg;    /* cached write control register value */
+	u32 wcreg_spdif;		/* S/PDIF setup */
+	u32 wcreg_spdif_stream;		/* S/PDIF setup (temporary) */
+	u32 rcreg;    /* cached read control register value */
+	u32 areg;     /* cached additional register value */
+	u16 vol[2]; /* cached volume of analog output */
+
+	u8 rev; /* card revision number */
+
+	snd_pcm_substream_t *playback_substream;
+	snd_pcm_substream_t *capture_substream;
+
+	int playback_frlog; /* log2 of framesize */
+	int capture_frlog;
+	
+	size_t playback_periodsize; /* in bytes, zero if not used */
+	size_t capture_periodsize; /* in bytes, zero if not used */
+
+	size_t playback_ptr;
+	size_t capture_ptr;
+
+	snd_card_t         *card;
+	snd_pcm_t          *spdif_pcm;
+	snd_pcm_t          *adat_pcm; 
+	struct pci_dev     *pci;
+	snd_info_entry_t   *proc_entry;
+	snd_kcontrol_t	   *spdif_ctl;
+} rme96_t;
+
+static struct pci_device_id snd_rme96_ids[] __devinitdata = {
+	{ PCI_VENDOR_ID_XILINX, PCI_DEVICE_ID_DIGI96,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },
+	{ PCI_VENDOR_ID_XILINX, PCI_DEVICE_ID_DIGI96_8,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },
+	{ PCI_VENDOR_ID_XILINX, PCI_DEVICE_ID_DIGI96_8_PRO,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },
+	{ PCI_VENDOR_ID_XILINX, PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, 
+	{ 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, snd_rme96_ids);
+
+#define RME96_ISPLAYING(rme96) ((rme96)->wcreg & RME96_WCR_START)
+#define RME96_ISRECORDING(rme96) ((rme96)->wcreg & RME96_WCR_START_2)
+#define	RME96_HAS_ANALOG_IN(rme96) ((rme96)->pci->device == PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST)
+#define	RME96_HAS_ANALOG_OUT(rme96) ((rme96)->pci->device == PCI_DEVICE_ID_DIGI96_8_PRO || \
+				     (rme96)->pci->device == PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST)
+#define	RME96_DAC_IS_1852(rme96) (RME96_HAS_ANALOG_OUT(rme96) && (rme96)->rev >= 4)
+#define	RME96_DAC_IS_1855(rme96) (((rme96)->pci->device == PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST && (rme96)->rev < 4) || \
+			          ((rme96)->pci->device == PCI_DEVICE_ID_DIGI96_8_PRO && (rme96)->rev == 2))
+#define	RME96_185X_MAX_OUT(rme96) ((1 << (RME96_DAC_IS_1852(rme96) ? RME96_AD1852_VOL_BITS : RME96_AD1855_VOL_BITS)) - 1)
+
+static int
+snd_rme96_playback_prepare(snd_pcm_substream_t *substream);
+
+static int
+snd_rme96_capture_prepare(snd_pcm_substream_t *substream);
+
+static int
+snd_rme96_playback_trigger(snd_pcm_substream_t *substream, 
+			   int cmd);
+
+static int
+snd_rme96_capture_trigger(snd_pcm_substream_t *substream, 
+			  int cmd);
+
+static snd_pcm_uframes_t
+snd_rme96_playback_pointer(snd_pcm_substream_t *substream);
+
+static snd_pcm_uframes_t
+snd_rme96_capture_pointer(snd_pcm_substream_t *substream);
+
+static void __init 
+snd_rme96_proc_init(rme96_t *rme96);
+
+static void
+snd_rme96_proc_done(rme96_t *rme96);
+
+static int
+snd_rme96_create_switches(snd_card_t *card,
+			  rme96_t *rme96);
+
+static inline unsigned int
+snd_rme96_playback_ptr(rme96_t *rme96)
+{
+	return (readl(rme96->iobase + RME96_IO_GET_PLAY_POS)
+		& RME96_RCR_AUDIO_ADDR_MASK) >> rme96->playback_frlog;
+}
+
+static inline unsigned int
+snd_rme96_capture_ptr(rme96_t *rme96)
+{
+	return (readl(rme96->iobase + RME96_IO_GET_REC_POS)
+		& RME96_RCR_AUDIO_ADDR_MASK) >> rme96->capture_frlog;
+}
+
+static int
+snd_rme96_playback_silence(snd_pcm_substream_t *substream,
+			   int channel, /* not used (interleaved data) */
+			   snd_pcm_uframes_t pos,
+			   snd_pcm_uframes_t count)
+{
+	rme96_t *rme96 = _snd_pcm_substream_chip(substream);
+	count <<= rme96->playback_frlog;
+	pos <<= rme96->playback_frlog;
+	memset_io(rme96->iobase + RME96_IO_PLAY_BUFFER + pos,
+		  0, count);
+	return 0;
+}
+
+static int
+snd_rme96_playback_copy(snd_pcm_substream_t *substream,
+			int channel, /* not used (interleaved data) */
+			snd_pcm_uframes_t pos,
+			void *src,
+			snd_pcm_uframes_t count)
+{
+	rme96_t *rme96 = _snd_pcm_substream_chip(substream);
+	count <<= rme96->playback_frlog;
+	pos <<= rme96->playback_frlog;
+	copy_from_user_toio(rme96->iobase + RME96_IO_PLAY_BUFFER + pos, src,
+			    count);
+	return 0;
+}
+
+static int
+snd_rme96_capture_copy(snd_pcm_substream_t *substream,
+		       int channel, /* not used (interleaved data) */
+		       snd_pcm_uframes_t pos,
+		       void *dst,
+		       snd_pcm_uframes_t count)
+{
+	rme96_t *rme96 = _snd_pcm_substream_chip(substream);
+	count <<= rme96->capture_frlog;
+	pos <<= rme96->capture_frlog;
+	copy_to_user_fromio(dst, rme96->iobase + RME96_IO_REC_BUFFER + pos,
+			    count);
+        return 0;
+}
+
+/*
+ * Digital output capabilites (S/PDIF)
+ */
+#ifdef TARGET_OS2
+static snd_pcm_hardware_t snd_rme96_playback_spdif_info =
+{
+/*	info:		  */   (SNDRV_PCM_INFO_MMAP |
+			      SNDRV_PCM_INFO_MMAP_VALID |
+			      SNDRV_PCM_INFO_INTERLEAVED |
+			      SNDRV_PCM_INFO_PAUSE),
+/*	formats:	  */   (SNDRV_PCM_FMTBIT_S16_LE |
+			      SNDRV_PCM_FMTBIT_S32_LE),
+/*	rates:		  */   (SNDRV_PCM_RATE_32000 |
+			      SNDRV_PCM_RATE_44100 | 
+			      SNDRV_PCM_RATE_48000 | 
+			      SNDRV_PCM_RATE_64000 |
+			      SNDRV_PCM_RATE_88200 | 
+			      SNDRV_PCM_RATE_96000),
+/*	rate_min:	  */   32000,
+/*	rate_max:	  */   96000,
+/*	channels_min:	  */   2,
+/*	channels_max:	  */   2,
+/*	buffer_bytes_max: */  RME96_BUFFER_SIZE,
+/*	period_bytes_min: */  RME96_SMALL_BLOCK_SIZE,
+/*	period_bytes_max: */  RME96_LARGE_BLOCK_SIZE,
+/*	periods_min:	  */   RME96_BUFFER_SIZE / RME96_LARGE_BLOCK_SIZE,
+/*	periods_max:	  */  RME96_BUFFER_SIZE / RME96_SMALL_BLOCK_SIZE,
+/*	fifo_size:	  */   0,
+};
+
+/*
+ * Digital input capabilites (S/PDIF)
+ */
+static snd_pcm_hardware_t snd_rme96_capture_spdif_info =
+{
+/*	info:		  */   (SNDRV_PCM_INFO_MMAP |
+			      SNDRV_PCM_INFO_MMAP_VALID |
+			      SNDRV_PCM_INFO_INTERLEAVED |
+			      SNDRV_PCM_INFO_PAUSE),
+/*	formats:	  */   (SNDRV_PCM_FMTBIT_S16_LE |
+			      SNDRV_PCM_FMTBIT_S32_LE),
+/*	rates:		  */   (SNDRV_PCM_RATE_32000 |
+			      SNDRV_PCM_RATE_44100 | 
+			      SNDRV_PCM_RATE_48000 | 
+			      SNDRV_PCM_RATE_64000 |
+			      SNDRV_PCM_RATE_88200 | 
+			      SNDRV_PCM_RATE_96000),
+/*	rate_min:	  */   32000,
+/*	rate_max:	  */   96000,
+/*	channels_min:	  */   2,
+/*	channels_max:	  */   2,
+/*	buffer_bytes_max: */  RME96_BUFFER_SIZE,
+/*	period_bytes_min: */  RME96_SMALL_BLOCK_SIZE,
+/*	period_bytes_max: */  RME96_LARGE_BLOCK_SIZE,
+/*	periods_min:	  */   RME96_BUFFER_SIZE / RME96_LARGE_BLOCK_SIZE,
+/*	periods_max:	  */   RME96_BUFFER_SIZE / RME96_SMALL_BLOCK_SIZE,
+/*	fifo_size:	  */   0,
+};
+
+/*
+ * Digital output capabilites (ADAT)
+ */
+static snd_pcm_hardware_t snd_rme96_playback_adat_info =
+{
+/*	info:		  */   (SNDRV_PCM_INFO_MMAP |
+			      SNDRV_PCM_INFO_MMAP_VALID |
+			      SNDRV_PCM_INFO_INTERLEAVED |
+			      SNDRV_PCM_INFO_PAUSE),
+/*	formats:          */   SNDRV_PCM_FMTBIT_S16_LE,
+/*	rates:	          */   (SNDRV_PCM_RATE_44100 | 
+			      SNDRV_PCM_RATE_48000),
+/*	rate_min:         */   44100,
+/*	rate_max:         */   48000,
+/*	channels_min:     */   8,
+/*	channels_max:	  */   8,
+/*	buffer_bytes_max: */  RME96_BUFFER_SIZE,
+/*	period_bytes_min: */  RME96_SMALL_BLOCK_SIZE,
+/*	period_bytes_max: */  RME96_LARGE_BLOCK_SIZE,
+/*	periods_min:	  */   RME96_BUFFER_SIZE / RME96_LARGE_BLOCK_SIZE,
+/*	periods_max:	  */   RME96_BUFFER_SIZE / RME96_SMALL_BLOCK_SIZE,
+/*	fifo_size:	  */   0,
+};
+
+/*
+ * Digital input capabilites (ADAT)
+ */
+static snd_pcm_hardware_t snd_rme96_capture_adat_info =
+{
+/*	info:		  */   (SNDRV_PCM_INFO_MMAP |
+			      SNDRV_PCM_INFO_MMAP_VALID |
+			      SNDRV_PCM_INFO_INTERLEAVED |
+			      SNDRV_PCM_INFO_PAUSE),
+/*	formats:          */   SNDRV_PCM_FMTBIT_S16_LE,
+/*	rates:	          */   (SNDRV_PCM_RATE_44100 | 
+			      SNDRV_PCM_RATE_48000),
+/*	rate_min:         */   44100,
+/*	rate_max:         */   48000,
+/*	channels_min:     */   8,
+/*	channels_max:	  */   8,
+/*	buffer_bytes_max: */  RME96_BUFFER_SIZE,
+/*	period_bytes_min: */  RME96_SMALL_BLOCK_SIZE,
+/*	period_bytes_max: */  RME96_LARGE_BLOCK_SIZE,
+/*	periods_min:	  */   RME96_BUFFER_SIZE / RME96_LARGE_BLOCK_SIZE,
+/*	periods_max:	  */   RME96_BUFFER_SIZE / RME96_SMALL_BLOCK_SIZE,
+/*	fifo_size:        */   0,
+};
+#else
+static snd_pcm_hardware_t snd_rme96_playback_spdif_info =
+{
+	info:		     (SNDRV_PCM_INFO_MMAP |
+			      SNDRV_PCM_INFO_MMAP_VALID |
+			      SNDRV_PCM_INFO_INTERLEAVED |
+			      SNDRV_PCM_INFO_PAUSE),
+	formats:	     (SNDRV_PCM_FMTBIT_S16_LE |
+			      SNDRV_PCM_FMTBIT_S32_LE),
+	rates:		     (SNDRV_PCM_RATE_32000 |
+			      SNDRV_PCM_RATE_44100 | 
+			      SNDRV_PCM_RATE_48000 | 
+			      SNDRV_PCM_RATE_64000 |
+			      SNDRV_PCM_RATE_88200 | 
+			      SNDRV_PCM_RATE_96000),
+	rate_min:	     32000,
+	rate_max:	     96000,
+	channels_min:	     2,
+	channels_max:	     2,
+	buffer_bytes_max:   RME96_BUFFER_SIZE,
+	period_bytes_min:   RME96_SMALL_BLOCK_SIZE,
+	period_bytes_max:   RME96_LARGE_BLOCK_SIZE,
+	periods_min:	     RME96_BUFFER_SIZE / RME96_LARGE_BLOCK_SIZE,
+	periods_max:	     RME96_BUFFER_SIZE / RME96_SMALL_BLOCK_SIZE,
+	fifo_size:	     0,
+};
+
+/*
+ * Digital input capabilites (S/PDIF)
+ */
+static snd_pcm_hardware_t snd_rme96_capture_spdif_info =
+{
+	info:		     (SNDRV_PCM_INFO_MMAP |
+			      SNDRV_PCM_INFO_MMAP_VALID |
+			      SNDRV_PCM_INFO_INTERLEAVED |
+			      SNDRV_PCM_INFO_PAUSE),
+	formats:	     (SNDRV_PCM_FMTBIT_S16_LE |
+			      SNDRV_PCM_FMTBIT_S32_LE),
+	rates:		     (SNDRV_PCM_RATE_32000 |
+			      SNDRV_PCM_RATE_44100 | 
+			      SNDRV_PCM_RATE_48000 | 
+			      SNDRV_PCM_RATE_64000 |
+			      SNDRV_PCM_RATE_88200 | 
+			      SNDRV_PCM_RATE_96000),
+	rate_min:	     32000,
+	rate_max:	     96000,
+	channels_min:	     2,
+	channels_max:	     2,
+	buffer_bytes_max:   RME96_BUFFER_SIZE,
+	period_bytes_min:   RME96_SMALL_BLOCK_SIZE,
+	period_bytes_max:   RME96_LARGE_BLOCK_SIZE,
+	periods_min:	     RME96_BUFFER_SIZE / RME96_LARGE_BLOCK_SIZE,
+	periods_max:	     RME96_BUFFER_SIZE / RME96_SMALL_BLOCK_SIZE,
+	fifo_size:	     0,
+};
+
+/*
+ * Digital output capabilites (ADAT)
+ */
+static snd_pcm_hardware_t snd_rme96_playback_adat_info =
+{
+	info:		     (SNDRV_PCM_INFO_MMAP |
+			      SNDRV_PCM_INFO_MMAP_VALID |
+			      SNDRV_PCM_INFO_INTERLEAVED |
+			      SNDRV_PCM_INFO_PAUSE),
+	formats:             SNDRV_PCM_FMTBIT_S16_LE,
+	rates:	             (SNDRV_PCM_RATE_44100 | 
+			      SNDRV_PCM_RATE_48000),
+	rate_min:            44100,
+	rate_max:            48000,
+	channels_min:        8,
+	channels_max:	     8,
+	buffer_bytes_max:   RME96_BUFFER_SIZE,
+	period_bytes_min:   RME96_SMALL_BLOCK_SIZE,
+	period_bytes_max:   RME96_LARGE_BLOCK_SIZE,
+	periods_min:	     RME96_BUFFER_SIZE / RME96_LARGE_BLOCK_SIZE,
+	periods_max:	     RME96_BUFFER_SIZE / RME96_SMALL_BLOCK_SIZE,
+	fifo_size:	     0,
+};
+
+/*
+ * Digital input capabilites (ADAT)
+ */
+static snd_pcm_hardware_t snd_rme96_capture_adat_info =
+{
+	info:		     (SNDRV_PCM_INFO_MMAP |
+			      SNDRV_PCM_INFO_MMAP_VALID |
+			      SNDRV_PCM_INFO_INTERLEAVED |
+			      SNDRV_PCM_INFO_PAUSE),
+	formats:             SNDRV_PCM_FMTBIT_S16_LE,
+	rates:	             (SNDRV_PCM_RATE_44100 | 
+			      SNDRV_PCM_RATE_48000),
+	rate_min:            44100,
+	rate_max:            48000,
+	channels_min:        8,
+	channels_max:	     8,
+	buffer_bytes_max:   RME96_BUFFER_SIZE,
+	period_bytes_min:   RME96_SMALL_BLOCK_SIZE,
+	period_bytes_max:   RME96_LARGE_BLOCK_SIZE,
+	periods_min:	     RME96_BUFFER_SIZE / RME96_LARGE_BLOCK_SIZE,
+	periods_max:	     RME96_BUFFER_SIZE / RME96_SMALL_BLOCK_SIZE,
+	fifo_size:           0,
+};
+#endif
+
+/*
+ * The CDATA, CCLK and CLATCH bits can be used to write to the SPI interface
+ * of the AD1852 or AD1852 D/A converter on the board.  CDATA must be set up
+ * on the falling edge of CCLK and be stable on the rising edge.  The rising
+ * edge of CLATCH after the last data bit clocks in the whole data word.
+ * A fast processor could probably drive the SPI interface faster than the
+ * DAC can handle (3MHz for the 1855, unknown for the 1852).  The udelay(1)
+ * limits the data rate to 500KHz and only causes a delay of 33 microsecs.
+ *
+ * NOTE: increased delay from 1 to 10, since there where problems setting
+ * the volume.
+ */
+static void
+snd_rme96_write_SPI(rme96_t *rme96, u16 val)
+{
+	int i;
+
+	for (i = 0; i < 16; i++) {
+		if (val & 0x8000) {
+			rme96->areg |= RME96_AR_CDATA;
+		} else {
+			rme96->areg &= ~RME96_AR_CDATA;
+		}
+		rme96->areg &= ~(RME96_AR_CCLK | RME96_AR_CLATCH);
+		writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG);
+		udelay(10);
+		rme96->areg |= RME96_AR_CCLK;
+		writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG);
+		udelay(10);
+		val <<= 1;
+	}
+	rme96->areg &= ~(RME96_AR_CCLK | RME96_AR_CDATA);
+	rme96->areg |= RME96_AR_CLATCH;
+	writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG);
+	udelay(10);
+	rme96->areg &= ~RME96_AR_CLATCH;
+	writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG);
+}
+
+static void
+snd_rme96_apply_dac_volume(rme96_t *rme96)
+{
+	if (RME96_DAC_IS_1852(rme96)) {
+		snd_rme96_write_SPI(rme96, (rme96->vol[0] << 2) | 0x0);
+		snd_rme96_write_SPI(rme96, (rme96->vol[1] << 2) | 0x2);
+	} else if (RME96_DAC_IS_1855(rme96)) {
+		snd_rme96_write_SPI(rme96, (rme96->vol[0] & 0x3FF) | 0x000);
+		snd_rme96_write_SPI(rme96, (rme96->vol[1] & 0x3FF) | 0x400);
+	}
+}
+
+static void
+snd_rme96_reset_dac(rme96_t *rme96)
+{
+	writel(rme96->wcreg | RME96_WCR_PD,
+	       rme96->iobase + RME96_IO_CONTROL_REGISTER);
+	writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
+}
+
+static int
+snd_rme96_getmontracks(rme96_t *rme96)
+{
+	return ((rme96->wcreg >> RME96_WCR_BITPOS_MONITOR_0) & 1) +
+		(((rme96->wcreg >> RME96_WCR_BITPOS_MONITOR_1) & 1) << 1);
+}
+
+static int
+snd_rme96_setmontracks(rme96_t *rme96,
+		       int montracks)
+{
+	if (montracks & 1) {
+		rme96->wcreg |= RME96_WCR_MONITOR_0;
+	} else {
+		rme96->wcreg &= ~RME96_WCR_MONITOR_0;
+	}
+	if (montracks & 2) {
+		rme96->wcreg |= RME96_WCR_MONITOR_1;
+	} else {
+		rme96->wcreg &= ~RME96_WCR_MONITOR_1;
+	}
+	writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
+	return 0;
+}
+
+static int
+snd_rme96_getattenuation(rme96_t *rme96)
+{
+	return ((rme96->wcreg >> RME96_WCR_BITPOS_GAIN_0) & 1) +
+		(((rme96->wcreg >> RME96_WCR_BITPOS_GAIN_1) & 1) << 1);
+}
+
+static int
+snd_rme96_setattenuation(rme96_t *rme96,
+			 int attenuation)
+{
+	switch (attenuation) {
+	case 0:
+		rme96->wcreg = (rme96->wcreg & ~RME96_WCR_GAIN_0) &
+			~RME96_WCR_GAIN_1;
+		break;
+	case 1:
+		rme96->wcreg = (rme96->wcreg | RME96_WCR_GAIN_0) &
+			~RME96_WCR_GAIN_1;
+		break;
+	case 2:
+		rme96->wcreg = (rme96->wcreg & ~RME96_WCR_GAIN_0) |
+			RME96_WCR_GAIN_1;
+		break;
+	case 3:
+		rme96->wcreg = (rme96->wcreg | RME96_WCR_GAIN_0) |
+			RME96_WCR_GAIN_1;
+		break;
+	default:
+		return -EINVAL;
+	}
+	writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
+	return 0;
+}
+
+
+static int
+snd_rme96_playback_getrate(rme96_t *rme96)
+{
+	int rate;
+	
+	rate = ((rme96->wcreg >> RME96_WCR_BITPOS_FREQ_0) & 1) +
+		(((rme96->wcreg >> RME96_WCR_BITPOS_FREQ_1) & 1) << 1);
+	switch (rate) {
+	case 1:
+		rate = 32000;
+		break;
+	case 2:
+		rate = 44100;
+		break;
+	case 3:
+		rate = 48000;
+		break;
+	default:
+		return -1;
+	}
+	return (rme96->wcreg & RME96_WCR_DS) ? rate << 1 : rate;
+}
+
+static int
+snd_rme96_capture_getrate(rme96_t *rme96,
+			  int *is_adat)
+{	
+	int n, rate;
+
+	*is_adat = 0;
+	if (rme96->areg & RME96_AR_ANALOG) {
+		/* Analog input, overrides S/PDIF setting */
+		n = ((rme96->areg >> RME96_AR_BITPOS_F0) & 1) +
+			(((rme96->areg >> RME96_AR_BITPOS_F1) & 1) << 1);
+		switch (n) {
+		case 1:
+			rate = 32000;
+			break;
+		case 2:
+			rate = 44100;
+			break;
+		case 3:
+			rate = 48000;
+			break;
+		default:
+			return -1;
+		}
+		return (rme96->areg & RME96_AR_BITPOS_F2) ? rate << 1 : rate;
+	}
+
+	rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER);
+	if (rme96->rcreg & RME96_RCR_LOCK) {
+		/* ADAT rate */
+		*is_adat = 1;
+		if (rme96->rcreg & RME96_RCR_T_OUT) {
+			return 48000;
+		}
+		return 44100;
+	}
+
+	if (rme96->rcreg & RME96_RCR_VERF) {
+		return -1;
+	}
+	
+	/* S/PDIF rate */
+	n = ((rme96->rcreg >> RME96_RCR_BITPOS_F0) & 1) +
+		(((rme96->rcreg >> RME96_RCR_BITPOS_F1) & 1) << 1) +
+		(((rme96->rcreg >> RME96_RCR_BITPOS_F2) & 1) << 2);
+	
+	switch (n) {
+	case 0:		
+		if (rme96->rcreg & RME96_RCR_T_OUT) {
+			return 64000;
+		}
+		return -1;
+	case 3: return 96000;
+	case 4: return 88200;
+	case 5: return 48000;
+	case 6: return 44100;
+	case 7: return 32000;
+	default:
+		break;
+	}
+	return -1;
+}
+
+static int
+snd_rme96_playback_setrate(rme96_t *rme96,
+			   int rate)
+{
+	int ds;
+
+	ds = rme96->wcreg & RME96_WCR_DS;
+	switch (rate) {
+	case 32000:
+		rme96->wcreg &= ~RME96_WCR_DS;
+		rme96->wcreg = (rme96->wcreg | RME96_WCR_FREQ_0) &
+			~RME96_WCR_FREQ_1;
+		break;
+	case 44100:
+		rme96->wcreg &= ~RME96_WCR_DS;
+		rme96->wcreg = (rme96->wcreg | RME96_WCR_FREQ_1) &
+			~RME96_WCR_FREQ_0;
+		break;
+	case 48000:
+		rme96->wcreg &= ~RME96_WCR_DS;
+		rme96->wcreg = (rme96->wcreg | RME96_WCR_FREQ_0) |
+			RME96_WCR_FREQ_1;
+		break;
+	case 64000:
+		rme96->wcreg |= RME96_WCR_DS;
+		rme96->wcreg = (rme96->wcreg | RME96_WCR_FREQ_0) &
+			~RME96_WCR_FREQ_1;
+		break;
+	case 88200:
+		rme96->wcreg |= RME96_WCR_DS;
+		rme96->wcreg = (rme96->wcreg | RME96_WCR_FREQ_1) &
+			~RME96_WCR_FREQ_0;
+		break;
+	case 96000:
+		rme96->wcreg |= RME96_WCR_DS;
+		rme96->wcreg = (rme96->wcreg | RME96_WCR_FREQ_0) |
+			RME96_WCR_FREQ_1;
+		break;
+	default:
+		return -EINVAL;
+	}
+	if ((!ds && rme96->wcreg & RME96_WCR_DS) ||
+	    (ds && !(rme96->wcreg & RME96_WCR_DS)))
+	{
+		/* change to/from double-speed: reset the DAC (if available) */
+		snd_rme96_reset_dac(rme96);
+	} else {
+		writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
+	}
+	return 0;
+}
+
+static int
+snd_rme96_capture_analog_setrate(rme96_t *rme96,
+				 int rate)
+{
+	switch (rate) {
+	case 32000:
+		rme96->areg = ((rme96->areg | RME96_AR_FREQPAD_0) &
+			       ~RME96_AR_FREQPAD_1) & ~RME96_AR_FREQPAD_2;
+		break;
+	case 44100:
+		rme96->areg = ((rme96->areg & ~RME96_AR_FREQPAD_0) |
+			       RME96_AR_FREQPAD_1) & ~RME96_AR_FREQPAD_2;
+		break;
+	case 48000:
+		rme96->areg = ((rme96->areg | RME96_AR_FREQPAD_0) |
+			       RME96_AR_FREQPAD_1) & ~RME96_AR_FREQPAD_2;
+		break;
+	case 64000:
+		if (rme96->rev < 4) {
+			return -EINVAL;
+		}
+		rme96->areg = ((rme96->areg | RME96_AR_FREQPAD_0) &
+			       ~RME96_AR_FREQPAD_1) | RME96_AR_FREQPAD_2;
+		break;
+	case 88200:
+		if (rme96->rev < 4) {
+			return -EINVAL;
+		}
+		rme96->areg = ((rme96->areg & ~RME96_AR_FREQPAD_0) |
+			       RME96_AR_FREQPAD_1) | RME96_AR_FREQPAD_2;
+		break;
+	case 96000:
+		rme96->areg = ((rme96->areg | RME96_AR_FREQPAD_0) |
+			       RME96_AR_FREQPAD_1) | RME96_AR_FREQPAD_2;
+		break;
+	default:
+		return -EINVAL;
+	}
+	writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG);
+	return 0;
+}
+
+static int
+snd_rme96_setclockmode(rme96_t *rme96,
+		       int mode)
+{
+	switch (mode) {
+	case RME96_CLOCKMODE_SLAVE:
+		rme96->wcreg &= ~RME96_WCR_MASTER;
+		rme96->areg &= ~RME96_AR_WSEL;
+		break;
+	case RME96_CLOCKMODE_MASTER:
+		rme96->wcreg |= RME96_WCR_MASTER;
+		rme96->areg &= ~RME96_AR_WSEL;
+		break;
+	case RME96_CLOCKMODE_WORDCLOCK:
+		/* Word clock is a master mode */
+		rme96->wcreg |= RME96_WCR_MASTER; 
+		rme96->areg |= RME96_AR_WSEL;
+		break;
+	default:
+		return -EINVAL;
+	}
+	writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
+	writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG);
+	return 0;
+}
+
+static int
+snd_rme96_getclockmode(rme96_t *rme96)
+{
+	if (rme96->areg & RME96_AR_WSEL) {
+		return RME96_CLOCKMODE_WORDCLOCK;
+	}
+	return (rme96->wcreg & RME96_WCR_MASTER) ? RME96_CLOCKMODE_MASTER :
+		RME96_CLOCKMODE_SLAVE;
+}
+
+static int
+snd_rme96_setinputtype(rme96_t *rme96,
+		       int type)
+{
+	int n;
+
+	switch (type) {
+	case RME96_INPUT_OPTICAL:
+		rme96->wcreg = (rme96->wcreg & ~RME96_WCR_INP_0) &
+			~RME96_WCR_INP_1;
+		break;
+	case RME96_INPUT_COAXIAL:
+		rme96->wcreg = (rme96->wcreg | RME96_WCR_INP_0) &
+			~RME96_WCR_INP_1;
+		break;
+	case RME96_INPUT_INTERNAL:
+		rme96->wcreg = (rme96->wcreg & ~RME96_WCR_INP_0) |
+			RME96_WCR_INP_1;
+		break;
+	case RME96_INPUT_XLR:
+		if (rme96->pci->device != PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST ||
+		    rme96->pci->device != PCI_DEVICE_ID_DIGI96_8_PRO ||
+		    (rme96->pci->device == PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST &&
+		     rme96->rev > 4))
+		{
+			/* Only Digi96/8 PRO and Digi96/8 PAD supports XLR */
+			return -EINVAL;
+		}
+		rme96->wcreg = (rme96->wcreg | RME96_WCR_INP_0) |
+			RME96_WCR_INP_1;
+		break;
+	case RME96_INPUT_ANALOG:
+		if (!RME96_HAS_ANALOG_IN(rme96)) {
+			return -EINVAL;
+		}
+		rme96->areg |= RME96_AR_ANALOG;
+		writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG);
+		if (rme96->rev < 4) {
+			/*
+			 * Revision less than 004 does not support 64 and
+			 * 88.2 kHz
+			 */
+			if (snd_rme96_capture_getrate(rme96, &n) == 88200) {
+				snd_rme96_capture_analog_setrate(rme96, 44100);
+			}
+			if (snd_rme96_capture_getrate(rme96, &n) == 64000) {
+				snd_rme96_capture_analog_setrate(rme96, 32000);
+			}
+		}
+		return 0;
+	default:
+		return -EINVAL;
+	}
+	if (type != RME96_INPUT_ANALOG && RME96_HAS_ANALOG_IN(rme96)) {
+		rme96->areg &= ~RME96_AR_ANALOG;
+		writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG);
+	}
+	writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
+	return 0;
+}
+
+static int
+snd_rme96_getinputtype(rme96_t *rme96)
+{
+	if (rme96->areg & RME96_AR_ANALOG) {
+		return RME96_INPUT_ANALOG;
+	}
+	return ((rme96->wcreg >> RME96_WCR_BITPOS_INP_0) & 1) +
+		(((rme96->wcreg >> RME96_WCR_BITPOS_INP_1) & 1) << 1);
+}
+
+static void
+snd_rme96_setframelog(rme96_t *rme96,
+		      int n_channels,
+		      int is_playback)
+{
+	int frlog;
+	
+	if (n_channels == 2) {
+		frlog = 1;
+	} else {
+		/* assume 8 channels */
+		frlog = 3;
+	}
+	if (is_playback) {
+		frlog += (rme96->wcreg & RME96_WCR_MODE24) ? 2 : 1;
+		rme96->playback_frlog = frlog;
+	} else {
+		frlog += (rme96->wcreg & RME96_WCR_MODE24_2) ? 2 : 1;
+		rme96->capture_frlog = frlog;
+	}
+}
+
+static int
+snd_rme96_playback_setformat(rme96_t *rme96,
+			     int format)
+{
+	switch (format) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		rme96->wcreg &= ~RME96_WCR_MODE24;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		rme96->wcreg |= RME96_WCR_MODE24;
+		break;
+	default:
+		return -EINVAL;
+	}
+	writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
+	return 0;
+}
+
+static int
+snd_rme96_capture_setformat(rme96_t *rme96,
+			    int format)
+{
+	switch (format) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		rme96->wcreg &= ~RME96_WCR_MODE24_2;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		rme96->wcreg |= RME96_WCR_MODE24_2;
+		break;
+	default:
+		return -EINVAL;
+	}
+	writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
+	return 0;
+}
+
+static void
+snd_rme96_set_period_properties(rme96_t *rme96,
+				size_t period_bytes)
+{
+	switch (period_bytes) {
+	case RME96_LARGE_BLOCK_SIZE:
+		rme96->wcreg &= ~RME96_WCR_ISEL;
+		break;
+	case RME96_SMALL_BLOCK_SIZE:
+		rme96->wcreg |= RME96_WCR_ISEL;
+		break;
+	default:
+		snd_BUG();
+		break;
+	}
+	rme96->wcreg &= ~RME96_WCR_IDIS;
+	writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
+}
+
+static int
+snd_rme96_playback_hw_params(snd_pcm_substream_t *substream,
+			     snd_pcm_hw_params_t *params)
+{
+	unsigned long flags;
+	rme96_t *rme96 = _snd_pcm_substream_chip(substream);
+	int err;
+
+	if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params))) < 0)
+		return err;
+	spin_lock_irqsave(&rme96->lock, flags);
+	if ((err = snd_rme96_playback_setrate(rme96, params_rate(params))) < 0) {
+		spin_unlock_irqrestore(&rme96->lock, flags);
+		return err;
+	}
+	if ((err = snd_rme96_playback_setformat(rme96, params_format(params))) < 0) {
+		spin_unlock_irqrestore(&rme96->lock, flags);
+		return err;
+	}
+	snd_rme96_setframelog(rme96, params_channels(params), 1);
+	if (rme96->capture_periodsize != 0) {
+		if (params_period_size(params) << rme96->playback_frlog !=
+		    rme96->capture_periodsize)
+		{
+			spin_unlock_irqrestore(&rme96->lock, flags);
+			return -EBUSY;
+		}
+	}
+	rme96->playback_periodsize =
+		params_period_size(params) << rme96->playback_frlog;
+	snd_rme96_set_period_properties(rme96, rme96->playback_periodsize);
+	/* S/PDIF setup */
+	if ((rme96->wcreg & RME96_WCR_ADAT) == 0) {
+		rme96->wcreg &= ~(RME96_WCR_PRO | RME96_WCR_DOLBY | RME96_WCR_EMP);
+		writel(rme96->wcreg |= rme96->wcreg_spdif_stream, rme96->iobase + RME96_IO_CONTROL_REGISTER);
+	}
+	spin_unlock_irqrestore(&rme96->lock, flags);
+		
+	return 0;
+}
+
+static int
+snd_rme96_playback_hw_free(snd_pcm_substream_t *substream)
+{
+	snd_pcm_lib_free_pages(substream);
+	return 0;
+}
+
+static int
+snd_rme96_capture_hw_params(snd_pcm_substream_t *substream,
+			    snd_pcm_hw_params_t *params)
+{
+	unsigned long flags;
+	rme96_t *rme96 = _snd_pcm_substream_chip(substream);
+	int err, isadat;
+	
+	if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params))) < 0)
+		return err;
+	spin_lock_irqsave(&rme96->lock, flags);
+	if ((err = snd_rme96_capture_setformat(rme96, params_format(params))) < 0) {
+		spin_unlock_irqrestore(&rme96->lock, flags);
+		return err;
+	}
+	if (snd_rme96_getinputtype(rme96) == RME96_INPUT_ANALOG) {
+		if ((err = snd_rme96_capture_analog_setrate(rme96,
+							    params_rate(params))) < 0)
+		{
+			spin_unlock_irqrestore(&rme96->lock, flags);
+			return err;
+		}
+	} else if (params_rate(params) != snd_rme96_capture_getrate(rme96, &isadat)) {
+		spin_unlock_irqrestore(&rme96->lock, flags);
+		return -EBUSY;
+	}
+	snd_rme96_setframelog(rme96, params_channels(params), 0);
+	if (rme96->playback_periodsize != 0) {
+		if (params_period_size(params) << rme96->capture_frlog !=
+		    rme96->playback_periodsize)
+		{
+			spin_unlock_irqrestore(&rme96->lock, flags);
+			return -EBUSY;
+		}
+	}
+	rme96->capture_periodsize =
+		params_period_size(params) << rme96->capture_frlog;
+	snd_rme96_set_period_properties(rme96, rme96->capture_periodsize);
+	spin_unlock_irqrestore(&rme96->lock, flags);
+
+	return 0;
+}
+
+static int
+snd_rme96_capture_hw_free(snd_pcm_substream_t *substream)
+{
+	snd_pcm_lib_free_pages(substream);
+	return 0;
+}
+
+static void
+snd_rme96_playback_start(rme96_t *rme96,
+			 int from_pause)
+{
+	snd_pcm_runtime_t *runtime = rme96->playback_substream->runtime;
+	
+	if (!from_pause) {
+		writel(0, rme96->iobase + RME96_IO_RESET_PLAY_POS);
+		if (runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) {
+			memcpy_toio(rme96->iobase + RME96_IO_PLAY_BUFFER,
+				    runtime->dma_area,
+				    rme96->playback_periodsize);
+			rme96->playback_ptr = rme96->playback_periodsize;
+		}
+	}
+
+	rme96->wcreg |= RME96_WCR_START;
+	writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
+}
+
+static void
+snd_rme96_capture_start(rme96_t *rme96,
+			int from_pause)
+{
+	if (!from_pause) {
+		writel(0, rme96->iobase + RME96_IO_RESET_REC_POS);
+	}
+
+	rme96->wcreg |= RME96_WCR_START_2;
+	writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
+}
+
+static void
+snd_rme96_playback_stop(rme96_t *rme96)
+{
+	/*
+	 * Check if there is an unconfirmed IRQ, if so confirm it, or else
+	 * the hardware will not stop generating interrupts
+	 */
+	rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER);
+	if (rme96->rcreg & RME96_RCR_IRQ) {
+		writel(0, rme96->iobase + RME96_IO_CONFIRM_PLAY_IRQ);
+	}	
+	rme96->wcreg &= ~RME96_WCR_START;
+	writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
+}
+
+static void
+snd_rme96_capture_stop(rme96_t *rme96)
+{
+	rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER);
+	if (rme96->rcreg & RME96_RCR_IRQ_2) {
+		writel(0, rme96->iobase + RME96_IO_CONFIRM_REC_IRQ);
+	}	
+	rme96->wcreg &= ~RME96_WCR_START_2;
+	writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
+}
+
+static void
+snd_rme96_interrupt(int irq,
+		    void *dev_id,
+		    struct pt_regs *regs)
+{
+	rme96_t *rme96 = (rme96_t *)dev_id;
+	snd_pcm_runtime_t *runtime;
+
+	rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER);
+	/* fastpath out, to ease interrupt sharing */
+	if (!((rme96->rcreg & RME96_RCR_IRQ) ||
+	      (rme96->rcreg & RME96_RCR_IRQ_2)))
+	{
+		return;
+	}
+	
+	if (rme96->rcreg & RME96_RCR_IRQ) {
+		/* playback */
+		runtime = rme96->playback_substream->runtime;
+		if (runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) {
+			memcpy_toio(rme96->iobase + RME96_IO_PLAY_BUFFER +
+				    rme96->playback_ptr,
+				    runtime->dma_area + rme96->playback_ptr,
+				    rme96->playback_periodsize);
+			rme96->playback_ptr += rme96->playback_periodsize;
+			if (rme96->playback_ptr == RME96_BUFFER_SIZE) {
+				rme96->playback_ptr = 0;
+			}
+		}
+		snd_pcm_period_elapsed(rme96->playback_substream);
+		writel(0, rme96->iobase + RME96_IO_CONFIRM_PLAY_IRQ);
+	}
+	if (rme96->rcreg & RME96_RCR_IRQ_2) {
+		/* capture */
+		runtime = rme96->capture_substream->runtime;
+		if (runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) {
+			memcpy_fromio(runtime->dma_area + rme96->capture_ptr,
+				      rme96->iobase + RME96_IO_REC_BUFFER +
+				      rme96->capture_ptr,
+				      rme96->capture_periodsize);
+			rme96->capture_ptr += rme96->capture_periodsize;
+			if (rme96->capture_ptr == RME96_BUFFER_SIZE) {
+				rme96->capture_ptr = 0;
+			}
+		}
+		snd_pcm_period_elapsed(rme96->capture_substream);		
+		writel(0, rme96->iobase + RME96_IO_CONFIRM_REC_IRQ);
+	}
+}
+
+static unsigned int period_bytes[] = { RME96_SMALL_BLOCK_SIZE, RME96_LARGE_BLOCK_SIZE };
+
+#define PERIOD_BYTES sizeof(period_bytes) / sizeof(period_bytes[0])
+
+#ifdef TARGET_OS2
+static snd_pcm_hw_constraint_list_t hw_constraints_period_bytes = {
+	PERIOD_BYTES,
+	period_bytes,
+	0
+};
+#else
+static snd_pcm_hw_constraint_list_t hw_constraints_period_bytes = {
+	count: PERIOD_BYTES,
+	list: period_bytes,
+	mask: 0
+};
+#endif
+
+static int
+snd_rme96_playback_spdif_open(snd_pcm_substream_t *substream)
+{
+	unsigned long flags;
+	rme96_t *rme96 = _snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	snd_pcm_set_sync(substream);
+
+	spin_lock_irqsave(&rme96->lock, flags);	
+	rme96->wcreg &= ~RME96_WCR_ADAT;
+	writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
+	rme96->playback_substream = substream;
+	rme96->playback_ptr = 0;
+	spin_unlock_irqrestore(&rme96->lock, flags);
+
+	runtime->hw = snd_rme96_playback_spdif_info;	
+	snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, RME96_BUFFER_SIZE, RME96_BUFFER_SIZE);
+	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_period_bytes);
+
+	rme96->wcreg_spdif_stream = rme96->wcreg_spdif;
+	rme96->spdif_ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+	snd_ctl_notify(rme96->card, SNDRV_CTL_EVENT_MASK_VALUE |
+		       SNDRV_CTL_EVENT_MASK_INFO, &rme96->spdif_ctl->id);
+	return 0;
+}
+
+static int
+snd_rme96_capture_spdif_open(snd_pcm_substream_t *substream)
+{
+	unsigned long flags;
+	int isadat;
+	rme96_t *rme96 = _snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER);
+	if (snd_rme96_capture_getrate(rme96, &isadat) < 0) {
+		/* no input */
+		return -EIO;
+	}
+	if (isadat) {
+		/* ADAT input */
+		return -EBUSY;
+	}
+	snd_pcm_set_sync(substream);
+
+	spin_lock_irqsave(&rme96->lock, flags);	
+	rme96->capture_substream = substream;
+	rme96->capture_ptr = 0;
+	spin_unlock_irqrestore(&rme96->lock, flags);
+	
+	runtime->hw = snd_rme96_capture_spdif_info;
+	
+	snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, RME96_BUFFER_SIZE, RME96_BUFFER_SIZE);
+	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_period_bytes);
+
+	return 0;
+}
+
+static int
+snd_rme96_playback_adat_open(snd_pcm_substream_t *substream)
+{
+	unsigned long flags;
+	rme96_t *rme96 = _snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	
+	snd_pcm_set_sync(substream);
+
+	spin_lock_irqsave(&rme96->lock, flags);	
+	rme96->wcreg |= RME96_WCR_ADAT;
+	writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
+	rme96->playback_substream = substream;
+	rme96->playback_ptr = 0;
+	spin_unlock_irqrestore(&rme96->lock, flags);
+	
+	runtime->hw = snd_rme96_playback_adat_info;
+	snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, RME96_BUFFER_SIZE, RME96_BUFFER_SIZE);
+	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_period_bytes);
+	return 0;
+}
+
+static int
+snd_rme96_capture_adat_open(snd_pcm_substream_t *substream)
+{
+	unsigned long flags;
+	int isadat;
+	rme96_t *rme96 = _snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER);
+	if (snd_rme96_capture_getrate(rme96, &isadat) < 0) {
+		/* no input */
+		return -EIO;
+	}
+	if (!isadat) {
+		/* S/PDIF input */
+		return -EBUSY;
+	}
+	snd_pcm_set_sync(substream);
+
+	spin_lock_irqsave(&rme96->lock, flags);	
+	rme96->capture_substream = substream;
+	rme96->capture_ptr = 0;
+	spin_unlock_irqrestore(&rme96->lock, flags);
+
+	runtime->hw = snd_rme96_capture_adat_info;
+	snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, RME96_BUFFER_SIZE, RME96_BUFFER_SIZE);
+	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_period_bytes);
+	return 0;
+}
+
+static int
+snd_rme96_playback_close(snd_pcm_substream_t *substream)
+{
+	unsigned long flags;
+	rme96_t *rme96 = _snd_pcm_substream_chip(substream);
+	int spdif = 0;
+
+	spin_lock_irqsave(&rme96->lock, flags);	
+	rme96->playback_substream = NULL;
+	rme96->playback_periodsize = 0;
+	spdif = (rme96->wcreg & RME96_WCR_ADAT) == 0;
+	spin_unlock_irqrestore(&rme96->lock, flags);
+	if (spdif) {
+		rme96->spdif_ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+		snd_ctl_notify(rme96->card, SNDRV_CTL_EVENT_MASK_VALUE |
+			       SNDRV_CTL_EVENT_MASK_INFO, &rme96->spdif_ctl->id);
+	}
+	return 0;
+}
+
+static int
+snd_rme96_capture_close(snd_pcm_substream_t *substream)
+{
+	unsigned long flags;
+	rme96_t *rme96 = _snd_pcm_substream_chip(substream);
+	
+	spin_lock_irqsave(&rme96->lock, flags);	
+	rme96->capture_substream = NULL;
+	rme96->capture_periodsize = 0;
+	spin_unlock_irqrestore(&rme96->lock, flags);
+	return 0;
+}
+
+static int
+snd_rme96_playback_prepare(snd_pcm_substream_t *substream)
+{
+	rme96_t *rme96 = _snd_pcm_substream_chip(substream);
+	unsigned long flags;
+	
+	spin_lock_irqsave(&rme96->lock, flags);	
+	if (RME96_ISPLAYING(rme96)) {
+		snd_rme96_playback_stop(rme96);
+	}
+	writel(0, rme96->iobase + RME96_IO_RESET_PLAY_POS);
+	spin_unlock_irqrestore(&rme96->lock, flags);
+	return 0;
+}
+
+static int
+snd_rme96_capture_prepare(snd_pcm_substream_t *substream)
+{
+	rme96_t *rme96 = _snd_pcm_substream_chip(substream);
+	unsigned long flags;
+	
+	spin_lock_irqsave(&rme96->lock, flags);	
+	if (RME96_ISRECORDING(rme96)) {
+		snd_rme96_capture_stop(rme96);
+	}
+	writel(0, rme96->iobase + RME96_IO_RESET_REC_POS);
+	spin_unlock_irqrestore(&rme96->lock, flags);
+	return 0;
+}
+
+static int
+snd_rme96_playback_trigger(snd_pcm_substream_t *substream, 
+			   int cmd)
+{
+	rme96_t *rme96 = _snd_pcm_substream_chip(substream);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		if (!RME96_ISPLAYING(rme96)) {
+			if (substream != rme96->playback_substream) {
+				return -EBUSY;
+			}
+			snd_rme96_playback_start(rme96, 0);
+		}
+		break;
+
+	case SNDRV_PCM_TRIGGER_STOP:
+		if (RME96_ISPLAYING(rme96)) {
+			if (substream != rme96->playback_substream) {
+				return -EBUSY;
+			}
+			snd_rme96_playback_stop(rme96);
+		}
+		break;
+
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		if (RME96_ISPLAYING(rme96)) {
+			snd_rme96_playback_stop(rme96);
+		}
+		break;
+
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		if (!RME96_ISPLAYING(rme96)) {
+			snd_rme96_playback_start(rme96, 1);
+		}
+		break;
+		
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int
+snd_rme96_capture_trigger(snd_pcm_substream_t *substream, 
+			  int cmd)
+{
+	rme96_t *rme96 = _snd_pcm_substream_chip(substream);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		if (!RME96_ISRECORDING(rme96)) {
+			if (substream != rme96->capture_substream) {
+				return -EBUSY;
+			}
+			snd_rme96_capture_start(rme96, 0);
+		}
+		break;
+
+	case SNDRV_PCM_TRIGGER_STOP:
+		if (RME96_ISRECORDING(rme96)) {
+			if (substream != rme96->capture_substream) {
+				return -EBUSY;
+			}
+			snd_rme96_capture_stop(rme96);
+		}
+		break;
+
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		if (RME96_ISRECORDING(rme96)) {
+			snd_rme96_capture_stop(rme96);
+		}
+		break;
+
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		if (!RME96_ISRECORDING(rme96)) {
+			snd_rme96_capture_start(rme96, 1);
+		}
+		break;
+		
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static snd_pcm_uframes_t
+snd_rme96_playback_pointer(snd_pcm_substream_t *substream)
+{
+	rme96_t *rme96 = _snd_pcm_substream_chip(substream);
+	return snd_rme96_playback_ptr(rme96);
+}
+
+static snd_pcm_uframes_t
+snd_rme96_capture_pointer(snd_pcm_substream_t *substream)
+{
+	rme96_t *rme96 = _snd_pcm_substream_chip(substream);
+	return snd_rme96_capture_ptr(rme96);
+}
+
+#ifdef TARGET_OS2
+static snd_pcm_ops_t snd_rme96_playback_spdif_ops = {
+/*	open:	  */	snd_rme96_playback_spdif_open,
+/*	close:	  */	snd_rme96_playback_close,
+/*	ioctl:	  */	snd_pcm_lib_ioctl,
+/*	hw_params:*/	snd_rme96_playback_hw_params,
+/*	hw_free:  */	snd_rme96_playback_hw_free,
+/*	prepare:  */	snd_rme96_playback_prepare,
+/*	trigger:  */	snd_rme96_playback_trigger,
+/*	pointer:  */	snd_rme96_playback_pointer,
+/*	copy:	  */	snd_rme96_playback_copy,
+/*	silence:  */	snd_rme96_playback_silence,
+};
+
+static snd_pcm_ops_t snd_rme96_capture_spdif_ops = {
+/*	open:	  */	snd_rme96_capture_spdif_open,
+/*	close:	  */	snd_rme96_capture_close,
+/*	ioctl:	  */	snd_pcm_lib_ioctl,
+/*	hw_params:*/	snd_rme96_capture_hw_params,
+/*	hw_free:  */	snd_rme96_capture_hw_free,
+/*	prepare:  */	snd_rme96_capture_prepare,
+/*	trigger:  */	snd_rme96_capture_trigger,
+/*	pointer:  */	snd_rme96_capture_pointer,
+/*	copy:	  */	snd_rme96_capture_copy,
+        0
+};
+
+static snd_pcm_ops_t snd_rme96_playback_adat_ops = {
+/*	open:	  */	snd_rme96_playback_adat_open,
+/*	close:	  */	snd_rme96_playback_close,
+/*	ioctl:	  */	snd_pcm_lib_ioctl,
+/*	hw_params:*/	snd_rme96_playback_hw_params,
+/*	hw_free:  */	snd_rme96_playback_hw_free,
+/*	prepare:  */	snd_rme96_playback_prepare,
+/*	trigger:  */	snd_rme96_playback_trigger,
+/*	pointer:  */	snd_rme96_playback_pointer,
+/*	copy:	  */	snd_rme96_playback_copy,
+/*	silence:  */	snd_rme96_playback_silence,
+};
+
+static snd_pcm_ops_t snd_rme96_capture_adat_ops = {
+/*	open:	  */	snd_rme96_capture_adat_open,
+/*	close:	  */	snd_rme96_capture_close,
+/*	ioctl:	  */	snd_pcm_lib_ioctl,
+/*	hw_params:*/	snd_rme96_capture_hw_params,
+/*	hw_free:  */	snd_rme96_capture_hw_free,
+/*	prepare:  */	snd_rme96_capture_prepare,
+/*	trigger:  */	snd_rme96_capture_trigger,
+/*	pointer:  */	snd_rme96_capture_pointer,
+/*	copy:	  */	snd_rme96_capture_copy,
+};
+#else
+static snd_pcm_ops_t snd_rme96_playback_spdif_ops = {
+	open:		snd_rme96_playback_spdif_open,
+	close:		snd_rme96_playback_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	hw_params:	snd_rme96_playback_hw_params,
+	hw_free:	snd_rme96_playback_hw_free,
+	prepare:	snd_rme96_playback_prepare,
+	trigger:	snd_rme96_playback_trigger,
+	pointer:	snd_rme96_playback_pointer,
+	copy:		snd_rme96_playback_copy,
+	silence:	snd_rme96_playback_silence,
+};
+
+static snd_pcm_ops_t snd_rme96_capture_spdif_ops = {
+	open:		snd_rme96_capture_spdif_open,
+	close:		snd_rme96_capture_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	hw_params:	snd_rme96_capture_hw_params,
+	hw_free:	snd_rme96_capture_hw_free,
+	prepare:	snd_rme96_capture_prepare,
+	trigger:	snd_rme96_capture_trigger,
+	pointer:	snd_rme96_capture_pointer,
+	copy:		snd_rme96_capture_copy,
+};
+
+static snd_pcm_ops_t snd_rme96_playback_adat_ops = {
+	open:		snd_rme96_playback_adat_open,
+	close:		snd_rme96_playback_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	hw_params:	snd_rme96_playback_hw_params,
+	hw_free:	snd_rme96_playback_hw_free,
+	prepare:	snd_rme96_playback_prepare,
+	trigger:	snd_rme96_playback_trigger,
+	pointer:	snd_rme96_playback_pointer,
+	copy:		snd_rme96_playback_copy,
+	silence:	snd_rme96_playback_silence,
+};
+
+static snd_pcm_ops_t snd_rme96_capture_adat_ops = {
+	open:		snd_rme96_capture_adat_open,
+	close:		snd_rme96_capture_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	hw_params:	snd_rme96_capture_hw_params,
+	hw_free:	snd_rme96_capture_hw_free,
+	prepare:	snd_rme96_capture_prepare,
+	trigger:	snd_rme96_capture_trigger,
+	pointer:	snd_rme96_capture_pointer,
+	copy:		snd_rme96_capture_copy,
+};
+#endif
+
+static void
+snd_rme96_free(void *private_data)
+{
+	rme96_t *rme96 = (rme96_t *)private_data;
+
+	if (rme96 == NULL) {
+	        return;
+	}
+	if (rme96->irq >= 0) {
+		snd_rme96_playback_stop(rme96);
+		snd_rme96_capture_stop(rme96);
+		rme96->areg &= ~RME96_AR_DAC_EN;
+		writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG);
+		snd_rme96_proc_done(rme96);
+		free_irq(rme96->irq, (void *)rme96);
+		rme96->irq = -1;
+	}
+	if (rme96->iobase) {
+		iounmap((void *)rme96->iobase);
+		rme96->iobase = 0;
+	}
+	if (rme96->res_port != NULL) {
+		release_resource(rme96->res_port);
+		rme96->res_port = NULL;
+	}
+}
+
+static void
+snd_rme96_free_spdif_pcm(snd_pcm_t *pcm)
+{
+	rme96_t *rme96 = (rme96_t *) pcm->private_data;
+	rme96->spdif_pcm = NULL;
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static void
+snd_rme96_free_adat_pcm(snd_pcm_t *pcm)
+{
+	rme96_t *rme96 = (rme96_t *) pcm->private_data;
+	rme96->adat_pcm = NULL;
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static int __init
+snd_rme96_create(rme96_t *rme96)
+{
+	struct pci_dev *pci = rme96->pci;
+	int err;
+
+	rme96->irq = -1;
+
+	if ((err = pci_enable_device(pci)) < 0)
+		return err;
+
+	rme96->port = pci_resource_start(rme96->pci, 0);
+
+	if ((rme96->res_port = request_mem_region(rme96->port, RME96_IO_SIZE, "RME96")) == NULL) {
+		snd_printk("unable to grab memory region 0x%lx-0x%lx\n", rme96->port, rme96->port + RME96_IO_SIZE - 1);
+		return -EBUSY;
+	}
+
+	if (request_irq(pci->irq, snd_rme96_interrupt, SA_INTERRUPT|SA_SHIRQ, "RME96", (void *)rme96)) {
+		snd_printk("unable to grab IRQ %d\n", pci->irq);
+		return -EBUSY;
+	}
+	rme96->irq = pci->irq;
+
+	spin_lock_init(&rme96->lock);
+	if ((rme96->iobase = (unsigned long) ioremap(rme96->port, RME96_IO_SIZE)) == 0) {
+		snd_printk("unable to remap memory region 0x%lx-0x%lx\n", rme96->port, rme96->port + RME96_IO_SIZE - 1);
+		return -ENOMEM;
+	}
+
+	/* read the card's revision number */
+	pci_read_config_byte(pci, 8, &rme96->rev);	
+	
+	/* set up ALSA pcm device for S/PDIF */
+	if ((err = snd_pcm_new(rme96->card, "Digi96 IEC958", 0,
+			       1, 1, &rme96->spdif_pcm)) < 0)
+	{
+		return err;
+	}
+	rme96->spdif_pcm->private_data = rme96;
+	rme96->spdif_pcm->private_free = snd_rme96_free_spdif_pcm;
+	strcpy(rme96->spdif_pcm->name, "Digi96 IEC958");
+	snd_pcm_set_ops(rme96->spdif_pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_rme96_playback_spdif_ops);
+	snd_pcm_set_ops(rme96->spdif_pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_rme96_capture_spdif_ops);
+
+	rme96->spdif_pcm->info_flags = 0;
+
+	snd_pcm_lib_preallocate_pages_for_all(rme96->spdif_pcm, RME96_BUFFER_SIZE, RME96_BUFFER_SIZE, GFP_KERNEL);
+
+	/* set up ALSA pcm device for ADAT */
+	if (pci->device == PCI_DEVICE_ID_DIGI96) {
+		/* ADAT is not available on the base model */
+		rme96->adat_pcm = NULL;
+	} else {
+		if ((err = snd_pcm_new(rme96->card, "Digi96 ADAT", 1,
+				       1, 1, &rme96->adat_pcm)) < 0)
+		{
+			return err;
+		}		
+		rme96->adat_pcm->private_data = rme96;
+		rme96->adat_pcm->private_free = snd_rme96_free_adat_pcm;
+		strcpy(rme96->adat_pcm->name, "Digi96 ADAT");
+		snd_pcm_set_ops(rme96->adat_pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_rme96_playback_adat_ops);
+		snd_pcm_set_ops(rme96->adat_pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_rme96_capture_adat_ops);
+		
+		rme96->adat_pcm->info_flags = 0;
+
+		snd_pcm_lib_preallocate_pages_for_all(rme96->adat_pcm, RME96_BUFFER_SIZE, RME96_BUFFER_SIZE, GFP_KERNEL);
+	}
+
+	rme96->playback_periodsize = 0;
+	rme96->capture_periodsize = 0;
+	
+	/* make sure playback/capture is stopped, if by some reason active */
+	snd_rme96_playback_stop(rme96);
+	snd_rme96_capture_stop(rme96);
+	
+	/* set default values in registers */
+	rme96->wcreg =
+		RME96_WCR_FREQ_1 | /* set 44.1 kHz playback */
+		RME96_WCR_SEL |    /* normal playback */
+		RME96_WCR_MASTER | /* set to master clock mode */
+		RME96_WCR_INP_0;   /* set coaxial input */
+
+	rme96->areg = RME96_AR_FREQPAD_1; /* set 44.1 kHz analog capture */
+
+	writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
+	writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG);
+	
+	/* reset the ADC */
+	writel(rme96->areg | RME96_AR_PD2,
+	       rme96->iobase + RME96_IO_ADDITIONAL_REG);
+	writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG);	
+
+	/* reset and enable the DAC (order is important). */
+	snd_rme96_reset_dac(rme96);
+	rme96->areg |= RME96_AR_DAC_EN;
+	writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG);
+
+	/* reset playback and record buffer pointers */
+	writel(0, rme96->iobase + RME96_IO_RESET_PLAY_POS);
+	writel(0, rme96->iobase + RME96_IO_RESET_REC_POS);
+
+	/* reset volume */
+	rme96->vol[0] = rme96->vol[1] = 0;
+	if (RME96_HAS_ANALOG_OUT(rme96)) {
+		snd_rme96_apply_dac_volume(rme96);
+	}
+	
+	/* init switch interface */
+	if ((err = snd_rme96_create_switches(rme96->card, rme96)) < 0) {
+		return err;
+	}
+
+        /* init proc interface */
+	snd_rme96_proc_init(rme96);
+	
+	return 0;
+}
+
+/*
+ * proc interface
+ */
+
+static void 
+snd_rme96_proc_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer)
+{
+	int n;
+	rme96_t *rme96 = (rme96_t *)entry->private_data;
+	
+	rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER);
+
+	snd_iprintf(buffer, rme96->card->longname);
+	snd_iprintf(buffer, " (index #%d)\n", rme96->card->number + 1);
+
+	snd_iprintf(buffer, "\nGeneral settings\n");
+	if (rme96->wcreg & RME96_WCR_IDIS) {
+		snd_iprintf(buffer, "  period size: N/A (interrupts "
+			    "disabled)\n");
+	} else if (rme96->wcreg & RME96_WCR_ISEL) {
+		snd_iprintf(buffer, "  period size: 2048 bytes\n");
+	} else {
+		snd_iprintf(buffer, "  period size: 8192 bytes\n");
+	}	
+	snd_iprintf(buffer, "\nInput settings\n");
+	switch (snd_rme96_getinputtype(rme96)) {
+	case RME96_INPUT_OPTICAL:
+		snd_iprintf(buffer, "  input: optical");
+		break;
+	case RME96_INPUT_COAXIAL:
+		snd_iprintf(buffer, "  input: coaxial");
+		break;
+	case RME96_INPUT_INTERNAL:
+		snd_iprintf(buffer, "  input: internal");
+		break;
+	case RME96_INPUT_XLR:
+		snd_iprintf(buffer, "  input: XLR");
+		break;
+	case RME96_INPUT_ANALOG:
+		snd_iprintf(buffer, "  input: analog");
+		break;
+	}
+	if (snd_rme96_capture_getrate(rme96, &n) < 0) {
+		snd_iprintf(buffer, "\n  sample rate: no valid signal\n");
+	} else {
+		if (n) {
+			snd_iprintf(buffer, " (8 channels)\n");
+		} else {
+			snd_iprintf(buffer, " (2 channels)\n");
+		}
+		snd_iprintf(buffer, "  sample rate: %d Hz\n",
+			    snd_rme96_capture_getrate(rme96, &n));
+	}
+	if (rme96->wcreg & RME96_WCR_MODE24_2) {
+		snd_iprintf(buffer, "  sample format: 24 bit\n");
+	} else {
+		snd_iprintf(buffer, "  sample format: 16 bit\n");
+	}
+	
+	snd_iprintf(buffer, "\nOutput settings\n");
+	if (rme96->wcreg & RME96_WCR_SEL) {
+		snd_iprintf(buffer, "  output signal: normal playback\n");
+	} else {
+		snd_iprintf(buffer, "  output signal: same as input\n");
+	}
+	snd_iprintf(buffer, "  sample rate: %d Hz\n",
+		    snd_rme96_playback_getrate(rme96));
+	if (rme96->wcreg & RME96_WCR_MODE24) {
+		snd_iprintf(buffer, "  sample format: 24 bit\n");
+	} else {
+		snd_iprintf(buffer, "  sample format: 16 bit\n");
+	}
+	if (rme96->areg & RME96_AR_WSEL) {
+		snd_iprintf(buffer, "  clock mode: word clock\n");
+	} else if (rme96->wcreg & RME96_WCR_MASTER) {
+		snd_iprintf(buffer, "  clock mode: master\n");
+	} else {
+		snd_iprintf(buffer, "  clock mode: slave\n");
+	}
+	if (rme96->wcreg & RME96_WCR_PRO) {
+		snd_iprintf(buffer, "  format: AES/EBU (professional)\n");
+	} else {
+		snd_iprintf(buffer, "  format: IEC958 (consumer)\n");
+	}
+	if (rme96->wcreg & RME96_WCR_EMP) {
+		snd_iprintf(buffer, "  emphasis: on\n");
+	} else {
+		snd_iprintf(buffer, "  emphasis: off\n");
+	}
+	if (rme96->wcreg & RME96_WCR_DOLBY) {
+		snd_iprintf(buffer, "  non-audio (dolby): on\n");
+	} else {
+		snd_iprintf(buffer, "  non-audio (dolby): off\n");
+	}
+	if (RME96_HAS_ANALOG_IN(rme96)) {
+		snd_iprintf(buffer, "\nAnalog output settings\n");
+		switch (snd_rme96_getmontracks(rme96)) {
+		case RME96_MONITOR_TRACKS_1_2:
+			snd_iprintf(buffer, "  monitored ADAT tracks: 1+2\n");
+			break;
+		case RME96_MONITOR_TRACKS_3_4:
+			snd_iprintf(buffer, "  monitored ADAT tracks: 3+4\n");
+			break;
+		case RME96_MONITOR_TRACKS_5_6:
+			snd_iprintf(buffer, "  monitored ADAT tracks: 5+6\n");
+			break;
+		case RME96_MONITOR_TRACKS_7_8:
+			snd_iprintf(buffer, "  monitored ADAT tracks: 7+8\n");
+			break;
+		}
+		switch (snd_rme96_getattenuation(rme96)) {
+		case RME96_ATTENUATION_0:
+			snd_iprintf(buffer, "  attenuation: 0 dB\n");
+			break;
+		case RME96_ATTENUATION_6:
+			snd_iprintf(buffer, "  attenuation: -6 dB\n");
+			break;
+		case RME96_ATTENUATION_12:
+			snd_iprintf(buffer, "  attenuation: -12 dB\n");
+			break;
+		case RME96_ATTENUATION_18:
+			snd_iprintf(buffer, "  attenuation: -18 dB\n");
+			break;
+		}
+		snd_iprintf(buffer, "  volume left: %u\n", rme96->vol[0]);
+		snd_iprintf(buffer, "  volume right: %u\n", rme96->vol[1]);
+	}
+}
+
+static void __init 
+snd_rme96_proc_init(rme96_t *rme96)
+{
+	snd_info_entry_t *entry;
+
+	if ((entry = snd_info_create_card_entry(rme96->card, "rme96", rme96->card->proc_root)) != NULL) {
+		entry->content = SNDRV_INFO_CONTENT_TEXT;
+		entry->private_data = rme96;
+		entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
+		entry->c.text.read_size = 256;
+		entry->c.text.read = snd_rme96_proc_read;
+		if (snd_info_register(entry) < 0) {
+			snd_info_free_entry(entry);
+			entry = NULL;
+		}
+	}
+	rme96->proc_entry = entry;
+}
+
+static void
+snd_rme96_proc_done(rme96_t * rme96)
+{
+	if (rme96->proc_entry) {
+		snd_info_unregister(rme96->proc_entry);
+		rme96->proc_entry = NULL;
+	}
+}
+
+/*
+ * control interface
+ */
+
+static int
+snd_rme96_info_loopback_control(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_rme96_get_loopback_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	rme96_t *rme96 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	
+	spin_lock_irqsave(&rme96->lock, flags);
+	ucontrol->value.integer.value[0] = rme96->wcreg & RME96_WCR_SEL ? 0 : 1;
+	spin_unlock_irqrestore(&rme96->lock, flags);
+	return 0;
+}
+static int
+snd_rme96_put_loopback_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	rme96_t *rme96 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	unsigned int val;
+	int change;
+	
+	val = ucontrol->value.integer.value[0] ? 0 : RME96_WCR_SEL;
+	spin_lock_irqsave(&rme96->lock, flags);
+	val = (rme96->wcreg & ~RME96_WCR_SEL) | val;
+	change = val != rme96->wcreg;
+	writel(rme96->wcreg = val, rme96->iobase + RME96_IO_CONTROL_REGISTER);
+	spin_unlock_irqrestore(&rme96->lock, flags);
+	return change;
+}
+
+static int
+snd_rme96_info_inputtype_control(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+#ifdef TARGET_OS2
+	static char *_texts[5] = { "Optical", "Coaxial", "Internal", "XLR", "Analog" };
+	rme96_t *rme96 = _snd_kcontrol_chip(kcontrol);
+	char *texts[5] = { "Optical", "Coaxial", "Internal", "XLR", "Analog" };
+#else
+	static char *_texts[5] = { "Optical", "Coaxial", "Internal", "XLR", "Analog" };
+	rme96_t *rme96 = _snd_kcontrol_chip(kcontrol);
+	char *texts[5] = { _texts[0], _texts[1], _texts[2], _texts[3], _texts[4] };
+#endif
+	
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	switch (rme96->pci->device) {
+	case PCI_DEVICE_ID_DIGI96:
+	case PCI_DEVICE_ID_DIGI96_8:
+		uinfo->value.enumerated.items = 3;
+		break;
+	case PCI_DEVICE_ID_DIGI96_8_PRO:
+		uinfo->value.enumerated.items = 4;
+		break;
+	case PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST:
+		if (rme96->rev > 4) {
+			/* PST */
+			uinfo->value.enumerated.items = 4;
+			texts[3] = _texts[4]; /* Analog instead of XLR */
+		} else {
+			/* PAD */
+			uinfo->value.enumerated.items = 5;
+		}
+		break;
+	default:
+		snd_BUG();
+		break;
+	}
+	if (uinfo->value.enumerated.item > uinfo->value.enumerated.items - 1) {
+		uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
+	}
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+static int
+snd_rme96_get_inputtype_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	rme96_t *rme96 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int items = 3;
+	
+	spin_lock_irqsave(&rme96->lock, flags);
+	ucontrol->value.enumerated.item[0] = snd_rme96_getinputtype(rme96);
+	
+	switch (rme96->pci->device) {
+	case PCI_DEVICE_ID_DIGI96:
+	case PCI_DEVICE_ID_DIGI96_8:
+		items = 3;
+		break;
+	case PCI_DEVICE_ID_DIGI96_8_PRO:
+		items = 4;
+		break;
+	case PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST:
+		if (rme96->rev > 4) {
+			/* for handling PST case, (INPUT_ANALOG is moved to INPUT_XLR */
+			if (ucontrol->value.enumerated.item[0] == RME96_INPUT_ANALOG) {
+				ucontrol->value.enumerated.item[0] = RME96_INPUT_XLR;
+			}
+			items = 4;
+		} else {
+			items = 5;
+		}
+		break;
+	default:
+		snd_BUG();
+		break;
+	}
+	if (ucontrol->value.enumerated.item[0] >= items) {
+		ucontrol->value.enumerated.item[0] = items - 1;
+	}
+	
+	spin_unlock_irqrestore(&rme96->lock, flags);
+	return 0;
+}
+static int
+snd_rme96_put_inputtype_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	rme96_t *rme96 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	unsigned int val;
+	int change, items = 3;
+	
+	switch (rme96->pci->device) {
+	case PCI_DEVICE_ID_DIGI96:
+	case PCI_DEVICE_ID_DIGI96_8:
+		items = 3;
+		break;
+	case PCI_DEVICE_ID_DIGI96_8_PRO:
+		items = 4;
+		break;
+	case PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST:
+		if (rme96->rev > 4) {
+			items = 4;
+		} else {
+			items = 5;
+		}
+		break;
+	default:
+		snd_BUG();
+		break;
+	}
+	val = ucontrol->value.enumerated.item[0] % items;
+	
+	/* special case for PST */
+	if (rme96->pci->device == PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST && rme96->rev > 4) {
+		if (val == RME96_INPUT_XLR) {
+			val = RME96_INPUT_ANALOG;
+		}
+	}
+	
+	spin_lock_irqsave(&rme96->lock, flags);
+	change = val != snd_rme96_getinputtype(rme96);
+	snd_rme96_setinputtype(rme96, val);
+	spin_unlock_irqrestore(&rme96->lock, flags);
+	return change;
+}
+
+static int
+snd_rme96_info_clockmode_control(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	static char *texts[3] = { "Slave", "Master", "Wordclock" };
+	
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 3;
+	if (uinfo->value.enumerated.item > 2) {
+		uinfo->value.enumerated.item = 2;
+	}
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+static int
+snd_rme96_get_clockmode_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	rme96_t *rme96 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	
+	spin_lock_irqsave(&rme96->lock, flags);
+	ucontrol->value.enumerated.item[0] = snd_rme96_getclockmode(rme96);
+	spin_unlock_irqrestore(&rme96->lock, flags);
+	return 0;
+}
+static int
+snd_rme96_put_clockmode_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	rme96_t *rme96 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	unsigned int val;
+	int change;
+	
+	val = ucontrol->value.enumerated.item[0] % 3;
+	spin_lock_irqsave(&rme96->lock, flags);
+	change = val != snd_rme96_getclockmode(rme96);
+	snd_rme96_setclockmode(rme96, val);
+	spin_unlock_irqrestore(&rme96->lock, flags);
+	return change;
+}
+
+static int
+snd_rme96_info_attenuation_control(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	static char *texts[4] = { "0 dB", "-6 dB", "-12 dB", "-18 dB" };
+	
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 4;
+	if (uinfo->value.enumerated.item > 3) {
+		uinfo->value.enumerated.item = 3;
+	}
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+static int
+snd_rme96_get_attenuation_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	rme96_t *rme96 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	
+	spin_lock_irqsave(&rme96->lock, flags);
+	ucontrol->value.enumerated.item[0] = snd_rme96_getattenuation(rme96);
+	spin_unlock_irqrestore(&rme96->lock, flags);
+	return 0;
+}
+static int
+snd_rme96_put_attenuation_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	rme96_t *rme96 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	unsigned int val;
+	int change;
+	
+	val = ucontrol->value.enumerated.item[0] % 4;
+	spin_lock_irqsave(&rme96->lock, flags);
+	change = val != snd_rme96_getattenuation(rme96);
+	snd_rme96_setattenuation(rme96, val);
+	spin_unlock_irqrestore(&rme96->lock, flags);
+	return change;
+}
+
+static int
+snd_rme96_info_montracks_control(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	static char *texts[4] = { "1+2", "3+4", "5+6", "7+8" };
+	
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 4;
+	if (uinfo->value.enumerated.item > 3) {
+		uinfo->value.enumerated.item = 3;
+	}
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+static int
+snd_rme96_get_montracks_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	rme96_t *rme96 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	
+	spin_lock_irqsave(&rme96->lock, flags);
+	ucontrol->value.enumerated.item[0] = snd_rme96_getmontracks(rme96);
+	spin_unlock_irqrestore(&rme96->lock, flags);
+	return 0;
+}
+static int
+snd_rme96_put_montracks_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	rme96_t *rme96 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	unsigned int val;
+	int change;
+	
+	val = ucontrol->value.enumerated.item[0] % 4;
+	spin_lock_irqsave(&rme96->lock, flags);
+	change = val != snd_rme96_getmontracks(rme96);
+	snd_rme96_setmontracks(rme96, val);
+	spin_unlock_irqrestore(&rme96->lock, flags);
+	return change;
+}
+
+static u32 snd_rme96_convert_from_aes(snd_aes_iec958_t *aes)
+{
+	u32 val = 0;
+	val |= (aes->status[0] & IEC958_AES0_PROFESSIONAL) ? RME96_WCR_PRO : 0;
+	val |= (aes->status[0] & IEC958_AES0_NONAUDIO) ? RME96_WCR_DOLBY : 0;
+	if (val & RME96_WCR_PRO)
+		val |= (aes->status[0] & IEC958_AES0_PRO_EMPHASIS_5015) ? RME96_WCR_EMP : 0;
+	else
+		val |= (aes->status[0] & IEC958_AES0_CON_EMPHASIS_5015) ? RME96_WCR_EMP : 0;
+	return val;
+}
+
+static void snd_rme96_convert_to_aes(snd_aes_iec958_t *aes, u32 val)
+{
+	aes->status[0] = ((val & RME96_WCR_PRO) ? IEC958_AES0_PROFESSIONAL : 0) |
+			 ((val & RME96_WCR_DOLBY) ? IEC958_AES0_NONAUDIO : 0);
+	if (val & RME96_WCR_PRO)
+		aes->status[0] |= (val & RME96_WCR_EMP) ? IEC958_AES0_PRO_EMPHASIS_5015 : 0;
+	else
+		aes->status[0] |= (val & RME96_WCR_EMP) ? IEC958_AES0_CON_EMPHASIS_5015 : 0;
+}
+
+static int snd_rme96_control_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+	uinfo->count = 1;
+	return 0;
+}
+
+static int snd_rme96_control_spdif_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	rme96_t *rme96 = _snd_kcontrol_chip(kcontrol);
+	
+	snd_rme96_convert_to_aes(&ucontrol->value.iec958, rme96->wcreg_spdif);
+	return 0;
+}
+
+static int snd_rme96_control_spdif_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	rme96_t *rme96 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int change;
+	u32 val;
+	
+	val = snd_rme96_convert_from_aes(&ucontrol->value.iec958);
+	spin_lock_irqsave(&rme96->lock, flags);
+	change = val != rme96->wcreg_spdif;
+	rme96->wcreg_spdif = val;
+	spin_unlock_irqrestore(&rme96->lock, flags);
+	return change;
+}
+
+static int snd_rme96_control_spdif_stream_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+	uinfo->count = 1;
+	return 0;
+}
+
+static int snd_rme96_control_spdif_stream_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	rme96_t *rme96 = _snd_kcontrol_chip(kcontrol);
+	
+	snd_rme96_convert_to_aes(&ucontrol->value.iec958, rme96->wcreg_spdif_stream);
+	return 0;
+}
+
+static int snd_rme96_control_spdif_stream_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	rme96_t *rme96 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int change;
+	u32 val;
+	
+	val = snd_rme96_convert_from_aes(&ucontrol->value.iec958);
+	spin_lock_irqsave(&rme96->lock, flags);
+	change = val != rme96->wcreg_spdif_stream;
+	rme96->wcreg_spdif_stream = val;
+	rme96->wcreg &= ~(RME96_WCR_PRO | RME96_WCR_DOLBY | RME96_WCR_EMP);
+	writel(rme96->wcreg |= val, rme96->iobase + RME96_IO_CONTROL_REGISTER);
+	spin_unlock_irqrestore(&rme96->lock, flags);
+	return change;
+}
+
+static int snd_rme96_control_spdif_mask_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+	uinfo->count = 1;
+	return 0;
+}
+
+static int snd_rme96_control_spdif_mask_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ucontrol->value.iec958.status[0] = kcontrol->private_value;
+	return 0;
+}
+
+static int
+snd_rme96_dac_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	rme96_t *rme96 = _snd_kcontrol_chip(kcontrol);
+	
+        uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+        uinfo->count = 2;
+        uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = RME96_185X_MAX_OUT(rme96);
+        return 0;
+}
+
+static int
+snd_rme96_dac_volume_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u)
+{
+	rme96_t *rme96 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+
+	spin_lock_irqsave(&rme96->lock, flags);
+        u->value.integer.value[0] = rme96->vol[0];
+        u->value.integer.value[1] = rme96->vol[1];
+	spin_unlock_irqrestore(&rme96->lock, flags);
+
+        return 0;
+}
+
+static int
+snd_rme96_dac_volume_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u)
+{
+	rme96_t *rme96 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+        int change = 0;
+
+	if (!RME96_HAS_ANALOG_OUT(rme96)) {
+		return -EINVAL;
+	}
+	spin_lock_irqsave(&rme96->lock, flags);
+        if (u->value.integer.value[0] != rme96->vol[0]) {
+		rme96->vol[0] = u->value.integer.value[0];
+                change = 1;
+        }
+        if (u->value.integer.value[1] != rme96->vol[1]) {
+		rme96->vol[1] = u->value.integer.value[1];
+                change = 1;
+        }
+	if (change) {
+		snd_rme96_apply_dac_volume(rme96);
+	}
+	spin_unlock_irqrestore(&rme96->lock, flags);
+
+        return change;
+}
+
+#ifdef TARGET_OS2
+static snd_kcontrol_new_t snd_rme96_controls[] = {
+{
+			SNDRV_CTL_ELEM_IFACE_PCM,0,0,
+			SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),0,0,0,
+			snd_rme96_control_spdif_info,
+			snd_rme96_control_spdif_get,
+			snd_rme96_control_spdif_put,0
+},
+{
+			SNDRV_CTL_ELEM_IFACE_PCM,0,0,
+			SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM),0,
+			SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,0,
+			snd_rme96_control_spdif_stream_info,
+			snd_rme96_control_spdif_stream_get,
+			snd_rme96_control_spdif_stream_put,0
+},
+{
+			SNDRV_CTL_ELEM_IFACE_MIXER,0,0,
+			SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK),0,
+			SNDRV_CTL_ELEM_ACCESS_READ,0,
+			snd_rme96_control_spdif_mask_info,
+			snd_rme96_control_spdif_mask_get,0,
+	 		IEC958_AES0_NONAUDIO |
+			IEC958_AES0_PROFESSIONAL |
+			IEC958_AES0_CON_EMPHASIS
+},
+{
+			SNDRV_CTL_ELEM_IFACE_MIXER,0,0,
+			SNDRV_CTL_NAME_IEC958("",PLAYBACK,PRO_MASK),0,
+			SNDRV_CTL_ELEM_ACCESS_READ,0,
+			snd_rme96_control_spdif_mask_info,
+			snd_rme96_control_spdif_mask_get,0,
+			IEC958_AES0_NONAUDIO |
+			IEC958_AES0_PROFESSIONAL |
+			IEC958_AES0_PRO_EMPHASIS
+},
+{
+                  SNDRV_CTL_ELEM_IFACE_PCM,0,0,
+	          "Input Connector",0,0,0,
+	          snd_rme96_info_inputtype_control, 
+	          snd_rme96_get_inputtype_control,
+	          snd_rme96_put_inputtype_control, 0
+},
+{
+                  SNDRV_CTL_ELEM_IFACE_PCM,0,0,
+	           "Loopback Input",0,0,0,
+	           snd_rme96_info_loopback_control,
+	            snd_rme96_get_loopback_control,
+	            snd_rme96_put_loopback_control,0
+},
+{
+        SNDRV_CTL_ELEM_IFACE_PCM,0,0,
+        "Clock Mode",0,0,0,
+        snd_rme96_info_clockmode_control, 
+        snd_rme96_get_clockmode_control,
+        snd_rme96_put_clockmode_control,0
+}, 
+{
+        SNDRV_CTL_ELEM_IFACE_PCM,0,0,
+        "Monitor Tracks",0,0,0,
+        snd_rme96_info_montracks_control, 
+        snd_rme96_get_montracks_control,
+        snd_rme96_put_montracks_control,0
+},
+{
+        SNDRV_CTL_ELEM_IFACE_PCM,0,0,
+	"Attenuation",0,0,0,
+	snd_rme96_info_attenuation_control, 
+	snd_rme96_get_attenuation_control,
+	snd_rme96_put_attenuation_control,0
+},
+{
+        SNDRV_CTL_ELEM_IFACE_MIXER,0,0,
+	"DAC Playback Volume",0,0,0,
+	snd_rme96_dac_volume_info,
+	snd_rme96_dac_volume_get,
+	snd_rme96_dac_volume_put,0
+}
+};
+#else
+static snd_kcontrol_new_t snd_rme96_controls[] = {
+{
+	iface:		SNDRV_CTL_ELEM_IFACE_PCM,
+	name:		SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
+	info:		snd_rme96_control_spdif_info,
+	get:		snd_rme96_control_spdif_get,
+	put:		snd_rme96_control_spdif_put
+},
+{
+	access:		SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+	iface:		SNDRV_CTL_ELEM_IFACE_PCM,
+	name:		SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM),
+	info:		snd_rme96_control_spdif_stream_info,
+	get:		snd_rme96_control_spdif_stream_get,
+	put:		snd_rme96_control_spdif_stream_put
+},
+{
+	access:		SNDRV_CTL_ELEM_ACCESS_READ,
+	iface:		SNDRV_CTL_ELEM_IFACE_MIXER,
+	name:		SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK),
+	info:		snd_rme96_control_spdif_mask_info,
+	get:		snd_rme96_control_spdif_mask_get,
+	private_value:	IEC958_AES0_NONAUDIO |
+			IEC958_AES0_PROFESSIONAL |
+			IEC958_AES0_CON_EMPHASIS
+},
+{
+	access:		SNDRV_CTL_ELEM_ACCESS_READ,
+	iface:		SNDRV_CTL_ELEM_IFACE_MIXER,
+	name:		SNDRV_CTL_NAME_IEC958("",PLAYBACK,PRO_MASK),
+	info:		snd_rme96_control_spdif_mask_info,
+	get:		snd_rme96_control_spdif_mask_get,
+	private_value:	IEC958_AES0_NONAUDIO |
+			IEC958_AES0_PROFESSIONAL |
+			IEC958_AES0_PRO_EMPHASIS
+},
+{
+        iface:          SNDRV_CTL_ELEM_IFACE_PCM,
+	name:           "Input Connector",
+	info:           snd_rme96_info_inputtype_control, 
+	get:            snd_rme96_get_inputtype_control,
+	put:            snd_rme96_put_inputtype_control 
+},
+{
+        iface:          SNDRV_CTL_ELEM_IFACE_PCM,
+	name:           "Loopback Input",
+	info:           snd_rme96_info_loopback_control,
+	get:            snd_rme96_get_loopback_control,
+	put:            snd_rme96_put_loopback_control
+},
+{
+        iface:          SNDRV_CTL_ELEM_IFACE_PCM,
+	name:           "Clock Mode",
+	info:           snd_rme96_info_clockmode_control, 
+	get:            snd_rme96_get_clockmode_control,
+	put:            snd_rme96_put_clockmode_control
+},
+{
+        iface:          SNDRV_CTL_ELEM_IFACE_PCM,
+	name:           "Monitor Tracks",
+	info:           snd_rme96_info_montracks_control, 
+	get:            snd_rme96_get_montracks_control,
+	put:            snd_rme96_put_montracks_control
+},
+{
+        iface:          SNDRV_CTL_ELEM_IFACE_PCM,
+	name:           "Attenuation",
+	info:           snd_rme96_info_attenuation_control, 
+	get:            snd_rme96_get_attenuation_control,
+	put:            snd_rme96_put_attenuation_control
+},
+{
+        iface:          SNDRV_CTL_ELEM_IFACE_MIXER,
+	name:           "DAC Playback Volume",
+	info:           snd_rme96_dac_volume_info,
+	get:            snd_rme96_dac_volume_get,
+	put:            snd_rme96_dac_volume_put
+}
+};
+#endif
+
+static int
+snd_rme96_create_switches(snd_card_t *card,
+			  rme96_t *rme96)
+{
+	int idx, err;
+	snd_kcontrol_t *kctl;
+
+	for (idx = 0; idx < 7; idx++) {
+		if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_rme96_controls[idx], rme96))) < 0)
+			return err;
+		if (idx == 1)	/* IEC958 (S/PDIF) Stream */
+			rme96->spdif_ctl = kctl;
+	}
+
+	if (RME96_HAS_ANALOG_OUT(rme96)) {
+		for (idx = 7; idx < 10; idx++)
+			if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_rme96_controls[idx], rme96))) < 0)
+				return err;
+	}
+	
+	return 0;
+}
+
+/*
+ * Card initialisation
+ */
+
+static void snd_rme96_card_free(snd_card_t *card)
+{
+	snd_rme96_free(card->private_data);
+}
+
+static int __init
+snd_rme96_probe(struct pci_dev *pci,
+		const struct pci_device_id *id)
+{
+	static int dev = 0;
+	rme96_t *rme96;
+	snd_card_t *card;
+	int err;
+	u8 val;
+
+	for ( ; dev < SNDRV_CARDS; dev++) {
+		if (!snd_enable[dev]) {
+			dev++;
+			return -ENOENT;
+		}
+		break;
+	}
+	if (dev >= SNDRV_CARDS) {
+		return -ENODEV;
+	}
+	if ((card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE,
+				 sizeof(rme96_t))) == NULL)
+		return -ENOMEM;
+	card->private_free = snd_rme96_card_free;
+	rme96 = (rme96_t *)card->private_data;	
+	rme96->card = card;
+	rme96->pci = pci;
+	if ((err = snd_rme96_create(rme96)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	
+	strcpy(card->driver, "Digi96");
+	switch (rme96->pci->device) {
+	case PCI_DEVICE_ID_DIGI96:
+		strcpy(card->shortname, "RME Digi96");
+		break;
+	case PCI_DEVICE_ID_DIGI96_8:
+		strcpy(card->shortname, "RME Digi96/8");
+		break;
+	case PCI_DEVICE_ID_DIGI96_8_PRO:
+		strcpy(card->shortname, "RME Digi96/8 PRO");
+		break;
+	case PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST:
+		pci_read_config_byte(rme96->pci, 8, &val);
+		if (val < 5) {
+			strcpy(card->shortname, "RME Digi96/8 PAD");
+		} else {
+			strcpy(card->shortname, "RME Digi96/8 PST");
+		}
+		break;
+	}
+	sprintf(card->longname, "%s at 0x%lx, irq %d", card->shortname,
+		rme96->port, rme96->irq);
+	
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;	
+	}
+	PCI_SET_DRIVER_DATA(pci, card);
+	dev++;
+	return 0;
+}
+
+static void __exit snd_rme96_remove(struct pci_dev *pci)
+{
+	snd_card_free(PCI_GET_DRIVER_DATA(pci));
+	PCI_SET_DRIVER_DATA(pci, NULL);
+}
+
+#ifdef TARGET_OS2
+static struct pci_driver driver = {
+	0,0,0, "RME Digi96",
+	snd_rme96_ids,
+	snd_rme96_probe,
+	snd_rme96_remove,0,0
+};
+#else
+static struct pci_driver driver = {
+	name: "RME Digi96",
+	id_table: snd_rme96_ids,
+	probe: snd_rme96_probe,
+	remove: snd_rme96_remove,
+};
+#endif
+
+static int __init alsa_card_rme96_init(void)
+{
+	int err;
+
+	if ((err = pci_module_init(&driver)) < 0) {
+#ifdef MODULE
+//		snd_printk("No RME Digi96 cards found\n");
+#endif
+		return err;
+	}
+	return 0;
+}
+
+static void __exit alsa_card_rme96_exit(void)
+{
+	pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_rme96_init)
+module_exit(alsa_card_rme96_exit)
+
+#ifndef MODULE
+
+/* format is: snd-card-rme96=snd_enable,snd_index,snd_id */
+
+static int __init alsa_card_rme96_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);
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-card-rme96=", alsa_card_rme96_setup);
+
+#endif /* ifndef MODULE */
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/rme9652/makefile.os2
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/rme9652/makefile.os2	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/rme9652/makefile.os2	(revision 91)
@@ -0,0 +1,51 @@
+CINCLUDES = ..\..\..\include;..\..\include;$(%WATCOM)\H
+AINCLUDES = $(DDK_BASE)\H
+
+CDEFINES = -D__KERNEL__ -DMODULE -dTARGET_OS2 -dALSA_BUILD -D__ISAPNP__ -DCONFIG_ISAPNP -D__i386_
+
+32BIT=1
+!include ..\..\..\..\makefile.inc
+
+
+#===================================================================
+#
+#   List of source files
+#
+#===================================================================
+FILE1    = rme9652_mem.obj rme9652.obj
+FILE2    = 
+FILE3    = 
+FILE4    = 
+FILE5    = 
+FILE6    = 
+FILE7    = 
+FILE8    = 
+FILELAST =
+FILES    = $(FILE0) $(FILE1) $(FILE2) $(FILE3) $(FILE4) $(FILE5) $(FILE6) $(FILE7) $(FILE8) $(FILE9) $(FILE10) $(FILE11) $(FILE12)
+
+TARGET   = rme9652
+
+LNKFILE  = $(OBJDIR)\$(TARGET).lnk
+
+
+#===================================================================
+#
+#   Specific dependencies
+#
+#===================================================================
+all: $(OBJDIR)\$(TARGET).lib 
+
+
+$(LNKFILE): makefile.os2
+    @%create $(LNKFILE)
+    @for %f in ($(FILES)) do @%append $(LNKFILE) -+$(OBJDIR)\%f
+
+$(OBJDIR)\$(TARGET).lib: $(LNKFILE) $(FILES)
+    $(LIB) $(OBJDIR)\$(TARGET).lib @$(LNKFILE)
+    copy $(OBJDIR)\$(TARGET).lib $(ALSA_LIB) 
+
+clean: .SYMBOLIC
+    -@rm  *.err 2>nul
+    @cd $(OBJDIR)
+    -rm *.obj *.lnk *.sys *.sym *.lst *.map *.wmap *.lib 2>nul
+    @cd ..\..
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/rme9652/rme9652.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/rme9652/rme9652.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/rme9652/rme9652.c	(revision 91)
@@ -0,0 +1,2984 @@
+/*
+ *   ALSA driver for RME Digi9652 audio interfaces 
+ *
+ *	Copyright (c) 1999 IEM - Winfried Ritsch
+ *      Copyright (c) 1999-2001  Paul Davis
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#define SNDRV_MAIN_OBJECT_FILE
+#include <sound/driver.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/info.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+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;	/* Enable this card */
+
+EXPORT_NO_SYMBOLS;
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for RME Digi9652 (Hammerfall) soundcard.");
+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 RME Digi9652 (Hammerfall) soundcard.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable/disable specific RME96{52,36} soundcards.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+MODULE_AUTHOR("Paul Davis <pbd@op.net>, Winfried Ritsch");
+MODULE_DESCRIPTION("RME Digi9652/Digi9636");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{RME,Hammerfall},"
+		"{RME,Hammerfall-Light}}");
+
+/* The Hammerfall has two sets of 24 ADAT + 2 S/PDIF channels, one for
+   capture, one for playback. Both the ADAT and S/PDIF channels appear
+   to the host CPU in the same block of memory. There is no functional
+   difference between them in terms of access.
+   
+   The Hammerfall Light is identical to the Hammerfall, except that it
+   has 2 sets 18 channels (16 ADAT + 2 S/PDIF) for capture and playback.
+*/
+
+#define RME9652_NCHANNELS       26
+#define RME9636_NCHANNELS       18
+
+/* Preferred sync source choices - used by "sync_pref" control switch */
+
+#define RME9652_SYNC_FROM_SPDIF 0
+#define RME9652_SYNC_FROM_ADAT1 1
+#define RME9652_SYNC_FROM_ADAT2 2
+#define RME9652_SYNC_FROM_ADAT3 3
+
+/* Possible sources of S/PDIF input */
+
+#define RME9652_SPDIFIN_OPTICAL 0	/* optical (ADAT1) */
+#define RME9652_SPDIFIN_COAXIAL 1	/* coaxial (RCA) */
+#define RME9652_SPDIFIN_INTERN  2	/* internal (CDROM) */
+
+/* ------------- Status-Register bits --------------------- */
+
+#define RME9652_IRQ	   (1<<0)	/* IRQ is High if not reset by irq_clear */
+#define RME9652_lock_2	   (1<<1)	/* ADAT 3-PLL: 1=locked, 0=unlocked */
+#define RME9652_lock_1	   (1<<2)	/* ADAT 2-PLL: 1=locked, 0=unlocked */
+#define RME9652_lock_0	   (1<<3)	/* ADAT 1-PLL: 1=locked, 0=unlocked */
+#define RME9652_fs48	   (1<<4)	/* sample rate is 0=44.1/88.2,1=48/96 Khz */
+#define RME9652_wsel_rd	   (1<<5)	/* if Word-Clock is used and valid then 1 */
+                                        /* bits 6-15 encode h/w buffer pointer position */
+#define RME9652_sync_2	   (1<<16)	/* if ADAT-IN 3 in sync to system clock */
+#define RME9652_sync_1	   (1<<17)	/* if ADAT-IN 2 in sync to system clock */
+#define RME9652_sync_0	   (1<<18)	/* if ADAT-IN 1 in sync to system clock */
+#define RME9652_DS_rd	   (1<<19)	/* 1=Double Speed Mode, 0=Normal Speed */
+#define RME9652_tc_busy	   (1<<20)	/* 1=time-code copy in progress (960ms) */
+#define RME9652_tc_out	   (1<<21)	/* time-code out bit */
+#define RME9652_F_0	   (1<<22)	/* 000=64kHz, 100=88.2kHz, 011=96kHz  */
+#define RME9652_F_1	   (1<<23)	/* 111=32kHz, 110=44.1kHz, 101=48kHz, */
+#define RME9652_F_2	   (1<<24)	/* external Crystal Chip if ERF=1 */
+#define RME9652_ERF	   (1<<25)	/* Error-Flag of SDPIF Receiver (1=No Lock) */
+#define RME9652_buffer_id  (1<<26)	/* toggles by each interrupt on rec/play */
+#define RME9652_tc_valid   (1<<27)	/* 1 = a signal is detected on time-code input */
+#define RME9652_SPDIF_READ (1<<28)      /* byte available from Rev 1.5+ S/PDIF interface */
+
+#define RME9652_sync	  (RME9652_sync_0|RME9652_sync_1|RME9652_sync_2)
+#define RME9652_lock	  (RME9652_lock_0|RME9652_lock_1|RME9652_lock_2)
+#define RME9652_F	  (RME9652_F_0|RME9652_F_1|RME9652_F_2)
+#define rme9652_decode_spdif_rate(x) ((x)>>22)
+
+/* Bit 6..15 : h/w buffer pointer */
+
+#define RME9652_buf_pos	  0x000FFC0
+
+/* Bits 31,30,29 are bits 5,4,3 of h/w pointer position on later
+   Rev G EEPROMS and Rev 1.5 cards or later.
+*/ 
+
+#define RME9652_REV15_buf_pos(x) ((((x)&0xE0000000)>>26)|((x)&RME9652_buf_pos))
+
+#ifndef PCI_VENDOR_ID_XILINX
+#define PCI_VENDOR_ID_XILINX		0x10ee
+#endif
+#ifndef PCI_DEVICE_ID_XILINX_HAMMERFALL
+#define PCI_DEVICE_ID_XILINX_HAMMERFALL	0x3fc4
+#endif
+
+/* amount of io space we remap for register access. i'm not sure we
+   even need this much, but 1K is nice round number :)
+*/
+
+#define RME9652_IO_EXTENT     1024
+
+#define RME9652_init_buffer       0
+#define RME9652_play_buffer       32	/* holds ptr to 26x64kBit host RAM */
+#define RME9652_rec_buffer        36	/* holds ptr to 26x64kBit host RAM */
+#define RME9652_control_register  64
+#define RME9652_irq_clear         96
+#define RME9652_time_code         100	/* useful if used with alesis adat */
+#define RME9652_thru_base         128	/* 132...228 Thru for 26 channels */
+
+/* Read-only registers */
+
+/* Writing to any of the register locations writes to the status
+   register. We'll use the first location as our point of access.
+*/
+
+#define RME9652_status_register    0
+
+/* --------- Control-Register Bits ---------------- */
+
+
+#define RME9652_start_bit	   (1<<0)	/* start record/play */
+                                                /* bits 1-3 encode buffersize/latency */
+#define RME9652_Master		   (1<<4)	/* Clock Mode Master=1,Slave/Auto=0 */
+#define RME9652_IE		   (1<<5)	/* Interupt Enable */
+#define RME9652_freq		   (1<<6)       /* samplerate 0=44.1/88.2, 1=48/96 kHz */
+#define RME9652_freq1		   (1<<7)       /* if 0, 32kHz, else always 1 */
+#define RME9652_DS                 (1<<8)	/* Doule Speed 0=44.1/48, 1=88.2/96 Khz */
+#define RME9652_PRO		   (1<<9)	/* S/PDIF out: 0=consumer, 1=professional */
+#define RME9652_EMP		   (1<<10)	/*  Emphasis 0=None, 1=ON */
+#define RME9652_Dolby		   (1<<11)	/*  Non-audio bit 1=set, 0=unset */
+#define RME9652_opt_out	           (1<<12)	/* Use 1st optical OUT as SPDIF: 1=yes,0=no */
+#define RME9652_wsel		   (1<<13)	/* use Wordclock as sync (overwrites master) */
+#define RME9652_inp_0		   (1<<14)	/* SPDIF-IN: 00=optical (ADAT1),     */
+#define RME9652_inp_1		   (1<<15)	/* 01=koaxial (Cinch), 10=Internal CDROM */
+#define RME9652_SyncPref_ADAT2	   (1<<16)
+#define RME9652_SyncPref_ADAT3	   (1<<17)
+#define RME9652_SPDIF_RESET        (1<<18)      /* Rev 1.5+: h/w S/PDIF receiver */
+#define RME9652_SPDIF_SELECT       (1<<19)
+#define RME9652_SPDIF_CLOCK        (1<<20)
+#define RME9652_SPDIF_WRITE        (1<<21)
+#define RME9652_ADAT1_INTERNAL     (1<<22)      /* Rev 1.5+: if set, internal CD connector carries ADAT */
+
+/* buffersize = 512Bytes * 2^n, where n is made from Bit2 ... Bit0 */
+
+#define RME9652_latency            0x0e
+#define rme9652_encode_latency(x)  (((x)&0x7)<<1)
+#define rme9652_decode_latency(x)  (((x)>>1)&0x7)
+#define rme9652_running_double_speed(s) ((s)->control_register & RME9652_DS)
+#define RME9652_inp                (RME9652_inp_0|RME9652_inp_1)
+#define rme9652_encode_spdif_in(x) (((x)&0x3)<<14)
+#define rme9652_decode_spdif_in(x) (((x)>>14)&0x3)
+
+#define RME9652_SyncPref_Mask      (RME9652_SyncPref_ADAT2|RME9652_SyncPref_ADAT3)
+#define RME9652_SyncPref_ADAT1	   0
+#define RME9652_SyncPref_SPDIF	   (RME9652_SyncPref_ADAT2|RME9652_SyncPref_ADAT3)
+
+/* the size of a substream (1 mono data stream) */
+
+#define RME9652_CHANNEL_BUFFER_SAMPLES  (16*1024)
+#define RME9652_CHANNEL_BUFFER_BYTES    (4*RME9652_CHANNEL_BUFFER_SAMPLES)
+
+/* the size of the area we need to allocate for DMA transfers. the
+   size is the same regardless of the number of channels - the 
+   9636 still uses the same memory area.
+
+   Note that we allocate 1 more channel than is apparently needed
+   because the h/w seems to write 1 byte beyond the end of the last
+   page. Sigh.
+*/
+
+#define RME9652_DMA_AREA_BYTES ((RME9652_NCHANNELS+1) * RME9652_CHANNEL_BUFFER_BYTES)
+#define RME9652_DMA_AREA_KILOBYTES (RME9652_DMA_AREA_BYTES/1024)
+
+typedef struct snd_rme9652 {
+	int dev;
+
+	spinlock_t lock;
+	int irq;
+	unsigned long port;
+	struct resource *res_port;
+	unsigned long iobase;
+
+	u32 control_register;	/* cached value */
+	u32 thru_bits;		/* thru 1=on, 0=off channel 1=Bit1... channel 26= Bit26 */
+
+	u32 creg_spdif;
+	u32 creg_spdif_stream;
+
+	char *card_name;		/* hammerfall or hammerfall light names */
+
+        size_t hw_offsetmask;     	/* &-with status register to get real hw_offset */
+	size_t prev_hw_offset;		/* previous hw offset */
+	size_t max_jitter;		/* maximum jitter in frames for 
+					   hw pointer */
+	size_t period_bytes;		/* guess what this is */
+
+	unsigned char ds_channels;
+	unsigned char ss_channels;	/* different for hammerfall/hammerfall-light */
+
+	void *capture_buffer_unaligned;	/* original buffer addresses */
+	void *playback_buffer_unaligned;	/* original buffer addresses */
+	unsigned char *capture_buffer;	/* suitably aligned address */
+	unsigned char *playback_buffer;	/* suitably aligned address */
+	dma_addr_t capture_buffer_addr;
+	dma_addr_t playback_buffer_addr;
+
+	pid_t capture_pid;
+	pid_t playback_pid;
+
+	snd_pcm_substream_t *capture_substream;
+	snd_pcm_substream_t *playback_substream;
+	int running;
+
+        int passthru;                   /* non-zero if doing pass-thru */
+        int hw_rev;                     /* h/w rev * 10 (i.e. 1.5 has hw_rev = 15) */
+
+	int last_spdif_sample_rate;	/* so that we can catch externally ... */
+	int last_adat_sample_rate;	/* ... induced rate changes            */
+
+        char *channel_map;
+
+	snd_card_t *card;
+	snd_pcm_t *pcm;
+	struct pci_dev *pci;
+	snd_info_entry_t *proc_entry;
+	snd_kcontrol_t *spdif_ctl;
+
+} rme9652_t;
+
+/* These tables map the ALSA channels 1..N to the channels that we
+   need to use in order to find the relevant channel buffer. RME
+   refer to this kind of mapping as between "the ADAT channel and
+   the DMA channel." We index it using the logical audio channel,
+   and the value is the DMA channel (i.e. channel buffer number)
+   where the data for that channel can be read/written from/to.
+*/
+
+static char channel_map_9652_ss[26] = {
+	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
+	18, 19, 20, 21, 22, 23, 24, 25
+};
+
+static char channel_map_9636_ss[26] = {
+	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 
+	/* channels 16 and 17 are S/PDIF */
+	24, 25,
+	/* channels 18-25 don't exist */
+	-1, -1, -1, -1, -1, -1, -1, -1
+};
+
+static char channel_map_9652_ds[26] = {
+	/* ADAT channels are remapped */
+	1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23,
+	/* channels 12 and 13 are S/PDIF */
+	24, 25,
+	/* others don't exist */
+	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
+};
+
+static char channel_map_9636_ds[26] = {
+	/* ADAT channels are remapped */
+	1, 3, 5, 7, 9, 11, 13, 15,
+	/* channels 8 and 9 are S/PDIF */
+	24, 25
+	/* others don't exist */
+	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
+};
+
+#define RME9652_PREALLOCATE_MEMORY	/* via module snd-rme9652_mem */
+
+#ifdef RME9652_PREALLOCATE_MEMORY
+extern void *snd_rme9652_get_buffer(int card, dma_addr_t *dmaaddr);
+extern void snd_rme9652_free_buffer(int card, void *ptr);
+#endif
+
+static struct pci_device_id snd_rme9652_ids[] __devinitdata = {
+	{0x10ee, 0x3fc4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0,},	/* RME Digi9652 */
+	{0,}
+};
+
+MODULE_DEVICE_TABLE(pci, snd_rme9652_ids);
+
+static inline void rme9652_write(rme9652_t *rme9652, int reg, int val)
+{
+	writel(val, rme9652->iobase + reg);
+}
+
+static inline unsigned int rme9652_read(rme9652_t *rme9652, int reg)
+{
+	return readl(rme9652->iobase + reg);
+}
+
+static inline int snd_rme9652_use_is_exclusive(rme9652_t *rme9652)
+{
+	unsigned long flags;
+	int ret = 1;
+
+	spin_lock_irqsave(&rme9652->lock, flags);
+	if ((rme9652->playback_pid != rme9652->capture_pid) &&
+	    (rme9652->playback_pid >= 0) && (rme9652->capture_pid >= 0)) {
+		ret = 0;
+	}
+	spin_unlock_irqrestore(&rme9652->lock, flags);
+	return ret;
+}
+
+static inline int rme9652_adat_sample_rate(rme9652_t *rme9652)
+{
+	if (rme9652_running_double_speed(rme9652)) {
+		return (rme9652_read(rme9652, RME9652_status_register) &
+			RME9652_fs48) ? 96000 : 88200;
+	} else {
+		return (rme9652_read(rme9652, RME9652_status_register) &
+			RME9652_fs48) ? 48000 : 44100;
+	}
+}
+
+static inline void rme9652_compute_period_size(rme9652_t *rme9652)
+{
+	unsigned int i;
+
+	i = rme9652->control_register & RME9652_latency;
+	rme9652->period_bytes = 1 << ((rme9652_decode_latency(i) + 8));
+	rme9652->hw_offsetmask = 
+		(rme9652->period_bytes * 2 - 1) & RME9652_buf_pos;
+	rme9652->max_jitter = 16 + (rme9652->period_bytes <= 1024 ? 32 : 64);
+}
+
+static inline snd_pcm_uframes_t rme9652_hw_pointer(rme9652_t *rme9652)
+{
+	int status;
+	int offset, frag;
+	snd_pcm_uframes_t period_size = rme9652->period_bytes / 4;
+	snd_pcm_sframes_t delta;
+
+	status = rme9652_read(rme9652, RME9652_status_register);
+	offset = status & RME9652_buf_pos;
+
+	/* The hardware may give a backward movement for up to 80 frames
+           Martin Kirst <martin.kirst@freenet.de> knows the details.
+	*/
+
+	delta = rme9652->prev_hw_offset - offset;
+	delta &= 0xffff;
+	if (delta <= rme9652->max_jitter * 4)
+		offset = rme9652->prev_hw_offset;
+	else
+		rme9652->prev_hw_offset = offset;
+	offset &= rme9652->hw_offsetmask;
+	offset /= 4;
+	frag = status & RME9652_buffer_id;
+
+	if (offset < period_size) {
+		if (offset > rme9652->max_jitter) {
+			if (frag)
+				printk("Unexpected hw_pointer position (bufid == 0): status: %x offset: %d\n", status, offset);
+		} else if (!frag)
+			return 0;
+		offset -= rme9652->max_jitter;
+		if (offset < 0)
+			offset += period_size * 2;
+	} else {
+		if (offset > period_size + rme9652->max_jitter) {
+			if (!frag)
+				printk("Unexpected hw_pointer position (bufid == 1): status: %x offset: %d\n", status, offset);
+		} else if (frag)
+			return period_size;
+		offset -= rme9652->max_jitter;
+	}
+
+	return offset;
+}
+
+static inline void rme9652_reset_hw_pointer(rme9652_t *rme9652)
+{
+	int i;
+
+	/* reset the FIFO pointer to zero. We do this by writing to 8
+	   registers, each of which is a 32bit wide register, and set
+	   them all to zero. Note that s->iobase is a pointer to
+	   int32, not pointer to char.  
+	*/
+
+	for (i = 0; i < 8; i++) {
+		rme9652_write(rme9652, i * 4, 0);
+		udelay(10);
+	}
+	rme9652->prev_hw_offset = 0;
+}
+
+static inline void rme9652_start(rme9652_t *s)
+{
+	s->control_register |= (RME9652_IE | RME9652_start_bit);
+	rme9652_write(s, RME9652_control_register, s->control_register);
+}
+
+static inline void rme9652_stop(rme9652_t *s)
+{
+	s->control_register &= ~(RME9652_start_bit | RME9652_IE);
+	rme9652_write(s, RME9652_control_register, s->control_register);
+}
+
+static int rme9652_set_interrupt_interval(rme9652_t *s,
+					  unsigned int frames)
+{
+	int restart = 0;
+	int n;
+
+	spin_lock_irq(&s->lock);
+
+	if ((restart = s->running)) {
+		rme9652_stop(s);
+	}
+
+	frames >>= 7;
+	n = 0;
+	while (frames) {
+		n++;
+		frames >>= 1;
+	}
+
+	s->control_register &= ~RME9652_latency;
+	s->control_register |= rme9652_encode_latency(n);
+
+	rme9652_write(s, RME9652_control_register, s->control_register);
+
+	rme9652_compute_period_size(s);
+
+	if (restart)
+		rme9652_start(s);
+
+	spin_unlock_irq(&s->lock);
+
+	return 0;
+}
+
+static int rme9652_set_rate(rme9652_t *rme9652, int rate)
+{
+	int restart;
+	int reject_if_open = 0;
+	int xrate;
+
+	/* Changing from a "single speed" to a "double speed" rate is
+	   not allowed if any substreams are open. This is because
+	   such a change causes a shift in the location of 
+	   the DMA buffers and a reduction in the number of available
+	   buffers. 
+
+	   Note that a similar but essentially insoluble problem
+	   exists for externally-driven rate changes. All we can do
+	   is to flag rate changes in the read/write routines.
+	 */
+
+	spin_lock_irq(&rme9652->lock);
+	xrate = rme9652_adat_sample_rate(rme9652);
+
+	switch (rate) {
+	case 44100:
+		if (xrate > 48000) {
+			reject_if_open = 1;
+		}
+		rate = 0;
+		break;
+	case 48000:
+		if (xrate > 48000) {
+			reject_if_open = 1;
+		}
+		rate = RME9652_freq;
+		break;
+	case 88200:
+		if (xrate < 48000) {
+			reject_if_open = 1;
+		}
+		rate = RME9652_DS;
+		break;
+	case 96000:
+		if (xrate < 48000) {
+			reject_if_open = 1;
+		}
+		rate = RME9652_DS | RME9652_freq;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (reject_if_open &&
+	    (rme9652->capture_pid >= 0 || rme9652->playback_pid >= 0)) {
+		spin_unlock_irq(&rme9652->lock);
+		return -EBUSY;
+	}
+
+	if ((restart = rme9652->running)) {
+		rme9652_stop(rme9652);
+	}
+
+	rme9652->control_register &= ~(RME9652_freq | RME9652_DS);
+	rme9652->control_register |= rate;
+	rme9652_write(rme9652, RME9652_control_register, rme9652->control_register);
+
+	if (restart) {
+		rme9652_start(rme9652);
+	}
+
+	if (rate & RME9652_DS) {
+		if (rme9652->ss_channels == RME9652_NCHANNELS) {
+			rme9652->channel_map = channel_map_9652_ds;
+		} else {
+			rme9652->channel_map = channel_map_9636_ds;
+		}
+	} else {
+		if (rme9652->ss_channels == RME9652_NCHANNELS) {
+			rme9652->channel_map = channel_map_9652_ss;
+		} else {
+			rme9652->channel_map = channel_map_9636_ss;
+		}
+	}
+
+	spin_unlock_irq(&rme9652->lock);
+	return 0;
+}
+
+static void rme9652_set_thru(rme9652_t *rme9652, int channel, int enable)
+{
+	int i;
+
+	rme9652->passthru = 0;
+
+	if (channel < 0) {
+
+		/* set thru for all channels */
+
+		if (enable) {
+			for (i = 0; i < RME9652_NCHANNELS; i++) {
+				rme9652->thru_bits |= (1 << i);
+				rme9652_write(rme9652, RME9652_thru_base + i * 4, 1);
+			}
+		} else {
+			for (i = 0; i < RME9652_NCHANNELS; i++) {
+				rme9652->thru_bits &= ~(1 << i);
+				rme9652_write(rme9652, RME9652_thru_base + i * 4, 0);
+			}
+		}
+
+	} else {
+		int mapped_channel;
+
+		snd_assert(channel == RME9652_NCHANNELS, return);
+		mapped_channel = rme9652->channel_map[channel];
+
+		if (enable) {
+			rme9652->thru_bits |= (1 << mapped_channel);
+		} else {
+			rme9652->thru_bits &= ~(1 << mapped_channel);
+		}
+
+		rme9652_write(rme9652,
+			       RME9652_thru_base + mapped_channel * 4,
+			       enable ? 1 : 0);			       
+	}
+}
+
+static int rme9652_set_passthru(rme9652_t *rme9652, int onoff)
+{
+	if (onoff) {
+		rme9652_set_thru(rme9652, -1, 1);
+
+		/* we don't want interrupts, so do a
+		   custom version of rme9652_start().
+		*/
+
+		rme9652->control_register =
+			RME9652_inp_0 | 
+			rme9652_encode_latency(7) |
+			RME9652_start_bit;
+
+		rme9652_reset_hw_pointer(rme9652);
+
+		rme9652_write(rme9652, RME9652_control_register,
+			      rme9652->control_register);
+		rme9652->passthru = 1;
+	} else {
+		rme9652_set_thru(rme9652, -1, 0);
+		rme9652_stop(rme9652);		
+		rme9652->passthru = 0;
+	}
+
+	return 0;
+}
+
+static void rme9652_spdif_set_bit (rme9652_t *rme9652, int mask, int onoff)
+{
+	if (onoff) 
+		rme9652->control_register |= mask;
+	else 
+		rme9652->control_register &= ~mask;
+		
+	rme9652_write(rme9652, RME9652_control_register, rme9652->control_register);
+}
+
+static void rme9652_spdif_write_byte (rme9652_t *rme9652, const int val)
+{
+	long mask;
+	long i;
+
+	for (i = 0, mask = 0x80; i < 8; i++, mask >>= 1) {
+		if (val & mask)
+			rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_WRITE, 1);
+		else 
+			rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_WRITE, 0);
+
+		rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_CLOCK, 1);
+		rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_CLOCK, 0);
+	}
+}
+
+static int rme9652_spdif_read_byte (rme9652_t *rme9652)
+{
+	long mask;
+	long val;
+	long i;
+
+	val = 0;
+
+	for (i = 0, mask = 0x80;  i < 8; i++, mask >>= 1) {
+		rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_CLOCK, 1);
+		if (rme9652_read (rme9652, RME9652_status_register) & RME9652_SPDIF_READ)
+			val |= mask;
+		rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_CLOCK, 0);
+	}
+
+	return val;
+}
+
+static void rme9652_write_spdif_codec (rme9652_t *rme9652, const int address, const int data)
+{
+	rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_SELECT, 1);
+	rme9652_spdif_write_byte (rme9652, 0x20);
+	rme9652_spdif_write_byte (rme9652, address);
+	rme9652_spdif_write_byte (rme9652, data);
+	rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_SELECT, 0);
+}
+
+
+static int rme9652_spdif_read_codec (rme9652_t *rme9652, const int address)
+{
+	int ret;
+
+	rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_SELECT, 1);
+	rme9652_spdif_write_byte (rme9652, 0x20);
+	rme9652_spdif_write_byte (rme9652, address);
+	rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_SELECT, 0);
+	rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_SELECT, 1);
+
+	rme9652_spdif_write_byte (rme9652, 0x21);
+	ret = rme9652_spdif_read_byte (rme9652);
+	rme9652_spdif_set_bit (rme9652, RME9652_SPDIF_SELECT, 0);
+
+	return ret;
+}
+
+static void rme9652_initialize_spdif_receiver (rme9652_t *rme9652)
+{
+	/* XXX what unsets this ? */
+
+	rme9652->control_register |= RME9652_SPDIF_RESET;
+
+	rme9652_write_spdif_codec (rme9652, 4, 0x40);
+	rme9652_write_spdif_codec (rme9652, 17, 0x13);
+	rme9652_write_spdif_codec (rme9652, 6, 0x02);
+}
+
+static inline int rme9652_spdif_sample_rate(rme9652_t *s)
+{
+	unsigned int rate_bits;
+
+	if (rme9652_read(s, RME9652_status_register) & RME9652_ERF) {
+		return -1;	/* error condition */
+	}
+	
+	if (s->hw_rev == 15) {
+
+		int x, y, ret;
+		
+		x = rme9652_spdif_read_codec (s, 30);
+
+		if (x != 0) 
+			y = 48000 * 64 / x;
+		else
+			y = 0;
+
+		if      (y > 30400 && y < 33600)  ret = 32000; 
+		else if (y > 41900 && y < 46000)  ret = 44100;
+		else if (y > 46000 && y < 50400)  ret = 48000;
+		else if (y > 60800 && y < 67200)  ret = 64000;
+		else if (y > 83700 && y < 92000)  ret = 88200;
+		else if (y > 92000 && y < 100000) ret = 96000;
+		else                              ret = 0;
+		return ret;
+	}
+
+	rate_bits = rme9652_read(s, RME9652_status_register) & RME9652_F;
+
+	switch (rme9652_decode_spdif_rate(rate_bits)) {
+	case 0x7:
+		return 32000;
+		break;
+
+	case 0x6:
+		return 44100;
+		break;
+
+	case 0x5:
+		return 48000;
+		break;
+
+	case 0x4:
+		return 88200;
+		break;
+
+	case 0x3:
+		return 96000;
+		break;
+
+	case 0x0:
+		return 64000;
+		break;
+
+	default:
+		snd_printk("%s: unknown S/PDIF input rate (bits = 0x%x)\n",
+			   s->card_name, rate_bits);
+		return 0;
+		break;
+	}
+}
+
+/*-----------------------------------------------------------------------------
+  Control Interface
+  ----------------------------------------------------------------------------*/
+
+static u32 snd_rme9652_convert_from_aes(snd_aes_iec958_t *aes)
+{
+	u32 val = 0;
+	val |= (aes->status[0] & IEC958_AES0_PROFESSIONAL) ? RME9652_PRO : 0;
+	val |= (aes->status[0] & IEC958_AES0_NONAUDIO) ? RME9652_Dolby : 0;
+	if (val & RME9652_PRO)
+		val |= (aes->status[0] & IEC958_AES0_PRO_EMPHASIS_5015) ? RME9652_EMP : 0;
+	else
+		val |= (aes->status[0] & IEC958_AES0_CON_EMPHASIS_5015) ? RME9652_EMP : 0;
+	return val;
+}
+
+static void snd_rme9652_convert_to_aes(snd_aes_iec958_t *aes, u32 val)
+{
+	aes->status[0] = ((val & RME9652_PRO) ? IEC958_AES0_PROFESSIONAL : 0) |
+			 ((val & RME9652_Dolby) ? IEC958_AES0_NONAUDIO : 0);
+	if (val & RME9652_PRO)
+		aes->status[0] |= (val & RME9652_EMP) ? IEC958_AES0_PRO_EMPHASIS_5015 : 0;
+	else
+		aes->status[0] |= (val & RME9652_EMP) ? IEC958_AES0_CON_EMPHASIS_5015 : 0;
+}
+
+static int snd_rme9652_control_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+	uinfo->count = 1;
+	return 0;
+}
+
+static int snd_rme9652_control_spdif_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol);
+	
+	snd_rme9652_convert_to_aes(&ucontrol->value.iec958, rme9652->creg_spdif);
+	return 0;
+}
+
+static int snd_rme9652_control_spdif_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int change;
+	u32 val;
+	
+	val = snd_rme9652_convert_from_aes(&ucontrol->value.iec958);
+	spin_lock_irqsave(&rme9652->lock, flags);
+	change = val != rme9652->creg_spdif;
+	rme9652->creg_spdif = val;
+	spin_unlock_irqrestore(&rme9652->lock, flags);
+	return change;
+}
+
+static int snd_rme9652_control_spdif_stream_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+	uinfo->count = 1;
+	return 0;
+}
+
+static int snd_rme9652_control_spdif_stream_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol);
+	
+	snd_rme9652_convert_to_aes(&ucontrol->value.iec958, rme9652->creg_spdif_stream);
+	return 0;
+}
+
+static int snd_rme9652_control_spdif_stream_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int change;
+	u32 val;
+	
+	val = snd_rme9652_convert_from_aes(&ucontrol->value.iec958);
+	spin_lock_irqsave(&rme9652->lock, flags);
+	change = val != rme9652->creg_spdif_stream;
+	rme9652->creg_spdif_stream = val;
+	rme9652->control_register &= ~(RME9652_PRO | RME9652_Dolby | RME9652_EMP);
+	rme9652_write(rme9652, RME9652_control_register, rme9652->control_register |= val);
+	spin_unlock_irqrestore(&rme9652->lock, flags);
+	return change;
+}
+
+static int snd_rme9652_control_spdif_mask_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+	uinfo->count = 1;
+	return 0;
+}
+
+static int snd_rme9652_control_spdif_mask_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ucontrol->value.iec958.status[0] = kcontrol->private_value;
+	return 0;
+}
+
+#ifdef TARGET_OS2
+#define RME9652_ADAT1_IN(xname, xindex) \
+{ SNDRV_CTL_ELEM_IFACE_PCM, 0, 0, xname, xindex, \
+  0, 0, snd_rme9652_info_adat1_in, \
+  snd_rme9652_get_adat1_in, \
+  snd_rme9652_put_adat1_in,0 }
+#else
+#define RME9652_ADAT1_IN(xname, xindex) \
+{ iface: SNDRV_CTL_ELEM_IFACE_PCM, name: xname, index: xindex, \
+  info: snd_rme9652_info_adat1_in, \
+  get: snd_rme9652_get_adat1_in, \
+  put: snd_rme9652_put_adat1_in }
+#endif
+
+static unsigned int rme9652_adat1_in(rme9652_t *rme9652)
+{
+	if (rme9652->control_register & RME9652_ADAT1_INTERNAL)
+		return 1; 
+	return 0;
+}
+
+static int rme9652_set_adat1_input(rme9652_t *rme9652, int internal)
+{
+	int restart = 0;
+
+	if (internal) {
+		rme9652->control_register |= RME9652_ADAT1_INTERNAL;
+	} else {
+		rme9652->control_register &= ~RME9652_ADAT1_INTERNAL;
+	}
+
+	/* XXX do we actually need to stop the card when we do this ? */
+
+	if ((restart = rme9652->running)) {
+		rme9652_stop(rme9652);
+	}
+
+	rme9652_write(rme9652, RME9652_control_register, rme9652->control_register);
+
+	if (restart) {
+		rme9652_start(rme9652);
+	}
+
+	return 0;
+}
+
+static int snd_rme9652_info_adat1_in(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	static char *texts[2] = {"ADAT1", "Internal"};
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 2;
+	if (uinfo->value.enumerated.item > 1)
+		uinfo->value.enumerated.item = 1;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int snd_rme9652_get_adat1_in(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	
+	spin_lock_irqsave(&rme9652->lock, flags);
+	ucontrol->value.enumerated.item[0] = rme9652_adat1_in(rme9652);
+	spin_unlock_irqrestore(&rme9652->lock, flags);
+	return 0;
+}
+
+static int snd_rme9652_put_adat1_in(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int change;
+	unsigned int val;
+	
+	if (!snd_rme9652_use_is_exclusive(rme9652))
+		return -EBUSY;
+	val = ucontrol->value.enumerated.item[0] % 2;
+	spin_lock_irqsave(&rme9652->lock, flags);
+	change = val != rme9652_adat1_in(rme9652);
+	if (change)
+		rme9652_set_adat1_input(rme9652, val);
+	spin_unlock_irqrestore(&rme9652->lock, flags);
+	return change;
+}
+
+#ifdef TARGET_OS2
+#define RME9652_SPDIF_IN(xname, xindex) \
+{ SNDRV_CTL_ELEM_IFACE_PCM, 0, 0, xname, xindex, \
+  0, 0, snd_rme9652_info_spdif_in, \
+  snd_rme9652_get_spdif_in, snd_rme9652_put_spdif_in,0 }
+#else
+#define RME9652_SPDIF_IN(xname, xindex) \
+{ iface: SNDRV_CTL_ELEM_IFACE_PCM, name: xname, index: xindex, \
+  info: snd_rme9652_info_spdif_in, \
+  get: snd_rme9652_get_spdif_in, put: snd_rme9652_put_spdif_in }
+#endif
+
+static unsigned int rme9652_spdif_in(rme9652_t *rme9652)
+{
+	return rme9652_decode_spdif_in(rme9652->control_register &
+				       RME9652_inp);
+}
+
+static int rme9652_set_spdif_input(rme9652_t *rme9652, int in)
+{
+	int restart = 0;
+
+	rme9652->control_register &= ~RME9652_inp;
+	rme9652->control_register |= rme9652_encode_spdif_in(in);
+
+	if ((restart = rme9652->running)) {
+		rme9652_stop(rme9652);
+	}
+
+	rme9652_write(rme9652, RME9652_control_register, rme9652->control_register);
+
+	if (restart) {
+		rme9652_start(rme9652);
+	}
+
+	return 0;
+}
+
+static int snd_rme9652_info_spdif_in(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	static char *texts[3] = {"ADAT1", "Coaxial", "Internal"};
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 3;
+	if (uinfo->value.enumerated.item > 2)
+		uinfo->value.enumerated.item = 2;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int snd_rme9652_get_spdif_in(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	
+	spin_lock_irqsave(&rme9652->lock, flags);
+	ucontrol->value.enumerated.item[0] = rme9652_spdif_in(rme9652);
+	spin_unlock_irqrestore(&rme9652->lock, flags);
+	return 0;
+}
+
+static int snd_rme9652_put_spdif_in(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int change;
+	unsigned int val;
+	
+	if (!snd_rme9652_use_is_exclusive(rme9652))
+		return -EBUSY;
+	val = ucontrol->value.enumerated.item[0] % 3;
+	spin_lock_irqsave(&rme9652->lock, flags);
+	change = val != rme9652_spdif_in(rme9652);
+	if (change)
+		rme9652_set_spdif_input(rme9652, val);
+	spin_unlock_irqrestore(&rme9652->lock, flags);
+	return change;
+}
+
+#ifdef TARGET_OS2
+#define RME9652_SPDIF_OUT(xname, xindex) \
+{ SNDRV_CTL_ELEM_IFACE_PCM, 0, 0, xname, xindex, \
+  0, 0, snd_rme9652_info_spdif_out, \
+  snd_rme9652_get_spdif_out, snd_rme9652_put_spdif_out, 0 }
+#else
+#define RME9652_SPDIF_OUT(xname, xindex) \
+{ iface: SNDRV_CTL_ELEM_IFACE_PCM, name: xname, index: xindex, \
+  info: snd_rme9652_info_spdif_out, \
+  get: snd_rme9652_get_spdif_out, put: snd_rme9652_put_spdif_out }
+#endif
+
+static int rme9652_spdif_out(rme9652_t *rme9652)
+{
+	return (rme9652->control_register & RME9652_opt_out) ? 1 : 0;
+}
+
+static int rme9652_set_spdif_output(rme9652_t *rme9652, int out)
+{
+	int restart = 0;
+
+	if (out) {
+		rme9652->control_register |= RME9652_opt_out;
+	} else {
+		rme9652->control_register &= ~RME9652_opt_out;
+	}
+
+	if ((restart = rme9652->running)) {
+		rme9652_stop(rme9652);
+	}
+
+	rme9652_write(rme9652, RME9652_control_register, rme9652->control_register);
+
+	if (restart) {
+		rme9652_start(rme9652);
+	}
+
+	return 0;
+}
+
+static int snd_rme9652_info_spdif_out(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_rme9652_get_spdif_out(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	
+	spin_lock_irqsave(&rme9652->lock, flags);
+	ucontrol->value.integer.value[0] = rme9652_spdif_out(rme9652);
+	spin_unlock_irqrestore(&rme9652->lock, flags);
+	return 0;
+}
+
+static int snd_rme9652_put_spdif_out(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int change;
+	unsigned int val;
+	
+	if (!snd_rme9652_use_is_exclusive(rme9652))
+		return -EBUSY;
+	val = ucontrol->value.integer.value[0] & 1;
+	spin_lock_irqsave(&rme9652->lock, flags);
+	change = val != rme9652_spdif_out(rme9652);
+	rme9652_set_spdif_output(rme9652, val);
+	spin_unlock_irqrestore(&rme9652->lock, flags);
+	return change;
+}
+
+#ifdef TARGET_OS2
+#define RME9652_SYNC_MODE(xname, xindex) \
+{ SNDRV_CTL_ELEM_IFACE_PCM, 0, 0, xname, xindex, \
+  0, 0, snd_rme9652_info_sync_mode, \
+  snd_rme9652_get_sync_mode, snd_rme9652_put_sync_mode,0 }
+#else
+#define RME9652_SYNC_MODE(xname, xindex) \
+{ iface: SNDRV_CTL_ELEM_IFACE_PCM, name: xname, index: xindex, \
+  info: snd_rme9652_info_sync_mode, \
+  get: snd_rme9652_get_sync_mode, put: snd_rme9652_put_sync_mode }
+#endif
+
+static int rme9652_sync_mode(rme9652_t *rme9652)
+{
+	if (rme9652->control_register & RME9652_wsel) {
+		return 2;
+	} else if (rme9652->control_register & RME9652_Master) {
+		return 1;
+	} else {
+		return 0;
+	}
+}
+
+static int rme9652_set_sync_mode(rme9652_t *rme9652, int mode)
+{
+	int restart = 0;
+
+	switch (mode) {
+	case 0:
+		rme9652->control_register &=
+		    ~(RME9652_Master | RME9652_wsel);
+		break;
+	case 1:
+		rme9652->control_register =
+		    (rme9652->control_register & ~RME9652_wsel) | RME9652_Master;
+		break;
+	case 2:
+		rme9652->control_register |=
+		    (RME9652_Master | RME9652_wsel);
+		break;
+	}
+
+	if ((restart = rme9652->running)) {
+		rme9652_stop(rme9652);
+	}
+
+	rme9652_write(rme9652, RME9652_control_register, rme9652->control_register);
+
+	if (restart) {
+		rme9652_start(rme9652);
+	}
+
+	return 0;
+}
+
+static int snd_rme9652_info_sync_mode(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	static char *texts[3] = {"AutoSync", "Master", "Word Clock"};
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 3;
+	if (uinfo->value.enumerated.item > 2)
+		uinfo->value.enumerated.item = 2;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int snd_rme9652_get_sync_mode(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	
+	spin_lock_irqsave(&rme9652->lock, flags);
+	ucontrol->value.enumerated.item[0] = rme9652_sync_mode(rme9652);
+	spin_unlock_irqrestore(&rme9652->lock, flags);
+	return 0;
+}
+
+static int snd_rme9652_put_sync_mode(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int change;
+	unsigned int val;
+	
+	val = ucontrol->value.enumerated.item[0] % 3;
+	spin_lock_irqsave(&rme9652->lock, flags);
+	change = val != rme9652_sync_mode(rme9652);
+	rme9652_set_sync_mode(rme9652, val);
+	spin_unlock_irqrestore(&rme9652->lock, flags);
+	return change;
+}
+
+#ifdef TARGET_OS2
+#define RME9652_SYNC_PREF(xname, xindex) \
+{ SNDRV_CTL_ELEM_IFACE_PCM, 0, 0, xname, xindex, \
+  0, 0, snd_rme9652_info_sync_pref, \
+  snd_rme9652_get_sync_pref, snd_rme9652_put_sync_pref, 0 }
+#else
+#define RME9652_SYNC_PREF(xname, xindex) \
+{ iface: SNDRV_CTL_ELEM_IFACE_PCM, name: xname, index: xindex, \
+  info: snd_rme9652_info_sync_pref, \
+  get: snd_rme9652_get_sync_pref, put: snd_rme9652_put_sync_pref }
+#endif
+
+static int rme9652_sync_pref(rme9652_t *rme9652)
+{
+	switch (rme9652->control_register & RME9652_SyncPref_Mask) {
+	case RME9652_SyncPref_ADAT1:
+		return RME9652_SYNC_FROM_ADAT1;
+	case RME9652_SyncPref_ADAT2:
+		return RME9652_SYNC_FROM_ADAT2;
+	case RME9652_SyncPref_ADAT3:
+		return RME9652_SYNC_FROM_ADAT3;
+	case RME9652_SyncPref_SPDIF:
+		return RME9652_SYNC_FROM_SPDIF;
+	}
+	/* Not reachable */
+	return 0;
+}
+
+static int rme9652_set_sync_pref(rme9652_t *rme9652, int pref)
+{
+	int restart;
+
+	rme9652->control_register &= ~RME9652_SyncPref_Mask;
+	switch (pref) {
+	case RME9652_SYNC_FROM_ADAT1:
+		rme9652->control_register |= RME9652_SyncPref_ADAT1;
+		break;
+	case RME9652_SYNC_FROM_ADAT2:
+		rme9652->control_register |= RME9652_SyncPref_ADAT2;
+		break;
+	case RME9652_SYNC_FROM_ADAT3:
+		rme9652->control_register |= RME9652_SyncPref_ADAT3;
+		break;
+	case RME9652_SYNC_FROM_SPDIF:
+		rme9652->control_register |= RME9652_SyncPref_SPDIF;
+		break;
+	}
+
+	if ((restart = rme9652->running)) {
+		rme9652_stop(rme9652);
+	}
+
+	rme9652_write(rme9652, RME9652_control_register, rme9652->control_register);
+
+	if (restart) {
+		rme9652_start(rme9652);
+	}
+
+	return 0;
+}
+
+static int snd_rme9652_info_sync_pref(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	static char *texts[4] = {"IEC958 In", "ADAT1 In", "ADAT2 In", "ADAT3 In"};
+	rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol);
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = rme9652->ss_channels == RME9652_NCHANNELS ? 4 : 3;
+	if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+		uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int snd_rme9652_get_sync_pref(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	
+	spin_lock_irqsave(&rme9652->lock, flags);
+	ucontrol->value.enumerated.item[0] = rme9652_sync_pref(rme9652);
+	spin_unlock_irqrestore(&rme9652->lock, flags);
+	return 0;
+}
+
+static int snd_rme9652_put_sync_pref(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int change, max;
+	unsigned int val;
+	
+	if (!snd_rme9652_use_is_exclusive(rme9652))
+		return -EBUSY;
+	max = rme9652->ss_channels == RME9652_NCHANNELS ? 4 : 3;
+	val = ucontrol->value.enumerated.item[0] % max;
+	spin_lock_irqsave(&rme9652->lock, flags);
+	change = val != rme9652_sync_pref(rme9652);
+	rme9652_set_sync_pref(rme9652, val);
+	spin_unlock_irqrestore(&rme9652->lock, flags);
+	return change;
+}
+
+static int snd_rme9652_info_thru(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol);
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = rme9652->ss_channels;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+	return 0;
+}
+
+static int snd_rme9652_get_thru(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol);
+	unsigned int k;
+	u32 thru_bits = rme9652->thru_bits;
+
+	for (k = 0; k < rme9652->ss_channels; ++k) {
+		ucontrol->value.integer.value[k] = !!(thru_bits & (1 << k));
+	}
+	return 0;
+}
+
+static int snd_rme9652_put_thru(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int change;
+	unsigned int chn;
+	u32 thru_bits = 0;
+
+	if (!snd_rme9652_use_is_exclusive(rme9652))
+		return -EBUSY;
+
+	for (chn = 0; chn < rme9652->ss_channels; ++chn) {
+		if (ucontrol->value.integer.value[chn])
+			thru_bits |= 1 << chn;
+	}
+	
+	spin_lock_irqsave(&rme9652->lock, flags);
+	change = thru_bits ^ rme9652->thru_bits;
+	if (change) {
+		for (chn = 0; chn < rme9652->ss_channels; ++chn) {
+			if (!(change & (1 << chn)))
+				continue;
+			rme9652_set_thru(rme9652,chn,thru_bits&(1<<chn));
+		}
+	}
+	spin_unlock_irqrestore(&rme9652->lock, flags);
+	return !!change;
+}
+
+#ifdef TARGET_OS2
+#define RME9652_PASSTHRU(xname, xindex) \
+{ SNDRV_CTL_ELEM_IFACE_PCM, 0, 0, xname, xindex, \
+  0, 0, snd_rme9652_info_passthru, \
+  snd_rme9652_put_passthru, \
+  snd_rme9652_get_passthru,0 }
+#else
+#define RME9652_PASSTHRU(xname, xindex) \
+{ iface: SNDRV_CTL_ELEM_IFACE_PCM, name: xname, index: xindex, \
+  info: snd_rme9652_info_passthru, \
+  put: snd_rme9652_put_passthru, \
+  get: snd_rme9652_get_passthru }
+#endif
+
+static int snd_rme9652_info_passthru(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_rme9652_get_passthru(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+
+	spin_lock_irqsave(&rme9652->lock, flags);
+	ucontrol->value.integer.value[0] = rme9652->passthru;
+	spin_unlock_irqrestore(&rme9652->lock, flags);
+	return 0;
+}
+
+static int snd_rme9652_put_passthru(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int change;
+	unsigned int val;
+	int err = 0;
+
+	if (!snd_rme9652_use_is_exclusive(rme9652))
+		return -EBUSY;
+
+	val = ucontrol->value.integer.value[0] & 1;
+	spin_lock_irqsave(&rme9652->lock, flags);
+	change = (ucontrol->value.integer.value[0] != rme9652->passthru);
+	if (change)
+		err = rme9652_set_passthru(rme9652, val);
+	spin_unlock_irqrestore(&rme9652->lock, flags);
+	return err ? err : change;
+}
+
+/* Read-only switches */
+
+#ifdef TARGET_OS2
+#define RME9652_SPDIF_RATE(xname, xindex) \
+{ SNDRV_CTL_ELEM_IFACE_PCM, 0,0, xname, xindex, \
+  SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+  0, snd_rme9652_info_spdif_rate, \
+  snd_rme9652_get_spdif_rate, 0,0 }
+#else
+#define RME9652_SPDIF_RATE(xname, xindex) \
+{ iface: SNDRV_CTL_ELEM_IFACE_PCM, name: xname, index: xindex, \
+  access: SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+  info: snd_rme9652_info_spdif_rate, \
+  get: snd_rme9652_get_spdif_rate }
+#endif
+
+static int snd_rme9652_info_spdif_rate(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 96000;
+	return 0;
+}
+
+static int snd_rme9652_get_spdif_rate(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	
+	spin_lock_irqsave(&rme9652->lock, flags);
+	ucontrol->value.integer.value[0] = rme9652_spdif_sample_rate(rme9652);
+	spin_unlock_irqrestore(&rme9652->lock, flags);
+	return 0;
+}
+
+#ifdef TARGET_OS2
+#define RME9652_ADAT_SYNC(xname, xindex, xidx) \
+{ SNDRV_CTL_ELEM_IFACE_PCM, 0, 0, xname, xindex, \
+  SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+  0, snd_rme9652_info_adat_sync, \
+  snd_rme9652_get_adat_sync, 0, xidx }
+#else
+#define RME9652_ADAT_SYNC(xname, xindex, xidx) \
+{ iface: SNDRV_CTL_ELEM_IFACE_PCM, name: xname, index: xindex, \
+  access: SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+  info: snd_rme9652_info_adat_sync, \
+  get: snd_rme9652_get_adat_sync, private_value: xidx }
+#endif
+
+static int snd_rme9652_info_adat_sync(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	static char *texts[4] = {"No Lock", "Lock", "No Lock Sync", "Lock Sync"};
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 4;
+	if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+		uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int snd_rme9652_get_adat_sync(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol);
+	unsigned int mask1, mask2, val;
+	
+	switch (kcontrol->private_value) {
+	case 0: mask1 = RME9652_lock_0; mask2 = RME9652_sync_0; break;	
+	case 1: mask1 = RME9652_lock_1; mask2 = RME9652_sync_1; break;	
+	case 2: mask1 = RME9652_lock_2; mask2 = RME9652_sync_2; break;	
+	default: return -EINVAL;
+	}
+	val = rme9652_read(rme9652, RME9652_status_register);
+	ucontrol->value.enumerated.item[0] = (val & mask1) ? 1 : 0;
+	ucontrol->value.enumerated.item[0] |= (val & mask2) ? 2 : 0;
+	return 0;
+}
+
+#ifdef TARGET_OS2
+#define RME9652_TC_VALID(xname, xindex) \
+{ SNDRV_CTL_ELEM_IFACE_PCM, 0,0, xname, xindex, \
+  SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+  0, snd_rme9652_info_tc_valid, \
+  snd_rme9652_get_tc_valid,0,0 }
+#else
+#define RME9652_TC_VALID(xname, xindex) \
+{ iface: SNDRV_CTL_ELEM_IFACE_PCM, name: xname, index: xindex, \
+  access: SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+  info: snd_rme9652_info_tc_valid, \
+  get: snd_rme9652_get_tc_valid }
+#endif
+
+static int snd_rme9652_info_tc_valid(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_rme9652_get_tc_valid(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol);
+	
+	ucontrol->value.integer.value[0] = 
+		(rme9652_read(rme9652, RME9652_status_register) & RME9652_tc_valid) ? 1 : 0;
+	return 0;
+}
+
+#if ALSA_HAS_STANDARD_WAY_OF_RETURNING_TIMECODE
+
+/* FIXME: this routine needs a port to the new control API --jk */
+
+static int snd_rme9652_get_tc_value(void *private_data,
+				    snd_kswitch_t *kswitch,
+				    snd_switch_t *uswitch)
+{
+	rme9652_t *s = (rme9652_t *) private_data;
+	u32 value;
+	int i;
+
+	uswitch->type = SNDRV_SW_TYPE_DWORD;
+
+	if ((rme9652_read(s, RME9652_status_register) &
+	     RME9652_tc_valid) == 0) {
+		uswitch->value.data32[0] = 0;
+		return 0;
+	}
+
+	/* timecode request */
+
+	rme9652_write(s, RME9652_time_code, 0);
+
+	/* XXX bug alert: loop-based timing !!!! */
+
+	for (i = 0; i < 50; i++) {
+		if (!(rme9652_read(s, i * 4) & RME9652_tc_busy))
+			break;
+	}
+
+	if (!(rme9652_read(s, i * 4) & RME9652_tc_busy)) {
+		return -EIO;
+	}
+
+	value = 0;
+
+	for (i = 0; i < 32; i++) {
+		value >>= 1;
+
+		if (rme9652_read(s, i * 4) & RME9652_tc_out)
+			value |= 0x80000000;
+	}
+
+	if (value > 2 * 60 * 48000) {
+		value -= 2 * 60 * 48000;
+	} else {
+		value = 0;
+	}
+
+	uswitch->value.data32[0] = value;
+
+	return 0;
+}
+
+#endif				/* ALSA_HAS_STANDARD_WAY_OF_RETURNING_TIMECODE */
+
+#define RME9652_CONTROLS (sizeof(snd_rme9652_controls)/sizeof(snd_kcontrol_new_t))
+
+static snd_kcontrol_new_t snd_rme9652_controls[] = {
+#ifdef TARGET_OS2
+{
+	SNDRV_CTL_ELEM_IFACE_PCM,0,0,
+	SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),0,0,0,
+	snd_rme9652_control_spdif_info,
+	snd_rme9652_control_spdif_get,
+	snd_rme9652_control_spdif_put,0
+},
+{
+	SNDRV_CTL_ELEM_IFACE_PCM,0, 0, 
+	SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM),0, 
+	SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,0,
+	snd_rme9652_control_spdif_stream_info,
+	snd_rme9652_control_spdif_stream_get,
+	snd_rme9652_control_spdif_stream_put,0
+},
+{
+	SNDRV_CTL_ELEM_IFACE_MIXER,0,0,
+	SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK),0,
+	SNDRV_CTL_ELEM_ACCESS_READ,0,
+	snd_rme9652_control_spdif_mask_info,
+	snd_rme9652_control_spdif_mask_get,0,
+	IEC958_AES0_NONAUDIO |
+	IEC958_AES0_PROFESSIONAL |
+	IEC958_AES0_CON_EMPHASIS	                                                                                      
+},
+{
+	SNDRV_CTL_ELEM_IFACE_MIXER,0,0,
+	SNDRV_CTL_NAME_IEC958("",PLAYBACK,PRO_MASK),0,
+	SNDRV_CTL_ELEM_ACCESS_READ,0,
+	snd_rme9652_control_spdif_mask_info,
+	snd_rme9652_control_spdif_mask_get,0,
+	IEC958_AES0_NONAUDIO |
+	IEC958_AES0_PROFESSIONAL |
+	IEC958_AES0_PRO_EMPHASIS
+},
+#else
+{
+	iface:		SNDRV_CTL_ELEM_IFACE_PCM,
+	name:		SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
+	info:		snd_rme9652_control_spdif_info,
+	get:		snd_rme9652_control_spdif_get,
+	put:		snd_rme9652_control_spdif_put,
+},
+{
+	access:		SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+	iface:		SNDRV_CTL_ELEM_IFACE_PCM,
+	name:		SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM),
+	info:		snd_rme9652_control_spdif_stream_info,
+	get:		snd_rme9652_control_spdif_stream_get,
+	put:		snd_rme9652_control_spdif_stream_put,
+},
+{
+	access:		SNDRV_CTL_ELEM_ACCESS_READ,
+	iface:		SNDRV_CTL_ELEM_IFACE_MIXER,
+	name:		SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK),
+	info:		snd_rme9652_control_spdif_mask_info,
+	get:		snd_rme9652_control_spdif_mask_get,
+	private_value:	IEC958_AES0_NONAUDIO |
+			IEC958_AES0_PROFESSIONAL |
+			IEC958_AES0_CON_EMPHASIS,	                                                                                      
+},
+{
+	access:		SNDRV_CTL_ELEM_ACCESS_READ,
+	iface:		SNDRV_CTL_ELEM_IFACE_MIXER,
+	name:		SNDRV_CTL_NAME_IEC958("",PLAYBACK,PRO_MASK),
+	info:		snd_rme9652_control_spdif_mask_info,
+	get:		snd_rme9652_control_spdif_mask_get,
+	private_value:	IEC958_AES0_NONAUDIO |
+			IEC958_AES0_PROFESSIONAL |
+			IEC958_AES0_PRO_EMPHASIS,
+},
+#endif
+RME9652_SPDIF_IN("IEC958 Input Connector", 0),
+RME9652_SPDIF_OUT("IEC958 Output also on ADAT1", 0),
+RME9652_SYNC_MODE("Sync Mode", 0),
+RME9652_SYNC_PREF("Preferred Sync Source", 0),
+{
+#ifdef TARGET_OS2
+	SNDRV_CTL_ELEM_IFACE_PCM,0, 0,
+	"Channels Thru",
+	0,0,0,
+	snd_rme9652_info_thru,
+	snd_rme9652_get_thru,
+	snd_rme9652_put_thru,0
+#else
+	iface: SNDRV_CTL_ELEM_IFACE_PCM,
+	name: "Channels Thru",
+	index: 0,
+	info: snd_rme9652_info_thru,
+	get: snd_rme9652_get_thru,
+	put: snd_rme9652_put_thru,
+#endif
+},
+RME9652_SPDIF_RATE("IEC958 Sample Rate", 0),
+RME9652_ADAT_SYNC("ADAT1 Sync Check", 0, 0),
+RME9652_ADAT_SYNC("ADAT2 Sync Check", 0, 1),
+RME9652_ADAT_SYNC("ADAT3 Sync Check", 0, 2),
+RME9652_TC_VALID("Timecode Valid", 0),
+RME9652_PASSTHRU("Passthru", 0)
+};
+
+static snd_kcontrol_new_t snd_rme9652_adat3_check =
+RME9652_ADAT_SYNC("ADAT3 Sync Check", 0, 2);
+
+static snd_kcontrol_new_t snd_rme9652_adat1_input =
+RME9652_ADAT1_IN("ADAT1 Input Source", 0);
+
+int snd_rme9652_create_controls(snd_card_t *card, rme9652_t *rme9652)
+{
+	int idx, err;
+	snd_kcontrol_t *kctl;
+
+	for (idx = 0; idx < RME9652_CONTROLS; idx++) {
+		if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_rme9652_controls[idx], rme9652))) < 0)
+			return err;
+		if (idx == 1)	/* IEC958 (S/PDIF) Stream */
+			rme9652->spdif_ctl = kctl;
+	}
+
+	if (rme9652->ss_channels == RME9652_NCHANNELS)
+		if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_rme9652_adat3_check, rme9652))) < 0)
+			return err;
+
+	if (rme9652->hw_rev >= 15)
+		if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_rme9652_adat1_input, rme9652))) < 0)
+			return err;
+
+	return 0;
+}
+
+/*------------------------------------------------------------
+   /proc interface 
+ ------------------------------------------------------------*/
+
+static void
+snd_rme9652_proc_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer)
+{
+	rme9652_t *rme9652 = (rme9652_t *) entry->private_data;
+	u32 thru_bits = rme9652->thru_bits;
+	int show_auto_sync_source = 0;
+	int i;
+	unsigned int status;
+	int x;
+
+	status = rme9652_read(rme9652, RME9652_status_register);
+
+	snd_iprintf(buffer, "%s (Card #%d)\n", rme9652->card_name, rme9652->card->number + 1);
+	snd_iprintf(buffer, "Buffers: capture %p playback %p\n",
+		    rme9652->capture_buffer, rme9652->playback_buffer);
+	snd_iprintf(buffer, "IRQ: %d Registers bus: 0x%lx VM: 0x%lx\n",
+		    rme9652->irq, rme9652->port, rme9652->iobase);
+
+	snd_iprintf(buffer, "\n");
+
+	x = 1 << (6 + rme9652_decode_latency(rme9652->control_register & 
+					     RME9652_latency));
+
+	snd_iprintf(buffer, "Latency: %d samples (2 periods of %lu bytes)\n", 
+		    x, (unsigned long) rme9652->period_bytes);
+	snd_iprintf(buffer, "Hardware pointer (frames): %ld\n",
+		    rme9652_hw_pointer(rme9652));
+	snd_iprintf(buffer, "Passthru: %s\n",
+		    rme9652->passthru ? "yes" : "no");
+
+	if ((rme9652->control_register & (RME9652_Master | RME9652_wsel)) == 0) {
+		snd_iprintf(buffer, "Clock mode: autosync\n");
+		show_auto_sync_source = 1;
+	} else if (rme9652->control_register & RME9652_wsel) {
+		if (status & RME9652_wsel_rd) {
+			snd_iprintf(buffer, "Clock mode: word clock\n");
+		} else {
+			snd_iprintf(buffer, "Clock mode: word clock (no signal)\n");
+		}
+	} else {
+		snd_iprintf(buffer, "Clock mode: master\n");
+	}
+
+	if (show_auto_sync_source) {
+		switch (rme9652->control_register & RME9652_SyncPref_Mask) {
+		case RME9652_SyncPref_ADAT1:
+			snd_iprintf(buffer, "Pref. sync source: ADAT1\n");
+			break;
+		case RME9652_SyncPref_ADAT2:
+			snd_iprintf(buffer, "Pref. sync source: ADAT2\n");
+			break;
+		case RME9652_SyncPref_ADAT3:
+			snd_iprintf(buffer, "Pref. sync source: ADAT3\n");
+			break;
+		case RME9652_SyncPref_SPDIF:
+			snd_iprintf(buffer, "Pref. sync source: IEC958\n");
+			break;
+		default:
+			snd_iprintf(buffer, "Pref. sync source: ???\n");
+		}
+	}
+
+	if (rme9652->hw_rev >= 15)
+		snd_iprintf(buffer, "\nADAT1 Input source: %s\n",
+			    (rme9652->control_register & RME9652_ADAT1_INTERNAL) ?
+			    "Internal" : "ADAT1 optical");
+
+	snd_iprintf(buffer, "\n");
+
+	switch (rme9652_decode_spdif_in(rme9652->control_register & 
+					RME9652_inp)) {
+	case RME9652_SPDIFIN_OPTICAL:
+		snd_iprintf(buffer, "IEC958 input: ADAT1\n");
+		break;
+	case RME9652_SPDIFIN_COAXIAL:
+		snd_iprintf(buffer, "IEC958 input: Coaxial\n");
+		break;
+	case RME9652_SPDIFIN_INTERN:
+		snd_iprintf(buffer, "IEC958 input: Internal\n");
+		break;
+	default:
+		snd_iprintf(buffer, "IEC958 input: ???\n");
+		break;
+	}
+
+	if (rme9652->control_register & RME9652_opt_out) {
+		snd_iprintf(buffer, "IEC958 output: Coaxial & ADAT1\n");
+	} else {
+		snd_iprintf(buffer, "IEC958 output: Coaxial only\n");
+	}
+
+	if (rme9652->control_register & RME9652_PRO) {
+		snd_iprintf(buffer, "IEC958 quality: Professional\n");
+	} else {
+		snd_iprintf(buffer, "IEC958 quality: Consumer\n");
+	}
+
+	if (rme9652->control_register & RME9652_EMP) {
+		snd_iprintf(buffer, "IEC958 emphasis: on\n");
+	} else {
+		snd_iprintf(buffer, "IEC958 emphasis: off\n");
+	}
+
+	if (rme9652->control_register & RME9652_Dolby) {
+		snd_iprintf(buffer, "IEC958 Dolby: on\n");
+	} else {
+		snd_iprintf(buffer, "IEC958 Dolby: off\n");
+	}
+
+	i = rme9652_spdif_sample_rate(rme9652);
+
+	if (i < 0) {
+		snd_iprintf(buffer,
+			    "IEC958 sample rate: error flag set\n");
+	} else if (i == 0) {
+		snd_iprintf(buffer, "IEC958 sample rate: undetermined\n");
+	} else {
+		snd_iprintf(buffer, "IEC958 sample rate: %d\n", i);
+	}
+
+	snd_iprintf(buffer, "\n");
+
+	snd_iprintf(buffer, "ADAT Sample rate: %dHz\n",
+		    rme9652_adat_sample_rate(rme9652));
+
+	/* Sync Check */
+
+	x = status & RME9652_sync_0;
+	if (status & RME9652_lock_0) {
+		snd_iprintf(buffer, "ADAT1: %s\n", x ? "Sync" : "Lock");
+	} else {
+		snd_iprintf(buffer, "ADAT1: No Lock\n");
+	}
+
+	x = status & RME9652_sync_1;
+	if (status & RME9652_lock_1) {
+		snd_iprintf(buffer, "ADAT2: %s\n", x ? "Sync" : "Lock");
+	} else {
+		snd_iprintf(buffer, "ADAT2: No Lock\n");
+	}
+
+	x = status & RME9652_sync_2;
+	if (status & RME9652_lock_2) {
+		snd_iprintf(buffer, "ADAT3: %s\n", x ? "Sync" : "Lock");
+	} else {
+		snd_iprintf(buffer, "ADAT3: No Lock\n");
+	}
+
+	snd_iprintf(buffer, "\n");
+
+	snd_iprintf(buffer, "Timecode signal: %s\n",
+		    (status & RME9652_tc_valid) ? "yes" : "no");
+
+	/* thru modes */
+
+	snd_iprintf(buffer, "Punch Status:\n\n");
+
+	for (i = 0; i < rme9652->ss_channels; i++) {
+		if (thru_bits & (1 << i)) {
+			snd_iprintf(buffer, "%2d:  on ", i + 1);
+		} else {
+			snd_iprintf(buffer, "%2d: off ", i + 1);
+		}
+
+		if (((i + 1) % 8) == 0) {
+			snd_iprintf(buffer, "\n");
+		}
+	}
+
+	snd_iprintf(buffer, "\n");
+}
+
+static void __init snd_rme9652_proc_init(rme9652_t *rme9652)
+{
+	snd_info_entry_t *entry;
+
+	if ((entry = snd_info_create_card_entry(rme9652->card, "rme9652", rme9652->card->proc_root)) !=
+	    NULL) {
+		entry->content = SNDRV_INFO_CONTENT_TEXT;
+		entry->private_data = rme9652;
+		entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
+		entry->c.text.read_size = 256;
+		entry->c.text.read = snd_rme9652_proc_read;
+		if (snd_info_register(entry) < 0) {
+			snd_info_free_entry(entry);
+			entry = NULL;
+		}
+	}
+	rme9652->proc_entry = entry;
+}
+
+static void snd_rme9652_proc_done(rme9652_t *rme9652)
+{
+	if (rme9652->proc_entry) {
+		snd_info_unregister(rme9652->proc_entry);
+		rme9652->proc_entry = NULL;
+	}
+}
+
+static void snd_rme9652_free_buffers(rme9652_t *rme9652)
+{
+	if (rme9652->capture_buffer_unaligned) {
+#ifndef RME9652_PREALLOCATE_MEMORY
+		snd_free_pci_pages(rme9652->pci,
+				   RME9652_DMA_AREA_BYTES,
+				   rme9652->capture_buffer_unaligned,
+				   rme9652->capture_buffer_addr);
+#else
+		snd_rme9652_free_buffer(rme9652->dev, rme9652->capture_buffer_unaligned);
+#endif
+	}
+
+	if (rme9652->playback_buffer_unaligned) {
+#ifndef RME9652_PREALLOCATE_MEMORY
+		snd_free_pci_pages(rme9652->pci,
+				   RME9652_DMA_AREA_BYTES,
+				   rme9652->playback_buffer_unaligned,
+				   rme9652->playback_buffer_addr);
+#else
+		snd_rme9652_free_buffer(rme9652->dev, rme9652->playback_buffer_unaligned);
+#endif
+	}
+}
+
+static int snd_rme9652_free(rme9652_t *rme9652)
+{
+	if (rme9652->irq >= 0)
+		rme9652_stop(rme9652);
+	snd_rme9652_proc_done(rme9652);
+	snd_rme9652_free_buffers(rme9652);
+
+	if (rme9652->iobase)
+		iounmap((void *) rme9652->iobase);
+	if (rme9652->res_port)
+		release_resource(rme9652->res_port);
+	if (rme9652->irq >= 0)
+		free_irq(rme9652->irq, (void *)rme9652);
+	return 0;
+}
+
+static int __init snd_rme9652_initialize_memory(rme9652_t *rme9652)
+{
+	void *pb, *cb;
+	dma_addr_t pb_addr, cb_addr;
+	unsigned long pb_bus, cb_bus;
+
+#ifndef RME9652_PREALLOCATE_MEMORY
+	cb = snd_malloc_pci_pages(rme9652->pci, RME9652_DMA_AREA_BYTES, &cb_addr);
+	pb = snd_malloc_pci_pages(rme9652->pci, RME9652_DMA_AREA_BYTES, &pb_addr);
+#else
+	cb = snd_rme9652_get_buffer(rme9652->dev, &cb_addr);
+	pb = snd_rme9652_get_buffer(rme9652->dev, &pb_addr);
+#endif
+
+	if (cb == 0 || pb == 0) {
+		if (cb) {
+#ifdef RME9652_PREALLOCATE_MEMORY
+			snd_rme9652_free_buffer(rme9652->dev, cb);
+#else
+			snd_free_pci_pages(rme9652->pci, RME9652_DMA_AREA_BYTES, cb, cb_addr);
+#endif
+		}
+		if (pb) {
+#ifdef RME9652_PREALLOCATE_MEMORY
+			snd_rme9652_free_buffer(rme9652->dev, pb);
+#else
+			snd_free_pci_pages(rme9652->pci, RME9652_DMA_AREA_BYTES, pb, pb_addr);
+#endif
+		}
+
+		snd_printk("%s: no buffers available\n",
+			   rme9652->card_name);
+		return -ENOMEM;
+	}
+
+	/* save raw addresses for use when freeing memory later */
+
+	rme9652->capture_buffer_unaligned = cb;
+	rme9652->playback_buffer_unaligned = pb;
+	rme9652->capture_buffer_addr = cb_addr;
+	rme9652->playback_buffer_addr = pb_addr;
+
+	/* Align to bus-space 64K boundary */
+
+	cb_bus = cb_addr;
+	cb_bus = (cb_bus + 0xFFFF) & ~0xFFFFl;
+
+	pb_bus = pb_addr;
+	pb_bus = (pb_bus + 0xFFFF) & ~0xFFFFl;
+
+	/* Tell the card where it is */
+
+	rme9652_write(rme9652, RME9652_rec_buffer, cb_bus);
+	rme9652_write(rme9652, RME9652_play_buffer, pb_bus);
+
+	rme9652->capture_buffer = bus_to_virt(cb_bus);
+	rme9652->playback_buffer = bus_to_virt(pb_bus);
+
+	return 0;
+}
+
+static void snd_rme9652_set_defaults(rme9652_t *rme9652)
+{
+	unsigned int k;
+
+	/* ASSUMPTION: rme9652->lock is either held, or
+	   there is no need to hold it (e.g. during module
+	   initalization).
+	 */
+
+	/* set defaults:
+
+	   SPDIF Input via Coax 
+	   autosync clock mode
+	   maximum latency (7 = 8192 samples, 64Kbyte buffer,
+	   which implies 2 4096 sample, 32Kbyte periods).
+	   
+	   if rev 1.5, initialize the S/PDIF receiver.
+
+	 */
+
+	rme9652->control_register =
+	    RME9652_inp_0 | rme9652_encode_latency(7);
+
+	rme9652_write(rme9652, RME9652_control_register, rme9652->control_register);
+
+	rme9652_reset_hw_pointer(rme9652);
+	rme9652_compute_period_size(rme9652);
+
+	/* default: thru off for all channels */
+
+	for (k = 0; k < RME9652_NCHANNELS; ++k)
+		rme9652_write(rme9652, RME9652_thru_base + k * 4, 0);
+
+	rme9652->thru_bits = 0;
+	rme9652->passthru = 0;
+
+	/* set a default rate so that the channel map is set up */
+
+	rme9652_set_rate(rme9652, 48000);
+}
+
+void snd_rme9652_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	rme9652_t *rme9652 = (rme9652_t *) dev_id;
+
+	if (!(rme9652_read(rme9652, RME9652_status_register) & RME9652_IRQ)) {
+		return;
+	}
+
+	rme9652_write(rme9652, RME9652_irq_clear, 0);
+
+	if (rme9652->capture_substream) {
+		snd_pcm_period_elapsed(rme9652->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream);
+	}
+
+	if (rme9652->playback_substream) {
+		snd_pcm_period_elapsed(rme9652->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream);
+	}
+}
+
+static snd_pcm_uframes_t snd_rme9652_hw_pointer(snd_pcm_substream_t *substream)
+{
+	rme9652_t *rme9652 = _snd_pcm_substream_chip(substream);
+	return rme9652_hw_pointer(rme9652);
+}
+
+static char *rme9652_channel_buffer_location(rme9652_t *rme9652,
+					     int stream,
+					     int channel)
+
+{
+	int mapped_channel;
+
+        snd_assert(channel >= 0 || channel < RME9652_NCHANNELS, return NULL);
+
+	if ((mapped_channel = rme9652->channel_map[channel]) < 0) {
+		return NULL;
+	}
+	
+	if (stream == SNDRV_PCM_STREAM_CAPTURE) {
+		return rme9652->capture_buffer +
+			(mapped_channel * RME9652_CHANNEL_BUFFER_BYTES);
+	} else {
+		return rme9652->playback_buffer +
+			(mapped_channel * RME9652_CHANNEL_BUFFER_BYTES);
+	}
+}
+
+static int snd_rme9652_playback_copy(snd_pcm_substream_t *substream, int channel,
+				     snd_pcm_uframes_t pos, void *src, snd_pcm_uframes_t count)
+{
+	rme9652_t *rme9652 = _snd_pcm_substream_chip(substream);
+	char *channel_buf;
+
+	snd_assert(pos + count <= RME9652_CHANNEL_BUFFER_BYTES / 4, return -EINVAL);
+	channel_buf = rme9652_channel_buffer_location (rme9652,
+						       substream->pstr->stream,
+						       channel);
+	snd_assert(channel_buf != NULL, return -EIO);
+	copy_from_user(channel_buf + pos * 4, src, count * 4);
+	return count;
+}
+
+static int snd_rme9652_capture_copy(snd_pcm_substream_t *substream, int channel,
+				    snd_pcm_uframes_t pos, void *dst, snd_pcm_uframes_t count)
+{
+	rme9652_t *rme9652 = _snd_pcm_substream_chip(substream);
+	char *channel_buf;
+
+	snd_assert(pos + count <= RME9652_CHANNEL_BUFFER_BYTES / 4, return -EINVAL);
+	channel_buf = rme9652_channel_buffer_location (rme9652,
+						       substream->pstr->stream,
+						       channel);
+	snd_assert(channel_buf != NULL, return -EIO);
+	copy_to_user(dst, channel_buf + pos * 4, count * 4);
+	return count;
+}
+
+static int snd_rme9652_hw_silence(snd_pcm_substream_t *substream, int channel,
+				  snd_pcm_uframes_t pos, snd_pcm_uframes_t count)
+{
+	rme9652_t *rme9652 = _snd_pcm_substream_chip(substream);
+	char *channel_buf;
+
+	channel_buf = rme9652_channel_buffer_location (rme9652,
+						       substream->pstr->stream,
+						       channel);
+	snd_assert(channel_buf != NULL, return -EIO);
+	memset(channel_buf + pos * 4, 0, count * 4);
+	return count;
+}
+
+static int snd_rme9652_reset(snd_pcm_substream_t *substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	rme9652_t *rme9652 = _snd_pcm_substream_chip(substream);
+	snd_pcm_substream_t *other;
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		other = rme9652->capture_substream;
+	else
+		other = rme9652->playback_substream;
+	if (rme9652->running)
+		runtime->status->hw_ptr = rme9652_hw_pointer(rme9652);
+	else
+		runtime->status->hw_ptr = 0;
+	if (other) {
+		snd_pcm_substream_t *s = substream;
+		snd_pcm_runtime_t *oruntime = other->runtime;
+		do {
+			s = s->link_next;
+			if (s == other) {
+				oruntime->status->hw_ptr = runtime->status->hw_ptr;
+				break;
+			}
+		} while (s != substream);
+	}
+	return 0;
+}
+
+static int snd_rme9652_hw_params(snd_pcm_substream_t *substream,
+				 snd_pcm_hw_params_t *params)
+{
+	rme9652_t *rme9652 = _snd_pcm_substream_chip(substream);
+	int err;
+	pid_t this_pid;
+	pid_t other_pid;
+
+	spin_lock_irq(&rme9652->lock);
+
+	if (substream->pstr->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		rme9652->control_register &= ~(RME9652_PRO | RME9652_Dolby | RME9652_EMP);
+		rme9652_write(rme9652, RME9652_control_register, rme9652->control_register |= rme9652->creg_spdif_stream);
+		this_pid = rme9652->playback_pid;
+		other_pid = rme9652->capture_pid;
+	} else {
+		this_pid = rme9652->capture_pid;
+		other_pid = rme9652->playback_pid;
+	}
+
+	if ((other_pid > 0) && (this_pid != other_pid)) {
+
+		/* The other stream is open, and not by the same
+		   task as this one. Make sure that the parameters
+		   that matter are the same.
+		 */
+
+		if (params_rate(params) !=
+		    rme9652_adat_sample_rate(rme9652)) {
+			spin_unlock_irq(&rme9652->lock);
+			_snd_pcm_hw_param_setempty(params, SNDRV_PCM_HW_PARAM_RATE);
+			return -EBUSY;
+		}
+
+		if (params_period_size(params) != rme9652->period_bytes / 4) {
+			spin_unlock_irq(&rme9652->lock);
+			_snd_pcm_hw_param_setempty(params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
+			return -EBUSY;
+		}
+
+		/* We're fine. */
+
+		spin_unlock_irq(&rme9652->lock);
+ 		return 0;
+
+	} else {
+		spin_unlock_irq(&rme9652->lock);
+	}
+
+	/* how to make sure that the rate matches an externally-set one ?
+	 */
+
+	if ((err = rme9652_set_rate(rme9652, params_rate(params))) < 0) {
+		_snd_pcm_hw_param_setempty(params, SNDRV_PCM_HW_PARAM_RATE);
+		return err;
+	}
+
+	if ((err = rme9652_set_interrupt_interval(rme9652, params_period_size(params))) < 0) {
+		_snd_pcm_hw_param_setempty(params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
+		return err;
+	}
+
+	return 0;
+}
+
+static int snd_rme9652_channel_info(snd_pcm_substream_t *substream,
+				    snd_pcm_channel_info_t *info)
+{
+	rme9652_t *rme9652 = _snd_pcm_substream_chip(substream);
+	int chn;
+
+	snd_assert(info->channel < RME9652_NCHANNELS, return -EINVAL);
+	if ((chn = rme9652->channel_map[info->channel]) < 0) {
+		return -EINVAL;
+	}
+
+	info->offset = chn * RME9652_CHANNEL_BUFFER_BYTES;
+	info->first = 0;
+	info->step = 32;
+	return 0;
+}
+
+static int snd_rme9652_ioctl(snd_pcm_substream_t *substream,
+			     unsigned int cmd, void *arg)
+{
+	switch (cmd) {
+	case SNDRV_PCM_IOCTL1_RESET:
+	{
+		return snd_rme9652_reset(substream);
+	}
+	case SNDRV_PCM_IOCTL1_CHANNEL_INFO:
+	{
+		snd_pcm_channel_info_t *info = arg;
+		return snd_rme9652_channel_info(substream, info);
+	}
+	default:
+		break;
+	}
+
+	return snd_pcm_lib_ioctl(substream, cmd, arg);
+}
+
+static void rme9652_silence_playback(rme9652_t *rme9652)
+{
+	memset(rme9652->playback_buffer, 0, RME9652_DMA_AREA_BYTES);
+}
+
+static int snd_rme9652_trigger(snd_pcm_substream_t *substream,
+			       int cmd)
+{
+	rme9652_t *rme9652 = _snd_pcm_substream_chip(substream);
+	snd_pcm_substream_t *other;
+	int running;
+	spin_lock(&rme9652->lock);
+	running = rme9652->running;
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		running |= 1 << substream->stream;
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		running &= ~(1 << substream->stream);
+		break;
+	default:
+		snd_BUG();
+		spin_unlock(&rme9652->lock);
+		return -EINVAL;
+	}
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		other = rme9652->capture_substream;
+	else
+		other = rme9652->playback_substream;
+
+	if (other) {
+		snd_pcm_substream_t *s = substream;
+		do {
+			s = s->link_next;
+			if (s == other) {
+				snd_pcm_trigger_done(s, substream);
+				if (cmd == SNDRV_PCM_TRIGGER_START)
+					running |= 1 << s->stream;
+				else
+					running &= ~(1 << s->stream);
+				goto _ok;
+			}
+		} while (s != substream);
+		if (cmd == SNDRV_PCM_TRIGGER_START) {
+			if (!(running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) &&
+			    substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+				rme9652_silence_playback(rme9652);
+		} else {
+			if (running &&
+			    substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+				rme9652_silence_playback(rme9652);
+		}
+	}
+ _ok:
+	snd_pcm_trigger_done(substream, substream);
+	if (!rme9652->running && running)
+		rme9652_start(rme9652);
+	else if (rme9652->running && !running)
+		rme9652_stop(rme9652);
+	rme9652->running = running;
+	spin_unlock(&rme9652->lock);
+
+	return 0;
+}
+
+static int snd_rme9652_prepare(snd_pcm_substream_t *substream)
+{
+	rme9652_t *rme9652 = _snd_pcm_substream_chip(substream);
+	int result = 0;
+
+	spin_lock_irq(&rme9652->lock);
+	if (!rme9652->running)
+		rme9652_reset_hw_pointer(rme9652);
+	spin_unlock_irq(&rme9652->lock);
+	return result;
+}
+
+#ifdef TARGET_OS2
+static snd_pcm_hardware_t snd_rme9652_playback_subinfo =
+{
+/*	info:		  */	(SNDRV_PCM_INFO_MMAP |
+				 SNDRV_PCM_INFO_MMAP_VALID |
+				 SNDRV_PCM_INFO_NONINTERLEAVED |
+				 SNDRV_PCM_INFO_SYNC_START |
+				 SNDRV_PCM_INFO_DOUBLE),
+/*	formats:	  */	SNDRV_PCM_FMTBIT_S32_LE,
+/*	rates:		  */	(SNDRV_PCM_RATE_44100 | 
+				 SNDRV_PCM_RATE_48000 | 
+				 SNDRV_PCM_RATE_88200 | 
+				 SNDRV_PCM_RATE_96000),
+/*	rate_min:	  */	44100,
+/*	rate_max:	  */	96000,
+/*	channels_min:	  */	10,
+/*	channels_max:	  */	26,
+/*	buffer_bytes_max:  */	1024*1024,
+/*	period_bytes_min:  */	1,
+/*	period_bytes_max:  */	1024*1024,
+/*	periods_min:	  */	2,
+/*	periods_max:	  */	2,
+/*	fifo_size:	  */	0,
+};
+
+static snd_pcm_hardware_t snd_rme9652_capture_subinfo =
+{
+/*	info:		  */	(SNDRV_PCM_INFO_MMAP |				 SNDRV_PCM_INFO_MMAP_VALID |
+				 SNDRV_PCM_INFO_NONINTERLEAVED |
+				 SNDRV_PCM_INFO_SYNC_START),
+/*	formats:	  */	SNDRV_PCM_FMTBIT_S32_LE,
+/*	rates:		  */	(SNDRV_PCM_RATE_44100 | 
+				 SNDRV_PCM_RATE_48000 | 
+				 SNDRV_PCM_RATE_88200 | 
+				 SNDRV_PCM_RATE_96000),
+/*	rate_min:	  */	44100,
+/*	rate_max:	  */	96000,
+/*	channels_min:	  */	10,
+/*	channels_max:	  */	26,
+/*	buffer_bytes_max:  */	1024*1024,
+/*	period_bytes_min:  */	1,
+/*	period_bytes_max:  */	1024*1024,
+/*	periods_min:	  */	2,
+/*	periods_max:	  */	2,
+/*	fifo_size:	  */	0,
+};
+#else
+static snd_pcm_hardware_t snd_rme9652_playback_subinfo =
+{
+	info:			(SNDRV_PCM_INFO_MMAP |
+				 SNDRV_PCM_INFO_MMAP_VALID |
+				 SNDRV_PCM_INFO_NONINTERLEAVED |
+				 SNDRV_PCM_INFO_SYNC_START |
+				 SNDRV_PCM_INFO_DOUBLE),
+	formats:		SNDRV_PCM_FMTBIT_S32_LE,
+	rates:			(SNDRV_PCM_RATE_44100 | 
+				 SNDRV_PCM_RATE_48000 | 
+				 SNDRV_PCM_RATE_88200 | 
+				 SNDRV_PCM_RATE_96000),
+	rate_min:		44100,
+	rate_max:		96000,
+	channels_min:		10,
+	channels_max:		26,
+	buffer_bytes_max:	1024*1024,
+	period_bytes_min:	1,
+	period_bytes_max:	1024*1024,
+	periods_min:		2,
+	periods_max:		2,
+	fifo_size:		0,
+};
+
+static snd_pcm_hardware_t snd_rme9652_capture_subinfo =
+{
+	info:			(SNDRV_PCM_INFO_MMAP |
+				 SNDRV_PCM_INFO_MMAP_VALID |
+				 SNDRV_PCM_INFO_NONINTERLEAVED |
+				 SNDRV_PCM_INFO_SYNC_START),
+	formats:		SNDRV_PCM_FMTBIT_S32_LE,
+	rates:			(SNDRV_PCM_RATE_44100 | 
+				 SNDRV_PCM_RATE_48000 | 
+				 SNDRV_PCM_RATE_88200 | 
+				 SNDRV_PCM_RATE_96000),
+	rate_min:		44100,
+	rate_max:		96000,
+	channels_min:		10,
+	channels_max:		26,
+	buffer_bytes_max:	1024*1024,
+	period_bytes_min:	1,
+	period_bytes_max:	1024*1024,
+	periods_min:		2,
+	periods_max:		2,
+	fifo_size:		0,
+};
+#endif
+
+static unsigned int period_sizes[] = { 64, 128, 256, 512, 1024, 2048, 4096, 8192 };
+
+#define PERIOD_SIZES sizeof(period_sizes) / sizeof(period_sizes[0])
+
+#ifdef TARGET_OS2
+static snd_pcm_hw_constraint_list_t hw_constraints_period_sizes = {
+	PERIOD_SIZES,
+	period_sizes,
+	0
+};
+#else
+static snd_pcm_hw_constraint_list_t hw_constraints_period_sizes = {
+	count: PERIOD_SIZES,
+	list: period_sizes,
+	mask: 0
+};
+#endif
+
+static int snd_rme9652_hw_rule_channels(snd_pcm_hw_params_t *params,
+					snd_pcm_hw_rule_t *rule)
+{
+	rme9652_t *rme9652 = rule->private;
+	snd_interval_t *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+#ifdef TARGET_OS2
+	unsigned int list[2];
+        list[0] = rme9652->ds_channels; list[1] = rme9652->ss_channels;
+#else
+	unsigned int list[2] = { rme9652->ds_channels, rme9652->ss_channels };
+#endif
+	return snd_interval_list(c, 2, list, 0);
+}
+
+static int snd_rme9652_hw_rule_channels_rate(snd_pcm_hw_params_t *params,
+					     snd_pcm_hw_rule_t *rule)
+{
+	rme9652_t *rme9652 = rule->private;
+	snd_interval_t *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+	snd_interval_t *r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+	if (r->min > 48000) {
+#ifdef TARGET_OS2
+		snd_interval_t t = {0};
+                t.min = rme9652->ds_channels;
+                t.max = rme9652->ds_channels;
+                t.integer = 1;
+#else
+		snd_interval_t t = {
+			min: rme9652->ds_channels,
+			max: rme9652->ds_channels,
+			integer: 1,
+		};
+#endif
+		return snd_interval_refine(c, &t);
+	} else if (r->max < 88200) {
+#ifdef TARGET_OS2
+		snd_interval_t t = {0};
+                t.min = rme9652->ss_channels;
+                t.max = rme9652->ss_channels;
+                t.integer = 1;
+#else
+		snd_interval_t t = {
+			min: rme9652->ss_channels,
+			max: rme9652->ss_channels,
+			integer: 1,
+		};
+#endif
+		return snd_interval_refine(c, &t);
+	}
+	return 0;
+}
+
+static int snd_rme9652_hw_rule_rate_channels(snd_pcm_hw_params_t *params,
+					     snd_pcm_hw_rule_t *rule)
+{
+	rme9652_t *rme9652 = rule->private;
+	snd_interval_t *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+	snd_interval_t *r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+	if (c->min >= rme9652->ss_channels) {
+		snd_interval_t t = {
+#ifdef TARGET_OS2
+			44100,
+			48000,
+			0, 0, 1, 0,
+#else
+			min: 44100,
+			max: 48000,
+			integer: 1,
+#endif
+		};
+		return snd_interval_refine(r, &t);
+	} else if (c->max <= rme9652->ds_channels) {
+		snd_interval_t t = {
+#ifdef TARGET_OS2
+			88200,
+			96000,
+			0, 0, 1, 0,
+#else
+			min: 88200,
+			max: 96000,
+			integer: 1,
+#endif
+		};
+		return snd_interval_refine(r, &t);
+	}
+	return 0;
+}
+
+static int snd_rme9652_playback_open(snd_pcm_substream_t *substream)
+{
+	rme9652_t *rme9652 = _snd_pcm_substream_chip(substream);
+	unsigned long flags;
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	spin_lock_irqsave(&rme9652->lock, flags);
+
+	snd_pcm_set_sync(substream);
+
+        runtime->hw = snd_rme9652_playback_subinfo;
+	runtime->dma_area = rme9652->playback_buffer;
+	runtime->dma_bytes = RME9652_DMA_AREA_BYTES;
+
+	if (rme9652->capture_substream == NULL) {
+		rme9652_stop(rme9652);
+		rme9652_set_thru(rme9652, -1, 0);
+	}
+
+	rme9652->playback_pid = current->pid;
+	rme9652->playback_substream = substream;
+
+	spin_unlock_irqrestore(&rme9652->lock, flags);
+
+	snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
+	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, &hw_constraints_period_sizes);
+	snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+			     snd_rme9652_hw_rule_channels, rme9652,
+			     SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+	snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+			     snd_rme9652_hw_rule_channels_rate, rme9652,
+			     SNDRV_PCM_HW_PARAM_RATE, -1);
+	snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+			     snd_rme9652_hw_rule_rate_channels, rme9652,
+			     SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+
+	rme9652->creg_spdif_stream = rme9652->creg_spdif;
+	rme9652->spdif_ctl->access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+	snd_ctl_notify(rme9652->card, SNDRV_CTL_EVENT_MASK_VALUE |
+		       SNDRV_CTL_EVENT_MASK_INFO, &rme9652->spdif_ctl->id);
+	return 0;
+}
+
+static int snd_rme9652_playback_release(snd_pcm_substream_t *substream)
+{
+	rme9652_t *rme9652 = _snd_pcm_substream_chip(substream);
+	unsigned long flags;
+
+	spin_lock_irqsave(&rme9652->lock, flags);
+
+	rme9652->playback_pid = -1;
+	rme9652->playback_substream = NULL;
+
+	spin_unlock_irqrestore(&rme9652->lock, flags);
+
+	rme9652->spdif_ctl->access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+	snd_ctl_notify(rme9652->card, SNDRV_CTL_EVENT_MASK_VALUE |
+		       SNDRV_CTL_EVENT_MASK_INFO, &rme9652->spdif_ctl->id);
+	return 0;
+}
+
+
+static int snd_rme9652_capture_open(snd_pcm_substream_t *substream)
+{
+	rme9652_t *rme9652 = _snd_pcm_substream_chip(substream);
+	unsigned long flags;
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	spin_lock_irqsave(&rme9652->lock, flags);
+
+	snd_pcm_set_sync(substream);
+
+	runtime->hw = snd_rme9652_capture_subinfo;
+	runtime->dma_area = rme9652->capture_buffer;
+	runtime->dma_bytes = RME9652_DMA_AREA_BYTES;
+
+	if (rme9652->playback_substream == NULL) {
+		rme9652_stop(rme9652);
+		rme9652_set_thru(rme9652, -1, 0);
+	}
+
+	rme9652->capture_pid = current->pid;
+	rme9652->capture_substream = substream;
+
+	spin_unlock_irqrestore(&rme9652->lock, flags);
+
+	snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
+	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, &hw_constraints_period_sizes);
+	snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+			     snd_rme9652_hw_rule_channels, rme9652,
+			     SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+	snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+			     snd_rme9652_hw_rule_channels_rate, rme9652,
+			     SNDRV_PCM_HW_PARAM_RATE, -1);
+	snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+			     snd_rme9652_hw_rule_rate_channels, rme9652,
+			     SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+	return 0;
+}
+
+static int snd_rme9652_capture_release(snd_pcm_substream_t *substream)
+{
+	rme9652_t *rme9652 = _snd_pcm_substream_chip(substream);
+	unsigned long flags;
+
+	spin_lock_irqsave(&rme9652->lock, flags);
+
+	rme9652->capture_pid = -1;
+	rme9652->capture_substream = NULL;
+
+	spin_unlock_irqrestore(&rme9652->lock, flags);
+	return 0;
+}
+
+#ifdef TARGET_OS2
+static snd_pcm_ops_t snd_rme9652_playback_ops = {
+/*	open:	  */	snd_rme9652_playback_open,
+/*	close:	  */	snd_rme9652_playback_release,
+/*	ioctl:	  */	snd_rme9652_ioctl,
+/*	hw_params:  */	snd_rme9652_hw_params,
+        0,
+/*	prepare:  */	snd_rme9652_prepare,
+/*	trigger:  */	snd_rme9652_trigger,
+/*	pointer:  */	snd_rme9652_hw_pointer,
+/*	copy:	  */	snd_rme9652_playback_copy,
+/*	silence:  */	snd_rme9652_hw_silence,
+};
+
+static snd_pcm_ops_t snd_rme9652_capture_ops = {
+/*	open:	  */	snd_rme9652_capture_open,
+/*	close:	  */	snd_rme9652_capture_release,
+/*	ioctl:	  */	snd_rme9652_ioctl,
+/*	hw_params:  */	snd_rme9652_hw_params,
+        0,
+/*	prepare:  */	snd_rme9652_prepare,
+/*	trigger:  */	snd_rme9652_trigger,
+/*	pointer:  */	snd_rme9652_hw_pointer,
+/*	copy:	  */	snd_rme9652_capture_copy,
+        0
+};
+#else
+static snd_pcm_ops_t snd_rme9652_playback_ops = {
+	open:		snd_rme9652_playback_open,
+	close:		snd_rme9652_playback_release,
+	ioctl:		snd_rme9652_ioctl,
+	hw_params:	snd_rme9652_hw_params,
+	prepare:	snd_rme9652_prepare,
+	trigger:	snd_rme9652_trigger,
+	pointer:	snd_rme9652_hw_pointer,
+	copy:		snd_rme9652_playback_copy,
+	silence:	snd_rme9652_hw_silence,
+};
+
+static snd_pcm_ops_t snd_rme9652_capture_ops = {
+	open:		snd_rme9652_capture_open,
+	close:		snd_rme9652_capture_release,
+	ioctl:		snd_rme9652_ioctl,
+	hw_params:	snd_rme9652_hw_params,
+	prepare:	snd_rme9652_prepare,
+	trigger:	snd_rme9652_trigger,
+	pointer:	snd_rme9652_hw_pointer,
+	copy:		snd_rme9652_capture_copy,
+};
+#endif
+
+static int __init snd_rme9652_create_pcm(snd_card_t *card,
+					 rme9652_t *rme9652)
+{
+	snd_pcm_t *pcm;
+	int err;
+
+	if ((err = snd_pcm_new(card,
+			       rme9652->card_name,
+			       0, 1, 1, &pcm)) < 0) {
+		return err;
+	}
+
+	rme9652->pcm = pcm;
+	pcm->private_data = rme9652;
+	strcpy(pcm->name, rme9652->card_name);
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_rme9652_playback_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_rme9652_capture_ops);
+
+	pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX;
+
+	return 0;
+}
+
+static int __init snd_rme9652_create(snd_card_t *card,
+				     rme9652_t *rme9652)
+{
+	struct pci_dev *pci = rme9652->pci;
+	int err;
+	int status;
+	unsigned short rev;
+
+	rme9652->irq = -1;
+	rme9652->card = card;
+
+	if ((err = pci_enable_device(pci)) < 0)
+		return err;
+
+	spin_lock_init(&rme9652->lock);
+
+	rme9652->port = pci_resource_start(pci, 0);
+	if ((rme9652->res_port = request_mem_region(rme9652->port, RME9652_IO_EXTENT, "rme9652")) == NULL) {
+		snd_printk("unable to grab memory region 0x%lx-0x%lx\n", rme9652->port, rme9652->port + RME9652_IO_EXTENT - 1);
+		return -EBUSY;
+	}
+
+	if (request_irq(pci->irq, snd_rme9652_interrupt, SA_INTERRUPT|SA_SHIRQ, "rme9652", (void *)rme9652)) {
+		snd_printk("unable to grab IRQ %d\n", pci->irq);
+		return -EBUSY;
+	}
+	rme9652->irq = pci->irq;
+
+	rme9652->iobase = (unsigned long) ioremap(rme9652->port, RME9652_IO_EXTENT);
+	if (rme9652->iobase == 0) {
+		snd_printk("unable to remap region 0x%lx-0x%lx\n", rme9652->port, rme9652->port + RME9652_IO_EXTENT - 1);
+		return -EBUSY;
+	}
+
+	/* Determine the h/w rev level of the card. This seems like
+	   a particularly kludgy way to encode it, but its what RME
+	   chose to do, so we follow them ...
+	*/
+
+	status = rme9652_read(rme9652, RME9652_status_register);
+	if (rme9652_decode_spdif_rate(status&RME9652_F) == 1) {
+		rme9652->hw_rev = 15;
+	} else {
+		rme9652->hw_rev = 11;
+	}
+
+	/* Differentiate between the standard Hammerfall, and the
+	   "Light", which does not have the expansion board. This
+	   method comes from information received from Mathhias
+	   Clausen at RME. Display the EEPROM and h/w revID where
+	   relevant.  
+	*/
+
+	pci_read_config_word(rme9652->pci, PCI_CLASS_REVISION, &rev);
+	strcpy(card->driver, "RME9652");
+	switch (rev & 0xff) {
+	case 8: /* original eprom */
+		strcpy(card->driver, "RME9636");
+		if (rme9652->hw_rev == 15) {
+			rme9652->card_name = "RME Digi9636 (Rev 1.5)";
+		} else {
+			rme9652->card_name = "RME Digi9636";
+		}
+		rme9652->ss_channels = RME9636_NCHANNELS;
+		break;
+	case 9: /* W36_G EPROM */
+		strcpy(card->driver, "RME9636");
+		rme9652->card_name = "RME Digi9636 (Rev G)";
+		rme9652->ss_channels = RME9636_NCHANNELS;
+		break;
+	case 4: /* W52_G EPROM */
+		rme9652->card_name = "RME Digi9652 (Rev G)";
+		rme9652->ss_channels = RME9652_NCHANNELS;
+		break;
+	default:
+	case 3: /* original eprom */
+		if (rme9652->hw_rev == 15) {
+			rme9652->card_name = "RME Digi9652 (Rev 1.5)";
+		} else {
+			rme9652->card_name = "RME Digi9652";
+		}
+		rme9652->ss_channels = RME9652_NCHANNELS;
+		break;
+	}
+
+	rme9652->ds_channels = (rme9652->ss_channels - 2) / 2 + 2;
+
+	pci_set_master(rme9652->pci);
+
+	if ((err = snd_rme9652_initialize_memory(rme9652)) < 0) {
+		return err;
+	}
+
+	if ((err = snd_rme9652_create_pcm(card, rme9652)) < 0) {
+		return err;
+	}
+
+	if ((err = snd_rme9652_create_controls(card, rme9652)) < 0) {
+		return err;
+	}
+
+	snd_rme9652_proc_init(rme9652);
+
+	rme9652->last_spdif_sample_rate = -1;
+	rme9652->last_adat_sample_rate = -1;
+	rme9652->playback_pid = -1;
+	rme9652->capture_pid = -1;
+	rme9652->capture_substream = NULL;
+	rme9652->playback_substream = NULL;
+
+	snd_rme9652_set_defaults(rme9652);
+
+	if (rme9652->hw_rev == 15) {
+		rme9652_initialize_spdif_receiver (rme9652);
+	}
+
+	return 0;
+}
+
+static void snd_rme9652_card_free(snd_card_t *card)
+{
+	rme9652_t *rme9652 = (rme9652_t *) card->private_data;
+
+	if (rme9652)
+		snd_rme9652_free(rme9652);
+}
+
+static int snd_rme9652_probe(struct pci_dev *pci,
+			     const struct pci_device_id *id)
+{
+	static int dev = 0;
+	rme9652_t *rme9652;
+	snd_card_t *card;
+	int err;
+
+	for (; dev < SNDRV_CARDS; dev++) {
+		if (!snd_enable[dev]) {
+			dev++;
+			return -ENOENT;
+		}
+		break;
+	}
+
+	if (dev >= SNDRV_CARDS)
+		return -ENODEV;
+
+	card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE,
+			    sizeof(rme9652_t));
+
+	if (!card)
+		return -ENOMEM;
+
+	rme9652 = (rme9652_t *) card->private_data;
+	card->private_free = snd_rme9652_card_free;
+	rme9652->dev = dev;
+	rme9652->pci = pci;
+
+	if ((err = snd_rme9652_create(card, rme9652)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	strcpy(card->shortname, rme9652->card_name);
+
+	sprintf(card->longname, "%s at 0x%lx, irq %d",
+		card->shortname, rme9652->port, rme9652->irq);
+
+	
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	PCI_SET_DRIVER_DATA(pci, card);
+	dev++;
+	return 0;
+}
+
+static void __exit snd_rme9652_remove(struct pci_dev *pci)
+{
+	snd_card_free(PCI_GET_DRIVER_DATA(pci));
+	PCI_SET_DRIVER_DATA(pci, NULL);
+}
+
+#ifdef TARGET_OS2
+static struct pci_driver driver = {
+	0, 0,"RME Digi9652 (Hammerfall)",
+	snd_rme9652_ids,
+	snd_rme9652_probe,
+	snd_rme9652_remove,0,0
+};
+#else
+static struct pci_driver driver = {
+	name:"RME Digi9652 (Hammerfall)",
+	id_table:snd_rme9652_ids,
+	probe:snd_rme9652_probe,
+	remove:snd_rme9652_remove,
+};
+#endif
+
+static int __init alsa_card_hammerfall_init(void)
+{
+	if (pci_module_init(&driver) < 0) {
+#ifdef MODULE
+		snd_printk("RME Digi9652/Digi9636: no cards found\n");
+#endif
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static void __exit alsa_card_hammerfall_exit(void)
+{
+	pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_hammerfall_init)
+module_exit(alsa_card_hammerfall_exit)
+
+#ifndef MODULE
+
+/* format is: snd-card-rme9652=snd_enable,snd_index,snd_id */
+
+static int __init alsa_card_rme9652_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);
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-card-rme9652=", alsa_card_rme9652_setup);
+
+#endif /* ifndef MODULE */
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/rme9652/rme9652_mem.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/rme9652/rme9652_mem.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/rme9652/rme9652_mem.c	(revision 91)
@@ -0,0 +1,299 @@
+/* 
+    ALSA memory allocation module for the RME Digi9652
+  
+ 	Copyright(c) 1999 IEM - Winfried Ritsch
+        Copyright (C) 1999 Paul Barton-Davis 
+
+    This module is only needed if you compiled the rme9652 driver with
+    the PREALLOCATE_MEMORY option. It allocates the memory need to
+    run the board and holds it until the module is unloaded. Because
+    we need 2 contiguous 1.6MB regions for the board, it can be
+    a problem getting them once the system memory has become fairly
+    fragmented. 
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+    $Id: rme9652_mem.c,v 1.1.1.1 2003/07/02 13:56:54 eleph Exp $
+
+
+    Tue Oct 17 2000  Jaroslav Kysela <perex@suse.cz>
+    	* space is allocated only for physical devices
+        * added support for 2.4 kernels (pci_alloc_consistent)
+    
+*/
+
+#ifdef ALSA_BUILD
+#ifdef TARGET_OS2
+#include <sound/driver.h>
+#endif
+#define MODULE
+#endif
+#include <linux/config.h>
+#include <linux/version.h>
+#if defined(ALSA_BUILD) && defined(CONFIG_MODVERSIONS) && !defined(__GENKSYMS__) && !defined(__DEPEND__)
+#define MODVERSIONS
+#include <linux/modversions.h>
+#include <sound/sndversions.h>
+#endif
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <sound/initval.h>
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 3, 0)
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/malloc.h>
+#include <asm/io.h>
+#define __init
+#define __exit
+#define virt_to_page(x) (&mem_map[MAP_NR(x)])
+#define pci_for_each_dev(dev) \
+	for(dev = pci_devices; dev; dev = dev->next)
+#endif
+
+#define RME9652_CARDS			8
+#define RME9652_CHANNEL_BUFFER_SAMPLES  (16*1024)
+#define RME9652_CHANNEL_BUFFER_BYTES    (4*RME9652_CHANNEL_BUFFER_SAMPLES)
+
+/* export */
+
+static int snd_enable[8] = {1,1,1,1,1,1,1,1};
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(RME9652_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable cards to allocate buffers for.");
+
+MODULE_AUTHOR("Winfried Ritsch, Paul Barton-Davis <pbd@op.net>");
+MODULE_DESCRIPTION("Memory allocator for RME Hammerfall");
+MODULE_CLASSES("{sound}");
+
+/* Since we don't know at this point if we're allocating memory for a
+   Hammerfall or a Hammerfall/Light, assume the worst and allocate
+   space for the maximum number of channels.
+		   
+   See note in rme9652.h about why we allocate for an extra channel.  
+*/
+
+#define TOTAL_SIZE (26+1)*(RME9652_CHANNEL_BUFFER_BYTES)
+#define NBUFS   2*RME9652_CARDS
+
+#define RME9652_BUF_ALLOCATED 0x1
+#define RME9652_BUF_USED      0x2
+
+typedef struct rme9652_buf_stru rme9652_buf_t;
+
+struct rme9652_buf_stru {
+	struct pci_dev *pci;
+	void *buf;
+	dma_addr_t addr;
+	char flags;
+};
+
+static rme9652_buf_t rme9652_buffers[NBUFS];
+
+/* These are here so that we have absolutely no dependencies on any
+   other modules. Dependencies can (1) cause us to lose in the rush
+   for 2x 1.6MB chunks of contiguous memory and (2) make driver
+   debugging difficult because unloading and reloading the snd module
+   causes us to have to do the same for this one. Since on 2.2
+   kernels, and before, we can rarely if ever allocate memory after
+   starting things running, this would be bad.
+*/
+
+/* remove hack for pci_alloc_consistent to avoid dependecy on snd module */
+#ifdef HACK_PCI_ALLOC_CONSISTENT
+#undef pci_alloc_consistent
+#endif
+
+static void *rme9652_malloc_pages(struct pci_dev *pci,
+				  unsigned long size,
+				  dma_addr_t *dmaaddr)
+{
+	void *res;
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 3, 0)
+	res = (void *) pci_alloc_consistent(pci, size, dmaaddr);
+#else
+	int pg;
+	for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++);
+	res = (void *)__get_free_pages(GFP_KERNEL, pg);
+	if (res != NULL)
+		*dmaaddr = virt_to_bus(res);
+#endif
+	if (res != NULL) {
+		mem_map_t *page = virt_to_page(res);
+		mem_map_t *last_page = page + (size + PAGE_SIZE - 1) / PAGE_SIZE;
+		while (page < last_page)
+			set_bit(PG_reserved, &(page++)->flags);
+	}
+	return res;
+}
+
+static void rme9652_free_pages(struct pci_dev *pci, unsigned long size,
+			       void *ptr, dma_addr_t dmaaddr)
+{
+	mem_map_t *page, *last_page;
+
+	if (ptr == NULL)
+		return;
+	page = virt_to_page(ptr);
+	last_page = virt_to_page(ptr) + (size + PAGE_SIZE - 1) / PAGE_SIZE;
+	while (page < last_page)
+		clear_bit(PG_reserved, &(page++)->flags);
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 3, 0)
+	pci_free_consistent(pci, size, ptr, dmaaddr);
+#else
+	{
+		int pg;
+		for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++);
+		if (bus_to_virt(dmaaddr) != ptr) {
+			printk(KERN_ERR "rme9652_free_pages: dmaaddr != ptr\n");
+			return;
+		}
+		free_pages((unsigned long)ptr, pg);
+	}
+#endif
+}
+
+void *snd_rme9652_get_buffer (int card, dma_addr_t *dmaaddr)
+
+{
+	int i;
+	rme9652_buf_t *rbuf;
+
+	if (card < 0 || card >= RME9652_CARDS) {
+		printk(KERN_ERR "snd_rme9652_get_buffer: card %d is out of range", card);
+		return NULL;
+	}
+	for (i = card * 2; i < card * 2 + 2; i++) {
+		rbuf = &rme9652_buffers[i];
+		if (rbuf->flags == RME9652_BUF_ALLOCATED) {
+			rbuf->flags |= RME9652_BUF_USED;
+			MOD_INC_USE_COUNT;
+			*dmaaddr = rbuf->addr;
+			return rbuf->buf;
+		}
+	}
+
+	return NULL;
+}
+
+void snd_rme9652_free_buffer (int card, void *addr)
+
+{
+	int i;
+	rme9652_buf_t *rbuf;
+
+	if (card < 0 || card >= RME9652_CARDS) {
+		printk(KERN_ERR "snd_rme9652_get_buffer: card %d is out of range", card);
+		return;
+	}
+	for (i = card * 2; i < card * 2 + 2; i++) {
+		rbuf = &rme9652_buffers[i];
+		if (rbuf->buf == addr) {
+			MOD_DEC_USE_COUNT;
+			rbuf->flags &= ~RME9652_BUF_USED;
+			return;
+		}
+	}
+
+	printk ("RME9652 memory allocator: unknown buffer address passed to free buffer");
+}
+
+static void __exit rme9652_free_buffers (void)
+
+{
+	int i;
+	rme9652_buf_t *rbuf;
+
+	for (i = 0; i < NBUFS; i++) {
+
+		/* We rely on general module code to prevent
+		   us from being unloaded with buffers in use.
+
+		   However, not quite. Do not release memory
+		   if it is still marked as in use. This might
+		   be unnecessary.
+		*/
+
+		rbuf = &rme9652_buffers[i];
+
+		if (rbuf->flags == RME9652_BUF_ALLOCATED) {
+			rme9652_free_pages (rbuf->pci, TOTAL_SIZE, rbuf->buf, rbuf->addr);
+			rbuf->buf = NULL;
+			rbuf->flags = 0;
+		}
+	}
+}				 
+
+static int __init alsa_rme9652_mem_init(void)
+{
+	int i;
+	struct pci_dev *pci;
+	rme9652_buf_t *rbuf;
+
+	/* make sure our buffer records are clean */
+
+	for (i = 0; i < NBUFS; i++) {
+		rbuf = &rme9652_buffers[i];
+		rbuf->pci = NULL;
+		rbuf->buf = NULL;
+		rbuf->flags = 0;
+	}
+
+	/* ensure sane values for the number of buffers */
+
+	/* Remember: 2 buffers per card, one for capture, one for
+	   playback.
+	*/
+	
+	i = 0;	/* card number */
+	rbuf = rme9652_buffers;
+	pci_for_each_dev(pci) {
+		int k;
+		if (pci->vendor != 0x10ee || pci->device != 0x3fc4)
+			continue;
+
+		if (!snd_enable[i])
+			continue;
+
+		for (k = 0; k < 2; ++k) {
+			rbuf->buf = rme9652_malloc_pages(pci, TOTAL_SIZE, &rbuf->addr);
+			if (rbuf->buf == NULL) {
+				rme9652_free_buffers();
+				printk(KERN_ERR "RME9652 memory allocator: no memory available for card %d buffer %d\n", i, k + 1);
+				return -ENOMEM;
+			}
+			rbuf->flags = RME9652_BUF_ALLOCATED;
+			rbuf++;
+		}
+		i++;
+	}
+
+	if (i == 0)
+		printk(KERN_ERR "RME9652 memory allocator: no RME9652 card found...\n");
+	
+	return 0;
+}
+
+static void __exit alsa_rme9652_mem_exit(void)
+{
+	rme9652_free_buffers();
+}
+
+module_init(alsa_rme9652_mem_init)
+module_exit(alsa_rme9652_mem_exit)
+
+EXPORT_SYMBOL(snd_rme9652_get_buffer);
+EXPORT_SYMBOL(snd_rme9652_free_buffer);
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/sonicvibes.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/sonicvibes.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/sonicvibes.c	(revision 91)
@@ -0,0 +1,1682 @@
+/*
+ *  Driver for S3 SonicVibes soundcard
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *  BUGS:
+ *    It looks like 86c617 rev 3 doesn't supports DDMA buffers above 16MB?
+ *    Driver sometimes hangs... Nobody knows why at this moment...
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#define SNDRV_MAIN_OBJECT_FILE
+#include <sound/driver.h>
+#include <sound/pcm.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/mpu401.h>
+#include <sound/opl3.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+EXPORT_NO_SYMBOLS;
+MODULE_DESCRIPTION("S3 SonicVibes PCI");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{S3,SonicVibes PCI}}");
+
+#ifndef PCI_VENDOR_ID_S3
+#define PCI_VENDOR_ID_S3             0x5333
+#endif
+#ifndef PCI_DEVICE_ID_S3_SONICVIBES
+#define PCI_DEVICE_ID_S3_SONICVIBES  0xca00
+#endif
+
+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;	/* Enable this card */
+#ifdef TARGET_OS2
+static int snd_reverb[SNDRV_CARDS] = {0,0,0,0,0,0,0,0};
+static int snd_mge[SNDRV_CARDS] = {0,0,0,0,0,0,0,0};
+#else
+static int snd_reverb[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0};
+static int snd_mge[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0};
+#endif
+static unsigned int snd_dmaio = 0x7a00;	/* DDMA i/o address */
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for S3 SonicVibes soundcard.");
+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 S3 SonicVibes soundcard.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable S3 SonicVibes soundcard.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(snd_reverb, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_reverb, "Enable reverb (SRAM is present) for S3 SonicVibes soundcard.");
+MODULE_PARM_SYNTAX(snd_reverb, SNDRV_ENABLED "," SNDRV_ENABLE_DESC);
+MODULE_PARM(snd_mge, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_mge, "MIC Gain Enable for S3 SonicVibes soundcard.");
+MODULE_PARM_SYNTAX(snd_mge, SNDRV_ENABLED "," SNDRV_ENABLE_DESC);
+MODULE_PARM(snd_dmaio, "i");
+MODULE_PARM_DESC(snd_dmaio, "DDMA i/o base address for S3 SonicVibes soundcard.");
+MODULE_PARM_SYNTAX(snd_dmaio, "global," SNDRV_PORT_DESC);
+
+/*
+ * Enhanced port direct registers
+ */
+
+#define SV_REG(sonic, x) ((sonic)->enh_port + SV_REG_##x)
+
+#define SV_REG_CONTROL	0x00	/* R/W: CODEC/Mixer control register */
+#define   SV_ENHANCED	  0x01	/* audio mode select - enhanced mode */
+#define   SV_TEST	  0x02	/* test bit */
+#define   SV_REVERB	  0x04	/* reverb enable */
+#define   SV_WAVETABLE	  0x08	/* wavetable active / FM active if not set */
+#define   SV_INTA	  0x20	/* INTA driving - should be always 1 */
+#define   SV_RESET	  0x80	/* reset chip */
+#define SV_REG_IRQMASK	0x01	/* R/W: CODEC/Mixer interrupt mask register */
+#define   SV_DMAA_MASK	  0x01	/* mask DMA-A interrupt */
+#define   SV_DMAC_MASK	  0x04	/* mask DMA-C interrupt */
+#define   SV_SPEC_MASK	  0x08	/* special interrupt mask - should be always masked */
+#define   SV_UD_MASK	  0x40	/* Up/Down button interrupt mask */
+#define   SV_MIDI_MASK	  0x80	/* mask MIDI interrupt */
+#define SV_REG_STATUS	0x02	/* R/O: CODEC/Mixer status register */
+#define   SV_DMAA_IRQ	  0x01	/* DMA-A interrupt */
+#define   SV_DMAC_IRQ	  0x04	/* DMA-C interrupt */
+#define   SV_SPEC_IRQ	  0x08	/* special interrupt */
+#define   SV_UD_IRQ	  0x40	/* Up/Down interrupt */
+#define   SV_MIDI_IRQ	  0x80	/* MIDI interrupt */
+#define SV_REG_INDEX	0x04	/* R/W: CODEC/Mixer index address register */
+#define   SV_MCE          0x40	/* mode change enable */
+#define   SV_TRD	  0x80	/* DMA transfer request disabled */
+#define SV_REG_DATA	0x05	/* R/W: CODEC/Mixer index data register */
+
+/*
+ * Enhanced port indirect registers
+ */
+
+#define SV_IREG_LEFT_ADC	0x00	/* Left ADC Input Control */
+#define SV_IREG_RIGHT_ADC	0x01	/* Right ADC Input Control */
+#define SV_IREG_LEFT_AUX1	0x02	/* Left AUX1 Input Control */
+#define SV_IREG_RIGHT_AUX1	0x03	/* Right AUX1 Input Control */
+#define SV_IREG_LEFT_CD		0x04	/* Left CD Input Control */
+#define SV_IREG_RIGHT_CD	0x05	/* Right CD Input Control */
+#define SV_IREG_LEFT_LINE	0x06	/* Left Line Input Control */
+#define SV_IREG_RIGHT_LINE	0x07	/* Right Line Input Control */
+#define SV_IREG_MIC		0x08	/* MIC Input Control */
+#define SV_IREG_GAME_PORT	0x09	/* Game Port Control */
+#define SV_IREG_LEFT_SYNTH	0x0a	/* Left Synth Input Control */
+#define SV_IREG_RIGHT_SYNTH	0x0b	/* Right Synth Input Control */
+#define SV_IREG_LEFT_AUX2	0x0c	/* Left AUX2 Input Control */
+#define SV_IREG_RIGHT_AUX2	0x0d	/* Right AUX2 Input Control */
+#define SV_IREG_LEFT_ANALOG	0x0e	/* Left Analog Mixer Output Control */
+#define SV_IREG_RIGHT_ANALOG	0x0f	/* Right Analog Mixer Output Control */
+#define SV_IREG_LEFT_PCM	0x10	/* Left PCM Input Control */
+#define SV_IREG_RIGHT_PCM	0x11	/* Right PCM Input Control */
+#define SV_IREG_DMA_DATA_FMT	0x12	/* DMA Data Format */
+#define SV_IREG_PC_ENABLE	0x13	/* Playback/Capture Enable Register */
+#define SV_IREG_UD_BUTTON	0x14	/* Up/Down Button Register */
+#define SV_IREG_REVISION	0x15	/* Revision */
+#define SV_IREG_ADC_OUTPUT_CTRL	0x16	/* ADC Output Control */
+#define SV_IREG_DMA_A_UPPER	0x18	/* DMA A Upper Base Count */
+#define SV_IREG_DMA_A_LOWER	0x19	/* DMA A Lower Base Count */
+#define SV_IREG_DMA_C_UPPER	0x1c	/* DMA C Upper Base Count */
+#define SV_IREG_DMA_C_LOWER	0x1d	/* DMA C Lower Base Count */
+#define SV_IREG_PCM_RATE_LOW	0x1e	/* PCM Sampling Rate Low Byte */
+#define SV_IREG_PCM_RATE_HIGH	0x1f	/* PCM Sampling Rate High Byte */
+#define SV_IREG_SYNTH_RATE_LOW	0x20	/* Synthesizer Sampling Rate Low Byte */
+#define SV_IREG_SYNTH_RATE_HIGH 0x21	/* Synthesizer Sampling Rate High Byte */
+#define SV_IREG_ADC_CLOCK	0x22	/* ADC Clock Source Selection */
+#define SV_IREG_ADC_ALT_RATE	0x23	/* ADC Alternative Sampling Rate Selection */
+#define SV_IREG_ADC_PLL_M	0x24	/* ADC PLL M Register */
+#define SV_IREG_ADC_PLL_N	0x25	/* ADC PLL N Register */
+#define SV_IREG_SYNTH_PLL_M	0x26	/* Synthesizer PLL M Register */
+#define SV_IREG_SYNTH_PLL_N	0x27	/* Synthesizer PLL N Register */
+#define SV_IREG_MPU401		0x2a	/* MPU-401 UART Operation */
+#define SV_IREG_DRIVE_CTRL	0x2b	/* Drive Control */
+#define SV_IREG_SRS_SPACE	0x2c	/* SRS Space Control */
+#define SV_IREG_SRS_CENTER	0x2d	/* SRS Center Control */
+#define SV_IREG_WAVE_SOURCE	0x2e	/* Wavetable Sample Source Select */
+#define SV_IREG_ANALOG_POWER	0x30	/* Analog Power Down Control */
+#define SV_IREG_DIGITAL_POWER	0x31	/* Digital Power Down Control */
+
+#define SV_IREG_ADC_PLL		SV_IREG_ADC_PLL_M
+#define SV_IREG_SYNTH_PLL	SV_IREG_SYNTH_PLL_M
+
+/*
+ *  DMA registers
+ */
+
+#define SV_DMA_ADDR0		0x00
+#define SV_DMA_ADDR1		0x01
+#define SV_DMA_ADDR2		0x02
+#define SV_DMA_ADDR3		0x03
+#define SV_DMA_COUNT0		0x04
+#define SV_DMA_COUNT1		0x05
+#define SV_DMA_COUNT2		0x06
+#define SV_DMA_MODE		0x0b
+#define SV_DMA_RESET		0x0d
+#define SV_DMA_MASK		0x0f
+
+/*
+ *  Record sources
+ */
+
+#define SV_RECSRC_RESERVED	(0x00<<5)
+#define SV_RECSRC_CD		(0x01<<5)
+#define SV_RECSRC_DAC		(0x02<<5)
+#define SV_RECSRC_AUX2		(0x03<<5)
+#define SV_RECSRC_LINE		(0x04<<5)
+#define SV_RECSRC_AUX1		(0x05<<5)
+#define SV_RECSRC_MIC		(0x06<<5)
+#define SV_RECSRC_OUT		(0x07<<5)
+
+/*
+ *  constants
+ */
+
+#define SV_FULLRATE		48000
+#define SV_REFFREQUENCY		24576000
+#define SV_ADCMULT		512
+
+#define SV_MODE_PLAY		1
+#define SV_MODE_CAPTURE		2
+
+/*
+
+ */
+
+typedef struct _snd_sonicvibes sonicvibes_t;
+#define chip_t sonicvibes_t
+
+struct _snd_sonicvibes {
+	unsigned long dma1size;
+	unsigned long dma2size;
+	int irq;
+
+	unsigned long sb_port;
+	struct resource *res_sb_port;
+	unsigned long enh_port;
+	struct resource *res_enh_port;
+	unsigned long synth_port;
+	struct resource *res_synth_port;
+	unsigned long midi_port;
+	struct resource *res_midi_port;
+	unsigned long game_port;
+	unsigned int dmaa_port;
+	struct resource *res_dmaa;
+	unsigned int dmac_port;
+	struct resource *res_dmac;
+
+	unsigned char enable;
+	unsigned char irqmask;
+	unsigned char revision;
+	unsigned char format;
+	unsigned char srs_space;
+	unsigned char srs_center;
+	unsigned char mpu_switch;
+	unsigned char wave_source;
+
+	unsigned int mode;
+
+	struct pci_dev *pci;
+	snd_card_t *card;
+	snd_pcm_t *pcm;
+	snd_pcm_substream_t *playback_substream;
+	snd_pcm_substream_t *capture_substream;
+	snd_rawmidi_t *rmidi;
+	snd_hwdep_t *fmsynth;	/* S3FM */
+
+	spinlock_t reg_lock;
+	snd_info_entry_t *proc_entry;
+
+	unsigned int p_dma_size;
+	unsigned int c_dma_size;
+
+	snd_kcontrol_t *master_mute;
+	snd_kcontrol_t *master_volume;
+};
+
+static struct pci_device_id snd_sonic_ids[] __devinitdata = {
+	{ 0x5333, 0xca00, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },
+        { 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, snd_sonic_ids);
+
+#ifdef TARGET_OS2
+static ratden_t sonicvibes_adc_clock = {
+	4000 * 65536,
+	48000UL * 65536,
+	1,
+	65536,
+};
+static snd_pcm_hw_constraint_ratdens_t snd_sonicvibes_hw_constraints_adc_clock = {
+	1,
+	&sonicvibes_adc_clock,
+};
+#else
+static ratden_t sonicvibes_adc_clock = {
+	num_min: 4000 * 65536,
+	num_max: 48000UL * 65536,
+	num_step: 1,
+	den: 65536,
+};
+static snd_pcm_hw_constraint_ratdens_t snd_sonicvibes_hw_constraints_adc_clock = {
+	nrats: 1,
+	rats: &sonicvibes_adc_clock,
+};
+#endif
+
+/*
+ *  common I/O routines
+ */
+
+static inline void snd_sonicvibes_setdmaa(sonicvibes_t * sonic,
+					  unsigned int addr,
+					  unsigned int count)
+{
+	count--;
+	outl(addr, sonic->dmaa_port + SV_DMA_ADDR0);
+	outl(count, sonic->dmaa_port + SV_DMA_COUNT0);
+	outb(0x18, sonic->dmaa_port + SV_DMA_MODE);
+#if 0
+	printk("program dmaa: addr = 0x%x, paddr = 0x%x\n", addr, inl(sonic->dmaa_port + SV_DMA_ADDR0));
+#endif
+}
+
+static inline void snd_sonicvibes_setdmac(sonicvibes_t * sonic,
+					  unsigned int addr,
+					  unsigned int count)
+{
+	/* note: dmac is working in word mode!!! */
+	count >>= 1;
+	count--;
+	outl(addr, sonic->dmac_port + SV_DMA_ADDR0);
+	outl(count, sonic->dmac_port + SV_DMA_COUNT0);
+	outb(0x14, sonic->dmac_port + SV_DMA_MODE);
+#if 0
+	printk("program dmac: addr = 0x%x, paddr = 0x%x\n", addr, inl(sonic->dmac_port + SV_DMA_ADDR0));
+#endif
+}
+
+static inline unsigned int snd_sonicvibes_getdmaa(sonicvibes_t * sonic)
+{
+	return (inl(sonic->dmaa_port + SV_DMA_COUNT0) & 0xffffff) + 1;
+}
+
+static inline unsigned int snd_sonicvibes_getdmac(sonicvibes_t * sonic)
+{
+	/* note: dmac is working in word mode!!! */
+	return ((inl(sonic->dmac_port + SV_DMA_COUNT0) & 0xffffff) + 1) << 1;
+}
+
+static void snd_sonicvibes_out1(sonicvibes_t * sonic,
+				unsigned char reg,
+				unsigned char value)
+{
+	outb(reg, SV_REG(sonic, INDEX));
+	udelay(10);
+	outb(value, SV_REG(sonic, DATA));
+	udelay(10);
+}
+
+static void snd_sonicvibes_out(sonicvibes_t * sonic,
+			       unsigned char reg,
+			       unsigned char value)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&sonic->reg_lock, flags);
+	outb(reg, SV_REG(sonic, INDEX));
+	udelay(10);
+	outb(value, SV_REG(sonic, DATA));
+	udelay(10);
+	spin_unlock_irqrestore(&sonic->reg_lock, flags);
+}
+
+static unsigned char snd_sonicvibes_in1(sonicvibes_t * sonic, unsigned char reg)
+{
+	unsigned char value;
+
+	outb(reg, SV_REG(sonic, INDEX));
+	udelay(10);
+	value = inb(SV_REG(sonic, DATA));
+	udelay(10);
+	return value;
+}
+
+static unsigned char snd_sonicvibes_in(sonicvibes_t * sonic, unsigned char reg)
+{
+	unsigned long flags;
+	unsigned char value;
+
+	spin_lock_irqsave(&sonic->reg_lock, flags);
+	outb(reg, SV_REG(sonic, INDEX));
+	udelay(10);
+	value = inb(SV_REG(sonic, DATA));
+	udelay(10);
+	spin_unlock_irqrestore(&sonic->reg_lock, flags);
+	return value;
+}
+
+#ifdef CONFIG_SND_DEBUG
+void snd_sonicvibes_debug(sonicvibes_t * sonic)
+{
+	printk("SV REGS:          INDEX = 0x%02x  ", inb(SV_REG(sonic, INDEX)));
+	printk("                 STATUS = 0x%02x\n", inb(SV_REG(sonic, STATUS)));
+	printk("  0x00: left input      = 0x%02x  ", snd_sonicvibes_in(sonic, 0x00));
+	printk("  0x20: synth rate low  = 0x%02x\n", snd_sonicvibes_in(sonic, 0x20));
+	printk("  0x01: right input     = 0x%02x  ", snd_sonicvibes_in(sonic, 0x01));
+	printk("  0x21: synth rate high = 0x%02x\n", snd_sonicvibes_in(sonic, 0x21));
+	printk("  0x02: left AUX1       = 0x%02x  ", snd_sonicvibes_in(sonic, 0x02));
+	printk("  0x22: ADC clock       = 0x%02x\n", snd_sonicvibes_in(sonic, 0x22));
+	printk("  0x03: right AUX1      = 0x%02x  ", snd_sonicvibes_in(sonic, 0x03));
+	printk("  0x23: ADC alt rate    = 0x%02x\n", snd_sonicvibes_in(sonic, 0x23));
+	printk("  0x04: left CD         = 0x%02x  ", snd_sonicvibes_in(sonic, 0x04));
+	printk("  0x24: ADC pll M       = 0x%02x\n", snd_sonicvibes_in(sonic, 0x24));
+	printk("  0x05: right CD        = 0x%02x  ", snd_sonicvibes_in(sonic, 0x05));
+	printk("  0x25: ADC pll N       = 0x%02x\n", snd_sonicvibes_in(sonic, 0x25));
+	printk("  0x06: left line       = 0x%02x  ", snd_sonicvibes_in(sonic, 0x06));
+	printk("  0x26: Synth pll M     = 0x%02x\n", snd_sonicvibes_in(sonic, 0x26));
+	printk("  0x07: right line      = 0x%02x  ", snd_sonicvibes_in(sonic, 0x07));
+	printk("  0x27: Synth pll N     = 0x%02x\n", snd_sonicvibes_in(sonic, 0x27));
+	printk("  0x08: MIC             = 0x%02x  ", snd_sonicvibes_in(sonic, 0x08));
+	printk("  0x28: ---             = 0x%02x\n", snd_sonicvibes_in(sonic, 0x28));
+	printk("  0x09: Game port       = 0x%02x  ", snd_sonicvibes_in(sonic, 0x09));
+	printk("  0x29: ---             = 0x%02x\n", snd_sonicvibes_in(sonic, 0x29));
+	printk("  0x0a: left synth      = 0x%02x  ", snd_sonicvibes_in(sonic, 0x0a));
+	printk("  0x2a: MPU401          = 0x%02x\n", snd_sonicvibes_in(sonic, 0x2a));
+	printk("  0x0b: right synth     = 0x%02x  ", snd_sonicvibes_in(sonic, 0x0b));
+	printk("  0x2b: drive ctrl      = 0x%02x\n", snd_sonicvibes_in(sonic, 0x2b));
+	printk("  0x0c: left AUX2       = 0x%02x  ", snd_sonicvibes_in(sonic, 0x0c));
+	printk("  0x2c: SRS space       = 0x%02x\n", snd_sonicvibes_in(sonic, 0x2c));
+	printk("  0x0d: right AUX2      = 0x%02x  ", snd_sonicvibes_in(sonic, 0x0d));
+	printk("  0x2d: SRS center      = 0x%02x\n", snd_sonicvibes_in(sonic, 0x2d));
+	printk("  0x0e: left analog     = 0x%02x  ", snd_sonicvibes_in(sonic, 0x0e));
+	printk("  0x2e: wave source     = 0x%02x\n", snd_sonicvibes_in(sonic, 0x2e));
+	printk("  0x0f: right analog    = 0x%02x  ", snd_sonicvibes_in(sonic, 0x0f));
+	printk("  0x2f: ---             = 0x%02x\n", snd_sonicvibes_in(sonic, 0x2f));
+	printk("  0x10: left PCM        = 0x%02x  ", snd_sonicvibes_in(sonic, 0x10));
+	printk("  0x30: analog power    = 0x%02x\n", snd_sonicvibes_in(sonic, 0x30));
+	printk("  0x11: right PCM       = 0x%02x  ", snd_sonicvibes_in(sonic, 0x11));
+	printk("  0x31: analog power    = 0x%02x\n", snd_sonicvibes_in(sonic, 0x31));
+	printk("  0x12: DMA data format = 0x%02x  ", snd_sonicvibes_in(sonic, 0x12));
+	printk("  0x32: ---             = 0x%02x\n", snd_sonicvibes_in(sonic, 0x32));
+	printk("  0x13: P/C enable      = 0x%02x  ", snd_sonicvibes_in(sonic, 0x13));
+	printk("  0x33: ---             = 0x%02x\n", snd_sonicvibes_in(sonic, 0x33));
+	printk("  0x14: U/D button      = 0x%02x  ", snd_sonicvibes_in(sonic, 0x14));
+	printk("  0x34: ---             = 0x%02x\n", snd_sonicvibes_in(sonic, 0x34));
+	printk("  0x15: revision        = 0x%02x  ", snd_sonicvibes_in(sonic, 0x15));
+	printk("  0x35: ---             = 0x%02x\n", snd_sonicvibes_in(sonic, 0x35));
+	printk("  0x16: ADC output ctrl = 0x%02x  ", snd_sonicvibes_in(sonic, 0x16));
+	printk("  0x36: ---             = 0x%02x\n", snd_sonicvibes_in(sonic, 0x36));
+	printk("  0x17: ---             = 0x%02x  ", snd_sonicvibes_in(sonic, 0x17));
+	printk("  0x37: ---             = 0x%02x\n", snd_sonicvibes_in(sonic, 0x37));
+	printk("  0x18: DMA A upper cnt = 0x%02x  ", snd_sonicvibes_in(sonic, 0x18));
+	printk("  0x38: ---             = 0x%02x\n", snd_sonicvibes_in(sonic, 0x38));
+	printk("  0x19: DMA A lower cnt = 0x%02x  ", snd_sonicvibes_in(sonic, 0x19));
+	printk("  0x39: ---             = 0x%02x\n", snd_sonicvibes_in(sonic, 0x39));
+	printk("  0x1a: ---             = 0x%02x  ", snd_sonicvibes_in(sonic, 0x1a));
+	printk("  0x3a: ---             = 0x%02x\n", snd_sonicvibes_in(sonic, 0x3a));
+	printk("  0x1b: ---             = 0x%02x  ", snd_sonicvibes_in(sonic, 0x1b));
+	printk("  0x3b: ---             = 0x%02x\n", snd_sonicvibes_in(sonic, 0x3b));
+	printk("  0x1c: DMA C upper cnt = 0x%02x  ", snd_sonicvibes_in(sonic, 0x1c));
+	printk("  0x3c: ---             = 0x%02x\n", snd_sonicvibes_in(sonic, 0x3c));
+	printk("  0x1d: DMA C upper cnt = 0x%02x  ", snd_sonicvibes_in(sonic, 0x1d));
+	printk("  0x3d: ---             = 0x%02x\n", snd_sonicvibes_in(sonic, 0x3d));
+	printk("  0x1e: PCM rate low    = 0x%02x  ", snd_sonicvibes_in(sonic, 0x1e));
+	printk("  0x3e: ---             = 0x%02x\n", snd_sonicvibes_in(sonic, 0x3e));
+	printk("  0x1f: PCM rate high   = 0x%02x  ", snd_sonicvibes_in(sonic, 0x1f));
+	printk("  0x3f: ---             = 0x%02x\n", snd_sonicvibes_in(sonic, 0x3f));
+}
+
+#endif
+
+static void snd_sonicvibes_setfmt(sonicvibes_t * sonic,
+                                  unsigned char mask,
+                                  unsigned char value)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&sonic->reg_lock, flags);
+	outb(SV_MCE | SV_IREG_DMA_DATA_FMT, SV_REG(sonic, INDEX));
+	if (mask) {
+		sonic->format = inb(SV_REG(sonic, DATA));
+		udelay(10);
+	}
+	sonic->format = (sonic->format & mask) | value;
+	outb(sonic->format, SV_REG(sonic, DATA));
+	udelay(10);
+	outb(0, SV_REG(sonic, INDEX));
+	udelay(10);
+	spin_unlock_irqrestore(&sonic->reg_lock, flags);
+}
+
+static void snd_sonicvibes_pll(unsigned int rate,
+			       unsigned int *res_r,
+			       unsigned int *res_m,
+			       unsigned int *res_n)
+{
+	unsigned int r, m = 0, n = 0;
+	unsigned int xm, xn, xr, xd, metric = ~0U;
+
+	if (rate < 625000 / SV_ADCMULT)
+		rate = 625000 / SV_ADCMULT;
+	if (rate > 150000000 / SV_ADCMULT)
+		rate = 150000000 / SV_ADCMULT;
+	/* slight violation of specs, needed for continuous sampling rates */
+	for (r = 0; rate < 75000000 / SV_ADCMULT; r += 0x20, rate <<= 1);
+	for (xn = 3; xn < 33; xn++)	/* 35 */
+		for (xm = 3; xm < 257; xm++) {
+			xr = ((SV_REFFREQUENCY / SV_ADCMULT) * xm) / xn;
+			if (xr >= rate)
+				xd = xr - rate;
+			else
+				xd = rate - xr;
+			if (xd < metric) {
+				metric = xd;
+				m = xm - 2;
+				n = xn - 2;
+			}
+		}
+	*res_r = r;
+	*res_m = m;
+	*res_n = n;
+#if 0
+	printk("metric = %i, xm = %i, xn = %i\n", metric, xm, xn);
+	printk("pll: m = 0x%x, r = 0x%x, n = 0x%x\n", reg, m, r, n);
+#endif
+}
+
+static void snd_sonicvibes_setpll(sonicvibes_t * sonic,
+                                  unsigned char reg,
+                                  unsigned int rate)
+{
+	unsigned long flags;
+	unsigned int r, m, n;
+
+	snd_sonicvibes_pll(rate, &r, &m, &n);
+	if (sonic != NULL) {
+		spin_lock_irqsave(&sonic->reg_lock, flags);
+		snd_sonicvibes_out1(sonic, reg, m);
+		snd_sonicvibes_out1(sonic, reg + 1, r | n);
+		spin_unlock_irqrestore(&sonic->reg_lock, flags);
+	}
+}
+
+static void snd_sonicvibes_set_adc_rate(sonicvibes_t * sonic, unsigned int rate)
+{
+	unsigned long flags;
+	unsigned int div;
+	unsigned char clock;
+
+	div = 48000 / rate;
+	if (div > 8)
+		div = 8;
+	if ((48000 / div) == rate) {	/* use the alternate clock */
+		clock = 0x10;
+	} else {			/* use the PLL source */
+		clock = 0x00;
+		snd_sonicvibes_setpll(sonic, SV_IREG_ADC_PLL, rate);
+	}
+	spin_lock_irqsave(&sonic->reg_lock, flags);
+	snd_sonicvibes_out1(sonic, SV_IREG_ADC_ALT_RATE, (div - 1) << 4);
+	snd_sonicvibes_out1(sonic, SV_IREG_ADC_CLOCK, clock);
+	spin_unlock_irqrestore(&sonic->reg_lock, flags);
+}
+
+static int snd_sonicvibes_hw_constraint_dac_rate(snd_pcm_hw_params_t *params,
+						 snd_pcm_hw_rule_t *rule)
+{
+	unsigned int rate, div, r, m, n;
+
+	if (hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE)->min == 
+	    hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE)->max) {
+		rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE)->min;
+		div = 48000 / rate;
+		if (div > 8)
+			div = 8;
+		if ((48000 / div) == rate) {
+			params->rate_num = rate;
+			params->rate_den = 1;
+		} else {
+			snd_sonicvibes_pll(rate, &r, &m, &n);
+			snd_assert((SV_REFFREQUENCY % 16) == 0, return -EINVAL);
+			snd_assert((SV_ADCMULT % 512) == 0, return -EINVAL);
+			params->rate_num = (SV_REFFREQUENCY/16) * (n+2) * r;
+			params->rate_den = (SV_ADCMULT/512) * (m+2);
+		}
+	}
+	return 0;
+}
+
+static void snd_sonicvibes_set_dac_rate(sonicvibes_t * sonic, unsigned int rate)
+{
+	unsigned int div;
+	unsigned long flags;
+
+	div = (rate * 65536 + SV_FULLRATE / 2) / SV_FULLRATE;
+	if (div > 65535)
+		div = 65535;
+	spin_lock_irqsave(&sonic->reg_lock, flags);
+	snd_sonicvibes_out1(sonic, SV_IREG_PCM_RATE_HIGH, div >> 8);
+	snd_sonicvibes_out1(sonic, SV_IREG_PCM_RATE_LOW, div);
+	spin_unlock_irqrestore(&sonic->reg_lock, flags);
+}
+
+static int snd_sonicvibes_trigger(sonicvibes_t * sonic, int what, int cmd)
+{
+	int result = 0;
+
+	spin_lock(&sonic->reg_lock);
+	if (cmd == SNDRV_PCM_TRIGGER_START) {
+		if (!(sonic->enable & what)) {
+			sonic->enable |= what;
+			snd_sonicvibes_out1(sonic, SV_IREG_PC_ENABLE, sonic->enable);
+		}
+	} else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+		if (sonic->enable & what) {
+			sonic->enable &= ~what;
+			snd_sonicvibes_out1(sonic, SV_IREG_PC_ENABLE, sonic->enable);
+		}
+	} else {
+		result = -EINVAL;
+	}
+	spin_unlock(&sonic->reg_lock);
+	return result;
+}
+
+static void snd_sonicvibes_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	sonicvibes_t *sonic = snd_magic_cast(sonicvibes_t, dev_id, return);
+	unsigned char status;
+
+	status = inb(SV_REG(sonic, STATUS));
+	if (!(status & (SV_DMAA_IRQ | SV_DMAC_IRQ | SV_MIDI_IRQ)))
+		return;
+	if (status == 0xff) {	/* failure */
+		outb(sonic->irqmask = ~0, SV_REG(sonic, IRQMASK));
+		snd_printk("IRQ failure - interrupts disabled!!\n");
+		return;
+	}
+	if (sonic->pcm) {
+		if (status & SV_DMAA_IRQ)
+			snd_pcm_period_elapsed(sonic->playback_substream);
+		if (status & SV_DMAC_IRQ)
+			snd_pcm_period_elapsed(sonic->capture_substream);
+	}
+	if (sonic->rmidi) {
+		if (status & SV_MIDI_IRQ)
+			snd_mpu401_uart_interrupt(irq, sonic->rmidi->private_data, regs);
+	}
+	if (status & SV_UD_IRQ) {
+		unsigned char udreg;
+		int vol, oleft, oright, mleft, mright;
+
+		spin_lock(&sonic->reg_lock);
+		udreg = snd_sonicvibes_in1(sonic, SV_IREG_UD_BUTTON);
+		vol = udreg & 0x3f;
+		if (!(udreg & 0x40))
+			vol = -vol;
+		oleft = mleft = snd_sonicvibes_in1(sonic, SV_IREG_LEFT_ANALOG);
+		oright = mright = snd_sonicvibes_in1(sonic, SV_IREG_RIGHT_ANALOG);
+		oleft &= 0x1f;
+		oright &= 0x1f;
+		oleft += vol;
+		if (oleft < 0)
+			oleft = 0;
+		if (oleft > 0x1f)
+			oleft = 0x1f;
+		oright += vol;
+		if (oright < 0)
+			oright = 0;
+		if (oright > 0x1f)
+			oright = 0x1f;
+		if (udreg & 0x80) {
+			mleft ^= 0x80;
+			mright ^= 0x80;
+		}
+		oleft |= mleft & 0x80;
+		oright |= mright & 0x80;
+		snd_sonicvibes_out1(sonic, SV_IREG_LEFT_ANALOG, oleft);
+		snd_sonicvibes_out1(sonic, SV_IREG_RIGHT_ANALOG, oright);
+		spin_unlock(&sonic->reg_lock);
+		snd_ctl_notify(sonic->card, SNDRV_CTL_EVENT_MASK_VALUE, &sonic->master_mute->id);
+		snd_ctl_notify(sonic->card, SNDRV_CTL_EVENT_MASK_VALUE, &sonic->master_volume->id);
+	}
+}
+
+/*
+ *  PCM part
+ */
+
+static int snd_sonicvibes_playback_trigger(snd_pcm_substream_t * substream,
+					   int cmd)
+{
+	sonicvibes_t *sonic = snd_pcm_substream_chip(substream);
+	return snd_sonicvibes_trigger(sonic, 1, cmd);
+}
+
+static int snd_sonicvibes_capture_trigger(snd_pcm_substream_t * substream,
+					  int cmd)
+{
+	sonicvibes_t *sonic = snd_pcm_substream_chip(substream);
+	return snd_sonicvibes_trigger(sonic, 2, cmd);
+}
+
+static int snd_sonicvibes_hw_params(snd_pcm_substream_t * substream,
+				    snd_pcm_hw_params_t * hw_params)
+{
+	return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+static int snd_sonicvibes_hw_free(snd_pcm_substream_t * substream)
+{
+	return snd_pcm_lib_free_pages(substream);
+}
+
+static int snd_sonicvibes_playback_prepare(snd_pcm_substream_t * substream)
+{
+	unsigned long flags;
+	sonicvibes_t *sonic = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	unsigned char fmt = 0;
+	unsigned int size = snd_pcm_lib_buffer_bytes(substream);
+	unsigned int count = snd_pcm_lib_period_bytes(substream);
+
+	sonic->p_dma_size = size;
+	count--;
+	if (runtime->channels > 1)
+		fmt |= 1;
+	if (snd_pcm_format_width(runtime->format) == 16)
+		fmt |= 2;
+	snd_sonicvibes_setfmt(sonic, ~3, fmt);
+	snd_sonicvibes_set_dac_rate(sonic, runtime->rate);
+	spin_lock_irqsave(&sonic->reg_lock, flags);
+	snd_sonicvibes_setdmaa(sonic, runtime->dma_addr, size);
+	snd_sonicvibes_out1(sonic, SV_IREG_DMA_A_UPPER, count >> 8);
+	snd_sonicvibes_out1(sonic, SV_IREG_DMA_A_LOWER, count);
+	spin_unlock_irqrestore(&sonic->reg_lock, flags);
+	return 0;
+}
+
+static int snd_sonicvibes_capture_prepare(snd_pcm_substream_t * substream)
+{
+	unsigned long flags;
+	sonicvibes_t *sonic = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	unsigned char fmt = 0;
+	unsigned int size = snd_pcm_lib_buffer_bytes(substream);
+	unsigned int count = snd_pcm_lib_period_bytes(substream);
+
+	sonic->c_dma_size = size;
+	count >>= 1;
+	count--;
+	if (runtime->channels > 1)
+		fmt |= 0x10;
+	if (snd_pcm_format_width(runtime->format) == 16)
+		fmt |= 0x20;
+	snd_sonicvibes_setfmt(sonic, ~0x30, fmt);
+	snd_sonicvibes_set_adc_rate(sonic, runtime->rate);
+	spin_lock_irqsave(&sonic->reg_lock, flags);
+	snd_sonicvibes_setdmac(sonic, runtime->dma_addr, size);
+	snd_sonicvibes_out1(sonic, SV_IREG_DMA_C_UPPER, count >> 8);
+	snd_sonicvibes_out1(sonic, SV_IREG_DMA_C_LOWER, count);
+	spin_unlock_irqrestore(&sonic->reg_lock, flags);
+	return 0;
+}
+
+static snd_pcm_uframes_t snd_sonicvibes_playback_pointer(snd_pcm_substream_t * substream)
+{
+	sonicvibes_t *sonic = snd_pcm_substream_chip(substream);
+	size_t ptr;
+
+	if (!(sonic->enable & 1))
+		return 0;
+	ptr = sonic->p_dma_size - snd_sonicvibes_getdmaa(sonic);
+	return bytes_to_frames(substream->runtime, ptr);
+}
+
+static snd_pcm_uframes_t snd_sonicvibes_capture_pointer(snd_pcm_substream_t * substream)
+{
+	sonicvibes_t *sonic = snd_pcm_substream_chip(substream);
+	size_t ptr;
+	if (!(sonic->enable & 2))
+		return 0;
+	ptr = sonic->c_dma_size - snd_sonicvibes_getdmac(sonic);
+	return bytes_to_frames(substream->runtime, ptr);
+}
+
+#ifdef TARGET_OS2
+static snd_pcm_hardware_t snd_sonicvibes_playback =
+{
+/*	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:		  */	SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+/*	rate_min:	  */	4000,
+/*	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:	  */	1,
+/*	periods_max:	  */	1024,
+/*	fifo_size:	  */	0,
+};
+
+static snd_pcm_hardware_t snd_sonicvibes_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:		  */	SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+/*	rate_min:	  */	4000,
+/*	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:	  */	1,
+/*	periods_max:	  */	1024,
+/*	fifo_size:	  */	0,
+};
+#else
+static snd_pcm_hardware_t snd_sonicvibes_playback =
+{
+	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:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	rate_min:		4000,
+	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:		1,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+
+static snd_pcm_hardware_t snd_sonicvibes_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:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	rate_min:		4000,
+	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:		1,
+	periods_max:		1024,
+	fifo_size:		0,
+};
+#endif
+
+static int snd_sonicvibes_playback_open(snd_pcm_substream_t * substream)
+{
+	sonicvibes_t *sonic = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	sonic->mode |= SV_MODE_PLAY;
+	sonic->playback_substream = substream;
+	runtime->hw = snd_sonicvibes_playback;
+	snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, snd_sonicvibes_hw_constraint_dac_rate, 0, SNDRV_PCM_HW_PARAM_RATE, -1);
+	return 0;
+}
+
+static int snd_sonicvibes_capture_open(snd_pcm_substream_t * substream)
+{
+	sonicvibes_t *sonic = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	sonic->mode |= SV_MODE_CAPTURE;
+	sonic->capture_substream = substream;
+	runtime->hw = snd_sonicvibes_capture;
+	snd_pcm_hw_constraint_ratdens(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+				      &snd_sonicvibes_hw_constraints_adc_clock);
+	return 0;
+}
+
+static int snd_sonicvibes_playback_close(snd_pcm_substream_t * substream)
+{
+	sonicvibes_t *sonic = snd_pcm_substream_chip(substream);
+
+	sonic->playback_substream = NULL;
+	sonic->mode &= ~SV_MODE_PLAY;
+	return 0;
+}
+
+static int snd_sonicvibes_capture_close(snd_pcm_substream_t * substream)
+{
+	sonicvibes_t *sonic = snd_pcm_substream_chip(substream);
+
+	sonic->capture_substream = NULL;
+	sonic->mode &= ~SV_MODE_CAPTURE;
+	return 0;
+}
+
+
+#ifdef TARGET_OS2
+static snd_pcm_ops_t snd_sonicvibes_playback_ops = {
+/*	open:	  */	snd_sonicvibes_playback_open,
+/*	close:	  */	snd_sonicvibes_playback_close,
+/*	ioctl:	  */	snd_pcm_lib_ioctl,
+/*	hw_params:*/	snd_sonicvibes_hw_params,
+/*	hw_free:  */	snd_sonicvibes_hw_free,
+/*	prepare:  */	snd_sonicvibes_playback_prepare,
+/*	trigger:  */	snd_sonicvibes_playback_trigger,
+/*	pointer:  */	snd_sonicvibes_playback_pointer,
+	0, 0
+};
+
+static snd_pcm_ops_t snd_sonicvibes_capture_ops = {
+/*	open:	  */	snd_sonicvibes_capture_open,
+/*	close:	  */	snd_sonicvibes_capture_close,
+/*	ioctl:	  */	snd_pcm_lib_ioctl,
+/*	hw_params:*/	snd_sonicvibes_hw_params,
+/*	hw_free:  */	snd_sonicvibes_hw_free,
+/*	prepare:  */	snd_sonicvibes_capture_prepare,
+/*	trigger:  */	snd_sonicvibes_capture_trigger,
+/*	pointer:  */	snd_sonicvibes_capture_pointer,
+	0, 0
+};
+#else
+static snd_pcm_ops_t snd_sonicvibes_playback_ops = {
+	open:		snd_sonicvibes_playback_open,
+	close:		snd_sonicvibes_playback_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	hw_params:	snd_sonicvibes_hw_params,
+	hw_free:	snd_sonicvibes_hw_free,
+	prepare:	snd_sonicvibes_playback_prepare,
+	trigger:	snd_sonicvibes_playback_trigger,
+	pointer:	snd_sonicvibes_playback_pointer,
+};
+
+static snd_pcm_ops_t snd_sonicvibes_capture_ops = {
+	open:		snd_sonicvibes_capture_open,
+	close:		snd_sonicvibes_capture_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	hw_params:	snd_sonicvibes_hw_params,
+	hw_free:	snd_sonicvibes_hw_free,
+	prepare:	snd_sonicvibes_capture_prepare,
+	trigger:	snd_sonicvibes_capture_trigger,
+	pointer:	snd_sonicvibes_capture_pointer,
+};
+#endif
+
+static void snd_sonicvibes_pcm_free(snd_pcm_t *pcm)
+{
+	sonicvibes_t *sonic = snd_magic_cast(sonicvibes_t, pcm->private_data, return);
+	sonic->pcm = NULL;
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static int __init snd_sonicvibes_pcm(sonicvibes_t * sonic, int device, snd_pcm_t ** rpcm)
+{
+	snd_pcm_t *pcm;
+	int err;
+
+	if ((err = snd_pcm_new(sonic->card, "s3_86c617", device, 1, 1, &pcm)) < 0)
+		return err;
+	snd_assert(pcm != NULL, return -EINVAL);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_sonicvibes_playback_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_sonicvibes_capture_ops);
+
+	pcm->private_data = sonic;
+	pcm->private_free = snd_sonicvibes_pcm_free;
+	pcm->info_flags = 0;
+	strcpy(pcm->name, "S3 SonicVibes");
+	sonic->pcm = pcm;
+
+	snd_pcm_lib_preallocate_pci_pages_for_all(sonic->pci, pcm, 64*1024, 128*1024);
+
+	if (rpcm)
+		*rpcm = pcm;
+	return 0;
+}
+
+/*
+ *  Mixer part
+ */
+
+#ifdef TARGET_OS2
+#define SONICVIBES_MUX(xname, xindex) \
+{ SNDRV_CTL_ELEM_IFACE_MIXER, 0, 0, xname, xindex, \
+  0, 0,snd_sonicvibes_info_mux, \
+  snd_sonicvibes_get_mux, snd_sonicvibes_put_mux, 0}
+#else
+#define SONICVIBES_MUX(xname, xindex) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
+  info: snd_sonicvibes_info_mux, \
+  get: snd_sonicvibes_get_mux, put: snd_sonicvibes_put_mux }
+#endif
+
+static int snd_sonicvibes_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	static char *texts[7] = {
+		"CD", "PCM", "Aux1", "Line", "Aux0", "Mic", "Mix"
+	};
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 2;
+	uinfo->value.enumerated.items = 7;
+	if (uinfo->value.enumerated.item >= 7)
+		uinfo->value.enumerated.item = 6;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int snd_sonicvibes_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	sonicvibes_t *sonic = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	
+	spin_lock_irqsave(&sonic->reg_lock, flags);
+	ucontrol->value.enumerated.item[0] = ((snd_sonicvibes_in1(sonic, SV_IREG_LEFT_ADC) & SV_RECSRC_OUT) >> 5) - 1;
+	ucontrol->value.enumerated.item[1] = ((snd_sonicvibes_in1(sonic, SV_IREG_RIGHT_ADC) & SV_RECSRC_OUT) >> 5) - 1;
+	spin_unlock_irqrestore(&sonic->reg_lock, flags);
+	return 0;
+}
+
+static int snd_sonicvibes_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	sonicvibes_t *sonic = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	unsigned short left, right, oval1, oval2;
+	int change;
+	
+	if (ucontrol->value.enumerated.item[0] >= 7 ||
+	    ucontrol->value.enumerated.item[1] >= 7)
+		return -EINVAL;
+	left = (ucontrol->value.enumerated.item[0] + 1) << 5;
+	right = (ucontrol->value.enumerated.item[1] + 1) << 5;
+	spin_lock_irqsave(&sonic->reg_lock, flags);
+	oval1 = snd_sonicvibes_in1(sonic, SV_IREG_LEFT_ADC);
+	oval2 = snd_sonicvibes_in1(sonic, SV_IREG_RIGHT_ADC);
+	left = (oval1 & ~SV_RECSRC_OUT) | left;
+	right = (oval2 & ~SV_RECSRC_OUT) | right;
+	change = left != oval1 || right != oval2;
+	snd_sonicvibes_out1(sonic, SV_IREG_LEFT_ADC, left);
+	snd_sonicvibes_out1(sonic, SV_IREG_RIGHT_ADC, right);
+	spin_unlock_irqrestore(&sonic->reg_lock, flags);
+	return change;
+}
+
+#ifdef TARGET_OS2
+#define SONICVIBES_SINGLE(xname, xindex, reg, shift, mask, invert) \
+{ SNDRV_CTL_ELEM_IFACE_MIXER, 0, 0, xname, xindex, \
+  0, 0, snd_sonicvibes_info_single, \
+  snd_sonicvibes_get_single, snd_sonicvibes_put_single, \
+  reg | (shift << 8) | (mask << 16) | (invert << 24) }
+#else
+#define SONICVIBES_SINGLE(xname, xindex, reg, shift, mask, invert) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
+  info: snd_sonicvibes_info_single, \
+  get: snd_sonicvibes_get_single, put: snd_sonicvibes_put_single, \
+  private_value: reg | (shift << 8) | (mask << 16) | (invert << 24) }
+#endif
+
+static int snd_sonicvibes_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+
+	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	return 0;
+}
+
+static int snd_sonicvibes_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	sonicvibes_t *sonic = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 8) & 0xff;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0xff;
+	
+	spin_lock_irqsave(&sonic->reg_lock, flags);
+	ucontrol->value.integer.value[0] = (snd_sonicvibes_in1(sonic, reg)>> shift) & mask;
+	spin_unlock_irqrestore(&sonic->reg_lock, flags);
+	if (invert)
+		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+	return 0;
+}
+
+static int snd_sonicvibes_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	sonicvibes_t *sonic = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 8) & 0xff;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0xff;
+	int change;
+	unsigned short val, oval;
+	
+	val = (ucontrol->value.integer.value[0] & mask);
+	if (invert)
+		val = mask - val;
+	val <<= shift;
+	spin_lock_irqsave(&sonic->reg_lock, flags);
+	oval = snd_sonicvibes_in1(sonic, reg);
+	val = (oval & ~(mask << shift)) | val;
+	change = val != oval;
+	snd_sonicvibes_out1(sonic, reg, val);
+	spin_unlock_irqrestore(&sonic->reg_lock, flags);
+	return change;
+}
+
+#ifdef TARGET_OS2
+#define SONICVIBES_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \
+{ SNDRV_CTL_ELEM_IFACE_MIXER, 0, 0, xname, xindex, \
+  0, 0, snd_sonicvibes_info_double, \
+  snd_sonicvibes_get_double, snd_sonicvibes_put_double, \
+  left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
+#else
+#define SONICVIBES_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
+  info: snd_sonicvibes_info_double, \
+  get: snd_sonicvibes_get_double, put: snd_sonicvibes_put_double, \
+  private_value: left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
+#endif
+
+static int snd_sonicvibes_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+
+	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	return 0;
+}
+
+static int snd_sonicvibes_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	sonicvibes_t *sonic = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int left_reg = kcontrol->private_value & 0xff;
+	int right_reg = (kcontrol->private_value >> 8) & 0xff;
+	int shift_left = (kcontrol->private_value >> 16) & 0x07;
+	int shift_right = (kcontrol->private_value >> 19) & 0x07;
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+	int invert = (kcontrol->private_value >> 22) & 1;
+	
+	spin_lock_irqsave(&sonic->reg_lock, flags);
+	ucontrol->value.integer.value[0] = (snd_sonicvibes_in1(sonic, left_reg) >> shift_left) & mask;
+	ucontrol->value.integer.value[1] = (snd_sonicvibes_in1(sonic, right_reg) >> shift_right) & mask;
+	spin_unlock_irqrestore(&sonic->reg_lock, flags);
+	if (invert) {
+		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+		ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1];
+	}
+	return 0;
+}
+
+static int snd_sonicvibes_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	sonicvibes_t *sonic = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int left_reg = kcontrol->private_value & 0xff;
+	int right_reg = (kcontrol->private_value >> 8) & 0xff;
+	int shift_left = (kcontrol->private_value >> 16) & 0x07;
+	int shift_right = (kcontrol->private_value >> 19) & 0x07;
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+	int invert = (kcontrol->private_value >> 22) & 1;
+	int change;
+	unsigned short val1, val2, oval1, oval2;
+	
+	val1 = ucontrol->value.integer.value[0] & mask;
+	val2 = ucontrol->value.integer.value[1] & mask;
+	if (invert) {
+		val1 = mask - val1;
+		val2 = mask - val2;
+	}
+	val1 <<= shift_left;
+	val2 <<= shift_right;
+	spin_lock_irqsave(&sonic->reg_lock, flags);
+	oval1 = snd_sonicvibes_in1(sonic, left_reg);
+	oval2 = snd_sonicvibes_in1(sonic, right_reg);
+	val1 = (oval1 & ~(mask << shift_left)) | val1;
+	val2 = (oval2 & ~(mask << shift_right)) | val2;
+	change = val1 != oval1 || val2 != oval2;
+	snd_sonicvibes_out1(sonic, left_reg, val1);
+	snd_sonicvibes_out1(sonic, right_reg, val2);
+	spin_unlock_irqrestore(&sonic->reg_lock, flags);
+	return change;
+}
+
+#define SONICVIBES_CONTROLS (sizeof(snd_sonicvibes_controls)/sizeof(snd_kcontrol_new_t))
+
+static snd_kcontrol_new_t snd_sonicvibes_controls[] = {
+SONICVIBES_DOUBLE("Capture Volume", 0, SV_IREG_LEFT_ADC, SV_IREG_RIGHT_ADC, 0, 0, 15, 0),
+SONICVIBES_DOUBLE("Aux Playback Switch", 0, SV_IREG_LEFT_AUX1, SV_IREG_RIGHT_AUX1, 7, 7, 1, 1),
+SONICVIBES_DOUBLE("Aux Playback Volume", 0, SV_IREG_LEFT_AUX1, SV_IREG_RIGHT_AUX1, 0, 0, 31, 1),
+SONICVIBES_DOUBLE("CD Playback Switch", 0, SV_IREG_LEFT_CD, SV_IREG_RIGHT_CD, 7, 7, 1, 1),
+SONICVIBES_DOUBLE("CD Playback Volume", 0, SV_IREG_LEFT_CD, SV_IREG_RIGHT_CD, 0, 0, 31, 1),
+SONICVIBES_DOUBLE("Line Playback Switch", 0, SV_IREG_LEFT_LINE, SV_IREG_RIGHT_LINE, 7, 7, 1, 1),
+SONICVIBES_DOUBLE("Line Playback Volume", 0, SV_IREG_LEFT_LINE, SV_IREG_RIGHT_LINE, 0, 0, 31, 1),
+SONICVIBES_SINGLE("Mic Playback Switch", 0, SV_IREG_MIC, 7, 1, 1),
+SONICVIBES_SINGLE("Mic Playback Volume", 0, SV_IREG_MIC, 0, 15, 1),
+SONICVIBES_SINGLE("Mic Boost", 0, SV_IREG_LEFT_ADC, 4, 1, 0),
+SONICVIBES_DOUBLE("Synth Playback Switch", 0, SV_IREG_LEFT_SYNTH, SV_IREG_RIGHT_SYNTH, 7, 7, 1, 1),
+SONICVIBES_DOUBLE("Synth Playback Volume", 0, SV_IREG_LEFT_SYNTH, SV_IREG_RIGHT_SYNTH, 0, 0, 31, 1),
+SONICVIBES_DOUBLE("Aux Playback Switch", 1, SV_IREG_LEFT_AUX2, SV_IREG_RIGHT_AUX2, 7, 7, 1, 1),
+SONICVIBES_DOUBLE("Aux Playback Volume", 1, SV_IREG_LEFT_AUX2, SV_IREG_RIGHT_AUX2, 0, 0, 31, 1),
+SONICVIBES_DOUBLE("Master Playback Switch", 0, SV_IREG_LEFT_ANALOG, SV_IREG_RIGHT_ANALOG, 7, 7, 1, 1),
+SONICVIBES_DOUBLE("Master Playback Volume", 0, SV_IREG_LEFT_ANALOG, SV_IREG_RIGHT_ANALOG, 0, 0, 31, 1),
+SONICVIBES_DOUBLE("PCM Playback Switch", 0, SV_IREG_LEFT_PCM, SV_IREG_RIGHT_PCM, 7, 7, 1, 1),
+SONICVIBES_DOUBLE("PCM Playback Volume", 0, SV_IREG_LEFT_PCM, SV_IREG_RIGHT_PCM, 0, 0, 63, 1),
+SONICVIBES_SINGLE("Loopback Capture Switch", 0, SV_IREG_ADC_OUTPUT_CTRL, 0, 1, 0),
+SONICVIBES_SINGLE("Loopback Capture Volume", 0, SV_IREG_ADC_OUTPUT_CTRL, 2, 63, 1),
+SONICVIBES_MUX("Capture Source", 0)
+};
+
+static void snd_sonicvibes_master_free(snd_kcontrol_t *kcontrol)
+{
+	sonicvibes_t *sonic = snd_magic_cast(sonicvibes_t, _snd_kcontrol_chip(kcontrol), return);
+	sonic->master_mute = NULL;
+	sonic->master_volume = NULL;
+}
+
+static int __init snd_sonicvibes_mixer(sonicvibes_t * sonic)
+{
+	snd_card_t *card;
+	snd_kcontrol_t *kctl;
+	int idx, err;
+
+	snd_assert(sonic != NULL && sonic->card != NULL, return -EINVAL);
+	card = sonic->card;
+	strcpy(card->mixername, "S3 SonicVibes");
+
+	for (idx = 0; idx < SONICVIBES_CONTROLS; idx++) {
+		if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_sonicvibes_controls[idx], sonic))) < 0)
+			return err;
+		switch (idx) {
+		case 0:
+		case 1: kctl->private_free = snd_sonicvibes_master_free; break;
+		}
+	}
+	return 0;
+}
+
+/*
+
+ */
+
+static void snd_sonicvibes_proc_read(snd_info_entry_t *entry, 
+				     snd_info_buffer_t * buffer)
+{
+	sonicvibes_t *sonic = snd_magic_cast(sonicvibes_t, entry->private_data, return);
+	unsigned char tmp;
+
+	tmp = sonic->srs_space & 0x0f;
+	snd_iprintf(buffer, "SRS 3D           : %s\n",
+		    sonic->srs_space & 0x80 ? "off" : "on");
+	snd_iprintf(buffer, "SRS Space        : %s\n",
+		    tmp == 0x00 ? "100%" :
+		    tmp == 0x01 ? "75%" :
+		    tmp == 0x02 ? "50%" :
+		    tmp == 0x03 ? "25%" : "0%");
+	tmp = sonic->srs_center & 0x0f;
+	snd_iprintf(buffer, "SRS Center       : %s\n",
+		    tmp == 0x00 ? "100%" :
+		    tmp == 0x01 ? "75%" :
+		    tmp == 0x02 ? "50%" :
+		    tmp == 0x03 ? "25%" : "0%");
+	tmp = sonic->wave_source & 0x03;
+	snd_iprintf(buffer, "WaveTable Source : %s\n",
+		    tmp == 0x00 ? "on-board ROM" :
+		    tmp == 0x01 ? "PCI bus" : "on-board ROM + PCI bus");
+	tmp = sonic->mpu_switch;
+	snd_iprintf(buffer, "Onboard synth    : %s\n", tmp & 0x01 ? "on" : "off");
+	snd_iprintf(buffer, "Ext. Rx to synth : %s\n", tmp & 0x02 ? "on" : "off");
+	snd_iprintf(buffer, "MIDI to ext. Tx  : %s\n", tmp & 0x04 ? "on" : "off");
+}
+
+static void __init snd_sonicvibes_proc_init(sonicvibes_t * sonic)
+{
+	snd_info_entry_t *entry;
+
+	if ((entry = snd_info_create_card_entry(sonic->card, "sonicvibes", sonic->card->proc_root)) != NULL) {
+		entry->content = SNDRV_INFO_CONTENT_TEXT;
+		entry->private_data = sonic;
+		entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
+		entry->c.text.read_size = 256;
+		entry->c.text.read = snd_sonicvibes_proc_read;
+		if (snd_info_register(entry) < 0) {
+			snd_info_free_entry(entry);
+			entry = NULL;
+		}
+	}
+	sonic->proc_entry = entry;
+}
+
+static void snd_sonicvibes_proc_done(sonicvibes_t * sonic)
+{
+	if (sonic->proc_entry) {
+		snd_info_unregister(sonic->proc_entry);
+		sonic->proc_entry = NULL;
+	}
+}
+
+/*
+
+ */
+
+static snd_kcontrol_new_t snd_sonicvibes_game_control =
+SONICVIBES_SINGLE("Joystick Speed", 0, SV_IREG_GAME_PORT, 1, 15, 0);
+
+static int snd_sonicvibes_free(sonicvibes_t *sonic)
+{
+	snd_sonicvibes_proc_done(sonic);
+	pci_write_config_dword(sonic->pci, 0x40, sonic->dmaa_port);
+	pci_write_config_dword(sonic->pci, 0x48, sonic->dmac_port);
+	if (sonic->res_sb_port)
+		release_resource(sonic->res_sb_port);
+	if (sonic->res_enh_port)
+		release_resource(sonic->res_enh_port);
+	if (sonic->res_synth_port)
+		release_resource(sonic->res_synth_port);
+	if (sonic->res_midi_port)
+		release_resource(sonic->res_midi_port);
+	if (sonic->res_dmaa)
+		release_resource(sonic->res_dmaa);
+	if (sonic->res_dmac)
+		release_resource(sonic->res_dmac);
+	if (sonic->irq >= 0)
+		free_irq(sonic->irq, (void *)sonic);
+	snd_magic_kfree(sonic);
+	return 0;
+}
+
+static int snd_sonicvibes_dev_free(snd_device_t *device)
+{
+	sonicvibes_t *sonic = snd_magic_cast(sonicvibes_t, device->device_data, return -ENXIO);
+	return snd_sonicvibes_free(sonic);
+}
+
+static int __init snd_sonicvibes_create(snd_card_t * card,
+					struct pci_dev *pci,
+					int reverb,
+					int mge,
+					sonicvibes_t ** rsonic)
+{
+	sonicvibes_t *sonic;
+	unsigned int dmaa, dmac;
+	int err;
+#ifdef TARGET_OS2
+	static snd_device_ops_t ops = {
+		snd_sonicvibes_dev_free,0,0,0
+	};
+#else
+	static snd_device_ops_t ops = {
+		dev_free:	snd_sonicvibes_dev_free,
+	};
+#endif
+
+	*rsonic = NULL;
+	/* enable PCI device */
+	if ((err = pci_enable_device(pci)) < 0)
+		return err;
+	/* check, if we can restrict PCI DMA transfers to 24 bits */
+        if (!pci_dma_supported(pci, 0x00ffffff)) {
+                snd_printk("architecture does not support 24bit PCI busmaster DMA\n");
+                return -ENXIO;
+        }
+	pci_set_dma_mask(pci, 0x00ffffff);
+
+	sonic = snd_magic_kcalloc(sonicvibes_t, 0, GFP_KERNEL);
+	if (sonic == NULL)
+		return -ENOMEM;
+	spin_lock_init(&sonic->reg_lock);
+	sonic->card = card;
+	sonic->pci = pci;
+	sonic->irq = -1;
+	sonic->sb_port = pci_resource_start(pci, 0);
+	if ((sonic->res_sb_port = request_region(sonic->sb_port, 0x10, "S3 SonicVibes SB")) == NULL) {
+		snd_sonicvibes_free(sonic);
+		snd_printk("unable to grab SB port at 0x%lx-0x%lx\n", sonic->sb_port, sonic->sb_port + 0x10 - 1);
+		return -EBUSY;
+	}
+	sonic->enh_port = pci_resource_start(pci, 1);
+	if ((sonic->res_enh_port = request_region(sonic->enh_port, 0x10, "S3 SonicVibes Enhanced")) == NULL) {
+		snd_sonicvibes_free(sonic);
+		snd_printk("unable to grab PCM port at 0x%lx-0x%lx\n", sonic->enh_port, sonic->enh_port + 0x10 - 1);
+		return -EBUSY;
+	}
+	sonic->synth_port = pci_resource_start(pci, 2);
+	if ((sonic->res_synth_port = request_region(sonic->synth_port, 4, "S3 SonicVibes Synth")) == NULL) {
+		snd_sonicvibes_free(sonic);
+		snd_printk("unable to grab synth port at 0x%lx-0x%lx\n", sonic->synth_port, sonic->synth_port + 4 - 1);
+		return -EBUSY;
+	}
+	sonic->midi_port = pci_resource_start(pci, 3);
+	if ((sonic->res_midi_port = request_region(sonic->midi_port, 4, "S3 SonicVibes Midi")) == NULL) {
+		snd_sonicvibes_free(sonic);
+		snd_printk("unable to grab MIDI port at 0x%lx-0x%lx\n", sonic->midi_port, sonic->midi_port + 4 - 1);
+		return -EBUSY;
+	}
+	sonic->game_port = pci_resource_start(pci, 4);
+	if (request_irq(pci->irq, snd_sonicvibes_interrupt, SA_INTERRUPT|SA_SHIRQ, "S3 SonicVibes", (void *)sonic)) {
+		snd_magic_kfree(sonic);
+		snd_printk("unable to grab IRQ %d\n", pci->irq);
+		return -EBUSY;
+	}
+	sonic->irq = pci->irq;
+
+	pci_read_config_dword(pci, 0x40, &dmaa);
+	pci_read_config_dword(pci, 0x48, &dmac);
+	snd_dmaio &= ~0x0f;
+	dmaa &= ~0x0f;
+	dmac &= ~0x0f;
+	if (!dmaa) {
+		dmaa = snd_dmaio;
+		snd_dmaio += 0x10;
+		snd_printk("BIOS did not allocate DDMA channel A i/o, allocated at 0x%x\n", dmaa);
+	}
+	if (!dmac) {
+		dmac = snd_dmaio;
+		snd_dmaio += 0x10;
+		snd_printk("BIOS did not allocate DDMA channel C i/o, allocated at 0x%x\n", dmac);
+	}
+	pci_write_config_dword(pci, 0x40, dmaa);
+	pci_write_config_dword(pci, 0x48, dmac);
+
+	if ((sonic->res_dmaa = request_region(dmaa, 0x10, "S3 SonicVibes DDMA-A")) == NULL) {
+		snd_sonicvibes_free(sonic);
+		snd_printk("unable to grab DDMA-A port at 0x%x-0x%x\n", dmaa, dmaa + 0x10 - 1);
+		return -EBUSY;
+	}
+	if ((sonic->res_dmac = request_region(dmac, 0x10, "S3 SonicVibes DDMA-C")) == NULL) {
+		snd_sonicvibes_free(sonic);
+		snd_printk("unable to grab DDMA-C port at 0x%x-0x%x\n", dmac, dmac + 0x10 - 1);
+		return -EBUSY;
+	}
+
+	pci_read_config_dword(pci, 0x40, &sonic->dmaa_port);
+	pci_read_config_dword(pci, 0x48, &sonic->dmac_port);
+	sonic->dmaa_port &= ~0x0f;
+	sonic->dmac_port &= ~0x0f;
+	pci_write_config_dword(pci, 0x40, sonic->dmaa_port | 9);	/* enable + enhanced */
+	pci_write_config_dword(pci, 0x48, sonic->dmac_port | 9);	/* enable */
+	/* ok.. initialize S3 SonicVibes chip */
+	outb(SV_RESET, SV_REG(sonic, CONTROL));		/* reset chip */
+	udelay(100);
+	outb(0, SV_REG(sonic, CONTROL));	/* release reset */
+	udelay(100);
+	outb(SV_ENHANCED | SV_INTA | (reverb ? SV_REVERB : 0), SV_REG(sonic, CONTROL));
+	inb(SV_REG(sonic, STATUS));	/* clear IRQs */
+#if 1
+	snd_sonicvibes_out(sonic, SV_IREG_DRIVE_CTRL, 0);	/* drive current 16mA */
+#else
+	snd_sonicvibes_out(sonic, SV_IREG_DRIVE_CTRL, 0x40);	/* drive current 8mA */
+#endif
+	snd_sonicvibes_out(sonic, SV_IREG_PC_ENABLE, sonic->enable = 0);	/* disable playback & capture */
+	outb(sonic->irqmask = ~(SV_DMAA_MASK | SV_DMAC_MASK | SV_UD_MASK), SV_REG(sonic, IRQMASK));
+	inb(SV_REG(sonic, STATUS));	/* clear IRQs */
+	snd_sonicvibes_out(sonic, SV_IREG_ADC_CLOCK, 0);	/* use PLL as clock source */
+	snd_sonicvibes_out(sonic, SV_IREG_ANALOG_POWER, 0);	/* power up analog parts */
+	snd_sonicvibes_out(sonic, SV_IREG_DIGITAL_POWER, 0);	/* power up digital parts */
+	snd_sonicvibes_setpll(sonic, SV_IREG_ADC_PLL, 8000);
+	snd_sonicvibes_out(sonic, SV_IREG_SRS_SPACE, sonic->srs_space = 0x80);	/* SRS space off */
+	snd_sonicvibes_out(sonic, SV_IREG_SRS_CENTER, sonic->srs_center = 0x00);/* SRS center off */
+	snd_sonicvibes_out(sonic, SV_IREG_MPU401, sonic->mpu_switch = 0x05);	/* MPU-401 switch */
+	snd_sonicvibes_out(sonic, SV_IREG_WAVE_SOURCE, sonic->wave_source = 0x00);	/* onboard ROM */
+	snd_sonicvibes_out(sonic, SV_IREG_PCM_RATE_LOW, (8000 * 65536 / SV_FULLRATE) & 0xff);
+	snd_sonicvibes_out(sonic, SV_IREG_PCM_RATE_HIGH, ((8000 * 65536 / SV_FULLRATE) >> 8) & 0xff);
+	snd_sonicvibes_out(sonic, SV_IREG_LEFT_ADC, mge ? 0xd0 : 0xc0);
+	snd_sonicvibes_out(sonic, SV_IREG_RIGHT_ADC, 0xc0);
+	snd_sonicvibes_out(sonic, SV_IREG_LEFT_AUX1, 0x9f);
+	snd_sonicvibes_out(sonic, SV_IREG_RIGHT_AUX1, 0x9f);
+	snd_sonicvibes_out(sonic, SV_IREG_LEFT_CD, 0x9f);
+	snd_sonicvibes_out(sonic, SV_IREG_RIGHT_CD, 0x9f);
+	snd_sonicvibes_out(sonic, SV_IREG_LEFT_LINE, 0x9f);
+	snd_sonicvibes_out(sonic, SV_IREG_RIGHT_LINE, 0x9f);
+	snd_sonicvibes_out(sonic, SV_IREG_MIC, 0x8f);
+	snd_sonicvibes_out(sonic, SV_IREG_LEFT_SYNTH, 0x9f);
+	snd_sonicvibes_out(sonic, SV_IREG_RIGHT_SYNTH, 0x9f);
+	snd_sonicvibes_out(sonic, SV_IREG_LEFT_AUX2, 0x9f);
+	snd_sonicvibes_out(sonic, SV_IREG_RIGHT_AUX2, 0x9f);
+	snd_sonicvibes_out(sonic, SV_IREG_LEFT_ANALOG, 0x9f);
+	snd_sonicvibes_out(sonic, SV_IREG_RIGHT_ANALOG, 0x9f);
+	snd_sonicvibes_out(sonic, SV_IREG_LEFT_PCM, 0xbf);
+	snd_sonicvibes_out(sonic, SV_IREG_RIGHT_PCM, 0xbf);
+	snd_sonicvibes_out(sonic, SV_IREG_ADC_OUTPUT_CTRL, 0xfc);
+#if 0
+	snd_sonicvibes_debug(sonic);
+#endif
+	sonic->revision = snd_sonicvibes_in(sonic, SV_IREG_REVISION);
+	snd_ctl_add(card, snd_ctl_new1(&snd_sonicvibes_game_control, sonic));
+	snd_sonicvibes_proc_init(sonic);
+
+	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, sonic, &ops)) < 0) {
+		snd_sonicvibes_free(sonic);
+		return err;
+	}
+
+	*rsonic = sonic;
+	return 0;
+}
+
+/*
+ *  MIDI section
+ */
+
+#define SONICVIBES_MIDI_CONTROLS (sizeof(snd_sonicvibes_midi_controls)/sizeof(snd_kcontrol_new_t))
+
+static snd_kcontrol_new_t snd_sonicvibes_midi_controls[] = {
+SONICVIBES_SINGLE("SonicVibes Wave Source RAM", 0, SV_IREG_WAVE_SOURCE, 0, 1, 0),
+SONICVIBES_SINGLE("SonicVibes Wave Source RAM+ROM", 0, SV_IREG_WAVE_SOURCE, 1, 1, 0),
+SONICVIBES_SINGLE("SonicVibes Onboard Synth", 0, SV_IREG_MPU401, 0, 1, 0),
+SONICVIBES_SINGLE("SonicVibes External Rx to Synth", 0, SV_IREG_MPU401, 1, 1, 0),
+SONICVIBES_SINGLE("SonicVibes External Tx", 0, SV_IREG_MPU401, 2, 1, 0)
+};
+
+static void snd_sonicvibes_midi_input_open(mpu401_t * mpu)
+{
+	sonicvibes_t *sonic = snd_magic_cast(sonicvibes_t, mpu->private_data, return);
+	outb(sonic->irqmask &= ~SV_MIDI_MASK, SV_REG(sonic, IRQMASK));
+}
+
+static void snd_sonicvibes_midi_input_close(mpu401_t * mpu)
+{
+	sonicvibes_t *sonic = snd_magic_cast(sonicvibes_t, mpu->private_data, return);
+	outb(sonic->irqmask |= SV_MIDI_MASK, SV_REG(sonic, IRQMASK));
+}
+
+static int snd_sonicvibes_midi(sonicvibes_t * sonic, snd_rawmidi_t * rmidi)
+{
+	mpu401_t * mpu = snd_magic_cast(mpu401_t, rmidi->private_data, return -ENXIO);
+	snd_card_t *card = sonic->card;
+	snd_rawmidi_str_t *dir;
+	int idx, err;
+
+	mpu->private_data = sonic;
+	mpu->open_input = snd_sonicvibes_midi_input_open;
+	mpu->close_input = snd_sonicvibes_midi_input_close;
+	dir = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT];
+	for (idx = 0; idx < SONICVIBES_MIDI_CONTROLS; idx++)
+		if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_sonicvibes_midi_controls[idx], sonic))) < 0)
+			return err;
+	return 0;
+}
+
+static int __init snd_sonic_probe(struct pci_dev *pci,
+				  const struct pci_device_id *id)
+{
+	static int dev = 0;
+	snd_card_t *card;
+	sonicvibes_t *sonic;
+	snd_rawmidi_t *midi_uart;
+	opl3_t *opl3;
+	int idx, err;
+
+	for ( ; dev < SNDRV_CARDS; dev++) {
+		if (!snd_enable[dev]) {
+			dev++;
+			return -ENOENT;
+		}
+		break;
+	}
+	if (dev >= SNDRV_CARDS)
+		return -ENODEV;
+ 
+	card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0);
+	if (card == NULL)
+		return -ENOMEM;
+	for (idx = 0; idx < 5; idx++) {
+		if (pci_resource_start(pci, idx) == 0 ||
+		    !(pci_resource_flags(pci, idx) & IORESOURCE_IO)) {
+			snd_card_free(card);
+			return -ENODEV;
+		}
+	}
+	if ((err = snd_sonicvibes_create(card, pci,
+					 snd_reverb[dev] ? 1 : 0,
+					 snd_mge[dev] ? 1 : 0,
+					 &sonic)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_sonicvibes_pcm(sonic, 0, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_sonicvibes_mixer(sonic)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+        if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_SONICVIBES,
+                                       sonic->midi_port, MPU401_INFO_INTEGRATED,
+				       sonic->irq, 0,
+				       &midi_uart)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	snd_sonicvibes_midi(sonic, midi_uart);
+	if ((err = snd_opl3_create(card, sonic->synth_port,
+				   sonic->synth_port + 2,
+				   OPL3_HW_OPL3_SV, 1, &opl3)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	strcpy(card->driver, "SonicVibes");
+	strcpy(card->shortname, "S3 SonicVibes");
+	sprintf(card->longname, "%s rev %i at 0x%lx, irq %i",
+		card->shortname,
+		sonic->revision,
+		pci_resource_start(pci, 1),
+		sonic->irq);
+
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	
+	PCI_SET_DRIVER_DATA(pci, card);
+	dev++;
+	return 0;
+}
+
+static void __exit snd_sonic_remove(struct pci_dev *pci)
+{
+	snd_card_free(PCI_GET_DRIVER_DATA(pci));
+	PCI_SET_DRIVER_DATA(pci, NULL);
+}
+
+static struct pci_driver driver = {
+#ifdef TARGET_OS2
+        0, 0, 0,
+	"S3 SonicVibes",
+	snd_sonic_ids,
+	snd_sonic_probe,
+	snd_sonic_remove,
+        0, 0
+#else
+	name: "S3 SonicVibes",
+	id_table: snd_sonic_ids,
+	probe: snd_sonic_probe,
+	remove: snd_sonic_remove,
+#endif
+};
+
+static int __init alsa_card_sonicvibes_init(void)
+{
+	int err;
+
+	if ((err = pci_module_init(&driver)) < 0) {
+#ifdef MODULE
+//		snd_printk("S3 SonicVibes soundcard not found or device busy\n");
+#endif
+		return err;
+	}
+	return 0;
+}
+
+static void __exit alsa_card_sonicvibes_exit(void)
+{
+	pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_sonicvibes_init)
+module_exit(alsa_card_sonicvibes_exit)
+
+#ifndef MODULE
+
+/* format is: snd-card-sonicvibes=snd_enable,snd_index,snd_id,
+				  snd_reverb,snd_mge,snd_dmaio */
+
+static int __init alsa_card_sonicvibes_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_reverb[nr_dev]) == 2 &&
+	       get_option(&str,&snd_mge[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&snd_dmaio) == 2);
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-card-sonicvibes=", alsa_card_sonicvibes_setup);
+
+#endif /* ifndef MODULE */
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/trident/aaa
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/trident/aaa	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/trident/aaa	(revision 91)
@@ -0,0 +1,32 @@
+Open Watcom Make Version 1.2
+Portions Copyright (c) 1988-2002 Sybase, Inc. All Rights Reserved.
+Source code is available under the Sybase Open Watcom Public License.
+See http://www.openwatcom.org/ for details.
+	WCC386 -bt=os2v2 -e60 -5r -omlinear -s -w4 -ze -zdp -zl -zq -nt=CODE32 -zff -zgf -zp4 -mf -DKEE -DFLATSTACK -D__KERNEL__ -DMODULE -dTARGET_OS2 -dALSA_BUILD -D__ISAPNP__ -DCONFIG_ISAPNP -D__i386_ -i..\..\..\include;..\..\include;e:\owatcom\H -fobin\Release.KEE\trident.obj trident.c
+..\..\include\sound\pcm.h(248): Warning! W202: Symbol 'constrs_interval' has been defined, but not referenced
+..\..\include\sound\pcm.h(242): Warning! W202: Symbol 'constrs_mask' has been defined, but not referenced
+..\..\include\sound\pcm.h(732): Warning! W202: Symbol 'hw_param_mask_c' has been defined, but not referenced
+..\..\include\sound\pcm.h(738): Warning! W202: Symbol 'hw_param_interval_c' has been defined, but not referenced
+..\..\include\sound\control.h(134): Warning! W202: Symbol 'snd_ctl_build_ioff' has been defined, but not referenced
+	WCC386 -bt=os2v2 -e60 -5r -omlinear -s -w4 -ze -zdp -zl -zq -nt=CODE32 -zff -zgf -zp4 -mf -DKEE -DFLATSTACK -D__KERNEL__ -DMODULE -dTARGET_OS2 -dALSA_BUILD -D__ISAPNP__ -DCONFIG_ISAPNP -D__i386_ -i..\..\..\include;..\..\include;e:\owatcom\H -fobin\Release.KEE\trident_main.obj trident_main.c
+..\..\include\sound\pcm.h(248): Warning! W202: Symbol 'constrs_interval' has been defined, but not referenced
+..\..\include\sound\pcm.h(242): Warning! W202: Symbol 'constrs_mask' has been defined, but not referenced
+..\..\include\sound\pcm.h(738): Warning! W202: Symbol 'hw_param_interval_c' has been defined, but not referenced
+..\..\include\sound\pcm.h(732): Warning! W202: Symbol 'hw_param_mask_c' has been defined, but not referenced
+trident_main.c(3546): Warning! W301: No prototype found for 'pci_request_regions'
+trident_main.c(3663): Warning! W301: No prototype found for 'pci_release_regions'
+	WCC386 -bt=os2v2 -e60 -5r -omlinear -s -w4 -ze -zdp -zl -zq -nt=CODE32 -zff -zgf -zp4 -mf -DKEE -DFLATSTACK -D__KERNEL__ -DMODULE -dTARGET_OS2 -dALSA_BUILD -D__ISAPNP__ -DCONFIG_ISAPNP -D__i386_ -i..\..\..\include;..\..\include;e:\owatcom\H -fobin\Release.KEE\trident_memory.obj trident_memory.c
+..\..\include\sound\pcm.h(248): Warning! W202: Symbol 'constrs_interval' has been defined, but not referenced
+..\..\include\sound\pcm.h(242): Warning! W202: Symbol 'constrs_mask' has been defined, but not referenced
+..\..\include\sound\pcm.h(732): Warning! W202: Symbol 'hw_param_mask_c' has been defined, but not referenced
+..\..\include\sound\pcm.h(738): Warning! W202: Symbol 'hw_param_interval_c' has been defined, but not referenced
+..\..\include\sound\control.h(134): Warning! W202: Symbol 'snd_ctl_build_ioff' has been defined, but not referenced
+	WCC386 -bt=os2v2 -e60 -5r -omlinear -s -w4 -ze -zdp -zl -zq -nt=CODE32 -zff -zgf -zp4 -mf -DKEE -DFLATSTACK -D__KERNEL__ -DMODULE -dTARGET_OS2 -dALSA_BUILD -D__ISAPNP__ -DCONFIG_ISAPNP -D__i386_ -i..\..\..\include;..\..\include;e:\owatcom\H -fobin\Release.KEE\trident_synth.obj trident_synth.c
+..\..\include\sound\pcm.h(248): Warning! W202: Symbol 'constrs_interval' has been defined, but not referenced
+..\..\include\sound\pcm.h(242): Warning! W202: Symbol 'constrs_mask' has been defined, but not referenced
+..\..\include\sound\pcm.h(732): Warning! W202: Symbol 'hw_param_mask_c' has been defined, but not referenced
+..\..\include\sound\pcm.h(738): Warning! W202: Symbol 'hw_param_interval_c' has been defined, but not referenced
+..\..\include\sound\control.h(134): Warning! W202: Symbol 'snd_ctl_build_ioff' has been defined, but not referenced
+	WLIB -s -t -c -b -q -n bin\Release.KEE\trident.lib @bin\Release.KEE\trident.lnk
+	copy bin\Release.KEE\trident.lib E:\DEV\USS\ua2\lib\Release.KEE
+        1 file(s) copied.
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/trident/diff.cmd
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/trident/diff.cmd	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/trident/diff.cmd	(revision 91)
@@ -0,0 +1,1 @@
+diff.exe -Nuwr e:\dev\uss\from_z\ua2\GPL\alsa-kernel\pci\trident\%1 E:\DEV\alsa\alsa-kernel\pci\trident\%1 >%1.diff
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/trident/makefile.os2
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/trident/makefile.os2	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/trident/makefile.os2	(revision 91)
@@ -0,0 +1,51 @@
+CINCLUDES = ..\..\..\include;..\..\include;$(%WATCOM)\H
+AINCLUDES = $(DDK_BASE)\H
+
+CDEFINES = -D__KERNEL__ -DMODULE -dTARGET_OS2 -dALSA_BUILD -D__ISAPNP__ -DCONFIG_ISAPNP -D__i386_
+
+32BIT=1
+!include ..\..\..\..\makefile.inc
+
+
+#===================================================================
+#
+#   List of source files
+#
+#===================================================================
+FILE1    = trident.obj trident_main.obj trident_memory.obj
+FILE2    = trident_synth.obj
+FILE3    = 
+FILE4    = 
+FILE5    = 
+FILE6    = 
+FILE7    = 
+FILE8    = 
+FILELAST =
+FILES    = $(FILE0) $(FILE1) $(FILE2) $(FILE3) $(FILE4) $(FILE5) $(FILE6) $(FILE7) $(FILE8) $(FILE9) $(FILE10) $(FILE11) $(FILE12)
+
+TARGET   = trident
+
+LNKFILE  = $(OBJDIR)\$(TARGET).lnk
+
+
+#===================================================================
+#
+#   Specific dependencies
+#
+#===================================================================
+all: $(OBJDIR)\$(TARGET).lib 
+
+
+$(LNKFILE): makefile.os2
+    @%create $(LNKFILE)
+    @for %f in ($(FILES)) do @%append $(LNKFILE) -+$(OBJDIR)\%f
+
+$(OBJDIR)\$(TARGET).lib: $(LNKFILE) $(FILES)
+    $(LIB) $(OBJDIR)\$(TARGET).lib @$(LNKFILE)
+    copy $(OBJDIR)\$(TARGET).lib $(ALSA_LIB) 
+
+clean: .SYMBOLIC
+    -@rm  *.err 2>nul
+    @cd $(OBJDIR)
+    -rm *.obj *.lnk *.sys *.sym *.lst *.map *.wmap *.lib 2>nul
+    @cd ..\..
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/trident/trident.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/trident/trident.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/trident/trident.c	(revision 91)
@@ -0,0 +1,234 @@
+/*
+ *  Driver for Trident 4DWave DX/NX & SiS SI7018 Audio PCI soundcard
+ *
+ *  Driver was originated by Trident <audio@tridentmicro.com>
+ *  			     Fri Feb 19 15:55:28 MST 1999
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#define SNDRV_MAIN_OBJECT_FILE
+#include <sound/driver.h>
+#include <sound/trident.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+EXPORT_NO_SYMBOLS;
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>, <audio@tridentmicro.com>");
+MODULE_DESCRIPTION("Trident 4D-WaveDX/NX & SiS SI7018");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{Trident,4DWave DX},"
+		"{Trident,4DWave NX},"
+		"{SiS,SI7018 PCI Audio},"
+		"{Best Union,Miss Melody 4DWave PCI},"
+		"{HIS,4DWave PCI},"
+		"{Warpspeed,ONSpeed 4DWave PCI},"
+		"{Aztech Systems,PCI 64-Q3D},"
+		"{Addonics,SV 750},"
+		"{CHIC,True Sound 4Dwave},"
+		"{Shark,Predator4D-PCI},"
+		"{Jaton,SonicWave 4D},"
+		"{Hoontech,SoundTrack Digital 4DWave NX}}");
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE;	/* Enable this card */
+static int pcm_channels[SNDRV_CARDS] = {REPEAT_SNDRV(32)};
+static int wavetable_size[SNDRV_CARDS] = {REPEAT_SNDRV(8192)};
+
+MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(index, "Index value for Trident 4DWave PCI soundcard.");
+MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC);
+MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+MODULE_PARM_DESC(id, "ID string for Trident 4DWave PCI soundcard.");
+MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC);
+MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(enable, "Enable Trident 4DWave PCI soundcard.");
+MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(pcm_channels, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(pcm_channels, "Number of hardware channels assigned for PCM.");
+MODULE_PARM_SYNTAX(pcm_channels, SNDRV_ENABLED ",default:32,allows:{{1,32}}");
+MODULE_PARM(wavetable_size, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(wavetable_size, "Maximum memory size in kB for wavetable synth.");
+MODULE_PARM_SYNTAX(wavetable_size, SNDRV_ENABLED ",default:8192,skill:advanced");
+
+
+static struct pci_device_id snd_trident_ids[] __devinitdata = {
+	{ 0x1023, 0x2000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },	/* Trident 4DWave DX PCI Audio */
+	{ 0x1023, 0x2001, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },	/* Trident 4DWave NX PCI Audio */
+	{ 0x1039, 0x7018, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },	/* SiS SI7018 PCI Audio */
+	{ 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, snd_trident_ids);
+
+static int __devinit snd_trident_probe(struct pci_dev *pci,
+				       const struct pci_device_id *pci_id)
+{
+	static int dev;
+	snd_card_t *card;
+	struct snd_trident *trident;
+	const char *str;
+	int err, pcm_dev = 0;
+
+	if (dev >= SNDRV_CARDS)
+		return -ENODEV;
+	if (!enable[dev]) {
+			dev++;
+			return -ENOENT;
+		}
+
+	card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+	if (card == NULL)
+		return -ENOMEM;
+
+	if ((err = snd_trident_create(card, pci,
+				      pcm_channels[dev],
+				      ((pci->vendor << 16) | pci->device) == TRIDENT_DEVICE_ID_SI7018 ? 1 : 2,
+				      wavetable_size[dev],
+				      &trident)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	switch (trident->device) {
+	case TRIDENT_DEVICE_ID_DX:
+		str = "TRID4DWAVEDX";
+		break;
+	case TRIDENT_DEVICE_ID_NX:
+		str = "TRID4DWAVENX";
+		break;
+	case TRIDENT_DEVICE_ID_SI7018:
+		str = "SI7018";
+		break;
+	default:
+		str = "Unknown";
+	}
+	strcpy(card->driver, str);
+	if (trident->device == TRIDENT_DEVICE_ID_SI7018) {
+		strcpy(card->shortname, "SiS ");
+	} else {
+		strcpy(card->shortname, "Trident ");
+	}
+	strcat(card->shortname, card->driver);
+	sprintf(card->longname, "%s PCI Audio at 0x%lx, irq %d",
+		card->shortname, trident->port, trident->irq);
+
+	if ((err = snd_trident_pcm(trident, pcm_dev++, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	switch (trident->device) {
+	case TRIDENT_DEVICE_ID_DX:
+	case TRIDENT_DEVICE_ID_NX:
+		if ((err = snd_trident_foldback_pcm(trident, pcm_dev++, NULL)) < 0) {
+			snd_card_free(card);
+			return err;
+		}
+		break;
+	}
+	if (trident->device == TRIDENT_DEVICE_ID_NX || trident->device == TRIDENT_DEVICE_ID_SI7018) {
+		if ((err = snd_trident_spdif_pcm(trident, pcm_dev++, NULL)) < 0) {
+			snd_card_free(card);
+			return err;
+		}
+	}
+        if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_TRID4DWAVE,
+                                       trident->midi_port,
+				       MPU401_INFO_INTEGRATED,
+				       trident->irq, 0, &trident->rmidi)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+#ifdef CONFIG_SND_SEQUENCER
+	if ((err = snd_trident_attach_synthesizer(trident)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+#endif
+
+        snd_trident_gameport(trident);
+
+	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_trident_remove(struct pci_dev *pci)
+{
+    snd_card_free(pci_get_drvdata(pci));
+    pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver driver = {
+	0, 0, 0, "Trident4DWaveAudio",
+	snd_trident_ids,
+	snd_trident_probe,
+        snd_trident_remove,
+        SND_PCI_PM_CALLBACKS
+};                                
+
+static int __init alsa_card_trident_init(void)
+{
+	int err;
+
+	if ((err = pci_module_init(&driver)) < 0) {
+#ifdef MODULE
+//		snd_printk("Trident 4DWave PCI soundcard not found or device busy\n");
+#endif
+		return err;
+	}
+	return 0;
+}
+
+static void __exit alsa_card_trident_exit(void)
+{
+	pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_trident_init)
+module_exit(alsa_card_trident_exit)
+
+#ifndef MODULE
+
+/* format is: snd-card-trident=snd_enable,snd_index,snd_id,
+			       snd_pcm_channels,snd_wavetable_size */
+
+static int __init alsa_card_trident_setup(char *str)
+{
+	static unsigned __initdata nr_dev = 0;
+
+	if (nr_dev >= SNDRV_CARDS)
+		return 0;
+	(void)(get_option(&str,&enable[nr_dev]) == 2 &&
+	       get_option(&str,&index[nr_dev]) == 2 &&
+	       get_id(&str,&id[nr_dev]) == 2 &&
+	       get_option(&str,&pcm_channels[nr_dev]) == 2 &&
+	       get_option(&str,&wavetable_size[nr_dev]) == 2);
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-trident=", alsa_card_trident_setup);
+
+#endif /* ifndef MODULE */
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/trident/trident_main.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/trident/trident_main.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/trident/trident_main.c	(revision 91)
@@ -0,0 +1,3995 @@
+/*
+ *  Maintained by Jaroslav Kysela <perex@suse.cz>
+ *  Originated by audio@tridentmicro.com
+ *  Fri Feb 19 15:55:28 MST 1999
+ *  Routines for control of Trident 4DWave (DX and NX) chip
+ *
+ *  BUGS:
+ *
+ *  TODO:
+ *    ---
+ *
+ *   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
+ *
+ *
+ *  SiS7018 S/PDIF support by Thomas Winischhofer <thomas@winischhofer.net>
+ */
+
+#define SNDRV_MAIN_OBJECT_FILE
+#include <sound/driver.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/trident.h>
+
+static int snd_trident_pcm_mixer_build(struct snd_trident *trident, struct snd_trident_voice* voice, snd_pcm_substream_t *substream);
+static int snd_trident_pcm_mixer_free(struct snd_trident *trident, struct snd_trident_voice* voice, snd_pcm_substream_t *substream);
+static irqreturn_t snd_trident_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static int snd_trident_sis_reset(struct snd_trident *trident);
+
+/*
+ *  common I/O routines
+ */
+
+
+#if 0
+static void snd_trident_print_voice_regs(struct snd_trident *trident, int voice)
+{
+    unsigned int val, tmp;
+
+    printk("Trident voice %i:\n", voice);
+    outb(voice, TRID_REG(trident, T4D_LFO_GC_CIR));
+    val = inl(TRID_REG(trident, CH_LBA));
+    printk("LBA: 0x%x\n", val);
+    val = inl(TRID_REG(trident, CH_GVSEL_PAN_VOL_CTRL_EC));
+    printk("GVSel: %i\n", val >> 31);
+    printk("Pan: 0x%x\n", (val >> 24) & 0x7f);
+    printk("Vol: 0x%x\n", (val >> 16) & 0xff);
+    printk("CTRL: 0x%x\n", (val >> 12) & 0x0f);
+    printk("EC: 0x%x\n", val & 0x0fff);
+    if (trident->device != TRIDENT_DEVICE_ID_NX) {
+        val = inl(TRID_REG(trident, CH_DX_CSO_ALPHA_FMS));
+        printk("CSO: 0x%x\n", val >> 16);
+        printk("Alpha: 0x%x\n", (val >> 4) & 0x0fff);
+        printk("FMS: 0x%x\n", val & 0x0f);
+        val = inl(TRID_REG(trident, CH_DX_ESO_DELTA));
+        printk("ESO: 0x%x\n", val >> 16);
+        printk("Delta: 0x%x\n", val & 0xffff);
+        val = inl(TRID_REG(trident, CH_DX_FMC_RVOL_CVOL));
+    } else {		// TRIDENT_DEVICE_ID_NX
+        val = inl(TRID_REG(trident, CH_NX_DELTA_CSO));
+        tmp = (val >> 24) & 0xff;
+        printk("CSO: 0x%x\n", val & 0x00ffffff);
+        val = inl(TRID_REG(trident, CH_NX_DELTA_ESO));
+        tmp |= (val >> 16) & 0xff00;
+        printk("Delta: 0x%x\n", tmp);
+        printk("ESO: 0x%x\n", val & 0x00ffffff);
+        val = inl(TRID_REG(trident, CH_NX_ALPHA_FMS_FMC_RVOL_CVOL));
+        printk("Alpha: 0x%x\n", val >> 20);
+        printk("FMS: 0x%x\n", (val >> 16) & 0x0f);
+    }
+    printk("FMC: 0x%x\n", (val >> 14) & 3);
+    printk("RVol: 0x%x\n", (val >> 7) & 0x7f);
+    printk("CVol: 0x%x\n", val & 0x7f);
+}
+#endif
+
+/*---------------------------------------------------------------------------
+ unsigned short snd_trident_codec_read(ac97_t *ac97, unsigned short reg)
+
+ Description: This routine will do all of the reading from the external
+ CODEC (AC97).
+
+ Parameters:  ac97 - ac97 codec structure
+ reg - CODEC register index, from AC97 Hal.
+
+ returns:     16 bit value read from the AC97.
+
+ ---------------------------------------------------------------------------*/
+static unsigned short snd_trident_codec_read(ac97_t *ac97, unsigned short reg)
+{
+    unsigned int data = 0, treg;
+    unsigned short count = 0xffff;
+    unsigned long flags;
+    struct snd_trident *trident = ac97->private_data;
+
+    spin_lock_irqsave(&trident->reg_lock, flags);
+    if (trident->device == TRIDENT_DEVICE_ID_DX) {
+        data = (DX_AC97_BUSY_READ | (reg & 0x000000ff));
+        outl(data, TRID_REG(trident, DX_ACR1_AC97_R));
+        do {
+            data = inl(TRID_REG(trident, DX_ACR1_AC97_R));
+            if ((data & DX_AC97_BUSY_READ) == 0)
+                break;
+        } while (--count);
+    } else if (trident->device == TRIDENT_DEVICE_ID_NX) {
+        data = (NX_AC97_BUSY_READ | (reg & 0x000000ff));
+        treg = ac97->num == 0 ? NX_ACR2_AC97_R_PRIMARY : NX_ACR3_AC97_R_SECONDARY;
+        outl(data, TRID_REG(trident, treg));
+        do {
+            data = inl(TRID_REG(trident, treg));
+            if ((data & 0x00000C00) == 0)
+                break;
+        } while (--count);
+    } else if (trident->device == TRIDENT_DEVICE_ID_SI7018) {
+        data = SI_AC97_BUSY_READ | SI_AC97_AUDIO_BUSY | (reg & 0x000000ff);
+        if (ac97->num == 1)
+            data |= SI_AC97_SECONDARY;
+        outl(data, TRID_REG(trident, SI_AC97_READ));
+        do {
+            data = inl(TRID_REG(trident, SI_AC97_READ));
+            if ((data & (SI_AC97_BUSY_READ)) == 0)
+                break;
+        } while (--count);
+    }
+
+    if (count == 0 && !trident->ac97_detect) {
+        snd_printk("ac97 codec read TIMEOUT [0x%x/0x%x]!!!\n", reg, data);
+        data = 0;
+    }
+
+    spin_unlock_irqrestore(&trident->reg_lock, flags);
+    return ((unsigned short) (data >> 16));
+}
+
+/*---------------------------------------------------------------------------
+ void snd_trident_codec_write(ac97_t *ac97, unsigned short reg, unsigned short wdata)
+
+ Description: This routine will do all of the writing to the external
+ CODEC (AC97).
+
+ Parameters:	ac97 - ac97 codec structure
+ reg - CODEC register index, from AC97 Hal.
+ data  - Lower 16 bits are the data to write to CODEC.
+
+ returns:     TRUE if everything went ok, else FALSE.
+
+ ---------------------------------------------------------------------------*/
+static void snd_trident_codec_write(ac97_t *ac97, unsigned short reg, unsigned short wdata)
+{
+    unsigned int address, data;
+    unsigned short count = 0xffff;
+    unsigned long flags;
+    struct snd_trident *trident = ac97->private_data;
+
+    data = ((unsigned long) wdata) << 16;
+
+    spin_lock_irqsave(&trident->reg_lock, flags);
+    if (trident->device == TRIDENT_DEVICE_ID_DX) {
+        address = DX_ACR0_AC97_W;
+
+        /* read AC-97 write register status */
+        do {
+            if ((inw(TRID_REG(trident, address)) & DX_AC97_BUSY_WRITE) == 0)
+                break;
+        } while (--count);
+
+        data |= (DX_AC97_BUSY_WRITE | (reg & 0x000000ff));
+    } else if (trident->device == TRIDENT_DEVICE_ID_NX) {
+        address = NX_ACR1_AC97_W;
+
+        /* read AC-97 write register status */
+        do {
+            if ((inw(TRID_REG(trident, address)) & NX_AC97_BUSY_WRITE) == 0)
+                break;
+        } while (--count);
+
+        data |= (NX_AC97_BUSY_WRITE | (ac97->num << 8) | (reg & 0x000000ff));
+    } else if (trident->device == TRIDENT_DEVICE_ID_SI7018) {
+        address = SI_AC97_WRITE;
+
+        /* read AC-97 write register status */
+        do {
+            if ((inw(TRID_REG(trident, address)) & (SI_AC97_BUSY_WRITE)) == 0)
+                break;
+        } while (--count);
+
+        data |= SI_AC97_BUSY_WRITE | SI_AC97_AUDIO_BUSY | (reg & 0x000000ff);
+        if (ac97->num == 1)
+            data |= SI_AC97_SECONDARY;
+    } else {
+        address = 0;	/* keep GCC happy */
+        count = 0;	/* return */
+    }
+
+    if (count == 0) {
+        spin_unlock_irqrestore(&trident->reg_lock, flags);
+        return;
+    }
+    outl(data, TRID_REG(trident, address));
+    spin_unlock_irqrestore(&trident->reg_lock, flags);
+}
+
+/*---------------------------------------------------------------------------
+ void snd_trident_enable_eso(struct snd_trident *trident)
+
+ Description: This routine will enable end of loop interrupts.
+ End of loop interrupts will occur when a running
+ channel reaches ESO.
+ Also enables middle of loop interrupts.
+
+ Parameters:  trident - pointer to target device class for 4DWave.
+
+ ---------------------------------------------------------------------------*/
+
+static void snd_trident_enable_eso(struct snd_trident * trident)
+{
+    unsigned int val;
+
+    val = inl(TRID_REG(trident, T4D_LFO_GC_CIR));
+    val |= ENDLP_IE;
+    val |= MIDLP_IE;
+    if (trident->device == TRIDENT_DEVICE_ID_SI7018)
+        val |= BANK_B_EN;
+    outl(val, TRID_REG(trident, T4D_LFO_GC_CIR));
+}
+
+/*---------------------------------------------------------------------------
+ void snd_trident_disable_eso(struct snd_trident *trident)
+
+ Description: This routine will disable end of loop interrupts.
+ End of loop interrupts will occur when a running
+ channel reaches ESO.
+ Also disables middle of loop interrupts.
+
+ Parameters:
+ trident - pointer to target device class for 4DWave.
+
+ returns:     TRUE if everything went ok, else FALSE.
+
+ ---------------------------------------------------------------------------*/
+
+static void snd_trident_disable_eso(struct snd_trident * trident)
+{
+    unsigned int tmp;
+
+    tmp = inl(TRID_REG(trident, T4D_LFO_GC_CIR));
+    tmp &= ~ENDLP_IE;
+    tmp &= ~MIDLP_IE;
+    outl(tmp, TRID_REG(trident, T4D_LFO_GC_CIR));
+}
+
+/*---------------------------------------------------------------------------
+ void snd_trident_start_voice(struct snd_trident * trident, unsigned int voice)
+
+ Description: Start a voice, any channel 0 thru 63.
+ This routine automatically handles the fact that there are
+ more than 32 channels available.
+
+ Parameters : voice - Voice number 0 thru n.
+ trident - pointer to target device class for 4DWave.
+
+ Return Value: None.
+
+ ---------------------------------------------------------------------------*/
+
+void snd_trident_start_voice(struct snd_trident * trident, unsigned int voice)
+{
+    unsigned int mask = 1 << (voice & 0x1f);
+    unsigned int reg = (voice & 0x20) ? T4D_START_B : T4D_START_A;
+
+    outl(mask, TRID_REG(trident, reg));
+}
+
+/*---------------------------------------------------------------------------
+ void snd_trident_stop_voice(struct snd_trident * trident, unsigned int voice)
+
+ Description: Stop a voice, any channel 0 thru 63.
+ This routine automatically handles the fact that there are
+ more than 32 channels available.
+
+ Parameters : voice - Voice number 0 thru n.
+ trident - pointer to target device class for 4DWave.
+
+ Return Value: None.
+
+ ---------------------------------------------------------------------------*/
+
+void snd_trident_stop_voice(struct snd_trident * trident, unsigned int voice)
+{
+    unsigned int mask = 1 << (voice & 0x1f);
+    unsigned int reg = (voice & 0x20) ? T4D_STOP_B : T4D_STOP_A;
+
+    outl(mask, TRID_REG(trident, reg));
+}
+
+/*---------------------------------------------------------------------------
+ int snd_trident_allocate_pcm_channel(struct snd_trident *trident)
+
+ Description: Allocate hardware channel in Bank B (32-63).
+
+ Parameters :  trident - pointer to target device class for 4DWave.
+
+ Return Value: hardware channel - 32-63 or -1 when no channel is available
+
+ ---------------------------------------------------------------------------*/
+
+static int snd_trident_allocate_pcm_channel(struct snd_trident * trident)
+{
+    int idx;
+
+    if (trident->ChanPCMcnt >= trident->ChanPCM)
+        return -1;
+    for (idx = 31; idx >= 0; idx--) {
+        if (!(trident->ChanMap[T4D_BANK_B] & (1 << idx))) {
+            trident->ChanMap[T4D_BANK_B] |= 1 << idx;
+            trident->ChanPCMcnt++;
+            return idx + 32;
+        }
+    }
+    return -1;
+}
+
+/*---------------------------------------------------------------------------
+ void snd_trident_free_pcm_channel(int channel)
+
+ Description: Free hardware channel in Bank B (32-63)
+
+ Parameters :  trident - pointer to target device class for 4DWave.
+ channel - hardware channel number 0-63
+
+ Return Value: none
+
+ ---------------------------------------------------------------------------*/
+
+static void snd_trident_free_pcm_channel(struct snd_trident *trident, int channel)
+{
+    if (channel < 32 || channel > 63)
+        return;
+    channel &= 0x1f;
+    if (trident->ChanMap[T4D_BANK_B] & (1 << channel)) {
+        trident->ChanMap[T4D_BANK_B] &= ~(1 << channel);
+        trident->ChanPCMcnt--;
+    }
+}
+
+/*---------------------------------------------------------------------------
+ unsigned int snd_trident_allocate_synth_channel(void)
+
+ Description: Allocate hardware channel in Bank A (0-31).
+
+ Parameters :  trident - pointer to target device class for 4DWave.
+
+ Return Value: hardware channel - 0-31 or -1 when no channel is available
+
+ ---------------------------------------------------------------------------*/
+
+static int snd_trident_allocate_synth_channel(struct snd_trident * trident)
+{
+    int idx;
+
+    for (idx = 31; idx >= 0; idx--) {
+        if (!(trident->ChanMap[T4D_BANK_A] & (1 << idx))) {
+            trident->ChanMap[T4D_BANK_A] |= 1 << idx;
+            trident->synth.ChanSynthCount++;
+            return idx;
+        }
+    }
+    return -1;
+}
+
+/*---------------------------------------------------------------------------
+ void snd_trident_free_synth_channel( int channel )
+
+ Description: Free hardware channel in Bank B (0-31).
+
+ Parameters :  trident - pointer to target device class for 4DWave.
+ channel - hardware channel number 0-63
+
+ Return Value: none
+
+ ---------------------------------------------------------------------------*/
+
+static void snd_trident_free_synth_channel(struct snd_trident *trident, int channel)
+{
+    if (channel < 0 || channel > 31)
+        return;
+    channel &= 0x1f;
+    if (trident->ChanMap[T4D_BANK_A] & (1 << channel)) {
+        trident->ChanMap[T4D_BANK_A] &= ~(1 << channel);
+        trident->synth.ChanSynthCount--;
+    }
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_write_voice_regs
+
+ Description: This routine will complete and write the 5 hardware channel
+ registers to hardware.
+
+ Paramters:   trident - pointer to target device class for 4DWave.
+ voice - synthesizer voice structure
+ Each register field.
+
+ ---------------------------------------------------------------------------*/
+
+void snd_trident_write_voice_regs(struct snd_trident * trident,
+                                  struct snd_trident_voice* voice)
+{
+    unsigned int FmcRvolCvol;
+    unsigned int regs[5];
+
+    regs[1] = voice->LBA;
+    regs[4] = (voice->GVSel << 31) |
+        ((voice->Pan & 0x0000007f) << 24) |
+        ((voice->CTRL & 0x0000000f) << 12);
+    FmcRvolCvol = ((voice->FMC & 3) << 14) |
+        ((voice->RVol & 0x7f) << 7) |
+        (voice->CVol & 0x7f);
+
+    switch (trident->device) {
+    case TRIDENT_DEVICE_ID_SI7018:
+        regs[4] |= voice->number > 31 ?
+            (voice->Vol & 0x000003ff) :
+            ((voice->Vol & 0x00003fc) << (16-2)) |
+            (voice->EC & 0x00000fff);
+        regs[0] = (voice->CSO << 16) | ((voice->Alpha & 0x00000fff) << 4) | (voice->FMS & 0x0000000f);
+        regs[2] = (voice->ESO << 16) | (voice->Delta & 0x0ffff);
+        regs[3] = (voice->Attribute << 16) | FmcRvolCvol;
+        break;
+    case TRIDENT_DEVICE_ID_DX:
+        regs[4] |= ((voice->Vol & 0x000003fc) << (16-2)) |
+            (voice->EC & 0x00000fff);
+        regs[0] = (voice->CSO << 16) | ((voice->Alpha & 0x00000fff) << 4) | (voice->FMS & 0x0000000f);
+        regs[2] = (voice->ESO << 16) | (voice->Delta & 0x0ffff);
+        regs[3] = FmcRvolCvol;
+        break;
+    case TRIDENT_DEVICE_ID_NX:
+        regs[4] |= ((voice->Vol & 0x000003fc) << (16-2)) |
+            (voice->EC & 0x00000fff);
+        regs[0] = (voice->Delta << 24) | (voice->CSO & 0x00ffffff);
+        regs[2] = ((voice->Delta << 16) & 0xff000000) | (voice->ESO & 0x00ffffff);
+        regs[3] = (voice->Alpha << 20) | ((voice->FMS & 0x0000000f) << 16) | FmcRvolCvol;
+        break;
+    default:
+        snd_BUG();
+        return;
+    }
+
+    outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
+    outl(regs[0], TRID_REG(trident, CH_START + 0));
+    outl(regs[1], TRID_REG(trident, CH_START + 4));
+    outl(regs[2], TRID_REG(trident, CH_START + 8));
+    outl(regs[3], TRID_REG(trident, CH_START + 12));
+    outl(regs[4], TRID_REG(trident, CH_START + 16));
+
+#if 0
+    printk("written %i channel:\n", voice->number);
+    printk("  regs[0] = 0x%x/0x%x\n", regs[0], inl(TRID_REG(trident, CH_START + 0)));
+    printk("  regs[1] = 0x%x/0x%x\n", regs[1], inl(TRID_REG(trident, CH_START + 4)));
+    printk("  regs[2] = 0x%x/0x%x\n", regs[2], inl(TRID_REG(trident, CH_START + 8)));
+    printk("  regs[3] = 0x%x/0x%x\n", regs[3], inl(TRID_REG(trident, CH_START + 12)));
+    printk("  regs[4] = 0x%x/0x%x\n", regs[4], inl(TRID_REG(trident, CH_START + 16)));
+#endif
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_write_cso_reg
+
+ Description: This routine will write the new CSO offset
+ register to hardware.
+
+ Paramters:   trident - pointer to target device class for 4DWave.
+ voice - synthesizer voice structure
+ CSO - new CSO value
+
+ ---------------------------------------------------------------------------*/
+
+static void snd_trident_write_cso_reg(struct snd_trident * trident, struct snd_trident_voice* voice, unsigned int CSO)
+{
+    voice->CSO = CSO;
+    outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
+    if (trident->device != TRIDENT_DEVICE_ID_NX) {
+        outw(voice->CSO, TRID_REG(trident, CH_DX_CSO_ALPHA_FMS) + 2);
+    } else {
+        outl((voice->Delta << 24) | (voice->CSO & 0x00ffffff), TRID_REG(trident, CH_NX_DELTA_CSO));
+    }
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_write_eso_reg
+
+ Description: This routine will write the new ESO offset
+ register to hardware.
+
+ Paramters:   trident - pointer to target device class for 4DWave.
+ voice - synthesizer voice structure
+ ESO - new ESO value
+
+ ---------------------------------------------------------------------------*/
+
+static void snd_trident_write_eso_reg(struct snd_trident * trident, struct snd_trident_voice* voice, unsigned int ESO)
+{
+    voice->ESO = ESO;
+    outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
+    if (trident->device != TRIDENT_DEVICE_ID_NX) {
+        outw(voice->ESO, TRID_REG(trident, CH_DX_ESO_DELTA) + 2);
+    } else {
+        outl(((voice->Delta << 16) & 0xff000000) | (voice->ESO & 0x00ffffff), TRID_REG(trident, CH_NX_DELTA_ESO));
+    }
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_write_vol_reg
+
+ Description: This routine will write the new voice volume
+ register to hardware.
+
+ Paramters:   trident - pointer to target device class for 4DWave.
+ voice - synthesizer voice structure
+ Vol - new voice volume
+
+ ---------------------------------------------------------------------------*/
+
+static void snd_trident_write_vol_reg(struct snd_trident * trident, struct snd_trident_voice* voice, unsigned int Vol)
+{
+    voice->Vol = Vol;
+    outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
+    switch (trident->device) {
+    case TRIDENT_DEVICE_ID_DX:
+    case TRIDENT_DEVICE_ID_NX:
+        outb(voice->Vol >> 2, TRID_REG(trident, CH_GVSEL_PAN_VOL_CTRL_EC + 2));
+        break;
+    case TRIDENT_DEVICE_ID_SI7018:
+        //		printk("voice->Vol = 0x%x\n", voice->Vol);
+        outw((voice->CTRL << 12) | voice->Vol, TRID_REG(trident, CH_GVSEL_PAN_VOL_CTRL_EC));
+        break;
+    }
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_write_pan_reg
+
+ Description: This routine will write the new voice pan
+ register to hardware.
+
+ Paramters:   trident - pointer to target device class for 4DWave.
+ voice - synthesizer voice structure
+ Pan - new pan value
+
+ ---------------------------------------------------------------------------*/
+
+static void snd_trident_write_pan_reg(struct snd_trident * trident, struct snd_trident_voice* voice, unsigned int Pan)
+{
+    voice->Pan = Pan;
+    outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
+    outb(((voice->GVSel & 0x01) << 7) | (voice->Pan & 0x7f), TRID_REG(trident, CH_GVSEL_PAN_VOL_CTRL_EC + 3));
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_write_rvol_reg
+
+ Description: This routine will write the new reverb volume
+ register to hardware.
+
+ Paramters:   trident - pointer to target device class for 4DWave.
+ voice - synthesizer voice structure
+ RVol - new reverb volume
+
+ ---------------------------------------------------------------------------*/
+
+static void snd_trident_write_rvol_reg(struct snd_trident * trident, struct snd_trident_voice* voice, unsigned int RVol)
+{
+    voice->RVol = RVol;
+    outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
+    outw(((voice->FMC & 0x0003) << 14) | ((voice->RVol & 0x007f) << 7) | (voice->CVol & 0x007f),
+         TRID_REG(trident, trident->device == TRIDENT_DEVICE_ID_NX ? CH_NX_ALPHA_FMS_FMC_RVOL_CVOL : CH_DX_FMC_RVOL_CVOL));
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_write_cvol_reg
+
+ Description: This routine will write the new chorus volume
+ register to hardware.
+
+ Paramters:   trident - pointer to target device class for 4DWave.
+ voice - synthesizer voice structure
+ CVol - new chorus volume
+
+ ---------------------------------------------------------------------------*/
+
+static void snd_trident_write_cvol_reg(struct snd_trident * trident, struct snd_trident_voice* voice, unsigned int CVol)
+{
+    voice->CVol = CVol;
+    outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
+    outw(((voice->FMC & 0x0003) << 14) | ((voice->RVol & 0x007f) << 7) | (voice->CVol & 0x007f),
+         TRID_REG(trident, trident->device == TRIDENT_DEVICE_ID_NX ? CH_NX_ALPHA_FMS_FMC_RVOL_CVOL : CH_DX_FMC_RVOL_CVOL));
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_convert_rate
+
+ Description: This routine converts rate in HZ to hardware delta value.
+
+ Paramters:   trident - pointer to target device class for 4DWave.
+ rate - Real or Virtual channel number.
+
+ Returns:     Delta value.
+
+ ---------------------------------------------------------------------------*/
+unsigned int snd_trident_convert_rate(unsigned int rate)
+{
+    unsigned int delta;
+
+    // We special case 44100 and 8000 since rounding with the equation
+    // does not give us an accurate enough value. For 11025 and 22050
+    // the equation gives us the best answer. All other frequencies will
+    // also use the equation. JDW
+    if (rate == 44100)
+        delta = 0xeb3;
+    else if (rate == 8000)
+        delta = 0x2ab;
+    else if (rate == 48000)
+        delta = 0x1000;
+    else
+        delta = (((rate << 12) + 24000) / 48000) & 0x0000ffff;
+    return delta;
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_convert_adc_rate
+
+ Description: This routine converts rate in HZ to hardware delta value.
+
+ Paramters:   trident - pointer to target device class for 4DWave.
+ rate - Real or Virtual channel number.
+
+ Returns:     Delta value.
+
+ ---------------------------------------------------------------------------*/
+static unsigned int snd_trident_convert_adc_rate(unsigned int rate)
+{
+    unsigned int delta;
+
+    // We special case 44100 and 8000 since rounding with the equation
+    // does not give us an accurate enough value. For 11025 and 22050
+    // the equation gives us the best answer. All other frequencies will
+    // also use the equation. JDW
+    if (rate == 44100)
+        delta = 0x116a;
+    else if (rate == 8000)
+        delta = 0x6000;
+    else if (rate == 48000)
+        delta = 0x1000;
+    else
+        delta = ((48000 << 12) / rate) & 0x0000ffff;
+    return delta;
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_spurious_threshold
+
+ Description: This routine converts rate in HZ to spurious threshold.
+
+ Paramters:   trident - pointer to target device class for 4DWave.
+ rate - Real or Virtual channel number.
+
+ Returns:     Delta value.
+
+ ---------------------------------------------------------------------------*/
+unsigned int snd_trident_spurious_threshold(unsigned int rate, unsigned int period_size)
+{
+    unsigned int res = (rate * period_size) / 48000;
+    if (res < 64)
+        res = res / 2;
+    else
+        res -= 32;
+    return res;
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_control_mode
+
+ Description: This routine returns a control mode for a PCM channel.
+
+ Paramters:   trident - pointer to target device class for 4DWave.
+ substream  - PCM substream
+
+ Returns:     Control value.
+
+ ---------------------------------------------------------------------------*/
+unsigned int snd_trident_control_mode(snd_pcm_substream_t *substream)
+{
+    unsigned int CTRL;
+    snd_pcm_runtime_t *runtime = substream->runtime;
+
+    /* set ctrl mode
+     CTRL default: 8-bit (unsigned) mono, loop mode enabled
+     */
+    CTRL = 0x00000001;
+    if (snd_pcm_format_width(runtime->format) == 16)
+        CTRL |= 0x00000008;	// 16-bit data
+    if (snd_pcm_format_signed(runtime->format))
+        CTRL |= 0x00000002;	// signed data
+    if (runtime->channels > 1)
+        CTRL |= 0x00000004;	// stereo data
+    return CTRL;
+}
+
+/*
+ *  PCM part
+ */
+
+/*---------------------------------------------------------------------------
+ snd_trident_ioctl
+
+ Description: Device I/O control handler for playback/capture parameters.
+
+ Paramters:   substream  - PCM substream class
+ cmd     - what ioctl message to process
+ arg     - additional message infoarg
+
+ Returns:     Error status
+
+ ---------------------------------------------------------------------------*/
+
+static int snd_trident_ioctl(snd_pcm_substream_t * substream,
+                             unsigned int cmd,
+                             void *arg)
+{
+    /* FIXME: it seems that with small periods the behaviour of
+     trident hardware is unpredictable and interrupt generator
+     is broken */
+    return snd_pcm_lib_ioctl(substream, cmd, arg);
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_allocate_pcm_mem
+
+ Description: Allocate PCM ring buffer for given substream
+
+ Parameters:  substream  - PCM substream class
+ hw_params  - hardware parameters
+
+ Returns:     Error status
+
+ ---------------------------------------------------------------------------*/
+
+int snd_trident_allocate_pcm_mem(snd_pcm_substream_t * substream,
+                                 snd_pcm_hw_params_t * hw_params)
+{
+    struct snd_trident *trident = snd_pcm_substream_chip(substream);
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    struct snd_trident_voice*voice = (struct snd_trident_voice*) runtime->private_data;
+    int err;
+
+    if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
+        return err;
+    if (trident->tlb.entries) {
+        if (err > 0) { /* change */
+            if (voice->memblk)
+                snd_trident_free_pages(trident, voice->memblk);
+            voice->memblk = snd_trident_alloc_pages(trident, substream);
+            if (voice->memblk == NULL)
+                return -ENOMEM;
+        }
+    }
+    return 0;
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_allocate_evoice
+
+ Description: Allocate extra voice as interrupt generator
+
+ Parameters:  substream  - PCM substream class
+ hw_params  - hardware parameters
+
+ Returns:     Error status
+
+ ---------------------------------------------------------------------------*/
+
+int snd_trident_allocate_evoice(snd_pcm_substream_t * substream,
+                                snd_pcm_hw_params_t * hw_params)
+{
+    struct snd_trident *trident = snd_pcm_substream_chip(substream);
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    struct snd_trident_voice*voice = (struct snd_trident_voice*) runtime->private_data;
+    struct snd_trident_voice*evoice = voice->extra;
+
+    /* voice management */
+
+    if (params_buffer_size(hw_params) / 2 != params_period_size(hw_params)) {
+        if (evoice == NULL) {
+            evoice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0);
+            if (evoice == NULL)
+                return -ENOMEM;
+            voice->extra = evoice;
+            evoice->substream = substream;
+        }
+    } else {
+        if (evoice != NULL) {
+            snd_trident_free_voice(trident, evoice);
+            voice->extra = evoice = NULL;
+        }
+    }
+
+    return 0;
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_hw_params
+
+ Description: Set the hardware parameters for the playback device.
+
+ Parameters:  substream  - PCM substream class
+ hw_params  - hardware parameters
+
+ Returns:     Error status
+
+ ---------------------------------------------------------------------------*/
+
+static int snd_trident_hw_params(snd_pcm_substream_t * substream,
+                                 snd_pcm_hw_params_t * hw_params)
+{
+    int err;
+
+    err = snd_trident_allocate_pcm_mem(substream, hw_params);
+    if (err >= 0)
+        err = snd_trident_allocate_evoice(substream, hw_params);
+    return err;
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_playback_hw_free
+
+ Description: Release the hardware resources for the playback device.
+
+ Parameters:  substream  - PCM substream class
+
+ Returns:     Error status
+
+ ---------------------------------------------------------------------------*/
+
+static int snd_trident_hw_free(snd_pcm_substream_t * substream)
+{
+    struct snd_trident *trident = snd_pcm_substream_chip(substream);
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    struct snd_trident_voice*voice = (struct snd_trident_voice*) runtime->private_data;
+    struct snd_trident_voice*evoice = voice ? voice->extra : NULL;
+
+    if (trident->tlb.entries) {
+        if (voice && voice->memblk) {
+            snd_trident_free_pages(trident, voice->memblk);
+            voice->memblk = NULL;
+        }
+    }
+    snd_pcm_lib_free_pages(substream);
+    if (evoice != NULL) {
+        snd_trident_free_voice(trident, evoice);
+        voice->extra = NULL;
+    }
+    return 0;
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_playback_prepare
+
+ Description: Prepare playback device for playback.
+
+ Parameters:  substream  - PCM substream class
+
+ Returns:     Error status
+
+ ---------------------------------------------------------------------------*/
+
+static int snd_trident_playback_prepare(snd_pcm_substream_t * substream)
+{
+    struct snd_trident *trident = snd_pcm_substream_chip(substream);
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    struct snd_trident_voice*voice = (struct snd_trident_voice*) runtime->private_data;
+    struct snd_trident_voice*evoice = voice->extra;
+    struct snd_trident_pcm_mixer*mix = &trident->pcm_mixer[substream->number];
+
+    spin_lock_irq(&trident->reg_lock);
+
+    /* set delta (rate) value */
+    voice->Delta = snd_trident_convert_rate(runtime->rate);
+    voice->spurious_threshold = snd_trident_spurious_threshold(runtime->rate, runtime->period_size);
+
+    /* set Loop Begin Address */
+    if (voice->memblk)
+        voice->LBA = voice->memblk->offset;
+    else
+        voice->LBA = runtime->dma_addr;
+
+    voice->CSO = 0;
+    voice->ESO = runtime->buffer_size - 1;	/* in samples */
+    voice->CTRL = snd_trident_control_mode(substream);
+    voice->FMC = 3;
+    voice->GVSel = 1;
+    voice->EC = 0;
+    voice->Alpha = 0;
+    voice->FMS = 0;
+    voice->Vol = mix->vol;
+    voice->RVol = mix->rvol;
+    voice->CVol = mix->cvol;
+    voice->Pan = mix->pan;
+    voice->Attribute = 0;
+#if 0
+    voice->Attribute = (1<<(30-16))|(2<<(26-16))|
+        (0<<(24-16))|(0x1f<<(19-16));
+#else
+    voice->Attribute = 0;
+#endif
+
+    snd_trident_write_voice_regs(trident, voice);
+
+    if (evoice != NULL) {
+        evoice->Delta = voice->Delta;
+        evoice->spurious_threshold = voice->spurious_threshold;
+        evoice->LBA = voice->LBA;
+        evoice->CSO = 0;
+        evoice->ESO = (runtime->period_size * 2) + 4 - 1; /* in samples */
+        evoice->CTRL = voice->CTRL;
+        evoice->FMC = 3;
+        evoice->GVSel = trident->device == TRIDENT_DEVICE_ID_SI7018 ? 0 : 1;
+        evoice->EC = 0;
+        evoice->Alpha = 0;
+        evoice->FMS = 0;
+        evoice->Vol = 0x3ff;			/* mute */
+        evoice->RVol = evoice->CVol = 0x7f;	/* mute */
+        evoice->Pan = 0x7f;			/* mute */
+#if 0
+        evoice->Attribute = (1<<(30-16))|(2<<(26-16))|
+            (0<<(24-16))|(0x1f<<(19-16));
+#else
+        evoice->Attribute = 0;
+#endif
+        snd_trident_write_voice_regs(trident, evoice);
+        evoice->isync2 = 1;
+        evoice->isync_mark = runtime->period_size;
+        evoice->ESO = (runtime->period_size * 2) - 1;
+    }
+
+    spin_unlock_irq(&trident->reg_lock);
+
+    return 0;
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_capture_hw_params
+
+ Description: Set the hardware parameters for the capture device.
+
+ Parameters:  substream  - PCM substream class
+ hw_params  - hardware parameters
+
+ Returns:     Error status
+
+ ---------------------------------------------------------------------------*/
+
+static int snd_trident_capture_hw_params(snd_pcm_substream_t * substream,
+                                         snd_pcm_hw_params_t * hw_params)
+{
+    return snd_trident_allocate_pcm_mem(substream, hw_params);
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_capture_prepare
+
+ Description: Prepare capture device for playback.
+
+ Parameters:  substream  - PCM substream class
+
+ Returns:     Error status
+
+ ---------------------------------------------------------------------------*/
+
+static int snd_trident_capture_prepare(snd_pcm_substream_t * substream)
+{
+    struct snd_trident *trident = snd_pcm_substream_chip(substream);
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    struct snd_trident_voice*voice = (struct snd_trident_voice*) runtime->private_data;
+    unsigned int val, ESO_bytes;
+
+    spin_lock_irq(&trident->reg_lock);
+
+    // Initilize the channel and set channel Mode
+    outb(0, TRID_REG(trident, LEGACY_DMAR15));
+
+    // Set DMA channel operation mode register
+    outb(0x54, TRID_REG(trident, LEGACY_DMAR11));
+
+    // Set channel buffer Address, DMAR0 expects contiguous PCI memory area
+    voice->LBA = runtime->dma_addr;
+    outl(voice->LBA, TRID_REG(trident, LEGACY_DMAR0));
+    if (voice->memblk)
+        voice->LBA = voice->memblk->offset;
+
+    // set ESO
+    ESO_bytes = snd_pcm_lib_buffer_bytes(substream) - 1;
+    outb((ESO_bytes & 0x00ff0000) >> 16, TRID_REG(trident, LEGACY_DMAR6));
+    outw((ESO_bytes & 0x0000ffff), TRID_REG(trident, LEGACY_DMAR4));
+    ESO_bytes++;
+
+    // Set channel sample rate, 4.12 format
+    val = (((unsigned int) 48000L << 12) + (runtime->rate/2)) / runtime->rate;
+    outw(val, TRID_REG(trident, T4D_SBDELTA_DELTA_R));
+
+    // Set channel interrupt blk length
+    if (snd_pcm_format_width(runtime->format) == 16) {
+        val = (unsigned short) ((ESO_bytes >> 1) - 1);
+    } else {
+        val = (unsigned short) (ESO_bytes - 1);
+    }
+
+    outl((val << 16) | val, TRID_REG(trident, T4D_SBBL_SBCL));
+
+    // Right now, set format and start to run captureing,
+    // continuous run loop enable.
+    trident->bDMAStart = 0x19;	// 0001 1001b
+
+    if (snd_pcm_format_width(runtime->format) == 16)
+        trident->bDMAStart |= 0x80;
+    if (snd_pcm_format_signed(runtime->format))
+        trident->bDMAStart |= 0x20;
+    if (runtime->channels > 1)
+        trident->bDMAStart |= 0x40;
+
+    // Prepare capture intr channel
+
+    voice->Delta = snd_trident_convert_rate(runtime->rate);
+    voice->spurious_threshold = snd_trident_spurious_threshold(runtime->rate, runtime->period_size);
+    voice->isync = 1;
+    voice->isync_mark = runtime->period_size;
+    voice->isync_max = runtime->buffer_size;
+
+    // Set voice parameters
+    voice->CSO = 0;
+    voice->ESO = voice->isync_ESO = (runtime->period_size * 2) + 6 - 1;
+    voice->CTRL = snd_trident_control_mode(substream);
+    voice->FMC = 3;
+    voice->RVol = 0x7f;
+    voice->CVol = 0x7f;
+    voice->GVSel = 1;
+    voice->Pan = 0x7f;		/* mute */
+    voice->Vol = 0x3ff;		/* mute */
+    voice->EC = 0;
+    voice->Alpha = 0;
+    voice->FMS = 0;
+    voice->Attribute = 0;
+
+    snd_trident_write_voice_regs(trident, voice);
+
+    spin_unlock_irq(&trident->reg_lock);
+    return 0;
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_si7018_capture_hw_params
+
+ Description: Set the hardware parameters for the capture device.
+
+ Parameters:  substream  - PCM substream class
+ hw_params  - hardware parameters
+
+ Returns:     Error status
+
+ ---------------------------------------------------------------------------*/
+
+static int snd_trident_si7018_capture_hw_params(snd_pcm_substream_t * substream,
+                                                snd_pcm_hw_params_t * hw_params)
+{
+    int err;
+
+    if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
+        return err;
+
+    return snd_trident_allocate_evoice(substream, hw_params);
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_si7018_capture_hw_free
+
+ Description: Release the hardware resources for the capture device.
+
+ Parameters:  substream  - PCM substream class
+
+ Returns:     Error status
+
+ ---------------------------------------------------------------------------*/
+
+static int snd_trident_si7018_capture_hw_free(snd_pcm_substream_t * substream)
+{
+    struct snd_trident *trident = snd_pcm_substream_chip(substream);
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    struct snd_trident_voice*voice = (struct snd_trident_voice*) runtime->private_data;
+    struct snd_trident_voice*evoice = voice ? voice->extra : NULL;
+
+    snd_pcm_lib_free_pages(substream);
+    if (evoice != NULL) {
+        snd_trident_free_voice(trident, evoice);
+        voice->extra = NULL;
+    }
+    return 0;
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_si7018_capture_prepare
+
+ Description: Prepare capture device for playback.
+
+ Parameters:  substream  - PCM substream class
+
+ Returns:     Error status
+
+ ---------------------------------------------------------------------------*/
+
+static int snd_trident_si7018_capture_prepare(snd_pcm_substream_t * substream)
+{
+    struct snd_trident *trident = snd_pcm_substream_chip(substream);
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    struct snd_trident_voice*voice = (struct snd_trident_voice*) runtime->private_data;
+    struct snd_trident_voice*evoice = voice->extra;
+
+    spin_lock_irq(&trident->reg_lock);
+
+    voice->LBA = runtime->dma_addr;
+    voice->Delta = snd_trident_convert_adc_rate(runtime->rate);
+    voice->spurious_threshold = snd_trident_spurious_threshold(runtime->rate, runtime->period_size);
+
+    // Set voice parameters
+    voice->CSO = 0;
+    voice->ESO = runtime->buffer_size - 1;		/* in samples */
+    voice->CTRL = snd_trident_control_mode(substream);
+    voice->FMC = 0;
+    voice->RVol = 0;
+    voice->CVol = 0;
+    voice->GVSel = 1;
+    voice->Pan = T4D_DEFAULT_PCM_PAN;
+    voice->Vol = 0;
+    voice->EC = 0;
+    voice->Alpha = 0;
+    voice->FMS = 0;
+
+    voice->Attribute = (2 << (30-16)) |
+        (2 << (26-16)) |
+        (2 << (24-16)) |
+        (1 << (23-16));
+
+    snd_trident_write_voice_regs(trident, voice);
+
+    if (evoice != NULL) {
+        evoice->Delta = snd_trident_convert_rate(runtime->rate);
+        evoice->spurious_threshold = voice->spurious_threshold;
+        evoice->LBA = voice->LBA;
+        evoice->CSO = 0;
+        evoice->ESO = (runtime->period_size * 2) + 20 - 1; /* in samples, 20 means correction */
+        evoice->CTRL = voice->CTRL;
+        evoice->FMC = 3;
+        evoice->GVSel = 0;
+        evoice->EC = 0;
+        evoice->Alpha = 0;
+        evoice->FMS = 0;
+        evoice->Vol = 0x3ff;			/* mute */
+        evoice->RVol = evoice->CVol = 0x7f;	/* mute */
+        evoice->Pan = 0x7f;			/* mute */
+        evoice->Attribute = 0;
+        snd_trident_write_voice_regs(trident, evoice);
+        evoice->isync2 = 1;
+        evoice->isync_mark = runtime->period_size;
+        evoice->ESO = (runtime->period_size * 2) - 1;
+    }
+    spin_unlock_irq(&trident->reg_lock);
+    return 0;
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_foldback_prepare
+
+ Description: Prepare foldback capture device for playback.
+
+ Parameters:  substream  - PCM substream class
+
+ Returns:     Error status
+
+ ---------------------------------------------------------------------------*/
+
+static int snd_trident_foldback_prepare(snd_pcm_substream_t * substream)
+{
+    struct snd_trident *trident = snd_pcm_substream_chip(substream);
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    struct snd_trident_voice*voice = (struct snd_trident_voice*) runtime->private_data;
+    struct snd_trident_voice*evoice = voice->extra;
+
+    spin_lock_irq(&trident->reg_lock);
+
+    /* Set channel buffer Address */
+    if (voice->memblk)
+        voice->LBA = voice->memblk->offset;
+    else
+        voice->LBA = runtime->dma_addr;
+
+    /* set target ESO for channel */
+    voice->ESO = runtime->buffer_size - 1;	/* in samples */
+
+    /* set sample rate */
+    voice->Delta = 0x1000;
+    voice->spurious_threshold = snd_trident_spurious_threshold(48000, runtime->period_size);
+
+    voice->CSO = 0;
+    voice->CTRL = snd_trident_control_mode(substream);
+    voice->FMC = 3;
+    voice->RVol = 0x7f;
+    voice->CVol = 0x7f;
+    voice->GVSel = 1;
+    voice->Pan = 0x7f;	/* mute */
+    voice->Vol = 0x3ff;	/* mute */
+    voice->EC = 0;
+    voice->Alpha = 0;
+    voice->FMS = 0;
+    voice->Attribute = 0;
+
+    /* set up capture channel */
+    outb(((voice->number & 0x3f) | 0x80), TRID_REG(trident, T4D_RCI + voice->foldback_chan));
+
+    snd_trident_write_voice_regs(trident, voice);
+
+    if (evoice != NULL) {
+        evoice->Delta = voice->Delta;
+        evoice->spurious_threshold = voice->spurious_threshold;
+        evoice->LBA = voice->LBA;
+        evoice->CSO = 0;
+        evoice->ESO = (runtime->period_size * 2) + 4 - 1; /* in samples */
+        evoice->CTRL = voice->CTRL;
+        evoice->FMC = 3;
+        evoice->GVSel = trident->device == TRIDENT_DEVICE_ID_SI7018 ? 0 : 1;
+        evoice->EC = 0;
+        evoice->Alpha = 0;
+        evoice->FMS = 0;
+        evoice->Vol = 0x3ff;			/* mute */
+        evoice->RVol = evoice->CVol = 0x7f;	/* mute */
+        evoice->Pan = 0x7f;			/* mute */
+        evoice->Attribute = 0;
+        snd_trident_write_voice_regs(trident, evoice);
+        evoice->isync2 = 1;
+        evoice->isync_mark = runtime->period_size;
+        evoice->ESO = (runtime->period_size * 2) - 1;
+    }
+
+    spin_unlock_irq(&trident->reg_lock);
+    return 0;
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_spdif_hw_params
+
+ Description: Set the hardware parameters for the spdif device.
+
+ Parameters:  substream  - PCM substream class
+ hw_params  - hardware parameters
+
+ Returns:     Error status
+
+ ---------------------------------------------------------------------------*/
+
+static int snd_trident_spdif_hw_params(snd_pcm_substream_t * substream,
+                                       snd_pcm_hw_params_t * hw_params)
+{
+    struct snd_trident *trident = snd_pcm_substream_chip(substream);
+    unsigned int old_bits = 0, change = 0;
+    int err;
+
+    err = snd_trident_allocate_pcm_mem(substream, hw_params);
+    if (err < 0)
+        return err;
+
+    if (trident->device == TRIDENT_DEVICE_ID_SI7018) {
+        err = snd_trident_allocate_evoice(substream, hw_params);
+        if (err < 0)
+            return err;
+    }
+
+    /* prepare SPDIF channel */
+    spin_lock_irq(&trident->reg_lock);
+    old_bits = trident->spdif_pcm_bits;
+    if (old_bits & IEC958_AES0_PROFESSIONAL)
+        trident->spdif_pcm_bits &= ~IEC958_AES0_PRO_FS;
+    else
+        trident->spdif_pcm_bits &= ~(IEC958_AES3_CON_FS << 24);
+    if (params_rate(hw_params) >= 48000) {
+        trident->spdif_pcm_ctrl = 0x3c;	// 48000 Hz
+        trident->spdif_pcm_bits |=
+            trident->spdif_bits & IEC958_AES0_PROFESSIONAL ?
+            IEC958_AES0_PRO_FS_48000 :
+            (IEC958_AES3_CON_FS_48000 << 24);
+    }
+    else if (params_rate(hw_params) >= 44100) {
+        trident->spdif_pcm_ctrl = 0x3e;	// 44100 Hz
+        trident->spdif_pcm_bits |=
+            trident->spdif_bits & IEC958_AES0_PROFESSIONAL ?
+            IEC958_AES0_PRO_FS_44100 :
+            (IEC958_AES3_CON_FS_44100 << 24);
+    }
+    else {
+        trident->spdif_pcm_ctrl = 0x3d;	// 32000 Hz
+        trident->spdif_pcm_bits |=
+            trident->spdif_bits & IEC958_AES0_PROFESSIONAL ?
+            IEC958_AES0_PRO_FS_32000 :
+            (IEC958_AES3_CON_FS_32000 << 24);
+    }
+    change = old_bits != trident->spdif_pcm_bits;
+    spin_unlock_irq(&trident->reg_lock);
+
+    if (change)
+        snd_ctl_notify(trident->card, SNDRV_CTL_EVENT_MASK_VALUE, &trident->spdif_pcm_ctl->id);
+
+    return 0;
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_spdif_prepare
+
+ Description: Prepare SPDIF device for playback.
+
+ Parameters:  substream  - PCM substream class
+
+ Returns:     Error status
+
+ ---------------------------------------------------------------------------*/
+
+static int snd_trident_spdif_prepare(snd_pcm_substream_t * substream)
+{
+    struct snd_trident *trident = snd_pcm_substream_chip(substream);
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    struct snd_trident_voice*voice = (struct snd_trident_voice*) runtime->private_data;
+    struct snd_trident_voice*evoice = voice->extra;
+    struct snd_trident_pcm_mixer*mix = &trident->pcm_mixer[substream->number];
+    unsigned int RESO, LBAO;
+    unsigned int temp;
+
+    spin_lock_irq(&trident->reg_lock);
+
+    if (trident->device != TRIDENT_DEVICE_ID_SI7018) {
+
+        /* set delta (rate) value */
+        voice->Delta = snd_trident_convert_rate(runtime->rate);
+        voice->spurious_threshold = snd_trident_spurious_threshold(runtime->rate, runtime->period_size);
+
+        /* set Loop Back Address */
+        LBAO = runtime->dma_addr;
+        if (voice->memblk)
+            voice->LBA = voice->memblk->offset;
+        else
+            voice->LBA = LBAO;
+
+        voice->isync = 1;
+        voice->isync3 = 1;
+        voice->isync_mark = runtime->period_size;
+        voice->isync_max = runtime->buffer_size;
+
+        /* set target ESO for channel */
+        RESO = runtime->buffer_size - 1;
+        voice->ESO = voice->isync_ESO = (runtime->period_size * 2) + 6 - 1;
+
+        /* set ctrl mode */
+        voice->CTRL = snd_trident_control_mode(substream);
+
+        voice->FMC = 3;
+        voice->RVol = 0x7f;
+        voice->CVol = 0x7f;
+        voice->GVSel = 1;
+        voice->Pan = 0x7f;
+        voice->Vol = 0x3ff;
+        voice->EC = 0;
+        voice->CSO = 0;
+        voice->Alpha = 0;
+        voice->FMS = 0;
+        voice->Attribute = 0;
+
+        /* prepare surrogate IRQ channel */
+        snd_trident_write_voice_regs(trident, voice);
+
+        outw((RESO & 0xffff), TRID_REG(trident, NX_SPESO));
+        outb((RESO >> 16), TRID_REG(trident, NX_SPESO + 2));
+        outl((LBAO & 0xfffffffc), TRID_REG(trident, NX_SPLBA));
+        outw((voice->CSO & 0xffff), TRID_REG(trident, NX_SPCTRL_SPCSO));
+        outb((voice->CSO >> 16), TRID_REG(trident, NX_SPCTRL_SPCSO + 2));
+
+        /* set SPDIF setting */
+        outb(trident->spdif_pcm_ctrl, TRID_REG(trident, NX_SPCTRL_SPCSO + 3));
+        outl(trident->spdif_pcm_bits, TRID_REG(trident, NX_SPCSTATUS));
+
+    } else {	/* SiS */
+
+        /* set delta (rate) value */
+        voice->Delta = 0x800;
+        voice->spurious_threshold = snd_trident_spurious_threshold(48000, runtime->period_size);
+
+        /* set Loop Begin Address */
+        if (voice->memblk)
+            voice->LBA = voice->memblk->offset;
+        else
+            voice->LBA = runtime->dma_addr;
+
+        voice->CSO = 0;
+        voice->ESO = runtime->buffer_size - 1;	/* in samples */
+        voice->CTRL = snd_trident_control_mode(substream);
+        voice->FMC = 3;
+        voice->GVSel = 1;
+        voice->EC = 0;
+        voice->Alpha = 0;
+        voice->FMS = 0;
+        voice->Vol = mix->vol;
+        voice->RVol = mix->rvol;
+        voice->CVol = mix->cvol;
+        voice->Pan = mix->pan;
+        voice->Attribute = (1<<(30-16))|(7<<(26-16))|
+            (0<<(24-16))|(0<<(19-16));
+
+        snd_trident_write_voice_regs(trident, voice);
+
+        if (evoice != NULL) {
+            evoice->Delta = voice->Delta;
+            evoice->spurious_threshold = voice->spurious_threshold;
+            evoice->LBA = voice->LBA;
+            evoice->CSO = 0;
+            evoice->ESO = (runtime->period_size * 2) + 4 - 1; /* in samples */
+            evoice->CTRL = voice->CTRL;
+            evoice->FMC = 3;
+            evoice->GVSel = trident->device == TRIDENT_DEVICE_ID_SI7018 ? 0 : 1;
+            evoice->EC = 0;
+            evoice->Alpha = 0;
+            evoice->FMS = 0;
+            evoice->Vol = 0x3ff;			/* mute */
+            evoice->RVol = evoice->CVol = 0x7f;	/* mute */
+            evoice->Pan = 0x7f;			/* mute */
+            evoice->Attribute = 0;
+            snd_trident_write_voice_regs(trident, evoice);
+            evoice->isync2 = 1;
+            evoice->isync_mark = runtime->period_size;
+            evoice->ESO = (runtime->period_size * 2) - 1;
+        }
+
+        outl(trident->spdif_pcm_bits, TRID_REG(trident, SI_SPDIF_CS));
+        temp = inl(TRID_REG(trident, T4D_LFO_GC_CIR));
+        temp &= ~(1<<19);
+        outl(temp, TRID_REG(trident, T4D_LFO_GC_CIR));
+        temp = inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL));
+        temp |= SPDIF_EN;
+        outl(temp, TRID_REG(trident, SI_SERIAL_INTF_CTRL));
+    }
+
+    spin_unlock_irq(&trident->reg_lock);
+
+    return 0;
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_trigger
+
+ Description: Start/stop devices
+
+ Parameters:  substream  - PCM substream class
+ cmd	- trigger command (STOP, GO)
+
+ Returns:     Error status
+
+ ---------------------------------------------------------------------------*/
+
+static int snd_trident_trigger(snd_pcm_substream_t *substream,
+                               int cmd)
+
+{
+    struct snd_trident *trident = snd_pcm_substream_chip(substream);
+    struct list_head *pos;
+    snd_pcm_substream_t *s;
+    unsigned int what, whati, capture_flag, spdif_flag;
+    struct snd_trident_voice*voice, *evoice;
+    unsigned int val, go;
+
+    switch (cmd) {
+    case SNDRV_PCM_TRIGGER_START:
+    case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+    case SNDRV_PCM_TRIGGER_RESUME:
+        go = 1;
+        break;
+    case SNDRV_PCM_TRIGGER_STOP:
+    case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+    case SNDRV_PCM_TRIGGER_SUSPEND:
+        go = 0;
+        break;
+    default:
+        return -EINVAL;
+    }
+    what = whati = capture_flag = spdif_flag = 0;
+    spin_lock(&trident->reg_lock);
+    val = inl(TRID_REG(trident, T4D_STIMER)) & 0x00ffffff;
+    snd_pcm_group_for_each(pos, substream) {
+        s = snd_pcm_group_substream_entry(pos);
+        if ((struct snd_trident *) snd_pcm_substream_chip(s) == trident) {
+            voice = (struct snd_trident_voice*) s->runtime->private_data;
+            evoice = voice->extra;
+            what |= 1 << (voice->number & 0x1f);
+            if (evoice == NULL) {
+                whati |= 1 << (voice->number & 0x1f);
+            } else {
+                what |= 1 << (evoice->number & 0x1f);
+                whati |= 1 << (evoice->number & 0x1f);
+                if (go)
+                    evoice->stimer = val;
+            }
+            if (go) {
+                voice->running = 1;
+                voice->stimer = val;
+            } else {
+                voice->running = 0;
+            }
+            snd_pcm_trigger_done(s, substream);
+            if (voice->capture)
+                capture_flag = 1;
+            if (voice->spdif)
+                spdif_flag = 1;
+        }
+    }
+    if (spdif_flag) {
+        if (trident->device != TRIDENT_DEVICE_ID_SI7018) {
+            outl(trident->spdif_pcm_bits, TRID_REG(trident, NX_SPCSTATUS));
+            outb(trident->spdif_pcm_ctrl, TRID_REG(trident, NX_SPCTRL_SPCSO + 3));
+        } else {
+            outl(trident->spdif_pcm_bits, TRID_REG(trident, SI_SPDIF_CS));
+            val = inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL)) | SPDIF_EN;
+            outl(val, TRID_REG(trident, SI_SERIAL_INTF_CTRL));
+        }
+    }
+    if (!go)
+        outl(what, TRID_REG(trident, T4D_STOP_B));
+    val = inl(TRID_REG(trident, T4D_AINTEN_B));
+    if (go) {
+        val |= whati;
+    } else {
+        val &= ~whati;
+    }
+    outl(val, TRID_REG(trident, T4D_AINTEN_B));
+    if (go) {
+        outl(what, TRID_REG(trident, T4D_START_B));
+
+        if (capture_flag && trident->device != TRIDENT_DEVICE_ID_SI7018)
+            outb(trident->bDMAStart, TRID_REG(trident, T4D_SBCTRL_SBE2R_SBDD));
+    } else {
+        if (capture_flag && trident->device != TRIDENT_DEVICE_ID_SI7018)
+            outb(0x00, TRID_REG(trident, T4D_SBCTRL_SBE2R_SBDD));
+    }
+    spin_unlock(&trident->reg_lock);
+    return 0;
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_playback_pointer
+
+ Description: This routine return the playback position
+
+ Parameters:	substream  - PCM substream class
+
+ Returns:     position of buffer
+
+ ---------------------------------------------------------------------------*/
+
+static snd_pcm_uframes_t snd_trident_playback_pointer(snd_pcm_substream_t * substream)
+{
+    struct snd_trident *trident = snd_pcm_substream_chip(substream);
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    struct snd_trident_voice*voice = (struct snd_trident_voice*) runtime->private_data;
+    unsigned int cso;
+
+    if (!voice->running)
+        return 0;
+
+    spin_lock(&trident->reg_lock);
+
+    outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
+
+    if (trident->device != TRIDENT_DEVICE_ID_NX) {
+        cso = inw(TRID_REG(trident, CH_DX_CSO_ALPHA_FMS + 2));
+    } else {		// ID_4DWAVE_NX
+        cso = (unsigned int) inl(TRID_REG(trident, CH_NX_DELTA_CSO)) & 0x00ffffff;
+    }
+
+    spin_unlock(&trident->reg_lock);
+
+    if (cso >= runtime->buffer_size)
+        cso = 0;
+
+    return cso;
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_capture_pointer
+
+ Description: This routine return the capture position
+
+ Paramters:   pcm1    - PCM device class
+
+ Returns:     position of buffer
+
+ ---------------------------------------------------------------------------*/
+
+static snd_pcm_uframes_t snd_trident_capture_pointer(snd_pcm_substream_t * substream)
+{
+    struct snd_trident *trident = snd_pcm_substream_chip(substream);
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    struct snd_trident_voice*voice = (struct snd_trident_voice*) runtime->private_data;
+    unsigned int result;
+
+    if (!voice->running)
+        return 0;
+
+    result = inw(TRID_REG(trident, T4D_SBBL_SBCL));
+    if (runtime->channels > 1)
+        result >>= 1;
+    if (result > 0)
+        result = runtime->buffer_size - result;
+
+    return result;
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_spdif_pointer
+
+ Description: This routine return the SPDIF playback position
+
+ Parameters:	substream  - PCM substream class
+
+ Returns:     position of buffer
+
+ ---------------------------------------------------------------------------*/
+
+static snd_pcm_uframes_t snd_trident_spdif_pointer(snd_pcm_substream_t * substream)
+{
+    struct snd_trident *trident = snd_pcm_substream_chip(substream);
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    struct snd_trident_voice*voice = (struct snd_trident_voice*) runtime->private_data;
+    unsigned int result;
+
+    if (!voice->running)
+        return 0;
+
+    result = inl(TRID_REG(trident, NX_SPCTRL_SPCSO)) & 0x00ffffff;
+
+    return result;
+}
+
+/*
+ *  Playback support device description
+ */
+
+static snd_pcm_hardware_t snd_trident_playback =
+{
+    /*	info:		  */	(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+                                 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+                                 SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START |
+                                 SNDRV_PCM_INFO_PAUSE /* | SNDRV_PCM_INFO_RESUME */),
+                                 /*	formats:	  */	(SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE |
+                                                                 SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE),
+                                                                 /*	rates:		  */	SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+                                                                 /*	rate_min:	  */	4000,
+                                                                 /*	rate_max:	  */	48000,
+                                                                 /*	channels_min:	  */	1,
+                                                                 /*	channels_max:	  */	2,
+                                                                 /*	buffer_bytes_max:  */	(256*1024),
+                                                                 /*	period_bytes_min:  */	64,
+                                                                 /*	period_bytes_max:  */	(256*1024),
+                                                                 /*	periods_min:	  */	1,
+                                                                 /*	periods_max:	  */	1024,
+                                                                 /*	fifo_size:	  */	0,
+};
+
+/*
+ *  Capture support device description
+ */
+
+static snd_pcm_hardware_t snd_trident_capture =
+{
+    /*	info:		  */	(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+                                 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+                                 SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START |
+                                 SNDRV_PCM_INFO_PAUSE /* | SNDRV_PCM_INFO_RESUME */),
+                                 /*	formats:	  */	(SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE |
+                                                                 SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE),
+                                                                 /*	rates:		  */	SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+                                                                 /*	rate_min:	  */	4000,
+                                                                 /*	rate_max:	  */	48000,
+                                                                 /*	channels_min:	  */	1,
+                                                                 /*	channels_max:	  */	2,
+                                                                 /*	buffer_bytes_max:  */	(128*1024),
+                                                                 /*	period_bytes_min:  */	64,
+                                                                 /*	period_bytes_max:  */	(128*1024),
+                                                                 /*	periods_min:	  */	1,
+                                                                 /*	periods_max:	  */	1024,
+                                                                 /*	fifo_size:	  */	0,
+};
+
+/*
+ *  Foldback capture support device description
+ */
+
+static snd_pcm_hardware_t snd_trident_foldback =
+{
+    /*	info:		  */	(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+                                 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+                                 SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START |
+                                 SNDRV_PCM_INFO_PAUSE /* | SNDRV_PCM_INFO_RESUME */),
+                                 /*	formats:	  */	SNDRV_PCM_FMTBIT_S16_LE,
+                                 /*	rates:		  */	SNDRV_PCM_RATE_48000,
+                                 /*	rate_min:	  */	48000,
+                                 /*	rate_max:	  */	48000,
+                                 /*	channels_min:	  */	2,
+                                 /*	channels_max:	  */	2,
+                                 /*	buffer_bytes_max:  */	(128*1024),
+                                 /*	period_bytes_min:  */	64,
+                                 /*	period_bytes_max:  */	(128*1024),
+                                 /*	periods_min:	  */	1,
+                                 /*	periods_max:	  */	1024,
+                                 /*	fifo_size:	  */	0,
+};
+
+/*
+ *  SPDIF playback support device description
+ */
+
+static snd_pcm_hardware_t snd_trident_spdif =
+{
+    /*	info:		  */	(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+                                 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+                                 SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START |
+                                 SNDRV_PCM_INFO_PAUSE /* | SNDRV_PCM_INFO_RESUME */),
+                                 /*	formats:	  */	SNDRV_PCM_FMTBIT_S16_LE,
+                                 /*	rates:		  */	(SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+                                                                 SNDRV_PCM_RATE_48000),
+                                                                 /*	rate_min:	  */	32000,
+                                                                 /*	rate_max:	  */	48000,
+                                                                 /*	channels_min:	  */	2,
+                                                                 /*	channels_max:	  */	2,
+                                                                 /*	buffer_bytes_max:  */	(128*1024),
+                                                                 /*	period_bytes_min:  */	64,
+                                                                 /*	period_bytes_max:  */	(128*1024),
+                                                                 /*	periods_min:	  */	1,
+                                                                 /*	periods_max:	  */	1024,
+                                                                 /*	fifo_size:	  */	0,
+};
+
+
+static snd_pcm_hardware_t snd_trident_spdif_7018 =
+{
+    /*	.info =		     */   (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+                                   SNDRV_PCM_INFO_BLOCK_TRANSFER |
+                                   SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START |
+                                   SNDRV_PCM_INFO_PAUSE /* | SNDRV_PCM_INFO_RESUME */),
+                                   /*	.formats =	     */   SNDRV_PCM_FMTBIT_S16_LE,
+                                   /*	.rates =	     */   SNDRV_PCM_RATE_48000,
+                                   /*	.rate_min =	     */   48000,
+                                   /*	.rate_max =	     */   48000,
+                                   /*	.channels_min =	     */   2,
+                                   /*	.channels_max =	     */   2,
+                                   /*	.buffer_bytes_max =  */   (128*1024),
+                                   /*	.period_bytes_min =  */   64,
+                                   /*	.period_bytes_max =  */   (128*1024),
+                                   /*	.periods_min =	     */   1,
+                                   /*	.periods_max =	     */   1024,
+                                   /*	.fifo_size =	     */   0,
+};
+static void snd_trident_pcm_free_substream(snd_pcm_runtime_t *runtime)
+{
+    struct snd_trident_voice*voice = (struct snd_trident_voice*) runtime->private_data;
+    struct snd_trident *trident;
+
+    if (voice) {
+        trident = voice->trident;
+        snd_trident_free_voice(trident, voice);
+    }
+}
+
+static int snd_trident_playback_open(snd_pcm_substream_t * substream)
+{
+    struct snd_trident *trident = snd_pcm_substream_chip(substream);
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    struct snd_trident_voice*voice;
+
+    voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0);
+    if (voice == NULL)
+        return -EAGAIN;
+    snd_trident_pcm_mixer_build(trident, voice, substream);
+    voice->substream = substream;
+    runtime->private_data = voice;
+    runtime->private_free = snd_trident_pcm_free_substream;
+    runtime->hw = snd_trident_playback;
+    snd_pcm_set_sync(substream);
+    snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 64*1024);
+    return 0;
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_playback_close
+
+ Description: This routine will close the 4DWave playback device. For now
+ we will simply free the dma transfer buffer.
+
+ Parameters:	substream  - PCM substream class
+
+ ---------------------------------------------------------------------------*/
+static int snd_trident_playback_close(snd_pcm_substream_t * substream)
+{
+    struct snd_trident *trident = snd_pcm_substream_chip(substream);
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    struct snd_trident_voice*voice = (struct snd_trident_voice*) runtime->private_data;
+
+    snd_trident_pcm_mixer_free(trident, voice, substream);
+    return 0;
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_spdif_open
+
+ Description: This routine will open the 4DWave SPDIF device.
+
+ Parameters:	substream  - PCM substream class
+
+ Returns:     status  - success or failure flag
+
+ ---------------------------------------------------------------------------*/
+
+static int snd_trident_spdif_open(snd_pcm_substream_t * substream)
+{
+    struct snd_trident *trident = snd_pcm_substream_chip(substream);
+    struct snd_trident_voice*voice;
+    snd_pcm_runtime_t *runtime = substream->runtime;
+
+    voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0);
+    if (voice == NULL)
+        return -EAGAIN;
+    voice->spdif = 1;
+    voice->substream = substream;
+    spin_lock_irq(&trident->reg_lock);
+    trident->spdif_pcm_bits = trident->spdif_bits;
+    spin_unlock_irq(&trident->reg_lock);
+
+    runtime->private_data = voice;
+    runtime->private_free = snd_trident_pcm_free_substream;
+    if (trident->device == TRIDENT_DEVICE_ID_SI7018) {
+        runtime->hw = snd_trident_spdif;
+    } else {
+        runtime->hw = snd_trident_spdif_7018;
+    }
+
+    trident->spdif_pcm_ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+    snd_ctl_notify(trident->card, SNDRV_CTL_EVENT_MASK_VALUE |
+                   SNDRV_CTL_EVENT_MASK_INFO, &trident->spdif_pcm_ctl->id);
+
+    snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 64*1024);
+    return 0;
+}
+
+
+/*---------------------------------------------------------------------------
+ snd_trident_spdif_close
+
+ Description: This routine will close the 4DWave SPDIF device.
+
+ Parameters:	substream  - PCM substream class
+
+ ---------------------------------------------------------------------------*/
+
+static int snd_trident_spdif_close(snd_pcm_substream_t * substream)
+{
+    struct snd_trident *trident = snd_pcm_substream_chip(substream);
+    unsigned int temp;
+
+    spin_lock_irq(&trident->reg_lock);
+    // restore default SPDIF setting
+    if (trident->device != TRIDENT_DEVICE_ID_SI7018) {
+        outb(trident->spdif_ctrl, TRID_REG(trident, NX_SPCTRL_SPCSO + 3));
+        outl(trident->spdif_bits, TRID_REG(trident, NX_SPCSTATUS));
+    } else {
+        outl(trident->spdif_bits, TRID_REG(trident, SI_SPDIF_CS));
+        temp = inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL));
+        if (trident->spdif_ctrl) {
+            temp |= SPDIF_EN;
+        } else {
+            temp &= ~SPDIF_EN;
+        }
+        outl(temp, TRID_REG(trident, SI_SERIAL_INTF_CTRL));
+    }
+    spin_unlock_irq(&trident->reg_lock);
+    trident->spdif_pcm_ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+    snd_ctl_notify(trident->card, SNDRV_CTL_EVENT_MASK_VALUE |
+                   SNDRV_CTL_EVENT_MASK_INFO, &trident->spdif_pcm_ctl->id);
+    return 0;
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_capture_open
+
+ Description: This routine will open the 4DWave capture device.
+
+ Parameters:	substream  - PCM substream class
+
+ Returns:     status  - success or failure flag
+
+ ---------------------------------------------------------------------------*/
+
+static int snd_trident_capture_open(snd_pcm_substream_t * substream)
+{
+    struct snd_trident *trident = snd_pcm_substream_chip(substream);
+    struct snd_trident_voice*voice;
+    snd_pcm_runtime_t *runtime = substream->runtime;
+
+    voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0);
+    if (voice == NULL)
+        return -EAGAIN;
+    voice->capture = 1;
+    voice->substream = substream;
+    runtime->private_data = voice;
+    runtime->private_free = snd_trident_pcm_free_substream;
+    runtime->hw = snd_trident_capture;
+    snd_pcm_set_sync(substream);
+    snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 64*1024);
+    return 0;
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_capture_close
+
+ Description: This routine will close the 4DWave capture device. For now
+ we will simply free the dma transfer buffer.
+
+ Parameters:	substream  - PCM substream class
+
+ ---------------------------------------------------------------------------*/
+static int snd_trident_capture_close(snd_pcm_substream_t * substream)
+{
+    return 0;
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_foldback_open
+
+ Description: This routine will open the 4DWave foldback capture device.
+
+ Parameters:	substream  - PCM substream class
+
+ Returns:     status  - success or failure flag
+
+ ---------------------------------------------------------------------------*/
+
+static int snd_trident_foldback_open(snd_pcm_substream_t * substream)
+{
+    struct snd_trident *trident = snd_pcm_substream_chip(substream);
+    struct snd_trident_voice*voice;
+    snd_pcm_runtime_t *runtime = substream->runtime;
+
+    voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0);
+    if (voice == NULL)
+        return -EAGAIN;
+    voice->foldback_chan = substream->number;
+    voice->substream = substream;
+    runtime->private_data = voice;
+    runtime->private_free = snd_trident_pcm_free_substream;
+    runtime->hw = snd_trident_foldback;
+    snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 64*1024);
+    return 0;
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_foldback_close
+
+ Description: This routine will close the 4DWave foldback capture device.
+ For now we will simply free the dma transfer buffer.
+
+ Parameters:	substream  - PCM substream class
+
+ ---------------------------------------------------------------------------*/
+static int snd_trident_foldback_close(snd_pcm_substream_t * substream)
+{
+    struct snd_trident *trident = snd_pcm_substream_chip(substream);
+    struct snd_trident_voice*voice;
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    voice = (struct snd_trident_voice*) runtime->private_data;
+
+    /* stop capture channel */
+    spin_lock_irq(&trident->reg_lock);
+    outb(0x00, TRID_REG(trident, T4D_RCI + voice->foldback_chan));
+    spin_unlock_irq(&trident->reg_lock);
+    return 0;
+}
+
+/*---------------------------------------------------------------------------
+ PCM operations
+ ---------------------------------------------------------------------------*/
+
+static snd_pcm_ops_t snd_trident_playback_ops = {
+    snd_trident_playback_open,
+    snd_trident_playback_close,
+    snd_trident_ioctl,
+    snd_trident_hw_params,
+    snd_trident_hw_free,
+    snd_trident_playback_prepare,
+    snd_trident_trigger,
+    snd_trident_playback_pointer,0,0
+};
+
+static snd_pcm_ops_t snd_trident_nx_playback_ops = {
+    snd_trident_playback_open,
+    snd_trident_playback_close,
+    snd_trident_ioctl,
+    snd_trident_hw_params,
+    snd_trident_hw_free,
+    snd_trident_playback_prepare,
+    snd_trident_trigger,
+    snd_trident_playback_pointer,0,0,
+    snd_pcm_sgbuf_ops_page, 0
+};
+
+static snd_pcm_ops_t snd_trident_capture_ops = {
+    snd_trident_capture_open,
+    snd_trident_capture_close,
+    snd_trident_ioctl,
+    snd_trident_capture_hw_params,
+    snd_trident_hw_free,
+    snd_trident_capture_prepare,
+    snd_trident_trigger,
+    snd_trident_capture_pointer,0,0
+};
+
+static snd_pcm_ops_t snd_trident_si7018_capture_ops = {
+    snd_trident_capture_open,
+    snd_trident_capture_close,
+    snd_trident_ioctl,
+    snd_trident_si7018_capture_hw_params,
+    snd_trident_si7018_capture_hw_free,
+    snd_trident_si7018_capture_prepare,
+    snd_trident_trigger,
+    snd_trident_playback_pointer,0,0
+};
+
+static snd_pcm_ops_t snd_trident_foldback_ops = {
+    snd_trident_foldback_open,
+    snd_trident_foldback_close,
+    snd_trident_ioctl,
+    snd_trident_hw_params,
+    snd_trident_hw_free,
+    snd_trident_foldback_prepare,
+    snd_trident_trigger,
+    snd_trident_playback_pointer,0,0
+};
+
+static snd_pcm_ops_t snd_trident_nx_foldback_ops = {
+    snd_trident_foldback_open,
+    snd_trident_foldback_close,
+    snd_trident_ioctl,
+    snd_trident_hw_params,
+    snd_trident_hw_free,
+    snd_trident_foldback_prepare,
+    snd_trident_trigger,
+    snd_trident_playback_pointer,0,0,
+    snd_pcm_sgbuf_ops_page,0
+};
+
+static snd_pcm_ops_t snd_trident_spdif_ops = {
+    snd_trident_spdif_open,
+    snd_trident_spdif_close,
+    snd_trident_ioctl,
+    snd_trident_spdif_hw_params,
+    snd_trident_hw_free,
+    snd_trident_spdif_prepare,
+    snd_trident_trigger,
+    snd_trident_spdif_pointer,0,0
+};
+
+static snd_pcm_ops_t snd_trident_spdif_7018_ops = {
+    snd_trident_spdif_open,
+    snd_trident_spdif_close,
+    snd_trident_ioctl,
+    snd_trident_spdif_hw_params,
+    snd_trident_hw_free,
+    snd_trident_spdif_prepare,
+    snd_trident_trigger,
+    snd_trident_playback_pointer,0,0
+};
+
+/*---------------------------------------------------------------------------
+ snd_trident_pcm_free
+
+ Description: This routine release the 4DWave private data.
+
+ Paramters:   private_data - pointer to 4DWave device info.
+
+ Returns:     None
+
+ ---------------------------------------------------------------------------*/
+static void snd_trident_pcm_free(snd_pcm_t *pcm)
+{
+    struct snd_trident *trident = pcm->private_data;
+    trident->pcm = NULL;
+    snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static void snd_trident_foldback_pcm_free(snd_pcm_t *pcm)
+{
+    struct snd_trident *trident = pcm->private_data;
+    trident->foldback = NULL;
+    snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static void snd_trident_spdif_pcm_free(snd_pcm_t *pcm)
+{
+    struct snd_trident *trident = pcm->private_data;
+    trident->spdif = NULL;
+    snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_pcm
+
+ Description: This routine registers the 4DWave device for PCM support.
+
+ Paramters:   trident - pointer to target device class for 4DWave.
+
+ Returns:     None
+
+ ---------------------------------------------------------------------------*/
+
+int __devinit snd_trident_pcm(struct snd_trident * trident, int device, snd_pcm_t ** rpcm)
+{
+	snd_pcm_t *pcm;
+	int err;
+
+	if (rpcm)
+		*rpcm = NULL;
+	if ((err = snd_pcm_new(trident->card, "trident_dx_nx", device, trident->ChanPCM, 1, &pcm)) < 0)
+		return err;
+
+	pcm->private_data = trident;
+	pcm->private_free = snd_trident_pcm_free;
+
+	if (trident->tlb.entries) {
+		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_trident_nx_playback_ops);
+	} else {
+		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_trident_playback_ops);
+	}
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+			trident->device != TRIDENT_DEVICE_ID_SI7018 ?
+			&snd_trident_capture_ops :
+			&snd_trident_si7018_capture_ops);
+
+	pcm->info_flags = 0;
+	pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
+	strcpy(pcm->name, "Trident 4DWave");
+	trident->pcm = pcm;
+
+	if (trident->tlb.entries) {
+		snd_pcm_substream_t *substream;
+		for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next)
+			snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV_SG,
+						      snd_dma_pci_data(trident->pci),
+						      64*1024, 128*1024);
+		snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream,
+					      SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(trident->pci),
+					      64*1024, 128*1024);
+	} else {
+		snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+						      snd_dma_pci_data(trident->pci), 64*1024, 128*1024);
+	}
+
+	if (rpcm)
+		*rpcm = pcm;
+	return 0;
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_foldback_pcm
+
+ Description: This routine registers the 4DWave device for foldback PCM support.
+
+ Paramters:   trident - pointer to target device class for 4DWave.
+
+ Returns:     None
+
+ ---------------------------------------------------------------------------*/
+
+int __devinit snd_trident_foldback_pcm(struct snd_trident * trident, int device, snd_pcm_t ** rpcm)
+{
+	snd_pcm_t *foldback;
+	int err;
+	int num_chan = 3;
+	snd_pcm_substream_t *substream;
+
+	if (rpcm)
+		*rpcm = NULL;
+	if (trident->device == TRIDENT_DEVICE_ID_NX)
+		num_chan = 4;
+	if ((err = snd_pcm_new(trident->card, "trident_dx_nx", device, 0, num_chan, &foldback)) < 0)
+		return err;
+
+	foldback->private_data = trident;
+	foldback->private_free = snd_trident_foldback_pcm_free;
+	if (trident->tlb.entries)
+		snd_pcm_set_ops(foldback, SNDRV_PCM_STREAM_CAPTURE, &snd_trident_nx_foldback_ops);
+	else
+		snd_pcm_set_ops(foldback, SNDRV_PCM_STREAM_CAPTURE, &snd_trident_foldback_ops);
+	foldback->info_flags = 0;
+	strcpy(foldback->name, "Trident 4DWave");
+	substream = foldback->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
+	strcpy(substream->name, "Front Mixer");
+	substream = substream->next;
+	strcpy(substream->name, "Reverb Mixer");
+	substream = substream->next;
+	strcpy(substream->name, "Chorus Mixer");
+	if (num_chan == 4) {
+		substream = substream->next;
+		strcpy(substream->name, "Second AC'97 ADC");
+	}
+	trident->foldback = foldback;
+
+	if (trident->tlb.entries)
+		snd_pcm_lib_preallocate_pages_for_all(foldback, SNDRV_DMA_TYPE_DEV_SG,
+						      snd_dma_pci_data(trident->pci), 0, 128*1024);
+	else
+		snd_pcm_lib_preallocate_pages_for_all(foldback, SNDRV_DMA_TYPE_DEV,
+						      snd_dma_pci_data(trident->pci), 64*1024, 128*1024);
+
+	if (rpcm)
+		*rpcm = foldback;
+	return 0;
+}
+
+
+/*---------------------------------------------------------------------------
+ snd_trident_spdif
+
+ Description: This routine registers the 4DWave-NX device for SPDIF support.
+
+ Paramters:   trident - pointer to target device class for 4DWave-NX.
+
+ Returns:     None
+
+ ---------------------------------------------------------------------------*/
+
+int __devinit snd_trident_spdif_pcm(struct snd_trident * trident, int device, snd_pcm_t ** rpcm)
+{
+	snd_pcm_t *spdif;
+	int err;
+
+	if (rpcm)
+		*rpcm = NULL;
+	if ((err = snd_pcm_new(trident->card, "trident_dx_nx IEC958", device, 1, 0, &spdif)) < 0)
+		return err;
+
+	spdif->private_data = trident;
+	spdif->private_free = snd_trident_spdif_pcm_free;
+	if (trident->device != TRIDENT_DEVICE_ID_SI7018) {
+		snd_pcm_set_ops(spdif, SNDRV_PCM_STREAM_PLAYBACK, &snd_trident_spdif_ops);
+	} else {
+		snd_pcm_set_ops(spdif, SNDRV_PCM_STREAM_PLAYBACK, &snd_trident_spdif_7018_ops);
+	}
+	spdif->info_flags = 0;
+	strcpy(spdif->name, "Trident 4DWave IEC958");
+	trident->spdif = spdif;
+
+	snd_pcm_lib_preallocate_pages_for_all(spdif, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(trident->pci), 64*1024, 128*1024);
+
+	if (rpcm)
+		*rpcm = spdif;
+	return 0;
+}
+/*
+ *  Mixer part
+ */
+
+
+/*---------------------------------------------------------------------------
+ snd_trident_spdif_control
+
+ Description: enable/disable S/PDIF out from ac97 mixer
+ ---------------------------------------------------------------------------*/
+
+static int snd_trident_spdif_control_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_trident_spdif_control_get(snd_kcontrol_t * kcontrol,
+                                         snd_ctl_elem_value_t * ucontrol)
+{
+    struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
+    unsigned char val;
+
+    spin_lock_irq(&trident->reg_lock);
+    val = trident->spdif_ctrl;
+    ucontrol->value.integer.value[0] = val == kcontrol->private_value;
+    spin_unlock_irq(&trident->reg_lock);
+    return 0;
+}
+
+static int snd_trident_spdif_control_put(snd_kcontrol_t * kcontrol,
+                                         snd_ctl_elem_value_t * ucontrol)
+{
+    struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
+    unsigned char val;
+    int change;
+
+    val = ucontrol->value.integer.value[0] ? (unsigned char) kcontrol->private_value : 0x00;
+    spin_lock_irq(&trident->reg_lock);
+    /* S/PDIF C Channel bits 0-31 : 48khz, SCMS disabled */
+    change = trident->spdif_ctrl != val;
+    trident->spdif_ctrl = val;
+    if (trident->device != TRIDENT_DEVICE_ID_SI7018) {
+        if ((inb(TRID_REG(trident, NX_SPCTRL_SPCSO + 3)) & 0x10) == 0) {
+            outl(trident->spdif_bits, TRID_REG(trident, NX_SPCSTATUS));
+            outb(trident->spdif_ctrl, TRID_REG(trident, NX_SPCTRL_SPCSO + 3));
+        }
+    } else {
+        if (trident->spdif == NULL) {
+            unsigned int temp;
+            outl(trident->spdif_bits, TRID_REG(trident, SI_SPDIF_CS));
+            temp = inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL)) & ~SPDIF_EN;
+            if (val)
+                temp |= SPDIF_EN;
+            outl(temp, TRID_REG(trident, SI_SERIAL_INTF_CTRL));
+        }
+    }
+    spin_unlock_irq(&trident->reg_lock);
+    return change;
+}
+
+static snd_kcontrol_new_t snd_trident_spdif_control __devinitdata =
+{
+    SNDRV_CTL_ELEM_IFACE_MIXER,0,0,
+    SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH),0,0,0,
+    snd_trident_spdif_control_info,
+    snd_trident_spdif_control_get,
+    snd_trident_spdif_control_put,
+    0x28,
+};
+
+/*---------------------------------------------------------------------------
+ snd_trident_spdif_default
+
+ Description: put/get the S/PDIF default settings
+ ---------------------------------------------------------------------------*/
+
+static int snd_trident_spdif_default_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+    uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+    uinfo->count = 1;
+    return 0;
+}
+
+static int snd_trident_spdif_default_get(snd_kcontrol_t * kcontrol,
+                                         snd_ctl_elem_value_t * ucontrol)
+{
+    struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
+
+    spin_lock_irq(&trident->reg_lock);
+    ucontrol->value.iec958.status[0] = (trident->spdif_bits >> 0) & 0xff;
+    ucontrol->value.iec958.status[1] = (trident->spdif_bits >> 8) & 0xff;
+    ucontrol->value.iec958.status[2] = (trident->spdif_bits >> 16) & 0xff;
+    ucontrol->value.iec958.status[3] = (trident->spdif_bits >> 24) & 0xff;
+    spin_unlock_irq(&trident->reg_lock);
+    return 0;
+}
+
+static int snd_trident_spdif_default_put(snd_kcontrol_t * kcontrol,
+                                         snd_ctl_elem_value_t * ucontrol)
+{
+    struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
+    unsigned int val;
+    int change;
+
+    val = (ucontrol->value.iec958.status[0] << 0) |
+        (ucontrol->value.iec958.status[1] << 8) |
+        (ucontrol->value.iec958.status[2] << 16) |
+        (ucontrol->value.iec958.status[3] << 24);
+    spin_lock_irq(&trident->reg_lock);
+    change = trident->spdif_bits != val;
+    trident->spdif_bits = val;
+    if (trident->device != TRIDENT_DEVICE_ID_SI7018) {
+        if ((inb(TRID_REG(trident, NX_SPCTRL_SPCSO + 3)) & 0x10) == 0)
+            outl(trident->spdif_bits, TRID_REG(trident, NX_SPCSTATUS));
+    } else {
+        if (trident->spdif == NULL)
+            outl(trident->spdif_bits, TRID_REG(trident, SI_SPDIF_CS));
+    }
+    spin_unlock_irq(&trident->reg_lock);
+    return change;
+}
+
+static snd_kcontrol_new_t snd_trident_spdif_default __devinitdata =
+{
+    SNDRV_CTL_ELEM_IFACE_PCM,0,0,
+    SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),0,0,0,
+    snd_trident_spdif_default_info,
+    snd_trident_spdif_default_get,
+    snd_trident_spdif_default_put,0
+};
+
+/*---------------------------------------------------------------------------
+ snd_trident_spdif_mask
+
+ Description: put/get the S/PDIF mask
+ ---------------------------------------------------------------------------*/
+
+static int snd_trident_spdif_mask_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+    uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+    uinfo->count = 1;
+    return 0;
+}
+
+static int snd_trident_spdif_mask_get(snd_kcontrol_t * kcontrol,
+                                      snd_ctl_elem_value_t * ucontrol)
+{
+    ucontrol->value.iec958.status[0] = 0xff;
+    ucontrol->value.iec958.status[1] = 0xff;
+    ucontrol->value.iec958.status[2] = 0xff;
+    ucontrol->value.iec958.status[3] = 0xff;
+    return 0;
+}
+
+static snd_kcontrol_new_t snd_trident_spdif_mask __devinitdata =
+{
+    SNDRV_CTL_ELEM_IFACE_PCM,0,0,
+    SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK),0,
+    SNDRV_CTL_ELEM_ACCESS_READ,0,
+    snd_trident_spdif_mask_info,
+    snd_trident_spdif_mask_get,0,0
+};
+
+/*---------------------------------------------------------------------------
+ snd_trident_spdif_stream
+
+ Description: put/get the S/PDIF stream settings
+ ---------------------------------------------------------------------------*/
+
+static int snd_trident_spdif_stream_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+    uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+    uinfo->count = 1;
+    return 0;
+}
+
+static int snd_trident_spdif_stream_get(snd_kcontrol_t * kcontrol,
+                                        snd_ctl_elem_value_t * ucontrol)
+{
+    struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
+
+    spin_lock_irq(&trident->reg_lock);
+    ucontrol->value.iec958.status[0] = (trident->spdif_pcm_bits >> 0) & 0xff;
+    ucontrol->value.iec958.status[1] = (trident->spdif_pcm_bits >> 8) & 0xff;
+    ucontrol->value.iec958.status[2] = (trident->spdif_pcm_bits >> 16) & 0xff;
+    ucontrol->value.iec958.status[3] = (trident->spdif_pcm_bits >> 24) & 0xff;
+    spin_unlock_irq(&trident->reg_lock);
+    return 0;
+}
+
+static int snd_trident_spdif_stream_put(snd_kcontrol_t * kcontrol,
+                                        snd_ctl_elem_value_t * ucontrol)
+{
+    struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
+    unsigned int val;
+    int change;
+
+    val = (ucontrol->value.iec958.status[0] << 0) |
+        (ucontrol->value.iec958.status[1] << 8) |
+        (ucontrol->value.iec958.status[2] << 16) |
+        (ucontrol->value.iec958.status[3] << 24);
+    spin_lock_irq(&trident->reg_lock);
+    change = trident->spdif_pcm_bits != val;
+    trident->spdif_pcm_bits = val;
+    if (trident->spdif != NULL) {
+        if (trident->device != TRIDENT_DEVICE_ID_SI7018) {
+            outl(trident->spdif_pcm_bits, TRID_REG(trident, NX_SPCSTATUS));
+        } else {
+            outl(trident->spdif_bits, TRID_REG(trident, SI_SPDIF_CS));
+        }
+    }
+    spin_unlock_irq(&trident->reg_lock);
+    return change;
+}
+
+static snd_kcontrol_new_t snd_trident_spdif_stream __devinitdata =
+{
+    SNDRV_CTL_ELEM_IFACE_PCM,0,0,
+    SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM),0,
+    SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,0,
+    snd_trident_spdif_stream_info,
+    snd_trident_spdif_stream_get,
+    snd_trident_spdif_stream_put,0
+};
+
+/*---------------------------------------------------------------------------
+ snd_trident_ac97_control
+
+ Description: enable/disable rear path for ac97
+ ---------------------------------------------------------------------------*/
+
+static int snd_trident_ac97_control_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_trident_ac97_control_get(snd_kcontrol_t * kcontrol,
+                                        snd_ctl_elem_value_t * ucontrol)
+{
+    struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
+    unsigned char val;
+
+    spin_lock_irq(&trident->reg_lock);
+    val = trident->ac97_ctrl = inl(TRID_REG(trident, NX_ACR0_AC97_COM_STAT));
+    ucontrol->value.integer.value[0] = (val & (1 << kcontrol->private_value)) ? 1 : 0;
+    spin_unlock_irq(&trident->reg_lock);
+    return 0;
+}
+
+static int snd_trident_ac97_control_put(snd_kcontrol_t * kcontrol,
+                                        snd_ctl_elem_value_t * ucontrol)
+{
+    struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
+    unsigned char val;
+    int change = 0;
+
+    spin_lock_irq(&trident->reg_lock);
+    val = trident->ac97_ctrl = inl(TRID_REG(trident, NX_ACR0_AC97_COM_STAT));
+    val &= ~(1 << kcontrol->private_value);
+    if (ucontrol->value.integer.value[0])
+        val |= 1 << kcontrol->private_value;
+    change = val != trident->ac97_ctrl;
+    trident->ac97_ctrl = val;
+    outl(trident->ac97_ctrl = val, TRID_REG(trident, NX_ACR0_AC97_COM_STAT));
+    spin_unlock_irq(&trident->reg_lock);
+    return change;
+}
+
+static snd_kcontrol_new_t snd_trident_ac97_rear_control __devinitdata =
+{
+    SNDRV_CTL_ELEM_IFACE_MIXER,0,0,
+    "Rear Path",0,0,0,
+    snd_trident_ac97_control_info,
+    snd_trident_ac97_control_get,
+    snd_trident_ac97_control_put,
+    4,
+};
+
+/*---------------------------------------------------------------------------
+ snd_trident_vol_control
+
+ Description: wave & music volume control
+ ---------------------------------------------------------------------------*/
+
+static int snd_trident_vol_control_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+    uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+    uinfo->count = 2;
+    uinfo->value.integer.min = 0;
+    uinfo->value.integer.max = 255;
+    return 0;
+}
+
+static int snd_trident_vol_control_get(snd_kcontrol_t * kcontrol,
+                                       snd_ctl_elem_value_t * ucontrol)
+{
+    struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
+    unsigned int val;
+
+    val = trident->musicvol_wavevol;
+    ucontrol->value.integer.value[0] = 255 - ((val >> kcontrol->private_value) & 0xff);
+    ucontrol->value.integer.value[1] = 255 - ((val >> (kcontrol->private_value + 8)) & 0xff);
+    return 0;
+}
+
+static int snd_trident_vol_control_put(snd_kcontrol_t * kcontrol,
+                                       snd_ctl_elem_value_t * ucontrol)
+{
+    struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
+    unsigned int val;
+    int change = 0;
+
+    spin_lock_irq(&trident->reg_lock);
+    val = trident->musicvol_wavevol;
+    val &= ~(0xffff << kcontrol->private_value);
+    val |= ((255 - (ucontrol->value.integer.value[0] & 0xff)) |
+            ((255 - (ucontrol->value.integer.value[1] & 0xff)) << 8)) << kcontrol->private_value;
+    change = val != trident->musicvol_wavevol;
+    outl(trident->musicvol_wavevol = val, TRID_REG(trident, T4D_MUSICVOL_WAVEVOL));
+    spin_unlock_irq(&trident->reg_lock);
+    return change;
+}
+
+
+static snd_kcontrol_new_t snd_trident_vol_music_control __devinitdata =
+{
+    SNDRV_CTL_ELEM_IFACE_MIXER,0,0,
+    "Music Playback Volume",0,0,0,
+    snd_trident_vol_control_info,
+    snd_trident_vol_control_get,
+    snd_trident_vol_control_put,
+    16,
+};
+
+
+static snd_kcontrol_new_t snd_trident_vol_wave_control __devinitdata =
+{
+    SNDRV_CTL_ELEM_IFACE_MIXER,0,0,
+    "Wave Playback Volume",0,0,0,
+    snd_trident_vol_control_info,
+    snd_trident_vol_control_get,
+    snd_trident_vol_control_put,
+    0,
+};
+
+/*---------------------------------------------------------------------------
+ snd_trident_pcm_vol_control
+
+ Description: PCM front volume control
+ ---------------------------------------------------------------------------*/
+
+static int snd_trident_pcm_vol_control_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+    struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
+
+    uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+    uinfo->count = 1;
+    uinfo->value.integer.min = 0;
+    uinfo->value.integer.max = 255;
+    if (trident->device == TRIDENT_DEVICE_ID_SI7018)
+        uinfo->value.integer.max = 1023;
+    return 0;
+}
+
+static int snd_trident_pcm_vol_control_get(snd_kcontrol_t * kcontrol,
+                                           snd_ctl_elem_value_t * ucontrol)
+{
+    struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
+    struct snd_trident_pcm_mixer*mix = &trident->pcm_mixer[snd_ctl_get_ioffnum(kcontrol, &ucontrol->id)];
+
+    if (trident->device == TRIDENT_DEVICE_ID_SI7018) {
+        ucontrol->value.integer.value[0] = 1023 - mix->vol;
+    } else {
+        ucontrol->value.integer.value[0] = 255 - (mix->vol>>2);
+    }
+    return 0;
+}
+
+static int snd_trident_pcm_vol_control_put(snd_kcontrol_t * kcontrol,
+                                           snd_ctl_elem_value_t * ucontrol)
+{
+    struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
+    struct snd_trident_pcm_mixer*mix = &trident->pcm_mixer[snd_ctl_get_ioffnum(kcontrol, &ucontrol->id)];
+    unsigned int val;
+    int change = 0;
+
+    if (trident->device == TRIDENT_DEVICE_ID_SI7018) {
+        val = 1023 - (ucontrol->value.integer.value[0] & 1023);
+    } else {
+        val = (255 - (ucontrol->value.integer.value[0] & 255)) << 2;
+    }
+    spin_lock_irq(&trident->reg_lock);
+    change = val != mix->vol;
+    mix->vol = val;
+    if (mix->voice != NULL)
+        snd_trident_write_vol_reg(trident, mix->voice, val);
+    spin_unlock_irq(&trident->reg_lock);
+    return change;
+}
+
+static snd_kcontrol_new_t snd_trident_pcm_vol_control __devinitdata =
+{
+    SNDRV_CTL_ELEM_IFACE_MIXER,0,0,
+    "PCM Front Playback Volume",0,
+    SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+    32,
+    snd_trident_pcm_vol_control_info,
+    snd_trident_pcm_vol_control_get,
+    snd_trident_pcm_vol_control_put,0
+};
+
+/*---------------------------------------------------------------------------
+ snd_trident_pcm_pan_control
+
+ Description: PCM front pan control
+ ---------------------------------------------------------------------------*/
+
+static int snd_trident_pcm_pan_control_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+    uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+    uinfo->count = 1;
+    uinfo->value.integer.min = 0;
+    uinfo->value.integer.max = 127;
+    return 0;
+}
+
+static int snd_trident_pcm_pan_control_get(snd_kcontrol_t * kcontrol,
+                                           snd_ctl_elem_value_t * ucontrol)
+{
+    struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
+    struct snd_trident_pcm_mixer*mix = &trident->pcm_mixer[snd_ctl_get_ioffnum(kcontrol, &ucontrol->id)];
+
+    ucontrol->value.integer.value[0] = mix->pan;
+    if (ucontrol->value.integer.value[0] & 0x40) {
+        ucontrol->value.integer.value[0] = (0x3f - (ucontrol->value.integer.value[0] & 0x3f));
+    } else {
+        ucontrol->value.integer.value[0] |= 0x40;
+    }
+    return 0;
+}
+
+static int snd_trident_pcm_pan_control_put(snd_kcontrol_t * kcontrol,
+                                           snd_ctl_elem_value_t * ucontrol)
+{
+    struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
+    struct snd_trident_pcm_mixer*mix = &trident->pcm_mixer[snd_ctl_get_ioffnum(kcontrol, &ucontrol->id)];
+    unsigned char val;
+    int change = 0;
+
+    if (ucontrol->value.integer.value[0] & 0x40)
+        val = ucontrol->value.integer.value[0] & 0x3f;
+    else
+        val = (0x3f - (ucontrol->value.integer.value[0] & 0x3f)) | 0x40;
+    spin_lock_irq(&trident->reg_lock);
+    change = val != mix->pan;
+    mix->pan = val;
+    if (mix->voice != NULL)
+        snd_trident_write_pan_reg(trident, mix->voice, val);
+    spin_unlock_irq(&trident->reg_lock);
+    return change;
+}
+
+static snd_kcontrol_new_t snd_trident_pcm_pan_control __devinitdata =
+{
+    SNDRV_CTL_ELEM_IFACE_MIXER,0,0,
+    "PCM Pan Playback Control",0,
+    SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+    32,
+    snd_trident_pcm_pan_control_info,
+    snd_trident_pcm_pan_control_get,
+    snd_trident_pcm_pan_control_put,0
+};
+
+/*---------------------------------------------------------------------------
+ snd_trident_pcm_rvol_control
+
+ Description: PCM reverb volume control
+ ---------------------------------------------------------------------------*/
+
+static int snd_trident_pcm_rvol_control_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+    uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+    uinfo->count = 1;
+    uinfo->value.integer.min = 0;
+    uinfo->value.integer.max = 127;
+    return 0;
+}
+
+static int snd_trident_pcm_rvol_control_get(snd_kcontrol_t * kcontrol,
+                                            snd_ctl_elem_value_t * ucontrol)
+{
+    struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
+    struct snd_trident_pcm_mixer*mix = &trident->pcm_mixer[snd_ctl_get_ioffnum(kcontrol, &ucontrol->id)];
+
+    ucontrol->value.integer.value[0] = 127 - mix->rvol;
+    return 0;
+}
+
+static int snd_trident_pcm_rvol_control_put(snd_kcontrol_t * kcontrol,
+                                            snd_ctl_elem_value_t * ucontrol)
+{
+    struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
+    struct snd_trident_pcm_mixer*mix = &trident->pcm_mixer[snd_ctl_get_ioffnum(kcontrol, &ucontrol->id)];
+    unsigned short val;
+    int change = 0;
+
+    val = 0x7f - (ucontrol->value.integer.value[0] & 0x7f);
+    spin_lock_irq(&trident->reg_lock);
+    change = val != mix->rvol;
+    mix->rvol = val;
+    if (mix->voice != NULL)
+        snd_trident_write_rvol_reg(trident, mix->voice, val);
+    spin_unlock_irq(&trident->reg_lock);
+    return change;
+}
+
+static snd_kcontrol_new_t snd_trident_pcm_rvol_control __devinitdata =
+{
+    SNDRV_CTL_ELEM_IFACE_MIXER,0,0,
+    "PCM Reverb Playback Volume",0,
+    SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+    32,
+    snd_trident_pcm_rvol_control_info,
+    snd_trident_pcm_rvol_control_get,
+    snd_trident_pcm_rvol_control_put,0
+};
+
+/*---------------------------------------------------------------------------
+ snd_trident_pcm_cvol_control
+
+ Description: PCM chorus volume control
+ ---------------------------------------------------------------------------*/
+
+static int snd_trident_pcm_cvol_control_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+    uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+    uinfo->count = 1;
+    uinfo->value.integer.min = 0;
+    uinfo->value.integer.max = 127;
+    return 0;
+}
+
+static int snd_trident_pcm_cvol_control_get(snd_kcontrol_t * kcontrol,
+                                            snd_ctl_elem_value_t * ucontrol)
+{
+    struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
+    struct snd_trident_pcm_mixer*mix = &trident->pcm_mixer[snd_ctl_get_ioffnum(kcontrol, &ucontrol->id)];
+
+    ucontrol->value.integer.value[0] = 127 - mix->cvol;
+    return 0;
+}
+
+static int snd_trident_pcm_cvol_control_put(snd_kcontrol_t * kcontrol,
+                                            snd_ctl_elem_value_t * ucontrol)
+{
+    struct snd_trident *trident = snd_kcontrol_chip(kcontrol);
+    struct snd_trident_pcm_mixer*mix = &trident->pcm_mixer[snd_ctl_get_ioffnum(kcontrol, &ucontrol->id)];
+    unsigned short val;
+    int change = 0;
+
+    val = 0x7f - (ucontrol->value.integer.value[0] & 0x7f);
+    spin_lock_irq(&trident->reg_lock);
+    change = val != mix->cvol;
+    mix->cvol = val;
+    if (mix->voice != NULL)
+        snd_trident_write_cvol_reg(trident, mix->voice, val);
+    spin_unlock_irq(&trident->reg_lock);
+    return change;
+}
+
+static snd_kcontrol_new_t snd_trident_pcm_cvol_control __devinitdata =
+{
+    SNDRV_CTL_ELEM_IFACE_MIXER,0,0,
+    "PCM Chorus Playback Volume",0,
+    SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+    32,
+    snd_trident_pcm_cvol_control_info,
+    snd_trident_pcm_cvol_control_get,
+    snd_trident_pcm_cvol_control_put,0
+};
+
+static void snd_trident_notify_pcm_change1(snd_card_t * card, snd_kcontrol_t *kctl, int num, int activate)
+{
+    snd_ctl_elem_id_t id;
+
+    snd_runtime_check(kctl != NULL, return);
+    if (activate)
+        kctl->vd[num].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+    else
+        kctl->vd[num].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+    snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE |
+                   SNDRV_CTL_EVENT_MASK_INFO,
+                   snd_ctl_build_ioff(&id, kctl, num));
+}
+
+static void snd_trident_notify_pcm_change(struct snd_trident *trident, struct snd_trident_pcm_mixer*tmix, int num, int activate)
+{
+    snd_trident_notify_pcm_change1(trident->card, trident->ctl_vol, num, activate);
+    snd_trident_notify_pcm_change1(trident->card, trident->ctl_pan, num, activate);
+    snd_trident_notify_pcm_change1(trident->card, trident->ctl_rvol, num, activate);
+    snd_trident_notify_pcm_change1(trident->card, trident->ctl_cvol, num, activate);
+}
+
+static int snd_trident_pcm_mixer_build(struct snd_trident *trident, struct snd_trident_voice*voice, snd_pcm_substream_t *substream)
+{
+    struct snd_trident_pcm_mixer*tmix;
+
+    snd_assert(trident != NULL && voice != NULL && substream != NULL, return -EINVAL);
+    tmix = &trident->pcm_mixer[substream->number];
+    tmix->voice = voice;
+    tmix->vol = T4D_DEFAULT_PCM_VOL;
+    tmix->pan = T4D_DEFAULT_PCM_PAN;
+    tmix->rvol = T4D_DEFAULT_PCM_RVOL;
+    tmix->cvol = T4D_DEFAULT_PCM_CVOL;
+    snd_trident_notify_pcm_change(trident, tmix, substream->number, 1);
+    return 0;
+}
+
+static int snd_trident_pcm_mixer_free(struct snd_trident *trident, struct snd_trident_voice*voice, snd_pcm_substream_t *substream)
+{
+    struct snd_trident_pcm_mixer*tmix;
+
+    snd_assert(trident != NULL && substream != NULL, return -EINVAL);
+    tmix = &trident->pcm_mixer[substream->number];
+    tmix->voice = NULL;
+    snd_trident_notify_pcm_change(trident, tmix, substream->number, 0);
+    return 0;
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_mixer
+
+ Description: This routine registers the 4DWave device for mixer support.
+
+ Paramters:   trident - pointer to target device class for 4DWave.
+
+ Returns:     None
+
+ ---------------------------------------------------------------------------*/
+
+static int __devinit snd_trident_mixer(struct snd_trident * trident, int pcm_spdif_device)
+{
+    ac97_template_t _ac97;
+    snd_card_t * card = trident->card;
+    snd_kcontrol_t *kctl;
+    snd_ctl_elem_value_t *uctl;
+    int idx, err, retries = 2;
+    static ac97_bus_ops_t ops = {
+        0,snd_trident_codec_write,
+        snd_trident_codec_read,0,0
+    };
+    uctl = kcalloc(1, sizeof(*uctl), GFP_KERNEL);
+    if (!uctl)
+        return -ENOMEM;
+
+    if ((err = snd_ac97_bus(trident->card, 0, &ops, NULL, &trident->ac97_bus)) < 0)
+        goto __out;
+
+    memset(&_ac97, 0, sizeof(_ac97));
+    _ac97.private_data = trident;
+    trident->ac97_detect = 1;
+
+__again:
+    if ((err = snd_ac97_mixer(trident->ac97_bus, &_ac97, &trident->ac97)) < 0) {
+        if (trident->device == TRIDENT_DEVICE_ID_SI7018) {
+            if ((err = snd_trident_sis_reset(trident)) < 0)
+                goto __out;
+            if (retries-- > 0)
+                goto __again;
+            err = -EIO;
+        }
+        goto __out;
+    }
+    printk("trident ac'97 codec detected\n");
+#if 0 // vladest
+    /* secondary codec? */
+    if (trident->device == TRIDENT_DEVICE_ID_SI7018 &&
+        (inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL)) & SI_AC97_PRIMARY_READY) != 0) {
+        _ac97.num = 1;
+        err = snd_ac97_mixer(trident->ac97_bus, &_ac97, &trident->ac97_sec);
+        if (err < 0)
+            snd_printk("SI7018: the secondary codec - invalid access\n");
+#if 0	// only for my testing purpose --jk
+        {
+            ac97_t *mc97;
+            err = snd_ac97_modem(trident->card, &_ac97, &mc97);
+            if (err < 0)
+                snd_printk("snd_ac97_modem returned error %i\n", err);
+        }
+#endif
+    }
+#endif
+    trident->ac97_detect = 0;
+
+    if (trident->device != TRIDENT_DEVICE_ID_SI7018) {
+        if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_trident_vol_wave_control, trident))) < 0)
+            goto __out;
+        kctl->put(kctl, uctl);
+        if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_trident_vol_music_control, trident))) < 0)
+            goto __out;
+        kctl->put(kctl, uctl);
+        outl(trident->musicvol_wavevol = 0x00000000, TRID_REG(trident, T4D_MUSICVOL_WAVEVOL));
+    } else {
+        outl(trident->musicvol_wavevol = 0xffff0000, TRID_REG(trident, T4D_MUSICVOL_WAVEVOL));
+    }
+
+    for (idx = 0; idx < 32; idx++) {
+        struct snd_trident_pcm_mixer*tmix;
+
+        tmix = &trident->pcm_mixer[idx];
+        tmix->voice = NULL;
+    }
+    if ((trident->ctl_vol = snd_ctl_new1(&snd_trident_pcm_vol_control, trident)) == NULL)
+        goto __nomem;
+    if ((err = snd_ctl_add(card, trident->ctl_vol)))
+        goto __out;
+
+    if ((trident->ctl_pan = snd_ctl_new1(&snd_trident_pcm_pan_control, trident)) == NULL)
+        goto __nomem;
+    if ((err = snd_ctl_add(card, trident->ctl_pan)))
+        goto __out;
+
+    if ((trident->ctl_rvol = snd_ctl_new1(&snd_trident_pcm_rvol_control, trident)) == NULL)
+        goto __nomem;
+    if ((err = snd_ctl_add(card, trident->ctl_rvol)))
+        goto __out;
+
+    if ((trident->ctl_cvol = snd_ctl_new1(&snd_trident_pcm_cvol_control, trident)) == NULL)
+        goto __nomem;
+    if ((err = snd_ctl_add(card, trident->ctl_cvol)))
+        goto __out;
+
+    if (trident->device == TRIDENT_DEVICE_ID_NX) {
+        if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_trident_ac97_rear_control, trident))) < 0)
+            goto __out;
+        kctl->put(kctl, uctl);
+    }
+    if (trident->device == TRIDENT_DEVICE_ID_NX || trident->device == TRIDENT_DEVICE_ID_SI7018) {
+        kctl = snd_ctl_new1(&snd_trident_spdif_control, trident);
+        if (kctl == NULL) {
+            err = -ENOMEM;
+            goto __out;
+        }
+        if (trident->ac97->ext_id & AC97_EI_SPDIF)
+            kctl->id.index++;
+        if (trident->ac97_sec && (trident->ac97_sec->ext_id & AC97_EI_SPDIF))
+            kctl->id.index++;
+        idx = kctl->id.index;
+        if ((err = snd_ctl_add(card, kctl)) < 0)
+            goto __out;
+        kctl->put(kctl, uctl);
+        kctl = snd_ctl_new1(&snd_trident_spdif_default, trident);
+        if (kctl == NULL) {
+            err = -ENOMEM;
+            goto __out;
+        }
+        kctl->id.index = idx;
+        kctl->id.device = pcm_spdif_device;
+        if ((err = snd_ctl_add(card, kctl)) < 0)
+            goto __out;
+        kctl = snd_ctl_new1(&snd_trident_spdif_mask, trident);
+        if (kctl == NULL) {
+            err = -ENOMEM;
+            goto __out;
+        }
+        kctl->id.index = idx;
+        kctl->id.device = pcm_spdif_device;
+        if ((err = snd_ctl_add(card, kctl)) < 0)
+            goto __out;
+        kctl = snd_ctl_new1(&snd_trident_spdif_stream, trident);
+        if (kctl == NULL) {
+            err = -ENOMEM;
+            goto __out;
+        }
+        kctl->id.index = idx;
+        kctl->id.device = pcm_spdif_device;
+        if ((err = snd_ctl_add(card, kctl)) < 0)
+            goto __out;
+        trident->spdif_pcm_ctl = kctl;
+    }
+
+    err = 0;
+    goto __out;
+
+    __nomem:
+        err = -ENOMEM;
+
+        __out:
+            kfree(uctl);
+
+            return err;
+}
+
+/*
+ * gameport interface
+ */
+
+#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
+
+typedef struct snd_trident_gameport {
+    struct gameport info;
+    struct snd_trident *chip;
+} trident_gameport_t;
+
+static unsigned char snd_trident_gameport_read(struct gameport *gameport)
+{
+    trident_gameport_t *gp = (trident_gameport_t *)gameport;
+    struct snd_trident *chip;
+    snd_assert(gp, return 0);
+    chip = gp->chip;
+    return inb(TRID_REG(chip, GAMEPORT_LEGACY));
+}
+
+static void snd_trident_gameport_trigger(struct gameport *gameport)
+{
+    trident_gameport_t *gp = (trident_gameport_t *)gameport;
+    struct snd_trident *chip;
+    snd_assert(gp, return);
+    chip = gp->chip;
+    outb(0xff, TRID_REG(chip, GAMEPORT_LEGACY));
+}
+
+static int snd_trident_gameport_cooked_read(struct gameport *gameport, int *axes, int *buttons)
+{
+    trident_gameport_t *gp = (trident_gameport_t *)gameport;
+    struct snd_trident *chip;
+    int i;
+
+    snd_assert(gp, return 0);
+    chip = gp->chip;
+
+    *buttons = (~inb(TRID_REG(chip, GAMEPORT_LEGACY)) >> 4) & 0xf;
+
+    for (i = 0; i < 4; i++) {
+        axes[i] = inw(TRID_REG(chip, GAMEPORT_AXES + i * 2));
+        if (axes[i] == 0xffff) axes[i] = -1;
+    }
+
+    return 0;
+}
+
+static int snd_trident_gameport_open(struct gameport *gameport, int mode)
+{
+    trident_gameport_t *gp = (trident_gameport_t *)gameport;
+    struct snd_trident *chip;
+    snd_assert(gp, return -1);
+    chip = gp->chip;
+
+    switch (mode) {
+    case GAMEPORT_MODE_COOKED:
+        outb(GAMEPORT_MODE_ADC, TRID_REG(chip, GAMEPORT_GCR));
+        msleep(20);
+        return 0;
+    case GAMEPORT_MODE_RAW:
+        outb(0, TRID_REG(chip, GAMEPORT_GCR));
+        return 0;
+    default:
+        return -1;
+    }
+}
+
+void __devinit snd_trident_gameport(struct snd_trident *chip)
+{
+    trident_gameport_t *gp;
+    gp = kmalloc(sizeof(*gp), GFP_KERNEL);
+    if (! gp) {
+        snd_printk("cannot allocate gameport area\n");
+        return;
+    }
+    memset(gp, 0, sizeof(*gp));
+    gp->chip = chip;
+    gp->info.fuzz = 64;
+    gp->info.read = snd_trident_gameport_read;
+    gp->info.trigger = snd_trident_gameport_trigger;
+    gp->info.cooked_read = snd_trident_gameport_cooked_read;
+    gp->info.open = snd_trident_gameport_open;
+    chip->gameport = gp;
+
+    gameport_register_port(&gp->info);
+}
+
+#else
+void __devinit snd_trident_gameport(struct snd_trident *chip)
+{
+}
+#endif /* CONFIG_GAMEPORT */
+
+/*
+ * delay for 1 tick
+ */
+inline static void do_delay(struct snd_trident *chip)
+{
+    set_current_state(TASK_UNINTERRUPTIBLE);
+    schedule_timeout(1);
+}
+
+/*
+ *  SiS reset routine
+ */
+
+static int snd_trident_sis_reset(struct snd_trident *trident)
+{
+    unsigned long end_time;
+    unsigned int i;
+    int r;
+
+    r = trident->in_suspend ? 0 : 2;	/* count of retries */
+__si7018_retry:
+    pci_write_config_byte(trident->pci, 0x46, 0x04);	/* SOFTWARE RESET */
+    udelay(100);
+    pci_write_config_byte(trident->pci, 0x46, 0x00);
+    udelay(100);
+    /* disable AC97 GPIO interrupt */
+    outb(0x00, TRID_REG(trident, SI_AC97_GPIO));
+    /* initialize serial interface, force cold reset */
+    i = PCMOUT|SURROUT|CENTEROUT|LFEOUT|SECONDARY_ID|COLD_RESET;
+    outl(i, TRID_REG(trident, SI_SERIAL_INTF_CTRL));
+    udelay(1000);
+    /* remove cold reset */
+    i &= ~COLD_RESET;
+    outl(i, TRID_REG(trident, SI_SERIAL_INTF_CTRL));
+    udelay(2000);
+    /* wait, until the codec is ready */
+    end_time = (jiffies + (HZ * 3) / 4) + 1;
+    do {
+        if ((inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL)) & SI_AC97_PRIMARY_READY) != 0)
+            goto __si7018_ok;
+        do_delay(trident);
+    } while (time_after_eq(end_time, jiffies));
+    snd_printk("AC'97 codec ready error [0x%x]\n", inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL)));
+    if (r-- > 0) {
+        end_time = jiffies + HZ;
+        do {
+            do_delay(trident);
+        } while (time_after_eq(end_time, jiffies));
+        goto __si7018_retry;
+    }
+__si7018_ok:
+    /* wait for the second codec */
+    do {
+        if ((inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL)) & SI_AC97_SECONDARY_READY) != 0)
+            break;
+        do_delay(trident);
+    } while (time_after_eq(end_time, jiffies));
+    /* enable 64 channel mode */
+    outl(BANK_B_EN, TRID_REG(trident, T4D_LFO_GC_CIR));
+    return 0;
+}
+
+/*
+ *  /proc interface
+ */
+
+static void snd_trident_proc_read(snd_info_entry_t *entry,
+                                  snd_info_buffer_t * buffer)
+{
+    struct snd_trident *trident = entry->private_data;
+    char *s;
+
+    switch (trident->device) {
+    case TRIDENT_DEVICE_ID_SI7018:
+        s = "SiS 7018 Audio";
+        break;
+    case TRIDENT_DEVICE_ID_DX:
+        s = "Trident 4DWave PCI DX";
+        break;
+    case TRIDENT_DEVICE_ID_NX:
+        s = "Trident 4DWave PCI NX";
+        break;
+    default:
+        s = "???";
+    }
+    snd_iprintf(buffer, "%s\n\n", s);
+    snd_iprintf(buffer, "Spurious IRQs    : %d\n", trident->spurious_irq_count);
+    snd_iprintf(buffer, "Spurious IRQ dlta: %d\n", trident->spurious_irq_max_delta);
+    if (trident->device == TRIDENT_DEVICE_ID_NX || trident->device == TRIDENT_DEVICE_ID_SI7018)
+        snd_iprintf(buffer, "IEC958 Mixer Out : %s\n", trident->spdif_ctrl == 0x28 ? "on" : "off");
+    if (trident->device == TRIDENT_DEVICE_ID_NX) {
+        snd_iprintf(buffer, "Rear Speakers    : %s\n", trident->ac97_ctrl & 0x00000010 ? "on" : "off");
+        if (trident->tlb.entries) {
+            snd_iprintf(buffer,"\nVirtual Memory\n");
+            snd_iprintf(buffer, "Memory Maximum : %d\n", trident->tlb.memhdr->size);
+            snd_iprintf(buffer, "Memory Used    : %d\n", trident->tlb.memhdr->used);
+            snd_iprintf(buffer, "Memory Free    : %d\n", snd_util_mem_avail(trident->tlb.memhdr));
+        }
+    }
+#ifdef CONFIG_SND_SEQUENCER
+    snd_iprintf(buffer,"\nWavetable Synth\n");
+    snd_iprintf(buffer, "Memory Maximum : %d\n", trident->synth.max_size);
+    snd_iprintf(buffer, "Memory Used    : %d\n", trident->synth.current_size);
+    snd_iprintf(buffer, "Memory Free    : %d\n", (trident->synth.max_size-trident->synth.current_size));
+#endif
+}
+
+static void __devinit snd_trident_proc_init(struct snd_trident * trident)
+{
+    snd_info_entry_t *entry;
+    const char *s = "trident";
+
+    if (trident->device == TRIDENT_DEVICE_ID_SI7018)
+        s = "sis7018";
+    if (! snd_card_proc_new(trident->card, s, &entry))
+        snd_info_set_text_ops(entry, trident, 1024, snd_trident_proc_read);
+}
+
+static int snd_trident_dev_free(snd_device_t *device)
+{
+    struct snd_trident *trident = device->device_data;
+    return snd_trident_free(trident);
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_tlb_alloc
+
+ Description: Allocate and set up the TLB page table on 4D NX.
+ Each entry has 4 bytes (physical PCI address).
+
+ Paramters:   trident - pointer to target device class for 4DWave.
+
+ Returns:     0 or negative error code
+
+ ---------------------------------------------------------------------------*/
+
+static int __devinit snd_trident_tlb_alloc(struct snd_trident *trident)
+{
+	int i;
+
+	/* TLB array must be aligned to 16kB !!! so we allocate
+	   32kB region and correct offset when necessary */
+
+	if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(trident->pci),
+				2 * SNDRV_TRIDENT_MAX_PAGES * 4, &trident->tlb.buffer) < 0) {
+		snd_printk(KERN_ERR "trident: unable to allocate TLB buffer\n");
+		return -ENOMEM;
+	}
+	trident->tlb.entries = (unsigned int*)(((unsigned long)trident->tlb.buffer.area + SNDRV_TRIDENT_MAX_PAGES * 4 - 1) & ~(SNDRV_TRIDENT_MAX_PAGES * 4 - 1));
+	trident->tlb.entries_dmaaddr = (trident->tlb.buffer.addr + SNDRV_TRIDENT_MAX_PAGES * 4 - 1) & ~(SNDRV_TRIDENT_MAX_PAGES * 4 - 1);
+	/* allocate shadow TLB page table (virtual addresses) */
+	trident->tlb.shadow_entries = (unsigned long *)vmalloc(SNDRV_TRIDENT_MAX_PAGES*sizeof(unsigned long));
+	if (trident->tlb.shadow_entries == NULL) {
+		snd_printk(KERN_ERR "trident: unable to allocate shadow TLB entries\n");
+		return -ENOMEM;
+	}
+	/* allocate and setup silent page and initialise TLB entries */
+	if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(trident->pci),
+				SNDRV_TRIDENT_PAGE_SIZE, &trident->tlb.silent_page) < 0) {
+		snd_printk(KERN_ERR "trident: unable to allocate silent page\n");
+		return -ENOMEM;
+	}
+	memset(trident->tlb.silent_page.area, 0, SNDRV_TRIDENT_PAGE_SIZE);
+	for (i = 0; i < SNDRV_TRIDENT_MAX_PAGES; i++) {
+		trident->tlb.entries[i] = cpu_to_le32(trident->tlb.silent_page.addr & ~(SNDRV_TRIDENT_PAGE_SIZE-1));
+		trident->tlb.shadow_entries[i] = (unsigned long)trident->tlb.silent_page.area;
+	}
+
+	/* use emu memory block manager code to manage tlb page allocation */
+	trident->tlb.memhdr = snd_util_memhdr_new(SNDRV_TRIDENT_PAGE_SIZE * SNDRV_TRIDENT_MAX_PAGES);
+	if (trident->tlb.memhdr == NULL)
+		return -ENOMEM;
+
+	trident->tlb.memhdr->block_extra_size = sizeof(struct snd_trident_memblk_arg);
+	return 0;
+}
+
+/*
+ * initialize 4D DX chip
+ */
+
+static void snd_trident_stop_all_voices(struct snd_trident *trident)
+{
+    outl(0xffffffff, TRID_REG(trident, T4D_STOP_A));
+    outl(0xffffffff, TRID_REG(trident, T4D_STOP_B));
+    outl(0, TRID_REG(trident, T4D_AINTEN_A));
+    outl(0, TRID_REG(trident, T4D_AINTEN_B));
+}
+
+static int snd_trident_4d_dx_init(struct snd_trident *trident)
+{
+    struct pci_dev *pci = trident->pci;
+    unsigned long end_time;
+
+    /* reset the legacy configuration and whole audio/wavetable block */
+    pci_write_config_dword(pci, 0x40, 0);	/* DDMA */
+    pci_write_config_byte(pci, 0x44, 0);	/* ports */
+    pci_write_config_byte(pci, 0x45, 0);	/* Legacy DMA */
+    pci_write_config_byte(pci, 0x46, 4); /* reset */
+    udelay(100);
+    pci_write_config_byte(pci, 0x46, 0); /* release reset */
+    udelay(100);
+
+    /* warm reset of the AC'97 codec */
+    outl(0x00000001, TRID_REG(trident, DX_ACR2_AC97_COM_STAT));
+    udelay(100);
+    outl(0x00000000, TRID_REG(trident, DX_ACR2_AC97_COM_STAT));
+    /* DAC on, disable SB IRQ and try to force ADC valid signal */
+    trident->ac97_ctrl = 0x0000004a;
+    outl(trident->ac97_ctrl, TRID_REG(trident, DX_ACR2_AC97_COM_STAT));
+    /* wait, until the codec is ready */
+    end_time = (jiffies + (HZ * 3) / 4) + 1;
+    do {
+        if ((inl(TRID_REG(trident, DX_ACR2_AC97_COM_STAT)) & 0x0010) != 0)
+            goto __dx_ok;
+        do_delay(trident);
+    } while (time_after_eq(end_time, jiffies));
+    snd_printk(KERN_ERR "AC'97 codec ready error\n");
+    return -EIO;
+
+    __dx_ok:
+        snd_trident_stop_all_voices(trident);
+
+        return 0;
+}
+
+/*
+ * initialize 4D NX chip
+ */
+static int snd_trident_4d_nx_init(struct snd_trident *trident)
+{
+    struct pci_dev *pci = trident->pci;
+    unsigned long end_time;
+
+    /* reset the legacy configuration and whole audio/wavetable block */
+    pci_write_config_dword(pci, 0x40, 0);	/* DDMA */
+    pci_write_config_byte(pci, 0x44, 0);	/* ports */
+    pci_write_config_byte(pci, 0x45, 0);	/* Legacy DMA */
+
+    pci_write_config_byte(pci, 0x46, 1); /* reset */
+    udelay(100);
+    pci_write_config_byte(pci, 0x46, 0); /* release reset */
+    udelay(100);
+
+    /* warm reset of the AC'97 codec */
+    outl(0x00000001, TRID_REG(trident, NX_ACR0_AC97_COM_STAT));
+    udelay(100);
+    outl(0x00000000, TRID_REG(trident, NX_ACR0_AC97_COM_STAT));
+    /* wait, until the codec is ready */
+    end_time = (jiffies + (HZ * 3) / 4) + 1;
+    do {
+        if ((inl(TRID_REG(trident, NX_ACR0_AC97_COM_STAT)) & 0x0008) != 0)
+            goto __nx_ok;
+        do_delay(trident);
+    } while (time_after_eq(end_time, jiffies));
+    snd_printk(KERN_ERR "AC'97 codec ready error [0x%x]\n", inl(TRID_REG(trident, NX_ACR0_AC97_COM_STAT)));
+    return -EIO;
+
+    __nx_ok:
+        /* DAC on */
+        trident->ac97_ctrl = 0x00000002;
+        outl(trident->ac97_ctrl, TRID_REG(trident, NX_ACR0_AC97_COM_STAT));
+        /* disable SB IRQ */
+        outl(NX_SB_IRQ_DISABLE, TRID_REG(trident, T4D_MISCINT));
+
+        snd_trident_stop_all_voices(trident);
+
+        if (trident->tlb.entries != NULL) {
+            unsigned int i;
+            /* enable virtual addressing via TLB */
+            i = trident->tlb.entries_dmaaddr;
+            i |= 0x00000001;
+            outl(i, TRID_REG(trident, NX_TLBC));
+        } else {
+            outl(0, TRID_REG(trident, NX_TLBC));
+        }
+        /* initialize S/PDIF */
+        outl(trident->spdif_bits, TRID_REG(trident, NX_SPCSTATUS));
+        outb(trident->spdif_ctrl, TRID_REG(trident, NX_SPCTRL_SPCSO + 3));
+
+        return 0;
+}
+
+/*
+ * initialize sis7018 chip
+ */
+static int snd_trident_sis_init(struct snd_trident *trident)
+{
+    int err;
+
+    if ((err = snd_trident_sis_reset(trident)) < 0)
+        return err;
+
+    snd_trident_stop_all_voices(trident);
+
+    /* initialize S/PDIF */
+    outl(trident->spdif_bits, TRID_REG(trident, SI_SPDIF_CS));
+
+    return 0;
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_create
+
+ Description: This routine will create the device specific class for
+ the 4DWave card. It will also perform basic initialization.
+
+ Paramters:   card  - which card to create
+ pci   - interface to PCI bus resource info
+ dma1ptr - playback dma buffer
+ dma2ptr - capture dma buffer
+ irqptr  -  interrupt resource info
+
+ Returns:     4DWave device class private data
+
+ ---------------------------------------------------------------------------*/
+
+int __devinit snd_trident_create(snd_card_t * card,
+                                 struct pci_dev *pci,
+                                 int pcm_streams,
+                                 int pcm_spdif_device,
+                                 int max_wavetable_size,
+                                 struct snd_trident ** rtrident)
+{
+    struct snd_trident *trident;
+    int i, err;
+    struct snd_trident_voice*voice;
+    struct snd_trident_pcm_mixer*tmix;
+#ifdef TARGET_OS2
+    static snd_device_ops_t ops = {
+        snd_trident_dev_free,0,0,0
+    };
+#endif
+    *rtrident = NULL;
+
+    /* enable PCI device */
+    if ((err = pci_enable_device(pci)) < 0)
+        return err;
+    /* check, if we can restrict PCI DMA transfers to 30 bits */
+    if (!pci_dma_supported(pci, 0x3fffffff)) {
+        snd_printk("architecture does not support 30bit PCI busmaster DMA\n");
+        return -ENXIO;
+    }
+    pci_set_dma_mask(pci, 0x3fffffff);
+
+    trident = kcalloc(1, sizeof(*trident), GFP_KERNEL);
+    if (trident == NULL)
+        return -ENOMEM;
+    trident->device = (pci->vendor << 16) | pci->device;
+    trident->card = card;
+    trident->pci = pci;
+    spin_lock_init(&trident->reg_lock);
+    spin_lock_init(&trident->event_lock);
+    spin_lock_init(&trident->voice_alloc);
+    if (pcm_streams < 1)
+        pcm_streams = 1;
+    if (pcm_streams > 32)
+        pcm_streams = 32;
+    trident->ChanPCM = pcm_streams;
+    if (max_wavetable_size < 0 )
+        max_wavetable_size = 0;
+    trident->synth.max_size = max_wavetable_size * 1024;
+    trident->irq = -1;
+
+    trident->midi_port = TRID_REG(trident, T4D_MPU401_BASE);
+    pci_set_master(pci);
+
+    if ((err = pci_request_regions(pci, "Trident Audio")) < 0) {
+        kfree(trident);
+        return err;
+    }
+    trident->port = pci_resource_start(pci, 0);
+
+    if (request_irq(pci->irq, snd_trident_interrupt, SA_INTERRUPT|SA_SHIRQ, "Trident Audio", (void *) trident)) {
+        snd_printk("unable to grab IRQ %d\n", pci->irq);
+        snd_trident_free(trident);
+        return -EBUSY;
+    }
+    trident->irq = pci->irq;
+
+    /* allocate 16k-aligned TLB for NX cards */
+    trident->tlb.entries = NULL;
+    trident->tlb.buffer.area = NULL;
+    if (trident->device == TRIDENT_DEVICE_ID_NX) {
+        if ((err = snd_trident_tlb_alloc(trident)) < 0) {
+            snd_trident_free(trident);
+            return err;
+        }
+    }
+
+    trident->spdif_bits = trident->spdif_pcm_bits = SNDRV_PCM_DEFAULT_CON_SPDIF;
+
+    /* initialize chip */
+    switch (trident->device) {
+    case TRIDENT_DEVICE_ID_DX:
+        err = snd_trident_4d_dx_init(trident);
+        break;
+    case TRIDENT_DEVICE_ID_NX:
+        err = snd_trident_4d_nx_init(trident);
+        break;
+    case TRIDENT_DEVICE_ID_SI7018:
+        err = snd_trident_sis_init(trident);
+        break;
+    default:
+        snd_BUG();
+        break;
+    }
+    if (err < 0) {
+        snd_trident_free(trident);
+        return err;
+    }
+
+    if ((err = snd_trident_mixer(trident, pcm_spdif_device)) < 0) {
+        snd_trident_free(trident);
+        return err;
+    }
+
+    /* initialise synth voices */
+    for (i = 0; i < 64; i++) {
+        voice = &trident->synth.voices[i];
+        voice->number = i;
+        voice->trident = trident;
+    }
+    /* initialize pcm mixer entries */
+    for (i = 0; i < 32; i++) {
+        tmix = &trident->pcm_mixer[i];
+        tmix->vol = T4D_DEFAULT_PCM_VOL;
+        tmix->pan = T4D_DEFAULT_PCM_PAN;
+        tmix->rvol = T4D_DEFAULT_PCM_RVOL;
+        tmix->cvol = T4D_DEFAULT_PCM_CVOL;
+    }
+
+    snd_trident_enable_eso(trident);
+
+    snd_trident_proc_init(trident);
+    if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, trident, &ops)) < 0) {
+        snd_trident_free(trident);
+        return err;
+    }
+    *rtrident = trident;
+    return 0;
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_free
+
+ Description: This routine will free the device specific class for
+ q    the 4DWave card.
+
+ Paramters:   trident  - device specific private data for 4DWave card
+
+ Returns:     None.
+
+ ---------------------------------------------------------------------------*/
+
+int snd_trident_free(struct snd_trident *trident)
+{
+#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
+	if (trident->gameport) {
+		gameport_unregister_port(&trident->gameport->info);
+		kfree(trident->gameport);
+	}
+#endif
+	snd_trident_disable_eso(trident);
+	// Disable S/PDIF out
+	if (trident->device == TRIDENT_DEVICE_ID_NX)
+		outb(0x00, TRID_REG(trident, NX_SPCTRL_SPCSO + 3));
+	else if (trident->device == TRIDENT_DEVICE_ID_SI7018) {
+		outl(0, TRID_REG(trident, SI_SERIAL_INTF_CTRL));
+	}
+	if (trident->tlb.buffer.area) {
+		outl(0, TRID_REG(trident, NX_TLBC));
+		if (trident->tlb.memhdr)
+			snd_util_memhdr_free(trident->tlb.memhdr);
+		if (trident->tlb.silent_page.area)
+			snd_dma_free_pages(&trident->tlb.silent_page);
+		if (trident->tlb.shadow_entries)
+			vfree(trident->tlb.shadow_entries);
+		snd_dma_free_pages(&trident->tlb.buffer);
+	}
+	if (trident->irq >= 0)
+		free_irq(trident->irq, (void *)trident);
+	pci_release_regions(trident->pci);
+	kfree(trident);
+	return 0;
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_interrupt
+
+ Description: ISR for Trident 4DWave device
+
+ Paramters:   trident  - device specific private data for 4DWave card
+
+ Problems:    It seems that Trident chips generates interrupts more than
+ one time in special cases. The spurious interrupts are
+ detected via sample timer (T4D_STIMER) and computing
+ corresponding delta value. The limits are detected with
+ the method try & fail so it is possible that it won't
+ work on all computers. [jaroslav]
+
+ Returns:     None.
+
+ ---------------------------------------------------------------------------*/
+
+static irqreturn_t snd_trident_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+    struct snd_trident *trident = dev_id;
+    unsigned int audio_int, chn_int, stimer, channel, mask, tmp;
+    int delta;
+    struct snd_trident_voice*voice;
+#ifdef TARGET_OS2
+    int fOurIrq = FALSE;
+#endif
+
+    audio_int = inl(TRID_REG(trident, T4D_MISCINT));
+    if ((audio_int & (ADDRESS_IRQ|MPU401_IRQ)) == 0)
+        return IRQ_NONE;
+    if (audio_int & ADDRESS_IRQ) {
+#ifdef TARGET_OS2
+        fOurIrq = TRUE;
+#endif
+        // get interrupt status for all channels
+        spin_lock(&trident->reg_lock);
+        stimer = inl(TRID_REG(trident, T4D_STIMER)) & 0x00ffffff;
+        chn_int = inl(TRID_REG(trident, T4D_AINT_A));
+        if (chn_int == 0)
+            goto __skip1;
+        outl(chn_int, TRID_REG(trident, T4D_AINT_A));	/* ack */
+    __skip1:
+        chn_int = inl(TRID_REG(trident, T4D_AINT_B));
+        if (chn_int == 0)
+            goto __skip2;
+        for (channel = 63; channel >= 32; channel--) {
+            mask = 1 << (channel&0x1f);
+            if ((chn_int & mask) == 0)
+                continue;
+            voice = &trident->synth.voices[channel];
+            if (!voice->pcm || voice->substream == NULL) {
+                outl(mask, TRID_REG(trident, T4D_STOP_B));
+                continue;
+            }
+            delta = (int)stimer - (int)voice->stimer;
+            if (delta < 0)
+                delta = -delta;
+            if ((unsigned int)delta < voice->spurious_threshold) {
+                /* do some statistics here */
+                trident->spurious_irq_count++;
+                if (trident->spurious_irq_max_delta < (unsigned int)delta)
+                    trident->spurious_irq_max_delta = delta;
+                continue;
+            }
+            voice->stimer = stimer;
+            if (voice->isync) {
+                if (!voice->isync3) {
+                    tmp = inw(TRID_REG(trident, T4D_SBBL_SBCL));
+                    if (trident->bDMAStart & 0x40)
+                        tmp >>= 1;
+                    if (tmp > 0)
+                        tmp = voice->isync_max - tmp;
+                } else {
+                    tmp = inl(TRID_REG(trident, NX_SPCTRL_SPCSO)) & 0x00ffffff;
+                }
+                if (tmp < voice->isync_mark) {
+                    if (tmp > 0x10)
+                        tmp = voice->isync_ESO - 7;
+                    else
+                        tmp = voice->isync_ESO + 2;
+                    /* update ESO for IRQ voice to preserve sync */
+                    snd_trident_stop_voice(trident, voice->number);
+                    snd_trident_write_eso_reg(trident, voice, tmp);
+                    snd_trident_start_voice(trident, voice->number);
+                }
+            } else if (voice->isync2) {
+                voice->isync2 = 0;
+                /* write original ESO and update CSO for IRQ voice to preserve sync */
+                snd_trident_stop_voice(trident, voice->number);
+                snd_trident_write_cso_reg(trident, voice, voice->isync_mark);
+                snd_trident_write_eso_reg(trident, voice, voice->ESO);
+                snd_trident_start_voice(trident, voice->number);
+            }
+#if 0
+            if (voice->extra) {
+                /* update CSO for extra voice to preserve sync */
+                snd_trident_stop_voice(trident, voice->extra->number);
+                snd_trident_write_cso_reg(trident, voice->extra, 0);
+                snd_trident_start_voice(trident, voice->extra->number);
+            }
+#endif
+            spin_unlock(&trident->reg_lock);
+            snd_pcm_period_elapsed(voice->substream);
+            spin_lock(&trident->reg_lock);
+        }
+        outl(chn_int, TRID_REG(trident, T4D_AINT_B));	/* ack */
+    __skip2:
+        spin_unlock(&trident->reg_lock);
+    }
+    if (audio_int & MPU401_IRQ) {
+#ifdef TARGET_OS2
+        fOurIrq = TRUE;
+#endif
+        if (trident->rmidi) {
+            snd_mpu401_uart_interrupt(irq, trident->rmidi->private_data, regs);
+        } else {
+            inb(TRID_REG(trident, T4D_MPUR0));
+        }
+    }
+    // outl((ST_TARGET_REACHED | MIXER_OVERFLOW | MIXER_UNDERFLOW), TRID_REG(trident, T4D_MISCINT));
+#ifdef TARGET_OS2
+    if (fOurIrq) {
+        eoi_irq(irq);
+    }
+#endif //TARGET_OS2
+    return IRQ_HANDLED;
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_attach_synthesizer, snd_trident_detach_synthesizer
+
+ Description: Attach/detach synthesizer hooks
+
+ Paramters:   trident  - device specific private data for 4DWave card
+
+ Returns:     None.
+
+ ---------------------------------------------------------------------------*/
+int snd_trident_attach_synthesizer(struct snd_trident *trident)
+{
+#ifdef CONFIG_SND_SEQUENCER
+    if (snd_seq_device_new(trident->card, 1, SNDRV_SEQ_DEV_ID_TRIDENT,
+                           sizeof(struct snd_trident*), &trident->seq_dev) >= 0) {
+        strcpy(trident->seq_dev->name, "4DWave");
+        *(struct snd_trident**)SNDRV_SEQ_DEVICE_ARGPTR(trident->seq_dev) = trident;
+    }
+#endif
+    return 0;
+}
+
+int snd_trident_detach_synthesizer(struct snd_trident *trident)
+{
+#ifdef CONFIG_SND_SEQUENCER
+    if (trident->seq_dev) {
+        snd_device_free(trident->card, trident->seq_dev);
+        trident->seq_dev = NULL;
+    }
+#endif
+    return 0;
+}
+
+struct snd_trident_voice*snd_trident_alloc_voice(struct snd_trident * trident, int type, int client, int port)
+{
+    struct snd_trident_voice*pvoice;
+    unsigned long flags;
+    int idx;
+
+    spin_lock_irqsave(&trident->voice_alloc, flags);
+    if (type == SNDRV_TRIDENT_VOICE_TYPE_PCM) {
+        idx = snd_trident_allocate_pcm_channel(trident);
+        if(idx < 0) {
+            spin_unlock_irqrestore(&trident->voice_alloc, flags);
+            return NULL;
+        }
+        pvoice = &trident->synth.voices[idx];
+        pvoice->use = 1;
+        pvoice->pcm = 1;
+        pvoice->capture = 0;
+        pvoice->spdif = 0;
+        pvoice->memblk = NULL;
+        pvoice->substream = NULL;
+        spin_unlock_irqrestore(&trident->voice_alloc, flags);
+        return pvoice;
+    }
+    if (type == SNDRV_TRIDENT_VOICE_TYPE_SYNTH) {
+        idx = snd_trident_allocate_synth_channel(trident);
+        if(idx < 0) {
+            spin_unlock_irqrestore(&trident->voice_alloc, flags);
+            return NULL;
+        }
+        pvoice = &trident->synth.voices[idx];
+        pvoice->use = 1;
+        pvoice->synth = 1;
+        pvoice->client = client;
+        pvoice->port = port;
+        pvoice->memblk = NULL;
+        spin_unlock_irqrestore(&trident->voice_alloc, flags);
+        return pvoice;
+    }
+    if (type == SNDRV_TRIDENT_VOICE_TYPE_MIDI) {
+    }
+    spin_unlock_irqrestore(&trident->voice_alloc, flags);
+    return NULL;
+}
+
+void snd_trident_free_voice(struct snd_trident * trident, struct snd_trident_voice*voice)
+{
+    unsigned long flags;
+    void (*private_free)(struct snd_trident_voice*);
+    void *private_data;
+
+    if (voice == NULL || !voice->use)
+        return;
+    snd_trident_clear_voices(trident, voice->number, voice->number);
+    spin_lock_irqsave(&trident->voice_alloc, flags);
+    private_free = voice->private_free;
+    private_data = voice->private_data;
+    voice->private_free = NULL;
+    voice->private_data = NULL;
+    if (voice->pcm)
+        snd_trident_free_pcm_channel(trident, voice->number);
+    if (voice->synth)
+        snd_trident_free_synth_channel(trident, voice->number);
+    voice->use = voice->pcm = voice->synth = voice->midi = 0;
+    voice->capture = voice->spdif = 0;
+    voice->sample_ops = NULL;
+    voice->substream = NULL;
+    voice->extra = NULL;
+    spin_unlock_irqrestore(&trident->voice_alloc, flags);
+    if (private_free)
+        private_free(voice);
+}
+
+void snd_trident_clear_voices(struct snd_trident * trident, unsigned short v_min, unsigned short v_max)
+{
+    unsigned int i, val, mask[2] = { 0, 0 };
+
+    snd_assert(v_min <= 63, return);
+    snd_assert(v_max <= 63, return);
+    for (i = v_min; i <= v_max; i++)
+        mask[i >> 5] |= 1 << (i & 0x1f);
+    if (mask[0]) {
+        outl(mask[0], TRID_REG(trident, T4D_STOP_A));
+        val = inl(TRID_REG(trident, T4D_AINTEN_A));
+        outl(val & ~mask[0], TRID_REG(trident, T4D_AINTEN_A));
+    }
+    if (mask[1]) {
+        outl(mask[1], TRID_REG(trident, T4D_STOP_B));
+        val = inl(TRID_REG(trident, T4D_AINTEN_B));
+        outl(val & ~mask[1], TRID_REG(trident, T4D_AINTEN_B));
+    }
+}
+
+#ifdef CONFIG_PM
+int snd_trident_suspend(struct pci_dev *pci, pm_message_t state)
+{
+    struct snd_card *card = pci_get_drvdata(pci);
+    struct snd_trident *trident = card->private_data;
+
+    trident->in_suspend = 1;
+    snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+    snd_pcm_suspend_all(trident->pcm);
+    snd_pcm_suspend_all(trident->foldback);
+    snd_pcm_suspend_all(trident->spdif);
+
+    snd_ac97_suspend(trident->ac97);
+    snd_ac97_suspend(trident->ac97_sec);
+
+    switch (trident->device) {
+    case TRIDENT_DEVICE_ID_DX:
+    case TRIDENT_DEVICE_ID_NX:
+        break;			/* TODO */
+    case TRIDENT_DEVICE_ID_SI7018:
+        break;
+    }
+    pci_disable_device(pci);
+    pci_save_state(pci);
+    return 0;
+}
+
+int snd_trident_resume(struct pci_dev *pci)
+{
+    struct snd_card *card = pci_get_drvdata(pci);
+    struct snd_trident *trident = card->private_data;
+
+    pci_restore_state(pci);
+    pci_enable_device(pci);
+    pci_set_master(pci); /* to be sure */
+
+    switch (trident->device) {
+    case TRIDENT_DEVICE_ID_DX:
+        snd_trident_4d_dx_init(trident);
+        break;
+    case TRIDENT_DEVICE_ID_NX:
+        snd_trident_4d_nx_init(trident);
+        break;
+    case TRIDENT_DEVICE_ID_SI7018:
+        snd_trident_sis_init(trident);
+        break;
+    }
+
+    snd_ac97_resume(trident->ac97);
+    snd_ac97_resume(trident->ac97_sec);
+
+    /* restore some registers */
+    outl(trident->musicvol_wavevol, TRID_REG(trident, T4D_MUSICVOL_WAVEVOL));
+
+    snd_trident_enable_eso(trident);
+
+    snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+    trident->in_suspend = 0;
+    return 0;
+}
+#endif /* CONFIG_PM */
+
+EXPORT_SYMBOL(snd_trident_alloc_voice);
+EXPORT_SYMBOL(snd_trident_free_voice);
+EXPORT_SYMBOL(snd_trident_start_voice);
+EXPORT_SYMBOL(snd_trident_stop_voice);
+EXPORT_SYMBOL(snd_trident_write_voice_regs);
+EXPORT_SYMBOL(snd_trident_clear_voices);
+/* trident_memory.c symbols */
+EXPORT_SYMBOL(snd_trident_synth_alloc);
+EXPORT_SYMBOL(snd_trident_synth_free);
+EXPORT_SYMBOL(snd_trident_synth_bzero);
+EXPORT_SYMBOL(snd_trident_synth_copy_from_user);
+
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/trident/trident_memory.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/trident/trident_memory.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/trident/trident_memory.c	(revision 91)
@@ -0,0 +1,499 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  Copyright (c) by Takashi Iwai <tiwai@suse.de>
+ *  Copyright (c) by Scott McNab <sdm@fractalgraphics.com.au>
+ *
+ *  Trident 4DWave-NX memory page allocation (TLB area)
+ *  Trident chip can handle only 16MByte of the memory at the same time.
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/pci.h>
+#include <sound/trident.h>
+
+/* page arguments of these two macros are Trident page (4096 bytes), not like
+ * aligned pages in others
+ */
+#define __set_tlb_bus(trident,page,ptr,addr) \
+	do { (trident)->tlb.entries[page] = cpu_to_le32((addr) & ~(SNDRV_TRIDENT_PAGE_SIZE-1)); \
+	     (trident)->tlb.shadow_entries[page] = (ptr); } while (0)
+#define __tlb_to_ptr(trident,page) \
+	(void*)((trident)->tlb.shadow_entries[page])
+#define __tlb_to_addr(trident,page) \
+	(dma_addr_t)le32_to_cpu((trident->tlb.entries[page]) & ~(SNDRV_TRIDENT_PAGE_SIZE - 1))
+
+#if PAGE_SIZE == 4096
+/* page size == SNDRV_TRIDENT_PAGE_SIZE */
+#define ALIGN_PAGE_SIZE		PAGE_SIZE	/* minimum page size for allocation */
+#define MAX_ALIGN_PAGES		SNDRV_TRIDENT_MAX_PAGES	/* maxmium aligned pages */
+/* fill TLB entrie(s) corresponding to page with ptr */
+#define set_tlb_bus(trident,page,ptr,addr) __set_tlb_bus(trident,page,ptr,addr)
+/* fill TLB entrie(s) corresponding to page with silence pointer */
+#define set_silent_tlb(trident,page)	__set_tlb_bus(trident, page, (unsigned long)trident->tlb.silent_page.area, trident->tlb.silent_page.addr)
+/* get aligned page from offset address */
+#define get_aligned_page(offset)	((offset) >> 12)
+/* get offset address from aligned page */
+#define aligned_page_offset(page)	((page) << 12)
+/* get buffer address from aligned page */
+#define page_to_ptr(trident,page)	__tlb_to_ptr(trident, page)
+/* get PCI physical address from aligned page */
+#define page_to_addr(trident,page)	__tlb_to_addr(trident, page)
+
+#elif PAGE_SIZE == 8192
+/* page size == SNDRV_TRIDENT_PAGE_SIZE x 2*/
+#define ALIGN_PAGE_SIZE		PAGE_SIZE
+#define MAX_ALIGN_PAGES		(SNDRV_TRIDENT_MAX_PAGES / 2)
+#define get_aligned_page(offset)	((offset) >> 13)
+#define aligned_page_offset(page)	((page) << 13)
+#define page_to_ptr(trident,page)	__tlb_to_ptr(trident, (page) << 1)
+#define page_to_addr(trident,page)	__tlb_to_addr(trident, (page) << 1)
+
+/* fill TLB entries -- we need to fill two entries */
+static inline void set_tlb_bus(struct snd_trident *trident, int page, unsigned long ptr, dma_addr_t addr)
+{
+	page <<= 1;
+	__set_tlb_bus(trident, page, ptr, addr);
+	__set_tlb_bus(trident, page+1, ptr + SNDRV_TRIDENT_PAGE_SIZE, addr + SNDRV_TRIDENT_PAGE_SIZE);
+}
+static inline void set_silent_tlb(struct snd_trident *trident, int page)
+{
+    page <<= 1;
+    __set_tlb_bus(trident, page, (unsigned long)trident->tlb.silent_page.area, trident->tlb.silent_page.addr);
+    __set_tlb_bus(trident, page+1, (unsigned long)trident->tlb.silent_page.area, trident->tlb.silent_page.addr);
+//	__set_tlb_bus(trident, page, (unsigned long)trident->tlb.silent_page, trident->tlb.silent_page_dmaaddr);
+//	__set_tlb_bus(trident, page+1, (unsigned long)trident->tlb.silent_page, trident->tlb.silent_page_dmaaddr);
+}
+
+#else
+/* arbitrary size */
+#define UNIT_PAGES		(PAGE_SIZE / SNDRV_TRIDENT_PAGE_SIZE)
+#define ALIGN_PAGE_SIZE		(SNDRV_TRIDENT_PAGE_SIZE * UNIT_PAGES)
+#define MAX_ALIGN_PAGES		(SNDRV_TRIDENT_MAX_PAGES / UNIT_PAGES)
+/* Note: if alignment doesn't match to the maximum size, the last few blocks
+ * become unusable.  To use such blocks, you'll need to check the validity
+ * of accessing page in set_tlb_bus and set_silent_tlb.  search_empty()
+ * should also check it, too.
+ */
+#define get_aligned_page(offset)	((offset) / ALIGN_PAGE_SIZE)
+#define aligned_page_offset(page)	((page) * ALIGN_PAGE_SIZE)
+#define page_to_ptr(trident,page)	__tlb_to_ptr(trident, (page) * UNIT_PAGES)
+#define page_to_addr(trident,page)	__tlb_to_addr(trident, (page) * UNIT_PAGES)
+
+/* fill TLB entries -- UNIT_PAGES entries must be filled */
+static inline void set_tlb_bus(struct snd_trident *trident, int page, unsigned long ptr, dma_addr_t addr)
+{
+	int i;
+	page *= UNIT_PAGES;
+	for (i = 0; i < UNIT_PAGES; i++, page++) {
+		__set_tlb_bus(trident, page, ptr, addr);
+		ptr += SNDRV_TRIDENT_PAGE_SIZE;
+		addr += SNDRV_TRIDENT_PAGE_SIZE;
+	}
+}
+static inline void set_silent_tlb(struct snd_trident *trident, int page)
+{
+	int i;
+	page *= UNIT_PAGES;
+        for (i = 0; i < UNIT_PAGES; i++, page++)
+            __set_tlb_bus(trident, page, (unsigned long)trident->tlb.silent_page.area, trident->tlb.silent_page.addr);
+	    //    __set_tlb_bus(trident, page, (unsigned long)trident->tlb.silent_page, trident->tlb.silent_page_dmaaddr);
+}
+
+#endif /* PAGE_SIZE */
+
+/* calculate buffer pointer from offset address */
+inline static void *offset_ptr(struct snd_trident *trident, int offset)
+{
+	char *ptr;
+	ptr = page_to_ptr(trident, get_aligned_page(offset));
+	ptr += offset % ALIGN_PAGE_SIZE;
+	return (void*)ptr;
+}
+
+/* first and last (aligned) pages of memory block */
+#define firstpg(blk)	(((struct snd_trident_memblk_arg*)snd_util_memblk_argptr(blk))->first_page)
+#define lastpg(blk)	(((struct snd_trident_memblk_arg*)snd_util_memblk_argptr(blk))->last_page)
+
+/*
+ * search empty pages which may contain given size
+ */
+static snd_util_memblk_t *
+search_empty(snd_util_memhdr_t *hdr, int size)
+{
+	snd_util_memblk_t *blk, *prev;
+	int page, psize;
+	struct list_head *p;
+
+	psize = get_aligned_page(size + ALIGN_PAGE_SIZE -1);
+	prev = NULL;
+	page = 0;
+	list_for_each(p, &hdr->block) {
+		blk = list_entry(p, snd_util_memblk_t, list);
+		if (page + psize <= firstpg(blk))
+			goto __found_pages;
+		page = lastpg(blk) + 1;
+	}
+	if (page + psize > MAX_ALIGN_PAGES)
+		return NULL;
+
+__found_pages:
+	/* create a new memory block */
+	blk = __snd_util_memblk_new(hdr, psize * ALIGN_PAGE_SIZE, p->prev);
+	if (blk == NULL)
+		return NULL;
+	blk->offset = aligned_page_offset(page); /* set aligned offset */
+	firstpg(blk) = page;
+	lastpg(blk) = page + psize - 1;
+	return blk;
+}
+
+
+/*
+ * check if the given pointer is valid for pages
+ */
+static int is_valid_page(unsigned long ptr)
+{
+	if (ptr & ~0x3fffffffUL) {
+		snd_printk("max memory size is 1GB!!\n");
+		return 0;
+	}
+	if (ptr & (SNDRV_TRIDENT_PAGE_SIZE-1)) {
+		snd_printk("page is not aligned\n");
+		return 0;
+	}
+	return 1;
+}
+
+/*
+ * page allocation for DMA (Scatter-Gather version)
+ */
+static snd_util_memblk_t *
+snd_trident_alloc_sg_pages(struct snd_trident *trident, snd_pcm_substream_t *substream)
+{
+	snd_util_memhdr_t *hdr;
+	snd_util_memblk_t *blk;
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int idx, page;
+	struct snd_sg_buf *sgbuf = snd_pcm_substream_sgbuf(substream);
+
+	snd_assert(runtime->dma_bytes > 0 && runtime->dma_bytes <= SNDRV_TRIDENT_MAX_PAGES * SNDRV_TRIDENT_PAGE_SIZE, return NULL);
+	hdr = trident->tlb.memhdr;
+	snd_assert(hdr != NULL, return NULL);
+
+	
+
+	down(&hdr->block_mutex);
+	blk = search_empty(hdr, runtime->dma_bytes);
+	if (blk == NULL) {
+		up(&hdr->block_mutex);
+		return NULL;
+	}
+	if (lastpg(blk) - firstpg(blk) >= sgbuf->pages) {
+		snd_printk(KERN_ERR "page calculation doesn't match: allocated pages = %d, trident = %d/%d\n", sgbuf->pages, firstpg(blk), lastpg(blk));
+		__snd_util_mem_free(hdr, blk);
+		up(&hdr->block_mutex);
+		return NULL;
+	}
+			   
+	/* set TLB entries */
+	idx = 0;
+	for (page = firstpg(blk); page <= lastpg(blk); page++, idx++) {
+		dma_addr_t addr = sgbuf->table[idx].addr;
+		unsigned long ptr = (unsigned long)sgbuf->table[idx].buf;
+		if (! is_valid_page(addr)) {
+			__snd_util_mem_free(hdr, blk);
+			up(&hdr->block_mutex);
+			return NULL;
+		}
+		set_tlb_bus(trident, page, ptr, addr);
+	}
+	up(&hdr->block_mutex);
+	return blk;
+}
+
+/*
+ * page allocation for DMA (contiguous version)
+ */
+static snd_util_memblk_t *
+snd_trident_alloc_cont_pages(struct snd_trident *trident, snd_pcm_substream_t *substream)
+{
+	snd_util_memhdr_t *hdr;
+	snd_util_memblk_t *blk;
+	int page;
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	dma_addr_t addr;
+	unsigned long ptr;
+
+	snd_assert(runtime->dma_bytes> 0 && runtime->dma_bytes <= SNDRV_TRIDENT_MAX_PAGES * SNDRV_TRIDENT_PAGE_SIZE, return NULL);
+	hdr = trident->tlb.memhdr;
+	snd_assert(hdr != NULL, return NULL);
+
+	down(&hdr->block_mutex);
+	blk = search_empty(hdr, runtime->dma_bytes);
+	if (blk == NULL) {
+		up(&hdr->block_mutex);
+		return NULL;
+	}
+			   
+	/* set TLB entries */
+	addr = runtime->dma_addr;
+	ptr = (unsigned long)runtime->dma_area;
+	for (page = firstpg(blk); page <= lastpg(blk); page++,
+	     ptr += SNDRV_TRIDENT_PAGE_SIZE, addr += SNDRV_TRIDENT_PAGE_SIZE) {
+		if (! is_valid_page(addr)) {
+			__snd_util_mem_free(hdr, blk);
+			up(&hdr->block_mutex);
+			return NULL;
+		}
+		set_tlb_bus(trident, page, ptr, addr);
+	}
+	up(&hdr->block_mutex);
+	return blk;
+}
+
+/*
+ * page allocation for DMA
+ */
+snd_util_memblk_t *
+snd_trident_alloc_pages(struct snd_trident *trident, snd_pcm_substream_t *substream)
+{
+	snd_assert(trident != NULL, return NULL);
+        snd_assert(substream != NULL, return NULL);
+        if (substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV_SG)
+		return snd_trident_alloc_sg_pages(trident, substream);
+	else
+		return snd_trident_alloc_cont_pages(trident, substream);
+}
+
+
+/*
+ * release DMA buffer from page table
+ */
+int snd_trident_free_pages(struct snd_trident *trident, snd_util_memblk_t *blk)
+{
+	snd_util_memhdr_t *hdr;
+	int page;
+
+	snd_assert(trident != NULL, return -EINVAL);
+	snd_assert(blk != NULL, return -EINVAL);
+
+	hdr = trident->tlb.memhdr;
+	down(&hdr->block_mutex);
+	/* reset TLB entries */
+	for (page = firstpg(blk); page <= lastpg(blk); page++)
+		set_silent_tlb(trident, page);
+	/* free memory block */
+	__snd_util_mem_free(hdr, blk);
+	up(&hdr->block_mutex);
+	return 0;
+}
+
+/*----------------------------------------------------------------
+ * memory allocation using multiple pages (for synth)
+ *----------------------------------------------------------------
+ * Unlike the DMA allocation above, non-contiguous pages are
+ * assigned to TLB.
+ *----------------------------------------------------------------*/
+
+/*
+ */
+static int synth_alloc_pages(struct snd_trident *hw, snd_util_memblk_t *blk);
+static int synth_free_pages(struct snd_trident *hw, snd_util_memblk_t *blk);
+
+/*
+ * allocate a synth sample area
+ */
+snd_util_memblk_t *
+snd_trident_synth_alloc(struct snd_trident *hw, unsigned int size)
+{
+	snd_util_memblk_t *blk;
+	snd_util_memhdr_t *hdr = hw->tlb.memhdr; 
+
+	down(&hdr->block_mutex);
+	blk = __snd_util_mem_alloc(hdr, size);
+	if (blk == NULL) {
+		up(&hdr->block_mutex);
+		return NULL;
+	}
+	if (synth_alloc_pages(hw, blk)) {
+		__snd_util_mem_free(hdr, blk);
+		up(&hdr->block_mutex);
+		return NULL;
+	}
+	up(&hdr->block_mutex);
+	return blk;
+}
+
+
+/*
+ * free a synth sample area
+ */
+int
+snd_trident_synth_free(struct snd_trident *hw, snd_util_memblk_t *blk)
+{
+	snd_util_memhdr_t *hdr = hw->tlb.memhdr; 
+
+	down(&hdr->block_mutex);
+	synth_free_pages(hw, blk);
+	 __snd_util_mem_free(hdr, blk);
+	up(&hdr->block_mutex);
+	return 0;
+}
+
+
+/*
+ * reset TLB entry and free kernel page
+ */
+static void clear_tlb(struct snd_trident *trident, int page)
+{
+	void *ptr = page_to_ptr(trident, page);
+	dma_addr_t addr = page_to_addr(trident, page);
+	set_silent_tlb(trident, page);
+        if (ptr)
+        {
+            struct snd_dma_buffer dmab;
+            dmab.dev.type = SNDRV_DMA_TYPE_DEV;
+            dmab.dev.dev = snd_dma_pci_data(trident->pci);
+            dmab.area = ptr;
+            dmab.addr = addr;
+            dmab.bytes = ALIGN_PAGE_SIZE;
+            snd_dma_free_pages(&dmab);
+        }
+}
+
+/* check new allocation range */
+static void get_single_page_range(snd_util_memhdr_t *hdr, snd_util_memblk_t *blk, int *first_page_ret, int *last_page_ret)
+{
+	struct list_head *p;
+	snd_util_memblk_t *q;
+	int first_page, last_page;
+	first_page = firstpg(blk);
+	if ((p = blk->list.prev) != &hdr->block) {
+		q = list_entry(p, snd_util_memblk_t, list);
+		if (lastpg(q) == first_page)
+			first_page++;  /* first page was already allocated */
+	}
+	last_page = lastpg(blk);
+	if ((p = blk->list.next) != &hdr->block) {
+		q = list_entry(p, snd_util_memblk_t, list);
+		if (firstpg(q) == last_page)
+			last_page--; /* last page was already allocated */
+	}
+	*first_page_ret = first_page;
+	*last_page_ret = last_page;
+}
+
+/*
+ * allocate kernel pages and assign them to TLB
+ */
+static int synth_alloc_pages(struct snd_trident *hw, snd_util_memblk_t *blk)
+{
+	int page, first_page, last_page;
+	struct snd_dma_buffer dmab;
+
+	firstpg(blk) = get_aligned_page(blk->offset);
+	lastpg(blk) = get_aligned_page(blk->offset + blk->size - 1);
+	get_single_page_range(hw->tlb.memhdr, blk, &first_page, &last_page);
+
+	/* allocate a kernel page for each Trident page -
+	 * fortunately Trident page size and kernel PAGE_SIZE is identical!
+	 */
+	for (page = first_page; page <= last_page; page++) {
+		if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(hw->pci),
+					ALIGN_PAGE_SIZE, &dmab) < 0)
+			goto __fail;
+		if (! is_valid_page(dmab.addr)) {
+			snd_dma_free_pages(&dmab);
+			goto __fail;
+		}
+		set_tlb_bus(hw, page, (unsigned long)dmab.area, dmab.addr);
+	}
+	return 0;
+
+__fail:
+	/* release allocated pages */
+	last_page = page - 1;
+	for (page = first_page; page <= last_page; page++)
+		clear_tlb(hw, page);
+
+	return -ENOMEM;
+}
+
+/*
+ * free pages
+ */
+static int synth_free_pages(struct snd_trident *trident, snd_util_memblk_t *blk)
+{
+	int page, first_page, last_page;
+
+	get_single_page_range(trident->tlb.memhdr, blk, &first_page, &last_page);
+	for (page = first_page; page <= last_page; page++)
+		clear_tlb(trident, page);
+
+	return 0;
+}
+
+/*
+ * bzero(blk + offset, size)
+ */
+int snd_trident_synth_bzero(struct snd_trident *trident, snd_util_memblk_t *blk, int offset, int size)
+{
+	int page, nextofs, end_offset, temp, temp1;
+
+	offset += blk->offset;
+	end_offset = offset + size;
+	page = get_aligned_page(offset) + 1;
+	do {
+		nextofs = aligned_page_offset(page);
+		temp = nextofs - offset;
+		temp1 = end_offset - offset;
+		if (temp1 < temp)
+			temp = temp1;
+		memset(offset_ptr(trident, offset), 0, temp);
+		offset = nextofs;
+		page++;
+	} while (offset < end_offset);
+	return 0;
+}
+
+/*
+ * copy_from_user(blk + offset, data, size)
+ */
+int snd_trident_synth_copy_from_user(struct snd_trident *trident, snd_util_memblk_t *blk, int offset, const char *data, int size)
+{
+	int page, nextofs, end_offset, temp, temp1;
+
+	offset += blk->offset;
+	end_offset = offset + size;
+	page = get_aligned_page(offset) + 1;
+	do {
+		nextofs = aligned_page_offset(page);
+		temp = nextofs - offset;
+		temp1 = end_offset - offset;
+		if (temp1 < temp)
+			temp = temp1;
+		if (copy_from_user(offset_ptr(trident, offset), data, temp))
+			return -EFAULT;
+		offset = nextofs;
+		data += temp;
+		page++;
+	} while (offset < end_offset);
+	return 0;
+}
+
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/trident/trident_synth.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/trident/trident_synth.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/trident/trident_synth.c	(revision 91)
@@ -0,0 +1,1029 @@
+/*
+ *  Routines for Trident 4DWave NX/DX soundcards - Synthesizer
+ *  Copyright (c) by Scott McNab <jedi@tartarus.uwa.edu.au>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#define SNDRV_MAIN_OBJECT_FILE
+#include <sound/driver.h>
+#include <linux/pci.h>
+#include <sound/trident.h>
+#include <sound/seq_device.h>
+
+MODULE_AUTHOR("Scott McNab <jedi@tartarus.uwa.edu.au>");
+MODULE_DESCRIPTION("Routines for Trident 4DWave NX/DX soundcards - Synthesizer");
+MODULE_LICENSE("GPL");
+
+/* linear to log pan conversion table (4.2 channel attenuation format) */
+static unsigned int pan_table[63] = {
+    7959, 7733, 7514, 7301, 7093, 6892, 6697, 6507,
+    6322, 6143, 5968, 5799, 5634, 5475, 5319, 5168,
+    5022, 4879, 4741, 4606, 4475, 4349, 4225, 4105,
+    3989, 3876, 3766, 3659, 3555, 3454, 3356, 3261,
+    3168, 3078, 2991, 2906, 2824, 2744, 2666, 2590,
+    2517, 2445, 2376, 2308, 2243, 2179, 2117, 2057,
+    1999, 1942, 1887, 1833, 1781, 1731, 1682, 1634,
+    1588, 1543, 1499, 1456, 1415, 1375, 1336
+};
+
+#define LOG_TABLE_SIZE 386
+
+/* Linear half-attenuation to log conversion table in the format:
+ *   {linear volume, logarithmic attenuation equivalent}, ...
+ *
+ * Provides conversion from a linear half-volume value in the range
+ * [0,8192] to a logarithmic attenuation value in the range 0 to 6.02dB.
+ * Halving the linear volume is equivalent to an additional 6dB of
+ * logarithmic attenuation. The algorithm used in log_from_linear()
+ * therefore uses this table as follows:
+ *
+ * - loop and for every time the volume is less than half the maximum
+ *   volume (16384), add another 6dB and halve the maximum value used
+ *   for this comparison.
+ * - when the volume is greater than half the maximum volume, take
+ *   the difference of the volume to half volume (in the range [0,8192])
+ *   and look up the log_table[] to find the nearest entry.
+ * - take the logarithic component of this entry and add it to the
+ *   resulting attenuation.
+ *
+ * Thus this routine provides a linear->log conversion for a range of
+ * [0,16384] using only 386 table entries
+ *
+ * Note: although this table stores log attenuation in 8.8 format, values
+ * were only calculated for 6 bits fractional precision, since that is
+ * the most precision offered by the trident hardware.
+ */
+
+static unsigned short log_table[LOG_TABLE_SIZE*2] =
+{
+    4, 0x0604, 19, 0x0600, 34, 0x05fc,
+    49, 0x05f8, 63, 0x05f4, 78, 0x05f0, 93, 0x05ec, 108, 0x05e8,
+    123, 0x05e4, 138, 0x05e0, 153, 0x05dc, 168, 0x05d8, 183, 0x05d4,
+    198, 0x05d0, 213, 0x05cc, 228, 0x05c8, 244, 0x05c4, 259, 0x05c0,
+    274, 0x05bc, 289, 0x05b8, 304, 0x05b4, 320, 0x05b0, 335, 0x05ac,
+    350, 0x05a8, 366, 0x05a4, 381, 0x05a0, 397, 0x059c, 412, 0x0598,
+    428, 0x0594, 443, 0x0590, 459, 0x058c, 474, 0x0588, 490, 0x0584,
+    506, 0x0580, 521, 0x057c, 537, 0x0578, 553, 0x0574, 568, 0x0570,
+    584, 0x056c, 600, 0x0568, 616, 0x0564, 632, 0x0560, 647, 0x055c,
+    663, 0x0558, 679, 0x0554, 695, 0x0550, 711, 0x054c, 727, 0x0548,
+    743, 0x0544, 759, 0x0540, 776, 0x053c, 792, 0x0538, 808, 0x0534,
+    824, 0x0530, 840, 0x052c, 857, 0x0528, 873, 0x0524, 889, 0x0520,
+    906, 0x051c, 922, 0x0518, 938, 0x0514, 955, 0x0510, 971, 0x050c,
+    988, 0x0508, 1004, 0x0504, 1021, 0x0500, 1037, 0x04fc, 1054, 0x04f8,
+    1071, 0x04f4, 1087, 0x04f0, 1104, 0x04ec, 1121, 0x04e8, 1138, 0x04e4,
+    1154, 0x04e0, 1171, 0x04dc, 1188, 0x04d8, 1205, 0x04d4, 1222, 0x04d0,
+    1239, 0x04cc, 1256, 0x04c8, 1273, 0x04c4, 1290, 0x04c0, 1307, 0x04bc,
+    1324, 0x04b8, 1341, 0x04b4, 1358, 0x04b0, 1376, 0x04ac, 1393, 0x04a8,
+    1410, 0x04a4, 1427, 0x04a0, 1445, 0x049c, 1462, 0x0498, 1479, 0x0494,
+    1497, 0x0490, 1514, 0x048c, 1532, 0x0488, 1549, 0x0484, 1567, 0x0480,
+    1584, 0x047c, 1602, 0x0478, 1620, 0x0474, 1637, 0x0470, 1655, 0x046c,
+    1673, 0x0468, 1690, 0x0464, 1708, 0x0460, 1726, 0x045c, 1744, 0x0458,
+    1762, 0x0454, 1780, 0x0450, 1798, 0x044c, 1816, 0x0448, 1834, 0x0444,
+    1852, 0x0440, 1870, 0x043c, 1888, 0x0438, 1906, 0x0434, 1924, 0x0430,
+    1943, 0x042c, 1961, 0x0428, 1979, 0x0424, 1997, 0x0420, 2016, 0x041c,
+    2034, 0x0418, 2053, 0x0414, 2071, 0x0410, 2089, 0x040c, 2108, 0x0408,
+    2127, 0x0404, 2145, 0x0400, 2164, 0x03fc, 2182, 0x03f8, 2201, 0x03f4,
+    2220, 0x03f0, 2239, 0x03ec, 2257, 0x03e8, 2276, 0x03e4, 2295, 0x03e0,
+    2314, 0x03dc, 2333, 0x03d8, 2352, 0x03d4, 2371, 0x03d0, 2390, 0x03cc,
+    2409, 0x03c8, 2428, 0x03c4, 2447, 0x03c0, 2466, 0x03bc, 2485, 0x03b8,
+    2505, 0x03b4, 2524, 0x03b0, 2543, 0x03ac, 2562, 0x03a8, 2582, 0x03a4,
+    2601, 0x03a0, 2621, 0x039c, 2640, 0x0398, 2660, 0x0394, 2679, 0x0390,
+    2699, 0x038c, 2718, 0x0388, 2738, 0x0384, 2758, 0x0380, 2777, 0x037c,
+    2797, 0x0378, 2817, 0x0374, 2837, 0x0370, 2857, 0x036c, 2876, 0x0368,
+    2896, 0x0364, 2916, 0x0360, 2936, 0x035c, 2956, 0x0358, 2976, 0x0354,
+    2997, 0x0350, 3017, 0x034c, 3037, 0x0348, 3057, 0x0344, 3077, 0x0340,
+    3098, 0x033c, 3118, 0x0338, 3138, 0x0334, 3159, 0x0330, 3179, 0x032c,
+    3200, 0x0328, 3220, 0x0324, 3241, 0x0320, 3261, 0x031c, 3282, 0x0318,
+    3303, 0x0314, 3323, 0x0310, 3344, 0x030c, 3365, 0x0308, 3386, 0x0304,
+    3406, 0x0300, 3427, 0x02fc, 3448, 0x02f8, 3469, 0x02f4, 3490, 0x02f0,
+    3511, 0x02ec, 3532, 0x02e8, 3553, 0x02e4, 3575, 0x02e0, 3596, 0x02dc,
+    3617, 0x02d8, 3638, 0x02d4, 3660, 0x02d0, 3681, 0x02cc, 3702, 0x02c8,
+    3724, 0x02c4, 3745, 0x02c0, 3767, 0x02bc, 3788, 0x02b8, 3810, 0x02b4,
+    3831, 0x02b0, 3853, 0x02ac, 3875, 0x02a8, 3896, 0x02a4, 3918, 0x02a0,
+    3940, 0x029c, 3962, 0x0298, 3984, 0x0294, 4006, 0x0290, 4028, 0x028c,
+    4050, 0x0288, 4072, 0x0284, 4094, 0x0280, 4116, 0x027c, 4138, 0x0278,
+    4160, 0x0274, 4182, 0x0270, 4205, 0x026c, 4227, 0x0268, 4249, 0x0264,
+    4272, 0x0260, 4294, 0x025c, 4317, 0x0258, 4339, 0x0254, 4362, 0x0250,
+    4384, 0x024c, 4407, 0x0248, 4430, 0x0244, 4453, 0x0240, 4475, 0x023c,
+    4498, 0x0238, 4521, 0x0234, 4544, 0x0230, 4567, 0x022c, 4590, 0x0228,
+    4613, 0x0224, 4636, 0x0220, 4659, 0x021c, 4682, 0x0218, 4705, 0x0214,
+    4728, 0x0210, 4752, 0x020c, 4775, 0x0208, 4798, 0x0204, 4822, 0x0200,
+    4845, 0x01fc, 4869, 0x01f8, 4892, 0x01f4, 4916, 0x01f0, 4939, 0x01ec,
+    4963, 0x01e8, 4987, 0x01e4, 5010, 0x01e0, 5034, 0x01dc, 5058, 0x01d8,
+    5082, 0x01d4, 5106, 0x01d0, 5130, 0x01cc, 5154, 0x01c8, 5178, 0x01c4,
+    5202, 0x01c0, 5226, 0x01bc, 5250, 0x01b8, 5274, 0x01b4, 5299, 0x01b0,
+    5323, 0x01ac, 5347, 0x01a8, 5372, 0x01a4, 5396, 0x01a0, 5420, 0x019c,
+    5445, 0x0198, 5469, 0x0194, 5494, 0x0190, 5519, 0x018c, 5543, 0x0188,
+    5568, 0x0184, 5593, 0x0180, 5618, 0x017c, 5643, 0x0178, 5668, 0x0174,
+    5692, 0x0170, 5717, 0x016c, 5743, 0x0168, 5768, 0x0164, 5793, 0x0160,
+    5818, 0x015c, 5843, 0x0158, 5868, 0x0154, 5894, 0x0150, 5919, 0x014c,
+    5945, 0x0148, 5970, 0x0144, 5995, 0x0140, 6021, 0x013c, 6047, 0x0138,
+    6072, 0x0134, 6098, 0x0130, 6124, 0x012c, 6149, 0x0128, 6175, 0x0124,
+    6201, 0x0120, 6227, 0x011c, 6253, 0x0118, 6279, 0x0114, 6305, 0x0110,
+    6331, 0x010c, 6357, 0x0108, 6384, 0x0104, 6410, 0x0100, 6436, 0x00fc,
+    6462, 0x00f8, 6489, 0x00f4, 6515, 0x00f0, 6542, 0x00ec, 6568, 0x00e8,
+    6595, 0x00e4, 6621, 0x00e0, 6648, 0x00dc, 6675, 0x00d8, 6702, 0x00d4,
+    6728, 0x00d0, 6755, 0x00cc, 6782, 0x00c8, 6809, 0x00c4, 6836, 0x00c0,
+    6863, 0x00bc, 6890, 0x00b8, 6917, 0x00b4, 6945, 0x00b0, 6972, 0x00ac,
+    6999, 0x00a8, 7027, 0x00a4, 7054, 0x00a0, 7081, 0x009c, 7109, 0x0098,
+    7136, 0x0094, 7164, 0x0090, 7192, 0x008c, 7219, 0x0088, 7247, 0x0084,
+    7275, 0x0080, 7303, 0x007c, 7331, 0x0078, 7359, 0x0074, 7387, 0x0070,
+    7415, 0x006c, 7443, 0x0068, 7471, 0x0064, 7499, 0x0060, 7527, 0x005c,
+    7556, 0x0058, 7584, 0x0054, 7613, 0x0050, 7641, 0x004c, 7669, 0x0048,
+    7698, 0x0044, 7727, 0x0040, 7755, 0x003c, 7784, 0x0038, 7813, 0x0034,
+    7842, 0x0030, 7870, 0x002c, 7899, 0x0028, 7928, 0x0024, 7957, 0x0020,
+    7986, 0x001c, 8016, 0x0018, 8045, 0x0014, 8074, 0x0010, 8103, 0x000c,
+    8133, 0x0008, 8162, 0x0004, 8192, 0x0000
+};
+
+static unsigned short lookup_volume_table( unsigned short value )
+{
+    /* This code is an optimised version of:
+     *   int i = 0;
+     *   while( volume_table[i*2] < value )
+     *       i++;
+     *   return volume_table[i*2+1];
+     */
+    unsigned short *ptr = log_table;
+    while( *ptr < value )
+        ptr += 2;
+    return *(ptr+1);
+}
+
+/* this function calculates a 8.8 fixed point logarithmic attenuation
+ * value from a linear volume value in the range 0 to 16384 */
+static unsigned short log_from_linear( unsigned short value )
+{
+    if (value >= 16384)
+        return 0x0000;
+    if (value) {
+        unsigned short result = 0;
+        int v, c;
+        for( c = 0, v = 8192; c < 14; c++, v >>= 1 ) {
+            if( value >= v ) {
+                result += lookup_volume_table( (value - v) << c );
+                return result;
+            }
+            result += 0x0605;	/* 6.0205 (result of -20*log10(0.5)) */
+        }
+    }
+    return 0xffff;
+}
+
+/*
+ * Sample handling operations
+ */
+
+static void sample_start(struct snd_trident * trident, struct snd_trident_voice * voice, snd_seq_position_t position);
+static void sample_stop(struct snd_trident * trident, struct snd_trident_voice * voice, int mode);
+static void sample_freq(struct snd_trident * trident, struct snd_trident_voice * voice, snd_seq_frequency_t freq);
+static void sample_volume(struct snd_trident * trident, struct snd_trident_voice * voice, snd_seq_ev_volume_t * volume);
+static void sample_loop(struct snd_trident * trident, struct snd_trident_voice * voice, snd_seq_ev_loop_t * loop);
+static void sample_pos(struct snd_trident * trident, struct snd_trident_voice * voice, snd_seq_position_t position);
+static void sample_private1(struct snd_trident * trident, struct snd_trident_voice * voice, unsigned char *data);
+
+static struct snd_trident_sample_ops sample_ops =
+{
+    sample_start,
+    sample_stop,
+    sample_freq,
+    sample_volume,
+    sample_loop,
+    sample_pos,
+    sample_private1
+};
+
+static void snd_trident_simple_init(struct snd_trident_voice * voice)
+{
+    //voice->handler_wave = interrupt_wave;
+    //voice->handler_volume = interrupt_volume;
+    //voice->handler_effect = interrupt_effect;
+    //voice->volume_change = NULL;
+    voice->sample_ops = &sample_ops;
+}
+
+static void sample_start(struct snd_trident * trident, struct snd_trident_voice * voice, snd_seq_position_t position)
+{
+    simple_instrument_t *simple;
+    snd_seq_kinstr_t *instr;
+    unsigned long flags;
+    unsigned int loop_start, loop_end, sample_start, sample_end, start_offset;
+    unsigned int value;
+    unsigned int shift = 0;
+
+    instr = snd_seq_instr_find(trident->synth.ilist, &voice->instr, 0, 1);
+    if (instr == NULL)
+        return;
+    voice->instr = instr->instr;	/* copy ID to speedup aliases */
+    simple = KINSTR_DATA(instr);
+
+    spin_lock_irqsave(&trident->reg_lock, flags);
+
+    if (trident->device == TRIDENT_DEVICE_ID_SI7018)
+        voice->GVSel = 1;	/* route to Wave volume */
+
+    voice->CTRL = 0;
+    voice->Alpha = 0;
+    voice->FMS = 0;
+
+    loop_start = simple->loop_start >> 4;
+    loop_end = simple->loop_end >> 4;
+    sample_start = (simple->start + position) >> 4;
+    if( sample_start >= simple->size )
+        sample_start = simple->start >> 4;
+    sample_end = simple->size;
+    start_offset = position >> 4;
+
+    if (simple->format & SIMPLE_WAVE_16BIT) {
+        voice->CTRL |= 8;
+        shift++;
+    }
+    if (simple->format & SIMPLE_WAVE_STEREO) {
+        voice->CTRL |= 4;
+        shift++;
+    }
+    if (!(simple->format & SIMPLE_WAVE_UNSIGNED))
+        voice->CTRL |= 2;
+
+    voice->LBA = simple->address.memory;
+
+    if (simple->format & SIMPLE_WAVE_LOOP) {
+        voice->CTRL |= 1;
+        voice->LBA += loop_start << shift;
+        if( start_offset >= loop_start ) {
+            voice->CSO = start_offset - loop_start;
+            voice->negCSO = 0;
+        } else {
+            voice->CSO = loop_start - start_offset;
+            voice->negCSO = 1;
+        }
+        voice->ESO = loop_end - loop_start - 1;
+    } else {
+        voice->LBA += start_offset << shift;
+        voice->CSO = sample_start;
+        voice->ESO = sample_end - 1;
+        voice->negCSO = 0;
+    }
+
+    if (voice->flags & SNDRV_TRIDENT_VFLG_RUNNING) {
+        snd_trident_stop_voice(trident, voice->number);
+        voice->flags &= ~SNDRV_TRIDENT_VFLG_RUNNING;
+    }
+
+    /* set CSO sign */
+    value = inl(TRID_REG(trident, T4D_SIGN_CSO_A));
+    if( voice->negCSO ) {
+        value |= 1 << (voice->number&31);
+    } else {
+        value &= ~(1 << (voice->number&31));
+    }
+    outl(value,TRID_REG(trident, T4D_SIGN_CSO_A));
+
+    voice->Attribute = 0;
+    snd_trident_write_voice_regs(trident, voice);
+    snd_trident_start_voice(trident, voice->number);
+    voice->flags |= SNDRV_TRIDENT_VFLG_RUNNING;
+    spin_unlock_irqrestore(&trident->reg_lock, flags);
+    snd_seq_instr_free_use(trident->synth.ilist, instr);
+}
+
+static void sample_stop(struct snd_trident * trident, struct snd_trident_voice * voice, int mode)
+{
+    unsigned long flags;
+
+    if (!(voice->flags & SNDRV_TRIDENT_VFLG_RUNNING))
+        return;
+
+    switch (mode) {
+    default:
+        spin_lock_irqsave(&trident->reg_lock, flags);
+        snd_trident_stop_voice(trident, voice->number);
+        voice->flags &= ~SNDRV_TRIDENT_VFLG_RUNNING;
+        spin_unlock_irqrestore(&trident->reg_lock, flags);
+        break;
+    case SAMPLE_STOP_LOOP:	/* disable loop only */
+        voice->CTRL &= ~1;
+        spin_lock_irqsave(&trident->reg_lock, flags);
+        outb((unsigned char) voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
+        outw((((voice->CTRL << 12) | (voice->EC & 0x0fff)) & 0xffff), CH_GVSEL_PAN_VOL_CTRL_EC);
+        spin_unlock_irqrestore(&trident->reg_lock, flags);
+        break;
+    }
+}
+
+static void sample_freq(struct snd_trident * trident, struct snd_trident_voice * voice, snd_seq_frequency_t freq)
+{
+    unsigned long flags;
+    freq >>= 4;
+
+    spin_lock_irqsave(&trident->reg_lock, flags);
+    if (freq == 44100)
+        voice->Delta = 0xeb3;
+    else if (freq == 8000)
+        voice->Delta = 0x2ab;
+    else if (freq == 48000)
+        voice->Delta = 0x1000;
+    else
+        voice->Delta = (((freq << 12) + freq) / 48000) & 0x0000ffff;
+
+    outb((unsigned char) voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
+    if (trident->device == TRIDENT_DEVICE_ID_NX) {
+        outb((unsigned char) voice->Delta, TRID_REG(trident, CH_NX_DELTA_CSO + 3));
+        outb((unsigned char) (voice->Delta >> 8), TRID_REG(trident, CH_NX_DELTA_ESO + 3));
+    } else {
+        outw((unsigned short) voice->Delta, TRID_REG(trident, CH_DX_ESO_DELTA));
+    }
+
+    spin_unlock_irqrestore(&trident->reg_lock, flags);
+}
+
+static void sample_volume(struct snd_trident * trident, struct snd_trident_voice * voice, snd_seq_ev_volume_t * volume)
+{
+    unsigned long flags;
+    unsigned short value;
+
+    spin_lock_irqsave(&trident->reg_lock, flags);
+    voice->GVSel = 0;	/* use global music volume */
+    voice->FMC = 0x03;	/* fixme: can we do something useful with FMC? */
+    if (volume->volume >= 0) {
+        volume->volume &= 0x3fff;
+        /* linear volume -> logarithmic attenuation conversion
+         * uses EC register for greater resolution (6.6 bits) than Vol register (5.3 bits)
+         * Vol register used when additional attenuation is required */
+        voice->RVol = 0;
+        voice->CVol = 0;
+        value = log_from_linear( volume->volume );
+        voice->Vol = 0;
+        voice->EC = (value & 0x3fff) >> 2;
+        if (value > 0x3fff) {
+            voice->EC |= 0xfc0;
+            if (value < 0x5f00 )
+                voice->Vol = ((value >> 8) - 0x3f) << 5;
+            else {
+                voice->Vol = 0x3ff;
+                voice->EC = 0xfff;
+            }
+        }
+    }
+    if (volume->lr >= 0) {
+        volume->lr &= 0x3fff;
+        /* approximate linear pan by attenuating channels */
+        if (volume->lr >= 0x2000) {	/* attenuate left (pan right) */
+            value = 0x3fff - volume->lr;
+            for (voice->Pan = 0; voice->Pan < 63; voice->Pan++ )
+                if (value >= pan_table[voice->Pan] )
+                    break;
+        } else {			/* attenuate right (pan left) */
+            for (voice->Pan = 0; voice->Pan < 63; voice->Pan++ )
+                if ((unsigned int)volume->lr >= pan_table[voice->Pan] )
+                    break;
+            voice->Pan |= 0x40;
+        }
+    }
+    outb((unsigned char) voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
+    outl((voice->GVSel << 31) | ((voice->Pan & 0x0000007f) << 24) |
+         ((voice->Vol & 0x000000ff) << 16) | ((voice->CTRL & 0x0000000f) << 12) |
+         (voice->EC & 0x00000fff), TRID_REG(trident, CH_GVSEL_PAN_VOL_CTRL_EC));
+    value = ((voice->FMC & 0x03) << 14) | ((voice->RVol & 0x7f) << 7) | (voice->CVol & 0x7f);
+    outw(value, TRID_REG(trident, CH_DX_FMC_RVOL_CVOL));
+    spin_unlock_irqrestore(&trident->reg_lock, flags);
+}
+
+static void sample_loop(struct snd_trident * trident, struct snd_trident_voice * voice, snd_seq_ev_loop_t * loop)
+{
+    unsigned long flags;
+    simple_instrument_t *simple;
+    snd_seq_kinstr_t *instr;
+    unsigned int loop_start, loop_end;
+
+    instr = snd_seq_instr_find(trident->synth.ilist, &voice->instr, 0, 1);
+    if (instr == NULL)
+        return;
+    voice->instr = instr->instr;	/* copy ID to speedup aliases */
+    simple = KINSTR_DATA(instr);
+
+    loop_start = loop->start >> 4;
+    loop_end = loop->end >> 4;
+
+    spin_lock_irqsave(&trident->reg_lock, flags);
+
+    voice->LBA = simple->address.memory + loop_start;
+    voice->CSO = 0;
+    voice->ESO = loop_end - loop_start - 1;
+
+    outb((unsigned char) voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
+    outb((voice->LBA >> 16), TRID_REG(trident, CH_LBA + 2));
+    outw((voice->LBA & 0xffff), TRID_REG(trident, CH_LBA));
+    if (trident->device == TRIDENT_DEVICE_ID_NX) {
+        outb((voice->ESO >> 16), TRID_REG(trident, CH_NX_DELTA_ESO + 2));
+        outw((voice->ESO & 0xffff), TRID_REG(trident, CH_NX_DELTA_ESO));
+        outb((voice->CSO >> 16), TRID_REG(trident, CH_NX_DELTA_CSO + 2));
+        outw((voice->CSO & 0xffff), TRID_REG(trident, CH_NX_DELTA_CSO));
+    } else {
+        outw((voice->ESO & 0xffff), TRID_REG(trident, CH_DX_ESO_DELTA + 2));
+        outw((voice->CSO & 0xffff), TRID_REG(trident, CH_DX_CSO_ALPHA_FMS + 2));
+    }
+
+    spin_unlock_irqrestore(&trident->reg_lock, flags);
+    snd_seq_instr_free_use(trident->synth.ilist, instr);
+}
+
+static void sample_pos(struct snd_trident * trident, struct snd_trident_voice * voice, snd_seq_position_t position)
+{
+    unsigned long flags;
+    simple_instrument_t *simple;
+    snd_seq_kinstr_t *instr;
+    unsigned int value;
+
+    instr = snd_seq_instr_find(trident->synth.ilist, &voice->instr, 0, 1);
+    if (instr == NULL)
+        return;
+    voice->instr = instr->instr;	/* copy ID to speedup aliases */
+    simple = KINSTR_DATA(instr);
+
+    spin_lock_irqsave(&trident->reg_lock, flags);
+
+    if (simple->format & SIMPLE_WAVE_LOOP) {
+        if( position >= simple->loop_start ) {
+            voice->CSO = (position - simple->loop_start) >> 4;
+            voice->negCSO = 0;
+        } else {
+            voice->CSO = (simple->loop_start - position) >> 4;
+            voice->negCSO = 1;
+        }
+    } else {
+        voice->CSO = position >> 4;
+        voice->negCSO = 0;
+    }
+
+    /* set CSO sign */
+    value = inl(TRID_REG(trident, T4D_SIGN_CSO_A));
+    if( voice->negCSO ) {
+        value |= 1 << (voice->number&31);
+    } else {
+        value &= ~(1 << (voice->number&31));
+    }
+    outl(value,TRID_REG(trident, T4D_SIGN_CSO_A));
+
+
+    outb((unsigned char) voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
+    if (trident->device == TRIDENT_DEVICE_ID_NX) {
+        outw((voice->CSO & 0xffff), TRID_REG(trident, CH_NX_DELTA_CSO));
+        outb((voice->CSO >> 16), TRID_REG(trident, CH_NX_DELTA_CSO + 2));
+    } else {
+        outw((voice->CSO & 0xffff), TRID_REG(trident, CH_DX_CSO_ALPHA_FMS) + 2);
+    }
+
+    spin_unlock_irqrestore(&trident->reg_lock, flags);
+    snd_seq_instr_free_use(trident->synth.ilist, instr);
+}
+
+static void sample_private1(struct snd_trident * trident, struct snd_trident_voice * voice, unsigned char *data)
+{
+}
+
+/*
+ * Memory management / sample loading
+ */
+
+static int snd_trident_simple_put_sample(void *private_data, simple_instrument_t * instr,
+					 char __user *data, long len, int atomic)
+{
+	struct snd_trident *trident = private_data;
+	int size = instr->size;
+	int shift = 0;
+
+	if (instr->format & SIMPLE_WAVE_BACKWARD ||
+	    instr->format & SIMPLE_WAVE_BIDIR ||
+	    instr->format & SIMPLE_WAVE_ULAW) 
+		return -EINVAL;	/* not supported */
+
+	if (instr->format & SIMPLE_WAVE_16BIT)
+		shift++;
+	if (instr->format & SIMPLE_WAVE_STEREO)
+		shift++;
+	size <<= shift;
+
+	if (trident->synth.current_size + size > trident->synth.max_size)
+		return -ENOMEM;
+
+	if (verify_area(VERIFY_READ, data, size))
+		return -EFAULT;
+
+	if (trident->tlb.entries) {
+		snd_util_memblk_t *memblk;
+		memblk = snd_trident_synth_alloc(trident, size); 
+		if (memblk == NULL)
+			return -ENOMEM;
+		if (snd_trident_synth_copy_from_user(trident, memblk, 0, data, size) ) {
+			snd_trident_synth_free(trident, memblk);
+			return -EFAULT;
+		}
+		instr->address.ptr = (unsigned char*)memblk;
+		instr->address.memory = memblk->offset;
+	} else {
+		struct snd_dma_buffer dmab;
+		if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(trident->pci),
+					size, &dmab) < 0)
+			return -ENOMEM;
+
+		if (copy_from_user(dmab.area, data, size)) {
+			snd_dma_free_pages(&dmab);
+			return -EFAULT;
+		}
+		instr->address.ptr = dmab.area;
+		instr->address.memory = dmab.addr;
+	}
+
+	trident->synth.current_size += size;
+	return 0;
+}
+
+
+static int snd_trident_simple_get_sample(void *private_data, simple_instrument_t * instr,
+                                         char *data, long len, int atomic)
+{
+    //struct snd_trident *trident = private_data;
+    int size = instr->size;
+    int shift = 0;
+
+    if (instr->format & SIMPLE_WAVE_16BIT)
+        shift++;
+    if (instr->format & SIMPLE_WAVE_STEREO)
+        shift++;
+    size <<= shift;
+
+    if (verify_area(VERIFY_WRITE, data, size))
+        return -EFAULT;
+
+    /* FIXME: not implemented yet */
+
+    return -EBUSY;
+}
+
+static int snd_trident_simple_remove_sample(void *private_data, simple_instrument_t * instr,
+                                            int atomic)
+{
+    struct snd_trident *trident = private_data;
+    int size = instr->size;
+
+    if (instr->format & SIMPLE_WAVE_16BIT)
+        size <<= 1;
+    if (instr->format & SIMPLE_WAVE_STEREO)
+        size <<= 1;
+
+    if (trident->tlb.entries) {
+        snd_util_memblk_t *memblk = (snd_util_memblk_t*)instr->address.ptr;
+        if (memblk)
+            snd_trident_synth_free(trident, memblk);
+        else
+            return -EFAULT;
+    } else {
+        struct snd_dma_buffer dmab;
+        dmab.dev.type = SNDRV_DMA_TYPE_DEV;
+        dmab.dev.dev = snd_dma_pci_data(trident->pci);
+        dmab.area = instr->address.ptr;
+        dmab.addr = instr->address.memory;
+        dmab.bytes = size;
+        snd_dma_free_pages(&dmab);
+    }
+
+    trident->synth.current_size -= size;
+    if (trident->synth.current_size < 0)	/* shouldn't need this check... */
+        trident->synth.current_size = 0;
+
+    return 0;
+}
+
+static void select_instrument(struct snd_trident * trident, struct snd_trident_voice * v)
+{
+    snd_seq_kinstr_t *instr;
+    instr = snd_seq_instr_find(trident->synth.ilist, &v->instr, 0, 1);
+    if (instr != NULL) {
+        if (instr->ops) {
+            if (instr->ops->instr_type == snd_seq_simple_id)
+                snd_trident_simple_init(v);
+        }
+        snd_seq_instr_free_use(trident->synth.ilist, instr);
+    }
+}
+
+/*
+
+*/
+
+static void event_sample(snd_seq_event_t * ev, struct snd_trident_port * p, struct snd_trident_voice * v)
+{
+    if (v->sample_ops && v->sample_ops->sample_stop)
+        v->sample_ops->sample_stop(p->trident, v, SAMPLE_STOP_IMMEDIATELY);
+    v->instr.std = ev->data.sample.param.sample.std;
+    if (v->instr.std & 0xff000000) {	/* private instrument */
+        v->instr.std &= 0x00ffffff;
+        v->instr.std |= (unsigned int)ev->source.client << 24;
+    }
+    v->instr.bank = ev->data.sample.param.sample.bank;
+    v->instr.prg = ev->data.sample.param.sample.prg;
+    select_instrument(p->trident, v);
+}
+
+static void event_cluster(snd_seq_event_t * ev, struct snd_trident_port * p, struct snd_trident_voice * v)
+{
+    if (v->sample_ops && v->sample_ops->sample_stop)
+        v->sample_ops->sample_stop(p->trident, v, SAMPLE_STOP_IMMEDIATELY);
+    v->instr.cluster = ev->data.sample.param.cluster.cluster;
+    select_instrument(p->trident, v);
+}
+
+static void event_start(snd_seq_event_t * ev, struct snd_trident_port * p, struct snd_trident_voice * v)
+{
+    if (v->sample_ops && v->sample_ops->sample_start)
+        v->sample_ops->sample_start(p->trident, v, ev->data.sample.param.position);
+}
+
+static void event_stop(snd_seq_event_t * ev, struct snd_trident_port * p, struct snd_trident_voice * v)
+{
+    if (v->sample_ops && v->sample_ops->sample_stop)
+        v->sample_ops->sample_stop(p->trident, v, ev->data.sample.param.stop_mode);
+}
+
+static void event_freq(snd_seq_event_t * ev, struct snd_trident_port * p, struct snd_trident_voice * v)
+{
+    if (v->sample_ops && v->sample_ops->sample_freq)
+        v->sample_ops->sample_freq(p->trident, v, ev->data.sample.param.frequency);
+}
+
+static void event_volume(snd_seq_event_t * ev, struct snd_trident_port * p, struct snd_trident_voice * v)
+{
+    if (v->sample_ops && v->sample_ops->sample_volume)
+        v->sample_ops->sample_volume(p->trident, v, &ev->data.sample.param.volume);
+}
+
+static void event_loop(snd_seq_event_t * ev, struct snd_trident_port * p, struct snd_trident_voice * v)
+{
+    if (v->sample_ops && v->sample_ops->sample_loop)
+        v->sample_ops->sample_loop(p->trident, v, &ev->data.sample.param.loop);
+}
+
+static void event_position(snd_seq_event_t * ev, struct snd_trident_port * p, struct snd_trident_voice * v)
+{
+    if (v->sample_ops && v->sample_ops->sample_pos)
+        v->sample_ops->sample_pos(p->trident, v, ev->data.sample.param.position);
+}
+
+static void event_private1(snd_seq_event_t * ev, struct snd_trident_port * p, struct snd_trident_voice * v)
+{
+    if (v->sample_ops && v->sample_ops->sample_private1)
+        v->sample_ops->sample_private1(p->trident, v, (unsigned char *) &ev->data.sample.param.raw8);
+}
+
+typedef void (trident_sample_event_handler_t) (snd_seq_event_t * ev, struct snd_trident_port * p, struct snd_trident_voice * v);
+
+static trident_sample_event_handler_t *trident_sample_event_handlers[9] =
+{
+    event_sample,
+    event_cluster,
+    event_start,
+    event_stop,
+    event_freq,
+    event_volume,
+    event_loop,
+    event_position,
+    event_private1
+};
+
+static void snd_trident_sample_event(snd_seq_event_t * ev, struct snd_trident_port * p)
+{
+    int idx, voice;
+    struct snd_trident *trident = p->trident;
+    struct snd_trident_voice *v;
+    unsigned long flags;
+
+    idx = ev->type - SNDRV_SEQ_EVENT_SAMPLE;
+    if (idx < 0 || idx > 8)
+        return;
+    for (voice = 0; voice < 64; voice++) {
+        v = &trident->synth.voices[voice];
+        if (v->use && v->client == ev->source.client &&
+            v->port == ev->source.port &&
+            v->index == ev->data.sample.channel) {
+            spin_lock_irqsave(&trident->event_lock, flags);
+            trident_sample_event_handlers[idx] (ev, p, v);
+            spin_unlock_irqrestore(&trident->event_lock, flags);
+            return;
+        }
+    }
+}
+
+/*
+
+*/
+
+static void snd_trident_synth_free_voices(struct snd_trident * trident, int client, int port)
+{
+    int idx;
+    struct snd_trident_voice *voice;
+
+    for (idx = 0; idx < 32; idx++) {
+        voice = &trident->synth.voices[idx];
+        if (voice->use && voice->client == client && voice->port == port)
+            snd_trident_free_voice(trident, voice);
+    }
+}
+
+static int snd_trident_synth_use(void *private_data, snd_seq_port_subscribe_t * info)
+{
+    struct snd_trident_port *port = (struct snd_trident_port *) private_data;
+    struct snd_trident *trident = port->trident;
+    struct snd_trident_voice *voice;
+    unsigned int idx;
+    unsigned long flags;
+
+    if (info->voices > 32)
+        return -EINVAL;
+    spin_lock_irqsave(&trident->reg_lock, flags);
+    for (idx = 0; idx < info->voices; idx++) {
+        voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_SYNTH, info->sender.client, info->sender.port);
+        if (voice == NULL) {
+            snd_trident_synth_free_voices(trident, info->sender.client, info->sender.port);
+            spin_unlock_irqrestore(&trident->reg_lock, flags);
+            return -EBUSY;
+        }
+        voice->index = idx;
+        voice->Vol = 0x3ff;
+        voice->EC = 0x0fff;
+    }
+#if 0
+    for (idx = 0; idx < info->midi_voices; idx++) {
+        port->midi_has_voices = 1;
+        voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_MIDI, info->sender.client, info->sender.port);
+        if (voice == NULL) {
+            snd_trident_synth_free_voices(trident, info->sender.client, info->sender.port);
+            spin_unlock_irqrestore(&trident->reg_lock, flags);
+            return -EBUSY;
+        }
+        voice->Vol = 0x3ff;
+        voice->EC = 0x0fff;
+    }
+#endif
+    spin_unlock_irqrestore(&trident->reg_lock, flags);
+    return 0;
+}
+
+static int snd_trident_synth_unuse(void *private_data, snd_seq_port_subscribe_t * info)
+{
+    struct snd_trident_port *port = (struct snd_trident_port *) private_data;
+    struct snd_trident *trident = port->trident;
+    unsigned long flags;
+
+    spin_lock_irqsave(&trident->reg_lock, flags);
+    snd_trident_synth_free_voices(trident, info->sender.client, info->sender.port);
+    spin_unlock_irqrestore(&trident->reg_lock, flags);
+    return 0;
+}
+
+/*
+
+*/
+
+static void snd_trident_synth_free_private_instruments(struct snd_trident_port * p, int client)
+{
+    snd_seq_instr_header_t ifree;
+
+    memset(&ifree, 0, sizeof(ifree));
+    ifree.cmd = SNDRV_SEQ_INSTR_FREE_CMD_PRIVATE;
+    snd_seq_instr_list_free_cond(p->trident->synth.ilist, &ifree, client, 0);
+}
+
+int snd_trident_synth_event_input(snd_seq_event_t * ev, int direct, void *private_data, int atomic, int hop)
+{
+    struct snd_trident_port *p = (struct snd_trident_port *) private_data;
+
+    if (p == NULL)
+        return -EINVAL;
+    if (ev->type >= SNDRV_SEQ_EVENT_SAMPLE &&
+        ev->type <= SNDRV_SEQ_EVENT_SAMPLE_PRIVATE1) {
+        snd_trident_sample_event(ev, p);
+        return 0;
+    }
+    if (ev->source.client == SNDRV_SEQ_CLIENT_SYSTEM &&
+        ev->source.port == SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE) {
+        if (ev->type == SNDRV_SEQ_EVENT_CLIENT_EXIT) {
+            snd_trident_synth_free_private_instruments(p, ev->data.addr.client);
+            return 0;
+        }
+    }
+    if (direct) {
+        if (ev->type >= SNDRV_SEQ_EVENT_INSTR_BEGIN) {
+            snd_seq_instr_event(&p->trident->synth.simple_ops.kops,
+                                p->trident->synth.ilist, ev,
+                                p->trident->synth.seq_client, atomic, hop);
+            return 0;
+        }
+    }
+    return 0;
+}
+
+static void snd_trident_synth_instr_notify(void *private_data,
+                                           snd_seq_kinstr_t * instr,
+                                           int what)
+{
+    int idx;
+    struct snd_trident *trident = private_data;
+    struct snd_trident_voice *pvoice;
+    unsigned long flags;
+
+    spin_lock_irqsave(&trident->event_lock, flags);
+    for (idx = 0; idx < 64; idx++) {
+        pvoice = &trident->synth.voices[idx];
+        if (pvoice->use && !memcmp(&pvoice->instr, &instr->instr, sizeof(pvoice->instr))) {
+            if (pvoice->sample_ops && pvoice->sample_ops->sample_stop) {
+                pvoice->sample_ops->sample_stop(trident, pvoice, SAMPLE_STOP_IMMEDIATELY);
+            } else {
+                snd_trident_stop_voice(trident, pvoice->number);
+                pvoice->flags &= ~SNDRV_TRIDENT_VFLG_RUNNING;
+            }
+        }
+    }
+    spin_unlock_irqrestore(&trident->event_lock, flags);
+}
+
+/*
+
+*/
+
+static void snd_trident_synth_free_port(void *private_data)
+{
+    struct snd_trident_port *p = (struct snd_trident_port *) private_data;
+
+    if (p)
+        snd_midi_channel_free_set(p->chset);
+}
+
+static int snd_trident_synth_create_port(struct snd_trident * trident, int idx)
+{
+    struct snd_trident_port *p;
+    struct snd_seq_port_callback callbacks;
+    char name[32];
+    char *str;
+    int result;
+
+    p = &trident->synth.seq_ports[idx];
+    p->chset = snd_midi_channel_alloc_set(16);
+    if (p->chset == NULL)
+        return -ENOMEM;
+    p->chset->private_data = p;
+    p->trident = trident;
+    p->client = trident->synth.seq_client;
+
+    memset(&callbacks, 0, sizeof(callbacks));
+    callbacks.owner = THIS_MODULE;
+    callbacks.use = snd_trident_synth_use;
+    callbacks.unuse = snd_trident_synth_unuse;
+    callbacks.event_input = snd_trident_synth_event_input;
+    callbacks.private_free = snd_trident_synth_free_port;
+    callbacks.private_data = p;
+
+    str = "???";
+    switch (trident->device) {
+    case TRIDENT_DEVICE_ID_DX:	str = "Trident 4DWave-DX"; break;
+    case TRIDENT_DEVICE_ID_NX:	str = "Trident 4DWave-NX"; break;
+    case TRIDENT_DEVICE_ID_SI7018:	str = "SiS 7018"; break;
+    }
+    sprintf(name, "%s port %i", str, idx);
+    p->chset->port = snd_seq_event_port_attach(trident->synth.seq_client,
+                                               &callbacks,
+                                               SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE,
+                                               SNDRV_SEQ_PORT_TYPE_DIRECT_SAMPLE |
+                                               SNDRV_SEQ_PORT_TYPE_SYNTH,
+                                               16, 0,
+                                               name);
+    if (p->chset->port < 0) {
+        result = p->chset->port;
+        snd_trident_synth_free_port(p);
+        return result;
+    }
+    p->port = p->chset->port;
+    return 0;
+}
+
+/*
+
+*/
+
+static int snd_trident_synth_new_device(snd_seq_device_t *dev)
+{
+    struct snd_trident *trident;
+    int client, i;
+    struct snd_seq_client_callback callbacks;
+    snd_seq_client_info_t cinfo;
+    snd_seq_port_subscribe_t sub;
+    snd_simple_ops_t *simpleops;
+    char *str;
+
+    trident = *(struct snd_trident **)SNDRV_SEQ_DEVICE_ARGPTR(dev);
+    if (trident == NULL)
+        return -EINVAL;
+
+    trident->synth.seq_client = -1;
+
+    /* allocate new client */
+    memset(&callbacks, 0, sizeof(callbacks));
+    callbacks.private_data = trident;
+    callbacks.allow_output = callbacks.allow_input = 1;
+    client = trident->synth.seq_client =
+        snd_seq_create_kernel_client(trident->card, 1, &callbacks);
+    if (client < 0)
+        return client;
+
+    /* change name of client */
+    memset(&cinfo, 0, sizeof(cinfo));
+    cinfo.client = client;
+    cinfo.type = KERNEL_CLIENT;
+    str = "???";
+    switch (trident->device) {
+    case TRIDENT_DEVICE_ID_DX:	str = "Trident 4DWave-DX"; break;
+    case TRIDENT_DEVICE_ID_NX:	str = "Trident 4DWave-NX"; break;
+    case TRIDENT_DEVICE_ID_SI7018:	str = "SiS 7018"; break;
+    }
+    sprintf(cinfo.name, str);
+    snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, &cinfo);
+
+    for (i = 0; i < 4; i++)
+        snd_trident_synth_create_port(trident, i);
+
+    trident->synth.ilist = snd_seq_instr_list_new();
+    if (trident->synth.ilist == NULL) {
+        snd_seq_delete_kernel_client(client);
+        trident->synth.seq_client = -1;
+        return -ENOMEM;
+    }
+    trident->synth.ilist->flags = SNDRV_SEQ_INSTR_FLG_DIRECT;
+
+    simpleops = &trident->synth.simple_ops;
+    snd_seq_simple_init(simpleops, trident, NULL);
+    simpleops->put_sample = snd_trident_simple_put_sample;
+    simpleops->get_sample = snd_trident_simple_get_sample;
+    simpleops->remove_sample = snd_trident_simple_remove_sample;
+    simpleops->notify = snd_trident_synth_instr_notify;
+
+    memset(&sub, 0, sizeof(sub));
+    sub.sender.client = SNDRV_SEQ_CLIENT_SYSTEM;
+    sub.sender.port = SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE;
+    sub.dest.client = client;
+    sub.dest.port = 0;
+    snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &sub);
+
+    return 0;
+}
+
+static int snd_trident_synth_delete_device(snd_seq_device_t *dev)
+{
+    struct snd_trident *trident;
+
+    trident = *(struct snd_trident **)SNDRV_SEQ_DEVICE_ARGPTR(dev);
+    if (trident == NULL)
+        return -EINVAL;
+
+    if (trident->synth.seq_client >= 0) {
+        snd_seq_delete_kernel_client(trident->synth.seq_client);
+        trident->synth.seq_client = -1;
+    }
+    if (trident->synth.ilist)
+        snd_seq_instr_list_free(&trident->synth.ilist);
+    return 0;
+}
+
+static int __init alsa_trident_synth_init(void)
+{
+    static snd_seq_dev_ops_t ops =
+    {
+        snd_trident_synth_new_device,
+        snd_trident_synth_delete_device
+    };
+
+    return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_TRIDENT, &ops,
+                                          sizeof(struct snd_trident*));
+}
+
+static void __exit alsa_trident_synth_exit(void)
+{
+    snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_TRIDENT);
+}
+
+module_init(alsa_trident_synth_init)
+module_exit(alsa_trident_synth_exit)
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/via82xx.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/via82xx.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/via82xx.c	(revision 91)
@@ -0,0 +1,2603 @@
+/*
+ *   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
+ *
+ */
+
+/*
+ * Changes:
+ *
+ * Dec. 19, 2002	Takashi Iwai <tiwai@suse.de>
+ *	- use the DSX channels for the first pcm playback.
+ *	  (on VIA8233, 8233C and 8235 only)
+ *	  this will allow you play simultaneously up to 4 streams.
+ *	  multi-channel playback is assigned to the second device
+ *	  on these chips.
+ *	- support the secondary capture (on VIA8233/C,8235)
+ *	- SPDIF support
+ *	  the DSX3 channel can be used for SPDIF output.
+ *	  on VIA8233A, this channel is assigned to the second pcm
+ *	  playback.
+ *	  the card config of alsa-lib will assign the correct
+ *	  device for applications.
+ *	- clean up the code, separate low-level initialization
+ *	  routines for each chipset.
+ *
+ * Sep. 26, 2005	Karsten Wiese <annabellesgarden@yahoo.de>
+ *	- Optimize position calculation for the 823x chips. 
+ */
+
+#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 <linux/gameport.h>
+#include <linux/moduleparam.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/info.h>
+#include <sound/tlv.h>
+#include <sound/ac97_codec.h>
+#include <sound/mpu401.h>
+#include <sound/initval.h>
+
+#if 0
+#define POINTER_DEBUG
+#endif
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("VIA VT82xx audio");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{VIA,VT82C686A/B/C,pci},{VIA,VT8233A/C,8235}}");
+
+#ifndef PCI_DEVICE_ID_VIA_8233_5
+#define PCI_DEVICE_ID_VIA_8233_5        0x3059
+#endif
+
+#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
+#define SUPPORT_JOYSTICK 1
+#endif
+
+static int index = SNDRV_DEFAULT_IDX1;	/* Index 0-MAX */
+static char *id = SNDRV_DEFAULT_STR1;	/* ID for this card */
+static long mpu_port;
+#ifdef SUPPORT_JOYSTICK
+static int joystick;
+#endif
+static int ac97_clock = 48000;
+static char *ac97_quirk;
+static int dxs_support;
+
+//module_param(index, int, 0444);
+MODULE_PARM_DESC(index, "Index value for VIA 82xx bridge.");
+//module_param(id, charp, 0444);
+MODULE_PARM_DESC(id, "ID string for VIA 82xx bridge.");
+//module_param(mpu_port, long, 0444);
+MODULE_PARM_DESC(mpu_port, "MPU-401 port. (VT82C686x only)");
+#ifdef SUPPORT_JOYSTICK
+//module_param(joystick, bool, 0444);
+MODULE_PARM_DESC(joystick, "Enable joystick. (VT82C686x only)");
+#endif
+//module_param(ac97_clock, int, 0444);
+MODULE_PARM_DESC(ac97_clock, "AC'97 codec clock (default 48000Hz).");
+//module_param(ac97_quirk, charp, 0444);
+MODULE_PARM_DESC(ac97_quirk, "AC'97 workaround for strange hardware.");
+//module_param(dxs_support, int, 0444);
+MODULE_PARM_DESC(dxs_support, "Support for DXS channels (0 = auto, 1 = enable, 2 = disable, 3 = 48k only, 4 = no VRA, 5 = enable any sample rate)");
+
+/* just for backward compatibility */
+static int enable;
+//module_param(enable, bool, 0444);
+
+
+/* revision numbers for via686 */
+#define VIA_REV_686_A		0x10
+#define VIA_REV_686_B		0x11
+#define VIA_REV_686_C		0x12
+#define VIA_REV_686_D		0x13
+#define VIA_REV_686_E		0x14
+#define VIA_REV_686_H		0x20
+
+/* 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 */
+#define VIA_REV_8237		0x60
+#define VIA_REV_8251		0x70
+
+/*
+ *  Direct registers
+ */
+
+#define VIAREG(via, x) ((via)->port + VIA_REG_##x)
+#define VIADEV_REG(viadev, x) ((viadev)->port + VIA_REG_##x)
+
+/* common offsets */
+#define VIA_REG_OFFSET_STATUS		0x00	/* byte - channel status */
+#define   VIA_REG_STAT_ACTIVE		0x80	/* RO */
+#define   VIA8233_SHADOW_STAT_ACTIVE	0x08	/* 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 (for via8233 only) */
+
+#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 */
+/* via686 */
+#define   VIA_REG_SGD_STAT_PB_FLAG	(1<<0)
+#define   VIA_REG_SGD_STAT_CP_FLAG	(1<<1)
+#define   VIA_REG_SGD_STAT_FM_FLAG	(1<<2)
+#define   VIA_REG_SGD_STAT_PB_EOL	(1<<4)
+#define   VIA_REG_SGD_STAT_CP_EOL	(1<<5)
+#define   VIA_REG_SGD_STAT_FM_EOL	(1<<6)
+#define   VIA_REG_SGD_STAT_PB_STOP	(1<<8)
+#define   VIA_REG_SGD_STAT_CP_STOP	(1<<9)
+#define   VIA_REG_SGD_STAT_FM_STOP	(1<<10)
+#define   VIA_REG_SGD_STAT_PB_ACTIVE	(1<<12)
+#define   VIA_REG_SGD_STAT_CP_ACTIVE	(1<<13)
+#define   VIA_REG_SGD_STAT_FM_ACTIVE	(1<<14)
+/* via8233 */
+#define   VIA8233_REG_SGD_STAT_FLAG	(1<<0)
+#define   VIA8233_REG_SGD_STAT_EOL	(1<<1)
+#define   VIA8233_REG_SGD_STAT_STOP	(1<<2)
+#define   VIA8233_REG_SGD_STAT_ACTIVE	(1<<3)
+#define VIA8233_INTR_MASK(chan) ((VIA8233_REG_SGD_STAT_FLAG|VIA8233_REG_SGD_STAT_EOL) << ((chan) * 4))
+#define   VIA8233_REG_SGD_CHAN_SDX	0
+#define   VIA8233_REG_SGD_CHAN_MULTI	4
+#define   VIA8233_REG_SGD_CHAN_REC	6
+#define   VIA8233_REG_SGD_CHAN_REC1	7
+
+#define VIA_REG_GPI_STATUS		0x88
+#define VIA_REG_GPI_INTR		0x8c
+
+/* multi-channel and capture registers for via8233 */
+DEFINE_VIA_REGSET(MULTPLAY, 0x40);
+DEFINE_VIA_REGSET(CAPTURE_8233, 0x60);
+
+/* via8233-specific registers */
+#define VIA_REG_OFS_PLAYBACK_VOLUME_L	0x02	/* byte */
+#define VIA_REG_OFS_PLAYBACK_VOLUME_R	0x03	/* byte */
+#define VIA_REG_OFS_MULTPLAY_FORMAT	0x02	/* 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_OFS_CAPTURE_FIFO	0x02	/* byte - bit 6 = fifo  enable */
+#define   VIA_REG_CAPTURE_FIFO_ENABLE	0x40
+
+#define VIA_DXS_MAX_VOLUME		31	/* max. volume (attenuation) of reg 0x32/33 */
+
+#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
+
+/* pci space */
+#define VIA_ACLINK_STAT		0x40
+#define  VIA_ACLINK_C11_READY	0x20
+#define  VIA_ACLINK_C10_READY	0x10
+#define  VIA_ACLINK_C01_READY	0x04 /* secondary codec ready */
+#define  VIA_ACLINK_LOWPOWER	0x02 /* low-power state */
+#define  VIA_ACLINK_C00_READY	0x01 /* primary codec ready */
+#define VIA_ACLINK_CTRL		0x41
+#define  VIA_ACLINK_CTRL_ENABLE	0x80 /* 0: disable, 1: enable */
+#define  VIA_ACLINK_CTRL_RESET	0x40 /* 0: assert, 1: de-assert */
+#define  VIA_ACLINK_CTRL_SYNC	0x20 /* 0: release SYNC, 1: force SYNC hi */
+#define  VIA_ACLINK_CTRL_SDO	0x10 /* 0: release SDO, 1: force SDO hi */
+#define  VIA_ACLINK_CTRL_VRA	0x08 /* 0: disable VRA, 1: enable VRA */
+#define  VIA_ACLINK_CTRL_PCM	0x04 /* 0: disable PCM, 1: enable PCM */
+#define  VIA_ACLINK_CTRL_FM	0x02 /* via686 only */
+#define  VIA_ACLINK_CTRL_SB	0x01 /* via686 only */
+#define  VIA_ACLINK_CTRL_INIT	(VIA_ACLINK_CTRL_ENABLE|\
+				 VIA_ACLINK_CTRL_RESET|\
+				 VIA_ACLINK_CTRL_PCM|\
+				 VIA_ACLINK_CTRL_VRA)
+#define VIA_FUNC_ENABLE		0x42
+#define  VIA_FUNC_MIDI_PNP	0x80 /* FIXME: it's 0x40 in the datasheet! */
+#define  VIA_FUNC_MIDI_IRQMASK	0x40 /* FIXME: not documented! */
+#define  VIA_FUNC_RX2C_WRITE	0x20
+#define  VIA_FUNC_SB_FIFO_EMPTY	0x10
+#define  VIA_FUNC_ENABLE_GAME	0x08
+#define  VIA_FUNC_ENABLE_FM	0x04
+#define  VIA_FUNC_ENABLE_MIDI	0x02
+#define  VIA_FUNC_ENABLE_SB	0x01
+#define VIA_PNP_CONTROL		0x43
+#define VIA_FM_NMI_CTRL		0x48
+#define VIA8233_VOLCHG_CTRL	0x48
+#define VIA8233_SPDIF_CTRL	0x49
+#define  VIA8233_SPDIF_DX3	0x08
+#define  VIA8233_SPDIF_SLOT_MASK	0x03
+#define  VIA8233_SPDIF_SLOT_1011	0x00
+#define  VIA8233_SPDIF_SLOT_34		0x01
+#define  VIA8233_SPDIF_SLOT_78		0x02
+#define  VIA8233_SPDIF_SLOT_69		0x03
+
+/*
+ */
+
+#define VIA_DXS_AUTO	0
+#define VIA_DXS_ENABLE	1
+#define VIA_DXS_DISABLE	2
+#define VIA_DXS_48K	3
+#define VIA_DXS_NO_VRA	4
+#define VIA_DXS_SRC	5
+
+
+/*
+ * pcm stream
+ */
+
+struct snd_via_sg_table {
+	unsigned int offset;
+	unsigned int size;
+} ;
+
+#define VIA_TABLE_SIZE	255
+
+struct viadev {
+	unsigned int reg_offset;
+	unsigned long port;
+	int direction;	/* playback = 0, capture = 1 */
+        struct snd_pcm_substream *substream;
+	int running;
+	unsigned int tbl_entries; /* # descriptors */
+	struct snd_dma_buffer table;
+	struct snd_via_sg_table *idx_table;
+	/* for recovery from the unexpected pointer */
+	unsigned int lastpos;
+	unsigned int fragsize;
+	unsigned int bufsize;
+	unsigned int bufsize2;
+	int hwptr_done;		/* processed frame position in the buffer */
+	int in_interrupt;
+	int shadow_shift;
+};
+
+
+enum { TYPE_CARD_VIA686 = 1, TYPE_CARD_VIA8233 };
+enum { TYPE_VIA686, TYPE_VIA8233, TYPE_VIA8233A };
+
+#define VIA_MAX_DEVS	7	/* 4 playback, 1 multi, 2 capture */
+
+struct via_rate_lock {
+	spinlock_t lock;
+	int rate;
+	int used;
+};
+
+struct via82xx {
+	int irq;
+
+	unsigned long port;
+	struct resource *mpu_res;
+	int chip_type;
+	unsigned char revision;
+
+	unsigned char old_legacy;
+	unsigned char old_legacy_cfg;
+#ifdef CONFIG_PM
+	unsigned char legacy_saved;
+	unsigned char legacy_cfg_saved;
+	unsigned char spdif_ctrl_saved;
+	unsigned char capture_src_saved[2];
+	unsigned int mpu_port_saved;
+#endif
+
+	unsigned char playback_volume[4][2]; /* for VIA8233/C/8235; default = 0 */
+	unsigned char playback_volume_c[2]; /* for VIA8233/C/8235; default = 0 */
+
+	unsigned int intr_mask; /* SGD_SHADOW mask to check interrupts */
+
+	struct pci_dev *pci;
+	struct snd_card *card;
+
+	unsigned int num_devs;
+	unsigned int playback_devno, multi_devno, capture_devno;
+	struct viadev devs[VIA_MAX_DEVS];
+	struct via_rate_lock rates[2]; /* playback and capture */
+	unsigned int dxs_fixed: 1;	/* DXS channel accepts only 48kHz */
+	unsigned int no_vra: 1;		/* no need to set VRA on DXS channels */
+	unsigned int dxs_src: 1;	/* use full SRC capabilities of DXS */
+	unsigned int spdif_on: 1;	/* only spdif rates work to external DACs */
+
+	struct snd_pcm *pcms[2];
+	struct snd_rawmidi *rmidi;
+
+	struct snd_ac97_bus *ac97_bus;
+	struct snd_ac97 *ac97;
+	unsigned int ac97_clock;
+	unsigned int ac97_secondary;	/* secondary AC'97 codec is present */
+
+	spinlock_t reg_lock;
+	struct snd_info_entry *proc_entry;
+
+#ifdef SUPPORT_JOYSTICK
+	struct gameport *gameport;
+#endif
+};
+
+static struct pci_device_id snd_via82xx_ids[] = {
+	/* 0x1106, 0x3058 */
+	{ PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686_5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPE_CARD_VIA686, },	/* 686A */
+	/* 0x1106, 0x3059 */
+	{ PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8233_5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPE_CARD_VIA8233, },	/* VT8233 */
+	{ 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, snd_via82xx_ids);
+
+/*
+ */
+
+/*
+ * allocate and initialize the descriptor buffers
+ * periods = number of periods
+ * fragsize = period size in bytes
+ */
+static int build_via_table(struct viadev *dev, struct snd_pcm_substream *substream,
+			   struct pci_dev *pci,
+			   unsigned int periods, unsigned int fragsize)
+{
+	unsigned int i, idx, ofs, rest;
+	struct via82xx *chip = snd_pcm_substream_chip(substream);
+	struct snd_sg_buf *sgbuf = snd_pcm_substream_sgbuf(substream);
+
+	if (dev->table.area == NULL) {
+		/* the start of each lists must be aligned to 8 bytes,
+		 * but the kernel pages are much bigger, so we don't care
+		 */
+		if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci),
+					PAGE_ALIGN(VIA_TABLE_SIZE * 2 * 8),
+					&dev->table) < 0)
+			return -ENOMEM;
+	}
+	if (! dev->idx_table) {
+		dev->idx_table = kmalloc(sizeof(*dev->idx_table) * VIA_TABLE_SIZE, GFP_KERNEL);
+		if (! dev->idx_table)
+			return -ENOMEM;
+	}
+
+	/* fill the entries */
+	idx = 0;
+	ofs = 0;
+	for (i = 0; i < periods; i++) {
+		rest = fragsize;
+		/* fill descriptors for a period.
+		 * a period can be split to several descriptors if it's
+		 * over page boundary.
+		 */
+		do {
+			unsigned int r;
+			unsigned int flag;
+
+			if (idx >= VIA_TABLE_SIZE) {
+				snd_printk(KERN_ERR "via82xx: too much table size!\n");
+				return -EINVAL;
+			}
+			((u32 *)dev->table.area)[idx << 1] = cpu_to_le32((u32)snd_pcm_sgbuf_get_addr(sgbuf, ofs));
+			r = PAGE_SIZE - (ofs % PAGE_SIZE);
+			if (rest < r)
+				r = rest;
+			rest -= r;
+			if (! rest) {
+				if (i == periods - 1)
+					flag = VIA_TBL_BIT_EOL; /* buffer boundary */
+				else
+					flag = VIA_TBL_BIT_FLAG; /* period boundary */
+			} else
+				flag = 0; /* period continues to the next */
+			// printk("via: tbl %d: at %d  size %d (rest %d)\n", idx, ofs, r, rest);
+			((u32 *)dev->table.area)[(idx<<1) + 1] = cpu_to_le32(r | flag);
+			dev->idx_table[idx].offset = ofs;
+			dev->idx_table[idx].size = r;
+			ofs += r;
+			idx++;
+		} while (rest > 0);
+	}
+	dev->tbl_entries = idx;
+	dev->bufsize = periods * fragsize;
+	dev->bufsize2 = dev->bufsize / 2;
+	dev->fragsize = fragsize;
+	return 0;
+}
+
+
+static int clean_via_table(struct viadev *dev, struct snd_pcm_substream *substream,
+			   struct pci_dev *pci)
+{
+	if (dev->table.area) {
+		snd_dma_free_pages(&dev->table);
+		dev->table.area = NULL;
+	}
+	kfree(dev->idx_table);
+	dev->idx_table = NULL;
+	return 0;
+}
+
+/*
+ *  Basic I/O
+ */
+
+static inline unsigned int snd_via82xx_codec_xread(struct via82xx *chip)
+{
+	return inl(VIAREG(chip, AC97));
+}
+ 
+static inline void snd_via82xx_codec_xwrite(struct via82xx *chip, unsigned int val)
+{
+	outl(val, VIAREG(chip, AC97));
+}
+ 
+static int snd_via82xx_codec_ready(struct via82xx *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(KERN_ERR "codec_ready: codec %i is not ready [0x%x]\n",
+		   secondary, snd_via82xx_codec_xread(chip));
+	return -EIO;
+}
+ 
+static int snd_via82xx_codec_valid(struct via82xx *chip, int secondary)
+{
+	unsigned int timeout = 1000;	/* 1ms */
+	unsigned int val, val1;
+	unsigned int stat = !secondary ? VIA_REG_AC97_PRIMARY_VALID :
+					 VIA_REG_AC97_SECONDARY_VALID;
+	
+	while (timeout-- > 0) {
+		val = snd_via82xx_codec_xread(chip);
+		val1 = val & (VIA_REG_AC97_BUSY | stat);
+		if (val1 == stat)
+			return val & 0xffff;
+		udelay(1);
+	}
+	return -EIO;
+}
+ 
+static void snd_via82xx_codec_wait(struct snd_ac97 *ac97)
+{
+	struct via82xx *chip = ac97->private_data;
+	int err;
+	err = snd_via82xx_codec_ready(chip, ac97->num);
+	/* here we need to wait fairly for long time.. */
+	msleep(500);
+}
+
+static void snd_via82xx_codec_write(struct snd_ac97 *ac97,
+				    unsigned short reg,
+				    unsigned short val)
+{
+	struct via82xx *chip = ac97->private_data;
+	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;
+	snd_via82xx_codec_xwrite(chip, xval);
+	snd_via82xx_codec_ready(chip, ac97->num);
+}
+
+static unsigned short snd_via82xx_codec_read(struct snd_ac97 *ac97, unsigned short reg)
+{
+	struct via82xx *chip = ac97->private_data;
+	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;
+      	while (1) {
+      		if (again++ > 3) {
+			snd_printk(KERN_ERR "codec_read: codec %i is not valid [0x%x]\n",
+				   ac97->num, snd_via82xx_codec_xread(chip));
+		      	return 0xffff;
+		}
+		snd_via82xx_codec_xwrite(chip, xval);
+		udelay (20);
+		if (snd_via82xx_codec_valid(chip, ac97->num) >= 0) {
+			udelay(25);
+			val = snd_via82xx_codec_xread(chip);
+			break;
+		}
+	}
+	return val & 0xffff;
+}
+
+static void snd_via82xx_channel_reset(struct via82xx *chip, struct viadev *viadev)
+{
+	outb(VIA_REG_CTRL_PAUSE | VIA_REG_CTRL_TERMINATE | VIA_REG_CTRL_RESET,
+	     VIADEV_REG(viadev, OFFSET_CONTROL));
+	inb(VIADEV_REG(viadev, OFFSET_CONTROL));
+	udelay(50);
+	/* disable interrupts */
+	outb(0x00, VIADEV_REG(viadev, OFFSET_CONTROL));
+	/* clear interrupts */
+	outb(0x03, VIADEV_REG(viadev, OFFSET_STATUS));
+	outb(0x00, VIADEV_REG(viadev, OFFSET_TYPE)); /* for via686 */
+	// outl(0, VIADEV_REG(viadev, OFFSET_CURR_PTR));
+	viadev->lastpos = 0;
+	viadev->hwptr_done = 0;
+}
+
+
+/*
+ *  Interrupt handler
+ *  Used for 686 and 8233A
+ */
+static irqreturn_t snd_via686_interrupt(int irq, void *dev_id)
+{
+	struct via82xx *chip = dev_id;
+	unsigned int status;
+	unsigned int i;
+
+	status = inl(VIAREG(chip, SGD_SHADOW));
+	if (! (status & chip->intr_mask)) {
+		if (chip->rmidi)
+                    /* check mpu401 interrupt */
+                    //return snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data);
+		return IRQ_NONE;
+	}
+
+	/* check status for each stream */
+	spin_lock(&chip->reg_lock);
+	for (i = 0; i < chip->num_devs; i++) {
+		struct viadev *viadev = &chip->devs[i];
+		unsigned char c_status = inb(VIADEV_REG(viadev, OFFSET_STATUS));
+		if (! (c_status & (VIA_REG_STAT_EOL|VIA_REG_STAT_FLAG|VIA_REG_STAT_STOPPED)))
+			continue;
+		if (viadev->substream && viadev->running) {
+			/*
+			 * Update hwptr_done based on 'period elapsed'
+			 * interrupts. We'll use it, when the chip returns 0 
+			 * for OFFSET_CURR_COUNT.
+			 */
+			if (c_status & VIA_REG_STAT_EOL)
+				viadev->hwptr_done = 0;
+			else
+				viadev->hwptr_done += viadev->fragsize;
+			viadev->in_interrupt = c_status;
+			spin_unlock(&chip->reg_lock);
+			snd_pcm_period_elapsed(viadev->substream);
+			spin_lock(&chip->reg_lock);
+			viadev->in_interrupt = 0;
+		}
+		outb(c_status, VIADEV_REG(viadev, OFFSET_STATUS)); /* ack */
+	}
+	spin_unlock(&chip->reg_lock);
+	return IRQ_HANDLED;
+}
+
+/*
+ *  Interrupt handler
+ */
+static irqreturn_t snd_via8233_interrupt(int irq, void *dev_id)
+{
+	struct via82xx *chip = dev_id;
+	unsigned int status;
+	unsigned int i;
+	int irqreturn = 0;
+
+	/* check status for each stream */
+	spin_lock(&chip->reg_lock);
+	status = inl(VIAREG(chip, SGD_SHADOW));
+
+	for (i = 0; i < chip->num_devs; i++) {
+		struct viadev *viadev = &chip->devs[i];
+		struct snd_pcm_substream *substream;
+		unsigned char c_status, shadow_status;
+
+		shadow_status = (status >> viadev->shadow_shift) &
+			(VIA8233_SHADOW_STAT_ACTIVE|VIA_REG_STAT_EOL|
+			 VIA_REG_STAT_FLAG);
+		c_status = shadow_status & (VIA_REG_STAT_EOL|VIA_REG_STAT_FLAG);
+		if (!c_status)
+			continue;
+
+		substream = viadev->substream;
+		if (substream && viadev->running) {
+			/*
+			 * Update hwptr_done based on 'period elapsed'
+			 * interrupts. We'll use it, when the chip returns 0 
+			 * for OFFSET_CURR_COUNT.
+			 */
+			if (c_status & VIA_REG_STAT_EOL)
+				viadev->hwptr_done = 0;
+			else
+				viadev->hwptr_done += viadev->fragsize;
+			viadev->in_interrupt = c_status;
+			if (shadow_status & VIA8233_SHADOW_STAT_ACTIVE)
+				viadev->in_interrupt |= VIA_REG_STAT_ACTIVE;
+			spin_unlock(&chip->reg_lock);
+
+			snd_pcm_period_elapsed(substream);
+
+			spin_lock(&chip->reg_lock);
+			viadev->in_interrupt = 0;
+		}
+		outb(c_status, VIADEV_REG(viadev, OFFSET_STATUS)); /* ack */
+		irqreturn = 1;
+	}
+	spin_unlock(&chip->reg_lock);
+	return IRQ_RETVAL(irqreturn);
+}
+
+/*
+ *  PCM callbacks
+ */
+
+/*
+ * trigger callback
+ */
+static int snd_via82xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct via82xx *chip = snd_pcm_substream_chip(substream);
+	struct viadev *viadev = substream->runtime->private_data;
+	unsigned char val;
+
+	if (chip->chip_type != TYPE_VIA686)
+		val = VIA_REG_CTRL_INT;
+	else
+		val = 0;
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		val |= VIA_REG_CTRL_START;
+		viadev->running = 1;
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		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, VIADEV_REG(viadev, OFFSET_CONTROL));
+	if (cmd == SNDRV_PCM_TRIGGER_STOP)
+		snd_via82xx_channel_reset(chip, viadev);
+	return 0;
+}
+
+
+/*
+ * pointer callbacks
+ */
+
+/*
+ * calculate the linear position at the given sg-buffer index and the rest count
+ */
+
+#define check_invalid_pos(viadev,pos) \
+	((pos) < viadev->lastpos && ((pos) >= viadev->bufsize2 ||\
+				     viadev->lastpos < viadev->bufsize2))
+
+static inline unsigned int calc_linear_pos(struct viadev *viadev, unsigned int idx,
+					   unsigned int count)
+{
+	unsigned int size, base, res;
+
+	size = viadev->idx_table[idx].size;
+	base = viadev->idx_table[idx].offset;
+	res = base + size - count;
+	if (res >= viadev->bufsize)
+		res -= viadev->bufsize;
+
+	/* check the validity of the calculated position */
+	if (size < count) {
+		snd_printd(KERN_ERR "invalid via82xx_cur_ptr (size = %d, count = %d)\n",
+			   (int)size, (int)count);
+		res = viadev->lastpos;
+	} else {
+		if (! count) {
+			/* Some mobos report count = 0 on the DMA boundary,
+			 * i.e. count = size indeed.
+			 * Let's check whether this step is above the expected size.
+			 */
+			int delta = res - viadev->lastpos;
+			if (delta < 0)
+				delta += viadev->bufsize;
+			if ((unsigned int)delta > viadev->fragsize)
+				res = base;
+		}
+		if (check_invalid_pos(viadev, res)) {
+#ifdef POINTER_DEBUG
+			printk(KERN_DEBUG "fail: idx = %i/%i, lastpos = 0x%x, "
+			       "bufsize2 = 0x%x, offsize = 0x%x, size = 0x%x, "
+			       "count = 0x%x\n", idx, viadev->tbl_entries,
+			       viadev->lastpos, viadev->bufsize2,
+			       viadev->idx_table[idx].offset,
+			       viadev->idx_table[idx].size, count);
+#endif
+			/* count register returns full size when end of buffer is reached */
+			res = base + size;
+			if (check_invalid_pos(viadev, res)) {
+				snd_printd(KERN_ERR "invalid via82xx_cur_ptr (2), "
+					   "using last valid pointer\n");
+				res = viadev->lastpos;
+			}
+		}
+	}
+	return res;
+}
+
+/*
+ * get the current pointer on via686
+ */
+static snd_pcm_uframes_t snd_via686_pcm_pointer(struct snd_pcm_substream *substream)
+{
+	struct via82xx *chip = snd_pcm_substream_chip(substream);
+	struct viadev *viadev = substream->runtime->private_data;
+	unsigned int idx, ptr, count, res;
+
+	snd_assert(viadev->tbl_entries, return 0);
+	if (!(inb(VIADEV_REG(viadev, OFFSET_STATUS)) & VIA_REG_STAT_ACTIVE))
+		return 0;
+
+	spin_lock(&chip->reg_lock);
+	count = inl(VIADEV_REG(viadev, OFFSET_CURR_COUNT)) & 0xffffff;
+	/* The via686a does not have the current index register,
+	 * so we need to calculate the index from CURR_PTR.
+	 */
+	ptr = inl(VIADEV_REG(viadev, OFFSET_CURR_PTR));
+	if (ptr <= (unsigned int)viadev->table.addr)
+		idx = 0;
+	else /* CURR_PTR holds the address + 8 */
+		idx = ((ptr - (unsigned int)viadev->table.addr) / 8 - 1) % viadev->tbl_entries;
+	res = calc_linear_pos(viadev, idx, count);
+	viadev->lastpos = res; /* remember the last position */
+	spin_unlock(&chip->reg_lock);
+
+	return bytes_to_frames(substream->runtime, res);
+}
+
+/*
+ * get the current pointer on via823x
+ */
+static snd_pcm_uframes_t snd_via8233_pcm_pointer(struct snd_pcm_substream *substream)
+{
+	struct via82xx *chip = snd_pcm_substream_chip(substream);
+	struct viadev *viadev = substream->runtime->private_data;
+	unsigned int idx, count, res;
+	int status;
+	
+	snd_assert(viadev->tbl_entries, return 0);
+
+	spin_lock(&chip->reg_lock);
+	count = inl(VIADEV_REG(viadev, OFFSET_CURR_COUNT));
+	status = viadev->in_interrupt;
+	if (!status)
+		status = inb(VIADEV_REG(viadev, OFFSET_STATUS));
+
+	/* An apparent bug in the 8251 is worked around by sending a
+	 * REG_CTRL_START. */
+	if (chip->revision == VIA_REV_8251 && (status & VIA_REG_STAT_EOL))
+		snd_via82xx_pcm_trigger(substream, SNDRV_PCM_TRIGGER_START);
+
+	if (!(status & VIA_REG_STAT_ACTIVE)) {
+		res = 0;
+		goto unlock;
+	}
+	if (count & 0xffffff) {
+		idx = count >> 24;
+		if (idx >= viadev->tbl_entries) {
+#ifdef POINTER_DEBUG
+			printk(KERN_DEBUG "fail: invalid idx = %i/%i\n", idx,
+			       viadev->tbl_entries);
+#endif
+			res = viadev->lastpos;
+		} else {
+			count &= 0xffffff;
+			res = calc_linear_pos(viadev, idx, count);
+		}
+	} else {
+		res = viadev->hwptr_done;
+		if (!viadev->in_interrupt) {
+			if (status & VIA_REG_STAT_EOL) {
+				res = 0;
+			} else
+				if (status & VIA_REG_STAT_FLAG) {
+					res += viadev->fragsize;
+				}
+		}
+	}			    
+unlock:
+	viadev->lastpos = res;
+	spin_unlock(&chip->reg_lock);
+
+	return bytes_to_frames(substream->runtime, res);
+}
+
+
+/*
+ * hw_params callback:
+ * allocate the buffer and build up the buffer description table
+ */
+static int snd_via82xx_hw_params(struct snd_pcm_substream *substream,
+				 struct snd_pcm_hw_params *hw_params)
+{
+	struct via82xx *chip = snd_pcm_substream_chip(substream);
+	struct viadev *viadev = substream->runtime->private_data;
+	int err;
+
+	err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+	if (err < 0)
+		return err;
+	err = build_via_table(viadev, substream, chip->pci,
+			      params_periods(hw_params),
+			      params_period_bytes(hw_params));
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+
+/*
+ * hw_free callback:
+ * clean up the buffer description table and release the buffer
+ */
+static int snd_via82xx_hw_free(struct snd_pcm_substream *substream)
+{
+	struct via82xx *chip = snd_pcm_substream_chip(substream);
+	struct viadev *viadev = substream->runtime->private_data;
+
+	clean_via_table(viadev, substream, chip->pci);
+	snd_pcm_lib_free_pages(substream);
+	return 0;
+}
+
+
+/*
+ * set up the table pointer
+ */
+static void snd_via82xx_set_table_ptr(struct via82xx *chip, struct viadev *viadev)
+{
+	snd_via82xx_codec_ready(chip, 0);
+	outl((u32)viadev->table.addr, VIADEV_REG(viadev, OFFSET_TABLE_PTR));
+	udelay(20);
+	snd_via82xx_codec_ready(chip, 0);
+}
+
+/*
+ * prepare callback for playback and capture on via686
+ */
+static void via686_setup_format(struct via82xx *chip, struct viadev *viadev,
+				struct snd_pcm_runtime *runtime)
+{
+	snd_via82xx_channel_reset(chip, viadev);
+	/* this must be set after channel_reset */
+	snd_via82xx_set_table_ptr(chip, viadev);
+	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, VIADEV_REG(viadev, OFFSET_TYPE));
+}
+
+static int snd_via686_playback_prepare(struct snd_pcm_substream *substream)
+{
+	struct via82xx *chip = snd_pcm_substream_chip(substream);
+	struct viadev *viadev = substream->runtime->private_data;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+
+	snd_ac97_set_rate(chip->ac97, AC97_PCM_FRONT_DAC_RATE, runtime->rate);
+	snd_ac97_set_rate(chip->ac97, AC97_SPDIF, runtime->rate);
+	via686_setup_format(chip, viadev, runtime);
+	return 0;
+}
+
+static int snd_via686_capture_prepare(struct snd_pcm_substream *substream)
+{
+	struct via82xx *chip = snd_pcm_substream_chip(substream);
+	struct viadev *viadev = substream->runtime->private_data;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+
+	snd_ac97_set_rate(chip->ac97, AC97_PCM_LR_ADC_RATE, runtime->rate);
+	via686_setup_format(chip, viadev, runtime);
+	return 0;
+}
+
+/*
+ * lock the current rate
+ */
+static int via_lock_rate(struct via_rate_lock *rec, int rate)
+{
+	int changed = 0;
+
+	spin_lock_irq(&rec->lock);
+	if (rec->rate != rate) {
+		if (rec->rate && rec->used > 1) /* already set */
+			changed = -EINVAL;
+		else {
+			rec->rate = rate;
+			changed = 1;
+		}
+	}
+	spin_unlock_irq(&rec->lock);
+	return changed;
+}
+
+/*
+ * prepare callback for DSX playback on via823x
+ */
+static int snd_via8233_playback_prepare(struct snd_pcm_substream *substream)
+{
+	struct via82xx *chip = snd_pcm_substream_chip(substream);
+	struct viadev *viadev = substream->runtime->private_data;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	int ac97_rate = chip->dxs_src ? 48000 : runtime->rate;
+	int rate_changed;
+	u32 rbits;
+
+	if ((rate_changed = via_lock_rate(&chip->rates[0], ac97_rate)) < 0)
+		return rate_changed;
+	if (rate_changed)
+		snd_ac97_set_rate(chip->ac97, AC97_PCM_FRONT_DAC_RATE,
+				  chip->no_vra ? 48000 : runtime->rate);
+	if (chip->spdif_on && viadev->reg_offset == 0x30)
+		snd_ac97_set_rate(chip->ac97, AC97_SPDIF, runtime->rate);
+
+	if (runtime->rate == 48000)
+		rbits = 0xfffff;
+	else
+		rbits = (0x100000 / 48000) * runtime->rate +
+			((0x100000 % 48000) * runtime->rate) / 48000;
+	snd_assert((rbits & ~0xfffff) == 0, return -EINVAL);
+	snd_via82xx_channel_reset(chip, viadev);
+	snd_via82xx_set_table_ptr(chip, viadev);
+	outb(chip->playback_volume[viadev->reg_offset / 0x10][0],
+	     VIADEV_REG(viadev, OFS_PLAYBACK_VOLUME_L));
+	outb(chip->playback_volume[viadev->reg_offset / 0x10][1],
+	     VIADEV_REG(viadev, OFS_PLAYBACK_VOLUME_R));
+	outl((runtime->format == SNDRV_PCM_FORMAT_S16_LE ? VIA8233_REG_TYPE_16BIT : 0) | /* format */
+	     (runtime->channels > 1 ? VIA8233_REG_TYPE_STEREO : 0) | /* stereo */
+	     rbits | /* rate */
+	     0xff000000,    /* STOP index is never reached */
+	     VIADEV_REG(viadev, OFFSET_STOP_IDX));
+	udelay(20);
+	snd_via82xx_codec_ready(chip, 0);
+	return 0;
+}
+
+/*
+ * prepare callback for multi-channel playback on via823x
+ */
+static int snd_via8233_multi_prepare(struct snd_pcm_substream *substream)
+{
+	struct via82xx *chip = snd_pcm_substream_chip(substream);
+	struct viadev *viadev = substream->runtime->private_data;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	unsigned int slots;
+	int fmt;
+
+	if (via_lock_rate(&chip->rates[0], runtime->rate) < 0)
+		return -EINVAL;
+	snd_ac97_set_rate(chip->ac97, AC97_PCM_FRONT_DAC_RATE, runtime->rate);
+	snd_ac97_set_rate(chip->ac97, AC97_PCM_SURR_DAC_RATE, runtime->rate);
+	snd_ac97_set_rate(chip->ac97, AC97_PCM_LFE_DAC_RATE, runtime->rate);
+	snd_ac97_set_rate(chip->ac97, AC97_SPDIF, runtime->rate);
+	snd_via82xx_channel_reset(chip, viadev);
+	snd_via82xx_set_table_ptr(chip, viadev);
+
+	fmt = (runtime->format == SNDRV_PCM_FORMAT_S16_LE) ?
+		VIA_REG_MULTPLAY_FMT_16BIT : VIA_REG_MULTPLAY_FMT_8BIT;
+	fmt |= runtime->channels << 4;
+	outb(fmt, VIADEV_REG(viadev, OFS_MULTPLAY_FORMAT));
+#if 0
+	if (chip->revision == VIA_REV_8233A)
+		slots = 0;
+	else
+#endif
+	{
+		/* set sample number to slot 3, 4, 7, 8, 6, 9 (for VIA8233/C,8235) */
+		/* corresponding to FL, FR, RL, RR, C, LFE ?? */
+		switch (runtime->channels) {
+		case 1: slots = (1<<0) | (1<<4); break;
+		case 2: slots = (1<<0) | (2<<4); break;
+		case 3: slots = (1<<0) | (2<<4) | (5<<8); break;
+		case 4: slots = (1<<0) | (2<<4) | (3<<8) | (4<<12); break;
+		case 5: slots = (1<<0) | (2<<4) | (3<<8) | (4<<12) | (5<<16); break;
+		case 6: slots = (1<<0) | (2<<4) | (3<<8) | (4<<12) | (5<<16) | (6<<20); break;
+		default: slots = 0; break;
+		}
+	}
+	/* STOP index is never reached */
+	outl(0xff000000 | slots, VIADEV_REG(viadev, OFFSET_STOP_IDX));
+	udelay(20);
+	snd_via82xx_codec_ready(chip, 0);
+	return 0;
+}
+
+/*
+ * prepare callback for capture on via823x
+ */
+static int snd_via8233_capture_prepare(struct snd_pcm_substream *substream)
+{
+	struct via82xx *chip = snd_pcm_substream_chip(substream);
+	struct viadev *viadev = substream->runtime->private_data;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+
+	if (via_lock_rate(&chip->rates[1], runtime->rate) < 0)
+		return -EINVAL;
+	snd_ac97_set_rate(chip->ac97, AC97_PCM_LR_ADC_RATE, runtime->rate);
+	snd_via82xx_channel_reset(chip, viadev);
+	snd_via82xx_set_table_ptr(chip, viadev);
+	outb(VIA_REG_CAPTURE_FIFO_ENABLE, VIADEV_REG(viadev, OFS_CAPTURE_FIFO));
+	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 */
+	     VIADEV_REG(viadev, OFFSET_STOP_IDX));
+	udelay(20);
+	snd_via82xx_codec_ready(chip, 0);
+	return 0;
+}
+
+
+/*
+ * pcm hardware definition, identical for both playback and capture
+ */
+static struct snd_pcm_hardware snd_via82xx_hw =
+{
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_MMAP_VALID |
+				 /* SNDRV_PCM_INFO_RESUME | */
+				 SNDRV_PCM_INFO_PAUSE),
+	.formats =		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+	.rates =		SNDRV_PCM_RATE_48000,
+	.rate_min =		48000,
+	.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 =		VIA_TABLE_SIZE / 2,
+	.fifo_size =		0,
+};
+
+
+/*
+ * open callback skeleton
+ */
+static int snd_via82xx_pcm_open(struct via82xx *chip, struct viadev *viadev,
+				struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	int err;
+	struct via_rate_lock *ratep;
+
+	runtime->hw = snd_via82xx_hw;
+	
+	/* set the hw rate condition */
+	ratep = &chip->rates[viadev->direction];
+	spin_lock_irq(&ratep->lock);
+	ratep->used++;
+	if (chip->spdif_on && viadev->reg_offset == 0x30) {
+		/* DXS#3 and spdif is on */
+		runtime->hw.rates = chip->ac97->rates[AC97_RATES_SPDIF];
+		snd_pcm_limit_hw_rates(runtime);
+	} else if (chip->dxs_fixed && viadev->reg_offset < 0x40) {
+		/* fixed DXS playback rate */
+		runtime->hw.rates = SNDRV_PCM_RATE_48000;
+		runtime->hw.rate_min = runtime->hw.rate_max = 48000;
+	} else if (chip->dxs_src && viadev->reg_offset < 0x40) {
+		/* use full SRC capabilities of DXS */
+		runtime->hw.rates = (SNDRV_PCM_RATE_CONTINUOUS |
+				     SNDRV_PCM_RATE_8000_48000);
+		runtime->hw.rate_min = 8000;
+		runtime->hw.rate_max = 48000;
+	} else if (! ratep->rate) {
+		int idx = viadev->direction ? AC97_RATES_ADC : AC97_RATES_FRONT_DAC;
+		runtime->hw.rates = chip->ac97->rates[idx];
+		snd_pcm_limit_hw_rates(runtime);
+	} else {
+		/* a fixed rate */
+		runtime->hw.rates = SNDRV_PCM_RATE_KNOT;
+		runtime->hw.rate_max = runtime->hw.rate_min = ratep->rate;
+	}
+	spin_unlock_irq(&ratep->lock);
+
+	/* 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;
+
+	runtime->private_data = viadev;
+	viadev->substream = substream;
+
+	return 0;
+}
+
+
+/*
+ * open callback for playback on via686 and via823x DSX
+ */
+static int snd_via82xx_playback_open(struct snd_pcm_substream *substream)
+{
+	struct via82xx *chip = snd_pcm_substream_chip(substream);
+	struct viadev *viadev = &chip->devs[chip->playback_devno + substream->number];
+	int err;
+
+	if ((err = snd_via82xx_pcm_open(chip, viadev, substream)) < 0)
+		return err;
+	return 0;
+}
+
+/*
+ * open callback for playback on via823x multi-channel
+ */
+static int snd_via8233_multi_open(struct snd_pcm_substream *substream)
+{
+	struct via82xx *chip = snd_pcm_substream_chip(substream);
+	struct viadev *viadev = &chip->devs[chip->multi_devno];
+	int err;
+	/* channels constraint for VIA8233A
+	 * 3 and 5 channels are not supported
+	 */
+	static unsigned int channels[] = {
+		1, 2, 4, 6
+	};
+	static struct snd_pcm_hw_constraint_list hw_constraints_channels = {
+		.count = ARRAY_SIZE(channels),
+		.list = channels,
+		.mask = 0,
+	};
+
+	if ((err = snd_via82xx_pcm_open(chip, viadev, substream)) < 0)
+		return err;
+	substream->runtime->hw.channels_max = 6;
+	if (chip->revision == VIA_REV_8233A)
+		snd_pcm_hw_constraint_list(substream->runtime, 0,
+					   SNDRV_PCM_HW_PARAM_CHANNELS,
+					   &hw_constraints_channels);
+	return 0;
+}
+
+/*
+ * open callback for capture on via686 and via823x
+ */
+static int snd_via82xx_capture_open(struct snd_pcm_substream *substream)
+{
+	struct via82xx *chip = snd_pcm_substream_chip(substream);
+	struct viadev *viadev = &chip->devs[chip->capture_devno + substream->pcm->device];
+
+	return snd_via82xx_pcm_open(chip, viadev, substream);
+}
+
+/*
+ * close callback
+ */
+static int snd_via82xx_pcm_close(struct snd_pcm_substream *substream)
+{
+	struct via82xx *chip = snd_pcm_substream_chip(substream);
+	struct viadev *viadev = substream->runtime->private_data;
+	struct via_rate_lock *ratep;
+
+	/* release the rate lock */
+	ratep = &chip->rates[viadev->direction];
+	spin_lock_irq(&ratep->lock);
+	ratep->used--;
+	if (! ratep->used)
+		ratep->rate = 0;
+        spin_unlock_irq(&ratep->lock);
+
+        if (! ratep->rate) {
+            if (! viadev->direction) {
+                snd_ac97_update_power(chip->ac97,
+                                      AC97_PCM_FRONT_DAC_RATE, 0);
+                snd_ac97_update_power(chip->ac97,
+                                      AC97_PCM_SURR_DAC_RATE, 0);
+                snd_ac97_update_power(chip->ac97,
+                                      AC97_PCM_LFE_DAC_RATE, 0);
+            } else
+                snd_ac97_update_power(chip->ac97,
+                                      AC97_PCM_LR_ADC_RATE, 0);
+        }
+	viadev->substream = NULL;
+	return 0;
+}
+
+
+/* via686 playback callbacks */
+static struct snd_pcm_ops snd_via686_playback_ops = {
+	.open =		snd_via82xx_playback_open,
+	.close =	snd_via82xx_pcm_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_via82xx_hw_params,
+	.hw_free =	snd_via82xx_hw_free,
+	.prepare =	snd_via686_playback_prepare,
+	.trigger =	snd_via82xx_pcm_trigger,
+	.pointer =	snd_via686_pcm_pointer,
+	.page =		snd_pcm_sgbuf_ops_page,
+};
+
+/* via686 capture callbacks */
+static struct snd_pcm_ops snd_via686_capture_ops = {
+	.open =		snd_via82xx_capture_open,
+	.close =	snd_via82xx_pcm_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_via82xx_hw_params,
+	.hw_free =	snd_via82xx_hw_free,
+	.prepare =	snd_via686_capture_prepare,
+	.trigger =	snd_via82xx_pcm_trigger,
+	.pointer =	snd_via686_pcm_pointer,
+	.page =		snd_pcm_sgbuf_ops_page,
+};
+
+/* via823x DSX playback callbacks */
+static struct snd_pcm_ops snd_via8233_playback_ops = {
+	.open =		snd_via82xx_playback_open,
+	.close =	snd_via82xx_pcm_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_via82xx_hw_params,
+	.hw_free =	snd_via82xx_hw_free,
+	.prepare =	snd_via8233_playback_prepare,
+	.trigger =	snd_via82xx_pcm_trigger,
+	.pointer =	snd_via8233_pcm_pointer,
+	.page =		snd_pcm_sgbuf_ops_page,
+};
+
+/* via823x multi-channel playback callbacks */
+static struct snd_pcm_ops snd_via8233_multi_ops = {
+	.open =		snd_via8233_multi_open,
+	.close =	snd_via82xx_pcm_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_via82xx_hw_params,
+	.hw_free =	snd_via82xx_hw_free,
+	.prepare =	snd_via8233_multi_prepare,
+	.trigger =	snd_via82xx_pcm_trigger,
+	.pointer =	snd_via8233_pcm_pointer,
+	.page =		snd_pcm_sgbuf_ops_page,
+};
+
+/* via823x capture callbacks */
+static struct snd_pcm_ops snd_via8233_capture_ops = {
+	.open =		snd_via82xx_capture_open,
+	.close =	snd_via82xx_pcm_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_via82xx_hw_params,
+	.hw_free =	snd_via82xx_hw_free,
+	.prepare =	snd_via8233_capture_prepare,
+	.trigger =	snd_via82xx_pcm_trigger,
+	.pointer =	snd_via8233_pcm_pointer,
+	.page =		snd_pcm_sgbuf_ops_page,
+};
+
+
+static void init_viadev(struct via82xx *chip, int idx, unsigned int reg_offset,
+			int shadow_pos, int direction)
+{
+	chip->devs[idx].reg_offset = reg_offset;
+	chip->devs[idx].shadow_shift = shadow_pos * 4;
+	chip->devs[idx].direction = direction;
+	chip->devs[idx].port = chip->port + reg_offset;
+}
+
+/*
+ * create pcm instances for VIA8233, 8233C and 8235 (not 8233A)
+ */
+static int __devinit snd_via8233_pcm_new(struct via82xx *chip)
+{
+	struct snd_pcm *pcm;
+	int i, err;
+
+	chip->playback_devno = 0;	/* x 4 */
+	chip->multi_devno = 4;		/* x 1 */
+	chip->capture_devno = 5;	/* x 2 */
+	chip->num_devs = 7;
+	chip->intr_mask = 0x33033333; /* FLAG|EOL for rec0-1, mc, sdx0-3 */
+
+	/* PCM #0:  4 DSX playbacks and 1 capture */
+	err = snd_pcm_new(chip->card, chip->card->shortname, 0, 4, 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;
+	strcpy(pcm->name, chip->card->shortname);
+	chip->pcms[0] = pcm;
+	/* set up playbacks */
+	for (i = 0; i < 4; i++)
+		init_viadev(chip, i, 0x10 * i, i, 0);
+	/* capture */
+	init_viadev(chip, chip->capture_devno, VIA_REG_CAPTURE_8233_STATUS, 6, 1);
+
+	if ((err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
+							 snd_dma_pci_data(chip->pci),
+							 64*1024, 128*1024)) < 0)
+		return err;
+
+	/* PCM #1:  multi-channel playback and 2nd capture */
+	err = snd_pcm_new(chip->card, chip->card->shortname, 1, 1, 1, &pcm);
+	if (err < 0)
+		return err;
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_via8233_multi_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_via8233_capture_ops);
+	pcm->private_data = chip;
+	strcpy(pcm->name, chip->card->shortname);
+	chip->pcms[1] = pcm;
+	/* set up playback */
+	init_viadev(chip, chip->multi_devno, VIA_REG_MULTPLAY_STATUS, 4, 0);
+	/* set up capture */
+	init_viadev(chip, chip->capture_devno + 1, VIA_REG_CAPTURE_8233_STATUS + 0x10, 7, 1);
+
+	if ((err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
+						         snd_dma_pci_data(chip->pci),
+							 64*1024, 128*1024)) < 0)
+		return err;
+
+	return 0;
+}
+
+/*
+ * create pcm instances for VIA8233A
+ */
+static int __devinit snd_via8233a_pcm_new(struct via82xx *chip)
+{
+	struct snd_pcm *pcm;
+	int err;
+
+	chip->multi_devno = 0;
+	chip->playback_devno = 1;
+	chip->capture_devno = 2;
+	chip->num_devs = 3;
+	chip->intr_mask = 0x03033000; /* FLAG|EOL for rec0, mc, sdx3 */
+
+	/* PCM #0:  multi-channel playback and capture */
+	err = snd_pcm_new(chip->card, chip->card->shortname, 0, 1, 1, &pcm);
+	if (err < 0)
+		return err;
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_via8233_multi_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_via8233_capture_ops);
+	pcm->private_data = chip;
+	strcpy(pcm->name, chip->card->shortname);
+	chip->pcms[0] = pcm;
+	/* set up playback */
+	init_viadev(chip, chip->multi_devno, VIA_REG_MULTPLAY_STATUS, 4, 0);
+	/* capture */
+	init_viadev(chip, chip->capture_devno, VIA_REG_CAPTURE_8233_STATUS, 6, 1);
+
+	if ((err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
+							 snd_dma_pci_data(chip->pci),
+							 64*1024, 128*1024)) < 0)
+		return err;
+
+	/* SPDIF supported? */
+	if (! ac97_can_spdif(chip->ac97))
+		return 0;
+
+	/* PCM #1:  DXS3 playback (for spdif) */
+	err = snd_pcm_new(chip->card, chip->card->shortname, 1, 1, 0, &pcm);
+	if (err < 0)
+		return err;
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_via8233_playback_ops);
+	pcm->private_data = chip;
+	strcpy(pcm->name, chip->card->shortname);
+	chip->pcms[1] = pcm;
+	/* set up playback */
+	init_viadev(chip, chip->playback_devno, 0x30, 3, 0);
+
+	if ((err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
+							 snd_dma_pci_data(chip->pci),
+							 64*1024, 128*1024)) < 0)
+		return err;
+
+	return 0;
+}
+
+/*
+ * create a pcm instance for via686a/b
+ */
+static int __devinit snd_via686_pcm_new(struct via82xx *chip)
+{
+	struct snd_pcm *pcm;
+	int err;
+
+	chip->playback_devno = 0;
+	chip->capture_devno = 1;
+	chip->num_devs = 2;
+	chip->intr_mask = 0x77; /* FLAG | EOL for PB, CP, FM */
+
+	err = snd_pcm_new(chip->card, chip->card->shortname, 0, 1, 1, &pcm);
+	if (err < 0)
+		return err;
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_via686_playback_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_via686_capture_ops);
+	pcm->private_data = chip;
+	strcpy(pcm->name, chip->card->shortname);
+	chip->pcms[0] = pcm;
+	init_viadev(chip, 0, VIA_REG_PLAYBACK_STATUS, 0, 0);
+	init_viadev(chip, 1, VIA_REG_CAPTURE_STATUS, 0, 1);
+
+	if ((err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
+							 snd_dma_pci_data(chip->pci),
+							 64*1024, 128*1024)) < 0)
+		return err;
+
+	return 0;
+}
+
+
+/*
+ *  Mixer part
+ */
+
+static int snd_via8233_capture_source_info(struct snd_kcontrol *kcontrol,
+					   struct snd_ctl_elem_info *uinfo)
+{
+	/* formerly they were "Line" and "Mic", but it looks like that they
+	 * have nothing to do with the actual physical connections...
+	 */
+	static char *texts[2] = {
+		"Input1", "Input2"
+	};
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 2;
+	if (uinfo->value.enumerated.item >= 2)
+		uinfo->value.enumerated.item = 1;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int snd_via8233_capture_source_get(struct snd_kcontrol *kcontrol,
+					  struct snd_ctl_elem_value *ucontrol)
+{
+	struct via82xx *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long port = chip->port + (kcontrol->id.index ? (VIA_REG_CAPTURE_CHANNEL + 0x10) : VIA_REG_CAPTURE_CHANNEL);
+	ucontrol->value.enumerated.item[0] = inb(port) & VIA_REG_CAPTURE_CHANNEL_MIC ? 1 : 0;
+	return 0;
+}
+
+static int snd_via8233_capture_source_put(struct snd_kcontrol *kcontrol,
+					  struct snd_ctl_elem_value *ucontrol)
+{
+	struct via82xx *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long port = chip->port + (kcontrol->id.index ? (VIA_REG_CAPTURE_CHANNEL + 0x10) : VIA_REG_CAPTURE_CHANNEL);
+	u8 val, oval;
+
+	spin_lock_irq(&chip->reg_lock);
+	oval = inb(port);
+	val = oval & ~VIA_REG_CAPTURE_CHANNEL_MIC;
+	if (ucontrol->value.enumerated.item[0])
+		val |= VIA_REG_CAPTURE_CHANNEL_MIC;
+	if (val != oval)
+		outb(val, port);
+	spin_unlock_irq(&chip->reg_lock);
+	return val != oval;
+}
+
+static struct snd_kcontrol_new snd_via8233_capture_source __devinitdata = {
+	.name = "Input Source Select",
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.info = snd_via8233_capture_source_info,
+	.get = snd_via8233_capture_source_get,
+	.put = snd_via8233_capture_source_put,
+};
+
+static int snd_via8233_dxs3_spdif_info(struct snd_kcontrol *kcontrol,
+				       struct snd_ctl_elem_info *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_via8233_dxs3_spdif_get(struct snd_kcontrol *kcontrol,
+				      struct snd_ctl_elem_value *ucontrol)
+{
+	struct via82xx *chip = snd_kcontrol_chip(kcontrol);
+	u8 val;
+
+	pci_read_config_byte(chip->pci, VIA8233_SPDIF_CTRL, &val);
+	ucontrol->value.integer.value[0] = (val & VIA8233_SPDIF_DX3) ? 1 : 0;
+	return 0;
+}
+
+static int snd_via8233_dxs3_spdif_put(struct snd_kcontrol *kcontrol,
+				      struct snd_ctl_elem_value *ucontrol)
+{
+	struct via82xx *chip = snd_kcontrol_chip(kcontrol);
+	u8 val, oval;
+
+	pci_read_config_byte(chip->pci, VIA8233_SPDIF_CTRL, &oval);
+	val = oval & ~VIA8233_SPDIF_DX3;
+	if (ucontrol->value.integer.value[0])
+		val |= VIA8233_SPDIF_DX3;
+	/* save the spdif flag for rate filtering */
+	chip->spdif_on = ucontrol->value.integer.value[0] ? 1 : 0;
+	if (val != oval) {
+		pci_write_config_byte(chip->pci, VIA8233_SPDIF_CTRL, val);
+		return 1;
+	}
+	return 0;
+}
+
+static struct snd_kcontrol_new snd_via8233_dxs3_spdif_control __devinitdata = {
+	.name = SNDRV_CTL_NAME_IEC958("Output ",NONE,SWITCH),
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.info = snd_via8233_dxs3_spdif_info,
+	.get = snd_via8233_dxs3_spdif_get,
+	.put = snd_via8233_dxs3_spdif_put,
+};
+
+static int snd_via8233_dxs_volume_info(struct snd_kcontrol *kcontrol,
+				       struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = VIA_DXS_MAX_VOLUME;
+	return 0;
+}
+
+static int snd_via8233_dxs_volume_get(struct snd_kcontrol *kcontrol,
+				      struct snd_ctl_elem_value *ucontrol)
+{
+	struct via82xx *chip = snd_kcontrol_chip(kcontrol);
+	unsigned int idx = snd_ctl_get_ioff(kcontrol, &ucontrol->id);
+
+	ucontrol->value.integer.value[0] = VIA_DXS_MAX_VOLUME - chip->playback_volume[idx][0];
+	ucontrol->value.integer.value[1] = VIA_DXS_MAX_VOLUME - chip->playback_volume[idx][1];
+	return 0;
+}
+
+static int snd_via8233_pcmdxs_volume_get(struct snd_kcontrol *kcontrol,
+					 struct snd_ctl_elem_value *ucontrol)
+{
+	struct via82xx *chip = snd_kcontrol_chip(kcontrol);
+	ucontrol->value.integer.value[0] = VIA_DXS_MAX_VOLUME - chip->playback_volume_c[0];
+	ucontrol->value.integer.value[1] = VIA_DXS_MAX_VOLUME - chip->playback_volume_c[1];
+	return 0;
+}
+
+static int snd_via8233_dxs_volume_put(struct snd_kcontrol *kcontrol,
+				      struct snd_ctl_elem_value *ucontrol)
+{
+	struct via82xx *chip = snd_kcontrol_chip(kcontrol);
+	unsigned int idx = snd_ctl_get_ioff(kcontrol, &ucontrol->id);
+	unsigned long port = chip->port + 0x10 * idx;
+	unsigned char val;
+	int i, change = 0;
+
+	for (i = 0; i < 2; i++) {
+		val = ucontrol->value.integer.value[i];
+		if (val > VIA_DXS_MAX_VOLUME)
+			val = VIA_DXS_MAX_VOLUME;
+		val = VIA_DXS_MAX_VOLUME - val;
+		change |= val != chip->playback_volume[idx][i];
+		if (change) {
+			chip->playback_volume[idx][i] = val;
+			outb(val, port + VIA_REG_OFS_PLAYBACK_VOLUME_L + i);
+		}
+	}
+	return change;
+}
+
+static int snd_via8233_pcmdxs_volume_put(struct snd_kcontrol *kcontrol,
+					 struct snd_ctl_elem_value *ucontrol)
+{
+	struct via82xx *chip = snd_kcontrol_chip(kcontrol);
+	unsigned int idx;
+	unsigned char val;
+	int i, change = 0;
+
+	for (i = 0; i < 2; i++) {
+		val = ucontrol->value.integer.value[i];
+		if (val > VIA_DXS_MAX_VOLUME)
+			val = VIA_DXS_MAX_VOLUME;
+		val = VIA_DXS_MAX_VOLUME - val;
+		if (val != chip->playback_volume_c[i]) {
+			change = 1;
+			chip->playback_volume_c[i] = val;
+			for (idx = 0; idx < 4; idx++) {
+				unsigned long port = chip->port + 0x10 * idx;
+				chip->playback_volume[idx][i] = val;
+				outb(val, port + VIA_REG_OFS_PLAYBACK_VOLUME_L + i);
+			}
+		}
+	}
+	return change;
+}
+static DECLARE_TLV_DB_SCALE(db_scale_dxs, -9450, 150, 1);
+
+static struct snd_kcontrol_new snd_via8233_pcmdxs_volume_control __devinitdata = {
+	.name = "PCM Playback Volume",
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+		   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+	.info = snd_via8233_dxs_volume_info,
+	.get = snd_via8233_pcmdxs_volume_get,
+        .put = snd_via8233_pcmdxs_volume_put,
+        .tlv = { .p = db_scale_dxs }
+};
+
+static struct snd_kcontrol_new snd_via8233_dxs_volume_control __devinitdata = {
+	.name = "VIA DXS Playback Volume",
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+		   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+	.count = 4,
+	.info = snd_via8233_dxs_volume_info,
+	.get = snd_via8233_dxs_volume_get,
+        .put = snd_via8233_dxs_volume_put,
+        .tlv = { .p = db_scale_dxs }
+};
+
+/*
+ */
+
+static void snd_via82xx_mixer_free_ac97_bus(struct snd_ac97_bus *bus)
+{
+	struct via82xx *chip = bus->private_data;
+	chip->ac97_bus = NULL;
+}
+
+static void snd_via82xx_mixer_free_ac97(struct snd_ac97 *ac97)
+{
+	struct via82xx *chip = ac97->private_data;
+	chip->ac97 = NULL;
+}
+
+static struct ac97_quirk ac97_quirks[] = {
+	{
+		.subvendor = 0x1106,
+		.subdevice = 0x4161,
+		.codec_id = 0x56494161, /* VT1612A */
+		.name = "Soltek SL-75DRV5",
+		.type = AC97_TUNE_NONE
+	},
+	{	/* FIXME: which codec? */
+		.subvendor = 0x1106,
+		.subdevice = 0x4161,
+		.name = "ASRock K7VT2",
+		.type = AC97_TUNE_HP_ONLY
+	},
+	{
+		.subvendor = 0x1019,
+		.subdevice = 0x0a81,
+		.name = "ECS K7VTA3",
+		.type = AC97_TUNE_HP_ONLY
+	},
+	{
+		.subvendor = 0x1019,
+		.subdevice = 0x0a85,
+		.name = "ECS L7VMM2",
+		.type = AC97_TUNE_HP_ONLY
+	},
+	{
+		.subvendor = 0x1849,
+		.subdevice = 0x3059,
+		.name = "ASRock K7VM2",
+		.type = AC97_TUNE_HP_ONLY	/* VT1616 */
+	},
+	{
+		.subvendor = 0x14cd,
+		.subdevice = 0x7002,
+		.name = "Unknown",
+		.type = AC97_TUNE_ALC_JACK
+	},
+	{
+		.subvendor = 0x1071,
+		.subdevice = 0x8590,
+		.name = "Mitac Mobo",
+		.type = AC97_TUNE_ALC_JACK
+	},
+	{
+		.subvendor = 0x161f,
+		.subdevice = 0x202b,
+		.name = "Arima Notebook",
+		.type = AC97_TUNE_HP_ONLY,
+	},
+	{
+		.subvendor = 0x161f,
+		.subdevice = 0x2032,
+		.name = "Targa Traveller 811",
+                .type = AC97_TUNE_HP_ONLY,
+        },
+	{
+		.subvendor = 0x161f,
+		.subdevice = 0x2032,
+		.name = "m680x",
+		.type = AC97_TUNE_HP_ONLY, /* http://launchpad.net/bugs/38546 */
+	},
+	{0} /* terminator */
+};
+
+static int __devinit snd_via82xx_mixer_new(struct via82xx *chip, const char *quirk_override)
+{
+	struct snd_ac97_template ac97;
+	int err;
+	static struct snd_ac97_bus_ops ops = {
+		.write = snd_via82xx_codec_write,
+		.read = snd_via82xx_codec_read,
+		.wait = snd_via82xx_codec_wait,
+	};
+
+	if ((err = snd_ac97_bus(chip->card, 0, &ops, chip, &chip->ac97_bus)) < 0)
+		return err;
+	chip->ac97_bus->private_free = snd_via82xx_mixer_free_ac97_bus;
+	chip->ac97_bus->clock = chip->ac97_clock;
+
+	memset(&ac97, 0, sizeof(ac97));
+	ac97.private_data = chip;
+	ac97.private_free = snd_via82xx_mixer_free_ac97;
+	ac97.pci = chip->pci;
+	ac97.scaps = AC97_SCAP_SKIP_MODEM;
+	if ((err = snd_ac97_mixer(chip->ac97_bus, &ac97, &chip->ac97)) < 0)
+		return err;
+
+	snd_ac97_tune_hardware(chip->ac97, ac97_quirks, quirk_override);
+
+	if (chip->chip_type != TYPE_VIA686) {
+		/* use slot 10/11 */
+		snd_ac97_update_bits(chip->ac97, AC97_EXTENDED_STATUS, 0x03 << 4, 0x03 << 4);
+	}
+
+	return 0;
+}
+
+#ifdef SUPPORT_JOYSTICK
+#define JOYSTICK_ADDR	0x200
+static int __devinit snd_via686_create_gameport(struct via82xx *chip, unsigned char *legacy)
+{
+	struct gameport *gp;
+	struct resource *r;
+
+	if (!joystick)
+		return -ENODEV;
+
+	r = request_region(JOYSTICK_ADDR, 8, "VIA686 gameport");
+	if (!r) {
+		printk(KERN_WARNING "via82xx: cannot reserve joystick port 0x%#x\n",
+		       JOYSTICK_ADDR);
+		return -EBUSY;
+	}
+
+	chip->gameport = gp = gameport_allocate_port();
+	if (!gp) {
+		printk(KERN_ERR "via82xx: cannot allocate memory for gameport\n");
+		release_and_free_resource(r);
+		return -ENOMEM;
+	}
+
+	gameport_set_name(gp, "VIA686 Gameport");
+	gameport_set_phys(gp, "pci%s/gameport0", pci_name(chip->pci));
+	gameport_set_dev_parent(gp, &chip->pci->dev);
+	gp->io = JOYSTICK_ADDR;
+	gameport_set_port_data(gp, r);
+
+	/* Enable legacy joystick port */
+	*legacy |= VIA_FUNC_ENABLE_GAME;
+	pci_write_config_byte(chip->pci, VIA_FUNC_ENABLE, *legacy);
+
+	gameport_register_port(chip->gameport);
+
+	return 0;
+}
+
+static void snd_via686_free_gameport(struct via82xx *chip)
+{
+	if (chip->gameport) {
+		struct resource *r = gameport_get_port_data(chip->gameport);
+
+		gameport_unregister_port(chip->gameport);
+		chip->gameport = NULL;
+		release_and_free_resource(r);
+	}
+}
+#else
+static inline int snd_via686_create_gameport(struct via82xx *chip, unsigned char *legacy)
+{
+	return -ENOSYS;
+}
+static inline void snd_via686_free_gameport(struct via82xx *chip) { }
+#endif
+
+
+/*
+ *
+ */
+
+static int __devinit snd_via8233_init_misc(struct via82xx *chip)
+{
+	int i, err, caps;
+	unsigned char val;
+
+	caps = chip->chip_type == TYPE_VIA8233A ? 1 : 2;
+	for (i = 0; i < caps; i++) {
+		snd_via8233_capture_source.index = i;
+		err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_via8233_capture_source, chip));
+		if (err < 0)
+			return err;
+	}
+	if (ac97_can_spdif(chip->ac97)) {
+		err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_via8233_dxs3_spdif_control, chip));
+		if (err < 0)
+			return err;
+	}
+	if (chip->chip_type != TYPE_VIA8233A) {
+		/* when no h/w PCM volume control is found, use DXS volume control
+		 * as the PCM vol control
+		 */
+		struct snd_ctl_elem_id sid;
+		memset(&sid, 0, sizeof(sid));
+		strcpy(sid.name, "PCM Playback Volume");
+		sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+		if (! snd_ctl_find_id(chip->card, &sid)) {
+			snd_printd(KERN_INFO "Using DXS as PCM Playback\n");
+			err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_via8233_pcmdxs_volume_control, chip));
+			if (err < 0)
+				return err;
+		}
+		else /* Using DXS when PCM emulation is enabled is really weird */
+		{
+			/* Standalone DXS controls */
+			err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_via8233_dxs_volume_control, chip));
+			if (err < 0)
+				return err;
+		}
+	}
+	/* select spdif data slot 10/11 */
+	pci_read_config_byte(chip->pci, VIA8233_SPDIF_CTRL, &val);
+	val = (val & ~VIA8233_SPDIF_SLOT_MASK) | VIA8233_SPDIF_SLOT_1011;
+	val &= ~VIA8233_SPDIF_DX3; /* SPDIF off as default */
+	pci_write_config_byte(chip->pci, VIA8233_SPDIF_CTRL, val);
+
+	return 0;
+}
+
+static int __devinit snd_via686_init_misc(struct via82xx *chip)
+{
+	unsigned char legacy, legacy_cfg;
+	int rev_h = 0;
+
+	legacy = chip->old_legacy;
+	legacy_cfg = chip->old_legacy_cfg;
+	legacy |= VIA_FUNC_MIDI_IRQMASK;	/* FIXME: correct? (disable MIDI) */
+	legacy &= ~VIA_FUNC_ENABLE_GAME;	/* disable joystick */
+	if (chip->revision >= VIA_REV_686_H) {
+		rev_h = 1;
+		if (mpu_port >= 0x200) {	/* force MIDI */
+			mpu_port &= 0xfffc;
+			pci_write_config_dword(chip->pci, 0x18, mpu_port | 0x01);
+#ifdef CONFIG_PM
+			chip->mpu_port_saved = mpu_port;
+#endif
+		} else {
+			mpu_port = pci_resource_start(chip->pci, 2);
+		}
+	} else {
+		switch (mpu_port) {	/* force MIDI */
+		case 0x300:
+		case 0x310:
+		case 0x320:
+		case 0x330:
+			legacy_cfg &= ~(3 << 2);
+			legacy_cfg |= (mpu_port & 0x0030) >> 2;
+			break;
+		default:			/* no, use BIOS settings */
+			if (legacy & VIA_FUNC_ENABLE_MIDI)
+				mpu_port = 0x300 + ((legacy_cfg & 0x000c) << 2);
+			break;
+		}
+	}
+	if (mpu_port >= 0x200 &&
+	    (chip->mpu_res = request_region(mpu_port, 2, "VIA82xx MPU401"))
+	    != NULL) {
+		if (rev_h)
+			legacy |= VIA_FUNC_MIDI_PNP;	/* enable PCI I/O 2 */
+		legacy |= VIA_FUNC_ENABLE_MIDI;
+	} else {
+		if (rev_h)
+			legacy &= ~VIA_FUNC_MIDI_PNP;	/* disable PCI I/O 2 */
+		legacy &= ~VIA_FUNC_ENABLE_MIDI;
+		mpu_port = 0;
+	}
+
+	pci_write_config_byte(chip->pci, VIA_FUNC_ENABLE, legacy);
+	pci_write_config_byte(chip->pci, VIA_PNP_CONTROL, legacy_cfg);
+	if (chip->mpu_res) {
+            if (snd_mpu401_uart_new(chip->card, 0, MPU401_HW_VIA686A,
+                                    mpu_port, MPU401_INFO_INTEGRATED,
+                                    chip->irq, 0, &chip->rmidi) < 0) {
+			printk(KERN_WARNING "unable to initialize MPU-401"
+			       " at 0x%lx, skipping\n", mpu_port);
+			legacy &= ~VIA_FUNC_ENABLE_MIDI;
+		} else {
+			legacy &= ~VIA_FUNC_MIDI_IRQMASK;	/* enable MIDI interrupt */
+		}
+		pci_write_config_byte(chip->pci, VIA_FUNC_ENABLE, legacy);
+	}
+
+	snd_via686_create_gameport(chip, &legacy);
+
+#ifdef CONFIG_PM
+	chip->legacy_saved = legacy;
+	chip->legacy_cfg_saved = legacy_cfg;
+#endif
+
+	return 0;
+}
+
+
+/*
+ * proc interface
+ */
+static void snd_via82xx_proc_read(struct snd_info_entry *entry,
+				  struct snd_info_buffer *buffer)
+{
+	struct via82xx *chip = entry->private_data;
+	int i;
+	
+	snd_iprintf(buffer, "%s\n\n", chip->card->longname);
+	for (i = 0; i < 0xa0; i += 4) {
+		snd_iprintf(buffer, "%02x: %08x\n", i, inl(chip->port + i));
+	}
+}
+
+static void __devinit snd_via82xx_proc_init(struct via82xx *chip)
+{
+	struct snd_info_entry *entry;
+
+	if (! snd_card_proc_new(chip->card, "via82xx", &entry))
+		snd_info_set_text_ops(entry, chip, 1024, snd_via82xx_proc_read);
+}
+
+/*
+ *
+ */
+
+static int snd_via82xx_chip_init(struct via82xx *chip)
+{
+	unsigned int val;
+	unsigned long end_time;
+	unsigned char pval;
+
+#if 0 /* broken on K7M? */
+	if (chip->chip_type == TYPE_VIA686)
+		/* disable all legacy ports */
+		pci_write_config_byte(chip->pci, VIA_FUNC_ENABLE, 0);
+#endif
+	pci_read_config_byte(chip->pci, VIA_ACLINK_STAT, &pval);
+	if (! (pval & VIA_ACLINK_C00_READY)) { /* codec not ready? */
+		/* deassert ACLink reset, force SYNC */
+		pci_write_config_byte(chip->pci, VIA_ACLINK_CTRL,
+				      VIA_ACLINK_CTRL_ENABLE |
+				      VIA_ACLINK_CTRL_RESET |
+				      VIA_ACLINK_CTRL_SYNC);
+		udelay(100);
+#if 1 /* FIXME: should we do full reset here for all chip models? */
+		pci_write_config_byte(chip->pci, VIA_ACLINK_CTRL, 0x00);
+		udelay(100);
+#else
+		/* deassert ACLink reset, force SYNC (warm AC'97 reset) */
+		pci_write_config_byte(chip->pci, VIA_ACLINK_CTRL,
+				      VIA_ACLINK_CTRL_RESET|VIA_ACLINK_CTRL_SYNC);
+		udelay(2);
+#endif
+		/* 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, VIA_ACLINK_CTRL, VIA_ACLINK_CTRL_INIT);
+		udelay(100);
+	}
+	
+	/* Make sure VRA is enabled, in case we didn't do a
+	 * complete codec reset, above */
+	pci_read_config_byte(chip->pci, VIA_ACLINK_CTRL, &pval);
+	if ((pval & VIA_ACLINK_CTRL_INIT) != VIA_ACLINK_CTRL_INIT) {
+		/* 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, VIA_ACLINK_CTRL, VIA_ACLINK_CTRL_INIT);
+		udelay(100);
+	}
+
+	/* wait until codec ready */
+	end_time = jiffies + msecs_to_jiffies(750);
+	do {
+		pci_read_config_byte(chip->pci, VIA_ACLINK_STAT, &pval);
+		if (pval & VIA_ACLINK_C00_READY) /* primary codec ready */
+			break;
+		schedule_timeout_uninterruptible(1);
+	} while (time_before(jiffies, end_time));
+
+	if ((val = snd_via82xx_codec_xread(chip)) & VIA_REG_AC97_BUSY)
+		snd_printk(KERN_ERR "AC'97 codec is not ready [0x%x]\n", val);
+
+#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));
+	end_time = jiffies + msecs_to_jiffies(750);
+	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;
+		}
+		schedule_timeout_interruptible(1);
+	} while (time_before(jiffies, end_time));
+	/* 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, VIA_FM_NMI_CTRL, 0);
+		/* disable all GPI interrupts */
+		outl(0, VIAREG(chip, GPI_INTR));
+	}
+
+	if (chip->chip_type != TYPE_VIA686) {
+		/* Workaround for Award BIOS bug:
+		 * DXS channels don't work properly with VRA if MC97 is disabled.
+		 */
+		struct pci_dev *pci;
+		pci = pci_get_device(0x1106, 0x3068, NULL); /* MC97 */
+		if (pci) {
+			unsigned char data;
+			pci_read_config_byte(pci, 0x44, &data);
+			pci_write_config_byte(pci, 0x44, data | 0x40);
+			pci_dev_put(pci);
+		}
+	}
+
+	if (chip->chip_type != TYPE_VIA8233A) {
+		int i, idx;
+		for (idx = 0; idx < 4; idx++) {
+			unsigned long port = chip->port + 0x10 * idx;
+			for (i = 0; i < 2; i++) {
+				chip->playback_volume[idx][i]=chip->playback_volume_c[i];
+				outb(chip->playback_volume_c[i],
+				     port + VIA_REG_OFS_PLAYBACK_VOLUME_L + i);
+			}
+		}
+	}
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+/*
+ * power management
+ */
+static int snd_via82xx_suspend(struct pci_dev *pci, pm_message_t state)
+{
+	struct snd_card *card = pci_get_drvdata(pci);
+	struct via82xx *chip = card->private_data;
+	int i;
+
+	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+	for (i = 0; i < 2; i++)
+		snd_pcm_suspend_all(chip->pcms[i]);
+	for (i = 0; i < chip->num_devs; i++)
+		snd_via82xx_channel_reset(chip, &chip->devs[i]);
+	synchronize_irq(chip->irq);
+	snd_ac97_suspend(chip->ac97);
+
+	/* save misc values */
+	if (chip->chip_type != TYPE_VIA686) {
+		pci_read_config_byte(chip->pci, VIA8233_SPDIF_CTRL, &chip->spdif_ctrl_saved);
+		chip->capture_src_saved[0] = inb(chip->port + VIA_REG_CAPTURE_CHANNEL);
+		chip->capture_src_saved[1] = inb(chip->port + VIA_REG_CAPTURE_CHANNEL + 0x10);
+	}
+
+	pci_disable_device(pci);
+        pci_save_state(pci);
+        pci_set_power_state(pci, pci_choose_state(pci, state));
+	return 0;
+}
+
+static int snd_via82xx_resume(struct pci_dev *pci)
+{
+	struct snd_card *card = pci_get_drvdata(pci);
+	struct via82xx *chip = card->private_data;
+	int i;
+
+	pci_set_power_state(pci, PCI_D0);
+	pci_restore_state(pci);
+	if (pci_enable_device(pci) < 0) {
+		printk(KERN_ERR "via82xx: pci_enable_device failed, "
+		       "disabling device\n");
+		snd_card_disconnect(card);
+		return -EIO;
+	}
+	pci_set_master(pci);
+
+	snd_via82xx_chip_init(chip);
+
+	if (chip->chip_type == TYPE_VIA686) {
+		if (chip->mpu_port_saved)
+			pci_write_config_dword(chip->pci, 0x18, chip->mpu_port_saved | 0x01);
+		pci_write_config_byte(chip->pci, VIA_FUNC_ENABLE, chip->legacy_saved);
+		pci_write_config_byte(chip->pci, VIA_PNP_CONTROL, chip->legacy_cfg_saved);
+	} else {
+		pci_write_config_byte(chip->pci, VIA8233_SPDIF_CTRL, chip->spdif_ctrl_saved);
+		outb(chip->capture_src_saved[0], chip->port + VIA_REG_CAPTURE_CHANNEL);
+		outb(chip->capture_src_saved[1], chip->port + VIA_REG_CAPTURE_CHANNEL + 0x10);
+	}
+
+	snd_ac97_resume(chip->ac97);
+
+	for (i = 0; i < chip->num_devs; i++)
+		snd_via82xx_channel_reset(chip, &chip->devs[i]);
+
+	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+	return 0;
+}
+#endif /* CONFIG_PM */
+
+static int snd_via82xx_free(struct via82xx *chip)
+{
+	unsigned int i;
+
+	if (chip->irq < 0)
+		goto __end_hw;
+	/* disable interrupts */
+	for (i = 0; i < chip->num_devs; i++)
+		snd_via82xx_channel_reset(chip, &chip->devs[i]);
+	synchronize_irq(chip->irq);
+      __end_hw:
+	if (chip->irq >= 0)
+		free_irq(chip->irq, chip);
+	release_and_free_resource(chip->mpu_res);
+	pci_release_regions(chip->pci);
+
+	if (chip->chip_type == TYPE_VIA686) {
+		snd_via686_free_gameport(chip);
+		pci_write_config_byte(chip->pci, VIA_FUNC_ENABLE, chip->old_legacy);
+		pci_write_config_byte(chip->pci, VIA_PNP_CONTROL, chip->old_legacy_cfg);
+	}
+	pci_disable_device(chip->pci);
+	kfree(chip);
+	return 0;
+}
+
+static int snd_via82xx_dev_free(struct snd_device *device)
+{
+	struct via82xx *chip = device->device_data;
+	return snd_via82xx_free(chip);
+}
+
+static int __devinit snd_via82xx_create(struct snd_card *card,
+					struct pci_dev *pci,
+					int chip_type,
+					int revision,
+					unsigned int ac97_clock,
+					struct via82xx ** r_via)
+{
+	struct via82xx *chip;
+	int err;
+        static struct snd_device_ops ops = {
+		.dev_free =	snd_via82xx_dev_free,
+        };
+
+	if ((err = pci_enable_device(pci)) < 0)
+		return err;
+
+	if ((chip = kzalloc(sizeof(*chip), GFP_KERNEL)) == NULL) {
+		pci_disable_device(pci);
+		return -ENOMEM;
+	}
+
+	chip->chip_type = chip_type;
+	chip->revision = revision;
+
+	spin_lock_init(&chip->reg_lock);
+	spin_lock_init(&chip->rates[0].lock);
+	spin_lock_init(&chip->rates[1].lock);
+	chip->card = card;
+	chip->pci = pci;
+	chip->irq = -1;
+
+	pci_read_config_byte(pci, VIA_FUNC_ENABLE, &chip->old_legacy);
+	pci_read_config_byte(pci, VIA_PNP_CONTROL, &chip->old_legacy_cfg);
+	pci_write_config_byte(chip->pci, VIA_FUNC_ENABLE,
+			      chip->old_legacy & ~(VIA_FUNC_ENABLE_SB|VIA_FUNC_ENABLE_FM));
+
+	if ((err = pci_request_regions(pci, card->driver)) < 0) {
+		kfree(chip);
+		pci_disable_device(pci);
+		return err;
+	}
+	chip->port = pci_resource_start(pci, 0);
+	if (request_irq(pci->irq,
+			chip_type == TYPE_VIA8233 ?
+			snd_via8233_interrupt :	snd_via686_interrupt,
+			SA_INTERRUPT|SA_SHIRQ,
+			card->driver, chip)) {
+		snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
+		snd_via82xx_free(chip);
+		return -EBUSY;
+	}
+	chip->irq = pci->irq;
+	if (ac97_clock >= 8000 && ac97_clock <= 48000)
+		chip->ac97_clock = ac97_clock;
+	synchronize_irq(chip->irq);
+
+	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);
+
+	snd_card_set_dev(card, &pci->dev);
+
+	*r_via = chip;
+	return 0;
+}
+
+struct via823x_info {
+	int revision;
+	char *name;
+	int type;
+};
+static struct via823x_info via823x_cards[] __devinitdata = {
+	{ VIA_REV_PRE_8233, "VIA 8233-Pre", TYPE_VIA8233 },
+	{ VIA_REV_8233C, "VIA 8233C", TYPE_VIA8233 },
+	{ VIA_REV_8233, "VIA 8233", TYPE_VIA8233 },
+	{ VIA_REV_8233A, "VIA 8233A", TYPE_VIA8233A },
+	{ VIA_REV_8235, "VIA 8235", TYPE_VIA8233 },
+        { VIA_REV_8237, "VIA 8237", TYPE_VIA8233 },
+        { VIA_REV_8251, "VIA 8251", TYPE_VIA8233 },
+};
+
+/*
+ * auto detection of DXS channel supports.
+ */
+struct dxs_whitelist {
+	unsigned short subvendor;
+	unsigned short subdevice; 
+	unsigned short mask; 
+	short action;	/* new dxs_support value */
+};
+
+static int __devinit check_dxs_list(struct pci_dev *pci, int revision)
+{
+	static struct dxs_whitelist whitelist[] = {
+		{ .subvendor = 0x1005, .subdevice = 0x4710, .action = VIA_DXS_ENABLE }, /* Avance Logic Mobo */
+		{ .subvendor = 0x1019, .subdevice = 0x0996, .action = VIA_DXS_48K },
+		{ .subvendor = 0x1019, .subdevice = 0x0a81, .action = VIA_DXS_NO_VRA }, /* ECS K7VTA3 v8.0 */
+		{ .subvendor = 0x1019, .subdevice = 0x0a85, .action = VIA_DXS_NO_VRA }, /* ECS L7VMM2 */
+                { .subvendor = 0x1019, .subdevice = 0xa101, .action = VIA_DXS_SRC },
+		{ .subvendor = 0x1019, .subdevice = 0xaa01, .action = VIA_DXS_SRC }, /* ECS K8T890-A */
+		{ .subvendor = 0x1025, .subdevice = 0x0033, .action = VIA_DXS_NO_VRA }, /* Acer Inspire 1353LM */
+		{ .subvendor = 0x1025, .subdevice = 0x0046, .action = VIA_DXS_SRC }, /* Acer Aspire 1524 WLMi */
+		{ .subvendor = 0x1043, .subdevice = 0x8095, .action = VIA_DXS_NO_VRA }, /* ASUS A7V8X (FIXME: possibly VIA_DXS_ENABLE?)*/
+		{ .subvendor = 0x1043, .subdevice = 0x80a1, .action = VIA_DXS_NO_VRA }, /* ASUS A7V8-X */
+		{ .subvendor = 0x1043, .subdevice = 0x80b0, .action = VIA_DXS_NO_VRA }, /* ASUS A7V600 & K8V*/ 
+		{ .subvendor = 0x1043, .subdevice = 0x810d, .action = VIA_DXS_SRC }, /* ASUS */
+		{ .subvendor = 0x1043, .subdevice = 0x812a, .action = VIA_DXS_SRC    }, /* ASUS A8V Deluxe */ 
+		{ .subvendor = 0x1043, .subdevice = 0x8174, .action = VIA_DXS_SRC    }, /* ASUS */
+		{ .subvendor = 0x1043, .subdevice = 0x81b9, .action = VIA_DXS_SRC    }, /* ASUS A8V-MX */
+		{ .subvendor = 0x1071, .subdevice = 0x8375, .action = VIA_DXS_NO_VRA }, /* Vobis/Yakumo/Mitac notebook */
+		{ .subvendor = 0x1071, .subdevice = 0x8399, .action = VIA_DXS_NO_VRA }, /* Umax AB 595T (VIA K8N800A - VT8237) */
+		{ .subvendor = 0x10cf, .subdevice = 0x118e, .action = VIA_DXS_ENABLE }, /* FSC laptop */
+		{ .subvendor = 0x1106, .subdevice = 0x4161, .action = VIA_DXS_NO_VRA }, /* ASRock K7VT2 */
+		{ .subvendor = 0x1106, .subdevice = 0x4552, .action = VIA_DXS_NO_VRA }, /* QDI Kudoz 7X/600-6AL */
+		{ .subvendor = 0x1106, .subdevice = 0xaa01, .action = VIA_DXS_NO_VRA }, /* EPIA MII */
+		{ .subvendor = 0x1106, .subdevice = 0xc001, .action = VIA_DXS_SRC }, /* Insight P4-ITX */
+		{ .subvendor = 0x1297, .subdevice = 0xa232, .action = VIA_DXS_ENABLE }, /* Shuttle ?? */
+		{ .subvendor = 0x1297, .subdevice = 0xc160, .action = VIA_DXS_ENABLE }, /* Shuttle SK41G */
+		{ .subvendor = 0x1458, .subdevice = 0xa002, .action = VIA_DXS_ENABLE }, /* Gigabyte GA-7VAXP */
+		{ .subvendor = 0x1462, .subdevice = 0x0080, .action = VIA_DXS_SRC }, /* MSI K8T Neo-FIS2R */
+		{ .subvendor = 0x1462, .subdevice = 0x0430, .action = VIA_DXS_SRC }, /* MSI 7142 (K8MM-V) */
+		{ .subvendor = 0x1462, .subdevice = 0x0470, .action = VIA_DXS_SRC }, /* MSI KT880 Delta-FSR */
+		{ .subvendor = 0x1462, .subdevice = 0x3800, .action = VIA_DXS_ENABLE }, /* MSI KT266 */
+                { .subvendor = 0x1462, .subdevice = 0x5901, .action = VIA_DXS_NO_VRA }, /* MSI KT6 Delta-SR */
+                { .subvendor = 0x1462, .subdevice = 0x7023, .action = VIA_DXS_SRC }, /* MSI K8T Neo2-FI */
+		{ .subvendor = 0x1462, .subdevice = 0x7120, .action = VIA_DXS_ENABLE }, /* MSI KT4V */
+                { .subvendor = 0x1462, .subdevice = 0x7142, .action = VIA_DXS_ENABLE }, /* MSI K8MM-V */
+                { .subvendor = 0x1462, .subdevice = 0xb012, .action = VIA_DXS_SRC }, /* P4M800/VIA8237R */
+		{ .subvendor = 0x147b, .subdevice = 0x1401, .action = VIA_DXS_ENABLE }, /* ABIT KD7(-RAID) */
+		{ .subvendor = 0x147b, .subdevice = 0x1411, .action = VIA_DXS_ENABLE }, /* ABIT VA-20 */
+		{ .subvendor = 0x147b, .subdevice = 0x1413, .action = VIA_DXS_ENABLE }, /* ABIT KV8 Pro */
+		{ .subvendor = 0x147b, .subdevice = 0x1415, .action = VIA_DXS_NO_VRA }, /* Abit AV8 */
+		{ .subvendor = 0x14ff, .subdevice = 0x0403, .action = VIA_DXS_ENABLE }, /* Twinhead mobo */
+		{ .subvendor = 0x14ff, .subdevice = 0x0408, .action = VIA_DXS_SRC }, /* Twinhead laptop */
+		{ .subvendor = 0x1558, .subdevice = 0x4701, .action = VIA_DXS_SRC }, /* Clevo D470 */
+		{ .subvendor = 0x1584, .subdevice = 0x8120, .action = VIA_DXS_ENABLE }, /* Gericom/Targa/Vobis/Uniwill laptop */
+		{ .subvendor = 0x1584, .subdevice = 0x8123, .action = VIA_DXS_NO_VRA }, /* Uniwill (Targa Visionary XP-210) */
+		{ .subvendor = 0x161f, .subdevice = 0x202b, .action = VIA_DXS_NO_VRA }, /* Amira Note book */
+		{ .subvendor = 0x161f, .subdevice = 0x2032, .action = VIA_DXS_48K }, /* m680x machines */
+		{ .subvendor = 0x1631, .subdevice = 0xe004, .action = VIA_DXS_ENABLE }, /* Easy Note 3174, Packard Bell */
+                { .subvendor = 0x1695, .subdevice = 0x3005, .action = VIA_DXS_ENABLE }, /* EPoX EP-8K9A */
+                { .subvendor = 0x1695, .subdevice = 0x300c, .action = VIA_DXS_SRC }, /* EPoX EP-8KRAI */
+                { .subvendor = 0x1695, .subdevice = 0x300e, .action = VIA_DXS_SRC }, /* EPoX 9HEAI */
+                { .subvendor = 0x16f3, .subdevice = 0x6405, .action = VIA_DXS_SRC }, /* Jetway K8M8MS */
+                { .subvendor = 0x1734, .subdevice = 0x1078, .action = VIA_DXS_SRC }, /* FSC Amilo L7300 */
+                { .subvendor = 0x1734, .subdevice = 0x1093, .action = VIA_DXS_SRC }, /* FSC */
+		{ .subvendor = 0x1734, .subdevice = 0x10ab, .action = VIA_DXS_SRC }, /* FSC */
+		{ .subvendor = 0x1849, .subdevice = 0x3059, .action = VIA_DXS_NO_VRA }, /* ASRock K7VM2 */
+		{ .subvendor = 0x1849, .subdevice = 0x9739, .action = VIA_DXS_SRC }, /* ASRock mobo(?) */
+		{ .subvendor = 0x1849, .subdevice = 0x9761, .action = VIA_DXS_SRC }, /* ASRock mobo(?) */
+		{ .subvendor = 0x1919, .subdevice = 0x200a, .action = VIA_DXS_NO_VRA }, /* Soltek SL-K8Tpro-939 */
+		{ .subvendor = 0x4005, .subdevice = 0x4710, .action = VIA_DXS_SRC },	/* MSI K7T266 Pro2 (MS-6380 V2.0) BIOS 3.7 */
+		{0} /* terminator */
+        };
+        const struct dxs_whitelist *w;
+	unsigned short subsystem_vendor;
+	unsigned short subsystem_device;
+
+	pci_read_config_word(pci, PCI_SUBSYSTEM_VENDOR_ID, &subsystem_vendor);
+	pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &subsystem_device);
+
+	for (w = whitelist; w->subvendor; w++) {
+		if (w->subvendor != subsystem_vendor)
+			continue;
+		if (w->mask) {
+			if ((w->mask & subsystem_device) == w->subdevice)
+				return w->action;
+		} else {
+			if (subsystem_device == w->subdevice)
+				return w->action;
+		}
+        }
+        /* for newer revision, default to DXS_SRC */
+        if (revision >= VIA_REV_8235)
+            return VIA_DXS_SRC;
+	/*
+	 * not detected, try 48k rate only to be sure.
+	 */
+	printk(KERN_INFO "via82xx: Assuming DXS channels with 48k fixed sample rate.\n");
+	printk(KERN_INFO "         Please try dxs_support=5 option\n");
+	printk(KERN_INFO "         and report if it works on your machine.\n");
+	printk(KERN_INFO "         For more details, read ALSA-Configuration.txt.\n");
+	return VIA_DXS_48K;
+};
+
+static int __devinit snd_via82xx_probe(struct pci_dev *pci,
+				       const struct pci_device_id *pci_id)
+{
+	struct snd_card *card;
+	struct via82xx *chip;
+	unsigned char revision;
+	int chip_type = 0, card_type;
+	unsigned int i;
+	int err;
+
+	card = snd_card_new(index, id, THIS_MODULE, 0);
+	if (card == NULL)
+		return -ENOMEM;
+
+	card_type = pci_id->driver_data;
+	pci_read_config_byte(pci, PCI_REVISION_ID, &revision);
+	switch (card_type) {
+	case TYPE_CARD_VIA686:
+		strcpy(card->driver, "VIA686A");
+		sprintf(card->shortname, "VIA 82C686A/B rev%x", revision);
+		chip_type = TYPE_VIA686;
+		break;
+	case TYPE_CARD_VIA8233:
+		chip_type = TYPE_VIA8233;
+		sprintf(card->shortname, "VIA 823x rev%x", revision);
+		for (i = 0; i < ARRAY_SIZE(via823x_cards); i++) {
+			if (revision == via823x_cards[i].revision) {
+				chip_type = via823x_cards[i].type;
+				strcpy(card->shortname, via823x_cards[i].name);
+				break;
+			}
+		}
+		if (chip_type != TYPE_VIA8233A) {
+                    if (dxs_support == VIA_DXS_AUTO)
+                        dxs_support = check_dxs_list(pci, revision);
+			/* force to use VIA8233 or 8233A model according to
+			 * dxs_support module option
+			 */
+			if (dxs_support == VIA_DXS_DISABLE)
+				chip_type = TYPE_VIA8233A;
+			else
+				chip_type = TYPE_VIA8233;
+		}
+		if (chip_type == TYPE_VIA8233A)
+			strcpy(card->driver, "VIA8233A");
+		else if (revision >= VIA_REV_8237)
+			strcpy(card->driver, "VIA8237"); /* no slog assignment */
+		else
+			strcpy(card->driver, "VIA8233");
+		break;
+	default:
+		snd_printk(KERN_ERR "invalid card type %d\n", card_type);
+		err = -EINVAL;
+		goto __error;
+	}
+		
+	if ((err = snd_via82xx_create(card, pci, chip_type, revision,
+				      ac97_clock, &chip)) < 0)
+		goto __error;
+	card->private_data = chip;
+	if ((err = snd_via82xx_mixer_new(chip, ac97_quirk)) < 0)
+		goto __error;
+
+	if (chip_type == TYPE_VIA686) {
+		if ((err = snd_via686_pcm_new(chip)) < 0 ||
+		    (err = snd_via686_init_misc(chip)) < 0)
+			goto __error;
+	} else {
+		if (chip_type == TYPE_VIA8233A) {
+			if ((err = snd_via8233a_pcm_new(chip)) < 0)
+				goto __error;
+			// chip->dxs_fixed = 1; /* FIXME: use 48k for DXS #3? */
+		} else {
+			if ((err = snd_via8233_pcm_new(chip)) < 0)
+				goto __error;
+			if (dxs_support == VIA_DXS_48K)
+				chip->dxs_fixed = 1;
+			else if (dxs_support == VIA_DXS_NO_VRA)
+				chip->no_vra = 1;
+			else if (dxs_support == VIA_DXS_SRC) {
+				chip->no_vra = 1;
+				chip->dxs_src = 1;
+			}
+		}
+		if ((err = snd_via8233_init_misc(chip)) < 0)
+			goto __error;
+	}
+
+	/* disable interrupts */
+	for (i = 0; i < chip->num_devs; i++)
+		snd_via82xx_channel_reset(chip, &chip->devs[i]);
+
+	sprintf(card->longname,
+		 "%s with %s at %#lx, irq %d", card->shortname,
+		 snd_ac97_get_short_name(chip->ac97), chip->port, chip->irq);
+
+	snd_via82xx_proc_init(chip);
+
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	pci_set_drvdata(pci, card);
+	return 0;
+
+ __error:
+	snd_card_free(card);
+	return err;
+}
+
+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),
+#ifdef CONFIG_PM
+	.suspend = snd_via82xx_suspend,
+	.resume = snd_via82xx_resume,
+#endif
+};
+
+static int __init alsa_card_via82xx_init(void)
+{
+	return pci_register_driver(&driver);
+}
+
+static void __exit alsa_card_via82xx_exit(void)
+{
+	pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_via82xx_init)
+module_exit(alsa_card_via82xx_exit)
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/ymfpci/diff.cmd
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/ymfpci/diff.cmd	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/ymfpci/diff.cmd	(revision 91)
@@ -0,0 +1,1 @@
+diff.exe -Nuwr e:\dev\uss\from_z\ua2\GPL\alsa-kernel\pci\ymfpci\%1 E:\DEV\alsa\alsa-kernel\pci\ymfpci\%1 >%1.diff
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/ymfpci/makefile.os2
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/ymfpci/makefile.os2	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/ymfpci/makefile.os2	(revision 91)
@@ -0,0 +1,51 @@
+CINCLUDES = ..\..\..\include;..\..\include;$(%WATCOM)\H
+AINCLUDES = $(DDK_BASE)\H
+
+CDEFINES = -D__KERNEL__ -DMODULE -dTARGET_OS2 -dALSA_BUILD -D__ISAPNP__ -DCONFIG_ISAPNP -D__i386_
+
+32BIT=1
+!include ..\..\..\..\makefile.inc
+
+
+#===================================================================
+#
+#   List of source files
+#
+#===================================================================
+FILE1    = ymfpci.obj ymfpci_main.obj
+FILE2    = 
+FILE3    = 
+FILE4    = 
+FILE5    = 
+FILE6    = 
+FILE7    = 
+FILE8    = 
+FILELAST =
+FILES    = $(FILE0) $(FILE1) $(FILE2) $(FILE3) $(FILE4) $(FILE5) $(FILE6) $(FILE7) $(FILE8) $(FILE9) $(FILE10) $(FILE11) $(FILE12)
+
+TARGET   = ymfpci
+
+LNKFILE  = $(OBJDIR)\$(TARGET).lnk
+
+
+#===================================================================
+#
+#   Specific dependencies
+#
+#===================================================================
+all: $(OBJDIR)\$(TARGET).lib 
+
+
+$(LNKFILE): makefile.os2
+    @%create $(LNKFILE)
+    @for %f in ($(FILES)) do @%append $(LNKFILE) -+$(OBJDIR)\%f
+
+$(OBJDIR)\$(TARGET).lib: $(LNKFILE) $(FILES)
+    $(LIB) $(OBJDIR)\$(TARGET).lib @$(LNKFILE)
+    copy $(OBJDIR)\$(TARGET).lib $(ALSA_LIB) 
+
+clean: .SYMBOLIC
+    -@rm  *.err 2>nul
+    @cd $(OBJDIR)
+    -rm *.obj *.lnk *.sys *.sym *.lst *.map *.wmap *.lib 2>nul
+    @cd ..\..
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/ymfpci/ymfpci.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/ymfpci/ymfpci.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/ymfpci/ymfpci.c	(revision 91)
@@ -0,0 +1,329 @@
+/*
+ *  The driver for the Yamaha's DS1/DS1E cards
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#define SNDRV_MAIN_OBJECT_FILE
+#include <sound/driver.h>
+#include <sound/ymfpci.h>
+#include <sound/mpu401.h>
+#include <sound/opl3.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+EXPORT_NO_SYMBOLS;
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Yamaha DS-XG PCI");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{Yamaha,YMF724},"
+		"{Yamaha,YMF724F},"
+		"{Yamaha,YMF740},"
+		"{Yamaha,YMF740C},"
+		"{Yamaha,YMF744},"
+		"{Yamaha,YMF754}}");
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE;	/* Enable this card */
+#ifdef TARGET_OS2
+static long fm_port[SNDRV_CARDS] = {REPEAT_SNDRV(-1)};
+static long mpu_port[SNDRV_CARDS] = {REPEAT_SNDRV(-1)};
+#else
+static long fm_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -1};
+static long mpu_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -1};
+#endif
+static int rear_switch[SNDRV_CARDS] = {1,1,1,1,1,1,1,1};
+
+MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(index, "Index value for the Yamaha DS-XG PCI soundcard.");
+MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC);
+MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+MODULE_PARM_DESC(id, "ID string for the Yamaha DS-XG PCI soundcard.");
+MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC);
+MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(enable, "Enable Yamaha DS-XG soundcard.");
+MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(mpu_port, "MPU-401 Port.");
+MODULE_PARM_SYNTAX(mpu_port, SNDRV_ENABLED);
+MODULE_PARM(fm_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(fm_port, "FM OPL-3 Port.");
+MODULE_PARM_SYNTAX(fm_port, SNDRV_ENABLED);
+MODULE_PARM(rear_switch, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(rear_switch, "Enable shared rear/line-in switch");
+MODULE_PARM_SYNTAX(rear_switch, SNDRV_ENABLED "," SNDRV_BOOLEAN_FALSE_DESC);
+
+static struct pci_device_id snd_ymfpci_ids[] = {
+        { 0x1073, 0x0004, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },   /* YMF724 */
+        { 0x1073, 0x000d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },   /* YMF724F */
+        { 0x1073, 0x000a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },   /* YMF740 */
+        { 0x1073, 0x000c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },   /* YMF740C */
+        { 0x1073, 0x0010, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },   /* YMF744 */
+        { 0x1073, 0x0012, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },   /* YMF754 */
+	{ 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, snd_ymfpci_ids);
+
+static int __devinit snd_card_ymfpci_probe(struct pci_dev *pci,
+					   const struct pci_device_id *pci_id)
+{
+	static int dev;
+	snd_card_t *card;
+	struct resource *fm_res = NULL;
+	struct resource *mpu_res = NULL;
+	ymfpci_t *chip;
+	opl3_t *opl3;
+	char *str;
+	int err;
+	u16 legacy_ctrl, legacy_ctrl2, old_legacy_ctrl;
+
+	if (dev >= SNDRV_CARDS)
+		return -ENODEV;
+	if (!enable[dev]) {
+			dev++;
+			return -ENOENT;
+		}
+
+	card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+	if (card == NULL)
+		return -ENOMEM;
+
+	switch (pci_id->device) {
+	case 0x0004: str = "YMF724"; break;
+	case 0x000d: str = "YMF724F"; break;
+	case 0x000a: str = "YMF740"; break;
+	case 0x000c: str = "YMF740C"; break;
+	case 0x0010: str = "YMF744"; break;
+	case 0x0012: str = "YMF754"; break;
+	default: str = "???"; break;
+	}
+	legacy_ctrl = 0;
+        legacy_ctrl2 = 0x0800;	/* SBEN = 0, SMOD = 01, LAD = 0 */
+
+#if 1
+
+	if (pci_id->device >= 0x0010) { /* YMF 744/754 */
+		if (fm_port[dev] < 0) {
+			fm_port[dev] = pci_resource_start(pci, 1);
+		}
+		if (fm_port[dev] >= 0 &&
+		    (fm_res = request_region(fm_port[dev], 4, "YMFPCI OPL3")) != NULL) {
+			legacy_ctrl |= YMFPCI_LEGACY_FMEN;
+			pci_write_config_word(pci, PCIR_DSXG_FMBASE, fm_port[dev]);
+		}
+		if (mpu_port[dev] < 0) {
+			mpu_port[dev] = pci_resource_start(pci, 1) + 0x20;
+		}
+		if (mpu_port[dev] >= 0 &&
+		    (mpu_res = request_region(mpu_port[dev], 2, "YMFPCI MPU401")) != NULL) {
+			legacy_ctrl |= YMFPCI_LEGACY_MEN;
+			pci_write_config_word(pci, PCIR_DSXG_MPU401BASE, mpu_port[dev]);
+		}
+	} else {
+		switch (fm_port[dev]) {
+		case 0x388: legacy_ctrl2 |= 0; break;
+		case 0x398: legacy_ctrl2 |= 1; break;
+		case 0x3a0: legacy_ctrl2 |= 2; break;
+		case 0x3a8: legacy_ctrl2 |= 3; break;
+		default: fm_port[dev] = -1; break;
+		}
+		if (fm_port[dev] > 0 &&
+		    (fm_res = request_region(fm_port[dev], 4, "YMFPCI OPL3")) != NULL) {
+			legacy_ctrl |= YMFPCI_LEGACY_FMEN;
+		} else {
+			legacy_ctrl2 &= ~YMFPCI_LEGACY2_FMIO;
+			fm_port[dev] = -1;
+		}
+		switch (mpu_port[dev]) {
+		case 0x330: legacy_ctrl2 |= 0 << 4; break;
+		case 0x300: legacy_ctrl2 |= 1 << 4; break;
+		case 0x332: legacy_ctrl2 |= 2 << 4; break;
+		case 0x334: legacy_ctrl2 |= 3 << 4; break;
+		default: mpu_port[dev] = -1; break;
+		}
+		if (mpu_port[dev] > 0 &&
+		    (mpu_res = request_region(mpu_port[dev], 2, "YMFPCI MPU401")) != NULL) {
+			legacy_ctrl |= YMFPCI_LEGACY_MEN;
+		} else {
+			legacy_ctrl2 &= ~YMFPCI_LEGACY2_MPUIO;
+			mpu_port[dev] = -1;
+		}
+	}
+	if (mpu_res) {
+		legacy_ctrl |= YMFPCI_LEGACY_MIEN;
+		legacy_ctrl2 |= YMFPCI_LEGACY2_IMOD;
+        }
+#endif
+	pci_read_config_word(pci, PCIR_DSXG_LEGACY, &old_legacy_ctrl);
+	pci_write_config_word(pci, PCIR_DSXG_LEGACY, legacy_ctrl);
+	pci_write_config_word(pci, PCIR_DSXG_ELEGACY, legacy_ctrl2);
+	if ((err = snd_ymfpci_create(card, pci,
+				     old_legacy_ctrl,
+			 	     &chip)) < 0) {
+		snd_card_free(card);
+		if (mpu_res) {
+			release_resource(mpu_res);
+			kfree_nocheck(mpu_res);
+		}
+		if (fm_res) {
+			release_resource(fm_res);
+			kfree_nocheck(fm_res);
+		}
+		return err;
+	}
+	chip->fm_res = fm_res;
+        chip->mpu_res = mpu_res;
+        card->private_data = chip;
+
+	strcpy(card->driver, str);
+	sprintf(card->shortname, "Yamaha DS-XG (%s)", str);
+	sprintf(card->longname, "%s at 0x%lx, irq %i",
+		card->shortname,
+		chip->reg_area_phys,
+		chip->irq);
+	if ((err = snd_ymfpci_pcm(chip, 0, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_ymfpci_pcm_spdif(chip, 1, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_ymfpci_pcm_4ch(chip, 2, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_ymfpci_pcm2(chip, 3, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_ymfpci_mixer(chip, rear_switch[dev])) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_ymfpci_timer(chip, 0)) < 0) {
+		snd_card_free(card);
+		return err;
+        }
+#if 1
+	if (chip->mpu_res) {
+            if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_YMFPCI,
+                                           mpu_port[dev],
+                                           MPU401_INFO_INTEGRATED,
+                                           pci->irq, 0, &chip->rawmidi)) < 0) {
+			printk(KERN_WARNING "ymfpci: cannot initialize MPU401 at 0x%lx, skipping...\n", mpu_port[dev]);
+			legacy_ctrl &= ~YMFPCI_LEGACY_MIEN; /* disable MPU401 irq */
+			pci_write_config_word(pci, PCIR_DSXG_LEGACY, legacy_ctrl);
+		}
+	}
+	if (chip->fm_res) {
+		if ((err = snd_opl3_create(card,
+					   fm_port[dev],
+					   fm_port[dev] + 2,
+					   OPL3_HW_OPL3, 1, &opl3)) < 0) {
+			printk(KERN_WARNING "ymfpci: cannot initialize FM OPL3 at 0x%lx, skipping...\n", fm_port[dev]);
+			legacy_ctrl &= ~YMFPCI_LEGACY_FMEN;
+			pci_write_config_word(pci, PCIR_DSXG_LEGACY, legacy_ctrl);
+		} else if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
+			snd_card_free(card);
+			snd_printk("cannot create opl3 hwdep\n");
+			return err;
+		}
+        }
+#endif
+#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
+	if ((err = snd_ymfpci_joystick(chip)) < 0) {
+		printk(KERN_WARNING "ymfpci: cannot initialize joystick, skipping...\n");
+	}
+#endif
+
+	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_card_ymfpci_remove(struct pci_dev *pci)
+{
+    snd_card_free(pci_get_drvdata(pci));
+    pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver driver = {
+    .name = "Yamaha DS-1 PCI",
+    .id_table = snd_ymfpci_ids,
+    .probe = snd_card_ymfpci_probe,
+    .remove = snd_card_ymfpci_remove,
+#ifdef CONFIG_PM
+    .suspend = snd_ymfpci_suspend,
+    .resume = snd_ymfpci_resume,
+#endif
+};
+
+
+static int __init alsa_card_ymfpci_init(void)
+{
+	int err;
+
+	if ((err = pci_module_init(&driver)) < 0) {
+#ifdef MODULE
+//		snd_printk("Yamaha DS-XG PCI soundcard not found or device busy\n");
+#endif
+		return err;
+	}
+	return 0;
+}
+
+static void __exit alsa_card_ymfpci_exit(void)
+{
+	pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_ymfpci_init)
+module_exit(alsa_card_ymfpci_exit)
+
+#ifndef MODULE
+
+/* format is: snd-card-ymfpci=snd_enable,snd_index,snd_id,
+			      snd_fm_port,snd_mpu_port,snd_mpu_irq */
+
+static int __init alsa_card_ymfpci_setup(char *str)
+{
+	static unsigned __initdata nr_dev = 0;
+
+	if (nr_dev >= SNDRV_CARDS)
+		return 0;
+	(void)(get_option(&str,&enable[nr_dev]) == 2 &&
+	       get_option(&str,&index[nr_dev]) == 2 &&
+	       get_id(&str,&id[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&fm_port[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&mpu_port[nr_dev]) == 2);
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-ymfpci=", alsa_card_ymfpci_setup);
+
+#endif /* ifndef MODULE */
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/ymfpci/ymfpci_image.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/ymfpci/ymfpci_image.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/ymfpci/ymfpci_image.h	(revision 91)
@@ -0,0 +1,1565 @@
+#ifndef _HWMCODE_
+#define _HWMCODE_
+
+static unsigned long DspInst[YDSXG_DSPLENGTH / 4] = {
+	0x00000081, 0x000001a4, 0x0000000a, 0x0000002f,
+	0x00080253, 0x01800317, 0x0000407b, 0x0000843f,
+	0x0001483c, 0x0001943c, 0x0005d83c, 0x00001c3c,
+	0x0000c07b, 0x00050c3f, 0x0121503c, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000
+};
+
+static unsigned long CntrlInst[YDSXG_CTRLLENGTH / 4] = {
+	0x000007, 0x240007, 0x0C0007, 0x1C0007,
+	0x060007, 0x700002, 0x000020, 0x030040,
+	0x007104, 0x004286, 0x030040, 0x000F0D,
+	0x000810, 0x20043A, 0x000282, 0x00020D,
+	0x000810, 0x20043A, 0x001282, 0x200E82,
+	0x001A82, 0x032D0D, 0x000810, 0x10043A,
+	0x02D38D, 0x000810, 0x18043A, 0x00010D,
+	0x020015, 0x0000FD, 0x000020, 0x038860,
+	0x039060, 0x038060, 0x038040, 0x038040,
+	0x038040, 0x018040, 0x000A7D, 0x038040,
+	0x038040, 0x018040, 0x200402, 0x000882,
+	0x08001A, 0x000904, 0x015986, 0x000007,
+	0x260007, 0x000007, 0x000007, 0x018A06,
+	0x000007, 0x030C8D, 0x000810, 0x18043A,
+	0x260007, 0x00087D, 0x018042, 0x00160A,
+	0x04A206, 0x000007, 0x00218D, 0x000810,
+	0x08043A, 0x21C206, 0x000007, 0x0007FD,
+	0x018042, 0x08000A, 0x000904, 0x029386,
+	0x000195, 0x090D04, 0x000007, 0x000820,
+	0x0000F5, 0x000B7D, 0x01F060, 0x0000FD,
+	0x032206, 0x018040, 0x000A7D, 0x038042,
+	0x13804A, 0x18000A, 0x001820, 0x059060,
+	0x058860, 0x018040, 0x0000FD, 0x018042,
+	0x70000A, 0x000115, 0x071144, 0x032386,
+	0x030000, 0x007020, 0x034A06, 0x018040,
+	0x00348D, 0x000810, 0x08043A, 0x21EA06,
+	0x000007, 0x02D38D, 0x000810, 0x18043A,
+	0x018206, 0x000007, 0x240007, 0x000F8D,
+	0x000810, 0x00163A, 0x002402, 0x005C02,
+	0x0028FD, 0x000020, 0x018040, 0x08000D,
+	0x000815, 0x510984, 0x000007, 0x00004D,
+	0x000E5D, 0x000E02, 0x00418D, 0x000810,
+	0x08043A, 0x2C8A06, 0x000007, 0x00008D,
+	0x000924, 0x000F02, 0x00458D, 0x000810,
+	0x08043A, 0x2C8A06, 0x000007, 0x00387D,
+	0x018042, 0x08000A, 0x001015, 0x010984,
+	0x018386, 0x000007, 0x01AA06, 0x000007,
+	0x0008FD, 0x018042, 0x18000A, 0x001904,
+	0x218086, 0x280007, 0x001810, 0x28043A,
+	0x280C02, 0x00000D, 0x000810, 0x28143A,
+	0x08808D, 0x000820, 0x0002FD, 0x018040,
+	0x200007, 0x00020D, 0x189904, 0x000007,
+	0x00402D, 0x0000BD, 0x0002FD, 0x018042,
+	0x08000A, 0x000904, 0x055A86, 0x000007,
+	0x000100, 0x000A20, 0x00047D, 0x018040,
+	0x018042, 0x20000A, 0x003015, 0x012144,
+	0x034986, 0x000007, 0x002104, 0x034986,
+	0x000007, 0x000F8D, 0x000810, 0x280C3A,
+	0x023944, 0x06C986, 0x000007, 0x001810,
+	0x28043A, 0x08810D, 0x000820, 0x0002FD,
+	0x018040, 0x200007, 0x002810, 0x78003A,
+	0x00688D, 0x000810, 0x08043A, 0x288A06,
+	0x000007, 0x00400D, 0x001015, 0x189904,
+	0x292904, 0x393904, 0x000007, 0x060206,
+	0x000007, 0x0004F5, 0x00007D, 0x000020,
+	0x00008D, 0x010860, 0x018040, 0x00047D,
+	0x038042, 0x21804A, 0x18000A, 0x021944,
+	0x215886, 0x000007, 0x004075, 0x71F104,
+	0x000007, 0x010042, 0x28000A, 0x002904,
+	0x212086, 0x000007, 0x003C0D, 0x30A904,
+	0x000007, 0x00077D, 0x018042, 0x08000A,
+	0x000904, 0x07DA86, 0x00057D, 0x002820,
+	0x03B060, 0x07F206, 0x018040, 0x003020,
+	0x03A860, 0x018040, 0x0002FD, 0x018042,
+	0x08000A, 0x000904, 0x07FA86, 0x000007,
+	0x00057D, 0x018042, 0x28040A, 0x000E8D,
+	0x000810, 0x280C3A, 0x00000D, 0x000810,
+	0x28143A, 0x09000D, 0x000820, 0x0002FD,
+	0x018040, 0x200007, 0x003DFD, 0x000020,
+	0x018040, 0x00107D, 0x008D8D, 0x000810,
+	0x08043A, 0x288A06, 0x000007, 0x000815,
+	0x08001A, 0x010984, 0x095186, 0x00137D,
+	0x200500, 0x280F20, 0x338F60, 0x3B8F60,
+	0x438F60, 0x4B8F60, 0x538F60, 0x5B8F60,
+	0x038A60, 0x018040, 0x007FBD, 0x383DC4,
+	0x000007, 0x001A7D, 0x001375, 0x018042,
+	0x09004A, 0x10000A, 0x0B8D04, 0x139504,
+	0x000007, 0x000820, 0x019060, 0x001104,
+	0x212086, 0x010040, 0x0017FD, 0x018042,
+	0x08000A, 0x000904, 0x212286, 0x000007,
+	0x00197D, 0x038042, 0x09804A, 0x10000A,
+	0x000924, 0x001664, 0x0011FD, 0x038042,
+	0x2B804A, 0x19804A, 0x00008D, 0x218944,
+	0x000007, 0x002244, 0x0AE186, 0x000007,
+	0x001A64, 0x002A24, 0x00197D, 0x080102,
+	0x100122, 0x000820, 0x039060, 0x018040,
+	0x003DFD, 0x00008D, 0x000820, 0x018040,
+	0x001375, 0x001A7D, 0x010042, 0x09804A,
+	0x10000A, 0x00021D, 0x0189E4, 0x2992E4,
+	0x309144, 0x000007, 0x00060D, 0x000A15,
+	0x000C1D, 0x001025, 0x00A9E4, 0x012BE4,
+	0x000464, 0x01B3E4, 0x0232E4, 0x000464,
+	0x000464, 0x000464, 0x000464, 0x00040D,
+	0x08B1C4, 0x000007, 0x000820, 0x000BF5,
+	0x030040, 0x00197D, 0x038042, 0x09804A,
+	0x000A24, 0x08000A, 0x080E64, 0x000007,
+	0x100122, 0x000820, 0x031060, 0x010040,
+	0x0064AC, 0x00027D, 0x000020, 0x018040,
+	0x00107D, 0x018042, 0x0011FD, 0x3B804A,
+	0x09804A, 0x20000A, 0x000095, 0x1A1144,
+	0x00A144, 0x0D2086, 0x00040D, 0x00B984,
+	0x0D2186, 0x0018FD, 0x018042, 0x0010FD,
+	0x09804A, 0x28000A, 0x000095, 0x010924,
+	0x002A64, 0x0D1186, 0x000007, 0x002904,
+	0x0D2286, 0x000007, 0x0D2A06, 0x080002,
+	0x00008D, 0x00387D, 0x000820, 0x018040,
+	0x00127D, 0x018042, 0x10000A, 0x003904,
+	0x0DD186, 0x00080D, 0x7FFFB5, 0x00B984,
+	0x0DA186, 0x000025, 0x0E7A06, 0x00002D,
+	0x000015, 0x00082D, 0x02C78D, 0x000820,
+	0x0EC206, 0x00000D, 0x7F8035, 0x00B984,
+	0x0E7186, 0x400025, 0x00008D, 0x110944,
+	0x000007, 0x00018D, 0x109504, 0x000007,
+	0x009164, 0x000424, 0x000424, 0x000424,
+	0x100102, 0x280002, 0x02C68D, 0x000820,
+	0x0EC206, 0x00018D, 0x00042D, 0x00008D,
+	0x109504, 0x000007, 0x00020D, 0x109184,
+	0x000007, 0x02C70D, 0x000820, 0x00008D,
+	0x0038FD, 0x018040, 0x003BFD, 0x001020,
+	0x03A860, 0x000815, 0x313184, 0x212184,
+	0x000007, 0x03B060, 0x03A060, 0x018040,
+	0x0022FD, 0x000095, 0x010924, 0x000424,
+	0x000424, 0x001264, 0x100102, 0x000820,
+	0x039060, 0x018040, 0x001924, 0x00FB8D,
+	0x00397D, 0x000820, 0x058040, 0x038042,
+	0x09844A, 0x000606, 0x08040A, 0x000424,
+	0x000424, 0x00117D, 0x018042, 0x08000A,
+	0x000A24, 0x280502, 0x280C02, 0x09800D,
+	0x000820, 0x0002FD, 0x018040, 0x200007,
+	0x0022FD, 0x018042, 0x08000A, 0x000095,
+	0x280DC4, 0x011924, 0x00197D, 0x018042,
+	0x0011FD, 0x09804A, 0x10000A, 0x0000B5,
+	0x113144, 0x0A8D04, 0x000007, 0x080A44,
+	0x129504, 0x000007, 0x0023FD, 0x001020,
+	0x038040, 0x101244, 0x000007, 0x000820,
+	0x039060, 0x018040, 0x0002FD, 0x018042,
+	0x08000A, 0x000904, 0x10FA86, 0x000007,
+	0x003BFD, 0x000100, 0x000A10, 0x0B807A,
+	0x13804A, 0x090984, 0x000007, 0x000095,
+	0x013D04, 0x118086, 0x10000A, 0x100002,
+	0x090984, 0x000007, 0x038042, 0x11804A,
+	0x090D04, 0x000007, 0x10000A, 0x090D84,
+	0x000007, 0x00257D, 0x000820, 0x018040,
+	0x00010D, 0x000810, 0x28143A, 0x00127D,
+	0x018042, 0x20000A, 0x00197D, 0x018042,
+	0x00117D, 0x31804A, 0x10000A, 0x003124,
+	0x01280D, 0x00397D, 0x000820, 0x058040,
+	0x038042, 0x09844A, 0x000606, 0x08040A,
+	0x300102, 0x003124, 0x000424, 0x000424,
+	0x001224, 0x280502, 0x001A4C, 0x130186,
+	0x700002, 0x00002D, 0x030000, 0x00387D,
+	0x018042, 0x10000A, 0x132A06, 0x002124,
+	0x0000AD, 0x100002, 0x00010D, 0x000924,
+	0x006B24, 0x01368D, 0x00397D, 0x000820,
+	0x058040, 0x038042, 0x09844A, 0x000606,
+	0x08040A, 0x003264, 0x00008D, 0x000A24,
+	0x001020, 0x00227D, 0x018040, 0x013C0D,
+	0x000810, 0x08043A, 0x29D206, 0x000007,
+	0x002820, 0x00207D, 0x018040, 0x00117D,
+	0x038042, 0x13804A, 0x33800A, 0x00387D,
+	0x018042, 0x08000A, 0x000904, 0x163A86,
+	0x000007, 0x00008D, 0x030964, 0x01478D,
+	0x00397D, 0x000820, 0x058040, 0x038042,
+	0x09844A, 0x000606, 0x08040A, 0x380102,
+	0x000424, 0x000424, 0x001224, 0x0002FD,
+	0x018042, 0x08000A, 0x000904, 0x14A286,
+	0x000007, 0x280502, 0x001A4C, 0x163986,
+	0x000007, 0x032164, 0x00632C, 0x003DFD,
+	0x018042, 0x08000A, 0x000095, 0x090904,
+	0x000007, 0x000820, 0x001A4C, 0x156186,
+	0x018040, 0x030000, 0x157A06, 0x002124,
+	0x00010D, 0x000924, 0x006B24, 0x015B8D,
+	0x00397D, 0x000820, 0x058040, 0x038042,
+	0x09844A, 0x000606, 0x08040A, 0x003A64,
+	0x000095, 0x001224, 0x0002FD, 0x018042,
+	0x08000A, 0x000904, 0x15DA86, 0x000007,
+	0x01628D, 0x000810, 0x08043A, 0x29D206,
+	0x000007, 0x14D206, 0x000007, 0x007020,
+	0x08010A, 0x10012A, 0x0020FD, 0x038860,
+	0x039060, 0x018040, 0x00227D, 0x018042,
+	0x003DFD, 0x08000A, 0x31844A, 0x000904,
+	0x16D886, 0x18008B, 0x00008D, 0x189904,
+	0x00312C, 0x17AA06, 0x000007, 0x00324C,
+	0x173386, 0x000007, 0x001904, 0x173086,
+	0x000007, 0x000095, 0x199144, 0x00222C,
+	0x003124, 0x00636C, 0x000E3D, 0x001375,
+	0x000BFD, 0x010042, 0x09804A, 0x10000A,
+	0x038AEC, 0x0393EC, 0x00224C, 0x17A986,
+	0x000007, 0x00008D, 0x189904, 0x00226C,
+	0x00322C, 0x30050A, 0x301DAB, 0x002083,
+	0x0018FD, 0x018042, 0x08000A, 0x018924,
+	0x300502, 0x001083, 0x001875, 0x010042,
+	0x10000A, 0x00008D, 0x010924, 0x001375,
+	0x330542, 0x330CCB, 0x332CCB, 0x3334CB,
+	0x333CCB, 0x3344CB, 0x334CCB, 0x3354CB,
+	0x305C8B, 0x006083, 0x0002F5, 0x010042,
+	0x08000A, 0x000904, 0x187A86, 0x000007,
+	0x001E2D, 0x0005FD, 0x018042, 0x08000A,
+	0x028924, 0x280502, 0x00060D, 0x000810,
+	0x280C3A, 0x00008D, 0x000810, 0x28143A,
+	0x0A808D, 0x000820, 0x0002F5, 0x010040,
+	0x220007, 0x001275, 0x030042, 0x21004A,
+	0x00008D, 0x1A0944, 0x000007, 0x01980D,
+	0x000810, 0x08043A, 0x2B2206, 0x000007,
+	0x0001F5, 0x030042, 0x0D004A, 0x10000A,
+	0x089144, 0x000007, 0x000820, 0x010040,
+	0x0025F5, 0x0A3144, 0x000007, 0x000820,
+	0x032860, 0x030040, 0x00217D, 0x038042,
+	0x0B804A, 0x10000A, 0x000820, 0x031060,
+	0x030040, 0x00008D, 0x000124, 0x00012C,
+	0x000E64, 0x001A64, 0x00636C, 0x08010A,
+	0x10012A, 0x000820, 0x031060, 0x030040,
+	0x0020FD, 0x018042, 0x08000A, 0x00227D,
+	0x018042, 0x10000A, 0x000820, 0x031060,
+	0x030040, 0x00197D, 0x018042, 0x08000A,
+	0x0022FD, 0x038042, 0x10000A, 0x000820,
+	0x031060, 0x030040, 0x090D04, 0x000007,
+	0x000820, 0x030040, 0x038042, 0x0B804A,
+	0x10000A, 0x000820, 0x031060, 0x030040,
+	0x038042, 0x13804A, 0x19804A, 0x110D04,
+	0x198D04, 0x000007, 0x08000A, 0x001020,
+	0x031860, 0x030860, 0x030040, 0x00008D,
+	0x0B0944, 0x000007, 0x000820, 0x010040,
+	0x0005F5, 0x030042, 0x08000A, 0x000820,
+	0x010040, 0x0000F5, 0x010042, 0x08000A,
+	0x000904, 0x1C6086, 0x001E75, 0x030042,
+	0x01044A, 0x000C0A, 0x1C7206, 0x000007,
+	0x000402, 0x000C02, 0x00177D, 0x001AF5,
+	0x018042, 0x03144A, 0x031C4A, 0x03244A,
+	0x032C4A, 0x03344A, 0x033C4A, 0x03444A,
+	0x004C0A, 0x00043D, 0x0013F5, 0x001AFD,
+	0x030042, 0x0B004A, 0x1B804A, 0x13804A,
+	0x20000A, 0x089144, 0x19A144, 0x0389E4,
+	0x0399EC, 0x005502, 0x005D0A, 0x030042,
+	0x0B004A, 0x1B804A, 0x13804A, 0x20000A,
+	0x089144, 0x19A144, 0x0389E4, 0x0399EC,
+	0x006502, 0x006D0A, 0x030042, 0x0B004A,
+	0x19004A, 0x2B804A, 0x13804A, 0x21804A,
+	0x30000A, 0x089144, 0x19A144, 0x2AB144,
+	0x0389E4, 0x0399EC, 0x007502, 0x007D0A,
+	0x03A9E4, 0x000702, 0x00107D, 0x000415,
+	0x018042, 0x08000A, 0x0109E4, 0x000F02,
+	0x002AF5, 0x0019FD, 0x010042, 0x09804A,
+	0x10000A, 0x000934, 0x001674, 0x0029F5,
+	0x010042, 0x10000A, 0x00917C, 0x002075,
+	0x010042, 0x08000A, 0x000904, 0x1ED286,
+	0x0026F5, 0x0027F5, 0x030042, 0x09004A,
+	0x10000A, 0x000A3C, 0x00167C, 0x001A75,
+	0x000BFD, 0x010042, 0x51804A, 0x48000A,
+	0x160007, 0x001075, 0x010042, 0x282C0A,
+	0x281D12, 0x282512, 0x001F32, 0x1E0007,
+	0x0E0007, 0x001975, 0x010042, 0x002DF5,
+	0x0D004A, 0x10000A, 0x009144, 0x1FB286,
+	0x010042, 0x28340A, 0x000E5D, 0x00008D,
+	0x000375, 0x000820, 0x010040, 0x05D2F4,
+	0x54D104, 0x00735C, 0x205386, 0x000007,
+	0x0C0007, 0x080007, 0x0A0007, 0x02040D,
+	0x000810, 0x08043A, 0x332206, 0x000007,
+	0x205A06, 0x000007, 0x080007, 0x002275,
+	0x010042, 0x20000A, 0x002104, 0x212086,
+	0x001E2D, 0x0002F5, 0x010042, 0x08000A,
+	0x000904, 0x209286, 0x000007, 0x002010,
+	0x30043A, 0x00057D, 0x0180C3, 0x08000A,
+	0x028924, 0x280502, 0x280C02, 0x0A810D,
+	0x000820, 0x0002F5, 0x010040, 0x220007,
+	0x0004FD, 0x018042, 0x70000A, 0x030000,
+	0x007020, 0x06FA06, 0x018040, 0x02180D,
+	0x000810, 0x08043A, 0x2B2206, 0x000007,
+	0x0002FD, 0x018042, 0x08000A, 0x000904,
+	0x218A86, 0x000007, 0x01F206, 0x000007,
+	0x000875, 0x0009FD, 0x00010D, 0x220A06,
+	0x000295, 0x000B75, 0x00097D, 0x00000D,
+	0x000515, 0x010042, 0x18000A, 0x001904,
+	0x287886, 0x0006F5, 0x001020, 0x010040,
+	0x0004F5, 0x000820, 0x010040, 0x000775,
+	0x010042, 0x09804A, 0x10000A, 0x001124,
+	0x000904, 0x22BA86, 0x000815, 0x080102,
+	0x101204, 0x22DA06, 0x000575, 0x081204,
+	0x000007, 0x100102, 0x000575, 0x000425,
+	0x021124, 0x100102, 0x000820, 0x031060,
+	0x010040, 0x001924, 0x287886, 0x00008D,
+	0x000464, 0x009D04, 0x278886, 0x180102,
+	0x000575, 0x010042, 0x28040A, 0x00018D,
+	0x000924, 0x280D02, 0x00000D, 0x000924,
+	0x281502, 0x10000D, 0x000820, 0x0002F5,
+	0x010040, 0x200007, 0x001175, 0x0002FD,
+	0x018042, 0x08000A, 0x000904, 0x23C286,
+	0x000007, 0x000100, 0x080B20, 0x130B60,
+	0x1B0B60, 0x030A60, 0x010040, 0x050042,
+	0x3D004A, 0x35004A, 0x2D004A, 0x20000A,
+	0x0006F5, 0x010042, 0x28140A, 0x0004F5,
+	0x010042, 0x08000A, 0x000315, 0x010D04,
+	0x24CA86, 0x004015, 0x000095, 0x010D04,
+	0x24B886, 0x100022, 0x10002A, 0x24E206,
+	0x000007, 0x333104, 0x2AA904, 0x000007,
+	0x032124, 0x280502, 0x001124, 0x000424,
+	0x000424, 0x003224, 0x00292C, 0x00636C,
+	0x25F386, 0x000007, 0x02B164, 0x000464,
+	0x000464, 0x00008D, 0x000A64, 0x280D02,
+	0x10008D, 0x000820, 0x0002F5, 0x010040,
+	0x220007, 0x00008D, 0x38B904, 0x000007,
+	0x03296C, 0x30010A, 0x0002F5, 0x010042,
+	0x08000A, 0x000904, 0x25BA86, 0x000007,
+	0x02312C, 0x28050A, 0x00008D, 0x01096C,
+	0x280D0A, 0x10010D, 0x000820, 0x0002F5,
+	0x010040, 0x220007, 0x001124, 0x000424,
+	0x000424, 0x003224, 0x300102, 0x032944,
+	0x267A86, 0x000007, 0x300002, 0x0004F5,
+	0x010042, 0x08000A, 0x000315, 0x010D04,
+	0x26C086, 0x003124, 0x000464, 0x300102,
+	0x0002F5, 0x010042, 0x08000A, 0x000904,
+	0x26CA86, 0x000007, 0x003124, 0x300502,
+	0x003924, 0x300583, 0x000883, 0x0005F5,
+	0x010042, 0x28040A, 0x00008D, 0x008124,
+	0x280D02, 0x00008D, 0x008124, 0x281502,
+	0x10018D, 0x000820, 0x0002F5, 0x010040,
+	0x220007, 0x001025, 0x000575, 0x030042,
+	0x09004A, 0x10000A, 0x0A0904, 0x121104,
+	0x000007, 0x001020, 0x050860, 0x050040,
+	0x0006FD, 0x018042, 0x09004A, 0x10000A,
+	0x0000A5, 0x0A0904, 0x121104, 0x000007,
+	0x000820, 0x019060, 0x010040, 0x0002F5,
+	0x010042, 0x08000A, 0x000904, 0x284286,
+	0x000007, 0x230A06, 0x000007, 0x000606,
+	0x000007, 0x0002F5, 0x010042, 0x08000A,
+	0x000904, 0x289286, 0x000007, 0x000100,
+	0x080B20, 0x138B60, 0x1B8B60, 0x238B60,
+	0x2B8B60, 0x338B60, 0x3B8B60, 0x438B60,
+	0x4B8B60, 0x538B60, 0x5B8B60, 0x638B60,
+	0x6B8B60, 0x738B60, 0x7B8B60, 0x038F60,
+	0x0B8F60, 0x138F60, 0x1B8F60, 0x238F60,
+	0x2B8F60, 0x338F60, 0x3B8F60, 0x438F60,
+	0x4B8F60, 0x538F60, 0x5B8F60, 0x638F60,
+	0x6B8F60, 0x738F60, 0x7B8F60, 0x038A60,
+	0x000606, 0x018040, 0x00008D, 0x000A64,
+	0x280D02, 0x000A24, 0x00027D, 0x018042,
+	0x10000A, 0x001224, 0x0003FD, 0x018042,
+	0x08000A, 0x000904, 0x2A8286, 0x000007,
+	0x00018D, 0x000A24, 0x000464, 0x000464,
+	0x080102, 0x000924, 0x000424, 0x000424,
+	0x100102, 0x02000D, 0x009144, 0x2AD986,
+	0x000007, 0x0001FD, 0x018042, 0x08000A,
+	0x000A44, 0x2ABB86, 0x018042, 0x0A000D,
+	0x000820, 0x0002FD, 0x018040, 0x200007,
+	0x00027D, 0x001020, 0x000606, 0x018040,
+	0x0002F5, 0x010042, 0x08000A, 0x000904,
+	0x2B2A86, 0x000007, 0x00037D, 0x018042,
+	0x08000A, 0x000904, 0x2B5A86, 0x000007,
+	0x000075, 0x002E7D, 0x010042, 0x0B804A,
+	0x000020, 0x000904, 0x000686, 0x010040,
+	0x31844A, 0x30048B, 0x000883, 0x00008D,
+	0x000810, 0x28143A, 0x00008D, 0x000810,
+	0x280C3A, 0x000675, 0x010042, 0x08000A,
+	0x003815, 0x010924, 0x280502, 0x0B000D,
+	0x000820, 0x0002F5, 0x010040, 0x000606,
+	0x220007, 0x000464, 0x000464, 0x000606,
+	0x000007, 0x000134, 0x007F8D, 0x00093C,
+	0x281D12, 0x282512, 0x001F32, 0x0E0007,
+	0x00010D, 0x00037D, 0x000820, 0x018040,
+	0x05D2F4, 0x000007, 0x080007, 0x00037D,
+	0x018042, 0x08000A, 0x000904, 0x2D0286,
+	0x000007, 0x000606, 0x000007, 0x000007,
+	0x000012, 0x100007, 0x320007, 0x600007,
+	0x100080, 0x48001A, 0x004904, 0x2D6186,
+	0x000007, 0x001210, 0x58003A, 0x000145,
+	0x5C5D04, 0x000007, 0x000080, 0x48001A,
+	0x004904, 0x2DB186, 0x000007, 0x001210,
+	0x50003A, 0x005904, 0x2E0886, 0x000045,
+	0x0000C5, 0x7FFFF5, 0x7FFF7D, 0x07D524,
+	0x004224, 0x500102, 0x200502, 0x000082,
+	0x40001A, 0x004104, 0x2E3986, 0x000007,
+	0x003865, 0x40001A, 0x004020, 0x00104D,
+	0x04C184, 0x301B86, 0x000040, 0x040007,
+	0x000165, 0x000145, 0x004020, 0x000040,
+	0x000765, 0x080080, 0x40001A, 0x004104,
+	0x2EC986, 0x000007, 0x001210, 0x40003A,
+	0x004104, 0x2F2286, 0x00004D, 0x0000CD,
+	0x004810, 0x20043A, 0x000882, 0x40001A,
+	0x004104, 0x2F3186, 0x000007, 0x004820,
+	0x005904, 0x300886, 0x000040, 0x0007E5,
+	0x200480, 0x2816A0, 0x3216E0, 0x3A16E0,
+	0x4216E0, 0x021260, 0x000040, 0x000032,
+	0x400075, 0x00007D, 0x07D574, 0x200512,
+	0x000082, 0x40001A, 0x004104, 0x2FE186,
+	0x000007, 0x037206, 0x640007, 0x060007,
+	0x0000E5, 0x000020, 0x000040, 0x000A65,
+	0x000020, 0x020040, 0x020040, 0x000040,
+	0x000165, 0x000042, 0x70000A, 0x007104,
+	0x30A286, 0x000007, 0x018206, 0x640007,
+	0x050000, 0x007020, 0x000040, 0x037206,
+	0x640007, 0x000007, 0x00306D, 0x028860,
+	0x029060, 0x08000A, 0x028860, 0x008040,
+	0x100012, 0x00100D, 0x009184, 0x314186,
+	0x000E0D, 0x009184, 0x325186, 0x000007,
+	0x300007, 0x001020, 0x003B6D, 0x008040,
+	0x000080, 0x08001A, 0x000904, 0x316186,
+	0x000007, 0x001220, 0x000DED, 0x008040,
+	0x008042, 0x10000A, 0x40000D, 0x109544,
+	0x000007, 0x001020, 0x000DED, 0x008040,
+	0x008042, 0x20040A, 0x000082, 0x08001A,
+	0x000904, 0x31F186, 0x000007, 0x003B6D,
+	0x008042, 0x08000A, 0x000E15, 0x010984,
+	0x329B86, 0x600007, 0x08001A, 0x000C15,
+	0x010984, 0x328386, 0x000020, 0x1A0007,
+	0x0002ED, 0x008040, 0x620007, 0x00306D,
+	0x028042, 0x0A804A, 0x000820, 0x0A804A,
+	0x000606, 0x10804A, 0x000007, 0x282512,
+	0x001F32, 0x05D2F4, 0x54D104, 0x00735C,
+	0x000786, 0x000007, 0x0C0007, 0x0A0007,
+	0x1C0007, 0x003465, 0x020040, 0x004820,
+	0x025060, 0x40000A, 0x024060, 0x000040,
+	0x454944, 0x000007, 0x004020, 0x003AE5,
+	0x000040, 0x0028E5, 0x000042, 0x48000A,
+	0x004904, 0x386886, 0x002C65, 0x000042,
+	0x40000A, 0x0000D5, 0x454104, 0x000007,
+	0x000655, 0x054504, 0x34F286, 0x0001D5,
+	0x054504, 0x34F086, 0x002B65, 0x000042,
+	0x003AE5, 0x50004A, 0x40000A, 0x45C3D4,
+	0x000007, 0x454504, 0x000007, 0x0000CD,
+	0x444944, 0x000007, 0x454504, 0x000007,
+	0x00014D, 0x554944, 0x000007, 0x045144,
+	0x34E986, 0x002C65, 0x000042, 0x48000A,
+	0x4CD104, 0x000007, 0x04C144, 0x34F386,
+	0x000007, 0x160007, 0x002CE5, 0x040042,
+	0x40000A, 0x004020, 0x000040, 0x002965,
+	0x000042, 0x40000A, 0x004104, 0x356086,
+	0x000007, 0x002402, 0x36A206, 0x005C02,
+	0x0025E5, 0x000042, 0x40000A, 0x004274,
+	0x002AE5, 0x000042, 0x40000A, 0x004274,
+	0x500112, 0x0029E5, 0x000042, 0x40000A,
+	0x004234, 0x454104, 0x000007, 0x004020,
+	0x000040, 0x003EE5, 0x000020, 0x000040,
+	0x002DE5, 0x400152, 0x50000A, 0x045144,
+	0x364A86, 0x0000C5, 0x003EE5, 0x004020,
+	0x000040, 0x002BE5, 0x000042, 0x40000A,
+	0x404254, 0x000007, 0x002AE5, 0x004020,
+	0x000040, 0x500132, 0x040134, 0x005674,
+	0x0029E5, 0x020042, 0x42000A, 0x000042,
+	0x50000A, 0x05417C, 0x0028E5, 0x000042,
+	0x48000A, 0x0000C5, 0x4CC144, 0x371086,
+	0x0026E5, 0x0027E5, 0x020042, 0x40004A,
+	0x50000A, 0x00423C, 0x00567C, 0x0028E5,
+	0x004820, 0x000040, 0x281D12, 0x282512,
+	0x001F72, 0x002965, 0x000042, 0x40000A,
+	0x004104, 0x37AA86, 0x0E0007, 0x160007,
+	0x1E0007, 0x003EE5, 0x000042, 0x40000A,
+	0x004104, 0x37E886, 0x002D65, 0x000042,
+	0x28340A, 0x003465, 0x020042, 0x42004A,
+	0x004020, 0x4A004A, 0x50004A, 0x05D2F4,
+	0x54D104, 0x00735C, 0x385186, 0x000007,
+	0x000606, 0x080007, 0x0C0007, 0x080007,
+	0x0A0007, 0x0001E5, 0x020045, 0x004020,
+	0x000060, 0x000365, 0x000040, 0x002E65,
+	0x001A20, 0x0A1A60, 0x000040, 0x003465,
+	0x020042, 0x42004A, 0x004020, 0x4A004A,
+	0x000606, 0x50004A, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000
+};
+
+// --------------------------------------------
+//  DS-1E Controller InstructionRAM Code
+//	1999/06/21
+//	Buf441 slot is Enabled.
+// --------------------------------------------
+// 04/09  creat
+// 04/12  stop nise fix
+// 06/21  WorkingOff timming
+static unsigned long CntrlInst1E[YDSXG_CTRLLENGTH / 4] = {
+	0x000007, 0x240007, 0x0C0007, 0x1C0007,
+	0x060007, 0x700002, 0x000020, 0x030040,
+	0x007104, 0x004286, 0x030040, 0x000F0D,
+	0x000810, 0x20043A, 0x000282, 0x00020D,
+	0x000810, 0x20043A, 0x001282, 0x200E82,
+	0x00800D, 0x000810, 0x20043A, 0x001A82,
+	0x03460D, 0x000810, 0x10043A, 0x02EC0D,
+	0x000810, 0x18043A, 0x00010D, 0x020015,
+	0x0000FD, 0x000020, 0x038860, 0x039060,
+	0x038060, 0x038040, 0x038040, 0x038040,
+	0x018040, 0x000A7D, 0x038040, 0x038040,
+	0x018040, 0x200402, 0x000882, 0x08001A,
+	0x000904, 0x017186, 0x000007, 0x260007,
+	0x400007, 0x000007, 0x03258D, 0x000810,
+	0x18043A, 0x260007, 0x284402, 0x00087D,
+	0x018042, 0x00160A, 0x05A206, 0x000007,
+	0x440007, 0x00230D, 0x000810, 0x08043A,
+	0x22FA06, 0x000007, 0x0007FD, 0x018042,
+	0x08000A, 0x000904, 0x02AB86, 0x000195,
+	0x090D04, 0x000007, 0x000820, 0x0000F5,
+	0x000B7D, 0x01F060, 0x0000FD, 0x033A06,
+	0x018040, 0x000A7D, 0x038042, 0x13804A,
+	0x18000A, 0x001820, 0x059060, 0x058860,
+	0x018040, 0x0000FD, 0x018042, 0x70000A,
+	0x000115, 0x071144, 0x033B86, 0x030000,
+	0x007020, 0x036206, 0x018040, 0x00360D,
+	0x000810, 0x08043A, 0x232206, 0x000007,
+	0x02EC0D, 0x000810, 0x18043A, 0x019A06,
+	0x000007, 0x240007, 0x000F8D, 0x000810,
+	0x00163A, 0x002402, 0x005C02, 0x0028FD,
+	0x000020, 0x018040, 0x08000D, 0x000815,
+	0x510984, 0x000007, 0x00004D, 0x000E5D,
+	0x000E02, 0x00430D, 0x000810, 0x08043A,
+	0x2E1206, 0x000007, 0x00008D, 0x000924,
+	0x000F02, 0x00470D, 0x000810, 0x08043A,
+	0x2E1206, 0x000007, 0x480480, 0x001210,
+	0x28043A, 0x00778D, 0x000810, 0x280C3A,
+	0x00068D, 0x000810, 0x28143A, 0x284402,
+	0x03258D, 0x000810, 0x18043A, 0x07FF8D,
+	0x000820, 0x0002FD, 0x018040, 0x260007,
+	0x200007, 0x0002FD, 0x018042, 0x08000A,
+	0x000904, 0x051286, 0x000007, 0x240007,
+	0x02EC0D, 0x000810, 0x18043A, 0x00387D,
+	0x018042, 0x08000A, 0x001015, 0x010984,
+	0x019B86, 0x000007, 0x01B206, 0x000007,
+	0x0008FD, 0x018042, 0x18000A, 0x001904,
+	0x22B886, 0x280007, 0x001810, 0x28043A,
+	0x280C02, 0x00000D, 0x000810, 0x28143A,
+	0x08808D, 0x000820, 0x0002FD, 0x018040,
+	0x200007, 0x00020D, 0x189904, 0x000007,
+	0x00402D, 0x0000BD, 0x0002FD, 0x018042,
+	0x08000A, 0x000904, 0x065A86, 0x000007,
+	0x000100, 0x000A20, 0x00047D, 0x018040,
+	0x018042, 0x20000A, 0x003015, 0x012144,
+	0x036186, 0x000007, 0x002104, 0x036186,
+	0x000007, 0x000F8D, 0x000810, 0x280C3A,
+	0x023944, 0x07C986, 0x000007, 0x001810,
+	0x28043A, 0x08810D, 0x000820, 0x0002FD,
+	0x018040, 0x200007, 0x002810, 0x78003A,
+	0x00788D, 0x000810, 0x08043A, 0x2A1206,
+	0x000007, 0x00400D, 0x001015, 0x189904,
+	0x292904, 0x393904, 0x000007, 0x070206,
+	0x000007, 0x0004F5, 0x00007D, 0x000020,
+	0x00008D, 0x010860, 0x018040, 0x00047D,
+	0x038042, 0x21804A, 0x18000A, 0x021944,
+	0x229086, 0x000007, 0x004075, 0x71F104,
+	0x000007, 0x010042, 0x28000A, 0x002904,
+	0x225886, 0x000007, 0x003C0D, 0x30A904,
+	0x000007, 0x00077D, 0x018042, 0x08000A,
+	0x000904, 0x08DA86, 0x00057D, 0x002820,
+	0x03B060, 0x08F206, 0x018040, 0x003020,
+	0x03A860, 0x018040, 0x0002FD, 0x018042,
+	0x08000A, 0x000904, 0x08FA86, 0x000007,
+	0x00057D, 0x018042, 0x28040A, 0x000E8D,
+	0x000810, 0x280C3A, 0x00000D, 0x000810,
+	0x28143A, 0x09000D, 0x000820, 0x0002FD,
+	0x018040, 0x200007, 0x003DFD, 0x000020,
+	0x018040, 0x00107D, 0x009D8D, 0x000810,
+	0x08043A, 0x2A1206, 0x000007, 0x000815,
+	0x08001A, 0x010984, 0x0A5186, 0x00137D,
+	0x200500, 0x280F20, 0x338F60, 0x3B8F60,
+	0x438F60, 0x4B8F60, 0x538F60, 0x5B8F60,
+	0x038A60, 0x018040, 0x00107D, 0x018042,
+	0x08000A, 0x000215, 0x010984, 0x3A8186,
+	0x000007, 0x007FBD, 0x383DC4, 0x000007,
+	0x001A7D, 0x001375, 0x018042, 0x09004A,
+	0x10000A, 0x0B8D04, 0x139504, 0x000007,
+	0x000820, 0x019060, 0x001104, 0x225886,
+	0x010040, 0x0017FD, 0x018042, 0x08000A,
+	0x000904, 0x225A86, 0x000007, 0x00197D,
+	0x038042, 0x09804A, 0x10000A, 0x000924,
+	0x001664, 0x0011FD, 0x038042, 0x2B804A,
+	0x19804A, 0x00008D, 0x218944, 0x000007,
+	0x002244, 0x0C1986, 0x000007, 0x001A64,
+	0x002A24, 0x00197D, 0x080102, 0x100122,
+	0x000820, 0x039060, 0x018040, 0x003DFD,
+	0x00008D, 0x000820, 0x018040, 0x001375,
+	0x001A7D, 0x010042, 0x09804A, 0x10000A,
+	0x00021D, 0x0189E4, 0x2992E4, 0x309144,
+	0x000007, 0x00060D, 0x000A15, 0x000C1D,
+	0x001025, 0x00A9E4, 0x012BE4, 0x000464,
+	0x01B3E4, 0x0232E4, 0x000464, 0x000464,
+	0x000464, 0x000464, 0x00040D, 0x08B1C4,
+	0x000007, 0x000820, 0x000BF5, 0x030040,
+	0x00197D, 0x038042, 0x09804A, 0x000A24,
+	0x08000A, 0x080E64, 0x000007, 0x100122,
+	0x000820, 0x031060, 0x010040, 0x0064AC,
+	0x00027D, 0x000020, 0x018040, 0x00107D,
+	0x018042, 0x0011FD, 0x3B804A, 0x09804A,
+	0x20000A, 0x000095, 0x1A1144, 0x00A144,
+	0x0E5886, 0x00040D, 0x00B984, 0x0E5986,
+	0x0018FD, 0x018042, 0x0010FD, 0x09804A,
+	0x28000A, 0x000095, 0x010924, 0x002A64,
+	0x0E4986, 0x000007, 0x002904, 0x0E5A86,
+	0x000007, 0x0E6206, 0x080002, 0x00008D,
+	0x00387D, 0x000820, 0x018040, 0x00127D,
+	0x018042, 0x10000A, 0x003904, 0x0F0986,
+	0x00080D, 0x7FFFB5, 0x00B984, 0x0ED986,
+	0x000025, 0x0FB206, 0x00002D, 0x000015,
+	0x00082D, 0x02E00D, 0x000820, 0x0FFA06,
+	0x00000D, 0x7F8035, 0x00B984, 0x0FA986,
+	0x400025, 0x00008D, 0x110944, 0x000007,
+	0x00018D, 0x109504, 0x000007, 0x009164,
+	0x000424, 0x000424, 0x000424, 0x100102,
+	0x280002, 0x02DF0D, 0x000820, 0x0FFA06,
+	0x00018D, 0x00042D, 0x00008D, 0x109504,
+	0x000007, 0x00020D, 0x109184, 0x000007,
+	0x02DF8D, 0x000820, 0x00008D, 0x0038FD,
+	0x018040, 0x003BFD, 0x001020, 0x03A860,
+	0x000815, 0x313184, 0x212184, 0x000007,
+	0x03B060, 0x03A060, 0x018040, 0x0022FD,
+	0x000095, 0x010924, 0x000424, 0x000424,
+	0x001264, 0x100102, 0x000820, 0x039060,
+	0x018040, 0x001924, 0x010F0D, 0x00397D,
+	0x000820, 0x058040, 0x038042, 0x09844A,
+	0x000606, 0x08040A, 0x000424, 0x000424,
+	0x00117D, 0x018042, 0x08000A, 0x000A24,
+	0x280502, 0x280C02, 0x09800D, 0x000820,
+	0x0002FD, 0x018040, 0x200007, 0x0022FD,
+	0x018042, 0x08000A, 0x000095, 0x280DC4,
+	0x011924, 0x00197D, 0x018042, 0x0011FD,
+	0x09804A, 0x10000A, 0x0000B5, 0x113144,
+	0x0A8D04, 0x000007, 0x080A44, 0x129504,
+	0x000007, 0x0023FD, 0x001020, 0x038040,
+	0x101244, 0x000007, 0x000820, 0x039060,
+	0x018040, 0x0002FD, 0x018042, 0x08000A,
+	0x000904, 0x123286, 0x000007, 0x003BFD,
+	0x000100, 0x000A10, 0x0B807A, 0x13804A,
+	0x090984, 0x000007, 0x000095, 0x013D04,
+	0x12B886, 0x10000A, 0x100002, 0x090984,
+	0x000007, 0x038042, 0x11804A, 0x090D04,
+	0x000007, 0x10000A, 0x090D84, 0x000007,
+	0x00257D, 0x000820, 0x018040, 0x00010D,
+	0x000810, 0x28143A, 0x00127D, 0x018042,
+	0x20000A, 0x00197D, 0x018042, 0x00117D,
+	0x31804A, 0x10000A, 0x003124, 0x013B8D,
+	0x00397D, 0x000820, 0x058040, 0x038042,
+	0x09844A, 0x000606, 0x08040A, 0x300102,
+	0x003124, 0x000424, 0x000424, 0x001224,
+	0x280502, 0x001A4C, 0x143986, 0x700002,
+	0x00002D, 0x030000, 0x00387D, 0x018042,
+	0x10000A, 0x146206, 0x002124, 0x0000AD,
+	0x100002, 0x00010D, 0x000924, 0x006B24,
+	0x014A0D, 0x00397D, 0x000820, 0x058040,
+	0x038042, 0x09844A, 0x000606, 0x08040A,
+	0x003264, 0x00008D, 0x000A24, 0x001020,
+	0x00227D, 0x018040, 0x014F8D, 0x000810,
+	0x08043A, 0x2B5A06, 0x000007, 0x002820,
+	0x00207D, 0x018040, 0x00117D, 0x038042,
+	0x13804A, 0x33800A, 0x00387D, 0x018042,
+	0x08000A, 0x000904, 0x177286, 0x000007,
+	0x00008D, 0x030964, 0x015B0D, 0x00397D,
+	0x000820, 0x058040, 0x038042, 0x09844A,
+	0x000606, 0x08040A, 0x380102, 0x000424,
+	0x000424, 0x001224, 0x0002FD, 0x018042,
+	0x08000A, 0x000904, 0x15DA86, 0x000007,
+	0x280502, 0x001A4C, 0x177186, 0x000007,
+	0x032164, 0x00632C, 0x003DFD, 0x018042,
+	0x08000A, 0x000095, 0x090904, 0x000007,
+	0x000820, 0x001A4C, 0x169986, 0x018040,
+	0x030000, 0x16B206, 0x002124, 0x00010D,
+	0x000924, 0x006B24, 0x016F0D, 0x00397D,
+	0x000820, 0x058040, 0x038042, 0x09844A,
+	0x000606, 0x08040A, 0x003A64, 0x000095,
+	0x001224, 0x0002FD, 0x018042, 0x08000A,
+	0x000904, 0x171286, 0x000007, 0x01760D,
+	0x000810, 0x08043A, 0x2B5A06, 0x000007,
+	0x160A06, 0x000007, 0x007020, 0x08010A,
+	0x10012A, 0x0020FD, 0x038860, 0x039060,
+	0x018040, 0x00227D, 0x018042, 0x003DFD,
+	0x08000A, 0x31844A, 0x000904, 0x181086,
+	0x18008B, 0x00008D, 0x189904, 0x00312C,
+	0x18E206, 0x000007, 0x00324C, 0x186B86,
+	0x000007, 0x001904, 0x186886, 0x000007,
+	0x000095, 0x199144, 0x00222C, 0x003124,
+	0x00636C, 0x000E3D, 0x001375, 0x000BFD,
+	0x010042, 0x09804A, 0x10000A, 0x038AEC,
+	0x0393EC, 0x00224C, 0x18E186, 0x000007,
+	0x00008D, 0x189904, 0x00226C, 0x00322C,
+	0x30050A, 0x301DAB, 0x002083, 0x0018FD,
+	0x018042, 0x08000A, 0x018924, 0x300502,
+	0x001083, 0x001875, 0x010042, 0x10000A,
+	0x00008D, 0x010924, 0x001375, 0x330542,
+	0x330CCB, 0x332CCB, 0x3334CB, 0x333CCB,
+	0x3344CB, 0x334CCB, 0x3354CB, 0x305C8B,
+	0x006083, 0x0002F5, 0x010042, 0x08000A,
+	0x000904, 0x19B286, 0x000007, 0x001E2D,
+	0x0005FD, 0x018042, 0x08000A, 0x028924,
+	0x280502, 0x00060D, 0x000810, 0x280C3A,
+	0x00008D, 0x000810, 0x28143A, 0x0A808D,
+	0x000820, 0x0002F5, 0x010040, 0x220007,
+	0x001275, 0x030042, 0x21004A, 0x00008D,
+	0x1A0944, 0x000007, 0x01AB8D, 0x000810,
+	0x08043A, 0x2CAA06, 0x000007, 0x0001F5,
+	0x030042, 0x0D004A, 0x10000A, 0x089144,
+	0x000007, 0x000820, 0x010040, 0x0025F5,
+	0x0A3144, 0x000007, 0x000820, 0x032860,
+	0x030040, 0x00217D, 0x038042, 0x0B804A,
+	0x10000A, 0x000820, 0x031060, 0x030040,
+	0x00008D, 0x000124, 0x00012C, 0x000E64,
+	0x001A64, 0x00636C, 0x08010A, 0x10012A,
+	0x000820, 0x031060, 0x030040, 0x0020FD,
+	0x018042, 0x08000A, 0x00227D, 0x018042,
+	0x10000A, 0x000820, 0x031060, 0x030040,
+	0x00197D, 0x018042, 0x08000A, 0x0022FD,
+	0x038042, 0x10000A, 0x000820, 0x031060,
+	0x030040, 0x090D04, 0x000007, 0x000820,
+	0x030040, 0x038042, 0x0B804A, 0x10000A,
+	0x000820, 0x031060, 0x030040, 0x038042,
+	0x13804A, 0x19804A, 0x110D04, 0x198D04,
+	0x000007, 0x08000A, 0x001020, 0x031860,
+	0x030860, 0x030040, 0x00008D, 0x0B0944,
+	0x000007, 0x000820, 0x010040, 0x0005F5,
+	0x030042, 0x08000A, 0x000820, 0x010040,
+	0x0000F5, 0x010042, 0x08000A, 0x000904,
+	0x1D9886, 0x001E75, 0x030042, 0x01044A,
+	0x000C0A, 0x1DAA06, 0x000007, 0x000402,
+	0x000C02, 0x00177D, 0x001AF5, 0x018042,
+	0x03144A, 0x031C4A, 0x03244A, 0x032C4A,
+	0x03344A, 0x033C4A, 0x03444A, 0x004C0A,
+	0x00043D, 0x0013F5, 0x001AFD, 0x030042,
+	0x0B004A, 0x1B804A, 0x13804A, 0x20000A,
+	0x089144, 0x19A144, 0x0389E4, 0x0399EC,
+	0x005502, 0x005D0A, 0x030042, 0x0B004A,
+	0x1B804A, 0x13804A, 0x20000A, 0x089144,
+	0x19A144, 0x0389E4, 0x0399EC, 0x006502,
+	0x006D0A, 0x030042, 0x0B004A, 0x19004A,
+	0x2B804A, 0x13804A, 0x21804A, 0x30000A,
+	0x089144, 0x19A144, 0x2AB144, 0x0389E4,
+	0x0399EC, 0x007502, 0x007D0A, 0x03A9E4,
+	0x000702, 0x00107D, 0x000415, 0x018042,
+	0x08000A, 0x0109E4, 0x000F02, 0x002AF5,
+	0x0019FD, 0x010042, 0x09804A, 0x10000A,
+	0x000934, 0x001674, 0x0029F5, 0x010042,
+	0x10000A, 0x00917C, 0x002075, 0x010042,
+	0x08000A, 0x000904, 0x200A86, 0x0026F5,
+	0x0027F5, 0x030042, 0x09004A, 0x10000A,
+	0x000A3C, 0x00167C, 0x001A75, 0x000BFD,
+	0x010042, 0x51804A, 0x48000A, 0x160007,
+	0x001075, 0x010042, 0x282C0A, 0x281D12,
+	0x282512, 0x001F32, 0x1E0007, 0x0E0007,
+	0x001975, 0x010042, 0x002DF5, 0x0D004A,
+	0x10000A, 0x009144, 0x20EA86, 0x010042,
+	0x28340A, 0x000E5D, 0x00008D, 0x000375,
+	0x000820, 0x010040, 0x05D2F4, 0x54D104,
+	0x00735C, 0x218B86, 0x000007, 0x0C0007,
+	0x080007, 0x0A0007, 0x02178D, 0x000810,
+	0x08043A, 0x34B206, 0x000007, 0x219206,
+	0x000007, 0x080007, 0x002275, 0x010042,
+	0x20000A, 0x002104, 0x225886, 0x001E2D,
+	0x0002F5, 0x010042, 0x08000A, 0x000904,
+	0x21CA86, 0x000007, 0x002010, 0x30043A,
+	0x00057D, 0x0180C3, 0x08000A, 0x028924,
+	0x280502, 0x280C02, 0x0A810D, 0x000820,
+	0x0002F5, 0x010040, 0x220007, 0x0004FD,
+	0x018042, 0x70000A, 0x030000, 0x007020,
+	0x07FA06, 0x018040, 0x022B8D, 0x000810,
+	0x08043A, 0x2CAA06, 0x000007, 0x0002FD,
+	0x018042, 0x08000A, 0x000904, 0x22C286,
+	0x000007, 0x020206, 0x000007, 0x000875,
+	0x0009FD, 0x00010D, 0x234206, 0x000295,
+	0x000B75, 0x00097D, 0x00000D, 0x000515,
+	0x010042, 0x18000A, 0x001904, 0x2A0086,
+	0x0006F5, 0x001020, 0x010040, 0x0004F5,
+	0x000820, 0x010040, 0x000775, 0x010042,
+	0x09804A, 0x10000A, 0x001124, 0x000904,
+	0x23F286, 0x000815, 0x080102, 0x101204,
+	0x241206, 0x000575, 0x081204, 0x000007,
+	0x100102, 0x000575, 0x000425, 0x021124,
+	0x100102, 0x000820, 0x031060, 0x010040,
+	0x001924, 0x2A0086, 0x00008D, 0x000464,
+	0x009D04, 0x291086, 0x180102, 0x000575,
+	0x010042, 0x28040A, 0x00018D, 0x000924,
+	0x280D02, 0x00000D, 0x000924, 0x281502,
+	0x10000D, 0x000820, 0x0002F5, 0x010040,
+	0x200007, 0x001175, 0x0002FD, 0x018042,
+	0x08000A, 0x000904, 0x24FA86, 0x000007,
+	0x000100, 0x080B20, 0x130B60, 0x1B0B60,
+	0x030A60, 0x010040, 0x050042, 0x3D004A,
+	0x35004A, 0x2D004A, 0x20000A, 0x0006F5,
+	0x010042, 0x28140A, 0x0004F5, 0x010042,
+	0x08000A, 0x000315, 0x010D04, 0x260286,
+	0x004015, 0x000095, 0x010D04, 0x25F086,
+	0x100022, 0x10002A, 0x261A06, 0x000007,
+	0x333104, 0x2AA904, 0x000007, 0x032124,
+	0x280502, 0x284402, 0x001124, 0x400102,
+	0x000424, 0x000424, 0x003224, 0x00292C,
+	0x00636C, 0x277386, 0x000007, 0x02B164,
+	0x000464, 0x000464, 0x00008D, 0x000A64,
+	0x280D02, 0x10008D, 0x000820, 0x0002F5,
+	0x010040, 0x220007, 0x00008D, 0x38B904,
+	0x000007, 0x03296C, 0x30010A, 0x0002F5,
+	0x010042, 0x08000A, 0x000904, 0x270286,
+	0x000007, 0x00212C, 0x28050A, 0x00316C,
+	0x00046C, 0x00046C, 0x28450A, 0x001124,
+	0x006B64, 0x100102, 0x00008D, 0x01096C,
+	0x280D0A, 0x10010D, 0x000820, 0x0002F5,
+	0x010040, 0x220007, 0x004124, 0x000424,
+	0x000424, 0x003224, 0x300102, 0x032944,
+	0x27FA86, 0x000007, 0x300002, 0x0004F5,
+	0x010042, 0x08000A, 0x000315, 0x010D04,
+	0x284086, 0x003124, 0x000464, 0x300102,
+	0x0002F5, 0x010042, 0x08000A, 0x000904,
+	0x284A86, 0x000007, 0x284402, 0x003124,
+	0x300502, 0x003924, 0x300583, 0x000883,
+	0x0005F5, 0x010042, 0x28040A, 0x00008D,
+	0x008124, 0x280D02, 0x00008D, 0x008124,
+	0x281502, 0x10018D, 0x000820, 0x0002F5,
+	0x010040, 0x220007, 0x001025, 0x000575,
+	0x030042, 0x09004A, 0x10000A, 0x0A0904,
+	0x121104, 0x000007, 0x001020, 0x050860,
+	0x050040, 0x0006FD, 0x018042, 0x09004A,
+	0x10000A, 0x0000A5, 0x0A0904, 0x121104,
+	0x000007, 0x000820, 0x019060, 0x010040,
+	0x0002F5, 0x010042, 0x08000A, 0x000904,
+	0x29CA86, 0x000007, 0x244206, 0x000007,
+	0x000606, 0x000007, 0x0002F5, 0x010042,
+	0x08000A, 0x000904, 0x2A1A86, 0x000007,
+	0x000100, 0x080B20, 0x138B60, 0x1B8B60,
+	0x238B60, 0x2B8B60, 0x338B60, 0x3B8B60,
+	0x438B60, 0x4B8B60, 0x538B60, 0x5B8B60,
+	0x638B60, 0x6B8B60, 0x738B60, 0x7B8B60,
+	0x038F60, 0x0B8F60, 0x138F60, 0x1B8F60,
+	0x238F60, 0x2B8F60, 0x338F60, 0x3B8F60,
+	0x438F60, 0x4B8F60, 0x538F60, 0x5B8F60,
+	0x638F60, 0x6B8F60, 0x738F60, 0x7B8F60,
+	0x038A60, 0x000606, 0x018040, 0x00008D,
+	0x000A64, 0x280D02, 0x000A24, 0x00027D,
+	0x018042, 0x10000A, 0x001224, 0x0003FD,
+	0x018042, 0x08000A, 0x000904, 0x2C0A86,
+	0x000007, 0x00018D, 0x000A24, 0x000464,
+	0x000464, 0x080102, 0x000924, 0x000424,
+	0x000424, 0x100102, 0x02000D, 0x009144,
+	0x2C6186, 0x000007, 0x0001FD, 0x018042,
+	0x08000A, 0x000A44, 0x2C4386, 0x018042,
+	0x0A000D, 0x000820, 0x0002FD, 0x018040,
+	0x200007, 0x00027D, 0x001020, 0x000606,
+	0x018040, 0x0002F5, 0x010042, 0x08000A,
+	0x000904, 0x2CB286, 0x000007, 0x00037D,
+	0x018042, 0x08000A, 0x000904, 0x2CE286,
+	0x000007, 0x000075, 0x002E7D, 0x010042,
+	0x0B804A, 0x000020, 0x000904, 0x000686,
+	0x010040, 0x31844A, 0x30048B, 0x000883,
+	0x00008D, 0x000810, 0x28143A, 0x00008D,
+	0x000810, 0x280C3A, 0x000675, 0x010042,
+	0x08000A, 0x003815, 0x010924, 0x280502,
+	0x0B000D, 0x000820, 0x0002F5, 0x010040,
+	0x000606, 0x220007, 0x000464, 0x000464,
+	0x000606, 0x000007, 0x000134, 0x007F8D,
+	0x00093C, 0x281D12, 0x282512, 0x001F32,
+	0x0E0007, 0x00010D, 0x00037D, 0x000820,
+	0x018040, 0x05D2F4, 0x000007, 0x080007,
+	0x00037D, 0x018042, 0x08000A, 0x000904,
+	0x2E8A86, 0x000007, 0x000606, 0x000007,
+	0x000007, 0x000012, 0x100007, 0x320007,
+	0x600007, 0x460007, 0x100080, 0x48001A,
+	0x004904, 0x2EF186, 0x000007, 0x001210,
+	0x58003A, 0x000145, 0x5C5D04, 0x000007,
+	0x000080, 0x48001A, 0x004904, 0x2F4186,
+	0x000007, 0x001210, 0x50003A, 0x005904,
+	0x2F9886, 0x000045, 0x0000C5, 0x7FFFF5,
+	0x7FFF7D, 0x07D524, 0x004224, 0x500102,
+	0x200502, 0x000082, 0x40001A, 0x004104,
+	0x2FC986, 0x000007, 0x003865, 0x40001A,
+	0x004020, 0x00104D, 0x04C184, 0x31AB86,
+	0x000040, 0x040007, 0x000165, 0x000145,
+	0x004020, 0x000040, 0x000765, 0x080080,
+	0x40001A, 0x004104, 0x305986, 0x000007,
+	0x001210, 0x40003A, 0x004104, 0x30B286,
+	0x00004D, 0x0000CD, 0x004810, 0x20043A,
+	0x000882, 0x40001A, 0x004104, 0x30C186,
+	0x000007, 0x004820, 0x005904, 0x319886,
+	0x000040, 0x0007E5, 0x200480, 0x2816A0,
+	0x3216E0, 0x3A16E0, 0x4216E0, 0x021260,
+	0x000040, 0x000032, 0x400075, 0x00007D,
+	0x07D574, 0x200512, 0x000082, 0x40001A,
+	0x004104, 0x317186, 0x000007, 0x038A06,
+	0x640007, 0x0000E5, 0x000020, 0x000040,
+	0x000A65, 0x000020, 0x020040, 0x020040,
+	0x000040, 0x000165, 0x000042, 0x70000A,
+	0x007104, 0x323286, 0x000007, 0x060007,
+	0x019A06, 0x640007, 0x050000, 0x007020,
+	0x000040, 0x038A06, 0x640007, 0x000007,
+	0x00306D, 0x028860, 0x029060, 0x08000A,
+	0x028860, 0x008040, 0x100012, 0x00100D,
+	0x009184, 0x32D186, 0x000E0D, 0x009184,
+	0x33E186, 0x000007, 0x300007, 0x001020,
+	0x003B6D, 0x008040, 0x000080, 0x08001A,
+	0x000904, 0x32F186, 0x000007, 0x001220,
+	0x000DED, 0x008040, 0x008042, 0x10000A,
+	0x40000D, 0x109544, 0x000007, 0x001020,
+	0x000DED, 0x008040, 0x008042, 0x20040A,
+	0x000082, 0x08001A, 0x000904, 0x338186,
+	0x000007, 0x003B6D, 0x008042, 0x08000A,
+	0x000E15, 0x010984, 0x342B86, 0x600007,
+	0x08001A, 0x000C15, 0x010984, 0x341386,
+	0x000020, 0x1A0007, 0x0002ED, 0x008040,
+	0x620007, 0x00306D, 0x028042, 0x0A804A,
+	0x000820, 0x0A804A, 0x000606, 0x10804A,
+	0x000007, 0x282512, 0x001F32, 0x05D2F4,
+	0x54D104, 0x00735C, 0x000786, 0x000007,
+	0x0C0007, 0x0A0007, 0x1C0007, 0x003465,
+	0x020040, 0x004820, 0x025060, 0x40000A,
+	0x024060, 0x000040, 0x454944, 0x000007,
+	0x004020, 0x003AE5, 0x000040, 0x0028E5,
+	0x000042, 0x48000A, 0x004904, 0x39F886,
+	0x002C65, 0x000042, 0x40000A, 0x0000D5,
+	0x454104, 0x000007, 0x000655, 0x054504,
+	0x368286, 0x0001D5, 0x054504, 0x368086,
+	0x002B65, 0x000042, 0x003AE5, 0x50004A,
+	0x40000A, 0x45C3D4, 0x000007, 0x454504,
+	0x000007, 0x0000CD, 0x444944, 0x000007,
+	0x454504, 0x000007, 0x00014D, 0x554944,
+	0x000007, 0x045144, 0x367986, 0x002C65,
+	0x000042, 0x48000A, 0x4CD104, 0x000007,
+	0x04C144, 0x368386, 0x000007, 0x160007,
+	0x002CE5, 0x040042, 0x40000A, 0x004020,
+	0x000040, 0x002965, 0x000042, 0x40000A,
+	0x004104, 0x36F086, 0x000007, 0x002402,
+	0x383206, 0x005C02, 0x0025E5, 0x000042,
+	0x40000A, 0x004274, 0x002AE5, 0x000042,
+	0x40000A, 0x004274, 0x500112, 0x0029E5,
+	0x000042, 0x40000A, 0x004234, 0x454104,
+	0x000007, 0x004020, 0x000040, 0x003EE5,
+	0x000020, 0x000040, 0x002DE5, 0x400152,
+	0x50000A, 0x045144, 0x37DA86, 0x0000C5,
+	0x003EE5, 0x004020, 0x000040, 0x002BE5,
+	0x000042, 0x40000A, 0x404254, 0x000007,
+	0x002AE5, 0x004020, 0x000040, 0x500132,
+	0x040134, 0x005674, 0x0029E5, 0x020042,
+	0x42000A, 0x000042, 0x50000A, 0x05417C,
+	0x0028E5, 0x000042, 0x48000A, 0x0000C5,
+	0x4CC144, 0x38A086, 0x0026E5, 0x0027E5,
+	0x020042, 0x40004A, 0x50000A, 0x00423C,
+	0x00567C, 0x0028E5, 0x004820, 0x000040,
+	0x281D12, 0x282512, 0x001F72, 0x002965,
+	0x000042, 0x40000A, 0x004104, 0x393A86,
+	0x0E0007, 0x160007, 0x1E0007, 0x003EE5,
+	0x000042, 0x40000A, 0x004104, 0x397886,
+	0x002D65, 0x000042, 0x28340A, 0x003465,
+	0x020042, 0x42004A, 0x004020, 0x4A004A,
+	0x50004A, 0x05D2F4, 0x54D104, 0x00735C,
+	0x39E186, 0x000007, 0x000606, 0x080007,
+	0x0C0007, 0x080007, 0x0A0007, 0x0001E5,
+	0x020045, 0x004020, 0x000060, 0x000365,
+	0x000040, 0x002E65, 0x001A20, 0x0A1A60,
+	0x000040, 0x003465, 0x020042, 0x42004A,
+	0x004020, 0x4A004A, 0x000606, 0x50004A,
+	0x0017FD, 0x018042, 0x08000A, 0x000904,
+	0x225A86, 0x000007, 0x00107D, 0x018042,
+	0x0011FD, 0x33804A, 0x19804A, 0x20000A,
+	0x000095, 0x2A1144, 0x01A144, 0x3B9086,
+	0x00040D, 0x00B184, 0x3B9186, 0x0018FD,
+	0x018042, 0x0010FD, 0x09804A, 0x38000A,
+	0x000095, 0x010924, 0x003A64, 0x3B8186,
+	0x000007, 0x003904, 0x3B9286, 0x000007,
+	0x3B9A06, 0x00000D, 0x00008D, 0x000820,
+	0x00387D, 0x018040, 0x700002, 0x00117D,
+	0x018042, 0x00197D, 0x29804A, 0x30000A,
+	0x380002, 0x003124, 0x000424, 0x000424,
+	0x002A24, 0x280502, 0x00068D, 0x000810,
+	0x28143A, 0x00750D, 0x00B124, 0x002264,
+	0x3D0386, 0x284402, 0x000810, 0x280C3A,
+	0x0B800D, 0x000820, 0x0002FD, 0x018040,
+	0x200007, 0x00758D, 0x00B124, 0x100102,
+	0x012144, 0x3E4986, 0x001810, 0x10003A,
+	0x00387D, 0x018042, 0x08000A, 0x000904,
+	0x3E4886, 0x030000, 0x3E4A06, 0x0000BD,
+	0x00008D, 0x023164, 0x000A64, 0x280D02,
+	0x0B808D, 0x000820, 0x0002FD, 0x018040,
+	0x200007, 0x00387D, 0x018042, 0x08000A,
+	0x000904, 0x3E3286, 0x030000, 0x0002FD,
+	0x018042, 0x08000A, 0x000904, 0x3D8286,
+	0x000007, 0x002810, 0x28043A, 0x00750D,
+	0x030924, 0x002264, 0x280D02, 0x02316C,
+	0x28450A, 0x0B810D, 0x000820, 0x0002FD,
+	0x018040, 0x200007, 0x00008D, 0x000A24,
+	0x3E4A06, 0x100102, 0x001810, 0x10003A,
+	0x0000BD, 0x003810, 0x30043A, 0x00187D,
+	0x018042, 0x0018FD, 0x09804A, 0x20000A,
+	0x0000AD, 0x028924, 0x07212C, 0x001010,
+	0x300583, 0x300D8B, 0x3014BB, 0x301C83,
+	0x002083, 0x00137D, 0x038042, 0x33844A,
+	0x33ACCB, 0x33B4CB, 0x33BCCB, 0x33C4CB,
+	0x33CCCB, 0x33D4CB, 0x305C8B, 0x006083,
+	0x001E0D, 0x0005FD, 0x018042, 0x20000A,
+	0x020924, 0x00068D, 0x00A96C, 0x00009D,
+	0x0002FD, 0x018042, 0x08000A, 0x000904,
+	0x3F6A86, 0x000007, 0x280502, 0x280D0A,
+	0x284402, 0x001810, 0x28143A, 0x0C008D,
+	0x000820, 0x0002FD, 0x018040, 0x220007,
+	0x003904, 0x225886, 0x001E0D, 0x00057D,
+	0x018042, 0x20000A, 0x020924, 0x0000A5,
+	0x0002FD, 0x018042, 0x08000A, 0x000904,
+	0x402A86, 0x000007, 0x280502, 0x280C02,
+	0x002010, 0x28143A, 0x0C010D, 0x000820,
+	0x0002FD, 0x018040, 0x225A06, 0x220007,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000
+};
+
+#endif	//_HWMCODE_
Index: /GPL/branches/alsa-resync1/alsa-kernel/pci/ymfpci/ymfpci_main.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/pci/ymfpci/ymfpci_main.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/pci/ymfpci/ymfpci_main.c	(revision 91)
@@ -0,0 +1,2377 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  Routines for control of YMF724/740/744/754 chips
+ *
+ *  BUGS:
+ *    --
+ *
+ *  TODO:
+ *    --
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <sound/control.h>
+#include <sound/info.h>
+#include <sound/ymfpci.h>
+#include <sound/timer.h>
+
+/*
+ *  constants
+ */
+
+/*
+ *  common I/O routines
+ */
+
+static void snd_ymfpci_irq_wait(ymfpci_t *chip);
+
+static inline u8 snd_ymfpci_readb(ymfpci_t *chip, u32 offset)
+{
+    return readb(chip->reg_area_virt + offset);
+}
+
+static inline void snd_ymfpci_writeb(ymfpci_t *chip, u32 offset, u8 val)
+{
+    writeb(val, chip->reg_area_virt + offset);
+}
+
+static inline u16 snd_ymfpci_readw(ymfpci_t *chip, u32 offset)
+{
+    return readw(chip->reg_area_virt + offset);
+}
+
+static inline void snd_ymfpci_writew(ymfpci_t *chip, u32 offset, u16 val)
+{
+    writew(val, chip->reg_area_virt + offset);
+}
+
+static inline u32 snd_ymfpci_readl(ymfpci_t *chip, u32 offset)
+{
+    return readl(chip->reg_area_virt + offset);
+}
+
+static inline void snd_ymfpci_writel(ymfpci_t *chip, u32 offset, u32 val)
+{
+    writel(val, chip->reg_area_virt + offset);
+}
+
+static int snd_ymfpci_codec_ready(ymfpci_t *chip, int secondary)
+{
+    unsigned long end_time;
+    u32 reg = secondary ? YDSXGR_SECSTATUSADR : YDSXGR_PRISTATUSADR;
+
+    end_time = jiffies + msecs_to_jiffies(750);
+    do {
+        if ((snd_ymfpci_readw(chip, reg) & 0x8000) == 0)
+            return 0;
+        set_current_state(TASK_UNINTERRUPTIBLE);
+        schedule_timeout(1);
+    } while (time_before(jiffies, end_time));
+    snd_printk("codec_ready: codec %i is not ready [0x%x]\n", secondary, snd_ymfpci_readw(chip, reg));
+    return -EBUSY;
+}
+
+static void snd_ymfpci_codec_write(ac97_t *ac97, u16 reg, u16 val)
+{
+    ymfpci_t *chip = ac97->private_data;
+    u32 cmd;
+
+    snd_ymfpci_codec_ready(chip, 0);
+    cmd = ((YDSXG_AC97WRITECMD | reg) << 16) | val;
+    snd_ymfpci_writel(chip, YDSXGR_AC97CMDDATA, cmd);
+}
+
+static u16 snd_ymfpci_codec_read(ac97_t *ac97, u16 reg)
+{
+    ymfpci_t *chip = ac97->private_data;
+
+    if (snd_ymfpci_codec_ready(chip, 0))
+        return ~0;
+    snd_ymfpci_writew(chip, YDSXGR_AC97CMDADR, YDSXG_AC97READCMD | reg);
+    if (snd_ymfpci_codec_ready(chip, 0))
+        return ~0;
+    if (chip->device_id == PCI_DEVICE_ID_YAMAHA_744 && chip->rev < 2) {
+        int i;
+        for (i = 0; i < 600; i++)
+            snd_ymfpci_readw(chip, YDSXGR_PRISTATUSDATA);
+    }
+    return snd_ymfpci_readw(chip, YDSXGR_PRISTATUSDATA);
+}
+
+/*
+ *  Misc routines
+ */
+
+static u32 snd_ymfpci_calc_delta(u32 rate)
+{
+    switch (rate) {
+    case 8000:	return 0x02aaab00;
+    case 11025:	return 0x03accd00;
+    case 16000:	return 0x05555500;
+    case 22050:	return 0x07599a00;
+    case 32000:	return 0x0aaaab00;
+    case 44100:	return 0x0eb33300;
+    default:	return ((rate << 16) / 375) << 5;
+    }
+}
+
+static u32 def_rate[8] = {
+    100, 2000, 8000, 11025, 16000, 22050, 32000, 48000
+};
+
+static u32 snd_ymfpci_calc_lpfK(u32 rate)
+{
+    u32 i;
+    static u32 val[8] = {
+        0x00570000, 0x06AA0000, 0x18B20000, 0x20930000,
+        0x2B9A0000, 0x35A10000, 0x3EAA0000, 0x40000000
+    };
+
+    if (rate == 44100)
+        return 0x40000000;	/* FIXME: What's the right value? */
+    for (i = 0; i < 8; i++)
+        if (rate <= def_rate[i])
+            return val[i];
+    return val[0];
+}
+
+static u32 snd_ymfpci_calc_lpfQ(u32 rate)
+{
+    u32 i;
+    static u32 val[8] = {
+        0x35280000, 0x34A70000, 0x32020000, 0x31770000,
+        0x31390000, 0x31C90000, 0x33D00000, 0x40000000
+    };
+
+    if (rate == 44100)
+        return 0x370A0000;
+    for (i = 0; i < 8; i++)
+        if (rate <= def_rate[i])
+            return val[i];
+    return val[0];
+}
+
+/*
+ *  Hardware start management
+ */
+
+static void snd_ymfpci_hw_start(ymfpci_t *chip)
+{
+    unsigned long flags;
+
+    spin_lock_irqsave(&chip->reg_lock, flags);
+    if (chip->start_count++ > 0)
+        goto __end;
+    snd_ymfpci_writel(chip, YDSXGR_MODE,
+                      snd_ymfpci_readl(chip, YDSXGR_MODE) | 3);
+    chip->active_bank = snd_ymfpci_readl(chip, YDSXGR_CTRLSELECT) & 1;
+__end:
+    spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+static void snd_ymfpci_hw_stop(ymfpci_t *chip)
+{
+    unsigned long flags;
+    long timeout = 1000;
+
+    spin_lock_irqsave(&chip->reg_lock, flags);
+    if (--chip->start_count > 0)
+        goto __end;
+    snd_ymfpci_writel(chip, YDSXGR_MODE,
+                      snd_ymfpci_readl(chip, YDSXGR_MODE) & ~3);
+    while (timeout-- > 0) {
+        if ((snd_ymfpci_readl(chip, YDSXGR_STATUS) & 2) == 0)
+            break;
+    }
+    if (atomic_read(&chip->interrupt_sleep_count)) {
+        atomic_set(&chip->interrupt_sleep_count, 0);
+        wake_up(&chip->interrupt_sleep);
+    }
+__end:
+    spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+/*
+ *  Playback voice management
+ */
+
+static int voice_alloc(ymfpci_t *chip, ymfpci_voice_type_t type, int pair, ymfpci_voice_t **rvoice)
+{
+    ymfpci_voice_t *voice, *voice2;
+    int idx;
+
+    *rvoice = NULL;
+    for (idx = 0; idx < YDSXG_PLAYBACK_VOICES; idx += pair ? 2 : 1) {
+        voice = &chip->voices[idx];
+        voice2 = pair ? &chip->voices[idx+1] : NULL;
+        if (voice->use || (voice2 && voice2->use))
+            continue;
+        voice->use = 1;
+        if (voice2)
+            voice2->use = 1;
+        switch (type) {
+        case YMFPCI_PCM:
+            voice->pcm = 1;
+            if (voice2)
+                voice2->pcm = 1;
+            break;
+        case YMFPCI_SYNTH:
+            voice->synth = 1;
+            break;
+        case YMFPCI_MIDI:
+            voice->midi = 1;
+            break;
+        }
+        snd_ymfpci_hw_start(chip);
+        if (voice2)
+            snd_ymfpci_hw_start(chip);
+        *rvoice = voice;
+        return 0;
+    }
+    return -ENOMEM;
+}
+
+int snd_ymfpci_voice_alloc(ymfpci_t *chip, ymfpci_voice_type_t type, int pair, ymfpci_voice_t **rvoice)
+{
+    unsigned long flags;
+    int result;
+
+    snd_assert(rvoice != NULL, return -EINVAL);
+    snd_assert(!pair || type == YMFPCI_PCM, return -EINVAL);
+
+    spin_lock_irqsave(&chip->voice_lock, flags);
+    for (;;) {
+        result = voice_alloc(chip, type, pair, rvoice);
+        if (result == 0 || type != YMFPCI_PCM)
+            break;
+        /* TODO: synth/midi voice deallocation */
+        break;
+    }
+    spin_unlock_irqrestore(&chip->voice_lock, flags);
+    return result;
+}
+
+int snd_ymfpci_voice_free(ymfpci_t *chip, ymfpci_voice_t *pvoice)
+{
+    unsigned long flags;
+
+    snd_assert(pvoice != NULL, return -EINVAL);
+    snd_ymfpci_hw_stop(chip);
+    spin_lock_irqsave(&chip->voice_lock, flags);
+    pvoice->use = pvoice->pcm = pvoice->synth = pvoice->midi = 0;
+    pvoice->ypcm = NULL;
+    pvoice->interrupt = NULL;
+    spin_unlock_irqrestore(&chip->voice_lock, flags);
+    return 0;
+}
+
+/*
+ *  PCM part
+ */
+
+static void snd_ymfpci_pcm_interrupt(ymfpci_t *chip, ymfpci_voice_t *voice)
+{
+    ymfpci_pcm_t *ypcm;
+    u32 pos, delta;
+
+    if ((ypcm = voice->ypcm) == NULL)
+        return;
+    if (ypcm->substream == NULL)
+        return;
+    spin_lock(&chip->reg_lock);
+    if (ypcm->running) {
+        pos = le32_to_cpu(voice->bank[chip->active_bank].start);
+        if (pos < ypcm->last_pos)
+            delta = pos + (ypcm->buffer_size - ypcm->last_pos);
+        else
+            delta = pos - ypcm->last_pos;
+        ypcm->period_pos += delta;
+        ypcm->last_pos = pos;
+        if (ypcm->period_pos >= ypcm->period_size) {
+            // printk("done - active_bank = 0x%x, start = 0x%x\n", chip->active_bank, voice->bank[chip->active_bank].start);
+            ypcm->period_pos %= ypcm->period_size;
+            spin_unlock(&chip->reg_lock);
+            snd_pcm_period_elapsed(ypcm->substream);
+            spin_lock(&chip->reg_lock);
+        }
+        if (unlikely(ypcm->update_pcm_vol)) {
+            unsigned int subs = ypcm->substream->number;
+            unsigned int next_bank = 1 - chip->active_bank;
+            snd_ymfpci_playback_bank_t *bank;
+            u32 volume;
+
+            bank = &voice->bank[next_bank];
+            volume = cpu_to_le32(chip->pcm_mixer[subs].left << 15);
+            bank->left_gain_end = volume;
+            if (ypcm->output_rear)
+                bank->eff2_gain_end = volume;
+            if (ypcm->voices[1])
+                bank = &ypcm->voices[1]->bank[next_bank];
+            volume = cpu_to_le32(chip->pcm_mixer[subs].right << 15);
+            bank->right_gain_end = volume;
+            if (ypcm->output_rear)
+                bank->eff3_gain_end = volume;
+            ypcm->update_pcm_vol--;
+        }
+    }
+    spin_unlock(&chip->reg_lock);
+}
+
+static void snd_ymfpci_pcm_capture_interrupt(snd_pcm_substream_t *substream)
+{
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    ymfpci_pcm_t *ypcm = runtime->private_data;
+    ymfpci_t *chip = ypcm->chip;
+    u32 pos, delta;
+
+    spin_lock(&chip->reg_lock);
+    if (ypcm->running) {
+        pos = le32_to_cpu(chip->bank_capture[ypcm->capture_bank_number][chip->active_bank]->start) >> ypcm->shift;
+        if (pos < ypcm->last_pos)
+            delta = pos + (ypcm->buffer_size - ypcm->last_pos);
+        else
+            delta = pos - ypcm->last_pos;
+        ypcm->period_pos += delta;
+        ypcm->last_pos = pos;
+        if (ypcm->period_pos >= ypcm->period_size) {
+            ypcm->period_pos %= ypcm->period_size;
+            // printk("done - active_bank = 0x%x, start = 0x%x\n", chip->active_bank, voice->bank[chip->active_bank].start);
+            spin_unlock(&chip->reg_lock);
+            snd_pcm_period_elapsed(substream);
+            spin_lock(&chip->reg_lock);
+        }
+    }
+    spin_unlock(&chip->reg_lock);
+}
+
+static int snd_ymfpci_playback_trigger(snd_pcm_substream_t * substream,
+                                       int cmd)
+{
+    ymfpci_t *chip = snd_pcm_substream_chip(substream);
+    ymfpci_pcm_t *ypcm = substream->runtime->private_data;
+    int result = 0;
+
+    spin_lock(&chip->reg_lock);
+    if (ypcm->voices[0] == NULL) {
+        result = -EINVAL;
+        goto __unlock;
+    }
+    switch (cmd) {
+    case SNDRV_PCM_TRIGGER_START:
+    case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+    case SNDRV_PCM_TRIGGER_RESUME:
+        chip->ctrl_playback[ypcm->voices[0]->number + 1] = cpu_to_le32(ypcm->voices[0]->bank_addr);
+        if (ypcm->voices[1] != NULL)
+            chip->ctrl_playback[ypcm->voices[1]->number + 1] = cpu_to_le32(ypcm->voices[1]->bank_addr);
+        ypcm->running = 1;
+        break;
+    case SNDRV_PCM_TRIGGER_STOP:
+    case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+    case SNDRV_PCM_TRIGGER_SUSPEND:
+        chip->ctrl_playback[ypcm->voices[0]->number + 1] = 0;
+        if (ypcm->voices[1] != NULL)
+            chip->ctrl_playback[ypcm->voices[1]->number + 1] = 0;
+        ypcm->running = 0;
+        break;
+    default:
+        result = -EINVAL;
+        break;
+    }
+__unlock:
+    spin_unlock(&chip->reg_lock);
+    return result;
+}
+
+static int snd_ymfpci_capture_trigger(snd_pcm_substream_t * substream,
+                                      int cmd)
+{
+    ymfpci_t *chip = snd_pcm_substream_chip(substream);
+    ymfpci_pcm_t *ypcm = substream->runtime->private_data;
+    int result = 0;
+    u32 tmp;
+
+    spin_lock(&chip->reg_lock);
+    switch (cmd) {
+    case SNDRV_PCM_TRIGGER_START:
+    case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+    case SNDRV_PCM_TRIGGER_RESUME:
+        tmp = snd_ymfpci_readl(chip, YDSXGR_MAPOFREC) | (1 << ypcm->capture_bank_number);
+        snd_ymfpci_writel(chip, YDSXGR_MAPOFREC, tmp);
+        ypcm->running = 1;
+        break;
+    case SNDRV_PCM_TRIGGER_STOP:
+    case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+    case SNDRV_PCM_TRIGGER_SUSPEND:
+        tmp = snd_ymfpci_readl(chip, YDSXGR_MAPOFREC) & ~(1 << ypcm->capture_bank_number);
+        snd_ymfpci_writel(chip, YDSXGR_MAPOFREC, tmp);
+        ypcm->running = 0;
+        break;
+    default:
+        result = -EINVAL;
+        break;
+    }
+    spin_unlock(&chip->reg_lock);
+    return result;
+}
+
+static int snd_ymfpci_pcm_voice_alloc(ymfpci_pcm_t *ypcm, int voices)
+{
+    int err;
+
+    if (ypcm->voices[1] != NULL && voices < 2) {
+        snd_ymfpci_voice_free(ypcm->chip, ypcm->voices[1]);
+        ypcm->voices[1] = NULL;
+    }
+    if (voices == 1 && ypcm->voices[0] != NULL)
+        return 0;		/* already allocated */
+    if (voices == 2 && ypcm->voices[0] != NULL && ypcm->voices[1] != NULL)
+        return 0;		/* already allocated */
+    if (voices > 1) {
+        if (ypcm->voices[0] != NULL && ypcm->voices[1] == NULL) {
+            snd_ymfpci_voice_free(ypcm->chip, ypcm->voices[0]);
+            ypcm->voices[0] = NULL;
+        }
+    }
+    err = snd_ymfpci_voice_alloc(ypcm->chip, YMFPCI_PCM, voices > 1, &ypcm->voices[0]);
+    if (err < 0)
+        return err;
+    ypcm->voices[0]->ypcm = ypcm;
+    ypcm->voices[0]->interrupt = snd_ymfpci_pcm_interrupt;
+    if (voices > 1) {
+        ypcm->voices[1] = &ypcm->chip->voices[ypcm->voices[0]->number + 1];
+        ypcm->voices[1]->ypcm = ypcm;
+    }
+    return 0;
+}
+
+static void snd_ymfpci_pcm_init_voice(ymfpci_pcm_t *ypcm, unsigned int voiceidx,
+                                      snd_pcm_runtime_t *runtime,
+                                      int has_pcm_volume)
+{
+    ymfpci_voice_t *voice = ypcm->voices[voiceidx];
+    u32 format;
+    u32 delta = snd_ymfpci_calc_delta(runtime->rate);
+    u32 lpfQ = snd_ymfpci_calc_lpfQ(runtime->rate);
+    u32 lpfK = snd_ymfpci_calc_lpfK(runtime->rate);
+    snd_ymfpci_playback_bank_t *bank;
+    unsigned int nbank;
+    u32 vol_left, vol_right;
+    u8 use_left, use_right;
+
+    snd_assert(voice != NULL, return);
+    if (runtime->channels == 1) {
+        use_left = 1;
+        use_right = 1;
+    } else {
+        use_left = (voiceidx & 1) == 0;
+        use_right = !use_left;
+    }
+    if (has_pcm_volume) {
+        vol_left = cpu_to_le32(ypcm->chip->pcm_mixer
+                               [ypcm->substream->number].left << 15);
+        vol_right = cpu_to_le32(ypcm->chip->pcm_mixer
+                                [ypcm->substream->number].right << 15);
+    } else {
+        vol_left = cpu_to_le32(0x40000000);
+        vol_right = cpu_to_le32(0x40000000);
+    }
+    format = runtime->channels == 2 ? 0x00010000 : 0;
+    if (snd_pcm_format_width(runtime->format) == 8)
+        format |= 0x80000000;
+    if (runtime->channels == 2 && (voiceidx & 1) != 0)
+  	                 format |= 1;
+    for (nbank = 0; nbank < 2; nbank++) {
+        bank = &voice->bank[nbank];
+        memset(bank, 0, sizeof(*bank));
+        bank->format = cpu_to_le32(format);
+        bank->base = cpu_to_le32(runtime->dma_addr);
+        bank->loop_end = cpu_to_le32(ypcm->buffer_size);
+        bank->lpfQ = cpu_to_le32(lpfQ);
+        bank->delta =
+            bank->delta_end = cpu_to_le32(delta);
+        bank->lpfK =
+            bank->lpfK_end = cpu_to_le32(lpfK);
+        bank->eg_gain =
+            bank->eg_gain_end = cpu_to_le32(0x40000000);
+
+        if (ypcm->output_front) {
+            if (use_left) {
+                bank->left_gain =
+                    bank->left_gain_end = vol_left;
+            }
+            if (use_right) {
+                bank->right_gain =
+                    bank->right_gain_end = vol_right;
+            }
+        }
+        if (ypcm->output_rear) {
+            if (use_left) {
+                bank->eff2_gain =
+                    bank->eff2_gain_end = vol_left;
+            }
+            if (use_right) {
+                bank->eff3_gain =
+                    bank->eff3_gain_end = vol_right;
+            }
+        }
+    }
+}
+
+static int __devinit snd_ymfpci_ac3_init(ymfpci_t *chip)
+{
+	if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci),
+				4096, &chip->ac3_tmp_base) < 0)
+		return -ENOMEM;
+
+	chip->bank_effect[3][0]->base =
+	chip->bank_effect[3][1]->base = cpu_to_le32(chip->ac3_tmp_base.addr);
+	chip->bank_effect[3][0]->loop_end =
+	chip->bank_effect[3][1]->loop_end = cpu_to_le32(1024);
+	chip->bank_effect[4][0]->base =
+	chip->bank_effect[4][1]->base = cpu_to_le32(chip->ac3_tmp_base.addr + 2048);
+	chip->bank_effect[4][0]->loop_end =
+	chip->bank_effect[4][1]->loop_end = cpu_to_le32(1024);
+
+	spin_lock_irq(&chip->reg_lock);
+	snd_ymfpci_writel(chip, YDSXGR_MAPOFEFFECT,
+			  snd_ymfpci_readl(chip, YDSXGR_MAPOFEFFECT) | 3 << 3);
+	spin_unlock_irq(&chip->reg_lock);
+	return 0;
+}
+
+static int snd_ymfpci_ac3_done(ymfpci_t *chip)
+{
+	spin_lock_irq(&chip->reg_lock);
+	snd_ymfpci_writel(chip, YDSXGR_MAPOFEFFECT,
+			  snd_ymfpci_readl(chip, YDSXGR_MAPOFEFFECT) & ~(3 << 3));
+	spin_unlock_irq(&chip->reg_lock);
+	// snd_ymfpci_irq_wait(chip);
+	if (chip->ac3_tmp_base.area) {
+		snd_dma_free_pages(&chip->ac3_tmp_base);
+		chip->ac3_tmp_base.area = NULL;
+	}
+	return 0;
+}
+
+static int snd_ymfpci_playback_hw_params(snd_pcm_substream_t * substream,
+                                         snd_pcm_hw_params_t * hw_params)
+{
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    ymfpci_pcm_t *ypcm = runtime->private_data;
+    int err;
+
+    if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
+        return err;
+    if ((err = snd_ymfpci_pcm_voice_alloc(ypcm, params_channels(hw_params))) < 0)
+        return err;
+    return 0;
+}
+
+static int snd_ymfpci_playback_hw_free(snd_pcm_substream_t * substream)
+{
+    ymfpci_t *chip = snd_pcm_substream_chip(substream);
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    ymfpci_pcm_t *ypcm;
+
+    if (runtime->private_data == NULL)
+        return 0;
+    ypcm = runtime->private_data;
+
+    /* wait, until the PCI operations are not finished */
+    snd_ymfpci_irq_wait(chip);
+    snd_pcm_lib_free_pages(substream);
+    if (ypcm->voices[1]) {
+        snd_ymfpci_voice_free(chip, ypcm->voices[1]);
+        ypcm->voices[1] = NULL;
+    }
+    if (ypcm->voices[0]) {
+        snd_ymfpci_voice_free(chip, ypcm->voices[0]);
+        ypcm->voices[0] = NULL;
+    }
+    return 0;
+}
+
+static int snd_ymfpci_playback_prepare(snd_pcm_substream_t * substream)
+{
+    ymfpci_t *chip = snd_pcm_substream_chip(substream);
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    ymfpci_pcm_t *ypcm = runtime->private_data;
+    unsigned int nvoice;
+
+    ypcm->period_size = runtime->period_size;
+    ypcm->buffer_size = runtime->buffer_size;
+    ypcm->period_pos = 0;
+    ypcm->last_pos = 0;
+    for (nvoice = 0; nvoice < runtime->channels; nvoice++)
+        snd_ymfpci_pcm_init_voice(ypcm, nvoice, runtime,
+                                  substream->pcm == chip->pcm);
+    return 0;
+}
+
+static int snd_ymfpci_capture_hw_params(snd_pcm_substream_t * substream,
+                                        snd_pcm_hw_params_t * hw_params)
+{
+    return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+static int snd_ymfpci_capture_hw_free(snd_pcm_substream_t * substream)
+{
+    ymfpci_t *chip = snd_pcm_substream_chip(substream);
+
+    /* wait, until the PCI operations are not finished */
+    snd_ymfpci_irq_wait(chip);
+    return snd_pcm_lib_free_pages(substream);
+}
+
+static int snd_ymfpci_capture_prepare(snd_pcm_substream_t * substream)
+{
+    ymfpci_t *chip = snd_pcm_substream_chip(substream);
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    ymfpci_pcm_t *ypcm = runtime->private_data;
+    snd_ymfpci_capture_bank_t * bank;
+    int nbank;
+    u32 rate, format;
+
+    ypcm->period_size = runtime->period_size;
+    ypcm->buffer_size = runtime->buffer_size;
+    ypcm->period_pos = 0;
+    ypcm->last_pos = 0;
+    ypcm->shift = 0;
+    rate = ((48000 * 4096) / runtime->rate) - 1;
+    format = 0;
+    if (runtime->channels == 2) {
+        format |= 2;
+        ypcm->shift++;
+    }
+    if (snd_pcm_format_width(runtime->format) == 8)
+        format |= 1;
+    else
+        ypcm->shift++;
+    switch (ypcm->capture_bank_number) {
+    case 0:
+        snd_ymfpci_writel(chip, YDSXGR_RECFORMAT, format);
+        snd_ymfpci_writel(chip, YDSXGR_RECSLOTSR, rate);
+        break;
+    case 1:
+        snd_ymfpci_writel(chip, YDSXGR_ADCFORMAT, format);
+        snd_ymfpci_writel(chip, YDSXGR_ADCSLOTSR, rate);
+        break;
+    }
+    for (nbank = 0; nbank < 2; nbank++) {
+        bank = chip->bank_capture[ypcm->capture_bank_number][nbank];
+        bank->base = cpu_to_le32(runtime->dma_addr);
+        bank->loop_end = cpu_to_le32(ypcm->buffer_size << ypcm->shift);
+        bank->start = 0;
+        bank->num_of_loops = 0;
+    }
+    return 0;
+}
+
+static snd_pcm_uframes_t snd_ymfpci_playback_pointer(snd_pcm_substream_t * substream)
+{
+    ymfpci_t *chip = snd_pcm_substream_chip(substream);
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    ymfpci_pcm_t *ypcm = runtime->private_data;
+    ymfpci_voice_t *voice = ypcm->voices[0];
+
+    if (!(ypcm->running && voice))
+        return 0;
+    return le32_to_cpu(voice->bank[chip->active_bank].start);
+}
+
+static snd_pcm_uframes_t snd_ymfpci_capture_pointer(snd_pcm_substream_t * substream)
+{
+    ymfpci_t *chip = snd_pcm_substream_chip(substream);
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    ymfpci_pcm_t *ypcm = runtime->private_data;
+
+    if (!ypcm->running)
+        return 0;
+    return le32_to_cpu(chip->bank_capture[ypcm->capture_bank_number][chip->active_bank]->start) >> ypcm->shift;
+}
+
+static void snd_ymfpci_irq_wait(ymfpci_t *chip)
+{
+    wait_queue_t wait;
+    int loops = 4;
+
+    while (loops-- > 0) {
+        if ((snd_ymfpci_readl(chip, YDSXGR_MODE) & 3) == 0)
+            continue;
+        init_waitqueue_entry(&wait, current);
+        add_wait_queue(&chip->interrupt_sleep, &wait);
+        atomic_inc(&chip->interrupt_sleep_count);
+        set_current_state(TASK_UNINTERRUPTIBLE);
+        schedule_timeout(HZ/20);
+        remove_wait_queue(&chip->interrupt_sleep, &wait);
+    }
+}
+
+static irqreturn_t snd_ymfpci_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+    ymfpci_t *chip = dev_id;
+    u32 status, nvoice, mode;
+    ymfpci_voice_t *voice;
+
+    status = snd_ymfpci_readl(chip, YDSXGR_STATUS);
+    if (status & 0x80000000) {
+        chip->active_bank = snd_ymfpci_readl(chip, YDSXGR_CTRLSELECT) & 1;
+        spin_lock(&chip->voice_lock);
+        for (nvoice = 0; nvoice < YDSXG_PLAYBACK_VOICES; nvoice++) {
+            voice = &chip->voices[nvoice];
+            if (voice->interrupt)
+                voice->interrupt(chip, voice);
+        }
+        for (nvoice = 0; nvoice < YDSXG_CAPTURE_VOICES; nvoice++) {
+            if (chip->capture_substream[nvoice])
+                snd_ymfpci_pcm_capture_interrupt(chip->capture_substream[nvoice]);
+        }
+#if 0
+        for (nvoice = 0; nvoice < YDSXG_EFFECT_VOICES; nvoice++) {
+            if (chip->effect_substream[nvoice])
+                snd_ymfpci_pcm_effect_interrupt(chip->effect_substream[nvoice]);
+        }
+#endif
+        spin_unlock(&chip->voice_lock);
+        spin_lock(&chip->reg_lock);
+        snd_ymfpci_writel(chip, YDSXGR_STATUS, 0x80000000);
+        mode = snd_ymfpci_readl(chip, YDSXGR_MODE) | 2;
+        snd_ymfpci_writel(chip, YDSXGR_MODE, mode);
+        spin_unlock(&chip->reg_lock);
+
+        if (atomic_read(&chip->interrupt_sleep_count)) {
+            atomic_set(&chip->interrupt_sleep_count, 0);
+            wake_up(&chip->interrupt_sleep);
+        }
+    }
+
+    status = snd_ymfpci_readw(chip, YDSXGR_INTFLAG);
+    if (status & 1) {
+        if (chip->timer)
+            snd_timer_interrupt(chip->timer, chip->timer->sticks);
+    }
+    snd_ymfpci_writew(chip, YDSXGR_INTFLAG, status);
+    if (chip->rawmidi)
+        snd_mpu401_uart_interrupt(irq, chip->rawmidi->private_data, regs);
+    return IRQ_HANDLED;
+}
+
+static snd_pcm_hardware_t snd_ymfpci_playback =
+{
+    /*	info:		  */	(SNDRV_PCM_INFO_MMAP |
+                                 SNDRV_PCM_INFO_MMAP_VALID |
+                                 SNDRV_PCM_INFO_INTERLEAVED |
+                                 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+                                 SNDRV_PCM_INFO_PAUSE |
+                                 SNDRV_PCM_INFO_RESUME),
+                                 /*	formats:	  */	SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+                                 /*	rates:		  */	SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+                                 /*	rate_min:	  */	8000,
+                                 /*	rate_max:	  */	48000,
+                                 /*	channels_min:	  */	1,
+                                 /*	channels_max:	  */	2,
+                                 /*	buffer_bytes_max:  */	256 * 1024, /* FIXME: enough? */
+                                 /*	period_bytes_min:  */	64,
+                                 /*	period_bytes_max:  */	256 * 1024, /* FIXME: enough? */
+                                 /*	periods_min:	  */	3,
+                                 /*	periods_max:	  */	1024,
+                                 /*	fifo_size:	  */	0,
+};
+
+static snd_pcm_hardware_t snd_ymfpci_capture =
+{
+    /*	info:		  */	(SNDRV_PCM_INFO_MMAP |
+                                 SNDRV_PCM_INFO_MMAP_VALID |
+                                 SNDRV_PCM_INFO_INTERLEAVED |
+                                 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+                                 SNDRV_PCM_INFO_PAUSE |
+                                 SNDRV_PCM_INFO_RESUME),
+                                 /*	formats:	  */	SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+                                 /*	rates:		  */	SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+                                 /*	rate_min:	  */	8000,
+                                 /*	rate_max:	  */	48000,
+                                 /*	channels_min:	  */	1,
+                                 /*	channels_max:	  */	2,
+                                 /*	buffer_bytes_max:  */	256 * 1024, /* FIXME: enough? */
+                                 /*	period_bytes_min:  */	64,
+                                 /*	period_bytes_max:  */	256 * 1024, /* FIXME: enough? */
+                                 /*	periods_min:	  */	3,
+                                 /*	periods_max:	  */	1024,
+                                 /*	fifo_size:	  */	0,
+};
+
+static void snd_ymfpci_pcm_free_substream(snd_pcm_runtime_t *runtime)
+{
+    ymfpci_pcm_t *ypcm = runtime->private_data;
+
+    if (ypcm)
+        kfree(ypcm);
+}
+
+static int snd_ymfpci_playback_open_1(snd_pcm_substream_t * substream)
+{
+    ymfpci_t *chip = snd_pcm_substream_chip(substream);
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    ymfpci_pcm_t *ypcm;
+
+    ypcm = kcalloc(1, sizeof(*ypcm), GFP_KERNEL);
+    if (ypcm == NULL)
+        return -ENOMEM;
+    ypcm->chip = chip;
+    ypcm->type = PLAYBACK_VOICE;
+    ypcm->substream = substream;
+    runtime->hw = snd_ymfpci_playback;
+    runtime->private_data = ypcm;
+    runtime->private_free = snd_ymfpci_pcm_free_substream;
+    /* FIXME? True value is 256/48 = 5.33333 ms */
+    snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 5333, UINT_MAX);
+    return 0;
+}
+
+/* call with spinlock held */
+static void ymfpci_open_extension(ymfpci_t *chip)
+{
+    if (! chip->rear_opened) {
+        if (! chip->spdif_opened) /* set AC3 */
+            snd_ymfpci_writel(chip, YDSXGR_MODE,
+                              snd_ymfpci_readl(chip, YDSXGR_MODE) | (1 << 30));
+        /* enable second codec (4CHEN) */
+        snd_ymfpci_writew(chip, YDSXGR_SECCONFIG,
+                          (snd_ymfpci_readw(chip, YDSXGR_SECCONFIG) & ~0x0330) | 0x0010);
+    }
+}
+
+/* call with spinlock held */
+static void ymfpci_close_extension(ymfpci_t *chip)
+{
+    if (! chip->rear_opened) {
+        if (! chip->spdif_opened)
+            snd_ymfpci_writel(chip, YDSXGR_MODE,
+                              snd_ymfpci_readl(chip, YDSXGR_MODE) & ~(1 << 30));
+        snd_ymfpci_writew(chip, YDSXGR_SECCONFIG,
+                          (snd_ymfpci_readw(chip, YDSXGR_SECCONFIG) & ~0x0330) & ~0x0010);
+    }
+}
+
+static int snd_ymfpci_playback_open(snd_pcm_substream_t * substream)
+{
+    ymfpci_t *chip = snd_pcm_substream_chip(substream);
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    ymfpci_pcm_t *ypcm;
+    snd_kcontrol_t *kctl;
+    int err;
+
+    if ((err = snd_ymfpci_playback_open_1(substream)) < 0)
+        return err;
+    ypcm = runtime->private_data;
+    ypcm->output_front = 1;
+    ypcm->output_rear = chip->mode_dup4ch ? 1 : 0;
+    spin_lock_irq(&chip->reg_lock);
+    if (ypcm->output_rear) {
+        ymfpci_open_extension(chip);
+        chip->rear_opened++;
+    }
+    spin_unlock_irq(&chip->reg_lock);
+
+    kctl = chip->pcm_mixer[substream->number].ctl;
+    kctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+    snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_INFO, &kctl->id);
+    return 0;
+}
+
+static int snd_ymfpci_playback_spdif_open(snd_pcm_substream_t * substream)
+{
+    ymfpci_t *chip = snd_pcm_substream_chip(substream);
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    ymfpci_pcm_t *ypcm;
+    int err;
+
+    if ((err = snd_ymfpci_playback_open_1(substream)) < 0)
+        return err;
+    ypcm = runtime->private_data;
+    ypcm->output_front = 0;
+    ypcm->output_rear = 1;
+    spin_lock_irq(&chip->reg_lock);
+    snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTCTRL,
+                      snd_ymfpci_readw(chip, YDSXGR_SPDIFOUTCTRL) | 2);
+    ymfpci_open_extension(chip);
+    chip->spdif_pcm_bits = chip->spdif_bits;
+    snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTSTATUS, chip->spdif_pcm_bits);
+    chip->spdif_opened++;
+    spin_unlock_irq(&chip->reg_lock);
+
+    chip->spdif_pcm_ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+    snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE |
+                   SNDRV_CTL_EVENT_MASK_INFO, &chip->spdif_pcm_ctl->id);
+    return 0;
+}
+
+static int snd_ymfpci_playback_4ch_open(snd_pcm_substream_t * substream)
+{
+    ymfpci_t *chip = snd_pcm_substream_chip(substream);
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    ymfpci_pcm_t *ypcm;
+    int err;
+
+    if ((err = snd_ymfpci_playback_open_1(substream)) < 0)
+        return err;
+    ypcm = runtime->private_data;
+    ypcm->output_front = 0;
+    ypcm->output_rear = 1;
+    spin_lock_irq(&chip->reg_lock);
+    ymfpci_open_extension(chip);
+    chip->rear_opened++;
+    spin_unlock_irq(&chip->reg_lock);
+    return 0;
+}
+
+static int snd_ymfpci_capture_open(snd_pcm_substream_t * substream,
+                                   u32 capture_bank_number)
+{
+    ymfpci_t *chip = snd_pcm_substream_chip(substream);
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    ymfpci_pcm_t *ypcm;
+
+    ypcm = kcalloc(1, sizeof(*ypcm), GFP_KERNEL);
+    if (ypcm == NULL)
+        return -ENOMEM;
+    ypcm->chip = chip;
+    ypcm->type = capture_bank_number + CAPTURE_REC;
+    ypcm->substream = substream;
+    ypcm->capture_bank_number = capture_bank_number;
+    chip->capture_substream[capture_bank_number] = substream;
+    runtime->hw = snd_ymfpci_capture;
+    /* FIXME? True value is 256/48 = 5.33333 ms */
+    snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 5333, UINT_MAX);
+    runtime->private_data = ypcm;
+    runtime->private_free = snd_ymfpci_pcm_free_substream;
+    snd_ymfpci_hw_start(chip);
+    return 0;
+}
+
+static int snd_ymfpci_capture_rec_open(snd_pcm_substream_t * substream)
+{
+    return snd_ymfpci_capture_open(substream, 0);
+}
+
+static int snd_ymfpci_capture_ac97_open(snd_pcm_substream_t * substream)
+{
+    return snd_ymfpci_capture_open(substream, 1);
+}
+
+static int snd_ymfpci_playback_close_1(snd_pcm_substream_t * substream)
+{
+    return 0;
+}
+
+static int snd_ymfpci_playback_close(snd_pcm_substream_t * substream)
+{
+    ymfpci_t *chip = snd_pcm_substream_chip(substream);
+    ymfpci_pcm_t *ypcm = substream->runtime->private_data;
+    snd_kcontrol_t *kctl;
+
+    spin_lock_irq(&chip->reg_lock);
+    if (ypcm->output_rear && chip->rear_opened > 0) {
+        chip->rear_opened--;
+        ymfpci_close_extension(chip);
+    }
+    spin_unlock_irq(&chip->reg_lock);
+    kctl = chip->pcm_mixer[substream->number].ctl;
+    kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+    snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_INFO, &kctl->id);
+    return snd_ymfpci_playback_close_1(substream);
+}
+
+static int snd_ymfpci_playback_spdif_close(snd_pcm_substream_t * substream)
+{
+    ymfpci_t *chip = snd_pcm_substream_chip(substream);
+
+    spin_lock_irq(&chip->reg_lock);
+    chip->spdif_opened = 0;
+    ymfpci_close_extension(chip);
+    snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTCTRL,
+                      snd_ymfpci_readw(chip, YDSXGR_SPDIFOUTCTRL) & ~2);
+    snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTSTATUS, chip->spdif_bits);
+    spin_unlock_irq(&chip->reg_lock);
+    chip->spdif_pcm_ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+    snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE |
+                   SNDRV_CTL_EVENT_MASK_INFO, &chip->spdif_pcm_ctl->id);
+    return snd_ymfpci_playback_close_1(substream);
+}
+
+static int snd_ymfpci_playback_4ch_close(snd_pcm_substream_t * substream)
+{
+    ymfpci_t *chip = snd_pcm_substream_chip(substream);
+
+    spin_lock_irq(&chip->reg_lock);
+    if (chip->rear_opened > 0) {
+        chip->rear_opened--;
+        ymfpci_close_extension(chip);
+    }
+    spin_unlock_irq(&chip->reg_lock);
+    return snd_ymfpci_playback_close_1(substream);
+}
+
+static int snd_ymfpci_capture_close(snd_pcm_substream_t * substream)
+{
+    ymfpci_t *chip = snd_pcm_substream_chip(substream);
+    snd_pcm_runtime_t *runtime = substream->runtime;
+    ymfpci_pcm_t *ypcm = runtime->private_data;
+
+    if (ypcm != NULL) {
+        chip->capture_substream[ypcm->capture_bank_number] = NULL;
+        snd_ymfpci_hw_stop(chip);
+    }
+    return 0;
+}
+
+static snd_pcm_ops_t snd_ymfpci_playback_ops = {
+    snd_ymfpci_playback_open,
+    snd_ymfpci_playback_close,
+    snd_pcm_lib_ioctl,
+    snd_ymfpci_playback_hw_params,
+    snd_ymfpci_playback_hw_free,
+    snd_ymfpci_playback_prepare,
+    snd_ymfpci_playback_trigger,
+    snd_ymfpci_playback_pointer,0,0,0,0
+};
+
+static snd_pcm_ops_t snd_ymfpci_capture_rec_ops = {
+    snd_ymfpci_capture_rec_open,
+    snd_ymfpci_capture_close,
+    snd_pcm_lib_ioctl,
+    snd_ymfpci_capture_hw_params,
+    snd_ymfpci_capture_hw_free,
+    snd_ymfpci_capture_prepare,
+    snd_ymfpci_capture_trigger,
+    snd_ymfpci_capture_pointer,0,0,0,0
+};
+
+static void snd_ymfpci_pcm_free(snd_pcm_t *pcm)
+{
+    ymfpci_t *chip = pcm->private_data;
+    chip->pcm = NULL;
+    snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+int __devinit snd_ymfpci_pcm(ymfpci_t *chip, int device, snd_pcm_t ** rpcm)
+{
+	snd_pcm_t *pcm;
+	int err;
+
+	if (rpcm)
+		*rpcm = NULL;
+	if ((err = snd_pcm_new(chip->card, "YMFPCI", device, 32, 1, &pcm)) < 0)
+		return err;
+	pcm->private_data = chip;
+	pcm->private_free = snd_ymfpci_pcm_free;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ymfpci_playback_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ymfpci_capture_rec_ops);
+
+	/* global setup */
+	pcm->info_flags = 0;
+	strcpy(pcm->name, "YMFPCI");
+	chip->pcm = pcm;
+
+	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+					      snd_dma_pci_data(chip->pci), 64*1024, 256*1024);
+
+	if (rpcm)
+		*rpcm = pcm;
+	return 0;
+}
+
+
+static snd_pcm_ops_t snd_ymfpci_capture_ac97_ops = {
+    snd_ymfpci_capture_ac97_open,
+    snd_ymfpci_capture_close,
+    snd_pcm_lib_ioctl,
+    snd_ymfpci_capture_hw_params,
+    snd_ymfpci_capture_hw_free,
+    snd_ymfpci_capture_prepare,
+    snd_ymfpci_capture_trigger,
+    snd_ymfpci_capture_pointer,0,0
+};
+
+static void snd_ymfpci_pcm2_free(snd_pcm_t *pcm)
+{
+    ymfpci_t *chip = pcm->private_data;
+    chip->pcm2 = NULL;
+    snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+int __devinit snd_ymfpci_pcm2(ymfpci_t *chip, int device, snd_pcm_t ** rpcm)
+{
+	snd_pcm_t *pcm;
+	int err;
+
+	if (rpcm)
+		*rpcm = NULL;
+	if ((err = snd_pcm_new(chip->card, "YMFPCI - PCM2", device, 0, 1, &pcm)) < 0)
+		return err;
+	pcm->private_data = chip;
+	pcm->private_free = snd_ymfpci_pcm2_free;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ymfpci_capture_ac97_ops);
+
+	/* global setup */
+	pcm->info_flags = 0;
+	sprintf(pcm->name, "YMFPCI - %s",
+		chip->device_id == PCI_DEVICE_ID_YAMAHA_754 ? "Direct Recording" : "AC'97");
+	chip->pcm2 = pcm;
+
+	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+					      snd_dma_pci_data(chip->pci), 64*1024, 256*1024);
+
+	if (rpcm)
+		*rpcm = pcm;
+	return 0;
+}
+
+
+static snd_pcm_ops_t snd_ymfpci_playback_spdif_ops = {
+    snd_ymfpci_playback_spdif_open,
+    snd_ymfpci_playback_spdif_close,
+    snd_pcm_lib_ioctl,
+    snd_ymfpci_playback_hw_params,
+    snd_ymfpci_playback_hw_free,
+    snd_ymfpci_playback_prepare,
+    snd_ymfpci_playback_trigger,
+    snd_ymfpci_playback_pointer,0,0
+};
+
+static void snd_ymfpci_pcm_spdif_free(snd_pcm_t *pcm)
+{
+    ymfpci_t *chip = pcm->private_data;
+    chip->pcm_spdif = NULL;
+    snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+int __devinit snd_ymfpci_pcm_spdif(ymfpci_t *chip, int device, snd_pcm_t ** rpcm)
+{
+	snd_pcm_t *pcm;
+	int err;
+
+	if (rpcm)
+		*rpcm = NULL;
+	if ((err = snd_pcm_new(chip->card, "YMFPCI - IEC958", device, 1, 0, &pcm)) < 0)
+		return err;
+	pcm->private_data = chip;
+	pcm->private_free = snd_ymfpci_pcm_spdif_free;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ymfpci_playback_spdif_ops);
+
+	/* global setup */
+	pcm->info_flags = 0;
+	strcpy(pcm->name, "YMFPCI - IEC958");
+	chip->pcm_spdif = pcm;
+
+	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+					      snd_dma_pci_data(chip->pci), 64*1024, 256*1024);
+
+	if (rpcm)
+		*rpcm = pcm;
+	return 0;
+}
+
+static snd_pcm_ops_t snd_ymfpci_playback_4ch_ops = {
+    snd_ymfpci_playback_4ch_open,
+    snd_ymfpci_playback_4ch_close,
+    snd_pcm_lib_ioctl,
+    snd_ymfpci_playback_hw_params,
+    snd_ymfpci_playback_hw_free,
+    snd_ymfpci_playback_prepare,
+    snd_ymfpci_playback_trigger,
+    snd_ymfpci_playback_pointer,0,0,0,0
+};
+
+static void snd_ymfpci_pcm_4ch_free(snd_pcm_t *pcm)
+{
+    ymfpci_t *chip = pcm->private_data;
+    chip->pcm_4ch = NULL;
+    snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+int __devinit snd_ymfpci_pcm_4ch(ymfpci_t *chip, int device, snd_pcm_t ** rpcm)
+{
+	snd_pcm_t *pcm;
+	int err;
+
+	if (rpcm)
+		*rpcm = NULL;
+	if ((err = snd_pcm_new(chip->card, "YMFPCI - Rear", device, 1, 0, &pcm)) < 0)
+		return err;
+	pcm->private_data = chip;
+	pcm->private_free = snd_ymfpci_pcm_4ch_free;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ymfpci_playback_4ch_ops);
+
+	/* global setup */
+	pcm->info_flags = 0;
+	strcpy(pcm->name, "YMFPCI - Rear PCM");
+	chip->pcm_4ch = pcm;
+
+	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+					      snd_dma_pci_data(chip->pci), 64*1024, 256*1024);
+
+	if (rpcm)
+		*rpcm = pcm;
+	return 0;
+}
+
+
+static int snd_ymfpci_spdif_default_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+    uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+    uinfo->count = 1;
+    return 0;
+}
+
+static int snd_ymfpci_spdif_default_get(snd_kcontrol_t * kcontrol,
+                                        snd_ctl_elem_value_t * ucontrol)
+{
+    ymfpci_t *chip = snd_kcontrol_chip(kcontrol);
+
+    spin_lock_irq(&chip->reg_lock);
+    ucontrol->value.iec958.status[0] = (chip->spdif_bits >> 0) & 0xff;
+    ucontrol->value.iec958.status[1] = (chip->spdif_bits >> 8) & 0xff;
+    spin_unlock_irq(&chip->reg_lock);
+    return 0;
+}
+
+static int snd_ymfpci_spdif_default_put(snd_kcontrol_t * kcontrol,
+                                        snd_ctl_elem_value_t * ucontrol)
+{
+    ymfpci_t *chip = snd_kcontrol_chip(kcontrol);
+    unsigned int val;
+    int change;
+
+    val = ((ucontrol->value.iec958.status[0] & 0x3e) << 0) |
+        (ucontrol->value.iec958.status[1] << 8);
+    spin_lock_irq(&chip->reg_lock);
+    change = chip->spdif_bits != val;
+    chip->spdif_bits = val;
+    if ((snd_ymfpci_readw(chip, YDSXGR_SPDIFOUTCTRL) & 1) && chip->pcm_spdif == NULL)
+        snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTSTATUS, chip->spdif_bits);
+    spin_unlock_irq(&chip->reg_lock);
+    return change;
+}
+
+static struct snd_kcontrol_new snd_ymfpci_spdif_default __devinitdata =
+{
+	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
+	.name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
+	.info =		snd_ymfpci_spdif_default_info,
+	.get =		snd_ymfpci_spdif_default_get,
+	.put =		snd_ymfpci_spdif_default_put
+};
+
+static int snd_ymfpci_spdif_mask_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+    uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+    uinfo->count = 1;
+    return 0;
+}
+
+static int snd_ymfpci_spdif_mask_get(snd_kcontrol_t * kcontrol,
+                                     snd_ctl_elem_value_t * ucontrol)
+{
+    ymfpci_t *chip = snd_kcontrol_chip(kcontrol);
+
+    spin_lock_irq(&chip->reg_lock);
+    ucontrol->value.iec958.status[0] = 0x3e;
+    ucontrol->value.iec958.status[1] = 0xff;
+    spin_unlock_irq(&chip->reg_lock);
+    return 0;
+}
+
+static struct snd_kcontrol_new snd_ymfpci_spdif_mask __devinitdata =
+{
+	.access =	SNDRV_CTL_ELEM_ACCESS_READ,
+	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
+	.name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK),
+	.info =		snd_ymfpci_spdif_mask_info,
+	.get =		snd_ymfpci_spdif_mask_get,
+};
+
+
+static int snd_ymfpci_spdif_stream_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+    uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+    uinfo->count = 1;
+    return 0;
+}
+
+static int snd_ymfpci_spdif_stream_get(snd_kcontrol_t * kcontrol,
+                                       snd_ctl_elem_value_t * ucontrol)
+{
+    ymfpci_t *chip = snd_kcontrol_chip(kcontrol);
+
+    spin_lock_irq(&chip->reg_lock);
+    ucontrol->value.iec958.status[0] = (chip->spdif_pcm_bits >> 0) & 0xff;
+    ucontrol->value.iec958.status[1] = (chip->spdif_pcm_bits >> 8) & 0xff;
+    spin_unlock_irq(&chip->reg_lock);
+    return 0;
+}
+
+static int snd_ymfpci_spdif_stream_put(snd_kcontrol_t * kcontrol,
+                                       snd_ctl_elem_value_t * ucontrol)
+{
+    ymfpci_t *chip = snd_kcontrol_chip(kcontrol);
+    unsigned int val;
+    int change;
+
+    val = ((ucontrol->value.iec958.status[0] & 0x3e) << 0) |
+        (ucontrol->value.iec958.status[1] << 8);
+    spin_lock_irq(&chip->reg_lock);
+    change = chip->spdif_pcm_bits != val;
+    chip->spdif_pcm_bits = val;
+    if ((snd_ymfpci_readw(chip, YDSXGR_SPDIFOUTCTRL) & 2))
+        snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTSTATUS, chip->spdif_pcm_bits);
+    spin_unlock_irq(&chip->reg_lock);
+    return change;
+}
+
+static struct snd_kcontrol_new snd_ymfpci_spdif_stream __devinitdata =
+{
+	.access =	SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
+	.name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM),
+	.info =		snd_ymfpci_spdif_stream_info,
+	.get =		snd_ymfpci_spdif_stream_get,
+	.put =		snd_ymfpci_spdif_stream_put
+};
+
+
+static int snd_ymfpci_drec_source_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *info)
+{
+    static char *texts[3] = {"AC'97", "IEC958", "ZV Port"};
+
+    info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+    info->count = 1;
+    info->value.enumerated.items = 3;
+    if (info->value.enumerated.item > 2)
+        info->value.enumerated.item = 2;
+    strcpy(info->value.enumerated.name, texts[info->value.enumerated.item]);
+    return 0;
+}
+
+static int snd_ymfpci_drec_source_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *value)
+{
+    ymfpci_t *chip = snd_kcontrol_chip(kcontrol);
+    u16 reg;
+
+    spin_lock_irq(&chip->reg_lock);
+    reg = snd_ymfpci_readw(chip, YDSXGR_GLOBALCTRL);
+    spin_unlock_irq(&chip->reg_lock);
+    if (!(reg & 0x100))
+        value->value.enumerated.item[0] = 0;
+    else
+        value->value.enumerated.item[0] = 1 + ((reg & 0x200) != 0);
+    return 0;
+}
+
+static int snd_ymfpci_drec_source_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *value)
+{
+    ymfpci_t *chip = snd_kcontrol_chip(kcontrol);
+    u16 reg, old_reg;
+
+    spin_lock_irq(&chip->reg_lock);
+    old_reg = snd_ymfpci_readw(chip, YDSXGR_GLOBALCTRL);
+    if (value->value.enumerated.item[0] == 0)
+        reg = old_reg & ~0x100;
+    else
+        reg = (old_reg & ~0x300) | 0x100 | ((value->value.enumerated.item[0] == 2) << 9);
+    snd_ymfpci_writew(chip, YDSXGR_GLOBALCTRL, reg);
+    spin_unlock_irq(&chip->reg_lock);
+    return reg != old_reg;
+}
+
+static struct snd_kcontrol_new snd_ymfpci_drec_source __devinitdata = {
+	.access =	SNDRV_CTL_ELEM_ACCESS_READWRITE,
+	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name =		"Direct Recording Source",
+	.info =		snd_ymfpci_drec_source_info,
+	.get =		snd_ymfpci_drec_source_get,
+	.put =		snd_ymfpci_drec_source_put
+};
+
+/*
+ *  Mixer controls
+ */
+
+#define YMFPCI_SINGLE(xname, xindex, reg, shift) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+  .info = snd_ymfpci_info_single, \
+  .get = snd_ymfpci_get_single, .put = snd_ymfpci_put_single, \
+  .private_value = ((reg) | ((shift) << 16)) }
+
+#if 0
+#define YMFPCI_SINGLE(xname, xindex, reg) \
+    { SNDRV_CTL_ELEM_IFACE_MIXER, 0,0, xname, xindex, \
+    0, 0, snd_ymfpci_info_single, \
+    snd_ymfpci_get_single, snd_ymfpci_put_single, \
+    reg }
+
+#endif
+static int snd_ymfpci_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+    unsigned int mask = 1;
+
+    switch (kcontrol->private_value) {
+    case YDSXGR_SPDIFOUTCTRL: break;
+    case YDSXGR_SPDIFINCTRL: break;
+    default: return -EINVAL;
+    }
+    uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+    uinfo->count = 1;
+    uinfo->value.integer.min = 0;
+    uinfo->value.integer.max = mask;
+    return 0;
+}
+
+static int snd_ymfpci_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+    ymfpci_t *chip = snd_kcontrol_chip(kcontrol);
+    int reg = kcontrol->private_value;
+    unsigned int shift = 0, mask = 1, invert = 0;
+
+    switch (kcontrol->private_value) {
+    case YDSXGR_SPDIFOUTCTRL: break;
+    case YDSXGR_SPDIFINCTRL: break;
+    default: return -EINVAL;
+    }
+    ucontrol->value.integer.value[0] = (snd_ymfpci_readl(chip, reg) >> shift) & mask;
+    if (invert)
+        ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+    return 0;
+}
+
+static int snd_ymfpci_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+    ymfpci_t *chip = snd_kcontrol_chip(kcontrol);
+    int reg = kcontrol->private_value;
+    unsigned int shift = 0, mask = 1, invert = 0;
+    int change;
+    unsigned int val, oval;
+
+    switch (kcontrol->private_value) {
+    case YDSXGR_SPDIFOUTCTRL: break;
+    case YDSXGR_SPDIFINCTRL: break;
+    default: return -EINVAL;
+    }
+    val = (ucontrol->value.integer.value[0] & mask);
+    if (invert)
+        val = mask - val;
+    val <<= shift;
+    spin_lock_irq(&chip->reg_lock);
+    oval = snd_ymfpci_readl(chip, reg);
+    val = (oval & ~(mask << shift)) | val;
+    change = val != oval;
+    snd_ymfpci_writel(chip, reg, val);
+    spin_unlock_irq(&chip->reg_lock);
+    return change;
+}
+
+#define YMFPCI_DOUBLE(xname, xindex, reg) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+  .info = snd_ymfpci_info_double, \
+  .get = snd_ymfpci_get_double, .put = snd_ymfpci_put_double, \
+  .private_value = reg }
+
+static int snd_ymfpci_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+    unsigned int reg = kcontrol->private_value;
+    unsigned int mask = 16383;
+
+    if (reg < 0x80 || reg >= 0xc0)
+        return -EINVAL;
+    uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+    uinfo->count = 2;
+    uinfo->value.integer.min = 0;
+    uinfo->value.integer.max = mask;
+    return 0;
+}
+
+static int snd_ymfpci_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+    ymfpci_t *chip = snd_kcontrol_chip(kcontrol);
+    unsigned int reg = kcontrol->private_value;
+    unsigned int shift_left = 0, shift_right = 16, mask = 16383, invert = 0;
+    unsigned int val;
+
+    if (reg < 0x80 || reg >= 0xc0)
+        return -EINVAL;
+    spin_lock_irq(&chip->reg_lock);
+    val = snd_ymfpci_readl(chip, reg);
+    spin_unlock_irq(&chip->reg_lock);
+    ucontrol->value.integer.value[0] = (val >> shift_left) & mask;
+    ucontrol->value.integer.value[1] = (val >> shift_right) & mask;
+    if (invert) {
+        ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+        ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1];
+    }
+    return 0;
+}
+
+static int snd_ymfpci_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+    ymfpci_t *chip = snd_kcontrol_chip(kcontrol);
+    unsigned int reg = kcontrol->private_value;
+    unsigned int shift_left = 0, shift_right = 16, mask = 16383, invert = 0;
+    int change;
+    unsigned int val1, val2, oval;
+
+    if (reg < 0x80 || reg >= 0xc0)
+        return -EINVAL;
+    val1 = ucontrol->value.integer.value[0] & mask;
+    val2 = ucontrol->value.integer.value[1] & mask;
+    if (invert) {
+        val1 = mask - val1;
+        val2 = mask - val2;
+    }
+    val1 <<= shift_left;
+    val2 <<= shift_right;
+    spin_lock_irq(&chip->reg_lock);
+    oval = snd_ymfpci_readl(chip, reg);
+    val1 = (oval & ~((mask << shift_left) | (mask << shift_right))) | val1 | val2;
+    change = val1 != oval;
+    snd_ymfpci_writel(chip, reg, val1);
+    spin_unlock_irq(&chip->reg_lock);
+    return change;
+}
+
+/*
+ * 4ch duplication
+ */
+static int snd_ymfpci_info_dup4ch(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_ymfpci_get_dup4ch(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+    ymfpci_t *chip = snd_kcontrol_chip(kcontrol);
+    ucontrol->value.integer.value[0] = chip->mode_dup4ch;
+    return 0;
+}
+
+static int snd_ymfpci_put_dup4ch(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+    ymfpci_t *chip = snd_kcontrol_chip(kcontrol);
+    int change;
+    change = (ucontrol->value.integer.value[0] != chip->mode_dup4ch);
+    if (change)
+        chip->mode_dup4ch = !!ucontrol->value.integer.value[0];
+    return change;
+}
+
+static struct snd_kcontrol_new snd_ymfpci_controls[] __devinitdata = {
+YMFPCI_DOUBLE("Wave Playback Volume", 0, YDSXGR_NATIVEDACOUTVOL),
+YMFPCI_DOUBLE("Wave Capture Volume", 0, YDSXGR_NATIVEDACLOOPVOL),
+YMFPCI_DOUBLE("Digital Capture Volume", 0, YDSXGR_NATIVEDACINVOL),
+YMFPCI_DOUBLE("Digital Capture Volume", 1, YDSXGR_NATIVEADCINVOL),
+YMFPCI_DOUBLE("ADC Playback Volume", 0, YDSXGR_PRIADCOUTVOL),
+YMFPCI_DOUBLE("ADC Capture Volume", 0, YDSXGR_PRIADCLOOPVOL),
+YMFPCI_DOUBLE("ADC Playback Volume", 1, YDSXGR_SECADCOUTVOL),
+YMFPCI_DOUBLE("ADC Capture Volume", 1, YDSXGR_SECADCLOOPVOL),
+YMFPCI_DOUBLE("FM Legacy Volume", 0, YDSXGR_LEGACYOUTVOL),
+YMFPCI_DOUBLE(SNDRV_CTL_NAME_IEC958("AC97 ", PLAYBACK,VOLUME), 0, YDSXGR_ZVOUTVOL),
+YMFPCI_DOUBLE(SNDRV_CTL_NAME_IEC958("", CAPTURE,VOLUME), 0, YDSXGR_ZVLOOPVOL),
+YMFPCI_DOUBLE(SNDRV_CTL_NAME_IEC958("AC97 ",PLAYBACK,VOLUME), 1, YDSXGR_SPDIFOUTVOL),
+YMFPCI_DOUBLE(SNDRV_CTL_NAME_IEC958("",CAPTURE,VOLUME), 1, YDSXGR_SPDIFLOOPVOL),
+YMFPCI_SINGLE(SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH), 0, YDSXGR_SPDIFOUTCTRL, 0),
+YMFPCI_SINGLE(SNDRV_CTL_NAME_IEC958("",CAPTURE,SWITCH), 0, YDSXGR_SPDIFINCTRL, 0),
+YMFPCI_SINGLE(SNDRV_CTL_NAME_IEC958("Loop",NONE,NONE), 0, YDSXGR_SPDIFINCTRL, 4),
+{
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "4ch Duplication",
+	.info = snd_ymfpci_info_dup4ch,
+	.get = snd_ymfpci_get_dup4ch,
+	.put = snd_ymfpci_put_dup4ch,
+},
+};
+
+
+/*
+ * GPIO
+ */
+
+static int snd_ymfpci_get_gpio_out(ymfpci_t *chip, int pin)
+{
+    u16 reg, mode;
+    unsigned long flags;
+
+    spin_lock_irqsave(&chip->reg_lock, flags);
+    reg = snd_ymfpci_readw(chip, YDSXGR_GPIOFUNCENABLE);
+    reg &= ~(1 << (pin + 8));
+    reg |= (1 << pin);
+    snd_ymfpci_writew(chip, YDSXGR_GPIOFUNCENABLE, reg);
+    /* set the level mode for input line */
+    mode = snd_ymfpci_readw(chip, YDSXGR_GPIOTYPECONFIG);
+    mode &= ~(3 << (pin * 2));
+    snd_ymfpci_writew(chip, YDSXGR_GPIOTYPECONFIG, mode);
+    snd_ymfpci_writew(chip, YDSXGR_GPIOFUNCENABLE, reg | (1 << (pin + 8)));
+    mode = snd_ymfpci_readw(chip, YDSXGR_GPIOINSTATUS);
+    spin_unlock_irqrestore(&chip->reg_lock, flags);
+    return (mode >> pin) & 1;
+}
+
+static int snd_ymfpci_set_gpio_out(ymfpci_t *chip, int pin, int enable)
+{
+    u16 reg;
+    unsigned long flags;
+
+    spin_lock_irqsave(&chip->reg_lock, flags);
+    reg = snd_ymfpci_readw(chip, YDSXGR_GPIOFUNCENABLE);
+    reg &= ~(1 << pin);
+    reg &= ~(1 << (pin + 8));
+    snd_ymfpci_writew(chip, YDSXGR_GPIOFUNCENABLE, reg);
+    snd_ymfpci_writew(chip, YDSXGR_GPIOOUTCTRL, enable << pin);
+    snd_ymfpci_writew(chip, YDSXGR_GPIOFUNCENABLE, reg | (1 << (pin + 8)));
+    spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+    return 0;
+}
+
+static int snd_ymfpci_gpio_sw_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_ymfpci_gpio_sw_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+    ymfpci_t *chip = snd_kcontrol_chip(kcontrol);
+    int pin = (int)kcontrol->private_value;
+    ucontrol->value.integer.value[0] = snd_ymfpci_get_gpio_out(chip, pin);
+    return 0;
+}
+
+static int snd_ymfpci_gpio_sw_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+    ymfpci_t *chip = snd_kcontrol_chip(kcontrol);
+    int pin = (int)kcontrol->private_value;
+
+    if (snd_ymfpci_get_gpio_out(chip, pin) != ucontrol->value.integer.value[0]) {
+        snd_ymfpci_set_gpio_out(chip, pin, !!ucontrol->value.integer.value[0]);
+        ucontrol->value.integer.value[0] = snd_ymfpci_get_gpio_out(chip, pin);
+        return 1;
+    }
+    return 0;
+}
+
+static struct snd_kcontrol_new snd_ymfpci_rear_shared __devinitdata = {
+	.name = "Shared Rear/Line-In Switch",
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.info = snd_ymfpci_gpio_sw_info,
+	.get = snd_ymfpci_gpio_sw_get,
+	.put = snd_ymfpci_gpio_sw_put,
+	.private_value = 2,
+};
+
+/*
+ * PCM voice volume
+ */
+
+static int snd_ymfpci_pcm_vol_info(snd_kcontrol_t *kcontrol,
+                                   snd_ctl_elem_info_t *uinfo)
+{
+    uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+    uinfo->count = 2;
+    uinfo->value.integer.min = 0;
+    uinfo->value.integer.max = 0x8000;
+    return 0;
+}
+
+static int snd_ymfpci_pcm_vol_get(snd_kcontrol_t *kcontrol,
+                                  snd_ctl_elem_value_t *ucontrol)
+{
+    ymfpci_t *chip = snd_kcontrol_chip(kcontrol);
+    unsigned int subs = kcontrol->id.subdevice;
+
+    ucontrol->value.integer.value[0] = chip->pcm_mixer[subs].left;
+    ucontrol->value.integer.value[1] = chip->pcm_mixer[subs].right;
+    return 0;
+}
+
+static int snd_ymfpci_pcm_vol_put(snd_kcontrol_t *kcontrol,
+                                  snd_ctl_elem_value_t *ucontrol)
+{
+    ymfpci_t *chip = snd_kcontrol_chip(kcontrol);
+    unsigned int subs = kcontrol->id.subdevice;
+    snd_pcm_substream_t *substream;
+    unsigned long flags;
+
+    if (ucontrol->value.integer.value[0] != chip->pcm_mixer[subs].left ||
+        ucontrol->value.integer.value[1] != chip->pcm_mixer[subs].right) {
+        chip->pcm_mixer[subs].left = ucontrol->value.integer.value[0];
+        chip->pcm_mixer[subs].right = ucontrol->value.integer.value[1];
+
+        substream = (snd_pcm_substream_t *)kcontrol->private_value;
+        spin_lock_irqsave(&chip->voice_lock, flags);
+        if (substream->runtime && substream->runtime->private_data) {
+            ymfpci_pcm_t *ypcm = substream->runtime->private_data;
+            ypcm->update_pcm_vol = 2;
+        }
+        spin_unlock_irqrestore(&chip->voice_lock, flags);
+        return 1;
+    }
+    return 0;
+}
+
+static snd_kcontrol_new_t snd_ymfpci_pcm_volume __devinitdata = {
+    .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+    .name = "PCM Playback Volume",
+    .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+    SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+    .info = snd_ymfpci_pcm_vol_info,
+    .get = snd_ymfpci_pcm_vol_get,
+    .put = snd_ymfpci_pcm_vol_put,
+};
+
+/*
+ *  Mixer routines
+ */
+
+static void snd_ymfpci_mixer_free_ac97_bus(ac97_bus_t *bus)
+{
+    ymfpci_t *chip = bus->private_data;
+    chip->ac97_bus = NULL;
+}
+
+static void snd_ymfpci_mixer_free_ac97(ac97_t *ac97)
+{
+    ymfpci_t *chip = ac97->private_data;
+    chip->ac97 = NULL;
+}
+
+int __devinit snd_ymfpci_mixer(ymfpci_t *chip, int rear_switch)
+{
+    ac97_template_t ac97;
+    snd_kcontrol_t *kctl;
+    snd_pcm_substream_t *substream;
+    unsigned int idx;
+    int err;
+
+    static struct snd_ac97_bus_ops ops = {
+        .write = snd_ymfpci_codec_write,
+        .read = snd_ymfpci_codec_read,
+    };
+    if ((err = snd_ac97_bus(chip->card, 0, &ops, chip, &chip->ac97_bus)) < 0)
+        return err;
+    chip->ac97_bus->private_free = snd_ymfpci_mixer_free_ac97_bus;
+
+    memset(&ac97, 0, sizeof(ac97));
+    ac97.private_data = chip;
+    ac97.private_free = snd_ymfpci_mixer_free_ac97;
+    if ((err = snd_ac97_mixer(chip->ac97_bus, &ac97, &chip->ac97)) < 0)
+        return err;
+
+    /* to be sure */
+    snd_ac97_update_bits(chip->ac97, AC97_EXTENDED_STATUS,
+                         AC97_EA_VRA|AC97_EA_VRM, 0);
+
+    for (idx = 0; idx < ARRAY_SIZE(snd_ymfpci_controls); idx++) {
+        if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_ymfpci_controls[idx], chip))) < 0)
+            return err;
+    }
+
+    /* add S/PDIF control */
+    snd_assert(chip->pcm_spdif != NULL, return -EIO);
+    if ((err = snd_ctl_add(chip->card, kctl = snd_ctl_new1(&snd_ymfpci_spdif_default, chip))) < 0)
+        return err;
+    kctl->id.device = chip->pcm_spdif->device;
+    if ((err = snd_ctl_add(chip->card, kctl = snd_ctl_new1(&snd_ymfpci_spdif_mask, chip))) < 0)
+        return err;
+    kctl->id.device = chip->pcm_spdif->device;
+    if ((err = snd_ctl_add(chip->card, kctl = snd_ctl_new1(&snd_ymfpci_spdif_stream, chip))) < 0)
+        return err;
+    kctl->id.device = chip->pcm_spdif->device;
+    chip->spdif_pcm_ctl = kctl;
+
+    /* direct recording source */
+    if (chip->device_id == PCI_DEVICE_ID_YAMAHA_754 &&
+        (err = snd_ctl_add(chip->card, kctl = snd_ctl_new1(&snd_ymfpci_drec_source, chip))) < 0)
+        return err;
+
+    /*
+     * shared rear/line-in
+     */
+    if (rear_switch) {
+        if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_ymfpci_rear_shared, chip))) < 0)
+            return err;
+    }
+    /* per-voice volume */
+    substream = chip->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+    for (idx = 0; idx < 32; ++idx) {
+        kctl = snd_ctl_new1(&snd_ymfpci_pcm_volume, chip);
+        if (!kctl)
+            return -ENOMEM;
+        kctl->id.device = chip->pcm->device;
+        kctl->id.subdevice = idx;
+        kctl->private_value = (unsigned long)substream;
+        if ((err = snd_ctl_add(chip->card, kctl)) < 0)
+            return err;
+        chip->pcm_mixer[idx].left = 0x8000;
+        chip->pcm_mixer[idx].right = 0x8000;
+        chip->pcm_mixer[idx].ctl = kctl;
+        substream = substream->next;
+    }
+
+    return 0;
+}
+
+/*
+ * timer
+ */
+
+static int snd_ymfpci_timer_start(snd_timer_t *timer)
+{
+    ymfpci_t *chip;
+    unsigned long flags;
+    unsigned int count;
+
+    chip = snd_timer_chip(timer);
+    count = (timer->sticks << 1) - 1;
+    spin_lock_irqsave(&chip->reg_lock, flags);
+    snd_ymfpci_writew(chip, YDSXGR_TIMERCOUNT, count);
+    snd_ymfpci_writeb(chip, YDSXGR_TIMERCTRL, 0x03);
+    spin_unlock_irqrestore(&chip->reg_lock, flags);
+    return 0;
+}
+
+static int snd_ymfpci_timer_stop(snd_timer_t *timer)
+{
+    ymfpci_t *chip;
+    unsigned long flags;
+
+    chip = snd_timer_chip(timer);
+    spin_lock_irqsave(&chip->reg_lock, flags);
+    snd_ymfpci_writeb(chip, YDSXGR_TIMERCTRL, 0x00);
+    spin_unlock_irqrestore(&chip->reg_lock, flags);
+    return 0;
+}
+
+static int snd_ymfpci_timer_precise_resolution(snd_timer_t *timer,
+                                               unsigned long *num, unsigned long *den)
+{
+    *num = 1;
+    *den = 48000;
+    return 0;
+}
+
+static struct snd_timer_hardware snd_ymfpci_timer_hw = {
+	.flags = SNDRV_TIMER_HW_AUTO,
+	.resolution = 20833, /* 1/fs = 20.8333...us */
+	.ticks = 0x8000,
+	.start = snd_ymfpci_timer_start,
+	.stop = snd_ymfpci_timer_stop,
+	.precise_resolution = snd_ymfpci_timer_precise_resolution,
+};
+
+int __devinit snd_ymfpci_timer(ymfpci_t *chip, int device)
+{
+    snd_timer_t *timer = NULL;
+    snd_timer_id_t tid;
+    int err;
+
+    tid.dev_class = SNDRV_TIMER_CLASS_CARD;
+    tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE;
+    tid.card = chip->card->number;
+    tid.device = device;
+    tid.subdevice = 0;
+    if ((err = snd_timer_new(chip->card, "YMFPCI", &tid, &timer)) >= 0) {
+        strcpy(timer->name, "YMFPCI timer");
+        timer->private_data = chip;
+        timer->hw = snd_ymfpci_timer_hw;
+    }
+    chip->timer = timer;
+    return err;
+}
+
+
+
+/*
+ *  proc interface
+ */
+
+static void snd_ymfpci_proc_read(snd_info_entry_t *entry,
+                                 snd_info_buffer_t * buffer)
+{
+    ymfpci_t *chip = entry->private_data;
+    int i;
+
+    snd_iprintf(buffer, "YMFPCI\n\n");
+    for (i = 0; i <= YDSXGR_WORKBASE; i += 4)
+        snd_iprintf(buffer, "%04x: %04x\n", i, snd_ymfpci_readl(chip, i));
+}
+
+static int __devinit snd_ymfpci_proc_init(snd_card_t * card, ymfpci_t *chip)
+{
+    snd_info_entry_t *entry;
+
+    if (! snd_card_proc_new(card, "ymfpci", &entry))
+        snd_info_set_text_ops(entry, chip, 1024, snd_ymfpci_proc_read);
+    return 0;
+}
+
+/*
+ *  initialization routines
+ */
+
+static void snd_ymfpci_aclink_reset(struct pci_dev * pci)
+{
+    u8 cmd;
+
+    pci_read_config_byte(pci, PCIR_DSXG_CTRL, &cmd);
+#if 0 // force to reset
+    if (cmd & 0x03) {
+#endif
+        pci_write_config_byte(pci, PCIR_DSXG_CTRL, cmd & 0xfc);
+        pci_write_config_byte(pci, PCIR_DSXG_CTRL, cmd | 0x03);
+        pci_write_config_byte(pci, PCIR_DSXG_CTRL, cmd & 0xfc);
+        pci_write_config_word(pci, PCIR_DSXG_PWRCTRL1, 0);
+        pci_write_config_word(pci, PCIR_DSXG_PWRCTRL2, 0);
+#if 0
+    }
+#endif
+}
+
+static void snd_ymfpci_enable_dsp(ymfpci_t *chip)
+{
+    snd_ymfpci_writel(chip, YDSXGR_CONFIG, 0x00000001);
+}
+
+static void snd_ymfpci_disable_dsp(ymfpci_t *chip)
+{
+    u32 val;
+    int timeout = 1000;
+
+    val = snd_ymfpci_readl(chip, YDSXGR_CONFIG);
+    if (val)
+        snd_ymfpci_writel(chip, YDSXGR_CONFIG, 0x00000000);
+    while (timeout-- > 0) {
+        val = snd_ymfpci_readl(chip, YDSXGR_STATUS);
+        if ((val & 0x00000002) == 0)
+            break;
+    }
+}
+
+#include "ymfpci_image.h"
+
+static void snd_ymfpci_download_image(ymfpci_t *chip)
+{
+    int i;
+    u16 ctrl;
+    unsigned long *inst;
+
+    snd_ymfpci_writel(chip, YDSXGR_NATIVEDACOUTVOL, 0x00000000);
+    snd_ymfpci_disable_dsp(chip);
+    snd_ymfpci_writel(chip, YDSXGR_MODE, 0x00010000);
+    snd_ymfpci_writel(chip, YDSXGR_MODE, 0x00000000);
+    snd_ymfpci_writel(chip, YDSXGR_MAPOFREC, 0x00000000);
+    snd_ymfpci_writel(chip, YDSXGR_MAPOFEFFECT, 0x00000000);
+    snd_ymfpci_writel(chip, YDSXGR_PLAYCTRLBASE, 0x00000000);
+    snd_ymfpci_writel(chip, YDSXGR_RECCTRLBASE, 0x00000000);
+    snd_ymfpci_writel(chip, YDSXGR_EFFCTRLBASE, 0x00000000);
+    ctrl = snd_ymfpci_readw(chip, YDSXGR_GLOBALCTRL);
+    snd_ymfpci_writew(chip, YDSXGR_GLOBALCTRL, ctrl & ~0x0007);
+
+    /* setup DSP instruction code */
+    for (i = 0; i < YDSXG_DSPLENGTH / 4; i++)
+        snd_ymfpci_writel(chip, YDSXGR_DSPINSTRAM + (i << 2), DspInst[i]);
+
+    /* setup control instruction code */
+    switch (chip->device_id) {
+    case PCI_DEVICE_ID_YAMAHA_724F:
+    case PCI_DEVICE_ID_YAMAHA_740C:
+    case PCI_DEVICE_ID_YAMAHA_744:
+    case PCI_DEVICE_ID_YAMAHA_754:
+        inst = CntrlInst1E;
+        break;
+    default:
+        inst = CntrlInst;
+        break;
+    }
+    for (i = 0; i < YDSXG_CTRLLENGTH / 4; i++)
+        snd_ymfpci_writel(chip, YDSXGR_CTRLINSTRAM + (i << 2), inst[i]);
+
+    snd_ymfpci_enable_dsp(chip);
+}
+
+static int __devinit snd_ymfpci_memalloc(ymfpci_t *chip)
+{
+	long size, playback_ctrl_size;
+	int voice, bank, reg;
+	u8 *ptr;
+	dma_addr_t ptr_addr;
+
+	playback_ctrl_size = 4 + 4 * YDSXG_PLAYBACK_VOICES;
+	chip->bank_size_playback = snd_ymfpci_readl(chip, YDSXGR_PLAYCTRLSIZE) << 2;
+	chip->bank_size_capture = snd_ymfpci_readl(chip, YDSXGR_RECCTRLSIZE) << 2;
+	chip->bank_size_effect = snd_ymfpci_readl(chip, YDSXGR_EFFCTRLSIZE) << 2;
+	chip->work_size = YDSXG_DEFAULT_WORK_SIZE;
+
+	size = ((playback_ctrl_size + 0x00ff) & ~0x00ff) +
+	       ((chip->bank_size_playback * 2 * YDSXG_PLAYBACK_VOICES + 0x00ff) & ~0x00ff) +
+	       ((chip->bank_size_capture * 2 * YDSXG_CAPTURE_VOICES + 0x00ff) & ~0x00ff) +
+	       ((chip->bank_size_effect * 2 * YDSXG_EFFECT_VOICES + 0x00ff) & ~0x00ff) +
+	       chip->work_size;
+	/* work_ptr must be aligned to 256 bytes, but it's already
+	   covered with the kernel page allocation mechanism */
+	if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci),
+				size, &chip->work_ptr) < 0)
+		return -ENOMEM;
+	ptr = chip->work_ptr.area;
+	ptr_addr = chip->work_ptr.addr;
+	memset(ptr, 0, size);	/* for sure */
+
+	chip->bank_base_playback = ptr;
+	chip->bank_base_playback_addr = ptr_addr;
+	chip->ctrl_playback = (u32 *)ptr;
+	chip->ctrl_playback[0] = cpu_to_le32(YDSXG_PLAYBACK_VOICES);
+	ptr += (playback_ctrl_size + 0x00ff) & ~0x00ff;
+	ptr_addr += (playback_ctrl_size + 0x00ff) & ~0x00ff;
+	for (voice = 0; voice < YDSXG_PLAYBACK_VOICES; voice++) {
+		chip->voices[voice].number = voice;
+		chip->voices[voice].bank = (snd_ymfpci_playback_bank_t *)ptr;
+		chip->voices[voice].bank_addr = ptr_addr;
+		for (bank = 0; bank < 2; bank++) {
+			chip->bank_playback[voice][bank] = (snd_ymfpci_playback_bank_t *)ptr;
+			ptr += chip->bank_size_playback;
+			ptr_addr += chip->bank_size_playback;
+		}
+	}
+	ptr = (char *)(((unsigned long)ptr + 0x00ff) & ~0x00ff);
+	ptr_addr = (ptr_addr + 0x00ff) & ~0x00ff;
+	chip->bank_base_capture = ptr;
+	chip->bank_base_capture_addr = ptr_addr;
+	for (voice = 0; voice < YDSXG_CAPTURE_VOICES; voice++)
+		for (bank = 0; bank < 2; bank++) {
+			chip->bank_capture[voice][bank] = (snd_ymfpci_capture_bank_t *)ptr;
+			ptr += chip->bank_size_capture;
+			ptr_addr += chip->bank_size_capture;
+		}
+	ptr = (char *)(((unsigned long)ptr + 0x00ff) & ~0x00ff);
+	ptr_addr = (ptr_addr + 0x00ff) & ~0x00ff;
+	chip->bank_base_effect = ptr;
+	chip->bank_base_effect_addr = ptr_addr;
+	for (voice = 0; voice < YDSXG_EFFECT_VOICES; voice++)
+		for (bank = 0; bank < 2; bank++) {
+			chip->bank_effect[voice][bank] = (snd_ymfpci_effect_bank_t *)ptr;
+			ptr += chip->bank_size_effect;
+			ptr_addr += chip->bank_size_effect;
+		}
+	ptr = (char *)(((unsigned long)ptr + 0x00ff) & ~0x00ff);
+	ptr_addr = (ptr_addr + 0x00ff) & ~0x00ff;
+	chip->work_base = ptr;
+	chip->work_base_addr = ptr_addr;
+
+	snd_assert(ptr + chip->work_size == chip->work_ptr.area + chip->work_ptr.bytes, );
+
+	snd_ymfpci_writel(chip, YDSXGR_PLAYCTRLBASE, chip->bank_base_playback_addr);
+	snd_ymfpci_writel(chip, YDSXGR_RECCTRLBASE, chip->bank_base_capture_addr);
+	snd_ymfpci_writel(chip, YDSXGR_EFFCTRLBASE, chip->bank_base_effect_addr);
+	snd_ymfpci_writel(chip, YDSXGR_WORKBASE, chip->work_base_addr);
+	snd_ymfpci_writel(chip, YDSXGR_WORKSIZE, chip->work_size >> 2);
+
+	/* S/PDIF output initialization */
+	chip->spdif_bits = chip->spdif_pcm_bits = SNDRV_PCM_DEFAULT_CON_SPDIF & 0xffff;
+	snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTCTRL, 0);
+	snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTSTATUS, chip->spdif_bits);
+
+	/* S/PDIF input initialization */
+	snd_ymfpci_writew(chip, YDSXGR_SPDIFINCTRL, 0);
+
+	/* digital mixer setup */
+	for (reg = 0x80; reg < 0xc0; reg += 4)
+		snd_ymfpci_writel(chip, reg, 0);
+	snd_ymfpci_writel(chip, YDSXGR_NATIVEDACOUTVOL, 0x3fff3fff);
+	snd_ymfpci_writel(chip, YDSXGR_ZVOUTVOL, 0x3fff3fff);
+	snd_ymfpci_writel(chip, YDSXGR_SPDIFOUTVOL, 0x3fff3fff);
+	snd_ymfpci_writel(chip, YDSXGR_NATIVEADCINVOL, 0x3fff3fff);
+	snd_ymfpci_writel(chip, YDSXGR_NATIVEDACINVOL, 0x3fff3fff);
+	snd_ymfpci_writel(chip, YDSXGR_PRIADCLOOPVOL, 0x3fff3fff);
+	snd_ymfpci_writel(chip, YDSXGR_LEGACYOUTVOL, 0x3fff3fff);
+
+	return 0;
+}
+
+static int snd_ymfpci_free(ymfpci_t *chip)
+{
+	u16 ctrl;
+
+	snd_assert(chip != NULL, return -EINVAL);
+
+	if (chip->res_reg_area) {	/* don't touch busy hardware */
+		snd_ymfpci_writel(chip, YDSXGR_NATIVEDACOUTVOL, 0);
+		snd_ymfpci_writel(chip, YDSXGR_BUF441OUTVOL, 0);
+		snd_ymfpci_writel(chip, YDSXGR_LEGACYOUTVOL, 0);
+		snd_ymfpci_writel(chip, YDSXGR_STATUS, ~0);
+		snd_ymfpci_disable_dsp(chip);
+		snd_ymfpci_writel(chip, YDSXGR_PLAYCTRLBASE, 0);
+		snd_ymfpci_writel(chip, YDSXGR_RECCTRLBASE, 0);
+		snd_ymfpci_writel(chip, YDSXGR_EFFCTRLBASE, 0);
+		snd_ymfpci_writel(chip, YDSXGR_WORKBASE, 0);
+		snd_ymfpci_writel(chip, YDSXGR_WORKSIZE, 0);
+		ctrl = snd_ymfpci_readw(chip, YDSXGR_GLOBALCTRL);
+		snd_ymfpci_writew(chip, YDSXGR_GLOBALCTRL, ctrl & ~0x0007);
+	}
+
+	snd_ymfpci_ac3_done(chip);
+
+	/* Set PCI device to D3 state */
+#if 0
+	/* FIXME: temporarily disabled, otherwise we cannot fire up
+	 * the chip again unless reboot.  ACPI bug?
+	 */
+	pci_set_power_state(chip->pci, 3);
+#endif
+
+#ifdef CONFIG_PM
+	if (chip->saved_regs)
+		vfree(chip->saved_regs);
+#endif
+	if (chip->mpu_res) {
+		release_resource(chip->mpu_res);
+		kfree_nocheck(chip->mpu_res);
+	}
+	if (chip->fm_res) {
+		release_resource(chip->fm_res);
+		kfree_nocheck(chip->fm_res);
+	}
+#ifdef SUPPORT_JOYSTICK
+	if (chip->joystick_res) {
+		if (chip->gameport.io)
+			gameport_unregister_port(&chip->gameport);
+		release_resource(chip->joystick_res);
+		kfree_nocheck(chip->joystick_res);
+	}
+#endif
+	if (chip->reg_area_virt)
+		iounmap((void *)chip->reg_area_virt);
+	if (chip->work_ptr.area)
+		snd_dma_free_pages(&chip->work_ptr);
+
+	if (chip->irq >= 0)
+		free_irq(chip->irq, (void *)chip);
+	if (chip->res_reg_area) {
+		release_resource(chip->res_reg_area);
+		kfree_nocheck(chip->res_reg_area);
+	}
+
+	pci_write_config_word(chip->pci, 0x40, chip->old_legacy_ctrl);
+
+	kfree(chip);
+	return 0;
+}
+
+
+static int snd_ymfpci_dev_free(snd_device_t *device)
+{
+    ymfpci_t *chip = device->device_data;
+    return snd_ymfpci_free(chip);
+}
+
+#ifdef CONFIG_PM
+static int saved_regs_index[] = {
+    /* spdif */
+    YDSXGR_SPDIFOUTCTRL,
+    YDSXGR_SPDIFOUTSTATUS,
+    YDSXGR_SPDIFINCTRL,
+    /* volumes */
+    YDSXGR_PRIADCLOOPVOL,
+    YDSXGR_NATIVEDACINVOL,
+    YDSXGR_NATIVEDACOUTVOL,
+    // YDSXGR_BUF441OUTVOL,
+    YDSXGR_NATIVEADCINVOL,
+    YDSXGR_SPDIFLOOPVOL,
+    YDSXGR_SPDIFOUTVOL,
+    YDSXGR_ZVOUTVOL,
+    YDSXGR_LEGACYOUTVOL,
+    /* address bases */
+    YDSXGR_PLAYCTRLBASE,
+    YDSXGR_RECCTRLBASE,
+    YDSXGR_EFFCTRLBASE,
+    YDSXGR_WORKBASE,
+    /* capture set up */
+    YDSXGR_MAPOFREC,
+    YDSXGR_RECFORMAT,
+    YDSXGR_RECSLOTSR,
+    YDSXGR_ADCFORMAT,
+    YDSXGR_ADCSLOTSR,
+};
+#define YDSXGR_NUM_SAVED_REGS	ARRAY_SIZE(saved_regs_index)
+
+int snd_ymfpci_suspend(struct pci_dev *pci, pm_message_t state)
+{
+    struct snd_card *card = pci_get_drvdata(pci);
+    struct snd_ymfpci *chip = card->private_data;
+    unsigned int i;
+
+    snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+    snd_pcm_suspend_all(chip->pcm);
+    snd_pcm_suspend_all(chip->pcm2);
+    snd_pcm_suspend_all(chip->pcm_spdif);
+    snd_pcm_suspend_all(chip->pcm_4ch);
+    snd_ac97_suspend(chip->ac97);
+    for (i = 0; i < YDSXGR_NUM_SAVED_REGS; i++)
+        chip->saved_regs[i] = snd_ymfpci_readl(chip, saved_regs_index[i]);
+    chip->saved_ydsxgr_mode = snd_ymfpci_readl(chip, YDSXGR_MODE);
+    snd_ymfpci_writel(chip, YDSXGR_NATIVEDACOUTVOL, 0);
+    snd_ymfpci_disable_dsp(chip);
+    pci_disable_device(pci);
+    pci_save_state(pci);
+    return 0;
+}
+
+int snd_ymfpci_resume(struct pci_dev *pci)
+{
+    struct snd_card *card = pci_get_drvdata(pci);
+    struct snd_ymfpci *chip = card->private_data;
+    unsigned int i;
+
+    pci_restore_state(pci);
+    pci_enable_device(pci);
+    pci_set_master(pci);
+    snd_ymfpci_aclink_reset(pci);
+    snd_ymfpci_codec_ready(chip, 0);
+    snd_ymfpci_download_image(chip);
+    udelay(100);
+
+    for (i = 0; i < YDSXGR_NUM_SAVED_REGS; i++)
+        snd_ymfpci_writel(chip, saved_regs_index[i], chip->saved_regs[i]);
+
+    snd_ac97_resume(chip->ac97);
+
+    /* start hw again */
+    if (chip->start_count > 0) {
+        spin_lock_irq(&chip->reg_lock);
+        snd_ymfpci_writel(chip, YDSXGR_MODE, chip->saved_ydsxgr_mode);
+        chip->active_bank = snd_ymfpci_readl(chip, YDSXGR_CTRLSELECT);
+        spin_unlock_irq(&chip->reg_lock);
+    }
+    snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+    return 0;
+}
+
+#endif /* CONFIG_PM */
+
+int __devinit snd_ymfpci_create(snd_card_t * card,
+                                struct pci_dev * pci,
+                                unsigned short old_legacy_ctrl,
+                                ymfpci_t ** rchip)
+{
+    ymfpci_t *chip;
+    int err;
+
+    static struct snd_device_ops ops = {
+        .dev_free =	snd_ymfpci_dev_free,
+    };
+
+    *rchip = NULL;
+
+    /* enable PCI device */
+    if ((err = pci_enable_device(pci)) < 0)
+        return err;
+
+    chip = kcalloc(1, sizeof(*chip), GFP_KERNEL);
+    if (chip == NULL)
+        return -ENOMEM;
+    chip->old_legacy_ctrl = old_legacy_ctrl;
+    spin_lock_init(&chip->reg_lock);
+    spin_lock_init(&chip->voice_lock);
+    init_waitqueue_head(&chip->interrupt_sleep);
+    atomic_set(&chip->interrupt_sleep_count, 0);
+    chip->card = card;
+    chip->pci = pci;
+    chip->irq = -1;
+    chip->device_id = pci->device;
+    pci_read_config_byte(pci, PCI_REVISION_ID, (u8 *)&chip->rev);
+
+    // vladest resource workaround
+#if 0
+    if (chip->device_id >= 0x0010)
+        chip->reg_area_phys = pci_resource_start(pci, 2);
+    else
+#endif
+        chip->reg_area_phys = pci_resource_start(pci, 0);
+
+    chip->reg_area_virt = (unsigned long)ioremap_nocache(chip->reg_area_phys, /*0x8000*/pci_resource_len(pci, 0));
+    pci_set_master(pci);
+
+    if ((chip->res_reg_area = request_mem_region(chip->reg_area_phys, /*0x8000*/pci_resource_len(pci, 0), "YMFPCI")) == NULL) {
+        snd_printk("unable to grab memory region 0x%lx-0x%lx\n", chip->reg_area_phys, chip->reg_area_phys + /*0x8000*/pci_resource_len(pci, 0) - 1);
+        snd_ymfpci_free(chip);
+        return -EBUSY;
+    }
+    if (request_irq(pci->irq, snd_ymfpci_interrupt, SA_INTERRUPT|SA_SHIRQ, "YMFPCI", (void *) chip)) {
+        snd_printk("unable to grab IRQ %d\n", pci->irq);
+        snd_ymfpci_free(chip);
+        return -EBUSY;
+    }
+    chip->irq = pci->irq;
+
+    snd_ymfpci_aclink_reset(pci);
+    if (snd_ymfpci_codec_ready(chip, 0) < 0) {
+        snd_ymfpci_free(chip);
+        return -EIO;
+    }
+
+    snd_ymfpci_download_image(chip);
+
+    udelay(100); /* seems we need a delay after downloading image.. */
+
+    if (snd_ymfpci_memalloc(chip) < 0) {
+        snd_ymfpci_free(chip);
+        return -EIO;
+    }
+    if ((err = snd_ymfpci_ac3_init(chip)) < 0) {
+        snd_ymfpci_free(chip);
+        return err;
+    }
+
+#ifdef CONFIG_PM
+    chip->saved_regs = vmalloc(YDSXGR_NUM_SAVED_REGS * sizeof(u32));
+    if (chip->saved_regs == NULL) {
+        snd_ymfpci_free(chip);
+        return -ENOMEM;
+    }
+#endif
+
+    if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+        snd_ymfpci_free(chip);
+        return err;
+    }
+
+    snd_ymfpci_proc_init(card, chip);
+
+    snd_card_set_dev(card, &pci->dev);
+
+    *rchip = chip;
+    return 0;
+}
Index: /GPL/branches/alsa-resync1/alsa-kernel/synth/emux/emux.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/synth/emux/emux.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/synth/emux/emux.c	(revision 91)
@@ -0,0 +1,201 @@
+/*
+ *  Copyright (C) 2000 Takashi Iwai <tiwai@suse.de>
+ *
+ *  Routines for control of EMU WaveTable chip
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include <sound/driver.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <sound/core.h>
+#include <sound/emux_synth.h>
+#include <linux/init.h>
+#include "emux_voice.h"
+
+MODULE_AUTHOR("Takashi Iwai");
+MODULE_DESCRIPTION("Routines for control of EMU WaveTable chip");
+MODULE_LICENSE("GPL");
+
+/*
+ * create a new hardware dependent device for Emu8000/Emu10k1
+ */
+int snd_emux_new(struct snd_emux **remu)
+{
+	struct snd_emux *emu;
+
+	*remu = NULL;
+	emu = kzalloc(sizeof(*emu), GFP_KERNEL);
+	if (emu == NULL)
+		return -ENOMEM;
+
+	spin_lock_init(&emu->voice_lock);
+	init_MUTEX(&emu->register_mutex);
+
+	emu->client = -1;
+#ifdef CONFIG_SND_SEQUENCER_OSS
+	emu->oss_synth = NULL;
+#endif
+	emu->max_voices = 0;
+	emu->use_time = 0;
+
+	init_timer(&emu->tlist);
+	emu->tlist.function = snd_emux_timer_callback;
+	emu->tlist.data = (unsigned long)emu;
+	emu->timer_active = 0;
+
+	*remu = emu;
+	return 0;
+}
+
+
+/*
+ */
+static int sf_sample_new(void *private_data, struct snd_sf_sample *sp,
+				  struct snd_util_memhdr *hdr,
+				  const void __user *buf, long count)
+{
+	struct snd_emux *emu = private_data;
+	return emu->ops.sample_new(emu, sp, hdr, buf, count);
+	
+}
+
+static int sf_sample_free(void *private_data, struct snd_sf_sample *sp,
+				   struct snd_util_memhdr *hdr)
+{
+	struct snd_emux *emu = private_data;
+	return emu->ops.sample_free(emu, sp, hdr);
+	
+}
+
+static void sf_sample_reset(void *private_data)
+{
+	struct snd_emux *emu = private_data;
+	emu->ops.sample_reset(emu);
+}
+
+int snd_emux_register(struct snd_emux *emu, struct snd_card *card, int index, char *name)
+{
+	int err;
+	struct snd_sf_callback sf_cb;
+
+	snd_assert(emu->hw != NULL, return -EINVAL);
+	snd_assert(emu->max_voices > 0, return -EINVAL);
+	snd_assert(card != NULL, return -EINVAL);
+	snd_assert(name != NULL, return -EINVAL);
+
+	emu->card = card;
+	emu->name = kstrdup(name, GFP_KERNEL);
+	emu->voices = kcalloc(emu->max_voices, sizeof(struct snd_emux_voice),
+			      GFP_KERNEL);
+	if (emu->voices == NULL)
+		return -ENOMEM;
+
+	/* create soundfont list */
+	memset(&sf_cb, 0, sizeof(sf_cb));
+	sf_cb.private_data = emu;
+	if (emu->ops.sample_new)
+		sf_cb.sample_new = sf_sample_new;
+	if (emu->ops.sample_free)
+		sf_cb.sample_free = sf_sample_free;
+	if (emu->ops.sample_reset)
+		sf_cb.sample_reset = sf_sample_reset;
+	emu->sflist = snd_sf_new(&sf_cb, emu->memhdr);
+	if (emu->sflist == NULL)
+		return -ENOMEM;
+
+	if ((err = snd_emux_init_hwdep(emu)) < 0)
+		return err;
+
+	snd_emux_init_voices(emu);
+
+	snd_emux_init_seq(emu, card, index);
+#ifdef CONFIG_SND_SEQUENCER_OSS
+	snd_emux_init_seq_oss(emu);
+#endif
+	snd_emux_init_virmidi(emu, card);
+
+#ifdef CONFIG_PROC_FS
+	snd_emux_proc_init(emu, card, index);
+#endif
+	return 0;
+}
+
+
+/*
+ */
+int snd_emux_free(struct snd_emux *emu)
+{
+	unsigned long flags;
+
+	if (! emu)
+		return -EINVAL;
+
+	spin_lock_irqsave(&emu->voice_lock, flags);
+	if (emu->timer_active)
+		del_timer(&emu->tlist);
+	spin_unlock_irqrestore(&emu->voice_lock, flags);
+
+#ifdef CONFIG_PROC_FS
+	snd_emux_proc_free(emu);
+#endif
+	snd_emux_delete_virmidi(emu);
+#ifdef CONFIG_SND_SEQUENCER_OSS
+	snd_emux_detach_seq_oss(emu);
+#endif
+	snd_emux_detach_seq(emu);
+
+	snd_emux_delete_hwdep(emu);
+
+	if (emu->sflist)
+		snd_sf_free(emu->sflist);
+
+	kfree(emu->voices);
+	kfree(emu->name);
+	kfree(emu);
+	return 0;
+}
+
+
+EXPORT_SYMBOL(snd_emux_new);
+EXPORT_SYMBOL(snd_emux_register);
+EXPORT_SYMBOL(snd_emux_free);
+
+EXPORT_SYMBOL(snd_emux_terminate_all);
+EXPORT_SYMBOL(snd_emux_lock_voice);
+EXPORT_SYMBOL(snd_emux_unlock_voice);
+
+/* soundfont.c */
+EXPORT_SYMBOL(snd_sf_linear_to_log);
+
+
+/*
+ *  INIT part
+ */
+
+static int __init alsa_emux_init(void)
+{
+	return 0;
+}
+
+static void __exit alsa_emux_exit(void)
+{
+}
+
+module_init(alsa_emux_init)
+module_exit(alsa_emux_exit)
Index: /GPL/branches/alsa-resync1/alsa-kernel/synth/emux/emux_effect.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/synth/emux/emux_effect.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/synth/emux/emux_effect.c	(revision 91)
@@ -0,0 +1,310 @@
+/*
+ *  Midi synth routines for the Emu8k/Emu10k1
+ *
+ *  Copyright (C) 1999 Steve Ratcliffe
+ *  Copyright (c) 1999-2000 Takashi Iwai <tiwai@suse.de>
+ *
+ *  Contains code based on awe_wave.c by Takashi Iwai
+ *
+ *   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 "emux_voice.h"
+#include <linux/slab.h>
+
+#ifdef SNDRV_EMUX_USE_RAW_EFFECT
+/*
+ * effects table
+ */
+
+#define xoffsetof(type,tag)	((long)(&((type)NULL)->tag) - (long)(NULL))
+
+#define parm_offset(tag)	xoffsetof(struct soundfont_voice_parm *, tag)
+
+#define PARM_IS_BYTE		(1 << 0)
+#define PARM_IS_WORD		(1 << 1)
+#define PARM_IS_ALIGNED		(3 << 2)
+#define PARM_IS_ALIGN_HI	(1 << 2)
+#define PARM_IS_ALIGN_LO	(2 << 2)
+#define PARM_IS_SIGNED		(1 << 4)
+
+#define PARM_WORD	(PARM_IS_WORD)
+#define PARM_BYTE_LO	(PARM_IS_BYTE|PARM_IS_ALIGN_LO)
+#define PARM_BYTE_HI	(PARM_IS_BYTE|PARM_IS_ALIGN_HI)
+#define PARM_BYTE	(PARM_IS_BYTE)
+#define PARM_SIGN_LO	(PARM_IS_BYTE|PARM_IS_ALIGN_LO|PARM_IS_SIGNED)
+#define PARM_SIGN_HI	(PARM_IS_BYTE|PARM_IS_ALIGN_HI|PARM_IS_SIGNED)
+
+static struct emux_parm_defs {
+	int type;	/* byte or word */
+	int low, high;	/* value range */
+	long offset;	/* offset in parameter record (-1 = not written) */
+	int update;	/* flgas for real-time update */
+} parm_defs[EMUX_NUM_EFFECTS] = {
+	{PARM_WORD, 0, 0x8000, parm_offset(moddelay), 0},	/* env1 delay */
+	{PARM_BYTE_LO, 1, 0x80, parm_offset(modatkhld), 0},	/* env1 attack */
+	{PARM_BYTE_HI, 0, 0x7e, parm_offset(modatkhld), 0},	/* env1 hold */
+	{PARM_BYTE_LO, 1, 0x7f, parm_offset(moddcysus), 0},	/* env1 decay */
+	{PARM_BYTE_LO, 1, 0x7f, parm_offset(modrelease), 0},	/* env1 release */
+	{PARM_BYTE_HI, 0, 0x7f, parm_offset(moddcysus), 0},	/* env1 sustain */
+	{PARM_BYTE_HI, 0, 0xff, parm_offset(pefe), 0},	/* env1 pitch */
+	{PARM_BYTE_LO, 0, 0xff, parm_offset(pefe), 0},	/* env1 fc */
+
+	{PARM_WORD, 0, 0x8000, parm_offset(voldelay), 0},	/* env2 delay */
+	{PARM_BYTE_LO, 1, 0x80, parm_offset(volatkhld), 0},	/* env2 attack */
+	{PARM_BYTE_HI, 0, 0x7e, parm_offset(volatkhld), 0},	/* env2 hold */
+	{PARM_BYTE_LO, 1, 0x7f, parm_offset(voldcysus), 0},	/* env2 decay */
+	{PARM_BYTE_LO, 1, 0x7f, parm_offset(volrelease), 0},	/* env2 release */
+	{PARM_BYTE_HI, 0, 0x7f, parm_offset(voldcysus), 0},	/* env2 sustain */
+
+	{PARM_WORD, 0, 0x8000, parm_offset(lfo1delay), 0},	/* lfo1 delay */
+	{PARM_BYTE_LO, 0, 0xff, parm_offset(tremfrq), SNDRV_EMUX_UPDATE_TREMFREQ},	/* lfo1 freq */
+	{PARM_SIGN_HI, -128, 127, parm_offset(tremfrq), SNDRV_EMUX_UPDATE_TREMFREQ},	/* lfo1 vol */
+	{PARM_SIGN_HI, -128, 127, parm_offset(fmmod), SNDRV_EMUX_UPDATE_FMMOD},	/* lfo1 pitch */
+	{PARM_BYTE_LO, 0, 0xff, parm_offset(fmmod), SNDRV_EMUX_UPDATE_FMMOD},	/* lfo1 cutoff */
+
+	{PARM_WORD, 0, 0x8000, parm_offset(lfo2delay), 0},	/* lfo2 delay */
+	{PARM_BYTE_LO, 0, 0xff, parm_offset(fm2frq2), SNDRV_EMUX_UPDATE_FM2FRQ2},	/* lfo2 freq */
+	{PARM_SIGN_HI, -128, 127, parm_offset(fm2frq2), SNDRV_EMUX_UPDATE_FM2FRQ2},	/* lfo2 pitch */
+
+	{PARM_WORD, 0, 0xffff, -1, SNDRV_EMUX_UPDATE_PITCH},	/* initial pitch */
+	{PARM_BYTE, 0, 0xff, parm_offset(chorus), 0},	/* chorus */
+	{PARM_BYTE, 0, 0xff, parm_offset(reverb), 0},	/* reverb */
+	{PARM_BYTE, 0, 0xff, parm_offset(cutoff), SNDRV_EMUX_UPDATE_VOLUME},	/* cutoff */
+	{PARM_BYTE, 0, 15, parm_offset(filterQ), SNDRV_EMUX_UPDATE_Q},	/* resonance */
+
+	{PARM_WORD, 0, 0xffff, -1, 0},	/* sample start */
+	{PARM_WORD, 0, 0xffff, -1, 0},	/* loop start */
+	{PARM_WORD, 0, 0xffff, -1, 0},	/* loop end */
+	{PARM_WORD, 0, 0xffff, -1, 0},	/* coarse sample start */
+	{PARM_WORD, 0, 0xffff, -1, 0},	/* coarse loop start */
+	{PARM_WORD, 0, 0xffff, -1, 0},	/* coarse loop end */
+	{PARM_BYTE, 0, 0xff, -1, SNDRV_EMUX_UPDATE_VOLUME},	/* initial attenuation */
+};
+
+/* set byte effect value */
+static void
+effect_set_byte(unsigned char *valp, struct snd_midi_channel *chan, int type)
+{
+	short effect;
+	struct snd_emux_effect_table *fx = chan->private;
+
+	effect = fx->val[type];
+	if (fx->flag[type] == EMUX_FX_FLAG_ADD) {
+		if (parm_defs[type].type & PARM_IS_SIGNED)
+			effect += *(char*)valp;
+		else
+			effect += *valp;
+	}
+	if (effect < parm_defs[type].low)
+		effect = parm_defs[type].low;
+	else if (effect > parm_defs[type].high)
+		effect = parm_defs[type].high;
+	*valp = (unsigned char)effect;
+}
+
+/* set word effect value */
+static void
+effect_set_word(unsigned short *valp, struct snd_midi_channel *chan, int type)
+{
+	int effect;
+	struct snd_emux_effect_table *fx = chan->private;
+
+	effect = *(unsigned short*)&fx->val[type];
+	if (fx->flag[type] == EMUX_FX_FLAG_ADD)
+		effect += *valp;
+	if (effect < parm_defs[type].low)
+		effect = parm_defs[type].low;
+	else if (effect > parm_defs[type].high)
+		effect = parm_defs[type].high;
+	*valp = (unsigned short)effect;
+}
+
+/* address offset */
+static int
+effect_get_offset(struct snd_midi_channel *chan, int lo, int hi, int mode)
+{
+	int addr = 0;
+	struct snd_emux_effect_table *fx = chan->private;
+
+	if (fx->flag[hi])
+		addr = (short)fx->val[hi];
+	addr = addr << 15;
+	if (fx->flag[lo])
+		addr += (short)fx->val[lo];
+	if (!(mode & SNDRV_SFNT_SAMPLE_8BITS))
+		addr /= 2;
+	return addr;
+}
+
+#ifdef CONFIG_SND_SEQUENCER_OSS
+/* change effects - for OSS sequencer compatibility */
+void
+snd_emux_send_effect_oss(struct snd_emux_port *port,
+			 struct snd_midi_channel *chan, int type, int val)
+{
+	int mode;
+
+	if (type & 0x40)
+		mode = EMUX_FX_FLAG_OFF;
+	else if (type & 0x80)
+		mode = EMUX_FX_FLAG_ADD;
+	else
+		mode = EMUX_FX_FLAG_SET;
+	type &= 0x3f;
+
+	snd_emux_send_effect(port, chan, type, val, mode);
+}
+#endif
+
+/* Modify the effect value.
+ * if update is necessary, call emu8000_control
+ */
+void
+snd_emux_send_effect(struct snd_emux_port *port, struct snd_midi_channel *chan,
+		     int type, int val, int mode)
+{
+	int i;
+	int offset;
+	unsigned char *srcp, *origp;
+	struct snd_emux *emu;
+	struct snd_emux_effect_table *fx;
+	unsigned long flags;
+
+	emu = port->emu;
+	fx = chan->private;
+	if (emu == NULL || fx == NULL)
+		return;
+	if (type < 0 || type >= EMUX_NUM_EFFECTS)
+		return;
+
+	fx->val[type] = val;
+	fx->flag[type] = mode;
+
+	/* do we need to modify the register in realtime ? */
+	if (! parm_defs[type].update || (offset = parm_defs[type].offset) < 0)
+		return;
+
+#ifdef SNDRV_LITTLE_ENDIAN
+	if (parm_defs[type].type & PARM_IS_ALIGN_HI)
+		offset++;
+#else
+	if (parm_defs[type].type & PARM_IS_ALIGN_LO)
+		offset++;
+#endif
+	/* modify the register values */
+	spin_lock_irqsave(&emu->voice_lock, flags);
+	for (i = 0; i < emu->max_voices; i++) {
+		struct snd_emux_voice *vp = &emu->voices[i];
+		if (!STATE_IS_PLAYING(vp->state) || vp->chan != chan)
+			continue;
+		srcp = (unsigned char*)&vp->reg.parm + offset;
+		origp = (unsigned char*)&vp->zone->v.parm + offset;
+		if (parm_defs[i].type & PARM_IS_BYTE) {
+			*srcp = *origp;
+			effect_set_byte(srcp, chan, type);
+		} else {
+			*(unsigned short*)srcp = *(unsigned short*)origp;
+			effect_set_word((unsigned short*)srcp, chan, type);
+		}
+	}
+	spin_unlock_irqrestore(&emu->voice_lock, flags);
+
+	/* activate them */
+	snd_emux_update_channel(port, chan, parm_defs[type].update);
+}
+
+
+/* copy wavetable registers to voice table */
+void
+snd_emux_setup_effect(struct snd_emux_voice *vp)
+{
+	struct snd_midi_channel *chan = vp->chan;
+	struct snd_emux_effect_table *fx;
+	unsigned char *srcp;
+	int i;
+
+	if (! (fx = chan->private))
+		return;
+
+	/* modify the register values via effect table */
+	for (i = 0; i < EMUX_FX_END; i++) {
+		int offset;
+		if (! fx->flag[i] || (offset = parm_defs[i].offset) < 0)
+			continue;
+#ifdef SNDRV_LITTLE_ENDIAN
+		if (parm_defs[i].type & PARM_IS_ALIGN_HI)
+			offset++;
+#else
+		if (parm_defs[i].type & PARM_IS_ALIGN_LO)
+			offset++;
+#endif
+		srcp = (unsigned char*)&vp->reg.parm + offset;
+		if (parm_defs[i].type & PARM_IS_BYTE)
+			effect_set_byte(srcp, chan, i);
+		else
+			effect_set_word((unsigned short*)srcp, chan, i);
+	}
+
+	/* correct sample and loop points */
+	vp->reg.start += effect_get_offset(chan, EMUX_FX_SAMPLE_START,
+					   EMUX_FX_COARSE_SAMPLE_START,
+					   vp->reg.sample_mode);
+
+	vp->reg.loopstart += effect_get_offset(chan, EMUX_FX_LOOP_START,
+					       EMUX_FX_COARSE_LOOP_START,
+					       vp->reg.sample_mode);
+
+	vp->reg.loopend += effect_get_offset(chan, EMUX_FX_LOOP_END,
+					     EMUX_FX_COARSE_LOOP_END,
+					     vp->reg.sample_mode);
+}
+
+/*
+ * effect table
+ */
+void
+snd_emux_create_effect(struct snd_emux_port *p)
+{
+	int i;
+	p->effect = kcalloc(p->chset.max_channels,
+			    sizeof(struct snd_emux_effect_table), GFP_KERNEL);
+	if (p->effect) {
+		for (i = 0; i < p->chset.max_channels; i++)
+			p->chset.channels[i].private = p->effect + i;
+	} else {
+		for (i = 0; i < p->chset.max_channels; i++)
+			p->chset.channels[i].private = NULL;
+	}
+}
+
+void
+snd_emux_delete_effect(struct snd_emux_port *p)
+{
+	kfree(p->effect);
+	p->effect = NULL;
+}
+
+void
+snd_emux_clear_effect(struct snd_emux_port *p)
+{
+	if (p->effect) {
+		memset(p->effect, 0, sizeof(struct snd_emux_effect_table) *
+		       p->chset.max_channels);
+	}
+}
+
+#endif /* SNDRV_EMUX_USE_RAW_EFFECT */
Index: /GPL/branches/alsa-resync1/alsa-kernel/synth/emux/emux_nrpn.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/synth/emux/emux_nrpn.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/synth/emux/emux_nrpn.c	(revision 91)
@@ -0,0 +1,396 @@
+/*
+ *  NRPN / SYSEX callbacks for Emu8k/Emu10k1
+ *
+ *  Copyright (c) 1999-2000 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 "emux_voice.h"
+#include <sound/asoundef.h>
+
+/*
+ * conversion from NRPN/control parameters to Emu8000 raw parameters
+ */
+
+/* NRPN / CC -> Emu8000 parameter converter */
+struct nrpn_conv_table {
+	int control;
+	int effect;
+	int (*convert)(int val);
+};
+
+/* effect sensitivity */
+
+#define FX_CUTOFF	0
+#define FX_RESONANCE	1
+#define FX_ATTACK	2
+#define FX_RELEASE	3
+#define FX_VIBRATE	4
+#define FX_VIBDEPTH	5
+#define FX_VIBDELAY	6
+#define FX_NUMS		7
+
+/*
+ * convert NRPN/control values
+ */
+
+static int send_converted_effect(struct nrpn_conv_table *table, int num_tables,
+				 struct snd_emux_port *port,
+				 struct snd_midi_channel *chan,
+				 int type, int val, int mode)
+{
+	int i, cval;
+	for (i = 0; i < num_tables; i++) {
+		if (table[i].control == type) {
+			cval = table[i].convert(val);
+			snd_emux_send_effect(port, chan, table[i].effect,
+					     cval, mode);
+			return 1;
+		}
+	}
+	return 0;
+}
+
+#define DEF_FX_CUTOFF		170
+#define DEF_FX_RESONANCE	6
+#define DEF_FX_ATTACK		50
+#define DEF_FX_RELEASE		50
+#define DEF_FX_VIBRATE		30
+#define DEF_FX_VIBDEPTH		4
+#define DEF_FX_VIBDELAY		1500
+
+/* effect sensitivities for GS NRPN:
+ *  adjusted for chaos 8MB soundfonts
+ */
+static int gs_sense[] = 
+{
+	DEF_FX_CUTOFF, DEF_FX_RESONANCE, DEF_FX_ATTACK, DEF_FX_RELEASE,
+	DEF_FX_VIBRATE, DEF_FX_VIBDEPTH, DEF_FX_VIBDELAY
+};
+
+/* effect sensitivies for XG controls:
+ * adjusted for chaos 8MB soundfonts
+ */
+static int xg_sense[] = 
+{
+	DEF_FX_CUTOFF, DEF_FX_RESONANCE, DEF_FX_ATTACK, DEF_FX_RELEASE,
+	DEF_FX_VIBRATE, DEF_FX_VIBDEPTH, DEF_FX_VIBDELAY
+};
+
+
+/*
+ * AWE32 NRPN effects
+ */
+
+static int fx_delay(int val);
+static int fx_attack(int val);
+static int fx_hold(int val);
+static int fx_decay(int val);
+static int fx_the_value(int val);
+static int fx_twice_value(int val);
+static int fx_conv_pitch(int val);
+static int fx_conv_Q(int val);
+
+/* function for each NRPN */		/* [range]  units */
+#define fx_env1_delay	fx_delay	/* [0,5900] 4msec */
+#define fx_env1_attack	fx_attack	/* [0,5940] 1msec */
+#define fx_env1_hold	fx_hold		/* [0,8191] 1msec */
+#define fx_env1_decay	fx_decay	/* [0,5940] 4msec */
+#define fx_env1_release	fx_decay	/* [0,5940] 4msec */
+#define fx_env1_sustain	fx_the_value	/* [0,127] 0.75dB */
+#define fx_env1_pitch	fx_the_value	/* [-127,127] 9.375cents */
+#define fx_env1_cutoff	fx_the_value	/* [-127,127] 56.25cents */
+
+#define fx_env2_delay	fx_delay	/* [0,5900] 4msec */
+#define fx_env2_attack	fx_attack	/* [0,5940] 1msec */
+#define fx_env2_hold	fx_hold		/* [0,8191] 1msec */
+#define fx_env2_decay	fx_decay	/* [0,5940] 4msec */
+#define fx_env2_release	fx_decay	/* [0,5940] 4msec */
+#define fx_env2_sustain	fx_the_value	/* [0,127] 0.75dB */
+
+#define fx_lfo1_delay	fx_delay	/* [0,5900] 4msec */
+#define fx_lfo1_freq	fx_twice_value	/* [0,127] 84mHz */
+#define fx_lfo1_volume	fx_twice_value	/* [0,127] 0.1875dB */
+#define fx_lfo1_pitch	fx_the_value	/* [-127,127] 9.375cents */
+#define fx_lfo1_cutoff	fx_twice_value	/* [-64,63] 56.25cents */
+
+#define fx_lfo2_delay	fx_delay	/* [0,5900] 4msec */
+#define fx_lfo2_freq	fx_twice_value	/* [0,127] 84mHz */
+#define fx_lfo2_pitch	fx_the_value	/* [-127,127] 9.375cents */
+
+#define fx_init_pitch	fx_conv_pitch	/* [-8192,8192] cents */
+#define fx_chorus	fx_the_value	/* [0,255] -- */
+#define fx_reverb	fx_the_value	/* [0,255] -- */
+#define fx_cutoff	fx_twice_value	/* [0,127] 62Hz */
+#define fx_filterQ	fx_conv_Q	/* [0,127] -- */
+
+static int fx_delay(int val)
+{
+	return (unsigned short)snd_sf_calc_parm_delay(val);
+}
+
+static int fx_attack(int val)
+{
+	return (unsigned short)snd_sf_calc_parm_attack(val);
+}
+
+static int fx_hold(int val)
+{
+	return (unsigned short)snd_sf_calc_parm_hold(val);
+}
+
+static int fx_decay(int val)
+{
+	return (unsigned short)snd_sf_calc_parm_decay(val);
+}
+
+static int fx_the_value(int val)
+{
+	return (unsigned short)(val & 0xff);
+}
+
+static int fx_twice_value(int val)
+{
+	return (unsigned short)((val * 2) & 0xff);
+}
+
+static int fx_conv_pitch(int val)
+{
+	return (short)(val * 4096 / 1200);
+}
+
+static int fx_conv_Q(int val)
+{
+	return (unsigned short)((val / 8) & 0xff);
+}
+
+
+static struct nrpn_conv_table awe_effects[] =
+{
+	{ 0, EMUX_FX_LFO1_DELAY,	fx_lfo1_delay},
+	{ 1, EMUX_FX_LFO1_FREQ,	fx_lfo1_freq},
+	{ 2, EMUX_FX_LFO2_DELAY,	fx_lfo2_delay},
+	{ 3, EMUX_FX_LFO2_FREQ,	fx_lfo2_freq},
+
+	{ 4, EMUX_FX_ENV1_DELAY,	fx_env1_delay},
+	{ 5, EMUX_FX_ENV1_ATTACK,fx_env1_attack},
+	{ 6, EMUX_FX_ENV1_HOLD,	fx_env1_hold},
+	{ 7, EMUX_FX_ENV1_DECAY,	fx_env1_decay},
+	{ 8, EMUX_FX_ENV1_SUSTAIN,	fx_env1_sustain},
+	{ 9, EMUX_FX_ENV1_RELEASE,	fx_env1_release},
+
+	{10, EMUX_FX_ENV2_DELAY,	fx_env2_delay},
+	{11, EMUX_FX_ENV2_ATTACK,	fx_env2_attack},
+	{12, EMUX_FX_ENV2_HOLD,	fx_env2_hold},
+	{13, EMUX_FX_ENV2_DECAY,	fx_env2_decay},
+	{14, EMUX_FX_ENV2_SUSTAIN,	fx_env2_sustain},
+	{15, EMUX_FX_ENV2_RELEASE,	fx_env2_release},
+
+	{16, EMUX_FX_INIT_PITCH,	fx_init_pitch},
+	{17, EMUX_FX_LFO1_PITCH,	fx_lfo1_pitch},
+	{18, EMUX_FX_LFO2_PITCH,	fx_lfo2_pitch},
+	{19, EMUX_FX_ENV1_PITCH,	fx_env1_pitch},
+	{20, EMUX_FX_LFO1_VOLUME,	fx_lfo1_volume},
+	{21, EMUX_FX_CUTOFF,		fx_cutoff},
+	{22, EMUX_FX_FILTERQ,	fx_filterQ},
+	{23, EMUX_FX_LFO1_CUTOFF,	fx_lfo1_cutoff},
+	{24, EMUX_FX_ENV1_CUTOFF,	fx_env1_cutoff},
+	{25, EMUX_FX_CHORUS,		fx_chorus},
+	{26, EMUX_FX_REVERB,		fx_reverb},
+};
+
+
+/*
+ * GS(SC88) NRPN effects; still experimental
+ */
+
+/* cutoff: quarter semitone step, max=255 */
+static int gs_cutoff(int val)
+{
+	return (val - 64) * gs_sense[FX_CUTOFF] / 50;
+}
+
+/* resonance: 0 to 15(max) */
+static int gs_filterQ(int val)
+{
+	return (val - 64) * gs_sense[FX_RESONANCE] / 50;
+}
+
+/* attack: */
+static int gs_attack(int val)
+{
+	return -(val - 64) * gs_sense[FX_ATTACK] / 50;
+}
+
+/* decay: */
+static int gs_decay(int val)
+{
+	return -(val - 64) * gs_sense[FX_RELEASE] / 50;
+}
+
+/* release: */
+static int gs_release(int val)
+{
+	return -(val - 64) * gs_sense[FX_RELEASE] / 50;
+}
+
+/* vibrato freq: 0.042Hz step, max=255 */
+static int gs_vib_rate(int val)
+{
+	return (val - 64) * gs_sense[FX_VIBRATE] / 50;
+}
+
+/* vibrato depth: max=127, 1 octave */
+static int gs_vib_depth(int val)
+{
+	return (val - 64) * gs_sense[FX_VIBDEPTH] / 50;
+}
+
+/* vibrato delay: -0.725msec step */
+static int gs_vib_delay(int val)
+{
+	return -(val - 64) * gs_sense[FX_VIBDELAY] / 50;
+}
+
+static struct nrpn_conv_table gs_effects[] =
+{
+	{32, EMUX_FX_CUTOFF,	gs_cutoff},
+	{33, EMUX_FX_FILTERQ,	gs_filterQ},
+	{99, EMUX_FX_ENV2_ATTACK, gs_attack},
+	{100, EMUX_FX_ENV2_DECAY, gs_decay},
+	{102, EMUX_FX_ENV2_RELEASE, gs_release},
+	{8, EMUX_FX_LFO1_FREQ, gs_vib_rate},
+	{9, EMUX_FX_LFO1_VOLUME, gs_vib_depth},
+	{10, EMUX_FX_LFO1_DELAY, gs_vib_delay},
+};
+
+
+/*
+ * NRPN events
+ */
+void
+snd_emux_nrpn(void *p, struct snd_midi_channel *chan,
+	      struct snd_midi_channel_set *chset)
+{
+	struct snd_emux_port *port;
+
+	port = p;
+	snd_assert(port != NULL, return);
+	snd_assert(chan != NULL, return);
+
+	if (chan->control[MIDI_CTL_NONREG_PARM_NUM_MSB] == 127 &&
+	    chan->control[MIDI_CTL_NONREG_PARM_NUM_LSB] <= 26) {
+		int val;
+		/* Win/DOS AWE32 specific NRPNs */
+		/* both MSB/LSB necessary */
+		val = (chan->control[MIDI_CTL_MSB_DATA_ENTRY] << 7) |
+			chan->control[MIDI_CTL_LSB_DATA_ENTRY]; 
+		val -= 8192;
+		send_converted_effect
+			(awe_effects, ARRAY_SIZE(awe_effects),
+			 port, chan, chan->control[MIDI_CTL_NONREG_PARM_NUM_LSB],
+			 val, EMUX_FX_FLAG_SET);
+		return;
+	}
+
+	if (port->chset.midi_mode == SNDRV_MIDI_MODE_GS &&
+	    chan->control[MIDI_CTL_NONREG_PARM_NUM_MSB] == 1) {
+		int val;
+		/* GS specific NRPNs */
+		/* only MSB is valid */
+		val = chan->control[MIDI_CTL_MSB_DATA_ENTRY];
+		send_converted_effect
+			(gs_effects, ARRAY_SIZE(gs_effects),
+			 port, chan, chan->control[MIDI_CTL_NONREG_PARM_NUM_LSB],
+			 val, EMUX_FX_FLAG_ADD);
+		return;
+	}
+}
+
+
+/*
+ * XG control effects; still experimental
+ */
+
+/* cutoff: quarter semitone step, max=255 */
+static int xg_cutoff(int val)
+{
+	return (val - 64) * xg_sense[FX_CUTOFF] / 64;
+}
+
+/* resonance: 0(open) to 15(most nasal) */
+static int xg_filterQ(int val)
+{
+	return (val - 64) * xg_sense[FX_RESONANCE] / 64;
+}
+
+/* attack: */
+static int xg_attack(int val)
+{
+	return -(val - 64) * xg_sense[FX_ATTACK] / 64;
+}
+
+/* release: */
+static int xg_release(int val)
+{
+	return -(val - 64) * xg_sense[FX_RELEASE] / 64;
+}
+
+static struct nrpn_conv_table xg_effects[] =
+{
+	{71, EMUX_FX_CUTOFF,	xg_cutoff},
+	{74, EMUX_FX_FILTERQ,	xg_filterQ},
+	{72, EMUX_FX_ENV2_RELEASE, xg_release},
+	{73, EMUX_FX_ENV2_ATTACK, xg_attack},
+};
+
+int
+snd_emux_xg_control(struct snd_emux_port *port, struct snd_midi_channel *chan,
+		    int param)
+{
+	return send_converted_effect(xg_effects, ARRAY_SIZE(xg_effects),
+				     port, chan, param,
+				     chan->control[param],
+				     EMUX_FX_FLAG_ADD);
+}
+
+/*
+ * receive sysex
+ */
+void
+snd_emux_sysex(void *p, unsigned char *buf, int len, int parsed,
+	       struct snd_midi_channel_set *chset)
+{
+	struct snd_emux_port *port;
+	struct snd_emux *emu;
+
+	port = p;
+	snd_assert(port != NULL, return);
+	snd_assert(chset != NULL, return);
+	emu = port->emu;
+
+	switch (parsed) {
+	case SNDRV_MIDI_SYSEX_GS_MASTER_VOLUME:
+		snd_emux_update_port(port, SNDRV_EMUX_UPDATE_VOLUME);
+		break;
+	default:
+		if (emu->ops.sysex)
+			emu->ops.sysex(emu, buf, len, parsed, chset);
+		break;
+	}
+}
+
Index: /GPL/branches/alsa-resync1/alsa-kernel/synth/emux/emux_proc.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/synth/emux/emux_proc.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/synth/emux/emux_proc.c	(revision 91)
@@ -0,0 +1,138 @@
+/*
+ *  Copyright (C) 2000 Takashi Iwai <tiwai@suse.de>
+ *
+ *  Proc interface for Emu8k/Emu10k1 WaveTable synth
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include <sound/driver.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/emux_synth.h>
+#include <sound/info.h>
+#include "emux_voice.h"
+
+#ifdef CONFIG_PROC_FS
+
+static void
+snd_emux_proc_info_read(struct snd_info_entry *entry, 
+			struct snd_info_buffer *buf)
+{
+	struct snd_emux *emu;
+	int i;
+
+	emu = entry->private_data;
+	down(&emu->register_mutex);
+	if (emu->name)
+		snd_iprintf(buf, "Device: %s\n", emu->name);
+	snd_iprintf(buf, "Ports: %d\n", emu->num_ports);
+	snd_iprintf(buf, "Addresses:");
+	for (i = 0; i < emu->num_ports; i++)
+		snd_iprintf(buf, " %d:%d", emu->client, emu->ports[i]);
+	snd_iprintf(buf, "\n");
+	snd_iprintf(buf, "Use Counter: %d\n", emu->used);
+	snd_iprintf(buf, "Max Voices: %d\n", emu->max_voices);
+	snd_iprintf(buf, "Allocated Voices: %d\n", emu->num_voices);
+	if (emu->memhdr) {
+		snd_iprintf(buf, "Memory Size: %d\n", emu->memhdr->size);
+		snd_iprintf(buf, "Memory Available: %d\n", snd_util_mem_avail(emu->memhdr));
+		snd_iprintf(buf, "Allocated Blocks: %d\n", emu->memhdr->nblocks);
+	} else {
+		snd_iprintf(buf, "Memory Size: 0\n");
+	}
+	if (emu->sflist) {
+		down(&emu->sflist->presets_mutex);
+		snd_iprintf(buf, "SoundFonts: %d\n", emu->sflist->fonts_size);
+		snd_iprintf(buf, "Instruments: %d\n", emu->sflist->zone_counter);
+		snd_iprintf(buf, "Samples: %d\n", emu->sflist->sample_counter);
+		snd_iprintf(buf, "Locked Instruments: %d\n", emu->sflist->zone_locked);
+		snd_iprintf(buf, "Locked Samples: %d\n", emu->sflist->sample_locked);
+		up(&emu->sflist->presets_mutex);
+	}
+#if 0  /* debug */
+	if (emu->voices[0].state != SNDRV_EMUX_ST_OFF && emu->voices[0].ch >= 0) {
+		struct snd_emux_voice *vp = &emu->voices[0];
+		snd_iprintf(buf, "voice 0: on\n");
+		snd_iprintf(buf, "mod delay=%x, atkhld=%x, dcysus=%x, rel=%x\n",
+			    vp->reg.parm.moddelay,
+			    vp->reg.parm.modatkhld,
+			    vp->reg.parm.moddcysus,
+			    vp->reg.parm.modrelease);
+		snd_iprintf(buf, "vol delay=%x, atkhld=%x, dcysus=%x, rel=%x\n",
+			    vp->reg.parm.voldelay,
+			    vp->reg.parm.volatkhld,
+			    vp->reg.parm.voldcysus,
+			    vp->reg.parm.volrelease);
+		snd_iprintf(buf, "lfo1 delay=%x, lfo2 delay=%x, pefe=%x\n",
+			    vp->reg.parm.lfo1delay,
+			    vp->reg.parm.lfo2delay,
+			    vp->reg.parm.pefe);
+		snd_iprintf(buf, "fmmod=%x, tremfrq=%x, fm2frq2=%x\n",
+			    vp->reg.parm.fmmod,
+			    vp->reg.parm.tremfrq,
+			    vp->reg.parm.fm2frq2);
+		snd_iprintf(buf, "cutoff=%x, filterQ=%x, chorus=%x, reverb=%x\n",
+			    vp->reg.parm.cutoff,
+			    vp->reg.parm.filterQ,
+			    vp->reg.parm.chorus,
+			    vp->reg.parm.reverb);
+		snd_iprintf(buf, "avol=%x, acutoff=%x, apitch=%x\n",
+			    vp->avol, vp->acutoff, vp->apitch);
+		snd_iprintf(buf, "apan=%x, aaux=%x, ptarget=%x, vtarget=%x, ftarget=%x\n",
+			    vp->apan, vp->aaux,
+			    vp->ptarget,
+			    vp->vtarget,
+			    vp->ftarget);
+		snd_iprintf(buf, "start=%x, end=%x, loopstart=%x, loopend=%x\n",
+			    vp->reg.start, vp->reg.end, vp->reg.loopstart, vp->reg.loopend);
+		snd_iprintf(buf, "sample_mode=%x, rate=%x\n", vp->reg.sample_mode, vp->reg.rate_offset);
+	}
+#endif
+	up(&emu->register_mutex);
+}
+
+
+void snd_emux_proc_init(struct snd_emux *emu, struct snd_card *card, int device)
+{
+	struct snd_info_entry *entry;
+	char name[64];
+
+	sprintf(name, "wavetableD%d", device);
+	entry = snd_info_create_card_entry(card, name, card->proc_root);
+	if (entry == NULL)
+		return;
+
+	entry->content = SNDRV_INFO_CONTENT_TEXT;
+	entry->private_data = emu;
+	entry->c.text.read_size = 1024;
+	entry->c.text.read = snd_emux_proc_info_read;
+	if (snd_info_register(entry) < 0)
+		snd_info_free_entry(entry);
+	else
+		emu->proc = entry;
+}
+
+void snd_emux_proc_free(struct snd_emux *emu)
+{
+	if (emu->proc) {
+		snd_info_unregister(emu->proc);
+		emu->proc = NULL;
+	}
+}
+
+#endif /* CONFIG_PROC_FS */
Index: /GPL/branches/alsa-resync1/alsa-kernel/synth/emux/emux_seq.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/synth/emux/emux_seq.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/synth/emux/emux_seq.c	(revision 91)
@@ -0,0 +1,428 @@
+/*
+ *  Midi Sequencer interface routines.
+ *
+ *  Copyright (C) 1999 Steve Ratcliffe
+ *  Copyright (c) 1999-2000 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 "emux_voice.h"
+#include <linux/slab.h>
+
+
+/* Prototypes for static functions */
+static void free_port(void *private);
+static void snd_emux_init_port(struct snd_emux_port *p);
+static int snd_emux_use(void *private_data, struct snd_seq_port_subscribe *info);
+static int snd_emux_unuse(void *private_data, struct snd_seq_port_subscribe *info);
+static int get_client(struct snd_card *card, int index, char *name);
+
+/*
+ * MIDI emulation operators
+ */
+static struct snd_midi_op emux_ops = {
+	snd_emux_note_on,
+	snd_emux_note_off,
+	snd_emux_key_press,
+	snd_emux_terminate_note,
+	snd_emux_control,
+	snd_emux_nrpn,
+	snd_emux_sysex,
+};
+
+
+/*
+ * number of MIDI channels
+ */
+#define MIDI_CHANNELS		16
+
+/*
+ * type flags for MIDI sequencer port
+ */
+#define DEFAULT_MIDI_TYPE	(SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC |\
+				 SNDRV_SEQ_PORT_TYPE_MIDI_GM |\
+				 SNDRV_SEQ_PORT_TYPE_MIDI_GS |\
+				 SNDRV_SEQ_PORT_TYPE_MIDI_XG |\
+				 SNDRV_SEQ_PORT_TYPE_DIRECT_SAMPLE)
+
+/*
+ * Initialise the EMUX Synth by creating a client and registering
+ * a series of ports.
+ * Each of the ports will contain the 16 midi channels.  Applications
+ * can connect to these ports to play midi data.
+ */
+int
+snd_emux_init_seq(struct snd_emux *emu, struct snd_card *card, int index)
+{
+	int  i;
+	struct snd_seq_port_callback pinfo;
+	char tmpname[64];
+
+	sprintf(tmpname, "%s WaveTable", emu->name);
+	emu->client = get_client(card, index, tmpname);
+	if (emu->client < 0) {
+		snd_printk("can't create client\n");
+		return -ENODEV;
+	}
+
+	if (emu->num_ports < 0) {
+		snd_printk("seqports must be greater than zero\n");
+		emu->num_ports = 1;
+	} else if (emu->num_ports >= SNDRV_EMUX_MAX_PORTS) {
+		snd_printk("too many ports."
+			   "limited max. ports %d\n", SNDRV_EMUX_MAX_PORTS);
+		emu->num_ports = SNDRV_EMUX_MAX_PORTS;
+	}
+
+	memset(&pinfo, 0, sizeof(pinfo));
+	pinfo.owner = THIS_MODULE;
+	pinfo.use = snd_emux_use;
+	pinfo.unuse = snd_emux_unuse;
+	pinfo.event_input = snd_emux_event_input;
+
+	for (i = 0; i < emu->num_ports; i++) {
+		struct snd_emux_port *p;
+
+		sprintf(tmpname, "%s Port %d", emu->name, i);
+		p = snd_emux_create_port(emu, tmpname, MIDI_CHANNELS,
+					 0, &pinfo);
+		if (p == NULL) {
+			snd_printk("can't create port\n");
+			return -ENOMEM;
+		}
+
+		p->port_mode =  SNDRV_EMUX_PORT_MODE_MIDI;
+		snd_emux_init_port(p);
+		emu->ports[i] = p->chset.port;
+		emu->portptrs[i] = p;
+	}
+
+	return 0;
+}
+
+
+/*
+ * Detach from the ports that were set up for this synthesizer and
+ * destroy the kernel client.
+ */
+void
+snd_emux_detach_seq(struct snd_emux *emu)
+{
+	if (emu->voices)
+		snd_emux_terminate_all(emu);
+		
+	down(&emu->register_mutex);
+	if (emu->client >= 0) {
+		snd_seq_delete_kernel_client(emu->client);
+		emu->client = -1;
+	}
+	up(&emu->register_mutex);
+}
+
+
+/*
+ * create a sequencer port and channel_set
+ */
+
+struct snd_emux_port *
+snd_emux_create_port(struct snd_emux *emu, char *name,
+		     int max_channels, int oss_port,
+		     struct snd_seq_port_callback *callback)
+{
+	struct snd_emux_port *p;
+	int i, type, cap;
+
+	/* Allocate structures for this channel */
+	if ((p = kzalloc(sizeof(*p), GFP_KERNEL)) == NULL) {
+		snd_printk("no memory\n");
+		return NULL;
+	}
+	p->chset.channels = kcalloc(max_channels, sizeof(struct snd_midi_channel), GFP_KERNEL);
+	if (p->chset.channels == NULL) {
+		snd_printk("no memory\n");
+		kfree(p);
+		return NULL;
+	}
+	for (i = 0; i < max_channels; i++)
+		p->chset.channels[i].number = i;
+	p->chset.private_data = p;
+	p->chset.max_channels = max_channels;
+	p->emu = emu;
+	p->chset.client = emu->client;
+#ifdef SNDRV_EMUX_USE_RAW_EFFECT
+	snd_emux_create_effect(p);
+#endif
+	callback->private_free = free_port;
+	callback->private_data = p;
+
+	cap = SNDRV_SEQ_PORT_CAP_WRITE;
+	if (oss_port) {
+		type = SNDRV_SEQ_PORT_TYPE_SPECIFIC;
+	} else {
+		type = DEFAULT_MIDI_TYPE;
+		cap |= SNDRV_SEQ_PORT_CAP_SUBS_WRITE;
+	}
+
+	p->chset.port = snd_seq_event_port_attach(emu->client, callback,
+						  cap, type, max_channels,
+						  emu->max_voices, name);
+
+	return p;
+}
+
+
+/*
+ * release memory block for port
+ */
+static void
+free_port(void *private_data)
+{
+	struct snd_emux_port *p;
+
+	p = private_data;
+	if (p) {
+#ifdef SNDRV_EMUX_USE_RAW_EFFECT
+		snd_emux_delete_effect(p);
+#endif
+		kfree(p->chset.channels);
+		kfree(p);
+	}
+}
+
+
+#define DEFAULT_DRUM_FLAGS	(1<<9)
+
+/*
+ * initialize the port specific parameters
+ */
+static void
+snd_emux_init_port(struct snd_emux_port *p)
+{
+	p->drum_flags = DEFAULT_DRUM_FLAGS;
+	p->volume_atten = 0;
+
+	snd_emux_reset_port(p);
+}
+
+
+/*
+ * reset port
+ */
+void
+snd_emux_reset_port(struct snd_emux_port *port)
+{
+	int i;
+
+	/* stop all sounds */
+	snd_emux_sounds_off_all(port);
+
+	snd_midi_channel_set_clear(&port->chset);
+
+#ifdef SNDRV_EMUX_USE_RAW_EFFECT
+	snd_emux_clear_effect(port);
+#endif
+
+	/* set port specific control parameters */
+	port->ctrls[EMUX_MD_DEF_BANK] = 0;
+	port->ctrls[EMUX_MD_DEF_DRUM] = 0;
+	port->ctrls[EMUX_MD_REALTIME_PAN] = 1;
+
+	for (i = 0; i < port->chset.max_channels; i++) {
+		struct snd_midi_channel *chan = port->chset.channels + i;
+		chan->drum_channel = ((port->drum_flags >> i) & 1) ? 1 : 0;
+	}
+}
+
+
+/*
+ * input sequencer event
+ */
+int
+snd_emux_event_input(struct snd_seq_event *ev, int direct, void *private_data,
+		     int atomic, int hop)
+{
+	struct snd_emux_port *port;
+
+	port = private_data;
+	snd_assert(port != NULL && ev != NULL, return -EINVAL);
+
+	snd_midi_process_event(&emux_ops, ev, &port->chset);
+
+	return 0;
+}
+
+
+/*
+ * increment usage count
+ */
+int
+snd_emux_inc_count(struct snd_emux *emu)
+{
+	emu->used++;
+	if (!try_module_get(emu->ops.owner))
+		goto __error;
+	if (!try_module_get(emu->card->module)) {
+		module_put(emu->ops.owner);
+	      __error:
+		emu->used--;
+		return 0;
+	}
+	return 1;
+}
+
+
+/*
+ * decrease usage count
+ */
+void
+snd_emux_dec_count(struct snd_emux *emu)
+{
+	module_put(emu->card->module);
+	emu->used--;
+	if (emu->used <= 0)
+		snd_emux_terminate_all(emu);
+	module_put(emu->ops.owner);
+}
+
+
+/*
+ * Routine that is called upon a first use of a particular port
+ */
+static int
+snd_emux_use(void *private_data, struct snd_seq_port_subscribe *info)
+{
+	struct snd_emux_port *p;
+	struct snd_emux *emu;
+
+	p = private_data;
+	snd_assert(p != NULL, return -EINVAL);
+	emu = p->emu;
+	snd_assert(emu != NULL, return -EINVAL);
+
+	down(&emu->register_mutex);
+	snd_emux_init_port(p);
+	snd_emux_inc_count(emu);
+	up(&emu->register_mutex);
+	return 0;
+}
+
+/*
+ * Routine that is called upon the last unuse() of a particular port.
+ */
+static int
+snd_emux_unuse(void *private_data, struct snd_seq_port_subscribe *info)
+{
+	struct snd_emux_port *p;
+	struct snd_emux *emu;
+
+	p = private_data;
+	snd_assert(p != NULL, return -EINVAL);
+	emu = p->emu;
+	snd_assert(emu != NULL, return -EINVAL);
+
+	down(&emu->register_mutex);
+	snd_emux_sounds_off_all(p);
+	snd_emux_dec_count(emu);
+	up(&emu->register_mutex);
+	return 0;
+}
+
+
+/*
+ * Create a sequencer client
+ */
+static int
+get_client(struct snd_card *card, int index, char *name)
+{
+	struct snd_seq_client_callback callbacks;
+	struct snd_seq_client_info cinfo;
+	int client;
+
+	memset(&callbacks, 0, sizeof(callbacks));
+	callbacks.private_data = NULL;
+	callbacks.allow_input = 1;
+	callbacks.allow_output = 1;
+
+	/* Find a free client, start from 1 as the MPU expects to use 0 */
+	client = snd_seq_create_kernel_client(card, index, &callbacks);
+	if (client < 0)
+		return client;
+
+	memset(&cinfo, 0, sizeof(cinfo));
+	cinfo.client = client;
+	cinfo.type = KERNEL_CLIENT;
+	strcpy(cinfo.name, name);
+	snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, &cinfo);
+
+	return client;
+}
+
+
+/*
+ * attach virtual rawmidi devices
+ */
+int snd_emux_init_virmidi(struct snd_emux *emu, struct snd_card *card)
+{
+	int i;
+
+	emu->vmidi = NULL;
+	if (emu->midi_ports <= 0)
+		return 0;
+
+	emu->vmidi = kcalloc(emu->midi_ports, sizeof(struct snd_rawmidi *), GFP_KERNEL);
+	if (emu->vmidi == NULL)
+		return -ENOMEM;
+
+	for (i = 0; i < emu->midi_ports; i++) {
+		struct snd_rawmidi *rmidi;
+		struct snd_virmidi_dev *rdev;
+		if (snd_virmidi_new(card, emu->midi_devidx + i, &rmidi) < 0)
+			goto __error;
+		rdev = rmidi->private_data;
+		sprintf(rmidi->name, "%s Synth MIDI", emu->name);
+		rdev->seq_mode = SNDRV_VIRMIDI_SEQ_ATTACH;
+		rdev->client = emu->client;
+		rdev->port = emu->ports[i];
+		if (snd_device_register(card, rmidi) < 0) {
+			snd_device_free(card, rmidi);
+			goto __error;
+		}
+		emu->vmidi[i] = rmidi;
+		//snd_printk("virmidi %d ok\n", i);
+	}
+	return 0;
+
+__error:
+	//snd_printk("error init..\n");
+	snd_emux_delete_virmidi(emu);
+	return -ENOMEM;
+}
+
+int snd_emux_delete_virmidi(struct snd_emux *emu)
+{
+	int i;
+
+	if (emu->vmidi == NULL)
+		return 0;
+
+	for (i = 0; i < emu->midi_ports; i++) {
+		if (emu->vmidi[i])
+			snd_device_free(emu->card, emu->vmidi[i]);
+	}
+	kfree(emu->vmidi);
+	emu->vmidi = NULL;
+	return 0;
+}
Index: /GPL/branches/alsa-resync1/alsa-kernel/synth/emux/emux_synth.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/synth/emux/emux_synth.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/synth/emux/emux_synth.c	(revision 91)
@@ -0,0 +1,966 @@
+/*
+ *  Midi synth routines for the Emu8k/Emu10k1
+ *
+ *  Copyright (C) 1999 Steve Ratcliffe
+ *  Copyright (c) 1999-2000 Takashi Iwai <tiwai@suse.de>
+ *
+ *  Contains code based on awe_wave.c by Takashi Iwai
+ *
+ *   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 "emux_voice.h"
+#include <sound/asoundef.h>
+
+/*
+ * Prototypes
+ */
+
+/*
+ * Ensure a value is between two points
+ * macro evaluates its args more than once, so changed to upper-case.
+ */
+#define LIMITVALUE(x, a, b) do { if ((x) < (a)) (x) = (a); else if ((x) > (b)) (x) = (b); } while (0)
+#define LIMITMAX(x, a) do {if ((x) > (a)) (x) = (a); } while (0)
+
+static int get_zone(struct snd_emux *emu, struct snd_emux_port *port,
+		    int *notep, int vel, struct snd_midi_channel *chan,
+		    struct snd_sf_zone **table);
+static int get_bank(struct snd_emux_port *port, struct snd_midi_channel *chan);
+static void terminate_note1(struct snd_emux *emu, int note,
+			    struct snd_midi_channel *chan, int free);
+static void exclusive_note_off(struct snd_emux *emu, struct snd_emux_port *port,
+			       int exclass);
+static void terminate_voice(struct snd_emux *emu, struct snd_emux_voice *vp, int free);
+static void update_voice(struct snd_emux *emu, struct snd_emux_voice *vp, int update);
+static void setup_voice(struct snd_emux_voice *vp);
+static int calc_pan(struct snd_emux_voice *vp);
+static int calc_volume(struct snd_emux_voice *vp);
+static int calc_pitch(struct snd_emux_voice *vp);
+
+
+/*
+ * Start a note.
+ */
+void
+snd_emux_note_on(void *p, int note, int vel, struct snd_midi_channel *chan)
+{
+	struct snd_emux *emu;
+	int i, key, nvoices;
+	struct snd_emux_voice *vp;
+	struct snd_sf_zone *table[SNDRV_EMUX_MAX_MULTI_VOICES];
+	unsigned long flags;
+	struct snd_emux_port *port;
+
+	port = p;
+	snd_assert(port != NULL && chan != NULL, return);
+
+	emu = port->emu;
+	snd_assert(emu != NULL, return);
+	snd_assert(emu->ops.get_voice != NULL, return);
+	snd_assert(emu->ops.trigger != NULL, return);
+
+	key = note; /* remember the original note */
+	nvoices = get_zone(emu, port, &note, vel, chan, table);
+	if (! nvoices)
+		return;
+
+	/* exclusive note off */
+	for (i = 0; i < nvoices; i++) {
+		struct snd_sf_zone *zp = table[i];
+		if (zp && zp->v.exclusiveClass)
+			exclusive_note_off(emu, port, zp->v.exclusiveClass);
+	}
+
+#if 0 // seems not necessary
+	/* Turn off the same note on the same channel. */
+	terminate_note1(emu, key, chan, 0);
+#endif
+
+	spin_lock_irqsave(&emu->voice_lock, flags);
+	for (i = 0; i < nvoices; i++) {
+
+		/* set up each voice parameter */
+		/* at this stage, we don't trigger the voice yet. */
+
+		if (table[i] == NULL)
+			continue;
+
+		vp = emu->ops.get_voice(emu, port);
+		if (vp == NULL || vp->ch < 0)
+			continue;
+		if (STATE_IS_PLAYING(vp->state))
+			emu->ops.terminate(vp);
+
+		vp->time = emu->use_time++;
+		vp->chan = chan;
+		vp->port = port;
+		vp->key = key;
+		vp->note = note;
+		vp->velocity = vel;
+		vp->zone = table[i];
+		if (vp->zone->sample)
+			vp->block = vp->zone->sample->block;
+		else
+			vp->block = NULL;
+
+		setup_voice(vp);
+
+		vp->state = SNDRV_EMUX_ST_STANDBY;
+		if (emu->ops.prepare) {
+			vp->state = SNDRV_EMUX_ST_OFF;
+			if (emu->ops.prepare(vp) >= 0)
+				vp->state = SNDRV_EMUX_ST_STANDBY;
+		}
+	}
+
+	/* start envelope now */
+	for (i = 0; i < emu->max_voices; i++) {
+		vp = &emu->voices[i];
+		if (vp->state == SNDRV_EMUX_ST_STANDBY &&
+		    vp->chan == chan) {
+			emu->ops.trigger(vp);
+			vp->state = SNDRV_EMUX_ST_ON;
+			vp->ontime = jiffies; /* remember the trigger timing */
+		}
+	}
+	spin_unlock_irqrestore(&emu->voice_lock, flags);
+
+#ifdef SNDRV_EMUX_USE_RAW_EFFECT
+	if (port->port_mode == SNDRV_EMUX_PORT_MODE_OSS_SYNTH) {
+		/* clear voice position for the next note on this channel */
+		struct snd_emux_effect_table *fx = chan->private;
+		if (fx) {
+			fx->flag[EMUX_FX_SAMPLE_START] = 0;
+			fx->flag[EMUX_FX_COARSE_SAMPLE_START] = 0;
+		}
+	}
+#endif
+}
+
+/*
+ * Release a note in response to a midi note off.
+ */
+void
+snd_emux_note_off(void *p, int note, int vel, struct snd_midi_channel *chan)
+{
+	int ch;
+	struct snd_emux *emu;
+	struct snd_emux_voice *vp;
+	unsigned long flags;
+	struct snd_emux_port *port;
+
+	port = p;
+	snd_assert(port != NULL && chan != NULL, return);
+
+	emu = port->emu;
+	snd_assert(emu != NULL, return);
+	snd_assert(emu->ops.release != NULL, return);
+
+	spin_lock_irqsave(&emu->voice_lock, flags);
+	for (ch = 0; ch < emu->max_voices; ch++) {
+		vp = &emu->voices[ch];
+		if (STATE_IS_PLAYING(vp->state) &&
+		    vp->chan == chan && vp->key == note) {
+			vp->state = SNDRV_EMUX_ST_RELEASED;
+			if (vp->ontime == jiffies) {
+				/* if note-off is sent too shortly after
+				 * note-on, emuX engine cannot produce the sound
+				 * correctly.  so we'll release this note
+				 * a bit later via timer callback.
+				 */
+				vp->state = SNDRV_EMUX_ST_PENDING;
+				if (! emu->timer_active) {
+					emu->tlist.expires = jiffies + 1;
+					add_timer(&emu->tlist);
+					emu->timer_active = 1;
+				}
+			} else
+				/* ok now release the note */
+				emu->ops.release(vp);
+		}
+	}
+	spin_unlock_irqrestore(&emu->voice_lock, flags);
+}
+
+/*
+ * timer callback
+ *
+ * release the pending note-offs
+ */
+void snd_emux_timer_callback(unsigned long data)
+{
+	struct snd_emux *emu = (struct snd_emux *) data;
+	struct snd_emux_voice *vp;
+	int ch, do_again = 0;
+
+	spin_lock(&emu->voice_lock);
+	for (ch = 0; ch < emu->max_voices; ch++) {
+		vp = &emu->voices[ch];
+		if (vp->state == SNDRV_EMUX_ST_PENDING) {
+			if (vp->ontime == jiffies)
+				do_again++; /* release this at the next interrupt */
+			else {
+				emu->ops.release(vp);
+				vp->state = SNDRV_EMUX_ST_RELEASED;
+			}
+		}
+	}
+	if (do_again) {
+		emu->tlist.expires = jiffies + 1;
+		add_timer(&emu->tlist);
+		emu->timer_active = 1;
+	} else
+		emu->timer_active = 0;
+	spin_unlock(&emu->voice_lock);
+}
+
+/*
+ * key pressure change
+ */
+void
+snd_emux_key_press(void *p, int note, int vel, struct snd_midi_channel *chan)
+{
+	int ch;
+	struct snd_emux *emu;
+	struct snd_emux_voice *vp;
+	unsigned long flags;
+	struct snd_emux_port *port;
+
+	port = p;
+	snd_assert(port != NULL && chan != NULL, return);
+
+	emu = port->emu;
+	snd_assert(emu != NULL, return);
+	snd_assert(emu->ops.update != NULL, return);
+
+	spin_lock_irqsave(&emu->voice_lock, flags);
+	for (ch = 0; ch < emu->max_voices; ch++) {
+		vp = &emu->voices[ch];
+		if (vp->state == SNDRV_EMUX_ST_ON &&
+		    vp->chan == chan && vp->key == note) {
+			vp->velocity = vel;
+			update_voice(emu, vp, SNDRV_EMUX_UPDATE_VOLUME);
+		}
+	}
+	spin_unlock_irqrestore(&emu->voice_lock, flags);
+}
+
+
+/*
+ * Modulate the voices which belong to the channel
+ */
+void
+snd_emux_update_channel(struct snd_emux_port *port, struct snd_midi_channel *chan, int update)
+{
+	struct snd_emux *emu;
+	struct snd_emux_voice *vp;
+	int i;
+	unsigned long flags;
+
+	if (! update)
+		return;
+
+	emu = port->emu;
+	snd_assert(emu != NULL, return);
+	snd_assert(emu->ops.update != NULL, return);
+
+	spin_lock_irqsave(&emu->voice_lock, flags);
+	for (i = 0; i < emu->max_voices; i++) {
+		vp = &emu->voices[i];
+		if (vp->chan == chan)
+			update_voice(emu, vp, update);
+	}
+	spin_unlock_irqrestore(&emu->voice_lock, flags);
+}
+
+/*
+ * Modulate all the voices which belong to the port.
+ */
+void
+snd_emux_update_port(struct snd_emux_port *port, int update)
+{
+	struct snd_emux *emu; 
+	struct snd_emux_voice *vp;
+	int i;
+	unsigned long flags;
+
+	if (! update)
+		return;
+
+	emu = port->emu;
+	snd_assert(emu != NULL, return);
+	snd_assert(emu->ops.update != NULL, return);
+
+	spin_lock_irqsave(&emu->voice_lock, flags);
+	for (i = 0; i < emu->max_voices; i++) {
+		vp = &emu->voices[i];
+		if (vp->port == port)
+			update_voice(emu, vp, update);
+	}
+	spin_unlock_irqrestore(&emu->voice_lock, flags);
+}
+
+
+/*
+ * Deal with a controler type event.  This includes all types of
+ * control events, not just the midi controllers
+ */
+void
+snd_emux_control(void *p, int type, struct snd_midi_channel *chan)
+{
+	struct snd_emux_port *port;
+
+	port = p;
+	snd_assert(port != NULL && chan != NULL, return);
+
+	switch (type) {
+	case MIDI_CTL_MSB_MAIN_VOLUME:
+	case MIDI_CTL_MSB_EXPRESSION:
+		snd_emux_update_channel(port, chan, SNDRV_EMUX_UPDATE_VOLUME);
+		break;
+		
+	case MIDI_CTL_MSB_PAN:
+		snd_emux_update_channel(port, chan, SNDRV_EMUX_UPDATE_PAN);
+		break;
+
+	case MIDI_CTL_SOFT_PEDAL:
+#ifdef SNDRV_EMUX_USE_RAW_EFFECT
+		/* FIXME: this is an emulation */
+		snd_emux_send_effect(port, chan, EMUX_FX_CUTOFF, -160,
+				     EMUX_FX_FLAG_ADD);
+#endif
+		break;
+
+	case MIDI_CTL_PITCHBEND:
+		snd_emux_update_channel(port, chan, SNDRV_EMUX_UPDATE_PITCH);
+		break;
+
+	case MIDI_CTL_MSB_MODWHEEL:
+	case MIDI_CTL_CHAN_PRESSURE:
+		snd_emux_update_channel(port, chan,
+					SNDRV_EMUX_UPDATE_FMMOD |
+					SNDRV_EMUX_UPDATE_FM2FRQ2);
+		break;
+
+	}
+
+	if (port->chset.midi_mode == SNDRV_MIDI_MODE_XG) {
+		snd_emux_xg_control(port, chan, type);
+	}
+}
+
+
+/*
+ * terminate note - if free flag is true, free the terminated voice
+ */
+static void
+terminate_note1(struct snd_emux *emu, int note, struct snd_midi_channel *chan, int free)
+{
+	int  i;
+	struct snd_emux_voice *vp;
+	unsigned long flags;
+
+	spin_lock_irqsave(&emu->voice_lock, flags);
+	for (i = 0; i < emu->max_voices; i++) {
+		vp = &emu->voices[i];
+		if (STATE_IS_PLAYING(vp->state) && vp->chan == chan &&
+		    vp->key == note)
+			terminate_voice(emu, vp, free);
+	}
+	spin_unlock_irqrestore(&emu->voice_lock, flags);
+}
+
+
+/*
+ * terminate note - exported for midi emulation
+ */
+void
+snd_emux_terminate_note(void *p, int note, struct snd_midi_channel *chan)
+{
+	struct snd_emux *emu;
+	struct snd_emux_port *port;
+
+	port = p;
+	snd_assert(port != NULL && chan != NULL, return);
+
+	emu = port->emu;
+	snd_assert(emu != NULL, return);
+	snd_assert(emu->ops.terminate != NULL, return);
+
+	terminate_note1(emu, note, chan, 1);
+}
+
+
+/*
+ * Terminate all the notes
+ */
+void
+snd_emux_terminate_all(struct snd_emux *emu)
+{
+	int i;
+	struct snd_emux_voice *vp;
+	unsigned long flags;
+
+	spin_lock_irqsave(&emu->voice_lock, flags);
+	for (i = 0; i < emu->max_voices; i++) {
+		vp = &emu->voices[i];
+		if (STATE_IS_PLAYING(vp->state))
+			terminate_voice(emu, vp, 0);
+		if (vp->state == SNDRV_EMUX_ST_OFF) {
+			if (emu->ops.free_voice)
+				emu->ops.free_voice(vp);
+			if (emu->ops.reset)
+				emu->ops.reset(emu, i);
+		}
+		vp->time = 0;
+	}
+	/* initialize allocation time */
+	emu->use_time = 0;
+	spin_unlock_irqrestore(&emu->voice_lock, flags);
+}
+
+
+/*
+ * Terminate all voices associated with the given port
+ */
+void
+snd_emux_sounds_off_all(struct snd_emux_port *port)
+{
+	int i;
+	struct snd_emux *emu;
+	struct snd_emux_voice *vp;
+	unsigned long flags;
+
+	snd_assert(port != NULL, return);
+	emu = port->emu;
+	snd_assert(emu != NULL, return);
+	snd_assert(emu->ops.terminate != NULL, return);
+
+	spin_lock_irqsave(&emu->voice_lock, flags);
+	for (i = 0; i < emu->max_voices; i++) {
+		vp = &emu->voices[i];
+		if (STATE_IS_PLAYING(vp->state) &&
+		    vp->port == port)
+			terminate_voice(emu, vp, 0);
+		if (vp->state == SNDRV_EMUX_ST_OFF) {
+			if (emu->ops.free_voice)
+				emu->ops.free_voice(vp);
+			if (emu->ops.reset)
+				emu->ops.reset(emu, i);
+		}
+	}
+	spin_unlock_irqrestore(&emu->voice_lock, flags);
+}
+
+
+/*
+ * Terminate all voices that have the same exclusive class.  This
+ * is mainly for drums.
+ */
+static void
+exclusive_note_off(struct snd_emux *emu, struct snd_emux_port *port, int exclass)
+{
+	struct snd_emux_voice *vp;
+	int  i;
+	unsigned long flags;
+
+	spin_lock_irqsave(&emu->voice_lock, flags);
+	for (i = 0; i < emu->max_voices; i++) {
+		vp = &emu->voices[i];
+		if (STATE_IS_PLAYING(vp->state) && vp->port == port &&
+		    vp->reg.exclusiveClass == exclass) {
+			terminate_voice(emu, vp, 0);
+		}
+	}
+	spin_unlock_irqrestore(&emu->voice_lock, flags);
+}
+
+/*
+ * terminate a voice
+ * if free flag is true, call free_voice after termination
+ */
+static void
+terminate_voice(struct snd_emux *emu, struct snd_emux_voice *vp, int free)
+{
+	emu->ops.terminate(vp);
+	vp->time = emu->use_time++;
+	vp->chan = NULL;
+	vp->port = NULL;
+	vp->zone = NULL;
+	vp->block = NULL;
+	vp->state = SNDRV_EMUX_ST_OFF;
+	if (free && emu->ops.free_voice)
+		emu->ops.free_voice(vp);
+}
+
+
+/*
+ * Modulate the voice
+ */
+static void
+update_voice(struct snd_emux *emu, struct snd_emux_voice *vp, int update)
+{
+	if (!STATE_IS_PLAYING(vp->state))
+		return;
+
+	if (vp->chan == NULL || vp->port == NULL)
+		return;
+	if (update & SNDRV_EMUX_UPDATE_VOLUME)
+		calc_volume(vp);
+	if (update & SNDRV_EMUX_UPDATE_PITCH)
+		calc_pitch(vp);
+	if (update & SNDRV_EMUX_UPDATE_PAN) {
+		if (! calc_pan(vp) && (update == SNDRV_EMUX_UPDATE_PAN))
+			return;
+	}
+	emu->ops.update(vp, update);
+}
+
+
+#if 0 // not used
+/* table for volume target calculation */
+static unsigned short voltarget[16] = { 
+	0xEAC0, 0xE0C8, 0xD740, 0xCE20, 0xC560, 0xBD08, 0xB500, 0xAD58,
+	0xA5F8, 0x9EF0, 0x9830, 0x91C0, 0x8B90, 0x85A8, 0x8000, 0x7A90
+};
+#endif
+
+#define LO_BYTE(v)	((v) & 0xff)
+#define HI_BYTE(v)	(((v) >> 8) & 0xff)
+
+/*
+ * Sets up the voice structure by calculating some values that
+ * will be needed later.
+ */
+static void
+setup_voice(struct snd_emux_voice *vp)
+{
+	struct soundfont_voice_parm *parm;
+	int pitch;
+
+	/* copy the original register values */
+	vp->reg = vp->zone->v;
+
+#ifdef SNDRV_EMUX_USE_RAW_EFFECT
+	snd_emux_setup_effect(vp);
+#endif
+
+	/* reset status */
+	vp->apan = -1;
+	vp->avol = -1;
+	vp->apitch = -1;
+
+	calc_volume(vp);
+	calc_pitch(vp);
+	calc_pan(vp);
+
+	parm = &vp->reg.parm;
+
+	/* compute filter target and correct modulation parameters */
+	if (LO_BYTE(parm->modatkhld) >= 0x80 && parm->moddelay >= 0x8000) {
+		parm->moddelay = 0xbfff;
+		pitch = (HI_BYTE(parm->pefe) << 4) + vp->apitch;
+		if (pitch > 0xffff)
+			pitch = 0xffff;
+		/* calculate filter target */
+		vp->ftarget = parm->cutoff + LO_BYTE(parm->pefe);
+		LIMITVALUE(vp->ftarget, 0, 255);
+		vp->ftarget <<= 8;
+	} else {
+		vp->ftarget = parm->cutoff;
+		vp->ftarget <<= 8;
+		pitch = vp->apitch;
+	}
+
+	/* compute pitch target */
+	if (pitch != 0xffff) {
+		vp->ptarget = 1 << (pitch >> 12);
+		if (pitch & 0x800) vp->ptarget += (vp->ptarget*0x102e)/0x2710;
+		if (pitch & 0x400) vp->ptarget += (vp->ptarget*0x764)/0x2710;
+		if (pitch & 0x200) vp->ptarget += (vp->ptarget*0x389)/0x2710;
+		vp->ptarget += (vp->ptarget >> 1);
+		if (vp->ptarget > 0xffff) vp->ptarget = 0xffff;
+	} else
+		vp->ptarget = 0xffff;
+
+	if (LO_BYTE(parm->modatkhld) >= 0x80) {
+		parm->modatkhld &= ~0xff;
+		parm->modatkhld |= 0x7f;
+	}
+
+	/* compute volume target and correct volume parameters */
+	vp->vtarget = 0;
+#if 0 /* FIXME: this leads to some clicks.. */
+	if (LO_BYTE(parm->volatkhld) >= 0x80 && parm->voldelay >= 0x8000) {
+		parm->voldelay = 0xbfff;
+		vp->vtarget = voltarget[vp->avol % 0x10] >> (vp->avol >> 4);
+	}
+#endif
+
+	if (LO_BYTE(parm->volatkhld) >= 0x80) {
+		parm->volatkhld &= ~0xff;
+		parm->volatkhld |= 0x7f;
+	}
+}
+
+/*
+ * calculate pitch parameter
+ */
+static unsigned char pan_volumes[256] = {
+0x00,0x03,0x06,0x09,0x0c,0x0f,0x12,0x14,0x17,0x1a,0x1d,0x20,0x22,0x25,0x28,0x2a,
+0x2d,0x30,0x32,0x35,0x37,0x3a,0x3c,0x3f,0x41,0x44,0x46,0x49,0x4b,0x4d,0x50,0x52,
+0x54,0x57,0x59,0x5b,0x5d,0x60,0x62,0x64,0x66,0x68,0x6a,0x6c,0x6f,0x71,0x73,0x75,
+0x77,0x79,0x7b,0x7c,0x7e,0x80,0x82,0x84,0x86,0x88,0x89,0x8b,0x8d,0x8f,0x90,0x92,
+0x94,0x96,0x97,0x99,0x9a,0x9c,0x9e,0x9f,0xa1,0xa2,0xa4,0xa5,0xa7,0xa8,0xaa,0xab,
+0xad,0xae,0xaf,0xb1,0xb2,0xb3,0xb5,0xb6,0xb7,0xb9,0xba,0xbb,0xbc,0xbe,0xbf,0xc0,
+0xc1,0xc2,0xc3,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xcb,0xcc,0xcd,0xce,0xcf,0xd0,0xd1,
+0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd7,0xd8,0xd9,0xda,0xdb,0xdc,0xdc,0xdd,0xde,0xdf,
+0xdf,0xe0,0xe1,0xe2,0xe2,0xe3,0xe4,0xe4,0xe5,0xe6,0xe6,0xe7,0xe8,0xe8,0xe9,0xe9,
+0xea,0xeb,0xeb,0xec,0xec,0xed,0xed,0xee,0xee,0xef,0xef,0xf0,0xf0,0xf1,0xf1,0xf1,
+0xf2,0xf2,0xf3,0xf3,0xf3,0xf4,0xf4,0xf5,0xf5,0xf5,0xf6,0xf6,0xf6,0xf7,0xf7,0xf7,
+0xf7,0xf8,0xf8,0xf8,0xf9,0xf9,0xf9,0xf9,0xf9,0xfa,0xfa,0xfa,0xfa,0xfb,0xfb,0xfb,
+0xfb,0xfb,0xfc,0xfc,0xfc,0xfc,0xfc,0xfc,0xfc,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,
+0xfd,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+};
+
+static int
+calc_pan(struct snd_emux_voice *vp)
+{
+	struct snd_midi_channel *chan = vp->chan;
+	int pan;
+
+	/* pan & loop start (pan 8bit, MSB, 0:right, 0xff:left) */
+	if (vp->reg.fixpan > 0)	/* 0-127 */
+		pan = 255 - (int)vp->reg.fixpan * 2;
+	else {
+		pan = chan->control[MIDI_CTL_MSB_PAN] - 64;
+		if (vp->reg.pan >= 0) /* 0-127 */
+			pan += vp->reg.pan - 64;
+		pan = 127 - (int)pan * 2;
+	}
+	LIMITVALUE(pan, 0, 255);
+
+	if (vp->emu->linear_panning) {
+		/* assuming linear volume */
+		if (pan != vp->apan) {
+			vp->apan = pan;
+			if (pan == 0)
+				vp->aaux = 0xff;
+			else
+				vp->aaux = (-pan) & 0xff;
+			return 1;
+		} else
+			return 0;
+	} else {
+		/* using volume table */
+		if (vp->apan != (int)pan_volumes[pan]) {
+			vp->apan = pan_volumes[pan];
+			vp->aaux = pan_volumes[255 - pan];
+			return 1;
+		}
+		return 0;
+	}
+}
+
+
+/*
+ * calculate volume attenuation
+ *
+ * Voice volume is controlled by volume attenuation parameter.
+ * So volume becomes maximum when avol is 0 (no attenuation), and
+ * minimum when 255 (-96dB or silence).
+ */
+
+/* tables for volume->attenuation calculation */
+static unsigned char voltab1[128] = {
+   0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+   0x63, 0x2b, 0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22,
+   0x21, 0x20, 0x1f, 0x1e, 0x1e, 0x1d, 0x1c, 0x1b, 0x1b, 0x1a,
+   0x19, 0x19, 0x18, 0x17, 0x17, 0x16, 0x16, 0x15, 0x15, 0x14,
+   0x14, 0x13, 0x13, 0x13, 0x12, 0x12, 0x11, 0x11, 0x11, 0x10,
+   0x10, 0x10, 0x0f, 0x0f, 0x0f, 0x0e, 0x0e, 0x0e, 0x0e, 0x0d,
+   0x0d, 0x0d, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0b, 0x0b, 0x0b,
+   0x0b, 0x0a, 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09,
+   0x08, 0x08, 0x08, 0x08, 0x08, 0x07, 0x07, 0x07, 0x07, 0x06,
+   0x06, 0x06, 0x06, 0x06, 0x05, 0x05, 0x05, 0x05, 0x05, 0x04,
+   0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02,
+   0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01,
+   0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static unsigned char voltab2[128] = {
+   0x32, 0x31, 0x30, 0x2f, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x2a,
+   0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x24, 0x23, 0x22, 0x21,
+   0x21, 0x20, 0x1f, 0x1e, 0x1e, 0x1d, 0x1c, 0x1c, 0x1b, 0x1a,
+   0x1a, 0x19, 0x19, 0x18, 0x18, 0x17, 0x16, 0x16, 0x15, 0x15,
+   0x14, 0x14, 0x13, 0x13, 0x13, 0x12, 0x12, 0x11, 0x11, 0x10,
+   0x10, 0x10, 0x0f, 0x0f, 0x0f, 0x0e, 0x0e, 0x0e, 0x0d, 0x0d,
+   0x0d, 0x0c, 0x0c, 0x0c, 0x0b, 0x0b, 0x0b, 0x0b, 0x0a, 0x0a,
+   0x0a, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x08, 0x08, 0x08,
+   0x08, 0x08, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x06, 0x06,
+   0x06, 0x06, 0x06, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03,
+   0x03, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01,
+   0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static unsigned char expressiontab[128] = {
+   0x7f, 0x6c, 0x62, 0x5a, 0x54, 0x50, 0x4b, 0x48, 0x45, 0x42,
+   0x40, 0x3d, 0x3b, 0x39, 0x38, 0x36, 0x34, 0x33, 0x31, 0x30,
+   0x2f, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x27, 0x26, 0x25,
+   0x24, 0x24, 0x23, 0x22, 0x21, 0x21, 0x20, 0x1f, 0x1e, 0x1e,
+   0x1d, 0x1d, 0x1c, 0x1b, 0x1b, 0x1a, 0x1a, 0x19, 0x18, 0x18,
+   0x17, 0x17, 0x16, 0x16, 0x15, 0x15, 0x15, 0x14, 0x14, 0x13,
+   0x13, 0x12, 0x12, 0x11, 0x11, 0x11, 0x10, 0x10, 0x0f, 0x0f,
+   0x0f, 0x0e, 0x0e, 0x0e, 0x0d, 0x0d, 0x0d, 0x0c, 0x0c, 0x0c,
+   0x0b, 0x0b, 0x0b, 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x09, 0x09,
+   0x08, 0x08, 0x08, 0x07, 0x07, 0x07, 0x07, 0x06, 0x06, 0x06,
+   0x06, 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03,
+   0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01,
+   0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/*
+ * Magic to calculate the volume (actually attenuation) from all the
+ * voice and channels parameters.
+ */
+static int
+calc_volume(struct snd_emux_voice *vp)
+{
+	int vol;
+	int main_vol, expression_vol, master_vol;
+	struct snd_midi_channel *chan = vp->chan;
+	struct snd_emux_port *port = vp->port;
+
+	expression_vol = chan->control[MIDI_CTL_MSB_EXPRESSION];
+	LIMITMAX(vp->velocity, 127);
+	LIMITVALUE(expression_vol, 0, 127);
+	if (port->port_mode == SNDRV_EMUX_PORT_MODE_OSS_SYNTH) {
+		/* 0 - 127 */
+		main_vol = chan->control[MIDI_CTL_MSB_MAIN_VOLUME];
+		vol = (vp->velocity * main_vol * expression_vol) / (127*127);
+		vol = vol * vp->reg.amplitude / 127;
+
+		LIMITVALUE(vol, 0, 127);
+
+		/* calc to attenuation */
+		vol = snd_sf_vol_table[vol];
+
+	} else {
+		main_vol = chan->control[MIDI_CTL_MSB_MAIN_VOLUME] * vp->reg.amplitude / 127;
+		LIMITVALUE(main_vol, 0, 127);
+
+		vol = voltab1[main_vol] + voltab2[vp->velocity];
+		vol = (vol * 8) / 3;
+		vol += vp->reg.attenuation;
+		vol += ((0x100 - vol) * expressiontab[expression_vol])/128;
+	}
+
+	master_vol = port->chset.gs_master_volume;
+	LIMITVALUE(master_vol, 0, 127);
+	vol += snd_sf_vol_table[master_vol];
+	vol += port->volume_atten;
+
+#ifdef SNDRV_EMUX_USE_RAW_EFFECT
+	if (chan->private) {
+		struct snd_emux_effect_table *fx = chan->private;
+		vol += fx->val[EMUX_FX_ATTEN];
+	}
+#endif
+
+	LIMITVALUE(vol, 0, 255);
+	if (vp->avol == vol)
+		return 0; /* value unchanged */
+
+	vp->avol = vol;
+	if (!SF_IS_DRUM_BANK(get_bank(port, chan))
+	    && LO_BYTE(vp->reg.parm.volatkhld) < 0x7d) {
+		int atten;
+		if (vp->velocity < 70)
+			atten = 70;
+		else
+			atten = vp->velocity;
+		vp->acutoff = (atten * vp->reg.parm.cutoff + 0xa0) >> 7;
+	} else {
+		vp->acutoff = vp->reg.parm.cutoff;
+	}
+
+	return 1; /* value changed */
+}
+
+/*
+ * calculate pitch offset
+ *
+ * 0xE000 is no pitch offset at 44100Hz sample.
+ * Every 4096 is one octave.
+ */
+
+static int
+calc_pitch(struct snd_emux_voice *vp)
+{
+	struct snd_midi_channel *chan = vp->chan;
+	int offset;
+
+	/* calculate offset */
+	if (vp->reg.fixkey >= 0) {
+		offset = (vp->reg.fixkey - vp->reg.root) * 4096 / 12;
+	} else {
+		offset = (vp->note - vp->reg.root) * 4096 / 12;
+	}
+	offset = (offset * vp->reg.scaleTuning) / 100;
+	offset += vp->reg.tune * 4096 / 1200;
+	if (chan->midi_pitchbend != 0) {
+		/* (128 * 8192: 1 semitone) ==> (4096: 12 semitones) */
+		offset += chan->midi_pitchbend * chan->gm_rpn_pitch_bend_range / 3072;
+	}
+
+	/* tuning via RPN:
+	 *   coarse = -8192 to 8192 (100 cent per 128)
+	 *   fine = -8192 to 8192 (max=100cent)
+	 */
+	/* 4096 = 1200 cents in emu8000 parameter */
+	offset += chan->gm_rpn_coarse_tuning * 4096 / (12 * 128);
+	offset += chan->gm_rpn_fine_tuning / 24;
+
+#ifdef SNDRV_EMUX_USE_RAW_EFFECT
+	/* add initial pitch correction */
+	if (chan->private) {
+		struct snd_emux_effect_table *fx = chan->private;
+		if (fx->flag[EMUX_FX_INIT_PITCH])
+			offset += fx->val[EMUX_FX_INIT_PITCH];
+	}
+#endif
+
+	/* 0xe000: root pitch */
+	offset += 0xe000 + vp->reg.rate_offset;
+	offset += vp->emu->pitch_shift;
+	LIMITVALUE(offset, 0, 0xffff);
+	if (offset == vp->apitch)
+		return 0; /* unchanged */
+	vp->apitch = offset;
+	return 1; /* value changed */
+}
+
+/*
+ * Get the bank number assigned to the channel
+ */
+static int
+get_bank(struct snd_emux_port *port, struct snd_midi_channel *chan)
+{
+	int val;
+
+	switch (port->chset.midi_mode) {
+	case SNDRV_MIDI_MODE_XG:
+		val = chan->control[MIDI_CTL_MSB_BANK];
+		if (val == 127)
+			return 128; /* return drum bank */
+		return chan->control[MIDI_CTL_LSB_BANK];
+
+	case SNDRV_MIDI_MODE_GS:
+		if (chan->drum_channel)
+			return 128;
+		/* ignore LSB (bank map) */
+		return chan->control[MIDI_CTL_MSB_BANK];
+		
+	default:
+		if (chan->drum_channel)
+			return 128;
+		return chan->control[MIDI_CTL_MSB_BANK];
+	}
+}
+
+
+/* Look for the zones matching with the given note and velocity.
+ * The resultant zones are stored on table.
+ */
+static int
+get_zone(struct snd_emux *emu, struct snd_emux_port *port,
+	 int *notep, int vel, struct snd_midi_channel *chan,
+	 struct snd_sf_zone **table)
+{
+	int preset, bank, def_preset, def_bank;
+
+	bank = get_bank(port, chan);
+	preset = chan->midi_program;
+
+	if (SF_IS_DRUM_BANK(bank)) {
+		def_preset = port->ctrls[EMUX_MD_DEF_DRUM];
+		def_bank = bank;
+	} else {
+		def_preset = preset;
+		def_bank = port->ctrls[EMUX_MD_DEF_BANK];
+	}
+
+	return snd_soundfont_search_zone(emu->sflist, notep, vel, preset, bank,
+					 def_preset, def_bank,
+					 table, SNDRV_EMUX_MAX_MULTI_VOICES);
+}
+
+/*
+ */
+void
+snd_emux_init_voices(struct snd_emux *emu)
+{
+	struct snd_emux_voice *vp;
+	int i;
+	unsigned long flags;
+
+	spin_lock_irqsave(&emu->voice_lock, flags);
+	for (i = 0; i < emu->max_voices; i++) {
+		vp = &emu->voices[i];
+		vp->ch = -1; /* not used */
+		vp->state = SNDRV_EMUX_ST_OFF;
+		vp->chan = NULL;
+		vp->port = NULL;
+		vp->time = 0;
+		vp->emu = emu;
+		vp->hw = emu->hw;
+	}
+	spin_unlock_irqrestore(&emu->voice_lock, flags);
+}
+
+/*
+ */
+void snd_emux_lock_voice(struct snd_emux *emu, int voice)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&emu->voice_lock, flags);
+	if (emu->voices[voice].state == SNDRV_EMUX_ST_OFF)
+		emu->voices[voice].state = SNDRV_EMUX_ST_LOCKED;
+	else
+		snd_printk("invalid voice for lock %d (state = %x)\n",
+			   voice, emu->voices[voice].state);
+	spin_unlock_irqrestore(&emu->voice_lock, flags);
+}
+
+/*
+ */
+void snd_emux_unlock_voice(struct snd_emux *emu, int voice)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&emu->voice_lock, flags);
+	if (emu->voices[voice].state == SNDRV_EMUX_ST_LOCKED)
+		emu->voices[voice].state = SNDRV_EMUX_ST_OFF;
+	else
+		snd_printk("invalid voice for unlock %d (state = %x)\n",
+			   voice, emu->voices[voice].state);
+	spin_unlock_irqrestore(&emu->voice_lock, flags);
+}
Index: /GPL/branches/alsa-resync1/alsa-kernel/synth/emux/emux_voice.h
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/synth/emux/emux_voice.h	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/synth/emux/emux_voice.h	(revision 91)
@@ -0,0 +1,97 @@
+#ifndef __EMUX_VOICE_H
+#define __EMUX_VOICE_H
+
+/*
+ * A structure to keep track of each hardware voice
+ *
+ *  Copyright (C) 1999 Steve Ratcliffe
+ *  Copyright (c) 1999-2000 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 <linux/wait.h>
+#include <linux/sched.h>
+#include <sound/core.h>
+#include <sound/emux_synth.h>
+
+/* Prototypes for emux_seq.c */
+int snd_emux_init_seq(struct snd_emux *emu, struct snd_card *card, int index);
+void snd_emux_detach_seq(struct snd_emux *emu);
+struct snd_emux_port *snd_emux_create_port(struct snd_emux *emu, char *name,
+					   int max_channels, int type,
+					   struct snd_seq_port_callback *callback);
+void snd_emux_reset_port(struct snd_emux_port *port);
+int snd_emux_event_input(struct snd_seq_event *ev, int direct, void *private,
+			 int atomic, int hop);
+int snd_emux_inc_count(struct snd_emux *emu);
+void snd_emux_dec_count(struct snd_emux *emu);
+int snd_emux_init_virmidi(struct snd_emux *emu, struct snd_card *card);
+int snd_emux_delete_virmidi(struct snd_emux *emu);
+
+/* Prototypes for emux_synth.c */
+void snd_emux_init_voices(struct snd_emux *emu);
+
+void snd_emux_note_on(void *p, int note, int vel, struct snd_midi_channel *chan);
+void snd_emux_note_off(void *p, int note, int vel, struct snd_midi_channel *chan);
+void snd_emux_key_press(void *p, int note, int vel, struct snd_midi_channel *chan);
+void snd_emux_terminate_note(void *p, int note, struct snd_midi_channel *chan);
+void snd_emux_control(void *p, int type, struct snd_midi_channel *chan);
+
+void snd_emux_sounds_off_all(struct snd_emux_port *port);
+void snd_emux_update_channel(struct snd_emux_port *port,
+			     struct snd_midi_channel *chan, int update);
+void snd_emux_update_port(struct snd_emux_port *port, int update);
+
+void snd_emux_timer_callback(unsigned long data);
+
+/* emux_effect.c */
+#ifdef SNDRV_EMUX_USE_RAW_EFFECT
+void snd_emux_create_effect(struct snd_emux_port *p);
+void snd_emux_delete_effect(struct snd_emux_port *p);
+void snd_emux_clear_effect(struct snd_emux_port *p);
+void snd_emux_setup_effect(struct snd_emux_voice *vp);
+void snd_emux_send_effect_oss(struct snd_emux_port *port,
+			      struct snd_midi_channel *chan, int type, int val);
+void snd_emux_send_effect(struct snd_emux_port *port,
+			  struct snd_midi_channel *chan, int type, int val, int mode);
+#endif
+
+/* emux_nrpn.c */
+void snd_emux_sysex(void *private_data, unsigned char *buf, int len,
+		    int parsed, struct snd_midi_channel_set *chset);
+int snd_emux_xg_control(struct snd_emux_port *port,
+			struct snd_midi_channel *chan, int param);
+void snd_emux_nrpn(void *private_data, struct snd_midi_channel *chan,
+		   struct snd_midi_channel_set *chset);
+
+/* emux_oss.c */
+void snd_emux_init_seq_oss(struct snd_emux *emu);
+void snd_emux_detach_seq_oss(struct snd_emux *emu);
+
+/* emux_proc.c */
+#ifdef CONFIG_PROC_FS
+void snd_emux_proc_init(struct snd_emux *emu, struct snd_card *card, int device);
+void snd_emux_proc_free(struct snd_emux *emu);
+#endif
+
+#define STATE_IS_PLAYING(s) ((s) & SNDRV_EMUX_ST_ON)
+
+/* emux_hwdep.c */
+int snd_emux_init_hwdep(struct snd_emux *emu);
+void snd_emux_delete_hwdep(struct snd_emux *emu);
+
+#endif
Index: /GPL/branches/alsa-resync1/alsa-kernel/synth/emux/makefile.os2
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/synth/emux/makefile.os2	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/synth/emux/makefile.os2	(revision 91)
@@ -0,0 +1,52 @@
+CINCLUDES = ..\..\..\include;..\..\include;$(%WATCOM)\H
+AINCLUDES = $(DDK_BASE)\H
+
+CDEFINES = -D__KERNEL__ -DMODULE -dTARGET_OS2 -dALSA_BUILD -D__ISAPNP__ -DCONFIG_ISAPNP -D__i386_
+
+32BIT=1
+!include ..\..\..\..\makefile.inc
+
+
+#===================================================================
+#
+#   List of source files
+#
+#===================================================================
+FILE1    = emux.obj emux_synth.obj emux_seq.obj emux_nrpn.obj
+FILE2    = emux_effect.obj emux_proc.obj soundfont.obj
+FILE3    = 
+FILE4    = 
+FILE5    = 
+FILE6    = 
+FILE7    = 
+FILE8    = 
+FILELAST =
+FILES    = $(FILE0) $(FILE1) $(FILE2) $(FILE3) $(FILE4) $(FILE5) $(FILE6) $(FILE7) $(FILE8) $(FILE9) $(FILE10) $(FILE11) $(FILE12)
+
+TARGET   = emux
+
+LNKFILE  = $(OBJDIR)\$(TARGET).lnk
+
+
+#===================================================================
+#
+#   Specific dependencies
+#
+#===================================================================
+all: $(OBJDIR)\$(TARGET).lib
+
+
+$(LNKFILE): makefile.os2
+    @%create $(LNKFILE)
+    @for %f in ($(FILES)) do @%append $(LNKFILE) -+$(OBJDIR)\%f
+
+$(OBJDIR)\$(TARGET).lib: $(LNKFILE) $(FILES)
+    $(LIB) $(OBJDIR)\$(TARGET).lib @$(LNKFILE)
+    copy $(OBJDIR)\$(TARGET).lib $(ALSA_LIB)
+
+
+clean: .SYMBOLIC
+     -@rm *.err 2>nul
+     @cd $(OBJDIR)
+     -rm *.obj *.lnk *.sys *.sym *.lst *.map *.wmap *.lib 2>nul
+     @cd ..\..
Index: /GPL/branches/alsa-resync1/alsa-kernel/synth/emux/soundfont.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/synth/emux/soundfont.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/synth/emux/soundfont.c	(revision 91)
@@ -0,0 +1,1488 @@
+/*
+ *  Soundfont generic routines.
+ *	It is intended that these should be used by any driver that is willing
+ *	to accept soundfont patches.
+ *
+ *  Copyright (C) 1999 Steve Ratcliffe
+ *  Copyright (c) 1999-2000 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
+ */
+/*
+ * Deal with reading in of a soundfont.  Code follows the OSS way
+ * of doing things so that the old sfxload utility can be used.
+ * Everything may change when there is an alsa way of doing things.
+ */
+#include <sound/driver.h>
+#include <asm/uaccess.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/soundfont.h>
+#include <sound/seq_oss_legacy.h>
+
+/* Prototypes for static functions */
+
+static int open_patch(struct snd_sf_list *sflist, const char __user *data,
+		      int count, int client);
+static struct snd_soundfont *newsf(struct snd_sf_list *sflist, int type, char *name);
+static int is_identical_font(struct snd_soundfont *sf, int type, unsigned char *name);
+static int close_patch(struct snd_sf_list *sflist);
+static int probe_data(struct snd_sf_list *sflist, int sample_id);
+static void set_zone_counter(struct snd_sf_list *sflist,
+			     struct snd_soundfont *sf, struct snd_sf_zone *zp);
+static struct snd_sf_zone *sf_zone_new(struct snd_sf_list *sflist,
+				       struct snd_soundfont *sf);
+static void set_sample_counter(struct snd_sf_list *sflist,
+			       struct snd_soundfont *sf, struct snd_sf_sample *sp);
+static struct snd_sf_sample *sf_sample_new(struct snd_sf_list *sflist,
+					   struct snd_soundfont *sf);
+static void sf_sample_delete(struct snd_sf_list *sflist,
+			     struct snd_soundfont *sf, struct snd_sf_sample *sp);
+static int load_map(struct snd_sf_list *sflist, const void __user *data, int count);
+static int load_info(struct snd_sf_list *sflist, const void __user *data, long count);
+static int remove_info(struct snd_sf_list *sflist, struct snd_soundfont *sf,
+		       int bank, int instr);
+static void init_voice_info(struct soundfont_voice_info *avp);
+static void init_voice_parm(struct soundfont_voice_parm *pp);
+static struct snd_sf_sample *set_sample(struct snd_soundfont *sf,
+					struct soundfont_voice_info *avp);
+static struct snd_sf_sample *find_sample(struct snd_soundfont *sf, int sample_id);
+static int load_data(struct snd_sf_list *sflist, const void __user *data, long count);
+static void rebuild_presets(struct snd_sf_list *sflist);
+static void add_preset(struct snd_sf_list *sflist, struct snd_sf_zone *cur);
+static void delete_preset(struct snd_sf_list *sflist, struct snd_sf_zone *zp);
+static struct snd_sf_zone *search_first_zone(struct snd_sf_list *sflist,
+					     int bank, int preset, int key);
+static int search_zones(struct snd_sf_list *sflist, int *notep, int vel,
+			int preset, int bank, struct snd_sf_zone **table,
+			int max_layers, int level);
+static int get_index(int bank, int instr, int key);
+static void snd_sf_init(struct snd_sf_list *sflist);
+static void snd_sf_clear(struct snd_sf_list *sflist);
+
+/*
+ * lock access to sflist
+ */
+static void
+lock_preset(struct snd_sf_list *sflist)
+{
+	unsigned long flags;
+	down(&sflist->presets_mutex);
+	spin_lock_irqsave(&sflist->lock, flags);
+	sflist->presets_locked = 1;
+	spin_unlock_irqrestore(&sflist->lock, flags);
+}
+
+
+/*
+ * remove lock
+ */
+static void
+unlock_preset(struct snd_sf_list *sflist)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&sflist->lock, flags);
+	sflist->presets_locked = 0;
+	spin_unlock_irqrestore(&sflist->lock, flags);
+	up(&sflist->presets_mutex);
+}
+
+
+/*
+ * close the patch if the patch was opened by this client.
+ */
+int
+snd_soundfont_close_check(struct snd_sf_list *sflist, int client)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&sflist->lock, flags);
+	if (sflist->open_client == client)  {
+		spin_unlock_irqrestore(&sflist->lock, flags);
+		return close_patch(sflist);
+	}
+	spin_unlock_irqrestore(&sflist->lock, flags);
+	return 0;
+}
+
+
+/*
+ * Deal with a soundfont patch.  Any driver could use these routines
+ * although it was designed for the AWE64.
+ *
+ * The sample_write and callargs pararameters allow a callback into
+ * the actual driver to write sample data to the board or whatever
+ * it wants to do with it.
+ */
+int
+snd_soundfont_load(struct snd_sf_list *sflist, const void __user *data,
+		   long count, int client)
+{
+	struct soundfont_patch_info patch;
+	unsigned long flags;
+	int  rc;
+
+	if (count < (long)sizeof(patch)) {
+		snd_printk("patch record too small %ld\n", count);
+		return -EINVAL;
+	}
+	if (copy_from_user(&patch, data, sizeof(patch)))
+		return -EFAULT;
+
+	count -= sizeof(patch);
+	(char*)data += sizeof(patch);
+
+	if (patch.key != SNDRV_OSS_SOUNDFONT_PATCH) {
+		snd_printk("'The wrong kind of patch' %x\n", patch.key);
+		return -EINVAL;
+	}
+	if (count < patch.len) {
+		snd_printk("Patch too short %ld, need %d\n", count, patch.len);
+		return -EINVAL;
+	}
+	if (patch.len < 0) {
+		snd_printk("poor length %d\n", patch.len);
+		return -EINVAL;
+	}
+
+	if (patch.type == SNDRV_SFNT_OPEN_PATCH) {
+		/* grab sflist to open */
+		lock_preset(sflist);
+		rc = open_patch(sflist, data, count, client);
+		unlock_preset(sflist);
+		return rc;
+	}
+
+	/* check if other client already opened patch */
+	spin_lock_irqsave(&sflist->lock, flags);
+	if (sflist->open_client != client) {
+		spin_unlock_irqrestore(&sflist->lock, flags);
+		return -EBUSY;
+	}
+	spin_unlock_irqrestore(&sflist->lock, flags);
+
+	lock_preset(sflist);
+	rc = -EINVAL;
+	switch (patch.type) {
+	case SNDRV_SFNT_LOAD_INFO:
+		rc = load_info(sflist, data, count);
+		break;
+	case SNDRV_SFNT_LOAD_DATA:
+		rc = load_data(sflist, data, count);
+		break;
+	case SNDRV_SFNT_CLOSE_PATCH:
+		rc = close_patch(sflist);
+		break;
+	case SNDRV_SFNT_REPLACE_DATA:
+		/*rc = replace_data(&patch, data, count);*/
+		break;
+	case SNDRV_SFNT_MAP_PRESET:
+		rc = load_map(sflist, data, count);
+		break;
+	case SNDRV_SFNT_PROBE_DATA:
+		rc = probe_data(sflist, patch.optarg);
+		break;
+	case SNDRV_SFNT_REMOVE_INFO:
+            /* patch must be opened */
+            if (!sflist->currsf) {
+                snd_printk("soundfont: remove_info: patch not opened\n");
+                rc = -EINVAL;
+            } else {
+                int bank, instr;
+                bank = ((unsigned short)patch.optarg >> 8) & 0xff;
+                instr = (unsigned short)patch.optarg & 0xff;
+                if (! remove_info(sflist, sflist->currsf, bank, instr))
+                    rc = -EINVAL;
+                else
+                    rc = 0;
+            }
+            break;
+	}
+	unlock_preset(sflist);
+
+	return rc;
+}
+
+
+/* check if specified type is special font (GUS or preset-alias) */
+static inline int
+is_special_type(int type)
+{
+	type &= 0x0f;
+	return (type == SNDRV_SFNT_PAT_TYPE_GUS ||
+		type == SNDRV_SFNT_PAT_TYPE_MAP);
+}
+
+
+/* open patch; create sf list */
+static int
+open_patch(struct snd_sf_list *sflist, const char __user *data,
+	   int count, int client)
+{
+	struct soundfont_open_parm parm;
+	struct snd_soundfont *sf;
+	unsigned long flags;
+
+	spin_lock_irqsave(&sflist->lock, flags);
+	if (sflist->open_client >= 0 || sflist->currsf) {
+		spin_unlock_irqrestore(&sflist->lock, flags);
+		return -EBUSY;
+	}
+	spin_unlock_irqrestore(&sflist->lock, flags);
+
+	if (copy_from_user(&parm, data, sizeof(parm)))
+		return -EFAULT;
+
+	if (is_special_type(parm.type)) {
+		parm.type |= SNDRV_SFNT_PAT_SHARED;
+		sf = newsf(sflist, parm.type, NULL);
+	} else 
+		sf = newsf(sflist, parm.type, parm.name);
+	if (sf == NULL) {
+		return -ENOMEM;
+	}
+
+	spin_lock_irqsave(&sflist->lock, flags);
+	sflist->open_client = client;
+	sflist->currsf = sf;
+	spin_unlock_irqrestore(&sflist->lock, flags);
+
+	return 0;
+}
+
+/*
+ * Allocate a new soundfont structure.
+ */
+static struct snd_soundfont *
+newsf(struct snd_sf_list *sflist, int type, char *name)
+{
+	struct snd_soundfont *sf;
+
+	/* check the shared fonts */
+	if (type & SNDRV_SFNT_PAT_SHARED) {
+		for (sf = sflist->fonts; sf; sf = sf->next) {
+			if (is_identical_font(sf, type, name)) {
+				return sf;
+			}
+		}
+	}
+
+	/* not found -- create a new one */
+	sf = kzalloc(sizeof(*sf), GFP_KERNEL);
+	if (sf == NULL)
+		return NULL;
+	sf->id = sflist->fonts_size;
+	sflist->fonts_size++;
+
+	/* prepend this record */
+	sf->next = sflist->fonts;
+	sflist->fonts = sf;
+
+	sf->type = type;
+	sf->zones = NULL;
+	sf->samples = NULL;
+	if (name)
+		memcpy(sf->name, name, SNDRV_SFNT_PATCH_NAME_LEN);
+
+	return sf;
+}
+
+/* check if the given name matches to the existing list */
+static int
+is_identical_font(struct snd_soundfont *sf, int type, unsigned char *name)
+{
+	return ((sf->type & SNDRV_SFNT_PAT_SHARED) &&
+		(sf->type & 0x0f) == (type & 0x0f) &&
+		(name == NULL ||
+		 memcmp(sf->name, name, SNDRV_SFNT_PATCH_NAME_LEN) == 0));
+}
+
+/*
+ * Close the current patch.
+ */
+static int
+close_patch(struct snd_sf_list *sflist)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&sflist->lock, flags);
+	sflist->currsf = NULL;
+	sflist->open_client = -1;
+	spin_unlock_irqrestore(&sflist->lock, flags);
+
+	rebuild_presets(sflist);
+
+	return 0;
+
+}
+
+/* probe sample in the current list -- nothing to be loaded */
+static int
+probe_data(struct snd_sf_list *sflist, int sample_id)
+{
+	/* patch must be opened */
+	if (sflist->currsf) {
+		/* search the specified sample by optarg */
+		if (find_sample(sflist->currsf, sample_id))
+			return 0;
+	}
+	return -EINVAL;
+}
+
+/*
+ * increment zone counter
+ */
+static void
+set_zone_counter(struct snd_sf_list *sflist, struct snd_soundfont *sf,
+		 struct snd_sf_zone *zp)
+{
+	zp->counter = sflist->zone_counter++;
+	if (sf->type & SNDRV_SFNT_PAT_LOCKED)
+		sflist->zone_locked = sflist->zone_counter;
+}
+
+/*
+ * allocate a new zone record
+ */
+static struct snd_sf_zone *
+sf_zone_new(struct snd_sf_list *sflist, struct snd_soundfont *sf)
+{
+	struct snd_sf_zone *zp;
+
+	if ((zp = kzalloc(sizeof(*zp), GFP_KERNEL)) == NULL)
+		return NULL;
+	zp->next = sf->zones;
+	sf->zones = zp;
+
+	init_voice_info(&zp->v);
+
+	set_zone_counter(sflist, sf, zp);
+	return zp;
+}
+
+
+/*
+ * increment sample couter
+ */
+static void
+set_sample_counter(struct snd_sf_list *sflist, struct snd_soundfont *sf,
+		   struct snd_sf_sample *sp)
+{
+	sp->counter = sflist->sample_counter++;
+	if (sf->type & SNDRV_SFNT_PAT_LOCKED)
+		sflist->sample_locked = sflist->sample_counter;
+}
+
+/*
+ * allocate a new sample list record
+ */
+static struct snd_sf_sample *
+sf_sample_new(struct snd_sf_list *sflist, struct snd_soundfont *sf)
+{
+	struct snd_sf_sample *sp;
+
+	if ((sp = kzalloc(sizeof(*sp), GFP_KERNEL)) == NULL)
+		return NULL;
+
+	sp->next = sf->samples;
+	sf->samples = sp;
+
+	set_sample_counter(sflist, sf, sp);
+	return sp;
+}
+
+/*
+ * delete sample list -- this is an exceptional job.
+ * only the last allocated sample can be deleted.
+ */
+static void
+sf_sample_delete(struct snd_sf_list *sflist, struct snd_soundfont *sf,
+		 struct snd_sf_sample *sp)
+{
+	/* only last sample is accepted */
+	if (sp == sf->samples) {
+		sf->samples = sp->next;
+		kfree(sp);
+	}
+}
+
+
+/* load voice map */
+static int
+load_map(struct snd_sf_list *sflist, const void __user *data, int count)
+{
+	struct snd_sf_zone *zp, *prevp;
+	struct snd_soundfont *sf;
+	struct soundfont_voice_map map;
+
+	/* get the link info */
+	if (count < (int)sizeof(map))
+		return -EINVAL;
+	if (copy_from_user(&map, data, sizeof(map)))
+		return -EFAULT;
+
+	if (map.map_instr < 0 || map.map_instr >= SF_MAX_INSTRUMENTS)
+		return -EINVAL;
+	
+	sf = newsf(sflist, SNDRV_SFNT_PAT_TYPE_MAP|SNDRV_SFNT_PAT_SHARED, NULL);
+	if (sf == NULL)
+		return -ENOMEM;
+
+	prevp = NULL;
+	for (zp = sf->zones; zp; prevp = zp, zp = zp->next) {
+		if (zp->mapped &&
+		    zp->instr == map.map_instr &&
+		    zp->bank == map.map_bank &&
+		    zp->v.low == map.map_key &&
+		    zp->v.start == map.src_instr &&
+		    zp->v.end == map.src_bank &&
+		    zp->v.fixkey == map.src_key) {
+			/* the same mapping is already present */
+			/* relink this record to the link head */
+			if (prevp) {
+				prevp->next = zp->next;
+				zp->next = sf->zones;
+				sf->zones = zp;
+			}
+			/* update the counter */
+			set_zone_counter(sflist, sf, zp);
+			return 0;
+		}
+	}
+
+	/* create a new zone */
+	if ((zp = sf_zone_new(sflist, sf)) == NULL)
+		return -ENOMEM;
+
+	zp->bank = map.map_bank;
+	zp->instr = map.map_instr;
+	zp->mapped = 1;
+	if (map.map_key >= 0) {
+		zp->v.low = map.map_key;
+		zp->v.high = map.map_key;
+	}
+	zp->v.start = map.src_instr;
+	zp->v.end = map.src_bank;
+	zp->v.fixkey = map.src_key;
+	zp->v.sf_id = sf->id;
+
+	add_preset(sflist, zp);
+
+	return 0;
+}
+
+
+/* remove the present instrument layers */
+static int
+remove_info(struct snd_sf_list *sflist, struct snd_soundfont *sf,
+	    int bank, int instr)
+{
+	struct snd_sf_zone *prev, *next, *p;
+	int removed = 0;
+
+	prev = NULL;
+	for (p = sf->zones; p; p = next) {
+		next = p->next;
+		if (! p->mapped &&
+		    p->bank == bank && p->instr == instr) {
+			/* remove this layer */
+			if (prev)
+				prev->next = next;
+			else
+				sf->zones = next;
+			removed++;
+			kfree(p);
+		} else
+			prev = p;
+	}
+	if (removed)
+		rebuild_presets(sflist);
+	return removed;
+}
+
+
+/*
+ * Read an info record from the user buffer and save it on the current
+ * open soundfont.
+ */
+static int
+load_info(struct snd_sf_list *sflist, const void __user *data, long count)
+{
+	struct snd_soundfont *sf;
+	struct snd_sf_zone *zone;
+	struct soundfont_voice_rec_hdr hdr;
+	int i;
+
+	/* patch must be opened */
+	if ((sf = sflist->currsf) == NULL)
+		return -EINVAL;
+
+	if (is_special_type(sf->type))
+		return -EINVAL;
+
+	if (count < (long)sizeof(hdr)) {
+		printk("Soundfont error: invalid patch zone length\n");
+		return -EINVAL;
+	}
+	if (copy_from_user((char*)&hdr, data, sizeof(hdr)))
+		return -EFAULT;
+	
+	(char*)data += sizeof(hdr);
+	count -= sizeof(hdr);
+
+	if (hdr.nvoices <= 0 || hdr.nvoices >= 100) {
+		printk("Soundfont error: Illegal voice number %d\n", hdr.nvoices);
+		return -EINVAL;
+	}
+
+	if (count < (long)sizeof(struct soundfont_voice_info) * hdr.nvoices) {
+		printk("Soundfont Error: patch length(%ld) is smaller than nvoices(%d)\n",
+		       count, hdr.nvoices);
+		return -EINVAL;
+	}
+
+	switch (hdr.write_mode) {
+	case SNDRV_SFNT_WR_EXCLUSIVE:
+		/* exclusive mode - if the instrument already exists,
+		   return error */
+		for (zone = sf->zones; zone; zone = zone->next) {
+			if (!zone->mapped &&
+			    zone->bank == hdr.bank &&
+			    zone->instr == hdr.instr)
+				return -EINVAL;
+		}
+		break;
+	case SNDRV_SFNT_WR_REPLACE:
+		/* replace mode - remove the instrument if it already exists */
+		remove_info(sflist, sf, hdr.bank, hdr.instr);
+		break;
+	}
+
+	for (i = 0; i < hdr.nvoices; i++) {
+		struct snd_sf_zone tmpzone;
+
+		/* copy awe_voice_info parameters */
+		if (copy_from_user(&tmpzone.v, data, sizeof(tmpzone.v))) {
+			return -EFAULT;
+		}
+
+		(char*)data += sizeof(tmpzone.v);
+		count -= sizeof(tmpzone.v);
+
+		tmpzone.bank = hdr.bank;
+		tmpzone.instr = hdr.instr;
+		tmpzone.mapped = 0;
+		tmpzone.v.sf_id = sf->id;
+		if (tmpzone.v.mode & SNDRV_SFNT_MODE_INIT_PARM)
+			init_voice_parm(&tmpzone.v.parm);
+
+		/* create a new zone */
+		if ((zone = sf_zone_new(sflist, sf)) == NULL) {
+			return -ENOMEM;
+		}
+
+		/* copy the temporary data */
+		zone->bank = tmpzone.bank;
+		zone->instr = tmpzone.instr;
+		zone->v = tmpzone.v;
+
+		/* look up the sample */
+		zone->sample = set_sample(sf, &zone->v);
+	}
+
+	return 0;
+}
+
+
+/* initialize voice_info record */
+static void
+init_voice_info(struct soundfont_voice_info *avp)
+{
+	memset(avp, 0, sizeof(*avp));
+
+	avp->root = 60;
+	avp->high = 127;
+	avp->velhigh = 127;
+	avp->fixkey = -1;
+	avp->fixvel = -1;
+	avp->fixpan = -1;
+	avp->pan = -1;
+	avp->amplitude = 127;
+	avp->scaleTuning = 100;
+
+	init_voice_parm(&avp->parm);
+}
+
+/* initialize voice_parm record:
+ * Env1/2: delay=0, attack=0, hold=0, sustain=0, decay=0, release=0.
+ * Vibrato and Tremolo effects are zero.
+ * Cutoff is maximum.
+ * Chorus and Reverb effects are zero.
+ */
+static void
+init_voice_parm(struct soundfont_voice_parm *pp)
+{
+	memset(pp, 0, sizeof(*pp));
+
+	pp->moddelay = 0x8000;
+	pp->modatkhld = 0x7f7f;
+	pp->moddcysus = 0x7f7f;
+	pp->modrelease = 0x807f;
+
+	pp->voldelay = 0x8000;
+	pp->volatkhld = 0x7f7f;
+	pp->voldcysus = 0x7f7f;
+	pp->volrelease = 0x807f;
+
+	pp->lfo1delay = 0x8000;
+	pp->lfo2delay = 0x8000;
+
+	pp->cutoff = 0xff;
+}	
+
+/* search the specified sample */
+static struct snd_sf_sample *
+set_sample(struct snd_soundfont *sf, struct soundfont_voice_info *avp)
+{
+	struct snd_sf_sample *sample;
+
+	sample = find_sample(sf, avp->sample);
+	if (sample == NULL)
+		return NULL;
+
+	/* add in the actual sample offsets:
+	 * The voice_info addresses define only the relative offset
+	 * from sample pointers.  Here we calculate the actual DRAM
+	 * offset from sample pointers.
+	 */
+	avp->start += sample->v.start;
+	avp->end += sample->v.end;
+	avp->loopstart += sample->v.loopstart;
+	avp->loopend += sample->v.loopend;
+
+	/* copy mode flags */
+	avp->sample_mode = sample->v.mode_flags;
+
+	return sample;
+}
+
+/* find the sample pointer with the given id in the soundfont */
+static struct snd_sf_sample *
+find_sample(struct snd_soundfont *sf, int sample_id)
+{
+	struct snd_sf_sample *p;
+
+	if (sf == NULL)
+		return NULL;
+
+	for (p = sf->samples; p; p = p->next) {
+		if (p->v.sample == sample_id)
+			return p;
+	}
+	return NULL;
+}
+
+
+/*
+ * Load sample information, this can include data to be loaded onto
+ * the soundcard.  It can also just be a pointer into soundcard ROM.
+ * If there is data it will be written to the soundcard via the callback
+ * routine.
+ */
+static int
+load_data(struct snd_sf_list *sflist, const void __user *data, long count)
+{
+	struct snd_soundfont *sf;
+	struct soundfont_sample_info sample_info;
+	struct snd_sf_sample *sp;
+	long off;
+
+	/* patch must be opened */
+	if ((sf = sflist->currsf) == NULL)
+		return -EINVAL;
+
+	if (is_special_type(sf->type))
+		return -EINVAL;
+
+	if (copy_from_user(&sample_info, data, sizeof(sample_info)))
+		return -EFAULT;
+
+	off = sizeof(sample_info);
+
+	if (sample_info.size != (count-off)/2)
+		return -EINVAL;
+
+	/* Check for dup */
+	if (find_sample(sf, sample_info.sample)) {
+		/* if shared sample, skip this data */
+		if (sf->type & SNDRV_SFNT_PAT_SHARED)
+			return 0;
+		return -EINVAL;
+	}
+
+	/* Allocate a new sample structure */
+	if ((sp = sf_sample_new(sflist, sf)) == NULL)
+		return -ENOMEM;
+
+	sp->v = sample_info;
+	sp->v.sf_id = sf->id;
+	sp->v.dummy = 0;
+	sp->v.truesize = sp->v.size;
+
+	/*
+	 * If there is wave data then load it.
+	 */
+	if (sp->v.size > 0) {
+		int  rc;
+		rc = sflist->callback.sample_new
+			(sflist->callback.private_data, sp, sflist->memhdr,
+			 (char*)data + off, count - off);
+		if (rc < 0) {
+			sf_sample_delete(sflist, sf, sp);
+			return rc;
+		}
+		sflist->mem_used += sp->v.truesize;
+	}
+
+	return count;
+}
+
+
+/* log2_tbl[i] = log2(i+128) * 0x10000 */
+static int log_tbl[129] = {
+	0x70000, 0x702df, 0x705b9, 0x7088e, 0x70b5d, 0x70e26, 0x710eb, 0x713aa,
+	0x71663, 0x71918, 0x71bc8, 0x71e72, 0x72118, 0x723b9, 0x72655, 0x728ed,
+	0x72b80, 0x72e0e, 0x73098, 0x7331d, 0x7359e, 0x7381b, 0x73a93, 0x73d08,
+	0x73f78, 0x741e4, 0x7444c, 0x746b0, 0x74910, 0x74b6c, 0x74dc4, 0x75019,
+	0x75269, 0x754b6, 0x75700, 0x75946, 0x75b88, 0x75dc7, 0x76002, 0x7623a,
+	0x7646e, 0x766a0, 0x768cd, 0x76af8, 0x76d1f, 0x76f43, 0x77164, 0x77382,
+	0x7759d, 0x777b4, 0x779c9, 0x77bdb, 0x77dea, 0x77ff5, 0x781fe, 0x78404,
+	0x78608, 0x78808, 0x78a06, 0x78c01, 0x78df9, 0x78fef, 0x791e2, 0x793d2,
+	0x795c0, 0x797ab, 0x79993, 0x79b79, 0x79d5d, 0x79f3e, 0x7a11d, 0x7a2f9,
+	0x7a4d3, 0x7a6ab, 0x7a880, 0x7aa53, 0x7ac24, 0x7adf2, 0x7afbe, 0x7b188,
+	0x7b350, 0x7b515, 0x7b6d8, 0x7b899, 0x7ba58, 0x7bc15, 0x7bdd0, 0x7bf89,
+	0x7c140, 0x7c2f5, 0x7c4a7, 0x7c658, 0x7c807, 0x7c9b3, 0x7cb5e, 0x7cd07,
+	0x7ceae, 0x7d053, 0x7d1f7, 0x7d398, 0x7d538, 0x7d6d6, 0x7d872, 0x7da0c,
+	0x7dba4, 0x7dd3b, 0x7ded0, 0x7e063, 0x7e1f4, 0x7e384, 0x7e512, 0x7e69f,
+	0x7e829, 0x7e9b3, 0x7eb3a, 0x7ecc0, 0x7ee44, 0x7efc7, 0x7f148, 0x7f2c8,
+	0x7f446, 0x7f5c2, 0x7f73d, 0x7f8b7, 0x7fa2f, 0x7fba5, 0x7fd1a, 0x7fe8d,
+	0x80000,
+};
+
+/* convert from linear to log value
+ *
+ * conversion: value = log2(amount / base) * ratio
+ *
+ * argument:
+ *   amount = linear value (unsigned, 32bit max)
+ *   offset = base offset (:= log2(base) * 0x10000)
+ *   ratio = division ratio
+ *
+ */
+int
+snd_sf_linear_to_log(unsigned int amount, int offset, int ratio)
+{
+	int v;
+	int s, low, bit;
+	
+	if (amount < 2)
+		return 0;
+	for (bit = 0; ! (amount & 0x80000000L); bit++)
+		amount <<= 1;
+	s = (amount >> 24) & 0x7f;
+	low = (amount >> 16) & 0xff;
+	/* linear approxmimation by lower 8 bit */
+	v = (log_tbl[s + 1] * low + log_tbl[s] * (0x100 - low)) >> 8;
+	v -= offset;
+	v = (v * ratio) >> 16;
+	v += (24 - bit) * ratio;
+	return v;
+}
+
+#define OFFSET_MSEC		653117		/* base = 1000 */
+#define OFFSET_ABSCENT		851781		/* base = 8176 */
+#define OFFSET_SAMPLERATE	1011119		/* base = 44100 */
+
+#define ABSCENT_RATIO		1200
+#define TIMECENT_RATIO		1200
+#define SAMPLERATE_RATIO	4096
+
+/*
+ * mHz to abscent
+ * conversion: abscent = log2(MHz / 8176) * 1200
+ */
+static int
+freq_to_note(int mhz)
+{
+	return snd_sf_linear_to_log(mhz, OFFSET_ABSCENT, ABSCENT_RATIO);
+}
+
+/* convert Hz to AWE32 rate offset:
+ * sample pitch offset for the specified sample rate
+ * rate=44100 is no offset, each 4096 is 1 octave (twice).
+ * eg, when rate is 22050, this offset becomes -4096.
+ *
+ * conversion: offset = log2(Hz / 44100) * 4096
+ */
+static int
+calc_rate_offset(int hz)
+{
+	return snd_sf_linear_to_log(hz, OFFSET_SAMPLERATE, SAMPLERATE_RATIO);
+}
+
+
+/* calculate GUS envelope time */
+static int
+calc_gus_envelope_time(int rate, int start, int end)
+{
+	int r, p, t;
+	r = (3 - ((rate >> 6) & 3)) * 3;
+	p = rate & 0x3f;
+	t = end - start;
+	if (t < 0) t = -t;
+	if (13 > r)
+		t = t << (13 - r);
+	else
+		t = t >> (r - 13);
+	return (t * 10) / (p * 441);
+}
+
+/* convert envelope time parameter to soundfont parameters */
+
+/* attack & decay/release time table (msec) */
+static short attack_time_tbl[128] = {
+32767, 32767, 5989, 4235, 2994, 2518, 2117, 1780, 1497, 1373, 1259, 1154, 1058, 970, 890, 816,
+707, 691, 662, 634, 607, 581, 557, 533, 510, 489, 468, 448, 429, 411, 393, 377,
+361, 345, 331, 317, 303, 290, 278, 266, 255, 244, 234, 224, 214, 205, 196, 188,
+180, 172, 165, 158, 151, 145, 139, 133, 127, 122, 117, 112, 107, 102, 98, 94,
+90, 86, 82, 79, 75, 72, 69, 66, 63, 61, 58, 56, 53, 51, 49, 47,
+45, 43, 41, 39, 37, 36, 34, 33, 31, 30, 29, 28, 26, 25, 24, 23,
+22, 21, 20, 19, 19, 18, 17, 16, 16, 15, 15, 14, 13, 13, 12, 12,
+11, 11, 10, 10, 10, 9, 9, 8, 8, 8, 8, 7, 7, 7, 6, 0,
+};
+
+static short decay_time_tbl[128] = {
+32767, 32767, 22614, 15990, 11307, 9508, 7995, 6723, 5653, 5184, 4754, 4359, 3997, 3665, 3361, 3082,
+2828, 2765, 2648, 2535, 2428, 2325, 2226, 2132, 2042, 1955, 1872, 1793, 1717, 1644, 1574, 1507,
+1443, 1382, 1324, 1267, 1214, 1162, 1113, 1066, 978, 936, 897, 859, 822, 787, 754, 722,
+691, 662, 634, 607, 581, 557, 533, 510, 489, 468, 448, 429, 411, 393, 377, 361,
+345, 331, 317, 303, 290, 278, 266, 255, 244, 234, 224, 214, 205, 196, 188, 180,
+172, 165, 158, 151, 145, 139, 133, 127, 122, 117, 112, 107, 102, 98, 94, 90,
+86, 82, 79, 75, 72, 69, 66, 63, 61, 58, 56, 53, 51, 49, 47, 45,
+43, 41, 39, 37, 36, 34, 33, 31, 30, 29, 28, 26, 25, 24, 23, 22,
+};
+
+/* delay time = 0x8000 - msec/92 */
+int
+snd_sf_calc_parm_hold(int msec)
+{
+	int val = (0x7f * 92 - msec) / 92;
+	if (val < 1) val = 1;
+	if (val >= 126) val = 126;
+	return val;
+}
+
+/* search an index for specified time from given time table */
+static int
+calc_parm_search(int msec, short *table)
+{
+	int left = 1, right = 127, mid;
+	while (left < right) {
+		mid = (left + right) / 2;
+		if (msec < (int)table[mid])
+			left = mid + 1;
+		else
+			right = mid;
+	}
+	return left;
+}
+
+/* attack time: search from time table */
+int
+snd_sf_calc_parm_attack(int msec)
+{
+	return calc_parm_search(msec, attack_time_tbl);
+}
+
+/* decay/release time: search from time table */
+int
+snd_sf_calc_parm_decay(int msec)
+{
+	return calc_parm_search(msec, decay_time_tbl);
+}
+
+int snd_sf_vol_table[128] = {
+	255,111,95,86,79,74,70,66,63,61,58,56,54,52,50,49,
+	47,46,45,43,42,41,40,39,38,37,36,35,34,34,33,32,
+	31,31,30,29,29,28,27,27,26,26,25,24,24,23,23,22,
+	22,21,21,21,20,20,19,19,18,18,18,17,17,16,16,16,
+	15,15,15,14,14,14,13,13,13,12,12,12,11,11,11,10,
+	10,10,10,9,9,9,8,8,8,8,7,7,7,7,6,6,
+	6,6,5,5,5,5,5,4,4,4,4,3,3,3,3,3,
+	2,2,2,2,2,1,1,1,1,1,0,0,0,0,0,0,
+};
+
+
+#define calc_gus_sustain(val)  (0x7f - snd_sf_vol_table[(val)/2])
+#define calc_gus_attenuation(val)	snd_sf_vol_table[(val)/2]
+
+/* load GUS patch */
+static int
+load_guspatch(struct snd_sf_list *sflist, const char __user *data,
+	      long count, int client)
+{
+	struct patch_info patch;
+	struct snd_soundfont *sf;
+	struct snd_sf_zone *zone;
+	struct snd_sf_sample *smp;
+	int note, sample_id;
+	int rc;
+
+	if (count < (long)sizeof(patch)) {
+		snd_printk("patch record too small %ld\n", count);
+		return -EINVAL;
+	}
+	if (copy_from_user(&patch, data, sizeof(patch)))
+		return -EFAULT;
+	
+	count -= sizeof(patch);
+	data += sizeof(patch);
+
+	sf = newsf(sflist, SNDRV_SFNT_PAT_TYPE_GUS|SNDRV_SFNT_PAT_SHARED, NULL);
+	if (sf == NULL)
+		return -ENOMEM;
+	if ((smp = sf_sample_new(sflist, sf)) == NULL)
+		return -ENOMEM;
+	sample_id = sflist->sample_counter;
+	smp->v.sample = sample_id;
+	smp->v.start = 0;
+	smp->v.end = patch.len;
+	smp->v.loopstart = patch.loop_start;
+	smp->v.loopend = patch.loop_end;
+	smp->v.size = patch.len;
+
+	/* set up mode flags */
+	smp->v.mode_flags = 0;
+	if (!(patch.mode & WAVE_16_BITS))
+		smp->v.mode_flags |= SNDRV_SFNT_SAMPLE_8BITS;
+	if (patch.mode & WAVE_UNSIGNED)
+		smp->v.mode_flags |= SNDRV_SFNT_SAMPLE_UNSIGNED;
+	smp->v.mode_flags |= SNDRV_SFNT_SAMPLE_NO_BLANK;
+	if (!(patch.mode & (WAVE_LOOPING|WAVE_BIDIR_LOOP|WAVE_LOOP_BACK)))
+		smp->v.mode_flags |= SNDRV_SFNT_SAMPLE_SINGLESHOT;
+	if (patch.mode & WAVE_BIDIR_LOOP)
+		smp->v.mode_flags |= SNDRV_SFNT_SAMPLE_BIDIR_LOOP;
+	if (patch.mode & WAVE_LOOP_BACK)
+		smp->v.mode_flags |= SNDRV_SFNT_SAMPLE_REVERSE_LOOP;
+
+	if (patch.mode & WAVE_16_BITS) {
+		/* convert to word offsets */
+		smp->v.size /= 2;
+		smp->v.end /= 2;
+		smp->v.loopstart /= 2;
+		smp->v.loopend /= 2;
+	}
+	/*smp->v.loopend++;*/
+
+	smp->v.dummy = 0;
+	smp->v.truesize = 0;
+	smp->v.sf_id = sf->id;
+
+	/* set up voice info */
+	if ((zone = sf_zone_new(sflist, sf)) == NULL) {
+		sf_sample_delete(sflist, sf, smp);
+		return -ENOMEM;
+	}
+
+	/*
+	 * load wave data
+	 */
+	if (sflist->callback.sample_new) {
+		rc = sflist->callback.sample_new
+			(sflist->callback.private_data, smp, sflist->memhdr,
+			 data, count);
+		if (rc < 0) {
+			sf_sample_delete(sflist, sf, smp);
+			return rc;
+		}
+		/* memory offset is updated after */
+	}
+
+	/* update the memory offset here */
+	sflist->mem_used += smp->v.truesize;
+
+	zone->v.sample = sample_id; /* the last sample */
+	zone->v.rate_offset = calc_rate_offset(patch.base_freq);
+	note = freq_to_note(patch.base_note);
+	zone->v.root = note / 100;
+	zone->v.tune = -(note % 100);
+	zone->v.low = (freq_to_note(patch.low_note) + 99) / 100;
+	zone->v.high = freq_to_note(patch.high_note) / 100;
+	/* panning position; -128 - 127 => 0-127 */
+	zone->v.pan = (patch.panning + 128) / 2;
+#if 0
+	snd_printk("gus: basefrq=%d (ofs=%d) root=%d,tune=%d, range:%d-%d\n",
+		   (int)patch.base_freq, zone->v.rate_offset,
+		   zone->v.root, zone->v.tune, zone->v.low, zone->v.high);
+#endif
+
+	/* detuning is ignored */
+	/* 6points volume envelope */
+	if (patch.mode & WAVE_ENVELOPES) {
+		int attack, hold, decay, release;
+		attack = calc_gus_envelope_time
+			(patch.env_rate[0], 0, patch.env_offset[0]);
+		hold = calc_gus_envelope_time
+			(patch.env_rate[1], patch.env_offset[0],
+			 patch.env_offset[1]);
+		decay = calc_gus_envelope_time
+			(patch.env_rate[2], patch.env_offset[1],
+			 patch.env_offset[2]);
+		release = calc_gus_envelope_time
+			(patch.env_rate[3], patch.env_offset[1],
+			 patch.env_offset[4]);
+		release += calc_gus_envelope_time
+			(patch.env_rate[4], patch.env_offset[3],
+			 patch.env_offset[4]);
+		release += calc_gus_envelope_time
+			(patch.env_rate[5], patch.env_offset[4],
+			 patch.env_offset[5]);
+		zone->v.parm.volatkhld = 
+			(snd_sf_calc_parm_hold(hold) << 8) |
+			snd_sf_calc_parm_attack(attack);
+		zone->v.parm.voldcysus = (calc_gus_sustain(patch.env_offset[2]) << 8) |
+			snd_sf_calc_parm_decay(decay);
+		zone->v.parm.volrelease = 0x8000 | snd_sf_calc_parm_decay(release);
+		zone->v.attenuation = calc_gus_attenuation(patch.env_offset[0]);
+#if 0
+		snd_printk("gus: atkhld=%x, dcysus=%x, volrel=%x, att=%d\n",
+			   zone->v.parm.volatkhld,
+			   zone->v.parm.voldcysus,
+			   zone->v.parm.volrelease,
+			   zone->v.attenuation);
+#endif
+	}
+
+	/* fast release */
+	if (patch.mode & WAVE_FAST_RELEASE) {
+		zone->v.parm.volrelease = 0x807f;
+	}
+
+	/* tremolo effect */
+	if (patch.mode & WAVE_TREMOLO) {
+		int rate = (patch.tremolo_rate * 1000 / 38) / 42;
+		zone->v.parm.tremfrq = ((patch.tremolo_depth / 2) << 8) | rate;
+	}
+	/* vibrato effect */
+	if (patch.mode & WAVE_VIBRATO) {
+		int rate = (patch.vibrato_rate * 1000 / 38) / 42;
+		zone->v.parm.fm2frq2 = ((patch.vibrato_depth / 6) << 8) | rate;
+	}
+	
+	/* scale_freq, scale_factor, volume, and fractions not implemented */
+
+	if (!(smp->v.mode_flags & SNDRV_SFNT_SAMPLE_SINGLESHOT))
+		zone->v.mode = SNDRV_SFNT_MODE_LOOPING;
+	else
+		zone->v.mode = 0;
+
+	/* append to the tail of the list */
+	/*zone->bank = ctrls[AWE_MD_GUS_BANK];*/
+	zone->bank = 0;
+	zone->instr = patch.instr_no;
+	zone->mapped = 0;
+	zone->v.sf_id = sf->id;
+
+	zone->sample = set_sample(sf, &zone->v);
+
+	/* rebuild preset now */
+	add_preset(sflist, zone);
+
+	return 0;
+}
+
+/* load GUS patch */
+int
+snd_soundfont_load_guspatch(struct snd_sf_list *sflist, const char __user *data,
+			    long count, int client)
+{
+	int rc;
+	lock_preset(sflist);
+	rc = load_guspatch(sflist, data, count, client);
+	unlock_preset(sflist);
+	return rc;
+}
+
+
+/*
+ * Rebuild the preset table.  This is like a hash table in that it allows
+ * quick access to the zone information.  For each preset there are zone
+ * structures linked by next_instr and by next_zone.  Former is the whole
+ * link for this preset, and latter is the link for zone (i.e. instrument/
+ * bank/key combination).
+ */
+static void
+rebuild_presets(struct snd_sf_list *sflist)
+{
+	struct snd_soundfont *sf;
+	struct snd_sf_zone *cur;
+
+	/* clear preset table */
+	memset(sflist->presets, 0, sizeof(sflist->presets));
+
+	/* search all fonts and insert each font */
+	for (sf = sflist->fonts; sf; sf = sf->next) {
+		for (cur = sf->zones; cur; cur = cur->next) {
+			if (! cur->mapped && cur->sample == NULL) {
+				/* try again to search the corresponding sample */
+				cur->sample = set_sample(sf, &cur->v);
+				if (cur->sample == NULL)
+					continue;
+			}
+
+			add_preset(sflist, cur);
+		}
+	}
+}
+
+
+/*
+ * add the given zone to preset table
+ */
+static void
+add_preset(struct snd_sf_list *sflist, struct snd_sf_zone *cur)
+{
+	struct snd_sf_zone *zone;
+	int index;
+
+	zone = search_first_zone(sflist, cur->bank, cur->instr, cur->v.low);
+	if (zone && zone->v.sf_id != cur->v.sf_id) {
+		/* different instrument was already defined */
+		struct snd_sf_zone *p;
+		/* compare the allocated time */
+		for (p = zone; p; p = p->next_zone) {
+			if (p->counter > cur->counter)
+				/* the current is older.. skipped */
+				return;
+		}
+		/* remove old zones */
+		delete_preset(sflist, zone);
+		zone = NULL; /* do not forget to clear this! */
+	}
+
+	/* prepend this zone */
+	if ((index = get_index(cur->bank, cur->instr, cur->v.low)) < 0)
+		return;
+	cur->next_zone = zone; /* zone link */
+	cur->next_instr = sflist->presets[index]; /* preset table link */
+	sflist->presets[index] = cur;
+}
+
+/*
+ * delete the given zones from preset_table
+ */
+static void
+delete_preset(struct snd_sf_list *sflist, struct snd_sf_zone *zp)
+{
+	int index;
+	struct snd_sf_zone *p;
+
+	if ((index = get_index(zp->bank, zp->instr, zp->v.low)) < 0)
+		return;
+	for (p = sflist->presets[index]; p; p = p->next_instr) {
+		while (p->next_instr == zp) {
+			p->next_instr = zp->next_instr;
+			zp = zp->next_zone;
+			if (zp == NULL)
+				return;
+		}
+	}
+}
+
+
+/*
+ * Search matching zones from preset table.
+ * The note can be rewritten by preset mapping (alias).
+ * The found zones are stored on 'table' array.  max_layers defines
+ * the maximum number of elements in this array.
+ * This function returns the number of found zones.  0 if not found.
+ */
+int
+snd_soundfont_search_zone(struct snd_sf_list *sflist, int *notep, int vel,
+			  int preset, int bank,
+			  int def_preset, int def_bank,
+			  struct snd_sf_zone **table, int max_layers)
+{
+	int nvoices;
+	unsigned long flags;
+
+	/* this function is supposed to be called atomically,
+	 * so we check the lock.  if it's busy, just returns 0 to
+	 * tell the caller the busy state
+	 */
+	spin_lock_irqsave(&sflist->lock, flags);
+	if (sflist->presets_locked) {
+		spin_unlock_irqrestore(&sflist->lock, flags);
+		return 0;
+	}
+	nvoices = search_zones(sflist, notep, vel, preset, bank,
+			       table, max_layers, 0);
+	if (! nvoices) {
+		if (preset != def_preset || bank != def_bank)
+			nvoices = search_zones(sflist, notep, vel,
+					       def_preset, def_bank,
+					       table, max_layers, 0);
+	}
+	spin_unlock_irqrestore(&sflist->lock, flags);
+	return nvoices;
+}
+
+
+/*
+ * search the first matching zone
+ */
+static struct snd_sf_zone *
+search_first_zone(struct snd_sf_list *sflist, int bank, int preset, int key)
+{
+	int index;
+	struct snd_sf_zone *zp;
+
+	if ((index = get_index(bank, preset, key)) < 0)
+		return NULL;
+	for (zp = sflist->presets[index]; zp; zp = zp->next_instr) {
+		if (zp->instr == preset && zp->bank == bank)
+			return zp;
+	}
+	return NULL;
+}
+
+
+/*
+ * search matching zones from sflist.  can be called recursively.
+ */
+static int
+search_zones(struct snd_sf_list *sflist, int *notep, int vel,
+	     int preset, int bank, struct snd_sf_zone **table,
+	     int max_layers, int level)
+{
+	struct snd_sf_zone *zp;
+	int nvoices;
+
+	zp = search_first_zone(sflist, bank, preset, *notep);
+	nvoices = 0;
+	for (; zp; zp = zp->next_zone) {
+		if (*notep >= zp->v.low && *notep <= zp->v.high &&
+		    vel >= zp->v.vellow && vel <= zp->v.velhigh) {
+			if (zp->mapped) {
+				/* search preset mapping (aliasing) */
+				int key = zp->v.fixkey;
+				preset = zp->v.start;
+				bank = zp->v.end;
+
+				if (level > 5) /* too deep alias level */
+					return 0;
+				if (key < 0)
+					key = *notep;
+				nvoices = search_zones(sflist, &key, vel,
+						       preset, bank, table,
+						       max_layers, level + 1);
+				if (nvoices > 0)
+					*notep = key;
+				break;
+			}
+			table[nvoices++] = zp;
+			if (nvoices >= max_layers)
+				break;
+		}
+	}
+
+	return nvoices;
+}
+
+
+/* calculate the index of preset table:
+ * drums are mapped from 128 to 255 according to its note key.
+ * other instruments are mapped from 0 to 127.
+ * if the index is out of range, return -1.
+ */
+static int
+get_index(int bank, int instr, int key)
+{
+	int index;
+	if (SF_IS_DRUM_BANK(bank))
+		index = key + SF_MAX_INSTRUMENTS;
+	else
+		index = instr;
+	index = index % SF_MAX_PRESETS;
+	if (index < 0)
+		return -1;
+	return index;
+}
+
+/*
+ * Initialise the sflist structure.
+ */
+static void
+snd_sf_init(struct snd_sf_list *sflist)
+{
+	memset(sflist->presets, 0, sizeof(sflist->presets));
+
+	sflist->mem_used = 0;
+	sflist->currsf = NULL;
+	sflist->open_client = -1;
+	sflist->fonts = NULL;
+	sflist->fonts_size = 0;
+	sflist->zone_counter = 0;
+	sflist->sample_counter = 0;
+	sflist->zone_locked = 0;
+	sflist->sample_locked = 0;
+}
+
+/*
+ * Release all list records
+ */
+static void
+snd_sf_clear(struct snd_sf_list *sflist)
+{
+	struct snd_soundfont *sf, *nextsf;
+	struct snd_sf_zone *zp, *nextzp;
+	struct snd_sf_sample *sp, *nextsp;
+
+	for (sf = sflist->fonts; sf; sf = nextsf) {
+		nextsf = sf->next;
+		for (zp = sf->zones; zp; zp = nextzp) {
+			nextzp = zp->next;
+			kfree(zp);
+		}
+		for (sp = sf->samples; sp; sp = nextsp) {
+			nextsp = sp->next;
+			if (sflist->callback.sample_free)
+				sflist->callback.sample_free(sflist->callback.private_data,
+							     sp, sflist->memhdr);
+			kfree(sp);
+		}
+		kfree(sf);
+	}
+
+	snd_sf_init(sflist);
+}
+
+
+/*
+ * Create a new sflist structure
+ */
+struct snd_sf_list *
+snd_sf_new(struct snd_sf_callback *callback, struct snd_util_memhdr *hdr)
+{
+	struct snd_sf_list *sflist;
+
+	if ((sflist = kzalloc(sizeof(*sflist), GFP_KERNEL)) == NULL)
+		return NULL;
+
+	init_MUTEX(&sflist->presets_mutex);
+	spin_lock_init(&sflist->lock);
+	sflist->memhdr = hdr;
+
+	if (callback)
+		sflist->callback = *callback;
+
+	snd_sf_init(sflist);
+	return sflist;
+}
+
+
+/*
+ * Free everything allocated off the sflist structure.
+ */
+void
+snd_sf_free(struct snd_sf_list *sflist)
+{
+	if (sflist == NULL)
+		return;
+	
+	lock_preset(sflist);
+	if (sflist->callback.sample_reset)
+		sflist->callback.sample_reset(sflist->callback.private_data);
+	snd_sf_clear(sflist);
+	unlock_preset(sflist);
+
+	kfree(sflist);
+}
+
+/*
+ * Remove all samples
+ * The soundcard should be silet before calling this function.
+ */
+int
+snd_soundfont_remove_samples(struct snd_sf_list *sflist)
+{
+	lock_preset(sflist);
+	if (sflist->callback.sample_reset)
+		sflist->callback.sample_reset(sflist->callback.private_data);
+	snd_sf_clear(sflist);
+	unlock_preset(sflist);
+
+	return 0;
+}
+
+/*
+ * Remove unlocked samples.
+ * The soundcard should be silent before calling this function.
+ */
+int
+snd_soundfont_remove_unlocked(struct snd_sf_list *sflist)
+{
+	struct snd_soundfont *sf;
+	struct snd_sf_zone *zp, *nextzp;
+	struct snd_sf_sample *sp, *nextsp;
+
+	lock_preset(sflist);
+
+	if (sflist->callback.sample_reset)
+		sflist->callback.sample_reset(sflist->callback.private_data);
+
+	/* to be sure */
+	memset(sflist->presets, 0, sizeof(sflist->presets));
+
+	for (sf = sflist->fonts; sf; sf = sf->next) {
+		for (zp = sf->zones; zp; zp = nextzp) {
+			if (zp->counter < sflist->zone_locked)
+				break;
+			nextzp = zp->next;
+			sf->zones = nextzp;
+			kfree(zp);
+		}
+
+		for (sp = sf->samples; sp; sp = nextsp) {
+			if (sp->counter < sflist->sample_locked)
+				break;
+			nextsp = sp->next;
+			sf->samples = nextsp;
+			sflist->mem_used -= sp->v.truesize;
+			if (sflist->callback.sample_free)
+				sflist->callback.sample_free(sflist->callback.private_data,
+							     sp, sflist->memhdr);
+			kfree(sp);
+		}
+	}
+
+	sflist->zone_counter = sflist->zone_locked;
+	sflist->sample_counter = sflist->sample_locked;
+
+	rebuild_presets(sflist);
+
+	unlock_preset(sflist);
+	return 0;
+}
+
Index: /GPL/branches/alsa-resync1/alsa-kernel/synth/makefile.os2
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/synth/makefile.os2	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/synth/makefile.os2	(revision 91)
@@ -0,0 +1,81 @@
+CINCLUDES = ..\..\include;..\include;$(%WATCOM)\H
+AINCLUDES = $(DDK_BASE)\H
+
+CDEFINES = -D__KERNEL__ -DMODULE -dTARGET_OS2 -dALSA_BUILD -D__ISAPNP__ -DCONFIG_ISAPNP -D__i386_
+
+32BIT=1
+!include ..\..\..\makefile.inc
+
+NMAKEOPTS=-f makefile.os2 $(LOGO)
+
+!if "$(DEBUG)" == "1"
+NMAKEOPTS += DEBUG=1
+!endif
+
+!if "$(KEE)" == "0"
+NMAKEOPTS += KEE=0
+KEE=0
+!else
+NMAKEOPTS += KEE=1
+KEE=1
+!endif
+
+!if "$(DEBUG)" == "1"
+!Message BUILDING DEBUG VERSION
+!else
+!Message BUILDING RELEASE VERSION
+!endif
+
+
+#===================================================================
+#
+#   List of source files
+#
+#===================================================================
+FILE1    = util_mem.obj
+FILE2    = 
+FILE3    = 
+FILE4    = 
+FILE5    = 
+FILE6    = 
+FILE7    = 
+FILE8    = 
+FILELAST =
+FILES    = $(FILE0) $(FILE1) $(FILE2) $(FILE3) $(FILE4) $(FILE5) $(FILE6) $(FILE7) $(FILE8) $(FILE9) $(FILE10) $(FILE11) $(FILE12)
+
+TARGET   = synth
+
+LNKFILE  = $(OBJDIR)\$(TARGET).lnk
+
+
+#===================================================================
+#
+#   Specific dependencies
+#
+#===================================================================
+all: $(OBJDIR)\$(TARGET).lib subdirs
+
+
+$(LNKFILE): makefile.os2
+    @%create $(LNKFILE)
+    @for %f in ($(FILES)) do @%append $(LNKFILE) -+$(OBJDIR)\%f
+
+$(OBJDIR)\$(TARGET).lib: $(LNKFILE) $(FILES)
+    $(LIB) $(OBJDIR)\$(TARGET).lib @$(LNKFILE)
+    copy $(OBJDIR)\$(TARGET).lib $(ALSA_LIB)
+
+subdirs: .SYMBOLIC
+    @echo BUILDING emux
+    @cd emux
+    @wmake $(NMAKEOPTS)
+    @cd ..
+
+clean: .SYMBOLIC
+    @echo CLEANING emux
+    @cd emux
+    @wmake $(NMAKEOPTS) clean
+    @cd ..
+    -@rm *.err 2>nul
+    @cd $(OBJDIR)
+    -rm *.obj *.lnk *.sys *.sym *.lst *.map *.wmap *.lib 2>nul
+    @cd ..\..
Index: /GPL/branches/alsa-resync1/alsa-kernel/synth/util_mem.c
===================================================================
--- /GPL/branches/alsa-resync1/alsa-kernel/synth/util_mem.c	(revision 91)
+++ /GPL/branches/alsa-resync1/alsa-kernel/synth/util_mem.c	(revision 91)
@@ -0,0 +1,208 @@
+/*
+ *  Copyright (C) 2000 Takashi Iwai <tiwai@suse.de>
+ *
+ *  Generic memory management routines for soundcard memory allocation
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/util_mem.h>
+
+MODULE_AUTHOR("Takashi Iwai");
+MODULE_DESCRIPTION("Generic memory management routines for soundcard memory allocation");
+MODULE_LICENSE("GPL");
+
+#define get_memblk(p)	list_entry(p, struct snd_util_memblk, list)
+
+/*
+ * create a new memory manager
+ */
+struct snd_util_memhdr *
+snd_util_memhdr_new(int memsize)
+{
+	struct snd_util_memhdr *hdr;
+
+	hdr = kzalloc(sizeof(*hdr), GFP_KERNEL);
+	if (hdr == NULL)
+		return NULL;
+	hdr->size = memsize;
+	init_MUTEX(&hdr->block_mutex);
+	INIT_LIST_HEAD(&hdr->block);
+
+	return hdr;
+}
+
+/*
+ * free a memory manager
+ */
+void snd_util_memhdr_free(struct snd_util_memhdr *hdr)
+{
+	struct list_head *p;
+
+	snd_assert(hdr != NULL, return);
+	/* release all blocks */
+	while ((p = hdr->block.next) != &hdr->block) {
+		list_del(p);
+		kfree(get_memblk(p));
+	}
+	kfree(hdr);
+}
+
+/*
+ * allocate a memory block (without mutex)
+ */
+struct snd_util_memblk *
+__snd_util_mem_alloc(struct snd_util_memhdr *hdr, int size)
+{
+	struct snd_util_memblk *blk;
+	unsigned int units, prev_offset;
+	struct list_head *p;
+
+	snd_assert(hdr != NULL, return NULL);
+	snd_assert(size > 0, return NULL);
+
+	/* word alignment */
+	units = size;
+	if (units & 1)
+		units++;
+	if (units > hdr->size)
+		return NULL;
+
+	/* look for empty block */
+	prev_offset = 0;
+	list_for_each(p, &hdr->block) {
+		blk = get_memblk(p);
+		if (blk->offset - prev_offset >= units)
+			goto __found;
+		prev_offset = blk->offset + blk->size;
+	}
+	if (hdr->size - prev_offset < units)
+		return NULL;
+
+__found:
+	return __snd_util_memblk_new(hdr, units, p->prev);
+}
+
+
+/*
+ * create a new memory block with the given size
+ * the block is linked next to prev
+ */
+struct snd_util_memblk *
+__snd_util_memblk_new(struct snd_util_memhdr *hdr, unsigned int units,
+		      struct list_head *prev)
+{
+	struct snd_util_memblk *blk;
+
+	blk = kmalloc(sizeof(struct snd_util_memblk) + hdr->block_extra_size,
+		      GFP_KERNEL);
+	if (blk == NULL)
+		return NULL;
+
+	if (! prev || prev == &hdr->block)
+		blk->offset = 0;
+	else {
+		struct snd_util_memblk *p = get_memblk(prev);
+		blk->offset = p->offset + p->size;
+	}
+	blk->size = units;
+	list_add(&blk->list, prev);
+	hdr->nblocks++;
+	hdr->used += units;
+	return blk;
+}
+
+
+/*
+ * allocate a memory block (with mutex)
+ */
+struct snd_util_memblk *
+snd_util_mem_alloc(struct snd_util_memhdr *hdr, int size)
+{
+	struct snd_util_memblk *blk;
+	down(&hdr->block_mutex);
+	blk = __snd_util_mem_alloc(hdr, size);
+	up(&hdr->block_mutex);
+	return blk;
+}
+
+
+/*
+ * remove the block from linked-list and free resource
+ * (without mutex)
+ */
+void
+__snd_util_mem_free(struct snd_util_memhdr *hdr, struct snd_util_memblk *blk)
+{
+	list_del(&blk->list);
+	hdr->nblocks--;
+	hdr->used -= blk->size;
+	kfree(blk);
+}
+
+/*
+ * free a memory block (with mutex)
+ */
+int snd_util_mem_free(struct snd_util_memhdr *hdr, struct snd_util_memblk *blk)
+{
+	snd_assert(hdr && blk, return -EINVAL);
+
+	down(&hdr->block_mutex);
+	__snd_util_mem_free(hdr, blk);
+	up(&hdr->block_mutex);
+	return 0;
+}
+
+/*
+ * return available memory size
+ */
+int snd_util_mem_avail(struct snd_util_memhdr *hdr)
+{
+	unsigned int size;
+	down(&hdr->block_mutex);
+	size = hdr->size - hdr->used;
+	up(&hdr->block_mutex);
+	return size;
+}
+
+
+EXPORT_SYMBOL(snd_util_memhdr_new);
+EXPORT_SYMBOL(snd_util_memhdr_free);
+EXPORT_SYMBOL(snd_util_mem_alloc);
+EXPORT_SYMBOL(snd_util_mem_free);
+EXPORT_SYMBOL(snd_util_mem_avail);
+EXPORT_SYMBOL(__snd_util_mem_alloc);
+EXPORT_SYMBOL(__snd_util_mem_free);
+EXPORT_SYMBOL(__snd_util_memblk_new);
+
+/*
+ *  INIT part
+ */
+
+static int __init alsa_util_mem_init(void)
+{
+	return 0;
+}
+
+static void __exit alsa_util_mem_exit(void)
+{
+}
+
+module_init(alsa_util_mem_init)
+module_exit(alsa_util_mem_exit)
